Warframe日本語 Wiki
編集の要約なし
編集の要約なし
1行目: 1行目:
  +
--- '''DropTables''' stores drop table data from the official WARFRAME drop table repository.<br />
  +
--
  +
-- On this Wiki, DropTables is used in:
  +
-- * [[Template:RelicTable]]
  +
--
  +
-- @module droptables
  +
-- @alias p
  +
-- @author [[User:Falterfire|Falterfire]]
  +
-- @attribution [[User:Croquemorttime|Croquemorttime]]
  +
-- @attribution [[User:Flaicher|Flaicher]]
  +
-- @attribution [[User:FINNER|FINNER]]
  +
-- @image
  +
-- @require [[Module:DropTables/data]]
  +
-- @require [[Module:Missions/data]]
  +
-- @require [[Module:String]]
  +
-- @require [[Module:Icon]]
  +
-- @require [[Module:Shared]]
  +
-- @require [[Module:Void]]
  +
-- @release stable
  +
-- <nowiki>
  +
 
--Rewritten version of Module:DropTables to work with new format of data.
 
--Rewritten version of Module:DropTables to work with new format of data.
   
18行目: 39行目:
 
local modCountCol = 3 -- If empty, default to 1. Normally only different for Endo
 
local modCountCol = 3 -- If empty, default to 1. Normally only different for Endo
 
-- in DropData["Enemies"].Avionics
 
-- in DropData["Enemies"].Avionics
  +
-- TODO: Remove this as avionics are now considered mods
 
local aviNameCol = 1 -- Avionic name
 
local aviNameCol = 1 -- Avionic name
 
local aviChanceCol = 2 -- Avionic drop chance
 
local aviChanceCol = 2 -- Avionic drop chance
26行目: 48行目:
 
local synCostCol = 3 -- The reputation cost for the offering
 
local synCostCol = 3 -- The reputation cost for the offering
 
local synRankCol = 4 -- The required Syndicate Rank to purchase the offering
 
local synRankCol = 4 -- The required Syndicate Rank to purchase the offering
 
   
 
local p = {}
 
local p = {}
33行目: 54行目:
 
local MissionData = mw.loadData( 'Module:Missions/data' )
 
local MissionData = mw.loadData( 'Module:Missions/data' )
 
local Icon = require( "Module:Icon" )
 
local Icon = require( "Module:Icon" )
  +
local String = require( "Module:String" )
 
local Shared = require( "Module:Shared" )
 
local Shared = require( "Module:Shared" )
 
local Void = require( "Module:Void" )
 
local Void = require( "Module:Void" )
   
 
local relicTooltipStart = "<span style=\"border-bottom: 1px dotted;\" class=\"relic-tooltip\" data-param=\""
 
local relicTooltipStart = "<span style=\"border-bottom: 1px dotted;\" class=\"relic-tooltip\" data-param=\""
local relicTooltipStartNoDot = "<span class=\"relic-tooltip\" data-param=\""
+
local relicTooltipStartNoDot = '<span class="tooltip" data-param2="Void" data-param="'
 
local relicTooltipCenter = "\">"
 
local relicTooltipCenter = "\">"
 
local relicTooltipEnd = "</span>"
 
local relicTooltipEnd = "</span>"
   
  +
---
  +
-- @function p.getMValue
 
function p.getMValue(theMission, ValName, noBreaks)
 
function p.getMValue(theMission, ValName, noBreaks)
 
if(type(theMission) == "string") then theMission = p.getMission(theMission) end
 
if(type(theMission) == "string") then theMission = p.getMission(theMission) end
75行目: 99行目:
 
end
 
end
   
  +
--- Goes by Type & Tier, Type & Name, or Alias
--タイプとTier、タイプと名前、またはエイリアスで移動します。
 
--So ("Survival", "Easy"), ("Survival", "Tier 1"), or ("Survival1")はすべて同じものを得る
+
-- So ("Survival", "Easy"), ("Survival", "Tier 1"), or ("Survival1") all get the same thing
  +
-- @function p.getMission
 
function p.getMission(MissionType, MissionTier, MissionName)
 
function p.getMission(MissionType, MissionTier, MissionName)
 
for i, Miss in pairs(DropData["Missions"]) do
 
for i, Miss in pairs(DropData["Missions"]) do
--MissionTiernilの場合、MissionType変数はAliasなので、その代わりにそれをチェックしてください。
+
--If MissionTier is nil, the MissionType variable is the Alias so check against that instead
 
if(MissionTier == nil) then
 
if(MissionTier == nil) then
 
if(MissionType == Miss.Alias or MissionType == Miss.Tier) and (Miss.Type == MissionName) then
 
if(MissionType == Miss.Alias or MissionType == Miss.Tier) and (Miss.Type == MissionName) then
97行目: 122行目:
 
end
 
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.
--NOTE:書いている時点では、敵にブループリントなどのドロップが記載されていないことが前提となっている。
 
  +
-- 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 function buildEnemyDrop(Enemy, Mod)
 
local drop = {EName = Enemy.Name, IName = Mod[modNameCol]}
 
local drop = {EName = Enemy.Name, IName = Mod[modNameCol]}
108行目: 137行目:
 
end
 
end
   
--Like above, but for mission drops
+
--- Like above, but for mission drops
--Calling this whenever I'm pulling drops from missions and passing them around
+
-- Calling this whenever I'm pulling drops from missions and passing them around
  +
-- @function buildMissionDrop
 
local function buildMissionDrop(theMission, Rotation, Item)
 
local function buildMissionDrop(theMission, Rotation, Item)
 
local drop = {MType = theMission.Type, MTier = theMission.Tier}
 
local drop = {MType = theMission.Type, MTier = theMission.Tier}
122行目: 152行目:
 
end
 
end
   
--Like above, but for Syndicate Offerings
+
--- Like above, but for Syndicate Offerings
  +
-- @function buildSyndicateDrop
 
local function buildSyndicateDrop(theSyndicate, Item)
 
local function buildSyndicateDrop(theSyndicate, Item)
 
local drop = {SName = theSyndicate.Name, IName = Item[synNameCol]}
 
local drop = {SName = theSyndicate.Name, IName = Item[synNameCol]}
132行目: 163行目:
   
 
--Like above, but for Avionics
 
--Like above, but for Avionics
  +
-- TODO: Remove unused function as avionics are now considered as mods
 
local function buildAvionicDrop(Enemy, Avionic)
 
local function buildAvionicDrop(Enemy, Avionic)
 
local drop = {EName = Enemy.Name, IName = Avionic[aviNameCol]}
 
local drop = {EName = Enemy.Name, IName = Avionic[aviNameCol]}
143行目: 175行目:
 
end
 
end
   
--Like above, but for resource drops
+
--- Like above, but for resource drops
  +
-- @function buildResourceDrop
 
local function buildResourceDrop(Enemy, Resource)
 
local function buildResourceDrop(Enemy, Resource)
 
local drop = {EName = Enemy.Name, IName = Resource[resourceNameCol]}
 
local drop = {EName = Enemy.Name, IName = Resource[resourceNameCol]}
170行目: 203行目:
 
--Links a Syndicate and returns it.
 
--Links a Syndicate and returns it.
 
--Doesn't actually do anything but add brackets right now
 
--Doesn't actually do anything but add brackets right now
  +
-- TODO: Remove this simple function since it is uneeded; can just concat the brackets when needed
 
local function linkSyndicate(SName)
 
local function linkSyndicate(SName)
 
return '[['..SName..']]'
 
return '[['..SName..']]'
 
end
 
end
   
--Returns the real drop chance of a specific enemy drop
+
--- Returns the "real" drop chance of a specific enemy drop
--This involves combining chance to drop mod/blueprint with chance of that specific item dropping
+
-- This involves combining chance to drop mod/blueprint with chance of that specific item dropping
--So 3% Mod Chance + 37.94% Pressure Point chance = 1.1382% real chance
+
-- So 3% Mod Chance + 37.94% Pressure Point chance = 1.1382% real chance
  +
-- @function getRealDropChance
 
local function getRealDropChance(EnemyDrop)
 
local function getRealDropChance(EnemyDrop)
 
local odds1 = EnemyDrop[eChance1Col]
 
local odds1 = EnemyDrop[eChance1Col]
184行目: 219行目:
 
end
 
end
   
  +
---
  +
-- @function getAllModDrops
 
local function getAllModDrops(enemyName)
 
local function getAllModDrops(enemyName)
 
local drops = {}
 
local drops = {}
203行目: 240行目:
 
end
 
end
   
--Custom table sort for reward tables
+
--- Custom table sort for reward tables
--WIP, initial rules:
+
-- WIP, initial rules:
--Sort first by type, then alphabetically within type, then by quantity
+
-- Sort first by type, then alphabetically within type, then by quantity
--WIP try sorting first by drop chance...
+
-- WIP try sorting first by drop chance...
  +
-- TODO: Finish this function
  +
-- @function rewardTableSort
 
local function rewardTableSort(theTable)
 
local function rewardTableSort(theTable)
 
local function sorter(r1, r2)
 
local function sorter(r1, r2)
if(r1.Chance == r2.Chance) then
+
if(r1.Chance == r2.Chance) then
if(r1.Type == r2.Type) then
+
if(r1.Type == r2.Type) then
if(r1.IName == r2.IName) then
+
if(r1.IName == r2.IName) then
return r1.Count < r2.Count
+
return r1.Count < r2.Count
else
+
else
return r1.IName < r2.IName
+
return r1.IName < r2.IName
end
 
else
 
return r1.Type < r2.Type
 
end
 
else
 
return r1.Chance > r2.Chance
 
end
 
 
end
 
end
+
else
  +
return r1.Type < r2.Type
  +
end
  +
else
  +
return r1.Chance > r2.Chance
  +
end
  +
end
  +
 
table.sort(theTable, sorter)
 
table.sort(theTable, sorter)
 
end
 
end
   
--Custom table sort for Enemy tables
+
--- Custom table sort for Enemy tables
--Rules:
+
-- Rules:
--Sort first by Drop Chance, then alphabetically within Drop Chance with Endo being last
+
-- Sort first by Drop Chance, then alphabetically within Drop Chance with Endo being last
  +
-- @function enemyTableSort
 
local function enemyTableSort(theTable)
 
local function enemyTableSort(theTable)
 
local function sorter(r1, r2)
 
local function sorter(r1, r2)
if(r1.Chance == r2.Chance) then
+
if(r1.Chance == r2.Chance) then
if(r1.Count == r2.Count) then
+
if(r1.Count == r2.Count) then
return r1.IName < r2.IName
+
return r1.IName < r2.IName
else
+
else
return r1.Count < r2.Count
+
return r1.Count < r2.Count
end
+
end
else
+
else
return r1.Chance > r2.Chance
+
return r1.Chance > r2.Chance
end
+
end
end
+
end
  +
 
 
table.sort(theTable, sorter)
 
table.sort(theTable, sorter)
 
end
 
end
   
  +
-- TODO: Remove this function and use M:Tooltips for tooltip functionality/building
 
local function getModLink(ModName)
 
local function getModLink(ModName)
 
--Scorch and Seeker are both enemies and mods. Thanks DE.
 
