Anonymous

Module:Skills/Gathering: Difference between revisions

From Melvor Idle
Use printError function
(Update for v1.1)
(Use printError function)
(20 intermediate revisions by 2 users not shown)
Line 12: Line 12:
local ItemSourceTables = require('Module:Items/SourceTables')
local ItemSourceTables = require('Module:Items/SourceTables')


-- TODO Move tool tables to Module:Shop
function p.getRecipeRequirements(skillName, recipe)
function p.getAxeTable(frame)
local reqText = {}
local toolArray = Shop.getPurchases(
if recipe.level ~= nil then
function(purch)
table.insert(reqText, Icons._SkillReq(skillName, recipe.level, false))
return purch.category == 'melvorD:SkillUpgrades' and string.find(purch.id, '_Axe$') ~= nil
end)
 
local result = '{| class="wikitable"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
result = result..'!!Cost!!This Axe!!Total'
 
local total = 0
 
for i, tool in ipairs(toolArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.customName..'"|'..Icons.Icon({tool.customName, type='upgrade', size='50', notext=true})
result = result..'||'..tool.customName
local level = 1
if tool.purchaseRequirements ~= nil and not Shared.tableIsEmpty(tool.purchaseRequirements) 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].level
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)
 
-- TODO TotH tools have other modifiers which should be included
local cutTime = 0
if tool.contains.modifiers.decreasedSkillIntervalPercent ~= nil then
cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1].value
end
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
end
end
 
if recipe.totalMasteryRequired ~= nil then
result = result..'\r\n|}'
table.insert(reqText, Shared.formatnum(recipe.totalMasteryRequired) .. ' ' .. Icons.Icon({skillName, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'}))
return result
end
 
function p.getPickaxeTable(frame)
local toolArray = Shop.getPurchases(
function(purch)
return purch.category == 'melvorD:SkillUpgrades' and string.find(purch.id, '_Pickaxe$') ~= nil
end)
 
local result = '{| class="wikitable"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Mine Time Decrease!!colspan="2"|2x Ore Chance'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'
 
local total = 0
local total2 = 0
 
for i, tool in ipairs(toolArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.customName..'"|'..Icons.Icon({tool.customName, type='upgrade', size='50', notext=true})
result = result..'||'..tool.customName
local level = 1
if tool.purchaseRequirements ~= nil and not Shared.tableIsEmpty(tool.purchaseRequirements) 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].level
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)
 
-- TODO TotH tools have other modifiers which should be included
local cutTime = 0
if tool.contains.modifiers.decreasedSkillIntervalPercent ~= nil then
cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1].value
end
total = total + cutTime
 
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
 
local OreDouble = (tool.contains.modifiers.increasedChanceToDoubleOres or 0)
total2 = total2 + OreDouble
 
result = result..'||style="text-align:right"|+'..OreDouble..'%'
result = result..'||style="text-align:right"|+'..total2..'%'
end
end
 
if recipe.shopItemPurchased ~= nil then
result = result..'\r\n|}'
local purchReq = Shop.getPurchaseByID(recipe.shopItemPurchased)
return result
if purchReq ~= nil then
end
table.insert(reqText, Shop._getPurchaseIcon({purchReq}))
 
function p.getRodTable(frame)
local toolArray = Shop.getPurchases(
function(purch)
return purch.category == 'melvorD:SkillUpgrades' and string.find(purch.id, '_Fishing_Rod$') ~= nil
end)
 
local result = '{| class="wikitable"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
result = result..'!!Cost!!This Rod!!Total'
 
local total = 0
 
for i, tool in Shared.skpairs(toolArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.customName..'"|'..Icons.Icon({tool.customName, type='upgrade', size='50', notext=true})
result = result..'||'..tool.customName
local level = 1
if tool.purchaseRequirements ~= nil and not Shared.tableIsEmpty(tool.purchaseRequirements) 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].level
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)
-- TODO TotH tools have other modifiers which should be included
local cutTime = 0
if tool.contains.modifiers.decreasedSkillIntervalPercent ~= nil then
cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1].value
end
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
end
end
 
return table.concat(reqText, '<br/>')
result = result..'\r\n|}'
return result
end
end


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


