Module:Items: Difference between revisions

Add gem link to gem equipment slot
(Added function to show extra text for some item skillboxes)
(Add gem link to gem equipment slot)
(186 intermediate revisions by 8 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 GameData = require('Module:GameData')
local ItemData = mw.loadData('Module:Items/data')
local Constants = require('Module:Constants')
local SkillData = mw.loadData('Module:Skills/data')
local Constants = mw.loadData('Module:Constants/data')
 
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local 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'}
--This is hardcoded, so there's no easy way to scrape it. Hopefully it doesn't change
p.EventItems = {'Christmas Cracker', 'Christmas Coal', 'Christmas Sweater',
local GemTable = {["Topaz"] = {name = 'Topaz', id = 128, chance = 50},  
'Christmas Wreath', 'Candy Cane', 'Santa Hat',
                  ["Sapphire"] = {name = "Sapphire", id = 129, chance = 17.5},  
'Friendship Bracelet', 'Event Clue 1', 'Event Clue 2',
                  ["Ruby"] = {name = "Ruby", id = 130, chance = 17.5},  
'Event Clue 3', 'Event Clue 4', 'Candle', 'Cake Base',
                  ["Emerald"] = {name = "Emerald", id = 131, chance = 10},  
'Magical Flavouring', 'Magical Icing', 'Birthday Cake',
                  ["Diamond"] = {name = "Diamond", id = 132, chance = 5}}
'Purple Party Hat', 'Birthday Token', 'Christmas Present (Yellow)',
--The base chance to receive a gem while mining
'Christmas Present (Blue)', 'Christmas Present (Green)', 'Christmas Present (White)',
local GemChance = .01
'Christmas Present (Purple)', 'Christmas Present (Standard)', 'Event Token - Holiday 2021',
--The number of different fishing junk items
'Holiday Scarf', 'Gingerbread House', 'Gingerbread Man', 'Edible Candy Cane',
local junkCount = 8
'Locked Chest', 'Locked Chest Key', 'Event Token (Holiday 2021)'}
--Items (aside from bars & gems) which can be created via Alt Magic
local AltMagicProducts = {'Rune Essence', 'Bones', 'Holy Dust'}


function p.getItemByID(ID)
return GameData.getEntityByID('items', ID)
end


local specialFishWt = 6721
function p.getItem(name)
local specialFishLoot = {{129, 2000}, {130, 1600}, {131, 1400}, {132, 1000}, {133, 400}, {668, 10}, {669, 10}, {671, 1}, {670, 50}, {121, 250}}
name = string.gsub(name, "%%27", "'")
 
name = string.gsub(name, "'", "'")
function p.buildSpecialFishingTable()
return GameData.getEntityByName('items', name)
  --This shouldn't ever be included in a page
  --This is for generating the above 'specialFishLoot' variable if it ever needs to change
  --To re-run, edit the module, type in "console.log(p.buildSpecialFishingTable())" and copy+paste the result as the new value of the variable
  --Also gives you the total fishing weight for saving time later
  local lootArray = {}
  local totalWt = 0
 
  for i, item in pairs(ItemData.Items) do
    if item.fishingCatchWeight ~= nil then
      totalWt = totalWt + item.fishingCatchWeight
      table.insert(lootArray, '{'..i..', '..item.fishingCatchWeight..'}')
    end
  end
 
  local result = 'local specialFishWt = '..totalWt..'\r\n'
  result = result..'local specialFishLoot = {'..table.concat(lootArray, ', ')..'}'
  return result
end
end


function p.getSpecialAttackByID(ID)
function p.getItems(checkFunc)
  local result = Shared.clone(ItemData.SpecialAttacks[ID + 1])
return GameData.getEntities('items', checkFunc)
  if result ~= nil then
    result.id = ID
  end
  return result
end
end


function p.getItemByID(ID)
function p._canItemUseSlot(item, equipSlot)
  local result = Shared.clone(ItemData.Items[ID + 1])
--Function to easily check if an item can fit in a given equipment slot
  if result ~= nil then
--Ex: p._canItemUseSlot({Bronze Platebody}, 'Platebody') returns true
    result.id = ID
if type(item) == 'string' then
  end
item = p.getItem(item)
  return result
end
return item.validSlots ~= nil and Shared.contains(item.validSlots, equipSlot)
end
end


function p.getItem(name)
function p._getItemEquipSlot(item)
  local result = nil
--Function to return the (non-Passive) equipment slot that an item occupies
  name = string.gsub(name, "'", "'")
if type(item) == 'string' then
  for i, item in pairs(ItemData.Items) do
item = p.getItem(item)
    if(item.name == name) then
end
      result = Shared.clone(item)
if item == nil or item.validSlots == nil then
      --Make sure every item has an id, and account for Lua being 1-index
return 'Invalid'
      result.id = i -1
end
      break
for i, slot in pairs(item.validSlots) do
    end
if slot ~= 'Passive' then
  end
return slot
  return result
end
end
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 item.equipmentStats ~= nil and item.equipmentStats[StatName] ~= nil then
      result = nil
result = item.equipmentStats[StatName]
    else
elseif StatName == 'attackSpeed' and item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon') then
      result = item.attackBonus[1]
-- Item can be equipped as a weapon but has no attack speed, so use default of 4000ms
    end
result = 4000
  elseif StatName == 'slashAttackBonus' then
elseif StatName == 'isTwoHanded' then
    if item.attackBonus == nil then  
if item.validSlots ~= nil and item.occupiesSlots ~= nil then
      result = nil
result = Shared.contains(item.validSlots, 'Weapon') and Shared.contains(item.occupiesSlots, 'Shield')
    else
else
      result = item.attackBonus[2]
result = false
    end
end
  elseif StatName == 'blockAttackBonus' then
elseif string.find(StatName, '^(.+)LevelRequired$') ~= nil and item.equipRequirements ~= nil then
    if item.attackBonus == nil then  
local skillName = Shared.titleCase(string.match(StatName, '^(.+)LevelRequired$'))
      result = nil
if skillName ~= nil then
    else
local skillID = Constants.getSkillID(skillName)
      result = item.attackBonus[3]
if skillID ~= nil then
    end
for i, requirement in ipairs(item.equipRequirements) do
  elseif StatName == 'attackType' then
if requirement.type == "SkillLevel" and requirement.skillID == skillID then
    result = p._getWeaponAttackType(item)
result = requirement.level
  elseif StatName == 'description' then
break
    result = item.description
end
    if result == nil or result == '' then result = 'No Description' end
end
  end
end
  if result == nil and ZeroIfNil then result = 0 end
end
  return result
elseif StatName == 'attackType' then
result = p._getWeaponAttackType(item)
elseif StatName == 'description' then
result = item.customDescription
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))
elseif StatName == 'category' then
-- Some categories have a namespace for some reason, remove it
local _, localID = GameData.getLocalID(result)
return localID
end
if result == nil and ZeroIfNil then result = 0 end
return result
end
end


