WARFRAME Wiki
WARFRAME Wiki
(Fix to the argument call on getRelicDrop)
mNo edit summary
 
(218 intermediate revisions by 15 users not shown)
Line 1: Line 1:
  +
--- '''Void''' retrieves drop table data of opening [[Void Relic]]s as well as
--WARFRAME Wiki Void Drop Table
 
  +
-- information on where Prime parts/blueprints drop.<br />
--http://warframe.wikia.com/
 
  +
--
--Written by User:ChickenBar
 
  +
-- Includes drop tables of [[Requiem Relic]]s.
  +
--
  +
-- Originally built when the [[Void]] rewarded [[Prime]]d parts and blueprints.<br />
  +
--
  +
-- On this Wiki, Void is used in:
  +
-- * [[Module:DropTables]]
  +
-- * [[Module:Tooltips/icon]]
  +
-- * [[Template:PrimeTable]]
  +
-- * [[Template:RelicPage]]
  +
-- * [[Ducats/Prices]]
  +
-- * [[Void Relic]]
  +
-- * [[Forma]]
  +
--
  +
-- @module void
  +
-- @alias p
  +
-- @author [[User:ChickenBar|ChickenBar]]
  +
-- @attribution [[User:Flaicher|Flaicher]]
  +
-- @attribution [[User:FINNER|FINNER]]
  +
-- @attribution [[User:Falterfire|Falterfire]]
  +
-- @attribution [[User:Gigamicro|Gigamicro]]
  +
-- @attribution [[User:Synthtech|Synthtech]]
  +
-- @attribution [[User:Cephalon Scientia|Cephalon Scientia]]
  +
-- @attribution [[User:Trajos|Trajos]]
  +
-- @image VoidProjectionsIronD.png
  +
-- @require [[Module:Void/data]]
  +
-- @require [[Module:Weapons/data]]
  +
-- @require [[Module:Warframes/data]]
  +
-- @require [[Module:Resources/data]]
  +
-- @require [[Module:Companions/data]]
  +
-- @require [[Module:Icon/data]]
  +
-- @require [[Module:Tooltips/icon]]
  +
-- @require [[Module:Tooltips]]
  +
-- @require [[Module:Icon]]
  +
-- @require [[Module:String]]
  +
-- @require [[Module:Math]]
  +
-- @require [[Module:Table]]
  +
-- @require [[Module:Tooltips]]
  +
-- @release stable
  +
-- <nowiki>
   
 
local p = {}
 
local p = {}
   
local VoidData = mw.loadData( 'Module:Void/data' )
+
local VoidData = mw.loadData('Module:Void/data')
local Icon = require( "Module:Icon" )
+
local RelicData = VoidData['RelicData']
local Shared = require( "Module:Shared" )
+
local PrimeData = VoidData['PrimeData']
  +
local WeaponData = require('Module:Weapons/data')
  +
local WarframeData = mw.loadData('Module:Warframes/data')['Warframes']
  +
local ResourceData = mw.loadData('Module:Resources/data')['Resources']
  +
local CompanionData = mw.loadData('Module:Companions/data')['Companions']
   
  +
local IconData = mw.loadData([[Module:Icon/data]]);
local TxtColors = {Common = '#9C7344', Uncommon = '#D3D3D3', Rare = '#D1B962'}
 
  +
local TooltipsIcon = require([[Module:Tooltips/icon]]);
  +
local Tooltips = require([[Module:Tooltips]]);
  +
local Icon = require('Module:Icon')
  +
local String = require('Module:String')
  +
local Math = require('Module:Math')
  +
local Table = require('Module:Table')
   
  +
p.RELIC_TIER_ORDER = { "Lith", "Meso", "Neo", "Axi", "Requiem" } -- For traversal
local tooltipStart = "<span style=\"border-bottom: 1px dotted;\" class=\"relic-tooltip\" data-param=\""
 
  +
p.RADIANT = { Common = .50/3, Uncommon = .40/2, Rare = .10/1 }
local tooltipCenter = "\">"
 
local tooltipEnd = "</span>"
 
   
-- Converts item names in data to proper names
+
--- Converts Prime part names in data to proper casing.
  +
-- @function p._getPartName
-- So for example 'LATRON' becomes 'Latron Prime'
 
  +
-- @param {string} partStr Item name
function p.getItemName(itemStr)
 
  +
-- @param[opt] {boolean} keepBlueprint If true, adds 'blueprint' to end of result, false otherwise.
caseItem = string.gsub(itemStr, "(%a)([%w_']*)", Shared.titleCase)
 
  +
-- Default value is true.
if(itemStr ~= "FORMA") then
 
  +
-- @return {string} Name of Prime part
caseItem = caseItem.." Prime"
 
  +
function p._getPartName(partStr, keepBlueprint)
end
 
  +
-- User:Falterfire 6/19/2018:
return caseItem
 
  +
-- New parameter to remove ' Blueprint' if wanted
  +
-- IE returns 'Neuroptics' instead of 'Neuroptics Blueprint'
  +
if keepBlueprint == nil then keepBlueprint = true end
  +
local result = String.titleCase(partStr)
  +
if not keepBlueprint and String.contains(result, ' Blueprint') then
  +
result = string.gsub(result, ' Blueprint', '')
  +
end
  +
return result
 
end
 
end
  +
  +
-- TODO: Remove this function, looks to be unused; cannot find it in M:Acquisition or M:VoidByReward
  +
-- TODO: Maybe store images of prime parts in the same or different /data subpage? That way we remove dependency from M:Icon?
  +
--- Returns the part icon for a drop.
  +
-- For example, "Braton Prime Barrel" returns [[File:Primebarrel.png|38px]]
  +
-- @function p._getPartIconForDrop
  +
-- @param {string} drop Item name
  +
-- @return {string} Icon in the form of a wikitext link
  +
function p._getPartIconForDrop(drop)
  +
local itemName = drop['Item']
  +
local partName = drop['Part']
  +
local iconSize =''
  +
local primeToggle = 'Prime '
  +
if itemName == 'Forma' then
  +
iconSize = '43'
  +
else
  +
iconSize = '54'
  +
end
  +
  +
-- Outliers for naming convention
  +
if (itemName == 'Odonata Prime') then
  +
if partName == 'Harness Blueprint' or partName == 'Systems Blueprint' or partName == 'Wings Blueprint' then
  +
primeToggle = 'Archwing '
  +
end
  +
elseif (partName == 'Carapace' or partName == 'Cerebrum' or partName == 'Systems') then
  +
primeToggle = ''
  +
end
  +
  +
if (partName == 'Blueprint') then
  +
return Icon._Prime(String.titleCase(drop['Item']), nil, iconSize)
  +
elseif (itemName == 'Kavasa Prime') then
  +
return Icon._Prime('Kavasa', nil, iconSize)
  +
end
  +
return Icon._Item(primeToggle..partName, '', iconSize)
  +
end
  +
  +
--- Returns the relics in which the item is dropped from.
  +
-- @function p.item
  +
-- @param {table} frame Frame object
  +
-- * {string} itemName Item name
  +
-- * {string} itemPart Item part
  +
