模块

卡牌:修订间差异

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献
无编辑摘要
律Rhyme留言 | 贡献
无编辑摘要
标签已被回退
第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 = "rgba(249, 249, 249, 0.5)", textColor = "white" },
     ["白"] = { bgColor = "#ffffff", textColor = "#333333" },
     ["蓝"] = { bgColor = "rgba(115, 236, 254, 0.5)", textColor = "#7de5ff" },
     ["蓝"] = { bgColor = "#4a90e2", textColor = "white" },
     [""] = { bgColor = "rgba(254, 199, 109, 0.5)", textColor = "#ffee75" },
     [""] = { bgColor = "#9b59b6", textColor = "white" },
     [""] = { bgColor = "rgba(201, 88, 241, 0.5)", textColor = "#eba2fc" }
     [""] = { bgColor = "#f1c40f", textColor = "#333333" }
}
}


-- 安全获取数据字段
-- 计算名称缩放比例
local function safeGet(data, field, default)
local function calculateNameScale(name)
     return data and data[field] or default
     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 description
         return ""
     end
     end
      
      
     local success, result = pcall(function()
     -- 处理换行符
         return frame:preprocess(description)
    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 success and result or description
    -- 处理高亮文本:<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 ~= "" and style.mechanism ~= "无" then
     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 mergeCardData(baseData, overrideData)
local function getBaseCardData(cardName)
     local result = {}
     local data = mw.loadData("模块:卡牌数据")
    for k, v in pairs(baseData or {}) do
     return data[cardName]
        result[k] = v
     end
    for k, v in pairs(overrideData or {}) do
        result[k] = v
    end
    return result
end
end


-- 获取并显示所有默认卡牌
-- 主函数:显示卡牌
local function displayAllDefaultCards(frame, characterModule, characterName)
function p.displayCard(frame)
     local cards = characterModule.card or {}
     local args = frame.args
     local cardOrder = characterModule.cardOrder or {}
    local cardName = args[1] or args["name"]
     local displayedCards = {}
     local characterName = args[2] or args["character"] or ""
     local deckType = args[3] or args["deck"] or ""
   
    if not cardName then
        return "错误:未指定卡牌名称"
    end
      
      
     -- 按cardOrder顺序遍历
     -- 优先从角色专属数据加载
     for orderIndex, name in ipairs(cardOrder) do
     local cardData = nil
         local cardArray = cards[name]
    local baseCardData = nil
         if cardArray and #cardArray > 0 then
   
             -- 只显示第一张卡牌
    if characterName ~= "" then
             table.insert(displayedCards, {
        local charDataModule = "模块:卡牌数据/" .. characterName
                 name = name,
         local success, charData = pcall(mw.loadData, charDataModule)
                data = cardArray[1],
         if success then
                 order = orderIndex
             -- 根据卡组类型查找
             })
             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
      
      
     -- 生成HTML
     -- 如果没有找到专属数据,从通用数据加载
     local htmlParts = {}
     if not cardData then
     for i, cardInfo in ipairs(displayedCards) do
        local data = mw.loadData("模块:卡牌数据")
         table.insert(htmlParts, buildCardHTML(frame, cardInfo.name, cardInfo.data, nil, characterName))
        cardData = data[cardName]
    end
   
     if not cardData then
         return string.format("错误:找不到卡牌 '%s' 的数据", cardName)
     end
     end
      
      
     return table.concat(htmlParts)
     return buildCardHTML(frame, cardName, cardData, baseCardData, characterName)
end
end


-- 处理特定卡牌请求
-- 批量显示卡牌
local function handleSpecificCard(frame, cardName, cardData, deckFilter, cardIndex, characterName)
function p.displayCards(frame)
     local baseCard = cardData[1]
     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 deckFilter == "" then
         return "错误:未指定卡牌列表"
         return buildCardHTML(frame, cardName, baseCard, nil, characterName)
     end
     end
      
      
     -- 筛选指定卡组的卡牌
     -- 解析卡牌列表
     local filteredCards = {}
     local cards = {}
     for _, card in ipairs(cardData) do
     for card in mw.text.gsplit(cardList, ",") do
         if card["卡组"] == deckFilter then
        card = mw.text.trim(card)
             table.insert(filteredCards, card)
         if card ~= "" then
             table.insert(cards, card)
         end
         end
     end
     end
      
      
     if #filteredCards == 0 then
     if #cards == 0 then
         return string.format("找不到卡组 '%s' 的卡牌: %s", deckFilter, cardName)
         return "错误:卡牌列表为空"
     end
     end
      
      
     -- 如果指定了索引,返回特定卡牌
     -- 构建HTML
     if cardIndex > 0 and cardIndex <= #filteredCards then
     local html = {}
         local mergedCard = mergeCardData(baseCard, filteredCards[cardIndex])
    table.insert(html, '<div style="display: flex; flex-wrap: wrap; gap: 10px;">')
         return buildCardHTML(frame, cardName, mergedCard, baseCard, characterName)
   
    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>')
    local htmlParts = {}
    for i, card in ipairs(filteredCards) do
        local mergedCard = mergeCardData(baseCard, card)
        table.insert(htmlParts, buildCardHTML(frame, cardName, mergedCard, baseCard, characterName))
    end
      
      
     return table.concat(htmlParts)
     return table.concat(html)
end
end


-- 主函数
-- 显示角色的所有卡牌
function p.main(frame)
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 cardName = args[2] or ""
     local deckType = args[2] or args["deck"] or "all"
    local deckFilter = args[3] or ""
     local columns = tonumber(args[3] or args["columns"]) or 5
     local cardIndex = tonumber(args[4]) or 0
      
      
    -- 加载战斗员模块
     if not characterName then
    local success, characterModule = pcall(require, "模块:卡牌/" .. characterName)
         return "错误:未指定角色名称"
     if not success or not characterModule then
         return string.format("找不到战斗员卡牌数据模块: 模块:卡牌/%s", characterName)
     end
     end
      
      
     -- 如果没有指定卡牌名,显示所有默认卡牌
    local charDataModule = "模块:卡牌数据/" .. characterName
     if cardName == "" then
    local success, charData = pcall(mw.loadData, charDataModule)
         return displayAllDefaultCards(frame, characterModule, characterName)
   
    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 cardData = (characterModule.card or {})[cardName]
     table.sort(cardsToDisplay, function(a, b)
    if not cardData then
        return a.name < b.name
        return string.format("找不到卡牌: %s", cardName)
    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 handleSpecificCard(frame, cardName, cardData, deckFilter, cardIndex, characterName)
    table.insert(html, '</div>')
   
     return table.concat(html)
end
end


-- 元表设置,支持直接调用战斗员名称作为方法
-- 搜索卡牌
setmetatable(p, {
function p.searchCards(frame)
    __index = function(t, characterName)
    local args = frame.args
        return function(frame)
    local searchTerm = args[1] or args["search"] or ""
            frame.args = {
    local searchType = args[2] or args["type"] or "name" -- name, description, mechanism
                [1] = characterName,
    local columns = tonumber(args[3] or args["columns"]) or 5
                [2] = frame.args[1] or "",
   
                [3] = frame.args[2] or "",
    if searchTerm == "" then
                [4] = frame.args[3] or 0
        return "错误:未指定搜索词"
            }
    end
             return p.main(frame)
   
    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