Module:Skills/Gathering: Difference between revisions

From Melvor Idle
(fix header for new columns in Fishing table)
(fix formatting)
Line 1: Line 1:
--Splitting some functions into here to avoid bloating a single file
--Splitting some functions into here to avoid bloating a single file
--Contains function for skills that consume resources (ie smithing, cooking, herblore, etc.)
local p = {}
local p = {}


local SkillData = mw.loadData('Module:Skills/data')
local SkillData = mw.loadData('Module:Skills/data')
local ShopData = mw.loadData('Module:Shop/data')


local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Items = require('Module:Items')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')


local thievingNormalLootChance = 75
function p.getCookedItemsTable(frame)
local thievingAreaLootChance = 0.2
local category = frame.args ~= nil and frame.args[1] or frame
 
local itemArray = nil
function p.getAxeTable(frame)
local toolArray = {}
if category == "Cooking Fire" then
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
itemArray = Items.getItems(function(item) return item.cookingCategory == 0 end)
if Shared.contains(upgrade.name, 'Axe') then
elseif category == "Furnace" then
table.insert(toolArray, upgrade)
itemArray = Items.getItems(function(item) return item.cookingCategory == 1 and item.name ~= 'Lemon Cake' end)
end
elseif category == "Pot"  then  
itemArray = Items.getItems(function(item) return item.cookingCategory == 2 end)
else
itemArray = Items.getItems(function(item) return item.cookingCategory ~= nil and item.name ~= 'Lemon Cake' end)
end
end
 
table.sort(itemArray, function(a, b) return a.cookingLevel < b.cookingLevel end)
local result = '{| class="wikitable"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
-- Logic for generating some cells of the table which are consistent for normal & perfect items
result = result..'\r\n|- class="headerRow-0"'
local getHealingCell = function(item, qty)
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
if item ~= nil then
result = result..'!!Cost!!This Axe!!Total'
return 'data-sort-value="'..(math.floor(item.healsFor) * qty)..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..math.floor(item.healsFor * 10)..(qty > 1 and ' (x'..qty..')' or '')
 
else
local total = 0
return ' '
 
for i, tool in Shared.skpairs(toolArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
result = result..'||'..tool.name
local level = 1
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
--Gonna be lazy and assume there's only the one skill level and it's the one we care about
level = tool.unlockRequirements.skillLevel[1][2]
end
end
result = result..'||style="text-align:right"|'..level
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
end
end
 
local getSaleValueCell = function(item, qty)
result = result..'\r\n|}'
if item ~= nil then
return result
return 'data-sort-value="'..(math.floor(item.sellsFor) * qty)..'"|'..Icons.GP(math.floor(item.sellsFor))..(qty > 1 and ' (x'..qty..')' or '')
end
else
 
return ' '
function p.getPickaxeTable(frame)
local toolArray = {}
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
if Shared.contains(upgrade.name, 'Pickaxe') then
table.insert(toolArray, upgrade)
end
end
end
end
 
local result = '{| class="wikitable"'
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Mine Time Decrease!!colspan="2"|2x Ore Chance'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
result = result..'\r\n!colspan="3" rowspan="2"|Cooked Item!!rowspan="2"|'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'
result = result..'!!rowspan="2"|Cook Time!!rowspan="2"|XP!!colspan="2"|Healing!!colspan="2"|Value!!rowspan="2"|Ingredients'
 
result = result..'\r\n|- class="headerRow-1"'
local total = 0
result = result..'\r\n!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true}) .. '!!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true})
local total2 = 0
 
for i, item in Shared.skpairs(itemArray) do
for i, tool in Shared.skpairs(toolArray) do
local perfectItem = nil
if item.perfectItem ~= nil then
perfectItem = Items.getItemByID(item.perfectItem)
end
local qty = item.cookingQty
if qty == nil then
qty = 1
end
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext=true, size='50'})
result = result..'||'..tool.name
result = result..'\r\n|style="min-width:25px"| '
local level = 1
if perfectItem ~= nil then
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
result = result..Icons.Icon({perfectItem.name, type='item', notext=true, size='50'})
--Gonna be lazy and assume there's only the one skill level and it's the one we care about
level = tool.unlockRequirements.skillLevel[1][2]
end
end
result = result..'||style="text-align:right"|'..level
result = result..'||'
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
if qty > 1 then
 
result = result..qty..'x '
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
 
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
 
local OreDouble = tool.contains.modifiers.increasedChanceToDoubleOres
total2 = total2 + OreDouble
 
result = result..'||style="text-align:right"|+'..OreDouble..'%'
result = result..'||style="text-align:right"|+'..total2..'%'
end
 
result = result..'\r\n|}'
return result
end
 
function p.getRodTable(frame)
local toolArray = {}
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
if Shared.contains(upgrade.name, 'Fishing Rod') then
table.insert(toolArray, upgrade)
end
end
end
result = result..Icons.Icon({item.name, type='item', noicon = true})
 
result = result..'||style="text-align:right"|'..item.cookingLevel
local result = '{| class="wikitable"'
result = result..'||style="text-align:right" data-sort-value="' .. item.cookingInterval .. '"|'..Shared.timeString(item.cookingInterval / 1000, true)
result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
result = result..'||style="text-align:right"|'..item.cookingXP
result = result..'\r\n|- class="headerRow-0"'
result = result..'||'..getHealingCell(item, qty)..'||'..getHealingCell(perfectItem, qty)
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
result = result..'||'..getSaleValueCell(item, qty)..'||'..getSaleValueCell(perfectItem, qty)
result = result..'!!Cost!!This Rod!!Total'
    local matArray = {}
 
    for j, reqSet in pairs(item.recipeRequirements)  do
local total = 0
    for k, mat in pairs(reqSet) do
 
local matItem = Items.getItemByID(mat.id)
for i, tool in Shared.skpairs(toolArray) do
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
result = result..'\r\n|-'
    end
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
result = result..'||'..tool.name
local level = 1
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
--Gonna be lazy and assume there's only the one skill level and it's the one we care about
level = tool.unlockRequirements.skillLevel[1][2]
end
end
result = result..'||style="text-align:right"|'..level
    result = result..'\r\n|'..table.concat(matArray, ' ')
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
 
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
end
end


