Module:Items/UseTables: Difference between revisions

From Melvor Idle
(Fixed use list for Shards to also check for the Summoning category)
(_getItemUseTable: Support GP & SC in various artisan skills)
(32 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local p = {}
local p = {}


local MonsterData = mw.loadData('Module:Monsters/data')
local ItemData = mw.loadData('Module:Items/data')
local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')
local SkillData = mw.loadData('Module:Skills/data')
Line 14: Line 13:
local Shop = require('Module:Shop')
local Shop = require('Module:Shop')


local SkillEnum = mw.loadData('Module:Constants/data').skill


--Brute forcing some item uses to make things easier
--Brute forcing some item uses to make things easier
local itemUseArray = {
local itemUseArray = {
  Agility = {},
Agility = {},
  Attack = {},
Astrology = {'Stardust', 'Golden Stardust'},
  Combat = {'Gold Emerald Ring'},
Attack = {},
  Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
Combat = {'Gold Emerald Ring', 'Obsidian Cape', 'Throwing Power Gloves'},
  Crafting = {'Crown of Rhaelyx'},
Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
  Defence = {},
Crafting = {'Crown of Rhaelyx'},
  Farming = {'Compost', 'Weird Gloop', 'Bob's Rake'},
Defence = {},
  Firemaking = {'Crown of Rhaelyx'},
Farming = {'Compost', 'Weird Gloop', 'Bob's Rake'},
  Fishing = {'Amulet of Fishing', 'Message in a Bottle'},
Firemaking = {'Crown of Rhaelyx'},
  Fletching = {'Crown of Rhaelyx'},
Fishing = {'Amulet of Fishing', 'Message in a Bottle', 'Barbarian Gloves'},
  Herblore = {'Crown of Rhaelyx'},
Fletching = {'Crown of Rhaelyx'},
  Hitpoints = {},
Herblore = {'Crown of Rhaelyx'},
  Magic = {},
Hitpoints = {},
  Mining = {'Mining Gloves', 'Gem Gloves'},
Magic = {},
  Prayer = {},
Mining = {'Mining Gloves', 'Gem Gloves'},
  Ranged = {},
Prayer = {},
  Runecrafting = {'Crown of Rhaelyx'},
Ranged = {},
  Slayer = {},
Runecrafting = {'Crown of Rhaelyx'},
  Smithing = {'Smithing Gloves', 'Crown of Rhaelyx'},
Slayer = {},
  Strength = {},
Smithing = {'Smithing Gloves', 'Crown of Rhaelyx'},
  Summoning = {'Crown of Rhaelyx'},
Strength = {},
  Thieving = {'Chapeau Noir', 'Thieving Gloves', 'Gloves of Silence'},
Summoning = {'Crown of Rhaelyx'},
  Woodcutting = {},
Thieving = {'Chapeau Noir', 'Thieving Gloves', 'Gloves of Silence'},
  }
Woodcutting = {},
}
local potionUseArray = {
local potionUseArray = {
  [0] = 'Combat',
[0] = 'Combat',
  [1] = 'Combat',
[1] = 'Combat',
  [2] = 'Combat',
[2] = 'Combat',
  [3] = 'Combat',
[3] = 'Combat',
  [4] = 'Combat',
[4] = 'Combat',
  [5] = 'Combat',
[5] = 'Combat',
  [6] = 'Combat',
[6] = 'Combat',
  [7] = 'Woodcutting',
[7] = 'Woodcutting',
  [8] = 'Fishing',
[8] = 'Fishing',
  [9] = 'Firemaking',
[9] = 'Firemaking',
  [10] = 'Cooking',
[10] = 'Cooking',
  [11] = 'Mining',
[11] = 'Mining',
  [12] = 'Smithing',
[12] = 'Smithing',
  [13] = 'Thieving',
[13] = 'Thieving',
  [14] = 'Farming',
[14] = 'Farming',
  [15] = 'Fletching',
[15] = 'Fletching',
  [16] = 'Crafting',
[16] = 'Crafting',
  [17] = 'Runecrafting',
[17] = 'Runecrafting',
  [18] = 'Herblore',
[18] = 'Herblore',
  [19] = 'Combat',
[19] = 'Combat',
  [20] = 'Combat',
[20] = 'Combat',
  [21] = 'Combat',
[21] = 'Combat',
  [22] = 'Combat',
[22] = 'Combat',
  [23] = 'Combat',
[23] = 'Combat',
  [24] = 'Agility',
[24] = 'Agility',
  [25] = 'Summoning'
[25] = 'Summoning',
[26] = 'Combat',
[27] = 'Combat',
[28] = 'Combat',
[29] = 'Astrology'
}
}


function p._getItemUses(item, asList, addCategories)
function p._getItemUses(item, asList, addCategories)
  local useArray = {}
-- Another fun one. This time getting all the different possible ways an item can be used
  local categoryArray = {}
local categoryArray = {}
  local chr = asList and '* ' or ''
local skillUses = {}
  --Another fun one. This time getting all the different possible ways an item can be used
local otherUses = {}
local otherUseText = {
["Combat"] = Icons.Icon({'Combat'}),
["Upgrade"] = '[[Upgrading Items]]',
["Food"] = '[[Food]]',
["Chest"] = '[[Chest Drop Tables|Can Be Opened]]',
["Mastery"] = Icons.Icon({'Mastery'}),
["AllSkills"] = 'All skills',
["AltMagic"] = Icons.Icon({'Alt. Magic', type='skill'}),
["ChargeStone"] = 'Powering ' .. Icons.Icon({'Crown of Rhaelyx', type='item'}),
["Shop"] = Icons.Icon({'Shop'})
}


  --Before anything else, if this is a potion add it to the appropriate override section
local addUse = function(useName)
  if item.herbloreMasteryID ~= nil then
local skillID = (type(useName) == 'number' and useName) or SkillEnum[useName]
    table.insert(itemUseArray[potionUseArray[item.herbloreMasteryID]], item.name)
if type(skillID) == 'number' then
  end
if skillUses[skillID] == nil then
skillUses[skillID] = Constants.getSkillName(skillID)
end
elseif not otherUses[useName] then
otherUses[useName] = true
end
end
local hasUse = function(useName)
local skillID = (type(useName) == 'number' and useName) or SkillEnum[useName]
if type(skillID) == 'number' then
return (skillUses[skillID] ~= nil) or false
else
return otherUses[useName] or false
end
end


  --If the item has any modifiers that affect a given skill, add it to those lists
-- Check for any overrides within itemUseArray
  if item.modifiers ~= nil then
for useName, itemList in pairs(itemUseArray) do
    local skillArray = Constants.getModifierSkills(item.modifiers)
if Shared.contains(itemList, item.name) then
    for i, skillName in Shared.skpairs(skillArray) do
addUse(useName)
      table.insert(itemUseArray[skillName], item.name)
end
    end
end
  end


  --First things added to the list are non-skill things that are easy to check
-- If this is a potion add it to the appropriate uses table
  if Items.hasCombatStats(item) or Shared.contains(itemUseArray.Combat, item.name) then
if type(item.masteryID) == 'table' and item.masteryID[1] == SkillEnum.Herblore then
    table.insert(useArray, chr..Icons.Icon({'Combat'}))
-- Default to 'Combat' if unknown
  end
local potionUse = potionUseArray[item.masteryID[2]] or 'Combat'
addUse(potionUseArray[item.masteryID[2]] or 'Combat')
end


  if item.healsFor ~= nil then
-- If the item has any modifiers that affect a given skill, add it to those tables
    table.insert(categoryArray, '[[Category:Food Items]]')
-- Added an exception for Mastery Tokens since they were being incorrectly flagged as usable for all skills
    table.insert(useArray, chr..'[[Food]]')
if item.modifiers ~= nil and (item.isToken == nil or not item.isToken) then
  end
local skillArray = Constants.getModifierSkills(item.modifiers)
for i, skillName in ipairs(skillArray) do
addUse(skillName)
end
end


  if item.dropTable ~= nil then
--First things added to the list are non-skill things that are easy to check
    table.insert(categoryArray, '[[Category:Openable Items]]')
if not hasUse('Combat') and (Items.hasCombatStats(item) or item.specialAttacks ~= nil) then
    table.insert(useArray, chr..'[[Chest Drop Tables|Can Be Opened]]')  
addUse('Combat')
  end
end


  --Next, upgrading, crafting, herblore, fletching, and runecrafting since we have to sift through other items for these
-- Check if the item is an entry requirement for any Slayer area
  local canUpgrade = false
if not hasUse(SkillEnum.Slayer) and item.isEquipment then
  local canCraft = false
local slayerAreas = Areas.getAreas(function(area) return area.type == 'slayer' and type(area.entryRequirements) == 'table' end)
  local canFletch = false
for i, area in pairs(slayerAreas) do
  local canRunecraft = false
for j, req in pairs(area.entryRequirements) do
  local canHerblore = false
if req.type == "SlayerItem" and req.itemID == item.id then
  local canAgile = false
addUse(SkillEnum.Slayer)
  local canSummon = false
break
end
end
if hasUse(SkillEnum.Slayer) then
break
end
end
end


  if item.trimmedItemID ~= nil then
-- Can the item be upgraded, or is it part of an upgrade recipe?
    canUpgrade = true
if item.canUpgrade then
  end
addUse('Upgrade')
else
for i, item2 in pairs(ItemData.Items) do
if item2.itemsRequired ~= nil then
for j, req in ipairs(item2.itemsRequired) do
if req[1] == item.id then
addUse('Upgrade')
break
end
end
if hasUse('Upgrade') then
break
end
end
end
end
if hasUse('Upgrade') then
table.insert(categoryArray, '[[Category:Upgradeable Items]]')
end


  for i, item2 in pairs(ItemData.Items) do
if item.healsFor ~= nil then
    if item2.itemsRequired ~= nil then
table.insert(categoryArray, '[[Category:Food Items]]')
      for j, req in pairs(item2.itemsRequired) do
addUse('Food')
        if req[1] == item.id then
end
          canUpgrade = true
          break
        end
      end
    end


    if item2.craftReq ~= nil then
if item.canOpen then
      for j, req in pairs(item2.craftReq) do
table.insert(categoryArray, '[[Category:Openable Items]]')
        if req.id == item.id then
addUse('Chest')
          canCraft = true
end
          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
-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
      for j, req in pairs(item2.herbloreReq) do
-- All have somewhat consistent recipe data structures
        if req.id == item.id then
local recipeSkillIDs = {
          canHerblore = true
SkillEnum.Cooking,
          break
SkillEnum.Smithing,
        end
SkillEnum.Fletching,
      end
SkillEnum.Crafting,
    end
SkillEnum.Runecrafting,
   
SkillEnum.Herblore
    if item2.summoningReq ~= nil then
}
      for j, reqSet in pairs(item2.summoningReq) do
for i, recipeSkillID in ipairs(recipeSkillIDs) do
        for k, req in pairs(reqSet) do
if not hasUse(recipeSkillID) then
          if req.id == item.id then
local recipeKey = (recipeSkillID == SkillEnum.Herblore and 'Potions') or 'Recipes'
            canSummon = true
local skillName = Constants.getSkillName(recipeSkillID)
            break
-- Iterate over all recipes for the current skill
          end
for j, recipe in ipairs(SkillData[skillName][recipeKey]) do
        end
for k, itemCost in ipairs(recipe.itemCosts) do
      end
if itemCost.id == item.id then
    end
addUse(recipeSkillID)
  end
break
end
end
-- Some items (such as Arrow shafts) have multiple recipes
if not hasUse(recipeSkillID) and type(recipe.alternativeCosts) == 'table' then
for k, altCost in ipairs(recipe.alternativeCosts) do
for m, itemCost in ipairs(altCost.itemCosts) do
if itemCost.id == item.id then
addUse(recipeSkillID)
break
end
end
if hasUse(recipeSkillID) then
break
end
end
end
if hasUse(recipeSkillID) then
break
end
end
end
end


  --Check if Agility applies here
-- Firemaking
  canAgile = Shared.tableCount(Agility.getObstaclesForItem(item.id)) > 0
if not hasUse(SkillEnum.Firemaking) and type(item.masteryID) == 'table' and item.masteryID[1] == SkillEnum.Firemaking then
 
addUse(SkillEnum.Firemaking)
  --Agility
end
  if canAgile or Shared.contains(itemUseArray.Agility, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Agility', type='skill'}))
  end
  --Cooking
  if item.cookedItemID ~= nil or Shared.contains(itemUseArray.Cooking, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Cooking', type='skill'}))
  end
  --Crafting
  if canCraft or Shared.contains(itemUseArray.Crafting, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Crafting', type='skill'}))
  end
  --Farming
  if item.grownItemID ~= nil or Shared.contains(itemUseArray.Farming, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Farming', type='skill'}))
  end
  --Firemaking
  if SkillData.Firemaking[item.id + 1] ~= nil or Shared.contains(itemUseArray.Firemaking, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Firemaking', type='skill'}))
  end
  --Fishing
  if Shared.contains(itemUseArray.Fishing, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Fishing', type='skill'}))
  end
  --Fletching
  if canFletch or Shared.contains(itemUseArray.Fletching, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Fletching', type='skill'}))
  end
  --Herblore
  if canHerblore or Shared.contains(itemUseArray.Herblore, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Herblore', type='skill'}))
  end
  --Mining
  if Shared.contains(itemUseArray.Mining, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Mining', type='skill'}))
  end
  --Prayer
  if item.prayerPoints ~= nil or (Shared.contains(itemUseArray.Prayer, item.name) and not Shared.contains(itemUseArray.Combat, item.name)) then
    if item.prayerPoints ~= nil then table.insert(categoryArray, '[[Category:Buriable Items]]') end
    table.insert(useArray, chr..Icons.Icon({'Prayer', type='skill'}))
  end
  --Runecrafting
  if canRunecraft or Shared.contains(itemUseArray.Runecrafting, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Runecrafting', type='skill'}))
  end
  --Slayer
  if item.slayerCost ~= nil or Shared.contains(itemUseArray.Slayer, item.name) then
    table.insert(useArray, chr..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, chr..Icons.Icon({'Smithing', type='skill'}))
  end
  --Summoning
  if canSummon or (item.type == 'Shard' and item.category == 'Summoning') or item.type == 'Familiar' or Shared.contains(itemUseArray.Summoning, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Summoning', type='skill'}))
  end
  --Thieving
  if Shared.contains(itemUseArray.Thieving, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Thieving', type='skill'}))
  end
  --Woodcutting
  if Shared.contains(itemUseArray.Woodcutting, item.name) then
    table.insert(useArray, chr..Icons.Icon({'Woodcutting', type='skill'}))
  end
 
  --Other odds and ends:


  --Mastery Tokens are tied to 'Mastery'
-- Farming
  if item.type == 'Token' then
if not hasUse(SkillEnum.Farming) and item.grownItemID ~= nil then
    table.insert(useArray, chr..Icons.Icon({'Mastery'}))
addUse(SkillEnum.Farming)
  end
end


  --Skillcapes are tied to the appropriate skill
-- Agility
  --Except Max Skillcape, which is tied to all skills. (And so is the Signet Ring)
if not hasUse(SkillEnum.Agility) and Shared.tableCount(Agility.getObstaclesForItem(item.id)) > 0 then
  --And combat skillcapes, since combat skills don't get special treatment
addUse(SkillEnum.Agility)
 
end
  local ignoreCapes = {'Ranged Skillcape', 'Attack Skillcape', 'Strength Skillcape', 'Hitpoints Skillcape', 'Defence Skillcape'}
  if item.name == 'Max Skillcape' or item.name == 'Aorpheat's Signet Ring' or item.name == 'Cape of Completion' then
    table.insert(useArray, chr..'All skills')
  elseif item.name == 'Magic Skillcape' then
    table.insert(useArray, chr..Icons.Icon({'Magic', type='skill'}))
    table.insert(useArray, chr..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]
    --Avoiding double-listing the same skill twice
    if not Shared.contains(itemUseArray[skillName], item.name) then
      table.insert(useArray, chr..Icons.Icon({skillName, type='skill'}))
    end
  end


  if Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion' then table.insert(categoryArray, '[[Category:Skillcapes]]') end
-- Summoning
if not hasUse(SkillEnum.Summoning) then
for i, recipe in ipairs(SkillData.Summoning.Marks) do
-- Tablets & Non-shard items
if recipe.itemID == item.id or Shared.contains(recipe.nonShardItemCosts, item.id) then
addUse(SkillEnum.Summoning)
break
else
-- Shards
for j, itemCost in ipairs(recipe.itemCosts) do
if itemCost.id == item.id then
addUse(SkillEnum.Summoning)
break
end
end
if hasUse(SkillEnum.Summoning) then
break
end
end
end
end


  --Special note for Charge Stone of Rhaelyx
-- Prayer
  if item.name == 'Charge Stone of Rhaelyx' then
if item.prayerPoints ~= nil then
    table.insert(useArray, chr..'Powering '..Icons.Icon({'Crown of Rhaelyx', type='item'}))
table.insert(categoryArray, '[[Category:Buriable Items]]')
  end
if not hasUse(SkillEnum.Prayer) then
addUse(SkillEnum.Prayer)
end
end


  --Some items are needed to make shop purchases
-- Magic
  local shopArray = Shop.getItemCostArray(item.id)
if not (hasUse('Magic') and hasUse('AltMagic')) then
  if Shared.tableCount(shopArray) > 0 then
-- First check if the item its self is used in any spells
    table.insert(useArray, chr..Icons.Icon({'Shop'}))
local spellList = Magic.getSpellsForItem(item.id, true)
  end
for i, spell in ipairs(spellList) do
local useKey = (spell.type == 'AltMagic' and 'AltMagic' or 'Magic')
if not hasUse(useKey) then
addUse(useKey)
end
end
-- Check if the item provides runes, if it does check where they are used also
if item.providesRune ~= nil then
for i, runeID in ipairs(item.providesRune) do
if hasUse('Magic') and hasUse('AltMagic') then
break
else
local spellList = Magic.getSpellsForItem(runeID, false)
for j, spell in ipairs(spellList) do
local useKey = (spell.type == 'AltMagic' and 'AltMagic' or 'Magic')
if not hasUse(useKey) then
addUse(useKey)
end
end
end
end
end
end


  if canUpgrade then
-- Other odds and ends:
    if item.canUpgrade or (item.type == 'Armour' and item.canUpgrade == nil) then
      table.insert(categoryArray, '[[Category:Upgradeable Items]]')
    end
    table.insert(useArray, chr..'[[Upgrading Items]]')
  end


  local result = asList and table.concat(useArray,'\r\n') or table.concat(useArray, '<br/>')
-- Mastery Tokens are tied to 'Mastery'
  if addCategories then result = result..table.concat(categoryArray, '') end
if item.isToken and item.skill ~= nil then
  return result
addUse('Mastery')
end
 
-- Skillcapes are tied to the appropriate skill
-- Except Maximum 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', 'HP Skillcape', 'Defence Skillcape'}
if Shared.contains({'Maximum Skillcape', "Aorpheat's Signet Ring", 'Ring of Wealth', 'Cape of Completion'}, item.name) then
addUse('AllSkills')
elseif item.name == 'Magic Skillcape' then
addUse(SkillEnum.Magic)
addUse('AltMagic')
elseif Shared.contains(item.name, 'Skillcape') and not Shared.contains(ignoreCapes, item.name) then
local skillName = Shared.splitString(item.name, ' ')[1]
addUse(skillName)
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
addUse('ChargeStone')
end
 
--Some items are needed to make shop purchases
local shopArray = Shop.getItemCostArray(item.id)
if Shared.tableCount(shopArray) > 0 then
addUse('Shop')
end
 
-- Generate result text
local useArray = {}
local prefix, delim = asList and '* ' or '', asList and '\r\n' or '<br/>'
for skillID, skillName in Shared.spairs(skillUses, function(t, a, b) return t[a] < t[b] end) do
table.insert(useArray, prefix .. Icons.Icon({skillName, type='skill'}))
end
for use, _ in Shared.skpairs(otherUses) do
table.insert(useArray, prefix .. (otherUseText[use] or use))
end
 
return table.concat(useArray, delim) .. (addCategories and table.concat(categoryArray, '') or '')
end
end


function p.getItemUses(frame)
function p.getItemUses(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 = Items.getItem(itemName)
local item = Items.getItem(itemName)
  local addCategories = false
local addCategories = false
  local asList = true
local asList = true
  if frame.args ~= nil then  
if frame.args ~= nil then
    addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
    asList = frame.args.addCategories == nil or frame.args.addCategories == '' or frame.args.addCategories == 'true'
asList = frame.args.addCategories == nil or frame.args.addCategories == '' or frame.args.addCategories == 'true'
  end
end
  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"
  end
end


  return p._getItemUses(item, asList, addCategories)
return p._getItemUses(item, asList, addCategories)
end
end


function p._getItemUseTable(item)
function p._getItemUseTable(item)
  local useArray = {}
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
-- Loop through all items to find anything that can be upgraded using our source
  for i, item2 in pairs(ItemData.Items) do
for i, item2 in ipairs(ItemData.Items) do
    if item2.itemsRequired ~= nil then
if item2.itemsRequired ~= nil then
      for j, req in pairs(item2.itemsRequired) do
for j, req in pairs(item2.itemsRequired) do
        if req[1] == item.id then
if req[1] == item.id then
          local mat = item2.itemsRequired
local mat = item2.itemsRequired
          local xp = 'N/A'
local xp = 'N/A'
          local rowReq = 'None'
local rowReq = nil
          --Potions do have upgrade requirements though
--Potions do have upgrade requirements though
          if item2.potionTier ~= nil then
if item2.potionTier ~= nil then
            rowReq = Icons._MasteryReq(item2.name, potTierMastery[item2.potionTier])
rowReq = Icons._MasteryReq(item2.name, SkillData.Herblore.TierMasteryLevels[item2.potionTier + 1])
          end
end
          table.insert(useArray, {item = item2, qty = 1, mats = mat, skill = 'Upgrade', req = rowReq, xp = xp, gp = item2.trimmedGPCost})
table.insert(useArray, {item = {id = item2.id, name = item2.name}, qty = 1, mats = mat, skill = 'Upgrade', req = rowReq, xp = xp, gp = item2.trimmedGPCost})
          break
break
        end
end
      end
end
    end
end
    if item2.craftReq ~= nil then
end
      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


    if item2.summoningReq ~= nil then
-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
      for j, reqSet in pairs(item2.summoningReq) do
-- All have somewhat consistent recipe data structures
        for k, req in pairs(reqSet) do
local recipeSkillIDs = {
          if req.id == item.id then
SkillEnum.Cooking,
            local mat = Shared.clone(reqSet)
SkillEnum.Smithing,
            mat[k].qty = math.max(math.floor(1000 / math.max(item.sellsFor, 20)), 1)
SkillEnum.Fletching,
            local xp = 5 + 2 * math.floor(item2.summoningLevel * 0.2)
SkillEnum.Crafting,
            local rowReq = Icons._SkillReq('Summoning', item2.summoningLevel)
SkillEnum.Runecrafting,
            table.insert(useArray, {item = item2, qty = 25, mats = mat, skill = 'Summoning', reqVal = item2.summoningLevel, req = rowReq, xp = xp})
SkillEnum.Herblore
          end
}
        end
for i, recipeSkillID in ipairs(recipeSkillIDs) do
      end
local skillName = Constants.getSkillName(recipeSkillID)
    end
local recipeKey = (recipeSkillID == SkillEnum.Herblore and 'Potions') or 'Recipes'
-- Iterate over all recipes for the current skill
for j, recipe in ipairs(SkillData[skillName][recipeKey]) do
local costLists = {recipe.alternativeCosts or {}, {{["itemCosts"] = recipe.itemCosts}}}
for k, costList in pairs(costLists) do
for m, costDef in pairs(costList) do
for n, itemCost in ipairs(costDef.itemCosts) do
if itemCost.id == item.id then
local recipeItemIDs = nil
if recipeSkillID == SkillEnum.Herblore then
recipeItemIDs = recipe.potionIDs
elseif recipeSkillID == SkillEnum.Cooking then
recipeItemIDs = {recipe.itemID, recipe.perfectCookID}
else
recipeItemIDs = {recipe.itemID}
end
for o, recipeItemID in ipairs(recipeItemIDs) do
local recipeItem = Items.getItemByID(recipeItemID)
if recipeItem ~= nil then
local itemDef = {id = recipe.itemID, name = recipeItem.name}
local qty = (recipe.baseQuantity or 1) * (costDef.quantityMultiplier or 1)
local rowReq = recipe.level
local reqVal = nil
if recipeSkillID == SkillEnum.Herblore then
-- Herblore may also have a mastery requirement
local masteryLvl = SkillData.Herblore.TierMasteryLevels[o]
if masteryLvl ~= nil and masteryLvl > 1 then
local masteryReq = Icons._MasteryReq(recipeItem.name, masteryLvl)
reqVal = rowReq + masteryLvl * 0.01
rowReq = Icons._SkillReq(skillName, rowReq) .. '<br/>' .. masteryReq
end
end
table.insert(useArray, {item = itemDef, qty = qty, mats = costDef.itemCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = skillName, reqVal = reqVal, req = rowReq, xp = recipe.baseXP})
end
end
break
end
end
end
end
end
end


  end
-- Farming
  if item.grownItemID ~= nil then
if item.grownItemID ~= nil then
    local item2 = Items.getItemByID(item.grownItemID)
local item2 = Items.getItemByID(item.grownItemID)
    local mat = {{id = item.id, qty = item.seedsRequired}}
local mat = {{id = item.id, qty = item.seedsRequired}}
    local xp = item.farmingXP
local xp = item.farmingXP
    local rowReq = item.farmingLevel
local rowReq = item.farmingLevel
    local qty = 5
local qty = (item.tier ~= nil and item.tier == 'Tree' and 35 or 15)
    table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Farming', req = rowReq, xp = xp})
table.insert(useArray, {item = {id = item2.id, name = item2.name}, qty = qty, mats = mat, skill = 'Farming', req = rowReq, xp = xp})
  end
end
  if item.cookedItemID ~= nil then
    local item2 = Items.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 = Items.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


  --Handle shop purchases using Module:Shop
-- Agility
  local shopUses = Shop.getItemCostArray(item.id)
local obstacles = Agility.getObstaclesForItem(item.id)
  for i, purchase in Shared.skpairs(shopUses) do
for i, obstacle in ipairs(obstacles) do
    local rowReq = Shop.getRequirementString(purchase.unlockRequirements)
local itemCosts = {}
    local iconType = (purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 0) and 'item' or 'upgrade'
for j, itemDef in ipairs(obstacle.cost.items) do
    table.insert(useArray, {item = {name = purchase.name}, qty = 1, mats = purchase.cost.items, skill = 'Shop', req = rowReq, xp = 'N/A', gp = purchase.cost.gp, type = iconType})
table.insert(itemCosts, {id = itemDef[1], qty = itemDef[2]})
  end
end
local req = Agility._getObstacleRequirements(obstacle)
--local objType = (obstacle.category == nil and 'Pillar') or 'Obstacle'
table.insert(useArray, {item = {id = obstacle.id, name = obstacle.name}, qty = 1, mats = itemCosts, gp = obstacle.cost.gp, sc = obstacle.cost.slayerCoins, skill = 'Agility', req = req, type = 'skill'})
end


  --Finally build the table using what we've learned
-- Summoning
  table.sort(useArray, function(a, b)
for i, recipe in ipairs(SkillData.Summoning.Marks) do
                        local aReqVal = a.reqVal ~= nil and a.reqVal or a.req
local recipeGPCost = SkillData.Summoning.RecipeGPCost
                        local bReqVal = b.reqVal ~= nil and b.reqVal or b.req
local useShards = false
                        if a.skill ~= b.skill then
local recipeItem = nil
                          return a.skill < b.skill
for j, itemCost in ipairs(recipe.itemCosts) do
                        elseif type(aReqVal) ~= type(bReqVal) then
if itemCost.id == item.id then
                          return tostring(aReqVal) < tostring(bReqVal)
useShards = true
                        elseif aReqVal ~= bReqVal then
break
                          return aReqVal < bReqVal
end
                        else
end
                          return a.item.name < b.item.name
-- Non-shard items
                        end end)
for j, nonShardItemID in ipairs(recipe.nonShardItemCosts) do
if useShards or nonShardItemID == item.id then
-- Item is used in this particular synergy recipe
if recipeItem == nil then
recipeItem = Items.getItemByID(recipe.itemID)
end
local nonShardItem = Items.getItemByID(nonShardItemID)
local itemValue = math.max(nonShardItem.sellsFor, 20)
local nonShardQty = math.max(1, math.floor(recipeGPCost / itemValue))
local recipeCosts = Shared.clone(recipe.itemCosts)
table.insert(recipeCosts, {id = nonShardItemID, qty = nonShardQty})
table.insert(useArray, {item = {id = recipe.itemID, name = recipeItem.name}, qty = recipe.baseQuantity, mats = recipeCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = 'Summoning', req = recipe.level, xp = recipe.baseXP})
end
end
end


  local obstacles = Agility.getObstaclesForItem(item.id)
--Handle shop purchases using Module:Shop
local shopUses = Shop.getItemCostArray(item.id)
for i, purchase in ipairs(shopUses) do
local rowReq = Shop.getRequirementString(purchase.unlockRequirements)
local iconType = (purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 0) and 'item' or 'upgrade'
table.insert(useArray, {item = {name = purchase.name}, qty = 1, mats = purchase.cost.items, skill = 'Shop', req = rowReq, xp = 'N/A', gp = purchase.cost.gp, type = iconType})
end


  local spellUseTable = p._getSpellUseTable(item)
--Finally build the table using what we've learned
  local result = ''
table.sort(useArray, function(a, b)
  if Shared.tableCount(useArray) == 0 and Shared.tableCount(obstacles) == 0 then
local aReqVal = a.reqVal ~= nil and a.reqVal or a.req
    if string.len(spellUseTable) > 0 then
local bReqVal = b.reqVal ~= nil and b.reqVal or b.req
      return '==Uses==\r\n==='..Icons.Icon({'Magic', type='skill', size='30'})..'===\r\n'..spellUseTable
if a.skill ~= b.skill then
    else
return a.skill < b.skill
      return ''
elseif type(aReqVal) ~= type(bReqVal) then
    end
return tostring(aReqVal) < tostring(bReqVal)
  end
elseif aReqVal ~= bReqVal then
  result = result..'{| class="wikitable sortable"'
return aReqVal < bReqVal
  result = result..'\r\n!colspan=2|Item Created!!Type!!Requirements!!XP!!Ingredients'
else
  for i, row in pairs(useArray) do
return a.item.name < b.item.name
    local qty = row.qty ~= nil and row.qty or 1
end
    local iconType = row.type ~= nil and row.type or 'item'
end)
    result = result..'\r\n|-\r\n|data-sort-value="'..row.item.name..'"|'
    result = result..Icons.Icon({row.item.name, type=iconType, 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"|'..Icons.Icon({'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
      result = result..'||data-sort-value="'..row.xp..'"|'..row.xp..' '..Icons.Icon({row.skill, type='skill', notext=true})..' XP'
    end
    result = result..'||'
    for i, mat in Shared.skpairs(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]
      local matText = ''
     
      if i > 1 then result = result..'<br/>' end
      if matID >= 0 then
        -- Item
        local matItem = Items.getItemByID(matID)
        if matItem == nil then
          matText = 'ERROR: Failed to find item with ID ' .. matID .. '[[Category:Pages with Script Errors]]'
        else
          matText = Icons.Icon({matItem.name, type='item', qty=matQty})
        end
      elseif matID == -4 then
        -- GP
        matText = Icons.GP(SkillData.Summoning.Settings.recipeGPCost)
      elseif matID == -5 then
        -- Slayer Coins
        matText = Icons.SC(SkillData.Summoning.Settings.recipeGPCost)
      else
        matText = 'ERROR: Unknown item ID: ' .. matID .. ' [[Category:Pages with Script Errors]]'
      end
      result = result .. matText
    end
    if row.gp ~= nil then result = result..'<br/>'..Icons.GP(row.gp) end
  end


  --Agility obstacles are weird and get their own section
  for i, obst in Shared.skpairs(obstacles) do
    result = result..'\r\n|-\r\n|'
    result = result..Icons.Icon({"Agility", type="skill", size="50", notext=true})
    result = result..'||[[Agility#Obstacles|'..obst.name..']]'
    result = result..'||'..Icons.Icon({"Agility", type="skill"})


    --Adding the requirements for the Agility Obstacle
local resultPart = {}
    local reqArray = {}
if Shared.tableCount(useArray) > 0 then
    if obst.category == nil then --nil category means this is a passive pillar
local typeTextList = {
      table.insert(reqArray, Icons._SkillReq('Agility', 99))
["Shop"] = Icons.Icon({'Shop'}),
    elseif obst.category > 0 then --Otherwise it's category * 10
["Upgrade"] = '[[Upgrading Items|Upgrade]]'
      table.insert(reqArray, Icons._SkillReq('Agility', obst.category * 10))
}
    end
    --Then the other skill levels if any are added
    if obst.requirements ~= nil and obst.requirements.skillLevel ~= nil then
      for j, req in Shared.skpairs(obst.requirements.skillLevel) do
        table.insert(reqArray, Icons._SkillReq(Constants.getSkillName(req[1]), req[2]))
      end
    end
    result = result..'||'..table.concat(reqArray, '<br/>')


    --Just including 'N/A' for XP since it doesn't really apply for Agility Obstacles
-- Header
    result = result..'||N/A'
table.insert(resultPart, '{| class="wikitable stickyHeader sortable"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!colspan=2|Item Created!!Type!!Requirements!!XP!!Ingredients')


    --Finally the cost
-- Rows
    local cost = obst.cost
for i, row in ipairs(useArray) do
    local costArray = {}
local qty = row.qty ~= nil and row.qty or 1
    if cost.gp ~= nil and cost.gp > 0 then
local iconType = row.type ~= nil and row.type or 'item'
      table.insert(costArray, Icons.GP(cost.gp))
local iconName = row.item.name
    end
if row.skill == 'Agility' then
    if cost.slayerCoins ~= nil and cost.slayerCoins > 0 then
iconName = 'Agility'
      table.insert(costArray, Icons.SC(cost.slayerCoins))
end
    end
local typeName = row.skill ~= nil and row.skill or ''
    for j, mat in Shared.skpairs(cost.items) do
local typeText = typeTextList[typeName] or Icons.Icon({typeName, type='skill'}) or ''
      local item = Items.getItemByID(mat[1])
local reqVal, reqText = row.reqVal, 'None'
      table.insert(costArray, Icons.Icon({item.name, type="item", qty=mat[2]}))
if type(row.req) == 'number' then
    end
reqVal = row.req
   
reqText = Icons._SkillReq(typeName, row.req)
    result = result..'||'..table.concat(costArray, '<br/>')
elseif type(row.req) == 'string' then
 
reqText = row.req
  end
end
 
local xpVal, xpText = 0, 'N/A'
  result = result..'\r\n|}'
if type(row.xp) == 'string' then
  if string.len(spellUseTable) > 0 then
xpText = row.xp
    result = result..'\r\n==='..Icons.Icon({'Magic', type='skill', size='30'})..'===\r\n'..spellUseTable
elseif type(row.xp) == 'number' then
  end
xpVal = row.xp
  return '==Uses==\r\n'..result
xpText = Shared.formatnum(row.xp) .. ' ' .. Icons.Icon({typeName, type='skill', notext=true}) .. ' XP'
end
local matRow = {}
if type(row.mats) == 'table' then
for j, itemCost in ipairs(row.mats) do
local matItemID = itemCost.id or itemCost[1] or -1
local matItem = Items.getItemByID(matItemID)
local matQty = itemCost.qty or itemCost[2] or 1
if matItem == nil then
table.insert(matRow, 'ERROR: Failed to find item with ID ' .. itemCost.id .. '[[Category:Pages with script errors]]')
elseif type(matQty) == 'number' then
table.insert(matRow, Icons.Icon({matItem.name, type='item', qty=matQty}))
else
table.insert(matRow, Icons.Icon({matItem.name, type='item'}))
end
end
end
if row.gp ~= nil and row.gp > 0 then
table.insert(matRow, Icons.GP(row.gp))
end
if row.sc ~= nil and row.sc > 0 then
table.insert(matRow, Icons.SC(row.sc))
end
-- Item created
table.insert(resultPart, '\r\n|-\r\n|data-sort-value="' .. row.item.name .. '"| ')
table.insert(resultPart, Icons.Icon({iconName, row.item.name, type=iconType, notext=true, size=50}))
table.insert(resultPart, '\r\n| ')
if qty > 1 then
table.insert(resultPart, "'''" .. Shared.formatnum(qty) .. "x''' ")
end
table.insert(resultPart, Icons.Icon({iconName, row.item.name, type=iconType, noicon=true}))
-- Type
table.insert(resultPart, '\r\n|data-sort-value="' .. typeName .. '"| ' .. typeText)
-- Requirements
table.insert(resultPart, '\r\n|style="text-align:right;"')
if row.reqVal ~= nil then
table.insert(resultPart, ' data-sort-value="' .. reqVal .. '"')
end
table.insert(resultPart, '| ' .. reqText)
-- XP
table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. xpVal .. '"| ' .. xpText)
-- Ingredients
table.insert(resultPart, '\r\n| ' .. table.concat(matRow, '<br/>'))
end
table.insert(resultPart, '\r\n|}')
end
local spellUseTable = p._getSpellUseTable(item)
if spellUseTable ~= nil and spellUseTable ~= '' then
table.insert(resultPart, '\r\n===' .. Icons.Icon({'Magic', type='skill', size=30}) .. '===\r\n' .. spellUseTable)
end
if Shared.tableCount(resultPart) == 0 then
return ''
else
return '==Uses==\r\n' .. table.concat(resultPart)
end
end
end


function p.getItemUseTable(frame)
function p.getItemUseTable(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 = Items.getItem(itemName)
local item = Items.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"
  end
end


  return p._getItemUseTable(item)
return p._getItemUseTable(item)
end
end


function p._getSpellUseTable(item)
function p._getSpellUseTable(item)
  local spellList = Magic.getSpellsForRune(item.id)
local spellList = Magic.getSpellsForItem(item.id, true)
  --Bail immediately if no spells are found
--Bail immediately if no spells are found
  if Shared.tableCount(spellList) == 0 then
if Shared.tableCount(spellList) == 0 then
    return ''
return ''
  end
end


  local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
local resultPart = {}
  result = result..'!!Requirements'
table.insert(resultPart, '{|class="wikitable sortable"\r\n!colspan="2"|Spell')
  result = result..'!!Type!!style="width:275px"|Description'
table.insert(resultPart, '!!Requirements')
  result = result..'!!Runes'
table.insert(resultPart, '!!Type!!style="width:275px"|Description')
  for i, spell in pairs(spellList) do
table.insert(resultPart, '!!Runes')
    local rowTxt = '\r\n|-\r\n|data-sort-value="'..spell.name..'"|'
for i, spell in pairs(spellList) do
    if spell.type == 'Auroras' then
local rowPart = {}
      rowTxt = rowTxt..Icons.Icon({spell.name, type='aurora', notext=true, size=50})
table.insert(rowPart, '\r\n|-\r\n|data-sort-value="'..spell.name..'"|')
    elseif spell.type == 'Curses' then
local iconType = (spell.type == 'Auroras' and 'aurora') or (spell.type == 'Curses' and 'curse') or 'spell'
      rowTxt = rowTxt..Icons.Icon({spell.name, type='curse', notext=true, size=50})
table.insert(rowPart, Icons.Icon({spell.name, type=iconType, notext=true, size=50}))
    else
table.insert(rowPart, '||'..Icons.Icon({spell.name, type=iconType, noicon=true}))
      rowTxt = rowTxt..Icons.Icon({spell.name, type='spell', notext=true, size=50})
table.insert(rowPart, '||data-sort-value="'..spell.level..'"|'..Icons._SkillReq('Magic', spell.level))
    end
--Handle required items/dungeon clears
    rowTxt = rowTxt..'||[['..spell.name..']]'
if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
    rowTxt = rowTxt..'||data-sort-value="'..spell.magicLevelRequired..'"|'..Icons._SkillReq('Magic', spell.magicLevelRequired)
local reqItem = Items.getItemByID(spell.requiredItem)
    --Handle required items/dungeon clears
table.insert(rowPart, '<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped')
    if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
end
      local reqItem = Items.getItemByID(spell.requiredItem)
if spell.requiredDungeonCompletion ~= nil then
      rowTxt = rowTxt..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
    end
table.insert(rowPart, '<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears')
    if spell.requiredDungeonCompletion ~= nil then
end
      local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
table.insert(rowPart, '||data-sort-value="'..Magic.getSpellTypeIndex(spell.type)..'"|')
      rowTxt = rowTxt..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
table.insert(rowPart, Magic.getSpellTypeLink(spell.type))
    end
table.insert(rowPart, '||'..Magic._getSpellStat(spell, 'description'))
    rowTxt = rowTxt..'||data-sort-value="'..Magic.getSpellTypeIndex(spell.type)..'"|'
table.insert(rowPart, '||style="text-align:center"|')
    rowTxt = rowTxt..Magic.getSpellTypeLink(spell.type)
table.insert(rowPart, Magic._getSpellRunes(spell))
    if spell.type == 'Spells' then
table.insert(resultPart, table.concat(rowPart))
      rowTxt = rowTxt..'||Combat spell with a max hit of '..(spell.maxHit * 10)
end
    else
--Add the table end and add the table to the result string
      rowTxt = rowTxt..'||'..spell.description
table.insert(resultPart, '\r\n|}')
    end
return table.concat(resultPart)
    rowTxt = rowTxt..'||style="text-align:center"|'
    rowTxt = rowTxt..Magic._getSpellRunes(spell)
    result = result..rowTxt
  end
  --Add the table end and add the table to the result string
  result = result..'\r\n|}'
  return result
end
end


function p.getSpellUseTable(frame)
function p.getSpellUseTable(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 = Items.getItem(itemName)
local item = Items.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"
  end
end
 
return p._getSpellUseTable(item)
end


  return p._getSpellUseTable(item)
--[==[
-- Uncomment this block and execute 'p.test()' within the debug console
-- to test after making changes
function p.test()
local checkItems = {
'Gold Bar',
'Raw Shrimp',
'Coal Ore',
'Rune Platebody',
'Arrow Shafts',
'Garum Seeds',
'Rune Essence',
'Runite Bar',
'Water Rune',
'Steam Rune',
'Controlled Heat Potion II',
'Wolf',
'Cyclops',
'Leprechaun',
'Redwood Logs',
'Carrot Cake',
'Carrot Cake (Perfect)',
'Mantalyme Herb',
'Carrot',
'Topaz',
'Rune Essence',
'Infernal Claw',
'Chapeau Noir',
'Stardust',
'Rope',
'Ancient Ring of Mastery',
'Mysterious Stone',
'Mastery Token (Cooking)',
'Gem Gloves',
'Basic Bag'
}
local checkFuncs = {
p.getItemUses,
p.getItemUseTable
}
local errCount = 0
for i, item in ipairs(checkItems) do
local param = {args={item}}
mw.log('=' .. item .. '=')
for j, func in ipairs(checkFuncs) do
local callSuccess, retVal = pcall(func, param)
if not callSuccess then
errCount = errCount + 1
mw.log('Error with item "' .. item .. '": ' .. retVal)
else
mw.log(retVal)
end
end
end
if errCount == 0 then
mw.log('Test successful')
else
mw.log('Test failed with ' .. errCount .. ' failures')
end
end
end
--]==]


return p
return p

Revision as of 21:36, 6 March 2022

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

local p = {}

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

local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Magic = require('Module:Magic')
local Areas = require('Module:CombatAreas')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Agility = require('Module:Skills/Agility')
local Shop = require('Module:Shop')

local SkillEnum = mw.loadData('Module:Constants/data').skill

--Brute forcing some item uses to make things easier
local itemUseArray = {
	Agility = {},
	Astrology = {'Stardust', 'Golden Stardust'},
	Attack = {},
	Combat = {'Gold Emerald Ring', 'Obsidian Cape', 'Throwing Power Gloves'},
	Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
	Crafting = {'Crown of Rhaelyx'},
	Defence = {},
	Farming = {'Compost', 'Weird Gloop', 'Bob&apos;s Rake'},
	Firemaking = {'Crown of Rhaelyx'},
	Fishing = {'Amulet of Fishing', 'Message in a Bottle', 'Barbarian Gloves'},
	Fletching = {'Crown of Rhaelyx'},
	Herblore = {'Crown of Rhaelyx'},
	Hitpoints = {},
	Magic = {},
	Mining = {'Mining Gloves', 'Gem Gloves'},
	Prayer = {},
	Ranged = {},
	Runecrafting = {'Crown of Rhaelyx'},
	Slayer = {},
	Smithing = {'Smithing Gloves', 'Crown of Rhaelyx'},
	Strength = {},
	Summoning = {'Crown of Rhaelyx'},
	Thieving = {'Chapeau Noir', 'Thieving Gloves', 'Gloves of Silence'},
	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',
	[24] = 'Agility',
	[25] = 'Summoning',
	[26] = 'Combat',
	[27] = 'Combat',
	[28] = 'Combat',
	[29] = 'Astrology'
}

function p._getItemUses(item, asList, addCategories)
	-- Another fun one. This time getting all the different possible ways an item can be used
	local categoryArray = {}
	local skillUses = {}
	local otherUses = {}
	local otherUseText = {
		["Combat"] = Icons.Icon({'Combat'}),
		["Upgrade"] = '[[Upgrading Items]]',
		["Food"] = '[[Food]]',
		["Chest"] = '[[Chest Drop Tables|Can Be Opened]]',
		["Mastery"] = Icons.Icon({'Mastery'}),
		["AllSkills"] = 'All skills',
		["AltMagic"] = Icons.Icon({'Alt. Magic', type='skill'}),
		["ChargeStone"] = 'Powering ' .. Icons.Icon({'Crown of Rhaelyx', type='item'}),
		["Shop"] = Icons.Icon({'Shop'})
	}

	local addUse = function(useName)
		local skillID = (type(useName) == 'number' and useName) or SkillEnum[useName]
		if type(skillID) == 'number' then
			if skillUses[skillID] == nil then
				skillUses[skillID] = Constants.getSkillName(skillID)
			end
		elseif not otherUses[useName] then
			otherUses[useName] = true
		end
	end
	local hasUse = function(useName)
		local skillID = (type(useName) == 'number' and useName) or SkillEnum[useName]
		if type(skillID) == 'number' then
			return (skillUses[skillID] ~= nil) or false
		else
			return otherUses[useName] or false
		end
	end

	-- Check for any overrides within itemUseArray
	for useName, itemList in pairs(itemUseArray) do
		if Shared.contains(itemList, item.name) then
			addUse(useName)
		end
	end

	-- If this is a potion add it to the appropriate uses table
	if type(item.masteryID) == 'table' and item.masteryID[1] == SkillEnum.Herblore then
		-- Default to 'Combat' if unknown
		local potionUse = potionUseArray[item.masteryID[2]] or 'Combat'
		addUse(potionUseArray[item.masteryID[2]] or 'Combat')
	end

	-- If the item has any modifiers that affect a given skill, add it to those tables
	-- Added an exception for Mastery Tokens since they were being incorrectly flagged as usable for all skills
	if item.modifiers ~= nil and (item.isToken == nil or not item.isToken) then
		local skillArray = Constants.getModifierSkills(item.modifiers)
		for i, skillName in ipairs(skillArray) do
			addUse(skillName)
		end
	end

	--First things added to the list are non-skill things that are easy to check
	if not hasUse('Combat') and (Items.hasCombatStats(item) or item.specialAttacks ~= nil) then
		addUse('Combat')
	end

	-- Check if the item is an entry requirement for any Slayer area
	if not hasUse(SkillEnum.Slayer) and item.isEquipment then
		local slayerAreas = Areas.getAreas(function(area) return area.type == 'slayer' and type(area.entryRequirements) == 'table' end)
		for i, area in pairs(slayerAreas) do
			for j, req in pairs(area.entryRequirements) do
				if req.type == "SlayerItem" and req.itemID == item.id then
					addUse(SkillEnum.Slayer)
					break
				end
			end
			if hasUse(SkillEnum.Slayer) then
				break
			end
		end
	end

	-- Can the item be upgraded, or is it part of an upgrade recipe?
	if item.canUpgrade then
		addUse('Upgrade')
	else
		for i, item2 in pairs(ItemData.Items) do
			if item2.itemsRequired ~= nil then
				for j, req in ipairs(item2.itemsRequired) do
					if req[1] == item.id then
						addUse('Upgrade')
						break
					end
				end
				if hasUse('Upgrade') then
					break
				end
			end
		end
	end
	if hasUse('Upgrade') then
		table.insert(categoryArray, '[[Category:Upgradeable Items]]')
	end

	if item.healsFor ~= nil then
		table.insert(categoryArray, '[[Category:Food Items]]')
		addUse('Food')
	end

	if item.canOpen then
		table.insert(categoryArray, '[[Category:Openable Items]]')
		addUse('Chest')
	end

	-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
	-- All have somewhat consistent recipe data structures
	local recipeSkillIDs = {
		SkillEnum.Cooking,
		SkillEnum.Smithing,
		SkillEnum.Fletching,
		SkillEnum.Crafting,
		SkillEnum.Runecrafting,
		SkillEnum.Herblore
	}
	for i, recipeSkillID in ipairs(recipeSkillIDs) do
		if not hasUse(recipeSkillID) then
			local recipeKey = (recipeSkillID == SkillEnum.Herblore and 'Potions') or 'Recipes'
			local skillName = Constants.getSkillName(recipeSkillID)
			-- Iterate over all recipes for the current skill
			for j, recipe in ipairs(SkillData[skillName][recipeKey]) do
				for k, itemCost in ipairs(recipe.itemCosts) do
					if itemCost.id == item.id then
						addUse(recipeSkillID)
						break
					end
				end
				-- Some items (such as Arrow shafts) have multiple recipes
				if not hasUse(recipeSkillID) and type(recipe.alternativeCosts) == 'table' then
					for k, altCost in ipairs(recipe.alternativeCosts) do
						for m, itemCost in ipairs(altCost.itemCosts) do
							if itemCost.id == item.id then
								addUse(recipeSkillID)
								break
							end
						end
						if hasUse(recipeSkillID) then
							break
						end
					end
				end
				if hasUse(recipeSkillID) then
					break
				end
			end
		end
	end

	-- Firemaking
	if not hasUse(SkillEnum.Firemaking) and type(item.masteryID) == 'table' and item.masteryID[1] == SkillEnum.Firemaking then
		addUse(SkillEnum.Firemaking)
	end

	-- Farming
	if not hasUse(SkillEnum.Farming) and item.grownItemID ~= nil then
		addUse(SkillEnum.Farming)
	end

	-- Agility
	if not hasUse(SkillEnum.Agility) and Shared.tableCount(Agility.getObstaclesForItem(item.id)) > 0 then
		addUse(SkillEnum.Agility)
	end

	-- Summoning
	if not hasUse(SkillEnum.Summoning) then
		for i, recipe in ipairs(SkillData.Summoning.Marks) do
			-- Tablets & Non-shard items
			if recipe.itemID == item.id or Shared.contains(recipe.nonShardItemCosts, item.id) then
				addUse(SkillEnum.Summoning)
				break
			else
				-- Shards
				for j, itemCost in ipairs(recipe.itemCosts) do
					if itemCost.id == item.id then
						addUse(SkillEnum.Summoning)
						break
					end
				end
				if hasUse(SkillEnum.Summoning) then
					break
				end
			end
		end
	end

	-- Prayer
	if item.prayerPoints ~= nil then
		table.insert(categoryArray, '[[Category:Buriable Items]]')
		if not hasUse(SkillEnum.Prayer) then
			addUse(SkillEnum.Prayer)
		end
	end

	-- Magic
	if not (hasUse('Magic') and hasUse('AltMagic')) then
		-- First check if the item its self is used in any spells
		local spellList = Magic.getSpellsForItem(item.id, true)
		for i, spell in ipairs(spellList) do
			local useKey = (spell.type == 'AltMagic' and 'AltMagic' or 'Magic')
			if not hasUse(useKey) then
				addUse(useKey)
			end
		end
		-- Check if the item provides runes, if it does check where they are used also
		if item.providesRune ~= nil then
			for i, runeID in ipairs(item.providesRune) do
				if hasUse('Magic') and hasUse('AltMagic') then
					break
				else
					local spellList = Magic.getSpellsForItem(runeID, false)
					for j, spell in ipairs(spellList) do
						local useKey = (spell.type == 'AltMagic' and 'AltMagic' or 'Magic')
						if not hasUse(useKey) then
							addUse(useKey)
						end
					end
				end
			end
		end
	end

	-- Other odds and ends:

	-- Mastery Tokens are tied to 'Mastery'
	if item.isToken and item.skill ~= nil then
		addUse('Mastery')
	end

	-- Skillcapes are tied to the appropriate skill
	-- Except Maximum 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', 'HP Skillcape', 'Defence Skillcape'}
	if Shared.contains({'Maximum Skillcape', "Aorpheat's Signet Ring", 'Ring of Wealth', 'Cape of Completion'}, item.name) then
		addUse('AllSkills')
	elseif item.name == 'Magic Skillcape' then
		addUse(SkillEnum.Magic)
		addUse('AltMagic')
	elseif Shared.contains(item.name, 'Skillcape') and not Shared.contains(ignoreCapes, item.name) then
		local skillName = Shared.splitString(item.name, ' ')[1]
		addUse(skillName)
	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
		addUse('ChargeStone')
	end

	--Some items are needed to make shop purchases
	local shopArray = Shop.getItemCostArray(item.id)
	if Shared.tableCount(shopArray) > 0 then
		addUse('Shop')
	end

	-- Generate result text
	local useArray = {}
	local prefix, delim = asList and '* ' or '', asList and '\r\n' or '<br/>'
	for skillID, skillName in Shared.spairs(skillUses, function(t, a, b) return t[a] < t[b] end) do
		table.insert(useArray, prefix .. Icons.Icon({skillName, type='skill'}))
	end
	for use, _ in Shared.skpairs(otherUses) do
		table.insert(useArray, prefix .. (otherUseText[use] or use))
	end

	return table.concat(useArray, delim) .. (addCategories and table.concat(categoryArray, '') or '')
end

function p.getItemUses(frame)
	local itemName = frame.args ~= nil and frame.args[1] or frame
	local item = Items.getItem(itemName)
	local addCategories = false
	local asList = true
	if frame.args ~= nil then
		addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
		asList = frame.args.addCategories == nil or frame.args.addCategories == '' or frame.args.addCategories == 'true'
	end
	if item == nil then
		return "ERROR: No item named "..itemName.." exists in the data module"
	end

	return p._getItemUses(item, asList, addCategories)
end

function p._getItemUseTable(item)
	local useArray = {}

	-- Loop through all items to find anything that can be upgraded using our source
	for i, item2 in ipairs(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 = nil
					--Potions do have upgrade requirements though
					if item2.potionTier ~= nil then
						rowReq = Icons._MasteryReq(item2.name, SkillData.Herblore.TierMasteryLevels[item2.potionTier + 1])
					end
					table.insert(useArray, {item = {id = item2.id, name = item2.name}, qty = 1, mats = mat, skill = 'Upgrade', req = rowReq, xp = xp, gp = item2.trimmedGPCost})
					break
				end
			end
		end
	end

	-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
	-- All have somewhat consistent recipe data structures
	local recipeSkillIDs = {
		SkillEnum.Cooking,
		SkillEnum.Smithing,
		SkillEnum.Fletching,
		SkillEnum.Crafting,
		SkillEnum.Runecrafting,
		SkillEnum.Herblore
	}
	for i, recipeSkillID in ipairs(recipeSkillIDs) do
		local skillName = Constants.getSkillName(recipeSkillID)
		local recipeKey = (recipeSkillID == SkillEnum.Herblore and 'Potions') or 'Recipes'
		-- Iterate over all recipes for the current skill
		for j, recipe in ipairs(SkillData[skillName][recipeKey]) do
			local costLists = {recipe.alternativeCosts or {}, {{["itemCosts"] = recipe.itemCosts}}}
			for k, costList in pairs(costLists) do
				for m, costDef in pairs(costList) do
					for n, itemCost in ipairs(costDef.itemCosts) do
						if itemCost.id == item.id then
							local recipeItemIDs = nil
							if recipeSkillID == SkillEnum.Herblore then
								recipeItemIDs = recipe.potionIDs
							elseif recipeSkillID == SkillEnum.Cooking then
								recipeItemIDs = {recipe.itemID, recipe.perfectCookID}
							else
								recipeItemIDs = {recipe.itemID}
							end
							for o, recipeItemID in ipairs(recipeItemIDs) do
								local recipeItem = Items.getItemByID(recipeItemID)
								if recipeItem ~= nil then
									local itemDef = {id = recipe.itemID, name = recipeItem.name}
									local qty = (recipe.baseQuantity or 1) * (costDef.quantityMultiplier or 1)
									local rowReq = recipe.level
									local reqVal = nil
									if recipeSkillID == SkillEnum.Herblore then
										-- Herblore may also have a mastery requirement
										local masteryLvl = SkillData.Herblore.TierMasteryLevels[o]
										if masteryLvl ~= nil and masteryLvl > 1 then
											local masteryReq = Icons._MasteryReq(recipeItem.name, masteryLvl)
											reqVal = rowReq + masteryLvl * 0.01
											rowReq = Icons._SkillReq(skillName, rowReq) .. '<br/>' .. masteryReq
										end
									end
									table.insert(useArray, {item = itemDef, qty = qty, mats = costDef.itemCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = skillName, reqVal = reqVal, req = rowReq, xp = recipe.baseXP})
								end
							end
							break
						end
					end
				end
			end
		end
	end

	-- Farming
	if item.grownItemID ~= nil then
		local item2 = Items.getItemByID(item.grownItemID)
		local mat = {{id = item.id, qty = item.seedsRequired}}
		local xp = item.farmingXP
		local rowReq = item.farmingLevel
		local qty = (item.tier ~= nil and item.tier == 'Tree' and 35 or 15)
		table.insert(useArray, {item = {id = item2.id, name = item2.name}, qty = qty, mats = mat, skill = 'Farming', req = rowReq, xp = xp})
	end

	-- Agility
	local obstacles = Agility.getObstaclesForItem(item.id)
	for i, obstacle in ipairs(obstacles) do
		local itemCosts = {}
		for j, itemDef in ipairs(obstacle.cost.items) do
			table.insert(itemCosts, {id = itemDef[1], qty = itemDef[2]})
		end
		local req = Agility._getObstacleRequirements(obstacle)
		--local objType = (obstacle.category == nil and 'Pillar') or 'Obstacle'
		table.insert(useArray, {item = {id = obstacle.id, name = obstacle.name}, qty = 1, mats = itemCosts, gp = obstacle.cost.gp, sc = obstacle.cost.slayerCoins, skill = 'Agility', req = req, type = 'skill'})
	end

	-- Summoning
	for i, recipe in ipairs(SkillData.Summoning.Marks) do
		local recipeGPCost = SkillData.Summoning.RecipeGPCost
		local useShards = false
		local recipeItem = nil
		for j, itemCost in ipairs(recipe.itemCosts) do
			if itemCost.id == item.id then
				useShards = true
				break
			end
		end
		-- Non-shard items
		for j, nonShardItemID in ipairs(recipe.nonShardItemCosts) do
			if useShards or nonShardItemID == item.id then
				-- Item is used in this particular synergy recipe
				if recipeItem == nil then
					recipeItem = Items.getItemByID(recipe.itemID)
				end
				local nonShardItem = Items.getItemByID(nonShardItemID)
				local itemValue = math.max(nonShardItem.sellsFor, 20)
				local nonShardQty = math.max(1, math.floor(recipeGPCost / itemValue))
				local recipeCosts = Shared.clone(recipe.itemCosts)
				table.insert(recipeCosts, {id = nonShardItemID, qty = nonShardQty})
				table.insert(useArray, {item = {id = recipe.itemID, name = recipeItem.name}, qty = recipe.baseQuantity, mats = recipeCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = 'Summoning', req = recipe.level, xp = recipe.baseXP})
			end
		end
	end

	--Handle shop purchases using Module:Shop
	local shopUses = Shop.getItemCostArray(item.id)
	for i, purchase in ipairs(shopUses) do
		local rowReq = Shop.getRequirementString(purchase.unlockRequirements)
		local iconType = (purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 0) and 'item' or 'upgrade'
		table.insert(useArray, {item = {name = purchase.name}, qty = 1, mats = purchase.cost.items, skill = 'Shop', req = rowReq, xp = 'N/A', gp = purchase.cost.gp, type = iconType})
	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 resultPart = {}
	if Shared.tableCount(useArray) > 0 then
		local typeTextList = {
			["Shop"] = Icons.Icon({'Shop'}),
			["Upgrade"] = '[[Upgrading Items|Upgrade]]'
		}

		-- Header
		table.insert(resultPart, '{| class="wikitable stickyHeader sortable"')
		table.insert(resultPart, '\r\n|- class="headerRow-0"')
		table.insert(resultPart, '\r\n!colspan=2|Item Created!!Type!!Requirements!!XP!!Ingredients')

		-- Rows
		for i, row in ipairs(useArray) do
			local qty = row.qty ~= nil and row.qty or 1
			local iconType = row.type ~= nil and row.type or 'item'
			local iconName = row.item.name
			if row.skill == 'Agility' then
				iconName = 'Agility'
			end
			local typeName = row.skill ~= nil and row.skill or ''
			local typeText = typeTextList[typeName] or Icons.Icon({typeName, type='skill'}) or ''
			local reqVal, reqText = row.reqVal, 'None'
			if type(row.req) == 'number' then
				reqVal = row.req
				reqText = Icons._SkillReq(typeName, row.req)
			elseif type(row.req) == 'string' then
				reqText = row.req
			end
			local xpVal, xpText = 0, 'N/A'
			if type(row.xp) == 'string' then
				xpText = row.xp
			elseif type(row.xp) == 'number' then
				xpVal = row.xp
				xpText = Shared.formatnum(row.xp) .. ' ' .. Icons.Icon({typeName, type='skill', notext=true}) .. ' XP'
			end
			local matRow = {}
			if type(row.mats) == 'table' then
				for j, itemCost in ipairs(row.mats) do
					local matItemID = itemCost.id or itemCost[1] or -1
					local matItem = Items.getItemByID(matItemID)
					local matQty = itemCost.qty or itemCost[2] or 1
					if matItem == nil then
						table.insert(matRow, 'ERROR: Failed to find item with ID ' .. itemCost.id .. '[[Category:Pages with script errors]]')
					elseif type(matQty) == 'number' then
						table.insert(matRow, Icons.Icon({matItem.name, type='item', qty=matQty}))
					else
						table.insert(matRow, Icons.Icon({matItem.name, type='item'}))
					end
				end
			end
			if row.gp ~= nil and row.gp > 0 then
				table.insert(matRow, Icons.GP(row.gp))
			end
			if row.sc ~= nil and row.sc > 0 then
				table.insert(matRow, Icons.SC(row.sc))
			end
			-- Item created
			table.insert(resultPart, '\r\n|-\r\n|data-sort-value="' .. row.item.name .. '"| ')
			table.insert(resultPart, Icons.Icon({iconName, row.item.name, type=iconType, notext=true, size=50}))
			table.insert(resultPart, '\r\n| ')
			if qty > 1 then
				table.insert(resultPart, "'''" .. Shared.formatnum(qty) .. "x''' ")
			end
			table.insert(resultPart, Icons.Icon({iconName, row.item.name, type=iconType, noicon=true}))
			-- Type
			table.insert(resultPart, '\r\n|data-sort-value="' .. typeName .. '"| ' .. typeText)
			-- Requirements
			table.insert(resultPart, '\r\n|style="text-align:right;"')
			if row.reqVal ~= nil then
				table.insert(resultPart, ' data-sort-value="' .. reqVal .. '"')
			end
			table.insert(resultPart, '| ' .. reqText)
			-- XP
			table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. xpVal .. '"| ' .. xpText)
			-- Ingredients
			table.insert(resultPart, '\r\n| ' .. table.concat(matRow, '<br/>'))
		end
		table.insert(resultPart, '\r\n|}')
	end
	local spellUseTable = p._getSpellUseTable(item)
	if spellUseTable ~= nil and spellUseTable ~= '' then
		table.insert(resultPart, '\r\n===' .. Icons.Icon({'Magic', type='skill', size=30}) .. '===\r\n' .. spellUseTable)
	end
	if Shared.tableCount(resultPart) == 0 then
		return ''
	else
		return '==Uses==\r\n' .. table.concat(resultPart)
	end
end

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

	return p._getItemUseTable(item)
end

function p._getSpellUseTable(item)
	local spellList = Magic.getSpellsForItem(item.id, true)
	--Bail immediately if no spells are found
	if Shared.tableCount(spellList) == 0 then
		return ''
	end

	local resultPart = {}
	table.insert(resultPart, '{|class="wikitable sortable"\r\n!colspan="2"|Spell')
	table.insert(resultPart, '!!Requirements')
	table.insert(resultPart, '!!Type!!style="width:275px"|Description')
	table.insert(resultPart, '!!Runes')
	for i, spell in pairs(spellList) do
		local rowPart = {}
		table.insert(rowPart, '\r\n|-\r\n|data-sort-value="'..spell.name..'"|')
		local iconType = (spell.type == 'Auroras' and 'aurora') or (spell.type == 'Curses' and 'curse') or 'spell'
		table.insert(rowPart, Icons.Icon({spell.name, type=iconType, notext=true, size=50}))
		table.insert(rowPart, '||'..Icons.Icon({spell.name, type=iconType, noicon=true}))
		table.insert(rowPart, '||data-sort-value="'..spell.level..'"|'..Icons._SkillReq('Magic', spell.level))
		--Handle required items/dungeon clears
		if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
			local reqItem = Items.getItemByID(spell.requiredItem)
			table.insert(rowPart, '<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped')
		end
		if spell.requiredDungeonCompletion ~= nil then
			local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
			table.insert(rowPart, '<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears')
		end
		table.insert(rowPart, '||data-sort-value="'..Magic.getSpellTypeIndex(spell.type)..'"|')
		table.insert(rowPart, Magic.getSpellTypeLink(spell.type))
		table.insert(rowPart, '||'..Magic._getSpellStat(spell, 'description'))
		table.insert(rowPart, '||style="text-align:center"|')
		table.insert(rowPart, Magic._getSpellRunes(spell))
		table.insert(resultPart, table.concat(rowPart))
	end
	 --Add the table end and add the table to the result string
	table.insert(resultPart, '\r\n|}')
	return table.concat(resultPart)
end

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

	return p._getSpellUseTable(item)
end

--[==[
-- Uncomment this block and execute 'p.test()' within the debug console
-- to test after making changes
function p.test()
	local checkItems = {
		'Gold Bar',
		'Raw Shrimp',
		'Coal Ore',
		'Rune Platebody',
		'Arrow Shafts',
		'Garum Seeds',
		'Rune Essence',
		'Runite Bar',
		'Water Rune',
		'Steam Rune',
		'Controlled Heat Potion II',
		'Wolf',
		'Cyclops',
		'Leprechaun',
		'Redwood Logs',
		'Carrot Cake',
		'Carrot Cake (Perfect)',
		'Mantalyme Herb',
		'Carrot',
		'Topaz',
		'Rune Essence',
		'Infernal Claw',
		'Chapeau Noir',
		'Stardust',
		'Rope',
		'Ancient Ring of Mastery',
		'Mysterious Stone',
		'Mastery Token (Cooking)',
		'Gem Gloves',
		'Basic Bag'
	}
	local checkFuncs = {
		p.getItemUses,
		p.getItemUseTable
	}
	local errCount = 0
	for i, item in ipairs(checkItems) do
		local param = {args={item}}
		mw.log('=' .. item .. '=')
		for j, func in ipairs(checkFuncs) do
			local callSuccess, retVal = pcall(func, param)
			if not callSuccess then
				errCount = errCount + 1
				mw.log('Error with item "' .. item .. '": ' .. retVal)
			else
				mw.log(retVal)
			end
		end
	end
	if errCount == 0 then
		mw.log('Test successful')
	else
		mw.log('Test failed with ' .. errCount .. ' failures')
	end
end
--]==]

return p