12 Commits

Author SHA1 Message Date
Brues 6936fd894b Replace unusable tooltip scan with C_PlayerInfo.CanUseItem
The libtipscan approach scanned each bag/bank item's tooltip for red
text, then had to carve out broken (0-durability) items since those also
color red. C_PlayerInfo.CanUseItem checks item requirements directly
(proficiency, level, class/race, skill/spell/rep) and ignores item
state, so broken-but-equippable gear is never flagged and the durability
exclusion drops out entirely. Bank slots resolve through
C_Container.GetContainerItemID(-1, slot) instead of the inventory-slot
workaround the scanner needed.
2026-07-12 13:48:36 -05:00
Brues b5277ea457 These modules aren't new anymore 2026-07-11 16:04:58 -05:00
Brues 63f0dcbf7a Use GetInventoryItemID instead of parsing link 2026-07-11 13:18:48 -05:00
Brues 0344bff471 cleanup 2026-07-11 12:59:33 -05:00
Brues 1510969105 Unused strings 2026-07-11 12:57:07 -05:00
Brues 6e7361c543 Removed redundant compare.basestats setting
Just disable the module if you don't want stat comparison
2026-07-11 12:43:15 -05:00
Brues 2e20a03cbd Restore Inspect UI frame skin
removed during the tbc purge
2026-07-11 12:36:03 -05:00
Brues 7389c241a4 using GetSpellInfo with just a spell id is dangerous
Handful of addons will polyfill their own GetSpellInfo that only accept (bookSlot, bookType) so it's only safe to use C_Spell.GetSpellInfo with just a spell id
2026-07-11 09:47:46 -05:00
Brues fb5230828c Add Loot History module
A pfUI-native group-loot roll history window built on ClassicAPI's
C_LootHistory backport, adapted from the anniversary Blizzard reference.

- Movable/scrollable window (ESC-closable, Clear button) listing rolled
  items; each row expands to per-player rolls.
- Item icon/name/quality rendered via the !!!ClassicAPI Item mixin
  (Item:CreateFromItemLink + ContinueOnItemLoad), so uncached items load
  asynchronously and repaint their row.
- Winner shown on the collapsed row (name/roll/roll-type) and marked in the
  expanded list with a checkmark left of the name (matches the reference).
- Expansion state keys on the stable rollID; events (FULL_UPDATE /
  ROLL_CHANGED / ROLL_COMPLETE) drive a rebuild while shown, and re-attach
  the scroll child so a growing list scrolls without a /reload.
- Toggle via /loothistory or /lh; optional auto-show on new rolls behind
  loothistory.autoshow (default off).
