2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

Module:Akopbasics

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索
Template-info.svg 模块文档  [查看] [编辑] [历史] [刷新]
本模块用来生成新版Template:明日方舟干员的主体结构。
-- This is a redo of the original T:明日方舟干员. New styling and probably worse performance, xd.
-- Sorry, IE. Something is just too new for you. 
-- Contains now only the data processing logic; requires M:Akopbasics/genwikitext to function.
-- DEPLOYED as T:明日方舟干员.
-- Crappy code originally by U:公的驱逐舰 (One-Six) of Moegirlpedia. Released under CC BY 4.0.

-- update: I shall claim this release as R1.2.0! It's the second merge from the /canary branch.
-- As /canary contains no work from other editors, 
-- this particular version (oldid=443022) should be safely eleased under CC BY 4.0 again.
-- Thank you to everyone who kept this module alive through 2021.

-- Gotta praise the Crocc. 

local p = {}

local getArgs = require ( 'Module:Arguments' ).getArgs
local getSabunArray = require ( 'Module:Aksabun' )._getSabunArray
local akMatData = require ( 'Module:明日方舟材料' )._skill
local genOpComplex = require ( 'Module:Akopbasics/genwikitext' ).genOpComplex
local wrapperArray = { 'Template:明日方舟干员', 'Template:明日方舟干员/canary', 'Template:沙盒' }

-- F: is string empty?
local function isEmpty( s )
	return (s == nil or s == '')
end
-- F: get arguments, alias-aware. Aliases towards the front of the array have priority.
local function getArgFromAlias ( args, argsAliasArray, defaultReturn )
	for i = 1, #argsAliasArray do
		if ( args[argsAliasArray[i]] ~= nil ) then
			return args[argsAliasArray[i]]
		end
	end
	return defaultReturn
end
-- F: same as getArgFromAlias, but will also ignore empty strings. Default return is still nil, check compatibility.
local function getNonEmptyArgs ( args, argsAliasArray, defaultReturn )
	for i = 1, #argsAliasArray do
		if ( not isEmpty( args[argsAliasArray[i]] ) ) then
			return args[argsAliasArray[i]]
		end
	end
	return defaultReturn
