Module:Items: Difference between revisions

From Melvor Idle
(Added p.getItemModifier and a related p.getItemStat modifier so that pulling slayerBonusXP properly pulls the new modifier)
(Added a check for level requirements to the 'hasCombatStats' check for autoarmourstats just to be safe)
(30 intermediate revisions by 4 users not shown)
Line 6: Line 6:
local p = {}
local p = {}


local MonsterData = mw.loadData('Module:Monsters/data')
local ItemData = mw.loadData('Module:Items/data')
local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')


local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
Line 14: Line 12:
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')


p.EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon'}
p.EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon', 'Easter Egg', 'Abnormal Log', 'Red Herring', 'Cool Glasses'}
p.EventItems = {'Christmas Cracker', 'Christmas Coal', 'Christmas Sweater',
'Christmas Wreath', 'Candy Cane', 'Santa Hat',
'Friendship Bracelet', 'Event Clue 1', 'Event Clue 2',
'Event Clue 3', 'Event Clue 4', 'Candle', 'Cake Base',
'Magical Flavouring', 'Magical Icing', 'Birthday Cake',
'Purple Party Hat', 'Birthday Token', 'Christmas Present (Yellow)',
'Christmas Present (Blue)', 'Christmas Present (Green)', 'Christmas Present (White)',
'Christmas Present (Purple)', 'Christmas Present (Standard)', 'Event Token - Holiday 2021',
'Holiday Scarf', 'Gingerbread House', 'Gingerbread Man', 'Edible Candy Cane',
'Locked Chest', 'Locked Chest Key', 'Event Token (Holiday 2021)'}
p.OtherShopItems = {'Cooking Gloves', 'Mining Gloves', 'Gem Gloves', 'Smithing Gloves', 'Thieving Gloves'}
p.OtherShopItems = {'Cooking Gloves', 'Mining Gloves', 'Gem Gloves', 'Smithing Gloves', 'Thieving Gloves'}
--This is hardcoded, so there's no easy way to scrape it. Hopefully it doesn't change
--This is hardcoded, so there's no easy way to scrape it. Hopefully it doesn't change
p.GemTable = {["Topaz"] = {name = 'Topaz', id = 128, chance = 50},  
p.GemTable = {["Topaz"] = {name = 'Topaz', id = 128, chance = 50},
                  ["Sapphire"] = {name = "Sapphire", id = 129, chance = 17.5},  
["Sapphire"] = {name = "Sapphire", id = 129, chance = 17.5},
                  ["Ruby"] = {name = "Ruby", id = 130, chance = 17.5},  
["Ruby"] = {name = "Ruby", id = 130, chance = 17.5},
                  ["Emerald"] = {name = "Emerald", id = 131, chance = 10},  
["Emerald"] = {name = "Emerald", id = 131, chance = 10},
                  ["Diamond"] = {name = "Diamond", id = 132, chance = 5}}
["Diamond"] = {name = "Diamond", id = 132, chance = 5}}
--The base chance to receive a gem while mining
--The base chance to receive a gem while mining
p.GemChance = .01
p.GemChance = .01
Line 27: Line 35:
p.junkCount = 8
p.junkCount = 8
--Items (aside from bars & gems) which can be created via Alt Magic
--Items (aside from bars & gems) which can be created via Alt Magic
local AltMagicProducts = {'Rune Essence', 'Bones', 'Holy Dust'}
p.AltMagicProducts = {'Rune Essence', 'Bones', 'Holy Dust'}
--The kinds of gloves with cost & charges
--The kinds of gloves with cost & charges
p.GloveTable = {['Cooking Gloves'] = {cost=50000, charges=500},
p.GloveTable = {['Cooking Gloves'] = {cost=50000, charges=500},
                    ['Mining Gloves'] = {cost=75000, charges=500},
['Mining Gloves'] = {cost=75000, charges=500},
                    ['Smithing Gloves'] = {cost=100000, charges=500},
['Smithing Gloves'] = {cost=100000, charges=500},
                    ['Thieving Gloves'] = {cost=100000, charges=500},
['Thieving Gloves'] = {cost=100000, charges=500},
                    ['Gem Gloves'] = {cost=500000, charges=2000}}
['Gem Gloves'] = {cost=500000, charges=2000}}




Line 40: Line 48:


function p.buildSpecialFishingTable()
function p.buildSpecialFishingTable()
  --This shouldn't ever be included in a page
--This shouldn't ever be included in a page
  --This is for generating the above 'specialFishLoot' variable if it ever needs to change
--This is for generating the above 'specialFishLoot' variable if it ever needs to change
  --To re-run, edit the module, type in "console.log(p.buildSpecialFishingTable())" and copy+paste the result as the new value of the variable
--To re-run, edit the module, type in "console.log(p.buildSpecialFishingTable())" and copy+paste the result as the new value of the variable
  --Also gives you the total fishing weight for saving time later
--Also gives you the total fishing weight for saving time later
  local lootArray = {}
local lootArray = {}
  local totalWt = 0
local totalWt = 0


  for i, item in pairs(ItemData.Items) do
for i, item in pairs(ItemData.Items) do
    if item.fishingCatchWeight ~= nil then
if item.fishingCatchWeight ~= nil then
      totalWt = totalWt + item.fishingCatchWeight
totalWt = totalWt + item.fishingCatchWeight
      table.insert(lootArray, '{'..(i - 1)..', '..item.fishingCatchWeight..'}')
table.insert(lootArray, '{'..(i - 1)..', '..item.fishingCatchWeight..'}')
    end
end
  end
end


  local result = 'p.specialFishWt = '..totalWt..'\r\n'
local result = 'p.specialFishWt = '..totalWt..'\r\n'
  result = result..'p.specialFishLoot = {'..table.concat(lootArray, ', ')..'}'
result = result..'p.specialFishLoot = {'..table.concat(lootArray, ', ')..'}'
  return result
return result
end
 
function p.getSpecialAttackByID(ID)
  local result = Shared.clone(ItemData.SpecialAttacks[ID + 1])
  if result ~= nil then
    result.id = ID
  end
  return result
end
end


function p.getItemByID(ID)
function p.getItemByID(ID)
  local result = Shared.clone(ItemData.Items[ID + 1])
local result = Shared.clone(ItemData.Items[ID + 1])
  if result ~= nil then
if result ~= nil then
    result.id = ID
result.id = ID
  end
end
  return result
return result
end
end


function p.getItem(name)
function p.getItem(name)
  local result = nil
local result = nil
  name = string.gsub(name, "%%27", "'")
name = string.gsub(name, "%%27", "'")
  name = string.gsub(name, "'", "'")
name = string.gsub(name, "'", "'")
  name = string.gsub(name, "'", "'")
