FANDOM


--This will eventually contain data for various weapons
--For use in automating assorted wiki functions and to reduce the number of updates needed
-- •
 
local p = {}
 
local WeaponData = mw.loadData( 'Module:Weapons/data' )
local ConclaveData = mw.loadData( 'Module:Weapons/Conclave/data' )
local Icon = require( "Module:Icon" )
local Shared = require( "Module:Shared" )
local Mod = require('Module:Mods')
local Tooltip = require( "Module:Tooltip" )
 
local Elements = {"Impact", "Perforation", "Tranchant", "Feu", "Glace", "Poison", "Électrique", "Explosif", "Corrosif", "Radiation", "Magnétique", "Gaz", "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"}
 
function p.doPlural(Text, Value)
  if(tonumber(Value) == 1) then
    Text = string.gsub(Text, "(<.+>)", "")
  else
    Text = string.gsub(Text, "<(.+)>", "%1")
  end
  return Text
end
 
function p.isVariant(WeapName)
  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
 
function p.buildName(BaseName, Variant)
  if(Variant == nil or Variant == 'Base' or Variant == '') then
    return BaseName
  elseif(Variant == 'Prime' or Variant == 'Wraith' or Variant == 'Vandal') then
    return BaseName..' '..Variant
  elseif(Variant == "MK1") then
    return "MK1-"..BaseName
  else
    return Variant..' '..BaseName
  end
end
 
--It's a bit of a mess, but this is for compressing a list with variants
--So if a list has Braton, Braton Prime, and MK1-Braton it'll list as
--                  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 = '[['..baseName.."]]"
      --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..'[['..p.buildName(baseName, varName)..'|'..varName..']]'
            count = count + 1
          end
        end
        thisRow = thisRow..')'
      end
      table.insert(result, thisRow)
    else
      for i, varName in pairs(variants) do
        table.insert(result, '[['..p.buildName(baseName, varName)..']]')
      end
    end
  end
  return result
end
 
function p.getWeapon(WeapName)
  local weapon = WeaponData["Weapons"][WeapName]
 
  if weapon ~= nil and weapon.Name == WeapName then
    return weapon
  else
    for key, Weapon in Shared.skpairs(WeaponData["Weapons"]) do
      if(Weapon.Name == WeapName or key == WeapName) then
        return Weapon
      end
    end
  end
 
  return nil
end
 
local function checkWeapon(weap, weapName, conclave)
  if weap == nil or type(weap) ~= 'table' then
    if conclave then
      return p.getConclaveWeapon(weapName)
    else
      return p.getWeapon(weapName)
    end
  elseif type(weap) == 'table' then
    return weap
  end
end
 
function p.weaponExists(frame)
  local weapName = frame.args ~= nil and frame.args[1] or nil
 
  return p._weaponExists(weapName)
end
 
function p._weaponExists(weapName, weap)
  local weap = checkWeapon(weap, weapName)
 
  if weapName == 'Épées Versatiles Sombres' then
    return true
  elseif weap then
    return true
  end
 
  if weapName == nil then
    return 'Enter weapon name.'
  elseif weap == nil then
    return 'No weapon '..weapName..' found.'
  end
 
 
end
 
function p.getLink(frame)
  local weapName = frame.args ~= nil and frame.args[1] or nil
  if weapName == nil then
    return 'Enter weapon name.'
  end
 
  return p._getLink(weapName)
end
 
function p._getLink(weapName, weap)
  local weap = checkWeapon(weap, weapName)
  local exists = p._weaponExists(weapName, weap)
  if weapName == "Épées Versatiles Sombres" then
    return "Épées Versatiles Sombres"
  elseif exists == true then
    local temp = weap.Link
    if weap.Type == "Arch-Fusil (Atmosphère)" then
      local atmoTemp = Shared.splitString(weap.Name, "%s")
      local atmoCount = Shared.tableCount(atmoTemp)
      if atmoCount == 2 then
        return atmoTemp[1]
      elseif atmoCount >= 2 then
        return table.concat(atmoTemp, ' ', 1, (atmoCount-1))
      end
      return weap.Name
    elseif temp then
      return temp
    else
      return weap.Name
    end
  elseif weapName == nil then
    return false
  else
    return false
  end
