Anonymous

Module:Items: Difference between revisions

From Melvor Idle
60,997 bytes removed ,  11 October 2021
Fixed check for WeaponAttackType for throwing knives and javelins
('Item Upgrade' in upgrade table now links to Upgrading Items page)
(Fixed check for WeaponAttackType for throwing knives and javelins)
(39 intermediate revisions by 4 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 Magic = require('Module:Magic')
local Areas = require('Module:CombatAreas')
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', 'Abnormal Log', 'Red Herring', 'Cool Glasses'}
local OtherShopItems = {'Cooking Gloves', 'Mining Gloves', 'Gem Gloves', 'Smithing Gloves', 'Thieving Gloves'}
p.OtherShopItems = {'Cooking Gloves', 'Mining Gloves', 'Gem Gloves', 'Smithing Gloves', 'Thieving Gloves'}
--This is hardcoded, so there's no easy way to scrape it. Hopefully it doesn't change
--This is hardcoded, so there's no easy way to scrape it. Hopefully it doesn't change
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
--The number of different fishing junk items
local junkCount = 8
p.junkCount = 8
--Items (aside from bars & gems) which can be created via Alt Magic
--Items (aside from bars & gems) which can be created via Alt Magic
local AltMagicProducts = {'Rune Essence', 'Bones', 'Holy Dust'}
p.AltMagicProducts = {'Rune Essence', 'Bones', 'Holy Dust'}
--The kinds of gloves with cost & charges
--The kinds of gloves with cost & charges
local GloveTable = {['Cooking Gloves'] = {cost=50000, charges=500},
p.GloveTable = {['Cooking Gloves'] = {cost=50000, charges=500},
                     ['Mining Gloves'] = {cost=75000, charges=500},
                     ['Mining Gloves'] = {cost=75000, charges=500},
                     ['Smithing Gloves'] = {cost=100000, charges=500},
                     ['Smithing Gloves'] = {cost=100000, charges=500},
Line 53: Line 54:
   local result = 'p.specialFishWt = '..totalWt..'\r\n'
   local result = 'p.specialFishWt = '..totalWt..'\r\n'
   result = result..'p.specialFishLoot = {'..table.concat(lootArray, ', ')..'}'
   result = result..'p.specialFishLoot = {'..table.concat(lootArray, ', ')..'}'
  return result
end
function p.getSpecialAttackByID(ID)
  local result = Shared.clone(ItemData.SpecialAttacks[ID + 1])
  if result ~= nil then
    result.id = ID
  end
   return result
   return result
end
end
Line 104: Line 97:
   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]
  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
     else
       result = item.attackBonus[1]
       result = false
     end
     end
   elseif StatName == 'slashAttackBonus' then
   elseif string.find(StatName, '^(.+)LevelRequired$') ~= nil and item.equipRequirements ~= nil and item.equipRequirements.Level ~= 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[2]
      if skillID ~= nil then
    end
        result = item.equipRequirements.Level[skillID]
  elseif StatName == 'blockAttackBonus' then
      end
     if item.attackBonus == nil then  
       result = nil
    else
      result = item.attackBonus[3]
     end
     end
   elseif StatName == 'attackType' then
   elseif StatName == 'attackType' then
Line 127: Line 119:
     result = item.description
     result = item.description
     if result == nil or result == '' then result = 'No Description' end
     if result == nil or result == '' then result = 'No Description' end
  elseif StatName == 'completionReq' then
    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)
   end
   end
   if result == nil and ZeroIfNil then result = 0 end
   if result == nil and ZeroIfNil then result = 0 end
Line 140: Line 140:
   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)
Line 147: Line 147:
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
 
 
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"
   end
   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 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] == skill then
    if potion == nil then
           result = subVal[2]
      mw.log("Failed to get tier "..tier)
           break
    else
      result = result..'\r\n|-'
      result = result..'\r\n|'..Icons.Icon({tierName, type='item', notext='true', size='60'})
      result = result..'||'..'[['..tierName..'|'..tier..']]'
      result = result..'||'..potion.potionCharges..'||'..potion.description
    end
  end
 
  result = result..'\r\n|}'
  return result
end
 
function p._getCreationTable(item)
  local skill = ''
  local specialReq = nil
  local time = 0
  local maxTime = nil
  local lvl = 0
  local xp = 0
  local qty = nil
  local req = nil
  local result = ''
 
  local tables = {}
  --First figure out what skill is used to make this...
   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
    if item.name == 'Arrow Shafts' then
      --Arrow Shafts get special (weird) treatment
      req = '1 of any [[Log]]'
      qty = '15 - 135'
    end
    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.miningID ~= nil then
    skill = 'Mining'
    lvl = SkillData.Mining[item.masteryID[2] + 1].level
    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
  --A couple special exceptions for Alt Magic
  --Not Gems or Bars though since those have their own separate thing
  if item.name == 'Rune Essence' then
    table.insert(tables, p.buildAltMagicTable('Just Learning'))
  elseif item.name == 'Bones' then
    table.insert(tables, p.buildAltMagicTable('Bone Offering')) 
  elseif item.name == 'Holy Dust' then
    table.insert(tables, p.buildAltMagicTable('Blessed Offering'))
  end
 
  if Shared.tableCount(tables) == 0 then
    return ""
  else
    return table.concat(tables, '\r\n')
  end
end
 
function p.buildAltMagicTable(spellName)
  local spell = Magic.getSpell(spellName, 'AltMagic')
  local result = '{|class="wikitable"\r\n|-'
  result = result..'\r\n!colspan="2"|'..Icons.Icon({spell.name, type='spell'})
  result = result..'\r\n|-\r\n!style="text-align:right;"|Requirements'
  result = result..'\r\n|'..Icons._SkillReq('Magic', spell.magicLevelRequired)
  -- 1 means select any item. 0 would mean Superheat, but that's handled elsewhere
  -- -1 means no item is needed, so hide this section
  if spell.selectItem == 1 then
    result = result..'\r\n|-\r\n!style="text-align:right;"|Materials'
    result = result..'\r\n|1 of any item'
  end
  --Add runes
  result = result..'\r\n|-\r\n!style="text-align:right;"|Runes\r\n|'
  for i, req in pairs(spell.runesRequired) do
    local rune = p.getItemByID(req.id)
    if i > 1 then result = result..', ' end
    result = result..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
  end
  if spell.runesRequiredAlt ~= nil and Shared.tableCount(spell.runesRequired) ~= Shared.tableCount(spell.runesRequiredAlt) then
    result = result.."<br/>'''OR'''<br/>"
    for i, req in pairs(spell.runesRequiredAlt) do
      local rune = p.getItemByID(req.id)
      if i > 1 then result = result..', ' end
      result = result..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
    end
  end
 
  --Now just need the output quantity, xp, and casting time (which is always 2)
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base Quantity\r\n|'..spell.convertToQty
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base XP\r\n|'..spell.magicXP
  result = result..'\r\n|-\r\n!style="text-align:right;"|Cast Time\r\n|2s'
  result = result..'\r\n|}'
  return result
