Anonymous

Module:Items/SourceTables: Difference between revisions

From Melvor Idle
Alt Magic -> Alt. Magic for consistency
m (Fix logic error)
(Alt Magic -> Alt. Magic for consistency)
(19 intermediate revisions by 3 users not shown)
Line 4: Line 4:
local ItemData = mw.loadData('Module:Items/data')
local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')
local SkillData = mw.loadData('Module:Skills/data')
local MagicData = mw.loadData('Module:Magic/data')


local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
Line 13: Line 14:
local Shop = require('Module:Shop')
local Shop = require('Module:Shop')
local Monsters = require('Module:Monsters')
local Monsters = require('Module:Monsters')
local GatheringSkills = require('Module:Skills/Gathering')
local Skills = require('Module:Skills')


local SkillEnum = mw.loadData('Module:Constants/data').skill
-- Implements overrides for sources which cannot be obtained from game data
-- Implements overrides for sources which cannot be obtained from game data
-- Currently only overrides for dungeon sources are implemented here
-- Currently only overrides for dungeon sources are implemented here
local sourceOverrides = {
local sourceOverrides = {
  ['Dungeon'] = {
['Dungeon'] = {
  [361] = 'Volcanic Cave', -- Fire Cape
[950] = 'Volcanic Cave', -- A Tale of the Past, a future's prophecy
  [941] = 'Infernal Stronghold', -- Infernal Cape
[951] = 'Fire God Dungeon', -- The First Hero and an Unknown Evil
    [950] = 'Volcanic Cave', -- A Tale of the Past, a future's prophecy
[1116] = 'Into the Mist' -- Beginning of the End
    [951] = 'Fire God Dungeon', -- The First Hero and an Unknown Evil
}
    [1116] = 'Into the Mist' -- Beginning of the End
  }
}
}


function p._getCreationTable(item)
function p._getCreationTable(item)
  local skill = ''
local skill = ''
  local specialReq = nil
local specialReq = nil
  local time = 0
local time = 0
  local maxTime = nil
local maxTime = nil
  local lvl = 0
local lvl = 0
  local xp = 0
local xp = 0
  local qty = nil
local qty = nil
  local req = nil
local req = nil


  local tables = {}
local tables = {}
  --First figure out what skill is used to make this...
--First figure out what skill is used to make this...
  if item.smithingLevel ~= nil then
if type(item.masteryID) == 'table' then
    skill = 'Smithing'
local skillID, masteryID = item.masteryID[1], item.masteryID[2]
    lvl = item.smithingLevel
skill = Constants.getSkillName(skillID)
    xp = item.smithingXP
if skillID == SkillEnum.Fishing then
    req = item.smithReq
-- Fishing
    qty = item.smithingQty
local fish = SkillData.Fishing.Fish[masteryID + 1]
    time = 2
if fish ~= nil and fish.itemID == item.id then
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
lvl = fish.level
  end
xp = fish.baseXP
  if item.craftingLevel ~= nil then
qty = 1
    skill = 'Crafting'
time = fish.baseMinInterval / 1000
    lvl = item.craftingLevel
maxTime = fish.baseMaxInterval / 1000
    xp = item.craftingXP
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime))
    req = item.craftReq
end
    qty = item.craftQty
elseif skillID == SkillEnum.Mining then
    time = 3
-- Mining
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, nil, item.craftGPCost))
local rock = SkillData.Mining.Rocks[masteryID + 1]
  end
if rock ~= nil then
  if item.runecraftingLevel ~= nil then
lvl = rock.levelRequired
    skill = 'Runecrafting'
xp = rock.baseExperience
    lvl = item.runecraftingLevel
qty = rock.baseQuantity
    xp = item.runecraftingXP
time = 3
    req = item.runecraftReq
if item.name == 'Dragonite Ore' then
    qty = item.runecraftQty
specialReq = Icons.Icon({"Mastery", notext='true'})..' 271 total [[Mining]] [[Mastery]]'
    time = 2
end
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, specialReq))
  end
end
  if item.fletchingLevel ~= nil then
elseif Shared.contains({SkillEnum.Smithing, SkillEnum.Fletching, SkillEnum.Crafting,
    skill = 'Fletching'
SkillEnum.Runecrafting, SkillEnum.Herblore}, skillID) then
    lvl = item.fletchingLevel
-- Smithing, Fletching, Crafting, Runecrafting, Herblore
    xp = item.fletchingXP
-- All have somewhat consistent recipe data structures
    req = item.fletchReq
local recipeKey = (skillID == SkillEnum.Herblore and 'Potions') or 'Recipes'
    qty = item.fletchQty
local recipe = SkillData[skill][recipeKey][masteryID + 1]
    time = 2
if recipe ~= nil then
    if item.name == 'Arrow Shafts' then
local masteryReq = nil
      --Arrow Shafts get special (weird) treatment
local itemMatch = (recipe.itemID == item.id)
      req = '1 of any [[Log]]'
if skillID == SkillEnum.Herblore then
      qty = '15 - 135'
-- For Herblore, we need to check a table of potion IDs & determine the mastery requirement
    end
for i, potionID in ipairs(recipe.potionIDs) do
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
if potionID == item.id then
  end
itemMatch = true
  if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
masteryReq = SkillData.Herblore.TierMasteryLevels[i]
  for i, reqSet in pairs(item.recipeRequirements) do
break
    skill = 'Cooking'
end
    lvl = item.cookingLevel
end
    xp = item.cookingXP
end
    req = reqSet
if itemMatch then
    qty = item.cookingQty
local baseTime = {
    time = item.cookingInterval / 1000
[SkillEnum.Smithing] = 2,
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
[SkillEnum.Fletching] = 2,
  end
[SkillEnum.Crafting] = 3,
  end
[SkillEnum.Runecrafting] = 2,
  if item.herbloreReq ~= nil then
[SkillEnum.Herblore] = 2
    skill = 'Herblore'
}
    req = item.herbloreReq
local baseQty = recipe.baseQuantity or 1
    --Currently using 'masteryID' as shorthand to find details, could be a better method
lvl = recipe.level
    local potionID = item.masteryID[2]
xp = recipe.baseXP
    local potionData = SkillData.Herblore.ItemData[potionID + 1]
time = baseTime[skillID]
    lvl = potionData.level
if masteryReq ~= nil and masteryReq > 1 then
    xp = potionData.herbloreXP
specialReq = Icons._MasteryReq(item.name, masteryReq, false)
    time = 2
end
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
-- Some items (such as Arrow shafts) have multiple recipes
  end
if type(recipe.alternativeCosts) == 'table' then
  if item.masteryID ~= nil and item.masteryID[1] == 4 then
local reqPart, qtyPart = {}, {}
    skill = 'Mining'
for i, altCost in ipairs(recipe.alternativeCosts) do
    lvl = SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired
local reqSubPart = {}
    time = 3
for j, itemCost in ipairs(altCost.itemCosts) do
    xp = item.miningXP
local reqItem = Items.getItemByID(itemCost.id)
    --Rune Essence has double quantity, but that's a hard-coded thing in the game so it's hard-coded here
if reqItem == nil then
    if item.name == 'Rune Essence' then qty = 2 else qty = 1 end
table.insert(reqSubPart, itemCost.qty .. 'x ?????')
else
table.insert(reqSubPart, Icons.Icon({reqItem.name, type='item', qty=itemCost.qty}))
end
end
if recipe.gpCost ~= nil and recipe.gpCost > 0 then
table.insert(reqSubPart, Icons.GP(recipe.gpCost))
end
if recipe.scCost ~= nil and recipe.scCost > 0 then
table.insert(reqSubPart, Icons.GP(recipe.scCost))
end
table.insert(reqPart, table.concat(reqSubPart, ', '))
table.insert(qtyPart, Shared.formatnum(baseQty * altCost.quantityMultiplier))
end
local sep = "<br/>'''OR''' "
req = table.concat(reqPart, sep)
qty = table.concat(qtyPart, sep)
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq))
elseif type(recipe.itemCosts) == 'table' and Shared.tableCount(recipe.itemCosts) > 0 then
req = recipe.itemCosts
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, baseQty, time, maxTime, specialReq, recipe.gpCost, recipe.scCost))
end
end
end
elseif skillID == SkillEnum.Summoning then
-- Summoning
local recipe = SkillData.Summoning.Marks[masteryID + 1]
if recipe ~= nil and recipe.itemID == item.id then
lvl = recipe.level
xp = recipe.baseXP
qty = recipe.baseQuantity
time = 5
-- Create item requirements text
local ShardCostArray, OtherCostArray = {}, {}
-- Shards
for j, cost in ipairs(recipe.itemCosts) do
local shard = Items.getItemByID(cost.id)
if shard ~= nil then
table.insert(ShardCostArray, Icons.Icon({shard.name, type='item', notext=true, qty=cost.qty}))
end
end
-- Other costs
local recipeGPCost = SkillData.Summoning.RecipeGPCost
if recipe.gpCost > 0 then
table.insert(OtherCostArray, Icons.GP(recipe.gpCost))
end
if recipe.scCost > 0 then
table.insert(OtherCostArray, Icons.SC(recipe.scCost))
end
for j, nonShardID in ipairs(recipe.nonShardItemCosts) do
local nonShard = Items.getItemByID(nonShardID)
if nonShard ~= nil then
local itemValue = math.max(nonShard.sellsFor, 20)
local nonShardQty = math.max(1, math.floor(recipeGPCost / itemValue))
table.insert(OtherCostArray, Icons.Icon({nonShard.name, type='item', notext=true, qty=nonShardQty}))
end
end
req = table.concat(ShardCostArray, ', ')
if #OtherCostArray > 0 then
req = req .. '<br/>and one of the following:<br/>' .. table.concat(OtherCostArray, "<br/>'''OR''' ")
end
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
end
end
end


    if item.name == 'Dragonite Ore' then
