Module:Skills/Gathering: Difference between revisions

Left align sources column
(Corrected the area drop chance for items to reflect ability to roll each item separately. Also added getThievingSourcesForItem and canItemBeStolen for use in Items/SourceTables)
(Left align sources column)
 
(87 intermediate revisions by 6 users not shown)
Line 1: Line 1:
--Splitting some functions into here to avoid bloating a single file
--Splitting some functions into here to avoid bloating a single file
local p = {}
local p = {}
local SkillData = mw.loadData('Module:Skills/data')
local ShopData = mw.loadData('Module:Shop/data')


local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Common = require('Module:Common')
local Common = require('Module:Common')
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')


local thievingNormalLootChance = 75
function p.getRecipeRequirements(skillName, recipe)
local thievingAreaLootChance = 0.2
local reqText = {}
 
if recipe.level ~= nil then
function p.getAxeTable(frame)
table.insert(reqText, Icons._SkillReq(skillName, recipe.level, false))
local toolArray = {}
end
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
if recipe.totalMasteryRequired ~= nil then
if Shared.contains(upgrade.name, 'Axe') then
table.insert(reqText, Shared.formatnum(recipe.totalMasteryRequired) .. ' ' .. Icons.Icon({skillName, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'}))
table.insert(toolArray, upgrade)
end
if type(recipe.requirements) == 'table' then
local reqs = Common.getRequirementString(recipe.requirements)
if reqs ~= nil then
table.insert(reqText, reqs)
end
end
end
end
return table.concat(reqText, '<br/>')
end


local result = '{| class="wikitable"'
function p.getTreesTable(frame)
result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
local resultPart = {}
result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '{| class="wikitable sortable"')
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
table.insert(resultPart, '\n|- class="headerRow-0"')
result = result..'!!Cost!!This Axe!!Total'
table.insert(resultPart, '\n!colspan="2"|Tree!!colspan="2"|Logs!!Requirements')
 
table.insert(resultPart, '!!XP!!Cut Time!!XP/s!!GP/s')
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]
local trees = Shared.shallowClone(SkillData.Woodcutting.trees)
total = total + cutTime
table.sort(trees, function(a, b) return a.level < b.level end)
result = result..'||style="text-align:right"|-'..cutTime..'%'
for i, tree in ipairs(trees) do
result = result..'||style="text-align:right"|-'..total..'%'
local log = Items.getItemByID(tree.productId)
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..tree.name..'"| '..Icons.Icon({log.name, img=tree.name, type='tree', notext=true, size=50}))
table.insert(resultPart, '\n|data-sort-value="'..tree.name..'"|'..Icons.getExpansionIcon(tree.id)..tree.name)
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..log.name..'"| '..Icons.Icon({log.name, type='item', notext=true, size=50}))
table.insert(resultPart, '\n| '..Icons.Icon({log.name, type='item', noicon=true}))
table.insert(resultPart, '\n|data-sort-value="' .. tree.level .. '"| ' .. p.getRecipeRequirements(SkillData.Woodcutting.name, tree))
table.insert(resultPart, '\n|style="text-align:right"| '..tree.baseExperience)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..tree.baseInterval..'"| '..Shared.timeString(tree.baseInterval/1000, true))
local XPs = tree.baseExperience / (tree.baseInterval / 1000)
local GPs = log.sellsFor / (tree.baseInterval / 1000)
table.insert(resultPart, '\n|style="text-align:right"| '..Shared.round(XPs, 2, 2))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..GPs..'"| '..Icons.GP(Shared.round(GPs, 2, 2)))
end
end


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


function p.getPickaxeTable(frame)
function p.getSpecialFishingTable(frame)
local toolArray = {}
local totalWt, lootValue = 0, 0
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
local itemArray = Shared.shallowClone(SkillData.Fishing.specialItems)
if Shared.contains(upgrade.name, 'Pickaxe') then
for i, itemDef in ipairs(itemArray) do
table.insert(toolArray, upgrade)
totalWt = totalWt + itemDef.weight
end
end
end
-- Sort the loot table by weight in descending order
table.sort(itemArray, function(a, b) return a.weight > b.weight end)


local result = '{| class="wikitable"'
local resultPart = {}
result = result..'\r\n!colspan="4"| !!colspan="2"|Mine Time Decrease!!colspan="2"|2x Ore Chance'
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!colspan="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
for i, itemDef in ipairs(itemArray) do
result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'
local item = Items.getItemByID(itemDef.itemID)
 
if item ~= nil then
local total = 0
local dropChance = itemDef.weight / totalWt * 100
local total2 = 0
-- 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'
for i, tool in Shared.skpairs(toolArray) do
table.insert(resultPart, '\r\n|-\r\n|class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true}))
result = result..'\r\n|-'
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
result = result..'||'..tool.name
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. itemDef.weight .. '"| ' .. Shared.fraction(itemDef.weight, totalWt))
local level = 1
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. string.format(fmt, dropChance) .. '%')
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
lootValue = lootValue + (dropChance / 100 * item.sellsFor)
--Gonna be lazy and assume there's only the one skill level and it's the one we care about
level = tool.unlockRequirements.skillLevel[1][2]
end
end
result = result..'||style="text-align:right"|'..level
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
local OreDouble = tool.contains.modifiers.increasedChanceToDoubleOres
total2 = total2 + OreDouble
result = result..'||style="text-align:right"|+'..OreDouble..'%'
result = result..'||style="text-align:right"|+'..total2..'%'
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)))
result = result..'\r\n|}'
return table.concat(resultPart)
return result
end
end


