diff --git a/EllesmereUI.lua b/EllesmereUI.lua index 866cf8e06..bc4d57b0c 100644 --- a/EllesmereUI.lua +++ b/EllesmereUI.lua @@ -2075,6 +2075,71 @@ do return text, map[key or ""] or W end + -- Crest bonuses identify the crafting tier independently of quality and + -- overlapping item levels. Midnight S2 Hero/Myth: 13835/13836. + -- Source: https://www.raidbots.com/static/data/live/bonuses.json + local craftedColors = { [13835] = HE, [13836] = MY } + function EllesmereUI.GetCraftedTrackColor(itemLink) + if type(itemLink) ~= "string" then return nil end + local payload = itemLink:match("item:([^|]+)") + if not payload then return nil end + local index, lastBonus = 0, 13 + for field in (payload .. ":"):gmatch("([^:]*):") do + index = index + 1 + if index == 13 then + lastBonus = 13 + (tonumber(field) or 0) + elseif index > 13 then + if index > lastBonus then break end + local color = craftedColors[tonumber(field)] + if color then return color end + end + end + end + + -- Season-specific bonuses keep older gear out of current track colours. + -- Update these IDs each season. Midnight S2 uses six ranks per track, + -- plus Hero/Myth crest bonuses for crafts, independent of craft quality. + -- Source: https://www.raidbots.com/static/data/live/bonuses.json + local currentSeasonColors = {} + for bonusID, color in pairs(craftedColors) do currentSeasonColors[bonusID] = color end + for firstBonus, color in pairs({ [12817] = W, [12825] = VE, [12833] = CH, [12841] = HE, [12849] = MY }) do + for rank = 0, 5 do currentSeasonColors[firstBonus + rank] = color end + end + local previousSeasonBonuses = { + [13621] = true, [13622] = true, + [13653] = true, [13654] = true, [13655] = true, + [13786] = true, [13787] = true, [13788] = true, [13789] = true, + } + for _, firstBonus in ipairs({ 12769, 12777, 12785, 12793, 12801 }) do + for rank = 0, 5 do previousSeasonBonuses[firstBonus + rank] = true end + end + function EllesmereUI.GetSeasonItemLevelColor(itemLink, greyPreviousSeason) + if type(itemLink) ~= "string" then return nil end + local payload = itemLink:match("item:([^|]+)") + if not payload then return nil end + local index, lastBonus = 0, 13 + local currentSeason, previousSeason = false, false + for field in (payload .. ":"):gmatch("([^:]*):") do + index = index + 1 + if index == 13 then + lastBonus = 13 + (tonumber(field) or 0) + elseif index > 13 then + if index > lastBonus then break end + local bonusID = tonumber(field) + local color = currentSeasonColors[bonusID] + if color then return color, true end + -- Recrafted S2 gear can retain older bonuses. The Tidal + -- crafting marker takes precedence even without a crest. + if bonusID == 13751 then currentSeason = true end + if previousSeasonBonuses[bonusID] then previousSeason = true end + end + end + if previousSeason and not currentSeason then + return greyPreviousSeason and GR or nil, false + end + return W, currentSeason + end + -- Item-level text color: custom override > upgrade-track hue > item rarity > -- white. Shared by character sheet, inspect sheet and equipment flyout. function EllesmereUI.GetItemLevelColor(itemLink, itemQuality) diff --git a/EllesmereUIBags/EllesmereUIBags.lua b/EllesmereUIBags/EllesmereUIBags.lua index 9fa38fc61..60af477e7 100644 --- a/EllesmereUIBags/EllesmereUIBags.lua +++ b/EllesmereUIBags/EllesmereUIBags.lua @@ -383,6 +383,11 @@ do if GetUpgradeTrack and _trackRank then local _, color = GetUpgradeTrack(d.itemLink) rank = color and _trackRank[color] or 0 + local craftedColor = EUI.GetCraftedTrackColor(d.itemLink) + if craftedColor then + rank = _trackRank[craftedColor] + ilvl = C_Item.GetDetailedItemLevelInfo(d.itemLink) or ilvl + end end c = { name = name or "", quality = quality or 0, ilvl = ilvl or 0, itemType = itemType or "", rank = rank, complete = name ~= nil } @@ -400,6 +405,18 @@ do d._sortType = c.itemType d._sortTrackRank = c.rank d._sortGear = d.categoryIndex and IsGearCategory(d.categoryIndex) or false + local sortOrder = BP().bagGearSortOrder + if d._sortGear and (sortOrder == "seasonTrack" or sortOrder == "seasonIlvl" or sortOrder == "ilvl") then + if c.detailedIlvl == nil then c.detailedIlvl = C_Item.GetDetailedItemLevelInfo(d.itemLink) end + d._sortIlvl = c.detailedIlvl or c.ilvl + end + if (sortOrder == "seasonTrack" or sortOrder == "seasonIlvl") and d._sortGear then + if c.currentSeason == nil then + local _, currentSeason = EUI.GetSeasonItemLevelColor(d.itemLink) + c.currentSeason = currentSeason or false + end + d._sortCurrentSeason = c.currentSeason + end if c.complete then d._sortCached = true end end end @@ -407,8 +424,16 @@ do end local function VisualSortCompare(a, b) - -- Gear sort: track (descending) > ilvl (descending) -- only for gear categories + -- Gear sort defaults to track then ilvl; optional modes prioritise season or ilvl. if a._sortGear and b._sortGear then + local sortOrder = BP().bagGearSortOrder + if (sortOrder == "seasonTrack" or sortOrder == "seasonIlvl") + and a._sortCurrentSeason ~= b._sortCurrentSeason then + return a._sortCurrentSeason == true + end + if (sortOrder == "ilvl" or sortOrder == "seasonIlvl") and a._sortIlvl ~= b._sortIlvl then + return a._sortIlvl > b._sortIlvl + end if a._sortTrackRank ~= b._sortTrackRank then return a._sortTrackRank > b._sortTrackRank end if a._sortTrackRank > 0 and b._sortTrackRank > 0 then if a._sortIlvl ~= b._sortIlvl then return a._sortIlvl > b._sortIlvl end @@ -3070,7 +3095,7 @@ local function RenderButton(btn, data, _, col, row, startX, currentY, _, interac local trackColor = data._giTrackColor if BP().itemlevelUseCustomColor and BP().itemlevelCustomColor then r, g, b = BP().itemlevelCustomColor.r, BP().itemlevelCustomColor.g, BP().itemlevelCustomColor.b - elseif rankText ~= "" and trackColor then + elseif trackColor then r, g, b = trackColor.r, trackColor.g, trackColor.b else r, g, b = GetItemQualityColor(data._giQuality or 1) @@ -5578,6 +5603,13 @@ function EUI_Bags:RefreshInventory() d._giTrackRank = rankText d._giTrackColor = trackColor end + if d._giIlvl and not (BP().itemlevelUseCustomColor and BP().itemlevelCustomColor) then + if BP().bagSeasonColors then + d._giTrackColor = EUI.GetSeasonItemLevelColor(itemLink, BP().bagGreyPreviousSeason) + elseif not d._giTrackColor then + d._giTrackColor = EUI.GetCraftedTrackColor(itemLink) + end + end end -- Warbound check (warbank dim overlay) + WuE bind check (gear only, when bind-type text is enabled). if loc and C_Item.DoesItemExist(loc) then diff --git a/EllesmereUIBags/EllesmereUIBags_Bank.lua b/EllesmereUIBags/EllesmereUIBags_Bank.lua index b20fe6482..189d34762 100644 --- a/EllesmereUIBags/EllesmereUIBags_Bank.lua +++ b/EllesmereUIBags/EllesmereUIBags_Bank.lua @@ -2437,13 +2437,18 @@ function EUI_Bank:RefreshBank() if showIlvl then btn.ItemLevelText:SetText(giIlvl or "") local r, g, b - if GetUpgradeTrack then - local rankText, trackColor = GetUpgradeTrack(itemLink) - if BP().itemlevelUseCustomColor and BP().itemlevelCustomColor then - r, g, b = BP().itemlevelCustomColor.r, BP().itemlevelCustomColor.g, BP().itemlevelCustomColor.b - elseif rankText and rankText ~= "" and trackColor then - r, g, b = trackColor.r, trackColor.g, trackColor.b + if BP().itemlevelUseCustomColor and BP().itemlevelCustomColor then + r, g, b = BP().itemlevelCustomColor.r, BP().itemlevelCustomColor.g, BP().itemlevelCustomColor.b + else + local trackColor + if BP().bagSeasonColors then + trackColor = EUI.GetSeasonItemLevelColor(itemLink, BP().bagGreyPreviousSeason) + else + local rankText + rankText, trackColor = GetUpgradeTrack(itemLink) + if not rankText or rankText == "" then trackColor = EUI.GetCraftedTrackColor(itemLink) end end + if trackColor then r, g, b = trackColor.r, trackColor.g, trackColor.b end end if not r then r, g, b = GetItemQualityColor(quality) diff --git a/EllesmereUIBags/EllesmereUIBags_DB.lua b/EllesmereUIBags/EllesmereUIBags_DB.lua index a9a191c1f..0aef84441 100644 --- a/EllesmereUIBags/EllesmereUIBags_DB.lua +++ b/EllesmereUIBags/EllesmereUIBags_DB.lua @@ -19,6 +19,8 @@ local BAGS_DEFAULTS = { showItemlevelInBags = true, showUpgradeIndicator = true, bagShowTrackRank = false, + bagGreyPreviousSeason = false, + bagSeasonColors = false, itemlevelUseCustomColor = false, bagHideEmptyCategories = true, bagSplitSetGearBySet = false, @@ -35,6 +37,7 @@ local BAGS_DEFAULTS = { bagShowPinRecentTips = true, bagShowSortIcon = true, bagSortToBottom = false, + bagGearSortOrder = "track", bagHideRandomize = false, bagDefaultBagType = "all", -- "all" | "onebag" | "multibag" bagDefaultOneBag = false, -- legacy; migrated to bagDefaultBagType diff --git a/EllesmereUIOptions/EUI_Bags_Options.lua b/EllesmereUIOptions/EUI_Bags_Options.lua index 2b493cb8d..b54702251 100644 --- a/EllesmereUIOptions/EUI_Bags_Options.lua +++ b/EllesmereUIOptions/EUI_Bags_Options.lua @@ -393,7 +393,7 @@ initFrame:SetScript("OnEvent", function(self) end) end - -- Category Title Size | Show Item Level (+ inline cog: Gear Track Rank) + -- Category Title Size | Show Item Level (+ inline options cog) local ilvlRow ilvlRow, h = W:DualRow(parent, y, { type="slider", text="Category Title Size", min=8, max=16, step=1, @@ -413,11 +413,11 @@ initFrame:SetScript("OnEvent", function(self) end } ); y = y - h - -- Inline cog on Show Item Level (right region): Show Gear Track Rank - -- (gated by Show Item Level; the rank only renders when ilvl is shown). + -- Item level options are gated by Show Item Level. if not EllesmereUI._prebuilding then local _, ilCogShow = EllesmereUI.BuildCogPopup({ title = "Item Level Options", + minWidth = 340, rows = { { type="toggle", label="Show Gear Track Rank", get=function() return db.profile.bagShowTrackRank or false end, @@ -425,6 +425,24 @@ initFrame:SetScript("OnEvent", function(self) db.profile.bagShowTrackRank = v if _G.EUI_Bags and _G.EUI_Bags.RefreshInventory then _G.EUI_Bags:RefreshInventory() end end }, + { type="toggle", label="Season-aware colours", + tooltip="Reserve track colours for recognised current-season gear in bags and bank. Untracked item levels become white. Older gear uses rarity colours unless grey text is enabled. Custom colours take priority.", + get=function() return db.profile.bagSeasonColors or false end, + set=function(v) + db.profile.bagSeasonColors = v + if _G.EUI_Bags and _G.EUI_Bags.RefreshInventory then _G.EUI_Bags:RefreshInventory() end + local bank = _G.EUI_BankFrame + if bank and bank.RefreshBank then bank:RefreshBank() end + end }, + { type="toggle", label="Grey out previous-season item levels", + tooltip="With Season-aware colours enabled, use grey item-level text for recognised previous-season gear in bags and bank. Custom item-level colours take priority.", + get=function() return db.profile.bagGreyPreviousSeason or false end, + set=function(v) + db.profile.bagGreyPreviousSeason = v + if _G.EUI_Bags and _G.EUI_Bags.RefreshInventory then _G.EUI_Bags:RefreshInventory() end + local bank = _G.EUI_BankFrame + if bank and bank.RefreshBank then bank:RefreshBank() end + end }, }, }) local rightRgn = ilvlRow._rightRegion @@ -709,7 +727,17 @@ initFrame:SetScript("OnEvent", function(self) if not EllesmereUI._prebuilding then local _, sortCogShow = EllesmereUI.BuildCogPopup({ title = "Sort Options", + dropdownWidth = 180, rows = { + { type="dropdown", label="Gear sort order", + tooltip="Choose the sorting priority in bag equipment categories. Season puts recognised current-season gear first. Ilvl means item level, highest first. Track sorts Myth before Hero and lower tracks. The other gear priority breaks ties. Blizzard's bank and MultiBag sorting are unchanged.", + values={ track="Track > ilvl", seasonTrack="Season > track", seasonIlvl="Season > ilvl", ilvl="Ilvl > track" }, + order={ "track", "seasonTrack", "seasonIlvl", "ilvl" }, + get=function() return db.profile.bagGearSortOrder or "track" end, + set=function(v) + db.profile.bagGearSortOrder = v + if _G.EUI_Bags and _G.EUI_Bags.RefreshInventory then _G.EUI_Bags:RefreshInventory() end + end }, { type="toggle", label="Sort to Bottom", tooltip="Sorting normally packs your items into the first free slots, at the top of the grid. Turn this on to pack them into the last slots instead, so the empty slots end up at the top. The item order itself does not change. This affects the OneBag, MultiBag and bank views -- category views fill their own grid with no gaps, so there is nothing to move. MultiBag and the bank use Blizzard's own sorting, so while this is on it also flips Blizzard's cleanup direction; turning it back off restores the direction you had.", get=function() return db.profile.bagSortToBottom == true end, diff --git a/EllesmereUIOptions/EllesmereUI_Widgets.lua b/EllesmereUIOptions/EllesmereUI_Widgets.lua index 794f1997a..2c6981dde 100644 --- a/EllesmereUIOptions/EllesmereUI_Widgets.lua +++ b/EllesmereUIOptions/EllesmereUI_Widgets.lua @@ -4046,7 +4046,7 @@ local function BuildCogPopup(opts) tmpFS:Hide() if maxLblW < 10 then maxLblW = 60 end - local COG_DD_W = 130 + local COG_DD_W = opts.dropdownWidth or 130 local SLIDER_LEFT = SIDE_PAD + maxLblW + LABEL_SLIDER_GAP local TARGET_W = opts.minWidth or 260 local SLIDER_W = math.max(80, TARGET_W - SLIDER_LEFT - SLIDER_INPUT_GAP - INPUT_W - SIDE_PAD)