来自碧蓝航线海事局

此模块的文档可以在模块:舰娘台词/doc创建

--[[
{{#invoke: 舰娘台词 | 台词表格
| name = 胡德
| touch = 我随时能够行动
| home = 漂亮的战斗,指挥官
| main_1 = 一名优秀的指挥官对于红茶应该有深入的了解
| main_2 = 这个字是……啊,原来刚才看岔了,抱歉,我的视力不太好呢
| main_3 = 多听,少说,您应当多听取他人的意见,但保留自己的判断 
| feeling1 = …………我会听从你的命令
| feeling1_jp = 私はあなたの命令に従います。
| feeling1_mediaFile = 胡德feeling1.mp3
}}	]]

--[[
{{#invoke: 舰娘台词 | 台词面板
| 标题1 = 其他台词
| 内容1 = {{#invoke: 舰娘台词 | 台词表格
  | profile = 汝想知道些什么?
  | drop_descrip = 旧V级驱逐舰—吸血鬼,舷号D68
  }}
| 标题2 = 吸血鬼·婚纱
| 内容2 = {{#invoke: 舰娘台词 | 台词表格
  | profile = 汝想知道些什么?
  | drop_descrip = 旧V级驱逐舰—吸血鬼,舷号D68
  }}
}}	]]


local p = {}

KeyNameDict = {
	extra	=	'登陆界面',
	drop_descrip	=	'舰船型号',
	profile	=	'自我介绍',
	desc	=	'皮肤描述',
	unlock	=	'获取台词',
	
	--主界面
	login	=	'登录台词',
	detail	=	'查看详情',
	main	=	'主界面',
	touch	=	'触摸台词',
	touch2	=	'特殊触摸',
	headtouch	=	'摸头台词',
	
	--场景
	mission	=	'任务提醒',
	mission_complete	=	'任务完成',
	mail	=	'邮件提醒',
	home	=	'回港台词',

	--好感度
	feeling1	=	'好感度-失望',
	feeling2	=	'好感度-陌生',
	feeling3	=	'好感度-友好',
	feeling4	=	'好感度-喜欢',
	feeling5	=	'好感度-爱',
	propose	=	'誓约台词',

	--场景
	expedition	=	'委托完成',
	upgrade	=	'强化成功',

	--战斗台词
	battle	=	'旗舰开战',
	win_mvp	=	'胜利台词',
	lose	=	'失败台词',
	skill =	'技能台词',
	hp_warning	=	'血量告急',

	couple_encourage = '彩蛋台词'
}

KeyList = {
	'extra',
	'drop_descrip',
	'profile',
	'desc',
	'unlock',
	
	--主界面
	'login',
	'detail',
	'main',
	'touch',
	'touch2',
	'headtouch',
	
	--场景
	'mission',
	'mission_complete',
	'mail',
	'home',

	--好感度
	'feeling1',
	'feeling2',
	'feeling3',
	'feeling4',
	'feeling5',
	'propose',

	--场景
	'expedition',
	'upgrade',

	--战斗台词
	'battle',
	'win_mvp',
	'lose',
	'skill',
	'hp_warning',

	'couple_encourage'
}

CollapsableKeys = {
	'hp_warning',
	'headtouch',
	'touch2'
}

local frame = mw.getCurrentFrame()

--------------------------------------------------
--	主要过程
--------------------------------------------------

local sm_html = [[
<div class='sm-bar'><a title="%s" class="sm-button sm-play-pause"></a>
<div class='sm-audio-src'>%s</div>
</div>]]
--生成语音的HTML并剔除台词文本中的<div>
function ParseMediaFileToHTML(mediaFile, wordLine, tooltip)
	local text = wordLine:gsub('<div.+</div>', '')
	local html = wordLine:match('<div.+</div>')
	
	if not mediaFile then
		--兼容 直接在嵌套{{Player|xx.mp3}}
		if html and not html:match('//%S+%.mp3') then
			mw.log('[舰娘台词]: 参数的<div>中没有包含mp3文件')
			mw.log(wordLine)
			return nil, text
		end
		return html, text
	end
	
	if html then
		mw.log('[舰娘台词]: 参数中不应当包含<div>')
		mw.log(wordLine)
	end
	
	--将文件名转换为URL
	if not mediaFile:match('^https?://') then
		local file = frame:callParserFunction('filepath', mediaFile) or ''
		if file == '' then
			mw.log('[舰娘台词]: 媒体文件不存在'..mediaFile)
			return nil, text
		end
		mediaFile = file
	end
	
	html = sm_html:format(tooltip or '播放/停止', mediaFile)
	
	return html, text
end

