Lua error at line 78: attempt to perform arithmetic on field '?' (a string value). Lua error at line 78: attempt to perform arithmetic on field '?' (a string value). Lua error at line 236: attempt to call field 'getFullTooltip' (a nil value). Lua error at line 223: attempt to call field 'getFullTooltip' (a nil value).
--- '''DropTables''' stores drop table data from the official WARFRAME drop table repository.<br />
--
-- On this Wiki, DropTables is used in:
-- * [[Template:RelicTable]]
-- * [[Template:DropLocations]]
--
-- @module droptables
-- @alias p
-- @author [[User:Falterfire|Falterfire]]
-- @attribution [[User:Croquemorttime|Croquemorttime]]
-- @attribution [[User:Flaicher|Flaicher]]
-- @attribution [[User:FINNER|FINNER]]
-- @image SampleMissionDropTable.png
-- @require [[Module:DropTables/data]]
-- @require [[Module:Missions/data]]
-- @require [[Module:String]]
-- @require [[Module:Icon]]
-- @require [[Module:Math]]
-- @require [[Module:Table]]
-- @require [[Module:Void]]
-- @release stable
-- <nowiki>
--Rewritten version of Module:DropTables to work with new format of data.
--NOTE: I'm currently in the process of copying over a new data format
-- It should be easier to keep up to date, but things may be a bit screwy over the next hour or two
-- Please do not revert this update without checking with me.
-- (For the fastest response, ping me on the Wiki discord)
-- User:Falterfire, 1/6/18
-- TODO: Create a new p._getMissionList(dropTableAlias) function
-- that returns a table instead of a wikitext string (for module use)
-- TODO: Update all drop chances in M:DropTables/data to be in decimal form
-- instead of being a percentage. Formatting of drop chances should be done in modules
-- not when they are stored as data. Doing so removes the need of dividing drop chances by 100
-- in here before passing into Math.percentage() for formatting.
--For reference:
-- in DropData["Missions"].Rewards
local MISSION_NAME_COL = 1 -- Name of the drop
local MISSION_TYPE_COL = 2 -- Type of thing dropped (IE Mod, Endo, Credits)
local MISSION_CHANCE_COL = 3 -- Chance for the thing to drop
local MISSION_COUNT_COL = 4 -- Number of things dropped. If empty, default to 1
-- in DropData["Enemies"].Mods
local MOD_NAME_COL = 1 -- Name of the mod
local MOD_CHANCE_COL = 2 -- The chance of a mod dropping
local MOD_COUNT_COL = 3 -- If empty, default to 1. Normally only different for Endo
local p = {}
local DropData = mw.loadData('Module:DropTables/data')
local RelicData = mw.loadData('Module:Void/data')['RelicData']
local MissionData = mw.loadData('Module:Missions/data')
local ModsData = mw.loadData('Module:Mods/data')
local Icon = require('Module:Icon')
local String = require('Module:String')
local Math = require('Module:Math')
local Table = require('Module:Table')
local Void = require('Module:Void')
local Tooltip = require('Module:Tooltips')
--- Looks through a drop table and appends drop source to list if drop table
-- contains specified item.
-- @function addDropFromDropTable
-- @param {string} itemName Item name to be searched
-- @param {table} dropTable Enemy drop table entry as as seen in <code>/data</code>
-- @param {string} subTableName Name of drop table w/o "s" (e.g. "Resource" or "Sigil")
-- @param {table} dropSourceList List of drop sources of item in the format:
-- { Name = drop table name, Link = article link, Chance = drop chance, Count = Number of drops }
local function addDropFromDropTable(itemName, dropTable, subTableName, dropSourceList)
for _, drop in ipairs(dropTable[subTableName..'s'] or {}) do
if (drop[1] == itemName) then
table.insert(dropSourceList, {
Name = dropTable.Name,
Link = dropTable.Link or dropTable.Name, -- TODO: Use name as fallback until M:Enemies/data is populated, afterwards remove the fallback
Chance = dropTable[subTableName..'Chance'] * drop[2] / 100,
Count = drop[3] -- Do not add a fallback value like in the case of some region resources with varying drop amounts
} )
-- Breaking early despite there being the possibility that there can be two same entries in the same drop table
break
end
end
end
--- Gets mission drop table by Alias.
-- @function p._getMission
-- @param {string} missionAlias Alias key value as seen in <code>/data</code>
-- @return {table} Mission drop table entry from <code>/data</code>
function p._getMission(missionAlias)
assert(missionAlias ~= nil, 'p._getMission(missionAlias): missionAlias cannot be a nil value')
return DropData["Missions"][missionAlias] or error('p._getMission(missionAlias): "'..missionAlias..'" does not exist in [[Module:DropTables/data]]')
end
--- Basically pretending to be semi-object oriented
-- Calling this whenever I'm pulling drops from enemies and passing them around
-- NOTE: As of writing, this assumes enemies don't have Blueprint or other drops listed.
-- TODO: buildEnemyDrop(), buildMissionDrop(), buildSyndicateDrop(), buildResourceDrop(),
-- and etc. can probably be in a single buildDrop function that passes in a table/function
-- argument telling how to format drops
-- @function buildEnemyDrop
local function buildEnemyDrop(Enemy, Mod)
local drop = { }
drop.EnemyName = Enemy.Name
drop.ItemName = Mod[MOD_NAME_COL]
drop.Chance = (Enemy.ModChance * Mod[MOD_CHANCE_COL]) / 100
drop.Count = Mod[MOD_COUNT_COL] ~= nil and Mod[MOD_COUNT_COL] or 1
if (drop.ItemName == 'Endo') then drop.Type = 'Endo' else drop.Type = 'Mod' end
return drop
end
--- Returns a table of a single drop from a mission's drop table.
-- @function buildMissionDrop
-- @param {table} missionDropTable Mission drop table entry as seen in <code>/data</code>
-- @param {string} rotation Rotation name (e.g. "A", "B", or "C")
-- @param {table} itemTable A single item drop entry within a drop table (e.g. { "Endo", 25, 15 })
-- @return {table} A table containing a single item from a mission drop table
local function buildMissionDrop(missionDropTable, rotation, itemTable)
local drop = { }
drop.MissionType = missionDropTable.Type
drop.MissionTier = missionDropTable.Alias
drop.Rotation = rotation
drop.ItemName = itemTable[MISSION_NAME_COL]
drop.Chance = itemTable[MISSION_CHANCE_COL]
drop.Count = itemTable[MISSION_COUNT_COL] ~= nil and itemTable[MISSION_COUNT_COL] or 1
drop.Type = itemTable[MISSION_TYPE_COL]
-- Just go ahead and tag this on because who knows what we'll need from it
drop.Mission = missionDropTable
return drop
end
--- Like above, but for Syndicate Offerings
-- @function buildSyndicateDrop
local function buildSyndicateDrop(theSyndicate, Item)
local drop = { }
drop.SyndicateName = theSyndicate.Name
drop.ItemName = Item[SYNDICATE_NAME_COL]
drop.Type = Item[SYNDICATE_TYPE_COL]
drop.Cost = Item[SYNDICATE_COST_COL]
drop.Rank = Item[SYNDICATE_RANK_COL]
return drop
end
local function linkEnemy(enemyName)
if not enemyName then
return "No name specified."
end
-- Cut off enemy names before parentheses while linking
local paren = string.find(enemyName, "%(")
local result = ""
if (paren ~= nil) then
result = "[["..string.sub(enemyName, 1, paren - 2).."|"..enemyName.."]]"
elseif (enemyName == "Fissure Corrupted Enemy") then
result = "[[Void Fissure|"..enemyName.."]]"
elseif (enemyName == "Dargyn" or enemyName == "Carrier") then
result = "[["..enemyName.." (Enemy)".."|"..enemyName.."]]"
else
result = "[["..enemyName.."]]"
end
return result
end
--- Custom table sort for reward tables
-- WIP, initial rules:
-- Sort first by type, then alphabetically within type, then by quantity
-- WIP try sorting first by drop chance...
-- TODO: Finish this function
-- @function rewardTableSort
local function rewardTableSort(theTable)
local function sorter(r1, r2)
if (r1.Chance == r2.Chance) then
if (r1.Type == r2.Type) then
if (r1.ItemName == r2.ItemName) then
return r1.Count < r2.Count
else
return r1.ItemName < r2.ItemName
end
else
return r1.Type < r2.Type
end
else
return r1.Chance > r2.Chance
end
end
table.sort(theTable, sorter)
end
--- Custom table sort for Enemy tables
-- Rules:
-- Sort first by Drop Chance, then alphabetically within Drop Chance with Endo being last
-- @function enemyTableSort
local function enemyTableSort(theTable)
local function sorter(r1, r2)
if (r1.Chance == r2.Chance) then
if (r1.Count == r2.Count) then
return r1.ItemName < r2.ItemName
else
return r1.Count < r2.Count
end
else
return r1.Chance > r2.Chance
end
end
table.sort(theTable, sorter)
end
--- Formats a string of text for a reward table
-- (NOTE: ALWAYS USES TWO COLUMNS)
-- Format is
-- [Icon] [Quantity] [Item Name with Link] || [Drop Chance]]
-- With some slight variation based on drop type
-- Variation is mostly helpful for getting the right icon
-- TODO: Nested if/else code blocks can probably be formatted as a map
-- @function formatDropString
local function formatDropString(drop)
local result = ""
local dropType = drop.Type
local iconText = ""
if (dropType == "Resource") then
iconText = Tooltip.getFullTooltip(drop.ItemName, "Resources")
elseif (dropType == "Arcane") then
iconText = Tooltip.getFullTooltip(drop.ItemName, "Arcane")
elseif (dropType == "Scene") then
--iconText = Icon._Item("Scene", nil, nil)
result = result.."[[Captura|"..drop.ItemName.."]]"
elseif (dropType == "Endo") then
iconText = Icon._Item("Endo", nil, nil)
result = result.."[[Endo]]"
elseif (dropType == "Ayatan Sculpture") then
--iconText = Icon._Item(drop.ItemName)
result = "[[Ayatan Sculpture|"..drop.ItemName.."]]"
elseif (dropType == "Mod") then
iconText = Tooltip.getFullTooltip(drop.ItemName, "Mods")
elseif (dropType == "Relic") then
local relicName = drop.ItemName
-- Mimicing frame object in order to replace displayed text with "<relic_name> (Radiant)"
local frame = { args = { string.gsub(relicName, " %(Radiant%)", ""), "Void", r = relicName } }
iconText = Tooltip.getFullTooltip(frame)
elseif (dropType == "Credits") then
iconText = Icon._Item("Credits", nil, nil)
result = result.."[[Credit Cache]]"
elseif (dropType == "Blueprint") then
local pieces = String.split(drop.ItemName, "%s")
local BPType = pieces[2]
local BPName = pieces[1]
local linkString = String.split(drop.ItemName, "%s")[1]
--Change the link for Eidolon Lenses from Eidolon to the correct page
if linkString == 'Eidolon' then
linkString = 'Focus Lens#Eidolon Lenses'
end
if (BPName == "Vidar" or BPName == "Lavan" or BPName == "Zetki") then
iconText = Icon._Item("Blueprint", nil, nil)
linkString='Railjack/Components'
elseif (BPType == "Cerebrum") then
iconText = Icon._Item("Neuroptics", nil, nil) -- see below
elseif (BPType == "Carapace") then
iconText = Icon._Item("Chassis", nil, nil) -- fix for nautilus icons
elseif (BPName == "Forma") then
iconText = Icon._Item("Forma", nil, nil)
elseif (BPName == "Miter" and BPType == "Chassis") then
--a workaround for displaying proper icons for Miter parts
iconText = Icon._Item("Stock", nil, nil)
elseif (BPName == "Miter" and BPType == "Handle") then
--because Miter has oddly named parts
iconText = Icon._Item("Receiver", nil, nil)
elseif (BPName == "Shedu" and BPType == "Chassis") then
--a workaround for displaying proper icons for Shedu parts
iconText = Icon._Item("Stock", nil, nil)
elseif (BPType == "Systems") then
iconText = Icon._Item("Systems", nil, nil)
elseif (BPType == "Chassis") then
iconText = Icon._Item("Chassis", nil, nil)
elseif (BPType == "Neuroptics") then
iconText = Icon._Item("Neuroptics", nil, nil)
elseif (BPType == "Fuselage") then
iconText = Icon._Item("Fuselage", nil, nil)
elseif (BPType == "Engines") then
iconText = Icon._Item("Engines", nil, nil)
elseif (BPType == "Avionics") then
iconText = Icon._Item("Avionics", nil, nil)
elseif (BPType == "Barrel") then
iconText = Icon._Item("Barrel", nil, nil)
elseif (BPType == "Stock") then
iconText = Icon._Item("Stock", nil, nil)
elseif (BPType == "Receiver") then
iconText = Icon._Item("Receiver", nil, nil)
elseif (BPType == "Blade") then
iconText = Icon._Item("Blade", nil, nil)
elseif (pieces[2] == "Wraith" or pieces[2] == "Vandal" or pieces[1] == "Carmine") then
--Now a workaround for Wraith & Vandal things to link them properly. U29.10 > now works with carmine penta
--In theory works for any Wraith/Vandal item
linkString = pieces[1].." "..pieces[2]
if(pieces[3] ~= "Blueprint") then
if (BPName == "Spectra" and pieces[Table.size(pieces)] == "Chassis") then
--a workaround for displaying proper icons for Spectra parts
iconText = Icon._Item("Stock", nil, nil)
elseif (BPName == "Spectra" and pieces[Table.size(pieces)] == "Handle") then
--because Spectra has oddly named parts
iconText = Icon._Item("Receiver", nil, nil)
else
iconText = Icon._Item(pieces[Table.size(pieces)], nil, nil)
end
else
iconText = Icon._Item("Blueprint", nil, nil)
end
elseif (BPName=="Ancient" or BPName=="Charger" or BPName=="Clem") then
iconText = Icon._Item("Blueprint", nil, nil)
linkString = "Specter"
elseif (pieces[3] == "Blueprint") then
--a workaround for Eidolon Lens BP or Twin Gremlins BP to link proper pages
--should work for other 3 part blueprint names as well
linkString = pieces[1].." "..pieces[2]
iconText = Icon._Item("Blueprint", nil, nil)
elseif (pieces[1] == "Equinox") then
--a workaround for Equinox's 4 piece names
if (pieces[3] == "Systems") then
iconText = Icon._Item("Systems", nil, nil)
elseif (pieces[3] == "Chassis") then
iconText = Icon._Item("Chassis", nil, nil)
elseif (pieces[3] == "Neuroptics") then
iconText = Icon._Item("Neuroptics", nil, nil)
else
iconText = Icon._Item("Blueprint", nil, nil)
end
elseif (pieces[3] == "Ephemera") then
iconText = Icon._Item("Blueprint", nil, nil)
linkString = "Ephemera"
elseif (BPName == "Arum") then
-- workarounf for arum spinosa
linkString = pieces[1].." "..pieces[2]
if (pieces[3]=="Blueprint") then
iconText = Icon._Item("Blueprint", nil, nil) -- this one is superfluous
elseif (pieces[3]=="Guard") then
iconText = Icon._Item("Pouch", nil, nil)
elseif (pieces[3]=="Rivet") then
iconText = Icon._Item("Link", nil, nil)
end
else
iconText = Icon._Item("Blueprint", nil, nil)
end
result = result.."[["..linkString.."|"..drop.ItemName.."]]"
elseif (dropType == "Fragments") then
-- iconText = Icon._Item("Mutate", nil, nil)
result = result.."[[Fragments|"..drop.ItemName.."]]"
elseif (dropType == "Item") then
if (string.find(drop.ItemName,"Sigil")~=nil) then
--iconText = Icon._Item(drop.ItemName)
result = result.."[[Sigils|"..drop.ItemName.."]]"
else
iconText = Icon._Item(drop.ItemName)
result = result.."[["..drop.ItemName.."]]"
end
else
result = result..drop.ItemName
end
if (drop.Count > 1) then
result = "x"..drop.Count.." "..result
end
result = iconText.." "..result.." || "..drop.Chance.."%"
return result
end
--- Returns a table of all rewards for a given mission, split by rotation.
-- @function getRewardsForMission
-- @param {table} mission Mission table entry as seen in <code>/data</code>
-- @return {table} List of formatted mission rewards
local function getRewardsForMission(mission)
local result = {}
if (mission.Rewards ~= nil) then
for key, dropTable in Table.skpairs(mission.Rewards) do
local temp = {}
for i, drop in pairs(dropTable) do
table.insert(temp, buildMissionDrop(mission, key, drop))
end
rewardTableSort(temp)
result[key] = temp
end
end
return result
end
--- Gets the list of missions that give rewards for a specific Alias (e.g. "Defense1").
-- @function p._getMissionTable
-- @param {string} missionAlias
-- @return {table} List of mission nodes with the specified drop table
function p._getMissionTable(missionAlias)
local data = {}
for _, m in Table.skpairs(MissionData["MissionDetails"]) do
if (m.DropTableAlias == missionAlias) then
table.insert(data, m)
end
end
return data
end
--- Gets a list of missions with rewards for a given planet.
-- @function p.getMissionsForPlanet
-- @param {string} planetName Name of planet
-- @return {table} List of mission node table entries as seen in M:Missions/data
function p.getMissionsForPlanet(planet)
local missions = {}
for _, m in pairs(MissionData["MissionDetails"]) do
if (m.Planet == planet and m.DropTableAlias == 'Landscape') then
for _, n in pairs(DropData["Missions"]) do
-- TODO: Could replace table constructor with a Table.clone()
local mData = { Node = m.Node, Planet = m.Planet, Type = m.Type, IsDarkSector = m.IsDarkSector, Tileset = m.Tileset, Enemy = m.Enemy, MinLevel = m.MinLevel, MaxLevel = m.MaxLevel, DropTableAlias = m.DropTableAlias, Pic = m.Pic }
-- TODO: Combine both of these if statements
if (planet == 'Earth') and (n.Type == 'Cetus Bounty' or n.Type == 'Cetus Bounty (Steel Path)' or n.Type == 'Ghoul Bounty') and (n.Ignore ~= true) then
mData.Type = n.Type; mData.DropTableAlias = n.DropTableAlias; mData.MinLevel = n.Name
elseif (planet == 'Venus') and (n.Type == 'Orb Vallis Bounty' or n.Type == 'Orb Vallis Bounty (Steel Path)') and (n.Ignore ~= true) then
mData.Type = n.Type; mData.DropTableAlias = n.DropTableAlias; mData.MinLevel = n.Name
end
mData.DropTableAlias = n.Alias
table.insert(missions, mData)
end
elseif (m.Planet == planet and m.DropTableAlias ~= nil) then
table.insert(missions, m)
end
end
return missions
end
--- Returns the rewards for the A tier only for a mission
-- Handy for missions like Capture that have a single reward
-- Returns as rows for a table with two columns
-- See the existing Capture rewards section for an example
-- Used on [[Template:Rewards]]
-- @function p.getSingleRotationRewards
-- @param {table} frame Frame object
-- @return {string} Resultant wikitext of a single row in a wikitable
function p.getSingleRotationRewards(frame)
local missionAlias = frame.args ~= nil and frame.args[1]
local result = ""
local mission = p._getMission(missionAlias)
local data = getRewardsForMission(mission)
if (data ~= nil and Table.size(data) > 0) then
local firstKey = nil
for k in pairs(data) do
if (firstKey == nil) then
firstKey = k
end
end
for i, drop in pairs(data[firstKey]) do
result = result.."\n|-\n| "..formatDropString(drop)
end
end
return result
end
--- Returns the rewards for a given mission/tier
-- Returns as rows for a table with six columns, two for each rotation
-- See existing Survival/Rewards/Normal_Mission for examples
-- if Tier==AllTier it will call a specific function to merge all tiers together in a single A, B, C table
-- Used on [[Template:Rewards]]
-- @function p.getRewardTable
-- @param {table} frame Frame object
-- @return {string} Resultant wikitext of wikitable
function p.getRewardTable(frame)
local missionAlias = frame.args ~= nil and frame.args[1]
local mission = p._getMission(missionAlias) or
error('p.getRewardTable(frame): Could find reward table alias "'..missionAlias..'" in [[Module:DropTables/data]]')
local data = getRewardsForMission(mission)
local dropTableA = data["A"] or error('p.getRewardTable(frame): Must have a drop table mapped to "A" key')
local dropTableB = data["B"] or {}
local dropTableC = data["C"] or {}
local result = {}
assert(Table.size(dropTableA) > 0, 'p.getRewardTable(frame): Drop table A cannot be an empty table')
-- TODO: Depreciate p.getSingleRotationRewards(frame)? This code branch is basically the same
-- as that function
-- Building a reward table with a single drop table
if (Table.size(dropTableB) == 0 and Table.size(dropTableC) == 0) then
table.insert(result, [=[
{|class="article-table" style="width:40%; text-align:right; margin:auto" border="0"
! colspan="2" style="text-align:center;" | Rewards
]=])
for _, drop in pairs(dropTableA) do
table.insert(result, "|-\n| "..formatDropString(drop))
end
-- Building a reward table with three drop tables (A, B, C)
else
table.insert(result, [=[{| class="article-table" style="width:100%" border="0"
|-
! colspan="2" style="width:33%; text-align:center;" | [[Mission Rewards|A]]
! colspan="2" style="width:34%; text-align:center;" | [[Mission Rewards|B]]
! colspan="2" style="width:33%; text-align:center;" | [[Mission Rewards|C]]
]=])
--Goes through all three rotations to find which one has the most items
local maxLen = Table.size(dropTableA)
local dropTableBItemCount = Table.size(dropTableB)
if (dropTableBItemCount > maxLen) then
maxLen = dropTableBItemCount
end
local dropTableCItemCount = Table.size(dropTableC)
if (dropTableCItemCount > maxLen) then
maxLen = dropTableCItemCount
end
-- We need as many rows as the longest list has items
-- So if any lists are shorter then after their last row the columns are just blank
for i = 1, maxLen, 1 do
table.insert(result, "|-")
table.insert(result, (dropTableA[i] ~= nil) and '| align="right" | '..formatDropString(dropTableA[i]) or "| || ")
table.insert(result, (dropTableB[i] ~= nil) and '| align="right" | '..formatDropString(dropTableB[i]) or "| || ")
table.insert(result, (dropTableC[i] ~= nil) and '| align="right" | '..formatDropString(dropTableC[i]) or "| || ")
end
end
table.insert(result, "|}")
return table.concat(result, "\n")
end
--- Gets a list of all the missions for a given Alias. Used on [[Template:Rewards]] and p.getSingleRelicByLocation()
-- @function p.getMissionList
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant list
function p.getMissionList(frame)
local missionAlias = frame.args ~= nil and frame.args[1]
local result = {}
local missionRecord = p._getMission(missionAlias)
local missions = p._getMissionTable(missionRecord['Alias'])
for _, m in pairs(missions) do
table.insert(result, "* "..m.Node..", [["..m.Planet.."]]")
end
return table.concat(result,'\n')
end
--- Get a list of all missions that drop a given item.
-- @function getDropMissions
-- @param {string} itemName Name of item
-- @return {table} List of tables each containing details of mission and drop chances
local function getDropMissions(itemName)
local dropList = {}
--For each mission...
for _, mission in pairs(DropData["Missions"]) do
--... if it has rewards...
if (mission.Rewards ~= nil and not mission.Ignore) then
--... then for each rotation in the mission...
for key, dropTable in Table.skpairs(mission.Rewards) do
--... for each drop in the rotation...
for _, drop in pairs(dropTable) do
-- ... if the drop is the right item, add it to the list
if (drop[MISSION_NAME_COL] == itemName) then
table.insert(dropList, buildMissionDrop(mission, key, drop))
end
end
end
end
end
return dropList
end
--- Returns an EnemyDrop object for each enemy that drops a given item.
-- @function getDropEnemies
-- @param {string} itemName Name of item
-- @return {table} List of tables each containing details of enemy and drop chances
local function getDropEnemies(itemName)
local dropList = {}
for _, enemy in pairs(DropData.Enemies) do
if not(enemy.Ignore) then
addDropFromDropTable(itemName, enemy, 'Mod', dropList)
addDropFromDropTable(itemName, enemy, 'Resource', dropList)
addDropFromDropTable(itemName, enemy, 'Item', dropList)
addDropFromDropTable(itemName, enemy, 'Sigil', dropList)
end
end
return dropList
end
--- Returns a wikitable of all mission types and the possible Void Relics they can drop.
-- Used on [[Void Relic/ByMission]]
-- Unlike p.getRewardTable(), this is just the full table with all formatting
-- This is pretty ugly, but kinda have to do it this way
-- (Unless you have a better solution, in which case by all means go ahead and fix it)
-- (I'm not exactly a Lua expert or a UI expert)
-- TODO: Break up this function into smaller functions
-- @function p.getRelicTable
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant wikitable
function p.getRelicTable(frame)
-- Okay, so first up, need to know which planet this is for
local planetName = frame.args ~= nil and frame.args[1] or error('p.getRelicTable(frame): First frame argument must be a Star Chart planet/region name')
-- planetName == nil is standing in for 'all planets', so adding option to explicitly call 'all'
if (planetName ~= nil and (planetName == "" or planetName == "All")) then
planetName = nil
end
-- I have other functions to get the list of missions for all/planet
-- So calling that here
local missions = nil
if (planetName == nil) then
missions = {}
for _, m in pairs(DropData["Missions"]) do
if (not m.Ignore) then
table.insert(missions, m)
end
end
else
missions = p.getMissionsForPlanet(planetName)
end
local tableRows = {}
local Relics = { ["Lith"] = {}, ["Meso"] = {}, ["Neo"] = {}, ["Axi"] = {}, ["Requiem"] = {} }
-- Now for the 'fun' part: Getting the list
for i, m in ipairs(missions) do
-- For each mission, the first thing we're doing is setting up what it's called
-- Or more accurately, what it appears as in the chart
local rowName = ""
local mission = nil
if (planetName == nil) then
rowName = "[["..m.Link.."|"..m.Type.."]] ("..m.Name..")"
mission = m
else
local placeName = m.Node
-- When showing a single planet, format is instead "Mission Name (Type)"
-- For example, "Rusalka (Capture)"
-- Mission type is still linked
-- Dark Sector is also linked if appropriate
if (m.IsDarkSector) then
rowName = placeName.." ([[Dark Sector]] [["..m.Type.."]])"
else
local mType = m.Type
mType = string.gsub(mType, "Cetus Bounty %(Steel Path%)", "Steel Path Cetus Bounty")
mType = string.gsub(mType, "Orb Vallis Bounty %(Steel Path%)", "Steel Path Orb Vallis Bounty")
rowName = placeName.." ([["..m.Type.."]])"
end
mission = p._getMission(m.DropTableAlias)
end
local thisRow = nil
-- This is where we get all the rewards for the mission
local drops = getRewardsForMission(mission)
-- Need to know if this is a single rotation
-- Because if it is, just a checkmark instead of a letter
local isSingleRot = Table.size(drops) == 1
-- For each mission, looping each rotation
for rot, dropTable in Table.skpairs(drops) do
-- And each drop for each rotation
for j, d in pairs(dropTable) do
-- We only care if it's a relic
if (d.Type == "Relic") then
-- Set up the row if we don't have it yet
-- Mission will not be added to the grid unless it drops at least one relic
-- Avoids adding a row for something like Assassination that never gives relics
if (thisRow == nil) then
thisRow = {}
end
-- Example: "Lith A1"
-- Also extracting names of Radiant relics if string contains " (Radiant)"
local RelicText, isRadiantRelic = string.gsub(d.ItemName, "%s%(Radiant%)", "")
isRadiantRelic = isRadiantRelic > 0
-- Example: {"Lith", "A1"}
local RelicBits = String.split(RelicText, "%s")
-- Example: "Lith"
local RTier = RelicBits[1]
-- Example: "A1"
local RName = RelicBits[2]
-- Make sure the relevant entry exists
if (thisRow[RelicText] == nil) then
thisRow[RelicText] = ""
end
-- And then fill it in
-- Adding checkmark to cells where there is no rotation reward A/B/C
-- and adding asterisk for Radiant relic drops
thisRow[RelicText] = ((isSingleRot) and "✔" or thisRow[RelicText]..rot)..((isRadiantRelic) and "*" or "")
-- Adding drop rate info when hover over rotation letter or checkmark:
-- If the drop rate is under 5%, set text color to red with "'"
-- If the drop rate is under 10%, set text color to orange with "^"
local relicTextColor = "inherit"
local icon = ''
if (d.Chance < 5) then
relicTextColor = "red"
icon = "'"
elseif (d.Chance < 10) then
relicTextColor = "orange"
icon = "^"
end
thisRow[RelicText] = ('<b><span style="color:%s;" title="Drop rate: %s%%">%s%s</span></b>')
:format(relicTextColor, d.Chance, thisRow[RelicText], icon)
-- Also gotta add the Relic to our list if we don't have it yet
if (Relics[RTier][RName] == nil) then
Relics[RTier][RName] = RelicText
end
end
end
end
if (thisRow ~= nil) then
tableRows[rowName] = thisRow
end
end
local result = {}
local headerRow = {}
local headerFirst = true
-- So this right here sets up the initial conditions of the table
-- If you want to change the styling, you've gotta do it here
result = { '{| class="wikitable" style="width:100%; border=1px; text-align:center; font-size:11px;"\n|-' }
-- Slightly different text for all missions VS missions for a planet
if (planetName == nil) then
table.insert(result, '\n! rowspan="2" | Mission Type (Tier)')
else
table.insert(result, '\n! rowspan="2" | Node (Type)')
end
-- Looping through each Relic tier
-- Doing two things here:
-- 1. Setting up the header row with the list of relics
-- 2. Setting up the topmost row that has the name of each relic tier
for _, tier in ipairs(Void.RELIC_TIER_ORDER) do
local relicCount = Table.size(Relics[tier])
if (relicCount > 0) then
table.insert(result, ('\n! colspan="%s" | %s'):format(relicCount, tier))
for rNum, trash in Table.skpairs(Relics[tier]) do
if (not headerFirst) then
table.insert(headerRow, " || ")
end
headerFirst = false
table.insert(headerRow, Tooltip.getFullTooltip(tier.." "..rNum, "Void"))
end
end
end
-- Then add the second row to the list
local headerTemp = table.concat(headerRow)
table.insert(result, "\n|-\n|"..headerTemp)
-- And now, at long last, it's time to add all the good stuff
for mName, relicRow in Table.skpairs(tableRows) do
table.insert(result, "\n|-\n|"..mName)
for _, tier in ipairs(Void.RELIC_TIER_ORDER) do
for rNum, rName in Table.skpairs(Relics[tier]) do
if(relicRow[rName] ~= nil) then
table.insert(result, ("||"..relicRow[rName]))
else
table.insert(result, "|| ")
end
end
end
end
table.insert(result, "\n|}")
-- And then ship it all back
return frame:preprocess(table.concat(result))
end
--- Returns a wikitable of each Void Relic's mission drop locations.
-- Used on [[Void Relic/DropLocationsByRelic]]
-- TODO: Break up this function into smaller functions
-- @function p.getRelicByLocation
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant wikitable
function p.getRelicByLocation(frame)
local tier = frame.args ~= nil and frame.args[1] or frame
local tierPieces = String.split(tier, "%s")
local relicData = {}
-- TODO: Refactor so there is at max 3 nested code blocks
--As with most of my functions, breaking this into two parts:
--First, gather all the data for each relic by going through missions
--We're looking through all drops for all missions to find relic drops
for _, mission in pairs(DropData["Missions"]) do
if (mission.Rewards ~= nil and mission.Ignore ~= true) then
for rot, dropTable in Table.skpairs(mission.Rewards) do
for _, drop in pairs(dropTable) do
--When we find a relic drop, make sure it's for the right tier
if (drop[MISSION_TYPE_COL] == "Relic") then
--Example: {"Lith", "A1"}
local RelicBits = String.split(drop[MISSION_NAME_COL], "%s")
--Example: "Lith"
local RTier = RelicBits[1]
--Example: "A1"
local RName = RelicBits[2]
--Then if it is for the right tier, it needs to be added to our table of data
if (RTier == tier) then
--Create an entry for this relic if we don't have one yet
if (relicData[RName] == nil) then
relicData[RName] = { Drops = {}, Rewards = RelicData[RTier..' '..RName]['Drops'] }
end
--Then add this drop to the relic's table
table.insert(relicData[RName].Drops, buildMissionDrop(mission, rot, drop))
end
end
end
end
end
end
--Second, build the actual table being sent back
local result = { [[
{| class="article-table" border="0" cellpadding="1" cellspacing="1" style="width: 100%;"
! Relic Name
! Drop locations]] }
local rHeader = [[
{| cellpadding="2" cellspacing="0" class="sortable" style="width:100%;border:1px solid black; text-align:right;font-size:12px;"
! Mission Type
! Source
! Rotation
! Chance
]]
for RName, RTable in Table.skpairs(relicData) do
local tierLink = "\n|-\n| ".."[["..tier.." "..RName.."]]"
table.insert(result, tierLink)
for i, reward in pairs(RTable.Rewards) do
local ItemName = reward.Item
local PartName = reward.Part
local itemLink = "\n* [["..ItemName.."|"..ItemName.." "..PartName.."]]"
table.insert(result, itemLink)
end
table.insert(result, "\n|\n")
table.insert(result, rHeader)
table.sort(RTable.Drops, function (d1, d2)
if (d1.MissionType == d2.MissionType) then
if (d1.Mission['Name'] == d2.Mission['Name']) then
return d1.Rotation < d2.Rotation
else
return d1.Mission['Name'] < d2.Mission['Name']
end
else
return d1.MissionType < d2.MissionType
end
end)
for i, d in pairs(RTable.Drops) do
table.insert(result, ([=[
|-
| [[%s]]
| [[%s|%s]]
| %s
| %s%%
]=]):format(d.MissionType, d.Mission['Link'], d.Mission['Name'], d.Rotation, d.Chance))
end
table.insert(result, "\n|}")
end
table.insert(result, "\n|}")
-- TODO: Remove all the newlines in elements that is inserted into table.
-- Can just concat newlines at the end here.
return table.concat(result)
end
--- Returns a wikitable with the mission types that drop a particular Void Relic.
-- TODO: Break up this function into smaller functions
-- @function p.getSingleRelicByLocation
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant wikitable
function p.getSingleRelicByLocation(frame)
-- Assume frame argument is a valid relic name (e.g. "Axi O5")
local relicName = frame.args ~= nil and frame.args['name']
local relicData = { Drops = {}, Rewards = RelicData[relicName]['Drops'] }
local missionData = {}
--As with most of my functions, breaking this into two parts:
--First, gather all the data for each relic by going through missions
--We're looking through all drops for all missions to find relic drops
for _, mission in pairs(DropData["Missions"]) do
if (mission.Rewards ~= nil and mission.Ignore ~= true) then
for rot, dropTable in Table.skpairs(mission.Rewards) do
for _, drop in pairs(dropTable) do
--When relic drop found, make sure it's for the right relic
if (drop[MISSION_TYPE_COL] == "Relic") then
--Then if it is for the right tier, it needs to be added to our table of data
if (drop[MISSION_NAME_COL] == relicName or
string.gsub(drop[MISSION_NAME_COL], "%s%(Radiant%)", "") == relicName) then
--Then add this drop to the relic's table
table.insert(relicData.Drops, buildMissionDrop(mission, rot, drop))
end
end
end
end
end
end
--Second, build the actual table being sent back
local result = { [=[
{| cellpadding="0" cellspacing="0" class="wikitable sortable" style="width:100%; border:1px solid black; text-align:left; font-size:12px; margin:12px 0 0 0;"
|-
! Mission Type
! Source
! [[Star Chart]] Nodes
! [[Mission_Rewards#Mission_Rotations|Rotations]]
! data-sort-type="numeric" | Chances]=] }
table.sort(relicData.Drops, function (d1, d2)
if(d1.MissionType == d2.MissionType) then
if (d1.Mission['Name'] == d2.Mission['Name']) then
return d1.Rotation < d2.Rotation
else
return d1.Mission['Name'] < d2.Mission['Name']
end
else
return d1.MissionType < d2.MissionType
end
end)
local types = {} -- 1st column
local dropTableAlias = {} --
local dropTableNames = {} -- 2nd column
local rotations = {} -- 3rd column
local chances = {} -- 4th column
for i, d in pairs(relicData.Drops) do
types[i] = d.MissionType
dropTableAlias[i] = d.Mission['Alias']
dropTableNames[i] = d.Mission['Name']
rotations[i] = d.Rotation
chances[i] = d.Chance.."%"
end
for i = 1, Table.size(relicData.Drops), 1 do
-- TOOD: I do not understand why this is needed. Duplicate entries? Why not use a set then?
if types[i] == types[i + 1] then
if dropTableAlias[i] == dropTableAlias[i + 1] then
rotations[i + 1] = rotations[i]..", "..rotations[i + 1]
chances[i + 1] = chances[i]..", "..chances[i + 1]
types[i] = ''
dropTableAlias[i] = ''
dropTableNames[i] = ''
rotations[i] = ''
chances[i] = ''
end
end
local num = string.gsub(chances[i], "%%", "") -- Extracting the drop chance from percentage string
local ChancesBits = String.split(num, ", ")
local highchance
-- TODO: All these tonumber() conversions are so inefficient. Is there any way we can get the drop percentage as a number natively?
if (Table.size(ChancesBits) <= 1) or (ChancesBits == nil) then
highchance = tonumber(num)
else
for j = 1, Table.size(ChancesBits) - 1, 1 do
if tonumber(ChancesBits[j]) <= tonumber(ChancesBits[j + 1]) then
highchance = tonumber(ChancesBits[j + 1])
else
highchance = tonumber(ChancesBits[j])
end
end
end
if types[i] ~= '' then
frame.args = { dropTableAlias[i] } -- Reusing frame object passed in to use its functions
local status, missionNodeList = pcall(p.getMissionList, frame)
if (not status) then
error(mw.dumpObject(dropTableAlias))
end
local mType = types[i]
mType = string.gsub(mType, " %(Steel Path%)", "<br />%(Steel Path%)")
-- A quick hack to have the correct link to the mission type, instead of to the mod
if mType == "Rush" then
mType = "Rush (Archwing)|Rush"
elseif mType == "Orphix" then
mType = "Orphix (Mission)|Orphix"
end
table.insert(result, '|-\n| style="padding:10px;" | [['..mType..']]')
table.insert(result, '| style="padding:10px;" | '..dropTableNames[i])
table.insert(result, '| style="padding:10px;" |\n'..missionNodeList)
table.insert(result, '| style="padding:10px;" | '..rotations[i])
table.insert(result, '| style="padding:10px;" data-sort-value="'..highchance..'" | '..chances[i])
end
end
table.insert(result, '|}')
return table.concat(result, '\n')
end
--- Returns a wikitable of missions that drop a particular item.
-- Used in [[Template:DropLocations]]
-- @function p.getItemByMissionTable
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant wikitable
function p.getItemByMissionTable(frame)
local itemName = frame.args ~= nil and frame.args[1] or error('p.getItemByMissionTable(frame): Must pass in an argument, the name of an item')
local missionDropLocations = getDropMissions(itemName)
table.sort(missionDropLocations, function (d1, d2) return d1.MissionType < d2.MissionType end)
local result = { [=[
{| cellpadding="0" cellspacing="0" class="wikitable sortable" style="width:100%; border:1px solid black; text-align:left; font-size:12px; margin:12px 0 0 0;"
|-
! Mission Type
! Source
! [[Star Chart]] Nodes
! [[Mission_Rewards#Mission_Rotations|Rotations]]
! data-sort-type="numeric" | Chances
|-
]=] }
for i, d in pairs(missionDropLocations) do
table.insert(result, ([=[
| [[%s]] || %s
|
%s
| %s || %s%%
|-
]=]):format(d.MissionType, d.Mission['Name'], p.getMissionList({ args = { d.Mission['Alias'] }}), d.Rotation, d.Chance))
-- TODO: Replace p.getMissionList(frame) with p._getMissionList(dropTableAlias) once it is implemented
end
table.insert(result, '|}')
return table.concat(result, '\n')
end
--- Returns a wikitable of enemies that drop a particular item.
-- Used in [[Template:DropLocations]]
-- @function p.getItemByEnemyTable
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant wikitable
function p.getItemByEnemyTable(frame)
local itemName = frame.args ~= nil and frame.args[1] or error('p.getItemByEnemyTable(frame): Must pass in an argument, the name of an item')
local enemyDropLocations = getDropEnemies(itemName)
table.sort(enemyDropLocations, function (d1, d2) return d1.Name < d2.Name end)
local result = { [=[{| cellpadding="2" cellspacing="0" class="wikitable sortable" style="border:1px solid black; font-size:12px;"
! Enemy
! Chance
]=] }
for _, drop in pairs(enemyDropLocations) do
table.insert(result, ([=[
|-
| [[%s|%s]] || %s%%
]=]):format(drop.Link, drop.Name, drop.Chance))
end
table.insert(result, '|}')
return table.concat(result, '\n')
end
--- Returns the list of drop locations used in [[Template:Component]]
-- TODO: Break up this function into smaller functions
-- TODO: Remove this function since infobox submodules use p._buildDropTableSourceStrings()
-- and build the lists in their own submodules (blocked by pages that use T:Component)
-- @function p.getItemDropList
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant list
function p.getItemDropList(frame)
local theDrop = frame.args ~= nil and frame.args[1] or frame
-- First, get all the missions that drop this
local Drops = getDropMissions(theDrop)
table.sort(Drops, function (d1, d2) return d1.MissionType < d2.MissionType end)
local checked = {}
local result = ""
local space = " "
if (Table.size(Drops) > 0) then
local finalTable = {}
result = "'''Missions:'''" -- Voided to add more mods (example Syndicates)
--Going through and grouping the drops by Type
for i, Drop in pairs(Drops) do
local Alias = Drop.Mission['Alias']
local MissionName = Drop.Mission['ShortName']
--This check prevents duplicating rows if the same item appears in multiple rotations of the same mission
if (checked[Alias] == nil) then
checked[Alias] = { Drop }
if (finalTable[Drop.MissionType] == nil) then
finalTable[Drop.MissionType] = {}
end
table.insert(finalTable[Drop.MissionType], Alias)
else
table.insert(checked[Alias], Drop)
end
end
table.sort(finalTable, function (r1, r2) return r1 < r2 end)
--This is where all the items are put into the list
--Each mission type gets its own row, with the relevant tiers in parentheses
--For example "Spy (T1, T2, Lua)" or "Survival (DS1, DS2)"
for mType, item in pairs(finalTable) do
table.sort(item)
result = result.."<br/>"..mType
local tierList = ""
for j, alias in pairs(item) do
local drop1 = checked[alias][1]
local shortName = drop1.Mission['ShortName'] or drop1.Mission['Name']
if shortName ~= "" then
local ttip = drop1.Mission['Name']
for k, thisDrop in pairs(checked[alias]) do
ttip = ttip..'\\nRotation '..thisDrop.Rotation
if (thisDrop.Chance ~= nil) then
ttip = ttip..': '..Math.percentage(thisDrop.Chance / 100)
end
end
if j > 1 then tierList = tierList..', ' end
tierList = tierList..'<span style="border-bottom: 1px dotted;" class="basic-tooltip" title="'..ttip..'">'..shortName..'</span>'
end
end
if tierList ~= "" then result = result.." ("..tierList..")" end
end
end
--Then all the enemies are added to the list if there are any
if (ModsData.Mods[theDrop] ~= nil) then --Skip if not a mod (getDropEnemies only check mods anyway)
Drops = getDropEnemies(theDrop)
if (Table.size(Drops) > 0) then
table.sort(Drops, function (d1, d2) return (d1.EnemyName or "") < (d2.EnemyName or "") end)
if (string.len(result) > 0) then result = result.."<br/>" end
result = result.."'''Enemies:'''"
for i, Drop in pairs(Drops) do
result = result.."<br/>"..linkEnemy(Drop.EnemyName)..space
result = result..Math.percentage(Drop.Chance / 100)
end
end
end
return result
end
--- Returns the number of enemies that drop a particular item.
-- @function p.getItemByEnemyCount
-- @param {table} frame Frame object
-- @return {number} Number of enemies that drop a particular item
function p.getItemByEnemyCount(frame)
local theDrop = frame.args ~= nil and frame.args[1] or frame
local Drops = getDropEnemies(theDrop)
return Table.size(Drops)
end
--- Returns the number of missions that drop a particular item.
-- @function p.getItemByMissionCount
-- @param {table} frame Frame object
-- @return {number} Number of missions that drop a particular item
function p.getItemByMissionCount(frame)
local theDrop = frame.args ~= nil and frame.args[1] or frame
local Drops = getDropMissions(theDrop)
return Table.size(Drops)
end
--- Returns a list of all enemies with a drop table as stored in <code>/data</code>
-- TODO: Remove this function once we populate [[Module:Enemies/data]]
-- @function p.getFullEnemyList
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant list
function p.getFullEnemyList(frame)
local result = "All Enemies: "
local enemyNameList = {}
for _, Enemy in pairs(DropData["Enemies"]) do
if (Enemy.Name ~= nil) then
table.insert(enemyNameList, Enemy.Name)
end
end
table.sort(enemyNameList, function(n1, n2) return n1 < n2 end)
for _, name in pairs(enemyNameList) do
result = result.."\n* "..linkEnemy(name)
end
return result
end
--- Returns a list of mod drops from a particular enemy. Used in [[Template:EnemyHoriz]]
-- @function p.getEnemyModDrops
-- @param {table} frame Frame object
-- @return {string} Wikitext of resultant list
function p.getEnemyModDrops(frame)
local enemyName = frame.args ~= nil and frame.args[1] or frame
-- @function getAllModDrops
local function getAllModDrops(enemyName)
local drops = {}
local Enemy = DropData["Enemies"][enemyName] or {}
for j, Mod in pairs(Enemy.Mods or {}) do
local drop = buildEnemyDrop(Enemy, Mod)
table.insert(drops, drop)
end
return drops
end
local Drops = getAllModDrops(enemyName)
if (Table.size(Drops) == 0) then
return
end
enemyTableSort(Drops)
local space = " "
local result = ""
for i, Drop in pairs(Drops) do
if i > 1 then result = result.."<br/>" end
if (Drop.ItemName == "Endo") then
if (Drop.Count < 10) then
result = result.."[[Endo]]"
else
result = result..Drop.Count.." [[Endo]]"
end
else
result = result..Tooltip.getFullTooltip(Drop.ItemName, "Mods")
end
result = result..space..Drop.Chance.."%"
end
return result
end
--- Builds lists of sources with drop table(s) where an item can be dropped from.
-- Used in infobox builders such as [[Module:Mods/infobox]]
-- @function p._buildDropTableSourceStrings
-- @param {string} name Name of item
-- @return {string} List of mission sources in wikitext
-- @return {string} List of enemy sources in wikitext
-- @return {string} List of container sources in wikitext
function p._buildDropTableSourceStrings(name)
local missions, missionList = {}, {}
local enemies, enemyList = {}, {}
local containers, containerList = {}, {}
for _, dropSourceEntry in ipairs(DropData.Rewards[name] or {}) do
local dropTableEntry = dropSourceEntry.Source
if not(dropTableEntry.Ignore) then
if (dropSourceEntry[1] == "Missions") then
local missionType = dropTableEntry.Type
local missionTier = dropTableEntry.Tier
if not missions[missionType] then
missions[missionType] = {}
end
if not missions[missionType][missionTier] then
missions[missionType][missionTier] = {}
end
table.insert(missions[missionType][missionTier],
{
Rot = dropSourceEntry[3],
Chance = dropSourceEntry[4],
Count = dropSourceEntry[5] and drop[5]..'x ' or ''
} )
else
local temp
if (dropSourceEntry[1] == "Enemies") then
temp = enemies
elseif (dropSourceEntry[1] == "Containers") then
temp = containers
end
table.insert(temp, {
Name = dropSourceEntry[2],
Link = dropSourceEntry.Link or dropSourceEntry[2], -- TODO: Use name as fallback until M:Enemies/data is populated, afterwards remove the fallback
Chance = dropSourceEntry.Probability,
Count = dropSourceEntry[5] -- Do not add a fallback value like in the case of some region resources with varying drop amounts
} )
end
end
end
-- Building missionList
for missionType, tiers in Table.skpairs(missions) do
-- Need to store reward rotation and mission tier data for a particular mission type
local tierList = {}
for tier, rots in Table.skpairs(tiers) do
local rotList = {}
table.sort(rots, function(a, b) return a.Rot < b.Rot end)
for _, v in ipairs(rots) do
table.insert(rotList, v.Count..'Rotation '..v.Rot..': '..Math.percentage(v.Chance / 100))
end
table.insert(tierList, ('<span style="border-bottom: 1px dotted;" class="basic-tooltip" title="%s">%s</span>')
:format(table.concat(rotList, '\\n'), tier:gsub('<br />', ': '):gsub(' ', ' ')))
end
-- TODO: Article link can be fetched from M:Missions/data instead
-- A quick hack to have the correct link to the missiontype, instead of to the mod
if missionType == "Rush" then
table.insert(missionList, '[[Rush (Archwing)|Rush]] ('..table.concat(tierList, ', ')..')')
elseif missionType == "Orphix" then
table.insert(missionList, '[[Orphix (Mission)|Orphix]] ('..table.concat(tierList, ', ')..')')
else
table.insert(missionList, '[['..missionType..']] ('..table.concat(tierList, ', ')..')')
end
end
-- Building enemyList
table.sort(enemies, function(a, b)
if a.Chance * (a.Count or 1) == b.Chance * (b.Count or 1) then
return a.Name < b.Name
end
return a.Chance * (a.Count or 1) > b.Chance * (b.Count or 1)
end)
for _, enemy in ipairs(enemies) do
table.insert(enemyList, (enemy.Count and enemy.Count..'x ' or '')..'[['..enemy.Name..']] '..Math.percentage(enemy.Chance / 100))
end
-- Building containerList
table.sort(containers, function(a, b)
if a.Chance * (a.Count or 1) == b.Chance * (b.Count or 1) then
return a.Name < b.Name
end
return a.Chance * (a.Count or 1) > b.Chance * (b.Count or 1)
end)
for _, container in ipairs(containers) do
table.insert(containerList, '[['..container.Link..'|'..container.Name..']] '..Math.percentage(container.Chance / 100))
end
return table.concat(missionList, '<br />'), table.concat(enemyList, '<br />'), table.concat(containerList, '<br />')
end
return p