function p.getItemStat(frame)
function p.getItemValue(item)
  local args = frame.args ~= nil and frame.args or frame
if type(item) == 'string' then
  local ItemName = args[1]
-- Specific check if the item is GP (value of 1)
  local StatName = args[2]
if Shared.compareString('GP', item, true)  
  local ZeroIfNil = args.ForceZero ~= nil and args.ForceZero ~= '' and args.ForceZero ~= 'false'
or Shared.compareString('Gold Pieces', item, true) then
  local formatNum = args.formatNum ~= nil and args.formatNum ~= '' and args.formatNum ~= 'false'
return 1
  local item = p.getItem(ItemName)
end
  if item == nil then
    return "ERROR: No item named "..ItemName.." exists in the data module"
  end
  local result = p._getItemStat(item, StatName, ZeroIfNil)
  if formatNum then result = Shared.formatnum(result) end
  return result
end


function p._getWeaponAttackType(item)
item = p.getItem(item)
  if item.type == 'Weapon' then
end
    return Icons.Icon({'Melee', nolink='true'})
  elseif item.type == 'Ranged Weapon' then
if item then
    return Icons.Icon({'Ranged', type='skill', nolink='true'})
return item.sellsFor
  elseif item.type == 'Magic Staff' or item.type == 'Magic Wand' then
end
    return Icons.Icon({'Magic', type='skill', nolink='true'})
  else
return nil
    return "Invalid"
  end
end
end


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


function p.getPotionTable(frame)
--Gets the value of a given modifier for a given itemg
  local potionName = frame.args ~= nil and frame.args[1] or frame
--asString is false by default, when true it writes the full bonus text
  local tiers = {'I', 'II', 'III', 'IV'}
function p._getItemModifier(item, modifier, skillID, asString)
if asString == nil then asString = false end
if skillID == '' then
skillID = nil
elseif string.find(skillID, ':') == nil then
-- Try to find a skill ID if it looks like a skill name has been passed
skillID = Constants.getSkillID(skillID)
end


  local result = '{| class="wikitable"'
local result = 0
  result = result..'\r\n!Potion!!Tier!!Charges!!Effect'


  local tier1potion = p.getItem(potionName..' I')
if item.modifiers ~= nil and item.modifiers[modifier] ~= nil then
  for i, tier in pairs(tiers) do
if type(item.modifiers[modifier]) == 'table' then
    local tierName = potionName..' '..tier
for i, subVal in Shared.skpairs(item.modifiers[modifier]) do
    local potion = p.getItemByID(tier1potion.id + i - 1)
if subVal[1] == skillID then
    if potion == nil then
result = subVal[2]
      mw.log("Failed to get tier "..tier)
break
    else
end
      result = result..'\r\n|-'
end
      result = result..'\r\n|'..Icons.Icon({tierName, type='item', notext='true', size='60'})
else
      result = result..'||'..'[['..tierName..'|'..tier..']]'
result = item.modifiers[modifier]
      result = result..'||'..potion.potionCharges..'||'..potion.description
end
    end
end
  end


  result = result..'\r\n|}'
if asString then
  return result
if skillID ~= nil then
return Constants._getModifierText(modifier, {skillID, result})
else
return Constants._getModifierText(modifier, result)
end
else
return result
end
end
end


function p.getCreationTable(frame)
function p.hasCombatStats(item)
  local itemName = frame.args ~= nil and frame.args[1] or frame
-- Checks if the combat stat is a valid, non-zero combat stat
  local item = p.getItem(itemName)
-- Ensure that, only in the case where the item is a Familar AND
  if item == nil then
-- the checked stat is summoningMaxhit, the result is ignored.
    return "ERROR: No item named "..itemName.." exists in the data module"
function isNonZeroStat(statName, statVal)
  end
if statName == 'summoningMaxhit' and (p._canItemUseSlot(item, 'Summon1') or p._canItemUseSlot(item, 'Summon2')) then
return false
end
return statVal ~= 0
end


  local skill = ''
if item.equipmentStats ~= nil then
  local specialReq = nil
-- Ensure at least one stat has a non-zero value
  local time = 0
for statName, statVal in pairs(item.equipmentStats) do
  local maxTime = nil
if isNonZeroStat(statName, statVal) then
  local lvl = 0
return true
  local xp = 0
end
  local qty = nil
end
  local req = nil
end
  local result = ''


  local tables = {}
