Module:Items/ComparisonTables: Difference between revisions

From Melvor Idle
m (Sort Double Loot table so like slayer gear stays together)
(Update for v1.3)
 
(88 intermediate revisions by 4 users not shown)
Line 1: Line 1:
local p = {}
local p = {}
local MonsterData = mw.loadData('Module:Monsters/data')
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 GameData = require('Module:GameData')
local Areas = require('Module:CombatAreas')
local Common = require('Module:Common')
local Modifiers = require('Module:Modifiers')
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 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", "Knight's Defender", "Ward of Flame Platebody"},
  --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', 'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
None = {},
NotMelee = {'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
NotRanged = {},
NotMagic = {'Torrential Blast Crossbow', 'Spectral Ice Sword', 'Lightning Strike 1H Sword', 'FrostSpark 1H Sword'}
}
 
local function getSlotID(slot)
-- If slot is a slot name, convert it to the slot ID instead
local slotID = Shared.getNamespacedID('melvorD', slot)
local slotData = GameData.getEntityByID('equipmentSlots', slotID)
-- Validate slotID
if slotData == nil then
-- slotID invalid, check if user provided a slot name
slotData = GameData.getEntityByProperty('equipmentSlots', 'emptyName', slot)
if slotData == nil then
return nil
end
slotID = slotData.id
end
return slotID
end
 
local function getItemDesc(item)
if item.customDescription ~= nil then
return item.customDescription
elseif item.modifiers ~= nil then
return Modifiers.getModifiersText(item.modifiers, false, false)
else
return ''
end
end
 
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', 'strengthLevelRequired', 'defenceLevelRequired',
'rangedLevelRequired', 'magicLevelRequired'
}


  if(Shared.tableCount(itemList) == 0) then
if Shared.tableIsEmpty(itemList) then
    return 'ERROR: you must select at least one item to get stats for'
return Shared.printError('You must select at least one item to get stats for')
  end
end


  local isWeaponType = Shared.contains(weaponTypes, itemList[1].type)
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)
--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 = 5
  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
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
  if isWeaponType then
    --Weapons have extra columns here for Attack Speed and "Two Handed?"
    result = result..'\r\n!colspan="4"|'
  else
    result = result..'\r\n!colspan="2"|'
  end
  if attBonusCols > 0 then
    result = result..'\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus'
  end
  if strBonusCols > 0 then
    result = result..'\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Str. Bonus'
  end
  if Shared.contains(statColumns, 'magicDamageBonus') then
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Damage Bonus'
  end
  if defBonusCols > 0 then
    result = result..'\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus'
  end
  if Shared.contains(statColumns, 'damageReduction') then
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|DR'
  end
  if lvlReqCols > 0 then
    result = result..'\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Lvl Req'
  end
  result = result..'\r\n!colspan="1"|'
  --One header row down, one to go
  result = result..'\r\n|-class="headerRow-1"'
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Item'
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Name'
  --Weapons have Attack Speed here
  if isWeaponType then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed'
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?'
  end
  --Attack bonuses
  if Shared.contains(statColumns, 'slashAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'stabAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'blockAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'rangedAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  --Strength bonuses
  if Shared.contains(statColumns, 'strengthBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'rangedStrengthBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicDamageBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  --Defence bonuses
  if Shared.contains(statColumns, 'defenceBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'rangedDefenceBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicDefenceBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'damageReduction') then
    result = result..'\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
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'defenceLevelRequired') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  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)
--Alright, let's start the table by building the shared header
  for i, item in pairs(itemList) do
local resultPart = {}
    if isWeaponType then
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
      --Building rows for weapons
if isWeaponType then
      result = result..'\r\n|-'
--Weapons have extra columns here for Attack Speed and "Two Handed?"
      result = result..'\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!colspan="4"|')
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
else
      result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.formatnum(item.attackSpeed)
table.insert(resultPart, '\r\n!colspan="2"|')
      --That's the first list out of the way, now for 2-Handed
end
      result = result..'\r\n| style ="text-align: right;"|'
if attBonusCols > 0 then
      if item.isTwoHanded then result = result..'Yes' else result = result..'No' end
table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"|Attack Bonus')
      for j, statName in pairs(statColumns) do
end
        local statValue = Items._getItemStat(item, statName, true)
if strBonusCols > 0 then
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"|Str. Bonus')
        if not Shared.contains(statName, 'LevelRequired') then
end
          if statValue > 0 then
if Shared.contains(statColumns, 'magicDamageBonus') then
            result = result..'background-color:lightgreen;'
