Module:Navboxes: Difference between revisions
From Melvor Idle
Inconvenient (talk | contribs) m (formatting error) |
(Changed Ores & Bars nav to show all ores and bars and sorted by (abyssalLevel+120 or level) to push ItA items to the end) |
||
(91 intermediate revisions by 6 users not shown) | |||
Line 2: | Line 2: | ||
local p = {} | local p = {} | ||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local GameData = require('Module:GameData') | |||
local SkillData = GameData.skillData | |||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
local Items = require('Module:Items') | |||
local Magic = require('Module:Magic') | |||
local Shop = require('Module:Shop') | |||
local Pets = require('Module:Pets') | |||
local Prayer = require('Module:Prayer') | |||
-- Generates a table with a single category which has an Item and it's Icon | |||
-- array is the list of items that will be added to the table | |||
-- iconType is the item's icon type | |||
-- headerData is the text displayed at the top of the generated table | |||
-- productID is the proper capitalization of the productID property -_- | |||
function p.buildSingleNavboxTable(array, iconType, headerData, productID) | |||
-- Generate Table contents | |||
table.sort(array, function(a, b) return a.level < b.level end) | |||
local iconArray = {} | |||
for i, item in ipairs(array) do | |||
if productID ~= nil then | |||
item = Items.getItemByID(item[productID]) | |||
end | |||
table.insert(iconArray, Icons.Icon({item.name, type=iconType, expicon=Icons.getExpansionIcon(item.id)})) | |||
end | |||
-- Generate navbox table | |||
local resultTable = mw.html.create('table') | |||
-- Table classes & styles | |||
resultTable | |||
:addClass('wikitable') | |||
:addClass('navigation-not-searchable') | |||
:css('text-align', 'center') | |||
:css('clear', 'both') | |||
:css('width', '100%') | |||
-- Header row | |||
:tag('tr') | |||
:tag('th') | |||
:css('background-color', '#275C87') | |||
:css('color', '#FFFFFF') | |||
:wikitext(Icons.Icon(headerData)) | |||
:done() | |||
:done() | |||
-- Content, list of logs | |||
:tag('tr') | |||
:tag('td') | |||
:wikitext(table.concat(iconArray, ' • ')) | |||
:done() | |||
:done() | |||
:done() | |||
return tostring(resultTable) | |||
end | |||
function p.getLogNavbox(frame) | |||
local trees = Shared.shallowClone(SkillData.Woodcutting.trees) | |||
return p.buildSingleNavboxTable(trees, 'item', {'Woodcutting', 'Logs', type='skill', section='Logs'}, 'productId') | |||
end | |||
function p.getFamiliarNavbox(frame) | |||
local familiars = Shared.shallowClone(SkillData.Summoning.recipes) | |||
return p.buildSingleNavboxTable(familiars, 'item', {'Summoning', 'Summoning Familiars', type='skill', section='Summoning_Tablets'}, 'productID') | |||
end | |||
function p.getThievingNavbox(frame) | |||
local npcs = Shared.shallowClone(SkillData.Thieving.npcs) | |||
return p.buildSingleNavboxTable(npcs, 'thieving', {'Thieving', 'Thieving Targets', type='skill', section='Thieving_Targets'}) | |||
end | |||
function p.getFarmingNavbox(frame) | |||
local resultPart = {} | |||
local seedsTable = {} | |||
local produceTable = {} | |||
for i, recipe in ipairs(SkillData.Farming.recipes) do | |||
local seed = Items.getItemByID(recipe.seedCost.id) | |||
local product = Items.getItemByID(recipe.productId) | |||
local tier = recipe.categoryID | |||
if seedsTable[tier] == nil then | |||
-- Initialize tier tables | |||
seedsTable[tier] = {} | |||
produceTable[tier] = {} | |||
end | |||
table.insert(seedsTable[tier], { ["name"] = seed.name, ["level"] = recipe.level }) | |||
table.insert(produceTable[tier], { ["name"] = product.name, ["level"] = recipe.level }) | |||
end | |||
-- Generate output table | |||
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!style="background-color:#275C87;color:#FFFFFF;padding-left:64px;" colspan="2"|' .. Icons.Icon({'Farming', type='skill'})) | |||
local getItemList = function(itemTable) | |||
local listPart = {} | |||
for i, item in ipairs(itemTable) do | |||
table.insert(listPart, Icons.Icon({item.name, type='item'})) | |||
end | |||
return table.concat(listPart, ' • ') | |||
end | |||
local sortFunc = function(a, b) return (a.level == b.level and a.name < b.name) or a.level < b.level end | |||
-- Generate table section for each tier | |||
for i, category in ipairs(SkillData.Farming.categories) do | |||
local tier = category.id | |||
-- Sort tables by Farming level order | |||
table.sort(seedsTable[tier], sortFunc) | |||
table.sort(produceTable[tier], sortFunc) | |||
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| ' .. category.name) | |||
table.insert(resultPart, '\r\n|-\r\n!scope="row"| Seeds') | |||
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. getItemList(seedsTable[tier])) | |||
table.insert(resultPart, '\r\n|-\r\n!scope="row"| Produce') | |||
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. getItemList(produceTable[tier])) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getFoodNavbox(frame) | |||
local foundIDs, cookedFood, harvestedFood, otherFood = {}, {}, {}, {} | |||
-- Exclude unobtainable lemonade | |||
foundIDs['melvorTotH:Lemonade_Full'] = true | |||
-- Cooked food | |||
for i, recipe in ipairs(SkillData.Cooking.recipes) do | |||
if not foundIDs[recipe.productID] then | |||
foundIDs[recipe.productID] = true | |||
if recipe.perfectCookID ~= nil then | |||
foundIDs[recipe.perfectCookID] = true | |||
end | |||
if recipe.noMastery == nil then | |||
local cookedItem = Items.getItemByID(recipe.productID) | |||
if cookedItem ~= nil then | |||
local catIdx = recipe.categoryID | |||
-- Initialize category if it doesn't already exist | |||
if cookedFood[catIdx] == nil then | |||
cookedFood[catIdx] = {} | |||
end | |||
local perfectName = nil | |||
if recipe.perfectCookID ~= nil then | |||
local perfectItem = Items.getItemByID(recipe.perfectCookID) | |||
if perfectItem ~= nil then | |||
perfectName = perfectItem.name | |||
end | |||
end | |||
table.insert(cookedFood[catIdx], { ["name"] = cookedItem.name, ["order"] = recipe.level, ["perfectName"] = perfectName }) | |||
end | |||
end | |||
end | |||
end | |||
-- Harvested foods | |||
for i, recipe in ipairs(SkillData.Farming.recipes) do | |||
if not foundIDs[recipe.productID] then | |||
local product = Items.getItemByID(recipe.productId) | |||
if product.healsFor ~= nil then | |||
table.insert(harvestedFood, { ["name"] = product.name, ["order"] = recipe.level }) | |||
foundIDs[product.id] = true | |||
end | |||
end | |||
end | |||
-- Other foods, must be checked after cooked & harvested foods have been identified | |||
for i, item in ipairs(GameData.rawData.items) do | |||
if item.healsFor ~= nil and not foundIDs[item.id] then | |||
-- Item can be eaten but isn't cooked nor harvested | |||
table.insert(otherFood, { ["name"] = item.name, ["order"] = item.id }) | |||
foundIDs[item.id] = true | |||
end | |||
end | |||
-- Sort food lists | |||
local sortFunc = function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end | |||
for i, items in pairs(cookedFood) do | |||
table.sort(cookedFood[i], sortFunc) | |||
end | |||
table.sort(harvestedFood, sortFunc) | |||
table.sort(otherFood, sortFunc) | |||
-- Generate food lists for final output | |||
local cookingCatHeader = { | |||
{ id = 'melvorD:Fire', header = Icons.Icon({'Normal Cooking Fire', 'Cooking Fire', type='upgrade', nolink=true}) }, | |||
{ id = 'melvorD:Furnace', header = Icons.Icon({'Basic Furnace', 'Furnace', type='upgrade', nolink=true}) }, | |||
{ id = 'melvorD:Pot', header = Icons.Icon({'Basic Pot', 'Pot', type='upgrade', nolink=true}) } | |||
} | |||
local getFoodList = function(foodTable) | |||
local listPart = {} | |||
if type(foodTable) == 'table' then | |||
for i, food in ipairs(foodTable) do | |||
local foodText = Icons.Icon({food.name, type='item'}) | |||
if food.perfectName ~= nil then | |||
foodText = Icons.Icon({food.perfectName, type='item', notext=true}) .. ' ' .. foodText | |||
end | |||
table.insert(listPart, foodText) | |||
end | |||
end | |||
return table.concat(listPart, ' • ') | |||
end | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:0 auto 10px; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n|-\r\n!style="background-color:#275C87;color:#FFFFFF;padding-left:64px;" colspan="2"| ') | |||
table.insert(resultPart, Icons.Icon({'Food', type='item', img='Crab'})) | |||
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| Cooked') | |||
for i, cat in ipairs(cookingCatHeader) do | |||
table.insert(resultPart, '\r\n|-\r\n!scope="row"| ' .. cat.header) | |||
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. getFoodList(cookedFood[cat.id])) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| Harvested') | |||
table.insert(resultPart, '\r\n|-\r\n|colspan="2" style="text-align:center;"| ' .. getFoodList(harvestedFood)) | |||
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| Other') | |||
table.insert(resultPart, '\r\n|-\r\n|colspan="2" style="text-align:center;"| ' .. getFoodList(otherFood)) | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getPotionNavbox(frame) | |||
local catList = { | |||
{ ["categoryID"] = 0, ["name"] = 'Combat' }, | |||
{ ["categoryID"] = 1, ["name"] = 'Skill' } | |||
} | |||
table.sort(catList, function(a, b) return a.name < b.name end) | |||
-- Compile list of potions to be included | |||
local potList = {} | |||
for i, potData in ipairs(SkillData.Herblore.recipes) do | |||
if potList[potData.categoryID] == nil then | |||
potList[potData.categoryID] = {} | |||
end | |||
local potFirstItem = Items.getItemByID(potData.potionIDs[1]) | |||
local potName = string.gsub(potFirstItem.name, ' Potion [IV]+$', '') | |||
table.insert(potList[potData.categoryID], { ["name"] = potName, ["order"] = potData.level, ["img"] = potFirstItem.name }) | |||
end | |||
local resultPart = {} | |||
-- Generate table header | |||
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!colspan=2|' .. Icons.Icon({ 'Herblore', 'Potions', type='skill' })) | |||
-- Generate section for each category of potions | |||
for i, catData in ipairs(SkillData.Herblore.categories) do | |||
-- Compile list of potions | |||
local potListText = {} | |||
table.sort(potList[catData.id], function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end) | |||
for j, potData in ipairs(potList[catData.id]) do | |||
table.insert(potListText, Icons.Icon({ potData.name .. ' Potion', potData.name, img=potData.img, type='item' })) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n! ' .. catData.name) | |||
table.insert(resultPart, '\r\n|class="center" style="vertical-align:middle;"| ' .. table.concat(potListText, ' • ')) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getPrayerNavbox(frame) | |||
local prayerList = { | |||
["Prayers"] = Prayer.getPrayers(function(prayer) return prayer.isUnholy == nil and prayer.isAbyssal == nil end), | |||
["Unholy Prayers"] = Prayer.getPrayers(function(prayer) return prayer.isUnholy end), | |||
["Abyssal Prayers"] = Prayer.getPrayers(function(prayer) return prayer.isAbyssal end) | |||
} | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!colspan=2|' .. Icons.Icon({'Prayer', 'Prayers', type='skill'})) | |||
for catName, subList in pairs(prayerList) do | |||
table.sort(subList, function(a, b) | |||
if a.level == b.level then | |||
return a.name < b.name | |||
else | |||
return a.level < b.level | |||
end | |||
end) | |||
local prayerText = {} | |||
-- get expansion icon for prayer categories other than base/TotH | |||
expicon = '' | |||
if catName ~= "Prayers" then | |||
for i, prayer in ipairs(subList) do | |||
expicon = Icons.getExpansionIcon(prayer.id) | |||
break | |||
end | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:center;"| ' .. expicon .. catName) | |||
for i, prayer in ipairs(subList) do | |||
-- Only show expansion in the base/TotH subtable | |||
expicon = (catName == "Prayers") and Icons.getExpansionIcon(prayer.id) | |||
table.insert(prayerText, Icons.Icon({prayer.name, type='prayer', expicon = expicon})) | |||
end | |||
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. table.concat(prayerText, ' • ')) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getRuneNavbox(frame) | |||
-- Assumes all runes are from Runecrafting, which may need revising in future updates | |||
local categoryIDs = { | |||
'melvorF:StandardRunes', | |||
'melvorF:CombinationRunes', | |||
'melvorItA:AbyssalRunes', | |||
'melvorItA:AbyssalComboRunes' | |||
} | |||
local runeList = {} | |||
for i, recipe in ipairs(SkillData.Runecrafting.recipes) do | |||
local catID = recipe.categoryID | |||
if Shared.contains(categoryIDs, catID) then | |||
if runeList[catID] == nil then | |||
runeList[catID] = {} | |||
end | |||
local product = Items.getItemByID(recipe.productID) | |||
if product ~= nil then | |||
table.insert(runeList[catID], { ["name"] = product.name, ["order"] = recipe.level }) | |||
end | |||
end | |||
end | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!colspan="2"|' .. Icons.Icon({'Runes', type='item', img='Air Rune'})) | |||
for i, catID in ipairs(categoryIDs) do | |||
local category = GameData.getEntityByID(SkillData.Runecrafting.categories, catID) | |||
if category ~= nil then | |||
table.sort(runeList[catID], function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end) | |||
table.insert(resultPart, '\r\n|-\r\n!scope="row"|' .. category.name) | |||
local listPart = {} | |||
for j, rune in ipairs(runeList[catID]) do | |||
table.insert(listPart, Icons.Icon({rune.name, type='item'})) | |||
end | |||
table.insert(resultPart, '\r\n|style="text-align:center;"|'..table.concat(listPart, ' • ')) | |||
end | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getSkillcapeNavbox(frame) | function p.getSkillcapeNavbox(frame) | ||
local capeList = { | |||
["Normal"] = Shop.getPurchases(function(purch) return Shop.isSkillcapePurchase(purch, false, nil) end), | |||
["Superior"] = Shop.getPurchases(function(purch) return Shop.isSkillcapePurchase(purch, true, nil) end) | |||
} | |||
local capeNames = {} | |||
for catName, subList in pairs(capeList) do | |||
for i, cape in ipairs(subList) do | |||
capeNames[cape.id] = Shop._getPurchaseName(cape) | |||
end | |||
table.sort(capeList[catName], function(a, b) | |||
local costA, costB = Shop._getPurchaseSortValue(a), Shop._getPurchaseSortValue(b) | |||
if costA == costB then | |||
return capeNames[a.id] < capeNames[b.id] | |||
else | |||
return costA < costB | |||
end | |||
end) | |||
end | |||
local capeText = {} | |||
for i, purch in ipairs(capeList) do | |||
table.insert(capeText, Icons.Icon({capeNames[purch.id], type='item'})) | |||
end | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!colspan="2"| ' .. Icons.Icon({'Skillcapes', type='item', img='Cape of Completion'})) | |||
for catName, subList in pairs(capeList) do | |||
local capeText = {} | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:center;"| ' .. catName) | |||
for i, cape in ipairs(subList) do | |||
table.insert(capeText, Icons.Icon({capeNames[cape.id], type='item'})) | |||
end | |||
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. table.concat(capeText, ' • ')) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getSpellNavbox(frame) | |||
local spellTable = {} | |||
for i, spellBook in ipairs(Magic.spellBooks) do | |||
spellTable[spellBook.id] = {} | |||
local spells = Magic.getSpellsBySpellBook(spellBook.id) | |||
for j, spell in ipairs(spells) do | |||
table.insert(spellTable[spellBook.id], { ["name"] = spell.name, ["order"] = spell.level }) | |||
end | |||
end | |||
local getSpellList = function(spellTable, imgType) | |||
local listPart = {} | |||
for i, obj in ipairs(spellTable) do | |||
table.insert(listPart, Icons.Icon({obj.name, type=imgType})) | |||
end | |||
return table.concat(listPart, ' • ') | |||
end | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!colspan=2|' .. Icons.Icon({'Spells', type='skill', img='Magic'})) | |||
for i, spellBook in ipairs(Magic.spellBooks) do | |||
table.sort(spellTable[spellBook.id], function(a, b) | |||
if a.order == b.order then | |||
return a.name < b.name | |||
else | |||
return a.order < b.order | |||
end | |||
end) | |||
table.insert(resultPart, '\r\n|-\r\n!scope="row"| ' .. Magic.getSpellTypeLink(spellBook.id)) | |||
table.insert(resultPart, '\r\n|style="text-align:center;| ' .. getSpellList(spellTable[spellBook.id], spellBook.imgType)) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getFishingNavbox(frame) | |||
local categoryHeader = {} | |||
local categoryItems = {} | |||
local addCatData = function(cat, catLink, itemName, itemOrder) | |||
if categoryItems[cat] == nil then | |||
-- Initialize category | |||
table.insert(categoryHeader, { ["name"] = cat, ["link"] = catLink }) | |||
categoryItems[cat] = {} | |||
end | |||
table.insert(categoryItems[cat], { ["name"] = itemName, ["order"] = itemOrder }) | |||
end | |||
-- Fishing areas | |||
-- Iterate through all fishing areas, identifying fish within each | |||
for i, area in ipairs(SkillData.Fishing.areas) do | |||
for j, fishID in ipairs(area.fishIDs) do | |||
local fishItem = Items.getItemByID(fishID) | |||
local recipe = GameData.getEntityByID(SkillData.Fishing.fish, fishID) | |||
if fishItem ~= nil and recipe ~= nil then | |||
addCatData(area.name, 'Fishing#Fishing Areas', fishItem.name, recipe.level) | |||
end | |||
end | |||
end | |||
-- Junk items | |||
for i, itemID in ipairs(SkillData.Fishing.junkItemIDs) do | |||
local item = Items.getItemByID(itemID) | |||
if item ~= nil then | |||
addCatData('Junk', 'Fishing#Junk', item.name, 1) | |||
end | |||
end | |||
-- Special items | |||
for i, itemDef in ipairs(SkillData.Fishing.specialItems) do | |||
local item = Items.getItemByID(itemDef.itemID) | |||
if item ~= nil then | |||
local weight = itemDef.weight or 1 | |||
addCatData('Special Items', 'Fishing#Special', item.name, 1 / weight) | |||
end | |||
end | |||
local resultPart = {} | |||
-- Generate navbox header | |||
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n|-\r\n!colspan="2" | ' .. Icons.Icon({'Fishing', type='skill'})) | |||
-- Generate section for each fishing area, junk, and special | |||
for i, cat in ipairs(categoryHeader) do | |||
local itemList = {} | |||
if categoryItems[cat.name] ~= nil then | |||
table.sort(categoryItems[cat.name], function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end) | |||
for j, item in ipairs(categoryItems[cat.name]) do | |||
table.insert(itemList, Icons.Icon({item.name, type='item'})) | |||
end | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!class="center" style="min-width:140px" | [[' .. (cat.link or cat.name) .. '|' .. cat.name .. ']]') | |||
table.insert(resultPart, '\r\n| class="center" style="vertical-align:middle;" | ' .. table.concat(itemList, ' • ')) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getOreBarNavbox(frame) | |||
local categoryList = { 'Ores', 'Bars' } | |||
local categoryItems = { ["Ores"] = {}, ["Bars"] = {} } | |||
local barOreIDs = {} | |||
-- Compile list of bars | |||
for i, recipe in ipairs(SkillData.Smithing.recipes) do | |||
if Shared.contains({'melvorD:Bars','melvorItA:AbyssalBars'}, recipe.categoryID) then | |||
local item = Items.getItemByID(recipe.productID) | |||
if item ~= nil then | |||
local displayName = string.gsub(item.name, ' Bar$', '') | |||
local level = recipe.level | |||
if recipe.abyssalLevel then | |||
level = recipe.abyssalLevel + 120 | |||
end | |||
table.insert(categoryItems['Bars'], { ["name"] = item.name, ["display"] = displayName, ["order"] = level }) | |||
end | |||
end | |||
end | |||
-- Compile list of ores | |||
for i, recipe in ipairs(SkillData.Mining.rockData) do | |||
local item = Items.getItemByID(recipe.productId) | |||
if item ~= nil then | |||
local level = recipe.level | |||
if recipe.abyssalLevel then | |||
level = recipe.abyssalLevel + 120 | |||
end | |||
table.insert(categoryItems['Ores'], { ["name"] = item.name, ["display"] = recipe.name, ["order"] = level }) | |||
end | |||
end | |||
local resultPart = {} | |||
-- Generate navbox header | |||
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n|-\r\n!style="background-color:#275C87;color:#FFFFFF;padding-left:64px;" colspan="2" | ' .. '<b>Ores & Bars</b>') | |||
-- Generate navbox content | |||
for i, cat in ipairs(categoryList) do | |||
table.sort(categoryItems[cat], function(a, b) return (a.order == b.order and a.display < b.display) or a.order < b.order end) | |||
local listPart = {} | |||
for j, listItem in ipairs(categoryItems[cat]) do | |||
table.insert(listPart, Icons.Icon({ listItem.name, listItem.display, type = 'item' })) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n! ' .. cat .. '\r\n|style="text-align:center;"| ' .. table.concat(listPart, ' • ')) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getPetNavbox(frame) | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; text-align:center; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n|-\r\n!colspan="2"|[[Pets]]') | |||
local petListOrder = { 'skill', 'boss', 'other' } | |||
local petList = { | |||
["skill"] = {}, | |||
["boss"] = {}, | |||
["other"] = {} | |||
} | |||
for i, petData in ipairs(GameData.rawData.pets) do | |||
local source = Pets._getPetSource(petData) | |||
local listCat = 'other' | |||
if type(source) == 'table' and source.type ~= nil then | |||
if source.type == 'skill' then | |||
listCat = 'skill' | |||
elseif Shared.contains({'dungeon', 'abyssDepth', 'stronghold'}, source.type) then | |||
listCat = 'boss' | |||
else | |||
listCat = 'other' | |||
end | |||
end | |||
table.insert(petList[listCat], petData.name) | |||
end | |||
local getIconList = | |||
function(pets) | |||
local result = {} | |||
for i, pet in ipairs(pets) do | |||
table.insert(result, Icons.Icon({pet, type='pet'})) | |||
end | |||
return table.concat(result, ' • ') | |||
end | |||
for i, catName in ipairs(petListOrder) do | |||
local catData = petList[catName] | |||
table.sort(catData, function(a, b) return a < b end) | |||
table.insert(resultPart, '\r\n|-\r\n!' .. Shared.titleCase(catName) .. ' Pets\r\n|' .. getIconList(catData)) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | end | ||
return p | return p |
Latest revision as of 19:37, 5 August 2024
Documentation for this module may be created at Module:Navboxes/doc
-- New module to stop navbox generators cluttering other modules
local p = {}
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Magic = require('Module:Magic')
local Shop = require('Module:Shop')
local Pets = require('Module:Pets')
local Prayer = require('Module:Prayer')
-- Generates a table with a single category which has an Item and it's Icon
-- array is the list of items that will be added to the table
-- iconType is the item's icon type
-- headerData is the text displayed at the top of the generated table
-- productID is the proper capitalization of the productID property -_-
function p.buildSingleNavboxTable(array, iconType, headerData, productID)
-- Generate Table contents
table.sort(array, function(a, b) return a.level < b.level end)
local iconArray = {}
for i, item in ipairs(array) do
if productID ~= nil then
item = Items.getItemByID(item[productID])
end
table.insert(iconArray, Icons.Icon({item.name, type=iconType, expicon=Icons.getExpansionIcon(item.id)}))
end
-- Generate navbox table
local resultTable = mw.html.create('table')
-- Table classes & styles
resultTable
:addClass('wikitable')
:addClass('navigation-not-searchable')
:css('text-align', 'center')
:css('clear', 'both')
:css('width', '100%')
-- Header row
:tag('tr')
:tag('th')
:css('background-color', '#275C87')
:css('color', '#FFFFFF')
:wikitext(Icons.Icon(headerData))
:done()
:done()
-- Content, list of logs
:tag('tr')
:tag('td')
:wikitext(table.concat(iconArray, ' • '))
:done()
:done()
:done()
return tostring(resultTable)
end
function p.getLogNavbox(frame)
local trees = Shared.shallowClone(SkillData.Woodcutting.trees)
return p.buildSingleNavboxTable(trees, 'item', {'Woodcutting', 'Logs', type='skill', section='Logs'}, 'productId')
end
function p.getFamiliarNavbox(frame)
local familiars = Shared.shallowClone(SkillData.Summoning.recipes)
return p.buildSingleNavboxTable(familiars, 'item', {'Summoning', 'Summoning Familiars', type='skill', section='Summoning_Tablets'}, 'productID')
end
function p.getThievingNavbox(frame)
local npcs = Shared.shallowClone(SkillData.Thieving.npcs)
return p.buildSingleNavboxTable(npcs, 'thieving', {'Thieving', 'Thieving Targets', type='skill', section='Thieving_Targets'})
end
function p.getFarmingNavbox(frame)
local resultPart = {}
local seedsTable = {}
local produceTable = {}
for i, recipe in ipairs(SkillData.Farming.recipes) do
local seed = Items.getItemByID(recipe.seedCost.id)
local product = Items.getItemByID(recipe.productId)
local tier = recipe.categoryID
if seedsTable[tier] == nil then
-- Initialize tier tables
seedsTable[tier] = {}
produceTable[tier] = {}
end
table.insert(seedsTable[tier], { ["name"] = seed.name, ["level"] = recipe.level })
table.insert(produceTable[tier], { ["name"] = product.name, ["level"] = recipe.level })
end
-- Generate output table
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!style="background-color:#275C87;color:#FFFFFF;padding-left:64px;" colspan="2"|' .. Icons.Icon({'Farming', type='skill'}))
local getItemList = function(itemTable)
local listPart = {}
for i, item in ipairs(itemTable) do
table.insert(listPart, Icons.Icon({item.name, type='item'}))
end
return table.concat(listPart, ' • ')
end
local sortFunc = function(a, b) return (a.level == b.level and a.name < b.name) or a.level < b.level end
-- Generate table section for each tier
for i, category in ipairs(SkillData.Farming.categories) do
local tier = category.id
-- Sort tables by Farming level order
table.sort(seedsTable[tier], sortFunc)
table.sort(produceTable[tier], sortFunc)
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| ' .. category.name)
table.insert(resultPart, '\r\n|-\r\n!scope="row"| Seeds')
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. getItemList(seedsTable[tier]))
table.insert(resultPart, '\r\n|-\r\n!scope="row"| Produce')
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. getItemList(produceTable[tier]))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getFoodNavbox(frame)
local foundIDs, cookedFood, harvestedFood, otherFood = {}, {}, {}, {}
-- Exclude unobtainable lemonade
foundIDs['melvorTotH:Lemonade_Full'] = true
-- Cooked food
for i, recipe in ipairs(SkillData.Cooking.recipes) do
if not foundIDs[recipe.productID] then
foundIDs[recipe.productID] = true
if recipe.perfectCookID ~= nil then
foundIDs[recipe.perfectCookID] = true
end
if recipe.noMastery == nil then
local cookedItem = Items.getItemByID(recipe.productID)
if cookedItem ~= nil then
local catIdx = recipe.categoryID
-- Initialize category if it doesn't already exist
if cookedFood[catIdx] == nil then
cookedFood[catIdx] = {}
end
local perfectName = nil
if recipe.perfectCookID ~= nil then
local perfectItem = Items.getItemByID(recipe.perfectCookID)
if perfectItem ~= nil then
perfectName = perfectItem.name
end
end
table.insert(cookedFood[catIdx], { ["name"] = cookedItem.name, ["order"] = recipe.level, ["perfectName"] = perfectName })
end
end
end
end
-- Harvested foods
for i, recipe in ipairs(SkillData.Farming.recipes) do
if not foundIDs[recipe.productID] then
local product = Items.getItemByID(recipe.productId)
if product.healsFor ~= nil then
table.insert(harvestedFood, { ["name"] = product.name, ["order"] = recipe.level })
foundIDs[product.id] = true
end
end
end
-- Other foods, must be checked after cooked & harvested foods have been identified
for i, item in ipairs(GameData.rawData.items) do
if item.healsFor ~= nil and not foundIDs[item.id] then
-- Item can be eaten but isn't cooked nor harvested
table.insert(otherFood, { ["name"] = item.name, ["order"] = item.id })
foundIDs[item.id] = true
end
end
-- Sort food lists
local sortFunc = function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end
for i, items in pairs(cookedFood) do
table.sort(cookedFood[i], sortFunc)
end
table.sort(harvestedFood, sortFunc)
table.sort(otherFood, sortFunc)
-- Generate food lists for final output
local cookingCatHeader = {
{ id = 'melvorD:Fire', header = Icons.Icon({'Normal Cooking Fire', 'Cooking Fire', type='upgrade', nolink=true}) },
{ id = 'melvorD:Furnace', header = Icons.Icon({'Basic Furnace', 'Furnace', type='upgrade', nolink=true}) },
{ id = 'melvorD:Pot', header = Icons.Icon({'Basic Pot', 'Pot', type='upgrade', nolink=true}) }
}
local getFoodList = function(foodTable)
local listPart = {}
if type(foodTable) == 'table' then
for i, food in ipairs(foodTable) do
local foodText = Icons.Icon({food.name, type='item'})
if food.perfectName ~= nil then
foodText = Icons.Icon({food.perfectName, type='item', notext=true}) .. ' ' .. foodText
end
table.insert(listPart, foodText)
end
end
return table.concat(listPart, ' • ')
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:0 auto 10px; clear:both; width: 100%"')
table.insert(resultPart, '\r\n|-\r\n!style="background-color:#275C87;color:#FFFFFF;padding-left:64px;" colspan="2"| ')
table.insert(resultPart, Icons.Icon({'Food', type='item', img='Crab'}))
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| Cooked')
for i, cat in ipairs(cookingCatHeader) do
table.insert(resultPart, '\r\n|-\r\n!scope="row"| ' .. cat.header)
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. getFoodList(cookedFood[cat.id]))
end
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| Harvested')
table.insert(resultPart, '\r\n|-\r\n|colspan="2" style="text-align:center;"| ' .. getFoodList(harvestedFood))
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| Other')
table.insert(resultPart, '\r\n|-\r\n|colspan="2" style="text-align:center;"| ' .. getFoodList(otherFood))
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getPotionNavbox(frame)
local catList = {
{ ["categoryID"] = 0, ["name"] = 'Combat' },
{ ["categoryID"] = 1, ["name"] = 'Skill' }
}
table.sort(catList, function(a, b) return a.name < b.name end)
-- Compile list of potions to be included
local potList = {}
for i, potData in ipairs(SkillData.Herblore.recipes) do
if potList[potData.categoryID] == nil then
potList[potData.categoryID] = {}
end
local potFirstItem = Items.getItemByID(potData.potionIDs[1])
local potName = string.gsub(potFirstItem.name, ' Potion [IV]+$', '')
table.insert(potList[potData.categoryID], { ["name"] = potName, ["order"] = potData.level, ["img"] = potFirstItem.name })
end
local resultPart = {}
-- Generate table header
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan=2|' .. Icons.Icon({ 'Herblore', 'Potions', type='skill' }))
-- Generate section for each category of potions
for i, catData in ipairs(SkillData.Herblore.categories) do
-- Compile list of potions
local potListText = {}
table.sort(potList[catData.id], function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end)
for j, potData in ipairs(potList[catData.id]) do
table.insert(potListText, Icons.Icon({ potData.name .. ' Potion', potData.name, img=potData.img, type='item' }))
end
table.insert(resultPart, '\r\n|-\r\n! ' .. catData.name)
table.insert(resultPart, '\r\n|class="center" style="vertical-align:middle;"| ' .. table.concat(potListText, ' • '))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getPrayerNavbox(frame)
local prayerList = {
["Prayers"] = Prayer.getPrayers(function(prayer) return prayer.isUnholy == nil and prayer.isAbyssal == nil end),
["Unholy Prayers"] = Prayer.getPrayers(function(prayer) return prayer.isUnholy end),
["Abyssal Prayers"] = Prayer.getPrayers(function(prayer) return prayer.isAbyssal end)
}
local resultPart = {}
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan=2|' .. Icons.Icon({'Prayer', 'Prayers', type='skill'}))
for catName, subList in pairs(prayerList) do
table.sort(subList, function(a, b)
if a.level == b.level then
return a.name < b.name
else
return a.level < b.level
end
end)
local prayerText = {}
-- get expansion icon for prayer categories other than base/TotH
expicon = ''
if catName ~= "Prayers" then
for i, prayer in ipairs(subList) do
expicon = Icons.getExpansionIcon(prayer.id)
break
end
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:center;"| ' .. expicon .. catName)
for i, prayer in ipairs(subList) do
-- Only show expansion in the base/TotH subtable
expicon = (catName == "Prayers") and Icons.getExpansionIcon(prayer.id)
table.insert(prayerText, Icons.Icon({prayer.name, type='prayer', expicon = expicon}))
end
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. table.concat(prayerText, ' • '))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getRuneNavbox(frame)
-- Assumes all runes are from Runecrafting, which may need revising in future updates
local categoryIDs = {
'melvorF:StandardRunes',
'melvorF:CombinationRunes',
'melvorItA:AbyssalRunes',
'melvorItA:AbyssalComboRunes'
}
local runeList = {}
for i, recipe in ipairs(SkillData.Runecrafting.recipes) do
local catID = recipe.categoryID
if Shared.contains(categoryIDs, catID) then
if runeList[catID] == nil then
runeList[catID] = {}
end
local product = Items.getItemByID(recipe.productID)
if product ~= nil then
table.insert(runeList[catID], { ["name"] = product.name, ["order"] = recipe.level })
end
end
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan="2"|' .. Icons.Icon({'Runes', type='item', img='Air Rune'}))
for i, catID in ipairs(categoryIDs) do
local category = GameData.getEntityByID(SkillData.Runecrafting.categories, catID)
if category ~= nil then
table.sort(runeList[catID], function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end)
table.insert(resultPart, '\r\n|-\r\n!scope="row"|' .. category.name)
local listPart = {}
for j, rune in ipairs(runeList[catID]) do
table.insert(listPart, Icons.Icon({rune.name, type='item'}))
end
table.insert(resultPart, '\r\n|style="text-align:center;"|'..table.concat(listPart, ' • '))
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getSkillcapeNavbox(frame)
local capeList = {
["Normal"] = Shop.getPurchases(function(purch) return Shop.isSkillcapePurchase(purch, false, nil) end),
["Superior"] = Shop.getPurchases(function(purch) return Shop.isSkillcapePurchase(purch, true, nil) end)
}
local capeNames = {}
for catName, subList in pairs(capeList) do
for i, cape in ipairs(subList) do
capeNames[cape.id] = Shop._getPurchaseName(cape)
end
table.sort(capeList[catName], function(a, b)
local costA, costB = Shop._getPurchaseSortValue(a), Shop._getPurchaseSortValue(b)
if costA == costB then
return capeNames[a.id] < capeNames[b.id]
else
return costA < costB
end
end)
end
local capeText = {}
for i, purch in ipairs(capeList) do
table.insert(capeText, Icons.Icon({capeNames[purch.id], type='item'}))
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan="2"| ' .. Icons.Icon({'Skillcapes', type='item', img='Cape of Completion'}))
for catName, subList in pairs(capeList) do
local capeText = {}
table.insert(resultPart, '\r\n|-\r\n!style="text-align:center;"| ' .. catName)
for i, cape in ipairs(subList) do
table.insert(capeText, Icons.Icon({capeNames[cape.id], type='item'}))
end
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. table.concat(capeText, ' • '))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getSpellNavbox(frame)
local spellTable = {}
for i, spellBook in ipairs(Magic.spellBooks) do
spellTable[spellBook.id] = {}
local spells = Magic.getSpellsBySpellBook(spellBook.id)
for j, spell in ipairs(spells) do
table.insert(spellTable[spellBook.id], { ["name"] = spell.name, ["order"] = spell.level })
end
end
local getSpellList = function(spellTable, imgType)
local listPart = {}
for i, obj in ipairs(spellTable) do
table.insert(listPart, Icons.Icon({obj.name, type=imgType}))
end
return table.concat(listPart, ' • ')
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan=2|' .. Icons.Icon({'Spells', type='skill', img='Magic'}))
for i, spellBook in ipairs(Magic.spellBooks) do
table.sort(spellTable[spellBook.id], function(a, b)
if a.order == b.order then
return a.name < b.name
else
return a.order < b.order
end
end)
table.insert(resultPart, '\r\n|-\r\n!scope="row"| ' .. Magic.getSpellTypeLink(spellBook.id))
table.insert(resultPart, '\r\n|style="text-align:center;| ' .. getSpellList(spellTable[spellBook.id], spellBook.imgType))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getFishingNavbox(frame)
local categoryHeader = {}
local categoryItems = {}
local addCatData = function(cat, catLink, itemName, itemOrder)
if categoryItems[cat] == nil then
-- Initialize category
table.insert(categoryHeader, { ["name"] = cat, ["link"] = catLink })
categoryItems[cat] = {}
end
table.insert(categoryItems[cat], { ["name"] = itemName, ["order"] = itemOrder })
end
-- Fishing areas
-- Iterate through all fishing areas, identifying fish within each
for i, area in ipairs(SkillData.Fishing.areas) do
for j, fishID in ipairs(area.fishIDs) do
local fishItem = Items.getItemByID(fishID)
local recipe = GameData.getEntityByID(SkillData.Fishing.fish, fishID)
if fishItem ~= nil and recipe ~= nil then
addCatData(area.name, 'Fishing#Fishing Areas', fishItem.name, recipe.level)
end
end
end
-- Junk items
for i, itemID in ipairs(SkillData.Fishing.junkItemIDs) do
local item = Items.getItemByID(itemID)
if item ~= nil then
addCatData('Junk', 'Fishing#Junk', item.name, 1)
end
end
-- Special items
for i, itemDef in ipairs(SkillData.Fishing.specialItems) do
local item = Items.getItemByID(itemDef.itemID)
if item ~= nil then
local weight = itemDef.weight or 1
addCatData('Special Items', 'Fishing#Special', item.name, 1 / weight)
end
end
local resultPart = {}
-- Generate navbox header
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n|-\r\n!colspan="2" | ' .. Icons.Icon({'Fishing', type='skill'}))
-- Generate section for each fishing area, junk, and special
for i, cat in ipairs(categoryHeader) do
local itemList = {}
if categoryItems[cat.name] ~= nil then
table.sort(categoryItems[cat.name], function(a, b) return (a.order == b.order and a.name < b.name) or a.order < b.order end)
for j, item in ipairs(categoryItems[cat.name]) do
table.insert(itemList, Icons.Icon({item.name, type='item'}))
end
end
table.insert(resultPart, '\r\n|-\r\n!class="center" style="min-width:140px" | [[' .. (cat.link or cat.name) .. '|' .. cat.name .. ']]')
table.insert(resultPart, '\r\n| class="center" style="vertical-align:middle;" | ' .. table.concat(itemList, ' • '))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getOreBarNavbox(frame)
local categoryList = { 'Ores', 'Bars' }
local categoryItems = { ["Ores"] = {}, ["Bars"] = {} }
local barOreIDs = {}
-- Compile list of bars
for i, recipe in ipairs(SkillData.Smithing.recipes) do
if Shared.contains({'melvorD:Bars','melvorItA:AbyssalBars'}, recipe.categoryID) then
local item = Items.getItemByID(recipe.productID)
if item ~= nil then
local displayName = string.gsub(item.name, ' Bar$', '')
local level = recipe.level
if recipe.abyssalLevel then
level = recipe.abyssalLevel + 120
end
table.insert(categoryItems['Bars'], { ["name"] = item.name, ["display"] = displayName, ["order"] = level })
end
end
end
-- Compile list of ores
for i, recipe in ipairs(SkillData.Mining.rockData) do
local item = Items.getItemByID(recipe.productId)
if item ~= nil then
local level = recipe.level
if recipe.abyssalLevel then
level = recipe.abyssalLevel + 120
end
table.insert(categoryItems['Ores'], { ["name"] = item.name, ["display"] = recipe.name, ["order"] = level })
end
end
local resultPart = {}
-- Generate navbox header
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n|-\r\n!style="background-color:#275C87;color:#FFFFFF;padding-left:64px;" colspan="2" | ' .. '<b>Ores & Bars</b>')
-- Generate navbox content
for i, cat in ipairs(categoryList) do
table.sort(categoryItems[cat], function(a, b) return (a.order == b.order and a.display < b.display) or a.order < b.order end)
local listPart = {}
for j, listItem in ipairs(categoryItems[cat]) do
table.insert(listPart, Icons.Icon({ listItem.name, listItem.display, type = 'item' }))
end
table.insert(resultPart, '\r\n|-\r\n! ' .. cat .. '\r\n|style="text-align:center;"| ' .. table.concat(listPart, ' • '))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getPetNavbox(frame)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable navigation-not-searchable" style="margin:auto; text-align:center; clear:both; width: 100%"')
table.insert(resultPart, '\r\n|-\r\n!colspan="2"|[[Pets]]')
local petListOrder = { 'skill', 'boss', 'other' }
local petList = {
["skill"] = {},
["boss"] = {},
["other"] = {}
}
for i, petData in ipairs(GameData.rawData.pets) do
local source = Pets._getPetSource(petData)
local listCat = 'other'
if type(source) == 'table' and source.type ~= nil then
if source.type == 'skill' then
listCat = 'skill'
elseif Shared.contains({'dungeon', 'abyssDepth', 'stronghold'}, source.type) then
listCat = 'boss'
else
listCat = 'other'
end
end
table.insert(petList[listCat], petData.name)
end
local getIconList =
function(pets)
local result = {}
for i, pet in ipairs(pets) do
table.insert(result, Icons.Icon({pet, type='pet'}))
end
return table.concat(result, ' • ')
end
for i, catName in ipairs(petListOrder) do
local catData = petList[catName]
table.sort(catData, function(a, b) return a < b end)
table.insert(resultPart, '\r\n|-\r\n!' .. Shared.titleCase(catName) .. ' Pets\r\n|' .. getIconList(catData))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
return p