--Scorch and Seeker are both enemies and mods. Thanks DE.
257行目: 298行目:
 
end
 
end
   
  +
-- TODO: Remove unused function as avionics are now considered as mods
 
local function getAvionicLink(AName, AHouse)
 
local function getAvionicLink(AName, AHouse)
 
local result = '<span style="white-space:pre">[['..AName
 
local result = '<span style="white-space:pre">[['..AName
273行目: 315行目:
 
end
 
end
   
--Formats a string of text for a reward table
+
--- Formats a string of text for a reward table
--(NOTE: ALWAYS USES TWO COLUMNS)
+
-- (NOTE: ALWAYS USES TWO COLUMNS)
--Format is
+
-- Format is
 
-- [Icon] [Quantity] [Item Name with Link] || [Drop Chance]]
 
-- [Icon] [Quantity] [Item Name with Link] || [Drop Chance]]
-- With some slight variation based on drop type
+
-- With some slight variation based on drop type
-- Variation is mostly helpful for getting the right icon
+
-- 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 function formatDropString(drop)
 
local result = ""
 
local result = ""
286行目: 330行目:
 
iconText = Icon._Resource(drop.IName, nil, nil)
 
iconText = Icon._Resource(drop.IName, nil, nil)
 
if(drop.IName == "Mutalist Alad V Nav Coordinate") then
 
if(drop.IName == "Mutalist Alad V Nav Coordinate") then
result = result.."[[ナビ座標#Mutalist ナビ座標|Mutalist Alad V ナビ座標]]"
+
result = result.."[[Nav Coordinates#Mutalist Nav Coordinates|Mutalist Alad V Nav Coordinate]]"
 
else
 
else
local pieces = Shared.splitString(drop.IName," ")
+
local pieces = String.split(drop.IName, "%s")
 
local lastPiece = pieces[Shared.tableCount(pieces)]
 
local lastPiece = pieces[Shared.tableCount(pieces)]
 
if(lastPiece == "Lens") then
 
if(lastPiece == "Lens") then
319行目: 363行目:
 
local sp1, trash = string.find(drop.IName, " ")
 
local sp1, trash = string.find(drop.IName, " ")
 
local tier = string.sub(drop.IName, 1, sp1 - 1)
 
local tier = string.sub(drop.IName, 1, sp1 - 1)
iconText = relicTooltipStartNoDot..drop.IName..relicTooltipCenter..Icon._Item(tier, nil, "40x40")
+
iconText = relicTooltipStartNoDot..string.gsub(drop.IName," %(.+%)","")..relicTooltipCenter..Icon._Item(tier, nil, "40x40")
result = result.."[["..string.gsub(drop.IName,"%s%(Radiant%)","").."|"..drop.IName.." Relic]]"..relicTooltipEnd
+
result = result.."[["..string.gsub(drop.IName," %(.+%)","").."|"..drop.IName.." Relic]]"..relicTooltipEnd
 
elseif (dropType == "Credits") then
 
elseif (dropType == "Credits") then
 
iconText = Icon._Item("Credits", nil, nil)
 
iconText = Icon._Item("Credits", nil, nil)
 
result = result.."[[Credit Cache]]"
 
result = result.."[[Credit Cache]]"
 
elseif (dropType == "Blueprint") then
 
elseif (dropType == "Blueprint") then
local pieces = Shared.splitString(drop.IName," ")
+
local pieces = String.split(drop.IName, "%s")
 
local BPType = pieces[2]
 
local BPType = pieces[2]
 
local BPName = pieces[1]
 
local BPName = pieces[1]
local linkString = Shared.splitString(drop.IName, " ")[1]
+
local linkString = String.split(drop.IName, "%s")[1]
 
--Change the link for Eidolon Lenses from Eidolon to the correct page
 
--Change the link for Eidolon Lenses from Eidolon to the correct page
 
if linkString == 'Eidolon' then
 
if linkString == 'Eidolon' then
336行目: 380行目:
 
if (BPName == "Vidar" or BPName == "Lavan" or BPName == "Zetki") then
 
if (BPName == "Vidar" or BPName == "Lavan" or BPName == "Zetki") then
 
iconText = Icon._Item("Blueprint", nil, nil)
 
iconText = Icon._Item("Blueprint", nil, nil)
linkString='レールジャック/構成部品'
+
linkString='Railjack/Components'
elseif (BPType == "セリブラム") then
+
elseif (BPType == "Cerebrum") then
iconText = Icon._Item("ニューロティック", nil, nil) -- see below
+
iconText = Icon._Item("Neuroptics", nil, nil) -- see below
elseif (BPType == "キャラペス") then
+
elseif (BPType == "Carapace") then
iconText = Icon._Item("シャーシ", nil, nil) -- fix for nautilus icons
+
iconText = Icon._Item("Chassis", nil, nil) -- fix for nautilus icons
elseif (BPName == "フォーマ") then
+
elseif (BPName == "Forma") then
iconText = Icon._Item("フォーマ", nil, nil)
+
iconText = Icon._Item("Forma", nil, nil)
 
elseif (BPName == "Miter" and BPType == "Chassis") then
 
elseif (BPName == "Miter" and BPType == "Chassis") then
 
--a workaround for displaying proper icons for Miter parts
 
--a workaround for displaying proper icons for Miter parts
411行目: 455行目:
 
iconText = Icon._Item("Blueprint", nil, nil)
 
iconText = Icon._Item("Blueprint", nil, nil)
 
linkString = "Ephemera"
 
linkString = "Ephemera"
elseif (BPName == "Arum") then
+
elseif (BPName == "Arum") then
-- workarounf for arum spinosa
+
-- workarounf for arum spinosa
linkString = pieces[1].." "..pieces[2]
+
linkString = pieces[1].." "..pieces[2]
if (pieces[3]=="Blueprint") then
+
if (pieces[3]=="Blueprint") then
iconText = Icon._Item("Blueprint", nil, nil) -- this one is superfluous
+
iconText = Icon._Item("Blueprint", nil, nil) -- this one is superfluous
elseif (pieces[3]=="Guard") then
+
elseif (pieces[3]=="Guard") then
iconText = Icon._Item("Pouch", nil, nil)
+
iconText = Icon._Item("Pouch", nil, nil)
elseif (pieces[3]=="Rivet") then
+
elseif (pieces[3]=="Rivet") then
iconText = Icon._Item("Link", nil, nil)
+
iconText = Icon._Item("Link", nil, nil)
end
+
end
 
else
 
else
 
iconText = Icon._Item("Blueprint", nil, nil)
 
iconText = Icon._Item("Blueprint", nil, nil)
451行目: 495行目:
 
end
 
end
   
--Returns a table of all rewards for a given mission, split by rotation
+
--- Returns a table of all rewards for a given mission, split by rotation
  +
-- @function getRewardsForMission
 
local function getRewardsForMission(theMission)
 
local function getRewardsForMission(theMission)
 
local result = {}
 
local result = {}
471行目: 516行目:
 
end
 
end
   
  +
-- TODO: Should this be a public/exported function?
 
function p.linkType(Type)
 
function p.linkType(Type)
 