table.insert(resultPart, '\r\n!colspan="1"|% Dmg Bonus')
          elseif statValue < 0 then
end
            result = result..'background-color:lightpink;'
if defBonusCols > 0 then
          end
table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"|Defence Bonus')
        end
end
        result = result..'"|'..Shared.formatnum(statValue)
if Shared.contains(statColumns, 'damageReduction') then
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
table.insert(resultPart, '\r\n!colspan="1"|DR')
      end
end
      --Finally, the Sources
if lvlReqCols > 0 then
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"|Lvl Req')
      result = result..ItemSourceTables._getItemSources(item)
end
    else
if includeModifiers and includeDescription then
      --Building rows for armour
table.insert(resultPart, '\r\n!colspan="2"|')
      result = result..'\r\n|-'
elseif includeModifiers or includeDescription then
      result = result..'\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!colspan="1"|')
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
end
      for j, statName in pairs(statColumns) do
--One header row down, one to go
        local statValue = Items._getItemStat(item, statName, true)
table.insert(resultPart, '\r\n|-class="headerRow-1"')
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
table.insert(resultPart, '\r\n!Item')
        if statValue > 0 then
table.insert(resultPart, '\r\n!Name')
          result = result..'background-color:lightgreen;'
--Weapons have Attack Speed here
        elseif statValue < 0 then
if isWeaponType then
          result = result..'background-color:lightpink;'
table.insert(resultPart, '\r\n!Attack Speed')
        end
table.insert(resultPart, '\r\n!Two Handed?')
        result = result..'"|'..Shared.formatnum(statValue)
end
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
--Attack bonuses
      end
if Shared.contains(statColumns, 'slashAttackBonus') then
      --Finally, the Sources
table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
end
      result = result..ItemSourceTables._getItemSources(item)
if Shared.contains(statColumns, 'stabAttackBonus') then
    end
table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
  end
end
if Shared.contains(statColumns, 'blockAttackBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'rangedAttackBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'magicAttackBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
end
--Strength bonuses
if Shared.contains(statColumns, 'meleeStrengthBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'rangedStrengthBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'magicDamageBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
end
--Defence bonuses
if Shared.contains(statColumns, 'meleeDefenceBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'rangedDefenceBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'magicDefenceBonus') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'damageReduction') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
--Level requirements
if Shared.contains(statColumns, 'attackLevelRequired') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'strengthLevelRequired') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'defenceLevelRequired') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'rangedLevelRequired') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'magicLevelRequired') then
table.insert(resultPart, '\r\n!'..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!Modifiers')
end
--If includeDescription is set to 'true', add the Description column
if includeDescription then
table.insert(resultPart, '\r\n!Description')
end


  result = result..'\r\n|}'
if sortByName then
  return result
table.sort(itemList, function(a, b) return a.name < b.name end)
end
for i, item in ipairs(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: centre;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style="text-align:right;" |'..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;" class="')
if string.find(statName, '^(.+)LevelRequired$') == nil then
if statValue > 0 then
table.insert(resultPart, 'table-positive')
elseif statValue < 0 then
table.insert(resultPart, 'table-negative')
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| ')
local txtLines = {}
if item.modifiers ~= nil then
table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
end
--For items with a special attack, show the details
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
table.insert(txtLines, "'''Special Attack:'''")
for i, spAttID in ipairs(item.specialAttacks) do
local spAtt = GameData.getEntityByID('attacks', spAttID)
local attChance = spAtt.defaultChance
if item.overrideSpecialChances ~= nil then
attChance = item.overrideSpecialChances[i]
end
table.insert(txtLines, attChance .. '% 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| ' .. getItemDesc(item))
end
else
--Building rows for armour
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|'..Icons.Icon({(item.name or 'Unknown'), type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. 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;" class="')
if statValue > 0 then
table.insert(resultPart, 'table-positive')
elseif statValue < 0 then
table.insert(resultPart, 'table-negative')
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| ')
local txtLines = {}
if item.modifiers ~= nil then
table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
end
--For items with a special attack, show the details
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
table.insert(txtLines, "'''Special Attack:'''")
for i, spAttID in ipairs(item.specialAttacks) do
local spAtt = GameData.getEntityByID('attacks', spAttID)
local attChance = spAtt.defaultChance
if item.overrideSpecialChances ~= nil then
attChance = item.overrideSpecialChances[i]
end
table.insert(txtLines, attChance .. '% 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| ' .. getItemDesc(item))
end
end
end
 
table.insert(resultPart, '\r\n|}')
 
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 slot is a slot name, convert it to the slot ID instead
  local type = args.type
