18 Commits
Author SHA1 Message Date
Brues 65fcba6698 Gracefully disable pfUI when ClassicAPI is missing
Add API_Check.lua as the first TOC entry. When the ClassicAPI DLL is
absent or below the minimum version it sets pfUI_disabled and stands up
an inert stub so the rest of the addon no-ops instead of flooding load
errors: modules and skins register their bodies into no-ops, and the
setfenv'd api/lib files run inside an environment where CreateFrame and
any missing global resolve to a null object -- so no real frames or live
handlers are created and missing API calls just return null. pfUI.lua
bails early on pfUI_disabled.
2026-08-31 16:58:42 -05:00
Brues bee1aa9bdd CAPI min bumped to 1.13.1 2026-08-31 16:44:54 -05:00
Brues 60f2c22a6c nameplates: source totem icons from UnitCreatedBySpell
Read the totem's icon from the totem-drop spell (UnitCreatedBySpell +
GetSpellTexture) instead of the self-aura at index 1 plus a
UNIT_SPELLCAST_SUCCEEDED capture for active totems. The drop spell is a
broadcast descriptor field present for every summoned unit in range, so
it resolves immediately for passive and active totems alike and gives the
totem's own icon rather than the attack-spell proxy. Drops the
UNIT_SPELLCAST_SUCCEEDED registration and handler.

Re-read the spell each render and key the cached texture on the spell id
so an in-place totem swap (same unit, new drop spell -- no plate re-add)
refreshes the icon without needing the plate to leave and re-enter view.
2026-08-31 16:44:54 -05:00
Brues 2b5288a688 nameplates: don't run GetUnitInfo for minions 2026-08-31 16:43:38 -05:00
Brues daba4e202e Default saved variables with 'or {}' instead of overwriting
Seed the SavedVariables globals only when absent (X = X or {}) rather
than unconditionally assigning {}. Behaviorally identical under the
current load order -- a returning character's data is restored over these
between file load and ADDON_LOADED -- but expresses intent as a default
and won't clobber data if that timing ever changed.
2026-08-31 12:04:00 -05:00
Brues 98c7751416 Utilize CAPI Tracking functions
commit 8ea5c81789
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Mon Aug 24 23:40:01 2026 -0500

    bump CAPI min to 1.12.4

commit 39a46563a1
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Mon Aug 24 23:35:49 2026 -0500

    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.

Fixes #42
2026-08-30 17:19:17 -05:00
Brues 8796fab6d8 libpredict: predict incoming heals for built-in click casting (#44)
Built-in click casting drives the cast through secure frame attributes,
bypassing the CastSpell/CastSpellByName/UseAction hooks that populate
spell_queue -- so the heal-prediction lookup in the SPELL_START_SELF
handler missed and no incoming heal showed.

Populate spell_queue from the cast's own rank-specific spellId (via
C_Spell.GetSpellName/GetSpellSubtext) right before the cache lookup, so
prediction works regardless of how the cast was initiated. Deriving the
rank from the actual spellId also fixes stale-rank lookups when a prior
keybind cast left a different rank of the same spell queued.
2026-08-24 21:37:41 -05:00
brues-code 7ca0e40d8b CAPI needs 1.12.1 for UnitAura changes 2026-08-24 12:05:18 -05:00
brues-code 2f93b246a1 Accidentally bumped ClassicAPI to the wrong version 2026-08-24 11:48:54 -05:00
Brues c647698f8f bump CAPI min 2026-08-24 11:07:38 -05:00
Brues 48026008b6 buffwatch: use official dispel-type colours for debuff bars
The debuff-bar auto colour read the vanilla DebuffTypeColor global,
diverging from unitframes/buff which use C_UnitAuras.GetAuraDispelTypeColor
(the BC GlobalColor.dbc palette ClassicAPI ships). Switch buffwatch to the
same accessor so all aura displays share one palette; nil/unknown types
fall back to DEBUFF_TYPE_NONE instead of the old ad-hoc (1, .2, .2).
2026-08-24 11:07:38 -05:00
Brues c1d96bae58 buff/buffwatch: positional aura reads in hot refresh loops
buff.lua RefreshBuffButton runs 50x per PLAYER_AURAS_CHANGED and
buffwatch.lua GetBuffData runs 32x per RefreshBuffBarFrame; both now use
the zero-allocation positional C_UnitAuras.UnitAura instead of the
table-allocating GetAuraDataByIndex.

buffwatch also threads the aura's dispelType out of GetBuffData onto the
frame.buffs row, so the debuff-bar colour reads it from the row instead
of re-fetching the aura with GetDebuffDataByIndex on every name change.

Cold single-lookups (hover/click tooltips) keep the readable table API.
2026-08-24 11:07:38 -05:00
Brues 1d44c5ec6f unitframes: scan debuff slots once for dispel indicators
The dispellable-indicator block re-scanned all 16 harmful slots for
every dispellable type (O(types x 16) UnitDebuff calls per refresh).
Scan the 16 slots once into a reusable set, then have the per-type loop
check the set. Zero-allocation (the set is cleared and reused within the
single RefreshUnit call).
2026-08-24 11:07:38 -05:00
Brues 8f88dfd852 Use positional C_UnitAuras accessors in hot aura scans
Swap the per-frame aura-refresh loops from table-allocating
GetAuraDataByIndex/GetBuffDataByIndex/GetDebuffDataByIndex/GetUnitAuras
to the zero-allocation positional C_UnitAuras.UnitAura/UnitBuff/UnitDebuff.

unitframes: buff/debuff icon loops, the dispellable-indicator 16-slot
scan, the two GetUnitAuras indicator passes, and the custom-debuff scan.
nameplates: the per-plate debuff scan now writes positional results
straight into the reusable display buffer, dropping the per-aura tables
and the result array.

