Difference between revisions of "Module:Skills/Gathering"

From Melvor Idle
m (Switch to spairs in getMiningGemsTable)
(added p.getThievingNPCLootTables)
Line 9: Line 9:
 
local Items = require('Module:Items')
 
local Items = require('Module:Items')
 
local Icons = require('Module:Icons')
 
local Icons = require('Module:Icons')
 +
 +
local thievingNormalLootChance = 75
 +
local thievingAreaLootChance = 0.2
  
 
function p.getAxeTable(frame)
 
function p.getAxeTable(frame)
  local toolArray = {}
+
local toolArray = {}
  for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
+
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
    if Shared.contains(upgrade.name, 'Axe') then
+
if Shared.contains(upgrade.name, 'Axe') then
      table.insert(toolArray, upgrade)
+
table.insert(toolArray, upgrade)
    end
+
end
  end
+
end
  
  local result = '{| class="wikitable"'
+
local result = '{| class="wikitable"'
  result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
+
result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
  result = result..'\r\n|- class="headerRow-0"'
+
result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
+
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
  result = result..'!!Cost!!This Axe!!Total'
+
result = result..'!!Cost!!This Axe!!Total'
  
  local total = 0
+
local total = 0
  
  for i, tool in Shared.skpairs(toolArray) do
+
for i, tool in Shared.skpairs(toolArray) do
    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" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
    result = result..'||'..tool.name
+
result = result..'||'..tool.name
    local level = 1
+
local level = 1
    if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
+
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
+
--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]
+
level = tool.unlockRequirements.skillLevel[1][2]
    end
+
end
    result = result..'||style="text-align:right"|'..level
+
result = result..'||style="text-align:right"|'..level
    result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
+
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]
+
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
    total = total + cutTime
+
total = total + cutTime
    result = result..'||style="text-align:right"|-'..cutTime..'%'
+
result = result..'||style="text-align:right"|-'..cutTime..'%'
    result = result..'||style="text-align:right"|-'..total..'%'
+
result = result..'||style="text-align:right"|-'..total..'%'
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  return result
+
return result
 
end
 
end
  
 
function p.getPickaxeTable(frame)
 
function p.getPickaxeTable(frame)
  local toolArray = {}
+
local toolArray = {}
  for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
+
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
    if Shared.contains(upgrade.name, 'Pickaxe') then
+
if Shared.contains(upgrade.name, 'Pickaxe') then
      table.insert(toolArray, upgrade)
+
table.insert(toolArray, upgrade)
    end
+
end
  end
+
end
  
  local result = '{| class="wikitable"'
+
local result = '{| class="wikitable"'
  result = result..'\r\n!colspan="4"| !!colspan="2"|Mine Time Decrease!!colspan="2"|2x Ore Chance'
+
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="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
  result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'
+
result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'
  
  local total = 0
+
local total = 0
  local total2 = 0
+
local total2 = 0
  
  for i, tool in Shared.skpairs(toolArray) do
+
for i, tool in Shared.skpairs(toolArray) do
    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" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
    result = result..'||'..tool.name
+
result = result..'||'..tool.name
    local level = 1
+
local level = 1
    if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
+
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
+
--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]
+
level = tool.unlockRequirements.skillLevel[1][2]
    end
+
end
    result = result..'||style="text-align:right"|'..level
+
result = result..'||style="text-align:right"|'..level
    result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
+
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]
+
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
    total = total + cutTime
+
total = total + cutTime
  
    result = result..'||style="text-align:right"|-'..cutTime..'%'
+
result = result..'||style="text-align:right"|-'..cutTime..'%'
    result = result..'||style="text-align:right"|-'..total..'%'
+
result = result..'||style="text-align:right"|-'..total..'%'
  
    local OreDouble = tool.contains.modifiers.increasedChanceToDoubleOres
+
local OreDouble = tool.contains.modifiers.increasedChanceToDoubleOres
    total2 = total2 + OreDouble
+
total2 = total2 + OreDouble
  
    result = result..'||style="text-align:right"|+'..OreDouble..'%'
+
result = result..'||style="text-align:right"|+'..OreDouble..'%'
    result = result..'||style="text-align:right"|+'..total2..'%'
+
result = result..'||style="text-align:right"|+'..total2..'%'
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  return result
+
return result
 