return({
 
return({
478行目: 524行目:
 
end
 
end
   
  +
---
  +
-- @function p.getMissions
 
function p.getMissions(compareFunction)
 
function p.getMissions(compareFunction)
 
local data = {}
 
local data = {}
488行目: 536行目:
 
end
 
end
 
 
--Gets the list of missions that give rewards for a specific Alias (ex Defense1)
+
--- Gets the list of missions that give rewards for a specific Alias (ex Defense1)
  +
-- @function p.getMissionTable
 
function p.getMissionTable(MissionAlias)
 
function p.getMissionTable(MissionAlias)
 
local data = {}
 
local data = {}
499行目: 548行目:
 
end
 
end
 
 
--Gets a list of missions with rewards for a given planet
+
--- Gets a list of missions with rewards for a given planet
  +
-- @function p.getMissionsForPlanet
 
function p.getMissionsForPlanet(Planet)
 
function p.getMissionsForPlanet(Planet)
 
local missions = {}
 
local missions = {}
523行目: 573行目:
 
end
 
end
   
--Returns the rewards for the A tier only for a mission
+
--- Returns the rewards for the A tier only for a mission
--Handy for missions like Capture that have a single reward
+
-- Handy for missions like Capture that have a single reward
--Returns as rows for a table with two columns
+
-- Returns as rows for a table with two columns
--See the existing Capture rewards section for an example
+
-- See the existing Capture rewards section for an example
  +
-- @function p.getSingleRotationRewards
 
function p.getSingleRotationRewards(frame)
 
function p.getSingleRotationRewards(frame)
 
local MissionType = frame.args ~= nil and frame.args[1]
 
local MissionType = frame.args ~= nil and frame.args[1]
533行目: 584行目:
 
 
 
local theMission = p.getMission(MissionType, MissionCat)
 
local theMission = p.getMission(MissionType, MissionCat)
  +
assert(theMission ~= nil, 'p.getSingleRotationRewards(frame): Could not get mission of type "'..MissionType..'" and category "'..MissionCat..'"')
if(theMission == nil) then
 
return "ERROR: No mission found for those parameters"
 
end
 
 
 
 
local data = getRewardsForMission(theMission)
 
local data = getRewardsForMission(theMission)
554行目: 603行目:
 
end
 
end
   
--Returns the rewards for a given mission/tier
+
--- Returns the rewards for a given mission/tier
--Returns as rows for a table with six columns, two for each rotation
+
-- Returns as rows for a table with six columns, two for each rotation
--See existing Survival/Rewards/Normal_Mission for examples
+
-- 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
+
-- if Tier==AllTier it will call a specific function to merge all tiers together in a single A,B,C table
  +
-- @function p.getRewardTable
 
function p.getRewardTable(frame)
 
function p.getRewardTable(frame)
 
local MissionType = frame.args ~= nil and frame.args[1]
 
local MissionType = frame.args ~= nil and frame.args[1]
570行目: 620行目:
 
 
 
local theMission = p.getMission(MissionType, MissionCat)
 
local theMission = p.getMission(MissionType, MissionCat)
  +
assert(theMission ~= nil, 'p.getRewardTable(frame): Could not get mission of type "'..MissionType..'" and category "'..MissionCat..'"')
if(theMission == nil) then
 
return "ERROR: No mission found for those parameters"
 
end
 
 
 
 
local data = getRewardsForMission(theMission)
 
local data = getRewardsForMission(theMission)
615行目: 663行目:
 
end
 
end
   
--Gets a list of all the missions for a given MissionType and Tier
+
--- Gets a list of all the missions for a given MissionType and Tier
  +
-- @function p.getMissionList
 
function p.getMissionList(frame)
 
function p.getMissionList(frame)
 
local MissionType = frame.args ~= nil and frame.args[1]
 
local MissionType = frame.args ~= nil and frame.args[1]
628行目: 677行目:
 
 
 
local missionRecord = p.getMission(MissionType, MissionTier)
 
local missionRecord = p.getMission(MissionType, MissionTier)
  +
if(missionRecord == nil) then
 
return "ERROR: Could not figure out that mission type"
+
assert(missionRecord ~= nil, 'p.getMissionList(frame): Could not figure out mission type "'..MissionType..'" and mission tier "'..MissionTier..'"')
end
+
 
local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
 
local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
 
 
640行目: 689行目:
 
end
 
end
   
--Gets a list of all the missions for a given MissionType and Tier
+
--- Gets a list of all the missions for a given MissionType and Tier
  +
-- TODO: Rename this function as "p.getMissionList2" does not describe the functionality
  +
-- @function p.getMissionList2
 
function p.getMissionList2(frame)
 
function p.getMissionList2(frame)
 
local MissionType = frame.args ~= nil and frame.args[1]
 
local MissionType = frame.args ~= nil and frame.args[1]
653行目: 704行目:
 
 
 
local missionRecord = p.getMission(MissionType, MissionTier)
 
local missionRecord = p.getMission(MissionType, MissionTier)
  +
if(missionRecord == nil) then
 
  +
assert(missionRecord ~= nil, 'p.getMissionList2(frame): Could not figure out mission type "'..MissionType..'" and mission tier "'..MissionTier..'"')
return "ERROR: ミッションの種類がわかりませんでした"
 
end
+
 
local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
 
local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
 
 
666行目: 717行目:
 
end
 
end
   
  +
--- WIP get missions regardless of tier
 
  +
-- TODO: Finish this function or remove it entirely if not used
 
  +
-- @function p.getMissionAllTier
--WIP get missions regardless of tier
 
 
function p.getMissionAllTier(MissionType)
 
function p.getMissionAllTier(MissionType)
 
local a={}
 
local a={}
679行目: 730行目:
 
end
 
end
   
--WIP Gets a the reward table of all Mission for a given MissionType (all tiers)
+
--- WIP Gets a the reward table of all Mission for a given MissionType (all tiers)
  +
-- TODO: Finish this function or remove it entirely if not used
  +
-- @function p.getRewardTableAllTier
 
function p.getRewardTableAllTier(frame)
 
function p.getRewardTableAllTier(frame)
 
local MissionType = frame.args ~= nil and frame.args[1]
 
local MissionType = frame.args ~= nil and frame.args[1]
 
 
 
 
local theMissiont = p.getMissionAllTier(MissionType)
 
local theMissiont = p.getMissionAllTier(MissionType)
  +
if(theMissiont == nil) then
 
return "ERROR: No mission found for those parameters"
+
assert(theMissiont ~= nil, 'p.getRewardTableAllTier(frame): No mission found with type "'..MissionType..'"')
end
 
 
 
 
local result = ""
 
local result = ""
741行目: 792行目:
 
end
 
end
   
--WIP Gets a list of all Mission for a given MissionType (all tiers)
+
--- WIP Gets a list of all Mission for a given MissionType (all tiers)
  +
-- TODO: Finish this function or remove it entirely if not used
  +
-- @function p.getMissionListAllTier
 
function p.getMissionListAllTier(frame)
 
function p.getMissionListAllTier(frame)
 
local MissionType = frame.args ~= nil and frame.args[1]
 
local MissionType = frame.args ~= nil and frame.args[1]
748行目: 801行目:
 
 
 
local missionRecordt = p.getMissionAllTier(MissionType)
 
local missionRecordt = p.getMissionAllTier(MissionType)
  +
if(missionRecordt == nil) then
 
return "ERROR: Could not figure out that mission type"
+
assert(missionRecordt ~= nil, 'p.getMissionListAllTier(frame): Could not figure out mission type "'..MissionType..'"')
end
+
 
for i, missionRecord in pairs(missionRecordt) do
 
for i, missionRecord in pairs(missionRecordt) do
 
local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
 
local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
761行目: 814行目:
 
end
 
end
   
  +
--- Get a list of all missions that drop a given item
 
  +
-- @function getDropMissions
 
--Get a list of all missions that drop a given item
 
 
local function getDropMissions(itemName)
 
local function getDropMissions(itemName)
 
local Drops = {}
 
local Drops = {}
787行目: 839行目:
 
end
 
end
   
  +
---
  +
-- @function getDropSyndicates
 
local function getDropSyndicates(itemName)
 
local function getDropSyndicates(itemName)
 
local Drops = {}
 
local Drops = {}
803行目: 857行目:
 
end
 
end
   
--Returns an EnemyDrop object for each enemy that drops a given mod
+
--- Returns an EnemyDrop object for each enemy that drops a given mod
  +
-- @function getDropEnemies
 
local function getDropEnemies(itemName)
 
local function getDropEnemies(itemName)
 
local drops = {}
 
local drops = {}
829行目: 884行目:
 
end
 
end
   
  +
--- Gets the table used on Void Relic/ByMission
 
  +
-- Unlike getRewardTable, this is just the full table with all formatting
--Gets the table used on Void Relic/ByMission
 
  +
-- This is pretty ugly, but kinda have to do it this way
--Unlike getRewardTable, this is just the full table with all formatting
 
  +
-- (Unless you have a better solution, in which case by all means go ahead and fix it)
--This is pretty ugly, but kinda have to do it this way
 
  +
-- (I'm not exactly a Lua expert or a UI expert)
--(Unless you have a better solution, in which case by all means go ahead and fix it)
 
  +
-- TODO: Break up this function into smaller functions
--(I'm not exactly a Lua expert or a UI expert)
 
  +
-- @function p.getRelicTable
 
function p.getRelicTable(frame)
 
function p.getRelicTable(frame)
  +
--Okay, so first up, need to know which planet this is for
--さて、まず最初に、これがどの惑星のためのものかを知る必要があります。
 
 
local Planet = nil
 
local Planet = nil
 
if(frame ~= nil) then
 
if(frame ~= nil) then
 
Planet = frame.args ~= nil and frame.args[1] or frame
 
Planet = frame.args ~= nil and frame.args[1] or frame
 
end
 
end
--Planet == nil 'all planet' の代わりになっています。
+
--Planet == nil is standing in for 'all planets', so adding option to explicitly call 'all'
 
if(Planet ~= nil and (Planet == "" or Planet == "All")) then
 
if(Planet ~= nil and (Planet == "" or Planet == "All")) then
 
Planet = nil
 
Planet = nil
865行目: 921行目:
 
--Now for the 'fun' part: Getting the list
 
--Now for the 'fun' part: Getting the list
 
for i, m in ipairs(missions) do
 
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 rowName = ""
 
local mAlias, theMission
 
local mAlias, theMission
if(Planet == nil) then
+
if(Planet == nil) then
--全て表示する場合は、 "Mission Type (Tier Name)"という形式で、ミッションタイプへのリンクが表示されます。
+
--When showing all, the format is "Mission Type (Tier Name)" with link to mission type
--For example, "[[Survival]] (Tier 1)" or "Spy (Lua)"
+
--For example, "[[Survival]] (Tier 1)" or "Spy (Lua)"
if (m.Type== "Bounty" or m.Type== "Ghoul Bounty" or m.Type== "Onslaught") then
+
if (m.Type== "Bounty" or m.Type== "Ghoul Bounty" or m.Type== "Onslaught") then
rowName = p.linkType(m.Type).." ("..p.getMValue(m, "SHORTNAME")..")"
+
rowName = p.linkType(m.Type).." ("..p.getMValue(m, "SHORTNAME")..")"
else
+
else
 
local mType = p.linkType(m.Type)
 
local mType = p.linkType(m.Type)
 
mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%] %(%[%[The Steel Path|Steel Path%]%]%)")
 
mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%] %(%[%[The Steel Path|Steel Path%]%]%)")
 
rowName = mType.." ("..p.getMValue(m, "NAME")..")"
 
rowName = mType.." ("..p.getMValue(m, "NAME")..")"
end
+
end
theMission = m
+
theMission = 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 == 1) then
  +
rowName = placeName.." ([[Dark Sector|DS]] "..p.linkType(m.Type)..")"
 
else
 
else
local placeName = m.Node
 
 
--単一惑星を表示する場合は、 "Mission Name (Type)"を表示します。
 
--For example, "Rusalka (Capture)"
 
--Mission type is still linked
 
--Dark Sector is also linked if appropriate
 
if (m.IsDarkSector == 1) then
 
rowName = placeName.." ([[Dark Sector|DS]] "..p.linkType(m.Type)..")"
 
else
 
 
local mType = p.linkType(m.Type)
 
local mType = p.linkType(m.Type)
 
mType = string.gsub(mType, "%[%[Cetus Bounty %(Steel Path%)%]%]", "%[%[The Steel Path|Steel Path%]%] %[%[Cetus Bounty|Bounty%]%]")
 
mType = string.gsub(mType, "%[%[Cetus Bounty %(Steel Path%)%]%]", "%[%[The Steel Path|Steel Path%]%] %[%[Cetus Bounty|Bounty%]%]")
899行目: 955行目:
 
mLevel = '<br />'..string.gsub(m.MinLevel, "<br />", " ")
 
mLevel = '<br />'..string.gsub(m.MinLevel, "<br />", " ")
 
end
 
end
  +
 
 
rowName = placeName..' ('..mType..')'..mLevel
 
rowName = placeName..' ('..mType..')'..mLevel
 
end
 
end
 
theMission = p.getMission(m.Tier, nil, m.Type)
 
theMission = p.getMission(m.Tier, nil, m.Type)
if(theMission == nil) then
+
assert(theMission ~= nil, 'p.getRelicTable(frame): Could not get mission of tier "'..m.Tier..'" and type "'..m.Type..'"')
  +
end
return "ERROR: Could not Miss "..m.Tier
 
end
+
local thisRow = nil
  +
--This is where we get all the rewards for the mission
end
 
local thisRow = nil
+
local drops = getRewardsForMission(theMission)
  +
--This is where we get all the rewards for the mission
 
local drops = getRewardsForMission(theMission)
+
--Need to know if this is a single rotation
  +
--Because if it is, just a checkmark instead of a letter
 
  +
local isSingleRot = Shared.tableCount(drops) == 1
--Need to know if this is a single rotation
 
--Because if it is, just a checkmark instead of a letter
+
--For each mission, looping each rotation
local isSingleRot = Shared.tableCount(drops) == 1
+
for rot, dropTable in Shared.skpairs(drops) do
--For each mission, looping each rotation
+
--And each drop for each rotation
for rot, dropTable in Shared.skpairs(drops) do
+
for j, d in pairs(dropTable) do
--And each drop for each rotation
+
--We only care if it's a relic
for j, d in pairs(dropTable) do
+
if(d.Type == "Relic") then
--We only care if it's a relic
+
--Set up the row if we don't have it yet
if(d.Type == "Relic") then
+
--Mission will not be added to the grid unless it drops at least one relic
--Set up the row if we don't have it yet
+
--Avoids adding a row for something like Assassination that never gives relics
--Mission will not be added to the grid unless it drops at least one relic
+
if(thisRow == nil) then
--Avoids adding a row for something like Assassination that never gives relics
+
thisRow = {}
if(thisRow == nil) then
+
end
thisRow = {}
+
end
+
--Example: "Lith A1"
  +
local RelicText = d.IName
  +
local RadiantRelic = ""
  +
if (string.find(RelicText,"%(Radiant%)")~=nil) then
  +
RadiantRelic = "*"
  +
RelicText = string.gsub(RelicText,"%s%(Radiant%)","")
  +
end
  +
  +
--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
  +
if (isSingleRot) then
  +
thisRow[RelicText] = "✔"..RadiantRelic
  +
else
  +
thisRow[RelicText] = thisRow[RelicText]..rot..RadiantRelic
  +
end
  +
  +
--Adding drop rate info when hovering
  +
--If the drop rate is under 1% we set it red
  +
--Under 2%, orangered
  +
local RelicTextColor = "inherit"
  +
if (d.Chance < 1) then
  +
RelicTextColor = "red"
  +
elseif (d.Chance < 2) then
  +
RelicTextColor = "orangered"
  +
end
  +
thisRow[RelicText] = "<span style=\"color:" .. RelicTextColor .. ";\" title=\"Drop rate : " .. d.Chance .. "%\">" .. thisRow[RelicText] .. "</span>"
 
 
--Example: "Lith A1"
+
local RelicText = d.IName
+
--Also gotta add the Relic to our list if we don't have it yet
local RadiantRelic = ""
+
if(Relics[RTier][RName] == nil) then
if (string.find(RelicText,"%(Radiant%)")~=nil) then
+
Relics[RTier][RName] = RelicText
RadiantRelic = "*"
 
RelicText = string.gsub(RelicText,"%s%(Radiant%)","")
 
end
 
 
--Example: {"Lith", "A1"}
 
local RelicBits = Shared.splitString(RelicText, " ")
 
--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
 
if (isSingleRot) then
 
thisRow[RelicText] = "✔"..RadiantRelic
 
else
 
thisRow[RelicText] = thisRow[RelicText]..rot..RadiantRelic
 
end
 
 
--Adding drop rate info when hovering
 
--If the drop rate is under 1% we set it red
 
--Under 2%, orangered
 
local RelicTextColor = "inherit"
 
if (d.Chance < 1) then
 
RelicTextColor = "red"
 
elseif (d.Chance < 2) then
 
RelicTextColor = "orangered"
 
end
 
thisRow[RelicText] = "<span style=\"color:" .. RelicTextColor .. ";\" title=\"Drop rate : " .. d.Chance .. "%\">" .. thisRow[RelicText] .. "</span>"
 
 
 
--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
 
end
 
end
 
end
  +
end
 
 
if ( thisRow ~= nil ) then
+
if ( thisRow ~= nil ) then
tableRows[rowName] = thisRow
+
tableRows[rowName] = thisRow
end
+
end
 
end
 
end
   
1,037行目: 1,091行目:
 
end
 
end
   
  +
--- Function used for building Void Relic/DropLocation table
 
  +
-- TODO: Break up this function into smaller functions
--Void Relic/DropLocation テーブルを構築するための関数です。
 
  +
-- @function p.getRelicByLocation
 
function p.getRelicByLocation(frame)
 
function p.getRelicByLocation(frame)
 
local tier = frame.args ~= nil and frame.args[1] or frame
 
local tier = frame.args ~= nil and frame.args[1] or frame
local tierPieces = Shared.splitString(tier, " ")
+
local tierPieces = String.split(tier, "%s")
 
 
 
--5/26/2018: You can now call on a single relic and get just the table
 
--5/26/2018: You can now call on a single relic and get just the table
  +
-- TODO: Use M:Table instead for table count/size
 
if(Shared.tableCount(tierPieces) == 2) then
 
if(Shared.tableCount(tierPieces) == 2) then
 
tier = tierPieces[1]
 
tier = tierPieces[1]
1,052行目: 1,108行目:
 
local relicData = {}
 
local relicData = {}
 
 
  +
-- TODO: Refactor so there is at max 3 nested code blocks
--私のほとんどの機能と同様に、これを2つの部分に分割します:
 
  +
--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 i, theMission in pairs(DropData["Missions"]) do
 
for i, theMission in pairs(DropData["Missions"]) do
 
if(theMission.Rewards ~= nil and theMission.Ignore ~= true) then
 
if(theMission.Rewards ~= nil and theMission.Ignore ~= true) then
1,060行目: 1,117行目:
 
for rot, dropTable in Shared.skpairs(theMission.Rewards) do
 
for rot, dropTable in Shared.skpairs(theMission.Rewards) do
 
for j, drop in pairs(dropTable) do
 
for j, drop in pairs(dropTable) do
  +
--When we find a relic drop, make sure it's for the right tier
--レリックドロップを見つけたら、それが正しいティアのものであることを確認してください
 
 
if(drop[misTypeCol] == "Relic") then
 
if(drop[misTypeCol] == "Relic") then
 
--Example: {"Lith", "A1"}
 
--Example: {"Lith", "A1"}
local RelicBits = Shared.splitString(drop[misNameCol], " ")
+
local RelicBits = String.split(drop[misNameCol], "%s")
 
--Example: "Lith"
 
--Example: "Lith"
 
local RTier = RelicBits[1]
 
local RTier = RelicBits[1]
1,098行目: 1,155行目:
 
!Rotation
 
!Rotation
 
!Chance]]
 
!Chance]]
  +
 
 
