Module:Items/UseTables: Difference between revisions

_getItemUseTable: Amend to show XP harvested per quantity for Farming
(Tweaked how UseTables figure out shop purchase uses)
(_getItemUseTable: Amend to show XP harvested per quantity for Farming)
(61 intermediate revisions by 4 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 SkillData = mw.loadData('Module:Skills/data')


local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Magic = require('Module:Magic')
local Magic = require('Module:Magic')
local Areas = require('Module:CombatAreas')
local Areas = require('Module:CombatAreas')
Line 13: Line 11:
local Agility = require('Module:Skills/Agility')
local Agility = require('Module:Skills/Agility')
local Shop = require('Module:Shop')
local Shop = require('Module:Shop')


--Brute forcing some item uses to make things easier
--Brute forcing some item uses to make things easier
local itemUseArray = {
local itemUseArray = {
  Agility = {},
Agility = {},
  Combat = {},
Astrology = {'melvorF:Stardust', 'melvorF:Golden_Stardust'},
  Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
Attack = {},
  Crafting = {'Crown of Rhaelyx'},
Cartography = {'melvorD:Crown_of_Rhaelyx'},
  Farming = {'Compost', 'Weird Gloop', 'Bob's Rake'},
Combat = {'melvorF:Gold_Emerald_Ring', 'melvorD:Obsidian_Cape', 'melvorF:Throwing_Power_Gloves'},
  Firemaking = {'Crown of Rhaelyx'},
Cooking = {'melvorD:Cooking_Gloves', 'melvorD:Crown_of_Rhaelyx'},
  Fishing = {'Amulet of Fishing', 'Message in a Bottle'},
Crafting = {'melvorD:Crown_of_Rhaelyx'},
  Fletching = {'Crown of Rhaelyx'},
Defence = {},
  Herblore = {'Crown of Rhaelyx'},
Farming = {'melvorD:Compost', 'melvorD:Weird_Gloop', 'melvorD:Bobs_Rake'},
  Mining = {'Mining Gloves', 'Gem Gloves'},
Firemaking = {'melvorD:Crown_of_Rhaelyx'},
  Prayer = {},
Fishing = {'melvorD:Amulet_of_Fishing', 'melvorD:Message_In_A_Bottle', 'melvorD:Barbarian_Gloves'},
  Runecrafting = {'Crown of Rhaelyx'},
Fletching = {'melvorD:Crown_of_Rhaelyx'},
  Slayer = {},
Herblore = {'melvorD:Crown_of_Rhaelyx'},
  Smithing = {'Smithing Gloves', 'Crown of Rhaelyx'},
Hitpoints = {},
  Thieving = {'Chapeau Noir', 'Thieving Gloves', 'Gloves of Silence'},
Magic = {},
  Woodcutting = {},
Mining = {'melvorD:Mining_Gloves', 'melvorD:Gem_Gloves'},
  }
Prayer = {},
local potionUseArray = {
Ranged = {},
  [0] = 'Combat',
Runecrafting = {'melvorD:Crown_of_Rhaelyx'},
  [1] = 'Combat',
Slayer = {},
  [2] = 'Combat',
Smithing = {'melvorD:Smithing_Gloves', 'melvorD:Crown_of_Rhaelyx'},
  [3] = 'Combat',
Strength = {},
  [4] = 'Combat',
Summoning = {'melvorD:Crown_of_Rhaelyx'},
  [5] = 'Combat',
Thieving = {'melvorF:Chapeau_Noir', 'melvorF:Thieving_Gloves', 'melvorF:Gloves_of_Silence'},
  [6] = 'Combat',
Township = {},
  [7] = 'Woodcutting',
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'
}


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'}),
["TownshipTask"] = Icons.Icon({'Tasks', type='township'})
}
 
local addUse = function(useName)
local skillID = Constants.getSkillID(useName)
if skillID == nil then
-- May have been passed a skill ID instead
local skillName = Constants.getSkillName(useName)
if skillName ~= nil then
skillID = useName
skillUses[skillID] = skillName
end
else
skillUses[skillID] = useName
end


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


  --First things added to the list are non-skill things that are easy to check
-- Check for any overrides within itemUseArray
  if item.equipmentSlot ~= nil or Shared.contains(itemUseArray.Combat, item.name) then
for useName, itemList in pairs(itemUseArray) do
    if item.equipmentSlot ~= nil then
if Shared.contains(itemList, item.id) then
      --table.insert(categoryArray, '[[Category:'..Items.getEquipmentSlotName(item.equipmentSlot)..' Slot Items]]')  
addUse(useName)
    end
end
    table.insert(useArray, chr..Icons.Icon({'Combat'}))
end
  end
  if item.healsFor ~= nil then
    table.insert(categoryArray, '[[Category:Food Items]]')
    table.insert(useArray, chr..'[[Food]]')
  end
  if item.dropTable ~= nil then
    table.insert(categoryArray, '[[Category:Openable Items]]')
    table.insert(useArray, chr..'[[Chest Drop Tables|Can Be Opened]]')
  end


  --Next, upgrading, crafting, herblore, fletching, and runecrafting since we have to sift through other items for these
-- If the item has any modifiers that affect a given skill, add it to those tables
  local canUpgrade = false
-- Added special handling for Mastery Tokens since they were being incorrectly flagged as usable for all skills
  local canCraft = false
if item.modifiers ~= nil then
  local canFletch = false
if item.modifiers.masteryToken ~= nil then
  local canRunecraft = false
-- Mastery tokens
  local canHerblore = false
addUse('Mastery')
  local canAgile = false
else
  if item.trimmedItemID ~= nil then
local globalMods = {
    canUpgrade = true
'increasedChanceToDoubleItemsGlobal',
  else
'decreasedChanceToDoubleItemsGlobal',
    for i, item2 in pairs(ItemData.Items) do
'increasedGlobalSkillIntervalPercent',
      if item2.itemsRequired ~= nil then
'decreasedGlobalSkillIntervalPercent',
        for j, req in pairs(item2.itemsRequired) do
'increasedGlobalSkillXP',
          if req[1] == item.id then
'decreasedGlobalSkillXP'
            canUpgrade = true
}
            break
for i, globalMod in ipairs(globalMods) do
          end
if item.modifiers[globalMod] ~= nil then
        end
addUse('AllSkills')
      end
break
end
end
if not hasUse('AllSkills') then
local skillArray = Constants.getModifierSkills(item.modifiers)
for i, skillName in ipairs(skillArray) do
addUse(skillName)
end
end
end
end


      if item2.craftReq ~= nil then
--First things added to the list are non-skill things that are easy to check
        for j, req in pairs(item2.craftReq) do
if not hasUse('Combat') and (Items.hasCombatStats(item) or item.specialAttacks ~= nil) then
          if req.id == item.id then
addUse('Combat')
            canCraft = true
end
            break
          end
        end
      end


      if item2.fletchReq ~= nil then
-- Check if the item is an entry requirement for any Slayer area
        for j, req in pairs(item2.fletchReq) do
if not hasUse('Slayer') and (item.validSlots ~= nil or item.occupiesSlots ~= nil or item.equipmentStats ~= nil) then
          if req.id == item.id then
local slayerAreas = Areas.getAreas(function(area) return area.type == 'slayerArea' and type(area.entryRequirements) == 'table' end)
            canFletch = true
for i, area in ipairs(slayerAreas) do
            break
for j, req in ipairs(area.entryRequirements) do
          end
if req.type == "SlayerItem" and req.itemID == item.id then
        end
addUse('Slayer')
      end
break
end
end
if hasUse('Slayer') then
break
end
end
end


      if item2.runecraftReq ~= nil then
-- Is the item a cost in an upgrade?
        for j, req in pairs(item2.runecraftReq) do
for i, upgrade in ipairs(GameData.rawData.itemUpgrades) do
          if req.id == item.id then
for j, itemCost in ipairs(upgrade.itemCosts) do
            canRunecraft = true
if itemCost.id == item.id then
            break
addUse('Upgrade')
          end
table.insert(categoryArray, '[[Category:Upgradeable Items]]')
        end
break
      end
end
end
if hasUse('Upgrade') then
break
end
end


      if item2.herbloreReq ~= nil then
if item.healsFor ~= nil then
        for j, req in pairs(item2.herbloreReq) do
table.insert(categoryArray, '[[Category:Food Items]]')
          if req.id == item.id then
addUse('Food')
            canHerblore = true
end
            break
          end
        end
      end
    end
  end
  --Check if Agility applies here
  canAgile = Shared.tableCount(Agility.getObstaclesForItem(item.id)) > 0


  if canUpgrade then
if item.dropTable ~= nil then
    if item.canUpgrade or (item.type == 'Armour' and item.canUpgrade == nil) then
table.insert(categoryArray, '[[Category:Openable Items]]')
      table.insert(categoryArray, '[[Category:Upgradeable Items]]')
addUse('Chest')
    end
end
    table.insert(useArray, chr..'[[Upgrading Items]]')
  end
 
  --Agility
  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) 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
  --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'
-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
  if item.type == 'Token' then
-- All have somewhat consistent recipe data structures
    table.insert(useArray, chr..Icons.Icon({'Mastery'}))
local recipeSkillIDs = {
  end
'melvorD:Cooking',
'melvorD:Smithing',
'melvorD:Fletching',
'melvorD:Crafting',
'melvorD:Runecrafting',
'melvorD:Herblore'
}
for i, recipeSkillID in ipairs(recipeSkillIDs) do
if not hasUse(recipeSkillID) then
local _, localSkillID = GameData.getLocalID(recipeSkillID)
-- Iterate over all recipes for the current skill
for j, recipe in ipairs(SkillData[localSkillID].recipes) 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


  --Skillcapes are tied to the appropriate skill
-- Firemaking
  --Except Max Skillcape, which is tied to all skills. (And so is the Signet Ring)
if not hasUse('melvorD:Firemaking') then
  --And combat skillcapes, since combat skills don't get special treatment
for i, recipe in ipairs(SkillData.Firemaking.logs) do
 
if recipe.logID == item.id then
  local ignoreCapes = {'Ranged Skillcape', 'Attack Skillcape', 'Strength Skillcape', 'Hitpoints Skillcape', 'Defence Skillcape'}
addUse('melvorD:Firemaking')
  if item.name == 'Max Skillcape' or item.name == 'Aorpheat's Signet Ring' or item.name == 'Cape of Completion' then
break
    table.insert(useArray, chr..'All skills')
end
  elseif item.name == 'Magic Skillcape' then
end
    table.insert(useArray, chr..Icons.Icon({'Magic', type='skill'}))
end
    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]
    table.insert(useArray, chr..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
-- Farming
if not hasUse('melvorD:Farming') then
for i, recipe in ipairs(SkillData.Farming.recipes) do
if recipe.seedCost.id == item.id then
addUse('melvorD:Farming')
break
end
end
end


  --Special note for Charge Stone of Rhaelyx
-- Agility
  if item.name == 'Charge Stone of Rhaelyx' then
if not hasUse('melvorD:Agility') and not Shared.tableIsEmpty(Agility.getObstaclesForItem(item.id)) then
    table.insert(useArray, chr..'Powering '..Icons.Icon({'Crown of Rhaelyx', type='item'}))
addUse('melvorD:Agility')
  end
end


  --Some items are needed to make shop purchases
-- Summoning
  local shopArray = Shop.getItemCostArray(item.id)
if not hasUse('melvorD:Summoning') then
  if Shared.tableCount(shopArray) > 0 then
for i, recipe in ipairs(SkillData.Summoning.recipes) do
    table.insert(useArray, chr..Icons.Icon({'Shop'}))
-- Tablets & Non-shard items
  end
if recipe.productID == item.id or Shared.contains(recipe.nonShardItemCosts, item.id) then
addUse('melvorD:Summoning')
break
else
-- Shards
for j, itemCost in ipairs(recipe.itemCosts) do
if itemCost.id == item.id then
addUse('melvorD:Summoning')
break
end
end
end
end
end


  local result = asList and table.concat(useArray,'\r\n') or table.concat(useArray, '<br/>')
-- Prayer
  if addCategories then result = result..table.concat(categoryArray, '') end
if item.prayerPoints ~= nil then
  return result
table.insert(categoryArray, '[[Category:Buriable Items]]')
if not hasUse('melvorD:Prayer') then
addUse('melvorD: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.getSpellsUsingItem(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.providedRunes ~= nil then
for i, rune in ipairs(item.providedRunes) do
if hasUse('Magic') and hasUse('AltMagic') then
break
else
local spellList = Magic.getSpellsUsingItem(rune.id, 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:
 
-- 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
if item.tier == 'Skillcape' then
local ignoreCapes = {
'melvorD:Attack_Skillcape',
'melvorD:Strength_Skillcape',
'melvorD:Defence_Skillcape',
'melvorD:Hitpoints_Skillcape',
'melvorF:Ranged_Skillcape',
'melvorTotH:Superior_Attack_Skillcape',
'melvorTotH:Superior_Strength_Skillcape',
'melvorTotH:Superior_Defence_Skillcape',
'melvorTotH:Superior_Hitpoints_Skillcape',
'melvorTotH:Superior_Ranged_Skillcape',
}
local allCapes = {
'melvorF:Max_Skillcape',
'melvorF:Cape_of_Completion',
'melvorTotH:Superior_Max_Skillcape',
'melvorTotH:Superior_Cape_Of_Completion'
}
if Shared.contains(allCapes, item.id) then
addUse('AllSkills')
elseif Shared.contains({'melvorF:Magic_Skillcape', 'melvorTotH:Superior_Magic_Skillcape'}, item.id) then
addUse('melvorD:Magic')
addUse('AltMagic')
elseif not Shared.contains(ignoreCapes, item.id) then
local splitName = Shared.splitString(item.name, ' ')
local skillName = (splitName[1] == 'Superior' and splitName[2]) or splitName[1]
addUse(skillName)
end
table.insert(categoryArray, '[[Category:Skillcapes]]')
end
 
--Special note for Charge Stone of Rhaelyx
if item.id == 'melvorD:Charge_Stone_of_Rhaelyx' then
addUse('ChargeStone')
end
 
--Some items are needed to make shop purchases
local shopArray = Shop.getItemCostArray(item.id)
if not Shared.tableIsEmpty(shopArray) then
addUse('Shop')
end
 
-- Township Tasks
for _, task in ipairs(SkillData.Township.tasks) do
if task.goals.items[1] ~= nil then -- Skip tasks with no items
if GameData.getEntityByID(task.goals.items, item.id) then
addUse('TownshipTask')
break
end
end
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 Shared.printError('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}
 
-- Loop through all upgrades to find anything that can be upgraded using our source
for i, upgrade in ipairs(GameData.rawData.itemUpgrades) do
for j, itemCost in ipairs(upgrade.itemCosts) do
if itemCost.id == item.id then
local rowReq = nil
-- Potions do have upgrade requirements though
local upgradeItem = Items.getItemByID(upgrade.upgradedItemID)
if upgradeItem ~= nil and upgradeItem.charges ~= nil and upgradeItem.tier ~= nil then
local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', upgradeItem.tier + 1)
if levelUnlock ~= nil then
rowReq = Icons._MasteryReq(upgradeItem.name, levelUnlock.level)
end
end
table.insert(useArray, {item = {id = upgradeItem.id, name = upgradeItem.name}, qty = 1, mats = upgrade.itemCosts, skill = 'Upgrade', req = rowReq, xp = 'N/A', gp = upgrade.gpCost, sc = upgrade.scCost})
end
end
end


  --First, loop through all items to find anything that can be made or upgraded into using our source
-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
  for i, item2 in pairs(ItemData.Items) do
-- All have somewhat consistent recipe data structures
    if item2.itemsRequired ~= nil then
local recipeSkillIDs = {
      for j, req in pairs(item2.itemsRequired) do
'melvorD:Cooking',
        if req[1] == item.id then
'melvorD:Smithing',
          local mat = item2.itemsRequired
'melvorD:Fletching',
          local xp = 'N/A'
'melvorD:Crafting',
          local rowReq = 'None'
'melvorD:Runecrafting',
          --Potions do have upgrade requirements though
'melvorD:Herblore'
          if item2.potionTier ~= nil then
}
            rowReq = Icons._MasteryReq(item2.name, potTierMastery[item2.potionTier])
for i, recipeSkillID in ipairs(recipeSkillIDs) do
          end
local skillName = Constants.getSkillName(recipeSkillID)
          table.insert(useArray, {item = item2, qty = 1, mats = mat, skill = 'Upgrade', req = rowReq, xp = xp, gp = item2.trimmedGPCost})
local _, localSkillID = GameData.getLocalID(recipeSkillID)
          break
        end
-- Iterate over all recipes for the current skill
      end
for j, recipe in ipairs(SkillData[localSkillID].recipes) do
    end
local costLists = {recipe.alternativeCosts or {}, {{["itemCosts"] = recipe.itemCosts}}}
    if item2.craftReq ~= nil then
for k, costList in pairs(costLists) do
      for j, req in pairs(item2.craftReq) do
for m, costDef in pairs(costList) do
        if req.id == item.id then
for n, itemCost in ipairs(costDef.itemCosts) do
          local mat = item2.craftReq
if itemCost.id == item.id then
          local xp = item2.craftingXP
local recipeItemIDs = nil
          local rowReq = item2.craftingLevel
if recipeSkillID == 'melvorD:Herblore' then
          local qty = item2.craftQty
recipeItemIDs = recipe.potionIDs
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Crafting', req = rowReq, xp = xp})
elseif recipeSkillID == 'melvorD:Cooking' then
          break
recipeItemIDs = {recipe.productID, recipe.perfectCookID}
        end
else
      end
recipeItemIDs = {recipe.productID}
    end
end
    if item2.fletchReq ~= nil then
for o, recipeItemID in ipairs(recipeItemIDs) do
      for j, req in pairs(item2.fletchReq) do
local recipeItem = Items.getItemByID(recipeItemID)
        if req.id == item.id then
if recipeItem ~= nil then
          local xp = item2.fletchingXP
local itemDef = {id = recipe.itemID, name = recipeItem.name}
          local rowReq = item2.fletchingLevel
local qty = (recipe.baseQuantity or 1) * (costDef.quantityMultiplier or 1)
          --Arrow Shafts are special and have to be treated specially
local rowReq = recipe.level
          local qty = item2.fletchQty
local reqVal = nil
          local mat = item2.fletchReq
if recipeSkillID == 'melvorD:Herblore' then
          if item2.name == 'Arrow Shafts' then
-- Herblore may also have a mastery requirement
            mat = {{id = item.id, qty = 1}}
local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', recipeItem.tier + 1)
            qty =  qty + (qty * item.id)
if levelUnlock ~= nil and levelUnlock.level > 1 then
          end
local masteryReq = Icons._MasteryReq(recipeItem.name, levelUnlock.level)
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Fletching', req = rowReq, xp = xp})
reqVal = rowReq + levelUnlock.level * 0.01
          break
rowReq = Icons._SkillReq(skillName, rowReq) .. '<br/>' .. masteryReq
        end
end
      end
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.baseExperience})
    if item2.smithReq ~= nil then
end
      for j, req in pairs(item2.smithReq) do
end
        if req.id == item.id then
break
          local mat = item2.smithReq
end
          local xp = item2.smithingXP
end
          local rowReq = item2.smithingLevel
end
          local qty = item2.smithingQty
end
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Smithing', req = rowReq, xp = xp})
end
          break
end
        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 = Items.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 = 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
-- Farming
  local shopUses = Shop.getItemCostArray(item.id)
for i, recipe in ipairs(SkillData.Farming.recipes) do
  for i, purchase in Shared.skpairs(shopUses) do
if recipe.seedCost.id == item.id then
    local rowReq = Shop.getRequirementString(purchase.unlockRequirements)
local product = Items.getItemByID(recipe.productId)
    local iconType = (purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 0) and 'item' or 'upgrade'
local mat = {{id = recipe.seedCost.id, quantity = recipe.seedCost.quantity}}
    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})