function p.getRodTable(frame)
function p.getFishingJunkTable(frame)
local toolArray = {}
local resultPart = {}
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
if Shared.contains(upgrade.name, 'Fishing Rod') then
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(toolArray, upgrade)
table.insert(resultPart, '\r\n!colspan="2"|Item!!Value')
end
end


local result = '{| class="wikitable"'
local itemArray = {}
result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
for i, itemID in ipairs(SkillData.Fishing.junkItemIDs) do
result = result..'\r\n|- class="headerRow-0"'
local item = Items.getItemByID(itemID)
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
if item ~= nil then
result = result..'!!Cost!!This Rod!!Total'
table.insert(itemArray, item)
 
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
end
result = result..'||style="text-align:right"|'..level
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
end
end
table.sort(itemArray, function(a, b) return a.name < b.name end)


result = result..'\r\n|}'
for i, item in ipairs(itemArray) do
return result
table.insert(resultPart, '\r\n|-')
end
table.insert(resultPart, '\r\n|class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true, size=50}))
 
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
function p.getTreesTable(frame)
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
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
end
 
table.insert(resultPart, '\r\n|}')
result = result..'\r\n|}'
return table.concat(resultPart)
return result
end
end


function p.getSpecialFishingTable(frame)
function p.getMiningOresTable(frame)
local lootValue = 0
local resultPart = {}
local totalWt = Items.specialFishWt
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan="2"|Rock!!colspan=2|Ore!!Type!!Requirements')
table.insert(resultPart, '!!XP!!Respawn Time!!Ore Value')


local result = ''
local mineData = Shared.shallowClone(SkillData.Mining.rockData)
result = result..'\r\n{|class="wikitable sortable stickyHeader"'
table.sort(mineData, function(a, b) return a.level < b.level end)
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
for i, oreData in ipairs(mineData) do
table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
local ore = Items.getItemByID(oreData.productId)
for i, row in pairs(Items.specialFishLoot) do
local respawnStyle, respawnSort, respawnText = 'class="table-na"', 0, 'N/A'
local thisItem = Items.getItemByID(row[1])
if oreData.hasPassiveRegen then
result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
respawnStyle = 'style="text-align:right;'
result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
respawnSort = oreData.baseRespawnInterval / 1000
result = result..'|'..Icons.GP(thisItem.sellsFor)
respawnText = Shared.timeString(respawnSort, true)
 
end
local dropChance = (row[2] / totalWt) * 100
local rockName = Icons.Icon({oreData.name, type='rock', noicon=true, nolink=true})
result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
local qtyText = (oreData.baseQuantity > 1 and '<b>' .. oreData.baseQuantity .. 'x</b> ') or ''
result = result..'|'..Shared.fraction(row[2], totalWt)
result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
table.insert(resultPart, '\n|-\n|class="table-img" data-sort-value="' .. rockName .. '"| '..Icons.Icon({oreData.name, type='rock', size='50', notext=true, nolink=true}))
lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
table.insert(resultPart, '\n| data-sort-value="' ..rockName.. '"|'..Icons.getExpansionIcon(oreData.id) .. rockName)
table.insert(resultPart, '\n|class="table-img" data-sort-value="' .. ore.name .. '"| '..Icons.Icon({ore.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\n| ' .. qtyText .. Icons.Icon({ore.name, type='item', noicon=true}))
table.insert(resultPart, '\n| ' .. oreData.type)
table.insert(resultPart, '\n|data-sort-value="' .. oreData.level ..'"| '..p.getRecipeRequirements(SkillData.Mining.name, oreData))
table.insert(resultPart, '\n|style="text-align:right"| '..oreData.baseExperience)
table.insert(resultPart, '\n|' .. respawnStyle .. ' data-sort-value="'..respawnSort..'"| ' .. respawnText)
table.insert(resultPart, '\n|data-sort-value="'..ore.sellsFor..'"| '..Icons.GP(ore.sellsFor))
end
end
result = result..'\r\n|}'
result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0))


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


function p.getFishingJunkTable(frame)
function p._getMiningGemsTable(gemType)
local result = '{| class="wikitable sortable stickyHeader"'
if type(gemType) ~= 'string' then
result = result..'\r\n|- class="headerRow-0"'
gemType = 'Normal'
result = result..'\r\n!colspan="2"|Item!!Value'
end
local validTypes = {
["Normal"] = 'randomGems',
["Superior"] = 'randomSuperiorGems'
}
local gemDataKey = validTypes[gemType]
if gemDataKey == nil then
return Shared.printError('No such gem type "' .. gemType .. '"')
end
local itemArray = Items.getItems(function(item) return item.type == "Junk" end)
local gemData = GameData.rawData[gemDataKey]
 
local totalWeight = 0
table.sort(itemArray, function(a, b) return a.name < b.name end)
for i, gem in ipairs(gemData) do
 
totalWeight = totalWeight + gem.weight
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
end
local resultPart = {}
table.insert(resultPart, '{|class="wikitable sortable"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan=2|Gem!!Gem Chance!!Gem Price')


result = result..'\r\n|}'
for i, gem in ipairs(gemData) do
 
local gemItem = Items.getItemByID(gem.itemID)
return result
local gemPct = gem.weight / totalWeight * 100
end
table.insert(resultPart, '\n|-\n|class="table-img"| ')
 
table.insert(resultPart, Icons.Icon({gemItem.name, type='item', size='50', notext=true}))
function p.getMiningOresTable(frame)
table.insert(resultPart, '\n| data-sort-value="'..gemItem.name..'"|'..Icons.getExpansionIcon(gemItem.id) .. Icons.Icon({gemItem.name, type='item', noicon=true}))
local result = '{|class="wikitable sortable stickyHeader"'
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. gemPct .. '" | ' .. string.format("%.1f%%", gemPct))
result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '\n|data-sort-value="' .. gemItem.sellsFor .. '"| ' .. Icons.GP(gemItem.sellsFor))
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
end


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