end
 
end
  
 
function p.getRodTable(frame)
 
function p.getRodTable(frame)
  local toolArray = {}
+
local toolArray = {}
  for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
+
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
    if Shared.contains(upgrade.name, 'Fishing Rod') then
+
if Shared.contains(upgrade.name, 'Fishing Rod') then
      table.insert(toolArray, upgrade)
+
table.insert(toolArray, upgrade)
    end
+
end
  end
+
end
  
  local result = '{| class="wikitable"'
+
local result = '{| class="wikitable"'
  result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
+
result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
  result = result..'\r\n|- class="headerRow-0"'
+
result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
+
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
  result = result..'!!Cost!!This Rod!!Total'
+
result = result..'!!Cost!!This Rod!!Total'
  
  local total = 0
+
local total = 0
  
  for i, tool in Shared.skpairs(toolArray) do
+
for i, tool in Shared.skpairs(toolArray) do
    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" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
    result = result..'||'..tool.name
+
result = result..'||'..tool.name
    local level = 1
+
local level = 1
    if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
+
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
+
--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]
+
level = tool.unlockRequirements.skillLevel[1][2]
    end
+
end
    result = result..'||style="text-align:right"|'..level
+
result = result..'||style="text-align:right"|'..level
    result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
+
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]
+
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
    total = total + cutTime
+
total = total + cutTime
    result = result..'||style="text-align:right"|-'..cutTime..'%'
+
result = result..'||style="text-align:right"|-'..cutTime..'%'
    result = result..'||style="text-align:right"|-'..total..'%'
+
result = result..'||style="text-align:right"|-'..total..'%'
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  return result
+
return result
 
end
 
end
  
 
function p.getTreesTable(frame)
 
function p.getTreesTable(frame)
  local result = '{| class="wikitable sortable"'
+
local result = '{| class="wikitable sortable"'
  result = result..'\r\n|- class="headerRow-0"'
+
result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!colspan="2"|Tree!!colspan="2"|Logs!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
+
result = result..'\r\n!colspan="2"|Tree!!colspan="2"|Logs!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
  result = result..'!!XP!!Cut Time!!XP/s!!GP/s'
+
result = result..'!!XP!!Cut Time!!XP/s!!GP/s'
  
  for i, tree in Shared.skpairs(SkillData.Woodcutting.Trees) do
+
for i, tree in Shared.skpairs(SkillData.Woodcutting.Trees) do
    result = result..'\r\n|-'
+
result = result..'\r\n|-'
    local treeName = Shared.titleCase(tree.type..' tree')
+
local treeName = Shared.titleCase(tree.type..' tree')
    local logName = Shared.titleCase(tree.type..' logs')
+
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..'\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..'||'..treeName..''
    result = result..'||style="min-width:25px" data-sort-value="'..logName..'"|'..Icons.Icon({logName, type='item', notext=true, size=50})
+
result = result..'||style="min-width:25px" data-sort-value="'..logName..'"|'..Icons.Icon({logName, type='item', notext=true, size=50})
    result = result..'||[['..logName..']]'
+
result = result..'||[['..logName..']]'
    result = result..'||style="text-align:right"|'..tree.level
+
result = result..'||style="text-align:right"|'..tree.level
    result = result..'||style="text-align:right"|'..tree.xp
+
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)
+
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 XPs = tree.xp / (tree.interval / 1000)
    local Log = Items.getItemByID(i - 1)
+
local Log = Items.getItemByID(i - 1)
    local GPs = Log.sellsFor / (tree.interval / 1000)
+
local GPs = Log.sellsFor / (tree.interval / 1000)
    result = result..'||style="text-align:right"|'..Shared.round(XPs, 2, 2)
+
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))
+
result = result..'||style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2))
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  return result
+
return result
 
end
 
end
  
 
function p.getSpecialFishingTable(frame)
 
function p.getSpecialFishingTable(frame)
  local lootValue = 0
+
local lootValue = 0
  local totalWt = Items.specialFishWt
+
local totalWt = Items.specialFishWt
  
  local result = ''
+
local result = ''
  result = result..'\r\n{|class="wikitable sortable stickyHeader"'
+
result = result..'\r\n{|class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
+
result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item'
+
result = result..'\r\n!Item'
  result = result..'!!Price!!colspan="2"|Chance'
