Module:Items: Difference between revisions

From Melvor Idle
(Added type & category categories)
(getItemDataExport: Implement)
(143 intermediate revisions by 7 users not shown)
Line 1: Line 1:
--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 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 = mw.loadData('Module:Constants/data')


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


local EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon'}
p.EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon', 'Easter Egg',
local OtherShopItems = {'Cooking Gloves', 'Mining Gloves', 'Gem Gloves', 'Smithing Gloves', 'Thieving Gloves'}
'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)'}
--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
local 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
local GemChance = .01
p.GemChance = .01
--The number of different fishing junk items
local junkCount = 8
--Items (aside from bars & gems) which can be created via Alt Magic
local AltMagicProducts = {'Rune Essence', 'Bones', 'Holy Dust'}


 
function p.getItemByID(ID)
local specialFishWt = 6721
local result = Shared.clone(ItemData.Items[ID + 1])
local specialFishLoot = {{129, 2000}, {130, 1600}, {131, 1400}, {132, 1000}, {133, 400}, {668, 10}, {669, 10}, {671, 1}, {670, 50}, {121, 250}}
if result ~= nil then
 
result.id = ID
function p.buildSpecialFishingTable()
end
  --This shouldn't ever be included in a page
return result
  --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..', '..item.fishingCatchWeight..'}')
    end
  end
 
  local result = 'local specialFishWt = '..totalWt..'\r\n'
  result = result..'local specialFishLoot = {'..table.concat(lootArray, ', ')..'}'
  return result
end
end


function p.getItemByID(ID)
function p.getItem(name)
  local result = Shared.clone(ItemData.Items[ID + 1])
local result = nil
  if result ~= nil then
name = string.gsub(name, "%%27", "'")
    result.id = ID
name = string.gsub(name, "'", "'")
  end
for i, item in ipairs(ItemData.Items) do
  return result
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
end


function p.getItem(name)
function p.getItems(checkFunc)
  local result = nil
local result = {}
  name = string.gsub(name, "'", "'")
local itemCount = 0
  for i, item in pairs(ItemData.Items) do
for i, item in ipairs(ItemData.Items) do
    if(item.name == name) then
if checkFunc(item) then
      result = Shared.clone(item)
local newItem = Shared.clone(item)
      --Make sure every item has an id, and account for Lua being 1-index
newItem.id = i - 1
      result.id = i -1
itemCount = itemCount + 1
      break
result[itemCount] = 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
  end
result = item.description
  if result == nil and ZeroIfNil then result = 0 end
if result == nil or result == '' then result = 'No Description' end
  return result
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
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"
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


function p._getWeaponAttackType(item)
--Gets the value of a given modifier for a given item
  if item.type == 'Weapon' then
--asString is false by default, when true it writes the full bonus text
    return Icons.Icon({'Melee', nolink='true'})
function p._getItemModifier(item, modifier, skill, asString)
  elseif item.type == 'Ranged Weapon' then
if asString == nil then asString = false end
    return Icons.Icon({'Ranged', type='skill', nolink='true'})
if skill == '' then
  elseif item.type == 'Magic Staff' or item.type == 'Magic Wand' then
skill = nil
    return Icons.Icon({'Magic', type='skill', nolink='true'})
elseif type(skill) == 'string' then
  else
skill = Constants.getSkillID(skill)
    return "Invalid"
end
  end
 
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


function p.getWeaponAttackType(frame)
if asString then
  local itemName = frame.args ~= nil and frame.args[1] or frame
if skill ~= nil then
  local item = p.getItem(itemName)
return Constants._getModifierText(modifier, {skill, result})
  if item == nil then
else
    return "ERROR: No item named "..ItemName.." exists in the data module"
return Constants._getModifierText(modifier, result)
  end
end
  return p._getWeaponAttackType(item)
else
return result
end
end
end


function p.getPotionTable(frame)
function p.hasCombatStats(item)
  local potionName = frame.args ~= nil and frame.args[1] or frame
if item.isEquipment or (item.validSlots == nil and item.equipmentStats ~= nil) then
  local tiers = {'I', 'II', 'III', 'IV'}
-- 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


  local result = '{| class="wikitable"'
function p._hasLevelRequirements(item)
  result = result..'\r\n!Potion!!Tier!!Charges!!Effect'
--Function true if an item has at least one level requirement to equip
 
if item.equipRequirements ~= nil and item.equipRequirements.Level ~= nil then
  local tier1potion = p.getItem(potionName..' I')
for skillID, lvl in pairs(item.equipRequirements.Level) do
  for i, tier in pairs(tiers) do
if lvl ~= nil and lvl > 1 then
    local tierName = potionName..' '..tier
return true
    local potion = p.getItemByID(tier1potion.id + i - 1)
end
    if potion == nil then
end
      mw.log("Failed to get tier "..tier)
return false
    else
else
      result = result..'\r\n|-'
return false
      result = result..'\r\n|'..Icons.Icon({tierName, type='item', notext='true', size='60'})
end
      result = result..'||'..'[['..tierName..'|'..tier..']]'
      result = result..'||'..potion.potionCharges..'||'..potion.description
    end
  end
 
  result = result..'\r\n|}'
  return result
end
end