end
 
function p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq)
  if qty == nil then qty = 1 end
  local result = '{|class="wikitable"'
  if req ~= nil then
    result = result..'\r\n!colspan="2"|Item Creation'
  else
    result = result..'\r\n!colspan="2"|Item Production'
  end
  result = result..'\r\n|-\r\n!style="text-align: right;"|Requirements'
  result = result..'\r\n|'..Icons._SkillReq(skill, lvl)
  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|'
    if type(req) == 'table' then
      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
       end
     else
     else
    result = result..req
      result = item.modifiers[modifier]
    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
 
function p.getCreationTable(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"
  end
 
  return p._getCreationTable(item)
end
 
function p.getEquipmentSlotName(id)
  for slotName, i in Shared.skpairs(Constants.equipmentSlot) do
    if i == id then
      return slotName
     end
     end
   end
   end
  return 'Invalid'
end


function p._getOtherItemBoxText(item)
   if asString then
  result = ''
     if skill ~= nil then
  --For equipment, show the slot they go in
      return Constants._getModifierText(modifier, {skill, result})
   if item.equipmentSlot ~= nil then
     result = result..'\r\n|-\r\n|Equipment Slot: '..p.getEquipmentSlotName(item.equipmentSlot)
  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
  --For Prayer Points, show how many you get
  if item.prayerPoints ~= nil then
    result = result..'\r\n|-\r\n|'..Icons.Icon({'Prayer', type='skill'})..' Points: '..item.prayerPoints
  end
  return result
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"
  end
 
  return p._getOtherItemBoxText(item, asList)
end
 
function p._getItemSources(item, asList, addCategories)
  local result = nil
  local lineArray = {}
  local categoryArray = {}
 
  --Alright, time to go through all the ways you can get an item...
  --First up: Can we kill somebody and take theirs?
  local killStr = ''
  local dungeonStr = ''
  local count1 = 0
  for i, monster in Shared.skpairs(MonsterData.Monsters) do
    local isDrop = false
    if monster.bones == item.id and ((monster.lootTable ~= nil and not monster.isBoss) or Shared.contains(item.name, "Shard")) 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
      if monster.isBoss then
        local areaList = Areas.getMonsterAreas(i - 1)
        --If this is a boss then we actually are completing dungeons for this and need to figure out which one
        for j, dung in pairs(areaList) do
          if string.len(dungeonStr) > 0 then
            dungeonStr = dungeonStr..','
          else
            dungeonStr = 'Completing: '
          end
          dungeonStr = dungeonStr..Icons.Icon({dung.name, type="dungeon", notext=true})
        end
      else
        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
  end
  --Add the Fire Cape's special exception:
  if item.name == 'Fire Cape' then
    if string.len(dungeonStr) > 0 then
      dungeonStr = dungeonStr..','
     else
     else
       dungeonStr = 'Completing: '
       return Constants._getModifierText(modifier, result)
    end
    dungeonStr = dungeonStr..Icons.Icon({"Volcanic Cave", type="dungeon", notext=true})
  end
 
  if string.len(dungeonStr) > 0 then table.insert(lineArray, dungeonStr) end
  if string.len(killStr) > 0 then table.insert(lineArray, killStr) end
 
  --Next: Can we find it in a box?
  --While we're here, check for upgrades, cooking, and growing
  local lootStr = ''
  local upgradeStr = ''
  local cookStr = ''
  local burnStr = ''
  local growStr = ''
  local count2 = 0
  count1 = 0
  for i, item2 in pairs(ItemData.Items) do
    if item2.dropTable ~= nil then
      for j, loot in pairs(item2.dropTable) do
        if loot[1] == item.id then
          count1 = count1 + 1
          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
          table.insert(categoryArray, '[[Category:Upgraded Items]]')
          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
          table.insert(categoryArray, '[[Category:Cooked Items]]')
          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
          table.insert(categoryArray, '[[Category:Burnt Items]]')
          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
          table.insert(categoryArray, '[[Category:Harvestable Items]]')
          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?
  local thiefStr = ''
  for i, npc in pairs(SkillData.Thieving) do
    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
  --SmithCheck:
  if item.smithingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
  end
 
  --CraftCheck:
  if item.craftingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
  end
 
  --FletchCheck:
  if item.fletchingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
  end
 
  --RunecraftCheck:
  if item.runecraftingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
  end
 
  --MineCheck:
  if item.miningID ~= nil then
    table.insert(lineArray, Icons._SkillReq("Mining", SkillData.Mining[item.masteryID[2] + 1].level))
  end
 
  --FishCheck:
  if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
    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
 
  --WoodcuttingCheck
  if item.type == 'Logs' then
    local treeData = SkillData.Woodcutting[item.id + 1]
    local lvl = treeData.level
    table.insert(lineArray, Icons._SkillReq("Woodcutting", lvl))
  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', notext = true})..', '..Icons.Icon({'Cooking', type = 'skill', notext = true})..', '..Icons.Icon({'Smithing', type = 'skill', notext = true})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Fletching', type = 'skill', notext = true})..', '..Icons.Icon({'Crafting', type = 'skill', notext = true})..', '..Icons.Icon({'Runecrafting', type = 'skill', notext = true})..',<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', notext = true})..', '..Icons.Icon({'Fishing', type = 'skill', notext = true})..', '..Icons.Icon({'Mining', type = 'skill', notext = true})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Thieving', type = 'skill', notext = true})..', '..Icons.Icon({'Farming', type = 'skill', notext = true})
    table.insert(lineArray, rhaStr)
  elseif item.name == 'Mysterious Stone' then
    local rhaStr = 'Any action in: '
    rhaStr = rhaStr..Icons.Icon({'Firemaking', type = 'skill', notext = true})..', '..Icons.Icon({'Cooking', type = 'skill', notext = true})..', '..Icons.Icon({'Smithing', type = 'skill', notext = true})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Fletching', type = 'skill', notext = true})..', '..Icons.Icon({'Crafting', type = 'skill', notext = true})..', '..Icons.Icon({'Runecrafting', type = 'skill', notext = true})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Herblore', type='skill'})..', '..Icons.Icon({'Woodcutting', type = 'skill', notext = true})..', '..Icons.Icon({'Fishing', type = 'skill', notext = true})..',<br/>'
    rhaStr = rhaStr..Icons.Icon({'Mining', type = 'skill', notext = true})..', '..Icons.Icon({'Thieving', type = 'skill', notext = true})..', '..Icons.Icon({'Farming', type = 'skill', notext = true})
    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
  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
   else
     result = table.concat(lineArray, "<br/>")
     return result
    result = '<div style="max-width:180px;text-align:right">'..result..'</div>'
   end
   end
  if addCategories then result = result..table.concat(categoryArray, '') end
  return result