end
 
function p.getConclaveWeapon(WeapName)
  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
 
function p.isMelee(frame)
  if(frame == nil) then
    return nil
  end
  local Weapon = frame.args ~= nil and frame.args[1] or frame
  if(type(Weapon) == "string") then
    Weapon = p.getWeapon(Weapon)
  end
 
  if(Weapon == nil) then
    return nil
  end
 
  if(Weapon.Type ~= nil and (Weapon.Type == "Mêlée" or Weapon.Type == "Arch-Mêlée")) then
    return "yes"
  end
 
  return nil
end
 
function p.isArchwing(frame)
  if(frame == nil) then
    return nil
  end
  local Weapon = frame.args ~= nil and frame.args[1] or frame
  if(type(Weapon) == "string") then
    Weapon = p.getWeapon(Weapon)
  end
 
  if(Weapon == nil) then
    return nil
  end
 
  if(Weapon.Type ~= nil and (Weapon.Type == "Arch-Fusil" or Weapon.Type == "Arch-Mêlée")) then
    return "yes"
  end
 
  return nil
end
 
local function getAttack(Weapon, AttackType)
  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
 
local function hasAttack(Weapon, AttackType)
  if(getAttack(Weapon, AttackType) ~= nil) then
    return true
  else
    return nil
  end
end
 
local function dontHasAttack(Weapon, AttackType)
  if(getAttack(Weapon, AttackType) ~= nil) then
    return nil
  else
    return true
  end
end
 
 
function p.hasAttack(frame)
  local WeapName = frame.args[1]
  local AttackName = frame.args[2]
 
  if(WeapName == nil) then
    return "ERREUR: Il n'y a pas le nom de l'arme"
  elseif(AttackName == nil) then
    AttackName = "Normal"
  end
 
  local Weapon = p.getWeapon(WeapName)
  if(Weapon == nil) then return "ERREUR: L'arme  "..WeapName.." n'a pas été trouvé" end
 
  return hasAttack(Weapon, AttackName)
end
 
function hasMultipleTypes(Attack)
  local typeCount = 0
  if(Attack ~= nil and Attack.Damage ~= nil) then
    for key, dmg in Shared.skpairs(Attack.Damage) do
      if(dmg > 0) then
        typeCount = typeCount + 1
      end
    end
  end
  if(typeCount > 1) then return "yes" else return nil end
end
 
function p.hasMultipleTypes(frame)
  local WeapName = frame.args[1]
  local AttackName = frame.args[2]
  if(AttackName == nil) then AttackName = "Normal" end
  local attack = getAttack(WeapName, AttackName)
  return hasMultipleTypes(attack)
end
 
function p.attackLoop(Weapon)
  if(Weapon == nil) then
    return function() return nil end
  end
  local aType = "Normal"
  local iterator = function()
    if(aType == "Normal") then
      local attack = getAttack(Weapon, aType)
      aType = "Charge"
      if attack ~= nil and attack.Damage ~= nil then return "Normal", attack end
    end
    if(aType == "Charge") then
      local attack = getAttack(Weapon, aType)
      aType = "Area"
      if attack ~= nil and attack.Damage ~= nil then return "Charge", attack end
    end
    if(aType == "Area") then
      local attack = getAttack(Weapon, aType)
      aType = "SecondaryArea"
      if attack ~= nil and attack.Damage ~= nil then return "Area", attack 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
 
    return nil
  end
  return iterator
end
 
 
 
local function getFamily(FamilyName)
  local familyMembers = {}
  for i, Weapon in Shared.skpairs(WeaponData["Weapons"]) do
    if(Weapon.Family == FamilyName) then
      table.insert(familyMembers, Weapon)
    end
  end
  return familyMembers
end
 