function p.getCreationTable(frame)
function p.getItemModifier(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
local itemName = frame.args ~= nil and frame.args[1] or frame[1]
  local item = p.getItem(itemName)
local modName = frame.args ~= nil and frame.args[2] or frame[2]
  if item == nil then
local skillName = frame.args ~= nil and frame.args[3] or frame[3]
    return "ERROR: No item named "..itemName.." exists in the data module"
local asString = frame.args ~= nil and frame.args[4] or frame[4]
  end
if asString ~= nil then
asString = (string.upper(asString) ~= 'FALSE')
end


  local skill = ''
local item = p.getItem(itemName)
  local specialReq = nil
if item == nil then
  local time = 0
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  local maxTime = nil
end
  local lvl = 0
  local xp = 0
  local qty = nil
  local req = nil
  local result = ''


  local tables = {}
return p._getItemModifier(item, modName, skillName, asString)
  --First figure out what skill is used to make this...
end
  if item.smithingLevel ~= nil then
    skill = 'Smithing'
    lvl = item.smithingLevel
    xp = item.smithingXP
    req = item.smithReq
    qty = item.smithingQty
    time = 2
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
  if item.craftingLevel ~= nil then
    skill = 'Crafting'
    lvl = item.craftingLevel
    xp = item.craftingXP
    req = item.craftReq
    qty = item.craftQty
    time = 3
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
  if item.runecraftingLevel ~= nil then
    skill = 'Runecrafting'
    lvl = item.runecraftingLevel
    xp = item.runecraftingXP
    req = item.runecraftReq
    qty = item.runecraftQty
    time = 2
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
  if item.fletchingLevel ~= nil then
    skill = 'Fletching'
    lvl = item.fletchingLevel
    xp = item.fletchingXP
    req = item.fletchReq
    qty = item.fletchQty
    time = 2
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
  if item.herbloreReq ~= nil then
    skill = 'Herblore'
    req = item.herbloreReq
    --Currently using 'herbloreMasteryID' as shorthand to find details, could be a better method
    local potionID = item.herbloreMasteryID
    local potionData = SkillData.Herblore.ItemData[potionID + 1]
    lvl = potionData.herbloreLevel
    xp = potionData.herbloreXP
    time = 2
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
  if item.miningLevel ~= nil then
    skill = 'Mining'
    lvl = item.miningLevel
    time = 3
    xp = item.miningXP
    if item.name == 'Dragonite Ore' then
      specialReq = Icons.Icon({"Mastery", notext='true'})..' 271 total [[Mining]] [[Mastery]]'
    end
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, specialReq))
  end
  if item.type == "Logs" then
    --Well this feels like cheating, but for as long as logs are the first items by ID it works
    local treeData = SkillData.Woodcutting[item.id + 1]
    skill = 'Woodcutting'
    lvl = treeData.level
    time = treeData.interval / 1000
    xp = treeData.xp
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
  if item.fishingLevel ~= nil then
    skill = 'Fishing'
    lvl = item.fishingLevel
    xp = item.fishingXP
    time = item.minFishingInterval/1000
    maxTime = item.maxFishingInterval/1000
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime))
  end
  if item.type == "Havest" or item.type == "Herb" or item.type == "Logs" then
    --Havest/Herb means farming
    --Logs might mean farming or might not. Depends on the logs
    --Yes, Havest. The typos are coming from inside the source code
    for i, item2 in pairs(ItemData.Items) do
      if item2.grownItemID == item.id then
        skill = 'Farming'
        lvl = item2.farmingLevel
        xp = item2.farmingXP
        time = item2.timeToGrow
        qty = 5
        req = {{id = i - 1, qty = (item2.seedsRequired ~= nil and item2.seedsRequired or 1)}}
        table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
        break
      end
    end
  end
  if item.type == "Food" or item.type == "Cooked Fish" then
    --Food/Cooked Fish is Fishing, need to figure out source item
    for i, item2 in pairs(ItemData.Items) do
      if item2.burntItemID == item.id or item2.cookedItemID == item.id then
        skill = 'Cooking'
        lvl = item2.cookingLevel
        if item2.burntItemID == item.id then
          xp = 1
        else
          xp = item2.cookingXP
        end
        time = 3
        req = {{id = i - 1, qty = 1}}
        break
      end
    end
    if skill ~= '' then
      table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
    end
  end


  if Shared.tableCount(tables) == 0 then
function p._getWeaponAttackType(item)
    return "Failed to find creation requirements for this (Possibly the module isn't properly updated for this skill)"
if item.isEquipment == true and (item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon')) or
  else
(item.occupiesSlots ~= nil and Shared.contains(item.occupiesSlots, 'Weapon')) then
    return table.concat(tables, '\r\n<br/>')
if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then
  end
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
end


function p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq)
function p.getWeaponAttackType(frame)
  if qty == nil then qty = 1 end
local itemName = frame.args ~= nil and frame.args[1] or frame
  local result = '{|class="wikitable"'
local item = p.getItem(itemName)
  if req ~= nil then
if item == nil then
    result = result..'\r\n!colspan="2"|Item Creation'
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  else
end
    result = result..'\r\n!colspan="2"|Item Production'
return p._getWeaponAttackType(item)
  end
  result = result..'\r\n|-\r\n!style="text-align: right;"|Requirements'
  result = result..'\r\n|'..Icons.Icon({skill, type="skill", notext="true"}).." '''"..lvl.."''' [["..skill.."]]"
  if specialReq ~= nil then result = result..'<br/>'..specialReq end
 
  if req ~= nil then
    result = result..'\r\n|-\r\n!style="text-align: right;"|Materials\r\n|'
    for i, mat in pairs(req) do
      if i > 1 then result = result..'<br/>' end
      local matItem = p.getItemByID(mat.id)
      if matItem == nil then
        result = result..mat.qty..'x ?????'
      else
        result = result..Icons.Icon({matItem.name, type='item', qty=mat.qty})
      end
    end
  end
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base Quantity'
  result = result..'\r\n|'..qty
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base Experience'
  result = result..'\r\n|'..Shared.formatnum(xp)..' XP'
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base Creation Time'
  result = result..'\r\n|'..Shared.formatnum(Shared.round(time, 2, 0))..'s'
  if maxTime ~= nil then result = result..' - '..Shared.formatnum(Shared.round(maxTime, 2, 0))..'s' end
  result = result..'\r\n|}'
 
  return result
