Author SHA1 Message Date
Brues 8ea5c81789 bump CAPI min to 1.12.4 2026-08-24 23:40:01 -05:00
Brues 39a46563a1 tracking: use native ClassicAPI tracking API
Replace the hardcoded knownTrackingSpells class table and the
icon-substring spellbook scan with ClassicAPI's native tracking
functions:

- RefreshSpells now enumerates GetNumTrackingTypes() / GetTrackingInfo(),
  which the DLL derives from the spellbook by tracking-aura effect. This
  drops the per-class spell/icon table and picks up server-custom
  trackers (e.g. Turtle's Find Trees) automatically.
- Selecting a tracker in the menu uses SetTracking(index) instead of
  CastSpell(slot, BOOKTYPE_SPELL).
- Keep the Druid Cat Form gate on Track Humanoids (5225) and the
  existing invalidSpells filter. Falls back to no entries if the tracking
  API is absent.

Active icon (GetTrackingTexture), cancel (CancelTrackingBuff), and the
hover tooltip (GameTooltip:SetTrackingSpell) were already native and are
unchanged.
2026-08-24 23:35:49 -05:00
55 changed files with 6189 additions and 6577 deletions
+3 -3
View File
@@ -23,12 +23,12 @@ jobs:
IFS=. read -r MAJOR MINOR PATCH <<<"$LATEST" IFS=. read -r MAJOR MINOR PATCH <<<"$LATEST"
PACKED=$((MAJOR * 10000 + MINOR * 100 + PATCH)) PACKED=$((MAJOR * 10000 + MINOR * 100 + PATCH))
echo "Pinning PFUI_CLASSIC_API_LATEST to $LATEST ($PACKED)" echo "Pinning PFUI_CLASSIC_API_LATEST to $LATEST ($PACKED)"
sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" API_Check.lua sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" pfUI.lua
grep 'local PFUI_CLASSIC_API_' API_Check.lua grep 'local PFUI_CLASSIC_API_' pfUI.lua
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Package and release to GitHub - name: Package and release to GitHub
uses: brues-code/packager@vCAPI uses: BigWigsMods/packager@v2
env: env:
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }} GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
-22
View File
@@ -1,22 +0,0 @@
{
"runtime": {
"version": "Lua 5.1"
},
"diagnostics": {
"disable": ["deprecated"],
"globals": [
"this",
"event",
"arg",
"arg1",
"arg2",
"arg3",
"arg4",
"arg5",
"arg6",
"arg7",
"arg8",
"arg9"
]
}
}
-4
View File
@@ -1,4 +0,0 @@
package-as: pfUI
ignore:
- .luarc.json
-78
View File
@@ -1,78 +0,0 @@
do
-- ClassicAPI dependency gate.
--
-- Which TOC the client opened already answers "is ClassicAPI here?". Whenever
-- the DLL is loaded it redirects the read of `pfUI\pfUI.toc` to
-- `pfUI_Turtle.toc` (Turtle clients) or `pfUI_ClassicAPI.toc` (everything
-- else). Reaching this file from the plain `pfUI.toc` therefore means
-- ClassicAPI is absent -- and that TOC deliberately loads nothing but this
-- file, so there is nothing to disable, only a notice to show.
--
-- The flavor TOCs still need a version gate of their own: the redirect has
-- existed since ClassicAPI v1.11.0, well below the API surface pfUI relies
-- on. There pfUI does load, and will throw wherever it reaches for something
-- the installed DLL doesn't have yet -- the popup names the cause so those
-- errors aren't a mystery.
local PFUI_CLASSIC_API_MIN = 11500 -- (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"
local function FormatVersion(packed)
local x = math.floor(packed / 10000)
local y = math.floor(math.mod(packed, 10000) / 100)
local z = math.mod(packed, 100)
return string.format("v%d.%d.%d", x, y, z)
end
local headline, detail
if not CLASSIC_API_VERSION then
headline = "|cff33ffccpf|cffffffffUI|r has been disabled."
detail = "The ClassicAPI DLL isn't loaded. Download the latest release from:"
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
headline = "|cff33ffccpf|cffffffffUI|r will not work correctly on ClassicAPI " .. FormatVersion(CLASSIC_API_VERSION) .. "."
detail = FormatVersion(PFUI_CLASSIC_API_MIN) .. " or newer is required -- any errors alongside this one are the APIs it is missing. Download the latest release from:"
end
if detail then
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = headline .. "\n\n" .. detail,
button1 = OKAY,
hasEditBox = 1,
editBoxWidth = 280,
timeout = 0,
whileDead = 1,
hideOnEscape = 1,
preferredIndex = 3,
OnShow = function()
local editBox = getglobal(this:GetName().."EditBox")
if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText()
editBox:SetFocus()
end
end,
}
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage(
headline .. " " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3
)
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function()
C_Timer.After(8, function()
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|rUI: ClassicAPI " .. FormatVersion(PFUI_CLASSIC_API_LATEST) .. " is available — " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.85, 0.3
)
end)
end)
end
end
-59
View File
@@ -204,26 +204,6 @@ function pfUI.api.UnitHasBuff(unit, name)
return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil
end end
-- [ ScanAuraSlots ]
-- Fills `buf` with the slot ids of the auras on `unit` that match `filter` and
-- returns how many (buf[1..n]; entries past n are cleared), so a module can
-- keep one buffer and refill it every refresh. Read each aura with
-- C_UnitAuras.UnitAuraBySlot(unit, buf[i]) (positional, no table) or
-- GetAuraDataBySlot. One enumeration walks the aura array once, where a
-- by-index loop (UnitAura(unit, i)) re-walks it from the start for every i.
-- Uses GetAuraSlots' fill-a-table form (table as the 5th argument) instead of
-- its vararg return: Lua 5.0 builds an `arg` table for every vararg call, so
-- collecting the returns in a Lua helper would allocate once per scan.
-- unit [string] unit token
-- filter [string] aura filter ("HELPFUL", "HARMFUL|PLAYER", ...)
-- buf [table] reusable buffer, filled in place
-- max [number] optional cap on the slot count (nil = all)
-- return: [number] count of slot ids written to buf
function pfUI.api.ScanAuraSlots(unit, filter, buf, max)
local _, n = C_UnitAuras.GetAuraSlots(unit, filter, max, nil, buf)
return n
end
-- [ IsPlayerGuid ] -- [ IsPlayerGuid ]
-- Returns whether a GUID or unit token refers to the local player. -- Returns whether a GUID or unit token refers to the local player.
-- guid [string] A unit GUID (or unitID) to test. -- guid [string] A unit GUID (or unitID) to test.
@@ -262,45 +242,6 @@ function pfUI.api.GetUnitColor(unitstr)
return classColor:GenerateHexColorMarkup(), classColor:GetRGB() return classColor:GenerateHexColorMarkup(), classColor:GetRGB()
end end
-- [ GetRaceIcon ]
-- Builds an inline race icon texture from the shared races atlas.
-- 'raceKey' [string] englishRace key (e.g. "NightElf")
-- 'sex' [int] unit sex (3 = female)
-- return: [string] inline texture escape, or an empty string
function pfUI.api.GetRaceIcon(raceKey, sex)
if not raceKey then return "" end
local gender = sex == 3 and "FEMALE" or "MALE"
local coords = RACE_ICON_TCOORDS[strupper(raceKey) .. "_" .. gender]
if not coords then return "" end
return string.format(
"|TInterface\\Glues\\CharacterCreate\\UI-CharacterCreate-Races:0:0:0:0:512:512:%d:%d:%d:%d|t",
coords[1] * 512, coords[2] * 512, coords[3] * 512, coords[4] * 512)
end
-- inline faction emblems (cropped from the target-frame PvP banners)
local FACTION_ICON = {
Alliance = "|TInterface\\TargetingFrame\\UI-PVP-Alliance:0:0:0:0:64:64:5:37:3:35|t",
Horde = "|TInterface\\TargetingFrame\\UI-PVP-Horde:0:0:0:0:64:64:5:37:3:35|t",
}
-- [ GetFactionIcon ]
-- Resolves an inline faction emblem from the englishRace key stored in L["race"].
-- 'raceKey' [string] englishRace key (e.g. "Orc")
-- return: [string] inline texture escape, or an empty string
function pfUI.api.GetFactionIcon(raceKey)
local info = raceKey and L["race"][raceKey]
if not info then return "" end
return FACTION_ICON[info.faction] or ""
end
-- [ GetPlayerRaceIcons ]
-- Returns the faction and race emblems of the player character.
-- return: [string] both inline textures, or an empty string
function pfUI.api.GetPlayerRaceIcons()
local _, raceKey = UnitRace("player")
return pfUI.api.GetFactionIcon(raceKey) .. pfUI.api.GetRaceIcon(raceKey, UnitSex("player"))
end
-- [ strvertical ] -- [ strvertical ]
-- Creates vertical text using linebreaks. Multibyte char friendly. -- Creates vertical text using linebreaks. Multibyte char friendly.
-- 'str' [string] String to columnize. -- 'str' [string] String to columnize.
+1 -1
View File
@@ -645,6 +645,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "animation", "zoomfade") pfUI:UpdateConfig("bars", nil, "animation", "zoomfade")
pfUI:UpdateConfig("bars", nil, "animmode", "keypress") pfUI:UpdateConfig("bars", nil, "animmode", "keypress")
pfUI:UpdateConfig("bars", nil, "animalways", "0") pfUI:UpdateConfig("bars", nil, "animalways", "0")
pfUI:UpdateConfig("bars", nil, "macroscan", "1")
pfUI:UpdateConfig("bars", nil, "reagents", "1") pfUI:UpdateConfig("bars", nil, "reagents", "1")
pfUI:UpdateConfig("bars", nil, "hunterbar", "0") pfUI:UpdateConfig("bars", nil, "hunterbar", "0")
pfUI:UpdateConfig("bars", nil, "pagemasteralt", "0") pfUI:UpdateConfig("bars", nil, "pagemasteralt", "0")
@@ -816,7 +817,6 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("chat", "text", "playerlinks", "1") pfUI:UpdateConfig("chat", "text", "playerlinks", "1")
pfUI:UpdateConfig("chat", "text", "detecturl", "1") pfUI:UpdateConfig("chat", "text", "detecturl", "1")
pfUI:UpdateConfig("chat", "text", "classcolor", "1") pfUI:UpdateConfig("chat", "text", "classcolor", "1")
pfUI:UpdateConfig("chat", "text", "playericons", "0")
pfUI:UpdateConfig("chat", "text", "whosearchunknown", "0") pfUI:UpdateConfig("chat", "text", "whosearchunknown", "0")
pfUI:UpdateConfig("chat", "text", "playerlevel", "0") pfUI:UpdateConfig("chat", "text", "playerlevel", "0")
pfUI:UpdateConfig("chat", "left", "width", "380") pfUI:UpdateConfig("chat", "left", "width", "380")
+198 -244
View File
@@ -4,17 +4,6 @@ setfenv(1, pfUI:GetEnvironment())
pfUI.uf = CreateFrame("Frame", nil, UIParent) pfUI.uf = CreateFrame("Frame", nil, UIParent)
pfUI.uf.frames = {} pfUI.uf.frames = {}
-- Reusable buffer for C_UnitAuras.GetAuraSlots slot ids (filled by
-- ScanAuraSlots, api.lua). Each aura scan in RefreshUnit fills it once, then
-- reads every aura by slot id: one array walk per scan instead of one per index.
-- Scans run back to back and consume the buffer before the next refill.
local auraSlots = {}
-- Separate buffer for the tooltip handlers. They run from OnEnter, which can
-- fire while RefreshUnit is showing/hiding icons under the cursor, so they must
-- not share the refresh buffer.
local tooltipSlots = {}
-- ============================================================================ -- ============================================================================
-- GUID-based Roster Tracking for Smart Updates -- GUID-based Roster Tracking for Smart Updates
-- Only updates frames where the unit actually changed, not ALL 40 frames -- Only updates frames where the unit actually changed, not ALL 40 frames
@@ -35,38 +24,14 @@ pfUI.api.RegisterSlashCommand("PFTEST", { "/pftest", "/pfuftest" }, function()
if pfUI.uf.raid and pfUI.uf.raid.LayoutPets then pfUI.uf.raid:LayoutPets() end if pfUI.uf.raid and pfUI.uf.raid.LayoutPets then pfUI.uf.raid:LayoutPets() end
end, true) end, true)
-- Buff indicators are identified by a spell id, resolved once into the aura's -- HoT buff indicators that need name verification because their icons are
-- localized name plus its icon. A match needs both to agree. -- reused by other spells. Maps icon (lowercased) → expected aura name +
-- -- libpredict key for the prediction integration.
-- Icon alone is ambiguous: Spell.dbc reuses icons across unrelated spells, so local HOT_INDICATORS = {
-- an icon-only filter lights the indicator for the wrong buff (Blessing of [strlower(C_Spell.GetSpellTexture(774))] = { name = strlower(C_Spell.GetSpellName(774)), predict = "Reju" },
-- Sanctuary shares its icon with Lightning Shield and Shadowguard, Blessing of [strlower(C_Spell.GetSpellTexture(139))] = { name = strlower(C_Spell.GetSpellName(139)), predict = "Renew" },
-- Kings with Mage Armor and Commanding Shout, Totemic Power with the Blessed [strlower(C_Spell.GetSpellTexture(8936))] = { name = strlower(C_Spell.GetSpellName(8936)), predict = "Regr" },
-- Sunfruit food buff). Name alone is ambiguous too -- creature and item auras }
-- reuse player spell names ("Renew", "Rejuvenation", "Fire Resistance").
--
-- Every rank of a spell carries the same name and icon, so one id per buff
-- covers the whole rank ladder. Ids missing from this client resolve to nil and
-- drop out of the list. 'predict' names the libpredict key of a HoT.
--
-- Both fields are stored raw, and RefreshUnit compares them raw. The aura's
-- name and icon and these come out of the same DBC records byte for byte --
-- Spell.dbc's localized name, SpellIcon.dbc's path -- so case folding either
-- side would only burn a string per aura per scan. Feed this ids, never
-- hand-written names or icon paths, or that equality quietly stops holding.
local indicator_cache = {}
local function AddIndicator(indicators, spellId, predict)
local record = indicator_cache[spellId]
if record == nil then
local name = C_Spell.GetSpellName(spellId)
local icon = name and C_Spell.GetSpellTexture(spellId)
-- cache misses as false, so an absent spell is only looked up once
record = icon and { name = name, icon = icon, predict = predict } or false
indicator_cache[spellId] = record
end
if record then table.insert(indicators, record) end
end
local glow = { local glow = {
edgeFile = pfUI.media["img:glow"], edgeSize = 8, edgeFile = pfUI.media["img:glow"], edgeSize = 8,
@@ -123,21 +88,16 @@ local function DebuffOnEnter()
local parent = this:GetParent() local parent = this:GetParent()
-- selfdebuff filters the displayed list to player-cast harmful auras, but -- selfdebuff filters the displayed list to player-cast harmful auras, but
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up -- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
-- the displayed aura via the PLAYER filter, then find its position in the -- up the displayed aura via the PLAYER filter, then scan engine slots for
-- unfiltered list by name + sourceGUID. -- one whose name + sourceGUID match.
--
-- The unfiltered list is NOT capped at 16: once a unit's 16 debuff slots
-- are full the server parks further debuffs in buff slots, and C_UnitAuras
-- reports those as harmful too. So enumerate however many the unit has.
if parent.config and parent.config.selfdebuff == "1" then if parent.config and parent.config.selfdebuff == "1" then
local ownAura = C_UnitAuras.GetAuraDataByIndex(unitstr, this.id, "HARMFUL|PLAYER") local ownAura = C_UnitAuras.GetAuraDataByIndex(unitstr, this.id, "HARMFUL|PLAYER")
if ownAura then if ownAura then
local n = ScanAuraSlots(unitstr, "HARMFUL", tooltipSlots) for gameSlot = 1, 16 do
for i = 1, n do local check = C_UnitAuras.GetDebuffDataByIndex(unitstr, gameSlot)
local check = C_UnitAuras.GetAuraDataBySlot(unitstr, tooltipSlots[i])
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
GameTooltip:SetUnitAura(unitstr, i, "HARMFUL") GameTooltip:SetUnitAura(unitstr, gameSlot, "HARMFUL")
return return
end end
end end
@@ -313,10 +273,9 @@ function pfUI.uf:UpdateVisibility()
self._label, self._id = nil, nil self._label, self._id = nil, nil
end end
local unitstr = ("%s%s"):format(self.label or "", self.id or "") local unitstr = string.format("%s%s", self.label or "", self.id or "")
self.unitstr = unitstr ~= "" and unitstr or nil self:SetAttribute("unit", unitstr ~= "" and unitstr or nil)
self:SetAttribute("unit", self.unitstr) local visibility = string.format("[target=%s,exists] show; hide", unitstr)
local visibility = ("[target=%s,exists] show; hide"):format(unitstr)
-- Group frames are redundant when the group is already shown as a raid grid: -- Group frames are redundant when the group is already shown as a raid grid:
-- either an actual raid, or a party promoted to the raid grid via -- either an actual raid, or a party promoted to the raid grid via
@@ -345,15 +304,6 @@ function pfUI.uf:UpdateVisibility()
self.visible = nil self.visible = nil
end end
-- This is the single place a frame's unit is ever assigned, so it is also the
-- place its subscriptions follow it to: the engine then delivers only this
-- unit's events, and OnEvent compares against the cached string instead of
-- rebuilding label..id -- and calling UnitGUID -- for every unit event fired
-- by anything, anywhere. A frame that is not in use drops them entirely; the
-- roster events that would bring it back are registered plainly, and
-- visibilityscan re-runs this every 0.2s regardless, so it recovers on its own.
self:RegisterUnitEvents(visibility ~= "hide" and self.unitstr or nil)
-- vanilla visibility -- vanilla visibility
if self.unitname then if self.unitname then
self:Show() self:Show()
@@ -634,14 +584,11 @@ function pfUI.uf:UpdateConfig()
f.feedbackText:ClearAllPoints() f.feedbackText:ClearAllPoints()
f.feedbackText:SetPoint("CENTER", f.portrait, "CENTER") f.feedbackText:SetPoint("CENTER", f.portrait, "CENTER")
end end
f.combatfeedback = true f:RegisterEvent("UNIT_COMBAT")
else else
f.feedbackText:Hide() f.feedbackText:Hide()
f.combatfeedback = nil f:UnregisterEvent("UNIT_COMBAT")
end end
-- RegisterUnitEvents owns UNIT_COMBAT; clearing the cached unit makes the
-- next UpdateVisibility re-run it against the new combatfeedback state.
f.eventunit = nil
f.hpLeftText:SetFontObject(GameFontWhite) f.hpLeftText:SetFontObject(GameFontWhite)
f.hpLeftText:SetFont(fontname, fontsize, fontstyle) f.hpLeftText:SetFont(fontname, fontsize, fontstyle)
@@ -814,7 +761,7 @@ function pfUI.uf:UpdateConfig()
if not f.buffs[i].cd then if not f.buffs[i].cd then
if cooldown_anim == 1 then if cooldown_anim == 1 then
-- Animation enabled: Use Model frame with CooldownFrameTemplate -- Animation enabled: Use Model frame with CooldownFrameTemplate
f.buffs[i].cd = CreateFrame("Model", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate") f.buffs[i].cd = CreateFrame(COOLDOWN_FRAME_TYPE, f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate")
else else
-- Animation disabled: Use regular Frame with dummy functions -- Animation disabled: Use regular Frame with dummy functions
f.buffs[i].cd = CreateFrame("Frame", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i]) f.buffs[i].cd = CreateFrame("Frame", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i])
@@ -887,7 +834,7 @@ function pfUI.uf:UpdateConfig()
if not f.debuffs[i].cd then if not f.debuffs[i].cd then
if cooldown_anim == 1 then if cooldown_anim == 1 then
-- Animation enabled: Use Model frame with CooldownFrameTemplate -- Animation enabled: Use Model frame with CooldownFrameTemplate
f.debuffs[i].cd = CreateFrame("Model", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate") f.debuffs[i].cd = CreateFrame(COOLDOWN_FRAME_TYPE, f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate")
else else
-- Animation disabled: Use regular Frame with dummy functions -- Animation disabled: Use regular Frame with dummy functions
f.debuffs[i].cd = CreateFrame("Frame", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i]) f.debuffs[i].cd = CreateFrame("Frame", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i])
@@ -931,9 +878,6 @@ function pfUI.uf:UpdateConfig()
f:UpdateFrameSize() f:UpdateFrameSize()
else else
f:UnregisterAllEvents() f:UnregisterAllEvents()
-- that dropped the unit filters along with the registrations, so the cache
-- has to go too or the next UpdateVisibility believes they are still set
f.eventunit = nil
f:Hide() f:Hide()
end end
end end
@@ -975,10 +919,6 @@ function pfUI.uf.OnEvent()
this:UnregisterAllEvents() this:UnregisterAllEvents()
this:SetScript("OnEvent", nil) this:SetScript("OnEvent", nil)
this:SetScript("OnUpdate", nil) this:SetScript("OnUpdate", nil)
-- visibilityscan is a separate frame and keeps ticking, so leaving this one
-- on its list would have UpdateVisibility re-register the unit events we
-- just dropped -- straight back into the crash 132 this branch prevents
visibilityscan.frames[this] = nil
return return
end end
@@ -1045,12 +985,8 @@ function pfUI.uf.OnEvent()
this.update_aura = true this.update_aura = true
elseif this.label == "pet" and event == "UNIT_HAPPINESS" then elseif this.label == "pet" and event == "UNIT_HAPPINESS" then
this.update_full = true this.update_full = true
-- UNIT_XXX Events. RegisterUnitEvents means arg1 can only be this frame's own -- UNIT_XXX Events
-- unit; the compare is kept for the case the filter sits out, which is when elseif arg1 and (arg1 == this.label .. this.id or (UnitGUID and arg1 == UnitGUID(this.label .. this.id))) then
-- arg1 is not a string. The old GUID alternative is gone: these events fire
-- once per token that resolves to the unit AND once with the raw GUID, so the
-- token form always arrives and the GUID form was only ever a duplicate wake.
elseif arg1 and arg1 == this.unitstr then
if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then
this.update_portrait = true this.update_portrait = true
elseif event == "UNIT_AURA" then elseif event == "UNIT_AURA" then
@@ -1275,56 +1211,25 @@ function pfUI.uf.OnUpdate()
end end
end end
-- The unit events whose arg1 is the frame's OWN unit -- everything the OnEvent
-- routes through its "UNIT_XXX Events" branch. These are not registered here:
-- UpdateVisibility owns them, because it owns the frame's unit (below).
--
-- UNIT_PET and UNIT_HAPPINESS are deliberately absent. Their branches key on
-- the frame's label, and UNIT_PET's arg1 is the pet's OWNER ("player" for a
-- "pet" frame), so filtering them by the frame's own unit would drop them.
local UNIT_EVENTS = {
"UNIT_DISPLAYPOWER",
"UNIT_HEALTH", "UNIT_MAXHEALTH",
"UNIT_MANA", "UNIT_MAXMANA",
"UNIT_RAGE", "UNIT_MAXRAGE",
"UNIT_ENERGY", "UNIT_MAXENERGY",
"UNIT_FOCUS",
"UNIT_PORTRAIT_UPDATE", "UNIT_MODEL_CHANGED",
"UNIT_FACTION",
"UNIT_AURA", -- frame=buff, frame=debuff
}
-- Point this frame's unit-event subscriptions at `unitstr`, or drop them when
-- the frame has no unit. Cheap to call repeatedly: it no-ops unless the unit
-- actually changed, which matters because visibilityscan runs UpdateVisibility
-- for every frame five times a second.
function pfUI.uf:RegisterUnitEvents(unitstr)
if self.eventunit == unitstr then return end
self.eventunit = unitstr
for i = 1, table.getn(UNIT_EVENTS) do
if unitstr then
self:RegisterUnitEvent(UNIT_EVENTS[i], unitstr)
else
self:UnregisterEvent(UNIT_EVENTS[i])
end
end
-- UNIT_COMBAT rides along only while the frame draws combat feedback text.
-- UpdateConfig clears eventunit when it toggles that, so the next
-- UpdateVisibility re-runs this.
if unitstr and self.combatfeedback then
self:RegisterUnitEvent("UNIT_COMBAT", unitstr)
else
self:UnregisterEvent("UNIT_COMBAT")
end
end
function pfUI.uf:EnableEvents() function pfUI.uf:EnableEvents()
local f = self local f = self
f:RegisterEvent("PLAYER_ENTERING_WORLD") f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("PLAYER_LOGOUT") f:RegisterEvent("PLAYER_LOGOUT")
f:RegisterEvent("UNIT_DISPLAYPOWER")
f:RegisterEvent("UNIT_HEALTH")
f:RegisterEvent("UNIT_MAXHEALTH")
f:RegisterEvent("UNIT_MANA")
f:RegisterEvent("UNIT_MAXMANA")
f:RegisterEvent("UNIT_RAGE")
f:RegisterEvent("UNIT_MAXRAGE")
f:RegisterEvent("UNIT_ENERGY")
f:RegisterEvent("UNIT_MAXENERGY")
f:RegisterEvent("UNIT_FOCUS")
f:RegisterEvent("UNIT_PORTRAIT_UPDATE")
f:RegisterEvent("UNIT_MODEL_CHANGED")
f:RegisterEvent("UNIT_FACTION")
f:RegisterEvent("UNIT_AURA") -- frame=buff, frame=debuff
f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff
f:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs) f:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs)
f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon
@@ -1442,7 +1347,6 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f.UpdateConfig = pfUI.uf.UpdateConfig f.UpdateConfig = pfUI.uf.UpdateConfig
f.EnableScripts = pfUI.uf.EnableScripts f.EnableScripts = pfUI.uf.EnableScripts
f.EnableEvents = pfUI.uf.EnableEvents f.EnableEvents = pfUI.uf.EnableEvents
f.RegisterUnitEvents = pfUI.uf.RegisterUnitEvents
f.EnableClickCast = pfUI.uf.EnableClickCast f.EnableClickCast = pfUI.uf.EnableClickCast
f.GetColor = pfUI.uf.GetColor f.GetColor = pfUI.uf.GetColor
@@ -1559,9 +1463,6 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f:UpdateFrameSize() f:UpdateFrameSize()
else else
f:UnregisterAllEvents() f:UnregisterAllEvents()
-- that dropped the unit filters along with the registrations, so the cache
-- has to go too or the next UpdateVisibility believes they are still set
f.eventunit = nil
f:Hide() f:Hide()
end end
@@ -1734,13 +1635,11 @@ function pfUI.uf:RefreshUnit(unit, component)
-- buffs -- buffs
if unit.buffs and ( component == "all" or component == "aura" ) then if unit.buffs and ( component == "all" or component == "aura" ) then
-- one GetAuraSlots enumeration per refresh, then a positional read per
-- slot id: allocates nothing and never re-walks the aura array per icon
ScanAuraSlots(unitstr, "HELPFUL", auraSlots, unit.config.bufflimit)
for i=1, unit.config.bufflimit do for i=1, unit.config.bufflimit do
if not unit.buffs[i] then break end if not unit.buffs[i] then break end
local name, icon, count, _, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i]) -- positional UnitBuff allocates nothing (vs a table per icon per refresh)
local name, icon, count, _, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitBuff(unitstr, i)
if name then if name then
unit.buffs[i].texture:SetTexture(icon) unit.buffs[i].texture:SetTexture(icon)
@@ -1824,13 +1723,6 @@ function pfUI.uf:RefreshUnit(unit, component)
reposition = true reposition = true
end end
-- selfdebuff narrows to player-cast harmful auras via the PLAYER filter.
-- Player-frame debuffs aren't gated on it (it'd hide most party-applied
-- effects on you). One GetAuraSlots enumeration per refresh; the i-th slot
-- is the i-th aura of the filtered list, so `i` stays the tooltip index.
local filter = (unit.label ~= "player" and selfdebuff == "1") and "HARMFUL|PLAYER" or "HARMFUL"
ScanAuraSlots(unitstr, filter, auraSlots, unit.config.debufflimit)
for i=1, unit.config.debufflimit do for i=1, unit.config.debufflimit do
if not unit.debuffs[i] then break end if not unit.debuffs[i] then break end
@@ -1847,8 +1739,12 @@ function pfUI.uf:RefreshUnit(unit, component)
invert_h * ((row+buffrow)*(multiply*default_border + unit.config.debuffsize + 1) + (multiply*default_border + 1))) invert_h * ((row+buffrow)*(multiply*default_border + unit.config.debuffsize + 1) + (multiply*default_border + 1)))
end end
-- positional read by slot id allocates nothing -- selfdebuff narrows to player-cast harmful auras via the PLAYER filter.
local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i]) -- Player-frame debuffs aren't gated on it (it'd hide most party-applied
-- effects on you).
-- positional UnitDebuff allocates nothing; PLAYER predicate honored for selfdebuff
local filter = (unit.label ~= "player" and selfdebuff == "1") and "PLAYER" or nil
local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitDebuff(unitstr, i, filter)
if name then if name then
texture, stacks, dtype = icon, count, dispelType texture, stacks, dtype = icon, count, dispelType
else else
@@ -1938,9 +1834,8 @@ function pfUI.uf:RefreshUnit(unit, component)
local present = pfUI.uf.dispelPresent or {} local present = pfUI.uf.dispelPresent or {}
pfUI.uf.dispelPresent = present pfUI.uf.dispelPresent = present
for k in pairs(present) do present[k] = nil end for k in pairs(present) do present[k] = nil end
local n = ScanAuraSlots(unitstr, "HARMFUL", auraSlots) for i=1,16 do
for i=1,n do local name, _, _, dispelType = C_UnitAuras.UnitDebuff(unitstr, i)
local name, _, _, dispelType = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
if name and dispelType and dispelType ~= "" then present[dispelType] = true end if name and dispelType and dispelType ~= "" then present[dispelType] = true end
end end
@@ -2022,7 +1917,7 @@ function pfUI.uf:RefreshUnit(unit, component)
if not unit.indicator_custom and unit.config.buff_indicator == "1" then if not unit.indicator_custom and unit.config.buff_indicator == "1" then
unit.indicator_custom = {} unit.indicator_custom = {}
for k, v in pairs({strsplit("#", unit.config.custom_indicator)}) do for k, v in pairs({strsplit("#", unit.config.custom_indicator)}) do
unit.indicator_custom[k] = v:lower() unit.indicator_custom[k] = string.lower(v)
end end
elseif not unit.indicator_custom then elseif not unit.indicator_custom then
unit.indicator_custom = {} unit.indicator_custom = {}
@@ -2030,16 +1925,21 @@ function pfUI.uf:RefreshUnit(unit, component)
local pos = 1 local pos = 1
if table.getn(unit.indicators) > 0 then if table.getn(unit.indicators) > 0 then
local n = ScanAuraSlots(unitstr, "HELPFUL", auraSlots) local i = 1
for i=1,n do while true do
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i]) local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitBuff(unitstr, i)
if not name then break end if not name then break end
local texLower = string.lower(icon)
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
for _, filter in pairs(unit.indicators) do for _, filter in pairs(unit.indicators) do
if filter.icon == icon and filter.name == name then if filter == texLower then
if filter.predict then local hot = HOT_INDICATORS[texLower]
local start, duration, prediction = libpredict:GetHotDuration(unitstr, filter.predict) if hot and string.lower(name) ~= hot.name then
break -- texture matches but name disambiguates (e.g. shared icon)
end
if hot then
local start, duration, prediction = libpredict:GetHotDuration(unitstr, hot.predict)
pfUI.uf:AddIcon(unit, pos, icon, timeleft or prediction, count, tonumber(start), tonumber(duration)) pfUI.uf:AddIcon(unit, pos, icon, timeleft or prediction, count, tonumber(start), tonumber(duration))
else else
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count) pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
@@ -2048,16 +1948,17 @@ function pfUI.uf:RefreshUnit(unit, component)
break break
end end
end end
i = i + 1
end end
end end
if table.getn(unit.indicator_custom) > 0 then if table.getn(unit.indicator_custom) > 0 then
local n = ScanAuraSlots(unitstr, "HELPFUL", auraSlots) local ai = 1
for i=1,n do while true do
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i]) local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitBuff(unitstr, ai)
if not name then break end if not name then break end
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
local lowerName = name:lower() local lowerName = string.lower(name)
for _, filter in pairs(unit.indicator_custom) do for _, filter in pairs(unit.indicator_custom) do
if filter == lowerName then if filter == lowerName then
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count) pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
@@ -2065,16 +1966,16 @@ function pfUI.uf:RefreshUnit(unit, component)
break break
end end
end end
ai = ai + 1
end end
local debuffFilter = unit.config.selfdebuff == "1" and "HARMFUL|PLAYER" or "HARMFUL" local debuffFilter = unit.config.selfdebuff == "1" and "PLAYER" or nil
n = ScanAuraSlots(unitstr, debuffFilter, auraSlots) for i=1,16 do -- scan for custom debuffs
for i=1,n do -- scan for custom debuffs local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitDebuff(unitstr, i, debuffFilter)
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
if name then if name then
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
for _, filter in pairs(unit.indicator_custom) do for _, filter in pairs(unit.indicator_custom) do
if filter == name:lower() then if filter == string.lower(name) then
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count) pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
pos = pos + 1 pos = pos + 1
break break
@@ -2283,17 +2184,17 @@ function pfUI.uf:EnableClickCast()
local action = pfUI_config.unitframes["clickcast"..bconf..mconf] local action = pfUI_config.unitframes["clickcast"..bconf..mconf]
if action and action ~= "" then if action and action ~= "" then
local prefix = modifier ~= "" and (modifier .. "-") or "" local prefix = modifier ~= "" and (modifier .. "-") or ""
local low = action:lower() local low = string.lower(action)
if low == "menu" then if low == "menu" then
self:SetAttribute(prefix .. "type" .. bid, "menu") self:SetAttribute(prefix .. "type" .. bid, "menu")
elseif low == "target" then elseif low == "target" then
self:SetAttribute(prefix .. "type" .. bid, "target") self:SetAttribute(prefix .. "type" .. bid, "target")
elseif low == "focus" then elseif low == "focus" then
self:SetAttribute(prefix .. "type" .. bid, "focus") self:SetAttribute(prefix .. "type" .. bid, "focus")
elseif low:find("^macro:") then elseif string.find(low, "^macro:") then
self:SetAttribute(prefix .. "type" .. bid, "macro") self:SetAttribute(prefix .. "type" .. bid, "macro")
self:SetAttribute(prefix .. "macro" .. bid, action:sub(7):gsub("^%s+", "")) self:SetAttribute(prefix .. "macro" .. bid, string.gsub(string.sub(action, 7), "^%s+", ""))
elseif action:find("^/") then elseif string.find(action, "^/") then
self:SetAttribute(prefix .. "type" .. bid, "macro") self:SetAttribute(prefix .. "type" .. bid, "macro")
self:SetAttribute(prefix .. "macrotext" .. bid, action) self:SetAttribute(prefix .. "macrotext" .. bid, action)
else else
@@ -2332,7 +2233,7 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
-- Check if parent frame has cooldown animation enabled -- Check if parent frame has cooldown animation enabled
local parent_cooldown_anim = frame.config and tonumber(frame.config.cooldown_anim) or 1 local parent_cooldown_anim = frame.config and tonumber(frame.config.cooldown_anim) or 1
if parent_cooldown_anim == 1 then if parent_cooldown_anim == 1 then
frame.icon[pos].cd = CreateFrame("Model", nil, frame.icon[pos]) frame.icon[pos].cd = CreateFrame(COOLDOWN_FRAME_TYPE, nil, frame.icon[pos])
else else
frame.icon[pos].cd = CreateFrame("Frame", nil, frame.icon[pos]) frame.icon[pos].cd = CreateFrame("Frame", nil, frame.icon[pos])
frame.icon[pos].cd.AdvanceTime = DoNothing frame.icon[pos].cd.AdvanceTime = DoNothing
@@ -2431,122 +2332,175 @@ function pfUI.uf:SetupBuffIndicators(config)
if config.show_buffs == "1" then -- buffs if config.show_buffs == "1" then -- buffs
if myclass == "DRUID" then if myclass == "DRUID" then
AddIndicator(indicators, 1126) -- Mark of the Wild -- Mark of the Wild
AddIndicator(indicators, 21849) -- Gift of the Wild table.insert(indicators, "interface\\icons\\spell_nature_regeneration")
AddIndicator(indicators, 467) -- Thorns -- Gift of the Wild
table.insert(indicators, "interface\\icons\\spell_nature_giftofthewild")
-- Thorns
table.insert(indicators, "interface\\icons\\spell_nature_thorns")
end end
if myclass == "PRIEST" then if myclass == "PRIEST" then
AddIndicator(indicators, 1243) -- Power Word: Fortitude -- Prayer Of Fortitude"
AddIndicator(indicators, 21562) -- Prayer of Fortitude table.insert(indicators, "interface\\icons\\spell_holy_wordfortitude")
AddIndicator(indicators, 6386) -- Divine Spirit table.insert(indicators, "interface\\icons\\spell_holy_prayeroffortitude")
AddIndicator(indicators, 27681) -- Prayer of Spirit -- Prayer of Spirit
AddIndicator(indicators, 976) -- Shadow Protection table.insert(indicators, "interface\\icons\\spell_holy_divinespirit")
AddIndicator(indicators, 27683) -- Prayer of Shadow Protection table.insert(indicators, "interface\\icons\\spell_holy_prayerofspirit")
AddIndicator(indicators, 6346) -- Fear Ward -- Shadow Protection
table.insert(indicators, "interface\\icons\\spell_shadow_antishadow")
table.insert(indicators, "interface\\icons\\spell_holy_prayerofshadowprotection")
-- Fear Ward
table.insert(indicators, "interface\\icons\\spell_holy_excorcism")
end end
if myclass == "PALADIN" then if myclass == "PALADIN" then
AddIndicator(indicators, 1038) -- Blessing of Salvation -- Blessing of Salvation
AddIndicator(indicators, 25895) -- Greater Blessing of Salvation table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofsalvation")
AddIndicator(indicators, 19742) -- Blessing of Wisdom table.insert(indicators, "interface\\icons\\spell_holy_sealofsalvation")
AddIndicator(indicators, 25894) -- Greater Blessing of Wisdom -- Blessing of Wisdom
AddIndicator(indicators, 20204) -- Blessing of Sanctuary table.insert(indicators, "interface\\icons\\spell_holy_sealofwisdom")
AddIndicator(indicators, 25899) -- Greater Blessing of Sanctuary table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofwisdom")
AddIndicator(indicators, 20217) -- Blessing of Kings -- Blessing of Sanctuary
AddIndicator(indicators, 25898) -- Greater Blessing of Kings table.insert(indicators, "interface\\icons\\spell_nature_lightningshield")
AddIndicator(indicators, 19740) -- Blessing of Might table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofsanctuary")
AddIndicator(indicators, 25782) -- Greater Blessing of Might -- Blessing of Kings
AddIndicator(indicators, 19977) -- Blessing of Light table.insert(indicators, "interface\\icons\\spell_magic_magearmor")
AddIndicator(indicators, 25890) -- Greater Blessing of Light table.insert(indicators, "interface\\icons\\spell_magic_greaterblessingofkings")
AddIndicator(indicators, 6940) -- Hand of Sacrifice -- Blessing of Might
AddIndicator(indicators, 45801) -- Greater Blessing of Sacrifice table.insert(indicators, "interface\\icons\\spell_holy_fistofjustice")
AddIndicator(indicators, 1044) -- Hand of Freedom table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofkings")
AddIndicator(indicators, 1022) -- Hand of Protection -- Blessing of Light
table.insert(indicators, "interface\\icons\\spell_holy_prayerofhealing02")
table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingoflight")
-- Blessing of Sacrifice
table.insert(indicators, "interface\\icons\\spell_holy_sealofsacrifice")
-- Blessing of Freedom
table.insert(indicators, "interface\\icons\\spell_holy_sealofvalor")
-- Blessing of Protection
table.insert(indicators, "interface\\icons\\spell_holy_sealofprotection")
end end
if myclass == "WARLOCK" then if myclass == "WARLOCK" then
AddIndicator(indicators, 1167) -- Fire Shield -- Fire Shield
AddIndicator(indicators, 6307) -- Blood Pact table.insert(indicators, "interface\\icons\\spell_fire_firearmor")
AddIndicator(indicators, 20707) -- Soulstone Resurrection -- Blood Pact
AddIndicator(indicators, 5697) -- Unending Breath table.insert(indicators, "interface\\icons\\spell_shadow_bloodboil")
AddIndicator(indicators, 2970) -- Detect Invisibility -- Soulstone
AddIndicator(indicators, 11743) -- Detect Greater Invisibility table.insert(indicators, "interface\\icons\\spell_shadow_soulgem")
AddIndicator(indicators, 132) -- Detect Lesser Invisibility -- Unending Breath
AddIndicator(indicators, 19480) -- Paranoia table.insert(indicators, "interface\\icons\\spell_shadow_demonbreath")
-- Detect Greater Invisibility or Detect Invisibility
table.insert(indicators, "interface\\icons\\spell_shadow_detectinvisibility")
-- Detect Lesser Invisibility
table.insert(indicators, "interface\\icons\\spell_shadow_detectlesserinvisibility")
-- Paranoia
table.insert(indicators, "interface\\icons\\Spell_Shadow_AuraOfDarkness")
end end
if myclass == "WARRIOR" then if myclass == "WARRIOR" then
AddIndicator(indicators, 5242) -- Battle Shout -- Battle Shout
AddIndicator(indicators, 45580) -- Commanding Shout table.insert(indicators, "interface\\icons\\ability_warrior_battleshout")
-- Commanding Shout (TBC)
table.insert(indicators, "interface\\icons\\ability_warrior_rallyingcry")
end end
if myclass == "MAGE" then if myclass == "MAGE" then
AddIndicator(indicators, 1459) -- Arcane Intellect -- Arcane Intellect
AddIndicator(indicators, 23028) -- Arcane Brilliance table.insert(indicators, "interface\\icons\\spell_holy_magicalsentry")
AddIndicator(indicators, 604) -- Dampen Magic table.insert(indicators, "interface\\icons\\spell_holy_arcaneintellect")
AddIndicator(indicators, 1008) -- Amplify Magic -- Dampen Magic
table.insert(indicators, "interface\\icons\\spell_nature_abolishmagic")
-- Amplify Magic
table.insert(indicators, "interface\\icons\\spell_holy_flashheal")
end end
if myclass == "HUNTER" then if myclass == "HUNTER" then
AddIndicator(indicators, 20043) -- Aspect of the Wild -- Aspect of the Wild
AddIndicator(indicators, 13159) -- Aspect of the Pack table.insert(indicators, "interface\\icons\\spell_nature_protectionformnature")
-- Aspect of the Pack
table.insert(indicators, "interface\\icons\\ability_mount_whitetiger")
-- Misdirection (TBC)
table.insert(indicators, "interface\\icons\\ability_hunter_misdirection")
end end
if myclass == "SHAMAN" then if myclass == "SHAMAN" then
AddIndicator(indicators, 45525) -- Earth Shield -- Earth Shield (TBC)
table.insert(indicators, "interface\\icons\\spell_nature_skinofearth")
end end
end end
if config.show_procs == "1" then -- procs if config.show_procs == "1" then -- procs
if myclass == "SHAMAN" or config.all_procs == "1" then if myclass == "SHAMAN" or config.all_procs == "1" then
AddIndicator(indicators, 16177) -- Ancestral Fortitude -- Ancestral Fortitude
AddIndicator(indicators, 29202) -- Healing Way table.insert(indicators, "interface\\icons\\spell_nature_undyingstrength")
-- Totemic Power is four auras, one per totem school, each with its own icon -- Healing Way
AddIndicator(indicators, 28824) table.insert(indicators, "interface\\icons\\spell_nature_healingway")
AddIndicator(indicators, 28825) -- Totemic Power (known issue: one conflicts with Blessed Sunfruit buff)
AddIndicator(indicators, 28826) table.insert(indicators, "interface\\icons\\spell_holy_spiritualguidence")
AddIndicator(indicators, 28827) table.insert(indicators, "interface\\icons\\spell_holy_devotion")
table.insert(indicators, "interface\\icons\\spell_holy_holynova")
table.insert(indicators, "interface\\icons\\spell_magic_magearmor")
end end
if myclass == "PRIEST" or config.all_procs == "1" then if myclass == "PRIEST" or config.all_procs == "1" then
AddIndicator(indicators, 14893) -- Inspiration -- Inspiration
table.insert(indicators, "interface\\icons\\inv_shield_06")
end end
end end
if config.show_hots == "1" then -- hots if config.show_hots == "1" then -- hots
if myclass == "PRIEST" or config.all_hots == "1" then if myclass == "PRIEST" or config.all_hots == "1" then
AddIndicator(indicators, 139, "Renew") -- Renew -- Renew
AddIndicator(indicators, 17) -- Power Word: Shield table.insert(indicators, "interface\\icons\\spell_holy_renew")
-- Power Word: Shield
table.insert(indicators, "interface\\icons\\spell_holy_powerwordshield")
-- Prayer of Mending (TBC)
table.insert(indicators, "interface\\icons\\spell_holy_prayerofmendingtga")
end end
if myclass == "DRUID" or config.all_hots == "1" then if myclass == "DRUID" or config.all_hots == "1" then
AddIndicator(indicators, 8936, "Regr") -- Regrowth -- Regrowth
AddIndicator(indicators, 774, "Reju") -- Rejuvenation table.insert(indicators, "interface\\icons\\spell_nature_resistnature")
-- Rejuvenation
table.insert(indicators, "interface\\icons\\spell_nature_rejuvenation")
-- Lifebloom
table.insert(indicators, "interface\\icons\\inv_misc_herb_felblossom")
end end
end end
if config.show_totems == "1" and myclass == "SHAMAN" then -- totems if config.show_totems == "1" and myclass == "SHAMAN" then -- totems
-- the aura each totem applies, not the cast that drops it: they share an -- Strength of Earth Totem
-- icon but the totem's own name carries a " Totem" suffix the aura lacks table.insert(indicators, "interface\\icons\\spell_nature_earthbindtotem")
AddIndicator(indicators, 8076) -- Strength of Earth -- Stoneskin Totem
AddIndicator(indicators, 8072) -- Stoneskin table.insert(indicators, "interface\\icons\\spell_nature_stoneskintotem")
AddIndicator(indicators, 5677) -- Mana Spring -- Mana Spring Totem
AddIndicator(indicators, 16191) -- Mana Tide table.insert(indicators, "interface\\icons\\spell_nature_manaregentotem")
AddIndicator(indicators, 5672) -- Healing Stream -- Mana Tide Totem
AddIndicator(indicators, 25909) -- Tranquil Air table.insert(indicators, "interface\\icons\\spell_frost_summonwaterelemental")
AddIndicator(indicators, 8836) -- Grace of Air -- Healing Spring Totem
AddIndicator(indicators, 8177) -- Grounding Totem table.insert(indicators, "interface\\icons\\inv_spear_04")
AddIndicator(indicators, 10596) -- Nature Resistance -- Tranquil Air Totem
AddIndicator(indicators, 8185) -- Fire Resistance table.insert(indicators, "interface\\icons\\spell_nature_brilliance")
AddIndicator(indicators, 8182) -- Frost Resistance -- Grace of Air Totem
table.insert(indicators, "interface\\icons\\spell_nature_invisibilitytotem")
-- Grounding Totem
table.insert(indicators, "interface\\icons\\spell_nature_groundingtotem")
-- Nature Resistance Totem
table.insert(indicators, "interface\\icons\\spell_nature_natureresistancetotem")
-- Fire Resistance Totem
table.insert(indicators, "interface\\icons\\spell_fireresistancetotem_01")
-- Frost Resistance Totem
table.insert(indicators, "interface\\icons\\spell_frostresistancetotem_01")
end end
return indicators return indicators
end end
local function abbrevname(t) local function abbrevname(t)
return t:sub(1,1)..". " return string.sub(t,1,1)..". "
end end
function pfUI.uf:GetNameString(unitstr) function pfUI.uf:GetNameString(unitstr)
@@ -2556,12 +2510,12 @@ function pfUI.uf:GetNameString(unitstr)
-- first try to only abbreviate the first word -- first try to only abbreviate the first word
if abbrev and name and strlen(name) > size then if abbrev and name and strlen(name) > size then
name = name:gsub("^(%S+) ", abbrevname) name = string.gsub(name, "^(%S+) ", abbrevname)
end end
-- abbreviate all if it still doesn't fit -- abbreviate all if it still doesn't fit
if abbrev and name and strlen(name) > size then if abbrev and name and strlen(name) > size then
name = name:gsub("(%S+) ", abbrevname) name = string.gsub(name, "(%S+) ", abbrevname)
end end
return name return name
+43
View File
@@ -0,0 +1,43 @@
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
-- [[ Constants ]]--
EVENTS_MINIMAP_ZONE_UPDATE = {"PLAYER_ENTERING_WORLD", "MINIMAP_ZONE_CHANGED"}
MICRO_BUTTONS = {
'CharacterMicroButton', 'SpellbookMicroButton', 'TalentMicroButton',
'QuestLogMicroButton', 'SocialsMicroButton', 'WorldMapMicroButton',
'MainMenuMicroButton', 'HelpMicroButton',
}
NAMEPLATE_OBJECTORDER = { "border", "glow", "name", "level", "levelicon", "raidicon" }
NAMEPLATE_FRAMETYPE = "Button"
MINIMAP_TRACKING_FRAME = _G.MiniMapTrackingFrame
FRIENDS_NAME_LOCATION = "ButtonTextNameLocation"
COOLDOWN_FRAME_TYPE = "Model"
LOOT_BUTTON_FRAME_TYPE = "LootButton"
PLAYER_BUFF_START_ID = -1
ACTIONBAR_SECURE_TEMPLATE_BAR = nil
ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil
--[[ Vanilla API Extensions ]]--
do -- RunMacroText
local obj = { ["GetText"] = function(self) return self.text end }
obj = setmetatable(obj, {__index = function(tab,key)
local value = function() return end
rawset(tab,key,value)
return value
end})
function RunMacroText(text)
obj.text = text
ChatEdit_ParseText(obj, 1)
end
end
+2358
View File
File diff suppressed because it is too large Load Diff
-2377
View File
File diff suppressed because it is too large Load Diff
-2991
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -670,6 +670,7 @@ pfUI_translation["deDE"] = {
["Scale"] = nil, ["Scale"] = nil,
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = nil, ["Screen Resolution"] = nil,
["Screenshot"] = nil, ["Screenshot"] = nil,
+1
View File
@@ -683,6 +683,7 @@ pfUI_translation["enUS"] = {
["Scale"] = nil, ["Scale"] = nil,
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = nil, ["Screen Resolution"] = nil,
["Screenshot"] = nil, ["Screenshot"] = nil,
+1
View File
@@ -670,6 +670,7 @@ pfUI_translation["esES"] = {
["Scale"] = "Escala", ["Scale"] = "Escala",
["Scale Border On HiDPI Displays"] = "Escalar los bordes en las pantallas con DPI alto", ["Scale Border On HiDPI Displays"] = "Escalar los bordes en las pantallas con DPI alto",
["Scaling"] = "Escalada", ["Scaling"] = "Escalada",
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = "Intensidad de brillo en los bordes de la pantalla", ["Screen Edge Glow Intensity"] = "Intensidad de brillo en los bordes de la pantalla",
["Screen Resolution"] = "Resolución de pantalla", ["Screen Resolution"] = "Resolución de pantalla",
["Screenshot"] = "Captura de pantalla", ["Screenshot"] = "Captura de pantalla",
+1
View File
@@ -670,6 +670,7 @@ pfUI_translation["frFR"] = {
["Scale"] = "Échelle", ["Scale"] = "Échelle",
["Scale Border On HiDPI Displays"] = "Échelle de bordure sur les écrans HiDPI", ["Scale Border On HiDPI Displays"] = "Échelle de bordure sur les écrans HiDPI",
["Scaling"] = "Mise à l'échelle", ["Scaling"] = "Mise à l'échelle",
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = "Résolution d'écran", ["Screen Resolution"] = "Résolution d'écran",
["Screenshot"] = "Imprime écran", ["Screenshot"] = "Imprime écran",
+1
View File
@@ -670,6 +670,7 @@ pfUI_translation["koKR"] = {
["Scale"] = nil, ["Scale"] = nil,
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = "화면 해상도", ["Screen Resolution"] = "화면 해상도",
["Screenshot"] = nil, ["Screenshot"] = nil,
+1
View File
@@ -670,6 +670,7 @@ pfUI_translation["ruRU"] = {
["Scale"] = "Масштаб", ["Scale"] = "Масштаб",
["Scale Border On HiDPI Displays"] = "Масштабировать границы на HiDPI мониторах", ["Scale Border On HiDPI Displays"] = "Масштабировать границы на HiDPI мониторах",
["Scaling"] = "Масштаб интерфейса", ["Scaling"] = "Масштаб интерфейса",
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = "Интенсивность свечения на краях экрана", ["Screen Edge Glow Intensity"] = "Интенсивность свечения на краях экрана",
["Screen Resolution"] = "Разрешение экрана", ["Screen Resolution"] = "Разрешение экрана",
["Screenshot"] = "Снимок экрана", ["Screenshot"] = "Снимок экрана",
+1
View File
@@ -670,6 +670,7 @@ pfUI_translation["zhCN"] = {
["Scale"] = "比例", ["Scale"] = "比例",
["Scale Border On HiDPI Displays"] = "缩放高DPI显示器上的边框", ["Scale Border On HiDPI Displays"] = "缩放高DPI显示器上的边框",
["Scaling"] = "UI缩放", ["Scaling"] = "UI缩放",
["Scan Macros For Spells"] = "扫描宏命令中的法术",
["Screen Edge Glow Intensity"] = "屏幕边缘发光强度", ["Screen Edge Glow Intensity"] = "屏幕边缘发光强度",
["Screen Resolution"] = "屏幕分辨率", ["Screen Resolution"] = "屏幕分辨率",
["Screenshot"] = "屏幕截图", ["Screenshot"] = "屏幕截图",
+1
View File
@@ -670,6 +670,7 @@ pfUI_translation["zhTW"] = {
["Scale"] = "比例", ["Scale"] = "比例",
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = "螢幕解析度", ["Screen Resolution"] = "螢幕解析度",
["Screenshot"] = nil, ["Screenshot"] = nil,
+1 -1
View File
@@ -1,3 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/"> <Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\env\tables_stock.lua"/> <Include file="..\compat\vanilla.lua"/>
</Ui> </Ui>
+2
View File
@@ -70,7 +70,9 @@
<Include file="..\modules\addoncompat.lua"/> <Include file="..\modules\addoncompat.lua"/>
<Include file="..\modules\energytick.lua"/> <Include file="..\modules\energytick.lua"/>
<Include file="..\modules\totems.lua"/> <Include file="..\modules\totems.lua"/>
<Include file="..\modules\macrotweak.lua"/>
<Include file="..\modules\macroicons.lua"/> <Include file="..\modules\macroicons.lua"/>
<Include file="..\modules\turtle-wow.lua"/>
<Include file="..\modules\superwow.lua"/> <Include file="..\modules\superwow.lua"/>
<Include file="..\modules\innervatecall.lua"/> <Include file="..\modules\innervatecall.lua"/>
<Include file="..\modules\nampower.lua"/> <Include file="..\modules\nampower.lua"/>
+7 -1
View File
@@ -35,6 +35,12 @@
<Include file="..\skins\blizzard\tooltips.lua"/> <Include file="..\skins\blizzard\tooltips.lua"/>
<Include file="..\skins\blizzard\tabard.lua"/> <Include file="..\skins\blizzard\tabard.lua"/>
<Include file="..\skins\blizzard\itemtext.lua"/> <Include file="..\skins\blizzard\itemtext.lua"/>
<Include file="..\skins\blizzard\lft.lua"/>
<Include file="..\skins\blizzard\turtle_shop.lua"/>
<!-- Turtle WoW -->
<Include file="..\skins\blizzard\barbershop.lua"/>
<Include file="..\skins\blizzard\transmog.lua"/>
<Include file="..\skins\blizzard\ebc.lua"/>
<!-- Turtle WoW skins live in init\turtle.xml (pfUI_Turtle.toc only). -->
</Ui> </Ui>
-14
View File
@@ -1,14 +0,0 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<!-- Turtle WoW only: pulled in by pfUI_Turtle.toc, which ClassicAPI selects
when it detects a Turtle client. Everything in here targets frames that
only a Turtle client ever creates. -->
<Include file="..\env\tables_turtle.lua"/>
<Include file="..\skins\blizzard\lft.lua"/>
<Include file="..\skins\blizzard\turtle_shop.lua"/>
<Include file="..\skins\blizzard\barbershop.lua"/>
<Include file="..\skins\blizzard\transmog.lua"/>
<Include file="..\skins\blizzard\ebc.lua"/>
<Include file="..\modules\turtle-wow.lua"/>
</Ui>
+2 -2
View File
@@ -10,8 +10,8 @@ local libhealth = CreateFrame("Frame")
libhealth.enabled = true libhealth.enabled = true
libhealth.reqhit = 4 libhealth.reqhit = 4
libhealth.reqdmg = 5 libhealth.reqdmg = 5
libhealth:RegisterUnitEvent("UNIT_HEALTH", "target") libhealth:RegisterEvent("UNIT_HEALTH")
libhealth:RegisterUnitEvent("UNIT_COMBAT", "target") libhealth:RegisterEvent("UNIT_COMBAT")
libhealth:RegisterEvent("PLAYER_TARGET_CHANGED") libhealth:RegisterEvent("PLAYER_TARGET_CHANGED")
libhealth:RegisterEvent("PLAYER_ENTERING_WORLD") libhealth:RegisterEvent("PLAYER_ENTERING_WORLD")
libhealth:SetScript("OnEvent", function() libhealth:SetScript("OnEvent", function()
+1 -1
View File
@@ -1134,7 +1134,7 @@ libpredict.sender:RegisterEvent("SPELL_HEAL_BY_SELF")
libpredict.sender:RegisterEvent("SPELL_HEAL_BY_OTHER") -- populates foreignCache for other healers libpredict.sender:RegisterEvent("SPELL_HEAL_BY_OTHER") -- populates foreignCache for other healers
-- force cache updates -- force cache updates
libpredict.sender:RegisterUnitEvent("UNIT_INVENTORY_CHANGED", "player") libpredict.sender:RegisterEvent("UNIT_INVENTORY_CHANGED")
libpredict.sender:RegisterEvent("SKILL_LINES_CHANGED") libpredict.sender:RegisterEvent("SKILL_LINES_CHANGED")
-- Shared cleanup helper for failed/interrupted casts -- Shared cleanup helper for failed/interrupted casts
+76 -26
View File
@@ -360,6 +360,68 @@ pfUI:RegisterModule("actionbar", function ()
end end
end end
local function ButtonMacroScan(self)
if self.bar > 10 then return end
if not self.scanmacro then return end
if pfUI.bars.skip_macro then return end
-- SuperCleveRoidMacros: for macros it manages, leave spellslot/booktype unset
-- so the button's icon, cooldown, and tooltip flow through the hooked
-- GetActionTexture / GetActionCooldown / GameTooltip:SetAction and follow the
-- active conditional dynamically, instead of being frozen to the first
-- statically-scanned spell.
if CleveRoids and CleveRoids.IsManagedAction and CleveRoids.IsManagedAction(self.id) then
self.spellslot, self.booktype, self.spellID = nil, nil, nil
return
end
local kind, slot = GetActionInfo(self.id)
self.spellslot, self.booktype, self.spellID = nil, nil, nil
if kind == 'macro' then
local name, _, body = GetMacroInfo(slot)
if name and body then
local match
for line in gfind(body, "[^%\n]+") do
_, _, match = string.find(line, '^#showtooltip (.+)')
-- allow the user to disable the scan
if match and strfind(match, "disable") then
return
end
if not match then
-- add support to specify custom tooltips via:
-- /run --showtooltip SPELLNAME
_, _, match = string.find(line, '%-%-showtooltip (.+)')
end
if not match then
_, _, match = string.find(line, '^/cast (.+)')
end
if not match then
_, _, match = string.find(line, '^/pfcast (.+)')
end
if not match then
_, _, match = string.find(line, '^/pfmouse (.+)')
end
if not match then
_, _, match = string.find(line, 'CastSpellByName%(%"(.+)%"%)')
end
if match then
self.spellslot, self.booktype, self.spellID = select(7, libspell.GetSpellInfo(match))
if self.spellslot and self.spellslot > 0 then return end
end
end
end
end
end
local function ButtonEnter(self) local function ButtonEnter(self)
self = self or this self = self or this
@@ -628,6 +690,7 @@ pfUI:RegisterModule("actionbar", function ()
local function ButtonFullUpdate(button) local function ButtonFullUpdate(button)
if not button then return end if not button then return end
ButtonMacroScan(button)
ButtonSlotUpdate(button) ButtonSlotUpdate(button)
ButtonRangeUpdate(button) ButtonRangeUpdate(button)
ButtonUsableUpdate(button) ButtonUsableUpdate(button)
@@ -744,28 +807,10 @@ pfUI:RegisterModule("actionbar", function ()
-- create the main event and update handler for pfUI actionbars -- create the main event and update handler for pfUI actionbars
local bars = CreateFrame("Frame", "pfActionBar", UIParent) local bars = CreateFrame("Frame", "pfActionBar", UIParent)
for event in pairs(special_events) do bars:RegisterEvent(event) end
-- The only unit events in the tables above; both concern the player alone. for event in pairs(global_events) do bars:RegisterEvent(event) end
-- A registration keeps its kind, so these have to go in unit-filtered from for event in pairs(aura_events) do bars:RegisterEvent(event) end
-- the start -- RegisterUnitEvent over a plain registration stays plain. for event in pairs(pet_events) do bars:RegisterEvent(event) end
local event_units = {
["UNIT_INVENTORY_CHANGED"] = "player",
["UNIT_PET"] = "player",
}
local function RegisterBarEvent(event)
local unit = event_units[event]
if unit then
bars:RegisterUnitEvent(event, unit)
else
bars:RegisterEvent(event)
end
end
for event in pairs(special_events) do RegisterBarEvent(event) end
for event in pairs(global_events) do RegisterBarEvent(event) end
for event in pairs(aura_events) do RegisterBarEvent(event) end
for event in pairs(pet_events) do RegisterBarEvent(event) end
-- refresh actionbar buttons on event -- refresh actionbar buttons on event
bars:SetScript("OnEvent", BarsEvent) bars:SetScript("OnEvent", BarsEvent)
@@ -971,7 +1016,7 @@ pfUI:RegisterModule("actionbar", function ()
local id = (bar-1)*12+button local id = (bar-1)*12+button
local exists = _G[button_name] and true or nil local exists = _G[button_name] and true or nil
local f = _G[button_name] or CreateFrame("Button", button_name, parent) local f = _G[button_name] or CreateFrame("Button", button_name, parent, ACTIONBAR_SECURE_TEMPLATE_BUTTON)
-- no button available, create a new one -- no button available, create a new one
if not exists then if not exists then
@@ -997,7 +1042,7 @@ pfUI:RegisterModule("actionbar", function ()
f.slot = id f.slot = id
-- cooldown -- cooldown
f.cd = CreateFrame("Model", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate") f.cd = CreateFrame(COOLDOWN_FRAME_TYPE, f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
f.cd.pfCooldownStyleAnimation = 1 f.cd.pfCooldownStyleAnimation = 1
f.cd.pfCooldownType = "NOGCD" f.cd.pfCooldownType = "NOGCD"
f.cd.pfCooldownSize = cd_size f.cd.pfCooldownSize = cd_size
@@ -1103,7 +1148,12 @@ pfUI:RegisterModule("actionbar", function ()
f.count:SetJustifyH("RIGHT") f.count:SetJustifyH("RIGHT")
f.count:SetJustifyV("BOTTOM") f.count:SetJustifyV("BOTTOM")
f.scanmacro, f.spellslot, f.booktype = nil, nil, nil -- macro spell scan (disabled when macro addons are loaded)
if C.bars.macroscan == "0" or pfUI:MacroAddonsLoaded() then
f.scanmacro, f.spellslot, f.booktype = nil, nil, nil
else
f.scanmacro = true
end
-- range glow color -- range glow color
f.rangeColor = GetStringColorObject(C.bars.rangecolor) f.rangeColor = GetStringColorObject(C.bars.rangecolor)
@@ -1183,7 +1233,7 @@ pfUI:RegisterModule("actionbar", function ()
-- create frame -- create frame
local init = not bars[i] local init = not bars[i]
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent) bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent, ACTIONBAR_SECURE_TEMPLATE_BAR)
bars[i]:SetID(i) bars[i]:SetID(i)
-- autohide -- autohide
+1 -1
View File
@@ -396,7 +396,7 @@ pfUI:RegisterModule("bags", function ()
if tpl == "BankItemButtonGenericTemplate" then if tpl == "BankItemButtonGenericTemplate" then
local bankslot = pfUI.bags[bag].slots[slot].frame local bankslot = pfUI.bags[bag].slots[slot].frame
local name = "pfBag" .. bag .. "item" .. slot .. "Cooldown" local name = "pfBag" .. bag .. "item" .. slot .. "Cooldown"
bankslot.cd = CreateFrame("Model", name, bankslot, "CooldownFrameTemplate") bankslot.cd = CreateFrame(COOLDOWN_FRAME_TYPE, name, bankslot, "CooldownFrameTemplate")
bankslot.cd:SetAllPoints(bankslot) bankslot.cd:SetAllPoints(bankslot)
bankslot.cd.pfCooldownStyleAnimation = 1 bankslot.cd.pfCooldownStyleAnimation = 1
bankslot.cd.pfCooldownType = "ALL" bankslot.cd.pfCooldownType = "ALL"
+2 -17
View File
@@ -7,15 +7,6 @@ pfUI:RegisterModule("buff", function ()
local br, bg, bb, ba = GetStringColor(pfUI_config.appearance.border.color) local br, bg, bb, ba = GetStringColor(pfUI_config.appearance.border.color)
-- Player aura slot ids, enumerated once per refresh pass (ScanPlayerAuraSlots)
-- and read per button by its aura index: one GetAuraSlots walk per range
-- instead of a by-index walk per button.
local helpfulSlots, harmfulSlots = {}, {}
local function ScanPlayerAuraSlots()
ScanAuraSlots("player", "HELPFUL", helpfulSlots, 32)
ScanAuraSlots("player", "HARMFUL", harmfulSlots, 16)
end
local function RefreshBuffButton(buff) local function RefreshBuffButton(buff)
if buff.btype == "HELPFUL" then if buff.btype == "HELPFUL" then
if C.buffs.separateweapons == "1" then if C.buffs.separateweapons == "1" then
@@ -32,8 +23,7 @@ pfUI:RegisterModule("buff", function ()
CreateBackdropShadow(buff) CreateBackdropShadow(buff)
end end
local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAura("player", buff.id, buff.btype)
local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
--detect weapon buffs --detect weapon buffs
if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then
@@ -164,7 +154,7 @@ pfUI:RegisterModule("buff", function ()
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent) pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED") pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED")
pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
pfUI.buff:RegisterUnitEvent("UNIT_MODEL_CHANGED", "player") pfUI.buff:RegisterEvent("UNIT_MODEL_CHANGED")
pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF") pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF")
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF") pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
pfUI.buff:SetScript("OnEvent", function() pfUI.buff:SetScript("OnEvent", function()
@@ -176,8 +166,6 @@ pfUI:RegisterModule("buff", function ()
pfUI.buff.wepbuffs.count = 0 pfUI.buff.wepbuffs.count = 0
end end
ScanPlayerAuraSlots()
for i=1,32 do for i=1,32 do
RefreshBuffButton(pfUI.buff.buffs.buttons[i]) RefreshBuffButton(pfUI.buff.buffs.buttons[i])
end end
@@ -258,9 +246,6 @@ pfUI:RegisterModule("buff", function ()
end end
end) end)
-- CreateBuffButton refreshes each new button from the slot buffers
ScanPlayerAuraSlots()
-- Weapon Buffs -- Weapon Buffs
pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent) pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent)
pfUI.buff.wepbuffs.count = 0 pfUI.buff.wepbuffs.count = 0
+10 -28
View File
@@ -73,16 +73,9 @@ pfUI:RegisterModule("buffwatch", function ()
return anchor return anchor
end end
-- reusable GetAuraSlots buffer, filled once per RefreshBuffBarFrame local function GetBuffData(unit, id, type, selfdebuff)
local auraSlots = {} local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type
local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAura(unit, id, filter)
-- Separate buffer for the tooltip handler: OnEnter can fire while a refresh
-- is showing/hiding bars under the cursor, so it must not share the above.
local tooltipSlots = {}
-- Reads one aura by the slot id GetAuraSlots returned (nil slot -> nil).
local function GetBuffData(unit, slot)
local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, slot)
if not name then return end if not name then return end
local remaining = expirationTime > 0 and (expirationTime - GetTime()) or 0 local remaining = expirationTime > 0 and (expirationTime - GetTime()) or 0
return remaining, icon, name, count, dispelType return remaining, icon, name, count, dispelType
@@ -120,22 +113,17 @@ pfUI:RegisterModule("buffwatch", function ()
GameTooltip:SetUnitAura("player", this.id, this.type) GameTooltip:SetUnitAura("player", this.id, this.type)
elseif this.type == "HARMFUL" then elseif this.type == "HARMFUL" then
-- selfdebuff filters the displayed list to player-cast harmful auras, but -- selfdebuff filters the displayed list to player-cast harmful auras, but
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up -- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
-- the displayed aura via the PLAYER filter, then find its position in the -- up the displayed aura via the PLAYER filter, then scan engine slots for
-- unfiltered list by name + sourceGUID. -- one whose name + sourceGUID match.
--
-- The unfiltered list is NOT capped at 16: once a unit's 16 debuff slots
-- are full the server parks further debuffs in buff slots, and
-- C_UnitAuras reports those as harmful too. So enumerate what it has.
local config = this.parent and this.parent.config local config = this.parent and this.parent.config
if config and config.selfdebuff == "1" then if config and config.selfdebuff == "1" then
local ownAura = C_UnitAuras.GetAuraDataByIndex(this.unit, this.id, "HARMFUL|PLAYER") local ownAura = C_UnitAuras.GetAuraDataByIndex(this.unit, this.id, "HARMFUL|PLAYER")
if ownAura then if ownAura then
local n = ScanAuraSlots(this.unit, "HARMFUL", tooltipSlots) for gameSlot = 1, 16 do
for i = 1, n do local check = C_UnitAuras.GetDebuffDataByIndex(this.unit, gameSlot)
local check = C_UnitAuras.GetAuraDataBySlot(this.unit, tooltipSlots[i])
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
GameTooltip:SetUnitAura(this.unit, i, "HARMFUL") GameTooltip:SetUnitAura(this.unit, gameSlot, "HARMFUL")
break break
end end
end end
@@ -246,15 +234,9 @@ pfUI:RegisterModule("buffwatch", function ()
local function RefreshBuffBarFrame(frame) local function RefreshBuffBarFrame(frame)
-- reinitialize all active buffs -- reinitialize all active buffs
local selfdebuff = frame.config.selfdebuff == "1" local selfdebuff = frame.config.selfdebuff == "1"
local filter = (selfdebuff and frame.type == "HARMFUL") and "HARMFUL|PLAYER" or frame.type
-- one GetAuraSlots enumeration per refresh instead of a by-index walk per
-- bar; the i-th slot is the i-th aura of the filtered list, so `i` stays
-- the index the tooltip / cancel handlers pass to the by-index API
ScanAuraSlots(frame.unit, filter, auraSlots, 32)
for i=1,32 do for i=1,32 do
local timeleft, texture, name, stacks, dtype = GetBuffData(frame.unit, auraSlots[i]) local timeleft, texture, name, stacks, dtype = GetBuffData(frame.unit, i, frame.type, selfdebuff)
timeleft = timeleft or 0 timeleft = timeleft or 0
if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then
+9 -15
View File
@@ -322,21 +322,15 @@ pfUI:RegisterModule("castbar", function ()
-- casts only ever fire arg1=="player" -- when the bar's unit resolves to the -- casts only ever fire arg1=="player" -- when the bar's unit resolves to the
-- player (target=self). PLAYER_TARGET/FOCUS_CHANGED re-polls so a unit -- player (target=self). PLAYER_TARGET/FOCUS_CHANGED re-polls so a unit
-- already mid-cast when it becomes the target/focus still shows. -- already mid-cast when it becomes the target/focus still shows.
-- Filter to this bar's unit, plus "player" for the target/focus bars: the cb:RegisterEvent("UNIT_SPELLCAST_START")
-- player's own casts only ever fire arg1=="player", so a self-targeted cast cb:RegisterEvent("UNIT_SPELLCAST_STOP")
-- has to reach them too (nil for the player bar itself, which the filter cb:RegisterEvent("UNIT_SPELLCAST_FAILED")
-- skips). This only narrows what arrives -- the arg1/UnitIsUnit test below cb:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
-- still decides whether the bar acts on it. cb:RegisterEvent("UNIT_SPELLCAST_DELAYED")
local selfunit = unitstr ~= "player" and "player" or nil cb:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
cb:RegisterUnitEvent("UNIT_SPELLCAST_START", unitstr, selfunit) cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
cb:RegisterUnitEvent("UNIT_SPELLCAST_STOP", unitstr, selfunit) cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
cb:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", unitstr, selfunit) cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
cb:RegisterUnitEvent("UNIT_SPELLCAST_INTERRUPTED", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_DELAYED", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_UPDATE", unitstr, selfunit)
if unitstr == "target" then if unitstr == "target" then
cb:RegisterEvent("PLAYER_TARGET_CHANGED") cb:RegisterEvent("PLAYER_TARGET_CHANGED")
elseif unitstr == "focus" then elseif unitstr == "focus" then
+23 -97
View File
@@ -63,23 +63,6 @@ pfUI:RegisterModule("chat", function ()
return pfUI_cache["chathistory"][realm][player][id] return pfUI_cache["chathistory"][realm][player][id]
end end
-- [ Chat Panel Colors ]
-- The panels normally inherit pfUI's global appearance theme; custom colors let chat
-- deviate from it. CreateBackdrop is re-run first to restore the theme, so toggling
-- custom colors back off doesn't leave the previous override behind.
local function ApplyPanelColors(panel)
if not panel then return end
CreateBackdrop(panel, default_border, nil, .8)
if C.chat.global.custombg ~= "1" then return end
local r, g, b, a = GetStringColor(C.chat.global.background)
panel.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border)
panel.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
pfUI.chat = CreateFrame("Frame",nil,UIParent) pfUI.chat = CreateFrame("Frame",nil,UIParent)
pfUI.chat.left = CreateFrame("Frame", "pfChatLeft", UIParent) pfUI.chat.left = CreateFrame("Frame", "pfChatLeft", UIParent)
@@ -93,11 +76,19 @@ pfUI:RegisterModule("chat", function ()
pfUI.chat.left:SetPoint("BOTTOMLEFT", 2*default_border,2*default_border) pfUI.chat.left:SetPoint("BOTTOMLEFT", 2*default_border,2*default_border)
pfUI.chat.left:SetScript("OnShow", function() pfUI.chat:RefreshChat() end) pfUI.chat.left:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
UpdateMovable(pfUI.chat.left) UpdateMovable(pfUI.chat.left)
ApplyPanelColors(pfUI.chat.left) CreateBackdrop(pfUI.chat.left, default_border, nil, .8)
if C.chat.global.frameshadow == "1" then if C.chat.global.frameshadow == "1" then
CreateBackdropShadow(pfUI.chat.left) CreateBackdropShadow(pfUI.chat.left)
end end
if C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background)
pfUI.chat.left.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border)
pfUI.chat.left.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
pfUI.chat.left.panelTop = CreateFrame("Frame", "leftChatPanelTop", pfUI.chat.left) pfUI.chat.left.panelTop = CreateFrame("Frame", "leftChatPanelTop", pfUI.chat.left)
pfUI.chat.left.panelTop:ClearAllPoints() pfUI.chat.left.panelTop:ClearAllPoints()
pfUI.chat.left.panelTop:SetHeight(C.global.font_size+default_border*2) pfUI.chat.left.panelTop:SetHeight(C.global.font_size+default_border*2)
@@ -260,11 +251,19 @@ pfUI:RegisterModule("chat", function ()
pfUI.chat.right:SetPoint("BOTTOMRIGHT", -2*default_border,2*default_border) pfUI.chat.right:SetPoint("BOTTOMRIGHT", -2*default_border,2*default_border)
pfUI.chat.right:SetScript("OnShow", function() pfUI.chat:RefreshChat() end) pfUI.chat.right:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
UpdateMovable(pfUI.chat.right) UpdateMovable(pfUI.chat.right)
ApplyPanelColors(pfUI.chat.right) CreateBackdrop(pfUI.chat.right, default_border, nil, .8)
if C.chat.global.frameshadow == "1" then if C.chat.global.frameshadow == "1" then
CreateBackdropShadow(pfUI.chat.right) CreateBackdropShadow(pfUI.chat.right)
end end
if C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background)
pfUI.chat.right.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border)
pfUI.chat.right.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
pfUI.chat.right.panelTop = CreateFrame("Frame", "rightChatPanelTop", pfUI.chat.right) pfUI.chat.right.panelTop = CreateFrame("Frame", "rightChatPanelTop", pfUI.chat.right)
pfUI.chat.right.panelTop:ClearAllPoints() pfUI.chat.right.panelTop:ClearAllPoints()
pfUI.chat.right.panelTop:SetHeight(C.global.font_size+default_border*2) pfUI.chat.right.panelTop:SetHeight(C.global.font_size+default_border*2)
@@ -297,68 +296,6 @@ pfUI:RegisterModule("chat", function ()
end end
end end
-- [ Chat Background Alpha ]
-- Blizzard's per-window transparency slider drives FCF_SetWindowAlpha, which only
-- touches the ChatFrame*Background textures that pfUI hides on docked frames. Mirror
-- the window's stored alpha onto the visible pfUI backdrop so the native slider
-- controls it directly.
local function ApplyChatBackgroundAlpha(panel, frame)
if not (panel and panel.backdrop and frame) then return end
local _, _, _, _, _, alpha = GetChatWindowInfo(frame:GetID())
alpha = tonumber(alpha)
if not alpha then return end
local r, g, b = panel.backdrop:GetBackdropColor()
panel.backdrop:SetBackdropColor(r, g, b, alpha)
end
function pfUI.chat:RefreshBackgroundAlpha()
-- Custom colors own the alpha. C.chat.global.background carries its own alpha, is
-- set by profiles and is shared with the meter skins, so the native slider must not
-- overwrite it -- doing so reverted the configured opacity on every refresh. The
-- slider only drives the panel when the user hasn't opted into pfUI's chat colors.
if C.chat.global.custombg == "1" then return end
-- left panel follows the currently selected docked tab
local selected = SELECTED_CHAT_FRAME
if not (selected and selected:GetParent() == pfUI.chat.left) then
selected = ChatFrame1
end
ApplyChatBackgroundAlpha(pfUI.chat.left, selected)
if C.chat.right.enable == "1" then
ApplyChatBackgroundAlpha(pfUI.chat.right, ChatFrame3)
end
end
-- live config apply, resolved by the gui as U["chat"]. Lets the color pickers
-- take effect on the spot instead of waiting for a /reload.
function pfUI.chat:UpdateConfig()
ApplyPanelColors(pfUI.chat.left)
ApplyPanelColors(pfUI.chat.right)
pfUI.chat:RefreshBackgroundAlpha()
end
function pfUI.chat:MigrateBackgroundAlpha()
-- Runs once per character. The previous SetupPositions default stored a hard 0 window
-- alpha, which now renders the pfUI backdrop fully transparent. Restore the historical
-- 0.8 look for any pfUI-managed window still sitting at 0. Only existing installs that
-- already ran chat setup carry that legacy 0; fresh installs get 0.8 from SetupPositions.
if pfUI_init.chatbgalpha then return end
if not pfUI_init["chat_position"] then return end
pfUI_init.chatbgalpha = true
local frames = { ChatFrame1, ChatFrame2 }
if C.chat.right.enable == "1" then table.insert(frames, ChatFrame3) end
for _, frame in ipairs(frames) do
local _, _, _, _, _, alpha = GetChatWindowInfo(frame:GetID())
alpha = tonumber(alpha)
if alpha and alpha <= 0 then
FCF_SetWindowAlpha(frame, 0.8)
end
end
end
function pfUI.chat:RefreshChat() function pfUI.chat:RefreshChat()
local panelheight = C.global.font_size*1.5 + default_border*2 + 2 local panelheight = C.global.font_size*1.5 + default_border*2 + 2
@@ -510,13 +447,9 @@ pfUI:RegisterModule("chat", function ()
for index, value in pairs(DOCKED_CHAT_FRAMES) do for index, value in pairs(DOCKED_CHAT_FRAMES) do
FCF_UpdateButtonSide(value) FCF_UpdateButtonSide(value)
end end
pfUI.chat:RefreshBackgroundAlpha()
end end
hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat) hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat)
hooksecurefunc("FCF_SetWindowAlpha", function() pfUI.chat:RefreshBackgroundAlpha() end)
hooksecurefunc("FCF_SelectDockFrame", function() pfUI.chat:RefreshBackgroundAlpha() end)
if C.chat.global.tabmouse == "1" then if C.chat.global.tabmouse == "1" then
pfUI.chat.mouseovertab = CreateFrame("Frame") pfUI.chat.mouseovertab = CreateFrame("Frame")
@@ -567,7 +500,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame1, 1) FCF_SetLocked(ChatFrame1, 1)
FCF_SetWindowName(ChatFrame1, GENERAL) FCF_SetWindowName(ChatFrame1, GENERAL)
FCF_SetWindowColor(ChatFrame1, 0, 0, 0) FCF_SetWindowColor(ChatFrame1, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame1, 0.8) FCF_SetWindowAlpha(ChatFrame1, 0)
FCF_SetChatWindowFontSize(ChatFrame1, 12) FCF_SetChatWindowFontSize(ChatFrame1, 12)
ChatFrame1:SetUserPlaced(1) ChatFrame1:SetUserPlaced(1)
@@ -575,7 +508,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame2, 1) FCF_SetLocked(ChatFrame2, 1)
FCF_SetWindowName(ChatFrame2, COMBAT_LOG) FCF_SetWindowName(ChatFrame2, COMBAT_LOG)
FCF_SetWindowColor(ChatFrame2, 0, 0, 0) FCF_SetWindowColor(ChatFrame2, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame2, 0.8) FCF_SetWindowAlpha(ChatFrame2, 0)
FCF_SetChatWindowFontSize(ChatFrame2, 12) FCF_SetChatWindowFontSize(ChatFrame2, 12)
ChatFrame2:SetUserPlaced(1) ChatFrame2:SetUserPlaced(1)
@@ -585,7 +518,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame3, 1) FCF_SetLocked(ChatFrame3, 1)
FCF_SetWindowName(ChatFrame3, T["Loot & Spam"]) FCF_SetWindowName(ChatFrame3, T["Loot & Spam"])
FCF_SetWindowColor(ChatFrame3, 0, 0, 0) FCF_SetWindowColor(ChatFrame3, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame3, 0.8) FCF_SetWindowAlpha(ChatFrame3, 0)
FCF_SetChatWindowFontSize(ChatFrame3, 12) FCF_SetChatWindowFontSize(ChatFrame3, 12)
FCF_UnDockFrame(ChatFrame3) FCF_UnDockFrame(ChatFrame3)
FCF_SetTabPosition(ChatFrame3, 0) FCF_SetTabPosition(ChatFrame3, 0)
@@ -633,9 +566,6 @@ pfUI:RegisterModule("chat", function ()
end end
pfUI.chat:SetScript("OnEvent", function() pfUI.chat:SetScript("OnEvent", function()
-- restore legacy chat windows stuck at 0 alpha before anything reads it
pfUI.chat:MigrateBackgroundAlpha()
-- set the default chat -- set the default chat
FCF_SelectDockFrame(SELECTED_CHAT_FRAME) FCF_SelectDockFrame(SELECTED_CHAT_FRAME)
@@ -827,7 +757,7 @@ pfUI:RegisterModule("chat", function ()
local real, _ = strsplit(":", name) local real, _ = strsplit(":", name)
local color = unknowncolorhex local color = unknowncolorhex
local match = false local match = false
local _, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real) local _, class = C_PlayerCache.GetPlayerInfoByName(real)
-- local guid = GetCurrentChatGUID() -- local guid = GetCurrentChatGUID()
-- if guid then -- if guid then
-- _, class = GetPlayerInfoByGUID(guid) -- _, class = GetPlayerInfoByGUID(guid)
@@ -845,12 +775,8 @@ pfUI:RegisterModule("chat", function ()
end end
if C.chat.text.tintunknown == "1" or match then if C.chat.text.tintunknown == "1" or match then
local icon = ""
if match and C.chat.text.playericons == "1" then
icon = GetFactionIcon(raceKey) .. GetRaceIcon(raceKey, sex)
end
text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)", text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)",
left..icon..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1") left..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1")
end end
end end
end end
+1 -1
View File
@@ -52,7 +52,7 @@ pfUI:RegisterModule("combopoints", function ()
-- combo -- combo
if class == "DRUID" or class == "ROGUE" then if class == "DRUID" or class == "ROGUE" then
local combo = CreateFrame("Frame") local combo = CreateFrame("Frame")
combo:RegisterUnitEvent("UNIT_COMBO_POINTS", "player") combo:RegisterEvent("UNIT_COMBO_POINTS")
combo:RegisterEvent("PLAYER_COMBO_POINTS") combo:RegisterEvent("PLAYER_COMBO_POINTS")
combo:RegisterEvent("PLAYER_TARGET_CHANGED") combo:RegisterEvent("PLAYER_TARGET_CHANGED")
combo:RegisterEvent("PLAYER_ENTERING_WORLD") combo:RegisterEvent("PLAYER_ENTERING_WORLD")
+18 -28
View File
@@ -6,35 +6,30 @@ pfUI:RegisterModule("cooldown", function ()
-- local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)} -- local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
-- local daycolor = {strsplit(",", C.appearance.cd.daycolor)} -- local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
local parent, parent_name
local function pfCooldownOnUpdate() local function pfCooldownOnUpdate()
-- Throttle FIRST. One of these runs per visible cooldown text, every frame, parent = this:GetParent()
-- so anything above this gate is multiplied by the frame rate and by how
-- many cooldowns are ticking.
local now = GetTime()
if (this.tick or 0) > now then return end
this.tick = now + .1
local parent = this:GetParent()
if not parent then this:Hide() return end if not parent then this:Hide() return end
parent_name = parent:GetName()
-- avoid to set cooldowns on invalid frames. The cooldown frame is stashed -- avoid to set cooldowns on invalid frames
-- at creation: resolving it as _G[parent:GetName() .. "Cooldown"] built and if parent_name and _G[parent_name .. "Cooldown"] then
-- interned that string twice per call, and this is the hottest path in the if not _G[parent_name .. "Cooldown"]:IsShown() then
-- UI. The stashed reference is also the frame itself rather than a guess this:Hide()
-- from its parent's name, so it holds for cooldowns named anything else. end
if this.cooldown and not this.cooldown:IsShown() then
this:Hide()
return
end end
-- only run every 0.1 seconds from here on
if ( this.tick or .1) > GetTime() then return else this.tick = GetTime() + .1 end
-- fix own alpha value (should be inherited, but somehow isn't always) -- fix own alpha value (should be inherited, but somehow isn't always)
if this:GetAlpha() ~= parent:GetAlpha() then if this:GetAlpha() ~= parent:GetAlpha() then
this:SetAlpha(parent:GetAlpha()) this:SetAlpha(parent:GetAlpha())
end end
if this.start < now then if this.start < GetTime() then
-- calculating remaining time as it should be -- calculating remaining time as it should be
local remaining = this.duration - (now - this.start) local remaining = this.duration - (GetTime() - this.start)
if remaining >= 0 then if remaining >= 0 then
this.text:SetText(GetColoredTimeString(remaining)) this.text:SetText(GetColoredTimeString(remaining))
else else
@@ -43,13 +38,13 @@ pfUI:RegisterModule("cooldown", function ()
else else
-- I have absolutely no idea, but it works: -- I have absolutely no idea, but it works:
-- https://github.com/Stanzilla/WoWUIBugs/issues/47 -- https://github.com/Stanzilla/WoWUIBugs/issues/47
local currentTime = time() local time = time()
local startupTime = currentTime - now local startupTime = time - GetTime()
-- just a simplification of: ((2^32) - (start * 1000)) / 1000 -- just a simplification of: ((2^32) - (start * 1000)) / 1000
local cdTime = (2 ^ 32) / 1000 - this.start local cdTime = (2 ^ 32) / 1000 - this.start
local cdStartTime = startupTime - cdTime local cdStartTime = startupTime - cdTime
local cdEndTime = cdStartTime + this.duration local cdEndTime = cdStartTime + this.duration
local remaining = cdEndTime - currentTime local remaining = cdEndTime - time
if remaining >= 0 then if remaining >= 0 then
this.text:SetText(GetColoredTimeString(remaining)) this.text:SetText(GetColoredTimeString(remaining))
@@ -60,16 +55,11 @@ pfUI:RegisterModule("cooldown", function ()
end end
local height, size local height, size
local textcount = 0
local function pfCreateCoolDown(cooldown, start, duration) local function pfCreateCoolDown(cooldown, start, duration)
textcount = textcount + 1 cooldown.pfCooldownText = CreateFrame("Frame", "pfCooldownFrame", cooldown:GetParent())
local name = cooldown.GetName and cooldown:GetName() or "pfCooldown" .. textcount
cooldown.pfCooldownText = CreateFrame("Frame", name .. "Text", cooldown:GetParent())
cooldown.pfCooldownText.cooldown = cooldown
cooldown.pfCooldownText:SetAllPoints(cooldown) cooldown.pfCooldownText:SetAllPoints(cooldown)
cooldown.pfCooldownText:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2) cooldown.pfCooldownText:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2)
cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString(name .. "TextString", "OVERLAY") cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString("pfCooldownFrameText", "OVERLAY")
if not cooldown.pfCooldownType then if not cooldown.pfCooldownType then
size = tonumber(C.appearance.cd.font_size_foreign) size = tonumber(C.appearance.cd.font_size_foreign)
+63 -188
View File
@@ -1,82 +1,20 @@
-- One server clock drives every power: Player::RegenerateAll fires every
-- REGEN_TIME_FULL (2s), re-arms with `+=`, and is never reset by casting. The
-- five-second rule (SetLastManaUse on any mana-costing cast) changes what a tick
-- pays, never when it lands; mp5 and the player's MOD_MANA_REGEN_INTERRUPT share
-- still come in. That share can't be computed here -- item sources are equip
-- auras absent from the buff list and m_modManaRegenInterrupt is never sent --
-- so the spark shows it instead: dim through the window until a tick lands.
--
-- The sweep free-runs on that clock and phase-locks to observed gains. A gain
-- mid-sweep (Illumination, Judgement of Wisdom, potions, a Mana Spring totem on
-- its own phase) is not the tick and never moves it.
local FIVE_SECOND_RULE = 5
-- gains farther than this from the predicted boundary are not the tick
local TICK_TOLERANCE = .25
-- arrival jitter; a tick inside this band confirms the sweep rather than
-- re-anchoring it, or the spark hitches at every wrap
local TICK_JITTER = .08
-- Player::RegenerateAll:
-- mod = GetTotalAuraModifier(SPELL_AURA_MOD_ENERGY_REGEN_TIME)
-- if mod > 0 then mod = mod * agility / 10 end
-- m_regenTimer += max(1, REGEN_TIME_FULL - mod) -- milliseconds
local REGEN_TIME_FULL = 2
local ENERGY_REGEN_TIME_AURA = 217 -- SPELL_AURA_MOD_ENERGY_REGEN_TIME
-- fixed magnitude is basePoints + baseDice (stored 11 -> 12); a die above 1 is
-- a roll the client can't know, so it counts as nothing rather than a guess
local amountCache = {}
local function auraAmount(spellID)
local amount = amountCache[spellID]
if amount then return amount end
amount = 0
local effects = C_Spell.GetSpellEffectInfo(spellID) -- nil for an id with no record
if effects then
for i = 1, 3 do
local fx = effects[i]
if fx.auraName == ENERGY_REGEN_TIME_AURA and fx.dieSides <= 1 then
amount = fx.basePoints + fx.baseDice
break
end
end
end
amountCache[spellID] = amount
return amount
end
-- A passive is in effect exactly while known (current rank only, never in the
-- buff list); anything castable or cast on us counts only while it is up.
local function getEnergyRegenTimeMod()
local sum = 0
for _, spellID in ipairs(C_SpellBook.GetPlayerSpellsByAura(ENERGY_REGEN_TIME_AURA)) do
if C_Spell.IsSpellPassive(spellID) then
sum = sum + auraAmount(spellID)
end
end
for i = 1, 32 do
local spellID = select(10, C_UnitAuras.UnitAura("player", i, "HELPFUL"))
if not spellID then break end
sum = sum + auraAmount(spellID)
end
return sum
end
-- cleared on SPELLS_CHANGED (passives) and PLAYER_AURAS_CHANGED (buffs), and
-- recomputed by the next tick that asks. Agility stays live: it's one call.
local energyRegenTimeMod
local function getAdjustedTickTimer() local function getAdjustedTickTimer()
if not energyRegenTimeMod then local adjustedEnergyTick = 2
energyRegenTimeMod = getEnergyRegenTimeMod()
end
if energyRegenTimeMod == 0 then return REGEN_TIME_FULL end
-- ms on the server, seconds here; the 1ms floor is the server's and this is a divisor -- Check rogue talents and compute energy tick timing reduction for Combat spec (1.18.0 Blade Rush Talent)
local reduction = energyRegenTimeMod * UnitStat("player", 2) / 10000 if UnitClassBase("player") == "ROGUE" then
return math.max(0.001, REGEN_TIME_FULL - reduction) local _, _, _, _, currRank = GetTalentInfo(2, 16)
local bladeRushRank = currRank or 0
if bladeRushRank > 0 then
local agility = UnitStat("player", 2) -- 2 is agility stat index
local reductionPerAgi = 0.0006 * bladeRushRank -- 0.0006 for rank 1, 0.0012 for rank 2
local totalReduction = agility * reductionPerAgi
adjustedEnergyTick = adjustedEnergyTick - totalReduction
end
end
return adjustedEnergyTick
end end
pfUI:RegisterModule("energytick", function() pfUI:RegisterModule("energytick", function()
@@ -84,53 +22,14 @@ pfUI:RegisterModule("energytick", function()
return return
end end
-- inside the module body on purpose: C is on pfUI.env, not _G
local function getBarWidth()
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
end
-- was this gain the regen tick? if so, re-anchor the sweep on it
local function lockTick(frame)
local now, period = GetTime(), getAdjustedTickTimer()
if frame.start then
-- signed distance to the nearest predicted boundary
local err = mod(now - frame.start, period)
if err > period / 2 then err = err - period end
if math.abs(err) <= TICK_TOLERANCE then
-- correct only what lies beyond normal jitter
if err > TICK_JITTER then
frame.start = frame.start + (err - TICK_JITTER)
elseif err < -TICK_JITTER then
frame.start = frame.start + (err + TICK_JITTER)
end
frame.max, frame.rejected = period, nil
return true
end
-- two rejected gains one period apart are the real clock: relock to it
local periodic = frame.rejected and math.abs(now - frame.rejected - period) <= TICK_TOLERANCE
if not periodic then
frame.rejected = now
return false
end
end
frame.start, frame.max, frame.rejected = now, period, nil
return true
end
local energytick = CreateFrame("Frame", nil, pfUI.uf.player.power.bar) local energytick = CreateFrame("Frame", nil, pfUI.uf.player.power.bar)
energytick:SetAllPoints(pfUI.uf.player.power.bar) energytick:SetAllPoints(pfUI.uf.player.power.bar)
energytick:RegisterEvent("PLAYER_ENTERING_WORLD") energytick:RegisterEvent("PLAYER_ENTERING_WORLD")
energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player") energytick:RegisterEvent("UNIT_DISPLAYPOWER")
energytick:RegisterUnitEvent("UNIT_ENERGY", "player") energytick:RegisterEvent("UNIT_ENERGY")
energytick:RegisterUnitEvent("UNIT_MANA", "player") energytick:RegisterEvent("UNIT_MANA")
energytick:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player") energytick:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF")
energytick:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", "player") energytick:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS")
energytick:RegisterEvent("SPELLS_CHANGED")
energytick:RegisterEvent("PLAYER_AURAS_CHANGED")
energytick:SetScript("OnEvent", function() energytick:SetScript("OnEvent", function()
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
@@ -143,52 +42,44 @@ pfUI:RegisterModule("energytick", function()
this:Hide() this:Hide()
end end
if event == "SPELLS_CHANGED" or event == "PLAYER_AURAS_CHANGED" then -- Filter nur eigene Energy-Gewinne von Talents/Buffs
energyRegenTimeMod = nil if event == "CHAT_MSG_SPELL_SELF_BUFF" or event == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" then
if string.find(arg1, "You gain") and string.find(arg1, "Energy from") then
this.ignoreNextGain = true
end
return return
end end
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
this.lastPower = UnitPower("player") this.lastMana = UnitPower("player")
end
-- the rule arms on the cast (Spell::TakePower: mana powerType, cost > 0),
-- not on a mana drop -- Mana Burn lowers mana without arming it
if event == "UNIT_SPELLCAST_SUCCEEDED" and arg1 == "player" then
local cost = C_Spell.GetSpellPowerCost(arg3)
cost = cost and cost[1]
if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then
this.fsrSpell, this.fsrEnd = arg3, GetTime() + FIVE_SECOND_RULE
this.fsrGain = nil
end
return
end
-- Unit::Update won't expire the rule while the spending spell still channels
if event == "UNIT_SPELLCAST_CHANNEL_STOP" and arg1 == "player" then
if this.fsrSpell and this.fsrSpell == arg3 then
this.fsrEnd, this.fsrGain = GetTime() + FIVE_SECOND_RULE, nil
end
return
end end
if (event == "UNIT_MANA" or event == "UNIT_ENERGY") and arg1 == "player" then if (event == "UNIT_MANA" or event == "UNIT_ENERGY") and arg1 == "player" then
local power = UnitPower("player") this.currentMana = UnitPower("player")
local diff = this.lastPower and (power - this.lastPower) or 0 local diff = 0
this.lastPower = power if this.lastMana then
diff = this.currentMana - this.lastMana
end
-- only a gain can be the tick; a spend never touches the phase if this.mode == "MANA" and diff < 0 then
if diff > 0 and lockTick(this) then this.target = 5
-- a tick inside the window proves regen continues through it elseif this.mode == "MANA" and diff > 0 then
if this.fsrEnd and this.fsrEnd > GetTime() then if UnitPower("player") >= UnitPowerMax("player") then
this.fsrGain = true this.start = nil
this.spark:SetAlpha(0)
this:Hide()
elseif this.max ~= 5 and diff > (this.badtick and this.badtick * 1.2 or 5) then
this.target = 2
else
this.badtick = diff
end end
elseif this.mode == "ENERGY" and diff >= 0 then
if not this.ignoreNextGain then
this.target = getAdjustedTickTimer()
end
this.ignoreNextGain = false
end end
this.lastMana = this.currentMana
-- phase is kept while hidden; OnUpdate catches up by whole periods
if this.mode == "MANA" and power >= UnitPowerMax("player") then
this:Hide()
end
end end
end) end)
@@ -199,53 +90,37 @@ pfUI:RegisterModule("energytick", function()
end end
this.tick = GetTime() + 0.020 -- ~50 FPS this.tick = GetTime() + 0.020 -- ~50 FPS
-- five-second rule drains to nothing if this.target then
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0 this.start, this.max = GetTime(), this.target
if this.mode == "MANA" and remaining > 0 then this.target = nil
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE) this.spark:SetAlpha(1)
this.fsrbar:Show() this:Show()
else
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
this.fsrbar:Hide()
end end
if not this.start then if not this.start then
this.spark:SetAlpha(0)
return
end
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
this.spark:SetAlpha(0)
return return
end end
this.current = GetTime() - this.start this.current = GetTime() - this.start
-- roll over by whole periods, not from now: restarting bakes frame
-- overshoot into the phase as drift the lock then has to chase
if this.current > this.max then if this.current > this.max then
this.start = this.start + this.max * math.floor(this.current / this.max) -- Don't restart tick timer if mana is full
this.max = getAdjustedTickTimer() if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
this.current = GetTime() - this.start this.start = nil
this.spark:SetAlpha(0)
return
end
this.start, this.max, this.current = GetTime(), getAdjustedTickTimer(), 0
end end
-- dim while the rule is up and nothing has ticked inside it yet local pos = (C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width)
this.spark:SetAlpha((remaining > 0 and not this.fsrGain) and .4 or 1) * (this.current / this.max)
if not C.unitframes.player.pheight then if not C.unitframes.player.pheight then
return return
end end
local pos = getBarWidth() * (this.current / this.max)
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0) this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
end) end)
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
energytick.fsrbar:SetTexture(1, 1, 1, .15)
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
energytick.fsrbar:Hide()
energytick.spark = energytick:CreateTexture(nil, "OVERLAY") energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark") energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
energytick.spark:SetHeight(C.unitframes.player.pheight + 15) energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
@@ -258,4 +133,4 @@ pfUI:RegisterModule("energytick", function()
energytick.spark:SetWidth(C.unitframes.player.pheight + 5) energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
hookUpdateConfig(pfUI.uf.player) hookUpdateConfig(pfUI.uf.player)
end end
end) end)
+1 -20
View File
@@ -130,28 +130,9 @@ pfUI:RegisterModule("eqcompare", function ()
SetTradeTargetItem = GetTradeTargetItemLink SetTradeTargetItem = GetTradeTargetItemLink
} }
-- Guda anchors its item tooltips with ANCHOR_NONE and its own SetPoint, and
-- the tooltip module then moves every ANCHOR_NONE tooltip to its configured
-- spot. Reading the rect inside the Set* call picks the side against the
-- position the tooltip is about to leave, so place the compare a frame later.
local deferred
if C_AddOns.DoesAddOnExist("Guda") then
EventUtil.ContinueOnAddOnLoaded("Guda", function()
deferred = true
end)
end
local function makeHook(getter) local function makeHook(getter)
return function(tooltip, arg1, arg2, arg3) return function(tooltip, arg1, arg2, arg3)
local link = getter(arg1, arg2, arg3) ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
if not deferred then
return ShowCompareItem(tooltip, link)
end
RunNextFrame(function()
if tooltip:IsShown() then
ShowCompareItem(tooltip, link)
end
end)
end end
end end
+8 -5
View File
@@ -2556,6 +2556,9 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["bars"], T["Button Animation"], C.bars, "animation", "dropdown", pfUI.gui.dropdowns.actionbuttonanimations) CreateConfig(U["bars"], T["Button Animation"], C.bars, "animation", "dropdown", pfUI.gui.dropdowns.actionbuttonanimations)
CreateConfig(U["bars"], T["Button Animation Trigger"], C.bars, "animmode", "dropdown", pfUI.gui.dropdowns.animationmode) CreateConfig(U["bars"], T["Button Animation Trigger"], C.bars, "animmode", "dropdown", pfUI.gui.dropdowns.animationmode)
CreateConfig(U["bars"], T["Show Animation On Hidden Bars"], C.bars, "animalways", "checkbox") CreateConfig(U["bars"], T["Show Animation On Hidden Bars"], C.bars, "animalways", "checkbox")
if not pfUI:MacroAddonsLoaded() then
CreateConfig(U["bars"], T["Scan Macros For Spells"], C.bars, "macroscan", "checkbox", nil, nil, nil, nil)
end
CreateConfig(U["bars"], T["Show Reagent Count"], C.bars, "reagents", "checkbox") CreateConfig(U["bars"], T["Show Reagent Count"], C.bars, "reagents", "checkbox")
CreateConfig(U["bars"], T["Highlight Equipped Items"], C.bars, "showequipped", "checkbox") CreateConfig(U["bars"], T["Highlight Equipped Items"], C.bars, "showequipped", "checkbox")
CreateConfig(U["bars"], T["Equipped Item Color"], C.bars, "eqcolor", "color") CreateConfig(U["bars"], T["Equipped Item Color"], C.bars, "eqcolor", "color")
@@ -2864,7 +2867,6 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Generate Playerlinks"], C.chat.text, "playerlinks", "checkbox") CreateConfig(nil, T["Generate Playerlinks"], C.chat.text, "playerlinks", "checkbox")
CreateConfig(nil, T["Enable URL Detection"], C.chat.text, "detecturl", "checkbox") CreateConfig(nil, T["Enable URL Detection"], C.chat.text, "detecturl", "checkbox")
CreateConfig(nil, T["Enable Class Colors"], C.chat.text, "classcolor", "checkbox") CreateConfig(nil, T["Enable Class Colors"], C.chat.text, "classcolor", "checkbox")
CreateConfig(nil, T["Show Faction & Race Icons"] .. " " .. GetPlayerRaceIcons(), C.chat.text, "playericons", "checkbox")
CreateConfig(nil, T["Enable Player Levels"], C.chat.text, "playerlevel", "checkbox") CreateConfig(nil, T["Enable Player Levels"], C.chat.text, "playerlevel", "checkbox")
CreateConfig(nil, T["Who Search Unknown Classes (|cffffaaaaExperimental|r)"], C.chat.text, "whosearchunknown", "checkbox") CreateConfig(nil, T["Who Search Unknown Classes (|cffffaaaaExperimental|r)"], C.chat.text, "whosearchunknown", "checkbox")
CreateConfig(nil, T["Colorize Unknown Classes"], C.chat.text, "tintunknown", "checkbox") CreateConfig(nil, T["Colorize Unknown Classes"], C.chat.text, "tintunknown", "checkbox")
@@ -2878,9 +2880,9 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Only Show Chat Dock On Mouseover"], C.chat.global, "tabmouse", "checkbox") CreateConfig(nil, T["Only Show Chat Dock On Mouseover"], C.chat.global, "tabmouse", "checkbox")
CreateConfig(nil, T["Enable Chat Tab Flashing"], C.chat.global, "chatflash", "checkbox") CreateConfig(nil, T["Enable Chat Tab Flashing"], C.chat.global, "chatflash", "checkbox")
CreateConfig(nil, T["Enable Frame Shadow"], C.chat.global, "frameshadow", "checkbox") CreateConfig(nil, T["Enable Frame Shadow"], C.chat.global, "frameshadow", "checkbox")
CreateConfig(U["chat"], T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox") CreateConfig(nil, T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox")
CreateConfig(U["chat"], T["Chat Background Color"], C.chat.global, "background", "color") CreateConfig(nil, T["Chat Background Color"], C.chat.global, "background", "color")
CreateConfig(U["chat"], T["Chat Border Color"], C.chat.global, "border", "color") CreateConfig(nil, T["Chat Border Color"], C.chat.global, "border", "color")
CreateConfig(nil, T["Enable Custom Incoming Whispers Layout"], C.chat.global, "whispermod", "checkbox") CreateConfig(nil, T["Enable Custom Incoming Whispers Layout"], C.chat.global, "whispermod", "checkbox")
CreateConfig(nil, T["Incoming Whispers Color"], C.chat.global, "whisper", "color") CreateConfig(nil, T["Incoming Whispers Color"], C.chat.global, "whisper", "color")
CreateConfig(nil, T["Enable Sticky Chat"], C.chat.global, "sticky", "checkbox") CreateConfig(nil, T["Enable Sticky Chat"], C.chat.global, "sticky", "checkbox")
@@ -3026,7 +3028,8 @@ pfUI:RegisterModule("gui", function ()
CreateGUIEntry(T["Components"], T["Modules"], function() CreateGUIEntry(T["Components"], T["Modules"], function()
table.sort(pfUI.modules) table.sort(pfUI.modules)
for i,m in pairs(pfUI.modules) do for i,m in pairs(pfUI.modules) do
if m ~= "gui" then -- skip gui and macrotweak when macro addons are loaded
if m ~= "gui" and not (m == "macrotweak" and pfUI:MacroAddonsLoaded()) then
-- create disabled entry if not existing and display -- create disabled entry if not existing and display
pfUI:UpdateConfig("disabled", nil, m, "0") pfUI:UpdateConfig("disabled", nil, m, "0")
CreateConfig(nil, T["Disable Module"] .. " " .. m, C.disabled, m, "checkbox") CreateConfig(nil, T["Disable Module"] .. " " .. m, C.disabled, m, "checkbox")
+1 -1
View File
@@ -511,7 +511,7 @@ pfUI:RegisterModule("loot", function ()
end end
function pfUI.loot:CreateSlot(id) function pfUI.loot:CreateSlot(id)
local frame = CreateFrame("LootButton", 'pfLootButton'..id, pfUI.loot) local frame = CreateFrame(LOOT_BUTTON_FRAME_TYPE, 'pfLootButton'..id, pfUI.loot)
frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp') frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp')
frame:SetPoint("LEFT", border*2, 0) frame:SetPoint("LEFT", border*2, 0)
frame:SetPoint("RIGHT", -border*2, 0) frame:SetPoint("RIGHT", -border*2, 0)
+17 -6
View File
@@ -104,6 +104,7 @@ pfUI:RegisterModule("loothistory", function ()
-- Frame pools -- Frame pools
-- ========================================================================== -- ==========================================================================
local itemFrames = {} local itemFrames = {}
local usedPlayers, freePlayers = {}, {}
local FullUpdate -- forward declaration (toggle handlers call it) local FullUpdate -- forward declaration (toggle handlers call it)
@@ -203,10 +204,20 @@ pfUI:RegisterModule("loothistory", function ()
return f return f
end end
local playerPool = CreateObjectPool(CreatePlayerFrame, function(_, pf) local function RecycleAllPlayers()
pf:Hide() for i = 1, table.getn(usedPlayers) do
pf:ClearAllPoints() local pf = usedPlayers[i]
end) pf:Hide()
table.insert(freePlayers, pf)
end
usedPlayers = {}
end
local function GetPlayerFrame()
local pf = table.remove(freePlayers) or CreatePlayerFrame()
table.insert(usedPlayers, pf)
return pf
end
local function SetToggleTexture(toggle, isExpanded) local function SetToggleTexture(toggle, isExpanded)
if isExpanded then if isExpanded then
@@ -298,7 +309,7 @@ pfUI:RegisterModule("loothistory", function ()
function FullUpdate() function FullUpdate()
if not pfUI.loothistory:IsShown() then return end if not pfUI.loothistory:IsShown() then return end
playerPool:ReleaseAll() RecycleAllPlayers()
local num = C_LootHistory.GetNumItems() local num = C_LootHistory.GetNumItems()
local y = -2 local y = -2
@@ -316,7 +327,7 @@ pfUI:RegisterModule("loothistory", function ()
for p = 1, f.numPlayers do for p = 1, f.numPlayers do
local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p) local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p)
if ShouldDisplayPlayer(f.isDone, roll, isMe) then if ShouldDisplayPlayer(f.isDone, roll, isMe) then
local pf = playerPool:Acquire() local pf = GetPlayerFrame()
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner) RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf:ClearAllPoints() pf:ClearAllPoints()
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y) pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
+67
View File
@@ -0,0 +1,67 @@
pfUI:RegisterModule("macrotweak", function ()
local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" }
local disabled = false
for _, addon in pairs(conflictAddons) do
local name = addon
EventUtil.ContinueOnAddOnLoaded(name, function()
if not disabled then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: " .. name .. " found, macrotweak disabled.")
end
disabled = true
end)
end
-- do not write macro calls into chat input history
-- (install once: _AddHistoryLine is our backup slot and is nil until we set it)
if not ChatFrameEditBox._AddHistoryLine then
local userinput
ChatFrameEditBox._AddHistoryLine = ChatFrameEditBox.AddHistoryLine
ChatFrameEditBox.AddHistoryLine = function(self, text)
if disabled then return ChatFrameEditBox._AddHistoryLine(self, text) end
if not userinput and text and string.find(text, "^/run(.+)") then return end
if not userinput and string.find(text, "^/script(.+)") then return end
if not userinput and string.find(text, "^/cast(.+)") then return end
ChatFrameEditBox._AddHistoryLine(self, text)
end
local OnEnter = ChatFrameEditBox:GetScript("OnEnterPressed")
ChatFrameEditBox:SetScript("OnEnterPressed", function(a1,a2,a3,a4)
userinput = true
OnEnter(a1,a2,a3,a4)
userinput = nil
end)
end
-- make sure #showtooltip inside macros won't be sent
local hookSendChatMessage = SendChatMessage
function _G.SendChatMessage(msg, ...)
if disabled then return hookSendChatMessage(msg, unpack(arg)) end
if msg and string.find(msg, "^#showtooltip ") then return end
hookSendChatMessage(msg, unpack(arg))
end
-- add /use and /equip to the macro api:
-- https://wowwiki.fandom.com/wiki/Making_a_macro
-- supported arguments:
-- /use <itemname>
-- /use <inventory slot>
-- /use <bag> <slot>
pfUI.api.RegisterSlashCommand("PFUSE", { "/equip" , "/use", "/pfequip", "/pfuse" }, function (msg)
if not msg or msg == "" then return end
local bag, slot, _
if string.find(msg, "%d+%s+%d+") then
_, _, bag, slot = string.find(msg, "(%d+)%s+(%d+)")
elseif string.find(msg, "%d+") then
_, _, slot = string.find(msg, "(%d+)")
else
bag, slot = FindItem(msg)
end
if bag and slot then
UseContainerItem(bag, slot)
elseif not bag and slot then
UseInventoryItem(slot)
end
end)
end)
+44 -42
View File
@@ -30,14 +30,20 @@ pfUI:RegisterModule("mapreveal", function ()
pfUI.mapreveal:UpdateConfig() pfUI.mapreveal:UpdateConfig()
end) end)
local explores = {}
local explorecaches = {} local explorecaches = {}
local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } } local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } }
-- Own texture pool - separate from Blizzard's WorldMapOverlay textures -- Own texture pool - separate from Blizzard's WorldMapOverlay textures
local overlayPool = CreateTexturePool(WorldMapDetailFrame, "BORDER", nil, nil, function(_, tex) local pfOverlays = {}
tex:Hide() local pfOverlayMax = 0
tex:ClearAllPoints()
end) local function pfGetOverlay(idx)
if not pfOverlays[idx] then
pfOverlays[idx] = WorldMapDetailFrame:CreateTexture("pfReveal"..idx, "BORDER")
end
return pfOverlays[idx]
end
local exploreEnter = function() local exploreEnter = function()
WorldMapTooltip:ClearLines() WorldMapTooltip:ClearLines()
@@ -63,38 +69,14 @@ pfUI:RegisterModule("mapreveal", function ()
end end
end end
-- Magnifying-glass icons for unexplored overlays. Everything constant lives
-- in the creator; the update only anchors and labels what it acquires -- and
-- it only acquires the ones it is going to show, where the old table grew an
-- icon for every overlay in the zone and hid most of them again.
local function CreateExplore()
local explore = CreateFrame("Frame", nil, WorldMapDetailFrame)
explore:SetSize(16, 16)
explore:SetScript("OnEnter", exploreEnter)
explore:SetScript("OnLeave", exploreLeave)
explore:EnableMouse(true)
explore:SetFrameLevel(255)
explore.tex = explore:CreateTexture(nil, "OVERLAY")
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
explore.tex:SetBlendMode("ADD")
explore.tex:SetTexCoord(.08, .92, .08, .92)
explore.tex:SetAllPoints()
return explore
end
local explorePool = CreateObjectPool(CreateExplore, function(_, explore)
explore:Hide()
explore:ClearAllPoints()
end)
local function pfWorldMapFrame_Update() local function pfWorldMapFrame_Update()
-- clear stale caches -- clear stale caches
for k in pairs(explorecaches) do explorecaches[k] = nil end for k in pairs(explorecaches) do explorecaches[k] = nil end
-- hide all our textures from last frame -- hide all our textures from last frame
overlayPool:ReleaseAll() for i = 1, pfOverlayMax do
pfOverlays[i]:Hide()
end
local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color) local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color)
local mapFileName = GetMapInfo() local mapFileName = GetMapInfo()
@@ -112,13 +94,14 @@ pfUI:RegisterModule("mapreveal", function ()
local zoneKnown = alreadyknown[mapFileName] local zoneKnown = alreadyknown[mapFileName]
-- hide explore icons -- hide explore icons
explorePool:ReleaseAll() for _, frame in pairs(explores) do frame:Hide() end
-- ClassicAPI: full overlay list for the viewed zone (explored + unexplored), -- ClassicAPI: full overlay list for the viewed zone (explored + unexplored),
-- read straight from WorldMapOverlay.dbc. Replaces the hand-measured pfMapOverlayData. -- read straight from WorldMapOverlay.dbc. Replaces the hand-measured pfMapOverlayData.
local zoneData = C_Map.GetMapOverlays() or {} local zoneData = C_Map.GetMapOverlays() or {}
local textureCount = 0
for _, overlay in ipairs(zoneData) do for i, overlay in ipairs(zoneData) do
local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE" local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE"
local textureName = overlay.texturePath -- full engine path (for SetTexture) local textureName = overlay.texturePath -- full engine path (for SetTexture)
local textureWidth = overlay.textureWidth local textureWidth = overlay.textureWidth
@@ -126,15 +109,30 @@ pfUI:RegisterModule("mapreveal", function ()
local offsetX = overlay.offsetX local offsetX = overlay.offsetX
local offsetY = overlay.offsetY local offsetY = overlay.offsetY
-- explore magnifying glass icon. `alreadyknown` stores the FULL paths -- explore magnifying glass icon
-- GetMapOverlayInfo returns, so compare with the full path, not the bare explores[i] = explores[i] or CreateFrame("Frame", nil, WorldMapDetailFrame)
-- name. local explore = explores[i]
explore:SetWidth(16)
explore:SetHeight(16)
explore:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", offsetX + textureWidth/2, -offsetY - textureHeight/2)
explore:SetScript("OnEnter", exploreEnter)
explore:SetScript("OnLeave", exploreLeave)
explore:EnableMouse(true)
explore:SetFrameLevel(255)
explore.name = mapFileName .. " (" .. name .. ")"
explore.area = name -- cache key: explorecaches is keyed by the plain area name
explore.tex = explore.tex or explore:CreateTexture("", "OVERLAY")
explore.tex:SetBlendMode("ADD")
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 if C.appearance.worldmap.mapexploration == "1" and not zoneKnown[string.upper(textureName)] then
local explore = explorePool:Acquire() explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
explore:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", offsetX + textureWidth/2, -offsetY - textureHeight/2)
explore.name = mapFileName .. " (" .. name .. ")"
explore.area = name -- cache key: explorecaches is keyed by the plain area name
explore:Show() explore:Show()
else
explore:Hide()
end end
-- render overlay texture tiles on BORDER draw layer -- render overlay texture tiles on BORDER draw layer
@@ -149,9 +147,11 @@ pfUI:RegisterModule("mapreveal", function ()
-- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun). -- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun).
if C.appearance.worldmap.mapreveal == "1" then if C.appearance.worldmap.mapreveal == "1" then
for _, tile in ipairs(overlay.tiles) do for _, tile in ipairs(overlay.tiles) do
local tex = overlayPool:Acquire() textureCount = textureCount + 1
local tex = pfGetOverlay(textureCount)
tex:SetSize(tile.width, tile.height) tex:SetWidth(tile.width)
tex:SetHeight(tile.height)
tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY) tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY)
tex:ClearAllPoints() tex:ClearAllPoints()
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY) tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY)
@@ -165,6 +165,8 @@ pfUI:RegisterModule("mapreveal", function ()
end end
end end
end end
pfOverlayMax = math.max(pfOverlayMax, textureCount)
end end
-- hook WorldMapFrame_Update -- hook WorldMapFrame_Update
+18 -38
View File
@@ -1,9 +1,12 @@
pfUI:RegisterModule("marktracking", function () pfUI:RegisterModule("marktracking", function ()
if not UnitExists("mark1") and not UnitExists("mark8") then
if not pcall(function() UnitExists("mark1") end) then return end
end
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star
local markerTokens = {} -- [i] = "markN" local markerTokens = {}
local markerIndex = {} -- ["markN"] = i, for the event handler's arg1
local markerConfigKeys = { local markerConfigKeys = {
"raidmarkercolor_star", "raidmarkercolor_star",
@@ -19,7 +22,6 @@ pfUI:RegisterModule("marktracking", function ()
local markerColors = {} local markerColors = {}
for i, markKey in ipairs(markerConfigKeys) do for i, markKey in ipairs(markerConfigKeys) do
markerTokens[i] = "mark" .. i markerTokens[i] = "mark" .. i
markerIndex[markerTokens[i]] = i
local r, g, b, a = GetStringColor(C.unitframes[markKey]) local r, g, b, a = GetStringColor(C.unitframes[markKey])
markerColors[i] = { tonumber(r), tonumber(g), tonumber(b), tonumber(a) } markerColors[i] = { tonumber(r), tonumber(g), tonumber(b), tonumber(a) }
end end
@@ -71,7 +73,8 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
else else
pfUI.marktracking:SetPoint("TOP", UIParent, "CENTER", 0, 0) pfUI.marktracking:SetPoint("TOP", UIParent, "CENTER", 0, 0)
end end
pfUI.marktracking:SetSize(TOTAL_ROW_WIDTH, 8 * (ROW_HEIGHT + 1) + border * 2 - 1) pfUI.marktracking:SetWidth(TOTAL_ROW_WIDTH)
pfUI.marktracking:SetHeight(8 * (ROW_HEIGHT + 1) + border * 2 - 1)
pfUI.marktracking:Hide() pfUI.marktracking:Hide()
CreateBackdrop(pfUI.marktracking) CreateBackdrop(pfUI.marktracking)
@@ -281,50 +284,27 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
-- Event-driven scanner frame -- Event-driven scanner frame
local scanner = CreateFrame("Frame") local scanner = CreateFrame("Frame")
-- Fallback poll: catches units that come into range AFTER a marker was set
-- (no event fires for that case, so we need this safety net). UpdateDisplay
-- is a full eight-row rebuild, so it exists only while grouped -- raid markers
-- are a group feature and there is nothing to discover alone. The group events
-- below start and cancel it, so outside a group there is no timer queued at
-- all rather than one waking every second to return early.
--
-- Deliberately NOT keyed on a mark being visible: a marker set on a unit that
-- is out of range shows no row, and that is exactly what this poll catches.
local poll
local function UpdatePoll()
local grouped = IsInGroup()
if grouped and not poll then
poll = C_Timer.NewTicker(FALLBACK_INTERVAL, UpdateDisplay)
elseif not grouped and poll then
poll:Cancel()
poll = nil
end
end
-- RAID_TARGET_UPDATE: a raid marker was set/cleared -> full refresh -- RAID_TARGET_UPDATE: a raid marker was set/cleared -> full refresh
-- PLAYER_ENTERING_WORLD: login/reload/zone -> full refresh -- PLAYER_ENTERING_WORLD: login/reload/zone -> full refresh
-- PARTY_MEMBERS_CHANGED/RAID_ROSTER_UPDATE: joined or left a group -> the rows -- UNIT_HEALTH/UNIT_MAXHEALTH: ClassicAPI fires these per token; with the mark
-- can change, and the fallback poll starts or stops with it -- tokens observed they arrive as arg1 == "markN", so we refresh just that
-- UNIT_HEALTH/UNIT_MAXHEALTH: filtered to the eight mark tokens, so arg1 is -- one row (UpdateRow) instead of rescanning all eight.
-- always "markN" and we refresh just that row (UpdateRow) instead of
-- rescanning all eight.
scanner:RegisterEvent("RAID_TARGET_UPDATE") scanner:RegisterEvent("RAID_TARGET_UPDATE")
scanner:RegisterEvent("PLAYER_ENTERING_WORLD") scanner:RegisterEvent("PLAYER_ENTERING_WORLD")
scanner:RegisterEvent("PARTY_MEMBERS_CHANGED") scanner:RegisterEvent("UNIT_HEALTH")
scanner:RegisterEvent("RAID_ROSTER_UPDATE") scanner:RegisterEvent("UNIT_MAXHEALTH")
scanner:RegisterUnitEvent("UNIT_HEALTH", "mark1", "mark2", "mark3", "mark4", "mark5", "mark6", "mark7", "mark8")
scanner:RegisterUnitEvent("UNIT_MAXHEALTH", "mark1", "mark2", "mark3", "mark4", "mark5", "mark6", "mark7", "mark8")
scanner:SetScript("OnEvent", function() scanner:SetScript("OnEvent", function()
if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
-- arg1 is one of the eight tokens we registered for, so this is a lookup -- arg1 is the token; "markN" -> N, nil for any non-mark token.
-- rather than a parse -- string.match would allocate a capture and local i = arg1 and tonumber(string.match(arg1, "^mark(%d)"))
-- tonumber would parse it, on every health tick of every marked unit.
local i = arg1 and markerIndex[arg1]
if i then UpdateRow(i) end if i then UpdateRow(i) end
return return
end end
if event ~= "RAID_TARGET_UPDATE" then UpdatePoll() end
UpdateDisplay() UpdateDisplay()
end) end)
-- Fallback poll: catches units that come into range AFTER a marker was set
-- (no event fires for that case, so we need this safety net)
C_Timer.NewTicker(FALLBACK_INTERVAL, UpdateDisplay)
end) end)
+1 -1
View File
@@ -225,7 +225,7 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimap.pvpicon = CreateFrame("Frame", nil, pfUI.minimap) pfUI.minimap.pvpicon = CreateFrame("Frame", nil, pfUI.minimap)
pfUI.minimap.pvpicon:Hide() pfUI.minimap.pvpicon:Hide()
pfUI.minimap.pvpicon:RegisterEvent("UPDATE_FACTION") pfUI.minimap.pvpicon:RegisterEvent("UPDATE_FACTION")
pfUI.minimap.pvpicon:RegisterUnitEvent("UNIT_FACTION", "player") pfUI.minimap.pvpicon:RegisterEvent("UNIT_FACTION")
pfUI.minimap.pvpicon:SetFrameStrata("HIGH") pfUI.minimap.pvpicon:SetFrameStrata("HIGH")
pfUI.minimap.pvpicon:SetSize(16, 16) pfUI.minimap.pvpicon:SetSize(16, 16)
pfUI.minimap.pvpicon:SetAlpha(.5) pfUI.minimap.pvpicon:SetAlpha(.5)
+118 -145
View File
@@ -5,7 +5,10 @@ pfUI:RegisterModule("nameplates", function ()
-- Local function references for performance -- Local function references for performance
local GetTime = GetTime local GetTime = GetTime
local UnitName = UnitName local UnitName = UnitName
local UnitClass = UnitClass
local UnitLevel = UnitLevel
local UnitIsPlayer = UnitIsPlayer local UnitIsPlayer = UnitIsPlayer
local UnitIsDead = UnitIsDead
local UnitAffectingCombat = UnitAffectingCombat local UnitAffectingCombat = UnitAffectingCombat
local UnitIsUnit = UnitIsUnit local UnitIsUnit = UnitIsUnit
local UnitCanAssist = UnitCanAssist local UnitCanAssist = UnitCanAssist
@@ -113,7 +116,6 @@ pfUI:RegisterModule("nameplates", function ()
-- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation) -- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation)
local debuffDisplayBuf = {} -- [i] = { effect, texture, stacks, dtype, duration, timeleft } local debuffDisplayBuf = {} -- [i] = { effect, texture, stacks, dtype, duration, timeleft }
for i = 1, 16 do debuffDisplayBuf[i] = {} end for i = 1, 16 do debuffDisplayBuf[i] = {} end
local auraSlots = {} -- reusable GetAuraSlots buffer for the per-plate aura scan
local threatMemory = {} -- guid -> true if mob had player targeted local threatMemory = {} -- guid -> true if mob had player targeted
-- local debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn) -- local debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn)
@@ -159,24 +161,6 @@ pfUI:RegisterModule("nameplates", function ()
cfg.debuffanim = tonumber(C.nameplates.debuffanim) or 0 cfg.debuffanim = tonumber(C.nameplates.debuffanim) or 0
cfg.debufftext = tonumber(C.nameplates.debufftext) or 1 cfg.debufftext = tonumber(C.nameplates.debufftext) or 1
-- Throttle delays, resolved once instead of per plate per tick.
-- libthrottle:Get walks the saved-variable table, a defaults fallback and a
-- preset table, and can build a "<category>_custom" key -- the per-plate
-- OnUpdate was calling it one or two times for every visible plate, a
-- hundred times a second, just to decide it had nothing to do.
--
-- cfg.throttle_min is the floor across all four. No plate can ever be due
-- sooner than that, so the update can bail on it before working out which
-- category it actually belongs to.
cfg.throttle_target = pfUI.throttle:Get("nameplates_target")
cfg.throttle_mass = pfUI.throttle:Get("nameplates_mass")
cfg.throttle_normal = pfUI.throttle:Get("nameplates")
cfg.throttle_castbar = pfUI.throttle:Get("nameplates_castbar")
cfg.throttle_min = cfg.throttle_target
if cfg.throttle_mass < cfg.throttle_min then cfg.throttle_min = cfg.throttle_mass end
if cfg.throttle_normal < cfg.throttle_min then cfg.throttle_min = cfg.throttle_normal end
if cfg.throttle_castbar < cfg.throttle_min then cfg.throttle_min = cfg.throttle_castbar end
-- Rebuild offtanks lookup table -- Rebuild offtanks lookup table
offtanks = {} offtanks = {}
for k, v in pairs({strsplit("#", C.nameplates.combatofftanks)}) do for k, v in pairs({strsplit("#", C.nameplates.combatofftanks)}) do
@@ -312,23 +296,18 @@ pfUI:RegisterModule("nameplates", function ()
return plate.creatureType return plate.creatureType
end end
-- Totem icon: UnitCreatedBySpell returns the totem-drop spell, whose icon IS -- Totem icon, read straight from the game:
-- the totem's icon. It's a broadcast descriptor field the client has for every -- * Passive totems self-cast their provided buff, so they carry exactly one
-- summoned unit in range, so it resolves immediately for passive and active -- aura whose icon IS the totem's icon (Totem::Summon TOTEM_PASSIVE).
-- totems alike -- no self-aura read or attack-cast capture needed. -- * Active totems (Searing/Magma/Fire Nova) cast at enemies and hold no
-- -- self-aura, so their icon arrives via UNIT_SPELLCAST_SUCCEEDED (cached
-- Re-read the spell every call rather than caching the icon outright: a shaman -- into plate.totemIcon by the event handler); nil here until then.
-- can swap the totem in place (same unit, new drop spell) with no plate re-add
-- to invalidate a cache, so key the cached texture on the spell id and refresh
-- it only when the spell changes.
local function TotemPlate(plate) local function TotemPlate(plate)
if C.nameplates.totemicons ~= "1" then return nil end if C.nameplates.totemicons ~= "1" then return nil end
if CreatureType(plate) ~= 11 then return nil end if CreatureType(plate) ~= 11 then return nil end
local spellId = UnitCreatedBySpell(plate.cachedGuid) if plate.totemIcon then return plate.totemIcon end
if spellId ~= plate.totemSpell then local aura = C_UnitAuras.GetBuffDataByIndex(plate.cachedGuid, 1)
plate.totemSpell = spellId if aura then plate.totemIcon = aura.icon end
plate.totemIcon = spellId and C_Spell.GetSpellTexture(spellId) or nil
end
return plate.totemIcon return plate.totemIcon
end end
@@ -448,7 +427,7 @@ pfUI:RegisterModule("nameplates", function ()
plate.debuffs[index].cd.SetSequenceTime = DoNothing plate.debuffs[index].cd.SetSequenceTime = DoNothing
else else
-- Use CooldownFrameTemplate for animation -- Use CooldownFrameTemplate for animation
plate.debuffs[index].cd = CreateFrame("Model", plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate") plate.debuffs[index].cd = CreateFrame(COOLDOWN_FRAME_TYPE, plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate")
plate.debuffs[index].cd:SetAllPoints(plate.debuffs[index]) plate.debuffs[index].cd:SetAllPoints(plate.debuffs[index])
plate.debuffs[index].cd:SetFrameLevel(6) plate.debuffs[index].cd:SetFrameLevel(6)
end end
@@ -505,7 +484,7 @@ local nameplates = CreateFrame("Frame", "pfNameplates", UIParent)
nameplates:RegisterEvent("PLAYER_ENTERING_WORLD") nameplates:RegisterEvent("PLAYER_ENTERING_WORLD")
nameplates:RegisterEvent("PLAYER_TARGET_CHANGED") nameplates:RegisterEvent("PLAYER_TARGET_CHANGED")
nameplates:RegisterEvent("PLAYER_LOGOUT") nameplates:RegisterEvent("PLAYER_LOGOUT")
nameplates:RegisterUnitEvent("UNIT_COMBO_POINTS", "player") nameplates:RegisterEvent("UNIT_COMBO_POINTS")
nameplates:RegisterEvent("PLAYER_COMBO_POINTS") nameplates:RegisterEvent("PLAYER_COMBO_POINTS")
nameplates:RegisterEvent("ZONE_CHANGED_NEW_AREA") nameplates:RegisterEvent("ZONE_CHANGED_NEW_AREA")
nameplates:RegisterEvent("RAID_ROSTER_UPDATE") nameplates:RegisterEvent("RAID_ROSTER_UPDATE")
@@ -513,10 +492,14 @@ nameplates:RegisterEvent("PARTY_MEMBERS_CHANGED")
nameplates:RegisterEvent("NAME_PLATE_CREATED") nameplates:RegisterEvent("NAME_PLATE_CREATED")
nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED") nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED")
nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED") nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
nameplates:RegisterEvent("UNIT_AURA")
nameplates:RegisterEvent("UNIT_FLAGS")
nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT") nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
-- UNIT_AURA / UNIT_FLAGS / UNIT_SPELLCAST_* are registered per plate, on the nameplates:RegisterEvent("UNIT_SPELLCAST_START")
-- plate's own frame, against its own token -- see OnCreate and nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
-- NAME_PLATE_UNIT_ADDED. nameplates:RegisterEvent("UNIT_SPELLCAST_STOP")
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
nameplates:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE") nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
nameplates:SetScript("OnEvent", function() nameplates:SetScript("OnEvent", function()
@@ -528,15 +511,6 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
if nameplates.mouselook then if nameplates.mouselook then
nameplates.mouselook:SetScript("OnUpdate", nil) nameplates.mouselook:SetScript("OnUpdate", nil)
end end
-- The plates hold their own unit subscriptions now, so silencing this
-- frame alone would leave them dispatching through logout -- exactly what
-- this branch exists to prevent.
for plate in pairs(registry) do
if plate.nameplate then
plate.nameplate:UnregisterAllEvents()
plate.nameplate:SetScript("OnEvent", nil)
end
end
return return
elseif event == "PLAYER_GUILD_UPDATE" and arg1 == 'player' then elseif event == "PLAYER_GUILD_UPDATE" and arg1 == 'player' then
@@ -612,19 +586,8 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
local guid = UnitGUID(arg1) local guid = UnitGUID(arg1)
plate.nameplate.cachedGuid = guid plate.nameplate.cachedGuid = guid
plate.nameplate.unit = arg1 plate.nameplate.unit = arg1
-- Point this plate's own subscriptions at the token it just took. On a
-- recycled frame these replace the previous unit rather than stacking:
-- RegisterUnitEvent on an already-filtered registration swaps the units.
plate.nameplate:RegisterUnitEvent("UNIT_AURA", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_FLAGS", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_START", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_STOP", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", arg1)
plate.nameplate.creatureType = nil -- recompute for the new unit plate.nameplate.creatureType = nil -- recompute for the new unit
plate.nameplate.totemIcon = nil plate.nameplate.totemIcon = nil
plate.nameplate.totemSpell = nil
if guid then if guid then
plateByGuid[guid] = plate.nameplate plateByGuid[guid] = plate.nameplate
-- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START -- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START
@@ -652,9 +615,14 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
if plate and plate.nameplate and plate.nameplate.cachedGuid == guid then if plate and plate.nameplate and plate.nameplate.cachedGuid == guid then
plate.nameplate.cachedGuid = nil plate.nameplate.cachedGuid = nil
plate.nameplate.unit = nil plate.nameplate.unit = nil
-- Drop the subscriptions with the token: this slot is now free and the end
-- next plate to take it would otherwise feed this frame its events. end
plate.nameplate:UnregisterAllEvents()
elseif event == "UNIT_FLAGS" then
if arg1 and strfind(arg1, "^nameplate") then
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate and plate.nameplate then
plate.nameplate.eventcache = true
end end
end end
@@ -669,6 +637,64 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
if pn then pn.eventcache = true end if pn then pn.eventcache = true end
end end
elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
-- ClassicAPI fires UNIT_SPELLCAST_* per unit token, including the caster's
-- "nameplateN". The payload has no timing, so poll it (PollCastInfo picks
-- cast vs channel) and cache -- only for a unit we have a plate for, so
-- the table stays bounded to on-screen casters.
if arg1 and strfind(arg1, "^nameplate") then
local guid = UnitGUID(arg1)
local plate = guid and plateByGuid[guid]
if plate then
castState[guid] = PollCastInfo(arg1)
if castState[guid] then
plate.castUpdate = true -- bypass the throttle so the bar shows now
end
end
end
elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then
-- Cast/channel ended (natural, interrupted, or cancelled -- the poll fires
-- STOP for all three). Clear the cached cast and refresh its plate.
if arg1 and strfind(arg1, "^nameplate") then
local guid = UnitGUID(arg1)
if guid and castState[guid] then
castState[guid] = nil
local plate = plateByGuid[guid]
if plate then plate.castUpdate = true end
end
end
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
-- Active totems (Searing/Magma/Fire Nova) carry no self-aura, so their
-- attack cast is the only icon source. Capture it once per totem, gated on
-- creature type so a normal caster's spell never styles it as a totem.
if arg1 and strfind(arg1, "^nameplate") then
local guid = UnitGUID(arg1)
local plate = guid and plateByGuid[guid]
if plate and not plate.totemIcon and arg3 and CreatureType(plate) == 11 then
local tex = C_Spell.GetSpellTexture(arg3)
if tex then
plate.totemIcon = tex
plate.castUpdate = true -- re-render now so the icon shows
end
end
end
elseif event == "UNIT_AURA" then
-- ClassicAPI: fires with arg1 == "nameplateN" when a unit's aura set
-- changes (add/remove/modify). Flag the matching plate so OnUpdate does a
-- fresh C_UnitAuras read next tick instead of waiting on the 0.5s
-- throttle -- covers expirations, dispels, refreshes, and stack changes
-- in one event. Guard on the token prefix (UNIT_AURA also fires for
-- target/party/raid).
if arg1 and strfind(arg1, "^nameplate") then
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate and plate.nameplate then
plate.nameplate.auraUpdate = true
end
end
elseif event == "PLAYER_TARGET_CHANGED" then elseif event == "PLAYER_TARGET_CHANGED" then
frameState.targetGuid = UnitGUID('target') frameState.targetGuid = UnitGUID('target')
-- Flag the target's plate for update -- Flag the target's plate for update
@@ -750,47 +776,11 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
nameplate.cache = {} nameplate.cache = {}
nameplate.original = {} nameplate.original = {}
-- Each plate watches its own unit. With RegisterUnitEvent the token IS the
-- subscription, so there is no central listener sifting every unit event in
-- the world for a "^nameplate" prefix and then resolving the plate back out
-- of arg1 -- the event arrives only at the plate it concerns, and `this` is
-- already that plate. NAME_PLATE_UNIT_ADDED points the registration at the
-- new token; _REMOVED drops it, which matters because freed slots are
-- reused and a stale token would feed this frame another unit's events.
nameplate:SetScript("OnEvent", function()
if event == "UNIT_AURA" then
-- a fresh C_UnitAuras read next tick rather than waiting out the 0.5s
-- throttle -- covers expiry, dispels, refreshes and stack changes
this.auraUpdate = true
elseif event == "UNIT_FLAGS" then
this.eventcache = true
elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
-- the payload carries no timing, so poll it (PollCastInfo picks cast
-- vs channel)
local guid = this.cachedGuid
if guid then
castState[guid] = PollCastInfo(this.unit)
if castState[guid] then
this.castUpdate = true -- bypass the throttle so the bar shows now
end
end
elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then
-- ended: natural, interrupted or cancelled -- the poll fires STOP for all
local guid = this.cachedGuid
if guid and castState[guid] then
castState[guid] = nil
this.castUpdate = true
end
end
end)
-- create shortcuts for all known elements and disable them -- create shortcuts for all known elements and disable them
nameplate.original.healthbar, nameplate.original.castbar = parent:GetChildren() nameplate.original.healthbar, nameplate.original.castbar = parent:GetChildren()
DisableObject(nameplate.original.healthbar) DisableObject(nameplate.original.healthbar)
DisableObject(nameplate.original.castbar) DisableObject(nameplate.original.castbar)
local NAMEPLATE_OBJECTORDER = { "border", "glow", "name", "level", "levelicon", "raidicon" }
for i, object in pairs({parent:GetRegions()}) do for i, object in pairs({parent:GetRegions()}) do
if NAMEPLATE_OBJECTORDER[i] and NAMEPLATE_OBJECTORDER[i] == "raidicon" then if NAMEPLATE_OBJECTORDER[i] and NAMEPLATE_OBJECTORDER[i] == "raidicon" then
nameplate[NAMEPLATE_OBJECTORDER[i]] = object nameplate[NAMEPLATE_OBJECTORDER[i]] = object
@@ -1049,29 +1039,13 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
local mouseover = plate.cachedGuid and plate.cachedGuid == frameState.mouseoverGuid or nil local mouseover = plate.cachedGuid and plate.cachedGuid == frameState.mouseoverGuid or nil
local unitstr = target and "target" or mouseover and "mouseover" or plate.cachedGuid or nil local unitstr = target and "target" or mouseover and "mouseover" or plate.cachedGuid or nil
-- target event sometimes fires too quickly, where nameplate identifiers are not
-- yet updated. So while being inside this event, we cannot trust the unitstr.
if event == "PLAYER_TARGET_CHANGED" then unitstr = nil end
-- remove unitstr when it doesn't resolve to this plate's unit (stale istarget,
-- stale mouseover guid or a despawned unit). Must run before the cache fills
-- below -- an unrelated unitstr would poison cache.player/minion with the
-- *other* unit's answer, and the nil-gate would then never recompute it.
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- resolve player vs npc from plate's own unit so libunitscan can't return -- resolve player vs npc from plate's own unit so libunitscan can't return
-- a player record for an NPC sharing the same name (e.g. Chromie). Stored -- a player record for an NPC sharing the same name (e.g. Chromie). Stored
-- as true/false/nil so it doubles as the GetUnitInfo hint. -- as true/false/nil so it doubles as the GetUnitInfo hint.
if plate.cache.player == nil and unitstr then if plate.cache.player == nil and unitstr then
plate.cache.player = UnitIsPlayer(unitstr) and true or false plate.cache.player = UnitIsPlayer(unitstr) and true or false
end end
if plate.cache.minion == nil and unitstr then local class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player)
plate.cache.minion = UnitIsMinion(unitstr) and true or false
end
local class, ulevel, elite, player, guild
if not plate.cache.minion then
class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player)
end
if plate.cache.player ~= nil then player = plate.cache.player or nil end if plate.cache.player ~= nil then player = plate.cache.player or nil end
-- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels) -- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels)
@@ -1091,11 +1065,18 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_PLAYER" end if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_PLAYER" end
if player and unittype == "FRIENDLY_NPC" then unittype = "FRIENDLY_PLAYER" end if player and unittype == "FRIENDLY_NPC" then unittype = "FRIENDLY_PLAYER" end
elite = plate.original.levelicon:IsShown() and not player and "boss" or elite elite = plate.original.levelicon:IsShown() and not player and "boss" or elite
if not class and not plate.cache.minion then plate.wait_for_scan = true end if not class then plate.wait_for_scan = true end
-- skip data updates on invisible frames -- skip data updates on invisible frames
if not visible then return end if not visible then return end
-- target event sometimes fires too quickly, where nameplate identifiers are not
-- yet updated. So while being inside this event, we cannot trust the unitstr.
if event == "PLAYER_TARGET_CHANGED" then unitstr = nil end
-- remove unitstr on unit name mismatch
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- always make sure to keep plate visible -- always make sure to keep plate visible
plate:Show() plate:Show()
@@ -1129,7 +1110,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
local TotemIcon = TotemPlate(plate) local TotemIcon = TotemPlate(plate)
if TotemIcon then if TotemIcon then
-- icon resolved from the totem-drop spell (already a full path) -- icon resolved from the totem's aura / attack cast (already a full path)
plate.totem.icon:SetTexture(TotemIcon) plate.totem.icon:SetTexture(TotemIcon)
plate.glow:Hide() plate.glow:Hide()
@@ -1333,12 +1314,11 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
if unitstr then if unitstr then
local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL" local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL"
local now = GetTime() local now = GetTime()
-- one GetAuraSlots enumeration, then positional by-slot reads straight -- positional UnitAura writes straight into the reusable buffer, so the
-- into the reusable buffer: the per-plate scan allocates nothing (no -- per-plate scan allocates nothing (no per-aura table, no result array)
-- per-aura table, no result array) and walks the aura array once local i = 1
local n = ScanAuraSlots(unitstr, filter, auraSlots, 16) while debuffCount < 16 do
for i = 1, n do local aname, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAura(unitstr, i, filter)
local aname, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
if not aname then break end if not aname then break end
debuffCount = debuffCount + 1 debuffCount = debuffCount + 1
local b = debuffDisplayBuf[debuffCount] local b = debuffDisplayBuf[debuffCount]
@@ -1348,6 +1328,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
b.dtype = dispelType b.dtype = dispelType
b.duration = duration b.duration = duration
b.timeleft = (expirationTime and expirationTime > 0) and (expirationTime - now) or nil b.timeleft = (expirationTime and expirationTime > 0) and (expirationTime - now) or nil
i = i + 1
end end
end end
for i = 1, 16 do for i = 1, 16 do
@@ -1427,16 +1408,6 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
-- cachedGuid is maintained by NAME_PLATE_UNIT_ADDED / _REMOVED events. -- cachedGuid is maintained by NAME_PLATE_UNIT_ADDED / _REMOVED events.
-- Cheap gate first. The central loop calls this for every visible plate ~100
-- times a second, and classifying the plate below costs two C calls, a cast
-- lookup and a throttle resolution -- all of it wasted on a plate that is
-- throttled to 10fps. cfg.throttle_min is the floor across every category,
-- so nothing that would have updated can be turned away here; the real
-- category-specific throttle is still applied after the classification.
-- Event flags bypass both gates, as before.
local hasEventUpdate = nameplate.eventcache or nameplate.auraUpdate or nameplate.castUpdate or nameplate.targetUpdate or nameplate.comboUpdate
if not hasEventUpdate and (nameplate.lasttick or 0) + cfg.throttle_min > now then return end
-- PERF: Intelligent throttling based on target/castbar status and plate count -- PERF: Intelligent throttling based on target/castbar status and plate count
-- Use GUID comparison as primary target detection: instant, immune to alpha transitions, -- Use GUID comparison as primary target detection: instant, immune to alpha transitions,
-- and immediately correct on de-target (unlike istarget which updates one tick later) -- and immediately correct on de-target (unlike istarget which updates one tick later)
@@ -1463,24 +1434,25 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
end end
end end
-- Resolved in CacheConfig, so these are table reads rather than a walk
-- through the saved variables and preset tables.
local throttle local throttle
if target then if target then
throttle = cfg.throttle_target throttle = pfUI.throttle:Get("nameplates_target")
elseif visiblePlateCount > 20 then elseif visiblePlateCount > 20 then
throttle = cfg.throttle_mass throttle = pfUI.throttle:Get("nameplates_mass")
else else
throttle = cfg.throttle_normal throttle = pfUI.throttle:Get("nameplates")
end end
-- Non-target plates with active castbar use the castbar throttle -- Non-target plates with active castbar use the castbar throttle
if isCastingNonTarget and cfg.throttle_castbar < throttle then if isCastingNonTarget then
throttle = cfg.throttle_castbar local cbThrottle = pfUI.throttle:Get("nameplates_castbar")
if cbThrottle < throttle then throttle = cbThrottle end
end end
-- The category-specific gate. hasEventUpdate was read above, before the -- Check for pending event updates (these bypass throttle for immediate response)
-- classification, and still bypasses the throttle. local hasEventUpdate = nameplate.eventcache or nameplate.auraUpdate or nameplate.castUpdate or nameplate.targetUpdate or nameplate.comboUpdate
-- Event updates bypass throttle
if not hasEventUpdate and (nameplate.lasttick or 0) + throttle > now then return end if not hasEventUpdate and (nameplate.lasttick or 0) + throttle > now then return end
nameplate.lasttick = now nameplate.lasttick = now
@@ -1579,7 +1551,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
-- otherwise an NPC sharing a name with a known player flips wait_for_scan -- otherwise an NPC sharing a name with a known player flips wait_for_scan
-- off here, then OnDataChanged re-sets it, every frame until the mob scan -- off here, then OnDataChanged re-sets it, every frame until the mob scan
-- lands. -- lands.
if nameplate.wait_for_scan and not nameplate.cache.minion and GetUnitInfo(name, true, nameplate.cache.player) then if nameplate.wait_for_scan and GetUnitInfo(name, true, nameplate.cache.player) then
nameplate.wait_for_scan = nil nameplate.wait_for_scan = nil
update = true update = true
end end
@@ -1682,9 +1654,10 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
-- engine framerate, decoupled from central loop). Only update non-target castbars here. -- engine framerate, decoupled from central loop). Only update non-target castbars here.
local isTargetPlate = target or nameplate.istarget or (nameplate.health and nameplate.health.zoomed) local isTargetPlate = target or nameplate.istarget or (nameplate.health and nameplate.health.zoomed)
if cfg.showcastbar and not cfg.targetcastbar and not isTargetPlate then if cfg.showcastbar and not cfg.targetcastbar and not isTargetPlate then
local cbThrottle = cfg.throttle_castbar local cbThrottle = pfUI.throttle:Get("nameplates_castbar")
if visiblePlateCount > 20 and cfg.throttle_mass > cbThrottle then if visiblePlateCount > 20 then
cbThrottle = cfg.throttle_mass local massThrottle = pfUI.throttle:Get("nameplates_mass")
if massThrottle > cbThrottle then cbThrottle = massThrottle end
end end
if (nameplate.castbar_tick or 0) + cbThrottle <= now then if (nameplate.castbar_tick or 0) + cbThrottle <= now then
nameplate.castbar_tick = now nameplate.castbar_tick = now
+5 -11
View File
@@ -457,7 +457,7 @@ pfUI:RegisterModule("panel", function()
do -- Zone do -- Zone
local widget = CreateFrame("Frame", "pfPanelWidgetZone", UIParent) local widget = CreateFrame("Frame", "pfPanelWidgetZone", UIParent)
for _,event in pairs({"PLAYER_ENTERING_WORLD", "MINIMAP_ZONE_CHANGED"}) do for _,event in pairs(EVENTS_MINIMAP_ZONE_UPDATE) do
widget:RegisterEvent(event) widget:RegisterEvent(event)
end end
widget.Tooltip = function() widget.Tooltip = function()
@@ -486,7 +486,7 @@ pfUI:RegisterModule("panel", function()
do -- Ammo do -- Ammo
local widget = CreateFrame("Frame", "pfPanelWidgetAmmo", UIParent) local widget = CreateFrame("Frame", "pfPanelWidgetAmmo", UIParent)
widget:RegisterEvent("PLAYER_ENTERING_WORLD") widget:RegisterEvent("PLAYER_ENTERING_WORLD")
widget:RegisterUnitEvent("UNIT_INVENTORY_CHANGED", "player") widget:RegisterEvent("UNIT_INVENTORY_CHANGED")
widget:RegisterEvent("BAG_UPDATE_DELAYED") widget:RegisterEvent("BAG_UPDATE_DELAYED")
widget.Tooltip = function() widget.Tooltip = function()
if GetInventoryItemQuality("player", 0) then if GetInventoryItemQuality("player", 0) then
@@ -777,15 +777,9 @@ pfUI:RegisterModule("panel", function()
pfUI.panel.microbutton:SetSize(145, 23) pfUI.panel.microbutton:SetSize(145, 23)
pfUI.panel.microbutton:SetFrameStrata("MEDIUM") pfUI.panel.microbutton:SetFrameStrata("MEDIUM")
local microButtons = { for i=1,table.getn(MICRO_BUTTONS) do
'CharacterMicroButton', 'SpellbookMicroButton', 'TalentMicroButton', local anchor = _G[MICRO_BUTTONS[i-1]] or pfUI.panel.microbutton
'QuestLogMicroButton', 'SocialsMicroButton', 'WorldMapMicroButton', local button = _G[MICRO_BUTTONS[i]]
'MainMenuMicroButton', 'HelpMicroButton',
}
for i=1,table.getn(microButtons) do
local anchor = _G[microButtons[i-1]] or pfUI.panel.microbutton
local button = _G[microButtons[i]]
button:ClearAllPoints() button:ClearAllPoints()
button:SetParent(pfUI.panel.microbutton) button:SetParent(pfUI.panel.microbutton)
if i == 1 then if i == 1 then
+1 -1
View File
@@ -81,7 +81,7 @@ pfUI:RegisterModule("socialmod", function ()
if not info or not info.name or info.name == _G.UNKNOWN then break end if not info or not info.name or info.name == _G.UNKNOWN then break end
local name = info.name local name = info.name
local friendName = _G["FriendsFrameFriendButton"..i.."ButtonTextName"] local friendName = _G["FriendsFrameFriendButton"..i.."ButtonTextName"]
local friendLoc = _G["FriendsFrameFriendButton"..i.."ButtonTextNameLocation"] local friendLoc = _G["FriendsFrameFriendButton"..i..FRIENDS_NAME_LOCATION]
local friendInfo = _G["FriendsFrameFriendButton"..i.."ButtonTextInfo"] local friendInfo = _G["FriendsFrameFriendButton"..i.."ButtonTextInfo"]
local caption = friendName or friendLoc local caption = friendName or friendLoc
+1 -1
View File
@@ -816,7 +816,7 @@ pfUI:RegisterModule("swingtimer", function ()
events:RegisterEvent("PLAYER_REGEN_DISABLED") events:RegisterEvent("PLAYER_REGEN_DISABLED")
events:RegisterEvent("PLAYER_REGEN_ENABLED") events:RegisterEvent("PLAYER_REGEN_ENABLED")
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED") events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
events:RegisterUnitEvent("UNIT_DIED", UnitGUID("player")) events:RegisterEvent("UNIT_DIED")
events:RegisterEvent("SPELL_QUEUE_EVENT") events:RegisterEvent("SPELL_QUEUE_EVENT")
events:RegisterEvent("START_AUTOATTACK") events:RegisterEvent("START_AUTOATTACK")
events:RegisterEvent("STOP_AUTOATTACK") events:RegisterEvent("STOP_AUTOATTACK")
+7 -11
View File
@@ -189,7 +189,6 @@ pfUI:RegisterModule("tooltip", function ()
-- Icons come from a ClassicAPI object pool: ReleaseAll hides the whole -- Icons come from a ClassicAPI object pool: ReleaseAll hides the whole
-- set each refresh, then we Acquire and re-anchor left-to-right. -- set each refresh, then we Acquire and re-anchor left-to-right.
local BUFF_SIZE, BUFF_SPACING, BUFF_MAX = 20, 2, 32 local BUFF_SIZE, BUFF_SPACING, BUFF_MAX = 20, 2, 32
local auraSlots = {} -- reusable GetAuraSlots buffer for UpdateBuffs
pfUI.tooltip.buffs = CreateFrame("Frame", "pfTooltipBuffs", GameTooltip) pfUI.tooltip.buffs = CreateFrame("Frame", "pfTooltipBuffs", GameTooltip)
pfUI.tooltip.buffs:SetPoint("BOTTOMLEFT", GameTooltipStatusBar, "TOPLEFT", 0, default_border + 2) pfUI.tooltip.buffs:SetPoint("BOTTOMLEFT", GameTooltipStatusBar, "TOPLEFT", 0, default_border + 2)
pfUI.tooltip.buffs:SetHeight(BUFF_SIZE) pfUI.tooltip.buffs:SetHeight(BUFF_SIZE)
@@ -240,20 +239,17 @@ pfUI:RegisterModule("tooltip", function ()
end end
local prev, count = nil, 0 local prev, count = nil, 0
-- one GetAuraSlots enumeration + positional by-slot reads: no per-aura for i = 1, BUFF_MAX do
-- table and no per-index re-walk of the aura array local aura = C_UnitAuras.GetBuffDataByIndex(unit, i)
local n = ScanAuraSlots(unit, "HELPFUL", auraSlots, BUFF_MAX) if not aura then break end
for i = 1, n do
local name, texture, applications, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, auraSlots[i])
if not name then break end
count = count + 1 count = count + 1
local icon = pfUI.tooltip.buffpool:Acquire() local icon = pfUI.tooltip.buffpool:Acquire()
icon.texture:SetTexture(texture) icon.texture:SetTexture(aura.icon)
icon.stacks:SetText(applications and applications > 1 and applications or "") icon.stacks:SetText(aura.applications and aura.applications > 1 and aura.applications or "")
icon.expirationTime = expirationTime icon.expirationTime = aura.expirationTime
local timeleft = expirationTime and expirationTime > 0 and (expirationTime - GetTime()) or 0 local timeleft = aura.expirationTime and aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
icon.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "") icon.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
if prev then if prev then
+1 -1
View File
@@ -113,7 +113,7 @@ pfUI:RegisterModule("totems", function ()
self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i]) self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i])
self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3) self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3)
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0) self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
self.bar[i].cd = self.bar[i].cd or CreateFrame("Model", "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate") self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
self.bar[i].cd.pfCooldownStyleAnimation = 1 self.bar[i].cd.pfCooldownStyleAnimation = 1
self.bar[i].cd.pfCooldownType = "ALL" self.bar[i].cd.pfCooldownType = "ALL"
+2 -2
View File
@@ -1,7 +1,7 @@
pfUI:RegisterModule("tracking", function () pfUI:RegisterModule("tracking", function ()
_G.MiniMapTrackingFrame:UnregisterAllEvents() MINIMAP_TRACKING_FRAME:UnregisterAllEvents()
_G.MiniMapTrackingFrame:Hide() MINIMAP_TRACKING_FRAME:Hide()
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local size = tonumber(C.appearance.minimap.tracking_size) local size = tonumber(C.appearance.minimap.tracking_size)
+2958 -1
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -363,9 +363,9 @@ end
b:EnableMouse(true) b:EnableMouse(true)
b:RegisterEvent("FACTION_STANDING_CHANGED") b:RegisterEvent("FACTION_STANDING_CHANGED")
b:RegisterUnitEvent("UNIT_PET", "player") b:RegisterEvent("UNIT_PET")
b:RegisterUnitEvent("UNIT_LEVEL", "player") b:RegisterEvent("UNIT_LEVEL")
b:RegisterUnitEvent("UNIT_PET_EXPERIENCE", "player", "pet") b:RegisterEvent("UNIT_PET_EXPERIENCE")
b:RegisterEvent("PLAYER_ENTERING_WORLD") b:RegisterEvent("PLAYER_ENTERING_WORLD")
b:RegisterEvent("UPDATE_EXHAUSTION") b:RegisterEvent("UPDATE_EXHAUSTION")
b:RegisterEvent("PLAYER_XP_UPDATE") b:RegisterEvent("PLAYER_XP_UPDATE")
+102 -37
View File
@@ -3,8 +3,6 @@ function SlashCmdList.RELOAD(msg, editbox)
ReloadUI() ReloadUI()
end end
local addonName = ...
SLASH_PFUI1 = '/pfui' SLASH_PFUI1 = '/pfui'
function SlashCmdList.PFUI(msg, editbox) function SlashCmdList.PFUI(msg, editbox)
pfUI.gui:SetShown(not pfUI.gui:IsShown()) pfUI.gui:SetShown(not pfUI.gui:IsShown())
@@ -15,19 +13,87 @@ function SlashCmdList.GM(msg, editbox)
ToggleHelpFrame(1) ToggleHelpFrame(1)
end end
local pfUI = CreateFrame("Frame", addonName, UIParent) pfUI = CreateFrame("Frame", nil, UIParent)
pfUI:RegisterEvent("ADDON_LOADED") pfUI:RegisterEvent("ADDON_LOADED")
-- setup bootvar -- setup bootvar
pfUI.bootup = true pfUI.bootup = true
pfUI_playerDB = pfUI_playerDB or {} do
pfUI_config = pfUI_config or {} -- ClassicAPI dependency check.
pfUI_init = pfUI_init or {} -- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
pfUI_profiles = pfUI_profiles or {} -- API surface that ClassicAPI polyfills, so presence is required.
pfUI_addon_profiles = pfUI_addon_profiles or {} local PFUI_CLASSIC_API_MIN = 11204 -- (X*10000 + Y*100 + Z)
pfUI_cache = pfUI_cache or {} local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
pfUI_throttle = pfUI_throttle or {} local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
local function FormatVersion(packed)
local x = math.floor(packed / 10000)
local y = math.floor(math.mod(packed, 10000) / 100)
local z = math.mod(packed, 100)
return string.format("v%d.%d.%d", x, y, z)
end
if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN)
pfUI.disabled = true
local detail
if not CLASSIC_API_VERSION then
detail = "The ClassicAPI DLL isn't loaded. The |cff33ffcc!!!ClassicAPI|r addon ships bundled with it -- delete your |cff33ffcc!!!ClassicAPI|r folder and install the latest release from:"
else
detail = "ClassicAPI " .. minVersion .. " or newer is required. Delete your |cff33ffcc!!!ClassicAPI|r folder and reinstall the latest release from:"
end
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = "|cff33ffccpf|cffffffffUI|r has been disabled.\n\n" .. detail,
button1 = OKAY,
hasEditBox = 1,
editBoxWidth = 280,
timeout = 0,
whileDead = 1,
hideOnEscape = 1,
preferredIndex = 3,
OnShow = function()
local editBox = getglobal(this:GetName().."EditBox")
if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText()
editBox:SetFocus()
end
end,
}
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|cffffffffUI|r disabled: " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3
)
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function()
C_Timer.After(8, function()
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|rUI: ClassicAPI " .. FormatVersion(PFUI_CLASSIC_API_LATEST) .. " is available — " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.85, 0.3
)
end)
end)
end
end
-- initialize saved variables
pfUI_playerDB = {}
pfUI_config = {}
pfUI_init = {}
pfUI_profiles = {}
pfUI_addon_profiles = {}
pfUI_cache = {}
pfUI_throttle = {}
-- localization -- localization
pfUI_locale = {} pfUI_locale = {}
@@ -52,24 +118,35 @@ pfUI.env = {}
-- the flag; one that lacks it should drop the flag and get the safe fallback. -- the flag; one that lacks it should drop the flag and get the safe fallback.
pfUI.handlesHookScript = true pfUI.handlesHookScript = true
pfUI.events = Mixin({}, CallbackRegistryMixin) if not pfUI.disabled then
pfUI.events:OnLoad() pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:SetUndefinedEventsAllowed(true) pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
end
-- check if macro addons are loaded (disables macrotweak/macroscan) -- check if macro addons are loaded (disables macrotweak/macroscan)
function pfUI:MacroAddonsLoaded() function pfUI:MacroAddonsLoaded()
return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros") return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros")
end end
pfUI.name = addonName -- detect current addon path
pfUI.path = "Interface\\AddOns\\" .. addonName local tocs = { "", "-master", "-tbc", "-wotlk" }
for _, name in pairs(tocs) do
local current = string.format("pfUI%s", name)
local title = C_AddOns.GetAddOnName(current)
if title then
pfUI.name = current
pfUI.path = "Interface\\AddOns\\" .. current
break
end
end
-- handle/convert media dir paths -- handle/convert media dir paths
pfUI.media = setmetatable({}, { __index = function(tab,key) pfUI.media = setmetatable({}, { __index = function(tab,key)
local value = tostring(key) local value = tostring(key)
if value:find("img:") then if strfind(value, "img:") then
value = string.gsub(value, "img:", pfUI.path .. "\\img\\") value = string.gsub(value, "img:", pfUI.path .. "\\img\\")
elseif value:find("font:") then elseif strfind(value, "font:") then
value = string.gsub(value, "font:", pfUI.path .. "\\fonts\\") value = string.gsub(value, "font:", pfUI.path .. "\\fonts\\")
else else
value = string.gsub(value, "Interface\\AddOns\\pfUI\\", pfUI.path .. "\\") value = string.gsub(value, "Interface\\AddOns\\pfUI\\", pfUI.path .. "\\")
@@ -78,7 +155,9 @@ pfUI.media = setmetatable({}, { __index = function(tab,key)
return value return value
end}) end})
pfUI.client = INTERFACE_VERSION -- cache client version
local _, _, _, client = GetBuildInfo()
pfUI.client = client or 11200
-- setup pfUI namespace -- setup pfUI namespace
setmetatable(pfUI.env, {__index = getfenv(0)}) setmetatable(pfUI.env, {__index = getfenv(0)})
@@ -312,13 +391,14 @@ function pfUI:CheckNewModules()
end end
local function BackwardsCompatRegister(func, arg3) local function BackwardsCompatRegister(func, arg3)
if arg3 and type(func) == "string" and type(arg3) == "function" and func:find("vanilla") then if arg3 and type(func) == "string" and type(arg3) == "function" and string.find(func, "vanilla") then
return arg3 return arg3
end end
return func return func
end end
function pfUI:RegisterModule(name, func, arg3) function pfUI:RegisterModule(name, func, arg3)
if pfUI.disabled then return end
if pfUI.module[name] then return end if pfUI.module[name] then return end
func = BackwardsCompatRegister(func, arg3) func = BackwardsCompatRegister(func, arg3)
pfUI.module[name] = func pfUI.module[name] = func
@@ -329,6 +409,7 @@ function pfUI:RegisterModule(name, func, arg3)
end end
function pfUI:RegisterSkin(name, func, arg3) function pfUI:RegisterSkin(name, func, arg3)
if pfUI.disabled then return end
if pfUI.skin[name] then return end if pfUI.skin[name] then return end
func = BackwardsCompatRegister(func, arg3) func = BackwardsCompatRegister(func, arg3)
pfUI.skin[name] = func pfUI.skin[name] = func
@@ -349,6 +430,7 @@ function pfUI:LoadSkin(s)
end end
pfUI:SetScript("OnEvent", function() pfUI:SetScript("OnEvent", function()
if pfUI.disabled then return end
-- make sure to initialize and set our fonts -- make sure to initialize and set our fonts
-- each time an addon got loaded but only -- each time an addon got loaded but only
@@ -362,7 +444,7 @@ pfUI:SetScript("OnEvent", function()
-- "@project-version@" until release tooling substitutes it — mark those -- "@project-version@" until release tooling substitutes it — mark those
-- explicitly as "dev" instead of pretending they're a real numbered build. -- explicitly as "dev" instead of pretending they're a real numbered build.
local raw = tostring(GetAddOnMetadata(pfUI.name, "Version")) local raw = tostring(GetAddOnMetadata(pfUI.name, "Version"))
if raw:find("@") then if strfind(raw, "@") then
pfUI.version.major, pfUI.version.minor, pfUI.version.fix = 0, 0, 0 pfUI.version.major, pfUI.version.minor, pfUI.version.fix = 0, 0, 0
pfUI.version.string = "dev" pfUI.version.string = "dev"
else else
@@ -496,20 +578,3 @@ function pfUI.SetupCVars()
end end
UIParentLoadAddOn("Blizzard_CombatText") UIParentLoadAddOn("Blizzard_CombatText")
end end
do -- RunMacroText
local obj = setmetatable({ ["GetText"] = function(self) return self.text end }, {
__index = function(tab,key)
local value = function() return end
rawset(tab,key,value)
return value
end
})
function RunMacroText(text)
obj.text = text
ChatEdit_ParseText(obj, 1)
end
end
_G.PLAYER_BUFF_START_ID = -1
+7 -5
View File
@@ -8,9 +8,11 @@
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB ## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
## X-Website: https://github.com/brues-code/pfUI ## X-Website: https://github.com/brues-code/pfUI
# ClassicAPI-less fallback. ClassicAPI redirects this read to pfUI_Turtle.toc pfUI.lua
# (Turtle clients) or pfUI_ClassicAPI.toc (everything else), so the client only
# ever reaches THIS file when the DLL is missing. Load the notice and nothing
# else.
API_Check.lua init\env.xml
init\compat.xml
init\api.xml
init\libs.xml
init\skins.xml
init\modules.xml
-23
View File
@@ -1,23 +0,0 @@
## Interface: 11200
## Title: |cff33ffccpf|cffffffffUI
## Author: Shagu - modified by me0wg4ming & brues
## Notes: A complete user interface replacement.
## Notes-ruRU: Полная замена пользовательского интерфейса.
## Version: @project-version@
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
## X-Website: https://github.com/brues-code/pfUI
# Selected by ClassicAPI on every non-Turtle client. Turtle clients get
# pfUI_Turtle.toc instead, which trades init\stock.xml for init\turtle.xml.
API_Check.lua
pfUI.lua
init\env.xml
init\stock.xml
init\api.xml
init\libs.xml
init\skins.xml
init\modules.xml
-24
View File
@@ -1,24 +0,0 @@
## Interface: 11200
## Title: |cff33ffccpf|cffffffffUI
## Author: Shagu - modified by me0wg4ming & brues
## Notes: A complete user interface replacement.
## Notes-ruRU: Полная замена пользовательского интерфейса.
## Version: @project-version@
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
## X-Website: https://github.com/brues-code/pfUI
# Selected by ClassicAPI on Turtle WoW, where it wins over pfUI_ClassicAPI.toc.
# Same list with init\stock.xml swapped for init\turtle.xml: the modules, skins
# and vendor prices that only mean anything on a Turtle client.
API_Check.lua
pfUI.lua
init\env.xml
init\api.xml
init\libs.xml
init\skins.xml
init\modules.xml
init\turtle.xml