Wiki Warframe

Utilisation du Wiki

Pour une meilleure utilisation du Wiki Warframe FR, merci d'activer les scripts Javascript lors de la consultation de certains pages, pour assurer le fonctionnement de certaines fonctionnalités.
Si vous souhaitez contribuer au Wiki, rejoignez-nous sur le Discord Warframe FR (espace wiki)

- Yazu 17:00 01/10/2022

EN SAVOIR PLUS

Wiki Warframe
Aucun résumé des modifications
Aucun résumé des modifications
 
(181 versions intermédiaires par 4 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
  +
--- '''Weapons''' contient toutes les [[Armes]] de [[WARFRAME]].
  +
--
  +
-- @module weapons
  +
-- @alias p
  +
-- @attribution [[User:Cephalon Scientia|Cephalon Scientia]]
  +
-- @attribution [[User:FINNER|FINNER]]
  +
-- @attribution [[User:Falterfire|Falterfire]]
  +
-- @attribution [[User:Gigamicro|Gigamicro]]
  +
-- @attribution [[User:Flaicher|Flaicher]]
  +
-- @attribution [[User:Synthtech|Synthtech]]
  +
-- @attribution [[User:Yazuh|Yazu]] (retranscription)
  +
-- @image IconPrimaryWeaponRifle.png
  +
-- @require [[Module:StatObject]]
  +
-- @require [[Module:DamageTypes]]
  +
-- @require [[Module:Polarity]]
  +
-- @require [[Module:Math]]
  +
-- @require [[Module:Table]]
  +
-- @require [[Module:Tooltips]]
  +
-- @require [[Module:Version]]
  +
-- @require [[Module:Stances/data]]
  +
-- @require [[Module:Weapons/data]]
  +
-- @require [[Module:Weapons/Conclave/data]]
  +
-- @release stable
  +
-- <nowiki>
  +
  +
-- TODO: Add LuaDoc style comments to new functions
 
local p = {}
 
local p = {}
   
local WeaponData = mw.loadData('Module:Weapons/data')
+
local Delay = require([[Module:Delay]])
local ConclaveData = mw.loadData('Module:Weapons/Conclave/data')
+
local WeaponData = Delay.require([[Module:Weapons/data]])
local Icon = require("Module:Icon")
+
local ConclaveData = Delay.require([[Module:Weapons/Conclave/data]])
local Shared = require("Module:Shared")
+
local Tooltip = Delay.require([[Module:Tooltips]]) -- full, icon
local Mod = require('Module:Mods')
+
local Version = Delay.require([[Module:Version]]) -- _getVersion, _getVersionDate
local Tooltip = require("Module:Tooltip")
+
local Polarity = Delay.require([[Module:Polarity]]) -- _pols, _polarity
  +
local Math = Delay.require([[Module:Math]]) -- formatnum
  +
local Table = Delay.require([[Module:Table]]) -- size, skpairs
  +
local iterationOrderArray = require([[Module:DamageTypes]]).iterationOrderArray
   
  +
-- TODO: Should decouple from localized names for internationalization
local Elements = {
 
  +
local VARIANT_LIST = {
"Impact", "Perforation", "Tranchant", "Feu", "Glace", "Poison",
 
"Électrique", "Explosif", "Corrosif", "Radiation", "Magnétique", "Gaz",
+
"Prime", "Prisma", "Wraith", "Vandal", "Vaykor", "Synoid", "Telos", "Secura",
  +
"Sancti", "Rakta", "Mara", "Carmine", "Ceti", "Dex", "MK1", "Kuva", "Principe"
"Viral"
 
}
 
local Physical = {"Impact", "Perforation", "Tranchant"}
 
local UseDefaultList = {
 
"NOISELEVEL", "AMMOTYPE", "MAXAMMO", "DISPOSITION", "CHANNELMULT",
 
"HEADSHOTMULTIPLIER"
 
}
 
local VariantList = {
 
"Prime", "Prisma", "Wraith", "Vandal", "Vaykor", "Synoid", "Telos",
 
"Secura", "Sancti", "Rakta", "Mara", "MK1"
 
 
}
 
}
   
  +
table.unpack = table.unpack or unpack
-- =======================
 
-- ===== FILTERS =====
 
-- =======================
 
   
  +
local StatObject = require [[Module:StatObject]]
local function weaponFilter_Melee(weap, mainCat, subCat)
 
  +
p.__StatObject = StatObject
return weap.Type == mainCat and weap.Class == subCat
 
  +
local statRead = StatObject.statRead
end
 
  +
local statFormat = StatObject.statFormat
   
  +
local indexes = StatObject.meta.indexes
local function weaponFilter_NonMelee(weap, mainCat, subCat, triggerType)
 
  +
local ors = StatObject.meta.ors
if (triggerType ~= nil and triggerType == "Hybride") then
 
  +
local unpacks = StatObject.meta.unpacks
return
 
weap.Type == mainCat and (subCat == nil or weap.Class == subCat) and
 
Shared.contains(weap.Trigger, '/')
 
else
 
return
 
weap.Type == mainCat and (subCat == nil or weap.Class == subCat) and
 
(triggerType == nil or weap.Trigger == triggerType)
 
end
 
end
 
   
  +
local passes = StatObject.meta.passes
-- =======================
 
  +
local percent = StatObject.meta.percent
-- ===== OTHERS =====
 
  +
local percents = StatObject.meta.percents
-- =======================
 
   
  +
--- Gets the attack entry from weapon entry.
function p.doPlural(Text, Value)
 
  +
-- @function p._getAttack
if (tonumber(Value) == 1) then
 
  +
-- @param {table} weap Weapon entry
Text = string.gsub(Text, "(<.+>)", "")
 
  +
-- @param[opt] {number|table} atk Attacks table index or Attack entry
else
 
  +
-- @return {table} A single weapon+attack struct
Text = string.gsub(Text, "<(.+)>", "%1")
 
  +
local function getWeaponAttack(weap, atk)
end
 
  +
if type(atk) == 'number' then return StatObject.getStruct2(weap,weap.Attacks[atk]) end
return Text
 
  +
if weap.AttackName then return weap end
  +
if type(atk) == 'table' then return StatObject.getStruct2(weap,atk) end
  +
local key = atk or weap['_TooltipAttackDisplay'] or 1
  +
if weap.Attacks == nil then
  +
error('p._getWeaponAttack(weap, atk): Attacks table is nil in '..mw.dumpObject(weap))
  +
end
  +
return StatObject.getStruct2(weap,weap.Attacks[key])
 
end
 
end
  +
p._getAttack = getWeaponAttack
  +
p._getWeaponAttack = getWeaponAttack
   
function p.isVariant(WeapName)
+
function p._statRead(w, a, ...)
  +
return statRead(getWeaponAttack(w, a), ...)
for i, var in pairs(VariantList) do
 
if (string.find(WeapName, var)) then
 
local baseName = string.gsub(WeapName, ' ?' .. var .. ' ?-?', '')
 
return true, var, baseName
 
end
 
end
 
 
return false, 'Base', WeapName
 
 
end
 
end
  +
function p._statFormat(w, a, ...)
 
  +
return statFormat(getWeaponAttack(w, a), ...)
function p.buildName(BaseName, Variant)
 
  +
end
if (Variant == nil or Variant == 'Base' or Variant == '') then
 
  +
function p.stat(frame)
return BaseName
 
  +
return p._statFormat(p._getWeapon(frame.args[1] or 'Skana Prime'), nil, frame.args[2] or 'Name')
elseif (Variant == "MK1") then
 
return "MK1-" .. BaseName
 
else
 
return BaseName .. ' ' .. Variant
 
end
 
 
end
 
end
   
  +
-- Wrapper function for use in StatObject
-- It's a bit of a mess, but this is for compressing a list with variants
 
  +
local function dmgTooltip(damageType)
-- So if a list has Braton, Braton Prime, and MK1-Braton it'll list as
 
  +
return Tooltip.full(damageType, 'DamageTypes')
-- Braton (MK1, Prime)
 
function p.shortLinkList(Weapons)
 
-- First grabbing all the pieces and stashing them in a table
 
local baseNames = {}
 
for key, weap in Shared.skpairs(Weapons) do
 
local isVar, varType, baseName = p.isVariant(weap.Name)
 
if (baseNames[baseName] == nil) then baseNames[baseName] = {} end
 
table.insert(baseNames[baseName], varType)
 
end
 
 
-- Then the fun part: Pulling the table together
 
local result = {}
 
for baseName, variants in Shared.skpairs(baseNames) do
 
-- So first, check if 'Base' is in the list
 
-- Because if it isn't, list all variants separately
 
if (Shared.contains(variants, "Base")) then
 
table.sort(variants)
 
-- First, get the basic version
 
local thisRow = Tooltip._tooltipText(baseName, 'Weapon')
 
-- then, if there are variants...
 
if (Shared.tableCount(variants) > 1) then
 
-- List them in parentheses one at a time
 
thisRow = thisRow .. ' ('
 
local count = 0
 
for i, varName in pairs(variants) do
 
if (varName ~= 'Base') then
 
if (count > 0) then
 
thisRow = thisRow .. ', '
 
end
 
thisRow = thisRow ..
 
Tooltip._tooltipText(
 
p.buildName(baseName, varName),
 
'Weapon', varName)
 
count = count + 1
 
end
 
end
 
thisRow = thisRow .. ')'
 
end
 
table.insert(result, thisRow)
 
else
 
for i, varName in pairs(variants) do
 
table.insert(result, Tooltip._tooltipText(
 
p.buildName(baseName, varName), 'Weapon'))
 
end
 
end
 
end
 
return result
 
 
end
 
end
   
  +
-- Defining getters/attributes whose names match the associated database key or some custom derived attribute.
function p.getWeapon(WeapName)
 
  +
-- Index key will be name of getter function and can be mapped to a single value (getter definition)
local weapon = WeaponData["Weapons"][WeapName]
 
  +
-- or a table with two values (getter and format function definitions)
   
  +
-- Cheatsheet on adding new keys:
if weapon ~= nil and weapon.Name == WeapName then
 
  +
-- StatName = default value -> Get raw value with the same StatName from M:Weapons/data and with no additional formatting (aka default formatting)
return weapon
 
  +
-- StatName = function(self) return self.StatName + 1 end -> Define custom getter function and use default formatting
else
 
  +
-- StatName = { default value, '%.2f' } -> Get raw value value with same StatName from M:Weapons/data and use format string for formatting
for key, Weapon in Shared.skpairs(WeaponData["Weapons"]) do
 
  +
-- StatName = { function(self) return ... end, '%.2f' } -> Define custom getter function and use format string for formatting
if (Weapon.Name == WeapName or key == WeapName) then
 
  +
-- StatName = { function(self) return ... end, function(self, returnValue1, returnValue2, ...) return tostring(returnValue) end } - > Define custom getter and format functions
return Weapon
 
  +
-- (Note that format function will pass in return value(s) from getter as well as object self)
end
 
end
 
end
 
   
  +
-- TODO: Put StatObject keys in alphabetical order for navigation
return nil
 
  +
StatObject.default = {
  +
AttackName = 'Normal Attack',
  +
AmmoCost = nil,
  +
AmmoPickup = function(weapAtk)
  +
return weapAtk['AmmoPickup'] or
  +
weapAtk['Slot'] == 'Principale' and 80 or
  +
weapAtk['Slot'] == 'Secondaire' and 40 or
  +
weapAtk['Slot'] == 'Arch-Fusil (Atmosphère)' and 1000 or
  +
0
  +
end,
  +
DamageBias = {
  +
function(weapAtk)
  +
if not weapAtk.Damage then
  +
error('DamageBias: no Attack.Damage')
  +
return 0, 0, 0
  +
end
  +
local total, bestdmg, bestdt = 0, 0, nil
  +
for dt, dmg in pairs(weapAtk.Damage) do
  +
local dmg = dmg
  +
if dmg >= bestdmg then
  +
bestdmg, bestdt = dmg, dt
  +
end
  +
total = total + dmg
  +
end
  +
return StatObject.ucacheIn(weapAtk, 'DamageBias', { bestdmg / total, bestdt, total })
  +
end,
  +
{ percent, passes(dmgTooltip), '' }
  +
},
  +
BiasPortion = { indexes('DamageBias', 1), percent },
  +
BiasType = { indexes('DamageBias', 2), function(self, biasType) return Tooltip.icon(biasType, 'DamageTypes') end },
  +
BaseDamage = { indexes('DamageBias', 3), '%.2f' },
  +
-- More precise damage values to 4 decimal places for PvP since PvP damage is calculated
  +
-- based on a floating-point scalar. Damage quantization is more relevant in PvP so more
  +
-- precise numbers needed.
  +
PvPBaseDamage = { indexes('DamageBias', 3), '%.4f' },
  +
TotalDamage = { function(weapAtk)
  +
return statRead(weapAtk, 'BaseDamage') * statRead(weapAtk, 'Multishot')
  +
end, passes(Math.formatnum)
  +
},
  +
-- Including max +60% Progenitor bonus for Kuva/Tenet weapons
  +
TotalDamageWithProgenBonus = { function(weapAtk)
  +
return statRead(weapAtk, 'TotalDamage') * (statRead(weapAtk, 'IsLichWeapon') and 1.6 or 1)
  +
end, passes(Math.formatnum)
  +
},
  +
ChargeTime = { 0, '%.1f s' },
  +
ExplosionDelay = { 0, '%.1f s' },
  +
ExtraHeadshotDmg = { 0, percents('+%.2f%%') },
  +
Falloff = {
  +
function(weapAtk)
  +
local fo = weapAtk['Falloff'] or {}
  +
return fo.StartRange or 0, fo.EndRange or math.huge, 1 - (fo.Reduction or 1)
  +
end,
  +
{ '%.1f m (100%%) -', '%.1f m', percents('(%.2f%%)') }
  +
},
  +
FalloffStart = { indexes('Falloff', 1), '%.1f m' },
  +
FalloffEnd = { indexes('Falloff', 2), '%.1f m' },
  +
-- Damage reduction from falloff instead of damage multiplier
  +
FalloffReduction = { function(weapAtk)
  +
local _, _, falloff = statRead(weapAtk, 'Falloff')
  +
return 1 - falloff
  +
end, percent
  +
},
  +
FalloffRate = { function(weapAtk)
  +
local startdist,enddist,endpercent = statRead(weapAtk, 'Falloff')
  +
return -(enddist-startdist)/(endpercent-1)
  +
end, '%.1fm/%%'
  +
},
  +
HeadshotMultiplier = { 1, '%.1fx' },
  +
Multishot = 1,
  +
PunchThrough = { 0, '%.1f m' },
  +
ShotSpeed = { nil, function(self, shotSpeed)
  +
if shotSpeed == nil or "?" then
  +
return 'N/A'
  +
end
  +
return ('%.1f m/s'):format(shotSpeed)
 
end
 
end
  +
},
 
  +
BurstDelay = { 0, '%.4f s' },
local function checkWeapon(weap, weapName, conclave)
 
  +
BurstReloadDelay = { 0, '%.2f s' },
if weap == nil or type(weap) ~= 'table' then
 
  +
BurstsPerSec = { function(weapAtk)
if conclave then
 
  +
-- There is no delay after last shot in burst
return p.getConclaveWeapon(weapName)
 
  +
return 1 / ( (1 / statRead(weapAtk, 'FireRate') ) + statRead(weapAtk, 'BurstDelay') * ( statRead(weapAtk, 'BurstCount') - 1) )
else
 
  +
end, '%.2f rafales/sec' },
return p.getWeapon(weapName)
 
  +
CritChance = { 0, percent },
end
 
  +
CritMultiplier = { 1, '%.2fx' },
elseif type(weap) == 'table' then
 
  +
ForcedProcs = { unpacks('ForcedProcs'), function(s, ...)
return weap
 
  +
local procs = { ... }
end
 
  +
if procs[1] == nil then
  +
return 'Aucun statut forcé'
  +
end
  +
local result = {}
  +
for _, proc in ipairs(procs) do
  +
table.insert(result, Tooltip.full(proc, 'DamageTypes'))
  +
end
  +
return table.concat(result, ', ')
 
end
 
end
  +
},
 
  +
Radius = { 0, '%.1f m' },
function p.weaponExists(frame)
 
  +
StatusChance = { 0, percent },
local weapName = frame.args ~= nil and frame.args[1] or nil
 
  +
Disposition = {
 
  +
function(weap)
return p._weaponExists(weapName)
 
  +
local d = weap['Disposition']
  +
-- Returning a categorical bin value of 1, 2, 3, 4, or 5 based on where disposition value
  +
-- is on the continuous scale of 0.5-1.55. If disposition value is nil then return 0
  +
return d or 0, type(d)=='number' and math.floor(5*(d-.3+.009*(d<1 and 1 or -1))) or 0
  +
end,
  +
function(s, v, d)
  +
return StatObject.default.Dispo[2](s, d)..(' (%.2fx)'):format(v)
  +
end
  +
},
  +
Dispo = { indexes('Disposition', 2), function(s, d)
  +
if d and d == d and d > 0 then
  +
return ('●'):rep(math.min(d, 5))..('○'):rep(5 - d)
  +
end
  +
return '×××××' -- '●○×' --> E2978F E2978B C397
  +
end },
  +
Introduced = { function(weap)
  +
return weap['Introduced'] and Version._getVersion(weap['Introduced'])['Name'] or 'N/A'
  +
end, passes(Version._getVersionLink)
  +
},
  +
IntroducedDate = function(weap)
  +
return weap['Introduced'] and Version._getVersionDate(weap['Introduced']) or 'N/A'
  +
end,
  +
IsLichWeapon = function(weap)
  +
return weap['IsLichWeapon'] and true or false
  +
end,
  +
Mastery = 0,
  +
Link = { nil, '[[%s]]' },
  +
Name = { nil, function(s, v) return Tooltip.full(v, 'Weapons') end },
  +
InternalName = '',
  +
NameLink = { function(weap) return weap.Link, weap.Name end, '[[%s|%s]]' },
  +
Polarities = { nil, passes(Polarity._pols) },
  +
Traits = { unpacks('Traits'), { sep = ', ' } },
  +
-- Default nil b/c some attacks don't have an associated accuracy/spread value (like AoE explosions)
  +
Accuracy = { nil, function(self, value)
  +
if (value == nil) then
  +
return 'N/A'
  +
end
  +
return value
 
end
 
end
  +
},
 
  +
-- Inverse of accuracy. Spread of 1 equates to no spread.
function p._weaponExists(weapName, weap)
 
  +
-- Alternatively, it can be calculated by the average of min and max spread, see AvgSpread getter.
local weap = checkWeapon(weap, weapName)
 
  +
Spread = { function(weapAtk)
 
  +
local accuracy = statRead(weapAtk, 'Accuracy')
if weapName == 'Épées Versatiles Sombres' then
 
  +
return (accuracy == nil) and nil or 100 / accuracy
return true
 
  +
end, function(self, value)
elseif weap then
 
  +
if (value == nil) then
return true
 
  +
return 'N/A'
end
 
  +
end
 
  +
return value
if weapName == nil then
 
return 'Enter weapon name.'
 
elseif weap == nil then
 
return 'No weapon ' .. weapName .. ' found.'
 
end
 
 
 
end
 
end
  +
},
 
  +
AmmoType = function(weapAtk)
function p.getLink(frame)
 
  +
return weapAtk['AmmoType'] or ({
local weapName = frame.args ~= nil and frame.args[1] or nil
 
  +
['Arch-Fusil (Atmosphère)'] = 'Lourde',
if weapName == nil then return 'Enter weapon name.' end
 
  +
['Secondaire'] = 'Secondaire',
 
  +
['Principale'] = 'Principale'
return p._getLink(weapName)
 
  +
})[weapAtk['Slot']] or 'None'
  +
end,
  +
-- Not all weapons have an Exilus slot so default to nil
  +
ExilusPolarity = { nil, function(self, exilusPolarity)
  +
if (exilusPolarity == nil) then
  +
return 'N/A'
  +
end
  +
return Polarity._polarity(exilusPolarity)
 
end
 
end
  +
},
 
  +
Magazine = 1,
function p._getLink(weapName, weap)
 
  +
AmmoMax = { function(weapAtk)
local weap = checkWeapon(weap, weapName)
 
  +
if statRead(weapAtk, 'IsMelee') then
local exists = p._weaponExists(weapName, weap)
 
  +
return nil
if weapName == "Épées Versatiles Sombres" then
 
  +
end
return "Épées Versatiles Sombres"
 
  +
return weapAtk['AmmoMax'] or math.huge
elseif exists == true then
 
  +
end, passes(Math.formatnum)
local temp = weap.Link
 
  +
},
if weap.Type == "Arch-Fusil (Atmosphère)" then
 
  +
Range = { function(weapAtk)
local atmoTemp = Shared.splitString(weap.Name, "%s")
 
  +
return weapAtk['Range'] or statRead(weapAtk, 'ShotType') == 'Impact-direct' and 300 or 0
local atmoCount = Shared.tableCount(atmoTemp)
 
  +
end, '%.1f m'
if atmoCount == 2 then
 
  +
},
return atmoTemp[1]
 
  +
Reload = { ors('Reload', 'RechargeTime', 0), '%.2f s' },
elseif atmoCount >= 2 then
 
  +
RechargeTime = { function(weapAtk)
return table.concat(atmoTemp, ' ', 1, (atmoCount - 1))
 
  +
return statRead(weapAtk, 'ReloadStyle'):find'[Rr]egen' and statRead(weapAtk, 'Magazine') / statRead(weapAtk, 'ReloadRate') or nil
end
 
  +
end, '%.2f s'
return weap.Name
 
  +
},
elseif temp then
 
  +
ReloadRate = { 0, '%.2f balles/sec' }, -- Used for rechargeable weapons; not necessarily inverse of reload time b/c of presence of reload delay
return temp
 
  +
ReloadDelay = { function(weapAtk)
else
 
  +
return weapAtk['ReloadDelay'] or 0
return weap.Name
 
  +
end, '%.2f s'
end
 
  +
},
elseif weapName == nil then
 
  +
ReloadDelayEmpty = { ors('ReloadDelayEmpty', 'ReloadDelay'), '%.2f s' },
return false
 
  +
-- Reload speed will be calculated as the inverse of reload time for purposes
else
 
  +
-- of keeping how we rank stats consistent for [[Module:Weapons/preprocess]]
return false
 
  +
-- (larger number = higher stat; a short reload time can be expressed as fast reload
end
 
  +
-- speed which would be a larger value in magnitude)
  +
ReloadSpeed = { function(weapAtk)
  +
return 1 / statRead(weapAtk, 'Reload')
  +
end, function(str, reloadSpeed)
  +
return string.format('%.2f%% progression de recharge par seconde', reloadSpeed * 100)
  +
end },
  +
ReloadStyle = 'Magazine',
  +
Spool = { 0, '%d balles' },
  +
SpoolStartFireRate = { 0, '%.1fx' }, -- scalar that is applied to fire rate stat for auto-spool weapons
  +
AvgSpread = { function(weapAtk)
  +
local minSpread = statRead(weapAtk, 'MinSpread')
  +
local maxSpread = statRead(weapAtk, 'MaxSpread')
  +
if (minSpread == nil) then
  +
return nil
  +
end
  +
return (minSpread + maxSpread) / 2
  +
end, function(self, value)
  +
if (value == nil) then
  +
return 'N/A'
  +
end
  +
return ('%.2f°'):format(value)
  +
end
  +
},
  +
-- Default nil b/c some attacks don't have an associated accuracy/spread value (like AoE explosions)
  +
MinSpread = { nil, function(self, value)
  +
if (value == nil) then
  +
return 'N/A'
  +
end
  +
return ('%.2f°'):format(value)
  +
end
  +
},
  +
MaxSpread = { nil, function(self, value)
  +
if (value == nil) then
  +
return 'N/A'
  +
end
  +
return ('%.2f°'):format(value)
  +
end
  +
},
  +
Trigger = 'N/A',
  +
BlockAngle = { 0, '%d&#176;' },
  +
ComboDur = { 0, '%.1f s' },
  +
FollowThrough = { 0, '%.1fx' },
  +
HeavyAttack = { 0, passes(Math.formatnum) },
  +
HeavySlamAttack = { 0, passes(Math.formatnum) },
  +
HeavyRadialDmg = { 0, passes(Math.formatnum) },
  +
HeavySlamRadius = { 0, '%.1f m' },
  +
MeleeRange = { 0, '%.2f m' },
  +
SlamAttack = { 0, passes(Math.formatnum) },
  +
SlamRadialDmg = { function(weapAtk)
  +
return weapAtk.SlamRadialDmg or 0, statRead(weapAtk, 'SlamRadialElement')
  +
end, function(self, dmg, elem)
  +
if elem then
  +
return Tooltip.icon(elem, 'DamageTypes')..' '..Math.formatnum(dmg)
  +
end
  +
return Math.formatnum(dmg)
 
end
 
end
  +
},
 
  +
SlamRadialElement = { nil, function(self, value)
function p.getConclaveWeapon(WeapName)
 
  +
return value ~= nil and Tooltip.full(value, 'DamageTypes') or 'Même répartition des types de dégâts que pour l\'attaque normale'
local weapon = ConclaveData["Weapons"][WeapName]
 
 
if weapon ~= nil and weapon.Name == WeapName then
 
return weapon
 
else
 
for key, Weapon in Shared.skpairs(ConclaveData["Weapons"]) do
 
if (Weapon.Name == WeapName or key == WeapName) then
 
return Weapon
 
end
 
end
 
end
 
 
return nil
 
 
end
 
end
  +
},
 
  +
-- Slam radial forced proc(s)
function p.isMelee(frame)
 
  +
SlamRadialProcs = { nil, function(self, proc)
if (frame == nil) then return nil end
 
  +
if type(proc)=='table' then
local Weapon = frame.args ~= nil and frame.args[1] or frame
 
  +
local result = {}
if (type(Weapon) == "string") then Weapon = p.getWeapon(Weapon) end
 
  +
for _, elem in ipairs(proc) do
 
  +
table.insert(result, Tooltip.full(elem, 'DamageTypes'))
if (Weapon == nil) then return nil end
 
  +
end
 
  +
return table.concat(result, '<br />')
if (Weapon.Type ~= nil and
 
  +
else
(Weapon.Type == "Mêlée" or Weapon.Type == "Arch-Mêlée")) then
 
return "yes"
+
return 'N/A'
end
+
end
 
return nil
 
 
end
 
end
  +
},
 
  +
SlamRadius = { 0, '%.1f m' },
function p.isArchwing(frame)
 
  +
SlideAttack = { function(weapAtk)
if (frame == nil) then return nil end
 
  +
return weapAtk.SlamRadialDmg or 0, statRead(weapAtk, 'SlideElement')
local Weapon = frame.args ~= nil and frame.args[1] or frame
 
  +
end, function(self, dmg, elem)
if (type(Weapon) == "string") then Weapon = p.getWeapon(Weapon) end
 
  +
if elem then
 
  +
return Tooltip.icon(elem, 'DamageTypes')..' '..Math.formatnum(dmg)
if (Weapon == nil) then return nil end
 
  +
end
 
  +
return Math.formatnum(dmg)
if (Weapon.Type ~= nil and
 
(Weapon.Type == "Arch-Fusil" or Weapon.Type == "Arch-Mêlée")) then
 
return "yes"
 
end
 
 
return nil
 
 
end
 
end
  +
},
 
  +
SlideElement = { nil, function(self, value)
local function getAttack(Weapon, AttackType)
 
  +
return value ~= nil and Tooltip.full(value, 'DamageTypes') or 'Même répartition des types de dégâts que pour l\'attaque normale'
if (Weapon == nil or AttackType == nil) then
 
return nil
 
elseif (type(Weapon) == "string") then
 
Weapon = p.getWeapon(Weapon)
 
end
 
AttackType = string.upper(AttackType)
 
if (AttackType == nil or AttackType == "NORMAL" or AttackType ==
 
"NORMALATTACK") then
 
if (Weapon.NormalAttack ~= nil) then
 
return Weapon.NormalAttack
 
elseif (Weapon.Damage ~= nil) then
 
return Weapon
 
else
 
return nil
 
end
 
elseif (AttackType == "CHARGE" or AttackType == "CHARGEATTACK") then
 
return Weapon.ChargeAttack
 
elseif (AttackType == "AREA" or AttackType == "AREAATTACK") then
 
return Weapon.AreaAttack
 
elseif (AttackType == "SECONDARYAREA" or AttackType == "SECONDARYAREAATTACK") then
 
return Weapon.SecondaryAreaAttack
 
elseif (AttackType == "THROW" or AttackType == "THROWATTACK") then
 
return Weapon.ThrowAttack
 
elseif (AttackType == "CHARGEDTHROW" or AttackType == "CHARGEDTHROWATTACK") then
 
return Weapon.ChargedThrowAttack
 
elseif (AttackType == "SECONDARY" or AttackType == "SECONDARYATTACK") then
 
return Weapon.SecondaryAttack
 
elseif (AttackType == "MELEESLAM") then
 
return Weapon.MeleeSlam
 
elseif (AttackType == "MELEEHEAVY") then
 
return Weapon.MeleeHeavy
 
else
 
return nil
 
end
 
 
end
 
end
  +
},
 
local function hasAttack(Weapon, AttackType)
+
--[[Stances = function(weapAtk)
  +
if not statRead(weapAtk, 'IsMelee') then return end
if (getAttack(Weapon, AttackType) ~= nil) then
 
  +
return Stances._getAllStancesSameType(statRead(weapAtk, "Class"))
return true
 
  +
-- ^ currently a local function
else
 
  +
end,--]]
return nil
 
  +
-- Not all weapons have an Stance slot so default to nil
end
 
  +
StancePolarity = { nil, function(self, stancePolarity)
  +
if (stancePolarity == nil) then
  +
return 'N/A'
  +
end
  +
return Polarity._polarity(stancePolarity)
 
end
 
end
  +
},
  +
SweepRadius = { 0, '%.2f m' },
  +
WindUp = { 0, '%.1f s' },
  +
BurstCount = 1,
  +
-- Average crit/proc count from a single attack input
  +
AvgCritCount = function(weapAtk)
  +
return statRead(weapAtk, 'CritChance') * statRead(weapAtk, 'Multishot')
  +
end,
  +
AvgCritPerSec = function(weapAtk)
  +
return statRead(weapAtk, 'AvgCritCount') * statRead(weapAtk, 'EffectiveFireRate')
  +
end,
  +
AvgProcCount = function(weapAtk)
  +
return ( statRead(weapAtk, 'StatusChance') + Table.size(weapAtk['ForcedProcs'] or {}) ) * statRead(weapAtk, 'Multishot')
  +
end,
  +
AvgProcPerSec = function(weapAtk)
  +
return statRead(weapAtk, 'AvgProcCount') * statRead(weapAtk, 'EffectiveFireRate')
  +
end,
  +
InterShotTime = function(weapAtk)
  +
local v = statRead(weapAtk, 'Magazine') == 1 and statRead(weapAtk, 'Reload') + statRead(weapAtk, 'ReloadDelayEmpty') or 0
  +
if v == 0 then v = 1 / statRead(weapAtk, 'FireRate') end
  +
return v
  +
end,
  +
EffectiveFireRate = function(weapAtk)
  +
return 1 / ( statRead(weapAtk, 'ChargeTime') + statRead(weapAtk, 'InterShotTime') )
  +
end,
  +
ShotsPerMag = function(weapAtk)
  +
-- Default to 1 "ammo cost" even if attack does not directly consume ammo (e.g. AoE hits, speargun throws, etc.)
  +
return math.floor(statRead(weapAtk, 'Magazine') / (statRead(weapAtk, 'AmmoCost') or 1))
  +
end,
  +
FireRate = { function(weapAtk)
  +
local dataFireRate = weapAtk['FireRate']
  +
if dataFireRate then return dataFireRate end
  +
-- TODO: Think we can safely remove this calculation of FireRate from BurstFireRate, BurstDelay, and BurstCount
  +
-- for burst-fire attacks since FireRate is also included for those
  +
mw.log('calcul de la cadence de tir depuis les stats de rafales pour '..statRead(weapAtk, 'Name'))
  +
local count = statRead(weapAtk, 'BurstCount')
  +
local fireRate = count / (1 / statRead(weapAtk, 'BurstFireRate') + count * statRead(weapAtk, 'BurstDelay'))
  +
return fireRate
  +
end, '%.3f tirs/sec'
  +
},
  +
BurstFireRate = { function(weapAtk)
  +
return 1 / statRead(weapAtk, 'BurstDelay')
  +
end, '%.2f tirs/sec'
  +
},
  +
--[[
  +
Describing what happens when a gun in WARFRAME is fired using player-made terminology:
   
  +
A particular gun consumes a set number of ammo in order to fire a set number of shots
local function dontHasAttack(Weapon, AttackType)
 
  +
on a single player input for a particular attack.
if (getAttack(Weapon, AttackType) ~= nil) then
 
return nil
 
else
 
return true
 
end
 
end
 
   
  +
A single player input is defined as:
function p.hasAttack(frame)
 
  +
* a single attack button press for semi-auto and burst trigger weapons
local WeapName = frame.args[1]
 
  +
* the moment the next shot is being fired when the attack button is being held for automatic/held trigger weapons
local AttackName = frame.args[2]
 
  +
* the action of holding the attack button for charge trigger weapons
  +
* for duplex-fire trigger weapons, the hold and release of the attack button counts as two inputs
   
  +
A shot is defined as the base unit of attack of a weapon when unmodded.
if (WeapName == nil) then
 
  +
* A single attack input can launch several shots as in the case of burst-fire weapons.
return "ERREUR: Il n'y a pas le nom de l'arme"
 
  +
* A single shot can shoot more than one projectile, affected by the multishot stat, as in the case of shotguns.
elseif (AttackName == nil) then
 
  +
* A single shot can consume more than one unit of ammo (e.g. Tenora's alt-fire) or
AttackName = "Normal"
 
  +
less than one unit of ammo (e.g. Ignis and most continuous weapons).
end
 
   
  +
A gun can have multiple attacks which can be triggered using different buttons
local Weapon = p.getWeapon(WeapName)
 
  +
and/or types of button inputs (e.g. pressing vs. holding)
if (Weapon == nil) then
 
  +
]]--
return "ERREUR: L'arme " .. WeapName .. " n'a pas été trouvé"
 
  +
CalcDamage = function(weapAtk)
end
 
  +
local weapon, attack = weapAtk, weapAtk
  +
-- Count
  +
-- How many shots are fired in a single player input
  +
local tapShots = statRead(weapAtk, 'BurstCount')
  +
-- How many individual player inputs can occur before depleting a magazine
  +
local magTaps = statRead(weapAtk, 'ShotsPerMag')
  +
-- How many additional projectiles are fired per ammo
  +
local multishot = statRead(weapAtk, 'Multishot')
  +
-- How much ammo is contained in the magazine
  +
local magazine = statRead(weapAtk, 'Magazine')
  +
-- How much ammo can be drawn from reserves (or?, how much ammo can be used without picking up more)
  +
local ammoMax = statRead(weapAtk, 'AmmoMax')
   
  +
-- Time^-1
return hasAttack(Weapon, AttackName)
 
  +
local fireRate = statRead(weapAtk, 'FireRate')
end
 
  +
-- Time
  +
local shotTime = statRead(weapAtk, 'InterShotTime')
  +
local chargeTime = statRead(weapAtk, 'ChargeTime')
  +
local burstDelayTime = statRead(weapAtk, 'BurstDelay')
  +
local reloadDelayTime = statRead(weapAtk, 'ReloadDelayEmpty')
  +
local reloadTime = statRead(weapAtk, 'Reload')
  +
local tapTime = chargeTime + (tapShots - 1) * burstDelayTime
  +
-- tapTime: The time between the last shot fired and the next valid attack input
  +
-- (omitting latency of course).
  +
-- Note that first shot of any non-charge trigger attack is instantenous
  +
local magDepletionTime = magTaps * tapTime
  +
if magDepletionTime == 0 then -- If attack is not a charged attack
  +
if shotTime == 0 then
  +
shotTime = 1 / fireRate
  +
end
  +
magDepletionTime = magTaps / fireRate
  +
end
  +
local shotDelayTime = math.max(0, shotTime - tapTime)
   
  +
-- Multiplier
local function hasMultipleTypes(Attack)
 
  +
local maxProgenitorBonus = statRead(weapAtk, 'IsLichWeapon') and 1.6 or 1
local typeCount = 0
 
  +
local avgCritMult = 1 + (statRead(weapAtk, 'CritMultiplier') - 1) * statRead(weapAtk, 'CritChance')
if (Attack ~= nil and Attack.Damage ~= nil) then
 
  +
-- Damage
for key, dmg in Shared.skpairs(Attack.Damage) do
 
  +
local biasPortion, biasType, hitDamage = statRead(weapAtk, 'DamageBias')
if (dmg > 0) then typeCount = typeCount + 1 end
 
  +
end
 
  +
local avgDmgOnTap = hitDamage * avgCritMult * multishot * tapShots * maxProgenitorBonus
end
 
  +
local avgDmgPerMag = avgDmgOnTap * magTaps
if (typeCount > 1) then
 
  +
return "yes"
 
  +
-- 1 is needed b/c one whole magazine is not included in reserve ammo count
else
 
  +
-- If there is no reserve ammo, that means that weapon can deal an infinite amount of damage theoretically
return nil
 
  +
local avgLifetimeDmg = (ammoMax ~= nil) and avgDmgPerMag * (1 + (ammoMax / magazine)) or math.huge
end
 
  +
  +
-- Damage / Time
  +
local baseDps = hitDamage * multishot / shotTime
  +
local avgSustainedDps = avgDmgPerMag / (magDepletionTime + reloadDelayTime + reloadTime) / tapShots
  +
local avgBurstDps = avgDmgOnTap / (tapTime + shotDelayTime) / tapShots
  +
-- Note that burst DPS can also be calculated as such:
  +
-- local avgBurstDps = (hitDamage * avgCritMults * maxProgenitorBonus) * multishot / shotTime
  +
-- local avgBurstDps = avgDmgPerMag / magDepletionTime
  +
  +
return StatObject.ucacheIn(weapAtk, 'CalcDamage',
  +
{ hitDamage, avgDmgOnTap, avgBurstDps, avgSustainedDps, avgLifetimeDmg, baseDps, avgDmgPerMag }
  +
)
  +
end,
  +
ShotDmg = indexes('CalcDamage', 1), -- Total damage per projectile
  +
AvgShotDmg = indexes('CalcDamage', 2), AvgTapDmg = indexes('CalcDamage', 2), -- Average total damage per each input button
  +
BurstDps = indexes('CalcDamage', 3), -- Average burst damage per second/DPS w/o reloading
  +
SustainedDps = indexes('CalcDamage', 4), -- Average sustained damage per second/DPS w/ reloading
  +
LifetimeDmg = indexes('CalcDamage', 5), -- Average total damage from entire ammo pool
  +
BaseDps = indexes('CalcDamage', 6), -- Base damage per second w/ multishot w/o crit
  +
MagDmg = indexes('CalcDamage', 7), -- Average total damage per magazine
  +
-- Average damage scaled by melee attack speed multiplier (numerator of melee DPS w/o accounting for stances and animation time)
  +
AvgDmgWithAnimSpeedMulti = function(weapAtk)
  +
if statRead(weapAtk, 'IsMelee') then
  +
-- Some melee weapons have attacks with multishot like Redeemer, Vastilok, and Quassus
  +
return statRead(weapAtk, 'BaseDamage') * statRead(weapAtk, 'Multishot') * statRead(weapAtk, 'AttackSpeed')
  +
end
  +
return 0
  +
end,
  +
AttackSpeed = { --[[ors('AttackSpeed', 'FireRate')]]function(weapAtk)
  +
if not statRead(weapAtk, 'IsMelee') then
  +
error('AttackSpeed: Cannot get AttackSpeed attribute for a non-melee weapon; use p.statRead(weapAtk, "FireRate") instead')
  +
end
  +
return statRead(weapAtk, 'FireRate')
  +
end, '%.2fx vitesse d\'animation'
  +
},
  +
IsMelee = function(weapAtk) return statRead(weapAtk, 'Slot'):find('Mêlée') ~= nil end,
  +
IsSilent = ors('IsSilent', 'IsMelee', false),
  +
HasAoEAttack = function(weap)
  +
for i, attackEntry in pairs(weap['Attacks']) do
  +
if attackEntry['ShotType'] == 'AoE' then
  +
return true
  +
end
  +
end
  +
return false
  +
end,
  +
Conclave = false,
  +
Image = { 'Panel.png', '[[File:%s|link=]]' },
  +
Attacks = ors('Attacks', p._getAttack, {}),
  +
Family = nil,
  +
FamilyList = { function(weapAtk)
  +
local family = statRead(weapAtk, 'Family')
  +
-- assert(family, 'i have no Family :\'(')
  +
if not family then return {weapAtk} end
  +
-- return family, statRead(weapAtk, 'Slot')
  +
local slot = statRead(weapAtk, 'Slot')
  +
local result = {}
  +
for _, w in pairs(WeaponData[slot] or error('FamilyList: no weapondata for slot '..(slot or '<nil>'))) do
  +
if w.Family == family then
  +
table.insert(result, w)
  +
end
  +
end
  +
table.sort(result, function(a,b) return a.Name<b.Name end)
  +
return result
  +
end, function(self, result)
  +
for i,w in ipairs(result) do
  +
result[i]=Tooltip.full(w.Name, 'Weapons', w)
  +
end
  +
return table.concat(result, '<br />')
 
end
 
end
  +
},
  +
BaseName = function(weapAtk) return weapAtk['BaseName'] or ({p._getVariant(statRead(weapAtk, 'Name'))})[3] end,
  +
-- TODO: Add comments to Explosion function for readability
  +
-- TODO: Do not rely on attack name to determine what AoE component is attached to which main direct hit component
  +
---^i suggest an explosion key with either the attack number of any corresponding explosion, nested attack tables, or some other way to make a tree
  +
-- TODO: Use ShotType = "AoE" to determine if attack entry is AoE
  +
Explosion = function(weapAtk)
  +
local weap, atk = weapAtk, weapAtk
  +
-- tbh this is a mess
  +
local explosion = weapAtk['Explosion'] or statRead(weapAtk, 'AttackName'):gsub(' Impact',''):gsub(' Contact','')..' Explosion'
  +
if type(explosion) == 'string' then
  +
explosion = weap.Attacks[tonumber(explosion:gsub('%D',''))] or explosion
  +
elseif type(explosion) == 'number' then
  +
explosion = weap.Attacks[explosion] or explosion
  +
end
  +
local explosions = {}
  +
if type(explosion) ~= 'table' then
  +
for i, v in ipairs(weap.Attacks) do
  +
if p._statRead(weapAtk, v, 'AttackName'):find 'xplosion' then
  +
if p._statRead(weapAtk, v, 'AttackName') == explosion then
  +
explosions[1] = nil
  +
explosion = v
  +
break
  +
end
  +
table.insert(explosions, v)
  +
end
  +
end
  +
explosion = explosions[1] or explosion
  +
end
  +
StatObject.pcacheIn(getWeaponAttack(weap, explosion), 'BaseAttack', atk)
  +
return StatObject.pucacheIn(weapAtk, 'Explosion', explosion)
  +
end,
  +
IsVariant = function(weap)
  +
return StatObject.pucacheIn(weap, 'IsVariant', p._isVariant(statRead(weap, 'Name')))
  +
end,
  +
Variant = indexes('IsVariant', 2),
  +
BaseName = indexes('IsVariant', 3),
  +
Categories = { function(weapAtk)
  +
local cats = { 'Armes' }
  +
-- Adding editor-defined traits from M:Weapons/data
  +
-- Note to make sure they have a proper category page associated with a trait
   
  +
for _, trait in ipairs(weapAtk.Traits or {}) do
function p.hasMultipleTypes(frame)
 
local WeapName = frame.args[1]
+
local weapsTraitsMap = ({
  +
-- De fabrication / origines
local AttackName = frame.args[2]
 
  +
["Tenno"] = { "Tenno", "Armes/Tennos" },
if (AttackName == nil) then AttackName = "Normal" end
 
  +
["Corpus"] = { "Corpus", "Armes/Corpus" },
local attack = getAttack(WeapName, AttackName)
 
  +
["Grineer"] = { "Grineer", "Armes/Grineers" },
return hasMultipleTypes(attack)
 
  +
["Infestée"] = { "Infestée", "Armes/Infestées" },
end
 
  +
["Orokin"] = { "Orokin", "Armes/Orokins" },
  +
["Sentient"] = { "Sentient", "Armes/Sentients" },
  +
["Entrati"] = { "Entrati", "Armes/Entratis" },
  +
["Céphalon"] = { "Céphalon", "Armes/Céphalons" },
  +
-- Spéciales & Variantes
  +
["Incarnon"] = { "Armes/Incarnon" },
  +
["Principe"] = { "Armes/Principe" },
  +
["Prisma"] = { "Armes/Prisma" },
  +
["Vandal"] = { "Armes/Vandal" },
  +
["Wraith"] = { "Armes/Wraith" },
  +
["MK1"] = { "Armes/MK1" },
  +
["Signature"] = { "Armes/Signature" },
  +
["Liche Kuva"] = { "Liche Kuva", "Armes/Liche Kuva" },
  +
-- Prime state
  +
["Prime"] = { "Prime", "Armes/Prime" },
  +
["Vaultée"] = { "Prime Vault", "Armes/Vault" },
  +
["Jamais Vaultée"] = { "Jamais en Vault" },
  +
-- Syndicats
  +
["Syndicats"] = { "Armes de Syndicats" },
  +
["Arbitres de Hexis"] = { "Arbitres de Hexis" }, -- Telos
  +
["Céphalon Suda"] = { "Céphalon Suda" }, -- Synoid
  +
["Méridien d'Acier"] = { "Méridien d'Acier" }, -- Vaykor
  +
["Nouveau Loka"] = { "Nouveau Loka" }, -- Sancti
  +
["La Séquence Perrin"] = { "La Séquence Perrin" }, -- Secura
  +
["Voile Rouge"] = { "Voile Rouge" }, -- Rakta
  +
["Céphalon Simaris"] = { "Céphalon Simaris" }, -- Special
  +
-- Armes de "Factions" / PNJ
  +
["Dax"] = { "Armes/Dax" },
  +
["Duviri"] = { "Armes/Duviri" },
  +
["Zariman"] = { "Armes/Zariman" },
  +
["Cetus"] = { "Armes/Cetus" },
  +
["Fortuna"] = { "Armes/Fortuna" },
  +
["Puy de Cambion"] = { "Armes/Puy de Cambion" },
  +
["Stalker"] = { "Stalker", "Armes/Stalker" },
  +
["Baro"] = { "Offres de Baro Ki'Teer" },
  +
-- Autres
  +
["Dex"] = { "Armes/Dex" },
  +
["Quête"] = { "Armes/Quêtes" },
  +
["Fondateur"] = { "Fondateur", "Armes/Fondateur" },
  +
["Invasions"] = { "Invasions", "Récompenses d'évènements", "Armes/Invasions", "Armes/Évènements" },
  +
["Alertes Tactiques"] = { "Récompenses d'Alertes Tactiques" },
  +
})[trait]
  +
  +
if weapsTraitsMap then
  +
for _, cat in ipairs(weapsTraitsMap) do
  +
table.insert(cats, cat)
  +
end
  +
else
  +
table.insert(cats, "Armes/Incomplètes")
  +
end
  +
end
   
  +
-- DamageTypes categories
function p.attackLoop(Weapon)
 
  +
local bias = p._getValue(weapAtk, "BiasType")
if (Weapon == nil) then return function() return nil end end
 
  +
table.insert(cats, ({
local aType = "Normal"
 
  +
-- Physic
local iterator = function()
 
  +
["Impact"] = "Armes/Dégâts/Impact",
if (aType == "Normal") then
 
  +
["Tranchant"] = "Armes/Dégâts/Tranchant",
local attack = getAttack(Weapon, aType)
 
  +
["Perforation"] = "Armes/Dégâts/Perforation",
aType = "Charge"
 
  +
-- Elemental
if attack ~= nil and attack.Damage ~= nil then
 
  +
-- -- Main
return "Normal", attack
 
  +
["Feu"] = "Armes/Dégâts/Feu",
end
 
  +
["Glace"] = "Armes/Dégâts/Glace",
end
 
  +
["Poison"] = "Armes/Dégâts/Poison",
if (aType == "Charge") then
 
  +
["Électrique"] = "Armes/Dégâts/Électrique",
local attack = getAttack(Weapon, aType)
 
  +
-- -- Mixed
aType = "Area"
 
  +
["Gaz"] = "Armes/Dégâts/Gaz",
if attack ~= nil and attack.Damage ~= nil then
 
  +
["Viral"] = "Armes/Dégâts/Viral",
return "Charge", attack
 
  +
["Corrosif"] = "Armes/Dégâts/Corrosif",
end
 
  +
["Explosif"] = "Armes/Dégâts/Explosif",
end
 
  +
["Radiation"] = "Armes/Dégâts/Radiation",
if (aType == "Area") then
 
  +
["Magnétique"] = "Armes/Dégâts/Magnétique",
local attack = getAttack(Weapon, aType)
 
  +
-- -- Specials
aType = "SecondaryArea"
 
  +
["Tau"] = "Armes/Dégâts/Tau",
if attack ~= nil and attack.Damage ~= nil then
 
  +
["Brut"] = "Armes/Dégâts/Brut",
return "Area", attack
 
  +
["Néant"] = "Armes/Dégâts/Néant",
end
 
end
 
if (aType == "SecondaryArea") then
 
local attack = getAttack(Weapon, aType)
 
aType = "Secondary"
 
if attack ~= nil and attack.Damage ~= nil then
 
return "SecondaryArea", attack
 
end
 
end
 
if (aType == "Secondary") then
 
local attack = getAttack(Weapon, aType)
 
aType = "Throw"
 
if attack ~= nil and attack.Damage ~= nil then
 
return "Secondary", attack
 
end
 
end
 
if (aType == "Throw") then
 
local attack = getAttack(Weapon, aType)
 
aType = "ChargedThrow"
 
if attack ~= nil and attack.Damage ~= nil then
 
return "Throw", attack
 
end
 
end
 
if (aType == "ChargedThrow") then
 
local attack = getAttack(Weapon, aType)
 
aType = "end"
 
if attack ~= nil and attack.Damage ~= nil then
 
return "ChargedThrow", attack
 
end
 
end
 
   
  +
})[bias] or "Armes/Dégâts/Inconnus")
return nil
 
  +
end
 
  +
local class = p._getValue(weapAtk, "Class")
return iterator
 
  +
table.insert(cats, ({
end
 
  +
-- Principales
  +
["Canon à bras"] = "Armes/Canons à bras",
  +
["Arc"] = "Armes/Arcs",
  +
["Arbalète"] = "Armes/Arcs",
  +
["Fusil"] = "Armes/Fusils",
  +
["Fusil Sniper"] = "Armes/Fusils de Sniper",
  +
["Fusil à Pompe"] = "Armes/Fusils à Pompe",
  +
["Lance à énergie"] = "Armes/Lances à énergie",
  +
["Lanceur"] = "Armes/Lanceurs",
  +
-- Secondaires
  +
["Doubles Pistolets"] = "Armes/Doubles Pistolets",
  +
["Fusil à Pompe à double canon"] = "Armes/Fusils à Pompe à double canon",
  +
["Fusil à Pompe court"] = "Armes/Fusils à Pompe courts",
  +
["Lancé"] = "Armes/Lancés",
  +
["Pistolet"] = "Armes/Pistolets",
  +
["Volume"] = "Armes/Volumes",
  +
-- Mêlées
  +
["Arme d'hast"] = "Armes/Armes d'hast",
  +
["Bâton"] = "Armes/Bâtons",
  +
["Dague"] = "Armes/Dagues",
  +
["Doubles Dagues"] = "Armes/Doubles Dagues",
  +
["Doubles Nikanas"] = "Armes/Doubles Nikanas",
  +
["Doubles Épées"] = "Armes/Doubles Épées",
  +
["Faux"] = "Armes/Faux",
  +
["Faux Lourde"] = "Armes/Faux Lourdes",
  +
["Fouet"] = "Armes/Fouets",
  +
["Glaive"] = "Armes/Glaives",
  +
["Griffes"] = "Armes/Griffes",
  +
["Lame Lourde"] = "Armes/Lames Lourdes",
  +
["Lame et Fouet"] = "Armes/Lames et Fouets",
  +
["Machette"] = "Armes/Machettes",
  +
["Mains et Pieds"] = "Armes/Mains et Pieds",
  +
["Marteau"] = "Armes/Marteaux",
  +
["Nikana"] = "Armes/Nikanas",
  +
["Nikana à Deux Mains"] = "Armes/Nikanas à Deux Mains",
  +
["Nunchaku"] = "Armes/Nunchakus",
  +
["Pistolame"] = "Armes/Pistolames",
  +
["Poings"] = "Armes/Poings",
  +
["Rapière"] = "Armes/Rapières",
  +
["Scie d'Assaut"] = "Armes/Scies d'Assaut",
  +
["Tonfa"] = "Armes/Tonfas",
  +
["Épée"] = "Armes/Épées",
  +
["Épée et Bouclier"] = "Armes/Épées et Boucliers",
  +
["Éventail de guerre"] = "Armes/Éventails de guerre",
  +
-- Spéciales
  +
["Arme Exaltée"] = "Armes/Exaltées",
  +
["Amplificateur"] = "Armes/Amplificateurs",
  +
["Arch-Fusil"] = "Armes/Arch-Fusils",
  +
["Arch-Fusil (Atmosphère)"] = "Armes/Arch-Fusils",
  +
["Arch-Mêlée"] = "Armes/Arch-Mêlées",
  +
["Pièce principale"] = "Armes/Railjack",
  +
["Tourelle"] = "Armes/Railjack",
  +
["Railjack Armament"] = "Armes/Railjack",
  +
["Unique"] = "Armes/Uniques", -- e.g Parazon/Robotiques/Molosses
  +
-- Zaws
  +
["Zaw Rapière / Arme d'hast"] = "Armes/Zaws",
  +
["Zaw Machette / Marteau"] = "Armes/Zaws",
  +
["Zaw Faux / Bâton"] = "Armes/Zaws",
  +
["Zaw Nikana / Bâton"] = "Armes/Zaws",
  +
["Zaw Épée / Bâton"] = "Armes/Zaws",
  +
["Zaw Épée / Arme d'hast"] = "Armes/Zaws",
  +
["Zaw Machette / Arme d'hast"] = "Armes/Zaws",
  +
["Zaw Faux / Lame Lourde"] = "Armes/Zaws",
  +
["Zaw Machette / Arme d'hast"] = "Armes/Zaws",
  +
["Zaw Dague / Bâton"] = "Armes/Zaws",
  +
["Zaw Rapière / Arme d'hast"] = "Armes/Zaws",
  +
-- Kitguns
  +
["Fusil Kitgun"] = "Armes/Kitguns",
  +
["Lanceur Kitgun"] = "Armes/Kitguns",
  +
["Pistolet Kitgun"] = "Armes/Kitguns",
  +
["Fusil à Pompe Kitgun"] = "Armes/Kitguns",
  +
})[class] or "Armes/Inconnues")
   
  +
-- Modular category ('Armes/Modulaires')
local function getFamily(FamilyName)
 
  +
if string.find(class, "^Zaw") or string.find(class, "Kitgun$") then
local familyMembers = {}
 
  +
table.insert(cats, "Armes/Modulaires")
for i, Weapon in Shared.skpairs(WeaponData["Weapons"]) do
 
  +
end
if (Weapon.Family == FamilyName) then
 
table.insert(familyMembers, Weapon)
 
end
 
end
 
return familyMembers
 
end
 
   
  +
local family = p._getValue(weapAtk, "Family")
-- Returns all melee weapons.
 
  +
table.insert(cats, family)
-- If weapType is not nil, only grab for a specific type
 
  +
-- For example, if weapType is "Nikana", only pull Nikanas
 
local function getMeleeWeapons(weapClass, PvP)
+
local slot = p._getValue(weapAtk, "Slot")
  +
table.insert(cats, ({
local weaps = {}
 
  +
["Principale"] = "Armes/Principales", -- Principale
local weapClasses = {}
 
  +
["Secondaire"] = "Armes/Secondaires", -- Secondaire
if (weapClass ~= nil) then
 
  +
["Mêlée"] = "Armes/Mêlées", -- Mêlée
weapClasses = Shared.splitString(weapClass, ',')
 
  +
-- Archwing
end
 
  +
["Arch-Fusil"] = "Armes/Arch-Fusils",
 
  +
["Arch-Fusil (Atmosphère)"] = "Armes/Arch-Fusils",
for i, weap in Shared.skpairs(WeaponData["Weapons"]) do
 
  +
["Arch-Mêlée"] = "Armes/Arch-Mêlées",
if ((weap.Ignore == nil or not weap.Ignore) and weap.Type ~= nil and
 
  +
-- Railjack
weap.Type == "Mêlée") then
 
  +
["Tourelle Railjack"] = "Armes/Railjacks",
local classMatch = (weapClass == nil or
 
  +
["Pièce principale Railjack"] = "Armes/Railjacks",
Shared.contains(weapClasses, weap.Class))
 
  +
--
local pvpMatch = (PvP == nil or
 
  +
["Amplificateur"] = "Armes/Amplificateurs", -- Amplificateurs
(PvP and weap.Conclave ~= nil and weap.Conclave))
 
  +
["Robotique"] = "Armes/Robotiques", -- Sentinelles
if (classMatch and pvpMatch) then
 
  +
["Molosse"] = "Armes/Molosses", -- Molosses
table.insert(weaps, weap)
 
  +
["Emplacement"] = "Armes/Emplacements", -- Rempart (only)
end
 
  +
["Équipement"] = "Armes/Équipements", -- Ebisu / Lanzo / etc..
end
 
  +
["Nech-Mêlée"] = "Armes/Nech-Mêlées", -- Pugil
end
 
  +
["Unique"] = "Armes/Uniques", -- e.g Parazon
  +
["Véhicule"] = "Armes/Véhicules", -- Dargyn
  +
})[slot] or "Armes/Inconnues")
  +
  +
-- Fonctionnalité désactivée pour y insérer un tableau associatif juste après ce commentaire (Yazuh)
  +
-- local trigger = p._getValue(weapAtk, "Trigger")
  +
-- table.insert(cats, 'Armes ' .. trigger)
   
  +
table.insert(cats, ({
return weaps
 
  +
["Activée"] = 'Armes/Tir à Activation',
  +
["Auto / Chargée"] = 'Armes/Chargée',
  +
["Automatique"] = 'Armes/Automatique',
  +
["Auto / Rafale"] = 'Armes/Tir en Rafale',
  +
["Cadence progressive"] = 'Armes/Automatique',
  +
["Rafale"] = 'Armes/Tir en Rafale',
  +
["Chargée"] = 'Armes/Chargée',
  +
["Duplex"] = 'Armes/Tir en Duplex',
  +
["Maintenue"] = 'Armes/Tir en continu',
  +
["Semi-auto"] = 'Armes/Semi-automatique',
  +
})[trigger] or 'Armes') -- fix for melee weapon (no trigger)
  +
  +
local users = p._getValue(weapAtk, "Users") or {}
  +
for _, user in ipairs(users) do table.insert(cats, user) end
  +
  +
-- Aucun besoin de cette fonctionnalité (by: Yazu)
  +
-- local variant = p._getValue(weapAtk, "Variant")
  +
-- table.insert(cats, variant)
  +
  +
local infAmmo = p._getValue(weapAtk, "AmmoMax") == math.huge
  +
local accuracy = p._getValue(weapAtk, "Accuracy")
  +
local pinpoint = accuracy ~= nil and accuracy >= 100
  +
local regens = p._getValue(weapAtk, "ReloadRate") > 0
  +
local silent = weapAtk.IsSilent -- automatically includes
  +
local single = p._getValue(weapAtk, "Magazine") == 1 and not p._getValue(weapAtk, "IsMelee")--meh, delet?
  +
local spools = p._getValue(weapAtk, "Spool") > 0
  +
local isAoE = p._getValue(weapAtk, "HasAoEAttack")
  +
local isCodexSecret = p._getValue(weapAtk, "CodexSecret")
  +
local isTradable = p._getValue(weapAtk, "Tradable")
  +
local isInConclave = p._getValue(weapAtk, "Conclave")
  +
  +
-- Arbitrarily ordering misc categories
  +
if infAmmo then table.insert(cats, 'Armes/Armes à munitions infinies') end
  +
if pinpoint then table.insert(cats, 'Armes/Armes de précision') end
  +
if regens then table.insert(cats, 'Armes/Armes à batterie') end
  +
if silent then
  +
table.insert(cats, 'Armes/Armes silencieuses')
  +
else
  +
table.insert(cats, 'Armes/Armes bruyantes')
  +
end
  +
if single then table.insert(cats, 'Armes/Armes à coup unique') end
  +
if spools then table.insert(cats, 'Armes/Armes à bobine') end
  +
if isAoE then table.insert(cats, 'Armes/Armes à zone d\'effet') end
  +
if isCodexSecret then table.insert(cats, 'Armes/Secret du codex') end
  +
if isTradable then
  +
table.insert(cats, 'Armes/Armes échangeables')
  +
else
  +
table.insert(cats, 'Armes/Armes non échangeables')
  +
end
  +
if isInConclave then table.insert(cats, 'Armes/Disponibles au Conclave') end
  +
  +
return StatObject.cacheIn(weapAtk, 'Categories', cats)
  +
end, function(s, cats)
  +
local wikitextResult = { '' } -- Need to prepend a newline so first asterisk is rendered as a wikitext list
  +
local formatStr = '*[[:Catégorie:%s|%s]][[Catégorie:%s]]'
  +
for _, category in ipairs(cats) do
  +
table.insert(wikitextResult, formatStr:format(category, category, category))
  +
end
  +
return table.concat(wikitextResult, '\n')
 
end
 
end
  +
},
 
  +
SyndicateEffect = { '', function(s, v)
-- As above, but for Conclave stats
 
  +
return (v == '' or type(v) ~= 'string') and '' or Tooltip.icon(({
local function getConclaveMeleeWeapons(weapClass, PvP)
 
  +
['rouille'] = 'Voile Rouge',
local weaps = {}
 
  +
['entropie'] = 'Céphalon Suda',
local weapClasses = {}
 
  +
['justice'] = 'Méridien d\'Acier',
if (weapClass ~= nil) then
 
  +
['pureté'] = 'Nouveau Loka',
weapClasses = Shared.splitString(weapClass, ',')
 
  +
['séquence'] = 'La Séquence Perrin',
end
 
  +
['vérité'] = 'Arbitres de Hexis',
 
  +
})[v:lower()] or 'Tenno', 'Factions')
for i, weap in Shared.skpairs(ConclaveData["Weapons"]) do
 
  +
..' '..v
if ((weap.Ignore == nil or not weap.Ignore) and weap.Type ~= nil and
 
weap.Type == "Mêlée") then
 
local classMatch = (weapClass == nil or
 
Shared.contains(weapClasses, weap.Class))
 
local pvpMatch = (PvP == nil or
 
(PvP and weap.Conclave ~= nil and weap.Conclave))
 
if (classMatch and pvpMatch) then
 
table.insert(weaps, weap)
 
end
 
end
 
end
 
 
return weaps
 
 
end
 
end
  +
},
 
  +
MinProgenitorBonus = function(weap) return weap.IsLichWeapon and statRead(weap, 'BaseDamage') * 0.25 or 0 end,
-- Learning new things... Trying to allow sending in an arbitrary function
 
  +
ProgenitorBonus = function(weap) return weap.IsLichWeapon and statRead(weap, 'BaseDamage') * 0.6 or 0 end,
function p.getWeapons(validateFunction)
 
  +
Class = '',
local weaps = {}
 
  +
SniperComboReset = { nil, '%.1f s' },
for i, weap in Shared.skpairs(WeaponData["Weapons"]) do
 
  +
SniperComboMin = { nil, '%d shot(s)' },
if ((weap.Ignore == nil or not weap.Ignore) and validateFunction(weap)) then
 
  +
Tradable = { function(weapAtk)
table.insert(weaps, weap)
 
  +
if type(weapAtk['Tradable'])=='number' then
end
 
  +
assert(weapAtk['Tradable']<=5,
end
 
  +
'Tradable: Does not support tradeability enums beyond 5; please update [[Module:Weapons/data]] and [[Module:Weapons]] to support more tradeability edge cases')
return weaps
 
  +
return ({
  +
[0]=false,
  +
[1]='Rang 0',
  +
[2]='Parties',
  +
[3]='Liches',
  +
[4]='Parties pour fabrication',
  +
[5]='Arme liée',
  +
})[weapAtk['Tradable']]
  +
end
  +
return weapAtk['Tradable']
  +
end, function(s, tradable)
  +
return ({
  +
[false] = 'Non-échangeable',
  +
['Unranked'] = 'Non classée sans Forma ou Catalyseur',
  +
['Parts'] = 'Pièces et/ou schéma uniquement',
  +
['Lich'] = 'Par le biais des échanges des [[Liche Kuva|Liches]].',
  +
['Built Parts'] = 'Uniquement des composants construits, et non des schémas',
  +
['Parent'] = 'Indirectement, livré avec le compagnon associé',
  +
})[tradable] or 'Non-échangeable?'
 
end
 
end
  +
},
 
  +
SellPrice = { nil, function(self, sellPrice)
-- Same as getWeapons, but for Conclave data
 
  +
if sellPrice == nil then
function p.getConclaveWeapons(validateFunction)
 
  +
return 'Invendable'
local weaps = {}
 
  +
end
for i, weap in Shared.skpairs(ConclaveData["Weapons"]) do
 
  +
return Tooltip.icon('Crédits', 'Resources')..' '..Math.formatnum(sellPrice)
if ((weap.Ignore == nil or not weap.Ignore) and validateFunction(weap)) then
 
table.insert(weaps, weap)
 
end
 
end
 
return weaps
 
 
end
 
end
  +
},
 
  +
DefaultUpgrades = { nil, function(self, upgradesArr)
local function asMultiplier(val)
 
  +
local result = {}
if (val == nil) then return "1.0x" end
 
  +
for _, modIndex in ipairs(upgradesArr or {}) do
return Shared.round(val, 2, 1) .. "x"
 
  +
table.insert(result, Tooltip.full(modIndex, 'Mods'))
  +
end
  +
return table.concat(result, '<br />')
 
end
 
end
  +
},
 
  +
Users = { nil, function(self, usersArr)
local function HasTrait(Weapon, Trait)
 
  +
local result = { '' }
if (Trait == nil or Weapon.Traits == nil) then return false end
 
  +
for _, user in ipairs(usersArr or {}) do
 
  +
table.insert(result, '*[['..user..']]')
for i, theTrait in pairs(Weapon.Traits) do
 
  +
end
if (theTrait == Trait) then return true end
 
  +
return table.concat(result, '\n')
end
 
 
return false
 
 
end
 
end
  +
},
 
  +
Zoom = { unpacks('Zoom'), { sep = '<br />' } },
-- If Type is not nil, get damage weapon deals of that type
 
  +
Slot = nil,
-- If it deals no damage of that type, return 0 instead of nil
 
  +
}
-- It Type is nil, return total damage
 
  +
-- Loops for adding to StatObject.default table
local function GetDamage(Attack, Type, ByPellet)
 
  +
-- Damage type getters:
if (ByPellet == nil) then ByPellet = false end
 
  +
-- <DamageType> = damage type value
if (Attack == nil or Attack.Damage == nil) then return 0 end
 
  +
-- <DamageType>Distribution = damage type distribution as a percentage
 
  +
-- PvP<DamageType> = damage type value with precise formatting for PvP purposes
local pCount = 1
 
  +
for _, damageType in ipairs(iterationOrderArray) do
if (ByPellet and Attack.PelletCount ~= nil) then
 
  +
StatObject.default[damageType] = {
pCount = Attack.PelletCount
 
  +
function(weapAtk) return weapAtk['Damage'][damageType] or 0 end,
end
 
  +
function(self, value) return Tooltip.icon(damageType, 'DamageTypes')..' '..Math.formatnum(value) end
if (Type == nil) then
 
  +
}
local total = 0
 
  +
-- Damage distribution as a percentage
for i, d in Shared.skpairs(Attack.Damage) do total = total + d end
 
  +
StatObject.default[damageType..'Distribution'] = {
return total / pCount
 
  +
function(weapAtk) return weapAtk['Damage'][damageType] / statRead(weapAtk, 'BaseDamage') end,
else
 
  +
function(self, value) return Tooltip.icon(damageType, 'DamageTypes')..' '..Math.percentage(value) end
if (Type == "Physical") then
 
  +
}
local Impact = Attack.Damage["Impact"] ~= nil and
 
  +
-- More precise damage values to 4 decimal places for PvP
Attack.Damage["Impact"] or 0
 
  +
StatObject.default['PvP'..damageType] = {
local Puncture = Attack.Damage["Perforation"] ~= nil and
 
  +
function(weapAtk) return weapAtk['Damage'][damageType] or 0 end,
Attack.Damage["Perforation"] or 0
 
  +
Tooltip.icon(damageType, 'DamageTypes')..' %.4f'
local Slash = Attack.Damage["Tranchant"] ~= nil and
 
  +
}
Attack.Damage["Tranchant"] or 0
 
return (Impact + Puncture + Slash) / pCount
 
elseif (Type == "Element") then
 
for dType, dmg in Shared.skpairs(Attack.Damage) do
 
if (not Shared.contains(Physical, dType) or dmg <= 0) then
 
return dmg / pCount
 
end
 
end
 
return 0
 
elseif (Attack.Damage[Type] == nil) then
 
return 0
 
else
 
return Attack.Damage[Type] / pCount
 
end
 
end
 
 
end
 
end
   
  +
-- TODO: Do not rely on localized name to determine a weapon's variant. Decouple localization from data
-- Returns the damage string as it's formatted in a comparison row
 
  +
--- Checks if a weapon is a variant or not.
-- So instead of '0', returns '-', and appends the icon for an element if necessary
 
  +
-- @function p._isVariant
local function GetDamageString(Attack, Type, ByPellet)
 
  +
-- @param {string} weaponName Weapon name
if (ByPellet == nil) then ByPellet = false end
 
  +
-- @returns {boolean} True if weapon is a variant, false otherwise
if (Attack == nil or Attack.Damage == nil) then return "" end
 
  +
-- @returns {string} Weapon's variant name or "Base" if weapon is not a variant
 
  +
-- @returns {string} Weapon name, same as weaponName
local pCount = 1
 
  +
function p._isVariant(weaponName)
if (ByPellet and Attack.PelletCount ~= nil) then
 
  +
for i, var in pairs(VARIANT_LIST) do
pCount = Attack.PelletCount
 
  +
if (var ~= "Dex" or weaponName ~= "Dex Pixia") then
end
 
if (Type == nil) then
+
if string.find(weaponName, var) then
  +
return true, var, (string.gsub(weaponName, " ?"..var.." ?-?", ""))
if (not hasMultipleTypes(Attack)) then
 
  +
end
for key, val in pairs(Attack.Damage) do
 
  +
end
if (val > 0) then
 
  +
end
return Icon._Proc(key) .. " " ..
 
  +
return false, "Variante de base", weaponName
Shared.round(val / pCount, 2)
 
end
 
end
 
return ""
 
else
 
return Shared.round(GetDamage(Attack, nil, ByPellet), {2, 1})
 
end
 
else
 
local thisVal = GetDamage(Attack, Type, ByPellet)
 
if (thisVal == 0) then
 
return ""
 
else
 
return Shared.round(thisVal, {2, 1})
 
end
 
end
 
 
end
 
end
   
  +
--- Builds the full name of a weapon's variant. Does not check if it exists or not.
local function GetDamageBias(Attack, IncludeSingle)
 
  +
-- @function p._buildName
if (IncludeSingle == nil) then IncludeSingle = false end
 
  +
-- @param {string} baseName Weapon's base name (e.g. "Braton")
 
  +
-- @param[opt] {string} variant Variant name (e.g. "Vandal"); if nil, returns base weapon name instead
if (Attack.Damage ~= nil and Shared.tableCount(Attack.Damage) > 0) then
 
  +
-- @returns {string} Weapon's variant name (e.g. "Braton Vandal")
local total = 0
 
  +
function p._buildName(baseName, variant)
local bestDmg = 0
 
  +
if not variant or variant == 'Base' or variant == '' then
local bestElement = nil
 
  +
return baseName
local count = 0
 
  +
end
for Element, Dmg in pairs(Attack.Damage) do
 
  +
return (({
if (Dmg > bestDmg) then
 
  +
-- Prime Laser Rifle is an edge case for Prime naming scheme (at least in EN localization)
bestDmg = Dmg
 
  +
Prime = baseName ~= 'Fusil Laser' and '%b %v',
bestElement = Element
 
  +
Vandal = '%b %v',
end
 
  +
Wraith = '%b %v',
total = total + Dmg
 
  +
MK1 = '%v-%b',
if (Dmg > 0) then count = count + 1 end
 
  +
})[variant] or '%v %b'):gsub('%%v', variant):gsub('%%b', baseName)
end
 
-- Make sure there are two damage instances that are above zero
 
-- Exception for physical damage types
 
if (count > 1 or (IncludeSingle and count > 0)) then
 
return (bestDmg / total), bestElement
 
else
 
return nil
 
end
 
else
 
return nil
 
end
 
 
end
 
end
   
  +
--- Returns a specific weapon table entry from <code>/data</code> or <code>/Conclave/data</code>.
-- If the attack has at least two damage types,
 
  +
-- @function p._getWeapon
-- Returns something like "58.3% Slash"
 
  +
-- @param {string} weaponName Weapon name
-- If it doesn't, returns nil
 
  +
-- @param[opt] {boolean} pvp If true, gets PvP stats of weapon instead, false otherwise; defaults to false
local function GetDamageBiasString(Attack, HideType, ShowText, IncludeSingle,
 
  +
-- @returns {table} Weapon table
Color)
 
  +
function p._getWeapon(weaponName, pvp)
if (HideType == nil) then HideType = false end
 
  +
weaponName = mw.text.decode(weaponName)
if (ShowText == nil) then ShowText = "" end
 
  +
return (pvp and ConclaveData or WeaponData)[weaponName] or
if (IncludeSingle == nil) then IncludeSingle = false end
 
  +
error('p._getWeapon(weaponName, pvp): "'..weaponName..
 
  +
'" does not exist in '..(pvp and '[[Module:Weapons/Conclave/data]]' or '[[Module:Weapons/data]]'))
local bestPercent, bestElement = GetDamageBias(Attack, IncludeSingle)
 
if (bestPercent ~= nil) then
 
local result = Shared.asPercent(bestPercent, 0)
 
if (not HideType) then
 
result = Icon._Proc(bestElement, ShowText, Color) .. " " .. result
 
end
 
return result
 
else
 
return nil
 
end
 
 
end
 
end
   
  +
--- Gets the raw value of a certain statistic of a weapon.
function p.GetPolarityString(Weapon)
 
  +
-- @function p._getValue
 
  +
-- @param {table} Weapon Weapon table
local ret = Icon.PolList(Weapon.Polarities)
 
  +
-- @param {string} key Name of key
if (ret ~= "Aucune") then
 
  +
-- @param[opt] {string} attack Name of attack to search through; defaults to 'Attack1' or what '_TooltipAttackDisplay' is set to
-- Adding Exceptions
 
  +
-- @returns {string, number} Value of statistic
if (Weapon.Name == "Lame Exaltée") then
 
  +
function p._getValue(weap, key, atk)--, formatted)
ret = ret .. "(Vanilla/Prime)<br/>" .. Icon._Pol("Umbra") ..
 
  +
-- return (formatted and statFormat or statRead)(weap, atk, key)
Icon._Pol("Umbra") .. "(Umbra)"
 
  +
return p._statRead(weap, atk, key)
end
 
end
 
 
return ret
 
 
end
 
end
   
  +
--- Gets the formatted value of a certain statistic of a weapon to be displayed
local function getWeaponStanceList(Weapon)
 
  +
-- the wiki.
if (Weapon == nil or Weapon.Type ~= "Mêlée") then return nil end
 
  +
-- @function p._getFormattedValue
 
  +
-- @param {table} Weapon Weapon table
local stances = Mod.getStances(Weapon.Class, Weapon.Conclave, Weapon.Name)
 
  +
-- @param {string} keyName Name of key
 
  +
-- @param[opt] {string} attackName Name of attack to search through; defaults to 'Attack1'
local result = ""
 
  +
-- @returns {string} Value of statistic
 
  +
function p._getFormattedValue(weap, key, atk)
for i, stance in pairs(stances) do
 
  +
-- return p._getValue(Weapon, keyName, attackName, true)
if (string.len(result) > 0) then result = result .. "<br/>" end
 
  +
return p._statFormat(weap, atk, key)
 
local polarity = ''
 
local link = ''
 
 
if Weapon.Class ~= "Arme Exaltée" or Weapon.Name == "Serres de Garuda" then
 
result = result .. Tooltip._tooltipText(stance.Name, 'Mod')
 
end
 
 
result = result .. polarity
 
-- If this is a PvP Stance, add the disclaimer
 
local stancePvP = Mod._getValue(stance.Name, "PVP")
 
if (stancePvP) then result = result .. " (PvP)" end
 
end
 
 
return result
 
 
end
 
end
   
  +
--- Function that returns a simpler getter function, for multiple _stat*() calls on the same weapon/attack pair.
local function getAttackValue(Weapon, Attack, ValName, giveDefault, asString,
 
  +
-- @function p._statReader
forTable)
 
  +
-- @param {table} weap Weapon entry
if (giveDefault == nil) then giveDefault = false end
 
  +
-- @param {number|table} atk Attacks table index or Attack entry
if (asString == nil) then asString = false end
 
  +
-- @return {function} Getter function
if (forTable == nil) then forTable = false end
 
  +
function p._statReader(weap, atk)
if (Attack == nil) then
 
  +
return function(...) return p._statRead(weap, atk, ...) end
if (asString) then
 
return ""
 
else
 
return nil
 
end
 
end
 
local regularVal = Shared.titleCase(string.sub(ValName, 1, 1),
 
string.sub(ValName, 2))
 
ValName = string.upper(ValName)
 
if (ValName == "DAMAGE") then
 
if (Attack.Damage ~= nil) then
 
if (asString) then
 
return GetDamageString(Attack, nil)
 
else
 
return GetDamage(Attack)
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "DAMAGEPLUS") then
 