Line 135: Line 86:
end
end


function p.getTreesTable(frame)
local tierSuffix = { 'I', 'II', 'III', 'IV' }
local result = '{| class="wikitable sortable"'
function p._getHerblorePotionTable(category)
result = result..'\r\n|- class="headerRow-0"'
  if string.upper(category) == 'COMBAT' then
result = result..'\r\n!colspan="2"|Tree!!colspan="2"|Logs!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
    category = 0
result = result..'!!XP!!Cut Time!!XP/s!!GP/s'
  elseif string.upper(category) == 'SKILL' then
 
    category = 1
for i, tree in Shared.skpairs(SkillData.Woodcutting.Trees) do
  elseif type(category) == 'string' then
result = result..'\r\n|-'
    category = tonumber(category)
local treeName = Shared.titleCase(tree.type..' tree')
  end
local logName = Shared.titleCase(tree.type..' logs')
result = result..'\r\n|style="min-width:25px" data-sort-value="'..treeName..'"|'..Icons.Icon({logName, img=treeName, type='tree', notext=true, size=50})
result = result..'||'..treeName..''
result = result..'||style="min-width:25px" data-sort-value="'..logName..'"|'..Icons.Icon({logName, type='item', notext=true, size=50})
result = result..'||'..Icons.Icon({logName, type='item', noicon=true})
result = result..'||style="text-align:right"|'..tree.level
result = result..'||style="text-align:right"|'..tree.xp
result = result..'||style="text-align:right" data-sort-value="'..tree.interval..'"|'..Shared.timeString(tree.interval/1000, true)
local XPs = tree.xp / (tree.interval / 1000)
local Log = Items.getItemByID(i - 1)
local GPs = Log.sellsFor / (tree.interval / 1000)
result = result..'||style="text-align:right"|'..Shared.round(XPs, 2, 2)
result = result..'||style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2))
end
 
result = result..'\r\n|}'
return result
end
 
function p.getSpecialFishingTable(frame)
local lootValue = 0
local totalWt = Items.specialFishWt
 
local result = ''
result = result..'\r\n{|class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!Item'
result = result..'!!Price!!colspan="2"|Chance'


--Sort the loot table by weight in descending order
  local potionArray = {}
table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
  for i, potion in Shared.skpairs(SkillData.Herblore.ItemData) do
for i, row in pairs(Items.specialFishLoot) do
    if potion.category == category then
local thisItem = Items.getItemByID(row[1])
      table.insert(potionArray, potion)
result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
    end
result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
  end
result = result..'|'..Icons.GP(thisItem.sellsFor)


local dropChance = (row[2] / totalWt) * 100
  local result = '{|class = "wikitable sortable stickyHeader"'
result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
  result = result..'\r\n|- class="headerRow-0"'
result = result..'|'..Shared.fraction(row[2], totalWt)
  result = result..'\r\n!Potion!!'..Icons.Icon({'Herblore', type='skill', notext=true})..' Level'
result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
  result = result..'!!XP!!Ingredients!!colspan="2"|Tier!!Value!!Charges!!Effect'
lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
end
result = result..'\r\n|}'
result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0))


return result
  table.sort(potionArray, function(a, b) return a.level < b.level end)
end


function p.getFishingJunkTable(frame)
  for i, potion in Shared.skpairs(potionArray) do
local result = '{| class="wikitable sortable stickyHeader"'
    local tierPots = {}
result = result..'\r\n|- class="headerRow-0"'
    for j = 1, 4, 1 do
result = result..'\r\n!colspan="2"|Item!!Value'
      table.insert(tierPots, Items.getItemByID(potion.itemID[j]))
    end
local itemArray = Items.getItems(function(item) return item.type == "Junk" end)
    result = result..'\r\n|-'
 
    if potion.name == 'Bird Nests Potion' then
table.sort(itemArray, function(a, b) return a.name < b.name end)
      result = result..'\r\n|rowspan="4"|[[Bird Nest Potion]]'
 
    else
for i, item in Shared.skpairs(itemArray) do
      result = result..'\r\n|rowspan="4"|[['..potion.name..']]'
result = result..'\r\n|-'
    end
result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext=true, size='50'})
    result = result..'||rowspan="4" style="text-align:right"|'..potion.level
result = result..'||'..Icons.Icon({item.name, type='item', noicon=true})
    result = result..'||rowspan="4" style="text-align:right"|'..potion.herbloreXP
result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
end


result = result..'\r\n|}'
    local matArray = {}
    for j, mat in Shared.skpairs(tierPots[1].herbloreReq) do
      local matItem = Items.getItemByID(mat.id)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
    result = result..'||rowspan="4"|'..table.concat(matArray, ', ')..'||'
   
    local tierRows = {}
    for j, tierPot in Shared.skpairs(tierPots) do
      local rowTxt = Icons.Icon({tierPot.name, type='item', notext=true})
      rowTxt = rowTxt..'||'..Icons.Icon({tierPot.name, tierSuffix[j], type = 'item', noicon = true})
      rowTxt = rowTxt..'||style="text-align:right;" data-sort-value="'..tierPot.sellsFor..'"|'..Icons.GP(tierPot.sellsFor)
      rowTxt = rowTxt..'||style="text-align:right;"|'..tierPot.potionCharges..'||'..tierPot.description
      table.insert(tierRows, rowTxt)
    end
    result = result..table.concat(tierRows, '\r\n|-\r\n|')
  end


return result
  result = result..'\r\n|}'
  return result
end
end


function p.getMiningOresTable(frame)
function p.getHerblorePotionTable(frame)
local result = '{|class="wikitable sortable stickyHeader"'
  local category = frame.args ~= nil and frame.args[1] or frame
result = result..'\r\n|- class="headerRow-0"'
  return p._getHerblorePotionTable(category)
result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
result = result..'!!XP!!Respawn Time!!Ore Value'
 
local mineData = Shared.clone(SkillData.Mining.Rocks)
 
