卡牌/display:修订间差异
来自卡厄思梦境WIKI
< 模块:卡牌
< 模块:卡牌
创建页面,内容为“local p = {} local function escFile(s) s = tostring(s or '') s = mw.text.trim(s) s = s:gsub('[%[%]%{%}%|]', '') return s end local function escText(s) return mw.text.encode(tostring(s or '')) end local function expandWikitext(s) if s == nil or s == '' then return '' end local ok, frame = pcall(mw.getCurrentFrame) if not ok or not frame then return tostring(s) end return frame:preprocess(tostring(s))…” |
无编辑摘要 标签:已被回退 |
||
| 第12行: | 第12行: | ||
end | end | ||
-- 确保模板与解析函数在内容中被展开 | |||
local function expandWikitext(s) | local function expandWikitext(s) | ||
if s == nil or s == '' then | if s == nil or s == '' then | ||
| 第23行: | 第24行: | ||
end | end | ||
-- 稀有度统一 | |||
local function normalizeRarity(r) | local function normalizeRarity(r) | ||
local s = tostring(r or ''):gsub('%s+', '') | local s = tostring(r or ''):gsub('%s+', '') | ||
| 第28行: | 第30行: | ||
if s == '白' then return '白' end | if s == '白' then return '白' end | ||
if s == '蓝' then return '蓝' end | if s == '蓝' then return '蓝' end | ||
if s == '橙' then return '橙' end | if s == '橙' or s == '橙色' then return '橙' end | ||
if s == '彩' then return '彩' end | if s == '彩' or s == '彩虹' then return '彩' end | ||
return nil | return nil | ||
end | end | ||
-- 稀有度对应的标题背景条颜色 | |||
local function rarityBarColor(r) | local function rarityBarColor(r) | ||
local key = normalizeRarity(r) or '蓝' | local key = normalizeRarity(r) or '蓝' | ||
local map = { | local map = { | ||
['白'] = 'rgba(239, 237, 237, 0. | ['白'] = 'rgba(239, 237, 237, 0.6)', | ||
['蓝'] = 'rgba(119, 236, 254, 0. | ['蓝'] = 'rgba(119, 236, 254, 0.6)', | ||
['橙'] = 'rgba(255, 200, 110, 0. | ['橙'] = 'rgba(255, 200, 110, 0.6)', | ||
['彩'] = 'rgba(180, 125, 242, 0. | ['彩'] = 'rgba(180, 125, 242, 0.6)', | ||
} | } | ||
return map[key] or map['蓝'] | return map[key] or map['蓝'] | ||
end | end | ||
-- 稀有度对应的卡牌名称颜色 | |||
local function rarityNameColor(r) | local function rarityNameColor(r) | ||
local key = normalizeRarity(r) or '蓝' | local key = normalizeRarity(r) or '蓝' | ||
local map = { | local map = { | ||
['白'] = '#cccccc', | ['白'] = '#cccccc', | ||
['蓝'] = '# | ['蓝'] = '#73ecfe', | ||
['橙'] = '# | ['橙'] = '#ffc86e', | ||
['彩'] = '# | ['彩'] = '#a37df7', | ||
} | } | ||
return map[key] or map['蓝'] | return map[key] or map['蓝'] | ||
end | end | ||
local function | -- 根据外框宽度返回标题背景条几何尺寸 | ||
local function getBarGeom(width) | |||
width = tonumber(width) or 160 | |||
if width <= 160 then | |||
return { top = 22, left = 5, w = 150, h = 8 } | |||
elseif width >= 320 then | |||
return { top = 44, left = 5, w = 305, h = 16 } | |||
if | |||
return | |||
elseif | |||
return | |||
else | else | ||
return | return { top = 33, left = 5, w = 230, h = 12 } | ||
end | end | ||
end | end | ||
local function | -- 渲染标题背景条层级,art 之上,其余元素之下 | ||
local function renderRarityBar(info, width) | |||
local | local g = getBarGeom(width) | ||
local color = ( | local color = rarityBarColor(info.rarity) | ||
return string.format( | return string.format( | ||
'<div style="position:absolute;z-index: | '<div style="position:absolute;z-index:1;top:%dpx;left:%dpx;width:%dpx;height:%dpx;background-color:%s"></div>', | ||
top, left, | g.top, g.left, g.w, g.h, color | ||
) | ) | ||
end | end | ||
-- 仅渲染立绘层(最低层) | |||
local function renderArtLayer(info, width) | |||
return '<div style="position: absolute; top: 0; left: 0; z-index:0;">[[File:' .. escFile(info.art) .. '|' .. width .. 'px|link=]]</div>' | |||
end | |||
-- 渲染覆盖层(横条之上) | |||
local function renderOverlayLayers(info, width) | |||
local t = {} | |||
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:2;">[[File:card_黑色蒙版.png|' .. width .. 'px|link=]]</div>') | |||
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:3;">[[File:card_ego_' .. escFile(info.ego) .. '.png|' .. width .. 'px|link=]]</div>') | |||
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:4;">[[File:card_rarity_' .. escFile(info.rarity) .. '.png|' .. width .. 'px|link=]]</div>') | |||
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:5;">[[File:card_顶层蒙版.png|' .. width .. 'px|link=]]</div>') | |||
return table.concat(t, '') | |||
end | |||
-- 将 tokens 渲染为可调用模板的短标识 | |||
local function dictSpanFromTokens(tokens, color) | local function dictSpanFromTokens(tokens, color) | ||
if type(tokens) ~= 'table' or #tokens == 0 then | if type(tokens) ~= 'table' or #tokens == 0 then | ||
| 第107行: | 第119行: | ||
end | end | ||
local function generateUniqueId(cardInfo) | |||
local timestamp = os.time() | |||
local random = math.random(1000, 9999) | |||
local base = (cardInfo.moduleName or '') .. '-' .. (cardInfo.cardName or '') .. (cardInfo.variantSuffix or '') | |||
local safeName = mw.uri.anchorEncode(base):gsub("%%", "-") | |||
return "card-" .. safeName .. "-" .. timestamp .. "-" .. random | |||
end | |||
-- AP 发光样式 | |||
local function apTextShadow(style) | |||
if style == 'red' then | |||
return 'text-shadow:0 0 10px #7a0e0e,0 0 20px #7a0e0e,0 0 30px #ff3030,0 0 40px #ff3030,0 0 50px #ff3030,0 0 60px #ff3030,0 0 70px #ff3030;' | |||
elseif style == 'green' then | |||
return 'text-shadow:0 0 10px #0e6f36,0 0 20px #0e6f36,0 0 30px #29ff85,0 0 40px #29ff85,0 0 50px #29ff85,0 0 60px #29ff85,0 0 70px #29ff85;' | |||
elseif style == 'blue' then | |||
return 'text-shadow:0 0 10px #132cbf,0 0 20px #132cbf,0 0 30px #4169ff,0 0 40px #4169ff,0 0 50px #4169ff,0 0 60px #4169ff,0 0 70px #4169ff;' | |||
else | |||
return '' -- gray: no glow | |||
end | |||
end | |||
local function renderApDiv(content, top, left, fontSize, style) | |||
content = tostring(content or '') | |||
local shadow = apTextShadow(style) | |||
local color = (style == 'gray') and '#cfcfcf' or 'white' | |||
return string.format( | |||
'<div style="position:absolute;z-index:10;top:%s;left:%s;font-family:number;font-size:%s;color:%s;%s">%s</div>', | |||
top, left, fontSize, color, shadow, content | |||
) | |||
end | |||
-- 单个机制卡片(固定 250px 宽度) | |||
local function renderMechanismCard(title, desc) | local function renderMechanismCard(title, desc) | ||
title = escText(title or '') | title = escText(title or '') | ||
| 第113行: | 第157行: | ||
d = '暂无详细说明' | d = '暂无详细说明' | ||
end | end | ||
-- 展开词典说明中的模板 | |||
local body = expandWikitext(d) | local body = expandWikitext(d) | ||
local dhtml = {} | local dhtml = {} | ||
table.insert(dhtml, '<div class="mechanism-card" style="width: 250px;">') | table.insert(dhtml, '<div class="mechanism-card" style="width: 250px;">') | ||
table.insert(dhtml, '<div style="width: | table.insert(dhtml, '<div style="width: 266px; height: 2px; background-color: #f2ba02;"></div>') | ||
table.insert(dhtml, '<div style="width: 100%; background-color: #343434; color: white; padding: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); line-height: 1.6;">') | table.insert(dhtml, '<div style="width: 100%; background-color: #343434; color: white; padding: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); line-height: 1.6;">') | ||
table.insert(dhtml, '<div style="font-weight: bold;">' .. title .. '</div>') | table.insert(dhtml, '<div style="font-weight: bold;">' .. title .. '</div>') | ||
| 第126行: | 第171行: | ||
end | end | ||
-- 右侧词典说明容器 | |||
local function renderDictMechanisms(dictEntries) | local function renderDictMechanisms(dictEntries) | ||
if type(dictEntries) ~= 'table' or #dictEntries == 0 then return '' end | if type(dictEntries) ~= 'table' or #dictEntries == 0 then return '' end | ||
local html = {} | local html = {} | ||
table.insert(html, '<div class="dict-mechanisms" style="display:flex;flex-direction:column;align-items:flex-start;gap | -- 固定宽度 250px | ||
table.insert(html, '<div class="dict-mechanisms" style="display:flex;flex-direction:column;align-items:flex-start;gap:10px;margin-left:10px;width:250px;flex:0 0 250px;overflow:visible;">') | |||
for _, entry in ipairs(dictEntries) do | for _, entry in ipairs(dictEntries) do | ||
table.insert(html, renderMechanismCard(entry.key, entry.desc)) | table.insert(html, renderMechanismCard(entry.key, entry.desc)) | ||
| 第138行: | 第185行: | ||
end | end | ||
function p.render(cardInfo, subCardInfo) | |||
local | local dictDisplay = dictSpanFromTokens(cardInfo.dictTokens, cardInfo.dictColor) | ||
local cardId = generateUniqueId(cardInfo) | |||
local html = {} | local html = {} | ||
-- | -- 渲染小卡片 | ||
table.insert(html, | table.insert(html, p.renderSmallCard(cardInfo, cardId, dictDisplay)) | ||
-- 渲染模态框(单独输出) | |||
-- | table.insert(html, p.renderModal(cardInfo, cardId, dictDisplay, subCardInfo)) | ||
table.insert(html, | |||
return table.concat(html, '') | return table.concat(html, '') | ||
end | end | ||
function p.renderSmallCard(cardInfo, cardId, dictDisplay) | |||
local html = {} | local html = {} | ||
local apDisplay = cardInfo.apText or '' | |||
local nameColor = rarityNameColor(cardInfo.rarity) | |||
local | |||
table.insert(html, '<div class="card-small-wrapper" data-card-id="' .. escText(cardId) .. '" style="display:inline-block;vertical-align:top;position:relative;width:160px;height:230px;overflow:hidden;cursor:pointer;">') | |||
table.insert(html, '<div | |||
table.insert(html, renderArtLayer(cardInfo, 160)) | |||
table.insert(html, renderRarityBar(cardInfo, 160)) | |||
table.insert(html, renderOverlayLayers(cardInfo, 160)) | |||
table.insert(html, | |||
table.insert(html, | |||
table.insert(html, | |||
table.insert(html, renderApDiv(apDisplay, '5px', '25px', '36px', cardInfo.apStyle or 'blue')) | |||
table.insert(html, renderApDiv('—', '23px', '23px', '32px', cardInfo.apStyle or 'blue')) | |||
table.insert(html, ' | |||
table.insert(html, ' | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:12px;left:50px;color:' .. nameColor .. ';font-size:18px">' .. escText(cardInfo.displayTitleShort or cardInfo.displayTitle or cardInfo.cardName) .. '</div>') | |||
table.insert(html, '<div style="position:absolute | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:33px;left:49px;">[[File:card_type_' .. escFile(cardInfo.cardType) .. '.png|18px|link=]]</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:32px;left:67px;font-size:14px;color:white">' .. escText(cardInfo.cardType) .. '</div>') | |||
table.insert(html, | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:110px;left:20px;width:130px;height:115px;color:white;text-align:center;font-size:13px;line-height:15px;">') | |||
local smallContent = expandWikitext((dictDisplay or '') .. (cardInfo.desc or '')) | |||
table.insert(html, '<div class="scroll-text"><div class="scroll-text-content">' .. smallContent .. '</div></div>') | |||
table.insert(html, '</div>') | |||
table.insert(html, '</div>') | |||
return table.concat(html, '') | return table.concat(html, '') | ||
end | end | ||
function p. | -- 新增:独立的模态框渲染函数 | ||
function p.renderModal(cardInfo, cardId, dictDisplay, subCardInfos) | |||
local html = {} | local html = {} | ||
local apDisplay = cardInfo.apText or '' | local apDisplay = cardInfo.apText or '' | ||
local nameColor = rarityNameColor(cardInfo.rarity) | local nameColor = rarityNameColor(cardInfo.rarity) | ||
table.insert(html, '<div id="' .. escText(cardId) .. '-modal" class="card-modal" style="display:none;position:fixed;z-index:9999;left:0;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.8);">') | table.insert(html, '<div id="' .. escText(cardId) .. '-modal" class="card-modal" style="display:none;position:fixed;z-index:9999;left:0;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.8);">') | ||
| 第316行: | 第238行: | ||
table.insert(html, '<div class="card-modal-inner" style="height:100vh;display:flex;align-items:center;justify-content:center;">') | table.insert(html, '<div class="card-modal-inner" style="height:100vh;display:flex;align-items:center;justify-content:center;">') | ||
table.insert(html, '<div class="original-card-view" style="display:flex;align-items:flex-start;justify-content:center;gap | table.insert(html, '<div class="original-card-view" style="display:flex;align-items:flex-start;justify-content:center;gap:10px;flex-wrap:wrap;">') | ||
-- | -- 左侧:衍生卡牌 | ||
if subCardInfos and #subCardInfos > 0 then | if subCardInfos and #subCardInfos > 0 then | ||
table.insert(html, '<div class="ocv-col-left" style="display:flex;flex-direction:column;align-items:center; | table.insert(html, '<div class="ocv-col-left" style="display:flex;flex-direction:column;align-items:center;gap:10px;max-width:240px;">') | ||
table.insert(html, p.renderSubCard(subCardInfos[1])) | table.insert(html, p.renderSubCard(subCardInfos[1])) | ||
if #subCardInfos > 1 then | if #subCardInfos > 1 then | ||
| 第328行: | 第250行: | ||
end | end | ||
-- | -- 中间:放大卡片 | ||
table.insert(html, '<div class="ocv-col-center" style="display:flex;flex-direction:column;align-items:center;gap:10px;max-width:320px;">') | |||
table.insert(html, '<div style="display:inline-block;vertical-align:top;position:relative;width:320px;height:460px;overflow:hidden;">') | |||
table.insert(html, renderArtLayer(cardInfo, 320)) | |||
table.insert(html, renderRarityBar(cardInfo, 320)) | |||
table.insert(html, renderOverlayLayers(cardInfo, 320)) | |||
table.insert(html, renderApDiv(apDisplay, '18px', '45px', '58px', cardInfo.apStyle or 'blue')) | |||
table.insert(html, renderApDiv('__', '48px', '40px', '32px', cardInfo.apStyle or 'blue')) | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:28px;left:80px;color:' .. nameColor .. ';font-size:32px">' .. escText(cardInfo.displayTitleShort or cardInfo.displayTitle or cardInfo.cardName) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:68px;left:79px;">[[File:card_type_' .. escFile(cardInfo.cardType) .. '.png|24px|link=]]</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:65px;left:105px;font-size:20px;color:white">' .. escText(cardInfo.cardType) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:250px;left:30px;width:300px;height:210px;color:white;text-align:center;font-size:28px;line-height:30px;">') | |||
local largeContent = expandWikitext((dictDisplay or '') .. (cardInfo.desc or '')) | |||
table.insert(html, '<div class="scroll-text" style="width:280px;height:200px"><div class="scroll-text-content">' .. largeContent .. '</div></div>') | |||
table.insert(html, '</div>') | |||
table.insert(html, '</div>') | |||
if cardInfo.hasInspiration or cardInfo.hasGodInspiration then | if cardInfo.hasInspiration or cardInfo.hasGodInspiration then | ||
table.insert(html, '<div class="variant-buttons" style="display:flex;gap: | table.insert(html, '<div class="variant-buttons" style="display:flex;gap:10px;align-items:center;justify-content:center;flex-wrap:wrap;">') | ||
if cardInfo.hasInspiration then | if cardInfo.hasInspiration then | ||
table.insert(html, '<div class="inspiration-button" style="display:inline-block;padding:10px 10px;background:linear-gradient(135deg,#6fd8fe 0%,#4da6cc 100%);color:white;font-size:18px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">灵光一闪</div>') | table.insert(html, '<div class="inspiration-button" style="display:inline-block;padding:10px 10px;background:linear-gradient(135deg,#6fd8fe 0%,#4da6cc 100%);color:white;font-size:18px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">灵光一闪</div>') | ||
| 第423行: | 第282行: | ||
table.insert(html, '</div>') -- 结束中间列 | table.insert(html, '</div>') -- 结束中间列 | ||
-- | -- 右侧:词典机制说明 | ||
table.insert(html, '<div class="ocv-col-right" style="display:flex;flex-direction:column;align-items:flex-start;gap | table.insert(html, '<div class="ocv-col-right" style="display:flex;flex-direction:column;align-items:flex-start;gap:10px;max-width:250px;">') | ||
table.insert(html, renderDictMechanisms(cardInfo.dictEntries)) | table.insert(html, renderDictMechanisms(cardInfo.dictEntries)) | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
| 第440行: | 第299行: | ||
table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回卡牌</div>') | table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回卡牌</div>') | ||
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">灵光一闪</div>') | table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">灵光一闪</div>') | ||
table.insert(html, '<div class="inspiration-cards-wrapper" style="display:flex;gap | table.insert(html, '<div class="inspiration-cards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;align-items:flex-start;">') | ||
for i, variantInfo in ipairs(cardInfo.inspirationVariants) do | for i, variantInfo in ipairs(cardInfo.inspirationVariants) do | ||
table.insert(html, p.renderInspirationVariant(variantInfo, i)) | table.insert(html, p.renderInspirationVariant(variantInfo, i)) | ||
| 第459行: | 第318行: | ||
if cardInfo.hasGodInspiration and cardInfo.godInspirationGroups and cardInfo.godInspirationGroupOrder then | if cardInfo.hasGodInspiration and cardInfo.godInspirationGroups and cardInfo.godInspirationGroupOrder then | ||
table.insert(html, '<div class="god-inspiration-view" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:flex-start | table.insert(html, '<div class="god-inspiration-view" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:flex-start;">') | ||
table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回卡牌</div>') | table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回卡牌</div>') | ||
table.insert(html, '<div style="color:white;font-size:24px;margin- | table.insert(html, '<div style="color:white;font-size:24px;margin-top: 24px;">神光一闪</div>') | ||
for _, groupName in ipairs(cardInfo.godInspirationGroupOrder) do | |||
local groupList = cardInfo.godInspirationGroups[groupName] | |||
if groupList and #groupList > 0 then | |||
for | table.insert(html, '<div style="color:#ffd36a;font-size:20px;margin:8px 0;">' .. escText(groupName) .. '</div>') | ||
table.insert(html, '<div class="god-inspiration-row" style="display:flex;gap:10px;justify-content:center;flex-wrap:nowrap;max-width:90vw;">') | |||
for _, variantInfo in ipairs(groupList) do | |||
table.insert(html, p.renderGodVariantCard(variantInfo)) | |||
end | end | ||
table.insert(html, '</div>') | |||
end | end | ||
end | end | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
end | end | ||
table.insert(html, '</div>') -- card-modal | |||
return table.concat(html, '') | |||
end | |||
function p.renderSubcardsView(subCardInfos) | function p.renderSubcardsView(subCardInfos) | ||
| 第524行: | 第344行: | ||
table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回所有衍生</div>') | table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回所有衍生</div>') | ||
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">衍生卡牌</div>') | table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">衍生卡牌</div>') | ||
table.insert(html, '<div class="subcards-wrapper" style="display:flex;gap | table.insert(html, '<div class="subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">') | ||
for i, subCardInfo in ipairs(subCardInfos) do | for i, subCardInfo in ipairs(subCardInfos) do | ||
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap | table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">') | ||
table.insert(html, p.renderSubCard(subCardInfo)) | table.insert(html, p.renderSubCard(subCardInfo)) | ||
if subCardInfo.subCards and #subCardInfo.subCards > 0 then | if subCardInfo.subCards and #subCardInfo.subCards > 0 then | ||
| 第548行: | 第368行: | ||
table.insert(html, '<div class="back-to-subcards-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回所有衍生</div>') | table.insert(html, '<div class="back-to-subcards-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回所有衍生</div>') | ||
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(parentCardInfo.displayTitle or parentCardInfo.cardName) .. ' 的衍生卡牌</div>') | table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(parentCardInfo.displayTitle or parentCardInfo.cardName) .. ' 的衍生卡牌</div>') | ||
table.insert(html, '<div class="nested-subcards-wrapper" style="display:flex;gap | table.insert(html, '<div class="nested-subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">') | ||
for _, nestedCardInfo in ipairs(parentCardInfo.subCards) do | for _, nestedCardInfo in ipairs(parentCardInfo.subCards) do | ||
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap | table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">') | ||
table.insert(html, p.renderSubCard(nestedCardInfo)) | table.insert(html, p.renderSubCard(nestedCardInfo)) | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
| 第564行: | 第384行: | ||
local apDisplay = variantInfo.apText or '' | local apDisplay = variantInfo.apText or '' | ||
local nameColor = rarityNameColor(variantInfo.rarity) | local nameColor = rarityNameColor(variantInfo.rarity) | ||
table.insert(html, '<div class="inspiration-variant" style="display:flex;flex-direction:column;align-items:center;gap:8px;width: | table.insert(html, '<div class="inspiration-variant" style="display:flex;flex-direction:column;align-items:center;gap:8px;width:240px;">') | ||
table.insert(html, '<div class="inspiration-variant-card" style="display:inline-block;vertical-align:top;position:relative;width:240px;height:345px;overflow:hidden;">') | |||
table.insert(html, renderArtLayer(variantInfo, 240)) | |||
table.insert(html, renderRarityBar(variantInfo, 240)) | |||
table.insert(html, renderOverlayLayers(variantInfo, 240)) | |||
table.insert(html, renderApDiv(apDisplay, '8px', '31px', '54px', variantInfo.apStyle or 'blue')) | |||
table.insert(html, renderApDiv('—', '46px', '32px', '32px', variantInfo.apStyle or 'blue')) | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:18px;left:64px;color:' .. nameColor .. ';font-size:24px">' .. escText(variantInfo.displayTitleShort or variantInfo.displayTitle or variantInfo.cardName) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:50px;left:63px;">[[File:card_type_' .. escFile(variantInfo.cardType) .. '.png|24px]]</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:49px;left:90px;font-size:20px;color:white">' .. escText(variantInfo.cardType) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:165px;left:20px;width:220px;height:175px;color:white;text-align:center;font-size:18px;line-height:20px;">') | |||
local inspContent = expandWikitext((dictDisplay or '') .. (variantInfo.desc or '')) | |||
table.insert(html, '<div class="scroll-text" style="width:212px;height:170px"><div class="scroll-text-content">' .. inspContent .. '</div></div>') | |||
table.insert(html, '</div>') | |||
table.insert(html, '</div>') | |||
if variantInfo.subCards and #variantInfo.subCards > 0 then | if variantInfo.subCards and #variantInfo.subCards > 0 then | ||
table.insert(html, '<div class="inspiration-subcards-button" data-variant-index="' .. index .. '" style="display:inline-block;padding:10px 20px;background:linear-gradient(135deg,# | table.insert(html, '<div class="inspiration-subcards-button" data-variant-index="' .. index .. '" style="display:inline-block;padding:10px 20px;background:linear-gradient(135deg,#6fd8fe 0%,#4da6cc 100%);color:white;font-size:15px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">查看衍生卡牌</div>') | ||
end | end | ||
| 第660行: | 第418行: | ||
table.insert(html, '<div class="back-to-inspiration-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回灵光一闪</div>') | table.insert(html, '<div class="back-to-inspiration-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回灵光一闪</div>') | ||
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(variantInfo.displayTitle or variantInfo.cardName) .. '的衍生卡牌</div>') | table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(variantInfo.displayTitle or variantInfo.cardName) .. '的衍生卡牌</div>') | ||
table.insert(html, '<div class="inspiration-subcards-wrapper" style="display:flex;gap | table.insert(html, '<div class="inspiration-subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">') | ||
for j, subCardInfo in ipairs(variantInfo.subCards) do | for j, subCardInfo in ipairs(variantInfo.subCards) do | ||
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap | table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">') | ||
table.insert(html, p.renderSubCard(subCardInfo)) | table.insert(html, p.renderSubCard(subCardInfo)) | ||
if subCardInfo.subCards and #subCardInfo.subCards > 0 then | if subCardInfo.subCards and #subCardInfo.subCards > 0 then | ||
| 第679行: | 第437行: | ||
table.insert(html, '<div class="back-to-inspiration-subcards-button" data-variant-index="' .. variantIndex .. '" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回灵光一闪的衍生</div>') | table.insert(html, '<div class="back-to-inspiration-subcards-button" data-variant-index="' .. variantIndex .. '" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回灵光一闪的衍生</div>') | ||
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(parentSubCardInfo.displayTitle or parentSubCardInfo.cardName) .. ' 的衍生卡牌</div>') | table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(parentSubCardInfo.displayTitle or parentSubCardInfo.cardName) .. ' 的衍生卡牌</div>') | ||
table.insert(html, '<div class="inspiration-nested-subcards-wrapper" style="display:flex;gap | table.insert(html, '<div class="inspiration-nested-subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">') | ||
for _, nestedCardInfo in ipairs(parentSubCardInfo.subCards or {}) do | for _, nestedCardInfo in ipairs(parentSubCardInfo.subCards or {}) do | ||
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap | table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">') | ||
table.insert(html, p.renderSubCard(nestedCardInfo)) | table.insert(html, p.renderSubCard(nestedCardInfo)) | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
| 第695行: | 第453行: | ||
local apDisplay = variantInfo.apText or '' | local apDisplay = variantInfo.apText or '' | ||
local nameColor = rarityNameColor(variantInfo.rarity) | local nameColor = rarityNameColor(variantInfo.rarity) | ||
table.insert(html, '<div class="god-variant-card" style="display:inline-block;vertical-align:top;position:relative;width:240px;height:345px;overflow:hidden;">') | |||
table.insert(html, renderArtLayer(variantInfo, 240)) | |||
table.insert(html, renderRarityBar(variantInfo, 240)) | |||
table.insert(html, renderOverlayLayers(variantInfo, 240)) | |||
table.insert(html, renderApDiv(apDisplay, '8px', '31px', '54px', variantInfo.apStyle or 'blue')) | |||
table.insert(html, renderApDiv('—', '46px', '32px', '32px', variantInfo.apStyle or 'blue')) | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:18px;left:64px;color:' .. nameColor .. ';font-size:24px">' .. escText(variantInfo.displayTitleShort or variantInfo.displayTitle or variantInfo.cardName) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:50px;left:63px;">[[File:card_type_' .. escFile(variantInfo.cardType) .. '.png|24px]]</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:49px;left:90px;font-size:20px;color:white">' .. escText(variantInfo.cardType) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:165px;left:20px;width:220px;height:175px;color:white;text-align:center;font-size:18px;line-height:20px;">') | |||
local godContent = expandWikitext((dictDisplay or '') .. (variantInfo.desc or '')) | |||
table.insert(html, '<div class="scroll-text" style="width:212px;height:170px"><div class="scroll-text-content">' .. godContent .. '</div></div>') | |||
table.insert(html, '</div>') | |||
table.insert(html, '</div>') | |||
return table.concat(html, '') | return table.concat(html, '') | ||
end | end | ||
| 第784行: | 第480行: | ||
local apDisplay = subCardInfo.apText or '' | local apDisplay = subCardInfo.apText or '' | ||
local nameColor = rarityNameColor(subCardInfo.rarity) | local nameColor = rarityNameColor(subCardInfo.rarity) | ||
table.insert(html, '<div style="display:inline-block;vertical-align:top;position:relative;width:240px;height:345px;overflow:hidden;">') | |||
table.insert(html, renderArtLayer(subCardInfo, 240)) | |||
table.insert(html, renderRarityBar(subCardInfo, 240)) | |||
table.insert(html, renderOverlayLayers(subCardInfo, 240)) | |||
table.insert(html, renderApDiv(apDisplay, '8px', '31px', '54px', subCardInfo.apStyle or 'blue')) | |||
table.insert(html, renderApDiv('—', '46px', '32px', '38px', subCardInfo.apStyle or 'blue')) | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:18px;left:64px;color:' .. nameColor .. ';font-size:24px">' .. escText(subCardInfo.displayTitleShort or subCardInfo.displayTitle or subCardInfo.cardName) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:50px;left:63px;">[[File:card_type_' .. escFile(subCardInfo.cardType) .. '.png|24px]]</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:49px;left:90px;font-size:20px;color:white">' .. escText(subCardInfo.cardType) .. '</div>') | |||
table.insert(html, '<div style="position:absolute;z-index:10;top:165px;left:20px;width:220px;height:175px;color:white;text-align:center;font-size:18px;line-height:20px;">') | |||
local subContent = expandWikitext((dictDisplay or '') .. (subCardInfo.desc or '')) | |||
table.insert(html, '<div class="scroll-text" style="width:212px;height:170px"><div class="scroll-text-content">' .. subContent .. '</div></div>') | |||
table.insert(html, '</div>') | |||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
return table.concat(html, '') | return table.concat(html, '') | ||
2025年10月15日 (三) 20:24的版本
此模块的文档可以在模块:卡牌/display/doc创建
local p = {}
local function escFile(s)
s = tostring(s or '')
s = mw.text.trim(s)
s = s:gsub('[%[%]%{%}%|]', '')
return s
end
local function escText(s)
return mw.text.encode(tostring(s or ''))
end
-- 确保模板与解析函数在内容中被展开
local function expandWikitext(s)
if s == nil or s == '' then
return ''
end
local ok, frame = pcall(mw.getCurrentFrame)
if not ok or not frame then
return tostring(s)
end
return frame:preprocess(tostring(s))
end
-- 稀有度统一
local function normalizeRarity(r)
local s = tostring(r or ''):gsub('%s+', '')
if s == '' then return nil end
if s == '白' then return '白' end
if s == '蓝' then return '蓝' end
if s == '橙' or s == '橙色' then return '橙' end
if s == '彩' or s == '彩虹' then return '彩' end
return nil
end
-- 稀有度对应的标题背景条颜色
local function rarityBarColor(r)
local key = normalizeRarity(r) or '蓝'
local map = {
['白'] = 'rgba(239, 237, 237, 0.6)',
['蓝'] = 'rgba(119, 236, 254, 0.6)',
['橙'] = 'rgba(255, 200, 110, 0.6)',
['彩'] = 'rgba(180, 125, 242, 0.6)',
}
return map[key] or map['蓝']
end
-- 稀有度对应的卡牌名称颜色
local function rarityNameColor(r)
local key = normalizeRarity(r) or '蓝'
local map = {
['白'] = '#cccccc',
['蓝'] = '#73ecfe',
['橙'] = '#ffc86e',
['彩'] = '#a37df7',
}
return map[key] or map['蓝']
end
-- 根据外框宽度返回标题背景条几何尺寸
local function getBarGeom(width)
width = tonumber(width) or 160
if width <= 160 then
return { top = 22, left = 5, w = 150, h = 8 }
elseif width >= 320 then
return { top = 44, left = 5, w = 305, h = 16 }
else
return { top = 33, left = 5, w = 230, h = 12 }
end
end
-- 渲染标题背景条层级,art 之上,其余元素之下
local function renderRarityBar(info, width)
local g = getBarGeom(width)
local color = rarityBarColor(info.rarity)
return string.format(
'<div style="position:absolute;z-index:1;top:%dpx;left:%dpx;width:%dpx;height:%dpx;background-color:%s"></div>',
g.top, g.left, g.w, g.h, color
)
end
-- 仅渲染立绘层(最低层)
local function renderArtLayer(info, width)
return '<div style="position: absolute; top: 0; left: 0; z-index:0;">[[File:' .. escFile(info.art) .. '|' .. width .. 'px|link=]]</div>'
end
-- 渲染覆盖层(横条之上)
local function renderOverlayLayers(info, width)
local t = {}
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:2;">[[File:card_黑色蒙版.png|' .. width .. 'px|link=]]</div>')
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:3;">[[File:card_ego_' .. escFile(info.ego) .. '.png|' .. width .. 'px|link=]]</div>')
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:4;">[[File:card_rarity_' .. escFile(info.rarity) .. '.png|' .. width .. 'px|link=]]</div>')
table.insert(t, '<div style="position: absolute; top: 0; left: 0; z-index:5;">[[File:card_顶层蒙版.png|' .. width .. 'px|link=]]</div>')
return table.concat(t, '')
end
-- 将 tokens 渲染为可调用模板的短标识
local function dictSpanFromTokens(tokens, color)
if type(tokens) ~= 'table' or #tokens == 0 then
return ''
end
local parts = {}
for _, t in ipairs(tokens) do
local key = mw.text.trim(t or '')
if key ~= '' then
table.insert(parts, '{{词典|' .. key .. '}}')
end
end
if #parts == 0 then return '' end
local colorMap = {
default = '#f2ba02',
green = '#22bb66'
}
local c = colorMap[color or 'default'] or colorMap.default
return '<span style="color: ' .. c .. '">[' .. table.concat(parts, '/') .. ']</span><br>'
end
local function generateUniqueId(cardInfo)
local timestamp = os.time()
local random = math.random(1000, 9999)
local base = (cardInfo.moduleName or '') .. '-' .. (cardInfo.cardName or '') .. (cardInfo.variantSuffix or '')
local safeName = mw.uri.anchorEncode(base):gsub("%%", "-")
return "card-" .. safeName .. "-" .. timestamp .. "-" .. random
end
-- AP 发光样式
local function apTextShadow(style)
if style == 'red' then
return 'text-shadow:0 0 10px #7a0e0e,0 0 20px #7a0e0e,0 0 30px #ff3030,0 0 40px #ff3030,0 0 50px #ff3030,0 0 60px #ff3030,0 0 70px #ff3030;'
elseif style == 'green' then
return 'text-shadow:0 0 10px #0e6f36,0 0 20px #0e6f36,0 0 30px #29ff85,0 0 40px #29ff85,0 0 50px #29ff85,0 0 60px #29ff85,0 0 70px #29ff85;'
elseif style == 'blue' then
return 'text-shadow:0 0 10px #132cbf,0 0 20px #132cbf,0 0 30px #4169ff,0 0 40px #4169ff,0 0 50px #4169ff,0 0 60px #4169ff,0 0 70px #4169ff;'
else
return '' -- gray: no glow
end
end
local function renderApDiv(content, top, left, fontSize, style)
content = tostring(content or '')
local shadow = apTextShadow(style)
local color = (style == 'gray') and '#cfcfcf' or 'white'
return string.format(
'<div style="position:absolute;z-index:10;top:%s;left:%s;font-family:number;font-size:%s;color:%s;%s">%s</div>',
top, left, fontSize, color, shadow, content
)
end
-- 单个机制卡片(固定 250px 宽度)
local function renderMechanismCard(title, desc)
title = escText(title or '')
local d = tostring(desc or '')
if d == '' then
d = '暂无详细说明'
end
-- 展开词典说明中的模板
local body = expandWikitext(d)
local dhtml = {}
table.insert(dhtml, '<div class="mechanism-card" style="width: 250px;">')
table.insert(dhtml, '<div style="width: 266px; height: 2px; background-color: #f2ba02;"></div>')
table.insert(dhtml, '<div style="width: 100%; background-color: #343434; color: white; padding: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); line-height: 1.6;">')
table.insert(dhtml, '<div style="font-weight: bold;">' .. title .. '</div>')
table.insert(dhtml, '<div>' .. body .. '</div>')
table.insert(dhtml, '</div>')
table.insert(dhtml, '</div>')
return table.concat(dhtml, '')
end
-- 右侧词典说明容器
local function renderDictMechanisms(dictEntries)
if type(dictEntries) ~= 'table' or #dictEntries == 0 then return '' end
local html = {}
-- 固定宽度 250px
table.insert(html, '<div class="dict-mechanisms" style="display:flex;flex-direction:column;align-items:flex-start;gap:10px;margin-left:10px;width:250px;flex:0 0 250px;overflow:visible;">')
for _, entry in ipairs(dictEntries) do
table.insert(html, renderMechanismCard(entry.key, entry.desc))
end
table.insert(html, '</div>')
return table.concat(html, '')
end
function p.render(cardInfo, subCardInfo)
local dictDisplay = dictSpanFromTokens(cardInfo.dictTokens, cardInfo.dictColor)
local cardId = generateUniqueId(cardInfo)
local html = {}
-- 渲染小卡片
table.insert(html, p.renderSmallCard(cardInfo, cardId, dictDisplay))
-- 渲染模态框(单独输出)
table.insert(html, p.renderModal(cardInfo, cardId, dictDisplay, subCardInfo))
return table.concat(html, '')
end
function p.renderSmallCard(cardInfo, cardId, dictDisplay)
local html = {}
local apDisplay = cardInfo.apText or ''
local nameColor = rarityNameColor(cardInfo.rarity)
table.insert(html, '<div class="card-small-wrapper" data-card-id="' .. escText(cardId) .. '" style="display:inline-block;vertical-align:top;position:relative;width:160px;height:230px;overflow:hidden;cursor:pointer;">')
table.insert(html, renderArtLayer(cardInfo, 160))
table.insert(html, renderRarityBar(cardInfo, 160))
table.insert(html, renderOverlayLayers(cardInfo, 160))
table.insert(html, renderApDiv(apDisplay, '5px', '25px', '36px', cardInfo.apStyle or 'blue'))
table.insert(html, renderApDiv('—', '23px', '23px', '32px', cardInfo.apStyle or 'blue'))
table.insert(html, '<div style="position:absolute;z-index:10;top:12px;left:50px;color:' .. nameColor .. ';font-size:18px">' .. escText(cardInfo.displayTitleShort or cardInfo.displayTitle or cardInfo.cardName) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:33px;left:49px;">[[File:card_type_' .. escFile(cardInfo.cardType) .. '.png|18px|link=]]</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:32px;left:67px;font-size:14px;color:white">' .. escText(cardInfo.cardType) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:110px;left:20px;width:130px;height:115px;color:white;text-align:center;font-size:13px;line-height:15px;">')
local smallContent = expandWikitext((dictDisplay or '') .. (cardInfo.desc or ''))
table.insert(html, '<div class="scroll-text"><div class="scroll-text-content">' .. smallContent .. '</div></div>')
table.insert(html, '</div>')
table.insert(html, '</div>')
return table.concat(html, '')
end
-- 新增:独立的模态框渲染函数
function p.renderModal(cardInfo, cardId, dictDisplay, subCardInfos)
local html = {}
local apDisplay = cardInfo.apText or ''
local nameColor = rarityNameColor(cardInfo.rarity)
table.insert(html, '<div id="' .. escText(cardId) .. '-modal" class="card-modal" style="display:none;position:fixed;z-index:9999;left:0;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.8);">')
table.insert(html, '<div class="modal-close-button" style="position:fixed;top:20px;right:20px;z-index:10001;width:40px;height:40px;background:rgba(255,255,255,0.2);border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all 0.3s;">')
table.insert(html, '<span style="color:white;font-size:24px;line-height:1;">×</span>')
table.insert(html, '</div>')
table.insert(html, '<div class="card-modal-inner" style="height:100vh;display:flex;align-items:center;justify-content:center;">')
table.insert(html, '<div class="original-card-view" style="display:flex;align-items:flex-start;justify-content:center;gap:10px;flex-wrap:wrap;">')
-- 左侧:衍生卡牌
if subCardInfos and #subCardInfos > 0 then
table.insert(html, '<div class="ocv-col-left" style="display:flex;flex-direction:column;align-items:center;gap:10px;max-width:240px;">')
table.insert(html, p.renderSubCard(subCardInfos[1]))
if #subCardInfos > 1 then
table.insert(html, '<div class="subcards-button" style="display:inline-block;padding:10px 10px;background:linear-gradient(135deg,#6fd8fe 0%,#4da6cc 100%);color:white;font-size:18px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">查看所有衍生卡牌</div>')
end
table.insert(html, '</div>')
end
-- 中间:放大卡片
table.insert(html, '<div class="ocv-col-center" style="display:flex;flex-direction:column;align-items:center;gap:10px;max-width:320px;">')
table.insert(html, '<div style="display:inline-block;vertical-align:top;position:relative;width:320px;height:460px;overflow:hidden;">')
table.insert(html, renderArtLayer(cardInfo, 320))
table.insert(html, renderRarityBar(cardInfo, 320))
table.insert(html, renderOverlayLayers(cardInfo, 320))
table.insert(html, renderApDiv(apDisplay, '18px', '45px', '58px', cardInfo.apStyle or 'blue'))
table.insert(html, renderApDiv('__', '48px', '40px', '32px', cardInfo.apStyle or 'blue'))
table.insert(html, '<div style="position:absolute;z-index:10;top:28px;left:80px;color:' .. nameColor .. ';font-size:32px">' .. escText(cardInfo.displayTitleShort or cardInfo.displayTitle or cardInfo.cardName) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:68px;left:79px;">[[File:card_type_' .. escFile(cardInfo.cardType) .. '.png|24px|link=]]</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:65px;left:105px;font-size:20px;color:white">' .. escText(cardInfo.cardType) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:250px;left:30px;width:300px;height:210px;color:white;text-align:center;font-size:28px;line-height:30px;">')
local largeContent = expandWikitext((dictDisplay or '') .. (cardInfo.desc or ''))
table.insert(html, '<div class="scroll-text" style="width:280px;height:200px"><div class="scroll-text-content">' .. largeContent .. '</div></div>')
table.insert(html, '</div>')
table.insert(html, '</div>')
if cardInfo.hasInspiration or cardInfo.hasGodInspiration then
table.insert(html, '<div class="variant-buttons" style="display:flex;gap:10px;align-items:center;justify-content:center;flex-wrap:wrap;">')
if cardInfo.hasInspiration then
table.insert(html, '<div class="inspiration-button" style="display:inline-block;padding:10px 10px;background:linear-gradient(135deg,#6fd8fe 0%,#4da6cc 100%);color:white;font-size:18px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">灵光一闪</div>')
end
if cardInfo.hasGodInspiration then
table.insert(html, '<div class="god-inspiration-button" style="display:inline-block;padding:10px 10px;background:linear-gradient(135deg,#ffd36a 0%,#e6a600 100%);color:white;font-size:18px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">神光一闪</div>')
end
table.insert(html, '</div>')
end
table.insert(html, '</div>') -- 结束中间列
-- 右侧:词典机制说明
table.insert(html, '<div class="ocv-col-right" style="display:flex;flex-direction:column;align-items:flex-start;gap:10px;max-width:250px;">')
table.insert(html, renderDictMechanisms(cardInfo.dictEntries))
table.insert(html, '</div>')
table.insert(html, '</div>') -- original-card-view
table.insert(html, '</div>') -- card-modal-inner
-- 其他视图
if subCardInfos and #subCardInfos > 1 then
table.insert(html, p.renderSubcardsView(subCardInfos))
end
if cardInfo.hasInspiration and cardInfo.inspirationVariants then
table.insert(html, '<div class="inspiration-view" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:center;">')
table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回卡牌</div>')
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">灵光一闪</div>')
table.insert(html, '<div class="inspiration-cards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;align-items:flex-start;">')
for i, variantInfo in ipairs(cardInfo.inspirationVariants) do
table.insert(html, p.renderInspirationVariant(variantInfo, i))
end
table.insert(html, '</div>')
table.insert(html, '</div>')
for i, variantInfo in ipairs(cardInfo.inspirationVariants) do
if variantInfo.subCards and #variantInfo.subCards > 0 then
table.insert(html, p.renderInspirationSubcardsView(variantInfo, i))
for j, subCardInfo in ipairs(variantInfo.subCards) do
if subCardInfo.subCards and #subCardInfo.subCards > 0 then
table.insert(html, p.renderInspirationNestedSubcardsView(variantInfo, i, subCardInfo, j))
end
end
end
end
end
if cardInfo.hasGodInspiration and cardInfo.godInspirationGroups and cardInfo.godInspirationGroupOrder then
table.insert(html, '<div class="god-inspiration-view" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:flex-start;">')
table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回卡牌</div>')
table.insert(html, '<div style="color:white;font-size:24px;margin-top: 24px;">神光一闪</div>')
for _, groupName in ipairs(cardInfo.godInspirationGroupOrder) do
local groupList = cardInfo.godInspirationGroups[groupName]
if groupList and #groupList > 0 then
table.insert(html, '<div style="color:#ffd36a;font-size:20px;margin:8px 0;">' .. escText(groupName) .. '</div>')
table.insert(html, '<div class="god-inspiration-row" style="display:flex;gap:10px;justify-content:center;flex-wrap:nowrap;max-width:90vw;">')
for _, variantInfo in ipairs(groupList) do
table.insert(html, p.renderGodVariantCard(variantInfo))
end
table.insert(html, '</div>')
end
end
table.insert(html, '</div>')
end
table.insert(html, '</div>') -- card-modal
return table.concat(html, '')
end
function p.renderSubcardsView(subCardInfos)
local html = {}
table.insert(html, '<div class="subcards-view" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:center;">')
table.insert(html, '<div class="back-to-card-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回所有衍生</div>')
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">衍生卡牌</div>')
table.insert(html, '<div class="subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">')
for i, subCardInfo in ipairs(subCardInfos) do
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">')
table.insert(html, p.renderSubCard(subCardInfo))
if subCardInfo.subCards and #subCardInfo.subCards > 0 then
table.insert(html, '<div class="view-nested-subcards-button" data-subcard-index="' .. i .. '" style="display:inline-block;padding:10px 20px;background:linear-gradient(135deg,#ffa500 0%,#cc8400 100%);color:white;font-size:15px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">查看衍生卡牌</div>')
end
table.insert(html, '</div>')
end
table.insert(html, '</div>')
table.insert(html, '</div>')
for i, subCardInfo in ipairs(subCardInfos) do
if subCardInfo.subCards and #subCardInfo.subCards > 0 then
table.insert(html, p.renderNestedSubcardsView(subCardInfo, i))
end
end
return table.concat(html, '')
end
function p.renderNestedSubcardsView(parentCardInfo, parentIndex)
local html = {}
table.insert(html, '<div class="nested-subcards-view" data-subcard-index="' .. parentIndex .. '" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:center;">')
table.insert(html, '<div class="back-to-subcards-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回所有衍生</div>')
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(parentCardInfo.displayTitle or parentCardInfo.cardName) .. ' 的衍生卡牌</div>')
table.insert(html, '<div class="nested-subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">')
for _, nestedCardInfo in ipairs(parentCardInfo.subCards) do
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">')
table.insert(html, p.renderSubCard(nestedCardInfo))
table.insert(html, '</div>')
end
table.insert(html, '</div>')
table.insert(html, '</div>')
return table.concat(html, '')
end
function p.renderInspirationVariant(variantInfo, index)
local html = {}
local dictDisplay = dictSpanFromTokens(variantInfo.dictTokens, variantInfo.dictColor)
local apDisplay = variantInfo.apText or ''
local nameColor = rarityNameColor(variantInfo.rarity)
table.insert(html, '<div class="inspiration-variant" style="display:flex;flex-direction:column;align-items:center;gap:8px;width:240px;">')
table.insert(html, '<div class="inspiration-variant-card" style="display:inline-block;vertical-align:top;position:relative;width:240px;height:345px;overflow:hidden;">')
table.insert(html, renderArtLayer(variantInfo, 240))
table.insert(html, renderRarityBar(variantInfo, 240))
table.insert(html, renderOverlayLayers(variantInfo, 240))
table.insert(html, renderApDiv(apDisplay, '8px', '31px', '54px', variantInfo.apStyle or 'blue'))
table.insert(html, renderApDiv('—', '46px', '32px', '32px', variantInfo.apStyle or 'blue'))
table.insert(html, '<div style="position:absolute;z-index:10;top:18px;left:64px;color:' .. nameColor .. ';font-size:24px">' .. escText(variantInfo.displayTitleShort or variantInfo.displayTitle or variantInfo.cardName) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:50px;left:63px;">[[File:card_type_' .. escFile(variantInfo.cardType) .. '.png|24px]]</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:49px;left:90px;font-size:20px;color:white">' .. escText(variantInfo.cardType) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:165px;left:20px;width:220px;height:175px;color:white;text-align:center;font-size:18px;line-height:20px;">')
local inspContent = expandWikitext((dictDisplay or '') .. (variantInfo.desc or ''))
table.insert(html, '<div class="scroll-text" style="width:212px;height:170px"><div class="scroll-text-content">' .. inspContent .. '</div></div>')
table.insert(html, '</div>')
table.insert(html, '</div>')
if variantInfo.subCards and #variantInfo.subCards > 0 then
table.insert(html, '<div class="inspiration-subcards-button" data-variant-index="' .. index .. '" style="display:inline-block;padding:10px 20px;background:linear-gradient(135deg,#6fd8fe 0%,#4da6cc 100%);color:white;font-size:15px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">查看衍生卡牌</div>')
end
table.insert(html, '</div>')
return table.concat(html, '')
end
function p.renderInspirationSubcardsView(variantInfo, index)
local html = {}
table.insert(html, '<div class="inspiration-subcards-view" data-variant-index="' .. index .. '" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:center;">')
table.insert(html, '<div class="back-to-inspiration-button" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回灵光一闪</div>')
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(variantInfo.displayTitle or variantInfo.cardName) .. '的衍生卡牌</div>')
table.insert(html, '<div class="inspiration-subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">')
for j, subCardInfo in ipairs(variantInfo.subCards) do
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">')
table.insert(html, p.renderSubCard(subCardInfo))
if subCardInfo.subCards and #subCardInfo.subCards > 0 then
table.insert(html, '<div class="view-inspiration-nested-subcards-button" data-variant-index="' .. index .. '" data-subcard-index="' .. j .. '" style="display:inline-block;padding:10px 20px;background:linear-gradient(135deg,#ffa500 0%,#cc8400 100%);color:white;font-size:15px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">查看衍生卡牌</div>')
end
table.insert(html, '</div>')
end
table.insert(html, '</div>')
table.insert(html, '</div>')
return table.concat(html, '')
end
function p.renderInspirationNestedSubcardsView(variantInfo, variantIndex, parentSubCardInfo, parentSubIndex)
local html = {}
table.insert(html, '<div class="inspiration-nested-subcards-view" data-variant-index="' .. variantIndex .. '" data-subcard-index="' .. parentSubIndex .. '" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:center;">')
table.insert(html, '<div class="back-to-inspiration-subcards-button" data-variant-index="' .. variantIndex .. '" style="position:fixed;top:20px;left:20px;z-index:10001;padding:10px 25px;background:linear-gradient(135deg,#ff6b6b 0%,#cc5555 100%);color:white;font-size:16px;font-weight:bold;border-radius:8px;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.3s;user-select:none;">← 返回灵光一闪的衍生</div>')
table.insert(html, '<div style="color:white;font-size:24px;margin-bottom:20px;">' .. escText(parentSubCardInfo.displayTitle or parentSubCardInfo.cardName) .. ' 的衍生卡牌</div>')
table.insert(html, '<div class="inspiration-nested-subcards-wrapper" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;max-width:90vw;">')
for _, nestedCardInfo in ipairs(parentSubCardInfo.subCards or {}) do
table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;gap:10px;">')
table.insert(html, p.renderSubCard(nestedCardInfo))
table.insert(html, '</div>')
end
table.insert(html, '</div>')
table.insert(html, '</div>')
return table.concat(html, '')
end
function p.renderGodVariantCard(variantInfo)
local html = {}
local dictDisplay = dictSpanFromTokens(variantInfo.dictTokens, variantInfo.dictColor)
local apDisplay = variantInfo.apText or ''
local nameColor = rarityNameColor(variantInfo.rarity)
table.insert(html, '<div class="god-variant-card" style="display:inline-block;vertical-align:top;position:relative;width:240px;height:345px;overflow:hidden;">')
table.insert(html, renderArtLayer(variantInfo, 240))
table.insert(html, renderRarityBar(variantInfo, 240))
table.insert(html, renderOverlayLayers(variantInfo, 240))
table.insert(html, renderApDiv(apDisplay, '8px', '31px', '54px', variantInfo.apStyle or 'blue'))
table.insert(html, renderApDiv('—', '46px', '32px', '32px', variantInfo.apStyle or 'blue'))
table.insert(html, '<div style="position:absolute;z-index:10;top:18px;left:64px;color:' .. nameColor .. ';font-size:24px">' .. escText(variantInfo.displayTitleShort or variantInfo.displayTitle or variantInfo.cardName) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:50px;left:63px;">[[File:card_type_' .. escFile(variantInfo.cardType) .. '.png|24px]]</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:49px;left:90px;font-size:20px;color:white">' .. escText(variantInfo.cardType) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:165px;left:20px;width:220px;height:175px;color:white;text-align:center;font-size:18px;line-height:20px;">')
local godContent = expandWikitext((dictDisplay or '') .. (variantInfo.desc or ''))
table.insert(html, '<div class="scroll-text" style="width:212px;height:170px"><div class="scroll-text-content">' .. godContent .. '</div></div>')
table.insert(html, '</div>')
table.insert(html, '</div>')
return table.concat(html, '')
end
function p.renderSubCard(subCardInfo)
local html = {}
local dictDisplay = dictSpanFromTokens(subCardInfo.dictTokens, subCardInfo.dictColor)
local apDisplay = subCardInfo.apText or ''
local nameColor = rarityNameColor(subCardInfo.rarity)
table.insert(html, '<div style="display:inline-block;vertical-align:top;position:relative;width:240px;height:345px;overflow:hidden;">')
table.insert(html, renderArtLayer(subCardInfo, 240))
table.insert(html, renderRarityBar(subCardInfo, 240))
table.insert(html, renderOverlayLayers(subCardInfo, 240))
table.insert(html, renderApDiv(apDisplay, '8px', '31px', '54px', subCardInfo.apStyle or 'blue'))
table.insert(html, renderApDiv('—', '46px', '32px', '38px', subCardInfo.apStyle or 'blue'))
table.insert(html, '<div style="position:absolute;z-index:10;top:18px;left:64px;color:' .. nameColor .. ';font-size:24px">' .. escText(subCardInfo.displayTitleShort or subCardInfo.displayTitle or subCardInfo.cardName) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:50px;left:63px;">[[File:card_type_' .. escFile(subCardInfo.cardType) .. '.png|24px]]</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:49px;left:90px;font-size:20px;color:white">' .. escText(subCardInfo.cardType) .. '</div>')
table.insert(html, '<div style="position:absolute;z-index:10;top:165px;left:20px;width:220px;height:175px;color:white;text-align:center;font-size:18px;line-height:20px;">')
local subContent = expandWikitext((dictDisplay or '') .. (subCardInfo.desc or ''))
table.insert(html, '<div class="scroll-text" style="width:212px;height:170px"><div class="scroll-text-content">' .. subContent .. '</div></div>')
table.insert(html, '</div>')
table.insert(html, '</div>')
return table.concat(html, '')
end
return p