如果你看到本段文字,说明该页面未正常加载全局JS,部分功能无法使用,请点击 刷新 重新加载页面。
如果打开页面显示缩略图创建出错,请点击刷新或页面右上WIKI功能中的刷新按钮清除页面缓存并刷新,如果还有问题,请多尝试几次。
全站通知:

模块:舰娘台词

来自碧蓝航线WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

此模块的文档可以在模块:舰娘台词/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	=	'登录界面',
	extra5th = '五周年登录',
	extra6th = '六周年登录',
	drop_descrip	=	'舰船型号',
	profile	=	'自我介绍',
	desc	=	'皮肤描述',
	unlock	=	'获取台词',
	
	--主界面
	login	=	'登录台词',
	detail	=	'查看详情',
	main	=	'主界面',
	touch	=	'触摸台词',
	touch2	=	'特殊触摸',
	headtouch	=	'摸头台词',
	
	--场景
	mission	=	'任务提醒',
	mission_complete	=	'任务完成',
	mail	=	'邮件提醒',
	home	=	'回港台词',

	--好感度
	feeling1	=	'好感度-失望',
	feeling2	=	'好感度-陌生',
	feeling2i	=	'好感度-普通',
	feeling3	=	'好感度-友好',
	feeling4	=	'好感度-喜欢',
	feeling4i	=	'好感度-协作',
	feeling5	=	'好感度-爱',
	feeling5i	=	'好感度-应援',
	feeling1m	=	'好感度-未知',
	feeling2m	=	'好感度-调率',
	feeling3m	=	'好感度-理解',
	feeling4m	=	'好感度-同步',
	feeling5m	=	'好感度-共鸣',
	propose	=	'誓约台词',

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

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

	couple_encourage = '彩蛋台词',
	
	--莱莎的炼金工房相关
	ryza_item = '素材收集',
	ryza_shop = '商店',
	ryza_atellier = '炼金工房',
	
	--闪乱神乐NL
	sknl_pt = '秘传忍法书',
	
	--领航员-TB
	tb_shengdan = '圣诞节',
	tb_chuxi = '除夕',
    tb_xinnian = '新年',
    tb_qingrenjie = '情人节',
    tb_zhongqiu = '中秋节',
    tb_wansheng = '万圣节',
    tb_huodong = '活动提醒',
    tb_genghuan = '更换外形模块',
    tb_chime = '报时'
}

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

	--好感度
	'feeling1',
	'feeling2',
	'feeling2i',
	'feeling3',
	'feeling4',
	'feeling4i',
	'feeling5',
	'feeling5i',
	'feeling1m',
	'feeling2m',
	'feeling3m',
	'feeling4m',
	'feeling5m',
	'propose',

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

	--战斗台词
	'battle',
	'win_mvp',
	'lose',
	'skill',
	'hp_warning',
	
	--彩蛋台词
	'couple_encourage',
	
	--莱莎的炼金工房相关
	'ryza_item',
	'ryza_shop',
	'ryza_atellier',
	
	--闪乱神乐NL
	'sknl_pt',
	
	--领航员-TB
	'tb_shengdan',
	'tb_chuxi',
    'tb_xinnian',
    'tb_qingrenjie',
    'tb_zhongqiu',
    'tb_wansheng',
    'tb_huodong',
    'tb_genghuan',
    'tb_chime',
}

HideIfNoMediaFile = {
	'extra5th',
	'extra6th',
}