function p.getMiningGemsTable(frame)
function p.getMiningGemsTable(frame)
local result = '{|class="wikitable sortable stickyHeader"'
local gemType = frame.args ~= nil and frame.args[1] or frame
result = result..'\r\n|- class="headerRow-0"'
return p._getMiningGemsTable(gemType)
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
end


function p.getFishTable(frame)
function p.getFishTable(frame)
local data = Items.getItems(function(item) return item.fishingID ~= nil end)
local recipeList = Shared.shallowClone(SkillData.Fishing.fish)
table.sort(recipeList, function(a, b) return a.level < b.level end)


table.sort(data, function(a, b) return a.fishingID < b.fishingID end)
-- Determine cooking levels for all fish
 
local cookReq = {}
local result = '{| class="wikitable sortable stickyHeader"'
for i, recipe in ipairs(SkillData.Cooking.recipes) do
result = result..'\r\n|- class="headerRow-0"'
-- This assumes that each raw fish only appears in a single recipe, which is a bit rubbish
result = result..'\r\n!Fish\r\n!Name\r\n!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level\r\n!Catch Time'
-- but currently holds
result = result..'\r\n!Experience\r\n!Fish Price\r\n!'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
for j, mat in ipairs(recipe.itemCosts) do
 
if cookReq[mat.id] == nil then
for i, fish in Shared.skpairs(data) do
cookReq[mat.id] = recipe.level
result = result..'\r\n|-'
end
result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', size='50', notext=true})
end
result = result..'\r\n| style ="text-align: left;" |[['..fish.name..']]'
end
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"
local resultPart = {}
if fish.cookingLevel ~= nil then
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
cookStr = fish.cookingLevel
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan="2" rowspan="2"|Fish\n!rowspan="2"|' .. Icons.Icon({'Fishing', type='skill', notext=true}) .. ' Level')
table.insert(resultPart, '\n!colspan="3"|Catch Time\n!rowspan="2"|XP\n!rowspan="2"|Value\n!rowspan="2"|XP/s\n!rowspan="2"|GP/s')
table.insert(resultPart, '\n!rowspan="2"|' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' Level')
table.insert(resultPart, '\n|- class="headerRow-1"\n!Min\n!Max\n!Avg')
for i, recipe in ipairs(recipeList) do
local fish = Items.getItemByID(recipe.productId)
if fish ~= nil then
local timeMin, timeMax = recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000
local timeAvg = (timeMin + timeMax) / 2
local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000
local timeStr = string.format("%.1fs - %.1fs", recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000)
local XPs = recipe.baseExperience / timeSortVal
local GPs = fish.sellsFor / timeSortVal
local cookSortVal = cookReq[recipe.productId] or 0
local cookStyle = (cookReq[recipe.productId] ~= nil and 'style="text-align:right" ' or 'class="table-na" ')
local cookStr = cookReq[recipe.productId] or 'N/A'
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|class="table-img"| ' .. Icons.Icon({fish.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\n|data-sort-value="'..fish.name..'"|'..Icons.getExpansionIcon(fish.id) .. Icons.Icon({fish.name, type='item', noicon=true}))
table.insert(resultPart, '\n|style="text-align:right"| ' .. recipe.level)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMin .. '"| ' .. string.format("%.1fs", timeMin))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMax .. '"| ' .. string.format("%.1fs", timeMax))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeAvg .. '"| ' .. string.format("%.1fs", timeAvg))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"| ' .. Shared.formatnum(recipe.baseExperience))
table.insert(resultPart, '\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Icons.GP(fish.sellsFor))
table.insert(resultPart, '\n|style="text-align:right"| ' .. Shared.round(XPs, 2, 2))
table.insert(resultPart, '\n|data-sort-value="' .. GPs .. '"|' .. Icons.GP(Shared.round(GPs, 2, 2)))
table.insert(resultPart, '\n|' .. cookStyle .. 'data-sort-value="' .. cookSortVal .. '"| ' .. cookStr)
end
end
result = result..'\r\n| style="text-align:right"|'..cookStr
end
end
 
table.insert(resultPart, '\n|}')
result = result..'\r\n|}'
return table.concat(resultPart)
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"'
Line 294: Line 252:
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 ipairs(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;" |'..Icons.getExpansionIcon(area.id)..area.name


local fishArray = {}
local fishArray = {}
for j, fish in Shared.skpairs(area.fish) do
for j, fishID in ipairs(area.fishIDs) do
local fishTable = Items.getItems(function(item) return item.fishingID == fish end)
local fishItem = Items.getItemByID(fishID)
local fishItem = fishTable[0] or fishTable[1]
if fishItem ~= nil then
table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
end
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..'%'
Line 315: Line 274:
end
end


function p.getThievingNPC(npcName)
function p.getThievingGeneralRareTable(frame)
local result = nil
return p._getThievingGeneralRareTable()
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
end


function p.getThievingNPCArea(npc)
function p._getThievingGeneralRareTable(npcID)
if type(npc) == 'string' then
local rareTxt = '{|class="wikitable sortable"'
npc = p.getThievingNPC(npc)
rareTxt = rareTxt..'\r\n!Item!!Qty'
end
rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
for i, drop in ipairs(SkillData.Thieving.generalRareItems) do
-- If an npcID has been passed and the item is NPC specific, only display
-- the item if it may be obtained while pickpocketing that NPC
if npcID == nil or drop.npcs == nil or Shared.contains(drop.npcs, npcID) then
local thisItem = Items.getItemByID(drop.itemID)
local odds = drop.chance
local result = nil
rareTxt = rareTxt..'\r\n|-\r\n|data-sort-value="'..thisItem.name..'"|'..Icons.getExpansionIcon(thisItem.id)..Icons.Icon({thisItem.name, type='item'})
for i, area in Shared.skpairs(SkillData.Thieving.Areas) do
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
for j, npcID in pairs(area.npcs) do
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(1, Shared.round2(1/(odds/100), 0))
if npcID == npc.id then
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.round(odds, 4, 4)..'%'
result = area
break
end
end
end
end
end
return result
rareTxt = rareTxt..'\r\n|}'
end
return rareTxt
 
function p._getThievingNPCStat(npc, statName)
local result = nil
if statName == 'level' then
result = Icons._SkillReq('Thieving', npc.level)
elseif statName == 'maxHit' then
result = npc.maxHit * 10
elseif statName == 'area' then
local area = p.getThievingNPCArea(npc)
result = area.name
else
result = npc[statName]
end
if result == nil then
result = ''
end
return result
end
 
function p.getThievingNPCStat(frame)
local npcName = frame.args ~= nil and frame.args[1] or frame[1]
local statName = frame.args ~= nil and frame.args[2] or frame[2]
local npc = p.getThievingNPC(npcName)
if npc == nil then
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
end
return p._getThievingNPCStat(npc, statName)
end
end


Line 378: Line 302:
local result = ''
local result = ''
local sectionTxt = {}
local sectionTxt = {}
 
--Five sections here: GP, normal loot, area loot, rare loot, and unique item
--Five sections here: GP, normal loot, area loot, rare loot, and unique item
--First up, GP:
--First up, GP:
local gpTxt = 'Successfully pickpocketing the '..npc.name..' will always give '..Icons.GP(1, npc.maxGP)
local gpTxt = 'Successfully pickpocketing the '..npc.name..' will always give '..Icons.GP(1, npc.maxGP)
table.insert(sectionTxt, gpTxt)
table.insert(sectionTxt, gpTxt)
 
--Next up, normal loot:
--Next up, normal loot:
--(Skip if no loot)
--(Skip if no loot)
if npc.lootTable ~= nil and Shared.tableCount(npc.lootTable) > 0 then
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 normalTxt = {}
table.insert(normalTxt, '===Possible Common Drops:===\r\nUp to one of these will be received on a successful pickpocket:')
local totalWt = 0
local totalWt = 0
local lootChance = thievingNormalLootChance
local lootChance = SkillData.Thieving.itemChance
local lootValue = 0
local lootValue = 0
 
--First loop through to get the total weight so we have it for later
--First loop through to get the total weight so we have it for later
for i, loot in pairs(npc.lootTable) do
for i, loot in pairs(npc.lootTable) do
totalWt = totalWt + loot[2]
totalWt = totalWt + loot.weight
end
end
 
normalTxt = normalTxt..'\r\n{|class="wikitable sortable"'
table.insert(normalTxt, '\r\n{|class="wikitable sortable"')
normalTxt = normalTxt..'\r\n!Item!!Qty'
table.insert(normalTxt, '\r\n!Item!!Qty')
normalTxt = normalTxt..'!!Price!!colspan="2"|Chance'
table.insert(normalTxt, '!!Price!!colspan="2"|Chance')
 
local lootTable = Shared.shallowClone(npc.lootTable)
--Then sort the loot table by weight
--Then sort the loot table by weight
table.sort(npc.lootTable, function(a, b) return a[2] > b[2] end)
table.sort(lootTable, function(a, b) return a.weight > b.weight end)
for i, row in Shared.skpairs(npc.lootTable) do
for i, loot in ipairs(lootTable) do
local thisItem = Items.getItemByID(row[1])
local thisItem = Items.getItemByID(loot.itemID)
local maxQty = row[3]
if thisItem ~= nil then
if thisItem ~= nil then
normalTxt = normalTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
table.insert(normalTxt, '\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}))
else
else
normalTxt = normalTxt..'\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]'
table.insert(normalTxt, '\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]')
end
end
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..maxQty..'"|'
table.insert(normalTxt, '||style="text-align:right" data-sort-value="'..(loot.minQuantity + loot.maxQuantity)..'"|')
 
if maxQty > 1 then
if loot.minQuantity ~= loot.maxQuantity then
normalTxt = normalTxt.. '1 - '
table.insert(normalTxt, Shared.formatnum(loot.minQuantity) .. ' - ' .. Shared.formatnum(loot.maxQuantity))
else
table.insert(normalTxt, Shared.formatnum(loot.maxQuantity))
end
end
normalTxt = normalTxt..Shared.formatnum(row[3])
 
--Adding price columns
--Adding price columns
local itemPrice = 0
local itemPrice = 0
if thisItem == nil then
if thisItem == nil then
normalTxt = normalTxt..'||data-sort-value="0"|???'
table.insert(normalTxt, '||data-sort-value="0"|???')
else
else
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
if itemPrice == 0 or maxQty == 1 then
if itemPrice == 0 or loot.minQuantity == loot.maxQuantity then
normalTxt = normalTxt..'||'..Icons.GP(itemPrice)
table.insert(normalTxt, '||'..Icons.GP(itemPrice))
else
else
normalTxt = normalTxt..'||'..Icons.GP(itemPrice, itemPrice * maxQty)
table.insert(normalTxt, '||'..Icons.GP(itemPrice * loot.minQuantity, itemPrice * loot.maxQuantity))
end
end
end
end
 
--Getting the drop chance
--Getting the drop chance
local dropChance = (row[2] / totalWt * lootChance)
local dropChance = (loot.weight / totalWt * lootChance)
if dropChance ~= 100 then
if dropChance < 100 then
--Show fraction as long as it isn't going to be 1/1
--Show fraction as long as it isn't going to be 1/1
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..row[2]..'"'
table.insert(normalTxt, '||style="text-align:right" data-sort-value="'..loot.weight..'"')
normalTxt = normalTxt..'|'..Shared.fraction(row[2] * lootChance, totalWt * 100)
table.insert(normalTxt, '|'..Shared.fraction(loot.weight * lootChance, totalWt * 100))
normalTxt = normalTxt..'||'
table.insert(normalTxt, '||')
else
else
normalTxt = normalTxt..'||colspan="2" data-sort-value="'..row[2]..'"'
table.insert(normalTxt, '||colspan="2" data-sort-value="'..loot.weight..'"')
end
end
normalTxt = normalTxt..'style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
table.insert(normalTxt, 'style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%')
 
--Adding to the average loot value based on price & dropchance
--Adding to the average loot value based on price & dropchance
lootValue = lootValue + (dropChance * 0.01 * itemPrice * ((1 + maxQty) / 2))
lootValue = lootValue + (dropChance * 0.01 * itemPrice * (loot.minQuantity + loot.maxQuantity) / 2)
end
end
if multiDrop then
if Shared.tableCount(npc.lootTable) > 1 then
normalTxt = normalTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
table.insert(normalTxt, '\r\n|-class="sortbottom" \r\n!colspan="3"|Total:')
if lootChance < 100 then
if lootChance < 100 then
normalTxt = normalTxt..'\r\n|style="text-align:right"|'..Shared.fraction(lootChance, 100)..'||'
table.insert(normalTxt, '\r\n|style="text-align:right"|'..Shared.fraction(lootChance, 100)..'||')
else
else
normalTxt = normalTxt..'\r\n|colspan="2" '
table.insert(normalTxt, '\r\n|colspan="2" ')
end
end
normalTxt = normalTxt..'style="text-align:right"|'..lootChance..'.00%'
table.insert(normalTxt, 'style="text-align:right"|'..Shared.round(lootChance, 2, 2)..'%')
end
end
normalTxt = normalTxt..'\r\n|}'
table.insert(normalTxt, '\r\n|}')
table.insert(sectionTxt, normalTxt)
table.insert(normalTxt, '\r\nThe loot obtained from the average successful pickpocket is worth ' .. Icons.GP(Shared.round(lootValue, 2, 2)) .. ' if sold.')
table.insert(normalTxt, '\r\n\r\nIncluding GP, the average successful pickpocket is worth ' .. Icons.GP(Shared.round(lootValue + (1 + npc.maxGP) / 2, 2, 2)) .. '.')
table.insert(sectionTxt, table.concat(normalTxt))
end
end
 
--After normal drops, add in rare drops
--After normal drops, add in rare drops
local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket'
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'..p._getThievingGeneralRareTable(npc.id)
rareTxt = rareTxt..'\r\n!Item!!Qty'
rareTxt = rareTxt..'!!Price!!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.round(odds, 4, 4)..'%'
end
rareTxt = rareTxt..'\r\n|}'
table.insert(sectionTxt, rareTxt)
table.insert(sectionTxt, rareTxt)
 
local areaTxt = '===Possible Area Unique Drops==='
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\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..'\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.'
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)
local area = Skills.getThievingNPCArea(npc)
areaTxt = areaTxt..'\r\n{|class="wikitable sortable"'
areaTxt = areaTxt..'\r\n{|class="wikitable sortable"'
areaTxt = areaTxt..'\r\n!Item!!Qty'
areaTxt = areaTxt..'\r\n!Item!!Qty'
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
local dropCount = Shared.tableCount(area.uniqueDrops)
local dropLines = {}
local dropLines = {}
for i, drop in pairs(area.uniqueDrops) do
for i, drop in ipairs(area.uniqueDrops) do
local thisItem = Items.getItemByID(drop.itemID)
local thisItem = Items.getItemByID(drop.id)
local lineTxt = ''
local lineTxt = ''
lineTxt = lineTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
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..'||data-sort-value="'..drop.quantity..'"| '..Shared.formatnum(drop.quantity)..'||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
lineTxt = lineTxt..'||style="text-align:right"|'..thievingAreaLootChance
lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))
lineTxt = lineTxt..'||'..Shared.round(thievingAreaLootChance, 2, 2)..'%'
lineTxt = lineTxt..'||'..Shared.round(SkillData.Thieving.baseAreaUniqueChance, 2, 2)..'%'
dropLines[thisItem.name] = lineTxt
dropLines[thisItem.name] = lineTxt
end
end
for i, txt in Shared.skpairs(dropLines) do
for i, txt in pairs(dropLines) do
areaTxt = areaTxt..txt
areaTxt = areaTxt..txt
end
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|}'
areaTxt = areaTxt..'\r\n|}'
table.insert(sectionTxt, areaTxt)
table.insert(sectionTxt, areaTxt)
 
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
if npc.uniqueDrop ~= nil and npc.uniqueDrop.id ~= nil then
local uniqueTxt = '===Possible NPC Unique Drop==='
local thisItem = Items.getItemByID(npc.uniqueDrop.id)
uniqueTxt = uniqueTxt..'\r\nThe chance of receiving the unique drop for an NPC is based on a combination of several factors.'
if thisItem ~= nil then
uniqueTxt = uniqueTxt..' The unique drop chance for an NPC is included in the tooltip for your Stealth against that NPC.'
local uniqueTxt = '===Possible NPC Unique Drop==='
local thisItem = Items.getItemByID(npc.uniqueDrop.itemID)
uniqueTxt = uniqueTxt..'\r\nThe chance of receiving the unique drop for an NPC is based on a combination of several factors.'
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
uniqueTxt = uniqueTxt..' The unique drop chance for an NPC is included in the tooltip for your Stealth against that NPC.'
if npc.uniqueDrop.qty > 1 then
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.qty})
if npc.uniqueDrop.quantity > 1 then
else
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.quantity}) .. '.'
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'})
else
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'}) .. '.'
end
table.insert(sectionTxt, uniqueTxt)
end
end
table.insert(sectionTxt, uniqueTxt)
end
end
 