local rowReq = recipe.level
  end
local category = GameData.getEntityByID(SkillData.Farming.categories, recipe.categoryID)
local qty = 5 * category.harvestMultiplier
local xp = recipe.baseExperience
table.insert(useArray, {item = {id = product.id, name = product.name}, qty = qty, mats = mat, skill = 'Farming', req = rowReq, xp = xp})
end
end


  --Finally build the table using what we've learned
-- Agility
  table.sort(useArray, function(a, b)
local obstacles = Agility.getObstaclesForItem(item.id)
                        local aReqVal = a.reqVal ~= nil and a.reqVal or a.req
for i, obstacle in ipairs(obstacles) do
                        local bReqVal = b.reqVal ~= nil and b.reqVal or b.req
local itemCosts = {}
                        if a.skill ~= b.skill then
for j, itemDef in ipairs(obstacle.itemCosts) do
                          return a.skill < b.skill
table.insert(itemCosts, {id = itemDef.id, quantity = itemDef.quantity})
                        elseif type(aReqVal) ~= type(bReqVal) then
end
                          return tostring(aReqVal) < tostring(bReqVal)
local req = Agility._getObstacleRequirements(obstacle)
                        elseif aReqVal ~= bReqVal then
--local objType = (obstacle.category == nil and 'Pillar') or 'Obstacle'
                          return aReqVal < bReqVal
table.insert(useArray, {item = {id = obstacle.id, name = obstacle.name}, qty = 1, mats = itemCosts, gp = obstacle.gpCost, sc = obstacle.scCost, skill = 'Agility', req = req, type = 'skill'})
                        else
end
                          return a.item.name < b.item.name
                        end end)


  local spellUseTable = p._getSpellUseTable(item)