end
-- F: generate parameter array from string; insert a common string for each instance
local function parseArray ( s, ins )
	-- When input string is entirely empty, return empty array.
	if ( isEmpty(s) ) then
		return {}
	end
	-- if common string is empty, make damn sure it's empty, not nil
	if ( isEmpty(ins) ) then
		ins = ''
	end
	local parsedArray = {}
	-- \239\188\155 是UTF-8全角分号(";")
	for temp in (s:gsub("\239\188\155",";"):gsub("[;]?%s*$",";") ):gmatch("([^;]*);%s*") do 
		local tempArray = {}
		-- \227\128\129 是UTF-8顿号("、")
		for w in (temp:gsub("\227\128\129","\\"):gsub("[\\]?%s*$","\\")..ins):gmatch("([^\\]*)\\%s*") do 
			tempArray[#tempArray+1] = w 
		end
	   	parsedArray[#parsedArray+1] = tempArray
	end
	return parsedArray
end


-- F: generate datatable for genOpComplex.
local function genData ( frame, args, opType )
	local d = {}
	-- expected components
	d.name = getArgFromAlias ( args, { "name", "中文名" } ) -- MUST BE PROVIDED BY USER
	d.dataName = getArgFromAlias ( args, { "data-name", "数据名" }, d.name )
	d.rarity = getArgFromAlias ( args, { "rarity", "稀有度" }, '' )
	d.rarity = tonumber( d.rarity ) or #d.rarity/3
	d.profession = getArgFromAlias ( args, { "profession","职业" } )
	d.placementTag = getArgFromAlias ( args, { "placement-tag","位置标签","站位" } )
	if isEmpty(d.placementTag) then
		if d.profession == "狙击" or d.profession == "术师" or d.profession == "医疗" or d.profession == "辅助" then
			placementTag = '远程位'
		elseif d.profession == "先锋" or d.profession == "近卫" or d.profession == "重装" then
			d.placementTag = "近战位"
		end
	end
		-- the radarPPS monstrosity
	local radarText = {
		"物理强度",
		"战场机动",
		"生理耐受",
		"战术规划",
		"战斗技巧",
		"源石<br />技艺<br />适应性"
	}
	local radarTextOffset = {"-32px","-24px"}
	local radarNumText = {}
	local radarNum = {}
	local radarLookup = {
		[""] = 0,
		["N/A"] = 0,
		["---"] = 0,
		["缺陷"] = 20,
		["普通"] = 40,
		["标准"] = 60,
		["优良"] = 80,
		["卓越"] = 100,
		["■■"] = 100
	}
	local isRobot = ( string.lower( opType or '' ) == "robot" )
	if isRobot then 
		radarText = {
			"最高速度",
			"爬坡能力",
			"制动性能",
			"通过性",
			"续航",
			"结构<br />稳定性"
		}
		radarTextOffset = {"-24px","-10px"}
		radarNumText[1] = getNonEmptyArgs ( args, { "radar-text1","雷达图文字1", "最高速度"},"---" ):gsub("([Kk][Mm]/[Hh])?$","km/h")
		local speed = radarNumText[1]:gsub("[^0-9%.]*","" )
		speed = tonumber( speed ~= '' and speed or 0 )
		radarNum[1] = tonumber( getNonEmptyArgs ( args, { "radar-num1","雷达图数值1", "最高速度数值"} ) ) or math.log(speed+1)*12.47+speed
		radarNumText[6] = getNonEmptyArgs ( args, { "radar-text6","雷达图文字6","结构稳定性" },"---" )
		radarNum[6] = tonumber( getNonEmptyArgs ( args, { "radar-num"..6,"雷达图数值"..6, "结构稳定性数值"} ) ) or radarLookup[radarNumText[6]]
	else
		radarNumText[1] = getNonEmptyArgs ( args, { "radar-text1","雷达图文字1", "物理强度"},"---" )
		radarNum[1] = tonumber( getNonEmptyArgs ( args, { "radar-num1","雷达图数值1", "物理强度数值"} ) ) or radarLookup[radarNumText[1]]
		radarNumText[6] = getNonEmptyArgs ( args, { "radar-text6","雷达图文字6","源石技艺适应性" },"---" )
		radarNum[6] = tonumber( getNonEmptyArgs ( args, { "radar-num6","雷达图数值6","源石技艺适应性数值"} ) ) or radarLookup[radarNumText[6]]
	end
	for i = 2, 5 do
		radarNumText[i] = getNonEmptyArgs ( args, { "radar-text"..i,"雷达图文字"..i,radarText[i] },"---" )
		radarNum[i] = tonumber( getNonEmptyArgs ( args, { "radar-num"..i,"雷达图数值"..i, radarText[i].."数值"} ) ) or radarLookup[radarNumText[i]]
	end
	local radarString = '{{雷达图|size=100'
	radarString = radarString .. '|text1=<span class="aoc-radar-cat-text" style="display:inline-block;position:absolute;width:60px;left:-12px;font-size:14px;">'..radarText[1]..'</span>'
	radarString = radarString .. '|var1='..radarNum[1]
	radarString = radarString .. '|num1=<span style="display:inline-block;position:absolute;text-align: center;width:max-content;left:6px;top:12px;transform:translateX(-50%);background-color:rgba(255,255,255,0.3);border-radius:5px;font-size:14px;">'..radarNumText[1]..'</span>'
	radarString = radarString .. '|text2=<span class="aoc-radar-cat-text" style="display:inline-block;position:absolute;width:30px;left:-30px;top:-10px;font-size:14px;">'..radarText[2]..'</span>'
	radarString = radarString .. '|var2='..radarNum[2]
	radarString = radarString .. '|num2=<span style="display:inline-block;position:absolute;text-align: center;width:30px;left:-35px;top:5px;background-color:rgba(255,255,255,0.3);border-radius:5px;font-size:14px;">'..radarNumText[2]..'</span>'
	radarString = radarString .. '|text3=<span class="aoc-radar-cat-text" style="display:inline-block;position:absolute;width:30px;left:-30px;top:-32px;font-size:14px;">'..radarText[3]..'</span>'
	radarString = radarString .. '|var3='..radarNum[3]
	radarString = radarString .. '|num3=<span style="display:inline-block;position:absolute;width:30px;left:-35px;top:-27px;background-color:rgba(255,255,255,0.3);border-radius:5px;font-size:14px;">'..radarNumText[3]..'</span>'
	radarString = radarString .. '|text4=<span class="aoc-radar-cat-text" style="display:inline-block;position:absolute;text-align: center;width:60px;left:-12px;top:-20px;font-size:14px;">'..radarText[4]..'</span>'
	radarString = radarString .. '|var4='..radarNum[4]
	radarString = radarString .. '|num4=<span style="display:inline-block;position:absolute;text-align: center;width:30px;left:-7px;top:-35px;background-color:rgba(255,255,255,0.3);border-radius:5px;font-size:14px;">'..radarNumText[4]..'</span>'
	radarString = radarString .. '|text5=<span class="aoc-radar-cat-text" style="display:inline-block;position:absolute;width:30px;top:'..radarTextOffset[1]..';font-size:14px;">'..radarText[5]..'</span>'
	radarString = radarString .. '|var5='..radarNum[5]
	radarString = radarString .. '|num5=<span style="display:inline-block;position:absolute;text-align: center;width:30px;left:5px;top:-27px;background-color:rgba(255,255,255,0.3);border-radius:5px;font-size:14px;">'..radarNumText[5]..'</span>'
	radarString = radarString .. '|text6=<span class="aoc-radar-cat-text" style="display:inline-block;position:absolute;width:45px;left:-15px;top:'..radarTextOffset[2]..';text-align:center;font-size:14px;">'..radarText[6]..'</span>'
	radarString = radarString .. '|var6='..radarNum[6]
	radarString = radarString .. '|num6=<span style="display:inline-block;position:absolute;text-align: center;width:30px;left:5px;top:5px;background-color:rgba(255,255,255,0.3);border-radius:5px;font-size:14px;">'..radarNumText[6]..'</span>'
    radarString = radarString .. '|color=#999|bgcolor=#333|ftcolor=#000|numcolor=white|num=}}'
    d.radarPPS = frame:preprocess( radarString )
    radarString = nil
    radarText = nil
	radarTextOffset = nil
	radarNumText = nil
	radarNum = nil
	radarLookup = nil
	
	-- optional components
	d.categoryArray = {}
	d.enName = getArgFromAlias ( args, { "en-name", "英文名" } )
	d.subProfession = getArgFromAlias ( args, { "sub-profession", "branch", "分支", "characterbranch" } )
	d.operatorModule = getArgFromAlias ( args, { "operatorModule", "模组", "characterequipt" } )
	d.altOpOverride = getArgFromAlias ( args, { "alt-op-override", "spcharacter", "异格任务" } )
	
	d.spIllustrator = getNonEmptyArgs ( args, { "spIllustrator", "multi-illustrator", "特殊画师", "多位画师" }, nil )
	if d.spIllustrator == nil then 
		d.illustrator = parseArray ( getNonEmptyArgs ( args, { "illustrator", "illust", "画师" } ) )
		mw.logObject(d.illustrator)
		if isEmpty ( d.illustrator[1] ) then
			d.illustrator = nil
			d.spIllustrator = '无资料'
		else
			local i = 1 
			while not isEmpty ( d.illustrator[i] ) do
				if d.illustrator[i][3] == "FORCENOLINK" then
					d.illustrator[i][3] = nil
					d.illustrator[i]["nolink"] = true
				end
				i=i+1
			end
		end
	end
	d.spcv = getNonEmptyArgs ( args, { "spcv", "multi-cv", "特殊配音", "多位配音" }, nil )
	if d.spcv == nil then 
		d.cv = parseArray ( getNonEmptyArgs ( args, { "cv","配音" } ) )
		mw.logObject(d.cv)
		if isEmpty ( d.cv[1] ) then
			d.cv = nil
			d.spcv = '无资料'
		else
		-- add CV name(s) into category array. TODO: remove category processing
			local i = 1 
			while not isEmpty ( d.cv[i] ) do
				if d.cv[i][3] == "FORCENOLINK" then
					d.cv[i][3] = nil
					d.cv[i]["nolink"] = true
				end
				d.categoryArray[#d.categoryArray+1] = d.cv[i][1] .. '配音角色'
				i = i + 1
			end
		end
	end
	
		-- tachie array
	local tachieInfo = {
		getNonEmptyArgs( args, { "e0name", "初始名" }, "初始"),
		getNonEmptyArgs( args, { "e0img",  "初始图" }, '明日方舟立绘_' .. d.name .. '_1.png' ),
		getNonEmptyArgs( args, { "e1name", "精一名", "精1名" }, "精英一"),
		getNonEmptyArgs( args, { "e1img",  "精一图", "精1图" }, '明日方舟立绘_' .. d.name .. '_1+.png' ),
		getNonEmptyArgs( args, { "e2name", "精二名", "精2名" }, "精英二"),
		getNonEmptyArgs( args, { "e2img",  "精二图", "精2图" }, '明日方舟立绘_' .. d.name .. '_2.png' )
	}
	d.tachieArray = {}
		-- All operators have E0 state and E0 tachie's. Generate unless explicitly disabled.
	if tachieInfo[1] ~= "FORCENOTAB" then
		d.tachieArray[#d.tachieArray+1] = { label = tachieInfo[1], file = tachieInfo[2] }
	end
		-- Amiya is the only operator right now that possesses E1 tachie; treat as special case, generate unless explicitly disabled.
	if d.name == '阿米娅' and tachieInfo[3] ~= "FORCENOTAB" then
		d.tachieArray[#d.tachieArray+1] = { label = tachieInfo[3], file = tachieInfo[4] }
	end
		-- All 4star+ operators have E2 states and E2 tachie's. Generate unless explicitly disabled.
	if d.rarity > 3 and tachieInfo[5] ~= "FORCENOTAB" then
		d.tachieArray[#d.tachieArray+1] = { label = tachieInfo[5], file = tachieInfo[6] }
	end
	tachieInfo = nil
		-- skins.
	local skinArray = parseArray ( getArgFromAlias ( args, { "skins","skin","时装","服装" } ) )
	for i = 1, #skinArray do
		-- if no explicit skin filename is provided, generate filename according to naming customs.
		if isEmpty(skinArray[i][2]) then
			skinArray[i][2] = '明日方舟立绘_'..d.name..'_skin'..i..'.png'
		end
		-- attach skin array.
		d.tachieArray[#d.tachieArray+1] = { label = skinArray[i][1], file = skinArray[i][2] }
	end
	skinArray = nil
		-- sabun's. TODO: offload work to new M:Aksabun (WIP)
	d.sabunArray = {}
	local sabunType = getArgFromAlias ( args, { "sabun-type","差分类型","sabun-type1","差分类型1" } )
	local sabunCode = getArgFromAlias ( args, { "sabun-code","差分代号","sabun-code1","差分代号1" } )
	local manualSabun = getArgFromAlias ( args, { "manual-sabun1","手动差分1" } )
	i = 1
	while ( ( not isEmpty( sabunCode ) ) or ( not isEmpty( manualSabun ) ) ) do
		sabunType = ( sabunType == 'npc' or sabunType == 'avg_npc' ) and 'avg_npc' or 'char'
		d.sabunArray[#d.sabunArray+1] = getSabunArray ( frame, {
			verbose = manualSabun,
			["suffix-list"] = getArgFromAlias ( args, { "sabun-suffix"..i, "差分后缀"..i } ),
			["sabun-type"] = sabunType,
			["sabun-code"] = sabunCode,
			separator = getArgFromAlias ( args, { "sabun-sep"..i, "差分分隔"..i } ),
			first = getArgFromAlias ( args, { "sabun-f"..i, "差分首"..i } ),
			last = getArgFromAlias ( args, { "sabun-l"..i, "差分末"..i } ),
			omit = getArgFromAlias ( args, { "sabun-omit"..i, "差分忽略"..i } )
		} )
		i = i + 1
		sabunType = getArgFromAlias ( args, { ("sabun-type"..i),("差分类型"..i) } )
		sabunCode = getArgFromAlias ( args, { ("sabun-code"..i),("差分代号"..i) } )
		manualSabun = getArgFromAlias ( args, { "manual-sabun"..i,"手动差分"..i } )
	end
	sabunType = nil
	sabunCode = nil
	manualSabun = nil
	-- other tachie. This includes lone AVG tachie's, tachie of related sub-characters/objects (e.g. summons, pets...), etc.
	d.otherTachie =  parseArray ( getArgFromAlias ( args, { "other-tachie","其他立绘" } ) )
	for i = 1, #d.otherTachie do
		d.otherTachie[i] = d.otherTachie[i][1]
	end

	d.faction = getNonEmptyArgs ( args, { "faction","势力" }, "罗德岛" )
	d.factionImgPath = frame:callParserFunction('filepath:Aklogo_'..d.faction..'.png')
		-- main game data.
	d.data = {
		maxHealth = getNonEmptyArgs ( args, { "max-health","生命上限" }, '暂无数据' ),
		attack = getNonEmptyArgs ( args, { "attack","atk","攻击" }, '暂无数据' ),
		defense = getNonEmptyArgs ( args, { "defense","def","防御" }, '暂无数据' ),
		artsResistance = getNonEmptyArgs ( args, { "arts-resist","法术抗性" }, '暂无数据' ),
		redeploy = getNonEmptyArgs ( args, { "redeploy","再部署" }, '暂无数据' ),
		cost = getNonEmptyArgs ( args, { "cost","部署费用" }, '暂无数据' ),
		block = getNonEmptyArgs ( args, { "block","阻挡数" }, '暂无数据' ),
		attackInterval = getNonEmptyArgs ( args, { "atk-int","攻击速度" }, '暂无数据' )
	}
		-- range. Must use Akrange-compatible descrpition strings.
	d.rangeArray = { { label = 'E0', rangeString = getArgFromAlias ( args, { "E0range","初始范围" } ) } }
	if not isEmpty(getArgFromAlias ( args, { "E1range","精一范围","精1范围" } )) then
		d.rangeArray[#d.rangeArray+1] = { label = 'E1', rangeString = getArgFromAlias ( args, { "E1range","精一范围","精1范围" } ) }
	end
	if not isEmpty(getArgFromAlias ( args, { "E2range","精二范围","精2范围" } )) then
		d.rangeArray[#d.rangeArray+1] = { label = 'E2', rangeString = getArgFromAlias ( args, { "E2range","精二范围","精2范围" } ) }
	end
	if not isEmpty(getArgFromAlias ( args, { "modulerange","模组范围","模组范围" } )) then
		d.rangeArray[#d.rangeArray+1] = { label = '模组', rangeString = getArgFromAlias ( args, { "modulerange","模组范围","模组范围" } ) }
	end
		-- potential buffs. 
	d.potentialBuffArray = {}
		-- get raw potential. if it actually exists, then make Lookup table.
	local rawPotential = getArgFromAlias ( args, { "potential","潜能" } )
	if rawPotential then
		local potentialNameLookup = {
			["n/a"] = false, ["false"] = false, [""] = false, [" "] = false, ["nil"] = false, 
			["生命上限"]  = "生命", ["生命"] = "生命", ["health"]      = "生命", ["hp"]     = "生命",
			["攻击力"]    = "攻击", ["攻击"] = "攻击", ["attack"]      = "攻击", ["atk"]    = "攻击",
			["防御力"]    = "防御", ["防御"] = "防御", ["defense"]     = "防御", ["def"]    = "生命",
			["法术抗性"]  = "法抗", ["法抗"] = "法抗", ["arts-resist"] = "法抗", ["ar"]     = "法抗",
			["再部署时间"]="再部署",["再部署"]="再部署",                         ["redeploy"]="再部署",
			["部署费用"]  = "费用", ["费用"] = "费用",                           ["cost"]   = "费用",
			["阻挡数"]    = "阻挡", ["阻挡"] = "阻挡",                           ["block"]  = "阻挡",
			["攻击速度"]  = "攻速", ["攻速"] = "攻速",                           ["atk-int"]= "攻速",
			                        ["天赋"] = "天赋",                           ["talent"] = "天赋",
			["第一天赋"] ="天赋一", ["天赋一"]="天赋一",                         ["talent1"]= "天赋一",
			["第二天赋"] ="天赋二", ["天赋二"]="天赋二",                         ["talent2"]= "天赋二"
		}
		-- parse raw potential.
		d.orderedPotentialBuffArray = {}
		-- \239\188\155 是UTF-8全角分号(";")
		i = 1
		for item in (rawPotential:gsub("\239\188\155",";"):gsub("[;]?%s*$",";") ):gmatch("([^;]*);%s*") do 
			-- \227\128\129 是UTF-8顿号("、")
			local tempArray = {}
			for w in (item:gsub("\227\128\129","\\"):gsub("[\\]?%s*$","\\")):gmatch("([^\\]*)\\%s*") do 
				tempArray[#tempArray+1] = string.lower( w )
			end
			d.orderedPotentialBuffArray[i] = {}
			d.orderedPotentialBuffArray[i][1] = potentialNameLookup[ tostring ( tempArray[1] ) ]
			if d.orderedPotentialBuffArray[i][1] then 
				d.orderedPotentialBuffArray[i][2] = tempArray[2]
				-- remove "秒" and non-number-nor-sign characters
				local temp = tempArray[2]:gsub('\xe7\xa7\x92$',''):gsub('[^%d+-]','')
				temp = tonumber( temp )
				if temp then d.potentialBuffArray[ d.orderedPotentialBuffArray[i][1] ] = ( d.potentialBuffArray[ d.orderedPotentialBuffArray[i][1] ] or 0 ) + temp end
			end
			i = i + 1
		end
		-- if provided potential buff is less then 5, fill the rest with emtpy array	
		while i <= 5 do
			d.orderedPotentialBuffArray[i] = {}
			i = i + 1
		end
		rawPotential = nil
		-- provide plus sign
		for k,v in pairs(d.potentialBuffArray) do
			if v > 0 then d.potentialBuffArray[k] = '+'..v end
		end
	end
	
	-- tag-characteristic-talent
	d.tags = getArgFromAlias ( args, { "tags","标签" }, '' )
	d.characteristic = getNonEmptyArgs ( args, { "characteristic","特性" }, '暂无资料' )
	d.talent = getArgFromAlias ( args, { "talent","天赋"}, '' )
	if d.talent == ''  then
		local talentComponent = getNonEmptyArgs ( args, { "talent1","天赋1"} )
		if talentComponent ~= nil then
			d.talent = talentComponent
			talentComponent = getNonEmptyArgs ( args, { "talent2","天赋2"} )
			i = 2
			while talentComponent do
				d.talent = d.talent .. '<hr />' .. talentComponent
				i = i + 1
				talentComponent = getNonEmptyArgs ( args, { "talent"..i,"天赋1"..i} )
			end
		else
			d.talent = '暂无资料'
		end
	end
	d.talent = d.talent:gsub('%<%/talent%>','</span>'):gsub('%<talent%>','<span class="talentblock">')
	
	-- rouge stuff
	local rogueOnlyRaw =  string.lower ( getArgFromAlias ( args, { 'rogue-only', '肉鸽限定' }, '' ) )
	d.isRogueOnly = ( rogueOnlyRaw == "true" or rogueOnlyRaw == "yes" or rogueOnlyRaw == "是" or rogueOnlyRaw == "was" or rogueOnlyRaw == "曾" )
	d.wasRogueOnly = ( rogueOnlyRaw == "was" or rogueOnlyRaw == "曾" )
	rogueOnlyRaw = nil
	d.rogueEvents = getNonEmptyArgs ( args, { 'rogue-events', 'rogue-event', '肉鸽活动' } )
	
	-- skills and related material consumptions
	d.skillArray = {}
	d.skillArray.useSwitch = string.lower( getArgFromAlias ( args, { 'skill-switch', '技能切换' }, '' ) )
	d.skillArray.useSwitch = not ( d.skillArray.useSwitch == "false" or d.skillArray.useSwitch == "no" or d.skillArray.useSwitch == "否" )
	d.skillArray.useItem = string.lower( getArgFromAlias ( args, { 'display-item', '显示材料' }, '' ) )
	d.skillArray.useItem = not ( d.skillArray.useItem == "false" or d.skillArray.useItem == "no" or d.skillArray.useItem == "否" )
	local possibleSkillDesp = getArgFromAlias ( args, { "sk1", "技能1", "技能一" } )
	local i = 1
	local skillLookup = { '一', '二', '三', '' } -- need an extra item so skillLookup[i+1] after the last item is exhausted wouldn't throw err
	while possibleSkillDesp ~= nil and i <= #skillLookup - 1 do
		local tabString = ( d.skillArray.useItem and d.rarity >= 4 ) and frame:preprocess( "{{tabs|color=black aoc-skill-masterTabs|DefaultTab=1|AutoWidth=yes"
			..'|bt1=专精一|tab1=[[File:明日方舟技能专精 1.png|50px|link=]]' .. akMatData ( frame, { d.dataName, 'v'..i..'7' } )
			..'|bt2=专精二|tab2=[[File:明日方舟技能专精 2.png|50px|link=]]' .. akMatData ( frame, { d.dataName, 'v'..i..'8' } )
			..'|bt3=专精三|tab3=[[File:明日方舟技能专精 3.png|50px|link=]]' .. akMatData ( frame, { d.dataName, 'v'..i..'9' } )..'}}'
		) or ''
		d.skillArray[i] = {
			skillName = getArgFromAlias ( args, { ('sk'..i..'name'), ('技能'..i..'名称'), ('技能'..skillLookup[i]..'名称') } ),
			activation = getArgFromAlias ( args, { "sk"..i.."a", "技能"..i.."触发", "技能"..skillLookup[i].."触发" }, ''),
			skillPoint = getArgFromAlias ( args, { "sk"..i.."sp", "技能"..i.."技力", "技能"..skillLookup[i].."技力" }, ''),
			description = possibleSkillDesp,
			masterTabsPPS = tabString
		}
		d.skillArray[i].icon = getArgFromAlias ( args, { "skill"..i.."icon", "技能"..i.."图", "技能"..skillLookup[i].."图", "技能"..i.."补", "技能"..skillLookup[i].."补" }, '明日方舟技能_'..(d.skillArray[i].skillName or '')..'.png' )
		i = i + 1
		possibleSkillDesp = getArgFromAlias ( args, { "sk"..i, "技能"..i, "技能"..skillLookup[i] } )
	end
	d.skillArray.switchPPS = frame:expandTemplate { title = '明日方舟技能条', ["args"] = { [1]='#style' } } -- '<templatestyles src="Template:明日方舟技能条/style.css" />'
	if  d.rarity >= 4 then -- don't know why the and-or shorthand doesn't work
		d.skillArray.switchPPS = d.skillArray.switchPPS .. frame:expandTemplate { title = '明日方舟技能条', ["args"] = { [1]='#skill2' } }
	else
		d.skillArray.switchPPS = d.skillArray.switchPPS .. frame:expandTemplate { title = '明日方舟技能条', ["args"] = { [1]='#skill1' } }
	end
	
	-- base skills.
	d.baseSkillArray = {}
	i = 1
	local baseSkDesp = getArgFromAlias ( args, { "bsk"..i, "后勤"..i } )
	while baseSkDesp do
		d.baseSkillArray[#d.baseSkillArray+1] = { icon = getArgFromAlias ( args, { "bsk-icon"..i, "后勤"..i.."图标" } ), description = baseSkDesp }
		i = i + 1
		baseSkDesp = getArgFromAlias ( args, { "bsk"..i, "后勤"..i } )
	end
	
	--if not in article space, destroy category array
	if frame:preprocess('{{NAMESPACENUMBER}}') ~= '0' then d.categoryArray = nil end
	
	return d
end

function p.main ( frame )
	local args = getArgs ( frame, { wrappers = wrapperArray	} )
	return p._exposed ( frame, args, "normal" )
end

function p.robot ( frame )
	local args = getArgs ( frame, { wrappers = wrapperArray	} )
	return p._exposed ( frame, args, "robot" )
end

function p.exposed ( frame )
	local args = getArgs ( frame, { wrappers = wrapperArray	} )
	return p._exposed ( frame, args, args["op-type"] )
end
function p._exposed ( frame, args, opType )
	return genOpComplex ( frame, genData ( frame, args, opType ) )
end

return p