return table.concat(sectionTxt, '\r\n')
return table.concat(sectionTxt, '\r\n')
end
end
Line 524: Line 438:
function p.getThievingNPCLootTables(frame)
function p.getThievingNPCLootTables(frame)
local npcName = frame.args ~= nil and frame.args[1] or frame
local npcName = frame.args ~= nil and frame.args[1] or frame
local npc = p.getThievingNPC(npcName)
local npc = Skills.getThievingNPC(npcName)
if npc == nil then
if npc == nil then
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
return Shared.printError('Invalid Thieving NPC "' .. npcName .. '"')
end
end
 
return p._getThievingNPCLootTables(npc)
return p._getThievingNPCLootTables(npc)
end
end
Line 535: Line 449:
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"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!Unique Drop'
result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!GP!!Unique Drop'
local npcArray = Shared.clone(SkillData.Thieving.NPCs)
local npcArray = Shared.shallowClone(SkillData.Thieving.npcs)
table.sort(npcArray, function(a, b) return a.level < b.level end)
table.sort(npcArray, function(a, b) return a.level < b.level end)
for i, npc in Shared.skpairs(npcArray) do
for i, npc in ipairs(npcArray) do
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true})
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true})
if npc.name == 'Golbin' or npc.name == 'Wizard' or npc.name == 'Cyclops' then
result = result..'||data-sort-value="'..npc.name..'"|'..Icons.getExpansionIcon(npc.id)..Icons.Icon({npc.name, type='thieving', noicon=true})
result = result..'||[['..npc.name..' (thieving)|'..npc.name..']]'
 