+
result = result..'!!Price!!colspan="2"|Chance'
  
  --Sort the loot table by weight in descending order
+
--Sort the loot table by weight in descending order
  table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
+
table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
  for i, row in pairs(Items.specialFishLoot) do
+
for i, row in pairs(Items.specialFishLoot) do
    local thisItem = Items.getItemByID(row[1])
+
local thisItem = Items.getItemByID(row[1])
    result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
+
result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
    result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
+
result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
    result = result..'|'..Icons.GP(thisItem.sellsFor)
+
result = result..'|'..Icons.GP(thisItem.sellsFor)
  
    local dropChance = (row[2] / totalWt) * 100
+
local dropChance = (row[2] / totalWt) * 100
    result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
+
result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
    result = result..'|'..Shared.fraction(row[2], totalWt)
+
result = result..'|'..Shared.fraction(row[2], totalWt)
    result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
+
result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
    lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
+
lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
  end
+
end
  result = result..'\r\n|}'
+
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))
+
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
+
return result
 
end
 
end
  
 
function p.getFishingJunkTable(frame)
 
function p.getFishingJunkTable(frame)
  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!colspan="2"|Item!!Value'
+
result = result..'\r\n!colspan="2"|Item!!Value'
 
+
  local itemArray = Items.getItems(function(item) return item.type == "Junk" end)
+
local itemArray = Items.getItems(function(item) return item.type == "Junk" end)
  
  table.sort(itemArray, function(a, b) return a.name < b.name end)
+
table.sort(itemArray, function(a, b) return a.name < b.name end)
  
  for i, item in Shared.skpairs(itemArray) do
+
for i, item in Shared.skpairs(itemArray) do
    result = result..'\r\n|-'
+
result = result..'\r\n|-'
    result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext='true', size='50'})..'||[['..item.name..']]'
+
result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext='true', size='50'})..'||[['..item.name..']]'
    result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
+
result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  
  return result
+
return result
 
end
 
end
  
 
function p.getMiningOresTable(frame)
 
function p.getMiningOresTable(frame)
  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!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
+
result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
  result = result..'!!XP!!Respawn Time!!Ore Value'
+
result = result..'!!XP!!Respawn Time!!Ore Value'
  
  local mineData = Shared.clone(SkillData.Mining.Rocks)
+
local mineData = Shared.clone(SkillData.Mining.Rocks)
  
  table.sort(mineData, function(a, b) return a.level < b.level end)
+
table.sort(mineData, function(a, b) return a.level < b.level end)
  
  for i, oreData in Shared.skpairs(mineData) do
+
for i, oreData in Shared.skpairs(mineData) do
    local ore = Items.getItemByID(oreData.ore)
+
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})..'||[['..ore.name..']]'
+
result = result..'\r\n|-\r\n|style="min-width:25px"|'..Icons.Icon({ore.name, type='item', size='50', notext=true})..'||[['..ore.name..']]'
    result = result..'||style="text-align:right"|'..oreData.level..'||style="text-align:right"|'..ore.miningXP
+
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..'||style="text-align:right" data-sort-value="'..oreData.respawnInterval..'"|'
    result = result..Shared.timeString(oreData.respawnInterval / 1000, true)
+
result = result..Shared.timeString(oreData.respawnInterval / 1000, true)
    result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
+
result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  return result
+
return result
 
end
 
end
  
 
function p.getMiningGemsTable(frame)
 
function p.getMiningGemsTable(frame)
  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!colspan=2|Gem!!Gem Chance!!Gem Price'
+
result = result..'\r\n!colspan=2|Gem!!Gem Chance!!Gem Price'
  
  -- Sort gems by ID order
+
-- Sort gems by ID order
  for i, gemData in Shared.spairs(Items.GemTable, function(t,a,b) return t[a].id < t[b].id end) do
+
for i, gemData in Shared.spairs(Items.GemTable, function(t,a,b) return t[a].id < t[b].id end) do
    local gem = Items.getItemByID(gemData.id)
+
local gem = Items.getItemByID(gemData.id)
    result = result..'\r\n|-\r\n|style="min-width:25px"|'
+
result = result..'\r\n|-\r\n|style="min-width:25px"|'
    result = result..Icons.Icon({gem.name, type='item', size='50', notext=true})..'||[['..gem.name..']]'