if (Attack.Damage ~= nil) then
 
if (asString) then
 
if (hasMultipleTypes(Attack)) then
 
return GetDamageString(Attack, nil) .. " (" ..
 
GetDamageBiasString(Attack, nil, "") .. ")"
 
else
 
return GetDamageString(Attack, nil)
 
end
 
else
 
return GetDamage(Attack)
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (Shared.contains(Elements, regularVal) or ValName == "PHYSICAL" or
 
ValName == "ELEMENT") then
 
if (Attack.Damage ~= nil) then
 
if (asString) then
 
if (hasMultipleTypes(Attack)) then
 
return GetDamageString(Attack, regularVal)
 
else
 
return nil
 
end
 
else
 
return GetDamage(Attack, regularVal)
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "ACCURACY") then
 
if (Attack.Accuracy ~= nil) then
 
return Attack.Accuracy
 
elseif (Attack == Weapon.NormalAttack) then
 
return Weapon.Accuracy
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "ATTACKNAME") then
 
if (Attack.AttackName ~= nil) then
 
return Attack.AttackName
 
elseif (giveDefault) then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "AMMOCOST") then
 
if (Attack.AmmoCost ~= nil) then
 
if (asString) then
 
return Attack.AmmoCost .. " mun. par tir"
 
else
 