else
local area = Skills.getThievingNPCArea(npc)
result = result..'||[['..npc.name..']]'
end
local area = p.getThievingNPCArea(npc)
result = result..'||'..area.name
result = result..'||'..area.name
result = result..'||'..Icons._SkillReq('Thieving', npc.level)
result = result..'||data-sort-value="' .. npc.level .. '"| '..Icons._SkillReq('Thieving', npc.level)
result = result..'||style="text-align:right"|'..npc.xp
result = result..'||style="text-align:right"|'..npc.baseExperience
result = result..'||style="text-align:right"|'..(npc.maxHit * 10)
result = result..'||style="text-align:right"|'..(npc.maxHit * 10)
result = result..'||style="text-align:right"|'..npc.perception
result = result..'||style="text-align:right" data-sort-value="' .. npc.perception .. '| '..Shared.formatnum(npc.perception)
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
result = result..'||data-sort-value="' .. npc.maxGP .. '"|'..Icons.GP(1, npc.maxGP)
local uniqueDrop = Items.getItemByID(npc.uniqueDrop.itemID)
if npc.uniqueDrop ~= nil then
if npc.uniqueDrop.qty > 1 then
local uniqueDrop = Items.getItemByID(npc.uniqueDrop.id)
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item', qty = npc.uniqueDrop.qty})
if npc.uniqueDrop.quantity > 1 then
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item', qty = npc.uniqueDrop.quantity})
else
else
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item'})
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item'})
Line 564: Line 476:
end
end
result = result..'\r\n|}'
result = result..'\r\n|}'
 