-- Woodcutting
      specialReq = Icons.Icon({"Mastery", notext='true'})..' 271 total [[Mining]] [[Mastery]]'
if item.type == 'Logs' then
    end
-- Determine which tree (if any) the log is from
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, specialReq))
for i, tree in ipairs(SkillData.Woodcutting.Trees) do
  end
if tree.logID == item.id then
  if item.type == "Logs" then
skill = 'Woodcutting'
    --Well this feels like cheating, but for as long as logs are the first items by ID it works
lvl = tree.levelRequired
    local treeData = SkillData.Woodcutting.Trees[item.id + 1]
time = tree.baseInterval / 1000
    skill = 'Woodcutting'
xp = tree.baseExperience
    lvl = treeData.level
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
    time = treeData.interval / 1000
break
    xp = treeData.xp
end
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
end
  end
end
  if item.fishingLevel ~= nil then
-- Cooking
    skill = 'Fishing'
if item.canEat then
    lvl = item.fishingLevel
for i, recipe in ipairs(SkillData.Cooking.Recipes) do
    xp = item.fishingXP
if recipe.itemID == item.id or recipe.perfectCookID == item.id then
    time = item.minFishingInterval/1000
skill = 'Cooking'
    maxTime = item.maxFishingInterval/1000
lvl = recipe.level
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime))
xp = recipe.baseXP
  end
req = recipe.itemCosts
  --had to add cooking to the list of valid categories here to account for cherries/apples
qty = recipe.baseQuantity
  if item.category == 'Cooking' or item.type == "Harvest" or item.type == "Herb" or item.type == "Logs" or Shared.contains(item.name, '(Perfect)') then
time = recipe.baseInterval / 1000
    --Harvest/Herb means farming
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
    --Logs might mean farming or might not. Depends on the logs
break
    for i, item2 in pairs(ItemData.Items) do
end
      if item2.grownItemID == item.id then
end
        skill = 'Farming'
end
        lvl = item2.farmingLevel
-- Farming
        xp = item2.farmingXP
if item.type == 'Herb' or item.type == 'Logs' or (item.type == 'Food' and item.category ~= 'Cooking') then
        time = item2.timeToGrow
-- Herb means farming
        if item.type == 'Logs' then
-- Logs/Food might mean farming or might not. Depends on the item
          qty = 35
for i, seed in ipairs(ItemData.Items) do
        else
if seed.grownItemID ~= nil and seed.grownItemID == item.id then
          qty = 15
skill = 'Farming'
        end
lvl = seed.farmingLevel
        req = {{id = i - 1, qty = (item2.seedsRequired ~= nil and item2.seedsRequired or 1)}}
xp = seed.farmingXP
        table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
time = seed.timeToGrow
        break
if item.type == 'Logs' then
      end
qty = 35
else
qty = 15
end
req = {{id = seed.id, qty = (seed.seedsRequired ~= nil and seed.seedsRequired or 1)}}
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
break
end
end
end


      --If this is a perfect item, need to find the original
-- Alt. Magic, excludes Gems and Bars
      if item2.perfectItem == item.id and item2.recipeRequirements ~= nil then
-- Bars are handled by getItemSuperheatTable()
  for j, reqSet in pairs(item2.recipeRequirements) do
-- Gems are handled by _getItemLootSourceTable()
      skill = 'Cooking'
for i, altSpell in ipairs(MagicData.AltMagic) do
    lvl = item2.cookingLevel
if type(altSpell.produces) == 'number' and altSpell.produces == item.id then
    xp = item2.cookingXP
table.insert(tables, p._buildAltMagicTable(altSpell))
    req = reqSet
    qty = item2.cookingQty
    time = item2.cookingInterval / 1000
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
end
end
  end
end
    end
  end
  if item.summoningLevel ~= nil then
    skill = 'Summoning'
    lvl = item.summoningLevel
    --Summoning uses a formula to calculate XP for creation instead of referring to the item's XP data directly
    xp = (5 + 2 * math.floor(item.summoningLevel / 5))
    local ShardCostArray = {}
    for j, cost in Shared.skpairs(item.summoningReq[1]) do
      if cost.id >= 0 then
        local item = Items.getItemByID(cost.id)
        if item.type == 'Shard' then
          table.insert(ShardCostArray, Icons.Icon({item.name,  type='item', notext=true, qty=cost.qty}))
        end
      end
    end
    req = table.concat(ShardCostArray, ', ')
    req = req..'<br/>\r\nand one of the following<br/>\r\n'
    local OtherCostArray = {}
    local recipeGPCost = SkillData.Summoning.Settings.recipeGPCost
    for j, altCost in Shared.skpairs(item.summoningReq) do
      local nonShardArray = {}
      for k, cost in Shared.skpairs(altCost) do
        if cost.id >= 0 then
          local item = Items.getItemByID(cost.id)
          if item.type ~= 'Shard' then
            local sellPrice = math.max(item.sellsFor, 20)
            table.insert(nonShardArray, Icons.Icon({item.name, type='item', notext=true, qty=math.max(1, math.floor(recipeGPCost / sellPrice))}))
          end
        else
          if cost.id == -4 then
            table.insert(nonShardArray, Icons.GP(recipeGPCost))
          elseif cost.id == -5 then
            table.insert(nonShardArray, Icons.SC(recipeGPCost))
          end
        end
      end
      table.insert(OtherCostArray, table.concat(nonShardArray, ', '))
    end
    req = req..table.concat(OtherCostArray, "<br/>'''OR''' ")
    qty = item.summoningQty
    time = 5
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
  --A couple special exceptions for Alt Magic
  --Not Gems or Bars though since those have their own separate thing
  if item.name == 'Rune Essence' then
    table.insert(tables, p.buildAltMagicTable('Just Learning'))
  elseif item.name == 'Bones' then
    table.insert(tables, p.buildAltMagicTable('Bone Offering'))
  elseif item.name == 'Holy Dust' then
    table.insert(tables, p.buildAltMagicTable('Blessed Offering'))
  end


  if Shared.tableCount(tables) == 0 then
if Shared.tableCount(tables) == 0 then
    return ""
return ""
  else
else
    return table.concat(tables, '\r\n')
return table.concat(tables, '\r\n')
  end
end
end
 
function p.getAltMagicTable(frame)
local spellName = frame.args ~= nil and frame.args[1] or frame
local spell = Magic.getSpell(spellName, 'AltMagic')
if spell == nil then
return 'ERROR: Could not find Alt. Magic spell "' .. spellName .. '"[[Category:Pages with script errors]]'
else
return p._buildAltMagicTable(spell)
end
end
end


function p.buildAltMagicTable(spellName)
function p._buildAltMagicTable(spell)
  local spell = Magic.getSpell(spellName, 'AltMagic')
local resultPart = {}
  local resultPart = {}
table.insert(resultPart, '{|class="wikitable"\r\n|-')
  table.insert(resultPart, '{|class="wikitable"\r\n|-')
table.insert(resultPart, '\r\n!colspan="2"|'..Icons.Icon({spell.name, type='spell'}))
  table.insert(resultPart, '\r\n!colspan="2"|'..Icons.Icon({spell.name, type='spell'}))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Requirements')
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Requirements')
table.insert(resultPart, '\r\n|'..Icons._SkillReq('Magic', spell.level))
  table.insert(resultPart, '\r\n|'..Icons._SkillReq('Magic', spell.level))
 
  -- 1 means select any item. 0 would mean Superheat, but that's handled elsewhere
-- The produces property of Alt. Magic spells is as follows:
  -- -1 means no item is needed, so hide this section
-- -3 = A random gem, using the same weights as Mining
  if spell.selectItem == 1 then
-- -2 = A bar of the type being created (Superheat)
    table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
-- -1 = GP (Alchemy)
    table.insert(resultPart, '\r\n|1 of any item')
--  0 = Undefined
  end
-- >0 = Item ID of the item being produced
  --Add runes
-- The amount produced is determined by the productionRatio property
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Runes\r\n|')
 
  for i, req in pairs(spell.runesRequired) do
-- The consumes property of Alt. Magic spells is as follows:
    local rune = Items.getItemByID(req.id)
-- 0 = Any item
    if i > 1 then table.insert(resultPart, ', ') end
-- 1 = Junk item
    table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
-- 2 = Superheat/ores with Coal
  end
-- 3 = Superheat/ores without Coal
  if spell.runesRequiredAlt ~= nil and Shared.tableCount(spell.runesRequired) ~= Shared.tableCount(spell.runesRequiredAlt) then
-- 4 = Nothing
    table.insert(resultPart, "<br/>'''OR'''<br/>")
-- 5 = Coal ore
    for i, req in pairs(spell.runesRequiredAlt) do
-- Superheat (2, 3) is handled by _getItemSuperheatTable()
      local rune = Items.getItemByID(req.id)