-- Summoning
  local result = ''
for i, recipe in ipairs(SkillData.Summoning.recipes) do
  if Shared.tableCount(useArray) == 0 then
local recipeGPCost = SkillData.Summoning.recipeGPCost
    if string.len(spellUseTable) > 0 then
local useShards = false
      return '==Uses==\r\n==='..Icons.Icon({'Magic', type='skill', size='30'})..'===\r\n'..spellUseTable
local recipeItem = nil
    else
for j, itemCost in ipairs(recipe.itemCosts) do
      return ''
if itemCost.id == item.id then
    end
useShards = true
  end
break
  result = result..'{| class="wikitable sortable"'
end
  result = result..'\r\n!colspan=2|Item Created!!Type!!Requirements!!XP!!Ingredients'
end
  for i, row in pairs(useArray) do
-- Non-shard items
    local qty = row.qty ~= nil and row.qty or 1
-- Familiar recipes may also have a currency cost without any non-shard
    local iconType = row.type ~= nil and row.type or 'item'
-- items, so account for this with a dummy ID such that one iteration
    result = result..'\r\n|-\r\n|data-sort-value="'..row.item.name..'"|'
-- of the below loop always occurs
    result = result..Icons.Icon({row.item.name, type=iconType, notext=true, size=50})..'||'
