Module:Items/ComparisonTables: Difference between revisions

From Melvor Idle
(_getEquipmentTable: Allow word wrap in modifiers column)
(Added ability to sort category tables by name and also added ability to get skillcape and non-skillcape tables)
Line 11: Line 11:


local styleOverrides = {
local styleOverrides = {
  Melee = {'Slayer Helmet (Basic)', 'Slayer Platebody (Basic)', 'Paladin Gloves', 'Desert Wrappings', 'Almighty Lute', 'Candy Cane', 'Bob's Rake'},
Melee = {'Slayer Helmet (Basic)', 'Slayer Platebody (Basic)', 'Paladin Gloves', 'Desert Wrappings', 'Almighty Lute', 'Candy Cane', 'Bob's Rake'},
  Ranged = {'Slayer Cowl (Basic)', 'Slayer Leather Body (Basic)', 'Ice Arrows'},
Ranged = {'Slayer Cowl (Basic)', 'Slayer Leather Body (Basic)', 'Ice Arrows'},
  Magic = {'Slayer Wizard Hat (Basic)', 'Slayer Wizard Robes (Basic)', 'Enchanted Shield', 'Elementalist Gloves'},
Magic = {'Slayer Wizard Hat (Basic)', 'Slayer Wizard Robes (Basic)', 'Enchanted Shield', 'Elementalist Gloves'},
  None = {},
None = {},
}
}


function p._getEquipmentTable(itemList, includeModifiers, includeDescription)
function p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
  if includeModifiers == nil then includeModifiers = false end
if includeModifiers == nil then includeModifiers = false end
if sortByName == nil then sortByName = false end


  --Getting some lists set up here that will be used later
--Getting some lists set up here that will be used later
  --First, the list of columns used by both weapons & armour
--First, the list of columns used by both weapons & armour
  local statColumns = {'stabAttackBonus', 'slashAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'meleeStrengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'meleeDefenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction', 'attackLevelRequired', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
local statColumns = {'stabAttackBonus', 'slashAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'meleeStrengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'meleeDefenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction', 'attackLevelRequired', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}


  if(Shared.tableCount(itemList) == 0) then
if(Shared.tableCount(itemList) == 0) then
    return 'ERROR: you must select at least one item to get stats for[[Category:Pages with script errors]]'
return 'ERROR: you must select at least one item to get stats for[[Category:Pages with script errors]]'
  end
end


  local isWeaponType = ((itemList[1].validSlots ~= nil and Shared.contains(itemList[1].validSlots, 'Weapon'))
local isWeaponType = ((itemList[1].validSlots ~= nil and Shared.contains(itemList[1].validSlots, 'Weapon'))
  or (itemList[1].occupiesSlots ~= nil and Shared.contains(itemList[1].occupiesSlots, 'Weapon'))) and Shared.contains(weaponTypes, itemList[1].type)
or (itemList[1].occupiesSlots ~= nil and Shared.contains(itemList[1].occupiesSlots, 'Weapon'))) and Shared.contains(weaponTypes, itemList[1].type)


  --Now that we have a preliminary list, let's figure out which columns are irrelevant (IE are zero for all items in the selection)
--Now that we have a preliminary list, let's figure out which columns are irrelevant (IE are zero for all items in the selection)
  local ignoreColumns = Shared.clone(statColumns)
local ignoreColumns = Shared.clone(statColumns)
  for i, item in pairs(itemList) do
for i, item in pairs(itemList) do
    local ndx = 1
local ndx = 1
    while Shared.tableCount(ignoreColumns) >= ndx do
while Shared.tableCount(ignoreColumns) >= ndx do
      if Items._getItemStat(item, ignoreColumns[ndx], true) ~= 0 then
if Items._getItemStat(item, ignoreColumns[ndx], true) ~= 0 then
        table.remove(ignoreColumns, ndx)
table.remove(ignoreColumns, ndx)
      else
else
        ndx = ndx + 1
ndx = ndx + 1
      end
end
    end
end
  end
end


  --Now to remove the ignored columns (and also we need to track groups like defence bonuses to see how many remain)
--Now to remove the ignored columns (and also we need to track groups like defence bonuses to see how many remain)
  local attBonusCols = 5
local attBonusCols = 5
  local strBonusCols = 2
local strBonusCols = 2
  local defBonusCols = 3
local defBonusCols = 3
  local lvlReqCols = 4
local lvlReqCols = 4
  local ndx = 1
local ndx = 1
  while Shared.tableCount(statColumns) >= ndx do
while Shared.tableCount(statColumns) >= ndx do
    local colName = statColumns[ndx]
local colName = statColumns[ndx]
    if Shared.contains(ignoreColumns, colName) then
if Shared.contains(ignoreColumns, colName) then
      if Shared.contains(colName, 'AttackBonus') then attBonusCols = attBonusCols - 1 end
if Shared.contains(colName, 'AttackBonus') then attBonusCols = attBonusCols - 1 end
      if Shared.contains(colName, 'trengthBonus') then strBonusCols = strBonusCols - 1 end
if Shared.contains(colName, 'trengthBonus') then strBonusCols = strBonusCols - 1 end
      if Shared.contains(colName, 'efenceBonus') then defBonusCols = defBonusCols - 1 end
if Shared.contains(colName, 'efenceBonus') then defBonusCols = defBonusCols - 1 end
      if Shared.contains(colName, 'LevelRequired') then lvlReqCols = lvlReqCols - 1 end
if Shared.contains(colName, 'LevelRequired') then lvlReqCols = lvlReqCols - 1 end
      table.remove(statColumns, ndx)
table.remove(statColumns, ndx)
    else
else
      ndx = ndx + 1
ndx = ndx + 1
    end
end
  end
end


  --Alright, let's start the table by building the shared header
--Alright, let's start the table by building the shared header
  local resultPart = {}
local resultPart = {}
  table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
  if isWeaponType then
if isWeaponType then
    --Weapons have extra columns here for Attack Speed and "Two Handed?"
--Weapons have extra columns here for Attack Speed and "Two Handed?"
    table.insert(resultPart, '\r\n!colspan="4"|')
table.insert(resultPart, '\r\n!colspan="4"|')
  else
else
    table.insert(resultPart, '\r\n!colspan="2"|')
table.insert(resultPart, '\r\n!colspan="2"|')
  end
end
  if attBonusCols > 0 then
if attBonusCols > 0 then
    table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus')
table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus')
  end
end
  if strBonusCols > 0 then
if strBonusCols > 0 then
    table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Str. Bonus')
table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Str. Bonus')
  end
end
  if Shared.contains(statColumns, 'magicDamageBonus') then
if Shared.contains(statColumns, 'magicDamageBonus') then
    table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Dmg Bonus')
table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Dmg Bonus')
  end
end
  if defBonusCols > 0 then
if defBonusCols > 0 then
    table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus')
table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus')
  end
end
  if Shared.contains(statColumns, 'damageReduction') then
if Shared.contains(statColumns, 'damageReduction') then
    table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|DR')
table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|DR')
  end
end
  if lvlReqCols > 0 then
if lvlReqCols > 0 then
    table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Lvl Req')
table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Lvl Req')
  end
end
  if includeModifiers and includeDescription then
if includeModifiers and includeDescription then
    table.insert(resultPart, '\r\n!colspan="2"|')
table.insert(resultPart, '\r\n!colspan="2"|')
  elseif includeModifiers or includeDescription then
elseif includeModifiers or includeDescription then
    table.insert(resultPart, '\r\n!colspan="1"|')
table.insert(resultPart, '\r\n!colspan="1"|')
  end
end
  --One header row down, one to go
--One header row down, one to go
  table.insert(resultPart, '\r\n|-class="headerRow-1"')
table.insert(resultPart, '\r\n|-class="headerRow-1"')
  table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Item')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Item')
  table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Name')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Name')
  --Weapons have Attack Speed here
--Weapons have Attack Speed here
  if isWeaponType then
if isWeaponType then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed')
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?')
  end
end
  --Attack bonuses
--Attack bonuses
  if Shared.contains(statColumns, 'slashAttackBonus') then
if Shared.contains(statColumns, 'slashAttackBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'stabAttackBonus') then
if Shared.contains(statColumns, 'stabAttackBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'blockAttackBonus') then
if Shared.contains(statColumns, 'blockAttackBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'rangedAttackBonus') then
if Shared.contains(statColumns, 'rangedAttackBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'magicAttackBonus') then
if Shared.contains(statColumns, 'magicAttackBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
  end
end
  --Strength bonuses
--Strength bonuses
  if Shared.contains(statColumns, 'meleeStrengthBonus') then
if Shared.contains(statColumns, 'meleeStrengthBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'rangedStrengthBonus') then
if Shared.contains(statColumns, 'rangedStrengthBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'magicDamageBonus') then
if Shared.contains(statColumns, 'magicDamageBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
  end
end
  --Defence bonuses
--Defence bonuses
  if Shared.contains(statColumns, 'meleeDefenceBonus') then
if Shared.contains(statColumns, 'meleeDefenceBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'rangedDefenceBonus') then
if Shared.contains(statColumns, 'rangedDefenceBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'magicDefenceBonus') then
if Shared.contains(statColumns, 'magicDefenceBonus') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'damageReduction') then
if Shared.contains(statColumns, 'damageReduction') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
  end
end
  --Level requirements
--Level requirements
  if Shared.contains(statColumns, 'attackLevelRequired') then
if Shared.contains(statColumns, 'attackLevelRequired') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'defenceLevelRequired') then
if Shared.contains(statColumns, 'defenceLevelRequired') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'rangedLevelRequired') then
if Shared.contains(statColumns, 'rangedLevelRequired') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
  end
end
  if Shared.contains(statColumns, 'magicLevelRequired') then
if Shared.contains(statColumns, 'magicLevelRequired') then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
  end
end
  --If includeModifiers is set to 'true', add the Modifiers column
--If includeModifiers is set to 'true', add the Modifiers column
  if includeModifiers then
if includeModifiers then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Modifiers')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Modifiers')
  end
end
  --If includeDescription is set to 'true', add the Description column
--If includeDescription is set to 'true', add the Description column
  if includeDescription then
if includeDescription then
    table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Description')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Description')
  end
end


  table.sort(itemList, function(a, b) return a.id < b.id end)
if sortByName then
  for i, item in pairs(itemList) do
table.sort(itemList, function(a, b) return a.name < b.name end)
    if isWeaponType then
else
      --Building rows for weapons
table.sort(itemList, function(a, b) return a.id < b.id end)
      local atkSpeed = Items._getItemStat(item, 'attackSpeed', true)
end
      table.insert(resultPart, '\r\n|-')
for i, item in pairs(itemList) do
      table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
if isWeaponType then
      table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
--Building rows for weapons
      table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.round(atkSpeed / 1000, 3, 1) .. 's')
local atkSpeed = Items._getItemStat(item, 'attackSpeed', true)
      --That's the first list out of the way, now for 2-Handed
table.insert(resultPart, '\r\n|-')
      table.insert(resultPart, '\r\n| style ="text-align: right;"|')
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
      table.insert(resultPart, Items._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
      for j, statName in pairs(statColumns) do
table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.round(atkSpeed / 1000, 3, 1) .. 's')
        local statValue = Items._getItemStat(item, statName, true)
--That's the first list out of the way, now for 2-Handed
        table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
table.insert(resultPart, '\r\n| style ="text-align: right;"|')
        if string.find(statName, '^(.+)LevelRequired$') == nil then
table.insert(resultPart, Items._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')
          if statValue > 0 then
for j, statName in pairs(statColumns) do
            table.insert(resultPart, 'background-color:lightgreen;')
local statValue = Items._getItemStat(item, statName, true)
          elseif statValue < 0 then
table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
            table.insert(resultPart, 'background-color:lightpink;')
if string.find(statName, '^(.+)LevelRequired$') == nil then
          end
if statValue > 0 then
        end
table.insert(resultPart, 'background-color:lightgreen;')
        table.insert(resultPart, '"|'..Shared.formatnum(statValue))
elseif statValue < 0 then
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
table.insert(resultPart, 'background-color:lightpink;')
      end
end
      --If requested, add the item Modifiers
end
      if includeModifiers then
table.insert(resultPart, '"|'..Shared.formatnum(statValue))
        table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
        table.insert(resultPart, Constants.getModifiersText(item.modifiers, true))
end
      end
--If requested, add the item Modifiers
      --If requested, add description
if includeModifiers then
      if includeDescription then
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
        table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
table.insert(resultPart, Constants.getModifiersText(item.modifiers, true))
        table.insert(resultPart, item.description ~= nil and item.description or '')
end
      end
--If requested, add description
    else
if includeDescription then
      --Building rows for armour
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
      table.insert(resultPart, '\r\n|-')
table.insert(resultPart, item.description ~= nil and item.description or '')
      table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
end
      table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
else
      for j, statName in pairs(statColumns) do
--Building rows for armour
        local statValue = Items._getItemStat(item, statName, true)
table.insert(resultPart, '\r\n|-')
        table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
        if statValue > 0 then
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
          table.insert(resultPart, 'background-color:lightgreen;')
for j, statName in pairs(statColumns) do
        elseif statValue < 0 then
local statValue = Items._getItemStat(item, statName, true)
          table.insert(resultPart, 'background-color:lightpink;')
table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
        end
if statValue > 0 then
        table.insert(resultPart, '"|'..Shared.formatnum(statValue))
table.insert(resultPart, 'background-color:lightgreen;')
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
elseif statValue < 0 then
      end
table.insert(resultPart, 'background-color:lightpink;')
      --If requested, add the item Modifiers
end
      if includeModifiers then
table.insert(resultPart, '"|'..Shared.formatnum(statValue))
        table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
        table.insert(resultPart, Constants.getModifiersText(item.modifiers, true))
end
      end
--If requested, add the item Modifiers
      --If requested, add description
if includeModifiers then
      if includeDescription then
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
        table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
table.insert(resultPart, Constants.getModifiersText(item.modifiers, true))
        table.insert(resultPart, item.description ~= nil and item.description or '')
end
      end
--If requested, add description
    end
if includeDescription then
  end
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
table.insert(resultPart, item.description ~= nil and item.description or '')
end
end
end


  table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')


  return table.concat(resultPart)
return table.concat(resultPart)
end
end


function p.getEquipmentTable(frame)
function p.getEquipmentTable(frame)
  local args = frame.args ~= nil and frame.args or frame
local args = frame.args ~= nil and frame.args or frame
  local type = args.type
local type = args.type
  local tier = args.tier
local tier = args.tier
  local slot = args.slot
local slot = args.slot
  local ammoTypeStr = args.ammoType
local ammoTypeStr = args.ammoType
  local category = args.category ~= nil and args.category or 'Combat'
local category = args.category ~= nil and args.category or 'Combat'


  --Find out what Ammo Type we're working with
--Find out what Ammo Type we're working with
  local ammoType = nil
local ammoType = nil
  if ammoTypeStr ~= nil then
if ammoTypeStr ~= nil then
    if ammoTypeStr == "Arrows" then
if ammoTypeStr == "Arrows" then
      ammoType = 0
ammoType = 0
    elseif ammoTypeStr == 'Bolts' then
elseif ammoTypeStr == 'Bolts' then
      ammoType = 1
ammoType = 1
    elseif ammoTypeStr == 'Javelins' then
elseif ammoTypeStr == 'Javelins' then
      ammoType = 2
ammoType = 2
    elseif ammoTypeStr == 'Throwing Knives' then
elseif ammoTypeStr == 'Throwing Knives' then
      ammoType = 3
ammoType = 3
    end
end
  end
end


  local isWeaponType = Shared.contains(weaponTypes, type)
local isWeaponType = Shared.contains(weaponTypes, type)


  --Now we need to figure out which items are in this list
--Now we need to figure out which items are in this list
  local itemList = {}
local itemList = {}
  for i, itemBase in pairs(ItemData.Items) do
for i, itemBase in pairs(ItemData.Items) do
    local item = Shared.clone(itemBase)
local item = Shared.clone(itemBase)
    item.id = i - 1
item.id = i - 1
    local listItem = false
local listItem = false
    if isWeaponType then
if isWeaponType then
    listItem = item.type == type and item.category == category
listItem = item.type == type and item.category == category
      if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
    else
else
      --Now for handling armour
--Now for handling armour
      if type == "Armour" or type == "Melee" then
if type == "Armour" or type == "Melee" then
        listItem = Items._getItemStat(item, 'defenceLevelRequired') ~= nil or (item.category == 'Combat' and item.type == 'Armour')
listItem = Items._getItemStat(item, 'defenceLevelRequired') ~= nil or (item.category == 'Combat' and item.type == 'Armour')
      elseif type == "Ranged Armour" or type == "Ranged" then
elseif type == "Ranged Armour" or type == "Ranged" then
        listItem = Items._getItemStat(item, 'rangedLevelRequired') ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
listItem = Items._getItemStat(item, 'rangedLevelRequired') ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
      elseif type == "Magic Armour" or type == "Magic" then
elseif type == "Magic Armour" or type == "Magic" then
        listItem = Items._getItemStat(item, 'magicLevelRequired') or (item.category == 'Combat' and item.type == 'Magic Armour')
listItem = Items._getItemStat(item, 'magicLevelRequired') or (item.category == 'Combat' and item.type == 'Magic Armour')
      else
else
        listItem = item.type == type and item.category ~= 'Combat'
listItem = item.type == type and item.category ~= 'Combat'
      end
end
      if ammoType ~= nil then listItem = listItem and item.ammoType == ammoType end
if ammoType ~= nil then listItem = listItem and item.ammoType == ammoType end
      if slot ~= nil then listItem = listItem and Shared.contains(item.validSlots, slot) end
if slot ~= nil then listItem = listItem and Shared.contains(item.validSlots, slot) end
    end
end
    if listItem then
if listItem then
      table.insert(itemList, item)
table.insert(itemList, item)
    end
end
  end
end


  local result = p._getEquipmentTable(itemList).."[[Category:getEquipmentTable]]"
local result = p._getEquipmentTable(itemList).."[[Category:getEquipmentTable]]"


  return result
return result
end
end


function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription)
function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
  if type(slot) == 'number' then
if type(slot) == 'number' then
    slot = Constants.getEquipmentSlotName(slot)
slot = Constants.getEquipmentSlotName(slot)
  end
end


  local itemList = Items.getItems(function(item)
local itemList = Items.getItems(function(item)
      local isMatch = true
local isMatch = true
      if style == 'Melee' then
if style == 'Melee' then
        if (Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name) then isMatch = false end
if (Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name) then isMatch = false end
      elseif style == 'Ranged' then
elseif style == 'Ranged' then
        if Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name) then isMatch = false end
if Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name) then isMatch = false end
      elseif style == 'Magic' then
elseif style == 'Magic' then
        if Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name) then isMatch = false end
if Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name) then isMatch = false end
      elseif style == 'None' then
elseif style == 'None' then
        if (Items._getItemStat(item, 'defenceLevelRequired') ~= nil or Items._getItemStat(item, 'rangedLevelRequired') ~= nil or Items._getItemStat(item, 'magicLevelRequired') ~= nil or
if (Items._getItemStat(item, 'defenceLevelRequired') ~= nil or Items._getItemStat(item, 'rangedLevelRequired') ~= nil or Items._getItemStat(item, 'magicLevelRequired') ~= nil or
          Shared.contains(styleOverrides.Melee, item.name) or Shared.contains(styleOverrides.Ranged, item.name) or Shared.contains(styleOverrides.Magic, item.name)) and
Shared.contains(styleOverrides.Melee, item.name) or Shared.contains(styleOverrides.Ranged, item.name) or Shared.contains(styleOverrides.Magic, item.name)) and
          not Shared.contains(styleOverrides.None, item.name) then
not Shared.contains(styleOverrides.None, item.name) then
          isMatch = false
isMatch = false
        end
end
      end
end
      if slot == nil or not Shared.contains(item.validSlots, slot) then isMatch = false end
if slot == nil or not Shared.contains(item.validSlots, slot) then isMatch = false end


      if isMatch and other ~= nil then
if isMatch and other ~= nil then
        if slot == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
if slot == 'Cape' then
          if other == 'Arrows' then
if other == 'Skillcape' then
            if item.ammoTypeRequired ~= 0 then isMatch = false end
if not Shared.contains(item.name, 'Skillcape') then
          elseif other == 'Bolts' then
isMatch = false
            if item.ammoTypeRequired ~= 1 then isMatch = false end
end
          end
elseif other == 'No Skillcapes' then
        elseif slot == 'Quiver' then
if Shared.contains(item.name, 'Skillcape') then
          if other == 'Arrows' then
isMatch = false
            if item.ammoType ~= 0 then isMatch = false end
end
          elseif other == 'Bolts' then
end
            if item.ammoType ~= 1 then isMatch = false end
end
          elseif other == 'Javelins' then
if slot == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
            if item.ammoType ~= 2 then isMatch = false end
if other == 'Arrows' then
          elseif other == 'Throwing Knives' then
if item.ammoTypeRequired ~= 0 then isMatch = false end
            if item.ammoType ~= 3 then isMatch = false end
elseif other == 'Bolts' then
          elseif other == 'Thrown' then
if item.ammoTypeRequired ~= 1 then isMatch = false end
            if item.ammoType ~= 2 and item.ammoType ~= 3 then isMatch = false end
end
          end
elseif slot == 'Quiver' then
        end
if other == 'Arrows' then
      end
if item.ammoType ~= 0 then isMatch = false end
elseif other == 'Bolts' then
if item.ammoType ~= 1 then isMatch = false end
elseif other == 'Javelins' then
if item.ammoType ~= 2 then isMatch = false end
elseif other == 'Throwing Knives' then
if item.ammoType ~= 3 then isMatch = false end
elseif other == 'Thrown' then
if item.ammoType ~= 2 and item.ammoType ~= 3 then isMatch = false end
end
end
end


      return isMatch
return isMatch
    end)
end)
 
  local result = p._getEquipmentTable(itemList, includeModifiers, includeDescription)
return p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
 
  return result
end
end


function p.getCategoryTable(frame)
function p.getCategoryTable(frame)
  local style = frame.args ~= nil and frame.args[1] or frame[1]
local style = frame.args ~= nil and frame.args[1] or frame[1]
  local slot = frame.args ~= nil and frame.args[2] or frame[2]
local slot = frame.args ~= nil and frame.args[2] or frame[2]
  local other = frame.args ~= nil and frame.args[3] or frame[3]
local other = frame.args ~= nil and frame.args[3] or frame[3]
  local includeModifiers = frame.args ~= nil and frame.args.includeModifiers or frame.includeModifiers
local includeModifiers = frame.args ~= nil and frame.args.includeModifiers or frame.includeModifiers
  local includeDescription = frame.args ~= nil and frame.args.includeDescription or frame.includeDescription
local includeDescription = frame.args ~= nil and frame.args.includeDescription or frame.includeDescription
local sortByName = frame.args ~= nil and frame.args.sortByName or frame.sortByName


  includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false
includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false
  includeDescription = includeDescription ~= nil and string.upper(includeDescription) == 'TRUE' or false
includeDescription = includeDescription ~= nil and string.upper(includeDescription) == 'TRUE' or false
sortByName = sortByName ~= nil and string.upper(sortByName) == 'TRUE' or false


  return p._getCategoryTable(style, slot, other, includeModifiers, includeDescription)
return p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
end
end


function p.getTableForList(frame)
function p.getTableForList(frame)
  local stuffString = frame.args ~= nil and frame.args[1] or frame[1]
local stuffString = frame.args ~= nil and frame.args[1] or frame[1]
  local itemNames = Shared.splitString(stuffString, ',')
local itemNames = Shared.splitString(stuffString, ',')


  local includeModifiers = frame.args ~= nil and frame.args[2] or frame[2]
local includeModifiers = frame.args ~= nil and frame.args[2] or frame[2]
  includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false
includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false


  local itemList = {}
local itemList = {}
  local errMsg = 'ERROR: Some items not found in database: [[Category:Pages with script errors]]'
local errMsg = 'ERROR: Some items not found in database: [[Category:Pages with script errors]]'
  local hasErr = false
local hasErr = false
  for i, name in Shared.skpairs(itemNames) do
for i, name in Shared.skpairs(itemNames) do
    local nextItem = Items.getItem(Shared.trim(name))
local nextItem = Items.getItem(Shared.trim(name))
    if nextItem == nil then
if nextItem == nil then
      errMsg = errMsg.." '"..name.."'"
errMsg = errMsg.." '"..name.."'"
      hasErr = true
hasErr = true
    else
else
      table.insert(itemList, nextItem)
table.insert(itemList, nextItem)
    end
end
  end
end


  if hasErr then
if hasErr then
    return errMsg
return errMsg
  else
else
    return p._getEquipmentTable(itemList, includeModifiers)
return p._getEquipmentTable(itemList, includeModifiers)
  end
end
end
end


function p.getDoubleLootTable(frame)
function p.getDoubleLootTable(frame)
  local modsDL = {
local modsDL = {
    'increasedChanceToDoubleLootCombat',
'increasedChanceToDoubleLootCombat',
    'decreasedChanceToDoubleLootCombat',
'decreasedChanceToDoubleLootCombat',
    'increasedChanceToDoubleLootThieving',
'increasedChanceToDoubleLootThieving',
    'decreasedChanceToDoubleLootThieving',
'decreasedChanceToDoubleLootThieving',
    'increasedChanceToDoubleItemsGlobal',
'increasedChanceToDoubleItemsGlobal',
    'decreasedChanceToDoubleItemsGlobal'
'decreasedChanceToDoubleItemsGlobal'
  }
}
  local modDetail = {}
local modDetail = {}
  for i, modName in pairs(modsDL) do
for i, modName in pairs(modsDL) do
    local mName, mText, mSign, mIsNeg, mValUnsigned = Constants.getModifierDetails(modName)
local mName, mText, mSign, mIsNeg, mValUnsigned = Constants.getModifierDetails(modName)
    modDetail[modName] = { mult = (mSign == "+" and 1 or -1) }
modDetail[modName] = { mult = (mSign == "+" and 1 or -1) }
  end
end


  local itemList = Items.getItems(function(item)
local itemList = Items.getItems(function(item)
      if item.modifiers ~= nil then
if item.modifiers ~= nil then
        for modName, val in pairs(item.modifiers) do
for modName, val in pairs(item.modifiers) do
          if Shared.contains(modsDL, modName) then return true end
if Shared.contains(modsDL, modName) then return true end
        end
end
        return false
return false
      end
end
    end)
end)
  table.sort(itemList, function(a, b) return a.id < b.id end)
table.sort(itemList, function(a, b) return a.id < b.id end)


  local resultPart = {}
local resultPart = {}
  table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
  table.insert(resultPart, '\r\n!colspan="2"|Name!!Bonus!!Description')
table.insert(resultPart, '\r\n!colspan="2"|Name!!Bonus!!Description')
  for i, item in Shared.skpairs(itemList) do
for i, item in Shared.skpairs(itemList) do
    local lootValue = 0
local lootValue = 0
    for modName, modDet in pairs(modDetail) do
for modName, modDet in pairs(modDetail) do
      lootValue = lootValue + (item.modifiers[modName] or 0) * modDet.mult
lootValue = lootValue + (item.modifiers[modName] or 0) * modDet.mult
    end
end
    table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
    table.insert(resultPart, '\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
    table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))
    table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
    table.insert(resultPart, '||'..item.description)