if spell.consumes ~= nil then
      if i > 1 then table.insert(resultPart, ', ') end
local consumeText = {
      table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
'1 of any item',
    end
'1 of any [[Fishing#Junk|Junk]] item',
  end
'1 x required ores for the chosen bar',
'1 x required ores (except ' .. Icons.Icon({'Coal Ore', type='item'}) .. ') for the chosen bar',
nil,
Icons.Icon({'Coal Ore', type='item', qty=1})
}
local consumeStr = consumeText[spell.consumes + 1]
if consumeStr ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
table.insert(resultPart, '\r\n| ' .. consumeStr)
end
end


  --Now just need the output quantity, xp, and casting time (which is always 2)
--Add runes
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity\r\n|'..spell.convertToQty)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Runes\r\n| ' .. Magic._getSpellRunes(spell))
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base XP\r\n|'..spell.magicXP)
 
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Cast Time\r\n|2s')
--Now just need the output quantity, xp, and casting time (which is always 2)
  table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity\r\n|' .. spell.productionRatio)
  return table.concat(resultPart)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base XP\r\n|' .. spell.baseExperience)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Cast Time\r\n|2s')
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
end


function p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq, gpCost)
function p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq, gpCost, scCost)
  if qty == nil then qty = 1 end
if qty == nil then qty = 1 end
  local resultPart = {}
local resultPart = {}
  table.insert(resultPart, '{|class="wikitable"')
table.insert(resultPart, '{|class="wikitable"')
  if req ~= nil then
if req ~= nil then
    table.insert(resultPart, '\r\n!colspan="2"|Item Creation')
table.insert(resultPart, '\r\n!colspan="2"|Item Creation')
  else
else
    table.insert(resultPart, '\r\n!colspan="2"|Item Production')
table.insert(resultPart, '\r\n!colspan="2"|Item Production')
  end
end
  table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Requirements')
table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Requirements')
  table.insert(resultPart, '\r\n|'..Icons._SkillReq(skill, lvl))
table.insert(resultPart, '\r\n|'..Icons._SkillReq(skill, lvl))
  if specialReq ~= nil then table.insert(resultPart, '<br/>'..specialReq) end
if specialReq ~= nil then table.insert(resultPart, '<br/>'..specialReq) end


  if req ~= nil then
if req ~= nil then
    table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Materials\r\n|')
table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Materials\r\n|')
    if type(req) == 'table' then
if type(req) == 'table' then
      for i, mat in pairs(req) do
for i, mat in pairs(req) do
        if i > 1 then table.insert(resultPart, '<br/>') end
if i > 1 then table.insert(resultPart, '<br/>') end
        local matItem = Items.getItemByID(mat.id)
local matItem = Items.getItemByID(mat.id)
        if matItem == nil then
if matItem == nil then
          table.insert(resultPart, mat.qty..'x ?????')
table.insert(resultPart, mat.qty..'x ?????')
        else
else
          table.insert(resultPart, Icons.Icon({matItem.name, type='item', qty=mat.qty}))
table.insert(resultPart, Icons.Icon({matItem.name, type='item', qty=mat.qty}))
        end
end
      end
end
      if gpCost ~= nil and gpCost > 0 then
if gpCost ~= nil and gpCost > 0 then
    table.insert(resultPart, '<br/>')
table.insert(resultPart, '<br/>')
    table.insert(resultPart, Icons.GP(gpCost))
table.insert(resultPart, Icons.GP(gpCost))
      end
end
    else
if scCost ~= nil and scCost > 0 then
    table.insert(resultPart, req)
table.insert(resultPart, '<br/>')
    end
table.insert(resultPart, Icons.SC(scCost))
  end
end
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity')
else
  table.insert(resultPart, '\r\n|'..qty)
table.insert(resultPart, req)
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Experience')
end
  table.insert(resultPart, '\r\n|'..Shared.formatnum(xp)..' XP')
end
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Creation Time')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity')
  table.insert(resultPart, '\r\n|'..Shared.formatnum(Shared.round(time, 2, 0))..'s')
table.insert(resultPart, '\r\n|'..qty)
  if maxTime ~= nil then table.insert(resultPart, ' - '..Shared.formatnum(Shared.round(maxTime, 2, 0))..'s') end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Experience')
  table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|'..Shared.formatnum(xp)..' XP')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Creation Time')
table.insert(resultPart, '\r\n|'..Shared.formatnum(Shared.round(time, 2, 0))..'s')
if maxTime ~= nil and maxTime > time then table.insert(resultPart, ' - '..Shared.formatnum(Shared.round(maxTime, 2, 0))..'s') end
table.insert(resultPart, '\r\n|}')


  return table.concat(resultPart)
return table.concat(resultPart)
end
end


function p.getCreationTable(frame)
function p.getCreationTable(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[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getCreationTable(item)
return p._getCreationTable(item)
end
end


function p._getItemSources(item, asList, addCategories)
function p._getItemSources(item, asList, addCategories)
  local lineArray = {}
local lineArray = {}
  local categoryArray = {}
local categoryArray = {}


  --Alright, time to go through all the ways you can get an item...
--Alright, time to go through all the ways you can get an item...
  --First up: Can we kill somebody and take theirs?
--First up: Can we kill somebody and take theirs?
  local killStrPart = {}
local killStrPart = {}
  local dungeonStrPart = {}
for i, monster in ipairs(MonsterData.Monsters) do
  for i, monster in ipairs(MonsterData.Monsters) do
local isDrop = false
  local isDrop, isBones = false, false
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
  if monster.bones == item.id and Monsters.getMonsterBones(monster) ~= nil then
-- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
  -- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
isDrop = true
  isDrop = true
elseif monster.lootTable ~= nil then
  isBones = true
-- If the monster has a loot table, check if the item we are looking for is in there
      elseif monster.lootTable ~= nil then
-- Dungeon exclusive monsters don't count as they are either:
      -- If the monster has a loot table, check if the item we are looking for is in there
--  - A monster before the boss, which doesn't drop anything except shards (checked above)
          for j, loot in ipairs(monster.lootTable) do
--  - A boss monster, whose drops are accounted for in data from Areas instead
              if loot[1] == item.id then
for j, loot in ipairs(monster.lootTable) do
            isDrop = true
if loot[1] == item.id and not Monsters._isDungeonOnlyMonster(monster) then
            break
isDrop = true
            end
break
        end
end
      end
end
  if isDrop then
end
  if not isBones and Monsters._isDungeonOnlyMonster(monster) then
if isDrop then
  -- For dungeon exclusive monsters, loot is only rolled when they are the last
-- Item drops when the monster is killed
  -- monster within that dungeon (unless it is a shard)
table.insert(killStrPart, Icons.Icon({monster.name, type='monster', notext=true}))
  if monster.isBoss then
end
  local areaList = Areas.getMonsterAreas(monster.id)
end
  for k, area in ipairs(areaList) do
-- Is the item dropped from any dungeon?
  if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
local dungeonStrPart = {}
  table.insert(dungeonStrPart, Icons.Icon({area.name, type='dungeon', notext=true}))
local dungeonList = Areas.getAreas(function(area) return area.type == 'dungeon' and type(area.rewards) == 'table' and Shared.contains(area.rewards, item.id) end)
  end
if dungeonList ~= nil then
  end
for i, dungeon in ipairs(dungeonList) do
  end
table.insert(dungeonStrPart, Icons.Icon({dungeon.name, type='dungeon', notext=true}))
  else
end
  -- Item is not an end of dungeon reward, and drops when the monster is killed
end
  table.insert(killStrPart, Icons.Icon({monster.name, type='monster', notext=true}))
-- Is the item dropped from a cycle of the Impending Darkness event?
  end
for i, eventItemID in ipairs(Areas.eventData.rewards) do
  end
if item.id == eventItemID then
  end
local dungPrefix = (i == Shared.tableCount(Areas.eventData.rewards) and '' or i .. ' ' .. (i == 1 and 'cycle' or 'cycles') .. ' of ')
  -- Is the item dropped from a cycle of the Impending Darkness event?
table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({'Impending Darkness Event', type='dungeon', notext=true}))
  for i, eventItemID in ipairs(Areas.eventData.rewards) do
break
      if item.id == eventItemID then
end
  local dungPrefix = (i == Shared.tableCount(Areas.eventData.rewards) and '' or i .. ' ' .. (i == 1 and 'cycle' or 'cycles') .. ' of ')
end
          table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({'Impending Darkness Event', type='dungeon', notext=true}))
-- Special exceptions for lore books
  break
if sourceOverrides['Dungeon'][item.id] ~= nil then
  end
table.insert(dungeonStrPart, Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon', notext=true}))
  end
end
  -- Special exceptions for Fire/Infernal Cape and first two lore books
  if sourceOverrides['Dungeon'][item.id] ~= nil then
    table.insert(dungeonStrPart, Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon', notext=true}))
  end


  if Shared.tableCount(dungeonStrPart) > 0 then
if Shared.tableCount(dungeonStrPart) > 0 then
  table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, ','))
table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, ','))
  end
end
  if Shared.tableCount(killStrPart) > 0 then
if Shared.tableCount(killStrPart) > 0 then
  table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, ','))
table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, ','))
  end
end


  --Next: Can we find it in a box?
--Next: Can we find it in a box?
  --While we're here, check for upgrades, originals (for perfect items), and growing