local nonShardItemIDs = (Shared.tableIsEmpty(recipe.nonShardItemCosts) and {''} or recipe.nonShardItemCosts)
    if qty > 1 then result = result.."'''"..qty.."x''' " end
for j, nonShardItemID in ipairs(nonShardItemIDs) do
    result = result..'[['..row.item.name..']]'
if useShards or nonShardItemID == item.id then
    if row.skill == 'Upgrade' then
-- Item is used in this particular synergy recipe
      result = result..'||data-sort-value="Upgrade"|[[Upgrading Items|Upgrade]]'
if recipeItem == nil then
    elseif row.skill == 'Shop' then
recipeItem = Items.getItemByID(recipe.productID)
      result = result..'||data-sort-value="Shop"|'..Icons.Icon({'Shop'})
end
    else
local nonShardItem = Items.getItemByID(nonShardItemID)
      result = result..'||data-sort-value="'..row.skill..'"|'..Icons.Icon({row.skill, type='skill'})
local recipeCosts = Shared.clone(recipe.itemCosts)
    end
local recipeCosts = {}
    if type(row.req) == 'number' then
for k, itemCost in ipairs(recipe.itemCosts) do
      result = result..'|| data-sort-value="'..row.req..'"|'..Icons._SkillReq(row.skill, row.req)