table.insert(resultPart, '||'..item.description)
  end
end


  table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
  return table.concat(resultPart)
return table.concat(resultPart)
end
end


function p.getStatChangeString(item1, item2)
function p.getStatChangeString(item1, item2)
  --Used by getItemUpgradeTable to get the stat change between two items
--Used by getItemUpgradeTable to get the stat change between two items
  local changeArray = {}
local changeArray = {}


  local getSpecificStatString = function(val1, val2, subStr)
local getSpecificStatString = function(val1, val2, subStr)
      if val1 == nil then val1 = 0 end
if val1 == nil then val1 = 0 end
      if val2 == nil then val2 = 0 end
if val2 == nil then val2 = 0 end


      if val1 ~= val2 then
if val1 ~= val2 then
        local txt = string.gsub(subStr, '{V}', Shared.numStrWithSign(val1 - val2))
local txt = string.gsub(subStr, '{V}', Shared.numStrWithSign(val1 - val2))
        table.insert(changeArray, txt)
table.insert(changeArray, txt)
      end
end
    end
end


  --Unfortunately just gonna have to manually check all the changes I think...
--Unfortunately just gonna have to manually check all the changes I think...
  local statList = {
local statList = {
    -- {'statName', 'statDescription'}
-- {'statName', 'statDescription'}
    {'stabAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Stab Bonus'},
{'stabAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Stab Bonus'},
    {'slashAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Slash Bonus'},
{'slashAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Slash Bonus'},
    {'blockAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Block Bonus'},
{'blockAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Block Bonus'},
    {'meleeStrengthBonus', '{V} '..Icons.Icon({'Strength', type='skill', notext=true})..' Strength Bonus'},
{'meleeStrengthBonus', '{V} '..Icons.Icon({'Strength', type='skill', notext=true})..' Strength Bonus'},
    {'rangedStrengthBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Strength Bonus'},
{'rangedStrengthBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Strength Bonus'},
    {'magicStrengthBonus', '{V}% '..Icons.Icon({'Magic', type='skill', notext=true})..' Damage Bonus'},
{'magicStrengthBonus', '{V}% '..Icons.Icon({'Magic', type='skill', notext=true})..' Damage Bonus'},
    {'meleeDefenceBonus', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Defence Bonus'},
{'meleeDefenceBonus', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Defence Bonus'},
    {'rangedDefenceBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Defence Bonus'},
{'rangedDefenceBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Defence Bonus'},
    {'magicDefenceBonus', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Defence Bonus'},
{'magicDefenceBonus', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Defence Bonus'},
    {'damageReduction', '{V}% Damage Reduction'},
{'damageReduction', '{V}% Damage Reduction'},
    {'increasedSlayerXP', '{V}% '..Icons.Icon({'Slayer', type='skill', notext=true})..' Bonus XP'},
{'increasedSlayerXP', '{V}% '..Icons.Icon({'Slayer', type='skill', notext=true})..' Bonus XP'},
    {'attackLevelRequired', '{V} '..Icons.Icon({'Attack', type='skill', notext=true})..' Level Required'},
{'attackLevelRequired', '{V} '..Icons.Icon({'Attack', type='skill', notext=true})..' Level Required'},
    {'defenceLevelRequired', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Level Required'},
{'defenceLevelRequired', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Level Required'},
    {'rangedLevelRequired', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Level Required'},
{'rangedLevelRequired', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Level Required'},
    {'magicLevelRequired', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Level Required'},
{'magicLevelRequired', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Level Required'},
  }
}
  for i, stat in ipairs(statList) do
for i, stat in ipairs(statList) do
    if stat[1] == 'increasedSlayerXP' then
if stat[1] == 'increasedSlayerXP' then
      getSpecificStatString(Items._getItemModifier(item1, stat[1], 'Slayer'), Items._getItemModifier(item2, stat[1], 'Slayer'), stat[2])
getSpecificStatString(Items._getItemModifier(item1, stat[1], 'Slayer'), Items._getItemModifier(item2, stat[1], 'Slayer'), stat[2])
    else
else
      getSpecificStatString(Items._getItemStat(item1, stat[1]), Items._getItemStat(item2, stat[1]), stat[2])
getSpecificStatString(Items._getItemStat(item1, stat[1]), Items._getItemStat(item2, stat[1]), stat[2])
    end
end
  end
end


  return table.concat(changeArray, '<br/>')
return table.concat(changeArray, '<br/>')
end
end


function p.getItemUpgradeTable(frame)
function p.getItemUpgradeTable(frame)
  local category = frame.args ~= nil and frame.args[1] or frame
local category = frame.args ~= nil and frame.args[1] or frame
  local itemArray = {}
local itemArray = {}
  local isEquipment = false
local isEquipment = false


  if string.upper(category) == 'POTION' then
if string.upper(category) == 'POTION' then
    itemArray = Items.getItems(function(item) return Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil end)
itemArray = Items.getItems(function(item) return Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil end)
  elseif string.upper(category) == 'OTHER' then
elseif string.upper(category) == 'OTHER' then
    itemArray = Items.getItems(function(item)
itemArray = Items.getItems(function(item)
          return not Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil and item.validSlots == nil
return not Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil and item.validSlots == nil
        end)
end)
  else
else
    if Constants.getEquipmentSlotID(category) == nil then
if Constants.getEquipmentSlotID(category) == nil then
      return 'ERROR: Invalid option. Choose either an equipment slot, Potion, or Other[[Category:Pages with script errors]]'
return 'ERROR: Invalid option. Choose either an equipment slot, Potion, or Other[[Category:Pages with script errors]]'
    end
end
    isEquipment = true
isEquipment = true
    itemArray = Items.getItems(function(item)
itemArray = Items.getItems(function(item)
          return item.itemsRequired ~= nil and Shared.contains(item.validSlots, category)
return item.itemsRequired ~= nil and Shared.contains(item.validSlots, category)
        end)
end)
  end
end
  table.sort(itemArray, function(a, b) return a.id < b.id end)
table.sort(itemArray, function(a, b) return a.id < b.id end)


  local resultPart = {}
local resultPart = {}
  table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
  table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
  table.insert(resultPart, '\r\n!colspan="2"|Upgraded Item!!Ingredients')
table.insert(resultPart, '\r\n!colspan="2"|Upgraded Item!!Ingredients')
  if isEquipment then table.insert(resultPart, '!!Stat Change') end
if isEquipment then table.insert(resultPart, '!!Stat Change') end


  for i, item in Shared.skpairs(itemArray) do
for i, item in Shared.skpairs(itemArray) do
    table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
    table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
    table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))


    local matArray = {}
local matArray = {}
    local statChangeString = ''
local statChangeString = ''
    for i, row in Shared.skpairs(item.itemsRequired) do
for i, row in Shared.skpairs(item.itemsRequired) do
      local mat = Items.getItemByID(row[1])
local mat = Items.getItemByID(row[1])
      table.insert(matArray, Icons.Icon({mat.name, type='item', qty=row[2]}))
table.insert(matArray, Icons.Icon({mat.name, type='item', qty=row[2]}))


      if item.validSlots ~= nil and mat.validSlots ~= nil and statChangeString == '' then
if item.validSlots ~= nil and mat.validSlots ~= nil and statChangeString == '' then
        statChangeString = p.getStatChangeString(item, mat)
statChangeString = p.getStatChangeString(item, mat)
      end
end
    end
end
    if item.trimmedGPCost ~= nil and item.trimmedGPCost > 0 then
if item.trimmedGPCost ~= nil and item.trimmedGPCost > 0 then
      table.insert(matArray, Icons.GP(item.trimmedGPCost))
table.insert(matArray, Icons.GP(item.trimmedGPCost))
    end
end
    table.insert(resultPart, '||'..table.concat(matArray, '<br/>'))
table.insert(resultPart, '||'..table.concat(matArray, '<br/>'))


    if isEquipment then
if isEquipment then
      table.insert(resultPart, '||'..statChangeString)
table.insert(resultPart, '||'..statChangeString)
    end
end
  end
end


  table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
  return table.concat(resultPart)
return table.concat(resultPart)
end
end


return p
return p

Revision as of 21:15, 1 January 2022

Documentation for this module may be created at Module:Items/ComparisonTables/doc

local p = {}

local ItemData = mw.loadData('Module:Items/data')

local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Icons = require('Module:Icons')
local Items = require('Module:Items')

local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}

local styleOverrides = {
	Melee = {'Slayer Helmet (Basic)', 'Slayer Platebody (Basic)', 'Paladin Gloves', 'Desert Wrappings', 'Almighty Lute', 'Candy Cane', 'Bob&apos;s Rake'},
	Ranged = {'Slayer Cowl (Basic)', 'Slayer Leather Body (Basic)', 'Ice Arrows'},
	Magic = {'Slayer Wizard Hat (Basic)', 'Slayer Wizard Robes (Basic)', 'Enchanted Shield', 'Elementalist Gloves'},
	None = {},
}

function p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
	if includeModifiers == nil then includeModifiers = false end
	if sortByName == nil then sortByName = false end

	--Getting some lists set up here that will be used later
	--First, the list of columns used by both weapons & armour
	local statColumns = {'stabAttackBonus', 'slashAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'meleeStrengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'meleeDefenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction', 'attackLevelRequired', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}

	if(Shared.tableCount(itemList) == 0) then
		return 'ERROR: you must select at least one item to get stats for[[Category:Pages with script errors]]'
	end

	local isWeaponType = ((itemList[1].validSlots ~= nil and Shared.contains(itemList[1].validSlots, 'Weapon'))
							or (itemList[1].occupiesSlots ~= nil and Shared.contains(itemList[1].occupiesSlots, 'Weapon'))) and Shared.contains(weaponTypes, itemList[1].type)

	--Now that we have a preliminary list, let's figure out which columns are irrelevant (IE are zero for all items in the selection)
	local ignoreColumns = Shared.clone(statColumns)
	for i, item in pairs(itemList) do
		local ndx = 1
		while Shared.tableCount(ignoreColumns) >= ndx do
			if Items._getItemStat(item, ignoreColumns[ndx], true) ~= 0 then
				table.remove(ignoreColumns, ndx)
			else
				ndx = ndx + 1
			end
		end
	end

	--Now to remove the ignored columns (and also we need to track groups like defence bonuses to see how many remain)
	local attBonusCols = 5
	local strBonusCols = 2
	local defBonusCols = 3
	local lvlReqCols = 4
	local ndx = 1
	while Shared.tableCount(statColumns) >= ndx do
		local colName = statColumns[ndx]
		if Shared.contains(ignoreColumns, colName) then
			if Shared.contains(colName, 'AttackBonus') then attBonusCols = attBonusCols - 1 end
			if Shared.contains(colName, 'trengthBonus') then strBonusCols = strBonusCols - 1 end
			if Shared.contains(colName, 'efenceBonus') then defBonusCols = defBonusCols - 1 end
			if Shared.contains(colName, 'LevelRequired') then lvlReqCols = lvlReqCols - 1 end
			table.remove(statColumns, ndx)
		else
			ndx = ndx + 1
		end
	end

	--Alright, let's start the table by building the shared header
	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
	if isWeaponType then
		--Weapons have extra columns here for Attack Speed and "Two Handed?"
		table.insert(resultPart, '\r\n!colspan="4"|')
	else
		table.insert(resultPart, '\r\n!colspan="2"|')
	end
	if attBonusCols > 0 then
		table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus')
	end
	if strBonusCols > 0 then
		table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Str. Bonus')
	end
	if Shared.contains(statColumns, 'magicDamageBonus') then
		table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Dmg Bonus')
	end
	if defBonusCols > 0 then
		table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus')
	end
	if Shared.contains(statColumns, 'damageReduction') then
		table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|DR')
	end
	if lvlReqCols > 0 then
		table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Lvl Req')
	end
	if includeModifiers and includeDescription then
		table.insert(resultPart, '\r\n!colspan="2"|')
	elseif includeModifiers or includeDescription then
		table.insert(resultPart, '\r\n!colspan="1"|')
	end
	--One header row down, one to go
	table.insert(resultPart, '\r\n|-class="headerRow-1"')
	table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Item')
	table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Name')
	--Weapons have Attack Speed here
	if isWeaponType then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed')
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?')
	end
	--Attack bonuses
	if Shared.contains(statColumns, 'slashAttackBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'stabAttackBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'blockAttackBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedAttackBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicAttackBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
	end
	--Strength bonuses
	if Shared.contains(statColumns, 'meleeStrengthBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedStrengthBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicDamageBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
	end
	--Defence bonuses
	if Shared.contains(statColumns, 'meleeDefenceBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedDefenceBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicDefenceBonus') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'damageReduction') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	--Level requirements
	if Shared.contains(statColumns, 'attackLevelRequired') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'defenceLevelRequired') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedLevelRequired') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicLevelRequired') then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
	end
	--If includeModifiers is set to 'true', add the Modifiers column
	if includeModifiers then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Modifiers')
	end
	--If includeDescription is set to 'true', add the Description column
	if includeDescription then
		table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Description')
	end

	if sortByName then
		table.sort(itemList, function(a, b) return a.name < b.name end)
	else	
		table.sort(itemList, function(a, b) return a.id < b.id end)
	end
	for i, item in pairs(itemList) do
		if isWeaponType then
			--Building rows for weapons
			local atkSpeed = Items._getItemStat(item, 'attackSpeed', true)
			table.insert(resultPart, '\r\n|-')
			table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
			table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
			table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.round(atkSpeed / 1000, 3, 1) .. 's')
			--That's the first list out of the way, now for 2-Handed
			table.insert(resultPart, '\r\n| style ="text-align: right;"|')
			table.insert(resultPart, Items._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')
			for j, statName in pairs(statColumns) do
				local statValue = Items._getItemStat(item, statName, true)
				table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
				if string.find(statName, '^(.+)LevelRequired$') == nil then
					if statValue > 0 then
						table.insert(resultPart, 'background-color:lightgreen;')
					elseif statValue < 0 then
						table.insert(resultPart, 'background-color:lightpink;')
					end
				end
				table.insert(resultPart, '"|'..Shared.formatnum(statValue))
				if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
			end
			--If requested, add the item Modifiers
			if includeModifiers then
				table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
				table.insert(resultPart, Constants.getModifiersText(item.modifiers, true))
			end
			--If requested, add description
			if includeDescription then
				table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
				table.insert(resultPart, item.description ~= nil and item.description or '')
			end
		else
			--Building rows for armour
			table.insert(resultPart, '\r\n|-')
			table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
			table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
			for j, statName in pairs(statColumns) do
				local statValue = Items._getItemStat(item, statName, true)
				table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
				if statValue > 0 then
					table.insert(resultPart, 'background-color:lightgreen;')
				elseif statValue < 0 then
					table.insert(resultPart, 'background-color:lightpink;')
				end
				table.insert(resultPart, '"|'..Shared.formatnum(statValue))
				if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
			end
			--If requested, add the item Modifiers
			if includeModifiers then
				table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
				table.insert(resultPart, Constants.getModifiersText(item.modifiers, true))
			end
			--If requested, add description
			if includeDescription then
				table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
				table.insert(resultPart, item.description ~= nil and item.description or '')
			end
		end
	end

	table.insert(resultPart, '\r\n|}')

	return table.concat(resultPart)
end

function p.getEquipmentTable(frame)
	local args = frame.args ~= nil and frame.args or frame
	local type = args.type
	local tier = args.tier
	local slot = args.slot
	local ammoTypeStr = args.ammoType
	local category = args.category ~= nil and args.category or 'Combat'

	 --Find out what Ammo Type we're working with
	local ammoType = nil
	if ammoTypeStr ~= nil then
		if ammoTypeStr == "Arrows" then
			ammoType = 0
		elseif ammoTypeStr == 'Bolts' then
			ammoType = 1
		elseif ammoTypeStr == 'Javelins' then
			ammoType = 2
		elseif ammoTypeStr == 'Throwing Knives' then
			ammoType = 3
		end
	end

	local isWeaponType = Shared.contains(weaponTypes, type)

	--Now we need to figure out which items are in this list
	local itemList = {}
	for i, itemBase in pairs(ItemData.Items) do
		local item = Shared.clone(itemBase)
		item.id = i - 1
		local listItem = false
		if isWeaponType then
		listItem = item.type == type and item.category == category
			if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
		else
			--Now for handling armour
			if type == "Armour" or type == "Melee" then
				listItem = Items._getItemStat(item, 'defenceLevelRequired') ~= nil or (item.category == 'Combat' and item.type == 'Armour')
			elseif type == "Ranged Armour" or type == "Ranged" then
				listItem = Items._getItemStat(item, 'rangedLevelRequired') ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
			elseif type == "Magic Armour" or type == "Magic" then
				listItem = Items._getItemStat(item, 'magicLevelRequired') or (item.category == 'Combat' and item.type == 'Magic Armour')
			else
				listItem = item.type == type and item.category ~= 'Combat'
			end
			if ammoType ~= nil then listItem = listItem and item.ammoType == ammoType end
			if slot ~= nil then listItem = listItem and Shared.contains(item.validSlots, slot) end
		end
		if listItem then
			table.insert(itemList, item)
		end
	end

	local result = p._getEquipmentTable(itemList).."[[Category:getEquipmentTable]]"

	return result
end

function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
	if type(slot) == 'number' then
		slot = Constants.getEquipmentSlotName(slot)
	end

	local itemList = Items.getItems(function(item)
			local isMatch = true
			if style == 'Melee' then
				if (Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name) then isMatch = false end
			elseif style == 'Ranged' then
				if Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name) then isMatch = false end
			elseif style == 'Magic' then
				if Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name) then isMatch = false end
			elseif style == 'None' then
				if (Items._getItemStat(item, 'defenceLevelRequired') ~= nil or Items._getItemStat(item, 'rangedLevelRequired') ~= nil or Items._getItemStat(item, 'magicLevelRequired') ~= nil or
					 Shared.contains(styleOverrides.Melee, item.name) or Shared.contains(styleOverrides.Ranged, item.name) or Shared.contains(styleOverrides.Magic, item.name)) and
					 not Shared.contains(styleOverrides.None, item.name) then
					isMatch = false
				end
			end
			if slot == nil or not Shared.contains(item.validSlots, slot) then isMatch = false end

			if isMatch and other ~= nil then
				if slot == 'Cape' then
					if other == 'Skillcape' then
						if not Shared.contains(item.name, 'Skillcape') then
							isMatch = false
						end
					elseif other == 'No Skillcapes' then
						if Shared.contains(item.name, 'Skillcape') then
							isMatch = false
						end
					end
				end
				if slot == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
					if other == 'Arrows' then
						if item.ammoTypeRequired ~= 0 then isMatch = false end
					elseif other == 'Bolts' then
						if item.ammoTypeRequired ~= 1 then isMatch = false end
					end
				elseif slot == 'Quiver' then
					if other == 'Arrows' then
						if item.ammoType ~= 0 then isMatch = false end
					elseif other == 'Bolts' then
						if item.ammoType ~= 1 then isMatch = false end
					elseif other == 'Javelins' then
						if item.ammoType ~= 2 then isMatch = false end
					elseif other == 'Throwing Knives' then
						if item.ammoType ~= 3 then isMatch = false end
					elseif other == 'Thrown' then
						if item.ammoType ~= 2 and item.ammoType ~= 3 then isMatch = false end
					end
				end
			end

			return isMatch
		end)
		
	return p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
end

function p.getCategoryTable(frame)
	local style = frame.args ~= nil and frame.args[1] or frame[1]
	local slot = frame.args ~= nil and frame.args[2] or frame[2]
	local other = frame.args ~= nil and frame.args[3] or frame[3]
	local includeModifiers = frame.args ~= nil and frame.args.includeModifiers or frame.includeModifiers
	local includeDescription = frame.args ~= nil and frame.args.includeDescription or frame.includeDescription
	local sortByName = frame.args ~= nil and frame.args.sortByName or frame.sortByName

	includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false
	includeDescription = includeDescription ~= nil and string.upper(includeDescription) == 'TRUE' or false
	sortByName = sortByName ~= nil and string.upper(sortByName) == 'TRUE' or false

	return p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
end

function p.getTableForList(frame)
	local stuffString = frame.args ~= nil and frame.args[1] or frame[1]
	local itemNames = Shared.splitString(stuffString, ',')

	local includeModifiers = frame.args ~= nil and frame.args[2] or frame[2]
	includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false

	local itemList = {}
	local errMsg = 'ERROR: Some items not found in database: [[Category:Pages with script errors]]'
	local hasErr = false
	for i, name in Shared.skpairs(itemNames) do
		local nextItem = Items.getItem(Shared.trim(name))
		if nextItem == nil then
			errMsg = errMsg.." '"..name.."'"
			hasErr = true
		else
			table.insert(itemList, nextItem)
		end
	end

	if hasErr then
		return errMsg
	else
		return p._getEquipmentTable(itemList, includeModifiers)
	end
end

function p.getDoubleLootTable(frame)
	local modsDL = {
		'increasedChanceToDoubleLootCombat',
		'decreasedChanceToDoubleLootCombat',
		'increasedChanceToDoubleLootThieving',
		'decreasedChanceToDoubleLootThieving',
		'increasedChanceToDoubleItemsGlobal',
		'decreasedChanceToDoubleItemsGlobal'
	}
	local modDetail = {}
	for i, modName in pairs(modsDL) do
		local mName, mText, mSign, mIsNeg, mValUnsigned = Constants.getModifierDetails(modName)
		modDetail[modName] = { mult = (mSign == "+" and 1 or -1) }
	end

	local itemList = Items.getItems(function(item)
			if item.modifiers ~= nil then
				for modName, val in pairs(item.modifiers) do
					if Shared.contains(modsDL, modName) then return true end
				end
				return false
			end
		end)
	table.sort(itemList, function(a, b) return a.id < b.id end)

	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
	table.insert(resultPart, '\r\n!colspan="2"|Name!!Bonus!!Description')
	for i, item in Shared.skpairs(itemList) do
		local lootValue = 0
		for modName, modDet in pairs(modDetail) do
			lootValue = lootValue + (item.modifiers[modName] or 0) * modDet.mult
		end
		table.insert(resultPart, '\r\n|-')
		table.insert(resultPart, '\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
		table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))
		table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
		table.insert(resultPart, '||'..item.description)
	end

	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

function p.getStatChangeString(item1, item2)
	--Used by getItemUpgradeTable to get the stat change between two items
	local changeArray = {}

	local getSpecificStatString = function(val1, val2, subStr)
			if val1 == nil then val1 = 0 end
			if val2 == nil then val2 = 0 end

			if val1 ~= val2 then
				local txt = string.gsub(subStr, '{V}', Shared.numStrWithSign(val1 - val2))
				table.insert(changeArray, txt)
			end
		end

	--Unfortunately just gonna have to manually check all the changes I think...
	local statList = {
		-- {'statName', 'statDescription'}
		{'stabAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Stab Bonus'},
		{'slashAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Slash Bonus'},
		{'blockAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Block Bonus'},
		{'meleeStrengthBonus', '{V} '..Icons.Icon({'Strength', type='skill', notext=true})..' Strength Bonus'},
		{'rangedStrengthBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Strength Bonus'},
		{'magicStrengthBonus', '{V}% '..Icons.Icon({'Magic', type='skill', notext=true})..' Damage Bonus'},
		{'meleeDefenceBonus', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Defence Bonus'},
		{'rangedDefenceBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Defence Bonus'},
		{'magicDefenceBonus', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Defence Bonus'},
		{'damageReduction', '{V}% Damage Reduction'},
		{'increasedSlayerXP', '{V}% '..Icons.Icon({'Slayer', type='skill', notext=true})..' Bonus XP'},
		{'attackLevelRequired', '{V} '..Icons.Icon({'Attack', type='skill', notext=true})..' Level Required'},
		{'defenceLevelRequired', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Level Required'},
		{'rangedLevelRequired', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Level Required'},
		{'magicLevelRequired', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Level Required'},
	}
	for i, stat in ipairs(statList) do
		if stat[1] == 'increasedSlayerXP' then
			getSpecificStatString(Items._getItemModifier(item1, stat[1], 'Slayer'), Items._getItemModifier(item2, stat[1], 'Slayer'), stat[2])
		else
			getSpecificStatString(Items._getItemStat(item1, stat[1]), Items._getItemStat(item2, stat[1]), stat[2])
		end
	end

	return table.concat(changeArray, '<br/>')
end

function p.getItemUpgradeTable(frame)
	local category = frame.args ~= nil and frame.args[1] or frame
	local itemArray = {}
	local isEquipment = false

	if string.upper(category) == 'POTION' then
		itemArray = Items.getItems(function(item) return Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil end)
	elseif string.upper(category) == 'OTHER' then
		itemArray = Items.getItems(function(item)
					 return not Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil and item.validSlots == nil
				 end)
	else
		if Constants.getEquipmentSlotID(category) == nil then
			return 'ERROR: Invalid option. Choose either an equipment slot, Potion, or Other[[Category:Pages with script errors]]'
		end
		isEquipment = true
		itemArray = Items.getItems(function(item)
					 return item.itemsRequired ~= nil and Shared.contains(item.validSlots, category)
				 end)
	end
	table.sort(itemArray, function(a, b) return a.id < b.id end)

	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
	table.insert(resultPart, '\r\n|- class="headerRow-0"')
	table.insert(resultPart, '\r\n!colspan="2"|Upgraded Item!!Ingredients')
	if isEquipment then table.insert(resultPart, '!!Stat Change') end

	for i, item in Shared.skpairs(itemArray) do
		table.insert(resultPart, '\r\n|-')
		table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
		table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))

		local matArray = {}
		local statChangeString = ''
		for i, row in Shared.skpairs(item.itemsRequired) do
			local mat = Items.getItemByID(row[1])
			table.insert(matArray, Icons.Icon({mat.name, type='item', qty=row[2]}))

			if item.validSlots ~= nil and mat.validSlots ~= nil and statChangeString == '' then
				statChangeString = p.getStatChangeString(item, mat)
			end
		end
		if item.trimmedGPCost ~= nil and item.trimmedGPCost > 0 then
			table.insert(matArray, Icons.GP(item.trimmedGPCost))
		end
		table.insert(resultPart, '||'..table.concat(matArray, '<br/>'))

		if isEquipment then
			table.insert(resultPart, '||'..statChangeString)
		end
	end

	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

return p