Module:Items/SourceTables: Difference between revisions

From Melvor Idle
m (Fix logic error)
(Updated indenting)
Line 18: Line 18:
-- 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
[361] = 'Volcanic Cave', -- Fire Cape
  [941] = 'Infernal Stronghold', -- Infernal Cape
[941] = 'Infernal Stronghold', -- Infernal Cape
    [950] = 'Volcanic Cave', -- A Tale of the Past, a future's prophecy
[950] = 'Volcanic Cave', -- A Tale of the Past, a future's prophecy
    [951] = 'Fire God Dungeon', -- The First Hero and an Unknown Evil
[951] = 'Fire God Dungeon', -- The First Hero and an Unknown Evil
    [1116] = 'Into the Mist' -- Beginning of the End
[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 item.smithingLevel ~= nil then
    skill = 'Smithing'
skill = 'Smithing'
    lvl = item.smithingLevel
lvl = item.smithingLevel
    xp = item.smithingXP
xp = item.smithingXP
    req = item.smithReq
req = item.smithReq
    qty = item.smithingQty
qty = item.smithingQty
    time = 2
time = 2
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
end
  if item.craftingLevel ~= nil then
if item.craftingLevel ~= nil then
    skill = 'Crafting'
skill = 'Crafting'
    lvl = item.craftingLevel
lvl = item.craftingLevel
    xp = item.craftingXP
xp = item.craftingXP
    req = item.craftReq
req = item.craftReq
    qty = item.craftQty
qty = item.craftQty
    time = 3
time = 3
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, nil, item.craftGPCost))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, nil, item.craftGPCost))
  end
end
  if item.runecraftingLevel ~= nil then
if item.runecraftingLevel ~= nil then
    skill = 'Runecrafting'
skill = 'Runecrafting'
    lvl = item.runecraftingLevel
lvl = item.runecraftingLevel
    xp = item.runecraftingXP
xp = item.runecraftingXP
    req = item.runecraftReq
req = item.runecraftReq
    qty = item.runecraftQty
qty = item.runecraftQty
    time = 2
time = 2
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
end
  if item.fletchingLevel ~= nil then
if item.fletchingLevel ~= nil then
    skill = 'Fletching'
skill = 'Fletching'
    lvl = item.fletchingLevel
lvl = item.fletchingLevel
    xp = item.fletchingXP
xp = item.fletchingXP
    req = item.fletchReq
req = item.fletchReq
    qty = item.fletchQty
qty = item.fletchQty
    time = 2
time = 2
    if item.name == 'Arrow Shafts' then
if item.name == 'Arrow Shafts' then
      --Arrow Shafts get special (weird) treatment
--Arrow Shafts get special (weird) treatment
      req = '1 of any [[Log]]'
req = '1 of any [[Log]]'
      qty = '15 - 135'
qty = '15 - 135'
    end
end
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
end
  if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
  for i, reqSet in pairs(item.recipeRequirements) do
for i, reqSet in pairs(item.recipeRequirements) do
    skill = 'Cooking'
skill = 'Cooking'
    lvl = item.cookingLevel
lvl = item.cookingLevel
    xp = item.cookingXP
xp = item.cookingXP
    req = reqSet
req = reqSet
    qty = item.cookingQty
qty = item.cookingQty
    time = item.cookingInterval / 1000
time = item.cookingInterval / 1000
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
end
  end
end
  if item.herbloreReq ~= nil then
if item.herbloreReq ~= nil then
    skill = 'Herblore'
skill = 'Herblore'
    req = item.herbloreReq
req = item.herbloreReq
    --Currently using 'masteryID' as shorthand to find details, could be a better method
--Currently using 'masteryID' as shorthand to find details, could be a better method
    local potionID = item.masteryID[2]
local potionID = item.masteryID[2]
    local potionData = SkillData.Herblore.ItemData[potionID + 1]
local potionData = SkillData.Herblore.ItemData[potionID + 1]
    lvl = potionData.level
lvl = potionData.level
    xp = potionData.herbloreXP
xp = potionData.herbloreXP
    time = 2
time = 2
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
  end
end
  if item.masteryID ~= nil and item.masteryID[1] == 4 then
if item.masteryID ~= nil and item.masteryID[1] == 4 then
    skill = 'Mining'
skill = 'Mining'
    lvl = SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired
lvl = SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired
    time = 3
time = 3
    xp = item.miningXP
xp = item.miningXP
    --Rune Essence has double quantity, but that's a hard-coded thing in the game so it's hard-coded here
--Rune Essence has double quantity, but that's a hard-coded thing in the game so it's hard-coded here
    if item.name == 'Rune Essence' then qty = 2 else qty = 1 end
if item.name == 'Rune Essence' then qty = 2 else qty = 1 end


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


      --If this is a perfect item, need to find the original
--If this is a perfect item, need to find the original
      if item2.perfectItem == item.id and item2.recipeRequirements ~= nil then
if item2.perfectItem == item.id and item2.recipeRequirements ~= nil then
  for j, reqSet in pairs(item2.recipeRequirements) do
for j, reqSet in pairs(item2.recipeRequirements) do
      skill = 'Cooking'
skill = 'Cooking'
    lvl = item2.cookingLevel
lvl = item2.cookingLevel
    xp = item2.cookingXP
xp = item2.cookingXP
    req = reqSet
req = reqSet
    qty = item2.cookingQty
qty = item2.cookingQty
    time = item2.cookingInterval / 1000
time = item2.cookingInterval / 1000
    table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
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
end
  end
req = req..table.concat(OtherCostArray, "<br/>'''OR''' ")
    end
qty = item.summoningQty
  end
time = 5
  if item.summoningLevel ~= nil then
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
    skill = 'Summoning'
end
    lvl = item.summoningLevel
--A couple special exceptions for Alt Magic
    --Summoning uses a formula to calculate XP for creation instead of referring to the item's XP data directly
--Not Gems or Bars though since those have their own separate thing
    xp = (5 + 2 * math.floor(item.summoningLevel / 5))
if item.name == 'Rune Essence' then
    local ShardCostArray = {}
table.insert(tables, p.buildAltMagicTable('Just Learning'))
    for j, cost in Shared.skpairs(item.summoningReq[1]) do
elseif item.name == 'Bones' then
      if cost.id >= 0 then
table.insert(tables, p.buildAltMagicTable('Bone Offering'))
        local item = Items.getItemByID(cost.id)
elseif item.name == 'Holy Dust' then
        if item.type == 'Shard' then
table.insert(tables, p.buildAltMagicTable('Blessed Offering'))
          table.insert(ShardCostArray, Icons.Icon({item.name,  type='item', notext=true, qty=cost.qty}))
end
        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
end


function p.buildAltMagicTable(spellName)
function p.buildAltMagicTable(spellName)
  local spell = Magic.getSpell(spellName, 'AltMagic')
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
-- 1 means select any item. 0 would mean Superheat, but that's handled elsewhere
  -- -1 means no item is needed, so hide this section
-- -1 means no item is needed, so hide this section
  if spell.selectItem == 1 then