table.sort(mineData, function(a, b) return a.level < b.level end)
 
for i, oreData in Shared.skpairs(mineData) do
local ore = Items.getItemByID(oreData.ore)
result = result..'\r\n|-\r\n|style="min-width:25px"|'..Icons.Icon({ore.name, type='item', size='50', notext=true})
result = result..'||'..Icons.Icon({ore.name, type='item', noicon=true})
result = result..'||style="text-align:right"|'..oreData.level..'||style="text-align:right"|'..ore.miningXP
result = result..'||style="text-align:right" data-sort-value="'..oreData.respawnInterval..'"|'
result = result..Shared.timeString(oreData.respawnInterval / 1000, true)
result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
end
 
result = result..'\r\n|}'
return result
end
end


function p.getMiningGemsTable(frame)
function p.getRunecraftingTable(frame)
local result = '{|class="wikitable sortable stickyHeader"'
  local category = frame.args ~= nil and frame.args[1] or frame
result = result..'\r\n|- class="headerRow-0"'
  local data = nil
result = result..'\r\n!colspan=2|Gem!!Gem Chance!!Gem Price'


-- Sort gems by ID order
  if    category == "Runes"      then data =
for i, gemData in Shared.spairs(Items.GemTable, function(t,a,b) return t[a].id < t[b].id end) do
        Items.getItems(function(item) return item.runecraftingCategory == 0 end)
local gem = Items.getItemByID(gemData.id)
  elseif category == "ComboRunes" then data =
result = result..'\r\n|-\r\n|style="min-width:25px"|'
        Items.getItems(function(item) return item.runecraftingCategory == 1 end)
result = result..Icons.Icon({gem.name, type='item', size='50', notext=true})
  elseif category == "Weapons"    then data =  
result = result..'||'..Icons.Icon({gem.name, type='item', noicon=true})
        Items.getItems(function(item) return item.runecraftingCategory == 2 and item.runecraftingID end)
result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.chance)
  elseif category == "AirGear"   then data =
result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor)
        Items.getItems(function(item) return item.runecraftingCategory == 3 end)
end
  elseif category == "WaterGear"  then data =  
        Items.getItems(function(item) return item.runecraftingCategory == 4 end)
  elseif category == "EarthGear" then data =
        Items.getItems(function(item) return item.runecraftingCategory == 5 end)
  elseif category == "FireGear"   then data =
        Items.getItems(function(item) return item.runecraftingCategory == 6 end)
  end


result = result..'\r\n|}'
  if data == nil then
return result
    return "ERROR: Invalid Runecrafting category name.[[Category:Pages with script errors]]"
end
  end


function p.getFishTable(frame)
  local result = '{| class="wikitable sortable stickyHeader"'
local data = Items.getItems(function(item) return item.fishingID ~= nil end)
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item\r\n!Name\r\n!'..Icons.Icon({'Runecrafting', type='skill', notext=true})..' Level\r\n!Experience'
  result = result..'\r\n!Item Price\r\n!Ingredients\r\n!XP/s\r\n!GP/s'


table.sort(data, function(a, b) return a.fishingID < b.fishingID end)
  table.sort(data, function(a, b) return (a.runecraftingLevel == b.runecraftingLevel and a.id < b.id)
                                      or a.runecraftingLevel < b.runecraftingLevel end)


local result = '{| class="wikitable sortable stickyHeader"'
  for i, rune in Shared.skpairs(data) do
result = result..'\r\n|- class="headerRow-0"'
    result = result..'\r\n|-'
result = result..'\r\n!Fish\r\n!Name\r\n!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level\r\n!Catch Time'
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({rune.name, type='item', size='50', notext=true})
result = result..'\r\n!Experience\r\n!Fish Price\r\n!XP/s\r\n!GP/s\r\n!'
    result = result..'\r\n| style ="text-align: left;" |'..Icons.Icon({rune.name, type='item', noicon=true})
result = result..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
    result = result..'\r\n| style="text-align:right"|'..rune.runecraftingLevel
 
    result = result..'\r\n| style="text-align:right"|'..rune.runecraftingXP
for i, fish in Shared.skpairs(data) do
    result = result..'\r\n| style="text-align:right"|'..rune.sellsFor
result = result..'\r\n|-'
result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', size='50', notext=true})
result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', noicon=true})
result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel


local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2
    local matArray = {}
local timeStr = string.format("%.1fs-%.1fs", (fish.minFishingInterval/1000), (fish.maxFishingInterval/1000))
    for j, mat in Shared.skpairs(rune.runecraftReq) do
result = result..'\r\n| style="text-align:right" data-sort-value="'..timeSortVal..'"|'..timeStr
      local matItem = Items.getItemByID(mat.id)
result = result..'\r\n| style="text-align:right"|'..fish.fishingXP
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
result = result..'\r\n| style="text-align:right"|'..fish.sellsFor
    end
local XPs = fish.fishingXP / (timeSortVal / 1000)
    result = result..'\r\n|'..table.concat(matArray, ' ')
local GPs = fish.sellsFor / (timeSortVal / 1000)
result = result..'\r\n| style="text-align:right"|'..Shared.round(XPs, 2, 2)
result = result..'\r\n| style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2))


local cookStr = "N/A"
    local rcCraftTime = 2.00
if fish.cookingLevel ~= nil then
    local xps = rune.runecraftingXP / rcCraftTime
cookStr = fish.cookingLevel
    local gps = rune.sellsFor / rcCraftTime
end
    result = result..'\r\n| style="text-align:right"|'..string.format("%.2f", xps)
result = result..'\r\n| style="text-align:right"|'..cookStr
    result = result..'\r\n| style="text-align:right"|'..string.format("%.2f", gps)
end
  end


result = result..'\r\n|}'
  result = result..'\r\n|}'
return result
  return result
end
end


function p.getFishingAreasTable(frame)
function p.getFletchingTable(frame)
  local category = frame.args ~= nil and frame.args[1] or frame
  local data = nil


local result = '{| class="wikitable sortable stickyHeader"'
  if    category == "Arrows"   then
