Module:曲包列表
此模块的文档可以在Module:曲包列表/doc创建
local p = {}
-- 单一数据源: 曲目列表.json (曲包信息由 songs 的 "曲包" 字段派生)
local DATA_TITLE = '模板:曲目列表.json'
-- 曲包展示顺序 (游戏内上架顺序), 不在表中的曲包按名称排序追加到末尾
local PACK_ORDER = {
'夏日序曲 第一章', '夏日序曲 第二章', '夏日序曲 第三章',
'泡沫假日1号包', '数据突围1号包', '旅行伊始1号包', '神秘国度1号包',
'冬日欢歌1号包', '新年快乐1号包', '闪音集结1号包', '五光十色1号包',
'万象春来1号包', 'ChillMe!1号包', '时序织梦1号包',
'中国BOY1号包', 'Nanatsukaze1号包',
'免费单曲(商店)', '免费单曲', 'CORE基础卡',
}
local function esc(s)
return (s:gsub('|', '|'))
end
-- 链接目标: # 是 MediaWiki 的锚点分隔符, 标题里一律换成全角 #
-- 优先用「链接名」字段; 没有就回退到曲名并自动替换 (双保险)
-- 注意: 光用 <nowiki>#</nowiki> 之类的转义救不了 —— # 在标题解析阶段就被切走了
local function pageName(song, suffix)
local n = song['链接名'] or song['曲名']
if suffix then
n = n .. '(' .. suffix .. ')'
end
return (n:gsub('#', '#'))
end
local function norm(s)
-- 图片文件名规范: 空格 -> 下划线
return (s:gsub(' ', '_'))
end
local function langWrap(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
function p.render(frame)
local demomode = frame and frame.args['demo']
local t = mw.title.new(DATA_TITLE)
if not t or not t.exists then
return '<span class="error">数据页不存在: [[' .. DATA_TITLE .. ']]</span>'
end
local ok, data = pcall(mw.text.jsonDecode, t:getContent())
if not ok or type(data) ~= 'table' then
return '<span class="error">数据页 JSON 解析失败: [[' .. DATA_TITLE .. ']]</span>'
end
-- 按曲包分组 (保持 songs 数组顺序)
local packs = {}
for _, song in ipairs(data.songs or {}) do
local pk = song['曲包']
if pk and pk ~= '' then
packs[pk] = packs[pk] or {}
table.insert(packs[pk], song)
end
end
-- 排序: PACK_ORDER 优先, 未知曲包按名称追加
local orderMap = {}
for i, name in ipairs(PACK_ORDER) do
orderMap[name] = i
end
local keys = {}
for pk in pairs(packs) do
table.insert(keys, pk)
end
table.sort(keys, function(a, b)
local ra = orderMap[a] or (#PACK_ORDER + 1)
local rb = orderMap[b] or (#PACK_ORDER + 1)
if ra ~= rb then
return ra < rb
end
return a < b
end)
local out = {}
table.insert(out, '{| class="wikitable sortable" style="width:100%"')
table.insert(out, '! 图片 !! 曲包 !! 曲目数 !! 包含曲目')
for _, pk in ipairs(keys) do
local songs = packs[pk]
-- 图片: 曲包物品-<曲包名>.png, 不存在则显示 —
local imgFile = '曲包物品-' .. norm(pk) .. '.png'
local imgTitle = mw.title.new('File:' .. imgFile)
local imgCell
if imgTitle and imgTitle.exists then
imgCell = '[[File:' .. esc(imgFile) .. '|80px]]'
else
imgCell = '—'
end
local links = {}
for _, song in ipairs(songs) do
table.insert(links, '[[' .. '曲目:' .. esc(song['曲名']) .. '|' .. langWrap(song) .. ']]')
end
-- 锚点: 其他页面可 [[曲包列表#<曲包名>]] 跳转到对应行
table.insert(out, '|-\n| id=\"' .. esc(pk) .. '\" | ' .. imgCell .. ' || ' .. esc(pk)
.. ' || ' .. tostring(#songs)
.. ' || ' .. table.concat(links, ' / '))
end
table.insert(out, '|}')
if not demomode then
table.insert(out, '\n[[Category:曲包]]')
end
-- Scribunto 返回文本中的 {{模板}} 不会被自动展开, 必须显式 preprocess
return frame:preprocess(table.concat(out, '\n'))
end
return p