for i, tree in ipairs(SkillData.Woodcutting.trees) do
for i, tree in ipairs(SkillData.Woodcutting.trees) do
local log = Items.getItemByID(tree.productId)
local log = Items.getItemByID(tree.productId)
result = result..'\r\n|-'
table.insert(resultPart, '\n|-')
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tree.name..'"|'..Icons.Icon({log.name, img=tree.name, type='tree', notext=true, size=50})
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}))
result = result..'||'..tree.name..''
table.insert(resultPart, '\n|data-sort-value="'..tree.name..'"|'..Icons.getExpansionIcon(tree.id)..tree.name)
result = result..'||style="min-width:25px" data-sort-value="'..log.name..'"|'..Icons.Icon({log.name, type='item', notext=true, size=50})
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..log.name..'"| '..Icons.Icon({log.name, type='item', notext=true, size=50}))
result = result..'||'..Icons.Icon({log.name, type='item', noicon=true})
table.insert(resultPart, '\n| '..Icons.Icon({log.name, type='item', noicon=true}))
result = result..'||style="text-align:right"|'..tree.level
table.insert(resultPart, '\n|data-sort-value="' .. tree.level .. '"| ' .. p.getRecipeRequirements(SkillData.Woodcutting.name, tree))
result = result..'||style="text-align:right"|'..tree.baseExperience
table.insert(resultPart, '\n|style="text-align:right"| '..tree.baseExperience)
result = result..'||style="text-align:right" data-sort-value="'..tree.baseInterval..'"|'..Shared.timeString(tree.baseInterval/1000, true)
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 XPs = tree.baseExperience / (tree.baseInterval / 1000)
local GPs = log.sellsFor / (tree.baseInterval / 1000)
local GPs = log.sellsFor / (tree.baseInterval / 1000)
result = result..'||style="text-align:right"|'..Shared.round(XPs, 2, 2)
table.insert(resultPart, '\n|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))
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


Line 174: Line 63:
end
end
-- Sort the loot table by weight in descending order
-- 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)
table.sort(itemArray, function(a, b) return a.weight > b.weight end)


local resultPart = {}
local resultPart = {}
Line 185: Line 74:
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
-- 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'
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|-\r\n|class="table-img"| ' .. 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| ' .. 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|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
Line 214: Line 103:
for i, item in ipairs(itemArray) do
for i, item in ipairs(itemArray) do
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="min-width:25px"| ' .. Icons.Icon({item.name, type='item', notext=true, size=50}))
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}))
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|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
Line 223: Line 112:


function p.getMiningOresTable(frame)
function p.getMiningOresTable(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|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
table.insert(resultPart, '\n|- class="headerRow-0"')
result = result..'!!XP!!Respawn Time!!Ore Value'
table.insert(resultPart, '\n!colspan="2"|Rock!!colspan=2|Ore!!Type!!Requirements')
table.insert(resultPart, '!!XP!!Respawn Time!!Ore Value')


local mineData = GameData.getEntities(SkillData.Mining.rockData, function(rock) return true end)
local mineData = GameData.getEntities(SkillData.Mining.rockData, function(rock) return true end)
table.sort(mineData, function(a, b) return a.level < b.level end)
table.sort(mineData, function(a, b) return a.level < b.level end)


for i, oreData in Shared.skpairs(mineData) do
for i, oreData in ipairs(mineData) do
local ore = Items.getItemByID(oreData.productID)
local ore = Items.getItemByID(oreData.productId)
result = result..'\r\n|-\r\n|style="min-width:25px"|'..Icons.Icon({ore.name, type='item', size='50', notext=true})
local respawnStyle, respawnSort, respawnText = 'class="table-na"', 0, 'N/A'
result = result..'||'..Icons.Icon({ore.name, type='item', noicon=true})
if oreData.hasPassiveRegen then
result = result..'||style="text-align:right"|'..oreData.level..'||style="text-align:right"|'..oreData.baseExperience
respawnStyle = 'style="text-align:right;'
result = result..'||style="text-align:right" data-sort-value="'..oreData.baseRespawnInterval..'"|'
respawnSort = oreData.baseRespawnInterval / 1000
result = result..Shared.timeString(oreData.baseRespawnInterval / 1000, true)
respawnText = Shared.timeString(respawnSort, true)
result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
end
local rockName = Icons.Icon({oreData.name, type='rock', noicon=true, nolink=true})
local qtyText = (oreData.baseQuantity > 1 and '<b>' .. oreData.baseQuantity .. 'x</b> ') or ''
table.insert(resultPart, '\n|-\n|class="table-img" data-sort-value="' .. rockName .. '"| '..Icons.Icon({oreData.name, type='rock', size='50', notext=true, nolink=true}))
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|}'
table.insert(resultPart, '\n|}')
return result
return table.concat(resultPart)
end
end