result = result..'\r\n|- class="headerRow-0"'
  data = Items.getItems(function(item) return item.fletchingCategory == 0 end)
result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance'
  elseif category == "Shortbows" then
result = result..'\r\n!Junk Chance\r\n!Special Chance'
  data = Items.getItems(function(item) return item.fletchingCategory == 1 end)
  elseif category == "Longbows"  then
  data = Items.getItems(function(item) return item.fletchingCategory == 2 end)
  elseif category == "Bolts"    then
  data =  Items.getItems(function(item) return item.fletchingCategory == 3 end)
  elseif category == "Crossbows" then
  data = Items.getItems(function(item) return item.fletchingCategory == 4 end)
  elseif category == "Javelins"  then
  data = Items.getItems(function(item) return item.fletchingCategory == 5 end)
  end


for i, area in Shared.skpairs(SkillData.Fishing.Areas) do
  if data == nil then
result = result..'\r\n|-'
    return "ERROR: Invalid Fletching category name.[[Category:Pages with script errors]]"
result = result..'\r\n| style ="text-align: left;" |'..area.name
  end


local fishArray = {}
  local result = '{| class="wikitable sortable stickyHeader"'
for j, fish in Shared.skpairs(area.fish) do
  result = result..'\r\n|- class="headerRow-0"'
local fishTable = Items.getItems(function(item) return item.fishingID == fish end)
  result = result..'\r\n!Item\r\n!Name\r\n!Fletching Level\r\n!Experience'
local fishItem = fishTable[0] or fishTable[1]
  result = result..'\r\n!Quantity\r\n!Sells For\r\n!Ingredients'
table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
end
result = result..'\r\n|'..table.concat(fishArray, '<br />')


result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%'
  table.sort(data, function(a, b) return (a.fletchingLevel == b.fletchingLevel and a.id < b.id)
result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
                                      or a.fletchingLevel <  b.fletchingLevel end)
result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
end
 
result = result..'\r\n|}'
return result
end


function p.getThievingNPC(npcName)
  for i, fletch in Shared.skpairs(data) do
local result = nil
    result = result..'\r\n|-'
for i, npc in Shared.skpairs(SkillData.Thieving.NPCs) do
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fletch.name, type='item', size='50', notext=true})
if npc.name == npcName then
    result = result..'\r\n| style ="text-align: left;" |'..Icons.Icon({fletch.name, type='item', noicon=true})
result = Shared.clone(npc)
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchingLevel
break
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchingXP
end
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchQty
end
    result = result..'\r\n| style="text-align:right"|'..fletch.sellsFor
return result
end


function p.getThievingNPCArea(npc)
    local matArray = {}
if type(npc) == 'string' then
    for j, mat in Shared.skpairs(fletch.fletchReq) do
npc = p.getThievingNPC(npc)
      local matItem = Items.getItemByID(mat.id)
end
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
local result = nil
    result = result..'\r\n|'..table.concat(matArray, ' ')
for i, area in Shared.skpairs(SkillData.Thieving.Areas) do
  end
for j, npcID in pairs(area.npcs) do
if npcID == npc.id then
result = area
break
end
end
end
return result
end


function p._getThievingNPCStat(npc, statName)
  result = result..'\r\n|}'
local result = nil
  return result
if statName == 'level' then
result = Icons._SkillReq('Thieving', npc.level)
elseif statName == 'maxHit' then
result = npc.maxHit * 10
elseif statName == 'area' then
local area = p.getThievingNPCArea(npc)
result = area.name
else
result = npc[statName]
end
if result == nil then
result = ''
end
return result
end
end


function p.getThievingNPCStat(frame)
function p.getCraftingTable(frame)
local npcName = frame.args ~= nil and frame.args[1] or frame[1]
  local category = frame.args ~= nil and frame.args[1] or frame
local statName = frame.args ~= nil and frame.args[2] or frame[2]
  local data = nil
local npc = p.getThievingNPC(npcName)
if npc == nil then
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
end
return p._getThievingNPCStat(npc, statName)
end


function p.getThievingGeneralRareTable(frame)
  if    category == "Leather"    then data =
local rareTxt = '{|class="wikitable sortable"'
        Items.getItems(function(item) return item.tier == "Leather" or item.tier == "Hard Leather" end)
rareTxt = rareTxt..'\r\n!Item!!Qty'
  elseif category == "Dragonhide" then data =  
rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
        Items.getItems(function(item) return item.tier == "Dragonhide" and item.craftingLevel ~= nil end)
for i, drop in pairs(SkillData.Thieving.RareItems) do
  elseif category == "Rings"      then data =  
local thisItem = Items.getItemByID(drop.itemID)
        Items.getItems(function(item) return item.type == "Ring" and item.craftingLevel ~= nil end)
local odds = drop.chance
  elseif category == "Necklaces" then data =
        Items.getItems(function(item) return item.type == "Amulet" and item.craftingLevel ~= nil end)
rareTxt = rareTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
  elseif category == "Bags"   then data =
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
  Items.getItems(function(item) return item.type == 'Bag' and item.craftingLevel ~= nil end)
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(1, Shared.round2(1/(odds/100), 0))
  end
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.round(odds, 4, 4)..'%'
end
rareTxt = rareTxt..'\r\n|}'
return rareTxt
end


function p._getThievingNPCLootTables(npc)
  if data == nil then
local result = ''
    return "ERROR: Invalid Crafting category name.[[Category:Pages with script errors]]"
local sectionTxt = {}
  end