if spell.selectItem == 1 then
    table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
    table.insert(resultPart, '\r\n|1 of any item')
table.insert(resultPart, '\r\n|1 of any item')
  end
end
  --Add runes
--Add runes
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Runes\r\n|')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Runes\r\n|')
  for i, req in pairs(spell.runesRequired) do
for i, req in pairs(spell.runesRequired) do
    local rune = Items.getItemByID(req.id)
local rune = Items.getItemByID(req.id)
    if i > 1 then table.insert(resultPart, ', ') end
if i > 1 then table.insert(resultPart, ', ') end
    table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
  end
end
  if spell.runesRequiredAlt ~= nil and Shared.tableCount(spell.runesRequired) ~= Shared.tableCount(spell.runesRequiredAlt) then
if spell.runesRequiredAlt ~= nil and Shared.tableCount(spell.runesRequired) ~= Shared.tableCount(spell.runesRequiredAlt) then
    table.insert(resultPart, "<br/>'''OR'''<br/>")
table.insert(resultPart, "<br/>'''OR'''<br/>")
    for i, req in pairs(spell.runesRequiredAlt) do
for i, req in pairs(spell.runesRequiredAlt) do
      local rune = Items.getItemByID(req.id)
local rune = Items.getItemByID(req.id)
      if i > 1 then table.insert(resultPart, ', ') end
if i > 1 then table.insert(resultPart, ', ') end
      table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
    end
end
  end
end


  --Now just need the output quantity, xp, and casting time (which is always 2)
--Now just need the output quantity, xp, and casting time (which is always 2)
  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;"|Base Quantity\r\n|'..spell.convertToQty)
  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;"|Base XP\r\n|'..spell.magicXP)
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Cast Time\r\n|2s')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Cast Time\r\n|2s')
  table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
  return table.concat(resultPart)
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)
  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
else
    table.insert(resultPart, req)
table.insert(resultPart, req)
    end
end
  end
end
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity')
  table.insert(resultPart, '\r\n|'..qty)
table.insert(resultPart, '\r\n|'..qty)
  table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Experience')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Experience')
  table.insert(resultPart, '\r\n|'..Shared.formatnum(xp)..' XP')
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|-\r\n!style="text-align:right;"|Base Creation Time')
  table.insert(resultPart, '\r\n|'..Shared.formatnum(Shared.round(time, 2, 0))..'s')
table.insert(resultPart, '\r\n|'..Shared.formatnum(Shared.round(time, 2, 0))..'s')
  if maxTime ~= nil then table.insert(resultPart, ' - '..Shared.formatnum(Shared.round(maxTime, 2, 0))..'s') end
if maxTime ~= nil then table.insert(resultPart, ' - '..Shared.formatnum(Shared.round(maxTime, 2, 0))..'s') end
  table.insert(resultPart, '\r\n|}')
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 = {}
local dungeonStrPart = {}
  for i, monster in ipairs(MonsterData.Monsters) do
for i, monster in ipairs(MonsterData.Monsters) do
  local isDrop, isBones = false, 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
  isBones = true
isBones = true
      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
for j, loot in ipairs(monster.lootTable) do
              if loot[1] == item.id then
    if loot[1] == item.id then
            isDrop = true
  isDrop = true
            break
  break
            end
        end
      end
  if isDrop then
  if not isBones and Monsters._isDungeonOnlyMonster(monster) then
  -- For dungeon exclusive monsters, loot is only rolled when they are the last
  -- monster within that dungeon (unless it is a shard)
  if monster.isBoss then
  local areaList = Areas.getMonsterAreas(monster.id)
  for k, area in ipairs(areaList) do
  if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
  table.insert(dungeonStrPart, Icons.Icon({area.name, type='dungeon', notext=true}))
  end
  end
  end
end
  end
end
  else
if isDrop then
  -- Item is not an end of dungeon reward, and drops when the monster is killed
if not isBones and Monsters._isDungeonOnlyMonster(monster) then
  table.insert(killStrPart, Icons.Icon({monster.name, type='monster', notext=true}))
-- For dungeon exclusive monsters, loot is only rolled when they are the last
  end
-- 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 a cycle of the Impending Darkness event?
for k, area in ipairs(areaList) do
  for i, eventItemID in ipairs(Areas.eventData.rewards) do
if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
      if item.id == eventItemID then
table.insert(dungeonStrPart, Icons.Icon({area.name, type='dungeon', notext=true}))
  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}))
end
  break
end
  end
else
  end
-- Item is not an end of dungeon reward, and drops when the monster is killed
  -- Special exceptions for Fire/Infernal Cape and first two lore books
table.insert(killStrPart, Icons.Icon({monster.name, type='monster', notext=true}))
  if sourceOverrides['Dungeon'][item.id] ~= nil then
end
    table.insert(dungeonStrPart, Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon', notext=true}))
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
local dungPrefix = (i == Shared.tableCount(Areas.eventData.rewards) and '' or i .. ' ' .. (i == 1 and 'cycle' or 'cycles') .. ' of ')
table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({'Impending Darkness Event', type='dungeon', notext=true}))
break
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, originals (for perfect items), and growing
  local lootStr = ''
local lootStr = ''
  local upgradeStr = ''
local upgradeStr = ''
  local cookStr = ''
local cookStr = ''
  local growStr = ''
local growStr = ''
  local count1 = 0
local count1 = 0
  local count2 = 0
local count2 = 0
  for i, item2 in pairs(ItemData.Items) do
for i, item2 in pairs(ItemData.Items) do
    if item2.dropTable ~= nil then
if item2.dropTable ~= nil then
      for j, loot in pairs(item2.dropTable) do
for j, loot in pairs(item2.dropTable) do
        if loot[1] == item.id then
if loot[1] == item.id then
          count1 = count1 + 1
count1 = count1 + 1
          if string.len(lootStr) > 0 then
if string.len(lootStr) > 0 then
            lootStr = lootStr..','
  lootStr = lootStr..','
            --if count1 % 3 == 1 and count1 > 1 then lootStr = lootStr..'<br/>' end
  --if count1 % 3 == 1 and count1 > 1 then lootStr = lootStr..'<br/>' end
            lootStr = lootStr..Icons.Icon({item2.name, type="item", notext="true"})
  lootStr = lootStr..Icons.Icon({item2.name, type="item", notext="true"})
          else
else
            lootStr = lootStr..'Opening: '..Icons.Icon({item2.name, type="item", notext="true"})
  lootStr = lootStr..'Opening: '..Icons.Icon({item2.name, type="item", notext="true"})
          end
end
        end
end
      end
end
    end
end
    if item2.trimmedItemID == item.id then
if item2.trimmedItemID == item.id then
          count2 = count2 + 1
count2 = count2 + 1
        if string.len(upgradeStr) > 0 then
if string.len(upgradeStr) > 0 then
          upgradeStr = upgradeStr..','
upgradeStr = upgradeStr..','
          --if count2 % 3 == 1 and count2 > 1 then upgradeStr = upgradeStr..'<br/>' end