Filters change from HARMFUL|PLAYER/HARMFUL to PLAYER/nil since UnitBuff/
UnitDebuff lock the range and still honor the PLAYER predicate. Cold
single-lookups (hover/click, tooltip, totem icon, libdebuff) keep the
readable table API.
2026-08-24 11:07:37 -05:00
Brues a57214aeb0 Bump ClassicAPI minimum version 2026-08-24 11:07:37 -05:00
Brues 9bde843e33 utlize inline icons and CreateGoldString in repair module 2026-08-24 11:07:37 -05:00
Brues ad13e6ace3 Utilize SetFormattedText
This change updates several text setters to use `SetFormattedText` instead of passing `string.format(...)` into `SetText`. It also switches a few regex captures from `string.find` to `string.match` for cleaner capture handling, and replaces explicit width/height setters with `SetSize` in the first-run dialog for consistency.
2026-08-24 11:07:37 -05:00
Brues 82c74c2db5 Skip declaring pfSellData on turtle client
This giant table is just going to be immediately replaced so there's no point in declaring it.
2026-08-21 14:59:19 -05:00
23 changed files with 339 additions and 296 deletions
+104
View File
@@ -0,0 +1,104 @@
do
-- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 11301 -- (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
if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN)
pfUI_disabled = true
pfUI_locale = {}
pfUI_profiles = {}
pfUI_translation = {}
-- ClassicAPI is required. Rather than let the ~140 files that follow flood
-- errors as they load, stand up an inert stub so everything no-ops:
-- * modules/skins register their body into a no-op, so it never runs;
-- * the setfenv'd api/lib files run inside `env`, where CreateFrame and
-- any missing global (C_*, SuperWoW, ...) resolve to a null object --
-- so no real frames or live OnUpdate/OnEvent handlers get created and
-- missing API calls just return null instead of erroring.
local null -- forward declaration so the metamethods can return it
local nullmt = {
__index = function() return null end,
__newindex = function() end,
__call = function() return null end,
__concat = function() return "" end,
__tostring = function() return "" end,
}
for _, op in ipairs({ "__add","__sub","__mul","__div","__mod","__pow","__unm" }) do
nullmt[op] = function() return null end
end
for _, op in ipairs({ "__lt","__le","__eq" }) do nullmt[op] = function() return false end end
null = setmetatable({}, nullmt)
local realG = getfenv(0)
pfUI = setmetatable({}, { __index = function() return null end })
pfUI.env = setmetatable({}, {
__index = function(_, k)
if k == "CreateFrame" then return function() return null end end
local v = realG[k]
if v ~= nil then return v end
return null
end,
})
function pfUI:RegisterModule(_, _, _) end
function pfUI:RegisterSkin(_, _, _) end
function pfUI:GetEnvironment() return pfUI.env end
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
+66 -53
View File
@@ -1638,39 +1638,40 @@ function pfUI.uf:RefreshUnit(unit, component)
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 aura = C_UnitAuras.GetBuffDataByIndex(unitstr, 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 aura then if name then
unit.buffs[i].texture:SetTexture(aura.icon) unit.buffs[i].texture:SetTexture(icon)
unit.buffs[i]:Show() unit.buffs[i]:Show()
if aura.applications > 1 then if count > 1 then
unit.buffs[i].stacks:SetText(aura.applications) unit.buffs[i].stacks:SetText(count)
else else
unit.buffs[i].stacks:SetText("") unit.buffs[i].stacks:SetText("")
end end
if aura.expirationTime > 0 then if expirationTime > 0 then
-- pfUI's cooldown text falls into a 2^32 wraparound branch when start > GetTime(), -- pfUI's cooldown text falls into a 2^32 wraparound branch when start > GetTime(),
-- which happens for talent-extended buffs where the real duration exceeds aura.duration -- which happens for talent-extended buffs where the real duration exceeds the
-- (the Spell.dbc base). Anchor start to now in that case to keep the remaining math sane. -- Spell.dbc base. Anchor start to now in that case to keep the remaining math sane.
local now = GetTime() local now = GetTime()
local start = aura.expirationTime - aura.duration local start = expirationTime - duration
local duration = aura.duration local dur = duration
if start > now or duration <= 0 then if start > now or dur <= 0 then
start, duration = now, aura.expirationTime - now start, dur = now, expirationTime - now
end end
if duration > 0 then if dur > 0 then
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1) CooldownFrame_SetTimer(unit.buffs[i].cd, start, dur, 1)
else else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
elseif aura.duration > 0 then elseif duration > 0 then
local guid = UnitGUID(unitstr) local guid = UnitGUID(unitstr)
local guidStarts = guid and pfUI.uf.aura_starts[guid] local guidStarts = guid and pfUI.uf.aura_starts[guid]
local start = guidStarts and guidStarts[aura.spellId] local start = guidStarts and guidStarts[spellId]
if start then if start then
CooldownFrame_SetTimer(unit.buffs[i].cd, start, aura.duration, 1) CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
else else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
@@ -1741,10 +1742,11 @@ function pfUI.uf:RefreshUnit(unit, component)
-- selfdebuff narrows to player-cast harmful auras via the PLAYER filter. -- 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 -- Player-frame debuffs aren't gated on it (it'd hide most party-applied
-- effects on you). -- effects on you).
local filter = (unit.label ~= "player" and selfdebuff == "1") and "HARMFUL|PLAYER" or "HARMFUL" -- positional UnitDebuff allocates nothing; PLAYER predicate honored for selfdebuff
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, filter) local filter = (unit.label ~= "player" and selfdebuff == "1") and "PLAYER" or nil
if aura then local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitDebuff(unitstr, i, filter)
texture, stacks, dtype = aura.icon, aura.applications, aura.dispelName if name then
texture, stacks, dtype = icon, count, dispelType
else else
texture, stacks, dtype = nil, 0, nil texture, stacks, dtype = nil, 0, nil
end end
@@ -1757,18 +1759,18 @@ function pfUI.uf:RefreshUnit(unit, component)
if texture then if texture then
unit.debuffs[i]:Show() unit.debuffs[i]:Show()
if aura and aura.expirationTime > 0 then if expirationTime > 0 then
-- Cap start to now so talent-extended debuffs (expirationTime past -- Cap start to now so talent-extended debuffs (expirationTime past
-- the dbc base duration) don't push start into the future and trip -- the dbc base duration) don't push start into the future and trip
-- CooldownFrame_SetTimer's 2^32-wraparound branch. -- CooldownFrame_SetTimer's 2^32-wraparound branch.
local now = GetTime() local now = GetTime()
local start = aura.expirationTime - aura.duration local start = expirationTime - duration
local duration = aura.duration local dur = duration
if start > now or duration <= 0 then if start > now or dur <= 0 then
start, duration = now, aura.expirationTime - now start, dur = now, expirationTime - now
end end
if duration > 0 then if dur > 0 then
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1) CooldownFrame_SetTimer(unit.debuffs[i].cd, start, dur, 1)
else else
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
end end
@@ -1826,6 +1828,17 @@ function pfUI.uf:RefreshUnit(unit, component)
end end
end end
-- Scan the 16 harmful slots once into a reusable set, instead of
-- re-scanning all 16 for every dispellable type below. Reused across
-- frames (populated and consumed within this single RefreshUnit call).
local present = pfUI.uf.dispelPresent or {}
pfUI.uf.dispelPresent = present
for k in pairs(present) do present[k] = nil end
for i=1,16 do
local name, _, _, dispelType = C_UnitAuras.UnitDebuff(unitstr, i)
if name and dispelType and dispelType ~= "" then present[dispelType] = true end
end
for _, debuff in pairs(unit.dispellable) do for _, debuff in pairs(unit.dispellable) do
indicator[debuff] = indicator[debuff] or CreateFrame("Frame", nil, indicator) indicator[debuff] = indicator[debuff] or CreateFrame("Frame", nil, indicator)
indicator[debuff]:SetParent(indicator) indicator[debuff]:SetParent(indicator)
@@ -1865,15 +1878,7 @@ function pfUI.uf:RefreshUnit(unit, component)
indicator[debuff].disp = indicator.disp indicator[debuff].disp = indicator.disp
end end
indicator[debuff].visible = nil indicator[debuff].visible = present[debuff]
for i=1,16 do
local a = C_UnitAuras.GetDebuffDataByIndex(unitstr, i)
local dtype = a and a.dispelName
if dtype == debuff then
indicator[debuff].visible = true
end
end
if indicator[debuff].visible then if indicator[debuff].visible then
indicator[debuff]:Show() indicator[debuff]:Show()
@@ -1920,50 +1925,58 @@ 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
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do local i = 1
local texLower = string.lower(aura.icon) while true do
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitBuff(unitstr, i)
if not name then break end
local texLower = string.lower(icon)
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 == texLower then if filter == texLower then
local hot = HOT_INDICATORS[texLower] local hot = HOT_INDICATORS[texLower]
if hot and string.lower(aura.name) ~= hot.name then if hot and string.lower(name) ~= hot.name then
break -- texture matches but name disambiguates (e.g. shared icon) break -- texture matches but name disambiguates (e.g. shared icon)
end end
if hot then if hot then
local start, duration, prediction = libpredict:GetHotDuration(unitstr, hot.predict) local start, duration, prediction = libpredict:GetHotDuration(unitstr, hot.predict)
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft or prediction, aura.applications, 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, aura.icon, timeleft, aura.applications) pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
end end
pos = pos + 1 pos = pos + 1
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
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do local ai = 1
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil while true do
local lowerName = string.lower(aura.name) local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitBuff(unitstr, ai)
if not name then break end
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
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, aura.icon, timeleft, aura.applications) pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
pos = pos + 1 pos = pos + 1
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
for i=1,16 do -- scan for custom debuffs for i=1,16 do -- scan for custom debuffs
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, debuffFilter) local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitDebuff(unitstr, i, debuffFilter)
if aura then if name then
local timeleft = aura.expirationTime > 0 and (aura.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 == string.lower(aura.name) then if filter == string.lower(name) then
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications) pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
pos = pos + 1 pos = pos + 1
break break
end end
+6
View File
@@ -63,6 +63,12 @@ for i=1,40 do
pfValidUnits["nameplate" .. i .. "target"] = true pfValidUnits["nameplate" .. i .. "target"] = true
end end
if TURTLE_WOW_VERSION then -- We already know this table is going to be replaced by turtle-wow.lua, so there's no point in declaring it.
pfSellData = {} -- create empty just incase user has turtle-wow module disabled.
return
end
-- This table makes me sick.
-- itemid = buyPrice (copper). Sell price comes from C_Item.GetItemSellPriceByID; -- itemid = buyPrice (copper). Sell price comes from C_Item.GetItemSellPriceByID;
-- this table only carries the curated vendor-buy price (which isn't a static -- this table only carries the curated vendor-buy price (which isn't a static
-- engine field — it's collected from real vendor encounters). -- engine field — it's collected from real vendor encounters).
+3
View File
@@ -168,6 +168,9 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
return return
end end
spell_queue[1] = spellName
spell_queue[2] = spellName .. (C_Spell.GetSpellSubtext(spellId) or "")
if spell_queue[1] == spellName and cache[spell_queue[2]] then if spell_queue[1] == spellName and cache[spell_queue[2]] then
local amount = cache[spell_queue[2]][1] local amount = cache[spell_queue[2]][1]
local casttime = castTime local casttime = castTime
+7 -7
View File
@@ -23,7 +23,7 @@ pfUI:RegisterModule("buff", function ()
CreateBackdropShadow(buff) CreateBackdropShadow(buff)
end end
local aura = C_UnitAuras.GetAuraDataByIndex("player", buff.id, buff.btype) local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAura("player", buff.id, buff.btype)
--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
@@ -55,16 +55,16 @@ pfUI:RegisterModule("buff", function ()
buff.texture:SetTexture(GetInventoryItemTexture("player", 17)) buff.texture:SetTexture(GetInventoryItemTexture("player", 17))
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1)) buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1))
end end
elseif aura and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then elseif name and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then
-- Set Buff Texture and Border -- Set Buff Texture and Border
buff.mode = buff.btype buff.mode = buff.btype
buff.expirationTime = aura.expirationTime buff.expirationTime = expirationTime
buff.stackCount = aura.applications buff.stackCount = count
buff.spellId = aura.spellId buff.spellId = spellId
buff.texture:SetTexture(aura.icon) buff.texture:SetTexture(icon)
if buff.btype == "HARMFUL" then if buff.btype == "HARMFUL" then
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(aura.dispelName) local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType)
buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA()) buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
else else
buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba) buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba)
+10 -11
View File
@@ -75,10 +75,10 @@ pfUI:RegisterModule("buffwatch", function ()
local function GetBuffData(unit, id, type, selfdebuff) local function GetBuffData(unit, id, type, selfdebuff)
local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type
local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, filter) local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAura(unit, id, filter)
if not aura then return end if not name then return end
local remaining = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0 local remaining = expirationTime > 0 and (expirationTime - GetTime()) or 0
return remaining, aura.icon, aura.name, aura.applications return remaining, icon, name, count, dispelType
end end
local function StatusBarOnClick() local function StatusBarOnClick()
@@ -236,7 +236,7 @@ pfUI:RegisterModule("buffwatch", function ()
local selfdebuff = frame.config.selfdebuff == "1" local selfdebuff = frame.config.selfdebuff == "1"
for i=1,32 do for i=1,32 do
local timeleft, texture, name, stacks = GetBuffData(frame.unit, i, frame.type, selfdebuff) 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
@@ -245,12 +245,14 @@ pfUI:RegisterModule("buffwatch", function ()
frame.buffs[i][3] = name frame.buffs[i][3] = name
frame.buffs[i][4] = texture frame.buffs[i][4] = texture
frame.buffs[i][5] = stacks frame.buffs[i][5] = stacks
frame.buffs[i][6] = dtype
else else
frame.buffs[i][1] = 0 frame.buffs[i][1] = 0
frame.buffs[i][2] = nil frame.buffs[i][2] = nil
frame.buffs[i][3] = nil frame.buffs[i][3] = nil
frame.buffs[i][4] = nil frame.buffs[i][4] = nil
frame.buffs[i][5] = 0 frame.buffs[i][5] = 0
frame.buffs[i][6] = nil
end end
end end
@@ -294,12 +296,9 @@ pfUI:RegisterModule("buffwatch", function ()
-- calculate dynamic auto color -- calculate dynamic auto color
local r, g, b local r, g, b
if frame.type == "HARMFUL" then if frame.type == "HARMFUL" then
r, g, b = 1, .2, .2 -- official Blizzard dispel-type colors (matches unitframes/buff);
local a = C_UnitAuras.GetDebuffDataByIndex(frame.unit, data[2]) -- nil/unknown type falls back to the DEBUFF_TYPE_NONE colour
local dtype = a and a.dispelName r, g, b = C_UnitAuras.GetAuraDispelTypeColor(data[6] or ""):GetRGBA()
if dtype and DebuffTypeColor[dtype] then
r,g,b = DebuffTypeColor[dtype].r,DebuffTypeColor[dtype].g,DebuffTypeColor[dtype].b
end
else else
r,g,b = str2rgb(data[3]) r,g,b = str2rgb(data[3])
end end
+1 -1
View File
@@ -45,7 +45,7 @@ pfUI:RegisterModule("castbar", function ()
if not cb.tradeskillTotal or not cb.activeName or not cb.showname then return end if not cb.tradeskillTotal or not cb.activeName or not cb.showname then return end
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0) local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
if remaining > 1 then if remaining > 1 then
cb.bar.left:SetText(string.format("%s (%d)", cb.activeName, remaining)) cb.bar.left:SetFormattedText("%s (%d)", cb.activeName, remaining)
else else
cb.bar.left:SetText(cb.activeName) cb.bar.left:SetText(cb.activeName)
end end
+6 -11
View File
@@ -127,7 +127,7 @@ pfUI:RegisterModule("firstrun", function ()
-- welcome dialog -- welcome dialog
pfUI.firstrun:AddStep("init", function() pfUI.firstrun:AddStep("init", function()
local f = CreateFirstRunPage() local f = CreateFirstRunPage()
f.text:SetText(string.format(T["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."], GetAddOnMetadata(pfUI.name, "X-Website"))) f.text:SetFormattedText(T["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."], GetAddOnMetadata(pfUI.name, "X-Website"))
return f return f
end) end)
@@ -137,8 +137,7 @@ pfUI:RegisterModule("firstrun", function ()
f.text:SetText(T["A new installation of |cff33ffccpf|rUI ships with 4 prebuilt design profiles. Click below if you wish to load one of these profiles."]) f.text:SetText(T["A new installation of |cff33ffccpf|rUI ships with 4 prebuilt design profiles. Click below if you wish to load one of these profiles."])
f.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Modern:SetWidth(120) f.Modern:SetSize(120, 20)
f.Modern:SetHeight(20)
f.Modern:SetPoint("BOTTOM", -65, 100) f.Modern:SetPoint("BOTTOM", -65, 100)
f.Modern:SetTextColor(1,1,1) f.Modern:SetTextColor(1,1,1)
f.Modern:SetText("Modern") f.Modern:SetText("Modern")
@@ -151,8 +150,7 @@ pfUI:RegisterModule("firstrun", function ()
SkinButton(f.Modern) SkinButton(f.Modern)
f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Nostalgia:SetWidth(120) f.Nostalgia:SetSize(120, 20)
f.Nostalgia:SetHeight(20)
f.Nostalgia:SetPoint("BOTTOM", 65, 100) f.Nostalgia:SetPoint("BOTTOM", 65, 100)
f.Nostalgia:SetTextColor(1,1,1) f.Nostalgia:SetTextColor(1,1,1)
f.Nostalgia:SetText("Nostalgia") f.Nostalgia:SetText("Nostalgia")
@@ -165,8 +163,7 @@ pfUI:RegisterModule("firstrun", function ()
SkinButton(f.Nostalgia) SkinButton(f.Nostalgia)
f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Legacy:SetWidth(120) f.Legacy:SetSize(120, 20)
f.Legacy:SetHeight(20)
f.Legacy:SetPoint("BOTTOM", 65, 75) f.Legacy:SetPoint("BOTTOM", 65, 75)
f.Legacy:SetTextColor(1,1,1) f.Legacy:SetTextColor(1,1,1)
f.Legacy:SetText("Legacy") f.Legacy:SetText("Legacy")
@@ -179,8 +176,7 @@ pfUI:RegisterModule("firstrun", function ()
SkinButton(f.Legacy) SkinButton(f.Legacy)
f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Slim:SetWidth(120) f.Slim:SetSize(120, 20)
f.Slim:SetHeight(20)
f.Slim:SetPoint("BOTTOM", -65, 75) f.Slim:SetPoint("BOTTOM", -65, 75)
f.Slim:SetTextColor(1,1,1) f.Slim:SetTextColor(1,1,1)
f.Slim:SetText("Slim") f.Slim:SetText("Slim")
@@ -197,8 +193,7 @@ pfUI:RegisterModule("firstrun", function ()
f.Slider.text:SetPoint("TOP", f.Slider, "BOTTOM", 0, 2) f.Slider.text:SetPoint("TOP", f.Slider, "BOTTOM", 0, 2)
f.Slider.text:SetText(T["Scale"]) f.Slider.text:SetText(T["Scale"])
f.Slider:SetWidth(240) f.Slider:SetSize(240, 20)
f.Slider:SetHeight(20)
f.Slider:SetPoint("BOTTOM", 0, 50) f.Slider:SetPoint("BOTTOM", 0, 50)
f.Slider:SetOrientation('HORIZONTAL') f.Slider:SetOrientation('HORIZONTAL')
f.Slider:SetMinMaxValues(0.5, 2.0) f.Slider:SetMinMaxValues(0.5, 2.0)
+1 -1
View File
@@ -181,7 +181,7 @@ pfUI:RegisterModule("map", function ()
end end
if mx and my and MouseIsOver(WorldMapButton) then if mx and my and MouseIsOver(WorldMapButton) then
WorldMapButton.coords.text:SetText(string.format('%.1f / %.1f', mx, my)) WorldMapButton.coords.text:SetFormattedText('%.1f / %.1f', mx, my)
else else
WorldMapButton.coords.text:SetText("") WorldMapButton.coords.text:SetText("")
end end
+1 -1
View File
@@ -138,7 +138,7 @@ pfUI:RegisterModule("minimap", function ()
local coord = pfUI.minimapCoordinates local coord = pfUI.minimapCoordinates
coord.posX, coord.posY = GetPlayerMapPosition("player") coord.posX, coord.posY = GetPlayerMapPosition("player")
if coord.posX ~= 0 and coord.posY ~= 0 then if coord.posX ~= 0 and coord.posY ~= 0 then
coord.text:SetText(string.format("%.1f, %.1f", round(coord.posX * 100, 1), round(coord.posY * 100, 1))) coord.text:SetFormattedText("%.1f, %.1f", round(coord.posX * 100, 1), round(coord.posY * 100, 1))
else else
coord.text:SetText("|cffffaaaaN/A") coord.text:SetText("|cffffaaaaN/A")
end end
+48 -50
View File
@@ -296,18 +296,23 @@ pfUI:RegisterModule("nameplates", function ()
return plate.creatureType return plate.creatureType
end end
-- Totem icon, read straight from the game: -- Totem icon: UnitCreatedBySpell returns the totem-drop spell, whose icon IS
-- * Passive totems self-cast their provided buff, so they carry exactly one -- the totem's icon. It's a broadcast descriptor field the client has for every
-- aura whose icon IS the totem's icon (Totem::Summon TOTEM_PASSIVE). -- summoned unit in range, so it resolves immediately for passive and active
-- * Active totems (Searing/Magma/Fire Nova) cast at enemies and hold no -- totems alike -- no self-aura read or attack-cast capture needed.
-- self-aura, so their icon arrives via UNIT_SPELLCAST_SUCCEEDED (cached --
-- into plate.totemIcon by the event handler); nil here until then. -- Re-read the spell every call rather than caching the icon outright: a shaman
-- 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
if plate.totemIcon then return plate.totemIcon end local spellId = UnitCreatedBySpell(plate.cachedGuid)
local aura = C_UnitAuras.GetBuffDataByIndex(plate.cachedGuid, 1) if spellId ~= plate.totemSpell then
if aura then plate.totemIcon = aura.icon end plate.totemSpell = spellId
plate.totemIcon = spellId and C_Spell.GetSpellTexture(spellId) or nil
end
return plate.totemIcon return plate.totemIcon
end end
@@ -499,7 +504,6 @@ nameplates:RegisterEvent("UNIT_SPELLCAST_START")
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START") nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
nameplates:RegisterEvent("UNIT_SPELLCAST_STOP") nameplates:RegisterEvent("UNIT_SPELLCAST_STOP")
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_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()
@@ -588,6 +592,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
plate.nameplate.unit = arg1 plate.nameplate.unit = 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
@@ -665,22 +670,6 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
end 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 elseif event == "UNIT_AURA" then
-- ClassicAPI: fires with arg1 == "nameplateN" when a unit's aura set -- ClassicAPI: fires with arg1 == "nameplateN" when a unit's aura set
-- changes (add/remove/modify). Flag the matching plate so OnUpdate does a -- changes (add/remove/modify). Flag the matching plate so OnUpdate does a
@@ -1045,7 +1034,13 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
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
local class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player) if plate.cache.minion == nil and unitstr then
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)
@@ -1065,7 +1060,7 @@ 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 then plate.wait_for_scan = true end if not class and not plate.cache.minion 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
@@ -1110,7 +1105,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's aura / attack cast (already a full path) -- icon resolved from the totem-drop spell (already a full path)
plate.totem.icon:SetTexture(TotemIcon) plate.totem.icon:SetTexture(TotemIcon)
plate.glow:Hide() plate.glow:Hide()
@@ -1151,7 +1146,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
if plate.cache.level ~= level or plate.cache.elite ~= elite then if plate.cache.level ~= level or plate.cache.elite ~= elite then
plate.cache.level = level plate.cache.level = level
plate.cache.elite = elite plate.cache.elite = elite
plate.level:SetText(string.format("%s%s", level, (elitestrings[elite] or ""))) plate.level:SetFormattedText("%s%s", level, (elitestrings[elite] or ""))
end end
-- Set level color from GetDifficultyColor when using DB level. -- Set level color from GetDifficultyColor when using DB level.
@@ -1212,21 +1207,21 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
local hasdata = ( rhp and rhpmax ) or estimated or hpmax > 100 or (round(hpmax/100*hp) ~= hp) local hasdata = ( rhp and rhpmax ) or estimated or hpmax > 100 or (round(hpmax/100*hp) ~= hp)
if setting == "curperc" and hasdata and rhp then if setting == "curperc" and hasdata and rhp then
plate.health.text:SetText(string.format("%s | %s%%", Abbreviate(rhp), ceil(hp/hpmax*100))) plate.health.text:SetFormattedText("%s | %s%%", Abbreviate(rhp), ceil(hp/hpmax*100))
elseif setting == "cur" and hasdata and rhp then elseif setting == "cur" and hasdata and rhp then
plate.health.text:SetText(string.format("%s", Abbreviate(rhp))) plate.health.text:SetFormattedText("%s", Abbreviate(rhp))
elseif setting == "curmax" and hasdata and rhp then elseif setting == "curmax" and hasdata and rhp then
plate.health.text:SetText(string.format("%s - %s", Abbreviate(rhp), Abbreviate(rhpmax))) plate.health.text:SetFormattedText("%s - %s", Abbreviate(rhp), Abbreviate(rhpmax))
elseif setting == "curmaxs" and hasdata and rhp then elseif setting == "curmaxs" and hasdata and rhp then
plate.health.text:SetText(string.format("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax))) plate.health.text:SetFormattedText("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax))
elseif setting == "curmaxperc" and hasdata and rhp then elseif setting == "curmaxperc" and hasdata and rhp then
plate.health.text:SetText(string.format("%s - %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100))) plate.health.text:SetFormattedText("%s - %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100))
elseif setting == "curmaxpercs" and hasdata and rhp then elseif setting == "curmaxpercs" and hasdata and rhp then
plate.health.text:SetText(string.format("%s / %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100))) plate.health.text:SetFormattedText("%s / %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100))
elseif setting == "deficit" and rhp then elseif setting == "deficit" and rhp then
plate.health.text:SetText(string.format("-%s" .. (hasdata and "" or "%%"), Abbreviate(rhpmax - rhp))) plate.health.text:SetFormattedText("-%s" .. (hasdata and "" or "%%"), Abbreviate(rhpmax - rhp))
else -- "percent" as fallback else -- "percent" as fallback
plate.health.text:SetText(string.format("%s%%", ceil(hp/hpmax*100))) plate.health.text:SetFormattedText("%s%%", ceil(hp/hpmax*100))
end end
else else
plate.health.text:SetText() plate.health.text:SetText()
@@ -1313,19 +1308,22 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
for i = 1, 16 do debuffDisplayBuf[i].effect = nil end for i = 1, 16 do debuffDisplayBuf[i].effect = nil end
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 auras = C_UnitAuras.GetUnitAuras(unitstr, filter)
local now = GetTime() local now = GetTime()
for _, aura in ipairs(auras) do -- positional UnitAura writes straight into the reusable buffer, so the
if debuffCount >= 16 then break end -- per-plate scan allocates nothing (no per-aura table, no result array)
local i = 1
while debuffCount < 16 do
local aname, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAura(unitstr, i, filter)
if not aname then break end
debuffCount = debuffCount + 1 debuffCount = debuffCount + 1
local timeleft = (aura.expirationTime and aura.expirationTime > 0) and (aura.expirationTime - now) or nil
local b = debuffDisplayBuf[debuffCount] local b = debuffDisplayBuf[debuffCount]
b.effect = aura.name b.effect = aname
b.texture = aura.icon b.texture = icon
b.stacks = aura.applications b.stacks = count
b.dtype = aura.dispelName b.dtype = dispelType
b.duration = aura.duration b.duration = duration
b.timeleft = timeleft 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
@@ -1548,7 +1546,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 GetUnitInfo(name, true, nameplate.cache.player) then if nameplate.wait_for_scan and not nameplate.cache.minion and GetUnitInfo(name, true, nameplate.cache.player) then
nameplate.wait_for_scan = nil nameplate.wait_for_scan = nil
update = true update = true
end end
@@ -1697,7 +1695,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
rounded = floor(remaining * 100) rounded = floor(remaining * 100)
if castbar.lastTextTick ~= rounded then if castbar.lastTextTick ~= rounded then
castbar.lastTextTick = rounded castbar.lastTextTick = rounded
castbar.text:SetText(string.format("%.2f", remaining)) castbar.text:SetFormattedText("%.2f", remaining)
end end
end end
end end
+7 -3
View File
@@ -418,8 +418,13 @@ pfUI:RegisterModule("panel", function()
local repPercent = floor(cur / max * 100) local repPercent = floor(cur / max * 100)
if repPercent < 100 then if repPercent < 100 then
local _, _, _, hex = GetColorGradient(repPercent/100) local _, _, _, hex = GetColorGradient(repPercent/100)
local link = GetInventoryItemLink("player", id)
local texture = GetInventoryItemTexture("player", id)
if texture then
link = CreateSimpleTextureMarkup(texture, 24) .. " " .. link
end
itemLines[table.getn(itemLines)+1] = { itemLines[table.getn(itemLines)+1] = {
GetInventoryItemLink("player", id), link,
string.format("%s%s%%|r", hex, repPercent) string.format("%s%s%%|r", hex, repPercent)
} }
end end
@@ -428,8 +433,7 @@ pfUI:RegisterModule("panel", function()
if totalRep > 0 then if totalRep > 0 then
GameTooltip:ClearLines() GameTooltip:ClearLines()
GameTooltip_SetDefaultAnchor(GameTooltip, this) GameTooltip_SetDefaultAnchor(GameTooltip, this)
GameTooltip:SetText("|cff555555"..(string.gsub(REPAIR_COST,":","")).."|r") GameTooltip:AddLine(REPAIR_COST.." " .. CreateGoldString(totalRep), 0.3333, 0.3333, 0.3333)
SetTooltipMoney(GameTooltip, totalRep)
for _,line in ipairs(itemLines) do for _,line in ipairs(itemLines) do
GameTooltip:AddDoubleLine(line[1],line[2]) GameTooltip:AddDoubleLine(line[1],line[2])
end end
+4 -4
View File
@@ -119,7 +119,7 @@ pfUI:RegisterModule("screenshot", function ()
end end
function pfUI.screenshot:CHAT_MSG_SYSTEM() function pfUI.screenshot:CHAT_MSG_SYSTEM()
local _,_, standing, rep = string.find(arg1, FACTION_STANDING_CHANGEDregex) local standing, rep = string.match(arg1, FACTION_STANDING_CHANGEDregex)
if standing and rep then if standing and rep then
local dt = date("%a, %b %d, %Y %X") local dt = date("%a, %b %d, %Y %X")
local loc = string.format("%s - %s",GetRealZoneText(),GetSubZoneText()) local loc = string.format("%s - %s",GetRealZoneText(),GetSubZoneText())
@@ -150,13 +150,13 @@ pfUI:RegisterModule("screenshot", function ()
end end
function pfUI.screenshot:CHAT_MSG_LOOT() function pfUI.screenshot:CHAT_MSG_LOOT()
local _,_, item, amount = string.find(arg1, LOOT_ITEM_SELF_MULTIPLEregex) local item, amount = string.match(arg1, LOOT_ITEM_SELF_MULTIPLEregex)
if amount then -- ignore stacks if amount then -- ignore stacks
return return
else else
_,_, item = string.find(arg1, LOOT_ITEM_SELFregex) item = string.match(arg1, LOOT_ITEM_SELFregex)
if item then if item then
local _, _, itemColor, itemString, itemName = string.find(item, "^(|c%x+)|H(.+)|h(%[.+%])") local itemColor, itemString, itemName = string.match(item, "^(|c%x+)|H(.+)|h(%[.+%])")
local quality = color2quality[itemColor] local quality = color2quality[itemColor]
if quality and quality >= tonumber(C.screenshot.loot) then if quality and quality >= tonumber(C.screenshot.loot) then
local dt = date("%a, %b %d, %Y %X") local dt = date("%a, %b %d, %Y %X")
+8 -8
View File
@@ -97,20 +97,20 @@ pfUI:RegisterModule("socialmod", function ()
if friendName then if friendName then
friendName:SetText(cname) friendName:SetText(cname)
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), zone, status)) friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), zone, status)
else else
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status)) friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status)
end end
friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), info.level, info.className)) friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), info.level, info.className)
caption:SetVertexColor(1,1,1,.9) caption:SetVertexColor(1,1,1,.9)
friendInfo:SetVertexColor(1,1,1,.9) friendInfo:SetVertexColor(1,1,1,.9)
else else
if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then
caption:SetText(format(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname)) caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname)
friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen)) friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen)
else else
caption:SetText(format(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r")) caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r")
friendInfo:SetText(TEXT(UNKNOWN)) friendInfo:SetText(TEXT(UNKNOWN))
end end
@@ -131,9 +131,9 @@ pfUI:RegisterModule("socialmod", function ()
local playerguild = GetGuildInfo("player") local playerguild = GetGuildInfo("player")
if num + 1 >= MAX_WHOS_FROM_SERVER then if num + 1 >= MAX_WHOS_FROM_SERVER then
WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), max).." |cffaaaaaa"..format(WHO_FRAME_SHOWN_TEMPLATE, MAX_WHOS_FROM_SERVER)) WhoFrameTotals:SetFormattedText("|cffffffff" .. GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num) .. " |cffaaaaaa" .. WHO_FRAME_SHOWN_TEMPLATE, max, MAX_WHOS_FROM_SERVER)
else else
WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), num).." |cffaaaaaa"..format(WHO_FRAME_SHOWN_TEMPLATE, num)) WhoFrameTotals:SetFormattedText("|cffffffff" .. GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num) .. " |cffaaaaaa" .. WHO_FRAME_SHOWN_TEMPLATE, num, num)
end end
for i=1, WHOS_TO_DISPLAY do for i=1, WHOS_TO_DISPLAY do
+8 -8
View File
@@ -592,10 +592,10 @@ pfUI:RegisterModule("swingtimer", function ()
end end
pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA) pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA)
if sw_showtext then if sw_showtext then
pfUI.swingtimer.mainhand.text:SetText(string.format("%.1f", math.floor(S.mhTimer * 10) / 10)) pfUI.swingtimer.mainhand.text:SetFormattedText("%.1f", math.floor(S.mhTimer * 10) / 10)
end end
if sw_showspeed and S.mhSpeed > 0 then if sw_showspeed and S.mhSpeed > 0 then
pfUI.swingtimer.mainhand.speed:SetText(string.format("%.2f", S.mhSpeed)) pfUI.swingtimer.mainhand.speed:SetFormattedText("%.2f", S.mhSpeed)
end end
anyActive = true anyActive = true
end end
@@ -626,10 +626,10 @@ pfUI:RegisterModule("swingtimer", function ()
end end
end end
if sw_showtext then if sw_showtext then
pfUI.swingtimer.offhand.text:SetText(string.format("%.1f", math.floor(S.ohTimer * 10) / 10)) pfUI.swingtimer.offhand.text:SetFormattedText("%.1f", math.floor(S.ohTimer * 10) / 10)
end end
if sw_showspeed and S.ohSpeed > 0 then if sw_showspeed and S.ohSpeed > 0 then
pfUI.swingtimer.offhand.speed:SetText(string.format("%.2f", S.ohSpeed)) pfUI.swingtimer.offhand.speed:SetFormattedText("%.2f", S.ohSpeed)
end end
anyActive = true anyActive = true
elseif not sw_showoh then elseif not sw_showoh then
@@ -668,9 +668,9 @@ pfUI:RegisterModule("swingtimer", function ()
end end
if sw_showtext then if sw_showtext then
if remaining <= 0.5 then if remaining <= 0.5 then
pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor(remaining * 10) / 10)) pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
else else
pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor((remaining - 0.5) * 10) / 10)) pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor((remaining - 0.5) * 10) / 10)
end end
end end
else else
@@ -683,11 +683,11 @@ pfUI:RegisterModule("swingtimer", function ()
pfUI.swingtimer.ranged.right:Hide() pfUI.swingtimer.ranged.right:Hide()
pfUI.swingtimer.ranged.warn:Hide() pfUI.swingtimer.ranged.warn:Hide()
if sw_showtext then if sw_showtext then
pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor(remaining * 10) / 10)) pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
end end
end end
if sw_showspeed and S.raSpeed > 0 then if sw_showspeed and S.raSpeed > 0 then
pfUI.swingtimer.ranged.speed:SetText(string.format("%.2f", S.raSpeed)) pfUI.swingtimer.ranged.speed:SetFormattedText("%.2f", S.raSpeed)
end end
local raProgress = 1 - (S.raTimer / S.raTimerMax) local raProgress = 1 - (S.raTimer / S.raTimerMax)
local raMarkerX = raProgress * sw_width local raMarkerX = raProgress * sw_width
+2 -2
View File
@@ -166,9 +166,9 @@ pfUI:RegisterModule("tooltip", function ()
end end
if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then
pfUI.tooltipStatusBar.HP:SetText(string.format("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax))) pfUI.tooltipStatusBar.HP:SetFormattedText("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax))
elseif hpmax > 0 then elseif hpmax > 0 then
pfUI.tooltipStatusBar.HP:SetText(string.format("%s%%", ceil(hp/hpmax*100))) pfUI.tooltipStatusBar.HP:SetFormattedText("%s%%", ceil(hp/hpmax*100))
else else
pfUI.tooltipStatusBar.HP:SetText("") pfUI.tooltipStatusBar.HP:SetText("")
end end
+23 -90
View File
@@ -3,56 +3,15 @@ pfUI:RegisterModule("tracking", function ()
MINIMAP_TRACKING_FRAME:UnregisterAllEvents() MINIMAP_TRACKING_FRAME:UnregisterAllEvents()
MINIMAP_TRACKING_FRAME:Hide() MINIMAP_TRACKING_FRAME:Hide()
local function HasEntries(tbl)
for _ in pairs(tbl) do
return true
end
return nil
end
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local size = tonumber(C.appearance.minimap.tracking_size) local size = tonumber(C.appearance.minimap.tracking_size)
local pulse = C.appearance.minimap.tracking_pulse == "1" local pulse = C.appearance.minimap.tracking_pulse == "1"
-- Tracking spells identified by SpellID + expected icon path. -- Tracking spells come from ClassicAPI's native GetNumTrackingTypes /
-- SpellID is the primary identifier (stable, locale-independent). -- GetTrackingInfo (see the DLL's docs/API.md "Tracking"). The DLL
-- Icon path is used as secondary confirmation when scanning the spellbook. -- enumerates them straight from the spellbook by tracking-aura effect,
local knownTrackingSpells = { -- so there is no spell table to maintain here and server-custom trackers
any = { -- (e.g. Turtle's Find Trees) are picked up automatically.
{ id = 2481, icon = "Racial_Dwarf_FindTreasure" }, -- Find Treasure
{ id = 2580, icon = "Spell_Nature_Earthquake" }, -- Find Minerals
{ id = 2383, icon = "INV_Misc_Flower_02" }, -- Find Herbs (Rank 1)
{ id = 8387, icon = "INV_Misc_Flower_02" }, -- Find Herbs (Rank 2)
{ id = 52917, icon = "INV_TradeSkillItem_03" }, -- Find Trees (TurtleWow)
},
HUNTER = {
{ id = 1494, icon = "Ability_Tracking" }, -- Track Beasts
{ id = 19883, icon = "Spell_Holy_PrayerOfHealing" }, -- Track Humanoids
{ id = 19884, icon = "Spell_Shadow_DarkSummoning" }, -- Track Undead
{ id = 19885, icon = "Ability_Stealth" }, -- Track Hidden
{ id = 19880, icon = "Spell_Frost_SummonWaterElemental" }, -- Track Elementals
{ id = 19878, icon = "Spell_Shadow_SummonFelHunter" }, -- Track Demons
{ id = 19882, icon = "Ability_Racial_Avatar" }, -- Track Giants
{ id = 19879, icon = "INV_Misc_Head_Dragon_01" }, -- Track Dragonkin
},
PALADIN = {
{ id = 5502, icon = "Spell_Holy_SenseUndead" }, -- Sense Undead
},
WARLOCK = {
{ id = 5500, icon = "Spell_Shadow_Metamorphosis" }, -- Sense Demons
},
DRUID = {
{ id = 5225, icon = "Ability_Tracking" }, -- Track Humanoids (Cat Form only)
},
}
-- Build a flat lookup: spellId -> entry, for fast spellbook matching
local spellIdLookup = {}
for _, entries in pairs(knownTrackingSpells) do
for _, entry in ipairs(entries) do
spellIdLookup[entry.id] = entry
end
end
local state = { local state = {
texture = nil, texture = nil,
@@ -128,50 +87,24 @@ pfUI:RegisterModule("tracking", function ()
end) end)
function pfUI.tracking:RefreshSpells() function pfUI.tracking:RefreshSpells()
local playerClass = UnitClassBase("player") state.spells = {}
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(playerClass) if not GetNumTrackingTypes then return end
-- Build set of valid SpellIDs for this class -- Druid Track Humanoids (5225) only casts in Cat Form. Hide it out of
local validIds = {} -- form so the menu never offers a tracker that would fail to cast.
for _, entry in ipairs(knownTrackingSpells.any) do local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(UnitClassBase("player"))
validIds[entry.id] = true
end
if knownTrackingSpells[playerClass] then
for _, entry in ipairs(knownTrackingSpells[playerClass]) do
validIds[entry.id] = true
end
end
-- Druids only get Track Humanoids in Cat Form for i = 1, GetNumTrackingTypes() do
if playerClass == "DRUID" and not isCatForm then local name, texture, _, _, spellId = GetTrackingInfo(i)
validIds[5225] = nil local castable = not (spellId == 5225 and not isCatForm)
end if name and texture and castable
and not pfUI.tracking.invalidSpells[name] then
-- Scan spellbook: match by icon path, confirm SpellID is valid for this class state.spells[i] = {
for tabIndex = 1, GetNumSpellTabs() do index = i, -- tracking index, passed to SetTracking()
local _, _, offset, numSpells = GetSpellTabInfo(tabIndex) name = name,
for spellIndex = offset + 1, offset + numSpells do texture = texture,
local spellTexture = GetSpellTexture(spellIndex, BOOKTYPE_SPELL) spellId = spellId,
local spellName = GetSpellName(spellIndex, BOOKTYPE_SPELL) }
if pfUI.tracking.invalidSpells[spellName] then
spellTexture = nil
end
if spellTexture then
local lowerTexture = string.lower(spellTexture)
for spellId, entry in pairs(spellIdLookup) do
if validIds[spellId] and not state.spells[spellId]
and strfind(lowerTexture, string.lower(entry.icon)) then
state.spells[spellId] = {
index = spellIndex,
name = spellName,
texture = spellTexture,
spellId = spellId,
}
end
end
end
end end
end end
end end
@@ -187,7 +120,7 @@ pfUI:RegisterModule("tracking", function ()
elseif not texture then elseif not texture then
state.texture = nil state.texture = nil
if pulse and HasEntries(state.spells) then if pulse and next(state.spells) ~= nil then
pfUI.tracking.pulse = true pfUI.tracking.pulse = true
pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
pfUI.tracking.icon:SetVertexColor(1,1,1,1) pfUI.tracking.icon:SetVertexColor(1,1,1,1)
@@ -217,7 +150,7 @@ pfUI:RegisterModule("tracking", function ()
checked = spell.texture == state.texture, checked = spell.texture == state.texture,
arg1 = spell, arg1 = spell,
func = function (arg1) func = function (arg1)
CastSpell(arg1.index, BOOKTYPE_SPELL) SetTracking(arg1.index)
CloseDropDownMenus() CloseDropDownMenus()
end end
}) })
+2 -2
View File
@@ -134,7 +134,7 @@ pfUI:RegisterModule("unitxp", function ()
end end
this.text:SetTextColor(r, g, b, 1) this.text:SetTextColor(r, g, b, 1)
this.text:SetText(string.format("%.1f%s", distance, suffix)) this.text:SetFormattedText("%.1f%s", distance, suffix)
this.text:Show() this.text:Show()
end) end)
@@ -206,7 +206,7 @@ pfUI:RegisterModule("unitxp", function ()
end end
f.text:SetTextColor(r, g, b, 1) f.text:SetTextColor(r, g, b, 1)
f.text:SetText(string.format("%.1f%s", distance, suffix)) f.text:SetFormattedText("%.1f%s", distance, suffix)
end) end)
end end
pfUI.uf.target.distanceIndicator = pfRangeDisplay pfUI.uf.target.distanceIndicator = pfRangeDisplay
+3 -3
View File
@@ -236,7 +236,7 @@ end
local xpperc = round(xp / xpmax * 100) local xpperc = round(xp / xpmax * 100)
local experc = ex and round(ex / xpmax * 100) or 0 local experc = ex and round(ex / xpmax * 100) or 0
if ex then text = "%s: %s%% (%s%% %s)" end if ex then text = "%s: %s%% (%s%% %s)" end
self.bar.text:SetText(string.format(text, T["Experience"], xpperc, experc, T["Rested"])) self.bar.text:SetFormattedText(text, T["Experience"], xpperc, experc, T["Rested"])
self.tick = GetTime() + self.timeout self.tick = GetTime() + self.timeout
if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then
@@ -254,7 +254,7 @@ end
local text = "%s: %s%%" local text = "%s: %s%%"
local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0 local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0
self.bar.text:SetText(string.format(text, T["Pet Experience"], xpperc)) self.bar.text:SetFormattedText(text, T["Pet Experience"], xpperc)
self.tick = GetTime() + self.timeout self.tick = GetTime() + self.timeout
return return
@@ -282,7 +282,7 @@ end
local text = "%s: %s%% (%s)" local text = "%s: %s%% (%s)"
local perc = round(barValue / barMax * 100) local perc = round(barValue / barMax * 100)
local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender) local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender)
self.bar.text:SetText(string.format(text, name, perc, standing)) self.bar.text:SetFormattedText(text, name, perc, standing)
self.tick = GetTime() + self.timeout self.tick = GetTime() + self.timeout
return return
+24 -37
View File
@@ -3,6 +3,10 @@ function SlashCmdList.RELOAD(msg, editbox)
ReloadUI() ReloadUI()
end end
if pfUI_disabled then return 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())
@@ -13,7 +17,7 @@ function SlashCmdList.GM(msg, editbox)
ToggleHelpFrame(1) ToggleHelpFrame(1)
end end
pfUI = CreateFrame("Frame", nil, UIParent) local pfUI = CreateFrame("Frame", addonName, UIParent)
pfUI:RegisterEvent("ADDON_LOADED") pfUI:RegisterEvent("ADDON_LOADED")
-- setup bootvar -- setup bootvar
@@ -23,7 +27,7 @@ do
-- ClassicAPI dependency check. -- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus -- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required. -- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 10906 -- (X*10000 + Y*100 + Z) local PFUI_CLASSIC_API_MIN = 11301 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI" local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest" local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
@@ -86,14 +90,13 @@ do
end end
end end
-- initialize saved variables pfUI_playerDB = pfUI_playerDB or {}
pfUI_playerDB = {} pfUI_config = pfUI_config or {}
pfUI_config = {} pfUI_init = pfUI_init or {}
pfUI_init = {} pfUI_profiles = pfUI_profiles or {}
pfUI_profiles = {} pfUI_addon_profiles = pfUI_addon_profiles or {}
pfUI_addon_profiles = {} pfUI_cache = pfUI_cache or {}
pfUI_cache = {} pfUI_throttle = pfUI_throttle or {}
pfUI_throttle = {}
-- localization -- localization
pfUI_locale = {} pfUI_locale = {}
@@ -118,35 +121,24 @@ 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
if not pfUI.disabled then pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events = Mixin({}, CallbackRegistryMixin) pfUI.events:OnLoad()
pfUI.events:OnLoad() pfUI.events:SetUndefinedEventsAllowed(true)
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
-- detect current addon path pfUI.name = addonName
local tocs = { "", "-master", "-tbc", "-wotlk" } pfUI.path = "Interface\\AddOns\\" .. addonName
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 strfind(value, "img:") then if value:find("img:") then
value = string.gsub(value, "img:", pfUI.path .. "\\img\\") value = string.gsub(value, "img:", pfUI.path .. "\\img\\")
elseif strfind(value, "font:") then elseif value:find("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 .. "\\")
@@ -155,9 +147,7 @@ pfUI.media = setmetatable({}, { __index = function(tab,key)
return value return value
end}) end})
-- cache client version pfUI.client = INTERFACE_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)})
@@ -391,14 +381,13 @@ 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 string.find(func, "vanilla") then if arg3 and type(func) == "string" and type(arg3) == "function" and func:find("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
@@ -409,7 +398,6 @@ 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
@@ -430,7 +418,6 @@ 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
@@ -444,7 +431,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 strfind(raw, "@") then if raw:find("@") 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
@@ -577,4 +564,4 @@ function pfUI.SetupCVars()
COMBAT_TEXT_SHOW_HONOR_GAINED = "1" COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
end end
UIParentLoadAddOn("Blizzard_CombatText") UIParentLoadAddOn("Blizzard_CombatText")
end end
+2
View File
@@ -8,6 +8,8 @@
## 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
API_Check.lua
pfUI.lua pfUI.lua
init\env.xml init\env.xml
+2 -3
View File
@@ -295,9 +295,8 @@ pfUI:RegisterSkin("Character", function ()
if faction and faction.reaction and faction.reaction < 8 then if faction and faction.reaction and faction.reaction < 8 then
local repLeft = faction.nextReactionThreshold - faction.currentStanding local repLeft = faction.nextReactionThreshold - faction.currentStanding
if repLeft > 1 then if repLeft > 1 then
local text = standing:GetText() .. string.format(" (%d)", repLeft) standing:SetFormattedText("%s (%d)", standing:GetText(), repLeft)
standing:SetText(text) standing:GetParent().standingText = standing:GetText()
standing:GetParent().standingText = text
end end
end end
end end
+1 -1
View File
@@ -125,7 +125,7 @@ pfUI:RegisterSkin("Inspect", function ()
local guild, title = GetGuildInfo(InspectFrame.unit) local guild, title = GetGuildInfo(InspectFrame.unit)
if guild then if guild then
InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1) InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1)
InspectGuildText:SetText(format(TEXT(GUILD_TITLE_TEMPLATE), title, guild)) InspectGuildText:SetFormattedText(TEXT(GUILD_TITLE_TEMPLATE), title, guild)
InspectGuildText:Show() InspectGuildText:Show()
else else
InspectGuildText:SetText("") InspectGuildText:SetText("")