1,048
edits
(getMiningOresTable: Fix & include shop purchases in requirements column) |
(Left align sources column) |
||
(32 intermediate revisions by 4 users not shown) | |||
Line 6: | Line 6: | ||
local GameData = require('Module:GameData') | local GameData = require('Module:GameData') | ||
local SkillData = GameData.skillData | local SkillData = GameData.skillData | ||
local Common = require('Module:Common') | |||
local Common = require('Module:Common') | |||
local Items = require('Module:Items') | local Items = require('Module:Items') | ||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
local Skills = require('Module:Skills') | local Skills = require('Module:Skills') | ||
local ItemSourceTables = require('Module:Items/SourceTables') | local ItemSourceTables = require('Module:Items/SourceTables') | ||
function p.getRecipeRequirements(skillName, recipe) | |||
function p. | local reqText = {} | ||
local | if recipe.level ~= nil then | ||
table.insert(reqText, Icons._SkillReq(skillName, recipe.level, false)) | |||
end | end | ||
if recipe.totalMasteryRequired ~= nil then | |||
table.insert(reqText, Shared.formatnum(recipe.totalMasteryRequired) .. ' ' .. Icons.Icon({skillName, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'})) | |||
end | end | ||
if | if type(recipe.requirements) == 'table' then | ||
local reqs = Common.getRequirementString(recipe.requirements) | |||
if reqs ~= nil then | |||
table.insert(reqText, reqs) | |||
local | |||
if | |||
end | end | ||
end | end | ||
return table.concat(reqText, '<br/>') | |||
return table.concat( | |||
end | end | ||
function p.getTreesTable(frame) | function p.getTreesTable(frame) | ||
local | local resultPart = {} | ||
table.insert(resultPart, '{| class="wikitable sortable"') | |||
table.insert(resultPart, '\n|- class="headerRow-0"') | |||
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( | local trees = Shared.shallowClone(SkillData.Woodcutting.trees) | ||
table.sort(trees, function(a, b) return a.level < b.level end) | |||
for i, tree in ipairs(trees) do | |||
local log = Items.getItemByID(tree.productId) | local log = Items.getItemByID(tree.productId) | ||
table.insert(resultPart, '\n|-') | |||
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..tree.name..'"| '..Icons.Icon({log.name, img=tree.name, type='tree', notext=true, size=50})) | |||
table.insert(resultPart, '\n|data-sort-value="'..tree.name..'"|'..Icons.getExpansionIcon(tree.id)..tree.name) | |||
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..log.name..'"| '..Icons.Icon({log.name, type='item', notext=true, size=50})) | |||
table.insert(resultPart, '\n| '..Icons.Icon({log.name, type='item', noicon=true})) | |||
table.insert(resultPart, '\n|data-sort-value="' .. tree.level .. '"| ' .. p.getRecipeRequirements(SkillData.Woodcutting.name, tree)) | |||
table.insert(resultPart, '\n|style="text-align:right"| '..tree.baseExperience) | |||
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..tree.baseInterval..'"| '..Shared.timeString(tree.baseInterval/1000, true)) | |||
local XPs = tree.baseExperience / (tree.baseInterval / 1000) | local XPs = tree.baseExperience / (tree.baseInterval / 1000) | ||
local GPs = log.sellsFor / (tree.baseInterval / 1000) | local GPs = log.sellsFor / (tree.baseInterval / 1000) | ||
table.insert(resultPart, '\n|style="text-align:right"| '..Shared.round(XPs, 2, 2)) | |||
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..GPs..'"| '..Icons.GP(Shared.round(GPs, 2, 2))) | |||
end | end | ||
table.insert(resultPart, '\n|}') | |||
return | return table.concat(resultPart) | ||
end | end | ||
function p.getSpecialFishingTable(frame) | function p.getSpecialFishingTable(frame) | ||
local totalWt, lootValue = 0, 0 | local totalWt, lootValue = 0, 0 | ||
local itemArray = | local itemArray = Shared.shallowClone(SkillData.Fishing.specialItems) | ||
for i, itemDef in ipairs(itemArray) do | for i, itemDef in ipairs(itemArray) do | ||
totalWt = totalWt + itemDef.weight | totalWt = totalWt + itemDef.weight | ||
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 | table.sort(itemArray, function(a, b) return a.weight > b.weight end) | ||
local resultPart = {} | local resultPart = {} | ||
Line 166: | Line 77: | ||
-- 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| | 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 195: | Line 106: | ||
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| | 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 204: | Line 115: | ||
function p.getMiningOresTable(frame) | function p.getMiningOresTable(frame) | ||
local | local resultPart = {} | ||
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"') | |||
table.insert(resultPart, '\n|- class="headerRow-0"') | |||
table.insert(resultPart, '\n!colspan="2"|Rock!!colspan=2|Ore!!Type!!Requirements') | |||
table.insert(resultPart, '!!XP!!Respawn Time!!Ore Value') | |||
local mineData = | local mineData = Shared.shallowClone(SkillData.Mining.rockData) | ||
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 ipairs(mineData) do | for i, oreData in ipairs(mineData) do | ||
local ore = Items.getItemByID(oreData.productId) | local ore = Items.getItemByID(oreData.productId) | ||
local | local respawnStyle, respawnSort, respawnText = 'class="table-na"', 0, 'N/A' | ||
if oreData.hasPassiveRegen then | if oreData.hasPassiveRegen then | ||
respawnStyle = 'style="text-align:right;' | |||
respawnSort = oreData.baseRespawnInterval / 1000 | respawnSort = oreData.baseRespawnInterval / 1000 | ||
respawnText = Shared.timeString(respawnSort, true) | respawnText = Shared.timeString(respawnSort, true) | ||
end | 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 | ||
table.insert(resultPart, '\n|}') | |||
return | return table.concat(resultPart) | ||
end | end | ||
function p._getMiningGemsTable(gemType) | |||
function p. | if type(gemType) ~= 'string' then | ||
local | gemType = 'Normal' | ||
end | |||
local validTypes = { | |||
["Normal"] = 'randomGems', | |||
["Superior"] = 'randomSuperiorGems' | |||
} | |||
local gemDataKey = validTypes[gemType] | |||
if gemDataKey == nil then | |||
return Shared.printError('No such gem type "' .. gemType .. '"') | |||
end | |||
local gemData = GameData.rawData[gemDataKey] | |||
local totalWeight = 0 | local totalWeight = 0 | ||
for i, | for i, gem in ipairs(gemData) do | ||
totalWeight = totalWeight + | totalWeight = totalWeight + gem.weight | ||
end | end | ||
local resultPart = {} | |||
table.insert(resultPart, '{|class="wikitable sortable"') | |||
table.insert(resultPart, '\n|- class="headerRow-0"') | |||
table.insert(resultPart, '\n!colspan=2|Gem!!Gem Chance!!Gem Price') | |||
for i, | for i, gem in ipairs(gemData) do | ||
local | local gemItem = Items.getItemByID(gem.itemID) | ||
local gemPct = gem.weight / totalWeight * 100 | |||
table.insert(resultPart, '\n|-\n|class="table-img"| ') | |||
table.insert(resultPart, Icons.Icon({gemItem.name, type='item', size='50', notext=true})) | |||
table.insert(resultPart, '\n| data-sort-value="'..gemItem.name..'"|'..Icons.getExpansionIcon(gemItem.id) .. Icons.Icon({gemItem.name, type='item', noicon=true})) | |||
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 | ||
table.insert(resultPart, '\n|}') | |||
return | 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 | ||
function p.getFishTable(frame) | function p.getFishTable(frame) | ||
local recipeList = | local recipeList = Shared.shallowClone(SkillData.Fishing.fish) | ||
table.sort(recipeList, function(a, b) return a.level < b.level end) | table.sort(recipeList, function(a, b) return a.level < b.level end) | ||
Line 281: | Line 211: | ||
local resultPart = {} | local resultPart = {} | ||
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"') | table.insert(resultPart, '{| class="wikitable sortable stickyHeader"') | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|- class="headerRow-0"') | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n!colspan="2" rowspan="2"|Fish\n!rowspan="2"|' .. Icons.Icon({'Fishing', type='skill', notext=true}) .. ' Level') | ||
table.insert(resultPart, '\ | table.insert(resultPart, '\n!colspan="3"|Catch Time\n!rowspan="2"|XP\n!rowspan="2"|Value\n!rowspan="2"|XP/s\n!rowspan="2"|GP/s') | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n!rowspan="2"|' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' Level') | ||
table.insert(resultPart, '\n|- class="headerRow-1"\n!Min\n!Max\n!Avg') | |||
for i, recipe in ipairs(recipeList) do | for i, recipe in ipairs(recipeList) do | ||
local fish = Items.getItemByID(recipe. | local fish = Items.getItemByID(recipe.productId) | ||
if fish ~= nil then | if fish ~= nil then | ||
local timeMin, timeMax = recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000 | |||
local timeAvg = (timeMin + timeMax) / 2 | |||
local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000 | local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000 | ||
local timeStr = string.format("%.1fs - %.1fs", recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000) | local timeStr = string.format("%.1fs - %.1fs", recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000) | ||
local XPs = recipe.baseExperience / timeSortVal | local XPs = recipe.baseExperience / timeSortVal | ||
local GPs = fish.sellsFor / timeSortVal | local GPs = fish.sellsFor / timeSortVal | ||
local cookSortVal = cookReq[recipe. | local cookSortVal = cookReq[recipe.productId] or 0 | ||
local cookStr = cookReq[recipe. | local cookStyle = (cookReq[recipe.productId] ~= nil and 'style="text-align:right" ' or 'class="table-na" ') | ||
table.insert(resultPart, ' | local cookStr = cookReq[recipe.productId] or 'N/A' | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|-') | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|class="table-img"| ' .. Icons.Icon({fish.name, type='item', size='50', notext=true})) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|data-sort-value="'..fish.name..'"|'..Icons.getExpansionIcon(fish.id) .. Icons.Icon({fish.name, type='item', noicon=true})) | ||
table.insert(resultPart, '\ | table.insert(resultPart, '\n|style="text-align:right"| ' .. recipe.level) | ||
table.insert(resultPart, '\ | table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMin .. '"| ' .. string.format("%.1fs", timeMin)) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMax .. '"| ' .. string.format("%.1fs", timeMax)) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeAvg .. '"| ' .. string.format("%.1fs", timeAvg)) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"| ' .. Shared.formatnum(recipe.baseExperience)) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Icons.GP(fish.sellsFor)) | ||
table.insert(resultPart, '\n|style="text-align:right"| ' .. Shared.round(XPs, 2, 2)) | |||
table.insert(resultPart, '\n|data-sort-value="' .. GPs .. '"|' .. Icons.GP(Shared.round(GPs, 2, 2))) | |||
table.insert(resultPart, '\n|' .. cookStyle .. 'data-sort-value="' .. cookSortVal .. '"| ' .. cookStr) | |||
end | end | ||
end | end | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|}') | ||
return table.concat(resultPart) | return table.concat(resultPart) | ||
end | end | ||
Line 318: | Line 254: | ||
for i, area in ipairs(SkillData.Fishing.areas) do | for i, area in ipairs(SkillData.Fishing.areas) do | ||
result = result..'\r\n|-' | result = result..'\r\n|-' | ||
result = result..'\r\n| style ="text-align: left;" |'..area.name | result = result..'\r\n| style ="text-align: left;" |'..Icons.getExpansionIcon(area.id)..area.name | ||
local fishArray = {} | local fishArray = {} | ||
Line 338: | Line 274: | ||
end | end | ||
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 | 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 | ||
-- the item if it may be obtained while pickpocketing that NPC | |||
if npcID == nil or drop.npcs == nil or Shared.contains(drop.npcs, npcID) then | |||
local thisItem = Items.getItemByID(drop.itemID) | |||
local odds = drop.chance | |||
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 383: | Line 326: | ||
table.insert(normalTxt, '!!Price!!colspan="2"|Chance') | table.insert(normalTxt, '!!Price!!colspan="2"|Chance') | ||
local lootTable = | local lootTable = Shared.shallowClone(npc.lootTable) | ||
--Then sort the loot table by weight | --Then sort the loot table by weight | ||
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. | 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 395: | Line 338: | ||
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. | if loot.minQuantity ~= loot.maxQuantity then | ||
table.insert(normalTxt, Shared.formatnum(loot. | 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 407: | Line 350: | ||
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. | 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. | table.insert(normalTxt, '||'..Icons.GP(itemPrice * loot.minQuantity, itemPrice * loot.maxQuantity)) | ||
end | end | ||
end | end | ||
Line 427: | Line 370: | ||
--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. | 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 439: | Line 382: | ||
end | end | ||
table.insert(normalTxt, '\r\n|}') | table.insert(normalTxt, '\r\n|}') | ||
table.insert(normalTxt, '\r\nThe loot obtained from the average successful | 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 | 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. | rareTxt = rareTxt..'\r\n'..p._getThievingGeneralRareTable(npc.id) | ||
table.insert(sectionTxt, rareTxt) | table.insert(sectionTxt, rareTxt) | ||
Line 459: | Line 402: | ||
areaTxt = areaTxt..'!!Price!!colspan="2"|Chance' | areaTxt = areaTxt..'!!Price!!colspan="2"|Chance' | ||
local dropLines = {} | local dropLines = {} | ||
for i, drop in | 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 465: | Line 408: | ||
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. | lineTxt = lineTxt..'||'..Shared.round(SkillData.Thieving.baseAreaUniqueChance, 2, 2)..'%' | ||
dropLines[thisItem.name] = lineTxt | dropLines[thisItem.name] = lineTxt | ||
end | end | ||
for i, txt in | for i, txt in pairs(dropLines) do | ||
areaTxt = areaTxt..txt | areaTxt = areaTxt..txt | ||
end | end | ||
areaTxt = areaTxt..'\r\n|}' | areaTxt = areaTxt..'\r\n|}' | ||
table.insert(sectionTxt, areaTxt) | table.insert(sectionTxt, areaTxt) | ||
Line 485: | Line 425: | ||
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 500: | Line 440: | ||
local npc = Skills.getThievingNPC(npcName) | local npc = Skills.getThievingNPC(npcName) | ||
if npc == nil then | if npc == nil then | ||
return | return Shared.printError('Invalid Thieving NPC "' .. npcName .. '"') | ||
end | end | ||
Line 510: | Line 450: | ||
result = result..'\r\n|- class="headerRow-0"' | result = result..'\r\n|- class="headerRow-0"' | ||
result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!GP!!Unique Drop' | result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!GP!!Unique Drop' | ||
local npcArray = | local npcArray = Shared.shallowClone(SkillData.Thieving.npcs) | ||
table.sort(npcArray, function(a, b) return a.level < b.level end) | table.sort(npcArray, function(a, b) return a.level < b.level end) | ||
for i, npc in ipairs(npcArray) do | for i, npc in ipairs(npcArray) do | ||
result = result..'\r\n|-' | result = result..'\r\n|-' | ||
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true}) | result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true}) | ||
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 522: | Line 462: | ||
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 550: | Line 490: | ||
-- 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. | if area.npcIDs ~= nil and not Shared.tableIsEmpty(area.npcIDs) then | ||
for j, npcID in ipairs(area. | 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 595: | Line 535: | ||
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 ' | 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 605: | Line 545: | ||
end | end | ||
local | local tbl = mw.html.create() | ||
local html = tbl:tag("table") | |||
:addClass("wikitable sortable stickyHeader") | |||
:tag('tr'):addClass("headerRow-0") | |||
:tag('th'):attr("colspan", 2):wikitext("Seeds") | |||
:tag('th'):wikitext(Icons.Icon({'Farming', type='skill', notext=true}) .. " Level") | |||
:tag('th'):wikitext('XP') | |||
:tag('th'):wikitext('Growth Time') | |||
:tag('th'):wikitext('Seed Value') | |||
if category.id == 'melvorD:Allotment' then | if category.id == 'melvorD:Allotment' then | ||
html:tag('th'):attr("colspan", 2):wikitext("Crop") | |||
:tag('th'):wikitext('Crop Healing') | |||
:tag('th'):wikitext('Crop Value') | |||
elseif category.id == 'melvorD:Herb' then | elseif category.id == 'melvorD:Herb' then | ||
html:tag('th'):attr("colspan", 2):wikitext("Herb") | |||
:tag('th'):wikitext('Herb Value') | |||
elseif category.id == 'melvorD:Tree' then | elseif category.id == 'melvorD:Tree' then | ||
html:tag('th'):attr("colspan", 2):wikitext("Logs") | |||
:tag('th'):wikitext('Log Value') | |||
end | end | ||
html = html:tag('th'):wikitext('Seed Sources') | |||
table.sort(seedList, function(a, b) return a. | 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 624: | Line 574: | ||
local productItem = Items.getItemByID(seed.productId) | local productItem = Items.getItemByID(seed.productId) | ||
if seedItem ~= nil and productItem ~= nil then | if seedItem ~= nil and productItem ~= nil then | ||
html = | |||
html:tag('tr') | |||
:tag('td'):wikitext(Icons.Icon({seedItem.name, type='item', size='50', notext=true})) | |||
:tag('td'):wikitext(Icons.getExpansionIcon(seedItem.id) .. Icons.Icon({seedItem.name, type='item', noicon=true})) | |||
:tag('td'):wikitext(seed.level) | |||
:tag('td'):wikitext(Shared.formatnum(seed.baseExperience)) | |||
:tag('td'):attr('data-sort-value', (seed.baseInterval / 1000)) | |||
:wikitext(Shared.timeString(seed.baseInterval / 1000, true)) | |||
:tag('td'):attr('data-sort-value', seedItem.sellsFor) | |||
:wikitext(Icons.GP(seedItem.sellsFor)) | |||
:tag('td'):wikitext(Icons.Icon({productItem.name, type='item', size='50', notext=true})) | |||
:tag('td'):wikitext(Icons.getExpansionIcon(productItem.id) .. Icons.Icon({productItem.name, type='item', noicon=true})) | |||
if category.id == 'melvorD:Allotment' then | if category.id == 'melvorD:Allotment' then | ||
html:tag('td'):wikitext(Icons.Icon({'Hitpoints', type='skill', notext=true})) | |||
:wikitext(' ') | |||
:wikitext(((productItem.healsFor or 0) * 10)) | |||
end | end | ||
html = | |||
html:tag('td'):attr('data-sort-value', productItem.sellsFor) | |||
:wikitext(Icons.GP(productItem.sellsFor)) | |||
:tag('td'):wikitext(ItemSourceTables._getItemSources(seedItem)) | |||
:css('text-align', 'left') | |||
:done() | |||
end | end | ||
end | end | ||
return tostring(tbl:done()) | |||
return | |||
end | end | ||
Line 656: | Line 618: | ||
local recipes = GameData.getEntities(SkillData.Farming.recipes, | local recipes = GameData.getEntities(SkillData.Farming.recipes, | ||
function(recipe) | function(recipe) | ||
local product = Items.getItemByID(recipe. | 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 662: | Line 624: | ||
for i, recipe in ipairs(recipes) do | for i, recipe in ipairs(recipes) do | ||
local product = Items.getItemByID(recipe. | 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'})..'|| | 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 679: | Line 642: | ||
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 | local category = GameData.getEntityByName(SkillData.Farming.categories, areaName) | ||
if category == nil then | |||
return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees') | |||
end | end | ||
if | local patches = GameData.getEntities(SkillData.Farming.plots, | ||
return | function(plot) | ||
return plot.categoryID == category.id | |||
end) | |||
table.sort(patches, | |||
function(a, b) | |||
if a.level == b.level then | |||
return a.id < b.id | |||
else | |||
return a.level < b.level | |||
end | |||
end) | |||
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' | ||
for i, patch in | for i, patch in ipairs(patches) do | ||
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 | ||
local costText = Common.getCostString({ items = patch.itemCosts, gp = patch.gpCost }, 'Free') | |||
result = result..'||style="text-align:right;" data-sort-value="'..patch.gpCost..'"|'..costText | |||
end | end | ||
Line 708: | Line 677: | ||
function p._buildAstrologyConstellationTable() | function p._buildAstrologyConstellationTable() | ||
local | local resultPart = {} | ||
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"') | |||
table.insert(resultPart, '\n|- class="headerRow-0"') | |||
table.insert(resultPart, '\n!rowspan="2" colspan="2"|Constellation!!rowspan="2"| ' .. Icons.Icon({"Astrology", type='skill', notext='true'}) .. ' Level') | |||
table.insert(resultPart, '!!rowspan="2"| XP!!rowspan="2"| Skills!!colspan="2"| Standard Stars!!colspan="2"| Unique Stars') | |||
table.insert(resultPart, '\n|- class="headerRow-1"') | |||
table.insert(resultPart, string.rep('\n! ' .. Icons.Icon({'Mastery', notext=true}) .. 'Level\n! Modifiers', 2)) | |||
for i, cons in ipairs( | local recipes = Shared.shallowClone(SkillData.Astrology.recipes) | ||
table.sort(recipes, function(a, b) return a.level < b.level end) | |||
for i, cons in ipairs(recipes) do | |||
-- Generate the list of modifiers first for the purpose of determining the | |||
-- the number of rows required to display all stars | |||
local modTypes = { 'standard', 'unique' } | |||
local modsRaw = { | |||
["standard"] = Skills._buildAstrologyModifierArray(cons, nil, true, false, false, false), | |||
["unique"] = Skills._buildAstrologyModifierArray(cons, nil, false, true, false, false) | |||
} | |||
-- Building the list of standard & unique modifiers | |||
local mods = {} | |||
local modLevels = {} | |||
local maxRows = 1 | |||
for j, modType in ipairs(modTypes) do | |||
mods[modType] = {} | |||
modLevels[modType] = {} | |||
for k, modifier in ipairs(modsRaw[modType]) do | |||
local masteryLevel = modifier.group | |||
if mods[modType][masteryLevel] == nil then | |||
mods[modType][masteryLevel] = {} | |||
table.insert(modLevels[modType], masteryLevel) | |||
end | |||
local modMagnitude = type(modifier[2]) == 'table' and {modifier[2]} or modifier[2] | |||
table.insert(mods[modType][masteryLevel], Constants._getModifierText(modifier[1], modMagnitude, false)) | |||
end | |||
table.sort(modLevels[modType]) | |||
local typeCount = Shared.tableCount(modLevels[modType]) | |||
if typeCount > maxRows then | |||
maxRows = typeCount | |||
end | |||
end | |||
local name = cons.name | local name = cons.name | ||
local rowSpan = (maxRows > 1 and 'rowspan="' .. maxRows .. '"') or '' | |||
table.insert(resultPart, '\n|-') | |||
table.insert(resultPart, '\n|' .. rowSpan .. ' data-sort-value="' .. name .. '" id="'..name..'"| ') | |||
table.insert(resultPart, Icons.Icon({name, type='constellation', size='50', notext=true})) | |||
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. Icons.getExpansionIcon(cons.id) .. name) | |||
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.level) | |||
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.baseExperience) | |||
local skillIconArray = {} | local skillIconArray = {} | ||
for j, skillID in ipairs(cons. | 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 | ||
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. table.concat(skillIconArray, '<br/>')) | |||
-- Generate table text for standard & unique modifiers | |||
for row = 1, maxRows, 1 do | |||
-- | for j, modType in ipairs(modTypes) do | ||
for j, | local masteryLevel = (modLevels[modType] ~= nil and modLevels[modType][row]) or nil | ||
if masteryLevel ~= nil then | |||
table.insert(resultPart, '\n|style="text-align:right"| ' .. masteryLevel) | |||
table.insert(resultPart, '\n| ' .. table.concat(mods[modType][masteryLevel], '<br/>')) | |||
else | |||
table.insert(resultPart, '\n|colspan="2" class="table-na"| ') | |||
end | |||
end | |||
if row < maxRows then | |||
table.insert(resultPart, '\n|-') | |||
end | |||
end | end | ||
end | end | ||
table.insert(resultPart, '\n|}') | |||
return | return table.concat(resultPart) | ||
end | end | ||
function p.buildAstrologyConstellationTable(frame) | function p.buildAstrologyConstellationTable(frame) | ||
return p._buildAstrologyConstellationTable() | return p._buildAstrologyConstellationTable() | ||
end | end | ||
return p | return p |
edits