--While we're here, check for upgrades, and growing
  local lootStr = ''
local lootPart, upgradePart, growPart = {}, {}, {}
  local upgradeStr = ''
for i, item2 in pairs(ItemData.Items) do
  local cookStr = ''
if item2.dropTable ~= nil then
  local growStr = ''
for j, loot in ipairs(item2.dropTable) do
  local count1 = 0
if loot[1] == item.id then
  local count2 = 0
table.insert(lootPart, Icons.Icon({item2.name, type='item', notext=true}))
  for i, item2 in pairs(ItemData.Items) do
break
    if item2.dropTable ~= nil then
end
      for j, loot in pairs(item2.dropTable) do
end
        if loot[1] == item.id then
end
          count1 = count1 + 1
if item2.trimmedItemID ~= nil and item2.trimmedItemID == item.id then
          if string.len(lootStr) > 0 then
table.insert(upgradePart, Icons.Icon({item2.name, type='item', notext=true}))
            lootStr = lootStr..','
end
            --if count1 % 3 == 1 and count1 > 1 then lootStr = lootStr..'<br/>' end
if item2.grownItemID == item.id then
            lootStr = lootStr..Icons.Icon({item2.name, type="item", notext="true"})
-- Farming
          else
table.insert(growPart, Icons.Icon({item2.name, type='item', notext=true}))
            lootStr = lootStr..'Opening: '..Icons.Icon({item2.name, type="item", notext="true"})
end
          end
end
        end
if #lootPart > 0 then
      end
table.insert(lineArray, 'Opening: ' .. table.concat(lootPart, ','))
    end
end
    if item2.trimmedItemID == item.id then
if #upgradePart > 0 then
          count2 = count2 + 1
table.insert(categoryArray, '[[Category:Upgraded Items]]')
        if string.len(upgradeStr) > 0 then
table.insert(lineArray, 'Upgrading: ' .. table.concat(upgradePart, ','))
          upgradeStr = upgradeStr..','
end
          --if count2 % 3 == 1 and count2 > 1 then upgradeStr = upgradeStr..'<br/>' end
if #growPart > 0 then
          upgradeStr = upgradeStr..Icons.Icon({item2.name, type="item", notext="true"})
table.insert(categoryArray, '[[Category:Harvestable Items]]')
        else
table.insert(lineArray, 'Growing: ' .. table.concat(growPart, ','))
          table.insert(categoryArray, '[[Category:Upgraded Items]]')
end
          upgradeStr = upgradeStr..'Upgrading: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.grownItemID == item.id then
        if string.len(growStr) > 0 then
          growStr = growStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          table.insert(categoryArray, '[[Category:Harvestable Items]]')
          growStr = growStr..'Growing: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.perfectItem == item.id and item2.cookingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq('Cooking', item2.cookingLevel))
    end
  end
  if string.len(lootStr) > 0 then table.insert(lineArray, lootStr) end
  if string.len(upgradeStr) > 0 then table.insert(lineArray, upgradeStr) end
  if string.len(cookStr) > 0 then table.insert(lineArray, cookStr) end
  if string.len(growStr) > 0 then table.insert(lineArray, growStr) end


  --Next: Can we take it from somebody else -without- killing them?
--Next: Can we take it from somebody else -without- killing them?
  local thiefItems = GatheringSkills.getThievingSourcesForItem(item.id)
local thiefItems = Skills.getThievingSourcesForItem(item.id)
  local thiefStr = ''
if type(thiefItems) == 'table' then
  if Shared.tableCount(thiefItems) > 0 then
local thiefPart = {}
  thiefStr = 'Pickpocketing: '
for i, thiefRow in ipairs(thiefItems) do
  for i, thiefRow in pairs(thiefItems) do
if thiefRow.npc == 'all' then
  if thiefRow.npc == 'all' then
--if 'all' is the npc, this is a rare item so just say 'Thieving level 1'
  --if 'any' is the npc, this is a rare item so just say 'Thieving level 1'
table.insert(lineArray, Icons._SkillReq('Thieving', 1))
  thiefStr = Icons._SkillReq('Thieving', 1)
else
  else
table.insert(thiefPart, Icons.Icon({thiefRow.npc, type='thieving', notext=true}))
  if i > 1 then thiefStr = thiefStr..', ' end
end
  thiefStr = thiefStr..Icons.Icon({thiefRow.npc, type='thieving', notext='true'})
end
  end
if #thiefPart > 0 then
  end
table.insert(lineArray, 'Pickpocketing: ' .. table.concat(thiefPart, ','))
  end
end
  if string.len(thiefStr) > 0 then table.insert(lineArray, thiefStr) end
end


  --If all else fails, I guess we should check if we can make it ourselves
--Check if we can make it ourselves
  --AstrologyCheck
--AstrologyCheck (Just a brute force for now because only two items)
  --(Just a brute force for now because only two items and I'm lazy)
if Shared.contains({'Stardust', 'Golden Stardust'}, item.name) then
  if item.name == 'Stardust' or item.name == 'Golden Stardust' then
table.insert(lineArray, Icons.Icon({'Astrology', type='skill'}))
  table.insert(lineArray, Icons.Icon({"Astrology", type="skill"}))
end
  end


  --SmithCheck:
-- Sources discoverable through mastery IDs
  if item.smithingLevel ~= nil then
-- Does _not_ handle:
    table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
-- Fishing: Junk, special items
  end
-- Cooking: perfect items
if type(item.masteryID) == 'table' then
local skillID, masteryID = item.masteryID[1], item.masteryID[2]
local skill = Constants.getSkillName(skillID)
local keyData = {
[SkillEnum.Fishing] = { ["recipe"] = 'Fish' },
[SkillEnum.Mining] = { ["recipe"] = 'Rocks', ["level"] = 'levelRequired', ["item"] = 'oreID' },
[SkillEnum.Smithing] = {},
[SkillEnum.Fletching] = {},
[SkillEnum.Crafting] = {},
[SkillEnum.Runecrafting] = {},
[SkillEnum.Herblore] = { ["recipe"] = 'Potions', ["item"] = 'potionIDs', ["isItemList"] = true },
[SkillEnum.Summoning] = { ["recipe"] = 'Marks' }
}
local keys = keyData[skillID]
if type(keys) == 'table' then
if keys.recipe == nil then
keys.recipe = 'Recipes'
end
if keys.level == nil then
keys.level = 'level'
end
if keys.item == nil then
keys.item = 'itemID'
end
if keys.isItemList == nil then
keys.isItemList = false
end


  --CraftCheck:
local recipe = SkillData[skill][keys.recipe][masteryID + 1]
  if item.craftingLevel ~= nil then
if recipe ~= nil and (
    table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
(not keys.isItemList and recipe[keys.item] == item.id) or
  end
(keys.isItemList and Shared.contains(recipe[keys.item], item.id))) then
local levelReq = recipe[keys.level]
if levelReq ~= nil then
table.insert(lineArray, Icons._SkillReq(skill, levelReq))
end
end
end
end


  --FletchCheck:
-- Woodcutting
  if item.fletchingLevel ~= nil then
for i, tree in ipairs(SkillData.Woodcutting.Trees) do
    table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
if tree.logID == item.id then
  end
table.insert(lineArray, Icons._SkillReq('Woodcutting', tree.levelRequired))
break
end
end


  --RunecraftCheck:
-- Woodcutting: Nests
  if item.runecraftingLevel ~= nil then
if item.name == 'Bird Nest' then
    table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
table.insert(lineArray, Icons._SkillReq('Woodcutting', 1))
  end
end


  --CookCheck
-- Fishing: Junk & Specials
  if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
if Shared.contains(SkillData.Fishing.JunkItems, item.id) then
  table.insert(lineArray, Icons._SkillReq('Cooking', item.cookingLevel))
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Junk|Junk]]')
  end
else
for i, specialItem in ipairs(SkillData.Fishing.SpecialItems) do
if specialItem[1] == item.id then
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Special|Special]]')
break
end
end
end


  --MineCheck:
-- Cooking
  if item.masteryID ~= nil and item.masteryID[1] == 4 then
if item.canEat then
    table.insert(lineArray, Icons._SkillReq("Mining", SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired))
for i, recipe in ipairs(SkillData.Cooking.Recipes) do
  end
if recipe.itemID == item.id or recipe.perfectCookID == item.id then
table.insert(lineArray, Icons._SkillReq('Cooking', recipe.level))
break
end
end
end


  --FishCheck:
-- Alt. Magic
  if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
for i, altSpell in ipairs(MagicData.AltMagic) do
    table.insert(lineArray, Icons.Icon({"Fishing", type='skill', notext=true})..' [[Fishing#'..item.type..'|'..item.type..']]')
if type(altSpell.produces) == 'number' and
  elseif item.fishingLevel ~= nil then
((altSpell.produces == -3 and item.type == 'Gem' and item.name ~= 'Jadestone')
    table.insert(lineArray, Icons._SkillReq("Fishing", item.fishingLevel))
or (altSpell.produces == -2 and item.type == 'Bar')
  end
    or (altSpell.produces > 0 and altSpell.produces == item.id)) then
table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
break
end
end


  --HerbCheck:
--Finally there are some weird exceptions:
  if item.masteryID ~= nil and item.masteryID[1] == 19 then