-- TODO TotH has random superior gems also
function p._getMiningGemsTable(gemType)
function p.getMiningGemsTable(frame)
if type(gemType) ~= 'string' then
local result = '{|class="wikitable sortable stickyHeader"'
gemType = 'Normal'
result = result..'\r\n|- class="headerRow-0"'
end
result = result..'\r\n!colspan=2|Gem!!Gem Chance!!Gem Price'
local validTypes = {
 
["Normal"] = 'randomGems',
["Superior"] = 'randomSuperiorGems'
}
local gemDataKey = validTypes[gemType]
if gemDataKey == nil then
return Shared.printError('No such gem type "' .. gemType .. '"')
end
local gemData = GameData.rawData[gemDataKey]
local totalWeight = 0
local totalWeight = 0
for i, gemData in ipairs(GameData.rawData.randomGems) do
for i, gem in ipairs(gemData) do
totalWeight = totalWeight + gemData.weight
totalWeight = totalWeight + gem.weight
end
end
local resultPart = {}
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan=2|Gem!!Gem Chance!!Gem Price')


for i, gemData in ipairs(GameData.rawData.randomGems) do
for i, gem in ipairs(gemData) do
local gem = Items.getItemByID(gemData.itemID)
local gemItem = Items.getItemByID(gem.itemID)
result = result..'\r\n|-\r\n|style="min-width:25px"|'
local gemPct = gem.weight / totalWeight * 100
result = result..Icons.Icon({gem.name, type='item', size='50', notext=true})
table.insert(resultPart, '\n|-\n|class="table-img"| ')
result = result..'||'..Icons.Icon({gem.name, type='item', noicon=true})
table.insert(resultPart, Icons.Icon({gemItem.name, type='item', size='50', notext=true}))
result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.weight / totalWeight * 100)
table.insert(resultPart, '\n| data-sort-value="'..gemItem.name..'"|'..Icons.getExpansionIcon(gemItem.id) .. Icons.Icon({gemItem.name, type='item', noicon=true}))
result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. gemPct .. '" | ' .. string.format("%.1f%%", gemPct))
table.insert(resultPart, '\n|data-sort-value="' .. gemItem.sellsFor .. '"| ' .. Icons.GP(gemItem.sellsFor))
end
end


result = result..'\r\n|}'
table.insert(resultPart, '\n|}')
return result
return table.concat(resultPart)
end
 
function p.getMiningGemsTable(frame)
local gemType = frame.args ~= nil and frame.args[1] or frame
return p._getMiningGemsTable(gemType)
end
end


Line 292: Line 213:
table.insert(resultPart, '\r\n!' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' Level')
table.insert(resultPart, '\r\n!' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' Level')
for i, recipe in ipairs(recipeList) do
for i, recipe in ipairs(recipeList) do
local fish = Items.getItemByID(recipe.productID)
local fish = Items.getItemByID(recipe.productId)
if fish ~= nil then
if fish ~= nil then
local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000
local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000
Line 298: Line 219:
local XPs = recipe.baseExperience / timeSortVal
local XPs = recipe.baseExperience / timeSortVal
local GPs = fish.sellsFor / timeSortVal
local GPs = fish.sellsFor / timeSortVal
local cookSortVal = cookReq[recipe.itemID] or 0
local cookSortVal = cookReq[recipe.productId] or 0
local cookStr = cookReq[recipe.itemID] or 'N/A'
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, '\r\n|-')
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|class="table-img"| ' .. 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|data-sort-value="'..fish.name..'"|'..Icons.getExpansionIcon(fish.id) .. 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"| ' .. 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" data-sort-value="' .. timeSortVal .. '"| ' .. timeStr)
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. recipe.baseXP)
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"| ' .. Shared.formatnum(recipe.baseExperience))
table.insert(resultPart, '\r\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Icons.GP(fish.sellsFor))
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|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|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)
table.insert(resultPart, '\r\n|' .. cookStyle .. 'data-sort-value="' .. cookSortVal .. '"| ' .. cookStr)
end
end
end
end
Line 344: Line 266:
end
end


