DropTables stores drop table data from the official WARFRAME drop table repository.
On this Wiki, DropTables is used in:
Usage
Template
In template: {{#invoke:DropTables|function|input1|input2|...}}
In articles: {{template|function|input1|input2|...}}
Module
local DropTable = require('Module:DropTables')
Documentation
Package items
droptables._getMissionDropTable(missionAlias)
(function)- Gets mission drop table by Alias.
- Parameter:
missionAlias
Alias key value as seen in/data
(string) - Returns: Mission drop table entry from
/data
(table) droptables._getMissionTable(missionDropTableAlias)
(function)- Gets the list of missions that give rewards for a specific Alias (e. g. "Defense1").
- Parameter:
missionDropTableAlias
(string) - Returns: List of mission nodes with the specified drop table (table)
droptables.getMissionsForPlanet(planetName)
(function)- Gets a list of missions with rewards for a given planet.
- Parameter:
planetName
Name of planet (string) - Returns: List of mission node table entries as seen in M:Missions/data (table)
droptables.getRewardTable(frame)
(function)- 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
- Parameter:
frame
Frame object (table) - Returns: Resultant wikitext of wikitable (string)
droptables.getMissionList(frame)
(function)- Gets a list of all the missions for a given Alias. Used on Template:Rewards and p.getSingleRelicByLocation()
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant list (string)
droptables.getRelicTable(frame)
(function)- 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
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
droptables.getRelicByLocation(frame)
(function)- Returns a wikitable of each Void Relic's mission drop locations. Used on Void Relic/DropLocationsByRelic TODO: Break up this function into smaller functions
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
droptables.getSingleRelicByLocation(frame)
(function)- Returns a wikitable with the mission types that drop a particular Void Relic. TODO: Break up this function into smaller functions
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
droptables.getItemByMissionTable(frame)
(function)- Returns a wikitable of missions that drop a particular item. Used in Template:DropLocations
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
droptables.getItemByEnemyTable(frame)
(function)- Returns a wikitable of enemies that drop a particular item. Used in Template:DropLocations
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
droptables.getItemByEnemyCount(frame)
(function)- Returns the number of enemies that drop a particular item.
- Parameter:
frame
Frame object (table) - Returns: Number of enemies that drop a particular item (number)
droptables.getItemByMissionCount(frame)
(function)- Returns the number of missions that drop a particular item.
- Parameter:
frame
Frame object (table) - Returns: Number of missions that drop a particular item (number)
droptables.getFullEnemyList(frame)
(function)- Returns a list of all enemies with a drop table as stored in
/data
TODO: Remove this function once we populate Module:Enemies/data - Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant list (string)
droptables.getEnemyModDrops(frame)
(function)- Returns a list of mod drops from a particular enemy. Used in Template:EnemyHoriz
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant list (string)
- Created with Docbunto
See Also
- DropTables/JSON
- DropTables/JSON/Cetus Bounties
- DropTables/JSON/Fortuna Bounties
- DropTables/JSON/Missions
- DropTables/JSON/Necralisk Bounties
- DropTables/JSON/Relics
- DropTables/JSON/Sorties
- DropTables/JSON/Transient
- DropTables/JSON/doc
- DropTables/JSON/slim
- DropTables/data
- DropTables/data/dev
- DropTables/data/doc
- DropTables/dev
- DropTables/dev/doc
- DropTables/doc
Code
--- '''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:Table]]
-- @require [[Module:Void]]
-- @require [[Module:Math]]
-- @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 NAME_COL = 1 -- Name of the drop
local TYPE_COL = 2 -- Type of thing dropped (IE Mod, Endo, Credits)
local CHANCE_COL = 3-- Chance for the thing to drop -- Chance of item dropping
local COUNT_COL = 4 -- Number of things dropped. If empty, default to 1. Normally only different for Endo
local CHANCE_MULTIPLIER = 1/100-- set to one when the data gets updated
local p = {}
local Delay = require[[Module:Delay]]
local DropData = Delay.mw.loadData[[Module:DropTables/data]]
local VoidData = Delay.mw.loadData[[Module:Void/data]]
local MissionData = Delay.mw.loadData[[Module:Missions/data]]
local Icon = Delay.require[[Module:Icon]] -- ._Item
local String = Delay.require[[Module:String]] -- .split
local Table = Delay.require[[Module:Table]] -- .size, .skpairs
local Void = Delay.require[[Module:Void]] -- .RELIC_TIER_ORDER
local Tooltip = Delay.require[[Module:Tooltips]] -- .full
--- 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,
-- DropTableType = drop table type (e.g. "Mod" or "Resource"),
-- DropTableChance = drop table chance,
-- ItemChance = individual item chance,
-- Chance = calculated drop chance,
-- Count = Item count rewarded on successful roll
-- }
local function addDropFromDropTable(itemName, dropTable, subTableName, dropSourceList)
for _, drop in ipairs(dropTable[subTableName..'s'] or {}) do
if (drop[NAME_COL] == 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
DropTableType = subTableName,
-- Chances are in percentage form not decimals
DropTableChance = dropTable[subTableName..'Chance'],
ItemChance = drop[CHANCE_COL],
Chance = dropTable[subTableName..'Chance'] * drop[CHANCE_COL] * CHANCE_MULTIPLIER,
-- TODO: Somehow represent a wildcard number for region resources that have a range of values
-- as possible drops
-- Do not add a fallback value like in the case of some region resources with varying drop amounts
Count = drop[COUNT_COL] or 1
} )
-- 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._getMissionDropTable
-- @param {string} missionAlias Alias key value as seen in <code>/data</code>
-- @return {table} Mission drop table entry from <code>/data</code>
function p._getMissionDropTable(missionAlias)
assert(missionAlias ~= nil, 'p._getMissionDropTable(missionAlias): missionAlias cannot be a nil value')
return DropData["Missions"][missionAlias] or error('p._getMissionDropTable(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(), 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
-- @param {table} Enemy Enemy drop table entry
-- @param {table} Mod Mod drop entry
local function buildEnemyDrop(Enemy, Mod)
return {
EnemyName = Enemy.Name,
ItemName = Mod[NAME_COL],
Chance = (Enemy.ModChance * Mod[CHANCE_COL]) * CHANCE_MULTIPLIER,
Count = Mod[COUNT_COL] ~= nil and Mod[COUNT_COL] or 1,
Type = Mod[TYPE_COL]
}
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)
return {
MissionType = missionDropTable.Type,
-- Some mission types use a different article name (e.g. Rush gamemode uses "Rush (Archwing)")
-- In the case we decouple article links from M:DropTables/data
-- (e.g. using Tier/Type key to map to article name instead of storing explicit Link key),
-- use MissionData.MissionTypes[missionDropTable.Type].Link
MissionLink = missionDropTable.Link,
MissionTier = missionDropTable.Alias,
Rotation = rotation,
ItemName = itemTable[NAME_COL],
Chance = itemTable[CHANCE_COL],
Count = itemTable[COUNT_COL] ~= nil and itemTable[COUNT_COL] or 1,
Type = itemTable[TYPE_COL],
-- Just go ahead and tag this on because who knows what we'll need from it
Mission = missionDropTable
}
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
return r1.Chance > r2.Chance
end
if r1.Type ~= r2.Type then
return r1.Type < r2.Type
end
if r1.ItemName ~= r2.ItemName then
return r1.ItemName < r2.ItemName
end
return r1.Count < r2.Count
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
return r1.Chance > r2.Chance
end
if r1.Count ~= r2.Count then
return r1.Count < r2.Count
end
return r1.ItemName < r2.ItemName
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.full(drop.ItemName, "Resources")
elseif (dropType == "Arcane") then
iconText = Tooltip.full(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.full(drop.ItemName, "Mods")
elseif (dropType == "Relic") then
local relicName = drop.ItemName
-- Mimicking frame object in order to replace displayed text with "<relic_name> (Radiant)"
iconText = Tooltip.full{ args = { string.gsub(relicName, " %(Radiant%)", ""), "Void", r = relicName } }
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 = pieces[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 (BPType == "Glove") then
iconText = Icon._Item("Glove", nil, nil)
elseif (pieces[3] == "Wraith") then
--A workaround for Twin Vipers Wraith
linkString = pieces[1].." "..pieces[2].." "..pieces[3]
iconText = Icon._Item(pieces[#pieces], 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[#pieces] == "Chassis") then
--a workaround for displaying proper icons for Spectra parts
iconText = Icon._Item("Stock", nil, nil)
elseif (BPName == "Spectra" and pieces[#pieces] == "Handle") then
--because Spectra has oddly named parts
iconText = Icon._Item("Receiver", nil, nil)
else
iconText = Icon._Item(pieces[#pieces], nil, nil)
end
else
iconText = Icon._Item("Blueprint", nil, nil)
end
elseif (BPType == "Handle") then
iconText = Icon._Item("Handle", nil, nil)
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 == "Sigil") then
result = result..Tooltip.full(drop.ItemName, "Sigils")
elseif (dropType == "Item") then
result = result..Tooltip.full(drop.ItemName, "Resources")
else
result = result..drop.ItemName
end
if (drop.Count > 1) then
result = "x"..drop.Count.." "..result
end
result = iconText.." "..result.." || "..(drop.Chance * 100 * CHANCE_MULTIPLIER).."%"
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} missionDropTableAlias
-- @return {table} List of mission nodes with the specified drop table
function p._getMissionTable(missionDropTableAlias)
local data = {}
for _, m in ipairs(MissionData["MissionDetails"]) do
if (m.DropTableAlias == missionDropTableAlias or
m.CacheDropTableAlias == missionDropTableAlias or
m.ExtraDropTableAlias == missionDropTableAlias) then
table.insert(data, m)
end
end
return data
end
-- tODO: Rename this function with an underscore prepended to match convention
--- 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 = { Name = m.Name, 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._IgnoreEntry ~= 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._IgnoreEntry ~= 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 not (m.DropTableAlias == nil and m.CacheDropTableAlias == nil)) then
table.insert(missions, m)
end
end
return missions
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 and frame.args[1]
local mission = p._getMissionDropTable(missionAlias) or
error('p.getRewardTable(frame): Could find reward table alias "'..missionAlias..'" in [[Module:DropTables/data]]')
local data = getRewardsForMission(mission)
assert(data.A, 'p.getRewardTable(frame): Must have a drop table mapped to "A" key')
local result = {}
-- Building a reward table with a single drop table
if not (data.B or {})[1] and not (data.C or {})[1] 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(data.A) 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]]
]=])
-- 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
-- local done = false
for i = 1, math.max(Table.size(data.A), Table.size(data.B or {}), Table.size(data.C or {})) do
-- while not done do
-- if done then error() end
-- done=true
table.insert(result, "|-")
for _, k in ipairs{'A','B','C'} do
-- table.insert(result, (data[k] or {})[i] and '| align="right" | '..formatDropString((data[k] or {})[i]) or "| || ")
if (data[k] or {})[i] then
table.insert(result, '| align="right" | '..formatDropString(data[k][i]))
-- done=false
else
table.insert(result, "| || ")
end
end
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]
return p._getMissionList(missionAlias)
end
function p._getMissionList(missionAlias)
local result = {}
local missionRecord = p._getMissionDropTable(missionAlias)
local missions = p._getMissionTable(missionRecord['Alias'])
-- If we don't find mission nodes associated with a drop table, assume that
-- drop table is associated with gameplay modifiers or unique gameplay objectives
-- within a particular the mission type ("the mission/gamemode within the mission"
-- or "the mission on top of the base mission")
-- E.g. Arbitrations, Sorties, Bounties
if (Table.size(missions) == 0) then
-- M:Missions/data's MissionModifiers collection contains entries with user-contributed
-- LocationNotes for these edge-cases since stuff like Sorties do not have a dedicated Star Chart node
if (missionRecord['Type'] == nil) then
error(mw.dumpObject(missionRecord))
end
table.insert(result, MissionData['MissionModifiers'][missionRecord['Type']]['LocationNote'])
else
for _, m in pairs(missions) do
table.insert(result, '* [['..m.Link..'|'..m.Name..']], [['..m.Planet..']]')
end
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._IgnoreEntry) 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[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._IgnoreEntry) 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._IgnoreEntry) 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
-- TODO: Refactor this loop for the scenario where a relic can appear in multiple drop tables associated with
-- a single node (e.g. end-of-mission reward + resource cache reward + end-of-mission extra reward tables)
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.Name
-- 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
-- Fallback to resource cache drop table if there is no rotation/end-of-mission drop table associated
-- with mission node
mission = p._getMissionDropTable(m.DropTableAlias or m.CacheDropTableAlias)
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 * CHANCE_MULTIPLIER < 0.05) then
relicTextColor = "red"
icon = "'"
elseif (d.Chance * CHANCE_MULTIPLIER < 0.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 * 100 * CHANCE_MULTIPLIER, 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.full(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._IgnoreEntry ~= 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[TYPE_COL] == "Relic") then
--Example: {"Lith", "A1"}
local RelicBits = String.split(drop[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 = VoidData.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 * 100 * CHANCE_MULTIPLIER))
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 = VoidData.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._IgnoreEntry ~= 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[TYPE_COL] == "Relic") then
--Then if it is for the right tier, it needs to be added to our table of data
if (drop[NAME_COL] == relicName or
string.gsub(drop[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
! [[Mission_Rewards#Reward_Rotations|Rotations]]
! data-sort-type="numeric" | Chances
! [[Star Chart]] Nodes]=] }
-- Sort entries by drop chance in descending order
table.sort(relicData.Drops, function (d1, d2)
if d1.Chance ~= d2.Chance then
return d1.Chance > d2.Chance
end
if d1.Mission['Name'] ~= d2.Mission['Name'] then
return d1.Mission['Name'] < d2.Mission['Name']
end
return d1.Rotation < d2.Rotation
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']
chances[i] = (d.Chance * 100 * CHANCE_MULTIPLIER).."%"
rotations[i] = d.Rotation
end
for i = 1, Table.size(relicData.Drops), 1 do
-- TODO: 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%)")
local missionType = mType
if (MissionData.MissionTypes[mType] ~= nil) then
missionType = '[['..MissionData.MissionTypes[mType].Link..'|'..MissionData.MissionTypes[mType].Name..']]'
elseif (MissionData.MissionModifiers[mType] ~= nil) then
missionType = '[['..MissionData.MissionModifiers[mType].Link..'|'..MissionData.MissionModifiers[mType].Name..']]'
end
table.insert(result, '|-\n| style="padding:10px;" | '..missionType)
table.insert(result, '| style="padding:10px;" | '..dropTableNames[i])
table.insert(result, '| style="padding:10px;" | '..rotations[i])
table.insert(result, '| style="padding:10px;" data-sort-value="'..highchance..'" | '..chances[i])
table.insert(result, '| style="padding:10px;" |\n'..missionNodeList)
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 mw.text.decode(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)
-- Forcing horizontal scroll on wikitable content for more consistent scroll behavior especially on mobile
-- I think readers read left to right in order of importance: mission type, rotation, drop chance, and then
-- the mission nodes to go to for that desired farming location
local result = { [=[<div style="overflow-x: scroll">
{| 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
! [[Mission_Rewards#Reward_Rotations|Rotations<br />or<br />Drop Table]]
! data-sort-type="numeric" | Chance<ref>Chance to roll item within drop table</ref>
! data-sort-type="numeric" | Quantity<ref>Amount rewarded on successful roll</ref>
! data-sort-type="numeric" | Avg. per roll<ref>(Quantity × Chance)</ref>
! [[Star Chart]] Nodes
|-
]=] }
for i, d in pairs(missionDropLocations) do
table.insert(result, ([=[
| [[%s|%s]] || %s
| %s
| %s%% || %s || %s
|
%s
|-
]=]):format(
d.MissionLink,
d.MissionType,
d.Mission['Name'],
d.Rotation,
d.Chance * 100 * CHANCE_MULTIPLIER,
d.Count,
d.Count * d.Chance * CHANCE_MULTIPLIER,
p._getMissionList(d.Mission['Alias'])
)
)
-- TODO: Replace p.getMissionList(frame) with p._getMissionList(dropTableAlias) once it is implemented
end
table.insert(result, '|}</div>')
return frame:preprocess(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 mw.text.decode(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 = { [=[<div style="overflow-x: scroll">
{| cellpadding="2" cellspacing="0" class="wikitable sortable" style="width:100%; border:1px solid black; font-size:12px;"
! Enemy
! data-sort-type="numeric" | Drop Table Chance<ref>Chance to roll drop table</ref>
! data-sort-type="numeric" | Item Chance<ref>Chance to roll item within drop table</ref>
! data-sort-type="numeric" | Chance<ref>(Drop Table Chance × Item Chance)</ref>
! data-sort-type="numeric" | Expected Kills<ref>(1 / Chance), see [[WARFRAME Wiki:Expected & Nearly Guaranteed Numbers]] for more details</ref>
! data-sort-type="numeric" | Quantity<ref>Amount rewarded on successful roll</ref>
! data-sort-type="numeric" | Avg. per roll attempt<ref>(Quantity × Chance), average item quantity on a roll attempt (successful or not)</ref>
]=] }
for _, drop in pairs(enemyDropLocations) do
table.insert(result, ([=[
|-
| [[%s|%s]] || %s%% || %s%% || %s%% || %s || %s || %s
]=]):format(
drop.Link,
drop.Name,
drop.DropTableChance * 100 * CHANCE_MULTIPLIER,
drop.ItemChance * 100 * CHANCE_MULTIPLIER,
drop.Chance * 100 * CHANCE_MULTIPLIER,
math.floor(.5 + 1/(drop.Chance * CHANCE_MULTIPLIER)),
drop.Count,
drop.Count * drop.Chance * CHANCE_MULTIPLIER)
)
end
-- TODO: Move average per roll calculation to addDropFromDropTable() and store as a key-value pair
table.insert(result, '|}</div>')
return frame:preprocess(table.concat(result, '\n'))
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 mw.text.decode(frame.args[1]) or frame
-- Item type to module name (e.g. 'Mod' to 'Mods' for Module:Mods)
local itemTypeModuleMap = {
Mod = 'Mods',
Resource = 'Resources',
Arcane = 'Arcane',
Relic = 'Void',
}
-- @function getAllModDrops
local function getAllModDrops(enemyName)
local drops = {}
local Enemy = DropData["Enemies"][enemyName] or {}
for _, 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 result = {}
for i, Drop in pairs(drops) do
table.insert(result,
Tooltip.full(Drop.ItemName, itemTypeModuleMap[Drop.Type])
..' '..
(Drop.Chance * 100 * CHANCE_MULTIPLIER).."%"
)
end
return table.concat(result,"<br/>")
end
return p