for RName, RTable in Shared.skpairs(relicData) do
 
for RName, RTable in Shared.skpairs(relicData) do
 
local tierLink = "\n|-\n| ".."[["..tier.." "..RName.."]]"
 
local tierLink = "\n|-\n| ".."[["..tier.." "..RName.."]]"
1,144行目: 1,201行目:
   
 
table.insert(result, "\n|}")
 
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)
 
return table.concat(result)
 
end
 
end
   
  +
--- TODO: Break up this function into smaller functions
  +
-- @function p.getSingleRelicByLocation
 
function p.getSingleRelicByLocation(tier, name)
 
function p.getSingleRelicByLocation(tier, name)
 
local relicData = {Drops = {}, Rewards = Void.getRelic(tier, name).Drops}
 
local relicData = {Drops = {}, Rewards = Void.getRelic(tier, name).Drops}
1,164行目: 1,225行目:
 
if(drop[misTypeCol] == "Relic") then
 
if(drop[misTypeCol] == "Relic") then
 
--Example: {"Lith", "A1"}
 
--Example: {"Lith", "A1"}
local RelicBits = Shared.splitString(drop[misNameCol], " ")
+
local RelicBits = String.split(drop[misNameCol], "%s")
 
--Example: "Lith"
 
--Example: "Lith"
 
local RTier = RelicBits[1]
 
local RTier = RelicBits[1]
1,209行目: 1,270行目:
 
types[i] = d.MType --p.linkType(d.MType)
 
types[i] = d.MType --p.linkType(d.MType)
 
if (d.MType == "Onslaught") then
 
if (d.MType == "Onslaught") then
missions[i] = p.getMValue(d.theMission, 'ShortName')
+
missions[i] = p.getMValue(d.theMission, 'Tier')
 
else
 
else
 
missions[i] = p.getMValue(d.theMission, 'Name')
 
missions[i] = p.getMValue(d.theMission, 'Name')
1,227行目: 1,288行目:
 
end
 
end
 
local num = string.gsub(chances[i],"%%","")
 
local num = string.gsub(chances[i],"%%","")
local ChancesBits = Shared.splitString(num,",&nbsp;")
+
local ChancesBits = String.split(num, ",&nbsp;")
local highchance
+
local highchance
 
if (table.getn(ChancesBits) <= 1) or (ChancesBits == nil) then
 
if (table.getn(ChancesBits) <= 1) or (ChancesBits == nil) then
 
highchance = tonumber(num)
 
highchance = tonumber(num)
1,246行目: 1,307行目:
 
local mType = p.linkType(types[i])
 
local mType = p.linkType(types[i])
 
mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%]<br />%(%[%[The Steel Path|Steel Path%]%]%)")
 
mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%]<br />%(%[%[The Steel Path|Steel Path%]%]%)")
  +
 
 
result = result.."\n|-\n|style=\"padding:10px;\"|"..mType
 
result = result.."\n|-\n|style=\"padding:10px;\"|"..mType
if(mlist == "" or mlist == "ERROR: Could not figure out that mission type") then
+
if(mlist == "") then
 
result = result.."\n|style=\"padding:10px;\"|"..missions[i]
 
result = result.."\n|style=\"padding:10px;\"|"..missions[i]
 
else
 
else
1,264行目: 1,325行目:
 
end
 
end
   
  +
---
  +
-- @function p.getItemByMissionTable
 
function p.getItemByMissionTable(frame)
 
function p.getItemByMissionTable(frame)
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
1,295行目: 1,358行目:
 
end
 
end
   
  +
---
  +
-- @function p.getItemByEnemyTable
 
function p.getItemByEnemyTable(frame)
 
function p.getItemByEnemyTable(frame)
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
1,324行目: 1,389行目:
 
end
 
end
   
--Returns the list of drop locations used by the ModBox
+
--- Returns the list of drop locations used by the ModBox
  +
-- TODO: Break up this function into smaller functions
  +
-- @function p.getItemDropList
 
function p.getItemDropList(frame)
 
function p.getItemDropList(frame)
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
1,361行目: 1,428行目:
 
end
 
end
 
 
table.sort(finalTable, function (r1, r2)
+
table.sort(finalTable, function (r1, r2)
 
return r1 < r2
 
return r1 < r2
 
end)
 
end)
1,372行目: 1,439行目:
 
local mType = p.linkType(i)
 
local mType = p.linkType(i)
 
mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%] %(%[%[The Steel Path|Steel Path%]%]%)")
 
mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%] %(%[%[The Steel Path|Steel Path%]%]%)")
  +
 
 
result = result.."<br/>"..mType
 
result = result.."<br/>"..mType
 
 
1,385行目: 1,452行目:
 
ttip = ttip..'\\nRotation '..thisDrop.Rotation
 
ttip = ttip..'\\nRotation '..thisDrop.Rotation
 
if (thisDrop.Chance ~= nil) then
 
if (thisDrop.Chance ~= nil) then
ttip = ttip..': '..thisDrop.Chance.."%"
+
ttip = ttip..': '..thisDrop.Chance.."%"
  +
end
end
 
 
end
 
end
 
 
1,430行目: 1,497行目:
 
end
 
end
   
  +
---
  +
-- @function p.getItemByEnemyCount
 
function p.getItemByEnemyCount(frame)
 
function p.getItemByEnemyCount(frame)
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
1,438行目: 1,507行目:
 
end
 
end
   
  +
---
  +
-- @function p.getItemByMissionCount
 
function p.getItemByMissionCount(frame)
 
function p.getItemByMissionCount(frame)
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
 
local theDrop = frame.args ~= nil and frame.args[1] or frame
1,446行目: 1,517行目:
 
end
 
end
   
  +
---
  +
-- @function p.getFullEnemyList
 
function p.getFullEnemyList(frame)
 
function p.getFullEnemyList(frame)
 
local result = "All Enemies: "
 
local result = "All Enemies: "
1,462行目: 1,535行目:
 
end
 
end
   
  +
---
  +
-- @function p.getEnemyModDrops
 
function p.getEnemyModDrops(frame)
 
function p.getEnemyModDrops(frame)
 
local EnemyName = frame.args ~= nil and frame.args[1] or frame
 
local EnemyName = frame.args ~= nil and frame.args[1] or frame
1,491行目: 1,566行目:
 
end
 
end
 
 
  +