table.insert(recipeCosts, {id = itemCost.id, quantity = itemCost.quantity})
    elseif row.reqVal ~= nil then
end
      result = result..'|| data-sort-value="'..row.reqVal..'"|'..row.req
if nonShardItem ~= nil then
    else
-- Item ID can be nil for recipes such as Leprechaun or Cyclops
      result = result..'||'..row.req
local itemValue = math.max(nonShardItem.sellsFor, 20)
    end
local nonShardQty = math.max(1, math.floor(recipeGPCost / itemValue))
    if type(row.xp) == 'string' then
table.insert(recipeCosts, {id = nonShardItemID, quantity = nonShardQty})
      result = result..'||data-sort-value="0"|'..row.xp
end
    else
table.insert(useArray, {item = {id = recipeItem.id, name = recipeItem.name}, qty = recipe.baseQuantity, mats = recipeCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = 'Summoning', req = recipe.level, xp = recipe.baseExperience})
      result = result..'||data-sort-value="'..row.xp..'"|'..row.xp..' '..Icons.Icon({row.skill, type='skill', notext=true})..' XP'
end
    end
end
    result = result..'||'
end
    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]
      matItem = Items.getItemByID(matID)
      if i > 1 then result = result..'<br/>' end
      result = result..Icons.Icon({matItem.name, type='item', qty=matQty})
    end
    if row.gp ~= nil then result = result..'<br/>'..Icons.GP(row.gp) end
  end


  --Agility obstacles are weird and get their own section