local slotID = getSlotID(slot)
  local tier = args.tier
if slotID == nil then
  local slotStr = args.slot
return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
  local ammoTypeStr = args.ammoType
end
  local category = args.category ~= nil and args.category or 'Combat'
local slotNS, slotLocalID = Shared.getLocalID(slotID)
 
local itemList = Items.getItems(function(item)
-- Exclude the debug item
if item.id == 'melvorD:DEBUG_ITEM' then
return false
end
-- 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)) or Shared.contains(styleOverrides.NotMelee, item.name) then isMatch = false end
elseif style == 'Ranged' then
if (Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name)) or Shared.contains(styleOverrides.NotRanged, item.name)  then isMatch = false end
elseif style == 'Magic' then
if (Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name)) or Shared.contains(styleOverrides.NotMagic, 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
local sID = slotLocalID
if sID == nil or not Shared.contains(item.validSlots, sID) then isMatch = false end
 
if isMatch and other ~= nil then
if slot == 'Cape' then
-- TODO Would be more reliable if based on items appearing within the relevant shop categories instead
local isSkillcape = Shared.contains(item.name, 'Skillcape') or Shared.contains(item.name, 'Cape of Completion')
if other == 'Skillcapes' then
isMatch = isSkillcape
elseif other == 'No Skillcapes' then
isMatch = not isSkillcape
end
end
if slotLocalID == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
isMatch = other == item.ammoTypeRequired
elseif slotLocalID == 'Quiver' then
if other == 'Thrown' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
isMatch = true
else
isMatch = other == item.ammoType
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 pFrame = frame:getParent()
  local itemNames = Shared.splitString(stuffString, ',')
local frameArgs = pFrame.args ~= nil and pFrame.args or frame
local includeModifiers = frameArgs.includeModifiers ~= nil and string.upper(frameArgs.includeModifiers) == 'TRUE' or false


  local itemList = {}
local itemList, errItems = {}, {}
  local errMsg = 'ERROR: Some items not found in database: '
local errMsg = 'Some items not found in database: '
  local hasErr = false
local hasErr = false
  for i, name in Shared.skpairs(itemNames) do
for i, rawItemName in ipairs(frameArgs) do
    local nextItem = Items.getItem(Shared.trim(name))
local itemName = Shared.trim(rawItemName)
    if nextItem == nil then
local item = Items.getItem(itemName)
      errMsg = errMsg.." '"..name.."'"
if item == nil then
      hasErr = true
table.insert(errItems, "'" .. itemName .. "'")
    else
else
      table.insert(itemList, nextItem)
table.insert(itemList, item)
    end
end
  end
end


  if hasErr then
if not Shared.tableIsEmpty(errItems) then
    return errMsg
return Shared.printError('Some items not found in database: ' .. table.concat(errItems, ', '))
  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.itemID < b.itemID end)
["id"] = {
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
'melvorD:doubleItemsSkill',
  result = result..'\r\n!colspan="2"|Name!!Bonus!!Description!!Sources'
'melvorD:doubleItemsChanceAgainstDamageType'
  for i, item in Shared.skpairs(itemList) do
},
    result = result..'\r\n|-'
["alias"] = {
    result = result..'\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
'increasedChanceToDoubleLootCombat',
    result = result..'||[['..item.name..']]'
'decreasedChanceToDoubleLootCombat',
    result = result..'||style ="text-align: right;" data-sort-value="'..item.chanceToDoubleLoot..'"|'..item.chanceToDoubleLoot..'%'
'increasedChanceToDoubleItemsGlobal',
    result = result..'||'..item.description
'decreasedChanceToDoubleItemsGlobal'
    result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
}
    result = result..ItemSourceTables._getItemSources(item)
}
  end