+
result = result..Icons.Icon({gem.name, type='item', size='50', notext=true})..'||[['..gem.name..']]'
    result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.chance)
+
result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.chance)
    result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor)
+
result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor)
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  return result
+
return result
 
end
 
end
  
 
function p.getFishTable(frame)
 
function p.getFishTable(frame)
  local data = Items.getItems(function(item) return item.fishingID ~= nil end)
+
local data = Items.getItems(function(item) return item.fishingID ~= nil end)
  
  table.sort(data, function(a, b) return a.fishingID < b.fishingID end)
+
table.sort(data, function(a, b) return a.fishingID < b.fishingID end)
  
  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!Fish\r\n!Name\r\n!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level\r\n!Catch Time'
+
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!Experience\r\n!Fish Price\r\n!'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
+
result = result..'\r\n!Experience\r\n!Fish Price\r\n!'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
  
  for i, fish in Shared.skpairs(data) do
+
for i, fish in Shared.skpairs(data) do
    result = result..'\r\n|-'
+
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', size='50', notext=true})
    result = result..'\r\n| style ="text-align: left;" |[['..fish.name..']]'
+
result = result..'\r\n| style ="text-align: left;" |[['..fish.name..']]'
    result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel
+
result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel
  
    local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2
+
local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2
    local timeStr = string.format("%.1fs-%.1fs", (fish.minFishingInterval/1000), (fish.maxFishingInterval/1000))
+
local timeStr = string.format("%.1fs-%.1fs", (fish.minFishingInterval/1000), (fish.maxFishingInterval/1000))
    result = result..'\r\n| style="text-align:right" data-sort-value="'..timeSortVal..'"|'..timeStr
+
result = result..'\r\n| style="text-align:right" data-sort-value="'..timeSortVal..'"|'..timeStr
    result = result..'\r\n| style="text-align:right"|'..fish.fishingXP
+
result = result..'\r\n| style="text-align:right"|'..fish.fishingXP
    result = result..'\r\n| style="text-align:right"|'..fish.sellsFor
+
result = result..'\r\n| style="text-align:right"|'..fish.sellsFor
  
    local cookStr = "N/A"
+
local cookStr = "N/A"
    if fish.cookingLevel ~= nil then  
+
if fish.cookingLevel ~= nil then  
      cookStr = fish.cookingLevel
+
cookStr = fish.cookingLevel
    end
+
end
    result = result..'\r\n| style="text-align:right"|'..cookStr
+
result = result..'\r\n| style="text-align:right"|'..cookStr
  end
+
end
  
  result = result..'\r\n|}'
+
result = result..'\r\n|}'
  return result
+
return result
 
end
 
end
  
 
function p.getFishingAreasTable(frame)
 
function p.getFishingAreasTable(frame)
  
  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!Name\r\n!Fish\r\n!Fish Chance'
+
result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance'
  result = result..'\r\n!Junk Chance\r\n!Special Chance'
+
result = result..'\r\n!Junk Chance\r\n!Special Chance'
  
  for i, area in Shared.skpairs(SkillData.Fishing.Areas) do
+
for i, area in Shared.skpairs(SkillData.Fishing.Areas) do
    result = result..'\r\n|-'
+
result = result..'\r\n|-'
    result = result..'\r\n| style ="text-align: left;" |'..area.name
+
result = result..'\r\n| style ="text-align: left;" |'..area.name
  
    local fishArray = {}
+
local fishArray = {}
    for j, fish in Shared.skpairs(area.fish) do
+
for j, fish in Shared.skpairs(area.fish) do
      local fishTable = Items.getItems(function(item) return item.fishingID == fish end)
+
local fishTable = Items.getItems(function(item) return item.fishingID == fish end)
      local fishItem = fishTable[0] or fishTable[1]
+
local fishItem = fishTable[0] or fishTable[1]
      table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
+
table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
    end
+
end
    result = result..'\r\n|'..table.concat(fishArray, '<br />')
+
result = result..'\r\n|'..table.concat(fishArray, '<br />')
  
    result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%'
+
result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%'
    result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
+
result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
    result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
+
result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
  end
+
end
 +
 
 +
result = result..'\r\n|}'
 +
return result
 +
end
 +
 
 +
function p.getThievingNPC(npcName)
 +