-- 根据包含台词信息的数据包解析为表格(面板的一个页面)
function ShowShipWordsTable(data)
	local result = [[
<table class="table-ShipWordsTable">
]]
	for _, k in ipairs(KeyList) do
		if not data[k] then
				--跳过空项目
		elseif table.maxn(data[k]) < 1 and inTable(CollapsableKeys, k) then
				--跳过空项目
		else
			local buff = {}
			for i, dd in pairs(data[k]) do

--[[
td
	.ship_word_block .ship_word_media_wrap
		p.ship_word_line	//日文台词
		[p.ship_word_line	//中文台词]
		.sm				//语音
	.ship_word_block
		.ship_word_media_wrap
			p.ship_word_line	//日文台词
			.sm				//日文语音
		.ship_word_media_wrap
			p.ship_word_line	//中文台词
			.sm				//中文台词
		
]]
				--根据mf参数和text参数获取语音和剔除div后的台词文本
				local html_sm, text = ParseMediaFileToHTML(dd.mf, dd.text or '', KeyNameDict[k])
				local html_sm2, text2 = ParseMediaFileToHTML(dd.jp_mf, dd.jp or '', KeyNameDict[k])
				
				-----------------------------
				--单语音文件
				if not html_sm or not html_sm2 then
					table.insert(buff,([[
<div class="ship_word_block ship_word_media_wrap" data-key="%s" data-key-i="%s">
]]):format(k, i))
					--插入【日语台词】
					if dd.jp and text2 ~= '' then
						table.insert(buff, ([[
<p class="ship_word_line" data-lang="jp" data-key="%s" data-key-i="%s">
]]):format(k, i))
						--TODO  修改Qchar.js后移除冗余的data-key
						table.insert(buff, text2)
						table.insert(buff, '</p>\n')
					end
					--插入【中文台词】
					if dd.text and text ~= '' then
						table.insert(buff, ([[
<p class="ship_word_line" data-lang="zh" data-key="%s" data-key-i="%s">
]]):format(k, i))
						--TODO  修改Qchar.js后移除冗余的data-key
						table.insert(buff, text)
						table.insert(buff, '</p>\n')
					end
					--插入【语音】
					table.insert(buff, html_sm or html_sm2 or '')
					table.insert(buff, '</div>')
				
				----------------------------
				-- 双语音文件
				else
				
					table.insert(buff, ([[
<div class="ship_word_block" data-key="%s" data-key-i="%s">
]]):format(k, i))
					--插入【日语台词】
					table.insert(buff, [[
<div class="ship_word_media_wrap">
<p class="ship_word_line" data-lang="jp">
]])
					table.insert(buff, text2)
					table.insert(buff, '</p>\n')
					
					--插入【日语语音】
					table.insert(buff, html_sm2)
					table.insert(buff, '\n</div>')
					
					--插入【中文台词】
					table.insert(buff, [[
<div class="ship_word_media_wrap">
<p class="ship_word_line" data-lang="zh">
]])
					table.insert(buff, text)
					table.insert(buff, '</p>\n')
					
					--插入【中文语音】
					table.insert(buff, html_sm)
					table.insert(buff, '\n</div></div>')
				end
			end
			
			result = result .. ([[
<tr data-key="%s">
<th>%s</th>
<td>%s</td>
</tr>
]]):format(	k, 
			KeyNameDict[k] or k, 
			table.concat(buff,''))
		end
	end
	
	return result..'</table>'
end
p.ShowShipWordsTable = ShowShipWordsTable

--解析一个包含全部信息的表,并生成整个折叠面板
function ParseShipWordsPanel(data)
	local result = ''
	result = result..parseCollapsePanel({
			'开始', 
			['主框'] = 1
		})
	for i, value in ipairs(data) do
			result = result..parseCollapsePanel({
				['标题'] = value.title,
				['选项'] = 'ShipWords-'..i,
				['主框'] = 1,
				['样式'] = data.theme or 'primary',
				['展开'] = value.active
			}) .. '\n' .. tostring(value.text) ..parseCollapsePanel({
				'内容结束'
			}) .. '\n'
	end
	result = result..parseCollapsePanel({
			'结束'
		})
	return result
end

--解析一个包含全部信息的表,并生成整个选项卡面板
function ParseShipWordsTabPanel(data)
	data.label_style = 'font-weight:bolder; padding:0.5em 1.2em;'
	return parseTabPanel(data)
end

--解析参数列表
function ParseArguments(frame)
	local data = {}
	data.theme = frame.args['样式'] or frame.args['主题']
	
	--默认不应用样式表
	data.no_stylesheet = not frame.args['应用样式表']
					or mw.text.trim(frame.args['应用样式表']) == '否'
	--默认展开第一个子面板
	local selected = tonumber(frame.args['选中'] or 1) or 1
	
	--生成随机种子,为生成ID做准备
	math.randomseed(os.time())
	
	local i = 1
	local labelN = '标题'..i
	while frame.args[labelN] do
		local title = mw.text.encode(frame.args[labelN])
		local text = frame.args['内容'..i]
		
		--将标题记录到table中,以方便js获取
		text = text:gsub('<table class=\"table%-ShipWordsTable\">',
			'<table class=\"table-ShipWordsTable\" data-title=\"'..title..'\">')
		
		data[i] = {
			title = title,
			text = text,
			id = generateId(frame.args['Id'..i] or frame.args['ID'..i]),
			active = i == selected
		}
		i = i + 1
		labelN = '标题'..i
	end
	
	return data
end

--解析台词表格的Key=Value参数列表
function ParseKeyWordArguments(frame) 
	local data = {};
	local args = frame.args;
	
	for _, k in ipairs(KeyList) do
		for i = 1, 15 do
			local key = i == 1 and args[k] and k or k..'_'..i
			
			local text = trimNullOrEmpty(args[key])
			
			--main_N 支持间断的N
			if not text and k ~= 'main' then break end
--[[
data[k] = {
	[1] = {
		text = '中文文本'
		jp = '日文文本'
		mf = 'xxx.mp3'
		jp_mf = 'xxx2.mp3'
	}
} ]]
			if text then
				data[k] = data[k] or {}
				data[k][i] = data[k][i] or {}
				data[k][i].text = text
				data[k][i].jp = trimNullOrEmpty(args[key..'_jp'])
				data[k][i].mf = trimNullOrEmpty(args[key..'_mediaFile'])
				data[k][i].jp_mf = trimNullOrEmpty(args[key..'_jp_mediaFile'])
			end
		end
	end
	
	--兼容旧的写法
	if data['main'] and table.maxn(data['main']) == 1 then
		local spl = mw.text.split(data['main'][1].text, '<br ?/?>')
		if #spl > 1 then
			for i = 1, #spl do
				data['main'][i] = data['main'][i] or {}
				data['main'][i].text = trimNullOrEmpty(spl[i])
			end
		end
	end
	if data['couple_encourage'] and #data['couple_encourage'] == 1 then
		local spl = mw.text.split(data['couple_encourage'][1].text, '<br ?/?>')
		if #spl > 1 then
			for i = 1, #spl do
				data['couple_encourage'][i] = data['couple_encourage'][i] or {}
				data['couple_encourage'][i].text = trimNullOrEmpty(spl[i])
			end
		end
	end
	
	return data
end


--------------------------------------------------
--	公开函数
--------------------------------------------------
p['台词表格'] = function(frame)
	local data = ParseKeyWordArguments(frame)
	return ShowShipWordsTable(data)
end

p['台词面板'] = function(frame)
	local data = ParseArguments(frame)
	return ParseShipWordsTabPanel(data)
end

p['台词折叠面板'] = function(frame)
	local data = ParseArguments(frame)
	--data[1].active = false
	return ParseShipWordsPanel(data)
end

p['测试'] = function(frame)
	frame = frame or {
		args = {
			home = '漂亮的战斗,指挥官<div cc>aa</div>';
			feeling1_jp = ' 私はあなたの命令に従います。<div cc>aa</div>';
			main_1 = 'main_1文字';
			main_1_jp = 'main_1JP';
			main_1_mediaFile = '平海换装2_main_1.mp3';
			main_2 = 'main_2文字';
			main_2_jp = '身份确认——欢迎回来,主人';
			main_2_mediaFile = '平海换装2_main_1.mp3';
			main_2_jp_mediaFile = '加斯科涅login_ex100.mp3';
			
		}
	}
	local data = ParseKeyWordArguments(frame)
	
	local text = ShowShipWordsTable(data)
	mw.log(text)
	
	return text
end

p.RunTest = function()
end

--------------------------------------------------
--	工具函数
--------------------------------------------------
function parseCollapsePanel(arg_list)
	return mw.getCurrentFrame():expandTemplate({
		title = '折叠面板',
		args = arg_list
	})
end

function parseTabPanel(data)
	local mTbPn = require('模块:选项卡面板')
	return mTbPn.GenerateWholePanel(data)
end

function generateId(text)
	text = text and text ~= '' and text:lower() 
			or ('%X'):format(math.random(1677215))
	return 'ShWrPn-'..text;
end

function inTable(tab, val)
	for k, v in pairs(tab) do
		if v == val then return true end
	end
	return false
end

function trimNullOrEmpty(s)
	if not s then return false end
	s = mw.text.trim(s)
	return s ~= '' and s
end
return p