end
end


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


  --Alright, time to go through all the ways you can get an item...
local resultPart = {}
  --First up: Can we kill somebody and take theirs?
table.insert(resultPart, '{| class="wikitable"')
  local killStr = ''
table.insert(resultPart, '\r\n!Potion!!Tier!!Charges!!Effect')
  local count1 = 0
  for i, monster in pairs(MonsterData.Monsters) do
    local isDrop = false
    if monster.bones == item.id then
      isDrop = true
    elseif monster.lootTable ~= nil then
      for j, loot in pairs(monster.lootTable) do
        if loot[1] == item.id then
          isDrop = true
        end
      end
    end
    if isDrop then
      count1 = count1 + 1
      if string.len(killStr) > 0 then
        killStr = killStr..','
        if count1 % 3 == 1 and count1 > 1 then killStr = killStr..'<br/>' end
        killStr = killStr..Icons.Icon({monster.name, type="monster", notext="true"})
      else
        killStr = killStr..'Killing: '..Icons.Icon({monster.name, type="monster", notext="true"})
      end
    end
  end
  if string.len(killStr) > 0 then table.insert(lineArray, killStr) end


  --Next: Can we find it in a box?
local tier1potion = p.getItem(potionName..' I')
  --While we're here, check for upgrades, cooking, and growing
if tier1potion == nil then
  local lootStr = ''
return 'ERROR: No potion named "' .. potionName .. '" was found[[Category:Pages with script errors]]'
  local upgradeStr = ''
end
  local cookStr = ''
for i, tier in ipairs(tiers) do
  local burnStr = ''
local tierName = potionName..' '..tier
  local growStr = ''
local potion = p.getItemByID(tier1potion.id + i - 1)
  local count2 = 0
if potion ~= nil then
  count1 = 0
table.insert(resultPart, '\r\n|-')
  for i, item2 in pairs(ItemData.Items) do
table.insert(resultPart, '\r\n|'..Icons.Icon({tierName, type='item', notext=true, size='60'}))
    if item2.dropTable ~= nil then
table.insert(resultPart, '||'..Icons.Icon({tierName, tier, type='item', noicon=true}))
      for j, loot in pairs(item2.dropTable) do
table.insert(resultPart, '||'..potion.potionCharges..'||'..potion.description)
        if loot[1] == item.id then
end
          count1 = count1 + 1
end
          if string.len(lootStr) > 0 then
            lootStr = lootStr..','
            if count1 % 3 == 1 and count1 > 1 then lootStr = lootStr..'<br/>' end
            lootStr = lootStr..Icons.Icon({item2.name, type="item", notext="true"})
          else
            lootStr = lootStr..'Opening: '..Icons.Icon({item2.name, type="item", notext="true"})
          end
        end
      end
    end
    if item2.trimmedItemID == item.id then
          count2 = count2 + 1
        if string.len(upgradeStr) > 0 then
          upgradeStr = upgradeStr..','
          if count2 % 3 == 1 and count2 > 1 then upgradeStr = upgradeStr..'<br/>' end
          upgradeStr = upgradeStr..Icons.Icon({item2.name, type="item", notext="true"})
        else
          upgradeStr = upgradeStr..'Upgrading: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.cookedItemID == item.id then
        if string.len(cookStr) > 0 then
          cookStr = cookStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          cookStr = cookStr..'Cooking: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.burntItemID == item.id then
        if string.len(burnStr) > 0 then
          burnStr = burnStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          burnStr = burnStr..'Burning: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.grownItemID == item.id then
        if string.len(growStr) > 0 then
          growStr = growStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          growStr = growStr..'Growing: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
  end
  if string.len(lootStr) > 0 then table.insert(lineArray, lootStr) end
  if string.len(upgradeStr) > 0 then table.insert(lineArray, upgradeStr) end
  if string.len(cookStr) > 0 then table.insert(lineArray, cookStr) end
  if string.len(burnStr) > 0 then table.insert(lineArray, burnStr) end
  if string.len(growStr) > 0 then table.insert(lineArray, growStr) end


  --Next: Can we take it from somebody else -without- killing them?
table.insert(resultPart, '\r\n|}')
  local thiefStr = ''
return table.concat(resultPart)
  for i, npc in pairs(SkillData.Thieving) do
end
    if npc.lootTable ~= nil then
      for j, loot in pairs(npc.lootTable) do
        if loot[1] == item.id then
          if string.len(thiefStr) > 0 then
            thiefStr = thiefStr..','..Icons.Icon({npc.name, type="thieving", notext="true"})
          else
            thiefStr = thiefStr..'Pickpocketing: '..Icons.Icon({npc.name, type="thieving", notext="true"})
          end
        end
      end
    end
  end
  if string.len(thiefStr) > 0 then table.insert(lineArray, thiefStr) end


  --If all else fails, I guess we should check if we can make it ourselves
function p._getOtherItemBoxText(item)
  --SmithCheck:
resultPart = {}
  if item.smithingLevel ~= nil then