end
end


 
function p.hasCombatStats(item)
function p.getItemSources(frame)
   if item.isEquipment or (item.validSlots == nil and item.equipmentStats ~= nil) then
   local itemName = frame.args ~= nil and frame.args[1] or frame
     -- Ensure at least one stat has a non-zero value
  local item = p.getItem(itemName)
     for statName, statVal in pairs(item.equipmentStats) do
  local asList = false
      if statVal ~= 0 then return true end
  local addCategories = false
  if frame.args ~= nil then
    asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
    addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
  end
  if item == nil then
     return "ERROR: No item named "..itemName.." exists in the data module"
  end
 
  return p._getItemSources(item, asList, addCategories)
end
 
--Brute forcing some item uses to make things easier
local itemUseArray = {
  Combat = {},
  Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
  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, addCategories)
  local useArray = {}
  local categoryArray = {}
  --Another fun one. This time getting all the different possible ways an item can be used
 
  --Before anything else, if this is a potion add it to the appropriate override section
  if item.herbloreMasteryID ~= nil then
     table.insert(itemUseArray[potionUseArray[item.herbloreMasteryID]], item.name)
  end
 
  --First things added to the list are non-skill things that are easy to check
  if item.equipmentSlot ~= nil or Shared.contains(itemUseArray.Combat, item.name) then
    if item.equipmentSlot ~= nil then  
      table.insert(categoryArray, '[[Category:'..p.getEquipmentSlotName(item.equipmentSlot)..' Slot Items]]')
     end
     end
    table.insert(useArray, '* '..Icons.Icon({'Combat'}))
  end
  if item.healsFor ~= nil then
    table.insert(categoryArray, '[[Category:Food Items]]')
    table.insert(useArray, '* [[Food]]')
  end
  if item.dropTable ~= nil then
    table.insert(categoryArray, '[[Category:Openable Items]]')
    table.insert(useArray, '* [[Chest Drop Tables|Can Be Opened]]')
   end
   end
 
   return false
   --Next, upgrading, crafting, herblore, fletching, and runecrafting since we have to sift through other items for these
  local canUpgrade = false
  local canCraft = false
  local canFletch = false
  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
        for j, req in pairs(item2.craftReq) do
          if req.id == item.id then
            canCraft = true
            break
          end
        end
      end
 
      if item2.fletchReq ~= nil then
        for j, req in pairs(item2.fletchReq) do
          if req.id == item.id then
            canFletch = true
            break
          end
        end
      end
 
      if item2.runecraftReq ~= nil then
        for j, req in pairs(item2.runecraftReq) do
          if req.id == item.id then
            canRunecraft = true
            break
          end
        end
      end
 
      if item2.herbloreReq ~= nil then
        for j, req in pairs(item2.herbloreReq) do
          if req.id == item.id then
            canHerblore = true
            break
          end
        end
      end
    end
  end
  if canUpgrade then
    if item.canUpgrade or (item.type == 'Armour' and item.canUpgrade == nil) then
      table.insert(categoryArray, '[[Category:Upgradeable Items]]')
    end
    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
    if item.prayerPoints ~= nil then table.insert(categoryArray, '[[Category:Buriable Items]]') end
    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
  --Except Max Skillcape, which is tied to all skills. (And so is the Signet Ring)
  --And combat skillcapes, since combat skills don't get special treatment
 
  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' or item.name == 'Cape of Completion' 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
 
  if Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion' then table.insert(categoryArray, '[[Category:Skillcapes]]') end
 
  --Special note for Charge Stone of Rhaelyx
  if item.name == 'Charge Stone of Rhaelyx' then
    table.insert(useArray, '* Powering '..Icons.Icon({'Crown of Rhaelyx', type='item'}))
  end
 
  local result = table.concat(useArray,'\r\n')
  if addCategories then result = result..table.concat(categoryArray, '') end
  return result
end
end


function p.getItemUses(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]
  local addCategories = false
   local skillName = frame.args ~= nil and frame.args[3] or frame[3]
  if frame.args ~= nil then
  local asString = frame.args ~= nil and frame.args[4] or frame[4]
    addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
   if asString ~= nil then
  end
     if string.upper(asString) == 'FALSE' then
  if item == nil then
    asString = false
    return "ERROR: No item named "..itemName.." exists in the data module"
  end
 
  return p._getItemUses(item, addCategories)
end
 