for i, item in pairs(ItemData.Items) do
  for i, item in pairs(ItemData.Items) do
local itemName = string.gsub(item.name, '#', '')
    local itemName = string.gsub(item.name, '#', '')
if name == itemName then
    if(name == itemName) then
result = Shared.clone(item)
      result = Shared.clone(item)
--Make sure every item has an id, and account for Lua being 1-index
      --Make sure every item has an id, and account for Lua being 1-index
result.id = i - 1
      result.id = i - 1
break
      break
end
    end
end
  end
return result
  return result
end
end


function p.getItems(checkFunc)
function p.getItems(checkFunc)
  local result = {}
local result = {}
  for i, item in pairs(ItemData.Items) do
for i, item in pairs(ItemData.Items) do
    if checkFunc(item) then
if checkFunc(item) then
      local newItem = Shared.clone(item)
local newItem = Shared.clone(item)
      newItem.id = i - 1
newItem.id = i - 1
      table.insert(result, newItem)
table.insert(result, newItem)
    end
end
  end
end
  return result
return result
end
end


function p._getItemStat(item, StatName, ZeroIfNil)
function p._getItemStat(item, StatName, ZeroIfNil)
  local result = item[StatName]
local result = item[StatName]
  --Special Overrides:
--Special Overrides:
  if StatName == 'stabAttackBonus' then
-- Equipment stats first
    if item.attackBonus == nil then  
if Shared.contains(ItemData.EquipmentStatKeys, StatName) and item.equipmentStats ~= nil then
      result = nil
result = item.equipmentStats[StatName]
    else
elseif StatName == 'isTwoHanded' then
      result = item.attackBonus[1]
if item.validSlots ~= nil and item.occupiesSlots ~= nil then
    end
result = Shared.contains(item.validSlots, 'Weapon') and Shared.contains(item.occupiesSlots, 'Shield')
  elseif StatName == 'slashAttackBonus' then
else
    if item.attackBonus == nil then  
result = false
      result = nil
end
    else
elseif string.find(StatName, '^(.+)LevelRequired$') ~= nil and item.equipRequirements ~= nil and item.equipRequirements.Level ~= nil then
      result = item.attackBonus[2]
local skillName = Shared.titleCase(string.match(StatName, '^(.+)LevelRequired$'))
    end
if skillName ~= nil then
  elseif StatName == 'blockAttackBonus' then
local skillID = Constants.getSkillID(skillName)
    if item.attackBonus == nil then  
if skillID ~= nil then
      result = nil
result = item.equipRequirements.Level[skillID]
    else
end
      result = item.attackBonus[3]
end
    end
elseif StatName == 'attackType' then
  elseif StatName == 'attackType' then
result = p._getWeaponAttackType(item)
    result = p._getWeaponAttackType(item)
elseif StatName == 'description' then
  elseif StatName == 'description' then
result = item.description
    result = item.description
if result == nil or result == '' then result = 'No Description' end
    if result == nil or result == '' then result = 'No Description' end
elseif StatName == 'completionReq' then
  elseif StatName == 'completionReq' then
if item.ignoreCompletion == nil or not item.ignoreCompletion then
    if item.ignoreCompletion == nil or not item.ignoreCompletion then
result = 'Yes'
      return 'Yes'
else
    else
result = 'No'
      return 'No'
end
    end
elseif StatName == 'slayerBonusXP' then
  elseif StatName == 'slayerBonusXP' then
return p._getItemModifier(item, 'increasedSkillXP', 'Slayer', false)
    return p.getItemModifier(item, 'increasedSkillXP', 'Slayer', false)
elseif StatName == 'hasCombatStats' then
  end
return tostring(p.hasCombatStats(item) or p._hasLevelRequirements(item))
  if result == nil and ZeroIfNil then result = 0 end
end
  return result
if result == nil and ZeroIfNil then result = 0 end
return result
end
end


function p.getItemStat(frame)
function p.getItemStat(frame)
  local args = frame.args ~= nil and frame.args or frame
local args = frame.args ~= nil and frame.args or frame
  local ItemName = args[1]
local ItemName = args[1]
  local StatName = args[2]
local StatName = args[2]
  local ZeroIfNil = args.ForceZero ~= nil and args.ForceZero ~= '' and args.ForceZero ~= 'false'
local ZeroIfNil = args.ForceZero ~= nil and args.ForceZero ~= '' and args.ForceZero ~= 'false'
  local formatNum = args.formatNum ~= nil and args.formatNum ~= '' and args.formatNum ~= 'false'
local formatNum = args.formatNum ~= nil and args.formatNum ~= '' and args.formatNum ~= 'false'
  local item = p.getItem(ItemName)
local item = p.getItem(ItemName)
  if item == nil then
if item == nil then
    return "ERROR: No item named "..ItemName.." exists in the data module[[Category:Pages with script errors]]"
return "ERROR: No item named "..ItemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end
  local result = p._getItemStat(item, StatName, ZeroIfNil)
local result = p._getItemStat(item, StatName, ZeroIfNil)
  if formatNum then result = Shared.formatnum(result) end
if formatNum then result = Shared.formatnum(result) end
  return result
return result
end
end


Line 161: Line 161:
--asString is false by default, when true it writes the full bonus text
--asString is false by default, when true it writes the full bonus text
function p._getItemModifier(item, modifier, skill, asString)
function p._getItemModifier(item, modifier, skill, asString)
  if asString == nil then asString = false end
if asString == nil then asString = false end
  if skill == '' then
if skill == '' then
    skill = nil
skill = nil
  elseif type(skill) == 'string' then
elseif type(skill) == 'string' then
    skill = Constants.getSkillID(skill)
skill = Constants.getSkillID(skill)
  end
end


  mw.log(item.name..', '..modifier)
local result = 0


  local result = 0
if item.modifiers ~= nil and item.modifiers[modifier] ~= nil then
if type(item.modifiers[modifier]) == 'table' then
for i, subVal in Shared.skpairs(item.modifiers[modifier]) do
if subVal[1] == skill then
result = subVal[2]
break
end
end
else
result = item.modifiers[modifier]
end
end


  if item.modifiers ~= nil and item.modifiers[modifier] ~= nil then
if asString then
    if type(item.modifiers[modifier]) == 'table' then
if skill ~= nil then
      for i, subVal in Shared.skpairs(item.modifiers[modifier]) do
return Constants._getModifierText(modifier, {skill, result})
        if subVal[1] == skill then
else
          result = subVal[2]
return Constants._getModifierText(modifier, result)
          break
end
        end
else
      end
return result
    else
end
      result = item.modifiers[modifier]
end
    end
  end


  if asString then
function p.hasCombatStats(item)
    if skill ~= nil then