2026-07-11 00:02:31 -05:00
Brues 7aee348a70 Bump min version to 1.6.4 2026-07-10 20:27:16 -05:00
Brues 2a5f480839 Show addon dependencies in tooltip
Add display of required and optional addon dependencies to the addons tooltip. Introduce AddDependencyLines helper in modules/addons.lua which lists dependencies with color coding: green for loaded, yellow for present but unloaded, and red for missing (uses new T["Missing"]). Store dependency arrays on addon frames (adeps / aoptdeps) using GetAddOnDependencies and C_AddOns.GetAddOnOptionalDependencies. Add translation keys 'Dependencies', 'Optional Dependencies', and 'Missing' to env/translations_enUS.lua.
2026-07-10 20:26:05 -05:00
Brues 23d1ab840c Show sell value in tooltip when merchant hidden
When the MerchantFrame is not shown, display the item's total vendor sell value on the tooltip. Adds a guard to call SetTooltipMoney(frame, sell * count) if sell > 0 so stacked items show their combined sell price.
2026-07-10 13:23:52 -05:00
25 changed files with 666 additions and 82 deletions
+2
View File
@@ -196,6 +196,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("loot", nil, "rollannounce", "0")
pfUI:UpdateConfig("loot", nil, "raritytimer", "1")
pfUI:UpdateConfig("loothistory", nil, "autoshow", "0")
pfUI:UpdateConfig("unitframes", nil, "disable", "0")
pfUI:UpdateConfig("unitframes", nil, "pastel", "1")
pfUI:UpdateConfig("unitframes", nil, "custom", "0")
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["deDE"] = {
["Combat Timer"] = nil,
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = nil,
+7 -1
View File
@@ -19,6 +19,7 @@ pfUI_translation["enUS"] = {
["Align Chat Windows"] = nil,
["Aligned Position"] = nil,
["All messages will be forwarded to:"] = nil,
["All players passed"] = nil,
["Alt-Click Action"] = nil,
["Always Allow Drag Via Shift Key"] = nil,
["Always Show"] = nil,
@@ -119,6 +120,7 @@ pfUI_translation["enUS"] = {
["Chat Bubble Transparency"] = nil,
["Chat Default Brackets"] = nil,
["Class"] = nil,
["Clear"] = nil,
["Clear Rolls"] = nil,
["Click Action"] = nil,
["Click Casting"] = nil,
@@ -138,7 +140,6 @@ pfUI_translation["enUS"] = {
["Combat Timer"] = nil,
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = nil,
@@ -195,6 +196,7 @@ pfUI_translation["enUS"] = {
["Deficit"] = nil,
["Delete profile"] = nil,
["Delete / Reset"] = nil,
["Dependencies"] = nil,
["Descending"] = nil,
["Description Font"] = nil,
["Description Font Size"] = nil,
@@ -477,6 +479,7 @@ pfUI_translation["enUS"] = {
["Look & Feel"] = nil,
["Loot"] = nil,
["Loot & Spam"] = nil,
["Loot History"] = nil,
["Macro Text Color"] = nil,
["Macro Text Size"] = nil,
["Main Actionbar"] = nil,
@@ -499,6 +502,7 @@ pfUI_translation["enUS"] = {
["Menu Font Size"] = nil,
["Messages are no longer forwarded to:"] = nil,
["Middle Mouse Button"] = nil,
["Missing"] = nil,
["Minimap"] = nil,
["Minimap Panel"] = nil,
["Minimap Size (|cffffaaaaExperimental|r)"] = nil,
@@ -552,6 +556,7 @@ pfUI_translation["enUS"] = {
["Only Show Own Debuffs (|cffffaaaaExperimental|r)"] = nil,
["Only Show Target Castbar"] = nil,
["On State Change"] = nil,
["Optional Dependencies"] = nil,
["Options"] = nil,
["Orientation"] = nil,
["Other Panel: Minimap"] = nil,
@@ -645,6 +650,7 @@ pfUI_translation["enUS"] = {
["Resting"] = nil,
["Reveal Unexplored Areas"] = nil,
["Switch to current zone"] = nil,
["Retrieving item information..."] = nil,
["Right"] = nil,
["Right Actionbar"] = nil,
["Right Anchor"] = nil,
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["esES"] = {
["Combat Timer"] = "Temporizador de combate",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Comparar estadísticas bases de objetos",
["Components"] = "Componentes",
["Config UI Settings"] = nil,
["Configuration"] = "Configuración",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["frFR"] = {
["Combat Timer"] = "Chronomètre de combat",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Compare les stats de base d'un objet",
["Components"] = "Composants",
["Config UI Settings"] = nil,
["Configuration"] = "Configuration",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["koKR"] = {
["Combat Timer"] = "전투 타이머",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = "구성",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["ruRU"] = {
["Combat Timer"] = "Таймер боя",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Сравнивать базовые характеристики предмета",
["Components"] = "Компоненты",
["Config UI Settings"] = "Настройка параметров пользовательского интерфейса",
["Configuration"] = "Конфигурация",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["zhCN"] = {
["Combat Timer"] = "战斗计时器",
["Combopoint Height"] = "连击点高度",
["Combopoint Width"] = "连击点宽度",
["Compare Item Base Stats"] = "基于属性的装备对比",
["Components"] = "组件",
["Config UI Settings"] = "界面设置",
["Configuration"] = "配置",
-1
View File
@@ -138,7 +138,6 @@ pfUI_translation["zhTW"] = {
["Combat Timer"] = "戰鬥計時器",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "基於屬性的裝備對比",
["Components"] = "組件",
["Config UI Settings"] = nil,
["Configuration"] = "配置",
+1
View File
@@ -78,4 +78,5 @@
<Include file="..\modules\unitxp.lua"/>
<Include file="..\modules\bgscore.lua"/>
<Include file="..\modules\equipmentmanager.lua"/>
<Include file="..\modules\loothistory.lua"/>
</Ui>
+1
View File
@@ -1,5 +1,6 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\skins\blizzard\character.lua"/>
<Include file="..\skins\blizzard\inspect.lua"/>
<Include file="..\skins\blizzard\spellbook.lua"/>
<Include file="..\skins\blizzard\friends.lua"/>
<Include file="..\skins\blizzard\talents.lua"/>
+2 -1
View File
@@ -1020,7 +1020,8 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
local kind, id = GetActionInfo(slot)
local effect, rank
if kind == "spell" then
effect, rank = GetSpellInfo(id)
local spellInfo = C_Spell.GetSpellInfo(id)
effect, rank = spellInfo.name, spellInfo.rank
elseif kind == "macro" then
effect, rank = GetMacroSpell(id)
end
+4 -3
View File
@@ -228,8 +228,8 @@ pfUI.libdebuff_spell_go_hooks["libtotem"] = function(spellId)
-- use pending name/icon if available (set by CastSpellByName hook), else GetSpellInfo
local name = active[slot].pending_name
local icon = active[slot].pending_icon
if not name and GetSpellInfo then
name = GetSpellInfo(spellId)
if not name then
name = C_Spell.GetSpellName(spellId)
end
active[slot].name = name
active[slot].duration = data.duration
@@ -265,7 +265,8 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
local name, rank, spellID
if kind == "spell" then
spellID = id
name, rank = GetSpellInfo(id)
local spellInfo = C_Spell.GetSpellInfo(id)
name, rank = spellInfo.name, spellInfo.rank
elseif kind == "macro" then
name, rank, spellID = GetMacroSpell(id)
end
+19
View File
@@ -217,6 +217,21 @@ pfUI:RegisterModule("addons", function ()
pfUI.addons.list:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.addons.list:SetHeight(GetNumAddOns() * 25 + 26)
local function AddDependencyLines(header, deps)
if not deps or table.getn(deps) == 0 then return end
GameTooltip:AddLine(" ")
GameTooltip:AddLine(header .. ":", .2, 1, .8)
for _, dep in ipairs(deps) do
if IsAddOnLoaded(dep) then
GameTooltip:AddLine(" " .. dep, .5, 1, .5)
elseif C_AddOns.DoesAddOnExist(dep) then
GameTooltip:AddLine(" " .. dep, 1, .82, 0)
else
GameTooltip:AddLine(" " .. dep .. " (" .. T["Missing"] .. ")", 1, .4, .4)
end
end
end
local function AddonOnEnter()
this:SetBackdropBorderColor(1,1,1,.08)
@@ -231,6 +246,8 @@ pfUI:RegisterModule("addons", function ()
end
GameTooltip:AddLine(this.anote, .75,.75,.75,1)
AddDependencyLines(T["Dependencies"], this.adeps)
AddDependencyLines(T["Optional Dependencies"], this.aoptdeps)
GameTooltip:SetWidth(180)
GameTooltip:Show()
end
@@ -272,6 +289,8 @@ pfUI:RegisterModule("addons", function ()
frame.anote = anote
frame.aauthor = aauthor
frame.aversion = aversion
frame.adeps = { GetAddOnDependencies(i) } -- required (.toc Dependencies)
frame.aoptdeps = { C_AddOns.GetAddOnOptionalDependencies(i) } -- optional (.toc OptionalDeps)
frame:SetWidth(340)
frame:SetHeight(25)
+3 -5
View File
@@ -88,7 +88,7 @@ pfUI:RegisterModule("eqcompare", function ()
local prevMerchant = ShoppingTooltip1.SetMerchantCompareItem
local function SetMerchantCompareItem(self, index, compareItem)
if C.tooltip.compare.basestats == "1" and compareItem == 1 then
if compareItem == 1 then
ShowCompareItem(nil, GetMerchantItemLink(index), 1)
return false
end
@@ -97,7 +97,7 @@ pfUI:RegisterModule("eqcompare", function ()
local prevAuction = ShoppingTooltip1.SetAuctionCompareItem
local function SetAuctionCompareItem(self, type, index, compareItem)
if C.tooltip.compare.basestats == "1" and compareItem == 1 then
if compareItem == 1 then
ShowCompareItem(nil, GetAuctionItemLink(type, index), 1)
return false
end
@@ -132,9 +132,7 @@ pfUI:RegisterModule("eqcompare", function ()
local function makeHook(getter)
return function(tooltip, arg1, arg2, arg3)
if C.tooltip.compare.basestats == "1" then
ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
end
ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
end
end
+1 -16
View File
@@ -2759,22 +2759,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha")
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox")
local showAlways = CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
local function gate()
local on = C.tooltip.compare.basestats == "1"
if on then
showAlways.input:Enable()
showAlways.caption:SetTextColor(1, 1, 1)
else
showAlways.input:Disable()
showAlways.caption:SetTextColor(0.5, 0.5, 0.5)
end
end
gate()
pfUI.events:RegisterCallback("config:changed", function(_, cat, key)
if cat == C.tooltip.compare and key == "basestats" then gate() end
end, "eqcompare-showalways-gate")
CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox")
CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox")
CreateConfig(U["questitem"], T["Show Required Questitem Count"], C.tooltip.questitem, "showcount", "checkbox")
-1
View File
@@ -2,7 +2,6 @@
-- Announces Innervate casts via raid/party/battleground chat
-- Registers AURA_CAST events directly - zero polling, pure event-driven
pfUI:RegisterNewModule("innervatecall", "Innervate Callout", "DRUID")
pfUI:RegisterModule("innervatecall", function ()
-- Requires Nampower for AURA_CAST events
if not GetNampowerVersion then return end
+390
View File
@@ -0,0 +1,390 @@
pfUI:RegisterNewModule("loothistory", "Loot History")
pfUI:RegisterModule("loothistory", function ()
local rawborder, border = GetBorderSize()
-- Layout
local ITEM_H, PLAYER_H = 24, 18
local ITEM_W, PLAYER_W = 350, 330
-- rollType constants returned by C_LootHistory.GetPlayerInfo (0/1/2; vanilla
-- has no disenchant roll).
local ROLL_PASS, ROLL_NEED, ROLL_GREED = 0, 1, 2
local ROLL_TEX = {
[ROLL_NEED] = "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
[ROLL_GREED] = "Interface\\Buttons\\UI-GroupLoot-Coin-Up",
[ROLL_PASS] = "Interface\\Buttons\\UI-GroupLoot-Pass-Up",
}
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
local function ClassColor(class)
local c = class and RAID_CLASS_COLORS[class]
if c then return c.r, c.g, c.b end
return 1, 1, 1
end
-- Paint an item row's icon/name/quality from a loaded Item mixin.
local function RenderItemVisual(f, item)
local r, g, b = 1, 1, 1
local qc = item:GetItemQualityColor()
if qc then r, g, b = qc.r, qc.g, qc.b end
f.icon:SetTexture(item:GetItemIcon())
f.iconbg:SetBackdropBorderColor(r, g, b, 1)
f.name:SetText(item:GetItemName() or UNKNOWN)
f.name:SetTextColor(r, g, b)
end
local function ShowRetrieving(f)
f.icon:SetTexture(QUESTIONMARK)
f.iconbg:SetBackdropBorderColor(1, .3, .3, 1)
f.name:SetText(T["Retrieving item information..."])
f.name:SetTextColor(1, .3, .3)
end
-- expansion state keyed on the stable rollID (survives ring-index shifts)
local expanded = {}
-- ==========================================================================
-- Window
-- ==========================================================================
pfUI.loothistory = CreateFrame("Frame", "pfLootHistory", UIParent)
pfUI.loothistory:SetFrameStrata("DIALOG")
pfUI.loothistory:SetSize(380, 490)
pfUI.loothistory:SetPoint("CENTER", 0, 0)
pfUI.loothistory:SetMovable(true)
pfUI.loothistory:EnableMouse(true)
pfUI.loothistory:RegisterForDrag("LeftButton")
pfUI.loothistory:SetScript("OnDragStart", function() this:StartMoving() end)
pfUI.loothistory:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
pfUI.loothistory:Hide()
CreateBackdrop(pfUI.loothistory, nil, true, .75)
CreateBackdropShadow(pfUI.loothistory)
tinsert(UISpecialFrames, "pfLootHistory")
pfUI.loothistory.caption = pfUI.loothistory:CreateFontString("Status", "LOW", "GameFontNormal")
pfUI.loothistory.caption:SetFont(pfUI.font_default, C.global.font_size + 4, "OUTLINE")
pfUI.loothistory.caption:SetTextColor(.2, 1, .8, 1)
pfUI.loothistory.caption:SetPoint("TOP", 0, -10)
pfUI.loothistory.caption:SetText(T["Loot History"])
-- close button
pfUI.loothistory.close = CreateFrame("Button", nil, pfUI.loothistory)
pfUI.loothistory.close:SetPoint("TOPRIGHT", -border*2, -border*2)
CreateBackdrop(pfUI.loothistory.close)
pfUI.loothistory.close:SetSize(15, 15)
pfUI.loothistory.close.texture = pfUI.loothistory.close:CreateTexture("pfLootHistoryClose")
pfUI.loothistory.close.texture:SetTexture(pfUI.media["img:close"])
pfUI.loothistory.close.texture:SetPoint("TOPLEFT", pfUI.loothistory.close, "TOPLEFT", 4, -4)
pfUI.loothistory.close.texture:SetPoint("BOTTOMRIGHT", pfUI.loothistory.close, "BOTTOMRIGHT", -4, 4)
pfUI.loothistory.close.texture:SetVertexColor(1, .25, .25, 1)
pfUI.loothistory.close:SetScript("OnEnter", function()
CreateBackdrop(pfUI.loothistory.close)
pfUI.loothistory.close.backdrop:SetBackdropBorderColor(1, .25, .25, 1)
end)
pfUI.loothistory.close:SetScript("OnLeave", function() CreateBackdrop(pfUI.loothistory.close) end)
pfUI.loothistory.close:SetScript("OnClick", function() pfUI.loothistory:Hide() end)
-- clear button
pfUI.loothistory.clear = CreateFrame("Button", nil, pfUI.loothistory, "UIPanelButtonTemplate")
SkinButton(pfUI.loothistory.clear)
pfUI.loothistory.clear:SetSize(60, 16)
pfUI.loothistory.clear:SetPoint("TOPLEFT", 10, -8)
pfUI.loothistory.clear:SetText(T["Clear"])
pfUI.loothistory.clear:SetScript("OnClick", function() C_LootHistory.Clear() end)
-- scroll frame
pfUI.loothistory.scroll = CreateScrollFrame("pfLootHistoryScroll", pfUI.loothistory)
pfUI.loothistory.scroll:SetSize(360, 440)
pfUI.loothistory.scroll:SetPoint("BOTTOM", 0, 10)
pfUI.loothistory.scroll.backdrop = CreateFrame("Frame", nil, pfUI.loothistory.scroll)
pfUI.loothistory.scroll.backdrop:SetFrameLevel(1)
pfUI.loothistory.scroll.backdrop:SetPoint("TOPLEFT", pfUI.loothistory.scroll, "TOPLEFT", -5, 5)
pfUI.loothistory.scroll.backdrop:SetPoint("BOTTOMRIGHT", pfUI.loothistory.scroll, "BOTTOMRIGHT", 5, -5)
CreateBackdrop(pfUI.loothistory.scroll.backdrop, nil, true)
local list = CreateScrollChild("pfLootHistoryList", pfUI.loothistory.scroll)
pfUI.loothistory.list = list
-- ==========================================================================
-- Frame pools
-- ==========================================================================
local itemFrames = {}
local usedPlayers, freePlayers = {}, {}
local FullUpdate -- forward declaration (toggle handlers call it)
local function CreateItemFrame()
local f = CreateFrame("Button", nil, list)
f:SetSize(ITEM_W, ITEM_H)
f:SetBackdrop(pfUI.backdrop_hover)
f:SetBackdropBorderColor(1, 1, 1, .04)
f:EnableMouse(1)
-- expand / collapse toggle
f.toggle = CreateFrame("Button", nil, f)
f.toggle:SetSize(14, 14)
f.toggle:SetPoint("LEFT", 4, 0)
f.toggle:SetScript("OnClick", function()
local id = f.rollID
if id then expanded[id] = not expanded[id]; FullUpdate() end
end)
-- icon + quality-colored border
f.iconbg = CreateFrame("Frame", nil, f)
f.iconbg:SetSize(ITEM_H - 8, ITEM_H - 8)
f.iconbg:SetPoint("LEFT", f.toggle, "RIGHT", 4, 0)
CreateBackdrop(f.iconbg, nil, true)
f.icon = f.iconbg:CreateTexture(nil, "ARTWORK")
f.icon:SetPoint("TOPLEFT", f.iconbg, "TOPLEFT", 2, -2)
f.icon:SetPoint("BOTTOMRIGHT", f.iconbg, "BOTTOMRIGHT", -2, 2)
f.icon:SetTexCoord(.08, .92, .08, .92)
-- winner block (right side, shown for decided rolls)
f.winicon = f:CreateTexture(nil, "OVERLAY")
f.winicon:SetSize(14, 14)
f.winicon:SetPoint("RIGHT", f, "RIGHT", -6, 0)
f.winroll = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.winroll:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.winroll:SetPoint("RIGHT", f.winicon, "LEFT", -2, 0)
f.winroll:SetTextColor(1, 1, 1, 1)
f.winname = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.winname:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.winname:SetPoint("RIGHT", f.winroll, "LEFT", -4, 0)
f.winname:SetJustifyH("RIGHT")
-- item name (leaves room on the right for the winner block)
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.name:SetPoint("LEFT", f.iconbg, "RIGHT", 5, 0)
f.name:SetPoint("RIGHT", f, "RIGHT", -100, 0)
f.name:SetJustifyH("LEFT")
f:SetScript("OnEnter", function()
this:SetBackdropBorderColor(1, 1, 1, .08)
if this.itemLink then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetHyperlink(this.itemLink)
GameTooltip:Show()
end
end)
f:SetScript("OnLeave", function()
this:SetBackdropBorderColor(1, 1, 1, .04)
GameTooltip:Hide()
end)
f:SetScript("OnClick", function()
local id = this.rollID
if id then expanded[id] = not expanded[id]; FullUpdate() end
end)
return f
end
local function CreatePlayerFrame()
local f = CreateFrame("Frame", nil, list)
f:SetSize(PLAYER_W, PLAYER_H)
-- name is indented to leave room for the winner checkmark on its left
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.name:SetPoint("LEFT", 20, 0)
f.name:SetJustifyH("LEFT")
f.rollicon = f:CreateTexture(nil, "OVERLAY")
f.rollicon:SetSize(16, 16)
f.rollicon:SetPoint("RIGHT", -4, 0)
f.rolltext = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.rolltext:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.rolltext:SetPoint("RIGHT", f.rollicon, "LEFT", -3, 0)
f.rolltext:SetTextColor(1, 1, 1, 1)
-- winner checkmark, just left of the player name (matches reference)
f.winmark = f:CreateTexture(nil, "OVERLAY")
f.winmark:SetSize(16, 16)
f.winmark:SetTexture(WINMARK)
f.winmark:SetPoint("RIGHT", f.name, "LEFT", -1, 0)
return f
end
local function RecycleAllPlayers()
for i = 1, table.getn(usedPlayers) do
local pf = usedPlayers[i]
pf:Hide()
table.insert(freePlayers, pf)
end
usedPlayers = {}
end
local function GetPlayerFrame()
local pf = table.remove(freePlayers) or CreatePlayerFrame()
table.insert(usedPlayers, pf)
return pf
end
local function SetToggleTexture(toggle, isExpanded)
if isExpanded then
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
else
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
end
end
-- ==========================================================================
-- Rendering
-- ==========================================================================
local function UpdateItemFrame(f, i)
local rollID, itemLink, numPlayers, isDone, winnerIdx = C_LootHistory.GetItem(i)
f.rollID = rollID
f.itemIdx = i
f.itemLink = itemLink
f.numPlayers = numPlayers or 0
f.isDone = isDone
local isExpanded = rollID and expanded[rollID]
SetToggleTexture(f.toggle, isExpanded)
-- Item icon/name/quality via the ClassicAPI Item mixin. When the item
-- isn't cached yet, show a placeholder and re-paint this row from the
-- ContinueOnItemLoad callback (guarded on rollID: rows are pooled, so the
-- callback must no-op if the row has since been reused for another roll).
local item = itemLink and Item:CreateFromItemLink(itemLink)
if item and not item:IsItemEmpty() then
if item:IsItemDataCached() then
RenderItemVisual(f, item)
else
ShowRetrieving(f)
local pending = rollID
item:ContinueOnItemLoad(function()
if f.rollID == pending then RenderItemVisual(f, item) end
end)
end
else
ShowRetrieving(f)
end
-- winner summary only on a decided, collapsed row
if isDone and not isExpanded then
if winnerIdx then
local wname, wclass, wrollType, wroll = C_LootHistory.GetPlayerInfo(i, winnerIdx)
f.winicon:SetTexture(ROLL_TEX[wrollType] or ROLL_TEX[ROLL_NEED])
f.winicon:Show()
if wroll and wroll > 0 then f.winroll:SetText(wroll) else f.winroll:SetText("") end
f.winroll:Show()
f.winname:SetText(wname or UNKNOWN)
f.winname:SetTextColor(ClassColor(wclass))
f.winname:Show()
else
-- nobody won: everyone passed
f.winicon:SetTexture(ROLL_TEX[ROLL_PASS])
f.winicon:Show()
f.winroll:SetText("")
f.winroll:Show()
f.winname:SetText(T["All players passed"])
f.winname:SetTextColor(1, .4, .4)
f.winname:Show()
end
else
f.winicon:Hide()
f.winroll:Hide()
f.winname:Hide()
end
end
local function RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf.name:SetText(name or UNKNOWN)
pf.name:SetTextColor(ClassColor(class))
pf.rollicon:SetTexture(ROLL_TEX[rollType] or ROLL_TEX[ROLL_PASS])
if roll and roll > 0 then pf.rolltext:SetText(roll) else pf.rolltext:SetText("") end
if isWinner then pf.winmark:Show() else pf.winmark:Hide() end
end
-- A player row is worth showing while the roll is undecided (see everyone),
-- or afterwards only if they actually rolled or it's you (hide the passers'
-- noise on a decided roll) — mirrors Blizzard's ShouldDisplayPlayer.
local function ShouldDisplayPlayer(isDone, roll, isMe)
return isMe or (roll and roll > 0) or not isDone
end
function FullUpdate()
if not pfUI.loothistory:IsShown() then return end
RecycleAllPlayers()
local num = C_LootHistory.GetNumItems()
local y = -2
for i = 1, num do
local f = itemFrames[i] or CreateItemFrame()
itemFrames[i] = f
UpdateItemFrame(f, i)
f:ClearAllPoints()
f:SetPoint("TOPLEFT", list, "TOPLEFT", 4, y)
f:Show()
y = y - ITEM_H - 2
if f.rollID and expanded[f.rollID] then
for p = 1, f.numPlayers do
local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p)
if ShouldDisplayPlayer(f.isDone, roll, isMe) then
local pf = GetPlayerFrame()
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf:ClearAllPoints()
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
pf:Show()
y = y - PLAYER_H
end
end
y = y - 2
end
end
for i = num + 1, table.getn(itemFrames) do
itemFrames[i]:Hide()
end
-- Resize the scroll child and re-attach it: SetHeight alone on a
-- SetAllPoints'd child doesn't make the ScrollFrame recompute its scroll
-- range, so a dynamically-grown list wouldn't scroll until a /reload.
list:SetHeight(math.max(1, -y + 2))
pfUI.loothistory.scroll:SetScrollChild(list)
pfUI.loothistory.scroll:UpdateScrollState()
end
pfUI.loothistory:SetScript("OnShow", function() FullUpdate() end)
-- ==========================================================================
-- Events
-- ==========================================================================
local events = CreateFrame("Frame")
events:RegisterEvent("LOOT_HISTORY_FULL_UPDATE")
events:RegisterEvent("LOOT_HISTORY_ROLL_CHANGED")
events:RegisterEvent("LOOT_HISTORY_ROLL_COMPLETE")
events:SetScript("OnEvent", function()
-- auto-show on a new roll opening / completing, if enabled
if C.loothistory.autoshow == "1"
and (event == "LOOT_HISTORY_FULL_UPDATE" or event == "LOOT_HISTORY_ROLL_COMPLETE")
and not pfUI.loothistory:IsShown() then
pfUI.loothistory:Show() -- OnShow runs FullUpdate
return
end
FullUpdate() -- no-op while hidden
end)
-- ==========================================================================
-- Slash command
-- ==========================================================================
local function Toggle()
pfUI.loothistory:SetShown(not pfUI.loothistory:IsShown())
end
_G.SLASH_PFLOOTHISTORY1, _G.SLASH_PFLOOTHISTORY2 = "/loothistory", "/pfloothistory"
_G.SlashCmdList.PFLOOTHISTORY = Toggle
end)
-1
View File
@@ -1,4 +1,3 @@
pfUI:RegisterNewModule("marktracking", "Mark Tracker")
pfUI:RegisterModule("marktracking", function ()
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
if not UnitExists("mark1") and not UnitExists("mark8") then
+2
View File
@@ -23,6 +23,8 @@ pfUI:RegisterModule("sellvalue", function ()
frame:AddDoubleLine(T["Buy"] .. ":", CreateGoldString(buy), 1, 1, 1)
end
end
elseif not MerchantFrame:IsShown() and sell > 0 then
SetTooltipMoney(frame, sell * count)
end
frame:Show()
end
+1 -2
View File
@@ -1,4 +1,3 @@
pfUI:RegisterNewModule("swingtimer", "Swing Timer")
pfUI:RegisterModule("swingtimer", function ()
local rawborder, border = GetBorderSize()
@@ -430,7 +429,7 @@ pfUI:RegisterModule("swingtimer", function ()
local kind, id = GetActionInfo(slot)
local name
if kind == "spell" then
name = GetSpellInfo(id)
name = C_Spell.GetSpellName(id)
elseif kind == "macro" then
name = GetMacroSpell(id)
end
+39 -21
View File
@@ -299,36 +299,54 @@ pfUI:RegisterModule("turtle-wow", function ()
-- break if theres nothing left to do
if initialized then return end
-- adjust ui positions
local _, border = GetBorderSize()
-- adjust the inspect frame's Talents tab
SkinTab(InspectFrameTab3)
InspectFrameTab3:ClearAllPoints()
InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", GetBorderSize()*2 + 1, 0)
TWTalentFrameTab1:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, TWTalentFrameTab1:GetHeight() + 4)
InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", border*2 + 1, 0)
-- reload text position
InspectFrameTab3:Hide()
InspectFrameTab3:Show()
-- skin inspect window elements
StripTextures(InspectTalentsFrame)
StripTextures(TWTalentFrameScrollFrame)
SkinScrollbar(TWTalentFrameScrollFrameScrollBar)
for i = 1, 3 do
SkinTab(_G["TWTalentFrameTab"..i])
end
-- the talent tree frame is created lazily; skin it once it exists
if TWTalentFrame then
StripTextures(InspectTalentsFrame)
StripTextures(TWTalentFrame)
StripTextures(TWTalentFrameScrollFrame)
SkinScrollbar(TWTalentFrameScrollFrameScrollBar)
-- skin each talent button
for i = 1, (MAX_NUM_TALENTS or 100) do
local talent = _G["TWTalentFrameTalent" .. i]
if talent then
StripTextures(talent)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent" .. i .. "IconTexture"])
_G["TWTalentFrameTalent" .. i .. "Rank"]:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
-- skin + position the talent-tree tabs
for i = 1, 3 do
local tab = _G["TWTalentFrameTab"..i]
if tab then
SkinTab(tab)
tab:ClearAllPoints()
local lastTab = _G["TWTalentFrameTab"..(i-1)]
if lastTab then
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else
tab:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, tab:GetHeight() + 4)
end
end
end
end
-- only run once
initialized = true
-- skin each talent button
for i = 1, (MAX_NUM_TALENTS or 100) do
local talent = _G["TWTalentFrameTalent"..i]
if talent then
StripTextures(talent)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent"..i.."IconTexture"])
local rank = _G["TWTalentFrameTalent"..i.."Rank"]
if rank then
rank:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
end
end
end
-- only run once the talent frame has actually been skinned
initialized = true
end
end)
end)
end
+8 -23
View File
@@ -4,8 +4,6 @@ pfUI:RegisterModule("unusable", function ()
pfUI.unusable = {}
local scanner = libtipscan:GetScanner("unusable")
local durability = string.gsub(DURABILITY_TEMPLATE, "%%[^%s]+", "(.+)")
local r, g, b, a = strsplit(",", C.appearance.bags.unusable_color)
function pfUI.unusable:UpdateSlot(bag, slot)
@@ -13,31 +11,18 @@ pfUI:RegisterModule("unusable", function ()
if not pfUI.bags[bag] then return end
if not pfUI.bags[bag].slots[slot] then return end
-- add button shortcuts
local frame = pfUI.bags[bag].slots[slot].frame
local name = frame:GetName()
-- return on empty buttons
local frame = pfUI.bags[bag].slots[slot].frame
if not frame.hasItem then return end
-- set the proper tooltip method
if bag == BANK_CONTAINER then
scanner:SetInventoryItem("player", 39+slot)
else
scanner:SetBagItem(bag, slot)
-- C_PlayerInfo.CanUseItem is the "is this red in the tooltip" gate:
-- proficiency, required level, class/race, skill/spell/rep. It checks
-- *requirements* only, so a broken (0-durability) item still reads as
-- usable -- no durability-line exclusion needed like the old scanner.
local itemID = C_Container.GetContainerItemID(bag, slot)
if itemID and not C_PlayerInfo.CanUseItem(itemID) then
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end
-- check for red color in tooltip
local red = scanner:Color(RED_FONT_COLOR)
if not red then return end
-- check for broken items
local left = scanner:Line(red)
local _, _, broken = string.find(left, durability, 1)
if broken then return end
-- update button vertex color
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end
-- update on regular pfUI button updates
+1 -1
View File
@@ -27,7 +27,7 @@ do
-- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 10600 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_MIN = 10605 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
+185
View File
@@ -0,0 +1,185 @@
local slots = {
"HeadSlot",
"NeckSlot",
"ShoulderSlot",
"BackSlot",
"ChestSlot",
"ShirtSlot",
"TabardSlot",
"WristSlot",
"HandsSlot",
"WaistSlot",
"LegsSlot",
"FeetSlot",
"Finger0Slot",
"Finger1Slot",
"Trinket0Slot",
"Trinket1Slot",
"MainHandSlot",
"SecondaryHandSlot",
"RangedSlot",
}
pfUI:RegisterSkin("Inspect", function ()
local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
HookAddonOrVariable("Blizzard_InspectUI", function()
local cache = {}
CreateBackdrop(InspectFrame, nil, nil, .75)
CreateBackdropShadow(InspectFrame)
InspectFrame.backdrop:SetPoint("TOPLEFT", 10, -10)
InspectFrame.backdrop:SetPoint("BOTTOMRIGHT", -30, 72)
InspectFrame:SetHitRectInsets(10,30,10,72)
EnableMovable("InspectFrame", "Blizzard_InspectUI", INSPECTFRAME_SUBFRAMES)
SkinCloseButton(InspectFrameCloseButton, InspectFrame.backdrop, -6, -6)
InspectFrame:DisableDrawLayer("ARTWORK")
InspectNameText:ClearAllPoints()
InspectNameText:SetPoint("TOP", InspectFrame.backdrop, "TOP", 0, -10)
-- Turtle WoW has up to 4 inspect tabs: Character, Honor, Arena, Talents
for i = 1, 4 do
local tab = _G["InspectFrameTab"..i]
if tab then
local lastTab = _G["InspectFrameTab"..(i-1)]
tab:ClearAllPoints()
if lastTab then
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else
tab:SetPoint("TOPLEFT", InspectFrame.backdrop, "BOTTOMLEFT", bpad, -(border + (border == 1 and 1 or 2)))
end
SkinTab(tab)
end
end
do -- Character Tab
StripTextures(InspectPaperDollFrame)
EnableClickRotate(InspectModelFrame)
local rotL = InspectModelRotateLeftButton or InspectModelFrameRotateLeftButton
if rotL then rotL:Hide() end
local rotR = InspectModelRotateRightButton or InspectModelFrameRotateRightButton
if rotR then rotR:Hide() end
for _, slot in pairs(slots) do
local frame = _G["Inspect"..slot]
StripTextures(frame)
CreateBackdrop(frame)
SetAllPointsOffset(frame.backdrop, frame, 0)
HandleIcon(frame.backdrop, _G["Inspect"..slot.."IconTexture"])
local funce = frame:GetScript("OnEnter")
frame:SetScript("OnEnter", function()
local bid = this:GetID()
if not GetInventoryItemLink(InspectFrame.unit, this:GetID()) and this.hasItem then
GameTooltip:SetOwner(this, "ANCHOR_TOPRIGHT")
GameTooltip:SetHyperlink("item:"..cache[bid]["id"])
GameTooltip:Show()
else
funce()
end
end)
end
local function ColorSlot(slot, id, itemID, vslot)
local item = Item:CreateFromItemID(itemID)
if item:IsItemEmpty() then return end
item:ContinueOnItemLoad(function()
if not InspectFrame.unit then return end
if GetInventoryItemID(InspectFrame.unit, id) ~= itemID then return end
local quality = item:GetItemQuality()
if not quality then return end
local r, g, b = GetItemQualityColor(quality)
slot.backdrop:SetBackdropBorderColor(r, g, b)
if ShaguScore then
if not slot.scoreText then
slot.scoreText = slot:CreateFontString(nil, "OVERLAY", "GameFontNormal")
slot.scoreText:SetFont(pfUI.font_default, 12, "OUTLINE")
slot.scoreText:SetPoint("TOPRIGHT", 0, 0)
end
local itemLevel = ShaguScore.Database[itemID] or 0
local score = ShaguScore:Calculate(vslot, quality, itemLevel)
if score and score > 0 then
slot.scoreText:SetText(score)
slot.scoreText:SetTextColor(r, g, b)
else
slot.scoreText:SetText("")
end
end
end)
end
local function UpdateSlots()
if not InspectFrame.unit then return end
local guild, title = GetGuildInfo(InspectFrame.unit)
if guild then
InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1)
InspectGuildText:SetText(format(TEXT(GUILD_TITLE_TEMPLATE), title, guild))
InspectGuildText:Show()
else
InspectGuildText:SetText("")
InspectGuildText:Hide()
end
for _, vslot in pairs(slots) do
local id = GetInventorySlotInfo(vslot)
local itemID = GetInventoryItemID(InspectFrame.unit, id)
local slot = _G["Inspect" .. vslot]
if itemID then
ColorSlot(slot, id, itemID, vslot)
elseif not slot.hasItem then
-- genuinely empty slot: reset to a plain backdrop
CreateBackdrop(slot)
SetAllPointsOffset(slot.backdrop, slot, 0)
if slot.scoreText then
slot.scoreText:SetText("")
end
end
end
end
hooksecurefunc("InspectPaperDollItemSlotButton_Update", function(button)
local bid = button:GetID()
local itemID = GetInventoryItemID(InspectFrame.unit, bid)
if itemID then
cache[bid] = cache[bid] or {}
cache[bid]["id"] = itemID
cache[bid]["tex"] = GetInventoryItemTexture(InspectFrame.unit, button:GetID())
cache[bid]["count"] = GetInventoryItemCount(InspectFrame.unit, button:GetID())
cache[bid]["name"] = UnitName(InspectFrame.unit)
elseif cache[bid] and UnitName(InspectFrame.unit) == cache[bid].name then
-- restore cache information
SetItemButtonTexture(button, cache[bid]["tex"])
SetItemButtonCount(button, cache[bid]["count"])
button.hasItem = 1
end
UpdateSlots()
end)
end
do -- Honor Tab
StripTextures(InspectHonorFrame)
CreateBackdrop(InspectHonorFrameProgressBar)
InspectHonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"])
InspectHonorFrameProgressBar:SetHeight(24)
end
-- NOTE: Turtle WoW's Talent tab (InspectTalentsFrame / TWTalentFrame) is
-- skinned in modules/turtle-wow.lua, which activates once this skin is
-- registered (it gates on pfUI.skin["Inspect"]).
end)
end)