return Attack.AmmoCost
 
end
 
elseif (giveDefault) then
 
return 1
 
else
 
return nil
 
end
 
elseif (ValName == "BLOCKANGLE") then
 
if (Weapon.BlockAngle ~= nil) then
 
if (asString) then
 
return Weapon.BlockAngle .. "°"
 
else
 
return Weapon.BlockAngle
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "BURSTCOUNT") then
 
if (Attack.BurstCount ~= nil) then
 
if (asString) then
 
local result = Attack.BurstCount .. " balles"
 
local dmg = GetDamage(Attack) * Attack.BurstCount
 
return result .. " (" .. Shared.round(dmg, 2, 1) ..
 
" dégâts totaux)"
 
else
 
return Attack.BurstCount
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "BURSTFIRERATE") then
 
if (Attack.BurstFireRate ~= nil) then
 
return Attack.BurstFireRate
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "CHARGETIME") then
 
if (Attack.ChargeTime ~= nil) then
 
if (asString) then
 
return Shared.round(Attack.ChargeTime, 2, 1) .. " s"
 
else
 
return Attack.ChargeTime
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "CRITCHANCE") then -- note there is getValue version
 
-- search in current attack, then normal,
 
if (Attack.CritChance ~= nil) then
 
if (asString) then
 
return Shared.asPercent(Attack.CritChance)
 
else
 
return Attack.CritChance
 
end
 
end
 
if (hasAttack(Weapon, "Normal")) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.CritChance ~= nil) then
 
if (asString) then
 
return Shared.asPercent(normAtt.CritChance)
 
else
 
return normAtt.CritChance
 
end
 
end
 
end
 
if giveDefault then
 
if (asString) then
 
return Shared.asPercent(0)
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "CRITMULTIPLIER") then -- note there is getValue version
 
-- search in current attack, then normal,
 
if (Attack.CritMultiplier ~= nil) then
 
if (asString) then
 
return asMultiplier(Attack.CritMultiplier)
 
else
 
return Attack.CritMultiplier
 
end
 
end
 
if (hasAttack(Weapon, "Normal")) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.CritMultiplier ~= nil) then
 
if (asString) then
 
return asMultiplier(normAtt.CritMultiplier)
 
else
 
return normAtt.CritMultiplier
 
end
 
end
 
end
 
if giveDefault then
 
if (asString) then
 
return asMultiplier(0)
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "DAMAGEBIAS") then
 
if (Shared.tableCount(Attack.Damage) <= 4) then
 
if (GetDamageBiasString(Attack, nil, nil, giveDefault) ~= nil) then
 
return GetDamageBiasString(Attack, nil, nil, giveDefault)
 
else
 
return ""
 
end
 
else
 
if (asString or giveDefault) then
 
return ""
 
else
 
return nil
 
end
 
end
 
elseif (ValName == "BULLETTYPE") then
 
if (Attack.ShotType ~= nil) then
 
return Attack.ShotType
 
elseif (giveDefault) then
 
return "Unknown"
 
else
 
return nil
 
end
 
elseif (ValName == "DURATION") then
 
if (Attack.Duration ~= nil) then
 
if (asString) then
 
return Shared.round(Attack.Duration, 1, 0) .. " s"
 
else
 
return Attack.Duration
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "ELEMENTTYPE") then
 
if (Attack.Damage ~= nil) then
 
for dType, dmg in Shared.skpairs(Attack.Damage) do
 
if (not Shared.contains(Physical, dType) or dmg <= 0) then
 
if (asString) then
 
return Icon._Proc(dType)
 
else
 
return dType
 
end
 
end
 
end
 
elseif asString then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "ELEMENTTYPENAME") then
 
if (Attack.Damage ~= nil) then
 
for dType, dmg in Shared.skpairs(Attack.Damage) do
 
if (not Shared.contains(Physical, dType) or dmg <= 0) then
 
return dType
 
end
 
end
 
elseif asString then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "FALLOFF") then
 
if (Attack.Falloff ~= nil) then
 
local falloffText = "Dégâts max. jusqu'à " ..
 
Shared.round(Attack.Falloff.StartRange, 2, 1) ..
 
" m"
 
falloffText = falloffText .. "<br/>Dégâts min. à partir de " ..
 
Shared.round(Attack.Falloff.EndRange, 2, 1) ..
 
" m"
 
if (Attack.Falloff.Reduction ~= nil) then
 
falloffText = falloffText .. "<br/>" ..
 
Shared.asPercent(Attack.Falloff.Reduction) ..
 
" réduction max."
 
end
 
return falloffText
 
else
 
return nil
 
end
 
elseif (ValName == "FIRERATE") then -- search in current attack, then normal, then weapon supertable
 
local returnVal = 0
 
if (Attack.FireRate ~= nil) then
 
returnVal = Attack.FireRate
 
elseif (hasAttack(Weapon, "Normal")) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.FireRate ~= nil) then
 
returnVal = normAtt.FireRate
 
end
 
elseif (Weapon.FireRate ~= nil) then
 
returnVal = Weapon.FireRate
 
end
 
if (asString) then
 
if (Weapon.Type ~= nil and Weapon.Type ~= "Mêlée" and Weapon.Type ~=
 
"Arch-Melee") then
 
if (forTable) then
 
return Shared.round(returnVal, {3, 1}) -- .." rps"
 
else
 
return Shared.round(returnVal, {3, 1}) ..
 
p.doPlural(" balle<s>/s", returnVal)
 
end
 
else
 
return Shared.round(returnVal, {3, 1})
 
end
 
else
 
return returnVal
 
end
 
if (giveDefault) then
 
returnVal = 0
 
else
 
return nil
 
end
 
elseif (ValName == "FOLLOWTHROUGH") then
 
if (Weapon.FollowThrough ~= nil) then
 
if (asString) then
 
return Shared.asPercent(Weapon.FollowThrough, 0)
 
else
 
return Weapon.FollowThrough
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "HEADSHOTMULTIPLIER") then
 
-- search in current attack, then normal, and finally fall back on base weapon value if for some reason that exists
 
if (Attack.HeadshotMultiplier ~= nil) then
 
if (asString) then
 
return asMultiplier(Attack.HeadshotMultiplier)
 
else
 
return Attack.HeadshotMultiplier
 
end
 
end
 
if (hasAttack(Weapon, "Normal")) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.HeadshotMultiplier ~= nil) then
 
if (asString) then
 
return asMultiplier(normAtt.HeadshotMultiplier)
 
else
 
return normAtt.HeadshotMultiplier
 
end
 
end
 
end
 
if (Weapon.HeadshotMultiplier ~= nil) then
 
if (asString) then
 
return asMultiplier(Weapon.HeadshotMultiplier)
 
else
 
return Weapon.HeadshotMultiplier
 
end
 
end
 
if giveDefault then
 
if (Weapon.Type ~= nil) then
 
-- Setting multiplier based on default for each weapon type
 
if (Weapon.Type == "Secondaire") then
 
if (Weapon.Class == "Shotgun Sidearm") then
 
return '1.2x'
 
elseif (Weapon.Class == "Arbalète" or Weapon.Class ==
 
"Thrown") then
 
return '1.5x'
 
else
 
if (Weapon.Trigger == 'Auto') then
 
return '1.2x'
 
else
 
return '1.5x'
 
end
 
end
 
elseif (Weapon.Type == "Principale") then
 
if (Weapon.Class == "Fusil à Pompe") then
 
return "1.2x"
 
elseif (Weapon.Class == "Arc") then
 
return "2.0x"
 
elseif (Weapon.Class == "Fusil Sniper") then
 
return "1.4x"
 
elseif (Weapon.Class == "Launcher") then
 
return "1.0x"
 
else
 
if (Weapon.Trigger == "Auto") then
 
return '1.2x'
 
else
 
return '1.5x'
 
end
 
end
 
end
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "MELEECOMBODUR") then
 
if (Weapon.MeleeComboDur ~= nil) then
 
if (asString) then
 
return Weapon.MeleeComboDur .. " s"
 
else
 
return Weapon.MeleeComboDur
 
end
 
elseif giveDefault then
 
return 5.0
 
else
 
return nil
 
end
 
elseif (ValName == "MELEERANGE") then
 
if (Weapon.MeleeRange ~= nil) then
 
if (asString) then
 
return Weapon.MeleeRange .. " m"
 
else
 
return Weapon.MeleeRange
 
end
 
elseif giveDefault then
 
return 1
 
else
 
return nil
 
end
 
elseif (ValName == "NOISELEVEL") then
 
if (Attack.NoiseLevel ~= nil) then
 
return Attack.NoiseLevel
 
else
 
return nil
 
end
 
elseif (ValName == "PELLETCOUNT") then
 
if (Attack.PelletCount ~= nil) then
 
if (asString) then
 
if (Attack.PelletName ~= nil) then
 
return Attack.PelletCount .. " " .. Attack.PelletName .. "s"
 
else
 
return Attack.PelletCount .. " Pellets"
 
end
 
else
 
return Attack.PelletCount
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "PELLETDAMAGE") then
 
if (Attack.PelletCount ~= nil) then
 
local result = GetDamageString(Attack, nil, true)
 
if (Attack.PelletName ~= nil) then
 
return result .. " par " .. string.lower(Attack.PelletName)
 
else
 
return result .. " par balle"
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "PELLETNAME") then
 
if (Attack.PelletCount ~= nil) then
 
if (Attack.PelletName ~= nil) then
 
return Attack.PelletName
 
else
 
return "Balle"
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "PELLETPLUS") then
 
if (Attack.PelletCount ~= nil) then
 
local result = Attack.PelletCount .. " ("
 
result = result .. Shared.round(GetDamage(Attack, nil, true), 2, 1)
 
if (Attack.PelletName ~= nil) then
 
return result .. " dégâts par " ..
 
string.lower(Attack.PelletName) .. ")"
 
else
 
return result .. " dégâts par balle)"
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "PROJECTILESPEED") then
 
if (Attack.ShotSpeed ~= nil) then
 
if (asString) then
 
return Attack.ShotSpeed .. " m/s"
 
else
 
return Attack.ShotSpeed
 
end
 
elseif (giveDefault) then
 
if (asString) then
 
return "0 m/s"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "PUNCHTHROUGH") then
 
if (Attack.PunchThrough ~= nil) then
 
if (asString) then
 
return Shared.round(Attack.PunchThrough, 2, 1) .. " m"
 
else
 
return Attack.PunchThrough
 
end
 
elseif giveDefault then
 
if (asString) then
 
return "0 m"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "RADIUS") then
 
if (Attack.Radius ~= nil) then
 
if (asString) then
 
return Shared.round(Attack.Radius, 2, 1) .. " m"
 
else
 
return Attack.Radius
 
end
 
elseif giveDefault then
 
if (asString) then
 
return "0 m"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "RANGE") then
 
if (Attack.Range ~= nil) then
 
if (asString) then
 
return Attack.Range .. " m"
 
else
 
return Attack.Range
 
end
 
elseif (giveDefault) then
 
if (asString) then
 
return "0 m"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "RELOAD") then
 
-- man screw you too Fusilai
 
if (Attack.Reload ~= nil) then
 
if (asString) then
 
return Shared.round(Attack.Reload, 2, 1) .. " s"
 
else
 
return Attack.Reload
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "SPOOL") then
 
if (Attack.Spool ~= nil) then
 
if (asString) then
 
return Attack.Spool .. " balles"
 
else
 
return Attack.Spool
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "STANCES") then
 
return getWeaponStanceList(Weapon)
 
elseif (ValName == "STATUSCHANCE") then -- search in current attck, then normal
 
if (Attack.StatusChance ~= nil) then
 
if (asString) then
 
return Shared.asPercent(Attack.StatusChance)
 
else
 
return Attack.StatusChance
 
end
 
end
 
if (hasAttack(Weapon, "Normal")) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.StatusChance ~= nil) then
 
if (asString) then
 
return Shared.asPercent(normAtt.StatusChance)
 
else
 
return normAtt.StatusChance
 
end
 
end
 
end
 
if giveDefault then
 
if (asString) then
 
return Shared.asPercent(0)
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "TRIGGER") then -- use getValue for tables
 
-- search in current attack, then weapon supertable
 
if (Attack == nil) then
 
if (Weapon.Trigger ~= nil) then return Weapon.Trigger end
 
else
 
return Attack.Trigger
 
end
 
 
if giveDefault then
 
return "Inconnu"
 
else
 
return nil
 
end
 
else
 
return "ERREUR : No such value " .. ValName
 
end
 
 
end
 
end
   
  +
--- Function that returns a simpler getter function, for multiple _stat*() calls on the same weapon/attack pair.
local function getValue(Weapon, ValName, giveDefault, asString, forTable)
 
  +
-- @function p._statFormatter
if (giveDefault == nil) then giveDefault = false end
 
  +
-- @param {table} weap Weapon entry
if (asString == nil) then asString = false end
 
  +
-- @param {number|table} atk Attacks table index or Attack entry
if (forTable == nil) then forTable = false end
 
  +
-- @return {function} Getter function
 
  +
function p._statFormatter(weap, atk)
if (type(ValName) == "table") then
 
  +
return function(...) return p._statFormat(weap, atk, ...) end
local VName1 = string.upper(ValName[1])
 
local VName2 = string.upper(ValName[2])
 
if (VName1 == nil or VName2 == nil) then return nil end
 
 
if (VName1 == "ATTACK" or VName1 == "NORMALATTACK" or VName1 == "NORMAL") then
 
return getAttackValue(Weapon, getAttack(Weapon, "Normal"), VName2,
 
giveDefault, asString, forTable)
 
elseif (VName1 == "AREA" or VName1 == "AREAATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "Area"), VName2,
 
giveDefault, asString, forTable)
 
elseif (VName1 == "SECONDARYAREA" or VName1 == "SECONDARYAREAATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "SecondaryArea"),
 
VName2, giveDefault, asString, forTable)
 
elseif (VName1 == "CHARGE" or VName1 == "CHARGEATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "Charge"), VName2,
 
giveDefault, asString, forTable)
 
elseif (VName1 == "CHARGEDTHROW" or VName1 == "CHARGEDTHROWATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "ChargedThrow"),
 
VName2, giveDefault, asString, forTable)
 
elseif (VName1 == "THROW" or VName1 == "THROWATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "Throw"), VName2,
 
giveDefault, asString, forTable)
 
elseif (VName1 == "SECONDARY" or VName1 == "SECONDARYATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "Secondary"),
 
VName2, giveDefault, asString, forTable)
 
elseif (VName1 == "SLAM" or VName1 == "SLAMATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "MeleeSlam"),
 
VName2, giveDefault, asString, forTable)
 
elseif (VName1 == "HEAVY" or VName1 == "HEAVYATTACK") then
 
return getAttackValue(Weapon, getAttack(Weapon, "MeleeHeavy"),
 
VName2, giveDefault, asString, forTable)
 
else
 
return "ERROR: No such attack '" .. VName1 .. "'"
 
end
 
end
 
 
ValName = string.upper(ValName)
 
if (ValName == "NAME") then
 
if (Weapon.Name ~= nil) then
 
if (forTable) then
 
if (string.find(Weapon.Name, "Atmosphère") ~= nil) then
 
return "[[" ..
 
string.gsub(Weapon.Name, "%s%(Atmosphère%)", "") ..
 
"|" .. string.gsub(Weapon.Name, "osphère", "") ..
 
"]]"
 
else
 
return "[[" .. Weapon.Name .. "]]"
 
end
 
else
 
return Weapon.Name
 
end
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "AMMOTYPE") then
 
if (Weapon.AmmoType ~= nil) then
 
return Weapon.AmmoType
 
elseif giveDefault then
 
if (Weapon.Type ~= nil) then
 
if (Weapon.Type == "Secondaire") then
 
return "Pistolet"
 
elseif (Weapon.Type == "Principale") then
 
if (Weapon.Class == nil or Weapon.Class == "Fusil") then
 
return "Fusil"
 
elseif (Weapon.Class == "Fusil à Pompe") then
 
return "Fusil à Pompe"
 
elseif (Weapon.Class == "Fusil de Sniper" or Weapon.Class ==
 
"Arc" or Weapon.Class == "Launcher") then
 
return "Sniper/Arc"
 
end
 
end
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "AUGMENT") then
 
local augments = Mod.getWeaponAugments(Weapon)
 
local ret = {}
 
for i, Aug in pairs(augments) do
 
if (Aug.PvP) then
 
table.insert(ret,
 
Tooltip._tooltipText(Aug.Name, 'Mod') .. ' (PvP)')
 
else
 
table.insert(ret, Tooltip._tooltipText(Aug.Name, 'Mod'))
 
end
 
end
 
return table.concat(ret, '\n')
 
elseif (ValName == "BLOCKRESIST") then
 
if (Weapon.Class ~= nil) then
 
if (Weapon.Type == "Mêlée") then
 
if (Weapon.BlockResist ~= nil) then
 
if (asString) then
 
return Shared.asPercent(Weapon.BlockResist)
 
else
 
return Weapon.BlockResist
 
end
 
elseif (Weapon.Class == "Claws" or Weapon.Class == "Dague" or
 
Weapon.Class == "Doubles Dagues" or Weapon.Class == "Glaive" or
 
Weapon.Class == "Nunchaku" or Weapon.Class == "Rapier" or
 
Weapon.Class == "Sparring" or Weapon.Class == "Tonfa" or
 
Weapon.Class == "Whip") then
 
if (asString) then
 
return "35%"
 
else
 
return .35
 
end
 
elseif (Weapon.Class == "Blade and Whip" or Weapon.Class ==
 
"Doubles Épées" or Weapon.Class == "Fist" or Weapon.Class ==
 
"Gunblade" or Weapon.Class == "Bâton" or Weapon.Class ==
 
"Épée") then
 
if (asString) then
 
return "60%"
 
else
 
return .6
 
end
 
elseif (Weapon.Class == "Marteau" or Weapon.Class ==
 
"Lame Lourde" or Weapon.Class == "Machette" or Weapon.Class ==
 
"Nikana" or Weapon.Class == "Polearm" or Weapon.Class ==
 
"Faux" or Weapon.Class == "Épée et Bouclier") then
 
if (asString) then
 
return "85%"
 
else
 
return .85
 
end
 
end
 
 
else
 
return ""
 
end
 
else
 
return ""
 
end
 
elseif (ValName == "CHANNELMULT") then
 
if (Weapon.ChannelMult ~= nil) then
 
if (asString) then
 
return asMultiplier(Weapon.ChannelMult)
 
else
 
return Weapon.ChannelMult
 
end
 
elseif giveDefault and (Weapon.Type ~= nil and Weapon.Type == "Mêlée") then
 
if (asString) then
 
return "1.5x"
 
else
 
return 1.5
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "CLASS") then
 
if (Weapon.Class ~= nil) then
 
return "[[:Category:" .. Weapon.Class .. "|" .. Weapon.Class .. "]]"
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "CONCLAVE") then
 
if (Weapon.Conclave ~= nil) then
 
if (asString) then
 
if (Weapon.Conclave) then
 
return "Yes"
 
else
 
return "No"
 
end
 
else
 
return Weapon.Conclave
 
end
 
elseif giveDefault then
 
return false
 
else
 
return nil
 
end
 
elseif (ValName == "DISPOSITION") then
 
if (Weapon.Type ~= nil and Weapon.Type == "Arch-Mêlée" or Weapon.Type ==
 
"Emplacement") then return nil end
 
 
if (Weapon.Disposition ~= nil) then
 
if (asString) then
 
return Icon._Dis(Weapon.Disposition) ..
 
'<div style="display:inline; position:relative; bottom:2px">(' ..
 
Weapon.Disposition .. ')</div>'
 
else
 
return Weapon.Disposition
 
end
 
elseif giveDefault then
 
if (asString) then
 
return "Inconnu"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "DISPOSITION5") then
 
if (Weapon.Type ~= nil and
 
(Weapon.Type == "Arch-Fusil" or Weapon.Type == "Arch-Mêlée") or
 
Weapon.Type == "Emplacement") then return nil end
 
 
if (Weapon.Disposition ~= nil) then
 
if (Weapon.Disposition < 0.7) then
 
return 1
 
elseif (Weapon.Disposition < 0.9) then
 
return 2
 
elseif (Weapon.Disposition <= 1.1) then
 
return 3
 
elseif (Weapon.Disposition <= 1.3) then
 
return 4
 
else
 
return 5
 
end
 
elseif giveDefault then
 
if (asString) then
 
return "Inconnu"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "FAMILY") then
 
if (Weapon.Family ~= nil) then
 
if (asString) then
 
if (Weapon.Family ~= nil) then
 
local FamilyString = ""
 
local Family = getFamily(Weapon.Family)
 
for i, Weap in pairs(Family) do
 
if (Weap.Name ~= Weapon.Name) then
 
if (string.len(FamilyString) > 0) then
 
FamilyString = FamilyString .. "<br/>"
 
end
 
FamilyString =
 
FamilyString ..
 
Tooltip._tooltipText(Weap.Name, 'Weapon')
 
end
 
end
 
return FamilyString
 
end
 
else
 
return Weapon.Family
 
end
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "EXILUSPOLARITY") then
 
if (Weapon.ExilusPolarity ~= nil) then
 
return Icon._Pol(Weapon.ExilusPolarity)
 
else
 
return nil
 
end
 
elseif (ValName == "FINISHERDAMAGE") then
 
if (Weapon.FinisherDamage ~= nil) then
 
if (asString) then
 
return Shared.round(Weapon.FinisherDamage, 2, 1)
 
else
 
return Weapon.FinisherDamage
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "IMAGE") then
 
if (Weapon.Image ~= nil) then
 
return Weapon.Image
 
elseif giveDefault then
 
return "Panel.png"
 
else
 
return nil
 
end
 
elseif (ValName == "HEAVYATTACK") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.Damage ~= nil) then
 
local heavyDamage = Weapon.MeleeHeavy.Damage
 
if (asString) then
 
if (Weapon.MeleeHeavy.Element ~= nil) then
 
return Icon._Proc(Weapon.MeleeHeavy.Element) .. " " ..
 
Shared.round(heavyDamage, 2, 1)
 
else
 
return Shared.round(heavyDamage, 2, 1)
 
end
 
else
 
return heavyDamage
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "HEAVYELEMENT") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.Element ~= nil) then
 
return Weapon.MeleeHeavy.Element
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "WINDUP") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.WindUp ~= nil) then
 
local windUp = Weapon.MeleeHeavy.WindUp
 
if (asString) then
 
return windUp .. " s"
 
else
 
return windUp
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "HEAVYSLAMATTACK") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.SlamDamage ~= nil) then
 
local heavySlamDamage = Weapon.MeleeHeavy.SlamDamage
 
if (asString) then
 
if (Weapon.MeleeHeavy.SlamElement ~= nil) then
 
return Icon._Proc(Weapon.MeleeHeavy.SlamElement) .. " " ..
 
Shared.round(heavySlamDamage, 2, 1)
 
else
 
return Shared.round(heavySlamDamage, 2, 1)
 
end
 
else
 
return heavySlamDamage
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "HEAVYSLAMELEMENT") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.SlamElement ~= nil) then
 
return Weapon.MeleeHeavy.SlamElement
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "HEAVYRADIALDMG") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.SlamRadialDamage ~=
 
nil) then
 
local damage = Weapon.MeleeHeavy.SlamRadialDamage
 
if (asString) then
 
if (Weapon.MeleeHeavy.SlamRadialElement ~= nil) then
 
return Icon._Proc(Weapon.MeleeHeavy.SlamRadialElement) ..
 
" " .. Shared.round(damage, 2, 1)
 
else
 
return Shared.round(damage, 2, 1)
 
end
 
else
 
return damage
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "HEAVYRADIALELEMENT") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.SlamRadialElement ~=
 
nil) then
 
return Weapon.MeleeHeavy.SlamRadialElement
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "HEAVYSLAMRADIUS") then
 
if (Weapon.MeleeHeavy ~= nil and Weapon.MeleeHeavy.SlamRadius ~= nil) then
 
local radius = Weapon.MeleeHeavy.SlamRadius
 
if (asString) then
 
return Shared.round(radius, 2, 1) .. " m"
 
else
 
return radius
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "INTRODUCED") then
 
if (Weapon.Introduced ~= nil) then
 
return Weapon.Introduced
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "JUMPATTACK") then
 
if (Weapon.JumpAttack ~= nil) then
 
if (asString) then
 
if (Weapon.JumpElement ~= nil) then
 
return Icon._Proc(Weapon.JumpElement) .. " " ..
 
Shared.round(Weapon.JumpAttack, 2, 1)
 
else
 
return Shared.round(Weapon.JumpAttack, 2, 1)
 
end
 
else
 
return Weapon.JumpAttack
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "JUMPELEMENT") then
 
if (Weapon.JumpElement ~= nil) then
 
return Weapon.JumpElement
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "JUMPRADIUS") then
 
if (Weapon.JumpRadius ~= nil) then
 
if (asString) then
 
return Shared.round(Weapon.JumpRadius, 2, 1) .. " m"
 
else
 
return Weapon.JumpRadius
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "MAGAZINE") then
 
if (Weapon.Magazine ~= nil) then
 
if (asString) then
 
if (forTable) then
 
return Weapon.Magazine
 
else
 
return Weapon.Magazine ..
 
p.doPlural(" balle<s> par chargeur",
 
Weapon.Magazine)
 
end
 
else
 
return Weapon.Magazine
 
end
 
elseif giveDefault then
 
if (asString) then
 
return "0 balles par chargeur"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "MASTERY") then
 
if (Weapon.Mastery ~= nil) then
 
return Weapon.Mastery
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "MAXAMMO") then
 
local returnVal = 0
 
if (Weapon.MaxAmmo ~= nil) then
 
returnVal = Weapon.MaxAmmo
 
elseif giveDefault then
 
if (Weapon.Type ~= nil) then
 
if (Weapon.Type == "Secondaire") then
 
returnVal = 210
 
elseif (Weapon.Type == "Principale") then
 
if (Weapon.Class == nil or Weapon.Class == "Fusil") then
 
returnVal = 540
 
elseif (Weapon.Class == "Fusil à Pompe") then
 
returnVal = 120
 
elseif (Weapon.Class == "Arc" or Weapon.Class ==
 
"Fusil Sniper") then
 
returnVal = 72
 
end
 
end
 
end
 
else
 
return nil
 
end
 
if (asString) then
 
if (returnVal > 0) then
 
return returnVal .. " munitions"
 
else
 
return nil
 
end
 
else
 
if (returnVal > 0) then
 
return returnVal
 
else
 
return nil
 
end
 
end
 
elseif (ValName == "NOISELEVEL") then
 
if (Weapon.NoiseLevel ~= nil) then
 
return Weapon.NoiseLevel
 
elseif giveDefault then
 
if (Weapon.Type ~= nil and Weapon.Type ~= "Mêlée" and Weapon.Type ~=
 
"Arch-Melee") then
 
if (Weapon.Class ~= nil and
 
(Weapon.Class == "Arc" or Weapon.Class == "Thrown")) then
 
return "Silencieux"
 
else
 
return "Bruyant"
 
end
 
else
 
return nil
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "POLARITIES") then
 
if (Weapon.Polarities ~= nil and type(Weapon.Polarities) == "table") then
 
if (asString) then
 
return p.GetPolarityString(Weapon)
 
else
 
return Weapon.Polarities
 
end
 
elseif giveDefault then
 
if (asString) then
 
return "Aucune"
 
else
 
return {}
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "RELOAD") then
 
if (Weapon.Reload ~= nil) then
 
if (asString) then
 
if (Weapon.ReloadStyle ~= nil) then
 
if (Weapon.ReloadStyle == "ByRound" and Weapon.Magazine ~=
 
nil) then
 
local result = Shared.round(
 
Weapon.Reload / Weapon.Magazine, 2, 1) ..
 
" sec per round"
 
result = result .. " (" ..
 
Shared.round(Weapon.Reload, 2, 1) ..
 
"s total)"
 
if (forTable) then
 
result = Shared.round(Weapon.Reload, 2, 1) .. " s"
 
end
 
return result
 
elseif (Weapon.ReloadStyle == "Regenerate") then
 
local result = Shared.round(Weapon.Reload, 2, 1) -- .."rounds p. s."
 
if (Weapon.Magazine ~= nil) then
 
result = result .. " (" ..
 
Shared.round(
 
Weapon.Magazine / Weapon.Reload, 2,
 
1) .. "s total)"
 
if (forTable) then
 
result =
 
Shared.round(
 
Weapon.Magazine / Weapon.Reload, 2, 1) ..
 
" s"
 
end
 
end
 
return result
 
end
 
end
 
return Shared.round(Weapon.Reload, 2, 1) .. " s"
 
else
 
return Weapon.Reload
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "SLAMATTACK") then
 
if (Weapon.MeleeSlam ~= nil and Weapon.MeleeSlam.Damage ~= nil) then
 
local slamDamage = Weapon.MeleeSlam.Damage
 
if (asString) then
 
if (Weapon.MeleeSlam.Element ~= nil) then
 
return Icon._Proc(Weapon.MeleeSlam.Element) .. " " ..
 
Shared.round(slamDamage, 2, 1)
 
else
 
return Shared.round(slamDamage, 2, 1)
 
end
 
else
 
return Weapon.MeleeSlam
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "SLAMELEMENT") then
 
if (Weapon.MeleeSlam ~= nil and Weapon.MeleeSlam.Element ~= nil) then
 
return Weapon.MeleeSlam.Element
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "SLAMRADIALDMG") then
 
if (Weapon.MeleeSlam ~= nil and Weapon.MeleeSlam.RadialDamage ~= nil) then
 
local radialDamage = Weapon.MeleeSlam.RadialDamage
 
if (asString) then
 
local radialElement = Weapon.MeleeSlam.RadialElement
 
if (radialElement ~= nil) then
 
return Icon._Proc(radialElement) .. " " ..
 
Shared.round(radialDamage, 2, 1)
 
else
 
return Shared.round(radialDamage, 2, 1)
 
end
 
else
 
return radialDamage
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "SLAMRADIALELEMENT") then
 
if (Weapon.MeleeSlam ~= nil and Weapon.MeleeSlam.Element ~= nil) then
 
return Weapon.MeleeSlam.Element
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "SLAMRADIUS") then
 
if (Weapon.MeleeSlam ~= nil and Weapon.MeleeSlam.Radius ~= nil) then
 
local radius = Weapon.MeleeSlam.Radius
 
if (asString) then
 
return Shared.round(radius, 2, 1) .. " m"
 
else
 
return radius
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "SLIDEATTACK") then
 
if (Weapon.SlideAttack ~= nil) then
 
if (asString) then
 
if (Weapon.SlideElement ~= nil) then
 
return Icon._Proc(Weapon.SlideElement) .. " " ..
 
Shared.round(Weapon.SlideAttack, 2, 1)
 
else
 
return Shared.round(Weapon.SlideAttack, 2, 1)
 
end
 
else
 
return Weapon.SlideAttack
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "SLIDEELEMENT") then
 
if (Weapon.SlideElement ~= nil) then
 
return Weapon.SlideElement
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "SNIPERCOMBORESET") then
 
if (Weapon.SniperComboReset ~= nil) then
 
if (asString) then
 
return Shared.round(Weapon.SniperComboReset, 2, 1) .. " s"
 
else
 
return Weapon.SniperComboReset
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "SNIPERCOMBOMIN") then
 
if (Weapon.SniperComboMin ~= nil) then
 
if (asString) then
 
return Weapon.SniperComboMin .. " tirs"
 
else
 
return Weapon.SniperComboMin
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "STAGGER") then
 
if (Weapon.Stagger ~= nil) then
 
return Weapon.Stagger
 
elseif giveDefault then
 
return "No"
 
else
 
return nil
 
end
 
elseif (ValName == "STANCEPOLARITY") then
 
if (Weapon.StancePolarity ~= nil) then
 
if (asString) then
 
return Icon._Pol(Weapon.StancePolarity)
 
else
 
return Weapon.StancePolarity
 
end
 
elseif giveDefault then
 
return "Aucune"
 
else
 
return nil
 
end
 
elseif (ValName == "SYNDICATEEFFECT") then
 
if (Weapon.SyndicateEffect ~= nil) then
 
if (asString) then
 
return "[[" .. Weapon.SyndicateEffect .. "]]"
 
else
 
return Weapon.SyndicateEffect
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "TRAITS") then
 
if (Weapon.Traits ~= nil) then
 
return Weapon.Traits
 
elseif giveDefault then
 
return {}
 
else
 
return nil
 
end
 
elseif (ValName == "TRIGGER") then -- Note there is a specific version for each attack in getAttackValue
 
if (Weapon.Trigger ~= nil) then
 
if (forTable) then -- return chargetime and burstcount only in tables
 
local trigger = Weapon.Trigger
 
if (trigger == "Charge") then
 
local cTime = getAttackValue(Weapon,
 
getAttack(Weapon, "Charge"),
 
"ChargeTime", false)
 
if (cTime ~= nil) then
 
return trigger .. " (" .. Shared.round(cTime, 2, 1) ..
 
"s)"
 
else
 
return trigger
 
end
 
elseif (trigger == "Rafale") then
 
local bCount = getAttackValue(Weapon,
 
getAttack(Weapon, "Normal"),
 
"BurstCount", false)
 
if (bCount ~= nil) then
 
return trigger .. " (" .. bCount .. ")"
 
else
 
return trigger
 
end
 
else
 
return trigger
 
end
 
else
 
return Weapon.Trigger
 
end
 
 
elseif giveDefault then
 
return "Unknown"
 
else
 
return nil
 