if item.isEquipment or (item.validSlots == nil and item.equipmentStats ~= nil) then
      return Constants.getModifierText(modifier, {skill, result})
-- Ensure at least one stat has a non-zero value
    else
for statName, statVal in pairs(item.equipmentStats) do
      return Constants.getModifierText(modifier, result)
if statVal ~= 0 then return true end
    end
end
  else
end
    return result
return false
  end
end
 
function p._hasLevelRequirements(item)
--Function true if an item has at least one level requirement to equip
if item.equipRequirements ~= nil and item.equipRequirements.Level ~= nil then
for skillID, lvl in pairs(item.equipRequirements.Level) do
if lvl ~= nil and lvl > 1 then
return true
end
end
return false
else
return false
end
end
end


function p.getItemModifier(frame)
function p.getItemModifier(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame[1]
local itemName = frame.args ~= nil and frame.args[1] or frame[1]
  local modName = frame.args ~= nil and frame.args[2] or frame[2]
local modName = frame.args ~= nil and frame.args[2] or frame[2]
  local skillName = frame.args ~= nil and frame.args[3] or frame[3]
local skillName = frame.args ~= nil and frame.args[3] or frame[3]
  local asString = frame.args ~= nil and frame.args[4] or frame[4]
local asString = frame.args ~= nil and frame.args[4] or frame[4]
  if asString ~= nil then
if asString ~= nil then
    if strupper(asString) == 'FALSE' then
asString = (string.upper(asString) ~= 'FALSE')
    asString = false
end
    else
    asString = true
    end
  end


  local item = p.getItem(itemName)
local item = p.getItem(itemName)
  if item == nil then
if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getItemModifier(item, modName, skillName, asString)
return p._getItemModifier(item, modName, skillName, asString)
end
end


function p._getWeaponAttackType(item)
function p._getWeaponAttackType(item)
  if item.type == 'Weapon' then
if item.isEquipment == true and (item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon')) or
    return Icons.Icon({'Melee', nolink='true'})
(item.occupiesSlots ~= nil and Shared.contains(item.occupiesSlots, 'Weapon')) then
  elseif item.type == 'Ranged Weapon' then
if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then
    return Icons.Icon({'Ranged', type='skill', nolink='true'})
local iconType = item.attackType ~= 'melee' and 'skill' or nil
  elseif item.type == 'Magic Staff' or item.type == 'Magic Wand' then
return Icons.Icon({Shared.titleCase(item.attackType), type=iconType, nolink='true'})
    return Icons.Icon({'Magic', type='skill', nolink='true'})
end
  else
end
    return "Invalid"
return 'Invalid'
  end
end
end


function p.getWeaponAttackType(frame)
function p.getWeaponAttackType(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
local itemName = frame.args ~= nil and frame.args[1] or frame
  local item = p.getItem(itemName)
local item = p.getItem(itemName)
  if item == nil then
if item == nil then
    return "ERROR: No item named "..ItemName.." exists in the data module[[Category:Pages with script errors]]"
return "ERROR: No item named "..ItemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end
  return p._getWeaponAttackType(item)
return p._getWeaponAttackType(item)
end
end


function p.getPotionTable(frame)
function p.getPotionTable(frame)
  local potionName = frame.args ~= nil and frame.args[1] or frame
local potionName = frame.args ~= nil and frame.args[1] or frame
  local tiers = {'I', 'II', 'III', 'IV'}
local tiers = {'I', 'II', 'III', 'IV'}


  local result = '{| class="wikitable"'
local resultPart = {}
  result = result..'\r\n!Potion!!Tier!!Charges!!Effect'
table.insert(resultPart, '{| class="wikitable"')
table.insert(resultPart, '\r\n!Potion!!Tier!!Charges!!Effect')


  local tier1potion = p.getItem(potionName..' I')
local tier1potion = p.getItem(potionName..' I')
  for i, tier in pairs(tiers) do
if tier1potion == nil then
    local tierName = potionName..' '..tier
return 'ERROR: No potion named "' .. potionName .. '" was found[[Category:Pages with script errors]]'
    local potion = p.getItemByID(tier1potion.id + i - 1)
end
    if potion ~= nil then
for i, tier in pairs(tiers) do
      result = result..'\r\n|-'
local tierName = potionName..' '..tier
      result = result..'\r\n|'..Icons.Icon({tierName, type='item', notext='true', size='60'})
local potion = p.getItemByID(tier1potion.id + i - 1)
      result = result..'||'..'[['..tierName..'|'..tier..']]'
if potion ~= nil then
      result = result..'||'..potion.potionCharges..'||'..potion.description
table.insert(resultPart, '\r\n|-')
    end
table.insert(resultPart, '\r\n|'..Icons.Icon({tierName, type='item', notext=true, size='60'}))
  end
table.insert(resultPart, '||'..Icons.Icon({tierName, tier, type='item', noicon=true}))
table.insert(resultPart, '||'..potion.potionCharges..'||'..potion.description)
end
end


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


function p._getOtherItemBoxText(item)
function p._getOtherItemBoxText(item)
  result = ''
resultPart = {}
  --For equipment, show the slot they go in
--For equipment, show the slot they go in
  if item.equipmentSlot ~= nil then
if item.validSlots ~= nil then
    result = result.."\r\n|-\r\n|'''Equipment Slot:''' "..Constants.getEquipmentSlotName(item.equipmentSlot)
local slotLinkMap = {
    if item.isPassiveItem then
["Helmet"] = 'Equipment#Helmets',
      result = result..', Passive'
["Platebody"] = 'Equipment#Platebodies',
    end
["Boots"] = 'Equipment#Boots',
  end
["Weapon"] = 'Equipment#Weapons',
  --For weapons with a special attack, show the details
["Shield"] = 'Equipment#Offhand',
  if item.hasSpecialAttack then
["Amulet"] = 'Equipment#Amulets',
    local spAtt = p.getSpecialAttackByID(item.specialAttackID)
["Ring"] = 'Equipment#Rings',
    result = result.."\r\n|-\r\n|'''Special Attack:'''"
["Gloves"] = 'Equipment#Gloves',
    result = result..'\r\n* '..spAtt.chance..'% chance for '..spAtt.name..':'
["Quiver"] = 'Equipment#Ammunition',
    result = result..'\r\n** '..spAtt.description
["Cape"] = 'Equipment#Capes',
  end
["Passive"] = 'Combat Passive Slot',
  --For potions, show the number of charges
["Summon1"] = 'Summoning',
  if item.potionCharges ~= nil then
["Summon2"] = 'Summoning'
    result = result.."\r\n|-\r\n|'''Charges:''' "..item.potionCharges
}
  end
local slotText = {}
  --For food, show how much it heals for
for i, slot in ipairs(item.validSlots) do
  if item.healsFor ~= nil then
local slotLink = slotLinkMap[slot]
    result = result.."\r\n|-\r\n|'''Heals for:''' "..Icons.Icon({"Hitpoints", type="skill", notext="true"})..' '..(item.healsFor * 10)
if slotLink == nil then
  end
table.insert(slotText, slot)
  --For Prayer Points, show how many you get
else
  if item.prayerPoints ~= nil then
table.insert(slotText, '[[' .. slotLink .. '|' .. slot .. ']]')
    result = result.."\r\n|-\r\n|'''"..Icons.Icon({'Prayer', type='skill'}).." Points:''' "..item.prayerPoints
end
  end
end
  --For items with modifiers, show what those are
table.insert(resultPart, "\r\n|-\r\n|'''Equipment Slot:''' "..table.concat(slotText, ', '))
  if item.modifiers ~= nil and Shared.tableCount(item.modifiers) > 0 then
end
    local modArray = {}
--For weapons with a special attack, show the details
    for bonus, value in Shared.skpairs(item.modifiers) do
if item.hasSpecialAttack then
      table.insert(modArray, Constants.getModifierText(bonus, value, false))
table.insert(resultPart, "\r\n|-\r\n|'''Special Attack:'''")
    end
for i, spAtt in ipairs(item.specialAttacks) do
    result = result.."\r\n|-\r\n|'''Bonuses:'''\r\n"..table.concat(modArray, "<br/>")
table.insert(resultPart, '\r\n* ' .. spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
  end
table.insert(resultPart, '\r\n** ' .. spAtt.description)
  return result
end
end
--For potions, show the number of charges
if item.potionCharges ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Charges:''' "..item.potionCharges)
end
--For food, show how much it heals for
if item.healsFor ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Heals for:''' "..Icons.Icon({"Hitpoints", type="skill", notext="true"})..' '..(item.healsFor * 10))
end
--For Prayer Points, show how many you get
if item.prayerPoints ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', type='skill'}).." Points:''' "..item.prayerPoints)
end
--For items with modifiers, show what those are
if item.modifiers ~= nil and Shared.tableCount(item.modifiers) > 0 then
table.insert(resultPart, "\r\n|-\r\n|'''Modifiers:'''\r\n"..Constants.getModifiersText(item.modifiers, true))
end
return table.concat(resultPart)
end
end


function p.getOtherItemBoxText(frame)
function p.getOtherItemBoxText(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
local itemName = frame.args ~= nil and frame.args[1] or frame
  local item = p.getItem(itemName)
local item = p.getItem(itemName)
  local asList = false
local asList = false
  if frame.args ~= nil then  
if frame.args ~= nil then
    asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
  end
end
  if item == nil then
if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getOtherItemBoxText(item, asList)
return p._getOtherItemBoxText(item, asList)
end
end


function p._getItemCategories(item)
function p._getItemCategories(item)
  local result = ''
local resultPart = {}
  if item.category ~= nil then result = result..'[[Category:'..item.category..']]' end
if item.category ~= nil then table.insert(resultPart, '[[Category:'..item.category..']]') end
  if item.type ~= nil then result = result..'[[Category:'..item.type..']]' end
if item.type ~= nil then table.insert(resultPart, '[[Category:'..item.type..']]') end
  if item.tier ~= nil then result = result..'[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]' end
if item.tier ~= nil then table.insert(resultPart, '[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]') end
  if item.hasSpecialAttack then result = result..'[[Category:Items With Special Attacks]]' end
if item.hasSpecialAttack then table.insert(resultPart, '[[Category:Items With Special Attacks]]') end
  if item.isPassiveItem then result = result..'[[Category:Passive Items]]' end
if item.validSlots ~= nil then
  if item.chanceToDoubleLoot ~= nil and item.chanceToDoubleLoot > 0 then result = result..'[[Category:Double Loot Chance Items]]' end
local slotRemap = {
  return result
['Passive'] = 'Passive Items',
['Summon1'] = 'Summoning Familiars',
['Summon2'] = ''
}
for i, slotName in ipairs(item.validSlots) do
local slotRemapName = slotName
if slotRemap[slotName] ~= nil then slotRemapName = slotRemap[slotName] end
if slotRemapName ~= '' then table.insert(resultPart, '[[Category:' .. slotRemapName .. ']]') end
end
end
if item.modifiers ~= nil then
local modsDL = {
'increasedChanceToDoubleLootCombat',
'decreasedChanceToDoubleLootCombat',
'increasedChanceToDoubleLootThieving',
'decreasedChanceToDoubleLootThieving',
'increasedChanceToDoubleItemsGlobal',
'decreasedChanceToDoubleItemsGlobal'
}
for modName, val in pairs(item.modifiers) do
if Shared.contains(modsDL, modName) then
table.insert(resultPart, '[[Category:Double Loot Chance Items]]')
break
end
end
end
return table.concat(resultPart)
end
end


function p.getItemCategories(frame)
function p.getItemCategories(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
local itemName = frame.args ~= nil and frame.args[1] or frame
  local item = p.getItem(itemName)
local item = p.getItem(itemName)
  if item == nil then
if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getItemCategories(item)
return p._getItemCategories(item)
end
end


function p.getSkillcapeTable(frame)
function p.getSkillcapeTable(frame)
  local skillName = frame.args ~= nil and frame.args[1] or frame
local skillName = frame.args ~= nil and frame.args[1] or frame
  local cape = p.getItem(skillName..' Skillcape')
local cape = p.getItem(skillName..' Skillcape')
  local result = '{| class="wikitable"\r\n'
local resultPart = {}
  result = result..'!Skillcape!!Name!!Effect'
table.insert(resultPart, '{| class="wikitable"\r\n')
  result = result..'\r\n|-\r\n|'..Icons.Icon({cape.name, type='item', size='60', notext=true})
table.insert(resultPart, '!Skillcape!!Name!!Effect')
  result = result..'||[['..cape.name..']]||'..cape.description
table.insert(resultPart, '\r\n|-\r\n|'..Icons.Icon({cape.name, type='item', size='60', notext=true}))
  result = result..'\r\n|}'
table.insert(resultPart, '||'..Icons.Icon({cape.name, type='item', noicon=true})..'||'..cape.description)
  return result
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
end


function p.getShopSkillcapeTable()
function p.getItemGrid(frame)
  local result = ''
local resultPart = {}
table.insert(resultPart, '{|')
for i, item in Shared.skpairs(ItemData.Items) do
if i % 17 == 1 then
table.insert(resultPart, '\r\n|-\r\n|')
else
table.insert(resultPart, '||')
end
table.insert(resultPart, 'style="padding:3px"|'..Icons.Icon({item.name, type='item', notext=true, size='40'}))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end


  local capeList = {}
function p.getSpecialAttackTable(frame)
  for i, item in pairs(ItemData.Items) do
local spAttTable = {}
    if Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion' then
      table.insert(capeList, item)
    end
  end


  result = result..'\r\n{|class="wikitable sortable"'
for i, item in Shared.skpairs(ItemData.Items) do
  result = result..'\r\n!colspan="2" style="width:200px"|Cape'
if item.hasSpecialAttack then
  result = result..'!!Description!!style="width:120px"|Price'
for i, spAtt in ipairs(item.specialAttacks) do
if spAttTable[spAtt.id] == nil then spAttTable[spAtt.id] = {sortName=item.name, defn = spAtt, Icons = {}} end
table.insert(spAttTable[spAtt.id].Icons, Icons.Icon({item.name, type='item'}))
end
end
end


  --Sort the table by cost and then name
local resultPart = {}
  table.sort(capeList, function(a, b)  
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
                        if a.buysFor == b.buysFor then
table.insert(resultPart, '\r\n|-class="headerRow-0"')
                          return a.name < b.name
table.insert(resultPart, '\r\n!style="min-width:180px"|Weapon(s)!!Name!!Chance!!Effect')
                        else
for i, spAttData in Shared.skpairs(spAttTable) do
                          return a.sellsFor < b.buysFor
local spAtt = spAttData.defn
                        end
table.sort(spAttData.Icons, function(a, b) return a < b end)
                      end)
table.insert(resultPart, '\r\n|-')
  for i, thisItem in pairs(capeList) do
table.insert(resultPart, '\r\n|data-sort-value="'..spAttData.sortName..'"|'..table.concat(spAttData.Icons, '<br/>'))
    result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item', size='50', notext=true})
table.insert(resultPart, '||'..spAtt.name..'||data-sort-value="'..spAtt.defaultChance..'"|'..spAtt.defaultChance..'%')
    result = result..'||[['..thisItem.name..']]'
table.insert(resultPart, '||'..spAtt.description)
    result = result..'\r\n||'..thisItem.description
end
    result = result..'||style="text-align:left" data-sort-value="'..thisItem.buysFor..'"'
table.insert(resultPart, '\r\n|}')
    result = result..'|'..Icons.GP(thisItem.buysFor)
  end
  result = result..'\r\n|}'


  return result
return table.concat(resultPart)
end
end


function p.getItemGrid(frame)
function p.getWeaponStatsBox(frame)
  result = '{|'
local itemName = frame.args ~= nil and frame.args[1] or frame
  for i, item in Shared.skpairs(ItemData.Items) do
local item = p.getItem(itemName)
    if i % 17 == 1 then
if item == nil then
      result = result..'\r\n|-\r\n|'
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
    else
end
      result = result..'||'
    end
local ico = {
    result = result..'style="padding:3px"|'..Icons.Icon({item.name, type='item', notext=true, size='40'})
["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
  end
["Combat"] = Icons.Icon({'Combat', notext=true}),
  result = result..'\r\n|}'
["Defence"] = Icons.Icon({'Defence', type='skill', notext=true}),
  return result
["Magic"] = Icons.Icon({'Magic', type='skill', notext=true}),
["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}),
["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}),
["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true})
}
local resultPart = {}
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Weapon Stats')
table.insert(resultPart, '\r\n|-\r\n!colspan="2" style="border-bottom:solid thin black;"| Offensive Stats')
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Defensive Stats')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Speed')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Shared.round(p._getItemStat(item, 'attackSpeed', true) / 1000, 3, 1) .. 's')
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Type')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackType'))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Damage Reduction')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'damageReduction', true) .. '%')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'stabAttackBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Other')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Slayer'] .. ' Bonus Slayer XP')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slayerBonusXP', true) .. '%')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Attack'] .. ' Level Required')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackLevelRequired', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Level Required')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedLevelRequired', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Level Required')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicLevelRequired', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
table.insert(resultPart, '\r\n!style="text-align:right;"| Two Handed?')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. (p._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No'))
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
end


function p.getSpecialAttackTable(frame)
function p.getArmourStatsBox(frame)
  local spAttTable = {}
local itemName = frame.args ~= nil and frame.args[1] or frame
 
local item = p.getItem(itemName)
  for i, item in Shared.skpairs(ItemData.Items) do
if item == nil then
    if item.hasSpecialAttack then
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
      if spAttTable[item.specialAttackID] == nil then spAttTable[item.specialAttackID] = {sortName=item.name, Icons = {}} end
end
      table.insert(spAttTable[item.specialAttackID].Icons, Icons.Icon({item.name, type='item'}))
    end
local ico = {
  end 
["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
 
["Combat"] = Icons.Icon({'Combat', notext=true}),
  local result = '{|class="wikitable sortable stickyHeader"'
["Defence"] = Icons.Icon({'Defence', type='skill', notext=true}),
  result = result..'\r\n|-class="headerRow-0"'
["Magic"] = Icons.Icon({'Magic', type='skill', notext=true}),
  result = result..'\r\n!style="min-width:180px"|Weapon(s)!!Name!!Chance!!Effect'
["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}),
  for i, spAttData in Shared.skpairs(spAttTable) do
["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}),
    local spAtt = p.getSpecialAttackByID(i)
["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true})
    table.sort(spAttData.Icons, function(a, b) return a < b end)
}
    result = result..'\r\n|-'
local resultPart = {}
    result = result..'\r\n|data-sort-value="'..spAttData.sortName..'"|'..table.concat(spAttData.Icons, '<br/>')
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Armour Stats')
    result = result..'||'..spAtt.name..'||data-sort-value="'..spAtt.chance..'"|'..spAtt.chance..'%'
table.insert(resultPart, '\r\n|-\r\n!colspan="2" style="border-bottom:solid thin black;"| Offensive Stats')
    result = result..'||'..spAtt.description
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Defensive Stats')
  end
  result = result..'\r\n|}'
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
 
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true))
  return result
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'stabAttackBonus', 0))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Damage Reduction')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'damageReduction', true) .. '%')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Other')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Slayer'] .. ' Bonus Slayer XP')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slayerBonusXP', true) .. '%')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Level Required')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'defenceLevelRequired', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Level Required')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedLevelRequired', true))
table.insert(resultPart, '\r\n|-\r\n| colspan="2"|')
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Level Required')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicLevelRequired', true))
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
end