local result = nil
 +
for i, npc in Shared.skpairs(SkillData.Thieving.NPCs) do
 +
if npc.name == npcName then
 +
result = Shared.clone(npc)
 +
break
 +
end
 +
end
 +
return result
 +
end
 +
 
 +
function p.getThievingNPCArea(npc)
 +
if type(npc) == 'string' then
 +
npc = p.getThievingNPC(npc)
 +
end
 +
 +
local result = nil
 +
for i, area in Shared.skpairs(SkillData.Thieving.Areas) do
 +
for j, npcID in pairs(area.npcs) do
 +
if npcID == npc.id then
 +
result = area
 +
break
 +
end
 +
end
 +
end
 +
return result
 +
end
 +
 
 +
function p._getThievingNPCLootTables(npc)
 +
local result = ''
 +
local sectionTxt = {}
 +
 +
--Five sections here: GP, normal loot, area loot, rare loot, and unique item
 +
--First up, GP:
 +
local gpTxt = 'Successfully pickpocketing '..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{|class="wikitable sortable"'
 +
rareTxt = rareTxt..'\r\n!Item!!Qty'
 +
rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
 +
for i, drop in pairs(SkillData.Thieving.RareItems) do
 +
local thisItem = Items.getItemByID(drop.itemID)
 +
local odds = drop.chance
 +
local roundOdds = math.floor(odds * 10000)
 +
 +
rareTxt = rareTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
 +
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
 +
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(roundOdds, 1000000)
 +
rareTxt = rareTxt..'||'..Shared.round(odds, 2, 2)..'%'
 +
end
 +
rareTxt = rareTxt..'\r\n|}'
 +
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..'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 areaDropChance = thievingAreaLootChance / dropCount
 +
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(areaDropChance, 100)
 +
lineTxt = lineTxt..'||'..Shared.round(areaDropChance, 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(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
  
  result = result..'\r\n|}'
+
function p.getThievingNPCLootTables(frame)
  return result
+
local npcName = frame.args ~= nil and frame.args[1] or frame
 +
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
 
end
  
 
return p
 
return p

Revision as of 20:19, 30 September 2021

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

--Splitting some functions into here to avoid bloating a single file
local p = {}

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 Items = require('Module:Items')
local Icons = require('Module:Icons')

local thievingNormalLootChance = 75
local thievingAreaLootChance = 0.2

function p.getAxeTable(frame)
	local toolArray = {}
	for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
		if Shared.contains(upgrade.name, 'Axe') then
			table.insert(toolArray, upgrade)
		end
	end

	local result = '{| class="wikitable"'
	result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
	result = result..'!!Cost!!This Axe!!Total'

	local total = 0

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

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

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

	local result = '{| class="wikitable"'
	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!colspan="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
	result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'

	local total = 0
	local total2 = 0

	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
		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..'%'

		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

	local result = '{| class="wikitable"'
	result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
	result = result..'!!Cost!!This Rod!!Total'

	local total = 0

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

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

function p.getTreesTable(frame)
	local result = '{| class="wikitable sortable"'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!colspan="2"|Tree!!colspan="2"|Logs!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
	result = result..'!!XP!!Cut Time!!XP/s!!GP/s'

	for i, tree in Shared.skpairs(SkillData.Woodcutting.Trees) do
		result = result..'\r\n|-'
		local treeName = Shared.titleCase(tree.type..' tree')
		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..'||[['..logName..']]'
		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
	table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
	for i, row in pairs(Items.specialFishLoot) do
		local thisItem = Items.getItemByID(row[1])
		result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
		result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
		result = result..'|'..Icons.GP(thisItem.sellsFor)

		local dropChance = (row[2] / totalWt) * 100
		result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
		result = result..'|'..Shared.fraction(row[2], totalWt)
		result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
		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
end

function p.getFishingJunkTable(frame)
	local result = '{| class="wikitable sortable stickyHeader"'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!colspan="2"|Item!!Value'
	
	local itemArray = Items.getItems(function(item) return item.type == "Junk" end)

	table.sort(itemArray, function(a, b) return a.name < b.name end)

	for i, item in Shared.skpairs(itemArray) do
		result = result..'\r\n|-'
		result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext='true', size='50'})..'||[['..item.name..']]'
		result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
	end

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

	return result
end

function p.getMiningOresTable(frame)
	local result = '{|class="wikitable sortable stickyHeader"'
	result = result..'\r\n|- class="headerRow-0"'
	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})..'||[['..ore.name..']]'
		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

