Module:Navboxes: Difference between revisions
From Melvor Idle
(getRuneNavbox: Order results by level & improve resilience against future rune additions; getSkillcapeNavbox: Include missing max cape & consistency with shop table; getFishingNavbox: Implement to replace manually maintained table) |
(getPrayerNavbox/getSpellNavbox: Order prayers/spells by level, in line with in-game appearance) |
||
Line 188: | Line 188: | ||
function p.getPrayerNavbox(frame) | function p.getPrayerNavbox(frame) | ||
local | local prayerList = {} | ||
for i, prayer in Shared.skpairs(SkillData.Prayer) do | for i, prayer in Shared.skpairs(SkillData.Prayer) do | ||
table.insert(prayerList, { ["name"] = prayer.name, ["order"] = prayer.prayerLevel }) | |||
end | end | ||
local | table.sort(prayerList, function(a, b) | ||
if a.order == b.order then | |||
return a.name < b.name | |||
else | |||
return | return a.order < b.order | ||
end | |||
end) | |||
local prayerListText = {} | |||
for i, prayer in ipairs(prayerList) do | |||
table.insert(prayerListText, Icons.Icon({ prayer.name, type='prayer' })) | |||
end | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!'..Icons.Icon({'Prayer', 'Prayers', type='skill'})) | |||
table.insert(resultPart, '\r\n|-\r\n|style="text-align:center;"| ' .. table.concat(prayerListText, ' • ')) | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | end | ||
Line 257: | Line 271: | ||
function p.getSpellNavbox(frame) | function p.getSpellNavbox(frame) | ||
local | local spellTable = { ["standard"] = {}, ["curse"] = {}, ["aurora"] = {}, ["ancient"] = {}, ["alt"] = {} } | ||
local | local catData = { | ||
{ ["name"] = 'standard', ["header"] = '[[Magic#Standard_Magic|Standard Spells]]', ["imgType"] = 'spell' }, | |||
{ ["name"] = 'curse', ["header"] = '[[Magic#Curses|Curses]]', ["imgType"] = 'curse' }, | |||
{ ["name"] = 'aurora', ["header"] = '[[Magic#Auroras|Auroras]]', ["imgType"] = 'aurora' }, | |||
{ ["name"] = 'ancient', ["header"] = '[[Magic#Ancient_Magicks|Ancient Magicks]]', ["imgType"] = 'spell' }, | |||
{ ["name"] = 'alt', ["header"] = '[[Alternative_Magic|Alt Magic]]', ["imgType"] = 'spell' }, | |||
} | |||
for i, spell in | for i, spell in ipairs(MagicData.Spells) do | ||
table.insert(spellTable['standard'], { ["name"] = spell.name, ["order"] = spell.level }) | |||
end | |||
for i, spell in ipairs(MagicData.Curses) do | |||
table.insert(spellTable['curse'], { ["name"] = spell.name, ["order"] = spell.level }) | |||
end | end | ||
for i, spell in | for i, spell in ipairs(MagicData.Auroras) do | ||
table.insert(spellTable['aurora'], { ["name"] = spell.name, ["order"] = spell.level }) | |||
end | end | ||
for i, spell in | for i, spell in ipairs(MagicData.Ancient) do | ||
table.insert(spellTable['ancient'], { ["name"] = spell.name, ["order"] = spell.level }) | |||
end | end | ||
for i, spell in | for i, spell in ipairs(MagicData.AltMagic) do | ||
table.insert(spellTable['alt'], { ["name"] = spell.name, ["order"] = spell.level }) | |||
end | end | ||
for i, | |||
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" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!colspan=2|[[File:Magic_(skill).svg|25px|link=Spells]] [[Spells]]') | |||
for i, catDefn in ipairs(catData) do | |||
table.sort(spellTable[catDefn.name], 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"| ' .. catDefn.header) | |||
table.insert(resultPart, '\r\n|style="text-align:center;| ' .. getSpellList(spellTable[catDefn.name], catDefn.imgType)) | |||
end | end | ||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | end | ||
Revision as of 21:08, 13 November 2021
Documentation for this module may be created at Module:Navboxes/doc
-- New module to stop navbox generators cluttering other modules
local p = {}
local SkillData = mw.loadData('Module:Skills/data')
local MagicData = mw.loadData('Module:Magic/data')
local ItemData = mw.loadData('Module:Items/data')
local Shared = require('Module:Shared')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Shop = require('Module:Shop')
function p.getFarmingNavbox(frame)
local resultPart = {}
local seedsTable = {}
local produceTable = {}
for i, item in ipairs(ItemData.Items) do
if item.farmingLevel ~= nil then
local tier = item.tier
if seedsTable[tier] == nil then
-- Initialise tier tables
seedsTable[tier] = {}
produceTable[tier] = {}
end
if item.grownItemID ~= nil then
local grownItem = Items.getItemByID(item.grownItemID)
if grownItem ~= nil then
table.insert(produceTable[tier], { ["name"] = grownItem.name, ["level"] = item.farmingLevel })
end
end
table.insert(seedsTable[tier], { ["name"] = item.name, ["level"] = item.farmingLevel })
end
end
-- Generate output table
table.insert(resultPart, '{| class="wikitable mw-collapsible" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan="2" style="padding-left:64px;"|' .. 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
-- Determine tier list & order in which tiers will be listed in output
local tierList = {}
for tier, seeds in pairs(seedsTable) do
table.insert(tierList, tier)
end
table.sort(tierList, function(a, b) return a < b end)
-- Generate table section for each tier
for i, tier in pairs(tierList) do
-- 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"| ' .. tier .. 's')
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 = {}, {}, {}, {}
-- Hide Lemon cake
foundIDs[1029] = true
foundIDs[1061] = true
-- Harvested food first
for i, item in ipairs(ItemData.Items) do
if item.grownItemID ~= nil then
local grownItem = Items.getItemByID(item.grownItemID)
if grownItem ~= nil and grownItem.canEat then
table.insert(harvestedFood, { ["name"] = grownItem.name, ["order"] = item.farmingLevel })
foundIDs[grownItem.id] = true
end
end
end
-- Any cooked & other food
for i, item in ipairs(ItemData.Items) do
-- If an item can be eaten then it must be food
if foundIDs[i - 1] == nil and item.canEat then
if item.cookingCategory ~= nil then
-- Item is cooked, such food items are split by category
if cookedFood[item.cookingCategory + 1] == nil then
cookedFood[item.cookingCategory + 1] = {}
end
local perfectName = nil
if item.perfectItem ~= nil then
local perfectItem = Items.getItemByID(item.perfectItem)
if perfectItem ~= nil then
perfectName = perfectItem.name
foundIDs[item.perfectItem] = true
end
end
table.insert(cookedFood[item.cookingCategory + 1], { ["name"] = item.name, ["order"] = item.cookingLevel, ["perfectName"] = perfectName })
else
-- Item cannot be cooked or grown, but can be eaten
table.insert(otherFood, { ["name"] = item.name, ["order"] = item.id })
end
foundIDs[i - 1] = 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 = {
Icons.Icon({'Normal Cooking Fire', 'Cooking Fire', type='upgrade', nolink=true}),
Icons.Icon({'Basic Furnace', 'Furnace', type='upgrade', nolink=true}),
Icons.Icon({'Basic Pot', 'Pot', type='upgrade', nolink=true})
}
local getFoodList = function(foodTable)
local listPart = {}
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
return table.concat(listPart, ' • ')
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable mw-collapsible" 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"| [[File:Crab_(item).svg|25px|link=Food]] [[Food]]')
table.insert(resultPart, '\r\n|-\r\n!colspan="2"| Cooked')
for catID, foodTable in ipairs(cookedFood) do
table.insert(resultPart, '\r\n|-\r\n!scope="row"| ' .. cookingCatHeader[catID])
table.insert(resultPart, '\r\n|style="text-align:center;"| ' .. getFoodList(foodTable))
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 result = '{| class="wikitable" style="margin:auto; clear:both; width: 100%"'
result = result..'\r\n!colspan=2|'..Icons.Icon({'Herblore', 'Potions', type='skill'})
local CombatPots = {}
local SkillPots = {}
for i, potData in Shared.skpairs(SkillData.Herblore.ItemData) do
if potData.category == 0 then
table.insert(CombatPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
else
if potData.name == 'Bird Nests Potion' then
table.insert(SkillPots, Icons.Icon({"Bird Nest Potion", type='item', img="Bird Nest Potion I"}))
else
table.insert(SkillPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
end
end
end
result = result..'\r\n|-\r\n!Combat Potions\r\n|class="center" style="vertical-align:middle;"'
result = result..'|'..table.concat(CombatPots, ' • ')
result = result..'\r\n|-\r\n!Skill Potions\r\n|class="center" style="vertical-align:middle;"'
result = result..'|'..table.concat(SkillPots, ' • ')
result = result..'\r\n|}'
return result
end
function p.getPrayerNavbox(frame)
local prayerList = {}
for i, prayer in Shared.skpairs(SkillData.Prayer) do
table.insert(prayerList, { ["name"] = prayer.name, ["order"] = prayer.prayerLevel })
end
table.sort(prayerList, function(a, b)
if a.order == b.order then
return a.name < b.name
else
return a.order < b.order
end
end)
local prayerListText = {}
for i, prayer in ipairs(prayerList) do
table.insert(prayerListText, Icons.Icon({ prayer.name, type='prayer' }))
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!'..Icons.Icon({'Prayer', 'Prayers', type='skill'}))
table.insert(resultPart, '\r\n|-\r\n|style="text-align:center;"| ' .. table.concat(prayerListText, ' • '))
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 runeList = { ["Standard"] = {}, ["Combination"] = {} }
for i, item in ipairs(ItemData.Items) do
if item.category == 'Runecrafting' and item.type ~= nil and item.type == 'Rune' and item.runecraftingLevel ~= nil then
local runeType = (type(item.providesRune) == 'table' and Shared.tableCount(item.providesRune) > 1 and 'Combination') or 'Standard'
table.insert(runeList[runeType], { ["name"] = item.name, ["order"] = item.runecraftingLevel })
end
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan="2"|[[File:Air_Rune_(item).svg|25px|link=Runes]] [[Runes]]')
for i, cat in ipairs({'Standard', 'Combination'}) do
table.sort(runeList[cat], 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"|' .. cat .. ' Runes')
local listPart = {}
for j, rune in ipairs(runeList[cat]) do
table.insert(listPart, Icons.Icon({rune.name, type='item'}))
end
table.insert(resultPart, '\r\n|style="text-align:center;"|'..table.concat(listPart, ' • '))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getSkillcapeNavbox(frame)
local capeList = Shop.getPurchases(function(cat, purch) return cat == 'Skillcapes' end)
table.sort(capeList, function(a, b)
if a.cost.gp == b.cost.gp then
return a.name < b.name
else
return a.cost.gp < b.cost.gp
end
end)
local capeText = {}
for i, purch in ipairs(capeList) do
if purch.contains ~= nil and purch.contains.items ~= nil then
local item = Items.getItemByID(purch.contains.items[1][1])
if item ~= nil then
table.insert(capeText, Icons.Icon({item.name, type='item'}))
end
end
end
local resultPart = {}
table.insert(resultPart, '{| class="wikitable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n![[File:Cape_of_Completion_(item).svg|25px|link=Skillcapes]] [[Skillcapes]]')
table.insert(resultPart, '\r\n|-\r\n|style="text-align:center;"|'..table.concat(capeText, ' • '))
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getSpellNavbox(frame)
local spellTable = { ["standard"] = {}, ["curse"] = {}, ["aurora"] = {}, ["ancient"] = {}, ["alt"] = {} }
local catData = {
{ ["name"] = 'standard', ["header"] = '[[Magic#Standard_Magic|Standard Spells]]', ["imgType"] = 'spell' },
{ ["name"] = 'curse', ["header"] = '[[Magic#Curses|Curses]]', ["imgType"] = 'curse' },
{ ["name"] = 'aurora', ["header"] = '[[Magic#Auroras|Auroras]]', ["imgType"] = 'aurora' },
{ ["name"] = 'ancient', ["header"] = '[[Magic#Ancient_Magicks|Ancient Magicks]]', ["imgType"] = 'spell' },
{ ["name"] = 'alt', ["header"] = '[[Alternative_Magic|Alt Magic]]', ["imgType"] = 'spell' },
}
for i, spell in ipairs(MagicData.Spells) do
table.insert(spellTable['standard'], { ["name"] = spell.name, ["order"] = spell.level })
end
for i, spell in ipairs(MagicData.Curses) do
table.insert(spellTable['curse'], { ["name"] = spell.name, ["order"] = spell.level })
end
for i, spell in ipairs(MagicData.Auroras) do
table.insert(spellTable['aurora'], { ["name"] = spell.name, ["order"] = spell.level })
end
for i, spell in ipairs(MagicData.Ancient) do
table.insert(spellTable['ancient'], { ["name"] = spell.name, ["order"] = spell.level })
end
for i, spell in ipairs(MagicData.AltMagic) do
table.insert(spellTable['alt'], { ["name"] = spell.name, ["order"] = spell.level })
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" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan=2|[[File:Magic_(skill).svg|25px|link=Spells]] [[Spells]]')
for i, catDefn in ipairs(catData) do
table.sort(spellTable[catDefn.name], 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"| ' .. catDefn.header)
table.insert(resultPart, '\r\n|style="text-align:center;| ' .. getSpellList(spellTable[catDefn.name], catDefn.imgType))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getFamiliarNavbox(frame)
local familiars = Items.getItems(function(item) return item.type == 'Familiar' end)
table.sort(familiars, function(a, b) return a.summoningLevel < b.summoningLevel end)
local result = '{| class="wikitable" style="margin:auto; clear:both; width: 100%"'
result = result..'\r\n!colspan=2|[[File:Summoning_(skill).svg|25px|link=Summoning]] [[Summoning|Summoning Familiars]]'
local iconArray = {}
for i, fam in Shared.skpairs(familiars) do
table.insert(iconArray, Icons.Icon({fam.name, type='item'}))
end
result = result..'\r\n|-\r\n|style="text-align:center;"|'..table.concat(iconArray, ' • ')
result = result..'\r\n|}'
return result
end
function p.getThievingNavbox()
local returnPart = {}
-- Create table header
table.insert(returnPart, '{| class="wikitable" style="text-align:center; clear:both; margin:auto; margin-bottom:1em;"')
table.insert(returnPart, '|-\r\n!' .. Icons.Icon({'Thieving', type='skill', notext=true}) .. '[[Thieving|Thieving Targets]]')
table.insert(returnPart, '|-\r\n|')
local npcData = {}
for i, npc in ipairs(SkillData.Thieving.NPCs) do
table.insert(npcData, {["level"] = npc.level, ["name"] = npc.name})
end
table.sort(npcData, function(a, b) return a.level < b.level end)
local npcList = {}
-- Create row for each NPC
for i, npc in ipairs(npcData) do
table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
end
table.insert(returnPart, table.concat(npcList, ' • '))
table.insert(returnPart, '|}')
return table.concat(returnPart, '\r\n')
end
function p.getFishingNavbox()
local categoryHeader = {}
local categoryItems = {}
local addCatData = function(cat, catLink, itemName, itemOrder)
if categoryItems[cat] == nil then
-- Initialise category
table.insert(categoryHeader, { ["name"] = cat, ["link"] = catLink })
categoryItems[cat] = {}
end
table.insert(categoryItems[cat], { ["name"] = itemName, ["order"] = itemOrder })
end
-- Identify fishing catchable items
local fishingToItemID = {}
local junkItems = {}
local specialItems = {}
for i, item in ipairs(ItemData.Items) do
if item.fishingID ~= nil then
-- Create FishingID to item map
fishingToItemID[item.fishingID] = item
elseif item.category == 'Fishing' and item.type == 'Junk' then
table.insert(junkItems, item)
elseif item.fishingCatchWeight ~= nil then
table.insert(specialItems, item)
end
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.fish) do
local fishItem = fishingToItemID[fishID]
if fishItem ~= nil then
addCatData(area.name, 'Fishing#Fishing Areas', fishItem.name, fishItem.fishingLevel)
end
end
end
-- Junk items
for i, item in ipairs(junkItems) do
addCatData('Junk', 'Fishing#Junk', item.name, 1)
end
-- Special items
for i, item in ipairs(specialItems) do
addCatData('Special Items', 'Fishing#Special', item.name, 1 / (item.fishingCatchWeight or 1))
end
local resultPart = {}
-- Generate navbox header
table.insert(resultPart, '{| class="wikitable" 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
return p