return p
return p

Revision as of 00:45, 12 January 2022

Lua module for generating various item tables. Pulls data from Module:GameData/data


--This module contains all sorts of functions for getting data on items
--Several functions related to use tables can be found at Module:Items/UseTables
--Functions related to source tables can be found at Module:Items/SourceTables
--Other functions moved to Module:Items/ComparisonTables

local p = {}

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

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

p.EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon', 'Easter Egg', 'Abnormal Log', 'Red Herring', 'Cool Glasses'}
p.EventItems = {'Christmas Cracker', 'Christmas Coal', 'Christmas Sweater',
				'Christmas Wreath', 'Candy Cane', 'Santa Hat',
				'Friendship Bracelet', 'Event Clue 1', 'Event Clue 2',
				'Event Clue 3', 'Event Clue 4', 'Candle', 'Cake Base',
				'Magical Flavouring', 'Magical Icing', 'Birthday Cake',
				'Purple Party Hat', 'Birthday Token', 'Christmas Present (Yellow)',
				'Christmas Present (Blue)', 'Christmas Present (Green)', 'Christmas Present (White)',
				'Christmas Present (Purple)', 'Christmas Present (Standard)', 'Event Token - Holiday 2021',
				'Holiday Scarf', 'Gingerbread House', 'Gingerbread Man', 'Edible Candy Cane',
				'Locked Chest', 'Locked Chest Key', 'Event Token (Holiday 2021)'}