-- TODO Expose list of NPCs somewhere when applicable (e.g. for Thieving Shorts)
function p.getThievingGeneralRareTable(frame)
function p.getThievingGeneralRareTable(frame)
return p._getThievingGeneralRareTable()
end
function p._getThievingGeneralRareTable(npcID)
local rareTxt = '{|class="wikitable sortable"'
local rareTxt = '{|class="wikitable sortable"'
rareTxt = rareTxt..'\r\n!Item!!Qty'
rareTxt = rareTxt..'\r\n!Item!!Qty'
rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
for i, drop in pairs(SkillData.Thieving.generalRareItems) do
for i, drop in ipairs(SkillData.Thieving.generalRareItems) do
local thisItem = Items.getItemByID(drop.itemID)
-- If an npcID has been passed and the item is NPC specific, only display
local odds = drop.chance
-- 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
rareTxt = rareTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
local thisItem = Items.getItemByID(drop.itemID)
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
local odds = drop.chance
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)..'%'
rareTxt = rareTxt..'\r\n|-\r\n|data-sort-value="'..thisItem.name..'"|'..Icons.getExpansionIcon(thisItem.id)..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
end
end
rareTxt = rareTxt..'\r\n|}'
rareTxt = rareTxt..'\r\n|}'
Line 393: Line 322:
table.sort(lootTable, function(a, b) return a.weight > b.weight end)
table.sort(lootTable, function(a, b) return a.weight > b.weight end)
for i, loot in ipairs(lootTable) do
for i, loot in ipairs(lootTable) do
local thisItem = Items.getItemByID(loot.id)
local thisItem = Items.getItemByID(loot.itemID)
if thisItem ~= nil then
if thisItem ~= nil then
table.insert(normalTxt, '\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}))
table.insert(normalTxt, '\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}))
Line 401: Line 330:
table.insert(normalTxt, '||style="text-align:right" data-sort-value="'..(loot.minQuantity + loot.maxQuantity)..'"|')
table.insert(normalTxt, '||style="text-align:right" data-sort-value="'..(loot.minQuantity + loot.maxQuantity)..'"|')


if loot.minQuantitiy ~= loot.maxQuantitiy then
if loot.minQuantity ~= loot.maxQuantity then
table.insert(normalTxt, Shared.formatnum(loot.minQuantitiy) .. ' - ' .. Shared.formatnum(loot.maxQuantity))
table.insert(normalTxt, Shared.formatnum(loot.minQuantity) .. ' - ' .. Shared.formatnum(loot.maxQuantity))
else
else
table.insert(normalTxt, Shared.formatnum(loot.maxQuantity))
table.insert(normalTxt, Shared.formatnum(loot.maxQuantity))
Line 413: Line 342:
else
else
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
if itemPrice == 0 or loot.minQuantity == loot.maxQuantitiy then
if itemPrice == 0 or loot.minQuantity == loot.maxQuantity then
table.insert(normalTxt, '||'..Icons.GP(itemPrice))
table.insert(normalTxt, '||'..Icons.GP(itemPrice))
else
else
table.insert(normalTxt, '||'..Icons.GP(itemPrice * loot.minQuantitiy, itemPrice * loot.maxQuantitiy))
table.insert(normalTxt, '||'..Icons.GP(itemPrice * loot.minQuantity, itemPrice * loot.maxQuantity))
end
end
end
end
Line 433: Line 362:


--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 * (loot.minQuantitiy + loot.maxQuantitiy) / 2)
lootValue = lootValue + (dropChance * 0.01 * itemPrice * (loot.minQuantity + loot.maxQuantity) / 2)
end
end
if Shared.tableCount(npc.lootTable) > 1 then
if Shared.tableCount(npc.lootTable) > 1 then
Line 445: Line 374:
end
end
table.insert(normalTxt, '\r\n|}')
table.insert(normalTxt, '\r\n|}')
table.insert(normalTxt, '\r\nThe loot obtained from the average successful pickocket is worth ' .. Icons.GP(Shared.round(lootValue, 2, 2)) .. ' if sold.')
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 pickocket is worth ' .. Icons.GP(Shared.round(lootValue + (1 + npc.maxGP) / 2, 2, 2)) .. '.')
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))
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'..p.getThievingGeneralRareTable()
rareTxt = rareTxt..'\r\n'..p._getThievingGeneralRareTable(npc.id)
table.insert(sectionTxt, rareTxt)
table.insert(sectionTxt, rareTxt)


