模块:Data

来自FFBE WIKI
跳转至: 导航搜索

此模块的文档可以在模块:Data/doc创建

local p = {
	stat_order = {'HP','MP','ATK','DEF','MAG','SPR','生命值','法力值','攻击','防御','魔力','精神'},
	equipment = {
		_class ={'武器', '防具', '首饰'},
		_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,
		["短剑"] = 1,
		["剑"] = 1,
		["大剑"] = 1,
		["刀"] = 1,
		["手杖"] = 1,
		["魔棒"] = 1,
		["弓"] = 1,
		["斧"] = 1,
		["锤"] = 1,
		["长枪"] = 1,
		["乐器"] = 1,
		["鞭"] = 1,
		["投掷武器"] = 1,
		["火枪"] = 1,
		["权杖"] = 1,
		["拳套"] = 1,
		["轻盾"] = 2,
		["重盾"] = 2,
		["帽子"] = 2,
		["头盔"] = 2,
		["衣服"] = 2,
		["轻甲"] = 2,
		["重甲"] = 2,
		["袍"] = 2,
		["首饰"] = 3
	},
	ability = {
		_class = {"特殊能力", "魔法"},
		Active = 1,
		Passive = 1,
		White = 2,
		Black = 2,
		Green = 2,
		Blue = 2,
		["主动"] = 1,
		["被动"] = 1,
		["白"] = 2,
		["黑"] = 2,
		["绿"] = 2,
		["蓝"] = 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 = '%[?%[?特殊任务[^,]-%s*%[?%[?'
	},
	template = {
		evade = '一定机率闪避%s攻击 (%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 chain_link(v, effect)
		if effect then
			return "{{Chain|" .. v .. "}}"
		else
			return "[[连击/" .. v .. "|" .. v .."]]"
		end
	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 == '装备' 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)
		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 %('
		} do if v:match(pattern) then
			for ailment in pairs(p.ailment) do
				v = v:gsub(' ' .. ailment:lower(), (' [[Status Ailments#%s|%s]]'):format(ailment, ailment:lower()))
			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.zhname or '',
			v == true and invokePage or v
		)
	end
	local function smallicon(v)
	--Makes 32x32px 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|32x32px]]' or '[[File:%s|%s|32x32px]]'):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 '属性:' .. args.element)
			:_(args.resist and '耐性:' .. split_link(args.resist, false, nil, ailment_link))
			:_(args.effect and '效果:' .. split_link(args.effect, nil, nil, ailment_link))
			:_(args.ability and '能力:' .. split_link(args.ability))
			:_(args.warning and (only and
				("'''%ss only'''"):format(mw.getContentLanguage():ucfirst(only))
				or ("'''%s'''"):format(args.warning):gsub(", ?", "<br>")
			))
			:_(args.used_by and '专用:' .. 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=2>'
				.. 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>%%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, (('%s%s%s%s (%s)')
					:format(
						positive and '提高' or '降低',
						table.concat(list[positive], '、'),
						list[positive][1] and '、' or '',
						table.concat(group[positive], '/'),
						last_percent[positive]
					)
					:gsub('%(%-', '(')
				))
			end
		end
		return effects
	end
	local function stats_shopcell()
		--generates stats cell used on category/shop pages
		local only = args.warning and string.match(args.warning, "Useable by '?'?'?(%a+)'?'?'? only")
		return table.concat(({_=result._})
				:_(table.concat(stats_sorted, '、'))
				:_(args.element and '属性:' .. args.element)
				:_(args.resist and '耐性:' .. split_link(args.resist, false, nil, ailment_link))
				:_(args.effect and '效果:' .. split_link(args.effect, nil, nil, ailment_link))
				:_(args.ability and '能力:' .. split_link(args.ability))
				:_(args.warning and (only and
					string.format("'''%ss only'''", mw.getContentLanguage():ucfirst(only))
					or string.gsub(string.format("'''%s'''", args.warning), ", ?", "<br>")
				))
				:_(args.used_by and '专用:' .. split_link(args.used_by)),
			'<br>'
		)
	end
	local function materia_effects(mode_tooltip, no_chain)
	--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 ~= '被动' 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'使用') then
				if v:gsub('<br ?/?>', '\n'):find'^[^\n]+:\n%(' then
					effects[k] = show_table and randomized_table(v) or v
				elseif args.type == '被动' 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=' .. effect:gsub('%%', '%%%1'):gsub('Randomly use:<br ?/?>', '') .. ' |style=white-space:nowrap}}'
						)
						effects[k] = v:gsub(
							name:gsub('%p', '%%%1'), 
							'%1'
						)
					end
				end
			else effects[k] = effect_auto_link(v) end
		end

		if not no_chain then
			if args.chain then table.insert(effects, chain_link(args.chain, true)) 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('魔法', args.evade_m)) end
		if args.evade_p then table.insert(effects, 1, p.template.evade:format('物理', 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, '专用:' .. 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) return string.format(parens and '([[:Category:%s|%s]])' or '[[:Category:%s|%s]]', p.equipment._plural[args.type] or args.type, args.type)
	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; 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
					:_'<u style="color:#37{{#invoke:ustring|char|100|59}}cursor:help" title="'
					:_(#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
						:_'{{#invoke:ustring|char|38|35|49|48|59}}Attack frames: '--escape '&#10;'
						:_(table.concat(frames, '-'))
				else output:_(table.concat(frames, nil, 2, #frames)) end
				hits = table.concat(output
					:_'">'
					:_(hits)
					:_'<{{#invoke:ustring|char|47}}u>'--escape '/'
				)
			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', '掉落:'},
			{'steal', '盗取:'},
			{'explore', '收集自:'},
			{'shop', args.quartz and '胖陆行鸟商店:' or '商店:'},
			{'recipe', '配方:'},
			{'chest', '宝箱:'},
			{'reward', '奖励:'},
			{'equip',  '装备:', 1},
			{'materia', '可装备能力', 1},
			{'learn', '兵员固有:', 2},
			{'esper', '幻兽:', 2},--because not a comma-separates list
			{'enable', '能力:', 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 == '能力觉醒' and 'awaken'
		or (namespace == 14
				or p.ability[args.type] and (
					currentTitle == '可装备能力'
					or currentTitle == (p.ability[args.type] == 1 and
						('特殊%s能力'):format(args.type)
						or '魔法'
					)
				)
			) 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 ~= '被动' or p_args[1] == 'unmerge'
		result
			:_'|-\n|'
			:_(learned[1])
			:_'\n|'
			:_(learned[2])
			:_'\n|[[File:'
			:_(args.image)
			:_'|'
			:_(args.zhname or args.name)
			:_'|link='
			:_(args.zhname or args.name)
			:_']]\n|style="text-align:left"|'
			:_(link(args.zhname, true) or 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.zhname 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 ("'''信赖度满点报酬:'''[[%s]]"):format(args.trust))
			table.insert(obtain, args.STMR and ("'''超级信赖度满点报酬:'''[[%s]]"):format(args.STMR))
			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%s || '):format(
					icon(true),
					link(args.zhname, true),
					'<br>' .. args.name
				))
				:_((p.equipment[args.type] or args.type == '被动') 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 "图片!!名称!!类型!!效果"
--					or "style='width:50px'|图片!!style='width:130px'|名称!!效果!--!style='width:50px'|段数!!style='width:50px'|MP")
--				))
				:_"\n|-\n| [[File:"
				:_(args.image or 'Icon-' .. args.name .. '.png')
				:_(('|link=@]]\n| '):gsub('@', args.zhname))
				:_(link(args.zhname, true))
				:_'\n|'
				:_(p.equipment[args.type] and type_link() ..'\n|')
				:_(widget_sort_stats())
				:_(p.equipment[args.type] and
					stats_shopcell()
					or table.concat{
						materia_effects(),
						multi_break(attack_frames(args.hits)),
						multi_break(args.MP_cost)
					}
				)
--				:_(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)
					:_'|使用<br>'
					:_(activator ~= '' and activator
						or args.enable and split_link(args.enable)
						or link(args.zhname)
						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 == '被动',
				args.effect and args.effect:match"几[^\n]+使用'?'?'?(.-)'?'?'?:?<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]-:", '随机使用:'))
						:_(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.zhname, 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 == '能力觉醒' 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] == 'chain') then
									new_effects = mw.text.split(new_effects, new_effects:find'\n' and '\n' or '<br>')
									table.insert(new_effects, bold(chain_link(diff[2], true)))
									new_effects = table.concat(new_effects, '<br>')
								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 '物理', diff[2]--:gsub('%%', '%%%%')
										evade = physical or '魔法'
										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 unit then new_lines.to_replace = {}
					else
						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!兵员!!能力!!class=spacer|基本效果!!class=spacer|觉醒效果'
						or '|-class=subheader\n!colspan=4|'))
					:_'\n|-\n|'
					:_(p_args[2] and
						('rowspan=%s style="text-align:center"|[[%s]]\n|')
							:format(tonumber(p_args[2]) or tonumber(p_args[3]) or 3, unit, unit))
					:_(link(args.zhname, 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!名称!!class=spacer|效果!!style="width:50px"|段数!!style="width:50px"|MP!!style="width:50px"|类型'
					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.zhname, true) or 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 == 'list' then
			result:_'*[[':_(args.name):_']]'
			extend(p_args.sep or '\n**')
		elseif args.mode == 'tooltip' then
			local wikilink, tip, lines =
				link(args.zhname, 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
				'数据:' .. 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)
				:allDone()
			))
		elseif args.mode == 'imagetooltip' 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'ttip tooltip-hide-icon'
					:tag'span'
					:addClass'mobileonly'
					:wikitext(smallicon(true))
				:done()
					:tag'span'
					:addClass'nomobile'
					:wikitext(smallicon(true))
				: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 0'}
							:attr{rowspan=2}
							:wikitext(icon'')
						:done()
							:tag'td'
							:css{font='15px Lato, sans-serif',['font-weight']='700',height='30px'}
							:node(args.name:gsub(' %(%w+%)', ''))
							:node((p.equipment[args.type] or p.ability[args.type] == 2) and mw.html.create'span'
								:css{position='absolute',right='8px',top='5px'}
								: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\n|{{%s|%s}}')--don't remove spaces
					:gsub('@', args.mode == 'item2' and ('{{item|' .. args.name .. '|cn=%%s}}') or '%%s')
					:format(
						args.mode == 'recipe' and '配方:'
							or '',
						args.mode == 'item2' and args.zhname or link(args.zhname, true),
						({recipe='-\n|',typeless='colspan=2'})[args.mode]
							or args.type .. '\n|',
						args.mode == 'recipe' and '-'
							or widget_sort_stats() .. (
								p.equipment[args.type] and stats_shopcell()
								or materia_effects()
							),	
						args.quartz and 'Star Quartz' or 'Gil',
						args.quartz
							or ({item=1,item2=1,typeless=1})[args.mode] and args.buy_gil
							or args.mode == 'recipe' and args.buy_rec
							or '?'
				))
				:_(p_args.limit and ('<br>(限量: %s)'):format(p_args.limit))
			extend()
		end
	else
	--info displayed on item (invoke) page
		result
			:_'__NOTOC__\n__NOEDITSECTION__'
			:_((args.image or args.name) and ('<div class="frame-%s">\n[[File:%s|link=]]\n</div>'):format( 
				p.equipment[args.type] and 'equip' or p.ability[args.type] and 'ability' or 'item',
				args.image or ('Icon-%s.png'):format(args.name),
				args.name or ''
			))
			:_(args.title and "\n\n'''" .. args.title .. "'''")
			:_(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== 资料 ==\n\n*'''类型:'''"
					:_(args.type == '首饰' and '[[:Category:首饰|首饰]]'
						or p.equipment._class[p.equipment[args.type]] .. type_link(true))
					:_"\n*'''数据:'''"
					:_(stats_sorted[1] and table.concat(stats_sorted, '、') or '-')
					:_"\n*'''属性:'''"
					:_(args.element or '-')
					:_"\n*'''耐性:'''"
					:_(args.resist and split_link(args.resist, false, nil, ailment_link) or '-')
					:_"\n*'''附加效果:'''"
					:_(#materia > 0 and 
						link2tooltip(args.ability and (--replace link2tooltip after all abilities use Data
							args.effect and 
								"%s\n*'''附加能力:'''%s"
								or "- \n*'''附加能力:'''%s"
							):format(unpack(materia))
							or materia[1], true) .. (args.warning and "\n*'''备注:'''" .. args.warning or '')
						or (args.warning and "- \n*'''备注:'''" .. args.warning or '-')
						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== 数据 ==\n*'''类型:'''"
					:_(p.ability._class[p.ability[args.type]])
					:_' ('
					:_(args.type)
					:_(p.ability[args.type] == 2 and '魔法 Lv ' .. args.mag_lv)
					:_")\n*'''效果:'''\n:"
					:_(materia_effects(false, true))
					:_(args.chain and "\n*'''连击家族:''' " .. chain_link(args.chain))
					:_(args.hits and "\n*'''段数:''' " .. attack_frames(args.hits))
					:_(args.MP_cost and "\n*'''法力值:''' " .. args.MP_cost)
					if args.ability or flags.rng_enable then
						result
							:_'\n==能力==\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" | 段数 !! width="50px" | MP\n'
								:_(p.item(frame))
								:_'\n|}'
						end
					end
			elseif args.type == '道具' and args.effect then
				result
					:_'\n==  效用 ==\n'
					:_(materia_effects())
			end
		end
		result
			:_(args.notes and '\n==备注==\n')
			:_(args.notes)
			:_'\n==制作配方==\n'
			:_(args.recipe and args.recipe ~= 'none' and
				('{|class=wikitable style="text-align:center"\n!素材!!Gil\n|-\n|align=left|%s\n||{{Gil|%s}}\n|}'):format(
					args.rec_mat and link2tooltip(frame:preprocess(split_link(args.rec_mat, nil, '<br>', function(v)
						local item, quantity, check = 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),
					args.rec_gil or '?')
				or '无'--, args.time or '?') or 'None'
			)
			:_((args.usage or args.quest) and result
				:_"\n==用途=="
				:_(args.awk_mat and "\n觉醒素材:\n*" .. split_link(args.quest, nil, '\n:*'))
				:_(args.usage and "\n制作素材:" .. multi_split(args.usage, "\n:%s:\n:*", '\n:*', '\n:*'))
				:_(args.quest and "\n任务:\n*" .. split_link(args.quest, nil, '\n*'))
				and nil
			)
			:_"\n==获得方式=="
		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:match'^ *%[%[.+%]%] *$' 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]]:match'^{|' then
					result:_(args[froms[i][1]])
				elseif args[froms[i][1]]:match'^!' then
					simpleTable(args[froms[i][1]])
				else result
					:_"*"
					:_(split_link(args[froms[i][1]], nil, '\n*', froms[i][1] == '装备' and tooltip_link))
				end
			end
		end
		result
			:_(args.STMR and ("\n'''超级信赖度满点报酬:'''\n:*[[%s]]"):format(args.STMR))
			:_(args.trust and ("\n信赖度满点报酬:\n*[[%s]]"):format(args.trust))
			:_(args.obtain and multi_split(args.obtain, "\n%s:\n*", '\n*', '\n*'))
			:_(args.used_by and "\n==专用兵员==\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==能力觉醒==',
					'width:100%%;margin-right:-99vh;%1"%2"\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('!名称', '!style="width:5em"|兵员/能力!%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 '' 
							) .. '<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.STMR or args.obtain then
					table.insert(categories, '物品')
					table.insert(categories, '可装备能力')	
				elseif args.esper then table.insert(categories, '可装备能力') end
				if p.ability[args.type] == 1 then
					table.insert(categories, ('%s%s'):format(
					    args.type,
						p.ability._class[p.ability[args.type]]
					))
				else
					table.insert(categories, '魔法')
					table.insert(categories, args.type .. '魔法')
					table.insert(categories, args.type .. '魔法等级' .. args.mag_lv)
				end
			else 
				table.insert(categories, '物品')
				if p.equipment[args.type] then
					table.insert(categories, '装备')
					if args.type == '首饰' then
						table.insert(categories, '首饰')
					else
						table.insert(categories, p.equipment._class[p.equipment[args.type]])
						table.insert(categories, p.equipment._plural[args.type] or args.type)
					end
				else
					if args.usage or args.awk_mat then table.insert(categories, '素材') end
				end
			end
		end
		if (args.recipe or args.reward or ''):find(p.patterns.event) then table.insert(categories, '活动物品') end
		for _, k in ipairs{
			--{arg key, category name}, if key is not nil, put in cat
			{'recipe', '制作配方'},
			{'buy_gil', '可购买'},
			{'STMR', '超级信赖度满点报酬'},
			{'trust', '信赖度满点报酬'},
			{hasExclusiveFX and 'effect' or 'used_by', '兵员专用物品'},
			{'awk_mat', '觉醒素材'},
			{'usage', '制作素材'},
			{'quest', '任务物品'},
			{'equip', '装备能力'},
			{'esper', '幻兽能力'}
		} do
			if args[k[1]] then table.insert(categories, k[2]) end
		end
		result
			:_((mw.title.getCurrentTitle().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 == '道具店' or name == '能力店') and
					'colspan=2| '
					or '类型!!'
				)
				:_'效果!!价钱'
				:_(v)
		end
		result:_'\n|}'
	end
	return frame:preprocess(table.concat(result))
end

return p

-- 08220856 co
-- 08171245 (tooltip X)

导航菜单