p.OtherShopItems = {'Cooking Gloves', 'Mining Gloves', 'Gem Gloves', 'Smithing Gloves', 'Thieving Gloves'}
--This is hardcoded, so there's no easy way to scrape it. Hopefully it doesn't change
p.GemTable = {["Topaz"] = {name = 'Topaz', id = 128, chance = 50},
				["Sapphire"] = {name = "Sapphire", id = 129, chance = 17.5},
				["Ruby"] = {name = "Ruby", id = 130, chance = 17.5},
				["Emerald"] = {name = "Emerald", id = 131, chance = 10},
				["Diamond"] = {name = "Diamond", id = 132, chance = 5}}
--The base chance to receive a gem while mining
p.GemChance = .01
--The number of different fishing junk items
p.junkCount = 8
--Items (aside from bars & gems) which can be created via Alt Magic
p.AltMagicProducts = {'Rune Essence', 'Bones', 'Holy Dust'}
--The kinds of gloves with cost & charges
p.GloveTable = {['Cooking Gloves'] = {cost=50000, charges=500},
				['Mining Gloves'] = {cost=75000, charges=500},
				['Smithing Gloves'] = {cost=100000, charges=500},
				['Thieving Gloves'] = {cost=100000, charges=500},
				['Gem Gloves'] = {cost=500000, charges=2000}}