local function linkMeleeClass(weapClass)
  if(weapClass == nil) then
    return nil
  else
    return "[[:Category:"..weapClass.."|"..weapClass.."]]"
  end
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
local function getMeleeWeapons(weapClass, PvP)
  local weaps = {}
  local weapClasses = {}
  if(weapClass ~= nil) then
    weapClasses = Shared.splitString(weapClass, ',')
  end
 
  for i, weap in Shared.skpairs(WeaponData["Weapons"]) do
    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
 
--As above, but for Conclave stats
local function getConclaveMeleeWeapons(weapClass, PvP)
  local weaps = {}
  local weapClasses = {}
  if(weapClass ~= nil) then
    weapClasses = Shared.splitString(weapClass, ',')
  end
 
  for i, weap in Shared.skpairs(ConclaveData["Weapons"]) do
    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
 
--Learning new things... Trying to allow sending in an arbitrary function
function p.getWeapons(validateFunction)
  local weaps = {}
  for i, weap in Shared.skpairs(WeaponData["Weapons"]) do
    if((weap.Ignore == nil or not weap.Ignore) and validateFunction(weap)) then
      table.insert(weaps, weap)
    end
  end
  return weaps
end
 
--Same as getWeapons, but for Conclave data
function p.getConclaveWeapons(validateFunction)
  local weaps = {}
  for i, weap in Shared.skpairs(ConclaveData["Weapons"]) do
    if((weap.Ignore == nil or not weap.Ignore) and validateFunction(weap)) then
      table.insert(weaps, weap)
    end
  end
  return weaps
end
 
local function asMultiplier(val)
  if(val == nil) then
    return "1.0x"
  end
  return Shared.round(val, 2, 1).."x"
end
 
local function HasTrait(Weapon, Trait)
  if(Trait == nil or Weapon.Traits == nil) then
    return false
  end
 
  for i, theTrait in pairs(Weapon.Traits) do
    if(theTrait == Trait) then
      return true
    end
  end
 
  return false
end
 
--If Type is not nil, get damage weapon deals of that type
--If it deals no damage of that type, return 0 instead of nil
--It Type is nil, return total damage
local function GetDamage(Attack, Type, ByPellet)
  if(ByPellet == nil) then ByPellet = false end
  if(Attack == nil or Attack.Damage == nil) then return 0 end
 
  local pCount = 1
  if(ByPellet and Attack.PelletCount ~= nil) then pCount = Attack.PelletCount end
  if(Type == nil) then
    local total = 0
    for i, d in Shared.skpairs(Attack.Damage) do
      total = total + d
    end
    return total / pCount
  else
    if(Type == "Physical") then
      local Impact = Attack.Damage["Impact"] ~= nil and Attack.Damage["Impact"] or 0
      local Puncture = Attack.Damage["Perforation"] ~= nil and Attack.Damage["Perforation"] or 0
      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
 
--Returns the damage string as it's formatted in a comparison row
--So instead of '0', returns '-', and appends the icon for an element if necessary
local function GetDamageString(Attack, Type, ByPellet)
  if(ByPellet == nil) then ByPellet = false end
  if(Attack == nil or Attack.Damage == nil) then return "" end
 
  local pCount = 1
  if(ByPellet and Attack.PelletCount ~= nil) then pCount = Attack.PelletCount end
  if(Type == nil) then
    if(not hasMultipleTypes(Attack)) then
      for key, val in pairs(Attack.Damage) do
        if(val > 0) then
          return Icon._Proc(key).." "..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
 
local function GetDamageBias(Attack, IncludeSingle)
  if(IncludeSingle == nil) then IncludeSingle = false end
 
  if(Attack.Damage ~= nil and Shared.tableCount(Attack.Damage) > 0) then
    local total = 0
    local bestDmg = 0
    local bestElement = nil
    local count = 0
    for Element, Dmg in pairs(Attack.Damage) do
      if(Dmg > bestDmg) then
        bestDmg = Dmg
        bestElement = Element
      end
      total = total + Dmg
      if(Dmg > 0) then
        count = count + 1
      end
    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
 