end
 
elseif (ValName == "CRITCHANCE") then -- Note there is a specific version for each attack in getAttackValue
 
-- search in charge attck, then normal, then secondary if still null
 
local returnVal = 0
 
local Attack = {}
 
if (hasAttack(Weapon, "Charge")) then
 
local chargAtt = getAttack(Weapon, "Charge")
 
if (chargAtt.CritChance ~= nil) then
 
returnVal = chargAtt.CritChance
 
Attack = chargAtt
 
end
 
end
 
if (hasAttack(Weapon, "Normal") and returnVal == 0) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.CritChance ~= nil) then
 
returnVal = normAtt.CritChance
 
Attack = normAtt
 
end
 
end
 
if (hasAttack(Weapon, "Secondaire") and returnVal == 0) then
 
local secAtt = getAttack(Weapon, "Secondaire")
 
if (secAtt.CritChance ~= nil) then
 
returnVal = secAtt.CritChance
 
Attack = secAtt
 
end
 
end
 
if (asString and returnVal ~= 0) then
 
return Shared.asPercent(returnVal)
 
else
 
return returnVal
 
end
 
if giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "CRITMULTIPLIER") then -- Note there is a specific version for each attack in getAttackValue
 
-- search in charge attck, then normal, then secondary if still null
 
local returnVal = 0
 
local Attack = {}
 
if (hasAttack(Weapon, "Charge")) then
 
local chargAtt = getAttack(Weapon, "Charge")
 
if (chargAtt.CritMultiplier ~= nil) then
 
returnVal = chargAtt.CritMultiplier
 
Attack = chargAtt
 
end
 
end
 
if (hasAttack(Weapon, "Normal") and returnVal == 0) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.CritMultiplier ~= nil) then
 
returnVal = normAtt.CritMultiplier
 
Attack = normAtt
 
end
 
end
 
if (hasAttack(Weapon, "Secondaire") and returnVal == 0) then
 
local secAtt = getAttack(Weapon, "Secondaire")
 
if (secAtt.CritMultiplier ~= nil) then
 
returnVal = secAtt.CritMultiplier
 
Attack = secAtt
 
end
 
end
 
if (asString and returnVal ~= 0) then
 
return asMultiplier(returnVal)
 
else
 
return returnVal
 
end
 
if giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "FIRERATE") then -- Note there is a specific version for each attack in getAttackValue -- search in global weapon then normal attck, then charge, then secondary if still null
 
local returnVal = 0
 
local Attack = {}
 
if (Weapon.FireRate ~= nil) then returnVal = Weapon.FireRate end
 
if (hasAttack(Weapon, "Normal") and returnVal == 0) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.FireRate ~= nil) then
 
returnVal = normAtt.FireRate
 
Attack = normAtt
 
end
 
end
 
if (hasAttack(Weapon, "Charge") and returnVal == 0) then
 
local chargAtt = getAttack(Weapon, "Charge")
 
if (chargAtt.FireRate ~= nil) then
 
returnVal = chargAtt.FireRate
 
Attack = chargAtt
 
end
 
end
 
if (hasAttack(Weapon, "Secondary") and returnVal == 0) then
 
local secAtt = getAttack(Weapon, "Secondary")
 
if (secAtt.FireRate ~= nil) then
 
returnVal = secAtt.FireRate
 
Attack = secAtt
 
end
 
end
 
if (asString and returnVal ~= 0) then
 
if (Weapon.Type ~= nil and Weapon.Type ~= "Mêlée" and Weapon.Type ~=
 
"Arch-Melee") then
 
if (forTable) then
 
return Shared.round(returnVal, {3, 1}) -- .." rps"
 
else
 
return Shared.round(returnVal, {3, 1}) ..
 
p.doPlural(" balle<s>/s", returnVal)
 
end
 
else
 
return Shared.round(returnVal, {3, 1})
 
end
 
else
 
return returnVal
 
end
 
if giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "STATUSCHANCE") then -- Note there is a specific version for each attack in getAttackValue
 
-- search in charge attck, then normal, then secondary if still null
 
local returnVal = 0
 
local Attack = {}
 
if (hasAttack(Weapon, "Charge")) then
 
local chargAtt = getAttack(Weapon, "Charge")
 
if (chargAtt.StatusChance ~= nil) then
 
returnVal = chargAtt.StatusChance
 
Attack = chargAtt
 
end
 
end
 
if (hasAttack(Weapon, "Normal") and returnVal == 0) then
 
local normAtt = getAttack(Weapon, "Normal")
 
if (normAtt.StatusChance ~= nil) then
 
returnVal = normAtt.StatusChance
 
Attack = normAtt
 
end
 
end
 
if (hasAttack(Weapon, "Secondaire") and returnVal == 0) then
 
local secAtt = getAttack(Weapon, "Secondaire")
 
if (secAtt.StatusChance ~= nil) then
 
returnVal = secAtt.StatusChance
 
Attack = secAtt
 
end
 
end
 
if (asString and returnVal ~= 0) then
 
return Shared.asPercent(returnVal)
 
else
 
return returnVal
 
end
 
if giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "TYPE") then
 
if (Weapon.Type ~= nil) then
 
return Weapon.Type
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "USERS") then
 
if (Weapon.Users ~= nil) then
 
if (asString) then
 
local result = ""
 
for i, str in pairs(Weapon.Users) do
 
if (i > 1) then
 
result = result .. '<br/>'
 
end
 
result = result .. str
 
end
 
return result
 
else
 
return Weapon.Users
 
end
 
elseif giveDefault then
 
return {}
 
else
 
return nil
 
end
 
elseif (ValName == "WALLATTACK") then
 
if (Weapon.WallAttack ~= nil) then
 
if (asString) then
 
if (Weapon.WallElement ~= nil) then
 
return Icon._Proc(Weapon.WallElement) .. " " ..
 
Shared.round(Weapon.WallAttack, 2, 1)
 
else
 
return Shared.round(Weapon.WallAttack, 2, 1)
 
end
 
else
 
return Weapon.WallAttack
 
end
 
elseif giveDefault then
 
return 0
 
else
 
return nil
 
end
 
elseif (ValName == "WALLELEMENT") then
 
if (Weapon.WallElement ~= nil) then
 
return Weapon.WallElement
 
elseif giveDefault then
 
return ""
 
else
 
return nil
 
end
 
elseif (ValName == "ZOOM") then
 
if (Weapon.Zoom ~= nil) then
 
if (asString) then
 
local result = ""
 
for i, str in pairs(Weapon.Zoom) do
 
if (i > 1) then
 
result = result .. '<br/>'
 
end
 
result = result .. str
 
end
 
return result
 
else
 
return Weapon.Zoom
 
end
 
else
 
return nil
 
end
 
elseif (ValName == "SPECIALFSPEED") then -- to show we can put very special keywords... if wanted
 
if (Weapon.Name == "Drakgoon") then
 
if (asString) then
 
return "bounce: 25 m/s"
 
else
 
return 25
 
end
 
elseif (getAttackValue(Weapon, getAttack(Weapon, "Area"),
 
"PROJECTILESPEED") ~= nil) then
 
if (asString) then
 
return getAttackValue(Weapon, getAttack(Weapon, "Area"),
 
"PROJECTILESPEED", true, true)
 
else
 
return getAttackValue(Weapon, getAttack(Weapon, "Area"),
 
"PROJECTILESPEED")
 
end
 
elseif (getAttackValue(Weapon, getAttack(Weapon, "Secondary"),
 
"PROJECTILESPEED") ~= nil) then
 
if (asString) then
 
return getAttackValue(Weapon, getAttack(Weapon, "Secondary"),
 
"PROJECTILESPEED", true, true)
 
else
 
return getAttackValue(Weapon, getAttack(Weapon, "Secondary"),
 
"PROJECTILESPEED")
 
end
 
elseif (giveDefault) then
 
if (asString) then
 
return "0 m/s"
 
else
 
return 0
 
end
 
else
 
return nil
 
end
 
 
else
 
-- if everything failed (and it should NOT) try in the getAttackValue
 
return getAttackValue(Weapon, getAttack(Weapon, "Normal"), ValName,
 
giveDefault, asString)
 
end
 
 
end
 
end
   
  +
--- Returns a subset of <code>/data</code> or <code>/Conclave/data</code> based on a validation function.
function p.getValue(frame)
 
  +
-- @function p._getWeapons
local WeapName = frame.args[1]
 
  +
-- @param {function} validateFunction Function that filters out a weapon by taking in a Weapon table argument
local ValName1 = frame.args[2]
 
  +
-- @param[opt] {string} source Name of weapon entry to use
local ValName2 = frame.args[3]
 
  +
-- @param[opt] {boolean} ignoreIgnore If true, ignores the _IgnoreEntry flag, false otherwise; defaults to false
local AsString = frame.args["Raw"] == nil or frame.args["Raw"] == ''
 
  +
-- @param[opt] {function} sortFunc Custom comparison function; false -> no sorting; defaults to sorting in ascending order by weapon name
 
  +
-- @returns {table} Table of weapon table entries as seen in <code>/data</code>
if (WeapName == nil) then
 
  +
function p._getWeapons(validateFunction, source, opts)
return ""
 
  +
opts=opts or {}
elseif (ValName1 == nil) then
 
  +
local ignoreIgnore, sortFunc, pvp = opts.ignoreIgnore, opts.sortFunc, opts.pvp
return "ERREUR : Pas de valeur spécifiée"
 
  +
validateFunction = validateFunction or function() return true end
end
 
  +
local data = pvp and ConclaveData or WeaponData
 
  +
if source then
local theWeap = p.getWeapon(WeapName)
 
  +
data = data[source]
if (theWeap == nil) then return "" end
 
  +
end
 
local vName
 
local useDefault
 
if (ValName2 == nil or ValName2 == "") then
 
vName = ValName1
 
useDefault = Shared.contains(UseDefaultList, string.upper(ValName1))
 
else
 
vName = {ValName1, ValName2}
 
useDefault = Shared.contains(UseDefaultList, string.upper(ValName2))
 
end
 
   
  +
local weaps = {}
return getValue(theWeap, vName, useDefault, AsString)
 
  +
for _, weap in pairs(data) do
  +
if (ignoreIgnore or not weap['_IgnoreEntry']) and validateFunction(weap) then
  +
table.insert(weaps, weap)
  +
end
  +
end
  +
if sortFunc ~= false then
  +
table.sort(weaps, sortFunc or function(a, b) return a.Name < b.Name end)
  +
end
  +
return weaps
 
end
 
end
   
  +
--- Returns all melee weapons. If weapType is not nil, only grab for a specific type
function p.getConclaveValue(frame)
 
  +
-- For example, if weapType is "Nikana", only pull Nikanas.
local WeapName = frame.args[1]
 
  +
-- @function p._getMeleeWeapons
local ValName1 = frame.args[2]
 
  +
-- @param[opt] {boolean} weapType
local ValName2 = frame.args[3]
 
  +
-- @param[opt] {boolean} pvp If true, only gets melee weapons available in Conclave, false otherwise; defaults to false
if (WeapName == nil) then
 
  +
-- @returns {table} An array of melee weapon table entries as seen in <code>/data</code>
return ""
 
  +
function p._getMeleeWeapons(weapType,pvp)
elseif (ValName1 == nil) then
 
  +
return p._getWeapons(weapType and function(weap) return weap.Class==weapType end, 'mêlée',{['pvp']=pvp==true})
return "ERROR: No value selected"
 
end
 
 
local theWeap = p.getConclaveWeapon(WeapName)
 
if (theWeap == nil) then return "" end
 
 
local vName
 
local useDefault
 
if (ValName2 == nil or ValName2 == "") then
 
vName = ValName1
 
useDefault = Shared.contains(UseDefaultList, string.upper(ValName1))
 
else
 
vName = {ValName1, ValName2}
 
useDefault = Shared.contains(UseDefaultList, string.upper(ValName2))
 
end
 
 
return getValue(theWeap, vName, useDefault, true)
 
 
end
 
end
   
  +
--- Main frame invokable function to access any raw/computed attribute/column/key of a weapon entry.
local function buildInfoboxRow(Label, Text, Collapse, Digits, Addon)
 
  +
-- See default table in M:Weapons to see all valid computed attributes.
local result = ""
 
  +
-- @function p.getValue
 
  +
-- @param {string} weap Weapon name in EN locale
if (Collapse == nil) then
 
  +
-- @param {number} atk Attacks table index
Collapse = false
 
  +
-- @param {string} k Key name
elseif (Collapse == 1) then
 
  +
-- @return Raw or computed value associated with k key
Collapse = true
 
  +
function p.getValue(frame)
end
 
  +
-- table.unpack doesn't work on the frame object which is why this is anonymous function is needed
if (Text ~= nil) then
 
  +
local weap, key, atk = (function(t) return t[1], t[2], t[3] end)(frame.args)
result = '<div style="margin-left:-3px;" '
 
  +
weap = p._getWeapon(weap)
if (Collapse) then
 
  +
return p._getValue(weap, key, atk)
result = result .. 'class="Infobox_Collapsible"'
 
end
 
result = result .. '>'
 
result = result .. '\n{| style="width:100%;"'
 
result = result .. '\n|-'
 
result = result ..
 
'\n| class="left" | <span style="white-space:nowrap;">'
 
result = result .. "'''" .. Label .. "'''"
 
result = result .. '</span>'
 
result = result .. '\n| class="right" | '
 
if (Digits == nil) then
 
result = result .. Text
 
else
 
result = result .. Shared.round(Text, Digits)
 
end
 
if (Addon ~= nil) then result = result .. p.doPlural(Addon, Text) end
 
result = result .. '\n|}'
 
result = result .. '\n</div>'
 
end
 
return result
 
 
end
 
end
   
  +
--- Main frame invokable function to access any formatted attribute/column/key of a weapon entry.
-- Returns a list of weapons for a stance
 
  +
-- See default table in M:Weapons to see all valid computed attributes.
-- Adds a ✓ if the weapon matches the polarity
 
function p.getStanceWeaponList(frame)
+
-- @function p.getFormattedValue
  +
-- @param {string} weap Weapon name in EN locale
 
  +
-- @param {number} atk Attacks table index
local StanceName = frame.args ~= nil and frame.args[1] or frame
 
  +
-- @param {string} k Key name
local Stance = Mod.getStance(StanceName)
 
  +
-- @return Formatted value associated with k key
 
  +
function p.getFormattedValue(frame)
if (Stance == nil) then
 
  +
local weap, key, atk = (function(t) return t[1], t[2], t[3] end)(frame.args)
return "ERREUR : " .. StanceName .. " introuvable"
 
  +
weap = p._getWeapon(weap)
end
 
  +
return p._getFormattedValue(weap, key, atk)
 
local stancePvP = Mod._getValue(Stance.Name, "PVP")
 
local weaps = getMeleeWeapons(Stance.Class, stancePvP)
 
local result = ''
 
 
for i, weap in Shared.skpairs(weaps) do
 
if (weap.Name ~= 'Désarmé') then
 
result = result .. '\n*' ..
 
Tooltip._tooltipText(weap.Name, 'Weapon', nil,
 
stancePvP)
 
if (weap.StancePolarity ~= nil) then
 
local weapStancePolIcon = Icon._Pol(weap.StancePolarity)
 
local modPolIcon = Icon._Pol(
 
Mod._getValue(Stance.Name, "POLARITY"))
 
if (weapStancePolIcon == modPolIcon) then
 
result = result .. " ✓"
 
end
 
end
 
end
 
end
 
 
return result
 
 
end
 
end
   
  +
--- Builds a melee weapon gallery as seen on [[Template:MeleeCategory]].
-- Returns a list of stances for a weapon
 
  +
-- @function p.getMeleeWeaponGallery
-- Adds the polarity for each stance
 
  +
-- @param {table} frame Frame object w/ first argumenting being string meleeClass
function p.getWeaponStanceList(frame)
 
  +
-- @returns {string} Resultant wikitext of gallery
local WeaponName = frame.args ~= nil and frame.args[1] or frame
 
  +
function p.getMeleeWeaponGallery(frame)
local Weapon = p.getWeapon(WeaponName)
 
  +
local meleeClass = frame.args[1] or ''
 
  +
local result = { "== Armes de type : "..meleeClass.."==", '<gallery widths="200" position="center" spacing="small">' }
if (Weapon == nil) then return "ERROR: " .. WeaponName .. " not found" end
 
  +
for i, weap in ipairs(p._getMeleeWeapons(meleeClass)) do
 
  +
table.insert(result, p._statRead(weap, nil, 'Image')..'|'..p._statFormat(weap, nil, 'Name'))
return getWeaponStanceList(Weapon)
 
  +
end
  +
table.insert(result, '</gallery>')
  +
return frame:preprocess(table.concat(result, '\n')) -- annoying that it needs to be preprocessed
 
end
 
end
   
  +
--- Gets the total count of weapons as used on [[Mastery Rank#Total Mastery]].
local function getWeaponGallery(Weapons)
 
  +
-- @function p.getWeaponCount
local result = '{| style="margin:auto;text-align:center;"'
 
  +
-- @param {table} frame Frame object w/ the first argument being the weaponSlot and the
local nameRow = ''
 
  +
-- second argument being a boolean to getFullList
for i, Weapon in pairs(Weapons) do
 
  +
-- @returns {number} Total count of weapons in a certain category/type
local theImage = Weapon.Image ~= nil and Weapon.Image or "Panel.png"
 
  +
-- @returns {table} List of weapon names that count for mastery in a particular weapon slot
local weapLink = p._getLink(Weapon.Name)
 
  +
function p._getWeaponCount(slot)
if ((i - 1) % 4 == 0) then
 
  +
slot = slot and slot:lower()
result = result .. nameRow .. '\n|-'
 
  +
local data = slot and WeaponData[slot] or WeaponData
nameRow = '\n|-'
 
  +
local fullList = {}
end
 
  +
result = result .. '\n| style="width:165px" |[[File:' .. theImage ..
 
  +
for name, weapon in pairs(data) do
'|150px|link=' .. Weapon.Name .. ']]'
 
  +
if not weapon._IgnoreInMasteryCount then
nameRow = nameRow .. '\n| style="vertical-align: text-top;" |[[' ..
 
  +
-- TODO: There should be a better way to determine/differentiate if a weapon is a kitgun b/c kitguns and zaws
weapLink .. '|' .. Weapon.Name .. ']]'
 
  +
-- are stored in the same M:Weapons/data/modular data store; add a new "Kitgun" or "Zaw" Trait and target that?
end
 
  +
if (slot == 'kitgun' and weapon.Slot == 'Secondaire')
result = result .. nameRow
 
  +
or (slot == 'zaw' and weapon.Slot == 'Mêlée')
result = result .. '\n|}'
 
  +
or (slot == 'robotic' and weapon.Slot ~= 'Molosse')
return result
 
  +
or (weapon.Slot:lower() == slot)
end
 
  +
or slot == nil then
  +
fullList[#fullList + 1] = name
  +
end
  +
end
  +
end
   
  +
return #fullList, fullList
function p.getMeleeWeaponGallery(frame)
 
local Type = frame.args ~= nil and frame.args[1] or frame
 
if (Type == nil) then return "" end
 
local WeapArray = getMeleeWeapons(Type)
 
local result = "==Armes de type " .. Type .. "==\n"
 
result = result .. getWeaponGallery(WeapArray)
 
return result
 
 
end
 
end
   
  +
--- Gets the total count of weapons as used on [[Mastery Rank#Total Mastery]].
  +
-- @function p.getWeaponCount
  +
-- @param {table} frame Frame object w/ the first argument being the weapon slot
  +
-- @return {number} Total number of weapons that can reward Mastery XP
 
function p.getWeaponCount(frame)
 
function p.getWeaponCount(frame)
local Type = frame.args ~= nil and frame.args[1] or frame
+
return (p._getWeaponCount(frame.args and frame.args[1] or nil))
local getFullList = frame.args ~= nil and frame.args[2]
 
if getFullList == "nil" then -- bleh, this lovely parsing of invokes..
 
getFullList = nil
 
end
 
local getAll = frame.args ~= nil and frame.args[3]
 
local count = 0
 
local fullList = ""
 
for i, val in Shared.skpairs(WeaponData["Weapons"]) do
 
if (not Shared.contains(WeaponData["IgnoreInCount"], i) and getAll ~=
 
"true") or getAll == "true" then
 
if (Type == nil or Type == "") then
 
count = count + 1
 
if (getFullList ~= nil) then
 
fullList = fullList .. '\n# ' .. val.Name
 
end
 
elseif (Type == "Warframe") then
 
if (val.Type == "Principale" or val.Type == "Secondaire" or
 
val.Type == "Mêlée") then
 
count = count + 1
 
if (getFullList ~= nil) then
 
fullList = fullList .. '\n# ' .. val.Name
 
end
 
end
 
elseif (Type == "Archwing") then
 
if (val.Type == "Arch-Gun" or val.Type == "Arch-Melee") then
 
count = count + 1
 
if (getFullList ~= nil) then
 
fullList = fullList .. '\n# ' .. val.Name
 
end
 
end
 
elseif (Type == "Rest") then
 
if (val.Type ~= "Arch-Gun" and val.Type ~= "Arch-Melee" and
 
val.Type ~= "Principale" and val.Type ~= "Secondaire" and
 
val.Type ~= "Mêlée") then
 
count = count + 1
 
if (getFullList ~= nil) then
 
fullList = fullList .. '\n# ' .. val.Name
 
end
 
end
 
else
 
if (val.Type == Type) then
 
count = count + 1
 
if (getFullList ~= nil) then
 
fullList = fullList .. '\n# ' .. val.Name
 
end
 
end
 
end
 
end
 
end
 
if (getFullList ~= nil) then return fullList end
 
return count
 
 
end
 
end
   
  +
--- Builds wikitable of all weapons' innate polarities as seen on [[Polarity]].
local function getSecondaryCategory(weapon)
 
  +
-- @function p.getPolarityTable
local class = getValue(weapon, "Class", true)
 
  +
-- @param {table} frame Frame object
if (class == "Jet") then
 
  +
-- @returns {string} Wikitext of resultant wikitable
return "Jet"
 
  +
function p.getPolarityTable(frame)
elseif (class == "Doubles Fusils à Pompe de Poche" or class ==
 
  +
local colNames = { 'Principale', 'Secondaire', 'Mêlée', 'Arch-Fusil', 'Arch-Mêlée' }
"Fusil à Pompe de Poche") then
 
  +
local cols = {} -- Will look like: {['Primary']={},['Secondary']={},['Melee']={},['Arch-Gun']={},['Arch-Melee']={},}
return "Fusil à Pompe"
 
  +
local colOrder = {} --{cols['Primary'],cols['Secondary'],cols['Melee'],cols['Arch-Gun'],cols['Arch-Melee'],}
else
 
  +
local colCounts = {}
local trigger = getValue(weapon, "Trigger", true)
 
if (trigger == "Semi-Auto" or trigger == "Rafale") then
 
return "Semi-Auto"
 
elseif (trigger == "Auto" or trigger == "Auto à Chauffe") then
 
return "Auto"
 
end
 
end
 
return "Other"
 
end
 
   
  +
for i, v in ipairs(colNames) do
local function getPrimaryCategory(weapon)
 
  +
cols[v] = {}
local class = getValue(weapon, "Class", true)
 
  +
colOrder[i] = cols[v]
if (class == "Fusil à Pompe") then
 
  +
colCounts[v] = 0
return "Fusil à Pompe"
 
  +
end
elseif (class == "Arc") then
 
return "Arc"
 
elseif (class == "Fusil de Sniper") then
 
return "Fusil de Sniper"
 
elseif (class == "Fusil") then
 
local trigger = getValue(weapon, "Trigger", true)
 
if (trigger == "Semi-Auto" or trigger == "Rafale") then
 
return "Semi-Auto"
 
elseif (trigger == "Auto" or trigger == "Auto à Chauffe") then
 
return "Auto"
 
end
 
end
 
return "Other"
 
end
 
   
  +
for _, weapon in pairs(WeaponData) do
function p.getPolarityTable()
 
  +
local pols = Table.size(weapon["Polarities"] or {})
local tHeader = ""
 
  +
local slot = weapon['Slot']
tHeader = tHeader ..
 
  +
if pols > 0 and cols[slot] then
'\n{| style="width: 100%; border-collapse: collapse; cellpadding: 2" border="1"'
 
  +
table.insert(cols[slot], {
tHeader = tHeader .. '\n! colspan="2" |Armes Principale'
 
  +
'|'..p._getFormattedValue(weapon, 'NameLink'):gsub(' ?%(.*%)', '')..'||'..p._getFormattedValue(weapon, "Polarities"),
tHeader = tHeader .. '\n! colspan="2" |Armes Secondaire'
 
  +
pols
tHeader = tHeader .. '\n! colspan="2" |Armes Mêlée'
 
  +
})
local tRows = ""
 
  +
colCounts[slot] = colCounts[slot] + 1
  +
end
  +
end
   
  +
for i, v in ipairs(colNames) do
local Melees = p.getWeapons(function(x)
 
  +
colCounts[i] = colCounts[v]
return p.isMelee(x) and
 
  +
table.sort(cols[v], function(a, b)return a[2] > b[2] end)
Shared.tableCount(getValue(x, "Polarities", true)) > 0
 
end)
+
end
local Pistols = p.getWeapons(function(x)
 
return x.Type == "Secondaire" and
 
Shared.tableCount(getValue(x, "Polarities", true)) > 0
 
end)
 
local Primaries = p.getWeapons(function(x)
 
return (x.Type == "Principale") and
 
Shared.tableCount(getValue(x, "Polarities", true)) > 0
 
end)
 
   
local ACount = Shared.tableCount(Melees)
+
local result = {[=[
  +
{| style="width: 100%; border-collapse: collapse;" cellpadding="2" border="1"
local maxLen = ACount
 
  +
|+ '''Armes avec une Polarité innée (en ignorant la Posture et l'emplacement Exilus)'''
local BCount = Shared.tableCount(Pistols)
 
  +
! colspan="2" |Principales
if (BCount > maxLen) then maxLen = BCount end
 
  +
! colspan="2" |Secondaires
local CCount = Shared.tableCount(Primaries)
 
  +
! colspan="2" |Mêlées
if (CCount > maxLen) then maxLen = CCount end
 
  +
! colspan="2" |Arch-Fuils
 
  +
! colspan="2" |Arch-Mêlées]=]}
for i = 1, maxLen, 1 do
 
  +
for i = 1, math.max(table.unpack(colCounts)) do --row
tRows = tRows .. "\n|-"
 
  +
table.insert(result, '|-')
if (i <= CCount) then
 
  +
for _, col in ipairs(colOrder) do --cell
tRows = tRows .. "\n|[[" .. Primaries[i].Name .. "]]||" ..
 
  +
table.insert(result,(col[i] or {'| ||'})[1])
p.GetPolarityString(Primaries[i])
 
  +
end
else
 
  +
end
tRows = tRows .. "\n| ||"
 
  +
table.insert(result, '|}')
end
 
  +
return table.concat(result, '\n')
if (i <= BCount) then
 
tRows = tRows .. "\n|[[" .. Pistols[i].Name .. "]]||" ..
 
p.GetPolarityString(Pistols[i])
 
else
 
tRows = tRows .. "\n| ||"
 
end
 
if (i <= ACount) then
 
tRows = tRows .. "\n|[[" .. Melees[i].Name .. "]]||" ..
 
p.GetPolarityString(Melees[i])
 
else
 
tRows = tRows .. "\n| ||"
 
end
 
end
 
 
return tHeader .. tRows .. "\n|}"
 
 
end
 
end
   
  +
--- Builds a table that lists out all weapons with a certain damage type
local function buildCompareString(Val1, Val2, ValName, Digits, Addon, Words,
 
  +
-- @function p.buildDamageTypeTable
Start, Middle)
 
  +
-- @param {table} frame Frame object
if (Val1 == nil or Val2 == nil) then return "" end
 
  +
-- @returns {string} Wikitext of resultant wikitable
local V1Str = Val1
 
  +
function p.buildDamageTypeTable(frame)
local V2Str = Val2
 
  +
local damageType = frame.args and frame.args[1] or frame
if (Digits ~= nil) then
 
  +
local mostly = frame.args and (frame.args[2] or '') ~= ''
local didWork, rowStr = pcall(Shared.round, Val1, Digits)
 
if (didWork) then
 
V1Str = rowStr
 
local didWork, rowStr = pcall(Shared.round, Val2, Digits)
 
if (didWork) then
 
V2Str = rowStr
 
else
 
mw.log("Failed to round " .. Val2)
 
end
 
else
 
mw.log("Failed to round " .. Val1)
 
end
 
end
 
if (Addon ~= nil) then
 
V1Str = V1Str .. p.doPlural(Addon, Val1)
 
V2Str = V2Str .. p.doPlural(Addon, Val2)
 
end
 
local bigWord = Words ~= nil and Words[1] or "Plus de"
 
local smallWord = Words ~= nil and Words[2] or "Moins de"
 
local start = Start ~= nil and Start or "\n**"
 
local bigMiddle = Middle ~= nil and Middle[1] or ""
 
local smallMiddle = Middle ~= nil and Middle[2] or ""
 
   
  +
local content = {}
if (Val1 > Val2) then
 
  +
for k,weap in pairs(WeaponData) do
return start .. " " .. bigWord .. " " .. ValName .. " " .. bigMiddle ..
 
  +
local weapAtk = getWeaponAttack(weap)--could add a loop here
" (" .. V1Str .. " vs. " .. V2Str .. ")"
 
  +
local portion, biastype, damage = statRead(weapAtk, 'DamageBias')
elseif (Val2 > Val1) then
 
  +
local typeDmg = statRead(weapAtk, damageType)
return
 
  +
if damage == 0 then typeDmg = weapAtk[damageType] and 1 or 0 end--modular pieces
start .. " " .. smallWord .. " " .. ValName .. " " .. smallMiddle ..
 
  +
--Filter for
" (" .. V1Str .. " vs. " .. V2Str .. ")"
 
  +
--a. any of the damage type in any attack - former 'not mostly'
else
 
  +
--b. at least one majority damage type - former 'mostly'
return ""
 
  +
--c. a majority of the damage type in the display attack - 'mostly'
end
 
  +
--d. any of the damage type in the display attack - 'not mostly'
end
 
  +
if biastype == damageType or not mostly and typeDmg > 0 then
 
  +
table.insert(content, ('| %s || %s || %s || %s || %s || data-sort-value="%s" | %s'):format(
local function buildGunComparisonString(Weapon1, Weapon2, Conclave)
 
  +
statFormat(weapAtk, 'Name'),
local result = ""
 
  +
statRead(weapAtk, 'Slot'),
if (Conclave) then
 
  +
statRead(weapAtk, 'Class'),
result = "* " .. Weapon1.Name .. ", comparé au [[Conclave:" ..
 
  +
statRead(weapAtk, 'AttackName'),
Weapon2.Name .. "|" .. Weapon2.Name .. "]]:"
 
  +
typeDmg,
else
 
  +
portion, statFormat(weapAtk, 'DamageBias')
result = "* " .. Weapon1.Name .. ", comparé au [[" .. Weapon2.Name ..
 
  +
))
"]]:"
 
end
+
end
  +
end
 
  +
table.sort(content)--will sort by tooltip span key
if (hasAttack(Weapon1, "Normal") and hasAttack(Weapon2, "Normal")) then
 
local Att1 = getAttack(Weapon1, "Normal")
 
local Att2 = getAttack(Weapon2, "Normal")
 
local dmgString = ""
 
dmgString = dmgString ..
 
buildCompareString(GetDamage(Att1), GetDamage(Att2),
 
"dégâts de base", {2, 1})
 
dmgString = dmgString ..
 
buildCompareString(Att1.Damage["Impact"],
 
Att2.Damage["Impact"],
 
" dégâts " ..
 
Icon._Proc("Impact", "text"),
 
{2, 1}, nil, {"Plus de", "Moins de"},
 
"\n***")
 
dmgString = dmgString ..
 
buildCompareString(Att1.Damage["Perforation"],
 
Att2.Damage["Perforation"],
 
" dégâts " ..
 
Icon._Proc("Perforation", "text"),
 
{2, 1}, nil, {"Plus de", "Moins de"},
 
"\n***")
 
dmgString = dmgString ..
 
buildCompareString(Att1.Damage["Tranchant"],
 
Att2.Damage["Tranchant"],
 
" dégâts " ..
 
Icon._Proc("Tranchant", "text"),
 
{2, 1}, nil, {"Plus de", "Moins de"},
 
"\n***")
 
if (string.len(dmgString) > 0 and GetDamage(Att1) == GetDamage(Att2)) then
 
dmgString =
 
"\n**Dégâts de base équivalent, mais composition différente:" ..
 
dmgString
 
end
 
result = result .. dmgString
 
end
 
if (hasAttack(Weapon1, "Charge") and hasAttack(Weapon2, "Charge")) then
 
local Att1 = getAttack(Weapon1, "Charge")
 
local Att2 = getAttack(Weapon2, "Charge")
 
-- local addedString = ""
 
-- if(dontHasAttack(Weapon1, "Normal")and dontHasAttack(Weapon2, "Normal")) then
 
-- addedString = "charged test"
 
-- end
 
if (Att1.CritChance ~= nil and Att2.CritChance ~= nil) then
 
if (dontHasAttack(Weapon1, "Normal") and
 
dontHasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"[[Chance de Coup Critique]]",
 
2, "%")
 
end
 
if (hasAttack(Weapon1, "Normal") and hasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"[[Chance de Coup Critique]] avec un tir chargé",
 
2, "%")
 
end
 
end
 
if (dontHasAttack(Weapon1, "Normal") and
 
dontHasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString(Att1.CritMultiplier,
 
Att2.CritMultiplier,
 
"[[Multiplicateur de Critique]]", 2,
 
"x")
 
end
 
if (hasAttack(Weapon1, "Normal") and hasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString(Att1.CritMultiplier,
 
Att2.CritMultiplier,
 
"[[Multiplicateur de Critique]] avec un tir chargé",
 
2, "x")
 
end
 
if (Att1.StatusChance ~= nil and Att2.StatusChance ~= nil) then
 
if (dontHasAttack(Weapon1, "Normal") and
 
dontHasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"[[Chance de Statut]]", 2, "%")
 
end
 
if (hasAttack(Weapon1, "Normal") and hasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"[[Chance de Statut]] avec un tir chargé",
 
2, "%")
 
end
 
end
 
if (dontHasAttack(Weapon1, "Normal") and
 
dontHasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString(GetDamage(Weapon1.ChargeAttack),
 
GetDamage(Weapon2.ChargeAttack),
 
"dégâts", {2, 1})
 
end
 
if (hasAttack(Weapon1, "Normal") and hasAttack(Weapon2, "Normal")) then
 
result = result ..
 
buildCompareString(GetDamage(Weapon1.ChargeAttack),
 
GetDamage(Weapon2.ChargeAttack),
 
"dégâts avec un tir chargé",
 
{2, 1})
 
end
 
result = result ..
 
buildCompareString(getValue(Weapon1,
 
{"Charge", "ChargeTime"}),
 
getValue(Weapon2,
 
{"Charge", "ChargeTime"}),
 
"Temps de charge", {2, 1}, " s",
 
{"", ""}, nil,
 
{"plus lent", "plus rapide"})
 
end
 
if (hasAttack(Weapon1, "Area") and hasAttack(Weapon2, "Area")) then
 
result = result ..
 
buildCompareString(GetDamage(Weapon1.AreaAttack),
 
GetDamage(Weapon2.AreaAttack),
 
"dégâts de zone", {2, 1})
 
end
 
if (hasAttack(Weapon1, "Secondary") and hasAttack(Weapon2, "Secondary")) then
 
result = result ..
 
buildCompareString(GetDamage(Weapon1.SecondaryAttack),
 
GetDamage(Weapon2.SecondaryAttack),
 
"secondary attack damage", {2, 1})
 
-- test code to fix stradavar wrong comparison
 
local Att1 = getAttack(Weapon1, "Secondary")
 
local Att2 = getAttack(Weapon2, "Secondary")
 
if (Att1.CritChance ~= nil and Att2.CritChance ~= nil) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"[[Chance de Coup Critique]] sur le [[Tir Alternatif]]",
 
2, "%")
 
end
 
result = result ..
 
buildCompareString(Att1.CritMultiplier,
 
Att2.CritMultiplier,
 
"[[Multiplicateur de Critique]] sur le [[Tir Alternatif]]",
 
2, "x")
 
if (Att1.StatusChance ~= nil and Att2.StatusChance ~= nil) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"[[Chance de Statut]] sur le [[Tir Alternatif]]",
 
2, "%")
 
end
 
result = result .. buildCompareString(Att1.FireRate, Att2.FireRate,
 
"[[Cadence de Tir]] sur le [[Tir Alternatif]]",
 
2, " balle<s>/s")
 
-- end of test code
 
end
 
 
-- test code to fix tiberon prime comparison
 
 
if ((hasAttack(Weapon1, "Normal") and hasAttack(Weapon2, "Secondary") and
 
dontHasAttack(Weapon1, "Secondary")) or
 
(hasAttack(Weapon1, "Secondary") and hasAttack(Weapon2, "Normal") and
 
dontHasAttack(Weapon2, "Secondary"))) then
 
local Att1 = getAttack(Weapon1, "Normal")
 
local Att2 = getAttack(Weapon2, "Normal")
 
if (Att1.CritChance ~= nil and Att2.CritChance ~= nil) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"[[Chance de Coup Critique]]", 2,
 
"%")
 
end
 
result = result ..
 
buildCompareString(Att1.CritMultiplier,
 
Att2.CritMultiplier,
 
"[[Multiplicateur de Critique]]", 2, "x")
 
 
if (Att1.StatusChance ~= nil and Att2.StatusChance ~= nil) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"[[Chance de Statut]]", 2, "%")
 
end
 
end
 
 
-- end of test code to fix tiberon prime comparison
 
 
if (hasAttack(Weapon1, "Normal") and hasAttack(Weapon2, "Normal")) then
 
local Att1 = getAttack(Weapon1, "Normal")
 
local Att2 = getAttack(Weapon2, "Normal")
 
if (Att1.CritChance ~= nil and Att2.CritChance ~= nil) then
 
if (dontHasAttack(Weapon1, "Charge") and
 
dontHasAttack(Weapon2, "Charge") and
 
dontHasAttack(Weapon1, "Secondary") and
 
dontHasAttack(Weapon2, "Secondary")) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"[[Chance de Coup Critique]]",
 
2, "%")
 
end
 
if (hasAttack(Weapon1, "Charge") and hasAttack(Weapon2, "Charge")) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"base [[Chance de Coup Critique]]",
 
2, "%")
 
end
 
if (hasAttack(Weapon1, "Secondary") and
 
hasAttack(Weapon2, "Secondary")) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"[[Chance de Coup Critique]] sur le tir principal",
 
2, "%")
 
end
 
end
 
if (dontHasAttack(Weapon1, "Charge") and
 
dontHasAttack(Weapon2, "Charge") and
 
dontHasAttack(Weapon1, "Secondary") and
 
dontHasAttack(Weapon2, "Secondary")) then
 
result = result ..
 
buildCompareString(Att1.CritMultiplier,
 
Att2.CritMultiplier,
 
"[[Multiplicateur de Critique]]", 2,
 
"x")
 
end
 
if (hasAttack(Weapon1, "Charge") and hasAttack(Weapon2, "Charge")) then
 
result = result ..
 
buildCompareString(Att1.CritMultiplier,
 
Att2.CritMultiplier,
 
"base [[Multiplicateur de Critique]]",
 
2, "x")
 
end
 
if (hasAttack(Weapon1, "Secondary") and hasAttack(Weapon2, "Secondary")) then
 
result = result ..
 
buildCompareString(Att1.CritMultiplier,
 
Att2.CritMultiplier,
 
"[[Multiplicateur de Critique]] sur le tir principal",
 
2, "x")
 
end
 
if (Att1.StatusChance ~= nil and Att2.StatusChance ~= nil) then
 
if (dontHasAttack(Weapon1, "Charge") and
 
dontHasAttack(Weapon2, "Charge") and
 
dontHasAttack(Weapon1, "Secondary") and
 
dontHasAttack(Weapon2, "Secondary")) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"[[Chance de Statut]]", 2, "%")
 
end
 
if (hasAttack(Weapon1, "Charge") and hasAttack(Weapon2, "Charge")) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"base [[Chance de Statut]]", 2,
 
"%")
 