p.specialFishWt = 6722
p.specialFishLoot = {{128, 2000}, {129, 1600}, {130, 1400}, {131, 1000}, {132, 400}, {667, 10}, {668, 10}, {902, 1}, {670, 1}, {669, 50}, {120, 250}}

function p.buildSpecialFishingTable()
	--This shouldn't ever be included in a page
	--This is for generating the above 'specialFishLoot' variable if it ever needs to change
	--To re-run, edit the module, type in "console.log(p.buildSpecialFishingTable())" and copy+paste the result as the new value of the variable
	--Also gives you the total fishing weight for saving time later
	local lootArray = {}
	local totalWt = 0

	for i, item in pairs(ItemData.Items) do
		if item.fishingCatchWeight ~= nil then
			totalWt = totalWt + item.fishingCatchWeight
			table.insert(lootArray, '{'..(i - 1)..', '..item.fishingCatchWeight..'}')
		end
	end

	local result = 'p.specialFishWt = '..totalWt..'\r\n'
	result = result..'p.specialFishLoot = {'..table.concat(lootArray, ', ')..'}'
	return result
end

function p.getItemByID(ID)
	local result = Shared.clone(ItemData.Items[ID + 1])
	if result ~= nil then
		result.id = ID
	end
	return result
end

function p.getItem(name)
	local result = nil
	name = string.gsub(name, "%%27", "'")
	name = string.gsub(name, "&#39;", "'")
	for i, item in pairs(ItemData.Items) do
		local itemName = string.gsub(item.name, '#', '')
		if name == itemName then
			result = Shared.clone(item)
			--Make sure every item has an id, and account for Lua being 1-index
			result.id = i - 1
			break
		end
	end
	return result
end

function p.getItems(checkFunc)
	local result = {}
	for i, item in pairs(ItemData.Items) do
		if checkFunc(item) then
			local newItem = Shared.clone(item)
			newItem.id = i - 1
			table.insert(result, newItem)
		end
	end
	return result
end

function p._getItemStat(item, StatName, ZeroIfNil)
	local result = item[StatName]
	--Special Overrides:
	-- Equipment stats first
	if Shared.contains(ItemData.EquipmentStatKeys, StatName) and item.equipmentStats ~= nil then
		result = item.equipmentStats[StatName]
	elseif StatName == 'isTwoHanded' then
		if item.validSlots ~= nil and item.occupiesSlots ~= nil then
			result = Shared.contains(item.validSlots, 'Weapon') and Shared.contains(item.occupiesSlots, 'Shield')
		else
			result = false
		end
	elseif string.find(StatName, '^(.+)LevelRequired$') ~= nil and item.equipRequirements ~= nil and item.equipRequirements.Level ~= nil then
		local skillName = Shared.titleCase(string.match(StatName, '^(.+)LevelRequired$'))
		if skillName ~= nil then
			local skillID = Constants.getSkillID(skillName)
			if skillID ~= nil then
				result = item.equipRequirements.Level[skillID]
			end
		end
	elseif StatName == 'attackType' then
		result = p._getWeaponAttackType(item)
	elseif StatName == 'description' then
		result = item.description
		if result == nil or result == '' then result = 'No Description' end
	elseif StatName == 'completionReq' then
		if item.ignoreCompletion == nil or not item.ignoreCompletion then
			result = 'Yes'
		else
			result = 'No'
		end
	elseif StatName == 'slayerBonusXP' then
		return p._getItemModifier(item, 'increasedSkillXP', 'Slayer', false)
	elseif StatName == 'hasCombatStats' then
		return tostring(p.hasCombatStats(item) or p._hasLevelRequirements(item))
	end
	if result == nil and ZeroIfNil then result = 0 end
	return result
end

function p.getItemStat(frame)
	local args = frame.args ~= nil and frame.args or frame
	local ItemName = args[1]
	local StatName = args[2]
	local ZeroIfNil = args.ForceZero ~= nil and args.ForceZero ~= '' and args.ForceZero ~= 'false'
	local formatNum = args.formatNum ~= nil and args.formatNum ~= '' and args.formatNum ~= 'false'
	local item = p.getItem(ItemName)
	if item == nil then
		return "ERROR: No item named "..ItemName.." exists in the data module[[Category:Pages with script errors]]"
	end
	local result = p._getItemStat(item, StatName, ZeroIfNil)
	if formatNum then result = Shared.formatnum(result) end
	return result
end

