mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-22 07:36:56 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 863d99647d | |||
| 06b3ae8d22 | |||
| 879d7afe83 | |||
| 4eab494a01 | |||
| bb8ce8fd63 | |||
| 75c4657ab9 | |||
| 5815b9e81e | |||
| 60f953178f | |||
| 1dc10964a2 | |||
| 0fc08e77c0 | |||
| 7f3795b45b | |||
| 8eee766652 | |||
| 2b5288a688 | |||
| daba4e202e | |||
| 98c7751416 | |||
| 8796fab6d8 | |||
| 7ca0e40d8b | |||
| 2f93b246a1 | |||
| c647698f8f | |||
| 48026008b6 | |||
| c1d96bae58 | |||
| 1d44c5ec6f | |||
| 8f88dfd852 | |||
| a57214aeb0 | |||
| 9bde843e33 | |||
| ad13e6ace3 | |||
| 82c74c2db5 | |||
| a3cfd711fe | |||
| a7ad1f073f | |||
| fe5b3b135b | |||
| 9c29f0c7d4 | |||
| 685ecb4a3e | |||
| 8cdaf1fc00 | |||
| bb39c9d94e | |||
| 18059ffbb7 | |||
| d75e280238 | |||
| 9eb34b8f0a | |||
| f8dde49f09 |
@@ -23,12 +23,12 @@ jobs:
|
||||
IFS=. read -r MAJOR MINOR PATCH <<<"$LATEST"
|
||||
PACKED=$((MAJOR * 10000 + MINOR * 100 + PATCH))
|
||||
echo "Pinning PFUI_CLASSIC_API_LATEST to $LATEST ($PACKED)"
|
||||
sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" pfUI.lua
|
||||
grep 'local PFUI_CLASSIC_API_' pfUI.lua
|
||||
sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" API_Check.lua
|
||||
grep 'local PFUI_CLASSIC_API_' API_Check.lua
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Package and release to GitHub
|
||||
uses: BigWigsMods/packager@v2
|
||||
uses: brues-code/packager@vCAPI
|
||||
env:
|
||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"runtime": {
|
||||
"version": "Lua 5.1"
|
||||
},
|
||||
"diagnostics": {
|
||||
"disable": ["deprecated"],
|
||||
"globals": [
|
||||
"this",
|
||||
"event",
|
||||
"arg",
|
||||
"arg1",
|
||||
"arg2",
|
||||
"arg3",
|
||||
"arg4",
|
||||
"arg5",
|
||||
"arg6",
|
||||
"arg7",
|
||||
"arg8",
|
||||
"arg9"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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 = 11304 -- (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
|
||||
+69
@@ -204,6 +204,32 @@ function pfUI.api.UnitHasBuff(unit, name)
|
||||
return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil
|
||||
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 ]
|
||||
-- Returns whether a GUID or unit token refers to the local player.
|
||||
-- guid [string] A unit GUID (or unitID) to test.
|
||||
-- return: [bool] true if it is the player otherwise "false"
|
||||
pfUI.api.IsPlayerGuid = _G.IsPlayerGuid
|
||||
|
||||
-- [ GetUnbuffedRoster ]
|
||||
-- Returns a comma-joined, colored list of group members missing the named aura.
|
||||
-- name [string] the localized aura name to check for
|
||||
@@ -236,6 +262,45 @@ function pfUI.api.GetUnitColor(unitstr)
|
||||
return classColor:GenerateHexColorMarkup(), classColor:GetRGB()
|
||||
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 ]
|
||||
-- Creates vertical text using linebreaks. Multibyte char friendly.
|
||||
-- 'str' [string] String to columnize.
|
||||
@@ -665,6 +730,10 @@ end
|
||||
function pfUI.api.CreateGoldString(money)
|
||||
if type(money) ~= "number" then return "-" end
|
||||
|
||||
if _G.GetCoinTextureString then
|
||||
return "|cffffffff" .. _G.GetCoinTextureString(money) .. "|r"
|
||||
end
|
||||
|
||||
local gold = floor(money/ 100 / 100)
|
||||
local silver = floor(mod((money/100),100))
|
||||
local copper = floor(mod(money,100))
|
||||
|
||||
+2
-1
@@ -223,7 +223,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("unitframes", nil, "always2dportrait", "0")
|
||||
pfUI:UpdateConfig("unitframes", nil, "portraittexture", "1")
|
||||
pfUI:UpdateConfig("unitframes", nil, "layout", "default")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck", "1")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck", "0")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck_mode", "vanilla")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck_distance", "40")
|
||||
pfUI:UpdateConfig("unitframes", nil, "buffdetect", "0")
|
||||
@@ -817,6 +817,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("chat", "text", "playerlinks", "1")
|
||||
pfUI:UpdateConfig("chat", "text", "detecturl", "1")
|
||||
pfUI:UpdateConfig("chat", "text", "classcolor", "1")
|
||||
pfUI:UpdateConfig("chat", "text", "playericons", "0")
|
||||
pfUI:UpdateConfig("chat", "text", "whosearchunknown", "0")
|
||||
pfUI:UpdateConfig("chat", "text", "playerlevel", "0")
|
||||
pfUI:UpdateConfig("chat", "left", "width", "380")
|
||||
|
||||
+130
-70
@@ -4,6 +4,17 @@ setfenv(1, pfUI:GetEnvironment())
|
||||
pfUI.uf = CreateFrame("Frame", nil, UIParent)
|
||||
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
|
||||
-- Only updates frames where the unit actually changed, not ALL 40 frames
|
||||
@@ -88,16 +99,21 @@ local function DebuffOnEnter()
|
||||
local parent = this:GetParent()
|
||||
|
||||
-- selfdebuff filters the displayed list to player-cast harmful auras, but
|
||||
-- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
|
||||
-- up the displayed aura via the PLAYER filter, then scan engine slots for
|
||||
-- one whose name + sourceGUID match.
|
||||
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up
|
||||
-- the displayed aura via the PLAYER filter, then find its position in the
|
||||
-- unfiltered list by name + sourceGUID.
|
||||
--
|
||||
-- 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
|
||||
local ownAura = C_UnitAuras.GetAuraDataByIndex(unitstr, this.id, "HARMFUL|PLAYER")
|
||||
if ownAura then
|
||||
for gameSlot = 1, 16 do
|
||||
local check = C_UnitAuras.GetDebuffDataByIndex(unitstr, gameSlot)
|
||||
local n = ScanAuraSlots(unitstr, "HARMFUL", tooltipSlots)
|
||||
for i = 1, n do
|
||||
local check = C_UnitAuras.GetAuraDataBySlot(unitstr, tooltipSlots[i])
|
||||
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
|
||||
GameTooltip:SetUnitAura(unitstr, gameSlot, "HARMFUL")
|
||||
GameTooltip:SetUnitAura(unitstr, i, "HARMFUL")
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -205,8 +221,13 @@ function pfUI.uf:UpdateVisibility()
|
||||
|
||||
-- cache result of strsub to avoid repeating calls
|
||||
if not self.cache_raid then
|
||||
if strsub(self:GetName(),0,6) == "pfRaid" then
|
||||
self.cache_raid = tonumber(strsub(self:GetName(),7,8)) or 0
|
||||
local name = self:GetName()
|
||||
if strsub(name,0,9) == "pfRaidPet" then
|
||||
-- pet grid slot (pfRaidPet1..40); used to mirror party pets below
|
||||
self.cache_raid = 0
|
||||
self.cache_raidpet = tonumber(strsub(name,10)) or 0
|
||||
elseif strsub(name,0,6) == "pfRaid" then
|
||||
self.cache_raid = tonumber(strsub(name,7,8)) or 0
|
||||
else
|
||||
self.cache_raid = 0
|
||||
end
|
||||
@@ -238,6 +259,26 @@ function pfUI.uf:UpdateVisibility()
|
||||
end
|
||||
end
|
||||
|
||||
-- show raidpet frames as party pets when a party is shown as a raid grid
|
||||
if self.cache_raidpet then
|
||||
if not IsInRaid() and IsInGroup() and C.unitframes.raidforgroup == "1" then
|
||||
local id = self.cache_raidpet
|
||||
if id == 1 then
|
||||
-- grid slot 1 mirrors the player, so its pet is the player's pet
|
||||
self.id = ""
|
||||
self.label = "pet"
|
||||
elseif id <= 5 then
|
||||
self.id = id - 1
|
||||
self.label = "partypet"
|
||||
end
|
||||
|
||||
-- reset to regular raidpet unitstrings after leaving party mode
|
||||
elseif self.label == "pet" or self.label == "partypet" then
|
||||
self.id = self.cache_raidpet
|
||||
self.label = "raidpet"
|
||||
end
|
||||
end
|
||||
|
||||
-- display every unit as player while pfUI.uf.showall is set
|
||||
if pfUI.uf.showall then
|
||||
self._label = self._label or self.label
|
||||
@@ -266,9 +307,10 @@ function pfUI.uf:UpdateVisibility()
|
||||
-- frame shall not be visible
|
||||
visibility = "hide"
|
||||
self.visible = nil
|
||||
elseif hide_group and self.cache_raid == 0 and self.label and strsub(self.label,0,5) == "party" then
|
||||
-- hide group while shown as a raid grid and option is set (raid frames
|
||||
-- carry label "party" under raidforgroup, so exclude them by cache_raid)
|
||||
elseif hide_group and self.cache_raid == 0 and not self.cache_raidpet and self.label and strsub(self.label,0,5) == "party" then
|
||||
-- hide group while shown as a raid grid and option is set (raid player
|
||||
-- frames carry label "party" and raid pet frames carry label "partypet"
|
||||
-- under raidforgroup, so exclude them by cache_raid / cache_raidpet)
|
||||
visibility = "hide"
|
||||
self.visible = nil
|
||||
elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" )
|
||||
@@ -735,7 +777,7 @@ function pfUI.uf:UpdateConfig()
|
||||
if not f.buffs[i].cd then
|
||||
if cooldown_anim == 1 then
|
||||
-- Animation enabled: Use Model frame with CooldownFrameTemplate
|
||||
f.buffs[i].cd = CreateFrame(COOLDOWN_FRAME_TYPE, f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate")
|
||||
f.buffs[i].cd = CreateFrame("Model", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate")
|
||||
else
|
||||
-- Animation disabled: Use regular Frame with dummy functions
|
||||
f.buffs[i].cd = CreateFrame("Frame", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i])
|
||||
@@ -808,7 +850,7 @@ function pfUI.uf:UpdateConfig()
|
||||
if not f.debuffs[i].cd then
|
||||
if cooldown_anim == 1 then
|
||||
-- Animation enabled: Use Model frame with CooldownFrameTemplate
|
||||
f.debuffs[i].cd = CreateFrame(COOLDOWN_FRAME_TYPE, f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate")
|
||||
f.debuffs[i].cd = CreateFrame("Model", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate")
|
||||
else
|
||||
-- Animation disabled: Use regular Frame with dummy functions
|
||||
f.debuffs[i].cd = CreateFrame("Frame", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i])
|
||||
@@ -1609,42 +1651,45 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
|
||||
-- buffs
|
||||
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
|
||||
if not unit.buffs[i] then break end
|
||||
|
||||
local aura = C_UnitAuras.GetBuffDataByIndex(unitstr, i)
|
||||
local name, icon, count, _, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
|
||||
if aura then
|
||||
unit.buffs[i].texture:SetTexture(aura.icon)
|
||||
if name then
|
||||
unit.buffs[i].texture:SetTexture(icon)
|
||||
unit.buffs[i]:Show()
|
||||
|
||||
if aura.applications > 1 then
|
||||
unit.buffs[i].stacks:SetText(aura.applications)
|
||||
if count > 1 then
|
||||
unit.buffs[i].stacks:SetText(count)
|
||||
else
|
||||
unit.buffs[i].stacks:SetText("")
|
||||
end
|
||||
|
||||
if aura.expirationTime > 0 then
|
||||
if expirationTime > 0 then
|
||||
-- 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
|
||||
-- (the Spell.dbc base). Anchor start to now in that case to keep the remaining math sane.
|
||||
-- which happens for talent-extended buffs where the real duration exceeds the
|
||||
-- Spell.dbc base. Anchor start to now in that case to keep the remaining math sane.
|
||||
local now = GetTime()
|
||||
local start = aura.expirationTime - aura.duration
|
||||
local duration = aura.duration
|
||||
if start > now or duration <= 0 then
|
||||
start, duration = now, aura.expirationTime - now
|
||||
local start = expirationTime - duration
|
||||
local dur = duration
|
||||
if start > now or dur <= 0 then
|
||||
start, dur = now, expirationTime - now
|
||||
end
|
||||
if duration > 0 then
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
|
||||
if dur > 0 then
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, dur, 1)
|
||||
else
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
|
||||
end
|
||||
elseif aura.duration > 0 then
|
||||
elseif duration > 0 then
|
||||
local guid = UnitGUID(unitstr)
|
||||
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
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, aura.duration, 1)
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
|
||||
else
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
|
||||
end
|
||||
@@ -1696,6 +1741,13 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
reposition = true
|
||||
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
|
||||
if not unit.debuffs[i] then break end
|
||||
|
||||
@@ -1712,13 +1764,10 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
invert_h * ((row+buffrow)*(multiply*default_border + unit.config.debuffsize + 1) + (multiply*default_border + 1)))
|
||||
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).
|
||||
local filter = (unit.label ~= "player" and selfdebuff == "1") and "HARMFUL|PLAYER" or "HARMFUL"
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, filter)
|
||||
if aura then
|
||||
texture, stacks, dtype = aura.icon, aura.applications, aura.dispelName
|
||||
-- positional read by slot id allocates nothing
|
||||
local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if name then
|
||||
texture, stacks, dtype = icon, count, dispelType
|
||||
else
|
||||
texture, stacks, dtype = nil, 0, nil
|
||||
end
|
||||
@@ -1731,18 +1780,18 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
if texture then
|
||||
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
|
||||
-- the dbc base duration) don't push start into the future and trip
|
||||
-- CooldownFrame_SetTimer's 2^32-wraparound branch.
|
||||
local now = GetTime()
|
||||
local start = aura.expirationTime - aura.duration
|
||||
local duration = aura.duration
|
||||
if start > now or duration <= 0 then
|
||||
start, duration = now, aura.expirationTime - now
|
||||
local start = expirationTime - duration
|
||||
local dur = duration
|
||||
if start > now or dur <= 0 then
|
||||
start, dur = now, expirationTime - now
|
||||
end
|
||||
if duration > 0 then
|
||||
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1)
|
||||
if dur > 0 then
|
||||
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, dur, 1)
|
||||
else
|
||||
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
|
||||
end
|
||||
@@ -1800,6 +1849,18 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
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
|
||||
local n = ScanAuraSlots(unitstr, "HARMFUL", auraSlots)
|
||||
for i=1,n do
|
||||
local name, _, _, dispelType = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if name and dispelType and dispelType ~= "" then present[dispelType] = true end
|
||||
end
|
||||
|
||||
for _, debuff in pairs(unit.dispellable) do
|
||||
indicator[debuff] = indicator[debuff] or CreateFrame("Frame", nil, indicator)
|
||||
indicator[debuff]:SetParent(indicator)
|
||||
@@ -1839,15 +1900,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
indicator[debuff].disp = indicator.disp
|
||||
end
|
||||
|
||||
indicator[debuff].visible = nil
|
||||
|
||||
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
|
||||
indicator[debuff].visible = present[debuff]
|
||||
|
||||
if indicator[debuff].visible then
|
||||
indicator[debuff]:Show()
|
||||
@@ -1894,21 +1947,24 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
|
||||
local pos = 1
|
||||
if table.getn(unit.indicators) > 0 then
|
||||
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do
|
||||
local texLower = string.lower(aura.icon)
|
||||
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
|
||||
local n = ScanAuraSlots(unitstr, "HELPFUL", auraSlots)
|
||||
for i=1,n do
|
||||
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[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
|
||||
if filter == texLower then
|
||||
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)
|
||||
end
|
||||
if hot then
|
||||
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
|
||||
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
|
||||
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
|
||||
end
|
||||
pos = pos + 1
|
||||
break
|
||||
@@ -1918,12 +1974,15 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
end
|
||||
|
||||
if table.getn(unit.indicator_custom) > 0 then
|
||||
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do
|
||||
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
|
||||
local lowerName = string.lower(aura.name)
|
||||
local n = ScanAuraSlots(unitstr, "HELPFUL", auraSlots)
|
||||
for i=1,n do
|
||||
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
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
|
||||
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
|
||||
break
|
||||
end
|
||||
@@ -1931,13 +1990,14 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
end
|
||||
|
||||
local debuffFilter = unit.config.selfdebuff == "1" and "HARMFUL|PLAYER" or "HARMFUL"
|
||||
for i=1,16 do -- scan for custom debuffs
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, debuffFilter)
|
||||
if aura then
|
||||
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
|
||||
n = ScanAuraSlots(unitstr, debuffFilter, auraSlots)
|
||||
for i=1,n do -- scan for custom debuffs
|
||||
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if name then
|
||||
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
|
||||
for _, filter in pairs(unit.indicator_custom) do
|
||||
if filter == string.lower(aura.name) then
|
||||
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
|
||||
if filter == string.lower(name) then
|
||||
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
|
||||
pos = pos + 1
|
||||
break
|
||||
end
|
||||
@@ -2194,7 +2254,7 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
|
||||
-- Check if parent frame has cooldown animation enabled
|
||||
local parent_cooldown_anim = frame.config and tonumber(frame.config.cooldown_anim) or 1
|
||||
if parent_cooldown_anim == 1 then
|
||||
frame.icon[pos].cd = CreateFrame(COOLDOWN_FRAME_TYPE, nil, frame.icon[pos])
|
||||
frame.icon[pos].cd = CreateFrame("Model", nil, frame.icon[pos])
|
||||
else
|
||||
frame.icon[pos].cd = CreateFrame("Frame", nil, frame.icon[pos])
|
||||
frame.icon[pos].cd.AdvanceTime = DoNothing
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
-- 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
|
||||
Vendored
-2352
File diff suppressed because it is too large
Load Diff
Vendored
+2377
File diff suppressed because it is too large
Load Diff
Vendored
+2991
File diff suppressed because it is too large
Load Diff
@@ -72,7 +72,6 @@
|
||||
<Include file="..\modules\totems.lua"/>
|
||||
<Include file="..\modules\macrotweak.lua"/>
|
||||
<Include file="..\modules\macroicons.lua"/>
|
||||
<Include file="..\modules\turtle-wow.lua"/>
|
||||
<Include file="..\modules\superwow.lua"/>
|
||||
<Include file="..\modules\innervatecall.lua"/>
|
||||
<Include file="..\modules\nampower.lua"/>
|
||||
|
||||
+1
-7
@@ -35,12 +35,6 @@
|
||||
<Include file="..\skins\blizzard\tooltips.lua"/>
|
||||
<Include file="..\skins\blizzard\tabard.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>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<Include file="..\compat\vanilla.lua"/>
|
||||
<Include file="..\env\tables_stock.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,14 @@
|
||||
<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>
|
||||
@@ -168,6 +168,9 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
|
||||
return
|
||||
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
|
||||
local amount = cache[spell_queue[2]][1]
|
||||
local casttime = castTime
|
||||
|
||||
@@ -1016,7 +1016,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
local id = (bar-1)*12+button
|
||||
local exists = _G[button_name] and true or nil
|
||||
local f = _G[button_name] or CreateFrame("Button", button_name, parent, ACTIONBAR_SECURE_TEMPLATE_BUTTON)
|
||||
local f = _G[button_name] or CreateFrame("Button", button_name, parent)
|
||||
|
||||
-- no button available, create a new one
|
||||
if not exists then
|
||||
@@ -1042,7 +1042,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
f.slot = id
|
||||
|
||||
-- cooldown
|
||||
f.cd = CreateFrame(COOLDOWN_FRAME_TYPE, f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
|
||||
f.cd = CreateFrame("Model", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
|
||||
f.cd.pfCooldownStyleAnimation = 1
|
||||
f.cd.pfCooldownType = "NOGCD"
|
||||
f.cd.pfCooldownSize = cd_size
|
||||
@@ -1233,7 +1233,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- create frame
|
||||
local init = not bars[i]
|
||||
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent, ACTIONBAR_SECURE_TEMPLATE_BAR)
|
||||
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent)
|
||||
bars[i]:SetID(i)
|
||||
|
||||
-- autohide
|
||||
|
||||
+1
-1
@@ -396,7 +396,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
if tpl == "BankItemButtonGenericTemplate" then
|
||||
local bankslot = pfUI.bags[bag].slots[slot].frame
|
||||
local name = "pfBag" .. bag .. "item" .. slot .. "Cooldown"
|
||||
bankslot.cd = CreateFrame(COOLDOWN_FRAME_TYPE, name, bankslot, "CooldownFrameTemplate")
|
||||
bankslot.cd = CreateFrame("Model", name, bankslot, "CooldownFrameTemplate")
|
||||
bankslot.cd:SetAllPoints(bankslot)
|
||||
bankslot.cd.pfCooldownStyleAnimation = 1
|
||||
bankslot.cd.pfCooldownType = "ALL"
|
||||
|
||||
+22
-7
@@ -7,6 +7,15 @@ pfUI:RegisterModule("buff", function ()
|
||||
|
||||
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)
|
||||
if buff.btype == "HELPFUL" then
|
||||
if C.buffs.separateweapons == "1" then
|
||||
@@ -23,7 +32,8 @@ pfUI:RegisterModule("buff", function ()
|
||||
CreateBackdropShadow(buff)
|
||||
end
|
||||
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex("player", buff.id, buff.btype)
|
||||
local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots
|
||||
local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
|
||||
|
||||
--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
|
||||
@@ -55,16 +65,16 @@ pfUI:RegisterModule("buff", function ()
|
||||
buff.texture:SetTexture(GetInventoryItemTexture("player", 17))
|
||||
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1))
|
||||
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
|
||||
buff.mode = buff.btype
|
||||
buff.expirationTime = aura.expirationTime
|
||||
buff.stackCount = aura.applications
|
||||
buff.spellId = aura.spellId
|
||||
buff.texture:SetTexture(aura.icon)
|
||||
buff.expirationTime = expirationTime
|
||||
buff.stackCount = count
|
||||
buff.spellId = spellId
|
||||
buff.texture:SetTexture(icon)
|
||||
|
||||
if buff.btype == "HARMFUL" then
|
||||
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(aura.dispelName)
|
||||
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType)
|
||||
buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
|
||||
else
|
||||
buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba)
|
||||
@@ -166,6 +176,8 @@ pfUI:RegisterModule("buff", function ()
|
||||
pfUI.buff.wepbuffs.count = 0
|
||||
end
|
||||
|
||||
ScanPlayerAuraSlots()
|
||||
|
||||
for i=1,32 do
|
||||
RefreshBuffButton(pfUI.buff.buffs.buttons[i])
|
||||
end
|
||||
@@ -246,6 +258,9 @@ pfUI:RegisterModule("buff", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
-- CreateBuffButton refreshes each new button from the slot buffers
|
||||
ScanPlayerAuraSlots()
|
||||
|
||||
-- Weapon Buffs
|
||||
pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent)
|
||||
pfUI.buff.wepbuffs.count = 0
|
||||
|
||||
+36
-19
@@ -73,12 +73,19 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
return anchor
|
||||
end
|
||||
|
||||
local function GetBuffData(unit, id, type, selfdebuff)
|
||||
local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, filter)
|
||||
if not aura then return end
|
||||
local remaining = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
|
||||
return remaining, aura.icon, aura.name, aura.applications
|
||||
-- reusable GetAuraSlots buffer, filled once per RefreshBuffBarFrame
|
||||
local auraSlots = {}
|
||||
|
||||
-- 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
|
||||
local remaining = expirationTime > 0 and (expirationTime - GetTime()) or 0
|
||||
return remaining, icon, name, count, dispelType
|
||||
end
|
||||
|
||||
local function StatusBarOnClick()
|
||||
@@ -113,17 +120,22 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
GameTooltip:SetUnitAura("player", this.id, this.type)
|
||||
elseif this.type == "HARMFUL" then
|
||||
-- selfdebuff filters the displayed list to player-cast harmful auras, but
|
||||
-- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
|
||||
-- up the displayed aura via the PLAYER filter, then scan engine slots for
|
||||
-- one whose name + sourceGUID match.
|
||||
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up
|
||||
-- the displayed aura via the PLAYER filter, then find its position in the
|
||||
-- unfiltered list by name + sourceGUID.
|
||||
--
|
||||
-- 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
|
||||
if config and config.selfdebuff == "1" then
|
||||
local ownAura = C_UnitAuras.GetAuraDataByIndex(this.unit, this.id, "HARMFUL|PLAYER")
|
||||
if ownAura then
|
||||
for gameSlot = 1, 16 do
|
||||
local check = C_UnitAuras.GetDebuffDataByIndex(this.unit, gameSlot)
|
||||
local n = ScanAuraSlots(this.unit, "HARMFUL", tooltipSlots)
|
||||
for i = 1, n do
|
||||
local check = C_UnitAuras.GetAuraDataBySlot(this.unit, tooltipSlots[i])
|
||||
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
|
||||
GameTooltip:SetUnitAura(this.unit, gameSlot, "HARMFUL")
|
||||
GameTooltip:SetUnitAura(this.unit, i, "HARMFUL")
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -234,9 +246,15 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
local function RefreshBuffBarFrame(frame)
|
||||
-- reinitialize all active buffs
|
||||
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
|
||||
local timeleft, texture, name, stacks = GetBuffData(frame.unit, i, frame.type, selfdebuff)
|
||||
local timeleft, texture, name, stacks, dtype = GetBuffData(frame.unit, auraSlots[i])
|
||||
timeleft = timeleft or 0
|
||||
|
||||
if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then
|
||||
@@ -245,12 +263,14 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame.buffs[i][3] = name
|
||||
frame.buffs[i][4] = texture
|
||||
frame.buffs[i][5] = stacks
|
||||
frame.buffs[i][6] = dtype
|
||||
else
|
||||
frame.buffs[i][1] = 0
|
||||
frame.buffs[i][2] = nil
|
||||
frame.buffs[i][3] = nil
|
||||
frame.buffs[i][4] = nil
|
||||
frame.buffs[i][5] = 0
|
||||
frame.buffs[i][6] = nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -294,12 +314,9 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
-- calculate dynamic auto color
|
||||
local r, g, b
|
||||
if frame.type == "HARMFUL" then
|
||||
r, g, b = 1, .2, .2
|
||||
local a = C_UnitAuras.GetDebuffDataByIndex(frame.unit, data[2])
|
||||
local dtype = a and a.dispelName
|
||||
if dtype and DebuffTypeColor[dtype] then
|
||||
r,g,b = DebuffTypeColor[dtype].r,DebuffTypeColor[dtype].g,DebuffTypeColor[dtype].b
|
||||
end
|
||||
-- official Blizzard dispel-type colors (matches unitframes/buff);
|
||||
-- nil/unknown type falls back to the DEBUFF_TYPE_NONE colour
|
||||
r, g, b = C_UnitAuras.GetAuraDispelTypeColor(data[6] or ""):GetRGBA()
|
||||
else
|
||||
r,g,b = str2rgb(data[3])
|
||||
end
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
if not cb.tradeskillTotal or not cb.activeName or not cb.showname then return end
|
||||
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
|
||||
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
|
||||
cb.bar.left:SetText(cb.activeName)
|
||||
end
|
||||
|
||||
+97
-23
@@ -63,6 +63,23 @@ pfUI:RegisterModule("chat", function ()
|
||||
return pfUI_cache["chathistory"][realm][player][id]
|
||||
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.left = CreateFrame("Frame", "pfChatLeft", UIParent)
|
||||
@@ -76,19 +93,11 @@ pfUI:RegisterModule("chat", function ()
|
||||
pfUI.chat.left:SetPoint("BOTTOMLEFT", 2*default_border,2*default_border)
|
||||
pfUI.chat.left:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
|
||||
UpdateMovable(pfUI.chat.left)
|
||||
CreateBackdrop(pfUI.chat.left, default_border, nil, .8)
|
||||
ApplyPanelColors(pfUI.chat.left)
|
||||
if C.chat.global.frameshadow == "1" then
|
||||
CreateBackdropShadow(pfUI.chat.left)
|
||||
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:ClearAllPoints()
|
||||
pfUI.chat.left.panelTop:SetHeight(C.global.font_size+default_border*2)
|
||||
@@ -251,19 +260,11 @@ pfUI:RegisterModule("chat", function ()
|
||||
pfUI.chat.right:SetPoint("BOTTOMRIGHT", -2*default_border,2*default_border)
|
||||
pfUI.chat.right:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
|
||||
UpdateMovable(pfUI.chat.right)
|
||||
CreateBackdrop(pfUI.chat.right, default_border, nil, .8)
|
||||
ApplyPanelColors(pfUI.chat.right)
|
||||
if C.chat.global.frameshadow == "1" then
|
||||
CreateBackdropShadow(pfUI.chat.right)
|
||||
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:ClearAllPoints()
|
||||
pfUI.chat.right.panelTop:SetHeight(C.global.font_size+default_border*2)
|
||||
@@ -296,6 +297,68 @@ pfUI:RegisterModule("chat", function ()
|
||||
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()
|
||||
local panelheight = C.global.font_size*1.5 + default_border*2 + 2
|
||||
|
||||
@@ -447,9 +510,13 @@ pfUI:RegisterModule("chat", function ()
|
||||
for index, value in pairs(DOCKED_CHAT_FRAMES) do
|
||||
FCF_UpdateButtonSide(value)
|
||||
end
|
||||
|
||||
pfUI.chat:RefreshBackgroundAlpha()
|
||||
end
|
||||
|
||||
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
|
||||
pfUI.chat.mouseovertab = CreateFrame("Frame")
|
||||
@@ -500,7 +567,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
FCF_SetLocked(ChatFrame1, 1)
|
||||
FCF_SetWindowName(ChatFrame1, GENERAL)
|
||||
FCF_SetWindowColor(ChatFrame1, 0, 0, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame1, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame1, 0.8)
|
||||
FCF_SetChatWindowFontSize(ChatFrame1, 12)
|
||||
ChatFrame1:SetUserPlaced(1)
|
||||
|
||||
@@ -508,7 +575,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
FCF_SetLocked(ChatFrame2, 1)
|
||||
FCF_SetWindowName(ChatFrame2, COMBAT_LOG)
|
||||
FCF_SetWindowColor(ChatFrame2, 0, 0, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame2, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame2, 0.8)
|
||||
FCF_SetChatWindowFontSize(ChatFrame2, 12)
|
||||
ChatFrame2:SetUserPlaced(1)
|
||||
|
||||
@@ -518,7 +585,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
FCF_SetLocked(ChatFrame3, 1)
|
||||
FCF_SetWindowName(ChatFrame3, T["Loot & Spam"])
|
||||
FCF_SetWindowColor(ChatFrame3, 0, 0, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame3, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame3, 0.8)
|
||||
FCF_SetChatWindowFontSize(ChatFrame3, 12)
|
||||
FCF_UnDockFrame(ChatFrame3)
|
||||
FCF_SetTabPosition(ChatFrame3, 0)
|
||||
@@ -566,6 +633,9 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
pfUI.chat:SetScript("OnEvent", function()
|
||||
-- restore legacy chat windows stuck at 0 alpha before anything reads it
|
||||
pfUI.chat:MigrateBackgroundAlpha()
|
||||
|
||||
-- set the default chat
|
||||
FCF_SelectDockFrame(SELECTED_CHAT_FRAME)
|
||||
|
||||
@@ -757,7 +827,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
local real, _ = strsplit(":", name)
|
||||
local color = unknowncolorhex
|
||||
local match = false
|
||||
local _, class = C_PlayerCache.GetPlayerInfoByName(real)
|
||||
local _, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
|
||||
-- local guid = GetCurrentChatGUID()
|
||||
-- if guid then
|
||||
-- _, class = GetPlayerInfoByGUID(guid)
|
||||
@@ -775,8 +845,12 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
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(.-:-)",
|
||||
left..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1")
|
||||
left..icon..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+28
-18
@@ -6,30 +6,35 @@ pfUI:RegisterModule("cooldown", function ()
|
||||
-- local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
|
||||
-- local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
|
||||
|
||||
local parent, parent_name
|
||||
local function pfCooldownOnUpdate()
|
||||
parent = this:GetParent()
|
||||
-- Throttle FIRST. One of these runs per visible cooldown text, every frame,
|
||||
-- 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
|
||||
parent_name = parent:GetName()
|
||||
|
||||
-- avoid to set cooldowns on invalid frames
|
||||
if parent_name and _G[parent_name .. "Cooldown"] then
|
||||
if not _G[parent_name .. "Cooldown"]:IsShown() then
|
||||
this:Hide()
|
||||
end
|
||||
-- avoid to set cooldowns on invalid frames. The cooldown frame is stashed
|
||||
-- at creation: resolving it as _G[parent:GetName() .. "Cooldown"] built and
|
||||
-- interned that string twice per call, and this is the hottest path in the
|
||||
-- UI. The stashed reference is also the frame itself rather than a guess
|
||||
-- from its parent's name, so it holds for cooldowns named anything else.
|
||||
if this.cooldown and not this.cooldown:IsShown() then
|
||||
this:Hide()
|
||||
return
|
||||
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)
|
||||
if this:GetAlpha() ~= parent:GetAlpha() then
|
||||
this:SetAlpha(parent:GetAlpha())
|
||||
end
|
||||
|
||||
if this.start < GetTime() then
|
||||
if this.start < now then
|
||||
-- calculating remaining time as it should be
|
||||
local remaining = this.duration - (GetTime() - this.start)
|
||||
local remaining = this.duration - (now - this.start)
|
||||
if remaining >= 0 then
|
||||
this.text:SetText(GetColoredTimeString(remaining))
|
||||
else
|
||||
@@ -38,13 +43,13 @@ pfUI:RegisterModule("cooldown", function ()
|
||||
else
|
||||
-- I have absolutely no idea, but it works:
|
||||
-- https://github.com/Stanzilla/WoWUIBugs/issues/47
|
||||
local time = time()
|
||||
local startupTime = time - GetTime()
|
||||
local currentTime = time()
|
||||
local startupTime = currentTime - now
|
||||
-- just a simplification of: ((2^32) - (start * 1000)) / 1000
|
||||
local cdTime = (2 ^ 32) / 1000 - this.start
|
||||
local cdStartTime = startupTime - cdTime
|
||||
local cdEndTime = cdStartTime + this.duration
|
||||
local remaining = cdEndTime - time
|
||||
local remaining = cdEndTime - currentTime
|
||||
|
||||
if remaining >= 0 then
|
||||
this.text:SetText(GetColoredTimeString(remaining))
|
||||
@@ -55,11 +60,16 @@ pfUI:RegisterModule("cooldown", function ()
|
||||
end
|
||||
|
||||
local height, size
|
||||
local textcount = 0
|
||||
local function pfCreateCoolDown(cooldown, start, duration)
|
||||
cooldown.pfCooldownText = CreateFrame("Frame", "pfCooldownFrame", cooldown:GetParent())
|
||||
textcount = textcount + 1
|
||||
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:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2)
|
||||
cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString("pfCooldownFrameText", "OVERLAY")
|
||||
cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString(name .. "TextString", "OVERLAY")
|
||||
|
||||
if not cooldown.pfCooldownType then
|
||||
size = tonumber(C.appearance.cd.font_size_foreign)
|
||||
|
||||
+6
-11
@@ -127,7 +127,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
-- welcome dialog
|
||||
pfUI.firstrun:AddStep("init", function()
|
||||
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
|
||||
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.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Modern:SetWidth(120)
|
||||
f.Modern:SetHeight(20)
|
||||
f.Modern:SetSize(120, 20)
|
||||
f.Modern:SetPoint("BOTTOM", -65, 100)
|
||||
f.Modern:SetTextColor(1,1,1)
|
||||
f.Modern:SetText("Modern")
|
||||
@@ -151,8 +150,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinButton(f.Modern)
|
||||
|
||||
f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Nostalgia:SetWidth(120)
|
||||
f.Nostalgia:SetHeight(20)
|
||||
f.Nostalgia:SetSize(120, 20)
|
||||
f.Nostalgia:SetPoint("BOTTOM", 65, 100)
|
||||
f.Nostalgia:SetTextColor(1,1,1)
|
||||
f.Nostalgia:SetText("Nostalgia")
|
||||
@@ -165,8 +163,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinButton(f.Nostalgia)
|
||||
|
||||
f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Legacy:SetWidth(120)
|
||||
f.Legacy:SetHeight(20)
|
||||
f.Legacy:SetSize(120, 20)
|
||||
f.Legacy:SetPoint("BOTTOM", 65, 75)
|
||||
f.Legacy:SetTextColor(1,1,1)
|
||||
f.Legacy:SetText("Legacy")
|
||||
@@ -179,8 +176,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinButton(f.Legacy)
|
||||
|
||||
f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Slim:SetWidth(120)
|
||||
f.Slim:SetHeight(20)
|
||||
f.Slim:SetSize(120, 20)
|
||||
f.Slim:SetPoint("BOTTOM", -65, 75)
|
||||
f.Slim:SetTextColor(1,1,1)
|
||||
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:SetText(T["Scale"])
|
||||
|
||||
f.Slider:SetWidth(240)
|
||||
f.Slider:SetHeight(20)
|
||||
f.Slider:SetSize(240, 20)
|
||||
f.Slider:SetPoint("BOTTOM", 0, 50)
|
||||
f.Slider:SetOrientation('HORIZONTAL')
|
||||
f.Slider:SetMinMaxValues(0.5, 2.0)
|
||||
|
||||
+4
-3
@@ -2867,6 +2867,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
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 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["Who Search Unknown Classes (|cffffaaaaExperimental|r)"], C.chat.text, "whosearchunknown", "checkbox")
|
||||
CreateConfig(nil, T["Colorize Unknown Classes"], C.chat.text, "tintunknown", "checkbox")
|
||||
@@ -2880,9 +2881,9 @@ pfUI:RegisterModule("gui", function ()
|
||||
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 Frame Shadow"], C.chat.global, "frameshadow", "checkbox")
|
||||
CreateConfig(nil, T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox")
|
||||
CreateConfig(nil, T["Chat Background Color"], C.chat.global, "background", "color")
|
||||
CreateConfig(nil, T["Chat Border Color"], C.chat.global, "border", "color")
|
||||
CreateConfig(U["chat"], T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox")
|
||||
CreateConfig(U["chat"], T["Chat Background Color"], C.chat.global, "background", "color")
|
||||
CreateConfig(U["chat"], 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["Incoming Whispers Color"], C.chat.global, "whisper", "color")
|
||||
CreateConfig(nil, T["Enable Sticky Chat"], C.chat.global, "sticky", "checkbox")
|
||||
|
||||
+1
-1
@@ -511,7 +511,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
end
|
||||
|
||||
function pfUI.loot:CreateSlot(id)
|
||||
local frame = CreateFrame(LOOT_BUTTON_FRAME_TYPE, 'pfLootButton'..id, pfUI.loot)
|
||||
local frame = CreateFrame("LootButton", 'pfLootButton'..id, pfUI.loot)
|
||||
frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp')
|
||||
frame:SetPoint("LEFT", border*2, 0)
|
||||
frame:SetPoint("RIGHT", -border*2, 0)
|
||||
|
||||
+6
-17
@@ -104,7 +104,6 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
-- Frame pools
|
||||
-- ==========================================================================
|
||||
local itemFrames = {}
|
||||
local usedPlayers, freePlayers = {}, {}
|
||||
|
||||
local FullUpdate -- forward declaration (toggle handlers call it)
|
||||
|
||||
@@ -204,20 +203,10 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
return f
|
||||
end
|
||||
|
||||
local function RecycleAllPlayers()
|
||||
for i = 1, table.getn(usedPlayers) do
|
||||
local pf = usedPlayers[i]
|
||||
pf:Hide()
|
||||
table.insert(freePlayers, pf)
|
||||
end
|
||||
usedPlayers = {}
|
||||
end
|
||||
|
||||
local function GetPlayerFrame()
|
||||
local pf = table.remove(freePlayers) or CreatePlayerFrame()
|
||||
table.insert(usedPlayers, pf)
|
||||
return pf
|
||||
end
|
||||
local playerPool = CreateObjectPool(CreatePlayerFrame, function(_, pf)
|
||||
pf:Hide()
|
||||
pf:ClearAllPoints()
|
||||
end)
|
||||
|
||||
local function SetToggleTexture(toggle, isExpanded)
|
||||
if isExpanded then
|
||||
@@ -309,7 +298,7 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
|
||||
function FullUpdate()
|
||||
if not pfUI.loothistory:IsShown() then return end
|
||||
RecycleAllPlayers()
|
||||
playerPool:ReleaseAll()
|
||||
|
||||
local num = C_LootHistory.GetNumItems()
|
||||
local y = -2
|
||||
@@ -327,7 +316,7 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
for p = 1, f.numPlayers do
|
||||
local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p)
|
||||
if ShouldDisplayPlayer(f.isDone, roll, isMe) then
|
||||
local pf = GetPlayerFrame()
|
||||
local pf = playerPool:Acquire()
|
||||
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
|
||||
pf:ClearAllPoints()
|
||||
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
|
||||
|
||||
+1
-1
@@ -181,7 +181,7 @@ pfUI:RegisterModule("map", function ()
|
||||
end
|
||||
|
||||
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
|
||||
WorldMapButton.coords.text:SetText("")
|
||||
end
|
||||
|
||||
+42
-44
@@ -30,20 +30,14 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
pfUI.mapreveal:UpdateConfig()
|
||||
end)
|
||||
|
||||
local explores = {}
|
||||
local explorecaches = {}
|
||||
local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } }
|
||||
|
||||
-- Own texture pool - separate from Blizzard's WorldMapOverlay textures
|
||||
local pfOverlays = {}
|
||||
local pfOverlayMax = 0
|
||||
|
||||
local function pfGetOverlay(idx)
|
||||
if not pfOverlays[idx] then
|
||||
pfOverlays[idx] = WorldMapDetailFrame:CreateTexture("pfReveal"..idx, "BORDER")
|
||||
end
|
||||
return pfOverlays[idx]
|
||||
end
|
||||
local overlayPool = CreateTexturePool(WorldMapDetailFrame, "BORDER", nil, nil, function(_, tex)
|
||||
tex:Hide()
|
||||
tex:ClearAllPoints()
|
||||
end)
|
||||
|
||||
local exploreEnter = function()
|
||||
WorldMapTooltip:ClearLines()
|
||||
@@ -69,14 +63,38 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
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()
|
||||
-- clear stale caches
|
||||
for k in pairs(explorecaches) do explorecaches[k] = nil end
|
||||
|
||||
-- hide all our textures from last frame
|
||||
for i = 1, pfOverlayMax do
|
||||
pfOverlays[i]:Hide()
|
||||
end
|
||||
overlayPool:ReleaseAll()
|
||||
|
||||
local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color)
|
||||
local mapFileName = GetMapInfo()
|
||||
@@ -94,14 +112,13 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
local zoneKnown = alreadyknown[mapFileName]
|
||||
|
||||
-- hide explore icons
|
||||
for _, frame in pairs(explores) do frame:Hide() end
|
||||
explorePool:ReleaseAll()
|
||||
|
||||
-- ClassicAPI: full overlay list for the viewed zone (explored + unexplored),
|
||||
-- read straight from WorldMapOverlay.dbc. Replaces the hand-measured pfMapOverlayData.
|
||||
local zoneData = C_Map.GetMapOverlays() or {}
|
||||
local textureCount = 0
|
||||
|
||||
for i, overlay in ipairs(zoneData) do
|
||||
for _, overlay in ipairs(zoneData) do
|
||||
local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE"
|
||||
local textureName = overlay.texturePath -- full engine path (for SetTexture)
|
||||
local textureWidth = overlay.textureWidth
|
||||
@@ -109,30 +126,15 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
local offsetX = overlay.offsetX
|
||||
local offsetY = overlay.offsetY
|
||||
|
||||
-- explore magnifying glass icon
|
||||
explores[i] = explores[i] or CreateFrame("Frame", nil, WorldMapDetailFrame)
|
||||
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.
|
||||
-- explore magnifying glass icon. `alreadyknown` stores the FULL paths
|
||||
-- GetMapOverlayInfo returns, so compare with the full path, not the bare
|
||||
-- name.
|
||||
if C.appearance.worldmap.mapexploration == "1" and not zoneKnown[string.upper(textureName)] then
|
||||
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
|
||||
local explore = explorePool:Acquire()
|
||||
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()
|
||||
else
|
||||
explore:Hide()
|
||||
end
|
||||
|
||||
-- render overlay texture tiles on BORDER draw layer
|
||||
@@ -147,11 +149,9 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
-- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun).
|
||||
if C.appearance.worldmap.mapreveal == "1" then
|
||||
for _, tile in ipairs(overlay.tiles) do
|
||||
textureCount = textureCount + 1
|
||||
local tex = pfGetOverlay(textureCount)
|
||||
local tex = overlayPool:Acquire()
|
||||
|
||||
tex:SetWidth(tile.width)
|
||||
tex:SetHeight(tile.height)
|
||||
tex:SetSize(tile.width, tile.height)
|
||||
tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY)
|
||||
tex:ClearAllPoints()
|
||||
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY)
|
||||
@@ -165,8 +165,6 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pfOverlayMax = math.max(pfOverlayMax, textureCount)
|
||||
end
|
||||
|
||||
-- hook WorldMapFrame_Update
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ pfUI:RegisterModule("minimap", function ()
|
||||
local coord = pfUI.minimapCoordinates
|
||||
coord.posX, coord.posY = GetPlayerMapPosition("player")
|
||||
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
|
||||
coord.text:SetText("|cffffaaaaN/A")
|
||||
end
|
||||
|
||||
+128
-84
@@ -116,6 +116,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
-- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation)
|
||||
local debuffDisplayBuf = {} -- [i] = { effect, texture, stacks, dtype, duration, timeleft }
|
||||
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 debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn)
|
||||
|
||||
@@ -296,18 +297,23 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
return plate.creatureType
|
||||
end
|
||||
|
||||
-- Totem icon, read straight from the game:
|
||||
-- * Passive totems self-cast their provided buff, so they carry exactly one
|
||||
-- aura whose icon IS the totem's icon (Totem::Summon TOTEM_PASSIVE).
|
||||
-- * Active totems (Searing/Magma/Fire Nova) cast at enemies and hold no
|
||||
-- self-aura, so their icon arrives via UNIT_SPELLCAST_SUCCEEDED (cached
|
||||
-- into plate.totemIcon by the event handler); nil here until then.
|
||||
-- Totem icon: UnitCreatedBySpell returns the totem-drop spell, whose icon IS
|
||||
-- the totem's icon. It's a broadcast descriptor field the client has for every
|
||||
-- summoned unit in range, so it resolves immediately for passive and active
|
||||
-- totems alike -- no self-aura read or attack-cast capture needed.
|
||||
--
|
||||
-- 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)
|
||||
if C.nameplates.totemicons ~= "1" then return nil end
|
||||
if CreatureType(plate) ~= 11 then return nil end
|
||||
if plate.totemIcon then return plate.totemIcon end
|
||||
local aura = C_UnitAuras.GetBuffDataByIndex(plate.cachedGuid, 1)
|
||||
if aura then plate.totemIcon = aura.icon end
|
||||
local spellId = UnitCreatedBySpell(plate.cachedGuid)
|
||||
if spellId ~= plate.totemSpell then
|
||||
plate.totemSpell = spellId
|
||||
plate.totemIcon = spellId and C_Spell.GetSpellTexture(spellId) or nil
|
||||
end
|
||||
return plate.totemIcon
|
||||
end
|
||||
|
||||
@@ -427,7 +433,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
plate.debuffs[index].cd.SetSequenceTime = DoNothing
|
||||
else
|
||||
-- Use CooldownFrameTemplate for animation
|
||||
plate.debuffs[index].cd = CreateFrame(COOLDOWN_FRAME_TYPE, plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate")
|
||||
plate.debuffs[index].cd = CreateFrame("Model", plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate")
|
||||
plate.debuffs[index].cd:SetAllPoints(plate.debuffs[index])
|
||||
plate.debuffs[index].cd:SetFrameLevel(6)
|
||||
end
|
||||
@@ -499,7 +505,6 @@ nameplates:RegisterEvent("UNIT_SPELLCAST_START")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_STOP")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
|
||||
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
|
||||
nameplates:SetScript("OnEvent", function()
|
||||
@@ -588,6 +593,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
plate.nameplate.unit = arg1
|
||||
plate.nameplate.creatureType = nil -- recompute for the new unit
|
||||
plate.nameplate.totemIcon = nil
|
||||
plate.nameplate.totemSpell = nil
|
||||
if guid then
|
||||
plateByGuid[guid] = plate.nameplate
|
||||
-- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START
|
||||
@@ -665,22 +671,6 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
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
|
||||
@@ -781,6 +771,8 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
DisableObject(nameplate.original.healthbar)
|
||||
DisableObject(nameplate.original.castbar)
|
||||
|
||||
local NAMEPLATE_OBJECTORDER = { "border", "glow", "name", "level", "levelicon", "raidicon" }
|
||||
|
||||
for i, object in pairs({parent:GetRegions()}) do
|
||||
if NAMEPLATE_OBJECTORDER[i] and NAMEPLATE_OBJECTORDER[i] == "raidicon" then
|
||||
nameplate[NAMEPLATE_OBJECTORDER[i]] = object
|
||||
@@ -1039,13 +1031,29 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
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
|
||||
|
||||
-- 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
|
||||
-- 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.
|
||||
if plate.cache.player == nil and unitstr then
|
||||
plate.cache.player = UnitIsPlayer(unitstr) and true or false
|
||||
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
|
||||
|
||||
-- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels)
|
||||
@@ -1065,18 +1073,11 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_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
|
||||
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
|
||||
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
|
||||
plate:Show()
|
||||
|
||||
@@ -1110,7 +1111,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
local TotemIcon = TotemPlate(plate)
|
||||
|
||||
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.glow:Hide()
|
||||
@@ -1151,15 +1152,24 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
if plate.cache.level ~= level or plate.cache.elite ~= elite then
|
||||
plate.cache.level = level
|
||||
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
|
||||
|
||||
-- Set level color from GetDifficultyColor when using DB level
|
||||
-- Set level color from GetDifficultyColor when using DB level.
|
||||
-- No brightening here: adding a flat offset to all three channels
|
||||
-- desaturates every tier toward white and collapses the boundaries
|
||||
-- (orange reads as yellow, red reads as orange). Use Blizzard's values.
|
||||
-- Clearing cache.levelcolor forces the sync block in OnUpdate to
|
||||
-- re-colour once the ?? resolves and it takes ownership again.
|
||||
if levelFromDB and type(level) == "number" then
|
||||
local color = GetDifficultyColor(level)
|
||||
plate.level:SetTextColor(color.r + 0.3, color.g + 0.3, color.b + 0.3, 1)
|
||||
plate.level:SetTextColor(color.r, color.g, color.b, 1)
|
||||
plate.cache.levelcolor = nil
|
||||
end
|
||||
|
||||
-- remember who owns the level colour so the two writers cannot fight
|
||||
plate.cache.levelfromdb = levelFromDB or nil
|
||||
|
||||
if guild and C.nameplates.showguildname == "1" then
|
||||
plate.guild:SetText(guild)
|
||||
if guild == myGuild then
|
||||
@@ -1203,21 +1213,21 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
local hasdata = ( rhp and rhpmax ) or estimated or hpmax > 100 or (round(hpmax/100*hp) ~= hp)
|
||||
|
||||
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
|
||||
plate.health.text:SetText(string.format("%s", Abbreviate(rhp)))
|
||||
plate.health.text:SetFormattedText("%s", Abbreviate(rhp))
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
plate.health.text:SetText(string.format("%s%%", ceil(hp/hpmax*100)))
|
||||
plate.health.text:SetFormattedText("%s%%", ceil(hp/hpmax*100))
|
||||
end
|
||||
else
|
||||
plate.health.text:SetText()
|
||||
@@ -1251,9 +1261,32 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
plate.cache.r, plate.cache.g, plate.cache.b = r, g, b
|
||||
end
|
||||
|
||||
if r + g + b ~= plate.cache.namecolor and unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassnamec"] == "1" and class and PFUI_CLASS_COLORS[class] then
|
||||
plate.name:SetTextColor(r, g, b, a)
|
||||
plate.cache.namecolor = r + g + b
|
||||
-- Friendly player names take this colour when friendclassnamec is on.
|
||||
-- Ownership is recorded so the OnUpdate sync below stands down rather than
|
||||
-- racing us: both writers used to share cache.namecolor despite storing
|
||||
-- unrelated quantities (this colour vs Blizzard's name FontString), so
|
||||
-- either could suppress the other -- and since nameplate.cache survives
|
||||
-- pool reuse, a recycled plate could keep the previous unit's name colour.
|
||||
local ownname = unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassnamec"] == "1"
|
||||
and class and true or nil
|
||||
|
||||
if plate.cache.ownname ~= ownname then
|
||||
plate.cache.ownname = ownname
|
||||
-- ownership flipped: whichever writer is now in charge must re-assert
|
||||
plate.cache.namecolor = nil
|
||||
plate.cache.ownnamecolor = nil
|
||||
end
|
||||
|
||||
-- read the class colour directly rather than reusing the bar's r,g,b: the
|
||||
-- bar only carries a class colour when friendclassc happens to be on, and
|
||||
-- it also picks up the tapped-grey and barcombatstate overrides, neither of
|
||||
-- which belongs on the name.
|
||||
if ownname then
|
||||
local cr, cg, cb, ca = PFUI_CLASS_COLORS[class]:GetRGBA()
|
||||
if cr + cg + cb ~= plate.cache.ownnamecolor then
|
||||
plate.cache.ownnamecolor = cr + cg + cb
|
||||
plate.name:SetTextColor(cr, cg, cb, ca)
|
||||
end
|
||||
end
|
||||
|
||||
if target and C.nameplates.cpdisplay == "1" then
|
||||
@@ -1281,19 +1314,22 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
for i = 1, 16 do debuffDisplayBuf[i].effect = nil end
|
||||
if unitstr then
|
||||
local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL"
|
||||
local auras = C_UnitAuras.GetUnitAuras(unitstr, filter)
|
||||
local now = GetTime()
|
||||
for _, aura in ipairs(auras) do
|
||||
if debuffCount >= 16 then break end
|
||||
-- one GetAuraSlots enumeration, then positional by-slot reads straight
|
||||
-- into the reusable buffer: the per-plate scan allocates nothing (no
|
||||
-- per-aura table, no result array) and walks the aura array once
|
||||
local n = ScanAuraSlots(unitstr, filter, auraSlots, 16)
|
||||
for i = 1, n do
|
||||
local aname, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if not aname then break end
|
||||
debuffCount = debuffCount + 1
|
||||
local timeleft = (aura.expirationTime and aura.expirationTime > 0) and (aura.expirationTime - now) or nil
|
||||
local b = debuffDisplayBuf[debuffCount]
|
||||
b.effect = aura.name
|
||||
b.texture = aura.icon
|
||||
b.stacks = aura.applications
|
||||
b.dtype = aura.dispelName
|
||||
b.duration = aura.duration
|
||||
b.timeleft = timeleft
|
||||
b.effect = aname
|
||||
b.texture = icon
|
||||
b.stacks = count
|
||||
b.dtype = dispelType
|
||||
b.duration = duration
|
||||
b.timeleft = (expirationTime and expirationTime > 0) and (expirationTime - now) or nil
|
||||
end
|
||||
end
|
||||
for i = 1, 16 do
|
||||
@@ -1516,38 +1552,46 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
-- 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
|
||||
-- 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
|
||||
update = true
|
||||
end
|
||||
|
||||
-- trigger update when name color changed (includes combat state check)
|
||||
local r, g, b = original.name:GetTextColor()
|
||||
local inCombatWithPlayer = cfg.namefightcolor and UnitAffectingCombat(nameplate.unit) and UnitAffectingCombat("player")
|
||||
|
||||
if r + g + b ~= nameplate.cache.namecolor or (cfg.namefightcolor and nameplate.cache.inCombat ~= inCombatWithPlayer) then
|
||||
nameplate.cache.namecolor = r + g + b
|
||||
nameplate.cache.inCombat = inCombatWithPlayer
|
||||
-- trigger update when name color changed (includes combat state check).
|
||||
-- Skipped once OnDataChanged owns the name colour (class-coloured friendly
|
||||
-- players), so the two writers cannot overwrite each other.
|
||||
if not nameplate.cache.ownname then
|
||||
local r, g, b = original.name:GetTextColor()
|
||||
local inCombatWithPlayer = cfg.namefightcolor and UnitAffectingCombat(nameplate.unit) and UnitAffectingCombat("player")
|
||||
|
||||
if cfg.namefightcolor then
|
||||
if (r > .9 and g < .2 and b < .2) or inCombatWithPlayer then
|
||||
nameplate.name:SetTextColor(1,0.4,0.2,1)
|
||||
if r + g + b ~= nameplate.cache.namecolor or (cfg.namefightcolor and nameplate.cache.inCombat ~= inCombatWithPlayer) then
|
||||
nameplate.cache.namecolor = r + g + b
|
||||
nameplate.cache.inCombat = inCombatWithPlayer
|
||||
|
||||
if cfg.namefightcolor then
|
||||
if (r > .9 and g < .2 and b < .2) or inCombatWithPlayer then
|
||||
nameplate.name:SetTextColor(1,0.4,0.2,1)
|
||||
else
|
||||
nameplate.name:SetTextColor(r,g,b,1)
|
||||
end
|
||||
else
|
||||
nameplate.name:SetTextColor(r,g,b,1)
|
||||
nameplate.name:SetTextColor(1,1,1,1)
|
||||
end
|
||||
else
|
||||
nameplate.name:SetTextColor(1,1,1,1)
|
||||
update = true
|
||||
end
|
||||
update = true
|
||||
end
|
||||
|
||||
-- trigger update when level color changed
|
||||
local r, g, b = original.level:GetTextColor()
|
||||
r, g, b = r + .3, g + .3, b + .3
|
||||
if r + g + b ~= nameplate.cache.levelcolor then
|
||||
nameplate.cache.levelcolor = r + g + b
|
||||
nameplate.level:SetTextColor(r,g,b,1)
|
||||
update = true
|
||||
-- trigger update when level color changed. Skipped while the level came
|
||||
-- from the database (?? plates), where OnDataChanged owns the colour --
|
||||
-- otherwise both writers race and cache.levelcolor goes stale, which
|
||||
-- leaves a recycled plate wearing the previous unit's colour.
|
||||
if not nameplate.cache.levelfromdb then
|
||||
local r, g, b = original.level:GetTextColor()
|
||||
if r + g + b ~= nameplate.cache.levelcolor then
|
||||
nameplate.cache.levelcolor = r + g + b
|
||||
nameplate.level:SetTextColor(r,g,b,1)
|
||||
update = true
|
||||
end
|
||||
end
|
||||
|
||||
-- use timer based updates
|
||||
@@ -1657,7 +1701,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
rounded = floor(remaining * 100)
|
||||
if castbar.lastTextTick ~= rounded then
|
||||
castbar.lastTextTick = rounded
|
||||
castbar.text:SetText(string.format("%.2f", remaining))
|
||||
castbar.text:SetFormattedText("%.2f", remaining)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+17
-7
@@ -418,8 +418,13 @@ pfUI:RegisterModule("panel", function()
|
||||
local repPercent = floor(cur / max * 100)
|
||||
if repPercent < 100 then
|
||||
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] = {
|
||||
GetInventoryItemLink("player", id),
|
||||
link,
|
||||
string.format("%s%s%%|r", hex, repPercent)
|
||||
}
|
||||
end
|
||||
@@ -428,8 +433,7 @@ pfUI:RegisterModule("panel", function()
|
||||
if totalRep > 0 then
|
||||
GameTooltip:ClearLines()
|
||||
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
||||
GameTooltip:SetText("|cff555555"..(string.gsub(REPAIR_COST,":","")).."|r")
|
||||
SetTooltipMoney(GameTooltip, totalRep)
|
||||
GameTooltip:AddLine(REPAIR_COST.." " .. CreateGoldString(totalRep), 0.3333, 0.3333, 0.3333)
|
||||
for _,line in ipairs(itemLines) do
|
||||
GameTooltip:AddDoubleLine(line[1],line[2])
|
||||
end
|
||||
@@ -453,7 +457,7 @@ pfUI:RegisterModule("panel", function()
|
||||
|
||||
do -- Zone
|
||||
local widget = CreateFrame("Frame", "pfPanelWidgetZone", UIParent)
|
||||
for _,event in pairs(EVENTS_MINIMAP_ZONE_UPDATE) do
|
||||
for _,event in pairs({"PLAYER_ENTERING_WORLD", "MINIMAP_ZONE_CHANGED"}) do
|
||||
widget:RegisterEvent(event)
|
||||
end
|
||||
widget.Tooltip = function()
|
||||
@@ -773,9 +777,15 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.panel.microbutton:SetSize(145, 23)
|
||||
pfUI.panel.microbutton:SetFrameStrata("MEDIUM")
|
||||
|
||||
for i=1,table.getn(MICRO_BUTTONS) do
|
||||
local anchor = _G[MICRO_BUTTONS[i-1]] or pfUI.panel.microbutton
|
||||
local button = _G[MICRO_BUTTONS[i]]
|
||||
local microButtons = {
|
||||
'CharacterMicroButton', 'SpellbookMicroButton', 'TalentMicroButton',
|
||||
'QuestLogMicroButton', 'SocialsMicroButton', 'WorldMapMicroButton',
|
||||
'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:SetParent(pfUI.panel.microbutton)
|
||||
if i == 1 then
|
||||
|
||||
+24
-2
@@ -125,6 +125,7 @@ pfUI:RegisterModule("raid", function ()
|
||||
local grid = self.petgrid
|
||||
|
||||
local function place(pet, cell, id)
|
||||
pet.label = "raidpet"
|
||||
pet.id = id
|
||||
local r, g = SlotToCoord(cell, grid.fill, grid.x, grid.y)
|
||||
pet:ClearAllPoints()
|
||||
@@ -140,6 +141,22 @@ pfUI:RegisterModule("raid", function ()
|
||||
return
|
||||
end
|
||||
|
||||
-- Party shown as a raid grid: mirror slots 1..5 (player + party members)
|
||||
-- into fixed cells. UpdateVisibility maps each slot to pet / partypet<N>.
|
||||
if not IsInRaid() and IsInGroup() and C.unitframes.raidforgroup == "1" then
|
||||
for id = 1, maxraid do
|
||||
if self.pets[id] then
|
||||
if id <= 5 then
|
||||
place(self.pets[id], id, id)
|
||||
else
|
||||
self.pets[id].id = 0
|
||||
self.pets[id]:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if C.unitframes.raidpet.collapse == "1" then
|
||||
-- Pack the pets that exist into the leading cells, no gaps.
|
||||
local k = 0
|
||||
@@ -213,8 +230,13 @@ pfUI:RegisterModule("raid", function ()
|
||||
this.tick = GetTime() + 1.0
|
||||
this.pendingUpdate = nil
|
||||
|
||||
-- don't proceed without raid
|
||||
if not IsInRaid() then return end
|
||||
-- Without a raid there is nothing to sort, but a party shown as a raid
|
||||
-- grid still needs its pet frames placed and mapped to the party pets.
|
||||
if not IsInRaid() then
|
||||
this:LayoutPets()
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- clear all existing frames
|
||||
for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end
|
||||
|
||||
@@ -119,7 +119,7 @@ pfUI:RegisterModule("screenshot", function ()
|
||||
end
|
||||
|
||||
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
|
||||
local dt = date("%a, %b %d, %Y %X")
|
||||
local loc = string.format("%s - %s",GetRealZoneText(),GetSubZoneText())
|
||||
@@ -150,13 +150,13 @@ pfUI:RegisterModule("screenshot", function ()
|
||||
end
|
||||
|
||||
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
|
||||
return
|
||||
else
|
||||
_,_, item = string.find(arg1, LOOT_ITEM_SELFregex)
|
||||
item = string.match(arg1, LOOT_ITEM_SELFregex)
|
||||
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]
|
||||
if quality and quality >= tonumber(C.screenshot.loot) then
|
||||
local dt = date("%a, %b %d, %Y %X")
|
||||
|
||||
@@ -81,7 +81,7 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
if not info or not info.name or info.name == _G.UNKNOWN then break end
|
||||
local name = info.name
|
||||
local friendName = _G["FriendsFrameFriendButton"..i.."ButtonTextName"]
|
||||
local friendLoc = _G["FriendsFrameFriendButton"..i..FRIENDS_NAME_LOCATION]
|
||||
local friendLoc = _G["FriendsFrameFriendButton"..i.."ButtonTextNameLocation"]
|
||||
local friendInfo = _G["FriendsFrameFriendButton"..i.."ButtonTextInfo"]
|
||||
local caption = friendName or friendLoc
|
||||
|
||||
@@ -97,20 +97,20 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
|
||||
if friendName then
|
||||
friendName:SetText(cname)
|
||||
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), zone, status))
|
||||
friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), zone, status)
|
||||
else
|
||||
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status))
|
||||
friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status)
|
||||
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)
|
||||
friendInfo:SetVertexColor(1,1,1,.9)
|
||||
else
|
||||
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))
|
||||
friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen))
|
||||
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname)
|
||||
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen)
|
||||
else
|
||||
caption:SetText(format(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r"))
|
||||
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r")
|
||||
friendInfo:SetText(TEXT(UNKNOWN))
|
||||
end
|
||||
|
||||
@@ -131,9 +131,9 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
local playerguild = GetGuildInfo("player")
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
for i=1, WHOS_TO_DISPLAY do
|
||||
|
||||
@@ -592,10 +592,10 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
end
|
||||
pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA)
|
||||
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
|
||||
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
|
||||
anyActive = true
|
||||
end
|
||||
@@ -626,10 +626,10 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
end
|
||||
end
|
||||
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
|
||||
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
|
||||
anyActive = true
|
||||
elseif not sw_showoh then
|
||||
@@ -668,9 +668,9 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
end
|
||||
if sw_showtext 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
|
||||
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
|
||||
else
|
||||
@@ -683,11 +683,11 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
pfUI.swingtimer.ranged.right:Hide()
|
||||
pfUI.swingtimer.ranged.warn:Hide()
|
||||
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
|
||||
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
|
||||
local raProgress = 1 - (S.raTimer / S.raTimerMax)
|
||||
local raMarkerX = raProgress * sw_width
|
||||
|
||||
+22
-9
@@ -166,9 +166,9 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
|
||||
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
|
||||
pfUI.tooltipStatusBar.HP:SetText(string.format("%s%%", ceil(hp/hpmax*100)))
|
||||
pfUI.tooltipStatusBar.HP:SetFormattedText("%s%%", ceil(hp/hpmax*100))
|
||||
else
|
||||
pfUI.tooltipStatusBar.HP:SetText("")
|
||||
end
|
||||
@@ -189,6 +189,7 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
-- Icons come from a ClassicAPI object pool: ReleaseAll hides the whole
|
||||
-- set each refresh, then we Acquire and re-anchor left-to-right.
|
||||
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:SetPoint("BOTTOMLEFT", GameTooltipStatusBar, "TOPLEFT", 0, default_border + 2)
|
||||
pfUI.tooltip.buffs:SetHeight(BUFF_SIZE)
|
||||
@@ -239,17 +240,20 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
|
||||
local prev, count = nil, 0
|
||||
for i = 1, BUFF_MAX do
|
||||
local aura = C_UnitAuras.GetBuffDataByIndex(unit, i)
|
||||
if not aura then break end
|
||||
-- one GetAuraSlots enumeration + positional by-slot reads: no per-aura
|
||||
-- table and no per-index re-walk of the aura array
|
||||
local n = ScanAuraSlots(unit, "HELPFUL", auraSlots, BUFF_MAX)
|
||||
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
|
||||
local icon = pfUI.tooltip.buffpool:Acquire()
|
||||
icon.texture:SetTexture(aura.icon)
|
||||
icon.stacks:SetText(aura.applications and aura.applications > 1 and aura.applications or "")
|
||||
icon.texture:SetTexture(texture)
|
||||
icon.stacks:SetText(applications and applications > 1 and applications or "")
|
||||
|
||||
icon.expirationTime = aura.expirationTime
|
||||
local timeleft = aura.expirationTime and aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
|
||||
icon.expirationTime = expirationTime
|
||||
local timeleft = expirationTime and expirationTime > 0 and (expirationTime - GetTime()) or 0
|
||||
icon.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
|
||||
|
||||
if prev then
|
||||
@@ -372,6 +376,15 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
caster = UnitNameFromGUID(aura.sourceGUID)
|
||||
end
|
||||
if caster and caster ~= "" then
|
||||
local classToken
|
||||
if aura.sourceUnit and UnitIsPlayer(aura.sourceUnit) then
|
||||
classToken = UnitClassBase(aura.sourceUnit)
|
||||
elseif aura.sourceGUID then
|
||||
classToken = select(2, GetPlayerInfoByGUID(aura.sourceGUID))
|
||||
end
|
||||
if classToken then
|
||||
caster = PFUI_CLASS_COLORS[classToken]:WrapTextInColorCode(caster)
|
||||
end
|
||||
self:AddLine(T["Cast by"] .. ": " .. caster, .25,.5,1)
|
||||
end
|
||||
end
|
||||
|
||||
+1
-1
@@ -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:SetSize(self.iconsize - 3, self.iconsize - 3)
|
||||
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
|
||||
self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
|
||||
self.bar[i].cd = self.bar[i].cd or CreateFrame("Model", "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
|
||||
self.bar[i].cd.pfCooldownStyleAnimation = 1
|
||||
self.bar[i].cd.pfCooldownType = "ALL"
|
||||
|
||||
|
||||
+25
-92
@@ -1,58 +1,17 @@
|
||||
pfUI:RegisterModule("tracking", function ()
|
||||
|
||||
MINIMAP_TRACKING_FRAME:UnregisterAllEvents()
|
||||
MINIMAP_TRACKING_FRAME:Hide()
|
||||
|
||||
local function HasEntries(tbl)
|
||||
for _ in pairs(tbl) do
|
||||
return true
|
||||
end
|
||||
return nil
|
||||
end
|
||||
_G.MiniMapTrackingFrame:UnregisterAllEvents()
|
||||
_G.MiniMapTrackingFrame:Hide()
|
||||
|
||||
local rawborder, border = GetBorderSize()
|
||||
local size = tonumber(C.appearance.minimap.tracking_size)
|
||||
local pulse = C.appearance.minimap.tracking_pulse == "1"
|
||||
|
||||
-- Tracking spells identified by SpellID + expected icon path.
|
||||
-- SpellID is the primary identifier (stable, locale-independent).
|
||||
-- Icon path is used as secondary confirmation when scanning the spellbook.
|
||||
local knownTrackingSpells = {
|
||||
any = {
|
||||
{ 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
|
||||
-- Tracking spells come from ClassicAPI's native GetNumTrackingTypes /
|
||||
-- GetTrackingInfo (see the DLL's docs/API.md "Tracking"). The DLL
|
||||
-- enumerates them straight from the spellbook by tracking-aura effect,
|
||||
-- so there is no spell table to maintain here and server-custom trackers
|
||||
-- (e.g. Turtle's Find Trees) are picked up automatically.
|
||||
|
||||
local state = {
|
||||
texture = nil,
|
||||
@@ -128,50 +87,24 @@ pfUI:RegisterModule("tracking", function ()
|
||||
end)
|
||||
|
||||
function pfUI.tracking:RefreshSpells()
|
||||
local playerClass = UnitClassBase("player")
|
||||
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(playerClass)
|
||||
state.spells = {}
|
||||
if not GetNumTrackingTypes then return end
|
||||
|
||||
-- Build set of valid SpellIDs for this class
|
||||
local validIds = {}
|
||||
for _, entry in ipairs(knownTrackingSpells.any) do
|
||||
validIds[entry.id] = true
|
||||
end
|
||||
if knownTrackingSpells[playerClass] then
|
||||
for _, entry in ipairs(knownTrackingSpells[playerClass]) do
|
||||
validIds[entry.id] = true
|
||||
end
|
||||
end
|
||||
-- Druid Track Humanoids (5225) only casts in Cat Form. Hide it out of
|
||||
-- form so the menu never offers a tracker that would fail to cast.
|
||||
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(UnitClassBase("player"))
|
||||
|
||||
-- Druids only get Track Humanoids in Cat Form
|
||||
if playerClass == "DRUID" and not isCatForm then
|
||||
validIds[5225] = nil
|
||||
end
|
||||
|
||||
-- Scan spellbook: match by icon path, confirm SpellID is valid for this class
|
||||
for tabIndex = 1, GetNumSpellTabs() do
|
||||
local _, _, offset, numSpells = GetSpellTabInfo(tabIndex)
|
||||
for spellIndex = offset + 1, offset + numSpells do
|
||||
local spellTexture = GetSpellTexture(spellIndex, BOOKTYPE_SPELL)
|
||||
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
|
||||
for i = 1, GetNumTrackingTypes() do
|
||||
local name, texture, _, _, spellId = GetTrackingInfo(i)
|
||||
local castable = not (spellId == 5225 and not isCatForm)
|
||||
if name and texture and castable
|
||||
and not pfUI.tracking.invalidSpells[name] then
|
||||
state.spells[i] = {
|
||||
index = i, -- tracking index, passed to SetTracking()
|
||||
name = name,
|
||||
texture = texture,
|
||||
spellId = spellId,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -187,7 +120,7 @@ pfUI:RegisterModule("tracking", function ()
|
||||
elseif not texture then
|
||||
state.texture = nil
|
||||
|
||||
if pulse and HasEntries(state.spells) then
|
||||
if pulse and next(state.spells) ~= nil then
|
||||
pfUI.tracking.pulse = true
|
||||
pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
pfUI.tracking.icon:SetVertexColor(1,1,1,1)
|
||||
@@ -217,7 +150,7 @@ pfUI:RegisterModule("tracking", function ()
|
||||
checked = spell.texture == state.texture,
|
||||
arg1 = spell,
|
||||
func = function (arg1)
|
||||
CastSpell(arg1.index, BOOKTYPE_SPELL)
|
||||
SetTracking(arg1.index)
|
||||
CloseDropDownMenus()
|
||||
end
|
||||
})
|
||||
|
||||
+1
-2958
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -134,7 +134,7 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
end
|
||||
|
||||
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()
|
||||
end)
|
||||
|
||||
@@ -206,7 +206,7 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
end
|
||||
|
||||
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
|
||||
pfUI.uf.target.distanceIndicator = pfRangeDisplay
|
||||
|
||||
+3
-3
@@ -236,7 +236,7 @@ end
|
||||
local xpperc = round(xp / xpmax * 100)
|
||||
local experc = ex and round(ex / xpmax * 100) or 0
|
||||
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
|
||||
if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then
|
||||
@@ -254,7 +254,7 @@ end
|
||||
|
||||
local text = "%s: %s%%"
|
||||
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
|
||||
return
|
||||
@@ -282,7 +282,7 @@ end
|
||||
local text = "%s: %s%% (%s)"
|
||||
local perc = round(barValue / barMax * 100)
|
||||
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
|
||||
return
|
||||
|
||||
@@ -3,6 +3,8 @@ function SlashCmdList.RELOAD(msg, editbox)
|
||||
ReloadUI()
|
||||
end
|
||||
|
||||
local addonName = ...
|
||||
|
||||
SLASH_PFUI1 = '/pfui'
|
||||
function SlashCmdList.PFUI(msg, editbox)
|
||||
pfUI.gui:SetShown(not pfUI.gui:IsShown())
|
||||
@@ -13,87 +15,19 @@ function SlashCmdList.GM(msg, editbox)
|
||||
ToggleHelpFrame(1)
|
||||
end
|
||||
|
||||
pfUI = CreateFrame("Frame", nil, UIParent)
|
||||
local pfUI = CreateFrame("Frame", addonName, UIParent)
|
||||
pfUI:RegisterEvent("ADDON_LOADED")
|
||||
|
||||
-- setup bootvar
|
||||
pfUI.bootup = true
|
||||
|
||||
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 = 10906 -- (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
|
||||
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 = {}
|
||||
pfUI_playerDB = pfUI_playerDB or {}
|
||||
pfUI_config = pfUI_config or {}
|
||||
pfUI_init = pfUI_init or {}
|
||||
pfUI_profiles = pfUI_profiles or {}
|
||||
pfUI_addon_profiles = pfUI_addon_profiles or {}
|
||||
pfUI_cache = pfUI_cache or {}
|
||||
pfUI_throttle = pfUI_throttle or {}
|
||||
|
||||
-- localization
|
||||
pfUI_locale = {}
|
||||
@@ -110,35 +44,32 @@ pfUI.movables = {}
|
||||
pfUI.version = {}
|
||||
pfUI.env = {}
|
||||
|
||||
if not pfUI.disabled then
|
||||
pfUI.events = Mixin({}, CallbackRegistryMixin)
|
||||
pfUI.events:OnLoad()
|
||||
pfUI.events:SetUndefinedEventsAllowed(true)
|
||||
end
|
||||
-- Capability flag: this fork's pfActionBar buttons correctly handle the modern
|
||||
-- HookScript widget method, so ClassicAPI's AddOnCompat shim must NOT shadow
|
||||
-- HookScript during actionbar load (older forks branch on `if button.HookScript`
|
||||
-- and take a modern path they were never written for). This describes behavior,
|
||||
-- not identity: a fork that keeps the HookScript-correct actionbar code keeps
|
||||
-- the flag; one that lacks it should drop the flag and get the safe fallback.
|
||||
pfUI.handlesHookScript = true
|
||||
|
||||
pfUI.events = Mixin({}, CallbackRegistryMixin)
|
||||
pfUI.events:OnLoad()
|
||||
pfUI.events:SetUndefinedEventsAllowed(true)
|
||||
|
||||
-- check if macro addons are loaded (disables macrotweak/macroscan)
|
||||
function pfUI:MacroAddonsLoaded()
|
||||
return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros")
|
||||
end
|
||||
|
||||
-- detect current addon path
|
||||
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
|
||||
pfUI.name = addonName
|
||||
pfUI.path = "Interface\\AddOns\\" .. addonName
|
||||
|
||||
-- handle/convert media dir paths
|
||||
pfUI.media = setmetatable({}, { __index = function(tab,key)
|
||||
local value = tostring(key)
|
||||
if strfind(value, "img:") then
|
||||
if value:find("img:") then
|
||||
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\\")
|
||||
else
|
||||
value = string.gsub(value, "Interface\\AddOns\\pfUI\\", pfUI.path .. "\\")
|
||||
@@ -147,9 +78,7 @@ pfUI.media = setmetatable({}, { __index = function(tab,key)
|
||||
return value
|
||||
end})
|
||||
|
||||
-- cache client version
|
||||
local _, _, _, client = GetBuildInfo()
|
||||
pfUI.client = client or 11200
|
||||
pfUI.client = INTERFACE_VERSION
|
||||
|
||||
-- setup pfUI namespace
|
||||
setmetatable(pfUI.env, {__index = getfenv(0)})
|
||||
@@ -293,6 +222,20 @@ function pfUI:GetEnvironment()
|
||||
pfUI.env.pfUI_throttle = _G.pfUI_throttle
|
||||
pfUI.env.L = (pfUI_locale[GetLocale()] or pfUI_locale["enUS"])
|
||||
pfUI.env.L["class"] = pfUI.env.L["class"] or tInvert(LOCALIZED_CLASS_NAMES_MALE)
|
||||
if not pfUI.env.L["race"] then
|
||||
pfUI.env.L["race"] = {}
|
||||
local i = 1
|
||||
local raceInfo = C_CreatureInfo.GetRaceInfo(i)
|
||||
while raceInfo ~= nil do
|
||||
pfUI.env.L["race"][raceInfo.clientFileString] = {
|
||||
raceName = raceInfo.raceName,
|
||||
raceID = raceInfo.raceID,
|
||||
faction = C_CreatureInfo.GetFactionInfo(i).groupTag,
|
||||
}
|
||||
i = i + 1
|
||||
raceInfo = C_CreatureInfo.GetRaceInfo(i)
|
||||
end
|
||||
end
|
||||
|
||||
return pfUI.env
|
||||
end
|
||||
@@ -369,14 +312,13 @@ function pfUI:CheckNewModules()
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
return func
|
||||
end
|
||||
|
||||
function pfUI:RegisterModule(name, func, arg3)
|
||||
if pfUI.disabled then return end
|
||||
if pfUI.module[name] then return end
|
||||
func = BackwardsCompatRegister(func, arg3)
|
||||
pfUI.module[name] = func
|
||||
@@ -387,7 +329,6 @@ function pfUI:RegisterModule(name, func, arg3)
|
||||
end
|
||||
|
||||
function pfUI:RegisterSkin(name, func, arg3)
|
||||
if pfUI.disabled then return end
|
||||
if pfUI.skin[name] then return end
|
||||
func = BackwardsCompatRegister(func, arg3)
|
||||
pfUI.skin[name] = func
|
||||
@@ -408,7 +349,6 @@ function pfUI:LoadSkin(s)
|
||||
end
|
||||
|
||||
pfUI:SetScript("OnEvent", function()
|
||||
if pfUI.disabled then return end
|
||||
|
||||
-- make sure to initialize and set our fonts
|
||||
-- each time an addon got loaded but only
|
||||
@@ -422,7 +362,7 @@ pfUI:SetScript("OnEvent", function()
|
||||
-- "@project-version@" until release tooling substitutes it — mark those
|
||||
-- explicitly as "dev" instead of pretending they're a real numbered build.
|
||||
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.string = "dev"
|
||||
else
|
||||
@@ -555,4 +495,21 @@ function pfUI.SetupCVars()
|
||||
COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
|
||||
end
|
||||
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
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
## Version: @project-version@
|
||||
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
|
||||
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
|
||||
## Dependencies: !!!ClassicAPI
|
||||
## X-Website: https://github.com/brues-code/pfUI
|
||||
|
||||
pfUI.lua
|
||||
# ClassicAPI-less fallback. ClassicAPI redirects this read to pfUI_Turtle.toc
|
||||
# (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.
|
||||
|
||||
init\env.xml
|
||||
init\compat.xml
|
||||
init\api.xml
|
||||
init\libs.xml
|
||||
init\skins.xml
|
||||
init\modules.xml
|
||||
API_Check.lua
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
## 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
|
||||
@@ -0,0 +1,24 @@
|
||||
## 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
|
||||
@@ -295,9 +295,8 @@ pfUI:RegisterSkin("Character", function ()
|
||||
if faction and faction.reaction and faction.reaction < 8 then
|
||||
local repLeft = faction.nextReactionThreshold - faction.currentStanding
|
||||
if repLeft > 1 then
|
||||
local text = standing:GetText() .. string.format(" (%d)", repLeft)
|
||||
standing:SetText(text)
|
||||
standing:GetParent().standingText = text
|
||||
standing:SetFormattedText("%s (%d)", standing:GetText(), repLeft)
|
||||
standing:GetParent().standingText = standing:GetText()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -125,7 +125,7 @@ pfUI:RegisterSkin("Inspect", function ()
|
||||
local guild, title = GetGuildInfo(InspectFrame.unit)
|
||||
if guild then
|
||||
InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1)
|
||||
InspectGuildText:SetText(format(TEXT(GUILD_TITLE_TEMPLATE), title, guild))
|
||||
InspectGuildText:SetFormattedText(TEXT(GUILD_TITLE_TEMPLATE), title, guild)
|
||||
InspectGuildText:Show()
|
||||
else
|
||||
InspectGuildText:SetText("")
|
||||
|
||||
Reference in New Issue
Block a user