Adds pantheons from temmings' pull request Adds partial Impale support from baranio's pull request Adds updated uniques from PJacek's pull request and my own Adds more tree highlighting options for node power from coldino's pull request Adds support for fossil mods in the crafting window. Including correct parsing for some mods that previously didn't work
308 lines
9.4 KiB
Lua
308 lines
9.4 KiB
Lua
-- Path of Building
|
|
--
|
|
-- Class: Item DB
|
|
-- Item DB control.
|
|
--
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local t_insert = table.insert
|
|
local m_max = math.max
|
|
local m_floor = math.floor
|
|
|
|
|
|
local ItemDBClass = newClass("ItemDBControl", "ListControl", function(self, anchor, x, y, width, height, itemsTab, db, dbType)
|
|
self.ListControl(anchor, x, y, width, height, 16, false, false)
|
|
self.itemsTab = itemsTab
|
|
self.db = db
|
|
self.dbType = dbType
|
|
self.dragTargetList = { }
|
|
self.sortControl = {
|
|
NAME = { key = "name", dir = "ASCEND", func = function(a,b) return a:gsub("^The ","") < b:gsub("^The ","") end },
|
|
STAT = { key = "measuredPower", dir = "DESCEND" },
|
|
}
|
|
self.sortDropList = { }
|
|
self.sortOrder = { }
|
|
self.sortMode = "NAME"
|
|
local leagueFlag = { }
|
|
local typeFlag = { }
|
|
for _, item in pairs(db.list) do
|
|
if item.league then
|
|
for leagueName in item.league:gmatch(" ?([%w ]+),?") do
|
|
leagueFlag[leagueName] = true
|
|
end
|
|
end
|
|
typeFlag[item.type] = true
|
|
end
|
|
self.leagueList = { }
|
|
for leagueName in pairs(leagueFlag) do
|
|
t_insert(self.leagueList, leagueName)
|
|
end
|
|
table.sort(self.leagueList)
|
|
t_insert(self.leagueList, 1, "Any league")
|
|
t_insert(self.leagueList, 2, "No league")
|
|
self.typeList = { }
|
|
for type in pairs(typeFlag) do
|
|
t_insert(self.typeList, type)
|
|
end
|
|
table.sort(self.typeList)
|
|
t_insert(self.typeList, 1, "Any type")
|
|
t_insert(self.typeList, 2, "Armour")
|
|
t_insert(self.typeList, 3, "Jewellery")
|
|
t_insert(self.typeList, 4, "One Handed Melee")
|
|
t_insert(self.typeList, 5, "Two Handed Melee")
|
|
self.slotList = { "Any slot", "Weapon 1", "Weapon 2", "Helmet", "Body Armour", "Gloves", "Boots", "Amulet", "Ring", "Belt", "Jewel" }
|
|
local baseY = dbType == "RARE" and -22 or -42
|
|
self.controls.slot = new("DropDownControl", {"BOTTOMLEFT",self,"TOPLEFT"}, 0, baseY, 179, 18, self.slotList, function(index, value)
|
|
self.listBuildFlag = true
|
|
end)
|
|
self.controls.type = new("DropDownControl", {"LEFT",self.controls.slot,"RIGHT"}, 2, 0, 179, 18, self.typeList, function(index, value)
|
|
self.listBuildFlag = true
|
|
end)
|
|
if dbType == "UNIQUE" then
|
|
self.controls.sort = new("DropDownControl", {"BOTTOMLEFT",self,"TOPLEFT"}, 0, baseY + 20, 179, 18, self.sortDropList, function(index, value)
|
|
self:SetSortMode(value.sortMode)
|
|
end)
|
|
self.controls.league = new("DropDownControl", {"LEFT",self.controls.sort,"RIGHT"}, 2, 0, 179, 18, self.leagueList, function(index, value)
|
|
self.listBuildFlag = true
|
|
end)
|
|
end
|
|
self.controls.search = new("EditControl", {"BOTTOMLEFT",self,"TOPLEFT"}, 0, -2, 258, 18, "", "Search", "%c", 100, function()
|
|
self.listBuildFlag = true
|
|
end)
|
|
self.controls.searchMode = new("DropDownControl", {"LEFT",self.controls.search,"RIGHT"}, 2, 0, 100, 18, { "Anywhere", "Names", "Modifiers" }, function(index, value)
|
|
self.listBuildFlag = true
|
|
end)
|
|
self:BuildSortOrder()
|
|
self.listBuildFlag = true
|
|
end)
|
|
|
|
function ItemDBClass:DoesItemMatchFilters(item)
|
|
if self.controls.slot.selIndex > 1 then
|
|
local primarySlot = item:GetPrimarySlot()
|
|
if primarySlot ~= self.slotList[self.controls.slot.selIndex] and primarySlot:gsub(" %d","") ~= self.slotList[self.controls.slot.selIndex] then
|
|
return false
|
|
end
|
|
end
|
|
local typeSel = self.controls.type.selIndex
|
|
if typeSel > 1 then
|
|
if typeSel == 2 then
|
|
if not item.base.armour then
|
|
return false
|
|
end
|
|
elseif typeSel == 3 then
|
|
if not (item.type == "Amulet" or item.type == "Ring" or item.type == "Belt") then
|
|
return false
|
|
end
|
|
elseif typeSel == 4 or typeSel == 5 then
|
|
local weaponInfo = self.itemsTab.build.data.weaponTypeInfo[item.type]
|
|
if not (weaponInfo and weaponInfo.melee and ((typeSel == 4 and weaponInfo.oneHand) or (typeSel == 5 and not weaponInfo.oneHand))) then
|
|
return false
|
|
end
|
|
elseif item.type ~= self.typeList[typeSel] then
|
|
return false
|
|
end
|
|
end
|
|
if self.dbType == "UNIQUE" and self.controls.league.selIndex > 1 then
|
|
if (self.controls.league.selIndex == 2 and item.league) or (self.controls.league.selIndex > 2 and (not item.league or not item.league:match(self.leagueList[self.controls.league.selIndex]))) then
|
|
return false
|
|
end
|
|
end
|
|
local searchStr = self.controls.search.buf:lower()
|
|
if searchStr:match("%S") then
|
|
local found = false
|
|
local mode = self.controls.searchMode.selIndex
|
|
if mode == 1 or mode == 2 then
|
|
if item.name:lower():find(searchStr, 1, true) then
|
|
found = true
|
|
end
|
|
end
|
|
if mode == 1 or mode == 3 then
|
|
for _, line in pairs(item.modLines) do
|
|
if line.line:lower():find(searchStr, 1, true) then
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
if not found then
|
|
searchStr = searchStr:gsub(" ","")
|
|
for i, mod in ipairs(item.baseModList) do
|
|
if mod.name:lower():find(searchStr, 1, true) then
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if not found then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function ItemDBClass:SetSortMode(sortMode)
|
|
self.sortMode = sortMode
|
|
self:BuildSortOrder()
|
|
self.listBuildFlag = true
|
|
end
|
|
|
|
function ItemDBClass:BuildSortOrder()
|
|
wipeTable(self.sortDropList)
|
|
for id,stat in pairs(data.powerStatList) do
|
|
if not stat.ignoreForItems then
|
|
t_insert(self.sortDropList, {
|
|
label="Sort by "..stat.label,
|
|
sortMode=stat.itemField or stat.stat,
|
|
itemField=stat.itemField,
|
|
stat=stat.stat,
|
|
transform=stat.transform,
|
|
})
|
|
end
|
|
end
|
|
wipeTable(self.sortOrder)
|
|
if self.controls.sort then
|
|
self.controls.sort.selIndex = 1
|
|
self.controls.sort:SelByValue(self.sortMode, "sortMode")
|
|
self.sortDetail = self.controls.sort.list[self.controls.sort.selIndex]
|
|
end
|
|
if self.sortDetail and self.sortDetail.stat then
|
|
t_insert(self.sortOrder, self.sortControl.STAT)
|
|
end
|
|
t_insert(self.sortOrder, self.sortControl.NAME)
|
|
end
|
|
|
|
function ItemDBClass:ListBuilder()
|
|
local list = { }
|
|
for id, item in pairs(self.db.list) do
|
|
if self:DoesItemMatchFilters(item) then
|
|
t_insert(list, item)
|
|
end
|
|
end
|
|
|
|
if self.sortDetail and self.sortDetail.stat then -- stat-based
|
|
local start = GetTime()
|
|
local calcFunc, calcBase = self.itemsTab.build.calcsTab:GetMiscCalculator(self.build)
|
|
local originalValue = calcBase[self.sortMode]
|
|
for itemIndex, item in ipairs(list) do
|
|
item.measuredPower = 0
|
|
for slotName, slot in pairs(self.itemsTab.slots) do
|
|
if self.itemsTab:IsItemValidForSlot(item, slotName) and not slot.inactive and (not slot.weaponSet or slot.weaponSet == (self.itemsTab.activeItemSet.useSecondWeaponSet and 2 or 1)) then
|
|
local output = calcFunc(item.base.flask and { toggleFlask = item } or { repSlotName = slotName, repItem = item })
|
|
item.measuredPower = output[self.sortMode] or 0
|
|
if self.sortDetail.transform then
|
|
item.measuredPower = self.sortDetail.transform(item.measuredPower)
|
|
end
|
|
end
|
|
end
|
|
local now = GetTime()
|
|
if now - start > 50 then
|
|
self.defaultText = "^7Sorting... ("..m_floor(itemIndex/#list*100).."%)"
|
|
coroutine.yield()
|
|
start = now
|
|
end
|
|
end
|
|
end
|
|
|
|
table.sort(list, function(a, b)
|
|
for _, data in ipairs(self.sortOrder) do
|
|
local aVal = a[data.key]
|
|
local bVal = b[data.key]
|
|
if aVal ~= bVal then
|
|
if data.dir == "DESCEND" then
|
|
if data.func then
|
|
return data.func(bVal, aVal)
|
|
else
|
|
return bVal < aVal
|
|
end
|
|
else
|
|
if data.func then
|
|
return data.func(aVal, bVal)
|
|
else
|
|
return aVal < bVal
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
self.list = list
|
|
self.defaultText = "^7No items found that match those filters."
|
|
end
|
|
|
|
function ItemDBClass:Draw(viewPort)
|
|
if self.itemsTab.build.outputRevision ~= self.listOutputRevision then
|
|
self.listBuildFlag = true
|
|
end
|
|
if self.listBuildFlag then
|
|
self.listBuildFlag = false
|
|
wipeTable(self.list)
|
|
self.listBuilder = coroutine.create(self.ListBuilder)
|
|
self.listOutputRevision = self.itemsTab.build.outputRevision
|
|
end
|
|
if self.listBuilder then
|
|
local res, errMsg = coroutine.resume(self.listBuilder, self)
|
|
if launch.devMode and not res then
|
|
error(errMsg)
|
|
end
|
|
if coroutine.status(self.listBuilder) == "dead" then
|
|
self.listBuilder = nil
|
|
end
|
|
end
|
|
self.ListControl.Draw(self, viewPort)
|
|
end
|
|
|
|
function ItemDBClass:GetRowValue(column, index, item)
|
|
if column == 1 then
|
|
return colorCodes[item.rarity] .. item.name
|
|
end
|
|
end
|
|
|
|
function ItemDBClass:AddValueTooltip(tooltip, index, item)
|
|
if main.popups[1] then
|
|
tooltip:Clear()
|
|
return
|
|
end
|
|
if tooltip:CheckForUpdate(item, IsKeyDown("SHIFT"), launch.devModeAlt, self.itemsTab.build.outputRevision) then
|
|
self.itemsTab:AddItemTooltip(tooltip, item, nil, true)
|
|
end
|
|
end
|
|
|
|
function ItemDBClass:GetDragValue(index, item)
|
|
return "Item", item
|
|
end
|
|
|
|
function ItemDBClass:OnSelClick(index, item, doubleClick)
|
|
if IsKeyDown("CTRL") then
|
|
-- Add item
|
|
local newItem = new("Item", self.itemsTab.build.targetVersion, item.raw)
|
|
newItem:NormaliseQuality()
|
|
self.itemsTab:AddItem(newItem, true)
|
|
|
|
-- Equip item if able
|
|
local slotName = newItem:GetPrimarySlot()
|
|
if slotName and self.itemsTab.slots[slotName] then
|
|
if self.itemsTab.slots[slotName].weaponSet == 1 and self.itemsTab.activeItemSet.useSecondWeaponSet then
|
|
-- Redirect to second weapon set
|
|
slotName = slotName .. " Swap"
|
|
end
|
|
if IsKeyDown("SHIFT") then
|
|
-- Redirect to second slot if possible
|
|
local altSlot = slotName:gsub("1","2")
|
|
if self.itemsTab:IsItemValidForSlot(newItem, altSlot) then
|
|
slotName = altSlot
|
|
end
|
|
end
|
|
self.itemsTab.slots[slotName]:SetSelItemId(newItem.id)
|
|
end
|
|
|
|
self.itemsTab:PopulateSlots()
|
|
self.itemsTab:AddUndoState()
|
|
self.itemsTab.build.buildFlag = true
|
|
elseif doubleClick then
|
|
self.itemsTab:CreateDisplayItemFromRaw(item.raw, true)
|
|
end
|
|
end
|
|
|
|
function ItemDBClass:OnSelCopy(index, item)
|
|
Copy(item.raw:gsub("\n","\r\n"))
|
|
end |