From ed5ecb1f93d21ae794a61ed5cb08e414c8b7885a Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Fri, 29 May 2026 18:31:52 -0500 Subject: [PATCH] character: durability % per inventory slot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a Fizzle-style durability label to each character pane slot. Renders at the bottom of the slot icon as "N%" colored by the existing DURABILITY_THRESHOLD_COLORS table (red < orange < yellow < invasion < green), or hidden when the item has no durability stat (necks, rings, trinkets) or the slot is empty. api/api.lua: new pfUI.api.CreateFontString(f, key, layer, size, flags, font) helper following the CreateBackdrop pattern — idempotent get-or-create that attaches the FontString to the parent frame as the named field. Defaults to pfUI.font_default at C.global.font_size with OUTLINE on the OVERLAY layer. skins/blizzard/character.lua: scoreText migrated from the inline "if not frame.scoreText then ..." block to the new helper as a working example; new durabilityText created the same way at BOTTOM/size 10. Durability render added to RefreshCharacterSlot — runs on every PaperDollItemSlotButton_Update so equip/unequip/repair/damage all refresh automatically. api/config.lua + modules/gui.lua: new C.character.inventory.durability toggle (default "1"). GUI lives under a new top-level "Character" tab → "Inventory" sub-tab, leaving room for future "Reputation" / "Skills" sub-tabs at the same level. --- api/api.lua | 20 ++++++++++++++++ api/config.lua | 2 ++ modules/gui.lua | 4 ++++ skins/blizzard/character.lua | 46 ++++++++++++++++++++++++++++-------- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/api/api.lua b/api/api.lua index 1c950205..d8c9a689 100644 --- a/api/api.lua +++ b/api/api.lua @@ -1167,6 +1167,26 @@ function pfUI.api.CreateBackdrop(f, inset, legacy, transp, backdropSetting) end end +-- [ Create FontString ] +-- Get-or-create a FontString attached to `f` as the named field. Idempotent — +-- subsequent calls return the existing FontString. Defaults to pfUI's typical +-- font style: default font at config size, OUTLINE flag, OVERLAY layer. +-- 'f' [frame] parent frame +-- 'key' [string] field name on `f` to stash the FontString (e.g. "scoreText") +-- 'layer' [string] draw layer. Defaults to "OVERLAY". +-- 'size' [int] font size. Defaults to C.global.font_size. +-- 'flags' [string] font flags. Defaults to "OUTLINE". +-- 'font' [string] font path. Defaults to pfUI.font_default. +function pfUI.api.CreateFontString(f, key, layer, size, flags, font) + if not f or not key then return end + if f[key] then return f[key] end + + local fs = f:CreateFontString(nil, layer or "OVERLAY") + fs:SetFont(font or pfUI.font_default, size or C.global.font_size, flags or "OUTLINE") + f[key] = fs + return fs +end + -- [ Create Shadow ] -- Creates a pfUI compatible frame as shadow element -- 'f' [frame] the frame which should get a backdrop. diff --git a/api/config.lua b/api/config.lua index f0841d8f..d442418e 100644 --- a/api/config.lua +++ b/api/config.lua @@ -913,6 +913,8 @@ function pfUI:LoadConfig() pfUI:UpdateConfig("gm", nil, "server", "elysium") pfUI:UpdateConfig("questlog", nil, "showQuestLevels", "0") + + pfUI:UpdateConfig("character", "inventory", "durability", "1") pfUI:UpdateConfig("thirdparty", nil, "chatbg", "1") pfUI:UpdateConfig("thirdparty", nil, "showmeter", "0") pfUI:UpdateConfig("thirdparty", "dpsmate", "skin", "0") diff --git a/modules/gui.lua b/modules/gui.lua index 7dd707e0..bad9acb0 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -2393,6 +2393,10 @@ pfUI:RegisterModule("gui", function () end) end + CreateGUIEntry(T["Character"], T["Inventory"], function() + CreateConfig(nil, T["Show Durability"], C.character.inventory, "durability", "checkbox") + end) + CreateGUIEntry(T["Bags & Bank"], nil, function() CreateConfig(nil, T["Disable Item Quality Color For \"Common\" Items"], C.appearance.bags, "borderlimit", "checkbox") CreateConfig(nil, T["Enable Item Quality Color For Equipment Only"], C.appearance.bags, "borderonlygear", "checkbox") diff --git a/skins/blizzard/character.lua b/skins/blizzard/character.lua index 5b7713f5..6e56d5ef 100644 --- a/skins/blizzard/character.lua +++ b/skins/blizzard/character.lua @@ -81,6 +81,14 @@ pfUI:RegisterSkin("Character", function () "AmmoSlot" } + local DURABILITY_THRESHOLD_COLORS = { + RED_FONT_COLOR, + ORANGE_THREAT_COLOR, + YELLOW_FONT_COLOR, + INVASION_FONT_COLOR, + PURE_GREEN_COLOR, + } + local function RefreshPetPosition() CharacterFrameTab3:ClearAllPoints() CharacterFrameTab3:SetPoint("LEFT", HasPetUI() and CharacterFrameTab2 or CharacterFrameTab1, "RIGHT", border*2 + 1, 0) @@ -88,9 +96,9 @@ pfUI:RegisterSkin("Character", function () local function RefreshCharacterSlot(slot) local slotId = slot:GetID() - local link = GetInventoryItemLink("player", slotId) + local itemID = GetInventoryItemID("player", slotId) if slot and slot.backdrop then - if link then + if itemID then local isBroken = GetInventoryItemBroken("player", slotId) local quality = GetInventoryItemQuality("player", slotId) if isBroken then @@ -101,13 +109,31 @@ pfUI:RegisterSkin("Character", function () else slot.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea) end + if C.character.inventory.durability == "1" then + local current, maximum = GetInventoryItemDurability(slotId) + if current and maximum and maximum > 0 then + local pct = math.floor((current / maximum) * 100) + slot.durabilityText:SetText(pct .. "%") + -- 0-20% red, 20-40% orange, 40-60% yellow, 60-80% invasion, 80-100% green + local color = DURABILITY_THRESHOLD_COLORS[math.max(1, math.ceil(pct / 20))] + slot.durabilityText:SetTextColor(color:GetRGB()) + slot.durabilityText:Show() + else + slot.durabilityText:SetText("") + slot.durabilityText:Hide() + end + else + slot.durabilityText:SetText("") + slot.durabilityText:Hide() + end else slot.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea) + slot.durabilityText:SetText("") + slot.durabilityText:Hide() end - if ShaguScore and link then - local _, _, itemID = string.find(GetInventoryItemLink("player", slotId), "item:(%d+):%d+:%d+:%d+") - local itemLevel = ShaguScore.Database[tonumber(itemID)] or 0 + if ShaguScore and itemID then + local itemLevel = C_Item.GetCurrentItemLevel({ equipmentSlotIndex = slotId }) local _, _, quality, _, _, _, _, _, itemSlot, _ = GetItemInfo(itemID) local score = ShaguScore:Calculate(itemSlot, quality, itemLevel) if score and score > 0 and quality and quality > 0 then @@ -174,11 +200,11 @@ pfUI:RegisterSkin("Character", function () HandleIcon(frame.backdrop, _G["Character"..slotName.."IconTexture"]) - if not frame.scoreText then - frame.scoreText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - frame.scoreText:SetFont(pfUI.font_default, 12, "OUTLINE") - frame.scoreText:SetPoint("TOPRIGHT", 0, 0) - end + CreateFontString(frame, "scoreText", "OVERLAY", 12) + frame.scoreText:SetPoint("TOPRIGHT", 0, 0) + + CreateFontString(frame, "durabilityText", "OVERLAY", 10) + frame.durabilityText:SetPoint("BOTTOM", 0, 2) end end