Files
PathOfBuilding/Modules/Items.lua
2016-05-18 21:59:04 +10:00

334 lines
12 KiB
Lua
Raw Blame History

-- Path of Building
--
-- Module: Items
-- Items view for the active build
--
local launch, main = ...
local t_insert = table.insert
local s_format = string.format
local baseSlots = { "Helmet", "Body Armour", "Gloves", "Boots", "Amulet", "Ring 1", "Ring 2", "Belt", "Weapon 1", "Weapon 2" }
local items = { }
function items:Init(build)
self.build = build
self.list = { }
self.orderList = { }
self.slots = { }
self.controls = { }
self.controls.addDisplayItem = common.New("ButtonControl", 0, 0, 60, 20, "Add", function()
self:AddDisplayItem()
end)
for index, slotName in pairs(baseSlots) do
t_insert(self.controls, common.New("ItemSlot", self, 400, (index - 1) * 20, slotName))
end
self.sockets = { }
for _, node in pairs(main.tree.nodes) do
if node.type == "socket" then
self.sockets[node.id] = common.New("ItemSlot", self, 400, 0, "Jewel "..node.id, "Socket")
end
end
end
function items:Shutdown()
self.controls = nil
self.slots = nil
self.list = nil
end
function items:Load(xml, dbFileName)
for _, node in ipairs(xml) do
if node.elem == "Item" then
local item = { }
item.raw = ""
item.id = tonumber(node.attrib.id)
itemLib.parseItemRaw(item)
for _, child in ipairs(node) do
if type(child) == "string" then
item.raw = child
itemLib.parseItemRaw(item)
elseif child.elem == "ModRange" then
local id = tonumber(child.attrib.id) or 0
local range = tonumber(child.attrib.range) or 1
if item.modLines[id] then
item.modLines[id].range = range
end
end
end
itemLib.buildItemModList(item)
self.list[item.id] = item
t_insert(self.orderList, item.id)
elseif node.elem == "Slot" then
if self.slots[node.attrib.name or ""] then
self.slots[node.attrib.name].selItem = tonumber(node.attrib.itemId)
end
end
end
self:PopulateSlots()
end
function items:Save(xml)
self.modFlag = false
for _, id in ipairs(self.orderList) do
local item = self.list[id]
local child = { elem = "Item", attrib = { id = tostring(id) } }
t_insert(child, item.raw)
for id, modLine in ipairs(item.modLines) do
if modLine.range then
t_insert(child, { elem = "ModRange", attrib = { id = tostring(id), range = tostring(modLine.range) } })
end
end
t_insert(xml, child)
end
for name, slot in pairs(self.slots) do
t_insert(xml, { elem = "Slot", attrib = { name = name, itemId = tostring(slot.selItem) }})
end
end
function items:DrawItems(viewPort, inputEvents)
common.controlsInput(self, inputEvents)
for id, event in ipairs(inputEvents) do
if event.type == "KeyDown" then
if event.key == "v" and IsKeyDown("CTRL") then
local newItem = Paste()
if newItem then
self.displayItem = {
raw = newItem:gsub("^%s+",""):gsub("%s+$",""):gsub("<EFBFBD>","-"):gsub("%b<>",""):gsub("<EFBFBD>","o")
}
itemLib.parseItemRaw(self.displayItem)
if not self.displayItem.baseName then
self.displayItem = nil
end
end
end
end
end
if self.displayItem then
self.controls.addDisplayItem.x = viewPort.x + viewPort.width - 530
self.controls.addDisplayItem.y = viewPort.y + 4
self.controls.addDisplayItem.hidden = false
self.controls.addDisplayItem.label = self.list[self.displayItem.id] and "Save" or "Add"
self:AddItemTooltip(self.displayItem)
main:DrawTooltip(viewPort.x + viewPort.width - 500, viewPort.y + 28, nil, nil, viewPort, data.colorCodes[self.displayItem.rarity], true)
else
self.controls.addDisplayItem.hidden = true
end
self:UpdateJewels()
common.controlsDraw(self, viewPort)
for index, id in pairs(self.orderList) do
local item = self.list[id]
local rarityCode = data.colorCodes[item.rarity]
SetDrawColor(rarityCode)
local x = viewPort.x + 2
local y = viewPort.y + 2 + 16 * (index - 1)
DrawString(x, y, "LEFT", 16, "VAR", item.name)
local cx, cy = GetCursorPos()
if cx >= x and cx < x + 250 and cy >= y and cy < y + 16 then
self:AddItemTooltip(item)
main:DrawTooltip(x, y, 250, 16, viewPort, rarityCode, true)
end
end
end
function items:PopulateSlots()
for _, slot in pairs(self.slots) do
slot:Populate()
end
end
function items:UpdateJewels()
local spec = self.build.spec
for nodeId, slot in pairs(self.sockets) do
if not spec.allocNodes[nodeId] then
slot.inactive = true
self.controls["socket"..nodeId] = nil
end
end
local socketList = { }
for nodeId, node in pairs(spec.allocNodes) do
if node.type == "socket" then
t_insert(socketList, nodeId)
end
end
table.sort(socketList)
for index, nodeId in pairs(socketList) do
local slot = self.sockets[nodeId]
self.controls["socket"..nodeId] = slot
slot.inactive = false
slot.baseY = (#baseSlots + index - 1) * 20
end
end
function items:GetSocketJewel(nodeId)
return self.sockets[nodeId], self.list[self.sockets[nodeId].selItem]
end
function items:AddDisplayItem()
for _, item in pairs(self.list) do
if item.raw == self.displayItem.raw then
self.displayItem = nil
return
end
end
if not self.displayItem.id then
self.displayItem.id = 1
while self.list[self.displayItem.id] do
self.displayItem.id = self.displayItem.id + 1
end
t_insert(self.orderList, self.displayItem.id)
end
self.list[self.displayItem.id] = self.displayItem
self.displayItem = nil
self:PopulateSlots()
end
function items:IsItemValidForSlot(item, slotName)
if item.type == slotName:gsub(" %d+","") then
return true
elseif slotName == "Weapon 1" or slotName == "Weapon" then
return data.itemBases[item.baseName].weapon ~= nil
elseif slotName == "Weapon 2" then
local weapon1Sel = self.slots["Weapon 1"].selItem
local weapon1Type = weapon1Sel > 0 and data.itemBases[self.list[weapon1Sel].baseName].type or "None"
if weapon1Type == "Bow" then
return item.type == "Quiver"
elseif data.weaponTypeInfo[weapon1Type].oneHand then
return item.type == "Shield" or (data.weaponTypeInfo[item.type] and data.weaponTypeInfo[item.type].oneHand and (weapon1Type == "None" or (weapon1Type == "Wand" and item.type == "Wand") or (weapon1Type ~= "Wand" and item.type ~= "Wand")))
end
end
end
function items:AddItemTooltip(item)
local rarityCode = data.colorCodes[item.rarity]
if item.title then
main:AddTooltipLine(20, rarityCode..item.title)
main:AddTooltipLine(20, rarityCode..item.baseName)
else
main:AddTooltipLine(20, rarityCode..item.name)
end
local base = data.itemBases[item.baseName]
modList = item.modList
if base.weapon then
main:AddTooltipSeperator(10)
main:AddTooltipLine(16, s_format("^x7F7F7F%s", base.type))
main:AddTooltipLine(16, "^x7F7F7FQuality: "..data.colorCodes.MAGIC.."+20%")
local totalDamage = 0
local totalDamageTypes = 0
if modList.weaponX_physicalMin then
totalDamage = totalDamage + (modList.weaponX_physicalMin + modList.weaponX_physicalMax) / 2
local physicalDPS = (modList.weaponX_physicalMin + modList.weaponX_physicalMax) / 2 * modList.weaponX_attackRate
main:AddTooltipLine(16, s_format("^x7F7F7FPhysical Damage: "..data.colorCodes.MAGIC.."%d-%d (%.1f DPS)", modList.weaponX_physicalMin, modList.weaponX_physicalMax, physicalDPS))
totalDamageTypes = totalDamageTypes + 1
end
local elemLine
local elemTotal = 0
for _, var in ipairs({"fire","cold","lightning"}) do
local min = modList["weaponX_"..var.."Min"]
local max = modList["weaponX_"..var.."Max"]
if min and max then
elemLine = elemLine and elemLine.."^x7F7F7F, " or "^x7F7F7FElemental Damage: "
elemLine = elemLine..s_format("%s%d-%d", data.colorCodes[var:upper()], min, max)
elemTotal = elemTotal + (min + max) / 2
end
end
totalDamage = totalDamage + elemTotal
if elemLine then
main:AddTooltipLine(16, elemLine)
main:AddTooltipLine(16, s_format("^x7F7F7FElemental DPS: "..data.colorCodes.MAGIC.."%.1f", elemTotal * modList.weaponX_attackRate))
totalDamageTypes = totalDamageTypes + 1
end
if modList.weaponX_chaosMin then
totalDamage = totalDamage + (modList.weaponX_chaosMin + modList.weaponX_chaosMax) / 2
local chaosDPS = (modList.weaponX_chaosMin + modList.weaponX_chaosMax) / 2 * modList.weaponX_attackRate
main:AddTooltipLine(16, s_format("^x7F7F7FChaos Damage: "..data.colorCodes.CHAOS.."%d-%d "..data.colorCodes.MAGIC.."(%.1f DPS)", modList.weaponX_chaosMin, modList.weaponX_chaosMax, chaosDPS))
totalDamageTypes = totalDamageTypes + 1
end
if totalDamageTypes > 1 then
main:AddTooltipLine(16, s_format("^x7F7F7FTotal DPS: "..data.colorCodes.MAGIC.."%.1f", totalDamage * modList.weaponX_attackRate))
end
main:AddTooltipLine(16, s_format("^x7F7F7FCritical Strike Chance: %s%.2f%%", modList.weaponX_critChanceBase ~= base.weapon.critChanceBase and data.colorCodes.MAGIC or "^7", modList.weaponX_critChanceBase))
main:AddTooltipLine(16, s_format("^x7F7F7FAttacks per Second: %s%.2f", modList.weaponX_attackRate ~= base.weapon.attackRateBase and data.colorCodes.MAGIC or "^7", modList.weaponX_attackRate))
elseif base.armour then
main:AddTooltipSeperator(10)
main:AddTooltipLine(16, "^x7F7F7FQuality: "..data.colorCodes.MAGIC.."+20%")
if base.armour.blockChance and modList.blockChance > 0 then
main:AddTooltipLine(16, s_format("^x7F7F7FChance to Block: %s%d%%", modList.blockChance ~= base.armour.blockChance and data.colorCodes.MAGIC or "^7", modList.blockChance))
end
for _, def in ipairs({{var="armour",label="Armour"},{var="evasion",label="Evasion Rating"},{var="energyShield",label="Energy Shield"}}) do
local itemVal = modList[def.var.."Base"]
if itemVal then
main:AddTooltipLine(16, s_format("^x7F7F7F%s: %s%d", def.label, itemVal ~= base.armour[def.var.."Base"] and data.colorCodes.MAGIC or "^7", itemVal))
end
end
elseif item.radius then
main:AddTooltipSeperator(10)
main:AddTooltipLine(16, "^x7F7F7FRadius: ^7"..data.jewelRadius[item.radius].label)
end
if item.modLines[1] then
main:AddTooltipSeperator(10)
for index, modLine in pairs(item.modLines) do
local line = modLine.range and itemLib.applyRange(modLine.line, modLine.range) or modLine.line
main:AddTooltipLine(16, (modLine.extra and data.colorCodes.NORMAL or data.colorCodes.MAGIC)..line)
if index == item.implicitLines and item.modLines[index + 1] then
main:AddTooltipSeperator(10)
end
end
end
if item.corrupted then
if #item.modLines == item.implicitLines then
main:AddTooltipSeperator(10)
end
main:AddTooltipLine(16, "^1Corrupted")
end
self:UpdateJewels()
for slotName, slot in pairs(self.slots) do
local selItem = self.list[slot.selItem]
if items:IsItemValidForSlot(item, slotName) and not slot.inactive and (item ~= selItem or item.type == "Jewel") then
local calcFunc, calcBase = self.build.calcs:GetItemCalculator()
if calcFunc then
local output = calcFunc(slotName, item ~= selItem and item)
local header = false
for _, statData in ipairs(self.build.displayStats) do
if statData.mod then
local diff = (output[statData.mod] or 0) - (calcBase[statData.mod] or 0)
if diff > 0.001 or diff < -0.001 then
if not header then
main:AddTooltipSeperator(14)
if item == selItem then
main:AddTooltipLine(14, "^7Removing this jewel will give you:")
else
main:AddTooltipLine(14, string.format("^7Equipping this item in %s%s will give you:", slot.label, selItem and " (replacing "..data.colorCodes[selItem.rarity]..selItem.name.."^7)" or ""))
end
header = true
end
main:AddTooltipLine(14, string.format("%s%+"..statData.fmt.." %s", diff > 0 and data.colorCodes.POSITIVE or data.colorCodes.NEGATIVE, diff * (statData.pc and 100 or 1), statData.label))
end
end
end
end
end
end
if launch.devMode and IsKeyDown("ALT") then
main:AddTooltipSeperator(10)
local nameList = { }
for k in pairs(modList) do
t_insert(nameList, k)
end
table.sort(nameList)
for _, name in ipairs(nameList) do
main:AddTooltipLine(16, "^7"..name.." = "..tostring(modList[name]))
end
end
end
return items