--Handle shop purchases using Module:Shop
  local obstacles = Agility.getObstaclesForItem(item.id)
local shopUses = Shop.getItemCostArray(item.id)
  for i, obst in Shared.skpairs(obstacles) do
for i, shopUse in ipairs(shopUses) do
    result = result..'\r\n|-\r\n|'
local purchase = shopUse.purchase
    result = result..Icons.Icon({"Agility", type="skill", size="50", notext=true})
local rowReq = Shop.getRequirementString(purchase.purchaseRequirements)
    result = result..'||[[Agility#Obstacles|'..obst.name..']]'
local iconType = (purchase.contains.items ~= nil and not Shared.tableIsEmpty(purchase.contains.items) and 'item') or 'upgrade'
    result = result..'||'..Icons.Icon({"Agility", type="skill"})
local gpCost = Shop.getCurrencyCostString(purchase.cost, 'gp')
local scCost = Shop.getCurrencyCostString(purchase.cost, 'slayerCoins')
local rcCost = Shop.getCurrencyCostString(purchase.cost, 'raidCoins')
table.insert(useArray, {item = {name = Shop._getPurchaseName(purchase)}, qty = 1, mats = purchase.cost.items, skill = 'Shop', req = rowReq, xp = 'N/A', gp = gpCost, sc = scCost, rc = rcCost, type = iconType})
end


    --Adding the requirements for the Agility Obstacle
