Final Fantasy Brave Exvius Wiki
(sync)
m (hmm)
Line 906: Line 906:
 
) then table.insert(new_lines.to_replace, {stat, stat}) end
 
) then table.insert(new_lines.to_replace, {stat, stat}) end
 
end
 
end
for n = 1, #new_lines.to_replace do table.insert(to_replace, n, new_lines.to_replace[n]) end
+
--for n = 1, #new_lines.to_replace do table.insert(to_replace, n, new_lines.to_replace[n]) end
 
if not unit then
 
if not unit then
  +
for n = 1, #new_lines.to_replace do table.insert(to_replace, n, new_lines.to_replace[n]) end--hmm
 
for n = 1, #new_lines do new_lines[n] = unbold(new_lines[n]) end
 
for n = 1, #new_lines do new_lines[n] = unbold(new_lines[n]) end
 
passive_stats = {}
 
passive_stats = {}

Revision as of 12:33, 25 October 2017

local p = {
	stat_order = {'HP','MP','ATK','DEF','MAG','SPR'},
	equipment = {
		_class ={'Weapon', 'Armor', 'Accessory'},
		_plural = {Clothes = 'Clothes', Staff = 'Staves'},
		Dagger = 1,
		Sword = 1,
		["Great Sword"] = 1,
		Katana = 1,
		Staff = 1,
		Rod = 1,
		Bow = 1,
		Axe = 1,
		Hammer = 1,
		Spear = 1,
		Harp = 1,
		Whip = 1,
		["Throwing Weapon"] = 1,
		Gun = 1,
		Mace = 1,
		Fist = 1,
		["Light Shield"] = 2,
		["Heavy Shield"] = 2,
		Hat = 2,
		Helm = 2,
		Clothes = 2,
		["Light Armor"] = 2,
		["Heavy Armor"] = 2,
		Robe = 2,
		Accessory = 3
	},
	ability = {
		_class = {"Special Ability", "Magic Ability"},
		Active = 1,
		Passive = 1,
		White = 2,
		Black = 2,
		Green = 2,
		Blue = 2
	},
	ailment = {
		Poison = 1,
		Blind = 1,
		Sleep = 1,
		Silence = 1,
		Paralyze = 1,
		Confuse = 1,
		Disease = 1,
		Petrify = 1
	},
	killer = {
	--Special thanks to FencerTJ for category names (except he forgot humans)
		Avians = 'bird',
		Plantoids = "plant",
		Aquatics = "aqua",
		Beasts = "brute",
		Demons = "cysidus",
		Dragons = "dra[cg]o",
		Fairies = "[sf][pa]ir",--spirit or fairy
		Insects = "bug",
		Machinas = "m[ae]ch",
		Stones = "lith",
		Reapers = "undead",
		Humans = "man-?"
	},
	patterns = {
	--Not all patterns here. Just those used more than once.
		wikilink = '%[%[[^%[%]]+%]%]',
		only = '%(%[%[([%w ]+)%]%] only%)',
		enable = '([^\n]*Enables access[^\n]-: )([^\n]+)',
		multi_break = '[^<]?%s-/?%s*([^>]?.-[^<])%s*/%s-[^>]-',
		rng_enable = '\n%(%d+%%%) +[^\n]+: Enables? ',
		event = '%(%[?%[?Event[^,]-%)%s*%[?%[?'
	},
	template = {
		evade = 'Chance to evade %s damage taken (%s)'
	}
}

local result = {}
function result:_(v)
--simplified version of mw:en:Module:Buffer
	if v and v ~= '' then table.insert(self, v) end
	return self
end
local esc_seq = string.char(127,4,127,2)

