Module:Items/UseTables: Difference between revisions

From Melvor Idle
(_getItemUses: Classify items with special attacks as used in combat)
(_getItemUses: Ensure 'All skills' is displayed before any skills)
 
(45 intermediate revisions by 7 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 Common = require('Module:Common')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Modifiers = require('Module:Modifiers')
local Skills = require('Module:Skills')
local Magic = require('Module:Magic')
local Magic = require('Module:Magic')
local Areas = require('Module:CombatAreas')
local Areas = require('Module:CombatAreas')
Line 13: Line 14:
local Agility = require('Module:Skills/Agility')
local Agility = require('Module:Skills/Agility')
local Shop = require('Module:Shop')
local Shop = require('Module:Shop')
 
local Num = require('Module:Number')


--Brute forcing some item uses to make things easier
--Brute forcing some item uses to make things easier
local itemUseArray = {
local itemUseArray = {
  Agility = {},
Agility = {},
  Astrology = {'Stardust', 'Golden Stardust'},
Astrology = {'melvorF:Stardust', 'melvorF:Golden_Stardust', 'melvorItA:Abyssal_Stardust'},
  Attack = {},
Archaeology = {},
  Combat = {'Gold Emerald Ring'},
Attack = {},
  Cooking = {'Cooking Gloves', 'Crown of Rhaelyx'},
Cartography = {},
  Crafting = {'Crown of Rhaelyx'},
Combat = {'melvorF:Gold_Emerald_Ring', 'melvorD:Obsidian_Cape', 'melvorF:Throwing_Power_Gloves'},
  Defence = {},
Cooking = {'melvorD:Cooking_Gloves'},
  Farming = {'Compost', 'Weird Gloop', 'Bob's Rake'},
Crafting = {'melvorItA:Abyssal_Crafting_Gloves'},
  Firemaking = {'Crown of Rhaelyx'},
Defence = {},
  Fishing = {'Amulet of Fishing', 'Message in a Bottle'},
Farming = {'melvorD:Compost', 'melvorD:Weird_Gloop', 'melvorItA:Abyssal_Compost', 'melvorD:Bobs_Rake'},
  Fletching = {'Crown of Rhaelyx'},
Firemaking = {'melvorItA:Abyssal_Firemaking_Gloves'},
  Herblore = {'Crown of Rhaelyx'},
Fishing = {'melvorD:Message_In_A_Bottle', 'melvorD:Barbarian_Gloves'},
  Hitpoints = {},
Fletching = {'melvorItA:Abyssal_Fletching_Gloves'},
  Magic = {},
Harvesting = {'melvorItA:Abyssal_Harvesting_Gloves'},
  Mining = {'Mining Gloves', 'Gem Gloves'},
Herblore = {'melvorItA:Abyssal_Herblore_Gloves'},
  Prayer = {},
Hitpoints = {},
  Ranged = {},
Magic = {},
  Runecrafting = {'Crown of Rhaelyx'},
Mining = {'melvorD:Mining_Gloves', 'melvorD:Gem_Gloves', 'melvorItA:Abyssal_Mining_Gloves'},
  Slayer = {},
Prayer = {},
  Smithing = {'Smithing Gloves', 'Crown of Rhaelyx'},
Ranged = {},
  Strength = {},
Runecrafting = {'melvorItA:Abyssal_Runecrafting_Gloves'},
  Summoning = {'Crown of Rhaelyx'},
Slayer = {},
  Thieving = {'Chapeau Noir', 'Thieving Gloves', 'Gloves of Silence'},
Smithing = {'melvorD:Smithing_Gloves', 'melvorItA:Abyssal_Smithing_Gloves'},
  Woodcutting = {},
Smithing = {},
  }
Strength = {},
local potionUseArray = {
Summoning = {},
  [0] = 'Combat',
Thieving = {'melvorF:Thieving_Gloves'},
  [1] = 'Combat',
Township = {},
  [2] = 'Combat',
Woodcutting = {},
  [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)
-- List of modifier IDs which, if present on an item and not specific to any
  local useArray = {}
-- particular skill, indicate that the item applies to all skills
  local categoryArray = {}
local allSkillModIDs = {
  local chr = asList and '* ' or ''
-- Skill XP
  --Another fun one. This time getting all the different possible ways an item can be used
'skillXP',
'nonCombatSkillXP',
'abyssalSkillXP',
-- Mastery
'flatMasteryTokens',
'xpFromMasteryTokens',
'masteryPoolCap',
'masteryXP',
'masteryPoolProgress',
-- Resource preservation
'bypassGlobalPreservationChance',
'skillPreservationChance',
'skillPreservationCap',
-- Other preservation (consumables, summons, potion charges)
'consumablePreservationChance',
'summoningChargePreservationChance',
'potionChargePreservationChance',
-- Item doubling
'globalItemDoublingChance',
-- Resource quantity
'flatBasePrimaryProductQuantity',
'basePrimaryProductQuantity',
'flatBaseRandomProductQuantity',
'flatAdditionalSkillItem',
'flatAdditionalPrimaryProductQuantity',
-- Cost reduction
'skillCostReduction',
-- Off item chance
'offItemChance'
}


  --Before anything else, if this is a potion add it to the appropriate override section
local function allSkillModCriteriaFromIDs(modIDs)
  if item.masteryID ~= nil and item.masteryID[1] == 19 then
local modIDsWithProps = {}
  if potionUseArray[item.masteryID[2]] ~= nil then
for i, modID in ipairs(allSkillModIDs) do
  table.insert(itemUseArray[potionUseArray[item.masteryID[2]]], item.name)
table.insert(modIDsWithProps, {
  else
["id"] = modID,
  table.insert(itemUseArray["Combat"], item.name)
["type"] = 'id',
["props"] = {
["skillID"] = 'nil',
["modType"] = 'pos'
}
})
end
end
  end
return Modifiers.getMatchCriteriaFromIDs(modIDsWithProps)
end


  --If the item has any modifiers that affect a given skill, add it to those lists
local allSkillModCriteria = allSkillModCriteriaFromIDs(allSkillModIDs)
  --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 Shared.skpairs(skillArray) do
      table.insert(itemUseArray[skillName], item.name)
    end
  end


  --First things added to the list are non-skill things that are easy to check
function p._getItemUses(item, asList, addCategories)
  if Items.hasCombatStats(item) or item.specialAttacks ~= nil or Shared.contains(itemUseArray.Combat, item.name) then
-- Another fun one. This time getting all the different possible ways an item can be used
    table.insert(useArray, chr..Icons.Icon({'Combat'}))
local categoryArray = {}
  end
local skillUses = {}
local otherUses = {}
local otherUseText = {
["Combat"] = Icons.Icon({'Combat'}),
["Upgrade"] = '[[Upgrading Items]]',
["Food"] = '[[Food]]',
["Chest"] = '[[Chest|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'})
}


  if item.healsFor ~= nil then
local addUse = function(useName)
    table.insert(categoryArray, '[[Category:Food Items]]')
local skillID = Constants.getSkillID(useName)
    table.insert(useArray, chr..'[[Food]]')
if skillID == nil then
  end
-- 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


  if item.dropTable ~= nil then
if skillID == nil and not otherUses[useName] then
    table.insert(categoryArray, '[[Category:Openable Items]]')
otherUses[useName] = true
    table.insert(useArray, chr..'[[Chest Drop Tables|Can Be Opened]]')
end
  end
end
local hasUse = function(useName)
local skillID = Constants.getSkillID(useName)
if skillID == nil and Constants.getSkillName(useName) ~= nil then
skillID = useName
end
if skillID ~= nil then
return (skillUses[skillID] ~= nil) or false
else
return otherUses[useName] or false
end
end


  -- Check if the item is an entry requirement for any Slayer area
-- Check for any overrides within itemUseArray
  local isSlayerAreaReq = false
for useName, itemList in pairs(itemUseArray) do
  if item.isEquipment then
if Shared.contains(itemList, item.id) then
    local slayerAreas = Areas.getAreas(function(area) return area.type == 'slayer' end)
addUse(useName)
    for i, area in pairs(slayerAreas) do
end
      if area.entryRequirements ~= nil and type(area.entryRequirements) == 'table' then
end
        for j, req in pairs(area.entryRequirements) do
          if req.type == "SlayerItem" and req.itemID == item.id then
            isSlayerAreaReq = true
            break
          end
        end
        if isSlayerAreaReq then break end
      end
    end
  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
  local canSummon = false
local modsAllSkills = Modifiers.getMatchingModifiers(item.modifiers, allSkillModCriteria)
  local canCook = false
if not Shared.tableIsEmpty(modsAllSkills.matched) then
addUse('AllSkills')
else
local skillArray = Modifiers.getModifierSkills(item.modifiers)
for i, skillName in ipairs(skillArray) do
addUse(skillName)
end
end
end
if item.modifiers.thievingStealth ~= nil then
addUse('melvorD:Thieving')
end
end


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


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


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


    if item2.summoningReq ~= nil then
if item.dropTable ~= nil then
      for j, reqSet in pairs(item2.summoningReq) do
table.insert(categoryArray, '[[Category:Openable Items]]')
        for k, req in pairs(reqSet) do
addUse('Chest')
          if req.id == item.id then
end
            canSummon = true
            break
          end
        end
      end
    end
   
    --Handling for new Cooking method
    if item2.recipeRequirements ~= nil and item2.recipeRequirements[1] ~= nil then
    for j, reqSet in pairs(item2.recipeRequirements) do
    for k, req in pairs(reqSet) do
    if req.id == item.id then
    canCook = true
    break
    end
    end
    end
    end
  end


  --Check if Agility applies here
-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
  canAgile = Shared.tableCount(Agility.getObstaclesForItem(item.id)) > 0
-- All have somewhat consistent recipe data structures
local recipeSkillIDs = {
'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


  --Agility
-- Firemaking
  if canAgile or Shared.contains(itemUseArray.Agility, item.name) then
if not hasUse('melvorD:Firemaking') then
    table.insert(useArray, chr..Icons.Icon({'Agility', type='skill'}))
for i, recipe in ipairs(SkillData.Firemaking.logs) do
  end
if recipe.logID == item.id then
  --Astrology
addUse('melvorD:Firemaking')
  if Shared.contains(itemUseArray.Astrology, item.name) then
break
    table.insert(useArray, chr..Icons.Icon({'Astrology', type='skill'}))
end
  end
end
  --Cooking
end
  if canCook 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 item.firemakingID ~= 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 isSlayerAreaReq 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:
-- 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


  --Mastery Tokens are tied to 'Mastery'
-- Agility
  if item.isToken and item.skill ~= nil then
if not hasUse('melvorD:Agility') and not Shared.tableIsEmpty(Agility.getObstaclesForItem(item.id)) then
    table.insert(useArray, chr..Icons.Icon({'Mastery'}))
addUse('melvorD:Agility')
  end
end


  --Skillcapes are tied to the appropriate skill
-- Summoning
  --Except Maximum Skillcape, which is tied to all skills. (And so is the Signet Ring)
if not hasUse('melvorD:Summoning') then
  --And combat skillcapes, since combat skills don't get special treatment
for i, recipe in ipairs(SkillData.Summoning.recipes) do
-- Tablets & Non-shard items
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 ignoreCapes = {'Ranged Skillcape', 'Attack Skillcape', 'Strength Skillcape', 'HP Skillcape', 'Defence Skillcape'}
-- Prayer
  if Shared.contains({'Maximum Skillcape', 'Aorpheat's Signet Ring', 'Cape of Completion'}, item.name) then
if item.prayerPoints ~= nil then
    table.insert(useArray, chr..'All skills')
table.insert(categoryArray, '[[Category:Buriable Items]]')
  elseif item.name == 'Magic Skillcape' then
if not hasUse('melvorD:Prayer') then
    table.insert(useArray, chr..Icons.Icon({'Magic', type='skill'}))
addUse('melvorD:Prayer')
    table.insert(useArray, chr..Icons.Icon({'Alt. Magic', type='skill'}))
end
  elseif Shared.contains(item.name, 'Skillcape') and not Shared.contains(ignoreCapes, item.name) then
elseif item.soulPoints ~= nil then
    local skillName = Shared.splitString(item.name, ' ')[1]
table.insert(categoryArray, '[[Category:Releasable Items]]')
    --Avoiding double-listing the same skill twice
if not hasUse('melvorD:Prayer') then
    if not Shared.contains(itemUseArray[skillName], item.name) then
addUse('melvorD:Prayer')
      table.insert(useArray, chr..Icons.Icon({skillName, type='skill'}))
end
    end
end
  end


  if Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion' then table.insert(categoryArray, '[[Category:Skillcapes]]') 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


  --Special note for Charge Stone of Rhaelyx
-- Other odds and ends:
  if item.name == 'Charge Stone of Rhaelyx' then
 
    table.insert(useArray, chr..'Powering '..Icons.Icon({'Crown of Rhaelyx', type='item'}))
-- Skillcapes are tied to the appropriate skill
  end
-- 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
--Some items are needed to make shop purchases
  local shopArray = Shop.getItemCostArray(item.id)
local shopArray = Shop.getItemCostArray(item.id)
  if Shared.tableCount(shopArray) > 0 then
if not Shared.tableIsEmpty(shopArray) then
    table.insert(useArray, chr..Icons.Icon({'Shop'}))
addUse('Shop')
  end
end


  if canUpgrade then
-- Township Tasks
    if item.canUpgrade or (item.type == 'Armour' and item.canUpgrade == nil) then
for _, task in ipairs(SkillData.Township.tasks) do
      table.insert(categoryArray, '[[Category:Upgradeable Items]]')
if task.goals.items ~= nil then -- Skip tasks with no items
    end
if GameData.getEntityByID(task.goals.items, item.id) then
    table.insert(useArray, chr..'[[Upgrading Items]]')
addUse('TownshipTask')
  end
break
end
end
end
-- Generate result text
local useArray = {}
local prefix, delim = asList and '* ' or '', asList and '\r\n' or '<br/>'
-- Always place 'All skills' use before any skills
if hasUse('AllSkills') then
table.insert(useArray, prefix .. (otherUseText.AllSkills or 'AllSkills'))
otherUses.AllSkills = nil
end
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


  local resultPart = {}
return table.concat(useArray, delim) .. (addCategories and table.concat(categoryArray, '') or '')
  table.insert(resultPart, asList and table.concat(useArray,'\r\n') or table.concat(useArray, '<br/>'))
  if addCategories then table.insert(resultPart, table.concat(categoryArray, '')) end
  return table.concat(resultPart)
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}


  --First, loop through all items to find anything that can be made or upgraded into using our source
-- Loop through all upgrades to find anything that can be upgraded using our source
  for i, item2 in pairs(ItemData.Items) do
for i, upgrade in ipairs(GameData.rawData.itemUpgrades) do
    if item2.itemsRequired ~= nil then
for j, itemCost in ipairs(upgrade.itemCosts) do
      for j, req in pairs(item2.itemsRequired) do
if itemCost.id == item.id then
        if req[1] == item.id then
local rowReq = nil
          local mat = item2.itemsRequired
-- Potions do have upgrade requirements though
          local xp = 'N/A'
local upgradeItem = Items.getItemByID(upgrade.upgradedItemID)
          local rowReq = 'None'
if upgradeItem ~= nil and upgradeItem.charges ~= nil and upgradeItem.tier ~= nil then
          --Potions do have upgrade requirements though
local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', upgradeItem.tier + 1)
          if item2.potionTier ~= nil then
if levelUnlock ~= nil then
            rowReq = Icons._MasteryReq(item2.name, potTierMastery[item2.potionTier])
rowReq = Icons._MasteryReq(upgradeItem.name, levelUnlock.level)
          end
end
          table.insert(useArray, {item = item2, qty = 1, mats = mat, skill = 'Upgrade', req = rowReq, xp = xp, gp = item2.trimmedGPCost})
end
          break
table.insert(useArray, {item = {id = upgradeItem.id, name = upgradeItem.name}, qty = (upgrade.quantity or 1), mats = upgrade.itemCosts, skill = 'Upgrade', req = rowReq, xp = 'N/A', gp = upgrade.gpCost, sc = upgrade.scCost})
        end
end
      end
end
    end
end
    if item2.craftReq ~= nil then
      for j, req in pairs(item2.craftReq) do
        if req.id == item.id then
          local mat = item2.craftReq
          local xp = item2.craftingXP
          local rowReq = item2.craftingLevel
          local qty = item2.craftQty
          table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Crafting', req = rowReq, xp = xp, gp = item2.craftGPCost})
          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
    --Handling for new Cooking method
    if item2.recipeRequirements ~= nil then
    for j, reqSet in pairs(item2.recipeRequirements) do
    for k, req in pairs(reqSet) do
    if req.id == item.id then
    local mat = reqSet
    local xp = item2.cookingXP
    local rowReq = item2.cookingLevel
    local qty = item2.cookingQty
    table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Cooking', req = rowReq, xp = xp})
   
    if item2.perfectItem ~= nil then
    local perfectItem = Items.getItemByID(item2.perfectItem)
    table.insert(useArray, {item = perfectItem, qty = qty, mats = mat, skill = 'Cooking', req = rowReq, xp = xp})
    end
    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.masteryID[2] + 1]
          local mat = item2.herbloreReq
          local xp = potionData.herbloreXP
          --Potions do have upgrade requirements though
          local rowReq = Icons._SkillReq('Herblore', potionData.level)
          local masteryLvl = potTierMastery[item2.potionTier]
          if masteryLvl > 0 then
            rowReq = rowReq..'<br/>'..Icons._MasteryReq(item2.name, masteryLvl)
          end
          local reqVal = potionData.level + (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
'melvorD:Cooking',
            local mat = Shared.clone(reqSet)
'melvorD:Smithing',
            mat[k].qty = math.max(math.floor(1000 / math.max(item.sellsFor, 20)), 1)
'melvorD:Fletching',
            local xp = 5 + 2 * math.floor(item2.summoningLevel * 0.2)
'melvorD:Crafting',
            local rowReq = item2.summoningLevel
'melvorD:Runecrafting',
            table.insert(useArray, {item = item2, qty = 25, mats = mat, skill = 'Summoning', req = rowReq, xp = xp})
'melvorD:Herblore'
          end
}
        end
for i, recipeSkillID in ipairs(recipeSkillIDs) do
      end
local skillName = Constants.getSkillName(recipeSkillID)
    end
local _, localSkillID = GameData.getLocalID(recipeSkillID)
  end
  if item.grownItemID ~= nil then
-- Iterate over all recipes for the current skill
    local item2 = Items.getItemByID(item.grownItemID)
for j, recipe in ipairs(SkillData[localSkillID].recipes) do
    local mat = {{id = item.id, qty = item.seedsRequired}}
local costLists = {recipe.alternativeCosts or {}, {{["itemCosts"] = recipe.itemCosts}}}
    local xp = item.farmingXP
for k, costList in pairs(costLists) do
    local rowReq = item.farmingLevel
for m, costDef in pairs(costList) do
    local qty = (item.tier ~= nil and item.tier == 'Tree' and 35 or 15)
for n, itemCost in ipairs(costDef.itemCosts) do
    table.insert(useArray, {item = item2, qty = qty, mats = mat, skill = 'Farming', req = rowReq, xp = xp})
if itemCost.id == item.id then
  end
local recipeItemIDs = nil
if recipeSkillID == 'melvorD:Herblore' then
recipeItemIDs = recipe.potionIDs
elseif recipeSkillID == 'melvorD:Cooking' then
recipeItemIDs = {recipe.productID, recipe.perfectCookID}
else
recipeItemIDs = {recipe.productID}
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, isAbyssal = Skills.getRecipeLevelRealm(recipeSkillID, recipe)
local reqVal = nil
local xp = recipe.baseAbyssalExperience or recipe.baseExperience
if recipeSkillID == 'melvorD:Herblore' then
-- Herblore may also have a mastery requirement
local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', recipeItem.tier + 1)
if levelUnlock ~= nil and levelUnlock.level > 1 then
local masteryReq = Icons._MasteryReq(recipeItem.name, levelUnlock.level)
reqVal = rowReq + levelUnlock.level * 0.01
rowReq = Icons._SkillReq(skillName, rowReq, false, (isAbyssal and "melvorItA:Abyssal" or nil)) .. '<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, isAbyssal = isAbyssal, xp = xp})
end
end
break
end
end
end
end
end
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, isAbyssal = Skills.getRecipeLevelRealm('melvorD:Farming', recipe)
  end
local category = GameData.getEntityByID(SkillData.Farming.categories, recipe.categoryID)
local qty = 5 * category.harvestMultiplier
local xp = recipe.baseAbyssalExperience or recipe.baseExperience
table.insert(useArray, {item = {id = product.id, name = product.name}, qty = qty, mats = mat, skill = 'Farming', req = rowReq, isAbyssal = isAbyssal, 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 obstacles = Agility.getObstaclesForItem(item.id)
-- Summoning
for i, recipe in ipairs(SkillData.Summoning.recipes) do
local recipeCost = 0
if recipe.abyssalLevel ~= nil then
recipeCost = SkillData.Summoning.recipeAPCost
else
recipeCost = SkillData.Summoning.recipeGPCost
end
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
-- Familiar recipes may also have a currency cost without any non-shard
-- items, so account for this with a dummy ID such that one iteration
-- of the below loop always occurs
local nonShardItemIDs = (Shared.tableIsEmpty(recipe.nonShardItemCosts) and {''} or recipe.nonShardItemCosts)
for j, nonShardItemID in ipairs(nonShardItemIDs) do
if useShards or nonShardItemID == item.id then
-- Item is used in this particular synergy recipe
if recipeItem == nil then
recipeItem = Items.getItemByID(recipe.productID)
end
local nonShardItem = Items.getItemByID(nonShardItemID)
local recipeCosts = Shared.clone(recipe.itemCosts)
local xp = recipe.baseAbyssalExperience or recipe.baseExperience
local req, isAbyssal = Skills.getRecipeLevelRealm('melvorD:Summoning', recipe)
local recipeCosts = {}
for k, itemCost in ipairs(recipe.itemCosts) do
table.insert(recipeCosts, {id = itemCost.id, quantity = itemCost.quantity})
end
if nonShardItem ~= nil then
-- Item ID can be nil for recipes such as Leprechaun or Cyclops
local itemValue = math.max(nonShardItem.sellsFor, 20)
local nonShardQty = math.max(1, math.ceil(recipeCost / itemValue))
table.insert(recipeCosts, {id = nonShardItemID, quantity = nonShardQty})
end
table.insert(useArray, {item = {id = recipeItem.id, name = recipeItem.name}, qty = recipe.baseQuantity, mats = recipeCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = 'Summoning', req = req, isAbyssal = isAbyssal, xp = xp})
end
end
end


  local spellUseTable = p._getSpellUseTable(item)
--Handle shop purchases using Module:Shop
  local resultPart = {}
local shopUses = Shop.getItemCostArray(item.id)
  if Shared.tableCount(useArray) == 0 and Shared.tableCount(obstacles) == 0 then
for i, shopUse in ipairs(shopUses) do
    if string.len(spellUseTable) > 0 then
local purchase = shopUse.purchase
      return '==Uses==\r\n==='..Icons.Icon({'Magic', type='skill', size='30'})..'===\r\n'..spellUseTable
local rowReq = Shop.getRequirementString(purchase.purchaseRequirements)
    else
local iconType = Common.getPurchaseIconType(purchase)
      return ''
local gpCost = Shop.getCurrencyCostString(purchase.cost, 'gp')
    end
local scCost = Shop.getCurrencyCostString(purchase.cost, 'slayerCoins')
  end
local rcCost = Shop.getCurrencyCostString(purchase.cost, 'raidCoins')
  table.insert(resultPart, '{| class="wikitable sortable"')
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})
  table.insert(resultPart, '\r\n!colspan=2|Item Created!!Type!!Requirements!!XP!!Ingredients')
end
  for i, row in pairs(useArray) do
    local qty = row.qty ~= nil and row.qty or 1
    local iconType = row.type ~= nil and row.type or 'item'
    table.insert(resultPart, '\r\n|-\r\n|data-sort-value="'..row.item.name..'"|')
    table.insert(resultPart, Icons.Icon({row.item.name, type=iconType, notext=true, size=50})..'||')
    if qty > 1 then table.insert(resultPart, "'''"..qty.."x''' ") end
    table.insert(resultPart, Icons.Icon({row.item.name, type='item', noicon=true}))
    if row.skill == 'Upgrade' then
      table.insert(resultPart, '||data-sort-value="Upgrade"|[[Upgrading Items|Upgrade]]')
    elseif row.skill == 'Shop' then
      table.insert(resultPart, '||data-sort-value="Shop"|'..Icons.Icon({'Shop'}))
    else
      table.insert(resultPart, '||data-sort-value="'..row.skill..'"|'..Icons.Icon({row.skill, type='skill'}))
    end
    if type(row.req) == 'number' then
      table.insert(resultPart, '|| style="text-align:right" data-sort-value="'..row.req..'"|'..Icons._SkillReq(row.skill, row.req))
    elseif row.reqVal ~= nil then
      table.insert(resultPart, '|| style="text-align:right" data-sort-value="'..row.reqVal..'"|'..row.req)
    else
      table.insert(resultPart, '||'..row.req)
    end
    if type(row.xp) == 'string' then
      table.insert(resultPart, '||style="text-align:right" data-sort-value="0"|'..row.xp)
    else
      table.insert(resultPart, '||style="text-align:right" data-sort-value="'..row.xp..'"|'..row.xp..' '..Icons.Icon({row.skill, type='skill', notext=true})..' XP')
    end
    table.insert(resultPart, '||')
    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 table.insert(resultPart, '<br/>') end
--Finally build the table using what we've learned
      if matID >= 0 then
table.sort(useArray, function(a, b)
        -- Item
local aReqVal = a.reqVal ~= nil and a.reqVal or a.req
        local matItem = Items.getItemByID(matID)
local bReqVal = b.reqVal ~= nil and b.reqVal or b.req
        if matItem == nil then
if a.skill ~= b.skill then
          matText = 'ERROR: Failed to find item with ID ' .. matID .. '[[Category:Pages with Script Errors]]'
return a.skill < b.skill
        else
elseif type(aReqVal) ~= type(bReqVal) then
          matText = Icons.Icon({matItem.name, type='item', qty=matQty})
return tostring(aReqVal) < tostring(bReqVal)
        end
elseif aReqVal ~= bReqVal then
      elseif matID == -4 then
return aReqVal < bReqVal
        -- GP
else
        matText = Icons.GP(SkillData.Summoning.Settings.recipeGPCost)
return a.item.name < b.item.name
      elseif matID == -5 then
end
        -- Slayer Coins
end)
        matText = Icons.SC(SkillData.Summoning.Settings.recipeGPCost)
      else
        matText = 'ERROR: Unknown item ID: ' .. matID .. ' [[Category:Pages with Script Errors]]'
      end
      table.insert(resultPart, matText)
    end
    if row.gp ~= nil then table.insert(resultPart, '<br/>'..Icons.GP(row.gp)) end
  end


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


    --Adding the requirements for the Agility Obstacle
local resultPart = {}
    local reqArray = {}
if not Shared.tableIsEmpty(useArray) 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
    table.insert(resultPart, '||style="text-align:right"|'..table.concat(reqArray, '<br/>'))


    --Just including 'N/A' for XP since it doesn't really apply for Agility Obstacles
-- Header
    table.insert(resultPart, '||style="text-align:right"|N/A')
table.insert(resultPart, '{| class="wikitable stickyHeader sortable col-1-img"')
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
iconType = '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, false, (row.isAbyssal and "melvorItA:Abyssal" or nil))
    table.insert(resultPart, '||'..table.concat(costArray, '<br/>'))
elseif type(row.req) == 'string' then
 
reqText = row.req
  end
end
 
local xpVal, xpText = 0, 'N/A'
  table.insert(resultPart, '\r\n|}')
if type(row.xp) == 'string' then
  if string.len(spellUseTable) > 0 then
xpText = row.xp
    table.insert(resultPart, '\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'..table.concat(resultPart)
xpText = Num.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._Currency('melvorD: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._Currency('melvorD:SlayerCoins', 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._Currency('melvorD:RaidCoins', 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, "'''" .. Num.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 resultPart = {}
local resultPart = {}
  table.insert(resultPart, '{|class="wikitable sortable"\r\n!colspan="2"|Spell')
table.insert(resultPart, '{|class="wikitable sortable"\r\n!colspan="2"|Spell')
  table.insert(resultPart, '!!Requirements')
table.insert(resultPart, '!!Requirements')
  table.insert(resultPart, '!!Type!!style="width:275px"|Description')
table.insert(resultPart, '!!Type!!style="width:275px"|Description')
  table.insert(resultPart, '!!Runes')
table.insert(resultPart, '!!Runes')
  for i, spell in pairs(spellList) do
if hasItems then
    local rowPart = {}
table.insert(resultPart, '!!Item Cost')
    table.insert(rowPart, '\r\n|-\r\n|data-sort-value="'..spell.name..'"|')
end
    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}))
for i, spell in pairs(spellList) do
    table.insert(rowPart, '||data-sort-value="'..spell.level..'"|'..Icons._SkillReq('Magic', spell.level))
local spellBook = Magic.getSpellBookFromSpell(spell)
    --Handle required items/dungeon clears
local rowPart = {}
    if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
table.insert(rowPart, '\r\n|-\r\n|data-sort-value="'..spell.name..'"|')
      local reqItem = Items.getItemByID(spell.requiredItem)
local iconType = Magic._getSpellIconType(spell)
      table.insert(rowPart, '<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped')
table.insert(rowPart, Icons.Icon({spell.name, type=iconType, notext=true, size=50}))
    end
table.insert(rowPart, '||'..Icons.Icon({spell.name, type=iconType, noicon=true}))
    if spell.requiredDungeonCompletion ~= nil then
table.insert(rowPart, '||data-sort-value="'..Skills.getRecipeLevel('melvorD:Summoning', spell)..'"|'..Magic._getSpellRequirements(spell))
      local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
table.insert(rowPart, '||data-sort-value="'.. spellBook.id ..'"|')
      table.insert(rowPart, '<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears')
table.insert(rowPart, Magic.getSpellTypeLink(spellBook.id))
    end
table.insert(rowPart, '||'..Magic._getSpellStat(spell, 'description'))
    table.insert(rowPart, '||data-sort-value="'..Magic.getSpellTypeIndex(spell.type)..'"|')
table.insert(rowPart, '||style="text-align:center"|')
    table.insert(rowPart, Magic.getSpellTypeLink(spell.type))
table.insert(rowPart, Magic._getSpellRunes(spell))
    table.insert(rowPart, '||'..Magic._getSpellStat(spell, 'description'))
if hasItems then
    table.insert(rowPart, '||style="text-align:center"|')
table.insert(rowPart, '||style="text-align:right"|')
    table.insert(rowPart, Magic._getSpellRunes(spell))
table.insert(rowPart, Magic._getSpellItems(spell))
    table.insert(resultPart, table.concat(rowPart))
end
  end
table.insert(resultPart, table.concat(rowPart))
  --Add the table end and add the table to the result string
end
  table.insert(resultPart, '\r\n|}')
--Add the table end and add the table to the result string
  return table.concat(resultPart)
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
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',
'Abyssal Stone',
'Withered Ash',
'Wrath Rune'
}
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

Latest revision as of 19:20, 6 September 2024

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

local p = {}

local Constants = require('Module:Constants')
local Common = require('Module:Common')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Modifiers = require('Module:Modifiers')
local Skills = require('Module:Skills')
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 Num = require('Module:Number')

--Brute forcing some item uses to make things easier
local itemUseArray = {
	Agility = {},
	Astrology = {'melvorF:Stardust', 'melvorF:Golden_Stardust', 'melvorItA:Abyssal_Stardust'},
	Archaeology = {},
	Attack = {},
	Cartography = {},
	Combat = {'melvorF:Gold_Emerald_Ring', 'melvorD:Obsidian_Cape', 'melvorF:Throwing_Power_Gloves'},
	Cooking = {'melvorD:Cooking_Gloves'},
	Crafting = {'melvorItA:Abyssal_Crafting_Gloves'},
	Defence = {},
	Farming = {'melvorD:Compost', 'melvorD:Weird_Gloop', 'melvorItA:Abyssal_Compost', 'melvorD:Bobs_Rake'},
	Firemaking = {'melvorItA:Abyssal_Firemaking_Gloves'},
	Fishing = {'melvorD:Message_In_A_Bottle', 'melvorD:Barbarian_Gloves'},
	Fletching = {'melvorItA:Abyssal_Fletching_Gloves'},
	Harvesting = {'melvorItA:Abyssal_Harvesting_Gloves'},
	Herblore = {'melvorItA:Abyssal_Herblore_Gloves'},
	Hitpoints = {},
	Magic = {},
	Mining = {'melvorD:Mining_Gloves', 'melvorD:Gem_Gloves', 'melvorItA:Abyssal_Mining_Gloves'},
	Prayer = {},
	Ranged = {},
	Runecrafting = {'melvorItA:Abyssal_Runecrafting_Gloves'},
	Slayer = {},
	Smithing = {'melvorD:Smithing_Gloves', 'melvorItA:Abyssal_Smithing_Gloves'},
	Smithing = {},
	Strength = {},
	Summoning = {},
	Thieving = {'melvorF:Thieving_Gloves'},
	Township = {},
	Woodcutting = {},
}

-- List of modifier IDs which, if present on an item and not specific to any
-- particular skill, indicate that the item applies to all skills
local allSkillModIDs = {
	-- Skill XP
	'skillXP',
	'nonCombatSkillXP',
	'abyssalSkillXP',
	-- Mastery
	'flatMasteryTokens',
	'xpFromMasteryTokens',
	'masteryPoolCap',
	'masteryXP',
	'masteryPoolProgress',
	-- Resource preservation
	'bypassGlobalPreservationChance',
	'skillPreservationChance',
	'skillPreservationCap',
	-- Other preservation (consumables, summons, potion charges)
	'consumablePreservationChance',
	'summoningChargePreservationChance',
	'potionChargePreservationChance',
	-- Item doubling
	'globalItemDoublingChance',
	-- Resource quantity
	'flatBasePrimaryProductQuantity',
	'basePrimaryProductQuantity',
	'flatBaseRandomProductQuantity',
	'flatAdditionalSkillItem',
	'flatAdditionalPrimaryProductQuantity',
	-- Cost reduction
	'skillCostReduction',
	-- Off item chance
	'offItemChance'
}

local function allSkillModCriteriaFromIDs(modIDs)
	local modIDsWithProps = {}
	for i, modID in ipairs(allSkillModIDs) do
		table.insert(modIDsWithProps, {
			["id"] = modID,
			["type"] = 'id',
			["props"] = {
				["skillID"] = 'nil',
				["modType"] = 'pos'
			}
		})
	end
	return Modifiers.getMatchCriteriaFromIDs(modIDsWithProps)
end

local allSkillModCriteria = allSkillModCriteriaFromIDs(allSkillModIDs)

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|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

		if skillID == nil and not otherUses[useName] then
			otherUses[useName] = true
		end
	end
	local hasUse = function(useName)
		local skillID = Constants.getSkillID(useName)
		if skillID == nil and Constants.getSkillName(useName) ~= nil then
			skillID = useName
		end
		if skillID ~= nil 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.id) then
			addUse(useName)
		end
	end

	-- If the item has any modifiers that affect a given skill, add it to those tables
	-- Added special handling for Mastery Tokens since they were being incorrectly flagged as usable for all skills
	if item.modifiers ~= nil then
		if item.modifiers.masteryToken ~= nil then
			-- Mastery tokens
			addUse('Mastery')
		else
			local modsAllSkills = Modifiers.getMatchingModifiers(item.modifiers, allSkillModCriteria)
			if not Shared.tableIsEmpty(modsAllSkills.matched) then
				addUse('AllSkills')
			else
				local skillArray = Modifiers.getModifierSkills(item.modifiers)
				for i, skillName in ipairs(skillArray) do
					addUse(skillName)
				end
			end
		end
		if item.modifiers.thievingStealth ~= nil then
			addUse('melvorD:Thieving')
		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('Slayer') and (item.validSlots ~= nil or item.occupiesSlots ~= nil or item.equipmentStats ~= nil) then
		local slayerAreas = Areas.getAreas(function(area) return area.type == 'slayerArea' and type(area.entryRequirements) == 'table' end)
		for i, area in ipairs(slayerAreas) do
			for j, req in ipairs(area.entryRequirements) do
				if req.type == "SlayerItem" and req.itemID == item.id then
					addUse('Slayer')
					break
				end
			end
			if hasUse('Slayer') then
				break
			end
		end
	end

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

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

	if item.dropTable ~= nil 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 = {
		'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

	-- Firemaking
	if not hasUse('melvorD:Firemaking') then
		for i, recipe in ipairs(SkillData.Firemaking.logs) do
			if recipe.logID == item.id then
				addUse('melvorD:Firemaking')
				break
			end
		end
	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

	-- Agility
	if not hasUse('melvorD:Agility') and not Shared.tableIsEmpty(Agility.getObstaclesForItem(item.id)) then
		addUse('melvorD:Agility')
	end

	-- Summoning
	if not hasUse('melvorD:Summoning') then
		for i, recipe in ipairs(SkillData.Summoning.recipes) do
			-- Tablets & Non-shard items
			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

	-- Prayer
	if item.prayerPoints ~= nil then
		table.insert(categoryArray, '[[Category:Buriable Items]]')
		if not hasUse('melvorD:Prayer') then
			addUse('melvorD:Prayer')
		end
	elseif item.soulPoints ~= nil then
		table.insert(categoryArray, '[[Category:Releasable 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 ~= 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/>'
	-- Always place 'All skills' use before any skills
	if hasUse('AllSkills') then
		table.insert(useArray, prefix .. (otherUseText.AllSkills or 'AllSkills'))
		otherUses.AllSkills = nil
	end
	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 Shared.printError('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 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 = (upgrade.quantity or 1), mats = upgrade.itemCosts, skill = 'Upgrade', req = rowReq, xp = 'N/A', gp = upgrade.gpCost, sc = upgrade.scCost})
			end
		end
	end

	-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore
	-- All have somewhat consistent recipe data structures
	local recipeSkillIDs = {
		'melvorD:Cooking',
		'melvorD:Smithing',
		'melvorD:Fletching',
		'melvorD:Crafting',
		'melvorD:Runecrafting',
		'melvorD:Herblore'
	}
	for i, recipeSkillID in ipairs(recipeSkillIDs) do
		local skillName = Constants.getSkillName(recipeSkillID)
		local _, localSkillID = GameData.getLocalID(recipeSkillID)
			
		-- Iterate over all recipes for the current skill
		for j, recipe in ipairs(SkillData[localSkillID].recipes) 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 == 'melvorD:Herblore' then
								recipeItemIDs = recipe.potionIDs
							elseif recipeSkillID == 'melvorD:Cooking' then
								recipeItemIDs = {recipe.productID, recipe.perfectCookID}
							else
								recipeItemIDs = {recipe.productID}
							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, isAbyssal = Skills.getRecipeLevelRealm(recipeSkillID, recipe)
									local reqVal = nil
									local xp = recipe.baseAbyssalExperience or recipe.baseExperience
									if recipeSkillID == 'melvorD:Herblore' then
										-- Herblore may also have a mastery requirement
										local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', recipeItem.tier + 1)
										if levelUnlock ~= nil and levelUnlock.level > 1 then
											local masteryReq = Icons._MasteryReq(recipeItem.name, levelUnlock.level)
											reqVal = rowReq + levelUnlock.level * 0.01
											rowReq = Icons._SkillReq(skillName, rowReq, false, (isAbyssal and "melvorItA:Abyssal" or nil)) .. '<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, isAbyssal = isAbyssal, xp = xp})
								end
							end
							break
						end
					end
				end
			end
		end
	end

	-- Farming
	for i, recipe in ipairs(SkillData.Farming.recipes) do
		if recipe.seedCost.id == item.id then
			local product = Items.getItemByID(recipe.productId)
			local mat = {{id = recipe.seedCost.id, quantity = recipe.seedCost.quantity}}
			local rowReq, isAbyssal = Skills.getRecipeLevelRealm('melvorD:Farming', recipe)
			local category = GameData.getEntityByID(SkillData.Farming.categories, recipe.categoryID)
			local qty = 5 * category.harvestMultiplier
			local xp = recipe.baseAbyssalExperience or recipe.baseExperience
			table.insert(useArray, {item = {id = product.id, name = product.name}, qty = qty, mats = mat, skill = 'Farming', req = rowReq, isAbyssal = isAbyssal, xp = xp})
		end
	end

	-- Agility
	local obstacles = Agility.getObstaclesForItem(item.id)
	for i, obstacle in ipairs(obstacles) do
		local itemCosts = {}
		for j, itemDef in ipairs(obstacle.itemCosts) do
			table.insert(itemCosts, {id = itemDef.id, quantity = itemDef.quantity})
		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.gpCost, sc = obstacle.scCost, skill = 'Agility', req = req, type = 'skill'})
	end

	-- Summoning
	for i, recipe in ipairs(SkillData.Summoning.recipes) do
		local recipeCost = 0
		if recipe.abyssalLevel ~= nil then
			recipeCost = SkillData.Summoning.recipeAPCost
		else
			recipeCost = SkillData.Summoning.recipeGPCost
		end
		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
		-- Familiar recipes may also have a currency cost without any non-shard
		-- items, so account for this with a dummy ID such that one iteration
		-- of the below loop always occurs
		local nonShardItemIDs = (Shared.tableIsEmpty(recipe.nonShardItemCosts) and {''} or recipe.nonShardItemCosts)
		for j, nonShardItemID in ipairs(nonShardItemIDs) do
			if useShards or nonShardItemID == item.id then
				-- Item is used in this particular synergy recipe
				if recipeItem == nil then
					recipeItem = Items.getItemByID(recipe.productID)
				end
				local nonShardItem = Items.getItemByID(nonShardItemID)
				local recipeCosts = Shared.clone(recipe.itemCosts)
				local xp = recipe.baseAbyssalExperience or recipe.baseExperience
				local req, isAbyssal = Skills.getRecipeLevelRealm('melvorD:Summoning', recipe)
				local recipeCosts = {}
				for k, itemCost in ipairs(recipe.itemCosts) do
					table.insert(recipeCosts, {id = itemCost.id, quantity = itemCost.quantity})
				end
				if nonShardItem ~= nil then
					-- Item ID can be nil for recipes such as Leprechaun or Cyclops
					local itemValue = math.max(nonShardItem.sellsFor, 20)
					local nonShardQty = math.max(1, math.ceil(recipeCost / itemValue))
					table.insert(recipeCosts, {id = nonShardItemID, quantity = nonShardQty})
				end
				table.insert(useArray, {item = {id = recipeItem.id, name = recipeItem.name}, qty = recipe.baseQuantity, mats = recipeCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = 'Summoning', req = req, isAbyssal = isAbyssal, xp = xp})
			end
		end
	end

	--Handle shop purchases using Module:Shop
	local shopUses = Shop.getItemCostArray(item.id)
	for i, shopUse in ipairs(shopUses) do
		local purchase = shopUse.purchase
		local rowReq = Shop.getRequirementString(purchase.purchaseRequirements)
		local iconType = Common.getPurchaseIconType(purchase)
		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

	--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 not Shared.tableIsEmpty(useArray) then
		local typeTextList = {
			["Shop"] = Icons.Icon({'Shop'}),
			["Upgrade"] = '[[Upgrading Items|Upgrade]]'
		}

		-- Header
		table.insert(resultPart, '{| class="wikitable stickyHeader sortable col-1-img"')
		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
				iconType = '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, false, (row.isAbyssal and "melvorItA:Abyssal" or nil))
			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 = Num.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._Currency('melvorD: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._Currency('melvorD:SlayerCoins', 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._Currency('melvorD:RaidCoins', 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, "'''" .. Num.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

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 Shared.printError('No item named "' .. itemName .. '" exists in the data module')
	end

	return p._getItemUseTable(item)
end

function p._getSpellUseTable(item)
	local spellList = Magic.getSpellsUsingItem(item.id, true)
	--Bail immediately if no spells are found
	if Shared.tableIsEmpty(spellList) then
		return ''
	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 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')
	if hasItems then
		table.insert(resultPart, '!!Item Cost')
	end
	
	
	for i, spell in pairs(spellList) do
		local spellBook = Magic.getSpellBookFromSpell(spell)
		local rowPart = {}
		table.insert(rowPart, '\r\n|-\r\n|data-sort-value="'..spell.name..'"|')
		local iconType = Magic._getSpellIconType(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="'..Skills.getRecipeLevel('melvorD:Summoning', spell)..'"|'..Magic._getSpellRequirements(spell))
		table.insert(rowPart, '||data-sort-value="'.. spellBook.id ..'"|')
		table.insert(rowPart, Magic.getSpellTypeLink(spellBook.id))
		table.insert(rowPart, '||'..Magic._getSpellStat(spell, 'description'))
		table.insert(rowPart, '||style="text-align:center"|')
		table.insert(rowPart, Magic._getSpellRunes(spell))
		if hasItems then
			table.insert(rowPart, '||style="text-align:right"|')
			table.insert(rowPart, Magic._getSpellItems(spell))
		end
		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 Shared.printError('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',
		'Bird Nest',
		'Abyssal Stone',
		'Withered Ash',
		'Wrath Rune'
	}
	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