-- * [opt] {string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
  +
-- @return {string} Wikitext of resultant list
  +
function p.item(frame)
  +
local itemName = frame.args[1]
  +
local partName = frame.args[2]
  +
local relicTier = frame.args[3]
  +
  +
assert(itemName ~= nil, 'p.item(frame): itemName is nil')
  +
assert(partName ~= nil, 'p.item(frame): itemName is nil')
  +
  +
return p._item(itemName, partName, relicTier)
  +
end
  +
  +
--- Returns the relics in which the item is dropped from.
  +
-- @function p._item
  +
-- @param {string} itemName Item name, case sensitive
  +
-- @param {string} partName Item part, case sensitive
  +
-- @param[opt] {string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
  +
-- @return {string} Wikitext of resultant list
  +
function p._item(itemName, partName, relicTier, tooltip)
  +
local relics = {}
  +
local vaultedRelics = {}
  +
  +
if tooltip == nil then
  +
tooltip = true
  +
end
  +
  +
if not PrimeData[itemName] or not PrimeData[itemName]['Parts'][partName] then
  +
error('p._item(itemName, partName, relicTier): Item "' .. itemName .. '" not found.')
  +
end
  +
  +
for relicName, rarity in Table.skpairs(PrimeData[itemName]['Parts'][partName]['Drops']) do
  +
local relic = RelicData[relicName]
  +
if (relicTier == nil or relic['Tier'] == relicTier) then
  +
local relicString = rarity
  +
if tooltip then
  +
relicString = Tooltips.full(relicName, 'Void', relic)..' '..relicString
  +
else
  +
relicString = '[['..relicName..']] '..relicString
  +
end
  +
if (relic['Vaulted'] ~= nil) then
  +
relicString = relicString..' ([[Prime Vault|V]])'
  +
table.insert(vaultedRelics, relicString)
  +
else
  +
if (relic['IsBaro']) then
  +
relicString = relicString..' ([[Baro Ki\'Teer|B]])'
  +
end
  +
table.insert(relics, relicString)
  +
end
  +
end
  +
end
  +
  +
-- Putting vaulted relics at the end of list
  +
for _, relicName in ipairs(vaultedRelics) do
  +
table.insert(relics, relicName)
  +
end
  +
  +
return table.concat(relics, '<br />')
  +
end
  +
  +
--- Gets the total number of relics in the game.
  +
-- @function p.getRelicTotal
  +
-- @param {table} frame Frame object
  +
-- @param[opt] {string} frame.args Options for what relic types to include are
  +
-- "unvaulted", "vaulted", "baro", or nil for all relics
  +
-- @return {number} The total count of all relics
  +
function p.getRelicTotal(frame)
  +
if (frame.args[1] == nil) then
  +
return Table.size(RelicData)
  +
end
  +
  +
local total = 0
  +
local countUnvaulted = Table.contains(frame.args, 'unvaulted')
  +
local countVaulted = Table.contains(frame.args, 'vaulted')
  +
local countBaro = Table.contains(frame.args, 'baro')
  +
  +
for _, relic in pairs(RelicData) do
  +
if (countUnvaulted and relic['Vaulted'] == nil) then
  +
total = total + 1
  +
end
  +
if (countVaulted and relic['Vaulted'] ~= nil) then
  +
total = total + 1
  +
end
  +
if (countBaro and relic['IsBaro']) then
  +
total = total + 1
  +
end
  +
end
  +
return total
  +
end
  +
  +
--- Gets the ducat value of a Prime part or blueprint.
  +
-- @function p.getDucatValue
  +
-- @param {string} itemName Prime item name
  +
-- @param {string} partName Part name
  +
-- @return {number} The ducat value of that Prime part/blueprint
  +
function p.getDucatValue(frame)
  +
local itemName = frame.args ~= nil and frame.args[1] or nil
  +
local partName = frame.args ~= nil and frame.args[2] or nil
  +
  +
assert(itemName ~= nil, 'p.getDucatValue(): Item name missing')
  +
assert(partName ~= nil, 'p.getDucatValue(): Part name missing')
  +
return p._getDucatValue(itemName, partName)
  +
end
  +
  +
--- Gets the ducat value of a Prime part or blueprint.
  +
-- @function p._getDucatValue
  +
-- @param {string} itemName Prime item name
  +
-- @param {string} partName Part name
  +
-- @return {number} The ducat value of that Prime part/blueprint
  +
function p._getDucatValue(itemName, partName)
  +
assert(PrimeData[itemName] ~= nil, 'p._getDucatValue(itemName, partName): item "'..itemName..'" does not exist in [[Module:Void/data]]')
  +
assert(PrimeData[itemName]['Parts'][partName] ~= nil, 'p._getDucatValue(itemName, partName): part "'..partName..'" of item "'..itemName..'" does not exist in [[Module:Void/data]]')
  +
  +
return PrimeData[itemName]['Parts'][partName]['DucatValue']
  +
end
  +
  +
--- Gets the total ducat value of all Prime parts and blueprints.
  +
-- @function p.getTotalDucats
  +
-- @param[opt] {string} relicTier Tier name if want to filter by a specific relic tier; default nil for all relic tiers
  +
-- @return {number} The total ducat value
  +
function p.getTotalDucats(frame)
  +
local relicTier = frame.args ~= nil and frame.args[1] or nil
  +
  +
-- TODO: The following wouldn't work if filtering by certain relic tiers
  +
-- though I'm sure there is a way to optimize these calculations without looping through
  +
-- all of a data table
  +
-- local totalItemCount = Table.size(RelicData) * 6 -- counting all items, including duplicates
  +
-- number of Forma BP drops; note some relics have Forma take up two slots (e.g. only Meso D1)
  +
-- local numFormaDrops = Table.size(data['FORMA']['BLUEPRINT']) + 1
  +
  +
local totalItemCount = 0 -- counting all items, including duplicates and non-prime items (e.g. Forma)
  +
local withoutFormaCount = 0 -- counting all items excluding forma
  +
local totalDucats = 0 -- all, including duplicates, itemDucats
  +
local weightedDucats = 0 -- all, including duplicates, itemDucats, * rarity weight for radiant relic
  +
local availableDucats = 0 -- total ducats for items from available relics
  +
local availableItems = 0 -- available items
  +
local availableItemsNoForma = 0 -- available items excluding forma
  +
local vaultedDucats = 0 -- total ducats for items from vaulted relics
  +
local vaultedItems = 0 -- vaulted items
  +
local vaultedItemsNoForma = 0 -- vaulted items excluding forma
  +
  +
-- TODO: Refactor so it won't be triple for loop, though this may be hard
  +
-- to do b/c of the way the tables in /data are formatted; the best we can
  +
-- do is probably add some optimizations?
  +
for itemName, itemTable in pairs(PrimeData) do
  +
for partName, relicTable in pairs(itemTable['Parts']) do
  +
for relicName, rarity in pairs(relicTable['Drops']) do
  +
local relic = RelicData[relicName]
  +
if relicTier == nil or relic['Tier'] == relicTier then
  +
if relic['Vaulted'] then
  +
vaultedItems = vaultedItems + 1
  +
else
  +
availableItems = availableItems + 1
  +
end
  +
  +
totalItemCount = totalItemCount + 1
  +
  +
if itemName ~= 'FORMA' then
  +
local tempDucat = p._getDucatValue(itemName, partName)
  +
totalDucats = totalDucats + tempDucat
  +
weightedDucats = weightedDucats + tempDucat * p.RADIANT[rarity]*6
  +
withoutFormaCount = withoutFormaCount + 1
  +
  +
if relic['Vaulted'] then
  +
vaultedDucats = vaultedDucats + tempDucat
  +
vaultedItemsNoForma = vaultedItemsNoForma + 1
  +
else
  +
availableDucats = availableDucats + tempDucat
  +
availableItemsNoForma = availableItemsNoForma + 1
  +
end
  +
end
  +
end
  +
end
  +
end
  +
end
  +
  +
-- TODO: Average ducat value is not a helpful statistic due to the different
  +
-- drop rates of parts. A better statistic would be average ducat value per
  +
-- <relic_tier> relic which would account for item part drop rarity weights.
  +
-- This is a more complicated problem since you would
  +
-- have to account for different refinement levels since the rarity weights change.
  +
local ducatsIcon = Tooltips.icon('Orokin Ducats', 'Resources')
  +
if relicTier then
  +
return string.format([[
  +
'''Average Ducats Value'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
  +
'''Weighted Average Ducats Value'''&#58; %s'''%s''' (for Radiant relics)<br />
  +
'''Available'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
  +
'''Vaulted'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />]],
  +
ducatsIcon,
  +
Math.round(totalDucats / totalItemCount, 0.01),
  +
Math.formatnum(totalItemCount),
  +
Math.formatnum(withoutFormaCount),
  +
ducatsIcon,
  +
Math.round(weightedDucats / totalItemCount, 0.01),
  +
ducatsIcon,
  +
Math.formatnum(availableDucats),
  +
Math.formatnum(availableItems),
  +
Math.formatnum(availableItemsNoForma),
  +
ducatsIcon,
  +
Math.formatnum(vaultedDucats),
  +
Math.formatnum(vaultedItems),
  +
Math.formatnum(vaultedItemsNoForma)
  +
)
  +
else
  +
return string.format([[
  +
'''Total Ducats Value'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
  +
'''Weighted Average Ducats Value'''&#58; %s'''%s''' (for Radiant relics)<br />
  +
'''Available'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
  +
'''Vaulted'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />]],
  +
ducatsIcon,
  +
Math.formatnum(totalDucats),
  +
Math.formatnum(totalItemCount),
  +
Math.formatnum(withoutFormaCount),
  +
ducatsIcon,
  +
Math.round(weightedDucats / totalItemCount, 0.01),
  +
ducatsIcon,
  +
Math.formatnum(availableDucats),
  +
Math.formatnum(availableItems),
  +
Math.formatnum(availableItemsNoForma),
  +
ducatsIcon,
  +
Math.formatnum(vaultedDucats),
  +
Math.formatnum(vaultedItems),
  +
Math.formatnum(vaultedItemsNoForma)
  +
)
  +
end
  +
end
  +
  +
--- Returns table row for wikitable generated by p.ducatRelicList().
  +
-- @function ducatValueRow
  +
-- @param {string} itemName Item name, case sensitive
  +
-- @param {string} partName Item part, case sensitive
  +
-- @param[opt] {string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
  +
-- @return {string} Wikitext of wikitable row
  +
local function ducatValueRow(itemName, partName, relicTier)
  +
local ducatValue = p._getDucatValue(itemName, partName)
  +
local sortValue = ''
  +
  +
--assert(itemName ~= nil, 'ducatValueRow(itemName, partName, relicTier): Please enter an item name')
  +
--assert(partName ~= nil, 'ducatValueRow(itemName, partName, relicTier): Please enter a part name')
  +
  +
-- First cell; blueprints will appear first before other parts of the same item
  +
if partName == 'Blueprint' then
  +
sortValue = itemName..' _'..partName
  +
else
  +
sortValue = itemName..' '..partName
  +
end
  +
  +
return string.format([[
  +
| data-sort-value="%s"| %s
  +
| %s
  +
| '''%d'''
  +
|-
  +
]],
  +
sortValue,
  +
itemName..' '..partName,
  +
p._item(itemName, partName, relicTier, false),
  +
ducatValue
  +
)
  +
end
  +
  +
--- Builds a table of all Prime parts, the relics they are in, and their ducat value
  +
-- as seen in [[Ducats/Prices/Lith]].
  +
-- @function p.ducatRelicList
  +
-- @param[opt] {string} relicTier Tier name if want to filter by a specific relic tier; default nil for all tiers
  +
-- @param[opt] {string} listMode If 'Vaulted' displays only vaulted items; if 'Unvaulted' displays only unvaulted items;
  +
-- if 'All' or nil, displays all items; default nil
  +
-- @param[opt] {string} skipForma If anything, Forma entries are skipped.
  +
-- @param[opt] {string} skipRequiem If anything, Requiem relics are skipped.
  +
-- @return {string} Wikitext of table
  +
function p.ducatRelicList(frame)
  +
local relicTier = frame.args ~= nil and frame.args['relicTier'] or nil
  +
-- Adding switch to choose only vaulted or unvaulted or all items to show
  +
local listMode = frame.args ~= nil and frame.args['listMode'] or 'ALL'
  +
listMode = String.titleCase(listMode)
  +
-- Adding a switch to skip Forma and Requiem relic entries.
  +
local skipForma = frame.args ~= nil and frame.args['skipForma'] or nil
  +
local skipRequiem = frame.args ~= nil and frame.args['skipRequiem'] or nil
  +
  +
-- For preventing more than a single entry per item in "itemSet".
  +
local itemsDone = {}
  +
  +
local itemSet = {}
  +
-- Note that newline at the end of table header needed so that first column
  +
-- will properly show
  +
local result = { [[
  +
<div style="overflow-y:auto; max-height:600px">
  +
{| style="width:100%;" class="listtable sortable" align="center"
  +
|-
  +
! Part
  +
! Drop Location(s)
  +
! data-sort-type="number" |]] }
  +
table.insert(result, Tooltips.icon('Orokin Ducats', 'Resources'))
  +
table.insert(result, [[Ducat Value
  +
|-
  +
]])
  +
-- Collecting data of all items, filtering based on relic tier
  +
for itemName, itemTable in Table.skpairs(PrimeData) do
  +
if ( itemName ~= "Forma" or skipForma == nil ) then
  +
for partName, relicTable in Table.skpairs(itemTable['Parts']) do
  +
for relicName, dropRarity in pairs(relicTable['Drops']) do
  +
if itemsDone[itemName..partName] ~= true then
  +
local relicEntry = RelicData[relicName]
  +
if ( relicEntry['Tier'] ~= "Requiem" or skipRequiem == nil ) then
  +
if relicEntry['Tier'] == relicTier or relicTier == nil then
  +
if listMode == 'Vaulted' then
  +
if relicEntry['IsBaro'] or relicEntry['Vaulted'] then
  +
itemSet[{ itemName, partName }] = true
  +
itemsDone[itemName..partName] = true
  +
end
  +
elseif listMode == 'Unvaulted' then
  +
if not relicEntry['IsBaro'] and not relicEntry['Vaulted'] then
  +
itemSet[{ itemName, partName }] = true
  +
itemsDone[itemName..partName] = true
  +
end
  +
else
  +
itemSet[{ itemName, partName }] = true
  +
itemsDone[itemName..partName] = true
  +
end
  +
end
  +
end
  +
end
  +
end
  +
end
  +
end
  +
end
  +
-- Note: Sorting doesn't appear to work on this table.
  +
table.sort(itemSet)
  +
  +
for itemTable, _ in pairs(itemSet) do
  +
-- Building actual table row
  +
table.insert(result, ducatValueRow(itemTable[1], itemTable[2], relicTier))
  +
end
  +
  +
table.insert(result, '|}</div><br />([[Prime Vault|V]]) Denotes [[Prime Vault|Vaulted]] Void Relics<br />([[Baro Ki\'Teer|B]]) Denotes [[Baro Ki\'Teer]] Exclusive Void Relics')
  +
return frame:preprocess(table.concat(result))
  +
end
  +
  +
--- Builds a wikitable for all prime parts. Used in [[Void Relic/ByRewards/SimpleTable]].
  +
-- @function p.simpleRewardTable
  +
-- @param {table} frame Frame object
  +
-- @return {string} Wikitext of resultant wikitable
  +
function p.simpleRewardTable(frame)
  +
local primePartTable = {}
 
 
  +
-- TODO: Replace this loop with a call to PrimeData?
-- Converts part names in data to proper casing
 
  +
-- Collect data for each relic
function p.getPartName(partStr)
 
  +
for relicName, relic in pairs(RelicData) do
return string.gsub(partStr, "(%a)([%w_']*)", Shared.titleCase)
 
  +
local vault = "No"
  +
if (relic.Vaulted ~= nil) then
  +
vault = "Yes"
  +
end
  +
  +
-- For each relic, need each drop
  +
for i, drop in pairs(relic['Drops']) do
  +
-- Custom objects are great
  +
local thisObj = { Item = drop['Item'], Part = drop['Part'],
  +
RelicName = relicName,
  +
Rarity = drop['Rarity'], Vaulted = vault }
  +
table.insert(primePartTable, thisObj)
  +
end
  +
end
  +
  +
-- Used for sorting prime parts in ascending order alphabetically with
  +
-- unvaulted status being first
  +
local function relicComparator(relic1, relic2)
  +
if(relic1['Item'] == relic2['Item']) then
  +
if(relic1['Part'] == relic2['Part']) then
  +
-- Vaulted is a string "Yes" or "No" as defined in for loop prior to this function
  +
return relic1['Vaulted'] < relic2['Vaulted']
  +
else
  +
return relic1['Part'] < relic2['Part']
  +
end
  +
else
  +
return relic1['Item'] < relic2['Item']
  +
end
  +
end
  +
  +
table.sort(primePartTable, relicComparator)
  +
  +
local rewards = {}
  +
local resultTable = { [[
  +
{| class="article-table sortable" style="width: 600px;margin: auto;" cellspacing="1" cellpadding="1" border="2"
  +
|-'
  +
! scope="col"|Item
  +
! scope="col"|Part
  +
! scope="col"|Relic Name
  +
! scope="col"|Rarity
  +
! scope="col"|Relic Vaulted?]] }
  +
  +
for i, primePart in pairs(primePartTable) do
  +
local rowStr = string.format([=[
  +
|-
  +
| [[%s]] || %s || %s || %s || %s]=],
  +
primePart['Item'],
  +
primePart['Part'],
  +
Tooltips.full(primePart['RelicName'], 'Void', RelicData[primePart['RelicName']]),
  +
primePart['Rarity'],
  +
primePart['Vaulted'])
  +
table.insert(resultTable, rowStr)
  +
end
  +
table.insert(resultTable, '|}')
  +
return table.concat(resultTable, '\n')
 
end
 
end
   
  +
--- Finds all related relics and auto-generates a wikitable.
--Gets the relic with the appropriate name
 
function p.getRelic(Tier, Name)
+
-- @function p.relicsTable
  +
-- @param {table} frame Frame object
for i, relic in pairs(VoidData["Relics"]) do
 
  +
-- nil or 'unvaulted' - all unvaulted relics
if (relic.Tier == Tier and relic.Name == Name) then
 
  +
-- 'vaulted' - all vaulted relics
return relic
 
  +
-- 'baro' - all Baro-exclusive relics
end
 
  +
-- @return {string} Wikitext of wikitable
end
 
  +
function p.relicsTable(frame)
return nil
 
  +
local filter = frame.args ~= nil and frame.args[1]
  +
if filter == nil then filter = 'unvaulted' end
  +
  +
local data = {}
  +
-- Putting all relic names of relics of a certain tier in a single data table entry
  +
for _, relic in pairs(RelicData) do
  +
if false
  +
or filter=='unvaulted' and not relic['Vaulted'] and not relic['IsBaro']-- is the baro check necessary?
  +
or filter=='vaulted' and relic['Vaulted']
  +
or filter=='baro' and relic['IsBaro']
  +
then
  +
local tier = relic['Tier']
  +
data[tier] = data[tier] or {}
  +
table.insert(data[tier], relic)
  +
end
  +
end
  +
for _,t in pairs(data) do
  +
table.sort(t,function(a,b)
  +
return a.Name<b.Name
  +
end)
  +
end
  +
  +
local resultTable = {
  +
'{| class="article-table" cellspacing="1" cellpadding="1" border="2" style="margin:auto"',
  +
('|+ %s Relics'):format(({
  +
unvaulted = 'Unvaulted/Available',
  +
vaulted = 'Vaulted/Unavailable',
  +
baro = 'Baro Ki\'Teer Exclusive',
  +
})[filter] or 'Available'),
  +
'|-'
  +
}
  +
-- Loop through each tier and add a header
  +
for _, tier in ipairs(p.RELIC_TIER_ORDER) do
  +
table.insert(resultTable,
  +
('! scope="col" style="width:20%%;text-align:center;" | %s<br />(%s relics)')
  +
:format(tier, Table.size(data[tier] or {}))
  +
)
  +
end
  +
table.insert(resultTable,'|-')
  +
  +
-- Loop through each tier and add all its matching relics
  +
for _, tier in ipairs(p.RELIC_TIER_ORDER) do
  +
table.insert(resultTable, '| ')
  +
for _, relic in ipairs(data[tier] or {}) do
  +
table.insert(resultTable, '*'..Tooltips.text(relic.Name,'Void', relic)..'<br />')
  +
end
  +
end
  +
table.insert(resultTable, '|}')
  +
  +
return frame:preprocess(table.concat(resultTable, '\n'))
 
end
 
end
   
  +
--- Builds a wikitable of each prime part and the relics it can be dropped from.
--Right now, all relics are on the same platform
 
  +
-- @function p.byReward
--If that changes, this function will allow separating by platform
 
  +
-- @param {table} frame Frame object
function p.isRelicOnPlatform(Relic, Platform)
 
  +
-- @return {string} Wikitext of table
local Platforms = Relic.Platforms
 
  +
function p.byReward(frame)
if(Platforms == nil) then
 
  +
local resultTable = { [[
return true
 
  +
{| class="bigmodtable" style="line-height:16px; font-size:14px;"
else
 
  +
|-
local foundIt = false
 
  +
! Item Name !! Part Name !! Relics]] }
for i, plat in pairs(Platforms) do
 
  +
if (plat == Platform) then
 
  +
for item, partTable in Table.skpairs(PrimeData) do
foundIt = true
 
  +
table.insert(resultTable, '|-')
end
 
  +
-- First column with item name and image; first column will span a number of rows
end
 
  +
-- that depend on how many prime parts are associated with item
return foundIt
 
  +
local imageName = WeaponData[item] and WeaponData[item].Image or
end
 
  +
WarframeData[item] and WarframeData[item]['Image'] or
  +
CompanionData[item] and CompanionData[item]['Image'] or
  +
ResourceData[item] and ResourceData[item]['Image'] or
  +
'Panel.png'
  +
  +
local itemColumn = string.format('| rowspan="%d"|[[File:%s|300px]]<br/>[[%s]]',
  +
Table.size(partTable['Parts']), imageName, item)
  +
table.insert(resultTable, itemColumn)
  +
  +
local firstRow = true
  +
-- Getting relics for each prime part of an item
  +
for part, drops in Table.skpairs(partTable['Parts']) do
  +
if (firstRow) then
  +
firstRow = false
  +
else
  +
table.insert(resultTable, '|-')
  +
end
  +
  +
local partName = p._getPartName(part)
  +
  +
-- Second column with part name
  +
table.insert(resultTable, '| '..partName..' || ')
  +
  +
local relics = {}
  +
local vaultedRelics = {}
  +
for relicName, rarity in pairs(drops['Drops']) do
  +
local relic = RelicData[relicName]
  +
local relicString = Tooltips.full(relicName, 'Void', relic).." "..rarity
  +
  +
if (relic['Vaulted'] ~= nil) then
  +
relicString = relicString..' ([[Prime Vault|V]])'
  +
table.insert(vaultedRelics, relicString)
  +
else
  +
if (relic['IsBaro']) then
  +
relicString = relicString..' ([[Baro Ki\'Teer|B]])'
  +
end
  +
table.insert(relics, relicString)
  +
end
  +
end
  +
-- Third column with list of relics that drops a prime part;
  +
-- unvaulted relics will appear at the top and vaulted relics at the bottom
  +
for _, relicName in ipairs(vaultedRelics) do
  +
table.insert(relics, relicName)
  +
end
  +
table.insert(resultTable, table.concat(relics, '<br />'))
  +
end
  +
end
  +
table.insert(resultTable, '|}')
  +
  +
return table.concat(resultTable, '\n')
 
end
 
end
   
--Returns the rarity if a relic drops a part
+
--- Builds a wikitable of each relic's drop tables.
  +
-- @function p.byRelic
--Otherwise, returns nil
 
  +
-- @param {table} frame Frame object
function p.getRelicDropRarity(Relic, item, part)
 
  +
-- @return {string} Wikitext of table
for i, drop in pairs(Relic.Drops) do
 
  +
function p.byRelic(frame)
if ( drop.Item == item and drop.Part == part) then
 
  +
local tableStr = { [[
return drop.Rarity
 
  +
{| class="article-table sortable"
end
 
  +
! Tier
end
 
  +
! Name
 
  +
! Exclusivity
return nil
 
  +
! Common Rewards
  +
! Uncommon Rewards
  +
! Rare Rewards]] }
  +
  +
local commonStr, uncommonStr, rareStr
  +
local itemName, partName, itemLink
  +
  +
-- Building each table row
  +
for relicName, relicTable in pairs(RelicData) do
  +
-- First, new row indicator
  +
table.insert(tableStr, "|-")
  +
-- Tier & relic names
  +
table.insert(tableStr, ("| "..relicTable['Tier']))
  +
table.insert(tableStr, ("| [["..relicName.."|"..relicTable['Name'].."]]"))
  +
  +
if (relicTable.isBaro) then
  +
table.insert(tableStr, ("| [[Baro Ki'Teer|Baro]]"))
  +
elseif (relicTable.Vaulted) then
  +
table.insert(tableStr, ("| [[Prime Vault|Vaulted]]"))
  +
else
  +
table.insert(tableStr, ("| &ndash;"))
  +
end
  +
  +
commonStr = { "| " }
  +
uncommonStr = { "| " }
  +
rareStr = { "| " }
  +
for i, drop in ipairs(relicTable['Drops']) do
  +
itemName = drop['Item']
  +
partName = drop['Part']
  +
itemLink = string.format('* [[%s|%s %s]]', itemName, itemName, partName)
  +
if (drop['Rarity'] == 'Common') then
  +
table.insert(commonStr, itemLink)
  +
elseif (drop['Rarity'] == 'Uncommon') then
  +
table.insert(uncommonStr, itemLink)
  +
else
  +
table.insert(rareStr, itemLink)
  +
end
  +
end
  +
  +
table.insert(tableStr, table.concat(commonStr, '\n'))
  +
table.insert(tableStr, table.concat(uncommonStr, '\n'))
  +
table.insert(tableStr, table.concat(rareStr, '\n'))
  +
end
  +
table.insert(tableStr, "|}")
  +
return table.concat(tableStr, '\n')
 
end
 
end
   
  +
--- Builds a wikitable of each relic's Ducat Values.
--Returns the part icon for a drop
 
  +
-- @function p.byDucats
--(IE Braton Prime Barrel returns the Prime Barrel icon)
 
  +
-- @param {table} frame Frame object
function p.getPartIconForDrop(drop)
 
  +
-- @return {string} Wikitext of table
local iName = p.getItemName(drop.Item)
 
  +
function p.byDucats(frame)
local pName = p.getPartName(drop.Part)
 
  +
local tableStr = { [[
local iconSize =''
 
  +
{| class="article-table sortable"
local primeToggle = 'Prime '
 
  +
! Name
if iName == 'Forma' then
 
  +
! Exclusivity
iconSize = '43'
 
  +
! Total Ducats
else
 
  +
! Intact Average
iconSize = '54'
 
  +
! Exceptional Average
end
 
  +
! Flawless Average
 
  +
! Radiant Average]] }
if iName == 'Odonata Prime' then
 
  +
if pName == 'Harness Blueprint' or pName == 'Systems Blueprint' or pName == 'Wings Blueprint' then
 
  +
local totalStr, intactStr, exceptionalStr, flawlessStr, radiantStr
primeToggle = 'Archwing '
 
  +
local value, total, intact, exceptional, flawless, radiant = 0, 0, 0, 0, 0, 0
end
 
  +
elseif pName == 'Carapace' or pName == 'Cerebrum' or pName == 'Systems' then
 
  +
-- Building each table row
primeToggle = ''
 
  +
for relicName, relicTable in pairs(RelicData) do
end
 
  +
table.insert(tableStr, "|-")
 
  +
table.insert(tableStr, ("| [["..relicName.."|"..relicTable['Name'].."]]"))
local icon =''
 
  +
if(pName == 'Blueprint') then
 
  +
if (relicTable.isBaro) then
icon = Icon._Prime(Shared.titleCase(drop.Item), nil,iconSize)
 
  +
table.insert(tableStr, ("| [[Baro Ki'Teer|Baro]]"))
elseif iName == 'Kavasa Prime' then
 
  +
elseif (relicTable.Vaulted) then
icon = Icon._Prime('Kavasa', nil,iconSize)
 
  +
table.insert(tableStr, ("| [[Prime Vault|Vaulted]]"))
else
 
  +
else
icon = Icon._Item(primeToggle..pName,"",iconSize)
 
  +
table.insert(tableStr, ("| &ndash;"))
end
 
  +
end
 
  +
return icon
 
  +
totalStr = { "| " }
  +
intactStr = { "| " }
  +
exceptionalStr = { "| " }
  +
flawlessStr = { "| " }
  +
radiantStr = { "| " }
  +
value, total, intact, exceptional, flawless, radiant = 0, 0, 0, 0, 0, 0
  +
  +
for i, drop in ipairs(relicTable['Drops']) do
  +
value = PrimeData[drop['Item']].Parts[drop['Part']].DucatValue
  +
  +
total = total + value
  +
if (drop['Rarity'] == 'Common') then
  +
intact = intact + 0.2533 * value
  +
exceptional = exceptional + 0.2333 * value
  +
flawless = flawless + 0.2 * value
  +
radiant = radiant + 0.1667 * value
  +
elseif (drop['Rarity'] == 'Uncommon') then
  +
intact = intact + 0.11 * value
  +
exceptional = exceptional + 0.13 * value
  +
flawless = flawless + 0.17 * value
  +
radiant = radiant + 0.2 * value
  +
else
  +
intact = intact + 0.02 * value
  +
exceptional = exceptional + 0.04 * value
  +
flawless = flawless + 0.06 * value
  +
radiant = radiant + 0.1 * value
  +
end
  +
end
  +
  +
table.insert(totalStr, total)
  +
table.insert(intactStr, intact)
  +
table.insert(exceptionalStr, exceptional)
  +
table.insert(flawlessStr, flawless)
  +
table.insert(radiantStr, radiant)
  +
  +
table.insert(tableStr, table.concat(totalStr, '\n'))
  +
table.insert(tableStr, table.concat(intactStr, '\n'))
  +
table.insert(tableStr, table.concat(exceptionalStr, '\n'))
  +
table.insert(tableStr, table.concat(flawlessStr, '\n'))
  +
table.insert(tableStr, table.concat(radiantStr, '\n'))
  +
end
  +
  +
table.insert(tableStr, "|}")
  +
return table.concat(tableStr, '\n')
 
end
 
end
   
  +
--- Builds a wikitable of all relic drops sorted by rarity.
--Returns the item icon for a drop
 
  +
-- @function p.byRarity
--(IE Braton Prime Barrel returns the Braton Prime icon)
 
  +
-- @param {table} frame Frame object
function p.getItemIconForDrop(drop)
 
  +
-- {string} checkTier Relic tier to filter by; default 'All' for all tiers
local iName = p.getItemName(drop.Item)
 
  +
-- @return {string} Wikitext of table
local pName = p.getPartName(drop.Part)
 
  +
function p.byRarity(frame)
local iconSize =''
 
  +
local checkTier = frame.args ~= nil and frame.args[1] or 'All'
if iName == 'Forma' then
 
  +
iconSize = '43'
 
  +
local rarityOrder = { 'Common', 'Uncommon', 'Rare' }
else
 
  +
local rarityData = { Common = {}, Uncommon = {}, Rare = {} }
iconSize = '54'
 
  +
end
 
  +
-- Collect data
 
  +
for relicName, relic in pairs(RelicData) do
local icon =''
 
  +
local tier = relic['Tier']
icon = Icon._Prime(Shared.titleCase(drop.Item), nil, iconSize)
 
  +
local name = relic['Name']
 
  +
-- So first make sure this row is the right tier
return icon
 
  +
if (tier == checkTier or tier == 'All') then
  +
for _, drop in pairs(relic['Drops']) do
  +
local rarity = drop['Rarity']
  +
local item = drop['Item']
  +
local part = drop['Part']
  +
  +
-- Using '|' as a delimiter to split by
  +
if (rarityData[rarity][item..'|'..part] == nil) then
  +
-- Assigning a dummy value that will not be used so that keys
  +
-- can be in an unsorted set
  +
rarityData[rarity][item..'|'..part] = true
  +
end
  +
end
  +
end
  +
end
  +
  +
-- Now we can actually format the table
  +
-- Starting with all the column headers
  +
local resultTable = { string.format([[
  +
{| class="article-table"
  +
! style="width:33%%;text-align:center;" | Common (%d rewards)
  +
! style="width:33%%;text-align:center;" | Uncommon (%d rewards)
  +
! style="width:33%%;text-align:center;" | Rare (%d rewards)
  +
|-
  +
|]],
  +
Table.size(rarityData['Common']),
  +
Table.size(rarityData['Uncommon']),
  +
Table.size(rarityData['Rare'])
  +
) }
  +
  +
-- Then go through each rarity
  +
-- Each rarity is the same steps:
  +
for _, rarity in ipairs(rarityOrder) do
  +
--1. Loop through each item in that rarity in ascending order
  +
for itemStr, _ in Table.skpairs(rarityData[rarity]) do
  +
--2. Get the item & part
  +
local delimiterIndex = string.find(itemStr, '|')
  +
local itemName = string.sub(itemStr, 1, delimiterIndex - 1)
  +
local partName = p._getPartName(string.sub(itemStr, delimiterIndex + 1))
  +
--3. Add the appropriate link to the table
  +
table.insert(resultTable, string.format('* [[%s|%s %s]]', itemName, itemName, partName))
  +
end
  +
table.insert(resultTable, '|')
  +
end
  +
table.insert(resultTable, '|}')
  +
return table.concat(resultTable, '\n')
 
end
 
end
   
  +
--- Builds wikitable of Void Relics in which a Prime item's parts drop from as
function p.item( frame )
 
  +
-- seen on any Prime item page (e.g. [[Braton Prime]]).
local platform = frame.args[1]
 
  +
-- @function p.buildPrimeTable
local item_type = string.upper(frame.args[2])
 
  +
-- @param {table} frame Frame object
local item_part = string.upper(frame.args[3])
 
  +
-- @returns {string} Resultant wikitext of wikitable
local relic_tier = frame.args[4]
 
  +
function p.buildPrimeTable(frame)
if (item_part == "HELMET BLUEPRINT") then
 
  +
local itemName = String.titleCase(mw.text.decode(frame.args['name'] or ''))
item_part = "NEUROPTICS BLUEPRINT"
 
  +
assert(PrimeData[itemName] ~= nil, 'p.buildPrimeTable(frame): "'..itemName..'" does not exist in [[Module:Void/data]]')
end
 
local locations = {}
+
local n = 1;
  +
local partData = PrimeData[itemName]['Parts']
local vaultLocations = {}
 
  +
local result = { ([=[
local i
 
  +
<div style="text-align:center;"><i>Lith, Meso, Neo, and Axi refer to [[Void Relic]]s&nbsp;&nbsp;|&nbsp;&nbsp;([[Prime Vault|V]]) Denotes [[Prime Vault|Vaulted]] Void Relics&nbsp;&nbsp;|&nbsp;&nbsp;([[Baro Ki'Teer|B]]) Denotes [[Baro Ki'Teer]] Exclusive Void Relic</i></div>
for i, relic in pairs(VoidData["Relics"]) do
 
  +
<div style=" overflow-x:auto;">
if(p.isRelicOnPlatform(relic, platform) and (relic_tier == nil or relic.Tier == relic_tier)) then
 
  +
{| style="width: 100%%; table-layout: fixed; margin-right:0px;" class="article-table" cellspacing="1" cellpadding="1" border="0" align="left"
local dropRarity = p.getRelicDropRarity(relic, item_type, item_part)
 
  +
|+ %s's Relic Drops
if(dropRarity ~= nil) then
 
  +
|-]=]):format(itemName) }
local relicText = relic.Tier.." "..relic.Name
 
  +
local relicString = tooltipStart..relicText..tooltipCenter..relicText..tooltipEnd.." "..dropRarity
 
  +
local blueprint = ' Blueprint';
if(relic.IsVaulted == 1) then
 
  +
for partName, _ in pairs(partData) do
relicString = relicString.." ([[Prime Vault|V]])"
 
  +
-- Use Mission for items like Forma else use Part for items that
table.insert(vaultLocations, relicString)
 
  +
-- are crafted from multiple parts that rewarded from drop table(s)
else
 
  +
local icon = '';
if(relic.IsBaro == 1) then
 
  +
local part = partName;
relicString = relicString.." ([[Baro Ki%27Teer|B]])"
 
end
+
if part ~= nil and part ~= '' then
  +
part = mw.text.decode(part); -- Replacing HTML codes in text with their ASCII character equivalent
table.insert(locations, relicString)
 
  +
part = string.gsub(" "..string.lower( part ), "%W%l", string.upper):sub(2);
end
 
  +
end
 
  +
if part == 'Blueprint' or part == 'Main Blueprint' then
end
 
  +
local source = 'Warframes';
end
 
  +
local index = TooltipsIcon[source](itemName);
 
  +
if not index then
for _, i in pairs(vaultLocations) do
 
  +
source = 'Companions';
table.insert(locations, i)
 
  +
index = TooltipsIcon[source](itemName);
end
 
  +
end
return table.concat(locations, "<br/>")
 
  +
if not index then
  +
source = 'Weapons';
  +
index = TooltipsIcon[source](itemName);
  +
end
  +
if index then
  +
icon = Tooltips.icon(itemName, source, index).."&nbsp;";
  +
end
  +
else
  +
if string.sub(part, -#blueprint) == blueprint then
  +
part = string.sub(part, 1, #part - #blueprint);
  +
end
  +
  +
if itemName == "Odonata Prime" then
  +
if IconData["Items"]["Archwing "..part..blueprint] ~= nil then
  +
icon = "Archwing "..part..blueprint
  +
end
  +
elseif IconData["Items"]["Prime "..part] ~= nil then
  +
icon = "Prime "..part
  +
elseif IconData["Items"]["Prime "..part..blueprint] ~= nil then
  +
icon = "Prime "..part..blueprint
  +
elseif IconData["Items"][part] ~= nil then
  +
icon = part
  +
end
  +
if icon ~= '' then
  +
icon = Icon._Item(icon, nil, nil)..'&nbsp;';
  +
end
  +
end
  +
end
  +
  +
table.insert(result, ('! style="text-align:center;" | %s'):format(icon..partName))
  +
end
  +
table.insert(result, '|-')
  +
for partName, _ in pairs(partData) do
  +
table.insert(result, ('| style="text-align:center; font-size:12px;" | %s'):format(p._item(itemName, partName, nil)))
  +
end
  +
  +
table.insert(result, '|}</div>')
  +
return table.concat(result, '\n')
 
end
 
end
   
  +
--- Builds wikitable of Void Relics in which Forma blueprints drop from as
function p.relicTooltip(frame)
 
  +
-- seen on [[Forma]].
local relicName = frame.args ~= nil and frame.args[1] or frame
 
  +
-- @function p.buildFormaBPRelicTable
local platform = frame.args ~= nil and frame.args[2]
 
  +
-- @returns {string} Resultant wikitext of wikitable
if(platform == nil) then platform = 'PC' end
 
  +
function p.buildFormaBPRelicTable()
if(relicName == nil) then return nil end
 
  +
return ([=[{| style="width: 100%%;" class="article-table" cellspacing="1" cellpadding="1" border="0" align="left"
 
  +
|-
local bits = Shared.splitString(relicName, ' ')
 
  +
! style="text-align:center; width: 25%%" | Lith
local Tier = bits[1]
 
  +
! style="text-align:center; width: 25%%" | Meso
local RName = bits[2]
 
  +
! style="text-align:center; width: 25%%" | Neo
 
  +
! style="text-align:center; width: 25%%" | Axi
local theRelic = p.getRelic(Tier, RName)
 
  +
|-
if(theRelic == nil) then return 'ERROR: No relic found' end
 
  +
| style="text-align:center; padding: 6px 0px" | %s
if(not p.isRelicOnPlatform(theRelic, Platform)) then return "ERROR: That relic isn't on that platform" end
 
  +
| style="text-align:center; padding: 6px 0px" | %s
 
  +
| style="text-align:center; padding: 6px 0px" | %s
local result = '{|'
 
  +
| style="text-align:center; padding: 6px 0px" | %s
 
  +
|-
local rareTxt = {Common = '', Uncommon = '', Rare = ''}
 
  +
|}
 
  +
<div style="text-align: center;"><i>Lith, Meso, Neo, and Axi refer to [[Void Relic]]s&nbsp;&nbsp;|&nbsp;&nbsp;([[Prime Vault|V]]) Denotes [[Prime Vault|Vaulted]] Void Relics&nbsp;&nbsp;|&nbsp;&nbsp;([[Baro Ki'Teer|B]]) Denotes [[Baro Ki'Teer]] Exclusive Void Relic</i></div>]=]):format(
for i, drop in pairs(theRelic.Drops) do
 
  +
p._item('Forma', 'Blueprint', 'Lith'),
local rarity = drop.Rarity
 
  +
p._item('Forma', 'Blueprint', 'Meso'),
if(rarity ~= nil) then
 
  +
p._item('Forma', 'Blueprint', 'Neo'),
if(rareTxt[rarity] ~= '') then rareTxt[rarity] = rareTxt[rarity]..'\n' end
 
  +
p._item('Forma', 'Blueprint', 'Axi')
if(i > 1) then rareTxt[rarity] = rareTxt[rarity]..'|-' end
 
  +
)
 
  +
end
local iName = p.getItemName(drop.Item)
 
local pName = p.getPartName(drop.Part)
 
 
local icon = p.getPartIconForDrop(drop)
 
 
rareTxt[rarity] = rareTxt[rarity]..'\n| rowspan=2 class=\"Image\" | '..icon
 
 
rareTxt[rarity] = rareTxt[rarity]..'\n| class = "gradientText" style = "vertical-align:bottom; color:'..TxtColors[rarity]..';" | '..iName
 
rareTxt[rarity] = rareTxt[rarity]..'\n|-\n| class = "gradientText" style = "vertical-align:top; color:'..TxtColors[rarity]..';" | '..pName
 
end
 
end
 
   
  +
--- Returns the unvaulted relic count for a Prime part.
 
  +
-- @function p._getUnvaultedRelicCountForPart
result = result..rareTxt['Common']..'\n'..rareTxt['Uncommon']
 
  +
-- @param {string} itemName Item name, case sensitive
result = result..'\n'..rareTxt['Rare']
 
  +
-- @param {string} partName Item part, case sensitive
result = result..'\n|}'
 
  +
-- @param[opt] {string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
 
  +
-- @return {number} Unvaulted relic count, 0 if the part is vaulted
return result
 
  +
function p._getUnvaultedRelicCountForPart(itemName, partName, relicTier)
  +
local result = 0
  +
  +
if not PrimeData[itemName] or not PrimeData[itemName]['Parts'][partName] then
  +
error('p._item(itemName, partName, relicTier): Item "' .. itemName .. '" not found.')
  +
end
  +
  +
for relicName, rarity in Table.skpairs(PrimeData[itemName]['Parts'][partName]['Drops']) do
  +
local relic = RelicData[relicName]
  +
if (relicTier == nil or relic['Tier'] == relicTier) then
  +
if not relic['Vaulted'] and not relic['IsBaro'] then
  +
result = result + 1
  +
end
  +
end
  +
end
  +
  +
return result
 
end
 
end
   
  +
--- Returns the number of unvaulted relics for a Prime set.
function p.getRelicDrop(frame)
 
  +
-- @function p._getUnvaultedRelicCountForSet
local relicName = frame.args ~= nil and frame.args[1] or nil
 
  +
-- @param {string} itemName Item name
local rarity = frame.args ~= nil and frame.args[2] or nil
 
  +
-- @return {number} Unvaulted relic count, 0 if the set is vaulted,
local number = frame.args ~= nil and frame.args[3] or nil
 
  +
-- -1 if item is not obtainable from Void Relics (e.g. base variant weapons)
if number == nil then
 
  +
function p._getUnvaultedRelicCountForSet(itemName)
--The number of the drop defaults to 1 if not set
 
  +
local partData = PrimeData[itemName] and PrimeData[itemName]['Parts'] or {}
number = 1
 
  +
local result = PrimeData[itemName] and 0 or -1
elseif type(number) == 'string' then
 
  +
--If the argument is a string, force it into being a number
 
  +
for partName, _ in pairs(partData) do
number = tonumber(number)
 
  +
result = result + p._getUnvaultedRelicCountForPart(itemName, partName, nil)
end
 
  +
end
 
  +
--Platform comes from a special argument. Defaults to PC if not set
 
  +
return result
local platform = frame.args ~= nil and frame.args.platform or nil
 
  +
end
if platform == nil then platform = 'PC' end
 
 
--Return an error if any arguments are missing
 
if relicName == nil or relicName == '' then
 
return "ERROR: Missing argument 'Relic Name'"
 
elseif rarity == nil or rarity == '' then
 
return "ERROR: Missing argument 'Rarity'"
 
end
 
 
local bits = Shared.splitString(relicName, ' ')
 
local Tier = bits[1]
 
local RName = bits[2]
 
 
local theRelic = p.getRelic(Tier, RName)
 
 
--Return an error if the relic wasn't found
 
if theRelic == nil then
 
return "ERROR: Invalid relic '"..relicName.."'"
 
end
 
 
local count = 0
 
for i, drop in pairs(theRelic.Drops) do
 
--Loop through the drops to find drops of the chosen rarity
 
if drop.Rarity == rarity then
 
count = count + 1
 
--Once enough drops of the right kind have been found, return the icon + the item name
 
if count == number then
 
local iName = p.getItemName(drop.Item)
 
local pName = p.getPartName(drop.Part)
 
local icon = p.getItemIconForDrop(drop)
 
 
return icon..' [['..iName..'|'..pName..']]'
 
end
 
end
 
end
 
   
  +
--- Returns the number of unvaulted relics for a Prime set.
--If we got to here, there weren't enough drops of that rarity for this relic.
 
  +
-- @function p.getUnvaultedRelicCountForSet
return "ERROR: Only found "..count.." drops of "..rarity.." rarity for "..relicName
 
  +
-- @param {table} frame Frame object
  +
-- @return {number} Unvaulted relic count, 0 if the set is vaulted,
  +
-- -1 if item is not obtainable from Void Relics (e.g. base variant weapons)
  +
function p.getUnvaultedRelicCountForSet(frame)
  +
local itemName = String.titleCase(mw.text.decode(frame.args['name'] or ''))
  +
return p._getUnvaultedRelicCountForSet(itemName)
 
end
 
end
   

Latest revision as of 03:17, 23 November 2023


Void retrieves drop table data of opening Void Relics as well as information on where Prime parts/blueprints drop.

Includes drop tables of Requiem Relics.

Originally built when the Void rewarded Primed parts and blueprints.

On this Wiki, Void is used in:

Usage

Module

local Void = require('Module:Void')

local function func(input)
    return Void.getRelicTotal()
end

Product Backlog

Name Type Status Priority Assignee Description Date Issued Last Update
Data validation Dev New Low

Add validation functions for RelicData and PrimeData in Module:Void/data/validate. This is to ensure the accuracy of our data based on known patterns and/or rules.

19:21, 30 June 2021 (UTC)
Refactor and clean up Dev Active Medium

Remove unused functions and perform some refactoring, especially with triple loop situations (though not entirely clear how to optimize table accesses since when we are building wikitables, we loop through all the data)

04:28, 16 October 2021 (UTC) update: Removed p._getItemName() which was used to convert item names from M:Void/data from SCREAMING UPPER CASE to Title Case. This is not needed as all names are stored in Title Case to avoid extra function calls.

21:36, 22 June 2021 (UTC) 04:28, 16 October 2021 (UTC)

Finished Issues

Name Type Status Priority Assignee Description Date Issued Completion Date
Luafy Template:RelicInfobox, Template:RelicTable, and Template:RelicTable/Check Dev & Refactor Active Medium User:Cephalon Scientia

Luafy the contents of these templates which will also remove the need for p.getRelicDrop(frame), p._getItemIconForDrop(itemName), and p.getDucatValue(frame)

02:15, 24 June 2021 (UTC) 18:55, 30 June 2021 (UTC)
Ducat values in /data Dev Completed Medium User:Cephalon Scientia

Add a new "DucatValue" key to each prime part in the PrimeData subtable of M:Void/data.

3:56, 23 June 2021 (UTC) 02:11, 24 June 2021 (UTC)
Documentation Documentation Completed High User:Cephalon Scientia

Add LuaDoc-style documentation for all functions.

21:36, 22 June 2021 (UTC)
Merging Module:VoidByReward to Module:Void Dev Completed Medium User:Cephalon Scientia

Migrating all functions from M:VoidByReward to this module.

21:36, 22 June 2021 (UTC)
Relic infobox Dev Active Medium User:Cephalon Scientia

Luafy the contents of Template:RelicInfobox which use parser functions to render the infobox and message boxes.

21:36, 22 June 2021 (UTC)
Introduced and vaulted keys Dev Completed High User:Cephalon Scientia

Adding Introduced and Vaulted key corresponding to the versions that they were first introduced and last vaulted for each relic table entry.

21:36, 22 June 2021 (UTC)
Access relic data by item and part names Dev Completed High User:Cephalon Scientia

Adding a new subtable part of /data that uses item and part names to get the relics that they are dropped from; think of it as the 'inverse' of the original relic data table.

21:36, 22 June 2021 (UTC)

Documentation

Package items

void._getPartName(partStr, keepBlueprint) (function)
Converts Prime part names in data to proper casing.
Parameters:
  • partStr Item name (string)
  • keepBlueprint If true, adds 'blueprint' to end of result, false otherwise. Default value is true. (boolean; optional)
Returns: Name of Prime part (string)
void._getPartIconForDrop(drop) (function)
Returns the part icon for a drop. For example, "Braton Prime Barrel" returns Primebarrel
Parameter: drop Item name (string)
Returns: Icon in the form of a wikitext link (string)
void.item(frame) (function)
Returns the relics in which the item is dropped from.
Parameter: frame Frame object
  • {string} itemName Item name
  • {string} itemPart Item part
  • [opt] {string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
(table)
Returns: Wikitext of resultant list (string)
void._item(itemName, partName, relicTier) (function)
Returns the relics in which the item is dropped from.
Parameters:
  • itemName Item name, case sensitive (string)
  • partName Item part, case sensitive (string)
  • relicTier Relic tier to search through; if nil looks for item in all relic tiers (string; optional)
Returns: Wikitext of resultant list (string)
void.getRelicTotal(frame) (function)
Gets the total number of relics in the game.
Parameters:
  • frame Frame object (table)
    • frame.args Options for what relic types to include are "unvaulted", "vaulted", "baro", or nil for all relics (string; optional)
Returns: The total count of all relics (number)
void.getDucatValue(itemName, partName) (function)
Gets the ducat value of a Prime part or blueprint.
Parameters:
  • itemName Prime item name (string)
  • partName Part name (string)
Returns: The ducat value of that Prime part/blueprint (number)
void._getDucatValue(itemName, partName) (function)
Gets the ducat value of a Prime part or blueprint.
Parameters:
  • itemName Prime item name (string)
  • partName Part name (string)
Returns: The ducat value of that Prime part/blueprint (number)
void.getTotalDucats(relicTier) (function)
Gets the total ducat value of all Prime parts and blueprints.
Parameter: relicTier Tier name if want to filter by a specific relic tier; default nil for all relic tiers (string; optional)
Returns: The total ducat value (number)
void.ducatRelicList(relicTier, listMode, skipForma, skipRequiem) (function)
Builds a table of all Prime parts, the relics they are in, and their ducat value as seen in Ducats/Prices/Lith.
Parameters:
  • relicTier Tier name if want to filter by a specific relic tier; default nil for all tiers (string; optional)
  • listMode If 'Vaulted' displays only vaulted items; if 'Unvaulted' displays only unvaulted items; if 'All' or nil, displays all items; default nil (string; optional)
  • skipForma If anything, Forma entries are skipped. (string; optional)
  • skipRequiem If anything, Requiem relics are skipped. (string; optional)
Returns: Wikitext of table (string)
void.simpleRewardTable(frame) (function)
Builds a wikitable for all prime parts. Used in Void Relic/ByRewards/SimpleTable.
Parameter: frame Frame object (table)
Returns: Wikitext of resultant wikitable (string)
void.relicsTable(frame) (function)
Finds all related relics and auto-generates a wikitable.
Parameter: frame Frame object nil or 'unvaulted' - all unvaulted relics 'vaulted' - all vaulted relics 'baro' - all Baro-exclusive relics (table)
Returns: Wikitext of wikitable (string)
void.byReward(frame) (function)
Builds a wikitable of each prime part and the relics it can be dropped from.
Parameter: frame Frame object (table)
Returns: Wikitext of table (string)
void.byRelic(frame) (function)
Builds a wikitable of each relic's drop tables.
Parameter: frame Frame object (table)
Returns: Wikitext of table (string)
void.byDucats(frame) (function)
Builds a wikitable of each relic's Ducat Values.
Parameter: frame Frame object (table)
Returns: Wikitext of table (string)
void.byRarity(frame) (function)
Builds a wikitable of all relic drops sorted by rarity.
Parameter: frame Frame object {string} checkTier Relic tier to filter by; default 'All' for all tiers (table)
Returns: Wikitext of table (string)
void.buildPrimeTable(frame) (function)
Builds wikitable of Void Relics in which a Prime item's parts drop from as seen on any Prime item page (e. g. Braton Prime).
Parameter: frame Frame object (table)
Returns: Resultant wikitext of wikitable (string)
void.buildFormaBPRelicTable() (function)
Builds wikitable of Void Relics in which Forma blueprints drop from as seen on Forma.
Returns: Resultant wikitext of wikitable (string)
void._getUnvaultedRelicCountForPart(itemName, partName, relicTier) (function)
Returns the unvaulted relic count for a Prime part.
Parameters:
  • itemName Item name, case sensitive (string)
  • partName Item part, case sensitive (string)
  • relicTier Relic tier to search through; if nil looks for item in all relic tiers (string; optional)
Returns: Unvaulted relic count, 0 if the part is vaulted (number)
void._getUnvaultedRelicCountForSet(itemName) (function)
Returns the number of unvaulted relics for a Prime set.
Parameter: itemName Item name (string)
Returns: Unvaulted relic count, 0 if the set is vaulted, -1 if item is not obtainable from Void Relics (e.g. base variant weapons) (number)
void.getUnvaultedRelicCountForSet(frame) (function)
Returns the number of unvaulted relics for a Prime set.
Parameter: frame Frame object (table)
Returns: Unvaulted relic count, 0 if the set is vaulted, -1 if item is not obtainable from Void Relics (e.g. base variant weapons) (number)

Created with Docbunto

See Also

Code


---	'''Void''' retrieves drop table data of opening [[Void Relic]]s as well as 
--	information on where Prime parts/blueprints drop.<br />
--	
--	Includes drop tables of [[Requiem Relic]]s.
--	
--	Originally built when the [[Void]] rewarded [[Prime]]d parts and blueprints.<br />
--	
--	On this Wiki, Void is used in:
--	* [[Module:DropTables]]
--	* [[Module:Tooltips/icon]]
--	* [[Template:PrimeTable]]
--	* [[Template:RelicPage]]
--	* [[Ducats/Prices]]
--	* [[Void Relic]]
--	* [[Forma]]
--	
--	@module			void
--	@alias			p
--	@author			[[User:ChickenBar|ChickenBar]]
--	@attribution	[[User:Flaicher|Flaicher]]
--	@attribution	[[User:FINNER|FINNER]]
--	@attribution	[[User:Falterfire|Falterfire]]
--	@attribution	[[User:Gigamicro|Gigamicro]]
--	@attribution	[[User:Synthtech|Synthtech]]
--	@attribution	[[User:Cephalon Scientia|Cephalon Scientia]]
--  @attribution	[[User:Trajos|Trajos]]
--	@image			VoidProjectionsIronD.png
--	@require		[[Module:Void/data]]
--	@require		[[Module:Weapons/data]]
--	@require		[[Module:Warframes/data]]
--	@require		[[Module:Resources/data]]
--	@require		[[Module:Companions/data]]
--  @require		[[Module:Icon/data]]
--  @require		[[Module:Tooltips/icon]]
--  @require		[[Module:Tooltips]]
--	@require		[[Module:Icon]]
--	@require		[[Module:String]]
--	@require		[[Module:Math]]
--	@require		[[Module:Table]]
--	@require		[[Module:Tooltips]]
--	@release		stable
--	<nowiki>

local p = {}

local VoidData = mw.loadData('Module:Void/data')
local RelicData = VoidData['RelicData']
local PrimeData = VoidData['PrimeData']
local WeaponData = require('Module:Weapons/data')
local WarframeData = mw.loadData('Module:Warframes/data')['Warframes']
local ResourceData = mw.loadData('Module:Resources/data')['Resources']
local CompanionData = mw.loadData('Module:Companions/data')['Companions']

local IconData = mw.loadData([[Module:Icon/data]]);
local TooltipsIcon = require([[Module:Tooltips/icon]]);
local Tooltips = require([[Module:Tooltips]]);
local Icon = require('Module:Icon')
local String = require('Module:String')
local Math = require('Module:Math')
local Table = require('Module:Table')

p.RELIC_TIER_ORDER = { "Lith", "Meso", "Neo", "Axi", "Requiem" }	-- For traversal
p.RADIANT = { Common = .50/3, Uncommon = .40/2, Rare = .10/1 }

---	Converts Prime part names in data to proper casing.
--	@function		p._getPartName
--	@param			{string} partStr Item name
--	@param[opt]		{boolean} keepBlueprint If true, adds 'blueprint' to end of result, false otherwise. 
--											Default value is true.
--	@return			{string} Name of Prime part 
function p._getPartName(partStr, keepBlueprint)
	-- User:Falterfire 6/19/2018:
	-- New parameter to remove ' Blueprint' if wanted
	-- IE returns 'Neuroptics' instead of 'Neuroptics Blueprint'
	if keepBlueprint == nil then keepBlueprint = true end
	local result = String.titleCase(partStr)
	if not keepBlueprint and String.contains(result, ' Blueprint') then
		result = string.gsub(result, ' Blueprint', '')
	end
	return result
end

-- TODO: Remove this function, looks to be unused; cannot find it in M:Acquisition or M:VoidByReward
-- TODO: Maybe store images of prime parts in the same or different /data subpage? That way we remove dependency from M:Icon?
---	Returns the part icon for a drop.
--	For example, "Braton Prime Barrel" returns [[File:Primebarrel.png|38px]]
--	@function		p._getPartIconForDrop
--	@param			{string} drop Item name
--	@return			{string} Icon in the form of a wikitext link
function p._getPartIconForDrop(drop)
	local itemName = drop['Item']
	local partName = drop['Part']
	local iconSize =''
	local primeToggle = 'Prime '
	if itemName == 'Forma' then
		iconSize = '43'
	else
		iconSize = '54'
	end
	
	-- Outliers for naming convention
	if (itemName == 'Odonata Prime') then
		if partName == 'Harness Blueprint' or partName == 'Systems Blueprint' or partName == 'Wings Blueprint' then
			primeToggle = 'Archwing '
		end
	elseif (partName == 'Carapace' or partName == 'Cerebrum' or partName == 'Systems') then
		primeToggle = ''
	end
	
	if (partName == 'Blueprint') then
		return Icon._Prime(String.titleCase(drop['Item']), nil, iconSize)
	elseif (itemName == 'Kavasa Prime') then
		return Icon._Prime('Kavasa', nil, iconSize)
	end
	return Icon._Item(primeToggle..partName, '', iconSize)
end

---	Returns the relics in which the item is dropped from.
--	@function		p.item
--	@param			{table} frame Frame object
--					* {string} itemName Item name
--					* {string} itemPart Item part
--					* [opt] {string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
--	@return			{string} Wikitext of resultant list
function p.item(frame)
	local itemName = frame.args[1]
	local partName = frame.args[2]
	local relicTier = frame.args[3]
	
	assert(itemName ~= nil, 'p.item(frame): itemName is nil')
	assert(partName ~= nil, 'p.item(frame): itemName is nil')
	
	return p._item(itemName, partName, relicTier)
end

--- Returns the relics in which the item is dropped from.
--  @function		p._item
--  @param			{string} itemName Item name, case sensitive
--  @param			{string} partName Item part, case sensitive
--  @param[opt]		{string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
--  @return			{string} Wikitext of resultant list
function p._item(itemName, partName, relicTier, tooltip)
	local relics = {}
	local vaultedRelics = {}
	
	if tooltip == nil then
		tooltip = true
	end
	
	if not PrimeData[itemName] or not PrimeData[itemName]['Parts'][partName] then
		error('p._item(itemName, partName, relicTier): Item "' .. itemName .. '" not found.')
	end
	
	for relicName, rarity in Table.skpairs(PrimeData[itemName]['Parts'][partName]['Drops']) do
		local relic = RelicData[relicName]
		if (relicTier == nil or relic['Tier'] == relicTier) then
			local relicString = rarity
			if tooltip then
				relicString = Tooltips.full(relicName, 'Void', relic)..' '..relicString
			else
				relicString = '[['..relicName..']] '..relicString
			end
			if (relic['Vaulted'] ~= nil) then
				relicString = relicString..' ([[Prime Vault|V]])'
				table.insert(vaultedRelics, relicString)
			else
				if (relic['IsBaro']) then
					relicString = relicString..' ([[Baro Ki\'Teer|B]])'
				end
				table.insert(relics, relicString)
			end
		end
	end
	
	-- Putting vaulted relics at the end of list
	for _, relicName in ipairs(vaultedRelics) do
		table.insert(relics, relicName)
	end
	
	return table.concat(relics, '<br />')
end

---	Gets the total number of relics in the game.
--	@function		p.getRelicTotal
--	@param			{table} frame Frame object
--	@param[opt]		{string} frame.args Options for what relic types to include are
--							 "unvaulted", "vaulted", "baro", or nil for all relics
--	@return			{number} The total count of all relics
function p.getRelicTotal(frame)
	if (frame.args[1] == nil) then
		return Table.size(RelicData)
	end
	
	local total = 0
	local countUnvaulted = Table.contains(frame.args, 'unvaulted')
	local countVaulted = Table.contains(frame.args, 'vaulted')
	local countBaro = Table.contains(frame.args, 'baro')
	
	for _, relic in pairs(RelicData) do
		if (countUnvaulted and relic['Vaulted'] == nil) then
			total = total + 1
		end
		if (countVaulted and relic['Vaulted'] ~= nil) then
			total = total + 1
		end
		if (countBaro and relic['IsBaro']) then
			total = total + 1
		end
	end
	return total
end

---	Gets the ducat value of a Prime part or blueprint.
--	@function		p.getDucatValue
--	@param			{string} itemName Prime item name
--	@param			{string} partName Part name
--	@return			{number} The ducat value of that Prime part/blueprint
function p.getDucatValue(frame)
	local itemName = frame.args ~= nil and frame.args[1] or nil
	local partName = frame.args ~= nil and frame.args[2] or nil
	
	assert(itemName ~= nil, 'p.getDucatValue(): Item name missing')
	assert(partName ~= nil, 'p.getDucatValue(): Part name missing')
	return p._getDucatValue(itemName, partName)
end

---	Gets the ducat value of a Prime part or blueprint.
--	@function		p._getDucatValue
--	@param			{string} itemName Prime item name
--	@param			{string} partName Part name
--	@return			{number} The ducat value of that Prime part/blueprint
function p._getDucatValue(itemName, partName)
	assert(PrimeData[itemName] ~= nil, 'p._getDucatValue(itemName, partName): item "'..itemName..'" does not exist in [[Module:Void/data]]')
	assert(PrimeData[itemName]['Parts'][partName] ~= nil, 'p._getDucatValue(itemName, partName): part "'..partName..'" of item "'..itemName..'" does not exist in [[Module:Void/data]]')
	
	return PrimeData[itemName]['Parts'][partName]['DucatValue']
end

---	Gets the total ducat value of all Prime parts and blueprints.
--	@function		p.getTotalDucats
--	@param[opt]		{string} relicTier Tier name if want to filter by a specific relic tier; default nil for all relic tiers
--	@return			{number} The total ducat value
function p.getTotalDucats(frame)
	local relicTier = frame.args ~= nil and frame.args[1] or nil

	-- TODO: The following wouldn't work if filtering by certain relic tiers
	-- though I'm sure there is a way to optimize these calculations without looping through
	-- all of a data table
	-- local totalItemCount = Table.size(RelicData) * 6 -- counting all items, including duplicates
	-- number of Forma BP drops; note some relics have Forma take up two slots (e.g. only Meso D1)
	-- local numFormaDrops = Table.size(data['FORMA']['BLUEPRINT']) + 1
	
	local totalItemCount = 0	-- counting all items, including duplicates and non-prime items (e.g. Forma)
	local withoutFormaCount = 0	-- counting all items excluding forma
	local totalDucats = 0	-- all, including duplicates, itemDucats
	local weightedDucats = 0	-- all, including duplicates, itemDucats, * rarity weight for radiant relic
	local availableDucats = 0	-- total ducats for items from available relics
	local availableItems = 0	-- available items
	local availableItemsNoForma = 0	-- available items excluding forma
	local vaultedDucats = 0	-- total ducats for items from vaulted relics
	local vaultedItems = 0	-- vaulted items
	local vaultedItemsNoForma = 0	-- vaulted items excluding forma
	
	-- TODO: Refactor so it won't be triple for loop, though this may be hard
	-- to do b/c of the way the tables in /data are formatted; the best we can
	-- do is probably add some optimizations?
	for itemName, itemTable in pairs(PrimeData) do
		for partName, relicTable in pairs(itemTable['Parts']) do
			for relicName, rarity in pairs(relicTable['Drops']) do
				local relic = RelicData[relicName]
				if relicTier == nil or relic['Tier'] == relicTier then
					if relic['Vaulted'] then
						vaultedItems = vaultedItems + 1
					else
						availableItems = availableItems + 1
					end
					
					totalItemCount = totalItemCount + 1
					
					if itemName ~= 'FORMA' then
						local tempDucat = p._getDucatValue(itemName, partName)
						totalDucats = totalDucats + tempDucat
						weightedDucats = weightedDucats + tempDucat * p.RADIANT[rarity]*6
						withoutFormaCount = withoutFormaCount + 1
						
						if relic['Vaulted'] then
							vaultedDucats = vaultedDucats + tempDucat
							vaultedItemsNoForma = vaultedItemsNoForma + 1
						else
							availableDucats = availableDucats + tempDucat
							availableItemsNoForma = availableItemsNoForma + 1
						end
					end
				end
			end
		end
	end
	
	-- TODO: Average ducat value is not a helpful statistic due to the different
	-- drop rates of parts. A better statistic would be average ducat value per 
	-- <relic_tier> relic which would account for item part drop rarity weights. 
	-- This is a more complicated problem since you would
	-- have to account for different refinement levels since the rarity weights change.
	local ducatsIcon = Tooltips.icon('Orokin Ducats', 'Resources')
	if relicTier then
		return string.format([[
'''Average Ducats Value'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
'''Weighted Average Ducats Value'''&#58; %s'''%s''' (for Radiant relics)<br />
'''Available'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
'''Vaulted'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />]], 
			ducatsIcon,
			Math.round(totalDucats / totalItemCount, 0.01),
			Math.formatnum(totalItemCount),
			Math.formatnum(withoutFormaCount),
			ducatsIcon,
			Math.round(weightedDucats / totalItemCount, 0.01),
			ducatsIcon,
			Math.formatnum(availableDucats),
			Math.formatnum(availableItems),
			Math.formatnum(availableItemsNoForma),
			ducatsIcon,
			Math.formatnum(vaultedDucats),
			Math.formatnum(vaultedItems),
			Math.formatnum(vaultedItemsNoForma)
		)
	else
		return string.format([[
'''Total Ducats Value'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
'''Weighted Average Ducats Value'''&#58; %s'''%s''' (for Radiant relics)<br />
'''Available'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />
'''Vaulted'''&#58; %s'''%s''' (%s rewards with %s Prime parts)<br />]], 
			ducatsIcon,
			Math.formatnum(totalDucats),
			Math.formatnum(totalItemCount),
			Math.formatnum(withoutFormaCount),
			ducatsIcon,
			Math.round(weightedDucats / totalItemCount, 0.01),
			ducatsIcon,
			Math.formatnum(availableDucats),
			Math.formatnum(availableItems),
			Math.formatnum(availableItemsNoForma),
			ducatsIcon,
			Math.formatnum(vaultedDucats),
			Math.formatnum(vaultedItems),
			Math.formatnum(vaultedItemsNoForma)
		)
	end
end

---	Returns table row for wikitable generated by p.ducatRelicList().
--	@function		ducatValueRow
--	@param			{string} itemName Item name, case sensitive
--	@param			{string} partName Item part, case sensitive
--	@param[opt]		{string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
--	@return			{string} Wikitext of wikitable row
local function ducatValueRow(itemName, partName, relicTier)
	local ducatValue = p._getDucatValue(itemName, partName)
	local sortValue = ''
	
	--assert(itemName ~= nil, 'ducatValueRow(itemName, partName, relicTier): Please enter an item name')
	--assert(partName ~= nil, 'ducatValueRow(itemName, partName, relicTier): Please enter a part name')
	
	-- First cell; blueprints will appear first before other parts of the same item
	if partName == 'Blueprint' then
		sortValue = itemName..' _'..partName
	else
		sortValue = itemName..' '..partName
	end

	return string.format([[
| data-sort-value="%s"| %s
| %s
| '''%d'''
|-
]],
	sortValue,
	itemName..' '..partName,
	p._item(itemName, partName, relicTier, false),
	ducatValue
)
end

---	Builds a table of all Prime parts, the relics they are in, and their ducat value
--	as seen in [[Ducats/Prices/Lith]].
--	@function		p.ducatRelicList
--	@param[opt]		{string} relicTier Tier name if want to filter by a specific relic tier; default nil for all tiers
--	@param[opt]		{string} listMode If 'Vaulted' displays only vaulted items; if 'Unvaulted' displays only unvaulted items; 
--							 if 'All' or nil, displays all items; default nil
--	@param[opt]		{string} skipForma If anything, Forma entries are skipped.
--	@param[opt]		{string} skipRequiem If anything, Requiem relics are skipped.
--	@return			{string} Wikitext of table
function p.ducatRelicList(frame)
	local relicTier = frame.args ~= nil and frame.args['relicTier'] or nil
	-- Adding switch to choose only vaulted or unvaulted or all items to show
	local listMode = frame.args ~= nil and frame.args['listMode'] or 'ALL'
	listMode = String.titleCase(listMode)
	-- Adding a switch to skip Forma and Requiem relic entries.
	local skipForma = frame.args ~= nil and frame.args['skipForma'] or nil
	local skipRequiem = frame.args ~= nil and frame.args['skipRequiem'] or nil
	
	-- For preventing more than a single entry per item in "itemSet".
	local itemsDone = {}
	
	local itemSet = {}
	-- Note that newline at the end of table header needed so that first column 
	-- will properly show
	local result = { [[
<div style="overflow-y:auto; max-height:600px">
{| style="width:100%;" class="listtable sortable" align="center"
|-
! Part
! Drop Location(s)
! data-sort-type="number" |]] }
	table.insert(result, Tooltips.icon('Orokin Ducats', 'Resources'))
	table.insert(result, [[Ducat Value
|-
]])
	-- Collecting data of all items, filtering based on relic tier
	for itemName, itemTable in Table.skpairs(PrimeData) do
		 if ( itemName ~= "Forma" or skipForma == nil ) then
			for partName, relicTable in Table.skpairs(itemTable['Parts']) do
				for relicName, dropRarity in pairs(relicTable['Drops']) do
					if itemsDone[itemName..partName] ~= true then
						local relicEntry = RelicData[relicName]
						if ( relicEntry['Tier'] ~= "Requiem" or skipRequiem == nil ) then
							if relicEntry['Tier'] == relicTier or relicTier == nil then
								if listMode == 'Vaulted' then
									if relicEntry['IsBaro'] or relicEntry['Vaulted'] then
										itemSet[{ itemName, partName }] = true
										itemsDone[itemName..partName] = true
									end
								elseif listMode == 'Unvaulted' then
									if not relicEntry['IsBaro'] and not relicEntry['Vaulted'] then
										itemSet[{ itemName, partName }] = true
										itemsDone[itemName..partName] = true
									end
								else
									itemSet[{ itemName, partName }] = true
									itemsDone[itemName..partName] = true
								end
							end
						end
					end
				end
			end
		end
	end
	-- Note: Sorting doesn't appear to work on this table.
	table.sort(itemSet)
	
	for itemTable, _ in pairs(itemSet) do
		-- Building actual table row
		table.insert(result, ducatValueRow(itemTable[1], itemTable[2], relicTier))
	end
	
	table.insert(result, '|}</div><br />([[Prime Vault|V]]) Denotes [[Prime Vault|Vaulted]] Void Relics<br />([[Baro Ki\'Teer|B]]) Denotes [[Baro Ki\'Teer]] Exclusive Void Relics')
	return frame:preprocess(table.concat(result))
end

---	Builds a wikitable for all prime parts. Used in [[Void Relic/ByRewards/SimpleTable]].
--	@function		p.simpleRewardTable
--	@param			{table} frame Frame object
--	@return			{string} Wikitext of resultant wikitable
function p.simpleRewardTable(frame)
	local primePartTable = {}
 
	-- TODO: Replace this loop with a call to PrimeData?
	-- Collect data for each relic
	for relicName, relic in pairs(RelicData) do
		local vault = "No"
		if (relic.Vaulted ~= nil) then
			vault = "Yes"
		end
		
		-- For each relic, need each drop
		for i, drop in pairs(relic['Drops']) do
			-- Custom objects are great
			local thisObj = { Item = drop['Item'], Part = drop['Part'],
							RelicName = relicName, 
							Rarity = drop['Rarity'], Vaulted = vault }
			table.insert(primePartTable, thisObj)
		end
	end

	-- Used for sorting prime parts in ascending order alphabetically with
	-- unvaulted status being first
	local function relicComparator(relic1, relic2)
		if(relic1['Item'] == relic2['Item']) then
			if(relic1['Part'] == relic2['Part']) then
				-- Vaulted is a string "Yes" or "No" as defined in for loop prior to this function
				return relic1['Vaulted'] < relic2['Vaulted']
			else
				return relic1['Part'] < relic2['Part']
			end
		else
			return relic1['Item'] < relic2['Item']
		end
	end
			
	table.sort(primePartTable, relicComparator)
 
	local rewards = {}
	local resultTable = { [[
{| class="article-table sortable" style="width: 600px;margin: auto;" cellspacing="1" cellpadding="1" border="2"
|-'
! scope="col"|Item
! scope="col"|Part
! scope="col"|Relic Name
! scope="col"|Rarity
! scope="col"|Relic Vaulted?]] }

	for i, primePart in pairs(primePartTable) do
		local rowStr = string.format([=[
|-
| [[%s]] || %s || %s || %s || %s]=], 
			primePart['Item'],
			primePart['Part'],
			Tooltips.full(primePart['RelicName'], 'Void', RelicData[primePart['RelicName']]),
			primePart['Rarity'],
			primePart['Vaulted'])
		table.insert(resultTable, rowStr)
	end
	table.insert(resultTable, '|}')
	return table.concat(resultTable, '\n')
end

---	Finds all related relics and auto-generates a wikitable.
--	@function		p.relicsTable
--	@param			{table} frame Frame object
--					nil or 'unvaulted' - all unvaulted relics
--					'vaulted' - all vaulted relics
--					'baro' - all Baro-exclusive relics
--	@return			{string} Wikitext of wikitable
function p.relicsTable(frame)
	local filter = frame.args ~= nil and frame.args[1]
	if filter == nil then filter = 'unvaulted' end
	
	local data = {}
	-- Putting all relic names of relics of a certain tier in a single data table entry
	for _, relic in pairs(RelicData) do
		if false
			or filter=='unvaulted' and not relic['Vaulted'] and not relic['IsBaro']-- is the baro check necessary?
			or filter=='vaulted' and relic['Vaulted']
			or filter=='baro' and relic['IsBaro']
			then
			local tier = relic['Tier']
			data[tier] = data[tier] or {}
			table.insert(data[tier], relic)
		end
	end
	for _,t in pairs(data) do
		table.sort(t,function(a,b)
			return a.Name<b.Name
		end)
	end
	
	local resultTable = {
		'{| class="article-table" cellspacing="1" cellpadding="1" border="2" style="margin:auto"',
		('|+ %s Relics'):format(({
			unvaulted = 'Unvaulted/Available',
			vaulted = 'Vaulted/Unavailable',
			baro = 'Baro Ki\'Teer Exclusive',
		})[filter] or 'Available'),
		'|-'
	}
	-- Loop through each tier and add a header
	for _, tier in ipairs(p.RELIC_TIER_ORDER) do
		table.insert(resultTable,
			('! scope="col" style="width:20%%;text-align:center;" | %s<br />(%s relics)')
			:format(tier, Table.size(data[tier] or {}))
		)
	end
	table.insert(resultTable,'|-')

	-- Loop through each tier and add all its matching relics
	for _, tier in ipairs(p.RELIC_TIER_ORDER) do
		table.insert(resultTable, '| ')
		for _, relic in ipairs(data[tier] or {}) do
			table.insert(resultTable, '*'..Tooltips.text(relic.Name,'Void', relic)..'<br />')
		end
	end
	table.insert(resultTable, '|}')

	return frame:preprocess(table.concat(resultTable, '\n'))
end

---	Builds a wikitable of each prime part and the relics it can be dropped from.
--	@function		p.byReward
--	@param			{table} frame Frame object
--	@return			{string} Wikitext of table
function p.byReward(frame)
	local resultTable = { [[
{| class="bigmodtable" style="line-height:16px; font-size:14px;"
|-
! Item Name !! Part Name !! Relics]] }
	
	for item, partTable in Table.skpairs(PrimeData) do
		table.insert(resultTable, '|-')
		-- First column with item name and image; first column will span a number of rows
		-- that depend on how many prime parts are associated with item
		local imageName = WeaponData[item] and WeaponData[item].Image or 
				WarframeData[item] and WarframeData[item]['Image'] or 
				CompanionData[item] and CompanionData[item]['Image'] or
				ResourceData[item] and ResourceData[item]['Image'] or
				'Panel.png'
		
		local itemColumn = string.format('| rowspan="%d"|[[File:%s|300px]]<br/>[[%s]]', 
			Table.size(partTable['Parts']), imageName, item)
		table.insert(resultTable, itemColumn)
		
		local firstRow = true
		-- Getting relics for each prime part of an item
		for part, drops in Table.skpairs(partTable['Parts']) do
			if (firstRow) then
				firstRow = false
			else
				table.insert(resultTable, '|-')
			end
			
			local partName = p._getPartName(part)
			
			-- Second column with part name
			table.insert(resultTable, '| '..partName..' || ')
			
			local relics = {}
			local vaultedRelics = {}
			for relicName, rarity in pairs(drops['Drops']) do
				local relic = RelicData[relicName]
				local relicString = Tooltips.full(relicName, 'Void', relic).." "..rarity
				
				if (relic['Vaulted'] ~= nil) then
					relicString = relicString..' ([[Prime Vault|V]])'
					table.insert(vaultedRelics, relicString)
				else
					if (relic['IsBaro']) then
						relicString = relicString..' ([[Baro Ki\'Teer|B]])'
					end
					table.insert(relics, relicString)
				end
			end
			-- Third column with list of relics that drops a prime part;
			-- unvaulted relics will appear at the top and vaulted relics at the bottom
			for _, relicName in ipairs(vaultedRelics) do
				table.insert(relics, relicName)
			end
			table.insert(resultTable, table.concat(relics, '<br />'))
		end
	end
	table.insert(resultTable, '|}')
	
	return table.concat(resultTable, '\n')
end

---	Builds a wikitable of each relic's drop tables.
--	@function		p.byRelic
--	@param			{table} frame Frame object
--	@return			{string} Wikitext of table
function p.byRelic(frame)
	local tableStr = { [[
{| class="article-table sortable"
! Tier
! Name
! Exclusivity
! Common Rewards
! Uncommon Rewards
! Rare Rewards]] }

	local commonStr, uncommonStr, rareStr
	local itemName, partName, itemLink
 
	-- Building each table row
	for relicName, relicTable in pairs(RelicData) do
		-- First, new row indicator
		table.insert(tableStr, "|-")
		-- Tier & relic names
		table.insert(tableStr, ("| "..relicTable['Tier']))
		table.insert(tableStr, ("| [["..relicName.."|"..relicTable['Name'].."]]"))
		
		if (relicTable.isBaro) then
			table.insert(tableStr, ("| [[Baro Ki'Teer|Baro]]"))
		elseif (relicTable.Vaulted) then
			table.insert(tableStr, ("| [[Prime Vault|Vaulted]]"))
		else
			table.insert(tableStr, ("| &ndash;"))
		end
 
		commonStr = { "| " }
		uncommonStr = { "| " }
		rareStr = { "| " }
		for i, drop in ipairs(relicTable['Drops']) do
			itemName = drop['Item']
			partName = drop['Part']
			itemLink = string.format('* [[%s|%s %s]]', itemName, itemName, partName)
			if (drop['Rarity'] == 'Common') then
				table.insert(commonStr, itemLink)
			elseif (drop['Rarity'] == 'Uncommon') then
				table.insert(uncommonStr, itemLink)
			else
				table.insert(rareStr, itemLink)
			end
		end
		
		table.insert(tableStr, table.concat(commonStr, '\n'))
		table.insert(tableStr, table.concat(uncommonStr, '\n'))
		table.insert(tableStr, table.concat(rareStr, '\n'))
	end
	table.insert(tableStr, "|}")
	return table.concat(tableStr, '\n')
end

---	Builds a wikitable of each relic's Ducat Values.
--	@function		p.byDucats
--	@param			{table} frame Frame object
--	@return			{string} Wikitext of table
function p.byDucats(frame)
	local tableStr = { [[
{| class="article-table sortable"
! Name
! Exclusivity
! Total Ducats
! Intact Average
! Exceptional Average
! Flawless Average
! Radiant Average]] }

	local totalStr, intactStr, exceptionalStr, flawlessStr, radiantStr
	local value, total, intact, exceptional, flawless, radiant = 0, 0, 0, 0, 0, 0
 
	-- Building each table row
	for relicName, relicTable in pairs(RelicData) do
		table.insert(tableStr, "|-")
		table.insert(tableStr, ("| [["..relicName.."|"..relicTable['Name'].."]]"))
		
		if (relicTable.isBaro) then
			table.insert(tableStr, ("| [[Baro Ki'Teer|Baro]]"))
		elseif (relicTable.Vaulted) then
			table.insert(tableStr, ("| [[Prime Vault|Vaulted]]"))
		else
			table.insert(tableStr, ("| &ndash;"))
		end
 
		totalStr = { "| " }
		intactStr = { "| " }
		exceptionalStr = { "| " }
		flawlessStr = { "| " }
		radiantStr = { "| " }
		value, total, intact, exceptional, flawless, radiant = 0, 0, 0, 0, 0, 0
		
		for i, drop in ipairs(relicTable['Drops']) do
			value = PrimeData[drop['Item']].Parts[drop['Part']].DucatValue
			
			total = total + value
			if (drop['Rarity'] == 'Common') then
				intact = intact + 0.2533 * value
				exceptional = exceptional + 0.2333 * value
				flawless = flawless + 0.2 * value
				radiant = radiant + 0.1667 * value
			elseif (drop['Rarity'] == 'Uncommon') then
				intact = intact + 0.11 * value
				exceptional = exceptional + 0.13 * value
				flawless = flawless + 0.17 * value
				radiant = radiant + 0.2 * value
			else
				intact = intact + 0.02 * value
				exceptional = exceptional + 0.04 * value
				flawless = flawless + 0.06 * value
				radiant = radiant + 0.1 * value
			end
		end
		
		table.insert(totalStr, total)
		table.insert(intactStr, intact)
		table.insert(exceptionalStr, exceptional)
		table.insert(flawlessStr, flawless)
		table.insert(radiantStr, radiant)
		
		table.insert(tableStr, table.concat(totalStr, '\n'))
		table.insert(tableStr, table.concat(intactStr, '\n'))
		table.insert(tableStr, table.concat(exceptionalStr, '\n'))
		table.insert(tableStr, table.concat(flawlessStr, '\n'))
		table.insert(tableStr, table.concat(radiantStr, '\n'))
	end
	
	table.insert(tableStr, "|}")
	return table.concat(tableStr, '\n')
end

---	Builds a wikitable of all relic drops sorted by rarity.
--	@function		p.byRarity
--	@param			{table} frame Frame object
--					{string} checkTier Relic tier to filter by; default 'All' for all tiers
--	@return			{string} Wikitext of table
function p.byRarity(frame)
	local checkTier = frame.args ~= nil and frame.args[1] or 'All'

	local rarityOrder = { 'Common', 'Uncommon', 'Rare' }
	local rarityData = { Common = {}, Uncommon = {}, Rare = {} }
 
	-- Collect data
	for relicName, relic in pairs(RelicData) do
		local tier = relic['Tier']
		local name = relic['Name']
		-- So first make sure this row is the right tier
		if (tier == checkTier or tier == 'All') then
			for _, drop in pairs(relic['Drops']) do
				local rarity = drop['Rarity']
				local item = drop['Item']
				local part = drop['Part']
				
				-- Using '|' as a delimiter to split by
				if (rarityData[rarity][item..'|'..part] == nil) then
					-- Assigning a dummy value that will not be used so that keys
					-- can be in an unsorted set
					rarityData[rarity][item..'|'..part] = true
				end
			end
		end
	end
 
	-- Now we can actually format the table
	-- Starting with all the column headers
	local resultTable = { string.format([[
{| class="article-table"
! style="width:33%%;text-align:center;" | Common (%d rewards)
! style="width:33%%;text-align:center;" | Uncommon (%d rewards)
! style="width:33%%;text-align:center;" | Rare (%d rewards)
|-
|]], 
	Table.size(rarityData['Common']),
	Table.size(rarityData['Uncommon']),
	Table.size(rarityData['Rare'])
) }

	-- Then go through each rarity
	-- Each rarity is the same steps:
	for _, rarity in ipairs(rarityOrder) do
		--1. Loop through each item in that rarity in ascending order
		for itemStr, _ in Table.skpairs(rarityData[rarity]) do
			--2. Get the item & part
			local delimiterIndex = string.find(itemStr, '|')
			local itemName = string.sub(itemStr, 1, delimiterIndex - 1)
			local partName = p._getPartName(string.sub(itemStr, delimiterIndex + 1))
			--3. Add the appropriate link to the table
			table.insert(resultTable, string.format('* [[%s|%s %s]]', itemName, itemName, partName))
		end
		table.insert(resultTable, '|')
	end
	table.insert(resultTable, '|}')
	return table.concat(resultTable, '\n')
end

---	Builds wikitable of Void Relics in which a Prime item's parts drop from as
--	seen on any Prime item page (e.g. [[Braton Prime]]).
--	@function		p.buildPrimeTable
--	@param			{table} frame Frame object
--	@returns		{string} Resultant wikitext of wikitable
function p.buildPrimeTable(frame)
	local itemName = String.titleCase(mw.text.decode(frame.args['name'] or ''))
	assert(PrimeData[itemName] ~= nil, 'p.buildPrimeTable(frame): "'..itemName..'" does not exist in [[Module:Void/data]]')
	local n = 1;
	local partData = PrimeData[itemName]['Parts']
	local result = { ([=[
<div style="text-align:center;"><i>Lith, Meso, Neo, and Axi refer to [[Void Relic]]s&nbsp;&nbsp;|&nbsp;&nbsp;([[Prime Vault|V]]) Denotes [[Prime Vault|Vaulted]] Void Relics&nbsp;&nbsp;|&nbsp;&nbsp;([[Baro Ki'Teer|B]]) Denotes [[Baro Ki'Teer]] Exclusive Void Relic</i></div>
<div style=" overflow-x:auto;">
{| style="width: 100%%; table-layout: fixed; margin-right:0px;" class="article-table" cellspacing="1" cellpadding="1" border="0" align="left"
|+ %s's Relic Drops
|-]=]):format(itemName) }

	local blueprint = ' Blueprint';
	for partName, _ in pairs(partData) do
    	-- Use Mission for items like Forma else use Part for items that 
    	-- are crafted from multiple parts that rewarded from drop table(s)
    	local icon = '';
		local part = partName;
    	if part ~= nil and part ~= '' then
			part = mw.text.decode(part); -- Replacing HTML codes in text with their ASCII character equivalent
			part = string.gsub(" "..string.lower( part ), "%W%l", string.upper):sub(2);
			
			if part == 'Blueprint' or part == 'Main Blueprint' then
				local source = 'Warframes';
				local index = TooltipsIcon[source](itemName);
				if not index then
					source = 'Companions';
					index = TooltipsIcon[source](itemName);
				end
				if not index then
					source = 'Weapons';
					index = TooltipsIcon[source](itemName);
				end
				if index then
					icon = Tooltips.icon(itemName, source, index).."&nbsp;";
				end
			else
				if string.sub(part, -#blueprint) == blueprint then
					part = string.sub(part, 1, #part - #blueprint);
				end
				
				if itemName == "Odonata Prime" then
					if IconData["Items"]["Archwing "..part..blueprint] ~= nil then
						icon = "Archwing "..part..blueprint
					end
				elseif IconData["Items"]["Prime "..part] ~= nil then
					icon = "Prime "..part
				elseif IconData["Items"]["Prime "..part..blueprint] ~= nil then
					icon = "Prime "..part..blueprint
				elseif IconData["Items"][part] ~= nil then
					icon = part
				end
				if icon ~= '' then
					icon = Icon._Item(icon, nil, nil)..'&nbsp;';
				end
			end
		end
			
		table.insert(result, ('! style="text-align:center;" | %s'):format(icon..partName))
	end
	table.insert(result, '|-')
	for partName, _ in pairs(partData) do
		table.insert(result, ('| style="text-align:center; font-size:12px;" | %s'):format(p._item(itemName, partName, nil)))
	end
	
	table.insert(result, '|}</div>')
	return table.concat(result, '\n')
end

---	Builds wikitable of Void Relics in which Forma blueprints drop from as
--	seen on [[Forma]].
--	@function		p.buildFormaBPRelicTable
--	@returns		{string} Resultant wikitext of wikitable
function p.buildFormaBPRelicTable()
	return ([=[{| style="width: 100%%;" class="article-table" cellspacing="1" cellpadding="1" border="0" align="left"
|-
! style="text-align:center; width: 25%%" | Lith
! style="text-align:center; width: 25%%" | Meso
! style="text-align:center; width: 25%%" | Neo
! style="text-align:center; width: 25%%" | Axi
|-
| style="text-align:center; padding: 6px 0px" | %s
| style="text-align:center; padding: 6px 0px" | %s
| style="text-align:center; padding: 6px 0px" | %s
| style="text-align:center; padding: 6px 0px" | %s
|-
|}
<div style="text-align: center;"><i>Lith, Meso, Neo, and Axi refer to [[Void Relic]]s&nbsp;&nbsp;|&nbsp;&nbsp;([[Prime Vault|V]]) Denotes [[Prime Vault|Vaulted]] Void Relics&nbsp;&nbsp;|&nbsp;&nbsp;([[Baro Ki'Teer|B]]) Denotes [[Baro Ki'Teer]] Exclusive Void Relic</i></div>]=]):format(
	p._item('Forma', 'Blueprint', 'Lith'),
	p._item('Forma', 'Blueprint', 'Meso'),
	p._item('Forma', 'Blueprint', 'Neo'),
	p._item('Forma', 'Blueprint', 'Axi')
)
end

---	Returns the unvaulted relic count for a Prime part.
--	@function		p._getUnvaultedRelicCountForPart
--	@param			{string} itemName Item name, case sensitive
--	@param			{string} partName Item part, case sensitive
--	@param[opt]		{string} relicTier Relic tier to search through; if nil looks for item in all relic tiers
--	@return			{number} Unvaulted relic count, 0 if the part is vaulted
function p._getUnvaultedRelicCountForPart(itemName, partName, relicTier)
	local result = 0

	if not PrimeData[itemName] or not PrimeData[itemName]['Parts'][partName] then
		error('p._item(itemName, partName, relicTier): Item "' .. itemName .. '" not found.')
	end
	
	for relicName, rarity in Table.skpairs(PrimeData[itemName]['Parts'][partName]['Drops']) do
		local relic = RelicData[relicName]
		if (relicTier == nil or relic['Tier'] == relicTier) then
			if not relic['Vaulted'] and not relic['IsBaro'] then
				result = result + 1
			end
		end
	end
	
	return result
end

---	Returns the number of unvaulted relics for a Prime set.
--	@function		p._getUnvaultedRelicCountForSet
--	@param			{string} itemName Item name
--	@return			{number} Unvaulted relic count, 0 if the set is vaulted, 
--							 -1 if item is not obtainable from Void Relics (e.g. base variant weapons)
function p._getUnvaultedRelicCountForSet(itemName)
	local partData = PrimeData[itemName] and PrimeData[itemName]['Parts'] or {}
	local result = PrimeData[itemName] and 0 or -1

	for partName, _ in pairs(partData) do
		result = result + p._getUnvaultedRelicCountForPart(itemName, partName, nil)
	end
	
	return result
end

---	Returns the number of unvaulted relics for a Prime set.
--	@function		p.getUnvaultedRelicCountForSet
--	@param			{table} frame Frame object
--	@return			{number} Unvaulted relic count, 0 if the set is vaulted, 
--							 -1 if item is not obtainable from Void Relics (e.g. base variant weapons)
function p.getUnvaultedRelicCountForSet(frame)
	local itemName = String.titleCase(mw.text.decode(frame.args['name'] or ''))
	return p._getUnvaultedRelicCountForSet(itemName)
end

return p