模块

模块:卡牌

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献2025年10月1日 (三) 21:15的版本

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

local p = {}

-- 颜色映射表(保持不变但添加快速查找)
local COLOR_MAP = {
    ["白"] = { bgColor = "rgba(249, 249, 249, 0.5)", textColor = "white" },
    ["蓝"] = { bgColor = "rgba(115, 236, 254, 0.5)", textColor = "#7de5ff" },
    ["橙"] = { bgColor = "rgba(254, 199, 109, 0.5)", textColor = "#ffee75" },
    ["彩"] = { bgColor = "rgba(201, 88, 241, 0.5)", textColor = "#eba2fc" }
}
local DEFAULT_COLOR = COLOR_MAP["白"]

-- 优化:减少函数调用,内联简单操作
local function safeGet(data, field, default)
    local value = data and data[field]
    return (value ~= nil and value ~= "") and value or default
end

-- 优化:添加缓存避免重复处理
local descriptionCache = {}
local function processDescription(frame, description)
    if not description or description == "" then
        return ""
    end
    
    -- 检查缓存
    if descriptionCache[description] then
        return descriptionCache[description]
    end
    
    local success, result = pcall(function()
        return frame:preprocess(description)
    end)
    
    local processed = success and result or description
    descriptionCache[description] = processed
    return processed
end

-- 优化:预构建HTML片段,减少字符串操作
local function buildImageTag(filename, size, extraStyle)
    extraStyle = extraStyle or ""
    return string.format('[[File:%s|%spx|link=]]', filename, size)
end

-- 优化:合并样式计算,减少重复代码
local function calculateCardStyle(cardData, baseCardData)
    local color = safeGet(cardData, "稀有度", "白")
    local colorStyle = COLOR_MAP[color] or DEFAULT_COLOR
    local ap = safeGet(cardData, "AP", "")
    local originalAP = baseCardData and safeGet(baseCardData, "AP", ap) or ap
    
    -- AP发光颜色计算
    local apGlowColor = "#4a90e2"
    if baseCardData and ap ~= originalAP and ap ~= "X" then
        local apNum, origNum = tonumber(ap), tonumber(originalAP)
        if apNum and origNum then
            apGlowColor = apNum < origNum and "#51f651" or "#f65151"
        end
    end
    
    -- 机制变化检测
    local mechanism = safeGet(cardData, "机制", "")
    local originalMechanism = baseCardData and safeGet(baseCardData, "机制", "") or mechanism
    mechanism = mechanism == "无" and "" or mechanism
    originalMechanism = originalMechanism == "无" and "" or originalMechanism
    
    return {
        color = color,
        colorStyle = colorStyle,
        attribute = safeGet(cardData, "属性", "虚无"),
        cardType = safeGet(cardData, "类型", ""),
        art = safeGet(cardData, "art", ""),
        ap = ap,
        apGlowColor = apGlowColor,
        mechanism = mechanism,
        mechanismChanged = baseCardData and mechanism ~= "" and originalMechanism == ""
    }
end

-- 优化:使用局部变量减少表查找
local format = string.format
local insert = table.insert
local concat = table.concat