local matchCriteria = Modifiers.getMatchCriteriaFromIDs(modsDL.id, modsDL.alias)
local itemMatchedMods = {}
local itemList = Items.getItems(
function(item)
if item.modifiers ~= nil then
local matchedMods = Modifiers.getMatchingModifiers(item.modifiers, matchCriteria)
if Shared.tableIsEmpty(matchedMods.matched) then
return false
else
itemMatchedMods[item.id] = matchedMods.matched
return true
end
end
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 = Modifiers.getModifierValue(itemMatchedMods[item.id])
 
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.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
table.insert(resultPart, '||' .. getItemDesc(item))
end
 
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
 
function p.getItemUpgradeTable(frame)
local args = frame.args ~= nil and frame.args or frame
local category, usedItemName = args[1], args.usedItem
local upgradeArray = {}
local isEquipment = false
local usedItemID = nil
if usedItemName ~= nil and usedItemName ~= '' then
local usedItem = Items.getItem(usedItemName)
if usedItem == nil then
return Shared.printError('Used item not found: ' .. usedItemName)
end
usedItemID = usedItem.id
end
local function upgradeConsumesItem(itemUpgrade, itemID)
if itemID == nil then
return true
end
for i, itemCost in ipairs(itemUpgrade.itemCosts) do
if itemCost.id == itemID then
return true
end
end
return false
end
 
if string.upper(category) == 'POTION' then
upgradeArray = GameData.getEntities('itemUpgrades',
function(upgrade)
return Shared.contains(upgrade.upgradedItemID, 'Potion') and upgradeConsumesItem(upgrade, usedItemID)
end
)
elseif string.upper(category) == 'OTHER' then
upgradeArray = GameData.getEntities('itemUpgrades',
function(upgrade)
if not Shared.contains(upgrade.upgradedItemID, 'Potion') and upgradeConsumesItem(upgrade, usedItemID) then
local item = Items.getItemByID(upgrade.upgradedItemID)
if item ~= nil then
return item.validSlots == nil or Shared.tableIsEmpty(item.validSlots)
end
end
return false
end
)
else
-- If category is a slot name, convert it to the slot ID instead
local slotID = getSlotID(category)
if slotID ~= nil then
local slotNS, slotLocalID = Shared.getLocalID(slotID)
category = slotLocalID
end
 
if category == nil then
return Shared.printError('Invalid option. Choose either an equipment slot, Potion, or Other')
end
isEquipment = true
upgradeArray = GameData.getEntities('itemUpgrades',
function(upgrade)
if upgradeConsumesItem(upgrade, usedItemID) then
local item = Items.getItemByID(upgrade.upgradedItemID)
if item ~= nil then
return item.validSlots ~= nil and Shared.contains(item.validSlots, category)
end
end
return false
end
)
end
 
local useStatChange = isEquipment or (string.upper(category) == 'POTION')
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 useStatChange then table.insert(resultPart, '!!Stat Change') end
 
for i, upgrade in ipairs(upgradeArray) do
local item = Items.getItemByID(upgrade.upgradedItemID)
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
table.insert(resultPart, '||' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
 
table.insert(resultPart, '|| ' .. Common.getCostString({ items = upgrade.itemCosts, gp = upgrade.gpCost, sc = upgrade.scCost}, 'None'))
 
if useStatChange then
-- Generate stat change column
local statChangeString = ''
if not Shared.tableIsEmpty(upgrade.rootItemIDs) then
-- Some items (e.g. FEZ) may have multiple root items. Simply use the first one
local rootItem = Items.getItemByID(upgrade.rootItemIDs[1])
if rootItem ~= nil then
statChangeString = Items.getStatChangeString(item, rootItem)
end
end
table.insert(resultPart, '|| '..statChangeString)
end
end
 
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
 
function p.getRuneProvidingItemTable(frame)
local itemArray = Items.getItems(function(item) return item.providedRunes ~= nil 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"|Item!!Runes Provided')
for i, item in pairs(itemArray) do
local PR = item.providedRunes
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="text-align: centre;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
local runeLines = {}
local sortVal = ''
for j, runePair in pairs(PR) do
local runeID = runePair.id
local qty = runePair.quantity
local rune = Items.getItemByID(runeID)
sortVal = sortVal..rune.name..qty
table.insert(runeLines, Icons.Icon({rune.name, type='item', qty=qty}))
end
table.insert(resultPart, '\r\n|data-sort-value="'..sortVal..'"|'..table.concat(runeLines, '<br/>'))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
 
function p._getDRTable(slots, items)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable" style="width:90%;"')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n! style="width:4%"|DR%')
for i, slot in pairs(slots) do
table.insert(resultPart, '\r\n! '..slot)
end
local DRTable = {}
table.sort(items, function(a, b) return a.name < b.name end)
for i, item in pairs(items) do
local DR = Items._getItemStat(item, 'damageReduction', true)
local EquipSlot = Items._getItemEquipSlot(item)
if DRTable[DR] == nil then
DRTable[DR] = {}
end
if DRTable[DR][EquipSlot] == nil then
DRTable[DR][EquipSlot] = {}
end
table.insert(DRTable[DR][EquipSlot], Icons.Icon({item.name, type='item', expicon = Icons.getExpansionIcon(item.id)}))
end
for DR, SlotTables in Shared.skpairs(DRTable) do
table.insert(resultPart, '\r\n|-\r\n|'..DR..'%')
for i, SlotName in pairs(slots) do
table.insert(resultPart, '\r\n|')
if SlotTables[SlotName] ~= nil then
table.insert(resultPart, table.concat(SlotTables[SlotName], '<br/>'))
end
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end


  result = result..'\r\n|}'
function p.getDRTable(frame)
  return result
local style = frame.args ~= nil and frame.args[1] or frame
local SlotNames = {}
local ItemList = {}
if style == 'Other' then
SlotNames = {'Helmet', 'Platelegs', 'Gloves', 'Shield', 'Cape', 'Amulet', 'Ring'}
else
SlotNames = {'Helmet', 'Platebody', 'Platelegs', 'Boots', 'Gloves', 'Weapon', 'Shield'}
end
ItemList =  Items.getItems(function(item)
local isMatch = true
if Items._getItemStat(item, 'damageReduction', true) <= 0 then
return false
end
-- 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
--Using the same checks for Melee/Ranged/Magic that the Equipment Tables use
if style == 'Melee' then
if ((Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name)) or Shared.contains(styleOverrides.NotMelee, item.name) then isMatch = false end
elseif style == 'Ranged' then
if (Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name)) or Shared.contains(styleOverrides.NotRanged, item.name)  then isMatch = false end
elseif style == 'Magic' then
if (Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name)) or Shared.contains(styleOverrides.NotMagic, item.name) then isMatch = false end
else
if (Items._getItemStat(item, 'attackLevelRequired') ~= nil or 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 isMatch and not Shared.contains(SlotNames, Items._getItemEquipSlot(item)) then
isMatch = false
end
return isMatch
end)
return p._getDRTable(SlotNames, ItemList)
end
end


return p
return p

Latest revision as of 17:55, 18 June 2024

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

local p = {}

local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local Common = require('Module:Common')
local Modifiers = require('Module:Modifiers')
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's Rake", "Knight's Defender", "Ward of Flame Platebody"},
	Ranged = {'Slayer Cowl (Basic)', 'Slayer Leather Body (Basic)', 'Ice Arrows'},
	Magic = {'Slayer Wizard Hat (Basic)', 'Slayer Wizard Robes (Basic)', 'Enchanted Shield', 'Elementalist Gloves', 'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
	None = {},
	NotMelee = {'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
	NotRanged = {},
	NotMagic = {'Torrential Blast Crossbow', 'Spectral Ice Sword', 'Lightning Strike 1H Sword', 'FrostSpark 1H Sword'}
}

local function getSlotID(slot)
	-- If slot is a slot name, convert it to the slot ID instead
	local slotID = Shared.getNamespacedID('melvorD', slot)
	local slotData = GameData.getEntityByID('equipmentSlots', slotID)
	-- Validate slotID
	if slotData == nil then
		-- slotID invalid, check if user provided a slot name
		slotData = GameData.getEntityByProperty('equipmentSlots', 'emptyName', slot)
		if slotData == nil then
			return nil
		end
		slotID = slotData.id
	end
	return slotID
end

local function getItemDesc(item)
	if item.customDescription ~= nil then
		return item.customDescription
	elseif item.modifiers ~= nil then
		return Modifiers.getModifiersText(item.modifiers, false, false)
	else
		return ''
	end
end

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', 'strengthLevelRequired', 'defenceLevelRequired',
		'rangedLevelRequired', 'magicLevelRequired'
	}

	if Shared.tableIsEmpty(itemList) then
		return Shared.printError('You must select at least one item to get stats for')
	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 = 5
	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..'"|Attack Bonus')
	end
	if strBonusCols > 0 then
		table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"|Str. Bonus')
	end
	if Shared.contains(statColumns, 'magicDamageBonus') then
		table.insert(resultPart, '\r\n!colspan="1"|% Dmg Bonus')
	end
	if defBonusCols > 0 then
		table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"|Defence Bonus')
	end
	if Shared.contains(statColumns, 'damageReduction') then
		table.insert(resultPart, '\r\n!colspan="1"|DR')
	end
	if lvlReqCols > 0 then
		table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"|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!Item')
	table.insert(resultPart, '\r\n!Name')
	--Weapons have Attack Speed here
	if isWeaponType then
		table.insert(resultPart, '\r\n!Attack Speed')
		table.insert(resultPart, '\r\n!Two Handed?')
	end
	--Attack bonuses
	if Shared.contains(statColumns, 'slashAttackBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'stabAttackBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'blockAttackBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedAttackBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicAttackBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
	end
	--Strength bonuses
	if Shared.contains(statColumns, 'meleeStrengthBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedStrengthBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicDamageBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
	end
	--Defence bonuses
	if Shared.contains(statColumns, 'meleeDefenceBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedDefenceBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicDefenceBonus') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'damageReduction') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	--Level requirements
	if Shared.contains(statColumns, 'attackLevelRequired') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'strengthLevelRequired') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'defenceLevelRequired') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'rangedLevelRequired') then
		table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
	end
	if Shared.contains(statColumns, 'magicLevelRequired') then
		table.insert(resultPart, '\r\n!'..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!Modifiers')
	end
	--If includeDescription is set to 'true', add the Description column
	if includeDescription then
		table.insert(resultPart, '\r\n!Description')
	end

	if sortByName then
		table.sort(itemList, function(a, b) return a.name < b.name end)
	end
	for i, item in ipairs(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: centre;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
			table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
			table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style="text-align:right;" |'..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;" class="')
				if string.find(statName, '^(.+)LevelRequired$') == nil then
					if statValue > 0 then
						table.insert(resultPart, 'table-positive')
					elseif statValue < 0 then
						table.insert(resultPart, 'table-negative')
					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| ')
				local txtLines = {}
				if item.modifiers ~= nil then
					table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
				end
				--For items with a special attack, show the details
				if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
					table.insert(txtLines, "'''Special Attack:'''")
					for i, spAttID in ipairs(item.specialAttacks) do
						local spAtt = GameData.getEntityByID('attacks', spAttID)
						local attChance = spAtt.defaultChance
						if item.overrideSpecialChances ~= nil then
							attChance = item.overrideSpecialChances[i]
						end
						table.insert(txtLines, attChance .. '% 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| ' .. getItemDesc(item))
			end
		else
			--Building rows for armour
			table.insert(resultPart, '\r\n|-')
			table.insert(resultPart, '\r\n|'..Icons.Icon({(item.name or 'Unknown'), type='item', size=50, notext=true}))
			table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. 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;" class="')
				if statValue > 0 then
					table.insert(resultPart, 'table-positive')
				elseif statValue < 0 then
					table.insert(resultPart, 'table-negative')
				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| ')
				local txtLines = {}
				if item.modifiers ~= nil then
					table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
				end
				--For items with a special attack, show the details
				if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
					table.insert(txtLines, "'''Special Attack:'''")
					for i, spAttID in ipairs(item.specialAttacks) do
						local spAtt = GameData.getEntityByID('attacks', spAttID)
						local attChance = spAtt.defaultChance
						if item.overrideSpecialChances ~= nil then
							attChance = item.overrideSpecialChances[i]
						end
						table.insert(txtLines, attChance .. '% 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| ' .. getItemDesc(item))
			end
		end
	end

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

	return table.concat(resultPart)
end

function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
	-- If slot is a slot name, convert it to the slot ID instead
	local slotID = getSlotID(slot)
	if slotID == nil then
		return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
	end
	local slotNS, slotLocalID = Shared.getLocalID(slotID)

	local itemList = Items.getItems(function(item)
			-- Exclude the debug item
			if item.id == 'melvorD:DEBUG_ITEM' then
				return false
			end
			-- 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)) or Shared.contains(styleOverrides.NotMelee, item.name) then isMatch = false end
			elseif style == 'Ranged' then
				if (Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name)) or Shared.contains(styleOverrides.NotRanged, item.name)  then isMatch = false end
			elseif style == 'Magic' then
				if (Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name)) or Shared.contains(styleOverrides.NotMagic, 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
			local sID = slotLocalID
			if sID == nil or not Shared.contains(item.validSlots, sID) then isMatch = false end

			if isMatch and other ~= nil then
				if slot == 'Cape' then
					-- TODO Would be more reliable if based on items appearing within the relevant shop categories instead
					local isSkillcape = Shared.contains(item.name, 'Skillcape') or Shared.contains(item.name, 'Cape of Completion')
					if other == 'Skillcapes' then
						isMatch = isSkillcape
					elseif other == 'No Skillcapes' then
						isMatch = not isSkillcape
					end
				end
				if slotLocalID == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
					isMatch = other == item.ammoTypeRequired
				elseif slotLocalID == 'Quiver' then
					if other == 'Thrown' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
						isMatch = true
					else
						isMatch = other == item.ammoType
					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 pFrame = frame:getParent()
	local frameArgs = pFrame.args ~= nil and pFrame.args or frame
	local includeModifiers = frameArgs.includeModifiers ~= nil and string.upper(frameArgs.includeModifiers) == 'TRUE' or false

	local itemList, errItems = {}, {}
	local errMsg = 'Some items not found in database: '
	local hasErr = false
	for i, rawItemName in ipairs(frameArgs) do
		local itemName = Shared.trim(rawItemName)
		local item = Items.getItem(itemName)
		if item == nil then
			table.insert(errItems, "'" .. itemName .. "'")
		else
			table.insert(itemList, item)
		end
	end

	if not Shared.tableIsEmpty(errItems) then
		return Shared.printError('Some items not found in database: ' .. table.concat(errItems, ', '))
	else
		return p._getEquipmentTable(itemList, includeModifiers)
	end
end

function p.getDoubleLootTable(frame)
	local modsDL = {
		["id"] = {
			'melvorD:doubleItemsSkill',
			'melvorD:doubleItemsChanceAgainstDamageType'
		},
		["alias"] = {
			'increasedChanceToDoubleLootCombat',
			'decreasedChanceToDoubleLootCombat',
			'increasedChanceToDoubleItemsGlobal',
			'decreasedChanceToDoubleItemsGlobal'
		}
	}
	local matchCriteria = Modifiers.getMatchCriteriaFromIDs(modsDL.id, modsDL.alias)
	local itemMatchedMods = {}
	local itemList = Items.getItems(
		function(item)
			if item.modifiers ~= nil then
				local matchedMods = Modifiers.getMatchingModifiers(item.modifiers, matchCriteria)
				if Shared.tableIsEmpty(matchedMods.matched) then
					return false
				else
					itemMatchedMods[item.id] = matchedMods.matched
					return true
				end
			end
		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 = Modifiers.getModifierValue(itemMatchedMods[item.id])

		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.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
		table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
		table.insert(resultPart, '||' .. getItemDesc(item))
	end

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

function p.getItemUpgradeTable(frame)
	local args = frame.args ~= nil and frame.args or frame
	local category, usedItemName = args[1], args.usedItem
	local upgradeArray = {}
	local isEquipment = false
	
	local usedItemID = nil
	if usedItemName ~= nil and usedItemName ~= '' then
		local usedItem = Items.getItem(usedItemName)
		if usedItem == nil then
			return Shared.printError('Used item not found: ' .. usedItemName)
		end
		usedItemID = usedItem.id
	end
	
	local function upgradeConsumesItem(itemUpgrade, itemID)
		if itemID == nil then
			return true
		end	
		for i, itemCost in ipairs(itemUpgrade.itemCosts) do
			if itemCost.id == itemID then
				return true
			end
		end
		return false
	end

	if string.upper(category) == 'POTION' then
		upgradeArray = GameData.getEntities('itemUpgrades',
			function(upgrade)
				return Shared.contains(upgrade.upgradedItemID, 'Potion') and upgradeConsumesItem(upgrade, usedItemID)
			end
			)
	elseif string.upper(category) == 'OTHER' then
		upgradeArray = GameData.getEntities('itemUpgrades',
			function(upgrade)
				if not Shared.contains(upgrade.upgradedItemID, 'Potion') and upgradeConsumesItem(upgrade, usedItemID) then
					local item = Items.getItemByID(upgrade.upgradedItemID)
					if item ~= nil then
						return item.validSlots == nil or Shared.tableIsEmpty(item.validSlots)
					end
				end
				return false
			end
			)
	else
		-- If category is a slot name, convert it to the slot ID instead
		local slotID = getSlotID(category)
		if slotID ~= nil then
			local slotNS, slotLocalID = Shared.getLocalID(slotID)
			category = slotLocalID
		end

		if category == nil then
			return Shared.printError('Invalid option. Choose either an equipment slot, Potion, or Other')
		end
		isEquipment = true
		upgradeArray = GameData.getEntities('itemUpgrades',
			function(upgrade)
				if upgradeConsumesItem(upgrade, usedItemID) then
					local item = Items.getItemByID(upgrade.upgradedItemID)
					if item ~= nil then
						return item.validSlots ~= nil and Shared.contains(item.validSlots, category)
					end
				end
				return false
			end
			)
	end

	local useStatChange = isEquipment or (string.upper(category) == 'POTION')
	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 useStatChange then table.insert(resultPart, '!!Stat Change') end

	for i, upgrade in ipairs(upgradeArray) do
		local item = Items.getItemByID(upgrade.upgradedItemID)
		table.insert(resultPart, '\r\n|-')
		table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
		table.insert(resultPart, '||' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))

		table.insert(resultPart, '|| ' .. Common.getCostString({ items = upgrade.itemCosts, gp = upgrade.gpCost, sc = upgrade.scCost}, 'None'))

		if useStatChange then
			-- Generate stat change column
			local statChangeString = ''
			if not Shared.tableIsEmpty(upgrade.rootItemIDs) then
				-- Some items (e.g. FEZ) may have multiple root items. Simply use the first one
				local rootItem = Items.getItemByID(upgrade.rootItemIDs[1])
				if rootItem ~= nil then
					statChangeString = Items.getStatChangeString(item, rootItem)
				end
			end
			table.insert(resultPart, '|| '..statChangeString)
		end
	end

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

function p.getRuneProvidingItemTable(frame)
	local itemArray = Items.getItems(function(item) return item.providedRunes ~= nil 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"|Item!!Runes Provided')
	
	for i, item in pairs(itemArray) do
		local PR = item.providedRunes
		table.insert(resultPart, '\r\n|-')
		table.insert(resultPart, '\r\n|style="text-align: centre;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
		table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
		local runeLines = {}
		local sortVal = ''
		for j, runePair in pairs(PR) do
			local runeID = runePair.id
			local qty = runePair.quantity
			local rune = Items.getItemByID(runeID)
			sortVal = sortVal..rune.name..qty
			table.insert(runeLines, Icons.Icon({rune.name, type='item', qty=qty}))
		end
		table.insert(resultPart, '\r\n|data-sort-value="'..sortVal..'"|'..table.concat(runeLines, '<br/>'))
	end
	
	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

function p._getDRTable(slots, items)
	local resultPart = {}
	
	table.insert(resultPart, '{| class="wikitable sortable" style="width:90%;"')
	table.insert(resultPart, '\r\n|-')
	table.insert(resultPart, '\r\n! style="width:4%"|DR%')
	for i, slot in pairs(slots) do
		table.insert(resultPart, '\r\n! '..slot)
	end
	
	local DRTable = {}
	table.sort(items, function(a, b) return a.name < b.name end)
	
	for i, item in pairs(items) do
		local DR = Items._getItemStat(item, 'damageReduction', true)
		local EquipSlot = Items._getItemEquipSlot(item)
		if DRTable[DR] == nil then
			DRTable[DR] = {}
		end
		if DRTable[DR][EquipSlot] == nil then
			DRTable[DR][EquipSlot] = {}
		end
		table.insert(DRTable[DR][EquipSlot], Icons.Icon({item.name, type='item', expicon = Icons.getExpansionIcon(item.id)}))
	end
	
	for DR, SlotTables in Shared.skpairs(DRTable) do
		table.insert(resultPart, '\r\n|-\r\n|'..DR..'%')
		for i, SlotName in pairs(slots) do
			table.insert(resultPart, '\r\n|')
			if SlotTables[SlotName] ~= nil then
				table.insert(resultPart, table.concat(SlotTables[SlotName], '<br/>'))
			end
		end
	end
	
	table.insert(resultPart, '\r\n|}')
	
	return table.concat(resultPart)
end

function p.getDRTable(frame)
	local style = frame.args ~= nil and frame.args[1] or frame
	local SlotNames = {}
	local ItemList = {}
	if style == 'Other' then
		SlotNames = {'Helmet', 'Platelegs', 'Gloves', 'Shield', 'Cape', 'Amulet', 'Ring'}
	else
		SlotNames = {'Helmet', 'Platebody', 'Platelegs', 'Boots', 'Gloves', 'Weapon', 'Shield'}
	end
	
	ItemList =  Items.getItems(function(item)
					local isMatch = true
					if Items._getItemStat(item, 'damageReduction', true) <= 0 then
						return false
					end
					-- 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
					--Using the same checks for Melee/Ranged/Magic that the Equipment Tables use
					if style == 'Melee' then
						if ((Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name)) or Shared.contains(styleOverrides.NotMelee, item.name) then isMatch = false end
					elseif style == 'Ranged' then
						if (Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name)) or Shared.contains(styleOverrides.NotRanged, item.name)  then isMatch = false end
					elseif style == 'Magic' then
						if (Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name)) or Shared.contains(styleOverrides.NotMagic, item.name) then isMatch = false end
					else
						if (Items._getItemStat(item, 'attackLevelRequired') ~= nil or 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 isMatch and not Shared.contains(SlotNames, Items._getItemEquipSlot(item)) then
						isMatch = false
					end
					return isMatch
				end)
	
	return p._getDRTable(SlotNames, ItemList)
end

return p