--Finally build the table using what we've learned
    local reqArray = {}
table.sort(useArray, function(a, b)
    if obst.category == nil then --nil category means this is a passive pillar
local aReqVal = a.reqVal ~= nil and a.reqVal or a.req
      table.insert(reqArray, Icons._SkillReq('Agility', 99))
local bReqVal = b.reqVal ~= nil and b.reqVal or b.req
    elseif obst.category > 0 then --Otherwise it's category * 10
if a.skill ~= b.skill then
      table.insert(reqArray, Icons._SkillReq('Agility', obst.category * 10))
return a.skill < b.skill
    end
elseif type(aReqVal) ~= type(bReqVal) then
    --Then the other skill levels if any are added
return tostring(aReqVal) < tostring(bReqVal)
    if obst.requirements ~= nil and obst.requirements.skillLevel ~= nil then
elseif aReqVal ~= bReqVal then
      for j, req in Shared.skpairs(obst.requirements.skillLevel) do
return aReqVal < bReqVal
        table.insert(reqArray, Icons._SkillReq(Constants.getSkillName(req[1]), req[2]))
else
      end
return a.item.name < b.item.name
    end
end
    result = result..'||'..table.concat(reqArray, '<br/>')
end)


    --Just including 'N/A' for XP since it doesn't really apply for Agility Obstacles
    result = result..'||N/A'


    --Finally the cost
local resultPart = {}
    local cost = obst.cost
if not Shared.tableIsEmpty(useArray) then
    local costArray = {}
local typeTextList = {
    if cost.gp ~= nil and cost.gp > 0 then
["Shop"] = Icons.Icon({'Shop'}),
      table.insert(costArray, Icons.GP(cost.gp))
["Upgrade"] = '[[Upgrading Items|Upgrade]]'
    end
}
    if cost.slayerCoins ~= nil and cost.slayerCoins > 0 then
      table.insert(costArray, Icons.SC(cost.slayerCoins))
    end
    for j, mat in Shared.skpairs(cost.items) do
      local item = Items.getItemByID(mat[1])
      table.insert(costArray, Icons.Icon({item.name, type="item", qty=mat[2]}))
    end
   
    result = result..'||'..table.concat(costArray, '<br/>')


  end