function p._getItemUseTable(item)
  local useArray = {}
  local potTierMastery = {[0] = 0, [1] = 20, [2] = 50, [3] = 90}
 
   --First, loop through all items to find anything that can be made or upgraded into using our source
  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
          local mat = item2.itemsRequired
          local xp = 'N/A'
          local rowReq = 'None'
          --Potions do have upgrade requirements though
          if item2.potionTier ~= nil then
            rowReq = Icons._MasteryReq(item2.name, potTierMastery[item2.potionTier])
          end
          table.insert(useArray, {item = item2, qty = 1, mats = mat, skill = 'Upgrade', req = rowReq, xp = xp, gp = item2.trimmedGPCost})
          break
        end
      end
    end
    if item.name == 'Leather' and item2.buysForLeather ~= nil then
      local mat = {{id = item.id, qty = item2.buysForLeather}}
      local xp = 'N/A'
      local rowReq = 'None'
      table.insert(useArray, {item = item2, qty = 1, mats = mat, skill = 'Shop', req = rowReq, xp = xp, gp = item2.buysFor})
    elseif item2.buysForItems ~= nil then
      for j, req in pairs(item2.buysForItems) do
        if req[1] == item.id then
          local mat = item2.buysForItems
          local xp = 'N/A'
          local rowReq = 'None'
          table.insert(useArray, {item = item2, qty = 1, mats = mat, skill = 'Shop', req = rowReq, xp = xp, gp = item2.buysForGP})
          break
        end
      end
    end
    if item2.craftReq ~= nil then
      for j, req in pairs(item2.craftReq) do
        if req.id == item.id then
          local mat = item2.craftReq
          local xp = item2.craftingXP
          local rowReq = item2.craftingLevel
          local qty = item2.craftQty
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Crafting', req = rowReq, xp = xp})
          break
        end
      end
    end
    if item2.fletchReq ~= nil then
      for j, req in pairs(item2.fletchReq) do
        if req.id == item.id then
          local xp = item2.fletchingXP
          local rowReq = item2.fletchingLevel
          --Arrow Shafts are special and have to be treated specially
          local qty = item2.fletchQty
          local mat = item2.fletchReq
          if item2.name == 'Arrow Shafts' then
            mat = {{id = item.id, qty = 1}}
            qty =  qty + (qty * item.id)
          end
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Fletching', req = rowReq, xp = xp})
          break
        end
      end
    end
    if item2.smithReq ~= nil then
      for j, req in pairs(item2.smithReq) do
        if req.id == item.id then
          local mat = item2.smithReq
          local xp = item2.smithingXP
          local rowReq = item2.smithingLevel
          local qty = item2.smithingQty
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Smithing', req = rowReq, xp = xp})
          break
        end
      end
    end
    if item2.runecraftReq ~= nil then
      for j, req in pairs(item2.runecraftReq) do
        if req.id == item.id then
          local mat = item2.runecraftReq
          local xp = item2.runecraftingXP
          local rowReq = item2.runecraftingLevel
          local qty = item2.runecraftQty
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Runecrafting', req = rowReq, xp = xp})
          break
        end
      end
    end
    if item2.herbloreReq ~= nil then
      for j, req in pairs(item2.herbloreReq) do
        if req.id == item.id then
          local potionData = SkillData.Herblore.ItemData[item2.herbloreMasteryID + 1]
          local mat = item2.herbloreReq
          local xp = potionData.herbloreXP
          --Potions do have upgrade requirements though
          local rowReq = Icons._SkillReq('Herblore', potionData.herbloreLevel)
          local masteryLvl = potTierMastery[item2.potionTier]
          if masteryLvl > 0 then
            rowReq = rowReq..'<br/>'..Icons._MasteryReq(item2.name, masteryLvl)
          end
          local reqVal = potionData.herbloreLevel + (masteryLvl * 0.01)
          table.insert(useArray, {item = item2, qty = 1, mats = mat, skill = 'Herblore', reqVal = reqVal, req = rowReq, xp = xp})
          break
        end
      end
    end
  end
  if item.grownItemID ~= nil then
    local item2 = p.getItemByID(item.grownItemID)
    local mat = {{id = item.id, qty = item.seedsRequired}}
    local xp = item.farmingXP
    local rowReq = item.farmingLevel
    local qty = 5
    table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Farming', req = rowReq, xp = xp})
  end
   if item.cookedItemID ~= nil then
    local item2 = p.getItemByID(item.cookedItemID)
    local mat = {{id = item.id, qty = 1}}
    local xp = item.cookingXP
    local rowReq = item.cookingLevel
    local qty = 1
    table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Cooking', req = rowReq, xp = xp})
  end
  if item.burntItemID ~= nil then
    local item2 = p.getItemByID(item.burntItemID)
    local mat = {{id = item.id, qty = 1}}
    local xp = 1
    local rowReq = item.cookingLevel
    local qty = 1
    table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Cooking', req = rowReq, xp = xp})
  end
 
  --Finally build the table using what we've learned
  table.sort(useArray, function(a, b)
                        local aReqVal = a.reqVal ~= nil and a.reqVal or a.req
                        local bReqVal = b.reqVal ~= nil and b.reqVal or b.req
                        if a.skill ~= b.skill then
                          return a.skill < b.skill
                        elseif type(aReqVal) ~= type(bReqVal) then
                          return tostring(aReqVal) < tostring(bReqVal)
                        elseif aReqVal ~= bReqVal then
                          return aReqVal < bReqVal
                        else
                          return a.item.name < b.item.name
                        end end)
 
  local spellUseTable = p._getSpellUseTable(item)
  local result = ''
  if Shared.tableCount(useArray) == 0 then
     if string.len(spellUseTable) > 0 then
      return '==Uses==\r\n==='..Icons.Icon({'Magic', type='skill', size='30'})..'===\r\n'..spellUseTable
    else
      return ''
    end
  end
  result = result..'{| class="wikitable sortable"'
  result = result..'\r\n!colspan=2|Item Created!!Type!!Requirements!!XP!!Ingredients'
  for i, row in pairs(useArray) do
    local qty = row.qty ~= nil and row.qty or 1
    result = result..'\r\n|-\r\n|data-sort-value="'..row.item.name..'"|'
    result = result..Icons.Icon({row.item.name, type='item', notext=true, size=50})..'||'
    if qty > 1 then result = result.."'''"..qty.."x''' " end
    result = result..'[['..row.item.name..']]'
    if row.skill == 'Upgrade' then
      result = result..'||data-sort-value="Upgrade"|[[Upgrading Items|Upgrade]]'
    elseif row.skill == 'Shop' then
      result = result..'||data-sort-value="Shop"|[[Shop]]'
    else
      result = result..'||data-sort-value="'..row.skill..'"|'..Icons.Icon({row.skill, type='skill'})
    end
    if type(row.req) == 'number' then
      result = result..'|| data-sort-value="'..row.req..'"|'..Icons._SkillReq(row.skill, row.req)
    elseif row.reqVal ~= nil then
      result = result..'|| data-sort-value="'..row.reqVal..'"|'..row.req
    else
      result = result..'||'..row.req
    end
    if type(row.xp) == 'string' then
      result = result..'||data-sort-value="0"|'..row.xp
     else
     else
      result = result..'||data-sort-value="'..row.xp..'"|'..row.xp..' '..Icons.Icon({row.skill, type='skill', notext=true})..' XP'
    asString = true
    end
    result = result..'||'
    for i, mat in pairs(row.mats) do
      local matID = mat.id ~= nil and mat.id or mat[1]
      local matQty = mat.qty ~= nil and mat.qty or mat[2]
      matItem = p.getItemByID(matID)
      if i > 1 then result = result..'<br/>' end
      result = result..Icons.Icon({matItem.name, type='item', qty=matQty})
     end
     end
    if row.gp ~= nil then result = result..'<br/>'..Icons.GP(row.gp) end
  end
  result = result..'\r\n|}'
  if string.len(spellUseTable) > 0 then
    result = result..'\r\n==='..Icons.Icon({'Magic', type='skill', size='30'})..'===\r\n'..spellUseTable
   end
   end
  return '==Uses==\r\n'..result
