105 Commits
Author SHA1 Message Date
Brues fa1dc5637e Bump ClassicAPI min to 1.7.4
Totem functions!
2026-07-20 22:17:49 -05:00
Brues 8506f5ffbf Localize the totem tooltip click hints
Add a Left Click / Right Click hint to the totem icon tooltip via
AddDoubleLine, matching the panel.lua convention. Reuses the existing
"Left Click" / "Right Click" strings and adds "Recast Totem" /
"Target Totem" as new translation keys (stubbed across all locales; they
fall back to English until translated).

Also drop the now-orphaned "Range Check Interval" string from every
locale manifest -- the setting was removed when librange stopped
scanning, but its translation stubs were left behind.
2026-07-20 22:16:05 -05:00
Brues 9c529512d1 Replace libtotem with ClassicAPI's native totem tracker
ClassicAPI now ships GetTotemInfo/GetTotemTimeLeft/GetTotemDuration/
TargetTotem plus a native PLAYER_TOTEM_UPDATE event, backed by a
data-driven tracker (slot from the Spell.dbc summon effect, duration from
SpellDuration.dbc, and object-manager death detection). That's exactly
what libtotem hand-rolled -- and better -- so delete the library outright:
its spellid/icon tables, the CastSpellByName/CastSpell/UseAction hooks,
the SPELL_GO commit path, and the active-totem bookkeeping.

modules/totems.lua becomes a thin consumer of the native API:
- Driven by PLAYER_TOTEM_UPDATE; drop the shaman tick-poller that only
  existed because vanilla had no totem event.
- Fix the GetTotemInfo call sites: the native 1st return is tool presence,
  not "summoned", so key active state on name/start instead.
- Right-click a totem icon to TargetTotem it; tooltip shows remaining time
  via GetTotemTimeLeft.

modules/turtle-wow.lua: drop the Totemic Recall handler that poked
libtotem:Clean() -- the native tracker detects the totems despawning and
clears the slots itself. Also clean up the now-orphaned translation string
and a stale libtotem mention in libdebuff's comment.
2026-07-20 21:51:08 -05:00
Brues 88d0f7bf74 Use table.wipe to reset spell caches
Clear existing spell cache tables with table.wipe instead of creating new tables when LEARNED_SPELL_IN_TAB fires. This preserves existing table references (spellmaxrank, spellindex, spellinfo), avoiding stale references and potential bugs while being slightly more efficient. Change applied in libs/libspell.lua.
2026-07-20 21:35:31 -05:00
Brues a3ff20782d Use table.wipe and table.insert for tables
Replace custom wipe implementation with a call to table.wipe in api/api.lua and update its doc comment to explain behavior (resets Lua 5.0 length via luaL_setn, advises using table.insert). Replace manual table.getn(t)+1 array appends with table.insert in modules/loot.lua (two sites). Makes table operations safer and more idiomatic, avoiding manual metatable handling and getn-based append idioms.
2026-07-20 21:16:12 -05:00
Brues 88c2462fbd CLASS_SORT_ORDER is already defined by !!!ClassicAPI 2026-07-20 21:15:25 -05:00
Brues e28a7e5606 Optimize isempty function using next() 2026-07-20 18:46:09 -05:00
Brues dfa74c5756 Make the bag sort family-aware and locale-independent
Two fixes to libbagsort's planning stage.

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

Locale-independent categories: SortCategoryPrefix compared the localized
itemType/quality strings from GetItemInfo, which silently miscategorize
everything on a non-enUS client. Switch to the numeric classID/quality
via C_Item.GetItemInfo and ClassicAPI's Enum.ItemClass / Enum.ItemQuality.
2026-07-20 17:46:37 -05:00
Brues 1c0c9dc019 Replace librange's position scan with ClassicAPI's UnitInRange
librange was a per-frame position scanner: it swept party/raid unit
tokens, cached each one's distance via UnitPosition, and answered range
queries from that cache. All of it existed only because 1.12 had no cheap
way to check an arbitrary unit's distance. ClassicAPI's UnitInRange does
exactly that C-side (fixed 40y healing range, position miss reported via
the second return), so the whole library collapses to a direct call.

Wins from dropping the cache:
- No staleness. The scanner's zone-death and roster-reindex bugs simply
  can't exist without a cache to go stale, so this supersedes the
  keep-alive fixes from 756e8840.
- All classes get target-frame range fading. The old target path faked a
  40y check via IsActionInRange on a healing spell found on the action
  bar, so classes without such a spell (GetRangeSlot returned nil) never
  had a working target range check.

The rangecheck == "0" master switch used to be enforced by hiding the
scanner; with no scanner, move that gate into pfUI.api.UnitInRange so
disabling the check still means nothing fades. Threshold is now 40y (the
ClassicAPI constant) rather than the old 45y. Drop the now-dead
rangechecki (Range Check Interval) setting, its GUI row, and migration.
2026-07-20 16:54:27 -05:00
Brues 801935130e Update GetItemLinkByName to use C_Item API
Replace vanilla GetItemInfo and manual link construction with C_Item.GetItemNameByID and C_Item.GetItemInfo
2026-07-20 15:11:50 -05:00
Brues 8cdced5ec0 Simplify modf function
Delegate to ClassicAPI
2026-07-20 15:10:31 -05:00
Brues 756e8840af Keep the rangecheck scanner alive across zones and roster changes
PLAYER_LEAVING_WORLD fires on every loading screen, not just logout, but
librange treated it as terminal: it latched librange_isLoggingOut and
tore OnUpdate off the frame, neither of which was ever restored. So the
first zone (into a BG, dungeon, etc.) permanently killed the distance
scan -- unitdata stopped updating and UnitInSpellRange defaulted every
unit to in-range until /reload, which the next loading screen then undid.
Only PLAYER_LOGOUT is terminal now; PLAYER_LEAVING_WORLD just hides for
the loading screen and PLAYER_ENTERING_WORLD re-shows it.

Also invalidate on RAID_ROSTER_UPDATE / PARTY_MEMBERS_CHANGED: the range
state is cached per unit token, so a roster re-index leaves each unitN
mapped to a different player with stale data. Clear the token cache and
restart the sweep so shifted/joined slots are re-evaluated within one
pass instead of inheriting the previous occupant's range.
2026-07-20 14:12:07 -05:00
Brues b2db091d54 bump min ClassicAPI version 2026-07-20 02:42:34 -05:00
Brues 5fae0bd459 Add priest shadowform auto-paging, sharing the druid-stealth flag
Mirror the druid-stealth page switch for priests: swap to the auto page
(8) while in Shadowform, back to the default page when it drops. Driven
by UPDATE_SHAPESHIFT_FORM (form ID 28), which fully covers shadowform on
its own -- no prowl-style stealth substate to chase.

Fold the new state into the existing prowl machinery instead of
duplicating it: prowl/shadowform are mutually exclusive by class, so one
shared `formpaging` flag and one page constant serve both, and the
OnUpdate switch collapses to a single class-gated block.
2026-07-20 02:40:37 -05:00
Brues 3deddf2b08 Drive questitem off QUEST_ACCEPTED/QUEST_REMOVED instead of rescanning
The item->quest map only changes when a quest enters or leaves the log,
not on progress, yet it was fully rebuilt on every QUEST_LOG_UPDATE burst
(a whole log walk + GetQuestDetails per quest). Maintain it incrementally
from ClassicAPI's QUEST_ACCEPTED / QUEST_REMOVED, keyed on the stable
questID with title/level cached at add time (the old log index shifted on
any add/remove). QUEST_LOG_UPDATE now only drives a one-time login seed
until the detail cache warms, since QUEST_ACCEPTED is suppressed for the
bulk sync; afterward it's a no-op and questing does no rebuilds.
2026-07-19 02:25:23 -05:00
Brues 650bcda001 Allocate newitem glow lazily, only for slots that go new
UpdateSlot previously created the glow texture and OnEnter acknowledge
hook on the first pass over every slot, then toggled visibility. Move
both inside the "is new" branch so a slot only allocates when it actually
holds a new item -- most bag/bank slots never do. Behavior is unchanged:
the glow is still created once per slot and reused, and the hook is
installed exactly when it's first needed.
2026-07-17 16:04:03 -05:00
Brues 931fe6c22d Harden the ClassicAPI-missing path so the disable notice can show
pfUI's TOC depends on the !!!ClassicAPI addon, so a nil CLASSIC_API_VERSION
means the addon loaded but the DLL isn't present -- yet the graceful-disable
branch itself called ClassicAPI APIs (EventUtil.ContinueOnPlayerLogin, and
Mixin/CallbackRegistryMixin for pfUI.events), so it crashed instead of
informing the user.

- Defer the popup with a bare PLAYER_ENTERING_WORLD frame (stock 1.12)
  instead of EventUtil/IsLoggedIn/PLAYER_LOGIN (ClassicAPI-era).
- Read the editbox via getglobal rather than _G.
- Guard pfUI.events creation behind `not pfUI.disabled`.
- Reword the notice: since the addon is present but the DLL isn't (or is
  outdated), tell the user the addon ships bundled with the DLL and to
  delete the !!!ClassicAPI folder and (re)install the latest release.
2026-07-17 15:12:30 -05:00
Brues bd4a40b0f2 Add newitem module: highlight freshly-acquired bag items
New module built on ClassicAPI's C_NewItems + BAG_NEW_ITEMS_UPDATED.
Glows bag slots (bags 0-4) holding items acquired since login, keyed on
item GUID so the flag survives rearranging. Hovering an item
acknowledges it (RemoveNewItem); closing the bags clears the rest via
ClearAll. Glow is UI-ActionButton-Border, sized off the slot width so it
tracks the icon_size config.

To stay decoupled from the bag frames, the bag module now broadcasts a
reusable "bag:closed" event through pfUI.events from its OnHide (carrying
the container so subscribers can tell backpack from bank), guarded so the
initial setup Hide() doesn't fire a phantom close.

Config: appearance.bags.newitem + newitem_color, with GUI toggles.
2026-07-17 14:47:22 -05:00
Brues 31ff2773b3 Defer macrotweak conflicts; use SetSize
Replace the previous RunNextFrame conflict check with per-addon EventUtil.ContinueOnAddOnLoaded calls in modules/macrotweak.lua so macrotweak is disabled as soon as known conflicting addons load. Simplify variable naming and ensure the disabled flag is set correctly. In skins/blizzard/macro.lua replace separate SetHeight/SetWidth calls with a single SetSize(150,22) for MacroEditButton to tidy UI sizing.
2026-07-15 13:45:06 -05:00
Brues 8176b906df Re-anchor tradeskill merge bar to real craft starts
EnterTradeskillMerge sized the merged bar as startMs + single*count, a
zero-latency assumption. Each craft boundary actually costs a server
round-trip, so the real chain runs ~(N-1)*lag longer than the bar
assumed. At the clear the overall fill was clamped to full and looked
done, but the fast per-craft spark tracks real time and came up short by
the accumulated lag -- freezing partway on the final craft.

StartTradeskillCraft now re-pins endTime to each craft's real start
(remaining crafts each still take `single`), so on the last craft
remaining==1 and endTime lands on its true completion. The spark and the
overall progress now reach the right edge together.
2026-07-13 12:34:45 -05:00
Brues 721ecce59d Remove RegisterNewModule call for loothistory 2026-07-13 00:32:21 -05:00
Brues 73b409fb88 Route slash registration through pfUI.api.RegisterSlashCommand
The RegisterSlashCommand helper in api/api.lua was effectively unused
(only macrotweak called it); every other command hand-rolled the
SLASH_*/SlashCmdList pair. Convert the existing manual registrations to
the helper with force=true, preserving the current always-bind behavior
while centralizing the pattern behind one code path (and its _G. and
conflict-check handling).

Left as-is: pfUI.lua's /rl, /pfui, /gm (registered before api.lua
defines the helper) and the vendored libs' debug commands.
2026-07-13 00:06:20 -05:00
Brues 0665764610 cleanup 2026-07-12 15:22:57 -05:00
Brues 3faf06141f Skin Arena Frame 2026-07-12 14:49:17 -05:00
Brues 0c06401ec4 Simplify GUI toggle logic 2026-07-12 14:14:38 -05:00
Brues c68e48111d Remove libtooltip 2026-07-12 14:01:25 -05:00
Brues 6936fd894b Replace unusable tooltip scan with C_PlayerInfo.CanUseItem
The libtipscan approach scanned each bag/bank item's tooltip for red
text, then had to carve out broken (0-durability) items since those also
color red. C_PlayerInfo.CanUseItem checks item requirements directly
(proficiency, level, class/race, skill/spell/rep) and ignores item
state, so broken-but-equippable gear is never flagged and the durability
exclusion drops out entirely. Bank slots resolve through
C_Container.GetContainerItemID(-1, slot) instead of the inventory-slot
workaround the scanner needed.
2026-07-12 13:48:36 -05:00
Brues b5277ea457 These modules aren't new anymore 2026-07-11 16:04:58 -05:00
Brues 63f0dcbf7a Use GetInventoryItemID instead of parsing link 2026-07-11 13:18:48 -05:00
Brues 0344bff471 cleanup 2026-07-11 12:59:33 -05:00
Brues 1510969105 Unused strings 2026-07-11 12:57:07 -05:00
Brues 6e7361c543 Removed redundant compare.basestats setting
Just disable the module if you don't want stat comparison
2026-07-11 12:43:15 -05:00
Brues 2e20a03cbd Restore Inspect UI frame skin
removed during the tbc purge
2026-07-11 12:36:03 -05:00
Brues 7389c241a4 using GetSpellInfo with just a spell id is dangerous
Handful of addons will polyfill their own GetSpellInfo that only accept (bookSlot, bookType) so it's only safe to use C_Spell.GetSpellInfo with just a spell id
2026-07-11 09:47:46 -05:00
Brues fb5230828c Add Loot History module
A pfUI-native group-loot roll history window built on ClassicAPI's
C_LootHistory backport, adapted from the anniversary Blizzard reference.

- Movable/scrollable window (ESC-closable, Clear button) listing rolled
  items; each row expands to per-player rolls.
- Item icon/name/quality rendered via the !!!ClassicAPI Item mixin
  (Item:CreateFromItemLink + ContinueOnItemLoad), so uncached items load
  asynchronously and repaint their row.
- Winner shown on the collapsed row (name/roll/roll-type) and marked in the
  expanded list with a checkmark left of the name (matches the reference).
- Expansion state keys on the stable rollID; events (FULL_UPDATE /
  ROLL_CHANGED / ROLL_COMPLETE) drive a rebuild while shown, and re-attach
  the scroll child so a growing list scrolls without a /reload.
- Toggle via /loothistory or /lh; optional auto-show on new rolls behind
  loothistory.autoshow (default off).
2026-07-11 00:02:31 -05:00
Brues 7aee348a70 Bump min version to 1.6.4 2026-07-10 20:27:16 -05:00
Brues 2a5f480839 Show addon dependencies in tooltip
Add display of required and optional addon dependencies to the addons tooltip. Introduce AddDependencyLines helper in modules/addons.lua which lists dependencies with color coding: green for loaded, yellow for present but unloaded, and red for missing (uses new T["Missing"]). Store dependency arrays on addon frames (adeps / aoptdeps) using GetAddOnDependencies and C_AddOns.GetAddOnOptionalDependencies. Add translation keys 'Dependencies', 'Optional Dependencies', and 'Missing' to env/translations_enUS.lua.
2026-07-10 20:26:05 -05:00
Brues 23d1ab840c Show sell value in tooltip when merchant hidden
When the MerchantFrame is not shown, display the item's total vendor sell value on the tooltip. Adds a guard to call SetTooltipMoney(frame, sell * count) if sell > 0 so stacked items show their combined sell price.
2026-07-10 13:23:52 -05:00
Brues 13a08b0ea3 Restore hooksecurefunc 2026-07-09 23:01:52 -05:00
Brues a2177fbf49 Restore HookScript 2026-07-09 22:55:26 -05:00
Brues 9cd83e90ad Added GetNoNameObject debugging 2026-07-09 22:31:58 -05:00
Brues 9bcc11e64f removed tbc logic 2026-07-09 22:26:32 -05:00
Brues acab272ec0 Generalize vendor price display across all tooltip types
Replace the single GameTooltip hook with a comprehensive hooking system that displays vendor prices across 17+ tooltip methods, including loot, quests, bags, mail, auctions, trades, merchants, and crafting. This ensures players see vendor prices consistently regardless of where they view items.
2026-07-09 22:05:25 -05:00
Brues 61c2f996fa Revert on-swing queue color when the ability is cancelled
Pressing Esc (or re-pressing) to cancel a queued Heroic Strike / Cleave /
Maul left the swing bar stuck in its queued color. The color is set on the
on-swing press and only cleared on nampower's ON_SWING_QUEUE_POPPED, which
fires solely when a queued-behind on-swing resolves; nampower's cancel path
touches no on-swing state and emits no event, so the flag never cleared.

Reconcile the event flag against the client's IsCurrentAction, which it does
clear on cancel: ReconcileQueued drops the flag once the client has confirmed
the ability as current and then stops showing it (the true->false transition).
It only acts once current has been seen, so a nampower-initiated cast the
client never flags as current keeps its color until its own pop/resolve --
preserving the reason the event-driven path exists.

RebuildQueueSlotCache now caches Maul slots and runs for druids, and no longer
bails in event mode so the caches stay fresh for reconciliation.
2026-07-09 19:45:17 -05:00
Brues 59ce6d9e74 Use a single shared frame for HookAddonOrVariable
Every HookAddonOrVariable call created its own lurker frame with three
event registrations. Share one frame across all hooks: they accumulate
in a pending list the single OnEvent handler walks, firing and dropping
each whose addon/variable is available, and unregistering events once the
list empties.

Behavior is preserved and slightly hardened: foundConfig now persists on
the shared frame (set on VARIABLES_LOADED and PLAYER_ENTERING_WORLD, both
of which imply config is ready), so a hook registered after config load
fires immediately if its addon is already loaded rather than waiting for
the next event.
2026-07-09 17:54:09 -05:00
Brues 967487e283 Utilizing ClassicAPI 1.6.0
commit d63057474083aa67fda2aa28ee6301334ffc4fdf
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Mon Jul 6 00:59:17 2026 -0500

    Use C_Map.GetMapOverlays instead of hardcoded pfMapOverlayData

    ClassicAPI's C_Map.GetMapOverlays reads WorldMapOverlay.dbc directly and
    returns the full overlay list for a zone (explored + unexplored) — the
    data vanilla's GetMapOverlayInfo withholds. mapreveal now iterates it
    straight (named fields: textureName/texturePath/width/height/offsets),
    dropping unpack_hash and the pfMapOverlayData tables entirely.

    Also fixes the explored-check: it compared the full texture path against
    GetMapOverlayInfo's bare-name keys, so the magnifying glass never
    suppressed on explored overlays. Now matches on the bare name.

    Removes ~870 lines of hand-measured overlay data (base + Turtle).

commit ad12c8209a80cd1211fe08e83f7013d24b5efd2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sun Jul 5 23:45:09 2026 -0500

    utilize HookScript from ClassicAPI
2026-07-09 00:56:06 -05:00
Brues ab11096b01 Show on-swing queue color on every HS/Cleave/Maul press
The SPELL_CAST_EVENT hook only called SetQueuedKind, but the queued
color path is gated behind S.useSpellQueueEvent. Nampower fires
SPELL_CAST_EVENT on the actual HS/Cleave/Maul press (not just the
rarer ON_SWING_QUEUED), so flip the event-driven flag there. The
queued color now shows even when the client IsCurrentAction state
does not reflect a natively-queued on-swing ability.
2026-07-08 10:34:45 -05:00
Brues 0402db380a logically sort Castbar options 2026-07-08 09:47:13 -05:00
Brues 68fe7e22db Split castbar config into General/Player/Target/Focus subcategories
The castbar options were one long list. Break them into four subentries
under the Castbar parent (matching the Settings/Actionbar layout):
General (fonts, colors, texture, disable-blizzard) plus one page each for
Player, Target, and Focus. Drops the now-redundant per-unit header rows.
2026-07-07 16:31:56 -05:00
Brues a93e9f530c Add spell name & timer text alignment options for unit frame castbars
Adds per-unit (player/target/focus) dropdowns to align the castbar
spell name (left text) and cast timer (right text) Left/Center/Right.
Both share a castbaralign dropdown; defaults preserve current behavior
(name LEFT, timer RIGHT). Applied at castbar creation, so takes effect
on /reload like the other castbar options.
2026-07-07 16:26:48 -05:00
Brues 3be0585039 Don't let the pet bar dodge reposition the stance bar during unlock
The pet bar force-shows in unlock mode, and its OnShow/OnHide dodge
handlers re-anchor the stance bar above the pet bar. That yanked the
stance bar off its real position while unlocking (dodging a pet bar
that isn't actually active), so it appeared to vanish and only returned
when unlock ended. Skip the dodge re-anchor while unlock is active so
the stance bar stays put and can be positioned.
2026-07-07 13:02:05 -05:00
Brues ee7c729bcb remove select 2026-07-07 09:16:33 -05:00
Brues 62e0d4eb07 prevent camera from constantly resetting 2026-07-06 17:33:16 -05:00
Brues a4ceb8ba53 utilize HEARTHSTONE_BOUND 2026-07-06 17:07:51 -05:00
Brues 80b677a6f8 utilize UnitIsAFK 2026-07-06 17:07:41 -05:00
Brues f6683b31af Clear castbar unlock preview when leaving unlock mode
The unlock preview drives the bar via alpha: the OnUpdate forces alpha=1
while the drag handle is shown. On lock, with no active cast (endTime nil)
and no fadeout, the idle branch returned early and left the empty bar
stuck at alpha 1. Reset leftover alpha to 0 in that branch so the preview
clears once the drag handle hides. Fixes #16.
2026-07-06 03:04:17 -05:00
Brues b47df9272d Bump ClassicAPI minimum version to 1.5.11 2026-07-05 23:51:57 -05:00
Brues c0b717878d Always set OnClick handler for action buttons
Ensure the action button OnClick handler is always assigned. Previously SetScript("OnClick", ButtonClick) only ran when HookScript was missing; now HookScript is still added only if absent, but the OnClick script is set unconditionally so existing frames won't miss the click handler. Change is limited to modules/actionbar.lua.
2026-07-05 23:48:02 -05:00
Brues 31c95606d0 Refactor turtle/Nampower checks and libdebuff cleanup
Replace ad-hoc Turtle/Nampower detection with global TURTLE_WOW_VERSION and EventUtil startup flow. Remove legacy IsTurtleWoW and manual PLAYER_ENTERING_WORLD frame; use EventUtil.ContinueOnPlayerLogin. Clean up libdebuff by removing combo-point capture, GetEnhancedDebuffs API, and noisy startup messages; rely on Nampower/AURA_CAST and database fallback for durations. Fix tooltip compare shift handling (cache shift state and pass through). Update xpbar to use TURTLE_WOW_VERSION. Purpose: simplify startup, avoid duplicated logic, and rely on modern APIs for accurate durations.
2026-07-05 23:47:39 -05:00
Brues 3fe072c594 Route hooksecurefunc callers through pfUI.hooksecurefunc; global belongs to ClassicAPI
pfUI's Lua hooksecurefunc lived in pfUI.env and shadowed ClassicAPI's C
global for all pfUI code. Replace it with a thin pfUI.hooksecurefunc shim
that keeps the missing-target no-op our call sites rely on (ClassicAPI
errors on a nil target) and delegates the actual hook to _G.hooksecurefunc.

Migrated all 70 internal call sites (modules/libs/skins) to
pfUI.hooksecurefunc; bare hooksecurefunc now resolves to ClassicAPI's C
version everywhere. Dropped the unused prepend path and the orphaned
pfUI.hooks table.
2026-07-05 14:58:26 -05:00
Brues 9706a74d16 UNIT_INVENTORY_CHANGED -> PLAYER_EQUIPMENT_CHANGED 2026-07-05 14:32:25 -05:00
Brues f9b0b5983a Don't need to worry about caching player guid anymore 2026-07-04 22:46:55 -05:00
Brues 176e131d6b Update README.md 2026-07-04 20:46:19 -05:00
Brues a17229abb1 GetLootSlotItemLink should be GetLootSlotLink 2026-07-04 13:12:40 -05:00
Brues e0cf01772c Update FUNDING.yml 2026-07-03 23:27:18 -05:00
Brues c2d4106170 Update README.md 2026-07-03 22:59:29 -05:00
Brues dbfd337d42 Bump pfUI min to 1.5.8 2026-07-03 21:45:04 -05:00
Brues d834be9f0b Revert "nameplates: hide castbar on remote interrupt / caster death (#11)"
This reverts commit 703d7770ca.
2026-07-03 21:38:05 -05:00
Brues 82a07e1e35 share: rebuild profile export/import on C_EncodingUtil (CBOR+zlib+base64)
Export now diffs the config against defaults and emits
SerializeCBOR -> CompressString (zlib) -> EncodeBase64 with a "!pf1!"
prefix. Import reverses that into a plain table — no loadstring, so a
pasted profile is data and can't execute code. The Decode/Encode button
converts blob <-> editable JSON (SerializeJSON/DeserializeJSON) for
inspection and hand-edits.

Old-format strings still import: standard base64 via DecodeBase64, the
custom LZW decompressor kept import-only, and the resulting Lua source
runs in an EMPTY setfenv sandbox that can only assign its config table.
Decode on a legacy string yields the JSON view, so Encode re-emits it
as a new-format blob (migration path).

Roughly half the paste size of the old LZW format, C-speed instead of
the old bit-string base64 (which froze the client on large configs),
and the format is fully standard — external tools can decode profiles
with stock base64/zlib/CBOR libraries.

Requires ClassicAPI with the SerializeCBOR buffer-growth fix (payloads
over 256 bytes returned nil before it).
2026-07-03 21:19:07 -05:00
Brues 703d7770ca nameplates: hide castbar on remote interrupt / caster death (#11)
ClassicAPI's remote-cast cache is stamped from SMSG_SPELL_START and only
expires by computed end time — 1.12 keeps no per-unit interrupt record,
so an interrupted cast kept animating on the plate until its would-be
finish (BG flag caps being the loudest repro).

Nampower does surface the missing signal in Lua: SPELL_FAILED_OTHER
(casterGuid, spellId; fired from the SMSG_SPELL_FAILED_OTHER handler)
and UNIT_DIED (guid). Stamp a guid-keyed suppression time on either
event and have GetCastInfo drop any cast that started before the stamp;
a newer cast clears its unit's entry. Both castbar paths (dedicated
target frame + central loop) already funnel through GetCastInfo, so one
check covers them. The handler only stamps when the unit actually has a
tracked cast, and flags the plate via castUpdate for a same-tick hide.
2026-07-03 19:28:34 -05:00
Brues bd722b0f0f bump classicapi min to 1.5.7 2026-07-03 02:29:15 -05:00
Brues e5c51fff48 eqcompare: rewrite on top of ClassicAPI SetHyperlinkCompareItem
Drop the manual C_Item.GetItemStatDelta rendering (inline annotations
and bottom-block summary) in favor of driving native shopping tooltips
via SetHyperlinkCompareItem — the 3.3.5 flow now available through
ClassicAPI. Way less code, and Blizzard's own comparison rendering
handles all stat types uniformly.

Also revert the mode dropdown back to a single basestats checkbox — the
new implementation doesn't distinguish base vs extended (Blizzard's
tooltip shows everything the item exposes), so the two-level control
was redundant. Existing basestats configs pass through unchanged.

AtlasLootTooltip now goes through the shared HookTooltip helper instead
of a bespoke OnShow shim.
2026-07-03 02:10:20 -05:00
Brues 6570afb70a eqcompare: collapse basestats/extendedstats checkboxes into a mode dropdown
Replace the two-checkbox arrangement (Compare Base Stats + Compare
Extended Stats, with the latter gated on the former) with a single
"Item Comparison" dropdown offering Off / Base / Extended. Migrate
existing configs.

Also route CreateConfig's value-change sites through pfUI.events
("config:changed", category, config) so callers can react to arbitrary
setting changes without frame-specific plumbing. Use it here to grey
out "Always Show Item Comparison" when the mode is Off.
2026-07-02 03:05:56 -05:00
Brues 0f97e8b0bd eqcompare: skip paperdoll owners in showalways mode
Comparing an equipped slot's tooltip against itself is pointless — every
delta is zero. Skip that case when `showalways=1`. Shift-hover still
forces the comparison unconditionally.
2026-07-02 01:50:48 -05:00
Brues 64c62a2a25 eqcompare: hook Set* via hooksecurefunc + unify base/extended stat block
Refactor delta rendering:
- extendedstats on  -> all stats (base + extended + DPS + block value)
  appended at the bottom via AddDoubleLine
- extendedstats off -> base stats annotated inline; nothing at the bottom

Requires ClassicAPI's 60-line tooltip fix so the bottom block isn't
truncated. DPS and block value were previously extended-only inline
matches — moved into BASE_STAT_KEYS so they also show at the bottom.

Also extract MakeDependent(child, parent) helper from the local
gate-lambda in gui.lua so the base/extended checkbox pairing generalizes.
2026-07-02 01:22:10 -05:00
Brues 4d316ed43a no select 2026-07-01 20:53:47 -05:00
Brues db0195f0f7 Update README.md 2026-07-01 17:20:54 -05:00
Brues 3d2d5f9254 bump ClassicAPI version 2026-07-01 17:06:31 -05:00
Brues dfbeeb454a eqcompare: use C_Item.GetItemStatDelta for the comparison math
Replaces the twin-tooltip text extraction/comparison pair
(ExtractAttributes + CompareAttributes) with a single delta pull from
ClassicAPI's C_Item.GetItemStatDelta(equippedLink, newLink).

Base stats (Str/Agi/Sta/Int/Spi/Mana/Health + Armor + resistances) stay
annotated inline on their existing tooltip line: iterate the tooltip's
FontString regions (no more _G["...TextLeft"..i] name lookup), match the
"+N Foo" prefix, look up the trailing noun in a label→key map, append
(+delta)/(-delta) from the ClassicAPI delta table.

Extended stats (attack power / ranged AP / spell damage/healing / crit
ratings / hit ratings / mana regen / defense / DPS) don't emit their own
line — vanilla mixes them into equip-spell descriptions ("Equip:
Increases your critical strike chance by 1%") — so aggregate them into
a "Compared to equipped:" block at the bottom via AddDoubleLine. New
`tooltip.compare.extendedstats` config knob (default 1) gates that
block; it depends on `basestats` being on (its GUI checkbox grays out
otherwise). DPS rounded to one decimal — ClassicAPI derives it from
damage/delay so it lands as a raw float.

Random-suffix bonuses ("of the Bear" etc.) now count correctly since
GetItemStatDelta walks item-record + equip-spell auras + suffix
enchants server-side.
2026-07-01 17:03:32 -05:00
Brues 439d5a397a eqcompare: numeric InventoryType lookup, drop bagtypes/itemtypes locales
Both sub-tables carried per-locale strings only so pfUI's own code could
match against localized text. ClassicAPI's numeric item APIs replace
both:

- GetBagFamily now reads classID/subClassID from C_Item.GetItemInfoInstant
  and switches on the numbers (class 1 = Container, class 11 = Quiver).
- eqcompare pulls the itemID from GameTooltip:GetItem(), fetches the
  numeric invType via C_Item.GetItemInventoryTypeByID, and looks up the
  destination slot(s) in a numeric slotTable keyed by Enum.InventoryType.
  Pair-slot invtypes (finger / trinket / one-hand weapon) list both
  destinations directly, so the "_other" string-concat hack is gone.

Removes the setglobal INVTYPE_* injection, the tooltip text scan, and
the itemtypes + bagtypes locale sub-tables across all 7 files.
2026-07-01 15:43:03 -05:00
Brues 5524ad3b91 locales: strip 4 dead sub-tables (hunterpaging, interrupts, spells, icons)
Audited every consumer of pfUI_locale[*][key] across the codebase. Four
sub-tables have no non-locale-file readers left:

  - hunterpaging  — old auto-page trigger removed
  - interrupts    — replaced by Nampower SPELL_INTERRUPTED events
  - spells        — replaced by ClassicAPI Spell.dbc lookups
  - icons         — replaced by ClassicAPI C_Spell.GetSpellName / icon path

Removed the entries from all 7 locale files. ~18k dead lines gone,
~70% shrink per file.
2026-07-01 12:13:25 -05:00
Brues 0f59a3a9b5 replace poll-until-cancel OnUpdate frames with C_Timer / RunNextFrame
Seven ad-hoc OnUpdate handlers were only spinning long enough to reach
a known deadline or a next-frame defer, then unhooking themselves.
Convert them to their proper primitives:

- autovendor: 0.3s wait after junk sell → C_Timer.After(0.3, ...)
- innervatecall: cooldown-expiry ready ping → C_Timer.After(cd, ...)
- focus: re-arm UI_ERROR_MESSAGE next tick → RunNextFrame
- macrotweak: conflict scan after addons load → RunNextFrame
- ui-widgets (CreateQuestionDialog): font-measure resize → RunNextFrame
- libdebuff: post-PEW Nampower init → RunNextFrame
- bubbles: WorldFrame scan after chat event → RunNextFrame

Net -18 lines and no more throwaway frames sitting on the OnUpdate list.
2026-07-01 10:43:46 -05:00
Brues ffdf376ac4 add pfUI.events callback registry, replace addoncompat OnUpdate poll
Introduce a central pfUI.events registry (ClassicAPI's
CallbackRegistryMixin, undefined events allowed) initialized in pfUI.lua
before any module body runs, so publishers/subscribers don't depend on
module load order.

firstrun sets `pfUI.firstrun.completed` and fires `firstrun:complete` at
the point NextStep detects no pending steps. PLAYER_ENTERING_WORLD re-
enters this path on every zone, so the flag is a one-shot guard.

addoncompat drops its 0.1s OnUpdate poll and either RunQueues immediately
(returning user, all steps already done) or subscribes to the event.
2026-07-01 10:20:34 -05:00
Brues 9bdde06150 unitframes: clear stale aura swirl on target swap
Buff and debuff slots only called CooldownFrame_SetTimer on the
`expirationTime > 0` (or `duration > 0` for buffs) paths. When the new
target's aura at the same slot index had neither — permanent / passive
auras like Retribution Aura — neither branch fired and the slot kept
displaying the previous target's swirl/timer.

Add an explicit 0/0/0 clear on every path that doesn't set a real
timer, so the button always starts from a known state.

Fixes #13.
2026-06-28 12:41:31 -05:00
Brues 8bf6672114 buffs: cancel by spellID instead of GetPlayerBuff slot index
`GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, filter)` assumes the
visual index pfUI shows matches the engine's slot order. When that
mismapping happens — most easily reproduced by stacking buffs that
share a slot family — right-clicking one buff cancels another.

`C_Spell.CancelSpellByID(spellID)` ships CMSG_CANCEL_AURA keyed to the
spell, not a slot, so it's immune to whatever order the slot table is
in. Cache `spellId` on the button at refresh time in buff.lua; in the
unitframes/buffwatch handlers fetch the aura fresh via
`C_UnitAuras.GetAuraDataByIndex` at click time.

Fixes #10.
2026-06-28 04:28:59 -05:00
Brues 29b948e6fa character cleanup 2026-06-28 00:14:40 -05:00
Brues 5f61f94a24 Texture ArenaFrame 2026-06-27 23:31:02 -05:00
Brues 65678da3d2 tooltip: optional movement-speed line via GetUnitSpeed
New tooltip.movespeed config knob (default off, checkbox in the GUI's
tooltip page). When on, the unit tooltip gains a "Speed: N%" line where
N is the unit's run speed normalized to vanilla's 7.0 yd/s base — 100
unmounted, 160 on a 60% mount, 200 on epic, less under snares.

Uses runSpeed (return 2 of GetUnitSpeed), not currentSpeed, so the
number reflects what the unit *would* be running at — visible even
while they're standing still. runSpeed is 0 for out-of-range units, so
the line is skipped in that case.
2026-06-27 19:28:40 -05:00
Brues 9b7aca8541 show CLASSIC_API_VERSION in /pfdll 2026-06-27 19:20:06 -05:00
Brues 60525eec3a Clean up throttle 2026-06-27 19:19:51 -05:00
Brues c3829c2bd2 player: drop talent-side modCastingTime fudge from haste display
The player frame's "Effective Haste" mode was hardcoded talent-position
scrapes: GetTalentInfo(1, 16) for the Mage "Accelerated Arcana"
(flat 5%) and GetTalentInfo(1, 14) for the Warlock "Rapid Deterioration"
(3% per rank), folded into the displayed haste % via
`(1 / (modCastSpeed * modCastingTime) - 1) * 100`.

That's two problems in one:
- Hardcoded talent indices and effect percentages — brittle to any
  Turtle tree reshuffle or retune.
- Conceptually muddled: it folds gear-haste and talent-cast-reduction
  into one number that's hard to read as anything specific. The actual
  effective cast time is already shown on the cast bar via
  C_Spell.UnitCastingInfo (engine helper accounts for SpellMod op 10).

Drop modCastingTime, the LEARNED_SPELL_IN_TAB watcher frame that
maintained it, the per-class talent scrape, and the hasteMode == "2"
display branch. Collapse the now-binary "display_haste" config from a
3-option dropdown to a checkbox. Users on legacy "2" will see the
checkbox unchecked once and can re-enable with a single click.
2026-06-27 19:13:49 -05:00
Brues 28de6fd835 finish UnitInRaid("player") → IsInRaid() sweep
Four more sites: GetUnbuffedRoster + SendChatMessageWide in api.lua,
the loot menu's inRaid local, and the raid module's early-return
guard. Same intent, named helper.
2026-06-27 18:57:21 -05:00
Brues fc75e091a4 finish IsInRaid sweep across remaining "raid count > 0" sites
Three more `if GetNumRaidMembers() > 0 then` raid-vs-party branches
switched to `if IsInRaid()`. Same intent, named helper. Repo is now
clean of the legacy idiom (verified with a final grep).

Loops that actually need the count (`for i = 1, GetNumRaidMembers() do
GetRaidRosterInfo(i)`) keep the call — only the boolean form changes.
2026-06-27 18:56:50 -05:00
Brues ef478b0552 thirdparty-vanilla: collapse solo check to not IsInGroup()
The HealComm self-message guard was `not UnitInRaid("player") and
GetNumPartyMembers() < 1` — the long form of "in no group at all."
2026-06-27 18:55:49 -05:00
Brues 760d8992b6 switch group-membership checks to IsInGroup / IsInRaid
ClassicAPI ships modern IsInGroup() / IsInRaid() backports — drop the
GetNumPartyMembers() > 0 and GetNumRaidMembers() > 0 idioms (and the
GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0 conflation) for the
named-intent variants. UnitInRaid("player") → IsInRaid() at the same
sites.
2026-06-27 18:54:04 -05:00
Brues 62a99ac410 mouseover/libpredict: drop legacy spell-target plumbing
With Nampower as a hard dep, /pfcast for spell names always takes the
early CastSpellByName(msg, unit) path. The fallback branch that did the
SpellTargetUnit dance (resolve a friendly unit token, disable AutoSelf
Cast, call SpellTargetUnit) hasn't been reachable in a while, and it
dragged a pile of supporting infrastructure with it.

modules/mouseover.lua:
- Drop the st_units token list, GetUnitString helper, and the
  UnitTokenFromGUID rewrite of GetUnitString — all only used by the
  dead fallback.
- Drop the NoSelfCast helper (only the dead fallback called it).
- Drop the pfMouseOver frame; its only purpose was to hold a .unit
  field the dead fallback wrote and libpredict's hook read.
- The macro path collapses to: if not the current target, swap target,
  run the loadstring'd func, restore the previous target.
- 99 lines → 34.

libs/libpredict.lua:
- Drop the dead `local mouseover = pfUI.uf.mouseover.unit` plumbing in
  the CastSpellByName hook — pfUI.uf.mouseover is gone and the field
  was permanently nil anyway. The three `target or mouseover or default`
  fallback chains collapse to `target or default`.

Modern mouseover/click-to-cast detection in libpredict goes through
pfUI.libpredict_pending_cast (populated by libdebuff from Nampower's
SPELL_CAST_EVENT) — that path is GUID-based, server-authoritative, and
untouched.
2026-06-27 18:41:11 -05:00
Brues deeec89955 predict + libdebuff + swingtimer: drop hardcoded spell data
Replace per-locale name tables and per-rank ID lists with single
canonical-rank lookups through C_Spell. Spell.dbc bits hoisted to
named constants at module top.

libpredict:
- Four 7-locale tables (PRAYER_OF_HEALING / REJUVENATION / RENEW /
  REGROWTH) collapsed to one C_Spell.GetSpellName(rank1id) call each.
- 25-entry SPELL_IDS (all ranks of Rejuv + Renew) for SPELL_GO_SELF
  HoT detection replaced by name comparison against REJUVENATION /
  RENEW. No per-rank ID maintenance.

libdebuff:
- GetSpellRecField(id, "name") → C_Spell.GetSpellName(id) at all call
  sites; the presence-guard pattern is gone (ClassicAPI is a hard
  dep, per memory).
- GetSpellRecField(id, "rank") → C_Spell.GetSpellSubtext(id).

swingtimer:
- Hoist FLAG_AUTOATTACK / ATTR_KEEP_SWINGS / ATTR_ON_NEXT_SWING to
  module-top constants via tonumber("0xNN", 16) so the SPELL_GO_SELF
  hot path stops re-parsing them on every call. Lua 5.0 has no hex
  number literals; strtoul-backed tonumber handles the "0x" prefix.
2026-06-27 18:22:03 -05:00
Brues cca9771ef0 swingtimer: derive spell categories from Spell.dbc attributes
Drop four hardcoded spell-ID tables (swingDelaySpells, hsSpellIDs,
cleaveSpellIDs, maulSpellIDs) and the broad interruptFlags > 0 reset
heuristic in favor of server-parity checks against Spell.dbc bits via
nampower's GetSpellRecField:

- "Resets the swing on cast complete" gated on InterruptFlags's
  SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08) and AttributesEx2's lack of
  SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS (0x20000) — mirrors the server's
  Spell::IsMeleeAttackResetSpell. Reset OH alongside MH to match.

- "Slam-style delay" derived from absent 0x08 + a cast time existing
  (implicit since SPELL_START_SELF only fires for cast-time spells).
  Freezes the swing timer at SPELL_START and adds cast duration on
  SPELL_GO instead of resetting.

- HS / Cleave / Maul classification via IsOnSwingSpell (ATTR_ON_NEXT_
  SWING bit 0x04) + name comparison against rank-1 canonical names,
  centralized into ClassifyOnSwingSpell + SetQueuedKind helpers.

Switch the lone GetSpellRec table-allocator call to GetSpellRecField for
consistency (single-field hash lookup, no shared-table reuse caveat).
2026-06-27 17:52:31 -05:00
Brues 371872bb90 swingtimer: route wand Shoot to ranged bar without resetting MH
Wand Shoot (spellID 5019) wasn't recognized as ranged anywhere, so it
fell through every branch of the SPELL_GO_SELF dispatch and hit the
catch-all interruptFlags > 0 reset, wiping the mainhand swing on every
shot. Casters melee-weaving between MH swings and wand fires lost their
swing visualization.

Replace the hardcoded RANGED_SPELLIDS / WAND_SHOOT_SPELLIDS tables with
C_Spell.IsRangedAutoAttackSpell (Spell.dbc AUTO_REPEAT bit) — catches
both Auto Shot and Shoot today, plus any future auto-repeat ranged
spell. ResetRanged now takes a replaceMH flag: true for Auto Shot /
Throw (Hunter ranged replaces melee), false for wand (independent
timers, both tick concurrently).

Closes #5.
2026-06-27 17:14:48 -05:00
Brues 591045a606 config: stamp sentinel version on dev builds to stop migration re-fire
Dev / git-cloned installs carry "@project-version@" in the toc, which
pfUI normalizes to pfUI.version.string = "dev". MigrateConfig was
writing that literal back into pfUI_config.version; on the next /reload
checkversion() parsed "dev" through tonumber() → nil → 0, evaluated
curversion as 0/0/0, and re-fired every version-gated migration block.
The >3.6.1 buff migration unconditionally rewrites buffs.{buffs,debuffs,
weapons} from the legacy global.hide{,w}buff knobs, so user toggles for
those checkboxes were getting wiped on every reload.

Stamp "999.999.999" on dev builds so subsequent comparisons turn false.
Affected users will need to re-toggle the buffs/debuffs/weapon-buffs
checkboxes once after this update.

Refs #7.
2026-06-27 16:46:56 -05:00
Brues f657a9e7e8 drop MobHealth3 / MobHealthFrame fallbacks
libhealth ships with pfUI and Nampower's GetUnitField (hard-dep) covers
the real-HP read; the MobHealth integration was a chained last-resort
fallback that could never actually win, and on the nameplate path it
also mixed percentage and real-value scales into the bar's SetMinMaxValues
since it overwrote hp/hpmax without hpmin.
2026-06-27 16:08:39 -05:00
Brues 0cbd9ecf98 Revert "nameplates: drop redundant name check from plate-reassignment gate"
This reverts commit 6ca482fcaa.
2026-06-27 16:01:24 -05:00
Brues 6ca482fcaa nameplates: drop redundant name check from plate-reassignment gate
GUID is the unique identity; the name check was leftover from the
name-only era and became dead weight once cachedGuid (NAME_PLATE_UNIT_
ADDED) was added alongside it.
2026-06-27 15:59:16 -05:00
Brues 8902d43f40 nameplates: wipe full plate.cache on reassignment
The plate-reuse reset only cleared name/guid/player/cdCache, leaving
hp/hpmax/rgb/namecolor/levelcolor/target/mouseover/inCombat behind. The
PERF gates downstream ("only update X when X changed") then skipped bar
fill / color / text updates when the new occupant happened to share a
cached value with the previous one — most easily reproduced on
plate-pool reuse in starting zones (mobs churn fast, percentage values
collide). Nuke the whole cache via table.wipe.

Refs #8.
2026-06-27 15:48:53 -05:00
brues-code 8f006c0643 Update issue templates 2026-06-27 15:07:04 -05:00
91 changed files with 2061 additions and 23018 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
github: [shagu] github: [brues]
ko_fi: shagu buy_me_a_coffee: brues
+7 -6
View File
@@ -4,16 +4,18 @@ about: Report a bug or issue with pfUI
title: "[Bug Description] " title: "[Bug Description] "
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
<!-- Please fill out ALL fields below. Issues without this info may be closed. --> <!-- Please fill out ALL fields below. Issues without this info may be closed. -->
**pfUI Branch:** **pfUI Version:**
- [ ] Master
- [ ] experiment **Your ClassicAPI Version:**
**Your Nampower Version:**
**Your Nampower Version:** (e.g. latest, or specific commit/date)
<!-- Skip if using Compat branch -->
**Other Addons installed:** **Other Addons installed:**
<!-- List any other addons you are using, or "none" --> <!-- List any other addons you are using, or "none" -->
@@ -45,4 +47,3 @@ Paste error log here
## Screenshot / Video ## Screenshot / Video
<!-- Attach a screenshot or video showing the issue. This is REQUIRED. --> <!-- Attach a screenshot or video showing the issue. This is REQUIRED. -->
<!-- Drag & drop images here or paste a link --> <!-- Drag & drop images here or paste a link -->
+1 -1
View File
@@ -4,6 +4,7 @@ about: Suggest a new feature or improvement for pfUI
title: "[Feature] " title: "[Feature] "
labels: enhancement labels: enhancement
assignees: '' assignees: ''
--- ---
## Feature Description ## Feature Description
@@ -20,4 +21,3 @@ assignees: ''
## Examples ## Examples
<!-- If possible, show examples from other addons or screenshots/mockups of how it could look --> <!-- If possible, show examples from other addons or screenshots/mockups of how it could look -->
+33 -1588
View File
File diff suppressed because it is too large Load Diff
+80 -68
View File
@@ -28,14 +28,6 @@ function pfUI.api.HasNampower()
return GetNampowerVersion and true or false return GetNampowerVersion and true or false
end end
local isTurtleWoW
function pfUI.api.IsTurtleWoW()
if isTurtleWoW == nil then
isTurtleWoW = C_Spell.GetSpellTexture(46050) == "Interface\\Icons\\Trade_Survival"
end
return isTurtleWoW
end
-- [ GetUnitDistance ] -- [ GetUnitDistance ]
-- Returns distance to unit using best available method -- Returns distance to unit using best available method
-- 'unit1' [string] first unit (default: "player") -- 'unit1' [string] first unit (default: "player")
@@ -110,11 +102,7 @@ end
-- 'tbl' [table] the table that shall be checked -- 'tbl' [table] the table that shall be checked
-- return: [boolean] result of the check. -- return: [boolean] result of the check.
function pfUI.api.isempty(tbl) function pfUI.api.isempty(tbl)
if not tbl then return true end return next(tbl or {}) == nil
for k, v in pairs(tbl) do
return false
end
return true
end end
-- [ checkversion ] -- [ checkversion ]
@@ -151,6 +139,11 @@ function pfUI.api.UnitInRange(unit)
return 1 return 1
end end
-- master switch: with the 40y check off, a visible unit beyond interact
-- range counts as in range (nothing fades). Invisible units already
-- returned nil above, matching the pre-collapse behavior.
if C.unitframes.rangecheck == "0" then return 1 end
-- UnitXP precise mode: skip librange entirely, use direct distance check -- UnitXP precise mode: skip librange entirely, use direct distance check
if C.unitframes.rangecheck_mode == "unitxp" and _G.UnitXP then if C.unitframes.rangecheck_mode == "unitxp" and _G.UnitXP then
local threshold = tonumber(C.unitframes.rangecheck_distance) or 40 local threshold = tonumber(C.unitframes.rangecheck_distance) or 40
@@ -208,7 +201,7 @@ function pfUI.api.GetUnbuffedRoster(name)
end end
end end
if UnitInRaid("player") then if IsInRaid() then
for i=1,40 do check("raid"..i) end for i=1,40 do check("raid"..i) end
else else
check("player") check("player")
@@ -274,11 +267,7 @@ end
-- 'f' [float] the number to breakdown. -- 'f' [float] the number to breakdown.
-- returns: [int],[float] whole and fractional part. -- returns: [int],[float] whole and fractional part.
function pfUI.api.modf(f) function pfUI.api.modf(f)
if modf then return modf(f) end return math.modf(f)
if f > 0 then
return math.floor(f), mod(f,1)
end
return math.ceil(f), mod(f,1)
end end
-- [ GetServerEpoch ] -- [ GetServerEpoch ]
@@ -397,10 +386,10 @@ end
-- returns: [string] entire itemLink for the given item -- returns: [string] entire itemLink for the given item
function pfUI.api.GetItemLinkByName(name) function pfUI.api.GetItemLinkByName(name)
for itemID = 1, 25818 do for itemID = 1, 25818 do
local itemName, hyperLink, itemQuality = GetItemInfo(itemID) local itemName = C_Item.GetItemNameByID(itemID)
if (itemName and itemName == name) then if itemName and itemName == name then
local _, _, _, hex = GetItemQualityColor(tonumber(itemQuality)) local _, itemLink = C_Item.GetItemInfo(itemID)
return hex.. "|H"..hyperLink.."|h["..itemName.."]|h|r" return itemLink
end end
end end
end end
@@ -434,13 +423,17 @@ function pfUI.api.GetBagFamily(bag)
local id = GetInventoryItemID("player", ContainerIDToInventoryID(bag)) local id = GetInventoryItemID("player", ContainerIDToInventoryID(bag))
if id then if id then
local _, _, _, _, _, itemType, subType = GetItemInfo(id) -- classID 1 = Container (bags), 11 = Quiver
local bagsubtype = L["bagtypes"][subType] -- Container subclasses: 0 = Bag (default), 1 = Soul Bag, 2+ = specialty (herb/enchanting/etc.)
-- Quiver subclasses: 2 = Quiver (arrows), 3 = Ammo Pouch (bullets)
if bagsubtype == "DEFAULT" then return "BAG" end local _, _, _, _, _, classID, subClassID = C_Item.GetItemInfoInstant(id)
if bagsubtype == "SOULBAG" then return "SOULBAG" end if classID == 1 then
if bagsubtype == "QUIVER" then return "QUIVER" end if subClassID == 0 then return "BAG" end
if bagsubtype == nil then return "SPECIAL" end if subClassID == 1 then return "SOULBAG" end
return "SPECIAL"
elseif classID == 11 then
return "QUIVER"
end
end end
return nil return nil
@@ -485,7 +478,7 @@ end
-- 'msg' [string] the message to send -- 'msg' [string] the message to send
function pfUI.api.SendChatMessageWide(msg) function pfUI.api.SendChatMessageWide(msg)
local channel = "SAY" local channel = "SAY"
if UnitInRaid("player") then if IsInRaid() then
if ( IsRaidLeader() or IsRaidOfficer() ) then if ( IsRaidLeader() or IsRaidOfficer() ) then
channel = "RAID_WARNING" channel = "RAID_WARNING"
else else
@@ -552,36 +545,54 @@ end
-- 'script' [string] the handler to hook -- 'script' [string] the handler to hook
-- 'func' [function] the function that should be added -- 'func' [function] the function that should be added
function HookScript(f, script, func) function HookScript(f, script, func)
local prev = f:GetScript(script) f:HookScript(script, func)
f:SetScript(script, function(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
if prev then prev(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
func(a1,a2,a3,a4,a5,a6,a7,a8,a9) function hooksecurefunc(tbl, name, func)
end) if type(tbl) == "string" then tbl, name, func = _G, tbl, name end
if not tbl or type(tbl[name]) ~= "function" then return end
return _G.hooksecurefunc(tbl, name, func)
end end
-- [ HookAddonOrVariable ] -- [ HookAddonOrVariable ]
-- Sets a function to be called automatically once an addon gets loaded -- Sets a function to be called automatically once an addon gets loaded
-- 'addon' [string] addon or variable name -- 'addon' [string] addon or variable name
-- 'func' [function] function that should run -- 'func' [function] function that should run
function pfUI.api.HookAddonOrVariable(addon, func) do
local lurker = CreateFrame("Frame", nil) local lurker
lurker.func = func local pending = {}
lurker:RegisterEvent("ADDON_LOADED")
lurker:RegisterEvent("VARIABLES_LOADED") local function ProcessPending()
lurker:RegisterEvent("PLAYER_ENTERING_WORLD") if not lurker.foundConfig then return end
lurker:SetScript("OnEvent",function() for i = table.getn(pending), 1, -1 do
-- only run when config is available local hook = pending[i]
if event == "ADDON_LOADED" and not this.foundConfig then if IsAddOnLoaded(hook.addon) or _G[hook.addon] then
return hook.func()
elseif event == "VARIABLES_LOADED" then table.remove(pending, i)
this.foundConfig = true end
end
if table.getn(pending) == 0 then
lurker:UnregisterAllEvents()
end
end
function pfUI.api.HookAddonOrVariable(addon, func)
if not lurker then
lurker = CreateFrame("Frame", nil)
lurker:SetScript("OnEvent", function()
if event == "VARIABLES_LOADED" or event == "PLAYER_ENTERING_WORLD" then
this.foundConfig = true
end
ProcessPending()
end)
end end
if IsAddOnLoaded(addon) or _G[addon] then table.insert(pending, { addon = addon, func = func })
this:func() lurker:RegisterEvent("ADDON_LOADED")
this:UnregisterAllEvents() lurker:RegisterEvent("VARIABLES_LOADED")
end lurker:RegisterEvent("PLAYER_ENTERING_WORLD")
end) ProcessPending()
end
end end
-- [ QueueFunction ] -- [ QueueFunction ]
@@ -705,24 +716,15 @@ function pfUI.api.CopyTable(src)
end end
-- [ Wipe Table ] -- [ Wipe Table ]
-- Empties a table and returns it -- Empties a table and returns it.
-- 'src' [table] the table that should be emptied. -- 'src' [table] the table that should be emptied.
-- return: [table] the emptied table. -- return: [table] the emptied table.
-- Delegates to ClassicAPI's table.wipe, which also resets the Lua 5.0 getn
-- length (luaL_setn(t,0)) so table.insert on a wiped table resumes at [1].
-- Append to wiped arrays with table.insert -- NOT the t[table.getn(t)+1]=v
-- idiom, which needs an unmanaged length counter and won't work here.
function pfUI.api.wipe(src) function pfUI.api.wipe(src)
-- notes: table.insert, table.remove will have undefined behavior return table.wipe(src)
-- when used on tables emptied this way because Lua removes nil
-- entries from tables after an indeterminate time.
-- Instead of table.insert(t,v) use t[table.getn(t)+1]=v as table.getn collapses nil entries.
-- There are no issues with hash tables, t[k]=v where k is not a number behaves as expected.
local mt = getmetatable(src) or {}
if mt.__mode == nil or mt.__mode ~= "kv" then
mt.__mode = "kv"
src=setmetatable(src,mt)
end
for k in pairs(src) do
src[k] = nil
end
return src
end end
-- [ Load Movable ] -- [ Load Movable ]
@@ -1563,6 +1565,16 @@ end
-- 'arg1' [string] -- 'arg1' [string]
-- return object -- return object
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2) function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
-- below, and the traceback stops here — useless, since all callers share this
-- line. pfUI shadows Lua's `error` (dropping the level arg and not throwing),
-- so fold the caller frame in via debugstack to name the offending skin, then
-- bail so we don't fall through and crash on frame:GetRegions() anyway.
if type(frame) ~= "table" or not frame.GetRegions then
error("GetNoNameObject: invalid parent frame\n" .. debugstack(2, 3, 0))
return
end
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1") local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1") local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
+26 -11
View File
@@ -162,6 +162,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "infight", "intensity", "16") pfUI:UpdateConfig("appearance", "infight", "intensity", "16")
pfUI:UpdateConfig("appearance", "bags", "unusable", "1") pfUI:UpdateConfig("appearance", "bags", "unusable", "1")
pfUI:UpdateConfig("appearance", "bags", "unusable_color", ".9,.2,.2,1") pfUI:UpdateConfig("appearance", "bags", "unusable_color", ".9,.2,.2,1")
pfUI:UpdateConfig("appearance", "bags", "newitem", "1")
pfUI:UpdateConfig("appearance", "bags", "newitem_color", "1,1,1,1")
pfUI:UpdateConfig("appearance", "bags", "borderlimit", "1") pfUI:UpdateConfig("appearance", "bags", "borderlimit", "1")
pfUI:UpdateConfig("appearance", "bags", "borderonlygear", "0") pfUI:UpdateConfig("appearance", "bags", "borderonlygear", "0")
pfUI:UpdateConfig("appearance", "bags", "fulltext", "1") pfUI:UpdateConfig("appearance", "bags", "fulltext", "1")
@@ -196,6 +198,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("loot", nil, "rollannounce", "0") pfUI:UpdateConfig("loot", nil, "rollannounce", "0")
pfUI:UpdateConfig("loot", nil, "raritytimer", "1") pfUI:UpdateConfig("loot", nil, "raritytimer", "1")
pfUI:UpdateConfig("loothistory", nil, "autoshow", "0")
pfUI:UpdateConfig("unitframes", nil, "disable", "0") pfUI:UpdateConfig("unitframes", nil, "disable", "0")
pfUI:UpdateConfig("unitframes", nil, "pastel", "1") pfUI:UpdateConfig("unitframes", nil, "pastel", "1")
pfUI:UpdateConfig("unitframes", nil, "custom", "0") pfUI:UpdateConfig("unitframes", nil, "custom", "0")
@@ -228,7 +232,6 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", nil, "druidmanaspace", "-3") pfUI:UpdateConfig("unitframes", nil, "druidmanaspace", "-3")
pfUI:UpdateConfig("unitframes", nil, "druidmanatexture", "Interface\\AddOns\\pfUI\\img\\bar") pfUI:UpdateConfig("unitframes", nil, "druidmanatexture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("unitframes", nil, "rangechecki", "4")
pfUI:UpdateConfig("unitframes", nil, "combowidth", "6") pfUI:UpdateConfig("unitframes", nil, "combowidth", "6")
pfUI:UpdateConfig("unitframes", nil, "comboheight", "6") pfUI:UpdateConfig("unitframes", nil, "comboheight", "6")
pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200") pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200")
@@ -624,6 +627,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "pagemastershift", "0") pfUI:UpdateConfig("bars", nil, "pagemastershift", "0")
pfUI:UpdateConfig("bars", nil, "pagemasterctrl", "0") pfUI:UpdateConfig("bars", nil, "pagemasterctrl", "0")
pfUI:UpdateConfig("bars", nil, "druidstealth", "0") pfUI:UpdateConfig("bars", nil, "druidstealth", "0")
pfUI:UpdateConfig("bars", nil, "priestshadow", "0")
pfUI:UpdateConfig("bars", nil, "showcastable", "1") pfUI:UpdateConfig("bars", nil, "showcastable", "1")
pfUI:UpdateConfig("bars", nil, "glowrange", "1") pfUI:UpdateConfig("bars", nil, "glowrange", "1")
pfUI:UpdateConfig("bars", nil, "rangecolor", "1,0.1,0.1,1") pfUI:UpdateConfig("bars", nil, "rangecolor", "1,0.1,0.1,1")
@@ -714,9 +718,11 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("castbar", "player", "txtleftoffy", "0") pfUI:UpdateConfig("castbar", "player", "txtleftoffy", "0")
pfUI:UpdateConfig("castbar", "player", "showlag", "0") pfUI:UpdateConfig("castbar", "player", "showlag", "0")
pfUI:UpdateConfig("castbar", "player", "showrank", "0") pfUI:UpdateConfig("castbar", "player", "showrank", "0")
pfUI:UpdateConfig("castbar", "player", "mergetradeskill", "1") pfUI:UpdateConfig("castbar", "player", "mergetradeskill", "0")
pfUI:UpdateConfig("castbar", "player", "txtrightoffx", "0") pfUI:UpdateConfig("castbar", "player", "txtrightoffx", "0")
pfUI:UpdateConfig("castbar", "player", "txtrightoffy", "0") pfUI:UpdateConfig("castbar", "player", "txtrightoffy", "0")
pfUI:UpdateConfig("castbar", "player", "namealign", "LEFT")
pfUI:UpdateConfig("castbar", "player", "timealign", "RIGHT")
pfUI:UpdateConfig("castbar", "target", "hide_pfui", "0") pfUI:UpdateConfig("castbar", "target", "hide_pfui", "0")
pfUI:UpdateConfig("castbar", "target", "width", "-1") pfUI:UpdateConfig("castbar", "target", "width", "-1")
pfUI:UpdateConfig("castbar", "target", "height", "-1") pfUI:UpdateConfig("castbar", "target", "height", "-1")
@@ -729,6 +735,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("castbar", "target", "showrank", "0") pfUI:UpdateConfig("castbar", "target", "showrank", "0")
pfUI:UpdateConfig("castbar", "target", "txtrightoffx", "0") pfUI:UpdateConfig("castbar", "target", "txtrightoffx", "0")
pfUI:UpdateConfig("castbar", "target", "txtrightoffy", "0") pfUI:UpdateConfig("castbar", "target", "txtrightoffy", "0")
pfUI:UpdateConfig("castbar", "target", "namealign", "LEFT")
pfUI:UpdateConfig("castbar", "target", "timealign", "RIGHT")
pfUI:UpdateConfig("castbar", "focus", "hide_pfui", "0") pfUI:UpdateConfig("castbar", "focus", "hide_pfui", "0")
pfUI:UpdateConfig("castbar", "focus", "width", "-1") pfUI:UpdateConfig("castbar", "focus", "width", "-1")
pfUI:UpdateConfig("castbar", "focus", "height", "-1") pfUI:UpdateConfig("castbar", "focus", "height", "-1")
@@ -741,6 +749,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("castbar", "focus", "showrank", "0") pfUI:UpdateConfig("castbar", "focus", "showrank", "0")
pfUI:UpdateConfig("castbar", "focus", "txtrightoffx", "0") pfUI:UpdateConfig("castbar", "focus", "txtrightoffx", "0")
pfUI:UpdateConfig("castbar", "focus", "txtrightoffy", "0") pfUI:UpdateConfig("castbar", "focus", "txtrightoffy", "0")
pfUI:UpdateConfig("castbar", "focus", "namealign", "LEFT")
pfUI:UpdateConfig("castbar", "focus", "timealign", "RIGHT")
pfUI:UpdateConfig("castbar", nil, "use_unitfonts", "0") pfUI:UpdateConfig("castbar", nil, "use_unitfonts", "0")
pfUI:UpdateConfig("tooltip", nil, "position", "chat") pfUI:UpdateConfig("tooltip", nil, "position", "chat")
@@ -748,6 +758,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("tooltip", nil, "cursoroffset", "20") pfUI:UpdateConfig("tooltip", nil, "cursoroffset", "20")
pfUI:UpdateConfig("tooltip", nil, "extguild", "1") pfUI:UpdateConfig("tooltip", nil, "extguild", "1")
pfUI:UpdateConfig("tooltip", nil, "itemid", "0") pfUI:UpdateConfig("tooltip", nil, "itemid", "0")
pfUI:UpdateConfig("tooltip", nil, "movespeed", "0")
pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8") pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8")
pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0") pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0")
pfUI:UpdateConfig("tooltip", "compare", "basestats", "1") pfUI:UpdateConfig("tooltip", "compare", "basestats", "1")
@@ -1098,13 +1109,6 @@ function pfUI:MigrateConfig()
end end
end end
-- migrating rangecheck interval (> 3.2.2)
if checkversion(3, 2, 2) then
if tonumber(pfUI_config.unitframes.rangechecki) <= 1 then
pfUI_config.unitframes.rangechecki = "2"
end
end
-- migrating legacy buff/debuff naming (> 3.5.0) -- migrating legacy buff/debuff naming (> 3.5.0)
if checkversion(3, 5, 0) then if checkversion(3, 5, 0) then
local unitframes = { "player", "target", "focus", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback" } local unitframes = { "player", "target", "focus", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback" }
@@ -1371,7 +1375,6 @@ function pfUI:MigrateConfig()
end end
end end
-- Remove "Show only own debuffs" from unitframes and nameplates -- Remove "Show only own debuffs" from unitframes and nameplates
-- (feature was removed; only Target Debuff Bar in buffwatch keeps it) -- (feature was removed; only Target Debuff Bar in buffwatch keeps it)
if pfUI_config.nameplates then if pfUI_config.nameplates then
@@ -1387,5 +1390,17 @@ function pfUI:MigrateConfig()
end end
end end
pfUI_config.version = pfUI.version.string -- Stamp the version we've migrated to. Dev / git-cloned builds carry
-- "@project-version@" in the toc, which pfUI.lua normalizes to "dev" —
-- writing that here would have checkversion() parse it back as 0/0/0 on
-- the next /reload and re-fire every migration block, clobbering user
-- toggles (notably the >3.6.1 buff migration that rewrites
-- buffs.{buffs,debuffs,weapons} from the deprecated global.hide{,w}buff
-- knobs). Stamp a far-future sentinel instead so checkversion() can't
-- flip true again on subsequent loads.
if pfUI.version.string == "dev" then
pfUI_config.version = "999.999.999"
else
pfUI_config.version = pfUI.version.string
end
end end
+5 -8
View File
@@ -569,7 +569,7 @@ end
-- 'frame' [frame] the modelframe that should be used -- 'frame' [frame] the modelframe that should be used
function pfUI.api.EnableClickRotate(frame) function pfUI.api.EnableClickRotate(frame)
frame:EnableMouse(true) frame:EnableMouse(true)
HookScript(frame, "OnUpdate", function() frame:HookScript("OnUpdate", function()
if this.rotate then if this.rotate then
local x,_ = GetCursorPosition() local x,_ = GetCursorPosition()
if this.curx > x then if this.curx > x then
@@ -582,14 +582,14 @@ function pfUI.api.EnableClickRotate(frame)
end end
end) end)
HookScript(frame, "OnMouseDown", function() frame:HookScript("OnMouseDown", function()
if arg1 == "LeftButton" then if arg1 == "LeftButton" then
this.rotate = true this.rotate = true
this.curx, this.cury = GetCursorPosition() this.curx, this.cury = GetCursorPosition()
end end
end) end)
HookScript(frame, "OnMouseUp", function() frame:HookScript("OnMouseUp", function()
this.rotate, this.curx, this.cury = nil, nil, nil this.rotate, this.curx, this.cury = nil, nil, nil
end) end)
end end
@@ -615,7 +615,6 @@ function pfUI.api.SetHighlight(frame, cr, cg, cb)
frame.rr, frame.rg, frame.rb, frame.ra = GetStringColor(pfUI_config.appearance.border.color) frame.rr, frame.rg, frame.rb, frame.ra = GetStringColor(pfUI_config.appearance.border.color)
if not frame.pfEnterLeave then if not frame.pfEnterLeave then
if not frame.HookScript then frame.HookScript = HookScript end
local enter, leave = frame:GetScript("OnEnter"), frame:GetScript("OnLeave") local enter, leave = frame:GetScript("OnEnter"), frame:GetScript("OnLeave")
if enter then if enter then
@@ -844,7 +843,6 @@ function pfUI.api.SkinScrollbar(frame, always)
-- always show parent frame -- always show parent frame
if always then if always then
RunOOC(function() RunOOC(function()
if not parent.HookScript then parent.HookScript = HookScript end
parent:HookScript("OnHide", function() this:Show() end) parent:HookScript("OnHide", function() this:Show() end)
end) end)
end end
@@ -1172,10 +1170,9 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
local width = 200 local width = 200
-- delay the auto sizing, to make sure the font rendering happened -- delay the auto sizing, to make sure the font rendering happened
question:SetScript("OnUpdate", function() RunNextFrame(function()
if question.text:GetStringWidth() > width then width = question.text:GetStringWidth() end if question.text:GetStringWidth() > width then width = question.text:GetStringWidth() end
question:SetWidth( width + 2*padding) question:SetWidth(width + 2*padding)
this:SetScript("OnUpdate", nil)
end) end)
end end
+25 -27
View File
@@ -19,10 +19,9 @@ function pfUI.uf.ClearGuidTracking()
end end
-- slash command to toggle unitframe test mode -- slash command to toggle unitframe test mode
_G.SLASH_PFTEST1, _G.SLASH_PFTEST2 = "/pftest", "/pfuftest" pfUI.api.RegisterSlashCommand("PFTEST", { "/pftest", "/pfuftest" }, function()
_G.SlashCmdList.PFTEST = function()
pfUI.uf.showall = not pfUI.uf.showall pfUI.uf.showall = not pfUI.uf.showall
end end, true)
-- HoT buff indicators that need name verification because their icons are -- HoT buff indicators that need name verification because their icons are
-- reused by other spells. Maps icon (lowercased) → expected aura name + -- reused by other spells. Maps icon (lowercased) → expected aura name +
@@ -73,8 +72,8 @@ end
local function BuffOnClick() local function BuffOnClick()
if this:GetParent().label == "player" then if this:GetParent().label == "player" then
local bid = GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, "HELPFUL") local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, "HELPFUL")
if bid >= 0 then CancelPlayerBuff(bid) end if aura and aura.spellId then C_Spell.CancelSpellByID(aura.spellId) end
end end
end end
@@ -114,8 +113,8 @@ end
local function DebuffOnClick() local function DebuffOnClick()
if this:GetParent().label == "player" then if this:GetParent().label == "player" then
local bid = GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, "HARMFUL") local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, "HARMFUL")
if bid >= 0 then CancelPlayerBuff(bid) end if aura and aura.spellId then C_Spell.CancelSpellByID(aura.spellId) end
end end
end end
@@ -305,12 +304,12 @@ function pfUI.uf:UpdateVisibility()
local id = self.cache_raid local id = self.cache_raid
-- always show self in raidframes -- always show self in raidframes
if not UnitInRaid("player") and GetNumPartyMembers() == 0 and C.unitframes.selfinraid == "1" and id == 1 then if not IsInRaid() and IsInGroup() and C.unitframes.selfinraid == "1" and id == 1 then
self.id = "" self.id = ""
self.label = "player" self.label = "player"
-- use raidframes for groups -- use raidframes for groups
elseif not UnitInRaid("player") and GetNumPartyMembers() > 0 and C.unitframes.raidforgroup == "1" then elseif not IsInRaid() and IsInGroup() and C.unitframes.raidforgroup == "1" then
if id == 1 then if id == 1 then
self.id = "" self.id = ""
self.label = "player" self.label = "player"
@@ -347,12 +346,12 @@ function pfUI.uf:UpdateVisibility()
-- frame shall not be visible -- frame shall not be visible
visibility = "hide" visibility = "hide"
self.visible = nil self.visible = nil
elseif C["unitframes"]["group"]["hide_in_raid"] == "1" and self.label and strsub(self.label,0,5) == "party" and UnitInRaid("player") then 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 -- hide group while in raid and option is set
visibility = "hide" visibility = "hide"
self.visible = nil self.visible = nil
elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" ) elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" )
and (GetNumPartyMembers() <= 0 or (C["unitframes"]["group"]["hide_in_raid"] == "1" and UnitInRaid("player"))) then 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 -- hide self in group if solo or hide in raid is set
visibility = "hide" visibility = "hide"
self.visible = nil self.visible = nil
@@ -778,8 +777,7 @@ function pfUI.uf:UpdateConfig()
invert_v * (i-1-row*perrow)*(multiply*default_border + f.config.buffsize + 1), invert_v * (i-1-row*perrow)*(multiply*default_border + f.config.buffsize + 1),
invert_h * (row*(multiply*default_border + f.config.buffsize + 1) + (multiply*default_border + 1))) invert_h * (row*(multiply*default_border + f.config.buffsize + 1) + (multiply*default_border + 1)))
f.buffs[i]:SetWidth(f.config.buffsize) f.buffs[i]:SetSize(f.config.buffsize, f.config.buffsize)
f.buffs[i]:SetHeight(f.config.buffsize)
-- Create CD frame if it doesn't exist -- Create CD frame if it doesn't exist
if not f.buffs[i].cd then if not f.buffs[i].cd then
@@ -808,11 +806,7 @@ function pfUI.uf:UpdateConfig()
-- immediately show/hide existing cooldown text -- immediately show/hide existing cooldown text
if f.buffs[i].cd.pfCooldownText then if f.buffs[i].cd.pfCooldownText then
if cooldown_text == 1 then f.buffs[i].cd.pfCooldownText:SetShown(cooldown_text == 1)
f.buffs[i].cd.pfCooldownText:Show()
else
f.buffs[i].cd.pfCooldownText:Hide()
end
end end
f.buffs[i].id = i f.buffs[i].id = i
@@ -1014,7 +1008,7 @@ function pfUI.uf.OnEvent()
-- Raid frames: update_full is set by raid.lua GUID tracker -- Raid frames: update_full is set by raid.lua GUID tracker
elseif this.label == "pet" and event == "UNIT_PET" then elseif this.label == "pet" and event == "UNIT_PET" then
this.update_full = true this.update_full = true
elseif this.label == "player" and (event == "PLAYER_AURAS_CHANGED" or event == "UNIT_INVENTORY_CHANGED") then elseif this.label == "player" and (event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_EQUIPMENT_CHANGED") then
this.update_aura = true this.update_aura = true
elseif this.label == "pet" and event == "UNIT_HAPPINESS" then elseif this.label == "pet" and event == "UNIT_HAPPINESS" then
this.update_full = true this.update_full = true
@@ -1552,7 +1546,7 @@ function pfUI.uf:EnableEvents()
f:RegisterEvent("UNIT_FACTION") f:RegisterEvent("UNIT_FACTION")
f:RegisterEvent("UNIT_AURA") -- frame=buff, frame=debuff f:RegisterEvent("UNIT_AURA") -- frame=buff, frame=debuff
f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff
f:RegisterEvent("UNIT_INVENTORY_CHANGED") -- label=player && frame=buff f:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs)
f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon
f:RegisterEvent("PARTY_LEADER_CHANGED") -- frame=leaderIcon f:RegisterEvent("PARTY_LEADER_CHANGED") -- frame=leaderIcon
f:RegisterEvent("RAID_ROSTER_UPDATE") -- label=raidIcon f:RegisterEvent("RAID_ROSTER_UPDATE") -- label=raidIcon
@@ -1773,7 +1767,7 @@ function pfUI.uf:RefreshIndicators(unit)
local unitstr = unit.label .. unit.id local unitstr = unit.label .. unit.id
if unit.leaderIcon then -- Leader Icon if unit.leaderIcon then -- Leader Icon
if unit.config.leadericon == "1" and UnitIsPartyLeader(unitstr) and ( GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0 ) then if unit.config.leadericon == "1" and UnitIsPartyLeader(unitstr) and IsInGroup() then
unit.leaderIcon:Show() unit.leaderIcon:Show()
else else
unit.leaderIcon:Hide() unit.leaderIcon:Hide()
@@ -1902,6 +1896,8 @@ function pfUI.uf:RefreshUnit(unit, component)
end end
if duration > 0 then if duration > 0 then
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1) CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
elseif aura.duration > 0 then elseif aura.duration > 0 then
local guid = UnitGUID(unitstr) local guid = UnitGUID(unitstr)
@@ -1912,6 +1908,8 @@ function pfUI.uf:RefreshUnit(unit, component)
else else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
else else
unit.buffs[i]:Hide() unit.buffs[i]:Hide()
@@ -2005,7 +2003,11 @@ function pfUI.uf:RefreshUnit(unit, component)
end end
if duration > 0 then if duration > 0 then
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1) CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1)
else
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
end end
else
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
end end
if stacks > 1 then if stacks > 1 then
@@ -2838,8 +2840,6 @@ function pfUI.uf:GetStatusValue(unit, pos)
-- Use libhealth for mob health estimation (overrides Nampower/Standard) -- Use libhealth for mob health estimation (overrides Nampower/Standard)
if pfUI.libhealth and pfUI.libhealth.enabled then if pfUI.libhealth and pfUI.libhealth.enabled then
rhp, rhpmax = pfUI.libhealth:GetUnitHealth(unitstr) rhp, rhpmax = pfUI.libhealth:GetUnitHealth(unitstr)
elseif unit.label == "target" and (MobHealth3 or MobHealthFrame) and MobHealth_GetTargetCurHP() then
rhp, rhpmax = MobHealth_GetTargetCurHP(), MobHealth_GetTargetMaxHP()
end end
if config == "unit" then if config == "unit" then
@@ -3014,9 +3014,7 @@ end
-- ============================================================================ -- ============================================================================
-- Slash Commands for Stats Frame -- Slash Commands for Stats Frame
-- ============================================================================ -- ============================================================================
_G.SLASH_PFUISTATS1 = "/pfuistats" pfUI.api.RegisterSlashCommand("PFUISTATS", { "/pfuistats", "/ufstats" }, function(msg)
_G.SLASH_PFUISTATS2 = "/ufstats"
_G.SlashCmdList["PFUISTATS"] = function(msg)
msg = string.lower(msg or "") msg = string.lower(msg or "")
if not pfUI.uf.stats then if not pfUI.uf.stats then
@@ -3068,4 +3066,4 @@ _G.SlashCmdList["PFUISTATS"] = function(msg)
end end
end end
end end
end end, true)
-27
View File
@@ -28,33 +28,6 @@ ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil
UNITFRAME_SECURE_TEMPLATE = nil UNITFRAME_SECURE_TEMPLATE = nil
--[[ Vanilla API Extensions ]]-- --[[ Vanilla API Extensions ]]--
function hooksecurefunc(tbl, name, func, prepend)
if type(tbl) == "string" then
prepend, func, name, tbl = func, name, tbl, _G
end
if not tbl or not tbl[name] then return end
pfUI.hooks[tostring(func)] = {}
pfUI.hooks[tostring(func)]["old"] = tbl[name]
pfUI.hooks[tostring(func)]["new"] = func
if prepend then
pfUI.hooks[tostring(func)]["function"] = function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
pfUI.hooks[tostring(func)]["new"](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
return pfUI.hooks[tostring(func)]["old"](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
end
else
pfUI.hooks[tostring(func)]["function"] = function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
local ok, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16 = pcall(pfUI.hooks[tostring(func)]["old"], a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
if not ok then return end
pfUI.hooks[tostring(func)]["new"](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
return r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16
end
end
tbl[name] = pfUI.hooks[tostring(func)]["function"]
end
do -- GetItemInfo do -- GetItemInfo
local name, link, rarity, minlevel, itype, isubtype, stack local name, link, rarity, minlevel, itype, isubtype, stack
-2620
View File
File diff suppressed because it is too large Load Diff
-2634
View File
File diff suppressed because it is too large Load Diff
-2628
View File
File diff suppressed because it is too large Load Diff
-2625
View File
File diff suppressed because it is too large Load Diff
-2591
View File
File diff suppressed because it is too large Load Diff
-2628
View File
File diff suppressed because it is too large Load Diff
-2612
View File
File diff suppressed because it is too large Load Diff
-72
View File
@@ -1,20 +1,3 @@
-- this table is added in later expansions
CLASS_SORT_ORDER = CLASS_SORT_ORDER or {
"WARRIOR",
"DEATHKNIGHT",
"PALADIN",
"MONK",
"PRIEST",
"SHAMAN",
"DRUID",
"ROGUE",
"MAGE",
"WARLOCK",
"HUNTER",
"DEMONHUNTER",
"UNKNOWN",
}
CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS or { CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS or {
["WARRIOR"] = {0, 0.25, 0, 0.25}, ["WARRIOR"] = {0, 0.25, 0, 0.25},
["MAGE"] = {0.25, 0.49609375, 0, 0.25}, ["MAGE"] = {0.25, 0.49609375, 0, 0.25},
@@ -2417,58 +2400,3 @@ pfSellData = {
[23725]=2600, [23727]=2600, [23728]=2600, [23743]=11, [24071]=393470, [24101]=400000, [23725]=2600, [23727]=2600, [23728]=2600, [23743]=11, [24071]=393470, [24101]=400000,
[24102]=400000, [24222]=221054, [24231]=211, [24232]=900, [24281]=1210, [24282]=18250, [24102]=400000, [24222]=221054, [24231]=211, [24232]=900, [24281]=1210, [24282]=18250,
[24283]=152345, [24358]=365815 } [24283]=152345, [24358]=365815 }
pfMapOverlayData = {
["Alterac"] = {"CHILLWINDPOINT:308:250:660:261", "CORRAHNSDAGGER:173:277:411:391", "CRUSHRIDGEHOLD:269:230:339:167:283:0", "DALARAN:277:283:39:275", "DANDREDSFOLD:273:224:282:0", "GALLOWSCORNER:183:185:415:287", "GAVINSNAZE:140:161:235:486", "GROWLESSCAVE:175:156:327:382", "LORDAMEREINTERNMENTCAMP:315:255:55:413", "MISTYSHORE:204:268:207:139", "RUINSOFALTERAC:252:249:274:201", "RAVENHOLDTMANOR:217:165:737:452", "SOFERASNAZE:241:312:469:311", "STRAHNBRAD:355:289:555:110", "THEHEADLAND:142:187:325:481", "THEUPLANDS:219:183:470:84",},
["AlteracValley"] = {"DUNBALDAR:254:228:356:18:3299:3318", "FROSTWOLFKEEP:220:278:408:382:3299:3318", "ICEBLOODGARRISON:286:288:343:179:3299:3318",},
["Arathi"] = {"BOULDERFISTHALL:190:214:442:371", "BOULDERGOR:222:214:244:167", "CIRCLEOFEASTBINDING:140:213:568:125", "CIRCLEOFINNERBINDING:186:164:302:321", "CIRCLEOFOUTERBINDING:149:137:429:301", "CIRCLEOFWESTBINDING:156:189:152:71", "DABYRIESFARMSTEAD:160:171:482:197", "FALDIRSCOVE:247:197:180:428", "FARWELLSTEAD:112:128:267:31", "GOSHEKFARM:208:177:539:284", "HAMMERFALL:182:235:665:128:318:0", "NORTHFOLDMANOR:202:205:204:119", "REFUGEPOINT:157:189:380:214", "RUINSOFZULRASAZ:160:160:346:29", "STROMGARDEKEEP:220:213:122:295", "THANDOLSPAN:175:198:369:424", "THORADINSWALL:173:225:103:148", "WITHERBARKVILLAGE:197:194:566:339", "WILDTUSKVILLAGE:183:102:379:145",},
["Ashenvale"] = {"ASTRANAAR:190:173:281:259", "BOUGHSHADOW:140:190:862:157", "FALLENSKYLAKE:219:190:555:433:432:0", "FELFIREHILL:241:244:720:349", "FIRESCARSHRINE:153:161:194:332", "FORESTSONG:162:142:817:232", "IRISLAKE:185:190:401:226", "LAKEFALATHIM:115:181:138:144", "MAESTRASPOST:198:290:216:46", "MYSTRALLAKE:262:228:363:354:420:2359", "NIGHTRUN:212:244:604:263:431:0", "RAYNEWOODRETREAT:165:235:527:243", "SATYRNAAR:163:172:702:230", "THEHOWLINGVALE:196:172:471:147", "THERUINSOFSTARDUST:141:135:268:382", "THESHRINEOFAESSINA:205:179:113:268", "THEZORAMSTRAND:228:235:26:33", "THISTLEFURVILLAGE:243:184:210:164", "WARSONGLUMBERCAMP:185:146:804:319",},
["Aszhara"] = {"BAYOFSTORMS:257:285:486:208", "BITTERREACHES:231:173:651:46", "FORLORNRIDGE:210:247:198:373", "HALDARRENCAMPMENT:191:138:83:339", "JAGGEDREEF:556:153:373:9", "LAKEMENNAR:301:184:302:437", "LEGASHENCAMPMENT:224:131:484:49", "RAVENCRESTMONUMENT:227:115:559:506", "RUINSOFELDARATH:251:268:246:228", "SHADOWSONGSHRINE:213:170:43:428", "SOUTHRIDGEBEACH:355:207:397:360", "TEMPLEOFARKKORAN:177:187:689:160", "THALASSIANBASECAMP:224:144:507:126", "THERUINEDREACHES:384:116:403:549", "THESHATTEREDSTRAND:148:199:412:202", "TIMBERMAWHOLD:209:194:258:175", "TOWEROFELDARA:107:145:824:114", "URSOLAN:219:212:327:94", "VALORMOK:201:158:93:236",},
["Badlands"] = {"AGMONDSEND:249:256:354:396", "ANGORFORTRESS:186:241:327:101", "APOCRYPHANSREST:251:195:20:315", "CAMPBOFF:195:207:506:361", "CAMPCAGG:247:205:15:432:347:0", "CAMPKOSH:214:211:556:56", "CRYSTALLINEPINNACLE:140:155:609:337", "DUSTWINDGULCH:229:190:506:216", "HAMMERTOESDIGSITE:185:177:455:131", "KARGATH:232:245:0:154", "LETHLORRAVINE:344:395:627:173", "MIRAGEFLATS:276:229:153:387", "REDBRANDSDIGSITE:325:155:496:454", "RUINSOFCORTHAN:210:200:203:124", "SCALEBANERIDGE:205:179:751:142", "THEDUSTBOWL:253:266:166:203", "THEMAKERSTERRACE:230:172:399:16:1517:0", "VALLEYOFFANGS:215:215:358:265",},
["Balor"] = {"BILGERATCOMPOUND:174:141:323:76", "CROAKINGPLATEAU:371:154:397:201", "GRAHANESTATE:93:116:306:392", "GULLWINGWRECKAGE:110:105:221:34", "LANGSTONORCHARD:102:129:410:346", "RUINSOFBREEZEHAVEN:163:141:450:256", "SCURRYINGTHICKET:144:158:283:161", "SIOUTPOST:175:232:597:389", "SORROWMORELAKE:174:140:303:277", "STORMBREAKERPOINT:243:197:583:212", "STORMREAVERSPIRE:151:119:491:429", "STORMWROUGHTCASTLE:202:166:458:332", "TREACHEROUSCRAGS:243:171:381:457", "VANDERFARMSTEAD:146:140:330:372", "WINDROCKCLIFFS:192:279:256:303",},
["Barrens"] = {"AGAMAGOR:183:173:348:242", "ANCHORSEDGE:118:83:690:300", "BAELMODAN:120:123:435:482", "BLACKTHORNRIDGE:140:123:340:465", "BOULDERLODEMINE:109:105:560:0", "BRAMBLESCAR:112:152:446:304", "CAMPTAURAJO:130:113:370:355", "DREADMISTPEAK:121:93:423:68", "FARWATCHPOST:85:152:572:58", "FIELDOFGIANTS:194:137:363:410", "GROLDOMFARM:112:107:499:67", "HONORSSTAND:128:128:306:130", "LUSHWATEROASIS:160:169:372:186", "NORTHWATCHFOLD:134:107:533:310", "RAPTORGROUNDS:101:99:512:299", "RATCHET:116:118:556:190", "RAZORFENDOWNS:137:106:414:562", "RAZORFENKRAUL:116:123:345:539", "THECROSSROADS:142:140:436:127", "THEDRYHILLS:185:134:324:36", "THEFORGOTTENPOOLS:104:116:391:120", "THEMERCHANTCOAST:80:123:584:249", "THEMORSHANRAMPART:118:91:418:0", "THESLUDGEFEN:153:113:463:0", "THESTAGNANTOASIS:147:126:484:211", "THORNHILL:128:120:502:123",},
["BlackstoneIsland"] = {"BLACKASHCOALPITS:303:474:209:178", "BLACKASHMINE:251:204:397:222", "GAZZIKSWORKSHOP:329:247:439:403", "RUSTGATELUMBERYARD:217:245:551:241", "RUSTGATERIDGE:396:226:372:360", "THEWATERHOLE:386:239:382:43", "VENTURECOSLUMS:270:192:498:129",},
["BlastedLands"] = {"ALTAROFSTORMS:169:142:318:141", "DARKPORTAL:254:208:460:265", "DREADMAULHOLD:200:171:351:21", "DREADMAULPOST:228:183:370:204", "GARRISONARMORY:155:192:480:15", "NETHERGARDEKEEP:169:178:567:35", "RISEOFTHEDEFILER:150:131:414:131", "SERPENTSCOIL:211:155:510:148", "THETAINTEDSCAR:370:438:220:184",},
["BurningSteppes"] = {"ALTAROFSTORMS:215:210:41:114", "BLACKROCKMOUNTAIN:249:272:178:104", "BLACKROCKPASS:259:299:597:285", "BLACKROCKSTRONGHOLD:232:258:342:120", "DRACODAR:405:309:61:262", "DREADMAULROCK:203:214:717:174", "MORGANSVIGIL:287:265:715:311", "PILLAROFASH:313:261:382:289", "RUINSOFTHAURISSAN:258:275:521:105", "TERRORWINGPATH:271:296:730:52",},
["Darkshore"] = {"AMETHARAN:163:178:340:323", "AUBERDINE:134:179:323:191", "BASHALARAN:153:171:379:199", "CLIFFSPRINGRIVER:205:161:391:112:445:0", "GROVEOFTHEANCIENTS:180:138:314:436", "REMTRAVELSEXCAVATION:148:164:245:504", "RUINSOFMATHYSTRA:176:205:519:3", "THEMASTERSGLAIVE:146:139:346:529", "TOWEROFALTHALAXX:142:159:484:110",},
["DeadwindPass"] = {"DEADMANSCROSSING:368:353:257:81:2938:0", "KARAZHAN:287:234:277:342", "THEVICE:254:257:433:305",},
["Desolace"] = {"ETHELRETHOR:189:237:318:68", "GELKISVILLAGE:181:235:300:433", "KODOGRAVEYARD:253:237:396:251", "KOLKARVILLAGE:206:208:615:221", "KORMEKSHUT:156:147:562:187", "MAGRAMVILLAGE:182:272:606:373", "MANNOROCCOVEN:270:271:408:385", "NIJELSPOINT:188:246:561:0", "RANAZJARISLE:88:89:248:11", "SARGERON:274:237:632:38", "SHADOWBREAKRAVINE:190:181:698:454", "SHADOWPREYVILLAGE:215:218:175:395", "TETHRISARAN:185:137:440:0", "THUNDERAXEFORTRESS:174:207:455:108", "VALLEYOFSPEARS:234:274:217:221",},
["DunMorogh"] = {"AMBERSTILLRANCH:105:104:582:288", "ANVILMAR:209:159:167:414:77:0", "BREWNALLVILLAGE:94:102:263:256", "CHILLBREEZEVALLEY:160:113:281:304", "COLDRIDGEPASS:134:123:300:387", "FROSTMANEHOLD:112:118:225:291", "GNOMERAGON:158:176:178:164", "GOLBOLARQUARRY:144:149:619:301", "HELMSBEDLAKE:131:153:706:284:716:0", "ICEFLOWLAKE:117:167:286:173", "IRONFORGE:291:187:414:166", "IRONFORGEAIRFIELDS:213:233:582:100", "KHARANOS:175:186:397:296:189:0", "MISTYPINEREFUGE:117:155:507:224", "NORTHERNGATEOUTPOST:115:155:766:177", "RUGFORDSMOUNTAINREST:199:114:739:406", "SHIMMERRIDGE:115:173:355:169", "SOUTHERNGATEOUTPOST:113:106:800:284", "THEGRIZZLEDDEN:178:155:321:328", "THETUNDRIDHILLS:135:115:531:328",},
["Durotar"] = {"DRYGULCHRAVINE:139:134:438:92:410:0", "ECHOISLES:175:236:561:432", "KOLKARCRAG:131:96:426:487", "ORGRIMMAR:427:150:251:0", "RAZORHILL:188:207:445:182", "RAZORMANEGROUNDS:196:202:318:204", "SENJINVILLAGE:133:167:485:398", "SKULLROCK:118:79:469:51", "SPARKWATERPORT:122:134:522:94", "THUNDERRIDGE:156:174:341:72", "TIRAGARDEKEEP:160:148:477:300", "VALLEYOFTRIALS:186:184:370:335",},
["Duskwood"] = {"ADDLESSTEAD:255:231:67:353", "BRIGHTWOODGROVE:191:315:520:131", "DARKSHIRE:291:259:644:173:576:0", "MANORMISTMANTLE:174:150:668:134", "RAVENHILL:166:115:117:317", "RAVENHILLCEMETARY:323:276:99:165:243:0", "THEDARKENEDBANK:886:184:105:44", "THEHUSHEDBANK:136:306:29:143", "THEROTTINGORCHARD:231:210:549:380", "THEYORGENFARMSTEAD:211:235:403:393", "TRANQUILGARDENSCEMETARY:204:196:692:363", "TWILIGHTGROVE:327:384:314:99", "VULGOLOGREMOUND:231:266:259:365",},
["Dustwallow"] = {"ALCAZISLAND:176:179:671:29", "BACKBAYWETLANDS:384:243:248:196", "BRACKENWALLVILLAGE:264:264:239:2:507:0", "THEDENOFFLAME:246:235:264:320", "THERAMOREISLE:214:187:542:232", "THEWYRMBOG:263:229:372:387", "WITCHHILL:230:311:431:1:518:0", "WESTHAVENHOLLOW:166:149:603:428",},
["EasternPlaguelands"] = {"BLACKWOODLAKE:217:221:451:207", "CORINSCROSSING:153:146:544:373", "CROWNGUARDTOWER:193:152:299:408", "DARROWSHIRE:195:167:316:497", "EASTWALLTOWER:164:143:600:250", "FORLORNSUMMIT:192:185:64:225", "LAKEMERELDAR:240:164:543:469", "LIGHTSHOPECHAPEL:165:238:721:304", "NORTHDALE:172:191:628:135", "NORTHPASSTOWER:228:178:465:118", "PESTILENTSCAR:189:241:416:349", "PLAGUEWOOD:346:258:178:92", "QUELLITHIENLODGE:216:136:430:44", "STRATHOLME:227:189:202:15", "TERRORDALE:175:129:86:137", "THEFUNGALVALE:202:199:277:267", "THEINFECTISSCAR:183:262:628:298", "THEMARRISSTEAD:182:194:164:367", "THENOXIOUSGLADE:211:202:730:172", "THEUNDERCROFT:171:136:178:484", "THONDRORILRIVER:210:348:12:235", "TYRSHAND:201:159:724:475", "ZULMASHAR:189:152:621:36",},
["Elwynn"] = {"BRACKWELLPUMPKINPATCH:222:223:592:431", "CRYSTALLAKE:196:192:435:345", "EASTVALELOGGINGCAMP:237:190:713:339", "FARGODEEPMINE:238:230:247:438:63:64", "FORESTSEDGE:242:335:132:333:120:0", "GOLDSHIRE:219:199:261:280", "JERODSLANDING:206:212:437:445", "NORTHSHIREVALLEY:237:244:390:154", "RIDGEPOINTTOWER:287:207:710:445", "STONECAIRNLAKE:278:230:601:203", "STORMWIND:452:392:21:3", "TOWEROFAZORA:232:226:561:303:54:0",},
["Felwood"] = {"BLOODVENOMFALLS:225:132:297:269", "DEADWOODVILLAGE:162:129:415:539", "EMERALDSANCTUARY:171:150:411:436", "FELPAWVILLAGE:224:138:489:0:1769:0", "IRONTREEWOODS:200:203:426:60", "JADEFIREGLEN:151:145:339:469", "JADEFIRERUN:181:159:338:35", "JAEDENAR:236:122:275:333", "MORLOSARAN:129:151:503:515", "RUINSOFCONSTELLAS:224:142:303:387", "SHATTERSCARVALE:218:186:315:130", "TALONBRANCHGLADE:145:130:554:97",},
["Feralas"] = {"CAMPMOJACHE:146:148:694:238", "CHIMAERAROOSTVALE:106:150:785:321", "DIREMAUL:214:180:461:208", "DREAMBOUGH:141:122:459:0", "FERALSCARVALE:103:104:491:335", "FRAYFEATHERHIGHLANDS:101:163:485:391", "GORDUNNIOUTPOST:154:128:697:148", "GRIMTOTEMCOMPOUND:110:186:630:172", "ISLEOFDREAD:205:285:197:381", "LOWERWILDS:214:152:756:204", "ONEIROS:101:103:499:74", "RUINSOFISILDIEN:179:241:547:326", "RUINSOFRAVENWIND:178:149:310:0", "SARDORISLE:167:168:216:241", "THEFORGOTTENCOAST:132:316:409:258", "THETWINCOLOSSALS:278:235:323:78", "THEWRITHINGDEEP:214:205:625:305:2519:0",},
["Gillijim"] = {"DEEPTIDESANCTUM:204:117:199:512", "DISTILLERYISLE:90:69:234:216", "FAELONSFOLLY:108:103:331:304", "GILLIJIMSTRAND:108:324:641:160", "JADEMINE:141:149:500:256", "KALKORPOINT:123:115:421:210", "KAZONISLAND:194:113:432:51", "MAULOGGPOST:137:227:512:401", "MAULOGGREFUGE:231:132:615:465", "RUINSOFZULRAZAR:215:136:369:384", "SILVERCOAST:245:184:311:386", "SILVERSANDBAR:47:128:256:398", "SOUTHSEASANDBAR:125:172:178:63", "TANGLEWOOD:135:188:576:297", "ZULRAZAR:137:168:375:282",},
["Gilneas"] = {"BLACKTHORNSCAMP:160:163:32:171", "BROLOKMOUND:140:120:467:349", "DAWNSTONEMINE:130:144:292:155", "FREYSHEARKEEP:72:70:624:405", "GILNEASCITY:295:242:89:105", "GLAYMORESTEAD:140:127:483:129", "GREYMANESWATCH:119:158:580:311", "HOLLOWWEBCEMETARY:180:167:215:395", "HOLLOWWEBWOODS:171:118:341:452", "NORTHGATETOWER:130:137:397:172", "OLDROCKPASS:155:165:290:37", "RAVENSHIRE:194:237:472:431", "RAVENWOODKEEP:253:174:496:494", "ROSEWICKPLANTATION:134:140:331:155", "RUINSOFGREYSHIRE:192:144:230:260", "SHADEMORETAVERN:117:120:279:336", "SOUTHMIREORCHARD:202:133:302:354", "STILLWARDCHURCH:213:166:471:220", "THEDRYROCKMINE:129:127:145:262", "THEDRYROCKPIT:230:217:69:295", "THEGREYMANEWALL:131:173:412:48", "THEOVERGROWNACRE:134:154:390:269",},
["GrimReaches"] = {"BAGGOTHSRAMPART:112:113:399:238", "BARLEYCRESTFARMSTEAD:116:88:499:222", "BRANGARSFOLLY:108:123:568:33", "DUNKITHAS:137:95:470:329", "EASTRIDGEOUTPOST:166:107:374:165", "GETHKAR:118:124:444:84", "GROLDANSEXCAVATION:125:117:567:231", "LAKEKITHAS:217:150:481:270", "RUINSOFSTOLGAZKEEP:134:146:366:53", "SALGAZMINES:150:119:535:380", "SHATTERBLADEPOST:186:130:512:126", "SLATEBEARDSFORGE:136:99:457:404", "THEGRIMHOLLOW:288:200:396:463", "THEHIGHPASS:97:141:409:315", "ZARMGETHPOINT:143:76:405:16", "ZARMGETHSTRONGHOLD:120:150:487:17",},
["Hilsbrad"] = {"AZURELOADMINE:146:174:188:288", "DARROWHILL:173:132:426:164", "DUNGAROK:225:257:644:305", "DURNHOLDEKEEP:367:347:614:82", "EASTERNSTRAND:208:301:537:350", "HILLSBRADFIELDS:282:257:211:166", "NETHANDERSTEAD:190:215:554:249", "PURGATIONISLE:120:94:111:485", "SOUTHPOINTTOWER:271:207:11:203", "SOUTHSHORE:219:245:423:213", "TARRENMILL:195:291:524:9", "WESTERNSTRAND:272:139:215:377",},
["Hinterlands"] = {"AERIEPEAK:240:195:20:250", "AGOLWATHA:188:180:382:173", "HIRIWATHA:201:118:188:314", "JINTHAALOR:223:277:514:338", "PLAGUEMISTRAVINE:124:203:170:158", "QUELDANILLODGE:168:181:248:194", "SERADANE:260:263:519:25", "SHADRAALOR:181:170:247:394", "SHAOLWATHA:265:190:580:247", "SKULKROCK:148:133:520:241", "THEALTAROFZUL:186:153:382:371", "THECREEPINGRUIN:168:157:417:268", "THEOVERLOOKCLIFFS:153:299:702:309", "THERASAZTRAILS:118:133:210:381", "VALORWINDLAKE:153:160:329:309",},
["Hyjal"] = {"BARKSKINPLATEAU:207:167:390:297", "BARKSKINVILLAGE:502:293:417:346", "BLEAKHOLLOWCRATER:297:335:401:20", "CIRCLEOFPOWER:277:331:203:63", "DARKHOLLOWPASS:214:237:342:389", "NORDANAAR:139:146:778:118", "NORDRASSILGLADE:418:408:578:0", "RUINSOFTELENNAS:165:193:141:236", "THEEMERALDGATEWAY:309:244:109:353", "ZULHATHA:191:210:3:194",},
["Icepoint"] = {"KANEQNUUN:512:430:237:129",},
["Lapidis"] = {"BRIGHTCOAST:298:213:237:246", "CAELANSREST:147:111:517:230", "CROWNISLAND:140:95:512:33", "GORDOSHHEIGHTS:253:243:256:89", "HAZURRIGLADE:87:82:486:306", "SHANKSREEF:207:90:446:109", "THEROCK:74:72:687:149", "TOWEROFLAPIDIS:203:118:487:175", "WALLOWINGCOAST:240:184:505:326", "ZULHAZU:227:178:348:407",},
["LochModan"] = {"GRIZZLEPAWRIDGE:262:343:326:325", "IRONBANDSEXCAVATIONSITE:321:294:491:276", "MOGROSHSTRONGHOLD:287:265:558:61", "NORTHGATEPASS:195:273:143:24:925:0", "SILVERSTREAMMINE:201:243:247:25", "STONESPLINTERVALLEY:226:255:230:363", "STONEWROUGHTDAM:260:149:354:23", "THEFARSTRIDERLODGE:162:182:741:298", "THELOCH:288:380:367:99", "THELSAMAR:243:205:225:214", "VALLEYOFKINGS:166:224:123:382",},
["Moonglade"] = {"LAKEELUNEARA:530:484:256:101:2361:0",},
["Moonwhisper"] = {"ANSHESRESPITE:170:120:398:81", "ANCESTRALGROUNDS:164:82:295:88", "BLACKROOTHOLD:162:137:495:465", "BLACKROOTVILLAGE:224:143:411:525", "FOULHEARTSANCTUM:157:149:496:232", "GROVEOFTHEMOON:152:146:464:347", "LUNARCLAWDEN:84:60:674:376", "MARASETHIL:96:94:640:417", "MOONHOOFRETREAT:172:144:419:167", "MOONHOOFVILLAGE:163:157:570:188", "MOONSILKHOLLOW:125:114:577:458", "MOROGAIVILLAGE:133:127:571:372", "NARVALISPOINT:162:139:503:118", "RUINSOFNENDIS:183:130:584:275", "STARSHARDCRADLE:122:109:334:121", "TYRANDAS:158:169:580:61", "VYSNAGOSASREST:163:102:420:8",},
["Mulgore"] = {"BAELDUNDIGSITE:175:151:269:227", "BLOODHOOFVILLAGE:236:182:378:311", "PALEMANEROCK:102:183:315:317", "RAVAGEDCARAVAN:110:100:482:269", "REDCLOUDMESA:436:231:285:437:221:0", "REDROCKS:154:138:522:92", "SUNTAILPASS:129:116:533:18", "THEGOLDENPLAINS:184:217:441:91", "THEROLLINGPLAINS:231:166:536:369", "THEVENTURECOMINE:187:204:549:251", "THUNDERBLUFF:254:217:264:70", "THUNDERHORNWATERWELL:118:137:384:248", "WILDMANEWATERWELL:152:111:299:10", "WINDFURYRIDGE:182:119:405:2", "WINTERHOOFWATERWELL:144:107:470:379",},
["Northwind"] = {"ABBEYGARDENS:250:224:512:33", "AMBERSHIRE:234:215:334:278", "AMBERWOODKEEP:243:205:187:308", "BLACKROCKBREACH:255:201:721:222", "BRISTLEWHISKERCAVERN:197:185:568:232", "CINDERFALLPASS:229:229:733:348", "CRAWFORDWINERY:160:222:513:285", "CRYSTALFALLS:320:205:627:464", "GRIMMENLAKE:218:210:550:352", "MERCHANTSHIGHROAD:353:249:256:420", "NORTHRIDGEPOINT:298:314:349:31", "NORTHWINDLOGGINGCAMP:224:185:258:171", "RUINSOFBIRKHAVEN:180:208:645:116", "SHERWOODQUARRY:293:287:702:1", "STILLHEARTPORT:174:155:116:217", "TOWEROFMAGILOU:181:223:189:169", "WITCHCOVEN:147:162:257:81",},
["Redridge"] = {"ALTHERSMILL:222:260:407:134", "GALARDELLVALLEY:234:240:661:168:96:0", "LAKEEVERSTILL:522:260:140:249", "LAKERIDGEHIGHWAY:411:274:196:341", "LAKESHIRE:325:179:92:204", "REDRIDGECANYONS:345:232:132:79:98:0", "RENDERSCAMP:264:248:284:4:998:0", "RENDERSVALLEY:453:248:491:365", "REDWALLKEEP:162:110:830:466", "STONEWATCH:243:289:507:221:999:0", "STONEWATCHFALLS:308:200:601:325", "THREECORNERS:360:337:0:288",},
["SearingGorge"] = {"BLACKCHARCAVE:264:224:84:371", "DUSTFIREVALLEY:446:355:428:11", "FIREWATCHRIDGE:385:413:95:39", "GRIMSILTDIGSITE:292:210:501:306", "TANNERCAMP:292:215:552:413", "THECAULDRON:414:310:257:178", "THESEAOFCINDERS:345:273:256:395",},
["Silithus"] = {"HIVEASHI:431:276:305:34:3426:0", "HIVEREGAL:469:335:262:333:3427:0", "HIVEZORA:343:424:117:188", "SOUTHWINDVILLAGE:346:348:519:83:3077:0", "THECRYSTALVALE:285:274:121:31", "THESCARABWALL:271:145:125:523:2741:0", "TWILIGHTBASECAMP:297:238:355:206:3097:2739",},
["Silverpine"] = {"AMBERMILL:204:210:512:279", "BERENSPERIL:206:146:508:433", "DEEPELEMMINE:127:143:484:274", "FENRISISLE:234:187:593:88:232:0", "MALDENSORCHARD:228:138:479:9:239:0", "NORTHTIDESHOLLOW:152:104:337:141", "OLSENSFARTHING:128:152:401:267", "PYREWOODVILLAGE:122:108:405:456", "SHADOWFANGKEEP:185:131:380:373", "THEDEADFIELD:141:134:419:82", "THEDECREPITFERRY:144:156:475:160", "THEGREYMANEWALL:175:185:398:465", "THESEPULCHER:193:135:360:184", "THESHININGSTRAND:225:192:475:27:227:0", "THESKITTERINGDARK:160:165:302:18",},
["StonetalonMountains"] = {"AMANIALOR:504:252:8:5", "BAELHARDUL:357:252:512:257", "BLACKSANDOILFIELDS:504:504:8:5", "BOULDERSLIDERAVINE:90:76:586:547", "BRAMBLETHORNPASS:382:359:512:257", "BROKENCLIFFMINE:252:504:260:5", "CAMPAPARAJE:222:100:670:552", "GRIMTOTEMPOST:146:77:675:519", "MALAKAJIN:137:86:662:562", "MIRKFALLONLAKE:504:504:260:5", "POWDERTOWN:252:252:260:257", "SISHIRCANYON:252:264:512:257", "STONETALONPEAK:258:252:260:5", "SUNROCKRETREAT:504:252:260:257", "THECHARREDVALE:252:308:260:257", "THEEARTHENRING:504:252:260:257", "VENTURECOMPANYCAMP:252:504:260:5", "WEBWINDERPATH:256:324:512:256", "WINDSHEARCRAG:256:256:512:256:1277:0",},
["Stranglethorn"] = {"BALALRUINS:83:65:245:101", "BALIAMAHRUINS:100:133:376:133", "BLOODSAILCOMPOUND:154:163:201:291", "BOOTYBAY:137:125:207:435:312:0", "CRYSTALVEINMINE:111:114:350:280", "GROMGOLBASECAMP:101:98:265:137", "JAGUEROISLE:132:107:319:498", "KALAIRUINS:86:89:303:93", "KURZENSCOMPOUND:141:139:394:5", "LAKENAZFERITI:124:120:332:60", "MISTVALEVALLEY:120:116:283:372", "MIZJAHRUINS:93:102:315:133", "MOSHOGGOGREMOUND:123:168:435:96", "NEKMANIWELLSPRING:80:102:215:365", "NESINGWARYSEXPEDITION:128:98:274:29", "REBELCAMP:164:86:289:0", "RUINSOFABORAZ:87:87:356:340", "RUINSOFJUBUWAL:100:105:312:304", "RUINSOFZULKUNDA:113:132:201:8", "RUINSOFZULMAMWE:162:122:397:213", "THEARENA:186:176:241:194", "THEVILEREEF:177:160:159:98", "VENTURECOBASECAMP:96:120:391:66", "WILDSHORE:151:175:237:431", "ZIATAJAIRUINS:127:120:364:231", "ZULGURUB:230:213:489:12", "ZUULDAIARUINS:103:105:160:47",},
["SwampOfSorrows"] = {"FALLOWSANCTUARY:348:292:501:0", "ITHARIUSSCAVE:230:232:0:270", "MISTYREEDSTRAND:250:664:752:0:1978:0", "MISTYVALLEY:211:179:29:148", "POOLOFTEARS:280:256:576:227", "SORROWMURK:199:351:734:128", "SPLINTERSPEARJUNCTION:258:222:138:245", "STAGALBOG:328:238:561:386", "STONARD:343:303:287:244", "SORROWGUARDKEEP:220:174:0:266", "THEHARBORAGE:216:187:182:157", "THESHIFTINGMIRE:303:221:294:118",},
["Tanaris"] = {"ABYSSALSANDS:194:159:376:207", "BROKENPILLAR:93:165:481:243", "CAVERNSOFTIME:139:132:567:262", "DUNEMAULCOMPOUND:186:127:337:298", "EASTMOONRUINS:143:130:405:355", "GADGETZAN:156:149:430:102", "LANDSENDBEACH:184:148:456:514", "LOSTRIGGERCOVE:141:173:635:230", "NOONSHADERUINS:129:191:530:43", "SANDSORROWWATCH:175:157:311:111", "SOUTHBREAKSHORE:192:151:510:304", "SOUTHMOONRUINS:185:196:324:366", "STEAMWHEEDLEPORT:136:129:599:86", "SLICKWICKOILRIG:237:181:129:436", "THEGAPINGCHASM:198:188:458:380", "THENOXIOUSLAIR:155:180:265:209", "THISTLESHRUBVALLEY:166:234:217:294", "VALLEYOFTHEWATCHERS:125:115:311:470", "WATERSPRINGFIELD:145:164:516:177", "ZALASHJISDEN:90:127:619:157", "ZULFARRAK:191:166:265:5",},
["TelAbim"] = {"BIXXLESSTOREHOUSE:300:127:368:178", "HIGHVALERISE:191:187:468:248", "TAZZOSSHACK:229:232:453:391", "TELCOBASECAMP:184:183:342:440", "THEDERELICTCAMP:165:205:364:273", "THEJAGGEDISLES:313:256:385:16",},
["Teldrassil"] = {"BANETHILHOLLOW:136:191:392:289", "DARNASSUS:294:252:110:251", "DOLANAAR:170:119:469:327", "GNARLPINEHOLD:230:158:316:417", "LAKEALAMETH:237:163:445:388", "POOLSOFARLITHRIEN:125:180:337:314", "RUTTHERANVILLAGE:110:91:504:548", "SHADOWGLEN:198:201:504:164", "STARBREEZEVILLAGE:153:175:588:303", "THEORACLEGLADE:150:223:279:135", "URSANHEIGHTS:229:154:243:423", "WELLSPRINGLAKE:166:245:383:98:265:0",},
["ThalassianHighlands"] = {"ALAHTHALAS:470:276:429:38", "ANASTERIANPARK:229:219:385:256", "BRINTHILIEN:180:211:395:450", "FELSTRIDERRETREAT:201:207:165:428", "ISLEOFETERNALAUTUMN:204:167:236:117", "RUINSOFNASHALARAN:249:266:36:151", "SILVERSUNMINE:208:189:288:373", "THEFARSTRIDE:230:199:212:255", "THELASTRUNESTONE:179:203:507:379",},
["ThousandNeedles"] = {"CAMPETHOK:289:300:7:1:2237:0", "DARKCLOUDPINNACLE:189:182:267:138", "FREEWINDPOST:193:173:366:273", "HIGHPERCH:178:174:39:164", "SPLITHOOFCRAG:193:175:400:203", "THEGREATLIFT:191:163:213:78", "THESCREECHINGCANYON:240:231:187:206", "THESHIMMERINGFLATS:312:355:612:308", "WINDBREAKCANYON:225:213:501:249",},
["Tirisfal"] = {"AGAMANDMILLS:244:191:342:148", "BALNIRFARMSTEAD:179:153:639:335", "BRIGHTWATERLAKE:176:280:598:143", "BRILL:121:144:540:305", "BULWARK:184:169:712:374", "COLDHEARTHMANOR:136:125:479:328", "CRUSADEROUTPOST:154:121:701:293", "DEATHKNELL:210:186:236:337", "GARRENSHAUNT:148:199:507:154", "GLENSHIRE:163:161:157:393:5033:5039", "MONASTARY:175:157:762:139:161:0", "NIGHTMAREVALE:209:151:370:355", "RUINSOFLORDAERON:299:216:469:370", "SCARLETWATCHPOST:141:223:701:113", "SOLLIDENFARMSTEAD:225:138:247:259", "STEEPCLIFFPORT:121:120:5:350", "STILLWATERPOND:168:123:401:279", "THECORINTHFARMSTEAD:116:152:73:402:5041:0", "THEWHISPERINGFOREST:243:175:86:263:5035:0", "VENOMWEBVALE:199:183:776:225",},
["UngoroCrater"] = {"FIREPLUMERIDGE:277:258:377:185", "GOLAKKAHOTSPRINGS:299:334:133:158", "IRONSTONEPLATEAU:276:281:588:69", "LAKKARITARPITS:553:255:170:13", "TERRORRUN:333:275:166:375", "THEMARSHLANDS:295:341:568:247", "THESLITHERINGSCAR:326:260:378:403",},
["WesternPlaguelands"] = {"CAERDARROW:158:150:608:421", "DALSONSTEARS:209:138:388:271", "DARROWMERELAKE:359:261:510:348", "FELSTONEFIELD:149:119:306:315", "GAHRRONSWITHERING:165:191:527:257", "HEARTHGLEN:331:280:312:20", "NORTHRIDGELUMBERCAMP:205:168:390:170", "RUINSOFANDORHOL:275:219:265:360", "SORROWHILL:290:201:360:467", "THEBULWARK:216:172:143:300", "THEWEEPINGCAVE:148:192:572:203", "THEWRITHINGHAUNT:159:177:457:329", "THONDRORILRIVER:193:326:597:94",},
["Westfall"] = {"ALEXSTONFARMSTEAD:275:182:220:274", "DEMONTSPLACE:166:155:226:391", "FURLBROWSPUMPKINFARM:186:191:399:24", "GOLDCOASTQUARRY:194:241:234:113", "JANGOLODEMINE:185:187:323:44", "MOONBROOK:208:174:317:349:919:0", "SALDEANSFARM:191:184:478:119", "SENTINELHILL:161:210:461:259", "THEDAGGERHILLS:235:149:349:430", "THEDEADACRE:164:213:542:267", "THEDUSTPLAINS:260:204:538:393", "THEJANSENSTEAD:143:183:498:6", "THEMOLSENFARM:199:180:342:161", "WESTFALLLIGHTHOUSE:306:161:171:482",},
["Wetlands"] = {"ANGERFANGENCAMPMENT:196:150:364:241", "BLACKCHANNELMARSH:213:173:95:260", "BLUEGILLMARSH:195:165:111:157", "DIREFORGEHILL:233:225:515:128", "DUNMODR:173:147:419:42", "DUNAGRATH:222:190:97:335", "GRIMBATOL:317:331:623:242:1037:0", "HAWKSVIGIL:170:162:264:353", "IRONBEARDSTOMB:165:156:368:135", "MENETHILHARBOR:156:116:27:321", "MOSSHIDEFEN:215:224:539:277", "RAPTORRIDGE:159:115:640:191", "SALTSPRAYGLEN:170:214:254:58", "SUNDOWNMARSH:265:212:113:100", "THEGREENBELT:153:209:473:143", "THELGANROCK:207:165:478:383:836:0", "WHELGARSEXCAVATIONSITE:172:156:264:224",},
["Winterspring"] = {"DARKWHISPERGORGE:247:191:452:447", "EVERLOOK:151:189:516:114", "FROSTFIREHOTSPRINGS:226:124:231:180", "FROSTSABERROCK:240:172:375:11", "FROSTWHISPERGORGE:184:144:531:383", "ICETHISTLEHILLS:116:155:617:247", "LAKEKELTHERIL:203:176:409:205", "MAZTHORIL:174:169:499:265", "OWLWINGTHICKET:149:127:600:348", "STARFALLVILLAGE:174:148:399:143", "THEHIDDENGROVE:162:174:562:33", "TIMBERMAWPOST:223:112:235:248", "WINTERFALLVILLAGE:133:120:622:163",},
}
+2 -3
View File
@@ -138,7 +138,6 @@ pfUI_translation["deDE"] = {
["Combat Timer"] = nil, ["Combat Timer"] = nil,
["Combopoint Height"] = nil, ["Combopoint Height"] = nil,
["Combopoint Width"] = nil, ["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil, ["Components"] = nil,
["Config UI Settings"] = nil, ["Config UI Settings"] = nil,
["Configuration"] = nil, ["Configuration"] = nil,
@@ -620,8 +619,8 @@ pfUI_translation["deDE"] = {
["Random Roll Announcement Rarity"] = nil, ["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil, ["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -783,6 +782,7 @@ pfUI_translation["deDE"] = {
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = nil, ["Target-Target"] = nil,
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -877,7 +877,6 @@ pfUI_translation["deDE"] = {
["XP Percentage"] = nil, ["XP Percentage"] = nil,
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil, ["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil, ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = nil, ["Your items have been repaired for"] = nil,
+18 -3
View File
@@ -19,6 +19,7 @@ pfUI_translation["enUS"] = {
["Align Chat Windows"] = nil, ["Align Chat Windows"] = nil,
["Aligned Position"] = nil, ["Aligned Position"] = nil,
["All messages will be forwarded to:"] = nil, ["All messages will be forwarded to:"] = nil,
["All players passed"] = nil,
["Alt-Click Action"] = nil, ["Alt-Click Action"] = nil,
["Always Allow Drag Via Shift Key"] = nil, ["Always Allow Drag Via Shift Key"] = nil,
["Always Show"] = nil, ["Always Show"] = nil,
@@ -119,6 +120,7 @@ pfUI_translation["enUS"] = {
["Chat Bubble Transparency"] = nil, ["Chat Bubble Transparency"] = nil,
["Chat Default Brackets"] = nil, ["Chat Default Brackets"] = nil,
["Class"] = nil, ["Class"] = nil,
["Clear"] = nil,
["Clear Rolls"] = nil, ["Clear Rolls"] = nil,
["Click Action"] = nil, ["Click Action"] = nil,
["Click Casting"] = nil, ["Click Casting"] = nil,
@@ -138,7 +140,6 @@ pfUI_translation["enUS"] = {
["Combat Timer"] = nil, ["Combat Timer"] = nil,
["Combopoint Height"] = nil, ["Combopoint Height"] = nil,
["Combopoint Width"] = nil, ["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil, ["Components"] = nil,
["Config UI Settings"] = nil, ["Config UI Settings"] = nil,
["Configuration"] = nil, ["Configuration"] = nil,
@@ -195,6 +196,7 @@ pfUI_translation["enUS"] = {
["Deficit"] = nil, ["Deficit"] = nil,
["Delete profile"] = nil, ["Delete profile"] = nil,
["Delete / Reset"] = nil, ["Delete / Reset"] = nil,
["Dependencies"] = nil,
["Descending"] = nil, ["Descending"] = nil,
["Description Font"] = nil, ["Description Font"] = nil,
["Description Font Size"] = nil, ["Description Font Size"] = nil,
@@ -409,6 +411,7 @@ pfUI_translation["enUS"] = {
["Highlight Equipped Items"] = nil, ["Highlight Equipped Items"] = nil,
["Highlight Not Usable Spells"] = nil, ["Highlight Not Usable Spells"] = nil,
["Highlight Out Of Mana Spells"] = nil, ["Highlight Out Of Mana Spells"] = nil,
["Highlight New Items"] = nil,
["Highlight Out Of Range Spells"] = nil, ["Highlight Out Of Range Spells"] = nil,
["Highlight Settings That Require Reload"] = nil, ["Highlight Settings That Require Reload"] = nil,
["Highlight Unusable Items"] = nil, ["Highlight Unusable Items"] = nil,
@@ -477,6 +480,7 @@ pfUI_translation["enUS"] = {
["Look & Feel"] = nil, ["Look & Feel"] = nil,
["Loot"] = nil, ["Loot"] = nil,
["Loot & Spam"] = nil, ["Loot & Spam"] = nil,
["Loot History"] = nil,
["Macro Text Color"] = nil, ["Macro Text Color"] = nil,
["Macro Text Size"] = nil, ["Macro Text Size"] = nil,
["Main Actionbar"] = nil, ["Main Actionbar"] = nil,
@@ -499,6 +503,7 @@ pfUI_translation["enUS"] = {
["Menu Font Size"] = nil, ["Menu Font Size"] = nil,
["Messages are no longer forwarded to:"] = nil, ["Messages are no longer forwarded to:"] = nil,
["Middle Mouse Button"] = nil, ["Middle Mouse Button"] = nil,
["Missing"] = nil,
["Minimap"] = nil, ["Minimap"] = nil,
["Minimap Panel"] = nil, ["Minimap Panel"] = nil,
["Minimap Size (|cffffaaaaExperimental|r)"] = nil, ["Minimap Size (|cffffaaaaExperimental|r)"] = nil,
@@ -525,6 +530,7 @@ pfUI_translation["enUS"] = {
["Network Latency"] = nil, ["Network Latency"] = nil,
["Network Up"] = nil, ["Network Up"] = nil,
["New entry:"] = nil, ["New entry:"] = nil,
["New Item Color"] = nil,
["NEW TIMER"] = nil, ["NEW TIMER"] = nil,
["Next"] = nil, ["Next"] = nil,
["Next Memory Cleanup"] = nil, ["Next Memory Cleanup"] = nil,
@@ -552,6 +558,7 @@ pfUI_translation["enUS"] = {
["Only Show Own Debuffs (|cffffaaaaExperimental|r)"] = nil, ["Only Show Own Debuffs (|cffffaaaaExperimental|r)"] = nil,
["Only Show Target Castbar"] = nil, ["Only Show Target Castbar"] = nil,
["On State Change"] = nil, ["On State Change"] = nil,
["Optional Dependencies"] = nil,
["Options"] = nil, ["Options"] = nil,
["Orientation"] = nil, ["Orientation"] = nil,
["Other Panel: Minimap"] = nil, ["Other Panel: Minimap"] = nil,
@@ -620,8 +627,8 @@ pfUI_translation["enUS"] = {
["Random Roll Announcement Rarity"] = nil, ["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil, ["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -645,6 +652,7 @@ pfUI_translation["enUS"] = {
["Resting"] = nil, ["Resting"] = nil,
["Reveal Unexplored Areas"] = nil, ["Reveal Unexplored Areas"] = nil,
["Switch to current zone"] = nil, ["Switch to current zone"] = nil,
["Retrieving item information..."] = nil,
["Right"] = nil, ["Right"] = nil,
["Right Actionbar"] = nil, ["Right Actionbar"] = nil,
["Right Anchor"] = nil, ["Right Anchor"] = nil,
@@ -750,6 +758,9 @@ pfUI_translation["enUS"] = {
["Show Totems Indicators"] = nil, ["Show Totems Indicators"] = nil,
["Shrink & Return"] = nil, ["Shrink & Return"] = nil,
["Size"] = nil, ["Size"] = nil,
["Spell Name Alignment"] = nil,
["Spell Name X Offset"] = nil,
["Spell Name Y Offset"] = nil,
["Skin"] = nil, ["Skin"] = nil,
["Skins"] = nil, ["Skins"] = nil,
["Slow"] = nil, ["Slow"] = nil,
@@ -777,12 +788,14 @@ pfUI_translation["enUS"] = {
["Switch Pages On Alt Key Press"] = nil, ["Switch Pages On Alt Key Press"] = nil,
["Switch Pages On Ctrl Key Press"] = nil, ["Switch Pages On Ctrl Key Press"] = nil,
["Switch Pages On Druid Stealth"] = nil, ["Switch Pages On Druid Stealth"] = nil,
["Switch Pages On Priest Shadowform"] = nil,
["Switch Pages On Shift Key Press"] = nil, ["Switch Pages On Shift Key Press"] = nil,
["Systeminfo"] = nil, ["Systeminfo"] = nil,
["Target"] = nil, ["Target"] = nil,
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = nil, ["Target-Target"] = nil,
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -798,6 +811,9 @@ pfUI_translation["enUS"] = {
["Threshold To Trust Health Estimation"] = nil, ["Threshold To Trust Health Estimation"] = nil,
["Time"] = nil, ["Time"] = nil,
["Timer"] = nil, ["Timer"] = nil,
["Timer Alignment"] = nil,
["Timer X Offset"] = nil,
["Timer Y Offset"] = nil,
["Time Remaining"] = nil, ["Time Remaining"] = nil,
["Timer In Minutes"] = nil, ["Timer In Minutes"] = nil,
["Timestamp Brackets"] = nil, ["Timestamp Brackets"] = nil,
@@ -877,7 +893,6 @@ pfUI_translation["enUS"] = {
["XP Percentage"] = nil, ["XP Percentage"] = nil,
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil, ["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil, ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = nil, ["Your items have been repaired for"] = nil,
+2 -3
View File
@@ -138,7 +138,6 @@ pfUI_translation["esES"] = {
["Combat Timer"] = "Temporizador de combate", ["Combat Timer"] = "Temporizador de combate",
["Combopoint Height"] = nil, ["Combopoint Height"] = nil,
["Combopoint Width"] = nil, ["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Comparar estadísticas bases de objetos",
["Components"] = "Componentes", ["Components"] = "Componentes",
["Config UI Settings"] = nil, ["Config UI Settings"] = nil,
["Configuration"] = "Configuración", ["Configuration"] = "Configuración",
@@ -620,8 +619,8 @@ pfUI_translation["esES"] = {
["Random Roll Announcement Rarity"] = "Anuncio para tirar los dados aleatoriamente", ["Random Roll Announcement Rarity"] = "Anuncio para tirar los dados aleatoriamente",
["Random Rolling"] = "Tirar los dados aleatoriamente", ["Random Rolling"] = "Tirar los dados aleatoriamente",
["Range Based Hunter Paging"] = "Paginado de alcance para cazadores", ["Range Based Hunter Paging"] = "Paginado de alcance para cazadores",
["Range Check Interval"] = "Intervalo de comprobación de alcance",
["Rank"] = "Rango", ["Rank"] = "Rango",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Borde rojo en unidades enemigas", ["Red Border On Enemy Units"] = "Borde rojo en unidades enemigas",
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -783,6 +782,7 @@ pfUI_translation["esES"] = {
["Target Castbar"] = "Barra de lanzamiento del objetivo", ["Target Castbar"] = "Barra de lanzamiento del objetivo",
["Target Debuff Bar"] = "Barra de perjuicios del objetivo", ["Target Debuff Bar"] = "Barra de perjuicios del objetivo",
["Target Nameplate Zoom Factor"] = "Factor de zoom de la placa de nombre del objetivo", ["Target Nameplate Zoom Factor"] = "Factor de zoom de la placa de nombre del objetivo",
["Target Totem"] = nil,
["Target-Target"] = "Objetivo-Objetivo", ["Target-Target"] = "Objetivo-Objetivo",
["Target-Target-Target"] = "Objetivo-Objetivo-Objetivo", ["Target-Target-Target"] = "Objetivo-Objetivo-Objetivo",
["Text"] = nil, ["Text"] = nil,
@@ -877,7 +877,6 @@ pfUI_translation["esES"] = {
["XP Percentage"] = "Porcentaje de exp.", ["XP Percentage"] = "Porcentaje de exp.",
["Yellow Border On Neutral Units"] = "Borde amarillo en unidades neutrales", ["Yellow Border On Neutral Units"] = "Borde amarillo en unidades neutrales",
["Yes"] = "Sí", ["Yes"] = "Sí",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Obtienes", ["You got"] = "Obtienes",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Ahora su interfaz está configurada.\n\nPara la configuración avanzada, abra la configuración |cff33ffccpf|rUI por el menú de escape o escriba \"|cffffffaa/pfui|r\" en el chat.", ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Ahora su interfaz está configurada.\n\nPara la configuración avanzada, abra la configuración |cff33ffccpf|rUI por el menú de escape o escriba \"|cffffffaa/pfui|r\" en el chat.",
["Your items have been repaired for"] = "Tus objetos se han reparado por", ["Your items have been repaired for"] = "Tus objetos se han reparado por",
+2 -3
View File
@@ -138,7 +138,6 @@ pfUI_translation["frFR"] = {
["Combat Timer"] = "Chronomètre de combat", ["Combat Timer"] = "Chronomètre de combat",
["Combopoint Height"] = nil, ["Combopoint Height"] = nil,
["Combopoint Width"] = nil, ["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Compare les stats de base d'un objet",
["Components"] = "Composants", ["Components"] = "Composants",
["Config UI Settings"] = nil, ["Config UI Settings"] = nil,
["Configuration"] = "Configuration", ["Configuration"] = "Configuration",
@@ -620,8 +619,8 @@ pfUI_translation["frFR"] = {
["Random Roll Announcement Rarity"] = "Rareté des annonces des jets de dés aléatoires", ["Random Roll Announcement Rarity"] = "Rareté des annonces des jets de dés aléatoires",
["Random Rolling"] = "Lancer de dés aléatoires", ["Random Rolling"] = "Lancer de dés aléatoires",
["Range Based Hunter Paging"] = "Pagination de distance basée sur le chasseur", ["Range Based Hunter Paging"] = "Pagination de distance basée sur le chasseur",
["Range Check Interval"] = "Intervalle de vérification de la distance",
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Bordure rouge sur les unités ennemies", ["Red Border On Enemy Units"] = "Bordure rouge sur les unités ennemies",
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -783,6 +782,7 @@ pfUI_translation["frFR"] = {
["Target Castbar"] = "Barre d'incantation de la cible", ["Target Castbar"] = "Barre d'incantation de la cible",
["Target Debuff Bar"] = "Barre des affaiblissements de la cible", ["Target Debuff Bar"] = "Barre des affaiblissements de la cible",
["Target Nameplate Zoom Factor"] = "Facteur de zoom du Nameplate de la cible", ["Target Nameplate Zoom Factor"] = "Facteur de zoom du Nameplate de la cible",
["Target Totem"] = nil,
["Target-Target"] = "Cible de la cible", ["Target-Target"] = "Cible de la cible",
["Target-Target-Target"] = "Cible de la cible de la cible", ["Target-Target-Target"] = "Cible de la cible de la cible",
["Text"] = nil, ["Text"] = nil,
@@ -877,7 +877,6 @@ pfUI_translation["frFR"] = {
["XP Percentage"] = "Pourcentage de la barre d'expérience", ["XP Percentage"] = "Pourcentage de la barre d'expérience",
["Yellow Border On Neutral Units"] = "Bordure jaune sur les unités neutres", ["Yellow Border On Neutral Units"] = "Bordure jaune sur les unités neutres",
["Yes"] = "Oui", ["Yes"] = "Oui",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Vous avez", ["You got"] = "Vous avez",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil, ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = "Vos objets ont été réparés pour", ["Your items have been repaired for"] = "Vos objets ont été réparés pour",
+2 -3
View File
@@ -138,7 +138,6 @@ pfUI_translation["koKR"] = {
["Combat Timer"] = "전투 타이머", ["Combat Timer"] = "전투 타이머",
["Combopoint Height"] = nil, ["Combopoint Height"] = nil,
["Combopoint Width"] = nil, ["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil, ["Components"] = nil,
["Config UI Settings"] = nil, ["Config UI Settings"] = nil,
["Configuration"] = "구성", ["Configuration"] = "구성",
@@ -620,8 +619,8 @@ pfUI_translation["koKR"] = {
["Random Roll Announcement Rarity"] = nil, ["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil, ["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -783,6 +782,7 @@ pfUI_translation["koKR"] = {
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = "대상-대상", ["Target-Target"] = "대상-대상",
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -877,7 +877,6 @@ pfUI_translation["koKR"] = {
["XP Percentage"] = "경험치 퍼센트", ["XP Percentage"] = "경험치 퍼센트",
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil, ["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil, ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = nil, ["Your items have been repaired for"] = nil,
+2 -3
View File
@@ -138,7 +138,6 @@ pfUI_translation["ruRU"] = {
["Combat Timer"] = "Таймер боя", ["Combat Timer"] = "Таймер боя",
["Combopoint Height"] = nil, ["Combopoint Height"] = nil,
["Combopoint Width"] = nil, ["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Сравнивать базовые характеристики предмета",
["Components"] = "Компоненты", ["Components"] = "Компоненты",
["Config UI Settings"] = "Настройка параметров пользовательского интерфейса", ["Config UI Settings"] = "Настройка параметров пользовательского интерфейса",
["Configuration"] = "Конфигурация", ["Configuration"] = "Конфигурация",
@@ -620,8 +619,8 @@ pfUI_translation["ruRU"] = {
["Random Roll Announcement Rarity"] = "Оповещение случайного броска для качества предмета", ["Random Roll Announcement Rarity"] = "Оповещение случайного броска для качества предмета",
["Random Rolling"] = "Случайный бросок костей для", ["Random Rolling"] = "Случайный бросок костей для",
["Range Based Hunter Paging"] = "[|cffA9D271Охотник|r] Переключение страниц на основе диапазона", ["Range Based Hunter Paging"] = "[|cffA9D271Охотник|r] Переключение страниц на основе диапазона",
["Range Check Interval"] = "Интервал проверки диапазона",
["Rank"] = "Ранг", ["Rank"] = "Ранг",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Красные границы вражеских юнитов", ["Red Border On Enemy Units"] = "Красные границы вражеских юнитов",
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = "Региональные настройки", ["Regional Settings"] = "Региональные настройки",
@@ -783,6 +782,7 @@ pfUI_translation["ruRU"] = {
["Target Castbar"] = "Панель применения цели", ["Target Castbar"] = "Панель применения цели",
["Target Debuff Bar"] = "Панель дебаффов цели", ["Target Debuff Bar"] = "Панель дебаффов цели",
["Target Nameplate Zoom Factor"] = "Коэффициент увеличения индикатора здоровья цели", ["Target Nameplate Zoom Factor"] = "Коэффициент увеличения индикатора здоровья цели",
["Target Totem"] = nil,
["Target-Target"] = "Цель цели", ["Target-Target"] = "Цель цели",
["Target-Target-Target"] = "Цель цели цели", ["Target-Target-Target"] = "Цель цели цели",
["Text"] = nil, ["Text"] = nil,
@@ -877,7 +877,6 @@ pfUI_translation["ruRU"] = {
["XP Percentage"] = "Процент опыта", ["XP Percentage"] = "Процент опыта",
["Yellow Border On Neutral Units"] = "Желтые границы на нейтральных юнитах", ["Yellow Border On Neutral Units"] = "Желтые границы на нейтральных юнитах",
["Yes"] = "Да", ["Yes"] = "Да",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Вы получили", ["You got"] = "Вы получили",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Теперь ваш интерфейс настроен.\n\nДля расширенной настройки откройте \"Настройки |cff33ffccpf|rUI\" с помощью escape меню или введите \"|cffffffaa/pfui|r\" в чат.\n\nЖелаю хорошего путешествия!\n\n|cffaaaaaa- Shagu", ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Теперь ваш интерфейс настроен.\n\nДля расширенной настройки откройте \"Настройки |cff33ffccpf|rUI\" с помощью escape меню или введите \"|cffffffaa/pfui|r\" в чат.\n\nЖелаю хорошего путешествия!\n\n|cffaaaaaa- Shagu",
["Your items have been repaired for"] = "Ваши предметы были отремонтированы за", ["Your items have been repaired for"] = "Ваши предметы были отремонтированы за",
+2 -3
View File
@@ -138,7 +138,6 @@ pfUI_translation["zhCN"] = {
["Combat Timer"] = "战斗计时器", ["Combat Timer"] = "战斗计时器",
["Combopoint Height"] = "连击点高度", ["Combopoint Height"] = "连击点高度",
["Combopoint Width"] = "连击点宽度", ["Combopoint Width"] = "连击点宽度",
["Compare Item Base Stats"] = "基于属性的装备对比",
["Components"] = "组件", ["Components"] = "组件",
["Config UI Settings"] = "界面设置", ["Config UI Settings"] = "界面设置",
["Configuration"] = "配置", ["Configuration"] = "配置",
@@ -620,8 +619,8 @@ pfUI_translation["zhCN"] = {
["Random Roll Announcement Rarity"] = "随机Roll点稀有度", ["Random Roll Announcement Rarity"] = "随机Roll点稀有度",
["Random Rolling"] = "随机Roll点 物品:", ["Random Rolling"] = "随机Roll点 物品:",
["Range Based Hunter Paging"] = "启用基于范围的自动分页[|cff7fff7f猎人|r]", ["Range Based Hunter Paging"] = "启用基于范围的自动分页[|cff7fff7f猎人|r]",
["Range Check Interval"] = "范围检查间隔",
["Rank"] = "军衔", ["Rank"] = "军衔",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "显示敌方单位红色边框", ["Red Border On Enemy Units"] = "显示敌方单位红色边框",
["Red Name Text On Infight Units"] = "进战斗的单位显示红色姓名", ["Red Name Text On Infight Units"] = "进战斗的单位显示红色姓名",
["Regional Settings"] = "区域设置", ["Regional Settings"] = "区域设置",
@@ -784,6 +783,7 @@ pfUI_translation["zhCN"] = {
["Target Castbar"] = "目标施法条", ["Target Castbar"] = "目标施法条",
["Target Debuff Bar"] = "目标Debuffs条", ["Target Debuff Bar"] = "目标Debuffs条",
["Target Nameplate Zoom Factor"] = "目标姓名板缩放系数", ["Target Nameplate Zoom Factor"] = "目标姓名板缩放系数",
["Target Totem"] = nil,
["Target-Target"] = "目标的目标", ["Target-Target"] = "目标的目标",
["Target-Target-Target"] = "目标的目标的目标", ["Target-Target-Target"] = "目标的目标的目标",
["Text"] = "文本", ["Text"] = "文本",
@@ -878,7 +878,6 @@ pfUI_translation["zhCN"] = {
["XP Percentage"] = "经验百分比", ["XP Percentage"] = "经验百分比",
["Yellow Border On Neutral Units"] = "显示中立单位黄色边框", ["Yellow Border On Neutral Units"] = "显示中立单位黄色边框",
["Yes"] = "是", ["Yes"] = "是",
["You gain (.+) Mana from Totemic Recall"] = "你从图腾召回获得了(.+)点法力值",
["You got"] = "你已得到", ["You got"] = "你已得到",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "你的界面现在已经完成设置.高级设置请点击游戏菜单或者输入命令/pfui进行设置.祝您游戏愉快", ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "你的界面现在已经完成设置.高级设置请点击游戏菜单或者输入命令/pfui进行设置.祝您游戏愉快",
["Your items have been repaired for"] = "你的物品已经修好了", ["Your items have been repaired for"] = "你的物品已经修好了",
+2 -3
View File
@@ -138,7 +138,6 @@ pfUI_translation["zhTW"] = {
["Combat Timer"] = "戰鬥計時器", ["Combat Timer"] = "戰鬥計時器",
["Combopoint Height"] = nil, ["Combopoint Height"] = nil,
["Combopoint Width"] = nil, ["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "基於屬性的裝備對比",
["Components"] = "組件", ["Components"] = "組件",
["Config UI Settings"] = nil, ["Config UI Settings"] = nil,
["Configuration"] = "配置", ["Configuration"] = "配置",
@@ -620,8 +619,8 @@ pfUI_translation["zhTW"] = {
["Random Roll Announcement Rarity"] = "隨機Roll點公示稀有度", ["Random Roll Announcement Rarity"] = "隨機Roll點公示稀有度",
["Random Rolling"] = "隨機Roll點 物品:", ["Random Rolling"] = "隨機Roll點 物品:",
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = "範圍檢查間隔",
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -783,6 +782,7 @@ pfUI_translation["zhTW"] = {
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = "目標的目標", ["Target-Target"] = "目標的目標",
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -877,7 +877,6 @@ pfUI_translation["zhTW"] = {
["XP Percentage"] = "經驗百分比", ["XP Percentage"] = "經驗百分比",
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil, ["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "您的UI已經設置完畢.使用遊戲菜單或在聊天窗口輸入\"|cffffffaa/pfui|r\"開啟高級設置.祝您遊戲愉快!\n\n|cffaaaaaa- Shagu", ["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "您的UI已經設置完畢.使用遊戲菜單或在聊天窗口輸入\"|cffffffaa/pfui|r\"開啟高級設置.祝您遊戲愉快!\n\n|cffaaaaaa- Shagu",
["Your items have been repaired for"] = "你的物品已經修好了", ["Your items have been repaired for"] = "你的物品已經修好了",
-2
View File
@@ -4,9 +4,7 @@
<Include file="..\libs\libdebuff.lua"/> <Include file="..\libs\libdebuff.lua"/>
<Include file="..\libs\librange.lua"/> <Include file="..\libs\librange.lua"/>
<Include file="..\libs\libunitscan.lua"/> <Include file="..\libs\libunitscan.lua"/>
<Include file="..\libs\libtooltip.lua"/>
<Include file="..\libs\libhealth.lua"/> <Include file="..\libs\libhealth.lua"/>
<Include file="..\libs\libtotem.lua"/>
<Include file="..\libs\libthrottle.lua"/> <Include file="..\libs\libthrottle.lua"/>
<Include file="..\libs\libpredict.lua"/> <Include file="..\libs\libpredict.lua"/>
<Include file="..\libs\libbagsort.lua"/> <Include file="..\libs\libbagsort.lua"/>
+2
View File
@@ -78,4 +78,6 @@
<Include file="..\modules\unitxp.lua"/> <Include file="..\modules\unitxp.lua"/>
<Include file="..\modules\bgscore.lua"/> <Include file="..\modules\bgscore.lua"/>
<Include file="..\modules\equipmentmanager.lua"/> <Include file="..\modules\equipmentmanager.lua"/>
<Include file="..\modules\loothistory.lua"/>
<Include file="..\modules\newitem.lua"/>
</Ui> </Ui>
+1
View File
@@ -1,5 +1,6 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/"> <Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\skins\blizzard\character.lua"/> <Include file="..\skins\blizzard\character.lua"/>
<Include file="..\skins\blizzard\inspect.lua"/>
<Include file="..\skins\blizzard\spellbook.lua"/> <Include file="..\skins\blizzard\spellbook.lua"/>
<Include file="..\skins\blizzard\friends.lua"/> <Include file="..\skins\blizzard\friends.lua"/>
<Include file="..\skins\blizzard\talents.lua"/> <Include file="..\skins\blizzard\talents.lua"/>
+85 -59
View File
@@ -11,26 +11,27 @@ pfUI.api.libbagsort = libbagsort
libbagsort.itemGrid = {} libbagsort.itemGrid = {}
libbagsort.bagList = nil libbagsort.bagList = nil
local HEARTHSTONE_ITEM_ID = 6948 local ItemClass = Enum.ItemClass
local ItemQuality = Enum.ItemQuality
-- Lower prefix = sorted earlier in the bag. -- Lower prefix = sorted earlier in the bag.
local function SortCategoryPrefix(itemId, itemType, itemSubType, quality) local function SortCategoryPrefix(itemId, classID, quality)
if itemId == HEARTHSTONE_ITEM_ID then return "00" end if itemId == HEARTHSTONE_ITEM_ID then return "00" end
if quality == 0 then return "13" end -- Poor (gray) always last if quality == ItemQuality.Poor then return "13" end -- gray always last
if itemType == "Weapon" or itemType == "Armor" then if classID == ItemClass.Weapon or classID == ItemClass.Armor then
if quality and quality >= 4 then return "01" end -- Epic+ gear if quality and quality >= ItemQuality.Epic then return "01" end -- Epic+ gear
if quality == 3 then return "02" end -- Rare gear if quality == ItemQuality.Rare then return "02" end -- Rare gear
if quality == 2 then return "03" end -- Uncommon gear if quality == ItemQuality.Uncommon then return "03" end -- Uncommon gear
return "04" -- Common/poor gear return "04" -- Common/poor gear
end end
if itemType == "Consumable" then return "05" end if classID == ItemClass.Consumable then return "05" end
if itemType == "Reagent" then return "06" end if classID == ItemClass.Reagent then return "06" end
if itemType == "Trade Goods" then return "07" end if classID == ItemClass.Tradegoods then return "07" end
if itemType == "Quest" then return "08" end if classID == ItemClass.Questitem then return "08" end
-- Non-gear items without a specific type, sorted by quality -- Non-gear items without a specific type, sorted by quality
if quality and quality >= 4 then return "09" end if quality and quality >= ItemQuality.Epic then return "09" end
if quality == 3 then return "10" end if quality == ItemQuality.Rare then return "10" end
if quality == 2 then return "11" end if quality == ItemQuality.Uncommon then return "11" end
return "12" return "12"
end end
@@ -41,9 +42,12 @@ local function SortCountSuffix(count)
return string.sub(s, -6) return string.sub(s, -6)
end end
local function SortKey(itemId, name, itype, subtype, quality, count) local function SortKey(itemId, name, classID, subClassID, quality, count)
return SortCategoryPrefix(itemId, itype, subtype, quality) -- Zero-pad the class/subclass so the secondary grouping sorts numerically
.. (itype or "") .. "|" .. (subtype or "") .. "|" .. (name or "zzz") .. "|" .. SortCountSuffix(count) -- (as a string, "10" would otherwise precede "2").
return SortCategoryPrefix(itemId, classID, quality)
.. string.format("%02d|%02d|", classID or 99, subClassID or 99)
.. (name or "zzz") .. "|" .. SortCountSuffix(count)
end end
local function ClearSortData() local function ClearSortData()
@@ -112,36 +116,61 @@ local function BuildConsolidateOps(bagList)
return ops return ops
end end
-- Bag family bitmask (1 << (familyID-1)); 0 = general-purpose (holds
-- anything). Backpack (0) and bank (-1) are always general. A specialty
-- bag's family comes from the equipped bag item -- ClassicAPI derives it
-- from the container subclass when the raw field is empty (Turtle leaves
-- bags' m_bagFamily at 0), so quivers/soul/profession bags report properly.
local function BagFamily(bag)
if bag == 0 or bag == -1 then return 0 end
local id = GetInventoryItemID("player", ContainerIDToInventoryID(bag))
return id and C_Item.GetItemFamily(id) or 0
end
local function BuildSortGrid() local function BuildSortGrid()
local bagList = libbagsort.bagList local bagList = libbagsort.bagList
libbagsort.itemGrid = {} libbagsort.itemGrid = {}
local normalItems = {} local normalItems = {}
local poorItems = {} local poorItems = {}
local bagCount = 0
local bagSlots = {} -- Destination cells, split by the family they can accept. A specialty
-- bag's slots only take items of its own family; general slots take
-- anything. Cells are collected in forward order (bag order, slot 1..n).
local generalCells = {} -- { {bag=,slot=}, ... }
local specialtyCells = {} -- family -> { {bag=,slot=}, ... }
for _, bag in ipairs(bagList) do for _, bag in ipairs(bagList) do
bagCount = bagCount + 1 local fam = BagFamily(bag)
local numSlots = GetContainerNumSlots(bag) local numSlots = GetContainerNumSlots(bag)
bagSlots[bagCount] = numSlots
if numSlots > 0 then if numSlots > 0 then
libbagsort.itemGrid[bag] = {} libbagsort.itemGrid[bag] = {}
for slot = 1, numSlots do for slot = 1, numSlots do
if fam == 0 then
tinsert(generalCells, {bag=bag, slot=slot})
else
specialtyCells[fam] = specialtyCells[fam] or {}
tinsert(specialtyCells[fam], {bag=bag, slot=slot})
end
local itemId = C_Container.GetContainerItemID(bag, slot) local itemId = C_Container.GetContainerItemID(bag, slot)
if itemId then if itemId then
-- pfUI's compat layer shims GetItemInfo to the modern 10-field -- C_Item.GetItemInfo is the full 18-field tuple; classID/subClassID
-- signature (inserts nil for itemLevel between quality and -- sit at positions 12/13. We categorize on those numeric class IDs
-- minlevel) — so itype/subtype sit at positions 6/7, not 5/6. -- rather than the localized itemType/itemSubType strings. (pfUI's
local name, _, quality, _, _, itype, subtype = GetItemInfo(itemId) -- shimmed global GetItemInfo is only 10 fields and lacks them.)
local name, _, quality, _, _, _, _, _, _, _, _, classID, subClassID = C_Item.GetItemInfo(itemId)
local _, count = GetContainerItemInfo(bag, slot) local _, count = GetContainerItemInfo(bag, slot)
local item = { local item = {
key = SortKey(itemId, name, itype, subtype, quality, count), key = SortKey(itemId, name, classID, subClassID, quality, count),
-- vanilla items carry at most one family bit, so equality
-- against a bag family suffices (no bit.band needed).
family = C_Item.GetItemFamily(itemId) or 0,
srcBag = bag, srcBag = bag,
srcSlot = slot, srcSlot = slot,
curBag = bag, curBag = bag,
curSlot = slot, curSlot = slot,
} }
if quality == 0 then if quality == ItemQuality.Poor then
tinsert(poorItems, item) tinsert(poorItems, item)
else else
tinsert(normalItems, item) tinsert(normalItems, item)
@@ -157,41 +186,38 @@ local function BuildSortGrid()
-- back-to-front (last poor item lands on the last slot). -- back-to-front (last poor item lands on the last slot).
table.sort(poorItems, function(a, b) return a.key > b.key end) table.sort(poorItems, function(a, b) return a.key > b.key end)
-- Forward pass: assign normal items from slot 1 of bag 1 onward -- Forward pass: route each normal item into the next free cell that
local bagIdx, destSlot = 1, 1 -- accepts it -- a matching specialty bag first, overflowing to general.
while bagIdx <= bagCount and bagSlots[bagIdx] == 0 do local genIdx = 1
bagIdx = bagIdx + 1 local specIdx = {} -- family -> next free index into specialtyCells[family]
end
for _, item in ipairs(normalItems) do for _, item in ipairs(normalItems) do
while bagIdx <= bagCount do local cell
if destSlot <= bagSlots[bagIdx] then break end local fam = item.family
bagIdx = bagIdx + 1 if fam ~= 0 and specialtyCells[fam] then
destSlot = 1 local i = specIdx[fam] or 1
if i <= table.getn(specialtyCells[fam]) then
cell = specialtyCells[fam][i]
specIdx[fam] = i + 1
end
end end
if bagIdx > bagCount then break end if not cell and genIdx <= table.getn(generalCells) then
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot] cell = generalCells[genIdx]
grid.destBag = bagList[bagIdx] genIdx = genIdx + 1
grid.destSlot = destSlot end
destSlot = destSlot + 1 if not cell then break end
item.destBag = cell.bag
item.destSlot = cell.slot
end end
-- Reverse pass: assign poor items from the last slot of the last bag backward -- Reverse pass: poor items are general; fill remaining general cells from
local rBagIdx = bagCount -- the back, stopping before the ones the forward pass already claimed.
local rDestSlot = 0 local genBack = table.getn(generalCells)
while rBagIdx >= 1 do
if bagSlots[rBagIdx] > 0 then rDestSlot = bagSlots[rBagIdx]; break end
rBagIdx = rBagIdx - 1
end
for _, item in ipairs(poorItems) do for _, item in ipairs(poorItems) do
while rBagIdx >= 1 and rDestSlot < 1 do if genBack < genIdx then break end
rBagIdx = rBagIdx - 1 local cell = generalCells[genBack]
rDestSlot = rBagIdx >= 1 and bagSlots[rBagIdx] or 0 genBack = genBack - 1
end item.destBag = cell.bag
if rBagIdx < 1 then break end item.destSlot = cell.slot
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot]
grid.destBag = bagList[rBagIdx]
grid.destSlot = rDestSlot
rDestSlot = rDestSlot - 1
end end
end end
+34 -163
View File
@@ -14,9 +14,9 @@ setfenv(1, pfUI:GetEnvironment())
-- The public per-aura readers (UnitDebuff, UnitOwnDebuff) were retired in favor -- The public per-aura readers (UnitDebuff, UnitOwnDebuff) were retired in favor
-- of ClassicAPI's C_UnitAuras (which now provides sourceUnit/sourceGUID and -- of ClassicAPI's C_UnitAuras (which now provides sourceUnit/sourceGUID and
-- non-player expirationTime). What remains in libdebuff is the cast-event -- non-player expirationTime). What remains in libdebuff is the cast-event
-- bookkeeping consumed by GetBestAuraCast / GetEnhancedDebuffs and the -- bookkeeping consumed by GetBestAuraCast (libpredict HoT tracking) and the
-- libdebuff_*_hooks broadcast surface (subscribers in actionbar / swingtimer -- libdebuff_*_hooks broadcast surface (subscribers in actionbar / swingtimer
-- / libtotem react to SPELL_GO and SPELL_FAILED). -- react to SPELL_GO and SPELL_FAILED).
-- return instantly when another libdebuff is already active -- return instantly when another libdebuff is already active
if pfUI.api.libdebuff then return end if pfUI.api.libdebuff then return end
@@ -43,16 +43,8 @@ if GetNampowerVersion then
end end
end end
-- Nampower startup check: show version info and ensure CVars are set. EventUtil.ContinueOnPlayerLogin(function()
-- Runs on first OnUpdate after PLAYER_ENTERING_WORLD to give Nampower time to initialize. RunNextFrame(function()
local nampowerCheckFrame = CreateFrame("Frame")
nampowerCheckFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
nampowerCheckFrame:SetScript("OnEvent", function()
-- Defer to next frame so Nampower is fully initialized
this:SetScript("OnUpdate", function()
this:SetScript("OnUpdate", nil)
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
if GetNampowerVersion then if GetNampowerVersion then
local major, minor, patch = GetNampowerVersion() local major, minor, patch = GetNampowerVersion()
@@ -60,8 +52,6 @@ nampowerCheckFrame:SetScript("OnEvent", function()
local versionString = major .. "." .. minor .. "." .. patch local versionString = major .. "." .. minor .. "." .. patch
if major > 3 or (major == 3 and minor > 0) or (major == 3 and minor == 0 and patch >= 0) then if major > 3 or (major == 3 and minor > 0) or (major == 3 and minor == 0 and patch >= 0) then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Nampower v" .. versionString .. " detected - GetUnitField mode enabled!")
if SetCVar and GetCVar then if SetCVar and GetCVar then
local cvarsToEnable = { local cvarsToEnable = {
"NP_EnableSpellStartEvents", "NP_EnableSpellStartEvents",
@@ -91,8 +81,6 @@ nampowerCheckFrame:SetScript("OnEvent", function()
if enabledCount > 0 then if enabledCount > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Enabled " .. enabledCount .. " Nampower CVars") DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Enabled " .. enabledCount .. " Nampower CVars")
elseif alreadyEnabledCount == table.getn(cvarsToEnable) then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r All required Nampower CVars already enabled")
end end
if failedCount > 0 then if failedCount > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00[libdebuff]|r Warning: Could not check/set " .. failedCount .. " CVars") DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00[libdebuff]|r Warning: Could not check/set " .. failedCount .. " CVars")
@@ -204,10 +192,6 @@ pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {}
pfUI.libdebuff_downrank_blocked_hooks = pfUI.libdebuff_downrank_blocked_hooks or {} pfUI.libdebuff_downrank_blocked_hooks = pfUI.libdebuff_downrank_blocked_hooks or {}
local AURA_CAST_DEDUPE_WINDOW = 0.1 -- Ignore duplicates within 100ms local AURA_CAST_DEDUPE_WINDOW = 0.1 -- Ignore duplicates within 100ms
-- Captured combo points from SPELL_CAST_EVENT (before client consumes them)
-- SPELL_CAST_EVENT fires BEFORE UnitAura updates, so GetComboPoints() still works
local capturedCP = nil
-- Pending cast info for libpredict (heal prediction target tracking) -- Pending cast info for libpredict (heal prediction target tracking)
-- SPELL_CAST_EVENT fires with targetGuid BEFORE SPELLCAST_START, -- SPELL_CAST_EVENT fires with targetGuid BEFORE SPELLCAST_START,
-- which allows libpredict to know the correct target for queued casts. -- which allows libpredict to know the correct target for queued casts.
@@ -280,50 +264,10 @@ local debuffOverwritePairs = {
["Demoralizing Roar"] = "Demoralizing Shout", ["Demoralizing Roar"] = "Demoralizing Shout",
} }
-- Combopoint-based abilities: Only show timers for OUR casts
-- Format: [spellName] = { base = N, perCP = N }
-- Duration formula: duration = base + combopoints * perCP
local combopointAbilities = {
-- Druid
["Rip"] = { base = 8, perCP = 2 },
-- Rogue
["Rupture"] = { base = 6, perCP = 2 },
["Kidney Shot"] = { base = 1, perCP = 1 },
["Slice and Dice"] = { base = 9, perCP = 3 },
["Expose Armor"] = { base = 30, perCP = 0 }, -- fixed 30s
}
-- ============================================================================ -- ============================================================================
-- HELPER FUNCTIONS -- HELPER FUNCTIONS
-- ============================================================================ -- ============================================================================
-- Check if spell is a combo-point ability
local function IsComboPointAbility(spellName)
if not spellName then return false end
return combopointAbilities[spellName] ~= nil
end
-- Get combo-point spell data (base duration and per-CP bonus)
local function GetComboPointData(spellName)
if not spellName then return nil, nil end
local cpData = combopointAbilities[spellName]
if cpData then
return cpData.base, cpData.perCP
end
return nil, nil
end
-- Player GUID Cache
local playerGUID = nil
local function GetPlayerGUID()
if not playerGUID and UnitGUID then
local guid = UnitGUID("player")
playerGUID = guid
end
return playerGUID
end
-- Debug Stats -- Debug Stats
pfUI.libdebuff_debugstats = pfUI.libdebuff_debugstats or { pfUI.libdebuff_debugstats = pfUI.libdebuff_debugstats or {
enabled = false, enabled = false,
@@ -427,7 +371,7 @@ local function GetDebuffSlotMap(guid)
local spellId = auras[auraSlot] local spellId = auras[auraSlot]
if spellId and spellId > 0 then if spellId and spellId > 0 then
displaySlot = displaySlot + 1 displaySlot = displaySlot + 1
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
local texture = libdebuff:GetSpellIcon(spellId) local texture = libdebuff:GetSpellIcon(spellId)
local stacks = (auraApps and auraApps[auraSlot] or 0) + 1 local stacks = (auraApps and auraApps[auraSlot] or 0) + 1
local dtype = nil local dtype = nil
@@ -463,7 +407,7 @@ local function GetSlotCaster(guid, auraSlot, spellName)
end end
-- Fallback: Check ownDebuffs -- Fallback: Check ownDebuffs
local myGuid = GetPlayerGUID() local myGuid = GetPlayerGuid()
if ownDebuffs[guid] and ownDebuffs[guid][spellName] then if ownDebuffs[guid] and ownDebuffs[guid][spellName] then
return myGuid, true return myGuid, true
end end
@@ -654,16 +598,7 @@ function libdebuff:GetDuration(effect, rank)
local rank = L["debuffs"][effect][rank] and rank or libdebuff:GetMaxRank(effect) local rank = L["debuffs"][effect][rank] and rank or libdebuff:GetMaxRank(effect)
local duration = L["debuffs"][effect][rank] local duration = L["debuffs"][effect][rank]
if effect == L["dyndebuffs"]["Rupture"] then if effect == L["dyndebuffs"]["Demoralizing Shout"] then
local cp = GetComboPoints() or 0
duration = duration + cp*2
elseif effect == L["dyndebuffs"]["Kidney Shot"] then
local cp = GetComboPoints() or 0
duration = duration + cp*1
elseif effect == "Rip" or effect == L["dyndebuffs"]["Rip"] then
local cp = GetComboPoints() or 0
duration = 8 + cp*2
elseif effect == L["dyndebuffs"]["Demoralizing Shout"] then
local _,_,_,_,count = GetTalentInfo(2,1) local _,_,_,_,count = GetTalentInfo(2,1)
if count and count > 0 then duration = duration + ( duration / 100 * (count*10)) end if count and count > 0 then duration = duration + ( duration / 100 * (count*10)) end
elseif effect == L["dyndebuffs"]["Shadow Word: Pain"] then elseif effect == L["dyndebuffs"]["Shadow Word: Pain"] then
@@ -800,7 +735,7 @@ function libdebuff:GetBestAuraCast(guid, spellName)
local data = ownDebuffs[guid][spellName] local data = ownDebuffs[guid][spellName]
local timeleft = (data.startTime + data.duration) - GetTime() local timeleft = (data.startTime + data.duration) - GetTime()
if timeleft > 0 then if timeleft > 0 then
return data.startTime, data.duration, timeleft, data.rank, GetPlayerGUID() return data.startTime, data.duration, timeleft, data.rank, GetPlayerGuid()
end end
end end
@@ -827,33 +762,6 @@ function libdebuff:GetBestAuraCast(guid, spellName)
return nil return nil
end end
-- ============================================================================
-- API: GetEnhancedDebuffs (for external modules)
-- ============================================================================
function libdebuff:GetEnhancedDebuffs(targetGUID)
if not targetGUID then return nil end
local result = {}
if ownDebuffs[targetGUID] then
local myGuid = GetPlayerGUID()
for spellName, data in pairs(ownDebuffs[targetGUID]) do
local timeleft = (data.startTime + data.duration) - GetTime()
if timeleft > 0 then
result[spellName] = result[spellName] or {}
result[spellName][myGuid] = {
startTime = data.startTime,
duration = data.duration,
texture = data.texture,
rank = data.rank
}
end
end
end
return result
end
-- ============================================================================ -- ============================================================================
-- NAMPOWER EVENT HANDLING -- NAMPOWER EVENT HANDLING
-- ============================================================================ -- ============================================================================
@@ -885,7 +793,7 @@ if hasNampower then
-- Carnage triggered! Refresh Rip & Rake -- Carnage triggered! Refresh Rip & Rake
local guid = carnageState.targetGuid local guid = carnageState.targetGuid
local refreshTime = GetTime() local refreshTime = GetTime()
local myGuid = GetPlayerGUID() local myGuid = GetPlayerGuid()
-- Refresh in ownDebuffs - only if timer still active -- Refresh in ownDebuffs - only if timer still active
if ownDebuffs[guid] then if ownDebuffs[guid] then
@@ -967,7 +875,7 @@ if hasNampower then
-- Channel interrupted by player - clear ownDebuffs for the channeled spell -- Channel interrupted by player - clear ownDebuffs for the channeled spell
-- via C_Spell.ChannelInfo. DEBUFF_REMOVED fires later (0.5-1s server lag), -- via C_Spell.ChannelInfo. DEBUFF_REMOVED fires later (0.5-1s server lag),
-- causing phantom debuff display without this pre-clear. -- causing phantom debuff display without this pre-clear.
local spellName = select(1, C_Spell.ChannelInfo()) local spellName = C_Spell.ChannelInfo()
if spellName then if spellName then
local targetGuid = UnitGUID and UnitGUID("target") local targetGuid = UnitGUID and UnitGUID("target")
if targetGuid and ownDebuffs[targetGuid] and ownDebuffs[targetGuid][spellName] then if targetGuid and ownDebuffs[targetGuid] and ownDebuffs[targetGuid][spellName] then
@@ -978,14 +886,8 @@ if hasNampower then
end end
end end
end end
elseif event == "PLAYER_ENTERING_WORLD" or event == "PLAYER_TALENT_UPDATE" then
elseif event == "PLAYER_ENTERING_WORLD" then
GetPlayerGUID()
UpdateCarnageRank() UpdateCarnageRank()
elseif event == "PLAYER_TALENT_UPDATE" then
UpdateCarnageRank()
elseif event == "UNIT_HEALTH" then elseif event == "UNIT_HEALTH" then
local guid = arg1 local guid = arg1
if guid and UnitIsDead and UnitIsDead(guid) then if guid and UnitIsDead and UnitIsDead(guid) then
@@ -1024,7 +926,7 @@ if hasNampower then
local targetGuid = arg4 local targetGuid = arg4
local numHit = arg6 or 0 local numHit = arg6 or 0
local numMissed = arg7 or 0 local numMissed = arg7 or 0
-- Fire registered SPELL_GO_SELF hooks BEFORE miss guard -- Fire registered SPELL_GO_SELF hooks BEFORE miss guard
-- (Swingtimer needs to see ALL casts, even misses, for swing reset) -- (Swingtimer needs to see ALL casts, even misses, for swing reset)
if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then
@@ -1035,15 +937,15 @@ if hasNampower then
if numMissed > 0 or numHit == 0 then return end if numMissed > 0 or numHit == 0 then return end
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
local spellRankString = GetSpellRecField and GetSpellRecField(spellId, "rank")
if not spellName then return end if not spellName then return end
local spellRankString = C_Spell.GetSpellSubtext(spellId)
local castRank = 0 local castRank = 0
if spellRankString and spellRankString ~= "" then if spellRankString and spellRankString ~= "" then
castRank = tonumber((string.gsub(spellRankString, "Rank ", ""))) or 0 castRank = tonumber((string.gsub(spellRankString, "Rank ", ""))) or 0
end end
-- Store in pendingCasts for DEBUFF_ADDED correlation. -- Store in pendingCasts for DEBUFF_ADDED correlation.
-- If this cast is a downrank of an already active debuff, fire the downrank blocked hook -- If this cast is a downrank of an already active debuff, fire the downrank blocked hook
-- so external addons (e.g. SuperCleveRoidMacros) don't need to re-implement this check. -- so external addons (e.g. SuperCleveRoidMacros) don't need to re-implement this check.
@@ -1080,7 +982,7 @@ if hasNampower then
local selfdebuffMode = pfUI_config and pfUI_config.buffbar and local selfdebuffMode = pfUI_config and pfUI_config.buffbar and
pfUI_config.buffbar.tdebuff and pfUI_config.buffbar.tdebuff.selfdebuff == "1" pfUI_config.buffbar.tdebuff and pfUI_config.buffbar.tdebuff.selfdebuff == "1"
if selfdebuffMode then if selfdebuffMode then
local myGuid2 = GetPlayerGUID() local myGuid2 = GetPlayerGuid()
if casterGuid == myGuid2 then if casterGuid == myGuid2 then
local duration = libdebuff:GetDuration(spellName, castRank) or 0 local duration = libdebuff:GetDuration(spellName, castRank) or 0
if duration > 0 then if duration > 0 then
@@ -1110,7 +1012,7 @@ if hasNampower then
end end
-- Store rank for our casts -- Store rank for our casts
local myGuid = GetPlayerGUID() local myGuid = GetPlayerGuid()
if casterGuid == myGuid then if casterGuid == myGuid then
lastCastRanks[spellName] = { lastCastRanks[spellName] = {
rank = castRank, rank = castRank,
@@ -1183,11 +1085,6 @@ if hasNampower then
pfUI.libpredict_pending_cast.time = nil pfUI.libpredict_pending_cast.time = nil
end end
-- Only capture CPs for combo-point abilities
if spellName and IsComboPointAbility(spellName) then
capturedCP = GetComboPoints() or 0
end
-- Fire registered SPELL_CAST_EVENT hooks -- Fire registered SPELL_CAST_EVENT hooks
if pfUI.libdebuff_spell_cast_hooks then if pfUI.libdebuff_spell_cast_hooks then
for _, fn in pairs(pfUI.libdebuff_spell_cast_hooks) do for _, fn in pairs(pfUI.libdebuff_spell_cast_hooks) do
@@ -1209,7 +1106,7 @@ if hasNampower then
if not spellId then return end if not spellId then return end
if not targetGuid or targetGuid == "" or targetGuid == "0x0000000000000000" then return end if not targetGuid or targetGuid == "" or targetGuid == "0x0000000000000000" then return end
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
-- Deduplicate: Ignore if we processed this exact cast recently (within 100ms) -- Deduplicate: Ignore if we processed this exact cast recently (within 100ms)
@@ -1235,37 +1132,17 @@ if hasNampower then
local duration = durationMs and (durationMs / 1000) or 0 local duration = durationMs and (durationMs / 1000) or 0
local startTime = GetTime() local startTime = GetTime()
local myGuid = GetPlayerGUID() local myGuid = GetPlayerGuid()
local isOurs = (myGuid and casterGuid == myGuid) local isOurs = (myGuid and casterGuid == myGuid)
if debugStats.enabled and isOurs then if debugStats.enabled and isOurs then
debugStats.aura_cast = debugStats.aura_cast + 1 debugStats.aura_cast = debugStats.aura_cast + 1
end end
-- Combo-point abilities: Calculate duration based on CPs used -- Duration comes from nampower's AURA_CAST event. ClassicAPI's
if IsComboPointAbility(spellName) then -- C_UnitAuras now resolves combo-scaled durations server-side, so
if isOurs then -- libdebuff only needs the database fallback when AURA_CAST reports 0.
-- OWN casts: use captured CPs from SPELL_CAST_EVENT (if available) if duration == 0 then
local cp = capturedCP or 0
local base, perCP = GetComboPointData(spellName)
if base and perCP then
duration = base + cp * perCP
else
-- Fallback to legacy database
duration = libdebuff:GetDuration(spellName, rankNum)
end
capturedCP = nil -- consumed
else
-- OTHER players: CP unknown, no timer (except Expose Armor = fixed 30s)
local base, perCP = GetComboPointData(spellName)
if perCP and perCP == 0 and base then
duration = base -- fixed duration (Expose Armor)
else
duration = 0 -- CP unknown for other players
end
end
elseif duration == 0 then
-- Non-CP managed spells: use database if AURA_CAST returned 0
duration = libdebuff:GetDuration(spellName, rankNum) or 0 duration = libdebuff:GetDuration(spellName, rankNum) or 0
end end
@@ -1367,15 +1244,12 @@ if hasNampower then
-- Notify unitframes of debuff updates (UNIT_AURA doesn't fire on refreshes!) -- Notify unitframes of debuff updates (UNIT_AURA doesn't fire on refreshes!)
-- Check player -- Check player
if UnitGUID("player") then if IsPlayerGuid(targetGuid) and pfPlayer then
local playerGuid = UnitGUID("player") pfPlayer.update_aura = true
if playerGuid == targetGuid and pfPlayer then
pfPlayer.update_aura = true
end
end end
-- Check target -- Check target
if UnitGUID("target") then if UnitExists("target") then
local tarUnitGUID = UnitGUID("target") local tarUnitGUID = UnitGUID("target")
if tarUnitGUID == targetGuid and pfTarget then if tarUnitGUID == targetGuid and pfTarget then
pfTarget.update_aura = true pfTarget.update_aura = true
@@ -1469,7 +1343,7 @@ if hasNampower then
-- Invalidate slot map cache for this GUID -- Invalidate slot map cache for this GUID
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
if debugStats.enabled then if debugStats.enabled then
@@ -1522,7 +1396,7 @@ if hasNampower then
end end
end end
local myGuid = GetPlayerGUID() local myGuid = GetPlayerGuid()
local isOurs = (myGuid and casterGuid == myGuid) local isOurs = (myGuid and casterGuid == myGuid)
-- Fallback: Check ownDebuffs timing -- Fallback: Check ownDebuffs timing
@@ -1557,7 +1431,6 @@ if hasNampower then
-- CRITICAL FIX: Update ownDebuffs here too for refresh timing! -- CRITICAL FIX: Update ownDebuffs here too for refresh timing!
-- This prevents the gap between DEBUFF_REMOVED and AURA_CAST where buffwatch shows nothing -- This prevents the gap between DEBUFF_REMOVED and AURA_CAST where buffwatch shows nothing
if isOurs and casterGuid then if isOurs and casterGuid then
local myGuid = GetPlayerGUID()
if myGuid and casterGuid == myGuid then if myGuid and casterGuid == myGuid then
-- Check if we have timer data from allAuraCasts -- Check if we have timer data from allAuraCasts
if allAuraCasts[guid] and allAuraCasts[guid][spellName] and allAuraCasts[guid][spellName][casterGuid] then if allAuraCasts[guid] and allAuraCasts[guid][spellName] and allAuraCasts[guid][spellName][casterGuid] then
@@ -1599,9 +1472,9 @@ if hasNampower then
local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil
-- Invalidate slot map cache for this GUID -- Invalidate slot map cache for this GUID
local spellName = (GetSpellRecField and GetSpellRecField(spellId, "name")) or "?" local spellName = C_Spell.GetSpellName(spellId) or "?"
if debugStats.enabled then if debugStats.enabled then
debugStats.debuff_removed = debugStats.debuff_removed + 1 debugStats.debuff_removed = debugStats.debuff_removed + 1
if IsCurrentTarget(guid) then if IsCurrentTarget(guid) then
@@ -1609,13 +1482,13 @@ if hasNampower then
GetDebugTimestamp(), displaySlot, auraSlot or -1, auraSlot_0based or -1, spellName)) GetDebugTimestamp(), displaySlot, auraSlot or -1, auraSlot_0based or -1, spellName))
end end
end end
-- If unit is dead, cleanup all -- If unit is dead, cleanup all
if UnitIsDead and UnitIsDead(guid) then if UnitIsDead and UnitIsDead(guid) then
CleanupUnit(guid) CleanupUnit(guid)
return return
end end
-- Get auraSlot from event parameter (Nampower 2.29+) -- Get auraSlot from event parameter (Nampower 2.29+)
-- Fallback to displayToAura mapping if not available -- Fallback to displayToAura mapping if not available
local foundAuraSlot = auraSlot local foundAuraSlot = auraSlot
@@ -1811,5 +1684,3 @@ _G.SlashCmdList["MEMCHECK"] = function()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00No ownSlots/allSlots (eliminated by GetUnitField approach!)|r") DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00No ownSlots/allSlots (eliminated by GetUnitField approach!)|r")
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff============================================================|r") DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff============================================================|r")
end end
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r GetUnitField Edition loaded!")
+38 -104
View File
@@ -44,78 +44,13 @@ local healGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER c
local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window) local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window)
local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s
local PRAYER_OF_HEALING -- Localized spell names resolved once from canonical rank-1 spellIDs.
do -- Prayer of Healing -- Every rank shares the same name, so per-rank comparisons elsewhere can
local locales = { -- be done against these constants without per-locale or per-rank tables.
["deDE"] = "Gebet der Heilung", local PRAYER_OF_HEALING = C_Spell.GetSpellName(596) -- Prayer of Healing (Rank 1)
["enUS"] = "Prayer of Healing", local REJUVENATION = C_Spell.GetSpellName(774) -- Rejuvenation (Rank 1)
["esES"] = "Rezo de curación", local RENEW = C_Spell.GetSpellName(139) -- Renew (Rank 1)
["frFR"] = "Prière de soins", local REGROWTH = C_Spell.GetSpellName(8936) -- Regrowth (Rank 1)
["koKR"] = "치유의 기원",
["ruRU"] = "Молитва исцеления",
["zhCN"] = "治疗祷言",
}
PRAYER_OF_HEALING = locales[GetLocale()] or locales["enUS"]
end
local REJUVENATION
do -- Rejuvenation
local locales = {
["deDE"] = "Verjüngung",
["enUS"] = "Rejuvenation",
["esES"] = "Rejuvenecimiento",
["frFR"] = "Récupération",
["koKR"] = "회복",
["ruRU"] = "Омоложение",
["zhCN"] = "回春术",
}
REJUVENATION = locales[GetLocale()] or locales["enUS"]
end
local RENEW
do -- Renew
local locales = {
["deDE"] = "Erneuerung",
["enUS"] = "Renew",
["esES"] = "Renovar",
["frFR"] = "Rénovation",
["koKR"] = "소생",
["ruRU"] = "Обновление",
["zhCN"] = "恢复",
}
RENEW = locales[GetLocale()] or locales["enUS"]
end
local REGROWTH
do -- Regrowth
local locales = {
["deDE"] = "Nachwachsen",
["enUS"] = "Regrowth",
["esES"] = "Recrecimiento",
["frFR"] = "Rétablissement",
["koKR"] = "재생",
["ruRU"] = "Восстановление",
["zhCN"] = "愈合",
}
REGROWTH = locales[GetLocale()] or locales["enUS"]
end
-- Spell IDs for SPELL_GO_SELF callback (Nampower) - Instant HoT detection
local SPELL_IDS = {
-- Rejuvenation (all ranks)
[774] = "Reju", [1058] = "Reju", [1430] = "Reju", [2090] = "Reju", [2091] = "Reju",
[3627] = "Reju", [8910] = "Reju", [9839] = "Reju", [9840] = "Reju", [9841] = "Reju",
[25299] = "Reju", [26981] = "Reju", [26982] = "Reju",
-- Renew (all ranks)
[139] = "Renew", [6074] = "Renew", [6075] = "Renew", [6076] = "Renew", [6077] = "Renew",
[6078] = "Renew", [10927] = "Renew", [10928] = "Renew", [10929] = "Renew", [25315] = "Renew",
[25221] = "Renew", [25222] = "Renew",
}
local libpredict = CreateFrame("Frame") local libpredict = CreateFrame("Frame")
libpredict:RegisterEvent("UNIT_HEALTH") libpredict:RegisterEvent("UNIT_HEALTH")
@@ -173,7 +108,7 @@ end
local function isRezSpell(spellId) local function isRezSpell(spellId)
if not L["resurrections"] then return false end if not L["resurrections"] then return false end
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
return spellName and L["resurrections"][spellName] return spellName and L["resurrections"][spellName]
end end
@@ -192,7 +127,7 @@ end)
-- SPELL_START_SELF: own cast started (heals + rez) -- SPELL_START_SELF: own cast started (heals + rez)
pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {} pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {}
pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime) pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local pendingTarget = nil local pendingTarget = nil
@@ -255,7 +190,7 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
-- selfcast (ALT) = player, otherwise = current target -- selfcast (ALT) = player, otherwise = current target
-- Use this to find the correct group to heal -- Use this to find the correct group to heal
local pohTarget = target or player local pohTarget = target or player
if GetNumRaidMembers() > 0 then if IsInRaid() then
-- Raid: find pohTarget's subgroup and heal only those members -- Raid: find pohTarget's subgroup and heal only those members
-- (Turtle WoW changed PoH to heal the target's group, not the caster's group) -- (Turtle WoW changed PoH to heal the target's group, not the caster's group)
local targetGroup local targetGroup
@@ -319,7 +254,7 @@ end
-- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer) -- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer)
pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId) pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId)
libpredict:HealStop(player) libpredict:HealStop(player)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if spellName == REGROWTH then if spellName == REGROWTH then
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
if libpredict.sender.regrowth_timer then if libpredict.sender.regrowth_timer then
@@ -337,7 +272,7 @@ end
-- Signature: fn(spellId, casterGuid, targetGuid, castTime) -- Signature: fn(spellId, casterGuid, targetGuid, castTime)
pfUI.libdebuff_spell_start_other_hooks = pfUI.libdebuff_spell_start_other_hooks or {} pfUI.libdebuff_spell_start_other_hooks = pfUI.libdebuff_spell_start_other_hooks or {}
pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime) pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local casterName = resolveNameFromGuid(casterGuid) local casterName = resolveNameFromGuid(casterGuid)
@@ -364,7 +299,7 @@ pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterG
-- Prayer of Healing: heal entire subgroup of the target -- Prayer of Healing: heal entire subgroup of the target
if spellName == PRAYER_OF_HEALING then if spellName == PRAYER_OF_HEALING then
if GetNumRaidMembers() > 0 then if IsInRaid() then
local targetGroup local targetGroup
for i = 1, GetNumRaidMembers() do for i = 1, GetNumRaidMembers() do
local rname, _, subgroup = GetRaidRosterInfo(i) local rname, _, subgroup = GetRaidRosterInfo(i)
@@ -404,8 +339,13 @@ end
-- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {} pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4, a5, a6, a7) pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4, a5, a6, a7)
-- Instant HoTs -- Instant HoTs — classify by name (rank-independent) instead of a
local hotType = SPELL_IDS[spellId] -- hardcoded per-rank ID table.
local spellName = C_Spell.GetSpellName(spellId)
local hotType
if spellName == REJUVENATION then hotType = "Reju"
elseif spellName == RENEW then hotType = "Renew"
end
if hotType then if hotType then
local targetGuid = a4 local targetGuid = a4
local targetName = resolveNameFromGuid(targetGuid) local targetName = resolveNameFromGuid(targetGuid)
@@ -426,9 +366,9 @@ pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4,
local rankStr = tostring(rank) local rankStr = tostring(rank)
if libpredict.sender and libpredict.sender.SendHealCommMsg then if libpredict.sender and libpredict.sender.SendHealCommMsg then
libpredict.sender:SendHealCommMsg(hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/") libpredict.sender:SendHealCommMsg(hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/")
elseif GetNumRaidMembers() > 0 then elseif IsInRaid() then
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "RAID") SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "RAID")
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "PARTY") SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "PARTY")
end end
end end
@@ -571,8 +511,7 @@ function libpredict:ParseComm(sender, msg)
local unit = senderUnit() local unit = senderUnit()
if not unit then return end if not unit then return end
local startMs = select(4, C_Spell.UnitCastingInfo(unit)) local _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit)
local endMs = select(5, C_Spell.UnitCastingInfo(unit))
if not startMs or not endMs then return end if not startMs or not endMs then return end
time = (endMs - startMs) / 1000 time = (endMs - startMs) / 1000
elseif msgtype == 1 then elseif msgtype == 1 then
@@ -583,8 +522,7 @@ function libpredict:ParseComm(sender, msg)
target = {strsplit(":", string.sub(msg,9, -1))} target = {strsplit(":", string.sub(msg,9, -1))}
local unit = senderUnit() local unit = senderUnit()
if not unit then return end if not unit then return end
local startMs = select(4, C_Spell.UnitCastingInfo(unit)) local _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit)
local endMs = select(5, C_Spell.UnitCastingInfo(unit))
if not startMs or not endMs then return end if not startMs or not endMs then return end
time = (endMs - startMs) / 1000 time = (endMs - startMs) / 1000
end end
@@ -878,7 +816,7 @@ local hotsetbonus = libtipscan:GetScanner("hotsetbonus")
resetcache:RegisterEvent("PLAYER_ENTERING_WORLD") resetcache:RegisterEvent("PLAYER_ENTERING_WORLD")
resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB") resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB")
resetcache:RegisterEvent("CHARACTER_POINTS_CHANGED") resetcache:RegisterEvent("CHARACTER_POINTS_CHANGED")
resetcache:RegisterEvent("UNIT_INVENTORY_CHANGED") resetcache:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
resetcache:SetScript("OnEvent", function() resetcache:SetScript("OnEvent", function()
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
-- load and initialize previous caches of spell amounts -- load and initialize previous caches of spell amounts
@@ -889,10 +827,7 @@ resetcache:SetScript("OnEvent", function()
cache = pfUI_cache["prediction"][realm][player]["heals"] cache = pfUI_cache["prediction"][realm][player]["heals"]
end end
if event == "UNIT_INVENTORY_CHANGED" or "PLAYER_ENTERING_WORLD" then if event == "PLAYER_EQUIPMENT_CHANGED" or event == "PLAYER_ENTERING_WORLD" then
-- skip non-player events
if arg1 and arg1 ~= "player" then return end
local gear = "" local gear = ""
for id = 1, 18 do for id = 1, 18 do
gear = gear .. (GetInventoryItemLink("player",id) or "") gear = gear .. (GetInventoryItemLink("player",id) or "")
@@ -1019,8 +954,6 @@ hooksecurefunc("CastSpellByName", function(effect, target)
if not libpredict.sender.enabled then return end if not libpredict.sender.enabled then return end
local effect, rank = libspell.GetSpellInfo(effect) local effect, rank = libspell.GetSpellInfo(effect)
if not effect then return end if not effect then return end
local mouseover = pfUI and pfUI.uf and pfUI.uf.mouseover and pfUI.uf.mouseover.unit
mouseover = mouseover and UnitCanAssist("player", mouseover) and UnitName(mouseover)
local default = UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player") local default = UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player")
@@ -1039,13 +972,13 @@ hooksecurefunc("CastSpellByName", function(effect, target)
if not libpredict.sender.current_cast then if not libpredict.sender.current_cast then
spell_queue[1] = effect spell_queue[1] = effect
spell_queue[2] = effect.. ( rank or "" ) spell_queue[2] = effect.. ( rank or "" )
spell_queue[3] = target or mouseover or default spell_queue[3] = target or default
end end
-- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback -- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = target or mouseover or default local hotTarget = target or default
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
local key = "Reju" .. hotTarget local key = "Reju" .. hotTarget
@@ -1062,7 +995,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
local rankStr = rankNum and tostring(rankNum) or "0" local rankStr = rankNum and tostring(rankNum) or "0"
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/") libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then elseif effect == RENEW then
local hotTarget = target or mouseover or default local hotTarget = target or default
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
local key = "Renew" .. hotTarget local key = "Renew" .. hotTarget
@@ -1087,7 +1020,8 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
local kind, id = GetActionInfo(slot) local kind, id = GetActionInfo(slot)
local effect, rank local effect, rank
if kind == "spell" then if kind == "spell" then
effect, rank = GetSpellInfo(id) local spellInfo = C_Spell.GetSpellInfo(id)
effect, rank = spellInfo.name, spellInfo.rank
elseif kind == "macro" then elseif kind == "macro" then
effect, rank = GetMacroSpell(id) effect, rank = GetMacroSpell(id)
end end
@@ -1145,10 +1079,10 @@ libpredict.sender = CreateFrame("Frame", "pfPredictionSender", UIParent)
libpredict.sender.enabled = true libpredict.sender.enabled = true
libpredict.sender.SendHealCommMsg = function(self, msg) libpredict.sender.SendHealCommMsg = function(self, msg)
-- Smart channel selection: Only send to relevant channel to avoid duplicates -- Smart channel selection: Only send to relevant channel to avoid duplicates
if GetNumRaidMembers() > 0 then if IsInRaid() then
-- In raid: Only send to RAID (includes all raid members) -- In raid: Only send to RAID (includes all raid members)
SendAddonMessage("HealComm", msg, "RAID") SendAddonMessage("HealComm", msg, "RAID")
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
-- In party: Only send to PARTY -- In party: Only send to PARTY
SendAddonMessage("HealComm", msg, "PARTY") SendAddonMessage("HealComm", msg, "PARTY")
end end
@@ -1157,10 +1091,10 @@ libpredict.sender.SendHealCommMsg = function(self, msg)
end end
libpredict.sender.SendResCommMsg = function(self, msg) libpredict.sender.SendResCommMsg = function(self, msg)
-- Smart channel selection: Only send to relevant channel to avoid duplicates -- Smart channel selection: Only send to relevant channel to avoid duplicates
if GetNumRaidMembers() > 0 then if IsInRaid() then
-- In raid: Only send to RAID (includes all raid members) -- In raid: Only send to RAID (includes all raid members)
SendAddonMessage("CTRA", msg, "RAID") SendAddonMessage("CTRA", msg, "RAID")
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
-- In party: Only send to PARTY -- In party: Only send to PARTY
SendAddonMessage("CTRA", msg, "PARTY") SendAddonMessage("CTRA", msg, "PARTY")
end end
@@ -1236,7 +1170,7 @@ libpredict.sender:SetScript("OnEvent", function()
local amount = arg4 local amount = arg4
local isCrit = arg5 == 1 local isCrit = arg5 == 1
local isPeriodic = arg6 == 1 local isPeriodic = arg6 == 1
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if spellName and spell_queue[1] == spellName then if spellName and spell_queue[1] == spellName then
UpdateCache(spell_queue[2], amount, isCrit) UpdateCache(spell_queue[2], amount, isCrit)
end end
@@ -1256,9 +1190,9 @@ libpredict.sender:SetScript("OnEvent", function()
local casterName = resolveNameFromGuid(casterGuid) local casterName = resolveNameFromGuid(casterGuid)
if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local rankStr = GetSpellRecField and GetSpellRecField(spellId, "rank") or "" local rankStr = C_Spell.GetSpellSubtext(spellId)
local spellKey = spellName .. (rankStr or "") local spellKey = spellName .. (rankStr or "")
foreignCache[casterName] = foreignCache[casterName] or {} foreignCache[casterName] = foreignCache[casterName] or {}
+14 -164
View File
@@ -2,178 +2,28 @@
setfenv(1, pfUI:GetEnvironment()) setfenv(1, pfUI:GetEnvironment())
--[[ librange ]]-- --[[ librange ]]--
-- A pfUI library that detects and caches distance to units. -- A thin wrapper over ClassicAPI's UnitInRange: a fixed 40y healing-range
-- check computed C-side from unit positions, valid for any unit. There is
-- no cache or scan loop -- the check is cheap enough to run per query,
-- which also sidesteps the staleness a cached scan hit on zone changes and
-- roster re-indexing.
-- --
-- librange:UnitInSpellRange(unit) -- librange:UnitInSpellRange(unit)
-- Returns `1` if the unit is within range, `nil` otherwise. -- Returns `1` if the unit is within range, `nil` otherwise.
--
-- Requires SuperWoW's UnitPosition for the friendly scan path. Target
-- range still works via IsActionInRange (vanilla-native) for any class
-- with a known 40y healing spell on the action bar.
if pfUI.api.librange then return end if pfUI.api.librange then return end
local _, class = UnitClass("player") local librange = {}
local librange = CreateFrame("Frame", "pfRangecheck", UIParent)
-- 40y spells per class. Only consulted to find an action-bar slot for the
-- IsActionInRange target-range path; the party/raid scan uses UnitPosition.
local spells = {
["PALADIN"] = {
"Interface\\Icons\\Spell_Holy_FlashHeal",
"Interface\\Icons\\Spell_Holy_HolyBolt",
},
["PRIEST"] = {
"Interface\\Icons\\Spell_Holy_FlashHeal",
"Interface\\Icons\\Spell_Holy_LesserHeal",
"Interface\\Icons\\Spell_Holy_Heal",
"Interface\\Icons\\Spell_Holy_GreaterHeal",
"Interface\\Icons\\Spell_Holy_Renew",
},
["DRUID"] = {
"Interface\\Icons\\Spell_Nature_HealingTouch",
"Interface\\Icons\\Spell_Nature_ResistNature",
"Interface\\Icons\\Spell_Nature_Rejuvenation",
},
["SHAMAN"] = {
"Interface\\Icons\\Spell_Nature_MagicImmunity",
"Interface\\Icons\\Spell_Nature_HealingWaveLesser",
"Interface\\Icons\\Spell_Nature_HealingWaveGreater",
},
}
-- friendly units the scan loop iterates
local units = {}
table.insert(units, "pet")
for i=1,4 do table.insert(units, "party" .. i) end
for i=1,4 do table.insert(units, "partypet" .. i) end
for i=1,40 do table.insert(units, "raid" .. i) end
for i=1,40 do table.insert(units, "raidpet" .. i) end
local numunits = table.getn(units)
local unitcache = {}
local unitdata = {}
local librange_isLoggingOut = false
librange.id = 1
librange:Hide()
librange:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
librange:RegisterEvent("PLAYER_ENTERING_WORLD")
librange:RegisterEvent("PLAYER_LOGOUT")
librange:RegisterEvent("PLAYER_LEAVING_WORLD")
librange:SetScript("OnEvent", function()
if event == "PLAYER_LOGOUT" or event == "PLAYER_LEAVING_WORLD" then
librange_isLoggingOut = true
this:SetScript("OnUpdate", nil)
this:Hide()
return
end
if pfUI_config.unitframes.rangecheck == "0" then
this:Hide()
return
end
this.interval = tonumber(C.unitframes.rangechecki)/numunits
if event == "ACTIONBAR_SLOT_CHANGED" or event == "PLAYER_ENTERING_WORLD" then
librange.slot = this:GetRangeSlot()
if UnitPosition then this:Show() end
end
end)
librange:SetScript("OnUpdate", function()
if librange_isLoggingOut then return end
if (this.tick or 1) > GetTime() then return end
this.tick = GetTime() + this.interval
while not this:NeedRangeScan(units[this.id]) and this.id <= numunits do
this.id = this.id + 1
end
if this.id <= numunits then
local unit = units[this.id]
if not UnitIsUnit("target", unit) then
local x1, y1, z1 = UnitPosition("player")
local x2, y2, z2 = UnitPosition(unit)
if x1 and x2 then
local distance = ((x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2)^.5
unitdata[unit] = distance < 45 and 1 or 0
end
end
this.id = this.id + 1
else
this.id = 1
end
end)
function librange:NeedRangeScan(unit)
if not UnitExists(unit) then return nil end
if not UnitIsVisible(unit) then return nil end
if CheckInteractDistance(unit, 4) then return nil end
return true
end
function librange:GetRealUnit(unit)
if unitdata[unit] then return unit end
if unitcache[unit] and UnitIsUnit(unitcache[unit], unit) then
return unitcache[unit]
end
for id, realunit in pairs(units) do
if UnitIsUnit(realunit, unit) then
unitcache[unit] = realunit
return realunit
end
end
return unit
end
function librange:GetRangeSlot()
if not spells[class] then return nil end
for i=1,120 do
-- Resolve the slot to a spellID for both spell and macro actions; the old
-- `not GetActionText` macro-filter missed macros that cast a 40y heal but
-- displayed a non-spell icon. C_Spell.GetSpellTexture(spellID) gives the
-- spell's *intrinsic* icon, which is what we match against.
local kind, id = GetActionInfo(i)
local spellID
if kind == "spell" then
spellID = id
elseif kind == "macro" then
local _, _, sid = GetMacroSpell(id)
spellID = sid
end
if spellID then
local texture = C_Spell.GetSpellTexture(spellID)
if texture then
for _, check in pairs(spells[class]) do
if check == texture then return i end
end
end
end
end
return nil
end
function librange:UnitInSpellRange(unit) function librange:UnitInSpellRange(unit)
if UnitIsUnit("target", unit) then -- _G-qualified: bare `UnitInRange` resolves to pfUI.api.UnitInRange inside
if not librange.slot then return nil end -- the pfUI environment (which calls us), so this must reach ClassicAPI's
return IsActionInRange(librange.slot) == 1 and 1 or nil -- global directly or it recurses.
end local inRange, checked = _G.UnitInRange(unit)
-- position miss (e.g. a unit outside the client's sync range): we can't
local unit = librange:GetRealUnit(unit) -- tell, so default to in-range -- matches the old cache's nil behavior.
if not checked then return 1 end
if unitdata[unit] and unitdata[unit] == 1 then return inRange and 1 or nil
return 1
elseif not unitdata[unit] then
return 1
else
return nil
end
end end
-- add librange to pfUI API -- add librange to pfUI API
+3 -1
View File
@@ -118,7 +118,9 @@ end
local resetcache = CreateFrame("Frame") local resetcache = CreateFrame("Frame")
resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB") resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB")
resetcache:SetScript("OnEvent", function() resetcache:SetScript("OnEvent", function()
spellmaxrank, spellindex, spellinfo = {}, {}, {} table.wipe(spellmaxrank)
table.wipe(spellindex)
table.wipe(spellinfo)
end) end)
-- add libspell to pfUI API -- add libspell to pfUI API
-59
View File
@@ -1,59 +0,0 @@
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
--[[ libtooltip ]]--
-- A pfUI library that provides additional GameTooltip information.
--
-- libtooltip:GetItemID()
-- returns the itemID of the current GameTooltip
-- `nil` when no item is displayed
--
-- libtooltip:GetItemLink()
-- returns the itemLink of the current GameTooltip
-- `nil` when no item is displayed
--
-- libtooltip:GetItemCount()
-- returns the item count (bags) of the current GameTooltip
-- `nil` when no item is displayed
-- return instantly when another libtooltip is already active
if pfUI.api.libtooltip then return end
local libtooltip = CreateFrame("Frame" , "pfLibTooltip", GameTooltip)
libtooltip:SetScript("OnShow", function()
if this:GetParent():HasItem() then
libtooltip.itemName, libtooltip.itemLink, libtooltip.itemID = this:GetParent():GetItem()
end
end)
libtooltip:SetScript("OnHide", function()
this.itemID = nil
this.itemLink = nil
this.itemCount = nil
this.itemName = nil
end)
-- core functions
libtooltip.GetItemID = function(self)
if not libtooltip.itemLink then return end
if not libtooltip.itemID then
libtooltip.itemID = C_Item.GetItemInfoInstant(libtooltip.itemLink)
end
return libtooltip.itemID
end
libtooltip.GetItemLink = function(self)
return libtooltip.itemLink
end
libtooltip.GetItemCount = function(self)
return libtooltip.itemCount
end
pfUI.api.libtooltip = libtooltip
hooksecurefunc(GameTooltip, "SetBagItem", function(self, container, slot)
_, libtooltip.itemCount = GetContainerItemInfo(container, slot)
end)
-274
View File
@@ -1,274 +0,0 @@
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
--[[ libtotem ]]--
-- A pfUI library that tries to emulate the TotemAPI that was introduced in Patch 2.4.
-- It detects and saves all current totems of the player and returns information based
-- on the totem slot ID. The function GetTotemInfo is supposed to work as it would
-- on later expansions.
--
-- GetTotemInfo(id)
-- Returns totem informations on the givent totem slot
-- active, name, start, duration, icon
-- return instantly when another libtotem is already active
if pfUI.api.libtotem then return end
MAX_TOTEMS = MAX_TOTEMS or 4
FIRE_TOTEM_SLOT = FIRE_TOTEM_SLOT or 1
EARTH_TOTEM_SLOT = EARTH_TOTEM_SLOT or 2
WATER_TOTEM_SLOT = WATER_TOTEM_SLOT or 3
AIR_TOTEM_SLOT = AIR_TOTEM_SLOT or 4
local _, class = UnitClass("player")
local libtotem
local active = { [1] = {}, [2] = {}, [3] = {}, [4] = {} }
-- SpellID -> { slot, duration } mapping
-- rank-specific durations are handled via spellId directly
local spellids = {
-- FIRE (slot 1)
[1535] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R1
[8498] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R2
[8499] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R3
[11314] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R4
[11315] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R5
[8227] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R1
[8249] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R2
[10526] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R3
[16387] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R4
[8184] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R1
[10478] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R2
[10479] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R3
[8190] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R1
[10585] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R2
[10586] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R3
[10587] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R4
[3599] = { slot = FIRE_TOTEM_SLOT, duration = 30 }, -- Searing Totem R1
[6363] = { slot = FIRE_TOTEM_SLOT, duration = 35 }, -- Searing Totem R2
[6364] = { slot = FIRE_TOTEM_SLOT, duration = 40 }, -- Searing Totem R3
[6365] = { slot = FIRE_TOTEM_SLOT, duration = 45 }, -- Searing Totem R4
[10437] = { slot = FIRE_TOTEM_SLOT, duration = 50 }, -- Searing Totem R5
[10438] = { slot = FIRE_TOTEM_SLOT, duration = 55 }, -- Searing Totem R6
-- EARTH (slot 2)
[2484] = { slot = EARTH_TOTEM_SLOT, duration = 45 }, -- Earthbind Totem
[5730] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R1
[6390] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R2
[6391] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R3
[6392] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R4
[10427] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R5
[10428] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R6
[8071] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R1
[8154] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R2
[8155] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R3
[10406] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R4
[10407] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R5
[10408] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R6
[8075] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R1
[8160] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R2
[8161] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R3
[10442] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R4
[25361] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R5
[8143] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Tremor Totem
-- WATER (slot 3)
[8170] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Disease Cleansing Totem
[8185] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R1
[10537] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R2
[10538] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R3
[5394] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R1
[6375] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R2
[6377] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R3
[10462] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R4
[10463] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R5
[5675] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R1
[10495] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R2
[10496] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R3
[10497] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R4
[16190] = { slot = WATER_TOTEM_SLOT, duration = 12 }, -- Mana Tide Totem
[8166] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Poison Cleansing Totem
-- AIR (slot 4)
[8835] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R1
[10627] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R2
[8177] = { slot = AIR_TOTEM_SLOT, duration = 45 }, -- Grounding Totem
[10595] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R1
[10600] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R2
[10601] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R3
[25359] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Tranquil Air Totem
[8512] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R1
[10613] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R2
[10614] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R3
[15107] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R1
[15421] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R2
[15422] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R3
}
-- icon-based fallback table (used by CastSpell/UseAction hooks that don't have spellId)
local totems = {
[FIRE_TOTEM_SLOT] = {
["Spell_Fire_SealOfFire"] = {[-1] = 5},
["Spell_Nature_GuardianWard"] = {[-1] = 120},
["Spell_FrostResistanceTotem_01"] = {[-1] = 120},
["Spell_Fire_SelfDestruct"] = {[-1] = 20},
["Spell_Fire_SearingTotem"] = {[-1] = 55,[1] = 30,[2] = 35,[3] = 40,[4] = 45,[5] = 50,[6] = 55},
},
[EARTH_TOTEM_SLOT] = {
["Spell_Nature_StrengthOfEarthTotem02"] = {[-1] = 45},
["Spell_Nature_StoneClawTotem"] = {[-1] = 15},
["Spell_Nature_StoneSkinTotem"] = {[-1] = 120},
["Spell_Nature_EarthBindTotem"] = {[-1] = 120},
["Spell_Nature_TremorTotem"] = {[-1] = 120},
},
[WATER_TOTEM_SLOT] = {
["Spell_Nature_DiseaseCleansingTotem"] = {[-1] = 120},
["Spell_FireResistanceTotem_01"] = {[-1] = 120},
["INV_Spear_04"] = {[-1] = 60},
["Spell_Nature_ManaRegenTotem"] = {[-1] = 60},
["Spell_Frost_SummonWaterElemental"] = {[-1] = 12},
["Spell_Nature_PoisonCleansingTotem"] = {[-1] = 120},
},
[AIR_TOTEM_SLOT] = {
["Spell_Nature_InvisibilityTotem"] = {[-1] = 120},
["Spell_Nature_GroundingTotem"] = {[-1] = 45},
["Spell_Nature_NatureResistanceTotem"] = {[-1] = 120},
["Spell_Nature_Brilliance"] = {[-1] = 120},
["Spell_Nature_Windfury"] = {[-1] = 120},
["Spell_Nature_EarthBind"] = {[-1] = 120},
},
}
GetTotemInfo = function(id)
if not active[id] or not active[id].name then return end
if active[id].start + active[id].duration - GetTime() < 0 then
libtotem:Clean(id)
return nil
end
return 1, active[id].name, active[id].start, active[id].duration, active[id].icon
end
if class ~= "SHAMAN" then return end
libtotem = CreateFrame("Frame")
libtotem:RegisterEvent("PLAYER_DEAD")
libtotem:SetScript("OnEvent", function()
if event == "PLAYER_DEAD" then
for i = 1, 4 do libtotem:Clean(i) end
end
end)
libtotem.totems = totems
libtotem.Clean = function(self, slot)
active[slot].name = nil
active[slot].start = nil
active[slot].duration = nil
active[slot].icon = nil
end
-- Direct SpellID commit (Nampower SPELL_GO_SELF, most accurate)
libtotem.CommitBySpellId = function(spellId, icon)
local data = spellids[spellId]
if not data then return false end
local slot = data.slot
active[slot].name = active[slot].pending_name or active[slot].name
active[slot].duration = data.duration
active[slot].icon = icon or active[slot].pending_icon
active[slot].start = GetTime()
active[slot].pending_name = nil
active[slot].pending_icon = nil
return true
end
-- Fallback: icon-based lookup (for CastSpell/UseAction without spellId)
libtotem.CheckAddQueue = function(self, name, rank, icon, spellId)
-- if we have a spellId, just store the name/icon as pending for SPELL_GO
if spellId and spellids[spellId] then
local slot = spellids[spellId].slot
active[slot].pending_name = name
active[slot].pending_icon = icon
return true
end
-- icon-based fallback
for slot = 1, 4 do
for texture, data in pairs(totems[slot]) do
if string.find(icon, texture, 1) then
if rank then
_, _, rank = string.find(rank, "%s(%d+)")
end
local duration
if rank and tonumber(rank) and data[tonumber(rank)] then
duration = data[tonumber(rank)]
else
duration = data[-1]
end
active[slot].pending_name = name
active[slot].pending_icon = icon
active[slot].pending_duration = duration
return true
end
end
end
return nil
end
-- assign library to global space
pfUI.api.libtotem = libtotem
-- SPELL_GO_SELF from libdebuff: commit directly by SpellID, no queue needed
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
pfUI.libdebuff_spell_go_hooks["libtotem"] = function(spellId)
if not spellId then return end
local data = spellids[spellId]
if not data then return end
local slot = data.slot
-- use pending name/icon if available (set by CastSpellByName hook), else GetSpellInfo
local name = active[slot].pending_name
local icon = active[slot].pending_icon
if not name and GetSpellInfo then
name = GetSpellInfo(spellId)
end
active[slot].name = name
active[slot].duration = data.duration
active[slot].icon = icon
active[slot].start = GetTime()
active[slot].pending_name = nil
active[slot].pending_icon = nil
active[slot].pending_duration = nil
end
-- Hook CastSpellByName to store pending name/icon per slot
hooksecurefunc("CastSpellByName", function(effect, target)
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(effect)
if not name then return end
libtotem:CheckAddQueue(name, rank, icon, spellId)
end)
-- Hook CastSpell to store pending name/icon per slot
hooksecurefunc("CastSpell", function(id, bookType)
if not id or not bookType then return end
if bookType ~= BOOKTYPE_SPELL and bookType ~= BOOKTYPE_PET then return end
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(id, bookType)
if not name then return end
libtotem:CheckAddQueue(name, rank, icon, spellId)
end)
-- Hook UseAction. GetActionInfo + GetMacroSpell give us the spellID
-- directly for both spell-action and macro-action slots, so the
-- tooltip-scan fallback (and the "no spellId available" caveat) goes away.
hooksecurefunc("UseAction", function(slot, target, selfcast)
if not IsCurrentAction(slot) then return end
local kind, id = GetActionInfo(slot)
local name, rank, spellID
if kind == "spell" then
spellID = id
name, rank = GetSpellInfo(id)
elseif kind == "macro" then
name, rank, spellID = GetMacroSpell(id)
end
if not name then return end
libtotem:CheckAddQueue(name, rank, GetActionTexture(slot), spellID)
end)
+34 -21
View File
@@ -335,8 +335,7 @@ pfUI:RegisterModule("actionbar", function ()
local slfcast = C.bars.altself == "1" and IsAltKeyDown() and true or self.slfcast local slfcast = C.bars.altself == "1" and IsAltKeyDown() and true or self.slfcast
slfcast = C.bars.rightself == "1" and arg1 and arg1 == "RightButton" and true or slfcast slfcast = C.bars.rightself == "1" and arg1 and arg1 == "RightButton" and true or slfcast
self.slfcast = nil self.slfcast = nil
if ( C.bars.keydown == "1" and keystate == "down" and not drag_active ) or (C.bars.keydown == "0" and keystate == "up" or drag_active ) or self.bar == 11 or mouse then
if ( pfUI_config.bars.keydown == "1" and keystate == "down" and not drag_active ) or (pfUI_config.bars.keydown == "0" and keystate == "up" or drag_active ) or self.bar == 11 or mouse then
if self.bar == 11 then if self.bar == 11 then
CastShapeshiftForm(self.id) CastShapeshiftForm(self.id)
elseif grid == 1 then elseif grid == 1 then
@@ -854,16 +853,20 @@ pfUI:RegisterModule("actionbar", function ()
end end
end end
local cat, stealth
local inCatForm = nil local inCatForm = nil
local prowlActive = nil local prowlActive = nil
-- Cat Form ID per vanilla 1.12 SpellShapeshiftForm.dbc. -- Form IDs per vanilla 1.12 SpellShapeshiftForm.dbc.
local CAT_FORM = 1 local CAT_FORM = 1
local SHADOWFORM = 28
local function HasCatForm() local function HasCatForm()
return GetShapeshiftFormID() == CAT_FORM and true or nil return GetShapeshiftFormID() == CAT_FORM and true or nil
end end
local function InShadowform()
return GetShapeshiftFormID() == SHADOWFORM and true or nil
end
local function FullScan() local function FullScan()
if class ~= "DRUID" then return nil end if class ~= "DRUID" then return nil end
inCatForm = HasCatForm() inCatForm = HasCatForm()
@@ -873,7 +876,7 @@ pfUI:RegisterModule("actionbar", function ()
-- pagemaster / meta page switch -- pagemaster / meta page switch
do do
local prowl, shift, ctrl, alt, default = 8, 6, 5, 3, 1 local formpage, shift, ctrl, alt, default = 8, 6, 5, 3, 1
-- set temporary pagemaster bindings keybinds -- set temporary pagemaster bindings keybinds
if C.bars.pagemaster == "1" then if C.bars.pagemaster == "1" then
@@ -891,8 +894,10 @@ pfUI:RegisterModule("actionbar", function ()
end) end)
end end
-- setup page switch frame -- setup page switch frame. `formpaging` is the shared "auto-page is
local prowling = nil -- active" flag; druid prowl and priest shadowform each drive it, and
-- no character is ever both, so one flag covers both features.
local formpaging = nil
local pageswitch = CreateFrame("Frame", "pfActionBarPageSwitch", UIParent) local pageswitch = CreateFrame("Frame", "pfActionBarPageSwitch", UIParent)
pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD") pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD")
pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM") pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
@@ -906,10 +911,17 @@ pfUI:RegisterModule("actionbar", function ()
return return
end end
if class == "PRIEST" then
if event == "UPDATE_SHAPESHIFT_FORM" or event == "PLAYER_ENTERING_WORLD" then
formpaging = InShadowform()
end
return
end
if class ~= "DRUID" then return end if class ~= "DRUID" then return end
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
prowling = FullScan() formpaging = FullScan()
return return
end end
@@ -918,7 +930,7 @@ pfUI:RegisterModule("actionbar", function ()
inCatForm = HasCatForm() inCatForm = HasCatForm()
if not inCatForm then if not inCatForm then
prowlActive = nil prowlActive = nil
prowling = nil formpaging = nil
end end
return return
end end
@@ -928,10 +940,10 @@ pfUI:RegisterModule("actionbar", function ()
if inCatForm then if inCatForm then
if IsStealthed() then if IsStealthed() then
prowlActive = true prowlActive = true
prowling = true formpaging = true
else else
prowlActive = nil prowlActive = nil
prowling = nil formpaging = nil
end end
end end
end) end)
@@ -947,7 +959,7 @@ pfUI:RegisterModule("actionbar", function ()
if PROWL_IDS[spellId] then if PROWL_IDS[spellId] then
inCatForm = true inCatForm = true
prowlActive = true prowlActive = true
prowling = true formpaging = true
end end
end end
pageswitch:SetScript("OnUpdate", function() pageswitch:SetScript("OnUpdate", function()
@@ -965,11 +977,12 @@ pfUI:RegisterModule("actionbar", function ()
SwitchBar(default) SwitchBar(default)
end end
-- switch actionbar page if druid stealth is detected -- switch actionbar page while druid stealth / priest shadowform is active
if C.bars.druidstealth == "1" then if (class == "DRUID" and C.bars.druidstealth == "1")
if prowling and _G.CURRENT_ACTIONBAR_PAGE == 1 then or (class == "PRIEST" and C.bars.priestshadow == "1") then
SwitchBar(prowl) if formpaging and _G.CURRENT_ACTIONBAR_PAGE == 1 then
elseif not prowling and _G.CURRENT_ACTIONBAR_PAGE == 8 then SwitchBar(formpage)
elseif not formpaging and _G.CURRENT_ACTIONBAR_PAGE == 8 then
SwitchBar(default) SwitchBar(default)
end end
end end
@@ -1013,10 +1026,8 @@ pfUI:RegisterModule("actionbar", function ()
-- no button available, create a new one -- no button available, create a new one
if not exists then if not exists then
-- prepare the button for vanilla -- prepare the button for vanilla
if not f.HookScript then if not f.HookScript then f.HookScript = HookScript end
f.HookScript = HookScript f:SetScript("OnClick", ButtonClick)
f:SetScript("OnClick", ButtonClick)
end
if bar ~= 11 then if bar ~= 11 then
f:RegisterForDrag("LeftButton", "RightButton") f:RegisterForDrag("LeftButton", "RightButton")
@@ -1358,6 +1369,7 @@ pfUI:RegisterModule("actionbar", function ()
-- make stance bar dodge by default -- make stance bar dodge by default
bars[i]:SetScript("OnShow", function() bars[i]:SetScript("OnShow", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints() bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border) bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
@@ -1367,6 +1379,7 @@ pfUI:RegisterModule("actionbar", function ()
-- restore old stance bar position -- restore old stance bar position
bars[i]:SetScript("OnHide", function() bars[i]:SetScript("OnHide", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints() bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border) bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
+1 -2
View File
@@ -440,6 +440,5 @@ pfUI:RegisterModule("addonbuttons", function ()
pfUI.addonbuttons:UpdateConfig() pfUI.addonbuttons:UpdateConfig()
_G.SLASH_PFABP1, _G.SLASH_PFABP2 = "/abp", "/pfabp" pfUI.api.RegisterSlashCommand("PFABP", { "/abp", "/pfabp" }, ManualAddOrRemove, true)
_G.SlashCmdList.PFABP = ManualAddOrRemove
end) end)
+4 -14
View File
@@ -134,19 +134,9 @@ pfUI:RegisterModule("addoncompat", function ()
end end
-- run the addonconflict queue when firstrun is ready -- run the addonconflict queue when firstrun is ready
local delay = CreateFrame("Frame") if pfUI.firstrun and pfUI.firstrun.completed then
delay:SetScript("OnUpdate", function()
-- throttle to to one query per .1 second
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
-- make sure the firstrun dialog has finished
if pfUI.firstrun and pfUI.firstrun.steps then
for _, step in pairs(pfUI.firstrun.steps) do
if not pfUI_init[step.name] then return end
end
end
RunQueue() RunQueue()
this:SetScript("OnUpdate", nil) else
end) pfUI.events:RegisterCallback("firstrun:complete", RunQueue, "addoncompat")
end
end) end)
+19
View File
@@ -217,6 +217,21 @@ pfUI:RegisterModule("addons", function ()
pfUI.addons.list:RegisterEvent("PLAYER_ENTERING_WORLD") pfUI.addons.list:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.addons.list:SetHeight(GetNumAddOns() * 25 + 26) pfUI.addons.list:SetHeight(GetNumAddOns() * 25 + 26)
local function AddDependencyLines(header, deps)
if not deps or table.getn(deps) == 0 then return end
GameTooltip:AddLine(" ")
GameTooltip:AddLine(header .. ":", .2, 1, .8)
for _, dep in ipairs(deps) do
if IsAddOnLoaded(dep) then
GameTooltip:AddLine(" " .. dep, .5, 1, .5)
elseif C_AddOns.DoesAddOnExist(dep) then
GameTooltip:AddLine(" " .. dep, 1, .82, 0)
else
GameTooltip:AddLine(" " .. dep .. " (" .. T["Missing"] .. ")", 1, .4, .4)
end
end
end
local function AddonOnEnter() local function AddonOnEnter()
this:SetBackdropBorderColor(1,1,1,.08) this:SetBackdropBorderColor(1,1,1,.08)
@@ -231,6 +246,8 @@ pfUI:RegisterModule("addons", function ()
end end
GameTooltip:AddLine(this.anote, .75,.75,.75,1) GameTooltip:AddLine(this.anote, .75,.75,.75,1)
AddDependencyLines(T["Dependencies"], this.adeps)
AddDependencyLines(T["Optional Dependencies"], this.aoptdeps)
GameTooltip:SetWidth(180) GameTooltip:SetWidth(180)
GameTooltip:Show() GameTooltip:Show()
end end
@@ -272,6 +289,8 @@ pfUI:RegisterModule("addons", function ()
frame.anote = anote frame.anote = anote
frame.aauthor = aauthor frame.aauthor = aauthor
frame.aversion = aversion frame.aversion = aversion
frame.adeps = { GetAddOnDependencies(i) } -- required (.toc Dependencies)
frame.aoptdeps = { C_AddOns.GetAddOnOptionalDependencies(i) } -- optional (.toc OptionalDeps)
frame:SetWidth(340) frame:SetWidth(340)
frame:SetHeight(25) frame:SetHeight(25)
+4 -5
View File
@@ -1,5 +1,4 @@
pfUI:RegisterModule("afkcam", function () pfUI:RegisterModule("afkcam", function ()
local MARKED_AFK_CAPTURE = SanitizePattern(MARKED_AFK_MESSAGE)
local social_chats = { local social_chats = {
"CHAT_MSG_SAY", "CHAT_MSG_SAY",
"CHAT_MSG_WHISPER", "CHAT_MSG_WHISPER",
@@ -165,10 +164,10 @@ pfUI:RegisterModule("afkcam", function ()
end) end)
afkcam:SetScript("OnEvent", function() afkcam:SetScript("OnEvent", function()
if event == "CHAT_MSG_SYSTEM" then if event == "PLAYER_FLAGS_CHANGED" then
if (arg1 == _G.MARKED_AFK) or strfind(arg1, MARKED_AFK_CAPTURE) then if UnitIsAFK('player') then
delay:Show() delay:Show()
elseif (arg1 == _G.CLEARED_AFK) then elseif delay:IsVisible() then
delay:Hide() delay:Hide()
this:stop() this:stop()
end end
@@ -180,7 +179,7 @@ pfUI:RegisterModule("afkcam", function ()
end end
end) end)
afkcam:RegisterEvent("CHAT_MSG_SYSTEM") afkcam:RegisterEvent("PLAYER_FLAGS_CHANGED")
afkcam:RegisterEvent("PLAYER_REGEN_DISABLED") afkcam:RegisterEvent("PLAYER_REGEN_DISABLED")
afkcam:RegisterEvent("PLAYER_LEAVING_WORLD") -- reseting cvars on PLAYER_LOGOUT crashes the client ¯\_(ツ)_/¯ afkcam:RegisterEvent("PLAYER_LEAVING_WORLD") -- reseting cvars on PLAYER_LOGOUT crashes the client ¯\_(ツ)_/¯
end) end)
+1 -6
View File
@@ -19,12 +19,7 @@ pfUI:RegisterModule("autovendor", function ()
local startGold = GetMoney() local startGold = GetMoney()
C_MerchantFrame.SellAllJunkItems() C_MerchantFrame.SellAllJunkItems()
local reporter = CreateFrame("Frame") C_Timer.After(0.3, function()
reporter.deadline = GetTime() + 0.3
reporter:SetScript("OnUpdate", function()
if GetTime() < this.deadline then return end
this:SetScript("OnUpdate", nil)
this:Hide()
local income = GetMoney() - startGold local income = GetMoney() - startGold
if income > 0 then if income > 0 then
DEFAULT_CHAT_FRAME:AddMessage(T["Your vendor trash has been sold and you earned"] .. " " .. CreateGoldString(income)) DEFAULT_CHAT_FRAME:AddMessage(T["Your vendor trash has been sold and you earned"] .. " " .. CreateGoldString(income))
+5
View File
@@ -337,6 +337,7 @@ pfUI:RegisterModule("bags", function ()
local chat = pfUI.chat and ( object == "bank" and pfUI.chat.left or pfUI.chat.right) or nil local chat = pfUI.chat and ( object == "bank" and pfUI.chat.left or pfUI.chat.right) or nil
frame:SetScript("OnShow", function() frame:SetScript("OnShow", function()
frame.opened = true
if C.appearance.bags.hidechat == "1" and chat and chat:IsVisible() then if C.appearance.bags.hidechat == "1" and chat and chat:IsVisible() then
frame.chatWasOpen = true frame.chatWasOpen = true
chat:Hide() chat:Hide()
@@ -355,6 +356,10 @@ pfUI:RegisterModule("bags", function ()
end end
pfUI.bag:CreateBags(object) pfUI.bag:CreateBags(object)
PlaySound("INTERFACESOUND_BACKPACKCLOSE") PlaySound("INTERFACESOUND_BACKPACKCLOSE")
if frame.opened then
frame.opened = nil
pfUI.events:TriggerEvent("bag:closed", object)
end
end) end)
end end
+3 -3
View File
@@ -12,7 +12,9 @@ pfUI:RegisterModule("bubbles", function ()
pfUI.bubbles:RegisterEvent("CHAT_MSG_MONSTER_PARTY") pfUI.bubbles:RegisterEvent("CHAT_MSG_MONSTER_PARTY")
pfUI.bubbles:SetScript("OnEvent", function() pfUI.bubbles:SetScript("OnEvent", function()
pfUI.bubbles:SetScript("OnUpdate", pfUI.bubbles.ScanBubbles) -- Bubble frames are attached to WorldFrame after the chat event fires,
-- so wait one tick before scanning.
RunNextFrame(function() pfUI.bubbles:ScanBubbles() end)
end) end)
function pfUI.bubbles:IsBubble(f) function pfUI.bubbles:IsBubble(f)
@@ -66,7 +68,5 @@ pfUI:RegisterModule("bubbles", function ()
end) end)
end end
end end
pfUI.bubbles:SetScript("OnUpdate", nil)
end end
end) end)
+4 -4
View File
@@ -60,6 +60,7 @@ pfUI:RegisterModule("buff", function ()
buff.mode = buff.btype buff.mode = buff.btype
buff.expirationTime = aura.expirationTime buff.expirationTime = aura.expirationTime
buff.stackCount = aura.applications buff.stackCount = aura.applications
buff.spellId = aura.spellId
buff.texture:SetTexture(aura.icon) buff.texture:SetTexture(aura.icon)
if buff.btype == "HARMFUL" then if buff.btype == "HARMFUL" then
@@ -146,9 +147,8 @@ pfUI:RegisterModule("buff", function ()
CancelItemTempEnchantment(1) CancelItemTempEnchantment(1)
elseif CancelItemTempEnchantment and this.mode and this.mode == "OFFHAND" then elseif CancelItemTempEnchantment and this.mode and this.mode == "OFFHAND" then
CancelItemTempEnchantment(2) CancelItemTempEnchantment(2)
else elseif this.spellId then
local bid = GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, this.btype) C_Spell.CancelSpellByID(this.spellId)
if bid >= 0 then CancelPlayerBuff(bid) end
end end
end) end)
@@ -159,7 +159,7 @@ pfUI:RegisterModule("buff", function ()
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent) pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED") pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED")
pfUI.buff:RegisterEvent("UNIT_INVENTORY_CHANGED") pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
pfUI.buff:RegisterEvent("UNIT_MODEL_CHANGED") pfUI.buff:RegisterEvent("UNIT_MODEL_CHANGED")
pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF") pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF")
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF") pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
+2 -2
View File
@@ -99,8 +99,8 @@ pfUI:RegisterModule("buffwatch", function ()
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now blacklisted."]) DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now blacklisted."])
end end
elseif this.parent.unit == "player" then elseif this.parent.unit == "player" then
local bid = GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, this.type) local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, this.type)
if bid >= 0 then CancelPlayerBuff(bid) end if aura and aura.spellId then C_Spell.CancelSpellByID(aura.spellId) end
end end
end end
+11 -3
View File
@@ -69,6 +69,11 @@ pfUI:RegisterModule("castbar", function ()
-- spark to the left edge of the bar. -- spark to the left edge of the bar.
local function StartTradeskillCraft(cb) local function StartTradeskillCraft(cb)
cb.currentCraftStart = GetTime() * 1000 cb.currentCraftStart = GetTime() * 1000
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
cb.endTime = cb.currentCraftStart + cb.tradeskillSingleMs * remaining
local duration = (cb.endTime - cb.startTime) / 1000
cb.bar:SetMinMaxValues(0, duration)
cb.lastMax = duration
UpdateTradeskillLabel(cb) UpdateTradeskillLabel(cb)
end end
@@ -193,7 +198,7 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.left:SetFontObject(GameFontWhite) cb.bar.left:SetFontObject(GameFontWhite)
cb.bar.left:SetTextColor(1,1,1,1) cb.bar.left:SetTextColor(1,1,1,1)
cb.bar.left:SetFont(font, font_size, "OUTLINE") cb.bar.left:SetFont(font, font_size, "OUTLINE")
cb.bar.left:SetJustifyH("left") cb.bar.left:SetJustifyH(C.castbar[unitstr].namealign or "LEFT")
-- text right -- text right
cb.bar.right = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal") cb.bar.right = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
@@ -204,7 +209,7 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.right:SetFontObject(GameFontWhite) cb.bar.right:SetFontObject(GameFontWhite)
cb.bar.right:SetTextColor(1,1,1,1) cb.bar.right:SetTextColor(1,1,1,1)
cb.bar.right:SetFont(font, font_size, "OUTLINE") cb.bar.right:SetFont(font, font_size, "OUTLINE")
cb.bar.right:SetJustifyH("right") cb.bar.right:SetJustifyH(C.castbar[unitstr].timealign or "RIGHT")
cb.bar.lag = cb.bar:CreateTexture(nil, "OVERLAY") cb.bar.lag = cb.bar:CreateTexture(nil, "OVERLAY")
cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0) cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0)
@@ -241,7 +246,10 @@ pfUI:RegisterModule("castbar", function ()
return return
end end
if not this.endTime then return end if not this.endTime then
if this:GetAlpha() ~= 0 then this:SetAlpha(0) end
return
end
-- Non-player bars: if the unit disappears (target died / detarget), -- Non-player bars: if the unit disappears (target died / detarget),
-- drop the bar immediately. -- drop the bar immediately.
+6 -7
View File
@@ -8,9 +8,9 @@ pfUI:RegisterModule("easteregg", function ()
local pvpking = CreateFrame("Frame", "pfPvPKing", UIParent) local pvpking = CreateFrame("Frame", "pfPvPKing", UIParent)
pvpking:Hide() pvpking:Hide()
pvpking:RegisterEvent("CHAT_MSG_SYSTEM") pvpking:RegisterEvent("PLAYER_FLAGS_CHANGED")
pvpking:SetScript("OnEvent", function() pvpking:SetScript("OnEvent", function()
if strfind(arg1, "You are now", 1) and strfind(arg1, "(AFK)", 1) then if UnitIsAFK('player') then
_G.CHAT_FLAG_AFK = title .. " " _G.CHAT_FLAG_AFK = title .. " "
this.time = GetTime() this.time = GetTime()
this:Show() this:Show()
@@ -61,14 +61,13 @@ pfUI:RegisterModule("easteregg", function ()
end) end)
-- trigger fireworks when being AFK -- trigger fireworks when being AFK
fireworks:RegisterEvent("CHAT_MSG_SYSTEM") fireworks:RegisterEvent("PLAYER_FLAGS_CHANGED")
fireworks:SetScript("OnEvent", function() fireworks:SetScript("OnEvent", function()
if strfind(arg1, _G.MARKED_AFK) or strfind(arg1, _G.MARKED_AFK_MESSAGE) then local isAFK = UnitIsAFK('player')
if isAFK then
this:SetAlpha(0) this:SetAlpha(0)
this:Show()
elseif strfind(arg1, _G.CLEARED_AFK) then
this:Hide()
end end
this:SetShown(isAFK)
end) end)
-- basic explosion animation -- basic explosion animation
+125 -220
View File
@@ -1,243 +1,148 @@
pfUI:RegisterModule("eqcompare", function () pfUI:RegisterModule("eqcompare", function ()
local sides = { "Left", "Right" } pfUI.eqcompare = {}
local loc = pfUI.cache["locale"]
for key, value in pairs(L["itemtypes"]) do setglobal(key, value) end
INVTYPE_WEAPON_OTHER = INVTYPE_WEAPON.."_other"
INVTYPE_FINGER_OTHER = INVTYPE_FINGER.."_other"
INVTYPE_TRINKET_OTHER = INVTYPE_TRINKET.."_other"
local function AddHeader(tooltip) local function ShowCompareItem(self, link, shift)
local name = tooltip:GetName() self = self or GameTooltip
shift = shift or IsShiftKeyDown()
-- shift all entries one line down if not link or (not shift and (C.tooltip.compare.showalways ~= "1" or C_Item.IsEquippedItem(link))) then
for i=tooltip:NumLines(), 1, -1 do return
for _, side in pairs(sides) do end
local current = _G[name.."Text"..side..i]
local below = _G[name.."Text"..side..i+1]
if current and current:IsShown() then local shoppingTooltip1, shoppingTooltip2 = unpack(self.shoppingTooltips or { ShoppingTooltip1, ShoppingTooltip2 });
local text = current:GetText()
local r, g, b = current:GetTextColor()
if text and text ~= "" then local SEPARATION = 6;
if tooltip:NumLines() < i+1 then local backdrop = shoppingTooltip1.GetBackdrop and shoppingTooltip1:GetBackdrop();
-- add new line if required local GAP = SEPARATION + ((type(backdrop) == "table" and backdrop.edgeSize) or 0);
tooltip:AddLine(text, r, g, b, true)
else
-- update existing lines
below:SetText(text)
below:SetTextColor(r, g, b)
below:Show()
-- hide processed line local item1 = nil;
current:Hide() local item2 = nil;
end local side = "left";
end if ( shoppingTooltip1:SetHyperlinkCompareItem(link, 1, shift, self) ) then
end item1 = true;
end
if ( shoppingTooltip2:SetHyperlinkCompareItem(link, 2, shift, self) ) then
item2 = true;
end
-- find correct side
local rightDist = 0;
local leftPos = self:GetLeft();
local rightPos = self:GetRight();
if ( not rightPos ) then
rightPos = 0;
end
if ( not leftPos ) then
leftPos = 0;
end
rightDist = GetScreenWidth() - rightPos;
if (leftPos and (rightDist < leftPos)) then
side = "left";
else
side = "right";
end
-- see if we should slide the tooltip
if ( self:GetAnchorType() and self:GetAnchorType() ~= "ANCHOR_PRESERVE" ) then
local totalWidth = 0;
if ( item1 ) then
totalWidth = totalWidth + shoppingTooltip1:GetWidth();
end
if ( item2 ) then
totalWidth = totalWidth + shoppingTooltip2:GetWidth();
end
if ( (side == "left") and (totalWidth > leftPos) ) then
self:SetAnchorType(self:GetAnchorType(), (totalWidth - leftPos), 0);
elseif ( (side == "right") and (rightPos + totalWidth) > GetScreenWidth() ) then
self:SetAnchorType(self:GetAnchorType(), -((rightPos + totalWidth) - GetScreenWidth()), 0);
end end
end end
-- add label to first line if ( item1 ) then
_G[name.."TextLeft1"]:SetTextColor(.5, .5, .5, 1) shoppingTooltip1:SetOwner(self, "ANCHOR_NONE");
_G[name.."TextLeft1"]:SetText(CURRENTLY_EQUIPPED) shoppingTooltip1:ClearAllPoints();
_G[name.."TextLeft1"]:Show() if ( side and side == "left" ) then
shoppingTooltip1:SetPoint("TOPRIGHT", self, "TOPLEFT", -GAP, -10);
else
shoppingTooltip1:SetPoint("TOPLEFT", self, "TOPRIGHT", GAP, -10);
end
shoppingTooltip1:SetHyperlinkCompareItem(link, 1, shift, self);
shoppingTooltip1:Show();
-- update tooltip sizes if ( item2 ) then
tooltip:Show() shoppingTooltip2:SetOwner(shoppingTooltip1, "ANCHOR_NONE");
shoppingTooltip2:ClearAllPoints();
if ( side and side == "left" ) then
shoppingTooltip2:SetPoint("TOPRIGHT", shoppingTooltip1, "TOPLEFT", -GAP, 0);
else
shoppingTooltip2:SetPoint("TOPLEFT", shoppingTooltip1, "TOPRIGHT", GAP, 0);
end
shoppingTooltip2:SetHyperlinkCompareItem(link, 2, shift, self);
shoppingTooltip2:Show();
end
end
end end
local slotTable = { local prevMerchant = ShoppingTooltip1.SetMerchantCompareItem
[INVTYPE_2HWEAPON] = "MainHandSlot", local function SetMerchantCompareItem(self, index, compareItem)
[INVTYPE_BODY] = "ShirtSlot", if compareItem == 1 then
[INVTYPE_CHEST] = "ChestSlot", ShowCompareItem(nil, GetMerchantItemLink(index), 1)
[INVTYPE_CLOAK] = "BackSlot", return false
[INVTYPE_FEET] = "FeetSlot", end
[INVTYPE_FINGER] = "Finger0Slot", return prevMerchant and prevMerchant(self, index, compareItem)
[INVTYPE_FINGER_OTHER] = "Finger1Slot", end
[INVTYPE_HAND] = "HandsSlot",
[INVTYPE_HEAD] = "HeadSlot",
[INVTYPE_HOLDABLE] = "SecondaryHandSlot",
[INVTYPE_LEGS] = "LegsSlot",
[INVTYPE_NECK] = "NeckSlot",
[INVTYPE_RANGED] = "RangedSlot",
[INVTYPE_RELIC] = "RangedSlot",
[INVTYPE_ROBE] = "ChestSlot",
[INVTYPE_SHIELD] = "SecondaryHandSlot",
[INVTYPE_SHOULDER] = "ShoulderSlot",
[INVTYPE_TABARD] = "TabardSlot",
[INVTYPE_TRINKET] = "Trinket0Slot",
[INVTYPE_TRINKET_OTHER] = "Trinket1Slot",
[INVTYPE_WAIST] = "WaistSlot",
[INVTYPE_WEAPON] = "MainHandSlot",
[INVTYPE_WEAPON_OTHER] = "SecondaryHandSlot",
[INVTYPE_WEAPONMAINHAND] = "MainHandSlot",
[INVTYPE_WEAPONOFFHAND] = "SecondaryHandSlot",
[INVTYPE_WRIST] = "WristSlot",
[INVTYPE_WAND] = "RangedSlot", local prevAuction = ShoppingTooltip1.SetAuctionCompareItem
[INVTYPE_GUN] = "RangedSlot", local function SetAuctionCompareItem(self, type, index, compareItem)
[INVTYPE_PROJECTILE] = "AmmoSlot", if compareItem == 1 then
[INVTYPE_CROSSBOW] = "RangedSlot", ShowCompareItem(nil, GetAuctionItemLink(type, index), 1)
[INVTYPE_THROWN] = "RangedSlot", return false
end
return prevAuction and prevAuction(self, type, index, compareItem)
end
ShoppingTooltip1.SetMerchantCompareItem = SetMerchantCompareItem
ShoppingTooltip2.SetMerchantCompareItem = SetMerchantCompareItem
ShoppingTooltip1.SetAuctionCompareItem = SetAuctionCompareItem
ShoppingTooltip2.SetAuctionCompareItem = SetAuctionCompareItem
local TooltipHooks = {
SetLootRollItem = GetLootRollItemLink,
SetLootItem = GetLootSlotLink,
SetQuestLogItem = GetQuestLogItemLink,
SetQuestItem = GetQuestItemLink,
SetHyperlink = function(link) return link end,
SetBagItem = GetContainerItemLink,
SetInboxItem = GetInboxItemLink,
SetInventoryItem = GetInventoryItemLink,
SetTradeSkillItem = function(skillIndex, reagentIndex)
if reagentIndex then
return GetTradeSkillReagentItemLink(skillIndex, reagentIndex)
else
return GetTradeSkillItemLink(skillIndex)
end
end,
SetAuctionSellItem = GetAuctionSellItemLink,
SetTradePlayerItem = GetTradePlayerItemLink,
SetTradeTargetItem = GetTradeTargetItemLink
} }
local function startsWith(str, start) local function makeHook(getter)
return string.sub(str, 1, string.len(start)) == start return function(tooltip, arg1, arg2, arg3)
end ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
local function ExtractAttributes(tooltip)
local name = tooltip:GetName()
-- get the name/header of the last set comparison tooltip
local comparetooltip = pfUI.eqcompare.tooltip:GetName()
local iname = _G[comparetooltip .. "TextLeft1"] and _G[comparetooltip .. "TextLeft1"]:GetText()
-- only run once per item
if tooltip.pfCompLastName == iname then return end
tooltip.pfCompData = {}
tooltip.pfCompLastName = iname
for i=1,30 do
local widget = _G[name.."TextLeft"..i]
if widget and widget:GetObjectType() == "FontString" then
local text = widget:GetText()
if text and not string.find(text, "-", 1, true) then
local start = 1
if startsWith(text, "\+") or startsWith(text, "\(") then start = 2 end
local space = string.find(text, " ", 1, true)
if space then
local value = tonumber(string.sub(text, start, space-1))
if value and text then
-- we've found an attr
local attr = string.sub(text, space, string.len(text))
tooltip.pfCompData[attr] = { value = tonumber(value), widget = widget }
end
end
end
end
end end
end end
local function CompareAttributes(data, targetData) local function HookTooltip(tooltip)
if not data then return end for setter, getter in pairs(TooltipHooks) do
hooksecurefunc(tooltip, setter, makeHook(getter))
for attr,v in pairs(data) do
if targetData then
local target = targetData[attr]
if target then
if v.value ~= target.value and v.widget:GetText() then
if v.value > target.value then
if not strfind(v.widget:GetText(), "|cff88ff88") and not strfind(v.widget:GetText(), "|cffff8888") then
v.widget:SetText(v.widget:GetText() .. "|cff88ff88 (+" .. round(v.value - target.value, 1) .. ")")
end
elseif not v.widget.compSet then
if not strfind(v.widget:GetText(), "|cff88ff88") and not strfind(v.widget:GetText(), "|cffff8888") then
v.widget:SetText(v.widget:GetText() .. "|cffff8888 (-" .. round(target.value - v.value, 1) .. ")")
end
end
target.processed = true
else
target.processed = true
end
else
-- this attribute doesnt exist in target
if v.widget and v.widget:GetText() then
if not strfind(v.widget:GetText(), "|cff88ff88") and not strfind(v.widget:GetText(), "|cffff8888") then
v.widget:SetText(v.widget:GetText() .. "|cff88ff88 (+" .. v.value .. ")")
end
end
end
end
end
for _,target in pairs(targetData) do
if target and not target.processed then
-- we are an extra value
local text = target.widget:GetText()
if text and not strfind(text, "|cff88ff88") and not strfind(text, "|cffff8888") then
target.widget:SetText(text .. "|cff88ff88 (+" .. target.value .. ")")
end
end
end end
end end
pfUI.eqcompare = {} HookTooltip(GameTooltip)
pfUI.eqcompare.GameTooltipShow = function()
-- use this tooltip for the next comparison
pfUI.eqcompare.tooltip = this
if not IsShiftKeyDown() and C.tooltip.compare.showalways ~= "1" then return end pfUI.eqcompare.HookTooltip = HookTooltip
local rawborder, border = GetBorderSize()
for i=1,this:NumLines() do
local tmpText = _G[this:GetName() .. "TextLeft"..i]
for slotType, slotName in pairs(slotTable) do
if tmpText:GetText() == slotType then
local slotID = GetInventorySlotInfo(slotTable[slotType])
-- determine screen part
local ltrigger = GetScreenWidth() / 2
local x = GetCursorPosition()
x = x / UIParent:GetEffectiveScale()
if x > ltrigger then ltrigger = nil end
-- first tooltip
ShoppingTooltip1:SetOwner(this, "ANCHOR_NONE")
ShoppingTooltip1:ClearAllPoints()
if ltrigger then
ShoppingTooltip1:SetPoint("BOTTOMLEFT", this, "BOTTOMRIGHT", 0, 0)
else
ShoppingTooltip1:SetPoint("BOTTOMRIGHT", this, "BOTTOMLEFT", -border*2-1, 0)
end
ShoppingTooltip1:SetInventoryItem("player", slotID)
ShoppingTooltip1:Show()
AddHeader(ShoppingTooltip1)
-- second tooltip
if slotTable[slotType .. "_other"] then
local slotID_other = GetInventorySlotInfo(slotTable[slotType .. "_other"])
ShoppingTooltip2:SetOwner(this, "ANCHOR_NONE")
ShoppingTooltip2:ClearAllPoints()
if ltrigger then
ShoppingTooltip2:SetPoint("BOTTOMLEFT", ShoppingTooltip1, "BOTTOMRIGHT", 0, 0)
else
ShoppingTooltip2:SetPoint("BOTTOMRIGHT", ShoppingTooltip1, "BOTTOMLEFT", -border*2-1, 0)
end
ShoppingTooltip2:SetInventoryItem("player", slotID_other)
ShoppingTooltip2:Show()
AddHeader(ShoppingTooltip2)
end
return true
end
end
end
end
-- add HookScript method if not already existing
GameTooltip.HookScript = GameTooltip.HookScript or HookScript
ShoppingTooltip1.HookScript = ShoppingTooltip1.HookScript or HookScript
ShoppingTooltip2.HookScript = ShoppingTooltip2.HookScript or HookScript
pfUI.eqcompare.ShoppingTooltipShow = function()
-- abort if no comparison tooltip has been set
if not pfUI.eqcompare.tooltip then return end
ExtractAttributes(this)
ExtractAttributes(pfUI.eqcompare.tooltip)
CompareAttributes(pfUI.eqcompare.tooltip.pfCompData, this.pfCompData)
end
-- Add Gametooltip Hooks
GameTooltip:HookScript("OnShow", pfUI.eqcompare.GameTooltipShow)
if C.tooltip.compare.basestats == "1" then
ShoppingTooltip1:HookScript("OnShow", pfUI.eqcompare.ShoppingTooltipShow)
ShoppingTooltip2:HookScript("OnShow", pfUI.eqcompare.ShoppingTooltipShow)
end
end) end)
+4 -5
View File
@@ -940,10 +940,10 @@ pfUI:RegisterModule("equipmentmanager", function()
-- Tie popout visibility to the EM sidecar. They appear when the -- Tie popout visibility to the EM sidecar. They appear when the
-- sidecar opens, hide when it closes, and the flyout closes too. -- sidecar opens, hide when it closes, and the flyout closes too.
HookScript(frame, "OnShow", function() frame:HookScript("OnShow", function()
for _, b in ipairs(popoutButtons) do b:Show() end for _, b in ipairs(popoutButtons) do b:Show() end
end) end)
HookScript(frame, "OnHide", function() frame:HookScript("OnHide", function()
for _, b in ipairs(popoutButtons) do b:Hide() end for _, b in ipairs(popoutButtons) do b:Hide() end
if flyout then flyout:Hide() end if flyout then flyout:Hide() end
end) end)
@@ -1048,11 +1048,10 @@ pfUI:RegisterModule("equipmentmanager", function()
events:RegisterEvent("EQUIPMENT_SETS_CHANGED") events:RegisterEvent("EQUIPMENT_SETS_CHANGED")
events:RegisterEvent("EQUIPMENT_SWAP_FINISHED") events:RegisterEvent("EQUIPMENT_SWAP_FINISHED")
events:RegisterEvent("EQUIPMENT_SWAP_PENDING") events:RegisterEvent("EQUIPMENT_SWAP_PENDING")
events:RegisterEvent("BAG_UPDATE_DELAYED") -- ClassicAPI debounced; ~4x fewer refreshes than BAG_UPDATE events:RegisterEvent("BAG_UPDATE_DELAYED")
events:RegisterEvent("UNIT_INVENTORY_CHANGED") events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
events:SetScript("OnEvent", function() events:SetScript("OnEvent", function()
if not frame:IsShown() then return end if not frame:IsShown() then return end
if event == "UNIT_INVENTORY_CHANGED" and arg1 ~= "player" then return end
pfUI.equipmentmanager.Refresh() pfUI.equipmentmanager.Refresh()
end) end)
+1 -2
View File
@@ -25,8 +25,7 @@ pfUI:RegisterModule("farmmode", function ()
Minimap_ZoomOut() Minimap_ZoomOut()
end end
_G.SLASH_PFFARMMAP1, _G.SLASH_PFFARMMAP2 = "/farm", "/farmmode" pfUI.api.RegisterSlashCommand("PFFARMMAP", { "/farm", "/farmmode" }, ToggleFarmMode, true)
_G.SlashCmdList.PFFARMMAP = ToggleFarmMode
pfUI.farmmap = CreateFrame("Minimap", "pfFarmMap", UIParent) pfUI.farmmap = CreateFrame("Minimap", "pfFarmMap", UIParent)
pfUI.farmmap:Hide() pfUI.farmmap:Hide()
+5
View File
@@ -46,6 +46,11 @@ pfUI:RegisterModule("firstrun", function ()
return return
end end
end end
if not self.completed then
self.completed = true
pfUI.events:TriggerEvent("firstrun:complete")
end
end end
-- main function to create wizard windows -- main function to create wizard windows
+7 -12
View File
@@ -30,8 +30,7 @@ end)
-- /focusname is pfUI-specific because the engine has no name→GUID -- /focusname is pfUI-specific because the engine has no name→GUID
-- lookup for off-screen units — we resolve via a short target-swap. -- lookup for off-screen units — we resolve via a short target-swap.
SLASH_PFFOCUSNAME1, SLASH_PFFOCUSNAME2 = '/focusname', '/pffocusname' pfUI.api.RegisterSlashCommand("PFFOCUSNAME", { '/focusname', '/pffocusname' }, function(msg)
function SlashCmdList.PFFOCUSNAME(msg)
if msg == "" then return end if msg == "" then return end
local prevGUID = UnitGUID("target") local prevGUID = UnitGUID("target")
@@ -50,10 +49,8 @@ function SlashCmdList.PFFOCUSNAME(msg)
FocusUnit("target") FocusUnit("target")
end end
local restore = CreateFrame("Frame") RunNextFrame(function()
restore:SetScript("OnUpdate", function()
UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE") UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
restore:SetScript("OnUpdate", nil)
end) end)
if prevGUID and prevGUID ~= "0x0000000000000000" then if prevGUID and prevGUID ~= "0x0000000000000000" then
@@ -63,10 +60,9 @@ function SlashCmdList.PFFOCUSNAME(msg)
else else
ClearTarget() ClearTarget()
end end
end end, true)
SLASH_PFCASTFOCUS1, SLASH_PFCASTFOCUS2 = '/castfocus', '/pfcastfocus' pfUI.api.RegisterSlashCommand("PFCASTFOCUS", { '/castfocus', '/pfcastfocus' }, function(msg)
function SlashCmdList.PFCASTFOCUS(msg)
local focusGUID = UnitGUID("focus") local focusGUID = UnitGUID("focus")
if not focusGUID or focusGUID == "0x0000000000000000" then if not focusGUID or focusGUID == "0x0000000000000000" then
UIErrorsFrame:AddMessage(SPELL_FAILED_BAD_TARGETS, 1, 0, 0) UIErrorsFrame:AddMessage(SPELL_FAILED_BAD_TARGETS, 1, 0, 0)
@@ -107,10 +103,9 @@ function SlashCmdList.PFCASTFOCUS(msg)
else else
TargetLastTarget() TargetLastTarget()
end end
end end, true)
SLASH_PFSWAPFOCUS1, SLASH_PFSWAPFOCUS2 = '/swapfocus', '/pfswapfocus' pfUI.api.RegisterSlashCommand("PFSWAPFOCUS", { '/swapfocus', '/pfswapfocus' }, function(msg)
function SlashCmdList.PFSWAPFOCUS(msg)
local targetGUID = UnitGUID("target") local targetGUID = UnitGUID("target")
local oldFocusGUID = UnitGUID("focus") local oldFocusGUID = UnitGUID("focus")
@@ -120,4 +115,4 @@ function SlashCmdList.PFSWAPFOCUS(msg)
TargetUnit(oldFocusGUID) TargetUnit(oldFocusGUID)
end end
end end
end end, true)
+41 -23
View File
@@ -212,6 +212,7 @@ pfUI:RegisterModule("gui", function ()
if not this:GetParent():IsShown() then if not this:GetParent():IsShown() then
category[config] = r .. "," .. g .. "," .. b .. "," .. a category[config] = r .. "," .. g .. "," .. b .. "," .. a
if ufunc then ufunc() else pfUI.gui.settingChanged = true end if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
end end
end end
@@ -278,6 +279,7 @@ pfUI:RegisterModule("gui", function ()
if this:GetText() ~= this:GetParent().category[this:GetParent().config] then if this:GetText() ~= this:GetParent().category[this:GetParent().config] then
this:GetParent().category[this:GetParent().config] = this:GetText() this:GetParent().category[this:GetParent().config] = this:GetText()
if ufunc then ufunc() else pfUI.gui.settingChanged = true end if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
end end
this:SetTextColor(.2,1,.8,1) this:SetTextColor(.2,1,.8,1)
else else
@@ -328,6 +330,7 @@ pfUI:RegisterModule("gui", function ()
end end
if ufunc then ufunc() else pfUI.gui.settingChanged = true end if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
end) end)
if category[config] == "1" then frame.input:SetChecked() end if category[config] == "1" then frame.input:SetChecked() end
@@ -360,6 +363,7 @@ pfUI:RegisterModule("gui", function ()
if category and category[config] ~= value then if category and category[config] ~= value then
category[config] = value category[config] = value
if ufunc then ufunc() else pfUI.gui.settingChanged = true end if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
end end
end end
@@ -394,6 +398,7 @@ pfUI:RegisterModule("gui", function ()
end end
category[config] = newconf category[config] = newconf
if ufunc then ufunc() else pfUI.gui.settingChanged = true end if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
frame.input:UpdateMenu() frame.input:UpdateMenu()
end) end)
@@ -409,6 +414,7 @@ pfUI:RegisterModule("gui", function ()
CreateQuestionDialog(T["New entry:"], function() CreateQuestionDialog(T["New entry:"], function()
category[config] = category[config] .. "#" .. this:GetParent().input:GetText() category[config] = category[config] .. "#" .. this:GetParent().input:GetText()
if ufunc then ufunc() else pfUI.gui.settingChanged = true end if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
frame.input:UpdateMenu() frame.input:UpdateMenu()
end, false, true) end, false, true)
end) end)
@@ -906,6 +912,11 @@ pfUI:RegisterModule("gui", function ()
"1:" .. T["1 Decimal (2.1)"], "1:" .. T["1 Decimal (2.1)"],
"2:" .. T["2 Decimals (2.14)"], "2:" .. T["2 Decimals (2.14)"],
}, },
["castbaralign"] = {
"LEFT:" .. T["Left"],
"CENTER:" .. T["Center"],
"RIGHT:" .. T["Right"],
},
["orientation"] = { ["orientation"] = {
"HORIZONTAL:" .. T["Horizontal"], "HORIZONTAL:" .. T["Horizontal"],
"VERTICAL:" .. T["Vertical"], "VERTICAL:" .. T["Vertical"],
@@ -2108,7 +2119,6 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable 40y-Range Check"], C.unitframes, "rangecheck", "checkbox", nil, nil, nil, nil) 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["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["UnitXP Range Threshold (yards)"], C.unitframes, "rangecheck_distance", nil, nil, nil, nil, nil)
CreateConfig(nil, T["Range Check Interval"], C.unitframes, "rangechecki", "dropdown", pfUI.gui.dropdowns.uf_rangecheckinterval, nil, nil, nil)
CreateConfig(nil, T["Use Raid Frames To Display Group Members"], C.unitframes, "raidforgroup", "checkbox") CreateConfig(nil, T["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["Always Show Self In Raid Frames"], C.unitframes, "selfinraid", "checkbox")
CreateConfig(nil, T["Show Self In Group Frames"], C.unitframes, "selfingroup", "checkbox") CreateConfig(nil, T["Show Self In Group Frames"], C.unitframes, "selfingroup", "checkbox")
@@ -2380,11 +2390,7 @@ pfUI:RegisterModule("gui", function ()
if c == "player" then if c == "player" then
CreateConfig(nil, T["Player SP/Haste Display"], nil, nil, "header") CreateConfig(nil, T["Player SP/Haste Display"], nil, nil, "header")
CreateConfig(nil, T["Haste Display"], C.unitframes[c], "display_haste", "dropdown", { CreateConfig(nil, T["Haste Display"], C.unitframes[c], "display_haste", "checkbox")
"0:"..T["None"],
"1:"..T["Haste (cast speed increase)"],
"2:"..T["Effective Haste (Haste * cast time reduction)"], -- Only affects mages/warlocks I believe
})
CreateConfig(nil, T["Haste Display Color"], C.unitframes[c], "display_haste_color", "color") CreateConfig(nil, T["Haste Display Color"], C.unitframes[c], "display_haste_color", "color")
CreateConfig(nil, T["Display Spell Power"], C.unitframes[c], "display_spellpower", "checkbox") CreateConfig(nil, T["Display Spell Power"], C.unitframes[c], "display_spellpower", "checkbox")
CreateConfig(nil, T["Use Custom Spell Power Color (unchecked = biggest school color)"], C.unitframes[c], "display_sp_color_override", "checkbox") CreateConfig(nil, T["Use Custom Spell Power Color (unchecked = biggest school color)"], C.unitframes[c], "display_sp_color_override", "checkbox")
@@ -2406,6 +2412,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable Item Quality Color For Equipment Only"], C.appearance.bags, "borderonlygear", "checkbox") CreateConfig(nil, T["Enable Item Quality Color For Equipment Only"], C.appearance.bags, "borderonlygear", "checkbox")
CreateConfig(nil, T["Highlight Unusable Items"], C.appearance.bags, "unusable", "checkbox") CreateConfig(nil, T["Highlight Unusable Items"], C.appearance.bags, "unusable", "checkbox")
CreateConfig(nil, T["Unusable Item Color"], C.appearance.bags, "unusable_color", "color") CreateConfig(nil, T["Unusable Item Color"], C.appearance.bags, "unusable_color", "color")
CreateConfig(nil, T["Highlight New Items"], C.appearance.bags, "newitem", "checkbox")
CreateConfig(nil, T["New Item Color"], C.appearance.bags, "newitem_color", "color")
CreateConfig(nil, T["Enable Movable Bags"], C.appearance.bags, "movable", "checkbox") CreateConfig(nil, T["Enable Movable Bags"], C.appearance.bags, "movable", "checkbox")
CreateConfig(nil, T["Anchor Bags Above Chat"], C.appearance.bags, "abovechat", "checkbox") CreateConfig(nil, T["Anchor Bags Above Chat"], C.appearance.bags, "abovechat", "checkbox")
CreateConfig(nil, T["Hide Chat When Bags Are Opened"], C.appearance.bags, "hidechat", "checkbox") CreateConfig(nil, T["Hide Chat When Bags Are Opened"], C.appearance.bags, "hidechat", "checkbox")
@@ -2566,6 +2574,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["bars"], T["Switch Pages On Shift Key Press"], C.bars, "pagemastershift", "checkbox") CreateConfig(U["bars"], T["Switch Pages On Shift Key Press"], C.bars, "pagemastershift", "checkbox")
CreateConfig(U["bars"], T["Switch Pages On Ctrl Key Press"], C.bars, "pagemasterctrl", "checkbox") CreateConfig(U["bars"], T["Switch Pages On Ctrl Key Press"], C.bars, "pagemasterctrl", "checkbox")
CreateConfig(U["bars"], T["Switch Pages On Druid Stealth"], C.bars, "druidstealth", "checkbox") CreateConfig(U["bars"], T["Switch Pages On Druid Stealth"], C.bars, "druidstealth", "checkbox")
CreateConfig(U["bars"], T["Switch Pages On Priest Shadowform"], C.bars, "priestshadow", "checkbox")
CreateConfig(nil, T["Range Based Hunter Paging"], C.bars, "hunterbar", "checkbox", nil, nil, nil, nil) CreateConfig(nil, T["Range Based Hunter Paging"], C.bars, "hunterbar", "checkbox", nil, nil, nil, nil)
end) end)
@@ -2749,64 +2758,73 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable Extended Guild Information"], C.tooltip, "extguild", "checkbox") CreateConfig(nil, T["Enable Extended Guild Information"], C.tooltip, "extguild", "checkbox")
CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox") CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox")
CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox") CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox")
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha") CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha")
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture) CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox")
CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox") CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox") CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox")
CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox") CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox")
CreateConfig(U["questitem"], T["Show Required Questitem Count"], C.tooltip.questitem, "showcount", "checkbox") CreateConfig(U["questitem"], T["Show Required Questitem Count"], C.tooltip.questitem, "showcount", "checkbox")
end) end)
CreateGUIEntry(T["Castbar"], nil, function() CreateGUIEntry(T["Castbar"], T["General"], function()
CreateConfig(nil, T["Use Unit Fonts"], C.castbar, "use_unitfonts", "checkbox") CreateConfig(nil, T["Use Unit Fonts"], C.castbar, "use_unitfonts", "checkbox")
CreateConfig(nil, T["Casting Color"], C.appearance.castbar, "castbarcolor", "color") CreateConfig(nil, T["Casting Color"], C.appearance.castbar, "castbarcolor", "color")
CreateConfig(nil, T["Channeling Color"], C.appearance.castbar, "channelcolor", "color") CreateConfig(nil, T["Channeling Color"], C.appearance.castbar, "channelcolor", "color")
CreateConfig(nil, T["Castbar Texture"], C.appearance.castbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture) CreateConfig(nil, T["Castbar Texture"], C.appearance.castbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Disable Blizzard Castbar"], C.castbar.player, "hide_blizz", "checkbox") CreateConfig(nil, T["Disable Blizzard Castbar"], C.castbar.player, "hide_blizz", "checkbox")
end)
CreateConfig(nil, T["Player Castbar"], nil, nil, "header") CreateGUIEntry(T["Castbar"], T["Player"], function()
CreateConfig(nil, T["Disable Player Castbar"], C.castbar.player, "hide_pfui", "checkbox") CreateConfig(nil, T["Disable Player Castbar"], C.castbar.player, "hide_pfui", "checkbox")
CreateConfig(nil, T["Castbar Width"], C.castbar.player, "width") CreateConfig(nil, T["Castbar Width"], C.castbar.player, "width")
CreateConfig(nil, T["Castbar Height"], C.castbar.player, "height") CreateConfig(nil, T["Castbar Height"], C.castbar.player, "height")
CreateConfig(nil, T["Show Spell Icon"], C.castbar.player, "showicon", "checkbox") CreateConfig(nil, T["Show Spell Icon"], C.castbar.player, "showicon", "checkbox")
CreateConfig(nil, T["Show Spell Name"], C.castbar.player, "showname", "checkbox") CreateConfig(nil, T["Show Spell Name"], C.castbar.player, "showname", "checkbox")
CreateConfig(nil, T["Spell Name Alignment"], C.castbar.player, "namealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
CreateConfig(nil, T["Spell Name X Offset"], C.castbar.player, "txtleftoffx")
CreateConfig(nil, T["Spell Name Y Offset"], C.castbar.player, "txtleftoffy")
CreateConfig(nil, T["Show Timer"], C.castbar.player, "showtimer", "checkbox") CreateConfig(nil, T["Show Timer"], C.castbar.player, "showtimer", "checkbox")
CreateConfig(nil, T["Left Text X Offset"], C.castbar.player, "txtleftoffx") CreateConfig(nil, T["Timer Alignment"], C.castbar.player, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.player, "txtleftoffy") CreateConfig(nil, T["Timer X Offset"], C.castbar.player, "txtrightoffx")
CreateConfig(nil, T["Timer Y Offset"], C.castbar.player, "txtrightoffy")
CreateConfig(nil, T["Show Lag"], C.castbar.player, "showlag", "checkbox") CreateConfig(nil, T["Show Lag"], C.castbar.player, "showlag", "checkbox")
CreateConfig(nil, T["Show Rank"], C.castbar.player, "showrank", "checkbox") CreateConfig(nil, T["Show Rank"], C.castbar.player, "showrank", "checkbox")
CreateConfig(nil, T["Merge Tradeskill Casts"], C.castbar.player, "mergetradeskill", "checkbox") CreateConfig(nil, T["Merge Tradeskill Casts"], C.castbar.player, "mergetradeskill", "checkbox")
CreateConfig(nil, T["Right Text X Offset"], C.castbar.player, "txtrightoffx") end)
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.player, "txtrightoffy")
CreateConfig(nil, T["Target Castbar"], nil, nil, "header") CreateGUIEntry(T["Castbar"], T["Target"], function()
CreateConfig(nil, T["Disable Target Castbar"], C.castbar.target, "hide_pfui", "checkbox") CreateConfig(nil, T["Disable Target Castbar"], C.castbar.target, "hide_pfui", "checkbox")
CreateConfig(nil, T["Castbar Width"], C.castbar.target, "width") CreateConfig(nil, T["Castbar Width"], C.castbar.target, "width")
CreateConfig(nil, T["Castbar Height"], C.castbar.target, "height") CreateConfig(nil, T["Castbar Height"], C.castbar.target, "height")
CreateConfig(nil, T["Show Spell Icon"], C.castbar.target, "showicon", "checkbox") CreateConfig(nil, T["Show Spell Icon"], C.castbar.target, "showicon", "checkbox")
CreateConfig(nil, T["Show Spell Name"], C.castbar.target, "showname", "checkbox") CreateConfig(nil, T["Show Spell Name"], C.castbar.target, "showname", "checkbox")
CreateConfig(nil, T["Spell Name Alignment"], C.castbar.target, "namealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
CreateConfig(nil, T["Spell Name X Offset"], C.castbar.target, "txtleftoffx")
CreateConfig(nil, T["Spell Name Y Offset"], C.castbar.target, "txtleftoffy")
CreateConfig(nil, T["Show Timer"], C.castbar.target, "showtimer", "checkbox") CreateConfig(nil, T["Show Timer"], C.castbar.target, "showtimer", "checkbox")
CreateConfig(nil, T["Left Text X Offset"], C.castbar.target, "txtleftoffx") CreateConfig(nil, T["Timer Alignment"], C.castbar.target, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.target, "txtleftoffy") CreateConfig(nil, T["Timer X Offset"], C.castbar.target, "txtrightoffx")
CreateConfig(nil, T["Timer Y Offset"], C.castbar.target, "txtrightoffy")
CreateConfig(nil, T["Show Lag"], C.castbar.target, "showlag", "checkbox") CreateConfig(nil, T["Show Lag"], C.castbar.target, "showlag", "checkbox")
CreateConfig(nil, T["Show Rank"], C.castbar.target, "showrank", "checkbox") CreateConfig(nil, T["Show Rank"], C.castbar.target, "showrank", "checkbox")
CreateConfig(nil, T["Right Text X Offset"], C.castbar.target, "txtrightoffx") end)
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.target, "txtrightoffy")
CreateConfig(nil, T["Focus Castbar"], nil, nil, "header") CreateGUIEntry(T["Castbar"], T["Focus"], function()
CreateConfig(nil, T["Disable Focus Castbar"], C.castbar.focus, "hide_pfui", "checkbox") CreateConfig(nil, T["Disable Focus Castbar"], C.castbar.focus, "hide_pfui", "checkbox")
CreateConfig(nil, T["Castbar Width"], C.castbar.focus, "width") CreateConfig(nil, T["Castbar Width"], C.castbar.focus, "width")
CreateConfig(nil, T["Castbar Height"], C.castbar.focus, "height") CreateConfig(nil, T["Castbar Height"], C.castbar.focus, "height")
CreateConfig(nil, T["Show Spell Icon"], C.castbar.focus, "showicon", "checkbox") CreateConfig(nil, T["Show Spell Icon"], C.castbar.focus, "showicon", "checkbox")
CreateConfig(nil, T["Show Spell Name"], C.castbar.focus, "showname", "checkbox") CreateConfig(nil, T["Show Spell Name"], C.castbar.focus, "showname", "checkbox")
CreateConfig(nil, T["Spell Name Alignment"], C.castbar.focus, "namealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
CreateConfig(nil, T["Spell Name X Offset"], C.castbar.focus, "txtleftoffx")
CreateConfig(nil, T["Spell Name Y Offset"], C.castbar.focus, "txtleftoffy")
CreateConfig(nil, T["Show Timer"], C.castbar.focus, "showtimer", "checkbox") CreateConfig(nil, T["Show Timer"], C.castbar.focus, "showtimer", "checkbox")
CreateConfig(nil, T["Left Text X Offset"], C.castbar.focus, "txtleftoffx") CreateConfig(nil, T["Timer Alignment"], C.castbar.focus, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.focus, "txtleftoffy") CreateConfig(nil, T["Timer X Offset"], C.castbar.focus, "txtrightoffx")
CreateConfig(nil, T["Timer Y Offset"], C.castbar.focus, "txtrightoffy")
CreateConfig(nil, T["Show Lag"], C.castbar.focus, "showlag", "checkbox") CreateConfig(nil, T["Show Lag"], C.castbar.focus, "showlag", "checkbox")
CreateConfig(nil, T["Show Rank"], C.castbar.focus, "showrank", "checkbox") CreateConfig(nil, T["Show Rank"], C.castbar.focus, "showrank", "checkbox")
CreateConfig(nil, T["Right Text X Offset"], C.castbar.focus, "txtrightoffx")
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.focus, "txtrightoffy")
end) end)
CreateGUIEntry(T["Chat"], nil, function() CreateGUIEntry(T["Chat"], nil, function()
+7 -21
View File
@@ -2,7 +2,6 @@
-- Announces Innervate casts via raid/party/battleground chat -- Announces Innervate casts via raid/party/battleground chat
-- Registers AURA_CAST events directly - zero polling, pure event-driven -- Registers AURA_CAST events directly - zero polling, pure event-driven
pfUI:RegisterNewModule("innervatecall", "Innervate Callout", "DRUID")
pfUI:RegisterModule("innervatecall", function () pfUI:RegisterModule("innervatecall", function ()
-- Requires Nampower for AURA_CAST events -- Requires Nampower for AURA_CAST events
if not GetNampowerVersion then return end if not GetNampowerVersion then return end
@@ -13,15 +12,6 @@ pfUI:RegisterModule("innervatecall", function ()
local INNERVATE_SPELLID = 29166 local INNERVATE_SPELLID = 29166
-- Cache player GUID
local playerGuid = nil
local function GetPlayerGuid()
if not playerGuid and UnitGUID then
playerGuid = UnitGUID("player")
end
return playerGuid
end
-- GUID → name resolution for the target. UnitTokenFromGUID walks the -- GUID → name resolution for the target. UnitTokenFromGUID walks the
-- engine's known unit tokens (player, party, raid, target, ...) and -- engine's known unit tokens (player, party, raid, target, ...) and
-- returns the first one currently bound to the GUID, so we don't have -- returns the first one currently bound to the GUID, so we don't have
@@ -39,11 +29,11 @@ pfUI:RegisterModule("innervatecall", function ()
return "BATTLEGROUND" return "BATTLEGROUND"
end end
if GetNumRaidMembers() > 0 then if IsInRaid() then
return "RAID" return "RAID"
end end
if GetNumPartyMembers() > 0 then if IsInGroup() then
return "PARTY" return "PARTY"
end end
@@ -75,7 +65,7 @@ pfUI:RegisterModule("innervatecall", function ()
if spellId ~= INNERVATE_SPELLID then return end if spellId ~= INNERVATE_SPELLID then return end
-- Only announce our own casts -- Only announce our own casts
if casterGuid ~= GetPlayerGuid() then return end if not IsPlayerGuid(casterGuid) then return end
-- Resolve target name from GUID -- Resolve target name from GUID
local targetName = ResolveTargetName(targetGuid) or "Unknown" local targetName = ResolveTargetName(targetGuid) or "Unknown"
@@ -96,14 +86,10 @@ pfUI:RegisterModule("innervatecall", function ()
end end
end end
local readyAt = GetTime() + cdRemaining C_Timer.After(cdRemaining, function()
frame:SetScript("OnUpdate", function() local ch = GetAnnounceChannel()
if GetTime() >= readyAt then if ch then
frame:SetScript("OnUpdate", nil) SendChatMessage(">> Innervate is ready <<", ch)
local ch = GetAnnounceChannel()
if ch then
SendChatMessage(">> Innervate is ready <<", ch)
end
end end
end) end)
end) end)
+3 -3
View File
@@ -236,7 +236,7 @@ pfUI:RegisterModule("loot", function ()
if (candidate) then if (candidate) then
index_to_name[i] = candidate index_to_name[i] = candidate
name_to_index[candidate] = i name_to_index[candidate] = i
randoms[table.getn(randoms)+1]=i table.insert(randoms, i)
if candidate == pfUI.loot.me then if candidate == pfUI.loot.me then
pfUI.loot.my_index = i pfUI.loot.my_index = i
end end
@@ -323,7 +323,7 @@ pfUI:RegisterModule("loot", function ()
end end
function pfUI.loot:InitGroupDropDown() function pfUI.loot:InitGroupDropDown()
local inRaid = UnitInRaid("player") local inRaid = IsInRaid()
if UIDROPDOWNMENU_MENU_LEVEL == 1 then if UIDROPDOWNMENU_MENU_LEVEL == 1 then
if ( inRaid ) then if ( inRaid ) then
pfUI.loot:BuildRaidMenu(UIDROPDOWNMENU_MENU_LEVEL) pfUI.loot:BuildRaidMenu(UIDROPDOWNMENU_MENU_LEVEL)
@@ -693,7 +693,7 @@ pfUI:RegisterModule("loot", function ()
else -- not an eligible candidate for that item else -- not an eligible candidate for that item
pfUI.loot.rollers[who] = {roll=tonumber(roll),value="disabled"} pfUI.loot.rollers[who] = {roll=tonumber(roll),value="disabled"}
end end
pfUI.loot.rollers_sorted[table.getn(pfUI.loot.rollers_sorted)+1]={who=who,roll=tonumber(roll),value=pfUI.loot.rollers[who].value} table.insert(pfUI.loot.rollers_sorted, {who=who,roll=tonumber(roll),value=pfUI.loot.rollers[who].value})
end end
end end
table.sort(pfUI.loot.rollers_sorted,function(a,b) table.sort(pfUI.loot.rollers_sorted,function(a,b)
+388
View File
@@ -0,0 +1,388 @@
pfUI:RegisterModule("loothistory", function ()
local rawborder, border = GetBorderSize()
-- Layout
local ITEM_H, PLAYER_H = 24, 18
local ITEM_W, PLAYER_W = 350, 330
-- rollType constants returned by C_LootHistory.GetPlayerInfo (0/1/2; vanilla
-- has no disenchant roll).
local ROLL_PASS, ROLL_NEED, ROLL_GREED = 0, 1, 2
local ROLL_TEX = {
[ROLL_NEED] = "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
[ROLL_GREED] = "Interface\\Buttons\\UI-GroupLoot-Coin-Up",
[ROLL_PASS] = "Interface\\Buttons\\UI-GroupLoot-Pass-Up",
}
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
local function ClassColor(class)
local c = class and RAID_CLASS_COLORS[class]
if c then return c.r, c.g, c.b end
return 1, 1, 1
end
-- Paint an item row's icon/name/quality from a loaded Item mixin.
local function RenderItemVisual(f, item)
local r, g, b = 1, 1, 1
local qc = item:GetItemQualityColor()
if qc then r, g, b = qc.r, qc.g, qc.b end
f.icon:SetTexture(item:GetItemIcon())
f.iconbg:SetBackdropBorderColor(r, g, b, 1)
f.name:SetText(item:GetItemName() or UNKNOWN)
f.name:SetTextColor(r, g, b)
end
local function ShowRetrieving(f)
f.icon:SetTexture(QUESTIONMARK)
f.iconbg:SetBackdropBorderColor(1, .3, .3, 1)
f.name:SetText(T["Retrieving item information..."])
f.name:SetTextColor(1, .3, .3)
end
-- expansion state keyed on the stable rollID (survives ring-index shifts)
local expanded = {}
-- ==========================================================================
-- Window
-- ==========================================================================
pfUI.loothistory = CreateFrame("Frame", "pfLootHistory", UIParent)
pfUI.loothistory:SetFrameStrata("DIALOG")
pfUI.loothistory:SetSize(380, 490)
pfUI.loothistory:SetPoint("CENTER", 0, 0)
pfUI.loothistory:SetMovable(true)
pfUI.loothistory:EnableMouse(true)
pfUI.loothistory:RegisterForDrag("LeftButton")
pfUI.loothistory:SetScript("OnDragStart", function() this:StartMoving() end)
pfUI.loothistory:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
pfUI.loothistory:Hide()
CreateBackdrop(pfUI.loothistory, nil, true, .75)
CreateBackdropShadow(pfUI.loothistory)
tinsert(UISpecialFrames, "pfLootHistory")
pfUI.loothistory.caption = pfUI.loothistory:CreateFontString("Status", "LOW", "GameFontNormal")
pfUI.loothistory.caption:SetFont(pfUI.font_default, C.global.font_size + 4, "OUTLINE")
pfUI.loothistory.caption:SetTextColor(.2, 1, .8, 1)
pfUI.loothistory.caption:SetPoint("TOP", 0, -10)
pfUI.loothistory.caption:SetText(T["Loot History"])
-- close button
pfUI.loothistory.close = CreateFrame("Button", nil, pfUI.loothistory)
pfUI.loothistory.close:SetPoint("TOPRIGHT", -border*2, -border*2)
CreateBackdrop(pfUI.loothistory.close)
pfUI.loothistory.close:SetSize(15, 15)
pfUI.loothistory.close.texture = pfUI.loothistory.close:CreateTexture("pfLootHistoryClose")
pfUI.loothistory.close.texture:SetTexture(pfUI.media["img:close"])
pfUI.loothistory.close.texture:SetPoint("TOPLEFT", pfUI.loothistory.close, "TOPLEFT", 4, -4)
pfUI.loothistory.close.texture:SetPoint("BOTTOMRIGHT", pfUI.loothistory.close, "BOTTOMRIGHT", -4, 4)
pfUI.loothistory.close.texture:SetVertexColor(1, .25, .25, 1)
pfUI.loothistory.close:SetScript("OnEnter", function()
CreateBackdrop(pfUI.loothistory.close)
pfUI.loothistory.close.backdrop:SetBackdropBorderColor(1, .25, .25, 1)
end)
pfUI.loothistory.close:SetScript("OnLeave", function() CreateBackdrop(pfUI.loothistory.close) end)
pfUI.loothistory.close:SetScript("OnClick", function() pfUI.loothistory:Hide() end)
-- clear button
pfUI.loothistory.clear = CreateFrame("Button", nil, pfUI.loothistory, "UIPanelButtonTemplate")
SkinButton(pfUI.loothistory.clear)
pfUI.loothistory.clear:SetSize(60, 16)
pfUI.loothistory.clear:SetPoint("TOPLEFT", 10, -8)
pfUI.loothistory.clear:SetText(T["Clear"])
pfUI.loothistory.clear:SetScript("OnClick", function() C_LootHistory.Clear() end)
-- scroll frame
pfUI.loothistory.scroll = CreateScrollFrame("pfLootHistoryScroll", pfUI.loothistory)
pfUI.loothistory.scroll:SetSize(360, 440)
pfUI.loothistory.scroll:SetPoint("BOTTOM", 0, 10)
pfUI.loothistory.scroll.backdrop = CreateFrame("Frame", nil, pfUI.loothistory.scroll)
pfUI.loothistory.scroll.backdrop:SetFrameLevel(1)
pfUI.loothistory.scroll.backdrop:SetPoint("TOPLEFT", pfUI.loothistory.scroll, "TOPLEFT", -5, 5)
pfUI.loothistory.scroll.backdrop:SetPoint("BOTTOMRIGHT", pfUI.loothistory.scroll, "BOTTOMRIGHT", 5, -5)
CreateBackdrop(pfUI.loothistory.scroll.backdrop, nil, true)
local list = CreateScrollChild("pfLootHistoryList", pfUI.loothistory.scroll)
pfUI.loothistory.list = list
-- ==========================================================================
-- Frame pools
-- ==========================================================================
local itemFrames = {}
local usedPlayers, freePlayers = {}, {}
local FullUpdate -- forward declaration (toggle handlers call it)
local function CreateItemFrame()
local f = CreateFrame("Button", nil, list)
f:SetSize(ITEM_W, ITEM_H)
f:SetBackdrop(pfUI.backdrop_hover)
f:SetBackdropBorderColor(1, 1, 1, .04)
f:EnableMouse(1)
-- expand / collapse toggle
f.toggle = CreateFrame("Button", nil, f)
f.toggle:SetSize(14, 14)
f.toggle:SetPoint("LEFT", 4, 0)
f.toggle:SetScript("OnClick", function()
local id = f.rollID
if id then expanded[id] = not expanded[id]; FullUpdate() end
end)
-- icon + quality-colored border
f.iconbg = CreateFrame("Frame", nil, f)
f.iconbg:SetSize(ITEM_H - 8, ITEM_H - 8)
f.iconbg:SetPoint("LEFT", f.toggle, "RIGHT", 4, 0)
CreateBackdrop(f.iconbg, nil, true)
f.icon = f.iconbg:CreateTexture(nil, "ARTWORK")
f.icon:SetPoint("TOPLEFT", f.iconbg, "TOPLEFT", 2, -2)
f.icon:SetPoint("BOTTOMRIGHT", f.iconbg, "BOTTOMRIGHT", -2, 2)
f.icon:SetTexCoord(.08, .92, .08, .92)
-- winner block (right side, shown for decided rolls)
f.winicon = f:CreateTexture(nil, "OVERLAY")
f.winicon:SetSize(14, 14)
f.winicon:SetPoint("RIGHT", f, "RIGHT", -6, 0)
f.winroll = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.winroll:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.winroll:SetPoint("RIGHT", f.winicon, "LEFT", -2, 0)
f.winroll:SetTextColor(1, 1, 1, 1)
f.winname = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.winname:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.winname:SetPoint("RIGHT", f.winroll, "LEFT", -4, 0)
f.winname:SetJustifyH("RIGHT")
-- item name (leaves room on the right for the winner block)
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.name:SetPoint("LEFT", f.iconbg, "RIGHT", 5, 0)
f.name:SetPoint("RIGHT", f, "RIGHT", -100, 0)
f.name:SetJustifyH("LEFT")
f:SetScript("OnEnter", function()
this:SetBackdropBorderColor(1, 1, 1, .08)
if this.itemLink then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetHyperlink(this.itemLink)
GameTooltip:Show()
end
end)
f:SetScript("OnLeave", function()
this:SetBackdropBorderColor(1, 1, 1, .04)
GameTooltip:Hide()
end)
f:SetScript("OnClick", function()
local id = this.rollID
if id then expanded[id] = not expanded[id]; FullUpdate() end
end)
return f
end
local function CreatePlayerFrame()
local f = CreateFrame("Frame", nil, list)
f:SetSize(PLAYER_W, PLAYER_H)
-- name is indented to leave room for the winner checkmark on its left
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.name:SetPoint("LEFT", 20, 0)
f.name:SetJustifyH("LEFT")
f.rollicon = f:CreateTexture(nil, "OVERLAY")
f.rollicon:SetSize(16, 16)
f.rollicon:SetPoint("RIGHT", -4, 0)
f.rolltext = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.rolltext:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.rolltext:SetPoint("RIGHT", f.rollicon, "LEFT", -3, 0)
f.rolltext:SetTextColor(1, 1, 1, 1)
-- winner checkmark, just left of the player name (matches reference)
f.winmark = f:CreateTexture(nil, "OVERLAY")
f.winmark:SetSize(16, 16)
f.winmark:SetTexture(WINMARK)
f.winmark:SetPoint("RIGHT", f.name, "LEFT", -1, 0)
return f
end
local function RecycleAllPlayers()
for i = 1, table.getn(usedPlayers) do
local pf = usedPlayers[i]
pf:Hide()
table.insert(freePlayers, pf)
end
usedPlayers = {}
end
local function GetPlayerFrame()
local pf = table.remove(freePlayers) or CreatePlayerFrame()
table.insert(usedPlayers, pf)
return pf
end
local function SetToggleTexture(toggle, isExpanded)
if isExpanded then
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
else
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
end
end
-- ==========================================================================
-- Rendering
-- ==========================================================================
local function UpdateItemFrame(f, i)
local rollID, itemLink, numPlayers, isDone, winnerIdx = C_LootHistory.GetItem(i)
f.rollID = rollID
f.itemIdx = i
f.itemLink = itemLink
f.numPlayers = numPlayers or 0
f.isDone = isDone
local isExpanded = rollID and expanded[rollID]
SetToggleTexture(f.toggle, isExpanded)
-- Item icon/name/quality via the ClassicAPI Item mixin. When the item
-- isn't cached yet, show a placeholder and re-paint this row from the
-- ContinueOnItemLoad callback (guarded on rollID: rows are pooled, so the
-- callback must no-op if the row has since been reused for another roll).
local item = itemLink and Item:CreateFromItemLink(itemLink)
if item and not item:IsItemEmpty() then
if item:IsItemDataCached() then
RenderItemVisual(f, item)
else
ShowRetrieving(f)
local pending = rollID
item:ContinueOnItemLoad(function()
if f.rollID == pending then RenderItemVisual(f, item) end
end)
end
else
ShowRetrieving(f)
end
-- winner summary only on a decided, collapsed row
if isDone and not isExpanded then
if winnerIdx then
local wname, wclass, wrollType, wroll = C_LootHistory.GetPlayerInfo(i, winnerIdx)
f.winicon:SetTexture(ROLL_TEX[wrollType] or ROLL_TEX[ROLL_NEED])
f.winicon:Show()
if wroll and wroll > 0 then f.winroll:SetText(wroll) else f.winroll:SetText("") end
f.winroll:Show()
f.winname:SetText(wname or UNKNOWN)
f.winname:SetTextColor(ClassColor(wclass))
f.winname:Show()
else
-- nobody won: everyone passed
f.winicon:SetTexture(ROLL_TEX[ROLL_PASS])
f.winicon:Show()
f.winroll:SetText("")
f.winroll:Show()
f.winname:SetText(T["All players passed"])
f.winname:SetTextColor(1, .4, .4)
f.winname:Show()
end
else
f.winicon:Hide()
f.winroll:Hide()
f.winname:Hide()
end
end
local function RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf.name:SetText(name or UNKNOWN)
pf.name:SetTextColor(ClassColor(class))
pf.rollicon:SetTexture(ROLL_TEX[rollType] or ROLL_TEX[ROLL_PASS])
if roll and roll > 0 then pf.rolltext:SetText(roll) else pf.rolltext:SetText("") end
if isWinner then pf.winmark:Show() else pf.winmark:Hide() end
end
-- A player row is worth showing while the roll is undecided (see everyone),
-- or afterwards only if they actually rolled or it's you (hide the passers'
-- noise on a decided roll) — mirrors Blizzard's ShouldDisplayPlayer.
local function ShouldDisplayPlayer(isDone, roll, isMe)
return isMe or (roll and roll > 0) or not isDone
end
function FullUpdate()
if not pfUI.loothistory:IsShown() then return end
RecycleAllPlayers()
local num = C_LootHistory.GetNumItems()
local y = -2
for i = 1, num do
local f = itemFrames[i] or CreateItemFrame()
itemFrames[i] = f
UpdateItemFrame(f, i)
f:ClearAllPoints()
f:SetPoint("TOPLEFT", list, "TOPLEFT", 4, y)
f:Show()
y = y - ITEM_H - 2
if f.rollID and expanded[f.rollID] then
for p = 1, f.numPlayers do
local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p)
if ShouldDisplayPlayer(f.isDone, roll, isMe) then
local pf = GetPlayerFrame()
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf:ClearAllPoints()
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
pf:Show()
y = y - PLAYER_H
end
end
y = y - 2
end
end
for i = num + 1, table.getn(itemFrames) do
itemFrames[i]:Hide()
end
-- Resize the scroll child and re-attach it: SetHeight alone on a
-- SetAllPoints'd child doesn't make the ScrollFrame recompute its scroll
-- range, so a dynamically-grown list wouldn't scroll until a /reload.
list:SetHeight(math.max(1, -y + 2))
pfUI.loothistory.scroll:SetScrollChild(list)
pfUI.loothistory.scroll:UpdateScrollState()
end
pfUI.loothistory:SetScript("OnShow", function() FullUpdate() end)
-- ==========================================================================
-- Events
-- ==========================================================================
local events = CreateFrame("Frame")
events:RegisterEvent("LOOT_HISTORY_FULL_UPDATE")
events:RegisterEvent("LOOT_HISTORY_ROLL_CHANGED")
events:RegisterEvent("LOOT_HISTORY_ROLL_COMPLETE")
events:SetScript("OnEvent", function()
-- auto-show on a new roll opening / completing, if enabled
if C.loothistory.autoshow == "1"
and (event == "LOOT_HISTORY_FULL_UPDATE" or event == "LOOT_HISTORY_ROLL_COMPLETE")
and not pfUI.loothistory:IsShown() then
pfUI.loothistory:Show() -- OnShow runs FullUpdate
return
end
FullUpdate() -- no-op while hidden
end)
-- ==========================================================================
-- Slash command
-- ==========================================================================
local function Toggle()
pfUI.loothistory:SetShown(not pfUI.loothistory:IsShown())
end
pfUI.api.RegisterSlashCommand("PFLOOTHISTORY", { "/loothistory", "/pfloothistory" }, Toggle, true)
end)
+6 -14
View File
@@ -2,15 +2,14 @@ pfUI:RegisterModule("macrotweak", function ()
local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" } local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" }
local disabled = false local disabled = false
local function CheckConflicts() for _, addon in pairs(conflictAddons) do
for _, name in pairs(conflictAddons) do local name = addon
if IsAddOnLoaded(name) then EventUtil.ContinueOnAddOnLoaded(name, function()
if not disabled then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: " .. name .. " found, macrotweak disabled.") DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: " .. name .. " found, macrotweak disabled.")
disabled = true
return true
end end
end disabled = true
return false end)
end end
-- do not write macro calls into chat input history -- do not write macro calls into chat input history
@@ -64,11 +63,4 @@ pfUI:RegisterModule("macrotweak", function ()
UseInventoryItem(slot) UseInventoryItem(slot)
end end
end) end)
-- Check conflicts after one tick so all addons have finished loading
local watcher = CreateFrame("Frame")
watcher:SetScript("OnUpdate", function()
this:SetScript("OnUpdate", nil)
CheckConflicts()
end)
end) end)
+4 -4
View File
@@ -54,7 +54,7 @@ pfUI:RegisterModule("map", function ()
if not this.hooked then if not this.hooked then
this.hooked = true this.hooked = true
HookScript(WorldMapFrame, "OnShow", function() WorldMapFrame:HookScript("OnShow", function()
-- customize -- customize
this:EnableKeyboard(false) this:EnableKeyboard(false)
this:EnableMouseWheel(1) this:EnableMouseWheel(1)
@@ -66,7 +66,7 @@ pfUI:RegisterModule("map", function ()
pfOrigSetMapToCurrentZone() pfOrigSetMapToCurrentZone()
end) end)
HookScript(WorldMapFrame, "OnMouseWheel", function() WorldMapFrame:HookScript("OnMouseWheel", function()
if IsShiftKeyDown() then if IsShiftKeyDown() then
alpha = clamp(WorldMapFrame:GetAlpha() + arg1/10, 0.1, 1.0) alpha = clamp(WorldMapFrame:GetAlpha() + arg1/10, 0.1, 1.0)
WorldMapFrame:SetAlpha(alpha) WorldMapFrame:SetAlpha(alpha)
@@ -91,11 +91,11 @@ pfUI:RegisterModule("map", function ()
SaveMovable(this, true) SaveMovable(this, true)
end) end)
HookScript(WorldMapFrame, "OnDragStart", function() WorldMapFrame:HookScript("OnDragStart", function()
WorldMapFrame:StartMoving() WorldMapFrame:StartMoving()
end) end)
HookScript(WorldMapFrame, "OnDragStop",function() WorldMapFrame:HookScript("OnDragStop",function()
WorldMapFrame:StopMovingOrSizing() WorldMapFrame:StopMovingOrSizing()
SaveMovable(this, true) SaveMovable(this, true)
end) end)
+2 -2
View File
@@ -50,7 +50,7 @@ pfUI:RegisterModule("mapcolors", function ()
end end
local function UpdateUnitFrames(unit_button_name) local function UpdateUnitFrames(unit_button_name)
if GetNumRaidMembers() > 0 then if IsInRaid() then
for i=1, MAX_RAID_MEMBERS do for i=1, MAX_RAID_MEMBERS do
local frame_name = unit_button_name.."Raid"..i local frame_name = unit_button_name.."Raid"..i
local frame = _G[frame_name] local frame = _G[frame_name]
@@ -68,7 +68,7 @@ pfUI:RegisterModule("mapcolors", function ()
end end
end end
end end
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
for i=1, MAX_PARTY_MEMBERS do for i=1, MAX_PARTY_MEMBERS do
local frame_name = unit_button_name.."Party"..i local frame_name = unit_button_name.."Party"..i
local frame = _G[frame_name] local frame = _G[frame_name]
+33 -70
View File
@@ -30,25 +30,6 @@ pfUI:RegisterModule("mapreveal", function ()
pfUI.mapreveal:UpdateConfig() pfUI.mapreveal:UpdateConfig()
end) end)
local function unpack_hash(prefix, hash)
local _, stored_prefix, textureName, textureWidth, textureHeight, offsetX, offsetY, mapPointX, mapPointY, name
_, _, stored_prefix, textureName, textureWidth, textureHeight, offsetX, offsetY = string.find(hash, "^([|]?)([^:]+):([^:]+):([^:]+):([^:]+):([^:]+)")
if (not textureName or not offsetY) then
return
end
if (offsetY) then
_, _, mapPointX, mapPointY = string.find(hash,"^[|]?[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:([^:]+):([^:]+)")
end
if (not mapPointY) then
mapPointX = 0 mapPointY = 0
end
if (stored_prefix ~= "|") then
name = textureName
textureName = string.format("%s%s",prefix,textureName)
end
return textureName, textureWidth + 0, textureHeight + 0, offsetX + 0, offsetY + 0, mapPointX + 0, mapPointY + 0, name
end
local explores = {} local explores = {}
local explorecaches = {} local explorecaches = {}
local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } } local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } }
@@ -88,13 +69,6 @@ pfUI:RegisterModule("mapreveal", function ()
end end
end end
-- overlay data with lazy init
local overlayData = setmetatable(pfMapOverlayData, {__index = function(t,k)
local v = {}
rawset(t,k,v)
return v
end})
local function pfWorldMapFrame_Update() local function pfWorldMapFrame_Update()
-- clear stale caches -- clear stale caches
for k in pairs(explorecaches) do explorecaches[k] = nil end for k in pairs(explorecaches) do explorecaches[k] = nil end
@@ -108,7 +82,6 @@ pfUI:RegisterModule("mapreveal", function ()
local mapFileName = GetMapInfo() local mapFileName = GetMapInfo()
if not mapFileName then mapFileName = "World" end if not mapFileName then mapFileName = "World" end
local prefix = string.format("Interface\\WorldMap\\%s\\", mapFileName)
local numOverlays = GetNumMapOverlays() local numOverlays = GetNumMapOverlays()
-- accumulate explored overlays per zone (never clear, only add) -- accumulate explored overlays per zone (never clear, only add)
@@ -123,12 +96,18 @@ pfUI:RegisterModule("mapreveal", function ()
-- hide explore icons -- hide explore icons
for _, frame in pairs(explores) do frame:Hide() end for _, frame in pairs(explores) do frame:Hide() end
local zoneData = overlayData[mapFileName] -- ClassicAPI: full overlay list for the viewed zone (explored + unexplored),
-- read straight from WorldMapOverlay.dbc. Replaces the hand-measured pfMapOverlayData.
local zoneData = C_Map.GetMapOverlays() or {}
local textureCount = 0 local textureCount = 0
for i, hash in ipairs(zoneData) do for i, overlay in ipairs(zoneData) do
local textureName, textureWidth, textureHeight, offsetX, offsetY, mapPointX, mapPointY, name = unpack_hash(prefix, hash) local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE"
if not textureName then break end local textureName = overlay.texturePath -- full engine path (for SetTexture)
local textureWidth = overlay.textureWidth
local textureHeight = overlay.textureHeight
local offsetX = overlay.offsetX
local offsetY = overlay.offsetY
-- explore magnifying glass icon -- explore magnifying glass icon
explores[i] = explores[i] or CreateFrame("Frame", nil, WorldMapDetailFrame) explores[i] = explores[i] or CreateFrame("Frame", nil, WorldMapDetailFrame)
@@ -146,6 +125,8 @@ pfUI:RegisterModule("mapreveal", function ()
explore.tex:SetTexCoord(.08, .92, .08, .92) explore.tex:SetTexCoord(.08, .92, .08, .92)
explore.tex:SetAllPoints() explore.tex:SetAllPoints()
-- `alreadyknown` stores the FULL paths GetMapOverlayInfo returns,
-- so compare with the full path, not the bare name.
if C.appearance.worldmap.mapexploration == "1" and not zoneKnown[string.upper(textureName)] then if C.appearance.worldmap.mapexploration == "1" and not zoneKnown[string.upper(textureName)] then
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass") explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
explore:Show() explore:Show()
@@ -155,49 +136,31 @@ pfUI:RegisterModule("mapreveal", function ()
-- render overlay texture tiles on BORDER draw layer -- render overlay texture tiles on BORDER draw layer
-- Blizzard's explored overlays on ARTWORK draw on top of BORDER -- Blizzard's explored overlays on ARTWORK draw on top of BORDER
--
-- overlay.tiles is pre-resolved by ClassicAPI: per-tile file /
-- draw size / texcoords / canvas position, with Octo's data
-- quirks (sliver columns the DBC rect rounds away, foreign tiles
-- appended to the number sequence, upscaled re-exports) already
-- disambiguated from the actual BLP dimensions. No 256px grid
-- math here — deriving the grid from textureWidth/Height is
-- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun).
if C.appearance.worldmap.mapreveal == "1" then if C.appearance.worldmap.mapreveal == "1" then
local numH = math.ceil(textureWidth / 256) for _, tile in ipairs(overlay.tiles) do
local numV = math.ceil(textureHeight / 256) textureCount = textureCount + 1
local texPixW, texFileW, texPixH, texFileH local tex = pfGetOverlay(textureCount)
for j = 1, numV do tex:SetWidth(tile.width)
if j < numV then tex:SetHeight(tile.height)
texPixH = 256 tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY)
texFileH = 256 tex:ClearAllPoints()
else tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY)
texPixH = mod(textureHeight, 256) tex:SetTexture(tile.file)
if texPixH == 0 then texPixH = 256 end
texFileH = 16
while texFileH < texPixH do texFileH = texFileH * 2 end
end
for k = 1, numH do explorecaches[name] = explorecaches[name] or {}
textureCount = textureCount + 1 explorecaches[name][tex] = true
local tex = pfGetOverlay(textureCount)
if k < numH then tex:SetVertexColor(r,g,b,a)
texPixW = 256 tex:Show()
texFileW = 256
else
texPixW = mod(textureWidth, 256)
if texPixW == 0 then texPixW = 256 end
texFileW = 16
while texFileW < texPixW do texFileW = texFileW * 2 end
end
tex:SetWidth(texPixW)
tex:SetHeight(texPixH)
tex:SetTexCoord(0, texPixW/texFileW, 0, texPixH/texFileH)
tex:ClearAllPoints()
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", offsetX + 256*(k-1), -(offsetY + 256*(j-1)))
tex:SetTexture(string.format("%s%s", textureName, ((j-1)*numH + k)))
explorecaches[name] = explorecaches[name] or {}
explorecaches[name][tex] = true
tex:SetVertexColor(r,g,b,a)
tex:Show()
end
end end
end end
end end
-1
View File
@@ -1,4 +1,3 @@
pfUI:RegisterNewModule("marktracking", "Mark Tracker")
pfUI:RegisterModule("marktracking", function () pfUI:RegisterModule("marktracking", function ()
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower) -- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
if not UnitExists("mark1") and not UnitExists("mark8") then if not UnitExists("mark1") and not UnitExists("mark8") then
+11 -78
View File
@@ -1,39 +1,5 @@
pfUI:RegisterModule("mouseover", function () pfUI:RegisterModule("mouseover", function ()
pfUI.uf.mouseover = CreateFrame("Frame", "pfMouseOver", UIParent) pfUI.api.RegisterSlashCommand("PFCAST", { "/pfcast", "/pfmouse" }, function(msg)
-- Prepare a list of units that can be used via SpellTargetUnit
local st_units = { [1] = "player", [2] = "target", [3] = "mouseover" }
for i=1, MAX_PARTY_MEMBERS do table.insert(st_units, "party"..i) end
for i=1, MAX_RAID_MEMBERS do table.insert(st_units, "raid"..i) end
-- Try to find a valid (friendly) unitstring that can be used for
-- SpellTargetUnit(unit) to avoid another target switch
local function GetUnitString(unit)
for index, unitstr in pairs(st_units) do
if UnitIsUnit(unit, unitstr) then
return unitstr
end
end
return nil
end
-- Same as CastSpellByName but with disabled AutoSelfCast
local function NoSelfCast(spell, onself)
local cvar_selfcast = GetCVar("AutoSelfCast")
if cvar_selfcast ~= "0" then
SetCVar("AutoSelfCast", "0")
pcall(CastSpellByName, spell, onself)
SetCVar("AutoSelfCast", cvar_selfcast)
else
CastSpellByName(spell, onself)
end
end
_G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse"
function SlashCmdList.PFCAST(msg)
local restore_target = true
local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg)
local unit = "mouseover" local unit = "mouseover"
@@ -50,51 +16,18 @@ pfUI:RegisterModule("mouseover", function ()
end end
end end
-- Nampower: CastSpellByName supports a second unit parameter directly. -- Spell-name path: Nampower's CastSpellByName takes a second unit
-- unit is already resolved to "mouseover", "target" or "player" at this point. -- parameter directly, no target swap dance required.
if not func and GetNampowerVersion then if not func then
CastSpellByName(msg, unit) CastSpellByName(msg, unit)
return return
end end
-- If target and mouseover are friendly units, we can't use spell target as it -- Macro path: switch target so the macro's spell calls land on `unit`,
-- would cast on the target instead of the mouseover. However, if the mouseover -- then restore.
-- is friendly and the target is not, we can try to obtain the best unitstring local restore_target = not UnitIsUnit("target", unit)
-- for the later SpellTargetUnit() call. if restore_target then TargetUnit(unit) end
local unitstr = not UnitCanAssist("player", "target") and UnitCanAssist("player", unit) and GetUnitString(unit) func()
if restore_target then TargetLastTarget() end
if UnitIsUnit("target", unit) or (not func and unitstr) then end, true)
-- no target change required, we can either use spell target
-- or the unit is already our current target.
restore_target = false
else
-- The spelltarget can't be used here, we need to switch
-- and restore the target during spell cast
TargetUnit(unit)
end
if func then
func()
else
-- write temporary unit name
pfUI.uf.mouseover.unit = unit
-- cast without self cast cvar setting
-- to allow spells to use spelltarget
NoSelfCast(msg)
-- set spell target to unitstring (or selfcast)
if SpellIsTargeting() then SpellTargetUnit(unitstr or "player") end
-- clean up spell target in error case
if SpellIsTargeting() then SpellStopTargeting() end
-- remove temporary mouseover unit
pfUI.uf.mouseover.unit = nil
end
if restore_target then
TargetLastTarget()
end
end
end) end)
+7 -15
View File
@@ -100,12 +100,6 @@ pfUI:RegisterModule("nameplates", function ()
-- PERF: visiblePlateCount maintained event-driven (NAME_PLATE_UNIT_ADDED/_REMOVED) -- PERF: visiblePlateCount maintained event-driven (NAME_PLATE_UNIT_ADDED/_REMOVED)
local visiblePlateCount = 0 local visiblePlateCount = 0
-- wipe polyfill
local wipe = wipe or function(t) for k in pairs(t) do t[k] = nil end end
-- Player GUID for filtering
local PlayerGUID = UnitGUID("player")
-- ============================================================================ -- ============================================================================
-- OPTIMIZATION: Config caching -- OPTIMIZATION: Config caching
-- ============================================================================ -- ============================================================================
@@ -492,7 +486,6 @@ end
elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
_, PlayerGUID = UnitExists("player")
CacheConfig() CacheConfig()
this:SetGameVariables() this:SetGameVariables()
RebuildRaidGuidCache() RebuildRaidGuidCache()
@@ -702,7 +695,7 @@ end
end end
end end
HookScript(nameplate.original.healthbar, "OnValueChanged", nameplates.OnValueChanged) nameplate.original.healthbar:HookScript("OnValueChanged", nameplates.OnValueChanged)
-- adjust sizes and scaling of the nameplate -- adjust sizes and scaling of the nameplate
nameplate:SetScale(UIParent:GetScale()) nameplate:SetScale(UIParent:GetScale())
@@ -937,10 +930,15 @@ end
-- name — name alone misses pool reuse between same-named units (e.g. plate -- name — name alone misses pool reuse between same-named units (e.g. plate
-- held a player "Ironforge Guard" and is now reassigned to the NPC by the -- held a player "Ironforge Guard" and is now reassigned to the NPC by the
-- same name), which would leak a stale "PLAYER" hint into GetUnitInfo. -- same name), which would leak a stale "PLAYER" hint into GetUnitInfo.
-- Wipe the whole cache table: the PERF gates below ("only update X when
-- X changed") would otherwise skip bar/color/text updates when the new
-- unit happens to share a cached value with the previous occupant
-- (e.g., both at 60% HP percentage on plate pool reuse → bar stays at
-- the old fill until the new mob actually changes HP).
if plate.cache.name ~= name or plate.cache.guid ~= plate.cachedGuid then if plate.cache.name ~= name or plate.cache.guid ~= plate.cachedGuid then
table.wipe(plate.cache)
plate.cache.name = name plate.cache.name = name
plate.cache.guid = plate.cachedGuid plate.cache.guid = plate.cachedGuid
plate.cache.player = nil
plate.cdCache = nil -- new unit, reset spell-keyed timer cache plate.cdCache = nil -- new unit, reset spell-keyed timer cache
plate.name:SetText(GetNameString(name)) plate.name:SetText(GetNameString(name))
end end
@@ -987,12 +985,6 @@ end
-- remove unitstr on unit name mismatch -- remove unitstr on unit name mismatch
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- use mobhealth values if addon is running
if (MobHealth3 or MobHealthFrame) and target and name == UnitName('target') and MobHealth_GetTargetCurHP() then
hp = MobHealth_GetTargetCurHP() > 0 and MobHealth_GetTargetCurHP() or hp
hpmax = MobHealth_GetTargetMaxHP() > 0 and MobHealth_GetTargetMaxHP() or hpmax
end
-- always make sure to keep plate visible -- always make sure to keep plate visible
plate:Show() plate:Show()
+2 -4
View File
@@ -150,9 +150,7 @@ pfUI:RegisterModule("nampower", function ()
-- /disenchantall slash command (DisenchantAll is Nampower-provided) -- /disenchantall slash command (DisenchantAll is Nampower-provided)
if DisenchantAll then if DisenchantAll then
_G.SLASH_PFDISENCHANTALL1 = "/disenchantall" pfUI.api.RegisterSlashCommand("PFDISENCHANTALL", { "/disenchantall", "/dea" }, function(msg)
_G.SLASH_PFDISENCHANTALL2 = "/dea"
SlashCmdList["PFDISENCHANTALL"] = function(msg)
-- DisenchantAll(itemIdOrName | quality, [includeSoulbound]). -- DisenchantAll(itemIdOrName | quality, [includeSoulbound]).
-- Quality is a string keyword ("greens", "blues", "purples", or pipe- -- Quality is a string keyword ("greens", "blues", "purples", or pipe-
-- combined). Numbers are interpreted as item IDs, not quality levels. -- combined). Numbers are interpreted as item IDs, not quality levels.
@@ -161,7 +159,7 @@ pfUI:RegisterModule("nampower", function ()
local target = tonumber(arg) or arg local target = tonumber(arg) or arg
DisenchantAll(target) DisenchantAll(target)
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: DisenchantAll(" .. tostring(target) .. ")") DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: DisenchantAll(" .. tostring(target) .. ")")
end end, true)
end end
-- Druid Secondary Mana Bar -- Druid Secondary Mana Bar
+68
View File
@@ -0,0 +1,68 @@
pfUI:RegisterModule("newitem", function ()
if not pfUI.bag then return end
if C.appearance.bags.newitem ~= "1" then return end
pfUI.newitem = {}
local color = CreateColor(strsplit(",", C.appearance.bags.newitem_color))
function pfUI.newitem:UpdateSlot(bag, slot)
if bag < 0 or bag > 4 then return end
if not pfUI.bags[bag] then return end
if not pfUI.bags[bag].slots[slot] then return end
local frame = pfUI.bags[bag].slots[slot].frame
if frame.hasItem and C_NewItems.IsNewItem(bag, slot) then
if not frame.newitem then
local glow = frame:CreateTexture(nil, "OVERLAY")
glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
glow:SetBlendMode("ADD")
glow:SetVertexColor(color:GetRGBA())
glow:SetPoint("CENTER", frame, "CENTER")
glow:Hide()
glow.RefreshSize = function(g)
local w = g:GetParent():GetWidth()
if w > 0 then g:SetSize(w * 1.8, w * 1.8) end
end
frame.newitem = glow
frame:HookScript("OnEnter", function()
C_NewItems.RemoveNewItem(bag, slot)
end)
end
frame.newitem:RefreshSize()
frame.newitem:Show()
elseif frame.newitem and frame.newitem:IsShown() then
frame.newitem:Hide()
end
end
-- The new-item set can change without any slot's contents changing (an item
-- acknowledged, pruned when it leaves the bags, or ClearAll) -- re-evaluate
-- every decorated slot when that happens.
function pfUI.newitem:RefreshAll()
for bag in pairs(pfUI.bags) do
local slots = pfUI.bags[bag] and pfUI.bags[bag].slots
if slots then
for slot in pairs(slots) do
pfUI.newitem:UpdateSlot(bag, slot)
end
end
end
end
-- per-slot: pfUI re-runs UpdateSlot whenever a slot's contents change.
hooksecurefunc(pfUI.bag, "UpdateSlot", function(self, bag, slot)
pfUI.newitem:UpdateSlot(bag, slot)
end)
EventRegistry:RegisterFrameEventAndCallback("BAG_NEW_ITEMS_UPDATED", function()
pfUI.newitem:RefreshAll()
end)
pfUI.events:RegisterCallback("bag:closed", function(_, object)
if object then return end
C_NewItems.ClearAll()
end, "newitem")
end)
+3 -4
View File
@@ -424,7 +424,8 @@ pfUI:RegisterModule("panel", function()
widget:RegisterEvent("PLAYER_REGEN_ENABLED") widget:RegisterEvent("PLAYER_REGEN_ENABLED")
widget:RegisterEvent("PLAYER_DEAD") widget:RegisterEvent("PLAYER_DEAD")
widget:RegisterEvent("PLAYER_UNGHOST") widget:RegisterEvent("PLAYER_UNGHOST")
widget:RegisterEvent("UNIT_INVENTORY_CHANGED") widget:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: equip/unequip changes total durability
widget:RegisterEvent("UPDATE_INVENTORY_DURABILITY") -- ClassicAPI: combat wear / repair
widget.Click = function() ToggleCharacter("PaperDollFrame") end widget.Click = function() ToggleCharacter("PaperDollFrame") end
widget.Tooltip = function() widget.Tooltip = function()
@@ -456,8 +457,6 @@ pfUI:RegisterModule("panel", function()
end end
end end
widget:SetScript("OnEvent", function() widget:SetScript("OnEvent", function()
if event == "UNIT_INVENTORY_CHANGED" and arg1 ~= "player" then return end
local totalCurr, totalMax = 0, 0 local totalCurr, totalMax = 0, 0
for id = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do for id = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do
local cur, max = GetInventoryItemDurability(id) local cur, max = GetInventoryItemDurability(id)
@@ -533,7 +532,7 @@ pfUI:RegisterModule("panel", function()
-- Hearthstone bind location -- Hearthstone bind location
local hearth = CreateFrame("Frame", "pfPanelBindLocation", UIParent) local hearth = CreateFrame("Frame", "pfPanelBindLocation", UIParent)
hearth:RegisterEvent("PLAYER_ENTERING_WORLD") hearth:RegisterEvent("PLAYER_ENTERING_WORLD")
hearth:RegisterEvent("CHAT_MSG_SYSTEM") hearth:RegisterEvent("HEARTHSTONE_BOUND")
hearth:SetScript("OnEvent", function() hearth:SetScript("OnEvent", function()
pfUI.panel:OutputPanel("bindlocation", T["Hearthstone"] .. ": " .. (GetBindLocation() or T["Not Set"])) pfUI.panel:OutputPanel("bindlocation", T["Hearthstone"] .. ": " .. (GetBindLocation() or T["Not Set"]))
end) end)
+16 -49
View File
@@ -38,38 +38,6 @@ pfUI:RegisterModule("player", function ()
playerFrame.myclass = myclass playerFrame.myclass = myclass
playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER" playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER"
-- Compute class-based casting speed modifier and cache on the frame.
-- This is re-evaluated on LEARNED_SPELL_IN_TAB (with 1s delay) so talent changes are handled.
-- Not sure if there are any other effects that give % cast reduction time
local function UpdatePlayerModCastingTime()
playerFrame.modCastingTime = 1
if myclass == "MAGE" then
local _, _, _, _, acceleratedArcana = GetTalentInfo(1, 16)
if acceleratedArcana and acceleratedArcana > 0 then
playerFrame.modCastingTime = 0.95
end
elseif myclass == "WARLOCK" then
local _, _, _, _, rapidDeter = GetTalentInfo(1, 14)
if rapidDeter and rapidDeter > 0 then
playerFrame.modCastingTime = 1 - (rapidDeter * 0.03)
end
end
end
local talentFrame = CreateFrame("Frame")
talentFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
talentFrame:RegisterEvent("LEARNED_SPELL_IN_TAB")
talentFrame:SetScript("OnEvent", function()
-- Delay 1s for both PLAYER_ENTERING_WORLD and LEARNED_SPELL_IN_TAB
local checkAt = GetTime() + 1
talentFrame:SetScript("OnUpdate", function()
if GetTime() >= checkAt then
talentFrame:SetScript("OnUpdate", nil)
UpdatePlayerModCastingTime()
end
end)
end)
-- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset -- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset
local function cfgColorToHex(colorStr) local function cfgColorToHex(colorStr)
if not colorStr or colorStr == "" then return nil end if not colorStr or colorStr == "" then return nil end
@@ -98,26 +66,25 @@ pfUI:RegisterModule("player", function ()
if not cfg then if not cfg then
return return
end end
local hasteMode = cfg.display_haste -- "0"=none, "1"=modCastSpeed, "2"=modCastSpeed*modCastingTime -- display_haste: "0"=hidden, "1"=show modCastSpeed (gear haste). Talent-
-- side cast-time reductions show up in the actual cast bar via
-- C_Spell.UnitCastingInfo; double-folding them into this overlay was
-- mixing two different concepts into one number.
local showHaste = cfg.display_haste == "1"
local showSP = cfg.display_spellpower == "1" local showSP = cfg.display_spellpower == "1"
local isSpellCaster = playerFrame.isSpellCaster local isSpellCaster = playerFrame.isSpellCaster
if (hasteMode == "0" or not isSpellCaster) and not showSP then if (not showHaste or not isSpellCaster) and not showSP then
playerFrame.infoTopCenterText:SetText("") playerFrame.infoTopCenterText:SetText("")
return return
end end
local haste = GetUnitField("player", "modCastSpeed") local haste = GetUnitField("player", "modCastSpeed")
local modCastingTime = playerFrame.modCastingTime or 1
local text = "" local text = ""
if isSpellCaster and haste then if showHaste and isSpellCaster and haste then
local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF" local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF"
if hasteMode == "1" then text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / haste - 1) * 100)
text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / haste - 1) * 100)
elseif hasteMode == "2" then
text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / (haste * modCastingTime) - 1) * 100)
end
end end
if showSP and isSpellCaster then if showSP and isSpellCaster then
@@ -151,21 +118,21 @@ pfUI:RegisterModule("player", function ()
end end
-- Add throttle to player frame OnUpdate -- Add throttle to player frame OnUpdate
-- Throttle the unit frame's existing OnUpdate to ~20 FPS so the per-frame
-- work stays cheap.
if pfUI.uf.player:GetScript("OnUpdate") then if pfUI.uf.player:GetScript("OnUpdate") then
local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate") local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate")
pfUI.uf.player:SetScript("OnUpdate", function() pfUI.uf.player:SetScript("OnUpdate", function()
if (this.throttleTick or 0) > GetTime() then if (this.throttleTick or 0) > GetTime() then return end
return this.throttleTick = GetTime() + 0.05
end
this.throttleTick = GetTime() + 0.05 -- Default: 20 FPS
originalOnUpdate() originalOnUpdate()
if (this.infoTextTick or 0) <= GetTime() then
this.infoTextTick = GetTime() + 0.25 -- Don't need to update haste/SP text as often
UpdateInfoText()
end
end) end)
end end
-- Haste / spell-power overlay text — refreshes 4×/sec on its own ticker,
-- independent of the unit frame's OnUpdate cadence.
C_Timer.NewTicker(0.25, UpdateInfoText)
-- Replace default's RESET_INSTANCES button with an always working one -- Replace default's RESET_INSTANCES button with an always working one
UnitPopupButtons["RESET_INSTANCES_FIX"] = { text = RESET_INSTANCES, dist = 0 } UnitPopupButtons["RESET_INSTANCES_FIX"] = { text = RESET_INSTANCES, dist = 0 }
for id, text in pairs(UnitPopupMenus["SELF"]) do for id, text in pairs(UnitPopupMenus["SELF"]) do
+57 -36
View File
@@ -1,8 +1,43 @@
pfUI:RegisterModule("questitem", function () pfUI:RegisterModule("questitem", function ()
-- [itemID] = { index = questLogIndex, count = requiredCount }. Rebuilt
-- on QUEST_LOG_UPDATE from ClassicAPI's per-quest cached requirements.
local requiredItems = {} local requiredItems = {}
local function AddQuest(questID)
local details = C_QuestLog.GetQuestDetails(questID)
if not details then return false end
if details.requirements then
for _, req in ipairs(details.requirements) do
if req.kind == "item" and req.id and req.id > 0 then
requiredItems[req.id] = {
questID = questID,
title = details.title,
level = details.level,
count = req.count,
}
end
end
end
return true
end
local function RemoveQuest(questID)
for itemID, entry in pairs(requiredItems) do
if entry.questID == questID then requiredItems[itemID] = nil end
end
end
local function Seed()
for k in pairs(requiredItems) do requiredItems[k] = nil end
local complete = true
local i = 1
while true do
local questID = C_QuestLog.GetQuestIDForLogIndex(i)
if questID == nil then break end
if questID > 0 and not AddQuest(questID) then complete = false end
i = i + 1
end
return complete
end
local function AddTooltip(frame, itemID) local function AddTooltip(frame, itemID)
if not itemID then return end if not itemID then return end
if C.tooltip.questitem.showquest ~= "1" then return end if C.tooltip.questitem.showquest ~= "1" then return end
@@ -19,7 +54,7 @@ pfUI:RegisterModule("questitem", function ()
if not entry and not replace then return end if not entry and not replace then return end
local quest, level = UNKNOWN, 255 local quest, level = UNKNOWN, 255
if entry then quest, level = GetQuestLogTitle(entry.index) end if entry then quest, level = entry.title, entry.level end
if not quest then return end if not quest then return end
local color = GetDifficultyColor(level) local color = GetDifficultyColor(level)
@@ -42,48 +77,37 @@ pfUI:RegisterModule("questitem", function ()
pfUI.questitem = CreateFrame("Frame", "pfQuestItemScanner", UIParent) pfUI.questitem = CreateFrame("Frame", "pfQuestItemScanner", UIParent)
pfUI.questitem:RegisterEvent("PLAYER_ENTERING_WORLD") pfUI.questitem:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.questitem:RegisterEvent("QUEST_LOG_UPDATE") pfUI.questitem:RegisterEvent("QUEST_LOG_UPDATE")
pfUI.questitem:RegisterEvent("QUEST_ACCEPTED")
pfUI.questitem:RegisterEvent("QUEST_REMOVED")
pfUI.questitem:SetScript("OnEvent", function() pfUI.questitem:SetScript("OnEvent", function()
-- debounce rebuilds — QUEST_LOG_UPDATE fires in bursts if C.tooltip.questitem.showquest ~= "1" then return end
this.run = GetTime() + .5 if event == "QUEST_ACCEPTED" then -- arg1 = logIndex, arg2 = questID
if not AddQuest(arg2) then -- cache cold (rare) -> reseed
this.seeding = true
this.run = GetTime() + .5
end
elseif event == "QUEST_REMOVED" then -- arg1 = questID
RemoveQuest(arg1)
elseif event == "PLAYER_ENTERING_WORLD" then
this.seeding = true -- seed pre-existing quests
this.run = GetTime() + .5
elseif event == "QUEST_LOG_UPDATE" and this.seeding then
this.run = GetTime() + .5 -- keep retrying while cache warms
end
end) end)
pfUI.questitem:SetScript("OnUpdate", function() pfUI.questitem:SetScript("OnUpdate", function()
if C.tooltip.questitem.showquest ~= "1" then return end
if not this.run or GetTime() < this.run then return end if not this.run or GetTime() < this.run then return end
for k in pairs(requiredItems) do requiredItems[k] = nil end
-- GetQuestIDForLogIndex returns nil past the end, 0 for headers, else
-- the questID. GetQuestDetails reads the engine's static-info cache —
-- nil if not yet populated; we'll catch it on the next refresh.
local i = 1
while true do
local questID = C_QuestLog.GetQuestIDForLogIndex(i)
if questID == nil then break end
if questID > 0 then
local details = C_QuestLog.GetQuestDetails(questID)
if details and details.requirements then
for _, req in ipairs(details.requirements) do
if req.kind == "item" and req.id and req.id > 0 then
requiredItems[req.id] = { index = i, count = req.count }
end
end
end
end
i = i + 1
end
this.run = nil this.run = nil
if Seed() then this.seeding = nil end
end) end)
-- reload quest entries on config change
pfUI.questitem.UpdateConfig = function() pfUI.questitem.UpdateConfig = function()
if C.tooltip.questitem.showquest ~= "1" then return end
pfUI.questitem.seeding = true
pfUI.questitem.run = GetTime() + .5 pfUI.questitem.run = GetTime() + .5
end end
-- regular tooltip: catch every Show via a child frame's OnShow, then ask
-- the tooltip directly for the item it's displaying. Replaces a libtooltip
-- indirection that did the same query with extra caching layers.
pfUI.questitem.tooltip = CreateFrame("Frame", "pfQuestItems", GameTooltip) pfUI.questitem.tooltip = CreateFrame("Frame", "pfQuestItems", GameTooltip)
pfUI.questitem.tooltip:SetScript("OnShow", function() pfUI.questitem.tooltip:SetScript("OnShow", function()
if GameTooltip:HasItem() then if GameTooltip:HasItem() then
@@ -92,9 +116,6 @@ pfUI:RegisterModule("questitem", function ()
end end
end) end)
-- itemref tooltip (chat link clicks): hooksecurefunc runs after SetItemRef
-- populates ItemRefTooltip, so we just read the item back out of the tooltip
-- instead of re-parsing the "item:NNN" out of the link string.
hooksecurefunc("SetItemRef", function() hooksecurefunc("SetItemRef", function()
if IsModifierKeyDown() then return end if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then if ItemRefTooltip:HasItem() then
+1 -1
View File
@@ -106,7 +106,7 @@ pfUI:RegisterModule("raid", function ()
this.pendingUpdate = nil this.pendingUpdate = nil
-- don't proceed without raid -- don't proceed without raid
if not UnitInRaid("player") then return end if not IsInRaid() then return end
-- clear all existing frames -- clear all existing frames
for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end
+137 -14
View File
@@ -1,13 +1,11 @@
pfUI:RegisterModule("sellvalue", function () pfUI:RegisterModule("sellvalue", function ()
local function AddVendorPrices(frame, id, count) local function AddVendorPrices(frame, id, count)
if not id then return end if not id then return end
-- Sell price comes from the engine (item DBC); buy price from pfSellData
-- (curated vendor data, since vendor purchase prices aren't a static field).
local sell = C_Item.GetItemSellPriceByID(id) or 0 local sell = C_Item.GetItemSellPriceByID(id) or 0
local buy = pfSellData[id] local buy = pfSellData[id]
if sell == 0 and not buy then return end if sell == 0 and not buy then return end
if C.tooltip.vendor.showalways == "1" or IsShiftKeyDown() then if C.tooltip.vendor.showalways == "1" or IsShiftKeyDown() then
frame:AddLine(" ") frame:AddLine(" ")
if sell > 0 then if sell > 0 then
@@ -25,21 +23,12 @@ pfUI:RegisterModule("sellvalue", function ()
frame:AddDoubleLine(T["Buy"] .. ":", CreateGoldString(buy), 1, 1, 1) frame:AddDoubleLine(T["Buy"] .. ":", CreateGoldString(buy), 1, 1, 1)
end end
end end
elseif not MerchantFrame:IsShown() and sell > 0 then
SetTooltipMoney(frame, sell * count)
end end
frame:Show() frame:Show()
end end
pfUI.sellvalue = CreateFrame("Frame", "pfGameTooltip", GameTooltip)
pfUI.sellvalue:SetScript("OnShow", function()
if GameTooltip:HasItem() then
local _, _, id = GameTooltip:GetItem()
if id then
local count = tonumber(libtooltip:GetItemCount()) or 1
AddVendorPrices(GameTooltip, id, math.max(count, 1))
end
end
end)
hooksecurefunc("SetItemRef", function() hooksecurefunc("SetItemRef", function()
if IsModifierKeyDown() then return end if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then if ItemRefTooltip:HasItem() then
@@ -47,4 +36,138 @@ pfUI:RegisterModule("sellvalue", function ()
if id then AddVendorPrices(ItemRefTooltip, id, 1) end if id then AddVendorPrices(ItemRefTooltip, id, 1) end
end end
end) end)
local TooltipHooks = {
SetLootRollItem = {
id = GetLootRollItemID,
count = function(slot)
local _, _, count = GetLootRollItemInfo(slot)
return count
end
},
SetLootItem = {
id = GetLootSlotItemID,
count = function(slot)
local _, _, count = GetLootSlotInfo(slot)
return count
end
},
SetQuestLogItem = {
id = GetQuestLogItemID,
count = function(type, index)
local itemCount, _;
if type == "choice" then
_, _, itemCount = GetQuestLogChoiceInfo(index);
else
_, _, itemCount = GetQuestLogRewardInfo(index)
end
return itemCount
end,
},
SetQuestItem = {
id = GetQuestItemID,
count = function(type, index)
local _, _, count = GetQuestItemInfo(type, index);
return count
end,
},
SetHyperlink = { id = C_Item.GetItemInfoInstant },
SetBagItem = {
id = C_Container.GetContainerItemID,
count = function(container, slot)
local _, count = GetContainerItemInfo(container, slot)
return count
end,
},
SetInboxItem = {
id = GetInboxItemID,
count = function(index)
local _, _, _, count = GetInboxItem(index)
return count
end,
},
SetSendMailItem = {
id = function()
local _, id = GetSendMailItemLink()
return id
end,
count = function()
local _, _, count = GetSendMailItem()
return count
end,
},
SetInventoryItem = { id = GetInventoryItemID },
SetTradeSkillItem = {
id = function(skillIndex, reagentIndex)
if reagentIndex then
return GetTradeSkillReagentItemID(skillIndex, reagentIndex)
else
return GetTradeSkillItemID(skillIndex)
end
end,
count = function(skillIndex, reagentIndex)
if reagentIndex then
local _, _, itemCount = GetTradeSkillReagentInfo(skillIndex, reagentIndex)
return itemCount
else
return GetTradeSkillNumMade(skillIndex)
end
end,
},
SetAuctionItem = {
id = GetAuctionItemLink,
count = function(viewType, index)
local _, _, count = GetAuctionItemInfo(viewType, index)
return count
end,
},
SetAuctionSellItem = { id = GetAuctionSellItemLink },
SetTradePlayerItem = {
id = GetTradePlayerItemLink,
count = function(id)
local _, _, count = GetTradePlayerItemInfo(id)
return count
end,
},
SetTradeTargetItem = {
id = GetTradeTargetItemLink,
count = function(id)
local _, _, count = GetTradeTargetItemInfo(id)
return count
end,
},
SetMerchantItem = {
id = GetMerchantItemID,
count = function(index)
local _, _, _, itemCount = GetMerchantItemInfo(index)
return itemCount
end
},
SetCraftItem = {
id = function(recipeIndex, reagentIndex)
return GetCraftReagentItemID(recipeIndex, reagentIndex)
end
},
SetBuybackItem = {
id = C_MerchantFrame.GetBuybackItemID,
count = function(slotIndex)
local _, _, _, itemCount = GetBuybackItemInfo(slotIndex)
return itemCount
end
}
}
local function makeHook(entry)
return function(tooltip, arg1, arg2, arg3)
AddVendorPrices(tooltip, entry.id(arg1, arg2, arg3), entry.count and entry.count(arg1, arg2, arg3) or 1)
end
end
local function HookTooltip(tooltip)
for setter, entry in pairs(TooltipHooks) do
hooksecurefunc(tooltip, setter, makeHook(entry))
end
end
HookTooltip(GameTooltip)
end) end)
+133 -197
View File
@@ -1,93 +1,78 @@
pfUI:RegisterModule("share", function () pfUI:RegisterModule("share", function ()
local function serialize(tbl, comp, name, ignored, spacing) -- Profile sharing rides ClassicAPI's C_EncodingUtil:
local spacing = spacing or "" -- export: diff vs defaults -> SerializeCBOR -> CompressString (zlib)
local match = nil -- -> EncodeBase64, prefixed with the format magic
local tname = ( spacing == "" and "" or "[\"" ) .. name .. ( spacing == "" and "" or "\"]" ) -- import: the reverse, into a plain table — no loadstring, so a
local str = spacing .. tname .. " = {\n" -- pasted profile can't execute code
-- The Decode/Encode button converts between the transport blob and an
-- editable JSON view of the same table.
local MAGIC = "!pf1!"
-- Deep-diff `tbl` against `default`, keeping only changed or added
-- values. `ignored` keys are skipped at the top level only (matches the
-- old serializer: "position"/"disabled" when Ignore Layout is checked).
-- Returns nil when nothing differs.
local function DiffConfig(tbl, default, ignored)
local diff = nil
for k, v in pairs(tbl) do for k, v in pairs(tbl) do
if not ( ignored[k] and spacing == "" ) and ( not comp or not comp[k] or comp[k] ~= tbl[k] ) then if not ( ignored and ignored[k] ) then
local dv = default and default[k]
if type(v) == "table" then if type(v) == "table" then
local result = serialize(tbl[k], comp and comp[k], k, ignored, spacing .. " ") local sub = DiffConfig(v, type(dv) == "table" and dv or nil)
if result then if sub then
match = true diff = diff or {}
str = str .. result diff[k] = sub
end end
elseif type(v) == "string" then elseif v ~= dv and ( type(v) == "string" or type(v) == "number" or type(v) == "boolean" ) then
match = true diff = diff or {}
local escaped = string.gsub(v, "\\", "\\\\") diff[k] = v
escaped = string.gsub(escaped, "\"", "\\\"")
str = str .. spacing .. " [\""..k.."\"] = \"".. escaped .."\",\n"
elseif type(v) == "number" then
match = true
str = str .. spacing .. " [\""..k.."\"] = ".. string.gsub(v, "\\", "\\\\") ..",\n"
end end
end end
end end
return diff
str = str .. spacing .. "}" .. ( spacing == "" and "" or "," ) .. "\n"
return match and str or nil
end end
local function compress(input) -- EditBoxes don't soft-wrap one giant unbroken line; chunk the blob.
-- based on Rochet2's lzw compression local function wrap(str, width)
if type(input) ~= "string" then local out = {}
return nil for i = 1, strlen(str), width do
table.insert(out, strsub(str, i, i + width - 1))
end end
local len = strlen(input) return table.concat(out, "\n")
if len <= 1 then
return "u"..input
end
local dict = {}
for i = 0, 255 do
local ic, iic = strchar(i), strchar(i, 0)
dict[ic] = iic
end
local a, b = 0, 1
local result = {"c"}
local resultlen = 1
local n = 2
local word = ""
for i = 1, len do
local c = strsub(input, i, i)
local wc = word..c
if not dict[wc] then
local write = dict[word]
if not write then
return nil
end
result[n] = write
resultlen = resultlen + strlen(write)
n = n+1
if len <= resultlen then
return "u"..input
end
local str = wc
if a >= 256 then
a, b = 0, b+1
if b >= 256 then
dict = {}
b = 1
end
end
dict[str] = strchar(a,b)
a = a+1
word = c
else
word = wc
end
end
result[n] = dict[word]
resultlen = resultlen+strlen(result[n])
n = n+1
if len <= resultlen then
return "u"..input
end
return table.concat(result)
end end
local function Encode(tbl)
return MAGIC .. wrap(C_EncodingUtil.EncodeBase64(
C_EncodingUtil.CompressString(
C_EncodingUtil.SerializeCBOR(tbl))), 92)
end
local function Decode(text)
if not text then return nil end
text = gsub(text, "%s", "")
if strsub(text, 1, strlen(MAGIC)) ~= MAGIC then return nil end
local ok, result = pcall(function()
return C_EncodingUtil.DeserializeCBOR(
C_EncodingUtil.DecompressString(
C_EncodingUtil.DecodeBase64(strsub(text, strlen(MAGIC) + 1))))
end)
if ok and type(result) == "table" then return result end
return nil
end
local function DecodeJSON(text)
if not text or gsub(text, "%s", "") == "" then return nil end
local ok, result = pcall(function()
return C_EncodingUtil.DeserializeJSON(text)
end)
if ok and type(result) == "table" then return result end
return nil
end
-- Legacy import (pre-!pf1! exports): base64 -> LZW -> Lua source
-- "pfUI_config = {...}". The base64 layer is standard and handled by
-- C_EncodingUtil.DecodeBase64; only the custom LZW format needs the old
-- Lua decoder. Import-only — new exports always use the CBOR pipeline.
local function decompress(input) local function decompress(input)
-- based on Rochet2's lzw compression -- based on Rochet2's lzw compression
if type(input) ~= "string" or strlen(input) < 1 then if type(input) ~= "string" or strlen(input) < 1 then
@@ -159,92 +144,32 @@ pfUI:RegisterModule("share", function ()
return table.concat(result) return table.concat(result)
end end
local function enc(to_encode) local function DecodeLegacy(text)
local index_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' if not text or gsub(text, "%s", "") == "" then return nil end
local bit_pattern = ''
local encoded = ''
local trailing = ''
for i = 1, string.len(to_encode) do -- encoded blob? peel base64 + LZW down to Lua source. Raw (already
local remaining = tonumber(string.byte(string.sub(to_encode, i, i))) -- decoded) source pastes are accepted as-is.
local bin_bits = '' local source = text
for i = 7, 0, -1 do local stripped = gsub(text, "%s", "")
local current_power = math.pow(2, i) local ok, decoded = pcall(function()
if remaining >= current_power then return C_EncodingUtil.DecodeBase64(stripped)
bin_bits = bin_bits .. '1' end)
remaining = remaining - current_power if ok and decoded then
else local decompressed = decompress(decoded)
bin_bits = bin_bits .. '0' if decompressed then source = decompressed end
end
end
bit_pattern = bit_pattern .. bin_bits
end end
if mod(string.len(bit_pattern), 3) == 2 then local chunk = loadstring(source)
trailing = '==' if not chunk then return nil end
bit_pattern = bit_pattern .. '0000000000000000'
elseif mod(string.len(bit_pattern), 3) == 1 then
trailing = '='
bit_pattern = bit_pattern .. '00000000'
end
local count = 0 -- Sandbox the chunk: an empty environment means it can assign its
for i = 1, string.len(bit_pattern), 6 do -- config table but can't reach any global or API function — a legacy
local byte = string.sub(bit_pattern, i, i+5) -- profile string is data, never code.
local offset = tonumber(tonumber(byte, 2)) local sandbox = {}
encoded = encoded .. string.sub(index_table, offset+1, offset+1) setfenv(chunk, sandbox)
count = count + 1 if not pcall(chunk) then return nil end
if count >= 92 then if type(sandbox.pfUI_config) == "table" then return sandbox.pfUI_config end
encoded = encoded .. "\n" return nil
count = 0
end
end
return string.sub(encoded, 1, -1 - string.len(trailing)) .. trailing
end
local function dec(to_decode)
local index_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
local padded = gsub(to_decode,"%s", "")
local unpadded = gsub(padded,"=", "")
local bit_pattern = ''
local decoded = ''
to_decode = gsub(to_decode,"\n", "")
to_decode = gsub(to_decode," ", "")
for i = 1, string.len(unpadded) do
local char = string.sub(to_decode, i, i)
local offset, _ = string.find(index_table, char)
if offset == nil then return nil end
local remaining = tonumber(offset-1)
local bin_bits = ''
for i = 7, 0, -1 do
local current_power = math.pow(2, i)
if remaining >= current_power then
bin_bits = bin_bits .. '1'
remaining = remaining - current_power
else
bin_bits = bin_bits .. '0'
end
end
bit_pattern = bit_pattern .. string.sub(bin_bits, 3)
end
for i = 1, string.len(bit_pattern), 8 do
local byte = string.sub(bit_pattern, i, i+7)
decoded = decoded .. strchar(tonumber(byte, 2))
end
local padding_length = string.len(padded)-string.len(unpadded)
if (padding_length == 1 or padding_length == 2) then
decoded = string.sub(decoded,1,-2)
end
return decoded
end end
do -- Window do -- Window
@@ -309,33 +234,43 @@ pfUI:RegisterModule("share", function ()
this:GetParent():UpdateScrollChildRect() this:GetParent():UpdateScrollChildRect()
this:GetParent():UpdateScrollState() this:GetParent():UpdateScrollState()
local _, error = loadstring(f.scroll.text:GetText()) local text = this:GetText()
if error or string.gsub(this:GetText(), " ", "") == "" then local blob = Decode(text)
f.loadButton:Disable() local json = not blob and DecodeJSON(text) or nil
f.loadButton.text:SetTextColor(1,.5,.5,1) local legacy = not blob and not json and DecodeLegacy(text) or nil
else
if blob or json or legacy then
f.loadButton:Enable() f.loadButton:Enable()
f.loadButton.text:SetTextColor(.5,1,.5,1) f.loadButton.text:SetTextColor(.5,1,.5,1)
else
f.loadButton:Disable()
f.loadButton.text:SetTextColor(1,.5,.5,1)
end end
local trydec = dec(this:GetText()) if blob or legacy then
if string.gsub(this:GetText(), " ", "") == "" then -- decoding a legacy string yields the JSON view; re-encoding it
f.readButton.text:SetText(T["N/A"]) -- from there produces a new-format blob (migration path)
f.readButton:Disable()
elseif not trydec or trydec == "" then
f.readButton:Enable()
f.readButton.text:SetText(T["Encode"])
f.readButton.func = function()
local compressed = enc(compress(f.scroll.text:GetText()))
f.scroll.text:SetText(compressed)
end
else
f.readButton:Enable() f.readButton:Enable()
f.readButton.text:SetText(T["Decode"]) f.readButton.text:SetText(T["Decode"])
f.readButton.func = function() f.readButton.func = function()
local uncompressed = decompress(dec(f.scroll.text:GetText())) local current = f.scroll.text:GetText()
f.scroll.text:SetText(uncompressed) local config = Decode(current) or DecodeLegacy(current)
if config then
f.scroll.text:SetText(C_EncodingUtil.SerializeJSON(config))
end
end end
elseif json then
f.readButton:Enable()
f.readButton.text:SetText(T["Encode"])
f.readButton.func = function()
local config = DecodeJSON(f.scroll.text:GetText())
if config then
f.scroll.text:SetText(Encode(config))
end
end
else
f.readButton:Disable()
f.readButton.text:SetText(T["N/A"])
end end
end) end)
f.scroll:SetScrollChild(f.scroll.text) f.scroll:SetScrollChild(f.scroll.text)
@@ -387,21 +322,23 @@ pfUI:RegisterModule("share", function ()
f.loadButton.text:SetFont(pfUI.font_default, pfUI_config.global.font_size, "OUTLINE") f.loadButton.text:SetFont(pfUI.font_default, pfUI_config.global.font_size, "OUTLINE")
f.loadButton.text:SetText(T["Import"]) f.loadButton.text:SetText(T["Import"])
f.loadButton:SetScript("OnClick", function() f.loadButton:SetScript("OnClick", function()
local ImportConfig, error = loadstring(f.scroll.text:GetText()) local text = f.scroll.text:GetText()
if not error and f.scroll.text:GetText() ~= "" then local config = Decode(text) or DecodeJSON(text) or DecodeLegacy(text)
ImportConfig() if not config then return end
pfUI:LoadConfig()
-- Skip firstrun wizard when importing a shared profile _G.pfUI_config = config
-- The imported config is a complete setup, no wizard needed C = _G.pfUI_config
if pfUI.firstrun and pfUI.firstrun.steps then pfUI:LoadConfig()
for _, step in pairs(pfUI.firstrun.steps) do
pfUI_init[step.name] = true -- Skip firstrun wizard when importing a shared profile
end -- The imported config is a complete setup, no wizard needed
if pfUI.firstrun and pfUI.firstrun.steps then
for _, step in pairs(pfUI.firstrun.steps) do
pfUI_init[step.name] = true
end end
CreateQuestionDialog(T["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"], ReloadUI)
end end
CreateQuestionDialog(T["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"], ReloadUI)
end) end)
end end
@@ -445,16 +382,15 @@ pfUI:RegisterModule("share", function ()
ignored["position"] = f.ignorePosition:GetChecked() ignored["position"] = f.ignorePosition:GetChecked()
ignored["disabled"] = f.ignorePosition:GetChecked() ignored["disabled"] = f.ignorePosition:GetChecked()
local compressed = enc(compress(serialize(myconfig, defconfig, "pfUI_config", ignored))) local encoded = Encode(DiffConfig(myconfig, defconfig, ignored) or {})
f.scroll.text:SetText(compressed) f.scroll.text:SetText(encoded)
f.scroll.text.value = compressed f.scroll.text.value = encoded
f.scroll:SetVerticalScroll(0) f.scroll:SetVerticalScroll(0)
end) end)
end end
_G.SLASH_PFEXPORT1, _G.SLASH_PFEXPORT2, _G.SLASH_PFEXPORT3 = "/export", "/import", "/share" pfUI.api.RegisterSlashCommand("PFEXPORT", { "/export", "/import", "/share" }, function(msg, editbox)
function SlashCmdList.PFEXPORT(msg, editbox)
f:Show() f:Show()
end end, true)
end end
end) end)
+11 -7
View File
@@ -2,8 +2,7 @@
-- https://github.com/balakethelock/SuperWoW -- https://github.com/balakethelock/SuperWoW
-- DLL Status Check Command (always available) -- DLL Status Check Command (always available)
SLASH_PFDLLSTATUS1 = "/pfdll" pfUI.api.RegisterSlashCommand("PFDLLSTATUS", { "/pfdll" }, function()
SlashCmdList["PFDLLSTATUS"] = function()
local chat = DEFAULT_CHAT_FRAME local chat = DEFAULT_CHAT_FRAME
chat:AddMessage("|cff33ffccpfUI|r: DLL Status Check") chat:AddMessage("|cff33ffccpfUI|r: DLL Status Check")
@@ -23,6 +22,13 @@ SlashCmdList["PFDLLSTATUS"] = function()
chat:AddMessage(" |cffff0000Nampower|r: Not detected") chat:AddMessage(" |cffff0000Nampower|r: Not detected")
end end
-- ClassicAPI
if CLASSIC_API_VERSION then
chat:AddMessage(" |cff00ff00ClassicAPI|r: v" .. CLASSIC_API_VERSION)
else
chat:AddMessage(" |cffff0000ClassicAPI|r: Not detected")
end
-- Check if castbar exists for indicator positioning -- Check if castbar exists for indicator positioning
if pfUI.castbar and pfUI.castbar.player then if pfUI.castbar and pfUI.castbar.player then
chat:AddMessage(" |cff00ff00Castbar|r: Available for indicator anchoring") chat:AddMessage(" |cff00ff00Castbar|r: Available for indicator anchoring")
@@ -36,7 +42,7 @@ SlashCmdList["PFDLLSTATUS"] = function()
else else
chat:AddMessage(" |cffff0000Target frame|r: NOT found") chat:AddMessage(" |cffff0000Target frame|r: NOT found")
end end
end end, true)
pfUI:RegisterModule("superwow", function () pfUI:RegisterModule("superwow", function ()
if SetAutoloot and SpellInfo and not SUPERWOW_VERSION then if SetAutoloot and SpellInfo and not SUPERWOW_VERSION then
@@ -166,12 +172,10 @@ pfUI:RegisterModule("superwow", function ()
end end
-- Add slash command for clickthrough toggle -- Add slash command for clickthrough toggle
_G.SLASH_PFCLICKTHROUGH1 = "/clickthrough" pfUI.api.RegisterSlashCommand("PFCLICKTHROUGH", { "/clickthrough", "/ct" }, function()
_G.SLASH_PFCLICKTHROUGH2 = "/ct"
SlashCmdList["PFCLICKTHROUGH"] = function()
local enabled = pfUI.api.ToggleClickthrough() local enabled = pfUI.api.ToggleClickthrough()
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: Clickthrough mode " .. (enabled and "|cff00ff00enabled|r" or "|cffff0000disabled|r")) DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: Clickthrough mode " .. (enabled and "|cff00ff00enabled|r" or "|cffff0000disabled|r"))
end end, true)
end end
end) end)
+145 -102
View File
@@ -1,4 +1,3 @@
pfUI:RegisterNewModule("swingtimer", "Swing Timer")
pfUI:RegisterModule("swingtimer", function () pfUI:RegisterModule("swingtimer", function ()
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
@@ -10,6 +9,11 @@ pfUI:RegisterModule("swingtimer", function ()
local ON_SWING_QUEUED = 0 local ON_SWING_QUEUED = 0
local ON_SWING_QUEUE_POPPED = 1 local ON_SWING_QUEUE_POPPED = 1
-- Spell.dbc bits used to mirror server-side swing-reset rules.
local FLAG_AUTOATTACK = tonumber("0x08", 16) -- SPELL_INTERRUPT_FLAG_AUTOATTACK
local ATTR_KEEP_SWINGS = tonumber("0x20000", 16) -- SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS
local ATTR_ON_NEXT_SWING = tonumber("0x04", 16) -- SPELL_ATTR_ON_NEXT_SWING
-- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max) -- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
local S = { local S = {
mhTimer = 0, mhTimerMax = 1, mhTimer = 0, mhTimerMax = 1,
@@ -23,60 +27,35 @@ pfUI:RegisterModule("swingtimer", function ()
pendingCastSpellId = nil, pendingCastSpellId = nil,
mhFrozenAt = nil, mhFrozenAt = nil,
hsQueued = false, cleaveQueued = false, maulQueued = false, hsQueued = false, cleaveQueued = false, maulQueued = false,
hsSeenCurrent = false, cleaveSeenCurrent = false, maulSeenCurrent = false,
isWarrior = false, isWarrior = false,
isDruid = false, isDruid = false,
cachedHSSlots = {}, cachedCleaveSlots = {}, cachedHSSlots = {}, cachedCleaveSlots = {}, cachedMaulSlots = {},
useSpellQueueEvent = false, useSpellQueueEvent = false,
playerGUID = nil,
swingThrottle = 0, swingThrottle = 0,
onSwingCache = {}, onSwingCache = {},
} }
-- Ranged spell IDs -- Wand "Shoot" runs on the ranged bar but is INDEPENDENT of the melee
local RANGED_SPELLIDS = { -- swing clock — casters melee-weave between mainhand swings and wand
[75] = true, -- Auto Shot (Hunter) -- fires, both timers tick concurrently. Every other ranged-auto-attack
[2764] = true, -- Throw (Warrior/Rogue) -- (Hunter Auto Shot, any future auto-repeat ranged spell) replaces MH.
} local WAND_SHOOT_SPELLID = 5019
local THROW_SPELLID = 2764 -- one-shot ranged, not auto-repeat
-- Spells that DELAY the swing timer by their cast duration but do NOT reset it. -- ATTR_ON_NEXT_SWING: spell replaces next auto-attack swing.
-- Slam: vanilla behavior on Turtle WoW - delays swing, does not reset.
-- Hammer of Wrath: Turtle WoW changed behavior - does not reset swing timer.
local swingDelaySpells = {
[1464] = true, [8820] = true, [11604] = true, [11605] = true, -- Slam R1-R4
[24275] = true, [24274] = true, [24239] = true, -- Hammer of Wrath R1-R3
}
-- SPELL_ATTR_ON_NEXT_SWING (bit 2, value 4): spell replaces next auto-attack swing.
-- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically. -- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically.
local ATTR_ON_NEXT_SWING = 4
local function IsOnSwingSpell(spellId) local function IsOnSwingSpell(spellId)
if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end
local rec = GetSpellRec(spellId) local attr = GetSpellRecField(spellId, "attributes") or 0
local result = rec and bit.band(rec.attributes, ATTR_ON_NEXT_SWING) ~= 0 or false local result = bit.band(attr, ATTR_ON_NEXT_SWING) ~= 0
S.onSwingCache[spellId] = result S.onSwingCache[spellId] = result
return result return result
end end
-- Heroic Strike spell IDs (all ranks) -- HS / Cleave / Maul are detected dynamically via IsOnSwingSpell (the
local hsSpellIDs = { -- SPELL_ATTR_ON_NEXT_SWING bit) + name comparison against the rank-1
[78] = true, [284] = true, [285] = true, [1608] = true, -- canonical names cached below — no per-rank ID maintenance.
[11564] = true, [11565] = true, [11566] = true, [11567] = true,
}
-- Cleave spell IDs (all ranks)
local cleaveSpellIDs = {
[845] = true, [7369] = true, [11608] = true, [11609] = true,
[20569] = true,
}
-- Maul spell IDs (all ranks 1 to 7)
local maulSpellIDs = {
[6807] = true,
[6808] = true,
[6809] = true,
[8972] = true,
[9745] = true, [9880] = true, [9881] = true,
}
-- Read config -- Read config
local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200 local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200
@@ -355,14 +334,17 @@ pfUI:RegisterModule("swingtimer", function ()
pfUI.swingtimer:Show() pfUI.swingtimer:Show()
end end
-- Reset ranged countdown -- Reset ranged countdown. replaceMH=true (Hunter Auto Shot, Throw) stops
local function ResetRanged() -- the melee swing clock while ranged ticks; replaceMH=false (wand Shoot)
-- leaves it running for melee weaving.
local function ResetRanged(replaceMH)
if not sw_showranged then return end if not sw_showranged then return end
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
if S.raSpeed <= 0 then return end if S.raSpeed <= 0 then return end
-- Ranged replaces MH bar if replaceMH then
S.mhActive = false S.mhActive = false
pfUI.swingtimer.mainhand:Hide() pfUI.swingtimer.mainhand:Hide()
end
S.raTimerMax = S.raSpeed S.raTimerMax = S.raSpeed
S.raTimer = S.raSpeed S.raTimer = S.raSpeed
S.raActive = true S.raActive = true
@@ -406,21 +388,48 @@ pfUI:RegisterModule("swingtimer", function ()
pfUI.swingtimer:Hide() pfUI.swingtimer:Hide()
end end
-- HS/Cleave helpers. Canonical rank-1 spellIDs resolve to the localized -- HS/Cleave/Maul helpers. Canonical rank-1 spellIDs resolve to the
-- spell name once, so the per-slot comparison is locale-independent without -- localized spell name once, so per-spell classification is rank- and
-- per-rank hardcoding (every rank of Heroic Strike returns the same name). -- locale-independent without per-rank ID hardcoding (every rank of
local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1) -- Heroic Strike etc. returns the same name).
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1) local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1)
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1)
local MAUL_NAME = C_Spell.GetSpellName(6807) -- Maul (Rank 1)
-- Classify an on-next-swing spell as "hs" / "cleave" / "maul" / nil.
-- IsOnSwingSpell gates the family (ATTR_ON_NEXT_SWING bit 0x04) so
-- unrelated spells return nil cheaply.
local function ClassifyOnSwingSpell(spellId)
if not IsOnSwingSpell(spellId) then return nil end
local name = C_Spell.GetSpellName(spellId)
if name == HS_NAME then return "hs"
elseif name == CLEAVE_NAME then return "cleave"
elseif name == MAUL_NAME then return "maul"
end
end
-- Set the queue flags from a ClassifyOnSwingSpell result. nil = no-op
-- (preserves prior flag state when the queued spell isn't one we
-- color-code, matching the legacy table-lookup behavior).
local function SetQueuedKind(kind)
if not kind then return end
S.hsQueued = (kind == "hs")
S.cleaveQueued = (kind == "cleave")
S.maulQueued = (kind == "maul")
S.hsSeenCurrent, S.cleaveSeenCurrent, S.maulSeenCurrent = false, false, false
end
local function RebuildQueueSlotCache() local function RebuildQueueSlotCache()
if not S.isWarrior or not sw_hsqueue or S.useSpellQueueEvent then return end if not sw_hsqueue then return end
S.cachedHSSlots = {} S.cachedHSSlots = {}
S.cachedCleaveSlots = {} S.cachedCleaveSlots = {}
S.cachedMaulSlots = {}
if not (S.isWarrior or S.isDruid) then return end
for slot = 1, 120 do for slot = 1, 120 do
local kind, id = GetActionInfo(slot) local kind, id = GetActionInfo(slot)
local name local name
if kind == "spell" then if kind == "spell" then
name = GetSpellInfo(id) name = C_Spell.GetSpellName(id)
elseif kind == "macro" then elseif kind == "macro" then
name = GetMacroSpell(id) name = GetMacroSpell(id)
end end
@@ -428,6 +437,8 @@ pfUI:RegisterModule("swingtimer", function ()
table.insert(S.cachedHSSlots, slot) table.insert(S.cachedHSSlots, slot)
elseif name == CLEAVE_NAME then elseif name == CLEAVE_NAME then
table.insert(S.cachedCleaveSlots, slot) table.insert(S.cachedCleaveSlots, slot)
elseif name == MAUL_NAME then
table.insert(S.cachedMaulSlots, slot)
end end
end end
end end
@@ -439,9 +450,30 @@ pfUI:RegisterModule("swingtimer", function ()
return false return false
end end
-- Reconcile a stale event-driven queue flag against the client's current
-- action. Not every de-queue emits a SPELL_QUEUE pop — pressing Esc or
-- re-pressing to cancel an on-swing spell doesn't — so the flag alone stays
-- set. IsCurrentAction, which the client clears on cancel, is the reconciling
-- signal, but only after it has confirmed the ability as current at least once
-- (`seen`): a nampower-initiated cast may never flip IsCurrentAction, and must
-- keep its color until its own pop/resolve rather than be cleared early.
-- Returns the updated (queued, seen).
local function ReconcileQueued(queued, slots, seen)
if not queued then return false, false end
if CheckQueuedAction(slots) then return true, true end
if seen then return false, false end -- was current, now gone -> cancelled
return true, false -- never confirmed current -> keep
end
local function IsHSOrCleaveQueued() local function IsHSOrCleaveQueued()
if not sw_hsqueue or not S.isWarrior then return false, false end if not sw_hsqueue or not S.isWarrior then return false, false end
if S.useSpellQueueEvent then return S.hsQueued, S.cleaveQueued end if S.useSpellQueueEvent then
S.hsQueued, S.hsSeenCurrent =
ReconcileQueued(S.hsQueued, S.cachedHSSlots, S.hsSeenCurrent)
S.cleaveQueued, S.cleaveSeenCurrent =
ReconcileQueued(S.cleaveQueued, S.cachedCleaveSlots, S.cleaveSeenCurrent)
return S.hsQueued, S.cleaveQueued
end
return CheckQueuedAction(S.cachedHSSlots), CheckQueuedAction(S.cachedCleaveSlots) return CheckQueuedAction(S.cachedHSSlots), CheckQueuedAction(S.cachedCleaveSlots)
end end
@@ -520,6 +552,10 @@ pfUI:RegisterModule("swingtimer", function ()
-- HS/Cleave color -- HS/Cleave color
local curR, curG, curB = mhDefaultR, mhDefaultG, mhDefaultB local curR, curG, curB = mhDefaultR, mhDefaultG, mhDefaultB
if sw_hsqueue then if sw_hsqueue then
if S.isDruid and S.useSpellQueueEvent then
S.maulQueued, S.maulSeenCurrent =
ReconcileQueued(S.maulQueued, S.cachedMaulSlots, S.maulSeenCurrent)
end
if S.maulQueued and S.isDruid then if S.maulQueued and S.isDruid then
curR, curG, curB = 1.0, 0.55, 0.0 -- orange for druid maul queue curR, curG, curB = 1.0, 0.55, 0.0 -- orange for druid maul queue
elseif S.isWarrior then elseif S.isWarrior then
@@ -694,11 +730,17 @@ pfUI:RegisterModule("swingtimer", function ()
local spellStartFrame = CreateFrame("Frame") local spellStartFrame = CreateFrame("Frame")
spellStartFrame:RegisterEvent("SPELL_START_SELF") spellStartFrame:RegisterEvent("SPELL_START_SELF")
spellStartFrame:SetScript("OnEvent", function() spellStartFrame:SetScript("OnEvent", function()
if arg1 and arg1 > 0 then if not (arg1 and arg1 > 0) then return end
S.pendingCastSpellId = arg1 S.pendingCastSpellId = arg1
-- Slam (and other delay-only spells): freeze the swing timer during cast -- Freeze the swing timer for cast-time spells that DON'T reset auto-
-- so it pauses instead of ticking down and expiring mid-cast -- attack on completion (Slam, Hammer of Wrath on Turtle, etc.) — those
if swingDelaySpells[arg1] and S.mhActive then -- let the swing resume from where it paused. Detect dynamically via the
-- absent AUTOATTACK interrupt flag (8); spells with that bit reset on
-- SPELL_GO_SELF so freezing isn't necessary. Subsumes the old hardcoded
-- swingDelaySpells list (no list maintenance for new Slam-style spells).
if S.mhActive then
local iflags = GetSpellRecField(arg1, "interruptFlags") or 0
if bit.band(iflags, FLAG_AUTOATTACK) == 0 then
S.mhFrozenAt = GetTime() S.mhFrozenAt = GetTime()
end end
end end
@@ -718,52 +760,60 @@ pfUI:RegisterModule("swingtimer", function ()
-- SPELL_GO hook via libdebuff -- SPELL_GO hook via libdebuff
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {} pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
pfUI.libdebuff_spell_go_hooks["swingtimer"] = function(spellId) pfUI.libdebuff_spell_go_hooks["swingtimer"] = function(spellId)
if RANGED_SPELLIDS[spellId] then -- C_Spell.IsRangedAutoAttackSpell catches both Auto Shot (75) and
ResetRanged() -- wand Shoot (5019) via Spell.dbc's AUTO_REPEAT attribute (covers
-- any future auto-repeat ranged spell automatically). Wand is the
-- one independent of the MH swing — everything else replaces it.
-- Throw isn't auto-repeat (single-shot) so it's handled explicitly.
if C_Spell.IsRangedAutoAttackSpell(spellId) then
ResetRanged(spellId ~= WAND_SHOOT_SPELLID)
return return
elseif swingDelaySpells[spellId] then elseif spellId == THROW_SPELLID then
-- Swing-delay spells (Slam, Hammer of Wrath on Turtle WoW): ResetRanged(true)
-- Delay the swing timer by cast duration, do NOT reset it.
if S.mhFrozenAt then
local castDuration = GetTime() - S.mhFrozenAt
S.mhTimer = S.mhTimer + castDuration
S.mhTimerMax = S.mhTimerMax + castDuration
S.mhFrozenAt = nil
end
S.pendingCastSpellId = nil
return return
elseif hsSpellIDs[spellId] or cleaveSpellIDs[spellId] or maulSpellIDs[spellId] or IsOnSwingSpell(spellId) then elseif IsOnSwingSpell(spellId) then
S.hsQueued = false; S.cleaveQueued = false -- On-next-swing ability (HS / Cleave / Maul / Raptor Strike / etc.)
S.maulQueued = false -- — the swing fires as the spell consumes it. Drop the queued color.
ResetMH() S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
elseif cleaveSpellIDs[spellId] then
S.hsQueued = false; S.cleaveQueued = false
ResetMH() ResetMH()
else else
-- Any spell with interruptFlags > 0 resets the swing timer -- Mirror the server rule for "does this spell reset the auto-attack
-- (Moonfire, Faerie Fire, Wrath, Starfire etc. - NOT Insect Swarm which has flags=0) -- swing" (Spell::IsMeleeAttackResetSpell in Turtle's core):
local _rec = GetSpellRec(spellId) -- InterruptFlags has SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08)
if _rec and _rec.interruptFlags and _rec.interruptFlags > 0 then -- AND AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS (0x20000).
-- If neither path resets and we're holding a frozen-swing-during-cast
-- (mhFrozenAt set by SPELL_START_SELF for non-AUTOATTACK spells), this
-- is a Slam-style cast — push the timer forward by the cast duration
-- so the bar resumes from where it paused.
local iflags = GetSpellRecField(spellId, "interruptFlags") or 0
if bit.band(iflags, FLAG_AUTOATTACK) ~= 0
and bit.band(GetSpellRecField(spellId, "attributesEx2") or 0, ATTR_KEEP_SWINGS) == 0 then
if S.mhActive and S.mhSpeed > 0 then if S.mhActive and S.mhSpeed > 0 then
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
S.mhTimerMax = S.mhSpeed S.mhTimerMax = S.mhSpeed
S.mhTimer = S.mhSpeed S.mhTimer = S.mhSpeed
end end
if S.ohActive and S.ohSpeed > 0 then
S.ohTimerMax = S.ohSpeed
S.ohTimer = S.ohSpeed
end
elseif S.mhFrozenAt then
local castDuration = GetTime() - S.mhFrozenAt
S.mhTimer = S.mhTimer + castDuration
S.mhTimerMax = S.mhTimerMax + castDuration
S.mhFrozenAt = nil
end end
end end
S.pendingCastSpellId = nil S.pendingCastSpellId = nil
end end
-- SPELL_CAST_EVENT hook: HS/Cleave queue tracking -- SPELL_CAST_EVENT hook: HS/Cleave/Maul queue tracking
pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {} pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {}
pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId) pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId)
if hsSpellIDs[spellId] then local kind = ClassifyOnSwingSpell(spellId)
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false if not kind then return end
elseif cleaveSpellIDs[spellId] then S.useSpellQueueEvent = true
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false SetQueuedKind(kind)
elseif maulSpellIDs[spellId] then
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
end
end end
@@ -771,7 +821,7 @@ pfUI:RegisterModule("swingtimer", function ()
events:RegisterEvent("AUTO_ATTACK_SELF") events:RegisterEvent("AUTO_ATTACK_SELF")
events:RegisterEvent("AUTO_ATTACK_OTHER") events:RegisterEvent("AUTO_ATTACK_OTHER")
events:RegisterEvent("PLAYER_ENTERING_WORLD") events:RegisterEvent("PLAYER_ENTERING_WORLD")
events:RegisterEvent("UNIT_INVENTORY_CHANGED") events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: per-slot, equipment-only
events:RegisterEvent("PLAYER_REGEN_DISABLED") events:RegisterEvent("PLAYER_REGEN_DISABLED")
events:RegisterEvent("PLAYER_REGEN_ENABLED") events:RegisterEvent("PLAYER_REGEN_ENABLED")
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED") events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
@@ -814,8 +864,7 @@ pfUI:RegisterModule("swingtimer", function ()
elseif event == "AUTO_ATTACK_OTHER" then elseif event == "AUTO_ATTACK_OTHER" then
-- Parry haste: enemy attacked the player and player parried -- Parry haste: enemy attacked the player and player parried
local targetGuid = arg2 local targetGuid = arg2
if not targetGuid or not S.playerGUID then return end if not targetGuid or not IsPlayerGuid(targetGuid) then return end
if targetGuid ~= S.playerGUID then return end
local victimState = arg5 or 0 local victimState = arg5 or 0
-- VICTIMSTATE_PARRY = 3 -- VICTIMSTATE_PARRY = 3
-- Vanilla: parry reduces the NEXT swing timer by 40% of weapon speed, -- Vanilla: parry reduces the NEXT swing timer by 40% of weapon speed,
@@ -847,13 +896,7 @@ pfUI:RegisterModule("swingtimer", function ()
local spellId = arg2 or 0 local spellId = arg2 or 0
if eventCode == ON_SWING_QUEUED then if eventCode == ON_SWING_QUEUED then
S.useSpellQueueEvent = true S.useSpellQueueEvent = true
if hsSpellIDs[spellId] then SetQueuedKind(ClassifyOnSwingSpell(spellId))
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false
elseif cleaveSpellIDs[spellId] then
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false
elseif maulSpellIDs[spellId] then
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
end
elseif eventCode == ON_SWING_QUEUE_POPPED then elseif eventCode == ON_SWING_QUEUE_POPPED then
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
end end
@@ -868,12 +911,12 @@ pfUI:RegisterModule("swingtimer", function ()
local _, class = UnitClass("player") local _, class = UnitClass("player")
S.isWarrior = (class == "WARRIOR") S.isWarrior = (class == "WARRIOR")
S.isDruid = (class == "DRUID") S.isDruid = (class == "DRUID")
S.playerGUID = UnitGUID("player")
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
RebuildQueueSlotCache() RebuildQueueSlotCache()
elseif event == "UNIT_INVENTORY_CHANGED" then elseif event == "PLAYER_EQUIPMENT_CHANGED" then
if arg1 and arg1 ~= "player" then return end -- arg1 = changed slot; only weapon slots (main/off/ranged) affect swing speed
if not INVSLOTS_EQUIPABLE_IN_COMBAT[arg1] then return end
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
if S.ohSpeed == 0 then if S.ohSpeed == 0 then
S.ohActive = false S.ohActive = false
@@ -898,7 +941,7 @@ pfUI:RegisterModule("swingtimer", function ()
S.maulQueued = false S.maulQueued = false
elseif event == "UNIT_DIED" then elseif event == "UNIT_DIED" then
if arg1 and arg1 == S.playerGUID then if IsPlayerGuid(arg1) then
ResetAll() ResetAll()
end end
end end
+4 -4
View File
@@ -267,7 +267,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
SW_BarFrame1_Selector:Hide() SW_BarFrame1_Selector:Hide()
-- let user select mode by clicking the title -- let user select mode by clicking the title
HookScript(SW_BarFrame1_Title, "OnMouseUp", function() SW_BarFrame1_Title:HookScript("OnMouseUp", function()
local page = SW_Settings and SW_Settings.BarFrames and SW_Settings.BarFrames.SW_BarFrame1 and SW_Settings.BarFrames.SW_BarFrame1.Selected local page = SW_Settings and SW_Settings.BarFrames and SW_Settings.BarFrames.SW_BarFrame1 and SW_Settings.BarFrames.SW_BarFrame1.Selected
if page then if page then
local target = (arg1 == "LeftButton") and (page + 1) or (arg1 == "RightButton") and (page - 1) local target = (arg1 == "LeftButton") and (page + 1) or (arg1 == "RightButton") and (page - 1)
@@ -943,8 +943,8 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(AtlasLootTooltip) CreateBackdropShadow(AtlasLootTooltip)
if pfUI.eqcompare then if pfUI.eqcompare then
HookScript(AtlasLootTooltip, "OnShow", pfUI.eqcompare.GameTooltipShow) pfUI.eqcompare.HookTooltip(AtlasLootTooltip)
HookScript(AtlasLootTooltip, "OnHide", function() AtlasLootTooltip:HookScript("OnHide", function()
ShoppingTooltip1:Hide() ShoppingTooltip1:Hide()
ShoppingTooltip2:Hide() ShoppingTooltip2:Hide()
end) end)
@@ -1182,7 +1182,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
-- use pfUI frames to draw healComm predictions -- use pfUI frames to draw healComm predictions
local pfHookHealCommSendAddonMessage = HealComm.SendAddonMessage local pfHookHealCommSendAddonMessage = HealComm.SendAddonMessage
function HealComm.SendAddonMessage(this, msg) function HealComm.SendAddonMessage(this, msg)
if not UnitInRaid("player") and GetNumPartyMembers() < 1 then if not IsInGroup() then
libpredict:ParseChatMessage(UnitName("player"), msg, "HealComm") libpredict:ParseChatMessage(UnitName("player"), msg, "HealComm")
end end
pfHookHealCommSendAddonMessage(this, msg) pfHookHealCommSendAddonMessage(this, msg)
+14 -14
View File
@@ -3,8 +3,7 @@ pfUI:RegisterModule("tooltip", function ()
pfUI.tooltip = CreateFrame('Frame', "pfTooltip", GameTooltip) pfUI.tooltip = CreateFrame('Frame', "pfTooltip", GameTooltip)
pfUI.tooltip.anchorframe = CreateFrame('Frame', "pfTooltipAnchor", UIParent) pfUI.tooltip.anchorframe = CreateFrame('Frame', "pfTooltipAnchor", UIParent)
pfUI.tooltip.anchorframe:SetWidth(128) pfUI.tooltip.anchorframe:SetSize(128, 72)
pfUI.tooltip.anchorframe:SetHeight(72)
pfUI.tooltip.anchorframe:SetPoint("TOP", UIParent, "TOP", 0, -50) pfUI.tooltip.anchorframe:SetPoint("TOP", UIParent, "TOP", 0, -50)
pfUI.tooltip.anchorframe:Hide() pfUI.tooltip.anchorframe:Hide()
UpdateMovable(pfUI.tooltip.anchorframe) UpdateMovable(pfUI.tooltip.anchorframe)
@@ -30,8 +29,8 @@ pfUI:RegisterModule("tooltip", function ()
-- create mouse follow frame -- create mouse follow frame
if not tooltip.cursor then if not tooltip.cursor then
tooltip.cursor = CreateFrame("Frame", nil, UIParent) tooltip.cursor = CreateFrame("Frame", nil, UIParent)
tooltip.cursor:SetWidth(tonumber(C.tooltip.cursoroffset) * 2) local size = tonumber(C.tooltip.cursoroffset) * 2
tooltip.cursor:SetHeight(tonumber(C.tooltip.cursoroffset) * 2) tooltip.cursor:SetSize(size, size)
tooltip.cursor:SetScript("OnUpdate", function() tooltip.cursor:SetScript("OnUpdate", function()
-- throttle - cursor following doesn't need to be every frame -- throttle - cursor following doesn't need to be every frame
if (this.tick or 0) > GetTime() then return end if (this.tick or 0) > GetTime() then return end
@@ -146,14 +145,6 @@ pfUI:RegisterModule("tooltip", function ()
rhp, rhpmax = hp, hpmax rhp, rhpmax = hp, hpmax
elseif pfUI.libhealth and pfUI.libhealth.enabled then elseif pfUI.libhealth and pfUI.libhealth.enabled then
rhp, rhpmax, estimated = pfUI.libhealth:GetUnitHealthByName(this.name, this.level, tonumber(hp), tonumber(hpmax)) rhp, rhpmax, estimated = pfUI.libhealth:GetUnitHealthByName(this.name, this.level, tonumber(hp), tonumber(hpmax))
elseif MobHealthFrame then
local index = (this.name or "") .. ":" .. (this.level or "")
local ppp = MobHealth_PPP(index)
if perc and ppp and ppp > 0 and not UnitIsUnit("mouseover", "pet") then
rhp = round(hp * ppp)
rhpmax = round(100 * ppp)
estimated = true
end
end end
if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then
@@ -180,8 +171,9 @@ pfUI:RegisterModule("tooltip", function ()
local unit = pfUI.tooltip:GetUnit() local unit = pfUI.tooltip:GetUnit()
if unit == "none" then if unit == "none" then
-- process item tooltips -- process item tooltips
if C.tooltip.itemid == "1" and libtooltip:GetItemID() then if C.tooltip.itemid == "1" and GameTooltip:HasItem() then
GameTooltip:AddLine(T["ItemID"] .. ": " .. libtooltip:GetItemID(), .25,.5,1) local _, _, itemID = GameTooltip:GetItem()
GameTooltip:AddLine(T["ItemID"] .. ": " .. itemID, .25,.5,1)
GameTooltip:Show() GameTooltip:Show()
end end
@@ -241,6 +233,14 @@ pfUI:RegisterModule("tooltip", function ()
end end
end end
if C.tooltip.movespeed == "1" then
local currentSpeed = GetUnitSpeed(unit)
if currentSpeed and currentSpeed > 0 then
local pct = floor(currentSpeed / 7 * 100 + 0.5)
GameTooltip:AddLine(T["Speed"] .. ": " .. pct .. "%", 0.7, 0.7, 1)
end
end
if hp and hpm then if hp and hpm then
if hp >= 1000 then hp = round(hp / 1000, 1) .. "k" end if hp >= 1000 then hp = round(hp / 1000, 1) .. "k" end
if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end
+20 -32
View File
@@ -1,6 +1,4 @@
pfUI:RegisterModule("totems", function () pfUI:RegisterModule("totems", function ()
local _, class = UnitClass("player")
local slots = { local slots = {
[FIRE_TOTEM_SLOT] = { r = .5, g = .2, b = .1 }, [FIRE_TOTEM_SLOT] = { r = .5, g = .2, b = .1 },
[EARTH_TOTEM_SLOT] = { r = .2, g = .4, b = .1 }, [EARTH_TOTEM_SLOT] = { r = .2, g = .4, b = .1 },
@@ -15,50 +13,42 @@ pfUI:RegisterModule("totems", function ()
totems:RefreshList() totems:RefreshList()
end) end)
if class == "SHAMAN" then
-- there's no totem event in vanilla using ticks instead
local eventemu = CreateFrame("Frame")
eventemu:SetScript("OnUpdate", function()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .5 end
totems:RefreshList()
end)
end
totems.OnEnter = function(self) totems.OnEnter = function(self)
if not this.id then return end local id = this:GetID()
local active, name, start, duration, icon = GetTotemInfo(this.id) local spellID = select(7, GetTotemInfo(id))
if not name or not active then return end -- Prüfen ob name gültig ist if not spellID or spellID == 0 then return end
local color = slots[this.id]
GameTooltip:SetOwner(this, "ANCHOR_LEFT") GameTooltip:SetOwner(this, "ANCHOR_LEFT")
GameTooltip:SetText(name, color.r+.2, color.g+.2, color.b+.2) GameTooltip:SetSpell(FindSpellBookSlotByID(spellID))
GameTooltip:AddDoubleLine(T["Left Click"], "|cffffffff" .. T["Recast Totem"])
GameTooltip:AddDoubleLine(T["Right Click"], "|cffffffff" .. T["Target Totem"])
GameTooltip:Show() GameTooltip:Show()
end
totems.OnLeave = function(self)
GameTooltip:Hide()
end end
totems.OnLeave = GameTooltip_Hide
totems.OnClick = function(self) totems.OnClick = function(self)
if this.id and arg1 and arg1 == "LeftButton" then local id = this:GetID()
-- Try to recast totem on left click if arg1 == "LeftButton" then
local active, name, start, duration, icon = GetTotemInfo(this.id) local _, name = GetTotemInfo(id)
if name then CastSpellByName(name) end if name and name ~= "" then CastSpellByName(name) end
elseif arg1 == "RightButton" then
TargetTotem(id)
end end
end end
totems.RefreshList = function(self) totems.RefreshList = function(self)
local count = 0 local count = 0
for i = 1, MAX_TOTEMS do for i = 1, MAX_TOTEMS do
local active, name, start, duration, icon = GetTotemInfo(i) local _, _, start, duration, icon = GetTotemInfo(i)
if active and icon and icon ~= "" then if start and start > 0 and icon and icon ~= "" then
count = count + 1 count = count + 1
local color = slots[i] local color = slots[i]
self.bar[count]:Show() self.bar[count]:Show()
self.bar[count]:SetBackdropBorderColor(color.r, color.g, color.b) self.bar[count]:SetBackdropBorderColor(color.r, color.g, color.b)
self.bar[count].icon:SetTexture(icon) self.bar[count].icon:SetTexture(icon)
self.bar[count].id = i self.bar[count]:SetID(i)
CooldownFrame_SetTimer(self.bar[count].cd, start, duration, 1) CooldownFrame_SetTimer(self.bar[count].cd, start, duration, 1)
end end
@@ -77,7 +67,7 @@ end
self:Show() self:Show()
end end
local count = count and count > 0 and count or MAX_TOTEMS count = count and count > 0 and count or MAX_TOTEMS
if pfUI_config.totems.direction == "HORIZONTAL" then if pfUI_config.totems.direction == "HORIZONTAL" then
self:SetHeight(self.iconsize + self.spacing*2) self:SetHeight(self.iconsize + self.spacing*2)
@@ -114,8 +104,7 @@ end
end end
end end
self.bar[i]:SetHeight(self.iconsize) self.bar[i]:SetSize(self.iconsize, self.iconsize)
self.bar[i]:SetWidth(self.iconsize)
CreateBackdrop(self.bar[i], nil, true) CreateBackdrop(self.bar[i], nil, true)
self.bar[i].icon = self.bar[i].icon or self.bar[i]:CreateTexture(nil, "ARTWORK") self.bar[i].icon = self.bar[i].icon or self.bar[i]:CreateTexture(nil, "ARTWORK")
@@ -123,8 +112,7 @@ end
SetAllPointsOffset(self.bar[i].icon, self.bar[i], 2,-2) SetAllPointsOffset(self.bar[i].icon, self.bar[i], 2,-2)
self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i]) self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i])
self.bar[i].cdbg:SetHeight(self.iconsize - 3) self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3)
self.bar[i].cdbg:SetWidth(self.iconsize - 3)
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0) self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate") self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
self.bar[i].cd.pfCooldownStyleAnimation = 1 self.bar[i].cd.pfCooldownStyleAnimation = 1
+41 -910
View File
@@ -1,5 +1,5 @@
-- skip module initialization on every other client than turtle-wow -- skip module initialization on every other client than turtle-wow
if not TargetHPText or not TargetHPPercText then return end if not TURTLE_WOW_VERSION then return end
pfUI:RegisterModule("turtle-wow", function () pfUI:RegisterModule("turtle-wow", function ()
-- Manage Turtle WoW's GroupUI (Turtle_GroupUI addon) vs pfUI frames. -- Manage Turtle WoW's GroupUI (Turtle_GroupUI addon) vs pfUI frames.
@@ -47,7 +47,7 @@ pfUI:RegisterModule("turtle-wow", function ()
-- Check if pfUI is handling group or raid for the current situation -- Check if pfUI is handling group or raid for the current situation
local function pfUIHandlesGroupOrRaid() local function pfUIHandlesGroupOrRaid()
local disabled = C["disabled"] or {} local disabled = C["disabled"] or {}
if GetNumRaidMembers() > 0 then if IsInRaid() then
if not C.unitframes.raid then return false end if not C.unitframes.raid then return false end
-- Hide Turtle frames if: pfUI raid module is active, OR player disabled visibility -- Hide Turtle frames if: pfUI raid module is active, OR player disabled visibility
if disabled["raid"] == "1" then if disabled["raid"] == "1" then
@@ -86,76 +86,6 @@ pfUI:RegisterModule("turtle-wow", function ()
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"]['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} L["debuffs"]['Deep Wound'] = {[0]=6.0}
-- add custom spell logic to libdebuff
HookScript(libdebuff, "OnEvent", function()
if event == "CHAT_MSG_SPELL_SELF_DAMAGE" then
-- refresh paladin judgements on holy strike
-- taken from: https://github.com/doorknob6/pfUI-turtle/blob/master/modules/debuffs.lua
local holystrike = string.find(string.sub(arg1,6,17), "Holy Strike")
--arg2 is spell dmg when it hits, nil when it misses
if holystrike and arg2 then
for seal in L["judgements"] do
local name = UnitName("target")
local level = UnitLevel("target")
if name and libdebuff.objects[name] then
if level and
libdebuff.objects[name][level] and
libdebuff.objects[name][level][seal] then
libdebuff:AddEffect(name, level, seal)
elseif libdebuff.objects[name][0] and
libdebuff.objects[name][0][seal] then
libdebuff:AddEffect(name, 0, seal)
end
end
end
end
-- refresh rip and rake duration on ferocious bite (Turtle WoW feature)
-- Only refresh if Ferocious Bite actually hit (not missed/dodged/parried/etc.)
local match = string.find(arg1, "Ferocious Bite")
if match and arg2 and not libdebuff:DidSpellFail("Ferocious Bite") then
local name = UnitName("target")
local level = UnitLevel("target")
-- Refresh Rip mit existierender Duration
if libdebuff.objects[name] and libdebuff.objects[name][level] and libdebuff.objects[name][level]["Rip"] then
local existingDuration = libdebuff.objects[name][level]["Rip"].duration
libdebuff:AddEffect(name, level, "Rip", existingDuration)
end
-- Refresh Rake mit existierender Duration
if libdebuff.objects[name] and libdebuff.objects[name][level] and libdebuff.objects[name][level]["Rake"] then
local existingDuration = libdebuff.objects[name][level]["Rake"].duration
libdebuff:AddEffect(name, level, "Rake", existingDuration)
end
end
-- refresh Immolate duration after cast Conflagrate
-- Only refresh if Conflagrate actually hit
local conflagrate = string.find(string.sub(arg1,6,17), "Conflagrate")
if conflagrate and arg2 and not libdebuff:DidSpellFail("Conflagrate") then
local name = UnitName("target")
local level = UnitLevel("target")
if libdebuff.objects[name] and libdebuff.objects[name][level] and libdebuff.objects[name][level]["Immolate"] then
local duration = libdebuff.objects[name][level]["Immolate"].duration
libdebuff:UpdateDuration(name, level, "Immolate", duration - 3)
end
end
end
end)
-- turtle wow totemic recall clear totem indicators
local _, class = UnitClass("player")
if libtotem and class == "SHAMAN" then
local trecall = CreateFrame("Frame", "pfTotemsRecall", UIParent)
trecall:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF")
trecall:SetScript("OnEvent", function()
if arg1 and string.find(arg1, T["You gain (.+) Mana from Totemic Recall"]) then
for i = 1, 4 do libtotem:Clean(i) end
end
end)
end
local delay = CreateFrame("Frame") local delay = CreateFrame("Frame")
delay:SetScript("OnUpdate", function() delay:SetScript("OnUpdate", function()
this:Hide() this:Hide()
@@ -357,36 +287,54 @@ pfUI:RegisterModule("turtle-wow", function ()
-- break if theres nothing left to do -- break if theres nothing left to do
if initialized then return end if initialized then return end
-- adjust ui positions local _, border = GetBorderSize()
-- adjust the inspect frame's Talents tab
SkinTab(InspectFrameTab3) SkinTab(InspectFrameTab3)
InspectFrameTab3:ClearAllPoints() InspectFrameTab3:ClearAllPoints()
InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", GetBorderSize()*2 + 1, 0) InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", border*2 + 1, 0)
TWTalentFrameTab1:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, TWTalentFrameTab1:GetHeight() + 4)
-- reload text position -- reload text position
InspectFrameTab3:Hide() InspectFrameTab3:Hide()
InspectFrameTab3:Show() InspectFrameTab3:Show()
-- skin inspect window elements -- the talent tree frame is created lazily; skin it once it exists
StripTextures(InspectTalentsFrame) if TWTalentFrame then
StripTextures(TWTalentFrameScrollFrame) StripTextures(InspectTalentsFrame)
SkinScrollbar(TWTalentFrameScrollFrameScrollBar) StripTextures(TWTalentFrame)
for i = 1, 3 do StripTextures(TWTalentFrameScrollFrame)
SkinTab(_G["TWTalentFrameTab"..i]) SkinScrollbar(TWTalentFrameScrollFrameScrollBar)
end
-- skin each talent button -- skin + position the talent-tree tabs
for i = 1, (MAX_NUM_TALENTS or 100) do for i = 1, 3 do
local talent = _G["TWTalentFrameTalent" .. i] local tab = _G["TWTalentFrameTab"..i]
if talent then if tab then
StripTextures(talent) SkinTab(tab)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent" .. i .. "IconTexture"]) tab:ClearAllPoints()
_G["TWTalentFrameTalent" .. i .. "Rank"]:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE") local lastTab = _G["TWTalentFrameTab"..(i-1)]
if lastTab then
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else
tab:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, tab:GetHeight() + 4)
end
end
end end
end
-- only run once -- skin each talent button
initialized = true for i = 1, (MAX_NUM_TALENTS or 100) do
local talent = _G["TWTalentFrameTalent"..i]
if talent then
StripTextures(talent)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent"..i.."IconTexture"])
local rank = _G["TWTalentFrameTalent"..i.."Rank"]
if rank then
rank:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
end
end
end
-- only run once the talent frame has actually been skinned
initialized = true
end
end) end)
end) end)
end end
@@ -413,823 +361,6 @@ pfUI:RegisterModule("turtle-wow", function ()
end end
end) end)
-- add turtle-wow overlay values
-- coordinates from paokkerkir/ShaguTweaks-extras, measured for TurtleWoW 1.18.1
pfMapOverlayData = {
["Durotar"] = {
"DRYGULCHRAVINE:139:134:438:92",
"ECHOISLES:175:236:561:432",
"KOLKARCRAG:131:96:426:487",
"ORGRIMMAR:427:150:251:0",
"RAZORHILL:188:207:445:182",
"RAZORMANEGROUNDS:196:202:318:204",
"SENJINVILLAGE:133:167:485:398",
"SKULLROCK:118:79:469:51",
"SparkwaterPort:122:134:522:94",
"THUNDERRIDGE:156:174:341:72",
"TIRAGARDEKEEP:160:148:477:300",
"VALLEYOFTRIALS:186:184:370:335",
},
["Mulgore"] = {
"BAELDUNDIGSITE:175:151:269:227",
"BLOODHOOFVILLAGE:236:182:378:311",
"PALEMANEROCK:102:183:315:317",
"RAVAGEDCARAVAN:110:100:482:269",
"REDCLOUDMESA:436:231:285:437",
"REDROCKS:154:138:522:92",
"SuntailPass:129:116:533:18",
"THEGOLDENPLAINS:184:217:441:91",
"THEROLLINGPLAINS:231:166:536:369",
"THEVENTURECOMINE:187:204:549:251",
"THUNDERBLUFF:254:217:264:70",
"THUNDERHORNWATERWELL:118:137:384:248",
"WILDMANEWATERWELL:152:111:299:10",
"WINDFURYRIDGE:182:119:405:2",
"WINTERHOOFWATERWELL:144:107:470:379",
},
["Barrens"] = {
"AGAMAGOR:183:173:348:242",
"AnchorsEdge:118:83:690:300",
"BAELMODAN:120:123:435:482",
"BLACKTHORNRIDGE:140:123:340:465",
"BOULDERLODEMINE:109:105:560:0",
"BRAMBLESCAR:112:152:446:304",
"CAMPTAURAJO:130:113:370:355",
"DREADMISTPEAK:121:93:423:68",
"FARWATCHPOST:85:152:572:58",
"FIELDOFGIANTS:194:137:363:410",
"GROLDOMFARM:112:107:499:67",
"HONORSSTAND:128:128:306:130",
"LUSHWATEROASIS:160:169:372:186",
"NORTHWATCHFOLD:134:107:533:310",
"RAPTORGROUNDS:101:99:512:299",
"RATCHET:116:118:556:190",
"RAZORFENDOWNS:137:106:414:562",
"RAZORFENKRAUL:116:123:345:539",
"THECROSSROADS:142:140:436:127",
"THEDRYHILLS:185:134:324:36",
"THEFORGOTTENPOOLS:104:116:391:120",
"THEMERCHANTCOAST:80:123:584:249",
"THEMORSHANRAMPART:118:91:418:0",
"THESLUDGEFEN:153:113:463:0",
"THESTAGNANTOASIS:147:126:484:211",
"THORNHILL:128:120:502:123",
},
["Alterac"] = {
"CHILLWINDPOINT:308:250:660:261",
"CORRAHNSDAGGER:173:277:411:391",
"CRUSHRIDGEHOLD:269:230:339:167",
"DALARAN:277:283:39:275",
"DANDREDSFOLD:273:224:282:0",
"GALLOWSCORNER:183:185:415:287",
"GAVINSNAZE:140:161:235:486",
"GROWLESSCAVE:175:156:327:382",
"LORDAMEREINTERNMENTCAMP:315:255:55:413",
"MISTYSHORE:204:268:207:139",
"RUINSOFALTERAC:252:249:274:201",
"RavenholdtManor:217:165:737:452",
"SOFERASNAZE:241:312:469:311",
"STRAHNBRAD:355:289:555:110",
"THEHEADLAND:142:187:325:481",
"THEUPLANDS:219:183:470:84",
},
["Arathi"] = {
"BOULDERFISTHALL:190:214:442:371",
"BOULDERGOR:222:214:244:167",
"CIRCLEOFEASTBINDING:140:213:568:125",
"CIRCLEOFINNERBINDING:186:164:302:321",
"CIRCLEOFOUTERBINDING:149:137:429:301",
"CIRCLEOFWESTBINDING:156:189:152:71",
"DABYRIESFARMSTEAD:160:171:482:197",
"FALDIRSCOVE:247:197:180:428",
"FarwellStead:112:128:267:31",
"GOSHEKFARM:208:177:539:284",
"HAMMERFALL:182:235:665:128",
"NORTHFOLDMANOR:202:205:204:119",
"REFUGEPOINT:157:189:380:214",
"RuinsOfZulRasaz:160:160:346:29",
"STROMGARDEKEEP:220:213:122:295",
"THANDOLSPAN:175:198:369:424",
"THORADINSWALL:173:225:103:148",
"WITHERBARKVILLAGE:197:194:566:339",
"WildtuskVillage:183:102:379:145",
},
["Badlands"] = {
"AGMONDSEND:249:256:354:396",
"ANGORFORTRESS:186:241:327:101",
"APOCRYPHANSREST:251:195:20:315",
"CAMPBOFF:195:207:506:361",
"CAMPCAGG:247:205:15:432",
"CAMPKOSH:214:211:556:56",
"CrystallinePinnacle:140:155:609:337",
"DUSTWINDGULCH:229:190:506:216",
"HAMMERTOESDIGSITE:185:177:455:131",
"KARGATH:232:245:0:154",
"LETHLORRAVINE:344:395:627:173",
"MIRAGEFLATS:276:229:153:387",
"RedbrandsDigsite:325:155:496:454",
"RuinsOfCorthan:210:200:203:124",
"ScalebaneRidge:205:179:751:142",
"THEDUSTBOWL:253:266:166:203",
"THEMAKERSTERRACE:230:172:399:16",
"VALLEYOFFANGS:215:215:358:265",
},
["BlastedLands"] = {
"ALTAROFSTORMS:169:142:318:141",
"DARKPORTAL:254:208:460:265",
"DREADMAULHOLD:200:171:351:21",
"DREADMAULPOST:228:183:370:204",
"GARRISONARMORY:155:192:480:15",
"NETHERGARDEKEEP:169:178:567:35",
"RISEOFTHEDEFILER:150:131:414:131",
"SERPENTSCOIL:211:155:510:148",
"THETAINTEDSCAR:370:438:220:184",
},
["Tirisfal"] = {
"AGAMANDMILLS:244:191:342:148",
"BALNIRFARMSTEAD:179:153:639:335",
"BRIGHTWATERLAKE:176:280:598:143",
"BRILL:121:144:540:305",
"BULWARK:184:169:712:374",
"COLDHEARTHMANOR:136:125:479:328",
"CRUSADEROUTPOST:154:121:701:293",
"DEATHKNELL:210:186:236:337",
"GARRENSHAUNT:148:199:507:154",
"GLENSHIRE:163:161:157:393",
"MONASTARY:175:157:762:139",
"NIGHTMAREVALE:209:151:370:355",
"RUINSOFLORDAERON:299:216:469:370",
"SCARLETWATCHPOST:141:223:701:113",
"SOLLIDENFARMSTEAD:225:138:247:259",
"STEEPCLIFFPORT:121:120:5:350",
"STILLWATERPOND:168:123:401:279",
"THECORINTHFARMSTEAD:116:152:73:402",
"THEWHISPERINGFOREST:243:175:86:263",
"VENOMWEBVALE:199:183:776:225",
},
["Silverpine"] = {
"AMBERMILL:204:210:512:279",
"BERENSPERIL:206:146:508:433",
"DEEPELEMMINE:127:143:484:274",
"FENRISISLE:234:187:593:88",
"MALDENSORCHARD:228:138:479:9",
"NORTHTIDESHOLLOW:152:104:337:141",
"OLSENSFARTHING:128:152:401:267",
"PYREWOODVILLAGE:122:108:405:456",
"SHADOWFANGKEEP:185:131:380:373",
"THEDEADFIELD:141:134:419:82",
"THEDECREPITFERRY:144:156:475:160",
"THEGREYMANEWALL:175:185:398:465",
"THESEPULCHER:193:135:360:184",
"THESHININGSTRAND:225:192:475:27",
"THESKITTERINGDARK:160:165:302:18",
},
["WesternPlaguelands"] = {
"CAERDARROW:158:150:608:421",
"DALSONSTEARS:209:138:388:271",
"DARROWMERELAKE:359:261:510:348",
"FELSTONEFIELD:149:119:306:315",
"GAHRRONSWITHERING:165:191:527:257",
"HEARTHGLEN:331:280:312:20",
"NORTHRIDGELUMBERCAMP:205:168:390:170",
"RUINSOFANDORHOL:275:219:265:360",
"SORROWHILL:290:201:360:467",
"THEBULWARK:216:172:143:300",
"THEWEEPINGCAVE:148:192:572:203",
"THEWRITHINGHAUNT:159:177:457:329",
"THONDRORILRIVER:193:326:597:94",
},
["EasternPlaguelands"] = {
"BLACKWOODLAKE:217:221:451:207",
"CORINSCROSSING:153:146:544:373",
"CROWNGUARDTOWER:193:152:299:408",
"DARROWSHIRE:195:167:316:497",
"EASTWALLTOWER:164:143:600:250",
"ForlornSummit:192:185:64:225",
"LAKEMERELDAR:240:164:543:469",
"LIGHTSHOPECHAPEL:165:238:721:304",
"NORTHDALE:172:191:628:135",
"NORTHPASSTOWER:228:178:465:118",
"PESTILENTSCAR:189:241:416:349",
"PLAGUEWOOD:346:258:178:92",
"QUELLITHIENLODGE:216:136:430:44",
"STRATHOLME:227:189:202:15",
"TERRORDALE:175:129:86:137",
"THEFUNGALVALE:202:199:277:267",
"THEINFECTISSCAR:183:262:628:298",
"THEMARRISSTEAD:182:194:164:367",
"THENOXIOUSGLADE:211:202:730:172",
"THEUNDERCROFT:171:136:178:484",
"THONDRORILRIVER:210:348:12:235",
"TYRSHAND:201:159:724:475",
"ZULMASHAR:189:152:621:36",
},
["Hilsbrad"] = {
"AZURELOADMINE:146:174:188:288",
"DARROWHILL:173:132:426:164",
"DUNGAROK:225:257:644:305",
"DURNHOLDEKEEP:367:347:614:82",
"EASTERNSTRAND:208:301:537:350",
"HILLSBRADFIELDS:282:257:211:166",
"NETHANDERSTEAD:190:215:554:249",
"PURGATIONISLE:120:94:111:485",
"SOUTHPOINTTOWER:271:207:11:203",
"SOUTHSHORE:219:245:423:213",
"TARRENMILL:195:291:524:9",
"WESTERNSTRAND:272:139:215:377",
},
["Hinterlands"] = {
"AERIEPEAK:240:195:20:250",
"AGOLWATHA:188:180:382:173",
"HIRIWATHA:201:118:188:314",
"JINTHAALOR:223:277:514:338",
"PLAGUEMISTRAVINE:124:203:170:158",
"QUELDANILLODGE:168:181:248:194",
"SERADANE:260:263:519:25",
"SHADRAALOR:181:170:247:394",
"SHAOLWATHA:265:190:580:247",
"SKULKROCK:148:133:520:241",
"THEALTAROFZUL:186:153:382:371",
"THECREEPINGRUIN:168:157:417:268",
"THEOVERLOOKCLIFFS:153:299:702:309",
"TheRasazTrails:118:133:210:381",
"VALORWINDLAKE:153:160:329:309",
},
["DunMorogh"] = {
"AMBERSTILLRANCH:105:104:582:288",
"ANVILMAR:209:159:167:414",
"BREWNALLVILLAGE:94:102:263:256",
"CHILLBREEZEVALLEY:160:113:281:304",
"COLDRIDGEPASS:134:123:300:387",
"FROSTMANEHOLD:112:118:225:291",
"GNOMERAGON:158:176:178:164",
"GOLBOLARQUARRY:144:149:619:301",
"HELMSBEDLAKE:131:153:706:284",
"ICEFLOWLAKE:117:167:286:173",
"IRONFORGE:291:187:414:166",
"IronforgeAirfields:213:233:582:100",
"KHARANOS:175:186:397:296",
"MISTYPINEREFUGE:117:155:507:224",
"NORTHERNGATEOUTPOST:115:155:766:177",
"RugfordsMountainRest:199:114:739:406",
"SHIMMERRIDGE:115:173:355:169",
"SOUTHERNGATEOUTPOST:113:106:800:284",
"THEGRIZZLEDDEN:178:155:321:328",
"THETUNDRIDHILLS:135:115:531:328",
},
["SearingGorge"] = {
"BLACKCHARCAVE:264:224:84:371",
"DUSTFIREVALLEY:446:355:428:11",
"FIREWATCHRIDGE:385:413:95:39",
"GRIMSILTDIGSITE:292:210:501:306",
"TANNERCAMP:292:215:552:413",
"THECAULDRON:414:310:257:178",
"THESEAOFCINDERS:345:273:256:395",
},
["BurningSteppes"] = {
"ALTAROFSTORMS:215:210:41:114",
"BLACKROCKMOUNTAIN:249:272:178:104",
"BLACKROCKPASS:259:299:597:285",
"BLACKROCKSTRONGHOLD:232:258:342:120",
"DRACODAR:405:309:61:262",
"DREADMAULROCK:203:214:717:174",
"MORGANSVIGIL:287:265:715:311",
"PILLAROFASH:313:261:382:289",
"RUINSOFTHAURISSAN:258:275:521:105",
"TERRORWINGPATH:271:296:730:52",
},
["Elwynn"] = {
"BRACKWELLPUMPKINPATCH:222:223:592:431",
"CRYSTALLAKE:196:192:435:345",
"EASTVALELOGGINGCAMP:237:190:713:339",
"FARGODEEPMINE:238:230:247:438",
"FORESTSEDGE:242:335:132:333",
"GOLDSHIRE:219:199:261:280",
"JERODSLANDING:206:212:437:445",
"NORTHSHIREVALLEY:237:244:390:154",
"RIDGEPOINTTOWER:287:207:710:445",
"STONECAIRNLAKE:278:230:601:203",
"STORMWIND:452:392:21:3",
"TOWEROFAZORA:232:226:561:303",
},
["DeadwindPass"] = {
"DEADMANSCROSSING:368:353:257:81",
"KARAZHAN:287:234:277:342",
"THEVICE:254:257:433:305",
},
["Duskwood"] = {
"ADDLESSTEAD:255:231:67:353",
"BRIGHTWOODGROVE:191:315:520:131",
"DARKSHIRE:291:259:644:173",
"MANORMISTMANTLE:174:150:668:134",
"RAVENHILL:166:115:117:317",
"RAVENHILLCEMETARY:323:276:99:165",
"THEDARKENEDBANK:886:184:105:44",
"THEHUSHEDBANK:136:306:29:143",
"THEROTTINGORCHARD:231:210:549:380",
"THEYORGENFARMSTEAD:211:235:403:393",
"TRANQUILGARDENSCEMETARY:204:196:692:363",
"TWILIGHTGROVE:327:384:314:99",
"VULGOLOGREMOUND:231:266:259:365",
},
["LochModan"] = {
"GRIZZLEPAWRIDGE:262:343:326:325",
"IRONBANDSEXCAVATIONSITE:321:294:491:276",
"MOGROSHSTRONGHOLD:287:265:558:61",
"NORTHGATEPASS:195:273:143:24",
"SILVERSTREAMMINE:201:243:247:25",
"STONESPLINTERVALLEY:226:255:230:363",
"STONEWROUGHTDAM:260:149:354:23",
"THEFARSTRIDERLODGE:162:182:741:298",
"THELOCH:288:380:367:99",
"THELSAMAR:243:205:225:214",
"VALLEYOFKINGS:166:224:123:382",
},
["Redridge"] = {
"ALTHERSMILL:222:260:407:134",
"GALARDELLVALLEY:234:240:661:168",
"LAKEEVERSTILL:522:260:140:249",
"LAKERIDGEHIGHWAY:411:274:196:341",
"LAKESHIRE:325:179:92:204",
"REDRIDGECANYONS:345:232:132:79",
"RENDERSCAMP:264:248:284:4",
"RENDERSVALLEY:453:248:491:365",
"RedwallKeep:162:110:830:466",
"STONEWATCH:243:289:507:221",
"STONEWATCHFALLS:308:200:601:325",
"THREECORNERS:360:337:0:288",
},
["Stranglethorn"] = {
"BALALRUINS:83:65:245:101",
"BALIAMAHRUINS:100:133:376:133",
"BLOODSAILCOMPOUND:154:163:201:291",
"BOOTYBAY:137:125:207:435",
"CRYSTALVEINMINE:111:114:350:280",
"GROMGOLBASECAMP:101:98:265:137",
"JAGUEROISLE:132:107:319:498",
"KALAIRUINS:86:89:303:93",
"KURZENSCOMPOUND:141:139:394:5",
"LAKENAZFERITI:124:120:332:60",
"MISTVALEVALLEY:120:116:283:372",
"MIZJAHRUINS:93:102:315:133",
"MOSHOGGOGREMOUND:123:168:435:96",
"NEKMANIWELLSPRING:80:102:215:365",
"NESINGWARYSEXPEDITION:128:98:274:29",
"REBELCAMP:164:86:289:0",
"RUINSOFABORAZ:87:87:356:340",
"RUINSOFJUBUWAL:100:105:312:304",
"RUINSOFZULKUNDA:113:132:201:8",
"RUINSOFZULMAMWE:162:122:397:213",
"THEARENA:186:176:241:194",
"THEVILEREEF:177:160:159:98",
"VENTURECOBASECAMP:96:120:391:66",
"WILDSHORE:151:175:237:431",
"ZIATAJAIRUINS:127:120:364:231",
"ZULGURUB:230:213:489:12",
"ZUULDAIARUINS:103:105:160:47",
},
["SwampOfSorrows"] = {
"FALLOWSANCTUARY:348:292:501:0",
"ITHARIUSSCAVE:230:232:0:270",
"MISTYREEDSTRAND:250:664:752:0",
"MISTYVALLEY:211:179:29:148",
"POOLOFTEARS:280:256:576:227",
"SORROWMURK:199:351:734:128",
"SPLINTERSPEARJUNCTION:258:222:138:245",
"STAGALBOG:328:238:561:386",
"STONARD:343:303:287:244",
"SorrowguardKeep:220:174:0:266",
"THEHARBORAGE:216:187:182:157",
"THESHIFTINGMIRE:303:221:294:118",
},
["Westfall"] = {
"ALEXSTONFARMSTEAD:275:182:220:274",
"DEMONTSPLACE:166:155:226:391",
"FURLBROWSPUMPKINFARM:186:191:399:24",
"GOLDCOASTQUARRY:194:241:234:113",
"JANGOLODEMINE:185:187:323:44",
"MOONBROOK:208:174:317:349",
"SALDEANSFARM:191:184:478:119",
"SENTINELHILL:161:210:461:259",
"THEDAGGERHILLS:235:149:349:430",
"THEDEADACRE:164:213:542:267",
"THEDUSTPLAINS:260:204:538:393",
"THEJANSENSTEAD:143:183:498:6",
"THEMOLSENFARM:199:180:342:161",
"WESTFALLLIGHTHOUSE:306:161:171:482",
},
["Wetlands"] = {
"ANGERFANGENCAMPMENT:196:150:364:241",
"BLACKCHANNELMARSH:213:173:95:260",
"BLUEGILLMARSH:195:165:111:157",
"DIREFORGEHILL:233:225:515:128",
"DUNMODR:173:147:419:42",
"DunAgrath:222:190:97:335",
"GRIMBATOL:317:331:623:242",
"HawksVigil:170:162:264:353",
"IRONBEARDSTOMB:165:156:368:135",
"MENETHILHARBOR:156:116:27:321",
"MOSSHIDEFEN:215:224:539:277",
"RAPTORRIDGE:159:115:640:191",
"SALTSPRAYGLEN:170:214:254:58",
"SUNDOWNMARSH:265:212:113:100",
"THEGREENBELT:153:209:473:143",
"THELGANROCK:207:165:478:383",
"WHELGARSEXCAVATIONSITE:172:156:264:224",
},
["Teldrassil"] = {
"BANETHILHOLLOW:136:191:392:289",
"DARNASSUS:294:252:110:251",
"DOLANAAR:170:119:469:327",
"GNARLPINEHOLD:230:158:316:417",
"LAKEALAMETH:237:163:445:388",
"POOLSOFARLITHRIEN:125:180:337:314",
"RUTTHERANVILLAGE:110:91:504:548",
"SHADOWGLEN:198:201:504:164",
"STARBREEZEVILLAGE:153:175:588:303",
"THEORACLEGLADE:150:223:279:135",
"UrsanHeights:229:154:243:423",
"WELLSPRINGLAKE:166:245:383:98",
},
["Darkshore"] = {
"AMETHARAN:163:178:340:323",
"AUBERDINE:134:179:323:191",
"BASHALARAN:153:171:379:199",
"CLIFFSPRINGRIVER:205:161:391:112",
"GROVEOFTHEANCIENTS:180:138:314:436",
"REMTRAVELSEXCAVATION:148:164:245:504",
"RUINSOFMATHYSTRA:176:205:519:3",
"THEMASTERSGLAIVE:146:139:346:529",
"TOWEROFALTHALAXX:142:159:484:110",
},
["Ashenvale"] = {
"ASTRANAAR:190:173:281:259",
"BOUGHSHADOW:140:190:862:157",
"FALLENSKYLAKE:219:190:555:433",
"FELFIREHILL:241:244:720:349",
"FIRESCARSHRINE:153:161:194:332",
"ForestSong:162:142:817:232",
"IRISLAKE:185:190:401:226",
"LAKEFALATHIM:115:181:138:144",
"MAESTRASPOST:198:290:216:46",
"MYSTRALLAKE:262:228:363:354",
"NIGHTRUN:212:244:604:263",
"RAYNEWOODRETREAT:165:235:527:243",
"SATYRNAAR:163:172:702:230",
"THEHOWLINGVALE:196:172:471:147",
"THERUINSOFSTARDUST:141:135:268:382",
"THESHRINEOFAESSINA:205:179:113:268",
"THEZORAMSTRAND:228:235:26:33",
"THISTLEFURVILLAGE:243:184:210:164",
"WARSONGLUMBERCAMP:185:146:804:319",
},
["ThousandNeedles"] = {
"CAMPETHOK:289:300:7:1",
"DARKCLOUDPINNACLE:189:182:267:138",
"FREEWINDPOST:193:173:366:273",
"HIGHPERCH:178:174:39:164",
"SPLITHOOFCRAG:193:175:400:203",
"THEGREATLIFT:191:163:213:78",
"THESCREECHINGCANYON:240:231:187:206",
"THESHIMMERINGFLATS:312:355:612:308",
"WINDBREAKCANYON:225:213:501:249",
},
["StonetalonMountains"] = {
"AMANIALOR:504:252:8:5",
"BAELHARDUL:357:252:512:257",
"BLACKSANDOILFIELDS:504:504:8:5",
"BOULDERSLIDERAVINE:90:76:586:547",
"BRAMBLETHORNPASS:382:359:512:257",
"BROKENCLIFFMINE:252:504:260:5",
"CAMPAPARAJE:222:100:670:552",
"GRIMTOTEMPOST:146:77:675:519",
"MALAKAJIN:137:86:662:562",
"MIRKFALLONLAKE:504:504:260:5",
"POWDERTOWN:252:252:260:257",
"SISHIRCANYON:252:264:512:257",
"STONETALONPEAK:258:252:260:5",
"SUNROCKRETREAT:504:252:260:257",
"THECHARREDVALE:252:308:260:257",
"THEEARTHENRING:504:252:260:257",
"VENTURECOMPANYCAMP:252:504:260:5",
"WEBWINDERPATH:256:324:512:256",
"WINDSHEARCRAG:256:256:512:256",
},
["Desolace"] = {
"ETHELRETHOR:189:237:318:68",
"GELKISVILLAGE:181:235:300:433",
"KODOGRAVEYARD:253:237:396:251",
"KOLKARVILLAGE:206:208:615:221",
"KORMEKSHUT:156:147:562:187",
"MAGRAMVILLAGE:182:272:606:373",
"MANNOROCCOVEN:270:271:408:385",
"NIJELSPOINT:188:246:561:0",
"RANAZJARISLE:88:89:248:11",
"SARGERON:274:237:632:38",
"SHADOWBREAKRAVINE:190:181:698:454",
"SHADOWPREYVILLAGE:215:218:175:395",
"TETHRISARAN:185:137:440:0",
"THUNDERAXEFORTRESS:174:207:455:108",
"VALLEYOFSPEARS:234:274:217:221",
},
["Feralas"] = {
"CAMPMOJACHE:146:148:694:238",
"ChimaeraRoostVale:106:150:785:321",
"DIREMAUL:214:180:461:208",
"DREAMBOUGH:141:122:459:0",
"FERALSCARVALE:103:104:491:335",
"FRAYFEATHERHIGHLANDS:101:163:485:391",
"GORDUNNIOUTPOST:154:128:697:148",
"GRIMTOTEMCOMPOUND:110:186:630:172",
"ISLEOFDREAD:205:285:197:381",
"LOWERWILDS:214:152:756:204",
"ONEIROS:101:103:499:74",
"RUINSOFISILDIEN:179:241:547:326",
"RUINSOFRAVENWIND:178:149:310:0",
"SARDORISLE:167:168:216:241",
"THEFORGOTTENCOAST:132:316:409:258",
"THETWINCOLOSSALS:278:235:323:78",
"THEWRITHINGDEEP:214:205:625:305",
},
["Dustwallow"] = {
"ALCAZISLAND:176:179:671:29",
"BACKBAYWETLANDS:384:243:248:196",
"BRACKENWALLVILLAGE:264:264:239:2",
"THEDENOFFLAME:246:235:264:320",
"THERAMOREISLE:214:187:542:232",
"THEWYRMBOG:263:229:372:387",
"WITCHHILL:230:311:431:1",
"WesthavenHollow:166:149:603:428",
},
["Tanaris"] = {
"ABYSSALSANDS:194:159:376:207",
"BROKENPILLAR:93:165:481:243",
"CAVERNSOFTIME:139:132:567:262",
"DUNEMAULCOMPOUND:186:127:337:298",
"EASTMOONRUINS:143:130:405:355",
"GADGETZAN:156:149:430:102",
"LANDSENDBEACH:184:148:456:514",
"LOSTRIGGERCOVE:141:173:635:230",
"NOONSHADERUINS:129:191:530:43",
"SANDSORROWWATCH:175:157:311:111",
"SOUTHBREAKSHORE:192:151:510:304",
"SOUTHMOONRUINS:185:196:324:366",
"STEAMWHEEDLEPORT:136:129:599:86",
"SlickwickOilRig:237:181:129:436",
"THEGAPINGCHASM:198:188:458:380",
"THENOXIOUSLAIR:155:180:265:209",
"THISTLESHRUBVALLEY:166:234:217:294",
"VALLEYOFTHEWATCHERS:125:115:311:470",
"WATERSPRINGFIELD:145:164:516:177",
"ZALASHJISDEN:90:127:619:157",
"ZULFARRAK:191:166:265:5",
},
["Aszhara"] = {
"BAYOFSTORMS:257:285:486:208",
"BITTERREACHES:231:173:651:46",
"FORLORNRIDGE:210:247:198:373",
"HALDARRENCAMPMENT:191:138:83:339",
"JAGGEDREEF:556:153:373:9",
"LAKEMENNAR:301:184:302:437",
"LEGASHENCAMPMENT:224:131:484:49",
"RAVENCRESTMONUMENT:227:115:559:506",
"RUINSOFELDARATH:251:268:246:228",
"SHADOWSONGSHRINE:213:170:43:428",
"SOUTHRIDGEBEACH:355:207:397:360",
"TEMPLEOFARKKORAN:177:187:689:160",
"THALASSIANBASECAMP:224:144:507:126",
"THERUINEDREACHES:384:116:403:549",
"THESHATTEREDSTRAND:148:199:412:202",
"TIMBERMAWHOLD:209:194:258:175",
"TOWEROFELDARA:107:145:824:114",
"URSOLAN:219:212:327:94",
"VALORMOK:201:158:93:236",
},
["Felwood"] = {
"BLOODVENOMFALLS:225:132:297:269",
"DEADWOODVILLAGE:162:129:415:539",
"EMERALDSANCTUARY:171:150:411:436",
"FELPAWVILLAGE:224:138:489:0",
"IRONTREEWOODS:200:203:426:60",
"JADEFIREGLEN:151:145:339:469",
"JADEFIRERUN:181:159:338:35",
"JAEDENAR:236:122:275:333",
"MORLOSARAN:129:151:503:515",
"RUINSOFCONSTELLAS:224:142:303:387",
"SHATTERSCARVALE:218:186:315:130",
"TALONBRANCHGLADE:145:130:554:97",
},
["UngoroCrater"] = {
"FIREPLUMERIDGE:277:258:377:185",
"GOLAKKAHOTSPRINGS:299:334:133:158",
"IRONSTONEPLATEAU:276:281:588:69",
"LAKKARITARPITS:553:255:170:13",
"TERRORRUN:333:275:166:375",
"THEMARSHLANDS:295:341:568:247",
"THESLITHERINGSCAR:326:260:378:403",
},
["Moonglade"] = {
"LAKEELUNEARA:530:484:256:101",
},
["Silithus"] = {
"HIVEASHI:431:276:305:34",
"HIVEREGAL:469:335:262:333",
"HIVEZORA:343:424:117:188",
"SOUTHWINDVILLAGE:346:348:519:83",
"THECRYSTALVALE:285:274:121:31",
"THESCARABWALL:271:145:125:523",
"TWILIGHTBASECAMP:297:238:355:206",
},
["Winterspring"] = {
"DARKWHISPERGORGE:247:191:452:447",
"EVERLOOK:151:189:516:114",
"FROSTFIREHOTSPRINGS:226:124:231:180",
"FROSTSABERROCK:240:172:375:11",
"FROSTWHISPERGORGE:184:144:531:383",
"ICETHISTLEHILLS:116:155:617:247",
"LAKEKELTHERIL:203:176:409:205",
"MAZTHORIL:174:169:499:265",
"OWLWINGTHICKET:149:127:600:348",
"STARFALLVILLAGE:174:148:399:143",
"THEHIDDENGROVE:162:174:562:33",
"TIMBERMAWPOST:223:112:235:248",
"WINTERFALLVILLAGE:133:120:622:163",
},
["AlteracValley"] = {
"DUNBALDAR:254:228:356:18",
"FROSTWOLFKEEP:220:278:408:382",
"ICEBLOODGARRISON:286:288:343:179",
},
["Hyjal"] = {
"BARKSKINPLATEAU:207:167:390:297",
"BARKSKINVILLAGE:502:293:417:346",
"BLEAKHOLLOWCRATER:297:335:401:20",
"CIRCLEOFPOWER:277:331:203:63",
"DARKHOLLOWPASS:214:237:342:389",
"NORDANAAR:139:146:778:118",
"NORDRASSILGLADE:418:408:578:0",
"RUINSOFTELENNAS:165:193:141:236",
"THEEMERALDGATEWAY:309:244:109:353",
"ZULHATHA:191:210:3:194",
},
["Lapidis"] = {
"BRIGHTCOAST:298:213:237:246",
"CAELANSREST:147:111:517:230",
"CROWNISLAND:140:95:512:33",
"GORDOSHHEIGHTS:253:243:256:89",
"HAZURRIGLADE:87:82:486:306",
"SHANKSREEF:207:90:446:109",
"THEROCK:74:72:687:149",
"TOWEROFLAPIDIS:203:118:487:175",
"WALLOWINGCOAST:240:184:505:326",
"ZULHAZU:227:178:348:407",
},
["Gillijim"] = {
"DEEPTIDESANCTUM:204:117:199:512",
"DISTILLERYISLE:90:69:234:216",
"FAELONSFOLLY:108:103:331:304",
"GILLIJIMSTRAND:108:324:641:160",
"JADEMINE:141:149:500:256",
"KALKORPOINT:123:115:421:210",
"KAZONISLAND:194:113:432:51",
"MAULOGGPOST:137:227:512:401",
"MAULOGGREFUGE:231:132:615:465",
"RUINSOFZULRAZAR:215:136:369:384",
"SILVERCOAST:245:184:311:386",
"SILVERSANDBAR:47:128:256:398",
"SOUTHSEASANDBAR:125:172:178:63",
"TANGLEWOOD:135:188:576:297",
"ZULRAZAR:137:168:375:282",
},
["TelAbim"] = {
"BIXXLESSTOREHOUSE:300:127:368:178",
"HIGHVALERISE:191:187:468:248",
"TAZZOSSHACK:229:232:453:391",
"TELCOBASECAMP:184:183:342:440",
"THEDERELICTCAMP:165:205:364:273",
"THEJAGGEDISLES:313:256:385:16",
},
["Gilneas"] = {
"BLACKTHORNSCAMP:160:163:32:171",
"BROLOKMOUND:140:120:467:349",
"DAWNSTONEMINE:130:144:292:155",
"FREYSHEARKEEP:72:70:624:405",
"GILNEASCITY:295:242:89:105",
"GLAYMORESTEAD:140:127:483:129",
"GREYMANESWATCH:119:158:580:311",
"HOLLOWWEBCEMETARY:180:167:215:395",
"HOLLOWWEBWOODS:171:118:341:452",
"NORTHGATETOWER:130:137:397:172",
"OLDROCKPASS:155:165:290:37",
"RAVENSHIRE:194:237:472:431",
"RAVENWOODKEEP:253:174:496:494",
"ROSEWICKPLANTATION:134:140:331:155",
"RUINSOFGREYSHIRE:192:144:230:260",
"SHADEMORETAVERN:117:120:279:336",
"SOUTHMIREORCHARD:202:133:302:354",
"STILLWARDCHURCH:213:166:471:220",
"THEDRYROCKMINE:129:127:145:262",
"THEDRYROCKPIT:230:217:69:295",
"THEGREYMANEWALL:131:173:412:48",
"THEOVERGROWNACRE:134:154:390:269",
},
["Icepoint"] = {
"KANEQNUUN:512:430:237:129",
},
["BlackstoneIsland"] = {
"BLACKASHCOALPITS:303:474:209:178",
"BLACKASHMINE:251:204:397:222",
"GAZZIKSWORKSHOP:329:247:439:403",
"RUSTGATELUMBERYARD:217:245:551:241",
"RUSTGATERIDGE:396:226:372:360",
"THEWATERHOLE:386:239:382:43",
"VENTURECOSLUMS:270:192:498:129",
},
["ThalassianHighlands"] = {
"ALAHTHALAS:470:276:429:38",
"ANASTERIANPARK:229:219:385:256",
"BRINTHILIEN:180:211:395:450",
"FELSTRIDERRETREAT:201:207:165:428",
"ISLEOFETERNALAUTUMN:204:167:236:117",
"RUINSOFNASHALARAN:249:266:36:151",
"SILVERSUNMINE:208:189:288:373",
"THEFARSTRIDE:230:199:212:255",
"THELASTRUNESTONE:179:203:507:379",
},
["GrimReaches"] = {
"BAGGOTHSRAMPART:112:113:399:238",
"BARLEYCRESTFARMSTEAD:116:88:499:222",
"BRANGARSFOLLY:108:123:568:33",
"DUNKITHAS:137:95:470:329",
"EASTRIDGEOUTPOST:166:107:374:165",
"GETHKAR:118:124:444:84",
"GROLDANSEXCAVATION:125:117:567:231",
"LAKEKITHAS:217:150:481:270",
"RUINSOFSTOLGAZKEEP:134:146:366:53",
"SALGAZMINES:150:119:535:380",
"SHATTERBLADEPOST:186:130:512:126",
"SLATEBEARDSFORGE:136:99:457:404",
"THEGRIMHOLLOW:288:200:396:463",
"THEHIGHPASS:97:141:409:315",
"ZARMGETHPOINT:143:76:405:16",
"ZARMGETHSTRONGHOLD:120:150:487:17",
},
["Balor"] = {
"BILGERATCOMPOUND:174:141:323:76",
"CROAKINGPLATEAU:371:154:397:201",
"GRAHANESTATE:93:116:306:392",
"GULLWINGWRECKAGE:110:105:221:34",
"LANGSTONORCHARD:102:129:410:346",
"RUINSOFBREEZEHAVEN:163:141:450:256",
"SCURRYINGTHICKET:144:158:283:161",
"SIOUTPOST:175:232:597:389",
"SORROWMORELAKE:174:140:303:277",
"STORMBREAKERPOINT:243:197:583:212",
"STORMREAVERSPIRE:151:119:491:429",
"STORMWROUGHTCASTLE:202:166:458:332",
"TREACHEROUSCRAGS:243:171:381:457",
"VANDERFARMSTEAD:146:140:330:372",
"WINDROCKCLIFFS:192:279:256:303",
},
["Northwind"] = {
"ABBEYGARDENS:250:224:512:33",
"AMBERSHIRE:234:215:334:278",
"AMBERWOODKEEP:243:205:187:308",
"BLACKROCKBREACH:255:201:721:222",
"BRISTLEWHISKERCAVERN:197:185:568:232",
"CINDERFALLPASS:229:229:733:348",
"CRAWFORDWINERY:160:222:513:285",
"CRYSTALFALLS:320:205:627:464",
"GRIMMENLAKE:218:210:550:352",
"MERCHANTSHIGHROAD:353:249:256:420",
"NORTHRIDGEPOINT:298:314:349:31",
"NORTHWINDLOGGINGCAMP:224:185:258:171",
"RUINSOFBIRKHAVEN:180:208:645:116",
"SHERWOODQUARRY:293:287:702:1",
"STILLHEARTPORT:174:155:116:217",
"TOWEROFMAGILOU:181:223:189:169",
"WITCHCOVEN:147:162:257:81",
},
["Moonwhisper"] = {
"AnShesRespite:170:120:398:81",
"AncestralGrounds:164:82:295:88",
"BlackrootHold:162:137:495:465",
"BlackrootVillage:224:143:411:525",
"FoulheartSanctum:157:149:496:232",
"GroveOfTheMoon:152:146:464:347",
"LunarclawDen:84:60:674:376",
"MarasEthil:96:94:640:417",
"MoonhoofRetreat:172:144:419:167",
"MoonhoofVillage:163:157:570:188",
"MoonsilkHollow:125:114:577:458",
"MoroGaiVillage:133:127:571:372",
"NarvalisPoint:162:139:503:118",
"RuinsOfNendis:183:130:584:275",
"StarshardCradle:122:109:334:121",
"Tyrandas:158:169:580:61",
"VysnagosasRest:163:102:420:8",
},
}
-- add turtle-wow sell values -- add turtle-wow sell values
pfSellData = { pfSellData = {
+2 -3
View File
@@ -352,8 +352,7 @@ pfUI:RegisterModule("unitxp", function ()
end end
-- Debug command to test UnitXP indicators -- Debug command to test UnitXP indicators
_G.SLASH_PFUNITXP1 = "/pfunitxp" pfUI.api.RegisterSlashCommand("PFUNITXP", { "/pfunitxp" }, function()
SlashCmdList["PFUNITXP"] = function()
local chat = DEFAULT_CHAT_FRAME local chat = DEFAULT_CHAT_FRAME
chat:AddMessage("|cff33ffccpfUI|r: UnitXP Indicator Debug") chat:AddMessage("|cff33ffccpfUI|r: UnitXP Indicator Debug")
@@ -387,5 +386,5 @@ pfUI:RegisterModule("unitxp", function ()
else else
chat:AddMessage(" Target frame: |cffff0000NOT found|r") chat:AddMessage(" Target frame: |cffff0000NOT found|r")
end end
end end, true)
end) end)
+8 -23
View File
@@ -4,8 +4,6 @@ pfUI:RegisterModule("unusable", function ()
pfUI.unusable = {} pfUI.unusable = {}
local scanner = libtipscan:GetScanner("unusable")
local durability = string.gsub(DURABILITY_TEMPLATE, "%%[^%s]+", "(.+)")
local r, g, b, a = strsplit(",", C.appearance.bags.unusable_color) local r, g, b, a = strsplit(",", C.appearance.bags.unusable_color)
function pfUI.unusable:UpdateSlot(bag, slot) function pfUI.unusable:UpdateSlot(bag, slot)
@@ -13,31 +11,18 @@ pfUI:RegisterModule("unusable", function ()
if not pfUI.bags[bag] then return end if not pfUI.bags[bag] then return end
if not pfUI.bags[bag].slots[slot] then return end if not pfUI.bags[bag].slots[slot] then return end
-- add button shortcuts
local frame = pfUI.bags[bag].slots[slot].frame
local name = frame:GetName()
-- return on empty buttons -- return on empty buttons
local frame = pfUI.bags[bag].slots[slot].frame
if not frame.hasItem then return end if not frame.hasItem then return end
-- set the proper tooltip method -- C_PlayerInfo.CanUseItem is the "is this red in the tooltip" gate:
if bag == BANK_CONTAINER then -- proficiency, required level, class/race, skill/spell/rep. It checks
scanner:SetInventoryItem("player", 39+slot) -- *requirements* only, so a broken (0-durability) item still reads as
else -- usable -- no durability-line exclusion needed like the old scanner.
scanner:SetBagItem(bag, slot) local itemID = C_Container.GetContainerItemID(bag, slot)
if itemID and not C_PlayerInfo.CanUseItem(itemID) then
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end end
-- check for red color in tooltip
local red = scanner:Color(RED_FONT_COLOR)
if not red then return end
-- check for broken items
local left = scanner:Line(red)
local _, _, broken = string.find(left, durability, 1)
if broken then return end
-- update button vertex color
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end end
-- update on regular pfUI button updates -- update on regular pfUI button updates
+1 -1
View File
@@ -5,7 +5,7 @@ pfUI:RegisterModule("xpbar", function ()
local REST_WINDOW = 300 -- seconds of sliding-window samples for rate calc local REST_WINDOW = 300 -- seconds of sliding-window samples for rate calc
local REST_CAP_MUL = 1.5 -- target rested cap = UnitXPMax * 1.5 local REST_CAP_MUL = 1.5 -- target rested cap = UnitXPMax * 1.5
if IsTurtleWoW() then if TURTLE_WOW_VERSION then
REST_CAP_MUL = 1.13 REST_CAP_MUL = 1.13
end end
+25 -11
View File
@@ -5,11 +5,7 @@ end
SLASH_PFUI1 = '/pfui' SLASH_PFUI1 = '/pfui'
function SlashCmdList.PFUI(msg, editbox) function SlashCmdList.PFUI(msg, editbox)
if pfUI.gui:IsShown() then pfUI.gui:SetShown(not pfUI.gui:IsShown())
pfUI.gui:Hide()
else
pfUI.gui:Show()
end
end end
SLASH_GM1, SLASH_GM2 = '/gm', '/support' SLASH_GM1, SLASH_GM2 = '/gm', '/support'
@@ -27,7 +23,7 @@ do
-- ClassicAPI dependency check. -- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus -- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required. -- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 10503 -- (X*10000 + Y*100 + Z) local PFUI_CLASSIC_API_MIN = 10704 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI" local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest" local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
@@ -40,9 +36,16 @@ do
if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN) local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN)
pfUI.disabled = true pfUI.disabled = true
EventUtil.ContinueOnPlayerLogin(function() local detail
if not CLASSIC_API_VERSION then
detail = "The ClassicAPI DLL isn't loaded. The |cff33ffcc!!!ClassicAPI|r addon ships bundled with it -- delete your |cff33ffcc!!!ClassicAPI|r folder and install the latest release from:"
else
detail = "ClassicAPI " .. minVersion .. " or newer is required. Delete your |cff33ffcc!!!ClassicAPI|r folder and reinstall the latest release from:"
end
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = { StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = "This fork of |cff33ffccpf|cffffffffUI|r requires ClassicAPI\n " .. minVersion .. " or newer.\n\nAll |cff33ffccpf|cffffffffUI|r modules have been disabled.\nInstall ClassicAPI from:", text = "|cff33ffccpf|cffffffffUI|r has been disabled.\n\n" .. detail,
button1 = OKAY, button1 = OKAY,
hasEditBox = 1, hasEditBox = 1,
editBoxWidth = 280, editBoxWidth = 280,
@@ -51,7 +54,7 @@ do
hideOnEscape = 1, hideOnEscape = 1,
preferredIndex = 3, preferredIndex = 3,
OnShow = function() OnShow = function()
local editBox = _G[this:GetName().."EditBox"] local editBox = getglobal(this:GetName().."EditBox")
if editBox then if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL) editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText() editBox:HighlightText()
@@ -61,9 +64,15 @@ do
} }
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED") StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage( DEFAULT_CHAT_FRAME:AddMessage(
"This fork of |cff33ffccpf|cffffffffUI|r requires ClassicAPI " .. minVersion .. "+. Get it at " .. PFUI_CLASSIC_API_LATEST_URL, "|cff33ffccpf|cffffffffUI|r disabled: " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3 1, 0.3, 0.3
) )
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end) end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function() EventUtil.ContinueOnPlayerLogin(function()
@@ -99,9 +108,14 @@ pfUI.skins = {}
pfUI.environment = {} pfUI.environment = {}
pfUI.movables = {} pfUI.movables = {}
pfUI.version = {} pfUI.version = {}
pfUI.hooks = {}
pfUI.env = {} pfUI.env = {}
if not pfUI.disabled then
pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
end
-- check if macro addons are loaded (disables macrotweak/macroscan) -- check if macro addons are loaded (disables macrotweak/macroscan)
function pfUI:MacroAddonsLoaded() function pfUI:MacroAddonsLoaded()
return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros") return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros")
+1 -1
View File
@@ -15,7 +15,7 @@ pfUI:RegisterSkin("Battlefield Minimap", function ()
BattlefieldMinimapTabText:ClearAllPoints() BattlefieldMinimapTabText:ClearAllPoints()
BattlefieldMinimapTabText:SetPoint("CENTER", 0, 0) BattlefieldMinimapTabText:SetPoint("CENTER", 0, 0)
HookScript(BattlefieldMinimap, "OnShow", function() BattlefieldMinimap:HookScript("OnShow", function()
BattlefieldMinimapTab:Hide() BattlefieldMinimapTab:Hide()
end) end)
+31 -28
View File
@@ -2,26 +2,32 @@ pfUI:RegisterSkin("Character", function ()
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel() local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
-- Compatibility -- Honor Tab
if PlayerTitleDropDown then -- tbc, wotlk StripTextures(HonorFrame)
-- Character Tab if ArenaFrame then
SkinDropDown(PlayerTitleDropDown) StripTextures(ArenaFrame)
PlayerTitleDropDown:SetPoint("TOP", CharacterLevelText, "BOTTOM", 0, -2) for _, frame in pairs({'Arena', 'Honor'}) do
PlayerTitleDropDownText:SetPoint("LEFT", PlayerTitleDropDown.backdrop, "LEFT", 6, 2) for i = 1, 2 do
SkinDropDown(PlayerStatFrameLeftDropDown) local tab = _G[frame.."FrameTab"..i]
SkinDropDown(PlayerStatFrameRightDropDown) local lastTab = _G[frame.."FrameTab"..(i-1)]
if lastTab and lastTab:IsShown() then
-- Honor Tab tab:ClearAllPoints()
StripTextures(PVPFrame) tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else -- vanilla end
-- Honor Tab SkinTab(tab)
StripTextures(HonorFrame) end
end
HonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"]) for i = 1, 3 do
CreateBackdrop(HonorFrameProgressBar) local team = _G["ArenaFrameTeam"..i]
HonorFrameProgressBar:SetHeight(24) StripTextures(team)
CreateBackdrop(team)
end
end end
HonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"])
CreateBackdrop(HonorFrameProgressBar)
HonorFrameProgressBar:SetHeight(24)
local magicResTextureCords = { local magicResTextureCords = {
{0.21875, 0.78125, 0.25, 0.3203125}, {0.21875, 0.78125, 0.25, 0.3203125},
{0.21875, 0.78125, 0.0234375, 0.09375}, {0.21875, 0.78125, 0.0234375, 0.09375},
@@ -148,22 +154,19 @@ pfUI:RegisterSkin("Character", function ()
end end
end end
HookScript(CharacterFrame, "OnShow", function() hooksecurefunc("CharacterFrame_OnShow", function()
RefreshCharacterSlots() RefreshCharacterSlots()
RefreshPetPosition() RefreshPetPosition()
end)
if not this.hooked then hooksecurefunc("PaperDollItemSlotButton_Update", function()
hooksecurefunc("PaperDollItemSlotButton_Update", function() if this:GetParent() == PaperDollFrame then
-- update only character slots! RefreshCharacterSlot(this)
if string.find(this:GetName(), "^Character.-Slot$") then
RefreshCharacterSlot(this)
end
end)
hooksecurefunc("PetTab_Update", RefreshPetPosition)
this.hooked = true
end end
end) end)
hooksecurefunc("PetTab_Update", RefreshPetPosition)
StripTextures(PaperDollFrame) StripTextures(PaperDollFrame)
StripTextures(CharacterAttributesFrame) StripTextures(CharacterAttributesFrame)
StripTextures(CharacterResistanceFrame) StripTextures(CharacterResistanceFrame)
+193
View File
@@ -0,0 +1,193 @@
local slots = {
"HeadSlot",
"NeckSlot",
"ShoulderSlot",
"BackSlot",
"ChestSlot",
"ShirtSlot",
"TabardSlot",
"WristSlot",
"HandsSlot",
"WaistSlot",
"LegsSlot",
"FeetSlot",
"Finger0Slot",
"Finger1Slot",
"Trinket0Slot",
"Trinket1Slot",
"MainHandSlot",
"SecondaryHandSlot",
"RangedSlot",
}
pfUI:RegisterSkin("Inspect", function ()
local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
HookAddonOrVariable("Blizzard_InspectUI", function()
local cache = {}
CreateBackdrop(InspectFrame, nil, nil, .75)
CreateBackdropShadow(InspectFrame)
InspectFrame.backdrop:SetPoint("TOPLEFT", 10, -10)
InspectFrame.backdrop:SetPoint("BOTTOMRIGHT", -30, 72)
InspectFrame:SetHitRectInsets(10,30,10,72)
EnableMovable("InspectFrame", "Blizzard_InspectUI", INSPECTFRAME_SUBFRAMES)
SkinCloseButton(InspectFrameCloseButton, InspectFrame.backdrop, -6, -6)
InspectFrame:DisableDrawLayer("ARTWORK")
InspectNameText:ClearAllPoints()
InspectNameText:SetPoint("TOP", InspectFrame.backdrop, "TOP", 0, -10)
-- Turtle WoW has up to 4 inspect tabs: Character, Honor, Arena, Talents
for i = 1, 4 do
local tab = _G["InspectFrameTab"..i]
if tab then
local lastTab = _G["InspectFrameTab"..(i-1)]
tab:ClearAllPoints()
if lastTab then
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else
tab:SetPoint("TOPLEFT", InspectFrame.backdrop, "BOTTOMLEFT", bpad, -(border + (border == 1 and 1 or 2)))
end
SkinTab(tab)
end
end
do -- Character Tab
StripTextures(InspectPaperDollFrame)
EnableClickRotate(InspectModelFrame)
local rotL = InspectModelRotateLeftButton or InspectModelFrameRotateLeftButton
if rotL then rotL:Hide() end
local rotR = InspectModelRotateRightButton or InspectModelFrameRotateRightButton
if rotR then rotR:Hide() end
for _, slot in pairs(slots) do
local frame = _G["Inspect"..slot]
StripTextures(frame)
CreateBackdrop(frame)
SetAllPointsOffset(frame.backdrop, frame, 0)
HandleIcon(frame.backdrop, _G["Inspect"..slot.."IconTexture"])
local funce = frame:GetScript("OnEnter")
frame:SetScript("OnEnter", function()
local bid = this:GetID()
if not GetInventoryItemLink(InspectFrame.unit, this:GetID()) and this.hasItem then
GameTooltip:SetOwner(this, "ANCHOR_TOPRIGHT")
GameTooltip:SetHyperlink("item:"..cache[bid]["id"])
GameTooltip:Show()
else
funce()
end
end)
end
local function ColorSlot(slot, id, itemID, vslot)
local item = Item:CreateFromItemID(itemID)
if item:IsItemEmpty() then return end
item:ContinueOnItemLoad(function()
if not InspectFrame.unit then return end
if GetInventoryItemID(InspectFrame.unit, id) ~= itemID then return end
local quality = item:GetItemQuality()
if not quality then return end
local r, g, b = GetItemQualityColor(quality)
slot.backdrop:SetBackdropBorderColor(r, g, b)
if ShaguScore then
if not slot.scoreText then
slot.scoreText = slot:CreateFontString(nil, "OVERLAY", "GameFontNormal")
slot.scoreText:SetFont(pfUI.font_default, 12, "OUTLINE")
slot.scoreText:SetPoint("TOPRIGHT", 0, 0)
end
local itemLevel = ShaguScore.Database[itemID] or 0
local score = ShaguScore:Calculate(vslot, quality, itemLevel)
if score and score > 0 then
slot.scoreText:SetText(score)
slot.scoreText:SetTextColor(r, g, b)
else
slot.scoreText:SetText("")
end
end
end)
end
local function UpdateSlots()
if not InspectFrame.unit then return end
local guild, title = GetGuildInfo(InspectFrame.unit)
if guild then
InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1)
InspectGuildText:SetText(format(TEXT(GUILD_TITLE_TEMPLATE), title, guild))
InspectGuildText:Show()
else
InspectGuildText:SetText("")
InspectGuildText:Hide()
end
for _, vslot in pairs(slots) do
local id = GetInventorySlotInfo(vslot)
local itemID = GetInventoryItemID(InspectFrame.unit, id)
local slot = _G["Inspect" .. vslot]
if itemID then
ColorSlot(slot, id, itemID, vslot)
elseif not slot.hasItem then
-- genuinely empty slot: reset to a plain backdrop
CreateBackdrop(slot)
SetAllPointsOffset(slot.backdrop, slot, 0)
if slot.scoreText then
slot.scoreText:SetText("")
end
end
end
end
hooksecurefunc("InspectPaperDollItemSlotButton_Update", function(button)
local bid = button:GetID()
local itemID = GetInventoryItemID(InspectFrame.unit, bid)
if itemID then
cache[bid] = cache[bid] or {}
cache[bid]["id"] = itemID
cache[bid]["tex"] = GetInventoryItemTexture(InspectFrame.unit, button:GetID())
cache[bid]["count"] = GetInventoryItemCount(InspectFrame.unit, button:GetID())
cache[bid]["name"] = UnitName(InspectFrame.unit)
elseif cache[bid] and UnitName(InspectFrame.unit) == cache[bid].name then
-- restore cache information
SetItemButtonTexture(button, cache[bid]["tex"])
SetItemButtonCount(button, cache[bid]["count"])
button.hasItem = 1
end
UpdateSlots()
end)
end
do -- Honor Tab
StripTextures(InspectHonorFrame)
if InspectArenaFrame then
StripTextures(InspectArenaFrame)
for i = 1, 3 do
local team = _G["InspectArenaFrameTeam"..i]
StripTextures(team)
CreateBackdrop(team)
end
end
CreateBackdrop(InspectHonorFrameProgressBar)
InspectHonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"])
InspectHonorFrameProgressBar:SetHeight(24)
end
-- NOTE: Turtle WoW's Talent tab (InspectTalentsFrame / TWTalentFrame) is
-- skinned in modules/turtle-wow.lua, which activates once this skin is
-- registered (it gates on pfUI.skin["Inspect"]).
end)
end)
+1 -1
View File
@@ -86,7 +86,7 @@ pfUI:RegisterSkin("Books", function ()
ItemTextScrollFrameScrollBarScrollDownButton.Hide = function(self) self:Disable() end ItemTextScrollFrameScrollBarScrollDownButton.Hide = function(self) self:Disable() end
local first local first
HookScript(ItemTextFrame, "OnShow", function() ItemTextFrame:HookScript("OnShow", function()
if not first then -- it is necessary to update the scrollbar when you first open the frame if not first then -- it is necessary to update the scrollbar when you first open the frame
ItemTextScrollFrameScrollBar:Show() ItemTextScrollFrameScrollBar:Show()
ItemTextScrollFrameScrollBar:Hide() ItemTextScrollFrameScrollBar:Hide()
+1 -2
View File
@@ -45,8 +45,7 @@ pfUI:RegisterSkin("Macro", function ()
MacroNewButton:ClearAllPoints() MacroNewButton:ClearAllPoints()
MacroNewButton:SetPoint("RIGHT", MacroExitButton, "LEFT", -2*bpad, 0) MacroNewButton:SetPoint("RIGHT", MacroExitButton, "LEFT", -2*bpad, 0)
MacroEditButton:SetHeight(22) MacroEditButton:SetSize(150, 22)
MacroEditButton:SetWidth(150)
MacroEditButton:ClearAllPoints() MacroEditButton:ClearAllPoints()
MacroEditButton:SetPoint("BOTTOMLEFT", MacroFrameSelectedMacroButton, "BOTTOMRIGHT", 6, -2) MacroEditButton:SetPoint("BOTTOMLEFT", MacroFrameSelectedMacroButton, "BOTTOMRIGHT", 6, -2)
+4 -4
View File
@@ -11,7 +11,7 @@ pfUI:RegisterSkin("Options - New", function ()
CreateBackdropShadow(OptionsFrame) CreateBackdropShadow(OptionsFrame)
EnableMovable(OptionsFrame) EnableMovable(OptionsFrame)
HookScript(OptionsFrame, "OnShow", function() OptionsFrame:HookScript("OnShow", function()
this:ClearAllPoints() this:ClearAllPoints()
this:SetPoint("CENTER", 0, 0) this:SetPoint("CENTER", 0, 0)
end) end)
@@ -109,13 +109,13 @@ pfUI:RegisterSkin("Options - New", function ()
local color = PFUI_CLASS_COLORS[class] local color = PFUI_CLASS_COLORS[class]
SetHighlight(btn, color.r, color.g, color.b) SetHighlight(btn, color.r, color.g, color.b)
btn:SetFont(pfUI.font_default, pfUI_config.global.font_size, "OUTLINE") btn:SetFont(pfUI.font_default, pfUI_config.global.font_size, "OUTLINE")
HookScript(btn, "OnMouseDown", function() btn:HookScript("OnMouseDown", function()
StripTextures(this, true) StripTextures(this, true)
this:SetBackdrop(pfUI.backdrop) this:SetBackdrop(pfUI.backdrop)
this:SetBackdropColor(br, bg, bb, 0.75) this:SetBackdropColor(br, bg, bb, 0.75)
this:SetBackdropBorderColor(er, eg, eb, 1) this:SetBackdropBorderColor(er, eg, eb, 1)
end) end)
HookScript(btn, "OnMouseUp", function() btn:HookScript("OnMouseUp", function()
StripTextures(this, true) StripTextures(this, true)
this:SetBackdrop(pfUI.backdrop) this:SetBackdrop(pfUI.backdrop)
this:SetBackdropColor(br, bg, bb, 0.75) this:SetBackdropColor(br, bg, bb, 0.75)
@@ -186,5 +186,5 @@ pfUI:RegisterSkin("Options - New", function ()
-- hook after category selection: UpdateOptions is local so we hook its caller -- hook after category selection: UpdateOptions is local so we hook its caller
hooksecurefunc("OptionsListButton_OnClick", SkinControls) hooksecurefunc("OptionsListButton_OnClick", SkinControls)
-- also cover initial load -- also cover initial load
HookScript(OptionsFrame, "OnShow", SkinControls) OptionsFrame:HookScript("OnShow", SkinControls)
end) end)
+11 -30
View File
@@ -5,37 +5,18 @@ pfUI:RegisterSkin("Options - Sound", function ()
-- Compatibility -- Compatibility
local SoundOptionsFrameHeaderText, NUM_CHECKBOXES, NUM_SLIDERS local SoundOptionsFrameHeaderText, NUM_CHECKBOXES, NUM_SLIDERS
if SOUND_OPTIONS then -- tbc
SoundOptionsFrameHeaderText = GetNoNameObject(SoundOptionsFrame, "FontString", "BACKGROUND", SOUND_OPTIONS)
NUM_CHECKBOXES = 11
NUM_SLIDERS = 6
StripTextures(AudioOptionsFrame) SoundOptionsFrameHeaderText = GetNoNameObject(SoundOptionsFrame, "FontString", "ARTWORK", SOUNDOPTIONS_MENU)
CreateBackdrop(SoundOptionsFramePlayback, nil, true, .75) NUM_CHECKBOXES = 8
CreateBackdrop(SoundOptionsFrameHardware, nil, true, .75) NUM_SLIDERS = 4
CreateBackdrop(SoundOptionsFrameVolume, nil, true, .75)
SkinDropDown(SoundOptionsOutputDropDown) SoundOptionsFrameOkay:ClearAllPoints()
SoundOptionsFrameOkay:SetPoint("RIGHT", SoundOptionsFrameCancel, "LEFT", -2*bpad, 0)
SoundOptionsFrameDefaults:ClearAllPoints() SoundOptionsFrameSlider1:ClearAllPoints()
SoundOptionsFrameDefaults:SetPoint("TOPLEFT", SoundOptionsFramePlayback, "BOTTOMLEFT", 0, -10) SoundOptionsFrameSlider1:SetPoint("TOPRIGHT", SoundOptionsFrame, "TOPRIGHT", -18, -43)
SoundOptionsFrameCancel:ClearAllPoints() for i=2, NUM_SLIDERS do
SoundOptionsFrameCancel:SetPoint("TOPRIGHT", SoundOptionsFrameVolume, "BOTTOMRIGHT", 0, -10) _G["SoundOptionsFrameSlider"..i]:ClearAllPoints()
SoundOptionsFrameOkay:ClearAllPoints() _G["SoundOptionsFrameSlider"..i]:SetPoint("TOP", _G["SoundOptionsFrameSlider"..i-1], "BOTTOM", 0, -30)
SoundOptionsFrameOkay:SetPoint("RIGHT", SoundOptionsFrameCancel, "LEFT", -2*bpad, 0)
else -- vanilla
SoundOptionsFrameHeaderText = GetNoNameObject(SoundOptionsFrame, "FontString", "ARTWORK", SOUNDOPTIONS_MENU)
NUM_CHECKBOXES = 8
NUM_SLIDERS = 4
SoundOptionsFrameOkay:ClearAllPoints()
SoundOptionsFrameOkay:SetPoint("RIGHT", SoundOptionsFrameCancel, "LEFT", -2*bpad, 0)
SoundOptionsFrameSlider1:ClearAllPoints()
SoundOptionsFrameSlider1:SetPoint("TOPRIGHT", SoundOptionsFrame, "TOPRIGHT", -18, -43)
for i=2, NUM_SLIDERS do
_G["SoundOptionsFrameSlider"..i]:ClearAllPoints()
_G["SoundOptionsFrameSlider"..i]:SetPoint("TOP", _G["SoundOptionsFrameSlider"..i-1], "BOTTOM", 0, -30)
end
end end
StripTextures(SoundOptionsFrame) StripTextures(SoundOptionsFrame)
@@ -44,7 +25,7 @@ pfUI:RegisterSkin("Options - Sound", function ()
EnableMovable(SoundOptionsFrame) EnableMovable(SoundOptionsFrame)
HookScript(SoundOptionsFrame, "OnShow", function() SoundOptionsFrame:HookScript("OnShow", function()
this:ClearAllPoints() this:ClearAllPoints()
this:SetPoint("CENTER", 0, 0) this:SetPoint("CENTER", 0, 0)
end) end)
+1 -1
View File
@@ -49,7 +49,7 @@ pfUI:RegisterSkin("Options - Video", function ()
EnableMovable(OptionsFrame) EnableMovable(OptionsFrame)
HookScript(OptionsFrame, "OnShow", function() OptionsFrame:HookScript("OnShow", function()
this:ClearAllPoints() this:ClearAllPoints()
this:SetPoint("CENTER", 0, 0) this:SetPoint("CENTER", 0, 0)
end) end)
+2 -2
View File
@@ -102,13 +102,13 @@ pfUI:RegisterSkin("Quest Log", function ()
end end
end) end)
HookScript(QuestLogDetailScrollFrame, "OnHide", function() QuestLogDetailScrollFrame:HookScript("OnHide", function()
SkinArrowButton(QuestLogFrameExpandButton, "RIGHT", 21) SkinArrowButton(QuestLogFrameExpandButton, "RIGHT", 21)
QuestLogDetailScrollFrame:Hide() QuestLogDetailScrollFrame:Hide()
QuestLogFrame:SetWidth(340) QuestLogFrame:SetWidth(340)
end) end)
HookScript(QuestLogDetailScrollFrame, "OnShow", function() QuestLogDetailScrollFrame:HookScript("OnShow", function()
SkinArrowButton(QuestLogFrameExpandButton, "LEFT", 21) SkinArrowButton(QuestLogFrameExpandButton, "LEFT", 21)
QuestLogDetailScrollFrame:Show() QuestLogDetailScrollFrame:Show()
QuestLogFrame:SetWidth(676) QuestLogFrame:SetWidth(676)
+2 -2
View File
@@ -7,7 +7,7 @@ pfUI:RegisterSkin("Tooltips", function ()
CreateBackdropShadow(tooltip) CreateBackdropShadow(tooltip)
end end
HookScript(WorldMapTooltip, "OnShow", function() WorldMapTooltip:HookScript("OnShow", function()
CreateBackdrop(WorldMapTooltip, nil, nil, alpha) CreateBackdrop(WorldMapTooltip, nil, nil, alpha)
CreateBackdropShadow(WorldMapTooltip) CreateBackdropShadow(WorldMapTooltip)
end) end)
@@ -16,7 +16,7 @@ pfUI:RegisterSkin("Tooltips", function ()
for _, tooltip in pairs({ShoppingTooltip1, ShoppingTooltip2}) do for _, tooltip in pairs({ShoppingTooltip1, ShoppingTooltip2}) do
tooltip:SetClampedToScreen(true) tooltip:SetClampedToScreen(true)
HookScript(tooltip, "OnShow", function() tooltip:HookScript("OnShow", function()
local a, b, c, x, y = this:GetPoint() local a, b, c, x, y = this:GetPoint()
if not x or x == 0 then x = (border*2) + ( x or 0 ) + 1 end if not x or x == 0 then x = (border*2) + ( x or 0 ) + 1 end
if a then this:SetPoint(a, b, c, x, y) end if a then this:SetPoint(a, b, c, x, y) end