26 Commits

Author SHA1 Message Date
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
48 changed files with 863 additions and 1165 deletions
+56 -14
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
@@ -131,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
@@ -982,16 +997,36 @@ 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 numbers
local color_cache = setmetatable({}, {
__index = function(t, k)
local color = { pfUI.api.strsplit(",", k) }
for i = 1, table.getn(color) do
color[i] = tonumber(color[i])
end
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 color = CreateColor(pfUI.api.GetStringColor(k))
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
@@ -999,12 +1034,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
@@ -1012,7 +1048,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 ""
+26 -1
View File
@@ -174,6 +174,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")
@@ -231,6 +233,7 @@ 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, "combowidth", "6")
pfUI:UpdateConfig("unitframes", nil, "comboheight", "6")
@@ -382,6 +385,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")
@@ -400,6 +424,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")
@@ -455,7 +480,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")
+23 -57
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)
@@ -936,9 +908,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 +1078,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 +1093,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 +1109,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 +1211,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()
+153 -459
View File
@@ -21,6 +21,7 @@ end
-- slash command to toggle unitframe test mode
pfUI.api.RegisterSlashCommand("PFTEST", { "/pftest", "/pfuftest" }, function()
pfUI.uf.showall = not pfUI.uf.showall
if pfUI.uf.raid and pfUI.uf.raid.LayoutPets then pfUI.uf.raid:LayoutPets() end
end, true)
-- HoT buff indicators that need name verification because their icons are
@@ -126,115 +127,16 @@ visibilityscan:SetScript("OnUpdate", function()
end)
-- ============================================================================
-- GetUnitStats - Nampower Integration for Health + Power
-- GetUnitStats - health + power for a unit token.
-- Returns: hp, maxHp, power, maxPower, powerType
-- IMPORTANT: Uses _G.UnitExists directly to avoid conflicts with Nampower's
-- use UnitGUID(unit) for GUID lookup (Nampower 3.0.0+)
-- ============================================================================
-- Cache für Stats-Tracking (nur Änderungen zählen)
pfUI.api.lastUnitStats = pfUI.api.lastUnitStats or {}
function pfUI.api.GetUnitStats(unitstr, trackStats)
local hp, maxHp, power, maxPower, powerType
local usedNampower = false
-- Try GetUnitField first if available (for all units: players, pets, NPCs)
if GetUnitField then
-- Use the standard check first, then get guid separately
local exists = _G.UnitExists(unitstr)
if exists then
-- Get guid via the extended UnitExists for Nampower
local _, guid = _G.UnitExists(unitstr)
if guid then
hp = GetUnitField(guid, "health")
maxHp = GetUnitField(guid, "maxHealth")
-- Get power type from bytes0
local bytes0 = GetUnitField(guid, "bytes0")
if bytes0 then
local temp = math.floor(bytes0 / 16777216)
powerType = temp - math.floor(temp / 256) * 256
else
powerType = UnitPowerType(unitstr) or 0
end
-- Get power values based on type
if powerType == 1 then
-- Rage (Nampower stores rage * 10, maxPower2 stores maxRage * 10)
local rage = GetUnitField(guid, "power2")
local maxRage = GetUnitField(guid, "maxPower2")
power = rage and math.floor(rage / 10) or UnitMana(unitstr)
maxPower = maxRage and math.floor(maxRage / 10) or UnitManaMax(unitstr)
elseif powerType == 3 then
-- Energy
power = GetUnitField(guid, "power4") or UnitMana(unitstr)
if power then power = math.floor(power) end
maxPower = GetUnitField(guid, "maxPower4") or UnitManaMax(unitstr)
elseif powerType == 2 then
-- Focus (Hunter pets use power3)
power = GetUnitField(guid, "power3") or UnitMana(unitstr)
if power then power = math.floor(power) end
maxPower = GetUnitField(guid, "maxPower3") or UnitManaMax(unitstr)
else
-- Mana (default)
power = GetUnitField(guid, "power1") or UnitMana(unitstr)
maxPower = GetUnitField(guid, "maxPower1") or UnitManaMax(unitstr)
end
-- Check if Nampower gave valid health data
if hp and hp > 0 and maxHp and maxHp > 0 then
usedNampower = true
-- Track Nampower success - NUR bei echten Änderungen
if trackStats and pfUI.uf and pfUI.uf.stats and pfUI.uf.stats.enabled then
local lastStats = pfUI.api.lastUnitStats[unitstr]
if not lastStats or lastStats.hp ~= hp or lastStats.maxHp ~= maxHp or
lastStats.power ~= power or lastStats.maxPower ~= maxPower then
pfUI.uf.stats.nampowerUsed = (pfUI.uf.stats.nampowerUsed or 0) + 1
pfUI.api.lastUnitStats[unitstr] = {
hp = hp,
maxHp = maxHp,
power = power,
maxPower = maxPower
}
end
end
return hp, maxHp, power or 0, maxPower or 1, powerType
end
end
end
end
-- Fallback to standard API (for players when Nampower fails)
hp = UnitHealth(unitstr) or 0
maxHp = UnitHealthMax(unitstr) or 1
powerType = UnitPowerType(unitstr) or 0
power = UnitMana(unitstr) or 0
maxPower = UnitManaMax(unitstr) or 1
-- Track Fallback usage - NUR bei echten Änderungen
if trackStats and not usedNampower then
if pfUI.uf and pfUI.uf.stats and pfUI.uf.stats.enabled then
local lastStats = pfUI.api.lastUnitStats[unitstr]
if not lastStats or lastStats.hp ~= hp or lastStats.maxHp ~= maxHp or
lastStats.power ~= power or lastStats.maxPower ~= maxPower then
pfUI.uf.stats.fallbackUsed = (pfUI.uf.stats.fallbackUsed or 0) + 1
pfUI.api.lastUnitStats[unitstr] = {
hp = hp,
maxHp = maxHp,
power = power,
maxPower = maxPower
}
end
end
end
return hp, maxHp, power, maxPower, powerType
function pfUI.api.GetUnitStats(unitstr)
local powerType = UnitPowerType(unitstr) or 0
return UnitHealth(unitstr) or 0,
UnitHealthMax(unitstr) or 1,
UnitPower(unitstr, powerType) or 0,
UnitPowerMax(unitstr, powerType) or 1,
powerType
end
local aggrodata = { }
@@ -293,7 +195,7 @@ function pfUI.uf:UpdateVisibility()
-- cache result of strsub to avoid repeating calls
if not self.cache_raid then
if strsub(self:GetName(),0,6) == "pfRaid" then
self.cache_raid = tonumber(strsub(self:GetName(),7,8))
self.cache_raid = tonumber(strsub(self:GetName(),7,8)) or 0
else
self.cache_raid = 0
end
@@ -304,7 +206,7 @@ function pfUI.uf:UpdateVisibility()
local id = self.cache_raid
-- always show self in raidframes
if not IsInRaid() and IsInGroup() and C.unitframes.selfinraid == "1" and id == 1 then
if not IsInGroup() and C.unitframes.selfinraid == "1" and id == 1 then
self.id = ""
self.label = "player"
@@ -338,6 +240,12 @@ function pfUI.uf:UpdateVisibility()
local unitstr = string.format("%s%s", self.label or "", self.id or "")
local visibility = string.format("[target=%s,exists] show; hide", unitstr)
-- Group frames are redundant when the group is already shown as a raid grid:
-- either an actual raid, or a party promoted to the raid grid via
-- raidforgroup. hide_in_raid gates whether we hide them in that case.
local hide_group = C["unitframes"]["group"]["hide_in_raid"] == "1"
and (IsInRaid() or (C.unitframes.raidforgroup == "1" and IsInGroup()))
if pfUI.unlock and pfUI.unlock:IsShown() then
-- display during unlock mode
visibility = "show"
@@ -346,13 +254,14 @@ function pfUI.uf:UpdateVisibility()
-- frame shall not be visible
visibility = "hide"
self.visible = nil
elseif C["unitframes"]["group"]["hide_in_raid"] == "1" and self.label and strsub(self.label,0,5) == "party" and IsInRaid() then
-- hide group while in raid and option is set
elseif hide_group and self.cache_raid == 0 and self.label and strsub(self.label,0,5) == "party" then
-- hide group while shown as a raid grid and option is set (raid frames
-- carry label "party" under raidforgroup, so exclude them by cache_raid)
visibility = "hide"
self.visible = nil
elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" )
and (not IsInGroup() or (C["unitframes"]["group"]["hide_in_raid"] == "1" and IsInRaid())) then
-- hide self in group if solo or hide in raid is set
and (not IsInGroup() or hide_group) then
-- hide self in group if solo or shown as a raid grid
visibility = "hide"
self.visible = nil
end
@@ -372,6 +281,11 @@ function pfUI.uf:UpdateVisibility()
self:Hide()
return
end
elseif self.label == "raidpet" then
if not UnitIsVisible(unitstr) or not UnitExists("raid" .. self.id) then
self:Hide()
return
end
elseif self.label == "pettarget" then
if not UnitIsVisible(unitstr) or not UnitExists("pet") then
self:Hide()
@@ -406,19 +320,16 @@ function pfUI.uf:UpdateFrameSize()
if self.config.portrait == "left" or self.config.portrait == "right" then
if ptwidth == "-1" and ptheight == "-1" then
-- align portrait size to frame
self.portrait:SetWidth(real_height)
self.portrait:SetHeight(real_height)
self.portrait:SetSize(real_height, real_height)
portrait = real_height + spacing + 2*default_border
else
-- use custom portrait size
self.portrait:SetWidth(ptwidth)
self.portrait:SetHeight(ptheight)
self.portrait:SetSize(ptwidth, ptheight)
portrait = ptwidth + spacing + 2*default_border
end
end
self:SetWidth(width + portrait)
self:SetHeight(real_height)
self:SetSize(width + portrait, real_height)
end
function pfUI.uf:UpdateConfig()
@@ -455,8 +366,7 @@ function pfUI.uf:UpdateConfig()
f.glow:SetScript("OnUpdate", pfUI.uf.glow.UpdateGlowAnimation)
f.glow:Hide()
f.combat:SetWidth(tonumber(f.config.squaresize))
f.combat:SetHeight(tonumber(f.config.squaresize))
f.combat:SetSize(tonumber(f.config.squaresize), tonumber(f.config.squaresize))
f.combat:ClearAllPoints()
f.combat:SetPoint(f.config.squarepos, 0, 0)
f.combat:Hide()
@@ -464,8 +374,7 @@ function pfUI.uf:UpdateConfig()
f.hp:ClearAllPoints()
f.hp:SetPoint("TOP", 0, 0)
f.hp:SetWidth(f.config.width)
f.hp:SetHeight(f.config.height)
f.hp:SetSize(f.config.width, f.config.height)
if tonumber(f.config.height) < 0 then f.hp:Hide() end
pfUI.api.CreateBackdrop(f.hp, default_border)
@@ -517,6 +426,50 @@ function pfUI.uf:UpdateConfig()
fontstyle = C.global.font_unit_style
end
-- Druid secondary mana bar: texture/color/size/position below the power bar,
-- using its own C.unitframes.druidmana* config. Values are read in
-- UpdateDruidMana; here we only lay it out.
if f.druidmana then
local DC = C.unitframes
local dmTexture = DC.druidmanatexture or "Interface\\AddOns\\pfUI\\img\\bar"
f.druidmana:SetStatusBarTexture(pfUI.media[dmTexture] or dmTexture)
f.druidmana:SetFrameLevel(f:GetFrameLevel() + 5)
local manacolor = f.config.defcolor == "0" and f.config.manacolor or C.unitframes.manacolor
f.druidmana:SetStatusBarColor(GetStringColor(manacolor))
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 dmSpacing = -2 * default_border - dmSpace
f.druidmana:SetHeight(dmHeight)
f.druidmana:ClearAllPoints()
local w = dmWidth ~= "-1" and tonumber(dmWidth) or nil
if w then
f.druidmana:SetWidth(w)
f.druidmana:SetPoint("TOP", f.power, "BOTTOM", dmOffX, dmSpacing + dmOffY)
else
f.druidmana:SetPoint("TOPLEFT", f.power, "BOTTOMLEFT", dmOffX, dmSpacing + dmOffY)
f.druidmana:SetPoint("TOPRIGHT", f.power, "BOTTOMRIGHT", dmOffX, dmSpacing + dmOffY)
end
if not f.druidmana._hasbd then
CreateBackdrop(f.druidmana, default_border)
CreateBackdropShadow(f.druidmana)
f.druidmana._hasbd = true
end
local tr, tg, tb = ManaBarColor[Enum.PowerType.Mana].r, ManaBarColor[Enum.PowerType.Mana].g, ManaBarColor[Enum.PowerType.Mana].b
if C.unitframes.pastel == "1" then
tr, tg, tb = (tr + .75) * .5, (tg + .75) * .5, (tb + .75) * .5
end
f.druidmana.text:SetFont(fontname, fontsize, fontstyle)
f.druidmana.text:SetTextColor(tr, tg, tb, 1)
end
f.portrait.tex:SetAllPoints(f.portrait)
f.portrait.tex:SetTexCoord(.1, .9, .1, .9)
f.portrait.model:SetAllPoints(f.portrait)
@@ -573,11 +526,7 @@ function pfUI.uf:UpdateConfig()
end
if f.group then
if f.config.raidgrouplabel == "1" then
f.group:Show()
else
f.group:Hide()
end
f.group:SetShown(f.config.raidgrouplabel == "1")
local xoff = tonumber(f.config.grouplabelxoff) or 0
local yoff = tonumber(f.config.grouplabelyoff) or 8
@@ -646,8 +595,7 @@ function pfUI.uf:UpdateConfig()
f.powerCenterText:SetPoint("TOPLEFT",f.power.bar, "TOPLEFT", f.config.txtpowercenteroffx, 1 + tonumber(f.config.txtpowercenteroffy))
f.powerCenterText:SetPoint("BOTTOMRIGHT",f.power.bar, "BOTTOMRIGHT", f.config.txtpowercenteroffx, f.config.txtpowercenteroffy)
f.incHeal:SetHeight(f.config.height)
f.incHeal:SetWidth(f.config.width)
f.incHeal:SetSize(f.config.width, f.config.height)
f.incHeal.texture:SetTexture(pfUI.media["img:bar"])
local cr, cg, cb, ca = GetStringColor(f.config.healcolor)
cr, cg, cb, ca = tonumber(cr), tonumber(cg), tonumber(cb), tonumber(ca)
@@ -663,53 +611,46 @@ function pfUI.uf:UpdateConfig()
end
f.ressIcon:SetFrameLevel(16)
f.ressIcon:SetWidth(32)
f.ressIcon:SetHeight(32)
f.ressIcon:SetSize(32, 32)
f.ressIcon:SetPoint("CENTER", f, "CENTER", 0, 4)
f.ressIcon.texture:SetTexture(pfUI.media["img:ress"])
f.ressIcon.texture:SetAllPoints(f.ressIcon)
f.ressIcon:Hide()
f.leaderIcon:SetWidth(10)
f.leaderIcon:SetHeight(10)
f.leaderIcon:SetSize(10, 10)
f.leaderIcon:SetPoint("CENTER", f, "TOPLEFT", 0, 0)
f.leaderIcon.texture:SetTexture("Interface\\GROUPFRAME\\UI-Group-LeaderIcon")
f.leaderIcon.texture:SetAllPoints(f.leaderIcon)
f.leaderIcon:Hide()
f.lootIcon:SetWidth(10)
f.lootIcon:SetHeight(10)
f.lootIcon:SetSize(10, 10)
f.lootIcon:SetPoint("CENTER", f, "LEFT", 0, 0)
f.lootIcon.texture:SetTexture("Interface\\GROUPFRAME\\UI-Group-MasterLooter")
f.lootIcon.texture:SetAllPoints(f.lootIcon)
f.lootIcon:Hide()
f.pvpIcon:SetWidth(f.config.pvpiconsize)
f.pvpIcon:SetHeight(f.config.pvpiconsize)
f.pvpIcon:SetSize(f.config.pvpiconsize, f.config.pvpiconsize)
f.pvpIcon:SetPoint(f.config.pvpiconalign, f, f.config.pvpiconalign, f.config.pvpiconoffx, f.config.pvpiconoffy)
f.pvpIcon.texture:SetTexture(pfUI.media["img:pvp"])
f.pvpIcon.texture:SetAllPoints(f.pvpIcon)
f.pvpIcon.texture:SetVertexColor(1,1,1,.5)
f.pvpIcon:Hide()
f.raidIcon:SetWidth(f.config.raidiconsize)
f.raidIcon:SetHeight(f.config.raidiconsize)
f.raidIcon:SetSize(f.config.raidiconsize, f.config.raidiconsize)
f.raidIcon:SetPoint("CENTER", f, f.config.raidiconalign, f.config.raidiconoffx, f.config.raidiconoffy)
local raidIconTex = C.unitframes.blizzard_raidicons == "1" and "Interface\\TargetingFrame\\UI-RaidTargetingIcons" or pfUI.media["img:raidicons"]
f.raidIcon.texture:SetTexture(raidIconTex)
f.raidIcon.texture:SetAllPoints(f.raidIcon)
f.raidIcon:Hide()
f.restIcon:SetWidth(16)
f.restIcon:SetHeight(16)
f.restIcon:SetSize(16, 16)
f.restIcon:SetPoint("TOP", f, "TOPLEFT", 0, -1)
f.restIcon.texture:SetTexture("Interface\\CharacterFrame\\UI-StateIcon", true)
f.restIcon.texture:SetTexCoord(0, .5, 0, .421875)
f.restIcon.texture:SetAllPoints(f.restIcon)
f.restIcon:Hide()
f.happinessIcon:SetWidth(tonumber(C.unitframes.pet.happinesssize))
f.happinessIcon:SetHeight(tonumber(C.unitframes.pet.happinesssize))
f.happinessIcon:SetSize(tonumber(C.unitframes.pet.happinesssize), tonumber(C.unitframes.pet.happinesssize))
f.happinessIcon:SetPoint("CENTER", f, "TOPLEFT", default_border, -default_border)
f.happinessIcon.texture:SetTexture(pfUI.media["img:neutral"])
f.happinessIcon.texture:SetAllPoints(f.happinessIcon)
@@ -880,11 +821,7 @@ function pfUI.uf:UpdateConfig()
-- immediately show/hide existing cooldown text
if f.debuffs[i].cd.pfCooldownText then
if cooldown_text == 1 then
f.debuffs[i].cd.pfCooldownText:Show()
else
f.debuffs[i].cd.pfCooldownText:Hide()
end
f.debuffs[i].cd.pfCooldownText:SetShown(cooldown_text == 1)
end
f.debuffs[i].id = i
@@ -1014,8 +951,6 @@ function pfUI.uf.OnEvent()
this.update_full = true
-- UNIT_XXX Events
elseif arg1 and (arg1 == this.label .. this.id or (UnitGUID and arg1 == UnitGUID(this.label .. this.id))) then
this.lastEventUpdate = GetTime()
if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then
this.update_portrait = true
elseif event == "UNIT_AURA" then
@@ -1037,154 +972,19 @@ local _GetTime = GetTime
pfUI.uf.now = 0
-- ============================================================================
-- GLOBAL FALLBACK THROTTLE - Limits total fallback updates across ALL frames
-- ============================================================================
pfUI.uf.fallbackThrottle = {
lastUpdate = 0,
interval = 0.1, -- 10 updates per second total (not per frame!)
updatesThisInterval = 0,
maxUpdatesPerInterval = 5 -- Max 5 frames can update per interval
}
-- ============================================================================
-- STATS SYSTEM - Performance tracking for Nampower vs Fallback
-- ============================================================================
pfUI.uf.stats = {
eventUpdates = 0,
heartbeatUpdates = 0,
earlyReturns = 0,
nampowerUsed = 0,
fallbackUsed = 0,
throttledSkips = 0,
startTime = 0,
enabled = true
}
-- Stats Frame (Live Display)
pfUI.uf.statsFrame = CreateFrame("Frame", "pfUIStatsFrame", UIParent)
pfUI.uf.statsFrame:SetWidth(200)
pfUI.uf.statsFrame:SetHeight(220)
pfUI.uf.statsFrame:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", -10, -200)
pfUI.uf.statsFrame:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 8,
insets = { left = 3, right = 3, top = 3, bottom = 3 }
})
pfUI.uf.statsFrame:SetBackdropColor(0, 0, 0, 0.8)
pfUI.uf.statsFrame:EnableMouse(true)
pfUI.uf.statsFrame:SetMovable(true)
pfUI.uf.statsFrame:SetClampedToScreen(true)
pfUI.uf.statsFrame:RegisterForDrag("LeftButton")
pfUI.uf.statsFrame:SetScript("OnDragStart", function() this:StartMoving() end)
pfUI.uf.statsFrame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
pfUI.uf.statsFrame:Hide()
-- Stats Title
pfUI.uf.statsFrame.title = pfUI.uf.statsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
pfUI.uf.statsFrame.title:SetPoint("TOP", pfUI.uf.statsFrame, "TOP", 0, -8)
pfUI.uf.statsFrame.title:SetText("Performance")
-- Stats Text (multi-line)
pfUI.uf.statsFrame.text = pfUI.uf.statsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
pfUI.uf.statsFrame.text:SetPoint("TOPLEFT", pfUI.uf.statsFrame, "TOPLEFT", 10, -30)
pfUI.uf.statsFrame.text:SetWidth(180)
pfUI.uf.statsFrame.text:SetHeight(180)
pfUI.uf.statsFrame.text:SetJustifyH("LEFT")
pfUI.uf.statsFrame.text:SetJustifyV("TOP")
pfUI.uf.statsFrame.text:SetText("Initializing...")
-- Update function for stats display
pfUI.uf.UpdateStatsDisplay = function()
local elapsed = GetTime() - pfUI.uf.stats.startTime
if elapsed < 0.1 then return end
local eventRate = pfUI.uf.stats.eventUpdates / elapsed
local heartbeatRate = pfUI.uf.stats.heartbeatUpdates / elapsed
local totalFrameUpdates = eventRate + heartbeatRate
-- Calculate Nampower vs Fallback percentages (ONLY counts actual data changes!)
local totalDataChanges = pfUI.uf.stats.nampowerUsed + pfUI.uf.stats.fallbackUsed
local nampowerPct = totalDataChanges > 0 and math.floor((pfUI.uf.stats.nampowerUsed / totalDataChanges) * 100) or 0
local fallbackPct = totalDataChanges > 0 and math.floor((pfUI.uf.stats.fallbackUsed / totalDataChanges) * 100) or 0
-- Calculate data change rate (how often HP/Mana actually changes)
local dataChangeRate = totalDataChanges / elapsed
local statsText = string.format(
"Time: %.1fs\n" ..
"|cffaaaaaa--- Frame Updates ---|r\n" ..
"Event: %.1f/s (%d)\n" ..
"Heartbeat: %.1f/s (%d)\n" ..
"Total: %.1f/s\n" ..
"\n" ..
"|cffaaaaaa--- Data Changes ---|r\n" ..
"Rate: %.1f/s (%d)\n" ..
"|cff00ff00NP: %d%% (%d)|r\n" ..
"|cffff8800FB: %d%% (%d)|r",
elapsed,
eventRate,
pfUI.uf.stats.eventUpdates,
heartbeatRate,
pfUI.uf.stats.heartbeatUpdates,
totalFrameUpdates,
dataChangeRate,
totalDataChanges,
nampowerPct,
pfUI.uf.stats.nampowerUsed,
fallbackPct,
pfUI.uf.stats.fallbackUsed
)
pfUI.uf.statsFrame.text:SetText(statsText)
end
-- Stats update timer
pfUI.uf.statsUpdateTimer = 0
-- Cache cleanup timer (clean lastUnitStats every 30s to prevent memory leak)
pfUI.uf.cacheCleanupTimer = 0
-- ============================================================================
-- OnUpdate with Heartbeat Polling and Fallback
-- OnUpdate - eventless per-frame work (range check, aggro glow) and draining
-- the event-set update flags. Frames refresh on events only; no polling.
-- ============================================================================
function pfUI.uf.OnUpdate()
local now = _GetTime()
pfUI.uf.now = now
-- Update stats display (throttled to 0.2s)
if pfUI.uf.statsFrame and pfUI.uf.statsFrame:IsShown() then
if (pfUI.uf.statsUpdateTimer or 0) <= now then
pfUI.uf.statsUpdateTimer = now + 0.2
if pfUI.uf.stats.startTime > 0 then
pfUI.uf.UpdateStatsDisplay()
end
end
end
-- Cleanup lastUnitStats cache every 30 seconds to prevent memory leak
if (pfUI.uf.cacheCleanupTimer or 0) <= now then
pfUI.uf.cacheCleanupTimer = now + 30
-- Only keep cache for units that currently exist
if pfUI.api.lastUnitStats then
for unitstr in pairs(pfUI.api.lastUnitStats) do
if not _G.UnitExists(unitstr) then
pfUI.api.lastUnitStats[unitstr] = nil
end
end
end
end
-- update combat feedback (no throttle - needs immediate feedback)
if this.feedbackText then CombatFeedback_OnUpdate(arg1) end
-- Throttle raid/party frames for performance
if this.label == "raid" or this.label == "party" then
if (this.throttleTick or 0) > now then
if pfUI.uf.stats and pfUI.uf.stats.enabled then
pfUI.uf.stats.throttledSkips = pfUI.uf.stats.throttledSkips + 1
end
return
end
this.throttleTick = now + 0.1 -- Default: 10 FPS
@@ -1268,19 +1068,7 @@ function pfUI.uf.OnUpdate()
local unit = this.label .. this.id
local heal = libpredict:UnitGetIncomingHeals(unit)
-- O(1) Nampower lookup via GUID (same pattern as nameplates.lua)
local health, maxHealth
if GetUnitField then
local guid = UnitGUID(unit)
if guid then
health = GetUnitField(guid, "health")
maxHealth = GetUnitField(guid, "maxHealth")
end
end
-- Fallback to standard API
if not health or not maxHealth or maxHealth == 0 then
health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
end
local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
if heal - health - maxHealth ~= this.predictstate then
local overhealperc = tonumber(this.config.overhealperc)
@@ -1337,86 +1125,6 @@ function pfUI.uf.OnUpdate()
-- EVENT-BASED UPDATES (Health, Mana, Auras, etc.)
-- ============================================================================
-- Check if we have pending updates from events
local hasUpdates = this.update_full or this.update_base or
this.update_aura or this.update_portrait or
this.update_pvp or this.update_indicators
-- Track event-triggered updates (not API calls, just frame updates)
if hasUpdates and pfUI.uf.stats and pfUI.uf.stats.enabled then
pfUI.uf.stats.eventUpdates = pfUI.uf.stats.eventUpdates + 1
end
-- Heartbeat Polling: If no events pending, check if we need fallback
if not hasUpdates then
local timeSinceEvent = this.lastEventUpdate and (now - this.lastEventUpdate) or 999
-- If >0.5s since last event and unit exists, try heartbeat
if timeSinceEvent > 0.5 and this.label and _G.UnitExists(this.label .. this.id) then
local needsFallback = false
-- Check if Nampower can provide data
if GetUnitField then
-- Use _G.UnitExists to avoid conflicts with range checking
local unitstr = this.label .. this.id
local exists = _G.UnitExists(unitstr)
if exists then
local _, guid = _G.UnitExists(unitstr)
if guid then
local hp = GetUnitField(guid, "health")
if not hp or hp == 0 then
needsFallback = true
end
else
needsFallback = true
end
else
needsFallback = true
end
else
needsFallback = true
end
if needsFallback then
-- GLOBAL Throttle: Limit fallback updates across ALL frames
local throttle = pfUI.uf.fallbackThrottle
-- Reset counter each interval
if now - throttle.lastUpdate > throttle.interval then
throttle.lastUpdate = now
throttle.updatesThisInterval = 0
end
-- Check if we've exceeded max updates this interval
if throttle.updatesThisInterval >= throttle.maxUpdatesPerInterval then
if pfUI.uf.stats and pfUI.uf.stats.enabled then
pfUI.uf.stats.earlyReturns = pfUI.uf.stats.earlyReturns + 1
end
return
end
throttle.updatesThisInterval = throttle.updatesThisInterval + 1
-- Nampower not available or no data - trigger fallback update
this.update_base = true
if pfUI.uf.stats and pfUI.uf.stats.enabled then
pfUI.uf.stats.heartbeatUpdates = pfUI.uf.stats.heartbeatUpdates + 1
end
else
-- Nampower working fine, no update needed
if pfUI.uf.stats and pfUI.uf.stats.enabled then
pfUI.uf.stats.earlyReturns = pfUI.uf.stats.earlyReturns + 1
end
return
end
else
-- Too soon or unit doesn't exist
if pfUI.uf.stats and pfUI.uf.stats.enabled then
pfUI.uf.stats.earlyReturns = pfUI.uf.stats.earlyReturns + 1
end
return
end
end
-- process indicator update events
if this.update_indicators then
@@ -1632,6 +1340,19 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f.power = CreateFrame("Frame",nil, f)
f.power.bar = CreateStatusBar(nil, f.power)
-- Druid secondary mana bar: shows base mana (read via ClassicAPI
-- UnitPower(unit, Enum.PowerType.Mana), which works regardless of the active
-- power) while shapeshifted into a form that uses energy/rage. Player +
-- target only;
-- styled/positioned in UpdateConfig, driven in UpdateDruidMana.
if C.unitframes.druidmanabar == "1" and (f.label == "player" or f.label == "target") then
f.druidmana = CreateFrame("StatusBar", "pfDruidMana_" .. f.label .. f.id, f)
f.druidmana.text = f.druidmana:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
f.druidmana.text:SetPoint("CENTER", f.druidmana, "CENTER", 0, 0)
f.druidmana.text:SetJustifyH("CENTER")
f.druidmana:Hide()
end
f.glow = CreateFrame("Frame", nil, f)
f.combat = CreateFrame("Frame", nil, f.hp.bar)
f.combat.tex = f.combat:CreateTexture(nil, "OVERLAY")
@@ -1841,6 +1562,32 @@ function pfUI.uf:RefreshIndicators(unit)
end
end
-- Druid secondary mana bar update. Reads base mana with ClassicAPI
-- UnitPower/UnitPowerMax(unit, Enum.PowerType.Mana), which return the mana
-- slot regardless of the unit's active power -- so it works while shapeshifted,
-- with no nampower/GetUnitField dependency. Shown only while off mana
-- (Cat=energy, Bear=rage); non-player frames only for druid units.
function pfUI.uf:UpdateDruidMana(unit)
local bar = unit.druidmana
local unitstr = unit.label .. unit.id
if not UnitExists(unitstr) then bar:Hide() return end
if unit.label ~= "player" then
local _, cls = UnitClass(unitstr)
if cls ~= "DRUID" then bar:Hide() return end
end
if UnitPowerType(unitstr) == Enum.PowerType.Mana then bar:Hide() return end
local mana, maxmana = UnitPower(unitstr, Enum.PowerType.Mana), UnitPowerMax(unitstr, Enum.PowerType.Mana)
if not maxmana or maxmana == 0 then bar:Hide() return end
bar:SetMinMaxValues(0, maxmana)
bar:SetValue(mana)
if C.unitframes.druidmanatext == "1" then
bar.text:SetText(pfUI.api.Abbreviate(mana) .. "/" .. pfUI.api.Abbreviate(maxmana))
else
bar.text:SetText("")
end
bar:Show()
end
function pfUI.uf:RefreshUnit(unit, component)
-- break early on misconfigured UF's
if not unit.label then return end
@@ -2261,7 +2008,7 @@ function pfUI.uf:RefreshUnit(unit, component)
-- base frame
if component == "all" or component == "base" then
-- Unit HP/MP with Nampower Integration
local hp, hpmax, power, powermax, powerType = pfUI.api.GetUnitStats(unitstr, true)
local hp, hpmax, power, powermax, powerType = pfUI.api.GetUnitStats(unitstr)
-- Store original values for color calculations (before invert_healthbar modifies hp)
local hp_orig, hpmax_orig = hp, hpmax
@@ -2345,13 +2092,13 @@ function pfUI.uf:RefreshUnit(unit, component)
local r, g, b, a = .5, .5, .5, 1
local utype = UnitPowerType(unitstr)
if utype == 0 then
if utype == Enum.PowerType.Mana then
r, g, b, a = GetStringColor(mana)
elseif utype == 1 then
elseif utype == Enum.PowerType.Rage then
r, g, b, a = GetStringColor(rage)
elseif utype == 2 then
elseif utype == Enum.PowerType.Focus then
r, g, b, a = GetStringColor(focus)
elseif utype == 3 then
elseif utype == Enum.PowerType.Energy then
r, g, b, a = GetStringColor(energy)
end
@@ -2387,6 +2134,8 @@ function pfUI.uf:RefreshUnit(unit, component)
end
end
if unit.druidmana then pfUI.uf:UpdateDruidMana(unit) end
pfUI.uf:RefreshUnitState(unit)
end
end
@@ -2834,7 +2583,7 @@ function pfUI.uf:GetStatusValue(unit, pos)
end
-- Get stats with Nampower Integration
local hp, hpmax, mp, mpmax, powerType = pfUI.api.GetUnitStats(unitstr, true)
local hp, hpmax, mp, mpmax, powerType = pfUI.api.GetUnitStats(unitstr)
local rhp, rhpmax = hp, hpmax
-- Use libhealth for mob health estimation (overrides Nampower/Standard)
@@ -2864,6 +2613,19 @@ function pfUI.uf:GetStatusValue(unit, pos)
else
return ""
end
elseif config == "ownername" then
local owner
if unit.label == "raidpet" then owner = "raid" .. unit.id
elseif unit.label == "partypet" then owner = "party" .. unit.id
elseif unit.label == "pet" then owner = "player" end
if not (owner and UnitExists(owner)) then return "" end
local color = ""
if unit.config["classcolor"] == "1" and UnitIsPlayer(owner) then
local _, r, g, b = GetUnitColor(owner)
if C.unitframes.pastel == "1" then r, g, b = (r+.75)*.5, (g+.75)*.5, (b+.75)*.5 end
color = rgbhex(r, g, b)
end
return color .. pfUI.uf:GetNameString(owner)
-- health
elseif config == "health" then
@@ -2923,7 +2685,7 @@ function pfUI.uf:GetStatusValue(unit, pos)
elseif config == "powermax" then
return unit:GetColor("power") .. pfUI.api.Abbreviate(mpmax)
elseif config == "powerperc" then
local perc = UnitManaMax(unitstr) > 0 and ceil(mp / mpmax * 100) or 0
local perc = UnitPowerMax(unitstr) > 0 and ceil(mp / mpmax * 100) or 0
return unit:GetColor("power") .. perc
elseif config == "powermiss" then
local power = ceil(mp - mpmax)
@@ -2934,7 +2696,7 @@ function pfUI.uf:GetStatusValue(unit, pos)
end
elseif config == "powerdyn" then
-- show percentage when only mana is less than 100%
if mp ~= mpmax and UnitPowerType(unitstr) == 0 then
if mp ~= mpmax and UnitPowerType(unitstr) == Enum.PowerType.Mana then
return unit:GetColor("power") .. pfUI.api.Abbreviate(mp) .. " - " .. ceil(mp / mpmax * 100) .. "%"
else
return unit:GetColor("power") .. pfUI.api.Abbreviate(mp)
@@ -2977,19 +2739,7 @@ function pfUI.uf.GetColor(self, preset)
r, g, b = color.r, color.g, color.b
elseif preset == "health" and config["healthcolor"] == "1" then
-- O(1) Nampower lookup for health gradient color
local hp, hpmax
if GetUnitField then
local guid = UnitGUID(unitstr)
if guid then
hp = GetUnitField(guid, "health")
hpmax = GetUnitField(guid, "maxHealth")
end
end
-- Fallback to standard API
if not hp or not hpmax then
hp, hpmax = UnitHealth(unitstr), UnitHealthMax(unitstr)
end
local hp, hpmax = UnitHealth(unitstr), UnitHealthMax(unitstr)
if hpmax and hpmax > 0 then
r, g, b = GetColorGradient(hp / hpmax)
else
@@ -3011,59 +2761,3 @@ function pfUI.uf.GetColor(self, preset)
return rgbhex(r,g,b)
end
-- ============================================================================
-- Slash Commands for Stats Frame
-- ============================================================================
pfUI.api.RegisterSlashCommand("PFUISTATS", { "/pfuistats", "/ufstats" }, function(msg)
msg = string.lower(msg or "")
if not pfUI.uf.stats then
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000ERROR:|r Stats not initialized!")
return
end
-- Initialize startTime on first use
if pfUI.uf.stats.startTime == 0 then
pfUI.uf.stats.startTime = GetTime()
end
if msg == "reset" then
pfUI.uf.stats.eventUpdates = 0
pfUI.uf.stats.heartbeatUpdates = 0
pfUI.uf.stats.earlyReturns = 0
pfUI.uf.stats.nampowerUsed = 0
pfUI.uf.stats.fallbackUsed = 0
pfUI.uf.stats.throttledSkips = 0
pfUI.uf.stats.startTime = GetTime()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00pfUI Stats:|r Reset!")
elseif msg == "toggle" then
pfUI.uf.stats.enabled = not pfUI.uf.stats.enabled
local status = pfUI.uf.stats.enabled and "|cff00ff00ON|r" or "|cffff0000OFF|r"
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00pfUI Stats:|r Tracking: " .. status)
elseif msg == "show" then
if pfUI.uf.statsFrame then
pfUI.uf.statsFrame:Show()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00pfUI Stats:|r Frame shown")
end
elseif msg == "hide" then
if pfUI.uf.statsFrame then
pfUI.uf.statsFrame:Hide()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00pfUI Stats:|r Frame hidden")
end
else
-- Toggle frame (default action)
if pfUI.uf.statsFrame then
if pfUI.uf.statsFrame:IsShown() then
pfUI.uf.statsFrame:Hide()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00pfUI Stats:|r Frame hidden")
else
pfUI.uf.statsFrame:Show()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00pfUI Stats:|r Frame shown")
end
end
end
end, true)
-9
View File
@@ -29,15 +29,6 @@ UNITFRAME_SECURE_TEMPLATE = nil
--[[ Vanilla API Extensions ]]--
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 }
obj = setmetatable(obj, {__index = function(tab,key)
+5 -1
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,
@@ -568,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,
@@ -615,6 +616,7 @@ 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,
@@ -702,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,
@@ -738,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,
+5 -1
View File
@@ -28,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,
@@ -126,6 +125,7 @@ pfUI_translation["enUS"] = {
["Click Casting"] = nil,
["Clock"] = nil,
["Close"] = nil,
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = nil,
["Color Debuff Stacks"] = nil,
@@ -576,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,
@@ -623,6 +624,7 @@ 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,
@@ -711,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,
@@ -747,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,
+5 -1
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",
@@ -568,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",
@@ -615,6 +616,7 @@ 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",
@@ -702,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",
@@ -738,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",
+5 -1
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",
@@ -568,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",
@@ -615,6 +616,7 @@ 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",
@@ -702,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,
@@ -738,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",
+5 -1
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,
@@ -568,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"] = "패널",
@@ -615,6 +616,7 @@ 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,
@@ -702,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,
@@ -738,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,
+5 -1
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"] = "Цвет стаков дебаффа",
@@ -568,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"] = "Панель",
@@ -615,6 +616,7 @@ 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"] = "Случайный бросок костей для",
@@ -702,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"] = "Показать частоту кадров и задержку в цвете",
@@ -738,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"] = "Показать стаки",
+5 -1
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堆叠颜色",
@@ -568,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"] = "面板",
@@ -615,6 +616,7 @@ 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点 物品:",
@@ -702,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"] = "显示帧数以及延迟颜色",
@@ -739,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"] = "显示堆叠",
+5 -1
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,
@@ -568,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"] = "面板",
@@ -615,6 +616,7 @@ 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點 物品:",
@@ -702,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,
@@ -738,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,
+39 -5
View File
@@ -53,10 +53,20 @@ 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.
@@ -181,10 +191,28 @@ 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
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.
@@ -274,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)
+58 -7
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
-- 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
@@ -762,6 +764,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 +1176,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
+4 -6
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)
+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
+11 -4
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()
@@ -343,7 +350,7 @@ pfUI:RegisterModule("bags", function ()
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")
@@ -448,7 +455,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
@@ -993,7 +1000,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
@@ -1231,7 +1238,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 -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
+5 -7
View File
@@ -90,11 +90,11 @@ 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 ""
if spellID then
rank = C_Spell.GetSpellSubtext(spellID) or ""
end
local spellname = (cb.showname and name) and (name .. " ") or ""
local rankstr = (cb.showrank and rank ~= "") and string.format("|cffaaffcc[%s]|r", rank) or ""
@@ -103,8 +103,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
@@ -169,8 +168,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()
+6 -6
View File
@@ -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
+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
+5 -3
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()
+16 -3
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,9 +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["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)
@@ -2142,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)
@@ -2184,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()
@@ -2233,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")
@@ -2277,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")
@@ -2424,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()
+1 -5
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 }
+9 -26
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)
@@ -158,8 +156,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 +168,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 +197,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 +229,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 +238,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)
+165 -137
View File
@@ -15,8 +15,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
@@ -63,13 +61,20 @@ pfUI:RegisterModule("nameplates", function ()
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 by nampower's SPELL_START_OTHER events and
-- cleared on SPELL_FAILED_OTHER / plate removal / expiry. This replaces the
-- old per-tick C_Spell poll on every visible plate: cast detection is now
-- 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 = {}
local targetPlateGuid = nil
-- One-shot C_Spell poll. Only used to seed a plate that spawns while its
-- unit is already mid-cast (its SPELL_START_OTHER fired before the plate
-- existed). 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,8 +93,21 @@ 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 }
@@ -161,18 +179,12 @@ pfUI:RegisterModule("nameplates", function ()
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 +203,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 +216,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")
@@ -468,10 +474,13 @@ 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")
-- nampower cast lifecycle for other units (gated by NP_EnableSpell{Start,Go}
-- Events, enabled by libdebuff). Drives castbars event-first instead of
-- polling C_Spell on every plate each tick. Mirrors castbar.lua's target bar.
nameplates:RegisterEvent("SPELL_START_OTHER")
nameplates:RegisterEvent("SPELL_FAILED_OTHER")
nameplates:SetScript("OnEvent", function()
-- Stop event handling during logout to prevent crash 132
@@ -489,6 +498,7 @@ end
CacheConfig()
this:SetGameVariables()
RebuildRaidGuidCache()
targetPlateGuid = UnitExists("target") and UnitGUID("target") or nil
end
-- Handle friendly zone nameplate disable feature
@@ -544,10 +554,20 @@ 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)
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 SPELL_START_OTHER 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
@@ -560,53 +580,92 @@ end
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
if castState[guid] then castState[guid] = nil end
if plateByGuid[guid] then plateByGuid[guid] = nil end
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
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
-- ClassicAPI: fires with arg1 == "nameplateN" when a unit's flags change
-- (stun, combat enter/leave). Flag that plate for an immediate update,
-- bypassing the throttle. Guard on the token prefix -- UNIT_FLAGS also
-- fires for target/party/raid, which aren't ours to handle here.
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 == "SPELL_START_OTHER" then
-- nampower: arg2=spellId, arg3=casterGuid, arg6=castTime(ms),
-- arg7=channel duration(ms, 0 if not a channel), arg8=spellType
-- (1 = channel). Cache the cast only for a unit we have a plate for, so
-- the table stays bounded to on-screen casters.
local casterGuid = arg3
local plate = casterGuid and plateByGuid[casterGuid]
if plate then
local isChannel = arg8 == 1
local durationMs = isChannel and arg7 or arg6
if durationMs and durationMs > 0 then
local spellId = arg2
local now = GetTime()
castState[casterGuid] = {
spellName = C_Spell.GetSpellName(spellId),
spellID = spellId,
icon = C_Spell.GetSpellTexture(spellId),
startTime = now,
endTime = now + durationMs / 1000,
duration = durationMs / 1000,
isChannel = isChannel,
}
plate.castUpdate = true -- bypass the throttle so the bar shows now
end
end
elseif event == "SPELL_FAILED_OTHER" then
-- nampower: arg1=casterGuid, arg2=spellId. Clear on interrupt/failure.
local casterGuid = arg1
if casterGuid and castState[casterGuid] then
castState[casterGuid] = nil
local plate = plateByGuid[casterGuid]
if plate then plate.castUpdate = true 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
targetPlateGuid = UnitExists("target") and UnitGUID("target") or nil
-- 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
@@ -623,7 +682,7 @@ end
-- PERF: Cache GetTime() once per frame
frameState.now = now
frameState.hasTarget, frameState.targetGuid = UnitExists("target")
frameState.hasMouseover = UnitExists("mouseover")
frameState.mouseoverGuid = UnitGUID("mouseover")
-- propagate events to all nameplates
if this.eventcache then
@@ -840,8 +899,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 +928,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
@@ -944,7 +999,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 +1043,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 +1125,7 @@ end
if guild and C.nameplates.showguildname == "1" then
plate.guild:SetText(guild)
if guild == GetGuildInfo("player") then
if UnitIsInMyGuild(plate.unit) then
plate.guild:SetTextColor(0, 0.9, 0, 1)
else
plate.guild:SetTextColor(0.8, 0.8, 0.8, 1)
@@ -1091,15 +1144,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 +1203,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 +1323,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)
@@ -1302,7 +1343,7 @@ end
-- 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 +1387,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 +1415,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 +1423,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
@@ -1441,13 +1482,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 +1530,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
@@ -1515,7 +1550,7 @@ end
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 +1559,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 +1625,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 +1641,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 +1661,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 targetPlateGuid then return end
local nameplate = plateByGuid[targetPlateGuid]
if not nameplate then return end
nameplates.UpdateCastbar(nameplate, GetTime())
end)
-- set nameplate game settings
+17 -216
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,18 +128,10 @@ 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
@@ -158,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) .. ")")
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)
+2 -2
View File
@@ -4,7 +4,7 @@ pfUI:RegisterModule("newitem", function ()
pfUI.newitem = {}
local color = CreateColor(strsplit(",", C.appearance.bags.newitem_color))
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
@@ -18,7 +18,7 @@ pfUI:RegisterModule("newitem", function ()
local glow = frame:CreateTexture(nil, "OVERLAY")
glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
glow:SetBlendMode("ADD")
glow:SetVertexColor(color:GetRGBA())
glow:SetVertexColor(r, g, b, a)
glow:SetPoint("CENTER", frame, "CENTER")
glow:Hide()
glow.RefreshSize = function(g)
+10 -13
View File
@@ -47,7 +47,7 @@ pfUI:RegisterModule("player", function ()
return string.format("%02X%02X%02X", r * 255, g * 255, b * 255)
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" }
@@ -60,16 +60,14 @@ 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.
-- 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 showHaste = cfg.display_haste == "1"
local showSP = cfg.display_spellpower == "1"
@@ -79,21 +77,20 @@ pfUI:RegisterModule("player", function ()
return
end
local haste = GetUnitField("player", "modCastSpeed")
local haste = UnitSpellHaste("player")
local text = ""
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)
text = string.format("|cff%s%.1f%%|r", hasteHex, haste)
end
if showSP and isSpellCaster then
local schools = { GetSpellPower("net") }
local defSchool = spDefaultSchool[myclass] or 2
local maxSP = schools[defSchool] or 0
local maxSP = GetSpellBonusDamage(defSchool) or 0
local maxColor = spColors[defSchool]
for i = 2, 7 do -- skip physical (1)
local v = schools[i] or 0
for i = 2, 7 do -- skip physical (1); default school seeds the tiebreak
local v = GetSpellBonusDamage(i) or 0
if v > maxSP then
maxSP = v
maxColor = spColors[i]
+116 -8
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
+1 -1
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
+44 -26
View File
@@ -23,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)
local size = tonumber(C.tooltip.cursoroffset) * 2
tooltip.cursor:SetSize(size, size)
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
+17 -18
View File
@@ -1,16 +1,16 @@
pfUI:RegisterModule("totems", function ()
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)
totems.OnEnter = function(self)
@@ -18,9 +18,9 @@ pfUI:RegisterModule("totems", function ()
local spellID = select(7, GetTotemInfo(id))
if not spellID or spellID == 0 then return end
GameTooltip:SetOwner(this, "ANCHOR_LEFT")
GameTooltip:SetSpell(FindSpellBookSlotByID(spellID))
GameTooltip:AddDoubleLine(T["Left Click"], "|cffffffff" .. T["Recast Totem"])
GameTooltip:AddDoubleLine(T["Right Click"], "|cffffffff" .. T["Target Totem"])
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
@@ -29,8 +29,8 @@ pfUI:RegisterModule("totems", function ()
totems.OnClick = function(self)
local id = this:GetID()
if arg1 == "LeftButton" then
local _, name = GetTotemInfo(id)
if name and name ~= "" then CastSpellByName(name) end
local spellID = select(7, GetTotemInfo(id))
if spellID and spellID > 0 then CastSpell(FindSpellBookSlotByID(spellID)) end
elseif arg1 == "RightButton" then
TargetTotem(id)
end
@@ -43,10 +43,9 @@ pfUI:RegisterModule("totems", function ()
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]:SetID(i)
@@ -69,12 +68,12 @@ pfUI:RegisterModule("totems", function ()
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
-6
View File
@@ -80,12 +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}
local delay = CreateFrame("Frame")
delay:SetScript("OnUpdate", function()
this:Hide()
-20
View File
@@ -331,26 +331,6 @@ 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
pfUI.api.RegisterSlashCommand("PFUNITXP", { "/pfunitxp" }, function()
local chat = DEFAULT_CHAT_FRAME
+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
+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])
+1 -1
View File
@@ -23,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 = 10704 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_MIN = 10802 -- (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"
+4 -9
View File
@@ -2,15 +2,10 @@ 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)
hooksecurefunc("AuctionFrame_OnShow", function()
AuctionFrame:ClearAllPoints()
+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()
+1 -2
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)
+5 -9
View File
@@ -131,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)
@@ -177,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")
@@ -236,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")
@@ -257,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")
@@ -356,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 -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()
+6 -24
View File
@@ -6,31 +6,15 @@ pfUI:RegisterSkin("Quest Log", function ()
_G.MAX_WATCHABLE_QUESTS = 20 -- TODO
do -- quest log frame
-- Compatibility
local QUEST_COUNT
if QuestLogCount then -- tbc
QUEST_COUNT = QuestLogCount
StripTextures(QUEST_COUNT)
QUEST_COUNT:ClearAllPoints()
hooksecurefunc("QuestLogUpdateQuestCount", function(numQuests)
QUEST_COUNT:ClearAllPoints()
QUEST_COUNT:SetPoint("BOTTOMRIGHT", QuestLogFrame, "TOPRIGHT", 0, -50)
end)
else -- vanilla
QUEST_COUNT = QuestLogQuestCount
QUEST_COUNT:ClearAllPoints()
QUEST_COUNT:SetPoint("TOPRIGHT", -10, -30)
end
QuestLogQuestCount:ClearAllPoints()
QuestLogQuestCount:SetPoint("TOPRIGHT", -10, -30)
hooksecurefunc("QuestLog_OnShow", function()
QuestLogFrame:ClearAllPoints()
QuestLogFrame:SetPoint("TOPLEFT", 10, -104)
end)
QuestLogFrame:SetWidth(676)
QuestLogFrame:SetHeight(440)
QuestLogFrame:SetSize(676, 440)
QuestLogFrame:DisableDrawLayer("BACKGROUND")
StripTextures(QuestLogFrame, true)
@@ -64,10 +48,9 @@ pfUI:RegisterSkin("Quest Log", function ()
QuestLogFrameLevelsCheckButtonText:SetText(T["Quest Levels"])
CreateBackdrop(QuestLogTrack)
QuestLogTrack:SetHeight(8)
QuestLogTrack:SetWidth(8)
QuestLogTrack:SetSize(8, 8)
QuestLogTrack:ClearAllPoints()
QuestLogTrack:SetPoint("RIGHT", QUEST_COUNT, "LEFT", -5, 0)
QuestLogTrack:SetPoint("RIGHT", QuestLogQuestCount, "LEFT", -5, 0)
StripTextures(QuestLogTrack)
QuestLogTrackTracking:SetTexture(.8,.8,.8,1)
@@ -228,8 +211,7 @@ pfUI:RegisterSkin("Quest Log", function ()
SetAllPointsOffset(item.backdrop, item, 4)
SetHighlight(item)
icon:SetWidth(ysize)
icon:SetHeight(ysize)
icon:SetSize(ysize, ysize)
icon:ClearAllPoints()
icon:SetPoint("LEFT", 6, 0)
icon:SetTexCoord(.08, .92, .08, .92)
+3 -12
View File
@@ -1,13 +1,5 @@
pfUI:RegisterSkin("Readycheck", function ()
HookAddonOrVariable("Blizzard_RaidUI", function()
-- Compatibility
local update_func
if ReadyCheckFrame_OnUpdate then -- tbc
update_func = "ReadyCheckFrame_OnUpdate"
else -- vanilla
update_func = "ReadyCheck_OnUpdate"
end
StripTextures(ReadyCheckFrame, true)
CreateBackdrop(ReadyCheckFrame, nil, nil, .75)
CreateBackdropShadow(ReadyCheckFrame)
@@ -25,8 +17,7 @@ pfUI:RegisterSkin("Readycheck", function ()
local frame = CreateFrame("Button", nil, ReadyCheckFrame)
frame:SetPoint("TOP", ReadyCheckFrameText, "BOTTOM", 0, -6)
frame:SetWidth(220)
frame:SetHeight(10)
frame:SetSize(220, 10)
frame.bar = CreateFrame("StatusBar", "ReadyCheckFrameStatusBar", ReadyCheckFrame)
frame.bar:SetStatusBarTexture(pfUI.media["img:bar"])
@@ -35,7 +26,7 @@ pfUI:RegisterSkin("Readycheck", function ()
frame.bar.text = frame.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
frame.bar.text:SetFontObject(GameFontWhite)
frame.bar.text:SetFont(pfUI.font_default, 12, "OUTLINE")
frame.bar.text:SetPoint("CENTER", 0, 0)
frame.bar.text:SetPoint("CENTER")
local max
hooksecurefunc("ShowReadyCheck", function()
@@ -43,7 +34,7 @@ pfUI:RegisterSkin("Readycheck", function ()
frame.bar:SetMinMaxValues(0, max)
end)
hooksecurefunc(update_func, function()
hooksecurefunc("ReadyCheck_OnUpdate", function()
if not ReadyCheckFrame.timer then return end
local perc = ReadyCheckFrame.timer/max
+1 -8
View File
@@ -4,14 +4,7 @@ pfUI:RegisterSkin("Talents", function ()
HookAddonOrVariable("Blizzard_TalentUI", function()
-- Compatibility
local TALENT_FRAME, TALENT_FRAME_NAME
if PlayerTalentFrame then -- tbc
TALENT_FRAME = _G.PlayerTalentFrame
else -- vanilla
TALENT_FRAME = _G.TalentFrame
end
TALENT_FRAME_NAME = TALENT_FRAME:GetName()
local TALENT_FRAME, TALENT_FRAME_NAME = _G.TalentFrame, _G.TalentFrame:GetName()
StripTextures(TALENT_FRAME)
CreateBackdrop(TALENT_FRAME, nil, nil, .75)