--Gets the value of a given modifier for a given item
--asString is false by default, when true it writes the full bonus text
function p._getItemModifier(item, modifier, skill, asString)
	if asString == nil then asString = false end
	if skill == '' then
		skill = nil
	elseif type(skill) == 'string' then
		skill = Constants.getSkillID(skill)
	end

	local result = 0

	if item.modifiers ~= nil and item.modifiers[modifier] ~= nil then
		if type(item.modifiers[modifier]) == 'table' then
			for i, subVal in Shared.skpairs(item.modifiers[modifier]) do
				if subVal[1] == skill then
					result = subVal[2]
					break
				end
			end
		else
			result = item.modifiers[modifier]
		end
	end

	if asString then
		if skill ~= nil then
			return Constants._getModifierText(modifier, {skill, result})
		else
			return Constants._getModifierText(modifier, result)
		end
	else
		return result
	end
end

function p.hasCombatStats(item)
	if item.isEquipment or (item.validSlots == nil and item.equipmentStats ~= nil) then
		-- Ensure at least one stat has a non-zero value
		for statName, statVal in pairs(item.equipmentStats) do
			if statVal ~= 0 then return true end
		end
	end
	return false
end

function p._hasLevelRequirements(item)
	--Function true if an item has at least one level requirement to equip
	if item.equipRequirements ~= nil and item.equipRequirements.Level ~= nil then
		for skillID, lvl in pairs(item.equipRequirements.Level) do
			if lvl ~= nil and lvl > 1 then
				return true
			end
		end
		return false
	else
		return false
	end
end

function p.getItemModifier(frame)
	local itemName = frame.args ~= nil and frame.args[1] or frame[1]
	local modName = frame.args ~= nil and frame.args[2] or frame[2]
	local skillName = frame.args ~= nil and frame.args[3] or frame[3]
	local asString = frame.args ~= nil and frame.args[4] or frame[4]
	if asString ~= nil then
		asString = (string.upper(asString) ~= 'FALSE')
	end

	local item = p.getItem(itemName)
	if item == nil then
		return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
	end

	return p._getItemModifier(item, modName, skillName, asString)
end