--Five sections here: GP, normal loot, area loot, rare loot, and unique item
--First up, GP:
local gpTxt = 'Successfully pickpocketing the '..npc.name..' will always give '..Icons.GP(1, npc.maxGP)
table.insert(sectionTxt, gpTxt)
--Next up, normal loot:
--(Skip if no loot)
if npc.lootTable ~= nil and Shared.tableCount(npc.lootTable) > 0 then
local normalTxt = '===Possible Common Drops:===\r\nUp to one of these will be received on a successful pickpocket:'
local totalWt = 0
local lootChance = thievingNormalLootChance
local lootValue = 0
--First loop through to get the total weight so we have it for later
for i, loot in pairs(npc.lootTable) do
totalWt = totalWt + loot[2]
end
normalTxt = normalTxt..'\r\n{|class="wikitable sortable"'
normalTxt = normalTxt..'\r\n!Item!!Qty'
normalTxt = normalTxt..'!!Price!!colspan="2"|Chance'
--Then sort the loot table by weight
table.sort(npc.lootTable, function(a, b) return a[2] > b[2] end)
for i, row in Shared.skpairs(npc.lootTable) do
local thisItem = Items.getItemByID(row[1])
local maxQty = row[3]
if thisItem ~= nil then
normalTxt = normalTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
else
normalTxt = normalTxt..'\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]'
end
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..maxQty..'"|'
if maxQty > 1 then
normalTxt = normalTxt.. '1 - '
end
normalTxt = normalTxt..Shared.formatnum(row[3])
--Adding price columns
local itemPrice = 0
if thisItem == nil then
normalTxt = normalTxt..'||data-sort-value="0"|???'
else
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
if itemPrice == 0 or maxQty == 1 then
normalTxt = normalTxt..'||'..Icons.GP(itemPrice)
else
normalTxt = normalTxt..'||'..Icons.GP(itemPrice, itemPrice * maxQty)
end
end
--Getting the drop chance
local dropChance = (row[2] / totalWt * lootChance)
if dropChance ~= 100 then
--Show fraction as long as it isn't going to be 1/1
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..row[2]..'"'
normalTxt = normalTxt..'|'..Shared.fraction(row[2] * lootChance, totalWt * 100)
normalTxt = normalTxt..'||'
else
normalTxt = normalTxt..'||colspan="2" data-sort-value="'..row[2]..'"'
end
normalTxt = normalTxt..'style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
--Adding to the average loot value based on price & dropchance
lootValue = lootValue + (dropChance * 0.01 * itemPrice * ((1 + maxQty) / 2))
end
if multiDrop then
normalTxt = normalTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
if lootChance < 100 then
normalTxt = normalTxt..'\r\n|style="text-align:right"|'..Shared.fraction(lootChance, 100)..'||'
else
normalTxt = normalTxt..'\r\n|colspan="2" '
end
normalTxt = normalTxt..'style="text-align:right"|'..lootChance..'.00%'
end
normalTxt = normalTxt..'\r\n|}'
table.insert(sectionTxt, normalTxt)
end
--After normal drops, add in rare drops
local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket'
rareTxt = rareTxt..'\r\n'..p.getThievingGeneralRareTable()
table.insert(sectionTxt, rareTxt)
local areaTxt = '===Possible Area Unique Drops==='
areaTxt = areaTxt..'\r\nAny Area Unique Drop is equally likely to be obtained after a successful pickpocket. '
areaTxt = areaTxt..'\r\nEach Area Unique Drop is rolled for separately, so it is possible to receive multiple Area Unique Drops from a single action. '
areaTxt = areaTxt..'The chance of receiving an Area Unique drop is tripled if the 95% Thieving Mastery Pool checkpoint is active.'
local area = p.getThievingNPCArea(npc)
areaTxt = areaTxt..'\r\n{|class="wikitable sortable"'
areaTxt = areaTxt..'\r\n!Item!!Qty'
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
local dropCount = Shared.tableCount(area.uniqueDrops)
local dropLines = {}
for i, drop in pairs(area.uniqueDrops) do
local thisItem = Items.getItemByID(drop.itemID)
local lineTxt = ''
lineTxt = lineTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
lineTxt = lineTxt..'||'..drop.qty..'||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100))
lineTxt = lineTxt..'||'..Shared.round(thievingAreaLootChance, 2, 2)..'%'
dropLines[thisItem.name] = lineTxt
end
for i, txt in Shared.skpairs(dropLines) do
areaTxt = areaTxt..txt
end
areaTxt = areaTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
areaTxt = areaTxt..'\r\n|style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100))..'||'
areaTxt = areaTxt..'style="text-align:right"|'..Shared.round(thievingAreaLootChance, 2, 2)..'%'
areaTxt = areaTxt..'\r\n|}'
table.insert(sectionTxt, areaTxt)
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
local uniqueTxt = '===Possible NPC Unique Drop==='
uniqueTxt = uniqueTxt..'\r\nThe chance of receiving the unique drop for an NPC is based on a combination of several factors.'
uniqueTxt = uniqueTxt..' The unique drop chance for an NPC is included in the tooltip for your Stealth against that NPC.'
local thisItem = Items.getItemByID(npc.uniqueDrop.itemID)
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
if npc.uniqueDrop.qty > 1 then
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.qty})
else
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'})
end
table.insert(sectionTxt, uniqueTxt)
end
return table.concat(sectionTxt, '\r\n')
end


function p.getThievingNPCLootTables(frame)
  table.sort(data, function(a, b) return (a.craftingLevel == b.craftingLevel and a.id < b.id)  
local npcName = frame.args ~= nil and frame.args[1] or frame
                                      or a.craftingLevel <  b.craftingLevel end)
local npc = p.getThievingNPC(npcName)
if npc == nil then
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
end
return p._getThievingNPCLootTables(npc)
end


function p.getThievingNPCTable()
  local result = '{| class="wikitable sortable stickyHeader"'
local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item\r\n!Name\r\n!'..Icons.Icon({'Crafting', type='skill', notext=true})..' Level\r\n!Experience'
result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!Unique Drop'
  result = result..'\r\n!Item Price\r\n!Ingredients'
local npcArray = Shared.clone(SkillData.Thieving.NPCs)
table.sort(npcArray, function(a, b) return a.level < b.level end)
for i, npc in Shared.skpairs(npcArray) do
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true})
result = result..'||'..Icons.Icon({npc.name, type='thieving', noicon=true})


local area = p.getThievingNPCArea(npc)
  for i, craft in Shared.skpairs(data) do
result = result..'||'..area.name
    result = result..'\r\n|-'
result = result..'||'..Icons._SkillReq('Thieving', npc.level)
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({craft.name, type='item', size='50', notext=true})
result = result..'||style="text-align:right"|'..npc.xp
    result = result..'\r\n| style ="text-align: left;" |'..Icons.Icon({craft.name, type='item', noicon=true})
