Card.js:修订间差异
来自卡厄思梦境WIKI
无编辑摘要 |
无编辑摘要 |
||
| 第148行: | 第148行: | ||
'<div class="ce-middle">', | '<div class="ce-middle">', | ||
'<div class="ce-group">', | '<div class="ce-group">', | ||
'<div class="ce-group-title">卡牌列表</div>', | '<div class="ce-group-title">卡牌列表 <span class="ce-hint">(拖动调整顺序)</span></div>', | ||
'<div class="ce-list-container">', | '<div class="ce-list-container">', | ||
'<div id="ce-card-list" class="ce-list"></div>', | '<div id="ce-card-list" class="ce-list ce-sortable"></div>', | ||
'</div>', | '</div>', | ||
'</div>', | '</div>', | ||
'<div class="ce-group">', | '<div class="ce-group">', | ||
'<div class="ce-group-title">变体列表</div>', | '<div class="ce-group-title">变体列表 <span class="ce-hint">(拖动调整顺序)</span></div>', | ||
'<div class="ce-list-container">', | '<div class="ce-list-container">', | ||
'<div id="ce-variant-list" class="ce-list"></div>', | '<div id="ce-variant-list" class="ce-list ce-sortable"></div>', | ||
'</div>', | '</div>', | ||
'</div>', | '</div>', | ||
| 第178行: | 第178行: | ||
'</div>', | '</div>', | ||
'</div>', | '</div>', | ||
'</div>' | '</div>', | ||
'<style>', | |||
'.ce-hint { font-size: 12px; color: #666; font-weight: normal; }', | |||
'.ce-list-item { position: relative; cursor: move; }', | |||
'.ce-list-item.ui-sortable-helper { background: #e3f2fd; border: 2px solid #2196F3; }', | |||
'.ce-list-item.ui-sortable-placeholder { background: #f5f5f5; border: 2px dashed #ccc; visibility: visible !important; }', | |||
'.ce-list-item.selected { background: #e3f2fd; }', | |||
'</style>' | |||
].join(''); | ].join(''); | ||
| 第249行: | 第256行: | ||
var index = $this.data('index'); | var index = $this.data('index'); | ||
self.onVariantSelected(index); | self.onVariantSelected(index); | ||
} | |||
}); | |||
// 初始化拖拽排序 | |||
this.initSortable(); | |||
}, | |||
initSortable: function() { | |||
var self = this; | |||
// 卡牌列表排序 | |||
$('#ce-card-list').sortable({ | |||
items: '.ce-list-item', | |||
placeholder: 'ce-list-item ui-sortable-placeholder', | |||
update: function(event, ui) { | |||
self.onCardListReorder(); | |||
} | |||
}); | |||
// 变体列表排序 | |||
$('#ce-variant-list').sortable({ | |||
items: '.ce-list-item:not(.ce-no-sort)', | |||
placeholder: 'ce-list-item ui-sortable-placeholder', | |||
update: function(event, ui) { | |||
self.onVariantListReorder(); | |||
} | } | ||
}); | }); | ||
}, | |||
onCardListReorder: function() { | |||
if (!this.currentCharacter) return; | |||
var newOrder = []; | |||
$('#ce-card-list .ce-list-item').each(function() { | |||
var index = $(this).data('index'); | |||
newOrder.push(index); | |||
}); | |||
// 重新排序卡牌数组 | |||
var cards = this.characters[this.currentCharacter]; | |||
var reorderedCards = []; | |||
for (var i = 0; i < newOrder.length; i++) { | |||
reorderedCards.push(cards[newOrder[i]]); | |||
} | |||
this.characters[this.currentCharacter] = reorderedCards; | |||
// 更新当前选中的卡牌引用 | |||
if (this.currentCard) { | |||
var currentCardIndex = reorderedCards.indexOf(this.currentCard); | |||
if (currentCardIndex === -1) { | |||
this.currentCard = null; | |||
this.currentVariantIndex = null; | |||
} | |||
} | |||
this.updateCardList(); | |||
this.updateCode(); | |||
}, | |||
onVariantListReorder: function() { | |||
if (!this.currentCard) return; | |||
var newOrder = []; | |||
$('#ce-variant-list .ce-list-item').each(function() { | |||
var index = $(this).data('index'); | |||
newOrder.push(index); | |||
}); | |||
// 重新排序变体数组 | |||
var variants = this.currentCard.variants; | |||
var reorderedVariants = []; | |||
for (var i = 0; i < newOrder.length; i++) { | |||
reorderedVariants.push(variants[newOrder[i]]); | |||
} | |||
this.currentCard.variants = reorderedVariants; | |||
// 更新当前选中的变体索引 | |||
if (this.currentVariantIndex !== null) { | |||
var oldVariant = variants[this.currentVariantIndex]; | |||
this.currentVariantIndex = reorderedVariants.indexOf(oldVariant); | |||
} | |||
this.updateVariantList(); | |||
this.updateCode(); | |||
}, | }, | ||
| 第515行: | 第604行: | ||
addVariant: function() { | addVariant: function() { | ||
if (!this.currentCard) { | |||
alert('请先选择一个卡牌!'); | |||
return; | |||
} | |||
var variantData = this.getCardData(); | |||
var variant = variantData.variants[0]; | |||
variant['卡组'] = '灵光一闪'; | |||
this.currentCard.variants.push(variant); | |||
this.currentVariantIndex = this.currentCard.variants.length - 1; | |||
this.updateVariantList(); | |||
this.updateCode(); | |||
alert('为 "' + this.currentCard.name + '" 添加变体成功!'); | |||
}, | |||
}, | |||
saveData: function() { | saveData: function() { | ||
if (!this.currentCard || this.currentVariantIndex === null) { | |||
alert('请先选择要保存的卡牌或变体!'); | |||
return; | |||
} | |||
var cardData = this.getCardData(); | |||
var isVariant = this.currentVariantIndex > 0; | |||
if (isVariant) { | |||
var variant = cardData.variants[0]; | |||
variant['卡组'] = '灵光一闪'; | |||
// 不再自动保留主卡牌的属性 | |||
this.currentCard.variants[this.currentVariantIndex] = variant; | |||
} else { | |||
this.currentCard.name = cardData.name; | |||
this.currentCard.variants[0] = cardData.variants[0]; | |||
} | |||
this.updateCardList(); | |||
this.updateVariantList(); | |||
this.updateCode(); | |||
alert('数据保存成功!'); | |||
}, | }, | ||
deleteCard: function() { | deleteCard: function() { | ||
| 第604行: | 第690行: | ||
onCardSelected: function(index) { | onCardSelected: function(index) { | ||
if (!this.currentCharacter) return; | if (!this.currentCharacter) return; | ||
this.currentCard = this.characters[this.currentCharacter][ | |||
// 由于重排序后,index对应的是DOM中的位置,需要找到对应的实际卡牌 | |||
var $item = $('#ce-card-list .ce-list-item').eq(index); | |||
var actualIndex = $item.data('original-index'); | |||
this.currentCard = this.characters[this.currentCharacter][actualIndex]; | |||
this.currentVariantIndex = 0; | this.currentVariantIndex = 0; | ||
this.updateVariantList(); | this.updateVariantList(); | ||
| 第612行: | 第703行: | ||
onVariantSelected: function(index) { | onVariantSelected: function(index) { | ||
if (!this.currentCard) return; | if (!this.currentCard) return; | ||
this.currentVariantIndex = | |||
this.setCardData(this.currentCard, | // 由于重排序后,index对应的是DOM中的位置,需要找到对应的实际变体 | ||
var $item = $('#ce-variant-list .ce-list-item').eq(index); | |||
var actualIndex = $item.data('original-index'); | |||
this.currentVariantIndex = actualIndex; | |||
this.setCardData(this.currentCard, actualIndex); | |||
}, | }, | ||
| 第623行: | 第719行: | ||
var card = cards[i]; | var card = cards[i]; | ||
var selected = (card === this.currentCard) ? ' selected' : ''; | var selected = (card === this.currentCard) ? ' selected' : ''; | ||
html += '<div class="ce-list-item' + selected + '" data-index="' + i + '">' + | html += '<div class="ce-list-item' + selected + '" data-index="' + i + | ||
'" data-original-index="' + i + '">' + | |||
'<span class="ce-drag-handle">≡ </span>' + | |||
card.name + '</div>'; | card.name + '</div>'; | ||
} | } | ||
| 第633行: | 第731行: | ||
$('#ce-card-list').html(html); | $('#ce-card-list').html(html); | ||
// 重新初始化排序 | |||
$('#ce-card-list').sortable('refresh'); | |||
}, | }, | ||
| 第643行: | 第744行: | ||
var name = i === 0 ? '主卡牌 (' + deck + ')' : '变体 ' + i + ' (灵光一闪)'; | var name = i === 0 ? '主卡牌 (' + deck + ')' : '变体 ' + i + ' (灵光一闪)'; | ||
var selected = (i === this.currentVariantIndex) ? ' selected' : ''; | var selected = (i === this.currentVariantIndex) ? ' selected' : ''; | ||
html += '<div class="ce-list-item' + selected + '" data-index="' + i + '">' + | var noSort = i === 0 ? ' ce-no-sort' : ''; // 主卡牌不可排序 | ||
html += '<div class="ce-list-item' + selected + noSort + | |||
'" data-index="' + i + | |||
'" data-original-index="' + i + '">' + | |||
(i > 0 ? '<span class="ce-drag-handle">≡ </span>' : '') + | |||
name + '</div>'; | name + '</div>'; | ||
} | } | ||
} | } | ||
$('#ce-variant-list').html(html); | $('#ce-variant-list').html(html); | ||
// 重新初始化排序 | |||
$('#ce-variant-list').sortable('refresh'); | |||
}, | }, | ||
| 第701行: | 第809行: | ||
generateLuaCode: function() { | generateLuaCode: function() { | ||
if (!this.currentCharacter || !this.characters[this.currentCharacter]) { | |||
return '-- 请先选择一个战斗员'; | |||
} | |||
var cards = this.characters[this.currentCharacter]; | |||
if ( | if (cards.length === 0) { | ||
return '-- ' + this.currentCharacter + ' 暂无卡牌数据'; | |||
} | } | ||
// | var lua = 'local p = {}\n\n'; | ||
// 生成 cardOrder | |||
var | lua += 'local cardOrder = {\n'; | ||
if ( | for (var i = 0; i < cards.length; i++) { | ||
lua += ' | var card = cards[i]; | ||
if (card.variants.length > 0 && card.variants[0]['卡组'] !== '衍生卡牌') { | |||
lua += ' "' + this.escapeLuaString(card.name) + '",\n'; | |||
} | } | ||
} | } | ||
lua += '}\n\n'; | |||
// 生成 card 数据 | |||
lua += 'local card = {\n'; | |||
for (var i = 0; i < cards.length; i++) { | |||
var card = cards[i]; | |||
lua += ' ["' + this.escapeLuaString(card.name) + '"] = {\n'; | |||
for (var j = 0; j < card.variants.length; j++) { | |||
var variant = card.variants[j]; | |||
lua += ' {\n'; | |||
var fieldOrder = ['art', '卡组', '属性', '稀有度', 'AP', '机制', '类型', '描述', '衍生卡牌']; | |||
// 如果是变体卡牌(非第一个且卡组为灵光一闪),只输出有差异的字段 | |||
if (j > 0 && variant['卡组'] === '灵光一闪') { | |||
var mainVariant = card.variants[0]; | |||
for (var k = 0; k < fieldOrder.length; k++) { | |||
var field = fieldOrder[k]; | |||
// 卡组字段必须输出 | |||
if (field === '卡组') { | |||
lua += ' ["' + field + '"] = "灵光一闪",\n'; | |||
continue; | |||
} | |||
// 只输出与主卡牌不同的字段 | |||
if (variant[field] !== undefined && variant[field] !== null && | |||
variant[field] !== mainVariant[field]) { | |||
var value = variant[field]; | |||
if (typeof value === 'string' && value.trim() !== '') { | |||
lua += ' ["' + field + '"] = "' + | |||
this.escapeLuaString(value) + '",\n'; | |||
} else if (typeof value === 'number' || value === 'X') { | |||
if (value === 'X') { | |||
lua += ' ["' + field + '"] = "X",\n'; | |||
} else { | |||
lua += ' ["' + field + '"] = ' + value + ',\n'; | |||
} | |||
} | |||
} | |||
} | |||
} else { | } else { | ||
lua += ' ["' + field + '"] = ' + value + ',\n'; | // 主卡牌或非灵光一闪卡牌,输出所有非空字段 | ||
for (var k = 0; k < fieldOrder.length; k++) { | |||
var field = fieldOrder[k]; | |||
if (variant[field] !== undefined && variant[field] !== null) { | |||
var value = variant[field]; | |||
if (typeof value === 'string' && value.trim() !== '') { | |||
lua += ' ["' + field + '"] = "' + | |||
this.escapeLuaString(value) + '",\n'; | |||
} else if (typeof value === 'number' || value === 'X') { | |||
if (value === 'X') { | |||
lua += ' ["' + field + '"] = "X",\n'; | |||
} else { | |||
lua += ' ["' + field + '"] = ' + value + ',\n'; | |||
} | |||
} | |||
} | |||
} | |||
} | } | ||
lua += ' },\n'; | |||
} | } | ||
lua += ' },\n'; | |||
} | } | ||
lua += '}\n\n'; | |||
lua += 'p.card = card\n'; | |||
lua += 'p.cardOrder = cardOrder\n\n'; | |||
lua += 'return p\n'; | |||
return lua; | |||
}, | |||
}, | |||
updateCode: function() { | updateCode: function() { | ||
| 第819行: | 第927行: | ||
// 提取每个卡牌 | // 提取每个卡牌 | ||
var cardRegex = / | var cardRegex = /$$"(.+?)"$$\s*=\s*\{([\s\S]*?)\n \},/g; | ||
var match; | var match; | ||
| 第840行: | 第948行: | ||
// 提取字段 | // 提取字段 | ||
var fieldRegex = / | var fieldRegex = /$$"(.+?)"$$\s*=\s*(.+?),/g; | ||
var fieldMatch; | var fieldMatch; | ||
| 第866行: | 第974行: | ||
cards.push(card); | cards.push(card); | ||
} | } | ||
} | |||
// 根据 cardOrder 排序 | |||
var orderMatch = luaContent.match(/local\s+cardOrder\s*=\s*\{([\s\S]*?)\}/); | |||
if (orderMatch) { | |||
var orderData = orderMatch[1]; | |||
var orderRegex = /"(.+?)"/g; | |||
var order = []; | |||
var orderMatch2; | |||
while ((orderMatch2 = orderRegex.exec(orderData)) !== null) { | |||
order.push(orderMatch2[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\')); | |||
} | |||
// 按照 cardOrder 重新排序 | |||
var sortedCards = []; | |||
for (var i = 0; i < order.length; i++) { | |||
var cardName = order[i]; | |||
for (var j = 0; j < cards.length; j++) { | |||
if (cards[j].name === cardName) { | |||
sortedCards.push(cards[j]); | |||
break; | |||
} | |||
} | |||
} | |||
// 添加不在 cardOrder 中的卡牌(比如衍生卡牌) | |||
for (var j = 0; j < cards.length; j++) { | |||
var found = false; | |||
for (var i = 0; i < sortedCards.length; i++) { | |||
if (sortedCards[i].name === cards[j].name) { | |||
found = true; | |||
break; | |||
} | |||
} | |||
if (!found) { | |||
sortedCards.push(cards[j]); | |||
} | |||
} | |||
cards = sortedCards; | |||
} | } | ||
} catch (e) { | } catch (e) { | ||
| 第909行: | 第1,058行: | ||
} | } | ||
}; | }; | ||
// 添加CSS样式 | |||
var style = document.createElement('style'); | |||
style.textContent = [ | |||
'#card-editor { margin: 20px 0; }', | |||
'.ce-container { display: flex; gap: 20px; }', | |||
'.ce-left, .ce-middle, .ce-right { flex: 1; }', | |||
'.ce-group { margin-bottom: 20px; background: #f8f9fa; border: 1px solid #ddd; border-radius: 5px; padding: 15px; }', | |||
'.ce-group-title { font-weight: bold; font-size: 16px; margin-bottom: 10px; color: #333; }', | |||
'.ce-row { display: flex; align-items: center; margin-bottom: 10px; }', | |||
'.ce-label { width: 100px; font-weight: bold; color: #555; flex-shrink: 0; }', | |||
'.ce-input, .ce-textarea, .ce-select-input { flex: 1; padding: 8px 12px; border: 1px solid #ccc; border-radius: 3px; font-size: 14px; }', | |||
'.ce-textarea { min-height: 100px; resize: vertical; }', | |||
'.ce-code { width: 100%; height: 500px; font-family: monospace; font-size: 12px; }', | |||
'.ce-buttons { text-align: center; margin-top: 15px; }', | |||
'.ce-btn { display: inline-block; padding: 8px 16px; margin: 0 5px; background: #36c; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; }', | |||
'.ce-btn:hover { background: #447ff5; }', | |||
'.ce-btn-primary { background: #0645ad; }', | |||
'.ce-btn-primary:hover { background: #0b61d4; }', | |||
'.ce-btn-danger { background: #d33; }', | |||
'.ce-btn-danger:hover { background: #e74c3c; }', | |||
'.ce-btn-small { padding: 4px 8px; font-size: 12px; margin: 0 2px; }', | |||
'.ce-btn-blue { background: #2196F3; }', | |||
'.ce-btn-green { background: #4CAF50; }', | |||
'.ce-btn-lime { background: #8BC34A; }', | |||
'.ce-btn-orange { background: #FF9800; }', | |||
'.ce-list-container { height: 250px; overflow-y: auto; border: 1px solid #ddd; border-radius: 3px; background: white; }', | |||
'.ce-list { padding: 10px; }', | |||
'.ce-list-item { padding: 8px 12px; margin-bottom: 5px; background: #f5f5f5; border-radius: 3px; cursor: pointer; transition: background 0.2s; }', | |||
'.ce-list-item:hover { background: #e8e8e8; }', | |||
'.ce-list-item.selected { background: #e3f2fd; color: #1976D2; font-weight: bold; }', | |||
'.ce-loading { text-align: center; padding: 20px; color: #666; }', | |||
'.ce-error { text-align: center; padding: 20px; color: #d33; }', | |||
'.ce-select-wrapper { position: relative; flex: 1; }', | |||
'.ce-select-input { cursor: pointer; background: white url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiI+PHBhdGggZmlsbD0iIzMzMyIgZD0iTTExIDQgNiA5IDEgNCIvPjwvc3ZnPg==") no-repeat right 10px center; background-size: 12px; padding-right: 30px; }', | |||
'.ce-select-dropdown { position: absolute; top: 100%; left: 0; right: 0; background: white; border: 1px solid #ccc; border-top: none; border-radius: 0 0 3px 3px; max-height: 200px; overflow-y: auto; z-index: 1000; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }', | |||
'.ce-select-option { padding: 8px 12px; cursor: pointer; transition: background 0.2s; }', | |||
'.ce-select-option:hover { background: #f5f5f5; }', | |||
'.ce-desc-wrapper { flex: 1; }', | |||
'.ce-format-buttons { margin-bottom: 5px; }', | |||
'input[disabled], textarea[disabled], select[disabled] { background-color: #f0f0f0; cursor: not-allowed; }', | |||
'.ce-drag-handle { color: #999; margin-right: 5px; cursor: move; font-weight: bold; }', | |||
'.ce-no-sort { cursor: default; }', | |||
'.ce-no-sort .ce-drag-handle { display: none; }' | |||
].join('\n'); | |||
document.head.appendChild(style); | |||
// 初始化编辑器 | // 初始化编辑器 | ||
2025年10月2日 (四) 13:18的版本
(function() {
// 确保jQuery和MediaWiki API已加载
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'jquery.ui'], function() {
$(document).ready(function() {
// 卡牌编辑器对象
var CardEditor = {
characters: {}, // 存储所有角色的卡牌数据
currentCharacter: null, // 当前选中的角色
currentCard: null,
currentVariantIndex: null,
api: new mw.Api(),
init: function() {
// 清空占位符
$('#mw-content-text').empty();
this.createUI();
this.bindEvents();
this.loadCharacterList();
},
createUI: function() {
var html = [
'<div id="card-editor">',
'<div class="ce-container">',
// 左侧输入区
'<div class="ce-left">',
// 角色选择区
'<div class="ce-group">',
'<div class="ce-group-title">战斗员选择</div>',
'<div class="ce-row">',
'<div class="ce-label">当前战斗员:</div>',
'<div class="ce-select-wrapper">',
'<input type="text" id="ce-character-select" class="ce-select-input" placeholder="选择或输入新战斗员..." readonly>',
'<div class="ce-select-dropdown" id="ce-character-dropdown" style="display:none;"></div>',
'</div>',
'<span class="ce-btn ce-btn-small ce-btn-primary" id="ce-new-character" style="margin-left: 10px;">新建</span>',
'</div>',
'</div>',
'<div class="ce-group">',
'<div class="ce-group-title">卡牌数据</div>',
'<div class="ce-form">',
// 名称
'<div class="ce-row">',
'<div class="ce-label">卡牌名称:</div>',
'<input type="text" id="ce-name" class="ce-input" placeholder="请输入卡牌名称...">',
'</div>',
// 卡组
'<div class="ce-row">',
'<div class="ce-label">卡组类型:</div>',
'<div class="ce-select-wrapper">',
'<input type="text" id="ce-deck" class="ce-select-input" placeholder="点击选择或输入..." readonly>',
'<div class="ce-select-dropdown" id="ce-deck-dropdown" style="display:none;">',
'<div class="ce-select-option" data-value="">请选择</div>',
'<div class="ce-select-option" data-value="起始卡牌">起始卡牌</div>',
'<div class="ce-select-option" data-value="独特卡牌">独特卡牌</div>',
'<div class="ce-select-option" data-value="灵光一闪">灵光一闪</div>',
'<div class="ce-select-option" data-value="衍生卡牌">衍生卡牌</div>',
'</div>',
'</div>',
'</div>',
// 图片
'<div class="ce-row">',
'<div class="ce-label">图片文件:</div>',
'<input type="text" id="ce-art" class="ce-input" placeholder="输入图片文件名...">',
'</div>',
// 属性
'<div class="ce-row">',
'<div class="ce-label">属性:</div>',
'<div class="ce-select-wrapper">',
'<input type="text" id="ce-attr" class="ce-select-input" placeholder="点击选择..." readonly>',
'<div class="ce-select-dropdown" id="ce-attr-dropdown" style="display:none;">',
'<div class="ce-select-option" data-value="">请选择</div>',
'<div class="ce-select-option" data-value="热情">热情</div>',
'<div class="ce-select-option" data-value="秩序">秩序</div>',
'<div class="ce-select-option" data-value="正义">正义</div>',
'<div class="ce-select-option" data-value="本能">本能</div>',
'<div class="ce-select-option" data-value="虚无">虚无</div>',
'</div>',
'</div>',
'</div>',
// 稀有度
'<div class="ce-row">',
'<div class="ce-label">稀有度:</div>',
'<div class="ce-select-wrapper">',
'<input type="text" id="ce-rarity" class="ce-select-input" placeholder="点击选择..." readonly>',
'<div class="ce-select-dropdown" id="ce-rarity-dropdown" style="display:none;">',
'<div class="ce-select-option" data-value="">请选择</div>',
'<div class="ce-select-option" data-value="白">白</div>',
'<div class="ce-select-option" data-value="蓝">蓝</div>',
'<div class="ce-select-option" data-value="橙">橙</div>',
'<div class="ce-select-option" data-value="彩">彩</div>',
'</div>',
'</div>',
'</div>',
// AP
'<div class="ce-row">',
'<div class="ce-label">AP (行动点):</div>',
'<input type="text" id="ce-ap" class="ce-input" placeholder="输入AP数值...">',
'</div>',
// 类型
'<div class="ce-row">',
'<div class="ce-label">卡牌类型:</div>',
'<div class="ce-select-wrapper">',
'<input type="text" id="ce-type" class="ce-select-input" placeholder="点击选择..." readonly>',
'<div class="ce-select-dropdown" id="ce-type-dropdown" style="display:none;">',
'<div class="ce-select-option" data-value="">请选择</div>',
'<div class="ce-select-option" data-value="攻击">攻击</div>',
'<div class="ce-select-option" data-value="技能">技能</div>',
'<div class="ce-select-option" data-value="强化">强化</div>',
'<div class="ce-select-option" data-value="状态异常">状态异常</div>',
'</div>',
'</div>',
'</div>',
// 机制
'<div class="ce-row">',
'<div class="ce-label">卡牌机制:</div>',
'<input type="text" id="ce-mechanism" class="ce-input" placeholder="请输入卡牌机制...">',
'</div>',
// 描述
'<div class="ce-row">',
'<div class="ce-label">卡牌描述:</div>',
'<div class="ce-desc-wrapper">',
'<div class="ce-format-buttons">',
'<span class="ce-btn ce-btn-small ce-btn-blue" id="ce-blue-text">蓝色文本</span>',
'<span class="ce-btn ce-btn-small ce-btn-green" id="ce-green-text">绿色文本</span>',
'<span class="ce-btn ce-btn-small ce-btn-lime" id="ce-green-stroke">绿色描边</span>',
'<span class="ce-btn ce-btn-small ce-btn-orange" id="ce-insert-br">插入换行</span>',
'</div>',
'<textarea id="ce-desc" class="ce-textarea" placeholder="请输入卡牌描述..."></textarea>',
'</div>',
'</div>',
// 衍生卡牌
'<div class="ce-row">',
'<div class="ce-label">衍生卡牌:</div>',
'<input type="text" id="ce-derived" class="ce-input" placeholder="请输入衍生卡牌...">',
'</div>',
'</div>',
'</div>',
// 按钮区
'<div class="ce-buttons">',
'<span class="ce-btn ce-btn-primary" id="ce-add-card">添加卡牌</span>',
'<span class="ce-btn ce-btn-primary" id="ce-add-variant">添加变体</span>',
'<span class="ce-btn" id="ce-save-data">保存数据</span>',
'<span class="ce-btn" id="ce-clear-form">清空表单</span>',
'</div>',
'</div>',
// 中间列表区
'<div class="ce-middle">',
'<div class="ce-group">',
'<div class="ce-group-title">卡牌列表 <span class="ce-hint">(拖动调整顺序)</span></div>',
'<div class="ce-list-container">',
'<div id="ce-card-list" class="ce-list ce-sortable"></div>',
'</div>',
'</div>',
'<div class="ce-group">',
'<div class="ce-group-title">变体列表 <span class="ce-hint">(拖动调整顺序)</span></div>',
'<div class="ce-list-container">',
'<div id="ce-variant-list" class="ce-list ce-sortable"></div>',
'</div>',
'</div>',
'<div class="ce-buttons">',
'<span class="ce-btn ce-btn-danger" id="ce-delete-card">删除卡牌</span>',
'<span class="ce-btn ce-btn-danger" id="ce-delete-variant">删除变体</span>',
'</div>',
'</div>',
// 右侧代码区
'<div class="ce-right">',
'<div class="ce-group">',
'<div class="ce-group-title">Lua代码预览 - <span id="ce-code-character">未选择战斗员</span></div>',
'<textarea id="ce-code-display" class="ce-code" readonly></textarea>',
'</div>',
'<div class="ce-buttons">',
'<span class="ce-btn" id="ce-reload-character">重新加载</span>',
'<span class="ce-btn ce-btn-primary" id="ce-save-to-wiki">保存到Wiki</span>',
'<span class="ce-btn" id="ce-copy-code">复制代码</span>',
'<span class="ce-btn ce-btn-danger" id="ce-delete-character">删除战斗员</span>',
'</div>',
'</div>',
'</div>',
'</div>',
'<style>',
'.ce-hint { font-size: 12px; color: #666; font-weight: normal; }',
'.ce-list-item { position: relative; cursor: move; }',
'.ce-list-item.ui-sortable-helper { background: #e3f2fd; border: 2px solid #2196F3; }',
'.ce-list-item.ui-sortable-placeholder { background: #f5f5f5; border: 2px dashed #ccc; visibility: visible !important; }',
'.ce-list-item.selected { background: #e3f2fd; }',
'</style>'
].join('');
$('#mw-content-text').html(html);
},
bindEvents: function() {
var self = this;
// 自定义下拉框事件
$(document).on('click', '.ce-select-input', function() {
var dropdown = $(this).siblings('.ce-select-dropdown');
$('.ce-select-dropdown').not(dropdown).hide();
dropdown.toggle();
});
$(document).on('click', '.ce-select-option', function() {
var value = $(this).data('value');
var text = $(this).text();
var input = $(this).parent().siblings('.ce-select-input');
input.val(value || '');
input.data('value', value);
$(this).parent().hide();
});
// 战斗员选择
$(document).on('click', '#ce-character-dropdown .ce-select-option', function() {
var character = $(this).data('value');
self.selectCharacter(character);
});
// 点击其他地方关闭下拉框
$(document).on('click', function(e) {
if (!$(e.target).closest('.ce-select-wrapper').length) {
$('.ce-select-dropdown').hide();
}
});
// 按钮事件
$('#ce-new-character').on('click', function() { self.createNewCharacter(); });
$('#ce-add-card').on('click', function() { self.addCard(); });
$('#ce-add-variant').on('click', function() { self.addVariant(); });
$('#ce-save-data').on('click', function() { self.saveData(); });
$('#ce-clear-form').on('click', function() { self.clearForm(); });
$('#ce-delete-card').on('click', function() { self.deleteCard(); });
$('#ce-delete-variant').on('click', function() { self.deleteVariant(); });
$('#ce-reload-character').on('click', function() { self.reloadCurrentCharacter(); });
$('#ce-save-to-wiki').on('click', function() { self.saveToWiki(); });
$('#ce-copy-code').on('click', function() { self.copyCode(); });
$('#ce-delete-character').on('click', function() { self.deleteCharacter(); });
// 文本格式化按钮
$('#ce-blue-text').on('click', function() { self.insertTextFormat('蓝'); });
$('#ce-green-text').on('click', function() { self.insertTextFormat('绿'); });
$('#ce-green-stroke').on('click', function() { self.insertStrokeFormat(); });
$('#ce-insert-br').on('click', function() { self.insertBr(); });
// 列表项点击事件
$(document).on('click', '.ce-list-item', function() {
var $this = $(this);
var listId = $this.parent().attr('id');
$('.ce-list-item').removeClass('selected');
$this.addClass('selected');
if (listId === 'ce-card-list') {
var index = $this.data('index');
self.onCardSelected(index);
} else if (listId === 'ce-variant-list') {
var index = $this.data('index');
self.onVariantSelected(index);
}
});
// 初始化拖拽排序
this.initSortable();
},
initSortable: function() {
var self = this;
// 卡牌列表排序
$('#ce-card-list').sortable({
items: '.ce-list-item',
placeholder: 'ce-list-item ui-sortable-placeholder',
update: function(event, ui) {
self.onCardListReorder();
}
});
// 变体列表排序
$('#ce-variant-list').sortable({
items: '.ce-list-item:not(.ce-no-sort)',
placeholder: 'ce-list-item ui-sortable-placeholder',
update: function(event, ui) {
self.onVariantListReorder();
}
});
},
onCardListReorder: function() {
if (!this.currentCharacter) return;
var newOrder = [];
$('#ce-card-list .ce-list-item').each(function() {
var index = $(this).data('index');
newOrder.push(index);
});
// 重新排序卡牌数组
var cards = this.characters[this.currentCharacter];
var reorderedCards = [];
for (var i = 0; i < newOrder.length; i++) {
reorderedCards.push(cards[newOrder[i]]);
}
this.characters[this.currentCharacter] = reorderedCards;
// 更新当前选中的卡牌引用
if (this.currentCard) {
var currentCardIndex = reorderedCards.indexOf(this.currentCard);
if (currentCardIndex === -1) {
this.currentCard = null;
this.currentVariantIndex = null;
}
}
this.updateCardList();
this.updateCode();
},
onVariantListReorder: function() {
if (!this.currentCard) return;
var newOrder = [];
$('#ce-variant-list .ce-list-item').each(function() {
var index = $(this).data('index');
newOrder.push(index);
});
// 重新排序变体数组
var variants = this.currentCard.variants;
var reorderedVariants = [];
for (var i = 0; i < newOrder.length; i++) {
reorderedVariants.push(variants[newOrder[i]]);
}
this.currentCard.variants = reorderedVariants;
// 更新当前选中的变体索引
if (this.currentVariantIndex !== null) {
var oldVariant = variants[this.currentVariantIndex];
this.currentVariantIndex = reorderedVariants.indexOf(oldVariant);
}
this.updateVariantList();
this.updateCode();
},
selectCharacter: function(character) {
this.currentCharacter = character;
$('#ce-character-select').val(character);
$('#ce-code-character').text(character);
// 清空当前数据
this.currentCard = null;
this.currentVariantIndex = null;
this.clearForm();
// 更新显示
this.updateCardList();
this.updateVariantList();
this.updateCode();
},
createNewCharacter: function() {
var character = prompt('请输入新战斗员的名称:');
if (!character || character.trim() === '') return;
character = character.trim();
// 检查是否已存在
if (this.characters[character]) {
alert('战斗员 "' + character + '" 已存在!');
return;
}
// 创建新战斗员
this.characters[character] = [];
this.updateCharacterDropdown();
this.selectCharacter(character);
alert('新战斗员 "' + character + '" 创建成功!');
},
deleteCharacter: function() {
if (!this.currentCharacter) {
alert('请先选择一个战斗员!');
return;
}
if (!confirm('确定要删除战斗员 "' + this.currentCharacter + '" 及其所有卡牌吗?\n此操作不可恢复!')) {
return;
}
delete this.characters[this.currentCharacter];
this.currentCharacter = null;
this.currentCard = null;
this.currentVariantIndex = null;
$('#ce-character-select').val('');
$('#ce-code-character').text('未选择战斗员');
this.updateCharacterDropdown();
this.clearForm();
this.updateCardList();
this.updateVariantList();
this.updateCode();
alert('战斗员删除成功!');
},
loadCharacterList: function() {
var self = this;
$('#ce-character-dropdown').html('<div class="ce-loading">正在加载战斗员列表...</div>');
// 搜索所有 模块:卡牌/+角色名 的页面
this.api.get({
action: 'query',
list: 'allpages',
apprefix: '卡牌/',
apnamespace: 828, // Module namespace
aplimit: 'max'
}).then(function(data) {
if (data.query && data.query.allpages) {
var pages = data.query.allpages;
var characterPromises = [];
// 初始化空字典
self.characters = {};
for (var i = 0; i < pages.length; i++) {
var title = pages[i].title;
var character = title.replace('模块:卡牌/', '');
self.characters[character] = [];
// 加载每个角色的数据
characterPromises.push(self.loadCharacterData(character));
}
// 等待所有角色数据加载完成
$.when.apply($, characterPromises).then(function() {
self.updateCharacterDropdown();
});
} else {
self.updateCharacterDropdown();
}
}).fail(function() {
$('#ce-character-dropdown').html('<div class="ce-error">加载失败,请稍后重试</div>');
});
},
loadCharacterData: function(character) {
var self = this;
var pageName = '模块:卡牌/' + character;
return this.api.get({
action: 'query',
prop: 'revisions',
titles: pageName,
rvprop: 'content',
rvlimit: 1
}).then(function(data) {
if (data.query && data.query.pages) {
for (var pageId in data.query.pages) {
var page = data.query.pages[pageId];
if (page.revisions && page.revisions[0]) {
var content = page.revisions[0]['*'];
self.characters[character] = self.parseCardData(content);
}
}
}
});
},
reloadCurrentCharacter: function() {
if (!this.currentCharacter) {
alert('请先选择一个战斗员!');
return;
}
var self = this;
this.loadCharacterData(this.currentCharacter).then(function() {
self.updateCardList();
self.updateVariantList();
self.updateCode();
alert('战斗员 "' + self.currentCharacter + '" 的数据已重新加载!');
});
},
updateCharacterDropdown: function() {
var html = '<div class="ce-select-option" data-value="">请选择战斗员</div>';
var characters = Object.keys(this.characters).sort();
for (var i = 0; i < characters.length; i++) {
var character = characters[i];
var cardCount = this.characters[character].length;
html += '<div class="ce-select-option" data-value="' + character + '">' +
character + ' (' + cardCount + ' 张卡牌)</div>';
}
$('#ce-character-dropdown').html(html);
},
getCardData: function() {
var variant = {};
var art = $('#ce-art').val().trim();
if (art) variant.art = art;
var deck = $('#ce-deck').val().trim();
if (deck) variant['卡组'] = deck;
var attr = $('#ce-attr').val().trim();
if (attr) variant['属性'] = attr;
var rarity = $('#ce-rarity').val().trim();
if (rarity) variant['稀有度'] = rarity;
var ap = $('#ce-ap').val().trim();
if (ap) {
if (ap.toUpperCase() === 'X') {
variant.AP = 'X';
} else if (/^\d+$/.test(ap)) {
variant.AP = parseInt(ap, 10);
}
}
var mechanism = $('#ce-mechanism').val().trim();
if (mechanism) variant['机制'] = mechanism;
var type = $('#ce-type').val().trim();
if (type) variant['类型'] = type;
var desc = $('#ce-desc').val().trim();
if (desc) variant['描述'] = desc;
var derived = $('#ce-derived').val().trim();
if (derived) variant['衍生卡牌'] = derived;
return {
name: $('#ce-name').val().trim(),
variants: [variant]
};
},
setCardData: function(card, variantIndex) {
$('#ce-name').val(card.name || '');
if (card.variants && card.variants[variantIndex]) {
var variant = card.variants[variantIndex];
var isVariant = variant['卡组'] === '灵光一闪' && variantIndex > 0;
// 设置字段是否可编辑
$('#ce-name').prop('disabled', isVariant);
$('#ce-art').prop('disabled', isVariant);
$('#ce-attr').prop('disabled', isVariant);
$('#ce-rarity').prop('disabled', isVariant);
$('#ce-derived').prop('disabled', isVariant);
// 设置值
$('#ce-art').val(variant.art || '');
$('#ce-deck').val(variant['卡组'] || '');
$('#ce-attr').val(variant['属性'] || '');
$('#ce-rarity').val(variant['稀有度'] || '');
var ap = variant.AP;
$('#ce-ap').val(ap !== undefined ? String(ap) : '');
$('#ce-mechanism').val(variant['机制'] || '');
$('#ce-type').val(variant['类型'] || '');
$('#ce-desc').val(variant['描述'] || '');
$('#ce-derived').val(variant['衍生卡牌'] || '');
}
},
clearForm: function() {
$('#ce-name').val('').prop('disabled', false);
$('#ce-art').val('').prop('disabled', false);
$('#ce-deck').val('');
$('#ce-attr').val('').prop('disabled', false);
$('#ce-rarity').val('').prop('disabled', false);
$('#ce-ap').val('');
$('#ce-mechanism').val('');
$('#ce-type').val('');
$('#ce-desc').val('');
$('#ce-derived').val('').prop('disabled', false);
},
addCard: function() {
if (!this.currentCharacter) {
alert('请先选择或创建一个战斗员!');
return;
}
var cardData = this.getCardData();
if (!cardData.name) {
alert('卡牌名称不能为空!');
return;
}
this.characters[this.currentCharacter].push(cardData);
this.currentCard = cardData;
this.currentVariantIndex = 0;
this.updateCardList();
this.updateVariantList();
this.updateCode();
this.clearForm();
alert('卡牌 "' + cardData.name + '" 添加成功!');
},
addVariant: function() {
if (!this.currentCard) {
alert('请先选择一个卡牌!');
return;
}
var variantData = this.getCardData();
var variant = variantData.variants[0];
variant['卡组'] = '灵光一闪';
this.currentCard.variants.push(variant);
this.currentVariantIndex = this.currentCard.variants.length - 1;
this.updateVariantList();
this.updateCode();
alert('为 "' + this.currentCard.name + '" 添加变体成功!');
},
saveData: function() {
if (!this.currentCard || this.currentVariantIndex === null) {
alert('请先选择要保存的卡牌或变体!');
return;
}
var cardData = this.getCardData();
var isVariant = this.currentVariantIndex > 0;
if (isVariant) {
var variant = cardData.variants[0];
variant['卡组'] = '灵光一闪';
// 不再自动保留主卡牌的属性
this.currentCard.variants[this.currentVariantIndex] = variant;
} else {
this.currentCard.name = cardData.name;
this.currentCard.variants[0] = cardData.variants[0];
}
this.updateCardList();
this.updateVariantList();
this.updateCode();
alert('数据保存成功!');
},
deleteCard: function() {
if (!this.currentCard || !this.currentCharacter) {
alert('请先选择要删除的卡牌!');
return;
}
if (confirm('确定要删除卡牌 "' + this.currentCard.name + '" 吗?')) {
var cards = this.characters[this.currentCharacter];
var index = cards.indexOf(this.currentCard);
cards.splice(index, 1);
this.currentCard = null;
this.currentVariantIndex = null;
this.updateCardList();
this.updateVariantList();
this.updateCode();
this.clearForm();
alert('卡牌删除成功!');
}
},
deleteVariant: function() {
if (!this.currentCard || this.currentVariantIndex === null) {
alert('请先选择要删除的变体!');
return;
}
if (this.currentVariantIndex === 0) {
alert('不能删除主卡牌变体!');
return;
}
if (confirm('确定要删除这个变体吗?')) {
this.currentCard.variants.splice(this.currentVariantIndex, 1);
this.currentVariantIndex = 0;
this.updateVariantList();
this.updateCode();
this.setCardData(this.currentCard, 0);
alert('变体删除成功!');
}
},
onCardSelected: function(index) {
if (!this.currentCharacter) return;
// 由于重排序后,index对应的是DOM中的位置,需要找到对应的实际卡牌
var $item = $('#ce-card-list .ce-list-item').eq(index);
var actualIndex = $item.data('original-index');
this.currentCard = this.characters[this.currentCharacter][actualIndex];
this.currentVariantIndex = 0;
this.updateVariantList();
this.setCardData(this.currentCard, 0);
},
onVariantSelected: function(index) {
if (!this.currentCard) return;
// 由于重排序后,index对应的是DOM中的位置,需要找到对应的实际变体
var $item = $('#ce-variant-list .ce-list-item').eq(index);
var actualIndex = $item.data('original-index');
this.currentVariantIndex = actualIndex;
this.setCardData(this.currentCard, actualIndex);
},
updateCardList: function() {
var html = '';
if (this.currentCharacter && this.characters[this.currentCharacter]) {
var cards = this.characters[this.currentCharacter];
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
var selected = (card === this.currentCard) ? ' selected' : '';
html += '<div class="ce-list-item' + selected + '" data-index="' + i +
'" data-original-index="' + i + '">' +
'<span class="ce-drag-handle">≡ </span>' +
card.name + '</div>';
}
}
if (html === '') {
html = '<div class="ce-loading">暂无卡牌数据</div>';
}
$('#ce-card-list').html(html);
// 重新初始化排序
$('#ce-card-list').sortable('refresh');
},
updateVariantList: function() {
var html = '';
if (this.currentCard) {
for (var i = 0; i < this.currentCard.variants.length; i++) {
var variant = this.currentCard.variants[i];
var deck = variant['卡组'] || '未知';
var name = i === 0 ? '主卡牌 (' + deck + ')' : '变体 ' + i + ' (灵光一闪)';
var selected = (i === this.currentVariantIndex) ? ' selected' : '';
var noSort = i === 0 ? ' ce-no-sort' : ''; // 主卡牌不可排序
html += '<div class="ce-list-item' + selected + noSort +
'" data-index="' + i +
'" data-original-index="' + i + '">' +
(i > 0 ? '<span class="ce-drag-handle">≡ </span>' : '') +
name + '</div>';
}
}
$('#ce-variant-list').html(html);
// 重新初始化排序
$('#ce-variant-list').sortable('refresh');
},
insertTextFormat: function(color) {
var textarea = document.getElementById('ce-desc');
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var text = textarea.value;
var selectedText = text.substring(start, end);
var newText = '{{文本|' + color + '|' + selectedText + '}}';
textarea.value = text.substring(0, start) + newText + text.substring(end);
// 设置光标位置
var cursorPos = start + newText.length;
textarea.setSelectionRange(cursorPos, cursorPos);
textarea.focus();
},
insertStrokeFormat: function() {
var textarea = document.getElementById('ce-desc');
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var text = textarea.value;
var selectedText = text.substring(start, end);
var newText = '{{描边|绿|' + selectedText + '}}';
textarea.value = text.substring(0, start) + newText + text.substring(end);
var cursorPos = start + newText.length;
textarea.setSelectionRange(cursorPos, cursorPos);
textarea.focus();
},
insertBr: function() {
var textarea = document.getElementById('ce-desc');
var start = textarea.selectionStart;
var text = textarea.value;
textarea.value = text.substring(0, start) + '<br>' + text.substring(start);
var cursorPos = start + 4;
textarea.setSelectionRange(cursorPos, cursorPos);
textarea.focus();
},
escapeLuaString: function(s) {
if (typeof s !== 'string') return s;
return s.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n');
},
generateLuaCode: function() {
if (!this.currentCharacter || !this.characters[this.currentCharacter]) {
return '-- 请先选择一个战斗员';
}
var cards = this.characters[this.currentCharacter];
if (cards.length === 0) {
return '-- ' + this.currentCharacter + ' 暂无卡牌数据';
}
var lua = 'local p = {}\n\n';
// 生成 cardOrder
lua += 'local cardOrder = {\n';
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
if (card.variants.length > 0 && card.variants[0]['卡组'] !== '衍生卡牌') {
lua += ' "' + this.escapeLuaString(card.name) + '",\n';
}
}
lua += '}\n\n';
// 生成 card 数据
lua += 'local card = {\n';
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
lua += ' ["' + this.escapeLuaString(card.name) + '"] = {\n';
for (var j = 0; j < card.variants.length; j++) {
var variant = card.variants[j];
lua += ' {\n';
var fieldOrder = ['art', '卡组', '属性', '稀有度', 'AP', '机制', '类型', '描述', '衍生卡牌'];
// 如果是变体卡牌(非第一个且卡组为灵光一闪),只输出有差异的字段
if (j > 0 && variant['卡组'] === '灵光一闪') {
var mainVariant = card.variants[0];
for (var k = 0; k < fieldOrder.length; k++) {
var field = fieldOrder[k];
// 卡组字段必须输出
if (field === '卡组') {
lua += ' ["' + field + '"] = "灵光一闪",\n';
continue;
}
// 只输出与主卡牌不同的字段
if (variant[field] !== undefined && variant[field] !== null &&
variant[field] !== mainVariant[field]) {
var value = variant[field];
if (typeof value === 'string' && value.trim() !== '') {
lua += ' ["' + field + '"] = "' +
this.escapeLuaString(value) + '",\n';
} else if (typeof value === 'number' || value === 'X') {
if (value === 'X') {
lua += ' ["' + field + '"] = "X",\n';
} else {
lua += ' ["' + field + '"] = ' + value + ',\n';
}
}
}
}
} else {
// 主卡牌或非灵光一闪卡牌,输出所有非空字段
for (var k = 0; k < fieldOrder.length; k++) {
var field = fieldOrder[k];
if (variant[field] !== undefined && variant[field] !== null) {
var value = variant[field];
if (typeof value === 'string' && value.trim() !== '') {
lua += ' ["' + field + '"] = "' +
this.escapeLuaString(value) + '",\n';
} else if (typeof value === 'number' || value === 'X') {
if (value === 'X') {
lua += ' ["' + field + '"] = "X",\n';
} else {
lua += ' ["' + field + '"] = ' + value + ',\n';
}
}
}
}
}
lua += ' },\n';
}
lua += ' },\n';
}
lua += '}\n\n';
lua += 'p.card = card\n';
lua += 'p.cardOrder = cardOrder\n\n';
lua += 'return p\n';
return lua;
},
updateCode: function() {
var code = this.generateLuaCode();
$('#ce-code-display').val(code);
},
copyCode: function() {
var codeDisplay = document.getElementById('ce-code-display');
codeDisplay.select();
document.execCommand('copy');
alert('代码已复制到剪贴板!');
},
parseCardData: function(luaContent) {
var cards = [];
try {
// 提取 card 表
var cardMatch = luaContent.match(/local\s+card\s*=\s*\{([\s\S]*?)\n\}/);
if (!cardMatch) return cards;
var cardData = cardMatch[1];
// 提取每个卡牌
var cardRegex = /$$"(.+?)"$$\s*=\s*\{([\s\S]*?)\n \},/g;
var match;
while ((match = cardRegex.exec(cardData)) !== null) {
var cardName = match[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\');
var variantsData = match[2];
var card = {
name: cardName,
variants: []
};
// 提取变体
var variantRegex = /\{([\s\S]*?)\},/g;
var variantMatch;
while ((variantMatch = variantRegex.exec(variantsData)) !== null) {
var variant = {};
var fields = variantMatch[1];
// 提取字段
var fieldRegex = /$$"(.+?)"$$\s*=\s*(.+?),/g;
var fieldMatch;
while ((fieldMatch = fieldRegex.exec(fields)) !== null) {
var key = fieldMatch[1];
var value = fieldMatch[2].trim();
// 处理字符串值
if (value.startsWith('"') && value.endsWith('"')) {
value = value.slice(1, -1)
.replace(/\\"/g, '"')
.replace(/\\\\/g, '\\')
.replace(/\\n/g, '\n');
} else if (!isNaN(value)) {
value = parseInt(value, 10);
}
variant[key] = value;
}
card.variants.push(variant);
}
if (card.variants.length > 0) {
cards.push(card);
}
}
// 根据 cardOrder 排序
var orderMatch = luaContent.match(/local\s+cardOrder\s*=\s*\{([\s\S]*?)\}/);
if (orderMatch) {
var orderData = orderMatch[1];
var orderRegex = /"(.+?)"/g;
var order = [];
var orderMatch2;
while ((orderMatch2 = orderRegex.exec(orderData)) !== null) {
order.push(orderMatch2[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\'));
}
// 按照 cardOrder 重新排序
var sortedCards = [];
for (var i = 0; i < order.length; i++) {
var cardName = order[i];
for (var j = 0; j < cards.length; j++) {
if (cards[j].name === cardName) {
sortedCards.push(cards[j]);
break;
}
}
}
// 添加不在 cardOrder 中的卡牌(比如衍生卡牌)
for (var j = 0; j < cards.length; j++) {
var found = false;
for (var i = 0; i < sortedCards.length; i++) {
if (sortedCards[i].name === cards[j].name) {
found = true;
break;
}
}
if (!found) {
sortedCards.push(cards[j]);
}
}
cards = sortedCards;
}
} catch (e) {
console.error('解析卡牌数据失败:', e);
}
return cards;
},
saveToWiki: function() {
if (!this.currentCharacter) {
alert('请先选择一个战斗员!');
return;
}
var cards = this.characters[this.currentCharacter];
if (cards.length === 0) {
alert('当前战斗员没有卡牌数据!');
return;
}
var pageName = '模块:卡牌/' + this.currentCharacter;
var luaCode = this.generateLuaCode();
if (!confirm('确定要保存到 ' + pageName + ' 吗?\n这将覆盖现有内容!')) {
return;
}
var self = this;
this.api.postWithToken('csrf', {
action: 'edit',
title: pageName,
text: luaCode,
summary: '通过卡牌编辑器更新 ' + this.currentCharacter + ' 的卡牌数据',
recreate: true
}).done(function() {
alert('保存成功!\n页面:' + pageName);
// 更新下拉列表中的卡牌数量
self.updateCharacterDropdown();
}).fail(function(code, error) {
alert('保存失败:' + (error.error ? error.error.info : code));
});
}
};
// 添加CSS样式
var style = document.createElement('style');
style.textContent = [
'#card-editor { margin: 20px 0; }',
'.ce-container { display: flex; gap: 20px; }',
'.ce-left, .ce-middle, .ce-right { flex: 1; }',
'.ce-group { margin-bottom: 20px; background: #f8f9fa; border: 1px solid #ddd; border-radius: 5px; padding: 15px; }',
'.ce-group-title { font-weight: bold; font-size: 16px; margin-bottom: 10px; color: #333; }',
'.ce-row { display: flex; align-items: center; margin-bottom: 10px; }',
'.ce-label { width: 100px; font-weight: bold; color: #555; flex-shrink: 0; }',
'.ce-input, .ce-textarea, .ce-select-input { flex: 1; padding: 8px 12px; border: 1px solid #ccc; border-radius: 3px; font-size: 14px; }',
'.ce-textarea { min-height: 100px; resize: vertical; }',
'.ce-code { width: 100%; height: 500px; font-family: monospace; font-size: 12px; }',
'.ce-buttons { text-align: center; margin-top: 15px; }',
'.ce-btn { display: inline-block; padding: 8px 16px; margin: 0 5px; background: #36c; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; }',
'.ce-btn:hover { background: #447ff5; }',
'.ce-btn-primary { background: #0645ad; }',
'.ce-btn-primary:hover { background: #0b61d4; }',
'.ce-btn-danger { background: #d33; }',
'.ce-btn-danger:hover { background: #e74c3c; }',
'.ce-btn-small { padding: 4px 8px; font-size: 12px; margin: 0 2px; }',
'.ce-btn-blue { background: #2196F3; }',
'.ce-btn-green { background: #4CAF50; }',
'.ce-btn-lime { background: #8BC34A; }',
'.ce-btn-orange { background: #FF9800; }',
'.ce-list-container { height: 250px; overflow-y: auto; border: 1px solid #ddd; border-radius: 3px; background: white; }',
'.ce-list { padding: 10px; }',
'.ce-list-item { padding: 8px 12px; margin-bottom: 5px; background: #f5f5f5; border-radius: 3px; cursor: pointer; transition: background 0.2s; }',
'.ce-list-item:hover { background: #e8e8e8; }',
'.ce-list-item.selected { background: #e3f2fd; color: #1976D2; font-weight: bold; }',
'.ce-loading { text-align: center; padding: 20px; color: #666; }',
'.ce-error { text-align: center; padding: 20px; color: #d33; }',
'.ce-select-wrapper { position: relative; flex: 1; }',
'.ce-select-input { cursor: pointer; background: white url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiI+PHBhdGggZmlsbD0iIzMzMyIgZD0iTTExIDQgNiA5IDEgNCIvPjwvc3ZnPg==") no-repeat right 10px center; background-size: 12px; padding-right: 30px; }',
'.ce-select-dropdown { position: absolute; top: 100%; left: 0; right: 0; background: white; border: 1px solid #ccc; border-top: none; border-radius: 0 0 3px 3px; max-height: 200px; overflow-y: auto; z-index: 1000; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }',
'.ce-select-option { padding: 8px 12px; cursor: pointer; transition: background 0.2s; }',
'.ce-select-option:hover { background: #f5f5f5; }',
'.ce-desc-wrapper { flex: 1; }',
'.ce-format-buttons { margin-bottom: 5px; }',
'input[disabled], textarea[disabled], select[disabled] { background-color: #f0f0f0; cursor: not-allowed; }',
'.ce-drag-handle { color: #999; margin-right: 5px; cursor: move; font-weight: bold; }',
'.ce-no-sort { cursor: default; }',
'.ce-no-sort .ce-drag-handle { display: none; }'
].join('\n');
document.head.appendChild(style);
// 初始化编辑器
CardEditor.init();
});
});
})();