Files
PathOfBuilding/Modules/ItemTools.lua
Openarl e7490aa7d7 Release 1.4.14
- Added "Have you been Crit Recently?" option
- Fixed template tooltips in all/shared items lists
- Fixed issue causing mod ranges to be lost when adding to shared item list
2017-05-24 00:43:58 +10:00

685 lines
24 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Path of Building
--
-- Module: Item Tools
-- Various functions for dealing with items.
--
local t_insert = table.insert
local t_remove = table.remove
local m_min = math.min
local m_floor = math.floor
local dmgTypeList = {"Physical", "Lightning", "Cold", "Fire", "Chaos"}
itemLib = { }
-- Apply range value (0 to 1) to a modifier that has a range: (x to x) or (x-x to x-x)
function itemLib.applyRange(line, range)
return line:gsub("%((%d+)%-(%d+) to (%d+)%-(%d+)%)", "(%1-%2) to (%3-%4)")
:gsub("(%+?)%((%-?%d+) to (%d+)%)", "%1(%2-%3)")
:gsub("(%+?)%((%-?%d+)%-(%d+)%)",
function(plus, min, max)
local numVal = m_floor(tonumber(min) + range * (tonumber(max) - tonumber(min)) + 0.5)
if numVal < 0 then
if plus == "+" then
plus = ""
end
end
return plus .. tostring(numVal)
end)
:gsub("%((%d+%.?%d*)%-(%d+%.?%d*)%)",
function(min, max)
local numVal = m_floor((tonumber(min) + range * (tonumber(max) - tonumber(min))) * 10 + 0.5) / 10
return tostring(numVal)
end)
:gsub("%-(%d+%%) increased", function(num) return num.." reduced" end)
end
-- Clean item text by removing or replacing unsupported or redundant characters or sequences
function itemLib.sanitiseItemText(text)
-- Something something unicode support something grumble
return text:gsub("^%s+",""):gsub("%s+$",""):gsub("\r\n","\n"):gsub("%b<>",""):gsub("","-"):gsub("\226\128\147","-"):gsub("\226\136\146","-"):gsub("ö","o"):gsub("\195\182","o"):gsub("[\128-\255]","?")
end
-- Make an item from raw data
function itemLib.makeItemFromRaw(raw)
local newItem = {
raw = itemLib.sanitiseItemText(raw)
}
itemLib.parseItemRaw(newItem)
if newItem.baseName then
return newItem
end
end
-- Parse raw item data and extract item name, base type, quality, and modifiers
function itemLib.parseItemRaw(item)
item.name = "?"
item.rarity = "UNIQUE"
item.quality = nil
item.rawLines = { }
for line in string.gmatch(item.raw .. "\r\n", "([^\r\n]*)\r?\n") do
line = line:gsub("^%s+",""):gsub("%s+$","")
if #line > 0 then
t_insert(item.rawLines, line)
end
end
local mode = "WIKI"
local l = 1
if item.rawLines[l] then
local rarity = item.rawLines[l]:match("^Rarity: (%a+)")
if rarity then
mode = "GAME"
if data.colorCodes[rarity:upper()] then
item.rarity = rarity:upper()
end
if item.rarity == "NORMAL" then
-- Hack for relics
for _, line in ipairs(item.rawLines) do
if line == "Relic Unique" then
item.rarity = "RELIC"
break
end
end
end
l = l + 1
end
end
if item.rawLines[l] then
item.name = item.rawLines[l]
l = l + 1
end
item.namePrefix = ""
item.nameSuffix = ""
if item.rarity == "NORMAL" or item.rarity == "MAGIC" then
for baseName, baseData in pairs(data.itemBases) do
local s, e = item.name:find(baseName, 1, true)
if s then
item.baseName = baseName
item.namePrefix = item.name:sub(1, s - 1)
item.nameSuffix = item.name:sub(e + 1)
item.type = baseData.type
break
end
end
if not item.baseName then
local s, e = item.name:find("Two-Toned Boots", 1, true)
if s then
-- Hack for Two-Toned Boots
item.baseName = "Two-Toned Boots (Armour/Energy Shield)"
item.namePrefix = item.name:sub(1, s - 1)
item.nameSuffix = item.name:sub(e + 1)
item.type = "Boots"
end
end
item.name = item.name:gsub(" %(.+%)","")
elseif item.rawLines[l] and not item.rawLines[l]:match("^%-") then
if item.rawLines[l] == "Two-Toned Boots" then
item.rawLines[l] = "Two-Toned Boots (Armour/Energy Shield)"
end
if data.itemBases[item.rawLines[l]] then
item.baseName = item.rawLines[l]
item.title = item.name
item.name = item.title .. ", " .. item.baseName:gsub(" %(.+%)","")
item.type = data.itemBases[item.baseName].type
l = l + 1
end
end
item.base = data.itemBases[item.baseName]
item.modLines = { }
item.implicitLines = 0
item.buffLines = 0
item.affixes = data.itemMods[item.base and item.base.type]
item.enchantments = data.enchantments[item.base and item.base.type]
item.prefixes = { }
item.suffixes = { }
local flaskBuffLines = { }
if item.base and item.base.flask and item.base.flask.buff then
item.buffLines = #item.base.flask.buff
for _, line in ipairs(item.base.flask.buff) do
flaskBuffLines[line] = true
local modList, extra = modLib.parseMod(line)
t_insert(item.modLines, { line = line, extra = extra, modList = modList or { }, buff = true })
end
end
local gameModeStage = "FINDIMPLICIT"
local gameModeSection = 1
local foundExplicit
while item.rawLines[l] do
local line = item.rawLines[l]
if flaskBuffLines[line] then
flaskBuffLines[line] = nil
elseif line == "--------" then
gameModeSection = gameModeSection + 1
if gameModeStage == "IMPLICIT" then
item.implicitLines = #item.modLines - item.buffLines
gameModeStage = "FINDEXPLICIT"
elseif gameModeStage == "EXPLICIT" then
gameModeStage = "DONE"
end
elseif line == "Corrupted" then
item.corrupted = true
else
local specName, specVal = line:match("^([%a ]+): (%x+)$")
if not specName then
specName, specVal = line:match("^([%a ]+): %+?([%d%-%.]+)")
end
if not specName then
specName, specVal = line:match("^([%a ]+): (.+)$")
end
if specName then
if specName == "Unique ID" then
item.uniqueID = specVal
elseif specName == "Item Level" then
item.itemLevel = tonumber(specVal)
elseif specName == "Quality" then
item.quality = tonumber(specVal)
elseif specName == "Sockets" then
local group = 0
item.sockets = { }
for c in specVal:gmatch(".") do
if c:match("[RGBW]") then
t_insert(item.sockets, { color = c, group = group })
elseif c == " " then
group = group + 1
end
end
elseif specName == "Radius" and item.type == "Jewel" then
for index, data in pairs(data.jewelRadius) do
if specVal == data.label then
item.jewelRadiusIndex = index
break
end
end
elseif specName == "Limited to" and item.type == "Jewel" then
item.limit = tonumber(specVal)
elseif specName == "Variant" then
if not item.variantList then
item.variantList = { }
end
t_insert(item.variantList, specVal)
elseif specName == "Selected Variant" then
item.variant = tonumber(specVal)
elseif specName == "League" then
item.league = specVal
elseif specName == "Crafted" then
item.crafted = true
elseif specName == "Prefix" then
t_insert(item.prefixes, specVal)
elseif specName == "Suffix" then
t_insert(item.suffixes, specVal)
elseif specName == "Implicits" then
item.implicitLines = tonumber(specVal)
gameModeStage = "EXPLICIT"
elseif specName == "Unreleased" then
item.unreleased = (specVal == "true")
elseif specName == "Evasion Rating" then
if item.baseName == "Two-Toned Boots (Armour/Energy Shield)" then
-- Another hack for Two-Toned Boots
item.baseName = "Two-Toned Boots (Armour/Evasion)"
item.base = data.itemBases[item.baseName]
end
elseif specName == "Energy Shield" then
if item.baseName == "Two-Toned Boots (Armour/Evasion)" then
-- Yet another hack for Two-Toned Boots
item.baseName = "Two-Toned Boots (Evasion/Energy Shield)"
item.base = data.itemBases[item.baseName]
end
end
end
if line == "Prefixes:" then
foundExplicit = true
gameModeStage = "EXPLICIT"
end
if not specName or foundExplicit then
local varSpec = line:match("{variant:([%d,]+)}")
local variantList
if varSpec then
variantList = { }
for varId in varSpec:gmatch("%d+") do
variantList[tonumber(varId)] = true
end
end
local rangeSpec = line:match("{range:([%d.]+)}")
local crafted = line:match("{crafted}")
line = line:gsub("%b{}", "")
local rangedLine
if line:match("%(%d+%-%d+ to %d+%-%d+%)") or line:match("%(%-?[%d%.]+ to %-?[%d%.]+%)") or line:match("%(%-?[%d%.]+%-[%d%.]+%)") then
rangedLine = itemLib.applyRange(line, 1)
end
local modList, extra = modLib.parseMod(rangedLine or line)
if (not modList or extra) and item.rawLines[l+1] then
-- Try to combine it with the next line
modList, extra = modLib.parseMod(line.." "..item.rawLines[l+1])
if modList and not extra then
line = line.."\n"..item.rawLines[l+1]
l = l + 1
else
modList, extra = modLib.parseMod(rangedLine or line)
end
end
if modList then
t_insert(item.modLines, { line = line, extra = extra, modList = modList, variantList = variantList, crafted = crafted, range = rangedLine and (tonumber(rangeSpec) or 0.5) })
if mode == "GAME" then
if gameModeStage == "FINDIMPLICIT" then
gameModeStage = "IMPLICIT"
elseif gameModeStage == "FINDEXPLICIT" then
foundExplicit = true
gameModeStage = "EXPLICIT"
elseif gameModeStage == "EXPLICIT" then
foundExplicit = true
end
else
foundExplicit = true
end
elseif mode == "GAME" then
if gameModeStage == "IMPLICIT" or gameModeStage == "EXPLICIT" then
t_insert(item.modLines, { line = line, extra = line, modList = { }, variantList = variantList, crafted = crafted })
elseif gameModeStage == "FINDEXPLICIT" then
gameModeStage = "DONE"
end
elseif foundExplicit then
t_insert(item.modLines, { line = line, extra = line, modList = { }, variantList = variantList, crafted = crafted })
end
end
end
l = l + 1
end
if item.base and item.base.implicit then
if item.implicitLines == 0 then
item.implicitLines = 1 + #item.base.implicit:gsub("[^\n]","")
end
elseif mode == "GAME" and not foundExplicit then
item.implicitLines = 0
end
item.affixLimit = 0
if item.crafted and item.affixes then
if item.rarity == "MAGIC" then
item.affixLimit = 2
elseif item.rarity == "RARE" then
item.affixLimit = (item.base.type == "Jewel" and 4 or 6)
end
end
if item.variantList then
item.variant = m_min(#item.variantList, item.variant or #item.variantList)
end
if not item.quality then
itemLib.normaliseQuality(item)
end
itemLib.buildItemModList(item)
end
function itemLib.normaliseQuality(item)
if item.base and (item.base.armour or item.base.weapon or item.base.flask) then
if not item.quality then
item.quality = item.corrupted and 0 or 20
elseif not item.uniqueID and not item.corrupted then
item.quality = 20
end
end
end
-- Create raw item data for given item
function itemLib.createItemRaw(item)
local rawLines = { }
t_insert(rawLines, "Rarity: "..item.rarity)
if item.title then
t_insert(rawLines, item.title)
t_insert(rawLines, item.baseName)
else
t_insert(rawLines, (item.namePrefix or "")..item.baseName..(item.nameSuffix or ""))
end
if item.uniqueID then
t_insert(rawLines, "Unique ID: "..item.uniqueID)
end
if item.league then
t_insert(rawLines, "League: "..item.league)
end
if item.unreleased then
t_insert(rawLines, "Unreleased: true")
end
if item.crafted then
t_insert(rawLines, "Crafted: true")
for _, name in ipairs(item.prefixes or { }) do
t_insert(rawLines, "Prefix: "..name)
end
for _, name in ipairs(item.suffixes or { }) do
t_insert(rawLines, "Suffix: "..name)
end
end
if item.itemLevel then
t_insert(rawLines, "Item Level: "..item.itemLevel)
end
if item.variantList then
for _, variantName in ipairs(item.variantList) do
t_insert(rawLines, "Variant: "..variantName)
end
t_insert(rawLines, "Selected Variant: "..item.variant)
end
if item.quality then
t_insert(rawLines, "Quality: "..item.quality)
end
if item.sockets then
local line = "Sockets: "
for i, socket in pairs(item.sockets) do
line = line .. socket.color
if item.sockets[i+1] then
line = line .. (socket.group == item.sockets[i+1].group and "-" or " ")
end
end
t_insert(rawLines, line)
end
if item.jewelRadiusIndex then
t_insert(rawLines, "Radius: "..data.jewelRadius[item.jewelRadiusIndex].label)
end
if item.limit then
t_insert(rawLines, "Limited to: "..item.limit)
end
t_insert(rawLines, "Implicits: "..item.implicitLines)
for _, modLine in ipairs(item.modLines) do
if not modLine.buff then
local line = modLine.line
if modLine.range then
line = "{range:"..round(modLine.range,3).."}" .. line
end
if modLine.crafted then
line = "{crafted}" .. line
end
if modLine.variantList then
local varSpec
for varId in pairs(modLine.variantList) do
varSpec = (varSpec and varSpec.."," or "") .. varId
end
line = "{variant:"..varSpec.."}"..line
end
t_insert(rawLines, line)
end
end
if item.corrupted then
t_insert(rawLines, "Corrupted")
end
return table.concat(rawLines, "\n")
end
-- Rebuild explicit modifiers using the item's affixes
function itemLib.craftItem(item)
local ranges = { }
for l = item.buffLines + item.implicitLines + 1, #item.modLines do
ranges[item.modLines[l].line] = item.modLines[l].range
item.modLines[l] = nil
end
item.namePrefix = ""
item.nameSuffix = ""
for _, list in ipairs({item.prefixes,item.suffixes}) do
for i = 1, item.affixLimit/2 do
local name = list[i]
if not name then
list[i] = "None"
end
local mod = item.affixes[name]
if mod then
if mod.type == "Prefix" then
item.namePrefix = name .. " "
elseif mod.type == "Suffix" then
item.nameSuffix = " " .. name
end
for _, line in ipairs(mod) do
t_insert(item.modLines, { line = line, range = ranges[line] })
end
end
end
end
if item.rarity == "MAGIC" then
item.name = newName
end
item.raw = itemLib.createItemRaw(item)
itemLib.parseItemRaw(item)
end
-- Return the name of the slot this item is equipped in
function itemLib.getPrimarySlotForItem(item)
if item.base.weapon then
return "Weapon 1"
elseif item.type == "Quiver" or item.type == "Shield" then
return "Weapon 2"
elseif item.type == "Ring" then
return "Ring 1"
elseif item.type == "Flask" then
return "Flask 1"
else
return item.type
end
end
-- Add up local modifiers, and removes them from the modifier list
-- To be considered local, a modifier must be an exact flag match, and cannot have any tags (e.g conditions, multipliers)
-- Only the InSlot tag is allowed (for Adds x to x X Damage in X Hand modifiers)
local function sumLocal(modList, name, type, flags)
local result = 0
local i = 1
while modList[i] do
local mod = modList[i]
if mod.name == name and mod.type == type and mod.flags == flags and mod.keywordFlags == 0 and (not mod.tagList[1] or mod.tagList[1].type == "InSlot") then
result = result + mod.value
t_remove(modList, i)
else
i = i + 1
end
end
return result
end
-- Build list of modifiers for an item in a given slot number (1 or 2) while applying local modifers and adding quality
function itemLib.buildItemModListForSlotNum(item, baseList, slotNum)
local slotName = itemLib.getPrimarySlotForItem(item)
if slotNum == 2 then
slotName = slotName:gsub("1", "2")
end
local modList = common.New("ModList")
for _, baseMod in ipairs(baseList) do
local mod = copyTable(baseMod)
local add = true
for _, tag in pairs(mod.tagList) do
if tag.type == "SlotNumber" or tag.type == "InSlot" then
if tag.num ~= slotNum then
add = false
break
end
elseif tag.type == "SocketedIn" then
tag.slotName = slotName
elseif tag.type == "Condition" and tag.var == "XHandAttack" then
tag.var = (slotNum == 1) and "MainHandAttack" or "OffHandAttack"
end
end
if add then
mod.sourceSlot = slotName
modList:AddMod(mod)
end
end
if item.base.weapon then
local weaponData = { }
item.weaponData[slotNum] = weaponData
weaponData.type = item.base.type
weaponData.name = item.name
weaponData.AttackSpeedInc = sumLocal(modList, "Speed", "INC", ModFlag.Attack)
weaponData.attackRate = round(item.base.weapon.attackRateBase * (1 + weaponData.AttackSpeedInc / 100), 2)
for _, dmgType in pairs(dmgTypeList) do
local min = (item.base.weapon[dmgType.."Min"] or 0) + sumLocal(modList, dmgType.."Min", "BASE", 0)
local max = (item.base.weapon[dmgType.."Max"] or 0) + sumLocal(modList, dmgType.."Max", "BASE", 0)
if dmgType == "Physical" then
local physInc = sumLocal(modList, "PhysicalDamage", "INC", 0)
min = round(min * (1 + (physInc + item.quality) / 100))
max = round(max * (1 + (physInc + item.quality) / 100))
end
if min > 0 and max > 0 then
weaponData[dmgType.."Min"] = min
weaponData[dmgType.."Max"] = max
local dps = (min + max) / 2 * weaponData.attackRate
weaponData[dmgType.."DPS"] = dps
if dmgType ~= "Physical" and dmgType ~= "Chaos" then
weaponData.ElementalDPS = (weaponData.ElementalDPS or 0) + dps
end
end
end
weaponData.critChance = round(item.base.weapon.critChanceBase * (1 + sumLocal(modList, "CritChance", "INC", 0) / 100), 2)
for _, value in ipairs(modList:Sum("LIST", nil, "WeaponData")) do
weaponData[value.key] = value.value
end
weaponData.AccuracyInc = sumLocal(modList, "Accuracy", "INC", 0)
if weaponData.AccuracyInc > 0 then
modList:NewMod("Accuracy", "MORE", weaponData.AccuracyInc, item.modSource, { type = "Condition", var = (slotNum == 1) and "MainHandAttack" or "OffHandAttack" })
end
if data.weaponTypeInfo[item.base.type].range then
weaponData.range = data.weaponTypeInfo[item.base.type].range + sumLocal(modList, "WeaponRange", "BASE", 0)
end
for _, mod in ipairs(modList) do
-- Convert accuracy, L/MGoH and PAD Leech modifiers to local
if (
(mod.name == "Accuracy" and mod.flags == 0) or
((mod.name == "LifeOnHit" or mod.name == "ManaOnHit") and mod.flags == ModFlag.Attack) or
((mod.name == "PhysicalDamageLifeLeech" or mod.name == "PhysicalDamageManaLeech") and mod.flags == ModFlag.Attack)
) and mod.keywordFlags == 0 and not mod.tagList[1] then
mod.tagList[1] = { type = "Condition", var = (slotNum == 1) and "MainHandAttack" or "OffHandAttack" }
end
end
weaponData.TotalDPS = 0
for _, dmgType in pairs(dmgTypeList) do
weaponData.TotalDPS = weaponData.TotalDPS + (weaponData[dmgType.."DPS"] or 0)
end
elseif item.base.armour then
local armourData = item.armourData
local armourBase = sumLocal(modList, "Armour", "BASE", 0) + (item.base.armour.armourBase or 0)
local evasionBase = sumLocal(modList, "Evasion", "BASE", 0) + (item.base.armour.evasionBase or 0)
local energyShieldBase = sumLocal(modList, "EnergyShield", "BASE", 0) + (item.base.armour.energyShieldBase or 0)
local armourInc = sumLocal(modList, "Armour", "INC", 0)
local armourEvasionInc = sumLocal(modList, "ArmourAndEvasion", "INC", 0)
local evasionInc = sumLocal(modList, "Evasion", "INC", 0)
local evasionEnergyShieldInc = sumLocal(modList, "EvasionAndEnergyShield", "INC", 0)
local energyShieldInc = sumLocal(modList, "EnergyShield", "INC", 0)
local armourEnergyShieldInc = sumLocal(modList, "ArmourAndEnergyShield", "INC", 0)
local defencesInc = sumLocal(modList, "Defences", "INC", 0)
armourData.Armour = round(armourBase * (1 + (armourInc + armourEvasionInc + armourEnergyShieldInc + defencesInc + item.quality) / 100))
armourData.Evasion = round(evasionBase * (1 + (evasionInc + armourEvasionInc + evasionEnergyShieldInc + defencesInc + item.quality) / 100))
armourData.EnergyShield = round(energyShieldBase * (1 + (energyShieldInc + armourEnergyShieldInc + evasionEnergyShieldInc + defencesInc + item.quality) / 100))
if item.base.armour.blockChance then
armourData.BlockChance = item.base.armour.blockChance + sumLocal(modList, "BlockChance", "BASE", 0)
end
if item.base.armour.movementPenalty then
modList:NewMod("MovementSpeed", "INC", -item.base.armour.movementPenalty, item.modSource, { type = "Condition", var = "IgnoreMovementPenalties", neg = true })
end
for _, value in ipairs(modList:Sum("LIST", nil, "ArmourData")) do
armourData[value.key] = value.value
end
elseif item.base.flask then
local flaskData = item.flaskData
local durationInc = sumLocal(modList, "Duration", "INC", 0)
if item.base.flask.life or item.base.flask.mana then
-- Recovery flask
flaskData.instantPerc = sumLocal(modList, "FlaskInstantRecovery", "BASE", 0)
local recoveryMod = 1 + sumLocal(modList, "FlaskRecovery", "INC", 0) / 100
local rateMod = 1 + sumLocal(modList, "FlaskRecoveryRate", "INC", 0) / 100
flaskData.duration = item.base.flask.duration * (1 + durationInc / 100) / rateMod
if item.base.flask.life then
flaskData.lifeBase = item.base.flask.life * (1 + item.quality / 100) * recoveryMod
flaskData.lifeInstant = flaskData.lifeBase * flaskData.instantPerc / 100
flaskData.lifeGradual = flaskData.lifeBase * (1 - flaskData.instantPerc / 100) * (1 + durationInc / 100)
flaskData.lifeTotal = flaskData.lifeInstant + flaskData.lifeGradual
end
if item.base.flask.mana then
flaskData.manaBase = item.base.flask.mana * (1 + item.quality / 100) * recoveryMod
flaskData.manaInstant = flaskData.manaBase * flaskData.instantPerc / 100
flaskData.manaGradual = flaskData.manaBase * (1 - flaskData.instantPerc / 100) * (1 + durationInc / 100)
flaskData.manaTotal = flaskData.manaInstant + flaskData.manaGradual
end
else
-- Utility flask
flaskData.duration = item.base.flask.duration * (1 + (durationInc + item.quality) / 100)
end
flaskData.chargesMax = item.base.flask.chargesMax + sumLocal(modList, "FlaskCharges", "BASE", 0)
flaskData.chargesUsed = m_floor(item.base.flask.chargesUsed * (1 + sumLocal(modList, "FlaskChargesUsed", "INC", 0) / 100))
flaskData.gainMod = 1 + sumLocal(modList, "FlaskChargeRecovery", "INC", 0) / 100
flaskData.effectInc = sumLocal(modList, "FlaskEffect", "INC", 0)
for _, value in ipairs(modList:Sum("LIST", nil, "FlaskData")) do
flaskData[value.key] = value.value
end
elseif item.type == "Jewel" then
local jewelData = item.jewelData
for _, func in ipairs(modList:Sum("LIST", nil, "JewelFunc")) do
jewelData.funcList = jewelData.funcList or { }
t_insert(jewelData.funcList, func)
end
for _, value in ipairs(modList:Sum("LIST", nil, "JewelData")) do
jewelData[value.key] = value.value
end
end
return { unpack(modList) }
end
-- Build lists of modifiers for each slot an item can occupy
function itemLib.buildItemModList(item)
if not item.base then
return
end
local baseList = common.New("ModList")
if item.base.weapon then
item.weaponData = { }
elseif item.base.armour then
item.armourData = { }
elseif item.base.flask then
item.flaskData = { }
item.buffModList = { }
elseif item.type == "Jewel" then
item.jewelData = { }
end
item.baseModList = baseList
item.rangeLineList = { }
item.modSource = "Item:"..(item.id or -1)..":"..item.name
for _, modLine in ipairs(item.modLines) do
if not modLine.extra and (not modLine.variantList or modLine.variantList[item.variant]) then
if modLine.range then
local line = itemLib.applyRange(modLine.line, modLine.range)
local list, extra = modLib.parseMod(line)
if list and not extra then
modLine.modList = list
t_insert(item.rangeLineList, modLine)
end
end
for _, mod in ipairs(modLine.modList) do
mod.source = item.modSource
if type(mod.value) == "table" and mod.value.mod then
mod.value.mod.source = mod.source
end
if modLine.buff then
t_insert(item.buffModList, mod)
else
baseList:AddMod(mod)
end
end
end
end
item.grantedSkills = { }
for _, skill in ipairs(baseList:Sum("LIST", nil, "ExtraSkill")) do
if skill.name ~= "Unknown" then
t_insert(item.grantedSkills, {
name = skill.name,
level = skill.level,
noSupports = skill.noSupports,
source = item.modSource,
})
end
end
if item.name == "Tabula Rasa, Simple Robe" or item.name == "Skin of the Loyal, Simple Robe" or item.name == "Skin of the Lords, Simple Robe" then
-- Hack to remove the energy shield
baseList:NewMod("ArmourData", "LIST", { key = "EnergyShield" })
end
if item.base.weapon or item.type == "Ring" then
item.slotModList = { }
for i = 1, 2 do
item.slotModList[i] = itemLib.buildItemModListForSlotNum(item, baseList, i)
end
else
item.modList = itemLib.buildItemModListForSlotNum(item, baseList)
end
end