--For equipment, show the slot they go in
    table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
if item.validSlots ~= nil then
  end
local slotLinkMap = {
["Helmet"] = 'Equipment#Helmets',
["Platebody"] = 'Equipment#Platebodies',
["Platelegs"] = 'Equipment#Platelegs',
["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


  --CraftCheck:
function p.getOtherItemBoxText(frame)
  if item.craftingLevel ~= nil then
local itemName = frame.args ~= nil and frame.args[1] or frame
    table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
local item = p.getItem(itemName)
  end
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


  --FletchCheck:
return p._getOtherItemBoxText(item, asList)
  if item.fletchingLevel ~= nil then
end
    table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
  end


  --RunecraftCheck:
function p._getItemCategories(item)
  if item.runecraftingLevel ~= nil then
local resultPart = {}
    table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
if item.category ~= nil then table.insert(resultPart, '[[Category:'..item.category..']]') end
  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


  --MineCheck:
function p.getItemCategories(frame)
  if item.miningLevel ~= nil then
local itemName = frame.args ~= nil and frame.args[1] or frame
    table.insert(lineArray, Icons._SkillReq("Mining", item.miningLevel))
local item = p.getItem(itemName)
  end
if item == nil then
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
end


  --FishCheck:
return p._getItemCategories(item)
  if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
end
    table.insert(lineArray, Icons._SkillReq("Fishing", 1))
  elseif item.fishingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Fishing", item.fishingLevel))
  end
 
  --HerbCheck:
  if item.herbloreMasteryID ~= nil then
    local potionData = SkillData.Herblore.ItemData[item.herbloreMasteryID + 1].herbloreLevel
    table.insert(lineArray, Icons._SkillReq("Herblore", potionData))
  end
 
  --Finally there are some weird exceptions:
  --Coal can be acquired via firemaking
  if item.name == "Coal Ore" then
    table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
  end
 
  --Gems can be acquired from mining, fishing, and alt. magic
  if item.type == 'Gem' then
    table.insert(lineArray, Icons._SkillReq("Fishing", 1))
    table.insert(lineArray, Icons._SkillReq("Mining", 1))
    table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
  end
 
  --Bars and some other stuff can also be acquired via Alt. Magic
  if type == 'Bar' or Shared.contains(AltMagicProducts, item.name) then
    table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
  end
 
  --Chapeau Noir & Bobby's Pocket are special Thieving items
  if item.name == "Chapeau Noir" or item.name == "Bobby&apos;s Pocket" then
    table.insert(lineArray, Icons._SkillReq("Thieving", 1))
  end
 
  --Rhaelyx pieces are also special
  if item.name == 'Jewel of Rhaelyx' then
    local rhaStr = 'Any action in: '
    rhaStr = rhaStr..Icons.Icon({'Firemaking', type = 'skill'})..', '..Icons.Icon({'Cooking', type = 'skill'})..', '..Icons.Icon({'Smithing', type = 'skill'})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Fletching', type = 'skill'})..', '..Icons.Icon({'Crafting', type = 'skill'})..', '..Icons.Icon({'Runecrafting', type = 'skill'})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Herblore', type='skill'})
    table.insert(lineArray, rhaStr)
  elseif item.name == 'Circlet of Rhaelyx' then
    local rhaStr = 'Any action in: '
    rhaStr = rhaStr..Icons.Icon({'Woodcutting', type = 'skill'})..', '..Icons.Icon({'Fishing', type = 'skill'})..', '..Icons.Icon({'Mining', type = 'skill'})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Thieving', type = 'skill'})..', '..Icons.Icon({'Farming', type = 'skill'})
    table.insert(lineArray, rhaStr)
  elseif item.name == 'Mysterious Stone' then
    local rhaStr = 'Any action in: '
    rhaStr = rhaStr..Icons.Icon({'Firemaking', type = 'skill'})..', '..Icons.Icon({'Cooking', type = 'skill'})..', '..Icons.Icon({'Smithing', type = 'skill'})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Fletching', type = 'skill'})..', '..Icons.Icon({'Crafting', type = 'skill'})..', '..Icons.Icon({'Runecrafting', type = 'skill'})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Herblore', type='skill'})..', '..Icons.Icon({'Woodcutting', type = 'skill'})..', '..Icons.Icon({'Fishing', type = 'skill'})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Mining', type = 'skill'})..', '..Icons.Icon({'Thieving', type = 'skill'})..', '..Icons.Icon({'Farming', type = 'skill'})
    rhaStr = rhaStr..'<br/>after finding '..Icons.Icon({'Crown of Rhaelyx', type='item'})
    table.insert(lineArray, rhaStr)
  end
  --Tokens are from the appropriate skill
  if item.isToken then
    for skill, id in pairs(Constants.skill) do
      if id == item.skill then
        table.insert(lineArray, Icons._SkillReq(skill, 1))
      end
    end
  end
 
  --Shop items (including special items like gloves that aren't otherwise listed)
  if item.slayerCost ~= nil or item.buysFor ~= nil or Shared.contains(OtherShopItems, item.name) then
    table.insert(lineArray, '[[Shop]]')
  end
 
  --Easter Eggs (manual list 'cause don't have a better way to do that)
  if Shared.contains(EasterEggs, item.name) then
    table.insert(lineArray, '[[Easter Eggs]]')
  end


  local result = ''
function p.getSkillcapeTable(frame)
  if asList then
local skillName = frame.args ~= nil and frame.args[1] or frame
    result = '* '..table.concat(lineArray, "\r\n* ")
local cape = p.getItem(skillName..' Skillcape')
  else
if cape == nil then
    result = table.concat(lineArray, "<br/>")
return ''
    result = '<div style="max-width:180px;text-align:right">'..result..'</div>'
else
  end
local resultPart = {}
  return result
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
end
end


 
function p.getItemGrid(frame)
function p.getItemSources(frame)
local resultPart = {}
  local itemName = frame.args ~= nil and frame.args[1] or frame
table.insert(resultPart, '{|')
  local item = p.getItem(itemName)
for i, item in Shared.skpairs(ItemData.Items) do
  local asList = false
if i % 17 == 1 then
  if frame.args ~= nil then  
table.insert(resultPart, '\r\n|-\r\n|')
    asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
else
  end
table.insert(resultPart, '||')
  if item == nil then
end
    return "ERROR: No item named "..itemName.." exists in the data module"
table.insert(resultPart, 'style="padding:3px"|'..Icons.Icon({item.name, type='item', notext=true, size='40'}))
  end
end
 
table.insert(resultPart, '\r\n|}')
  return p._getItemSources(item, asList)
return table.concat(resultPart)
end
end


--Brute forcing some item uses to make things easier
function p.getWeaponStatsBox(frame)
local itemUseArray = {
local itemName = frame.args ~= nil and frame.args[1] or frame
  Combat = {},
local item = p.getItem(itemName)
  Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
if item == nil then
  Crafting = {'Crown of Rhaelyx'},
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  Farming = {'Compost', 'Weird Gloop', 'Bob&apos;s Rake'},
end
  Firemaking = {'Crown of Rhaelyx'},
  Fishing = {'Amulet of Fishing', 'Message in a Bottle'},
  Fletching = {'Crown of Rhaelyx'},
  Herblore = {'Crown of Rhaelyx'},
  Mining = {'Mining Gloves', 'Gem Gloves'},
  Prayer = {},
  Runecrafting = {'Crown of Rhaelyx'},
  Slayer = {},
  Smithing = {'Smithing Gloves', 'Crown of Rhaelyx'},
  Thieving = {'Chapeau Noir', 'Thieving Gloves'},
  Woodcutting = {},
  }
local potionUseArray = {
  [0] = 'Combat',
  [1] = 'Combat',
  [2] = 'Combat',
  [3] = 'Combat',
  [4] = 'Combat',
  [5] = 'Combat',
  [6] = 'Combat',
  [7] = 'Woodcutting',
  [8] = 'Fishing',
  [9] = 'Firemaking',
  [10] = 'Cooking',
  [11] = 'Mining',
  [12] = 'Smithing',
  [13] = 'Thieving',
  [14] = 'Farming',
  [15] = 'Fletching',
  [16] = 'Crafting',
  [17] = 'Runecrafting',
  [18] = 'Herblore',
  [19] = 'Combat',
  [20] = 'Combat',
  [21] = 'Combat',
  [22] = 'Combat',
  [23] = 'Combat',
}


function p._getItemUses(item)
local ico = {
  local useArray = {}
["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
  --Another fun one. This time getting all the different possible ways an item can be used
["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')


  --Before anything else, if this is a potion add it to the appropriate override section
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Speed')
  if item.herbloreMasteryID ~= nil then
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Shared.round(p._getItemStat(item, 'attackSpeed', true) / 1000, 3, 1) .. 's')
    table.insert(itemUseArray[potionUseArray[item.herbloreMasteryID]], item.name)
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
  end
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true))


  --First things added to the list are non-skill things that are easy to check
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Type')
  if item.equipmentSlot ~= nil or Shared.contains(itemUseArray.Combat, item.name) then
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackType'))
    table.insert(useArray, '* '..Icons.Icon({'Combat'}))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Damage Reduction')
  end
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'damageReduction', true) .. '%')
  if item.healsFor ~= nil then
    table.insert(useArray, '* [[Food]]')
  end
  if item.dropTable ~= nil then
    table.insert(useArray, '* [[Chest Drop Tables|Can Be Opened]]')  
  end


  --Next, upgrading, crafting, herblore, fletching, and runecrafting since we have to sift through other items for these
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
  local canUpgrade = false
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true))
  local canCraft = false
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
  local canFletch = false
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))
  local canRunecraft = false
  local canHerblore = false
  if item.trimmedItemID ~= nil then
    canUpgrade = true
  else
    for i, item2 in pairs(ItemData.Items) do
      if item2.itemsRequired ~= nil then
        for j, req in pairs(item2.itemsRequired) do
          if req[1] == item.id then
            canUpgrade = true
            break
          end
        end
      end


      if item2.craftReq ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
        for j, req in pairs(item2.craftReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'stabAttackBonus', true))
          if req.id == item.id then
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
            canCraft = true
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
            break
          end
        end
      end


      if item2.fletchReq ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
        for j, req in pairs(item2.fletchReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
          if req.id == item.id then
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Other')
            canFletch = true
            break
          end
        end
      end


      if item2.runecraftReq ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
        for j, req in pairs(item2.runecraftReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
          if req.id == item.id then
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Slayer'] .. ' Bonus Slayer XP')
            canRunecraft = true
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slayerBonusXP', true) .. '%')
            break
          end
        end
      end


      if item2.herbloreReq ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
        for j, req in pairs(item2.herbloreReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
          if req.id == item.id then
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Attack'] .. ' Level Required')
            canHerblore = true
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackLevelRequired', true))
            break
          end
        end
      end
    end
  end
  if canUpgrade then
    table.insert(useArray, '* [[Upgrading Items]]')
  end
   
  --Cooking
  if item.cookedItemID ~= nil or Shared.contains(itemUseArray.Cooking, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Cooking', type='skill'}))
  end
  --Crafting
  if canCraft or Shared.contains(itemUseArray.Crafting, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Crafting', type='skill'}))
  end
  --Farming
  if item.grownItemID ~= nil or Shared.contains(itemUseArray.Farming, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Farming', type='skill'}))
  end
  --Firemaking
  if item.firemakingID ~= nil or Shared.contains(itemUseArray.Firemaking, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Firemaking', type='skill'}))
  end
  --Fishing
  if Shared.contains(itemUseArray.Fishing, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Fishing', type='skill'}))
  end
  --Fletching
  if canFletch or Shared.contains(itemUseArray.Fletching, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Fletching', type='skill'}))
  end
  --Herblore
  if canHerblore or Shared.contains(itemUseArray.Herblore, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Herblore', type='skill'}))
  end
  --Mining
  if Shared.contains(itemUseArray.Mining, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Mining', type='skill'}))
  end
  --Prayer
  if item.prayerPoints ~= nil or Shared.contains(itemUseArray.Prayer, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Prayer', type='skill'}))
  end
  --Runecrafting
  if canRunecraft or Shared.contains(itemUseArray.Runecrafting, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Runecrafting', type='skill'}))
  end
  --Slayer
  if item.slayerCost ~= nil or Shared.contains(itemUseArray.Slayer, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Slayer', type='skill'}))
  end
  --Smithing
  if item.type == 'Bar' or item.type == 'Ore' or Shared.contains(itemUseArray.Smithing, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Smithing', type='skill'}))
  end
  --Thieving
  if Shared.contains(itemUseArray.Thieving, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Thieving', type='skill'}))
  end
  --Woodcutting
  if Shared.contains(itemUseArray.Woodcutting, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Woodcutting', type='skill'}))
  end
 
  --Other odds and ends:
  --Mastery Tokens are tied to 'Mastery'
  if item.type == 'Token' then
    table.insert(useArray, '* '..Icons.Icon({'Mastery'}))
  end


  --Skillcapes are tied to the appropriate skill
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
  --Except Max Skillcape, which is tied to all skills. (And so is the Signet Ring)
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
  --And combat skillcapes, since combat skills don't get special treatment
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))
  local ignoreCapes = {'Ranged Skillcape', 'Attack Skillcape', 'Strength Skillcape', 'Hitpoints Skillcape', 'Defence Skillcape'}
  if item.name == 'Max Skillcape' or item.name == 'Aorpheat&apos;s Signet Ring' then
    table.insert(useArray, '* All skills')
  elseif item.name == 'Magic Skillcape' then
    table.insert(useArray, '* '..Icons.Icon({'Magic', type='skill'}))
    table.insert(useArray, '* '..Icons.Icon({'Alt. Magic', type='skill'}))
  elseif Shared.contains(item.name, 'Skillcape') and not Shared.contains(ignoreCapes, item.name) then
    local skillName = Shared.splitString(item.name, ' ')[1]
    table.insert(useArray, '* '..Icons.Icon({skillName, type='skill'}))
  end


  --Special note for Charge Stone of Rhaelyx
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
  if item.name == 'Charge Stone of Rhaelyx' then
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true))
    table.insert(useArray, '* Powering '..Icons.Icon({'Crown of Rhaelyx', type='item'}))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Level Required')
  end
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicLevelRequired', true))


  return table.concat(useArray,'\r\n')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
end
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'))


function p.getItemUses(frame)
table.insert(resultPart, '\r\n|}')
  local itemName = frame.args ~= nil and frame.args[1] or frame
return table.concat(resultPart)
  local item = p.getItem(itemName)
  if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module"
  end
 
  return p._getItemUses(item)
end
end


function p._getItemLootSourceTable(item)
function p.getArmourStatsBox(frame)
  local result = '{| class="wikitable sortable stickyHeader"'
local itemName = frame.args ~= nil and frame.args[1] or frame
  result = result..'\r\n|- class="headerRow-0"'
local item = p.getItem(itemName)
  result = result..'\r\n!Source!!Source Type!!Quantity!!Chance'
if item == nil then
 
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  --Set up function for adding rows
end
  local buildRow = function(source, type, minqty, qty, chance)
    if minqty == nil then minqty = 1 end
    local rowTxt = '\r\n|-'
    rowTxt = rowTxt..'\r\n|style ="text-align: left;"|'..source
    rowTxt = rowTxt..'\r\n|style ="text-align: left;"|'..type
 
    rowTxt = rowTxt..'\r\n|style ="text-align: right;" data-sort-value:"'..qty..'"|'..minqty
    if qty ~= minqty then rowTxt = rowTxt..' - '..qty end
    rowTxt = rowTxt..'\r\n|style ="text-align: right;"|'..Shared.round(chance, 2, 2)..'%'
    return rowTxt
  end
  local dropRows = {}
 
  --Alright, time to go through a few ways to get the item
  --First up: Can we kill somebody and take theirs?
  for i, monster in pairs(MonsterData.Monsters) do
    local minqty = 1
    local qty = 1
    local chance = 0
    local wt = 0
    local totalWt = 0
    --Only add bones if this monster has loot (ie appears outside a dungeon) and isn't a boss
    --... unless we're looking for Shards of course, at which point we'll take any monster with the right bones
    if ((monster.lootTable ~= nil and not monster.isBoss) or Shared.contains(item.name, 'Shard')) and monster.bones == item.id then
      qty = monster.boneQty ~= nil and monster.boneQty or 1
      minqty = qty
      chance = 100
    elseif monster.lootTable ~= nil then
      for j, loot in pairs(monster.lootTable) do
        totalWt = totalWt + loot[2]
        if loot[1] == item.id then
          wt = loot[2]
          qty = loot[3]
        end
      end
      if wt > 0 then
        local lootChance = monster.lootChance ~= nil and monster.lootChance or 100
        chance = ((wt * lootChance) / (totalWt * 100)) * 100
      end
    end
    if chance > 0 then
      local sourceTxt = Icons.Icon({monster.name, type='monster'})
      table.insert(dropRows, {source = sourceTxt, type = '[[Monster]]', minqty = minqty, qty = qty, chance = chance})
    end
  end
 
  --Next: Can we find it by rummaging around in another item?
  for i, item2 in pairs(ItemData.Items) do
    if item2.dropTable ~= nil then
      local qty = 1
      local chance = 0
      local wt = 0
      local totalWt = 0
      for j, loot in pairs(item2.dropTable) do
        totalWt = totalWt + loot[2]
        if loot[1] == item.id then
          wt = loot[2]
          if item2.dropQty ~= nil then qty = item2.dropQty[j] end
        end
      end
 
      if wt > 0 then
        chance = (wt / totalWt) * 100
        local sourceTxt = Icons.Icon({item2.name, type='item'})
        table.insert(dropRows, {source = sourceTxt, type = '[[Chest]]', minqty = 1, qty = qty, chance = chance})
      end
    end
  end
 
  --Finally, let's try just stealing it
  local thiefType = Icons.Icon({"Thieving", type='skill'})
  for i, npc in pairs(SkillData.Thieving) do
    local qty = 1
    local chance = 0
    local wt = 0
    local totalWt = 0
    if npc.lootTable ~= nil then
      for j, loot in pairs(npc.lootTable) do
        totalWt = totalWt + loot[2]
        if loot[1] == item.id then
          wt = loot[2]
        end
      end
      if wt > 0 then
        chance = (wt / totalWt) * 75
        local sourceTxt = Icons.Icon({npc.name, type='thieving'})
        table.insert(dropRows, {source = sourceTxt, type = thiefType, minqty = 1, qty = qty, chance = chance})
      end
    end
  end
 
  --Bonus overtime: Special Fishing table & mining gem table. Also Rags to Riches
  if item.type == 'Gem' then
    local mineType = Icons.Icon({'Mining', type='skill'})
    local thisGemChance = GemTable[item.name].chance
    table.insert(dropRows, {source = '[[Mining#Gems|Gem]]', type = mineType, minqty = 1, qty = 1, chance = thisGemChance})
    local magicType = Icons.Icon({'Magic', type = 'skill'})
    table.insert(dropRows, {source = Icons.Icon({"Rags to Riches I", type="spell"}), type = magicType, minqty = 1, qty = 1, chance = thisGemChance})
    table.insert(dropRows, {source = Icons.Icon({"Rags to Riches II", type="spell"}), type = magicType, minqty = 1, qty = 1, chance = thisGemChance})
  end
 
  if item.fishingCatchWeight ~= nil then
    local fishSource = '[[Fishing#Special|Special]]'
    local fishType = Icons.Icon({'Fishing', type='skill'})
    local thisChance = (item.fishingCatchWeight / specialFishWt) * 100
    table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, chance = thisChance})
  end
 
  if item.type == 'Junk' then
    local fishSource = '[[Fishing#Junk|Junk]]'
    local fishType = Icons.Icon({'Fishing', type='skill'})
    local thisChance = 100 / junkCount
    table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, chance = thisChance})
  end
 
  table.sort(dropRows, function(a, b) return a.qty * a.chance > b.qty * b.chance end)
  for i, data in pairs(dropRows) do
    result = result..buildRow(data.source, data.type, data.minqty, data.qty, data.chance)
  end


  result = result..'\r\n|}'
local ico = {
  return result
["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
end
["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')


function p.getItemLootSourceTable(frame)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
  local itemName = frame.args ~= nil and frame.args[1] or frame
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true))
  local item = p.getItem(itemName)
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
  if item == nil then
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true))
    return "ERROR: No item named "..itemName.." exists in the data module"
  end


  return p._getItemLootSourceTable(item)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
end
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) .. '%')


function p.getEquipmentTable(frame)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
  local args = frame.args ~= nil and frame.args or frame
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
  local type = args.type
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
  local tier = args.tier
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))
  local slotStr = args.slot
  local ammoTypeStr = args.ammoType
  local category = args.category ~= nil and args.category or 'Combat'


  --Find out what Ammo Type we're working with
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
  local ammoType = nil
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
  if ammoTypeStr ~= nil then
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
    if ammoTypeStr == "Arrows" then
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
      ammoType = 0
    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
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
  local slot = nil
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
  if slotStr ~= nil then
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Other')
    slot = Constants.equipmentSlot[slotStr]
  end
  mw.log("Type = "..(type ~= nil and type or '')..", Slot = "..(slot ~= nil and slot or '')..", AmmoType = "..(ammoType ~= nil and ammoType or ''))
 


  --Getting some lists set up here that will be used later
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
  --First, the list of columns used by both weapons & armour
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
  local statColumns = {'slashAttackBonus', 'stabAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'strengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'defenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus'}
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Slayer'] .. ' Bonus Slayer XP')
  --Then the lists for just weapons/just armour
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slayerBonusXP', true) .. '%')
  local weaponStatColumns = {'attackLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
  local armourStatColumns = {'damageReduction', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
  --Then the list of weapon types
  local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}


  local isWeaponType = Shared.contains(weaponTypes, type)
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))
  --Alright, let's start the table by building the shared header
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Level Required')
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'defenceLevelRequired', true))
  if isWeaponType then
    --Weapons have an extra column here for Attack Speed
    result = result..'\r\n!colspan="3"|'
  else
    result = result..'\r\n!colspan="2"|'
  end
  result = result..'\r\n!colspan="5"style="padding:0 0.5em 0 0.5em;"|Attack Bonus'
  result = result..'\r\n!colspan="2"style="padding:0 0.5em 0 0.5em;"|Strength Bonus'
  result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Damage Bonus'
  result = result..'\r\n!colspan="3"style="padding:0 0.5em 0 0.5em;"|Defence Bonus'
  if isWeaponType then
    --Weapons have an extra columns here for "Two Handed?"
    result = result..'\r\n!colspan="1"|'
  else
    --Only armour pieces have DR right now, so ignore that column for weapons
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|Damage Reduction'
  end
  result = result..'\r\n!colspan="3"style="padding:0 0.5em 0 0.5em;"|Levels Required'
  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'
  end
  --Attack bonuses
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --Strength bonuses
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --Defence bonuses
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --Damage Reduction/Defence Req for armour, 2-handed/Attack Req for weapons
  if isWeaponType then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?'
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  else
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  --Then Ranged/Magic requirements
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --And finally Sources
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Sources'


  --And with all the header out of the way, finally time to actually build the table itself.
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
  local itemList = {}
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
  for i, itemBase in pairs(ItemData.Items) do
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Level Required')
    local item = Shared.clone(itemBase)
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedLevelRequired', true))
    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


  table.sort(itemList, function(a, b) return a.id < b.id end)
table.insert(resultPart, '\r\n|-\r\n| colspan="2"|')
  for i, item in pairs(itemList) do
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Level Required')
    if isWeaponType then
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicLevelRequired', true))
      --Building rows for weapons
      result = result..'\r\n|-'
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
      result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.formatnum(item.attackSpeed)
      for j, statName in pairs(statColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if statValue > 0 then
          result = result..'background-color:lightgreen;'
        elseif statValue < 0 then
          result = result..'background-color:lightpink;'
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --That's the first list out of the way, now for 2-Handed
      result = result..'\r\n| style ="text-align: right;"|'
      if item.isTwoHanded then result = result..'Yes' else result = result..'No' end
      --Now the weapon exclusive columns
      for j, statName in pairs(weaponStatColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --Finally, the Sources
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
      result = result..p._getItemSources(item)
    else
      --Building rows for armour
      result = result..'\r\n|-'
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
      for j, statName in pairs(statColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if statValue > 0 then
          result = result..'background-color:lightgreen;'
        elseif statValue < 0 then
          result = result..'background-color:lightpink;'
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --That's the first list out of the way, now for armour specific things
      for j, statName in pairs(armourStatColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if j == 1 then
          if statValue > 0 then
            result = result..'background-color:lightgreen;'
          elseif statValue < 0 then
            result = result..'background-color:lightpink;'
          end
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --Finally, the Sources
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
      result = result..p._getItemSources(item)
    end
  end


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


function p._getItemCategories(item)
function p.getItemDataExport(frame)
  local result = ''
local resultTable = mw.html.create('table')
  if item.category ~= nil then result = result..'[[Category:'..item.category..']]' end
resultTable:addClass('wikitable')
  if item.type ~= nil then result = result..'[[Category:'..item.type..']]' end
resultTable:tag('tr'):addClass('headerRow-0')
  return result
:tag('th'):wikitext('ItemID'):done()
end
:tag('th'):wikitext('ItemName'):done()
 
:tag('th'):wikitext('GPValue'):done()
function p.getItemCategories(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
for i, item in ipairs(ItemData.Items) do
  local item = p.getItem(itemName)
resultTable:tag('tr')
  if item == nil then
:tag('td'):wikitext(item.id):done()
    return "ERROR: No item named "..itemName.." exists in the data module"
:tag('td'):wikitext(item.name):done()
  end
:tag('td'):wikitext(item.sellsFor):done()
 
end
  return p._getItemCategories(item)
return tostring(resultTable)
end
end


return p
return p

Revision as of 20:53, 9 March 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)'}
--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

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 ipairs(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 = {}
	local itemCount = 0
	for i, item in ipairs(ItemData.Items) do
		if checkFunc(item) then
			local newItem = Shared.clone(item)
			newItem.id = i - 1
			itemCount = itemCount + 1
			result[itemCount] = 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 ipairs(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',
			["Platelegs"] = 'Equipment#Platelegs',
			["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')
	if cape == nil then
		return ''
	else
		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
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.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

function p.getItemDataExport(frame)
	local resultTable = mw.html.create('table')
	resultTable:addClass('wikitable')
	resultTable:tag('tr'):addClass('headerRow-0')
		:tag('th'):wikitext('ItemID'):done()
		:tag('th'):wikitext('ItemName'):done()
		:tag('th'):wikitext('GPValue'):done()
	
	for i, item in ipairs(ItemData.Items) do
		resultTable:tag('tr')
			:tag('td'):wikitext(item.id):done()
			:tag('td'):wikitext(item.name):done()
			:tag('td'):wikitext(item.sellsFor):done()
	end
	return tostring(resultTable)
end

return p