ShipWordsParameters = {
	extra5th = {'五周年登录台词', '五周年登录', mediaFile = '%sextra5th.mp3'};
	extra6th = {'六周年登录台词', '六周年登录', mediaFile = '%sextra6th.mp3'};
	drop_descrip = '舰船型号台词', 
	profile = '自我介绍台词', 
	desc = {'描述台词', '皮肤描述台词', mediaFile = '%sget.mp3'};
	unlock = {'获取台词', '获得台词', mediaFile = '%sget.mp3'};
	login ={'登录台词', '登陆台词'};
	detail = {'查看详情台词', '详情台词'};
	--主界面台词序列特殊处理
	main = {'主界面台词'}; 
	touch = {'普通触摸台词', '触摸台词', mediaFile = '%stouch_1.mp3'};
	touch2 = {'特殊触摸台词', '特殊触摸', mediaFile = '%stouch_2.mp3'};
	headtouch = {'摸头台词', mediaFile = '%stouch_head.mp3'};
	mission = {'任务提醒台词', '任务台词', mediaFile = '%stask.mp3'}; 
	mission_complete = {'任务完成台词', '任务完成'}; 
	mail = {'邮件提醒台词', '邮件台词'}; 
	home = '回港台词',
	feeling1 = '好感度-失望台词', 
	feeling2 = '好感度-陌生台词',  
	feeling2i = '好感度-普通台词', 
	feeling3 = '好感度-友好台词',  
	feeling4 = '好感度-喜欢台词',  
	feeling4i = '好感度-协作台词',
	feeling5 = '好感度-爱台词',  
	feeling5i = '好感度-应援台词',  
	feeling1m = {'好感度-未知台词', mediaFile = '%sfeeling1.mp3'}; 
	feeling2m = {'好感度-调率台词', mediaFile = '%sfeeling2.mp3'}; 
	feeling3m = {'好感度-理解台词', mediaFile = '%sfeeling3.mp3'}; 
	feeling4m = {'好感度-同步台词', mediaFile = '%sfeeling4.mp3'}; 
	feeling5m = {'好感度-共鸣台词', mediaFile = '%sfeeling5.mp3'}; 
	propose = '誓约台词',  
	expedition = {'委托完成台词', '军事委托完成台词'}; 
	upgrade = {'强化成功台词', '强化成功'}; 
	battle = {'旗舰开战台词', '旗舰开战', mediaFile = '%swarcry.mp3'};
	win_mvp = {'胜利台词', '胜利MVP台词', mediaFile = '%smvp.mp3'};
	lose = {'失败台词', '战斗失败'};
	skill = '技能台词',  
	hp_warning = {'血量告急台词', 'hp告急台词', mediaFile = '%shp.mp3'}; 
	--彩蛋台词序列特殊处理
	couple_encourage = {'彩蛋台词'};
		--莱莎的炼金工房相关
	ryza_item = {'素材收集'};
	ryza_shop = {'商店'};
	ryza_atellier = {'炼金工房'};
	--闪乱神乐NL
	sknl_pt = {'秘传忍法书'};
	--领航员-TB
	tb_shengdan = {'圣诞节'};
	tb_chuxi = {'除夕'};
    tb_xinnian = {'新年'};
    tb_qingrenjie = {'情人节'};
    tb_zhongqiu = {'中秋节'};
    tb_wansheng = {'万圣节'};
    tb_huodong = {'活动提醒'};
    tb_genghuan = {'更换外形模块'};
    tb_chime = {'报时'};
}
p.ShipWordsParameters = ShipWordsParameters

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

GroupKeys = {
	main = true,
	couple_encourage = true
}

local frame = mw.getCurrentFrame()

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

local sm_html = [[
<div class='sm-bar' style='display: block;'>&nbsp;
<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(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 and not html_sm2 and inTable(HideIfNoMediaFile, k) then
				
				-----------------------------
				--单语音文件
				elseif 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
			
			--跳过空语音周年项目
			if not (not next(buff) and inTable(HideIfNoMediaFile, k)) then
				result = result .. ([[
<tr data-key="%s">
<th>%s</th>
<td>%s</td>
</tr>
]]):format(	k, 
				KeyNameDict[k] or k, 
				table.concat(buff,''))
			end
		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,
				['样式'] = 'shiptable',
				['展开'] = value.active
			}) .. '\n' .. tostring(value.text) ..parseCollapsePanel({
				'内容结束'
			}) .. '\n'
	end
	result = result..parseCollapsePanel({
			'结束'
		})
	return result
end

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

--解析参数列表
function ParseArguments(frame)
	local data = {}
	data.theme = 'shiptable'
	
	--默认不应用样式表
	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;
	local shipName = args["shipName"] or false
	
	for _, k in ipairs(KeyList) do
		if k == "shipName" then break end --舰娘名称,用于默认语音
		for i = 1, 24 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'])
				
				if not data[k][i].mf and shipName then
					local mediaFile = type(ShipWordsParameters[k]) == 'table' and ShipWordsParameters[k].mediaFile
					if not GroupKeys[k] then
						data[k][i].mf = mediaFile 
								and mediaFile:format(shipName .. '_')
								or shipName .. '_' .. k ..'.mp3';
					else
						data[k][i].mf = mediaFile 
								and mediaFile:format(shipName .. '_')
								or shipName .. '_' .. k .. '_' .. i ..'.mp3';
					end
				end
			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