-- 优化:模板化HTML生成
-- 修复后的 buildCardHTML 函数中的HTML生成部分
local function buildCardHTML(frame, cardName, cardData, baseCardData, characterName)
    if not cardData then
        return format('<div style="color:red;">找不到卡牌: %s</div>', cardName or "未指定")
    end
    
    local style = calculateCardStyle(cardData, baseCardData)
    local description = processDescription(frame, safeGet(cardData, "描述", ""))
    
    -- 数据属性
    local dataAttrs = format(
        'data-card-name="%s" data-character="%s" data-deck-type="%s" data-derived-cards="%s"',
        cardName or "", 
        characterName or "", 
        safeGet(cardData, "卡组", ""), 
        safeGet(cardData, "衍生卡牌", "")
    )
    
    -- 机制HTML(优化:提前判断避免无用字符串生成)
    local mechanismHTML = ""
    if style.mechanism ~= "" then
        local mechanismColor = style.mechanismChanged and "#b5f651" or "#f6c478"
        mechanismHTML = format(
            '<div style="color: %s; margin-bottom: 4px;">[%s]</div>', 
            mechanismColor, 
            style.mechanism:gsub("、", "/")
        )
    end
    
    -- AP显示(复用格式字符串)
    local apShadow = format(
        "0 0 10px %s,0 0 20px %s, 0 0 30px %s, 0 0 40px %s",
        style.apGlowColor, style.apGlowColor, style.apGlowColor, style.apGlowColor
    )
    
    local html = {}
    
    -- 容器开始
    insert(html, 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
        -- 状态异常布局(使用 [=[ ]=] 避免冲突)
        insert(html, format([=[
<div style="position: absolute; top: 5px; left: 5px;">%s</div>
<div style="position: absolute; top: 0px; left: 0px;">%s</div>
<div style="position: absolute; left: 24px; top: 4px; color: white; font-weight: bold; text-shadow: %s; font-size: 32px;">%s</div>
<div style="position: absolute; left: 25px; top: 34px; color: white; font-weight: bold; text-shadow: %s;">—</div>
<div style="position: absolute; left: 50px; top: 13px; color: %s; font-size:16px">%s</div>
<div style="position: absolute; left: 48px; top: 30px;">%s</div>
<div style="position: absolute; left: 68px; top: 33px; color: white; font-size:14px">%s</div>
<div style="position: absolute; left: 0px; bottom: 0px;">[[File:card_顶层蒙版.png|168px|link=]]</div>]=],
            buildImageTag(style.art, 150),
            buildImageTag(format("card_状态异常_%s.png", style.attribute), 159),
            apShadow, style.ap,
            apShadow,
            style.colorStyle.textColor, cardName,
            buildImageTag(format("icon_card_%s.png", style.cardType), 18),
            style.cardType
        ))
    else
        -- 普通卡牌布局(使用 [=[ ]=] 避免冲突)
        insert(html, format([=[
<div style="position: absolute; top: 1px; left: 12px;">%s</div>
<div style="position: absolute; top: 1px; left: 12px;">[[File:card_黑色蒙版.png|151px|link=]]</div>
<div style="position: absolute; top: 21px; left: 10px; width: 148px; height: 8px; background-color: %s;"></div>
<div style="position: absolute; left: 24px; top: 4px; color: white; font-weight: bold; text-shadow: %s; font-size: 32px;">%s</div>
<div style="position: absolute; left: 25px; top: 34px; color: white; font-weight: bold; text-shadow: %s;">—</div>
<div style="position: absolute; left: 50px; top: 13px; color: %s; font-size:16px">%s</div>
<div style="position: absolute; left: 48px; top: 30px;">%s</div>
<div style="position: absolute; left: 68px; top: 33px; color: white; font-size:14px">%s</div>
<div style="position: absolute; top: 0px; left: 1px;">%s</div>
<div style="position: absolute; top: 10px; left: 0px;">%s</div>
<div style="position: absolute; top: 2px; right: 4px;">%s</div>
<div style="position: absolute; left: 0px; bottom: 0px;">[[File:card_顶层蒙版.png|168px|link=]]</div>]=],
            buildImageTag(style.art, 151),
            style.colorStyle.bgColor,
            apShadow, style.ap,
            apShadow,
            style.colorStyle.textColor, cardName,
            buildImageTag(format("icon_card_%s.png", style.cardType), 18),
            style.cardType,
            buildImageTag(format("card_属性边框_%s.png", style.attribute), 160),
            buildImageTag(format("card_稀有度_%s.png", style.color), 22),
            buildImageTag(format("card_稀有度_边框_%s.png", style.color), 11)
        ))
    end
    
    -- 描述区域(使用 [=[ ]=] 避免冲突)
    insert(html, format([=[
<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;">
<div style="line-height: 13px;">%s%s</div>
</div>
</div>]=], mechanismHTML, description))
    
    return concat(html)
end

-- 批量生成卡牌HTML
local function displayAllDefaultCards(frame, characterModule, characterName)
    local cards = characterModule.card or {}
    local cardOrder = characterModule.cardOrder or {}
    
    if #cardOrder == 0 then
        return "<div>没有找到卡牌顺序信息</div>"
    end
    
    local htmlParts = {}
    for _, name in ipairs(cardOrder) do
        local cardArray = cards[name]
        if cardArray and #cardArray > 0 then
            insert(htmlParts, buildCardHTML(frame, name, cardArray[1], nil, characterName))
        end
    end
    
    return concat(htmlParts)
end

local function handleSpecificCard(frame, cardName, cardData, deckFilter, cardIndex, characterName)
    local baseCard = cardData[1]
    
    if deckFilter == "" then
        return buildCardHTML(frame, cardName, baseCard, nil, characterName)
    end
    
    -- 筛选卡组
    local filteredCards = {}
    for _, card in ipairs(cardData) do
        if card["卡组"] == deckFilter then
            insert(filteredCards, card)
        end
    end
    
    if #filteredCards == 0 then
        return format('<div style="color:orange;">找不到卡组 "%s" 的卡牌: %s</div>', deckFilter, cardName)
    end
    
    -- 合并数据函数
    local function mergeData(base, override)
        local result = {}
        for k, v in pairs(base) do result[k] = v end
        for k, v in pairs(override) do result[k] = v end
        return result
    end
    
    -- 特定索引
    if cardIndex > 0 and cardIndex <= #filteredCards then
        return buildCardHTML(frame, cardName, mergeData(baseCard, filteredCards[cardIndex]), baseCard, characterName)
    end
    
    -- 所有卡牌
    local htmlParts = {}
    for _, card in ipairs(filteredCards) do
        insert(htmlParts, buildCardHTML(frame, cardName, mergeData(baseCard, card), baseCard, characterName))
    end
    
    return concat(htmlParts)
end

-- 主函数
function p.main(frame)
    local args = frame.args
    local characterName = args[1] or ""
    local cardName = args[2] or ""
    local deckFilter = args[3] or ""
    local cardIndex = tonumber(args[4]) or 0
    
    -- 失败
    if characterName == "" then
        return '<div style="color:red;">请指定战斗员名称</div>'
    end
    
    -- 加载模块
    local success, characterModule = pcall(require, "模块:卡牌/" .. characterName)
    if not success or not characterModule then
        return format('<div style="color:red;">找不到战斗员: %s</div>', characterName)
    end
    
    -- 显示所有卡牌
    if cardName == "" then
        return displayAllDefaultCards(frame, characterModule, characterName)
    end
    
    -- 显示特定卡牌
    local cardData = (characterModule.card or {})[cardName]
    if not cardData then
        return format('<div style="color:red;">找不到卡牌: %s</div>', cardName)
    end
    
    return handleSpecificCard(frame, cardName, cardData, deckFilter, cardIndex, characterName)
end

-- 元表设置
setmetatable(p, {
    __index = function(t, characterName)
        return function(frame)
            frame.args = {
                [1] = characterName,
                [2] = frame.args[1] or "",
                [3] = frame.args[2] or "",
                [4] = frame.args[3] or 0
            }
            return p.main(frame)
        end
    end
})

return p