end
 
if (hasAttack(Weapon1, "Secondary") and
 
hasAttack(Weapon2, "Secondary")) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"[[Chance de Statut]] sur le tir principal",
 
2, "%")
 
end
 
end
 
result = result ..
 
buildCompareString(Att1.FireRate, Att2.FireRate,
 
"[[Cadence de Tir]]", 2, " balle<s>/s")
 
 
-- Handling Damage Falloff
 
if (Att1.Falloff ~= nil and Att2.Falloff == nil) then
 
result = result .. "\n** " .. Att1.Falloff.StartRange .. "m - " ..
 
Att1.Falloff.EndRange .. "m dégradation dégâts"
 
if (Att1.Falloff.Reduction ~= nil) then
 
result = result .. " avec jusqu'à " ..
 
(Att1.Falloff.Reduction * 100) ..
 
"% réduction dégâts"
 
end
 
elseif (Att2.Falloff ~= nil and Att1.Falloff == nil) then
 
result =
 
result .. "\n** No " .. Att2.Falloff.StartRange .. "m - " ..
 
Att2.Falloff.EndRange .. "m damage falloff"
 
if (Att2.Falloff.Reduction ~= nil) then
 
result = result .. " with up to " ..
 
(Att2.Falloff.Reduction * 100) ..
 
"% réduction dégâts"
 
end
 
elseif (Att1.Falloff ~= nil and Att2.Falloff ~= nil) then
 
result = result ..
 
buildCompareString(Att1.Falloff.StartRange,
 
Att2.Falloff.StartRange,
 
"range before damage falloff starts",
 
2, "m", {"Longer", "Shorter"})
 
result = result ..
 
buildCompareString(Att1.Falloff.EndRange,
 
Att2.Falloff.EndRange,
 
"range before damage falloff ends",
 
2, "m", {"Longer", "Shorter"})
 
if (Att1.Falloff.Reduction ~= nil and Att2.Falloff.Reduction ~= nil) then
 
result = result ..
 
buildCompareString(Att1.Falloff.Reduction * 100,
 
Att2.Falloff.Reduction * 100,
 
"max damage reduction due to falloff",
 
2, "%",
 
{"Plus grand", "Plus petit"})
 
end
 
end
 
end
 
 
result = result ..
 
buildCompareString(Weapon1.Magazine, Weapon2.Magazine,
 
"chargeur", 0, " balle<s>",
 
{"Plus gros", "Plus petit"})
 
result = result ..
 
buildCompareString(Weapon1.MaxAmmo, Weapon2.MaxAmmo,
 
"Réserve de munition", 0, " balle<s>",
 
{"", ""}, nil,
 
{"plus grande", "plus petite"})
 
-- If this is a weapon that regenerates ammo, flip the comparison
 
if (Weapon1.ReloadStyle ~= nil and Weapon1.ReloadStyle == "Regenerate") then
 
result = result ..
 
buildCompareString(Weapon1.Reload, Weapon2.Reload,
 
"[[Rechargement]]", 2, " balle<s>/s",
 
{"", ""}, nil,
 
{"plus lent", "plus rapide"})
 
else
 
result = result ..
 
buildCompareString(Weapon1.Reload, Weapon2.Reload,
 
"[[Rechargement]]", 2, " s", {"", ""},
 
nil, {"plus lent", "plus rapide"})
 
end
 
result = result ..
 
buildCompareString(Weapon1.Spool, Weapon2.Spool,
 
"temps de chauffe", 0, " balle<s>",
 
{"", ""}, nil, {"plus lent", "plus rapide"})
 
local Acc1 = getValue(Weapon1, "Accuracy")
 
local Acc2 = getValue(Weapon2, "Accuracy")
 
if (type(Acc1) == "number" and type(Acc2) == "number") then
 
result = result ..
 
buildCompareString(Acc1, Acc2, "[[Précision]]", 0, nil,
 
{"Meilleure", "Moins bonne"})
 
end
 
 
-- Handling Syndicate radial effects
 
if (Weapon1.SyndicateEffect ~= nil and Weapon2.SyndicateEffect == nil) then
 
result = result .. "\n** Innate [[" .. Weapon1.SyndicateEffect ..
 
"]] effect"
 
elseif (Weapon2.SyndicateEffect ~= nil and Weapon1.SyndicateEffect == nil) then
 
result = result .. "\n** Lack of an innate [[" ..
 
Weapon2.SyndicateEffect .. "]] effect"
 
elseif (Weapon1.SyndicateEffect ~= nil and Weapon2.SyndicateEffect ~= nil and
 
Weapon1.SyndicateEffect ~= Weapon2.SyndicateEffect2) then
 
result = result ..
 
"\n** Different innate [[Syndicate Radial Effects|Syndicate Effect]]: [[" ..
 
Weapon1.SyndicateEffect .. "]] vs. [[" ..
 
Weapon2.SyndicateEffect .. "]]"
 
end
 
 
-- Handling Polarities
 
local Pol1 = Weapon1.Polarities ~= nil and Weapon1.Polarities or {}
 
local Pol2 = Weapon2.Polarities ~= nil and Weapon2.Polarities or {}
 
local count1 = Shared.tableCount(Pol1)
 
local count2 = Shared.tableCount(Pol2)
 
local isDifferent = count1 ~= count2
 
if (not isDifferent and count1 > 0) then
 
table.sort(Pol1, function(x, y) return x < y end)
 
table.sort(Pol2, function(x, y) return x < y end)
 
for i, pol in pairs(Pol1) do
 
if (pol ~= Pol2[i]) then isDifferent = true end
 
end
 
end
 
 
if (isDifferent) then
 
result = result .. "\n** Différentes polarités (" ..
 
p.GetPolarityString(Weapon1) .. " vs. " ..
 
p.GetPolarityString(Weapon2) .. ")"
 
end
 
   
  +