result = result..'||style="text-align:right"|'..(npc.maxHit * 10)
    result = result..'\r\n| style="text-align:right"|'..craft.craftingLevel
result = result..'||style="text-align:right"|'..npc.perception
    result = result..'\r\n| style="text-align:right"|'..craft.craftingXP
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
    result = result..'\r\n| style="text-align:right"|'..craft.sellsFor
local uniqueDrop = Items.getItemByID(npc.uniqueDrop.itemID)
if npc.uniqueDrop.qty > 1 then
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item', qty = npc.uniqueDrop.qty})
else
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item'})
end
else
result = result..'|| '
end
end
result = result..'\r\n|}'
return result
end


function p.getThievingAreaTable(frame)
    local matArray = {}
local resultPart = {}
    if craft.craftGPCost ~= nil and craft.craftGPCost > 0 then
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
    table.insert(matArray, Icons.GP(craft.craftGPCost))
table.insert(resultPart, '\r\n|- class="headerRow-0"')
    end
table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')
    for j, mat in Shared.skpairs(craft.craftReq) do
      local matItem = Items.getItemByID(mat.id)
local areaArray = Shared.clone(SkillData.Thieving.Areas)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
table.sort(areaArray, function(a, b) return a.id < b.id end)
    end
for i, area in ipairs(areaArray) do
    result = result..'\r\n|'..table.concat(matArray, ', ')
local minLevel, npcList, areaItemList = nil, {}, {}
  end
-- Build NPC list & determine level for area, this is the minimum
-- Thieving level required for all NPCs within that area
if area.npcs ~= nil and Shared.tableCount(area.npcs) > 0 then
for j, npcID in ipairs(area.npcs) do
-- Don't bother cloning the NPC below since we aren't modifying any part of it
local npc = SkillData.Thieving.NPCs[npcID + 1]
if minLevel == nil or npc.level < minLevel then
minLevel = npc.level
end
table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
end
else
table.insert(npcList, '')
end
-- Build area unique item list
if area.uniqueDrops ~= nil and Shared.tableCount(area.uniqueDrops) > 0 then
for k, drop in ipairs(area.uniqueDrops) do
local areaItem = Items.getItemByID(drop.itemID)
if areaItem == nil then
table.insert(areaItemList, 'Unknown[[Category:Pages with script errors]]')
else
local iconDef = {areaItem.name, type='item'}
if drop.qty > 1 then
iconDef.qty = drop.qty
end
table.insert(areaItemList, Icons.Icon(iconDef))
end
end
else
table.insert(areaItemList, '')
end
-- Generate table row
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|' .. area.name)
table.insert(resultPart, '\r\n|' .. Icons._SkillReq('Thieving', minLevel))
table.insert(resultPart, '\r\n|' .. table.concat(npcList, '<br/>'))
table.insert(resultPart, '\r\n|' .. table.concat(areaItemList, '<br/>'))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end


function p.getThievingSourcesForItem(itemID)
  result = result..'\r\n|}'
local resultArray = {}
  return result
local areaNPCs = {}
--First check area unique drops
--If an area drops the item, add all the NPC ids to the list so we can add them later
if not result then
for  i, area in pairs(SkillData.Thieving.Areas) do
for j, drop in pairs(area.uniqueDrops) do
if drop.itemID == itemID then
for k, npcID in pairs(area.npcs) do
areaNPCs[npcID] = drop.qty
end
break
end
end
end
end
--Now go through and get drop chances on each NPC if needed
for i, npc in pairs(SkillData.Thieving.NPCs) do
local totalWt = 0
local dropWt = 0
local dropQty = 0
for j, drop in pairs(npc.lootTable) do
totalWt = totalWt + drop[2]
if drop[1] == itemID then
dropWt = drop[2]
dropQty = drop[3]
end
end
if dropWt > 0 then
table.insert(resultArray, {npc = npc.name, minQty = 1, maxQty = dropQty, wt = dropWt * thievingNormalLootChance, totalWt = totalWt * 100, level = npc.level})
end
--Chance of -1 on unique drops is to indicate variable chance
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID == itemID then
table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.qty, maxQty = npc.uniqueDrop.qty, wt = -1, totalWt = -1, level = npc.level})
end
if areaNPCs[npc.id] ~= nil then
table.insert(resultArray, {npc = npc.name, minQty = areaNPCs[npc.id], maxQty = areaNPCs[npc.id], wt = thievingAreaLootChance, totalWt = 100, level = npc.level})
end
end
for i, drop in pairs(SkillData.Thieving.RareItems) do
if drop.itemID == itemID then
table.insert(resultArray, {npc = 'all', minQty = 1, maxQty = 1, wt = 1, totalWt = Shared.round2(1/(drop.chance/100), 0), level = 1})
end
end
return resultArray
end
end


return p
return p

Revision as of 22:13, 5 November 2021

Documentation for this module may be created at Module:Skills/Gathering/doc

--Splitting some functions into here to avoid bloating a single file
--Contains function for skills that consume resources (ie smithing, cooking, herblore, etc.)
local p = {}

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

local Shared = require('Module:Shared')
local Items = require('Module:Items')
local Icons = require('Module:Icons')