function p.item(frame)
	local args, p_args, flags, currentTitle, namespace, invokePage = {}, frame:getParent().args, {}
	if p_args.page or args.page then
	--No worries. Scribunto is sandboxed, so overwriting mw.title.getCurrentTitle won't affect any other #invokes
		local emulate = mw.title.new(p_args.page or args.page)
		mw.title.getCurrentTitle = function() return emulate end
	end
	for k, v in pairs(frame.args) do
	--Basic cleaning of arguments and argument grouping
		v = mw.text.trim(v)
		if v and v~='' and v~='-' then args[k] = v end
	end
	local stats_sorted, stats = {}, {HP = {}, MP = {}, ATK = {}, DEF = {}, MAG = {}, SPR = {}}
	for k, v in pairs(stats) do
	--grab percents and integers in the form such that DEF+7, DEF+50% is entered as 'DEF=7+50%'
		if args[k] then
			v[2] = args[k]:match'%-?%d+%%'
			if v[2] then v[1] = args[k]:match'^%+?(%-?%d+)%+'--tolerate ATK=+10
			else table.insert(v, args[k]:match'^%+?(%-?%d+)$') end
		end
	end
	if p.equipment[args.type] then
		for i = 1, 2 do
		--show percentage stats after integer stats
			for _, v in ipairs(p.stat_order) do--this line controls order of stats
				table.insert(
					stats_sorted,
					stats[v][i] and (i==1 and '%s+%s' or '[[%s +%s]]'):format(v, stats[v][i])
				)
			end
		end
	end
	local function link(v, hide_disambiguation)
	--[[
	Autolinks if does not contain a link. If hide_disambiguation evals true, links "Page (Disambigation)" as
	"Page (Disambigation)|Page"
	]]
		return v and (v:find(p.patterns.wikilink) and v
			or hide_disambiguation and v:find'%b()' and v:gsub('([^%(]+) %(([^%)]+)%)', '[[%1 (%2)|%1]]')
			or v:gsub('^(.-) -( ?\\?%+?%d?)$', function(a, b)
					if v == invokePage then a, b = v, ''
					elseif b:sub(2, 2) ~= '+' or not tonumber(b:sub(3, 3)) then a, b = a .. b, '' end
					return ('[[%s]]%s'):format(a, b):gsub('\\%+', '+')
				end, 1))
	end
	local function tooltip_link(v, hide_disambiguation)
	--Makes tooltip link.
		if v == currentTitle then return "'''" .. v .. "'''" end
		local fetch, only = {title = ':' .. v, args = {'effect', 'ability', mode = 'fetch'}}
		only = frame:expandTemplate(fetch):match('%[%[' .. args.name:gsub('%p', '%%%1') .. '%f[|%]][^%]]-%]%] *%((.- only)%)')
			or frame:expandTemplate(
				fetch,
				rawset(fetch.args, 2, nil),
				rawset(fetch.args, 1, 'warning')
			):match'[^,]+ only'
		only = only and ' (' .. only .. ')' or ''
		return ('{{:%s|mode=tooltip%s}}')
			:format(v, hide_disambiguation and '' or '|full_pg=1')
			.. only
	end
	local function link2tooltip(v, hide_disambiguation)
	--checks all links for Module:Data and replaces them with tooltips when possible. Only use when non-Module:Data pages may be passed
		for lookback, wikilink, page in v:gmatch'(.?)(%[%[([^:#][^|%]]+)|?[^%]]*%]%])' do
			if lookback ~= '(' and not (page:find':[^ _]' or page:find'#' or stats[page:match'(.+) %+%d'] or mw.title.new(page).redirectTarget) then
				local tooltip = frame:preprocess('{{msgnw::' .. page .. '}}')
				if tooltip:lower():find'#invoke:data%s*&#124;%s*item' then
					v = v:gsub(wikilink:gsub('%p', '%%%1'), tooltip_link(page, hide_disambiguation):gsub('%p', '%%%1'))
				end
			end
		end
		return v
	end
	local function split_link(v, hide_disambiguation, sep, alt_link_func)
	--Splits v by commas and joins with sep (or comma if omitted). Links using link() unless passed alt_link_func
		local t = {}
		for s in mw.text.gsplit(v:gsub("\\,", esc_seq), ',%s*') do
			s = mw.text.trim(s)
			if s ~= '' then table.insert(t, (alt_link_func or link)(s, hide_disambiguation)) end
		end
		return table.concat(t, sep or ', '):gsub(esc_seq, ',')
	end
	local function multi_split(v, head_format, sep, headless_sep)
	--Splits by ';' and passes each to split_link(). If first item prefixed by 'text:', treats 'text' as group name
		local result = {_=result._}
		for x in mw.text.gsplit(v, ';') do
			local group, list = x:gsub('\\:', esc_seq):match'^([^:]+): -(.+)$'
			group, list = group:gsub(esc_seq, ':'), list:gsub(esc_seq, ':')
			result
				:_(group and head_format:format(group) or headless_sep)
				:_(split_link(list or x, nil, group and sep or headless_sep, group == 'Equipment' and tooltip_link))
				:_'\n'
		end
		return table.concat(result)
	end
	local function ailment_link(v, hide_disambiguation)
	--Autolinks ailments. When hide_disambiguation is nil or true, also autolinks non-ailments.
		local status, chance  = v:match'^([^%(]-) ?%(([^%)%(]-)%)'
		if status and (p.ailment)[status] then
			return ('[[Status Ailments#%s|%s]] (%s)'):format(status, status, chance)
		elseif status and v:find' status ailments? ' then return (v:gsub(' (status ailments?) ', ' [[Status Ailments|%1]] ', 1)) end
		if hide_disambiguation == false then return v end--for args.resist
		return link(v, hide_disambiguation)
	end
	local function materia_ailment_link(v)
	--auto-links ailments for materia.
		if v then for i, pattern in ipairs{
			'Cures? [^\n<]+ to ',
			'Inflicts? [^\n<]+ %(',
			'Increase resistance to [^\n<]+ %(',
			'Increase [^\n<]+ resistance %(',
			'[Rr]emove '
		} do if v:find(pattern) then
			for ailment in pairs(p.ailment) do
				v = v:gsub(
					" ('*)" .. ailment:lower(),
					(' %s1[[Status Ailments#%s|%s]]'):format('%', ailment, ailment:lower()),
					1
				)
			end
			flags.groupStatusCure, v =
				flags.groupStatusCure or i == 1 and v:find'all allies',
				v:gsub(" ('*)(status ailments?)('*) ", ' %1[[Status Ailments|%2]]%3 ', 1)
			break
		end end end
		return v
	end
	local function icon(v)
	--Makes item image. If passed a string, links to it. If passed true, links to the #invoke page (not name param)
		return (v and '[[File:%s|%s|link=%s]]' or '[[File:%s|%s]]'):format(
			args.image or ('Icon-%s.png'):format(args.name),
			args.name or '',
			v == true and invokePage or v
		)
	end
	local function recipe()
		return args.rec_mat and link2tooltip(frame:preprocess(split_link(args.rec_mat, nil, '<br>', function(v)
			local item, quantity, found_img = v:match'(.-) ?%(?(%d+)%)?$'
			found_img = mw.text.decode(frame:preprocess('{{msgnw::'.. (item or v) ..'}}')):match'|%s*image%s*=%s*([^<]-%.png)'
			return ('{{Item|%s|%s%s}}'):format(item or v, quantity or 1, found_img and '|image=' .. found_img or '')
		end)), true)
	end
	local function widget_sort_stats(close)
	--provide html element attributes for Widget:Sort_Stats
		local sortdata = {}
		for k, v in pairs(stats) do if v[1] or v[2] then
			table.insert(sortdata, 'data-'..k..'="'..args[k]..'"')
		end end
		if args.evade then table.insert(sortdata, 'data-evade="'..args.evade..'"') end
		return ' style="text-align:left" class=stats_cell ' .. table.concat(sortdata, ' ') .. (close or ' | ')
	end
	local function stats_cell(mode_tooltip)
	--generates stats cell used on category/shop pages for equipment; if mode_tooltip, hide_disambiguation and return stats table without concat
		local only, lines =
			args.warning and args.warning:match"Useable by '?'?'?(%a+)'?'?'? only",
			{stats_sorted[1] and table.concat(stats_sorted, ', '), _=result._}
		lines
			:_(args.element and 'Element: ' .. args.element)
			:_(args.resist and 'Resistance: ' .. split_link(args.resist, false, nil, ailment_link))
			:_(args.effect and 'Effect: ' .. link2tooltip(split_link(args.effect, mode_tooltip, nil, ailment_link), mode_tooltip))
			:_(args.ability and 'Ability: ' .. link2tooltip(split_link(args.ability, mode_tooltip), mode_tooltip))--use tooltip_link after module used on all ability pages
			:_(args.warning and (only and
				("'''%ss only'''"):format(mw.getContentLanguage():ucfirst(only))
				or ("'''%s'''"):format(args.warning):gsub(", ?", "<br>")
			))
			:_(args.used_by and 'Exclusive: ' .. split_link(args.used_by))
		return mode_tooltip and lines or lines[1] and table.concat(lines, '<br>')
	end
	local function randomized_table(v)
	--takes a string with multiple lines (separated by <br> or \n) and appends effects to each line from each double bullet in args.ability.
		local s = v:gsub('<br ?/?>', '\n')
		--if not s:find'\n(%([^\n]+)' then return v end
		return args.ability and s:find'\n%(' and
			'<table style="margin:0"><tr><td colspan=3>'
				.. s
					:gsub('\n\n+', '\n')
					:gsub("\n%f[%w '{<]", '</td></tr></table>', 1)
					:gsub("'''", '')
					:gsub('%%', '%%%%')
					:gsub(
						"\n(%([^\n]+)",
						'</td></tr><tr style="vertical-align:top"><td style="white-space:nowrap;padding-left:3em;text-indent:-3em">%1</td><td>&nbsp;-&nbsp;</td><td>%%s'
					)
					:gsub('(%) +%w+[- ] *%w+[- ] *)(%(?[%w]+)', '%1<wbr>%2')
					:format(select(2, unpack(mw.text.split(materia_ailment_link(args.ability):gsub('\n', ''), "%*.-%*%*"))))
				.. (not v:find'</table>' and '</td></tr></table>' or '')
			or v
	end
	local function effect_auto_link(v)
	--autolink killers, equipment masteries and wields, and status ailments in materia effects
		if v:find' killer ' or v:find' against ' then
			for cat, initial in pairs(p.killer) do
				local match = v:match(" '*(" .. cat:sub(1, -1):lower() .. "%l?)")
					or v:match(" '*(" .. initial .. "%l*)")
				v = match and v:gsub(match, ('[[:Category:%s|%s]]'):format(cat, match), 1) or v
			end
		elseif v:find'Increase .- equipped with' or v:find'Allow use' then
			for cat in pairs(p.equipment) do
				local match = v:match("'*([%a,']+ +'*" .. cat:lower() .. (cat:sub(-1)=='y' and '?i?e?s?)' or 's?)'))
					or cat == 'Staff' and
						v:match("'*([%a,']+ +'*staves)")
				v = match and match:sub(1, 5) ~= 'great' and v:gsub(
					match:gsub("^.- '*", '', 1),
					('[[:Category:%s|%s]]'):format(
						(p.equipment._plural[cat]
							or cat:gsub('y$', 'ie') .. 's'
						),
						match:gsub("^.- '*", '', 1)
					),
					1
				) or v
			end
		else v = materia_ailment_link(v) end
		return v
	end
	local function passive_stat_boost(effects)
		local list, group, last_percent = {[true] = {}, [false] = {}}, {[true] = {}, [false] = {}}, {[true] = {}, [false] = {}}
		for i = 1, #p.stat_order do
			if args[p.stat_order[i]] then
				local positive = tonumber(args[p.stat_order[i]]:match"^'*(%-?%d*)%%'*$") > 0
				if group[positive][1] then
					if last_percent[positive] ~= args[p.stat_order[i]] then
						table.insert(list[positive], ('%s (%s)'):format(table.concat(group[positive], '/'), last_percent[positive]))
						group[positive], last_percent[positive] = {}, args[p.stat_order[i]]
					end
				else last_percent[positive] = args[p.stat_order[i]] end
				table.insert(group[positive], p.stat_order[i])
				effects[args[p.stat_order[i]]] = (effects[args[p.stat_order[i]]] or 0) + 1
			end
		end
		for _, positive in ipairs{false, true} do
			if group[positive][1] then
				table.insert(effects, 1, (('%screase %s%s%s (%s)')
					:format(
						positive and 'In' or 'De',
						table.concat(list[positive], ', '),
						list[positive][1] and ' and ' or '',
						table.concat(group[positive], '/'),
						last_percent[positive]
					)
					:gsub('%(%-', '(')
				))
			end
		end
		return effects
	end
	local function materia_effects(mode_tooltip)
	--generates ability materia effects. If passed true as, returns effects as a table without concat.
		local effects, transcluded, show_table = 
			args.effect and mw.text.split(args.effect, '%s*\n%s*') or {},
			invokePage ~= currentTitle,
			args.ability and (args.type ~= 'Passive' or args.mode == 'category')
		for k, v in ipairs(effects) do
			local enable, skills = v:match(p.patterns.enable)
			if enable then
				effects[k] = args.effect:match(p.patterns.wikilink) and
					effects[k]:gsub('(\\?)\\,', '%1,')
					or enable .. split_link(skills)
				while v:find((effects[k + 1] or ''):match'%[(.+)%]%S*%s*=' or '^%$$') do table.remove(effects, k + 1) end
			elseif transcluded and args.ability and (v:find':[\n<][b%(][r%d]' or v:find'[Cc]ounter') then
				if v:gsub('<br ?/?>', '\n'):find'^[^\n]+:\n%(' then
					effects[k] = show_table and randomized_table(v) or v
				elseif args.type == 'Passive' then
					for name, effect in args.ability:gmatch'%* *([^\n]-) *\n%*%*%s*([^\n]+)' do
						effects[k] = v:gsub(
							name:gsub('%p', '%%%1'), 
							'{{tooltip|2=%1|1=%1<br>' .. effect:gsub('%%', '%%%1'):gsub('Randomly use:<br ?/?>', '') .. ' |style=white-space:nowrap}}'
						)
					end
				end
			else effects[k] = effect_auto_link(v) end
		end

		--Note: Below inserted in backwards order to the front of the effects table
		if args.evade_m then table.insert(effects, 1, p.template.evade:format('magic', args.evade_m)) end
		if args.evade_p then table.insert(effects, 1, p.template.evade:format('physical', args.evade_p)) end
		passive_stat_boost(effects)
		if args.used_by and not args.learn and args.mode and args.mode ~= 'exclusiveFX' then
			table.insert(effects, 'Exclusive: ' .. split_link(args.used_by))
		end
		return mode_tooltip and effects or effects[1] and table.concat(effects, '<br>'):gsub('(</table>)<br>', '%1')
	end
	local function type_link(parens)
	--makes [[:Category:Weapons]] ([[:Category:Guns]]). If parens is true, places them in parenthesis
		return p.ability[args.type] and '[['
				.. p.ability._class[p.ability[args.type]]:sub(1, -2)
				.. (p.ability[args.type] == 1 and 'ies (%s)]]' or 'ies]]')
					:format(args.type)
			or p.equipment[args.type] and (parens and ' ([[:Category:%s|%s]])' or '[[:Category:%s|%s]]'):format(
					p.equipment._plural[args.type] or args.type:gsub('y$', 'ie') .. 's',
					args.type
				)
			or args.usage and '[[:Category:Materials]]' or '[[Miscellaneous Items]]'	
	end
	local function multi_break(s)
	--takes args.hits or arg.MP_cost and splits them by backslash or semicolon, inserting same number of breaks per item as in args.ability
		if s and not tonumber(s) and s:find(p.patterns.multi_break) then
			local hits = ('/ ' .. s .. ' /'):gmatch(p.patterns.multi_break) 
			s = {}
			for v in mw.text.gsplit(
				args.ability
					or args.effect:gsub('\nIf used after[^\n]+:', '<br>'):gsub(':\n', '<br>'),
				args.ability and '\n%*%*' or '\n'
			) do
				table.insert(s, ('<br>'):rep(select(2, v:gsub('<br ?/?>', '')) + 1))
				table.insert(s, (hits()))
			end
			if not args.ability then table.remove(s, 1) end
			s = table.concat(s)
		end
		return '\n| ' .. (s or '-')
	end
	local frame_tooltip = '[[#.|{{Tooltip|style=text-align:left' .. frame:callParserFunction('#tag:nowiki', ';') .. 'white-space:nowrap|hideicon=|'
	local function attack_frames(hits, v)
	--[[
		pass args.hits as first param. Reads args.atk_frm and makes tooltip.
		Also recursively splits and tooltips args.atk_frm by / or ; characters if such are present in args.hits
	]]
		hits = hits ~= '' and hits ~= '-' and hits
		if hits and args.atk_frm then
			if not v and hits:find(p.patterns.multi_break) then
				local split = {
					hits = ('/ ' .. hits .. ' /'):gmatch(p.patterns.multi_break), 
					a_frames = ('/ ' .. args.atk_frm  .. ' /'):gmatch(p.patterns.multi_break)
				}
				for hit_count in split.hits do
					local animation = split.a_frames()
					table.insert(split, animation and tonumber(hit_count) and attack_frames(hit_count, animation) or hit_count)
				end
				hits = table.concat(split, ' / ')
			else
				local output, frames = {_=result._}, mw.text.split(v or args.atk_frm, ' ?[,%-] ?')
				output
					:_(frame_tooltip)
					:_(#frames > 1 and 'Frame delay: ' or 'Attack frame: ')
					:_(frames[1])
				if #frames > 1 then
					for i = 2, #frames do output:_(-tonumber(frames[i]) + tonumber(frames[i - 1])) end
					output
						:_'<br>Attack frames: '
						:_(table.concat(frames, '-'))
				else output:_(table.concat(frames, nil, 2, #frames)) end
				hits = table.concat(output:_'|':_(mw.text.trim(hits)):_'}}]]')
			end
		end
		return hits
	end
	local froms, hasExclusiveFX, learned = {
		--[[
		{parameter name, how-to-obtain title, flag}
		Set flag for sources that would not place an ability in Category:Items and to exclude source from how-to-obtain cell for mode=category
		1 = list parameter for equipable sources
		2 = non-equipment sources or non-list parameter
		]]
			{'drop', 'Dropped from'},
			{'steal', 'Stolen from'},
			{'explore', 'Collected from'},
			{'shop', args.quartz and 'Fat Chocobo' or 'Shop'},
			{'recipe', 'Recipe'},
			{'chest', 'Chest'},
			{'reward', 'Reward'},
			{'equip',  'Equipment', 1},
			{'materia', 'Ability Materia', 1},
			{'learn', 'Learned by', 2},
			{'esper', 'Esper', 2},--because not a comma-separates list
			{'enable', 'Enabled by', 2}
		},
		args.effect and--do not check used_by here
			args.effect:match(p.patterns.only)
			or args.ability and args.ability:match(p.patterns.only)--use match() not find()
	currentTitle, namespace, invokePage = 
		mw.title.getCurrentTitle().fullText,
		mw.title.getCurrentTitle().namespace,
		frame:getParent():getTitle()
	args.name = args.name or not invokePage:find':' and invokePage
	args.mode = args.mode
		or p_args.mode
		or currentTitle == 'Module:Data' and 'demo'
		or currentTitle == 'Ability Awakening' and 'awaken'
		or (namespace == 14
				or p.ability[args.type] and (
					currentTitle == 'Ability Materia'
					or currentTitle == (p.ability[args.type] == 1 and
						('Special Abilities (%s)'):format(args.type)
						or 'Magic Abilities'
					)
				)
			) and 'category'
		or invokePage ~= currentTitle and (
			(args.recipe or args.reward) and (args.recipe or args.reward):find(
				p.patterns.event
				.. currentTitle:gsub('_', ' '):gsub('%p', '%%%1')
			) and 'event'
			or (hasExclusiveFX == currentTitle or args.used_by and args.used_by:find(currentTitle)) and 'exclusiveFX'
			or p.equipment[args.type] and 'item'
			or 'typeless'
		)
	local function format_skill_args()
		--simplifies pattern matching by allowing newlines and <br> to be used interchangeably
		args.effect = args.effect and args.effect
			:gsub('(([^\n]+)\n%()', function(match, prev_line)
				if prev_line:find'=' then return match end
				return prev_line .. '<br>('
			end)
			:gsub('\n+(If used after)', '\n<br>%1')
		if args.ability and not args.ability:find('\n*', 1, 1) then
		--automatically takes names from effect param for randomized abilities and transcludes effect if line == '[data]'
			local a = mw.text.split(args.ability, '\n')
			if a[2] then
				args.ability = {}
				for name in args.effect:gmatch'<br ?/?>%([^\n<]+%) ([^\n<]+)' do
					local abil = table.remove(a, 1)
					if abil == '[data]' then
						abil = frame:expandTemplate{title = ':' .. name:match'%[%[(.-)[|%]]', args = {'STATS', mode = 'fetch'}}
					end
					table.insert(args.ability, ('*%s\n**%s'):format(name, abil or 'NO DATA'))
				end
				args.ability = table.concat(args.ability, '\n')
			end
		end
	end
	local function format_split_args(v) return v and v:gsub(' ?; ?', ' / '):gsub(' +', ' '):gsub('^ ?/', '- /'):gsub('/ /', '/ - /') end
	if p.ability[args.type] then
		learned = not p_args.mode and args.learn and invokePage ~= currentTitle and {args.learn:match('\n|? *%[%[' .. currentTitle .. '.-|?[$|]?%s*(<[^\n]+>)%s*|?[$|] ?(%d%d?%d?)')}
		args.hits, args.atk_frm, args.MP_cost =
			args.hits and (
				args.hits:find'^[dD][^/;]*$' and
					((args.mode or learned and learned[1]) and 'D' or 'Default unit attack')
					or format_split_args(args.hits)
			),
			args.atk_frm and format_split_args(args.atk_frm),
			args.MP_cost and
				format_split_args(args.MP_cost)
				or args.type == 'Active' and 0
		format_skill_args()
	end
	if learned and learned[1] then
	--Display when transcluded onto a page listed in args.learn
		local unmerge, hits = args.type ~= 'Passive' or p_args[1] == 'unmerge'
		result
			:_'|-\n|'
			:_(learned[1])
			:_'\n|'
			:_(learned[2])
			:_'\n|'
			:_(icon(true))
			:_'\n|style="text-align:left"|'
			:_(link(args.name, true))
			:_'\n|style="text-align:left"'
			:_(unmerge and '|' or ' colspan=3|')
			:_(materia_effects():gsub("(%) with [^\n:]+): ?<br ?/?> ?%([^\n]+", '%1'))--save random counter info for mode=conditional
		if unmerge then
			result
				:_(multi_break(attack_frames(args.hits)))
				:_(multi_break(args.MP_cost))
		end
	elseif args.mode and args.mode ~= 'demo' then
		local function BADGE()
			return ('<p style="text-align:center">%s<br>%s<br>(%s)</p>'):format(
				icon(true),
				link(invokePage ~= args.name and invokePage .. '|' .. args.name or invokePage),
				type_link()
			)
		end
		local function STATS(b)
			local v = (p.equipment[args.type] and stats_cell or materia_effects)()
			return not (b or p_args.sep or args.mode == 'fetch' or p_args[1] == 'STATS') and v and widget_sort_stats() .. v or v
		end
		local function extend(sep)
		--Reads customization parameters from parent frame. Def = default value, sep = separator
			local magic_words = {
				STATS = {STATS},
				ICON = {icon, true},
				TYPE = {type_link},
				LINK = {link, table.concat({invokePage, args.name}, '|')},
				RECIPE = {recipe},
				BADGE = {BADGE}
			}
			for _, v in ipairs(p_args) do
				_ = mw.text.trim(mw.text.killMarkers(v))
				if _ ~= '' then
					local a, b = v:gsub('\\@', esc_seq):match'^(.-)@(.*)'
					result:_(sep or '\n| '):_(
						a and (a~='' and 
							((args[a] or magic_words[a]) and 
								b:gsub(esc_seq, '@'):format(
									frame.args[a] ~= '' and frame.args[a]
									or args[a]
									or magic_words[a][1](unpack(a == 'STATS' and {nil, b} or magic_words[a], 2))
								)
								or '-')
							or b and b:gsub(esc_seq, '@'))
						or frame.args[_] ~= '' and frame.args[_]--return unprocessed non-empty args
						or args[_]--for auto args
						or magic_words[v] and magic_words[v][1](unpack(magic_words[v], 2))
						or p_args.default or '-'
					)
				end
			end
		end
		if args.mode == 'category' then
		--Display when on category page
			local obtain, equipped = {}, {}
			for i = 1, #froms do
				if args[froms[i][1]] and froms[i][3] ~= 2 then
					table.insert(froms[i][3] and equipped or obtain, 
						froms[i][3] and
							split_link(args[froms[i][1]]:gsub('%]%] %([^%)]+%)', ']]'), nil, froms[i][3] and '<br>')
							or ("'''%s:''' %s"):format(
								froms[i][2],
								split_link(args[froms[i][1]])
					))
				end
			end
			if args.esper then
				for esper in args.esper:gmatch'%[%[[^:]-%]%]' do table.insert(equipped, esper) end
			end
			table.insert(obtain, args.trust and ("'''Trust:''' [[%s]]"):format(args.trust))
			table.insert(obtain, args.obtain and multi_split(args.obtain, "'''%s''': ", ', ', '<br ?/?>'))
			result
				:_'|-\n|'
				:_(tonumber(p_args.rowspan) and ('rowspan=%s style="text-align:center"| %s ||')
					:format(p_args.rowspan, args.mag_lv or '')
				)
				:_(args.image and p.ability[args.type] and 'data-sort-value=' .. args.image:match'_(%d+)%.')
				:_((' align=center|%s|| %s || '):format(
					icon(true),
					link(args.name, true)
				))
				:_((p.equipment[args.type] or args.type == 'Passive') and widget_sort_stats())
				:_(p.equipment[args.type] and stats_cell()
					or materia_effects())
				:_' \n| '
				:_(p.ability[args.type] and result
					:_(args.MP_cost and ('style="text-align:center"| %s ||'):format(args.MP_cost) or '')
					:_(equipped[1] and table.concat(equipped, '<br>') or '-')
					:_' || '
					and nil
				)
				:_(obtain[1] and table.concat(obtain, '<br>') or '-')
			extend()
		elseif args.mode == 'exclusiveFX' then
		--Display when current page title matches name of the unit which benefits from an exclusive effect
			result
				:_(p_args[1] ~= 'rowonly' and ("{| class=wikitable style='text-align:center;width:100%'\n!" ..
					(p.equipment[args.type] and "Icon!!Name!!Type!!Effect"
					or "style='width:50px'|Icon!!style='width:130px'|Name!!Effect!!style='width:50px'|Hits{{tooltip|style=white-space:nowrap|D {{=}} Default unit attack}}!!style='width:50px'|MP")
				))
				:_"\n|-\n|"
				:_(icon(true))
				:_'\n| '
				:_(link(args.name, true))
				:_'\n|'
				:_(p.equipment[args.type] and type_link() ..'\n|')
				:_(widget_sort_stats())
				:_(p.equipment[args.type] and
					(stats_cell() or '')--in case stats_cell() returns nil
					or table.concat{
						materia_effects(),
						multi_break(attack_frames(args.hits)),
						multi_break(args.MP_cost)
					}
				)
			if p_args[1] ~= 'rowonly' then result:_'\n|}' end
		elseif args.mode == 'conditional' then
			local function conditional_row(rowspan, activator)
				result
					:_'|-\n|colspan=3'
					:_(rowspan and result:_' rowspan=' and rowspan)
					:_'|Activated by<br>'
					:_(activator ~= '' and activator
						or args.enable and split_link(args.enable)
						or link(args.name)
					)
					:_'||style="text-align:left"| '
			end
			local ability_count, isPassive, randomized_counter, unmerge =
				args.ability and select(2, ('\n'..args.ability):gsub("%*'''", 0)),
				args.type == 'Passive',
				args.effect and args.effect:match"Chance to counter[^\n]+with '?'?'?(.-)'?'?'?:<br ?/?>%(%d+%%",
				p_args[1] == 'unmerge'
			if args.ability then
				conditional_row()
				if randomized_counter then
					result
						:_(randomized_counter)
						:_'\n|style="text-align:left"| '
						:_(randomized_table(materia_effects()):gsub("Chance to counter[^\n]-:", 'Randomly use:'))
						:_(multi_break(attack_frames(args.hits)))
						:_(multi_break(args.MP_cost))
				else
					if ability_count > 0 then table.insert(result, 2, ' rowspan=' .. ability_count) end
					if args.ability and not args.hits and args.ability:find'>Hits:' then
						args.hits, args.MP_cost = {}
						for line in args.ability:gmatch'\n%*%*[^\n]+' do
							local a, b, h = line:find'<br ?/?>Hits: *(.+)'
							if a then
								local offset = args.ability:find(line, 3, 1)
								args.ability = args.ability:sub(1, offset + a - 2) .. args.ability:sub(offset + b)
								table.insert(args.hits, h)
							else table.insert(args.hits, '-') end
						end
						args.hits = table.concat(args.hits, ' / '):gsub('Default unit attack', 'D')
					end
					result
						:_(args.ability and materia_ailment_link(args.ability)
							:gsub("\n%* *'''(.-)'''", '|-\n|style="text-align:left"| %1')
							:gsub("%* *'''(.-)'''", '%1')
							:gsub('%*%*', '| style="text-align:left"' 
								.. (isPassive and not (args.hits or args.MP_cost or unmerge) and
									' colspan=3| '
									or '| '
							))
						)
					if unmerge or args.hits or args.MP_cost then
						result
							:_(multi_break(attack_frames(args.hits)))
							:_(multi_break(args.MP_cost))
					end
				end
			elseif args.enable then
				if p_args.rowspan == '0' then result:_'\n|-\n|'
				else conditional_row(tonumber(p_args.rowspan)) end
				result
					:_(link(args.name, true))
					:_'\n|style="text-align:left"| '
					:_(materia_effects())
					:_(multi_break(attack_frames(args.hits)))
					:_(multi_break(args.MP_cost))
			else for line in args.effect:gsub('<br ?/?>%s*%(', '\n('):gmatch'[^\n]*Enable[^\n]+' do if not line:find'%].*=' then
				local skills, ability_count = (select(2, line:match(p.patterns.enable)) or line:match' of (.+)')
					:gsub('([^\\]), *', '%1@$#')
				result:_(result[1] and '\n')
				conditional_row(
					ability_count > 0 and ability_count + 1,
					(line:gsub('\\:', esc_seq):match'^%(%d+%%%) *(.-):' or ''):gsub(esc_seq, ':')
				)
				for k, v in ipairs(mw.text.split(skills, '@$#')) do--no gsplit because k needed
					if k > 1 then result:_'\n|-\n|style="text-align:left"| ' end
					local title, page = (v:match'%[%[(.-)[|%]\]' or v):gsub('\\,', ',')
					page = mw.title.new(title).exists and frame:expandTemplate{title = ':' .. title, args = {page = title}}
						:gsub('Default unit attack', 'D')
					local override, page_effect =
						frame.args.effect:match('\n%s*effect%[' .. title:gsub('%p', '%%%1') .. '%]%s*=%s*([^\n]+)')
							or frame.args.effect:match('\n%s*%[' .. title:gsub('%p', '%%%1') .. '%]effect%s*=%s*([^\n]+)'),
						page and page:match("'''Effect:'''\n:([^*=]+)\n")
					args.effect = (override or page_effect or 'ERROR - No page or override found for ' .. link(title)):gsub('\\n', '\n')
					result
						:_(page and link(v, true) or v)--when all abilities use Data, replace all with:						 
						--:_('{{:'..v..'|mode=fetch|STATS|{{tooltip|{{{hits}}}|Attack frame(s): {{{atk_frm}}}}}|MP}}') 
						:_'\n|style="text-align:left"| '
						:_(page and override and '<span class="tool">')
						:_(args.effect)
						:_(override and page_effect and 
							"<span class=tip style='width:40em'><u>Automated Warning</u><br>A page exists for ''"
							.. link(v)
							.. "'' but an override has been set on ''"
							.. link(invokePage)
							.. "''. If the effect shown is the same as the following, please remove the override:<table class=wikitable  style='margin:auto' ><tr><td>"
							.. page_effect
							.. "</td></tr></table></span></span>")
					--args.atk_frm = args.effect:match('\n%s*atk_frm%[' .. title:gsub('%p', '%%%1') .. '%]%s*=%s*([^\n]+)') or args.atk_frm
					for i, m in ipairs{{'Hits', 'hits'}, {'MP', 'MP_cost'}} do
						result:_(multi_break(
							frame.args.effect:match('\n%s*' .. m[2] .. '%[' .. title:gsub('%p', '%%%1') .. '%]%s*=%s*([^\n]+)')
								or frame.args.effect:match('\n%s*%[' .. title:gsub('%p', '%%%1') .. '%]' .. m[2] .. '%s*=%s*([^\n]+)')
								or page and page:match("'''" .. m[1] .. ":''' ([^\n]+)")
								or '-'
						))
					end
				end
			end end end
		elseif args.mode == 'awaken' then
			local function bold(v)
				return v and ((v:find"'''" or v:find'</?b>') and v or ("'''%s'''"):format(v)) or ''
			end
			local function unbold(v, manual_bold)--removes striked text and all tags except those that start with a t (table)
				if manual_bold then return v end
				return v and (v
					:gsub("'''", '')
					:gsub('<br><b><s%b></s></b><br>', '\n')
					:gsub('<s%b></s>', '')
					:gsub('%b<>', function(tag)
						if tag == '<wbr>' or tag:find'^</?t' then return tag
						elseif tag:sub(2, 3) == 'br' then return '\n' end
						return ''
					end)
					:gsub('font-weight:bold;', '')
				)
			end
			local function calculate_effects()
				return unbold(materia_effects())
			end
			local unit, o_args, passive_stats, effects, new_effects = 
				p_args[1] ~= 'table' and p_args[1] ~= 'conditional' and p_args[1] or currentTitle == 'Ability Awakening' and 'Error - unspecified unit',
				{},
				{},
				unbold(materia_effects()):gsub('\n', '<br>')
			new_effects, passive_stats.lines = effects, passive_stat_boost{}
			local function parse_awakening(input)
				local to_replace, to_append, recalc, manual_bold = {}, {}
				if input then 
					for _, v in next, {'effect', 'ability'} do
						_ = '%s+' .. v .. '%s*=%s*([^=]+%f[\n])([^=]+=?)'
						local m, m2, m3 = ('\n' .. input):match(_)
						if not m then
							_ = '%s+' .. v .. '%s*=%s*([^\n]*)'
							m3 = ('\n' .. input):match(_)
						end
						if m or m3 then
							input, args[v], o_args[v], recalc, manual_bold =
								('\n' .. input):gsub(_, m2 and m2:find'=' and '%2' or ''),
								(m3 or (m2:find'=' and m or m .. m2):gsub('\n%(', '<br>(')):gsub('\\n', '\n'),
								args[v],
								true,
								manual_bold or (m or m3):find"'''" or (m or m3):find"<b>"
						end
					end
				end
				input = input and mw.text.split(input, '%s*\n%s*') or {'awk_mat=NO DATA,0'}
				for i = 1, #input do
					if i == 1 and args[input[i]] then parse_awakening(args[input[i]])
					else
						local diff = mw.text.split(input[i], '%s*=>%s*')
						if diff[2] then
							table.insert(to_replace, {diff[1]:gsub('\\n', '\n'), diff[2]:gsub('\\n', '\n')})
						else
							diff = mw.text.split(input[i], '%s*=%s*')
							if diff[2] then
								diff[2] = diff[2]:gsub('\\n', '\n')
								if diff[2] == '' then diff[2] = nil end
								if ({hits=1,atk_frm=1,MP_cost=1})[diff[1]] then diff[2] = format_split_args(diff[2]) end
								if stats[diff[1]] then
									if diff[2] == '0' or diff[2] == '0%' then diff[2] = nil end
									if args[diff[1]] then passive_stats.lines[args[diff[1]]] = passive_stats.lines[args[diff[1]]] - 1 end
									o_args[diff[1]], args[diff[1]], passive_stats.changed, passive_stats[diff[1]] = args[diff[1]], diff[2], true, true
								elseif (diff[1] == 'evade_p' or diff[1] == 'evade_m') then
									if not recalc and args[diff[1]] ~= diff[2] then
										local physical, v, evade = diff[1] == 'evade_p' and 'physical', diff[2]--:gsub('%%', '%%%%')
										evade = physical or 'magic'
										if args[diff[1]] then
											table.insert(to_replace, {
												new_effects:match(p.template.evade:format(evade, '[^(]+'):gsub('[()]', '%%%1')),
												p.template.evade:format(evade, (#(frame.args[diff[1]] or '') > 0 or not unit and args[diff[1]]) and bold(v) or v)
											})
										else
											new_effects = mw.text.split(new_effects, new_effects:find'\n' and '\n' or '<br>')
											table.insert(
												new_effects,
												1 + #passive_stats.lines + (not physical and args.evade_p and 1 or 0),
												bold(p.template.evade:format(evade, v))
											)
											new_effects = table.concat(new_effects, '<br>')
										end
									end
									args[diff[1]] = diff[2]--no recalc
								elseif not args[diff[1]] or unbold(diff[2]) ~= unbold(args[diff[1]]) then
									o_args[diff[1]], args[diff[1]] =
										args[diff[1]] or '0',
										({awk_mat=1,name=1,atk_frm=1})[diff[1]] and diff[2] or bold(diff[2])
								end
							elseif input[i] ~= '' then table.insert(to_append, input[i]) end
						end
					end
				end
				if recalc then
					if args.ability then
						format_skill_args()
						new_effects = frame.args.ability and frame.args.ability ~= '' and bold(unbold(materia_effects(), manual_bold))
							or unbold(materia_effects(), manual_bold)
								:gsub('(<table style=")', '%1font-weight:bold;')
								:gsub('(</table>)(.+)', "%1'''%2'''" )
								:gsub('^(.+)(<table)', "'''%1'''%2")
					else new_effects = bold(unbold(materia_effects(), manual_bold)) end
					new_effects, passive_stats.changed = frame:preprocess(new_effects)
					local a, b = new_effects:find(effects, 1, 1)
					if not a then a, b = new_effects:find(frame.args.effect, 1, 1) end
					if a and (not frame.args.ability or frame.args.ability == '') then
						new_effects = new_effects:sub(1, a - 1)
							.. '<span style="font-weight:normal">' .. effects .. '</span>'
							.. new_effects:sub(b + 1)
					else
					--Unbold if override effect is found in original (assume strikethrough was undesired)
						local plain = unbold(new_effects)
						if effects:find(plain, 1, 1) or frame.args.effect:find(plain, 1, 1) then new_effects = plain end
					end
				elseif passive_stats.changed then
					new_effects = mw.text.split(new_effects, new_effects:find'\n' and '\n' or '<br>')
					local new_lines = passive_stat_boost{to_replace = unit and passive_stats.lines.to_replace or {}}
					if #new_lines > #passive_stats.lines then
						for n = 1, #new_lines do
							local a = new_lines[n]:sub(1, 3)
							if not table.concat(passive_stats.lines):find(a) then new_lines[n] = bold(new_lines[n]) end
						end
					elseif #new_lines < #passive_stats.lines then
						for n = 1, #passive_stats.lines do
							local a = passive_stats.lines[n]:match'(%u%l%l)rease '
							if not table.concat(new_lines):find(a) then
								table.insert(new_lines, select(a == 'Inc' and 1 or 2, 1, ("<b><s>%s</s></b>"):format(passive_stats.lines[n])))
							end
						end
					end
					for n = 1, #passive_stats.lines do table.remove(new_effects, 1) end
					table.insert(new_effects, 1, table.concat(new_lines, '<br>'))
					new_effects = table.concat(new_effects, '<br>')
					for _, stat in ipairs(p.stat_order) do
						if args[stat] then
							if passive_stats.lines[args[stat]] == 0 or not passive_stats.lines[args[stat]] then
								local percent = args[stat]:sub((args[stat]:find'%d'))
								table.insert(new_lines.to_replace, {percent, percent})
							end
							passive_stats.lines[args[stat]] = (passive_stats.lines[args[stat]] or 0) + 1
						end
						if passive_stats[stat] and (
							not o_args[stat]
							or args[stat] and (
								o_args[stat]:sub(1, 1) == '-' and args[stat]:sub(1, 1) ~= '-'
								or o_args[stat]:sub(1, 1) ~= '-' and args[stat]:sub(1, 1) == '-'
							)
						) then table.insert(new_lines.to_replace, {stat, stat}) end
					end
					--for n = 1, #new_lines.to_replace do table.insert(to_replace, n, new_lines.to_replace[n]) end
					if not unit then
						for n = 1, #new_lines.to_replace do table.insert(to_replace, n, new_lines.to_replace[n]) end--hmm
						for n = 1, #new_lines do new_lines[n] = unbold(new_lines[n]) end
						passive_stats = {}
					end
					passive_stats.lines, passive_stats.changed = new_lines
				end
				for _, diff in ipairs(to_replace) do
					local a, b = new_effects:find(diff[1], 1, 1)
					if not a then
						a, b = new_effects:find(diff[1]:gsub('<br>', '\n'), 1, 1)
						if not a then
							a, b = new_effects:find(diff[1]:gsub('\n', '<br>'), 1, 1)
							if not a then 
								a, b = new_effects:find(diff[1]:gsub("'''", ''), 1, 1)
								if not a then a, b = new_effects:find(unbold(diff[1]), 1, 1) end
							end
						end
					end
					if a then
						if diff[1] == '' then new_effects = new_effects .. ' ' .. bold(diff[2])
						else
							if diff[2] == '' then diff[2] = ("<b><s>%s</s></b>"):format(diff[1]:gsub('\n', '<br>'))
							else--no bold if inside bold line
								local line = new_effects:gsub('<br>', '\n'):match('[^\n]*' .. diff[1]:gsub('%p', '%%%1') .. '[^\n]*')
								if not line then line = new_effects:gsub('<br>', '\n'):match('[^\n]*' .. unbold(diff[1]):gsub('%p', '%%%1') .. '[^\n]*') end
								if line:sub(1, 3) ~= "'''" or line:sub(-3) ~= "'''" or line:match"^'?'?'?(.-)'?'?'?$":find"'''" then diff[2] = bold(diff[2]) end
							end
							new_effects = new_effects:sub(1, a - 1)
								.. diff[2]
								.. (diff[2] == '' and (a == 1 or new_effects:find('\n' .. diff[1], 1, 1)) and
									new_effects:sub(b + 1, b + 2):upper()
										.. new_effects:sub(b + 3)
									or new_effects:sub(b + 1)
								)
						end
					elseif diff[1] == '^' then new_effects = bold(diff[2]) .. ' ' .. new_effects:sub(1, 1):lower() .. new_effects:sub(2)
					else table.insert(to_append, "''Error - Unable to find: ''" .. bold(diff[1])) end
				end
				for k, v in ipairs(to_append) do to_append[k] = bold(effect_auto_link(v)) end
				table.insert(to_append, 1, to_replace[1] and effect_auto_link(new_effects) or new_effects)
				new_effects = table.concat(to_append, '<br>'):gsub('\n', '<br>'):gsub("'''/'''", '/')
				return new_effects
			end
			local function missing_plus(unit, i) return 'awk_mat=NO DATA,0\nWarning: {{param|' .. unit .. '+' .. i .. '}} undefined' end
			if unit then
				for i = 1, tonumber(args.max_plus or p_args.max_plus) or 2 do
					new_effects = parse_awakening(args[unit .. '+' .. i] or missing_plus(unit, i))
				end
				new_effects = new_effects:gsub("'+''(''%b'''')'''+"--[[looks wrong but isn't]], "%1"):gsub(("'"):rep(6),'')
				local function arg_change(i)
					return o_args[i] and ("<br>'''%s %sed to %s'''"):format(
						({hits = 'Hit count', MP_cost = 'MP cost'})[i],
						tonumber(unbold(args[i])) and
							(tonumber(unbold(o_args[i])) > tonumber(unbold(args[i])) and ' reduc' or ' increas')
							or 'chang',
						unbold(args[i])
					) or ''
				end
				result
					:_(p_args[2] and (p_args[2] == 'table' and					
						'{|class=wikitable\n!Unit!!Abilities!!class=spacer|Base Effects!!class=spacer|Awakening'
						or '|-class=subheader\n!colspan=4|'))
					:_'\n|-\n|'
					:_(p_args[2] and
						('rowspan=%s style="text-align:center"|{{Sprite|%s}}<br>[[%s]]\n|')
							:format(tonumber(p_args[2]) or tonumber(p_args[3]) or 3, unit, unit))
					:_(link(args.name, true))
					:_'<br>('
					:_(args.awk_mat and args.awk_mat:match'^[^,]+' or '<b>awk_mat</b> undefined')
					:_')\n| '
					:_(args.ability and effects:sub(1, 7) ~= '<table ' and randomized_table(effects) or effects)
					:_'\n| '
					:_(args[unit .. '+1'] and new_effects or "''No data for unit named'' " .. bold(unit))
					:_((arg_change'hits' .. arg_change'MP_cost'):sub(new_effects:sub(-8) == '</table>' and 5 or 1))
			else
				if p_args[1] == 'table' then
					result:_'{|style="text-align:center;width:100%" class=wikitable\n|-\n!Name!!class=spacer|Effect!!style="width:50px"|{{tooltip|D {{=}} Default unit attack|Hits}}!!style="width:50px"|MP!!style="width:50px"|Type'
					for i = 1, 5 do result:_'!!style="width:20px"|T':_(i) end
					result:_'!!style="width:100px"|Gil'
				elseif p_args[1] == 'conditional' then result:_'|-\n! colspan="11" | Conditional\n|-'
				else result:_'|-class=subheader\n!colspan=11|' end
				local last_atk_frm = args.atk_frm
				for i = 1, tonumber(args.max_plus or p_args.max_plus) or 2 do
					new_effects = unbold(new_effects)
					parse_awakening(args[(p_args.skill or currentTitle) .. '+' .. i] or missing_plus(p_args.skill or currentTitle, i))
					result
						:_'\n|-\n|'
						:_(link(args.name, true))
						:_' +'
						:_(i)
						:_'\n|style="text-align:left"| '
						:_(new_effects
							:gsub("'''''%b'''''''", "'''%1'''")
							:gsub(("'"):rep(6),'')
							:gsub('\n', '<br>')
						)
						:_(atk_frm ~= args.atk_frm and
							(function()
								last_atk_frm = args.atk_frm
								return multi_break(attack_frames(args.hits)):gsub('[FA]', function(m)return'New '..m:lower()end)
							end)()
							or multi_break(args.hits)
						)
						:_(multi_break(args.MP_cost))
					if args.awk_mat then 
						local mats = mw.text.split(args.awk_mat, '%s*,%s*')
						for c = 1, #mats - 1 do result:_'\n|':_(mats[c]) end
						result
							:_'\n|'
							:_((' -\n|'):rep(7 - #mats))
							:_(frame:expandTemplate{title='Gil', args = {mats[#mats]}})
					else
						result:_'\n|colspan=7|'
						if p_args.skill then 
							if currentTitle ~= invokePage then result
								:_'via '
								:_(link(p_args.skill))
								:_' +'
								:_(i)
							else result:_' -'end
						else result:_'<b>awk_mat</b> undefined' end
					end
					args.MP_cost, args.hits = unbold(args.MP_cost), unbold(args.hits)
				end
			end
		elseif args.mode == 'event' then
			result
				:_'|-\n|'
				:_(BADGE())
				:_'\n| '
				:_(p.equipment[args.type] and stats_cell()
					or materia_effects())
			if args.recipe then result:_'\n| ':_(recipe()) end
			extend()
		elseif args.mode == 'custom' then
			result:_'|-'
			extend()
		elseif args.mode == 'fetch' then
			extend(p_args.sep)
			if p_args.sep ~= '' then table.remove(result, 1) end--removes first sep
		elseif args.mode == 'tooltip' then
			local wikilink, tip, lines =
				link(args.name, not p_args.full_pg),
				mw.html.create'td',
				(p.equipment[args.type] and stats_cell or materia_effects)(true)
			lines[1] = stats_sorted[1] and p.equipment[args.type] and
				'Stats: ' .. lines[1]
					:gsub('%((%-%d+%%?)%)', '(<span style="color:#f00">%1</span>)')
					:gsub('([^ ])(%+%d+%%?)', '%1<span style="color:#0f0">%2</span>')
				 or lines[1]
			for i = 1, #lines do
				if lines[i]:find'^<table' then
					tip:node(lines[i]:gsub('(<table style=")', '%1font-size:8.5px;'))
					lines.table = true
				else tip
					:tag'div'
					:css{
						['padding-left']='2ex',
						['text-indent']='-2ex',
						['font-size']=lines.table and '8.5px' or nil
					}
					:wikitext(lines[i])
				end
			end
			tip:css{background = '#014'}
			return frame:preprocess(tostring(mw.html.create'span'
				:addClass'tool'
					:tag'span'
					:addClass'mobileonly'
					:wikitext(wikilink)
				:done()
					:tag'span'
					:addClass'nomobile'
					:wikitext(wikilink)
				:done()
				:tag'span'
					:addClass'tip module-tooltip'
					:tag'table'
						:tag'tr'
							:tag'td'
							:css{width='90px',['text-align']='center',['vertical-align']='top',padding='12px 6px'}
							:attr{rowspan=2}
							:wikitext(icon'')
						:done()
							:tag'td'
							:css{padding='5px 0 0 5px',font='15px Arial, sans-serif',height='30px'}
							:node(args.name:gsub(' %(%w+%)', ''))
							:node((p.equipment[args.type] or p.ability[args.type] == 2) and mw.html.create'span'
								:css{float='right',['margin-top']='-9px'}
								:wikitext(
									frame:expandTemplate{
										title = p.equipment[args.type] and 'Equip' or 'Affinity',
										args = {args.type == 'Throwing Weapon' and
											'Throwing'
											or args.type
										}
									},
									p.ability[args.type] and '&nbsp;Lvl&nbsp;' .. args.mag_lv .. '&nbsp;'
								)
							)
						:done()
					:done()
						:tag'tr'
						:node(tip)
				:allDone()
			))
		else
		--Display when on a shop page (mode = recipe/item/item2/typeless)
			result
				:_(('|-\n| %s@\n| %s %s')--don't remove spaces
					:gsub('@', args.mode == 'item2' and '{{item|%%s}}' or '%%s')
					:format(
						args.mode == 'recipe' and 'Recipe for '
							or '',
						args.mode == 'item2' and args.name or link(args.name, true),
						(args.mode == 'typeless' or args.mode == 'recipe') and
							(p.equipment[args.type] and '-\n| ' or 'colspan=2' .. (args.mode == 'recipe' and '| ' or ''))
							or type_link() .. '\n|',
						args.mode == 'recipe' and '-'
							or widget_sort_stats() .. (
								p.equipment[args.type] and stats_cell()
								or materia_effects()
								or (args.usage or args.awk_mat) and 'Material'
								or '-'
				)))
				:_(p_args.limit ~= '0' and ('\n|{{%s|%s}}%s'):format(
					args.quartz and 'Star Quartz' or 'Gil',
					args.quartz
						or ({item=1,item2=1})[args.mode] and args.buy_gil
						or args.mode == 'recipe' and args.buy_rec
						or '?',
					p_args.limit and ('<br>(Limit: %s)'):format(p_args.limit) or ''
				))
			extend()
		end
	else
	--info displayed on item (invoke) page
		result
			:_'__NOTOC__\n__NOEDITSECTION__'
			:_((args.image or args.name) and ('<div class="frame-%s">\n%s\n</div>'):format( 
				p.equipment[args.type] and 'equip' or p.ability[args.type] and 'ability' or 'item',
				icon()
			))
			:_(args.desc and '\n\n' .. args.desc)
		if args.mode then table.remove(result, 1) end--mode = demo
		if args.type then
			if p.equipment[args.type] then
				local materia = {args.effect}
				table.insert(materia, args.ability)
				for k, v in ipairs(materia) do materia[k] = split_link(v, true, nil, ailment_link) end
				result
					:_"\n== Statistics ==\n*'''Type:''' "
					:_(args.type == 'Accessory' and '[[:Category:Accessories|Accessory]]'
						or p.equipment._class[p.equipment[args.type]] .. type_link(true))
					:_"\n*'''Stats:''' "
					:_(stats_sorted[1] and table.concat(stats_sorted, ', ') or '-')
					:_"\n*'''Element:''' "
					:_(args.element or '-')
					:_"\n*'''Resistance:''' "
					:_(args.resist and split_link(args.resist, false, nil, ailment_link) or '-')
					:_"\n*'''Additional effect:''' "
					:_(#materia > 0 and 
						link2tooltip(args.ability and (--replace link2tooltip after all abilities use Data
							args.effect and 
								"%s and enables use of %s"
								or 'Enables use of %s'
							):format(unpack(materia))
							or materia[1], true) .. (args.warning and ', ' .. args.warning or '')
						or args.warning
						or '-'
					)
			elseif p.ability[args.type] then
				flags.rng_enable = args.effect and args.effect:find'%(%d+%%%) [^\n]*Enable' and not args.ability
				result
					:_"\n== Statistics ==\n*'''Type:''' "
					:_(p.ability._class[p.ability[args.type]])
					:_' ('
					:_(args.type)
					:_(p.ability[args.type] == 2 and ' Magic Lv ' .. args.mag_lv)
					:_")\n*'''Effect:'''\n:"
					:_(materia_effects())
					:_(args.hits and "\n*'''Hits:''' " .. attack_frames(args.hits))
					:_(args.MP_cost and "\n*'''MP:''' " .. args.MP_cost)
					if args.ability or flags.rng_enable then
						result
							:_'\n==Abilities==\n'
							:_(args.ability and materia_ailment_link(args.ability))
						if flags.rng_enable then
							local r, fake_parent = result, {
								getTitle = function() return currentTitle end,
								args = {mode = 'conditional'},
								getParent = frame.getParent
							}
							function frame.getParent() return fake_parent end
							result = {_ = result._}
							frame.getParent, result = fake_parent.getParent, r
								:_'{|class=wikitable style="text-align:center;width:100%"\n|-\n! colspan="3" | Condition !! width="130px" | Name !! class="spacer" | Effect !! width="50px" | Hits {{Tooltip|D {{=}} Default unit attack}} !! width="50px" | MP\n'
								:_(p.item(frame))
								:_'\n|}'
						end
					end
			elseif args.type == 'Item' and args.effect then
				result
					:_'\n== Effect ==\n'
					:_(materia_effects())
			end
		end
		result
			:_(args.notes and '\n==Notes==\n')
			:_(args.notes)
			:_'\n==Crafting recipe==\n'
			:_(args.recipe and args.recipe ~= 'none' and
				('{|class=wikitable style="text-align:center"\n!Materials!!Gil\n|-\n|align=left|%s\n||{{Gil|%s}}\n|}'):format(
					recipe() or '{{param|rec_mat}} is undefined',
					args.rec_gil or '?')
				or 'None'
			)
			:_((args.usage or args.quest) and result
				:_"\n==Usage=="
				:_(args.awk_mat and "\n''Awakening Material''\n:*" .. split_link(args.quest, nil, '\n:*'))
				:_(args.usage and "\n''Crafting Material''" .. multi_split(args.usage, "\n:''%s''\n::*", '\n::*', '\n:*'))
				:_(args.quest and "\n''Involved in Quest''\n:*" .. split_link(args.quest, nil, '\n:*'))
				and nil
			)
			:_"\n==How to obtain=="
		local function simpleTable(v)
			local rows = mw.text.split(v, '\n+')
			for k, r in ipairs(rows) do
				local cells = mw.text.split(r, "%$")
				for i, c in ipairs(cells) do
					if c:find'^ *%[%[.+%]%] *$' then cells[i] = 'style="text-align:left"| ' .. c end
				end
				rows[k] = table.concat(cells, ' || ')
			end
			result
				:_'{|class="wikitable sortable" style="text-align:center"\n'
				:_(table.concat(rows, '\n|-\n| '))
				:_'\n|}'
		end
		for i = 1, #froms do
			if args[froms[i][1]] then
				flags.isItem = flags.isItem or not froms[i][3]
				result
					:_"\n''"
					:_(froms[i][2])
					:_"''\n:"
				if args[froms[i][1]]:find'^{|' then
					result:_(args[froms[i][1]])
				elseif args[froms[i][1]]:find'^!' then
					simpleTable(args[froms[i][1]])
				else result
					:_"*"
					:_(split_link(args[froms[i][1]], nil, '\n:*', froms[i][1] == 'equip' and tooltip_link))
				end
			end
		end
		result
			:_(args.trust and ("\n''Trust Master Reward''\n:*[[%s]]"):format(args.trust))
			:_(args.obtain and multi_split(args.obtain, "\n''%s''\n:*", '\n:*', '\n*'))
			:_(args.used_by and "\n==Equippable By==\n:*" .. split_link(args.used_by, nil, '\n:*'))
		if args.learn then
			local awk = {}
			for name in args.learn:gmatch'\n|? *%[%[([^%]|]+)' do
				if args[name .. '+1'] then table.insert(awk, {name}) end
			end
			if args.enable then for skill in args.enable:gsub('\\,', '@$'):gmatch'%f[%w][^,]+' do
				if args[skill .. '+1'] then table.insert(awk, {skill:gsub('@$', ','), isSkill = true}) end
			end end
			if awk[1] then
				table.sort(awk, function(a, b) return a[1] < b[1] end)
				local fake_parent = {
					getTitle = function() return currentTitle end,
					args = {'table', mode = 'awaken'}
				}
				function frame.getParent() return fake_parent end
				local r, wide_tab = result:_'\n==Ability Awakening==',
					'width:200%%;margin-right:-99vh;%1"%2 mw-collapsible mw-collapsed" data-expandtext="Show table (hidden on testcase pages only due to width)"\n|+'
				for k, name in ipairs(awk) do
					result, fake_parent.args.page, fake_parent.args.skill = 
						setmetatable({}, {__index = r}),
						select(name.isSkill and 1 or 2, invokePage, name[1])
					awk, fake_parent.args[1] = k== 1 and 
						p.item(frame)
							:gsub('!Name', '!style="width:5em"|Unit/Skill!%1', 1)
							:gsub(currentTitle:find'testcases/' and 'width:100%%([^"]*" class=)(wikitable)' or '^$',
								wide_tab,
								1
							)
						or p.item(frame)
							:gsub('(colspan=)(%d+)', function(a,b)return a..b+1 end, 1)--future maintenance proof
					r:_'\n':_(awk:gsub(
						'(%|%[%[.-%]%] %+)',
						'|rowspan=' .. select(2, awk:gsub('[^|]%[%[([^%]|]+)%]%] ?%+1', '')) * 3 + 2 .. '|'
							.. (name.isSkill and
								'{{:' .. name[1] .. '|mode=fetch|ICON}}'
								or '{{sprite|' .. name[1] .. '}}' 
							) .. '<br>'.. link(name[1]) ..'|%1',
						1
					))
					for conditional in awk:gmatch'[^|]%[%[([^%]|]+)%]%] ?%+1' do
						local m = ''
						for n in awk:gmatch('%[%[' .. conditional .. '%]%] ?%+%s') do m = m < n and n or m end
						r:_'\n':_(frame
							:expandTemplate{title = ':' .. conditional, args = {
								mode = 'awaken',
								skill = args.name,
								max_plus = m
							}}
							:gsub('undefined', '%1 at ' .. link(conditional))
						)
					end
				end
				result = r:_'\n|}'
			end
		end
		local categories = {''}
		if args.type then
			if p.ability[args.type] then
				table.insert(categories, 'Module:Data Ability')
				if flags.isItem or args.trust or args.obtain then
					table.insert(categories, 'Items')
					table.insert(categories, 'Ability Materia')
				elseif args.esper or args.equip then table.insert(categories, 'Ability Materia') end
				if p.ability[args.type] == 1 then
					table.insert(categories, ('%s (%s)'):format(
						p.ability._class[p.ability[args.type]]:sub(1, -2) .. 'ies',
						args.type
					))
				else
					table.insert(categories, 'Magic')
					table.insert(categories, args.type .. ' Magic')
					table.insert(categories, args.type .. ' Magic Level ' .. args.mag_lv)
				end
			else 
				table.insert(categories, 'Items')
				if p.equipment[args.type] then
					table.insert(categories, 'Equipment')
					if args.type == 'Accessory' then
						table.insert(categories, 'Accessories')
					else
						table.insert(categories, p.equipment._class[p.equipment[args.type]] .. 's')
						table.insert(categories, p.equipment._plural[args.type] or args.type .. 's')
					end
				else
					if args.usage or args.awk_mat then table.insert(categories, 'Materials') end
				end
			end
		end
		if (args.recipe or args.reward or ''):find(p.patterns.event) then table.insert(categories, 'Event Items') end
		for _, k in ipairs{
			--{arg key, category name}, if key is not nil, put in cat
			{'recipe', 'Crafting Recipes'},
			{'buy_gil', 'Purchasables'},
			{'trust', 'Trust Master Rewards'},
			{hasExclusiveFX and 'effect' or 'used_by', 'Unit Exclusive Items'},
			{'awk_mat', 'Awakening Materials'},
			{'usage', 'Crafting Materials'},
			{'quest', 'Quest Items'},
			{'equip', 'Equipment-given Abilities'},
			{'esper', 'Esper-given Abilities'}
		} do
			if args[k[1]] then table.insert(categories, k[2]) end
		end
		result
			:_((namespace ~= 0 and --escape catagories if not on main namespace
				table.concat(categories, ']]\n[[:Category:'):gsub('%[C', '[:C')
				or table.concat(categories, ']]\n[[Category:')
			):sub(3, -1))
			:_']]'
	end
	return frame:preprocess(table.concat(result))
end

function p.wrap(frame)
	if frame.args.mode == 'shop' then
		result
			:_'{|class=wikitable'
		for k, v in ipairs(frame.args) do
			local name = k==1 and frame.args.name or frame.args['name'..k] or ''
			result
				:_'\n|-\n!colspan=4| '
				:_(name)
				:_'\n|-\n!Name!!'
				:_((name == 'Item Shop' or name == 'Ability Shop') and
					'colspan=2| '
					or 'Type!!'
				)
				:_'Description!!Price'
				:_(v)
		end
		result:_'\n|}'
	end
	return frame:preprocess(table.concat(result))
end

function p.find(frame)
	local data, bad, title, result, this = {}, {}, mw.title.getCurrentTitle().fullText
	this = mw.text.decode(frame:preprocess('{{msgnw::' .. (frame.args[1] or mw.title.getCurrentTitle().fullText) .. '}}'))
	if title:find'User:' or not title:find':' and not title:find'/' then
		for m, p, l in this:gmatch'|%s*%[(%b[])%] *(.)(%d?)' do 
			m = m:sub(2, -2):match'^[^|]+'
			if not (m:find':[^ _]' or m:find'#') and (p ~= '+' or l ~= '2') then
				local page = mw.text.decode(frame:preprocess('{{msgnw::' .. m .. '}}')):gsub('<!%-%b--%->', '')
				if page:lower():find'<onlyinclude>%s*{{#invoke:data|item' and (not title:find'Abilit' or page:find'Ability_%d+%.png') then
					if this:sub(1, 99):find'Unit Infobox' then
						table.insert(page:find(title) and data or bad, p == '+' and tonumber(l) and m .. '|mode=awaken' or m)
					else 
						if title == 'Ability Awakening' then
							for awk in page:gmatch'|%s*([^\n]-)%+1%s*=' do
								if not this:find(m .. '|'.. awk, 1, 1) then
									local rowspan = this:match(
										'rowspan%s*=%s*["\']?(%d+)[^\n]+'
										.. awk .. '[^\n]+\n|%s*%[%[' .. m
									)
									table.insert(data, m .. '|' .. awk
										.. (rowspan and '|' .. rowspan or ''))
								end
							end
						else
							local rowspan
							if title:find'Abilit' and page:find'mag_lv%s*=%s*%d' then
								rowspan = this:match('rowspan=["\']?(%d+)[^\n]+\n|[^\n]+|' .. m)
							end
							table.insert(data, rowspan and
								m .. '|rowspan=' .. rowspan
								or m
							)
						end
					end
				end
			end
		end
	end
	if data[1] then
		result = '{{Kupo2|Kupo! The following '
			.. (data[2] and 'are [[Module:Data]] pages' or 'is a [[Module:Data]] page')
			.. ':\n<pre>{{:' 
			.. table.concat(data, '}}\n{{:')
			.. '}}</pre>\nPlease replace where appropriate. Thanks!\n\nThis message only appears if links to such pages are found.\n}}'
	end
	if bad[1] then
		result = (result or '')
			.. '{{Kupo2|The following '
			.. (bad[2] and '[[Module:Data]] abilites do' or '[[Module:Data]] ability does')
			.. ' not include this unit in {{param|learn}}:\n\n[[' 
			.. table.concat(bad, ']], [[') 
			.. ']]\n\nPlease add this unit to '
			.. (bad[2] and 'those pages' or 'that page')
			.. '. Thanks, Kupo!}}'
	end
	local count = #data
	if bad[1] then count = '*'
	elseif #data > 9 then count = mw.ustring.char(('A'):byte() + #data - 10) end
	return result and frame:preprocess(result) .. (not this:find'{{Unreleased}}' and '[[Category:Pages needing a Module:Data transclusion|' .. count.. ']]' or '')
	--following script can be used on pages like Special Abilities Passive in edit mode
	--javascript:for(i=0,m=document.querySelector('div>pre').innerHTML.match(/[^{}:][^{}\n]+?(?=\}\})/g);m[i];i++)wpTextbox1.value=wpTextbox1.value.replace(new RegExp('\\n\\|\\-.*?\\n?.*?\n?.*?\\[\\[('+m[i].replace(/([^\w\s])/g, "\\$1")+')(\]\]|\\|)[\\s\\S]*?(?=\\n(\\|(\\-|\\})|\\{\\{:))'), "\n{{:$1}}");
end

return p