--if count2 % 3 == 1 and count2 > 1 then upgradeStr = upgradeStr..'<br/>' end
          upgradeStr = upgradeStr..Icons.Icon({item2.name, type="item", notext="true"})
upgradeStr = upgradeStr..Icons.Icon({item2.name, type="item", notext="true"})
        else
else
          table.insert(categoryArray, '[[Category:Upgraded Items]]')
table.insert(categoryArray, '[[Category:Upgraded Items]]')
          upgradeStr = upgradeStr..'Upgrading: '..Icons.Icon({item2.name, type="item", notext="true"})
upgradeStr = upgradeStr..'Upgrading: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
end
    end
end
    if item2.grownItemID == item.id then
if item2.grownItemID == item.id then
        if string.len(growStr) > 0 then
if string.len(growStr) > 0 then
          growStr = growStr..','..Icons.Icon({item2.name, type="item", notext="true"})
growStr = growStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
else
          table.insert(categoryArray, '[[Category:Harvestable Items]]')
table.insert(categoryArray, '[[Category:Harvestable Items]]')
          growStr = growStr..'Growing: '..Icons.Icon({item2.name, type="item", notext="true"})
growStr = growStr..'Growing: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
end
    end
end
    if item2.perfectItem == item.id and item2.cookingLevel ~= nil then
if item2.perfectItem == item.id and item2.cookingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq('Cooking', item2.cookingLevel))
table.insert(lineArray, Icons._SkillReq('Cooking', item2.cookingLevel))
    end
end
  end
end
  if string.len(lootStr) > 0 then table.insert(lineArray, lootStr) 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(upgradeStr) > 0 then table.insert(lineArray, upgradeStr) end
  if string.len(cookStr) > 0 then table.insert(lineArray, cookStr) end
if string.len(cookStr) > 0 then table.insert(lineArray, cookStr) end
  if string.len(growStr) > 0 then table.insert(lineArray, growStr) 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 = GatheringSkills.getThievingSourcesForItem(item.id)
  local thiefStr = ''
local thiefStr = ''
  if Shared.tableCount(thiefItems) > 0 then
if Shared.tableCount(thiefItems) > 0 then
  thiefStr = 'Pickpocketing: '
thiefStr = 'Pickpocketing: '
  for i, thiefRow in pairs(thiefItems) do
for i, thiefRow in pairs(thiefItems) do
  if thiefRow.npc == 'all' then
if thiefRow.npc == 'all' then
  --if 'any' 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'
  thiefStr = Icons._SkillReq('Thieving', 1)
thiefStr = Icons._SkillReq('Thieving', 1)
  else
else
  if i > 1 then thiefStr = thiefStr..', ' end
if i > 1 then thiefStr = thiefStr..', ' end
  thiefStr = thiefStr..Icons.Icon({thiefRow.npc, type='thieving', notext='true'})
thiefStr = thiefStr..Icons.Icon({thiefRow.npc, type='thieving', notext='true'})
  end
end
  end
end
  end
end
  if string.len(thiefStr) > 0 then table.insert(lineArray, thiefStr) end
if string.len(thiefStr) > 0 then table.insert(lineArray, thiefStr) end


  --If all else fails, I guess we should check if we can make it ourselves
--If all else fails, I guess we should check if we can make it ourselves
  --AstrologyCheck