return false
  --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._hasLevelRequirements(item)
    return "Failed to find creation requirements for this (Possibly the module isn't properly updated for this skill)"
--Function true if an item has at least one level requirement to equip
  else
if item.equipRequirements ~= nil then
    return table.concat(tables, '\r\n<br/>')
for idx, requirement in ipairs(item.equipRequirements) do
  end
if requirement.type == 'SkillLevel' and requirement.level > 1 then
return true
end
end
end
return false
end
end


function p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq)
function p.getItemModifier(frame)
  if qty == nil then qty = 1 end
local itemName = frame.args ~= nil and frame.args[1] or frame[1]
  local result = '{|class="wikitable"'
local modName = frame.args ~= nil and frame.args[2] or frame[2]
  if req ~= nil then
local skillName = frame.args ~= nil and frame.args[3] or frame[3]
    result = result..'\r\n!colspan="2"|Item Creation'
local asString = frame.args ~= nil and frame.args[4] or frame[4]
  else
if asString ~= nil then
    result = result..'\r\n!colspan="2"|Item Production'
asString = (string.upper(asString) ~= 'FALSE')
  end
end
  result = result..'\r\n|-\r\n!style="text-align: right;"|Requirements'
  result = result..'\r\n|'..Icons._SkillReq(skill, lvl, true)
  if specialReq ~= nil then result = result..'<br/>'..specialReq end


  if req ~= nil then
local item = p.getItem(itemName)
    result = result..'\r\n|-\r\n!style="text-align: right;"|Materials\r\n|'
if item == nil then
    for i, mat in pairs(req) do
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
      if i > 1 then result = result..'<br/>' end
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
return p._getItemModifier(item, modName, skillName, asString)
end
end


function p._getOtherItemBoxText(item)
function p._getWeaponAttackType(item)
  result = ''