return ([[
result = result .. buildCompareString(Weapon1.Mastery, Weapon2.Mastery,
 
  +
{| class = "listtable sortable" style="margin:auto;"
"[[Rang de Maîtrise]] requis", 0,
 
  +
|+ '''Armes avec des dégâts %s%s'''
nil, {"", ""}, nil,
 
  +
|-
{"supérieur", "inférieur"})
 
  +
! Nom !! Emplacement !! Classe !! Nom de l'attaque !! data-sort-type="number" | %s !! data-sort-type="number" | Majorité
result = result ..
 
  +
|-
buildCompareString(Weapon1.Disposition, Weapon2.Disposition,
 
  +
]]):format(mostly and 'mostly ' or '', damageType, Tooltip.full(damageType, 'DamageTypes'))
"[[Mod_Riven#Disposition|disposition]]", 2)
 
  +
..table.concat(content, '\n|-\n')..'\n|}'
return result
 
 
end
 
end
   
  +
--- _isVariant adapter for p._shortLinkList
local function buildMeleeComparisonString(Weapon1, Weapon2, Conclave)
 
local result = ""
+
local function variantOf(weap)
  +
local full, _, var, base = weap.Name, p._isVariant(weap.Name)
if (Conclave) then
 
  +
return var, base, full
result = "* " .. Weapon1.Name .. ", comparé au [[Conclave:" ..
 
Weapon2.Name .. "|" .. Weapon2.Name .. "]]:"
 
else
 
result = "* " .. Weapon1.Name .. ", comparé au [[" .. Weapon2.Name ..
 
"]]:"
 
end
 
 
local dmgString = ""
 
local Att1 = getAttack(Weapon1, "Normal")
 
local Att2 = getAttack(Weapon2, "Normal")
 
dmgString = dmgString ..
 
buildCompareString(GetDamage(Att1), GetDamage(Att2),
 
"dégâts de base", {2, 1})
 
dmgString = dmgString ..
 
buildCompareString(Att1.Damage["Impact"],
 
Att2.Damage["Impact"], " dégâts " ..
 
Icon._Proc("Impact", "text"), {2, 1},
 
nil, {"Plus de", "Moins de"}, "\n***")
 
dmgString = dmgString ..
 
buildCompareString(Att1.Damage["Perforation"],
 
Att2.Damage["Perforation"],
 
" dégâts " ..
 
Icon._Proc("Perforation", "text"),
 
{2, 1}, nil, {"Plus de", "Moins de"},
 
"\n***")
 
dmgString = dmgString ..
 
buildCompareString(Att1.Damage["Tranchant"],
 
Att2.Damage["Tranchant"],
 
" dégâts " ..
 
Icon._Proc("Tranchant", "text"),
 
{2, 1}, nil, {"Plus de", "Moins de"},
 
"\n***")
 
if (string.len(dmgString) > 0 and GetDamage(Att1) == GetDamage(Att2)) then
 
dmgString =
 
"\n**Dégâts de base équivalent, mais composition différente:" ..
 
dmgString
 
end
 
result = result .. dmgString
 
 
if (Att1.CritChance ~= nil and Att2.CritChance ~= nil) then
 
result = result ..
 
buildCompareString((Att1.CritChance * 100),
 
(Att2.CritChance * 100),
 
"[[Chance de Coup Critique]]", 2, "%")
 
end
 
result = result ..
 
buildCompareString(Att1.CritMultiplier, Att2.CritMultiplier,
 
"[[Multiplicateur de Critique]]", {2, 1},
 
"x")
 
 
if (Att1.StatusChance ~= nil and Att2.StatusChance ~= nil) then
 
result = result ..
 
buildCompareString((Att1.StatusChance * 100),
 
(Att2.StatusChance * 100),
 
"[[Chance de Statut]]", 2, "%")
 
end
 
result = result ..
 
buildCompareString(Att1.FireRate, Att2.FireRate,
 
"[[Vitesse d'Attaque]]", 2)
 
result = result .. buildCompareString(Icon._Pol(Weapon1.StancePolarity),
 
Icon._Pol(Weapon2.StancePolarity),
 
"polarité de [[Posture]]", nil, nil,
 
{"Différente", "Différente"})
 
result = result ..
 
buildCompareString(getValue(Weapon1, "ChannelMult", true),
 
getValue(Weapon2, "ChannelMult", true),
 
"Multiplicateur de Canalisation", 1, "x")
 
 
-- Handling Polarities
 
local Pol1 = Weapon1.Polarities ~= nil and Weapon1.Polarities or {}
 
local Pol2 = Weapon2.Polarities ~= nil and Weapon2.Polarities or {}
 
local count1 = Shared.tableCount(Pol1)
 
local count2 = Shared.tableCount(Pol2)
 
local isDifferent = count1 ~= count2
 
if (not isDifferent and count1 > 0) then
 
table.sort(Pol1, function(x, y) return x < y end)
 
table.sort(Pol2, function(x, y) return x < y end)
 
for i, pol in pairs(Pol1) do
 
if (pol ~= Pol2[i]) then isDifferent = true end
 
end
 
end
 
 
if (isDifferent) then
 
result = result .. "\n** Polarités différentes (" ..
 
p.GetPolarityString(Weapon1) .. " vs. " ..
 
p.GetPolarityString(Weapon2) .. ")"
 
end
 
 
result = result .. buildCompareString(Weapon1.Mastery, Weapon2.Mastery,
 
"[[Rang de Maîtrise]] requis", 0)
 
return result
 
 
end
 
end
   
  +
--- Builds a list of weapons, with variants being next to base weapon name inside parentheses
function p.buildComparison(frame)
 
  +
-- (e.g. {{Weapon|Braton}} ({{Weapon|MK1-Braton|MK1}}, {{Weapon|Braton Prime|Prime}})).
local WeapName1 = frame.args[1]
 
  +
-- @function p._shortLinkList
local WeapName2 = frame.args[2]
 
  +
-- @param {table} Weapon Weapon table
 
  +
-- @param {boolean} tooltip If true, adds weapon tooltips, false otherwise; defaults to false
if (WeapName1 == nil or WeapName2 == nil) then
 
  +
-- @returns {string} Wikitext of resultant list
return '<span style="color:red">ERROR: Must compare two weapons</span>'
 
  +
function p._shortLinkList(Weapons, tooltip)
end
 
  +
return StatObject.shortLinkList(Weapons, variantOf, tooltip and 'Weapons')
 
local Weapon1 = p.getWeapon(WeapName1)
 
if (Weapon1 == nil) then
 
return '<span style="color:red">ERROR: Could not find ' .. WeapName1 ..
 
'</span>'
 
end
 
local Weapon2 = p.getWeapon(WeapName2)
 
if (Weapon2 == nil) then
 
return '<span style="color:red">ERROR: Could not find ' .. WeapName2 ..
 
'</span>'
 
end
 
 
if (p.isMelee(Weapon1)) then
 
return buildMeleeComparisonString(Weapon1, Weapon2) -- .."[[Category:Automatic Comparison]]"
 
else
 
return buildGunComparisonString(Weapon1, Weapon2) -- .."[[Category:Automatic Comparison]]"
 
end
 
 
end
 
end
   
  +
--- Builds a list of weapons' mastery requirements as seen on [[Template:EquipmentUnlock]],
function p.buildConclaveComparison(frame)
 
  +
-- [[Template:EquipmentUnlock/Primary]], [[Template:EquipmentUnlock/Secondary]],
local WeapName1 = frame.args[1]
 
  +
-- [[Template:EquipmentUnlock/Melee]], etc.
local WeapName2 = frame.args[2]
 
  +
-- @function p.getMasteryShortList
 
  +
-- @param {table} frame Frame object w/ first argument being a string weaponSlot
if (WeapName1 == nil or WeapName2 == nil) then
 
  +
-- @returns {string} Wikitext of resultant list
return '<span style="color:red">ERROR: Must compare two weapons</span>'
 
  +
function p.getMasteryShortList(frame)
end
 
  +
local weaponSlot = frame.args[1]
 
  +
local masteryRank = tonumber(frame.args[2])
local Weapon1 = p.getConclaveWeapon(WeapName1)
 
  +
local weapArray = p._getWeapons(function(x)
if (Weapon1 == nil) then
 
  +
return x.Slot == weaponSlot and x.Mastery == masteryRank
return '<span style="color:red">ERROR: Could not find ' .. WeapName1 ..
 
  +
end)
'</span>'
 
  +
return table.concat(StatObject.shortLinkList(weapArray, variantOf, 'Weapons'), ' • ')
end
 
local Weapon2 = p.getConclaveWeapon(WeapName2)
 
if (Weapon2 == nil) then
 
return '<span style="color:red">ERROR: Could not find ' .. WeapName2 ..
 
'</span>'
 
end
 
 
if (p.isMelee(Weapon1)) then
 
return buildMeleeComparisonString(Weapon1, Weapon2, true) -- .."[[Category:Automatic Comparison]]"
 
else
 
return buildGunComparisonString(Weapon1, Weapon2, true) -- .."[[Category:Automatic Comparison]]"
 
end
 
 
end
 
end
   
function p.buildFamilyComparison(frame)
+
function p.fullList()
  +
return table.concat(StatObject.shortLinkList(WeaponData, variantOf, 'Weapons'), ' • ')
local WeapName = frame.args ~= nil and frame.args[1] or frame
 
if (WeapName == nil) then
 
return
 
'<span style="color:red">ERROR: Must provide a Weapon name</span>'
 
end
 
 
local Weapon = p.getWeapon(WeapName)
 
if (Weapon == nil) then
 
return '<span style="color:red">ERROR: Could not find ' .. WeapName ..
 
'</span>'
 
end
 
if (Weapon.Family == nil) then
 
return '<span style="color:red">ERROR: ' .. WeapName ..
 
' doesn\'t have a family</span>'
 
end
 
 
local relatives = getFamily(Weapon.Family)
 
local result = {}
 
for i, NewWeapon in pairs(relatives) do
 
if (WeapName ~= NewWeapon.Name) then
 
if (p.isMelee(NewWeapon)) then
 
table.insert(result,
 
buildMeleeComparisonString(Weapon, NewWeapon))
 
else
 
table.insert(result, buildGunComparisonString(Weapon, NewWeapon))
 
end
 
end
 
end
 
return table.concat(result, "\n")
 
 
end
 
end
   
  +
--- Builds a list of PvP weapons as seen on [[PvP#Limitations]].
function p.buildAutoboxCategories(frame)
 
  +
-- @function p.getConclaveList
local WeapName = frame.args ~= nil and frame.args[1] or frame
 
  +
-- @param {table} frame Frame object w/ first argument being a string weaponSlot
local Weapon = p.getWeapon(WeapName)
 
  +
-- @returns {string} Wikitext of resultant list
local result = "[[Category:Armes]]"
 
  +
function p.getConclaveList(frame)
if (Weapon == nil) then
 
  +
local weaponSlot = frame.args[1] or 'All'
return ""
 
  +
local weapArray = p._getWeapons(function(weap)
elseif (Weapon.IgnoreCategories ~= nil and Weapon.IgnoreCategories) then
 
  +
return weap.Conclave == true
return ""
 
  +
end, weaponSlot, {pvp=true})
end
 
  +
return '*'..table.concat(StatObject.shortLinkList(weapArray, variantOf), '\n* ')
if (Weapon.Type ~= nil) then
 
if (Weapon.Type == "Arch-Mêlée") then
 
result = result .. "[[Category:Arch-Mêlée]]"
 
elseif (Weapon.Type == "Arch-Fusil" or Weapon.Type ==
 
"Arch-Fusil (Atmosphère)") then
 
result = result .. "[[Category:Arch-Fusil]]"
 
elseif (Weapon.Type == "Arch-Fusil (Atmosphere)") then
 
result = result
 
elseif (Weapon.Type == "Mêlée") then
 
result = result .. "[[Category:Arme de Mêlée]]"
 
elseif (Weapon.Type == "Amplificateur") then
 
result = result
 
else
 
result = result .. "[[Category: Arme " .. Weapon.Type .. "]]"
 
end
 
end
 
if (Weapon.Class ~= nil) then
 
result = result .. "[[Category:" .. Weapon.Class .. "]]"
 
end
 
 
local augments = Mod.getWeaponAugments(Weapon)
 
if (Shared.tableCount(augments) > 0) then
 
result = result .. "[[Category:Arme d'Augment]]"
 
end
 
 
if (HasTrait(Weapon, "Prime")) then
 
result = result .. "[[Category:Prime]]"
 
if (HasTrait(Weapon, "Never Vaulted")) then
 
result = result .. "[[Category:Never Vaulted]]"
 
elseif (HasTrait(Weapon, "Vaulted")) then
 
result = result .. "[[Category:Vaulted]]"
 
end
 
end
 
 
if (HasTrait(Weapon, "Grineer")) then
 
result = result .. "[[Category:Armes Grineer]]"
 
elseif (HasTrait(Weapon, "Corpus")) then
 
result = result .. "[[Category:Armes Corpus]]"
 
elseif (HasTrait(Weapon, "Infesté")) then
 
result = result .. "[[Category:Armes Infesté]]"
 
elseif (HasTrait(Weapon, "Tenno")) then
 
result = result .. "[[Category:Armes Tenno]]"
 
end
 
 
local attack = nil
 
if (hasAttack(Weapon, "Normal")) then
 
attack = getAttack(Weapon, "Normal")
 
elseif (hasAttack(Weapon, "Charge")) then
 
attack = getAttack(Weapon, "Charge")
 
end
 
if (attack ~= nil) then
 
local bestPercent, bestElement = GetDamageBias(attack)
 
if (bestElement == "Impact" or bestElement == "Perforation" or
 
bestElement == "Tranchant") then
 
if (bestPercent > .38) then
 
result = result .. "[[Category:Arme de Type " .. bestElement ..
 
"]]"
 
else
 
result = result .. "[[Category:Arme de Type Equilibré]]"
 
end
 
end
 
 
for key, value in Shared.skpairs(attack.Damage) do
 
if (key ~= "Impact" and key ~= "Perforation" and key ~= "Tranchant") then
 
result = result .. "[[Category:Arme de Type " .. key .. "]]"
 
end
 
end
 
end
 
 
if (hasAttack(Weapon, "Secondaire")) then
 
result = result .. "[[Category:Weapons with Alt Fire]]"
 
end
 
 
return result
 
end
 
 
function p.buildDamageTypeTable(frame)
 
local DamageType = frame.args ~= nil and frame.args[1] or frame
 
local Weapons = {}
 
local WeapArray = p.getWeapons(function(x)
 
local Dmg, Element
 
if (hasAttack(x, "Normal")) then
 
Dmg, Element = GetDamageBias(getAttack(x, "Normal"), true)
 
elseif (hasAttack(x, "Charge")) then
 
Dmg, Element = GetDamageBias(getAttack(x, "Charge"), true)
 
else
 
return false
 
end
 
return Element == DamageType
 
end)
 
 
local procString = Icon._Proc(DamageType, 'text')
 
local procShort = Icon._Proc(DamageType)
 
local result = ""
 
local tHeader = '{|class = "listtable sortable" style="margin:auto;"'
 
tHeader = tHeader .. '\n|-'
 
tHeader = tHeader .. '\n!Name'
 
tHeader = tHeader .. '\n!Type'
 
tHeader = tHeader .. '\n!Class'
 
tHeader = tHeader .. '\n!' .. procString
 
tHeader = tHeader .. '\n!' .. procShort .. '%'
 
local tRows = ""
 
for i, Weapon in pairs(WeapArray) do
 
local thisRow = '\n|-\n|'
 
thisRow =
 
thisRow .. "|" .. Tooltip._tooltipText(Weapon.Name, 'Weapon') ..
 
"||" .. Weapon.Type .. "|| " ..
 
getValue(Weapon, "Class", true, true)
 
if (hasAttack(Weapon, "Normal")) then
 
local tempBias = getValue(Weapon, {"Normal", "DamageBias"}, true)
 
local tempBiasStripped = string.match(tempBias, "(%d*)%%")
 
thisRow = thisRow .. "||" ..
 
getValue(Weapon, {"Normal", DamageType}) .. "||" ..
 
"data-sort-value=" .. tempBiasStripped .. "|" ..
 
tempBias
 
elseif (hasAttack(Weapon, "Charge")) then
 
local tempBias = getValue(Weapon, {"Charge", "DamageBias"}, true)
 
local tempBiasStripped = string.match(tempBias, "(%d*)%%")
 
thisRow = thisRow .. "||" ..
 
getValue(Weapon, {"Charge", DamageType}) .. "||" ..
 
"data-sort-value=" .. tempBiasStripped .. "|" ..
 
tempBias
 
end
 
 
tRows = tRows .. thisRow
 
end
 
result = tHeader .. tRows .. "\n|}"
 
return result
 
end
 
 
function p.buildWeaponByMasteryRank(frame)
 
local MasteryRank
 
local MRTable = {}
 
for i, Weapon in Shared.skpairs(WeaponData["Weapons"]) do
 
if (Weapon.Mastery ~= nil) then
 
if (MRTable[Weapon.Mastery] == nil) then
 
MRTable[Weapon.Mastery] = {}
 
end
 
if (MRTable[Weapon.Mastery][Weapon.Type] == nil) then
 
MRTable[Weapon.Mastery][Weapon.Type] = {}
 
end
 
table.insert(MRTable[Weapon.Mastery][Weapon.Type], Weapon)
 
end
 
end
 
local result = ""
 
for i, TypeTable in Shared.skpairs(MRTable) do
 
local thisTable = "\n==Mastery Rank " .. i .. " Required=="
 
if (i == 0) then thisTable = "==No Mastery Rank Required==" end
 
thisTable = thisTable .. '\n{| style="width:80%;margin:auto"'
 
thisTable = thisTable ..
 
'\n|-\n| style="vertical-align:top;width:33%;" |'
 
if (TypeTable["Primary"] ~= nil and
 
Shared.tableCount(TypeTable["Primary"]) > 0) then
 
thisTable = thisTable .. "\n===Primary==="
 
local tempList = p.shortLinkList(TypeTable["Primary"])
 
for i, text in pairs(tempList) do
 
thisTable = thisTable .. "\n* " .. text
 
end
 
end
 
 
thisTable = thisTable .. '\n| style="vertical-align:top;width:33%;" |'
 
if (TypeTable["Secondary"] ~= nil and
 
Shared.tableCount(TypeTable["Secondary"]) > 0) then
 
thisTable = thisTable .. "\n===Secondary==="
 
local tempList = p.shortLinkList(TypeTable["Secondary"])
 
for i, text in pairs(tempList) do
 
thisTable = thisTable .. "\n* " .. text
 
end
 
end
 
 
thisTable = thisTable .. '\n| style="vertical-align:top;width:33%;" |'
 
if (TypeTable["Mêlée"] ~= nil and
 
Shared.tableCount(TypeTable["Mêlée"]) > 0) then
 
thisTable = thisTable .. "\n===Melee==="
 
local tempList = p.shortLinkList(TypeTable["Mêlée"])
 
for i, text in pairs(tempList) do
 
thisTable = thisTable .. "\n* " .. text
 
end
 
end
 
thisTable = thisTable .. "\n|}"
 
result = result .. thisTable
 
end
 
return result
 
end
 
 
function p.getMasteryShortList(frame)
 
local WeaponType = frame.args[1]
 
local MasteryRank = tonumber(frame.args[2])
 
 
local weapArray = p.getWeapons(function(x)
 
if (x.Type ~= nil and x.Mastery ~= nil) then
 
return x.Type == WeaponType and x.Mastery == MasteryRank
 
else
 
return false
 
end
 
end)
 
 
local result = {}
 
local shortList = p.shortLinkList(weapArray)
 
for i, pair in Shared.skpairs(shortList) do table.insert(result, pair) end
 
return table.concat(result, " • ")
 
end
 
 
function p.getRivenDispositionList(frame)
 
local WeaponType = frame.args[1]
 
local Disposition = tonumber(frame.args[2])
 
 
local weapArray = p.getWeapons(function(x)
 
if (x.Type ~= nil and x.Disposition ~= nil) then
 
return
 
(string.upper(WeaponType) == "ALL" or x.Type == WeaponType) and
 
x.Disposition == Disposition
 
else
 
return false
 
end
 
end)
 
 
local result = ""
 
local shortList = p.shortLinkList(weapArray)
 
for i, pair in Shared.skpairs(shortList) do
 
result = result .. '\n* ' .. pair
 
end
 
return result
 
 
end
 
end
   
  +
--- Builds a disposition wikitable as seen on [[Riven Mods/Weapon Dispos]].
  +
-- @function p.getRivenDispositionTable
  +
-- @param {table} frame Frame object w/ first argument being a string weaponSlot
  +
-- @returns {string} Wikitext of resultant wikitable
 
function p.getRivenDispositionTable(frame)
 
function p.getRivenDispositionTable(frame)
local WeaponType = frame.args[1]
+
local weaponSlot = frame.args[1]
  +
local result = {
  +
'{| class="article-table" border="0" cellpadding="1" cellspacing="1" style="width: 100%"',
  +
'|-',
  +
{'[[a| '}, -- Wikitable header row
  +
'|-'
  +
}
   
  +
-- local ranges = {'○○○○○', '●○○○○', '●●○○○', '●●●○○', '●●●●○', '●●●●●'}
local result =
 
  +
local dispo = {}
'{| class="article-table" border="0" cellpadding="1" cellspacing="1" style="width: 100%"'
 
result = result .. '\n|-'
 
for i = 5, 1, -1 do
 
local j = nil
 
if (i == 5) then
 
j = 1.5
 
elseif (i == 4) then
 
j = 1.3
 
elseif (i == 3) then
 
j = 1.1
 
elseif (i == 2) then
 
j = 0.89
 
else
 
j = 0.69
 
end
 
result = result .. '\n! scope="col" style="text-align:center;"|' ..
 
Icon._Dis(j)
 
end
 
result = result .. '\n|-'
 
for i = 5, 1, -1 do
 
result = result .. '\n| style="vertical-align:top" |'
 
if (i == 5) then
 
for j = 1550, 1301, -1 do
 
result = result ..
 
p.getRivenDispositionList(
 
{args = {WeaponType, j / 1000}})
 
end
 
elseif (i == 4) then
 
for j = 1300, 1101, -1 do
 
result = result ..
 
p.getRivenDispositionList(
 
{args = {WeaponType, j / 1000}})
 
end
 
elseif (i == 3) then
 
for j = 1100, 900, -1 do
 
result = result ..
 
p.getRivenDispositionList(
 
{args = {WeaponType, j / 1000}})
 
end
 
elseif (i == 2) then
 
for j = 899, 700, -1 do
 
result = result ..
 
p.getRivenDispositionList(
 
{args = {WeaponType, j / 1000}})
 
end
 
else
 
for j = 699, 500, -1 do
 
result = result ..
 
p.getRivenDispositionList(
 
{args = {WeaponType, j / 1000}})
 
end
 
end
 
end
 
result = result .. '\n|}'
 
   
  +
for k, weapon in pairs(WeaponData) do
return result
 
  +
if weapon['Disposition'] and (weaponSlot == 'All' or weapon['Slot'] == weaponSlot) then
end
 
  +
local disp = p._statFormat(weapon, nil, 'Dispo')
  +
dispo[disp] = dispo[disp] or {}
  +
table.insert(dispo[disp], weapon)
  +
end
  +
end
   
  +
for str, dis in Table.skpairs(dispo) do
function p.getConclaveList(frame)
 
  +
table.sort(dis, function(a, b) return a['Disposition'] > b['Disposition'] end)
local WeaponType = frame.args[1]
 
  +
local col = { '| style="vertical-align:top; font-size:small" |' }
  +
for _, weap in ipairs(dis) do
  +
table.insert(col, p._statFormat(weap, nil, 'NameLink')..' ('..weap['Disposition']..')')
  +
end
  +
table.insert(result[3], str)
  +
table.insert(result, table.concat(col, '\n* '))
  +
end
   
  +
result[3] = table.concat(result[3], ']]\n! scope="col" style="text-align:center;"|[[Mods Riven#Disposition|')..']]'
local weapArray = p.getWeapons(function(x)
 
  +
table.insert(result, '|}')
if (x.Type ~= nil and x.Conclave ~= nil) then
 
  +
return table.concat(result, '\n')
return x.Type == WeaponType and x.Conclave
 
else
 
return false
 
end
 
end)
 
 
local result = ""
 
local shortList = p.shortLinkList(weapArray)
 
for i, pair in Shared.skpairs(shortList) do
 
result = result .. '\n* ' .. pair
 
end
 
return result
 
 
end
 
end
   
  +
--- Builds a simple div in the Weapon part of the abilities if an Exalted Weapon is existing.
-- Runs a bunch of things to quickly check if anything broke
 
  +
-- @function p.buildAbilityWeaponTab
function p.checkForBugs(frame)
 
  +
-- @return <table> with weapLink, main page link, weapImg, introSection, fetched from the main Weapon page
return p.buildComparison({args = {"Lato", "Lato Prime"}}) ..
 
  +
function p.buildAbilityWeaponTab(frame)
p.buildComparison({args = {"Glaive", "Glaive Prime"}}) ..
 
  +
local abilityName = frame.args[1]
p.getMeleeComparisonTable({}) ..
 
  +
local AbilityWeaponTab = {}
p.getSecondaryComparisonTable({})
 
end
 
   
  +
for _, weap in pairs(WeaponData) do
function p.checkElements(frame)
 
  +
if weap.WeaponAugment then
local result = "wyrd"
 
  +
if abilityName == weap.WeaponAugment then
for key, theWeap in Shared.skpairs(WeaponData["Weapons"]) do
 
  +
if weap.Name ~= "Lame Exaltée Prime" and weap.Name ~= "Lame Exaltée Umbra" then -- escape duplicate entries
for attName, Attack in p.attackLoop(theWeap) do
 
local elementCount = 0
+
table.insert(AbilityWeaponTab, ([==[
  +
{| id="AbilityWeaponTab" style="width: %s; background-color: #2a2a31; text-align: center; padding: 1rem;"
if (Attack.Damage ~= nil) then
 
for element, dmg in Shared.skpairs(Attack.Damage) do
+
|-
if (not Shared.contains(Physical, element)) then
+
| rowspan=2 | [[Fichier:%s|200px]] || [[%s]]
elementCount = elementCount + 1
+
|-
end
+
| %s
  +
|}
  +
]==]):format(
  +
'100%', -- escape auto-format
  +
weap.Image or 'Panel.png',
  +
weap.Link or 'Lien non trouvé.[[Catégorie:AbilityWeaponTab error]]',
  +
frame:expandTemplate{ title = 'fetchSection', args = {weap.Link, 'intro'} } or 'Aucune description trouvée sur la page.[[Catégorie:AbilityWeaponTab error]]'
  +
))
 
end
 
end
else
 
result = result .. "\n" .. key .. " attempted to loop the " ..
 
attName .. " attack"
 
end
 
if (elementCount > 1) then
 
result = result .. "\n" .. key .. " has " .. elementCount ..
 
" elements in its " .. attName .. " attack"
 
 
end
 
end
 
end
 
end
 
end
 
end
return result
 
end
 
   
  +
-- close and return table
function p.checkForMissingData(frame)
 
  +
return table.concat(AbilityWeaponTab, "")
local result = ""
 
for key, weapon in Shared.skpairs(WeaponData["Weapons"]) do
 
if (weapon.Name == nil) then
 
result = result .. "\n" .. key .. " does not have a Name"
 
end
 
if (weapon.Image == nil) then
 
result = result .. "\n" .. key .. " does not have an Image"
 
end
 
if (weapon.Mastery == nil) then
 
result = result .. "\n" .. key .. " does not have Mastery"
 
end
 
if (weapon.Disposition == nil and p.isArchwing(weapon) == nil) then
 
result = result .. "\n" .. key .. " does not have Disposition"
 
end
 
if (weapon.Type == nil) then
 
result = result .. "\n" .. key .. " does not have a Type"
 
end
 
if (weapon.Class == nil and p.isArchwing(weapon) == nil) then
 
result = result .. "\n" .. key .. " does not have a Class"
 
end
 
if (weapon.NormalAttack ~= nil and weapon.NormalAttack.Damage == nil) then
 
result = result .. "\n" .. key .. " does not do Normal Attacks"
 
end
 
end
 
if (result == "") then result = "All weapons complete based on this test" end
 
return result
 
 
end
 
end
   
function p.buildTunaWeaponTable(frame)
+
function p.getSignatureWeaponsTable(frame)
  +
local signWpsTable = mw.html.create("table"):addClass("bigmodtable sortable"):css("width", "100%") -- main Table
local Category = frame.args ~= nil and frame.args[1] or frame
 
local Weapons = p.getWeapons(function(x)
 
return getValue(x, "Type", true) == Category
 
end)
 
   
  +
-- build main Table headings
local result = '{| style="margin:auto;text-align:center;"'
 
  +
signWpsTable:tag("tr")
local i = 0
 
  +
:tag("th"):wikitext('Warframe/Compagnon'):done()
for key, Weapon in Shared.skpairs(Weapons) do
 
  +
:tag("th"):attr("data-sort-type", "text"):wikitext('Arme(s)'):done()
i = i + 1
 
  +
:tag("th"):addClass("unsortable"):wikitext('Synergie'):done()
local theImage = Weapon.Image ~= nil and Weapon.Image or "Panel.png"
 
  +
:done()
if ((i - 1) % 7 == 0) then result = result .. '\n|-' end
 
result = result .. '\n| style="width:85px" |[[File:' .. Weapon.Name ..
 
'.png|70px]]'
 
end
 
result = result .. '\n|}'
 
return result
 
end
 
   
  +
for key, weaps in pairs(WeaponData) do
-- and we are back... new table building functions !
 
  +
if weaps.Traits then
 
  +
for x, traits in pairs(weaps.Traits) do
local function BuildCompRow(Head, Weapon, UseCompDisplay)
 
  +
if traits == "Signature" then
-- User:Falterfire 6/12/18 - Adding new Comparison Display functionality
 
  +
if weaps.SignatureTags and weaps.SignatureDesc then
-- Toggled with a variable, which is false if not specified
 
  +
local WFAndCompanions = {}
if UseCompDisplay == nil then UseCompDisplay = false end
 
  +
-- Ajouter les éléments de weaps.SignatureTags.Warframes
local styleString = "" -- "border: 1px solid lightgray;"
 
  +
if weaps.SignatureTags.Warframes then
local td = ''
 
  +
for _, wfs in pairs(weaps.SignatureTags.Warframes) do
local result = ''
 
  +
table.insert(WFAndCompanions, Tooltip.full(wfs, "Warframes"))
local ValNameZ = nil
 
local ValName = nil
 
 
-- User:Faltefire 6/12/18 - By default, use old version of code
 
if not UseCompDisplay or Weapon.ComparisonDisplay == nil then
 
local attName = ""
 
if (hasAttack(Weapon, "Charge")) then
 
attName = "Charge"
 
elseif (hasAttack(Weapon, "Normal")) then
 
attName = "Normal"
 
else
 
return ""
 
end
 
 
result = '\n|-\n|'
 
 
for i, Hline in ipairs(Head) do
 
ValName = Hline[1]
 
local isName = false
 
 
if (type(ValName) == "table" and ValName[1] == "default") then
 
ValName = {attName, ValName[2]}
 
elseif (type(ValName) == "table") then
 
ValName = {ValName[1], ValName[2]}
 
else
 
isName = string.upper(ValName) == "NAME"
 
end
 
 
if (i == 1) then
 
td = ''
 
else
 
td = '||'
 
end
 
-- Override pour le nom et link correctement
 
if (getValue(Weapon, ValName) ~= nil) then
 
-- Replace the default name with the name from ComparisonDisplay
 
if isName then
 
local weapName = getValue(Weapon, ValName)
 
local weapLink = p._getLink(weapName, Weapon)
 
result =
 
result .. td .. 'style="' .. styleString .. '"|[[' ..
 
weapLink .. '|' .. weapName .. ']]'
 
elseif (Hline[2]) then
 
-- Add a data sort value if requested
 
result = result .. td .. 'data-sort-value="' ..
 
getValue(Weapon, ValName) .. '" style="' ..
 
styleString .. '"|' ..
 
getValue(Weapon, ValName, true, true, true)
 
else
 
result = result .. td .. 'style="' .. styleString .. '"|' ..
 
getValue(Weapon, ValName, true, true, true)
 
end
 
else
 
result = result .. td .. 'style = "' .. styleString .. '"|' ..
 
"N/A"
 
end
 
end
 
else
 
-- User:Faltefire 6/12/18 - Swap to new version if ComparisonDisplay is set for this weapon and UseCompDisplay is true
 
for i, row in pairs(Weapon.ComparisonDisplay) do
 
local attCount = Shared.tableCount(row.Attacks)
 
if attCount > 0 then
 
local rowText = '\n|-\n|'
 
local attName = row.Attacks[1]
 
for j, Hline in ipairs(Head) do
 
ValName = Hline[1]
 
local KeyName = ''
 
 
-- If we're using the ComparisonDisplay, we're overriding the attempt to shunt to a different attack
 
-- So always use attName + ValName, with one two exceptions: Damage and Name are overridden
 
if (type(ValName) == "table") then
 
ValName = {attName, ValName[2]}
 
KeyName = string.upper(ValName[2])
 
else
 
KeyName = string.upper(ValName)
 
end
 
 
if (j == 1) then
 
td = ''
 
else
 
td = '||'
 
end
 
if KeyName == 'NAME' then
 
local rowName = string.gsub(row.Name, "%[NAME%]",
 
Weapon.Name)
 
-- Replace the default name with the name from ComparisonDisplay
 
rowText = rowText .. td .. 'style="' .. styleString ..
 
'"|[[' .. rowName .. '|' .. rowName ..
 
']]'
 
elseif KeyName == 'DAMAGE' then
 
-- For damage, go with the listed attack
 
if attCount == 1 then
 
rowText = rowText .. td .. 'data-sort-value="' ..
 
getValue(Weapon, {attName, "Damage"}) ..
 
'" style="' .. styleString .. '"|' ..
 
getValue(Weapon, {attName, "Damage"},
 
true, true, true)
 
else
 
-- If multiple attacks are listed, show the combined damage
 
local attDmg = 0
 
for k, att in pairs(row.Attacks) do
 
mw.log(att)
 
attDmg =
 
attDmg + getValue(Weapon, {att, "Damage"})
 
 
end
 
end
rowText = rowText .. td .. 'data-sort-value="' ..
 
attDmg .. '" style="' .. styleString ..
 
'"|' .. attDmg
 
 
end
 
end
else
+
-- Ajouter les éléments de weaps.SignatureTags.Companions
if (getValue(Weapon, ValName) ~= nil) then
+
if weaps.SignatureTags.Companions then
-- Add a data sort value if requested
+
for _, comps in pairs(weaps.SignatureTags.Companions) do
if (Hline[2]) then
+
table.insert(WFAndCompanions, Tooltip.full(comps, "Companions"))
rowText =
 
rowText .. td .. 'data-sort-value="' ..
 
getValue(Weapon, ValName) .. '" style="' ..
 
styleString .. '"|' ..
 
getValue(Weapon, ValName, true, true,
 
true)
 
else
 
rowText =
 
rowText .. td .. 'style="' .. styleString ..
 
'"|' ..
 
getValue(Weapon, ValName, true, true,
 
true)
 
 
end
 
end
else
 
rowText = rowText .. td .. 'style = "' ..
 
styleString .. '"|' .. "N/A"
 
 
end
 
end
end
 
end
 
   
result = result .. rowText
+
-- Afficher les informations
end
+
signWpsTable:tag("tr")
  +
:tag("td"):wikitext(table.concat(WFAndCompanions, '<br/>')):done()
end
 
  +
:tag("td"):wikitext(Tooltip.full(weaps.Name, "Weapons")):done()
end
 
  +
:tag("td"):wikitext(weaps.SignatureDesc):done()
 
  +
:done()
return result
 
end
 
 
local function BuildCompTable(Head, Weapons, UseCompDisplay)
 
 
if (#Weapons > 0) then
 
local styleString = "border: 1px solid black;border-collapse: collapse;"
 
local tHeader = ""
 
tHeader = tHeader ..
 
'\n{| cellpadding="1" cellspacing="0" class="listtable sortable" style="font-size:11px;"'
 
for i, Hline in ipairs(Head) do
 
if (Hline[2] == true) then
 
tHeader = tHeader .. '\n! data-sort-type="number" style="' ..
 
styleString .. '"|' .. Hline[3] .. ''
 
else
 
tHeader = tHeader .. '\n! style="' .. styleString .. '"|' ..
 
Hline[3] .. ''
 
end
 
end
 
-- mw.log(tHeader)
 
 
local tRows = ""
 
for i, Weap in pairs(Weapons) do
 
local rowStr = BuildCompRow(Head, Weap, UseCompDisplay)
 
tRows = tRows .. rowStr
 
end
 
-- mw.log(tRows)
 
return tHeader .. tRows .. "\n|}"
 
else
 
return Shared.printTemplateError('Pas d\'armes trouvees.',
 
'BuildCompTable')
 
end
 
end
 
 
function p.getCompTableGuns(frame)
 
 
local compType = frame.args ~= nil and frame.args[1]
 
local compCat = frame.args ~= nil and frame.args[2] or nil
 
local compTrigger = frame.args ~= nil and frame.args[3] or nil
 
 
-- Check param
 
if (compCat == '') then compCat = nil end
 
if (compTrigger == '') then compTrigger = nil end
 
 
local wpArray = {}
 
if (Shared.contains({'PRINCIPALE', 'SECONDAIRE', 'ROBOTIQUE'}, compType,
 
true)) then
 
wpArray = p.getWeapons(function(x)
 
return weaponFilter_NonMelee(x, compType, compCat, compTrigger)
 
end)
 
else
 
return Shared.printTemplateError('Type inconnu.', 'getCompTableGuns')
 
end
 
 
local Head = {{"Name", false, "Name"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {"Trigger", false, "Mode de Tir"})
 
table.insert(Head, {{"default", "Damage"}, true, "[[Dégâts]]"})
 
table.insert(Head, {
 
{"default", "CritChance"}, true, "[[Chance de Critique|Chance Crit.]]"
 
})
 
table.insert(Head, {
 
{"default", "CritMultiplier"}, true,
 
"[[Multiplicateur de Critique|Multiplicateur de Crit.]]"
 
})
 
table.insert(Head,
 
{{"default", "StatusChance"}, true, "[[Chance de Statut]]"})
 
table.insert(Head, {{"default", "BulletType"}, false, "Type de Projectile"})
 
table.insert(Head, {{"default", "FireRate"}, true, "[[Cadence de Tir]]"})
 
table.insert(Head, {
 
"Magazine", true, "[[Munitions#Capacité du Chargeur|Taille Chargeur]]"
 
})
 
table.insert(Head, {"Reload", true, "[[Rechargement|Tmps. Rechargement]]"})
 
table.insert(Head, {"Mastery", true, "[[Rang de Maîtrise]]"})
 
table.insert(Head,
 
{"Disposition", true, "[[Mod Riven#Disposition|Disposition]]"})
 
 
return BuildCompTable(Head, wpArray, true)
 
end
 
 
function p.getCompTableConclaveGuns(frame)
 
local Catt = frame.args ~= nil and frame.args[1]
 
local Type = frame.args ~= nil and frame.args[2] or nil
 
if (Type == "All") then Type = nil end
 
local WeapArray = {}
 
if (Catt == "Principale") then
 
WeapArray = p.getConclaveWeapons(
 
function(x)
 
if ((getValue(x, "Type", true) == "Principale") and
 
(getValue(x, "Conclave", true) == true)) then
 
if (Type ~= nil) then
 
return getPrimaryCategory(x) == Type
 
else
 
return true
 
 
end
 
end
end
 
return false
 
end)
 
elseif (Catt == "Secondaire") then
 
WeapArray = p.getConclaveWeapons(
 
function(x)
 
if ((getValue(x, "Type", true) == "Secondaire") and
 
(getValue(x, "Conclave", true) == true)) then
 
if (Type ~= nil) then
 
return getSecondaryCategory(x) == Type
 
else
 
return true
 
end
 
end
 
return false
 
end)
 
else
 
return
 
"\n Error : Wrong Class (use Principale or Secondaire) [[Category:Invalid Comp Table]]"
 
end
 
 
local Head = {{"Name", false, "Name"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {"Trigger", false, "[[Fire Rate|Trigger Type]]"})
 
table.insert(Head, {{"default", "Damage"}, true, "[[Dégâts]]"})
 
table.insert(Head, {"HeadshotMultiplier", true, "HS Multiplier"})
 
table.insert(Head, {{"default", "BulletType"}, false, "Projectile Type"})
 
table.insert(Head, {"FireRate", true, "[[Fire Rate]]"})
 
table.insert(Head, {
 
"Magazine", true, "[[Munitions#Magazine Capacity|Magazine Size]]"
 
})
 
table.insert(Head, {"Reload", true, "[[Reload Speed|Reload Time]]"})
 
table.insert(Head, {"Mastery", true, "[[Rang de Maîtrise]]"})
 
 
return BuildCompTable(Head, WeapArray)
 
end
 
 
function p.getCompTableArchGuns(frame)
 
local ArchType = "Arch-Fusil"
 
if (frame.args ~= nil) then
 
if (frame.args[1] == "Arch-Fusil (Atmosphère)") then
 
ArchType = "Arch-Fusil (Atmosphère)"
 
end
 
end
 
 
local WeapArray = {}
 
WeapArray = p.getWeapons(function(x)
 
return getValue(x, "Type", true) == ArchType
 
end)
 
 
local Head = {{"Name", false, "Name"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {"Trigger", false, "Mode de Tir"})
 
table.insert(Head, {{"default", "Damage"}, true, "[[Dégâts]]"})
 
table.insert(Head, {"CritChance", true, "[[Chance de Coup Critique]]"})
 
table.insert(Head, {
 
"CritMultiplier", true,
 
"[[Multiplicateur de Critique|Multiplicateur de Crit.]]"
 
})
 
table.insert(Head, {"StatusChance", true, "[[Chance de Statut]]"})
 
table.insert(Head, {{"default", "BulletType"}, false, "Type de Projectile"})
 
table.insert(Head, {"FireRate", true, "[[Cadence de Tir]]"})
 
table.insert(Head, {
 
"Magazine", true, "[[Ammo#Magazine Capacity|Taille Chargeur]]"
 
})
 
table.insert(Head, {"Reload", true, "[[Vitesse de Rechargement]]"})
 
table.insert(Head, {"Mastery", true, "[[Rang de Maîtrise]]"})
 
return BuildCompTable(Head, WeapArray)
 
end
 
 
function p.getCompTableMelees(frame)
 
-- Changed formatting, now only takes type since only class handled is Melee
 
-- Keeping old formatting to avoid breaking pages
 
local Type = frame.args ~= nil and frame.args[2] or nil
 
if (Type == nil) then Type = frame.args ~= nil and frame.args[1] or nil end
 
if (Type == "All") then Type = nil end
 
local WeapArray = {}
 
WeapArray = getMeleeWeapons(Type)
 
local addClass = Type == nil or Shared.contains(Type, ",")
 
 
local Head = {{"Name", false, "Nom"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {"Class", false, "Type"})
 
table.insert(Head, {{"Normal", "Damage"}, true, "[[Dégâts|Normal]]"})
 
table.insert(Head,
 
{"SlideAttack", true, "[[Mêlée#Attaque Glissée|Glissée]]"})
 
table.insert(Head, {{"default", "FireRate"}, true, "[[Vitesse d'Attaque]]"})
 
table.insert(Head, {
 
"CritChance", true, "[[Chance de Coup Critique|Chance Crit.]]"
 
})
 
table.insert(Head, {
 
"CritMultiplier", true,
 
"[[Multiplicateur de Critique|Multiplicateur Crit.]]"
 
})
 
table.insert(Head, {"StatusChance", true, "[[Chance de Statut]]"})
 
table.insert(Head, {"Mastery", true, "[[Rang de Maîtrise]]"})
 
table.insert(Head, {"STANCEPOLARITY", false, "[[Posture]]"})
 
table.insert(Head,
 
{"Disposition", true, "[[Mod Riven#Disposition|Disposition]]"})
 
for k, v in pairs(Head[1]) do mw.log(k, v) end
 
 
return BuildCompTable(Head, WeapArray)
 
end
 
 
-- As above, but for conclave
 
function p.getCompTableConclaveMelees(frame)
 
local Type = frame.args ~= nil and frame.args[1] or nil
 
if (Type == "All") then Type = nil end
 
local WeapArray = {}
 
WeapArray = getConclaveMeleeWeapons(Type)
 
local addClass = Type == nil or Shared.contains(Type, ",")
 
 
local Head = {{"Name", false, "Name"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {"Class", false, "Type"})
 
table.insert(Head, {{"Normal", "Damage"}, true, "[[Dégâts|Normal]]"})
 
table.insert(Head,
 
{"SlideAttack", true, "[[Mêlée#Attaque Glissée|Glissée]]"})
 
table.insert(Head, {{"default", "FireRate"}, true, "[[Vitesse d'Attaque]]"})
 
table.insert(Head, {"Mastery", true, "[[Rang de Maîtrise]]"})
 
table.insert(Head, {"STANCEPOLARITY", false, "[[Posture]]"})
 
for k, v in pairs(Head[1]) do mw.log(k, v) end
 
 
return BuildCompTable(Head, WeapArray)
 
end
 
 
function p.getCompTableArchMelees(frame)
 
local WeapArray = {}
 
WeapArray = p.getWeapons(function(x)
 
return getValue(x, "Type", true) == "Arch-Mêlée"
 
end)
 
 
local Head = {{"Name", false, "Name"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {{"Normal", "Damage"}, true, "[[Dégâts|Normal]]"})
 
table.insert(Head,
 
{"SlideAttack", true, "[[Mêlée#Attaque Glissée|Glissée]]"})
 
table.insert(Head, {{"default", "FireRate"}, true, "[[Vitesse d'Attaque]]"})
 
table.insert(Head, {
 
"CritChance", true, "[[Chance de Coup Critique|Chance de Crit.]]"
 
})
 
table.insert(Head, {
 
"CritMultiplier", true,
 
"[[Multiplicateur de Critique|Multiplicateur de Crit.]]"
 
})
 
table.insert(Head, {"StatusChance", true, "[[Chance de Statut]]"})
 
table.insert(Head, {"Mastery", true, "[[Rang de Maîtrise]]"})
 
for k, v in pairs(Head[1]) do mw.log(k, v) end
 
 
return BuildCompTable(Head, WeapArray)
 
end
 
 
function p.getCompTableSpeedGuns(frame)
 
local Catt = frame.args ~= nil and frame.args[1]
 
local Type = frame.args ~= nil and frame.args[2] or nil
 
if (Type == "All") then Type = nil end
 
local WeapArray = {}
 
if (Catt == "Principale") then
 
WeapArray = p.getWeapons(function(x)
 
if (getValue(x, "Type", true) == "Principale") then
 
if (Type ~= nil) then
 
return getPrimaryCategory(x) == Type
 
else
 
return true
 
 
end
 
end
 
end
 
end
return false
 
end)
 
elseif (Catt == "Secondaire") then
 
WeapArray = p.getWeapons(function(x)
 
if (getValue(x, "Type", true) == "Secondaire") then
 
if (Type ~= nil) then
 
return getSecondaryCategory(x) == Type
 
else
 
return true
 
end
 
end
 
return false
 
end)
 
elseif (Catt == "Robotique") then
 
WeapArray = p.getWeapons(function(x)
 
return getValue(x, "Type", true) == "Robotique"
 
end)
 
elseif (Catt == "Arch-Fusil") then
 
WeapArray = p.getWeapons(function(x)
 
return getValue(x, "Type", true) == "Arch-Fusil"
 
end)
 
else
 
return
 
"\n Erreur : Mauvaise Class (utilisez Principale, Secondaire, Robotique, ou Arch-Fusil) [[Category:Invalid Comp Table]]"
 
end
 
-- special sorting for projectile weapons
 
local WeapArray2 = {}
 
for k, Weapon in ipairs(WeapArray) do
 
local attName = ""
 
if (hasAttack(Weapon, "Charge")) then
 
attName = "Charge"
 
elseif (hasAttack(Weapon, "Normal")) then
 
attName = "Normal"
 
else
 
return ""
 
end
 
if (getValue(Weapon, {attName, "BulletType"}, false) == "Projectile") then
 
table.insert(WeapArray2, Weapon)
 
elseif (getValue(Weapon, {"Area", "BulletType"}, false) == "Projectile") then
 
table.insert(WeapArray2, Weapon)
 
elseif (getValue(Weapon, {"Secondaire", "BulletType"}, false) ==
 
"Projectile") then
 
table.insert(WeapArray2, Weapon)
 
elseif (getValue(Weapon, {attName, "BulletType"}, false) == "Thrown") then
 
table.insert(WeapArray2, Weapon)
 
 
end
 
end
 
end
 
end
   
  +
signWpsTable:allDone()
local Head = {{"Name", false, "Name"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {"Class", false, "Type"})
 
table.insert(Head, {{"Normal", "PROJECTILESPEED"}, true, "Flight Speed"})
 
table.insert(Head,
 
{{"Charge", "PROJECTILESPEED"}, true, "Charge Flight Speed"})
 
table.insert(Head, {"SPECIALFSPEED", true, "Alt-Fire / Special"})
 
 
return BuildCompTable(Head, WeapArray2)
 
end
 
 
function p.getCompTableSpeedMelees(frame)
 
local Catt = frame.args ~= nil and frame.args[1]
 
local Type = frame.args ~= nil and frame.args[2] or nil
 
if (Type == "Tous") then Type = nil end
 
local WeapArray = {}
 
if (Catt == "Mêlée") then
 
WeapArray = getMeleeWeapons(Type)
 
local addClass = Type == nil or Shared.contains(Type, ",")
 
elseif (Catt == "Arch-Mêlée") then
 
WeapArray = p.getWeapons(function(x)
 
return getValue(x, "Type", true) == "Arch-Mêlée"
 
end)
 
else
 
return
 
"\n Error : Wrong Class (use Melee or Arch-Melee) [[Category:Invalid Comp Table]]"
 
end
 
-- special sorting for projectile weapons ONLY WORKS FOR GLAIVE TYPE MELEE WEAPONS !!!
 
local WeapArray2 = {}
 
for k, Weapon in ipairs(WeapArray) do
 
if (getValue(Weapon, {"Normal", "BulletType"}, false) == "Thrown") then
 
table.insert(WeapArray2, Weapon)
 
end
 
end
 
 
local Head = {{"Name", false, "Name"}}
 
-- better if Name is always the first column !!!
 
table.insert(Head, {{"Normal", "PROJECTILESPEED"}, true, "Flight Speed"})
 
table.insert(Head, {"SPECIALFSPEED", true, "Special"})
 
 
return BuildCompTable(Head, WeapArray2)
 
end
 
 
function p.weaponTooltip(frame)
 
local weapName = frame.args ~= nil and frame.args[1]
 
local conclave = frame.args[2] == "true" and true
 
-- there's no "Dark Split-Sword" in m:weapons/data -> setting name to dual sword variant
 
if weapName == 'Épées Versatiles Sombres' then
 
weapName = 'Épées Versatiles Sombres (Doubles Épées)'
 
end
 
 
local A2Name = nil
 
local A2Value = nil
 
local C1Name = nil
 
local C1Value = nil
 
local C1Toggle = nil
 
local C2Name = nil
 
local C2Value = nil
 
local D1Name = nil
 
local D1Value = nil
 
local D1Value2 = nil
 
local titleText = ''
 
local hasCharged = false
 
local attackType = 'Normal'
 
local attack = nil
 
local attackText = ''
 
local space = '&nbsp;'
 
local attackBiasText = ''
 
 
if weapName == nil then return nil end
 
 
local Weapon = nil
 
local cAvailability = false
 
if conclave then
 
Weapon = p.getConclaveWeapon(weapName)
 
if Weapon ~= nil then
 
cAvailability = getValue(Weapon, "Conclave", true, false, false)
 
end
 
if not cAvailability then Weapon = p.getWeapon(weapName) end
 
else
 
Weapon = p.getWeapon(weapName)
 
end
 
 
local conclaveNotice = ''
 
if conclave and cAvailability == false then
 
conclaveNotice =
 
'\n{| class="Data" style="font-size:10px; white-space:normal;"\n|-\n|Note: Non disponible en Conclave, displaying Cooperative stats and Cooperative Link.\n|}'
 
end
 
 
if Weapon == nil then
 
return 'L\'arme ' .. weapName .. ' n\'a pas été trouvée.'
 
end
 
 
local function Value(valueName, asString, forTable, giveDefault)
 
-- note that the three last parameters aren't in the same order in functions "Value" and "getValue"
 
if (asString == nil) then asString = false end
 
if (forTable == nil) then forTable = false end
 
if (giveDefault == nil) then giveDefault = true end
 
return getValue(Weapon, valueName, giveDefault, asString, forTable)
 
end
 
 
local function whitePols(valueName)
 
local pols = Value(valueName)
 
local polIcon = ''
 
 
if type(pols) == "table" then
 
local i = 0
 
if pols[1] == nil then
 
polIcon = 'Aucune'
 
return polIcon
 
else
 
while pols[i + 1] ~= nil do i = i + 1 end
 
for j = 1, i do
 
polIcon = polIcon .. Icon._Pol(pols[j], 'white', 'x16')
 
end
 
return polIcon
 
end
 
elseif pols ~= nil and type(pols) == "string" and pols ~= "Aucune" then
 
return Icon._Pol(pols, 'white', 'x16')
 
end
 
return 'Aucune'
 
end
 
 
-- for weapons which have no max ammo
 
local ammoException = nil
 
if Value("MaxAmmo") == nil then ammoException = true end
 
 
local isMelee = p.isMelee(Weapon) == "yes"
 
 
local chargedExceptions = {
 
"Tiberon Prime", "Kohm", "Kohmak", "Twin Kohmak", "Stug"
 
} -- weapons which have a charged attack but normal attack values are used for the tooltip
 
local secondaryExceptions = {"Penta", "Carmine Penta", "Secura Penta"}
 
local areaExceptions = {
 
"Ogris", "Zarr", "Tonkor", "Kulstar", "Angstrum", "Prisma Angstrum"
 
}
 
local secondaryAreaExceptions = {"Lenz"}
 
 
if isMelee ~= true then
 
if Shared.contains(chargedExceptions, weapName) ~= true and
 
Value({"Charge", "AttackName"}) ~= nil then
 
hasCharged = true
 
attackType = 'Charge'
 
end
 
if Shared.contains(secondaryExceptions, weapName) then
 
attackType = 'Secondary'
 
elseif Shared.contains(areaExceptions, weapName) then
 
attackType = 'Area'
 
elseif Shared.contains(secondaryAreaExceptions, weapName) then
 
attackType = 'SecondaryArea'
 
end
 
end
 
 
attack = getAttack(Weapon, attackType)
 
local count = 0
 
for type, dmg in Shared.skpairs(attack.Damage) do
 
if count == 0 then
 
attackText = '\n| style=\"padding-right:2px;\" |' ..
 
Icon._Proc(type, '', 'white', 'x16') .. space ..
 
dmg
 
count = count + 1
 
else
 
attackText = attackText .. '|| style=\"padding-right:4px;\" |' ..
 
Icon._Proc(type, '', 'white', 'x16') .. space ..
 
dmg
 
end
 
end
 
 
if (hasMultipleTypes(attack)) then
 
attackBiasText = '\n| colspan=4 |' .. GetDamageString(attack, nil) ..
 
" (" ..
 
GetDamageBiasString(attack, nil, '', nil, 'white',
 
'x16') .. ")"
 
end
 
 
local mRankIcon = ''
 
local mRank = Value("Mastery", false, false, false)
 
local mRankIconLoc = 'top:4px; left:9.5px;'
 
if mRank then
 
if string.len(mRank) >= 2 then
 
mRankIconLoc = 'top:4px; left:5px;'
 
end
 
mRankIcon =
 
'<div style="position:absolute;top:6px; left:4px; color:white; font-size:16px; font-weight:bold; text-shadow: 0 0 1px #0D1B1C, 0 0 4px #0D1B1C, 1px 1px 2px #0D1B1C, -1px 1px 2px #0D1B1C, 1px -1px 2px #0D1B1C, -1px -1px 2px #0D1B1C;">[[File:MasteryAffinity64.png|28px]]<div style="position:absolute;' ..
 
mRankIconLoc .. '">' .. mRank .. '</div></div>'
 
end
 
 
local dispoIcon = ''
 
local dispoVal = Value("Disposition5", false, false, false)
 
if dispoVal then
 
dispoIcon =
 
'<div style="position:absolute;top:6px; right:4px; color:white; font-size:16px; font-weight:bold; text-shadow: 0 0 1px #0D1B1C, 0 0 4px #0D1B1C, 1px 1px 2px #0D1B1C, -1px 1px 2px #0D1B1C, 1px -1px 2px #0D1B1C, -1px -1px 2px #0D1B1C;">[[File:RivenIcon64.png|28px]]<div style="position:absolute;top:3.5px; right:9.5px;">' ..
 
dispoVal .. '</div></div>'
 
end
 
 
if isMelee == true then
 
A2Name = 'Type'
 
A2Value = 'Class'
 
C1Name = 'Vitesse Atq.'
 
C1Value = 'FireRate'
 
C1Toggle = true
 
C2Name = 'Atq. Glissade'
 
C2Value = 'SlideAttack'
 
D1Name = 'Polarités'
 
D1Value = 'Polarities'
 
D1Value2 = 'StancePolarity'
 
else
 
A2Name = 'Tir'
 
A2Value = 'Trigger'
 
if hasCharged == true then
 
C1Name = 'Charge Time'
 
if weapName == "Staticor" then
 
C1Value = {'Area', 'ChargeTime'}
 
else
 
C1Value = {'Charge', 'ChargeTime'}
 
end
 
C1Toggle = true
 
else
 
C1Name = 'Cad. de Tir'
 
C1Value = 'FireRate'
 
end
 
C2Name = 'Sonorité'
 
C2Value = 'NoiseLevel'
 
D1Name = 'Polarités'
 
D1Value = 'Polarities'
 
end
 
 
local function Link(linkName)
 
local spanStart = '<span class=\"LinkText\">'
 
local spanEnd = '</span>'
 
return spanStart .. linkName .. spanEnd
 
end
 
 
local zeroPadding = '\n| style=\"padding:0px;\" |'
 
local newRow = '\n|-'
 
local spacer = '\n| class=\"Spacer\" |'
 
local halfTable = '\n| class=\"TableHalf\" |'
 
local dataText = '\n{| class=\"Data\" style=\"font-size:12px;\"'
 
local dataTextCenter =
 
'\n{| class=\"Data\" style=\"font-size:12px; text-align:center;\"'
 
local tableEnd = '\n|}'
 
 
local Type = ''
 
local tType = Value("Type")
 
if tType == "Arch-Gun (Atmosphere)" then
 
Type = "Arch-Gun (Atmo)"
 
else
 
Type = tType
 
end
 
 
local image =
 
'\n| class=\"Image\" style=\"height:120px;\" | <div style="position:relative; z-index:2;">[[File:' ..
 
Value("Image") .. '|160px]]</div>'
 
 
-- creating the table
 
local result = '<div style="position:relative;">\n{| class=\"Sub\"' ..
 
newRow .. image .. mRankIcon .. dispoIcon .. newRow ..
 
spacer .. newRow .. zeroPadding
 
result = result .. dataText .. newRow
 
result =
 
result .. halfTable .. Link("Slot") .. space .. Type .. halfTable ..
 
Link(A2Name) .. space .. Value(A2Value) .. tableEnd
 
result = result .. newRow .. spacer .. titleText .. newRow .. zeroPadding
 
result = result .. dataTextCenter .. newRow .. attackText
 
if (attackBiasText ~= '') then
 
result = result .. newRow .. attackBiasText
 
end
 
result = result .. tableEnd
 
result = result .. newRow .. spacer .. newRow .. zeroPadding .. dataText ..
 
newRow .. halfTable .. Link("Critique") .. space ..
 
Value("CritChance", true) .. ' | ' ..
 
Value("CritMultiplier", true) .. halfTable .. Link("Statut") ..
 
space .. Value("StatusChance", true)
 
result = result .. newRow .. halfTable .. Link(C1Name) .. space ..
 
Value(C1Value, C1Toggle) .. halfTable .. Link(C2Name) .. space ..
 
Value(C2Value)
 
-- if not melee and not fishing spear => reload and ammo stats
 
if not isMelee and Weapon.Type ~= "Gear" then
 
result = result .. newRow .. halfTable .. Link("Recharge") .. space ..
 
Value("Reload", true, true) .. halfTable ..
 
Link("Munitions") .. space .. Value("Magazine")
 
-- if has max ammo => add max ammo stat
 
if ammoException == nil then
 
result = result .. '&thinsp;/&thinsp;' .. Value("MaxAmmo")
 
end
 
result = result .. newRow ..
 
'\n| style=\"text-align:center;\" colspan=2 |' ..
 
Link(D1Name) .. space .. whitePols(D1Value)
 
elseif isMelee then
 
result = result .. newRow ..
 
'\n| style=\"text-align:center;\" colspan=2 |' ..
 
Link(D1Name) .. space .. whitePols(D1Value2) .. ' | ' ..
 
whitePols(D1Value)
 
end
 
result = result .. tableEnd .. conclaveNotice .. tableEnd .. '\n</div>'
 
 
return result
 
end
 
 
function p.getWeaponReplaceList(frame)
 
-- local Type = frame.args ~= nil and frame.args[1] or frame
 
local fullList = {}
 
local primaries = {}
 
local secondaries = {}
 
local melees = {}
 
local archGuns = {}
 
local archMelees = {}
 
local theRest = {}
 
 
local avoidNames = {
 
'Lame Exaltée (Umbra)', 'Corvas (Atmosphere)', 'Cyngas (Atmosphere)',
 
'Dargyn', 'Doubles Decurion (Atmosphere)', 'Fluctus (Atmosphere)',
 
'Grattler (Atmosphere)', 'Imperator (Atmosphere)',
 
'Imperator Vandal (Atmosphere)', 'Phaedra (Atmosphere)', 'Rampart',
 
'Velocitus (Atmosphere)'
 
}
 
 
local function addToList(name, link, list)
 
if link == nil then link = name end
 
-- local 6s = ' '
 
-- local 8s = ' '
 
local temp = ' <Replacement>\n <Find>[[' .. link ..
 
']]</Find>\n <Replace>{{Weapon|' .. name ..
 
'}}</Replace>\n <Comment />\n <IsRegex>false</IsRegex>\n <Enabled>true</Enabled>\n <Minor>false</Minor>\n <BeforeOrAfter>false</BeforeOrAfter>\n <RegularExpressionOptions>IgnoreCase</RegularExpressionOptions>\n </Replacement>'
 
return table.insert(list, temp)
 
end
 
 
for i, val in Shared.skpairs(WeaponData["Weapons"]) do
 
if not Shared.contains(avoidNames, val.Name) then
 
 
if val.Type == "Principale" then
 
addToList(val.Name, val.Link, primaries)
 
elseif val.Type == "Secondaire" then
 
addToList(val.Name, val.Link, secondaries)
 
elseif val.Type == "Mêlée" then
 
addToList(val.Name, val.Link, melees)
 
elseif val.Type == "Arch-Fusil" then
 
addToList(val.Name, val.Link, archGuns)
 
elseif val.Type == "Arch-Mêlée" then
 
addToList(val.Name, val.Link, archMelees)
 
else
 
addToList(val.Name, val.Link, theRest)
 
end
 
end
 
end
 
 
table.insert(fullList, table.concat(primaries, '\n'))
 
table.insert(fullList, table.concat(secondaries, '\n'))
 
table.insert(fullList, table.concat(melees, '\n'))
 
table.insert(fullList, table.concat(archGuns, '\n'))
 
table.insert(fullList, table.concat(archMelees, '\n'))
 
table.insert(fullList, table.concat(theRest, '\n'))
 
 
return table.concat(fullList, "\n")
 
end
 
 
local function buildHTMLTable(table)
 
local resultTable = '\n{| class="wikitable"'
 
for i, val in Shared.skpairs(table) do
 
resultTable = resultTable .. '\n|' .. i .. '||'
 
if (val ~= nil and type(val) == "table") then
 
resultTable = resultTable .. buildHTMLTable(val)
 
else
 
resultTable = resultTable .. tostring(val)
 
end
 
resultTable = resultTable .. '\n|-'
 
end
 
resultTable = resultTable .. '\n|}'
 
return resultTable
 
end
 
 
function p.buildHTMLDB() return buildHTMLTable(WeaponData["Weapons"]) end
 
 
local function buildFamilyMap(weapArray)
 
 
local ret = {}
 
 
for _, weapon in ipairs(weapArray) do
 
local key = nil
 
if (weapon.Family ~= nil) then
 
key = weapon.Family
 
else
 
key = weapon.Name
 
end
 
if (ret[key] == nil) then
 
ret[key] = {weapon.Name}
 
else
 
table.insert(ret[key], weapon.Name)
 
end
 
end
 
 
return ret
 
end
 
 
local function printFamily(familyName, familyValues, conclave)
 
 
local ret = {}
 
local ttType = 'Weapon'
 
 
table.insert(ret,
 
Tooltip._tooltipText(familyName, ttType, nil, conclave, nil))
 
 
local famString = {}
 
if (#familyValues > 1) then
 
for _, wpName in pairs(familyValues) do
 
if (wpName ~= familyName) then
 
local newName = Shared.trim(wpName:gsub(familyName, ""))
 
-- Special cases
 
if (newName == "MK1-") then
 
newName = "MK1"
 
elseif (Shared.contains(newName, "Dex ")) then
 
newName = "Dex"
 
end
 
 
table.insert(famString, Tooltip._tooltipText(wpName, ttType,
 
newName, conclave,
 
nil))
 
end
 
end
 
 
table.insert(ret,
 
'(' .. table.concat(famString, Shared.getListSep()) .. ')')
 
end
 
 
return table.concat(ret, ' ')
 
end
 
 
function p.getWeaponsList(frame)
 
 
local mainCat = frame.args ~= nil and frame.args[1]
 
local subCat = frame.args ~= nil and frame.args[2]
 
local triggerType = frame.args ~= nil and frame.args[3]
 
local conclave = frame.args ~= nil and frame.args[4]
 
conclave = conclave ~= nil and conclave ~= "false"
 
 
return p._getWeaponsList(mainCat, subCat, triggerType, conclave)
 
end
 
 
function p._getWeaponsList(mainCat, subCat, triggerType, conclave)
 
 
local testFunction = nil
 
if (mainCat == "Mêlée") then
 
testFunction = function(x)
 
return weaponFilter_Melee(x, mainCat, subCat)
 
end
 
else
 
testFunction = function(x)
 
return weaponFilter_NonMelee(x, mainCat, subCat, triggerType)
 
end
 
end
 
-- Get array of weapons
 
local weapArray = nil
 
if (conclave) then
 
weapArray = p.getConclaveWeapons(testFunction)
 
else
 
weapArray = p.getWeapons(testFunction)
 
end
 
-- Build family map
 
local weapMap = buildFamilyMap(weapArray)
 
-- Build String for result array
 
local ret = {}
 
for family, wpList in Shared.skpairs(weapMap) do
 
if (Shared.contains(wpList, family, true)) then
 
table.insert(ret, printFamily(family, wpList, conclave))
 
else
 
for _, tmpName in pairs(wpList) do
 
table.insert(ret, Tooltip._tooltipText(tmpName, 'Weapon', nil,
 
conclave, nil))
 
end
 
end
 
end
 
 
return table.concat(ret, Shared.getListSep())
 
end
 
 
local function getAbilityWeaponArray(abilityName)
 
 
local ret = WeaponData["AbilityWeapons"][abilityName]
 
if (ret == nil) then ret = {} end
 
 
return ret
 
end
 
 
function p.buildAbilityWeaponTab(frame)
 
 
local abilityName = frame.args[1]
 
local ret = {}
 
local left = true
 
 
local wpArray = getAbilityWeaponArray(abilityName)
 
for _, wpName in pairs(wpArray) do
 
table.insert(ret, '[[File:')
 
table.insert(ret, getValue(p.getWeapon(wpName), "IMAGE"))
 
table.insert(ret, '|200px|')
 
if (left) then
 
left = false
 
table.insert(ret, 'left]]')
 
else
 
left = true
 
table.insert(ret, 'right]]')
 
end
 
local pageName = p._getLink(wpName)
 
table.insert(ret, frame:preprocess('{{main|' .. pageName .. '}}'))
 
local introSection = frame:expandTemplate{
 
title = 'fetchSection',
 
args = {pageName, 'intro'}
 
}
 
if (introSection == nil or introSection == '') then
 
introSection = "L'article [[" .. pageName ..
 
"]] ne possède pas de section intro."
 
end
 
table.insert(ret, '<br/>')
 
table.insert(ret, introSection)
 
table.insert(ret, frame:expandTemplate{title = 'clr'})
 
end
 
   
return table.concat(ret)
+
return frame:preprocess(tostring(signWpsTable))
 
end
 
end
   

Dernière version du 1 avril 2024 à 10:27

Weapons contient toutes les Armes de WARFRAME.

Usage

Template

In template and articles: {{#invoke:Weapons|function|input1|input2|...}}

Documentation

Objets du paquet

weapons._isVariant(weaponName) (function)
Checks if a weapon is a variant or not.
Paramètre: weaponName Weapon name (string)
Retours:
  • True if weapon is a variant, false otherwise (boolean)
  • Weapon's variant name or "Base" if weapon is not a variant (string)
  • Weapon name, same as weaponName (string)
weapons._buildName(baseName, variant) (function)
Builds the full name of a weapon's variant. Does not check if it exists or not.
Paramètres:
  • baseName Weapon's base name (e.g. "Braton") (string)
  • variant Variant name (e.g. "Vandal"); if nil, returns base weapon name instead (string; optionnel)
Retours: Weapon's variant name (e.g. "Braton Vandal") (string)
weapons._getWeapon(weaponName, pvp) (function)
Returns a specific weapon table entry from /data or /Conclave/data.
Paramètres:
  • weaponName Weapon name (string)
  • pvp If true, gets PvP stats of weapon instead, false otherwise; defaults to false (boolean; optionnel)
Retours: Weapon table (table)
weapons._getValue(Weapon, key, attack) (function)
Gets the raw value of a certain statistic of a weapon.
Paramètres:
  • Weapon Weapon table (table)
  • key Name of key (string)
  • attack Name of attack to search through; defaults to 'Attack1' or what '_TooltipAttackDisplay' is set to (string; optionnel)
Retours: Value of statistic (string, number)
weapons._getFormattedValue(Weapon, keyName, attackName) (function)
Gets the formatted value of a certain statistic of a weapon to be displayed the wiki.
Paramètres:
  • Weapon Weapon table (table)
  • keyName Name of key (string)
  • attackName Name of attack to search through; defaults to 'Attack1' (string; optionnel)
Retours: Value of statistic (string)
weapons._statReader(weap, atk) (function)
Function that returns a simpler getter function, for multiple _stat*() calls on the same weapon/attack pair.
Paramètres:
  • weap Weapon entry (table)
  • atk Attacks table index or Attack entry (number|table)
Retours: Getter function (function)
weapons._statFormatter(weap, atk) (function)
Function that returns a simpler getter function, for multiple _stat*() calls on the same weapon/attack pair.
Paramètres:
  • weap Weapon entry (table)
  • atk Attacks table index or Attack entry (number|table)
Retours: Getter function (function)
weapons._getWeapons(validateFunction, source, ignoreIgnore, sortFunc) (function)
Returns a subset of /data or /Conclave/data based on a validation function.
Paramètres:
  • validateFunction Function that filters out a weapon by taking in a Weapon table argument (function)
  • source Name of weapon entry to use (string; optionnel)
  • ignoreIgnore If true, ignores the _IgnoreEntry flag, false otherwise; defaults to false (boolean; optionnel)
  • sortFunc Custom comparison function; false -> no sorting; defaults to sorting in ascending order by weapon name (function; optionnel)
Retours: Table of weapon table entries as seen in /data (table)
weapons._getMeleeWeapons(weapType, pvp) (function)
Returns all melee weapons. If weapType is not nil, only grab for a specific type For example, if weapType is "Nikana", only pull Nikanas.
Paramètres:
  • weapType (boolean; optionnel)
  • pvp If true, only gets melee weapons available in Conclave, false otherwise; defaults to false (boolean; optionnel)
Retours: An array of melee weapon table entries as seen in /data (table)
weapons.getValue(weap, atk, k) (function)
Main frame invokable function to access any raw/computed attribute/column/key of a weapon entry. See default table in M:Weapons to see all valid computed attributes.
Paramètres:
  • weap Weapon name in EN locale (string)
  • atk Attacks table index (number)
  • k Key name (string)
Retours: Raw or computed value associated with k key
weapons.getFormattedValue(weap, atk, k) (function)
Main frame invokable function to access any formatted attribute/column/key of a weapon entry. See default table in M:Weapons to see all valid computed attributes.
Paramètres:
  • weap Weapon name in EN locale (string)
  • atk Attacks table index (number)
  • k Key name (string)
Retours: Formatted value associated with k key
weapons.getMeleeWeaponGallery(frame) (function)
Builds a melee weapon gallery as seen on Template:MeleeCategory.
Paramètre: frame Frame object w/ first argumenting being string meleeClass (table)
Retours: Resultant wikitext of gallery (string)
weapons.getWeaponCount(frame) (function)
Gets the total count of weapons as used on Mastery Rank#Total Mastery.
Paramètre: frame Frame object w/ the first argument being the weaponSlot and the second argument being a boolean to getFullList (table)
Retours:
  • Total count of weapons in a certain category/type (number)
  • List of weapon names that count for mastery in a particular weapon slot (table)
weapons.getWeaponCount(frame) (function)
Gets the total count of weapons as used on Mastery Rank#Total Mastery.
Paramètre: frame Frame object w/ the first argument being the weapon slot (table)
Retours: Total number of weapons that can reward Mastery XP (number)
weapons.getPolarityTable(frame) (function)
Builds wikitable of all weapons' innate polarities as seen on Polarity.
Paramètre: frame Frame object (table)
Retours: Wikitext of resultant wikitable (string)
weapons.buildDamageTypeTable(frame) (function)
Builds a table that lists out all weapons with a certain damage type
Paramètre: frame Frame object (table)
Retours: Wikitext of resultant wikitable (string)
weapons._shortLinkList(Weapon, tooltip) (function)
Builds a list of weapons, with variants being next to base weapon name inside parentheses (e. g. Braton Braton (Braton MK1, Braton Prime Prime)).
Paramètres:
  • Weapon Weapon table (table)
  • tooltip If true, adds weapon tooltips, false otherwise; defaults to false (boolean)
Retours: Wikitext of resultant list (string)
weapons.getMasteryShortList(frame) (function)
Builds a list of weapons' mastery requirements as seen on Template:EquipmentUnlock, Template:EquipmentUnlock/Primary, Template:EquipmentUnlock/Secondary, Template:EquipmentUnlock/Melee, etc.
Paramètre: frame Frame object w/ first argument being a string weaponSlot (table)
Retours: Wikitext of resultant list (string)
weapons.getConclaveList(frame) (function)
Builds a list of PvP weapons as seen on PvP#Limitations.
Paramètre: frame Frame object w/ first argument being a string weaponSlot (table)
Retours: Wikitext of resultant list (string)
weapons.getRivenDispositionTable(frame) (function)
Builds a disposition wikitable as seen on Riven Mods/Weapon Dispos.
Paramètre: frame Frame object w/ first argument being a string weaponSlot (table)
Retours: Wikitext of resultant wikitable (string)
weapons.buildAbilityWeaponTab() (function)
Builds a simple div in the Weapon part of the abilities if an Exalted Weapon is existing.
Retours: with weapLink, main page link, weapImg, introSection, fetched from the main Weapon page
Documentation automatique créée à l'aide de Docbuntu (-> Modèle automatique)

Voir aussi

Code


---	'''Weapons''' contient toutes les [[Armes]] de [[WARFRAME]].
--	
--	@module			weapons
--	@alias			p
--	@attribution	[[User:Cephalon Scientia|Cephalon Scientia]]
--	@attribution	[[User:FINNER|FINNER]]
--	@attribution	[[User:Falterfire|Falterfire]]
--	@attribution	[[User:Gigamicro|Gigamicro]]
--	@attribution	[[User:Flaicher|Flaicher]]
--	@attribution	[[User:Synthtech|Synthtech]]
--	@attribution	[[User:Yazuh|Yazu]] (retranscription)
--	@image		IconPrimaryWeaponRifle.png
--	@require	[[Module:StatObject]]
--	@require	[[Module:DamageTypes]]
--	@require	[[Module:Polarity]]
--	@require	[[Module:Math]]
--	@require	[[Module:Table]]
--	@require	[[Module:Tooltips]]
--	@require	[[Module:Version]]
--	@require	[[Module:Stances/data]]
--	@require	[[Module:Weapons/data]]
--	@require	[[Module:Weapons/Conclave/data]]
--	@release	stable
--	<nowiki>

-- TODO: Add LuaDoc style comments to new functions
local p = {}

local Delay = require([[Module:Delay]])
local WeaponData = Delay.require([[Module:Weapons/data]])
local ConclaveData = Delay.require([[Module:Weapons/Conclave/data]])
local Tooltip = Delay.require([[Module:Tooltips]]) -- full, icon
local Version = Delay.require([[Module:Version]]) -- _getVersion, _getVersionDate
local Polarity = Delay.require([[Module:Polarity]]) -- _pols, _polarity
local Math = Delay.require([[Module:Math]]) -- formatnum
local Table = Delay.require([[Module:Table]]) -- size, skpairs
local iterationOrderArray = require([[Module:DamageTypes]]).iterationOrderArray

-- TODO: Should decouple from localized names for internationalization
local VARIANT_LIST = {
	"Prime", "Prisma", "Wraith", "Vandal", "Vaykor", "Synoid", "Telos", "Secura",
	"Sancti", "Rakta", "Mara", "Carmine", "Ceti", "Dex", "MK1", "Kuva", "Principe"
}

table.unpack = table.unpack or unpack

local StatObject = require [[Module:StatObject]]
p.__StatObject = StatObject
local statRead = StatObject.statRead
local statFormat = StatObject.statFormat

local indexes = StatObject.meta.indexes
local ors = StatObject.meta.ors
local unpacks = StatObject.meta.unpacks

local passes = StatObject.meta.passes
local percent = StatObject.meta.percent
local percents = StatObject.meta.percents

---	Gets the attack entry from weapon entry.
--	@function		p._getAttack
--	@param			{table} weap Weapon entry
--	@param[opt]		{number|table} atk Attacks table index or Attack entry
--	@return			{table} A single weapon+attack struct
local function getWeaponAttack(weap, atk)
	if type(atk) == 'number' then return StatObject.getStruct2(weap,weap.Attacks[atk]) end
	if weap.AttackName then return weap end
	if type(atk) == 'table' then return StatObject.getStruct2(weap,atk) end
	local key = atk or weap['_TooltipAttackDisplay'] or 1
	if weap.Attacks == nil then
		error('p._getWeaponAttack(weap, atk): Attacks table is nil in '..mw.dumpObject(weap))
	end
	return StatObject.getStruct2(weap,weap.Attacks[key])
end
p._getAttack = getWeaponAttack
p._getWeaponAttack = getWeaponAttack

function p._statRead(w, a, ...)
	return statRead(getWeaponAttack(w, a), ...)
end
function p._statFormat(w, a, ...)
	return statFormat(getWeaponAttack(w, a), ...)
end
function p.stat(frame)
	return p._statFormat(p._getWeapon(frame.args[1] or 'Skana Prime'), nil, frame.args[2] or 'Name')
end

-- Wrapper function for use in StatObject
local function dmgTooltip(damageType)
	return Tooltip.full(damageType, 'DamageTypes')
end

-- Defining getters/attributes whose names match the associated database key or some custom derived attribute.
-- Index key will be name of getter function and can be mapped to a single value (getter definition) 
-- or a table with two values (getter and format function definitions)

-- Cheatsheet on adding new keys:
-- StatName = default value -> Get raw value with the same StatName from M:Weapons/data and with no additional formatting (aka default formatting)
-- StatName = function(self) return self.StatName + 1 end -> Define custom getter function and use default formatting
-- StatName = { default value, '%.2f' } -> Get raw value value with same StatName from M:Weapons/data and use format string for formatting
-- StatName = { function(self) return ... end, '%.2f' } -> Define custom getter function and use format string for formatting
-- StatName = { function(self) return ... end, function(self, returnValue1, returnValue2, ...) return tostring(returnValue) end } - > Define custom getter and format functions
-- (Note that format function will pass in return value(s) from getter as well as object self)

-- TODO: Put StatObject keys in alphabetical order for navigation
StatObject.default = {
AttackName = 'Normal Attack',
AmmoCost = nil,
AmmoPickup = function(weapAtk)
	return weapAtk['AmmoPickup'] or
		weapAtk['Slot'] == 'Principale' and 80 or 
		weapAtk['Slot'] == 'Secondaire' and 40 or 
		weapAtk['Slot'] == 'Arch-Fusil (Atmosphère)' and 1000 or
		0
end,
DamageBias = {
	function(weapAtk)
		if not weapAtk.Damage then
			error('DamageBias: no Attack.Damage')
			return 0, 0, 0
		end
		local total, bestdmg, bestdt = 0, 0, nil
		for dt, dmg in pairs(weapAtk.Damage) do
			local dmg = dmg
			if dmg >= bestdmg then
				bestdmg, bestdt = dmg, dt
			end
			total = total + dmg
		end
		return StatObject.ucacheIn(weapAtk, 'DamageBias', { bestdmg / total, bestdt, total })
	end,
	{ percent, passes(dmgTooltip), '' }
},
BiasPortion = { indexes('DamageBias', 1), percent },
BiasType = { indexes('DamageBias', 2), function(self, biasType) return Tooltip.icon(biasType, 'DamageTypes') end },
BaseDamage = { indexes('DamageBias', 3), '%.2f' },
-- More precise damage values to 4 decimal places for PvP since PvP damage is calculated
-- based on a floating-point scalar. Damage quantization is more relevant in PvP so more
-- precise numbers needed.
PvPBaseDamage = { indexes('DamageBias', 3), '%.4f' },
TotalDamage = { function(weapAtk)
	return statRead(weapAtk, 'BaseDamage') * statRead(weapAtk, 'Multishot')
end, passes(Math.formatnum)
},
-- Including max +60% Progenitor bonus for Kuva/Tenet weapons
TotalDamageWithProgenBonus = { function(weapAtk)
	return statRead(weapAtk, 'TotalDamage') * (statRead(weapAtk, 'IsLichWeapon') and 1.6 or 1)
end, passes(Math.formatnum)
},
ChargeTime = { 0, '%.1f s' },
ExplosionDelay = { 0, '%.1f s' },
ExtraHeadshotDmg = { 0, percents('+%.2f%%') },
Falloff = {
	function(weapAtk)
		local fo = weapAtk['Falloff'] or {}
		return fo.StartRange or 0, fo.EndRange or math.huge, 1 - (fo.Reduction or 1)
	end,
	{ '%.1f m (100%%) -', '%.1f m', percents('(%.2f%%)') }
},
FalloffStart = { indexes('Falloff', 1), '%.1f m' },
FalloffEnd = { indexes('Falloff', 2), '%.1f m' },
-- Damage reduction from falloff instead of damage multiplier
FalloffReduction = { function(weapAtk)
	local _, _, falloff = statRead(weapAtk, 'Falloff')
	return 1 - falloff
end, percent
},
FalloffRate = { function(weapAtk)
	local startdist,enddist,endpercent = statRead(weapAtk, 'Falloff')
	return -(enddist-startdist)/(endpercent-1)
end, '%.1fm/%%'
},
HeadshotMultiplier = { 1, '%.1fx' },
Multishot = 1,
PunchThrough = { 0, '%.1f m' },
ShotSpeed = { nil, function(self, shotSpeed)
	if shotSpeed == nil or "?" then
		return 'N/A'
	end
	return ('%.1f m/s'):format(shotSpeed)
end
},
BurstDelay = { 0, '%.4f s' },
BurstReloadDelay = { 0, '%.2f s' },
BurstsPerSec = { function(weapAtk)
	-- There is no delay after last shot in burst
	return 1 / ( (1 / statRead(weapAtk, 'FireRate') ) + statRead(weapAtk, 'BurstDelay') * ( statRead(weapAtk, 'BurstCount') - 1) )
end, '%.2f rafales/sec' },
CritChance = { 0, percent },
CritMultiplier = { 1, '%.2fx' },
ForcedProcs = { unpacks('ForcedProcs'), function(s, ...)
	local procs = { ... }
	if procs[1] == nil then
		return 'Aucun statut forcé'
	end
	local result = {}
	for _, proc in ipairs(procs) do
		table.insert(result, Tooltip.full(proc, 'DamageTypes'))
	end
	return table.concat(result, ', ')
end
},
Radius = { 0, '%.1f m' },
StatusChance = { 0, percent },
Disposition = {
	function(weap)
		local d = weap['Disposition']
		-- Returning a categorical bin value of 1, 2, 3, 4, or 5 based on where disposition value
		-- is on the continuous scale of 0.5-1.55. If disposition value is nil then return 0
		return d or 0, type(d)=='number' and math.floor(5*(d-.3+.009*(d<1 and 1 or -1))) or 0
	end,
	function(s, v, d)
		return StatObject.default.Dispo[2](s, d)..(' (%.2fx)'):format(v)
	end
},
Dispo = { indexes('Disposition', 2), function(s, d)
	if d and d == d and d > 0 then
		return ('●'):rep(math.min(d, 5))..('○'):rep(5 - d)
	end
	return '×××××' -- '●○×' --> E2978F E2978B C397
end },
Introduced = { function(weap)
	return weap['Introduced'] and Version._getVersion(weap['Introduced'])['Name'] or 'N/A'
end, passes(Version._getVersionLink)
},
IntroducedDate = function(weap)
	return weap['Introduced'] and Version._getVersionDate(weap['Introduced']) or 'N/A'
end,
IsLichWeapon = function(weap)
	return weap['IsLichWeapon'] and true or false
end,
Mastery = 0,
Link = { nil, '[[%s]]' },
Name = { nil, function(s, v) return Tooltip.full(v, 'Weapons') end },
InternalName = '',
NameLink = { function(weap) return weap.Link, weap.Name end, '[[%s|%s]]' },
Polarities = { nil, passes(Polarity._pols) },
Traits = { unpacks('Traits'), { sep = ', ' } },
-- Default nil b/c some attacks don't have an associated accuracy/spread value (like AoE explosions)
Accuracy = { nil, function(self, value)
	if (value == nil) then
		return 'N/A'
	end
	return value
end
},
-- Inverse of accuracy. Spread of 1 equates to no spread.
-- Alternatively, it can be calculated by the average of min and max spread, see AvgSpread getter.
Spread = { function(weapAtk)
	local accuracy = statRead(weapAtk, 'Accuracy')
	return (accuracy == nil) and nil or 100 / accuracy
end, function(self, value)
	if (value == nil) then
		return 'N/A'
	end
	return value
end
},
AmmoType = function(weapAtk)
	return weapAtk['AmmoType'] or ({
		['Arch-Fusil (Atmosphère)'] = 'Lourde',
		['Secondaire'] = 'Secondaire',
		['Principale'] = 'Principale'
	})[weapAtk['Slot']] or 'None'
end,
-- Not all weapons have an Exilus slot so default to nil
ExilusPolarity = { nil, function(self, exilusPolarity)
	if (exilusPolarity == nil) then
		return 'N/A'
	end
	return Polarity._polarity(exilusPolarity)
end
},
Magazine = 1,
AmmoMax = { function(weapAtk)
	if statRead(weapAtk, 'IsMelee') then
		return nil
	end
	return weapAtk['AmmoMax'] or math.huge
end, passes(Math.formatnum)
},
Range = { function(weapAtk)
	return weapAtk['Range'] or statRead(weapAtk, 'ShotType') == 'Impact-direct' and 300 or 0
end, '%.1f m'
},
Reload = { ors('Reload', 'RechargeTime', 0), '%.2f s' },
RechargeTime = { function(weapAtk)
	return statRead(weapAtk, 'ReloadStyle'):find'[Rr]egen' and statRead(weapAtk, 'Magazine') / statRead(weapAtk, 'ReloadRate') or nil
end, '%.2f s'
},
ReloadRate = { 0, '%.2f balles/sec' },	-- Used for rechargeable weapons; not necessarily inverse of reload time b/c of presence of reload delay
ReloadDelay = { function(weapAtk)
	return weapAtk['ReloadDelay'] or 0
end, '%.2f s'
},
ReloadDelayEmpty = { ors('ReloadDelayEmpty', 'ReloadDelay'), '%.2f s' },
-- Reload speed will be calculated as the inverse of reload time for purposes
-- of keeping how we rank stats consistent for [[Module:Weapons/preprocess]]
-- (larger number = higher stat; a short reload time can be expressed as fast reload 
-- speed which would be a larger value in magnitude)
ReloadSpeed = { function(weapAtk)
	return 1 / statRead(weapAtk, 'Reload')
end, function(str, reloadSpeed)
	return string.format('%.2f%% progression de recharge par seconde', reloadSpeed * 100)
end },
ReloadStyle = 'Magazine',
Spool = { 0, '%d balles' },
SpoolStartFireRate = { 0, '%.1fx' },	-- scalar that is applied to fire rate stat for auto-spool weapons
AvgSpread = { function(weapAtk)
	local minSpread = statRead(weapAtk, 'MinSpread')
	local maxSpread = statRead(weapAtk, 'MaxSpread')
	if (minSpread == nil) then
		return nil
	end
	return (minSpread + maxSpread) / 2
end, function(self, value)
		if (value == nil) then
			return 'N/A'
		end
		return ('%.2f°'):format(value)
	end
},
-- Default nil b/c some attacks don't have an associated accuracy/spread value (like AoE explosions)
MinSpread = { nil, function(self, value)
	if (value == nil) then
			return 'N/A'
		end
		return ('%.2f°'):format(value)
	end
},
MaxSpread = { nil, function(self, value)
	if (value == nil) then
			return 'N/A'
		end
		return ('%.2f°'):format(value)
	end
},
Trigger = 'N/A',
BlockAngle = { 0, '%d&#176;' },
ComboDur = { 0, '%.1f s' },
FollowThrough = { 0, '%.1fx' },
HeavyAttack = { 0, passes(Math.formatnum) },
HeavySlamAttack = { 0, passes(Math.formatnum) },
HeavyRadialDmg = { 0, passes(Math.formatnum) },
HeavySlamRadius = { 0, '%.1f m' },
MeleeRange = { 0, '%.2f m' },
SlamAttack = { 0, passes(Math.formatnum) },
SlamRadialDmg = { function(weapAtk)
	return weapAtk.SlamRadialDmg or 0, statRead(weapAtk, 'SlamRadialElement')
end, function(self, dmg, elem)
	if elem then
		return Tooltip.icon(elem, 'DamageTypes')..' '..Math.formatnum(dmg)
	end
	return Math.formatnum(dmg)
end
},
SlamRadialElement = { nil, function(self, value)
	return value ~= nil and Tooltip.full(value, 'DamageTypes') or 'Même répartition des types de dégâts que pour l\'attaque normale'
end
},
-- Slam radial forced proc(s)
SlamRadialProcs = { nil, function(self, proc)
	if type(proc)=='table' then
		local result = {}
		for _, elem in ipairs(proc) do
			table.insert(result, Tooltip.full(elem, 'DamageTypes'))
		end
		return table.concat(result, '<br />')
	else
		return 'N/A'
	end
end
},
SlamRadius = { 0, '%.1f m' },
SlideAttack = { function(weapAtk)
	return weapAtk.SlamRadialDmg or 0, statRead(weapAtk, 'SlideElement')
end, function(self, dmg, elem)
	if elem then
		return Tooltip.icon(elem, 'DamageTypes')..' '..Math.formatnum(dmg)
	end
	return Math.formatnum(dmg)
end
},
SlideElement = { nil, function(self, value)
	return value ~= nil and Tooltip.full(value, 'DamageTypes') or 'Même répartition des types de dégâts que pour l\'attaque normale'
end
},
--[[Stances = function(weapAtk)
	if not statRead(weapAtk, 'IsMelee') then return end
	return Stances._getAllStancesSameType(statRead(weapAtk, "Class"))
	--             ^ currently a local function
end,--]]
-- Not all weapons have an Stance slot so default to nil
StancePolarity = { nil, function(self, stancePolarity)
	if (stancePolarity == nil) then
		return 'N/A'
	end
	return Polarity._polarity(stancePolarity)
end
},
SweepRadius = { 0, '%.2f m' },
WindUp = { 0, '%.1f s' },
BurstCount = 1,
-- Average crit/proc count from a single attack input
AvgCritCount = function(weapAtk)
	return statRead(weapAtk, 'CritChance') * statRead(weapAtk, 'Multishot')
end,
AvgCritPerSec = function(weapAtk)
	return statRead(weapAtk, 'AvgCritCount') * statRead(weapAtk, 'EffectiveFireRate')
end,
AvgProcCount = function(weapAtk)
	return ( statRead(weapAtk, 'StatusChance') + Table.size(weapAtk['ForcedProcs'] or {}) ) * statRead(weapAtk, 'Multishot')
end,
AvgProcPerSec = function(weapAtk)
	return statRead(weapAtk, 'AvgProcCount') * statRead(weapAtk, 'EffectiveFireRate')
end,
InterShotTime = function(weapAtk)
	local v = statRead(weapAtk, 'Magazine') == 1 and statRead(weapAtk, 'Reload') + statRead(weapAtk, 'ReloadDelayEmpty') or 0
	if v == 0 then v = 1 / statRead(weapAtk, 'FireRate') end
	return v
end,
EffectiveFireRate = function(weapAtk)
	return 1 / ( statRead(weapAtk, 'ChargeTime') + statRead(weapAtk, 'InterShotTime') )
end,
ShotsPerMag = function(weapAtk)
	-- Default to 1 "ammo cost" even if attack does not directly consume ammo (e.g. AoE hits, speargun throws, etc.)
	return math.floor(statRead(weapAtk, 'Magazine') / (statRead(weapAtk, 'AmmoCost') or 1))
end,
FireRate = { function(weapAtk)
	local dataFireRate = weapAtk['FireRate']
	if dataFireRate then return dataFireRate end
	-- TODO: Think we can safely remove this calculation of FireRate from BurstFireRate, BurstDelay, and BurstCount
	-- for burst-fire attacks since FireRate is also included for those
	mw.log('calcul de la cadence de tir depuis les stats de rafales pour '..statRead(weapAtk, 'Name'))
	local count = statRead(weapAtk, 'BurstCount')
	local fireRate = count / (1 / statRead(weapAtk, 'BurstFireRate') + count * statRead(weapAtk, 'BurstDelay'))
	return fireRate
end, '%.3f tirs/sec'
},
BurstFireRate = { function(weapAtk)
	return 1 / statRead(weapAtk, 'BurstDelay')
end, '%.2f tirs/sec'
},
--[[
Describing what happens when a gun in WARFRAME is fired using player-made terminology:

A particular gun consumes a set number of ammo in order to fire a set number of shots
on a single player input for a particular attack.

A single player input is defined as:
* a single attack button press for semi-auto and burst trigger weapons
* the moment the next shot is being fired when the attack button is being held for automatic/held trigger weapons
* the action of holding the attack button for charge trigger weapons
* for duplex-fire trigger weapons, the hold and release of the attack button counts as two inputs

A shot is defined as the base unit of attack of a weapon when unmodded.
* A single attack input can launch several shots as in the case of burst-fire weapons.
* A single shot can shoot more than one projectile, affected by the multishot stat, as in the case of shotguns.
* A single shot can consume more than one unit of ammo (e.g. Tenora's alt-fire) or 
less than one unit of ammo (e.g. Ignis and most continuous weapons).

A gun can have multiple attacks which can be triggered using different buttons 
and/or types of button inputs (e.g. pressing vs. holding)
]]--
CalcDamage = function(weapAtk)
	local weapon, attack = weapAtk, weapAtk
	-- Count
	-- How many shots are fired in a single player input
	local tapShots = statRead(weapAtk, 'BurstCount')
	-- How many individual player inputs can occur before depleting a magazine
	local magTaps = statRead(weapAtk, 'ShotsPerMag')
	-- How many additional projectiles are fired per ammo
	local multishot = statRead(weapAtk, 'Multishot')
	-- How much ammo is contained in the magazine
	local magazine = statRead(weapAtk, 'Magazine')
	-- How much ammo can be drawn from reserves (or?, how much ammo can be used without picking up more)
	local ammoMax = statRead(weapAtk, 'AmmoMax')

	-- Time^-1
	local fireRate = statRead(weapAtk, 'FireRate')
	-- Time
	local shotTime = statRead(weapAtk, 'InterShotTime')
	local chargeTime = statRead(weapAtk, 'ChargeTime')
	local burstDelayTime = statRead(weapAtk, 'BurstDelay')
	local reloadDelayTime = statRead(weapAtk, 'ReloadDelayEmpty')
	local reloadTime = statRead(weapAtk, 'Reload')
	local tapTime = chargeTime + (tapShots - 1) * burstDelayTime
	-- tapTime: The time between the last shot fired and the next valid attack input
	-- (omitting latency of course).
	-- Note that first shot of any non-charge trigger attack is instantenous
	local magDepletionTime = magTaps * tapTime
	if magDepletionTime == 0 then -- If attack is not a charged attack
		if shotTime == 0 then
			shotTime = 1 / fireRate
		end
		magDepletionTime = magTaps / fireRate
	end
	local shotDelayTime = math.max(0, shotTime - tapTime)

	-- Multiplier
	local maxProgenitorBonus = statRead(weapAtk, 'IsLichWeapon') and 1.6 or 1
	local avgCritMult = 1 + (statRead(weapAtk, 'CritMultiplier') - 1) * statRead(weapAtk, 'CritChance')
	-- Damage
	local biasPortion, biasType, hitDamage = statRead(weapAtk, 'DamageBias')
	
	local avgDmgOnTap = hitDamage * avgCritMult * multishot * tapShots * maxProgenitorBonus
	local avgDmgPerMag = avgDmgOnTap * magTaps
	
	-- 1 is needed b/c one whole magazine is not included in reserve ammo count
	-- If there is no reserve ammo, that means that weapon can deal an infinite amount of damage theoretically
	local avgLifetimeDmg = (ammoMax ~= nil) and avgDmgPerMag * (1 + (ammoMax / magazine)) or math.huge
	
	-- Damage / Time
	local baseDps = hitDamage * multishot / shotTime
	local avgSustainedDps = avgDmgPerMag / (magDepletionTime + reloadDelayTime + reloadTime) / tapShots
	local avgBurstDps = avgDmgOnTap / (tapTime + shotDelayTime) / tapShots
	-- Note that burst DPS can also be calculated as such:
	-- local avgBurstDps = (hitDamage * avgCritMults * maxProgenitorBonus) * multishot / shotTime
	-- local avgBurstDps = avgDmgPerMag / magDepletionTime
	
	return StatObject.ucacheIn(weapAtk, 'CalcDamage',
			{ hitDamage, avgDmgOnTap, avgBurstDps, avgSustainedDps, avgLifetimeDmg, baseDps, avgDmgPerMag }
	)
end,
ShotDmg = indexes('CalcDamage', 1),	-- Total damage per projectile
AvgShotDmg = indexes('CalcDamage', 2), AvgTapDmg = indexes('CalcDamage', 2),	-- Average total damage per each input button
BurstDps = indexes('CalcDamage', 3),	-- Average burst damage per second/DPS w/o reloading
SustainedDps = indexes('CalcDamage', 4),	-- Average sustained damage per second/DPS w/ reloading
LifetimeDmg = indexes('CalcDamage', 5),	-- Average total damage from entire ammo pool
BaseDps = indexes('CalcDamage', 6),	-- Base damage per second w/ multishot w/o crit
MagDmg = indexes('CalcDamage', 7),	-- Average total damage per magazine
-- Average damage scaled by melee attack speed multiplier (numerator of melee DPS w/o accounting for stances and animation time)
AvgDmgWithAnimSpeedMulti = function(weapAtk)
	if statRead(weapAtk, 'IsMelee') then
		-- Some melee weapons have attacks with multishot like Redeemer, Vastilok, and Quassus
		return statRead(weapAtk, 'BaseDamage') * statRead(weapAtk, 'Multishot') * statRead(weapAtk, 'AttackSpeed')
	end
	return 0
end,
AttackSpeed = { --[[ors('AttackSpeed', 'FireRate')]]function(weapAtk)
	if not statRead(weapAtk, 'IsMelee') then
		error('AttackSpeed: Cannot get AttackSpeed attribute for a non-melee weapon; use p.statRead(weapAtk, "FireRate") instead')
	end
	return statRead(weapAtk, 'FireRate')
end, '%.2fx vitesse d\'animation'
},
IsMelee = function(weapAtk) return statRead(weapAtk, 'Slot'):find('Mêlée') ~= nil end,
IsSilent = ors('IsSilent', 'IsMelee', false),
HasAoEAttack = function(weap)
	for i, attackEntry in pairs(weap['Attacks']) do
		if attackEntry['ShotType'] == 'AoE' then
			return true
		end
	end
	return false
end,
Conclave = false,
Image = { 'Panel.png', '[[File:%s|link=]]' },
Attacks = ors('Attacks', p._getAttack, {}),
Family = nil,
FamilyList = { function(weapAtk)
	local family = statRead(weapAtk, 'Family')
	-- assert(family, 'i have no Family :\'(')
	if not family then return {weapAtk} end
	-- return family, statRead(weapAtk, 'Slot')
	local slot = statRead(weapAtk, 'Slot')
	local result = {}
	for _, w in pairs(WeaponData[slot] or error('FamilyList: no weapondata for slot '..(slot or '<nil>'))) do
		if w.Family == family then
			table.insert(result, w)
		end
	end
	table.sort(result, function(a,b) return a.Name<b.Name end)
	return result
end, function(self, result)
	for i,w in ipairs(result) do
		result[i]=Tooltip.full(w.Name, 'Weapons', w)
	end
	return table.concat(result, '<br />')
end
},
BaseName = function(weapAtk) return weapAtk['BaseName'] or ({p._getVariant(statRead(weapAtk, 'Name'))})[3] end,
-- TODO: Add comments to Explosion function for readability
-- TODO: Do not rely on attack name to determine what AoE component is attached to which main direct hit component
---^i suggest an explosion key with either the attack number of any corresponding explosion, nested attack tables, or some other way to make a tree
-- TODO: Use ShotType = "AoE" to determine if attack entry is AoE
Explosion = function(weapAtk)
	local weap, atk = weapAtk, weapAtk
	-- tbh this is a mess
	local explosion = weapAtk['Explosion'] or statRead(weapAtk, 'AttackName'):gsub(' Impact',''):gsub(' Contact','')..' Explosion'
	if type(explosion) == 'string' then
		explosion = weap.Attacks[tonumber(explosion:gsub('%D',''))] or explosion
	elseif type(explosion) == 'number' then
		explosion = weap.Attacks[explosion] or explosion
	end
	local explosions = {}
	if type(explosion) ~= 'table' then
		for i, v in ipairs(weap.Attacks) do
			if p._statRead(weapAtk, v, 'AttackName'):find 'xplosion' then
				if p._statRead(weapAtk, v, 'AttackName') == explosion then
					explosions[1] = nil
					explosion = v
					break
				end
				table.insert(explosions, v)
			end
		end
		explosion = explosions[1] or explosion
	end
	StatObject.pcacheIn(getWeaponAttack(weap, explosion), 'BaseAttack', atk)
	return StatObject.pucacheIn(weapAtk, 'Explosion', explosion)
end,
IsVariant = function(weap)
	return StatObject.pucacheIn(weap, 'IsVariant', p._isVariant(statRead(weap, 'Name')))
end,
Variant = indexes('IsVariant', 2),
BaseName = indexes('IsVariant', 3),
Categories = { function(weapAtk)
	local cats = { 'Armes' }
	-- Adding editor-defined traits from M:Weapons/data
	-- Note to make sure they have a proper category page associated with a trait

	for _, trait in ipairs(weapAtk.Traits or {}) do
		local weapsTraitsMap = ({
			-- De fabrication / origines
			["Tenno"] = { "Tenno", "Armes/Tennos" },
			["Corpus"] = { "Corpus", "Armes/Corpus" },
			["Grineer"] = { "Grineer", "Armes/Grineers" },
			["Infestée"] = { "Infestée", "Armes/Infestées" },
			["Orokin"] = { "Orokin", "Armes/Orokins" },
			["Sentient"] = { "Sentient", "Armes/Sentients" },
			["Entrati"] = { "Entrati", "Armes/Entratis" },
			["Céphalon"] = { "Céphalon", "Armes/Céphalons" },
			-- Spéciales & Variantes
			["Incarnon"] = { "Armes/Incarnon" },
			["Principe"] = { "Armes/Principe" },
			["Prisma"] = { "Armes/Prisma" },
			["Vandal"] = { "Armes/Vandal" },
			["Wraith"] = { "Armes/Wraith" },
			["MK1"] = { "Armes/MK1" },
			["Signature"] = { "Armes/Signature" },
			["Liche Kuva"] = { "Liche Kuva", "Armes/Liche Kuva" },
			-- Prime state
			["Prime"] = { "Prime", "Armes/Prime" },
			["Vaultée"] = { "Prime Vault", "Armes/Vault" },
			["Jamais Vaultée"] = { "Jamais en Vault" },
			-- Syndicats
			["Syndicats"] = { "Armes de Syndicats" },
			["Arbitres de Hexis"] = { "Arbitres de Hexis" }, -- Telos
			["Céphalon Suda"] = { "Céphalon Suda" }, -- Synoid
			["Méridien d'Acier"] = { "Méridien d'Acier" }, -- Vaykor
			["Nouveau Loka"] = { "Nouveau Loka" }, -- Sancti
			["La Séquence Perrin"] = { "La Séquence Perrin" }, -- Secura
			["Voile Rouge"] = { "Voile Rouge" }, -- Rakta
			["Céphalon Simaris"] = { "Céphalon Simaris" }, -- Special
			-- Armes de "Factions" / PNJ
			["Dax"] = { "Armes/Dax" },
			["Duviri"] = { "Armes/Duviri" },
			["Zariman"] = { "Armes/Zariman" },
			["Cetus"] = { "Armes/Cetus" },
			["Fortuna"] = { "Armes/Fortuna" },
			["Puy de Cambion"] = { "Armes/Puy de Cambion" },
			["Stalker"] = { "Stalker", "Armes/Stalker" },
			["Baro"] = { "Offres de Baro Ki'Teer" },
			-- Autres
			["Dex"] = { "Armes/Dex" },
			["Quête"] = { "Armes/Quêtes" },
			["Fondateur"] = { "Fondateur", "Armes/Fondateur" },
			["Invasions"] = { "Invasions", "Récompenses d'évènements", "Armes/Invasions", "Armes/Évènements" },
			["Alertes Tactiques"] = { "Récompenses d'Alertes Tactiques" },
		})[trait]
		
		if weapsTraitsMap then
			for _, cat in ipairs(weapsTraitsMap) do
				table.insert(cats, cat)
			end
		else
			table.insert(cats, "Armes/Incomplètes")
		end
	end

	-- DamageTypes categories
	local bias = p._getValue(weapAtk, "BiasType")
	table.insert(cats, ({
		-- Physic
		["Impact"] = "Armes/Dégâts/Impact",
		["Tranchant"] = "Armes/Dégâts/Tranchant",
		["Perforation"] = "Armes/Dégâts/Perforation",
		-- Elemental
		-- -- Main
		["Feu"] = "Armes/Dégâts/Feu",
		["Glace"] = "Armes/Dégâts/Glace",
		["Poison"] = "Armes/Dégâts/Poison",
		["Électrique"] = "Armes/Dégâts/Électrique",
		-- -- Mixed
		["Gaz"] = "Armes/Dégâts/Gaz",
		["Viral"] = "Armes/Dégâts/Viral",
		["Corrosif"] = "Armes/Dégâts/Corrosif",
		["Explosif"] = "Armes/Dégâts/Explosif",
		["Radiation"] = "Armes/Dégâts/Radiation",
		["Magnétique"] = "Armes/Dégâts/Magnétique",
		-- -- Specials
		["Tau"] = "Armes/Dégâts/Tau",
		["Brut"] = "Armes/Dégâts/Brut",
		["Néant"] = "Armes/Dégâts/Néant",

	})[bias] or "Armes/Dégâts/Inconnus")
	
	local class = p._getValue(weapAtk, "Class")
	table.insert(cats, ({
		-- Principales
		["Canon à bras"] = "Armes/Canons à bras",
		["Arc"] = "Armes/Arcs",
		["Arbalète"] = "Armes/Arcs",
		["Fusil"] = "Armes/Fusils",
		["Fusil Sniper"] = "Armes/Fusils de Sniper",
		["Fusil à Pompe"] = "Armes/Fusils à Pompe",
		["Lance à énergie"] = "Armes/Lances à énergie",
		["Lanceur"] = "Armes/Lanceurs",
		-- Secondaires
		["Doubles Pistolets"] = "Armes/Doubles Pistolets",
		["Fusil à Pompe à double canon"] = "Armes/Fusils à Pompe à double canon",
		["Fusil à Pompe court"] = "Armes/Fusils à Pompe courts",
		["Lancé"] = "Armes/Lancés",
		["Pistolet"] = "Armes/Pistolets",
		["Volume"] = "Armes/Volumes",
		-- Mêlées
		["Arme d'hast"] = "Armes/Armes d'hast",
		["Bâton"] = "Armes/Bâtons",
		["Dague"] = "Armes/Dagues",
		["Doubles Dagues"] = "Armes/Doubles Dagues",
		["Doubles Nikanas"] = "Armes/Doubles Nikanas",
		["Doubles Épées"] = "Armes/Doubles Épées",
		["Faux"] = "Armes/Faux",
		["Faux Lourde"] = "Armes/Faux Lourdes",
		["Fouet"] = "Armes/Fouets",
		["Glaive"] = "Armes/Glaives",
		["Griffes"] = "Armes/Griffes",
		["Lame Lourde"] = "Armes/Lames Lourdes",
		["Lame et Fouet"] = "Armes/Lames et Fouets",
		["Machette"] = "Armes/Machettes",
		["Mains et Pieds"] = "Armes/Mains et Pieds",
		["Marteau"] = "Armes/Marteaux",
		["Nikana"] = "Armes/Nikanas",
		["Nikana à Deux Mains"] = "Armes/Nikanas à Deux Mains",
		["Nunchaku"] = "Armes/Nunchakus",
		["Pistolame"] = "Armes/Pistolames",
		["Poings"] = "Armes/Poings",
		["Rapière"] = "Armes/Rapières",
		["Scie d'Assaut"] = "Armes/Scies d'Assaut",
		["Tonfa"] = "Armes/Tonfas",
		["Épée"] = "Armes/Épées",
		["Épée et Bouclier"] = "Armes/Épées et Boucliers",
		["Éventail de guerre"] = "Armes/Éventails de guerre",
		-- Spéciales
		["Arme Exaltée"] = "Armes/Exaltées",
		["Amplificateur"] = "Armes/Amplificateurs",
		["Arch-Fusil"] = "Armes/Arch-Fusils",
		["Arch-Fusil (Atmosphère)"] = "Armes/Arch-Fusils",
		["Arch-Mêlée"] = "Armes/Arch-Mêlées",
		["Pièce principale"] = "Armes/Railjack",
		["Tourelle"] = "Armes/Railjack",
		["Railjack Armament"] = "Armes/Railjack",
		["Unique"] = "Armes/Uniques", -- e.g Parazon/Robotiques/Molosses
		-- Zaws
		["Zaw Rapière / Arme d'hast"] = "Armes/Zaws",
		["Zaw Machette / Marteau"] = "Armes/Zaws",
		["Zaw Faux / Bâton"] = "Armes/Zaws",
		["Zaw Nikana / Bâton"] = "Armes/Zaws",
		["Zaw Épée / Bâton"] = "Armes/Zaws",
		["Zaw Épée / Arme d'hast"] = "Armes/Zaws",
		["Zaw Machette / Arme d'hast"] = "Armes/Zaws",
		["Zaw Faux / Lame Lourde"] = "Armes/Zaws",
		["Zaw Machette / Arme d'hast"] = "Armes/Zaws",
		["Zaw Dague / Bâton"] = "Armes/Zaws",
		["Zaw Rapière / Arme d'hast"] = "Armes/Zaws",
		-- Kitguns
		["Fusil Kitgun"] = "Armes/Kitguns",
		["Lanceur Kitgun"] = "Armes/Kitguns",
		["Pistolet Kitgun"] = "Armes/Kitguns",
		["Fusil à Pompe Kitgun"] = "Armes/Kitguns",
	})[class] or "Armes/Inconnues")

	-- Modular category ('Armes/Modulaires')
	if string.find(class, "^Zaw") or string.find(class, "Kitgun$") then
		table.insert(cats, "Armes/Modulaires")
	end

	local family = p._getValue(weapAtk, "Family")
	table.insert(cats, family)
	
	local slot = p._getValue(weapAtk, "Slot")
	table.insert(cats, ({
		["Principale"] = "Armes/Principales", -- Principale
		["Secondaire"] = "Armes/Secondaires", -- Secondaire
		["Mêlée"] = "Armes/Mêlées", -- Mêlée
		-- Archwing
		["Arch-Fusil"] = "Armes/Arch-Fusils",
		["Arch-Fusil (Atmosphère)"] = "Armes/Arch-Fusils",
		["Arch-Mêlée"] = "Armes/Arch-Mêlées",
		-- Railjack
		["Tourelle Railjack"] = "Armes/Railjacks",
		["Pièce principale Railjack"] = "Armes/Railjacks",
		--
		["Amplificateur"] = "Armes/Amplificateurs", -- Amplificateurs
		["Robotique"] = "Armes/Robotiques", -- Sentinelles
		["Molosse"] = "Armes/Molosses", -- Molosses
		["Emplacement"] = "Armes/Emplacements", -- Rempart (only)
		["Équipement"] = "Armes/Équipements", -- Ebisu / Lanzo / etc..
		["Nech-Mêlée"] = "Armes/Nech-Mêlées", -- Pugil
		["Unique"] = "Armes/Uniques", -- e.g Parazon
		["Véhicule"] = "Armes/Véhicules", -- Dargyn
	})[slot] or "Armes/Inconnues")
	
	-- Fonctionnalité désactivée pour y insérer un tableau associatif juste après ce commentaire (Yazuh)
	-- local trigger = p._getValue(weapAtk, "Trigger")
	-- table.insert(cats, 'Armes ' .. trigger)

	table.insert(cats, ({
		["Activée"] = 'Armes/Tir à Activation',
		["Auto / Chargée"] = 'Armes/Chargée',
		["Automatique"] = 'Armes/Automatique',
		["Auto / Rafale"] = 'Armes/Tir en Rafale',
		["Cadence progressive"] = 'Armes/Automatique',
		["Rafale"] = 'Armes/Tir en Rafale',
		["Chargée"] = 'Armes/Chargée',
		["Duplex"] = 'Armes/Tir en Duplex',
		["Maintenue"] = 'Armes/Tir en continu',
		["Semi-auto"] = 'Armes/Semi-automatique',
	})[trigger] or 'Armes') -- fix for melee weapon (no trigger)
	
	local users = p._getValue(weapAtk, "Users") or {}
	for _, user in ipairs(users) do table.insert(cats, user) end
	
	-- Aucun besoin de cette fonctionnalité (by: Yazu)
	-- local variant = p._getValue(weapAtk, "Variant")
	-- table.insert(cats, variant)
	
	local infAmmo = p._getValue(weapAtk, "AmmoMax") == math.huge
	local accuracy = p._getValue(weapAtk, "Accuracy")
	local pinpoint = accuracy ~= nil and accuracy >= 100
	local regens = p._getValue(weapAtk, "ReloadRate") > 0
	local silent = weapAtk.IsSilent -- automatically includes
	local single = p._getValue(weapAtk, "Magazine") == 1 and not p._getValue(weapAtk, "IsMelee")--meh, delet?
	local spools = p._getValue(weapAtk, "Spool") > 0
	local isAoE = p._getValue(weapAtk, "HasAoEAttack")
	local isCodexSecret = p._getValue(weapAtk, "CodexSecret")
	local isTradable = p._getValue(weapAtk, "Tradable")
	local isInConclave = p._getValue(weapAtk, "Conclave")
	
	-- Arbitrarily ordering misc categories
	if infAmmo then table.insert(cats, 'Armes/Armes à munitions infinies') end
	if pinpoint then table.insert(cats, 'Armes/Armes de précision') end
	if regens then table.insert(cats, 'Armes/Armes à batterie') end
	if silent then
		table.insert(cats, 'Armes/Armes silencieuses')
	else
		table.insert(cats, 'Armes/Armes bruyantes')
	end
	if single then table.insert(cats, 'Armes/Armes à coup unique') end
	if spools then table.insert(cats, 'Armes/Armes à bobine') end
	if isAoE then table.insert(cats, 'Armes/Armes à zone d\'effet') end
	if isCodexSecret then table.insert(cats, 'Armes/Secret du codex') end
	if isTradable then
		table.insert(cats, 'Armes/Armes échangeables')
	else
		table.insert(cats, 'Armes/Armes non échangeables')
	end
	if isInConclave then table.insert(cats, 'Armes/Disponibles au Conclave') end
	
	return StatObject.cacheIn(weapAtk, 'Categories', cats)
end, function(s, cats)
	local wikitextResult = { '' }	-- Need to prepend a newline so first asterisk is rendered as a wikitext list
	local formatStr = '*[[:Catégorie:%s|%s]][[Catégorie:%s]]'
	for _, category in ipairs(cats) do
		table.insert(wikitextResult, formatStr:format(category, category, category))
	end
	return table.concat(wikitextResult, '\n')
end
},
SyndicateEffect = { '', function(s, v)
	return (v == '' or type(v) ~= 'string') and '' or Tooltip.icon(({
		['rouille'] = 'Voile Rouge',
		['entropie'] = 'Céphalon Suda',
		['justice'] = 'Méridien d\'Acier',
		['pureté'] = 'Nouveau Loka',
		['séquence'] = 'La Séquence Perrin',
		['vérité'] = 'Arbitres de Hexis',
	})[v:lower()] or 'Tenno', 'Factions')
	..' '..v
end
},
MinProgenitorBonus = function(weap) return weap.IsLichWeapon and statRead(weap, 'BaseDamage') * 0.25 or 0 end,
ProgenitorBonus = function(weap) return weap.IsLichWeapon and statRead(weap, 'BaseDamage') * 0.6 or 0 end,
Class = '',
SniperComboReset = { nil, '%.1f s' },
SniperComboMin = { nil, '%d shot(s)' },
Tradable = { function(weapAtk)
	if type(weapAtk['Tradable'])=='number' then
		assert(weapAtk['Tradable']<=5,
			'Tradable: Does not support tradeability enums beyond 5; please update [[Module:Weapons/data]] and [[Module:Weapons]] to support more tradeability edge cases')
		return ({
			[0]=false,
			[1]='Rang 0',
			[2]='Parties',
			[3]='Liches',
			[4]='Parties pour fabrication',
			[5]='Arme liée',
		})[weapAtk['Tradable']]
	end
	return weapAtk['Tradable']
end, function(s, tradable)
	return ({ 
		[false] = 'Non-échangeable',
		['Unranked'] = 'Non classée sans Forma ou Catalyseur',
		['Parts'] = 'Pièces et/ou schéma uniquement',
		['Lich'] = 'Par le biais des échanges des [[Liche Kuva|Liches]].',
		['Built Parts'] = 'Uniquement des composants construits, et non des schémas',
		['Parent'] = 'Indirectement, livré avec le compagnon associé',
	})[tradable] or 'Non-échangeable?'
end
},
SellPrice = { nil, function(self, sellPrice)
	if sellPrice == nil then
		return 'Invendable'
	end
	return Tooltip.icon('Crédits', 'Resources')..' '..Math.formatnum(sellPrice)
end
},
DefaultUpgrades = { nil, function(self, upgradesArr)
	local result = {}
	for _, modIndex in ipairs(upgradesArr or {}) do
		table.insert(result, Tooltip.full(modIndex, 'Mods'))
	end
	return table.concat(result, '<br />')
end
},
Users = { nil, function(self, usersArr)
	local result = { '' }
	for _, user in ipairs(usersArr or {}) do
		table.insert(result, '*[['..user..']]')
	end
	return table.concat(result, '\n')
end
},
Zoom = { unpacks('Zoom'), { sep = '<br />' } },
Slot = nil,
}
-- Loops for adding to StatObject.default table
-- Damage type getters:
-- <DamageType> = damage type value
-- <DamageType>Distribution = damage type distribution as a percentage
-- PvP<DamageType> = damage type value with precise formatting for PvP purposes
for _, damageType in ipairs(iterationOrderArray) do
	StatObject.default[damageType] = {
		function(weapAtk) return weapAtk['Damage'][damageType] or 0 end,
		function(self, value) return Tooltip.icon(damageType, 'DamageTypes')..' '..Math.formatnum(value) end
	}
	-- Damage distribution as a percentage
	StatObject.default[damageType..'Distribution'] = {
		function(weapAtk) return weapAtk['Damage'][damageType] / statRead(weapAtk, 'BaseDamage') end,
		function(self, value) return Tooltip.icon(damageType, 'DamageTypes')..' '..Math.percentage(value) end
	}
	-- More precise damage values to 4 decimal places for PvP
	StatObject.default['PvP'..damageType] = {
		function(weapAtk) return weapAtk['Damage'][damageType] or 0 end,
		Tooltip.icon(damageType, 'DamageTypes')..' %.4f'
	}
end

-- TODO: Do not rely on localized name to determine a weapon's variant. Decouple localization from data
---	Checks if a weapon is a variant or not.
--	@function		p._isVariant
--	@param			{string} weaponName Weapon name
--	@returns		{boolean} True if weapon is a variant, false otherwise
--	@returns		{string} Weapon's variant name or "Base" if weapon is not a variant
--	@returns		{string} Weapon name, same as weaponName
function p._isVariant(weaponName)
	for i, var in pairs(VARIANT_LIST) do
		if (var ~= "Dex" or weaponName ~= "Dex Pixia") then
			if string.find(weaponName, var) then
				return true, var, (string.gsub(weaponName, " ?"..var.." ?-?", ""))
			end
		end
	end
	return false, "Variante de base", weaponName
end

---	Builds the full name of a weapon's variant. Does not check if it exists or not.
--	@function		p._buildName
--	@param			{string} baseName Weapon's base name (e.g. "Braton")
--	@param[opt]		{string} variant Variant name (e.g. "Vandal"); if nil, returns base weapon name instead
--	@returns		{string} Weapon's variant name (e.g. "Braton Vandal")
function p._buildName(baseName, variant)
	if not variant or variant == 'Base' or variant == '' then
		return baseName
	end
	return (({
		-- Prime Laser Rifle is an edge case for Prime naming scheme (at least in EN localization)
		Prime =  baseName ~= 'Fusil Laser' and '%b %v',
		Vandal = '%b %v',
		Wraith = '%b %v',
		MK1 = '%v-%b',
	})[variant] or '%v %b'):gsub('%%v', variant):gsub('%%b', baseName)
end

---	Returns a specific weapon table entry from <code>/data</code> or <code>/Conclave/data</code>.
--	@function		p._getWeapon
--	@param			{string} weaponName Weapon name
--	@param[opt]		{boolean} pvp If true, gets PvP stats of weapon instead, false otherwise; defaults to false
--	@returns		{table} Weapon table
function p._getWeapon(weaponName, pvp)
	weaponName = mw.text.decode(weaponName)
	return (pvp and ConclaveData or WeaponData)[weaponName] or
	error('p._getWeapon(weaponName, pvp): "'..weaponName..
		'" does not exist in '..(pvp and '[[Module:Weapons/Conclave/data]]' or '[[Module:Weapons/data]]'))
end

---	Gets the raw value of a certain statistic of a weapon.
--	@function		p._getValue
--	@param			{table} Weapon Weapon table
--	@param			{string} key Name of key
--	@param[opt]		{string} attack Name of attack to search through; defaults to 'Attack1' or what '_TooltipAttackDisplay' is set to
--	@returns		{string, number} Value of statistic
function p._getValue(weap, key, atk)--, formatted)
	-- return (formatted and statFormat or statRead)(weap, atk, key)
	return p._statRead(weap, atk, key)
end

---	Gets the formatted value of a certain statistic of a weapon to be displayed
--	the wiki.
--	@function		p._getFormattedValue
--	@param			{table} Weapon Weapon table
--	@param			{string} keyName Name of key
--	@param[opt]		{string} attackName Name of attack to search through; defaults to 'Attack1'
--	@returns		{string} Value of statistic
function p._getFormattedValue(weap, key, atk)
	-- return p._getValue(Weapon, keyName, attackName, true)
	return p._statFormat(weap, atk, key)
end

--- Function that returns a simpler getter function, for multiple _stat*() calls on the same weapon/attack pair.
--	@function		p._statReader
--	@param			{table} weap Weapon entry
--	@param			{number|table} atk Attacks table index or Attack entry
--	@return			{function} Getter function
function p._statReader(weap, atk)
	return function(...) return p._statRead(weap, atk, ...) end
end

--- Function that returns a simpler getter function, for multiple _stat*() calls on the same weapon/attack pair.
--	@function		p._statFormatter
--	@param			{table} weap Weapon entry
--	@param			{number|table} atk Attacks table index or Attack entry
--	@return			{function} Getter function
function p._statFormatter(weap, atk)
	return function(...) return p._statFormat(weap, atk, ...) end
end

---	Returns a subset of <code>/data</code> or <code>/Conclave/data</code> based on a validation function.
--	@function		p._getWeapons
--	@param			{function} validateFunction Function that filters out a weapon by taking in a Weapon table argument
--	@param[opt]		{string} source Name of weapon entry to use
--	@param[opt]		{boolean} ignoreIgnore If true, ignores the _IgnoreEntry flag, false otherwise; defaults to false
--	@param[opt]		{function} sortFunc Custom comparison function; false -> no sorting; defaults to sorting in ascending order by weapon name
--	@returns		{table} Table of weapon table entries as seen in <code>/data</code>
function p._getWeapons(validateFunction, source, opts)
	opts=opts or {}
	local ignoreIgnore, sortFunc, pvp = opts.ignoreIgnore, opts.sortFunc, opts.pvp
	validateFunction = validateFunction or function() return true end
	local data = pvp and ConclaveData or WeaponData
	if source then
		data = data[source]
	end

	local weaps = {}
	for _, weap in pairs(data) do
		if (ignoreIgnore or not weap['_IgnoreEntry']) and validateFunction(weap) then
			table.insert(weaps, weap)
		end
	end
	if sortFunc ~= false then
		table.sort(weaps, sortFunc or function(a, b) return a.Name < b.Name end)
	end
	return weaps
end

---	Returns all melee weapons. If weapType is not nil, only grab for a specific type
--	For example, if weapType is "Nikana", only pull Nikanas.
--	@function		p._getMeleeWeapons
--	@param[opt]		{boolean} weapType
--	@param[opt]		{boolean} pvp If true, only gets melee weapons available in Conclave, false otherwise; defaults to false
--	@returns		{table} An array of melee weapon table entries as seen in <code>/data</code>
function p._getMeleeWeapons(weapType,pvp)
	return p._getWeapons(weapType and function(weap) return weap.Class==weapType end, 'mêlée',{['pvp']=pvp==true})
end

---	Main frame invokable function to access any raw/computed attribute/column/key of a weapon entry.
--	See default table in M:Weapons to see all valid computed attributes.
--	@function		p.getValue
--	@param			{string} weap Weapon name in EN locale
--	@param			{number} atk Attacks table index
--	@param			{string} k Key name
--	@return			Raw or computed value associated with k key
function p.getValue(frame)
	-- table.unpack doesn't work on the frame object which is why this is anonymous function is needed
	local weap, key, atk = (function(t) return t[1], t[2], t[3] end)(frame.args)
	weap = p._getWeapon(weap)
	return p._getValue(weap, key, atk)
end

---	Main frame invokable function to access any formatted attribute/column/key of a weapon entry.
--	See default table in M:Weapons to see all valid computed attributes.
--	@function		p.getFormattedValue
--	@param			{string} weap Weapon name in EN locale
--	@param			{number} atk Attacks table index
--	@param			{string} k Key name
--	@return			Formatted value associated with k key
function p.getFormattedValue(frame)
	local weap, key, atk = (function(t) return t[1], t[2], t[3] end)(frame.args)
	weap = p._getWeapon(weap)
	return p._getFormattedValue(weap, key, atk)
end

---	Builds a melee weapon gallery as seen on [[Template:MeleeCategory]].
--	@function		p.getMeleeWeaponGallery
--	@param			{table} frame Frame object w/ first argumenting being string meleeClass
--	@returns		{string} Resultant wikitext of gallery
function p.getMeleeWeaponGallery(frame)
	local meleeClass = frame.args[1] or ''
	local result = { "== Armes de type : "..meleeClass.."==", '<gallery widths="200" position="center" spacing="small">' }
	for i, weap in ipairs(p._getMeleeWeapons(meleeClass)) do
		table.insert(result, p._statRead(weap, nil, 'Image')..'|'..p._statFormat(weap, nil, 'Name'))
	end
	table.insert(result, '</gallery>')
	return frame:preprocess(table.concat(result, '\n')) -- annoying that it needs to be preprocessed
end

---	Gets the total count of weapons as used on [[Mastery Rank#Total Mastery]].
--	@function		p.getWeaponCount
--	@param			{table} frame Frame object w/ the first argument being the weaponSlot and the
--						  second argument being a boolean to getFullList
--	@returns		{number} Total count of weapons in a certain category/type
--	@returns		{table} List of weapon names that count for mastery in a particular weapon slot
function p._getWeaponCount(slot)
	slot = slot and slot:lower()
	local data = slot and WeaponData[slot] or WeaponData
	local fullList = {}
	
	for name, weapon in pairs(data) do
		if not weapon._IgnoreInMasteryCount then
			-- TODO: There should be a better way to determine/differentiate if a weapon is a kitgun b/c kitguns and zaws
			-- are stored in the same M:Weapons/data/modular data store; add a new "Kitgun" or "Zaw" Trait and target that?
			if (slot == 'kitgun' and weapon.Slot == 'Secondaire')
				or (slot == 'zaw' and weapon.Slot == 'Mêlée')
				or (slot == 'robotic' and weapon.Slot ~= 'Molosse')
				or (weapon.Slot:lower() == slot)
				or slot == nil then
				fullList[#fullList + 1] = name
			end
		end
	end

	return #fullList, fullList
end

---	Gets the total count of weapons as used on [[Mastery Rank#Total Mastery]].
--	@function		p.getWeaponCount
--	@param			{table} frame Frame object w/ the first argument being the weapon slot
--	@return			{number} Total number of weapons that can reward Mastery XP
function p.getWeaponCount(frame)
	return (p._getWeaponCount(frame.args and frame.args[1] or nil))
end

---	Builds wikitable of all weapons' innate polarities as seen on [[Polarity]].
--	@function		p.getPolarityTable
--	@param			{table} frame Frame object
--	@returns		{string} Wikitext of resultant wikitable
function p.getPolarityTable(frame)
	local colNames = { 'Principale', 'Secondaire', 'Mêlée', 'Arch-Fusil', 'Arch-Mêlée' }
	local cols = {}	-- Will look like: {['Primary']={},['Secondary']={},['Melee']={},['Arch-Gun']={},['Arch-Melee']={},}
	local colOrder = {}	--{cols['Primary'],cols['Secondary'],cols['Melee'],cols['Arch-Gun'],cols['Arch-Melee'],}
	local colCounts = {}

	for i, v in ipairs(colNames) do
		cols[v] = {}
		colOrder[i] = cols[v]
		colCounts[v] = 0
	end

	for _, weapon in pairs(WeaponData) do
		local pols = Table.size(weapon["Polarities"] or {})
		local slot = weapon['Slot']
		if pols > 0 and cols[slot] then
			table.insert(cols[slot], {
				'|'..p._getFormattedValue(weapon, 'NameLink'):gsub(' ?%(.*%)', '')..'||'..p._getFormattedValue(weapon, "Polarities"),
				pols
			})
			colCounts[slot] = colCounts[slot] + 1
		end
	end

	for i, v in ipairs(colNames) do
		colCounts[i] = colCounts[v]
		table.sort(cols[v], function(a, b)return a[2] > b[2] end)
	end

	local result = {[=[
{| style="width: 100%; border-collapse: collapse;" cellpadding="2" border="1"
|+ '''Armes avec une Polarité innée (en ignorant la Posture et l'emplacement Exilus)'''
! colspan="2" |Principales
! colspan="2" |Secondaires
! colspan="2" |Mêlées
! colspan="2" |Arch-Fuils
! colspan="2" |Arch-Mêlées]=]}
	for i = 1, math.max(table.unpack(colCounts)) do --row
		table.insert(result, '|-')
		for _, col in ipairs(colOrder) do --cell
			table.insert(result,(col[i] or {'| ||'})[1])
		end
	end
	table.insert(result, '|}')
	return table.concat(result, '\n')
end

---	Builds a table that lists out all weapons with a certain damage type
--	@function		p.buildDamageTypeTable
--	@param			{table} frame Frame object
--	@returns		{string} Wikitext of resultant wikitable
function p.buildDamageTypeTable(frame)
	local damageType = frame.args and frame.args[1] or frame
	local mostly = frame.args and (frame.args[2] or '') ~= ''

	local content = {}
	for k,weap in pairs(WeaponData) do
		local weapAtk = getWeaponAttack(weap)--could add a loop here
		local portion, biastype, damage = statRead(weapAtk, 'DamageBias')
		local typeDmg = statRead(weapAtk, damageType)
		if damage == 0 then typeDmg = weapAtk[damageType] and 1 or 0 end--modular pieces
		--Filter for
		--a. any of the damage type in any attack - former 'not mostly'
		--b. at least one majority damage type - former 'mostly'
		--c. a majority of the damage type in the display attack - 'mostly'
		--d. any of the damage type in the display attack - 'not mostly'
		if biastype == damageType or not mostly and typeDmg > 0 then
			table.insert(content, ('| %s || %s || %s || %s || %s || data-sort-value="%s" | %s'):format(
				statFormat(weapAtk, 'Name'),
				statRead(weapAtk, 'Slot'),
				statRead(weapAtk, 'Class'),
				statRead(weapAtk, 'AttackName'),
				typeDmg,
				portion, statFormat(weapAtk, 'DamageBias')
			))
		end
	end
	table.sort(content)--will sort by tooltip span key

	return ([[
{| class = "listtable sortable" style="margin:auto;"
|+ '''Armes avec des dégâts %s%s'''
|-
! Nom !! Emplacement !! Classe !! Nom de l'attaque !! data-sort-type="number" | %s !! data-sort-type="number" | Majorité
|-
]]):format(mostly and 'mostly ' or '', damageType, Tooltip.full(damageType, 'DamageTypes'))
	..table.concat(content, '\n|-\n')..'\n|}'
end

--- _isVariant adapter for p._shortLinkList
local function variantOf(weap)
	local full, _, var, base = weap.Name, p._isVariant(weap.Name)
	return var, base, full
end

---	Builds a list of weapons, with variants being next to base weapon name inside parentheses
--	(e.g. {{Weapon|Braton}} ({{Weapon|MK1-Braton|MK1}}, {{Weapon|Braton Prime|Prime}})).
--	@function		p._shortLinkList
--	@param			{table} Weapon Weapon table
--	@param			{boolean} tooltip If true, adds weapon tooltips, false otherwise; defaults to false
--	@returns		{string} Wikitext of resultant list
function p._shortLinkList(Weapons, tooltip)
	return StatObject.shortLinkList(Weapons, variantOf, tooltip and 'Weapons')
end

---	Builds a list of weapons' mastery requirements as seen on [[Template:EquipmentUnlock]],
--	[[Template:EquipmentUnlock/Primary]], [[Template:EquipmentUnlock/Secondary]], 
--	[[Template:EquipmentUnlock/Melee]], etc.
--	@function		p.getMasteryShortList
--	@param			{table} frame Frame object w/ first argument being a string weaponSlot
--	@returns		{string} Wikitext of resultant list
function p.getMasteryShortList(frame)
	local weaponSlot = frame.args[1]
	local masteryRank = tonumber(frame.args[2])
	local weapArray = p._getWeapons(function(x)
		return x.Slot == weaponSlot and x.Mastery == masteryRank
	end)
	return table.concat(StatObject.shortLinkList(weapArray, variantOf, 'Weapons'), ' • ')
end

function p.fullList()
	return table.concat(StatObject.shortLinkList(WeaponData, variantOf, 'Weapons'), ' • ')
end

---	Builds a list of PvP weapons as seen on [[PvP#Limitations]].
--	@function		p.getConclaveList
--	@param			{table} frame Frame object w/ first argument being a string weaponSlot
--	@returns		{string} Wikitext of resultant list
function p.getConclaveList(frame)
	local weaponSlot = frame.args[1] or 'All'
	local weapArray = p._getWeapons(function(weap)
		return weap.Conclave == true
	end, weaponSlot, {pvp=true})
	return '*'..table.concat(StatObject.shortLinkList(weapArray, variantOf), '\n* ')
end

---	Builds a disposition wikitable as seen on [[Riven Mods/Weapon Dispos]].
--	@function		p.getRivenDispositionTable
--	@param			{table} frame Frame object w/ first argument being a string weaponSlot
--	@returns		{string} Wikitext of resultant wikitable
function p.getRivenDispositionTable(frame)
	local weaponSlot = frame.args[1]
	local result = {
		'{| class="article-table" border="0" cellpadding="1" cellspacing="1" style="width: 100%"',
		'|-',
		{'[[a| '},	-- Wikitable header row
		'|-'
	}

	-- local ranges = {'○○○○○', '●○○○○', '●●○○○', '●●●○○', '●●●●○', '●●●●●'}
	local dispo = {}

	for k, weapon in pairs(WeaponData) do
		if weapon['Disposition'] and (weaponSlot == 'All' or weapon['Slot'] == weaponSlot) then
			local disp = p._statFormat(weapon, nil, 'Dispo')
			dispo[disp] = dispo[disp] or {}
			table.insert(dispo[disp], weapon)
		end
	end

	for str, dis in Table.skpairs(dispo) do
		table.sort(dis, function(a, b) return a['Disposition'] > b['Disposition'] end)
		local col = { '| style="vertical-align:top; font-size:small" |' }
		for _, weap in ipairs(dis) do
			table.insert(col, p._statFormat(weap, nil, 'NameLink')..' ('..weap['Disposition']..')')
		end
		table.insert(result[3], str)
		table.insert(result, table.concat(col, '\n* '))
	end

	result[3] = table.concat(result[3], ']]\n! scope="col" style="text-align:center;"|[[Mods Riven#Disposition|')..']]'
	table.insert(result, '|}')
	return table.concat(result, '\n')
end

--- Builds a simple div in the Weapon part of the abilities if an Exalted Weapon is existing.
--  @function		p.buildAbilityWeaponTab
--  @return			<table> with weapLink, main page link, weapImg, introSection, fetched from the main Weapon page
function p.buildAbilityWeaponTab(frame)
    local abilityName = frame.args[1]
    local AbilityWeaponTab = {}

    for _, weap in pairs(WeaponData) do
        if weap.WeaponAugment then
            if abilityName == weap.WeaponAugment then
                if weap.Name ~= "Lame Exaltée Prime" and weap.Name ~= "Lame Exaltée Umbra" then -- escape duplicate entries
                    table.insert(AbilityWeaponTab, ([==[
                    {| id="AbilityWeaponTab" style="width: %s; background-color: #2a2a31; text-align: center; padding: 1rem;"
                    |-
                    | rowspan=2 | [[Fichier:%s|200px]] || [[%s]]
                    |-
                    | %s
                    |}
                    ]==]):format(
                        '100%', -- escape auto-format
                        weap.Image or 'Panel.png',
                        weap.Link or 'Lien non trouvé.[[Catégorie:AbilityWeaponTab error]]',
                        frame:expandTemplate{ title = 'fetchSection', args = {weap.Link, 'intro'} } or 'Aucune description trouvée sur la page.[[Catégorie:AbilityWeaponTab error]]'
                    ))
                end
            end
        end
    end

    -- close and return table
    return table.concat(AbilityWeaponTab, "")
end

function p.getSignatureWeaponsTable(frame)
    local signWpsTable = mw.html.create("table"):addClass("bigmodtable sortable"):css("width", "100%") -- main Table

    -- build main Table headings
    signWpsTable:tag("tr")
        :tag("th"):wikitext('Warframe/Compagnon'):done()
        :tag("th"):attr("data-sort-type", "text"):wikitext('Arme(s)'):done()
        :tag("th"):addClass("unsortable"):wikitext('Synergie'):done()
    :done()

    for key, weaps in pairs(WeaponData) do
        if weaps.Traits then
            for x, traits in pairs(weaps.Traits) do
                if traits == "Signature" then
                    if weaps.SignatureTags and weaps.SignatureDesc then
                        local WFAndCompanions = {}
                        -- Ajouter les éléments de weaps.SignatureTags.Warframes
                        if weaps.SignatureTags.Warframes then
                            for _, wfs in pairs(weaps.SignatureTags.Warframes) do
                                table.insert(WFAndCompanions, Tooltip.full(wfs, "Warframes"))
                            end
                        end
                        -- Ajouter les éléments de weaps.SignatureTags.Companions
                        if weaps.SignatureTags.Companions then
                            for _, comps in pairs(weaps.SignatureTags.Companions) do
                                table.insert(WFAndCompanions, Tooltip.full(comps, "Companions"))
                            end
                        end

                        -- Afficher les informations
                        signWpsTable:tag("tr")
                            :tag("td"):wikitext(table.concat(WFAndCompanions, '<br/>')):done()
                            :tag("td"):wikitext(Tooltip.full(weaps.Name, "Weapons")):done()
                            :tag("td"):wikitext(weaps.SignatureDesc):done()
                        :done()
                    end
                end
            end
        end
    end

    signWpsTable:allDone()

    return frame:preprocess(tostring(signWpsTable))
end

return p