58 Commits

Author SHA1 Message Date
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
66 changed files with 1399 additions and 21819 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
github: [shagu]
ko_fi: shagu
github: [brues]
buy_me_a_coffee: brues
+33 -1588
View File
File diff suppressed because it is too large Load Diff
+61 -37
View File
@@ -28,14 +28,6 @@ function pfUI.api.HasNampower()
return GetNampowerVersion and true or false
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 ]
-- Returns distance to unit using best available method
-- 'unit1' [string] first unit (default: "player")
@@ -434,13 +426,17 @@ function pfUI.api.GetBagFamily(bag)
local id = GetInventoryItemID("player", ContainerIDToInventoryID(bag))
if id then
local _, _, _, _, _, itemType, subType = GetItemInfo(id)
local bagsubtype = L["bagtypes"][subType]
if bagsubtype == "DEFAULT" then return "BAG" end
if bagsubtype == "SOULBAG" then return "SOULBAG" end
if bagsubtype == "QUIVER" then return "QUIVER" end
if bagsubtype == nil then return "SPECIAL" end
-- classID 1 = Container (bags), 11 = Quiver
-- Container subclasses: 0 = Bag (default), 1 = Soul Bag, 2+ = specialty (herb/enchanting/etc.)
-- Quiver subclasses: 2 = Quiver (arrows), 3 = Ammo Pouch (bullets)
local _, _, _, _, _, classID, subClassID = C_Item.GetItemInfoInstant(id)
if classID == 1 then
if subClassID == 0 then return "BAG" end
if subClassID == 1 then return "SOULBAG" end
return "SPECIAL"
elseif classID == 11 then
return "QUIVER"
end
end
return nil
@@ -552,36 +548,54 @@ end
-- 'script' [string] the handler to hook
-- 'func' [function] the function that should be added
function HookScript(f, script, func)
local prev = f:GetScript(script)
f:SetScript(script, function(a1,a2,a3,a4,a5,a6,a7,a8,a9)
if prev then prev(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
func(a1,a2,a3,a4,a5,a6,a7,a8,a9)
end)
f:HookScript(script, func)
end
function hooksecurefunc(tbl, name, func)
if type(tbl) == "string" then tbl, name, func = _G, tbl, name end
if not tbl or type(tbl[name]) ~= "function" then return end
return _G.hooksecurefunc(tbl, name, func)
end
-- [ HookAddonOrVariable ]
-- Sets a function to be called automatically once an addon gets loaded
-- 'addon' [string] addon or variable name
-- 'func' [function] function that should run
function pfUI.api.HookAddonOrVariable(addon, func)
local lurker = CreateFrame("Frame", nil)
lurker.func = func
lurker:RegisterEvent("ADDON_LOADED")
lurker:RegisterEvent("VARIABLES_LOADED")
lurker:RegisterEvent("PLAYER_ENTERING_WORLD")
lurker:SetScript("OnEvent",function()
-- only run when config is available
if event == "ADDON_LOADED" and not this.foundConfig then
return
elseif event == "VARIABLES_LOADED" then
this.foundConfig = true
do
local lurker
local pending = {}
local function ProcessPending()
if not lurker.foundConfig then return end
for i = table.getn(pending), 1, -1 do
local hook = pending[i]
if IsAddOnLoaded(hook.addon) or _G[hook.addon] then
hook.func()
table.remove(pending, i)
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
if IsAddOnLoaded(addon) or _G[addon] then
this:func()
this:UnregisterAllEvents()
end
end)
table.insert(pending, { addon = addon, func = func })
lurker:RegisterEvent("ADDON_LOADED")
lurker:RegisterEvent("VARIABLES_LOADED")
lurker:RegisterEvent("PLAYER_ENTERING_WORLD")
ProcessPending()
end
end
-- [ QueueFunction ]
@@ -1563,6 +1577,16 @@ end
-- 'arg1' [string]
-- return object
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
-- below, and the traceback stops here — useless, since all callers share this
-- line. pfUI shadows Lua's `error` (dropping the level arg and not throwing),
-- so fold the caller frame in via debugstack to name the offending skin, then
-- bail so we don't fall through and crash on frame:GetRegions() anyway.
if type(frame) ~= "table" or not frame.GetRegions then
error("GetNoNameObject: invalid parent frame\n" .. debugstack(2, 3, 0))
return
end
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
+9 -2
View File
@@ -196,6 +196,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("loot", nil, "rollannounce", "0")
pfUI:UpdateConfig("loot", nil, "raritytimer", "1")
pfUI:UpdateConfig("loothistory", nil, "autoshow", "0")
pfUI:UpdateConfig("unitframes", nil, "disable", "0")
pfUI:UpdateConfig("unitframes", nil, "pastel", "1")
pfUI:UpdateConfig("unitframes", nil, "custom", "0")
@@ -714,9 +716,11 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("castbar", "player", "txtleftoffy", "0")
pfUI:UpdateConfig("castbar", "player", "showlag", "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", "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", "width", "-1")
pfUI:UpdateConfig("castbar", "target", "height", "-1")
@@ -729,6 +733,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("castbar", "target", "showrank", "0")
pfUI:UpdateConfig("castbar", "target", "txtrightoffx", "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", "width", "-1")
pfUI:UpdateConfig("castbar", "focus", "height", "-1")
@@ -741,6 +747,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("castbar", "focus", "showrank", "0")
pfUI:UpdateConfig("castbar", "focus", "txtrightoffx", "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("tooltip", nil, "position", "chat")
@@ -1372,7 +1380,6 @@ function pfUI:MigrateConfig()
end
end
-- Remove "Show only own debuffs" from unitframes and nameplates
-- (feature was removed; only Target Debuff Bar in buffwatch keeps it)
if pfUI_config.nameplates then
+5 -8
View File
@@ -569,7 +569,7 @@ end
-- 'frame' [frame] the modelframe that should be used
function pfUI.api.EnableClickRotate(frame)
frame:EnableMouse(true)
HookScript(frame, "OnUpdate", function()
frame:HookScript("OnUpdate", function()
if this.rotate then
local x,_ = GetCursorPosition()
if this.curx > x then
@@ -582,14 +582,14 @@ function pfUI.api.EnableClickRotate(frame)
end
end)
HookScript(frame, "OnMouseDown", function()
frame:HookScript("OnMouseDown", function()
if arg1 == "LeftButton" then
this.rotate = true
this.curx, this.cury = GetCursorPosition()
end
end)
HookScript(frame, "OnMouseUp", function()
frame:HookScript("OnMouseUp", function()
this.rotate, this.curx, this.cury = nil, nil, nil
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)
if not frame.pfEnterLeave then
if not frame.HookScript then frame.HookScript = HookScript end
local enter, leave = frame:GetScript("OnEnter"), frame:GetScript("OnLeave")
if enter then
@@ -844,7 +843,6 @@ function pfUI.api.SkinScrollbar(frame, always)
-- always show parent frame
if always then
RunOOC(function()
if not parent.HookScript then parent.HookScript = HookScript end
parent:HookScript("OnHide", function() this:Show() end)
end)
end
@@ -1172,10 +1170,9 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
local width = 200
-- 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
question:SetWidth( width + 2*padding)
this:SetScript("OnUpdate", nil)
question:SetWidth(width + 2*padding)
end)
end
+10 -2
View File
@@ -1014,7 +1014,7 @@ function pfUI.uf.OnEvent()
-- Raid frames: update_full is set by raid.lua GUID tracker
elseif this.label == "pet" and event == "UNIT_PET" then
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
elseif this.label == "pet" and event == "UNIT_HAPPINESS" then
this.update_full = true
@@ -1552,7 +1552,7 @@ function pfUI.uf:EnableEvents()
f:RegisterEvent("UNIT_FACTION")
f:RegisterEvent("UNIT_AURA") -- frame=buff, frame=debuff
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_LEADER_CHANGED") -- frame=leaderIcon
f:RegisterEvent("RAID_ROSTER_UPDATE") -- label=raidIcon
@@ -1902,6 +1902,8 @@ function pfUI.uf:RefreshUnit(unit, component)
end
if duration > 0 then
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end
elseif aura.duration > 0 then
local guid = UnitGUID(unitstr)
@@ -1912,6 +1914,8 @@ function pfUI.uf:RefreshUnit(unit, component)
else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end
else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end
else
unit.buffs[i]:Hide()
@@ -2005,7 +2009,11 @@ function pfUI.uf:RefreshUnit(unit, component)
end
if duration > 0 then
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1)
else
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
end
else
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
end
if stacks > 1 then
-27
View File
@@ -28,33 +28,6 @@ ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil
UNITFRAME_SECURE_TEMPLATE = nil
--[[ 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
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
-55
View File
@@ -2417,58 +2417,3 @@ pfSellData = {
[23725]=2600, [23727]=2600, [23728]=2600, [23743]=11, [24071]=393470, [24101]=400000,
[24102]=400000, [24222]=221054, [24231]=211, [24232]=900, [24281]=1210, [24282]=18250,
[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",},
}
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["deDE"] = {
["Combat Timer"] = nil,
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = nil,
+13 -1
View File
@@ -19,6 +19,7 @@ pfUI_translation["enUS"] = {
["Align Chat Windows"] = nil,
["Aligned Position"] = nil,
["All messages will be forwarded to:"] = nil,
["All players passed"] = nil,
["Alt-Click Action"] = nil,
["Always Allow Drag Via Shift Key"] = nil,
["Always Show"] = nil,
@@ -119,6 +120,7 @@ pfUI_translation["enUS"] = {
["Chat Bubble Transparency"] = nil,
["Chat Default Brackets"] = nil,
["Class"] = nil,
["Clear"] = nil,
["Clear Rolls"] = nil,
["Click Action"] = nil,
["Click Casting"] = nil,
@@ -138,7 +140,6 @@ pfUI_translation["enUS"] = {
["Combat Timer"] = nil,
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = nil,
@@ -195,6 +196,7 @@ pfUI_translation["enUS"] = {
["Deficit"] = nil,
["Delete profile"] = nil,
["Delete / Reset"] = nil,
["Dependencies"] = nil,
["Descending"] = nil,
["Description Font"] = nil,
["Description Font Size"] = nil,
@@ -477,6 +479,7 @@ pfUI_translation["enUS"] = {
["Look & Feel"] = nil,
["Loot"] = nil,
["Loot & Spam"] = nil,
["Loot History"] = nil,
["Macro Text Color"] = nil,
["Macro Text Size"] = nil,
["Main Actionbar"] = nil,
@@ -499,6 +502,7 @@ pfUI_translation["enUS"] = {
["Menu Font Size"] = nil,
["Messages are no longer forwarded to:"] = nil,
["Middle Mouse Button"] = nil,
["Missing"] = nil,
["Minimap"] = nil,
["Minimap Panel"] = nil,
["Minimap Size (|cffffaaaaExperimental|r)"] = nil,
@@ -552,6 +556,7 @@ pfUI_translation["enUS"] = {
["Only Show Own Debuffs (|cffffaaaaExperimental|r)"] = nil,
["Only Show Target Castbar"] = nil,
["On State Change"] = nil,
["Optional Dependencies"] = nil,
["Options"] = nil,
["Orientation"] = nil,
["Other Panel: Minimap"] = nil,
@@ -645,6 +650,7 @@ pfUI_translation["enUS"] = {
["Resting"] = nil,
["Reveal Unexplored Areas"] = nil,
["Switch to current zone"] = nil,
["Retrieving item information..."] = nil,
["Right"] = nil,
["Right Actionbar"] = nil,
["Right Anchor"] = nil,
@@ -750,6 +756,9 @@ pfUI_translation["enUS"] = {
["Show Totems Indicators"] = nil,
["Shrink & Return"] = nil,
["Size"] = nil,
["Spell Name Alignment"] = nil,
["Spell Name X Offset"] = nil,
["Spell Name Y Offset"] = nil,
["Skin"] = nil,
["Skins"] = nil,
["Slow"] = nil,
@@ -798,6 +807,9 @@ pfUI_translation["enUS"] = {
["Threshold To Trust Health Estimation"] = nil,
["Time"] = nil,
["Timer"] = nil,
["Timer Alignment"] = nil,
["Timer X Offset"] = nil,
["Timer Y Offset"] = nil,
["Time Remaining"] = nil,
["Timer In Minutes"] = nil,
["Timestamp Brackets"] = nil,
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["esES"] = {
["Combat Timer"] = "Temporizador de combate",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Comparar estadísticas bases de objetos",
["Components"] = "Componentes",
["Config UI Settings"] = nil,
["Configuration"] = "Configuración",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["frFR"] = {
["Combat Timer"] = "Chronomètre de combat",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Compare les stats de base d'un objet",
["Components"] = "Composants",
["Config UI Settings"] = nil,
["Configuration"] = "Configuration",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["koKR"] = {
["Combat Timer"] = "전투 타이머",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = "구성",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["ruRU"] = {
["Combat Timer"] = "Таймер боя",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Сравнивать базовые характеристики предмета",
["Components"] = "Компоненты",
["Config UI Settings"] = "Настройка параметров пользовательского интерфейса",
["Configuration"] = "Конфигурация",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["zhCN"] = {
["Combat Timer"] = "战斗计时器",
["Combopoint Height"] = "连击点高度",
["Combopoint Width"] = "连击点宽度",
["Compare Item Base Stats"] = "基于属性的装备对比",
["Components"] = "组件",
["Config UI Settings"] = "界面设置",
["Configuration"] = "配置",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["zhTW"] = {
["Combat Timer"] = "戰鬥計時器",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "基於屬性的裝備對比",
["Components"] = "組件",
["Config UI Settings"] = nil,
["Configuration"] = "配置",
+1
View File
@@ -78,4 +78,5 @@
<Include file="..\modules\unitxp.lua"/>
<Include file="..\modules\bgscore.lua"/>
<Include file="..\modules\equipmentmanager.lua"/>
<Include file="..\modules\loothistory.lua"/>
</Ui>
+1
View File
@@ -1,5 +1,6 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\skins\blizzard\character.lua"/>
<Include file="..\skins\blizzard\inspect.lua"/>
<Include file="..\skins\blizzard\spellbook.lua"/>
<Include file="..\skins\blizzard\friends.lua"/>
<Include file="..\skins\blizzard\talents.lua"/>
+20 -149
View File
@@ -14,7 +14,7 @@ setfenv(1, pfUI:GetEnvironment())
-- The public per-aura readers (UnitDebuff, UnitOwnDebuff) were retired in favor
-- of ClassicAPI's C_UnitAuras (which now provides sourceUnit/sourceGUID and
-- non-player expirationTime). What remains in libdebuff is the cast-event
-- bookkeeping consumed by GetBestAuraCast / GetEnhancedDebuffs and the
-- bookkeeping consumed by GetBestAuraCast (libpredict HoT tracking) and the
-- libdebuff_*_hooks broadcast surface (subscribers in actionbar / swingtimer
-- / libtotem react to SPELL_GO and SPELL_FAILED).
@@ -43,16 +43,8 @@ if GetNampowerVersion then
end
end
-- Nampower startup check: show version info and ensure CVars are set.
-- Runs on first OnUpdate after PLAYER_ENTERING_WORLD to give Nampower time to initialize.
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)
EventUtil.ContinueOnPlayerLogin(function()
RunNextFrame(function()
if GetNampowerVersion then
local major, minor, patch = GetNampowerVersion()
@@ -60,8 +52,6 @@ nampowerCheckFrame:SetScript("OnEvent", function()
local versionString = major .. "." .. minor .. "." .. patch
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
local cvarsToEnable = {
"NP_EnableSpellStartEvents",
@@ -91,8 +81,6 @@ nampowerCheckFrame:SetScript("OnEvent", function()
if enabledCount > 0 then
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
if failedCount > 0 then
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 {}
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)
-- SPELL_CAST_EVENT fires with targetGuid BEFORE SPELLCAST_START,
-- which allows libpredict to know the correct target for queued casts.
@@ -280,50 +264,10 @@ local debuffOverwritePairs = {
["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
-- ============================================================================
-- 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
pfUI.libdebuff_debugstats = pfUI.libdebuff_debugstats or {
enabled = false,
@@ -463,7 +407,7 @@ local function GetSlotCaster(guid, auraSlot, spellName)
end
-- Fallback: Check ownDebuffs
local myGuid = GetPlayerGUID()
local myGuid = GetPlayerGuid()
if ownDebuffs[guid] and ownDebuffs[guid][spellName] then
return myGuid, true
end
@@ -654,16 +598,7 @@ function libdebuff:GetDuration(effect, rank)
local rank = L["debuffs"][effect][rank] and rank or libdebuff:GetMaxRank(effect)
local duration = L["debuffs"][effect][rank]
if effect == L["dyndebuffs"]["Rupture"] 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
if effect == L["dyndebuffs"]["Demoralizing Shout"] then
local _,_,_,_,count = GetTalentInfo(2,1)
if count and count > 0 then duration = duration + ( duration / 100 * (count*10)) end
elseif effect == L["dyndebuffs"]["Shadow Word: Pain"] then
@@ -800,7 +735,7 @@ function libdebuff:GetBestAuraCast(guid, spellName)
local data = ownDebuffs[guid][spellName]
local timeleft = (data.startTime + data.duration) - GetTime()
if timeleft > 0 then
return data.startTime, data.duration, timeleft, data.rank, GetPlayerGUID()
return data.startTime, data.duration, timeleft, data.rank, GetPlayerGuid()
end
end
@@ -827,33 +762,6 @@ function libdebuff:GetBestAuraCast(guid, spellName)
return nil
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
-- ============================================================================
@@ -885,7 +793,7 @@ if hasNampower then
-- Carnage triggered! Refresh Rip & Rake
local guid = carnageState.targetGuid
local refreshTime = GetTime()
local myGuid = GetPlayerGUID()
local myGuid = GetPlayerGuid()
-- Refresh in ownDebuffs - only if timer still active
if ownDebuffs[guid] then
@@ -967,7 +875,7 @@ if hasNampower then
-- Channel interrupted by player - clear ownDebuffs for the channeled spell
-- via C_Spell.ChannelInfo. DEBUFF_REMOVED fires later (0.5-1s server lag),
-- causing phantom debuff display without this pre-clear.
local spellName = select(1, C_Spell.ChannelInfo())
local spellName = C_Spell.ChannelInfo()
if spellName then
local targetGuid = UnitGUID and UnitGUID("target")
if targetGuid and ownDebuffs[targetGuid] and ownDebuffs[targetGuid][spellName] then
@@ -978,14 +886,8 @@ if hasNampower then
end
end
end
elseif event == "PLAYER_ENTERING_WORLD" then
GetPlayerGUID()
elseif event == "PLAYER_ENTERING_WORLD" or event == "PLAYER_TALENT_UPDATE" then
UpdateCarnageRank()
elseif event == "PLAYER_TALENT_UPDATE" then
UpdateCarnageRank()
elseif event == "UNIT_HEALTH" then
local guid = arg1
if guid and UnitIsDead and UnitIsDead(guid) then
@@ -1080,7 +982,7 @@ if hasNampower then
local selfdebuffMode = pfUI_config and pfUI_config.buffbar and
pfUI_config.buffbar.tdebuff and pfUI_config.buffbar.tdebuff.selfdebuff == "1"
if selfdebuffMode then
local myGuid2 = GetPlayerGUID()
local myGuid2 = GetPlayerGuid()
if casterGuid == myGuid2 then
local duration = libdebuff:GetDuration(spellName, castRank) or 0
if duration > 0 then
@@ -1110,7 +1012,7 @@ if hasNampower then
end
-- Store rank for our casts
local myGuid = GetPlayerGUID()
local myGuid = GetPlayerGuid()
if casterGuid == myGuid then
lastCastRanks[spellName] = {
rank = castRank,
@@ -1183,11 +1085,6 @@ if hasNampower then
pfUI.libpredict_pending_cast.time = nil
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
if pfUI.libdebuff_spell_cast_hooks then
for _, fn in pairs(pfUI.libdebuff_spell_cast_hooks) do
@@ -1235,37 +1132,17 @@ if hasNampower then
local duration = durationMs and (durationMs / 1000) or 0
local startTime = GetTime()
local myGuid = GetPlayerGUID()
local myGuid = GetPlayerGuid()
local isOurs = (myGuid and casterGuid == myGuid)
if debugStats.enabled and isOurs then
debugStats.aura_cast = debugStats.aura_cast + 1
end
-- Combo-point abilities: Calculate duration based on CPs used
if IsComboPointAbility(spellName) then
if isOurs then
-- OWN casts: use captured CPs from SPELL_CAST_EVENT (if available)
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 comes from nampower's AURA_CAST event. ClassicAPI's
-- C_UnitAuras now resolves combo-scaled durations server-side, so
-- libdebuff only needs the database fallback when AURA_CAST reports 0.
if duration == 0 then
duration = libdebuff:GetDuration(spellName, rankNum) or 0
end
@@ -1367,15 +1244,12 @@ if hasNampower then
-- Notify unitframes of debuff updates (UNIT_AURA doesn't fire on refreshes!)
-- Check player
if UnitGUID("player") then
local playerGuid = UnitGUID("player")
if playerGuid == targetGuid and pfPlayer then
pfPlayer.update_aura = true
end
if IsPlayerGuid(targetGuid) and pfPlayer then
pfPlayer.update_aura = true
end
-- Check target
if UnitGUID("target") then
if UnitExists("target") then
local tarUnitGUID = UnitGUID("target")
if tarUnitGUID == targetGuid and pfTarget then
pfTarget.update_aura = true
@@ -1522,7 +1396,7 @@ if hasNampower then
end
end
local myGuid = GetPlayerGUID()
local myGuid = GetPlayerGuid()
local isOurs = (myGuid and casterGuid == myGuid)
-- Fallback: Check ownDebuffs timing
@@ -1557,7 +1431,6 @@ if hasNampower then
-- CRITICAL FIX: Update ownDebuffs here too for refresh timing!
-- This prevents the gap between DEBUFF_REMOVED and AURA_CAST where buffwatch shows nothing
if isOurs and casterGuid then
local myGuid = GetPlayerGUID()
if myGuid and casterGuid == myGuid then
-- Check if we have timer data from allAuraCasts
if allAuraCasts[guid] and allAuraCasts[guid][spellName] and allAuraCasts[guid][spellName][casterGuid] then
@@ -1811,5 +1684,3 @@ _G.SlashCmdList["MEMCHECK"] = function()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00No ownSlots/allSlots (eliminated by GetUnitField approach!)|r")
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff============================================================|r")
end
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r GetUnitField Edition loaded!")
+6 -10
View File
@@ -511,8 +511,7 @@ function libpredict:ParseComm(sender, msg)
local unit = senderUnit()
if not unit then return end
local startMs = select(4, C_Spell.UnitCastingInfo(unit))
local endMs = select(5, C_Spell.UnitCastingInfo(unit))
local _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit)
if not startMs or not endMs then return end
time = (endMs - startMs) / 1000
elseif msgtype == 1 then
@@ -523,8 +522,7 @@ function libpredict:ParseComm(sender, msg)
target = {strsplit(":", string.sub(msg,9, -1))}
local unit = senderUnit()
if not unit then return end
local startMs = select(4, C_Spell.UnitCastingInfo(unit))
local endMs = select(5, C_Spell.UnitCastingInfo(unit))
local _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit)
if not startMs or not endMs then return end
time = (endMs - startMs) / 1000
end
@@ -818,7 +816,7 @@ local hotsetbonus = libtipscan:GetScanner("hotsetbonus")
resetcache:RegisterEvent("PLAYER_ENTERING_WORLD")
resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB")
resetcache:RegisterEvent("CHARACTER_POINTS_CHANGED")
resetcache:RegisterEvent("UNIT_INVENTORY_CHANGED")
resetcache:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
resetcache:SetScript("OnEvent", function()
if event == "PLAYER_ENTERING_WORLD" then
-- load and initialize previous caches of spell amounts
@@ -829,10 +827,7 @@ resetcache:SetScript("OnEvent", function()
cache = pfUI_cache["prediction"][realm][player]["heals"]
end
if event == "UNIT_INVENTORY_CHANGED" or "PLAYER_ENTERING_WORLD" then
-- skip non-player events
if arg1 and arg1 ~= "player" then return end
if event == "PLAYER_EQUIPMENT_CHANGED" or event == "PLAYER_ENTERING_WORLD" then
local gear = ""
for id = 1, 18 do
gear = gear .. (GetInventoryItemLink("player",id) or "")
@@ -1025,7 +1020,8 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
local kind, id = GetActionInfo(slot)
local effect, rank
if kind == "spell" then
effect, rank = GetSpellInfo(id)
local spellInfo = C_Spell.GetSpellInfo(id)
effect, rank = spellInfo.name, spellInfo.rank
elseif kind == "macro" then
effect, rank = GetMacroSpell(id)
end
+4 -3
View File
@@ -228,8 +228,8 @@ pfUI.libdebuff_spell_go_hooks["libtotem"] = function(spellId)
-- 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)
if not name then
name = C_Spell.GetSpellName(spellId)
end
active[slot].name = name
active[slot].duration = data.duration
@@ -265,7 +265,8 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
local name, rank, spellID
if kind == "spell" then
spellID = id
name, rank = GetSpellInfo(id)
local spellInfo = C_Spell.GetSpellInfo(id)
name, rank = spellInfo.name, spellInfo.rank
elseif kind == "macro" then
name, rank, spellID = GetMacroSpell(id)
end
+5 -6
View File
@@ -335,8 +335,7 @@ pfUI:RegisterModule("actionbar", function ()
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
self.slfcast = nil
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 ( 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 self.bar == 11 then
CastShapeshiftForm(self.id)
elseif grid == 1 then
@@ -1013,10 +1012,8 @@ pfUI:RegisterModule("actionbar", function ()
-- no button available, create a new one
if not exists then
-- prepare the button for vanilla
if not f.HookScript then
f.HookScript = HookScript
f:SetScript("OnClick", ButtonClick)
end
if not f.HookScript then f.HookScript = HookScript end
f:SetScript("OnClick", ButtonClick)
if bar ~= 11 then
f:RegisterForDrag("LeftButton", "RightButton")
@@ -1358,6 +1355,7 @@ pfUI:RegisterModule("actionbar", function ()
-- make stance bar dodge by default
bars[i]:SetScript("OnShow", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
@@ -1367,6 +1365,7 @@ pfUI:RegisterModule("actionbar", function ()
-- restore old stance bar position
bars[i]:SetScript("OnHide", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
+4 -14
View File
@@ -134,19 +134,9 @@ pfUI:RegisterModule("addoncompat", function ()
end
-- run the addonconflict queue when firstrun is ready
local delay = CreateFrame("Frame")
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
if pfUI.firstrun and pfUI.firstrun.completed then
RunQueue()
this:SetScript("OnUpdate", nil)
end)
else
pfUI.events:RegisterCallback("firstrun:complete", RunQueue, "addoncompat")
end
end)
+19
View File
@@ -217,6 +217,21 @@ pfUI:RegisterModule("addons", function ()
pfUI.addons.list:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.addons.list:SetHeight(GetNumAddOns() * 25 + 26)
local function AddDependencyLines(header, deps)
if not deps or table.getn(deps) == 0 then return end
GameTooltip:AddLine(" ")
GameTooltip:AddLine(header .. ":", .2, 1, .8)
for _, dep in ipairs(deps) do
if IsAddOnLoaded(dep) then
GameTooltip:AddLine(" " .. dep, .5, 1, .5)
elseif C_AddOns.DoesAddOnExist(dep) then
GameTooltip:AddLine(" " .. dep, 1, .82, 0)
else
GameTooltip:AddLine(" " .. dep .. " (" .. T["Missing"] .. ")", 1, .4, .4)
end
end
end
local function AddonOnEnter()
this:SetBackdropBorderColor(1,1,1,.08)
@@ -231,6 +246,8 @@ pfUI:RegisterModule("addons", function ()
end
GameTooltip:AddLine(this.anote, .75,.75,.75,1)
AddDependencyLines(T["Dependencies"], this.adeps)
AddDependencyLines(T["Optional Dependencies"], this.aoptdeps)
GameTooltip:SetWidth(180)
GameTooltip:Show()
end
@@ -272,6 +289,8 @@ pfUI:RegisterModule("addons", function ()
frame.anote = anote
frame.aauthor = aauthor
frame.aversion = aversion
frame.adeps = { GetAddOnDependencies(i) } -- required (.toc Dependencies)
frame.aoptdeps = { C_AddOns.GetAddOnOptionalDependencies(i) } -- optional (.toc OptionalDeps)
frame:SetWidth(340)
frame:SetHeight(25)
+4 -5
View File
@@ -1,5 +1,4 @@
pfUI:RegisterModule("afkcam", function ()
local MARKED_AFK_CAPTURE = SanitizePattern(MARKED_AFK_MESSAGE)
local social_chats = {
"CHAT_MSG_SAY",
"CHAT_MSG_WHISPER",
@@ -165,10 +164,10 @@ pfUI:RegisterModule("afkcam", function ()
end)
afkcam:SetScript("OnEvent", function()
if event == "CHAT_MSG_SYSTEM" then
if (arg1 == _G.MARKED_AFK) or strfind(arg1, MARKED_AFK_CAPTURE) then
if event == "PLAYER_FLAGS_CHANGED" then
if UnitIsAFK('player') then
delay:Show()
elseif (arg1 == _G.CLEARED_AFK) then
elseif delay:IsVisible() then
delay:Hide()
this:stop()
end
@@ -180,7 +179,7 @@ pfUI:RegisterModule("afkcam", function ()
end
end)
afkcam:RegisterEvent("CHAT_MSG_SYSTEM")
afkcam:RegisterEvent("PLAYER_FLAGS_CHANGED")
afkcam:RegisterEvent("PLAYER_REGEN_DISABLED")
afkcam:RegisterEvent("PLAYER_LEAVING_WORLD") -- reseting cvars on PLAYER_LOGOUT crashes the client ¯\_(ツ)_/¯
end)
+1 -6
View File
@@ -19,12 +19,7 @@ pfUI:RegisterModule("autovendor", function ()
local startGold = GetMoney()
C_MerchantFrame.SellAllJunkItems()
local reporter = CreateFrame("Frame")
reporter.deadline = GetTime() + 0.3
reporter:SetScript("OnUpdate", function()
if GetTime() < this.deadline then return end
this:SetScript("OnUpdate", nil)
this:Hide()
C_Timer.After(0.3, function()
local income = GetMoney() - startGold
if income > 0 then
DEFAULT_CHAT_FRAME:AddMessage(T["Your vendor trash has been sold and you earned"] .. " " .. CreateGoldString(income))
+3 -3
View File
@@ -12,7 +12,9 @@ pfUI:RegisterModule("bubbles", function ()
pfUI.bubbles:RegisterEvent("CHAT_MSG_MONSTER_PARTY")
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)
function pfUI.bubbles:IsBubble(f)
@@ -66,7 +68,5 @@ pfUI:RegisterModule("bubbles", function ()
end)
end
end
pfUI.bubbles:SetScript("OnUpdate", nil)
end
end)
+1 -1
View File
@@ -159,7 +159,7 @@ pfUI:RegisterModule("buff", function ()
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
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("BUFF_UPDATE_DURATION_SELF")
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
+6 -3
View File
@@ -193,7 +193,7 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.left:SetFontObject(GameFontWhite)
cb.bar.left:SetTextColor(1,1,1,1)
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
cb.bar.right = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
@@ -204,7 +204,7 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.right:SetFontObject(GameFontWhite)
cb.bar.right:SetTextColor(1,1,1,1)
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:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0)
@@ -241,7 +241,10 @@ pfUI:RegisterModule("castbar", function ()
return
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),
-- drop the bar immediately.
+6 -7
View File
@@ -8,9 +8,9 @@ pfUI:RegisterModule("easteregg", function ()
local pvpking = CreateFrame("Frame", "pfPvPKing", UIParent)
pvpking:Hide()
pvpking:RegisterEvent("CHAT_MSG_SYSTEM")
pvpking:RegisterEvent("PLAYER_FLAGS_CHANGED")
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 .. " "
this.time = GetTime()
this:Show()
@@ -61,14 +61,13 @@ pfUI:RegisterModule("easteregg", function ()
end)
-- trigger fireworks when being AFK
fireworks:RegisterEvent("CHAT_MSG_SYSTEM")
fireworks:RegisterEvent("PLAYER_FLAGS_CHANGED")
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:Show()
elseif strfind(arg1, _G.CLEARED_AFK) then
this:Hide()
end
this:SetShown(isAFK)
end)
-- basic explosion animation
+125 -220
View File
@@ -1,243 +1,148 @@
pfUI:RegisterModule("eqcompare", function ()
local sides = { "Left", "Right" }
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"
pfUI.eqcompare = {}
local function AddHeader(tooltip)
local name = tooltip:GetName()
local function ShowCompareItem(self, link, shift)
self = self or GameTooltip
shift = shift or IsShiftKeyDown()
-- shift all entries one line down
for i=tooltip:NumLines(), 1, -1 do
for _, side in pairs(sides) do
local current = _G[name.."Text"..side..i]
local below = _G[name.."Text"..side..i+1]
if not link or (not shift and (C.tooltip.compare.showalways ~= "1" or C_Item.IsEquippedItem(link))) then
return
end
if current and current:IsShown() then
local text = current:GetText()
local r, g, b = current:GetTextColor()
local shoppingTooltip1, shoppingTooltip2 = unpack(self.shoppingTooltips or { ShoppingTooltip1, ShoppingTooltip2 });
if text and text ~= "" then
if tooltip:NumLines() < i+1 then
-- add new line if required
tooltip:AddLine(text, r, g, b, true)
else
-- update existing lines
below:SetText(text)
below:SetTextColor(r, g, b)
below:Show()
local SEPARATION = 6;
local backdrop = shoppingTooltip1.GetBackdrop and shoppingTooltip1:GetBackdrop();
local GAP = SEPARATION + ((type(backdrop) == "table" and backdrop.edgeSize) or 0);
-- hide processed line
current:Hide()
end
end
end
local item1 = nil;
local item2 = nil;
local side = "left";
if ( shoppingTooltip1:SetHyperlinkCompareItem(link, 1, shift, self) ) then
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
-- add label to first line
_G[name.."TextLeft1"]:SetTextColor(.5, .5, .5, 1)
_G[name.."TextLeft1"]:SetText(CURRENTLY_EQUIPPED)
_G[name.."TextLeft1"]:Show()
if ( item1 ) then
shoppingTooltip1:SetOwner(self, "ANCHOR_NONE");
shoppingTooltip1:ClearAllPoints();
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
tooltip:Show()
if ( item2 ) then
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
local slotTable = {
[INVTYPE_2HWEAPON] = "MainHandSlot",
[INVTYPE_BODY] = "ShirtSlot",
[INVTYPE_CHEST] = "ChestSlot",
[INVTYPE_CLOAK] = "BackSlot",
[INVTYPE_FEET] = "FeetSlot",
[INVTYPE_FINGER] = "Finger0Slot",
[INVTYPE_FINGER_OTHER] = "Finger1Slot",
[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",
local prevMerchant = ShoppingTooltip1.SetMerchantCompareItem
local function SetMerchantCompareItem(self, index, compareItem)
if compareItem == 1 then
ShowCompareItem(nil, GetMerchantItemLink(index), 1)
return false
end
return prevMerchant and prevMerchant(self, index, compareItem)
end
[INVTYPE_WAND] = "RangedSlot",
[INVTYPE_GUN] = "RangedSlot",
[INVTYPE_PROJECTILE] = "AmmoSlot",
[INVTYPE_CROSSBOW] = "RangedSlot",
[INVTYPE_THROWN] = "RangedSlot",
local prevAuction = ShoppingTooltip1.SetAuctionCompareItem
local function SetAuctionCompareItem(self, type, index, compareItem)
if compareItem == 1 then
ShowCompareItem(nil, GetAuctionItemLink(type, index), 1)
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)
return string.sub(str, 1, string.len(start)) == start
end
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
local function makeHook(getter)
return function(tooltip, arg1, arg2, arg3)
ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
end
end
local function CompareAttributes(data, targetData)
if not data then return end
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
local function HookTooltip(tooltip)
for setter, getter in pairs(TooltipHooks) do
hooksecurefunc(tooltip, setter, makeHook(getter))
end
end
pfUI.eqcompare = {}
pfUI.eqcompare.GameTooltipShow = function()
-- use this tooltip for the next comparison
pfUI.eqcompare.tooltip = this
HookTooltip(GameTooltip)
if not IsShiftKeyDown() and C.tooltip.compare.showalways ~= "1" then return end
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
pfUI.eqcompare.HookTooltip = HookTooltip
end)
+4 -5
View File
@@ -940,10 +940,10 @@ pfUI:RegisterModule("equipmentmanager", function()
-- Tie popout visibility to the EM sidecar. They appear when the
-- 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
end)
HookScript(frame, "OnHide", function()
frame:HookScript("OnHide", function()
for _, b in ipairs(popoutButtons) do b:Hide() end
if flyout then flyout:Hide() end
end)
@@ -1048,11 +1048,10 @@ pfUI:RegisterModule("equipmentmanager", function()
events:RegisterEvent("EQUIPMENT_SETS_CHANGED")
events:RegisterEvent("EQUIPMENT_SWAP_FINISHED")
events:RegisterEvent("EQUIPMENT_SWAP_PENDING")
events:RegisterEvent("BAG_UPDATE_DELAYED") -- ClassicAPI debounced; ~4x fewer refreshes than BAG_UPDATE
events:RegisterEvent("UNIT_INVENTORY_CHANGED")
events:RegisterEvent("BAG_UPDATE_DELAYED")
events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
events:SetScript("OnEvent", function()
if not frame:IsShown() then return end
if event == "UNIT_INVENTORY_CHANGED" and arg1 ~= "player" then return end
pfUI.equipmentmanager.Refresh()
end)
+5
View File
@@ -46,6 +46,11 @@ pfUI:RegisterModule("firstrun", function ()
return
end
end
if not self.completed then
self.completed = true
pfUI.events:TriggerEvent("firstrun:complete")
end
end
-- main function to create wizard windows
+1 -3
View File
@@ -50,10 +50,8 @@ function SlashCmdList.PFFOCUSNAME(msg)
FocusUnit("target")
end
local restore = CreateFrame("Frame")
restore:SetScript("OnUpdate", function()
RunNextFrame(function()
UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
restore:SetScript("OnUpdate", nil)
end)
if prevGUID and prevGUID ~= "0x0000000000000000" then
+36 -17
View File
@@ -212,6 +212,7 @@ pfUI:RegisterModule("gui", function ()
if not this:GetParent():IsShown() then
category[config] = r .. "," .. g .. "," .. b .. "," .. a
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
end
end
@@ -278,6 +279,7 @@ pfUI:RegisterModule("gui", function ()
if this:GetText() ~= this:GetParent().category[this:GetParent().config] then
this:GetParent().category[this:GetParent().config] = this:GetText()
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
end
this:SetTextColor(.2,1,.8,1)
else
@@ -328,6 +330,7 @@ pfUI:RegisterModule("gui", function ()
end
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
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
category[config] = value
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
end
end
@@ -394,6 +398,7 @@ pfUI:RegisterModule("gui", function ()
end
category[config] = newconf
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
frame.input:UpdateMenu()
end)
@@ -409,6 +414,7 @@ pfUI:RegisterModule("gui", function ()
CreateQuestionDialog(T["New entry:"], function()
category[config] = category[config] .. "#" .. this:GetParent().input:GetText()
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
frame.input:UpdateMenu()
end, false, true)
end)
@@ -906,6 +912,11 @@ pfUI:RegisterModule("gui", function ()
"1:" .. T["1 Decimal (2.1)"],
"2:" .. T["2 Decimals (2.14)"],
},
["castbaralign"] = {
"LEFT:" .. T["Left"],
"CENTER:" .. T["Center"],
"RIGHT:" .. T["Right"],
},
["orientation"] = {
"HORIZONTAL:" .. T["Horizontal"],
"VERTICAL:" .. T["Vertical"],
@@ -2748,62 +2759,70 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha")
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox")
CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox")
CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox")
CreateConfig(U["questitem"], T["Show Required Questitem Count"], C.tooltip.questitem, "showcount", "checkbox")
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["Casting Color"], C.appearance.castbar, "castbarcolor", "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["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["Castbar Width"], C.castbar.player, "width")
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 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["Left Text X Offset"], C.castbar.player, "txtleftoffx")
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.player, "txtleftoffy")
CreateConfig(nil, T["Timer Alignment"], C.castbar.player, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
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 Rank"], C.castbar.player, "showrank", "checkbox")
CreateConfig(nil, T["Merge Tradeskill Casts"], C.castbar.player, "mergetradeskill", "checkbox")
CreateConfig(nil, T["Right Text X Offset"], C.castbar.player, "txtrightoffx")
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.player, "txtrightoffy")
end)
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["Castbar Width"], C.castbar.target, "width")
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 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["Left Text X Offset"], C.castbar.target, "txtleftoffx")
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.target, "txtleftoffy")
CreateConfig(nil, T["Timer Alignment"], C.castbar.target, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
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 Rank"], C.castbar.target, "showrank", "checkbox")
CreateConfig(nil, T["Right Text X Offset"], C.castbar.target, "txtrightoffx")
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.target, "txtrightoffy")
end)
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["Castbar Width"], C.castbar.focus, "width")
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 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["Left Text X Offset"], C.castbar.focus, "txtleftoffx")
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.focus, "txtleftoffy")
CreateConfig(nil, T["Timer Alignment"], C.castbar.focus, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
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 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)
CreateGUIEntry(T["Chat"], nil, function()
+5 -19
View File
@@ -2,7 +2,6 @@
-- Announces Innervate casts via raid/party/battleground chat
-- Registers AURA_CAST events directly - zero polling, pure event-driven
pfUI:RegisterNewModule("innervatecall", "Innervate Callout", "DRUID")
pfUI:RegisterModule("innervatecall", function ()
-- Requires Nampower for AURA_CAST events
if not GetNampowerVersion then return end
@@ -13,15 +12,6 @@ pfUI:RegisterModule("innervatecall", function ()
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
-- engine's known unit tokens (player, party, raid, target, ...) and
-- returns the first one currently bound to the GUID, so we don't have
@@ -75,7 +65,7 @@ pfUI:RegisterModule("innervatecall", function ()
if spellId ~= INNERVATE_SPELLID then return end
-- Only announce our own casts
if casterGuid ~= GetPlayerGuid() then return end
if not IsPlayerGuid(casterGuid) then return end
-- Resolve target name from GUID
local targetName = ResolveTargetName(targetGuid) or "Unknown"
@@ -96,14 +86,10 @@ pfUI:RegisterModule("innervatecall", function ()
end
end
local readyAt = GetTime() + cdRemaining
frame:SetScript("OnUpdate", function()
if GetTime() >= readyAt then
frame:SetScript("OnUpdate", nil)
local ch = GetAnnounceChannel()
if ch then
SendChatMessage(">> Innervate is ready <<", ch)
end
C_Timer.After(cdRemaining, function()
local ch = GetAnnounceChannel()
if ch then
SendChatMessage(">> Innervate is ready <<", ch)
end
end)
end)
+390
View File
@@ -0,0 +1,390 @@
pfUI:RegisterNewModule("loothistory", "Loot History")
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
_G.SLASH_PFLOOTHISTORY1, _G.SLASH_PFLOOTHISTORY2 = "/loothistory", "/pfloothistory"
_G.SlashCmdList.PFLOOTHISTORY = Toggle
end)
+1 -5
View File
@@ -66,9 +66,5 @@ pfUI:RegisterModule("macrotweak", function ()
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)
RunNextFrame(CheckConflicts)
end)
+4 -4
View File
@@ -54,7 +54,7 @@ pfUI:RegisterModule("map", function ()
if not this.hooked then
this.hooked = true
HookScript(WorldMapFrame, "OnShow", function()
WorldMapFrame:HookScript("OnShow", function()
-- customize
this:EnableKeyboard(false)
this:EnableMouseWheel(1)
@@ -66,7 +66,7 @@ pfUI:RegisterModule("map", function ()
pfOrigSetMapToCurrentZone()
end)
HookScript(WorldMapFrame, "OnMouseWheel", function()
WorldMapFrame:HookScript("OnMouseWheel", function()
if IsShiftKeyDown() then
alpha = clamp(WorldMapFrame:GetAlpha() + arg1/10, 0.1, 1.0)
WorldMapFrame:SetAlpha(alpha)
@@ -91,11 +91,11 @@ pfUI:RegisterModule("map", function ()
SaveMovable(this, true)
end)
HookScript(WorldMapFrame, "OnDragStart", function()
WorldMapFrame:HookScript("OnDragStart", function()
WorldMapFrame:StartMoving()
end)
HookScript(WorldMapFrame, "OnDragStop",function()
WorldMapFrame:HookScript("OnDragStop",function()
WorldMapFrame:StopMovingOrSizing()
SaveMovable(this, true)
end)
+33 -70
View File
@@ -30,25 +30,6 @@ pfUI:RegisterModule("mapreveal", function ()
pfUI.mapreveal:UpdateConfig()
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 explorecaches = {}
local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } }
@@ -88,13 +69,6 @@ pfUI:RegisterModule("mapreveal", function ()
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()
-- clear stale caches
for k in pairs(explorecaches) do explorecaches[k] = nil end
@@ -108,7 +82,6 @@ pfUI:RegisterModule("mapreveal", function ()
local mapFileName = GetMapInfo()
if not mapFileName then mapFileName = "World" end
local prefix = string.format("Interface\\WorldMap\\%s\\", mapFileName)
local numOverlays = GetNumMapOverlays()
-- accumulate explored overlays per zone (never clear, only add)
@@ -123,12 +96,18 @@ pfUI:RegisterModule("mapreveal", function ()
-- hide explore icons
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
for i, hash in ipairs(zoneData) do
local textureName, textureWidth, textureHeight, offsetX, offsetY, mapPointX, mapPointY, name = unpack_hash(prefix, hash)
if not textureName then break end
for i, overlay in ipairs(zoneData) do
local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE"
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
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: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
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
explore:Show()
@@ -155,49 +136,31 @@ pfUI:RegisterModule("mapreveal", function ()
-- render overlay texture tiles on BORDER draw layer
-- 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
local numH = math.ceil(textureWidth / 256)
local numV = math.ceil(textureHeight / 256)
local texPixW, texFileW, texPixH, texFileH
for _, tile in ipairs(overlay.tiles) do
textureCount = textureCount + 1
local tex = pfGetOverlay(textureCount)
for j = 1, numV do
if j < numV then
texPixH = 256
texFileH = 256
else
texPixH = mod(textureHeight, 256)
if texPixH == 0 then texPixH = 256 end
texFileH = 16
while texFileH < texPixH do texFileH = texFileH * 2 end
end
tex:SetWidth(tile.width)
tex:SetHeight(tile.height)
tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY)
tex:ClearAllPoints()
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY)
tex:SetTexture(tile.file)
for k = 1, numH do
textureCount = textureCount + 1
local tex = pfGetOverlay(textureCount)
explorecaches[name] = explorecaches[name] or {}
explorecaches[name][tex] = true
if k < numH then
texPixW = 256
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
tex:SetVertexColor(r,g,b,a)
tex:Show()
end
end
end
-1
View File
@@ -1,4 +1,3 @@
pfUI:RegisterNewModule("marktracking", "Mark Tracker")
pfUI:RegisterModule("marktracking", function ()
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
if not UnitExists("mark1") and not UnitExists("mark8") then
+1 -8
View File
@@ -100,12 +100,6 @@ pfUI:RegisterModule("nameplates", function ()
-- PERF: visiblePlateCount maintained event-driven (NAME_PLATE_UNIT_ADDED/_REMOVED)
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
-- ============================================================================
@@ -492,7 +486,6 @@ end
elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then
if event == "PLAYER_ENTERING_WORLD" then
_, PlayerGUID = UnitExists("player")
CacheConfig()
this:SetGameVariables()
RebuildRaidGuidCache()
@@ -702,7 +695,7 @@ end
end
end
HookScript(nameplate.original.healthbar, "OnValueChanged", nameplates.OnValueChanged)
nameplate.original.healthbar:HookScript("OnValueChanged", nameplates.OnValueChanged)
-- adjust sizes and scaling of the nameplate
nameplate:SetScale(UIParent:GetScale())
+3 -4
View File
@@ -424,7 +424,8 @@ pfUI:RegisterModule("panel", function()
widget:RegisterEvent("PLAYER_REGEN_ENABLED")
widget:RegisterEvent("PLAYER_DEAD")
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.Tooltip = function()
@@ -456,8 +457,6 @@ pfUI:RegisterModule("panel", function()
end
end
widget:SetScript("OnEvent", function()
if event == "UNIT_INVENTORY_CHANGED" and arg1 ~= "player" then return end
local totalCurr, totalMax = 0, 0
for id = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do
local cur, max = GetInventoryItemDurability(id)
@@ -533,7 +532,7 @@ pfUI:RegisterModule("panel", function()
-- Hearthstone bind location
local hearth = CreateFrame("Frame", "pfPanelBindLocation", UIParent)
hearth:RegisterEvent("PLAYER_ENTERING_WORLD")
hearth:RegisterEvent("CHAT_MSG_SYSTEM")
hearth:RegisterEvent("HEARTHSTONE_BOUND")
hearth:SetScript("OnEvent", function()
pfUI.panel:OutputPanel("bindlocation", T["Hearthstone"] .. ": " .. (GetBindLocation() or T["Not Set"]))
end)
+137 -14
View File
@@ -1,13 +1,11 @@
pfUI:RegisterModule("sellvalue", function ()
local function AddVendorPrices(frame, id, count)
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 buy = pfSellData[id]
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(" ")
if sell > 0 then
@@ -25,21 +23,12 @@ pfUI:RegisterModule("sellvalue", function ()
frame:AddDoubleLine(T["Buy"] .. ":", CreateGoldString(buy), 1, 1, 1)
end
end
elseif not MerchantFrame:IsShown() and sell > 0 then
SetTooltipMoney(frame, sell * count)
end
frame:Show()
end
pfUI.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()
if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then
@@ -47,4 +36,138 @@ pfUI:RegisterModule("sellvalue", function ()
if id then AddVendorPrices(ItemRefTooltip, id, 1) 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)
+131 -194
View File
@@ -1,93 +1,78 @@
pfUI:RegisterModule("share", function ()
local function serialize(tbl, comp, name, ignored, spacing)
local spacing = spacing or ""
local match = nil
local tname = ( spacing == "" and "" or "[\"" ) .. name .. ( spacing == "" and "" or "\"]" )
local str = spacing .. tname .. " = {\n"
-- Profile sharing rides ClassicAPI's C_EncodingUtil:
-- export: diff vs defaults -> SerializeCBOR -> CompressString (zlib)
-- -> EncodeBase64, prefixed with the format magic
-- import: the reverse, into a plain table — no loadstring, so a
-- 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
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
local result = serialize(tbl[k], comp and comp[k], k, ignored, spacing .. " ")
if result then
match = true
str = str .. result
local sub = DiffConfig(v, type(dv) == "table" and dv or nil)
if sub then
diff = diff or {}
diff[k] = sub
end
elseif type(v) == "string" then
match = true
local escaped = string.gsub(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"
elseif v ~= dv and ( type(v) == "string" or type(v) == "number" or type(v) == "boolean" ) then
diff = diff or {}
diff[k] = v
end
end
end
str = str .. spacing .. "}" .. ( spacing == "" and "" or "," ) .. "\n"
return match and str or nil
return diff
end
local function compress(input)
-- based on Rochet2's lzw compression
if type(input) ~= "string" then
return nil
-- EditBoxes don't soft-wrap one giant unbroken line; chunk the blob.
local function wrap(str, width)
local out = {}
for i = 1, strlen(str), width do
table.insert(out, strsub(str, i, i + width - 1))
end
local len = strlen(input)
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)
return table.concat(out, "\n")
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)
-- based on Rochet2's lzw compression
if type(input) ~= "string" or strlen(input) < 1 then
@@ -159,92 +144,32 @@ pfUI:RegisterModule("share", function ()
return table.concat(result)
end
local function enc(to_encode)
local index_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
local bit_pattern = ''
local encoded = ''
local trailing = ''
local function DecodeLegacy(text)
if not text or gsub(text, "%s", "") == "" then return nil end
for i = 1, string.len(to_encode) do
local remaining = tonumber(string.byte(string.sub(to_encode, i, i)))
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 .. bin_bits
-- encoded blob? peel base64 + LZW down to Lua source. Raw (already
-- decoded) source pastes are accepted as-is.
local source = text
local stripped = gsub(text, "%s", "")
local ok, decoded = pcall(function()
return C_EncodingUtil.DecodeBase64(stripped)
end)
if ok and decoded then
local decompressed = decompress(decoded)
if decompressed then source = decompressed end
end
if mod(string.len(bit_pattern), 3) == 2 then
trailing = '=='
bit_pattern = bit_pattern .. '0000000000000000'
elseif mod(string.len(bit_pattern), 3) == 1 then
trailing = '='
bit_pattern = bit_pattern .. '00000000'
end
local chunk = loadstring(source)
if not chunk then return nil end
local count = 0
for i = 1, string.len(bit_pattern), 6 do
local byte = string.sub(bit_pattern, i, i+5)
local offset = tonumber(tonumber(byte, 2))
encoded = encoded .. string.sub(index_table, offset+1, offset+1)
count = count + 1
if count >= 92 then
encoded = encoded .. "\n"
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
-- Sandbox the chunk: an empty environment means it can assign its
-- config table but can't reach any global or API function — a legacy
-- profile string is data, never code.
local sandbox = {}
setfenv(chunk, sandbox)
if not pcall(chunk) then return nil end
if type(sandbox.pfUI_config) == "table" then return sandbox.pfUI_config end
return nil
end
do -- Window
@@ -309,33 +234,43 @@ pfUI:RegisterModule("share", function ()
this:GetParent():UpdateScrollChildRect()
this:GetParent():UpdateScrollState()
local _, error = loadstring(f.scroll.text:GetText())
if error or string.gsub(this:GetText(), " ", "") == "" then
f.loadButton:Disable()
f.loadButton.text:SetTextColor(1,.5,.5,1)
else
local text = this:GetText()
local blob = Decode(text)
local json = not blob and DecodeJSON(text) or nil
local legacy = not blob and not json and DecodeLegacy(text) or nil
if blob or json or legacy then
f.loadButton:Enable()
f.loadButton.text:SetTextColor(.5,1,.5,1)
else
f.loadButton:Disable()
f.loadButton.text:SetTextColor(1,.5,.5,1)
end
local trydec = dec(this:GetText())
if string.gsub(this:GetText(), " ", "") == "" then
f.readButton.text:SetText(T["N/A"])
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
if blob or legacy then
-- decoding a legacy string yields the JSON view; re-encoding it
-- from there produces a new-format blob (migration path)
f.readButton:Enable()
f.readButton.text:SetText(T["Decode"])
f.readButton.func = function()
local uncompressed = decompress(dec(f.scroll.text:GetText()))
f.scroll.text:SetText(uncompressed)
local current = f.scroll.text:GetText()
local config = Decode(current) or DecodeLegacy(current)
if config then
f.scroll.text:SetText(C_EncodingUtil.SerializeJSON(config))
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)
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:SetText(T["Import"])
f.loadButton:SetScript("OnClick", function()
local ImportConfig, error = loadstring(f.scroll.text:GetText())
if not error and f.scroll.text:GetText() ~= "" then
ImportConfig()
pfUI:LoadConfig()
local text = f.scroll.text:GetText()
local config = Decode(text) or DecodeJSON(text) or DecodeLegacy(text)
if not config then return end
-- Skip firstrun wizard when importing a shared profile
-- 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
_G.pfUI_config = config
C = _G.pfUI_config
pfUI:LoadConfig()
-- Skip firstrun wizard when importing a shared profile
-- 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
CreateQuestionDialog(T["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"], ReloadUI)
end
CreateQuestionDialog(T["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"], ReloadUI)
end)
end
@@ -445,9 +382,9 @@ pfUI:RegisterModule("share", function ()
ignored["position"] = f.ignorePosition:GetChecked()
ignored["disabled"] = f.ignorePosition:GetChecked()
local compressed = enc(compress(serialize(myconfig, defconfig, "pfUI_config", ignored)))
f.scroll.text:SetText(compressed)
f.scroll.text.value = compressed
local encoded = Encode(DiffConfig(myconfig, defconfig, ignored) or {})
f.scroll.text:SetText(encoded)
f.scroll.text.value = encoded
f.scroll:SetVerticalScroll(0)
end)
end
@@ -457,4 +394,4 @@ pfUI:RegisterModule("share", function ()
f:Show()
end
end
end)
end)
+45 -14
View File
@@ -1,4 +1,3 @@
pfUI:RegisterNewModule("swingtimer", "Swing Timer")
pfUI:RegisterModule("swingtimer", function ()
local rawborder, border = GetBorderSize()
@@ -28,11 +27,11 @@ pfUI:RegisterModule("swingtimer", function ()
pendingCastSpellId = nil,
mhFrozenAt = nil,
hsQueued = false, cleaveQueued = false, maulQueued = false,
hsSeenCurrent = false, cleaveSeenCurrent = false, maulSeenCurrent = false,
isWarrior = false,
isDruid = false,
cachedHSSlots = {}, cachedCleaveSlots = {},
cachedHSSlots = {}, cachedCleaveSlots = {}, cachedMaulSlots = {},
useSpellQueueEvent = false,
playerGUID = nil,
swingThrottle = 0,
onSwingCache = {},
}
@@ -417,17 +416,20 @@ pfUI:RegisterModule("swingtimer", function ()
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()
if not S.isWarrior or not sw_hsqueue or S.useSpellQueueEvent then return end
if not sw_hsqueue then return end
S.cachedHSSlots = {}
S.cachedCleaveSlots = {}
S.cachedMaulSlots = {}
if not (S.isWarrior or S.isDruid) then return end
for slot = 1, 120 do
local kind, id = GetActionInfo(slot)
local name
if kind == "spell" then
name = GetSpellInfo(id)
name = C_Spell.GetSpellName(id)
elseif kind == "macro" then
name = GetMacroSpell(id)
end
@@ -435,6 +437,8 @@ pfUI:RegisterModule("swingtimer", function ()
table.insert(S.cachedHSSlots, slot)
elseif name == CLEAVE_NAME then
table.insert(S.cachedCleaveSlots, slot)
elseif name == MAUL_NAME then
table.insert(S.cachedMaulSlots, slot)
end
end
end
@@ -446,9 +450,30 @@ pfUI:RegisterModule("swingtimer", function ()
return false
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()
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)
end
@@ -527,6 +552,10 @@ pfUI:RegisterModule("swingtimer", function ()
-- HS/Cleave color
local curR, curG, curB = mhDefaultR, mhDefaultG, mhDefaultB
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
curR, curG, curB = 1.0, 0.55, 0.0 -- orange for druid maul queue
elseif S.isWarrior then
@@ -781,7 +810,10 @@ pfUI:RegisterModule("swingtimer", function ()
-- 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["swingtimer"] = function(success, spellId)
SetQueuedKind(ClassifyOnSwingSpell(spellId))
local kind = ClassifyOnSwingSpell(spellId)
if not kind then return end
S.useSpellQueueEvent = true
SetQueuedKind(kind)
end
@@ -789,7 +821,7 @@ pfUI:RegisterModule("swingtimer", function ()
events:RegisterEvent("AUTO_ATTACK_SELF")
events:RegisterEvent("AUTO_ATTACK_OTHER")
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_ENABLED")
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
@@ -832,8 +864,7 @@ pfUI:RegisterModule("swingtimer", function ()
elseif event == "AUTO_ATTACK_OTHER" then
-- Parry haste: enemy attacked the player and player parried
local targetGuid = arg2
if not targetGuid or not S.playerGUID then return end
if targetGuid ~= S.playerGUID then return end
if not targetGuid or not IsPlayerGuid(targetGuid) then return end
local victimState = arg5 or 0
-- VICTIMSTATE_PARRY = 3
-- Vanilla: parry reduces the NEXT swing timer by 40% of weapon speed,
@@ -880,12 +911,12 @@ pfUI:RegisterModule("swingtimer", function ()
local _, class = UnitClass("player")
S.isWarrior = (class == "WARRIOR")
S.isDruid = (class == "DRUID")
S.playerGUID = UnitGUID("player")
UpdateWeaponSpeeds()
RebuildQueueSlotCache()
elseif event == "UNIT_INVENTORY_CHANGED" then
if arg1 and arg1 ~= "player" then return end
elseif event == "PLAYER_EQUIPMENT_CHANGED" then
-- arg1 = changed slot; only weapon slots (main/off/ranged) affect swing speed
if not INVSLOTS_EQUIPABLE_IN_COMBAT[arg1] then return end
UpdateWeaponSpeeds()
if S.ohSpeed == 0 then
S.ohActive = false
@@ -910,7 +941,7 @@ pfUI:RegisterModule("swingtimer", function ()
S.maulQueued = false
elseif event == "UNIT_DIED" then
if arg1 and arg1 == S.playerGUID then
if IsPlayerGuid(arg1) then
ResetAll()
end
end
+3 -3
View File
@@ -267,7 +267,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
SW_BarFrame1_Selector:Hide()
-- 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
if page then
local target = (arg1 == "LeftButton") and (page + 1) or (arg1 == "RightButton") and (page - 1)
@@ -943,8 +943,8 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(AtlasLootTooltip)
if pfUI.eqcompare then
HookScript(AtlasLootTooltip, "OnShow", pfUI.eqcompare.GameTooltipShow)
HookScript(AtlasLootTooltip, "OnHide", function()
pfUI.eqcompare.HookTooltip(AtlasLootTooltip)
AtlasLootTooltip:HookScript("OnHide", function()
ShoppingTooltip1:Hide()
ShoppingTooltip2:Hide()
end)
+40 -897
View File
@@ -1,5 +1,5 @@
-- 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 ()
-- Manage Turtle WoW's GroupUI (Turtle_GroupUI addon) vs pfUI frames.
@@ -86,64 +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"]['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
@@ -357,36 +299,54 @@ pfUI:RegisterModule("turtle-wow", function ()
-- break if theres nothing left to do
if initialized then return end
-- adjust ui positions
local _, border = GetBorderSize()
-- adjust the inspect frame's Talents tab
SkinTab(InspectFrameTab3)
InspectFrameTab3:ClearAllPoints()
InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", GetBorderSize()*2 + 1, 0)
TWTalentFrameTab1:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, TWTalentFrameTab1:GetHeight() + 4)
InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", border*2 + 1, 0)
-- reload text position
InspectFrameTab3:Hide()
InspectFrameTab3:Show()
-- skin inspect window elements
StripTextures(InspectTalentsFrame)
StripTextures(TWTalentFrameScrollFrame)
SkinScrollbar(TWTalentFrameScrollFrameScrollBar)
for i = 1, 3 do
SkinTab(_G["TWTalentFrameTab"..i])
end
-- the talent tree frame is created lazily; skin it once it exists
if TWTalentFrame then
StripTextures(InspectTalentsFrame)
StripTextures(TWTalentFrame)
StripTextures(TWTalentFrameScrollFrame)
SkinScrollbar(TWTalentFrameScrollFrameScrollBar)
-- skin each talent button
for i = 1, (MAX_NUM_TALENTS or 100) do
local talent = _G["TWTalentFrameTalent" .. i]
if talent then
StripTextures(talent)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent" .. i .. "IconTexture"])
_G["TWTalentFrameTalent" .. i .. "Rank"]:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
-- skin + position the talent-tree tabs
for i = 1, 3 do
local tab = _G["TWTalentFrameTab"..i]
if tab then
SkinTab(tab)
tab:ClearAllPoints()
local lastTab = _G["TWTalentFrameTab"..(i-1)]
if lastTab then
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else
tab:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, tab:GetHeight() + 4)
end
end
end
end
-- only run once
initialized = true
-- skin each talent button
for i = 1, (MAX_NUM_TALENTS or 100) do
local talent = _G["TWTalentFrameTalent"..i]
if talent then
StripTextures(talent)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent"..i.."IconTexture"])
local rank = _G["TWTalentFrameTalent"..i.."Rank"]
if rank then
rank:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
end
end
end
-- only run once the talent frame has actually been skinned
initialized = true
end
end)
end)
end
@@ -413,823 +373,6 @@ pfUI:RegisterModule("turtle-wow", function ()
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
pfSellData = {
+8 -23
View File
@@ -4,8 +4,6 @@ pfUI:RegisterModule("unusable", function ()
pfUI.unusable = {}
local scanner = libtipscan:GetScanner("unusable")
local durability = string.gsub(DURABILITY_TEMPLATE, "%%[^%s]+", "(.+)")
local r, g, b, a = strsplit(",", C.appearance.bags.unusable_color)
function pfUI.unusable:UpdateSlot(bag, slot)
@@ -13,31 +11,18 @@ pfUI:RegisterModule("unusable", function ()
if not pfUI.bags[bag] then return end
if not pfUI.bags[bag].slots[slot] then return end
-- add button shortcuts
local frame = pfUI.bags[bag].slots[slot].frame
local name = frame:GetName()
-- return on empty buttons
local frame = pfUI.bags[bag].slots[slot].frame
if not frame.hasItem then return end
-- set the proper tooltip method
if bag == BANK_CONTAINER then
scanner:SetInventoryItem("player", 39+slot)
else
scanner:SetBagItem(bag, slot)
-- C_PlayerInfo.CanUseItem is the "is this red in the tooltip" gate:
-- proficiency, required level, class/race, skill/spell/rep. It checks
-- *requirements* only, so a broken (0-durability) item still reads as
-- usable -- no durability-line exclusion needed like the old scanner.
local itemID = C_Container.GetContainerItemID(bag, slot)
if itemID and not C_PlayerInfo.CanUseItem(itemID) then
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end
-- check for red color in tooltip
local red = scanner:Color(RED_FONT_COLOR)
if not red then return end
-- check for broken items
local left = scanner:Line(red)
local _, _, broken = string.find(left, durability, 1)
if broken then return end
-- update button vertex color
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end
-- update on regular pfUI button updates
+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_CAP_MUL = 1.5 -- target rested cap = UnitXPMax * 1.5
if IsTurtleWoW() then
if TURTLE_WOW_VERSION then
REST_CAP_MUL = 1.13
end
+5 -2
View File
@@ -27,7 +27,7 @@ do
-- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 10503 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_MIN = 10605 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
@@ -99,9 +99,12 @@ pfUI.skins = {}
pfUI.environment = {}
pfUI.movables = {}
pfUI.version = {}
pfUI.hooks = {}
pfUI.env = {}
pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
-- check if macro addons are loaded (disables macrotweak/macroscan)
function pfUI:MacroAddonsLoaded()
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:SetPoint("CENTER", 0, 0)
HookScript(BattlefieldMinimap, "OnShow", function()
BattlefieldMinimap:HookScript("OnShow", function()
BattlefieldMinimapTab:Hide()
end)
+185
View File
@@ -0,0 +1,185 @@
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)
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
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
ItemTextScrollFrameScrollBar:Show()
ItemTextScrollFrameScrollBar:Hide()
+4 -4
View File
@@ -11,7 +11,7 @@ pfUI:RegisterSkin("Options - New", function ()
CreateBackdropShadow(OptionsFrame)
EnableMovable(OptionsFrame)
HookScript(OptionsFrame, "OnShow", function()
OptionsFrame:HookScript("OnShow", function()
this:ClearAllPoints()
this:SetPoint("CENTER", 0, 0)
end)
@@ -109,13 +109,13 @@ pfUI:RegisterSkin("Options - New", function ()
local color = PFUI_CLASS_COLORS[class]
SetHighlight(btn, color.r, color.g, color.b)
btn:SetFont(pfUI.font_default, pfUI_config.global.font_size, "OUTLINE")
HookScript(btn, "OnMouseDown", function()
btn:HookScript("OnMouseDown", function()
StripTextures(this, true)
this:SetBackdrop(pfUI.backdrop)
this:SetBackdropColor(br, bg, bb, 0.75)
this:SetBackdropBorderColor(er, eg, eb, 1)
end)
HookScript(btn, "OnMouseUp", function()
btn:HookScript("OnMouseUp", function()
StripTextures(this, true)
this:SetBackdrop(pfUI.backdrop)
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
hooksecurefunc("OptionsListButton_OnClick", SkinControls)
-- also cover initial load
HookScript(OptionsFrame, "OnShow", SkinControls)
OptionsFrame:HookScript("OnShow", SkinControls)
end)
+11 -30
View File
@@ -5,37 +5,18 @@ pfUI:RegisterSkin("Options - Sound", function ()
-- Compatibility
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)
CreateBackdrop(SoundOptionsFramePlayback, nil, true, .75)
CreateBackdrop(SoundOptionsFrameHardware, nil, true, .75)
CreateBackdrop(SoundOptionsFrameVolume, nil, true, .75)
SoundOptionsFrameHeaderText = GetNoNameObject(SoundOptionsFrame, "FontString", "ARTWORK", SOUNDOPTIONS_MENU)
NUM_CHECKBOXES = 8
NUM_SLIDERS = 4
SkinDropDown(SoundOptionsOutputDropDown)
SoundOptionsFrameDefaults:ClearAllPoints()
SoundOptionsFrameDefaults:SetPoint("TOPLEFT", SoundOptionsFramePlayback, "BOTTOMLEFT", 0, -10)
SoundOptionsFrameCancel:ClearAllPoints()
SoundOptionsFrameCancel:SetPoint("TOPRIGHT", SoundOptionsFrameVolume, "BOTTOMRIGHT", 0, -10)
SoundOptionsFrameOkay:ClearAllPoints()
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
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
StripTextures(SoundOptionsFrame)
@@ -44,7 +25,7 @@ pfUI:RegisterSkin("Options - Sound", function ()
EnableMovable(SoundOptionsFrame)
HookScript(SoundOptionsFrame, "OnShow", function()
SoundOptionsFrame:HookScript("OnShow", function()
this:ClearAllPoints()
this:SetPoint("CENTER", 0, 0)
end)
+1 -1
View File
@@ -49,7 +49,7 @@ pfUI:RegisterSkin("Options - Video", function ()
EnableMovable(OptionsFrame)
HookScript(OptionsFrame, "OnShow", function()
OptionsFrame:HookScript("OnShow", function()
this:ClearAllPoints()
this:SetPoint("CENTER", 0, 0)
end)
+2 -2
View File
@@ -102,13 +102,13 @@ pfUI:RegisterSkin("Quest Log", function ()
end
end)
HookScript(QuestLogDetailScrollFrame, "OnHide", function()
QuestLogDetailScrollFrame:HookScript("OnHide", function()
SkinArrowButton(QuestLogFrameExpandButton, "RIGHT", 21)
QuestLogDetailScrollFrame:Hide()
QuestLogFrame:SetWidth(340)
end)
HookScript(QuestLogDetailScrollFrame, "OnShow", function()
QuestLogDetailScrollFrame:HookScript("OnShow", function()
SkinArrowButton(QuestLogFrameExpandButton, "LEFT", 21)
QuestLogDetailScrollFrame:Show()
QuestLogFrame:SetWidth(676)
+2 -2
View File
@@ -7,7 +7,7 @@ pfUI:RegisterSkin("Tooltips", function ()
CreateBackdropShadow(tooltip)
end
HookScript(WorldMapTooltip, "OnShow", function()
WorldMapTooltip:HookScript("OnShow", function()
CreateBackdrop(WorldMapTooltip, nil, nil, alpha)
CreateBackdropShadow(WorldMapTooltip)
end)
@@ -16,7 +16,7 @@ pfUI:RegisterSkin("Tooltips", function ()
for _, tooltip in pairs({ShoppingTooltip1, ShoppingTooltip2}) do
tooltip:SetClampedToScreen(true)
HookScript(tooltip, "OnShow", function()
tooltip:HookScript("OnShow", function()
local a, b, c, x, y = this:GetPoint()
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