WARFRAME Wiki

Read about the future of the Fandom platform and the WARFRAME wiki! Any feedback would be appreciated! User blog:Cephalon Scientia/2022 Fandom Community Connect. - 06:11, 6 June 2022 (UTC)

READ MORE

WARFRAME Wiki
Advertisement
WARFRAME Wiki

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
}
Advertisement