--If the attack has at least two damage types,
--  Returns something like "58.3% Slash"
--If it doesn't, returns nil
local function GetDamageBiasString(Attack, HideType, ShowText, IncludeSingle, Color)
  if(HideType == nil) then HideType = false end
  if(ShowText == nil) then ShowText = "" end
  if(IncludeSingle == nil) then IncludeSingle = false end
 
  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
 
function p.GetPolarityString(Weapon)
  if(Weapon.Polarities == nil or type(Weapon.Polarities) ~= "table" or Shared.tableCount(Weapon.Polarities) == 0) then
    return "Aucune"
  else
    local resString = ""
    for i, pol in pairs(Weapon.Polarities) do
      local tempPol = Icon._Pol(pol)
      resString = resString..tempPol
    end
    --Adding Exceptions
    if(Weapon.Name == "Lame Exaltée") then
      resString = resString.."(Vanilla/Prime)<br/>"..Icon._Pol("Umbra")..Icon._Pol("Umbra").."(Umbra)"
    end
 
 
    return resString
  end
end
 
local function getWeaponStanceList(Weapon)
  if(Weapon == nil or Weapon.Type ~= "Mêlée") then return nil end
 
  local stances = Mod.getStances(Weapon.Class, Weapon.Conclave, Weapon.Name)
 
  local result = ""
 
  for i, stance in pairs(stances) do
    if (string.len(result) > 0) then
      result = result.."<br/>"
    end
 
    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
 
local function getAttackValue(Weapon, Attack, ValName, giveDefault, asString, forTable)
  if(giveDefault == nil) then giveDefault = false end
  if(asString == nil) then asString = false end
  if(forTable == nil) then forTable = false end
  if(Attack == nil) then
    if(asString) then
      return ""
    else
      return nil
    end
  end
  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
 
local function getValue(Weapon, ValName, giveDefault, asString, forTable)
  if(giveDefault == nil) then giveDefault = false end
  if(asString == nil) then asString = false end
  if(forTable == nil) then forTable = false end
 
  if(type(ValName) == "table") then
    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.getAugments(Weapon)
    if(Shared.tableCount(augments) > 0) then
      local AugString = ""
      for i, Aug in pairs(augments) do
        if(i>1) then AugString = AugString.."<br/>" end
        AugString = AugString..Tooltip._tooltipText(Aug.Name, 'Mod')
      end
      return AugString
    else
      return nil
    end
  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 == "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
 
function p.getValue(frame)
  local WeapName = frame.args[1]
  local ValName1 = frame.args[2]
  local ValName2 = frame.args[3]
  local AsString = frame.args["Raw"] == nil
  if(WeapName == nil) then
    return ""
  elseif(ValName1 == nil) then
    return "ERREUR : Pas de valeur spécifiée"
  end
 
  local theWeap = p.getWeapon(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, AsString)
end
 
function p.getConclaveValue(frame)
  local WeapName = frame.args[1]
  local ValName1 = frame.args[2]
  local ValName2 = frame.args[3]
  if(WeapName == nil) then
    return ""
  elseif(ValName1 == nil) then
    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
 
local function buildInfoboxRow(Label, Text, Collapse, Digits, Addon)
  local result = ""
 
  if(Collapse == nil) then
    Collapse = false
  elseif(Collapse == 1) then
    Collapse = true
  end
  if(Text ~= nil) then
    result = '<div style="margin-left:-3px;" '
    if(Collapse) then
      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
 
--Returns a list of weapons for a stance
--Adds a ✓ if the weapon matches the polarity
function p.getStanceWeaponList(frame)
 
  local StanceName = frame.args ~= nil and frame.args[1] or frame
  local Stance = Mod.getStance(StanceName)
 
  if(Stance == nil) then
    return "ERREUR : "..StanceName.." introuvable"
  end
 
  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
 
--Returns a list of stances for a weapon
--Adds the polarity for each stance
function p.getWeaponStanceList(frame)
  local WeaponName = frame.args ~= nil and frame.args[1] or frame
  local Weapon = p.getWeapon(WeaponName)
 
  if(Weapon == nil) then
    return "ERROR: "..WeaponName.." not found"
  end
 
  return getWeaponStanceList(Weapon)
end
 