function p.getCookedItemsTable(frame)
	local category = frame.args ~= nil and frame.args[1] or frame
	local itemArray = nil
	
	if category == "Cooking Fire" then 
		itemArray = Items.getItems(function(item) return item.cookingCategory == 0 end)
	elseif category == "Furnace" then 
		itemArray = Items.getItems(function(item) return item.cookingCategory == 1 and item.name ~= 'Lemon Cake' end)
	elseif category == "Pot"  then 
		itemArray = Items.getItems(function(item) return item.cookingCategory == 2 end)
	else
		itemArray = Items.getItems(function(item) return item.cookingCategory ~= nil and item.name ~= 'Lemon Cake' end)
	end
	table.sort(itemArray, function(a, b) return a.cookingLevel < b.cookingLevel end)
	
	-- Logic for generating some cells of the table which are consistent for normal & perfect items
	local getHealingCell = function(item, qty)
		if item ~= nil then
			return 'data-sort-value="'..(math.floor(item.healsFor) * qty)..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..math.floor(item.healsFor * 10)..(qty > 1 and ' (x'..qty..')' or '')
		else
			return ' '
		end
	end
	local getSaleValueCell = function(item, qty)
		if item ~= nil then
			return 'data-sort-value="'..(math.floor(item.sellsFor) * qty)..'"|'..Icons.GP(math.floor(item.sellsFor))..(qty > 1 and ' (x'..qty..')' or '')
		else
			return ' '
		end
	end
	
	local result = '{| class="wikitable sortable stickyHeader"'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!colspan="3" rowspan="2"|Cooked Item!!rowspan="2"|'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
	result = result..'!!rowspan="2"|Cook Time!!rowspan="2"|XP!!colspan="2"|Healing!!colspan="2"|Value!!rowspan="2"|Ingredients'
	result = result..'\r\n|- class="headerRow-1"'
	result = result..'\r\n!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true}) .. '!!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true})
	
	for i, item in Shared.skpairs(itemArray) do
		local perfectItem = nil
		if item.perfectItem ~= nil then
			perfectItem = Items.getItemByID(item.perfectItem)
		end
		local qty = item.cookingQty
		if qty == nil then
			qty = 1
		end
		result = result..'\r\n|-'
		result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext=true, size='50'})
		result = result..'\r\n|style="min-width:25px"| '
		if perfectItem ~= nil then
			result = result..Icons.Icon({perfectItem.name, type='item', notext=true, size='50'})
		end
		result = result..'||'
		if qty > 1 then
			result = result..qty..'x '
		end
		result = result..Icons.Icon({item.name, type='item', noicon = true})
		result = result..'||style="text-align:right"|'..item.cookingLevel
		result = result..'||style="text-align:right" data-sort-value="' .. item.cookingInterval .. '"|'..Shared.timeString(item.cookingInterval / 1000, true)
		result = result..'||style="text-align:right"|'..item.cookingXP
		result = result..'||'..getHealingCell(item, qty)..'||'..getHealingCell(perfectItem, qty)
		result = result..'||'..getSaleValueCell(item, qty)..'||'..getSaleValueCell(perfectItem, qty)
	    local matArray = {}
	    for j, reqSet in pairs(item.recipeRequirements)  do
		    for k, mat in pairs(reqSet) do
				local matItem = Items.getItemByID(mat.id)
				table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
	    	end
		end
	    result = result..'\r\n|'..table.concat(matArray, ' ')
	end

	result = result..'\r\n|}'
	return result
end

local tierSuffix = { 'I', 'II', 'III', 'IV' }
function p._getHerblorePotionTable(category)
  if string.upper(category) == 'COMBAT' then
    category = 0
  elseif string.upper(category) == 'SKILL' then
    category = 1
  elseif type(category) == 'string' then
    category = tonumber(category)
  end

  local potionArray = {}
  for i, potion in Shared.skpairs(SkillData.Herblore.ItemData) do
    if potion.category == category then
      table.insert(potionArray, potion)
    end
  end

  local result = '{|class = "wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Potion!!'..Icons.Icon({'Herblore', type='skill', notext=true})..' Level'
  result = result..'!!XP!!Ingredients!!colspan="2"|Tier!!Value!!Charges!!Effect'

  table.sort(potionArray, function(a, b) return a.level < b.level end)

  for i, potion in Shared.skpairs(potionArray) do
    local tierPots = {}
    for j = 1, 4, 1 do
      table.insert(tierPots, Items.getItemByID(potion.itemID[j]))
    end
    result = result..'\r\n|-'
    if potion.name == 'Bird Nests Potion' then
      result = result..'\r\n|rowspan="4"|[[Bird Nest Potion]]'
    else
      result = result..'\r\n|rowspan="4"|[['..potion.name..']]'
    end
    result = result..'||rowspan="4" style="text-align:right"|'..potion.level
    result = result..'||rowspan="4" style="text-align:right"|'..potion.herbloreXP

    local matArray = {}
    for j, mat in Shared.skpairs(tierPots[1].herbloreReq) do
      local matItem = Items.getItemByID(mat.id)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
    result = result..'||rowspan="4"|'..table.concat(matArray, ', ')..'||'
    
    local tierRows = {}
    for j, tierPot in Shared.skpairs(tierPots) do
      local rowTxt = Icons.Icon({tierPot.name, type='item', notext=true})
      rowTxt = rowTxt..'||'..Icons.Icon({tierPot.name, tierSuffix[j], type = 'item', noicon = true})
      rowTxt = rowTxt..'||style="text-align:right;" data-sort-value="'..tierPot.sellsFor..'"|'..Icons.GP(tierPot.sellsFor)
      rowTxt = rowTxt..'||style="text-align:right;"|'..tierPot.potionCharges..'||'..tierPot.description
      table.insert(tierRows, rowTxt)
    end
    result = result..table.concat(tierRows, '\r\n|-\r\n|')
  end

  result = result..'\r\n|}'
  return result
end

function p.getHerblorePotionTable(frame)
  local category = frame.args ~= nil and frame.args[1] or frame
  return p._getHerblorePotionTable(category)
end

