From 5a837926b36e92770943ae34ee70c1feabd1f966 Mon Sep 17 00:00:00 2001 From: xoxorwr Date: Fri, 31 Jul 2026 20:09:38 +0200 Subject: [PATCH 1/2] Add items filter by loadout --- src/Classes/ItemListControl.lua | 174 +++++++++++++++++++++++++++----- 1 file changed, 147 insertions(+), 27 deletions(-) diff --git a/src/Classes/ItemListControl.lua b/src/Classes/ItemListControl.lua index 53f9fde103e..622a7a57b02 100644 --- a/src/Classes/ItemListControl.lua +++ b/src/Classes/ItemListControl.lua @@ -4,21 +4,46 @@ -- Build item list control. -- local pairs = pairs +local ipairs = ipairs local t_insert = table.insert local ItemListClass = newClass("ItemListControl", "ListControl", function(self, anchor, rect, itemsTab, forceTooltip) self.ListControl(anchor, rect, 16, "VERTICAL", true, itemsTab.itemOrderList, forceTooltip) self.itemsTab = itemsTab - self.label = "^7All items:" self.defaultText = "^x7F7F7FThis is the list of items that have been added to this build.\nYou can add items to this list by dragging them from\none of the other lists, or by clicking 'Add to build' when\nviewing an item." self.dragTargetList = { } - self.controls.delete = new("ButtonControl", {"BOTTOMRIGHT",self,"TOPRIGHT"}, {0, -2, 60, 18}, "Delete", function() - self:OnSelDelete(self.selIndex, self.selValue) + self.controls.loadoutFilter = new("DropDownControl", {"BOTTOMLEFT",self,"TOPLEFT"}, {0, -2, 110, 18}, nil, function() + self:UpdateList() end) - self.controls.delete.enabled = function() - return self.selValue ~= nil + self.controls.loadoutFilter.enableDroppedWidth = true + self.controls.sort = new("ButtonControl", {"LEFT",self.controls.loadoutFilter,"RIGHT"}, {4, 0, 42, 18}, "Sort", function() + itemsTab:SortItemList() + self:UpdateList() + end) + self.controls.deleteUnused = new("ButtonControl", {"LEFT",self.controls.sort,"RIGHT"}, {4, 0, 84, 18}, "Del Unused", function() + local delList = {} + for _, itemId in pairs(self.list) do + if not itemsTab:GetEquippedSlotForItem(itemsTab.items[itemId]) and not self:FindEquippedAbyssJewel(itemId, false) and not self:FindSocketedJewel(itemId, false) then + t_insert(delList, itemId) + end + end + -- Delete in reverse order so as to not delete the wrong item whilst deleting + for i = #delList, 1, -1 do + itemsTab:DeleteItem(itemsTab.items[delList[i]], true) + end + -- Rebuild cluster jewel graphs, populate slots, and create an undo state, as we deferred doing this during itemsTab:DeleteItem(...) + for _, spec in pairs(itemsTab.build.treeTab.specList) do + spec:BuildClusterJewelGraphs() + end + itemsTab:PopulateSlots() + itemsTab:AddUndoState() + itemsTab.build.buildFlag = true + self:UpdateList() + end) + self.controls.deleteUnused.enabled = function() + return #self.list > 0 end - self.controls.deleteAll = new("ButtonControl", {"RIGHT",self.controls.delete,"LEFT"}, {-4, 0, 70, 18}, "Delete All", function() + self.controls.deleteAll = new("ButtonControl", {"LEFT",self.controls.deleteUnused,"RIGHT"}, {4, 0, 58, 18}, "Del All", function() main:OpenConfirmPopup("Delete All", "Are you sure you want to delete all items in this build?", "Delete", function() for _, slot in pairs(itemsTab.slots) do slot:SetSelItemId(0) @@ -35,37 +60,127 @@ local ItemListClass = newClass("ItemListControl", "ListControl", function(self, itemsTab.build.buildFlag = true self.selIndex = nil self.selValue = nil + self:UpdateList() end) end) self.controls.deleteAll.enabled = function() return #self.list > 0 end - self.controls.deleteUnused = new("ButtonControl", {"RIGHT",self.controls.deleteAll,"LEFT"}, {-4, 0, 100, 18}, "Delete Unused", function() - local delList = {} - for _, itemId in pairs(self.list) do - if not itemsTab:GetEquippedSlotForItem(itemsTab.items[itemId]) and not self:FindEquippedAbyssJewel(itemId, false) and not self:FindSocketedJewel(itemId, false) then - t_insert(delList, itemId) + self.controls.delete = new("ButtonControl", {"LEFT",self.controls.deleteAll,"RIGHT"}, {4, 0, 50, 18}, "Delete", function() + self:OnSelDelete(self.selIndex, self.selValue) + end) + self.controls.delete.enabled = function() + return self.selValue ~= nil + end +end) + +function ItemListClass:UpdateLoadoutList() + local list = { "Any Loadout", "Current Loadout", "Unused Items" } + local build = self.itemsTab.build + if build and build.controls and build.controls.buildLoadouts then + for _, val in ipairs(build.controls.buildLoadouts.list) do + if val ~= "^7^7Loadouts:" and val ~= "^7^7-----" and val ~= "^7^7New Loadout" and val ~= "^7^7Sync" and val ~= "^7^7Help >>" then + if not isValueInArray(list, val) then + t_insert(list, val) + end end end - -- Delete in reverse order so as to not delete the wrong item whilst deleting - for i = #delList, 1, -1 do - itemsTab:DeleteItem(itemsTab.items[delList[i]], true) + end + if self.itemsTab.itemSetOrderList then + for _, itemSetId in ipairs(self.itemsTab.itemSetOrderList) do + local itemSet = self.itemsTab.itemSets[itemSetId] + local title = itemSet and (itemSet.title or "Default") + if title and not isValueInArray(list, title) then + t_insert(list, title) + end end - -- Rebuild cluster jewel graphs, populate slots, and create an undo state, as we deferred doing this during itemsTab:DeleteItem(...) - for _, spec in pairs(itemsTab.build.treeTab.specList) do - spec:BuildClusterJewelGraphs() + end + local selIndex = self.controls.loadoutFilter.selIndex or 1 + self.controls.loadoutFilter:SetList(list) + self.controls.loadoutFilter.selIndex = math.min(selIndex, #list) +end + +function ItemListClass:IsItemInLoadout(itemId, filterVal) + local item = self.itemsTab.items[itemId] + if not item then + return false + end + + -- Check item sets + if self.itemsTab.itemSetOrderList then + for _, itemSetId in ipairs(self.itemsTab.itemSetOrderList) do + local itemSet = self.itemsTab.itemSets[itemSetId] + if itemSet then + local title = itemSet.title or "Default" + if title == filterVal or title:find(filterVal, 1, true) or filterVal:find(title, 1, true) or #self.itemsTab.itemSetOrderList == 1 then + local slot, equipSet = self.itemsTab:GetEquippedSlotForItem(item) + if (slot and (not equipSet or equipSet == itemSet)) or self:FindEquippedAbyssJewel(itemId, false) == title then + return true + end + end + end end - itemsTab:PopulateSlots() - itemsTab:AddUndoState() - itemsTab.build.buildFlag = true - end) - self.controls.deleteUnused.enabled = function() - return #self.list > 0 end - self.controls.sort = new("ButtonControl", {"RIGHT",self.controls.deleteUnused,"LEFT"}, {-4, 0, 60, 18}, "Sort", function() - itemsTab:SortItemList() - end) -end) + + -- Check passive tree specs + local treeTab = self.itemsTab.build.treeTab + if treeTab and treeTab.specList then + for _, spec in ipairs(treeTab.specList) do + local title = spec.title or "Default" + if title == filterVal or title:find(filterVal, 1, true) or filterVal:find(title, 1, true) or #treeTab.specList == 1 then + if self:FindSocketedJewel(itemId, false) == title then + return true + end + end + end + end + + return false +end + +function ItemListClass:UpdateList() + self:UpdateLoadoutList() + local selFilter = self.controls.loadoutFilter.selIndex or 1 + local filterVal = self.controls.loadoutFilter.list[selFilter] or "Any Loadout" + + if selFilter == 1 or filterVal == "Any Loadout" then + self.list = self.itemsTab.itemOrderList + return + end + + local newList = {} + for _, itemId in ipairs(self.itemsTab.itemOrderList) do + local item = self.itemsTab.items[itemId] + if item then + if selFilter == 2 or filterVal == "Current Loadout" then + if self.itemsTab:GetEquippedSlotForItem(item) or self:FindEquippedAbyssJewel(itemId, false) or self:FindSocketedJewel(itemId, false) then + t_insert(newList, itemId) + end + elseif selFilter == 3 or filterVal == "Unused Items" then + if not self.itemsTab:GetEquippedSlotForItem(item) and not self:FindEquippedAbyssJewel(itemId, false) and not self:FindSocketedJewel(itemId, false) then + t_insert(newList, itemId) + end + else + if self:IsItemInLoadout(itemId, filterVal) then + t_insert(newList, itemId) + end + end + end + end + self.list = newList + if self.selIndex and self.selIndex > #self.list then + self.selIndex = #self.list > 0 and #self.list or nil + self.selValue = self.selIndex and self.list[self.selIndex] or nil + end +end + +function ItemListClass:Draw(viewPort) + if self.itemsTab.build and self.itemsTab.build.outputRevision ~= self.lastOutputRevision then + self.lastOutputRevision = self.itemsTab.build.outputRevision + self:UpdateList() + end + self.ListControl.Draw(self, viewPort) +end function ItemListClass:FindSocketedJewel(jewelId, excludeActiveSpec) if not self.itemsTab.items[jewelId] or self.itemsTab.items[jewelId].type ~= "Jewel" then @@ -151,6 +266,7 @@ function ItemListClass:ReceiveDrag(type, value, source) self.itemsTab:AddItem(newItem, true, self.selDragIndex) self.itemsTab:PopulateSlots() self.itemsTab:AddUndoState() + self:UpdateList() end end @@ -204,6 +320,7 @@ function ItemListClass:OnSelDelete(index, itemId) self.itemsTab:DeleteItem(item) self.selIndex = nil self.selValue = nil + self:UpdateList() end) else local equipSet = self:FindEquippedAbyssJewel(itemId, true) @@ -213,6 +330,7 @@ function ItemListClass:OnSelDelete(index, itemId) self.itemsTab:DeleteItem(item) self.selIndex = nil self.selValue = nil + self:UpdateList() end) else local equipTree = self:FindSocketedJewel(itemId, true) @@ -221,11 +339,13 @@ function ItemListClass:OnSelDelete(index, itemId) self.itemsTab:DeleteItem(item) self.selIndex = nil self.selValue = nil + self:UpdateList() end) else self.itemsTab:DeleteItem(item) self.selIndex = nil self.selValue = nil + self:UpdateList() end end end From b1967da6d9d0e9a36adc2bfc31dc69abda407aac Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 3 Aug 2026 19:28:53 +1000 Subject: [PATCH 2/2] Fix item loadout filtering and deletion state Restrict Current Loadout filtering to the active item set and passive tree Resolve named loadouts using their actual set membership and linked identifiers Preserve support for old passive-tree display names Clear hidden selections and preserve visible selections by item ID Clear the canonical item order when deleting all items from a filtered view Delete unused items across the entire build Add regression tests for filtering, selection, and deletion behavior --- spec/System/TestItemListControl_spec.lua | 175 +++++++++++++++++++++++ src/Classes/ItemListControl.lua | 145 ++++++++++--------- 2 files changed, 254 insertions(+), 66 deletions(-) create mode 100644 spec/System/TestItemListControl_spec.lua diff --git a/spec/System/TestItemListControl_spec.lua b/spec/System/TestItemListControl_spec.lua new file mode 100644 index 00000000000..17982f5a310 --- /dev/null +++ b/spec/System/TestItemListControl_spec.lua @@ -0,0 +1,175 @@ +describe("ItemListControl", function() + local originalOpenConfirmPopup + + local function newItemListControl() + local activeItemSet = { + id = 1, + title = "Boss", + ["Body Armour"] = { selItemId = 1 }, + } + local otherItemSet = { + id = 2, + title = "Mapping", + ["Body Armour"] = { selItemId = 2 }, + } + local treeTab = { + activeSpec = 1, + specList = { + { + title = "Boss", + jewels = { [100] = 3 }, + nodes = { [100] = { alloc = true } }, + }, + { + title = "Mapping", + jewels = { [200] = 4 }, + nodes = { [200] = { alloc = true } }, + }, + }, + } + local itemsTab = { + itemOrderList = { 1, 2, 3, 4 }, + items = { + [1] = { id = 1, type = "Body Armour", base = { subType = "" } }, + [2] = { id = 2, type = "Body Armour", base = { subType = "" } }, + [3] = { id = 3, type = "Jewel", base = { subType = "" } }, + [4] = { id = 4, type = "Jewel", base = { subType = "" } }, + }, + itemSetOrderList = { 1, 2 }, + itemSets = { activeItemSet, otherItemSet }, + activeItemSetId = 1, + activeItemSet = activeItemSet, + slots = { }, + build = { + itemListSpecialLinks = { }, + treeListSpecialLinks = { }, + controls = { + buildLoadouts = { list = { "Boss", "Mapping" } }, + }, + treeTab = treeTab, + }, + PopulateSlots = function() end, + AddUndoState = function() end, + } + local control = new("ItemListControl", nil, { 0, 0, 360, 308 }, itemsTab, true) + return control, itemsTab, treeTab + end + + before_each(function() + originalOpenConfirmPopup = main.OpenConfirmPopup + end) + + after_each(function() + main.OpenConfirmPopup = originalOpenConfirmPopup + end) + + it("only shows items from the active item set and passive tree", function() + local control = newItemListControl() + control:UpdateLoadoutList() + control.controls.loadoutFilter.selIndex = 2 + + control:UpdateList() + + assert.are.same({ 1, 3 }, control.list) + end) + + it("uses the selected item set and passive tree for named loadouts", function() + local control = newItemListControl() + control:UpdateLoadoutList() + control.controls.loadoutFilter.selIndex = isValueInArray(control.controls.loadoutFilter.list, "Mapping") + + control:UpdateList() + + assert.are.same({ 2, 4 }, control.list) + end) + + it("matches linked sets and old passive tree display names", function() + local control, itemsTab, treeTab = newItemListControl() + itemsTab.itemSets[2].title = "Gear {mapping}" + treeTab.specList[2].title = "Tree {mapping}" + itemsTab.build.itemListSpecialLinks.mapping = { setId = 2 } + itemsTab.build.treeListSpecialLinks.mapping = { setId = 2 } + itemsTab.build.controls.buildLoadouts.list = { "Tree {mapping}", "[3.28] Boss" } + control:UpdateLoadoutList() + control.controls.loadoutFilter.selIndex = isValueInArray(control.controls.loadoutFilter.list, "Tree {mapping}") + + control:UpdateList() + + assert.are.same({ 2, 4 }, control.list) + + control.controls.loadoutFilter.selIndex = isValueInArray(control.controls.loadoutFilter.list, "[3.28] Boss") + control:UpdateList() + + assert.are.same({ 1, 3 }, control.list) + end) + + it("clears hidden selections and preserves visible selections by item ID", function() + local control = newItemListControl() + control:UpdateLoadoutList() + control.selIndex = 2 + control.selValue = 2 + control.controls.loadoutFilter.selIndex = 2 + + control:UpdateList() + + assert.is_nil(control.selIndex) + assert.is_nil(control.selValue) + + control.selIndex = 3 + control.selValue = 3 + control:UpdateList() + + assert.are.equal(2, control.selIndex) + assert.are.equal(3, control.selValue) + end) + + it("only allows internal reordering in the unfiltered item list", function() + local control = newItemListControl() + control:UpdateLoadoutList() + control:UpdateList() + + assert.is_true(control.isMutable) + + control.controls.loadoutFilter.selIndex = 2 + control:UpdateList() + + assert.is_false(control.isMutable) + + control.controls.loadoutFilter.selIndex = 1 + control:UpdateList() + + assert.is_true(control.isMutable) + end) + + it("refreshes filter options when loadouts are renamed without a new output revision", function() + local control, itemsTab, treeTab = newItemListControl() + itemsTab.build.outputRevision = 1 + control.lastOutputRevision = 1 + control:UpdateLoadoutList() + itemsTab.itemSets[2].title = "Renamed" + treeTab.specList[2].title = "Renamed" + itemsTab.build.controls.buildLoadouts.list = { "Boss", "Renamed" } + wipeTable(itemsTab.itemOrderList) + wipeTable(itemsTab.items) + + control:Draw({ x = 0, y = 0, width = 1920, height = 1080 }) + + assert.is_nil(isValueInArray(control.controls.loadoutFilter.list, "Mapping")) + assert.is_not_nil(isValueInArray(control.controls.loadoutFilter.list, "Renamed")) + end) + + it("clears the canonical item order when deleting all from a filtered list", function() + local control, itemsTab = newItemListControl() + control:UpdateLoadoutList() + control.controls.loadoutFilter.selIndex = isValueInArray(control.controls.loadoutFilter.list, "Mapping") + control:UpdateList() + main.OpenConfirmPopup = function(_, _, _, _, onConfirm) + onConfirm() + end + + control.controls.deleteAll.onClick() + + assert.are.same({ }, itemsTab.itemOrderList) + assert.are.same({ }, itemsTab.items) + end) +end) diff --git a/src/Classes/ItemListControl.lua b/src/Classes/ItemListControl.lua index 622a7a57b02..11ae71487b7 100644 --- a/src/Classes/ItemListControl.lua +++ b/src/Classes/ItemListControl.lua @@ -22,7 +22,7 @@ local ItemListClass = newClass("ItemListControl", "ListControl", function(self, end) self.controls.deleteUnused = new("ButtonControl", {"LEFT",self.controls.sort,"RIGHT"}, {4, 0, 84, 18}, "Del Unused", function() local delList = {} - for _, itemId in pairs(self.list) do + for _, itemId in pairs(itemsTab.itemOrderList) do if not itemsTab:GetEquippedSlotForItem(itemsTab.items[itemId]) and not self:FindEquippedAbyssJewel(itemId, false) and not self:FindSocketedJewel(itemId, false) then t_insert(delList, itemId) end @@ -53,7 +53,7 @@ local ItemListClass = newClass("ItemListControl", "ListControl", function(self, spec.jewels[nodeId] = 0 end end - wipeTable(self.list) + wipeTable(itemsTab.itemOrderList) wipeTable(self.itemsTab.items) itemsTab:PopulateSlots() itemsTab:AddUndoState() @@ -76,12 +76,14 @@ end) function ItemListClass:UpdateLoadoutList() local list = { "Any Loadout", "Current Loadout", "Unused Items" } + local listValues = { ["Any Loadout"] = true, ["Current Loadout"] = true, ["Unused Items"] = true } local build = self.itemsTab.build if build and build.controls and build.controls.buildLoadouts then for _, val in ipairs(build.controls.buildLoadouts.list) do if val ~= "^7^7Loadouts:" and val ~= "^7^7-----" and val ~= "^7^7New Loadout" and val ~= "^7^7Sync" and val ~= "^7^7Help >>" then - if not isValueInArray(list, val) then + if not listValues[val] then t_insert(list, val) + listValues[val] = true end end end @@ -90,93 +92,104 @@ function ItemListClass:UpdateLoadoutList() for _, itemSetId in ipairs(self.itemsTab.itemSetOrderList) do local itemSet = self.itemsTab.itemSets[itemSetId] local title = itemSet and (itemSet.title or "Default") - if title and not isValueInArray(list, title) then + if title and not listValues[title] then t_insert(list, title) + listValues[title] = true end end end - local selIndex = self.controls.loadoutFilter.selIndex or 1 - self.controls.loadoutFilter:SetList(list) - self.controls.loadoutFilter.selIndex = math.min(selIndex, #list) -end - -function ItemListClass:IsItemInLoadout(itemId, filterVal) - local item = self.itemsTab.items[itemId] - if not item then + local listKey = table.concat(list, "\0") + if self.loadoutListKey == listKey then return false end - - -- Check item sets - if self.itemsTab.itemSetOrderList then - for _, itemSetId in ipairs(self.itemsTab.itemSetOrderList) do - local itemSet = self.itemsTab.itemSets[itemSetId] - if itemSet then - local title = itemSet.title or "Default" - if title == filterVal or title:find(filterVal, 1, true) or filterVal:find(title, 1, true) or #self.itemsTab.itemSetOrderList == 1 then - local slot, equipSet = self.itemsTab:GetEquippedSlotForItem(item) - if (slot and (not equipSet or equipSet == itemSet)) or self:FindEquippedAbyssJewel(itemId, false) == title then - return true - end - end - end - end - end - - -- Check passive tree specs - local treeTab = self.itemsTab.build.treeTab - if treeTab and treeTab.specList then - for _, spec in ipairs(treeTab.specList) do - local title = spec.title or "Default" - if title == filterVal or title:find(filterVal, 1, true) or filterVal:find(title, 1, true) or #treeTab.specList == 1 then - if self:FindSocketedJewel(itemId, false) == title then - return true - end - end - end - end - - return false + self.loadoutListKey = listKey + local selIndex = self.controls.loadoutFilter.selIndex or 1 + local selValue = self.controls.loadoutFilter.list and self.controls.loadoutFilter.list[selIndex] or "Any Loadout" + self.controls.loadoutFilter:SetList(list) + self.controls.loadoutFilter.selIndex = isValueInArray(list, selValue) or 1 + return true end function ItemListClass:UpdateList() self:UpdateLoadoutList() local selFilter = self.controls.loadoutFilter.selIndex or 1 local filterVal = self.controls.loadoutFilter.list[selFilter] or "Any Loadout" + local selectedItemId = self.selValue if selFilter == 1 or filterVal == "Any Loadout" then self.list = self.itemsTab.itemOrderList - return - end - - local newList = {} - for _, itemId in ipairs(self.itemsTab.itemOrderList) do - local item = self.itemsTab.items[itemId] - if item then - if selFilter == 2 or filterVal == "Current Loadout" then - if self.itemsTab:GetEquippedSlotForItem(item) or self:FindEquippedAbyssJewel(itemId, false) or self:FindSocketedJewel(itemId, false) then - t_insert(newList, itemId) + self.isMutable = true + else + self.isMutable = false + local filterItemSet + local filterSpec + if selFilter == 2 or filterVal == "Current Loadout" then + filterItemSet = self.itemsTab.activeItemSet + filterSpec = self.itemsTab.build.treeTab.specList[self.itemsTab.build.treeTab.activeSpec] + elseif selFilter ~= 3 and filterVal ~= "Unused Items" then + local filterTitle = filterVal:gsub("^%[[^%]]+%]%s*", "") + for _, itemSetId in ipairs(self.itemsTab.itemSetOrderList) do + local itemSet = self.itemsTab.itemSets[itemSetId] + if (itemSet.title or "Default") == filterTitle then + filterItemSet = itemSet + break end - elseif selFilter == 3 or filterVal == "Unused Items" then - if not self.itemsTab:GetEquippedSlotForItem(item) and not self:FindEquippedAbyssJewel(itemId, false) and not self:FindSocketedJewel(itemId, false) then - t_insert(newList, itemId) + end + local treeTab = self.itemsTab.build.treeTab + for _, spec in ipairs(treeTab.specList) do + if (spec.title or "Default") == filterTitle then + filterSpec = spec + break end - else - if self:IsItemInLoadout(itemId, filterVal) then - t_insert(newList, itemId) + end + local linkId = filterVal:match("%{(%w+)%}") + local itemLink = linkId and self.itemsTab.build.itemListSpecialLinks and self.itemsTab.build.itemListSpecialLinks[linkId] + local treeLink = linkId and self.itemsTab.build.treeListSpecialLinks and self.itemsTab.build.treeListSpecialLinks[linkId] + filterItemSet = filterItemSet or #self.itemsTab.itemSetOrderList == 1 and self.itemsTab.itemSets[self.itemsTab.itemSetOrderList[1]] or itemLink and self.itemsTab.itemSets[itemLink.setId] + filterSpec = filterSpec or #treeTab.specList == 1 and treeTab.specList[1] or treeLink and treeTab.specList[treeLink.setId] + end + filterItemSet = filterItemSet or { } + local newList = {} + for _, itemId in ipairs(self.itemsTab.itemOrderList) do + local item = self.itemsTab.items[itemId] + if item then + if selFilter == 3 or filterVal == "Unused Items" then + if not self.itemsTab:GetEquippedSlotForItem(item) and not self:FindEquippedAbyssJewel(itemId, false) and not self:FindSocketedJewel(itemId, false) then + t_insert(newList, itemId) + end + else + local inLoadout = false + for _, slot in pairs(filterItemSet) do + if type(slot) == "table" and slot.selItemId == itemId then + inLoadout = true + break + end + end + if not inLoadout and filterSpec then + for nodeId, jewelId in pairs(filterSpec.jewels) do + if jewelId == itemId and filterSpec.nodes[nodeId] and filterSpec.nodes[nodeId].alloc then + inLoadout = true + break + end + end + end + if inLoadout then + t_insert(newList, itemId) + end end end end + self.list = newList end - self.list = newList - if self.selIndex and self.selIndex > #self.list then - self.selIndex = #self.list > 0 and #self.list or nil - self.selValue = self.selIndex and self.list[self.selIndex] or nil - end + self.selIndex = selectedItemId and isValueInArray(self.list, selectedItemId) or nil + self.selValue = self.selIndex and self.list[self.selIndex] or nil end function ItemListClass:Draw(viewPort) - if self.itemsTab.build and self.itemsTab.build.outputRevision ~= self.lastOutputRevision then - self.lastOutputRevision = self.itemsTab.build.outputRevision + local loadoutListChanged = self:UpdateLoadoutList() + local outputRevision = self.itemsTab.build and self.itemsTab.build.outputRevision + if loadoutListChanged or outputRevision ~= self.lastOutputRevision then + self.lastOutputRevision = outputRevision self:UpdateList() end self.ListControl.Draw(self, viewPort)