local function getWeaponGallery(Weapons)
  local result = '{| style="margin:auto;text-align:center;"'
  local nameRow = ''
  for i, Weapon in pairs(Weapons) do
    local theImage = Weapon.Image ~= nil and Weapon.Image or "Panel.png"
    local weapLink = p._getLink(Weapon.Name)
    if((i - 1) % 4 == 0) then
      result = result..nameRow..'\n|-'
      nameRow = '\n|-'
    end
    result = result..'\n| style="width:165px" |[[File:'..theImage..'|150px|link='..Weapon.Name..']]'
    nameRow = nameRow..'\n| style="vertical-align: text-top;" |[['..weapLink..'|'..Weapon.Name..']]'
  end
  result = result..nameRow
  result = result..'\n|}'
  return result
end
 
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
 
function p.getWeaponCount(frame)
  local Type = frame.args ~= nil and frame.args[1] or frame
  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
 
function getSecondaryCategory(weapon)
  local class = getValue(weapon, "Class", true)
  if(class == "Jet") then
    return "Jet"
  elseif(class == "Doubles Fusils à Pompe" or class == "Fusil à Pompe de poing") then
    return "Fusil à Pompe"
  else
    local trigger = getValue(weapon, "Trigger", true)
    if(trigger == "Semi-Auto" or trigger == "Rafale") then
      return "Semi-Auto"
    elseif(trigger == "Auto" or trigger == "Auto à Cadence Croissante") then
      return "Auto"
    end
  end
  return "Other"
end
 
function getPrimaryCategory(weapon)
  local class = getValue(weapon, "Class", true)
  if(class == "Fusil à Pompe") then
    return "Fusil à Pompe"
  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 à Cadence Croissante") then
      return "Auto"
    end
  end
  return "Other"
end
 
function p.getPolarityTable()
  local tHeader = ""
  tHeader = tHeader..'\n{| style="width: 100%; border-collapse: collapse; cellpadding: 2" border="1"'
  tHeader = tHeader..'\n! colspan="2" |Armes Principale'
  tHeader = tHeader..'\n! colspan="2" |Armes Secondaire'
  tHeader = tHeader..'\n! colspan="2" |Armes Mêlée'
  local tRows = ""
 
  local Melees = p.getWeapons(function(x) return p.isMelee(x) and Shared.tableCount(getValue(x, "Polarities", true)) > 0 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 maxLen = ACount
  local BCount = Shared.tableCount(Pistols)
  if(BCount > maxLen) then
    maxLen = BCount
  end
  local CCount = Shared.tableCount(Primaries)
  if(CCount > maxLen) then
    maxLen = CCount
  end
 
  for i=1, maxLen, 1 do
    tRows = tRows.."\n|-"
    if(i <= CCount) then
      tRows = tRows.."\n|[["..Primaries[i].Name.."]]||"..p.GetPolarityString(Primaries[i])
    else
      tRows = tRows.."\n| ||"
    end
    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
 
function buildCompareString(Val1, Val2, ValName, Digits, Addon, Words, Start, Middle)
  if(Val1 == nil or Val2 == nil) then
    return ""
  end
  local V1Str = Val1
  local V2Str = Val2
  if(Digits ~= nil) then
    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 ""
 
  if(Val1 > Val2) then
    return start.." "..bigWord.." "..ValName.." "..bigMiddle.." ("..V1Str.." vs. "..V2Str..")"
  elseif(Val2 > Val1) then
    return start.." "..smallWord.." "..ValName.." "..smallMiddle.." ("..V1Str.." vs. "..V2Str..")"
  else
    return ""
  end
end
 
function buildGunComparisonString(Weapon1, Weapon2, Conclave)
  local result = ""
  if(Conclave) then
    result = "* "..Weapon1.Name..", comparé au [[Conclave:"..Weapon2.Name.."|"..Weapon2.Name.."]]:"
  else
    result = "* "..Weapon1.Name..", comparé au [["..Weapon2.Name.."]]:"
  end
 
  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), "secondary mode [[Chance de Coup Critique]]", 2, "%")
    end
    result = result..buildCompareString(Att1.CritMultiplier, Att2.CritMultiplier, "secondary mode [[Multiplicateur de Critique]]", 2, "x")
    if(Att1.StatusChance ~= nil and Att2.StatusChance ~= nil) then
      result = result..buildCompareString((Att1.StatusChance * 100), (Att2.StatusChance * 100), "secondary mode [[Chance de Statut]]", 2, "%")
    end
    result = result..buildCompareString(Att1.FireRate, Att2.FireRate, "secondary mode [[Cadence de Tir]]", 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), "primary [[Chance de Coup Critique]]", 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, "primary [[Multiplicateur de Critique]]", 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), "primary [[Chance de Statut]]", 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, "spool-up", 0, " balle<s>", {"Slower", "Faster"})
  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
 
  result = result..buildCompareString(Weapon1.Mastery, Weapon2.Mastery, "[[Rang de Maîtrise]] requis", 0, nil, {"", ""}, nil, {"supérieur", "inférieur"})
  return result
