Weapons/infobox builds the infobox on weapon pages and adds the appropriate category pages.
Documentation
Package items
weapons.buildInfobox(frame)
(function)- Builds the weapon infobox.
- Parameter:
frame
Frame object (table) - Returns: Wikitext of infobox (string)
- Created with Docbunto
See Also
Code
--- '''Weapons/infobox''' builds the infobox on weapon pages and adds the
-- appropriate category pages.<br />
--
-- @module weapons
-- @alias p
-- @author [[User:FINNER|FINNER]]
-- @attribution [[User:Gigamicro|Gigamicro]]
-- @attribution [[User:Cephalon Scientia|Cephalon Scientia]]
-- @image
-- @require [[Module:Weapons/data]]
-- @require [[Module:Weapons/Conclave/data]]
-- @require [[Module:Math]]
-- @require [[Module:Polarity]]
-- @require [[Module:Tooltips]]
-- @require [[Module:Vendors]]
-- @require [[Module:Baro]]
-- @require [[Module:InfoboxBuilder]]
-- @release stable
-- <nowiki>
local Math = require([[Module:Math]])
local Polarity = require([[Module:Polarity]])
local Tooltips = require([[Module:Tooltips]])
local Vendor = require([[Module:Vendors]])
local Baro = require([[Module:Baro]])
local InfoboxBuilder = require([[Module:InfoboxBuilder]])
local function override(arg)
return arg and arg..'[[Category:InfoboxOverride]]'
end
-- TODO: Replace with Weapons._statRead()
local function format(weapon, str)
return weapon and weapon..str
end
return {
--- Builds the weapon infobox.
-- @function p.buildInfobox
-- @param {table} frame Frame object
-- @return {string} Wikitext of infobox
buildInfobox = function(frame)
local args = frame.args;
local name = mw.text.decode(args['Name']);
local data = {}
local weapon = {}
if args['Conclave'] == 'true' then
data = require([[Module:Weapons/data]]){ usePvPData = true };
else
data = require([[Module:Weapons/data]]);
end
weapon = data[name]
assert(weapon ~= nil, ('p.buildInfobox(frame): weapon with name "%s" does not exist in %s')
:format(name, args['Conclave'] == 'true' and '[[Module:Weapons/Conclave/data]]' or '[[Module:Weapons/data]]'))
-- TODO: Use Weapons._statRead() and Weapons._statFormat() instead for reading data from M:Weapons/data
local dispo = tonumber(override(args.Disposition)) or weapon.Disposition
if dispo then
dispo = math.min(math.floor(5 * (dispo - (dispo < 1 and 0.3 or 0.309))), 5)
dispo = ('<span style="font-size:15px;display:inline;position:relative;top:2px">%s%s</span> (%.2fx)')
:format(('⬤'):rep(dispo), ('◯'):rep(5-dispo), tonumber(override(args.Disposition)) or weapon.Disposition)
end
local users = ''
if weapon.Users then
for _, user in ipairs(weapon.Users) do
users = users..'[['..user..']]<br />'
end
users = string.sub(users, 1, -7)
end
local family = {}
if weapon.Family then
for _, weap in pairs(data[weapon.Slot]) do
if weap.Family == weapon.Family and weap.Name ~= name then
table.insert(family, Tooltips.getFullTooltip(weap.Name, 'Weapons'))
end
end
table.sort(family)
family = table.concat(family, '<br />')
end
local tradableTextMap = {
[0] = "{{text|red|Untradeable}}[[Category:Untradeable Weapons]]",
[1] = "[[File:TradableIcon.png|x32px|class=icon dark-invert]] '''[[Trading|{{text|green|%s}}]][[Category:Tradeable Weapons]]<br />(unranked w/ no Forma or Catalyst)'''",
[2] = "[[File:TradableIcon.png|x32px|class=icon dark-invert]] '''[[Trading|{{text|green|%s}}]][[Category:Tradeable Weapons]]<br />(parts and/or blueprint only)'''",
[3] = "[[File:TradableIcon.png|x32px|class=icon dark-invert]] '''[[Trading|{{text|green|%s}}]][[Category:Tradeable Weapons]]<br />(indirectly through [[Lich System|Lich]] trading)'''",
[4] = "[[File:TradableIcon.png|x32px|class=icon dark-invert]] '''[[Trading|{{text|green|%s}}]][[Category:Tradeable Weapons]]<br />(only fully built components, not blueprints)'''",
[5] = "[[File:TradableIcon.png|x32px|class=icon dark-invert]] '''[[Trading|{{text|green|%s}}]][[Category:Tradeable Weapons]]<br />(indirectly, comes with parent companion)'''",
}
local Infobox = InfoboxBuilder('MediaWiki:Custom-Weapons/i18n.json', 'MediaWiki:Custom-General/i18n.json')
:tag('title')
:tag('default')
:tag('b'):wikitext(name):done()
:done()
:done()
:tag('image'):attr('source', 'Image')
:tag('default'):wikitext(weapon.Image or 'Panel.png'):done()
:done()
:group()
:caption('Tradeable', weapon.Tradable and tradableTextMap[weapon.Tradable] or tradableTextMap[0], 'tradable')
:caption('UniqueUpgrade', weapon.IsLichWeapon and 'Innate [[Lich System#Notes|%s]]</span>' or '', 'progenitor-bonus')
:caption('CodexSecret', weapon.CodexSecret and '[[Codex|%s]][[Category:Codex Secret]]' or nil, 'codex-secret')
:done()
:header('%s', 'general-information')
:row('Class', '[[File:MiniMapMod.png|x18px|link=Mods#Mod_List]] [[Mods#Mod_List|%s]]', weapon.Class and '[[:Category:'..weapon.Class..'|'..weapon.Class..']][[Category:'..weapon.Class..']]', 'type')
:row('Mastery', '[[File:MasterySigilClear.png|x18px|link=Mastery Rank]] [[Mastery Rank|%s]]', weapon.Mastery or '0', 'mastery')
:row('Slot', '[[File:TopWeapon.png|x18px|link=Weapons]] [[Weapons|%s]]', weapon.Slot and '[[:Category:'..weapon.Slot..' Weapons|'..weapon.Slot..']][[Category:'..weapon.Slot..' Weapons]]', 'slot')
:row('Trigger', '[[Trigger Type|%s]]', weapon.Trigger == 'Held' and weapon.Trigger..'[[Category:Continuous Weapons]]' or weapon.Trigger, 'trigger-type')
:group():header('%s', 'utility')
:row('Accuracy', '[[Accuracy|%s]]', weapon.Accuracy, 'accuracy')
:row('AmmoType', '[[Ammo#Ammo Packs|%s]]', ((weapon.Slot == 'Melee' or weapon.Slot == 'Arch-Melee') and '') or (weapon.Slot == 'Secondary' and 'Pistol') or (weapon.Slot == 'Arch-Gun (Atmosphere)' and 'Heavy') or weapon.AmmoType or weapon.Class, 'ammo-type')
:row('BlockAngle', '[[Melee#Blocking|%s]]', format(weapon.BlockAngle, '°'), 'block-angle')
:row('ComboDur', '[[Melee|%s]]', format(weapon.ComboDur, ' s'), 'combo-duration')
:row('Disposition', '[[Riven_Mods#Disposition|%s]]', dispo, 'disposition')
:row('FireRate', weapon.Slot == 'Melee' and '[[Attack Speed|%s]]' or '[[Fire Rate|%s]]', weapon.Attacks[1].FireRate,
weapon.Slot == 'Melee' and 'attack-speed' or 'fire-rate')
:row('FollowThrough', '[[Melee#Follow_Through|%s]]', weapon.FollowThrough and Math.round(100*weapon.FollowThrough, 0.01)..'%', 'follow-through')
:row('MeleeRange', '%s', format(weapon.MeleeRange, ' m'), 'range')
:row('NoiseLevel', '[[Noise Level|%s]]', weapon.Attacks[1].IsSilent and 'Silent[[Category:Silent]]' or 'Alarming[[Category:Alarming]]', 'noise-level')
:row('Magazine', '[[Ammo#Magazine Capacity|%s]]', weapon.Magazine, 'magazine-size')
:row('MaxAmmo', '[[Ammo#Ammo Maximum|%s]]', weapon.MaxAmmo and Math.formatnum(weapon.MaxAmmo), 'max-ammo')
:row('Reload', '[[Reload|%s]]', format(weapon.Reload, ' s'), 'reload-time')
:row('ReloadDelay', '[[Reload|%s]]', format(weapon.ReloadDelay, ' s'), 'reload-delay')
:row('ReloadRate', '[[Reload|%s]]', format(weapon.ReloadRate, ' rounds/s'), 'reload-rate')
:row('ShotSpeed', '[[Projectile Speed|%s]]', format(weapon.Attacks[1].ShotSpeed, ' m/s'), 'projectile-speed')
:row('ShotType', '%s', weapon.Attacks[1].ShotType, 'projectile-type')
:row('SniperComboReset', '[[Sniper Rifle|%s]]', format(weapon.SniperComboReset, ' s'), 'combo-decay')
:row('SniperComboMin', '[[Sniper Rifle|%s]]', format(weapon.SniperComboMin, ' shots'), 'combo-min')
:row('Spool', '[[Fire Rate#Auto-Spool Weapons|%s]]', weapon.Spool, 'spool-up-rate')
:row('Zoom', '[[Zoom|%s]]', weapon.Zoom and table.concat(weapon.Zoom, '<br />'), 'zoom')
:done()
local horiz, elems, total, plur, plurDT, attack
for i, attackEntry in ipairs(weapon.Attacks) do
attack = 'Attack'..i
elems = {}
plur = -1
for dt, dmg in pairs(attackEntry.Damage) do
if plur < dmg then plur = dmg; plurDT = dt end
if dt ~= 'Impact' and dt ~= 'Puncture' and dt ~= 'Slash' then
table.insert(elems, dt)
end
end
total = (override(args[attack..'Impact']) or attackEntry.Damage.Impact or 0) +
(override(args[attack..'Puncture']) or attackEntry.Damage.Puncture or 0) +
(override(args[attack..'Slash']) or attackEntry.Damage.Slash or 0)
horiz = mw.html.create('group'):attr('layout', 'horizontal')
:row(attack..'Impact', nil, attackEntry.Damage.Impact and Tooltips.getImage('Impact', 'DamageTypes', true)..Math.formatnum(attackEntry.Damage.Impact), 'impact')
:row(attack..'Puncture', nil, attackEntry.Damage.Puncture and Tooltips.getImage('Puncture', 'DamageTypes', true)..Math.formatnum(attackEntry.Damage.Puncture), 'puncture')
:row(attack..'Slash', nil, attackEntry.Damage.Slash and Tooltips.getImage('Slash', 'DamageTypes', true)..Math.formatnum(attackEntry.Damage.Slash), 'slash')
for _, elem in ipairs(elems) do
horiz:row(attack..elem, nil, attackEntry.Damage[elem] and Tooltips.getImage(elem, 'DamageTypes', true)..Math.formatnum(attackEntry.Damage[elem]), elem)
total = total + (override(args[attack..elem]) or attackEntry.Damage[elem])
end
local multishot = attackEntry.Multishot or 1
Infobox:group():header(attackEntry.AttackName or name)
:node(horiz)
:row(attack..'Total', '[[Damage|%s]]', plur/total == 1 and Math.formatnum(total*multishot)..'[[Category:'..plurDT..' Damage Weapons]]' or ('%s (%s%s%%)[[Category:%s Damage Weapons]]'):format(Math.formatnum(total*multishot), Tooltips.getImage(plurDT, 'DamageTypes', true), Math.round(100*plur/total, 0.01), plurDT), 'total-damage')
:row(attack..'Accuracy', '[[Accuracy|%s]]', attackEntry.Accuracy, 'accuracy')
:row(attack..'AmmoCost', '%s', attackEntry.AmmoCost ~= 1 and attackEntry.AmmoCost or nil, 'ammo-cost')
:row(attack..'BurstCount', '%s', attackEntry.BurstCount, 'burst-count')
:row(attack..'BurstDelay', '%s', format(attackEntry.BurstDelay, ' s'), 'burst-delay')
:row(attack..'BurstFireRate', '%s', attackEntry.BurstFireRate, 'burst-rate')
:row(attack..'ChargeTime', '[[Fire Rate#Charged Weapons|%s]]', format(attackEntry.ChargeTime, ' s'), 'charge-time')
:row(attack..'CritChance', '[[Critical Hit|%s]]', attackEntry.CritChance and Math.round(100*attackEntry.CritChance, 0.01)..'%', 'crit-chance')
:row(attack..'CritMultiplier', '[[Critical Hit|%s]]', format(attackEntry.CritMultiplier, 'x'), 'crit-multiplier')
:row(attack..'Falloff', '[[Damage Falloff|%s]]', attackEntry.Falloff and ('100%% damage up to %s m<br />%.0f%% damage at %s m<br />%.0f%% max reduction'):format(attackEntry.Falloff.StartRange, 100*(1 - (attackEntry.Falloff.Reduction or 1)), attackEntry.Falloff.EndRange, 100*(attackEntry.Falloff.Reduction or 1)), 'damage-falloff')
:row(attack..'EffectDuration', '%s', format(attackEntry.Duration, ' s'), 'effect-duration')
:row(attack..'ExplosionDelay', '%s', format(attackEntry.ExplosionDelay, ' s'), 'explosion-delay')
:row(attack..'FireRate', weapon.Slot == 'Melee' and '[[Attack Speed|%s]]' or '[[Fire Rate|%s]]', attackEntry.FireRate,
weapon.Slot == 'Melee' and 'attack-speed' or 'fire-rate')
:row('ForcedProcs', '[[Status Effect|%s]]', (function()
local result = {}
for i, elem in ipairs(attackEntry.ForcedProcs or {}) do
table.insert(result, Tooltips.getFullTooltip(elem, 'DamageTypes'))
end
return table.concat(result, ', ')
end)(), 'forced-procs')
:row(attack..'HeadshotMultiplier', '%s', format(attackEntry.HeadshotMultiplier, 'x'), 'headshot-multiplier')
:row(attack..'Multishot', '[[Multishot|%s]]', attackEntry.Multishot and ('%d (%s damage per projectile)'):format(attackEntry.Multishot, Math.round(total, 0.01)), 'multishot')
:row(attack..'NoiseLevel', '[[Noise Level|%s]]', attackEntry.IsSilent and 'Silent[[Category:Silent]]' or 'Alarming[[Category:Alarming]]', 'noise-level')
:row(attack..'PunchThrough', '[[Punch Through|%s]]', attackEntry.PunchThrough ~= 0 and format(attackEntry.PunchThrough, ' m') or nil, 'punch-through')
:row(attack..'Range', '%s', format(attackEntry.Range, ' m'), 'range')
:row(attack..'Reload', '[[Reload|%s]]', format(attackEntry.Reload, ' s'), 'reload-time')
:row(attack..'StatusChance', '[[Status Chance|%s]]', attackEntry.StatusChance and Math.round(100*attackEntry.StatusChance, 0.01)..'%', 'status-chance')
:row(attack..'ShotSpeed', '[[Projectile Speed|%s]]', format((attackEntry.ShotSpeed ~= nil and attackEntry.ShotSpeed > 0) and attackEntry.ShotSpeed or nil, ' m/s'), 'projectile-speed')
:row(attack..'ShotType', '%s', attackEntry.ShotType == 'Thrown' and ('%s[[Category:Thrown]]'):format(attackEntry.ShotType) or attackEntry.ShotType == 'AoE' and ('%s[[Category:Weapons with Area of Effect]]'):format(attackEntry.ShotType) or attackEntry.ShotType, 'projectile-type')
:row(attack..'Trigger', '[[Trigger Type|%s]]', attackEntry.Trigger, 'trigger-type')
:done()
end
local slamRadial, slide, stance
if override(args.SlamRadialDamage) or weapon.SlamRadialDmg then
if override(args.SlamRadialElement) or weapon.SlamRadialElement then
slamRadial = Tooltips.getFullTooltip(weapon.SlamRadialElement, 'DamageTypes')..' '..(override(args.SlamRadialDamage) or Math.formatnum(weapon.SlamRadialDmg))
else
slamRadial = override(args.SlamRadialDamage) or Math.formatnum(weapon.SlamRadialDmg)
end
end
if override(args.SlideAttack) or weapon.SlideAttack then
if override(args.SlideElement) or weapon.SlideElement then
slide = Tooltips.getImage(weapon.SlideElement, 'DamageTypes')..' '..(override(args.SlideAttack) or Math.formatnum(weapon.SlideAttack))
else
slide = override(args.SlideDamage) or Math.formatnum(weapon.SlideAttack)
end
end
if weapon.Slot == 'Melee' then
stance = weapon.StancePolarity and Polarity._polarity(weapon.StancePolarity) or 'None'
end
local slamproc;
if weapon.SlamRadialProc and type(weapon.SlamRadialProc) == 'table' then
slamproc = table.concat(weapon.SlamRadialProc, '<br />')
elseif weapon.SlamRadialProc then
slamproc = weapon.SlamRadialProc
end
Infobox:group():header('%s', 'heavy-attack')
:row('HeavyAttack', '[[Melee#Heavy Attack|%s]]', weapon.HeavyAttack and Math.formatnum(weapon.HeavyAttack), 'heavy-damage')
:row('HeavySlamAttack', '[[Melee#Heavy Attack|%s]]', weapon.HeavySlamAttack and Math.formatnum(weapon.HeavySlamAttack), 'slam-damage')
:row('HeavyRadialDmg', '%s', weapon.HeavyRadialDmg and Math.formatnum(weapon.HeavyRadialDmg), 'radial-damage')
:row('HeavySlamRadius', '%s', format(weapon.HeavySlamRadius, ' m'), 'slam-radius')
:row('WindUp', '[[Melee#Heavy Attack|%s]]', format(weapon.WindUp, ' s'), 'wind-up')
:done()
:group():header('%s', 'slam-attack')
:row('SlamAttack', '[[Melee#Slam Attack|%s]]', weapon.SlamAttack and Math.formatnum(weapon.SlamAttack), 'slam-damage')
:row('SlamRadius', '%s', format(weapon.SlamRadius, ' m'), 'slam-radius')
:row('SlamRadialDamage', '%s', slamRadial, 'radial-damage')
:row('SlamRadialProc', '%s', slamproc, 'radial-forced-proc')
:done()
:group():header('%s', 'slide-attack')
:row('SlideAttack', '[[Melee#Slide Attack|%s]]', slide, 'slide-damage')
:done()
:group():header('%s', 'miscellaneous')
:row('Augment', '[[Weapon Augments|%s]]', weapon.Augment, 'augments')
:row('DefaultUpgrades', '%s', (function()
local result = {}
for _, modIndex in ipairs(weapon.DefaultUpgrades or {}) do
table.insert(result, Tooltips.getFullTooltip(modIndex, 'Mods'))
end
return table.concat(result, '<br />')
end)(), 'default-upgrades')
:row('RivenFamily', '[[Riven Mods|%s]]', weapon.Family, 'riven-family')
:row('ExilusPolarity', '[[Exilus Weapon Adapter|%s]]', weapon.ExilusPolarity and Polarity._polarity(weapon.ExilusPolarity) or 'None', 'exilus-polarity')
--:row('InternalName', '%s', weapon.InternalName and '<code>'..weapon.InternalName..'</code>' or nil, 'internal-name')
:row('Introduced', '%s', weapon.Introduced and '{{ver|'..weapon.Introduced..'}}[[Category:Update '..weapon.Introduced:sub(1, (string.find(weapon.Introduced, '%.') or 0) - 1)..']]', 'introduced')
:row('Polarities', '[[Polarity|%s]]', Polarity._pols(weapon.Polarities), 'polarities')
:row('StancePolarity', '[[Stance|%s]]', stance, 'stance-polarity')
:row('SyndicateEffect', '[[Syndicate Radial Effects|%s]]', weapon.SyndicateEffect, 'syndicate-effect')
:row('Users', '%s', users, 'users')
:row('Variants', '%s', weapon.Family and family, 'variants')
:done()
local vendorStr = Vendor._buildVendorSourceStrings(name)
local baroStr = Baro._buildBaroSourceStrings(name)
Infobox:group():header('%s', 'vendor-sources')
:tag('data'):attr('source', 'Offerings')
:tag('default')
:tag('div'):addClass('tabber-borderless')
:wikitext(('<tabber>%s%s</tabber>'):format(
(vendorStr ~= '') and '|-|Vendors='..vendorStr..'\n' or '',
(baroStr ~= '') and '|-|Baro Ki\'Teer='..baroStr..'\n' or ''
)):done()
:done()
:done()
:done()
return frame:preprocess('{{#invoke:Weapons|stat|'..name..'|Categories}}\n'..tostring(Infobox))
end
}