return result
return result
end
end


function p.canItemBeStolen(itemID)
function p.getThievingAreaTable(frame)
local result = false
local resultPart = {}
--First, check each NPC to see if they drop the item
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
for i, npc in pairs(SkillData.Thieving.NPCs) do
table.insert(resultPart, '\r\n|- class="headerRow-0"')
for j, drop in pairs(npc.lootTable) do
table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')
if drop[1] == itemID then
 
result = true
for i, area in ipairs(SkillData.Thieving.areas) 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.npcIDs ~= nil and not Shared.tableIsEmpty(area.npcIDs) then
for j, npcID in ipairs(area.npcIDs) do
local npc = Skills.getThievingNPCByID(npcID)
if minLevel == nil or npc.level < minLevel then
minLevel = npc.level
end
table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
end
end
else
table.insert(npcList, '')
end
end
 
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID == itemID then
-- Build area unique item list
result = true
if area.uniqueDrops ~= nil and Shared.tableCount(area.uniqueDrops) > 0 then
for k, drop in ipairs(area.uniqueDrops) do
local areaItem = Items.getItemByID(drop.id)
if areaItem == nil then
table.insert(areaItemList, 'Unknown[[Category:Pages with script errors]]')
else
local iconDef = {areaItem.name, type='item'}
if drop.quantity > 1 then
iconDef.qty = drop.quantity
end
table.insert(areaItemList, Icons.Icon(iconDef))
end
end
else
table.insert(areaItemList, '')
end
 