end
 
function buildMeleeComparisonString(Weapon1, Weapon2, Conclave)
  local result = ""
  if(Conclave) then
    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
 
function p.buildComparison(frame)
  local WeapName1 = frame.args[1]
  local WeapName2 = frame.args[2]
 
  if(WeapName1 == nil or WeapName2 == nil) then
    return '<span style="color:red">ERROR: Must compare two weapons</span>'
  end
 
  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
 
function p.buildConclaveComparison(frame)
  local WeapName1 = frame.args[1]
  local WeapName2 = frame.args[2]
 
  if(WeapName1 == nil or WeapName2 == nil) then
    return '<span style="color:red">ERROR: Must compare two weapons</span>'
  end
 
  local Weapon1 = p.getConclaveWeapon(WeapName1)
  if(Weapon1 == nil) then
    return '<span style="color:red">ERROR: Could not find '..WeapName1..'</span>'
  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
 
function p.buildFamilyComparison(frame)
  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 '..WeapName1..'</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
 
function p.buildAutoboxCategories(frame)
  local WeapName = frame.args ~= nil and frame.args[1] or frame
  local Weapon = p.getWeapon(WeapName)
  local result = "[[Category:Armes]]"
  if(Weapon == nil) then
    return ""
  elseif(Weapon.IgnoreCategories ~= nil and Weapon.IgnoreCategories) then
    return ""
  end
  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.getAugments(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 noNil(val, default)
  if(val ~= nil) then
    return val
  else
    if(default ~= nil) then
      return default
    else
      return ""
    end
  end
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.."|[["..Weapon.Name.."]]||"..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
    if(string.len(result) > 0) then result = result.." • " end
    result = result..pair
  end
  return 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
 
function p.getRivenDispositionTable(frame)
  local WeaponType = frame.args[1]
 
  local result = '{| class="article-table" border="0" cellpadding="1" cellspacing="1" style="width: 100%"'
  result = result..'\n|-'
  for i = 5, 1, -1 do
    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|}'
 
  return result
end
 
