25 Commits

Author SHA1 Message Date
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
65 changed files with 396 additions and 1576 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
github: [shagu]
ko_fi: shagu
github: [brues]
buy_me_a_coffee: brues
+12 -4
View File
@@ -1,6 +1,6 @@
# pfUI - ClassicAPI Edition
[![Turtle WoW](https://img.shields.io/badge/Octo%20WoW-1.18.1-brightgreen.svg)](https://octowow.st/)
[![Octo WoW](https://img.shields.io/badge/Octo%20WoW-1.18.1-brightgreen.svg)](https://octowow.st/)
[![ClassicAPI](https://img.shields.io/badge/ClassicAPI-Required-purple.svg)](https://github.com/brues-code/ClassicAPI)
[![SuperWoW](https://img.shields.io/badge/SuperWoW-Required-purple.svg)](https://github.com/balakethelock/SuperWoW)
[![Nampower](https://img.shields.io/badge/Nampower-Required-purple.svg)](https://github.com/brues-code/nampower)
@@ -23,11 +23,19 @@ Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functi
### [ClassicAPI](https://github.com/brues-code/ClassicAPI)
Provides:
- C_NamePlate - Event-driven nameplates (no more per-frame WorldFrame scanning) with real per-plate unit tokens and GUIDs
- C_UnitAuras - Replaces over 3k lines of hand-rolled aura tracking
- C_Spell - Replaces thousands of hardcoded spell names for each locale and powers castbar
- Instant Bag Sorting
- C_EquipmentSet
- Focus
- Real cast bars - Actual cast/channel timing, spellID and interrupt state for any unit (C_Spell.UnitCastingInfo)
- Focus - A genuine `focus` unit plus `/focus` and `/clearfocus` (FocusUnit / ClearFocus)
- C_EquipmentSet - Full Equipment Manager with GUID-tracked gear sets (tells apart duplicate/enchanted items)
- Instant Bag Sorting - C_Container.MoveItem / SwapItems instead of cursor pickup/drop
- Sell junk in one call - C_MerchantFrame.GetNumJunkItems / SellAllJunkItems
- Real item data - C_Item counts, quality, sell price and item info by ID
- GUID-based targeting - Nameplates, mouseover and focus resolve by GUID, not by name text
- Faster, safer profile sharing - Engine-side serialize/compress/base64 via C_EncodingUtil
- C_Timer - After / NewTicker replacing hand-rolled OnUpdate throttles
- Feign death, shapeshift and quest-item detection via real API calls
- Plenty other functions
### [SuperWoW](https://github.com/balakethelock/SuperWoW)
-17
View File
@@ -28,10 +28,6 @@ function pfUI.api.HasNampower()
return GetNampowerVersion and true or false
end
function pfUI.api.IsTurtleWoW()
return C_Spell.GetSpellTexture(46050) == "Interface\\Icons\\Trade_Survival"
end
-- [ GetUnitDistance ]
-- Returns distance to unit using best available method
-- 'unit1' [string] first unit (default: "player")
@@ -546,19 +542,6 @@ do -- create a scope so we don't have to worry about upvalue collisions
end
end
-- [ HookScript ]
-- Securely post-hooks a script handler.
-- 'f' [frame] the frame which needs a hook
-- 'script' [string] the handler to hook
-- 'func' [function] the function that should be added
function HookScript(f, script, func)
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)
end
-- [ HookAddonOrVariable ]
-- Sets a function to be called automatically once an addon gets loaded
-- 'addon' [string] addon or variable name
+7 -1
View File
@@ -714,9 +714,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 +731,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 +745,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")
+3 -5
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
+2 -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
+10 -26
View File
@@ -28,32 +28,16 @@ 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"]
-- Safe post-hook helper. The global `hooksecurefunc` belongs to ClassicAPI
-- (its C implementation); this wrapper only adds pfUI's missing-target guard:
-- ClassicAPI errors when target[name] isn't a function, whereas a lot of our
-- call sites hook optional/late-loaded frames and rely on a silent no-op.
-- Normalizes the string form, skips when the target is absent, then delegates
-- to the C version (uncapped args, callback-pcall, taint parity).
function pfUI.hooksecurefunc(tbl, name, func)
if type(tbl) == "string" then tbl, name, func = _G, tbl, name end
if not tbl or type(tbl[name]) ~= "function" then return end
return _G.hooksecurefunc(tbl, name, func)
end
do -- GetItemInfo
-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",},
}
+6
View File
@@ -750,6 +750,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 +801,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,
+18 -145
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,13 +43,7 @@ if GetNampowerVersion then
end
end
-- Nampower startup check: show version info and ensure CVars are set.
-- Runs the frame after PLAYER_ENTERING_WORLD so Nampower has finished initializing.
local nampowerCheckFrame = CreateFrame("Frame")
nampowerCheckFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
nampowerCheckFrame:SetScript("OnEvent", function()
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
EventUtil.ContinueOnPlayerLogin(function()
RunNextFrame(function()
if GetNampowerVersion then
@@ -58,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",
@@ -89,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")
@@ -202,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.
@@ -278,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,
@@ -461,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
@@ -652,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
@@ -798,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
@@ -825,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
-- ============================================================================
@@ -883,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
@@ -976,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
@@ -1078,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
@@ -1108,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,
@@ -1181,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
@@ -1233,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
@@ -1365,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
@@ -1520,7 +1396,7 @@ if hasNampower then
end
end
local myGuid = GetPlayerGUID()
local myGuid = GetPlayerGuid()
local isOurs = (myGuid and casterGuid == myGuid)
-- Fallback: Check ownDebuffs timing
@@ -1555,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
@@ -1809,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!")
+7 -12
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 "")
@@ -902,7 +897,7 @@ local INSTANT_HOT_COOLDOWN = 1.0 -- 1 Sekunde Cooldown (GCD ist 1.5s)
local pendingHots = {}
-- Gather Data by User Actions
hooksecurefunc("CastSpell", function(id, bookType)
pfUI.hooksecurefunc("CastSpell", function(id, bookType)
if not libpredict.sender.enabled then return end
local effect, rank = libspell.GetSpellInfo(id, bookType)
if not effect then return end
@@ -955,7 +950,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
end
end)
hooksecurefunc("CastSpellByName", function(effect, target)
pfUI.hooksecurefunc("CastSpellByName", function(effect, target)
if not libpredict.sender.enabled then return end
local effect, rank = libspell.GetSpellInfo(effect)
if not effect then return end
@@ -1019,7 +1014,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
end
end)
hooksecurefunc("UseAction", function(slot, target, selfcast)
pfUI.hooksecurefunc("UseAction", function(slot, target, selfcast)
if not libpredict.sender.enabled then return end
if not IsCurrentAction(slot) then return end
local kind, id = GetActionInfo(slot)
+1 -1
View File
@@ -54,6 +54,6 @@ end
pfUI.api.libtooltip = libtooltip
hooksecurefunc(GameTooltip, "SetBagItem", function(self, container, slot)
pfUI.hooksecurefunc(GameTooltip, "SetBagItem", function(self, container, slot)
_, libtooltip.itemCount = GetContainerItemInfo(container, slot)
end)
+3 -3
View File
@@ -241,14 +241,14 @@ pfUI.libdebuff_spell_go_hooks["libtotem"] = function(spellId)
end
-- Hook CastSpellByName to store pending name/icon per slot
hooksecurefunc("CastSpellByName", function(effect, target)
pfUI.hooksecurefunc("CastSpellByName", function(effect, target)
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(effect)
if not name then return end
libtotem:CheckAddQueue(name, rank, icon, spellId)
end)
-- Hook CastSpell to store pending name/icon per slot
hooksecurefunc("CastSpell", function(id, bookType)
pfUI.hooksecurefunc("CastSpell", function(id, bookType)
if not id or not bookType then return end
if bookType ~= BOOKTYPE_SPELL and bookType ~= BOOKTYPE_PET then return end
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(id, bookType)
@@ -259,7 +259,7 @@ end)
-- Hook UseAction. GetActionInfo + GetMacroSpell give us the spellID
-- directly for both spell-action and macro-action slots, so the
-- tooltip-scan fallback (and the "no spellId available" caveat) goes away.
hooksecurefunc("UseAction", function(slot, target, selfcast)
pfUI.hooksecurefunc("UseAction", function(slot, target, selfcast)
if not IsCurrentAction(slot) then return end
local kind, id = GetActionInfo(slot)
local name, rank, spellID
+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 -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 -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")
+7 -4
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.
@@ -487,7 +490,7 @@ pfUI:RegisterModule("castbar", function ()
-- (the config knob is read at event time so toggling takes effect on the
-- next craft without a /reload). DoTradeSkill is synchronous; the server
-- roundtrip to SPELLCAST_START gives us plenty of time after this hook.
hooksecurefunc("DoTradeSkill", function(index, num)
pfUI.hooksecurefunc("DoTradeSkill", function(index, num)
if pfUI.castbar.player then
pfUI.castbar.player.pendingTradeskillCount = tonumber(num) or 1
end
+2 -2
View File
@@ -24,7 +24,7 @@ pfUI:RegisterModule("chat", function ()
end
end
hooksecurefunc("UnitPopup_OnClick", function(self)
pfUI.hooksecurefunc("UnitPopup_OnClick", function(self)
if this.value == "IGNORE_PLAYER" then
AddIgnore(_G[UIDROPDOWNMENU_INIT_MENU].name)
end
@@ -449,7 +449,7 @@ pfUI:RegisterModule("chat", function ()
end
end
hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat)
pfUI.hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat)
if C.chat.global.tabmouse == "1" then
pfUI.chat.mouseovertab = CreateFrame("Frame")
+1 -1
View File
@@ -136,5 +136,5 @@ pfUI:RegisterModule("cooldown", function ()
-- vanilla does not have a cooldown frame type, so we hook the
-- regular SetTimer function that each one is calling.
hooksecurefunc("CooldownFrame_SetTimer", SetCooldown)
pfUI.hooksecurefunc("CooldownFrame_SetTimer", SetCooldown)
end)
+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
+6 -5
View File
@@ -3,8 +3,9 @@ pfUI:RegisterModule("eqcompare", function ()
local function ShowCompareItem(self, link, shift)
self = self or GameTooltip
shift = shift or IsShiftKeyDown()
if not link or (not IsShiftKeyDown() and (C.tooltip.compare.showalways ~= "1" or C_Item.IsEquippedItem(link))) then
if not link or (not shift and (C.tooltip.compare.showalways ~= "1" or C_Item.IsEquippedItem(link))) then
return
end
@@ -88,7 +89,7 @@ pfUI:RegisterModule("eqcompare", function ()
local prevMerchant = ShoppingTooltip1.SetMerchantCompareItem
local function SetMerchantCompareItem(self, index, compareItem)
if C.tooltip.compare.basestats == "1" and compareItem == 1 then
ShowCompareItem(nil, GetMerchantItemLink(index))
ShowCompareItem(nil, GetMerchantItemLink(index), 1)
return false
end
return prevMerchant and prevMerchant(self, index, compareItem)
@@ -97,7 +98,7 @@ pfUI:RegisterModule("eqcompare", function ()
local prevAuction = ShoppingTooltip1.SetAuctionCompareItem
local function SetAuctionCompareItem(self, type, index, compareItem)
if C.tooltip.compare.basestats == "1" and compareItem == 1 then
ShowCompareItem(nil, GetAuctionItemLink(type, index))
ShowCompareItem(nil, GetAuctionItemLink(type, index), 1)
return false
end
return prevAuction and prevAuction(self, type, index, compareItem)
@@ -110,7 +111,7 @@ pfUI:RegisterModule("eqcompare", function ()
local TooltipHooks = {
SetLootRollItem = GetLootRollItemLink,
SetLootItem = GetLootSlotItemLink,
SetLootItem = GetLootSlotLink,
SetQuestLogItem = GetQuestLogItemLink,
SetQuestItem = GetQuestItemLink,
SetHyperlink = function(link) return link end,
@@ -139,7 +140,7 @@ pfUI:RegisterModule("eqcompare", function ()
local function HookTooltip(tooltip)
for setter, getter in pairs(TooltipHooks) do
_G['hooksecurefunc'](tooltip, setter, makeHook(getter))
pfUI.hooksecurefunc(tooltip, setter, makeHook(getter))
end
end
+4 -5
View File
@@ -940,10 +940,10 @@ pfUI:RegisterModule("equipmentmanager", function()
-- Tie popout visibility to the EM sidecar. They appear when the
-- 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)
+2 -2
View File
@@ -150,7 +150,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
hooksecurefunc("WhoList_Update", function()
pfUI.hooksecurefunc("WhoList_Update", function()
for i = 1, WHOS_TO_DISPLAY do
local level = _G["WhoFrameButton"..i.."Level"]
level:ClearAllPoints()
@@ -231,7 +231,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
hooksecurefunc("GuildStatus_Update", function()
pfUI.hooksecurefunc("GuildStatus_Update", function()
for i = 1, GUILDMEMBERS_TO_DISPLAY do
local level = _G["GuildFrameButton"..i.."Level"]
level:ClearAllPoints()
+1 -1
View File
@@ -86,7 +86,7 @@ pfUI:RegisterModule("gm", function ()
-- pet dropdown
-- table.insert(UnitPopupMenus["PET"], "GM_HEADER")
hooksecurefunc("UnitPopup_OnClick", function()
pfUI.hooksecurefunc("UnitPopup_OnClick", function()
local dropdownFrame = _G[UIDROPDOWNMENU_INIT_MENU]
local button = this.value
local unit = dropdownFrame.unit
+30 -16
View File
@@ -912,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"],
@@ -2775,55 +2780,64 @@ pfUI:RegisterModule("gui", function ()
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()
+1 -10
View File
@@ -13,15 +13,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 +66,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"
+1 -1
View File
@@ -41,7 +41,7 @@ pfUI:RegisterModule("itemcount", function ()
end
end)
hooksecurefunc("SetItemRef", function()
pfUI.hooksecurefunc("SetItemRef", function()
if ItemRefTooltip:HasItem() then
local _, _, id = ItemRefTooltip:GetItem()
if id then AddCounts(ItemRefTooltip, id) end
+2 -2
View File
@@ -392,7 +392,7 @@ pfUI:RegisterModule("loot", function ()
end
pfUI.loot:RemoveMasterlootMenus() -- remove then add to ensure no duplicate menus
pfUI.loot:AddMasterLootMenus()
hooksecurefunc("UnitPopup_OnClick",function()
pfUI.hooksecurefunc("UnitPopup_OnClick",function()
local dropdownFrame = _G[UIDROPDOWNMENU_INIT_MENU]
if not dropdownFrame then return end
local button = this.value
@@ -405,7 +405,7 @@ pfUI:RegisterModule("loot", function ()
end
end
end)
hooksecurefunc("UnitPopup_HideButtons",function()
pfUI.hooksecurefunc("UnitPopup_HideButtons",function()
local dropdownFrame = _G[UIDROPDOWNMENU_INIT_MENU]
local unit = dropdownFrame.unit
local name = dropdownFrame.name
+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)
+4 -4
View File
@@ -125,13 +125,13 @@ pfUI:RegisterModule("mapcolors", function ()
-- WorldMap
Initialize('WorldMap')
hooksecurefunc('WorldMapButton_OnUpdate', function()
pfUI.hooksecurefunc('WorldMapButton_OnUpdate', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitFrames('WorldMap')
end)
if C.appearance.worldmap.colornames == "1" then
hooksecurefunc('WorldMapUnit_OnEnter', function()
pfUI.hooksecurefunc('WorldMapUnit_OnEnter', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitColors('WorldMap', WorldMapTooltip)
end)
@@ -141,13 +141,13 @@ pfUI:RegisterModule("mapcolors", function ()
HookAddonOrVariable("Blizzard_BattlefieldMinimap", function()
Initialize('BattlefieldMinimap')
hooksecurefunc('BattlefieldMinimap_OnUpdate', function()
pfUI.hooksecurefunc('BattlefieldMinimap_OnUpdate', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitFrames('BattlefieldMinimap')
end)
if C.appearance.worldmap.colornames == "1" then
hooksecurefunc('BattlefieldMinimapUnit_OnEnter', function()
pfUI.hooksecurefunc('BattlefieldMinimapUnit_OnEnter', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitColors('BattlefieldMinimap', GameTooltip)
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 -1
View File
@@ -58,7 +58,7 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimap:UpdateConfig()
hooksecurefunc("ToggleMinimap", function()
pfUI.hooksecurefunc("ToggleMinimap", function()
if pfUI.farmmap and pfUI.farmmap:IsShown() then
Minimap:Hide()
return
+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)
+1 -1
View File
@@ -141,7 +141,7 @@ pfUI:RegisterModule("player", function ()
end
end
hooksecurefunc("UnitPopup_OnClick", function()
pfUI.hooksecurefunc("UnitPopup_OnClick", function()
local button = this.value
if button == "RESET_INSTANCES_FIX" then
StaticPopup_Show("CONFIRM_RESET_INSTANCES")
+1 -1
View File
@@ -95,7 +95,7 @@ pfUI:RegisterModule("questitem", function ()
-- itemref tooltip (chat link clicks): hooksecurefunc runs after SetItemRef
-- populates ItemRefTooltip, so we just read the item back out of the tooltip
-- instead of re-parsing the "item:NNN" out of the link string.
hooksecurefunc("SetItemRef", function()
pfUI.hooksecurefunc("SetItemRef", function()
if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then
local _, _, id = ItemRefTooltip:GetItem()
+1 -1
View File
@@ -164,7 +164,7 @@ pfUI:RegisterModule("raid", function ()
end
end
hooksecurefunc("UnitPopup_OnClick", function()
pfUI.hooksecurefunc("UnitPopup_OnClick", function()
local dropdownFrame = UIDROPDOWNMENU_INIT_MENU and _G[UIDROPDOWNMENU_INIT_MENU]
if not dropdownFrame then return end
local button = this.value
+1 -1
View File
@@ -40,7 +40,7 @@ pfUI:RegisterModule("sellvalue", function ()
end
end)
hooksecurefunc("SetItemRef", function()
pfUI.hooksecurefunc("SetItemRef", function()
if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then
local _, _, id = ItemRefTooltip:GetItem()
+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)
+2 -2
View File
@@ -51,12 +51,12 @@ pfUI:RegisterModule("skin", function ()
DurabilityFrame.SetPoint = function() return end
if C.appearance.cd.blizzard == "1" then
hooksecurefunc("PaperDollItemSlotButton_Update", function()
pfUI.hooksecurefunc("PaperDollItemSlotButton_Update", function()
local cooldown = _G[this:GetName().."Cooldown"]
if cooldown then cooldown.pfCooldownType = "BLIZZARD" end
end)
hooksecurefunc("SpellButton_UpdateButton", function()
pfUI.hooksecurefunc("SpellButton_UpdateButton", function()
local cooldown = _G[this:GetName().."Cooldown"]
if cooldown then cooldown.pfCooldownType = "BLIZZARD" end
end)
+3 -3
View File
@@ -10,7 +10,7 @@ pfUI:RegisterModule("socialmod", function ()
end
end)
do -- add colors to guild list
hooksecurefunc("GuildStatus_Update", function()
pfUI.hooksecurefunc("GuildStatus_Update", function()
local playerzone = GetRealZoneText()
local off = FauxScrollFrame_GetOffset(GuildListScrollFrame)
for i=1, GUILDMEMBERS_TO_DISPLAY, 1 do
@@ -70,7 +70,7 @@ pfUI:RegisterModule("socialmod", function ()
end
do -- add colors to friend list
hooksecurefunc("FriendsList_Update", function()
pfUI.hooksecurefunc("FriendsList_Update", function()
if GetNumFriends() == 0 then return end
local playerzone = GetRealZoneText()
@@ -123,7 +123,7 @@ pfUI:RegisterModule("socialmod", function ()
end
do -- add colors to who list
hooksecurefunc("WhoList_Update", function()
pfUI.hooksecurefunc("WhoList_Update", function()
local num, max = GetNumWhoResults()
local off = FauxScrollFrame_GetOffset(WhoListScrollFrame)
+10 -9
View File
@@ -32,7 +32,6 @@ pfUI:RegisterModule("swingtimer", function ()
isDruid = false,
cachedHSSlots = {}, cachedCleaveSlots = {},
useSpellQueueEvent = false,
playerGUID = nil,
swingThrottle = 0,
onSwingCache = {},
}
@@ -781,7 +780,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 +791,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 +834,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 +881,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 +911,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
+7 -7
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)
@@ -530,7 +530,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
end
-- replace wim class colors with pfUI ones
hooksecurefunc("WIM_InitClassProps", function()
pfUI.hooksecurefunc("WIM_InitClassProps", function()
for class in pairs(PFUI_CLASS_COLORS) do
local wimclass = _G[format("WIM_LOCALIZED_%s",class)]
local colorstr = "|c" .. PFUI_CLASS_COLORS[class].colorStr
@@ -547,7 +547,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
btnClose:SetWidth(13)
btnClose:SetHeight(13)
end
hooksecurefunc("WIM_Icon_DropDown_Update", function()
pfUI.hooksecurefunc("WIM_Icon_DropDown_Update", function()
for i=1,_G.WIM_MaxMenuCount do
local btn = _G["WIM_ConversationMenuTellButton"..i]
if i==1 and btn:IsEnabled() == 0 then return end
@@ -729,7 +729,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
SkinScrollbar(WIM_HelpScrollFrameScrollBar)
end
hooksecurefunc("WIM_WindowOnShow", function()
pfUI.hooksecurefunc("WIM_WindowOnShow", function()
if this.backdrop then return end -- already skinned
local windowname = this:GetName()
@@ -904,7 +904,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
end
-- trigger the event whenever SuperMacro got an update
hooksecurefunc("SM_UpdateActionSpell", function()
pfUI.hooksecurefunc("SM_UpdateActionSpell", function()
for slot=1,120 do pfUI.bars.update[slot] = true end
end)
end)
@@ -922,7 +922,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
pfUI.bars.skip_macro = true
-- send clevermacro events to pfUI actionbars
hooksecurefunc("ActionButton_OnEvent", function(event)
pfUI.hooksecurefunc("ActionButton_OnEvent", function(event)
events(this, event)
end)
end)
@@ -944,7 +944,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
if pfUI.eqcompare then
pfUI.eqcompare.HookTooltip(AtlasLootTooltip)
HookScript(AtlasLootTooltip, "OnHide", function()
AtlasLootTooltip:HookScript("OnHide", function()
ShoppingTooltip1:Hide()
ShoppingTooltip2:Hide()
end)
+4 -879
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.
@@ -65,14 +65,14 @@ pfUI:RegisterModule("turtle-wow", function ()
HookAddonOrVariable("GroupFrame", function()
-- After Turtle's own init, hide frames if pfUI handles them
hooksecurefunc("GroupFrame_Toggle", function()
pfUI.hooksecurefunc("GroupFrame_Toggle", function()
if pfUIHandlesGroupOrRaid() then
DisableTurtleGroupFrames()
end
end)
-- After every group/raid update, re-hide if pfUI handles them
hooksecurefunc("GroupFrame_Update", function()
pfUI.hooksecurefunc("GroupFrame_Update", function()
if pfUIHandlesGroupOrRaid() then
DisableTurtleGroupFrames()
end
@@ -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
@@ -353,7 +295,7 @@ pfUI:RegisterModule("turtle-wow", function ()
local initialized = false
HookAddonOrVariable("Blizzard_InspectUI", function()
hooksecurefunc("InspectFrame_Show", function()
pfUI.hooksecurefunc("InspectFrame_Show", function()
-- break if theres nothing left to do
if initialized then return end
@@ -413,823 +355,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 = {
+2 -2
View File
@@ -41,12 +41,12 @@ pfUI:RegisterModule("unusable", function ()
end
-- update on regular pfUI button updates
hooksecurefunc(pfUI.bag, "UpdateSlot", function(self, bag, slot)
pfUI.hooksecurefunc(pfUI.bag, "UpdateSlot", function(self, bag, slot)
pfUI.unusable:UpdateSlot(bag, slot)
end)
-- update on bank frame itemlock updates
hooksecurefunc("BankFrameItemButton_UpdateLock", function()
pfUI.hooksecurefunc("BankFrameItemButton_UpdateLock", function()
pfUI.unusable:UpdateSlot(-1, this:GetID())
end)
end)
+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
+1 -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 = 10507 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_MIN = 10600 -- (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,7 +99,6 @@ pfUI.skins = {}
pfUI.environment = {}
pfUI.movables = {}
pfUI.version = {}
pfUI.hooks = {}
pfUI.env = {}
pfUI.events = Mixin({}, CallbackRegistryMixin)
+5 -5
View File
@@ -12,7 +12,7 @@ pfUI:RegisterSkin("Auctionhouse", function ()
SkinArrowButton(AuctionsNextPageButton, "right", 18)
end
hooksecurefunc("AuctionFrame_OnShow", function()
pfUI.hooksecurefunc("AuctionFrame_OnShow", function()
AuctionFrame:ClearAllPoints()
AuctionFrame:SetPoint("TOPLEFT", 10, -104)
end)
@@ -76,7 +76,7 @@ pfUI:RegisterSkin("Auctionhouse", function ()
item:SetPoint("LEFT", 2, 0)
end
end
hooksecurefunc("AuctionFrameBrowse_Update", function()
pfUI.hooksecurefunc("AuctionFrameBrowse_Update", function()
for i = 1, NUM_BROWSE_TO_DISPLAY do
HandleIcon(_G["BrowseButton"..i.."Item"], _G["BrowseButton"..i.."ItemIconTexture"])
end
@@ -145,7 +145,7 @@ pfUI:RegisterSkin("Auctionhouse", function ()
item:ClearAllPoints()
item:SetPoint("LEFT", 2, 0)
end
hooksecurefunc("AuctionFrameBid_Update", function()
pfUI.hooksecurefunc("AuctionFrameBid_Update", function()
for i = 1, NUM_BIDS_TO_DISPLAY do
HandleIcon(_G["BidButton"..i.."Item"], _G["BidButton"..i.."ItemIconTexture"])
end
@@ -191,14 +191,14 @@ pfUI:RegisterSkin("Auctionhouse", function ()
item:ClearAllPoints()
item:SetPoint("LEFT", 2, 0)
end
hooksecurefunc("AuctionFrameAuctions_Update", function()
pfUI.hooksecurefunc("AuctionFrameAuctions_Update", function()
for i = 1, NUM_AUCTIONS_TO_DISPLAY do
HandleIcon(_G["AuctionsButton"..i.."Item"], _G["AuctionsButton"..i.."ItemIconTexture"])
end
end)
SkinButton(AuctionsItemButton)
hooksecurefunc("AuctionSellItemButton_OnEvent", function()
pfUI.hooksecurefunc("AuctionSellItemButton_OnEvent", function()
if event ~= "NEW_AUCTION_UPDATE" then return end
HandleIcon(AuctionsItemButton, AuctionsItemButton:GetNormalTexture())
end)
+2 -2
View File
@@ -15,11 +15,11 @@ pfUI:RegisterSkin("Battlefield Minimap", function ()
BattlefieldMinimapTabText:ClearAllPoints()
BattlefieldMinimapTabText:SetPoint("CENTER", 0, 0)
HookScript(BattlefieldMinimap, "OnShow", function()
BattlefieldMinimap:HookScript("OnShow", function()
BattlefieldMinimapTab:Hide()
end)
hooksecurefunc("BattlefieldMinimap_ShowOpacity", function()
pfUI.hooksecurefunc("BattlefieldMinimap_ShowOpacity", function()
OpacityFrame:ClearAllPoints()
OpacityFrame:SetPoint("TOPRIGHT", "BattlefieldMinimap", "TOPLEFT", -2*border, 0)
end)
+4 -4
View File
@@ -136,18 +136,18 @@ pfUI:RegisterSkin("Character", function ()
end
end
hooksecurefunc("CharacterFrame_OnShow", function()
pfUI.hooksecurefunc("CharacterFrame_OnShow", function()
RefreshCharacterSlots()
RefreshPetPosition()
end)
hooksecurefunc("PaperDollItemSlotButton_Update", function()
pfUI.hooksecurefunc("PaperDollItemSlotButton_Update", function()
if this:GetParent() == PaperDollFrame then
RefreshCharacterSlot(this)
end
end)
hooksecurefunc("PetTab_Update", RefreshPetPosition)
pfUI.hooksecurefunc("PetTab_Update", RefreshPetPosition)
StripTextures(PaperDollFrame)
StripTextures(CharacterAttributesFrame)
@@ -270,7 +270,7 @@ pfUI:RegisterSkin("Character", function ()
-- the FactionStanding text from `bar.standingText` on mouseout, so we
-- stash our augmented text there too — otherwise hovering a bar strips
-- the "(N)" suffix off.
hooksecurefunc("ReputationFrame_Update", function()
pfUI.hooksecurefunc("ReputationFrame_Update", function()
if C.character.reputation.repRequired ~= "1" then return end
local offset = FauxScrollFrame_GetOffset(ReputationListScrollFrame)
for i = 1, NUM_FACTIONS_DISPLAYED do
+2 -2
View File
@@ -150,7 +150,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
hooksecurefunc("WhoList_Update", function()
pfUI.hooksecurefunc("WhoList_Update", function()
for i = 1, WHOS_TO_DISPLAY do
local level = _G["WhoFrameButton"..i.."Level"]
level:ClearAllPoints()
@@ -231,7 +231,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
hooksecurefunc("GuildStatus_Update", function()
pfUI.hooksecurefunc("GuildStatus_Update", function()
for i = 1, GUILDMEMBERS_TO_DISPLAY do
local level = _G["GuildFrameButton"..i.."Level"]
level:ClearAllPoints()
+2 -2
View File
@@ -24,11 +24,11 @@ pfUI:RegisterSkin("Gossip and Quest", function ()
QuestRewardItemHighlightBG:SetTexture(1,1,1,.2)
QuestRewardItemHighlightBG:SetAllPoints()
hooksecurefunc("QuestFrameItems_Update", function()
pfUI.hooksecurefunc("QuestFrameItems_Update", function()
QuestRewardItemHighlight:Hide()
end)
hooksecurefunc("QuestRewardItem_OnClick", function()
pfUI.hooksecurefunc("QuestRewardItem_OnClick", function()
if this.type == "choice" then
QuestRewardItemHighlight:SetAllPoints(this.backdrop)
QuestRewardItemHighlight:Show()
+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()
+2 -2
View File
@@ -12,7 +12,7 @@ pfUI:RegisterSkin("Mailbox", function ()
StripTextures(SendMailPackageButton)
SkinButton(SendMailPackageButton, nil, nil, nil, nil, true)
hooksecurefunc("SendMailFrame_Update", function()
pfUI.hooksecurefunc("SendMailFrame_Update", function()
HandleIcon(SendMailPackageButton, SendMailPackageButton:GetNormalTexture())
local _, itemID = GetSendMailItemLink()
@@ -62,7 +62,7 @@ pfUI:RegisterSkin("Mailbox", function ()
do -- OpenMailFrame
SkinButton(OpenMailPackageButton, nil, nil, nil, OpenMailPackageButtonIconTexture)
hooksecurefunc("InboxFrame_OnClick", function(index)
pfUI.hooksecurefunc("InboxFrame_OnClick", function(index)
local _, itemID = GetInboxItemLink(index)
if itemID then
local quality = C_Item.GetItemQualityByID(itemID)
+2 -2
View File
@@ -6,7 +6,7 @@ pfUI:RegisterSkin("Merchant", function ()
if MerchantGuildBankRepairButton then -- tbc
SkinButton(MerchantGuildBankRepairButton, nil, nil, nil, MerchantGuildBankRepairButtonIcon)
MerchantGuildBankRepairButtonIcon:SetTexCoord(.59, .82, .06, .54)
hooksecurefunc("MerchantFrame_UpdateRepairButtons", function()
pfUI.hooksecurefunc("MerchantFrame_UpdateRepairButtons", function()
MerchantGuildBankRepairButton:ClearAllPoints()
MerchantGuildBankRepairButton:SetPoint("RIGHT", MerchantBuyBackItemItemButton, "LEFT", -14, 0)
MerchantRepairAllButton:ClearAllPoints()
@@ -56,7 +56,7 @@ pfUI:RegisterSkin("Merchant", function ()
moneyFrame:SetPoint("BOTTOMLEFT", itemButton, "BOTTOMRIGHT", 5, 1)
end
hooksecurefunc("MerchantFrame_UpdateMerchantInfo", function()
pfUI.hooksecurefunc("MerchantFrame_UpdateMerchantInfo", function()
if MerchantFrame.selectedTab == 1 then
for i = 3, 11, 2 do
_G["MerchantItem"..i]:ClearAllPoints()
+5 -5
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)
@@ -184,7 +184,7 @@ pfUI:RegisterSkin("Options - New", function ()
end
-- hook after category selection: UpdateOptions is local so we hook its caller
hooksecurefunc("OptionsListButton_OnClick", SkinControls)
pfUI.hooksecurefunc("OptionsListButton_OnClick", SkinControls)
-- also cover initial load
HookScript(OptionsFrame, "OnShow", SkinControls)
OptionsFrame:HookScript("OnShow", SkinControls)
end)
+1 -1
View File
@@ -44,7 +44,7 @@ pfUI:RegisterSkin("Options - Sound", function ()
EnableMovable(SoundOptionsFrame)
HookScript(SoundOptionsFrame, "OnShow", function()
SoundOptionsFrame:HookScript("OnShow", function()
this:ClearAllPoints()
this:SetPoint("CENTER", 0, 0)
end)
+2 -2
View File
@@ -19,7 +19,7 @@ pfUI:RegisterSkin("Options - Video", function ()
slider:SetPoint(point, anchor, anchorPoint, x, y - shift)
end
hooksecurefunc("OptionsFrame_Load", function()
pfUI.hooksecurefunc("OptionsFrame_Load", function()
OptionsFramePixelShaders:SetWidth(230)
OptionsFrameMiscellaneous:ClearAllPoints()
OptionsFrameMiscellaneous:SetPoint("LEFT", OptionsFramePixelShaders, "RIGHT", 6, 0)
@@ -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)
+1 -1
View File
@@ -192,7 +192,7 @@ pfUI:RegisterSkin("Profession", function ()
reagentlabel:SetTextColor(1,1,1,1)
local scanner = libtipscan:GetScanner(name)
hooksecurefunc(SetSelection, function(id)
pfUI.hooksecurefunc(SetSelection, function(id)
if id and id ~= 0 then
detailscroll:Show()
HandleIcon(icon, icon:GetNormalTexture())
+5 -5
View File
@@ -13,7 +13,7 @@ pfUI:RegisterSkin("Quest Log", function ()
StripTextures(QUEST_COUNT)
QUEST_COUNT:ClearAllPoints()
hooksecurefunc("QuestLogUpdateQuestCount", function(numQuests)
pfUI.hooksecurefunc("QuestLogUpdateQuestCount", function(numQuests)
QUEST_COUNT:ClearAllPoints()
QUEST_COUNT:SetPoint("BOTTOMRIGHT", QuestLogFrame, "TOPRIGHT", 0, -50)
end)
@@ -24,7 +24,7 @@ pfUI:RegisterSkin("Quest Log", function ()
QUEST_COUNT:SetPoint("TOPRIGHT", -10, -30)
end
hooksecurefunc("QuestLog_OnShow", function()
pfUI.hooksecurefunc("QuestLog_OnShow", function()
QuestLogFrame:ClearAllPoints()
QuestLogFrame:SetPoint("TOPLEFT", 10, -104)
end)
@@ -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)
@@ -163,7 +163,7 @@ pfUI:RegisterSkin("Quest Log", function ()
QuestLogListScrollFrame:SetPoint("TOPLEFT", 10, -54)
QuestLogListScrollFrame:SetHeight(350)
hooksecurefunc("QuestLog_Update", function()
pfUI.hooksecurefunc("QuestLog_Update", function()
local numEntries = GetNumQuestLogEntries()
local questIndex, text, level, questTag, isHeader
+2 -2
View File
@@ -38,12 +38,12 @@ pfUI:RegisterSkin("Readycheck", function ()
frame.bar.text:SetPoint("CENTER", 0, 0)
local max
hooksecurefunc("ShowReadyCheck", function()
pfUI.hooksecurefunc("ShowReadyCheck", function()
max = ReadyCheckFrame.timer
frame.bar:SetMinMaxValues(0, max)
end)
hooksecurefunc(update_func, function()
pfUI.hooksecurefunc(update_func, function()
if not ReadyCheckFrame.timer then return end
local perc = ReadyCheckFrame.timer/max
+1 -1
View File
@@ -32,7 +32,7 @@ pfUI:RegisterSkin("GM Survey", function ()
CreateBackdrop(GMSurveyCommentFrame, nil, true, .75)
SkinScrollbar(GMSurveyCommentScrollFrameScrollBar)
GMSurveyFrameComment:SetMaxLetters(2000)
hooksecurefunc("GMSurveyFrame_Update", function()
pfUI.hooksecurefunc("GMSurveyFrame_Update", function()
GMSurveyFrameComment:SetWidth(505)
end)
end)
+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
+1 -1
View File
@@ -41,7 +41,7 @@ pfUI:RegisterSkin("Trade", function ()
RecipientButtonBG:SetAllPoints()
end
hooksecurefunc("TradeFrame_UpdateTargetItem", function(id)
pfUI.hooksecurefunc("TradeFrame_UpdateTargetItem", function(id)
HandleIcon(_G["TradeRecipientItem"..id.."ItemButton"], _G["TradeRecipientItem"..id..'IconTexture'])
end)
+1 -1
View File
@@ -36,7 +36,7 @@ pfUI:RegisterSkin("Trainer", function ()
StripTextures(ClassTrainerSkillIcon)
SkinButton(ClassTrainerSkillIcon, nil, nil, nil, nil, true)
hooksecurefunc("ClassTrainer_SetSelection", function()
pfUI.hooksecurefunc("ClassTrainer_SetSelection", function()
HandleIcon(ClassTrainerSkillIcon, ClassTrainerSkillIcon:GetNormalTexture())
end)