-- Generate table row
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|' .. area.name)
table.insert(resultPart, '\r\n|' .. Icons._SkillReq('Thieving', minLevel))
table.insert(resultPart, '\r\n|' .. table.concat(npcList, '<br/>'))
table.insert(resultPart, '\r\n|' .. table.concat(areaItemList, '<br/>'))
end
table.insert(resultPart, '\r\n|}')
 
return table.concat(resultPart)
end
 
function p._getFarmingTable(categoryName)
local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
if category == nil then
return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees')
end
local seedList = GameData.getEntities(SkillData.Farming.recipes,
function(recipe)
return recipe.categoryID == category.id
end)
if Shared.tableIsEmpty(seedList) then
return ''
end
 
local tbl = mw.html.create()
local html = tbl:tag("table")
        :addClass("wikitable sortable stickyHeader")
    :tag('tr'):addClass("headerRow-0")
        :tag('th'):attr("colspan", 2):wikitext("Seeds")
        :tag('th'):wikitext(Icons.Icon({'Farming', type='skill', notext=true}) .. " Level")
        :tag('th'):wikitext('XP')
        :tag('th'):wikitext('Growth Time')
        :tag('th'):wikitext('Seed Value')
 
if category.id == 'melvorD:Allotment' then
html:tag('th'):attr("colspan", 2):wikitext("Crop")
:tag('th'):wikitext('Crop Healing')
:tag('th'):wikitext('Crop Value')
elseif category.id == 'melvorD:Herb' then
html:tag('th'):attr("colspan", 2):wikitext("Herb")
:tag('th'):wikitext('Herb Value')
elseif category.id == 'melvorD:Tree' then
html:tag('th'):attr("colspan", 2):wikitext("Logs")
:tag('th'):wikitext('Log Value')
end
html = html:tag('th'):wikitext('Seed Sources')
 
table.sort(seedList, function(a, b) return a.level < b.level end)
 
for i, seed in ipairs(seedList) do
local seedItem = Items.getItemByID(seed.seedCost.id)
local productItem = Items.getItemByID(seed.productId)
if seedItem ~= nil and productItem ~= nil then
html =
html:tag('tr')
:tag('td'):wikitext(Icons.Icon({seedItem.name, type='item', size='50', notext=true}))
:tag('td'):wikitext(Icons.getExpansionIcon(seedItem.id) .. Icons.Icon({seedItem.name, type='item', noicon=true}))
:tag('td'):wikitext(seed.level)
:tag('td'):wikitext(Shared.formatnum(seed.baseExperience))
:tag('td'):attr('data-sort-value', (seed.baseInterval / 1000))
  :wikitext(Shared.timeString(seed.baseInterval / 1000, true))
:tag('td'):attr('data-sort-value', seedItem.sellsFor)
      :wikitext(Icons.GP(seedItem.sellsFor))
:tag('td'):wikitext(Icons.Icon({productItem.name, type='item', size='50', notext=true}))
:tag('td'):wikitext(Icons.getExpansionIcon(productItem.id) .. Icons.Icon({productItem.name, type='item', noicon=true}))
 
if category.id == 'melvorD:Allotment' then
html:tag('td'):wikitext(Icons.Icon({'Hitpoints', type='skill', notext=true}))
  :wikitext(' ')
  :wikitext(((productItem.healsFor or 0) * 10))
end
html =
html:tag('td'):attr('data-sort-value', productItem.sellsFor)
  :wikitext(Icons.GP(productItem.sellsFor))
:tag('td'):wikitext(ItemSourceTables._getItemSources(seedItem))
  :css('text-align', 'left')
:done()
end
end
end
if result then
 
break
return tostring(tbl:done())
end
 
function p.getFarmingTable(frame)
local category = frame.args ~= nil and frame.args[1] or frame
 
return p._getFarmingTable(category)
end
 
function p.getFarmingFoodTable(frame)
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 recipes = GameData.getEntities(SkillData.Farming.recipes,
function(recipe)
local product = Items.getItemByID(recipe.productId)
return product ~= nil and product.healsFor ~= nil and product.healsFor > 0
end)
table.sort(recipes, function(a, b) return a.level < b.level end)
 
for i, recipe in ipairs(recipes) do
local product = Items.getItemByID(recipe.productId)
if product ~= nil and product.healsFor ~= nil and product.healsFor > 0 then
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({product.name, type='item', notext='true', size='50'})
result = result..'|| ' .. Icons.getExpansionIcon(product.id) .. Icons.Icon({product.name, type='item', noicon=true})
result = result..'||style="text-align:right;"|'..recipe.level
result = result..'||style="text-align:right" data-sort-value="'..product.healsFor..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..(product.healsFor * 10)
result = result..'||style="text-align:right" data-sort-value="'..product.sellsFor..'"|'..Icons.GP(product.sellsFor)
end
end
end
end
 
--Then check area unique drops
result = result..'\r\n|}'
if not result then
 
for  i, area in pairs(SkillData.Thieving.Areas) do
return result
for j, drop in pairs(area.uniqueDrops) do
end
if drop.itemID == itemID then
 
result = true
function p.getFarmingPlotTable(frame)
break
local areaName = frame.args ~= nil and frame.args[1] or frame
end
local category = GameData.getEntityByName(SkillData.Farming.categories, areaName)
if category == nil then
return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees')
end
local patches = GameData.getEntities(SkillData.Farming.plots,
function(plot)
return plot.categoryID == category.id
end)
table.sort(patches,
function(a, b)
if a.level == b.level then
return a.id < b.id
else
return a.level < b.level
end
end
end)
if result then
if Shared.tableIsEmpty(patches) then
break
return ''
end
end
end
 
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'
 
