Module:曲目页面
外观
此模块的文档可以在Module:曲目页面/doc创建
local p = {}
-- 数据页: 曲目列表 (曲绘/曲师/曲包/定数 的单一来源)
local DATA_TITLE = '模板:曲目列表.json'
local validator = require('模块:数据页校验')
local function esc(s)
return (s:gsub('|', '|'))
end
local function norm(s)
return (s:gsub(' ', '_'))
end
-- Lua 中空字符串为真值, 必须显式判空
local function arg(frame, k, default)
local v = frame.args[k]
if v == nil or v == '' then
return default
end
return v
end
local function fmt(v)
if type(v) == 'number' then
return string.format('%.1f', v)
end
return v or ''
end
local function loadData()
return validator.load(DATA_TITLE)
end
local function findSong(data, name, specialOnly)
if not specialOnly then
for _, s in ipairs(data.songs or {}) do
if s['曲名'] == name then
return s, false
end
end
end
for _, s in ipairs(data.special or {}) do
if s['曲名'] == name then
return s, true
end
end
return nil, false
end
local function langWrapName(song)
local lang = song['lang']
if lang == 'ja' then
return '{{lang|ja|' .. esc(song['曲名']) .. '}}'
elseif lang == 'zh-Hant' then
return '{{lang|zh-Hant|' .. esc(song['曲名']) .. '}}'
end
return esc(song['曲名'])
end
local function songArt(song, isSpecial)
local art = song['曲绘'] or song['曲名'] or ''
if isSpecial then
art = art:gsub('(石墩子模式)$', '') .. '_石墩子模式'
end
return norm(art)
end
-- 自动生成解锁方式: 按曲包类型
local function autoUnlock(pack)
if pack == '' or pack == nil then
return '(待补充)'
end
if pack == '免费单曲' then
return '无需解锁'
elseif pack == '免费单曲(商店)' then
return '前往[[商店#单曲|商店]]使用[[货币#瓶盖|瓶盖]]购买单曲'
elseif pack == 'CORE基础卡' then
return '前往[[商店#闪音卡|商店]]购买基础卡解锁'
else
return '前往[[商店#曲包|商店]]购买[[曲包列表#' .. esc(pack) .. '|' .. esc(pack) .. ']]解锁'
end
end
function p.render(frame)
local name = arg(frame, '曲名', '')
if name == '' then
return '<span class="error">未指定 |曲名= 参数</span>'
end
local data, err = loadData()
if not data then
return '<span class="error">' .. tostring(err) .. '</span>'
end
local specialOnly = arg(frame, '特殊', '') == '石墩子'
local song, isSpecial = findSong(data, name, specialOnly)
if not song then
return '<span class="error">数据页中找不到曲目: ' .. esc(name)
.. '(请检查 [[模板:曲目列表.json]])</span>'
end
local defaultTitle = isSpecial
and (langWrapName(song) .. '(石墩子模式)')
or song['曲名']
local title = arg(frame, '标题', defaultTitle)
-- 标题先单独展开 ({{lang}} 嵌套在信息框参数内不会被最终 preprocess 二次展开)
title = frame:preprocess(title)
local art = songArt(song, isSpecial)
local consts = song['定数'] or {}
local out = {}
local tagline = arg(frame, '标语', '')
if tagline ~= '' then
table.insert(out, '<div class="song-tagline">' .. tagline .. '</div>')
end
-- 信息框: 自动填 标题/图片/曲师/曲包/定数/谱师/时长/BPM, 手工传参可覆盖
table.insert(out, '{{曲目信息框')
table.insert(out, '|标题= ' .. esc(title))
local imgOverride = arg(frame, '图片', '')
if imgOverride ~= '' then
table.insert(out, '|图片= ' .. imgOverride)
else
table.insert(out, '|图片= [[文件:曲绘-' .. esc(art) .. '.png|256px]]')
end
table.insert(out, '|曲师= ' .. esc(arg(frame, '曲师', song['曲师'] or '')))
-- 曲包: 手工传参优先; 否则自动生成指向 曲包列表#<曲包名> 的链接
local packArg = frame.args['曲包']
local packText = song['曲包'] or ''
if packArg ~= nil and packArg ~= '' then
packText = packArg
elseif packText ~= '' then
packText = '[[' .. '曲包列表#' .. esc(packText) .. '|' .. esc(packText) .. ']]'
end
table.insert(out, '|曲包= ' .. packText)
table.insert(out, '|时长= ' .. esc(arg(frame, '时长', song['时长'] or '')))
table.insert(out, '|BPM= ' .. esc(arg(frame, 'BPM', song['BPM'] or '')))
local charters = song['谱师'] or {}
for _, d in ipairs({ 'CASUAL', 'TOUGH', 'INSANE', 'PRO' }) do
table.insert(out, '|' .. d .. '定数= ' .. fmt(consts[d]))
table.insert(out, '|' .. d .. '谱师= ' .. esc(arg(frame, d .. '谱师', charters[d] or '')))
table.insert(out, '|' .. d .. '连击= ' .. esc(arg(frame, d .. '连击', '')))
end
table.insert(out, '}}')
-- 标准段落
-- 解锁方式: 手工传参优先, 否则按曲包自动生成
local unlock = arg(frame, '解锁方式', '')
if unlock == '' then
unlock = autoUnlock(song['曲包'] or '')
end
table.insert(out, '\n== 解锁方式 ==\n' .. unlock)
table.insert(out, '\n== 游戏相关 ==\n=== 定数调整历史 ===\n'
.. arg(frame, '定数调整历史', '(待补充)'))
-- 其他相关子章节: 新参数优先, 兼容旧参数 |游戏相关=
local otherRelated = arg(frame, '其他相关', '')
local legacy = arg(frame, '游戏相关', '')
if otherRelated == '' then
otherRelated = legacy
end
if otherRelated == '' then
otherRelated = '(待补充)'
end
table.insert(out, '\n=== 其他相关 ===\n' .. otherRelated)
table.insert(out, '\n== 歌词 ==\n' .. arg(frame, '歌词', '纯音乐,请欣赏'))
local other = arg(frame, '其他', '')
if other ~= '' then
table.insert(out, '\n== 其他 ==\n' .. other)
end
table.insert(out, '\n[[Category:曲目]]')
-- 展开 {{曲目信息框}} 等模板
return frame:preprocess(table.concat(out, '\n'))
end
return p