end


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


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


function p._getSpellUseTable(item)
function p._getWeaponAttackType(item)
   local spellList = Magic.getSpellsForRune(item.id)
   if item.isEquipment == true and (item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon')) or
  --Bail immediately if no spells are found
  (item.occupiesSlots ~= nil and Shared.contains(item.occupiesSlots, 'Weapon')) then
  if Shared.tableCount(spellList) == 0 then
     if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then
    return ''
       local iconType = item.attackType ~= 'melee' and 'skill' or nil
  end
       return Icons.Icon({Shared.titleCase(item.attackType), type=iconType, nolink='true'})
 
  local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
  result = result..'!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level'
  result = result..'!!Type!!style="width:275px"|Description'
  result = result..'!!Runes'
  for i, spell in pairs(spellList) do
    local rowTxt = '\r\n|-\r\n|data-sort-value="'..spell.name..'"|'
    if spell.type == 'Auroras' then
      rowTxt = rowTxt..Icons.Icon({spell.name, type='aurora', notext=true, size=50})
    elseif spell.type == 'Curses' then
      rowTxt = rowTxt..Icons.Icon({spell.name, type='curse', notext=true, size=50})
    else
      rowTxt = rowTxt..Icons.Icon({spell.name, type='spell', notext=true, size=50})
    end
    rowTxt = rowTxt..'||'..spell.name
    rowTxt = rowTxt..'||data-sort-value="'..spell.magicLevelRequired..'"|'..Icons._SkillReq('Magic', spell.magicLevelRequired)
    --Handle required items/dungeon clears
    if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
      local reqItem = p.getItemByID(spell.requiredItem)
      rowTxt = rowTxt..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
    end
     if spell.requiredDungeonCompletion ~= nil then
      local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
      rowTxt = rowTxt..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
    end
    rowTxt = rowTxt..'||data-sort-value="'..Magic.getSpellTypeIndex(spell.type)..'"|'
    rowTxt = rowTxt..Magic.getSpellTypeLink(spell.type)
    if spell.type == 'Spells' then
       rowTxt = rowTxt..'||Combat spell with a max hit of '..(spell.maxHit * 10)
    else
      rowTxt = rowTxt..'||'..spell.description
    end
    rowTxt = rowTxt..'||style="text-align:center"|'
    for i, req in pairs(spell.runesRequired) do
       local rune = p.getItemByID(req.id)
      if i > 1 then rowTxt = rowTxt..', ' end
      rowTxt = rowTxt..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
    end
    if spell.runesRequiredAlt ~= nil then
      rowTxt = rowTxt.."<br/>'''OR'''<br/>"
      for i, req in pairs(spell.runesRequiredAlt) do
        local rune = p.getItemByID(req.id)
        if i > 1 then rowTxt = rowTxt..', ' end
        rowTxt = rowTxt..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
      end
     end
     end
    result = result..rowTxt
   end
   end
  --Add the table end and add the table to the result string
   return 'Invalid'
   result = result..'\r\n|}'
  return result
end
end


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


function p._getItemLootSourceTable(item)
function p.getPotionTable(frame)
   local result = '{| class="wikitable sortable stickyHeader"'
   local potionName = frame.args ~= nil and frame.args[1] or frame
   result = result..'\r\n|- class="headerRow-0"'
   local tiers = {'I', 'II', 'III', 'IV'}
  result = result..'\r\n!Source!!Source Type!!Quantity!!Chance'


   --Set up function for adding rows
   local resultPart = {}
   local buildRow = function(source, type, minqty, qty, chance)
   table.insert(resultPart, '{| class="wikitable"')
    if minqty == nil then minqty = 1 end
  table.insert(resultPart, '\r\n!Potion!!Tier!!Charges!!Effect')
    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
  local tier1potion = p.getItem(potionName..' I')
    if qty ~= minqty then rowTxt = rowTxt..' - '..qty end
  if tier1potion == nil then
     rowTxt = rowTxt..'\r\n|style ="text-align: right;"|'..Shared.round(chance, 2, 2)..'%'
     return 'ERROR: No potion named "' .. potionName .. '" was found[[Category:Pages with script errors]]'
    return rowTxt
   end
   end
  local dropRows = {}
   for i, tier in pairs(tiers) do
 
     local tierName = potionName..' '..tier
  --Alright, time to go through a few ways to get the item
     local potion = p.getItemByID(tier1potion.id + i - 1)
  --First up: Can we kill somebody and take theirs?
     if potion ~= nil then
   for i, monster in pairs(MonsterData.Monsters) do
       table.insert(resultPart, '\r\n|-')
     local minqty = 1
      table.insert(resultPart, '\r\n|'..Icons.Icon({tierName, type='item', notext=true, size='60'}))
    local qty = 1
       table.insert(resultPart, '||'..Icons.Icon({tierName, tier, type='item', noicon=true}))
    local chance = 0
       table.insert(resultPart, '||'..potion.potionCharges..'||'..potion.description)
    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
       --If we're dealing with a boss, this is a Dungeon row instead
      if monster.isBoss and not Shared.contains(item.name, 'Shard') then
        local dung = Areas.getMonsterAreas(i - 1)[1]
        local sourceTxt = Icons.Icon({dung.name, type="dungeon", notext=true})
        table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = minqty, qty = qty, chance = chance})
      else
        local sourceTxt = Icons.Icon({monster.name, type='monster'})
        table.insert(dropRows, {source = sourceTxt, type = '[[Monster]]', minqty = minqty, qty = qty, chance = chance})
      end
    end
  end
 
  --Special exception for the Fire Cape as a bonus dungeon drop
  if item.name == 'Fire Cape' then
      local sourceTxt = Icons.Icon({"Volcanic Cave", type="dungeon", notext=true})
       table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, chance = 100})
  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
   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'})
    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 / p.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
 
  --Make sure to return nothing if there are no drop sources 
   if Shared.tableCount(dropRows) == 0 then return '' end
 
  table.sort(dropRows, function(a, b) return a.chance > 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|}'
  return result
