warframe

Lua error in Module:Docbunto at line 577: attempt to concatenate local 'item_name' (a nil value).


Created with Docbunto

See Also

Code


---	'''Ability/infobox''' builds the infobox on ability pages and adds the 
--	appropriate category pages.<br />
--
--	@module		ability
--	@alias		p
--	@author		[[User:Gigamicro|Gigamicro]]
--	@image		
--	@require	[[Module:Ability/data]]
--	@require	[[Module:Tooltips]]
--	@require	[[Module:Version]]
--	@release	unstable
--	<nowiki>

-- TODO: Fix code formatting so Module:Docbunto can properly generate documentation page
local AbilityData = mw.loadData([[Module:Ability/data]])
local Tooltips = require([[Module:Tooltips]])
local Version = require([[Module:Version]])

--Stuff that should go elsewhere
--TODO!
local function byRank(r0, r1, r2, r3)
	return ('<span style="border-bottom:2px dotted;" title="%s / %s / %s / %s">%s</span>'):format(r0, r1, r2, r3, r3)
end
-- TODO: Rename v key to vals or values and u key to units for readability
local drains = {
	['Aegis Storm'  ] = { {v=true}, {v=25, u='/s'}, {v=25, u='/s/enemy'}, {v=50, u='/dodge'}, },
	['Airburst'     ] = { {v=true}, {v=-25, u=' in air'}, }, -- WIP?
	['Artemis Bow'  ] = { {v=true}, {v=15, u='/shot'}, },
	['Blade Storm'  ] = { {v=true}, {v='6-12', u='/mark'}, },
	['Danse Macabre'] = { {v=true}, {v='20-40', u='/s'}, },
	['Desecrate'    ] = { {v=true}, {v=10, u='/corpse'}, },
	['Effigy'       ] = { {v=true}, {v=10, u='/s'}, },
	['Elude'        ] = { {v=true}, {v=2.5, u='/s'}, },
	['Exalted Blade'] = { {v=true}, {v=2.5, u='/s'}, },
	['Exalted Ironbride'] = { {v=true}, {v=2.5, u='/s'}, },
	-- ['Exalted Shadow'] = { {v=1, u='% well/s'}, }, -- WIP
	['Gloom'        ] = { {v=true}, {v='.75-7.5', u='/s'}, },
	['Guard Mode'   ] = { {v=true}, {v=5, u='/s'}, },
	['Haven'        ] = { {v=true}, {v=5, u='/s/ally'}, {v=25, u='/s/enemy'}, },
	['Hysteria'     ] = { {v=true}, {v='2.5-15', u='/s'}, },
	['Immolation'   ] = { {v=true}, {v='<hr/>', u='<small>when at max heat:</small>'}, {v='1t', u='/s<sup>-2</sup><br>t = time at max heat in seconds'}, }, -- WIP? too long?
	['Inferno'      ] = { {v=true}, {v=10, u='/enemy<br/>(max 10x)'}, }, -- WIP?
	['Mach Rush'    ] = { {v=true}, {v=12.5, u='/s'}, },
	['Mass Vitrify' ] = { {v=true}, {v='3-5', u='/s'}, },
	['Mend & Maim'  ] = { {v=true}, {v=3.5, u='/s'}, },
	['Navigator'    ] = { {v=true}, {v=3, u='/s'}, {v=2, u='/s<sup>2</sup>'}, },--:gsub('%^([0-9-]+)','<sup>%1</sup>')
	['Neutralize'   ] = { {v=true}, {v=10, u='/shot'}, {v=20, u='/alt fire'} },
	['Noctua'       ] = { {v=true}, {v=2, u='/shot'}, },
	['Pacify & Provoke'] = { {v=true}, {v=byRank(1.5,1.25,1,.5), u='/s/enemy'}, {v=byRank(5,4.33,3.66,3), u='/ability'}, },
	['Peacemaker'   ] = { {v=true}, {v=15, u='/s'}, },
	['Penumbra'     ] = { {v=true}, {v=5, u='/s'}, },
	['Primal Fury'  ] = { {v=true}, {v=5, u='/s'}, },
	['Prism'        ] = { {v=true}, {v=10, u='/s'}, },
	['Prowl'        ] = { {v=true}, {v='1-3', u='/s'}, },
	['Razorwing'    ] = { {v=true}, {v=5, u='/s'}, },
	['Renewal'      ] = { {v=true}, {v=2, u='/s'}, {v=3, u='/s/target'}, },
	['Sol Gate'     ] = { {v=true}, {v='12-24', u='/s'}, },
	['Sound Quake'  ] = { {v=true}, {v=byRank(25,18,15,12), u='/s'}, },
	['Spectral Scream'] = { {v=true}, {v=3, u='/s'}, },
	['Tail Wind'    ] = { {v=true}, {v=-12.5, u=' (airborne)'}, {v=5, u='/s hovering'}, }, -- WIP
	['Undertow'     ] = { {v=true}, {v=6, u='/s'}, {v=5, u='/grab'}, {v=5, u='/m'--[[eter']]}, },
	['Warding Grace'] = { {v=true}, {v=10, u='/s'}, },
}
-- TODO: Use Module:DamageTypes for this mapping (refactor since this function is a common use case in many modules)
local function damageTypeTextIcon(s)
	return s:gsub('<DT_([A-Z_]+)>',function(dt)return Tooltips.icon( ({
		IMPACT='Impact',PUNCTURE='Puncture',SLASH='Slash',ELECTRICITY='Electricity',
		RADIATION='Radiation',GAS='Gas',MAGNETIC='Magnetic',VIRAL='Viral',CORROSIVE='Corrosive',
		FIRE='Heat',
		FREEZE='Cold',
		POISON='Toxin',
		EXPLOSION='Blast',
		RADIANT='Void',
		SENTIENT='Tau',
		FINISHER='True',
	})[dt], 'DamageTypes') end)
end
local specialWeap = {
	--Exalted Shadow
	['/WraithReaperAbility']={nil,[=[
[[File:RavenousWraithMod.png|200px|left]]
{{main|Ravenous Wraith}}
{{#lst:Ravenous Wraith|intro}}
{{StanceTable|Name=Ravenous Wraith}}]=]},
	--Razorwing
	['/FairyFlightAbility']={[=[
[[File:DexPixia.png|200px|left]]
{{main|Dex Pixia}}
{{#lst:Dex Pixia|intro}}<hr style=\"clear:both;border-style:dashed\"/>
{{#lsth:Dex Pixia|Characteristics}}

<hr style="clear:both"/>

[[File:diwata.png|200px|left]]
{{main|Diwata}}
{{#lst:Diwata|intro}}<hr style=\"clear:both;border-style:dashed\"/>
{{#lsth:Diwata|Characteristics}}]=],"[[File:RazorwingMod.png|200px|left]]"--\n{{StanceTable|Name=Razorwing}}"
	},
}


return {
---	Builds the horizontal ability infobox.
--	@function	p.buildInfobox
--	@param		{table} frame Frame object
--	@return		{string} Wikitext of box
buildInfobox = function(frame)
	local args = frame.args or frame
	
	local name = args.name ~= '' and args.name --or mw.text.decode(frame:getTitle()):gsub('_',' '):gsub('and','&')--:gsub("'",'')
	local onpage = name == mw.text.decode(mw.title.getCurrentTitle().text):gsub(' (Ability)', '')
	-- local conclave = false and mw.loadData [[Module:Ability/Conclave/data]]
	local ability = AbilityData.Ability[name] or AbilityData.Archived[name]
	if not ability then error('buildInfobox: No ability "'..(name or '<'..type(name)..'>')..'" found in [[Module:Ability/data]]') end
	local link = args.link or ability.Link or name

	-- ability.Name, ability.InternalName unused
	local cardImage, gameImage = ability.CardImage, ability.Icon
	local link = ability.Link
	local version = ability.Introduced
	-- local costType, cost, drain = ability.CostType, ability.Cost, nil
	local hotkey, description = ability.Key, ability.Description
	local subsumable = ability.Subsumable
	local cardOnly = --[[args.cardonly and args.cardonly~='' or--]] not onpage

	--TODO:auto?
	local sub = (args.sub or ''):gsub('%[%[(Ability [SDRE][a-z]+)|<span style="[^"]+">[A-Z][a-z]+</span>%]%]',Tooltips'Stats''full')
	:gsub('<span style="color:white;">Misc</span>','<b style="color:white;border-bottom:2px dotted;" title="Stats unlisted in-game, not affected by Mods">Misc</b>')
	local strength, duration, range, efficiency, misc = args.strength, args.duration, args.range, args.efficiency, args.misc
	local info, attributes, tips, max, bugs = args.info, args.attributes, args.tips, args.max, args.bugs
	-- drain = args.drain
	
	-- TODO: AbilityDurationBuff.png is a flat white icon and needs to be inverted on light theme
	local costIcon=({energy='EnergyOrb.png', time='AbilityDurationBuff.png', shield='IconShield.png'})[ability.CostType or 'energy']
	local drain = drains[name] or { { v=true }, }
	for i, v in ipairs(drain) do
		if v.v==true then v.v=ability.Cost end
		drain[i] = (v.u and '' or '[[File:'..costIcon..'|24px|link='..(ability.CostType or 'energy'):gsub('^.', string.upper)..']]')..
			('[[%s|<b style="font-size:16px;">%s<small>%s</small></b>]]')
			:format(v.u and v.u:find('/s') and 'Channeled Abilities' or 'Ability Efficiency', v.v, (v.u or '') )
	end
	drain = table.concat(drain,'<br/>+'):gsub('<br/>%+(%[%[[^<]-<[^>]->)%-', '<br/>-%1')
	if drain:find('>0[<small>/b]+]]$') then drain = '' end
	
	-- TODO: Decouple code into a separate module dedicated to parsing text icon strings into Tooltips
	-- or image equivalents as seen on [[Text Icons]]; could be implemented as a string.format() function
	-- using text between angle brackets as format specifiers or dedicate a module for a custom templating language
	-- on the wiki
	description = damageTypeTextIcon(description)

	local augment = {}
	for i,v in ipairs(ability.Augments or {}) do
		table.insert(augment, ("[[File:%sMod.png|200px|left]]\n{{main|%s}}\n{{#lst:%s|intro}}\n{{#lsth:%s|Stats}}"):format(v:gsub(' ', ''), v, v, v))
		-- frame:expandTemplate('main',v)
		-- frame:callParserFunction('#lst,v,'intro')
		-- frame:callParserFunction('#lsth',v,'Stats')
		-- but it doesn't matter, bc this goes through a preprocess in a tabber anyway
	end
	augment = table.concat(augment, '\n\n<hr style="clear:both"/>\n\n')

	local weapon, stance = nil, nil
	local abilityid=ability.InternalName and ability.InternalName:match('/%w+$') or '<no InternalName>'
	if ability.Weapon then -- TODO: missing any? all stances correct?
		local weappagename = ability.Weapon == name and ability.Weapon..' (Weapon)' or ability.Weapon
		weapon = ("[[File:%s.png|200px|left]]\n{{main|%s}}\n{{#lst:%s|intro}}<hr style=\"clear:both;border-style:dashed\"/>\n{{#lsth:%s|Characteristics}}")
			:format(weappagename:gsub("[' ()]",''), weappagename, weappagename, weappagename)
			-- TODO: Could add a key to M:Ability/data to map stances to Exalted Weapons/abilities
		if not ({
				['/RangerBowAbility']=true,
				['/IronFrameBlastAbility']=true,
				['/GunFuAbility']=true,
				['/ScribeBookAbility']=true,
				['/EntratiMechSwordAbility']=true,-- archmelee has no stance
				['/FairyFlightAbility']=true,-- two exalted weapons is difficult; plus diwata's Razorwing stance has no page
				['/WraithReaperAbility']=true,-- Shadow Claws use Ravenous Wraith stance
			})[abilityid] then
			stance = ("[[File:%sStanceMod.png|200px|left]]\n{{main|%s (Stance)}}\n{{#lst:%s (Stance)|intro}}\n{{StanceTable|Name=%s}}")
				:format(name:gsub("[' ]", ''), name, name, name)
		end
	end
	--todo these in another place
	weapon = weapon or (specialWeap[abilityid] or {})[1] or mw.log('weapon fail: '..abilityid) or weapon
	stance = stance or (specialWeap[abilityid] or {})[2] or mw.log('stance fail: '..abilityid) or stance

	local function tab(s, c)
		return c and c ~= '' and ('|-|%s=<b style="display:none;">%s<br />\n</b>\n%s'):format(s, s, c) or ''
	end

	-- todo? c: Ability Weapons, Line Of Sight Abilities, One-Handed Abilities, Stealth Abilities, Toggled Abilities, Two-Handed Abilities
	local categories = { 'Warframe Abilities', ability.Powersuit, insert = table.insert}
	if args.special or args.damage then
		categories:insert('Pages using deprecated terms')
		strength = strength or args.damage
	end
	-- TODO: Should add a key to M:Ability/data that specifies ability has drain instead of relying on string finds
	if drain:find('/s<') then categories:insert('Channeled Abilities') end
	if drain == ''      then categories:insert('Free Abilities') end
	if weapon           then categories:insert('Exalted Weapon') end
	-- TODO: Remove these debugging categories when they are not needed
	if args.drain ~= '' and args.drain then
		categories:insert('Ability with Drain parameter')
	end
	if args.description and args.description:find('Drain') then
		--categories:insert('desc param mentions drain')
		--[[
		Absorb
		Aegis Storm
		Artemis Bow
		Conclave:Defy
		Danse Macabre
		Effigy
		Elude
		Exalted Blade
		Exalted Ironbride
		Exalted Shadow
		Haven
		Hysteria
		Immolation
		Mach Rush
		Mass Vitrify
		Mend & Maim
		Pacify & Provoke
		Peacemaker
		Penumbra
		Primal Fury
		Prism
		Prowl
		Razorwing
		Renewal
		Sandstorm
		Scarab Swarm
		Sol Gate
		Sound Quake
		Spectral Scream
		Tail Wind
		Undertow
		Warding Grace
		World On Fire
		]]
		if not drains[name] then
			description=args.description
			categories:insert('desc param override')
		end
	end
	if conclave then
		categories = {'PvP','PvP Abilities'}
	end
	categories = '[[Category:'..table.concat(categories,']][[Category:')..']]'

--deprecated:  special  damage
--TODO:
-- drain
-- sub 
-- strength duration range efficiency misc
-- info attributes 
-- weapon stance 
-- tips max bugs
	
	-- TODO: Refactor these inline stylings to a CSS class for ability infoboxes
	local abilityStats = sub and sub ~= '' and
'|width=30% style="padding-right: 6px; font-size: 14px; vertical-align: top;"|<div class="tabber-borderless">'..frame:preprocess('<tabber>'..sub..'</tabber>')..'</div>\n\n|-\n|\n|-\n|\n|-\n'
or ([=[
|width=30%% style="padding-right: 6px; font-size: 14px; border-bottom:1px solid;"|'''%s:'''<span style="float:right; font-size:12px;">%s</span>
|-
| style="padding-right: 6px; font-size: 14px; border-bottom:1px solid;"|'''%s:'''<span style="float:right; font-size:12px;">%s</span>
|-
| style="padding-right: 6px; font-size: 14px;"|'''%s:'''<span style="float:right; font-size:12px;">%s</span>
|-
%s
%s]=]):format(
Tooltips.full{'Ability Strength', 'Stats', r='Strength'}, strength ~= '' and strength or 'N/A',
Tooltips.full{'Ability Duration', 'Stats', r='Duration'}, duration ~= '' and duration or 'N/A',
Tooltips.full{'Ability Range',    'Stats', r='Range'   }, range    ~= '' and range    or 'N/A',

efficiency ~= '' and efficiency and ([=[| style="padding-right: 6px; font-size: 14px; border-top:1px solid;" |'''%s:'''
<span style="float:right;font-size:12px;">%s</span>
|-]=]):format(Tooltips.full{'Ability Efficiency', 'Stats', r='Efficiency'}, efficiency) or '',

misc ~= '' and misc and ([=[| style="padding-right: 6px; font-size: 14px; border-top:1px solid;" |
<b style="border-bottom:2px dotted;" title="Stats unlisted in-game, not affected by Mods">Misc</b>:
<span style="float:right;font-size:12px;">%s</span>
|-]=]):format(misc) or '',

nil)
	
	local keybindLink = ([=[[[Key Bindings|<b style="position:relative; background-color:#272727; color:white; 
padding:1px calc(.81ch - 1px); border:solid #aaadb4; border-width:2px 3px 4px 2px; border-radius:5px; 
font-size:12px;">%s</b>]]]=]):format(hotkey ~= '' and hotkey or 'N/A')
	
-- TODO: Break up this string into local functions/variables for readability
	return ([=[
{| class="ability-box" style="width:100%%;"
|-
|rowspan=5 style="width: 150px;"|[[File:%s|150px|link=%s]]
|rowspan=5 style="width: 60px; text-align: center;"|[[File:%s|x50px|link=%s|class=light-invert]]<br />%s<br />%s
|rowspan=5|<span id="%s" style="position: relative; top: -125px; height: 0px;"></span><b class="light-invert">%s</b><br/>
%s
<hr />
%s
%s
%s
|-
|}
<div id="mw-customcollapsible-%s" class="tabber-borderless mw-collapsible %s" style=%s>%s</div>
<div class="mw-customtoggle-%s button-c">
<div id="image-c" class="%s light-invert" style="padding-bottom: 0px;">[[File:Expand.svg|30px|link=#%s|Expand/Collapse]]</div>
</div>
%s]=]):format(
cardImage or 'Panel.png', link or name,
gameImage or 'UnidentifiedItem.png', link or name, keybindLink, drain,
name, Tooltips.text { { 'Ability', name, link } },
description ~= '' and description or "''No description available.''",
'Introduced in '..Version._getVersionLink(version),
abilityStats,
subsumable and [=[| style="padding-right:6px; font-size:14px; font-weight:bold; border-top:1px solid;"|[[Helminth#Ability Replacement|Subsumable to Helminth]]
|-]=] or '',
-- Content inside custom collapsible element
name:gsub(' ', '_'), cardOnly and 'mw-collapsed' or 'mw-uncollapsed', cardOnly and '"display: none;"' or '',
frame:preprocess('<tabber>'
..tab('Info', info)
..tab('Attributes', attributes)
..tab(abilityid=='/FairyFlightAbility' and 'Weapons' or 'Weapon', weapon)
..tab('Stance', stance)
..tab('Augment', augment)
..tab('Tips & Tricks', tips)
..tab('Maximization', max)
..tab('Bugs', bugs)
..'</tabber>'),
name:gsub(' ', '_'), (cardOnly and 'rotate2-c' or 'rotate-c'), name,
-- Content below custom collapsible element
onpage and categories or '',
nil)
--[[
.. (onpage and "parameter description: "..(args.description or 'none').."\n\npls add a drain param if there's drain on that\n\n"..[=[
<div align="center">
	<div class="mw-customtoggle-AbilParams" style="cursor:pointer; position:relative; width:85%; background: linear-gradient(#768993, #55646d); 
	display:inline-block; padding:0px 2px; margin:2px; line-height:20px; color:white; font-size:13px;border-radius: 3px;
	">View Param List<span style="position:absolute; left:5px;">&#9662;</span><span style="position:absolute;right:5px;">&#9662;</span>
	</div>
</div>
<div id="mw-customcollapsible-AbilParams" class="mw-collapsible mw-collapsed" style="display:flow-root;">
]=]..(function(args)local t={i=table.insert}for k,v in pairs(args)do t:i('=='..k..'==')t:i(v)end return table.concat(t,'\n')end)(args)..'</div>' or '')
]]--
end
}