Module:定数表
外观
此模块的文档可以在Module:定数表/doc创建
local p = {}
-- 数据页: 曲目列表 (含定数)
local DATA_TITLE = '模板:曲目列表.json'
local MIN_CONST = 9.0
local DIFF_CLASS = { CASUAL = 'cas', TOUGH = 'tou', INSANE = 'ins', PRO = 'pro' }
local DIFF_RANK = { CASUAL = 1, TOUGH = 2, INSANE = 3, PRO = 4 }
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 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 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
-- 收集 >= MIN_CONST 的条目, 按定数分档
local steps = {}
for _, song in ipairs(data.songs or {}) do
local consts = song['定数'] or {}
for _, diff in ipairs(song['难度'] or {}) do
local c = consts[diff]
if type(c) == 'number' and c >= MIN_CONST then
local key = string.format('%.1f', c)
steps[key] = steps[key] or {}
table.insert(steps[key], { song = song, diff = diff })
end
end
end
-- 定数从高到低, 每 0.1 一档连续列到 MIN_CONST
local keys = {}
for key in pairs(steps) do
table.insert(keys, tonumber(key))
end
table.sort(keys, function(a, b) return a > b end)
local maxConst = keys[1] or MIN_CONST
local out = {}
table.insert(out, '{| class="wikitable" style="width:100%"')
for i = math.floor(maxConst * 10 + 0.5), math.floor(MIN_CONST * 10 + 0.5), -1 do
local key = string.format('%.1f', i / 10)
local items = steps[key]
if not items then
table.insert(out, '|-\n| style="width:60px;text-align:center;font-size:135%;font-weight:bold;color:#999" | '
.. key .. ' || <span style="color:#999">—</span>')
else
-- 难度从低到高, 同难度按曲名
table.sort(items, function(a, b)
local ra = DIFF_RANK[a.diff] or 9
local rb = DIFF_RANK[b.diff] or 9
if ra ~= rb then
return ra < rb
end
return a.song['曲名'] < b.song['曲名']
end)
local tiles = {}
for _, it in ipairs(items) do
local song = it.song
local name = song['曲名']
local art = (song['曲绘'] or name):gsub(' ', '_')
local cls = DIFF_CLASS[it.diff] or 'unknown'
local link = '曲目:' .. esc(pageName(song))
local tile = '<div style="display:inline-block;text-align:center;margin:3px 6px;vertical-align:top;width:124px;font-size:88%">'
.. '[[File:曲绘-' .. esc(art) .. '.png|96px|link=' .. link .. ']]<br>'
.. '[[' .. link .. '|' .. langWrap(song) .. ']]<br>'
.. ' {{难度|' .. it.diff .. '}}'
.. '</div>'
table.insert(tiles, tile)
end
table.insert(out, '|-\n| style="width:60px;text-align:center;font-size:135%;font-weight:bold" | '
.. key .. ' || ' .. table.concat(tiles))
end
end
table.insert(out, '|}')
-- Scribunto 返回文本中的 {{模板}} 不会被自动展开, 必须显式 preprocess
return frame:preprocess(table.concat(out, '\n'))
end
function p.renderDetail(frame)
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
local DIFFS = { 'CASUAL', 'TOUGH', 'INSANE', 'PRO' }
local out = {}
table.insert(out, '{| class="wikitable sortable empty-last" style="width:100%;text-align:center"')
table.insert(out, '! 曲名 !! 曲包 !! {{难度|CASUAL}} !! {{难度|TOUGH}} !! {{难度|INSANE}} !! {{难度|PRO}}')
for _, song in ipairs(data.songs or {}) do
local name = song['曲名']
local pack = song['曲包'] or ''
local link = '曲目:' .. esc(name)
local cells = {}
table.insert(cells, '[[' .. link .. '|' .. langWrap(song) .. ']]')
if pack ~= '' then
table.insert(cells, '[[曲包:' .. esc(pack) .. '|' .. esc(pack) .. ']]')
else
table.insert(cells, '—')
end
local consts = song['定数'] or {}
for _, diff in ipairs(DIFFS) do
local c = consts[diff]
if type(c) == 'number' then
table.insert(cells, string.format('%.1f', c))
else
table.insert(cells, '<span class="bleap-sort-empty">—</span>')
end
end
table.insert(out, '|-\n| ' .. table.concat(cells, ' || '))
end
table.insert(out, '|}')
return frame:preprocess(table.concat(out, '\n'))
end
return p