Anonymous

Module:Skills/Gathering: Difference between revisions

From Melvor Idle
Move functions to Module:Skills; Move Farming functions from Module:Skills
(getFishingAreasTable)
(Move functions to Module:Skills; Move Farming functions from Module:Skills)
(48 intermediate revisions by 4 users not shown)
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 Skills = require('Module:Skills')
local ItemSourceTables = require('Module:Items/SourceTables')


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..'||'..Icons.Icon({logName, type='item', noicon=true})
    result = result..'||style="text-align:right"|'..tree.level
result = result..'||style="text-align:right"|'..tree.levelRequired
    result = result..'||style="text-align:right"|'..tree.xp
result = result..'||style="text-align:right"|'..tree.baseExperience
    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.baseInterval..'"|'..Shared.timeString(tree.baseInterval/1000, true)
    local XPs = tree.xp / (tree.interval / 1000)
local XPs = tree.baseExperience / (tree.baseInterval / 1000)
    local Log = Items.getItemByID(i - 1)
local Log = Items.getItemByID(tree.logID)
    local GPs = Log.sellsFor / (tree.interval / 1000)
local GPs = Log.sellsFor / (tree.baseInterval / 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 totalWt, lootValue = 0, 0
  local totalWt = Items.specialFishWt
local itemArray = Shared.clone(SkillData.Fishing.SpecialItems)
for i, itemDef in ipairs(itemArray) do
totalWt = totalWt + itemDef[2]
end
-- Sort the loot table by weight in descending order
table.sort(itemArray, function(a, b) return (a[2] == b[2] and a[1] < b[1]) or a[2] > b[2] end)


  local result = ''
local resultPart = {}
  result = result..'\r\n{|class="wikitable sortable stickyHeader"'
table.insert(resultPart, '\r\n{|class="wikitable sortable stickyHeader"')
  result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '\r\n|- class="headerRow-0"\r\n!colspan="2"| Item\r\n!Value\r\n!colspan="2"|Chance')
  result = result..'\r\n!Item'
for i, itemDef in ipairs(itemArray) do
  result = result..'!!Price!!colspan="2"|Chance'
local item = Items.getItemByID(itemDef[1])
if item ~= nil then
local dropChance = itemDef[2] / totalWt * 100
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
table.insert(resultPart, '\r\n|-\r\n|style="text-align:center"| ' .. Icons.Icon({item.name, type='item', notext=true}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. itemDef[2] .. '"| ' .. Shared.fraction(itemDef[2], totalWt))
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. string.format(fmt, dropChance) .. '%')
lootValue = lootValue + (dropChance / 100 * item.sellsFor)
end
end
table.insert(resultPart, '\r\n|}\r\nThe average value of a roll on the special fishing loot table is ' .. Icons.GP(Shared.round(lootValue, 2, 0)))
return table.concat(resultPart)
end
 
function p.getFishingJunkTable(frame)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!colspan="2"|Item!!Value')
 
local itemArray = {}
for i, itemID in ipairs(SkillData.Fishing.JunkItems) do
local item = Items.getItemByID(itemID)
if item ~= nil then
table.insert(itemArray, item)
end
end
table.sort(itemArray, function(a, b) return a.name < b.name end)
 
for i, item in ipairs(itemArray) do
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="min-width:25px"| ' .. Icons.Icon({item.name, type='item', notext=true, size=50}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
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.levelRequired < b.levelRequired end)
 
for i, oreData in Shared.skpairs(mineData) do
local ore = Items.getItemByID(oreData.oreID)
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.levelRequired..'||style="text-align:right"|'..oreData.baseExperience
result = result..'||style="text-align:right" data-sort-value="'..oreData.baseRespawnInterval..'"|'
result = result..Shared.timeString(oreData.baseRespawnInterval / 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})
result = result..'||'..Icons.Icon({gem.name, type='item', noicon=true})
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 recipeList = {}
for i, recipe in ipairs(SkillData.Fishing.Fish) do
table.insert(recipeList, recipe)
end
table.sort(recipeList, function(a, b) return (a.level == b.level and a.masteryID < b.masteryID) or a.level < b.level end)
 