function p.getMiningGemsTable(frame)
	local result = '{|class="wikitable sortable stickyHeader"'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!colspan=2|Gem!!Gem Chance!!Gem Price'

	-- Sort gems by ID order
	for i, gemData in Shared.spairs(Items.GemTable, function(t,a,b) return t[a].id < t[b].id end) do
		local gem = Items.getItemByID(gemData.id)
		result = result..'\r\n|-\r\n|style="min-width:25px"|'
		result = result..Icons.Icon({gem.name, type='item', size='50', notext=true})..'||[['..gem.name..']]'
		result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.chance)
		result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor)
	end

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

function p.getFishTable(frame)
	local data = Items.getItems(function(item) return item.fishingID ~= nil end)

	table.sort(data, function(a, b) return a.fishingID < b.fishingID end)

	local result = '{| class="wikitable sortable stickyHeader"'
	result = result..'\r\n|- class="headerRow-0"'
	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!Experience\r\n!Fish Price\r\n!'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'

	for i, fish in Shared.skpairs(data) do
		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;" |[['..fish.name..']]'
		result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel

		local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2
		local timeStr = string.format("%.1fs-%.1fs", (fish.minFishingInterval/1000), (fish.maxFishingInterval/1000))
		result = result..'\r\n| style="text-align:right" data-sort-value="'..timeSortVal..'"|'..timeStr
		result = result..'\r\n| style="text-align:right"|'..fish.fishingXP
		result = result..'\r\n| style="text-align:right"|'..fish.sellsFor

		local cookStr = "N/A"
		if fish.cookingLevel ~= nil then 
			cookStr = fish.cookingLevel
		end
		result = result..'\r\n| style="text-align:right"|'..cookStr
	end

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

function p.getFishingAreasTable(frame)

	local result = '{| class="wikitable sortable stickyHeader"'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance'
	result = result..'\r\n!Junk Chance\r\n!Special Chance'

	for i, area in Shared.skpairs(SkillData.Fishing.Areas) do
		result = result..'\r\n|-'
		result = result..'\r\n| style ="text-align: left;" |'..area.name

		local fishArray = {}
		for j, fish in Shared.skpairs(area.fish) do
			local fishTable = Items.getItems(function(item) return item.fishingID == fish end)
			local fishItem = fishTable[0] or fishTable[1]
			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..'%'
		result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
		result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
	end

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

function p.getThievingNPC(npcName)
	local result = nil
	for i, npc in Shared.skpairs(SkillData.Thieving.NPCs) do
		if npc.name == npcName then
			result = Shared.clone(npc)
			break
		end
	end
	return result
end

function p.getThievingNPCArea(npc)
	if type(npc) == 'string' then
		npc = p.getThievingNPC(npc)
	end
	
	local result = nil
	for i, area in Shared.skpairs(SkillData.Thieving.Areas) do
		for j, npcID in pairs(area.npcs) do
			if npcID == npc.id then
				result = area
				break
			end
		end
	end
	return result
end

function p._getThievingNPCLootTables(npc)
	local result = ''
	local sectionTxt = {}
	
	--Five sections here: GP, normal loot, area loot, rare loot, and unique item
	--First up, GP:
	local gpTxt = 'Successfully pickpocketing '..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{|class="wikitable sortable"'
	rareTxt = rareTxt..'\r\n!Item!!Qty'
	rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
	for i, drop in pairs(SkillData.Thieving.RareItems) do
		local thisItem = Items.getItemByID(drop.itemID)
		local odds = drop.chance
		local roundOdds = math.floor(odds * 10000)
		
		rareTxt = rareTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
		rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
		rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(roundOdds, 1000000)
		rareTxt = rareTxt..'||'..Shared.round(odds, 2, 2)..'%'
	end
	rareTxt = rareTxt..'\r\n|}'
	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..'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 areaDropChance = thievingAreaLootChance / dropCount
	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(areaDropChance, 100)
		lineTxt = lineTxt..'||'..Shared.round(areaDropChance, 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(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)
	local npcName = frame.args ~= nil and frame.args[1] or frame
	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

return p