-- 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')


  result = result..'\r\n|}'
-- Rows
  if string.len(spellUseTable) > 0 then
for i, row in ipairs(useArray) do
    result = result..'\r\n==='..Icons.Icon({'Magic', type='skill', size='30'})..'===\r\n'..spellUseTable
local qty = row.qty ~= nil and row.qty or 1
  end
local iconType = row.type ~= nil and row.type or 'item'
  return '==Uses==\r\n'..result
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.quantity or itemCost[2] or 1
if matItem == nil then
table.insert(matRow, Shared.printError('Failed to find item with ID "' .. itemCost.id .. '"'))
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 then
local gpText = nil
if type(row.gp) == 'number' and row.gp > 0 then
gpText = Icons.GP(row.gp)
elseif type(row.gp) == 'string' then
gpText = row.gp
end
table.insert(matRow, gpText)
end
if row.sc ~= nil then
local scText = nil
if type(row.sc) == 'number' and row.sc > 0 then
scText = Icons.SC(row.sc)
elseif type(row.sc) == 'string' then
scText = row.sc
end
table.insert(matRow, scText)
end
if row.rc ~= nil then
local rcText = nil
if type(row.rc) == 'number' and row.rc > 0 then
rcText = Icons.RC(row.rc)
elseif type(row.rc) == 'string' then
rcText = row.rc
end
table.insert(matRow, rcText)
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.tableIsEmpty(resultPart) 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 Shared.printError('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.getSpellsUsingItem(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.tableIsEmpty(spellList) then
    return ''
return ''
  end
end
--Adding a check for if the Items column is needed
local hasItems = false
for i, spell in pairs(spellList) do
if Magic._getSpellItems(spell) ~= '' then
hasItems = true
break
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..'"|'
if hasItems then
    if spell.type == 'Auroras' then
table.insert(resultPart, '!!Item Cost')
      rowTxt = rowTxt..Icons.Icon({spell.name, type='aurora', notext=true, size=50})
end
    elseif spell.type == 'Curses' then
      rowTxt = rowTxt..Icons.Icon({spell.name, type='curse', notext=true, size=50})
    else
for i, spell in pairs(spellList) do
      rowTxt = rowTxt..Icons.Icon({spell.name, type='spell', notext=true, size=50})
local rowPart = {}
    end
table.insert(rowPart, '\r\n|-\r\n|data-sort-value="'..spell.name..'"|')
    rowTxt = rowTxt..'||[['..spell.name..']]'
local iconType = Magic._getSpellIconType(spell)
    rowTxt = rowTxt..'||data-sort-value="'..spell.magicLevelRequired..'"|'..Icons._SkillReq('Magic', spell.magicLevelRequired)
table.insert(rowPart, Icons.Icon({spell.name, type=iconType, notext=true, size=50}))
    --Handle required items/dungeon clears
table.insert(rowPart, '||'..Icons.Icon({spell.name, type=iconType, noicon=true}))
    if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
table.insert(rowPart, '||data-sort-value="'..spell.level..'"|'..Magic._getSpellRequirements(spell))
      local reqItem = Items.getItemByID(spell.requiredItem)
table.insert(rowPart, '||data-sort-value="'.. spell.spellBook ..'"|')
      rowTxt = rowTxt..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
table.insert(rowPart, Magic.getSpellTypeLink(spell.spellBook))
    end
table.insert(rowPart, '||'..Magic._getSpellStat(spell, 'description'))
    if spell.requiredDungeonCompletion ~= nil then
table.insert(rowPart, '||style="text-align:center"|')
      local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
table.insert(rowPart, Magic._getSpellRunes(spell))
      rowTxt = rowTxt..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
if hasItems then
    end
table.insert(rowPart, '||style="text-align:right"|')
    rowTxt = rowTxt..'||data-sort-value="'..Magic.getSpellTypeIndex(spell.type)..'"|'
table.insert(rowPart, Magic._getSpellItems(spell))
    rowTxt = rowTxt..Magic.getSpellTypeLink(spell.type)
end
    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 Shared.printError('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',
'Bird Nest'
}
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