-- TODO: Remove this function or move it to /validate subpage of /data
 
function p.TestFrame(frame)
 
function p.TestFrame(frame)
 
local txt = "VALUES:\n\n"
 
local txt = "VALUES:\n\n"

2021年6月7日 (月) 02:33時点における版

このモジュールについての説明文ページを モジュール:DropTables/doc に作成できます

---	'''DropTables''' stores drop table data from the official WARFRAME drop table repository.<br />
--	
--	On this Wiki, DropTables is used in:
--	* [[Template:RelicTable]]
--	
--	@module		droptables
--	@alias		p
--	@author			[[User:Falterfire|Falterfire]]
--	@attribution	[[User:Croquemorttime|Croquemorttime]]
--	@attribution	[[User:Flaicher|Flaicher]]
--	@attribution	[[User:FINNER|FINNER]]
--	@image		
--	@require	[[Module:DropTables/data]]
--	@require	[[Module:Missions/data]]
--	@require	[[Module:String]]
--	@require	[[Module:Icon]]
--	@require	[[Module:Shared]]
--	@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

--For reference:
--    in DropData["Missions"].Rewards
local misNameCol  = 1  -- Name of the drop
local misTypeCol  = 2  -- Type of thing dropped (IE Mod, Endo, Credits)
local misChanceCol = 3 -- Chance for the thing to drop
local misCountCol  = 4 -- Number of things dropped. If empty, default to 1
--    in DropData["Enemies"].Mods
local modNameCol   = 1 -- Name of the mod
local modChanceCol = 2 -- The chance of a mod dropping
local modCountCol  = 3 -- If empty, default to 1. Normally only different for Endo
--    in DropData["Enemies"].Avionics
-- TODO: Remove this as avionics are now considered mods
local aviNameCol   = 1 -- Avionic name
local aviChanceCol = 2 -- Avionic drop chance
local aviHouseCol  = 3 -- Avionic house (optional if known)
--    in DropData["Syndicates"].Offerings
local synNameCol   = 1 -- Name of the offering
local synTypeCol   = 2 -- Type of offering
local synCostCol   = 3 -- The reputation cost for the offering
local synRankCol   = 4 -- The required Syndicate Rank to purchase the offering

local p = {}

local DropData = mw.loadData( 'Module:DropTables/data' )
local MissionData = mw.loadData( 'Module:Missions/data' )
local Icon = require( "Module:Icon" )
local String = require( "Module:String" )
local Shared = require( "Module:Shared" )
local Void = require( "Module:Void" )

local relicTooltipStart = "<span style=\"border-bottom: 1px dotted;\" class=\"relic-tooltip\" data-param=\""
local relicTooltipStartNoDot = '<span class="tooltip" data-param2="Void" data-param="'
local relicTooltipCenter = "\">"
local relicTooltipEnd = "</span>"

---
--	@function		p.getMValue
function p.getMValue(theMission, ValName, noBreaks)
    if(type(theMission) == "string") then theMission = p.getMission(theMission) end
    if(theMission == nil) then return nil end
    
    ValName = string.upper(ValName)
    if(ValName == 'ALIAS') then
        if(theMission.Alias ~= nil) then
            return theMission.Alias
        else
            return theMission.Type..' - '..theMission.Tier
        end
    elseif(ValName == 'NAME') then
        if(theMission.Name ~= nil) then
            local mName = theMission.Name
            if noBreaks == true then
                mName = string.gsub(mName, "<br />", " ")
            end
            return mName
        else
            return theMission.Tier
        end
    elseif(ValName == 'SHORTNAME') then
        if(theMission.ShortName ~= nil) then
            return theMission.ShortName
        else
            return p.getMValue(theMission, 'NAME')
        end
    elseif(ValName == 'TIER') then
        return theMission.Tier
    elseif(ValName == 'TYPE') then
        return theMission.Type
    end
end

---	Goes by Type & Tier, Type & Name, or Alias
--	So ("Survival", "Easy"), ("Survival", "Tier 1"), or ("Survival1") all get the same thing
--	@function		p.getMission
function p.getMission(MissionType, MissionTier, MissionName)
    for i, Miss in pairs(DropData["Missions"]) do
        --If MissionTier is nil, the MissionType variable is the Alias so check against that instead
        if(MissionTier == nil) then
            if(MissionType == Miss.Alias or MissionType == Miss.Tier) and (Miss.Type == MissionName) then
                return Miss
            elseif(MissionType == Miss.Alias) then
                return Miss
            elseif (Miss.Type == "Assassinate") and (Miss.Tier == MissionType) then --for the peculiar call p.getRelicTable does
                return Miss
            end
        else
            if(Miss.Type == MissionType and 
                (Miss.Tier == MissionTier or Miss.Name == MissionTier)) then
                return Miss
            end
        end
    end
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 = {EName = Enemy.Name, IName = Mod[modNameCol]}
    drop.Chance = (Enemy.ModChance * Mod[modChanceCol]) / 100
    drop.Count = Mod[modCountCol] ~= nil and Mod[modCountCol] or 1
    if(drop.IName == 'Endo') then drop.Type = 'Endo' else drop.Type = 'Mod' end
    return drop
end

---	Like above, but for mission drops
--	Calling this whenever I'm pulling drops from missions and passing them around
--	@function		buildMissionDrop
local function buildMissionDrop(theMission, Rotation, Item)
    local drop = {MType = theMission.Type, MTier = theMission.Tier}
    drop.Rotation = Rotation
    drop.IName = Item[misNameCol]
    drop.Chance = Item[misChanceCol]
    drop.Count = Item[misCountCol] ~= nil and Item[misCountCol] or 1
    drop.Type = Item[misTypeCol]
    --Just go ahead and tag this on because who knows what we'll need from it
    drop.theMission = theMission
    return drop
end

---	Like above, but for Syndicate Offerings
--	@function		buildSyndicateDrop
local function buildSyndicateDrop(theSyndicate, Item)
    local drop = {SName = theSyndicate.Name, IName = Item[synNameCol]}
    drop.Type = Item[synTypeCol]
    drop.Cost = Item[synCostCol]
    drop.Rank = Item[synRankCol]
    return drop
end

--Like above, but for Avionics
-- TODO: Remove unused function as avionics are now considered as mods
local function buildAvionicDrop(Enemy, Avionic)
    local drop = {EName = Enemy.Name, IName = Avionic[aviNameCol]}
    drop.Chance = (Enemy.ModChance * Avionic[aviChanceCol]) / 100
    drop.Count = 1
    drop.Type = 'Avionic'
    if (Avionic[aviHouseCol] ~= nil) then
        drop.House = Avionic[aviHouseCol]
    end
    return drop
end

---	Like above, but for resource drops
--	@function		buildResourceDrop
local function buildResourceDrop(Enemy, Resource)
    local drop = {EName = Enemy.Name, IName = Resource[resourceNameCol]}
    drop.Chance = (Enemy.ResourceChance * Resource[resourceChanceCol]) / 100
    drop.Count = 1
    drop.Type = 'Resource'
    return drop
end

local function linkEnemy(EName)
    --Cut off enemy names before parentheses while linking
    local paren, trash = string.find(EName, "%(")
    local Result = ""
    if(paren ~= nil) then
        Result = "[["..string.sub(EName, 1, paren - 2).."|"..EName.."]]"
    elseif (EName == "Fissure Corrupted Enemy") then
        Result = "[[Void Fissure|"..EName.."]]"
    elseif (EName == "Dargyn" or EName == "Carrier") then
        Result = "[["..EName.." (Enemy)".."|"..EName.."]]"
    else
        Result = "[["..EName.."]]"
    end
    return Result
end

--Links a Syndicate and returns it.
--Doesn't actually do anything but add brackets right now
-- TODO: Remove this simple function since it is uneeded; can just concat the brackets when needed
local function linkSyndicate(SName)
    return '[['..SName..']]'
end

---	Returns the "real" drop chance of a specific enemy drop
--	This involves combining chance to drop mod/blueprint with chance of that specific item dropping
--	So 3% Mod Chance + 37.94% Pressure Point chance = 1.1382% real chance
--	@function		getRealDropChance
local function getRealDropChance(EnemyDrop)
    local odds1 = EnemyDrop[eChance1Col]
    local odds2 = EnemyDrop[eChance2Col]
    local result = ((odds1 * odds2) / 100)
    return result
end

---	
--	@function		getAllModDrops
local function getAllModDrops(enemyName)
    local drops = {}
    for i, Enemy in pairs(DropData["Enemies"]) do
        if(Enemy.Name == enemyName and Enemy.Mods ~= nil) then
            for j, Mod in pairs(Enemy.Mods) do
                local drop = buildEnemyDrop(Enemy, Mod)
                table.insert(drops, drop)
            end
        end
        if(Enemy.Name == enemyName and Enemy.Avionics ~= nil) then
            for j, Avionic in pairs(Enemy.Avionics) do
                local drop = buildAvionicDrop(Enemy, Avionic)
                table.insert(drops, drop)
            end
        end
    end
    return drops
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.IName == r2.IName) then
                    return r1.Count < r2.Count
                else
                    return r1.IName < r2.IName
                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.IName < r2.IName
            else
                return r1.Count < r2.Count
            end
        else
            return r1.Chance > r2.Chance
        end
    end

    table.sort(theTable, sorter)
end

-- TODO: Remove this function and use M:Tooltips for tooltip functionality/building
local function getModLink(ModName)
    --Scorch and Seeker are both enemies and mods. Thanks DE.
    --Also Sanctuary as mod VS Simaris's thing
    --Also Antitoxin, mod vs gear
    if(ModName == "Scorch" or ModName == "Seeker" or ModName == "Sanctuary" or ModName == "Antitoxin") then
        return "<span class=\"mod-tooltip\" data-param=\""..ModName.."\" style=\"white-space:pre\">[["..ModName.." (Mod)".."|"..ModName.."]]</span>"
    else
        return "<span class=\"mod-tooltip\" data-param=\""..ModName.."\" style=\"white-space:pre\">[["..ModName.."]]</span>"
    end
end