function p.getRunecraftingTable(frame)
  local category = frame.args ~= nil and frame.args[1] or frame
  local data = nil

  if     category == "Runes"      then data = 
         Items.getItems(function(item) return item.runecraftingCategory == 0 end)
  elseif category == "ComboRunes" then data = 
         Items.getItems(function(item) return item.runecraftingCategory == 1 end)
  elseif category == "Weapons"    then data = 
         Items.getItems(function(item) return item.runecraftingCategory == 2 and item.runecraftingID end)
  elseif category == "AirGear"    then data = 
         Items.getItems(function(item) return item.runecraftingCategory == 3 end)
  elseif category == "WaterGear"  then data = 
         Items.getItems(function(item) return item.runecraftingCategory == 4 end)
  elseif category == "EarthGear"  then data = 
         Items.getItems(function(item) return item.runecraftingCategory == 5 end)
  elseif category == "FireGear"   then data = 
         Items.getItems(function(item) return item.runecraftingCategory == 6 end)
  end

  if data == nil then
    return "ERROR: Invalid Runecrafting category name.[[Category:Pages with script errors]]"
  end

  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item\r\n!Name\r\n!'..Icons.Icon({'Runecrafting', type='skill', notext=true})..' Level\r\n!Experience'
  result = result..'\r\n!Item Price\r\n!Ingredients\r\n!XP/s\r\n!GP/s'

  table.sort(data, function(a, b) return (a.runecraftingLevel == b.runecraftingLevel and a.id < b.id) 
                                       or a.runecraftingLevel <  b.runecraftingLevel end)

  for i, rune in Shared.skpairs(data) do
    result = result..'\r\n|-'
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({rune.name, type='item', size='50', notext=true})
    result = result..'\r\n| style ="text-align: left;" |'..Icons.Icon({rune.name, type='item', noicon=true})
    result = result..'\r\n| style="text-align:right"|'..rune.runecraftingLevel
    result = result..'\r\n| style="text-align:right"|'..rune.runecraftingXP
    result = result..'\r\n| style="text-align:right"|'..rune.sellsFor

    local matArray = {}
    for j, mat in Shared.skpairs(rune.runecraftReq) do
      local matItem = Items.getItemByID(mat.id)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
    result = result..'\r\n|'..table.concat(matArray, ' ')

    local rcCraftTime = 2.00
    local xps = rune.runecraftingXP / rcCraftTime
    local gps = rune.sellsFor / rcCraftTime
    result = result..'\r\n| style="text-align:right"|'..string.format("%.2f", xps)
    result = result..'\r\n| style="text-align:right"|'..string.format("%.2f", gps)
  end

  result = result..'\r\n|}'
  return result
end

function p.getFletchingTable(frame)
  local category = frame.args ~= nil and frame.args[1] or frame
  local data = nil

  if     category == "Arrows"    then 
  	data = Items.getItems(function(item) return item.fletchingCategory == 0 end)
  elseif category == "Shortbows" then 
  	data = Items.getItems(function(item) return item.fletchingCategory == 1 end)
  elseif category == "Longbows"  then 
  	data = Items.getItems(function(item) return item.fletchingCategory == 2 end)
  elseif category == "Bolts"     then 
  	data =  Items.getItems(function(item) return item.fletchingCategory == 3 end)
  elseif category == "Crossbows" then 
  	data = Items.getItems(function(item) return item.fletchingCategory == 4 end)
  elseif category == "Javelins"  then 
  	data = Items.getItems(function(item) return item.fletchingCategory == 5 end)
  end

  if data == nil then
    return "ERROR: Invalid Fletching category name.[[Category:Pages with script errors]]"
  end

  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item\r\n!Name\r\n!Fletching Level\r\n!Experience'
  result = result..'\r\n!Quantity\r\n!Sells For\r\n!Ingredients'

  table.sort(data, function(a, b) return (a.fletchingLevel == b.fletchingLevel and a.id < b.id) 
                                       or a.fletchingLevel <  b.fletchingLevel end)

  for i, fletch in Shared.skpairs(data) do
    result = result..'\r\n|-'
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fletch.name, type='item', size='50', notext=true})
    result = result..'\r\n| style ="text-align: left;" |'..Icons.Icon({fletch.name, type='item', noicon=true})
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchingLevel
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchingXP
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchQty
    result = result..'\r\n| style="text-align:right"|'..fletch.sellsFor

    local matArray = {}
    for j, mat in Shared.skpairs(fletch.fletchReq) do
      local matItem = Items.getItemByID(mat.id)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
    result = result..'\r\n|'..table.concat(matArray, ' ')
  end

  result = result..'\r\n|}'
  return result
end

function p.getCraftingTable(frame)
  local category = frame.args ~= nil and frame.args[1] or frame
  local data = nil

  if     category == "Leather"    then data = 
         Items.getItems(function(item) return item.tier == "Leather" or item.tier == "Hard Leather" end)
  elseif category == "Dragonhide" then data = 
         Items.getItems(function(item) return item.tier == "Dragonhide" and item.craftingLevel ~= nil end)
  elseif category == "Rings"      then data = 
         Items.getItems(function(item) return item.type == "Ring" and item.craftingLevel ~= nil end)
  elseif category == "Necklaces"  then data = 
         Items.getItems(function(item) return item.type == "Amulet" and item.craftingLevel ~= nil end)
  elseif category == "Bags"		  then data =
  		 Items.getItems(function(item) return item.type == 'Bag' and item.craftingLevel ~= nil end)
  end

  if data == nil then
    return "ERROR: Invalid Crafting category name.[[Category:Pages with script errors]]"
  end

  table.sort(data, function(a, b) return (a.craftingLevel == b.craftingLevel and a.id < b.id) 
                                       or a.craftingLevel <  b.craftingLevel end)

  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item\r\n!Name\r\n!'..Icons.Icon({'Crafting', type='skill', notext=true})..' Level\r\n!Experience'
  result = result..'\r\n!Item Price\r\n!Ingredients'

  for i, craft in Shared.skpairs(data) do
    result = result..'\r\n|-'
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({craft.name, type='item', size='50', notext=true})
    result = result..'\r\n| style ="text-align: left;" |'..Icons.Icon({craft.name, type='item', noicon=true})
    result = result..'\r\n| style="text-align:right"|'..craft.craftingLevel
    result = result..'\r\n| style="text-align:right"|'..craft.craftingXP
    result = result..'\r\n| style="text-align:right"|'..craft.sellsFor

    local matArray = {}
    if craft.craftGPCost ~= nil and craft.craftGPCost > 0 then
    	table.insert(matArray, Icons.GP(craft.craftGPCost))
    end
    for j, mat in Shared.skpairs(craft.craftReq) do
      local matItem = Items.getItemByID(mat.id)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
    result = result..'\r\n|'..table.concat(matArray, ', ')
  end

  result = result..'\r\n|}'
  return result
end

return p