Line 465: Line 394:
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
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.id)
local thisItem = Items.getItemByID(drop.id)
local lineTxt = ''
local lineTxt = ''
Line 471: Line 400:
lineTxt = lineTxt..'||data-sort-value="'..drop.quantity..'"| '..Shared.formatnum(drop.quantity)..'||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"|'..Shared.fraction(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))
lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))
lineTxt = lineTxt..'||'..Shared.round(SkillData.Thieving.AreaUniqueChance, 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(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))..'||'
areaTxt = areaTxt..'style="text-align:right"|'..Shared.round(SkillData.Thieving.baseAreaUniqueChance, 2, 2)..'%'
areaTxt = areaTxt..'\r\n|}'
areaTxt = areaTxt..'\r\n|}'
table.insert(sectionTxt, areaTxt)
table.insert(sectionTxt, areaTxt)
Line 491: Line 417:
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
if npc.uniqueDrop.quantity > 1 then
if npc.uniqueDrop.quantity > 1 then
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.quantity})
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.quantity}) .. '.'
else
else
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'})
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'}) .. '.'
end
end
table.insert(sectionTxt, uniqueTxt)
table.insert(sectionTxt, uniqueTxt)
Line 506: Line 432:
local npc = Skills.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


Line 521: Line 447:
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})
result = result..'||'..Icons.Icon({npc.name, type='thieving', noicon=true})
result = result..'||data-sort-value="'..npc.name..'"|'..Icons.getExpansionIcon(npc.id)..Icons.Icon({npc.name, type='thieving', noicon=true})


