模块:ArtsShop
来自蒙纳多研究本部
此模块的文档可以在模块:ArtsShop/doc创建
-- Module:ArtsShop —— 商店战技书查询(独立模块,与 ArtsData 主链路解耦)
--
-- 用法:
-- {{#invoke:ArtsShop|main|12}} 渲染战技12的商店在售表
-- {{#invoke:ArtsShop|offers|12}} 返回原始数据(Lua 表,供其他模块调用)
--
-- 数据链路(XBDB 深度解析已验证 2026-10-08):
-- shop_index[战技ID] → 货架行号列表(静态:XBDB 无法枚举,行号映射离线建)
-- → post shoplist[货架行](实时)
-- → post FLD_npclist(shop_npc 货架行) → 商店名(实时中文,缓存)
-- → post ITM_artslist[战技书行] → 书名 + 实时价格
--
-- ⚠ 注释风格约定:本文件只用 -- 行注释,不用 --[[ ]] 块注释
-- (块注释会被正文里的 ]] 提前终止,已踩坑一次)
local p = {}
local post = require("Module:RequestHelper").post
local getArgs = require("Module:Arguments").getArgs
local html = require("Module:Html")
local compress = html.compress
local shopIndex = require("Module:ArtsShop/index")
local shopNpc = require("Module:ArtsShop/npc")
local lv1 = require("Module:ArtsData/lv1")
-- 货架行号 -> 商店名(NPC 名 + mob_name,缓存)
local npcCache = {}
local function fetchShopName(shopRow)
local npcId = shopNpc[shopRow]
if not npcId then return nil end
local cached = npcCache[npcId]
if cached ~= nil then return cached end
local name = nil
local ok, npc = pcall(post, "bdat_common.FLD_npclist", npcId)
if ok and type(npc) == "table" then
local parts = {}
if npc.name and npc.name ~= "" then parts[#parts + 1] = npc.name end
if npc.mob_name and npc.mob_name ~= "" and npc.mob_name ~= npc.name then
parts[#parts + 1] = npc.mob_name
end
name = #parts > 0 and table.concat(parts, "·") or nil
end
npcCache[npcId] = name
return name
end
-- 查询某战技的商店在售。返回数组或 nil:
-- { { shopRow=货架行, shopName=商店名, artName=书名, money=价格 }, ... }
function p.fetchOffers(artId)
artId = tonumber(artId)
local entries = artId and shopIndex[artId] or nil
if not entries or #entries == 0 then
return nil
end
local offers = {}
for _, e in ipairs(entries) do
local o = { shopRow = e.sr, money = e.m }
local ok, row = pcall(post, "bdat_common.shoplist", e.sr)
if ok and type(row) == "table" then
o.shopName = fetchShopName(e.sr)
local okB, book = pcall(post, "bdat_common.ITM_artslist", e.br)
if okB and type(book) == "table" and book.name then
o.artName = book.name
if book.money then o.money = book.money end
end
offers[#offers + 1] = o
end
end
if #offers == 0 then return nil end
return offers
end
-- Lua 层调用别名
p.offers = p.fetchOffers
-- 渲染商店在售表
function p.main(frame)
local args = getArgs(frame)
local artId = args[1] and mw.text.trim(args[1]) or ""
local offers = p.fetchOffers(artId)
if not offers then
return ""
end
return compress(p.renderOffers(artId, offers))
end
-- 按角色渲染全部有商店在售的战技:{{#invoke:ArtsShop|role|shulk}}
function p.role(frame)
local args = getArgs(frame)
local key = args[1] and mw.text.trim(args[1]) or ""
local role = lv1.roles[key]
if not role then
return ""
end
local out = {}
out[#out + 1] = html.h2(role.name .. " · 战技书商店在售")
for _, id in ipairs(role.ids) do
local offers = p.fetchOffers(id)
if offers then
out[#out + 1] = p.renderOffers(id, offers)
end
end
if #out == 1 then
return ""
end
return compress(table.concat(out, "\n"))
end
-- 从 main 提取的渲染核心(单个战技的商店表),供 main 与 role 复用
function p.renderOffers(artId, offers)
local artName = offers[1].artName or ("战技 " .. artId)
local out = {}
out[#out + 1] = html.h3(artName .. " · 商店在售")
out[#out + 1] = '<table class="wikitable" style="text-align:center;">'
out[#out + 1] = "<tr><th>商店</th><th>战技书</th><th>价格</th></tr>"
for _, o in ipairs(offers) do
out[#out + 1] = "<tr>"
.. "<td>" .. (o.shopName or ("货架 " .. tostring(o.shopRow))) .. "</td>"
.. "<td>" .. (o.artName or "—") .. "</td>"
.. "<td>" .. (o.money and (tostring(o.money) .. " G") or "—") .. "</td>"
.. "</tr>"
end
out[#out + 1] = "</table>"
return table.concat(out, "\n")
end
return p