-- Determine cooking levels for all fish
local cookReq = {}
for i, recipe in ipairs(SkillData.Cooking.Recipes) do
-- This assumes that each raw fish only appears in a single recipe, which is a bit rubbish
-- but currently holds
for j, mat in ipairs(recipe.itemCosts) do
if cookReq[mat.id] == nil then
cookReq[mat.id] = recipe.level
end
end
end
 
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!Fish\r\n!Name\r\n!' .. Icons.Icon({'Fishing', type='skill', notext=true}) .. ' Level\r\n!Catch Time')
table.insert(resultPart, '\r\n!XP\r\n!Value\r\n!XP/s\r\n!GP/s')
table.insert(resultPart, '\r\n!' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' Level')
for i, recipe in ipairs(recipeList) do
local fish = Items.getItemByID(recipe.itemID)
if fish ~= nil then
local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000
local timeStr = string.format("%.1fs - %.1fs", recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000)
local XPs = recipe.baseXP / timeSortVal
local GPs = fish.sellsFor / timeSortVal
local cookSortVal = cookReq[recipe.itemID] or 0
local cookStr = cookReq[recipe.itemID] or 'N/A'
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="text-align:center"| ' .. Icons.Icon({fish.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({fish.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. recipe.level)
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. timeSortVal .. '"| ' .. timeStr)
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. recipe.baseXP)
table.insert(resultPart, '\r\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Icons.GP(fish.sellsFor))
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. Shared.round(XPs, 2, 2))
table.insert(resultPart, '\r\n|data-sort-value="' .. GPs .. '"|' .. Icons.GP(Shared.round(GPs, 2, 2)))
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. cookSortVal .. '"| ' .. cookStr)
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
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 ipairs(SkillData.Fishing.Areas) do
result = result..'\r\n|-'
result = result..'\r\n| style ="text-align: left;" |'..area.name
 
local fishArray = {}
for j, fish in ipairs(area.fish) do
local fishItem = Items.getItemByID(fish.itemID)
if fishItem ~= nil then
table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
end
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.getThievingGeneralRareTable(frame)
local rareTxt = '{|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
 
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(1, Shared.round2(1/(odds/100), 0))
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)
local result = ''
local sectionTxt = {}
 
--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 = SkillData.Thieving.ItemChance
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 Shared.tableCount(npc.lootTable) > 1 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"|'..Shared.round(lootChance, 2, 2)..'%'
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 = Skills.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/(SkillData.Thieving.AreaUniqueChance/100))
lineTxt = lineTxt..'||'..Shared.round(SkillData.Thieving.AreaUniqueChance, 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/(SkillData.Thieving.AreaUniqueChance/100))..'||'
areaTxt = areaTxt..'style="text-align:right"|'..Shared.round(SkillData.Thieving.AreaUniqueChance, 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 = Skills.getThievingNPC(npcName)
if npc == nil then
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
end
 
return p._getThievingNPCLootTables(npc)
end


  --Sort the loot table by weight in descending order
function p.getThievingNPCTable()
  table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
local result = '{| class="wikitable sortable stickyHeader"'
  for i, row in pairs(Items.specialFishLoot) do
result = result..'\r\n|- class="headerRow-0"'
    local thisItem = Items.getItemByID(row[1])
result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!GP!!Unique Drop'
    result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
local npcArray = Shared.clone(SkillData.Thieving.NPCs)
    result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
table.sort(npcArray, function(a, b) return a.level < b.level end)
    result = result..'|'..Icons.GP(thisItem.sellsFor)
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 dropChance = (row[2] / totalWt) * 100
local area = Skills.getThievingNPCArea(npc)
    result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
result = result..'||'..area.name
    result = result..'|'..Shared.fraction(row[2], totalWt)
result = result..'||'..Icons._SkillReq('Thieving', npc.level)
    result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
result = result..'||style="text-align:right"|'..npc.xp
    lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
result = result..'||style="text-align:right"|'..(npc.maxHit * 10)
  end
result = result..'||style="text-align:right"|'..npc.perception
  result = result..'\r\n|}'
result = result..'||data-sort-value="' .. npc.maxGP .. '"|'..Icons.GP(1, npc.maxGP)
  result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0))
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
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
return result
end
end


function p.getFishingJunkTable(frame)
function p.getThievingAreaTable(frame)
  local result = '{| class="wikitable sortable stickyHeader"'
local resultPart = {}
  result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
  result = result..'\r\n!colspan="2"|Item!!Value'
table.insert(resultPart, '\r\n|- class="headerRow-0"')
 
table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')
  local itemArray = Items.getItems(function(item) return item.type == "Junk" end)


  table.sort(itemArray, function(a, b) return a.name < b.name end)
