模块

模块:Tbox

来自蒙纳多研究本部

头号云​(留言 | 贡献)2026年10月15日 (四) 00:11的版本 (创建页面,内容为“-- 模块:Tbox —— 宝箱渲染层(列表 / 单箱 infobox / SMW 注解) -- -- 用法: -- {{#invoke:Tbox|list}} 全部 20 个宝箱(按地图分组) -- {{#invoke:Tbox|list|map=ma1202}} 指定地图 -- {{#invoke:Tbox|list|mapName=监狱岛}} 按地图中文名 -- {{#invoke:Tbox|infobox|id=7}} 单个宝箱信息框 -- {{#invoke:Tbox|whereItem|代达罗斯}} 反向查询:某物品出自哪…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)

此模块的文档可以在模块:Tbox/doc创建

-- 模块:Tbox —— 宝箱渲染层(列表 / 单箱 infobox / SMW 注解)
--
-- 用法:
--   {{#invoke:Tbox|list}}                    全部 20 个宝箱(按地图分组)
--   {{#invoke:Tbox|list|map=ma1202}}         指定地图
--   {{#invoke:Tbox|list|mapName=监狱岛}}      按地图中文名
--   {{#invoke:Tbox|infobox|id=7}}            单个宝箱信息框
--   {{#invoke:Tbox|whereItem|代达罗斯}}        反向查询:某物品出自哪些宝箱(SMW)
--
-- 数据来源:模块:TboxData(硬编码快照)。
--   待 XB1DEData 注册 FLD_tboxlist / Gimlist 后,把 TboxData 换成实时取数即可,
--   本模块对外接口完全不变。
--
-- 地图:本期不启用。XB1DEWikiMap 目前只认 mine/collection/enemy 三种图层
--   (渲染器选择器 `.xb1map-${mode}`),宝箱图层需站长加 REST 端点后才能挂载。

local p = {}

local data = require("Module:TboxData")

-- ====== SMW 属性(需先在 wiki 上建好属性页,见《上线流程》) ======
-- 属性:Tbox编号     Number  — FLD_tboxlist.__id
-- 属性:Tbox地图     Text    — 地图中文名
-- 属性:Tbox地标     Text    — 最近地标
-- 属性:Tbox物品     Text    — 箱内物品(顿号连接,仅展示)
-- 属性:Tbox物品名   Text    — 箱内物品(多值,供 #ask 反查)
-- 属性:Tbox物品数   Number  — 箱内物品件数
-- 属性:Tbox坐标X    Number
-- 属性:Tbox坐标Y    Number
-- 属性:Tbox坐标Z    Number

local seen = {}  -- 同一次页面解析内防重复注解

-- 物品名 → 站内页面。存在则加链接,不存在则纯文本(避免红链)。
-- 现状:50 个物品里 36 个已有「物品:」页;宝珠/战技教学书类尚无独立页。
local function itemLink(name)
  local t = mw.title.new("物品:" .. name)
  if t and t.exists then
    return "[[" .. t.fullText .. "|" .. name .. "]]"
  end
  return name
end

-- ====== SMW 注解:每个宝箱一个 subobject ======
-- 依据《SMW踩坑记录》§13:多实体必须用 subobject,不能用 #set
--   (#set 会把整页箱子的值堆进同一属性,无法回答「哪一箱有这把武器」)
local function annotate(c)
  if seen[c.id] then return end
  seen[c.id] = true

  local smw = mw.smw
  if smw == nil then return end  -- SMW 不可用时静默跳过,页面照常渲染

  local items = c.items or {}
  local sub = {
    ["Tbox编号"] = c.id,
    ["Tbox地图"] = c.mapName or "",
    ["Tbox地标"] = c.landmark or "",
    ["Tbox坐标X"] = c.x,
    ["Tbox坐标Y"] = c.y,
    ["Tbox坐标Z"] = c.z,
    ["Tbox物品数"] = #items,
  }

  if #items > 0 then
    local arr = {}
    for i, n in ipairs(items) do arr[i] = n end
    sub["Tbox物品名"] = arr               -- 多值属性,供反查
    sub["Tbox物品"] = table.concat(arr, "、")
  end

  smw.subobject(sub)
end

-- ====== 物品单元格 ======
local function itemCell(c)
  local names = c.items or {}
  if #names == 0 then return "<i>(无记录)</i>" end
  local out = {}
  for _, n in ipairs(names) do
    table.insert(out, itemLink(n))
  end
  return table.concat(out, "<br>")
end

-- ====== 列表(按地图分组) ======
function p.list(frame)
  local args = frame.args
  local wantMap = args.map
  local wantName = args.mapName

  local byMap = data.byMap()
  local out = {}

  for _, mapId in ipairs(data.mapOrder) do
    local grp = byMap[mapId]
    if grp
      and (wantMap == nil or wantMap == mapId)
      and (wantName == nil or wantName == grp.name) then

      table.insert(out, '<h3>' .. grp.name .. '</h3>')
      table.insert(out, '<table class="wikitable xbtable" style="text-align:left;">')
      table.insert(out, "<tr><th>最近地标</th><th>内容</th></tr>")

      for _, c in ipairs(grp.list) do
        annotate(c)
        table.insert(out, "<tr>")
        table.insert(out, "<td>" .. (c.landmark or "—") .. "</td>")
        table.insert(out, "<td>" .. itemCell(c) .. "</td>")
        table.insert(out, "</tr>")
      end

      table.insert(out, "</table>")
    end
  end

  if #out == 0 then
    return "<i>未找到匹配的宝箱。</i>"
  end
  return table.concat(out, "\n")
end

-- ====== 单个宝箱 infobox ======
function p.infobox(frame)
  local id = tonumber(frame.args.id or frame.args[1])
  local c = data.get(id)
  if not c then
    return "<i>未找到宝箱 #" .. tostring(id) .. "</i>"
  end
  annotate(c)

  local out = {}
  table.insert(out, '<table class="wikitable xbtable" style="text-align:left;">')
  table.insert(out, '<tr><th colspan="2">宝箱 #' .. c.id .. '(' .. c.mapName .. ')</th></tr>')
  table.insert(out, "<tr><th>最近地标</th><td>" .. (c.landmark or "—") ..
    (c.dist and ("(约 " .. string.format("%.0f", c.dist) .. ")") or "") .. "</td></tr>")
  table.insert(out, '<tr><th>坐标</th><td><code>' ..
    string.format("%.1f, %.1f, %.1f", c.x, c.y, c.z) .. "</code></td></tr>")
  table.insert(out, "<tr><th>内容</th><td>" .. itemCell(c) .. "</td></tr>")
  table.insert(out, '<tr><th>gimID</th><td>' .. c.gimID .. "</td></tr>")
  table.insert(out, "</table>")
  return table.concat(out, "\n")
end

-- ====== 反向查询:某物品出自哪些宝箱 ======
-- 用法:{{#invoke:Tbox|whereItem|代达罗斯}} 或 |物品名=代达罗斯
function p.whereItem(frame)
  local name = frame.args["物品名"] or frame.args[1]
  if not name or name == "" then
    return "<i>请指定物品名</i>"
  end
  local smw = mw.smw
  if smw == nil then return "<i>SMW 不可用</i>" end

  local res = smw.ask {
    "[[Tbox物品名::" .. name .. "]]",
    "?Tbox地图",
    "?Tbox地标",
    "?Tbox编号",
    mainlabel = "宝箱",
  }
  if not res or #res == 0 then
    return "<i>没有宝箱记录包含「" .. name .. "」。</i>"
  end

  local out = { '<table class="wikitable xbtable">',
    "<tr><th>宝箱</th><th>地图</th><th>最近地标</th></tr>" }
  for _, r in ipairs(res) do
    table.insert(out, "<tr><td>" .. tostring(r[1] or "?") ..
      "</td><td>" .. tostring(r["Tbox地图"] or "?") ..
      "</td><td>" .. tostring(r["Tbox地标"] or "?") .. "</td></tr>")
  end
  table.insert(out, "</table>")
  return table.concat(out, "\n")
end

return p