if (item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon')) or
  --For equipment, show the slot they go in
(item.occupiesSlots ~= nil and Shared.contains(item.occupiesSlots, 'Weapon')) then
  if item.equipmentSlot ~= nil then
if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then
    for slotName, i in Shared.skpairs(Constants.equipmentSlot) do
local iconType = item.attackType ~= 'melee' and 'skill' or nil
      if i == item.equipmentSlot then
return Icons.Icon({Shared.titleCase(item.attackType), type=iconType, nolink='true'})
        result = result..'\r\n|-\r\n|Equipment Slot: '..slotName
end
        break
end
      end
return 'Invalid'
    end
  end
  --For weapons with a special attack, show the details
  if item.hasSpecialAttack then
    local spAtt = p.getSpecialAttackByID(item.specialAttackID)
    result = result..'\r\n|-\r\n|Special Attack:'
    result = result..'\r\n* '..spAtt.chance..'% chance for '..spAtt.name..':'
    result = result..'\r\n** '..spAtt.description
  end
  --For potions, show the number of charges
  if item.potionCharges ~= nil then
    result = result..'\r\n|-\r\n|Charges: '..item.potionCharges
  end
  --For food, show how much it heals for
  if item.healsFor ~= nil then
    result = result..'\r\n|-\r\n|Heals for: '..Icons.Icon({"Hitpoints", type="skill", notext="true"})..' '..(item.healsFor * 10)
  end
  return result
end
end


function p.getOtherItemBoxText(frame)
function p.getWeaponAttackType(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
local itemName = frame.args ~= nil and frame.args[1] or frame
  local item = p.getItem(itemName)
local item = p.getItem(itemName)
  local asList = false
if item == nil then
  if frame.args ~= nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
    asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
end
  end
return p._getWeaponAttackType(item)
  if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module"
  end
 
  return p._getOtherItemBoxText(item, asList)
end
end


function p._getItemSources(item, asList)
local statChangeDefs = {
  local result = nil
{
  local lineArray = {}
stat = 'stabAttackBonus',
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Stab Bonus'
},
{
stat = 'slashAttackBonus',
suffix =  ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Slash Bonus'
},
{
stat = 'blockAttackBonus',
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Block Bonus'
},
{
stat = 'meleeStrengthBonus',
suffix = ' ' .. Icons.Icon({'Strength', type='skill', notext=true}) .. ' Strength Bonus'
},
{
stat = 'rangedStrengthBonus',
suffix =  ' ' .. Icons.Icon({'Ranged', type='skill', notext=true}) .. ' Strength Bonus'
},
{
stat = 'magicStrengthBonus',
suffix = '% ' .. Icons.Icon({'Magic', type='skill', notext=true}) .. ' Damage Bonus'
},
{
stat = 'meleeDefenceBonus',
suffix = ' ' .. Icons.Icon({'Defence', type='skill', notext=true}) .. ' Defence Bonus' },
{
stat = 'rangedDefenceBonus',
suffix = ' ' .. Icons.Icon({'Ranged', type='skill', notext=true}) .. ' Defence Bonus'
},
{
stat = 'magicDefenceBonus',
suffix = ' ' .. Icons.Icon({'Magic', type='skill', notext=true}) .. ' Defence Bonus'
},
{
stat = 'damageReduction',
suffix = '% Damage Reduction'
},
{
stat = 'levelRequired',
suffix = ' Level Required'
}
}


  --Alright, time to go through all the ways you can get an item...
-- Produces a list of stat & modifier changes between two items of equipmednt
  --First up: Can we kill somebody and take theirs?
function p.getStatChangeString(item1, item2)
  local killStr = ''
local changeArray = {}
  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 equipStats = {
  --While we're here, check for upgrades, cooking, and growing
type(item1.equipmentStats) == 'table' and item1.equipmentStats or {},
  local lootStr = ''
type(item2.equipmentStats) == 'table' and item2.equipmentStats or {}
  local upgradeStr = ''
}
  local cookStr = ''
for i, statDef in ipairs(statChangeDefs) do
  local burnStr = ''
local val1, val2 = 0, 0
  local growStr = ''
if statDef.stat == 'levelRequired' then
  local count2 = 0
-- Iterate over equipment stats for both items, determining level requirements
  count1 = 0
local levelReqs = {}
  for i, item2 in pairs(ItemData.Items) do
for itemNum, item in ipairs({item1, item2}) do
    if item2.dropTable ~= nil then
levelReqs[itemNum] = {}
      for j, loot in pairs(item2.dropTable) do
if item.equipRequirements ~= nil then
        if loot[1] == item.id then
for j, req in ipairs(item.equipRequirements) do
          count1 = count1 + 1
if req.type == 'SkillLevel' then
          if string.len(lootStr) > 0 then
levelReqs[itemNum][req.skillID] = req.level
            lootStr = lootStr..','
end
            if count1 % 3 == 1 and count1 > 1 then lootStr = lootStr..'<br/>' end
end
            lootStr = lootStr..Icons.Icon({item2.name, type="item", notext="true"})
end
          else
end
            lootStr = lootStr..'Opening: '..Icons.Icon({item2.name, type="item", notext="true"})
-- Iterate over all skills, checking if there are requirements for these in either skill
          end
for j, skillData in ipairs(GameData.rawData.skillData) do
        end
local skillID = skillData.skillID
      end
val1, val2 = levelReqs[1][skillID] or 0, levelReqs[2][skillID] or 0
    end
if val1 ~= val2 then
    if item2.trimmedItemID == item.id then
table.insert(changeArray, Shared.numStrWithSign(val1 - val2) .. ' ' .. Icons.Icon({skillData.data.name, type='skill', notext=true}) .. (statDef.suffix or ''))
          count2 = count2 + 1
end
        if string.len(upgradeStr) > 0 then
end
          upgradeStr = upgradeStr..','
else
          if count2 % 3 == 1 and count2 > 1 then upgradeStr = upgradeStr..'<br/>' end
-- Equipment stats
          upgradeStr = upgradeStr..Icons.Icon({item2.name, type="item", notext="true"})
val1, val2 = equipStats[1][statDef.stat] or 0, equipStats[2][statDef.stat] or 0
        else
if val1 ~= val2 then
          upgradeStr = upgradeStr..'Upgrading: '..Icons.Icon({item2.name, type="item", notext="true"})
table.insert(changeArray, Shared.numStrWithSign(val1 - val2) .. (statDef.suffix or ''))
        end
end
    end
end
    if item2.cookedItemID == item.id then
end
        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?
-- Include differences in modifiers
  local thiefStr = ''
local modDiff = Constants.getModifiersText(Constants.getModifiersDifference(item2.modifiers, item1.modifiers))
  for i, npc in pairs(SkillData.Thieving) do
if modDiff ~= nil and modDiff ~= '' then
    if npc.lootTable ~= nil then
table.insert(changeArray, modDiff)
      for j, loot in pairs(npc.lootTable) do
end
        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
return table.concat(changeArray, '<br/>')
  --SmithCheck:
end
  if item.smithingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
  end


  --CraftCheck:
function p._getOtherItemBoxText(item)
  if item.craftingLevel ~= nil then
local resultPart = {}
    table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
--For equipment, show the slot they go in
  end
local isPassive = false
if item.validSlots ~= nil then
local slotLinkMap = {
["Helmet"] = 'Helmets',
["Platebody"] = 'Platebodies',
["Platelegs"] = 'Platelegs',
["Boots"] = 'Boots',
["Weapon"] = 'Weapons',
["Shield"] = 'Shields',
["Amulet"] = 'Amulets',
["Ring"] = 'Rings',
["Gloves"] = 'Gloves',
["Quiver"] = 'Ammunition',
["Cape"] = 'Capes',
["Consumable"] = 'Consumables',
["Passive"] = 'Combat Passive Slot',
["Summon1"] = 'Summoning',
["Summon2"] = 'Summoning',
["Gem"] = "Gems_(Equipment)"
}
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
if slot == 'Passive' then
isPassive = true
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.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
table.insert(resultPart, "\r\n|-\r\n|'''Special Attack:'''")
for i, spAttID in ipairs(item.specialAttacks) do
local spAtt = GameData.getEntityByID('attacks', spAttID)
if spAtt ~= nil then
local spAttChance = spAtt.defaultChance
if type(item.overrideSpecialChances) == 'table' and item.overrideSpecialChances[i] ~= nil then
spAttChance = item.overrideSpecialChances[i]
end
local spAttDesc = string.gsub(spAtt.description, '<Attack> ', '')
table.insert(resultPart, '\r\n* ' .. spAttChance .. '% chance for ' .. spAtt.name .. ':')
table.insert(resultPart, '\r\n** ' .. spAttDesc)
end
end
end
-- For Summoning combat familiars, show the max hit
if item.equipmentStats ~= nil and item.equipmentStats.summoningMaxhit ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Max Hit:''' " .. Shared.formatnum(item.equipmentStats.summoningMaxhit * 10))
end
--For potions, show the number of charges
if item.charges ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Charges:''' "..item.charges)
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 that provide runes, show which runes are provided
if item.providedRunes ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Runes Provided:''' ")
local runeLines = {}
local sortVal = ''
for j, runePair in pairs(item.providedRunes) do
local runeID = runePair.id
local qty = runePair.quantity
local rune = p.getItemByID(runeID)
sortVal = sortVal..rune.name..qty
table.insert(runeLines, Icons.Icon({rune.name, type='item', qty=qty}))
end
table.insert(resultPart, table.concat(runeLines, ', '))
end
--For items with modifiers, show what those are
if item.modifiers ~= nil and not Shared.tableIsEmpty(item.modifiers) then
table.insert(resultPart, "\r\n|-\r\n|'''Modifiers:'''\r\n")
if isPassive then
table.insert(resultPart, '<span style="color:green">Passive:</span><br/>')
end
table.insert(resultPart, Constants.getModifiersText(item.modifiers, true, false, 10))
end
return table.concat(resultPart)
end


  --FletchCheck:
function p.getOtherItemBoxText(frame)
  if item.fletchingLevel ~= nil then
local itemName = frame.args ~= nil and frame.args[1] or frame
    table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
local item = p.getItem(itemName)
  end
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end


  --RunecraftCheck:
return p._getOtherItemBoxText(item)
  if item.runecraftingLevel ~= nil then
end
    table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
  end


  --MineCheck:
function p._getItemCategories(item)
  if item.miningLevel ~= nil then
local resultPart = {}
    table.insert(lineArray, Icons._SkillReq("Mining", item.miningLevel))
local isEquipment = item.validSlots ~= nil or item.occupiesSlots ~= nil or item.equipmentStats ~= nil
  end
local category = p._getItemStat(item, 'category', false)
if category ~= nil and category ~= 'Skills' then
table.insert(resultPart, '[[Category:'..category..']]')
end
if item.type ~= nil then
table.insert(resultPart, '[[Category:'..item.type..']]')
end
if isEquipment and item.tier ~= nil then
table.insert(resultPart, '[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]')
end
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) 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


  --FishCheck:
function p.getItemCategories(frame)
  if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
local itemName = frame.args ~= nil and frame.args[1] or frame
    table.insert(lineArray, Icons._SkillReq("Fishing", 1))
local item = p.getItem(itemName)
  elseif item.fishingLevel ~= nil then
if item == nil then
    table.insert(lineArray, Icons._SkillReq("Fishing", item.fishingLevel))
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
  end
end


  --HerbCheck:
return p._getItemCategories(item)
  if item.herbloreMasteryID ~= nil then
end
    local potionData = SkillData.Herblore.ItemData[item.herbloreMasteryID + 1].herbloreLevel
    table.insert(lineArray, Icons._SkillReq("Herblore", potionData))
  end


  --Finally there are some weird exceptions:
function p.getItemGrid(frame)
  --Coal can be acquired via firemaking
local resultPart = {}
  if item.name == "Coal Ore" then
table.insert(resultPart, '{|')
    table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
for i, item in ipairs(GameData.rawData.items) do
  end
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


  --Gems can be acquired from mining, fishing, and alt. magic
function p.getEquipRequirementRow(req)
  if item.type == 'Gem' then
local result = ""
    table.insert(lineArray, Icons._SkillReq("Fishing", 1))
if req.type == "SkillLevel" then
    table.insert(lineArray, Icons._SkillReq("Mining", 1))
local skillName = Constants.getSkillName(req.skillID)
    table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
local skillIcon = Icons.Icon({skillName, type='skill', notext=true})
  end
result = '\r\n!style="text-align:right;"| '..skillIcon..' Level Required'
 
result = result..'\r\n|style="text-align:right;"| '..req.level
  --Bars and some other stuff can also be acquired via Alt. Magic
elseif req.type == "DungeonCompletion" then
  if type == 'Bar' or Shared.contains(AltMagicProducts, item.name) then
local dungeonName = GameData.getEntityByID("dungeons", req.dungeonID).name
    table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
local dungeonIcon = Icons.Icon({dungeonName, type="dungeon", notext=true})
  end
result = '\r\n!style="text-align:right;"| '..dungeonIcon..' Completions'
 
result = result..'\r\n|style="text-align:right;"| '..req.count
  --Chapeau Noir & Bobby's Pocket are special Thieving items
elseif req.type == "Completion" then
  if item.name == "Chapeau Noir" or item.name == "Bobby&apos;s Pocket" then
local ns = GameData.getEntityByName('namespaces', req.namespace)
    table.insert(lineArray, Icons._SkillReq("Thieving", 1))
if ns == nil then
  end
return '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid namespace for completion requirement "' .. req.namespace .. '"')
 
else
  --Rhaelyx pieces are also special
result = '\r\n!style="text-align:right;"| ' .. ns.displayName .. ' Completion'
  if item.name == 'Jewel of Rhaelyx' then
result = result .. '\r\n|style="text-align:right;"| ' .. req.percent .. '%'
    local rhaStr = 'Any action in: '
end
    rhaStr = rhaStr..Icons.Icon({'Firemaking', type = 'skill'})..', '..Icons.Icon({'Cooking', type = 'skill'})..', '..Icons.Icon({'Smithing', type = 'skill'})..',<br/>'
else
    rhaStr = rhaStr..Icons.Icon({'Fletching', type = 'skill'})..', '..Icons.Icon({'Crafting', type = 'skill'})..', '..Icons.Icon({'Runecrafting', type = 'skill'})..',<br/>'
return '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid equip requirement type "' .. req.type .. '"')
    rhaStr = rhaStr..Icons.Icon({'Herblore', type='skill'})
end
    table.insert(lineArray, rhaStr)
return result
  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 = ''
  if asList then
    result = '* '..table.concat(lineArray, "\r\n* ")
  else
    result = table.concat(lineArray, "<br/>")
    result = '<div style="max-width:180px;text-align:right">'..result..'</div>'
  end
  return result
end
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 Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end


function p.getItemSources(frame)
local ico = {
  local itemName = frame.args ~= nil and frame.args[1] or frame
["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
  local item = p.getItem(itemName)
["Combat"] = Icons.Icon({'Combat', notext=true}),
  local asList = false
["Defence"] = Icons.Icon({'Defence', type='skill', notext=true}),
  if frame.args ~= nil then
["Magic"] = Icons.Icon({'Magic', type='skill', notext=true}),
    asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}),
  end
["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}),
  if item == nil then
["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true})
    return "ERROR: No item named "..itemName.." exists in the data module"
}
  end
local reqCount = item.equipRequirements ~= nil and Shared.tableCount(item.equipRequirements) or 0
local emptyRow = '\r\n!colspan="2"|'
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')


  return p._getItemSources(item, asList)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Speed')
end
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))


--Brute forcing some item uses to make things easier
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Type')
local itemUseArray = {
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackType'))
  Combat = {},
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Damage Reduction')
  Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'damageReduction', true) .. '%')
  Crafting = {'Crown of Rhaelyx'},
  Farming = {'Compost', 'Weird Gloop', 'Bob&apos;s Rake'},
  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)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
  local useArray = {}
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true))
  --Another fun one. This time getting all the different possible ways an item can be used
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))


  --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;"| ' .. ico['Combat'] .. ' Stab Bonus')
  if item.herbloreMasteryID ~= nil then
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'stabAttackBonus', true))
    table.insert(itemUseArray[potionUseArray[item.herbloreMasteryID]], item.name)
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
  end
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', 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;"| ' .. ico['Combat'] .. ' Slash Bonus')
  if item.equipmentSlot ~= nil or Shared.contains(itemUseArray.Combat, item.name) then
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
    table.insert(useArray, '* '..Icons.Icon({'Combat'}))
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Equip Requirements')
  end
  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['Combat'] .. ' Block Bonus')
  local canUpgrade = false
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
  local canCraft = false
if reqCount > 0 then
  local canFletch = false
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[1]))
  local canRunecraft = false
else
  local canHerblore = false
table.insert(resultPart, '\r\n|colspan=2 style="text-align:right"|None')
  if item.trimmedItemID ~= nil then
end
    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['Ranged'] .. ' Attack Bonus')
        for j, req in pairs(item2.craftReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
          if req.id == item.id then
if reqCount > 1 then
            canCraft = true
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[2]))
            break
else
          end
table.insert(resultPart, emptyRow)
        end
end
      end


      if item2.fletchReq ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
        for j, req in pairs(item2.fletchReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
          if req.id == item.id then
if reqCount > 2 then
            canFletch = true
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[3]))
            break
else
          end
table.insert(resultPart, emptyRow)
        end
end
      end


      if item2.runecraftReq ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
        for j, req in pairs(item2.runecraftReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true))
          if req.id == item.id then
if reqCount > 3 then
            canRunecraft = true
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[4]))
            break
else
          end
table.insert(resultPart, emptyRow)
        end
end
      end


      if item2.herbloreReq ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
        for j, req in pairs(item2.herbloreReq) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
          if req.id == item.id then
if reqCount > 4 then
            canHerblore = true
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[5]))
            break
else
          end
table.insert(resultPart, emptyRow)
        end
end
      end
    end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Two Handed?')
  end
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. (p._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No'))
  if canUpgrade then
if reqCount > 5 then
    table.insert(useArray, '* [[Upgrading Items]]')
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[6]))
  end
else
   
table.insert(resultPart, emptyRow)
  --Cooking
end
  if item.cookedItemID ~= nil or Shared.contains(itemUseArray.Cooking, item.name) then
    table.insert(useArray, '* '..Icons.Icon({'Cooking', type='skill'}))
--Add extra rows at the end for items that have more than 3 different requirements
  end
if reqCount > 6 then
  --Crafting
for i = 7, reqCount, 1 do
  if canCraft or Shared.contains(itemUseArray.Crafting, item.name) then
table.insert(resultPart,"\r\n|-")
    table.insert(useArray, '* '..Icons.Icon({'Crafting', type='skill'}))
table.insert(resultPart, emptyRow)
  end
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[i]))
  --Farming
end
  if item.grownItemID ~= nil or Shared.contains(itemUseArray.Farming, item.name) then
end
    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|}')
  --Except Max Skillcape, which is tied to all skills. (And so is the Signet Ring)
return table.concat(resultPart)
  --And combat skillcapes, since combat skills don't get special treatment
end
 
  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
function p.getArmourStatsBox(frame)
  if item.name == 'Charge Stone of Rhaelyx' then
local itemName = frame.args ~= nil and frame.args[1] or frame
    table.insert(useArray, '* Powering '..Icons.Icon({'Crown of Rhaelyx', type='item'}))
local item = p.getItem(itemName)
  end
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end


  return table.concat(useArray,'\r\n')
local ico = {
end
["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 reqCount = item.equipRequirements ~= nil and Shared.tableCount(item.equipRequirements) or 0
local emptyRow = '\r\n!colspan="2"|'
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.getItemUses(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._getItemUses(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._getItemLootSourceTable(item)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
  local result = '{| class="wikitable sortable stickyHeader"'
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true))
  result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
  result = result..'\r\n!Source!!Source Type!!Quantity!!Chance'
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))


  --Set up function for adding rows
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
  local buildRow = function(source, type, minqty, qty, chance)
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true))
    if minqty == nil then minqty = 1 end
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
    local rowTxt = '\r\n|-'
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
    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
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
    if qty ~= minqty then rowTxt = rowTxt..' - '..qty end
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true))
    rowTxt = rowTxt..'\r\n|style ="text-align: right;"|'..Shared.round(chance, 2, 2)..'%'
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Equip Requirements')
    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?
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
  for i, item2 in pairs(ItemData.Items) do
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true))
    if item2.dropTable ~= nil then
if reqCount > 0 then
      local qty = 1
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[1]))
      local chance = 0
else
      local wt = 0
table.insert(resultPart, '\r\n|colspan=2 style="text-align:right"|None')
      local totalWt = 0
end
      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
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
        chance = (wt / totalWt) * 100
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true))
        local sourceTxt = Icons.Icon({item2.name, type='item'})
if reqCount > 1 then
        table.insert(dropRows, {source = sourceTxt, type = '[[Chest]]', minqty = 1, qty = qty, chance = chance})
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[2]))
      end
else
    end
table.insert(resultPart, emptyRow)
  end
end


  --Finally, let's try just stealing it
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
  local thiefType = Icons.Icon({"Thieving", type='skill'})
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
  for i, npc in pairs(SkillData.Thieving) do
if reqCount > 2 then
    local qty = 1
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[3]))
    local chance = 0
else
    local wt = 0
table.insert(resultPart, emptyRow)
    local totalWt = 0
end
    if npc.lootTable ~= nil then
      for j, loot in pairs(npc.lootTable) do
--Add extra rows at the end for items that have more than 3 different requirements
        totalWt = totalWt + loot[2]
if reqCount > 3 then
        if loot[1] == item.id then
for i = 4, reqCount, 1 do
          wt = loot[2]
table.insert(resultPart, "\r\n|-")
        end
table.insert(resultPart, emptyRow)
      end
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[i]))
      if wt > 0 then
end
        chance = (wt / totalWt) * 75
end
        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
table.insert(resultPart, '\r\n|}')
  if item.type == 'Gem' then
return table.concat(resultPart)
    local mineType = Icons.Icon({'Mining', type='skill'})
end
    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
function p.getItemDataExport(frame)
    local fishSource = '[[Fishing#Junk|Junk]]'
local resultTable = mw.html.create('table')
    local fishType = Icons.Icon({'Fishing', type='skill'})
resultTable:addClass('wikitable')
    local thisChance = 100 / junkCount
resultTable:tag('tr'):addClass('headerRow-0')
    table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, chance = thisChance})
:tag('th'):wikitext('ItemID'):done()
  end
:tag('th'):wikitext('ItemName'):done()
 
:tag('th'):wikitext('GPValue'):done()
  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|}'
for i, item in ipairs(GameData.rawData.items) do
  return result
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
end


function p.getItemLootSourceTable(frame)
--Returns the expansion icon for the item if it has one
  local itemName = frame.args ~= nil and frame.args[1] or frame
function p.getExpansionIcon(frame)
  local item = p.getItem(itemName)
local itemName = frame.args ~= nil and frame.args[1] or frame
  if item == nil then
local item = p.getItem(itemName)
    return "ERROR: No item named "..itemName.." exists in the data module"
if item == nil then
  end
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end


  return p._getItemLootSourceTable(item)
return Icons.getExpansionIcon(item.id)
end
end


function p.getEquipmentTable(frame)
function p.buildSmithableArmourNav(frame)
  local args = frame.args ~= nil and frame.args or frame
local resultPart = {}
  local type = args.type
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
  local tier = args.tier
table.insert(resultPart, '\r\n!colspan = 2 style="background-color:#275C87;color:#FFFFFF;min-width:730px;"|')
  local slotStr = args.slot
table.insert(resultPart, Icons.Icon({'Smithing', type='skill', notext=true}))
  local ammoTypeStr = args.ammoType
table.insert(resultPart, ' Smithable Armour Sets')
  local category = args.category ~= nil and args.category or 'Combat'


  --Find out what Ammo Type we're working with
local metalTypes = {'Bronze', 'Iron', 'Steel', 'Mithril', {'Adamant', 'Adamantite'}, {'Rune', 'Runite'}, {'Dragon', 'Dragonite'},
  local ammoType = nil
{'Corundum', 'Corundumite', TotH = true}, {'Augite', 'Augite', TotH = true}, {'Divine', 'Divinite', TotH = true}}
  if ammoTypeStr ~= nil then
local pieces = {"Helmet", "Platebody", "Platelegs", "Boots", "Shield"}
    if ammoTypeStr == "Arrows" then
for i, metal in ipairs(metalTypes) do
      ammoType = 0
local metalName, barName
    elseif ammoTypeStr == 'Bolts' then
local isTotH = false
      ammoType = 1
if type(metal) == 'table' then
    elseif ammoTypeStr == 'Javelins' then
metalName = metal[1]
      ammoType = 2
barName = metal[2]..' Bar'
    elseif ammoTypeStr == 'Throwing Knives' then
isTotH = metal.TotH ~= nil and metal.TotH
      ammoType = 3
else
    end
metalName = metal
  end
barName = metal..' Bar'
end
table.insert(resultPart, '\r\n|-\r\n!')
if isTotH then
table.insert(resultPart, Icons.TotH())
end
table.insert(resultPart, Icons.Icon({barName, type="item", notext=true}))
table.insert(resultPart, " "..metalName)
table.insert(resultPart, "\r\n|")


  --Find out what slot we're working with
for j, piece in ipairs(pieces) do
  local slot = nil
if j > 1 then
  if slotStr ~= nil then
table.insert(resultPart, ' • ')
    slot = Constants.equipmentSlot[slotStr]
end
  end
table.insert(resultPart, '<span style="display:inline-block">')
  mw.log("Type = "..(type ~= nil and type or '')..", Slot = "..(slot ~= nil and slot or '')..", AmmoType = "..(ammoType ~= nil and ammoType or ''))
table.insert(resultPart, Icons.Icon({metalName..' '..piece, piece, type='item'}))
 
if isTotH then
table.insert(resultPart, ' '..Icons.Icon({'(I) '..metalName..' '..piece, '(I)', type='item'}))
table.insert(resultPart, ' '..Icons.Icon({'(P) '..metalName..' '..piece, '(P)', type='item'}))
else
table.insert(resultPart, ' '..Icons.Icon({'(S) '..metalName..' '..piece, '(S)', type='item'}))
table.insert(resultPart, ' '..Icons.Icon({'(G) '..metalName..' '..piece, '(G)', type='item'}))
end
table.insert(resultPart, '</span>')
end
end


  --Getting some lists set up here that will be used later
table.insert(resultPart, '\r\n|}')
  --First, the list of columns used by both weapons & armour
return table.concat(resultPart)
  local statColumns = {'slashAttackBonus', 'stabAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'strengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'defenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus'}
end
  --Then the lists for just weapons/just armour
  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)
function p.buildCraftableArmourNav(frame)
 
local resultPart = {}
  --Alright, let's start the table by building the shared header
table.insert(resultPart, '{| class="wikitable mw-collapsible"')
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
table.insert(resultPart, '\r\n!colspan = 2 style="background-color:#275C87;color:#FFFFFF;min-width:730px;"|')
  if isWeaponType then
table.insert(resultPart, Icons.Icon({'Crafting', type='skill', notext=true}))
    --Weapons have an extra column here for Attack Speed
table.insert(resultPart, ' Craftable Armour Sets')
    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.
local leatherTypes = {'Leather', 'Hard Leather'}
  local itemList = {}
local leatherPieces = {"Cowl", "Body", "Chaps", "Gloves", "Vambraces", "Boots"}
  for i, itemBase in pairs(ItemData.Items) do
table.insert(resultPart, '\r\n|-\r\n!')
    local item = Shared.clone(itemBase)
table.insert(resultPart, Icons.Icon({'Leather', type='item', notext=true}))
    item.id = i - 1
table.insert(resultPart, ' Leather')
    local listItem = false
for i, material in pairs(leatherTypes) do
    if isWeaponType then
if i > 1 then table.insert(resultPart, '\r\n|-\r\n!Hard Leather') end
    listItem = item.type == type and item.category == category
table.insert(resultPart, '\r\n|')
      if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
for j, piece in ipairs(leatherPieces) do
    else
if j > 1 then
      --Now for handling armour
table.insert(resultPart, ' • ')
      if type == "Armour" or type == "Melee" then
end
        listItem = item.defenceLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Armour')
table.insert(resultPart, Icons.Icon({material..' '..piece, piece, type='item'}))
      elseif type == "Ranged Armour" or type == "Ranged" then
end
        listItem = item.rangedLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
end
      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)
local materialTypes = {{'Green D-hide', 'Green Dragonhide'}, {'Blue D-hide', 'Blue Dragonhide'}, {'Red D-hide', 'Red Dragonhide'}, {'Black D-hide', 'Black Dragonhide'},
  for i, item in pairs(itemList) do
{'Elderwood', 'Elderwood Logs', TotH = true}, {'Revenant', 'Revenant Logs', TotH = true}, {'Carrion', 'Carrion Logs', TotH = true}}
    if isWeaponType then
local pieces = {"Body", "Chaps", "Vambraces", "Shield"}
      --Building rows for weapons
for i, material in ipairs(materialTypes) do
      result = result..'\r\n|-'
local isTotH = false
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
local craftName = material[1]
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
local matName = material[2]
      result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.formatnum(item.attackSpeed)
isTotH = material.TotH ~= nil and material.TotH
      for j, statName in pairs(statColumns) do
table.insert(resultPart, '\r\n|-\r\n!')
        local statValue = p._getItemStat(item, statName, true)
if isTotH then
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
table.insert(resultPart, Icons.TotH())
        if statValue > 0 then
end
          result = result..'background-color:lightgreen;'
table.insert(resultPart, Icons.Icon({matName, type="item", notext=true}))
        elseif statValue < 0 then
table.insert(resultPart, " "..craftName)
          result = result..'background-color:lightpink;'
table.insert(resultPart, "\r\n|")
        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|}'
for j, piece in ipairs(pieces) do
  return result
if j > 1 then
end
table.insert(resultPart, ' • ')
end
table.insert(resultPart, '<span style="display:inline-block">')
table.insert(resultPart, Icons.Icon({craftName..' '..piece, piece, type='item'}))
table.insert(resultPart, ' '..Icons.Icon({'(U) '..craftName..' '..piece, '(U)', type='item'}))
table.insert(resultPart, '</span>')
end
end


function p._getItemCategories(item)
table.insert(resultPart, '\r\n|}')
  local result = ''
return table.concat(resultPart)
  if item.category ~= nil then result = result..'[[Category:'..item.category..']]' end
  if item.type ~= nil then result = result..'[[Category:'..item.type..']]' end
  return result
end
end


function p.getItemCategories(frame)
function p.getLifestealWeapons()
  local itemName = frame.args ~= nil and frame.args[1] or frame
local items = p.getItems(function(item)
  local item = p.getItem(itemName)
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
  if item == nil then
for i, spAttID in ipairs(item.specialAttacks) do
    return "ERROR: No item named "..itemName.." exists in the data module"
local spAtt = GameData.getEntityByID('attacks', spAttID)
  end
if spAtt ~= nil then
 
return spAtt.lifesteal > 0
  return p._getItemCategories(item)
end
end
end
return false
end)
for i, item in ipairs(items) do
mw.log(item.name)
end
end
end


return p
return p
915

edits