local areaArray = Shared.clone(SkillData.Thieving.Areas)
table.sort(areaArray, function(a, b) return a.id < b.id end)
for i, area in ipairs(areaArray) do
local minLevel, npcList, areaItemList = nil, {}, {}
-- 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


  for i, item in Shared.skpairs(itemArray) do
-- Build area unique item list
    result = result..'\r\n|-'
if area.uniqueDrops ~= nil and Shared.tableCount(area.uniqueDrops) > 0 then
    result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext='true', size='50'})..'||[['..item.name..']]'
for k, drop in ipairs(area.uniqueDrops) do
    result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
local areaItem = Items.getItemByID(drop.itemID)
  end
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


  result = result..'\r\n|}'
-- 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 result
return table.concat(resultPart)
end
end


function p.getMiningOresTable(frame)
function p._getFarmingTable(category)
  local result = '{|class="wikitable sortable stickyHeader"'
local seedList = {}
  result = result..'\r\n|- class="headerRow-0"'
if category == 'Allotment' or category == 'Herb' or category == 'Tree' then
  result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
seedList = Items.getItems(function(item) return item.tier == category end)
  result = result..'!!XP!!Respawn Time!!Ore Value'
else
return 'ERROR: Invalid farming category. Please choose Allotment, Herb, or Tree'
end
 
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan=2|Seeds!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level'
result = result..'!!XP!!Growth Time!!Seed Value'
if category == 'Allotment' then
result = result..'!!colspan="2"|Crop!!Crop Healing!!Crop Value'
elseif category == 'Herb' then
result = result..'!!colspan="2"|Herb!!Herb Value'
elseif category == 'Tree' then
result = result..'!!colspan="2"|Logs!!Log Value'
end
result = result..'!!Seed Sources'
 
table.sort(seedList, function(a, b) return a.farmingLevel < b.farmingLevel end)
 
for i, seed in pairs(seedList) do
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({seed.name, type='item', size='50', notext=true})..'||[['..seed.name..']]'
result = result..'||'..seed.farmingLevel..'||'..Shared.formatnum(seed.farmingXP)
result = result..'||data-sort-value="'..seed.timeToGrow..'"|'..Shared.timeString(seed.timeToGrow, true)
result = result..'||data-sort-value="'..seed.sellsFor..'"|'..Icons.GP(seed.sellsFor)


  local mineData = Shared.clone(SkillData.Mining.Rocks)
local crop = Items.getItemByID(seed.grownItemID)
result = result..'||'..Icons.Icon({crop.name, type='item', size='50', notext=true})..'||[['..crop.name..']]'
if category == 'Allotment' then
result = result..'||'..Icons.Icon({'Hitpoints', type='skill', notext=true})..' '..(crop.healsFor * 10)
end
result = result..'||data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
result = result..'||'..ItemSourceTables._getItemSources(seed)
end


  table.sort(mineData, function(a, b) return a.level < b.level end)
result = result..'\r\n|}'
return result
end


  for i, oreData in Shared.skpairs(mineData) do
function p.getFarmingTable(frame)
    local ore = Items.getItemByID(oreData.ore)
local category = frame.args ~= nil and frame.args[1] or frame
    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 p._getFarmingTable(category)
  return result
end
end


function p.getFishTable(frame)
function p.getFarmingFoodTable(frame)
  local data = Items.getItems(function(item) return item.fishingID ~= nil end)
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Crop!!'..Icons.Icon({"Farming", type="skill", notext=true})..' Level'
result = result..'!!Healing!!Value'
 
local itemArray = Items.getItems(function(item) return item.grownItemID ~= nil end)
 
table.sort(itemArray, function(a, b) return a.farmingLevel < b.farmingLevel end)
 
for i, item in Shared.skpairs(itemArray) do
local crop = Items.getItemByID(item.grownItemID)
if crop.healsFor ~= nil and crop.healsFor > 0 then
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({crop.name, type='item', notext='true', size='50'})..'||[['..crop.name..']]'
result = result..'||style="text-align:right;"|'..item.farmingLevel
result = result..'||style="text-align:right" data-sort-value="'..crop.healsFor..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..(crop.healsFor * 10)
result = result..'||style="text-align:right" data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
end
end


  table.sort(data, function(a, b) return a.fishingID < b.fishingID end)
result = result..'\r\n|}'


  local result = '{| class="wikitable sortable stickyHeader"'