--AstrologyCheck
  --(Just a brute force for now because only two items and I'm lazy)
--(Just a brute force for now because only two items and I'm lazy)
  if item.name == 'Stardust' or item.name == 'Golden Stardust' 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:
--SmithCheck:
  if item.smithingLevel ~= nil then
if item.smithingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
  end
end


  --CraftCheck:
--CraftCheck:
  if item.craftingLevel ~= nil then
if item.craftingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
  end
end


  --FletchCheck:
--FletchCheck:
  if item.fletchingLevel ~= nil then
if item.fletchingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
  end
end


  --RunecraftCheck:
--RunecraftCheck:
  if item.runecraftingLevel ~= nil then
if item.runecraftingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
  end
end


  --CookCheck
--CookCheck
  if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
  table.insert(lineArray, Icons._SkillReq('Cooking', item.cookingLevel))
table.insert(lineArray, Icons._SkillReq('Cooking', item.cookingLevel))
  end
end


  --MineCheck:
--MineCheck:
  if item.masteryID ~= nil and item.masteryID[1] == 4 then
if item.masteryID ~= nil and item.masteryID[1] == 4 then
    table.insert(lineArray, Icons._SkillReq("Mining", SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired))
table.insert(lineArray, Icons._SkillReq("Mining", SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired))
  end
end


  --FishCheck:
--FishCheck:
  if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
    table.insert(lineArray, Icons.Icon({"Fishing", type='skill', notext=true})..' [[Fishing#'..item.type..'|'..item.type..']]')
table.insert(lineArray, Icons.Icon({"Fishing", type='skill', notext=true})..' [[Fishing#'..item.type..'|'..item.type..']]')
  elseif item.fishingLevel ~= nil then
elseif item.fishingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Fishing", item.fishingLevel))
table.insert(lineArray, Icons._SkillReq("Fishing", item.fishingLevel))
  end
end


  --HerbCheck:
--HerbCheck:
  if item.masteryID ~= nil and item.masteryID[1] == 19 then
if item.masteryID ~= nil and item.masteryID[1] == 19 then
    local potionData = SkillData.Herblore.ItemData[item.masteryID[2] + 1].level
local potionData = SkillData.Herblore.ItemData[item.masteryID[2] + 1].level
    table.insert(lineArray, Icons._SkillReq("Herblore", potionData))
table.insert(lineArray, Icons._SkillReq("Herblore", potionData))
  end
end


  --WoodcuttingCheck:
--WoodcuttingCheck:
  if item.type == 'Logs' then
if item.type == 'Logs' then
    local treeData = SkillData.Woodcutting.Trees[item.id + 1]
local treeData = SkillData.Woodcutting.Trees[item.id + 1]
    local lvl = treeData.level
local lvl = treeData.level
    table.insert(lineArray, Icons._SkillReq("Woodcutting", lvl))
table.insert(lineArray, Icons._SkillReq("Woodcutting", lvl))
  end
end


  --SummoningCheck:
--SummoningCheck:
  if item.summoningLevel ~= nil then
if item.summoningLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Summoning", item.summoningLevel))
table.insert(lineArray, Icons._SkillReq("Summoning", item.summoningLevel))
  end
end


  --Finally there are some weird exceptions:
--Finally there are some weird exceptions:
  --Coal can be acquired via firemaking
--Coal can be acquired via firemaking
  if item.name == "Coal Ore" then
if item.name == "Coal Ore" then
    table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
  end
end


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


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


  --Rhaelyx pieces are also special
--Rhaelyx pieces are also special
  if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
    local rhaSkills = {
local rhaSkills = {
      Circlet = {'Woodcutting', 'Fishing', 'Mining', 'Thieving', 'Farming', 'Agility', 'Astrology'},
Circlet = {'Woodcutting', 'Fishing', 'Mining', 'Thieving', 'Farming', 'Agility', 'Astrology'},
      Jewel = {'Firemaking', 'Cooking', 'Smithing', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Summoning'}
Jewel = {'Firemaking', 'Cooking', 'Smithing', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Summoning'}
    }
}
    local rhaSkList = {}
local rhaSkList = {}
    if item.name == 'Circlet of Rhaelyx' then
if item.name == 'Circlet of Rhaelyx' then
      rhaSkList = rhaSkills.Circlet
rhaSkList = rhaSkills.Circlet
    elseif item.name == 'Jewel of Rhaelyx' then
elseif item.name == 'Jewel of Rhaelyx' then
      rhaSkList = rhaSkills.Jewel
rhaSkList = rhaSkills.Jewel
    elseif item.name == 'Mysterious Stone' then
elseif item.name == 'Mysterious Stone' then
      rhaSkList = rhaSkills.Jewel
rhaSkList = rhaSkills.Jewel
      for i, v in ipairs(rhaSkills.Circlet) do
for i, v in ipairs(rhaSkills.Circlet) do
        table.insert(rhaSkList, v)
table.insert(rhaSkList, v)
      end
end
    end
end


    local rhaStrPart = {}
local rhaStrPart = {}
    for i, skillName in ipairs(rhaSkList) do
for i, skillName in ipairs(rhaSkList) do
      table.insert(rhaStrPart, Icons.Icon({skillName, type='skill', notext=true}))
table.insert(rhaStrPart, Icons.Icon({skillName, type='skill', notext=true}))
    end
end
    local rhaStr = 'Any action in: ' .. table.concat(rhaStrPart, ', ')
local rhaStr = 'Any action in: ' .. table.concat(rhaStrPart, ', ')
    if item.name == 'Mysterious Stone' then rhaStr = rhaStr .. '<br/>after finding ' .. Icons.Icon({'Crown of Rhaelyx', type='item'}) end
if item.name == 'Mysterious Stone' then rhaStr = rhaStr .. '<br/>after finding ' .. Icons.Icon({'Crown of Rhaelyx', type='item'}) end
    table.insert(lineArray, rhaStr)
table.insert(lineArray, rhaStr)
  end
end


  --Tokens are from the appropriate skill
--Tokens are from the appropriate skill
  if item.isToken and item.skill ~= nil then
if item.isToken and item.skill ~= nil then
    table.insert(lineArray, Icons._SkillReq(Constants.getSkillName(item.skill), 1))
table.insert(lineArray, Icons._SkillReq(Constants.getSkillName(item.skill), 1))
  end
end


  --Shop items (including special items like gloves that aren't otherwise listed)
--Shop items (including special items like gloves that aren't otherwise listed)
  local shopSources = Shop.getItemSourceArray(item.id)
local shopSources = Shop.getItemSourceArray(item.id)
  if Shared.tableCount(shopSources) > 0 then
if Shared.tableCount(shopSources) > 0 then
    table.insert(lineArray, Icons.Icon({'Shop'}))
table.insert(lineArray, Icons.Icon({'Shop'}))
  end
end


  --Easter Eggs (manual list 'cause don't have a better way to do that)
--Easter Eggs (manual list 'cause don't have a better way to do that)
  if Shared.contains(Items.EasterEggs, item.name) then
if Shared.contains(Items.EasterEggs, item.name) then
    table.insert(lineArray, '[[Easter Eggs]]')
table.insert(lineArray, '[[Easter Eggs]]')
  end
end
  -- Event exclusive items (also a manual list)
-- Event exclusive items (also a manual list)
  if Shared.contains(Items.EventItems, item.name) then
if Shared.contains(Items.EventItems, item.name) then
  table.insert(lineArray, '[[Events]]')
table.insert(lineArray, '[[Events]]')
  end
end
 
  --Gold Topaz Ring drops from any action (when not wearing a Gold Topaz Ring)
--Gold Topaz Ring drops from any action (when not wearing a Gold Topaz Ring)
  --Also handling Signet Ring things here
--Also handling Signet Ring things here
  if item.name == 'Gold Topaz Ring' then
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, '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"})..')')
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
elseif item.name == 'Signet Ring Half (a)' then
table.insert(lineArray, 'Any non-combat action while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
table.insert(lineArray, 'Any non-combat action while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
  elseif item.name == 'Signet Ring Half (b)' then
elseif item.name == 'Signet Ring Half (b)' then
    table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
  end
end


  local resultPart = {}
local resultPart = {}
  if asList then
if asList then
    table.insert(resultPart, '* '..table.concat(lineArray, "\r\n* "))
table.insert(resultPart, '* '..table.concat(lineArray, "\r\n* "))
  else
else
    table.insert(resultPart, '<div style="max-width:180px;text-align:right">' .. table.concat(lineArray, "<br/>") .. '</div>')
table.insert(resultPart, '<div style="max-width:180px;text-align:right">' .. table.concat(lineArray, "<br/>") .. '</div>')
  end
end
  if addCategories then table.insert(resultPart, table.concat(categoryArray, '')) end
if addCategories then table.insert(resultPart, table.concat(categoryArray, '')) end
  return table.concat(resultPart)
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..'"|'..minqty)
    if qty ~= minqty then table.insert(rowPart, ' - '..qty) end
if qty ~= minqty then table.insert(rowPart, ' - '..qty) end
    local chance = Shared.round(weight / totalWeight * 100, 2, 2)
local chance = Shared.round(weight / totalWeight * 100, 2, 2)
    if weight >= totalWeight then
if weight >= totalWeight then
      -- Fraction would be 1/1, so only show the percentage
-- Fraction would be 1/1, so only show the percentage
      chance = 100
chance = 100
      table.insert(rowPart, '\r\n|colspan="2" ')
table.insert(rowPart, '\r\n|colspan="2" ')
    else
else
local fraction = Shared.fraction(weight, totalWeight)
local fraction = Shared.fraction(weight, totalWeight)
if Shared.contains(fraction, '%.') then
if Shared.contains(fraction, '%.') then
Line 638: Line 638:
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|')
table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="' .. chance .. '"| ' .. Shared.fraction(weight, totalWeight) .. '\r\n|')
    end
end
    end
end
    if weight == -1 then
if weight == -1 then
    --Weight of -1 means this is a weird row that has a variable percentage
--Weight of -1 means this is a weird row that has a variable percentage
    table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
    else
else
    table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chance .. '"|'..chance..'%')
table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chance .. '"|'..chance..'%')
    end
end
    return table.concat(rowPart)
return table.concat(rowPart)
  end
end
  local dropRows = {}
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
for j, loot in ipairs(monster.lootTable) 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]
          qty = loot[3]
qty = loot[3]
        end
end
      end
end
    end
end
    local lootChance = monster.lootChance ~= nil and monster.bones ~= item.id and monster.lootChance or 100
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
if not Shared.contains(item.name, 'Shard') and Monsters._isDungeonOnlyMonster(monster) then
-- For dungeon exclusive monsters, loot is only rolled when they are the last
-- For dungeon exclusive monsters, loot is only rolled when they are the last
  -- monster within that dungeon (unless it is a shard)
-- monster within that dungeon (unless it is a shard)
if monster.isBoss then
if monster.isBoss then
  local areaList = Areas.getMonsterAreas(monster.id)
local areaList = Areas.getMonsterAreas(monster.id)
  for k, area in ipairs(areaList) do
for k, area in ipairs(areaList) do
            if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
  if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
  table.insert(dropRows, {source = Icons.Icon({area.name, type='dungeon'}), type = '[[Dungeon]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
table.insert(dropRows, {source = Icons.Icon({area.name, type='dungeon'}), type = '[[Dungeon]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
    end
end
  end
end
    end
end
  else
else
        -- Item is not an end of dungeon reward, and drops when the monster is killed
-- Item is not an end of dungeon reward, and drops when the monster is killed
table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
  end
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
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 pairs(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 = GatheringSkills.getThievingSourcesForItem(item.id)
  for i, thiefRow in pairs(thiefItems) do
for i, thiefRow in pairs(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
--Jadestone is special and doesn't count
  if item.type == 'Gem' and item.name ~= 'Jadestone' then
if item.type == 'Gem' and item.name ~= 'Jadestone' then
    local mineType = Icons.Icon({'Mining', type='skill'})
local mineType = Icons.Icon({'Mining', type='skill'})
    local thisGemChance = Items.GemTable[item.name].chance
local thisGemChance = Items.GemTable[item.name].chance
    local totalGemChance = 0
local totalGemChance = 0
    for i, gem in pairs(Items.GemTable) do
for i, gem in pairs(Items.GemTable) do
      totalGemChance = totalGemChance + gem.chance
totalGemChance = totalGemChance + gem.chance
    end
end
    table.insert(dropRows, {source = '[[Mining#Gems|Gem]]', type = mineType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
table.insert(dropRows, {source = '[[Mining#Gems|Gem]]', type = mineType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
    local magicType = Icons.Icon({'Magic', type = 'skill'})
local magicType = Icons.Icon({'Magic', type = 'skill'})
    table.insert(dropRows, {source = Icons.Icon({"Rags to Riches I", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
table.insert(dropRows, {source = Icons.Icon({"Rags to Riches I", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
    table.insert(dropRows, {source = Icons.Icon({"Rags to Riches II", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
table.insert(dropRows, {source = Icons.Icon({"Rags to Riches II", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
  end
end


  if item.fishingCatchWeight ~= nil then
if item.fishingCatchWeight ~= nil then
    local fishSource = '[[Fishing#Special|Special]]'
local fishSource = '[[Fishing#Special|Special]]'
    local fishType = Icons.Icon({'Fishing', type='skill'})
local fishType = Icons.Icon({'Fishing', type='skill'})
    table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = item.fishingCatchWeight, totalWeight = Items.specialFishWt})
table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = item.fishingCatchWeight, totalWeight = Items.specialFishWt})
  end
end


  if item.type == 'Junk' then
if item.type == 'Junk' then
    local fishSource = '[[Fishing#Junk|Junk]]'
local fishSource = '[[Fishing#Junk|Junk]]'
    local fishType = Icons.Icon({'Fishing', type='skill'})
local fishType = Icons.Icon({'Fishing', type='skill'})
    table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = 1, totalWeight = Items.junkCount})
table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = 1, totalWeight = Items.junkCount})
  end
end


  --Make sure to return nothing if there are no drop sources
--Make sure to return nothing if there are no drop sources
  if Shared.tableCount(dropRows) == 0 then return '' end
if Shared.tableCount(dropRows) == 0 then return '' end


  table.sort(dropRows, function(a, b)
table.sort(dropRows, function(a, b)
                        if a.weight / a.totalWeight == b.weight / b.totalWeight then
              if a.weight / a.totalWeight == b.weight / b.totalWeight then
                          return a.minqty + a.qty > b.minqty + b.qty
                return a.minqty + a.qty > b.minqty + b.qty
                        else
              else
                          return a.weight / a.totalWeight > b.weight / b.totalWeight
                return a.weight / a.totalWeight > b.weight / b.totalWeight
                        end
              end
                      end)
            end)
  for i, data in pairs(dropRows) do
for i, data in pairs(dropRows) do
    table.insert(resultPart, buildRow(data.source, data.type, data.minqty, data.qty, data.weight, data.totalWeight))
table.insert(resultPart, buildRow(data.source, data.type, data.minqty, data.qty, data.weight, data.totalWeight))
  end
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 item.trimmedGPCost ~= nil 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 = ''
local oreString = ''
  local coalString = ''
local coalString = ''
  for i, mat in pairs(item.smithReq) do
for i, mat in pairs(item.smithReq) do
    local thisMat = Items.getItemByID(mat.id)
local thisMat = Items.getItemByID(mat.id)
    if thisMat.name == 'Coal Ore' then
if thisMat.name == 'Coal Ore' then
      coalString = ', '..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
coalString = ', '..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
    else
else
      if string.len(oreString) > 0 then oreString = oreString..', ' end
if string.len(oreString) > 0 then oreString = oreString..', ' end
      oreString =  oreString..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
oreString =  oreString..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
    end
end
  end
end
  --Set up the header
--Set up the header
  local superheatTable = {}
local superheatTable = {}
  table.insert(superheatTable, '{|class="wikitable"\r\n!colspan="2"|Spell')
table.insert(superheatTable, '{|class="wikitable"\r\n!colspan="2"|Spell')
  table.insert(superheatTable, '!!'..Icons.Icon({'Smithing', type='skill', notext='true'})..' Level')
table.insert(superheatTable, '!!'..Icons.Icon({'Smithing', type='skill', notext='true'})..' Level')
  table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level')
table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level')
  table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' XP')
table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' XP')
  table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
  table.insert(superheatTable, '!!Ore!!Runes')
table.insert(superheatTable, '!!Ore!!Runes')
  --Loop through all the variants
--Loop through all the variants
  local spellNames = {'Superheat I', 'Superheat II', 'Superheat III', 'Superheat IV'}
local spellNames = {'Superheat I', 'Superheat II', 'Superheat III', 'Superheat IV'}
  for i, sName in pairs(spellNames) do
for i, sName in pairs(spellNames) do
    local spell = Magic.getSpell(sName, 'AltMagic')
local spell = Magic.getSpell(sName, 'AltMagic')
    local rowPart = {}
local rowPart = {}
    table.insert(rowPart, '\r\n|-\r\n|'..Icons.Icon({spell.name, type='spell', notext=true, size=50}))
table.insert(rowPart, '\r\n|-\r\n|'..Icons.Icon({spell.name, type='spell', notext=true, size=50}))
    table.insert(rowPart, '||'..Icons.Icon({spell.name, type='spell', noicon=true})..'||style="text-align:right;"|'..item.smithingLevel)
table.insert(rowPart, '||'..Icons.Icon({spell.name, type='spell', noicon=true})..'||style="text-align:right;"|'..item.smithingLevel)
    table.insert(rowPart, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.magicXP..'||style="text-align:right;"|'..spell.convertToQty)
table.insert(rowPart, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.magicXP..'||style="text-align:right;"|'..spell.convertToQty)
    table.insert(rowPart, '||'..oreString)
table.insert(rowPart, '||'..oreString)
    if spell.ignoreCoal ~= nil and not spell.ignoreCoal then table.insert(rowPart, coalString) end
if spell.ignoreCoal ~= nil and not spell.ignoreCoal then table.insert(rowPart, coalString) end
    table.insert(rowPart, '||style="text-align:center"|')
table.insert(rowPart, '||style="text-align:center"|')
    for i, req in pairs(spell.runesRequired) do
for i, req in pairs(spell.runesRequired) do
      local rune = Items.getItemByID(req.id)
local rune = Items.getItemByID(req.id)
      if i > 1 then table.insert(rowPart, ', ') end
if i > 1 then table.insert(rowPart, ', ') end
      table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
    end
end
    table.insert(rowPart, "<br/>'''OR'''<br/>")
table.insert(rowPart, "<br/>'''OR'''<br/>")
    for i, req in pairs(spell.runesRequiredAlt) do
for i, req in pairs(spell.runesRequiredAlt) do
      local rune = Items.getItemByID(req.id)
local rune = Items.getItemByID(req.id)
      if i > 1 then table.insert(rowPart, ', ') end
if i > 1 then table.insert(rowPart, ', ') end
      table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
    end
end
    table.insert(superheatTable, table.concat(rowPart))
table.insert(superheatTable, table.concat(rowPart))
  end
end
  --Add the table end and add the table to the result string
--Add the table end and add the table to the result string
  table.insert(superheatTable, '\r\n|}')
table.insert(superheatTable, '\r\n|}')
  return table.concat(superheatTable)
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 item.type == 'Bar' then
    table.insert(resultPart, '\r\n==='..Icons.Icon({'Alt Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item))
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)
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)
table.sort(itemArray, function(a, b) return a.id < b.id end)


  for i, item in ipairs(itemArray) do
for i, item in ipairs(itemArray) do
    table.insert(resultPart, '|-\r\n')
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, '! '..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')
table.insert(resultPart, '| '..item.description..'\r\n')
  end
end


  table.insert(resultPart, '|}')
table.insert(resultPart, '|}')


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


return p
return p

Revision as of 19:11, 8 December 2021

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

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 Shared = require('Module:Shared')
local Magic = require('Module:Magic')
local Areas = require('Module:CombatAreas')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Shop = require('Module:Shop')
local Monsters = require('Module:Monsters')
local GatheringSkills = require('Module:Skills/Gathering')

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

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

	local tables = {}
	--First figure out what skill is used to make this...
	if item.smithingLevel ~= nil then
		skill = 'Smithing'
		lvl = item.smithingLevel
		xp = item.smithingXP
		req = item.smithReq
		qty = item.smithingQty
		time = 2
		table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
	end
	if item.craftingLevel ~= nil then
		skill = 'Crafting'
		lvl = item.craftingLevel
		xp = item.craftingXP
		req = item.craftReq
		qty = item.craftQty
		time = 3
		table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, nil, item.craftGPCost))
	end
	if item.runecraftingLevel ~= nil then
		skill = 'Runecrafting'
		lvl = item.runecraftingLevel
		xp = item.runecraftingXP
		req = item.runecraftReq
		qty = item.runecraftQty
		time = 2
		table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
	end
	if item.fletchingLevel ~= nil then
		skill = 'Fletching'
		lvl = item.fletchingLevel
		xp = item.fletchingXP
		req = item.fletchReq
		qty = item.fletchQty
		time = 2
		if item.name == 'Arrow Shafts' then
			--Arrow Shafts get special (weird) treatment
			req = '1 of any [[Log]]'
			qty = '15 - 135'
		end
		table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
	end
	if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
		for i, reqSet in pairs(item.recipeRequirements) do
			skill = 'Cooking'
			lvl = item.cookingLevel
			xp = item.cookingXP
			req = reqSet
			qty = item.cookingQty
			time = item.cookingInterval / 1000
			table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
		end
	end
	if item.herbloreReq ~= nil then
		skill = 'Herblore'
		req = item.herbloreReq
		--Currently using 'masteryID' as shorthand to find details, could be a better method
		local potionID = item.masteryID[2]
		local potionData = SkillData.Herblore.ItemData[potionID + 1]
		lvl = potionData.level
		xp = potionData.herbloreXP
		time = 2
		table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
	end
	if item.masteryID ~= nil and item.masteryID[1] == 4 then
		skill = 'Mining'
		lvl = SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired
		time = 3
		xp = item.miningXP
		--Rune Essence has double quantity, but that's a hard-coded thing in the game so it's hard-coded here
		if item.name == 'Rune Essence' then qty = 2 else qty = 1 end

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

			--If this is a perfect item, need to find the original
			if item2.perfectItem == item.id and item2.recipeRequirements ~= nil then
			for j, reqSet in pairs(item2.recipeRequirements) do
					skill = 'Cooking'
				lvl = item2.cookingLevel
				xp = item2.cookingXP
				req = reqSet
				qty = item2.cookingQty
				time = item2.cookingInterval / 1000
				table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
		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
		return ""
	else
		return table.concat(tables, '\r\n')
	end
end

function p.buildAltMagicTable(spellName)
	local spell = Magic.getSpell(spellName, 'AltMagic')
	local resultPart = {}
	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|-\r\n!style="text-align:right;"|Requirements')
	table.insert(resultPart, '\r\n|'..Icons._SkillReq('Magic', spell.level))
	-- 1 means select any item. 0 would mean Superheat, but that's handled elsewhere
	-- -1 means no item is needed, so hide this section
	if spell.selectItem == 1 then
		table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
		table.insert(resultPart, '\r\n|1 of any item')
	end
	--Add runes
	table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Runes\r\n|')
	for i, req in pairs(spell.runesRequired) do
		local rune = Items.getItemByID(req.id)
		if i > 1 then table.insert(resultPart, ', ') end
		table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
	end
	if spell.runesRequiredAlt ~= nil and Shared.tableCount(spell.runesRequired) ~= Shared.tableCount(spell.runesRequiredAlt) then
		table.insert(resultPart, "<br/>'''OR'''<br/>")
		for i, req in pairs(spell.runesRequiredAlt) do
			local rune = Items.getItemByID(req.id)
			if i > 1 then table.insert(resultPart, ', ') end
			table.insert(resultPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
		end
	end

	--Now just need the output quantity, xp, and casting time (which is always 2)
	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;"|Base XP\r\n|'..spell.magicXP)
	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

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

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

	return table.concat(resultPart)
end

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

	return p._getCreationTable(item)
end

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

	--Alright, time to go through all the ways you can get an item...
	--First up: Can we kill somebody and take theirs?
	local killStrPart = {}
	local dungeonStrPart = {}
	for i, monster in ipairs(MonsterData.Monsters) do
		local isDrop, isBones = false, false
		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
			isDrop = true
			isBones = true
			elseif monster.lootTable ~= nil then
				-- 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
					    if loot[1] == item.id then
					  	isDrop = true
					  	break
					  end
				end
			end
		if isDrop then
			if not isBones and Monsters._isDungeonOnlyMonster(monster) then
				-- For dungeon exclusive monsters, loot is only rolled when they are the last
				-- monster within that dungeon (unless it is a shard)
				if monster.isBoss then
					local areaList = Areas.getMonsterAreas(monster.id)
					for k, area in ipairs(areaList) do
						if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
							table.insert(dungeonStrPart, Icons.Icon({area.name, type='dungeon', notext=true}))
						end
					end
				end
			else
				-- Item is not an end of dungeon reward, and drops when the monster is killed
				table.insert(killStrPart, Icons.Icon({monster.name, type='monster', notext=true}))
			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
			local dungPrefix = (i == Shared.tableCount(Areas.eventData.rewards) and '' or i .. ' ' .. (i == 1 and 'cycle' or 'cycles') .. ' of ')
					table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({'Impending Darkness Event', type='dungeon', notext=true}))
			break
		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
		table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, ','))
	end
	if Shared.tableCount(killStrPart) > 0 then
		table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, ','))
	end

	--Next: Can we find it in a box?
	--While we're here, check for upgrades, originals (for perfect items), and growing
	local lootStr = ''
	local upgradeStr = ''
	local cookStr = ''
	local growStr = ''
	local count1 = 0
	local count2 = 0
	for i, item2 in pairs(ItemData.Items) do
		if item2.dropTable ~= nil then
			for j, loot in pairs(item2.dropTable) do
				if loot[1] == item.id then
					count1 = count1 + 1
					if string.len(lootStr) > 0 then
					  lootStr = lootStr..','
					  --if count1 % 3 == 1 and count1 > 1 then lootStr = lootStr..'<br/>' end
					  lootStr = lootStr..Icons.Icon({item2.name, type="item", notext="true"})
					else
					  lootStr = lootStr..'Opening: '..Icons.Icon({item2.name, type="item", notext="true"})
					end
				end
			end
		end
		if item2.trimmedItemID == item.id then
					count2 = count2 + 1
				if string.len(upgradeStr) > 0 then
					upgradeStr = upgradeStr..','
					--if count2 % 3 == 1 and count2 > 1 then upgradeStr = upgradeStr..'<br/>' end
					upgradeStr = upgradeStr..Icons.Icon({item2.name, type="item", notext="true"})
				else
					table.insert(categoryArray, '[[Category:Upgraded Items]]')
					upgradeStr = upgradeStr..'Upgrading: '..Icons.Icon({item2.name, type="item", notext="true"})
				end
		end
		if item2.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?
	local thiefItems = GatheringSkills.getThievingSourcesForItem(item.id)
	local thiefStr = ''
	if Shared.tableCount(thiefItems) > 0 then
		thiefStr = 'Pickpocketing: '
		for i, thiefRow in pairs(thiefItems) do
			if thiefRow.npc == 'all' then
				--if 'any' is the npc, this is a rare item so just say 'Thieving level 1'
				thiefStr = Icons._SkillReq('Thieving', 1)
			else
				if i > 1 then thiefStr = thiefStr..', ' end
				thiefStr = thiefStr..Icons.Icon({thiefRow.npc, type='thieving', notext='true'})
			end
		end
	end
	if string.len(thiefStr) > 0 then table.insert(lineArray, thiefStr) end

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

	--SmithCheck:
	if item.smithingLevel ~= nil then
		table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
	end

	--CraftCheck:
	if item.craftingLevel ~= nil then
		table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
	end

	--FletchCheck:
	if item.fletchingLevel ~= nil then
		table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
	end

	--RunecraftCheck:
	if item.runecraftingLevel ~= nil then
		table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
	end

	--CookCheck
	if item.cookingLevel ~= nil and item.recipeRequirements ~= nil then
		table.insert(lineArray, Icons._SkillReq('Cooking', item.cookingLevel))
	end

	--MineCheck:
	if item.masteryID ~= nil and item.masteryID[1] == 4 then
		table.insert(lineArray, Icons._SkillReq("Mining", SkillData.Mining.Rocks[item.masteryID[2] + 1].levelRequired))
	end

	--FishCheck:
	if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
		table.insert(lineArray, Icons.Icon({"Fishing", type='skill', notext=true})..' [[Fishing#'..item.type..'|'..item.type..']]')
	elseif item.fishingLevel ~= nil then
		table.insert(lineArray, Icons._SkillReq("Fishing", item.fishingLevel))
	end

	--HerbCheck:
	if item.masteryID ~= nil and item.masteryID[1] == 19 then
		local potionData = SkillData.Herblore.ItemData[item.masteryID[2] + 1].level
		table.insert(lineArray, Icons._SkillReq("Herblore", potionData))
	end

	--WoodcuttingCheck:
	if item.type == 'Logs' then
		local treeData = SkillData.Woodcutting.Trees[item.id + 1]
		local lvl = treeData.level
		table.insert(lineArray, Icons._SkillReq("Woodcutting", lvl))
	end

	--SummoningCheck:
	if item.summoningLevel ~= nil then
		table.insert(lineArray, Icons._SkillReq("Summoning", item.summoningLevel))
	end

	--Finally there are some weird exceptions:
	--Coal can be acquired via firemaking
	if item.name == "Coal Ore" then
		table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
	end

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

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

	--Rhaelyx pieces are also special
	if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
		local rhaSkills = {
			Circlet = {'Woodcutting', 'Fishing', 'Mining', 'Thieving', 'Farming', 'Agility', 'Astrology'},
			Jewel = {'Firemaking', 'Cooking', 'Smithing', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Summoning'}
		}
		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 = {}
		for i, skillName in ipairs(rhaSkList) do
			table.insert(rhaStrPart, Icons.Icon({skillName, type='skill', notext=true}))
		end
		local rhaStr = 'Any action in: ' .. table.concat(rhaStrPart, ', ')
		if item.name == 'Mysterious Stone' then rhaStr = rhaStr .. '<br/>after finding ' .. Icons.Icon({'Crown of Rhaelyx', type='item'}) end
		table.insert(lineArray, rhaStr)
	end

	--Tokens are from the appropriate skill
	if item.isToken and item.skill ~= nil then
		table.insert(lineArray, Icons._SkillReq(Constants.getSkillName(item.skill), 1))
	end

	--Shop items (including special items like gloves that aren't otherwise listed)
	local shopSources = Shop.getItemSourceArray(item.id)
	if Shared.tableCount(shopSources) > 0 then
		table.insert(lineArray, Icons.Icon({'Shop'}))
	end

	--Easter Eggs (manual list 'cause don't have a better way to do that)
	if Shared.contains(Items.EasterEggs, item.name) then
		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

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

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

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

	--Set up function for adding rows
	local buildRow = function(source, type, minqty, qty, weight, totalWeight)
		if minqty == nil then minqty = 1 end
		local rowPart = {}
		table.insert(rowPart, '\r\n|-')
		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: right;" data-sort-value="'..qty..'"|'..minqty)
		if qty ~= minqty then table.insert(rowPart, ' - '..qty) end
		local chance = Shared.round(weight / totalWeight * 100, 2, 2)
		if weight >= totalWeight then
			-- Fraction would be 1/1, so only show the percentage
			chance = 100
			table.insert(rowPart, '\r\n|colspan="2" ')
		else
		local fraction = Shared.fraction(weight, totalWeight)
		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" ')
		else
				table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="' .. chance .. '"| ' .. Shared.fraction(weight, totalWeight) .. '\r\n|')
			end
		end
		if weight == -1 then
			--Weight of -1 means this is a weird row that has a variable percentage
			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
	--First up: Can we kill somebody and take theirs?
	for i, monster in ipairs(MonsterData.Monsters) do
		local minqty = 1
		local qty = 1
		local wt = 0
		local totalWt = 0
		--Only add bones if this monster has loot (ie appears outside a dungeon) and isn't a boss
		--... unless we're looking for Shards of course, at which point we'll take any monster with the right bones
		if monster.bones == item.id and Monsters.getMonsterBones(monster) ~= nil then
			qty = monster.boneQty ~= nil and monster.boneQty or 1
			minqty = qty
			wt = 1
			totalWt = 1
		elseif monster.lootTable ~= nil then
		-- 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
				totalWt = totalWt + loot[2]
				if loot[1] == item.id then
					wt = loot[2]
					qty = loot[3]
				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 not Shared.contains(item.name, 'Shard') and Monsters._isDungeonOnlyMonster(monster) then
		-- For dungeon exclusive monsters, loot is only rolled when they are the last
			-- monster within that dungeon (unless it is a shard)
		if monster.isBoss then
			local areaList = Areas.getMonsterAreas(monster.id)
			for k, area in ipairs(areaList) do
					  if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
				table.insert(dropRows, {source = Icons.Icon({area.name, type='dungeon'}), type = '[[Dungeon]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
				end
			end
			end
		else
				-- Item is not an end of dungeon reward, and drops when the monster is killed
		table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
		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?
	for i, item2 in pairs(ItemData.Items) do
		if item2.dropTable ~= nil then
			local qty = 1
			local wt = 0
			local totalWt = 0
			for j, loot in pairs(item2.dropTable) do
				totalWt = totalWt + loot[2]
				if loot[1] == item.id then
					wt = loot[2]
					if item2.dropQty ~= nil then qty = item2.dropQty[j] end
				end
			end

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

	--Finally, let's try just stealing it
	local thiefType = Icons.Icon({"Thieving", type='skill'})
	local thiefItems = GatheringSkills.getThievingSourcesForItem(item.id)
	for i, thiefRow in pairs(thiefItems) do
		local sourceTxt = ''
		if thiefRow.npc == 'all' then
			sourceTxt = "Thieving Rare Drop"
		else
			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

	--Bonus overtime: Special Fishing table & mining gem table. Also Rags to Riches
	--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'})
		table.insert(dropRows, {source = Icons.Icon({"Rags to Riches I", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
		table.insert(dropRows, {source = Icons.Icon({"Rags to Riches II", type="spell"}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
	end

	if item.fishingCatchWeight ~= nil then
		local fishSource = '[[Fishing#Special|Special]]'
		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
		local fishSource = '[[Fishing#Junk|Junk]]'
		local fishType = Icons.Icon({'Fishing', type='skill'})
		table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = 1, totalWeight = Items.junkCount})
	end

	--Make sure to return nothing if there are no drop sources
	if Shared.tableCount(dropRows) == 0 then return '' end

	table.sort(dropRows, function(a, b)
					               if a.weight / a.totalWeight == b.weight / b.totalWeight then
					                 return a.minqty + a.qty > b.minqty + b.qty
					               else
					                 return a.weight / a.totalWeight > b.weight / b.totalWeight
					               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|}')
	return table.concat(resultPart)
end

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

	return p._getItemLootSourceTable(item)
end

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

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

	return p._getItemUpgradeTable(item)
end

function p._getItemSuperheatTable(item)
	--Manually build the Superheat Item table
	local oreString = ''
	local coalString = ''
	for i, mat in pairs(item.smithReq) do
		local thisMat = Items.getItemByID(mat.id)
		if thisMat.name == 'Coal Ore' then
			coalString = ', '..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
		else
			if string.len(oreString) > 0 then oreString = oreString..', ' end
			oreString =  oreString..Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
		end
	end
	--Set up the header
	local superheatTable = {}
	table.insert(superheatTable, '{|class="wikitable"\r\n!colspan="2"|Spell')
	table.insert(superheatTable, '!!'..Icons.Icon({'Smithing', type='skill', notext='true'})..' Level')
	table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level')
	table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' XP')
	table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
	table.insert(superheatTable, '!!Ore!!Runes')
	 --Loop through all the variants
	local spellNames = {'Superheat I', 'Superheat II', 'Superheat III', 'Superheat IV'}
	for i, sName in pairs(spellNames) do
		local spell = Magic.getSpell(sName, 'AltMagic')
		local rowPart = {}
		table.insert(rowPart, '\r\n|-\r\n|'..Icons.Icon({spell.name, type='spell', notext=true, size=50}))
		table.insert(rowPart, '||'..Icons.Icon({spell.name, type='spell', noicon=true})..'||style="text-align:right;"|'..item.smithingLevel)
		table.insert(rowPart, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.magicXP..'||style="text-align:right;"|'..spell.convertToQty)
		table.insert(rowPart, '||'..oreString)
		if spell.ignoreCoal ~= nil and not spell.ignoreCoal then table.insert(rowPart, coalString) end
		table.insert(rowPart, '||style="text-align:center"|')
		for i, req in pairs(spell.runesRequired) do
			local rune = Items.getItemByID(req.id)
			if i > 1 then table.insert(rowPart, ', ') end
			table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
		end
		table.insert(rowPart, "<br/>'''OR'''<br/>")
		for i, req in pairs(spell.runesRequiredAlt) do
			local rune = Items.getItemByID(req.id)
			if i > 1 then table.insert(rowPart, ', ') end
			table.insert(rowPart, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
		end
		table.insert(superheatTable, table.concat(rowPart))
	end
	 --Add the table end and add the table to the result string
	table.insert(superheatTable, '\r\n|}')
	return table.concat(superheatTable)
end

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

	return p._getItemSuperheatTable(item)
end

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

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

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

	if item.type == 'Bar' then
		table.insert(resultPart, '\r\n==='..Icons.Icon({'Alt Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item))
	end

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

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

	return p._getItemSourceTables(item)
end

function p.getCombatPassiveSlotItems(frame)
	local resultPart = {}
	table.insert(resultPart, '{| class="wikitable"\r\n')
	table.insert(resultPart, '|-\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

	table.insert(resultPart, '|}')

	return table.concat(resultPart)
end

return p