ArtsData:修订间差异
来自蒙纳多研究本部
无编辑摘要 |
无编辑摘要 |
||
| 第1行: | 第1行: | ||
local p = {} | local p = {} | ||
local | local post = require("Module:RequestHelper").post | ||
local getArgs = require("Module:Arguments").getArgs | local getArgs = require("Module:Arguments").getArgs | ||
local html = require("Module:Html") | local html = require("Module:Html") | ||
local compress = html.compress | local compress = html.compress | ||
-- 子模块(若你部署的模块名不是"模块:ArtsData",请把下面两行的前缀一并改掉) | |||
local static = require("Module:ArtsData/static") | |||
local acquire = require("Module:ArtsData/acquire") | |||
local ROMAN = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"} | local ROMAN = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"} | ||
local MAX_RANK = 12 | local MAX_RANK = 12 | ||
-- ---------- 工具 ---------- | |||
local function num(v) | local function num(v) | ||
| 第19行: | 第20行: | ||
end | end | ||
--[[ | |||
数值格式化(四舍五入 + 剥离小数尾零) | |||
注意:绝不能对整数部分做尾零剥离("50" 不能变成 "5") | |||
]] | |||
local function fmt(v, digits) | local function fmt(v, digits) | ||
digits = digits or 2 | digits = digits or 2 | ||
local factor = 10 ^ digits | local factor = 10 ^ digits | ||
-- 用 floor(x+0.5) 实现 ROUND_HALF_UP;先加一个小 epsilon 抵消浮点表示误差 | |||
local scaled = v * factor | local scaled = v * factor | ||
local rounded | local rounded | ||
| 第37行: | 第43行: | ||
local fracPart = math.abs(rounded) % factor | local fracPart = math.abs(rounded) % factor | ||
local fracStr = string.format("%0" .. digits .. "d", fracPart) | local fracStr = string.format("%0" .. digits .. "d", fracPart) | ||
-- 仅剥离小数尾零 | |||
fracStr = fracStr:gsub("0+$", "") | fracStr = fracStr:gsub("0+$", "") | ||
local sign = rounded < 0 and "-" or "" | local sign = rounded < 0 and "-" or "" | ||
| 第45行: | 第52行: | ||
end | end | ||
-- atk_type: 1=物理 2=以太 3=状态 | |||
local ATK_TYPE = {[1] = "物理", [2] = "以太", [3] = "状态"} | local ATK_TYPE = {[1] = "物理", [2] = "以太", [3] = "状态"} | ||
-- 习得条件:get_type 1=初始/等级, 2=剧情习得 | |||
local function learnText(data) | local function learnText(data) | ||
if num(data.get_type) == 2 then | if num(data.get_type) == 2 then | ||
| 第58行: | 第67行: | ||
end | end | ||
-- ---------- 等级序列展开 ---------- | |||
-- 通用等差序列:(rank1 + (rank-1)*grow) / scale | |||
local function series(rank1, grow, digits, scale) | local function series(rank1, grow, digits, scale) | ||
scale = scale or 1 | scale = scale or 1 | ||
| 第67行: | 第79行: | ||
end | end | ||
-- 填充时间:按基础冷却的百分比递减;recast=0 返回 nil(页面显示 —) | |||
local function recastSeries(data) | local function recastSeries(data) | ||
local recast = num(data.recast) | local recast = num(data.recast) | ||
| 第78行: | 第91行: | ||
end | end | ||
-- 命中修正:dex + 5*(rank-1) | |||
local function dexSeries(data) | local function dexSeries(data) | ||
local dex = num(data.dex) | local dex = num(data.dex) | ||
| 第87行: | 第101行: | ||
end | end | ||
-- ---------- 表格行 ---------- | |||
local function row12(label, cells) | local function row12(label, cells) | ||
| 第117行: | 第118行: | ||
'" style="text-align:left;">' .. text .. "</td></tr>" | '" style="text-align:left;">' .. text .. "</td></tr>" | ||
end | end | ||
-- 安全查询:XBDB 查询未注册表会抛 PHP 异常,必须包裹 | |||
local function fetchArt(artId) | |||
local ok, data = pcall(post, "bdat_common.pc_arts", artId) | |||
if not ok then | |||
mw.log("Arts: 查询失败 id=" .. tostring(artId) .. " err=" .. tostring(data)) | |||
return nil | |||
end | |||
if type(data) ~= "table" or not data.row_id then | |||
return nil | |||
end | |||
return data | |||
end | |||
-- ---------- 单战技渲染 ---------- | |||
function p.renderArt(artId) | function p.renderArt(artId) | ||
local data = fetchArt(artId) | |||
local data = | |||
if not data then | if not data then | ||
return "" | return "" | ||
end | end | ||
local name = (data.name and data.name ~= "") and data.name | local s = static.arts[artId] or {} | ||
local name = (data.name and data.name ~= "") and data.name or s.name or ("战技 " .. artId) | |||
local atype = ATK_TYPE[num(data.atk_type)] or "未知" | local atype = ATK_TYPE[num(data.atk_type)] or "未知" | ||
local learn = learnText(data) | local learn = learnText(data) | ||
local out = {} | local out = {} | ||
| 第137行: | 第149行: | ||
out[#out + 1] = "'''类型''':" .. atype .. " '''习得''':" .. learn | out[#out + 1] = "'''类型''':" .. atype .. " '''习得''':" .. learn | ||
-- 表头 | |||
local head = {"<tr><th>等级</th>"} | local head = {"<tr><th>等级</th>"} | ||
for r = 1, MAX_RANK do | for r = 1, MAX_RANK do | ||
| 第145行: | 第158行: | ||
local rows = {} | local rows = {} | ||
rows[#rows + 1] = rowSpan("效果", | -- 效果说明(官方描述) | ||
rows[#rows + 1] = rowSpan("效果", s.desc or "") | |||
-- AP 消耗(天赋战技无 AP 数据时输出全 0,与原页面保持一致) | |||
rows[#rows + 1] = row12("AP 消耗", | do | ||
local cells = {} | |||
for r = 1, MAX_RANK do | |||
cells[r] = s.ap and tostring(s.ap[r] or 0) or "0" | |||
end | |||
rows[#rows + 1] = row12("AP 消耗", cells) | |||
end | end | ||
-- 倍率 | |||
local r1, r2 = num(data.rate1), num(data.rate2) | local r1, r2 = num(data.rate1), num(data.rate2) | ||
if r2 ~= r1 then | if r2 ~= r1 then | ||
| 第159行: | 第179行: | ||
end | end | ||
-- 填充时间 | |||
local rs = recastSeries(data) | local rs = recastSeries(data) | ||
if rs then | if rs then | ||
| 第166行: | 第187行: | ||
end | end | ||
-- 效果时间 | |||
if num(data.st_time) ~= 0 or num(data.grow_st_time) ~= 0 then | if num(data.st_time) ~= 0 or num(data.grow_st_time) ~= 0 then | ||
rows[#rows + 1] = row12("效果时间(秒)", series(data.st_time, data.grow_st_time, 1, 10)) | rows[#rows + 1] = row12("效果时间(秒)", series(data.st_time, data.grow_st_time, 1, 10)) | ||
end | end | ||
-- 命中修正 | |||
rows[#rows + 1] = row12("命中修正(决定版)", dexSeries(data)) | rows[#rows + 1] = row12("命中修正(决定版)", dexSeries(data)) | ||
-- 效果数值 | |||
if num(data.st_val) ~= 0 or num(data.grow_st_val) ~= 0 then | if num(data.st_val) ~= 0 or num(data.grow_st_val) ~= 0 then | ||
rows[#rows + 1] = row12("效果数值", series(data.st_val, data.grow_st_val, 0, 1)) | rows[#rows + 1] = row12("效果数值", series(data.st_val, data.grow_st_val, 0, 1)) | ||
| 第179行: | 第203行: | ||
table.concat(head) .. table.concat(rows) .. "</table>" | table.concat(head) .. table.concat(rows) .. "</table>" | ||
-- SMW 关键字段 | |||
local maxRate | local maxRate | ||
if r2 ~= r1 then | if r2 ~= r1 then | ||
| 第196行: | 第221行: | ||
return table.concat(out, "\n") | return table.concat(out, "\n") | ||
end | end | ||
-- ---------- 获取表渲染 ---------- | |||
local ACQ_HEADER = {"战技名", "等级", "地点", "掉落", "商店", "出售", "备注"} | local ACQ_HEADER = {"战技名", "等级", "地点", "掉落", "商店", "出售", "备注"} | ||
| 第227行: | 第254行: | ||
return table.concat(out) | return table.concat(out) | ||
end | end | ||
-- ---------- 主入口 ---------- | |||
function p.main(frame) | function p.main(frame) | ||
| 第233行: | 第262行: | ||
local first = args[1] | local first = args[1] | ||
local role = first and | local role = first and static.roles[first] or nil | ||
if role then | if role then | ||
| 第240行: | 第269行: | ||
out[#out + 1] = p.renderArt(id) | out[#out + 1] = p.renderArt(id) | ||
end | end | ||
-- 获取表(可通过 noacquire=1 关闭) | |||
if args.noacquire ~= "1" then | if args.noacquire ~= "1" then | ||
out[#out + 1] = renderAcquire(first) | out[#out + 1] = renderAcquire(first) | ||
2026年10月8日 (四) 19:01的版本
此模块的文档可以在模块:ArtsData/doc创建
local p = {}
local post = require("Module:RequestHelper").post
local getArgs = require("Module:Arguments").getArgs
local html = require("Module:Html")
local compress = html.compress
-- 子模块(若你部署的模块名不是"模块:ArtsData",请把下面两行的前缀一并改掉)
local static = require("Module:ArtsData/static")
local acquire = require("Module:ArtsData/acquire")
local ROMAN = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"}
local MAX_RANK = 12
-- ---------- 工具 ----------
local function num(v)
if v == nil then return 0 end
return tonumber(v) or 0
end
--[[
数值格式化(四舍五入 + 剥离小数尾零)
注意:绝不能对整数部分做尾零剥离("50" 不能变成 "5")
]]
local function fmt(v, digits)
digits = digits or 2
local factor = 10 ^ digits
-- 用 floor(x+0.5) 实现 ROUND_HALF_UP;先加一个小 epsilon 抵消浮点表示误差
local scaled = v * factor
local rounded
if scaled >= 0 then
rounded = math.floor(scaled + 0.5 + 1e-9)
else
rounded = -math.floor(-scaled + 0.5 + 1e-9)
end
if digits == 0 then
return tostring(rounded)
end
local intPart = math.floor(math.abs(rounded) / factor)
local fracPart = math.abs(rounded) % factor
local fracStr = string.format("%0" .. digits .. "d", fracPart)
-- 仅剥离小数尾零
fracStr = fracStr:gsub("0+$", "")
local sign = rounded < 0 and "-" or ""
if fracStr == "" then
return sign .. tostring(intPart)
end
return sign .. tostring(intPart) .. "." .. fracStr
end
-- atk_type: 1=物理 2=以太 3=状态
local ATK_TYPE = {[1] = "物理", [2] = "以太", [3] = "状态"}
-- 习得条件:get_type 1=初始/等级, 2=剧情习得
local function learnText(data)
if num(data.get_type) == 2 then
return "剧情习得"
end
local lv = num(data.get_lv)
if lv > 0 then
return "等级 " .. lv
end
return "初始拥有"
end
-- ---------- 等级序列展开 ----------
-- 通用等差序列:(rank1 + (rank-1)*grow) / scale
local function series(rank1, grow, digits, scale)
scale = scale or 1
local out = {}
for r = 1, MAX_RANK do
out[r] = fmt((num(rank1) + (r - 1) * num(grow)) / scale, digits)
end
return out
end
-- 填充时间:按基础冷却的百分比递减;recast=0 返回 nil(页面显示 —)
local function recastSeries(data)
local recast = num(data.recast)
if recast == 0 then return nil end
local glow = num(data.glow_recast)
local out = {}
for r = 1, MAX_RANK do
out[r] = fmt(recast * (1 - (r - 1) * glow / 100), 1)
end
return out
end
-- 命中修正:dex + 5*(rank-1)
local function dexSeries(data)
local dex = num(data.dex)
local out = {}
for r = 1, MAX_RANK do
out[r] = "+" .. tostring(dex + 5 * (r - 1)) .. "%"
end
return out
end
-- ---------- 表格行 ----------
local function row12(label, cells)
local t = {"<tr><th>" .. label .. "</th>"}
for r = 1, MAX_RANK do
local v = cells and cells[r] or nil
t[#t + 1] = "<td>" .. (v or "—") .. "</td>"
end
t[#t + 1] = "</tr>"
return table.concat(t)
end
local function rowSpan(label, text)
if not text or text == "" then return "" end
return '<tr><th>' .. label .. '</th><td colspan="' .. MAX_RANK ..
'" style="text-align:left;">' .. text .. "</td></tr>"
end
-- 安全查询:XBDB 查询未注册表会抛 PHP 异常,必须包裹
local function fetchArt(artId)
local ok, data = pcall(post, "bdat_common.pc_arts", artId)
if not ok then
mw.log("Arts: 查询失败 id=" .. tostring(artId) .. " err=" .. tostring(data))
return nil
end
if type(data) ~= "table" or not data.row_id then
return nil
end
return data
end
-- ---------- 单战技渲染 ----------
function p.renderArt(artId)
local data = fetchArt(artId)
if not data then
return ""
end
local s = static.arts[artId] or {}
local name = (data.name and data.name ~= "") and data.name or s.name or ("战技 " .. artId)
local atype = ATK_TYPE[num(data.atk_type)] or "未知"
local learn = learnText(data)
local out = {}
out[#out + 1] = html.h3(name)
out[#out + 1] = "'''类型''':" .. atype .. " '''习得''':" .. learn
-- 表头
local head = {"<tr><th>等级</th>"}
for r = 1, MAX_RANK do
head[#head + 1] = "<th>" .. ROMAN[r] .. "</th>"
end
head[#head + 1] = "</tr>"
local rows = {}
-- 效果说明(官方描述)
rows[#rows + 1] = rowSpan("效果", s.desc or "")
-- AP 消耗(天赋战技无 AP 数据时输出全 0,与原页面保持一致)
do
local cells = {}
for r = 1, MAX_RANK do
cells[r] = s.ap and tostring(s.ap[r] or 0) or "0"
end
rows[#rows + 1] = row12("AP 消耗", cells)
end
-- 倍率
local r1, r2 = num(data.rate1), num(data.rate2)
if r2 ~= r1 then
rows[#rows + 1] = row12("倍率(下限)", series(r1, data.grow_powl, 2, 100))
rows[#rows + 1] = row12("倍率(上限)", series(r2, data.grow_powh, 2, 100))
else
rows[#rows + 1] = row12("倍率", series(r1, data.grow_powl, 2, 100))
end
-- 填充时间
local rs = recastSeries(data)
if rs then
rows[#rows + 1] = row12("填充时间(秒)", rs)
else
rows[#rows + 1] = row12("填充时间(秒)", nil)
end
-- 效果时间
if num(data.st_time) ~= 0 or num(data.grow_st_time) ~= 0 then
rows[#rows + 1] = row12("效果时间(秒)", series(data.st_time, data.grow_st_time, 1, 10))
end
-- 命中修正
rows[#rows + 1] = row12("命中修正(决定版)", dexSeries(data))
-- 效果数值
if num(data.st_val) ~= 0 or num(data.grow_st_val) ~= 0 then
rows[#rows + 1] = row12("效果数值", series(data.st_val, data.grow_st_val, 0, 1))
end
out[#out + 1] = '<table class="wikitable" style="text-align:center;">' ..
table.concat(head) .. table.concat(rows) .. "</table>"
-- SMW 关键字段
local maxRate
if r2 ~= r1 then
maxRate = (r2 + 11 * num(data.grow_powh)) / 100
else
maxRate = (r1 + 11 * num(data.grow_powl)) / 100
end
mw.smw.set({
artId = artId,
artName = name,
artType = atype,
artLearn = learn,
artRate = maxRate,
artRecast = num(data.recast),
})
return table.concat(out, "\n")
end
-- ---------- 获取表渲染 ----------
local ACQ_HEADER = {"战技名", "等级", "地点", "掉落", "商店", "出售", "备注"}
local function renderAcquire(roleKey)
local rows = acquire[roleKey]
if not rows or #rows == 0 then
return ""
end
local out = {html.h2("获取")}
out[#out + 1] = '<table class="wikitable sortable">'
out[#out + 1] = "<tr>"
for _, h in ipairs(ACQ_HEADER) do
out[#out + 1] = "<th>" .. h .. "</th>"
end
out[#out + 1] = "</tr>"
for _, r in ipairs(rows) do
out[#out + 1] = "<tr>"
out[#out + 1] = "<td>" .. (r.art or "") .. "</td>"
out[#out + 1] = "<td>" .. (r.level or "") .. "</td>"
out[#out + 1] = "<td>" .. (r.place or "") .. "</td>"
out[#out + 1] = "<td>" .. (r.drop or "") .. "</td>"
out[#out + 1] = "<td>" .. (r.shop or "") .. "</td>"
out[#out + 1] = "<td>" .. (r.sell or "") .. "</td>"
out[#out + 1] = "<td>" .. (r.note or "") .. "</td>"
out[#out + 1] = "</tr>"
end
out[#out + 1] = "</table>"
return table.concat(out)
end
-- ---------- 主入口 ----------
function p.main(frame)
local args = getArgs(frame)
local out = {}
local first = args[1]
local role = first and static.roles[first] or nil
if role then
out[#out + 1] = "== 战技 =="
for _, id in ipairs(role.ids) do
out[#out + 1] = p.renderArt(id)
end
-- 获取表(可通过 noacquire=1 关闭)
if args.noacquire ~= "1" then
out[#out + 1] = renderAcquire(first)
end
else
for _, v in ipairs(args) do
local id = tonumber(mw.text.trim(v))
if id then
out[#out + 1] = p.renderArt(id)
end
end
end
if #out == 0 then
return ""
end
return compress(table.concat(out, "\n"))
end
return p