function p.getConclaveList(frame)
  local WeaponType = frame.args[1]
 
  local weapArray = p.getWeapons(function(x)
    if(x.Type ~= nil and x.Conclave ~= nil) then
      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
 
--Runs a bunch of things to quickly check if anything broke
function p.checkForBugs(frame)
  return p.buildComparison({args = {"Lato", "Lato Prime"}})..p.buildComparison({args = {"Glaive", "Glaive Prime"}})..p.getMeleeComparisonTable({})..p.getSecondaryComparisonTable({})
end
 
function p.checkElements(frame)
  local result = "wyrd"
  for key, theWeap in Shared.skpairs(WeaponData["Weapons"]) do
    for attName, Attack in p.attackLoop(theWeap) do
      local elementCount = 0
      if(Attack.Damage ~= nil) then
        for element, dmg in Shared.skpairs(Attack.Damage) do
          if(not Shared.contains(Physical, element)) then
            elementCount = elementCount + 1
          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
  return result
end
 
function p.checkForMissingData(frame)
  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
 
 
function p.buildTunaWeaponTable(frame)
  local Category = frame.args ~= nil and frame.args[1] or frame
  local Weapons = p.getWeapons( function(x)
    return getValue(x, "Type", true) == Category
  end)
 
 
  local result = '{| style="margin:auto;text-align:center;"'
  local i = 0
  for key, Weapon in Shared.skpairs(Weapons) do
    i = i + 1
    local theImage = Weapon.Image ~= nil and Weapon.Image or "Panel.png"
    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
 
-- and we are back... new table building functions !
 
local function BuildCompRow(Head, Weapon, UseCompDisplay)
  --User:Falterfire 6/12/18 - Adding new Comparison Display functionality
  --                          Toggled with a variable, which is false if not specified
  if UseCompDisplay == nil then UseCompDisplay = false end
  local styleString = ""--"border: 1px solid lightgray;"
  local td = ''
  local result = ''
  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
              rowText = rowText..td..'data-sort-value="'..attDmg..'" style="'..styleString..'"|'..attDmg
            end
          else
            if(getValue(Weapon, ValName) ~= nil) then
              --Add a data sort value if requested
              if(Hline[2]) then
                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
            else
              rowText = rowText..td..'style = "'..styleString..'"|'.."N/A"
            end
          end
        end
 
        result = result..rowText
      end
    end
  end
 
  return result
end
 
local function BuildCompTable(Head, Weapons, UseCompDisplay)
  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
    rowStr = BuildCompRow(Head, Weap, UseCompDisplay)
    tRows = tRows..rowStr
  end
  --    mw.log(tRows)
  return  tHeader..tRows.."\n|}"--[[Category:Automatic Comparison Table]]
end
 
function p.getCompTableGuns(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
    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)
  else return "\n Error : Wrong Class (use Principale, Secondaire, or Robotique) [[Category:Invalid Comp Table]]"
  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 Coup 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, WeapArray, 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
    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
    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
 
  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)
  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 filterWeaponsArray_Melee(subCat, weapArray)
 
  local ret = {}
  for _, weapon in ipairs(weapArray) do
    if(weapon.Class == subCat) then
      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
  end
  return ret
end
 
local function printFamily(familyName, familyValues, conclave)
 
  local ret = Tooltip._tooltipText(familyName, 'Weapon', nil, conclave, nil)
  local first = true
  for _, wpName in ipairs(familyValues) do
    if(wpName ~= familyName) then
      local newName = Shared.trim(wpName:gsub(familyName, ""))
      --Special case
      if(newName == "MK1-") then
        newName = "MK1"
      end
      if(first) then
        first = false
        ret = ret.." ("..Tooltip._tooltipText(wpName, 'Weapon', newName, conclave, nil)
      else
        ret = ret.." • "..Tooltip._tooltipText(wpName, 'Weapon', newName, conclave, nil)
      end
    end
  end
  if(not first) then
    ret = ret..")"
  end
  return 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 conclave = frame.args ~= nil and frame.args[3]
  conclave = conclave ~= nil and conclave ~= "false"
 
  return p._getWeaponsList(mainCat, subCat, conclave)
end
 
function p._getWeaponsList(mainCat, subCat, conclave)
 
  local weapArray = nil
  --Get array of mainCat weapons
  if(conclave) then
    weapArray = p.getConclaveWeapons(function(x) return x.Type == mainCat end)
  else
    weapArray = p.getWeapons(function(x) return x.Type == mainCat end)
  end
  --Filter for second cat
  local weapMap = {}
  if(mainCat == "Mêlée") then
    weapMap = filterWeaponsArray_Melee(subCat, weapArray)
  end
  --Build String for result array
  local first = true
  local ret = nil
  for family, wpList in Shared.skpairs(weapMap) do
    if(first) then
      ret = printFamily(family, wpList, conclave)
      first = false
    else
      ret = ret.." • "..printFamily(family, wpList, conclave)
    end
  end
 
  return ret
end
 
return p
Sauf mention contraire, le contenu de la communauté est disponible sous licence CC-BY-SA .