卡牌:修订间差异
来自卡厄思梦境WIKI
小无编辑摘要 |
小无编辑摘要 标签:已被回退 |
||
| 第1行: | 第1行: | ||
local p = {} | local p = {} | ||
-- 安全获取嵌套表数据 | |||
local function safeGet(tbl, key, default) | |||
if tbl and tbl[key] ~= nil then | |||
return tbl[key] | |||
end | |||
return default or "" | |||
end | |||
-- 颜色映射表 | -- 颜色映射表 | ||
local COLOR_MAP = { | local COLOR_MAP = { | ||
["白"] = { bgColor = " | ["白"] = { bgColor = "#ffffff", textColor = "#333333" }, | ||
["蓝"] = { bgColor = " | ["蓝"] = { bgColor = "#4a90e2", textColor = "white" }, | ||
[" | ["紫"] = { bgColor = "#9b59b6", textColor = "white" }, | ||
[" | ["金"] = { bgColor = "#f1c40f", textColor = "#333333" } | ||
} | } | ||
-- | -- 计算名称缩放比例 | ||
local function | local function calculateNameScale(name) | ||
return | local length = mw.ustring.len(name) | ||
-- 基准是5个字符以内 | |||
if length <= 5 then | |||
return "1" | |||
elseif length <= 7 then | |||
return "0.9" | |||
elseif length <= 9 then | |||
return "0.8" | |||
elseif length <= 11 then | |||
return "0.7" | |||
else | |||
return "0.6" | |||
end | |||
end | end | ||
-- | -- 处理描述文本 | ||
local function processDescription(frame, description) | local function processDescription(frame, description) | ||
if not description or description == "" then | if not description or description == "" then | ||
return | return "" | ||
end | end | ||
-- 处理换行符 | |||
return | description = description:gsub("\r?\n", "<br>") | ||
-- 处理图标:{{图标名称}} 转换为对应图片 | |||
description = description:gsub("{{(.-)}}", function(iconName) | |||
return string.format('[[File:icon_%s.png|16px|link=|style="vertical-align: -3px; margin: 0 1px;"]]', iconName) | |||
end) | end) | ||
return | -- 处理高亮文本:<hl>文本</hl> 转换为金色 | ||
description = description:gsub("<hl>(.-)</hl>", function(text) | |||
return string.format('<span style="color: #f1c832; font-weight: bold; text-shadow: 0 0 2px rgba(241, 200, 50, 0.5);">%s</span>', text) | |||
end) | |||
return frame:preprocess(description) | |||
end | end | ||
| 第51行: | 第80行: | ||
end | end | ||
-- | -- 检查原卡牌与当前卡牌的机制变化(将"无"视为空字符串) | ||
local mechanism = safeGet(cardData, "机制", "") | local mechanism = safeGet(cardData, "机制", "") | ||
local originalMechanism = baseCardData and safeGet(baseCardData, "机制", "") or mechanism | local originalMechanism = baseCardData and safeGet(baseCardData, "机制", "") or mechanism | ||
| 第68行: | 第97行: | ||
apGlowColor = apGlowColor, | apGlowColor = apGlowColor, | ||
cardType = safeGet(cardData, "类型", ""), | cardType = safeGet(cardData, "类型", ""), | ||
art = safeGet(cardData, "art", ""), | art = safeGet(cardData, "art", "start_1041_01.png"), | ||
mechanism = mechanism, | mechanism = mechanism, | ||
mechanismChanged = mechanismChanged | mechanismChanged = mechanismChanged | ||
| 第91行: | 第120行: | ||
cardName or "", characterName or "", deckType or "", derivedCards or "") | cardName or "", characterName or "", deckType or "", derivedCards or "") | ||
-- | -- 处理机制文本(当机制为"无"或空时不显示) | ||
local mechanismHTML = "" | local mechanismHTML = "" | ||
if style.mechanism and style.mechanism ~= " | if style.mechanism and style.mechanism ~= "" then | ||
local mechanismColor = style.mechanismChanged and "#b5f651" or "#f6c478" | local mechanismColor = style.mechanismChanged and "#b5f651" or "#f6c478" | ||
mechanismHTML = string.format('<div style="color: %s; margin-bottom: 4px;">[%s]</div>', | mechanismHTML = string.format('<div style="color: %s; margin-bottom: 4px;">[%s]</div>', | ||
mechanismColor, style.mechanism:gsub("、", "/")) | mechanismColor, style.mechanism:gsub("、", "/")) | ||
end | end | ||
-- 计算名称缩放比例 | |||
local nameScale = calculateNameScale(cardName) | |||
-- 使用字符串拼接优化HTML生成 | -- 使用字符串拼接优化HTML生成 | ||
| 第116行: | 第148行: | ||
style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor)) | style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor)) | ||
-- | -- 卡牌名称(使用transform缩放) | ||
table.insert(htmlParts, string.format('<div style="position: absolute; left: 50px; top: 13px; color: %s; font-size:16px">%s</div>', | table.insert(htmlParts, string.format('<div style="position: absolute; left: 50px; top: 13px; width: 105px; transform-origin: left center; transform: scaleX(%s);"><span style="color: %s; font-size: 16px; white-space: nowrap;">%s</span></div>', | ||
style.colorStyle.textColor, cardName)) | nameScale, style.colorStyle.textColor, cardName)) | ||
-- 卡牌类型 | |||
table.insert(htmlParts, string.format('<div style="position: absolute; left: 48px; top: 30px;">[[File:icon_card_%s.png|18px|link=]]</div>', | table.insert(htmlParts, string.format('<div style="position: absolute; left: 48px; top: 30px;">[[File:icon_card_%s.png|18px|link=]]</div>', | ||
style.cardType)) | style.cardType)) | ||
| 第143行: | 第177行: | ||
style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor)) | style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor)) | ||
-- | -- 卡牌名称(使用transform缩放) | ||
table.insert(htmlParts, string.format('<div style="position: absolute; left: 50px; top: 13px; color: %s; font-size:16px">%s</div>', | table.insert(htmlParts, string.format('<div style="position: absolute; left: 50px; top: 13px; width: 105px; transform-origin: left center; transform: scaleX(%s);"><span style="color: %s; font-size: 16px; white-space: nowrap;">%s</span></div>', | ||
style.colorStyle.textColor, cardName)) | nameScale, style.colorStyle.textColor, cardName)) | ||
-- 卡牌类型 | |||
table.insert(htmlParts, string.format('<div style="position: absolute; left: 48px; top: 30px;">[[File:icon_card_%s.png|18px|link=]]</div>', | table.insert(htmlParts, string.format('<div style="position: absolute; left: 48px; top: 30px;">[[File:icon_card_%s.png|18px|link=]]</div>', | ||
style.cardType)) | style.cardType)) | ||
| 第164行: | 第200行: | ||
end | end | ||
-- | -- 描述文本(包含机制)- 对所有类型都显示 | ||
table.insert(htmlParts, '<div style="position: absolute; bottom: 7px; left: 15px; width: 144px; height: 100px; font-size: 12px; color: white; line-height: 13px; display: flex; justify-content: center; align-items: center; text-align: center;">') | table.insert(htmlParts, '<div style="position: absolute; bottom: 7px; left: 15px; width: 144px; height: 100px; font-size: 12px; color: white; line-height: 13px; display: flex; justify-content: center; align-items: center; text-align: center;">') | ||
table.insert(htmlParts, '<div style="line-height: 13px;">') | table.insert(htmlParts, '<div style="line-height: 13px;">') | ||
| 第176行: | 第212行: | ||
end | end | ||
-- | -- 获取基础卡牌数据(用于对比) | ||
local function | local function getBaseCardData(cardName) | ||
local | local data = mw.loadData("模块:卡牌数据") | ||
return data[cardName] | |||
end | end | ||
-- | -- 主函数:显示卡牌 | ||
function p.displayCard(frame) | |||
local | local args = frame.args | ||
local | local cardName = args[1] or args["name"] | ||
local | local characterName = args[2] or args["character"] or "" | ||
local deckType = args[3] or args["deck"] or "" | |||
if not cardName then | |||
return "错误:未指定卡牌名称" | |||
end | |||
-- | -- 优先从角色专属数据加载 | ||
local cardData = nil | |||
local | local baseCardData = nil | ||
if | |||
-- | if characterName ~= "" then | ||
local charDataModule = "模块:卡牌数据/" .. characterName | |||
local success, charData = pcall(mw.loadData, charDataModule) | |||
if success then | |||
-- 根据卡组类型查找 | |||
if deckType ~= "" and charData[deckType] then | |||
cardData = charData[deckType][cardName] | |||
else | |||
-- 遍历所有卡组查找 | |||
for _, deck in pairs(charData) do | |||
if type(deck) == "table" and deck[cardName] then | |||
cardData = deck[cardName] | |||
break | |||
end | |||
end | |||
end | |||
end | |||
-- 如果是变体卡牌,获取基础卡牌数据 | |||
if cardData then | |||
baseCardData = getBaseCardData(cardName) | |||
end | end | ||
end | end | ||
-- | -- 如果没有找到专属数据,从通用数据加载 | ||
local | if not cardData then | ||
local data = mw.loadData("模块:卡牌数据") | |||
cardData = data[cardName] | |||
end | |||
if not cardData then | |||
return string.format("错误:找不到卡牌 '%s' 的数据", cardName) | |||
end | end | ||
return | return buildCardHTML(frame, cardName, cardData, baseCardData, characterName) | ||
end | end | ||
-- | -- 批量显示卡牌 | ||
function p.displayCards(frame) | |||
local | local args = frame.args | ||
local cardList = args[1] or args["cards"] or "" | |||
local characterName = args[2] or args["character"] or "" | |||
local columns = tonumber(args[3] or args["columns"]) or 5 | |||
if cardList == "" then | |||
if | return "错误:未指定卡牌列表" | ||
return | |||
end | end | ||
-- | -- 解析卡牌列表 | ||
local | local cards = {} | ||
for | for card in mw.text.gsplit(cardList, ",") do | ||
if card | card = mw.text.trim(card) | ||
table.insert( | if card ~= "" then | ||
table.insert(cards, card) | |||
end | end | ||
end | end | ||
if # | if #cards == 0 then | ||
return | return "错误:卡牌列表为空" | ||
end | end | ||
-- | -- 构建HTML | ||
if | local html = {} | ||
local | table.insert(html, '<div style="display: flex; flex-wrap: wrap; gap: 10px;">') | ||
for i, cardName in ipairs(cards) do | |||
-- 获取卡牌数据 | |||
local cardData = nil | |||
local baseCardData = nil | |||
if characterName ~= "" then | |||
local charDataModule = "模块:卡牌数据/" .. characterName | |||
local success, charData = pcall(mw.loadData, charDataModule) | |||
if success then | |||
for _, deck in pairs(charData) do | |||
if type(deck) == "table" and deck[cardName] then | |||
cardData = deck[cardName] | |||
break | |||
end | |||
end | |||
end | |||
if cardData then | |||
baseCardData = getBaseCardData(cardName) | |||
end | |||
end | |||
if not cardData then | |||
local data = mw.loadData("模块:卡牌数据") | |||
cardData = data[cardName] | |||
end | |||
if cardData then | |||
table.insert(html, buildCardHTML(frame, cardName, cardData, baseCardData, characterName)) | |||
else | |||
table.insert(html, string.format('<div style="color: red;">找不到卡牌: %s</div>', cardName)) | |||
end | |||
-- 添加换行 | |||
if i % columns == 0 and i < #cards then | |||
table.insert(html, '</div><div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">') | |||
end | |||
end | end | ||
table.insert(html, '</div>') | |||
return table.concat( | return table.concat(html) | ||
end | end | ||
-- | -- 显示角色的所有卡牌 | ||
function p. | function p.displayCharacterCards(frame) | ||
local args = frame.args | local args = frame.args | ||
local characterName = args[1] or "" | local characterName = args[1] or args["character"] | ||
local | local deckType = args[2] or args["deck"] or "all" | ||
local columns = tonumber(args[3] or args["columns"]) or 5 | |||
local | |||
if not characterName then | |||
return "错误:未指定角色名称" | |||
if not | |||
return | |||
end | end | ||
-- | local charDataModule = "模块:卡牌数据/" .. characterName | ||
if | local success, charData = pcall(mw.loadData, charDataModule) | ||
return | |||
if not success then | |||
return string.format("错误:找不到角色 '%s' 的卡牌数据", characterName) | |||
end | |||
-- 收集要显示的卡牌 | |||
local cardsToDisplay = {} | |||
if deckType == "all" then | |||
-- 显示所有卡组 | |||
local deckOrder = {"起始卡牌", "基础卡牌", "高级卡牌", "其他卡牌"} | |||
for _, dName in ipairs(deckOrder) do | |||
if charData[dName] then | |||
for cardName, cardData in pairs(charData[dName]) do | |||
table.insert(cardsToDisplay, {name = cardName, data = cardData, deck = dName}) | |||
end | |||
end | |||
end | |||
else | |||
-- 显示特定卡组 | |||
if charData[deckType] then | |||
for cardName, cardData in pairs(charData[deckType]) do | |||
table.insert(cardsToDisplay, {name = cardName, data = cardData, deck = deckType}) | |||
end | |||
else | |||
return string.format("错误:角色 '%s' 没有 '%s' 卡组", characterName, deckType) | |||
end | |||
end | end | ||
-- | -- 排序(可选:按稀有度、AP值等排序) | ||
local | table.sort(cardsToDisplay, function(a, b) | ||
return a.name < b.name | |||
end) | |||
-- 构建HTML | |||
local html = {} | |||
table.insert(html, '<div style="display: flex; flex-wrap: wrap; gap: 10px;">') | |||
for i, card in ipairs(cardsToDisplay) do | |||
local baseCardData = getBaseCardData(card.name) | |||
table.insert(html, buildCardHTML(frame, card.name, card.data, baseCardData, characterName)) | |||
if i % columns == 0 and i < #cardsToDisplay then | |||
table.insert(html, '</div><div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">') | |||
end | |||
end | end | ||
return | table.insert(html, '</div>') | ||
return table.concat(html) | |||
end | end | ||
-- | -- 搜索卡牌 | ||
function p.searchCards(frame) | |||
local args = frame.args | |||
local searchTerm = args[1] or args["search"] or "" | |||
local searchType = args[2] or args["type"] or "name" -- name, description, mechanism | |||
local columns = tonumber(args[3] or args["columns"]) or 5 | |||
if searchTerm == "" then | |||
return "错误:未指定搜索词" | |||
end | |||
searchTerm = mw.ustring.lower(searchTerm) | |||
local data = mw.loadData("模块:卡牌数据") | |||
local results = {} | |||
for cardName, cardData in pairs(data) do | |||
local match = false | |||
if searchType == "name" then | |||
match = mw.ustring.find(mw.ustring.lower(cardName), searchTerm) ~= nil | |||
elseif searchType == "description" then | |||
local desc = safeGet(cardData, "描述", "") | |||
match = mw.ustring.find(mw.ustring.lower(desc), searchTerm) ~= nil | |||
elseif searchType == "mechanism" then | |||
local mech = safeGet(cardData, "机制", "") | |||
match = mw.ustring.find(mw.ustring.lower(mech), searchTerm) ~= nil | |||
elseif searchType == "all" then | |||
local nameMatch = mw.ustring.find(mw.ustring.lower(cardName), searchTerm) ~= nil | |||
local descMatch = mw.ustring.find(mw.ustring.lower(safeGet(cardData, "描述", "")), searchTerm) ~= nil | |||
local mechMatch = mw.ustring.find(mw.ustring.lower(safeGet(cardData, "机制", "")), searchTerm) ~= nil | |||
match = nameMatch or descMatch or mechMatch | |||
end | |||
if match then | |||
table.insert(results, {name = cardName, data = cardData}) | |||
end | end | ||
end | end | ||
}) | |||
if #results == 0 then | |||
return "没有找到符合条件的卡牌" | |||
end | |||
-- 排序 | |||
table.sort(results, function(a, b) | |||
return a.name < b.name | |||
end) | |||
-- 构建HTML | |||
local html = {} | |||
table.insert(html, string.format('<div>找到 %d 张卡牌:</div>', #results)) | |||
table.insert(html, '<div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">') | |||
for i, result in ipairs(results) do | |||
table.insert(html, buildCardHTML(frame, result.name, result.data, nil, "")) | |||
if i % columns == 0 and i < #results then | |||
table.insert(html, '</div><div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">') | |||
end | |||
end | |||
table.insert(html, '</div>') | |||
return table.concat(html) | |||
end | |||
return p | return p | ||
2025年10月1日 (三) 18:34的版本
此模块的文档可以在模块:卡牌/doc创建
local p = {}
-- 安全获取嵌套表数据
local function safeGet(tbl, key, default)
if tbl and tbl[key] ~= nil then
return tbl[key]
end
return default or ""
end
-- 颜色映射表
local COLOR_MAP = {
["白"] = { bgColor = "#ffffff", textColor = "#333333" },
["蓝"] = { bgColor = "#4a90e2", textColor = "white" },
["紫"] = { bgColor = "#9b59b6", textColor = "white" },
["金"] = { bgColor = "#f1c40f", textColor = "#333333" }
}
-- 计算名称缩放比例
local function calculateNameScale(name)
local length = mw.ustring.len(name)
-- 基准是5个字符以内
if length <= 5 then
return "1"
elseif length <= 7 then
return "0.9"
elseif length <= 9 then
return "0.8"
elseif length <= 11 then
return "0.7"
else
return "0.6"
end
end
-- 处理描述文本
local function processDescription(frame, description)
if not description or description == "" then
return ""
end
-- 处理换行符
description = description:gsub("\r?\n", "<br>")
-- 处理图标:{{图标名称}} 转换为对应图片
description = description:gsub("{{(.-)}}", function(iconName)
return string.format('[[File:icon_%s.png|16px|link=|style="vertical-align: -3px; margin: 0 1px;"]]', iconName)
end)
-- 处理高亮文本:<hl>文本</hl> 转换为金色
description = description:gsub("<hl>(.-)</hl>", function(text)
return string.format('<span style="color: #f1c832; font-weight: bold; text-shadow: 0 0 2px rgba(241, 200, 50, 0.5);">%s</span>', text)
end)
return frame:preprocess(description)
end
-- 生成卡牌HTML样式组件
local function buildStyleComponents(cardData, cardName, baseCardData)
local color = safeGet(cardData, "稀有度", "白")
local colorStyle = COLOR_MAP[color] or COLOR_MAP["白"]
local attribute = safeGet(cardData, "属性", "虚无")
local cardDeck = safeGet(cardData, "卡组", "起始卡牌")
local ap = safeGet(cardData, "AP", "")
local originalAP = baseCardData and safeGet(baseCardData, "AP", ap) or ap
-- 决定AP的发光颜色
local apGlowColor = "#4a90e2" -- 默认蓝色
if baseCardData and ap ~= originalAP then
if ap == "X" then
apGlowColor = "#4a90e2" -- X值保持默认蓝色
elseif tonumber(ap) and tonumber(originalAP) then
if tonumber(ap) < tonumber(originalAP) then
apGlowColor = "#51f651" -- 绿色(AP降低)
else
apGlowColor = "#f65151" -- 红色(AP增加)
end
end
end
-- 检查原卡牌与当前卡牌的机制变化(将"无"视为空字符串)
local mechanism = safeGet(cardData, "机制", "")
local originalMechanism = baseCardData and safeGet(baseCardData, "机制", "") or mechanism
-- 将"无"视为空字符串
if mechanism == "无" then mechanism = "" end
if originalMechanism == "无" then originalMechanism = "" end
local mechanismChanged = baseCardData and mechanism ~= "" and originalMechanism == ""
return {
color = color,
colorStyle = colorStyle,
attribute = attribute,
ap = ap,
apGlowColor = apGlowColor,
cardType = safeGet(cardData, "类型", ""),
art = safeGet(cardData, "art", "start_1041_01.png"),
mechanism = mechanism,
mechanismChanged = mechanismChanged
}
end
-- 构建卡牌HTML
local function buildCardHTML(frame, cardName, cardData, baseCardData, characterName)
if not cardData then
return string.format("找不到卡牌数据: %s", cardName or "未指定")
end
local style = buildStyleComponents(cardData, cardName, baseCardData)
local description = processDescription(frame, safeGet(cardData, "描述", ""))
-- 获取衍生卡牌信息
local derivedCards = safeGet(cardData, "衍生卡牌", "")
local deckType = safeGet(cardData, "卡组", "")
-- 生成数据属性
local dataAttrs = string.format('data-card-name="%s" data-character="%s" data-deck-type="%s" data-derived-cards="%s"',
cardName or "", characterName or "", deckType or "", derivedCards or "")
-- 处理机制文本(当机制为"无"或空时不显示)
local mechanismHTML = ""
if style.mechanism and style.mechanism ~= "" then
local mechanismColor = style.mechanismChanged and "#b5f651" or "#f6c478"
mechanismHTML = string.format('<div style="color: %s; margin-bottom: 4px;">[%s]</div>',
mechanismColor, style.mechanism:gsub("、", "/"))
end
-- 计算名称缩放比例
local nameScale = calculateNameScale(cardName)
-- 使用字符串拼接优化HTML生成
local htmlParts = {
string.format('<div class="game-card" %s style="display: inline-block; vertical-align: top; position: relative; width: 168px; height: 230px; overflow: hidden; margin: 0px; cursor: pointer;">', dataAttrs),
}
-- 根据类型判断使用哪种布局
if style.cardType == "状态异常" then
-- 状态异常卡牌的特殊布局
table.insert(htmlParts, string.format('<div style="position: absolute; top: 5px; left: 5px;">[[File:%s|150px|link=]]</div>', style.art))
table.insert(htmlParts, string.format('<div style="position: absolute; top: 0px; left: 0px;">[[File:card_状态异常_%s.png|159px|link=]]</div>', style.attribute))
-- AP值
table.insert(htmlParts, string.format('<div style="position: absolute; left: 24px; top: 4px; color: white; font-weight: bold; text-shadow: 0 0 10px %s,0 0 20px %s, 0 0 30px %s, 0 0 40px %s; font-size: 32px;">%s</div>',
style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor, style.ap))
table.insert(htmlParts, string.format('<div style="position: absolute; left: 25px; top: 34px; color: white; font-weight: bold; text-shadow: 0 0 10px %s,0 0 20px %s, 0 0 30px %s, 0 0 40px %s;">—</div>',
style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor))
-- 卡牌名称(使用transform缩放)
table.insert(htmlParts, string.format('<div style="position: absolute; left: 50px; top: 13px; width: 105px; transform-origin: left center; transform: scaleX(%s);"><span style="color: %s; font-size: 16px; white-space: nowrap;">%s</span></div>',
nameScale, style.colorStyle.textColor, cardName))
-- 卡牌类型
table.insert(htmlParts, string.format('<div style="position: absolute; left: 48px; top: 30px;">[[File:icon_card_%s.png|18px|link=]]</div>',
style.cardType))
table.insert(htmlParts, string.format('<div style="position: absolute; left: 68px; top: 33px; color: white; font-size:14px">%s</div>',
style.cardType))
-- 顶层蒙版
table.insert(htmlParts, '<div style="position: absolute; left: 0px; bottom: 0px;">[[File:card_顶层蒙版.png|168px|link=]]</div>')
else
-- 普通卡牌的布局
-- 背景图片层
table.insert(htmlParts, string.format('<div style="position: absolute; top: 1px; left: 12px;">[[File:%s|151px|link=]]</div>', style.art))
table.insert(htmlParts, '<div style="position: absolute; top: 1px; left: 12px;">[[File:card_黑色蒙版.png|151px|link=]]</div>')
-- 颜色条
table.insert(htmlParts, string.format('<div style="position: absolute; top: 21px; left: 10px; width: 148px; height: 8px; background-color: %s;"></div>',
style.colorStyle.bgColor))
-- AP值
table.insert(htmlParts, string.format('<div style="position: absolute; left: 24px; top: 4px; color: white; font-weight: bold; text-shadow: 0 0 10px %s,0 0 20px %s, 0 0 30px %s, 0 0 40px %s; font-size: 32px;">%s</div>',
style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor, style.ap))
table.insert(htmlParts, string.format('<div style="position: absolute; left: 25px; top: 34px; color: white; font-weight: bold; text-shadow: 0 0 10px %s,0 0 20px %s, 0 0 30px %s, 0 0 40px %s;">—</div>',
style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor))
-- 卡牌名称(使用transform缩放)
table.insert(htmlParts, string.format('<div style="position: absolute; left: 50px; top: 13px; width: 105px; transform-origin: left center; transform: scaleX(%s);"><span style="color: %s; font-size: 16px; white-space: nowrap;">%s</span></div>',
nameScale, style.colorStyle.textColor, cardName))
-- 卡牌类型
table.insert(htmlParts, string.format('<div style="position: absolute; left: 48px; top: 30px;">[[File:icon_card_%s.png|18px|link=]]</div>',
style.cardType))
table.insert(htmlParts, string.format('<div style="position: absolute; left: 68px; top: 33px; color: white; font-size:14px">%s</div>',
style.cardType))
-- 边框
table.insert(htmlParts, string.format('<div style="position: absolute; top: 0px; left: 1px;">[[File:card_属性边框_%s.png|160px|link=]]</div>',
style.attribute))
-- 稀有度
table.insert(htmlParts, string.format('<div style="position: absolute; top: 10px; left: 0px;">[[File:card_稀有度_%s.png|22px|link=]]</div>',
style.color))
table.insert(htmlParts, string.format('<div style="position: absolute; top: 2px; right: 4px;">[[File:card_稀有度_边框_%s.png|11px|link=]]</div>',
style.color))
-- 顶层蒙版
table.insert(htmlParts, '<div style="position: absolute; left: 0px; bottom: 0px;">[[File:card_顶层蒙版.png|168px|link=]]</div>')
end
-- 描述文本(包含机制)- 对所有类型都显示
table.insert(htmlParts, '<div style="position: absolute; bottom: 7px; left: 15px; width: 144px; height: 100px; font-size: 12px; color: white; line-height: 13px; display: flex; justify-content: center; align-items: center; text-align: center;">')
table.insert(htmlParts, '<div style="line-height: 13px;">')
table.insert(htmlParts, mechanismHTML)
table.insert(htmlParts, description)
table.insert(htmlParts, '</div>')
table.insert(htmlParts, '</div>')
table.insert(htmlParts, '</div>')
return table.concat(htmlParts)
end
-- 获取基础卡牌数据(用于对比)
local function getBaseCardData(cardName)
local data = mw.loadData("模块:卡牌数据")
return data[cardName]
end
-- 主函数:显示卡牌
function p.displayCard(frame)
local args = frame.args
local cardName = args[1] or args["name"]
local characterName = args[2] or args["character"] or ""
local deckType = args[3] or args["deck"] or ""
if not cardName then
return "错误:未指定卡牌名称"
end
-- 优先从角色专属数据加载
local cardData = nil
local baseCardData = nil
if characterName ~= "" then
local charDataModule = "模块:卡牌数据/" .. characterName
local success, charData = pcall(mw.loadData, charDataModule)
if success then
-- 根据卡组类型查找
if deckType ~= "" and charData[deckType] then
cardData = charData[deckType][cardName]
else
-- 遍历所有卡组查找
for _, deck in pairs(charData) do
if type(deck) == "table" and deck[cardName] then
cardData = deck[cardName]
break
end
end
end
end
-- 如果是变体卡牌,获取基础卡牌数据
if cardData then
baseCardData = getBaseCardData(cardName)
end
end
-- 如果没有找到专属数据,从通用数据加载
if not cardData then
local data = mw.loadData("模块:卡牌数据")
cardData = data[cardName]
end
if not cardData then
return string.format("错误:找不到卡牌 '%s' 的数据", cardName)
end
return buildCardHTML(frame, cardName, cardData, baseCardData, characterName)
end
-- 批量显示卡牌
function p.displayCards(frame)
local args = frame.args
local cardList = args[1] or args["cards"] or ""
local characterName = args[2] or args["character"] or ""
local columns = tonumber(args[3] or args["columns"]) or 5
if cardList == "" then
return "错误:未指定卡牌列表"
end
-- 解析卡牌列表
local cards = {}
for card in mw.text.gsplit(cardList, ",") do
card = mw.text.trim(card)
if card ~= "" then
table.insert(cards, card)
end
end
if #cards == 0 then
return "错误:卡牌列表为空"
end
-- 构建HTML
local html = {}
table.insert(html, '<div style="display: flex; flex-wrap: wrap; gap: 10px;">')
for i, cardName in ipairs(cards) do
-- 获取卡牌数据
local cardData = nil
local baseCardData = nil
if characterName ~= "" then
local charDataModule = "模块:卡牌数据/" .. characterName
local success, charData = pcall(mw.loadData, charDataModule)
if success then
for _, deck in pairs(charData) do
if type(deck) == "table" and deck[cardName] then
cardData = deck[cardName]
break
end
end
end
if cardData then
baseCardData = getBaseCardData(cardName)
end
end
if not cardData then
local data = mw.loadData("模块:卡牌数据")
cardData = data[cardName]
end
if cardData then
table.insert(html, buildCardHTML(frame, cardName, cardData, baseCardData, characterName))
else
table.insert(html, string.format('<div style="color: red;">找不到卡牌: %s</div>', cardName))
end
-- 添加换行
if i % columns == 0 and i < #cards then
table.insert(html, '</div><div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">')
end
end
table.insert(html, '</div>')
return table.concat(html)
end
-- 显示角色的所有卡牌
function p.displayCharacterCards(frame)
local args = frame.args
local characterName = args[1] or args["character"]
local deckType = args[2] or args["deck"] or "all"
local columns = tonumber(args[3] or args["columns"]) or 5
if not characterName then
return "错误:未指定角色名称"
end
local charDataModule = "模块:卡牌数据/" .. characterName
local success, charData = pcall(mw.loadData, charDataModule)
if not success then
return string.format("错误:找不到角色 '%s' 的卡牌数据", characterName)
end
-- 收集要显示的卡牌
local cardsToDisplay = {}
if deckType == "all" then
-- 显示所有卡组
local deckOrder = {"起始卡牌", "基础卡牌", "高级卡牌", "其他卡牌"}
for _, dName in ipairs(deckOrder) do
if charData[dName] then
for cardName, cardData in pairs(charData[dName]) do
table.insert(cardsToDisplay, {name = cardName, data = cardData, deck = dName})
end
end
end
else
-- 显示特定卡组
if charData[deckType] then
for cardName, cardData in pairs(charData[deckType]) do
table.insert(cardsToDisplay, {name = cardName, data = cardData, deck = deckType})
end
else
return string.format("错误:角色 '%s' 没有 '%s' 卡组", characterName, deckType)
end
end
-- 排序(可选:按稀有度、AP值等排序)
table.sort(cardsToDisplay, function(a, b)
return a.name < b.name
end)
-- 构建HTML
local html = {}
table.insert(html, '<div style="display: flex; flex-wrap: wrap; gap: 10px;">')
for i, card in ipairs(cardsToDisplay) do
local baseCardData = getBaseCardData(card.name)
table.insert(html, buildCardHTML(frame, card.name, card.data, baseCardData, characterName))
if i % columns == 0 and i < #cardsToDisplay then
table.insert(html, '</div><div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">')
end
end
table.insert(html, '</div>')
return table.concat(html)
end
-- 搜索卡牌
function p.searchCards(frame)
local args = frame.args
local searchTerm = args[1] or args["search"] or ""
local searchType = args[2] or args["type"] or "name" -- name, description, mechanism
local columns = tonumber(args[3] or args["columns"]) or 5
if searchTerm == "" then
return "错误:未指定搜索词"
end
searchTerm = mw.ustring.lower(searchTerm)
local data = mw.loadData("模块:卡牌数据")
local results = {}
for cardName, cardData in pairs(data) do
local match = false
if searchType == "name" then
match = mw.ustring.find(mw.ustring.lower(cardName), searchTerm) ~= nil
elseif searchType == "description" then
local desc = safeGet(cardData, "描述", "")
match = mw.ustring.find(mw.ustring.lower(desc), searchTerm) ~= nil
elseif searchType == "mechanism" then
local mech = safeGet(cardData, "机制", "")
match = mw.ustring.find(mw.ustring.lower(mech), searchTerm) ~= nil
elseif searchType == "all" then
local nameMatch = mw.ustring.find(mw.ustring.lower(cardName), searchTerm) ~= nil
local descMatch = mw.ustring.find(mw.ustring.lower(safeGet(cardData, "描述", "")), searchTerm) ~= nil
local mechMatch = mw.ustring.find(mw.ustring.lower(safeGet(cardData, "机制", "")), searchTerm) ~= nil
match = nameMatch or descMatch or mechMatch
end
if match then
table.insert(results, {name = cardName, data = cardData})
end
end
if #results == 0 then
return "没有找到符合条件的卡牌"
end
-- 排序
table.sort(results, function(a, b)
return a.name < b.name
end)
-- 构建HTML
local html = {}
table.insert(html, string.format('<div>找到 %d 张卡牌:</div>', #results))
table.insert(html, '<div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">')
for i, result in ipairs(results) do
table.insert(html, buildCardHTML(frame, result.name, result.data, nil, ""))
if i % columns == 0 and i < #results then
table.insert(html, '</div><div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;">')
end
end
table.insert(html, '</div>')
return table.concat(html)
end
return p