Module:Items/ComparisonTables: Difference between revisions

From Melvor Idle
(Final tweaks)
(added code to show special attacks in the Modifiers table for equipment because it seemed like the thing to do)
(33 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local p = {}
local p = {}


local MonsterData = mw.loadData('Module:Monsters/data')
local Constants = require('Module:Constants')
local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')
local Constants = mw.loadData('Module:Constants/data')
 
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Magic = require('Module:Magic')
local Areas = require('Module:CombatAreas')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Items = require('Module:Items')
local ItemSourceTables = require('Module:Items/SourceTables')
local ItemUseTables = require('Module:Items/UseTables')


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


function p._getEquipmentTable(itemList)
local styleOverrides = {
  --Getting some lists set up here that will be used later
Melee = {'Slayer Helmet (Basic)', 'Slayer Platebody (Basic)', 'Paladin Gloves', 'Desert Wrappings', 'Almighty Lute', 'Candy Cane', 'Bob's Rake'},
  --First, the list of columns used by both weapons & armour
Ranged = {'Slayer Cowl (Basic)', 'Slayer Leather Body (Basic)', 'Ice Arrows'},
  local statColumns = {'stabAttackBonus', 'slashAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'strengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'defenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction', 'attackLevelRequired', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
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


  if(Shared.tableCount(itemList) == 0) then
local isWeaponType = ((itemList[1].validSlots ~= nil and Shared.contains(itemList[1].validSlots, 'Weapon'))
    return 'ERROR: you must select at least one item to get stats for'
or (itemList[1].occupiesSlots ~= nil and Shared.contains(itemList[1].occupiesSlots, 'Weapon'))) and Shared.contains(weaponTypes, itemList[1].type)
  end


  local isWeaponType = 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 that we have a preliminary list, let's figure out which columns are irrelevant (IE are zero for all items in the selection)
--Now to remove the ignored columns (and also we need to track groups like defence bonuses to see how many remain)
  local ignoreColumns = Shared.clone(statColumns)
local attBonusCols = 5
  for i, item in pairs(itemList) do
local strBonusCols = 2
    local ndx = 1
local defBonusCols = 3
    while Shared.tableCount(ignoreColumns) >= ndx do
local lvlReqCols = 4
      if Items._getItemStat(item, ignoreColumns[ndx], true) ~= 0 then
local ndx = 1
        table.remove(ignoreColumns, ndx)
while Shared.tableCount(statColumns) >= ndx do
      else
local colName = statColumns[ndx]
        ndx = ndx + 1
if Shared.contains(ignoreColumns, colName) then
      end
if Shared.contains(colName, 'AttackBonus') then attBonusCols = attBonusCols - 1 end
    end
if Shared.contains(colName, 'trengthBonus') then strBonusCols = strBonusCols - 1 end
  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


  --Now to remove the ignored columns (and also we need to track groups like defence bonuses to see how many remain)
--Alright, let's start the table by building the shared header
  local attBonusCols = 5
local resultPart = {}
  local strBonusCols = 2
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
  local defBonusCols = 3
if isWeaponType then
  local lvlReqCols = 4
--Weapons have extra columns here for Attack Speed and "Two Handed?"
  local ndx = 1
table.insert(resultPart, '\r\n!colspan="4"|')
  while Shared.tableCount(statColumns) >= ndx do
else
    local colName = statColumns[ndx]
table.insert(resultPart, '\r\n!colspan="2"|')
    if Shared.contains(ignoreColumns, colName) then
end
      if Shared.contains(colName, 'AttackBonus') then attBonusCols = attBonusCols - 1 end
if attBonusCols > 0 then
      if Shared.contains(colName, 'trengthBonus') then strBonusCols = strBonusCols - 1 end
table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus')
      if Shared.contains(colName, 'efenceBonus') then defBonusCols = defBonusCols - 1 end
end
      if Shared.contains(colName, 'LevelRequired') then lvlReqCols = lvlReqCols - 1 end
if strBonusCols > 0 then
      table.remove(statColumns, ndx)
table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Str. Bonus')
    else
end
      ndx = ndx + 1
if Shared.contains(statColumns, 'magicDamageBonus') then
    end
table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Dmg Bonus')
  end
end
 
if defBonusCols > 0 then
  --Alright, let's start the table by building the shared header
table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus')
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
end
  if isWeaponType then
if Shared.contains(statColumns, 'damageReduction') then
    --Weapons have extra columns here for Attack Speed and "Two Handed?"
table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|DR')
    result = result..'\r\n!colspan="4"|'
end
  else
if lvlReqCols > 0 then
    result = result..'\r\n!colspan="2"|'
table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Lvl Req')
  end
end
  if attBonusCols > 0 then
if includeModifiers and includeDescription then
    result = result..'\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus'
table.insert(resultPart, '\r\n!colspan="2"|')
  end
elseif includeModifiers or includeDescription then
  if strBonusCols > 0 then
table.insert(resultPart, '\r\n!colspan="1"|')
    result = result..'\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Str. Bonus'
end
  end
--One header row down, one to go
  if Shared.contains(statColumns, 'magicDamageBonus') then
table.insert(resultPart, '\r\n|-class="headerRow-1"')
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Damage Bonus'
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Item')
  end
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Name')
  if defBonusCols > 0 then
--Weapons have Attack Speed here
    result = result..'\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus'
if isWeaponType then
  end
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed')
  if Shared.contains(statColumns, 'damageReduction') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?')
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|DR'
end
  end
--Attack bonuses
  if lvlReqCols > 0 then
if Shared.contains(statColumns, 'slashAttackBonus') then
    result = result..'\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Lvl Req'
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
  end
end
  result = result..'\r\n!colspan="1"|'
if Shared.contains(statColumns, 'stabAttackBonus') then
  --One header row down, one to go
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
  result = result..'\r\n|-class="headerRow-1"'
end
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Item'
if Shared.contains(statColumns, 'blockAttackBonus') then
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Name'
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
  --Weapons have Attack Speed here
end
  if isWeaponType then
if Shared.contains(statColumns, 'rangedAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed'
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?'
end
  end
if Shared.contains(statColumns, 'magicAttackBonus') then
  --Attack bonuses
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
  if Shared.contains(statColumns, 'slashAttackBonus') then
end
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
--Strength bonuses
  end
if Shared.contains(statColumns, 'meleeStrengthBonus') 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'}))
    result = result..'\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, 'blockAttackBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
end
  end
if Shared.contains(statColumns, 'magicDamageBonus') then
  if Shared.contains(statColumns, 'rangedAttackBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
end
  end
--Defence bonuses
  if Shared.contains(statColumns, 'magicAttackBonus') then
if Shared.contains(statColumns, 'meleeDefenceBonus') then
    result = result..'\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({'Defence', type='skill', notext='true'}))
  end
end
  --Strength bonuses
if Shared.contains(statColumns, 'rangedDefenceBonus') then
  if Shared.contains(statColumns, 'strengthBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
end
  end
if Shared.contains(statColumns, 'magicDefenceBonus') then
  if Shared.contains(statColumns, 'rangedStrengthBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
end
  end
if Shared.contains(statColumns, 'damageReduction') then
  if Shared.contains(statColumns, 'magicDamageBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
end
  end
--Level requirements
  --Defence bonuses
if Shared.contains(statColumns, 'attackLevelRequired') then
  if Shared.contains(statColumns, 'defenceBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
end
  end
if Shared.contains(statColumns, 'defenceLevelRequired') then
  if Shared.contains(statColumns, 'rangedDefenceBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
end
  end
if Shared.contains(statColumns, 'rangedLevelRequired') then
  if Shared.contains(statColumns, 'magicDefenceBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
end
  end
if Shared.contains(statColumns, 'magicLevelRequired') then
  if Shared.contains(statColumns, 'damageReduction') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
end
  end
--If includeModifiers is set to 'true', add the Modifiers column
  --Level requirements
if includeModifiers then
  if Shared.contains(statColumns, 'attackLevelRequired') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Modifiers')
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
end
  end
--If includeDescription is set to 'true', add the Description column
  if Shared.contains(statColumns, 'defenceLevelRequired') then
if includeDescription then
    result = result..'\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;"|Description')
  end
end
  if Shared.contains(statColumns, 'rangedLevelRequired') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicLevelRequired') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  --And finally Sources
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Sources'


  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)
      result = result..'\r\n|-'
end
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
for i, item in pairs(itemList) do
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
if isWeaponType then
      result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.formatnum(item.attackSpeed)
--Building rows for weapons
      --That's the first list out of the way, now for 2-Handed
local atkSpeed = Items._getItemStat(item, 'attackSpeed', true)
      result = result..'\r\n| style ="text-align: right;"|'
table.insert(resultPart, '\r\n|-')
      if item.isTwoHanded then result = result..'Yes' else result = result..'No' end
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
      for j, statName in pairs(statColumns) do
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
        local statValue = Items._getItemStat(item, statName, 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')
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
--That's the first list out of the way, now for 2-Handed
        if not Shared.contains(statName, 'LevelRequired') then
table.insert(resultPart, '\r\n| style ="text-align: right;"|')
          if statValue > 0 then
table.insert(resultPart, Items._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')
            result = result..'background-color:lightgreen;'
for j, statName in pairs(statColumns) do
          elseif statValue < 0 then
local statValue = Items._getItemStat(item, statName, true)
            result = result..'background-color:lightpink;'
table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
          end
if string.find(statName, '^(.+)LevelRequired$') == nil then
        end
if statValue > 0 then
        result = result..'"|'..Shared.formatnum(statValue)
table.insert(resultPart, 'background-color:lightgreen;')
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
elseif statValue < 0 then
      end
table.insert(resultPart, 'background-color:lightpink;')
      --Finally, the Sources
end
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
end
      result = result..ItemSourceTables._getItemSources(item)
table.insert(resultPart, '"|'..Shared.formatnum(statValue))
    else
if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
      --Building rows for armour
end
      result = result..'\r\n|-'
--If requested, add the item Modifiers
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
if includeModifiers then
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
      for j, statName in pairs(statColumns) do
local txtLines = {}
        local statValue = Items._getItemStat(item, statName, true)
local modTxt = Constants.getModifiersText(item.modifiers, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
if modTxt ~= '' then
        if statValue > 0 then
table.insert(txtLines, modTxt)
          result = result..'background-color:lightgreen;'
end
        elseif statValue < 0 then
--For items with a special attack, show the details
          result = result..'background-color:lightpink;'
if item.hasSpecialAttack then
        end
table.insert(txtLines, "'''Special Attack:'''")
        result = result..'"|'..Shared.formatnum(statValue)
for i, spAtt in ipairs(item.specialAttacks) do
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
table.insert(txtLines, spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
      end
table.insert(txtLines, spAtt.description)
      --Finally, the Sources
end
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
end
      result = result..ItemSourceTables._getItemSources(item)
table.insert(resultPart, table.concat(txtLines, '<br/>'))
    end
end
  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;"|')
local txtLines = {}
local modTxt = Constants.getModifiersText(item.modifiers, true)
if modTxt ~= '' then
table.insert(txtLines, modTxt)
end
--For items with a special attack, show the details
if item.hasSpecialAttack then
table.insert(txtLines, "'''Special Attack:'''")
for i, spAtt in ipairs(item.specialAttacks) do
table.insert(txtLines, spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
table.insert(txtLines, spAtt.description)
end
end
table.insert(resultPart, table.concat(txtLines, '<br/>'))
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


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


function p.getEquipmentTable(frame)
function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
  local args = frame.args ~= nil and frame.args or frame
if type(slot) == 'number' then
  local type = args.type
slot = Constants.getEquipmentSlotName(slot)
  local tier = args.tier
end
  local slotStr = args.slot
 
  local ammoTypeStr = args.ammoType
local itemList = Items.getItems(function(item)
  local category = args.category ~= nil and args.category or 'Combat'
-- Exclude Golbin raid exclusives for now, such that they don't
-- pollute various equipment tables
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
return false
end
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
local isSkillcape = Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion'
if other == 'Skillcapes' then
isMatch = isSkillcape
elseif other == 'No Skillcapes' then
isMatch = not isSkillcape
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


  --Find out what Ammo Type we're working with
return isMatch
  local ammoType = nil
end)
  if ammoTypeStr ~= nil then
    if ammoTypeStr == "Arrows" then
return p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
      ammoType = 0
end
    elseif ammoTypeStr == 'Bolts' then
      ammoType = 1
    elseif ammoTypeStr == 'Javelins' then
      ammoType = 2
    elseif ammoTypeStr == 'Throwing Knives' then
      ammoType = 3
    end
  end


  --Find out what slot we're working with
function p.getCategoryTable(frame)
  local slot = nil
local style = frame.args ~= nil and frame.args[1] or frame[1]
  if slotStr ~= nil then
local slot = frame.args ~= nil and frame.args[2] or frame[2]
    slot = Constants.equipmentSlot[slotStr]
local other = frame.args ~= nil and frame.args[3] or frame[3]
  end
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 isWeaponType = Shared.contains(weaponTypes, type)
local sortByName = frame.args ~= nil and frame.args.sortByName or frame.sortByName


  --Now we need to figure out which items are in this list
includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false
  local itemList = {}
includeDescription = includeDescription ~= nil and string.upper(includeDescription) == 'TRUE' or false
  for i, itemBase in pairs(ItemData.Items) do
sortByName = sortByName ~= nil and string.upper(sortByName) == 'TRUE' or false
    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 = item.defenceLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Armour')
      elseif type == "Ranged Armour" or type == "Ranged" then
        listItem = item.rangedLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
      elseif type == "Magic Armour" or type == "Magic" then
        listItem = item.magicLevelRequired ~= nil 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 item.equipmentSlot == slot end
    end
    if listItem then
      table.insert(itemList, item)
    end
  end


  return p._getEquipmentTable(itemList)
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
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]
includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false


  local itemList = {}
local itemList = {}
  local errMsg = 'ERROR: Some items not found in database: '
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)
return p._getEquipmentTable(itemList, includeModifiers)
  end
end
end
end


function p.getDoubleLootTable(frame)
function p.getDoubleLootTable(frame)
  local itemList = Items.getItems(function(item) return item.chanceToDoubleLoot ~= nil and item.chanceToDoubleLoot > 0 end)
local modsDL = {
  table.sort(itemList, function(a, b) return a.id < b.id end)
'increasedChanceToDoubleLootCombat',
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
'decreasedChanceToDoubleLootCombat',
  result = result..'\r\n!colspan="2"|Name!!Bonus!!Description!!Sources'
'increasedChanceToDoubleLootThieving',
  for i, item in Shared.skpairs(itemList) do
'decreasedChanceToDoubleLootThieving',
    result = result..'\r\n|-'
'increasedChanceToDoubleItemsGlobal',
    result = result..'\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
'decreasedChanceToDoubleItemsGlobal'
    result = result..'||[['..item.name..']]'
}
    result = result..'||style ="text-align: right;" data-sort-value="'..item.chanceToDoubleLoot..'"|'..item.chanceToDoubleLoot..'%'
local modDetail = {}
    result = result..'||'..item.description
for i, modName in pairs(modsDL) do
    result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
local mName, mText, mSign, mIsNeg, mValUnsigned = Constants.getModifierDetails(modName)
    result = result..ItemSourceTables._getItemSources(item)
modDetail[modName] = { mult = (mSign == "+" and 1 or -1) }
  end
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)


  result = result..'\r\n|}'
local resultPart = {}
  return result
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
end


function p.getItemTableRows(frame)
function p.getStatChangeString(item1, item2)
  local startID = frame.args ~= nil and frame.args[1] or frame[1]
--Used by getItemUpgradeTable to get the stat change between two items
  local rowCount = frame.args ~= nil and frame.args[2] or frame[2]
local changeArray = {}
  if type(startID ) == 'string' then startID = tonumber(startID) end
 
  if rowCount == nil then rowCount = 200 end
local getSpecificStatString = function(val1, val2, subStr)
  if type(rowCount) == 'string' then rowCount = tonumber(rowCount) end
if val1 == nil then val1 = 0 end
 
if val2 == nil then val2 = 0 end
  local rowResult = {}
 
  for i = startID, startID + rowCount - 1, 1 do
if val1 ~= val2 then
    local item = Items.getItemByID(i)
local txt = string.gsub(subStr, '{V}', Shared.numStrWithSign(val1 - val2))
    if item == nil then break end
table.insert(changeArray, txt)
    local rowTxt = '|-\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true})..'||[['..item.name..']]'
end
    rowTxt = rowTxt..'||style="text-align:right;"|'..i..'||'..item.category..'||'..item.type
end
    rowTxt = rowTxt..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
 
    rowTxt = rowTxt..'||style="text-align:right;"|'..ItemSourceTables._getItemSources(item, false, 'false')
--Unfortunately just gonna have to manually check all the changes I think...
    rowTxt = rowTxt..'||style="text-align:right;"|'..ItemUseTables._getItemUses(item, false, 'false')
local statList = {
    table.insert(rowResult, rowTxt)
-- {'statName', 'statDescription'}
  end
{'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


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


return p
return p

Revision as of 21:04, 22 February 2022

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

local p = {}

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;"|')
				local txtLines = {}
				local modTxt = Constants.getModifiersText(item.modifiers, true)
				if modTxt ~= '' then
					table.insert(txtLines, modTxt)
				end
				--For items with a special attack, show the details
				if item.hasSpecialAttack then
					table.insert(txtLines, "'''Special Attack:'''")
					for i, spAtt in ipairs(item.specialAttacks) do
						table.insert(txtLines, spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
						table.insert(txtLines, spAtt.description)
					end
				end
				table.insert(resultPart, table.concat(txtLines, '<br/>'))
			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;"|')
				local txtLines = {}
				local modTxt = Constants.getModifiersText(item.modifiers, true)
				if modTxt ~= '' then
					table.insert(txtLines, modTxt)
				end
				--For items with a special attack, show the details
				if item.hasSpecialAttack then
					table.insert(txtLines, "'''Special Attack:'''")
					for i, spAtt in ipairs(item.specialAttacks) do
						table.insert(txtLines, spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
						table.insert(txtLines, spAtt.description)
					end
				end
				table.insert(resultPart, table.concat(txtLines, '<br/>'))
			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._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
	if type(slot) == 'number' then
		slot = Constants.getEquipmentSlotName(slot)
	end

	local itemList = Items.getItems(function(item)
			-- Exclude Golbin raid exclusives for now, such that they don't
			-- pollute various equipment tables
			if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
				return false
			end
			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
					local isSkillcape = Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion'
					if other == 'Skillcapes' then
						isMatch = isSkillcape
					elseif other == 'No Skillcapes' then
						isMatch = not isSkillcape
					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