function p._getWeaponAttackType(item)
	if item.isEquipment == true and (item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon')) or
									(item.occupiesSlots ~= nil and Shared.contains(item.occupiesSlots, 'Weapon')) then
		if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then
			local iconType = item.attackType ~= 'melee' and 'skill' or nil
			return Icons.Icon({Shared.titleCase(item.attackType), type=iconType, nolink='true'})
		end
	end
	return 'Invalid'
end

function p.getWeaponAttackType(frame)
	local itemName = frame.args ~= nil and frame.args[1] or frame
	local item = p.getItem(itemName)
	if item == nil then
		return "ERROR: No item named "..ItemName.." exists in the data module[[Category:Pages with script errors]]"
	end
	return p._getWeaponAttackType(item)
end

function p.getPotionTable(frame)
	local potionName = frame.args ~= nil and frame.args[1] or frame
	local tiers = {'I', 'II', 'III', 'IV'}

	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable"')
	table.insert(resultPart, '\r\n!Potion!!Tier!!Charges!!Effect')

	local tier1potion = p.getItem(potionName..' I')
	if tier1potion == nil then
		return 'ERROR: No potion named "' .. potionName .. '" was found[[Category:Pages with script errors]]'
	end
	for i, tier in pairs(tiers) do
		local tierName = potionName..' '..tier
		local potion = p.getItemByID(tier1potion.id + i - 1)
		if potion ~= nil then
			table.insert(resultPart, '\r\n|-')
			table.insert(resultPart, '\r\n|'..Icons.Icon({tierName, type='item', notext=true, size='60'}))
			table.insert(resultPart, '||'..Icons.Icon({tierName, tier, type='item', noicon=true}))
			table.insert(resultPart, '||'..potion.potionCharges..'||'..potion.description)
		end
	end

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

function p._getOtherItemBoxText(item)
	resultPart = {}
	--For equipment, show the slot they go in
	if item.validSlots ~= nil then
		local slotLinkMap = {
			["Helmet"] = 'Equipment#Helmets',
			["Platebody"] = 'Equipment#Platebodies',
			["Boots"] = 'Equipment#Boots',
			["Weapon"] = 'Equipment#Weapons',
			["Shield"] = 'Equipment#Offhand',
			["Amulet"] = 'Equipment#Amulets',
			["Ring"] = 'Equipment#Rings',
			["Gloves"] = 'Equipment#Gloves',
			["Quiver"] = 'Equipment#Ammunition',
			["Cape"] = 'Equipment#Capes',
			["Passive"] = 'Combat Passive Slot',
			["Summon1"] = 'Summoning',
			["Summon2"] = 'Summoning'
		}
		local slotText = {}
		for i, slot in ipairs(item.validSlots) do
			local slotLink = slotLinkMap[slot]
			if slotLink == nil then
				table.insert(slotText, slot)
			else
				table.insert(slotText, '[[' .. slotLink .. '|' .. slot .. ']]')
			end
		end
		table.insert(resultPart, "\r\n|-\r\n|'''Equipment Slot:''' "..table.concat(slotText, ', '))
	end
	--For weapons with a special attack, show the details
	if item.hasSpecialAttack then
		table.insert(resultPart, "\r\n|-\r\n|'''Special Attack:'''")
		for i, spAtt in ipairs(item.specialAttacks) do
			table.insert(resultPart, '\r\n* ' .. spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
			table.insert(resultPart, '\r\n** ' .. spAtt.description)
		end
	end
	--For potions, show the number of charges
	if item.potionCharges ~= nil then
		table.insert(resultPart, "\r\n|-\r\n|'''Charges:''' "..item.potionCharges)
	end
	--For food, show how much it heals for
	if item.healsFor ~= nil then
		table.insert(resultPart, "\r\n|-\r\n|'''Heals for:''' "..Icons.Icon({"Hitpoints", type="skill", notext="true"})..' '..(item.healsFor * 10))
	end
	--For Prayer Points, show how many you get
	if item.prayerPoints ~= nil then
		table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', type='skill'}).." Points:''' "..item.prayerPoints)
	end
	--For items with modifiers, show what those are
	if item.modifiers ~= nil and Shared.tableCount(item.modifiers) > 0 then
		table.insert(resultPart, "\r\n|-\r\n|'''Modifiers:'''\r\n"..Constants.getModifiersText(item.modifiers, true))
	end
	return table.concat(resultPart)
end

function p.getOtherItemBoxText(frame)
	local itemName = frame.args ~= nil and frame.args[1] or frame
	local item = p.getItem(itemName)
	local asList = false
	if frame.args ~= nil then
		asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
	end
	if item == nil then
		return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
	end

	return p._getOtherItemBoxText(item, asList)
end

function p._getItemCategories(item)
	local resultPart = {}
	if item.category ~= nil then table.insert(resultPart, '[[Category:'..item.category..']]') end
	if item.type ~= nil then table.insert(resultPart, '[[Category:'..item.type..']]') end
	if item.tier ~= nil then table.insert(resultPart, '[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]') end
	if item.hasSpecialAttack then table.insert(resultPart, '[[Category:Items With Special Attacks]]') end
	if item.validSlots ~= nil then
		local slotRemap = {
			['Passive'] = 'Passive Items',
			['Summon1'] = 'Summoning Familiars',
			['Summon2'] = ''
		}
		for i, slotName in ipairs(item.validSlots) do
			local slotRemapName = slotName
			if slotRemap[slotName] ~= nil then slotRemapName = slotRemap[slotName] end
			if slotRemapName ~= '' then table.insert(resultPart, '[[Category:' .. slotRemapName .. ']]') end
		end
	end
	if item.modifiers ~= nil then
		local modsDL = {
			'increasedChanceToDoubleLootCombat',
			'decreasedChanceToDoubleLootCombat',
			'increasedChanceToDoubleLootThieving',
			'decreasedChanceToDoubleLootThieving',
			'increasedChanceToDoubleItemsGlobal',
			'decreasedChanceToDoubleItemsGlobal'
		}
		for modName, val in pairs(item.modifiers) do
			if Shared.contains(modsDL, modName) then
				table.insert(resultPart, '[[Category:Double Loot Chance Items]]')
				break
			end
		end
	end
	return table.concat(resultPart)
end

function p.getItemCategories(frame)
	local itemName = frame.args ~= nil and frame.args[1] or frame
	local item = p.getItem(itemName)
	if item == nil then
		return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
	end

	return p._getItemCategories(item)
end

function p.getSkillcapeTable(frame)
	local skillName = frame.args ~= nil and frame.args[1] or frame
	local cape = p.getItem(skillName..' Skillcape')
	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable"\r\n')
	table.insert(resultPart, '!Skillcape!!Name!!Effect')
	table.insert(resultPart, '\r\n|-\r\n|'..Icons.Icon({cape.name, type='item', size='60', notext=true}))
	table.insert(resultPart, '||'..Icons.Icon({cape.name, type='item', noicon=true})..'||'..cape.description)
	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

function p.getItemGrid(frame)
	local resultPart = {}
	table.insert(resultPart, '{|')
	for i, item in Shared.skpairs(ItemData.Items) do
		if i % 17 == 1 then
			table.insert(resultPart, '\r\n|-\r\n|')
		else
			table.insert(resultPart, '||')
		end
		table.insert(resultPart, 'style="padding:3px"|'..Icons.Icon({item.name, type='item', notext=true, size='40'}))
	end
	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

function p.getSpecialAttackTable(frame)
	local spAttTable = {}

	for i, item in Shared.skpairs(ItemData.Items) do
		if item.hasSpecialAttack then
			for i, spAtt in ipairs(item.specialAttacks) do
				if spAttTable[spAtt.id] == nil then spAttTable[spAtt.id] = {sortName=item.name, defn = spAtt, Icons = {}} end
				table.insert(spAttTable[spAtt.id].Icons, Icons.Icon({item.name, type='item'}))
			end
		end
	end

	local resultPart = {}
	table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
	table.insert(resultPart, '\r\n|-class="headerRow-0"')
	table.insert(resultPart, '\r\n!style="min-width:180px"|Weapon(s)!!Name!!Chance!!Effect')
	for i, spAttData in Shared.skpairs(spAttTable) do
		local spAtt = spAttData.defn
		table.sort(spAttData.Icons, function(a, b) return a < b end)
		table.insert(resultPart, '\r\n|-')
		table.insert(resultPart, '\r\n|data-sort-value="'..spAttData.sortName..'"|'..table.concat(spAttData.Icons, '<br/>'))
		table.insert(resultPart, '||'..spAtt.name..'||data-sort-value="'..spAtt.defaultChance..'"|'..spAtt.defaultChance..'%')
		table.insert(resultPart, '||'..spAtt.description)
	end
	table.insert(resultPart, '\r\n|}')

	return table.concat(resultPart)
end

function p.getWeaponStatsBox(frame)
	local itemName = frame.args ~= nil and frame.args[1] or frame
	local item = p.getItem(itemName)
	if item == nil then
		return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
	end
	
	local ico = {
		["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
		["Combat"] = Icons.Icon({'Combat', notext=true}),
		["Defence"] = Icons.Icon({'Defence', type='skill', notext=true}),
		["Magic"] = Icons.Icon({'Magic', type='skill', notext=true}),
		["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}),
		["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}),
		["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true})
	}
	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Weapon Stats')
	table.insert(resultPart, '\r\n|-\r\n!colspan="2" style="border-bottom:solid thin black;"| Offensive Stats')
	table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Defensive Stats')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Speed')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Shared.round(p._getItemStat(item, 'attackSpeed', true) / 1000, 3, 1) .. 's')
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Type')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackType'))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Damage Reduction')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'damageReduction', true) .. '%')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'stabAttackBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
	table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Other')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Slayer'] .. ' Bonus Slayer XP')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slayerBonusXP', true) .. '%')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Attack'] .. ' Level Required')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackLevelRequired', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Level Required')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedLevelRequired', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Level Required')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicLevelRequired', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
	table.insert(resultPart, '\r\n!style="text-align:right;"| Two Handed?')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. (p._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No'))
	
	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

function p.getArmourStatsBox(frame)
	local itemName = frame.args ~= nil and frame.args[1] or frame
	local item = p.getItem(itemName)
	if item == nil then
		return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
	end
	
	local ico = {
		["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
		["Combat"] = Icons.Icon({'Combat', notext=true}),
		["Defence"] = Icons.Icon({'Defence', type='skill', notext=true}),
		["Magic"] = Icons.Icon({'Magic', type='skill', notext=true}),
		["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}),
		["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}),
		["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true})
	}
	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Armour Stats')
	table.insert(resultPart, '\r\n|-\r\n!colspan="2" style="border-bottom:solid thin black;"| Offensive Stats')
	table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Defensive Stats')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'stabAttackBonus', 0))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Damage Reduction')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'damageReduction', true) .. '%')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
	table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Other')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Slayer'] .. ' Bonus Slayer XP')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slayerBonusXP', true) .. '%')
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true))
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Level Required')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'defenceLevelRequired', true))
	
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Level Required')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedLevelRequired', true))
	
	table.insert(resultPart, '\r\n|-\r\n| colspan="2"|')
	table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Level Required')
	table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicLevelRequired', true))
	
	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

return p