WARFRAME Wiki
Advertisement
WARFRAME Wiki

Documentation for this module may be created at Module:Weapons/infobox/doc

---	'''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]]
--	@image		
--	@require	[[Module:Weapons/data]]
--	@require	[[Module:Weapons/Conclave/data]]
--	@require	[[Module:Math]]
--	@require	[[Module:Polarity]]
--	@require	[[Module:Tooltips]]
--	@require	[[Module:InfoboxBuilder]]
--	@release	stable
--	<nowiki>

local Math = require([[Module:Math]])
local Polarity = require([[Module:Polarity]])
local Tooltips = require([[Module:Tooltips]])
local Infobox = require([[Module:InfoboxBuilder]])

local attacks = {'Normal Attack', 'Secondary Attack', 'Charge Attack', 
	'Charge Area Attack', 'Area Attack', 'Secondary Charge Attack', 
	'Secondary Area Attack', 'Charged Throw Attack', 'Throw Attack'}

local function override(arg)
	return arg and arg..'[[Category:InfoboxOverride]]'
end

local function attack(weapon, param)
	return weapon[param] or
		(weapon.Attack1 and weapon.Attack1[param]) or
		(weapon.Attack2 and weapon.Attack2[param]) or
		(weapon.Attack3 and weapon.Attack3[param]) or
		(weapon.Attack4 and weapon.Attack4[param]) or
		(weapon.Attack5 and weapon.Attack5[param]) or
		(weapon.Attack6 and weapon.Attack6[param]) or
		(weapon.Attack7 and weapon.Attack7[param]) or
		(weapon.Attack8 and weapon.Attack8[param]) or
		(weapon.Attack9 and weapon.Attack9[param])
end

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 = mw.loadData([[Module:Weapons/Conclave/data]]).Weapons;
	else
		data = mw.loadData([[Module:Weapons/data]]).Weapons;
	end
	weapon = data[name] or data[name..' (Weapon)']

	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]]'))
	
	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:27px;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) 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 infobox = Infobox('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('Tradable', weapon.Tradable and "'''[[Trading|{{text|green|%s}}]]'''" or nil, 'tradable')
	: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]]', attack(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]]', attack(weapon, '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]]', attack(weapon, '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(attack(weapon, 'Reload'), ' s'), 'reload-time')
		:row('ReloadDelay', '[[Reload|%s]]', format(attack(weapon, 'ReloadDelay'), ' s'), 'reload-delay')
		:row('ReloadRate', '[[Reload|%s]]', format(attack(weapon, 'ReloadRate'), ' rounds/s'), 'reload-rate')
		:row('ShotSpeed', '[[Projectile Speed|%s]]', format(attack(weapon, 'ShotSpeed'), ' m/s'), 'projectile-speed')
		:row('ShotType', '%s', attack(weapon, '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, attackData
	for i, name in ipairs(attacks) do
		attack = 'Attack'..i
		attackData = weapon['Attack'..i]
		if attackData then
			elems = {}
			plur = -1
			for dt, dmg in pairs(attackData.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 attackData.Damage.Impact or 0) +
				(override(args[attack..'Puncture']) or attackData.Damage.Puncture or 0) +
				(override(args[attack..'Slash']) or attackData.Damage.Slash or 0)

			horiz = mw.html.create('group'):attr('layout', 'horizontal')
			:row(attack..'Impact', nil, attackData.Damage.Impact and Tooltips.getImage('Impact', 'DamageTypes', true)..Math.formatnum(attackData.Damage.Impact), 'impact')
			:row(attack..'Puncture', nil, attackData.Damage.Puncture and Tooltips.getImage('Puncture', 'DamageTypes', true)..Math.formatnum(attackData.Damage.Puncture), 'puncture')
			:row(attack..'Slash', nil, attackData.Damage.Slash and Tooltips.getImage('Slash', 'DamageTypes', true)..Math.formatnum(attackData.Damage.Slash), 'slash')

			for _, elem in ipairs(elems) do
				horiz:row(attack..elem, nil, attackData.Damage[elem] and Tooltips.getImage(elem, 'DamageTypes', true)..Math.formatnum(attackData.Damage[elem]), elem)
				total = total + (override(args[attack..elem]) or attackData.Damage[elem])
			end

			local multishot = attackData.Multishot or 1
			infobox
			:group():header(attackData.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]]', attackData.Accuracy, 'accuracy')
				:row(attack..'AmmoCost', '%s', attackData.AmmoCost ~= 1 and attackData.AmmoCost or nil, 'ammo-cost')
				:row(attack..'BurstCount', '%s', attackData.BurstCount, 'burst-count')
				:row(attack..'BurstDelay', '%s', format(attackData.BurstDelay, ' s'), 'burst-delay')
				:row(attack..'BurstFireRate', '%s', attackData.BurstFireRate, 'burst-rate')
				:row(attack..'ChargeTime', '[[Fire Rate#Charged Weapons|%s]]', format(attackData.ChargeTime, ' s'), 'charge-time')
				:row(attack..'CritChance', '[[Critical Hit|%s]]', attackData.CritChance and Math.round(100*attackData.CritChance, 0.01)..'%', 'crit-chance')
				:row(attack..'CritMultiplier', '[[Critical Hit|%s]]', format(attackData.CritMultiplier, 'x'), 'crit-multiplier')
				:row(attack..'Falloff', '[[Damage Falloff|%s]]', attackData.Falloff and ('100%% damage up to %s m<br />%.0f%% damage at %s m<br />%.0f%% max reduction'):format(attackData.Falloff.StartRange, 100*(1 - (attackData.Falloff.Reduction or 1)), attackData.Falloff.EndRange, 100*(attackData.Falloff.Reduction or 1)), 'damage-falloff')
				:row(attack..'EffectDuration', '%s', format(attackData.Duration, ' s'), 'effect-duration')
				:row(attack..'ExplosionDelay', '%s', format(attackData.ExplosionDelay, ' s'), 'explosion-delay')
				:row(attack..'FireRate', weapon.Slot == 'Melee' and '[[Attack Speed|%s]]' or '[[Fire Rate|%s]]', attackData.FireRate, 
					weapon.Slot == 'Melee' and 'attack-speed' or 'fire-rate')
				:row('ForcedProcs', '[[Status Effect|%s]]', (function()
					local result = {}
					for i, elem in ipairs(attackData.ForcedProcs or {}) do
						table.insert(result, Tooltips.getFullTooltip(elem, 'DamageTypes'))
					end
					return table.concat(result, ', ')
				end)(), 'forced-procs')
				:row(attack..'HeadshotMultiplier', '%s', format(attackData.HeadshotMultiplier, 'x'), 'headshot-multiplier')
				:row(attack..'Multishot', '[[Multishot|%s]]', attackData.Multishot and ('%d (%s damage per projectile)'):format(attackData.Multishot, Math.round(total, 0.01)), 'multishot')
				:row(attack..'NoiseLevel', '[[Noise Level|%s]]', attackData.IsSilent and 'Silent[[Category:Silent]]' or 'Alarming[[Category:Alarming]]', 'noise-level')
				:row(attack..'PunchThrough', '[[Punch Through|%s]]', attackData.PunchThrough ~= 0 and format(attackData.PunchThrough, ' m') or nil, 'punch-through')
				:row(attack..'Range', '%s', format(attackData.Range, ' m'), 'range')
				:row(attack..'Reload', '[[Reload|%s]]', format(attackData.Reload, ' s'), 'reload-time')
				:row(attack..'StatusChance', '[[Status Chance|%s]]', attackData.StatusChance and Math.round(100*attackData.StatusChance, 0.01)..'%', 'status-chance')
				:row(attack..'ShotSpeed', '[[Projectile Speed|%s]]', format(attackData.ShotSpeed, ' m/s'), 'projectile-speed')
				:row(attack..'ShotType', '%s', attackData.ShotType == 'Thrown' and ('%s[[Category:Thrown]]'):format(attackData.ShotType) or attackData.ShotType == 'AoE' and ('%s[[Category:Weapons with Area of Effect]]'):format(attackData.ShotType) or attackData.ShotType, 'projectile-type')
				:row(attack..'Trigger', '[[Trigger Type|%s]]', attackData.Trigger, 'trigger-type')
			:done()
		end
	end

	local slamRadial, slide, stance
	if override(args.SlamRadialDamage) or weapon.SlamRadialDamage then
		if override(args.SlamRadialElement) or weapon.SlamRadialElement then
			slamRadial = Tooltips.getFullTooltip(weapon.SlamRadialElement, 'DamageTypes')..(override(args.SlamRadialDamage) or Math.formatnum(weapon.SlamRadialDamage))
		else
			slamRadial = override(args.SlamRadialDamage) or Math.formatnum(weapon.SlamRadialDamage)
		end
	end

	if override(args.SlideAttack) or weapon.SlideAttack then
		if override(args.SlideElement) or weapon.SlideElement then
			slide = Tooltips.getFullTooltip(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('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 cats = {'[[Category:Weapons]]'}
	for _, trait in ipairs(weapon.Traits or {}) do
		table.insert(cats, '[[Category:'..trait..' Weapons]]')
	end
	table.insert(cats, tostring(infobox))

	return frame:preprocess(table.concat(cats, '\n'))
end
}
Advertisement