local area = Skills.getThievingNPCArea(npc)
local area = Skills.getThievingNPCArea(npc)
Line 528: Line 454:
result = result..'||style="text-align:right"|'..npc.baseExperience
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)
result = result..'||data-sort-value="' .. npc.maxGP .. '"|'..Icons.GP(1, npc.maxGP)
result = result..'||data-sort-value="' .. npc.maxGP .. '"|'..Icons.GP(1, npc.maxGP)
if npc.uniqueDrop ~= nil then
if npc.uniqueDrop ~= nil then
Line 556: Line 482:
-- Build NPC list & determine level for area, this is the minimum
-- Build NPC list & determine level for area, this is the minimum
-- Thieving level required for all NPCs within that area
-- Thieving level required for all NPCs within that area
if area.npcs ~= nil and not Shared.tableIsEmpty(area.npcs) then
if area.npcIDs ~= nil and not Shared.tableIsEmpty(area.npcIDs) then
for j, npcID in ipairs(area.npcs) do
for j, npcID in ipairs(area.npcIDs) do
local npc = Skills.getThievingNPCByID(npcID)
local npc = Skills.getThievingNPCByID(npcID)
if minLevel == nil or npc.level < minLevel then
if minLevel == nil or npc.level < minLevel then
Line 601: Line 527:
local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
if category == nil then
if category == nil then
return 'ERROR: Invalid farming category. Please choose Allotments, Herbs, or Trees'
return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees')
end
end
local seedList = GameData.getEntities(SkillData.Farming.recipes,
local seedList = GameData.getEntities(SkillData.Farming.recipes,
Line 624: Line 550:
result = result..'!!Seed Sources'
result = result..'!!Seed Sources'


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


for i, seed in ipairs(seedList) do
for i, seed in ipairs(seedList) do
Line 631: Line 557:
if seedItem ~= nil and productItem ~= nil then
if seedItem ~= nil and productItem ~= nil then
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({seedItem.name, type='item', size='50', notext=true})..'||[['..seedItem.name..']]'
result = result..'\r\n|'..Icons.Icon({seedItem.name, type='item', size='50', notext=true})
result = result..'|| ' .. Icons.getExpansionIcon(seedItem.id) .. Icons.Icon({seedItem.name, type='item', noicon=true})
result = result..'||'..seed.level..'||'..Shared.formatnum(seed.baseExperience)
result = result..'||'..seed.level..'||'..Shared.formatnum(seed.baseExperience)
result = result..'||data-sort-value="'..(seed.baseInterval / 1000)..'"|'..Shared.timeString(seed.baseInterval / 1000, true)
result = result..'||data-sort-value="'..(seed.baseInterval / 1000)..'"|'..Shared.timeString(seed.baseInterval / 1000, true)
result = result..'||data-sort-value="'..seedItem.sellsFor..'"|'..Icons.GP(seedItem.sellsFor)
result = result..'||data-sort-value="'..seedItem.sellsFor..'"|'..Icons.GP(seedItem.sellsFor)
result = result..'||'..Icons.Icon({productItem.name, type='item', size='50', notext=true})..'||[['..productItem.name..']]'
result = result..'||'..Icons.Icon({productItem.name, type='item', size='50', notext=true})
result = result..'|| ' .. Icons.getExpansionIcon(productItem.id) .. Icons.Icon({productItem.name, type='item', noicon=true})
if category.id == 'melvorD:Allotment' then
if category.id == 'melvorD:Allotment' then
result = result..'||'..Icons.Icon({'Hitpoints', type='skill', notext=true})..' '..((productItem.healsFor or 0) * 10)
result = result..'||'..Icons.Icon({'Hitpoints', type='skill', notext=true})..' '..((productItem.healsFor or 0) * 10)
end
end
result = result..'||data-sort-value="'..productItem.sellsFor..'"|'..Icons.GP(productItem.sellsFor)
result = result..'||data-sort-value="'..productItem.sellsFor..'"|'..Icons.GP(productItem.sellsFor)
result = result..'||'..ItemSourceTables._getItemSources(seedItem.id)
result = result..'||'..ItemSourceTables._getItemSources(seedItem)
end
end
end
end
Line 662: Line 590:
local recipes = GameData.getEntities(SkillData.Farming.recipes,
local recipes = GameData.getEntities(SkillData.Farming.recipes,
function(recipe)
function(recipe)
local product = Items.getItemByID(recipe.productID)
local product = Items.getItemByID(recipe.productId)
return product ~= nil and product.healsFor ~= nil and product.healsFor > 0
return product ~= nil and product.healsFor ~= nil and product.healsFor > 0
end)
end)
Line 668: Line 596:


for i, recipe in ipairs(recipes) do
for i, recipe in ipairs(recipes) do
local product = Items.getItemByID(recipe.productID)
local product = Items.getItemByID(recipe.productId)
if product ~= nil and product.healsFor ~= nil and product.healsFor > 0 then
if product ~= nil and product.healsFor ~= nil and product.healsFor > 0 then
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({product.name, type='item', notext='true', size='50'})..'||[['..product.name..']]'
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;"|'..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.healsFor..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..(product.healsFor * 10)
Line 685: Line 614:
function p.getFarmingPlotTable(frame)
function p.getFarmingPlotTable(frame)
local areaName = frame.args ~= nil and frame.args[1] or frame
local areaName = frame.args ~= nil and frame.args[1] or frame
local patches = nil
local category = GameData.getEntityByName(SkillData.Farming.categories, areaName)
for i, area in Shared.skpairs(SkillData.Farming.Patches) do
if category == nil then
if area.areaName == areaName then
return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees')
patches = area.patches
break
end
end
end
if patches == nil then
local patches = GameData.getEntities(SkillData.Farming.plots,
return "ERROR: Invalid area name.[[Category:Pages with script errors]]"
function(plot)
return plot.categoryID == category.id
end)
if Shared.tableIsEmpty(patches) then
return ''
end
end


local result = '{|class="wikitable"'
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'


Line 702: Line 632:
result = result..'\r\n|-\r\n|'..i
result = result..'\r\n|-\r\n|'..i
result = result..'||style="text-align:right;" data-sort-value="' .. patch.level .. '"|'..patch.level
result = result..'||style="text-align:right;" data-sort-value="' .. patch.level .. '"|'..patch.level
if patch.cost == 0 then
local costText = (patch.gpCost > 0 and Icons.GP(patch.gpCost)) or 'Free'
result = result..'||Free'
result = result..'||style="text-align:right;" data-sort-value="'..patch.gpCost..'"|'..costText
else
result = result..'||style="text-align:right;" data-sort-value="'..patch.cost..'"|'..Icons.GP(patch.cost)
end
end
end


Line 714: Line 641:


function p._buildAstrologyConstellationTable()
function p._buildAstrologyConstellationTable()
local maxModifier = 5
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 720: Line 646:
result = result..'!!XP!!Skills!!Standard Modifiers!!Unique Modifiers'
result = result..'!!XP!!Skills!!Standard Modifiers!!Unique Modifiers'


for i, cons in ipairs(SkillData.Astrology.Constellations) do
for i, cons in ipairs(SkillData.Astrology.recipes) do
local name = cons.name
local name = cons.name
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='constellation', size='50', notext=true})..'||'..name
result = result..'\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='constellation', size='50', notext=true})
result = result..'||'..cons.level..'||'..cons.provides.xp
result = result..'|| ' .. Icons.getExpansionIcon(cons.id) .. name
result = result..'||'..cons.level..'||'..cons.baseExperience


