From 9c6803d54ead0f9390db3ec7cb7523fe3aca99bb Mon Sep 17 00:00:00 2001 From: bdbch Date: Sat, 7 Feb 2026 14:41:39 +0100 Subject: [PATCH 1/6] fix quest XP tracking display --- State.lua | 27 +++++++++++++++++++++++---- UI.lua | 6 +++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/State.lua b/State.lua index c24d840..13fa6a8 100644 --- a/State.lua +++ b/State.lua @@ -47,7 +47,18 @@ function F.State:UpdateQuestXP() local rewardXP = GetQuestLogRewardXP(i) or 0 if rewardXP > 0 then qXP = qXP + rewardXP - if C_QuestLog.IsComplete(info.questID) or C_QuestLog.ReadyForTurnIn(info.questID) then + + local completeState = info.isComplete + if completeState == nil then + completeState = C_QuestLog.IsComplete(info.questID) + end + + local isComplete = completeState == 1 + if not isComplete then + isComplete = C_QuestLog.ReadyForTurnIn(info.questID) + end + + if isComplete then cXP = cXP + rewardXP else iXP = iXP + rewardXP @@ -77,10 +88,18 @@ function F.State:DebugDumpQuests() status = "HEADER" elseif info.isHidden then status = "HIDDEN" - elseif info.questID and (C_QuestLog.IsComplete(info.questID) or C_QuestLog.ReadyForTurnIn(info.questID)) then - status = "COMPLETE" else - status = "INCOMPLETE" + local completeState = info.isComplete + if completeState == nil and info.questID then + completeState = C_QuestLog.IsComplete(info.questID) + end + + local isComplete = completeState == 1 + if not isComplete and info.questID then + isComplete = C_QuestLog.ReadyForTurnIn(info.questID) + end + + status = isComplete and "COMPLETE" or "INCOMPLETE" end print(string.format(" [%d] id=%s title=\"%s\" reward=%d status=%s isHeader=%s isHidden=%s", diff --git a/UI.lua b/UI.lua index 0dc9b86..0a1a098 100644 --- a/UI.lua +++ b/UI.lua @@ -15,9 +15,9 @@ local CFG = { colorBorder = {0, 0, 0, 1}, colorMain = {0.76, 0.38, 1, 1}, -- Base Purple - colorRested = {0.34, 0.61, 0.99, .5}, -- Rested Blue + colorRested = {0.34, 0.61, 0.99, .8}, -- Rested Blue colorQuest = {1, 0.64, 0.0078, 0.25}, -- Quest Yellow - colorQuestComplete = {1, 0.64, 0.0078, 0.5}, -- Completed Quest + colorQuestComplete = {1, 0.64, 0.0078, 0.8}, -- Completed Quest } function F.UI.CreateStatusBar(parent, layer, subLevel, color) @@ -241,7 +241,7 @@ function F.UI.Update() if s.maxXP > 0 and projectedQuest > projectedComplete and projectedComplete < s.maxXP then F.BarQuest:Show() - F.BarQuest:SetMinMaxValues(projectedComplete, maxXP) + F.BarQuest:SetMinMaxValues(0, maxXP) F.BarQuest:SetValue(math.min(projectedQuest, s.maxXP)) else F.BarQuest:Hide() -- 2.51.2 From fd0cf0a985af2927b179d172f87c005a66c73ad5 Mon Sep 17 00:00:00 2001 From: bdbch Date: Sat, 7 Feb 2026 14:46:19 +0100 Subject: [PATCH 2/6] fix quest reward XP aggregation --- State.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/State.lua b/State.lua index 13fa6a8..2a42ef3 100644 --- a/State.lua +++ b/State.lua @@ -44,7 +44,12 @@ function F.State:UpdateQuestXP() -- this skips the XP header for a category / campaign as they should not be respected if info and not info.isHeader and not info.isHidden and info.questID > 0 then -- Use the quest log index when querying the reward XP - local rewardXP = GetQuestLogRewardXP(i) or 0 + local rewardXP = 0 + if info.questID and C_QuestLog.GetQuestLogRewardXP then + rewardXP = C_QuestLog.GetQuestLogRewardXP(info.questID) or 0 + else + rewardXP = GetQuestLogRewardXP(i) or 0 + end if rewardXP > 0 then qXP = qXP + rewardXP @@ -81,7 +86,12 @@ function F.State:DebugDumpQuests() for i = 1, numEntries do local info = C_QuestLog.GetInfo(i) if info then - local reward = GetQuestLogRewardXP(i) or 0 + local reward = 0 + if info.questID and C_QuestLog.GetQuestLogRewardXP then + reward = C_QuestLog.GetQuestLogRewardXP(info.questID) or 0 + else + reward = GetQuestLogRewardXP(i) or 0 + end local status = "UNKNOWN" if info.isHeader then -- 2.51.2 From ef6c37de93f07dffd0b65bf6c8a4a5cc2314a74f Mon Sep 17 00:00:00 2001 From: bdbch Date: Sat, 7 Feb 2026 15:09:45 +0100 Subject: [PATCH 3/6] add label toggles and quest xp text --- Config.lua | 65 ++++++++++++++++++++++++++++++++++++++++++++++++------ UI.lua | 61 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/Config.lua b/Config.lua index 2a72ad9..a5aefa8 100644 --- a/Config.lua +++ b/Config.lua @@ -8,10 +8,17 @@ F.Config = { enabled = true, -- Text Toggles + showLevelText = true, + showXPText = true, + showPercentText = true, showLevelTimeText = true, showSessionTimeText = true, showXPHourText = true, + showLevelingText = true, showQuestRestedText = true, + showIncompleteQuestXPText = true, + showCompletedQuestXPText = true, + showRestedXPText = true, -- Logic Settings resetOnReload = true, @@ -38,11 +45,27 @@ SLASH_NIXXNUXXPBAR1 = SLASH_NIXXNUXXPBAR1 or "/nxp" SlashCmdList = SlashCmdList or {} SlashCmdList["NIXXNUXXPBAR"] = function(msg) msg = msg or "" - local cmd = msg:match("^(%S+)") + local cmd, arg1 = msg:match("^(%S+)%s*(.-)$") cmd = cmd and cmd:lower() or "" + arg1 = arg1 and arg1:lower() or "" - if cmd == "toggle" then - -- Ensure DB is initialized, then flip the flag + local function showHelp() + print("NixxnuxXPBar: Commands:") + print(" /nxp help") + print(" /nxp toggle | /nxp show | /nxp hide") + print(" /nxp toggle level | xp | percent | quest | leveling") + print(" /nxp toggle incomplete | completed | rested") + end + + local function toggleLabel(flagKey, label) + if F.InitDB then F.InitDB() end + F.DB[flagKey] = not F.DB[flagKey] + local state = F.DB[flagKey] and "On" or "Off" + print("NixxnuxXPBar: " .. label .. " " .. state) + if F.UpdateUI then F.UpdateUI() end + end + + if cmd == "toggle" and arg1 == "" then if F.InitDB then F.InitDB() end F.DB.enabled = not F.DB.enabled @@ -53,17 +76,45 @@ SlashCmdList["NIXXNUXXPBAR"] = function(msg) print("NixxnuxXPBar: Hidden") if F.Frame and F.Frame.Hide then F.Frame:Hide() end end - elseif cmd == "show" then + return + end + + if cmd == "toggle" then + local map = { + level = { key = "showLevelText", label = "Level Text" }, + xp = { key = "showXPText", label = "XP Text" }, + percent = { key = "showPercentText", label = "Percent Text" }, + quest = { key = "showQuestRestedText", label = "Quest/Rested Percent Text" }, + leveling = { key = "showLevelingText", label = "Leveling Text" }, + incomplete = { key = "showIncompleteQuestXPText", label = "Uncompleted Quest XP Text" }, + completed = { key = "showCompletedQuestXPText", label = "Completed Quest XP Text" }, + rested = { key = "showRestedXPText", label = "Rested XP Text" }, + } + + local entry = map[arg1] + if entry then + toggleLabel(entry.key, entry.label) + else + showHelp() + end + return + end + + if cmd == "show" then if F.InitDB then F.InitDB() end F.DB.enabled = true print("NixxnuxXPBar: Visible") if F.UpdateUI then F.UpdateUI() end - elseif cmd == "hide" then + return + end + + if cmd == "hide" then if F.InitDB then F.InitDB() end F.DB.enabled = false print("NixxnuxXPBar: Hidden") if F.Frame and F.Frame.Hide then F.Frame:Hide() end - else - print("NixxnuxXPBar: Commands: /nxp toggle | /nxp show | /nxp hide") + return end + + showHelp() end diff --git a/UI.lua b/UI.lua index 0a1a098..30f339c 100644 --- a/UI.lua +++ b/UI.lua @@ -165,6 +165,14 @@ function F.UI.Create() F.InfoText:SetDrawLayer("OVERLAY", 2) F.InfoText:SetTextColor(1, 1, 1, 1) + -- quest/rested XP amounts + F.QuestXPText = overlay:CreateFontString(nil, "OVERLAY") + F.QuestXPText:SetFont(CFG.font, CFG.fontSize - 2, "OUTLINE") + F.QuestXPText:SetPoint("TOP", overlay, "BOTTOM", 0, -22) + F.QuestXPText:SetJustifyH("CENTER") + F.QuestXPText:SetDrawLayer("OVERLAY", 2) + F.QuestXPText:SetTextColor(1, 1, 1, 1) + -- Leveling info F.LevelingText = overlay:CreateFontString(nil, "OVERLAY") F.LevelingText:SetFont(CFG.font, CFG.fontSize - 2, "OUTLINE") @@ -260,12 +268,24 @@ function F.UI.Update() local percentXP = (s.maxXP > 0) and (s.currentXP / s.maxXP * 100) or 0 local percentRested = (s.maxXP > 0) and (s.restedXP / s.maxXP * 100) or 0 - F.LevelText:SetText("Level " .. s.level) + if db.showLevelText then + F.LevelText:SetText("Level " .. s.level) + F.LevelText:Show() + else + F.LevelText:SetText("") + F.LevelText:Hide() + end - if s.isMaxLevel then - F.TextCenter:SetText("Level " .. s.level .. " (Max)") + if db.showXPText then + if s.isMaxLevel then + F.TextCenter:SetText("Level " .. s.level .. " (Max)") + else + F.TextCenter:SetText(string.format("%s / %s", FormatLargeNumber(s.currentXP), FormatLargeNumber(s.maxXP))) + end + F.TextCenter:Show() else - F.TextCenter:SetText(string.format("%s / %s", FormatLargeNumber(s.currentXP), FormatLargeNumber(s.maxXP))) + F.TextCenter:SetText("") + F.TextCenter:Hide() end local percentStr = "" @@ -284,10 +304,12 @@ function F.UI.Update() end end - if percentStr ~= "" and s.maxXP > 0 then + if db.showPercentText and percentStr ~= "" and s.maxXP > 0 then F.TextRight:SetText(percentStr) + F.TextRight:Show() else F.TextRight:SetText("") + F.TextRight:Hide() end do @@ -309,16 +331,41 @@ function F.UI.Update() levelingStr = string.format("Leveling in: %s (%s XP/Hr)", timerText, xpRateText) end - if #infoParts > 0 then + if db.showQuestRestedText and #infoParts > 0 then F.InfoText:SetText(table.concat(infoParts, " - ")) + F.InfoText:Show() else F.InfoText:SetText("") + F.InfoText:Hide() end - if levelingStr ~= "" then + if db.showLevelingText and levelingStr ~= "" then F.LevelingText:SetText(levelingStr) + F.LevelingText:Show() else F.LevelingText:SetText("") + F.LevelingText:Hide() + end + end + + do + local xpParts = {} + if db.showIncompleteQuestXPText and (s.incompleteXP or 0) > 0 then + table.insert(xpParts, string.format("Uncompleted Quest XP: %s", FormatLargeNumber(s.incompleteXP or 0))) + end + if db.showCompletedQuestXPText and (s.completeXP or 0) > 0 then + table.insert(xpParts, string.format("Completed Quest XP: %s", FormatLargeNumber(s.completeXP or 0))) + end + if db.showRestedXPText and (s.restedXP or 0) > 0 then + table.insert(xpParts, string.format("Rested XP: %s", FormatLargeNumber(s.restedXP or 0))) + end + + if #xpParts > 0 then + F.QuestXPText:SetText(table.concat(xpParts, " - ")) + F.QuestXPText:Show() + else + F.QuestXPText:SetText("") + F.QuestXPText:Hide() end end end -- 2.51.2 From 348830be950cdd0ebe7eb62e039d5a381d1ef391 Mon Sep 17 00:00:00 2001 From: bdbch Date: Sat, 7 Feb 2026 15:09:45 +0100 Subject: [PATCH 4/6] use selected quest reward xp --- State.lua | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/State.lua b/State.lua index 2a42ef3..bedb843 100644 --- a/State.lua +++ b/State.lua @@ -23,6 +23,35 @@ F.State = { } } +local function GetQuestRewardXP(index, questID) + local rewardXP = 0 + + if questID and C_QuestLog.GetQuestLogRewardXP then + rewardXP = C_QuestLog.GetQuestLogRewardXP(questID) or 0 + end + + if questID and C_QuestLog.SetSelectedQuest and C_QuestLog.GetSelectedQuest and GetQuestLogRewardXP then + local previous = C_QuestLog.GetSelectedQuest() + if previous ~= questID then + C_QuestLog.SetSelectedQuest(questID) + end + + local selectedXP = GetQuestLogRewardXP() or 0 + + if previous and previous ~= questID then + C_QuestLog.SetSelectedQuest(previous) + end + + if selectedXP > 0 then + rewardXP = selectedXP + end + elseif rewardXP == 0 and GetQuestLogRewardXP then + rewardXP = GetQuestLogRewardXP(index) or 0 + end + + return rewardXP +end + function F.State:UpdatePlayerXP() self.level = UnitLevel("player") self.currentXP = UnitXP("player") @@ -44,12 +73,7 @@ function F.State:UpdateQuestXP() -- this skips the XP header for a category / campaign as they should not be respected if info and not info.isHeader and not info.isHidden and info.questID > 0 then -- Use the quest log index when querying the reward XP - local rewardXP = 0 - if info.questID and C_QuestLog.GetQuestLogRewardXP then - rewardXP = C_QuestLog.GetQuestLogRewardXP(info.questID) or 0 - else - rewardXP = GetQuestLogRewardXP(i) or 0 - end + local rewardXP = GetQuestRewardXP(i, info.questID) if rewardXP > 0 then qXP = qXP + rewardXP @@ -86,12 +110,7 @@ function F.State:DebugDumpQuests() for i = 1, numEntries do local info = C_QuestLog.GetInfo(i) if info then - local reward = 0 - if info.questID and C_QuestLog.GetQuestLogRewardXP then - reward = C_QuestLog.GetQuestLogRewardXP(info.questID) or 0 - else - reward = GetQuestLogRewardXP(i) or 0 - end + local reward = GetQuestRewardXP(i, info.questID) local status = "UNKNOWN" if info.isHeader then -- 2.51.2 From 1686b297c709c5e076b2f44793e76d20c8c7b2ce Mon Sep 17 00:00:00 2001 From: bdbch Date: Sat, 7 Feb 2026 15:11:22 +0100 Subject: [PATCH 5/6] fix quest completion detection --- State.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/State.lua b/State.lua index bedb843..9d842f8 100644 --- a/State.lua +++ b/State.lua @@ -82,7 +82,7 @@ function F.State:UpdateQuestXP() completeState = C_QuestLog.IsComplete(info.questID) end - local isComplete = completeState == 1 + local isComplete = completeState == 1 or completeState == true if not isComplete then isComplete = C_QuestLog.ReadyForTurnIn(info.questID) end @@ -123,7 +123,7 @@ function F.State:DebugDumpQuests() completeState = C_QuestLog.IsComplete(info.questID) end - local isComplete = completeState == 1 + local isComplete = completeState == 1 or completeState == true if not isComplete and info.questID then isComplete = C_QuestLog.ReadyForTurnIn(info.questID) end -- 2.51.2 From e80e88581c09d85c2cdf8b244706c65b4aa7d161 Mon Sep 17 00:00:00 2001 From: bdbch Date: Sat, 7 Feb 2026 15:20:52 +0100 Subject: [PATCH 6/6] add tracked quest mode and docs --- Config.lua | 11 +++++++++++ Core.lua | 4 +++- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ State.lua | 44 ++++++++++++++++++++++++++------------------ 4 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 README.md diff --git a/Config.lua b/Config.lua index a5aefa8..adbef20 100644 --- a/Config.lua +++ b/Config.lua @@ -23,6 +23,7 @@ F.Config = { -- Logic Settings resetOnReload = true, hideBlizzardXPBar = true, + trackedOnly = true, } function F.InitDB() @@ -55,6 +56,7 @@ SlashCmdList["NIXXNUXXPBAR"] = function(msg) print(" /nxp toggle | /nxp show | /nxp hide") print(" /nxp toggle level | xp | percent | quest | leveling") print(" /nxp toggle incomplete | completed | rested") + print(" /nxp tracked (toggle tracked-only quests)") end local function toggleLabel(flagKey, label) @@ -108,6 +110,15 @@ SlashCmdList["NIXXNUXXPBAR"] = function(msg) return end + if cmd == "tracked" then + if F.InitDB then F.InitDB() end + F.DB.trackedOnly = not F.DB.trackedOnly + local state = F.DB.trackedOnly and "On" or "Off" + print("NixxnuxXPBar: Tracked-only " .. state) + if F.UpdateUI then F.UpdateUI() end + return + end + if cmd == "hide" then if F.InitDB then F.InitDB() end F.DB.enabled = false diff --git a/Core.lua b/Core.lua index 1ae67eb..434e441 100644 --- a/Core.lua +++ b/Core.lua @@ -6,6 +6,8 @@ F.Events:RegisterEvent("PLAYER_LEVEL_UP") F.Events:RegisterEvent("PLAYER_XP_UPDATE") F.Events:RegisterEvent("UPDATE_FACTION") F.Events:RegisterEvent("QUEST_LOG_UPDATE") +F.Events:RegisterEvent("QUEST_WATCH_LIST_CHANGED") +F.Events:RegisterEvent("QUEST_WATCH_UPDATE") F.Events:RegisterEvent("ADDON_LOADED") F.Events:SetScript("OnEvent", function(self, event, ...) @@ -42,7 +44,7 @@ F.Events:SetScript("OnEvent", function(self, event, ...) F.State.session.lastXP = currentXP F.State:UpdatePlayerXP() - elseif event == "QUEST_LOG_UPDATE" then + elseif event == "QUEST_LOG_UPDATE" or event == "QUEST_WATCH_LIST_CHANGED" or event == "QUEST_WATCH_UPDATE" then F.State:UpdateQuestXP() end diff --git a/README.md b/README.md new file mode 100644 index 0000000..c568f4c --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# NixxnuxXPBar + +NixxnuxXPBar is a World of Warcraft addon that replaces the default XP bar with a detailed bar showing XP progress, quest XP overlays (completed and uncompleted), and rested XP. It also provides leveling stats like XP/hour and time-to-level. + +## Features + +- Custom XP bar with quest and rested overlays +- Separate completed and uncompleted quest XP tracking +- Rested XP display +- Session XP rate and time-to-level +- Toggleable labels and modes via slash commands +- Optional tracked-only quest counting (pinned quests) + +## Installation + +1. Copy the `NixxnuxXPBar` folder into your WoW AddOns directory. +2. Reload the UI or restart the game. + +## Commands + +General: + +- `/nxp help` - Show all commands +- `/nxp toggle` - Toggle the addon visibility +- `/nxp show` - Show the addon +- `/nxp hide` - Hide the addon + +Quest mode: + +- `/nxp tracked` - Toggle counting only tracked (pinned) quests + +Label toggles: + +- `/nxp toggle level` - Level text +- `/nxp toggle xp` - Current XP text +- `/nxp toggle percent` - Percent text +- `/nxp toggle quest` - Quest/rested percent text +- `/nxp toggle leveling` - Leveling info text +- `/nxp toggle incomplete` - Uncompleted quest XP text +- `/nxp toggle completed` - Completed quest XP text +- `/nxp toggle rested` - Rested XP text + +## Usage + +- The bar updates automatically as you gain XP or complete quests. +- The quest overlays show completed quest XP and uncompleted quest XP stacked on top of your current XP. +- Use `/nxp tracked` to count only pinned quests, or toggle it off to include all quests in your log. +- Use label toggles to customize what text appears around the bar. + +## Notes + +- Drag the bar by holding Shift and left-clicking. +- If Blizzard XP/reputation bars are hidden, the addon manages that when enabled. diff --git a/State.lua b/State.lua index 9d842f8..a0a6d63 100644 --- a/State.lua +++ b/State.lua @@ -72,25 +72,33 @@ function F.State:UpdateQuestXP() local info = C_QuestLog.GetInfo(i) -- this skips the XP header for a category / campaign as they should not be respected if info and not info.isHeader and not info.isHidden and info.questID > 0 then - -- Use the quest log index when querying the reward XP - local rewardXP = GetQuestRewardXP(i, info.questID) - if rewardXP > 0 then - qXP = qXP + rewardXP - - local completeState = info.isComplete - if completeState == nil then - completeState = C_QuestLog.IsComplete(info.questID) - end - - local isComplete = completeState == 1 or completeState == true - if not isComplete then - isComplete = C_QuestLog.ReadyForTurnIn(info.questID) - end + local isTracked = true + if F.DB and F.DB.trackedOnly and C_QuestLog.GetQuestWatchType then + local watchType = C_QuestLog.GetQuestWatchType(info.questID) + isTracked = (watchType == 1) + end - if isComplete then - cXP = cXP + rewardXP - else - iXP = iXP + rewardXP + if isTracked then + -- Use the quest log index when querying the reward XP + local rewardXP = GetQuestRewardXP(i, info.questID) + if rewardXP > 0 then + qXP = qXP + rewardXP + + local completeState = info.isComplete + if completeState == nil then + completeState = C_QuestLog.IsComplete(info.questID) + end + + local isComplete = completeState == 1 or completeState == true + if not isComplete then + isComplete = C_QuestLog.ReadyForTurnIn(info.questID) + end + + if isComplete then + cXP = cXP + rewardXP + else + iXP = iXP + rewardXP + end end end end -- 2.51.2