diff --git a/Config.lua b/Config.lua --- a/Config.lua +++ b/Config.lua @@ -4,8 +4,33 @@ -- Display Options showAtMaxLevel = false, showIncompleteQuestBar = true, + questTrackingEnabled = true, + showTicks = true, + tickOpacity = 0.35, + anchorPoint = "TOP", + offsetX = 0, + offsetY = 100, -- Master visibility toggle (can be flipped with /nxp toggle) enabled = true, + + -- Bar Size + barWidth = 600, + barHeight = 24, + + -- Textures + barTextureMain = "Interface\\Buttons\\WHITE8x8", + barTextureRested = "Interface\\Buttons\\WHITE8x8", + barTextureQuest = "Interface\\Buttons\\WHITE8x8", + barTextureQuestComplete = "Interface\\Buttons\\WHITE8x8", + + -- Font + fontPath = "Fonts\\FRIZQT__.TTF", + + -- Bar Colors (RGBA) + colorMain = { r = 0.76, g = 0.38, b = 1, a = 1 }, + colorRested = { r = 0.34, g = 0.61, b = 0.99, a = 0.8 }, + colorQuest = { r = 1, g = 0.64, b = 0.0078, a = 0.25 }, + colorQuestComplete = { r = 1, g = 0.64, b = 0.0078, a = 0.8 }, -- Text Toggles showLevelText = true, @@ -53,6 +78,7 @@ local function showHelp() print("NixxnuxXPBar: Commands:") print(" /nxp help") + print(" /nxp options") print(" /nxp toggle | /nxp show | /nxp hide") print(" /nxp toggle level | xp | percent | quest | leveling") print(" /nxp toggle incomplete | completed | rested") @@ -107,6 +133,15 @@ F.DB.enabled = true print("NixxnuxXPBar: Visible") if F.UpdateUI then F.UpdateUI() end + return + end + + if cmd == "options" then + if F.Options and F.Options.Open then + F.Options.Open() + else + print("NixxnuxXPBar: Options not available yet") + end return end diff --git a/NixxnuxXPBar.toc b/NixxnuxXPBar.toc --- a/NixxnuxXPBar.toc +++ b/NixxnuxXPBar.toc @@ -15,9 +15,11 @@ ## Author: Nixxnux ## Category: Interface Enhancements ## IconTexture: Interface\Icons\Inv_cape_special_treasure_c_01 +## SavedVariables: NXP_DB Init.lua Config.lua +Options.lua Utils.lua UI.lua State.lua diff --git a/Options.lua b/Options.lua new file mode 100644 --- /dev/null +++ b/Options.lua @@ -0,0 +1,515 @@ +local _, F = ... + +F.Options = F.Options or {} + +local panel +local content +local controls = {} +local questRelatedKeys = { + trackedOnly = true, + showQuestRestedText = true, + showIncompleteQuestXPText = true, + showCompletedQuestXPText = true, + showIncompleteQuestBar = true, + colorQuest = true, + colorQuestComplete = true, + barTextureQuest = true, + barTextureQuestComplete = true, +} + +local TICK_OPACITY_KEY = "tickOpacity" +local DEFAULT_STATUSBAR = "Interface\\Buttons\\WHITE8x8" +local DEFAULT_FONT = "Fonts\\FRIZQT__.TTF" + +local function GetLibSharedMedia() + if LibStub then + return LibStub("LibSharedMedia-3.0", true) + end + return nil +end + +local function SetOption(key, value) + if F.InitDB then F.InitDB() end + if not F.DB then return end + + F.DB[key] = value and true or false + + if key == "enabled" then + if value then + if F.UpdateUI then F.UpdateUI() end + else + if F.Frame and F.Frame.Hide then F.Frame:Hide() end + end + return + end + + if key == "hideBlizzardXPBar" and F.UI and F.UI.HideBlizzardBars then + F.UI.HideBlizzardBars() + end + + if F.UpdateUI then F.UpdateUI() end + + if key == "questTrackingEnabled" or key == "trackedOnly" then + if F.State and F.State.UpdateQuestXP then + F.State:UpdateQuestXP() + end + if key == "questTrackingEnabled" and panel and panel:IsShown() then + F.Options.Refresh() + end + end +end + +local function SetNumericOption(key, value) + if F.InitDB then F.InitDB() end + if not F.DB then return end + if key == "barWidth" then + local width = tonumber(value) or 0 + local scale = UIParent and UIParent:GetEffectiveScale() or 1 + local stepPixels = math.floor(((width / 20) * scale) + 0.5) + local snapped = (stepPixels / scale) * 20 + if snapped < 200 then snapped = 200 end + F.DB[key] = snapped + elseif key == TICK_OPACITY_KEY then + F.DB[key] = (tonumber(value) or 0) / 100 + else + F.DB[key] = value + end + if F.UpdateUI then F.UpdateUI() end +end + +local function SetStringOption(key, value) + if F.InitDB then F.InitDB() end + if not F.DB then return end + F.DB[key] = value + if F.UpdateUI then F.UpdateUI() end +end + +local function SetColorOption(key, r, g, b, a) + if F.InitDB then F.InitDB() end + if not F.DB then return end + F.DB[key] = { r = r, g = g, b = b, a = a } + if F.UpdateUI then F.UpdateUI() end +end + +local function GetColorOption(key, fallback) + if F.InitDB then F.InitDB() end + if not F.DB then return fallback[1], fallback[2], fallback[3], fallback[4] end + local color = F.DB[key] + if type(color) == "table" then + if color.r then + return color.r or 1, color.g or 1, color.b or 1, color.a or 1 + end + if color[1] then + return color[1] or 1, color[2] or 1, color[3] or 1, color[4] or 1 + end + end + return fallback[1], fallback[2], fallback[3], fallback[4] +end + +local function CreateHeader(parent, text, y) + local fs = parent:CreateFontString(nil, "ARTWORK", "GameFontNormal") + fs:SetPoint("TOPLEFT", parent, "TOPLEFT", 16, y) + fs:SetText(text) + return y - 20 +end + +local function CreateCheckbox(parent, key, label, y) + local cb = CreateFrame("CheckButton", nil, parent, "InterfaceOptionsCheckButtonTemplate") + cb.Text:SetText(label) + cb:SetPoint("TOPLEFT", parent, "TOPLEFT", 16, y) + cb:SetScript("OnClick", function(self) + SetOption(key, self:GetChecked()) + end) + + controls[key] = { type = "checkbox", widget = cb } + return y - 28 +end + +local function CreateSlider(parent, key, label, y, minValue, maxValue, step) + local name = "NXP_Options_" .. key .. "Slider" + local slider = CreateFrame("Slider", name, parent, "OptionsSliderTemplate") + slider:SetPoint("TOPLEFT", parent, "TOPLEFT", 16, y) + slider:SetMinMaxValues(minValue, maxValue) + slider:SetValueStep(step) + slider:SetObeyStepOnDrag(true) + + local labelText = _G[name .. "Text"] + local lowText = _G[name .. "Low"] + local highText = _G[name .. "High"] + if labelText then labelText:SetText(label) end + if lowText then lowText:SetText(tostring(minValue)) end + if highText then highText:SetText(tostring(maxValue)) end + + slider:SetScript("OnValueChanged", function(self, value) + if self._updating then return end + local rounded = math.floor(value + 0.5) + if step and step < 1 then + rounded = value + end + self._updating = true + self:SetValue(rounded) + self._updating = false + SetNumericOption(key, rounded) + end) + + controls[key] = { type = "slider", widget = slider } + return y - 52 +end + +local function CreateSliderWithInput(parent, key, label, y, minValue, maxValue, step, inputWidth) + local name = "NXP_Options_" .. key .. "Slider" + local slider = CreateFrame("Slider", name, parent, "OptionsSliderTemplate") + slider:SetPoint("TOPLEFT", parent, "TOPLEFT", 16, y) + slider:SetMinMaxValues(minValue, maxValue) + slider:SetValueStep(step) + slider:SetObeyStepOnDrag(true) + + local labelText = _G[name .. "Text"] + local lowText = _G[name .. "Low"] + local highText = _G[name .. "High"] + if labelText then labelText:SetText(label) end + if lowText then lowText:SetText(tostring(minValue)) end + if highText then highText:SetText(tostring(maxValue)) end + + local edit = CreateFrame("EditBox", nil, parent, "InputBoxTemplate") + edit:SetAutoFocus(false) + edit:SetSize(inputWidth or 60, 20) + edit:SetPoint("LEFT", slider, "RIGHT", 12, 0) + edit:SetNumeric(true) + + slider:SetScript("OnValueChanged", function(self, value) + if self._updating then return end + local rounded = math.floor(value + 0.5) + if step and step < 1 then + rounded = value + end + self._updating = true + self:SetValue(rounded) + self._updating = false + edit:SetNumber(rounded) + SetNumericOption(key, rounded) + end) + + local function commitEdit() + local value = tonumber(edit:GetText()) + if not value then return end + if value < minValue then value = minValue end + if value > maxValue then value = maxValue end + slider._updating = true + slider:SetValue(value) + slider._updating = false + edit:SetNumber(value) + SetNumericOption(key, value) + end + + edit:SetScript("OnEnterPressed", function(self) + commitEdit() + self:ClearFocus() + end) + + edit:SetScript("OnEditFocusLost", function() + commitEdit() + end) + + controls[key] = { type = "offset", slider = slider, edit = edit } + return y - 52 +end + +local function BuildMediaList(kind, defaultLabel, defaultValue) + local list = {} + local order = {} + + list[defaultValue] = defaultLabel + table.insert(order, defaultValue) + + local LSM = GetLibSharedMedia() + if LSM and LSM.HashTable then + local entries = {} + for name, path in pairs(LSM:HashTable(kind) or {}) do + if type(path) == "string" and path ~= "" then + table.insert(entries, { name = tostring(name), path = path }) + end + end + table.sort(entries, function(a, b) return a.name < b.name end) + for _, entry in ipairs(entries) do + list[entry.path] = entry.name + table.insert(order, entry.path) + end + end + + return list, order +end + +local function CreateDropdown(parent, key, label, y, list, order, width) + local name = "NXP_Options_" .. key .. "Dropdown" + local dropdown = CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate") + dropdown:SetPoint("TOPLEFT", parent, "TOPLEFT", -6, y) + + local labelText = dropdown:CreateFontString(nil, "ARTWORK", "GameFontHighlight") + labelText:SetPoint("TOPLEFT", dropdown, "TOPLEFT", 16, 14) + labelText:SetText(label) + + UIDropDownMenu_SetWidth(dropdown, width or 220) + UIDropDownMenu_Initialize(dropdown, function(self) + for _, value in ipairs(order or {}) do + local info = UIDropDownMenu_CreateInfo() + info.text = list[value] or value + info.value = value + info.func = function() + UIDropDownMenu_SetSelectedValue(dropdown, value) + SetStringOption(key, value) + end + UIDropDownMenu_AddButton(info) + end + end) + + controls[key] = { type = "dropdown", widget = dropdown, list = list, label = labelText } + return y - 50 +end + +local function CreateColorPicker(parent, key, label, y, fallback) + local labelText = parent:CreateFontString(nil, "ARTWORK", "GameFontHighlight") + labelText:SetPoint("TOPLEFT", parent, "TOPLEFT", 16, y) + labelText:SetText(label) + + local swatch = CreateFrame("Button", nil, parent) + swatch:SetSize(16, 16) + swatch:SetPoint("LEFT", labelText, "RIGHT", 8, 0) + swatch:SetNormalTexture("Interface\\Buttons\\WHITE8x8") + swatch:SetHighlightTexture("Interface\\Buttons\\WHITE8x8", "ADD") + local swatchTex = swatch:GetNormalTexture() + + local border = swatch:CreateTexture(nil, "BORDER") + border:SetPoint("TOPLEFT", swatch, "TOPLEFT", -1, 1) + border:SetPoint("BOTTOMRIGHT", swatch, "BOTTOMRIGHT", 1, -1) + border:SetColorTexture(0, 0, 0, 1) + if swatchTex then + swatchTex:SetPoint("TOPLEFT", swatch, "TOPLEFT", 1, -1) + swatchTex:SetPoint("BOTTOMRIGHT", swatch, "BOTTOMRIGHT", -1, 1) + end + + swatch:SetScript("OnClick", function() + local r, g, b, a = GetColorOption(key, fallback) + local previous = { r = r, g = g, b = b, a = a } + + local function getPickerAlpha() + if ColorPickerFrame.GetColorAlpha then + return ColorPickerFrame:GetColorAlpha() + end + return 1 - (ColorPickerFrame.opacity or 0) + end + + local function applyColor() + local nr, ng, nb = ColorPickerFrame:GetColorRGB() + local na = getPickerAlpha() + SetColorOption(key, nr, ng, nb, na) + F.Options.Refresh() + end + + local function cancelColor() + local pv = previous + if pv then + SetColorOption(key, pv.r, pv.g, pv.b, pv.a) + F.Options.Refresh() + end + end + + if ColorPickerFrame.SetupColorPickerAndShow then + ColorPickerFrame:SetupColorPickerAndShow({ + r = r, + g = g, + b = b, + opacity = 1 - (a or 1), + hasOpacity = true, + swatchFunc = applyColor, + opacityFunc = applyColor, + cancelFunc = cancelColor, + }) + else + ColorPickerFrame.hasOpacity = true + ColorPickerFrame.opacity = 1 - (a or 1) + ColorPickerFrame.previousValues = previous + ColorPickerFrame.func = applyColor + ColorPickerFrame.opacityFunc = applyColor + ColorPickerFrame.cancelFunc = cancelColor + ColorPickerFrame:SetColorRGB(r, g, b) + ColorPickerFrame:Hide() + ColorPickerFrame:Show() + end + end) + + controls[key] = { type = "color", widget = swatch, label = labelText, fallback = fallback } + return y - 24 +end + +function F.Options.Refresh() + if F.InitDB then F.InitDB() end + if not F.DB then return end + + local defaults = F.Config or {} + + for key, control in pairs(controls) do + if control.type == "checkbox" then + control.widget:SetChecked(F.DB[key] and true or false) + elseif control.type == "slider" then + local value = F.DB[key] + if value == nil then value = defaults[key] end + if value == nil then value = 0 end + if key == TICK_OPACITY_KEY then + value = (tonumber(value) or 0) * 100 + end + control.widget._updating = true + control.widget:SetValue(tonumber(value) or 0) + control.widget._updating = false + elseif control.type == "color" then + local r, g, b, a = GetColorOption(key, control.fallback) + local tex = control.widget:GetNormalTexture() + if tex then tex:SetColorTexture(r, g, b, a) end + elseif control.type == "offset" then + local value = F.DB[key] + if value == nil then value = defaults[key] end + if value == nil then value = 0 end + control.slider._updating = true + control.slider:SetValue(tonumber(value) or 0) + control.slider._updating = false + control.edit:SetNumber(tonumber(value) or 0) + elseif control.type == "dropdown" then + local defaults = F.Config or {} + local value = F.DB[key] + if value == nil then value = defaults[key] end + UIDropDownMenu_SetSelectedValue(control.widget, value) + UIDropDownMenu_SetText(control.widget, control.list and control.list[value] or value or "") + end + end + + local questEnabled = F.DB.questTrackingEnabled ~= false + for key, _ in pairs(questRelatedKeys) do + local control = controls[key] + if control then + if control.type == "checkbox" or control.type == "slider" then + control.widget:SetEnabled(questEnabled) + if control.widget.Text then + local color = questEnabled and NORMAL_FONT_COLOR or GRAY_FONT_COLOR + control.widget.Text:SetTextColor(color.r, color.g, color.b) + end + elseif control.type == "color" then + if questEnabled then + control.widget:Enable() + control.label:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b) + else + control.widget:Disable() + control.label:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b) + end + end + end + end +end + +function F.Options.Open() + if not panel then return end + + if Settings and Settings.OpenToCategory then + Settings.OpenToCategory(panel.name) + elseif InterfaceOptionsFrame_OpenToCategory then + InterfaceOptionsFrame_OpenToCategory(panel) + InterfaceOptionsFrame_OpenToCategory(panel) + end +end + +function F.Options.Create() + if panel then return end + + panel = CreateFrame("Frame", "NXP_OptionsPanel", UIParent) + panel.name = "NixxnuxXPBar" + + local scroll = CreateFrame("ScrollFrame", "NXP_OptionsScrollFrame", panel, "UIPanelScrollFrameTemplate") + scroll:SetPoint("TOPLEFT", panel, "TOPLEFT", 0, -8) + scroll:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -30, 8) + + content = CreateFrame("Frame", nil, scroll) + content:SetSize(1, 1) + scroll:SetScrollChild(content) + + local title = content:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge") + title:SetPoint("TOPLEFT", content, "TOPLEFT", 16, -8) + title:SetText("NixxnuxXPBar") + + local y = -40 + y = CreateHeader(content, "General", y) + y = CreateCheckbox(content, "enabled", "Enable addon", y) + y = CreateCheckbox(content, "questTrackingEnabled", "Enable quest tracking", y) + y = CreateCheckbox(content, "trackedOnly", "Count tracked (pinned) quests only", y) + y = CreateCheckbox(content, "showIncompleteQuestBar", "Show uncompleted quest XP bar", y) + y = CreateCheckbox(content, "showTicks", "Show XP ticks", y) + y = y - 6 + y = CreateSlider(content, "tickOpacity", "Tick opacity", y, 10, 100, 1) + + y = y - 10 + y = CreateHeader(content, "Position", y) + y = y - 6 + local anchorList = { + TOP = "Top", + BOTTOM = "Bottom", + LEFT = "Left", + RIGHT = "Right", + CENTER = "Center", + } + local anchorOrder = { "TOP", "BOTTOM", "LEFT", "RIGHT", "CENTER" } + y = CreateDropdown(content, "anchorPoint", "Anchor position", y, anchorList, anchorOrder) + y = CreateSliderWithInput(content, "offsetX", "X offset", y, -1000, 1000, 1, 60) + y = CreateSliderWithInput(content, "offsetY", "Y offset", y, -1000, 1000, 1, 60) + + y = y - 10 + y = CreateHeader(content, "Bar Size", y) + y = y - 6 + y = CreateSlider(content, "barWidth", "Bar width", y, 200, 900, 1) + y = CreateSlider(content, "barHeight", "Bar height", y, 8, 60, 1) + + y = y - 6 + y = CreateHeader(content, "Bar Colors", y) + y = CreateColorPicker(content, "colorMain", "Normal XP", y, { 0.76, 0.38, 1, 1 }) + y = CreateColorPicker(content, "colorRested", "Rested XP", y, { 0.34, 0.61, 0.99, 0.8 }) + y = CreateColorPicker(content, "colorQuest", "Uncompleted Quest XP", y, { 1, 0.64, 0.0078, 0.25 }) + y = CreateColorPicker(content, "colorQuestComplete", "Completed Quest XP", y, { 1, 0.64, 0.0078, 0.8 }) + + y = y - 6 + y = CreateHeader(content, "Textures", y) + y = y - 6 + local statusbarList, statusbarOrder = BuildMediaList("statusbar", "Default (Solid)", DEFAULT_STATUSBAR) + y = CreateDropdown(content, "barTextureMain", "Normal XP texture", y, statusbarList, statusbarOrder) + y = CreateDropdown(content, "barTextureRested", "Rested XP texture", y, statusbarList, statusbarOrder) + y = CreateDropdown(content, "barTextureQuest", "Uncompleted quest XP texture", y, statusbarList, statusbarOrder) + y = CreateDropdown(content, "barTextureQuestComplete", "Completed quest XP texture", y, statusbarList, statusbarOrder) + + y = y - 6 + y = CreateHeader(content, "Font", y) + y = y - 6 + local fontList, fontOrder = BuildMediaList("font", "Default", DEFAULT_FONT) + y = CreateDropdown(content, "fontPath", "Font", y, fontList, fontOrder) + + y = y - 10 + y = CreateHeader(content, "Text Labels", y) + y = CreateCheckbox(content, "showLevelText", "Level text", y) + y = CreateCheckbox(content, "showXPText", "XP text", y) + y = CreateCheckbox(content, "showPercentText", "Percent text", y) + y = CreateCheckbox(content, "showQuestRestedText", "Quest/rested percent text", y) + y = CreateCheckbox(content, "showLevelingText", "Leveling info text", y) + y = CreateCheckbox(content, "showIncompleteQuestXPText", "Uncompleted quest XP text", y) + y = CreateCheckbox(content, "showCompletedQuestXPText", "Completed quest XP text", y) + y = CreateCheckbox(content, "showRestedXPText", "Rested XP text", y) + + content:SetHeight(-y + 20) + + panel:SetScript("OnShow", function() + F.Options.Refresh() + end) + + if Settings and Settings.RegisterCanvasLayoutCategory then + local category = Settings.RegisterCanvasLayoutCategory(panel, panel.name) + Settings.RegisterAddOnCategory(category) + elseif InterfaceOptions_AddCategory then + InterfaceOptions_AddCategory(panel) + end +end + +F.Options.Create() diff --git a/State.lua b/State.lua --- a/State.lua +++ b/State.lua @@ -65,6 +65,13 @@ end function F.State:UpdateQuestXP() + if F.DB and F.DB.questTrackingEnabled == false then + self.questXP = 0 + self.completeXP = 0 + self.incompleteXP = 0 + return + end + local numEntries = C_QuestLog.GetNumQuestLogEntries() local qXP, cXP, iXP = 0, 0, 0 diff --git a/UI.lua b/UI.lua --- a/UI.lua +++ b/UI.lua @@ -32,81 +32,11 @@ function F.UI.Create() local f = CreateFrame("Frame", "NXP_Frame", UIParent) f:SetSize(CFG.width, CFG.height) - f:SetPoint("BOTTOM", UIParent, "BOTTOM", 0, 260) -- Default Position + f:SetPoint("TOP", UIParent, "TOP", 0, 100) -- Default Position f:SetFrameStrata("LOW") - -- Enable dragging (only while Shift is held) - f:SetMovable(true) + f:SetMovable(false) f:EnableMouse(false) - f:RegisterForDrag("LeftButton") - - local function stopAndMaybeSnap(self) - if self.__nxb_moving then - self:StopMovingOrSizing() - self.__nxb_moving = nil - - local fx, fy = self:GetCenter() - if fx and fy then - local cx, cy = UIParent:GetCenter() - if cx and cy then - local dx, dy = fx - cx, fy - cy - local distSq = dx * dx + dy * dy - local snapRadius = 40 - if distSq <= (snapRadius * snapRadius) then - self:ClearAllPoints() - self:SetPoint("CENTER", UIParent, "CENTER", 0, 0) - end - end - end - - self:SetScript("OnUpdate", nil) - end - end - - f:SetScript("OnDragStart", function(self) - if IsShiftKeyDown() then - self:StartMoving() - self.__nxb_moving = true - - self:SetScript("OnUpdate", function(self, elapsed) - if not IsShiftKeyDown() or not IsMouseButtonDown("LeftButton") then - stopAndMaybeSnap(self) - end - end) - end - end) - - f:SetScript("OnDragStop", stopAndMaybeSnap) - - f:SetScript("OnMouseDown", function(self, button) - if button == "LeftButton" and IsShiftKeyDown() then - self:StartMoving() - self.__nxb_moving = true - - self:SetScript("OnUpdate", function(self, elapsed) - if not IsShiftKeyDown() or not IsMouseButtonDown("LeftButton") then - stopAndMaybeSnap(self) - end - end) - end - end) - - f:SetScript("OnMouseUp", function(self, button) - if button == "LeftButton" then - stopAndMaybeSnap(self) - end - end) - - f:RegisterEvent("MODIFIER_STATE_CHANGED") - f:SetScript("OnEvent", function(self, event, key, state) - if event == "MODIFIER_STATE_CHANGED" then - -- Enable/disable clicks based on Shift state - self:EnableMouse(IsShiftKeyDown()) - if not IsShiftKeyDown() and self.__nxb_moving then - stopAndMaybeSnap(self) - end - end - end) F.Frame = f @@ -127,6 +57,18 @@ -- Main XP Bar F.BarMain = F.UI.CreateStatusBar(f, "ARTWORK", 1, CFG.colorMain) + + -- XP Ticks (20 segments) + local tickContainer = CreateFrame("Frame", nil, f) + tickContainer:SetAllPoints() + tickContainer.ticks = {} + for i = 1, 19 do + local tick = tickContainer:CreateTexture(nil, "OVERLAY") + tick:SetColorTexture(0, 0, 0, 0.35) + tick:SetWidth(1) + tickContainer.ticks[i] = tick + end + F.TickContainer = tickContainer local overlay = CreateFrame("Frame", nil, f) overlay:SetAllPoints() @@ -215,6 +157,97 @@ local s = F.State local db = F.DB + + if db and db.barWidth and db.barHeight then + local width = db.barWidth + if width then + local scale = F.Frame:GetEffectiveScale() or 1 + local stepPixels = math.floor(((width / 20) * scale) + 0.5) + width = (stepPixels / scale) * 20 + end + F.Frame:SetSize(width or db.barWidth, db.barHeight) + end + + if db then + local anchor = db.anchorPoint or "TOP" + local x = tonumber(db.offsetX) or 0 + local y = tonumber(db.offsetY) or 0 + if anchor == "TOP" then + y = -y + end + F.Frame:ClearAllPoints() + F.Frame:SetPoint(anchor, UIParent, anchor, x, y) + end + + if F.TickContainer then + if db and db.showTicks then + local width = F.Frame:GetWidth() + local height = F.Frame:GetHeight() + if width and width > 0 then + local step = width / 20 + local scale = F.Frame:GetEffectiveScale() or 1 + local opacity = 0.35 + if db and type(db.tickOpacity) == "number" then + opacity = db.tickOpacity + end + for i = 1, 19 do + local tick = F.TickContainer.ticks[i] + if tick then + local x = step * i + x = math.floor((x * scale) + 0.5) / scale + tick:ClearAllPoints() + tick:SetColorTexture(0, 0, 0, opacity) + tick:SetPoint("TOPLEFT", F.Frame, "TOPLEFT", x, 0) + tick:SetPoint("BOTTOMLEFT", F.Frame, "BOTTOMLEFT", x, 0) + tick:Show() + end + end + F.TickContainer:Show() + else + F.TickContainer:Hide() + end + else + F.TickContainer:Hide() + end + end + + local function resolveColor(color, fallback) + if type(color) == "table" then + if color.r then + return color.r or 1, color.g or 1, color.b or 1, color.a or 1 + end + if color[1] then + return color[1] or 1, color[2] or 1, color[3] or 1, color[4] or 1 + end + end + return fallback[1], fallback[2], fallback[3], fallback[4] + end + + if db then + local fontPath = db.fontPath or CFG.font + F.LevelText:SetFont(fontPath, CFG.barFontSize, "OUTLINE") + F.TextCenter:SetFont(fontPath, CFG.barFontSize, "OUTLINE") + F.TextRight:SetFont(fontPath, CFG.barFontSize, "OUTLINE") + F.InfoText:SetFont(fontPath, CFG.fontSize - 2, "OUTLINE") + F.LevelingText:SetFont(fontPath, CFG.fontSize - 2, "OUTLINE") + if F.QuestXPText then + F.QuestXPText:SetFont(fontPath, CFG.fontSize - 2, "OUTLINE") + end + + local mainTexture = db.barTextureMain or CFG.texture + local restedTexture = db.barTextureRested or CFG.texture + local questTexture = db.barTextureQuest or CFG.texture + local questCompleteTexture = db.barTextureQuestComplete or CFG.texture + F.BarMain:SetStatusBarTexture(mainTexture) + F.BarRested:SetStatusBarTexture(restedTexture) + F.BarQuest:SetStatusBarTexture(questTexture) + F.BarQuestComplete:SetStatusBarTexture(questCompleteTexture) + + F.BarMain:SetStatusBarColor(resolveColor(db.colorMain, CFG.colorMain)) + F.BarRested:SetStatusBarColor(resolveColor(db.colorRested, CFG.colorRested)) + F.BarQuest:SetStatusBarColor(resolveColor(db.colorQuest, CFG.colorQuest)) + F.BarQuestComplete:SetStatusBarColor(resolveColor(db.colorQuestComplete, CFG.colorQuestComplete)) + end if s.isMaxLevel and not db.showAtMaxLevel then F.Frame:Hide() @@ -316,7 +349,7 @@ local infoParts = {} if db.showQuestRestedText and s.maxXP > 0 then - if (s.completeXP or 0) > 0 then + if db.questTrackingEnabled ~= false and (s.completeXP or 0) > 0 then table.insert(infoParts, string.format("Completed Quests: %.1f%%", ((s.completeXP or 0) / s.maxXP * 100))) end if (s.restedXP or 0) > 0 then @@ -350,11 +383,13 @@ 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))) + if db.questTrackingEnabled ~= false then + 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 end if db.showRestedXPText and (s.restedXP or 0) > 0 then table.insert(xpParts, string.format("Rested XP: %s", FormatLargeNumber(s.restedXP or 0)))