--Coal can be acquired via firemaking
    local potionData = SkillData.Herblore.ItemData[item.masteryID[2] + 1].level
if item.name == "Coal Ore" then
    table.insert(lineArray, Icons._SkillReq("Herblore", potionData))
table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
  end
end


  --WoodcuttingCheck:
--Gems can be acquired from Mining
  if item.type == 'Logs' then
if item.type == 'Gem' and item.name ~= 'Jadestone' then
    local treeData = SkillData.Woodcutting.Trees[item.id + 1]
table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]')
    local lvl = treeData.level
end
    table.insert(lineArray, Icons._SkillReq("Woodcutting", lvl))
  end


  --SummoningCheck:
--Rhaelyx pieces are also special
  if item.summoningLevel ~= nil then
if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
    table.insert(lineArray, Icons._SkillReq("Summoning", item.summoningLevel))
local rhaSkills = {
  end
Circlet = {'Woodcutting', 'Fishing', 'Mining', 'Thieving', 'Farming', 'Agility', 'Astrology'},
Jewel = {'Firemaking', 'Cooking', 'Smithing', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Summoning'}
}
local rhaSkList, subText = nil, ''
if item.name == 'Circlet of Rhaelyx' then
rhaSkList = {rhaSkills.Circlet}
elseif item.name == 'Jewel of Rhaelyx' then
rhaSkList = {rhaSkills.Jewel}
elseif item.name == 'Mysterious Stone' then
rhaSkList = {rhaSkills.Jewel, rhaSkills.Circlet}
subText = '<br/>after finding ' .. Icons.Icon({'Crown of Rhaelyx', type='item'})
end


  --Finally there are some weird exceptions:
local rhaStrPart = {}
  --Coal can be acquired via firemaking
for i, skillList in ipairs(rhaSkList) do
  if item.name == "Coal Ore" then
for j, skillName in ipairs(skillList) do
    table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
table.insert(rhaStrPart, Icons.Icon({skillName, type='skill', notext=true}))
  end
end
end
table.insert(lineArray, 'Any action in: ' .. table.concat(rhaStrPart, ', ') .. subText)
end


  --Gems can be acquired from mining, fishing, and alt. magic
--Tokens are from the appropriate skill
  if item.type == 'Gem' and item.name ~= 'Jadestone' then
if item.isToken and item.skill ~= nil then
    table.insert(lineArray, Icons.Icon({"Fishing", type='skill', notext=true})..' [[Fishing#Special|Special]]')
table.insert(lineArray, Icons._SkillReq(Constants.getSkillName(item.skill), 1))
    table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]')
end
    table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
  end


  --Bars and some other stuff can also be acquired via Alt. Magic
-- Golbin Raid exclusive items
  if item.type == 'Bar' or Shared.contains(Items.AltMagicProducts, item.name) then
if item.golbinRaidExclusive then
    table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
table.insert(lineArray, Icons.Icon({'Golbin Raid', type='pet', img='Golden Golbin'}))
  end
end


  --Rhaelyx pieces are also special