-- TODO: Remove unused function as avionics are now considered as mods
local function getAvionicLink(AName, AHouse)
    local result = '<span style="white-space:pre">[['..AName
    --Wiki name collisions are handled here
    --Warhead is also an Archwing ability
    if(AName == 'Warhead') then
        result = result..' (Avionic)|'..AName
    end
    result = result..']]'
    --Add the house if it was passed in
    if (AHouse ~= nil) then
        result = result..' ('..AHouse..')'
    end
    result = result..'</span>'
    return result
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 = Icon._Resource(drop.IName, nil, nil)
        if(drop.IName == "Mutalist Alad V Nav Coordinate") then
            result = result.."[[Nav Coordinates#Mutalist Nav Coordinates|Mutalist Alad V Nav Coordinate]]"
        else
            local pieces = String.split(drop.IName, "%s")
            local lastPiece = pieces[Shared.tableCount(pieces)]
            if(lastPiece == "Lens") then
                if(pieces[1] == "Greater") then
                    iconText = Icon._Resource("Greater Focus Lens")
                else
                    iconText = Icon._Resource("Focus Lens")
                end
                result = "[[Focus Lens|"..drop.IName.."]]"
            else
                result = result.."[["..drop.IName.."]]"
            end
        end
    elseif (dropType == "Scene") then
        --iconText = Icon._Item("Scene", nil, nil)
        result = result.."[[Captura|"..drop.IName.."]]"
    elseif (dropType == "Endo") then
        iconText = Icon._Item("Endo", nil, nil)
        result = result.."[[Endo]]"
    elseif (dropType == "Ayatan Sculpture") then
        --iconText = Icon._Item(drop.IName)
        result = "[[Ayatan Sculpture|"..drop.IName.."]]"
    elseif (dropType == "Mod") then
        iconText = Icon._Item("Mods", nil, nil)
        result = result..getModLink(drop.IName)
    elseif (dropType == "Relic") then
        --Grabbing everything before the first space to get tier
        --That way I can get the right icon
        --IE 'Lith' from 'Lith V1'
        local sp1, trash = string.find(drop.IName, " ")
        local tier = string.sub(drop.IName, 1, sp1 - 1)
        iconText = relicTooltipStartNoDot..string.gsub(drop.IName," %(.+%)","")..relicTooltipCenter..Icon._Item(tier, nil, "40x40")
        result = result.."[["..string.gsub(drop.IName," %(.+%)","").."|"..drop.IName.." Relic]]"..relicTooltipEnd
    elseif (dropType == "Credits") then
        iconText = Icon._Item("Credits", nil, nil)
        result = result.."[[Credit Cache]]"
    elseif (dropType == "Blueprint") then
        local pieces = String.split(drop.IName, "%s")
        local BPType = pieces[2]
        local BPName = pieces[1]
        local linkString = String.split(drop.IName, "%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[Shared.tableCount(pieces)] == "Chassis") then
                    --a workaround for displaying proper icons for Spectra parts
                    iconText = Icon._Item("Stock", nil, nil)
                elseif (BPName == "Spectra" and pieces[Shared.tableCount(pieces)] == "Handle") then   
                    --because Spectra has oddly named parts
                    iconText = Icon._Item("Receiver", nil, nil)
                else
                iconText = Icon._Item(pieces[Shared.tableCount(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.IName.."]]"
        
    elseif (dropType == "Fragments") then
    --    iconText = Icon._Item("Mutate", nil, nil)
        result = result.."[[Fragments|"..drop.IName.."]]"
    elseif (dropType == "Item") then
        if (string.find(drop.IName,"Sigil")~=nil) then
            --iconText = Icon._Item(drop.IName)
            result = result.."[[Sigils|"..drop.IName.."]]"
        else
            iconText = Icon._Item(drop.IName)
            result = result.."[["..drop.IName.."]]"
        end
        
    else
        result = result..drop.IName
    end
    
    if(drop.Count > 1) then
        result = 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
local function getRewardsForMission(theMission)
    local result = {}
    
    if(theMission.Rewards ~= nil) then
        for key, dropTable in Shared.skpairs(theMission.Rewards) do
            local temp = {}
            
            for i, drop in pairs(dropTable) do
                table.insert(temp, buildMissionDrop(theMission, key, drop))
            end
            
            rewardTableSort(temp)
            result[key] = temp
        end
    end
    
    return result
end

-- TODO: Should this be a public/exported function?
function p.linkType(Type)
    return({
        Salvage="[[Infested Salvage|Salvage]]",
        Rush="[[Rush (Archwing)|Rush]]",
    })[Type] or  "[["..Type.."]]"
end

---
--	@function		p.getMissions
function p.getMissions(compareFunction)
    local data = {}
    for i, m in ipairs(MissionData["MissionDetails"]) do
        if(compareFunction(m)) then
            table.insert(data, m)
        end
    end
    return data
end
 
---	Gets the list of missions that give rewards for a specific Alias (ex Defense1)
--	@function		p.getMissionTable
function p.getMissionTable(MissionAlias)
    local data = {}
    for _, m in Shared.skpairs(MissionData["MissionDetails"]) do
        if(m.Tier == 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
function p.getMissionsForPlanet(Planet)
    local missions = {}
 
    for _, m in pairs(MissionData["MissionDetails"]) do
        if (m.Planet == Planet and m.Tier == 'Landscape') then
            for i, n in ipairs(DropData["Missions"]) do
                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, Tier = m.Tier, Pic = m.Pic}
                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.Tier = n.Tier; mData.MinLevel = n.Name
                    table.insert(missions, mData)
                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.Tier = n.Tier; mData.MinLevel = n.Name
                    table.insert(missions, mData)
                end
            end
        elseif (m.Planet == Planet and m.Tier ~= 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
--	@function		p.getSingleRotationRewards
function p.getSingleRotationRewards(frame)
    local MissionType = frame.args ~= nil and frame.args[1]
    local MissionCat = frame.args ~= nil and frame.args[2]
    local result = ""
    
    local theMission = p.getMission(MissionType, MissionCat)
    assert(theMission ~= nil, 'p.getSingleRotationRewards(frame): Could not get mission of type "'..MissionType..'" and category "'..MissionCat..'"')
    
    local data = getRewardsForMission(theMission)
    
    if(data ~= nil and Shared.tableCount(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
--	@function		p.getRewardTable
function p.getRewardTable(frame)
    local MissionType = frame.args ~= nil and frame.args[1]
    local MissionCat = frame.args ~= nil and frame.args[2]

    local result = ""
    
    if(MissionCat == "AllTier") then 
        result = p.getRewardTableAllTier(frame) 
        return result
    end
    
    local theMission = p.getMission(MissionType, MissionCat)
    assert(theMission ~= nil, 'p.getRewardTable(frame): Could not get mission of type "'..MissionType..'" and category "'..MissionCat..'"')
    
    local data = getRewardsForMission(theMission)
    local RotA = data["A"]
    local RotB = data["B"]
    local RotC = data["C"]
    
    --Goes through all three rotations to find which one has the most items
    local ACount = Shared.tableCount(RotA)
    local maxLen = ACount
    local BCount = Shared.tableCount(RotB)
    if(BCount > maxLen) then
        maxLen = BCount
    end
    local CCount = Shared.tableCount(RotC)
    if(CCount > maxLen) then
        maxLen = CCount
    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
        result = result.."\n|-"
        if(RotA[i] ~= nil) then
            result = result.."\n| align=\"right\" | "..formatDropString(RotA[i])
        else
            result = result.."\n| || "
        end
        if(RotB[i] ~= nil) then
            result = result.."\n| align=\"right\" | "..formatDropString(RotB[i])
        else
            result = result.."\n| || "
        end
        if(RotC[i] ~= nil) then
            result = result.."\n| align=\"right\" | "..formatDropString(RotC[i])
        else
            result = result.."\n| || "
        end
    end
    
    return result
end

---	Gets a list of all the missions for a given MissionType and Tier
--	@function		p.getMissionList
function p.getMissionList(frame)
    local MissionType = frame.args ~= nil and frame.args[1]
    local MissionTier = frame.args ~= nil and frame.args[2]
    
    local result = ""
    
    if(MissionTier == "AllTier") then 
        result = p.getMissionListAllTier(frame) 
        return result
    end
    
    local missionRecord = p.getMission(MissionType, MissionTier)
    
    assert(missionRecord ~= nil, 'p.getMissionList(frame): Could not figure out mission type "'..MissionType..'" and mission tier "'..MissionTier..'"')
    
    local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
    
    for _, m in pairs(missions) do
        result = result.."\n* "..m.Node..", [["..m.Planet.."]]"
    end
    
    return result
end

---	Gets a list of all the missions for a given MissionType and Tier
-- TODO: Rename this function as "p.getMissionList2" does not describe the functionality
--	@function		p.getMissionList2
function p.getMissionList2(frame)
    local MissionType = frame.args ~= nil and frame.args[1]
    local MissionTier = frame.args ~= nil and frame.args[2]
    
    local result = ""
    
    if(MissionTier == "AllTier") then 
        result = p.getMissionListAllTier(frame) 
        return result
    end
    
    local missionRecord = p.getMission(MissionType, MissionTier)
    
    assert(missionRecord ~= nil, 'p.getMissionList2(frame): Could not figure out mission type "'..MissionType..'" and mission tier "'..MissionTier..'"')
    
    local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
    
    for _, m in pairs(missions) do
        result = result..m.Node..', '..m.Planet..[[\n
]]
    end
    
    return result
end

---	WIP get missions regardless of tier
--	TODO: Finish this function or remove it entirely if not used
--	@function		p.getMissionAllTier
function p.getMissionAllTier(MissionType)
    local a={}
    for i, Miss in pairs(DropData["Missions"]) do
            if(Miss.Type == MissionType) then
                table.insert(a, Miss)
            end
    end
    return a
end

---	WIP Gets a the reward table of all Mission for a given MissionType (all tiers)
--	TODO: Finish this function or remove it entirely if not used
--	@function		p.getRewardTableAllTier
function p.getRewardTableAllTier(frame)
    local MissionType = frame.args ~= nil and frame.args[1]
    
    local theMissiont = p.getMissionAllTier(MissionType)
    
    assert(theMissiont ~= nil, 'p.getRewardTableAllTier(frame): No mission found with type "'..MissionType..'"')
    
    local result = ""
    local RotA = {}
    local RotB = {}
    local RotC = {}
    
    for i, theMission in pairs(theMissiont) do
        local data = getRewardsForMission(theMission)
        local RA = data["A"]
        local RB = data["B"]
        local RC = data["C"]
        --for k,v in pairs(dd) do data[k] = v end
        for k,v in pairs(RA) do table.insert(RotA, v) end
        for k,v in pairs(RB) do table.insert(RotB, v) end
        for k,v in pairs(RC) do table.insert(RotC, v) end
    end
    
    --Goes through all three rotations to find which one has the most items
    local ACount = Shared.tableCount(RotA)
    local maxLen = ACount
    local BCount = Shared.tableCount(RotB)
    if(BCount > maxLen) then
        maxLen = BCount
    end
    local CCount = Shared.tableCount(RotC)
    if(CCount > maxLen) then
        maxLen = CCount
    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
        result = result.."\n|-"
        if(RotA[i] ~= nil) then
            result = result.."\n| align=\"right\" | "..formatDropString(RotA[i])
        else
            result = result.."\n| || "
        end
        if(RotB[i] ~= nil) then
            result = result.."\n| align=\"right\" | "..formatDropString(RotB[i])
        else
            result = result.."\n| || "
        end
        if(RotC[i] ~= nil) then
            result = result.."\n| align=\"right\" | "..formatDropString(RotC[i])
        else
            result = result.."\n| || "
        end
    end
    
    return result
end

---	WIP Gets a list of all Mission for a given MissionType (all tiers)
--	TODO: Finish this function or remove it entirely if not used
--	@function		p.getMissionListAllTier
function p.getMissionListAllTier(frame)
    local MissionType = frame.args ~= nil and frame.args[1]

    local result = ""
    
    local missionRecordt = p.getMissionAllTier(MissionType)
    
    assert(missionRecordt ~= nil, 'p.getMissionListAllTier(frame): Could not figure out mission type "'..MissionType..'"')
    
    for i, missionRecord in pairs(missionRecordt) do
        local missions = p.getMissionTable(p.getMValue(missionRecord, "ALIAS"))
        for _, m in pairs(missions) do
            result = result.."\n* "..m.Node..", [["..m.Planet.."]]"
        end
    end

    return result
end

---	Get a list of all missions that drop a given item
--	@function		getDropMissions
local function getDropMissions(itemName)
    local Drops = {}
    
    --For each mission...
    for i, theMission in pairs(DropData["Missions"]) do
        --... if it has rewards...
        if(theMission.Rewards ~= nil and theMission.Ignore ~= true) then
            --... then for each rotation in the mission...
            for key, dropTable in Shared.skpairs(theMission.Rewards) do
                --... for each drop in the rotation...
                for j, drop in pairs(dropTable) do
                    -- ... if the drop is the right item, add it to the list
                    if(drop[misNameCol] == itemName) then
                        table.insert(Drops, buildMissionDrop(theMission, key, drop))
                    end
                end
            end
        end
    end
    
    return Drops
end

---	
--	@function		getDropSyndicates
local function getDropSyndicates(itemName)
    local Drops = {}
    
    for i, theSyndicate in pairs(DropData["Syndicates"]) do
        if(theSyndicate.Offerings ~= nil and theSyndicate.Ignore ~= true) then
            for i, Offering in pairs(theSyndicate.Offerings) do
                if(Offering[synNameCol] == itemName) then
                    table.insert(Drops, buildSyndicateDrop(theSyndicate, Offering))
                end
            end
        end
    end

    return Drops
end

---	Returns an EnemyDrop object for each enemy that drops a given mod
--	@function		getDropEnemies
local function getDropEnemies(itemName)
    local drops = {}
    
    for i, Enemy in pairs(DropData["Enemies"]) do
        if(Enemy.Mods ~= nil and Enemy.Ignore ~= true) then
            for j, Mod in pairs(Enemy.Mods) do
                if(Mod[modNameCol] == itemName) then
                    local drop = buildEnemyDrop(Enemy, Mod)
                    table.insert(drops, drop)
                end
            end
        end
        if(Enemy.Avionics ~= nil and Enemy.Ignore ~= true) then
            for j, Avionic in pairs(Enemy.Avionics) do
                if(Avionic[aviNameCol] == itemName) then
                    local drop = buildAvionicDrop(Enemy, Avionic)
                    table.insert(drops, drop)
                end
            end
        end
    end
    
    return drops
end

---	Gets the table used on Void Relic/ByMission
--	Unlike 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
function p.getRelicTable(frame)
    --Okay, so first up, need to know which planet this is for
    local Planet = nil
    if(frame ~= nil) then
        Planet = frame.args ~= nil and frame.args[1] or frame
    end
    --Planet == nil is standing in for 'all planets', so adding option to explicitly call 'all'
    if(Planet ~= nil and (Planet == "" or Planet == "All")) then
        Planet = nil
    end
    
    --I have other functions to get the list of missions for all/planet
    --So calling that here
    local missions
    if(Planet == nil) then
        missions = {}
        for i, m in pairs(DropData["Missions"]) do
            if(m.Ignore ~= true) then
                table.insert(missions, m)
            end
        end
    else
        missions = p.getMissionsForPlanet(Planet)
    end
    
    local tableRows = {}
    local Relics = {["Lith"] = {}, ["Meso"] = {}, ["Neo"] = {}, ["Axi"] = {}}
    
    --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 mAlias, theMission
        if(Planet == nil) then
            --When showing all, the format is "Mission Type (Tier Name)" with link to mission type
            --For example, "[[Survival]] (Tier 1)" or "Spy (Lua)"
            if (m.Type== "Bounty" or m.Type== "Ghoul Bounty" or m.Type== "Onslaught") then
                rowName = p.linkType(m.Type).." ("..p.getMValue(m, "SHORTNAME")..")"
            else
                local mType = p.linkType(m.Type)
                mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%] %(%[%[The Steel Path|Steel Path%]%]%)")
                rowName = mType.." ("..p.getMValue(m, "NAME")..")"
            end
            theMission = 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 == 1) then
                rowName = placeName.." ([[Dark Sector|DS]] "..p.linkType(m.Type)..")"
            else
                local mType = p.linkType(m.Type)
                mType = string.gsub(mType, "%[%[Cetus Bounty %(Steel Path%)%]%]", "%[%[The Steel Path|Steel Path%]%] %[%[Cetus Bounty|Bounty%]%]")
                mType = string.gsub(mType, "%[%[Cetus Bounty%]%]", "%[%[Cetus Bounty|Bounty%]%]")
                mType = string.gsub(mType, "%[%[Orb Vallis Bounty %(Steel Path%)%]%]", "%[%[The Steel Path|Steel Path%]%] %[%[Orb Vallis Bounty|Bounty%]%]")
                mType = string.gsub(mType, "%[%[Orb Vallis Bounty%]%]", "%[%[Orb Vallis Bounty|Bounty%]%]")
                local mLevel = ''
                if (placeName == 'Plains of Eidolon' or placeName == 'Orb Vallis') then
                    mLevel = '<br />'..string.gsub(m.MinLevel, "<br />", " ")
                end
                
                rowName = placeName..' ('..mType..')'..mLevel
            end
            theMission = p.getMission(m.Tier, nil, m.Type)
            assert(theMission ~= nil, 'p.getRelicTable(frame): Could not get mission of tier "'..m.Tier..'" and type "'..m.Type..'"')
        end
        local thisRow = nil
        --This is where we get all the rewards for the mission
        local drops = getRewardsForMission(theMission)
        
        --Need to know if this is a single rotation
        --Because if it is, just a checkmark instead of a letter
        local isSingleRot = Shared.tableCount(drops) == 1
        --For each mission, looping each rotation
        for rot, dropTable in Shared.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"
                    local RelicText = d.IName
                    local RadiantRelic = ""
                    if (string.find(RelicText,"%(Radiant%)")~=nil) then
                        RadiantRelic = "*"
                        RelicText = string.gsub(RelicText,"%s%(Radiant%)","")
                    end
                    
                    --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
                    if (isSingleRot) then
                        thisRow[RelicText] = "✔"..RadiantRelic
                    else
                        thisRow[RelicText] = thisRow[RelicText]..rot..RadiantRelic
                    end
                    
                    --Adding drop rate info when hovering
                    --If the drop rate is under 1% we set it red
                    --Under 2%, orangered
                    local RelicTextColor = "inherit"
                    if (d.Chance < 1) then
                        RelicTextColor = "red"
                    elseif (d.Chance < 2) then
                        RelicTextColor = "orangered"
                    end
                    thisRow[RelicText] = "<span style=\"color:" .. RelicTextColor .. ";\" title=\"Drop rate : " .. d.Chance .. "%\">" .. thisRow[RelicText] .. "</span>"
                        
                    
                    --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" '}
    table.insert(result, 'style="width:100%; border=1px; text-align:center; font-size:11px;"\n|-')
    --Slightly different text for all missions VS missions for a planet
    if(Planet == 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 Shared.relicLoop() do
        local relicCount = Shared.tableCount(Relics[tier])
        if(relicCount > 0) then
            table.insert(result, ('\n! colspan="'..relicCount..'" |'..tier))
            for rNum, trash in Shared.skpairs(Relics[tier]) do
                if not headerFirst then
                    table.insert(headerRow, " || ")
                end
                headerFirst = false
                table.insert(headerRow, (relicTooltipStart..tier..' '..rNum..relicTooltipCenter..'[['..tier..' '..rNum..'|'..rNum..']]'..relicTooltipEnd))
            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 Shared.skpairs(tableRows) do
        table.insert(result, ("\n|-\n|"..mName))
        for tier in Shared.relicLoop() do
            for rNum, rName in Shared.skpairs(Relics[tier]) do
                if(relicRow[rName] ~= nil) then
                    table.insert(result, ("||"..relicRow[rName]))
                else
                    table.insert(result, "|| ")
                end
            end
        end
    end

    --And the all-important coda
    table.insert(result, "\n|}")
    
    --And then ship it all back
    return table.concat(result)
end

---	Function used for building Void Relic/DropLocation table
--	TODO: Break up this function into smaller functions
--	@function		p.getRelicByLocation
function p.getRelicByLocation(frame)
    local tier = frame.args ~= nil and frame.args[1] or frame
    local tierPieces = String.split(tier, "%s")
    
    --5/26/2018: You can now call on a single relic and get just the table
    -- TODO: Use M:Table instead for table count/size
    if(Shared.tableCount(tierPieces) == 2) then
        tier = tierPieces[1]
        local relic = tierPieces[2]
        return p.getSingleRelicByLocation(tier, relic)
    end
    
    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 i, theMission in pairs(DropData["Missions"]) do
        if(theMission.Rewards ~= nil and theMission.Ignore ~= true) then
            
            for rot, dropTable in Shared.skpairs(theMission.Rewards) do
                for j, drop in pairs(dropTable) do
                    --When we find a relic drop, make sure it's for the right tier
                    if(drop[misTypeCol] == "Relic") then
                        --Example: {"Lith", "A1"}
                        local RelicBits = String.split(drop[misNameCol], "%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 = Void.getRelic(RTier, RName).Drops}
                            end
                            
                            --Then add this drop to the relic's table
                            table.insert(relicData[RName].Drops, buildMissionDrop(theMission, rot, drop))
                        end
                    end
                end
            end
        end
    end

    --Second, build the actual table being sent back
    local result = {}
    table.insert(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;"
!Type
!Category
!Rotation
!Chance]]

    for RName, RTable in Shared.skpairs(relicData) do
        local tierLink = "\n|-\n| ".."[["..tier.." "..RName.."]]"
        table.insert(result, tierLink)
        for i, reward in pairs(RTable.Rewards) do
            local ItemName = Void.getItemName(reward.Item)
            local PartName = Void.getPartName(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.MType == d2.MType) then
                if(p.getMValue(d1.theMission, 'Name') == p.getMValue(d2.theMission, 'Name')) then
                    return d1.Rotation < d2.Rotation
                else
                    return p.getMValue(d1.theMission, 'Name') < p.getMValue(d2.theMission, 'Name')
                end
            else
                return d1.MType < d2.MType
            end
        end)
        
        for i, d in pairs(RTable.Drops) do
            local mType = p.linkType(d.MType)
            mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%]<br />%(%[%[The Steel Path|Steel Path%]%]%)")
            local mTypeLink = "\n|-\n|"..mType
            table.insert(result, mTypeLink)
            if (d.MType == "Bounty" or d.MType == "Ghoul Bounty" or d.MType == "Onslaught") then
                local mNameLink = "||"..p.getMValue(d.theMission, 'ShortName')
                table.insert(result, mNameLink)
            else
                local mNameLink = "||"..p.getMValue(d.theMission, 'Name')
                table.insert(result, mNameLink)
            end
            local mRotC = "||"..d.Rotation.."||"..d.Chance.."%"
            table.insert(result, mRotC)
        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

---	TODO: Break up this function into smaller functions
--	@function		p.getSingleRelicByLocation
function p.getSingleRelicByLocation(tier, name)
    local relicData = {Drops = {}, Rewards = Void.getRelic(tier, name).Drops}
    local missionData = {}
    
    local result = ""
    
    --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 i, theMission in pairs(DropData["Missions"]) do
        if(theMission.Rewards ~= nil and theMission.Ignore ~= true) then
            for rot, dropTable in Shared.skpairs(theMission.Rewards) do
                for j, drop in pairs(dropTable) do
                    --When relic drop found, make sure it's for the right relic
                    if(drop[misTypeCol] == "Relic") then
                        --Example: {"Lith", "A1"}
                        local RelicBits = String.split(drop[misNameCol], "%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 and RName == name) then
                            --Then add this drop to the relic's table
                            table.insert(relicData.Drops, buildMissionDrop(theMission, rot, drop))
                        end
                    end
                end
            end
        end
    end

    --Second, build the actual table being sent back
    local result = ''
    result = "{| cellpadding=\"0\" cellspacing=\"0\" class=\"article-table sortable\" "
    result = result.."style=\"width:100%;border:1px solid black;"
    result = result.."text-align:left;font-size:12px;margin:12px 0 0 0;\""
    result = result.."\n!style=\"color:#3a3a3a;\"|Mission&nbsp;"
    result = result.."\n![[Mission_Rewards#Mission_Tier|<span style=\"color:#3a3a3a;\">Tier</span>]]&nbsp;"
    result = result.."\n![[Mission_Rewards#Mission_Rotations|<span style=\"color:#3a3a3a;\">Rotations</span>]]&nbsp;"
    result = result.."\n!style=\"color:#3a3a3a;\" data-sort-type=\"numeric\" | Chances&nbsp;"
        
    table.sort(relicData.Drops, function (d1, d2)
        if(d1.MType == d2.MType) then
            if(p.getMValue(d1.theMission, 'Name') == p.getMValue(d2.theMission, 'Name')) then
                return d1.Rotation < d2.Rotation
            else
                return p.getMValue(d1.theMission, 'Name') < p.getMValue(d2.theMission, 'Name')
            end
        else
            return d1.MType < d2.MType
        end
    end)

    local types = {}; local missions = {}
    local rotations = {}; local chances = {}
    
    for i, d in pairs(relicData.Drops) do
        types[i] = d.MType --p.linkType(d.MType)
        if (d.MType == "Onslaught") then
            missions[i] = p.getMValue(d.theMission, 'Tier')
        else
            missions[i] = p.getMValue(d.theMission, 'Name')
        end
        rotations[i] = d.Rotation
        chances[i] = d.Chance.."%"
    end
    
    local i = 1; while i <= table.getn(relicData.Drops) do
        if types[i] == types[i+1] then
            if missions[i] == missions[i+1] then
                rotations[i+1] = rotations[i]..",&nbsp;"..rotations[i+1]
                chances[i+1] = chances[i]..",&nbsp;"..chances[i+1]
                types[i] = ''; missions[i] = '';
                rotations[i] = ''; chances[i] = '';
            end
        end
        local num = string.gsub(chances[i],"%%","")
        local ChancesBits = String.split(num, ",&nbsp;")
        local highchance
        if (table.getn(ChancesBits) <= 1) or (ChancesBits == nil) then
                highchance = tonumber(num)
        else
            for j=1, (table.getn(ChancesBits) - 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
            local framepass={}
            framepass["args"]={types[i],missions[i]}
            local mlist= p.getMissionList2(framepass)
            local mType = p.linkType(types[i])
            mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%]<br />%(%[%[The Steel Path|Steel Path%]%]%)")
            
            result = result.."\n|-\n|style=\"padding:10px;\"|"..mType
            if(mlist == "") then
                result = result.."\n|style=\"padding:10px;\"|"..missions[i]
            else
                result = result.."\n|style=\"padding:10px;\"|<span style=\"border-bottom: 1px dotted;\" class=\"basic-tooltip\" title=\""..mlist.."\">"..missions[i].."</span>"
            end
            result = result.."\n|style=\"padding:10px;\"|"..rotations[i]
            result = result.."\n|style=\"padding:10px;\" data-sort-value=\""..highchance.."\"|"..chances[i]
        end
        i = i + 1
    end

    result = result.."\n|}"

    return result
end

---	
--	@function		p.getItemByMissionTable
function p.getItemByMissionTable(frame)
    local theDrop = frame.args ~= nil and frame.args[1] or frame
    
    local Drops = getDropMissions(theDrop)
    table.sort(Drops,   function (d1, d2)
                            return d1.MType < d2.MType
                        end)
    
    local rHeader 
    
    rHeader = "{| cellpadding=\"2\" cellspacing=\"0\" class=\"sortable\" "
    rHeader = rHeader.."style=\"width:100%;border:1px solid black; "
    rHeader = rHeader.."text-align:right;font-size:12px;\""
    rHeader = rHeader.."\n!Type"
    rHeader = rHeader.."\n!Category"
    rHeader = rHeader.."\n!Rotation"
    rHeader = rHeader.."\n!Chance"
    
        
    local rTable = rHeader
        
    for i, d in pairs(Drops) do
        rTable = rTable.."\n|-\n|"..p.linkType(d.MType).."||"..p.getMValue(d.theMission, 'NAME')
        rTable = rTable.."||"..d.Rotation.."||"..d.Chance.."%"
    end
    
    rTable = rTable.."\n|}"
    
    return rTable
end

---	
--	@function		p.getItemByEnemyTable
function p.getItemByEnemyTable(frame)
    local theDrop = frame.args ~= nil and frame.args[1] or frame
    
    local Drops = getDropEnemies(theDrop)
    table.sort(Drops,   function (d1, d2)
                            return d1.EName < d2.EName
                        end)
    
    local rHeader 
    
    rHeader = "{| cellpadding=\"2\" cellspacing=\"0\" class=\"sortable\" "
    rHeader = rHeader.."style=\"width:100%;border:1px solid black; "
    rHeader = rHeader.."text-align:right;font-size:12px;\""
    rHeader = rHeader.."\n!Enemy"
    rHeader = rHeader.."\n!Chance"
    
        
    local rTable = rHeader
        
    for i, d in pairs(Drops) do
        rTable = rTable.."\n|-\n|"..linkEnemy(d.EName)
        rTable = rTable.."||"..d.Chance.."%"
    end
    
    rTable = rTable.."\n|}"
    
    return rTable
end

---	Returns the list of drop locations used by the ModBox
--	TODO: Break up this function into smaller functions
--	@function		p.getItemDropList
function p.getItemDropList(frame)
    local theDrop = frame.args ~= nil and frame.args[1] or frame
    
    --little looparound for Scorch... add other exceptions after if needed
    if(theDrop == "Scorch (Mod)") then theDrop = "Scorch" end
    
    --First, get all the missions that drop this
    local Drops = getDropMissions(theDrop)
    table.sort(Drops,   function (d1, d2)
                            return d1.MType < d2.MType
                        end)
                        
    local checked = {}
    local result = ""
    local space = " "
    
    if(Shared.tableCount(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 = p.getMValue(Drop.theMission, 'ALIAS')
            local MissionName = p.getMValue(Drop.theMission, '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.MType] == nil) then
                    finalTable[Drop.MType] = {}
                end
                table.insert(finalTable[Drop.MType], 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 i, item in pairs(finalTable) do
            table.sort(item)
            local mType = p.linkType(i)
            mType = string.gsub(mType, " %(Steel Path%)%]%]", "%]%] %(%[%[The Steel Path|Steel Path%]%]%)")
            
            result = result.."<br/>"..mType
            
            local tierList = ""
            for j, alias in pairs(item) do
                local drop1 = checked[alias][1]
                local shortName = p.getMValue(drop1.theMission, 'SHORTNAME')
                if shortName ~= "" then
                    local ttip = p.getMValue(drop1.theMission, 'NAME', true)
                    
                    for k, thisDrop in pairs(checked[alias]) do
                        ttip = ttip..'\\nRotation '..thisDrop.Rotation
                        if (thisDrop.Chance ~= nil) then
                            ttip = ttip..': '..thisDrop.Chance.."%" 
                        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

    local Drops = getDropSyndicates(theDrop)
    if(Shared.tableCount(Drops) > 0) then
        table.sort(Drops,   function(d1, d2)
                                return d1.SName < d2.SName
                            end)
        
        if(string.len(result) > 0) then result = result.."<br/>" end
        result = result.."'''Syndicates:'''"
        for i, Drop in pairs(Drops) do
            result = result.."<br/>"..linkSyndicate(Drop.SName)..' '..Icon._Item("Standing","","x20")..' '..Shared.formatnum(Drop.Cost)
        end
    end

    --Then all the enemies are added to the list if there are any
    Drops = getDropEnemies(theDrop)
    if(Shared.tableCount(Drops) > 0) then
        table.sort(Drops,   function (d1, d2)
                                return d1.EName < d2.EName
                            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.EName)..space
            if (Drop.Type == "Avionic" and Drop.House ~= nil) then
                result = result.."("..Drop.House..")"..space
            end
            result = result..Drop.Chance.."%"
        end
    end
    
    return result
end

---	
--	@function		p.getItemByEnemyCount
function p.getItemByEnemyCount(frame)
    local theDrop = frame.args ~= nil and frame.args[1] or frame
    
    local Drops = getDropEnemies(theDrop)
    
    return Shared.tableCount(Drops)
end

---	
--	@function		p.getItemByMissionCount
function p.getItemByMissionCount(frame)
    local theDrop = frame.args ~= nil and frame.args[1] or frame
    
    local Drops = getDropMissions(theDrop)
    
    return Shared.tableCount(Drops)
end

---	
--	@function		p.getFullEnemyList
function p.getFullEnemyList(frame)
    local result = "All Enemies: "
    local ENames = {}
    for i, Enemy in pairs(DropData["Enemies"]) do
        if(Enemy.Name ~= nil) then
            table.insert(ENames, Enemy.Name)
        end
    end

    table.sort(ENames, function(n1, n2) return n1 < n2 end)
    for i, Name in pairs(ENames) do 
        result = result.."\n* "..linkEnemy(Name)
    end
    return result
end

---	
--	@function		p.getEnemyModDrops
function p.getEnemyModDrops(frame)
    local EnemyName = frame.args ~= nil and frame.args[1] or frame
    local Drops = getAllModDrops(EnemyName)
    
    if(Shared.tableCount(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.IName == "Endo") then
            if(Drop.Count<10) then
                result = result.."[[Endo]]"
            else
                result = result..Drop.Count.." [[Endo]]"
            end
        elseif(Drop.Type == "Avionic") then
            result = result..getAvionicLink(Drop.IName, Drop.House)
        else
            result = result..getModLink(Drop.IName)
        end
        result = result..space..Drop.Chance.."%"
    end
    return result
end
 
 -- TODO: Remove this function or move it to /validate subpage of /data
function p.TestFrame(frame)
    local txt = "VALUES:\n\n"
    for key, val in Shared.skpairs(p) do
        if(val ~= nil) then
            if(type(val) == "string" or type(val) == "number") then
                txt = txt..'\n* '..key..' = '..val
            else
                txt = txt..'\n* '..key..' is a '..type(val)
            end
        else
            txt = txt..'\n* '..key..' is nil'
        end
    end
    return txt
end

return p