local skillIconArray = {}
local skillIconArray = {}
for j, skillID in ipairs(cons.skills) do
for j, skillID in ipairs(cons.skillIDs) do
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
end
end
result = result..'||'..table.concat(skillIconArray, '<br/>')
result = result..'||'..table.concat(skillIconArray, '<br/>')


local standModsRaw = Skills._buildAstrologyModifierArray(cons, maxModifier, true, false, false, false)
--Adding a function that converts an array of connected bonuses into text [Falterfire 22/10/27]
local groupedModsToText = function(allMods)
local outArray = {}
for i, group in ipairs(allMods) do
local groupTxt = table.concat(group, ' & ')
table.insert(outArray, groupTxt)
end
return table.concat(outArray, '<br/>')
end
 
local standModsRaw = Skills._buildAstrologyModifierArray(cons, nil, true, false, false, false)
local standMods = {}
local standMods = {}
--Building the list of Standard modifiers:
--Building the list of Standard modifiers:
for j, modifier in ipairs(standModsRaw) do
for j, modifier in ipairs(standModsRaw) do
table.insert(standMods, Constants._getModifierText(modifier[1], modifier[2], false))
local modMagnitude = type(modifier[2]) == 'table' and {modifier[2]} or modifier[2]
local groupNum = modifier.group
if standMods[groupNum] == nil then standMods[groupNum] = {} end
table.insert(standMods[groupNum], Constants._getModifierText(modifier[1], modMagnitude, false))
end
end
result = result..'|| '..table.concat(standMods, '<br/>')
result = result..'|| '..groupedModsToText(standMods)


--Building the list of all Unique Modifiers
--Building the list of all Unique Modifiers
local uModsRaw = Skills._buildAstrologyModifierArray(cons, maxModifier, false, true, false, false)
local uModsRaw = Skills._buildAstrologyModifierArray(cons, nil, false, true, false, false)
local uMods = {}
local uMods = {}
for j, modifier in ipairs(uModsRaw) do
for j, modifier in ipairs(uModsRaw) do
table.insert(uMods, Constants._getModifierText(modifier[1], modifier[2], false))
local modMagnitude = type(modifier[2]) == 'table' and {modifier[2]} or modifier[2]
local groupNum = modifier.group
if uMods[groupNum] == nil then uMods[groupNum] = {} end
table.insert(uMods[groupNum], Constants._getModifierText(modifier[1], modMagnitude, false))
end
end
result = result..'||'..table.concat(uMods, '<br/>')
result = result..'|| '..groupedModsToText(uMods)
end
end
result = result..'\r\n|}'
result = result..'\r\n|}'
Line 755: Line 699:
function p.buildAstrologyConstellationTable(frame)
function p.buildAstrologyConstellationTable(frame)
return p._buildAstrologyConstellationTable()
return p._buildAstrologyConstellationTable()
end
function p.buildAstrologyValueTable()
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