--Shop items (including special items like gloves that aren't otherwise listed)
  if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
local shopSources = Shop.getItemSourceArray(item.id)
    local rhaSkills = {
if Shared.tableCount(shopSources) > 0 then
      Circlet = {'Woodcutting', 'Fishing', 'Mining', 'Thieving', 'Farming', 'Agility', 'Astrology'},
table.insert(lineArray, Icons.Icon({'Shop'}))
      Jewel = {'Firemaking', 'Cooking', 'Smithing', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Summoning'}
end
    }
    local rhaSkList = {}
    if item.name == 'Circlet of Rhaelyx' then
      rhaSkList = rhaSkills.Circlet
    elseif item.name == 'Jewel of Rhaelyx' then
      rhaSkList = rhaSkills.Jewel
    elseif item.name == 'Mysterious Stone' then
      rhaSkList = rhaSkills.Jewel
      for i, v in ipairs(rhaSkills.Circlet) do
        table.insert(rhaSkList, v)
      end
    end


    local rhaStrPart = {}
--Easter Eggs (manual list 'cause don't have a better way to do that)
    for i, skillName in ipairs(rhaSkList) do
if Shared.contains(Items.EasterEggs, item.name) then
      table.insert(rhaStrPart, Icons.Icon({skillName, type='skill', notext=true}))
table.insert(lineArray, '[[Easter Eggs]]')
    end
end
    local rhaStr = 'Any action in: ' .. table.concat(rhaStrPart, ', ')
-- Event exclusive items (also a manual list)
    if item.name == 'Mysterious Stone' then rhaStr = rhaStr .. '<br/>after finding ' .. Icons.Icon({'Crown of Rhaelyx', type='item'}) end
if Shared.contains(Items.EventItems, item.name) then
    table.insert(lineArray, rhaStr)
table.insert(lineArray, '[[Events]]')
  end
end


  --Tokens are from the appropriate skill
--Gold Topaz Ring drops from any action (when not wearing a Gold Topaz Ring)
  if item.isToken and item.skill ~= nil then
--Also handling Signet Ring things here
    table.insert(lineArray, Icons._SkillReq(Constants.getSkillName(item.skill), 1))
if item.name == 'Gold Topaz Ring' then
  end
table.insert(lineArray, 'Any non-combat action if not worn (Instead of '..Icons.Icon({"Signet Ring Half (a)", type="item"})..')')
table.insert(lineArray, 'Killing any monster if not worn (Instead of '..Icons.Icon({"Signet Ring Half (b)", type="item"})..')')
elseif item.name == 'Signet Ring Half (a)' then
table.insert(lineArray, 'Any non-combat action while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
elseif item.name == 'Signet Ring Half (b)' then
table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
end


  --Shop items (including special items like gloves that aren't otherwise listed)
local resultPart = {}
  local shopSources = Shop.getItemSourceArray(item.id)
if asList then
  if Shared.tableCount(shopSources) > 0 then
table.insert(resultPart, '* '..table.concat(lineArray, "\r\n* "))
    table.insert(lineArray, Icons.Icon({'Shop'}))
else
  end
table.insert(resultPart, '<div style="max-width:180px;text-align:right">' .. table.concat(lineArray, "<br/>") .. '</div>')
 
end
  --Easter Eggs (manual list 'cause don't have a better way to do that)
if addCategories then table.insert(resultPart, table.concat(categoryArray, '')) end
  if Shared.contains(Items.EasterEggs, item.name) then
return table.concat(resultPart)
    table.insert(lineArray, '[[Easter Eggs]]')
  end
  -- Event exclusive items (also a manual list)
  if Shared.contains(Items.EventItems, item.name) then
  table.insert(lineArray, '[[Events]]')
  end
 
  --Gold Topaz Ring drops from any action (when not wearing a Gold Topaz Ring)
  --Also handling Signet Ring things here
  if item.name == 'Gold Topaz Ring' then
table.insert(lineArray, 'Any non-combat action if not worn (Instead of '..Icons.Icon({"Signet Ring Half (a)", type="item"})..')')
table.insert(lineArray, 'Killing any monster if not worn (Instead of '..Icons.Icon({"Signet Ring Half (b)", type="item"})..')')
  elseif item.name == 'Signet Ring Half (a)' then
table.insert(lineArray, 'Any non-combat action while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
  elseif item.name == 'Signet Ring Half (b)' then
    table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
  end
 
  local resultPart = {}
  if asList then
    table.insert(resultPart, '* '..table.concat(lineArray, "\r\n* "))
  else
    table.insert(resultPart, '<div style="max-width:180px;text-align:right">' .. table.concat(lineArray, "<br/>") .. '</div>')
  end
  if addCategories then table.insert(resultPart, table.concat(categoryArray, '')) end
  return table.concat(resultPart)
end
end


function p.getItemSources(frame)
function p.getItemSources(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 asList = false
local asList = false
  local addCategories = false
local addCategories = false
  if frame.args ~= nil then
if frame.args ~= nil then
    asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
    addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
  end
end
  if item == nil then
if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


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


function p._getItemLootSourceTable(item)
function p._getItemLootSourceTable(item)
  local resultPart = {}
local resultPart = {}
  table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
  table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
  table.insert(resultPart, '\r\n!Source!!Source Type!!Quantity!!colspan="2"|Chance')
table.insert(resultPart, '\r\n!Source!!Source Type!!Quantity!!colspan="2"|Chance')


  --Set up function for adding rows
--Set up function for adding rows
  local buildRow = function(source, type, minqty, qty, weight, totalWeight)
local buildRow = function(source, type, minqty, qty, weight, totalWeight)
    if minqty == nil then minqty = 1 end
if minqty == nil then minqty = 1 end
    local rowPart = {}
local rowPart = {}
    table.insert(rowPart, '\r\n|-')
table.insert(rowPart, '\r\n|-')
    table.insert(rowPart, '\r\n|style="text-align: left;"|'..source)
table.insert(rowPart, '\r\n|style="text-align: left;"|'..source)
    table.insert(rowPart, '\r\n|style="text-align: left;"|'..type)
table.insert(rowPart, '\r\n|style="text-align: left;"|'..type)


    table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="'..qty..'"|'..minqty)
table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="'..qty..'"|'..Shared.formatnum(minqty))
    if qty ~= minqty then table.insert(rowPart, ' - '..qty) end
if qty ~= minqty then table.insert(rowPart, ' - '..Shared.formatnum(qty)) end
    local chance = Shared.round(weight / totalWeight * 100, 2, 2)
local chance = weight / totalWeight * 100
    if weight >= totalWeight then
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
      -- Fraction would be 1/1, so only show the percentage
local fmt = (chance < 0.10 and '%.2g') or '%.2f'
      chance = 100
chance = string.format(fmt, chance)
      table.insert(rowPart, '\r\n|colspan="2" ')
if weight >= totalWeight then
    else
-- Fraction would be 1/1, so only show the percentage
local fraction = Shared.fraction(weight, totalWeight)
chance = 100
if Shared.contains(fraction, '%.') then
--If fraction contains decimals, something screwy happened so just show only percentage
--(happens sometimes with the rare thieving items)
table.insert(rowPart, '\r\n|colspan="2" ')
table.insert(rowPart, '\r\n|colspan="2" ')
else
else
    table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="' .. chance .. '"| ' .. Shared.fraction(weight, totalWeight) .. '\r\n|')
local fraction = Shared.fraction(weight, totalWeight)
    end
if Shared.contains(fraction, '%.') then
    end
--If fraction contains decimals, something screwy happened so just show only percentage
    if weight == -1 then
--(happens sometimes with the rare thieving items)
    --Weight of -1 means this is a weird row that has a variable percentage
table.insert(rowPart, '\r\n|colspan="2" ')
    table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
else
    else
table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="' .. chance .. '"| ' .. Shared.fraction(weight, totalWeight) .. '\r\n|')
    table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chance .. '"|'..chance..'%')
end
    end
end
    return table.concat(rowPart)
if weight == -1 then
  end
--Weight of -1 means this is a weird row that has a variable percentage
  local dropRows = {}
table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
else
table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chance .. '"|'..chance..'%')
end
return table.concat(rowPart)
end
local dropRows = {}


  --Alright, time to go through a few ways to get the item
--Alright, time to go through a few ways to get the item
  --First up: Can we kill somebody and take theirs?
--First up: Can we kill somebody and take theirs?
  for i, monster in ipairs(MonsterData.Monsters) do
for i, monster in ipairs(MonsterData.Monsters) do
    local minqty = 1
local minqty = 1
    local qty = 1
local qty = 1
    local wt = 0
local wt = 0
    local totalWt = 0
local totalWt = 0
    --Only add bones if this monster has loot (ie appears outside a dungeon) and isn't a boss
--Only add bones if this monster has loot (ie appears outside a dungeon) and isn't a boss
    --... unless we're looking for Shards of course, at which point we'll take any monster with the right bones
--... unless we're looking for Shards of course, at which point we'll take any monster with the right bones
    if monster.bones == item.id and Monsters.getMonsterBones(monster) ~= nil then
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
      qty = monster.boneQty ~= nil and monster.boneQty or 1
qty = monster.boneQty ~= nil and monster.boneQty or 1
      minqty = qty
minqty = qty
      wt = 1
wt = 1
      totalWt = 1
totalWt = 1
    elseif monster.lootTable ~= nil then
elseif monster.lootTable ~= nil then
  -- If the monster has a loot table, check if the item we are looking for is in there
-- If the monster has a loot table, check if the item we are looking for is in there
      for j, loot in ipairs(monster.lootTable) do
-- Dungeon exclusive monsters don't count as they are either:
        totalWt = totalWt + loot[2]
--  - A monster before the boss, which doesn't drop anything except shards (checked above)
        if loot[1] == item.id then
--  - A boss monster, whose drops are accounted for in data from Areas instead
          wt = loot[2]
for j, loot in ipairs(monster.lootTable) do
          qty = loot[3]
totalWt = totalWt + loot[2]
        end
if loot[1] == item.id and not Monsters._isDungeonOnlyMonster(monster) then
      end
wt = loot[2]
    end
qty = loot[3]
    local lootChance = monster.lootChance ~= nil and monster.bones ~= item.id and monster.lootChance or 100
end
end
end
local lootChance = monster.lootChance ~= nil and monster.bones ~= item.id and monster.lootChance or 100


if wt > 0 and lootChance > 0 then
if wt > 0 and lootChance > 0 then
  if not Shared.contains(item.name, 'Shard') and Monsters._isDungeonOnlyMonster(monster) then
-- Item drops when the monster is killed
-- For dungeon exclusive monsters, loot is only rolled when they are the last
table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
  -- monster within that dungeon (unless it is a shard)
end
if monster.isBoss then
end
  local areaList = Areas.getMonsterAreas(monster.id)
-- Is the item dropped from any dungeon?
  for k, area in ipairs(areaList) do
local dungeonList = Areas.getAreas(function(area) return area.type == 'dungeon' and type(area.rewards) == 'table' and Shared.contains(area.rewards, item.id) end)
            if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
if dungeonList ~= nil then
  table.insert(dropRows, {source = Icons.Icon({area.name, type='dungeon'}), type = '[[Dungeon]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
for i, dungeon in ipairs(dungeonList) do
    end
table.insert(dropRows, {source = Icons.Icon({dungeon.name, type='dungeon'}), type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
  end
end
    end
end
  else
-- Is the item dropped from a cycle of the Impending Darkness event?
        -- Item is not an end of dungeon reward, and drops when the monster is killed
for i, eventItemID in ipairs(Areas.eventData.rewards) do
table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
if item.id == eventItemID then
  end
sourceTxt = Icons.Icon({'Impending Darkness Event', type='dungeon'}) .. (i == Shared.tableCount(Areas.eventData.rewards) and '' or ', Cycle ' .. i)
table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
break
end
end
--Special exception for the Fire/Infernal Cape and first two lore books as bonus dungeon drops
if sourceOverrides['Dungeon'][item.id] ~= nil then
local sourceTxt = Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon'})
table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
end
end
  end
  -- Is the item dropped from a cycle of the Impending Darkness event?
  for i, eventItemID in ipairs(Areas.eventData.rewards) do
  if item.id == eventItemID then
    sourceTxt = Icons.Icon({'Impending Darkness Event', type='dungeon'}) .. (i == Shared.tableCount(Areas.eventData.rewards) and '' or ', Cycle ' .. i)
    table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
    break
  end
  end
  --Special exception for the Fire/Infernal Cape and first two lore books as bonus dungeon drops
  if sourceOverrides['Dungeon'][item.id] ~= nil then
    local sourceTxt = Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon'})
    table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
  end


  --Next: Can we find it by rummaging around in another item?
--Next: Can we find it by rummaging around in another item?
  for i, item2 in pairs(ItemData.Items) do
for i, item2 in ipairs(ItemData.Items) do
    if item2.dropTable ~= nil then
if item2.dropTable ~= nil then
      local qty = 1
local qty = 1
      local wt = 0
local wt = 0
      local totalWt = 0
local totalWt = 0
      for j, loot in pairs(item2.dropTable) do
for j, loot in pairs(item2.dropTable) do
        totalWt = totalWt + loot[2]
totalWt = totalWt + loot[2]
        if loot[1] == item.id then
if loot[1] == item.id then
          wt = loot[2]
wt = loot[2]
          if item2.dropQty ~= nil then qty = item2.dropQty[j] end
if item2.dropQty ~= nil then qty = item2.dropQty[j] end
        end
end
      end
end


      if wt > 0 then
if wt > 0 then
        local sourceTxt = Icons.Icon({item2.name, type='item'})
local sourceTxt = Icons.Icon({item2.name, type='item'})
        table.insert(dropRows, {source = sourceTxt, type = '[[Chest]]', minqty = 1, qty = qty, weight = wt, totalWeight = totalWt})
table.insert(dropRows, {source = sourceTxt, type = '[[Chest]]', minqty = 1, qty = qty, weight = wt, totalWeight = totalWt})
      end
end
    end
end
  end
end


  --Finally, let's try just stealing it
--Finally, let's try just stealing it
  local thiefType = Icons.Icon({"Thieving", type='skill'})
local thiefType = Icons.Icon({"Thieving", type='skill'})
  local thiefItems = GatheringSkills.getThievingSourcesForItem(item.id)
local thiefItems = Skills.getThievingSourcesForItem(item.id)
  for i, thiefRow in pairs(thiefItems) do
for i, thiefRow in ipairs(thiefItems) do
  local sourceTxt = ''
local sourceTxt = ''
    if thiefRow.npc == 'all' then
if thiefRow.npc == 'all' then
    sourceTxt = "Thieving Rare Drop"
sourceTxt = "Thieving Rare Drop"
    else
else
    sourceTxt = Icons.Icon({thiefRow.npc, type='thieving'})
sourceTxt = Icons.Icon({thiefRow.npc, type='thieving'})
end
table.insert(dropRows, {source = sourceTxt, type = thiefType, minqty = thiefRow.minQty, qty = thiefRow.maxQty, weight = thiefRow.wt, totalWeight = thiefRow.totalWt})
end
end
    table.insert(dropRows, {source = sourceTxt, type = thiefType, minqty = thiefRow.minQty, qty = thiefRow.maxQty, weight = thiefRow.wt, totalWeight = thiefRow.totalWt})
  end


  --Bonus overtime: Special Fishing table & mining gem table. Also Rags to Riches
--Bonus overtime: Special Fishing table & mining gem table. Also Rags to Riches
  --Jadestone is special and doesn't count
if Shared.contains(SkillData.Fishing.JunkItems, item.id) then
  if item.type == 'Gem' and item.name ~= 'Jadestone' then
local fishSource = '[[Fishing#Junk|Junk]]'
    local mineType = Icons.Icon({'Mining', type='skill'})
local fishType = Icons.Icon({'Fishing', type='skill'})
    local thisGemChance = Items.GemTable[item.name].chance
local fishTotWeight = Shared.tableCount(SkillData.Fishing.JunkItems)
    local totalGemChance = 0
table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = 1, totalWeight = fishTotWeight})
    for i, gem in pairs(Items.GemTable) do
else
      totalGemChance = totalGemChance + gem.chance
local fishTotWeight, fishItem = 0, nil
    end
for i, specialItem in ipairs(SkillData.Fishing.SpecialItems) do
    table.insert(dropRows, {source = '[[Mining#Gems|Gem]]', type = mineType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
if specialItem[1] == item.id then
    local magicType = Icons.Icon({'Magic', type = 'skill'})
fishItem = specialItem
    table.insert(dropRows, {source = Icons.Icon({"Rags to Riches I", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
end
    table.insert(dropRows, {source = Icons.Icon({"Rags to Riches II", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
fishTotWeight = fishTotWeight + specialItem[2]
  end
end
if fishItem ~= nil then
local fishSource = '[[Fishing#Special|Special]]'
local fishType = Icons.Icon({'Fishing', type='skill'})
table.insert(dropRows, {source = fishSource, type = fishType, minqty = fishItem[3], qty = fishItem[3], weight = fishItem[2], totalWeight = fishTotWeight})
end
end
--Jadestone is special and doesn't count
if item.type == 'Gem' and item.name ~= 'Jadestone' then
local mineType = Icons.Icon({'Mining', type='skill'})
local thisGemChance = Items.GemTable[item.name].chance
local totalGemChance = 0
for i, gem in pairs(Items.GemTable) do
totalGemChance = totalGemChance + gem.chance
end
table.insert(dropRows, {source = '[[Mining#Gems|Gem]]', type = mineType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
local magicType = Icons.Icon({'Magic', type = 'skill'})
for i, altSpell in ipairs(MagicData.AltMagic) do
if type(altSpell.produces) == 'number' and altSpell.produces == -3 then
table.insert(dropRows, {source = Icons.Icon({altSpell.name, type='spell'}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
end
end
end


  if item.fishingCatchWeight ~= nil then
--Make sure to return nothing if there are no drop sources
    local fishSource = '[[Fishing#Special|Special]]'
if Shared.tableCount(dropRows) == 0 then return '' end
    local fishType = Icons.Icon({'Fishing', type='skill'})
    table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = item.fishingCatchWeight, totalWeight = Items.specialFishWt})
  end


  if item.type == 'Junk' then
table.sort(dropRows, function(a, b)
    local fishSource = '[[Fishing#Junk|Junk]]'
if a.weight / a.totalWeight == b.weight / b.totalWeight then
    local fishType = Icons.Icon({'Fishing', type='skill'})
if a.minqty + a.qty == b.minqty + b.qty then
    table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = 1, totalWeight = Items.junkCount})
return (a.type == b.type and a.source < b.source) or a.type < b.type
  end
else
 
return a.minqty + a.qty > b.minqty + b.qty
  --Make sure to return nothing if there are no drop sources
end
  if Shared.tableCount(dropRows) == 0 then return '' end
else
 
return a.weight / a.totalWeight > b.weight / b.totalWeight
  table.sort(dropRows, function(a, b)
end
                        if a.weight / a.totalWeight == b.weight / b.totalWeight then
end)
                          return a.minqty + a.qty > b.minqty + b.qty
for i, data in pairs(dropRows) do
                        else
table.insert(resultPart, buildRow(data.source, data.type, data.minqty, data.qty, data.weight, data.totalWeight))
                          return a.weight / a.totalWeight > b.weight / b.totalWeight
end
                        end
                      end)
  for i, data in pairs(dropRows) do
    table.insert(resultPart, buildRow(data.source, data.type, data.minqty, data.qty, data.weight, data.totalWeight))
  end


  table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
  return table.concat(resultPart)
return table.concat(resultPart)
end
end


function p.getItemLootSourceTable(frame)
function p.getItemLootSourceTable(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[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getItemLootSourceTable(item)
return p._getItemLootSourceTable(item)
end
end


function p._getItemUpgradeTable(item)
function p._getItemUpgradeTable(item)
  local resultPart = {}
local resultPart = {}
  if item.itemsRequired ~= nil then
if item.itemsRequired ~= nil then
    --First, get details on all the required materials
--First, get details on all the required materials
    local upgradeFrom = {}
local upgradeFrom = {}
    local materials = {}
local materials = {}
    for i, row in pairs(item.itemsRequired) do
for i, row in pairs(item.itemsRequired) do
      local mat = Items.getItemByID(row[1])
local mat = Items.getItemByID(row[1])
      --Check to see if the source item can trigger the upgrade
--Check to see if the source item can trigger the upgrade
      if mat.canUpgrade or (mat.type == 'Armour' and mat.canUpgrade == nil) then
if mat.canUpgrade or (mat.type == 'Armour' and mat.canUpgrade == nil) then
        table.insert(upgradeFrom, Icons.Icon({mat.name, type='item'}))
table.insert(upgradeFrom, Icons.Icon({mat.name, type='item'}))
      end
end
      table.insert(materials, Icons.Icon({mat.name, type='item', qty=row[2]}))
table.insert(materials, Icons.Icon({mat.name, type='item', qty=row[2]}))
    end
end
    if item.trimmedGPCost ~= nil then
if type(item.trimmedGPCost) == 'number' and item.trimmedGPCost > 0 then
      table.insert(materials, Icons.GP(item.trimmedGPCost))
table.insert(materials, Icons.GP(item.trimmedGPCost))
    end
end
    table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Upgrading Items|Item Upgrade]]')
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Upgrading Items|Item Upgrade]]')
    --[[result = result..'\r\n|-\r\n!style="text-align:right;"|Upgrades From\r\n|'
--[[result = result..'\r\n|-\r\n!style="text-align:right;"|Upgrades From\r\n|'
    result = result..table.concat(upgradeFrom, '<br/>')--]]
result = result..table.concat(upgradeFrom, '<br/>')--]]
    table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials\r\n|')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials\r\n|')
    table.insert(resultPart, table.concat(materials, '<br/>'))
table.insert(resultPart, table.concat(materials, '<br/>'))
    table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
  end
end
  return table.concat(resultPart)
return table.concat(resultPart)
end
end


function p.getItemUpgradeTable(frame)
function p.getItemUpgradeTable(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[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getItemUpgradeTable(item)
return p._getItemUpgradeTable(item)
end
end


function p._getItemSuperheatTable(item)
function p._getItemSuperheatTable(item)
  --Manually build the Superheat Item table
--Manually build the Superheat Item table
  local oreString = ''
-- Validate that the item can be superheated
  local coalString = ''
local canSuperheat, smithRecipe = true, nil
  for i, mat in pairs(item.smithReq) do
if type(item.masteryID) ~= 'table' then
    local thisMat = Items.getItemByID(mat.id)
canSuperheat = false
    if thisMat.name == 'Coal Ore' then
elseif item.masteryID[1] ~= SkillEnum.Smithing then
      coalString = ', '..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
canSuperheat = false
    else
else
      if string.len(oreString) > 0 then oreString = oreString..', ' end
smithRecipe = SkillData.Smithing.Recipes[item.masteryID[2] + 1]
      oreString =  oreString..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
if smithRecipe == nil or smithRecipe.category ~= 0 then
    end
canSuperheat = false
  end
end
  --Set up the header
end
  local superheatTable = {}
if not canSuperheat then
  table.insert(superheatTable, '{|class="wikitable"\r\n!colspan="2"|Spell')
return 'ERROR: The item "' .. item.name .. '" cannot be superheated[[Category:Pages with script errors]]'
  table.insert(superheatTable, '!!'..Icons.Icon({'Smithing', type='skill', notext='true'})..' Level')
end
  table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level')
 
  table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' XP')
local oreStringPart, coalString = {}, ''
  table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
for i, mat in ipairs(smithRecipe.itemCosts) do
  table.insert(superheatTable, '!!Ore!!Runes')
local thisMat = Items.getItemByID(mat.id)
  --Loop through all the variants
if thisMat.name == 'Coal Ore' then
  local spellNames = {'Superheat I', 'Superheat II', 'Superheat III', 'Superheat IV'}
coalString = Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
  for i, sName in pairs(spellNames) do
else
    local spell = Magic.getSpell(sName, 'AltMagic')
table.insert(oreStringPart, Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty}))
    local rowPart = {}
end
    table.insert(rowPart, '\r\n|-\r\n|'..Icons.Icon({spell.name, type='spell', notext=true, size=50}))
end
    table.insert(rowPart, '||'..Icons.Icon({spell.name, type='spell', noicon=true})..'||style="text-align:right;"|'..item.smithingLevel)
--Set up the header
    table.insert(rowPart, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.magicXP..'||style="text-align:right;"|'..spell.convertToQty)
local superheatTable = {}
    table.insert(rowPart, '||'..oreString)
table.insert(superheatTable, '{|class="wikitable"\r\n!colspan="2"|Spell')
    if spell.ignoreCoal ~= nil and not spell.ignoreCoal then table.insert(rowPart, coalString) end
table.insert(superheatTable, '!!'..Icons.Icon({'Smithing', type='skill', notext='true'})..' Level')
    table.insert(rowPart, '||style="text-align:center"|')
table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level')
    for i, req in pairs(spell.runesRequired) do
table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' XP')
      local rune = Items.getItemByID(req.id)
table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
      if i > 1 then table.insert(rowPart, ', ') end
table.insert(superheatTable, '!!Ore!!Runes')
      table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
--Loop through all the variants
    end
local spellNames = {'Superheat I', 'Superheat II', 'Superheat III', 'Superheat IV'}
    table.insert(rowPart, "<br/>'''OR'''<br/>")
for i, sName in pairs(spellNames) do
    for i, req in pairs(spell.runesRequiredAlt) do
local spell = Magic.getSpell(sName, 'AltMagic')
      local rune = Items.getItemByID(req.id)
table.insert(superheatTable, '\r\n|-\r\n|'..Icons.Icon({spell.name, type='spell', notext=true, size=50}))
      if i > 1 then table.insert(rowPart, ', ') end
table.insert(superheatTable, '||'..Icons.Icon({spell.name, type='spell', noicon=true})..'||style="text-align:right;"|'..smithRecipe.level)
      table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
table.insert(superheatTable, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.baseExperience)
    end
table.insert(superheatTable, '||style="text-align:right;"|'..spell.productionRatio)
    table.insert(superheatTable, table.concat(rowPart))
table.insert(superheatTable, '|| '..table.concat(oreStringPart, ', '))
  end
if spell.consumes == 2 and coalString ~= '' then
  --Add the table end and add the table to the result string
-- 2 = Superheat with coal, 3 = Superheat without coal
  table.insert(superheatTable, '\r\n|}')
table.insert(superheatTable, (Shared.tableCount(oreStringPart) > 0 and ', ' or '') .. coalString)
  return table.concat(superheatTable)
end
table.insert(superheatTable, '||style="text-align:center"| ' .. Magic._getSpellRunes(spell))
end
--Add the table end and add the table to the result string
table.insert(superheatTable, '\r\n|}')
return table.concat(superheatTable)
end
end


function p.getItemSuperheatTable(frame)
function p.getItemSuperheatTable(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[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getItemSuperheatTable(item)
return p._getItemSuperheatTable(item)
end
end


function p._getItemSourceTables(item)
function p._getItemSourceTables(item)
  local resultPart = {}
local resultPart = {}
  local shopTable = Shop._getItemShopTable(item)
local shopTable = Shop._getItemShopTable(item)
  if string.len(shopTable) > 0 then
if string.len(shopTable) > 0 then
    table.insert(resultPart, '===Shop===\r\n'..shopTable)
table.insert(resultPart, '===Shop===\r\n'..shopTable)
  end
end


  local creationTable = p._getCreationTable(item)
local creationTable = p._getCreationTable(item)
  if string.len(creationTable) > 0 then
if string.len(creationTable) > 0 then
    if #resultPart > 0 then table.insert(resultPart, '\r\n') end
if #resultPart > 0 then table.insert(resultPart, '\r\n') end
    table.insert(resultPart, '===Creation===\r\n'..creationTable)
table.insert(resultPart, '===Creation===\r\n'..creationTable)
  end
end


  local upgradeTable = p._getItemUpgradeTable(item)
local upgradeTable = p._getItemUpgradeTable(item)
  if string.len(upgradeTable) > 0 then
if string.len(upgradeTable) > 0 then
    if #resultPart > 0 then table.insert(resultPart, '\r\n') end
if #resultPart > 0 then table.insert(resultPart, '\r\n') end
    if string.len(creationTable) == 0 then table.insert(resultPart, '===Creation===\r\n') end
if string.len(creationTable) == 0 then table.insert(resultPart, '===Creation===\r\n') end
    table.insert(resultPart, upgradeTable)
table.insert(resultPart, upgradeTable)
  end
end


  if item.type == 'Bar' then
if type(item.masteryID) == 'table' and item.masteryID[1] == SkillEnum.Smithing then
    table.insert(resultPart, '\r\n==='..Icons.Icon({'Alt Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item))
local recipe = SkillData.Smithing.Recipes[item.masteryID[2] + 1]
  end
if recipe ~= nil and recipe.category == 0 then
table.insert(resultPart, '\r\n==='..Icons.Icon({'Alt. Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item))
end
end


  local lootTable = p._getItemLootSourceTable(item)
local lootTable = p._getItemLootSourceTable(item)
  if string.len(lootTable) > 0 then
if string.len(lootTable) > 0 then
    if #resultPart > 0 then table.insert(resultPart, '\r\n') end
if #resultPart > 0 then table.insert(resultPart, '\r\n') end
    table.insert(resultPart, '===Loot===\r\n'..lootTable)
table.insert(resultPart, '===Loot===\r\n'..lootTable)
  end
end
  return table.concat(resultPart)
return table.concat(resultPart)
end
end


function p.getItemSourceTables(frame)
function p.getItemSourceTables(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[[Category:Pages with script errors]]"
return "ERROR: No item named "..itemName.." exists in the data module[[Category:Pages with script errors]]"
  end
end


  return p._getItemSourceTables(item)
return p._getItemSourceTables(item)
end
end


function p.getCombatPassiveSlotItems(frame)
function p.getCombatPassiveSlotItems(frame)
  local resultPart = {}
local resultPart = {}
  table.insert(resultPart, '{| class="wikitable"\r\n')
table.insert(resultPart, '{| class="wikitable"\r\n')
  table.insert(resultPart, '|-\r\n')
table.insert(resultPart, '|-\r\n')
  table.insert(resultPart, '!colspan="2"|Item\r\n! Passive\r\n')
table.insert(resultPart, '!colspan="2"|Item\r\n! Passive\r\n')
 
local itemArray = Items.getItems(function(item) return item.validSlots ~= nil and Shared.contains(item.validSlots, 'Passive') end)
 
table.sort(itemArray, function(a, b) return a.id < b.id end)
 
for i, item in ipairs(itemArray) do
table.insert(resultPart, '|-\r\n')
table.insert(resultPart, '! '..Icons.Icon({item.name, type='item', notext='true'})..'\r\n! '..Icons.Icon({item.name, type='item', noicon=true})..'\r\n')
table.insert(resultPart, '| '..item.description..'\r\n')
end


  local itemArray = Items.getItems(function(item) return item.validSlots ~= nil and Shared.contains(item.validSlots, 'Passive') end)
table.insert(resultPart, '|}')


  table.sort(itemArray, function(a, b) return a.id < b.id end)
return table.concat(resultPart)
end


  for i, item in ipairs(itemArray) do
function p._getItemMonsterSources(item)
    table.insert(resultPart, '|-\r\n')
local resultArray = {}
    table.insert(resultPart, '! '..Icons.Icon({item.name, type='item', notext='true'})..'\r\n! '..Icons.Icon({item.name, type='item', noicon=true})..'\r\n')
for i, monster in ipairs(MonsterData.Monsters) do
    table.insert(resultPart, '| '..item.description..'\r\n')
local chance = 0
  end
local weight = 0
local minQty = 1
local maxQty = 1
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
-- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
chance = 1
weight = 1
if monster.boneQty ~= nil then
minQty = monster.boneQty
maxQty = monster.boneQty
end
elseif monster.lootTable ~= nil then
-- If the monster has a loot table, check if the item we are looking for is in there
-- Dungeon exclusive monsters don't count as they are either:
--  - A monster before the boss, which doesn't drop anything except shards (checked above)
--  - A boss monster, whose drops are accounted for in data from Areas instead
for j, loot in ipairs(monster.lootTable) do
weight = weight + loot[2]
if loot[1] == item.id and not Monsters._isDungeonOnlyMonster(monster) then
chance = loot[2]
maxQty = loot[3]
end
end
local lootChance = monster.lootChance ~= nil and monster.lootChance or 100
chance = chance * lootChance
weight = weight * 100
chance, weight = Shared.fractionpair(chance, weight)
end
if chance > 0 then
-- Item drops when the monster is killed
table.insert(resultArray, {id = monster.id, dropWt = chance, totalWt = weight, minQty = minQty, maxQty = maxQty})
end
end
return resultArray
end


  table.insert(resultPart, '|}')
function p.getItemMonsterSources(itemName)
local item = Items.getItem(itemName)
return p._getItemMonsterSources(item)
end


  return table.concat(resultPart)
--[==[
-- 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',
'Yew Longbow',
'Water Rune',
'Steam Rune',
'Controlled Heat Potion II',
'Wolf',
'Cyclops',
'Leprechaun',
'Redwood Logs',
'Carrot Cake',
'Carrot Cake (Perfect)',
'Mantalyme Herb',
'Carrot',
'Topaz',
'Rune Essence',
'Sanguine Blade',
'Ring of Power',
'Infernal Claw',
'Chapeau Noir',
'Stardust',
'Rope',
'Ancient Ring of Mastery',
'Mysterious Stone',
'Mastery Token (Cooking)',
'Gem Gloves',
"Thief's Moneysack"
}
local checkFuncs = {
p.getItemSourceTables,
--p.getCreationTable,
p.getItemSources,
--p.getItemLootSourceTable,
}
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