(Documentation done) |
(Removing getAugments() since it is only used to determine whether or not to add Category:Augmented Weapons which is unused ever since we migrated weapon infobox builders to M:Weapons/infobox; the category itself is likely unused since most people could just look through T:AugmentedMods) |
||
Line 709: | Line 709: | ||
end |
end |
||
return nextAttack, Weapon |
return nextAttack, Weapon |
||
− | end |
||
− | |||
− | -- TODO: Not sure if we will use this in the future or not since augment mod data being |
||
− | -- stored in M:Weapons/data is not really appropriate |
||
− | local function getAugments(Weapon) |
||
− | local name = Weapon.Name ~= nil and Weapon.Name or Weapon |
||
− | local augments = {} |
||
− | for i, Augment in pairs(WeaponData["Augments"]) do |
||
− | for j, WeapName in pairs(Augment.Weapons) do |
||
− | if (WeapName == name) then |
||
− | table.insert(augments, Augment) |
||
− | end |
||
− | end |
||
− | end |
||
− | return augments |
||
end |
end |
||
Line 1,378: | Line 1,363: | ||
end |
end |
||
table.insert(result, CATEGORY_MAP[Weapon['Class']]..CATEGORY_MAP[Weapon['Trigger']]..CATEGORY_MAP[Weapon['Type']]) |
table.insert(result, CATEGORY_MAP[Weapon['Class']]..CATEGORY_MAP[Weapon['Trigger']]..CATEGORY_MAP[Weapon['Type']]) |
||
− | |||
− | -- TODO: Archive this category b/c not sure how important it is or how often |
||
− | -- it is used; augments are moved out of M:Weapons/data and should use M:Mods/data |
||
− | local augments = getAugments(Weapon) |
||
− | if (Table.size(augments) > 0) then |
||
− | table.insert(result, "[[Category:Augmented Weapons]]") |
||
− | end |
||
-- Adding appropriate categories to page based on weapon's categorical traits |
-- Adding appropriate categories to page based on weapon's categorical traits |
Revision as of 06:40, 31 July 2021
Weapons contains all of WARFRAME's non-modular weapon data.
Usage
Template
In template and articles: {{#invoke:Weapons|function|input1|input2|...}}
Quick navigation to submodules:
- Module:Weapons/Conclave/data - data store for Conclave-specific weapon stats
- Module:Weapons/characteristics - for generating advantage/disadvantage text under Characteristic sections of weapon articles based on stored data in Module:Weapons/data
- Module:Weapons/compare - for generating text for side-by-side comparison between two weapons
- Module:Weapons/comptable
- Module:Weapons/csv - for generating CSV output of a subset of weapon stats as stored in Module:Weapons/data
- Module:Weapons/data - main submodule for weapon data store
- Module:Weapons/data/credits - in-progress data store for mapping weapons to their development credits
- Module:Weapons/data/dev - sandbox page for data store
- Module:Weapons/data/validate - data validation scripts
- Module:Weapons/dev - sandbox page
- Module:Weapons/infobox - builds weapon infoboxes
- Module:Weapons/nav - builds weapon navigation box as seen at the bottom of weapon articles
- Module:Weapons/ppdata - preprocessed weapon data containing statistical
- Module:Weapons/preprocess - script for seeding ppdata
- Module:Weapons/ppdata/seeder - archived script for seeding ppdata
- Module:Weapons/testcases - unit test suite for Module:Weapons
Product Backlog
Name | Type | Status | Priority | Assignee | Description | Date Issued | Last Update |
---|---|---|---|---|---|---|---|
Module:StatObject as OOP paradigm | Dev | Planning | Low |
Currently our usage of Module:StatObject is as a static class with local StatObject = require('Module:StatObject') -- Base class
local WeaponData = require('Module:Weapons/data')
-- Doing some metaprogramming to extend functionality of StatObject class
StatObject.default = {
Name = { nil, 'Weapon Name: %s' }, -- Sample definition for Name field getter/formatter
...
}
local BratonStatObject = StatObject(WeaponData['Braton'])
-- Get raw Name value "Braton" instead of StatObject.statRead(WeaponData['Braton'], 'Name')
local name = BratonStatObject.Name
-- Get formatted Name value "Weapon Name: Braton" (as defined in StatObject.default) instead of StatObject.statFormat(WeaponData['Braton'], 'Name')
print(BratonStatObject.Name)
mw.log(BratonStatObject.Name)
local formattedName = tostring(BratonStatObject.Name)
-- If the above is not possible in Lua then maybe add a __call metamethod to Name key to return its formatted value
formattedName = BratonStatObject.Name()
-- Or add a format() function to instantiated StatObject's metatable, passing in key name as argument
formattedName = BratonStatObject:format('Name')
|
22:01, 5 December 2022 (UTC) | ||
Include attack name/context in Module:Weapons/ppdata | Dev | Planning | Low |
Update Module:Weapons/ppdata/seeder to add attack names associated with the respective stats used for comparing so that Module:Weapons/characteristics can add additional context to the stat comparisons. See https://warframe.fandom.com/wiki/Quassus?commentId=4400000000003635575. For Quassus's case, Jat Kusar has a base 35% crit chance, but since we are comparing against non-normal attacks, Quassus's Ethereal Daggers will have second highest crit chance (30%) behind Tenet Exec's slam shockwaves (38%). |
22:09, 5 August 2022 (UTC) | ||
Weapon and Attack classes | Refactor and Dev | Planning | Low |
|
21:18, 18 January 2022 (UTC) | ||
ExplosionDelay key
|
Refactor | Planning | Low |
|
22:10, 6 January 2022 (UTC) | ||
Reload key
|
Refactor | Planning | Low |
|
|||
Augments in /data
|
Refactor | New | Low |
|
01:37, 31 May 2021 (UTC) | ||
Data validation | Dev/database | Active | Medium |
Create
|
01:37, 31 May 2021 (UTC) | 23:33, 1 August 2021 (UTC) | |
Error handling | Clean up | New | Medium |
Change all return statements with "ERROR" to either
|
01:37, 31 May 2021 (UTC) | ||
Update database schema | Database | Active | Medium | User:Cephalon Scientia |
Reworking how attacks are stored in tables for flexibility. Should have one Attack column that contains multiple tables, each representing a unique attack for that weapon. Would probably improve/simplify Weapon Comparison and Template:WeaponInfoboxAutomatic in displaying multiple attacks of a weapon. Right now we are hacking the use of 23:33, 1 August 2021 (UTC) update: There are lots of changes to these tables as I slowly create validation functions to check what keys-value pairs are needed or not, see documentation in Module:Weapons/data/doc for possible key-value pairs. Right now, attacks are stored in generic 21:18, 18 January 2022 (UTC) update: User:Gigamicro implemented a new 21:35, 19 January 2022 (UTC) update: |
01:50, 31 May 2021 (UTC) | 21:35, 19 January 2022 (UTC) |
Unit tests | Testing | Archived | High | User:Cephalon Scientia |
Add unit tests in Module:Weapons/testcases for each function in Module:Weapons. See Module:Math/testcases for examples and https://dev.fandom.com/wiki/Global_Lua_Modules/Testharness for documentation on how to format tests. 20:29, 31 July 2021 (UTC) update: Do not feel like it is appropriate to add unit tests using Module:TestHarness to most of the functions in this module since they mostly pertain to building wikitext to display to the reader. We can add a Module:Weapons/testcases subpage for visual tests to ensure rendered wikitext is not broken. Otherwise, I think it is more important to validate the data in Module:Weapons/data which are being formatted and displayed to the reader. |
02:01, 31 May 2021 (UTC) | 20:29, 31 July 2021 (UTC) |
Finished Issues
Name | Type | Status | Priority | Assignee | Description | Date Issued | Completion Date |
---|---|---|---|---|---|---|---|
Advantages/disadvantages | Refactor | Completed | Low | User:Cephalon Scientia |
|
06:05, 3 October 2021 (UTC) | 17:47, 2 November 2021 (UTC) |
Railjack Weapons | Dev/Edit/Database | Long-term support | Medium | User:Cephalon Scientia |
02:32, 6 September 2021 (UTC) update: Added most Railjack turrets and ordnances to 00:12, 29 September 2021 (UTC) update: Added Zetki Photor MK II, Zetki Carcinnox MK II, and Zetki Apoc MK I stats according to the Mobile Export. |
22:17, 2 August 2021 (UTC) | 00:12, 29 September 2021 (UTC) |
Update Conclave database schema | Database | Long-term support | Low | User:Cephalon Scientia |
Remove keys that represent PvE stats as they are irrelevant to PvP. Most other key-value pairs (except those in attack tables) are shared with |
06:18, 10 August 2021 (UTC) | 03:10, 16 August 2021 (UTC) |
Clean up | Clean up | Completed | Medium | User:Cephalon Scientia |
|
01:37, 31 May 2021 (UTC) | 06:20, 10 August 2021 (UTC) |
Refactoring | Refactor | Long-term support | Medium | User:Cephalon Scientia |
Refactor these functionalities for code reuse, better performance, better maintainability, and etc.:
|
17:43, 3 June 2021 (UTC) | 00:19, 7 August 2021 (UTC) |
Documentation | Documentation | New | High |
Add LuaDoc-style documentation for all functions. |
01:37, 31 May 2021 (UTC) | 06:35, 31 July 2021 (UTC) | |
Comparison tables and comparing two weapons | Refactor | Completed | High | User:Cephalon Scientia |
Refactor
20:53, 29 July 2021 (UTC) update: We now use |
01:37, 31 May 2021 (UTC) | 20:53, 29 July 2021 (UTC) |
getAttackValue()
|
Refactor | Completed | Medium | User:Cephalon Scientia |
Refactor
20:53, 29 July 2021 (UTC) update: |
01:37, 31 May 2021 (UTC) | 20:53, 29 July 2021 (UTC) |
p.getRivenDispositionTable()
|
Refactor | Completed | Low |
|
3:42, 21 July 2021 (UTC) | 4:49, 21 July 2021 (UTC) | |
p.buildDamageTypeTable(frame)
|
Refactor | Completed | Low |
|
01:37, 31 May 2021 (UTC) | 21:54, 20 July 2021 (UTC) | |
p.buildAutoboxCategories(frame)
|
Refactor | Completed | Medium |
Implement a map/dictionary for mapping traits and trigger types to category link. |
01:37, 31 May 2021 (UTC) | 21:54, 20 July 2021 (UTC) | |
Weapon nav | Dev | Completed | Medium | User:FINNER |
Add a new function that constructs the same navbox as Template:WeaponNav. Goal is to reduce memory used by calling Template:Weapon 400+ times on every weapon page as well as automating navbox updates whenever a new weapon is added. Right now, Template:Weapon uses ~11MB, sometimes ~20MB on pages like Volnus Prime. Weapon navigation box generator resides in Module:Weapons/nav. |
03:24, 5 June 2021 (UTC) | 6:07, 7 June 2021 (UTC) |
Weapon infobox | Dev | Completed | Medium | User:FINNER |
Migrate wikitext from Template:WeaponInfoboxAutomatic into a infobox builder function.
Weapon infobox generator resides in Module:Weapons/infobox. |
01:37, 31 May 2021 (UTC) | 22:14, 8 June 2021 (UTC) |
High lua memory usage | Dev/Debugging | Completed | High | User:FINNER |
Some weapon pages have unusually high memory usage for Lua scripts, this will be problematic the moment we add new weapons to Template:WeaponNav:
Normally, memory usage is ~37 MB which is why this is odd. This issue has been fixed when we now generate Template:WeaponNav using this module, instead of calling Template:Weapon 400+ times per page. |
02:04, 1 June 2021 (UTC) | 18:03, 7 June 2021 (UTC) |
Forked Repos
- https://warframe.fandom.com/fr/wiki/Module:Weapons
- https://warframe.fandom.com/es/wiki/M%C3%B3dulo:Weapons
- https://warframe.fandom.com/it/wiki/Modulo:Weapons
- https://warframe.fandom.com/de/wiki/Modul:Weapons
- https://warframe.fandom.com/pt-br/wiki/M%C3%B3dulo:Weapons
- https://warframe.fandom.com/zh-tw/wiki/%E6%A8%A1%E7%B5%84:Weapons
- https://warframe.huijiwiki.com/wiki/%E6%A8%A1%E5%9D%97:Weapons#
Documentation
Package items
weapons.csvGunComparisonTable(frame)
(function)- Builds a CSV table of all WARFRAME's guns with the exception of Kitguns.
- Parameter:
frame
Frame object (table) - Returns: Preformatted text of CSV text (string)
weapons._isVariant(weaponName)
(function)- Checks if a weapon is a variant or not.
- Parameter:
weaponName
Weapon name (string) - Returns:
- True if weapon is a variant, false otherwise (boolean)
- Weapon's variant name or "Base" if weapon is not a variant (string)
- Weapon name, same as weaponName (string)
weapons._buildName(baseName, variant)
(function)- Builds the full name of a weapon's variant. Does not check if it exists or not.
- Parameters:
baseName
Weapon's base name (e.g. "Braton") (string)variant
Variant name (e.g. "Vandal") (string)
- Returns: Weapon's variant name (e.g. "Braton Vandal") (string)
weapons._shortLinkList(Weapon, tooltip)
(function)- Builds a list of weapons, with variants being next to base weapon name inside parentheses (e. g. Braton (MK1, Prime)).
- Parameters:
Weapon
Weapon table (table)tooltip
If true, adds weapon tooltips, false otherwise; defaults to false (boolean)
- Returns: Wikitext of resultant list (string)
weapons._getWeapon(weaponName)
(function)- Returns a specific weapon table entry from
/data
. - Parameter:
weaponName
Weapon name (string) - Returns: Weapon table (table)
weapons._getConclaveWeapon(weaponName)
(function)- Returns a specific weapon table entry from
/Conclave/data
. - Parameter:
weaponName
Weapon name (string) - Returns: Weapon table (table)
weapons._attackLoop(Weapon)
(function)- Loops through all possible attacks that a weapon may have.
- Parameter:
Weapon
Weapon entry as seen in/data
(table) - Returns:
- An iterator function that returns the key-value pair of next attack entry (function)
- Original weapon entry passed into function (table)
weapons._getWeapons(validateFunction)
(function)- Returns a subset of
/data
based on a validation function. - Parameter:
validateFunction
Function that filters out a weapon by taking in a Weapon table argument (function) - Returns: Table of weapon table entries as seen in
/data
(table) weapons._getConclaveWeapons(validateFunction)
(function)- Returns a subset of
/Conclave/data
based on a validation function. - Parameter:
validateFunction
Function that filters out a weapon by taking in a Weapon table argument (function) - Returns: Table of weapon table entries as seen in
/Conclave/data
(table) weapons.getWeaponStanceList(frame)
(function)- Builds list of stances for a particular melee weapon as seen on Template:MeleeCategory.
- Parameter:
frame
Frame object w/ first argument being string weaponName (table) - Returns: Resultant wikitext of comparison list (string)
weapons.getStanceWeaponList(frame)
(function)- Builds list of weapons that can equip a particlar stance mod as seen on Template:StanceWeapons.
- Parameter:
frame
Frame object w/ first argument being string stanceName (table) - Returns: Resultant wikitext of comparison list (string)
weapons.getMeleeWeaponGallery(frame)
(function)- Builds a melee weapon gallery as seen on Template:MeleeCategory.
- Parameter:
frame
Frame object w/ first argumenting being string meleeClass (table) - Returns: Resultant wikitext of gallery (string)
weapons.getWeaponCount(frame)
(function)- Gets the total count of weapons as used on Mastery Rank.
- Parameter:
frame
Frame object w/ the first argument being the weaponType, second argument being a boolean to getFullList, and the third being a boolean to getAll weapons in/data
(table) - Returns: Total count of weapons in a certain category/type (number)
weapons.getPolarityTable(frame)
(function)- Builds wikitable of all weapons' innate polarities as seen on Polarity.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.buildComparison(frame)
(function)- Builds comparison list between two weapons in PvE.
- Parameter:
frame
Frame object (table) - Returns: Resultant wikitext of comparison list (string)
weapons.buildComparison(frame)
(function)- Builds comparison list between two weapons in PvP (Conclave).
- Parameter:
frame
Frame object (table) - Returns: Resultant wikitext of comparison list (string)
weapons.buildAutoboxCategories(frame)
(function)- Adds weapon categories.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of category links (string)
weapons.buildDamageTypeTable(frame)
(function)- Builds a table that lists out all weapons with a certain damage type and the percentage that it makes up of their base damage of the attack specified in their tooltip on the wiki.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getMasteryShortList(frame)
(function)- Builds a list of weapons' mastery requirements as seen on Template:EquipmentUnlock.
- Parameter:
frame
Frame object w/ first argument being a string weaponType (table) - Returns: Wikitext of resultant list (string)
weapons.getRivenDispositionTable(frame)
(function)- Builds a disposition wikitable as seen on Riven Mods/Weapon Dispos.
- Parameter:
frame
Frame object w/ first argument being a string weaponType (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getConclaveList(frame)
(function)- Builds a list of PvP weapons.
- Parameter:
frame
Frame object w/ first argument being a string weaponType (table) - Returns: Wikitext of resultant list (string)
weapons.getCompTableGuns(frame)
(function)- Builds comparison table of gun stats as seen on Weapon Comparison.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getCompTableConclaveGuns(frame)
(function)- Builds comparison table of gun Conclave stats as seen on Weapon Comparison/Conclave.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getCompTableMelees(frame)
(function)- Builds comparison table of melee stats as seen on Weapon Comparison.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getCompTableConclaveMelees(frame)
(function)- Builds comparison table of melee conclave stats as seen on Weapon Comparison/Conclave.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getCompTableArchMelees(frame)
(function)- Builds comparison table of arch-melee stats as seen on Weapon Comparison.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getCompTableSpeedGuns(frame)
(function)- Builds comparison table of projectile flight speeds as seen on Projectile Speed.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
weapons.getCompTableSpeedMelees(frame)
(function)- Builds comparison table of glaive melees' projectile flight speeds as seen on Projectile Speed.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of resultant wikitable (string)
- Created with Docbunto
See Also
- Weapons/Conclave/data
- Weapons/Conclave/data/doc
- Weapons/Conclave/data/melee
- Weapons/Conclave/data/primary
- Weapons/Conclave/data/secondary
- Weapons/characteristics
- Weapons/characteristics/doc
- Weapons/compare
- Weapons/compare/doc
- Weapons/comptable
- Weapons/comptable/doc
- Weapons/csv
- Weapons/csv/doc
- Weapons/data
- Weapons/data/archwing
- Weapons/data/archwing/doc
- Weapons/data/companion
- Weapons/data/companion/doc
- Weapons/data/credits
- Weapons/data/credits/doc
- Weapons/data/dev
- Weapons/data/doc
- Weapons/data/melee
- Weapons/data/melee/doc
- Weapons/data/misc
- Weapons/data/misc/doc
- Weapons/data/modular
- Weapons/data/modular/doc
- Weapons/data/primary
- Weapons/data/primary/doc
- Weapons/data/railjack
- Weapons/data/railjack/doc
- Weapons/data/secondary
- Weapons/data/secondary/doc
- Weapons/data/validate
- Weapons/data/validate/doc
- Weapons/dev
- Weapons/dev/doc
- Weapons/doc
- Weapons/infobox
- Weapons/infobox/doc
- Weapons/nav
- Weapons/nav/doc
- Weapons/ppdata
- Weapons/ppdata/doc
- Weapons/ppdata/seeder
- Weapons/ppdata/seeder/doc
- Weapons/preprocess
- Weapons/preprocess/doc
- Weapons/testcases
- Weapons/testcases/doc
Code
--- '''Weapons''' contains all of [[WARFRAME]]'s non-modular [[Weapons|weapon]] data.<br />
--
-- @module weapons
-- @alias p
-- @author [[User:Falterfire|Falterfire]]
-- @attribution [[User:Flaicher|Flaicher]]
-- @attribution [[User:FINNER|FINNER]]
-- @attribution [[User:Cephalon Scientia|Cephalon Scientia]]
-- @attribution [[User:Gigamicro|Gigamicro]]
-- @attribution [[User:Synthtech|Synthtech]]
-- @attribution [[User:Calenhed|Calenhed]]
-- @image
-- @require [[Module:Icon]]
-- @require [[Module:Math]]
-- @require [[Module:String]]
-- @require [[Module:Table]]
-- @require [[Module:Mods/data]]
-- @require [[Module:Modular/data]]
-- @require [[Module:Stances/data]]
-- @require [[Module:Tooltips]]
-- @require [[Module:Version]]
-- @require [[Module:Weapons/data]]
-- @require [[Module:Weapons/Conclave/data]]
-- @release stable
-- <nowiki>
local p = {}
local ConclaveData = mw.loadData [[Module:Weapons/Conclave/data]]
local ModData = mw.loadData [[Module:Mods/data]]
local ModularData = mw.loadData [[Module:Modular/data]]
local StanceData = mw.loadData [[Module:Stances/data]]
local WeaponData = mw.loadData [[Module:Weapons/data]]
local Icon = require [[Module:Icon]]
local Math = require [[Module:Math]]
local String = require [[Module:String]]
local Tooltip = require [[Module:Tooltips]]
local Version = require [[Module:Version]]
local Table = require [[Module:Table]]
local Polarity = require [[Module:Polarity]]
-- TODO: Could use M:DamageTypes instead of this table?
local Elements = {
"Impact", "Puncture", "Slash", "Heat", "Cold", "Toxin",
"Electricity", "Blast", "Corrosive", "Radiation", "Magnetic", "Gas",
"Viral", "Void", "True", "MinProgenitorBonus"
}
local VARIANT_LIST = {
"Prime", "Prisma", "Wraith", "Vandal", "Vaykor", "Synoid",
"Telos", "Secura", "Sancti", "Rakta", "Mara", "MK1", "Kuva"
}
local function makeDTooltip(dt, addText)
local text = (addText) and dt or ''
return ('<span class="tooltip" data-param="%s" data-param2="DamageTypes">[[File:Dmg%sSmall64.png|x19px|link=]] %s</span>')
:format(dt, dt, text)
end
--- Returns the damage type with the highest damage distribution in an attack.
-- @function getDamageBias
-- @param {table} attackEntry Attack table
-- @returns {number} Damage distribution as a decimal (e.g. 1 being 100%)
-- @returns {string} Name of damage type with highest distribution
local function getDamageBias(attackEntry)
if (attackEntry.Damage ~= nil and Table.size(attackEntry.Damage) > 0) then
local totalDmg = 0
local bestDmg = 0
local bestElement = nil
local elemCount = 0
for damageType, dmg in pairs(attackEntry.Damage) do
if (dmg > bestDmg) then
bestDmg = dmg
bestElement = damageType
end
totalDmg = totalDmg + dmg
if (dmg > 0) then
elemCount = elemCount + 1
end
end
-- Make sure there are two damage instances that are above zero
-- Exception for physical damage types
if (elemCount > 0) then
return (bestDmg / totalDmg), bestElement
end
error('getDamageBias(Attack): '..
'Damage key in Attack entry has no key-value pairs'..mw.dumpObject(attackEntry))
end
error('getDamageBias(Attack): Attack entry has no Damage key')
end
--- Returns the highest damage type of an attack in the form of a percentage
-- followed by a tooltip icon of the damage type
-- @function getDamageBiasString
-- @param {table} attackEntry Attack table
-- @returns {string} Resultant text
local function getDamageBiasString(attackEntry)
local bestPercent, bestElement = getDamageBias(attackEntry)
local result = Math.percentage(bestPercent)
return result..' '..makeDTooltip(bestElement)
end
--- Calculates the derived damage stats commonly used in comparing gun performance.
-- Using what attacks the weapons tooltips are displaying for DPS calculations.
-- @function calculateGunDerivedDamage
-- @param {table} Weapon Weapon table data as seen in M:Weapons/data
-- @returns {number} Returns five number stats in a tuple:
-- * Total damage: final damage when accounting multishot; same as arsenal display
-- * Average shot: damage per single input
-- * Average burst DPS: damage per second without reloading
-- * Average sustained DPS: damage per second w/ reloading
-- * Average lifetime damage: total damage that can be dealt in a single magazine + reserve ammo w/o picking up ammo drops
local function calculateGunDerivedDamage(Weapon)
local TooltipAttack = Weapon[Weapon['TooltipAttackDisplay'] or 'Attack1'] or {}
local totalDamage = 0
for damageType, value in pairs(TooltipAttack['Damage'] or {}) do
if (damageType == 'MinProgenitorBonus') then
-- For a more competitive comparison, assume that Lich weapons have
-- max base damage bonus (+60% of a Progenitor Warframe's element);
-- The value stored in 'MinProgenitorBonus' is based on the minimum +25% bonus
totalDamage = totalDamage + (value * 0.6/0.25)
else
totalDamage = totalDamage + value
end
end
totalDamage = totalDamage * (TooltipAttack['Multishot'] or 1)
local critChance = TooltipAttack['CritChance']
local critMultiplier = TooltipAttack['CritMultiplier']
-- If an attack uses the charge trigger, use that instead since
-- theoretically players want to charge an attack for more damage/DPS
local fireRate = TooltipAttack['ChargeTime'] and (1 / TooltipAttack['ChargeTime']) or TooltipAttack['FireRate']
if TooltipAttack['ChargeTime'] and TooltipAttack['FireRate'] then
fireRate = (1 / TooltipAttack['ChargeTime']) < TooltipAttack['FireRate'] and (1 / TooltipAttack['ChargeTime']) or TooltipAttack['FireRate']
end
local reloadTime = Weapon['Reload']
local magazine = Weapon['Magazine']
local maxAmmo = Weapon['MaxAmmo']
-- TODO: If we are defining average shot as average damage dealt per a single attack input
-- than this is is inaccurate for burst-fire attacks (being picky on wording, but it is
-- for more accurate calculations)
local avgShot = totalDamage * (1 + critChance * (critMultiplier - 1))
-- Extra one needed in calculation to account for initial filled mag
-- If there is no reserve ammo, that means that weapon can deal an infinite amount of damage theoretically
local avgLifetimeDmg = maxAmmo < math.huge and avgShot * (magazine / (TooltipAttack['AmmoCost'] or 1)) * (1 + (maxAmmo / magazine)) or math.huge
-- Need to ignore the first shot of guns since it is instantaneous and is
-- not affected by fire rate (which causes the delay between shots)
local numShotPerMag = magazine / (TooltipAttack['AmmoCost'] or 1)
local avgBurst = avgShot * fireRate
local avgSustained
if Weapon.Name ~= 'Vectis' and Weapon.Name ~= 'Vectis Prime' then
avgSustained = avgBurst * numShotPerMag / (fireRate * reloadTime + numShotPerMag)
else
avgSustained = avgBurst * numShotPerMag / (fireRate * reloadTime + numShotPerMag - 1)
end
return totalDamage, avgShot, avgBurst, avgSustained, avgLifetimeDmg
end
-- Getter functions for attack keys and derived stats
local ATTACK_KEY_MAP = {
Impact = function(attackEntry) return attackEntry['Damage']['Impact'] or 0 end,
Puncture = function(attackEntry) return attackEntry['Damage']['Puncture'] or 0 end,
Slash = function(attackEntry) return attackEntry['Damage']['Slash'] or 0 end,
Cold = function(attackEntry) return attackEntry['Damage']['Cold'] or 0 end,
Electricity = function(attackEntry) return attackEntry['Damage']['Electricity'] or 0 end,
Heat = function(attackEntry) return attackEntry['Damage']['Heat'] or 0 end,
Toxin = function(attackEntry) return attackEntry['Damage']['Toxin'] or 0 end,
Blast = function(attackEntry) return attackEntry['Damage']['Blast'] or 0 end,
Corrosive = function(attackEntry) return attackEntry['Damage']['Corrosive'] or 0 end,
Gas = function(attackEntry) return attackEntry['Damage']['Gas'] or 0 end,
Magnetic = function(attackEntry) return attackEntry['Damage']['Magnetic'] or 0 end,
Radiation = function(attackEntry) return attackEntry['Damage']['Radiation'] or 0 end,
Viral = function(attackEntry) return attackEntry['Damage']['Viral'] or 0 end,
Void = function(attackEntry) return attackEntry['Damage']['Void'] or 0 end,
MinProgenitorBonus = function(attackEntry) return attackEntry['Damage']['MinProgenitorBonus'] or 0 end,
AttackName = function(attackEntry) return attackEntry['AttackName'] or 'Normal' end,
AmmoCost = function(attackEntry) return attackEntry['AmmoCost'] or 1 end,
BurstCount = function(attackEntry) return attackEntry['BurstCount'] end,
BurstFireRate = function(attackEntry) return attackEntry['BurstFireRate'] end,
CompTableFireRate = function(attackEntry)
-- if (attackEntry['FireRate'] ~= nil and attackEntry['ChargeTime'] ~= nil) then
-- return attackEntry['FireRate'] > (1 / attackEntry['ChargeTime']) and attackEntry['FireRate'] or
-- Math.round(1 / attackEntry['ChargeTime'], 0.01)
if (attackEntry['ChargeTime'] ~= nil) then
return 1 / attackEntry['ChargeTime']
end
return attackEntry['FireRate']
end,
BaseDamage = function(attackEntry)
local total = 0
for damageType, value in pairs(attackEntry['Damage']) do
total = total + value
end
return total
end,
TotalDamage = function(attackEntry)
local total = 0
for damageType, value in pairs(attackEntry['Damage']) do
total = total + value
end
return total * (attackEntry['Multishot'] or 1)
end,
DamageBias = function(attackEntry) return getDamageBiasString(attackEntry) end,
ChargeTime = function(attackEntry) return attackEntry['ChargeTime'] end,
CritChance = function(attackEntry) return Math.percentage(attackEntry['CritChance']) end,
CritMultiplier = function(attackEntry) return (attackEntry['CritMultiplier'])..'x' end,
FalloffEnd = function(attackEntry) return attackEntry['Falloff'] and attackEntry['Falloff']['EndRange'] or 600 end,
FalloffReduction = function(attackEntry) return attackEntry['Falloff'] and attackEntry['Falloff']['Reduction'] or 0.99 end,
FalloffStart = function(attackEntry) return attackEntry['Falloff'] and attackEntry['Falloff']['StartRange'] or 300 end,
FireRate = function(attackEntry) return attackEntry['FireRate'] end,
HeadshotMultiplier = function(attackEntry)
return attackEntry['HeadshotMultiplier'] and (attackEntry['HeadshotMultiplier'])..'x' or '1x'
end,
Multishot = function(attackEntry) return attackEntry['Multishot'] or 1 end,
PelletName = function(attackEntry) return attackEntry['PelletName'] end,
PunchThrough = function(attackEntry) return attackEntry['PunchThrough'] or 0 end,
Radius = function(attackEntry) return attackEntry['Radius'] end,
ShotSpeed = function(attackEntry) return attackEntry['ShotSpeed'] end,
ShotType = function(attackEntry) return attackEntry['ShotType'] end,
StatusChance = function(attackEntry) return Math.percentage(attackEntry['StatusChance']) end,
}
-- Getter functions for shared weapon keys and derived stats
local SHARED_KEY_MAP = {
Class = function(weaponEntry) return weaponEntry['Class'] end,
Disposition = function(weaponEntry)
return weaponEntry['Disposition'] ~= nil and weaponEntry['Disposition'] or 'N/A'
end,
Family = function(weaponEntry) return weaponEntry['Family'] end,
Introduced = function(weaponEntry)
return weaponEntry['Introduced'] and Version._getVersion(weaponEntry['Introduced'])['Name'] or 'N/A'
end,
IntroducedDate = function(weaponEntry)
return weaponEntry['Introduced'] and Version._getVersionDate(weaponEntry['Introduced']) or 'N/A'
end,
Link = function(weaponEntry) return '[['..weaponEntry['Link']..']]' end,
Mastery = function(weaponEntry) return weaponEntry['Mastery'] or 'N/A' end,
Name = function(weaponEntry) return '[['..weaponEntry['Name']..']]' end,
NameLink = function(weaponEntry) return '[['..weaponEntry['Link']..'|'..weaponEntry['Name']..']]' end,
Polarities = function(weaponEntry) return Polarity._pols(weaponEntry['Polarities']) end,
Traits = function(weaponEntry)
local traitString = {}
for _, trait in ipairs(weaponEntry['Traits']) do
table.insert(traitString, trait)
end
return table.concat(traitString, ', ')
end,
Type = function(weaponEntry) return weaponEntry['Type'] end
}
-- Getter functions for gun weapon keys and derived stats
local GUN_KEY_MAP = {
Accuracy = function(weaponEntry)
if (weaponEntry['Accuracy'] ~= nil) then return weaponEntry['Accuracy'] end
return weaponEntry[weaponEntry['TooltipAttackDisplay'] or 'Attack1']['Accuracy']
end,
AmmoType = function(weaponEntry)
if (weaponEntry['AmmoType'] ~= nil) then
return weaponEntry['AmmoType']
elseif(weaponEntry['Type'] == 'Secondary') then
return 'Pistol'
elseif(weaponEntry['Type'] == 'Primary') then
local class = Weapon['Class']
if (class == 'Rifle') then
return 'Rifle'
elseif (class == 'Shotgun') then
return 'Shotgun'
elseif (class == 'Bow') then
return 'Bow'
elseif (class == 'Sniper Rifle' or class == 'Launcher') then
return "Sniper"
end
end
return ''
end,
AvgProcCount = function(weaponEntry)
local attackEntry = weaponEntry[weaponEntry['TooltipAttackDisplay'] or 'Attack1']
local statusChance = attackEntry['StatusChance'] or 0
local multishot = attackEntry['Multishot'] or 1
local numForcedProcs = attackEntry['ForcedProcs'] and Table.size(attackEntry['ForcedProcs']) or 0
return (statusChance + numForcedProcs) * multishot
end,
AvgProcPerSec = function(weaponEntry)
local attackEntry = weaponEntry[weaponEntry['TooltipAttackDisplay'] or 'Attack1']
local statusChance = attackEntry['StatusChance'] or 0
local multishot = attackEntry['Multishot'] or 1
local numForcedProcs = attackEntry['ForcedProcs'] and Table.size(attackEntry['ForcedProcs']) or 0
local fireRate = (attackEntry['ChargeTime'] ~= nil) and (1 / attackEntry['ChargeTime']) or attackEntry['FireRate']
return (statusChance + numForcedProcs) * multishot * fireRate
end,
AvgShotDmg = function(weaponEntry)
local totalDamage, avgShot = calculateGunDerivedDamage(weaponEntry)
return avgShot
end,
BurstDps = function(weaponEntry)
local totalDamage, avgShot, avgBurst = calculateGunDerivedDamage(weaponEntry)
return avgBurst
end,
ExilusPolarity = function(weaponEntry) return Polarity._polarity(weaponEntry['ExilusPolarity']) end,
IsSilent = function(weaponEntry) return weaponEntry['IsSilent'] end,
Magazine = function(weaponEntry) return weaponEntry['Magazine'] end,
MaxAmmo = function(weaponEntry) return weaponEntry['MaxAmmo'] end,
Range = function(weaponEntry) return weaponEntry['Range'] end,
Reload = function(weaponEntry) return weaponEntry['Reload'] end,
ReloadDelay = function(weaponEntry) return weaponEntry['ReloadDelay'] or 0 end,
ReloadDelayEmpty = function(weaponEntry) return weaponEntry['ReloadDelayEmpty'] or 0 end,
ReloadStyle = function(weaponEntry) return weaponEntry['ReloadStyle'] end,
Spool = function(weaponEntry) return weaponEntry['Spool'] or 0 end,
SustainedDps = function(weaponEntry)
local totalDamage, avgShot, avgBurst, avgSustained = calculateGunDerivedDamage(weaponEntry)
return avgSustained
end,
Trigger = function(weaponEntry) return weaponEntry['Trigger'] end
}
-- Getter functions for melee weapon keys and derived stats
local MELEE_KEY_MAP = {
BlockAngle = function(weaponEntry) return weaponEntry['BlockAngle'] or 0 end,
ComboDur = function(weaponEntry) return weaponEntry['ComboDur'] or 0 end,
FollowThrough = function(weaponEntry) return weaponEntry['FollowThrough'] or 0 end,
HeavyAttack = function(weaponEntry) return weaponEntry['HeavyAttack'] or 0 end,
HeavyElement = function(weaponEntry) return weaponEntry['HeavyElement'] end,
HeavySlamAttack = function(weaponEntry) return weaponEntry['HeavySlamAttack'] end,
HeavySlamElement = function(weaponEntry) return weaponEntry['HeavySlamElement'] end,
HeavyRadialDmg = function(weaponEntry) return weaponEntry['HeavyRadialDmg'] or 0 end,
HeavyRadialElement = function(weaponEntry) return weaponEntry['HeavyRadialElement'] end,
HeavySlamRadius = function(weaponEntry) return weaponEntry['HeavySlamRadius'] or 0 end,
MeleeRange = function(weaponEntry) return weaponEntry['MeleeRange'] or 0 end,
SlamAttack = function(weaponEntry) return weaponEntry['SlamAttack'] or 0 end,
SlamElement = function(weaponEntry) return weaponEntry['SlamElement'] end,
SlamRadialDmg = function(weaponEntry) return weaponEntry['SlamRadialDmg'] or 0 end,
SlamRadialElement = function(weaponEntry) return weaponEntry['SlamRadialElement'] end,
SlamRadialProc = function(weaponEntry) return weaponEntry['SlamRadialProc'] end,
SlamRadius = function(weaponEntry) return weaponEntry['SlamRadius'] or 0 end,
SlideAttack = function(weaponEntry) return weaponEntry['SlideAttack'] or 0 end,
SlideElement = function(weaponEntry) return weaponEntry['SlideElement'] end,
Stances = function(weaponEntry) return getWeaponStanceList(weaponEntry) end,
StancePolarity = function(weaponEntry)
return weaponEntry['StancePolarity'] ~= nil and
Polarity._polarity(weaponEntry['StancePolarity']) or 'N/A'
end,
WindUp = function(weaponEntry) return weaponEntry['WindUp'] or 0 end
}
-- For mapping weapon traits or types to a category link
local CATEGORY_MAP = {
Primary = '[[Category:Primary Weapons]]',
Secondary = '[[Category:Secondary Weapons]]',
Melee = '[[Category:Melee Weapons]]',
['Arch-Melee'] = '[[Category:Archwing Melee]]',
['Arch-Gun'] = '[[Category:Archwing Gun]]',
['Arch-Gun (Atmosphere)'] = '[[Category:Archwing Gun]]',
Kitgun = '[[Category:Kitgun]]',
Zaw = '[[Category:Zaw]]',
['Railjack Armament'] = '[[Category:Railjack]]',
Gear = '[[Category:Gear]]',
Rifle = '[[Category:Assault Rifle]]',
['Sniper Rifle'] = '[[Category:Sniper Rifle]]',
Shotgun = '[[Category:Shotgun]]',
Pistol = '[[Category:Pistol]]',
['Dual Pistols'] = '[[Category:Dual Pistols]]',
Bow = '[[Category:Bow]]',
Launcher = '[[Category:Launcher]]',
['Arm-Cannon'] = '[[Category:Arm-Cannon]]',
['Speargun'] = '[[Category:Speargun]]',
Thrown = '[[Category:Thrown]]',
['Shotgun Sidearm'] = '[[Category:Shotgun Sidearm]]',
Prime = '[[Category:Prime Weapons]]',
['Never Vaulted'] = '[[Category:Never Vaulted]]',
Vaulted = '[[Category:Vaulted]]',
Wraith = '[[Category:Wraith]]',
Vandal = '[[Category:Vandal]]',
-- Maybe replace with 'Kuva' category? Though technically Broken Scepter is a "Kuva" weapon which
-- is why this distinction is made (in the past, editors mislabeled this trait)
['Kuva Lich'] = '[[Category:Kuva Lich]]',
Prisma = '[[Category:Prisma]]',
Grineer = '[[Category:Grineer Weapons]]',
Corpus = '[[Category:Corpus Weapons]]',
Infested = '[[Category:Infested Weapons]]',
Tenno = '[[Category:Tenno Weapons]]',
Sentient = '[[Category:Sentient Weapons]]',
Entrati = '[[Category:Entrati Weapons]]',
Baro = '[[Category:Baro Ki\'Teer Offering]]',
Syndicate = '[[Category:Syndicate Offerings]]',
['Invasion Reward'] = '[[Category:Invasion Reward]]',
['Alt Fire'] = '[[Category:Weapons with Alt Fire]]',
['AoE'] = '[[Category:Weapons with Area of Effect]][[Category:Self Interrupt Weapons]]',
Active = '[[Category:Active]]',
Auto = '[[Category:Automatic]]',
['Auto-Spool'] = '[[Category:Automatic]]',
Burst = '[[Category:Burst Fire]]',
['Auto-Burst'] = '[[Category:Burst Fire]]',
['Auto Charge'] = '[[Category:Charge]]',
Charge = '[[Category:Charge]]',
Duplex = '[[Category:Duplex Fire]]',
['Semi-Auto'] = '[[Category:Semi-Automatic]]',
Held = '[[Category:Continuous Weapons]]'
}
--- Builds a CSV table of all WARFRAME's guns with the exception of Kitguns.
-- @function p.csvGunComparisonTable
-- @param {table} frame Frame object
-- @returns {string} Preformatted text of CSV text
function p.csvGunComparisonTable(frame)
-- Weapon types to show in resultant table
local weaponTypesFilter = { 'Primary', 'Secondary', 'Robotic', 'Arch-Gun', 'Arch-Gun (Atmosphere)', 'Amp' }
local tableEntryTemplate = {} -- Would look like '%s,%s,%s'
local tableHeader = {
'Name',
'Trigger',
'AttackName',
'Impact',
'Puncture',
'Slash',
'Cold',
'Electricity',
'Heat',
'Toxin',
'Blast',
'Corrosive',
'Gas',
'Magnetic',
'Radiation',
'Viral',
'Void',
'MinProgenitorBonus',
'Damage',
'TotalDmg',
'CritChance',
'CritMultiplier',
'AvgShotDmg',
'BurstDps',
'SustainedDps',
'LifetimeDmg',
'StatusChance',
'AvgProcCount',
'AvgProcPerSec',
'Multishot',
'FireRate',
'Disposition',
'Mastery',
'Magazine',
'MaxAmmo',
'Reload',
'ShotType',
'PunchThrough',
'Accuracy',
'Introduced',
'IntroducedDate',
'Type',
'Class',
'AmmoType'
}
for i, _ in ipairs(tableHeader) do table.insert(tableEntryTemplate, '%s,') end
tableEntryTemplate[#tableEntryTemplate] = '%s' -- Last column
tableEntryTemplate = table.concat(tableEntryTemplate)
local csvResult = { '<pre>' }
table.insert(csvResult, string.format(tableEntryTemplate, unpack(tableHeader)))
for weaponName, weaponData in Table.skpairs(WeaponData['Weapons']) do
if (Table.contains(weaponTypesFilter, weaponData['Type'])) then
-- Going through all the possible Attack keys and adding them to CSV
-- (TODO: consolidate these keys into one Attack table with key-value pairs)
for i = 1, 9, 1 do
if (weaponData['Attack'..i] ~= nil) then
local totalDamage, avgShot, avgBurst, avgSustained, avgLifetimeDmg = calculateGunDerivedDamage(weaponData)
local weaponAttack = weaponData['Attack'..i]
local baseDamage = 0
for damageType, damageValue in pairs(weaponAttack['Damage']) do
baseDamage = baseDamage + damageValue
end
local tableEntryValues = {
weaponName,
tostring(weaponData['Trigger']),
tostring(weaponAttack['AttackName'] or 'Normal'),
tostring(weaponAttack['Damage']['Impact'] or 0),
tostring(weaponAttack['Damage']['Puncture'] or 0),
tostring(weaponAttack['Damage']['Slash'] or 0),
tostring(weaponAttack['Damage']['Cold'] or 0),
tostring(weaponAttack['Damage']['Electricity'] or 0),
tostring(weaponAttack['Damage']['Heat'] or 0),
tostring(weaponAttack['Damage']['Toxin'] or 0),
tostring(weaponAttack['Damage']['Blast'] or 0),
tostring(weaponAttack['Damage']['Corrosive'] or 0),
tostring(weaponAttack['Damage']['Gas'] or 0),
tostring(weaponAttack['Damage']['Magnetic'] or 0),
tostring(weaponAttack['Damage']['Radiation'] or 0),
tostring(weaponAttack['Damage']['Viral'] or 0),
tostring(weaponAttack['Damage']['Void'] or 0),
tostring(weaponAttack['Damage']['MinProgenitorBonus'] or 0),
tostring(baseDamage),
tostring(totalDamage),
tostring(weaponAttack['CritChance']),
tostring(weaponAttack['CritMultiplier']),
tostring(avgShot),
tostring(avgBurst),
tostring(avgSustained),
tostring(avgLifetimeDmg),
tostring(weaponAttack['StatusChance']),
tostring((weaponAttack['StatusChance'] or 0) * (weaponAttack['Multishot'] or 1)),
tostring(GUN_KEY_MAP['AvgProcPerSec'](weaponData)),
tostring(weaponAttack['Multishot'] or 1),
-- If an attack uses the charge trigger, use that instead since
-- theoretically players want to charge an attack for more damage/DPS
tostring(weaponAttack['ChargeTime'] and (1 / weaponAttack['ChargeTime']) or weaponAttack['FireRate']),
tostring(weaponData['Disposition']),
tostring(weaponData['Mastery']),
tostring(weaponData['Magazine']),
tostring(weaponData['MaxAmmo']),
tostring(weaponData['Reload']),
tostring(weaponAttack['ShotType']),
tostring(weaponAttack['PunchThrough'] or 0),
tostring(weaponData['Accuracy']),
tostring(weaponData['Introduced'] and Version._getVersion(weaponData['Introduced'])['Name'] or nil),
tostring(weaponData['Introduced'] and Version._getVersionDate(weaponData['Introduced']) or nil),
tostring(weaponData['Type']),
tostring(weaponData['Class']),
tostring((weaponData['Type'] == 'Secondary' and 'Pistol') or weaponData['AmmoType'] or weaponData['Class'])
}
local tableEntry = string.format(tableEntryTemplate, unpack(tableEntryValues))
table.insert(csvResult, tableEntry)
end
end
end
end
table.insert(csvResult, '</pre>')
return table.concat(csvResult, '\n')
end
--- Checks if a weapon is a variant or not.
-- @function p._isVariant
-- @param {string} weaponName Weapon name
-- @returns {boolean} True if weapon is a variant, false otherwise
-- @returns {string} Weapon's variant name or "Base" if weapon is not a variant
-- @returns {string} Weapon name, same as weaponName
function p._isVariant(weaponName)
for i, var in pairs(VARIANT_LIST) do
if (string.find(weaponName, var)) then
local baseName = string.gsub(weaponName, " ?"..var.." ?-?", "")
return true, var, baseName
end
end
return false, "Base", weaponName
end
--- Builds the full name of a weapon's variant. Does not check if it exists or not.
-- @function p._buildName
-- @param {string} baseName Weapon's base name (e.g. "Braton")
-- @param {string} variant Variant name (e.g. "Vandal")
-- @returns {string} Weapon's variant name (e.g. "Braton Vandal")
function p._buildName(baseName, variant)
if (variant == nil or variant == 'Base' or variant == '') then
return baseName
elseif (baseName == 'Laser Rifle' and variant == 'Prime') then
return variant..' '..baseName -- Laser Rifle has a primed version called 'Prime Laser Rifle'
elseif (variant == 'Prime' or variant == 'Wraith' or variant == 'Vandal') then
return baseName..' '..variant
elseif (variant == 'MK1') then
return 'MK1-'..baseName
end
return variant..' '..baseName
end
-- TODO: Function can be refactored
--- Builds a list of weapons, with variants being next to base weapon name inside parentheses
-- (e.g. Braton (MK1, Prime)).
-- @function p._shortLinkList
-- @param {table} Weapon Weapon table
-- @param {boolean} tooltip If true, adds weapon tooltips, false otherwise; defaults to false
-- @returns {string} Wikitext of resultant list
function p._shortLinkList(Weapons, tooltip)
-- First grabbing all the pieces and stashing them in a table
local baseNames = {}
for key, weap in Table.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 Table.skpairs(baseNames) do
-- So first, check if "Base" is in the list
-- Because if it isn't, list all variants separately
if (Table.contains(variants, "Base")) then
table.sort(variants)
-- First, get the basic version
local thisRow = ""
if (tooltip) then
thisRow = "{{Weapon|"..baseName.."}}"
else
thisRow = "[["..baseName.."]]"
end
-- Then, if there are variants...
if (Table.size(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
if (tooltip) then
thisRow = thisRow.."{{Weapon|"..p._buildName(baseName, varName).."|"..varName.."}}"
else
thisRow = thisRow.."[["..p._buildName(baseName, varName).."|"..varName.."]]"
end
count = count + 1
end
end
thisRow = thisRow..")"
end
table.insert(result, thisRow)
else
for i, varName in pairs(variants) do
if (tooltip) then
table.insert(result, "{{Weapon|"..p._buildName(baseName, varName).."}}")
else
table.insert(result, "[["..p._buildName(baseName, varName).."]]")
end
end
end
end
return result
end
--- Returns a specific weapon table entry from <code>/data</code>.
-- @function p._getWeapon
-- @param {string} weaponName Weapon name
-- @returns {table} Weapon table
function p._getWeapon(weaponName)
local weapon = WeaponData["Weapons"][weaponName]
if weapon ~= nil then
return weapon
end
for key, Weapon in Table.skpairs(WeaponData["Weapons"]) do
if (Weapon.Name == WeapName) then return Weapon end
end
error('p._getWeapon(weaponName): "'..weaponName..'" does not exist in M:Weapons/data')
end
--- Returns a specific weapon table entry from <code>/Conclave/data</code>.
-- @function p._getConclaveWeapon
-- @param {string} weaponName Weapon name
-- @returns {table} Weapon table
function p._getConclaveWeapon(weaponName)
local weapon = ConclaveData["Weapons"][weaponName]
if (weapon ~= nil and weapon.Name == weaponName) then
return weapon
end
for key, Weapon in Table.skpairs(ConclaveData["Weapons"]) do
if (Weapon.Name == WeapName or key == WeapName) then
return Weapon
end
end
error('p._getConclaveWeapon(weaponName): "'..weaponName..'" does not exist in M:Weapons/Conclave/data')
end
--- Returns a specific attack table from a weapon table entry.
-- @function getAttack
-- @param {table, string} Weapon Weapon entry as seen in <code>/data</code> or the name of weapon
-- @param[opt] {string} attackName Name of attack key; if omitted, will default to "Attack1"
-- @returns {table} Attack table
local function getAttack(Weapon, attackName)
if (Weapon == nil or attackName == nil) then return end
if (type(Weapon) == "string") then
Weapon = p._getWeapon(Weapon)
end
if (not attackName) then
return Weapon.Attack1 or Weapon.Damage and Weapon
end
return Weapon[attackName:find 'Attack' and attackName or 'Attack'..attackName]
end
--- Loops through all possible attacks that a weapon may have.
-- @function p._attackLoop
-- @param {table} Weapon Weapon entry as seen in <code>/data</code>
-- @returns {function} An iterator function that returns the key-value pair of next attack entry
-- @returns {table} Original weapon entry passed into function
function p._attackLoop(Weapon)
if (Weapon == nil) then
return function() return nil end
end
local function nextAttack(t, k)
if not k then return '1', t['Damage'] and t or t['Attack1'] end
local v
repeat
k, v = next(t,k)
until type(v) == 'table' and v['Damage']
return k, v
end
return nextAttack, Weapon
end
--- Returns all melee weapons. If weapType is not nil, only grab for a specific type
-- For example, if weapType is "Nikana", only pull Nikanas.
-- @function getMeleeWeapons
-- @param[opt] {string} weapClass Name of melee class to filter by; if nil then gets all melee weapons
-- @param[opt] {boolean} PvP If true, only gets melee weapons available in Conclave, false otherwise; defaults to false
-- @returns {table} An array of melee weapon table entries as seen in <code>/data</code>
local function getMeleeWeapons(weapClass, PvP)
local weaps = {}
for i, weap in Table.skpairs(WeaponData["Weapons"]) do
if ((weap.Ignore == nil or not weap.Ignore) and weap.Type ~= nil and weap.Type == "Melee") then
local classMatch = (weapClass == nil or weap.Class == weapClass)
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
-- TODO: Can probably be removed and refactored into getMeleeWeapons()
--- Returns all melee weapons. If weapType is not nil, only grab for a specific type
-- For example, if weapType is "Nikana", only pull Nikanas.
-- @function getConclaveMeleeWeapons
-- @param[opt] {string} weapClass Name of melee class to filter by; if nil then gets all melee weapons
-- @param[opt] {boolean} PvP If true, only gets melee weapons available in Conclave, false otherwise; defaults to false
-- @returns {table} An array of melee weapon table entries as seen in <code>/Conclave/data</code>
local function getConclaveMeleeWeapons(weapClass, PvP)
local weaps = {}
local weapClasses = {}
if (weapClass ~= nil) then
weapClasses = String.split(weapClass, ",")
end
for i, weap in Table.skpairs(ConclaveData["Weapons"]) do
if ((weap.Ignore == nil or not weap.Ignore) and weap.Type ~= nil and weap.Type == "Melee") then
local classMatch = (weapClass == nil or Table.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
--- Gets stance mods for a particular melee class.
--- Returns a subset of <code>/data</code> based on a validation function.
-- @function p._getWeapons
-- @param {function} validateFunction Function that filters out a weapon by taking in a Weapon table argument
-- @returns {table} Table of weapon table entries as seen in <code>/data</code>
function p._getWeapons(validateFunction)
local weaponList = {}
for weaponName, weaponEntry in Table.skpairs(WeaponData["Weapons"]) do
if (not Table.contains(WeaponData["IgnoreInCount"], weaponName) and
(weaponEntry.Ignore == nil or not weaponEntry.Ignore) and validateFunction(weaponEntry)) then
table.insert(weaponList, weaponEntry)
end
end
return weaponList
end
-- TODO: Remove b/c unused in this module?
--- Returns a subset of <code>/Conclave/data</code> based on a validation function.
-- @function p._getConclaveWeapons
-- @param {function} validateFunction Function that filters out a weapon by taking in a Weapon table argument
-- @returns {table} Table of weapon table entries as seen in <code>/Conclave/data</code>
function p._getConclaveWeapons(validateFunction)
local weaps = {}
for i, weap in Table.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
-- TODO: Move to M:Stances?
--- Gets stance mods for a particular melee class.
-- @function getWeaponStanceList
-- @param {string} meleeClass Melee class
-- @param {boolean} pvpOnly If true, only gets PvP-exclusive stance mods, otherwise gets all applicable stance mods; default false
-- @returns {table} Table of stance table entries as seen in <code>M:Stance/data</code>
local function getStances(meleeClass, pvpOnly)
local stanceTable = {}
for stanceName, Stance in pairs(StanceData) do
local typeMatch = (meleeClass == nil or meleeClass == Stance.WeaponType)
local pvpMatch = (pvpOnly ~= nil and pvpOnly) or (Stance.ConclaveOnly == nil or not Stance.ConclaveOnly)
if (typeMatch and pvpMatch) then
stanceTable[stanceName] = Stance
end
end
return stanceTable
end
--- Builds list of stances for a particular melee weapon as seen on [[Template:MeleeCategory]].
-- @function getWeaponStanceList
-- @param {table} Weapon Wepaon table
-- @returns {string} Resultant wikitext of comparison list
local function getWeaponStanceList(Weapon)
if (Weapon == nil or Weapon.Type ~= "Melee") then return nil end
local stanceTable = {}
if Weapon.Class == "Exalted Weapon" then
stanceTable = getStances(Weapon.Name, Weapon.Conclave)
else
stanceTable = getStances(Weapon.Class, Weapon.Conclave)
end
local result = ""
for stanceName, Stance in pairs(stanceTable) do
if (string.len(result) > 0) then
result = result.."<br/>"
end
local polarity = ""
local link = ""
if Weapon.Class == "Exalted Weapon" then
link = "[["..ModData["Mods"][stanceName].Link.."|"..stanceName.."]]"
else
polarity = " ("..Polarity._polarity(ModData["Mods"][stanceName].Polarity)..")"
link = "[["..stanceName.."]]"
end
-- Adding tooltip
result = result..Tooltip.getIcon(stanceName, "Mods")
-- If this is a PvP Stance, add the disclaimer
if (Stance.ConclaveOnly ~= nil and Stance.ConclaveOnly) then
result = result.." (PvP Only)"
end
end
return result
end
-- TODO: Pull stance data from M:Stance/data?
--- Builds list of stances for a particular melee weapon as seen on [[Template:MeleeCategory]].
-- @function p.getWeaponStanceList
-- @param {table} frame Frame object w/ first argument being string weaponName
-- @returns {string} Resultant wikitext of comparison list
function p.getWeaponStanceList(frame)
local weaponName = frame.args ~= nil and frame.args[1] or frame
local Weapon = p._getWeapon(weaponName)
return getWeaponStanceList(Weapon)
end
--- Gets the value of a certain statistic of a weapon.
-- @function getValue
-- @param {table} Weapon Weapon table
-- @param {string} keyName Name of key
-- @param[opt] {string} attackName Name of attack to search through
-- @returns {string, number} Value of statistic
local function getValue(Weapon, keyName, attackName)
-- Trying to use pcall to get more specific errors
-- for _, map in pairs({ SHARED_KEY_MAP, GUN_KEY_MAP, MELEE_KEY_MAP, ATTACK_KEY_MAP }) do
-- if (map[keyName] ~= nil) then
-- local status, result = pcall(map[keyName], Weapon)
-- end
-- end
-- error('getValue(Weapon, keyName, attackName): Cannot get keyName "'..keyName..'" in '..mw.dumpObject(Weapon))
return SHARED_KEY_MAP[keyName] ~= nil and SHARED_KEY_MAP[keyName](Weapon) or
GUN_KEY_MAP[keyName] ~= nil and GUN_KEY_MAP[keyName](Weapon) or
MELEE_KEY_MAP[keyName] ~= nil and MELEE_KEY_MAP[keyName](Weapon) or
ATTACK_KEY_MAP[keyName] ~= nil and ATTACK_KEY_MAP[keyName](Weapon[attackName or Weapon['TooltipAttackDisplay'] or 'Attack1']) or
error('getValue(Weapon, keyName, attackName): Cannot get keyName "'..keyName..'" in '..mw.dumpObject(Weapon))
end
-- TODO: Move this function to M:Stance?
--- Builds list of weapons that can equip a particlar stance mod as seen on [[Template:StanceWeapons]].
-- @function p.getStanceWeaponList
-- @param {table} frame Frame object w/ first argument being string stanceName
-- @returns {string} Resultant wikitext of comparison list
function p.getStanceWeaponList(frame)
local stanceName = frame.args ~= nil and frame.args[1] or frame
local Stance = StanceData[stanceName]
assert(Stance ~= nil, "p.getStanceWeaponList(frame): "..stanceName.." not found")
local weaps = getMeleeWeapons(Stance.WeaponType, Stance.ConclaveOnly)
local result = ""
for i, weap in Table.skpairs(weaps) do
if (string.len(result) > 0) then
result = result.."\n"
end
if (Stance.ConclaveOnly) then
result = result.."*[[Conclave:"..weap.Name.."|"..weap.Name.."]]"
else
result = result.."*[["..weap.Name.."]]"
end
if (weap.StancePolarity == ModData["Mods"][stanceName].Polarity) then
result = result.." ✓"
end
end
return result
end
--- Builds a weapon gallery.
-- @function getWeaponGallery
-- @param {table} Weapons Array of weapon table entries to be displayed
-- @returns {string} Resultant wikitext of gallery
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"
if ((i - 1) % 5 == 0) then
table.insert(result, nameRow.."\n|-")
nameRow = "\n|-"
end
table.insert(result, '| style="width:165px" |[[File:'..theImage..'|150px|link='..Weapon.Name..']]')
nameRow = nameRow..'\n| style="vertical-align: text-top;" |[['..Weapon.Name..']]'
end
table.insert(result, nameRow)
table.insert(result, '|}')
return table.concat(result, '\n')
end
--- Builds a melee weapon gallery as seen on [[Template:MeleeCategory]].
-- @function p.getMeleeWeaponGallery
-- @param {table} frame Frame object w/ first argumenting being string meleeClass
-- @returns {string} Resultant wikitext of gallery
function p.getMeleeWeaponGallery(frame)
local meleeClass = frame.args ~= nil and frame.args[1] or ""
local WeapArray = getMeleeWeapons(meleeClass)
local result = "=="..meleeClass.." Weapons==\n"
result = result..getWeaponGallery(WeapArray)
return result
end
--- Gets the total count of weapons as used on [[Mastery Rank]].
-- @function p.getWeaponCount
-- @param {table} frame Frame object w/ the first argument being the weaponType,
-- second argument being a boolean to getFullList, and the third being a boolean to getAll weapons in <code>/data</code>
-- @returns {number} Total count of weapons in a certain category/type
function p.getWeaponCount(frame)
local weaponType = frame.args ~= nil and frame.args[1] or frame
local getFullList = frame.args ~= nil and frame.args[2]
local getAll = frame.args ~= nil and frame.args[3]
local count = 0
local fullList = ""
for i, val in Table.skpairs(WeaponData["Weapons"]) do
if (not Table.contains(WeaponData["IgnoreInCount"], i) and getAll ~= "true") or getAll == "true" then
if (weaponType == nil or weaponType == "") then
count = count + 1
if (getFullList ~= nil) then fullList = fullList.."\n# "..val.Name end
elseif (weaponType == "Warframe") then
if ((val.Type == "Primary" or val.Type == "Secondary" or val.Type == "Melee") and val.Class ~= "Exalted Weapon") then
count = count + 1
if (getFullList ~= nil) then fullList = fullList.."\n# "..val.Name end
end
elseif (weaponType == "Archwing") then
if ((val.Type == "Arch-Gun" or val.Type == "Arch-Melee") and val.Class ~= "Exalted Weapon") then
count = count + 1
if (getFullList ~= nil) then fullList = fullList.."\n# "..val.Name end
end
elseif (weaponType == "Rest") then
if (val.Type ~= "Arch-Gun" and val.Type ~= "Arch-Melee" and val.Type ~= "Primary" and
val.Type ~= "Secondary" and val.Type ~= "Melee" and val.Class ~= "Exalted Weapon") then
count = count + 1
if (getFullList ~= nil) then fullList = fullList.."\n# "..val.Name end
end
elseif (val.Type == weaponType and val.Class ~= "Exalted Weapon") then
count = count + 1
if (getFullList ~= nil) then fullList = fullList.."\n# "..val.Name end
end
end
end
if (getFullList ~= nil) then return fullList end
return count
end
--- Gets the weapon class of secondary weapons.
-- @function getSecondaryCategory
-- @param {table} weapon Weapon table
-- @returns {string} Category name
local function getSecondaryCategory(weapon)
local class = getValue(weapon, "Class")
if (class == "Thrown") then
return "Thrown"
elseif (class == "Dual Shotguns" or class == "Shotgun Sidearm") then
return "Shotgun"
else
local trigger = getValue(weapon, "Trigger")
if (trigger == "Semi-Auto" or trigger == "Burst") then
return "Semi-Auto"
elseif (trigger == "Auto" or trigger == "Auto-Spool") then
return "Auto"
end
end
return "Other"
end
--- Gets the weapon class of primary weapons.
-- @function getPrimaryCategory
-- @param {table} weapon Weapon table
-- @returns {string} Category name
local function getPrimaryCategory(weapon)
local class = getValue(weapon, "Class")
if (class == "Shotgun") then
return "Shotgun"
elseif (class == "Bow") then
return "Bow"
elseif (class == "Sniper Rifle") then
return "Sniper"
elseif (class == "Rifle") then
local trigger = getValue(weapon, "Trigger")
if (trigger == "Semi-Auto" or trigger == "Burst") then
return "Semi-Auto"
elseif (trigger == "Auto" or trigger == "Auto-Spool") then
return "Auto"
end
end
return "Other"
end
--- Builds wikitable of all weapons' innate polarities as seen on [[Polarity]].
-- @function p.getPolarityTable
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
function p.getPolarityTable(frame)
local tableResult = { [[
{| style="width: 100%; border-collapse: collapse;" cellpadding="2" border="1"
|+ '''Weapons with Innate Polarities (ignoring Stance and Exilus slots)'''
! colspan="2" |Primaries
! colspan="2" |Secondaries
! colspan="2" |Melees
! colspan="2" |Arch-Guns
! colspan="2" |Arch-Melees]] }
local filterBy = function(weaponType)
return function(weaponEntry)
return (weaponEntry['Type'] == weaponType) and string.len(getValue(weaponEntry, "Polarities")) > 0
end
end
local meleeEntries = p._getWeapons(filterBy('Melee'))
local pistolEntries = p._getWeapons(filterBy('Secondary'))
local primaryEntries = p._getWeapons(filterBy('Primary'))
local archGunEntries = p._getWeapons(filterBy('Arch-Gun'))
local archMeleeEntries = p._getWeapons(filterBy('Arch-Melee'))
local meleeCount = Table.size(meleeEntries)
local pistolCount = Table.size(pistolEntries)
local primaryCount = Table.size(primaryEntries)
local archGunCount = Table.size(archGunEntries)
local archMeleeCount = Table.size(archMeleeEntries)
local maxLen = meleeCount
if (pistolCount > maxLen) then maxLen = pistolCount end
if (primaryCount > maxLen) then maxLen = primaryCount end
if (archGunCount > maxLen) then maxLen = archGunCount end
if (archMeleeCount > maxLen) then maxLen = archMeleeCount end
local traverseOrder = { primaryCount, pistolCount, meleeCount, archGunCount, archMeleeCount }
-- Note: Cannot map tables to their size since it would exceed allocated script time
-- to access these
local countEntries = {
[primaryCount] = primaryEntries,
[pistolCount] = pistolEntries,
[meleeCount] = meleeEntries,
[archGunCount] = archGunEntries,
[archMeleeCount] = archMeleeEntries
}
for i = 1, maxLen, 1 do
table.insert(tableResult, '|-')
-- Adding each row in table
for _, entriesCount in ipairs(traverseOrder) do
if (i <= entriesCount) then
table.insert(tableResult, '| [['..countEntries[entriesCount][i]['Name']..']] ||'..Polarity._pols(countEntries[entriesCount][i]['Polarities']))
else
table.insert(tableResult, '| ||')
end
end
end
table.insert(tableResult, '|}')
return table.concat(tableResult, '\n')
end
--- Builds comparison string between two values.
-- @function p.buildCompareString
-- @param {string, number} firstVal Value used for comparison
-- @param {string, number} secondVal Value used to compare the first value against
-- @param {string} valName Name of statistic that values represent (e.g. "Critical Damage")
-- @param[opt] {number} digits The decimal to round the values by (e.g. if you want to round to two decimal places put in 0.01)
-- @param[opt] {string} unit The values' unit (e.g. "m" or "seconds")
-- @param[opt] {table} compareAdjs Two element table that contains the greater than and less than comparative adjectives (e.g. { "Higher", "Lower" } )
-- @param[opt] {string} start What to start the comparison string by for if you want to increase the bullet level (e.g. "\n***")
-- @returns {string} Resultant wikitext of comparison string
local function buildCompareString(firstVal, secondVal, valName, digits, unit, compareAdjs, start)
if (firstVal == nil or secondVal == nil) then
return ""
end
local firstValStr = firstVal
local secondValStr = secondVal
if (digits ~= nil) then
firstValStr = Math.round(firstVal, digits)
secondValStr = Math.round(secondVal, digits)
end
if (unit ~= nil) then
firstValStr = firstValStr..unit
secondValStr = secondValStr..unit
end
local bigWord = compareAdjs ~= nil and compareAdjs[1] or "Higher"
local smallWord = compareAdjs ~= nil and compareAdjs[2] or "Lower"
local start = start ~= nil and start or "\n**"
if (firstVal > secondVal) then
return start.." "..bigWord.." "..valName.." ("..firstValStr.." vs. "..secondValStr..")"
elseif (secondVal > firstVal) then
return start.." "..smallWord.." "..valName.." ("..firstValStr.." vs. "..secondValStr..")"
else
return ""
end
end
--- Builds damage comparison string between two attacks.
-- @function p.buildComparison
-- @param {table} Attack1 Attack used for comparison
-- @param {table} Attack2 Attack used to compare the first attack against
-- @returns {string} Resultant wikitext of comparison string
local function buildDamageTypeComparisonString(Attack1, Attack2)
local result = ""
--ipairs iterates in the order given in Elements, so IPS is always first
for i, element in ipairs(Elements) do
local damage1 = Attack1.Damage[element]
local damage2 = Attack2.Damage[element]
if (damage1 ~= nil or damage2 ~= nil) then
if (damage1 == nil) then damage1 = 0 end
if (damage2 == nil) then damage2 = 0 end
result = result..buildCompareString(damage1, damage2, Tooltip.getIcon(element, 'DamageTypes').." damage", 0.01, nil, {"Higher", "Lower"}, "\n***")
end
end
return result
end
-- TODO: The above TODO can be done in conjunction with changing all the "Attack1" and "Attack2" keys
-- to a singular "Attack" key with indexed table elements
--- Builds comparison list between two gun weapons.
-- @function p.buildComparison
-- @param {table} Weapon1 Weapon used for comparison
-- @param {table} Weapon2 Weapon used to compare the first weapon against
-- @param {boolean} conclave If true, makes comparison list based on PvP stats, otherwise uses PvE stats; default false
-- @returns {string} Resultant wikitext of comparison list
local function buildGunComparisonString(Weapon1, Weapon2, Conclave)
local result = {}
-- Adding this assignment to support method chaining w/ colon syntax
result.insert = function(self, elem) table.insert(self, elem) return self end
local Att1 = getAttack(Weapon1, Weapon1['TooltipAttackDisplay'] or 'Attack1')
local Att2 = getAttack(Weapon2, Weapon2['TooltipAttackDisplay'] or 'Attack1')
if (Conclave) then
result:insert("* [["..Weapon1.Name.."]], compared to [[Conclave:"..Weapon2.Name.."|"..Weapon2.Name.."]]:")
else
result:insert("* [["..Weapon1.Name.."]] (attack: "..(Att1.AttackName or "Normal").."), compared to [["..Weapon2.Name.."]] (attack: "..(Att2.AttackName or "Normal").."):")
end
local dmgString = ""
dmgString = dmgString..buildCompareString(getValue(Weapon1, "BaseDamage"), getValue(Weapon2, "BaseDamage"), "base damage", 0.01)
dmgString = dmgString..buildDamageTypeComparisonString(Att1, Att2)
if (string.len(dmgString) > 0 and getValue(Weapon1, "BaseDamage") == getValue(Weapon2, "BaseDamage")) then
dmgString = "\n**Equal base damage, but different composition:"..dmgString
end
dmgString = dmgString..buildCompareString(getValue(Weapon1, "TotalDamage"), getValue(Weapon2, "TotalDamage"), "total damage", 0.01)
-- TODO: Find a better way of finding out if an attack is a charge attack; maybe add Trigger = "Charge" to all of those attacks?
-- result = result..buildCompareString(getValue(Weapon1, "ChargeTime", "Attack3"), getValue(Weapon2, "ChargeTime", "Attack3"), "charge time", 0.01, " s", {"Slower", "Faster"})
result:insert(dmgString)
if (not Conclave) then
result:insert(
buildCompareString((Att1.CritChance * 100), (Att2.CritChance * 100), "base [[critical chance]]", 0.01, "%")
):insert(
buildCompareString(Att1.CritMultiplier, Att2.CritMultiplier, "base [[critical multiplier]]", 0.01, "x")
):insert(
buildCompareString((Att1.StatusChance * 100), (Att2.StatusChance * 100), "base [[status chance]]", 0.01, "%")
):insert(
buildCompareString(getValue(Weapon1, "AvgShotDmg"), getValue(Weapon2, "AvgShotDmg"), "[[Damage#Final_Calculations|average damage per shot]] (max Progenitor bonus if applicable)", 0.01)
):insert(
buildCompareString(getValue(Weapon1, "BurstDps"), getValue(Weapon2, "BurstDps"), "[[Damage#Final_Calculations|burst DPS]] (max Progenitor bonus if applicable)", 0.01)
):insert(buildCompareString(getValue(Weapon1, "SustainedDps"), getValue(Weapon2, "SustainedDps"), "[[Damage#Final_Calculations|sustained DPS]] (max Progenitor bonus if applicable)", 0.01)
):insert(
buildCompareString(getValue(Weapon1, "FalloffStart"), getValue(Weapon2, "FalloffStart"), "starting [[Damage Falloff|damage falloff]] distance", 0.1, "m", {"Farther", "Closer"})
):insert(
buildCompareString(getValue(Weapon1, "FalloffEnd"), getValue(Weapon2, "FalloffEnd"), "ending damage falloff distance", 0.1, "m", {"Farther", "Closer"})
):insert(
buildCompareString(getValue(Weapon1, "FalloffReduction") * 100, getValue(Weapon2, "FalloffReduction") * 100, "max damage reduction at ending falloff distance", 0.01, "%", {"Greater", "Lesser"})
)
end
result:insert(
buildCompareString(Att1.FireRate, Att2.FireRate, "[[fire rate]]", 0.01, " round(s)/sec")
):insert(
buildCompareString(Att1.Multishot or 1, Att2.Multishot or 1, "[[multishot]]", 1, " projectile(s)")
):insert(
buildCompareString(Weapon1.Magazine, Weapon2.Magazine, "magazine", 1, " round(s)", {"Larger", "Smaller"})
):insert(
buildCompareString(Weapon1.MaxAmmo, Weapon2.MaxAmmo, "max ammo capacity", 1, " round(s)", {"Larger", "Smaller"})
):insert(
buildCompareString(Weapon1.Reload, Weapon2.Reload, "[[Reload|reload time]]", 0.01, " s", {"Slower", "Faster"})
):insert(
buildCompareString(Weapon1.Spool, Weapon2.Spool, "spool-up", 1, " round(s)", {"Slower", "Faster"})
):insert(
buildCompareString(getValue(Weapon1, "Accuracy"), getValue(Weapon2, "Accuracy"), "[[Accuracy|accurate]]", 0.01, nil, {"More", "Less"})
):insert(
buildCompareString(getValue(Weapon1, "Polarities"), getValue(Weapon2, "Polarities"), "[[Polarity|polarities]]", nil, nil, {"Different", "Different"})
):insert(
buildCompareString(Weapon1.Mastery, Weapon2.Mastery, "[[Mastery Rank]] required", 1)
):insert(
buildCompareString(Weapon1.Disposition, Weapon2.Disposition, "[[disposition]]", 0.01)
)
--Handling Syndicate radial effects
if (Weapon1.SyndicateEffect ~= nil and Weapon2.SyndicateEffect == nil) then
result:insert("\n** Innate [["..Weapon1.SyndicateEffect.."]] effect")
elseif (Weapon2.SyndicateEffect ~= nil and Weapon1.SyndicateEffect == nil) then
result:insert("\n** Lack of an innate [["..Weapon2.SyndicateEffect.."]] effect")
elseif (Weapon1.SyndicateEffect ~= nil and Weapon2.SyndicateEffect ~= nil and
Weapon1.SyndicateEffect ~= Weapon2.SyndicateEffect2) then
result:insert("\n** Different innate [[Syndicate Radial Effects|Syndicate Effect]]: [["..
Weapon1.SyndicateEffect.."]] vs. [["..Weapon2.SyndicateEffect.."]]")
end
return table.concat(result)
end
--- Builds comparison list between two melee or arch-melee weapons.
-- @function p.buildComparison
-- @param {table} Weapon1 Weapon used for comparison
-- @param {table} Weapon2 Weapon used to compare the first weapon against
-- @param {boolean} conclave If true, makes comparison list based on PvP stats, otherwise uses PvE stats; default false
-- @returns {string} Resultant wikitext of comparison list
local function buildMeleeComparisonString(Weapon1, Weapon2, Conclave)
local result = {}
-- Adding this assignment to support method chaining w/ colon syntax
result.insert = function(self, elem) table.insert(self, elem) return self end
local Att1 = getAttack(Weapon1, Weapon1['TooltipAttackDisplay'] or 'Attack1')
local Att2 = getAttack(Weapon2, Weapon2['TooltipAttackDisplay'] or 'Attack1')
if (Conclave) then
result:insert("* "..Weapon1.Name..", compared to [[Conclave:"..Weapon2.Name.."|"..Weapon2.Name.."]]:")
else
result:insert("* "..Weapon1.Name.." (attack: "..(Att1.AttackName or "Normal").."), compared to [["..Weapon2.Name.."]] (attack: "..(Att2.AttackName or "Normal").."):")
end
local dmgString = ""
dmgString = dmgString..buildCompareString(getValue(Weapon1, "BaseDamage"), getValue(Weapon2, "BaseDamage"), "base damage", 0.01)
dmgString = dmgString..buildDamageTypeComparisonString(Att1, Att2)
if (string.len(dmgString) > 0 and getValue(Weapon1, "BaseDamage") == getValue(Weapon2, "BaseDamage")) then
dmgString = "\n**Equal base damage, but different composition:"..dmgString
end
result:insert(dmgString)
if (not Conclave) then
result:insert(
buildCompareString((Att1.CritChance * 100), (Att2.CritChance * 100), "[[critical chance]]", 0.01, "%")
):insert(
buildCompareString(Att1.CritMultiplier, Att2.CritMultiplier, "[[critical multiplier]]", 0.01, "x")
):insert(
buildCompareString((Att1.StatusChance * 100), (Att2.StatusChance * 100), "[[status chance]]", 0.01, "%")
):insert(
buildCompareString(getValue(Weapon1, "MeleeRange"), getValue(Weapon2, "MeleeRange"), "Range", 0.01, " m")
):insert(
buildCompareString(Weapon1.Disposition, Weapon2.Disposition, "[[disposition]]", 0.01)
)
end
result:insert(
buildCompareString(Att1.FireRate, Att2.FireRate, "[[attack speed]]", 0.01)
):insert(
buildCompareString(getValue(Weapon1, "Polarities"), getValue(Weapon2, "Polarities"), "[[Polarity|polarities]]", nil, nil, {"Different", "Different"})
):insert(
buildCompareString(Weapon1.Mastery, Weapon2.Mastery, "[[Mastery Rank]] required", 1)
)
-- Quick fix to passing in an empty string for polarity when comparing with an arch-melee weapon
if (Weapon1.Type ~= "Arch-Melee" and Weapon2.Type ~= "Arch-Melee") then
result:insert(
buildCompareString(getValue(Weapon1, "ComboDur"), getValue(Weapon2, "ComboDur"), "[[Melee Combo|Combo Duration]]", 1, " s")
):insert(
buildCompareString(getValue(Weapon1, "BlockAngle"), getValue(Weapon2, "BlockAngle"), "Block Angle", 1, "°")
):insert(
buildCompareString(Polarity._polarity(Weapon1.StancePolarity), Polarity._polarity(Weapon2.StancePolarity), "[[Stance]] Polarity", nil, nil, {"Different", "Different"})
)
end
return table.concat(result)
end
--- Builds comparison list between two weapons in PvE.
-- @function p.buildComparison
-- @param {table} frame Frame object
-- @returns {string} Resultant wikitext of comparison list
function p.buildComparison(frame)
local weaponName1 = frame.args[1]
local weaponName2 = frame.args[2]
assert(weaponName1 ~= '' and weaponName2 ~= '', 'p.buildComparison(frame): Must compare two weapons')
local Weapon1 = p._getWeapon(weaponName1)
local Weapon2 = p._getWeapon(weaponName2)
local comparisonString = ''
if (Weapon1.Type == 'Melee' or Weapon1.Type == 'Arch-Melee') then
comparisonString = buildMeleeComparisonString(Weapon1, Weapon2)
else
comparisonString = buildGunComparisonString(Weapon1, Weapon2)
end
return comparisonString..'[[Category:Automatic Comparison]]'
end
--- Builds comparison list between two weapons in PvP ([[Conclave]]).
-- @function p.buildComparison
-- @param {table} frame Frame object
-- @returns {string} Resultant wikitext of comparison list
function p.buildConclaveComparison(frame)
local weaponName1 = frame.args[1]
local weaponName2 = frame.args[2]
assert(weaponName1 ~= '' and weaponName2 ~= '', 'p.buildConclaveComparison(frame): Must compare two weapons')
local Weapon1 = p._getConclaveWeapon(weaponName1)
local Weapon2 = p._getConclaveWeapon(weaponName2)
local comparisonString = ''
if (Weapon1.Type == 'Melee' or Weapon1.Type == 'Arch-Melee') then
comparisonString = buildMeleeComparisonString(Weapon1, Weapon2, true)
else
comparisonString = buildGunComparisonString(Weapon1, Weapon2, true)
end
return comparisonString..'[[Category:Automatic Comparison]]'
end
--- Adds weapon categories.
-- @function p.buildAutoboxCategories
-- @param {table} frame Frame object
-- @returns {string} Wikitext of category links
function p.buildAutoboxCategories(frame)
local WeapName = frame.args ~= nil and frame.args[1] or frame
local Weapon = p._getWeapon(WeapName)
local result = { "[[Category:Automatic Weapon Box]][[Category:Weapons]]" }
if (Weapon == nil or (Weapon.IgnoreCategories ~= nil and Weapon.IgnoreCategories)) then
return ""
end
table.insert(result, CATEGORY_MAP[Weapon['Class']]..CATEGORY_MAP[Weapon['Trigger']]..CATEGORY_MAP[Weapon['Type']])
-- Adding appropriate categories to page based on weapon's categorical traits
for _, trait in pairs(Weapon['Traits'] or {}) do
table.insert(result, CATEGORY_MAP[trait])
end
local bestPercent, bestElement = getDamageBias(Weapon[Weapon['TooltipAttackDisplay'] or 'Attack1'])
if (bestElement == "Impact" or bestElement == "Puncture" or bestElement == "Slash") then
if (bestPercent > .38) then
table.insert(result, "[[Category:"..bestElement.." Damage Weapons]]")
else
table.insert(result, "[[Category:Balanced Physical Damage Weapons]]")
end
end
for key, value in Table.skpairs(attack.Damage) do
if (key ~= "Impact" and key ~= "Puncture" and key ~= "Slash") then
table.insert(result, "[[Category:"..key.." Damage Weapons]]")
end
end
return table.concat(result)
end
--- Builds a table that lists out all weapons with a certain damage type
-- and the percentage that it makes up of their base damage of the attack specified
-- in their tooltip on the wiki.
-- @function p.buildDamageTypeTable
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
function p.buildDamageTypeTable(frame)
local damageType = frame.args ~= nil and frame.args[1] or frame
local Weapons = {}
local WeapArray = p._getWeapons(function(weaponEntry)
-- Want to ignore Kitgun entries which have 0 as placeholder damage values
if (weaponEntry['Type'] == 'Kitgun' or weaponEntry['Type'] == 'Primary Kitgun') then
return false
end
local attackEntry = getAttack(weaponEntry, weaponEntry['TooltipAttackDisplay'] or 'Attack1')
assert(attackEntry ~= nil, 'p.buildDamageTypeTable(frame): "'..weaponEntry.Name..'" has no attack entry for "Attack1" or attack in "TooltipAttackDisplay"')
local dmg, element = getDamageBias(attackEntry)
return element == damageType
end)
local weapLink = function(weap)
return weap.Link ~= nil and '[['..weap.Link..'|'..weap.Name..']]' or '[['..weap.Name..']]'
end
local procString = makeDTooltip(damageType, true)
local procShort = makeDTooltip(damageType)
local result = ''
local tHeader = string.format([[
{| class = "listtable sortable" style="margin:auto;"
|+ '''Weapons with %s damage'''
|-
! Name !! Type !! Class !! %s !! %s%%
]], damageType, procString, procShort)
local tRows = {}
for i, Weapon in pairs(WeapArray) do
local thisRow = [[
|-
| {{Weapon|%s}} || %s || %s || %s || data-sort-value="%s" | %s]]
local weaponLink = weapLink(Weapon)
assert(Weapon.Type ~= nil, 'p.buildDamageTypeTable(frame): "'..weaponLink..'" has a nil value for Type"')
local attack = Weapon['TooltipAttackDisplay'] or 'Attack1'
local damageTypeVal = getValue(Weapon, damageType, attack)
local damageBias = getValue(Weapon, 'DamageBias', attack)
thisRow = string.format(thisRow,
Weapon.Name,
Weapon.Type,
getValue(Weapon, 'Class'),
damageTypeVal,
string.match(damageBias, '(%d*%.?%d+)%%'),
damageBias
)
table.insert(tRows, thisRow)
end
result = tHeader..table.concat(tRows, '\n')..'\n|}'
return frame:preprocess(result)
end
--- Builds a list of weapons' mastery requirements as seen on [[Template:EquipmentUnlock]].
-- @function p.getMasteryShortList
-- @param {table} frame Frame object w/ first argument being a string weaponType
-- @returns {string} Wikitext of resultant list
function p.getMasteryShortList(frame)
local weaponType = frame.args[1]
local masteryRank = tonumber(frame.args[2])
local checkTypeAndMastery = function(x) return x.Type == weaponType and x.Mastery == masteryRank end
local weapArray = p._getWeapons(checkTypeAndMastery)
local result = ''
local name = ''
local shortList = p._shortLinkList(weapArray, true)
for i, pair in Table.skpairs(shortList) do
if (string.len(result) > 0) then result = result..' • ' end
result = result..pair
end
return frame:preprocess(result)
end
--- Builds a disposition wikitable as seen on [[Riven Mods/Weapon Dispos]].
-- @function p.getRivenDispositionTable
-- @param {table} frame Frame object w/ first argument being a string weaponType
-- @returns {string} Wikitext of resultant wikitable
function p.getRivenDispositionTable(frame)
local weaponType = frame.args[1]
local ranges = { 1550, 1300, 1100, 899, 699, 499 }
local result = {'{| class="article-table" border="0" cellpadding="1" cellspacing="1" style="width: 100%" \n|-'}
for i = 1, 5 do
table.insert(result, '\n! scope="col" style="text-align:center;"|'..Icon._Dis(ranges[i]/1000))
end
table.insert(result, '\n|-')
for dispoRating = 5, 1, -1 do
table.insert(result, '\n| style="vertical-align:top; font-size:small" |')
local checkRightDispo = function(weaponEntry)
if (weaponEntry['Type'] ~= nil and weaponEntry['Disposition'] ~= nil) then
return (weaponType == 'All' or weaponEntry['Type'] == weaponType) and
math.min(math.floor(5 * (weaponEntry['Disposition'] - (weaponEntry['Disposition'] < 1 and 0.3 or 0.309))), 5) == dispoRating
end
return false
end
-- Filtering out weapons that are between certain disposition ranges
local weapArray = p._getWeapons(checkRightDispo)
-- Building a list of weapons with a particular disposition rating (e.g. 5 dots)
local weaponDispoList = {}
-- Want to iterate in descending order so highest disposition weapon is near the top of table
local descendingOrder = function(a, b)
-- a and b are number indexes
return weapArray[a]['Disposition'] > weapArray[b]['Disposition']
end
for _, weaponEntry in Table.skpairs(weapArray, descendingOrder) do
table.insert(weaponDispoList, '\n* [['..weaponEntry['Name']..']] ('..weaponEntry['Disposition']..')')
end
table.insert(result, table.concat(weaponDispoList))
end
table.insert(result, '\n|}')
return table.concat(result)
end
--- Builds a list of PvP weapons.
-- @function p.getConclaveList
-- @param {table} frame Frame object w/ first argument being a string weaponType
-- @returns {string} Wikitext of resultant list
function p.getConclaveList(frame)
local weaponType = frame.args[1]
local weapArray = p._getWeapons(function(x) return x.Type == weaponType and x.Conclave end)
local result = ''
local shortList = p._shortLinkList(weapArray, false)
for i, pair in Table.skpairs(shortList) do
result = result..'\n* '..pair
end
return result
end
--- Builds a row forcomparison table as seen on [[Weapon Comparison]].
-- @function buildCompRow
-- @param {table} tableHeaders Wikitable's header names an the specific getter function that it will pull from
-- (e.g. { { "CritChance", "[[Critical Chance|Crit<br />Chance]]" }, { "CritMultiplier", "[[Critical Multiplier|Crit Multi]]" } } )
-- @param {table} Weapon A weapon table entry as pulled from <code>/data</code>
-- @returns {string} Wikitext of resultant wikitable row
local function buildCompRow(tableHeaders, Weapon)
local styleString = ''--'border: 1px solid lightgray;'
local result = {}
local valueName = nil
local attackName = nil
for i, headerLine in ipairs(tableHeaders) do
valueName = headerLine[1]
attackName = headerLine[3] ~= nil and headerLine[3] or nil
local value = getValue(Weapon, valueName, attackName)
if (type(value) == 'number') then
value = Math.round(value, 0.01)
end
table.insert(result, 'style="'..styleString..'"|'..(value or 'N/A'))
end
return '|-\n|'..table.concat(result, '||')
end
--- Builds comparison table as seen on [[Weapon Comparison]].
-- @function buildCompTable
-- @param {table} tableHeaders Wikitable's header names an the specific getter function that it will pull from
-- (e.g. { { "CritChance", "[[Critical Chance|Crit<br />Chance]]" }, { "CritMultiplier", "[[Critical Multiplier|Crit Multi]]" } } )
-- @param {table} Weapons Array of weapon table entries as pulled from <code>/data</code>
-- @returns {string} Wikitext of resultant wikitable
local function buildCompTable(tableHeaders, Weapons)
local styleString = 'border: 1px solid black;border-collapse: collapse;'
local dataSortType
local result = {}
table.insert(result, '{| cellpadding="1" cellspacing="0" class="listtable sortable" style="font-size:11px;"')
for _, headerLine in ipairs(tableHeaders) do
dataSortType = ''
if (headerLine[1] == 'DamageBias' or headerLine[1] == 'CritMultiplier' or
headerLine[1] == 'HeadshotMultiplier') then
dataSortType = 'data-sort-type="number"'
end
table.insert(result, '! style="'..styleString..'" '..dataSortType..'|'..headerLine[2])
end
for _, Weap in pairs(Weapons) do
local rowStr = buildCompRow(tableHeaders, Weap)
table.insert(result, rowStr)
end
table.insert(result, '|}[[Category:Automatic Comparison Table]]')
return table.concat(result, '\n')
end
--- Builds comparison table of gun stats as seen on [[Weapon Comparison]].
-- @function p.getCompTableGuns
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
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 == "Primary") then WeapArray = p._getWeapons(function(x)
if (getValue(x, "Type") == "Primary") then
if (Type ~= nil) then return getPrimaryCategory(x) == Type else return true end
end
return false
end)
elseif (Catt == "Secondary") then WeapArray = p._getWeapons(function(x)
if (getValue(x, "Type") == "Secondary") then
if (Type ~= nil) then return getSecondaryCategory(x) == Type else return true end
end
return false
end)
elseif (Catt == "Arch-Gun") then WeapArray = p._getWeapons(function(x)
return getValue(x, "Type") == "Arch-Gun"
end)
elseif (Catt == "Arch-Gun (Atmosphere)") then WeapArray = p._getWeapons(function(x)
return getValue(x, "Type") == "Arch-Gun (Atmosphere)"
end)
elseif (Catt == "Robotic") then WeapArray = p._getWeapons(function(x)
return getValue(x, "Type") == "Robotic"
end)
elseif (Catt == "Amp") then WeapArray = p._getWeapons(function(x)
return getValue(x, "Type") == "Amp"
end)
else
error('p.getCompTableGuns(frame): Wrong gun weapon class'..
'(use "Primary", "Secondary", "Arch-Gun", "Arch-Gun (Atmosphere)", "Robotic", or "Amp")'..
'[[Category:Invalid Comp Table]]')
end
local tableHeaders = { {"NameLink", "Name"} }
-- Adding this assignment to support method chaining w/ colon syntax
tableHeaders.insert = function(self, elem) table.insert(self, elem) return self end
tableHeaders:insert({ "Trigger", "[[Fire Rate|Trigger]]" })
tableHeaders:insert({ "AttackName", "Attack" })
tableHeaders:insert({ "DamageBias", "Main<br/>Element" })
tableHeaders:insert({ "BaseDamage", "Base<br/>[[Damage|Dmg]]" })
tableHeaders:insert({ "CritChance", "[[Critical Chance|Crit]]" })
tableHeaders:insert({ "CritMultiplier", "[[Critical multiplier|Crit<br/>Dmg]]" })
tableHeaders:insert({ "AvgShotDmg", "Avg<br/>Shot" })
tableHeaders:insert({ "BurstDps", "Burst<br/>DPS" })
tableHeaders:insert({ "SustainedDps", "Sust<br/>DPS" })
tableHeaders:insert({ "StatusChance", "[[Status Chance|Status]]" })
tableHeaders:insert({ "AvgProcPerSec", "[[Status Chance|Avg. Procs]]/<br/>s" })
tableHeaders:insert({ "CompTableFireRate", "[[Fire Rate|Fire<br/>Rate]]" })
tableHeaders:insert({ "Disposition", "[[Riven Mods#Disposition|Dispo]]" })
tableHeaders:insert({ "Mastery", "[[Mastery Rank|MR]]" })
tableHeaders:insert({ "Magazine", "[[Ammo#Magazine Capacity|Mag<br/>Size]]" })
tableHeaders:insert({ "MaxAmmo", "[[Ammo|Ammo<br/>Cap]]" })
tableHeaders:insert({ "Reload", "[[Reload Speed|Reload]]" })
tableHeaders:insert({ "ShotType", "Shot<br/>Type" })
tableHeaders:insert({ "PunchThrough", "[[Punch Through|PT]]" })
tableHeaders:insert({ "Accuracy", "[[Accuracy]]" })
tableHeaders:insert({ "IntroducedDate", "Intro" })
return buildCompTable(tableHeaders, WeapArray)
end
--- Builds comparison table of gun Conclave stats as seen on [[Weapon Comparison/Conclave]].
-- @function p.getCompTableConclaveGuns
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
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 == "Primary") then WeapArray = p._getConclaveWeapons(function(x)
if (getValue(x, "Type") == "Primary") then
if (Type ~= nil) then return getPrimaryCategory(x) == Type else return true end
end
return false
end)
elseif (Catt == "Secondary") then WeapArray = p._getConclaveWeapons(function(x)
if (getValue(x, "Type") == "Secondary") then
if (Type ~= nil) then return getSecondaryCategory(x) == Type else return true end
end
return false
end)
else
error('p.getCompTableConclaveGuns(frame): Wrong gun weapon class for Conclave (use "Primary" or "Secondary")[[Category:Invalid Comp Table]]')
end
local tableHeaders = { {"Name", "Name"} }
-- Adding this assignment to support method chaining w/ colon syntax
tableHeaders.insert = function(self, elem) table.insert(self, elem) return self end
tableHeaders:insert({ "Trigger", "[[Fire Rate|Trigger Type]]" })
tableHeaders:insert({ "DamageBias", "Main<br/>Element" })
tableHeaders:insert({ "BaseDamage", "[[Damage]]" })
tableHeaders:insert({ "HeadshotMultiplier", "HS Multiplier" })
tableHeaders:insert({ "ShotType", "Shot<br/>Type" })
tableHeaders:insert({ "CompTableFireRate", "[[Fire Rate]]" })
tableHeaders:insert({ "Magazine", "[[Ammo#Magazine Capacity|Magazine Size]]" })
tableHeaders:insert({ "Reload", "[[Reload Speed|Reload Time]]" })
tableHeaders:insert({ "Mastery", "[[Mastery Rank]]" })
tableHeaders:insert({ "IntroducedDate", "Introduced" })
return buildCompTable(tableHeaders,WeapArray)
end
--- Builds comparison table of melee stats as seen on [[Weapon Comparison]].
-- @function p.getCompTableMelees
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
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 tableHeaders = { {"NameLink", "Name"} }
-- Adding this assignment to support method chaining w/ colon syntax
tableHeaders.insert = function(self, elem) table.insert(self, elem) return self end
tableHeaders:insert({ "Class", "Type" })
tableHeaders:insert({ "DamageBias", "Main<br/>Element" })
tableHeaders:insert({ "BaseDamage", "[[Damage|Normal]]" })
tableHeaders:insert({ "HeavyAttack", "[[Melee#Heavy Attack|Heavy]]" })
tableHeaders:insert({ "SlamAttack", "[[Melee#Slam Attack|Slam]]" })
tableHeaders:insert({ "SlideAttack", "[[Melee#Slide Attack|Slide]]" })
tableHeaders:insert({ "MeleeRange", "[[Melee#Range|Range]]" })
tableHeaders:insert({ "SlamRadius", "[[Melee#Slam Attack|Slam Radius]]" })
tableHeaders:insert({ "FireRate", "[[Attack Speed|Speed]]" })
tableHeaders:insert({ "CritChance", "[[Critical Chance|Crit]]" })
tableHeaders:insert({ "CritMultiplier", "[[Critical multiplier|Crit Dmg]]" })
tableHeaders:insert({ "StatusChance", "[[Status Chance|Status]]" })
tableHeaders:insert({ "Disposition", "[[Riven Mods#Disposition|Dispo]]" })
tableHeaders:insert({ "FollowThrough", "[[Follow Through|Follow<br />Through]]" })
tableHeaders:insert({ "BlockAngle", "[[Blocking|Block<br />Angle]]" })
tableHeaders:insert({ "Mastery", "[[Mastery Rank|MR]]" })
tableHeaders:insert({ "StancePolarity", "[[Stance]]" })
tableHeaders:insert({ "IntroducedDate", "Intro" })
return buildCompTable(tableHeaders, WeapArray)
end
--- Builds comparison table of melee conclave stats as seen on [[Weapon Comparison/Conclave]].
-- @function p.getCompTableConclaveMelees
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
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 tableHeaders = { {"Name", "Name"} }
-- Adding this assignment to support method chaining w/ colon syntax
tableHeaders.insert = function(self, elem) table.insert(self, elem) return self end
tableHeaders:insert({ "Class", "Type" })
tableHeaders:insert({ "BaseDamage", "[[Damage|Normal]]" })
tableHeaders:insert({ "SlideAttack", "[[Melee#Slide Attack|Slide]]" })
tableHeaders:insert({ "FireRate", "[[Attack Speed]]" })
tableHeaders:insert({ "Mastery", "[[Mastery_Rank|Mastery Rank]]" })
tableHeaders:insert({ "StancePolarity", "[[Stance]]" })
tableHeaders:insert({ "IntroducedDate", "Introduced" })
return buildCompTable(tableHeaders, WeapArray)
end
--- Builds comparison table of arch-melee stats as seen on [[Weapon Comparison]].
-- @function p.getCompTableArchMelees
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
function p.getCompTableArchMelees(frame)
local WeapArray = {}
WeapArray = p._getWeapons(function(x)
return getValue(x, "Type") == "Arch-Melee"
end)
local tableHeaders = { {"NameLink", "Name"} }
-- Adding this assignment to support method chaining w/ colon syntax
tableHeaders.insert = function(self, elem) table.insert(self, elem) return self end
tableHeaders:insert({ "DamageBias", "Main<br/>Element" })
tableHeaders:insert({ "BaseDamage", "[[Damage|Normal]]" })
tableHeaders:insert({ "FireRate", "[[Attack Speed]]" })
tableHeaders:insert({ "CritChance", "[[Critical Chance]]" })
tableHeaders:insert({ "CritMultiplier", "[[Critical multiplier|Critical Damage]]" })
tableHeaders:insert({ "StatusChance", "[[Status Chance]]" })
tableHeaders:insert({ "Mastery", "[[Mastery Rank]]" })
tableHeaders:insert({ "IntroducedDate", "Introduced" })
return buildCompTable(tableHeaders, WeapArray)
end
--- Builds comparison table of projectile flight speeds as seen on [[Projectile Speed]].
-- @function p.getCompTableSpeedGuns
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
function p.getCompTableSpeedGuns(frame)
local weaponType = frame.args ~= nil and frame.args[1]
local weaponClass = frame.args ~= nil and frame.args[2] or nil
if (weaponClass == "All") then weaponClass = nil end
local weaponList = {}
if (weaponType == "Primary") then
weaponList = p._getWeapons(function(x)
if (getValue(x, "Type") == "Primary") then
return (weaponClass ~= nil) and getPrimaryCategory(x) == weaponClass or true
end
return false
end)
elseif (weaponType == "Secondary") then
weaponList = p._getWeapons(
function(x)
if (getValue(x, "Type") == "Secondary") then
return (weaponClass ~= nil) and getSecondaryCategory(x) == weaponClass or true
end
return false
end)
elseif (weaponType == "Robotic") then
weaponList = p._getWeapons(
function(x)
return getValue(x, "Type") == "Robotic"
end)
elseif (weaponType == "Arch-Gun") then
weaponList = p._getWeapons(
function(x)
return getValue(x, "Type") == "Arch-Gun"
end)
else
error('p.getCompTableSpeedGuns(frame): Wrong gun weapon class '..
'(use "Primary", "Secondary", "Robotic", or "Arch-Gun")[[Category:Invalid Comp Table]]')
end
-- special sorting for projectile weapons
local projectileWeaponList = {}
for k, Weapon in ipairs(weaponList) do
local shotType = getValue(Weapon, "ShotType")
if (shotType == "Projectile" or shotType == "Thrown") then
table.insert(projectileWeaponList, Weapon)
end
end
local tableHeaders = { { "NameLink", "Name" } }
table.insert(tableHeaders,{ "Class", "Class" })
table.insert(tableHeaders,{ "ShotSpeed", "Flight Speed" })
return buildCompTable(tableHeaders, projectileWeaponList)
end
--- Builds comparison table of glaive melees' projectile flight speeds as seen on [[Projectile Speed]].
-- @function p.getCompTableSpeedMelees
-- @param {table} frame Frame object
-- @returns {string} Wikitext of resultant wikitable
function p.getCompTableSpeedMelees(frame)
local weaponList = p._getWeapons(
function(x)
return getValue(x, "Class") == "Glaive"
end)
local tableHeaders = { {"NameLink", "Name"} }
table.insert(tableHeaders, { "ShotSpeed", "Flight Speed", "Attack2" })
return buildCompTable(tableHeaders, weaponList)
end
return p