return result
  result = result..'\r\n|- class="headerRow-0"'
end
  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
function p.getFarmingPlotTable(frame)
    result = result..'\r\n|-'
local areaName = frame.args ~= nil and frame.args[1] or frame
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', size='50', notext=true})
local patches = nil
    result = result..'\r\n| style ="text-align: left;" |[['..fish.name..']]'
for i, area in Shared.skpairs(SkillData.Farming.Patches) do
    result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel
if area.areaName == areaName then
patches = area.patches
break
end
end
if patches == nil then
return "ERROR: Invalid area name.[[Category:Pages with script errors]]"
end


    local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2
local result = '{|class="wikitable"'
    local timeStr = string.format("%.1fs-%.1fs", (fish.minFishingInterval/1000), (fish.maxFishingInterval/1000))
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'
    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"
for i, patch in Shared.skpairs(patches) do
    if fish.cookingLevel ~= nil then  
result = result..'\r\n|-\r\n|'..i
      cookStr = fish.cookingLevel
result = result..'||style="text-align:right;" data-sort-value="' .. patch.level .. '"|'..patch.level
    end
if patch.cost == 0 then
    result = result..'\r\n| style="text-align:right"|'..cookStr
result = result..'||Free'
  end
else
result = result..'||style="text-align:right;" data-sort-value="'..patch.cost..'"|'..Icons.GP(patch.cost)
end
end


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


function p.getFishingAreasTable(frame)
function p._buildAstrologyConstellationTable()
local maxModifier = 5
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Constellation!!'..Icons.Icon({"Astrology", type='skill', notext='true'})..' Level'
result = result..'!!XP!!Skills!!Standard Modifiers!!Unique Modifiers'
 
for i, cons in ipairs(SkillData.Astrology.Constellations) do
local name = cons.name
result = result..'\r\n|-'
result = result..'\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='constellation', size='50', notext=true})..'||'..name
result = result..'||'..cons.level..'||'..cons.provides.xp
 
local skillIconArray = {}
for j, skillID in ipairs(cons.skills) do
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
end
result = result..'||'..table.concat(skillIconArray, '<br/>')


  local result = '{| class="wikitable sortable stickyHeader"'
local standModsRaw = Skills._buildAstrologyModifierArray(cons, maxModifier, true, false, false, false)
  result = result..'\r\n|- class="headerRow-0"'
local standMods = {}
  result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance'
--Building the list of Standard modifiers:
  result = result..'\r\n!Junk Chance\r\n!Special Chance'
for j, modifier in ipairs(standModsRaw) do
table.insert(standMods, Constants._getModifierText(modifier[1], modifier[2], false))
end
result = result..'|| '..table.concat(standMods, '<br/>')


  for i, area in Shared.skpairs(SkillData.Fishing.Areas) do
--Building the list of all Unique Modifiers
    result = result..'\r\n|-'
local uModsRaw = Skills._buildAstrologyModifierArray(cons, maxModifier, false, true, false, false)
    result = result..'\r\n| style ="text-align: left;" |'..area.name
local uMods = {}
for j, modifier in ipairs(uModsRaw) do
table.insert(uMods, Constants._getModifierText(modifier[1], modifier[2], false))
end
result = result..'||'..table.concat(uMods, '<br/>')
end
result = result..'\r\n|}'


    local fishArray = {}
return result
    for j, fish in Shared.skpairs(area.fish) do
end
      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, '\r\n')


    result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%'
function p.buildAstrologyConstellationTable(frame)
    result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
return p._buildAstrologyConstellationTable()
    result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
end
  end


  result = result..'\r\n|}'
function p.buildAstrologyValueTable()
  return result
local result = '{|class="wikitable sortable"'
result = result..'\r\n!rowspan="2"| Value!!colspan="2"| Chance'
result = result..'\r\n|-\r\n! This Value!! This Value or Greater'
local cumulativeChance = 100
for i, chance in ipairs(SkillData.Astrology.ModifierMagnitudeChances) do
result = result..'\r\n|-'
result = result..'\r\n|style="text-align:right"| ' .. i
result = result..'\r\n|style="text-align:right"| ' .. chance .. '%'
result = result..'\r\n|style="text-align:right"| ' .. cumulativeChance .. '%'
cumulativeChance = cumulativeChance - chance
end
result = result..'\r\n|}'
return result
end
end


return p
return p