for i, patch in ipairs(patches) do
result = result..'\r\n|-\r\n|'..i
result = result..'||style="text-align:right;" data-sort-value="' .. patch.level .. '"|'..patch.level
local costText = Common.getCostString({ items = patch.itemCosts, gp = patch.gpCost }, 'Free')
result = result..'||style="text-align:right;" data-sort-value="'..patch.gpCost..'"|'..costText
end
end
 
result = result..'\r\n|}'
return result
return result
end
end


function p.getThievingSourcesForItem(itemID)
function p._buildAstrologyConstellationTable()
local resultArray = {}
local resultPart = {}
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
local areaNPCs = {}
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!rowspan="2" colspan="2"|Constellation!!rowspan="2"| ' .. Icons.Icon({"Astrology", type='skill', notext='true'}) .. ' Level')
--First check area unique drops
table.insert(resultPart, '!!rowspan="2"| XP!!rowspan="2"| Skills!!colspan="2"| Standard Stars!!colspan="2"| Unique Stars')
--If an area drops the item, add all the NPC ids to the list so we can add them later
table.insert(resultPart, '\n|- class="headerRow-1"')
if not result then
table.insert(resultPart, string.rep('\n! ' .. Icons.Icon({'Mastery', notext=true}) .. 'Level\n! Modifiers', 2))
for  i, area in pairs(SkillData.Thieving.Areas) do
 
for j, drop in pairs(area.uniqueDrops) do
local recipes = Shared.shallowClone(SkillData.Astrology.recipes)
if drop.itemID == itemID then
table.sort(recipes, function(a, b) return a.level < b.level end)
for k, npcID in pairs(area.npcs) do
for i, cons in ipairs(recipes) do
areaNPCs[npcID] = drop.qty
-- Generate the list of modifiers first for the purpose of determining the
end
-- the number of rows required to display all stars
break
local modTypes = { 'standard', 'unique' }
local modsRaw = {
["standard"] = Skills._buildAstrologyModifierArray(cons, nil, true, false, false, false),
["unique"] = Skills._buildAstrologyModifierArray(cons, nil, false, true, false, false)
}
-- Building the list of standard & unique modifiers
local mods = {}
local modLevels = {}
local maxRows = 1
for j, modType in ipairs(modTypes) do
mods[modType] = {}
modLevels[modType] = {}
for k, modifier in ipairs(modsRaw[modType]) do
local masteryLevel = modifier.group
if mods[modType][masteryLevel] == nil then
mods[modType][masteryLevel] = {}
table.insert(modLevels[modType], masteryLevel)
end
end
local modMagnitude = type(modifier[2]) == 'table' and {modifier[2]} or modifier[2]
table.insert(mods[modType][masteryLevel], Constants._getModifierText(modifier[1], modMagnitude, false))
end
end
end
table.sort(modLevels[modType])
end
local typeCount = Shared.tableCount(modLevels[modType])
if typeCount > maxRows then
--Now go through and get drop chances on each NPC if needed
maxRows = typeCount
for i, npc in pairs(SkillData.Thieving.NPCs) do
local totalWt = 0
local dropWt = 0
local dropQty = 0
for j, drop in pairs(npc.lootTable) do
totalWt = totalWt + drop[2]
if drop[1] == itemID then
dropWt = drop[2]
dropQty = drop[3]
end
end
end
if dropWt > 0 then
table.insert(resultArray, {npc = npc.name, minQty = 1, maxQty = dropQty, chance = dropWt / totalWt * 100})
end
end
--Chance of -1 on unique drops is to indicate variable chance
local name = cons.name
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID == itemID then
local rowSpan = (maxRows > 1 and 'rowspan="' .. maxRows .. '"') or ''
table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.qty, maxQty = npc.uniqueDrop.qty, chance = -1})
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|' .. rowSpan .. ' data-sort-value="' .. name .. '" id="'..name..'"| ')
table.insert(resultPart, Icons.Icon({name, type='constellation', size='50', notext=true}))
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. Icons.getExpansionIcon(cons.id) .. name)
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.level)
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.baseExperience)
 
local skillIconArray = {}
for j, skillID in ipairs(cons.skillIDs) do
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
end
end
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. table.concat(skillIconArray, '<br/>'))
if areaNPCs[npc.id] ~= nil then
 
table.insert(resultArray, {npc = npc.name, minQty = areaNPCs[npc.id], maxQty = areaNPCs[npc.id], chance = thievingAreaLootChance})
-- Generate table text for standard & unique modifiers
for row = 1, maxRows, 1 do
for j, modType in ipairs(modTypes) do
local masteryLevel = (modLevels[modType] ~= nil and modLevels[modType][row]) or nil
if masteryLevel ~= nil then
table.insert(resultPart, '\n|style="text-align:right"| ' .. masteryLevel)
table.insert(resultPart, '\n| ' .. table.concat(mods[modType][masteryLevel], '<br/>'))
else
table.insert(resultPart, '\n|colspan="2" class="table-na"| ')
end
end
if row < maxRows then
table.insert(resultPart, '\n|-')
end
end
end
end
end
table.insert(resultPart, '\n|}')
return resultArray
 
return table.concat(resultPart)
end
 
function p.buildAstrologyConstellationTable(frame)
return p._buildAstrologyConstellationTable()
end
end


return p
return p
915

edits