Module:Skills/Gathering: Difference between revisions
From Melvor Idle
(fix formatting) |
Tag: Undo |
||
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 SkillData = mw.loadData('Module:Skills/data') | ||
local ShopData = mw.loadData('Module:Shop/data') | |||
local Constants = require('Module:Constants') | |||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local Items = require('Module:Items') | local Items = require('Module:Items') | ||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
function p. | local thievingNormalLootChance = 75 | ||
local | local thievingAreaLootChance = 0.2 | ||
function p.getAxeTable(frame) | |||
local toolArray = {} | |||
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do | |||
if Shared.contains(upgrade.name, 'Axe') then | |||
table.insert(toolArray, upgrade) | |||
end | |||
end | end | ||
local result = '{| class="wikitable"' | |||
result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease' | |||
local | result = result..'\r\n|- class="headerRow-0"' | ||
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level' | |||
result = result..'!!Cost!!This Axe!!Total' | |||
local total = 0 | |||
for i, tool in Shared.skpairs(toolArray) do | |||
result = result..'\r\n|-' | |||
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true}) | |||
result = result..'||'..tool.name | |||
local level = 1 | |||
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then | |||
--Gonna be lazy and assume there's only the one skill level and it's the one we care about | |||
level = tool.unlockRequirements.skillLevel[1][2] | |||
end | 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 | ||
result = result..'\r\n|}' | |||
return result | |||
end | |||
function p.getPickaxeTable(frame) | |||
local toolArray = {} | |||
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do | |||
if Shared.contains(upgrade.name, 'Pickaxe') then | |||
table.insert(toolArray, upgrade) | |||
end | end | ||
end | end | ||
local result = '{| class="wikitable | local result = '{| class="wikitable"' | ||
result = result..'\r\n!colspan="4"| !!colspan="2"|Mine Time Decrease!!colspan="2"|2x Ore Chance' | |||
result = result..'\r\n|- class="headerRow-0"' | result = result..'\r\n|- class="headerRow-0"' | ||
result = result..'\r\n!colspan | result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level' | ||
result = result..'!! | result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total' | ||
result = result..'\r\n|- | |||
local total = 0 | |||
local total2 = 0 | |||
local | for i, tool in Shared.skpairs(toolArray) do | ||
if | 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 | ||
local | result = result..'||style="text-align:right"|'..level | ||
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp) | |||
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2] | |||
total = total + cutTime | |||
result = result..'||style="text-align:right"|-'..cutTime..'%' | |||
result = result..'||style="text-align:right"|-'..total..'%' | |||
local OreDouble = tool.contains.modifiers.increasedChanceToDoubleOres | |||
total2 = total2 + OreDouble | |||
result = result..'||style="text-align:right"|+'..OreDouble..'%' | |||
result = result..'||style="text-align:right"|+'..total2..'%' | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | |||
function p.getRodTable(frame) | |||
local toolArray = {} | |||
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do | |||
if Shared.contains(upgrade.name, 'Fishing Rod') then | |||
table.insert(toolArray, upgrade) | |||
end | end | ||
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|-' | ||
result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({ | 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..' | result = result..'||'..tool.name | ||
if | 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..'||' | result = result..'||style="text-align:right"|'..level | ||
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp) | |||
end | local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2] | ||
total = total + cutTime | |||
result = result..'||style=" | result = result..'||style="text-align:right"|-'..cutTime..'%' | ||
result = result..'||style=" | result = result..'||style="text-align:right"|-'..total..'%' | ||
result = result..'||style="text-align:right"|'.. | end | ||
result = result..'||'.. | |||
result = result..'||'.. | result = result..'\r\n|}' | ||
return result | |||
end | |||
function p.getTreesTable(frame) | |||
local result = '{| class="wikitable sortable"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!colspan="2"|Tree!!colspan="2"|Logs!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level' | |||
result = result..'!!XP!!Cut Time!!XP/s!!GP/s' | |||
for i, tree in Shared.skpairs(SkillData.Woodcutting.Trees) do | |||
result = result..'\r\n|-' | |||
local treeName = Shared.titleCase(tree.type..' tree') | |||
local logName = Shared.titleCase(tree.type..' logs') | |||
result = result..'\r\n|style="min-width:25px" data-sort-value="'..treeName..'"|'..Icons.Icon({logName, img=treeName, type='tree', notext=true, size=50}) | |||
result = result..'||'..treeName..'' | |||
result = result..'||style="min-width:25px" data-sort-value="'..logName..'"|'..Icons.Icon({logName, type='item', notext=true, size=50}) | |||
result = result..'||'..Icons.Icon({logName, type='item', noicon=true}) | |||
result = result..'||style="text-align:right"|'..tree.level | |||
result = result..'||style="text-align:right"|'..tree.xp | |||
result = result..'||style="text-align:right" data-sort-value="'..tree.interval..'"|'..Shared.timeString(tree.interval/1000, true) | |||
local XPs = tree.xp / (tree.interval / 1000) | |||
local Log = Items.getItemByID(i - 1) | |||
local GPs = Log.sellsFor / (tree.interval / 1000) | |||
result = result..'||style="text-align:right"|'..Shared.round(XPs, 2, 2) | |||
result = result..'||style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2)) | |||
end | end | ||
Line 86: | Line 163: | ||
end | end | ||
function p.getSpecialFishingTable(frame) | |||
function p. | local lootValue = 0 | ||
local totalWt = Items.specialFishWt | |||
local result = '' | |||
result = result..'\r\n{|class="wikitable sortable stickyHeader"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!Item' | |||
result = result..'!!Price!!colspan="2"|Chance' | |||
--Sort the loot table by weight in descending order | |||
table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end) | |||
for i, row in pairs(Items.specialFishLoot) do | |||
local thisItem = Items.getItemByID(row[1]) | |||
result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}) | |||
result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"' | |||
result = result..'|'..Icons.GP(thisItem.sellsFor) | |||
local dropChance = (row[2] / totalWt) * 100 | |||
result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"' | |||
result = result..'|'..Shared.fraction(row[2], totalWt) | |||
result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%' | |||
lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor) | |||
end | |||
result = result..'\r\n|}' | |||
result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0)) | |||
return result | |||
end | |||
function p.getFishingJunkTable(frame) | |||
local result = '{| class="wikitable sortable stickyHeader"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!colspan="2"|Item!!Value' | |||
local itemArray = Items.getItems(function(item) return item.type == "Junk" end) | |||
table.sort(itemArray, function(a, b) return a.name < b.name end) | |||
for i, item in Shared.skpairs(itemArray) do | |||
result = result..'\r\n|-' | |||
result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext=true, size='50'}) | |||
result = result..'||'..Icons.Icon({item.name, type='item', noicon=true}) | |||
result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor) | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | end | ||
function p. | function p.getMiningOresTable(frame) | ||
local result = '{|class="wikitable sortable stickyHeader"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level' | |||
result = result..'!!XP!!Respawn Time!!Ore Value' | |||
local mineData = Shared.clone(SkillData.Mining.Rocks) | |||
table.sort(mineData, function(a, b) return a.level < b.level end) | |||
for i, oreData in Shared.skpairs(mineData) do | |||
local ore = Items.getItemByID(oreData.ore) | |||
result = result..'\r\n|-\r\n|style="min-width:25px"|'..Icons.Icon({ore.name, type='item', size='50', notext=true}) | |||
result = result..'||'..Icons.Icon({ore.name, type='item', noicon=true}) | |||
result = result..'||style="text-align:right"|'..oreData.level..'||style="text-align:right"|'..ore.miningXP | |||
result = result..'||style="text-align:right" data-sort-value="'..oreData.respawnInterval..'"|' | |||
result = result..Shared.timeString(oreData.respawnInterval / 1000, true) | |||
result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor) | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | end | ||
function p. | function p.getMiningGemsTable(frame) | ||
local result = '{|class="wikitable sortable stickyHeader"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!colspan=2|Gem!!Gem Chance!!Gem Price' | |||
-- Sort gems by ID order | |||
for i, gemData in Shared.spairs(Items.GemTable, function(t,a,b) return t[a].id < t[b].id end) do | |||
local gem = Items.getItemByID(gemData.id) | |||
result = result..'\r\n|-\r\n|style="min-width:25px"|' | |||
result = result..Icons.Icon({gem.name, type='item', size='50', notext=true}) | |||
result = result..'||'..Icons.Icon({gem.name, type='item', noicon=true}) | |||
result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.chance) | |||
result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor) | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | |||
function p.getFishTable(frame) | |||
local data = Items.getItems(function(item) return item.fishingID ~= nil end) | |||
table.sort(data, function(a, b) return a.fishingID < b.fishingID end) | |||
local result = '{| class="wikitable sortable stickyHeader"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!Fish\r\n!Name\r\n!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level\r\n!Catch Time' | |||
result = result..'\r\n!Experience\r\n!Fish Price\r\n!XP/s\r\n!GP/s\r\n!' | |||
result = result..Icons.Icon({'Cooking', type='skill', notext=true})..' Level' | |||
for i, fish in Shared.skpairs(data) do | |||
result = result..'\r\n|-' | |||
result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', size='50', notext=true}) | |||
result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', noicon=true}) | |||
result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel | |||
local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2 | |||
local 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 XPs = fish.fishingXP / (timeSortVal / 1000) | |||
local GPs = fish.sellsFor / (timeSortVal / 1000) | |||
result = result..'\r\n| style="text-align:right"|'..Shared.round(XPs, 2, 2) | |||
result = result..'\r\n| style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2)) | |||
local cookStr = "N/A" | |||
if fish.cookingLevel ~= nil then | |||
cookStr = fish.cookingLevel | |||
end | |||
result = result..'\r\n| style="text-align:right"|'..cookStr | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | end | ||
function p. | function p.getFishingAreasTable(frame) | ||
local result = '{| class="wikitable sortable stickyHeader"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance' | |||
result = result..'\r\n!Junk Chance\r\n!Special Chance' | |||
for i, area in Shared.skpairs(SkillData.Fishing.Areas) do | |||
result = result..'\r\n|-' | |||
result = result..'\r\n| style ="text-align: left;" |'..area.name | |||
local fishArray = {} | |||
for j, fish in Shared.skpairs(area.fish) do | |||
local fishTable = Items.getItems(function(item) return item.fishingID == fish end) | |||
local fishItem = fishTable[0] or fishTable[1] | |||
table.insert(fishArray, Icons.Icon({fishItem.name, type='item'})) | |||
end | |||
result = result..'\r\n|'..table.concat(fishArray, '<br />') | |||
result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%' | |||
result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%' | |||
result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%' | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | |||
function p.getThievingNPC(npcName) | |||
local result = nil | |||
for i, npc in Shared.skpairs(SkillData.Thieving.NPCs) do | |||
if npc.name == npcName then | |||
result = Shared.clone(npc) | |||
break | |||
end | |||
end | |||
return result | |||
end | |||
function p.getThievingNPCArea(npc) | |||
if type(npc) == 'string' then | |||
npc = p.getThievingNPC(npc) | |||
end | |||
local result = nil | |||
for i, area in Shared.skpairs(SkillData.Thieving.Areas) do | |||
for j, npcID in pairs(area.npcs) do | |||
if npcID == npc.id then | |||
result = area | |||
break | |||
end | |||
end | |||
end | |||
return result | |||
end | |||
function p._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 | end | ||
function p. | 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 | |||
function p.getThievingGeneralRareTable(frame) | |||
local rareTxt = '{|class="wikitable sortable"' | |||
rareTxt = rareTxt..'\r\n!Item!!Qty' | |||
rareTxt = rareTxt..'!!Price!!colspan="2"|Chance' | |||
for i, drop in pairs(SkillData.Thieving.RareItems) do | |||
local thisItem = Items.getItemByID(drop.itemID) | |||
local odds = drop.chance | |||
rareTxt = rareTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}) | |||
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor) | |||
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(1, Shared.round2(1/(odds/100), 0)) | |||
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.round(odds, 4, 4)..'%' | |||
end | |||
rareTxt = rareTxt..'\r\n|}' | |||
return rareTxt | |||
end | |||
function p._getThievingNPCLootTables(npc) | |||
local result = '' | |||
local sectionTxt = {} | |||
--Five sections here: GP, normal loot, area loot, rare loot, and unique item | |||
--First up, GP: | |||
local gpTxt = 'Successfully pickpocketing the '..npc.name..' will always give '..Icons.GP(1, npc.maxGP) | |||
table.insert(sectionTxt, gpTxt) | |||
--Next up, normal loot: | |||
--(Skip if no loot) | |||
if npc.lootTable ~= nil and Shared.tableCount(npc.lootTable) > 0 then | |||
local normalTxt = '===Possible Common Drops:===\r\nUp to one of these will be received on a successful pickpocket:' | |||
local totalWt = 0 | |||
local lootChance = thievingNormalLootChance | |||
local lootValue = 0 | |||
--First loop through to get the total weight so we have it for later | |||
for i, loot in pairs(npc.lootTable) do | |||
totalWt = totalWt + loot[2] | |||
end | |||
normalTxt = normalTxt..'\r\n{|class="wikitable sortable"' | |||
normalTxt = normalTxt..'\r\n!Item!!Qty' | |||
normalTxt = normalTxt..'!!Price!!colspan="2"|Chance' | |||
--Then sort the loot table by weight | |||
table.sort(npc.lootTable, function(a, b) return a[2] > b[2] end) | |||
for i, row in Shared.skpairs(npc.lootTable) do | |||
local thisItem = Items.getItemByID(row[1]) | |||
local maxQty = row[3] | |||
if thisItem ~= nil then | |||
normalTxt = normalTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}) | |||
else | |||
normalTxt = normalTxt..'\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]' | |||
end | |||
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..maxQty..'"|' | |||
if maxQty > 1 then | |||
normalTxt = normalTxt.. '1 - ' | |||
end | |||
normalTxt = normalTxt..Shared.formatnum(row[3]) | |||
--Adding price columns | |||
local itemPrice = 0 | |||
if thisItem == nil then | |||
normalTxt = normalTxt..'||data-sort-value="0"|???' | |||
else | |||
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0 | |||
if itemPrice == 0 or maxQty == 1 then | |||
normalTxt = normalTxt..'||'..Icons.GP(itemPrice) | |||
else | |||
normalTxt = normalTxt..'||'..Icons.GP(itemPrice, itemPrice * maxQty) | |||
end | |||
end | |||
--Getting the drop chance | |||
local dropChance = (row[2] / totalWt * lootChance) | |||
if dropChance ~= 100 then | |||
--Show fraction as long as it isn't going to be 1/1 | |||
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..row[2]..'"' | |||
normalTxt = normalTxt..'|'..Shared.fraction(row[2] * lootChance, totalWt * 100) | |||
normalTxt = normalTxt..'||' | |||
else | |||
normalTxt = normalTxt..'||colspan="2" data-sort-value="'..row[2]..'"' | |||
end | |||
normalTxt = normalTxt..'style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%' | |||
--Adding to the average loot value based on price & dropchance | |||
lootValue = lootValue + (dropChance * 0.01 * itemPrice * ((1 + maxQty) / 2)) | |||
end | |||
if multiDrop then | |||
normalTxt = normalTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:' | |||
if lootChance < 100 then | |||
normalTxt = normalTxt..'\r\n|style="text-align:right"|'..Shared.fraction(lootChance, 100)..'||' | |||
else | |||
normalTxt = normalTxt..'\r\n|colspan="2" ' | |||
end | |||
normalTxt = normalTxt..'style="text-align:right"|'..lootChance..'.00%' | |||
end | |||
normalTxt = normalTxt..'\r\n|}' | |||
table.insert(sectionTxt, normalTxt) | |||
end | |||
--After normal drops, add in rare drops | |||
local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket' | |||
rareTxt = rareTxt..'\r\n'..p.getThievingGeneralRareTable() | |||
table.insert(sectionTxt, rareTxt) | |||
local areaTxt = '===Possible Area Unique Drops===' | |||
areaTxt = areaTxt..'\r\nAny Area Unique Drop is equally likely to be obtained after a successful pickpocket. ' | |||
areaTxt = areaTxt..'\r\nEach Area Unique Drop is rolled for separately, so it is possible to receive multiple Area Unique Drops from a single action. ' | |||
areaTxt = areaTxt..'The chance of receiving an Area Unique drop is tripled if the 95% Thieving Mastery Pool checkpoint is active.' | |||
local area = p.getThievingNPCArea(npc) | |||
areaTxt = areaTxt..'\r\n{|class="wikitable sortable"' | |||
areaTxt = areaTxt..'\r\n!Item!!Qty' | |||
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance' | |||
local dropCount = Shared.tableCount(area.uniqueDrops) | |||
local dropLines = {} | |||
for i, drop in pairs(area.uniqueDrops) do | |||
local thisItem = Items.getItemByID(drop.itemID) | |||
local lineTxt = '' | |||
lineTxt = lineTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}) | |||
lineTxt = lineTxt..'||'..drop.qty..'||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor) | |||
lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100)) | |||
lineTxt = lineTxt..'||'..Shared.round(thievingAreaLootChance, 2, 2)..'%' | |||
dropLines[thisItem.name] = lineTxt | |||
end | |||
for i, txt in Shared.skpairs(dropLines) do | |||
areaTxt = areaTxt..txt | |||
end | |||
areaTxt = areaTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:' | |||
areaTxt = areaTxt..'\r\n|style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100))..'||' | |||
areaTxt = areaTxt..'style="text-align:right"|'..Shared.round(thievingAreaLootChance, 2, 2)..'%' | |||
areaTxt = areaTxt..'\r\n|}' | |||
table.insert(sectionTxt, areaTxt) | |||
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then | |||
local uniqueTxt = '===Possible NPC Unique Drop===' | |||
uniqueTxt = uniqueTxt..'\r\nThe chance of receiving the unique drop for an NPC is based on a combination of several factors.' | |||
uniqueTxt = uniqueTxt..' The unique drop chance for an NPC is included in the tooltip for your Stealth against that NPC.' | |||
local thisItem = Items.getItemByID(npc.uniqueDrop.itemID) | |||
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is ' | |||
if npc.uniqueDrop.qty > 1 then | |||
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.qty}) | |||
else | |||
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'}) | |||
end | |||
table.insert(sectionTxt, uniqueTxt) | |||
end | |||
return table.concat(sectionTxt, '\r\n') | |||
end | |||
function p.getThievingNPCLootTables(frame) | |||
local npcName = frame.args ~= nil and frame.args[1] or frame | |||
local npc = p.getThievingNPC(npcName) | |||
if npc == nil then | |||
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]" | |||
end | |||
return p._getThievingNPCLootTables(npc) | |||
end | |||
function p.getThievingNPCTable() | |||
local result = '{| class="wikitable sortable stickyHeader"' | |||
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' | |||
local npcArray = Shared.clone(SkillData.Thieving.NPCs) | |||
table.sort(npcArray, function(a, b) return a.level < b.level end) | |||
for i, npc in Shared.skpairs(npcArray) do | |||
result = result..'\r\n|-' | |||
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true}) | |||
result = result..'||'..Icons.Icon({npc.name, type='thieving', noicon=true}) | |||
local area = p.getThievingNPCArea(npc) | |||
result = result..'||'..area.name | |||
result = result..'||'..Icons._SkillReq('Thieving', npc.level) | |||
result = result..'||style="text-align:right"|'..npc.xp | |||
result = result..'||style="text-align:right"|'..(npc.maxHit * 10) | |||
result = result..'||style="text-align:right"|'..npc.perception | |||
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then | |||
local uniqueDrop = Items.getItemByID(npc.uniqueDrop.itemID) | |||
if npc.uniqueDrop.qty > 1 then | |||
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item', qty = npc.uniqueDrop.qty}) | |||
else | |||
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item'}) | |||
end | |||
else | |||
result = result..'|| ' | |||
end | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | |||
function p.getThievingAreaTable(frame) | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"') | |||
table.insert(resultPart, '\r\n|- class="headerRow-0"') | |||
table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops') | |||
local areaArray = Shared.clone(SkillData.Thieving.Areas) | |||
table.sort(areaArray, function(a, b) return a.id < b.id end) | |||
for i, area in ipairs(areaArray) do | |||
local minLevel, npcList, areaItemList = nil, {}, {} | |||
-- Build NPC list & determine level for area, this is the minimum | |||
-- Thieving level required for all NPCs within that area | |||
if area.npcs ~= nil and Shared.tableCount(area.npcs) > 0 then | |||
for j, npcID in ipairs(area.npcs) do | |||
-- Don't bother cloning the NPC below since we aren't modifying any part of it | |||
local npc = SkillData.Thieving.NPCs[npcID + 1] | |||
if minLevel == nil or npc.level < minLevel then | |||
minLevel = npc.level | |||
end | |||
table.insert(npcList, Icons.Icon({npc.name, type='thieving'})) | |||
end | |||
else | |||
table.insert(npcList, '') | |||
end | |||
-- Build area unique item list | |||
if area.uniqueDrops ~= nil and Shared.tableCount(area.uniqueDrops) > 0 then | |||
for k, drop in ipairs(area.uniqueDrops) do | |||
local areaItem = Items.getItemByID(drop.itemID) | |||
if areaItem == nil then | |||
table.insert(areaItemList, 'Unknown[[Category:Pages with script errors]]') | |||
else | |||
local iconDef = {areaItem.name, type='item'} | |||
if drop.qty > 1 then | |||
iconDef.qty = drop.qty | |||
end | |||
table.insert(areaItemList, Icons.Icon(iconDef)) | |||
end | |||
end | |||
else | |||
table.insert(areaItemList, '') | |||
end | |||
-- Generate table row | |||
table.insert(resultPart, '\r\n|-') | |||
table.insert(resultPart, '\r\n|' .. area.name) | |||
table.insert(resultPart, '\r\n|' .. Icons._SkillReq('Thieving', minLevel)) | |||
table.insert(resultPart, '\r\n|' .. table.concat(npcList, '<br/>')) | |||
table.insert(resultPart, '\r\n|' .. table.concat(areaItemList, '<br/>')) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getThievingSourcesForItem(itemID) | |||
local resultArray = {} | |||
local areaNPCs = {} | |||
--First check area unique drops | |||
--If an area drops the item, add all the NPC ids to the list so we can add them later | |||
if not result then | |||
for i, area in pairs(SkillData.Thieving.Areas) do | |||
for j, drop in pairs(area.uniqueDrops) do | |||
if drop.itemID == itemID then | |||
for k, npcID in pairs(area.npcs) do | |||
areaNPCs[npcID] = drop.qty | |||
end | |||
break | |||
end | |||
end | |||
end | |||
end | |||
--Now go through and get drop chances on each NPC if needed | |||
for i, npc in pairs(SkillData.Thieving.NPCs) do | |||
local totalWt = 0 | |||
local dropWt = 0 | |||
local dropQty = 0 | |||
for j, drop in pairs(npc.lootTable) do | |||
totalWt = totalWt + drop[2] | |||
if drop[1] == itemID then | |||
dropWt = drop[2] | |||
dropQty = drop[3] | |||
end | |||
end | |||
if dropWt > 0 then | |||
table.insert(resultArray, {npc = npc.name, minQty = 1, maxQty = dropQty, wt = dropWt * thievingNormalLootChance, totalWt = totalWt * 100, level = npc.level}) | |||
end | |||
--Chance of -1 on unique drops is to indicate variable chance | |||
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID == itemID then | |||
table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.qty, maxQty = npc.uniqueDrop.qty, wt = -1, totalWt = -1, level = npc.level}) | |||
end | |||
if areaNPCs[npc.id] ~= nil then | |||
table.insert(resultArray, {npc = npc.name, minQty = areaNPCs[npc.id], maxQty = areaNPCs[npc.id], wt = thievingAreaLootChance, totalWt = 100, level = npc.level}) | |||
end | |||
end | |||
for i, drop in pairs(SkillData.Thieving.RareItems) do | |||
if drop.itemID == itemID then | |||
table.insert(resultArray, {npc = 'all', minQty = 1, maxQty = 1, wt = 1, totalWt = Shared.round2(1/(drop.chance/100), 0), level = 1}) | |||
end | |||
end | |||
return resultArray | |||
end | end | ||
return p | return p |
Revision as of 22:15, 5 November 2021
Documentation for this module may be created at Module:Skills/Gathering/doc
--Splitting some functions into here to avoid bloating a single file
local p = {}
local SkillData = mw.loadData('Module:Skills/data')
local ShopData = mw.loadData('Module:Shop/data')
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local thievingNormalLootChance = 75
local thievingAreaLootChance = 0.2
function p.getAxeTable(frame)
local toolArray = {}
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
if Shared.contains(upgrade.name, 'Axe') then
table.insert(toolArray, upgrade)
end
end
local result = '{| class="wikitable"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
result = result..'!!Cost!!This Axe!!Total'
local total = 0
for i, tool in Shared.skpairs(toolArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
result = result..'||'..tool.name
local level = 1
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
--Gonna be lazy and assume there's only the one skill level and it's the one we care about
level = tool.unlockRequirements.skillLevel[1][2]
end
result = result..'||style="text-align:right"|'..level
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
end
result = result..'\r\n|}'
return result
end
function p.getPickaxeTable(frame)
local toolArray = {}
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
if Shared.contains(upgrade.name, 'Pickaxe') then
table.insert(toolArray, upgrade)
end
end
local result = '{| class="wikitable"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Mine Time Decrease!!colspan="2"|2x Ore Chance'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'
local total = 0
local total2 = 0
for i, tool in Shared.skpairs(toolArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
result = result..'||'..tool.name
local level = 1
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
--Gonna be lazy and assume there's only the one skill level and it's the one we care about
level = tool.unlockRequirements.skillLevel[1][2]
end
result = result..'||style="text-align:right"|'..level
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
local OreDouble = tool.contains.modifiers.increasedChanceToDoubleOres
total2 = total2 + OreDouble
result = result..'||style="text-align:right"|+'..OreDouble..'%'
result = result..'||style="text-align:right"|+'..total2..'%'
end
result = result..'\r\n|}'
return result
end
function p.getRodTable(frame)
local toolArray = {}
for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
if Shared.contains(upgrade.name, 'Fishing Rod') then
table.insert(toolArray, upgrade)
end
end
local result = '{| class="wikitable"'
result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
result = result..'!!Cost!!This Rod!!Total'
local total = 0
for i, tool in Shared.skpairs(toolArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
result = result..'||'..tool.name
local level = 1
if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
--Gonna be lazy and assume there's only the one skill level and it's the one we care about
level = tool.unlockRequirements.skillLevel[1][2]
end
result = result..'||style="text-align:right"|'..level
result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
total = total + cutTime
result = result..'||style="text-align:right"|-'..cutTime..'%'
result = result..'||style="text-align:right"|-'..total..'%'
end
result = result..'\r\n|}'
return result
end
function p.getTreesTable(frame)
local result = '{| class="wikitable sortable"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Tree!!colspan="2"|Logs!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
result = result..'!!XP!!Cut Time!!XP/s!!GP/s'
for i, tree in Shared.skpairs(SkillData.Woodcutting.Trees) do
result = result..'\r\n|-'
local treeName = Shared.titleCase(tree.type..' tree')
local logName = Shared.titleCase(tree.type..' logs')
result = result..'\r\n|style="min-width:25px" data-sort-value="'..treeName..'"|'..Icons.Icon({logName, img=treeName, type='tree', notext=true, size=50})
result = result..'||'..treeName..''
result = result..'||style="min-width:25px" data-sort-value="'..logName..'"|'..Icons.Icon({logName, type='item', notext=true, size=50})
result = result..'||'..Icons.Icon({logName, type='item', noicon=true})
result = result..'||style="text-align:right"|'..tree.level
result = result..'||style="text-align:right"|'..tree.xp
result = result..'||style="text-align:right" data-sort-value="'..tree.interval..'"|'..Shared.timeString(tree.interval/1000, true)
local XPs = tree.xp / (tree.interval / 1000)
local Log = Items.getItemByID(i - 1)
local GPs = Log.sellsFor / (tree.interval / 1000)
result = result..'||style="text-align:right"|'..Shared.round(XPs, 2, 2)
result = result..'||style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2))
end
result = result..'\r\n|}'
return result
end
function p.getSpecialFishingTable(frame)
local lootValue = 0
local totalWt = Items.specialFishWt
local result = ''
result = result..'\r\n{|class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!Item'
result = result..'!!Price!!colspan="2"|Chance'
--Sort the loot table by weight in descending order
table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
for i, row in pairs(Items.specialFishLoot) do
local thisItem = Items.getItemByID(row[1])
result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
result = result..'|'..Icons.GP(thisItem.sellsFor)
local dropChance = (row[2] / totalWt) * 100
result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
result = result..'|'..Shared.fraction(row[2], totalWt)
result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
end
result = result..'\r\n|}'
result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0))
return result
end
function p.getFishingJunkTable(frame)
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Item!!Value'
local itemArray = Items.getItems(function(item) return item.type == "Junk" end)
table.sort(itemArray, function(a, b) return a.name < b.name end)
for i, item in Shared.skpairs(itemArray) do
result = result..'\r\n|-'
result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext=true, size='50'})
result = result..'||'..Icons.Icon({item.name, type='item', noicon=true})
result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
end
result = result..'\r\n|}'
return result
end
function p.getMiningOresTable(frame)
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
result = result..'!!XP!!Respawn Time!!Ore Value'
local mineData = Shared.clone(SkillData.Mining.Rocks)
table.sort(mineData, function(a, b) return a.level < b.level end)
for i, oreData in Shared.skpairs(mineData) do
local ore = Items.getItemByID(oreData.ore)
result = result..'\r\n|-\r\n|style="min-width:25px"|'..Icons.Icon({ore.name, type='item', size='50', notext=true})
result = result..'||'..Icons.Icon({ore.name, type='item', noicon=true})
result = result..'||style="text-align:right"|'..oreData.level..'||style="text-align:right"|'..ore.miningXP
result = result..'||style="text-align:right" data-sort-value="'..oreData.respawnInterval..'"|'
result = result..Shared.timeString(oreData.respawnInterval / 1000, true)
result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
end
result = result..'\r\n|}'
return result
end
function p.getMiningGemsTable(frame)
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan=2|Gem!!Gem Chance!!Gem Price'
-- Sort gems by ID order
for i, gemData in Shared.spairs(Items.GemTable, function(t,a,b) return t[a].id < t[b].id end) do
local gem = Items.getItemByID(gemData.id)
result = result..'\r\n|-\r\n|style="min-width:25px"|'
result = result..Icons.Icon({gem.name, type='item', size='50', notext=true})
result = result..'||'..Icons.Icon({gem.name, type='item', noicon=true})
result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.chance)
result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor)
end
result = result..'\r\n|}'
return result
end
function p.getFishTable(frame)
local data = Items.getItems(function(item) return item.fishingID ~= nil end)
table.sort(data, function(a, b) return a.fishingID < b.fishingID end)
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!Fish\r\n!Name\r\n!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level\r\n!Catch Time'
result = result..'\r\n!Experience\r\n!Fish Price\r\n!XP/s\r\n!GP/s\r\n!'
result = result..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
for i, fish in Shared.skpairs(data) do
result = result..'\r\n|-'
result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', size='50', notext=true})
result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', noicon=true})
result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel
local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2
local 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 XPs = fish.fishingXP / (timeSortVal / 1000)
local GPs = fish.sellsFor / (timeSortVal / 1000)
result = result..'\r\n| style="text-align:right"|'..Shared.round(XPs, 2, 2)
result = result..'\r\n| style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2))
local cookStr = "N/A"
if fish.cookingLevel ~= nil then
cookStr = fish.cookingLevel
end
result = result..'\r\n| style="text-align:right"|'..cookStr
end
result = result..'\r\n|}'
return result
end
function p.getFishingAreasTable(frame)
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance'
result = result..'\r\n!Junk Chance\r\n!Special Chance'
for i, area in Shared.skpairs(SkillData.Fishing.Areas) do
result = result..'\r\n|-'
result = result..'\r\n| style ="text-align: left;" |'..area.name
local fishArray = {}
for j, fish in Shared.skpairs(area.fish) do
local fishTable = Items.getItems(function(item) return item.fishingID == fish end)
local fishItem = fishTable[0] or fishTable[1]
table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
end
result = result..'\r\n|'..table.concat(fishArray, '<br />')
result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%'
result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
end
result = result..'\r\n|}'
return result
end
function p.getThievingNPC(npcName)
local result = nil
for i, npc in Shared.skpairs(SkillData.Thieving.NPCs) do
if npc.name == npcName then
result = Shared.clone(npc)
break
end
end
return result
end
function p.getThievingNPCArea(npc)
if type(npc) == 'string' then
npc = p.getThievingNPC(npc)
end
local result = nil
for i, area in Shared.skpairs(SkillData.Thieving.Areas) do
for j, npcID in pairs(area.npcs) do
if npcID == npc.id then
result = area
break
end
end
end
return result
end
function p._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
function p.getThievingGeneralRareTable(frame)
local rareTxt = '{|class="wikitable sortable"'
rareTxt = rareTxt..'\r\n!Item!!Qty'
rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
for i, drop in pairs(SkillData.Thieving.RareItems) do
local thisItem = Items.getItemByID(drop.itemID)
local odds = drop.chance
rareTxt = rareTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(1, Shared.round2(1/(odds/100), 0))
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.round(odds, 4, 4)..'%'
end
rareTxt = rareTxt..'\r\n|}'
return rareTxt
end
function p._getThievingNPCLootTables(npc)
local result = ''
local sectionTxt = {}
--Five sections here: GP, normal loot, area loot, rare loot, and unique item
--First up, GP:
local gpTxt = 'Successfully pickpocketing the '..npc.name..' will always give '..Icons.GP(1, npc.maxGP)
table.insert(sectionTxt, gpTxt)
--Next up, normal loot:
--(Skip if no loot)
if npc.lootTable ~= nil and Shared.tableCount(npc.lootTable) > 0 then
local normalTxt = '===Possible Common Drops:===\r\nUp to one of these will be received on a successful pickpocket:'
local totalWt = 0
local lootChance = thievingNormalLootChance
local lootValue = 0
--First loop through to get the total weight so we have it for later
for i, loot in pairs(npc.lootTable) do
totalWt = totalWt + loot[2]
end
normalTxt = normalTxt..'\r\n{|class="wikitable sortable"'
normalTxt = normalTxt..'\r\n!Item!!Qty'
normalTxt = normalTxt..'!!Price!!colspan="2"|Chance'
--Then sort the loot table by weight
table.sort(npc.lootTable, function(a, b) return a[2] > b[2] end)
for i, row in Shared.skpairs(npc.lootTable) do
local thisItem = Items.getItemByID(row[1])
local maxQty = row[3]
if thisItem ~= nil then
normalTxt = normalTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
else
normalTxt = normalTxt..'\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]'
end
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..maxQty..'"|'
if maxQty > 1 then
normalTxt = normalTxt.. '1 - '
end
normalTxt = normalTxt..Shared.formatnum(row[3])
--Adding price columns
local itemPrice = 0
if thisItem == nil then
normalTxt = normalTxt..'||data-sort-value="0"|???'
else
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
if itemPrice == 0 or maxQty == 1 then
normalTxt = normalTxt..'||'..Icons.GP(itemPrice)
else
normalTxt = normalTxt..'||'..Icons.GP(itemPrice, itemPrice * maxQty)
end
end
--Getting the drop chance
local dropChance = (row[2] / totalWt * lootChance)
if dropChance ~= 100 then
--Show fraction as long as it isn't going to be 1/1
normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..row[2]..'"'
normalTxt = normalTxt..'|'..Shared.fraction(row[2] * lootChance, totalWt * 100)
normalTxt = normalTxt..'||'
else
normalTxt = normalTxt..'||colspan="2" data-sort-value="'..row[2]..'"'
end
normalTxt = normalTxt..'style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
--Adding to the average loot value based on price & dropchance
lootValue = lootValue + (dropChance * 0.01 * itemPrice * ((1 + maxQty) / 2))
end
if multiDrop then
normalTxt = normalTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
if lootChance < 100 then
normalTxt = normalTxt..'\r\n|style="text-align:right"|'..Shared.fraction(lootChance, 100)..'||'
else
normalTxt = normalTxt..'\r\n|colspan="2" '
end
normalTxt = normalTxt..'style="text-align:right"|'..lootChance..'.00%'
end
normalTxt = normalTxt..'\r\n|}'
table.insert(sectionTxt, normalTxt)
end
--After normal drops, add in rare drops
local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket'
rareTxt = rareTxt..'\r\n'..p.getThievingGeneralRareTable()
table.insert(sectionTxt, rareTxt)
local areaTxt = '===Possible Area Unique Drops==='
areaTxt = areaTxt..'\r\nAny Area Unique Drop is equally likely to be obtained after a successful pickpocket. '
areaTxt = areaTxt..'\r\nEach Area Unique Drop is rolled for separately, so it is possible to receive multiple Area Unique Drops from a single action. '
areaTxt = areaTxt..'The chance of receiving an Area Unique drop is tripled if the 95% Thieving Mastery Pool checkpoint is active.'
local area = p.getThievingNPCArea(npc)
areaTxt = areaTxt..'\r\n{|class="wikitable sortable"'
areaTxt = areaTxt..'\r\n!Item!!Qty'
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
local dropCount = Shared.tableCount(area.uniqueDrops)
local dropLines = {}
for i, drop in pairs(area.uniqueDrops) do
local thisItem = Items.getItemByID(drop.itemID)
local lineTxt = ''
lineTxt = lineTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
lineTxt = lineTxt..'||'..drop.qty..'||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100))
lineTxt = lineTxt..'||'..Shared.round(thievingAreaLootChance, 2, 2)..'%'
dropLines[thisItem.name] = lineTxt
end
for i, txt in Shared.skpairs(dropLines) do
areaTxt = areaTxt..txt
end
areaTxt = areaTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
areaTxt = areaTxt..'\r\n|style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100))..'||'
areaTxt = areaTxt..'style="text-align:right"|'..Shared.round(thievingAreaLootChance, 2, 2)..'%'
areaTxt = areaTxt..'\r\n|}'
table.insert(sectionTxt, areaTxt)
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
local uniqueTxt = '===Possible NPC Unique Drop==='
uniqueTxt = uniqueTxt..'\r\nThe chance of receiving the unique drop for an NPC is based on a combination of several factors.'
uniqueTxt = uniqueTxt..' The unique drop chance for an NPC is included in the tooltip for your Stealth against that NPC.'
local thisItem = Items.getItemByID(npc.uniqueDrop.itemID)
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
if npc.uniqueDrop.qty > 1 then
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.qty})
else
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'})
end
table.insert(sectionTxt, uniqueTxt)
end
return table.concat(sectionTxt, '\r\n')
end
function p.getThievingNPCLootTables(frame)
local npcName = frame.args ~= nil and frame.args[1] or frame
local npc = p.getThievingNPC(npcName)
if npc == nil then
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
end
return p._getThievingNPCLootTables(npc)
end
function p.getThievingNPCTable()
local result = '{| class="wikitable sortable stickyHeader"'
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'
local npcArray = Shared.clone(SkillData.Thieving.NPCs)
table.sort(npcArray, function(a, b) return a.level < b.level end)
for i, npc in Shared.skpairs(npcArray) do
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true})
result = result..'||'..Icons.Icon({npc.name, type='thieving', noicon=true})
local area = p.getThievingNPCArea(npc)
result = result..'||'..area.name
result = result..'||'..Icons._SkillReq('Thieving', npc.level)
result = result..'||style="text-align:right"|'..npc.xp
result = result..'||style="text-align:right"|'..(npc.maxHit * 10)
result = result..'||style="text-align:right"|'..npc.perception
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
local uniqueDrop = Items.getItemByID(npc.uniqueDrop.itemID)
if npc.uniqueDrop.qty > 1 then
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item', qty = npc.uniqueDrop.qty})
else
result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item'})
end
else
result = result..'|| '
end
end
result = result..'\r\n|}'
return result
end
function p.getThievingAreaTable(frame)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')
local areaArray = Shared.clone(SkillData.Thieving.Areas)
table.sort(areaArray, function(a, b) return a.id < b.id end)
for i, area in ipairs(areaArray) do
local minLevel, npcList, areaItemList = nil, {}, {}
-- Build NPC list & determine level for area, this is the minimum
-- Thieving level required for all NPCs within that area
if area.npcs ~= nil and Shared.tableCount(area.npcs) > 0 then
for j, npcID in ipairs(area.npcs) do
-- Don't bother cloning the NPC below since we aren't modifying any part of it
local npc = SkillData.Thieving.NPCs[npcID + 1]
if minLevel == nil or npc.level < minLevel then
minLevel = npc.level
end
table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
end
else
table.insert(npcList, '')
end
-- Build area unique item list
if area.uniqueDrops ~= nil and Shared.tableCount(area.uniqueDrops) > 0 then
for k, drop in ipairs(area.uniqueDrops) do
local areaItem = Items.getItemByID(drop.itemID)
if areaItem == nil then
table.insert(areaItemList, 'Unknown[[Category:Pages with script errors]]')
else
local iconDef = {areaItem.name, type='item'}
if drop.qty > 1 then
iconDef.qty = drop.qty
end
table.insert(areaItemList, Icons.Icon(iconDef))
end
end
else
table.insert(areaItemList, '')
end
-- Generate table row
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|' .. area.name)
table.insert(resultPart, '\r\n|' .. Icons._SkillReq('Thieving', minLevel))
table.insert(resultPart, '\r\n|' .. table.concat(npcList, '<br/>'))
table.insert(resultPart, '\r\n|' .. table.concat(areaItemList, '<br/>'))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getThievingSourcesForItem(itemID)
local resultArray = {}
local areaNPCs = {}
--First check area unique drops
--If an area drops the item, add all the NPC ids to the list so we can add them later
if not result then
for i, area in pairs(SkillData.Thieving.Areas) do
for j, drop in pairs(area.uniqueDrops) do
if drop.itemID == itemID then
for k, npcID in pairs(area.npcs) do
areaNPCs[npcID] = drop.qty
end
break
end
end
end
end
--Now go through and get drop chances on each NPC if needed
for i, npc in pairs(SkillData.Thieving.NPCs) do
local totalWt = 0
local dropWt = 0
local dropQty = 0
for j, drop in pairs(npc.lootTable) do
totalWt = totalWt + drop[2]
if drop[1] == itemID then
dropWt = drop[2]
dropQty = drop[3]
end
end
if dropWt > 0 then
table.insert(resultArray, {npc = npc.name, minQty = 1, maxQty = dropQty, wt = dropWt * thievingNormalLootChance, totalWt = totalWt * 100, level = npc.level})
end
--Chance of -1 on unique drops is to indicate variable chance
if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID == itemID then
table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.qty, maxQty = npc.uniqueDrop.qty, wt = -1, totalWt = -1, level = npc.level})
end
if areaNPCs[npc.id] ~= nil then
table.insert(resultArray, {npc = npc.name, minQty = areaNPCs[npc.id], maxQty = areaNPCs[npc.id], wt = thievingAreaLootChance, totalWt = 100, level = npc.level})
end
end
for i, drop in pairs(SkillData.Thieving.RareItems) do
if drop.itemID == itemID then
table.insert(resultArray, {npc = 'all', minQty = 1, maxQty = 1, wt = 1, totalWt = Shared.round2(1/(drop.chance/100), 0), level = 1})
end
end
return resultArray
end
return p