|
|
| 第1行: |
第1行: |
| -- Module:CardData | | -- Module:CardData |
| -- 从 Directus API 获取「夜幕之下」卡片数据并渲染 | | -- 生成卡片 HTML 骨架,实际数据由前端 JS 从 Directus API 获取并填充 |
| -- 用法:{{#invoke:CardData|render|复仇童谣}} | | -- 用法:{{#invoke:CardData|render|复仇童谣}} |
|
| |
|
| local p = {} | | local p = {} |
|
| |
| -- ==============================
| |
| -- 配置
| |
| -- ==============================
| |
|
| |
| local API_BASE = "https://data.saltedkiss.org/items/cards"
| |
|
| |
| -- 需要获取的字段,去掉所有 user 相关信息
| |
| local FIELDS = table.concat({
| |
| "stylename",
| |
| "rarity",
| |
| "character.name",
| |
| "profession.name",
| |
| "desire.name",
| |
| "skill_normal_attack.name",
| |
| "skill_normal_attack.type",
| |
| "skill_normal_attack.weapon",
| |
| "skill_normal_attack.description",
| |
| "skill_normal_attack.levels.*",
| |
| "skill_passive.name",
| |
| "skill_passive.type",
| |
| "skill_passive.trigger_type",
| |
| "skill_passive.trigger_value",
| |
| "skill_passive.description",
| |
| "skill_passive.levels.*",
| |
| "skill_ultimate.name",
| |
| "skill_ultimate.type",
| |
| "skill_ultimate.desire_cost",
| |
| "skill_ultimate.description",
| |
| "skill_ultimate.levels.*",
| |
| "feats.stages"
| |
| }, ",")
| |
|
| |
| -- ==============================
| |
| -- 内部函数:通过卡名获取数据
| |
| -- ==============================
| |
|
| |
| local function fetchCardByName(name)
| |
| local url = API_BASE
| |
| .. "?fields=" .. FIELDS
| |
| .. "&filter[stylename][_eq]=" .. mw.uri.encode(name)
| |
|
| |
| -- 发起 HTTP 请求
| |
| local ok, result = pcall(mw.http.get, url)
| |
| if not ok or not result then
| |
| return nil, "HTTP 请求失败,请检查 $wgAllowExternalReqs 是否开启"
| |
| end
| |
|
| |
| -- 解析 JSON
| |
| local ok2, data = pcall(mw.text.jsonDecode, result)
| |
| if not ok2 or not data then
| |
| return nil, "JSON 解析失败"
| |
| end
| |
|
| |
| if not data.data or #data.data == 0 then
| |
| return nil, "找不到卡片:" .. name
| |
| end
| |
|
| |
| return data.data[1]
| |
| end
| |
|
| |
| -- ==============================
| |
| -- 内部函数:安全取值(避免 nil 报错)
| |
| -- ==============================
| |
|
| |
| local function safe(val, fallback)
| |
| if val ~= nil and val ~= "" then
| |
| return tostring(val)
| |
| end
| |
| return fallback or "—"
| |
| end
| |
|
| |
| -- ==============================
| |
| -- 内部函数:渲染单个技能区块
| |
| -- ==============================
| |
|
| |
| local function renderSkill(skill)
| |
| if not skill then return "" end
| |
|
| |
| local lines = {}
| |
| table.insert(lines, "; " .. safe(skill.name))
| |
| table.insert(lines, ": 类型:" .. safe(skill.type))
| |
|
| |
| -- 普通攻击有武器
| |
| if skill.weapon then
| |
| table.insert(lines, ": 武器:" .. safe(skill.weapon))
| |
| end
| |
|
| |
| -- 被动有触发条件
| |
| if skill.trigger_type then
| |
| table.insert(lines, ": 触发:" .. safe(skill.trigger_type) .. " × " .. safe(skill.trigger_value))
| |
| end
| |
|
| |
| -- 终结技有欲望消耗
| |
| if skill.desire_cost then
| |
| table.insert(lines, ": 欲望消耗:" .. safe(skill.desire_cost))
| |
| end
| |
|
| |
| table.insert(lines, ": " .. safe(skill.description))
| |
|
| |
| -- 升级数值
| |
| if skill.levels and #skill.levels > 0 then
| |
| for _, lv in ipairs(skill.levels) do
| |
| table.insert(lines, ":; 升级效果:" .. safe(lv.name))
| |
| if lv.levels then
| |
| table.insert(lines, ":: " .. table.concat(lv.levels, " / "))
| |
| end
| |
| end
| |
| end
| |
|
| |
| return table.concat(lines, "\n")
| |
| end
| |
|
| |
| -- ==============================
| |
| -- 内部函数:渲染觉醒区块
| |
| -- ==============================
| |
|
| |
| local function renderFeats(feats)
| |
| if not feats or #feats == 0 then return "" end
| |
|
| |
| local lines = {}
| |
| table.insert(lines, "=== 觉醒 ===")
| |
|
| |
| for _, feat in ipairs(feats) do
| |
| if feat.stages then
| |
| for _, stage in ipairs(feat.stages) do
| |
| local stageName = stage.extra_name or ("觉醒 " .. tostring(stage.stage))
| |
| table.insert(lines, "; " .. stageName)
| |
|
| |
| -- 属性加成
| |
| if stage.stat_boosts then
| |
| for _, boost in ipairs(stage.stat_boosts) do
| |
| table.insert(lines, ": " .. safe(boost.type) .. " +" .. safe(boost.value))
| |
| end
| |
| end
| |
|
| |
| -- 技能效果
| |
| if stage.value then
| |
| for _, v in ipairs(stage.value) do
| |
| table.insert(lines, ": " .. safe(v.description))
| |
| end
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| return table.concat(lines, "\n")
| |
| end
| |
|
| |
| -- ==============================
| |
| -- 对外入口:render
| |
| -- ==============================
| |
|
| |
|
| function p.render(frame) | | function p.render(frame) |
| -- 支持 {{#invoke:CardData|render|卡名}} 或 {{#invoke:CardData|render|name=卡名}} | | -- 支持位置参数或具名参数 |
| local name = frame.args[1] or frame.args.name | | local name = frame.args[1] or frame.args.name or "" |
| if not name or name == "" then
| |
| return '<span style="color:red">错误:请提供卡片名称,例如 {{#invoke:CardData|render|复仇童谣}}</span>'
| |
| end
| |
| | |
| name = mw.text.trim(name) | | name = mw.text.trim(name) |
|
| |
|
| local card, err = fetchCardByName(name) | | if name == "" then |
| if not card then
| | return '<span class="error">错误:请提供卡片名称,例如 {{#invoke:CardData|render|复仇童谣}}</span>' |
| return '<span style="color:red">错误:' .. (err or "未知错误") .. '</span>' | |
| end | | end |
|
| |
|
| -- 开始拼装输出 | | -- 生成骨架容器,data-cardname 供 JS 读取 |
| local lines = {}
| | -- JS 会找到所有 .ron-card 元素,根据 data-cardname 请求 Directus,然后填充内容 |
| | | return '<div class="ron-card" data-cardname="' |
| -- 基本信息 | | .. mw.text.encode(name) |
| table.insert(lines, "== " .. card.stylename .. " ==") | | .. '"><div class="ron-card-loading">⏳ 加载中…</div></div>' |
| table.insert(lines, "; 稀有度:" .. safe(card.rarity))
| |
| table.insert(lines, "; 角色:" .. safe(card.character and card.character.name))
| |
| table.insert(lines, "; 职业:" .. safe(card.profession and card.profession.name))
| |
| table.insert(lines, "; 欲望:" .. safe(card.desire and card.desire.name))
| |
| | |
| -- 技能
| |
| table.insert(lines, "=== 普通攻击 ===")
| |
| table.insert(lines, renderSkill(card.skill_normal_attack))
| |
| | |
| table.insert(lines, "=== 被动技能 ===")
| |
| table.insert(lines, renderSkill(card.skill_passive))
| |
| | |
| table.insert(lines, "=== 终结技 ===")
| |
| table.insert(lines, renderSkill(card.skill_ultimate))
| |
| | |
| -- 觉醒
| |
| table.insert(lines, renderFeats(card.feats))
| |
| | |
| return table.concat(lines, "\n")
| |
| end | | end |
|
| |
|
| return p | | return p |
此模块的文档可以在模块:CardData/doc创建
-- Module:CardData
-- 生成卡片 HTML 骨架,实际数据由前端 JS 从 Directus API 获取并填充
-- 用法:{{#invoke:CardData|render|复仇童谣}}
local p = {}
function p.render(frame)
-- 支持位置参数或具名参数
local name = frame.args[1] or frame.args.name or ""
name = mw.text.trim(name)
if name == "" then
return '<span class="error">错误:请提供卡片名称,例如 {{#invoke:CardData|render|复仇童谣}}</span>'
end
-- 生成骨架容器,data-cardname 供 JS 读取
-- JS 会找到所有 .ron-card 元素,根据 data-cardname 请求 Directus,然后填充内容
return '<div class="ron-card" data-cardname="'
.. mw.text.encode(name)
.. '"><div class="ron-card-loading">⏳ 加载中…</div></div>'
end
return p