end
end


function p.getItemLootSourceTable(frame)
function p._getOtherItemBoxText(item)
   local itemName = frame.args ~= nil and frame.args[1] or frame
   resultPart = {}
   local item = p.getItem(itemName)
   --For equipment, show the slot they go in
   if item == nil then
   if item.validSlots ~= nil then
     return "ERROR: No item named "..itemName.." exists in the data module"
     table.insert(resultPart, "\r\n|-\r\n|'''Equipment Slot:''' "..table.concat(item.validSlots, ', '))
   end
   end
 
  --For weapons with a special attack, show the details
   return p._getItemLootSourceTable(item)
   if item.hasSpecialAttack then
end
    table.insert(resultPart, "\r\n|-\r\n|'''Special Attack:'''")
 
     for i, spAtt in ipairs(item.specialAttacks) do
function p._getItemShopTable(item)
      table.insert(resultPart, '\r\n* ' .. spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
  local result = '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Shop]] Purchase'
      table.insert(resultPart, '\r\n** ' .. spAtt.description)
  result = result..'\r\n|-\r\n!style="text-align:right;"|Cost\r\n|'
  local cost = {}
  local qty = '1'
  if item.buysFor ~= nil then
     if item.buysFor > 0 then table.insert(cost, Icons.GP(item.buysFor)) end
  elseif item.slayerCost ~= nil then
    table.insert(cost, Icons.SC(item.slayerCost))
  elseif GloveTable[item.name] ~= nil then
    table.insert(cost, Icons.GP(GloveTable[item.name].cost))
    qty = ' +'..Shared.formatnum(GloveTable[item.name].charges)..' Charges'
  end
  if item.buysForLeather ~= nil then
    table.insert(cost, Icons.Icon({'Leather', type='item', qty=item.buysForLeather}))
  end
  if item.buysForItems ~= nil then
    for i, row in pairs(item.buysForItems) do
      local mat = p.getItemByID(row[1])
      table.insert(cost, Icons.Icon({mat.name, type='item', qty=row[2]}))
     end
     end
   end
   end
   if Shared.tableCount(cost) == 0 then
   --For potions, show the number of charges
    --If no cost is set, return an empty string
   if item.potionCharges ~= nil then
    return ''
     table.insert(resultPart, "\r\n|-\r\n|'''Charges:''' "..item.potionCharges)
   else
     result = result..table.concat(cost, '<br/>')
   end
   end
 
   --For food, show how much it heals for
   --For right now, only have requirements on Skillcapes
   if item.healsFor ~= nil then
   result = result..'\r\n|-\r\n!style="text-align:right;"|Requirements\r\n|'
    table.insert(resultPart, "\r\n|-\r\n|'''Heals for:''' "..Icons.Icon({"Hitpoints", type="skill", notext="true"})..' '..(item.healsFor * 10))
  if item.name == 'Cape of Completion' then
    result = result..'100% Completion Log'
  elseif item.name == 'Max Skillcape' then
    result = result..'Level 99 in all [[Skills]]'
  elseif Shared.contains(item.name, 'Skillcape') then
    local skillName = Shared.splitString(item.name)[1]
    result = result..Icons._SkillReq(skillName, 99)
  else
    result = result..'None'
   end
   end
 
  --For Prayer Points, show how many you get
   result = result..'\r\n|-\r\n!style="text-align:right;"|Quantity\r\n|'..qty
   if item.prayerPoints ~= nil then
  result = result..'\r\n|}'
    table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', type='skill'}).." Points:''' "..item.prayerPoints)
  return result
end
 
function p.getItemShopTable(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"
   end
   end
 
   --For items with modifiers, show what those are
   return p._getItemShopTable(item)
   if item.modifiers ~= nil and Shared.tableCount(item.modifiers) > 0 then
end
     table.insert(resultPart, "\r\n|-\r\n|'''Modifiers:'''\r\n"..Constants.getModifiersText(item.modifiers, true))
 
function p._getItemUpgradeTable(item)
  local result = ''
   if item.itemsRequired ~= nil then
    --First, get details on all the required materials
    local upgradeFrom = {}
    local materials = {}
    for i, row in pairs(item.itemsRequired) do
      local mat = p.getItemByID(row[1])
      --Check to see if the source item can trigger the upgrade
      if mat.canUpgrade or (mat.type == 'Armour' and mat.canUpgrade == nil) then
        table.insert(upgradeFrom, Icons.Icon({mat.name, type='item'}))
      end
      table.insert(materials, Icons.Icon({mat.name, type='item', qty=row[2]}))
     end
    if item.trimmedGPCost ~= nil then
      table.insert(materials, Icons.GP(item.trimmedGPCost))
    end
    result = '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Upgrading Items|Item Upgrade]]'
    --[[result = result..'\r\n|-\r\n!style="text-align:right;"|Upgrades From\r\n|'
    result = result..table.concat(upgradeFrom, '<br/>')--]]
    result = result..'\r\n|-\r\n!style="text-align:right;"|Materials\r\n|'
    result = result..table.concat(materials, '<br/>')
    result = result..'\r\n|}'
   end
   end
   return result
   return table.concat(resultPart)
end
end


function p.getItemUpgradeTable(frame)
function p.getOtherItemBoxText(frame)
   local itemName = frame.args ~= nil and frame.args[1] or frame
   local itemName = frame.args ~= nil and frame.args[1] or frame
   local item = p.getItem(itemName)
   local item = p.getItem(itemName)
  if item == nil then
   local asList = false
    return "ERROR: No item named "..itemName.." exists in the data module"
   if frame.args ~= nil then
  end
     asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
 
  return p._getItemUpgradeTable(item)
end
 
function p._getItemSuperheatTable(item)
  --Manually build the Superheat Item table
  local oreString = ''
   local coalString = ''
   for i, mat in pairs(item.smithReq) do
    local thisMat = p.getItemByID(mat.id)
    if thisMat.name == 'Coal Ore' then
      coalString = ', '..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
     else
      if string.len(oreString) > 0 then oreString = oreString..', ' end
      oreString =  oreString..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
    end
  end
  --Set up the header
  local superheatTable = '{|class="wikitable"\r\n!colspan="2"|Spell'
  superheatTable = superheatTable..'!!'..Icons.Icon({'Smithing', type='skill', notext='true'})..' Level'
  superheatTable = superheatTable..'!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level'
  superheatTable = superheatTable..'!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' XP'
  superheatTable = superheatTable..'!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars'
  superheatTable = superheatTable..'!!Ore!!Runes'
  --Loop through all the variants
  local spellNames = {'Superheat I', 'Superheat II', 'Superheat III', 'Superheat IV'}
  for i, sName in pairs(spellNames) do
    local spell = Magic.getSpell(sName, 'AltMagic')
    local rowTxt = '\r\n|-\r\n|'..Icons.Icon({spell.name, type='spell', notext=true, size=50})
    rowTxt = rowTxt..'||[['..spell.name..']]||'..item.smithingLevel
    rowTxt = rowTxt..'||'..spell.magicLevelRequired..'||'..spell.magicXP..'||'..spell.convertToQty
    rowTxt = rowTxt..'||'..oreString
    if spell.ignoreCoal ~= nil and not spell.ignoreCoal then rowTxt = rowTxt..coalString end
    rowTxt = rowTxt..'||style="text-align:center"|'
    for i, req in pairs(spell.runesRequired) do
      local rune = p.getItemByID(req.id)
      if i > 1 then rowTxt = rowTxt..', ' end
      rowTxt = rowTxt..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
    end
    rowTxt = rowTxt.."<br/>'''OR'''<br/>"
    for i, req in pairs(spell.runesRequiredAlt) do
      local rune = p.getItemByID(req.id)
      if i > 1 then rowTxt = rowTxt..', ' end
      rowTxt = rowTxt..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
    end
    superheatTable = superheatTable..rowTxt
   end
   end
  --Add the table end and add the table to the result string
  superheatTable = superheatTable..'\r\n|}'
  return superheatTable
end
function p.getItemSuperheatTable(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
  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


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


function p._getItemSourceTables(item)
function p._getItemCategories(item)
   local result = ''
   local resultPart = {}
   local shopTable = p._getItemShopTable(item)
   if item.category ~= nil then table.insert(resultPart, '[[Category:'..item.category..']]') end
  if string.len(shopTable) > 0 then
  if item.type ~= nil then table.insert(resultPart, '[[Category:'..item.type..']]') end
    result = result..'===Shop===\r\n'..shopTable
   if item.tier ~= nil then table.insert(resultPart, '[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]') end
  end
   if item.hasSpecialAttack then table.insert(resultPart, '[[Category:Items With Special Attacks]]') end
 
   if item.validSlots ~= nil then
  local creationTable = p._getCreationTable(item)
    local slotRemap = {
  if string.len(creationTable) > 0 then
      ['Passive'] = 'Passive Items',
    if string.len(result) > 0 then result = result..'\r\n' end
      ['Summon1'] = 'Summoning Familiars',
    result = result..'===Creation===\r\n'..creationTable
      ['Summon2'] = ''
  end
    }
 
    for i, slotName in ipairs(item.validSlots) do
  local upgradeTable = p._getItemUpgradeTable(item)
      local slotRemapName = slotName
  if string.len(upgradeTable) > 0 then
      if slotRemap[slotName] ~= nil then slotRemapName = slotRemap[slotName] end
    if string.len(result) > 0 then result = result..'\r\n' end
       if slotRemapName ~= '' then table.insert(resultPart, '[[Category:' .. slotRemapName .. ']]') end
    if string.len(creationTable) == 0 then result = result..'===Creation===\r\n' end
    result = result..upgradeTable
  end
 
   if item.type == 'Bar' then
    result = result..'\r\n==='..Icons.Icon({'Alt Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item)
  end
 
  local lootTable = p._getItemLootSourceTable(item)
  if string.len(lootTable) > 0 then
    if string.len(result) > 0 then result = result..'\r\n' end
    result = result..'===Loot===\r\n'..lootTable
  end
  return result
end
 
function p.getItemSourceTables(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"
  end
 
  return p._getItemSourceTables(item)
end
 
function p.getEquipmentTable(frame)
   local args = frame.args ~= nil and frame.args or frame
  local type = args.type
  local tier = args.tier
  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
  local ammoType = nil
  if ammoTypeStr ~= nil then
    if ammoTypeStr == "Arrows" then
       ammoType = 0
    elseif ammoTypeStr == 'Bolts' then
      ammoType = 1
    elseif ammoTypeStr == 'Javelins' then
      ammoType = 2
    elseif ammoTypeStr == 'Throwing Knives' then
      ammoType = 3
     end
     end
   end
   end
 
   if item.modifiers ~= nil then
  --Find out what slot we're working with
     local modsDL = {
  local slot = nil
       'increasedChanceToDoubleLootCombat',
   if slotStr ~= nil then
       'decreasedChanceToDoubleLootCombat',
     slot = Constants.equipmentSlot[slotStr]
       'increasedChanceToDoubleLootThieving',
  end
       'decreasedChanceToDoubleLootThieving',
  mw.log("Type = "..(type ~= nil and type or '')..", Slot = "..(slot ~= nil and slot or '')..", AmmoType = "..(ammoType ~= nil and ammoType or ''))
       'increasedChanceToDoubleItemsGlobal',
 
       'decreasedChanceToDoubleItemsGlobal'
 
     }
  --Getting some lists set up here that will be used later
     for modName, val in pairs(item.modifiers) do
  --First, the list of columns used by both weapons & armour
       if Shared.contains(modsDL, modName) then
  local statColumns = {'stabAttackBonus', 'slashAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'strengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'defenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction', 'attackLevelRequired', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
         table.insert(resultPart, '[[Category:Double Loot Chance Items]]')
  --Then the lists for just weapons/just armour
         break
  --Then the list of weapon types
  local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}
 
  local isWeaponType = Shared.contains(weaponTypes, type)
 
  --Now we need to figure out which items are in this list
  local itemList = {}
  for i, itemBase in pairs(ItemData.Items) do
    local item = Shared.clone(itemBase)
    item.id = i - 1
    local listItem = false
    if isWeaponType then
    listItem = item.type == type and item.category == category
       if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
    else
      --Now for handling armour
      if type == "Armour" or type == "Melee" then
        listItem = item.defenceLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Armour')
      elseif type == "Ranged Armour" or type == "Ranged" then
        listItem = item.rangedLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
      elseif type == "Magic Armour" or type == "Magic" then
        listItem = item.magicLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Magic Armour')
      else
        listItem = item.type == type and item.category ~= 'Combat'
      end
      if ammoType ~= nil then listItem = listItem and item.ammoType == ammoType end
      if slot ~= nil then listItem = listItem and item.equipmentSlot == slot end
    end
    if listItem then
      table.insert(itemList, item)
    end
  end
 
  --Now that we have a preliminary list, let's figure out which columns are irrelevant (IE are zero for all items in the selection)
 
  mw.log(table.concat(statColumns, ', '))
  local ignoreColumns = Shared.clone(statColumns)
  for i, item in pairs(itemList) do
    local ndx = 1
    while Shared.tableCount(ignoreColumns) >= ndx do
      if p._getItemStat(item, ignoreColumns[ndx], true) ~= 0 then
        table.remove(ignoreColumns, ndx)
       else
        ndx = ndx + 1
      end
    end
  end
  mw.log(table.concat(ignoreColumns, ', '))
 
  --Now to remove the ignored columns (and also we need to track groups like defence bonuses to see how many remain)
  local attBonusCols = 5
  local strBonusCols = 2
  local defBonusCols = 3
  local lvlReqCols = 4
  local ndx = 1
  while Shared.tableCount(statColumns) >= ndx do
    local colName = statColumns[ndx]
    if Shared.contains(ignoreColumns, colName) then
       mw.log('Removing '..colName..' ('..ndx..')')
      if Shared.contains(colName, 'AttackBonus') then attBonusCols = attBonusCols - 1 end
       if Shared.contains(colName, 'trengthBonus') then strBonusCols = strBonusCols - 1 end
      if Shared.contains(colName, 'efenceBonus') then defBonusCols = defBonusCols - 1 end
       if Shared.contains(colName, 'LevelRequired') then lvlReqCols = lvlReqCols - 1 end
      table.remove(statColumns, ndx)
    else
       ndx = ndx + 1
    end
  end
  mw.log(table.concat(statColumns, ', '))
 
 
  --Alright, let's start the table by building the shared header
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
  if isWeaponType then
    --Weapons have extra columns here for Attack Speed and "Two Handed?"
    result = result..'\r\n!colspan="4"|'
  else
    result = result..'\r\n!colspan="2"|'
  end
  if attBonusCols > 0 then
     result = result..'\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus'
  end
  if strBonusCols > 0 then
    result = result..'\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Strength Bonus'
  end
  if Shared.contains(statColumns, 'magicDamageBonus') then
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Damage Bonus'
  end
  if defBonusCols > 0 then
    result = result..'\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus'
  end
  if Shared.contains(statColumns, 'damageReduction') then
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|Damage Reduction'
  end
  if lvlReqCols > 0 then
    result = result..'\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Levels Required'
  end
  result = result..'\r\n!colspan="1"|'
  --One header row down, one to go
  result = result..'\r\n|-class="headerRow-1"'
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Item'
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Name'
  --Weapons have Attack Speed here
  if isWeaponType then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed'
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?'
  end
  --Attack bonuses
  if Shared.contains(statColumns, 'slashAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'stabAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'blockAttackBonus') then
     result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'rangedAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicAttackBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  --Strength bonuses
  if Shared.contains(statColumns, 'strengthBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'rangedStrengthBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicDamageBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  --Defence bonuses
  if Shared.contains(statColumns, 'defenceBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'rangedDefenceBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicDefenceBonus') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'damageReduction') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  --Level requirements
  if Shared.contains(statColumns, 'attackLevelRequired') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'defenceLevelRequired') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'rangedLevelRequired') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  end
  if Shared.contains(statColumns, 'magicLevelRequired') then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  end
  --And finally Sources
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Sources'
 
  table.sort(itemList, function(a, b) return a.id < b.id end)
  for i, item in pairs(itemList) do
    if isWeaponType then
      --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)
      --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
      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 not Shared.contains(statName, 'LevelRequired') 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)
    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
       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
   end
   end
 
   return table.concat(resultPart)
  result = result..'\r\n|}'
   return result
end
 
function p._getItemCategories(item)
  local result = ''
  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


Line 1,885: Line 349:
   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


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


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


  result = result..'\r\n{|class="wikitable sortable"'
function p.getSpecialAttackTable(frame)
   result = result..'\r\n!colspan="2" style="width:200px"|Cape'
   local spAttTable = {}
  result = result..'!!Description!!style="width:120px"|Price'


   --Sort the table by cost and then name
   for i, item in Shared.skpairs(ItemData.Items) do
  table.sort(capeList, function(a, b)  
    if item.hasSpecialAttack then
                        if a.buysFor == b.buysFor then
      for i, spAtt in ipairs(item.specialAttacks) do
                          return a.name < b.name
        if spAttTable[spAtt.id] == nil then spAttTable[spAtt.id] = {sortName=item.name, defn = spAtt, Icons = {}} end
                        else
        table.insert(spAttTable[spAtt.id].Icons, Icons.Icon({item.name, type='item'}))
                          return a.sellsFor < b.buysFor
      end
                        end
     end
                      end)
  for i, thisItem in pairs(capeList) do
    result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item', size='50', notext=true})
    result = result..'||[['..thisItem.name..']]'
     result = result..'\r\n||'..thisItem.description
    result = result..'||style="text-align:left" data-sort-value="'..thisItem.buysFor..'"'
    result = result..'|'..Icons.GP(thisItem.buysFor)
   end
   end
  result = result..'\r\n|}'


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


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


return p
return p