MediaWiki:Card.js
来自卡厄思梦境WIKI
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
(function() {
'use strict';
// 加载CSS
mw.loader.load('/index.php?title=Mediawiki:Card.css&action=raw&ctype=text/css', 'text/css');
console.log('Card Manager: 脚本已加载');
class CardManager {
constructor() {
console.log('Card Manager: 初始化中...');
this.currentFighter = '';
this.cards = {};
this.currentCard = null;
this.fighters = ['示例战斗员1', '示例战斗员2'];
this.defaultData = {
order: [],
ego: ''
};
this.cardData = {
name: '',
displayname: '',
art: '',
group: '',
rarity: '',
god: '',
ap: '',
type: '攻击',
dict: '',
desc_global: '',
sub: '',
isinspiration: false,
isgod_inspiration: false,
inspirations: [],
god_inspirations: {
circen: [],
diallos: [],
nihilum: [],
secred: [],
vitor: []
}
};
}
async init() {
console.log('Card Manager: 开始初始化');
await this.loadFighters();
this.render();
console.log('Card Manager: 初始化完成');
}
async loadFighters() {
try {
const api = new mw.Api();
const response = await api.get({
action: 'query',
list: 'categorymembers',
cmtitle: 'Category:战斗员',
cmlimit: 500
});
if (response.query && response.query.categorymembers) {
this.fighters = response.query.categorymembers.map(page => page.title);
console.log('Card Manager: 加载了', this.fighters.length, '个战斗员');
}
} catch (error) {
console.error('Card Manager: 加载战斗员列表失败:', error);
}
}
render() {
console.log('Card Manager: 开始渲染');
const contentDiv = document.getElementById('mw-content-text');
if (!contentDiv) {
console.error('Card Manager: 找不到内容容器');
return;
}
contentDiv.innerHTML = '';
const container = document.createElement('div');
container.className = 'card-manager';
const leftPanel = document.createElement('div');
leftPanel.className = 'card-input-panel';
leftPanel.appendChild(this.renderFighterSelect());
leftPanel.appendChild(this.renderDefaultInfo());
leftPanel.appendChild(this.renderCardInput());
const middlePanel = document.createElement('div');
middlePanel.className = 'card-list-panel';
middlePanel.appendChild(this.renderCardLists());
const rightPanel = document.createElement('div');
rightPanel.className = 'card-preview-panel';
rightPanel.appendChild(this.renderPreview());
container.appendChild(leftPanel);
container.appendChild(middlePanel);
container.appendChild(rightPanel);
contentDiv.appendChild(container);
console.log('Card Manager: 渲染完成');
}
renderFighterSelect() {
const container = document.createElement('div');
container.className = 'form-group';
const label = document.createElement('div');
label.className = 'form-label';
label.textContent = '当前战斗员';
const selectContainer = document.createElement('div');
selectContainer.className = 'custom-select';
const display = document.createElement('div');
display.className = 'select-display';
display.textContent = this.currentFighter || '请选择战斗员...';
const arrow = document.createElement('span');
arrow.className = 'select-arrow';
arrow.textContent = '▼';
display.appendChild(arrow);
const dropdown = document.createElement('div');
dropdown.className = 'select-dropdown';
this.fighters.forEach(fighter => {
const option = document.createElement('div');
option.className = 'select-option';
option.textContent = fighter;
if (fighter === this.currentFighter) {
option.classList.add('selected');
}
option.onclick = async () => {
this.currentFighter = fighter;
display.firstChild.textContent = fighter;
dropdown.querySelectorAll('.select-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
dropdown.classList.remove('active');
await this.loadFighterData();
};
dropdown.appendChild(option);
});
display.onclick = (e) => {
e.stopPropagation();
dropdown.classList.toggle('active');
};
document.addEventListener('click', () => {
dropdown.classList.remove('active');
});
selectContainer.appendChild(display);
selectContainer.appendChild(dropdown);
container.appendChild(label);
container.appendChild(selectContainer);
return container;
}
renderDefaultInfo() {
const container = document.createElement('div');
container.className = 'default-info-section';
const title = document.createElement('div');
title.className = 'section-title';
title.textContent = '默认信息';
container.appendChild(title);
const orderGroup = document.createElement('div');
orderGroup.className = 'form-group';
const orderLabel = document.createElement('div');
orderLabel.className = 'form-label';
orderLabel.textContent = '卡牌顺序(card.order)';
const orderInput = document.createElement('div');
orderInput.className = 'custom-input';
orderInput.contentEditable = true;
orderInput.id = 'order-input';
orderInput.textContent = this.defaultData.order.join(',');
orderInput.addEventListener('input', () => {
this.defaultData.order = orderInput.textContent.split(',').map(s => s.trim()).filter(s => s);
this.updatePreview();
});
orderGroup.appendChild(orderLabel);
orderGroup.appendChild(orderInput);
container.appendChild(orderGroup);
const egoGroup = document.createElement('div');
egoGroup.className = 'form-group';
const egoLabel = document.createElement('div');
egoLabel.className = 'form-label';
egoLabel.textContent = '属性(ego)';
const egoInput = document.createElement('div');
egoInput.className = 'custom-input';
egoInput.contentEditable = true;
egoInput.id = 'ego-input';
egoInput.textContent = this.defaultData.ego;
egoInput.addEventListener('input', () => {
this.defaultData.ego = egoInput.textContent;
this.updatePreview();
});
egoGroup.appendChild(egoLabel);
egoGroup.appendChild(egoInput);
container.appendChild(egoGroup);
return container;
}
renderCardInput() {
const container = document.createElement('div');
const title = document.createElement('div');
title.className = 'section-title';
title.textContent = '卡牌数据';
container.appendChild(title);
container.appendChild(this.createInputField('卡牌名称', 'name'));
container.appendChild(this.createInputField('显示名称(displayname)', 'displayname'));
container.appendChild(this.createInputField('图片(art)', 'art'));
container.appendChild(this.createGroupSelect());
container.appendChild(this.createRaritySelect());
container.appendChild(this.createGodSelect());
container.appendChild(this.createInputField('AP', 'ap'));
container.appendChild(this.createTypeSelect());
container.appendChild(this.createInputField('机制(dict)', 'dict'));
const descGroup = document.createElement('div');
descGroup.className = 'form-group';
const descLabel = document.createElement('div');
descLabel.className = 'form-label';
descLabel.textContent = '描述(desc_global)';
const formatButtons = this.createFormatButtons();
const descInput = document.createElement('div');
descInput.className = 'custom-textarea';
descInput.contentEditable = true;
descInput.textContent = this.cardData.desc_global;
descInput.id = 'desc-input';
descInput.addEventListener('input', () => {
this.cardData.desc_global = descInput.textContent;
this.updatePreview();
});
descGroup.appendChild(descLabel);
descGroup.appendChild(formatButtons);
descGroup.appendChild(descInput);
container.appendChild(descGroup);
container.appendChild(this.createInputField('衍生卡牌(sub)', 'sub'));
container.appendChild(this.createCheckboxField('是否存在灵光一闪', 'isinspiration'));
container.appendChild(this.createCheckboxField('是否存在神光一闪', 'isgod_inspiration'));
// 添加灵光一闪变体显示
if (this.cardData.isinspiration && this.cardData.inspirations.length > 0) {
container.appendChild(this.renderInspirations());
}
// 添加神光一闪变体显示
if (this.cardData.isgod_inspiration) {
container.appendChild(this.renderGodInspirations());
}
const btnGroup = document.createElement('div');
btnGroup.className = 'btn-group';
const saveBtn = document.createElement('div');
saveBtn.className = 'btn btn-primary';
saveBtn.textContent = '保存卡牌';
saveBtn.onclick = () => this.saveCard();
const newBtn = document.createElement('div');
newBtn.className = 'btn btn-success';
newBtn.textContent = '新建卡牌';
newBtn.onclick = () => this.newCard();
btnGroup.appendChild(saveBtn);
btnGroup.appendChild(newBtn);
container.appendChild(btnGroup);
return container;
}
renderInspirations() {
const container = document.createElement('div');
container.className = 'variant-section';
const title = document.createElement('div');
title.className = 'section-title';
title.textContent = '灵光一闪变体';
container.appendChild(title);
this.cardData.inspirations.forEach((inspiration, index) => {
const variantBox = document.createElement('div');
variantBox.className = 'variant-box';
const variantTitle = document.createElement('div');
variantTitle.className = 'variant-title';
variantTitle.textContent = `变体 ${index + 1}`;
variantBox.appendChild(variantTitle);
const variantContent = document.createElement('div');
variantContent.className = 'variant-content';
if (inspiration.ap) {
const apLine = document.createElement('div');
apLine.textContent = `AP: ${inspiration.ap}`;
variantContent.appendChild(apLine);
}
if (inspiration.type) {
const typeLine = document.createElement('div');
typeLine.textContent = `类型: ${inspiration.type}`;
variantContent.appendChild(typeLine);
}
if (inspiration.dict) {
const dictLine = document.createElement('div');
dictLine.textContent = `机制: ${inspiration.dict}`;
variantContent.appendChild(dictLine);
}
if (inspiration.desc_global) {
const descLine = document.createElement('div');
descLine.className = 'variant-desc';
descLine.textContent = `描述: ${inspiration.desc_global}`;
variantContent.appendChild(descLine);
}
variantBox.appendChild(variantContent);
container.appendChild(variantBox);
});
return container;
}
renderGodInspirations() {
const container = document.createElement('div');
container.className = 'variant-section';
const title = document.createElement('div');
title.className = 'section-title';
title.textContent = '神光一闪变体';
container.appendChild(title);
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
const godNames = {
'circen': '瑟肯',
'diallos': '迪亚罗斯',
'nihilum': '虚无',
'secred': '萨克雷德',
'vitor': '维托尔'
};
gods.forEach(god => {
const godInspirations = this.cardData.god_inspirations[god];
if (!godInspirations || godInspirations.length === 0) return;
const godSection = document.createElement('div');
godSection.className = 'god-section';
const godTitle = document.createElement('div');
godTitle.className = 'god-title';
godTitle.textContent = godNames[god] || god;
godSection.appendChild(godTitle);
godInspirations.forEach((inspiration, index) => {
const variantBox = document.createElement('div');
variantBox.className = 'variant-box small';
const variantTitle = document.createElement('div');
variantTitle.className = 'variant-title';
variantTitle.textContent = `${godNames[god]} 变体 ${index + 1}`;
variantBox.appendChild(variantTitle);
const variantContent = document.createElement('div');
variantContent.className = 'variant-content';
if (inspiration.ap) {
const apLine = document.createElement('div');
apLine.textContent = `AP: ${inspiration.ap}`;
variantContent.appendChild(apLine);
}
if (inspiration.type) {
const typeLine = document.createElement('div');
typeLine.textContent = `类型: ${inspiration.type}`;
variantContent.appendChild(typeLine);
}
if (inspiration.dict) {
const dictLine = document.createElement('div');
dictLine.textContent = `机制: ${inspiration.dict}`;
variantContent.appendChild(dictLine);
}
if (inspiration.desc_global) {
const descLine = document.createElement('div');
descLine.className = 'variant-desc';
descLine.textContent = `描述: ${inspiration.desc_global}`;
variantContent.appendChild(descLine);
}
variantBox.appendChild(variantContent);
godSection.appendChild(variantBox);
});
container.appendChild(godSection);
});
return container;
}
createInputField(label, field) {
const group = document.createElement('div');
group.className = 'form-group';
const labelDiv = document.createElement('div');
labelDiv.className = 'form-label';
labelDiv.textContent = label;
const input = document.createElement('div');
input.className = 'custom-input';
input.contentEditable = true;
input.dataset.field = field;
input.textContent = this.cardData[field] || '';
input.addEventListener('input', () => {
this.cardData[field] = input.textContent;
this.updatePreview();
});
group.appendChild(labelDiv);
group.appendChild(input);
return group;
}
createCheckboxField(label, field) {
const group = document.createElement('div');
group.className = 'form-group';
const labelDiv = document.createElement('div');
labelDiv.className = 'form-label';
labelDiv.textContent = label;
const checkboxContainer = document.createElement('div');
checkboxContainer.className = 'checkbox-group';
const checkbox = document.createElement('div');
checkbox.className = 'custom-checkbox';
checkbox.dataset.field = field;
if (this.cardData[field]) {
checkbox.classList.add('checked');
}
const statusLabel = document.createElement('div');
statusLabel.textContent = this.cardData[field] ? '是' : '否';
checkbox.onclick = () => {
checkbox.classList.toggle('checked');
this.cardData[field] = checkbox.classList.contains('checked');
statusLabel.textContent = this.cardData[field] ? '是' : '否';
this.updatePreview();
};
checkboxContainer.appendChild(checkbox);
checkboxContainer.appendChild(statusLabel);
group.appendChild(labelDiv);
group.appendChild(checkboxContainer);
return group;
}
createFormatButtons() {
const container = document.createElement('div');
container.className = 'format-buttons';
const buttons = [
{ label: '蓝色文本', class: 'blue', template: '{{文本|蓝|%s}}' },
{ label: '绿色文本', class: 'green', template: '{{文本|绿|%s}}' },
{ label: '绿色描边', class: 'green', template: '{{描边|绿|%s}}' },
{ label: '词典', class: '', template: '{{词典|%s}}' },
{ label: '换行', class: '', template: '<br>' }
];
buttons.forEach(btn => {
const button = document.createElement('div');
button.className = 'format-btn ' + btn.class;
button.textContent = btn.label;
button.onclick = () => {
const textarea = document.getElementById('desc-input');
if (textarea) {
this.insertTemplate(textarea, btn.template);
}
};
container.appendChild(button);
});
return container;
}
insertTemplate(element, template) {
element.focus();
const selection = window.getSelection();
let selectedText = '';
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
if (element.contains(range.commonAncestorContainer)) {
selectedText = range.toString();
}
}
let insertText;
if (template === '<br>') {
insertText = '<br>';
document.execCommand('insertHTML', false, insertText);
} else {
insertText = template.replace('%s', selectedText || '选择文字');
document.execCommand('insertText', false, insertText);
}
this.cardData.desc_global = element.textContent;
this.updatePreview();
}
createGroupSelect() {
const group = document.createElement('div');
group.className = 'form-group';
const label = document.createElement('div');
label.className = 'form-label';
label.textContent = '卡组(group)';
const container = document.createElement('div');
container.className = 'group-options';
const groups = ['自我意识技能', '起始卡牌', '独特卡牌', '灵光一闪', '神光一闪'];
groups.forEach(groupName => {
const option = document.createElement('div');
option.className = 'group-option';
option.textContent = groupName;
if (this.cardData.group === groupName) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.group-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.group = groupName;
this.updatePreview();
};
container.appendChild(option);
});
group.appendChild(label);
group.appendChild(container);
return group;
}
createRaritySelect() {
const group = document.createElement('div');
group.className = 'form-group';
const label = document.createElement('div');
label.className = 'form-label';
label.textContent = '稀有度(rarity)';
const container = document.createElement('div');
container.className = 'rarity-options';
const rarities = ['白', '蓝', '橙', '彩'];
rarities.forEach(rarity => {
const option = document.createElement('div');
option.className = 'rarity-option';
option.textContent = rarity;
if (this.cardData.rarity === rarity) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.rarity-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.rarity = rarity;
this.updatePreview();
};
container.appendChild(option);
});
group.appendChild(label);
group.appendChild(container);
return group;
}
createGodSelect() {
const group = document.createElement('div');
group.className = 'form-group';
const label = document.createElement('div');
label.className = 'form-label';
label.textContent = '神明';
const container = document.createElement('div');
container.className = 'god-options';
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
gods.forEach(god => {
const option = document.createElement('div');
option.className = 'god-option';
option.textContent = god;
if (this.cardData.god === god) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.god-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.god = god;
this.updatePreview();
};
container.appendChild(option);
});
group.appendChild(label);
group.appendChild(container);
return group;
}
createTypeSelect() {
const group = document.createElement('div');
group.className = 'form-group';
const label = document.createElement('div');
label.className = 'form-label';
label.textContent = '卡牌类型(type)';
const container = document.createElement('div');
container.className = 'type-options';
const types = ['攻击', '技能', '强化'];
types.forEach(type => {
const option = document.createElement('div');
option.className = 'type-option';
option.textContent = type;
if (this.cardData.type === type) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.type-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.type = type;
this.updatePreview();
};
container.appendChild(option);
});
group.appendChild(label);
group.appendChild(container);
return group;
}
renderCardLists() {
const container = document.createElement('div');
const title = document.createElement('div');
title.className = 'panel-title';
title.textContent = '卡牌列表';
container.appendChild(title);
// 主卡牌列表
const mainSection = document.createElement('div');
mainSection.className = 'card-list-section';
const mainTitle = document.createElement('div');
mainTitle.className = 'section-title';
mainTitle.textContent = '主卡牌';
mainSection.appendChild(mainTitle);
const mainList = document.createElement('div');
mainList.className = 'list-container';
mainList.id = 'main-card-list';
mainSection.appendChild(mainList);
container.appendChild(mainSection);
// 灵光一闪列表
const inspirationSection = document.createElement('div');
inspirationSection.className = 'card-list-section';
const inspirationTitle = document.createElement('div');
inspirationTitle.className = 'section-title';
inspirationTitle.textContent = '灵光一闪卡牌';
inspirationSection.appendChild(inspirationTitle);
const inspirationList = document.createElement('div');
inspirationList.className = 'list-container';
inspirationList.id = 'inspiration-card-list';
inspirationSection.appendChild(inspirationList);
container.appendChild(inspirationSection);
// 神之一闪列表
const godSection = document.createElement('div');
godSection.className = 'card-list-section';
const godTitle = document.createElement('div');
godTitle.className = 'section-title';
godTitle.textContent = '神之一闪卡牌';
godSection.appendChild(godTitle);
const godList = document.createElement('div');
godList.className = 'list-container';
godList.id = 'god-card-list';
godSection.appendChild(godList);
container.appendChild(godSection);
this.updateCardLists();
return container;
}
updateCardLists() {
const mainList = document.getElementById('main-card-list');
const inspirationList = document.getElementById('inspiration-card-list');
const godList = document.getElementById('god-card-list');
if (!mainList || !inspirationList || !godList) return;
const mainCards = [];
const inspirationCards = [];
const godCards = [];
// 分类卡牌
Object.keys(this.cards).forEach(name => {
const card = this.cards[name];
if (card.isgod_inspiration) {
godCards.push(name);
} else if (card.isinspiration) {
inspirationCards.push(name);
} else {
mainCards.push(name);
}
});
// 更新各个列表
this.updateCardListSection(mainList, mainCards);
this.updateCardListSection(inspirationList, inspirationCards);
this.updateCardListSection(godList, godCards);
}
updateCardListSection(container, cardNames) {
container.innerHTML = '';
if (cardNames.length === 0) {
const empty = document.createElement('div');
empty.className = 'list-empty';
empty.textContent = '暂无卡牌';
container.appendChild(empty);
return;
}
cardNames.forEach(name => {
const card = this.cards[name];
const item = document.createElement('div');
item.className = 'card-list-item';
const info = document.createElement('div');
const nameDiv = document.createElement('div');
nameDiv.className = 'card-list-item-name';
nameDiv.textContent = name;
const detailDiv = document.createElement('div');
detailDiv.className = 'card-list-item-info';
detailDiv.textContent = `${card.type || '攻击'} | AP:${card.ap || 0} | ${card.rarity || ''}`;
info.appendChild(nameDiv);
info.appendChild(detailDiv);
const deleteBtn = document.createElement('div');
deleteBtn.className = 'delete-btn';
deleteBtn.textContent = '删除';
deleteBtn.onclick = (e) => {
e.stopPropagation();
if (confirm(`确定要删除卡牌"${name}"吗?`)) {
delete this.cards[name];
this.updateCardLists();
this.updatePreview();
if (this.currentCard === name) {
this.newCard();
}
}
};
item.appendChild(info);
item.appendChild(deleteBtn);
item.onclick = () => {
this.loadCard(name);
document.querySelectorAll('.card-list-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
};
container.appendChild(item);
});
}
renderPreview() {
const container = document.createElement('div');
const title = document.createElement('div');
title.className = 'panel-title';
title.textContent = 'Lua代码预览';
container.appendChild(title);
const preview = document.createElement('div');
preview.className = 'preview-code';
preview.id = 'code-preview';
container.appendChild(preview);
const copyBtn = document.createElement('div');
copyBtn.className = 'btn btn-primary';
copyBtn.textContent = '复制代码';
copyBtn.style.marginTop = '10px';
copyBtn.onclick = () => this.copyCode();
container.appendChild(copyBtn);
this.updatePreview();
return container;
}
updatePreview() {
const preview = document.getElementById('code-preview');
if (!preview) return;
preview.textContent = this.generateLuaCode();
}
generateLuaCode() {
let code = 'local card = {}\n\n';
if (this.defaultData.order.length > 0) {
code += `card.order = { "${this.defaultData.order.join(',')}" }\n\n`;
}
code += 'card.info = {\n';
if (this.defaultData.ego) {
code += ` ego = "${this.defaultData.ego}",\n`;
}
code += '}\n\n';
Object.keys(this.cards).forEach(name => {
const card = this.cards[name];
code += this.generateCardCode(name, card);
});
code += 'return card';
return code;
}
generateCardCode(name, card) {
let code = `card["${name}"] = {\n`;
code += ' base = {\n';
if (card.displayname) code += ` displayname = "${card.displayname}",\n`;
if (card.art) code += ` art = "${card.art}",\n`;
if (card.group) code += ` group = "${card.group}",\n`;
if (card.rarity) code += ` rarity = "${card.rarity}",\n`;
if (card.ap) code += ` ap = ${isNaN(card.ap) ? `"${card.ap}"` : card.ap},\n`;
if (card.type) code += ` type = "${card.type}",\n`;
if (card.dict) code += ` dict = "${card.dict}",\n`;
if (card.desc_global) {
const desc = card.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"');
code += ` desc_global = "${desc}",\n`;
}
if (card.sub) code += ` sub = "${card.sub}",\n`;
if (card.isinspiration) code += ` isinspiration = 1,\n`;
if (card.isgod_inspiration) code += ` isgod_inspiration = 1,\n`;
code += ' },\n';
// 添加变体数据
if ((card.isinspiration && card.inspirations.length > 0) ||
(card.isgod_inspiration && this.hasGodInspirations(card))) {
code += ' \n var = {\n';
// 灵光一闪变体
if (card.isinspiration && card.inspirations.length > 0) {
code += ' inspiration = {\n';
card.inspirations.forEach(inspiration => {
code += ' {\n';
if (inspiration.ap) code += ` ap = ${isNaN(inspiration.ap) ? `"${inspiration.ap}"` : inspiration.ap},\n`;
if (inspiration.type) code += ` type = "${inspiration.type}",\n`;
if (inspiration.dict) code += ` dict = "${inspiration.dict}",\n`;
if (inspiration.desc_global) {
const desc = inspiration.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"');
code += ` desc_global = "${desc}",\n`;
}
code += ' },\n';
});
code += ' },\n';
}
// 神之一闪变体
if (card.isgod_inspiration) {
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
const hasAnyGod = gods.some(god => card.god_inspirations[god] && card.god_inspirations[god].length > 0);
if (hasAnyGod) {
code += ' god_inspiration = {\n';
gods.forEach(god => {
const godInspirations = card.god_inspirations[god];
if (godInspirations && godInspirations.length > 0) {
code += ` ${god} = {\n`;
godInspirations.forEach(inspiration => {
code += ' {\n';
if (inspiration.ap) code += ` ap = ${isNaN(inspiration.ap) ? `"${inspiration.ap}"` : inspiration.ap},\n`;
if (inspiration.type) code += ` type = "${inspiration.type}",\n`;
if (inspiration.dict) code += ` dict = "${inspiration.dict}",\n`;
if (inspiration.desc_global) {
const desc = inspiration.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"');
code += ` desc_global = "${desc}",\n`;
}
code += ' },\n';
});
code += ' },\n';
}
});
code += ' },\n';
}
}
code += ' },\n';
}
code += '}\n\n';
return code;
}
hasGodInspirations(card) {
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
return gods.some(god => card.god_inspirations[god] && card.god_inspirations[god].length > 0);
}
saveCard() {
if (!this.cardData.name) {
alert('请输入卡牌名称!');
return;
}
this.cards[this.cardData.name] = JSON.parse(JSON.stringify(this.cardData));
this.currentCard = this.cardData.name;
if (!this.defaultData.order.includes(this.cardData.name)) {
this.defaultData.order.push(this.cardData.name);
const orderInput = document.getElementById('order-input');
if (orderInput) {
orderInput.textContent = this.defaultData.order.join(',');
}
}
this.updateCardLists();
this.updatePreview();
alert('卡牌保存成功!');
}
newCard() {
this.currentCard = null;
this.cardData = {
name: '',
displayname: '',
art: '',
group: '',
rarity: '',
god: '',
ap: '',
type: '攻击',
dict: '',
desc_global: '',
sub: '',
isinspiration: false,
isgod_inspiration: false,
inspirations: [],
god_inspirations: {
circen: [],
diallos: [],
nihilum: [],
secred: [],
vitor: []
}
};
this.render();
}
loadCard(name) {
if (!this.cards[name]) return;
this.currentCard = name;
this.cardData = JSON.parse(JSON.stringify(this.cards[name]));
this.render();
}
async loadFighterData() {
if (!this.currentFighter) return;
try {
const api = new mw.Api();
const moduleName = `模块:卡牌/${this.currentFighter}`;
const response = await api.get({
action: 'query',
prop: 'revisions',
titles: moduleName,
rvprop: 'content',
rvslots: 'main'
});
const pages = response.query.pages;
const page = Object.values(pages)[0];
if (page.revisions && page.revisions[0]) {
const content = page.revisions[0].slots.main['*'];
console.log('Card Manager: 加载了战斗员数据');
// 解析Lua代码
this.parseLuaContent(content);
// 重新渲染界面
this.render();
alert(`成功加载战斗员"${this.currentFighter}"的数据!`);
} else {
console.log('Card Manager: 该战斗员暂无数据');
alert(`战斗员"${this.currentFighter}"暂无卡牌数据`);
}
} catch (error) {
console.error('Card Manager: 加载战斗员数据失败:', error);
alert('加载失败: ' + error.message);
}
}
parseLuaContent(content) {
// 重置数据
this.cards = {};
this.defaultData = { order: [], ego: '' };
try {
// 解析 card.order
const orderMatch = content.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/);
if (orderMatch) {
this.defaultData.order = orderMatch[1].split(',').map(s => s.trim()).filter(s => s);
}
// 解析 card.info.ego
const infoBlockMatch = content.match(/card\.info\s*=\s*\{([^}]*)\}/);
if (infoBlockMatch) {
const egoMatch = infoBlockMatch[1].match(/ego\s*=\s*"([^"]+)"/);
if (egoMatch) {
this.defaultData.ego = egoMatch[1];
}
}
// 使用正则表达式匹配每个卡牌块
const cardPattern = /card\["([^"]+)"\]\s*=\s*\{([\s\S]*?)\n\}/g;
let match;
while ((match = cardPattern.exec(content)) !== null) {
const cardName = match[1];
const cardContent = match[2];
console.log(`Card Manager: 解析卡牌 "${cardName}"`);
// 初始化卡牌数据
this.cards[cardName] = {
name: cardName,
displayname: '',
art: '',
group: '',
rarity: '',
god: '',
ap: '',
type: '攻击',
dict: '',
desc_global: '',
sub: '',
isinspiration: false,
isgod_inspiration: false,
inspirations: [],
god_inspirations: {
circen: [],
diallos: [],
nihilum: [],
secred: [],
vitor: []
}
};
// 解析 base 部分
const baseMatch = cardContent.match(/base\s*=\s*\{([\s\S]*?)\n\s*\},/);
if (baseMatch) {
this.parseCardBase(cardName, baseMatch[1]);
}
// 解析 var 部分
const varMatch = cardContent.match(/var\s*=\s*\{([\s\S]*?)\n\s*\},/);
if (varMatch) {
const varContent = varMatch[1];
// 解析 inspiration
const inspirationMatch = varContent.match(/inspiration\s*=\s*\{([\s\S]*?)\n\s*\},/);
if (inspirationMatch) {
this.parseInspirationsList(cardName, inspirationMatch[1]);
}
// 解析 god_inspiration
const godInspirationMatch = varContent.match(/god_inspiration\s*=\s*\{([\s\S]*?)\n\s*\},/);
if (godInspirationMatch) {
this.parseGodInspirationsList(cardName, godInspirationMatch[1]);
}
}
}
console.log('Card Manager: 解析完成');
console.log('Card Manager: order =', this.defaultData.order);
console.log('Card Manager: ego =', this.defaultData.ego);
console.log('Card Manager: 卡牌数量 =', Object.keys(this.cards).length);
} catch (error) {
console.error('Card Manager: 解析Lua内容时出错:', error);
alert('解析卡牌数据失败: ' + error.message);
}
}
parseCardBase(cardName, baseContent) {
const card = this.cards[cardName];
card.displayname = this.extractFieldValue(baseContent, 'displayname');
card.art = this.extractFieldValue(baseContent, 'art');
card.group = this.extractFieldValue(baseContent, 'group');
card.rarity = this.extractFieldValue(baseContent, 'rarity');
card.god = this.extractFieldValue(baseContent, 'god');
card.ap = this.extractFieldValue(baseContent, 'ap');
card.type = this.extractFieldValue(baseContent, 'type') || '攻击';
card.dict = this.extractFieldValue(baseContent, 'dict');
card.desc_global = this.extractFieldValue(baseContent, 'desc_global').replace(/<br>/g, '\n');
card.sub = this.extractFieldValue(baseContent, 'sub');
card.isinspiration = baseContent.includes('isinspiration = 1');
card.isgod_inspiration = baseContent.includes('isgod_inspiration = 1');
}
parseInspirationsList(cardName, content) {
// 匹配所有的变体块 {...},
const variantPattern = /\{([^\}]*)\},?/g;
let match;
while ((match = variantPattern.exec(content)) !== null) {
const variantContent = match[1];
if (variantContent.trim()) {
const variant = {
ap: this.extractFieldValue(variantContent, 'ap'),
type: this.extractFieldValue(variantContent, 'type'),
dict: this.extractFieldValue(variantContent, 'dict'),
desc_global: this.extractFieldValue(variantContent, 'desc_global').replace(/<br>/g, '\n')
};
this.cards[cardName].inspirations.push(variant);
}
}
console.log(`Card Manager: 卡牌 "${cardName}" 有 ${this.cards[cardName].inspirations.length} 个灵光一闪变体`);
}
parseGodInspirationsList(cardName, content) {
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
gods.forEach(god => {
const godPattern = new RegExp(`${god}\\s*=\\s*\\{([\\s\\S]*?)\\n\\s*\\},`, 'i');
const godMatch = content.match(godPattern);
if (godMatch) {
const godContent = godMatch[1];
const variantPattern = /\{([^\}]*)\},?/g;
let match;
while ((match = variantPattern.exec(godContent)) !== null) {
const variantContent = match[1];
if (variantContent.trim()) {
const variant = {
ap: this.extractFieldValue(variantContent, 'ap'),
type: this.extractFieldValue(variantContent, 'type'),
dict: this.extractFieldValue(variantContent, 'dict'),
desc_global: this.extractFieldValue(variantContent, 'desc_global').replace(/<br>/g, '\n')
};
this.cards[cardName].god_inspirations[god].push(variant);
}
}
console.log(`Card Manager: 卡牌 "${cardName}" 的神明 "${god}" 有 ${this.cards[cardName].god_inspirations[god].length} 个神光一闪变体`);
}
});
}
loadCard(name) {
if (!this.cards[name]) return;
this.currentCard = name;
this.cardData = JSON.parse(JSON.stringify(this.cards[name]));
// 确保变体数据结构完整
if (!this.cardData.inspirations) {
this.cardData.inspirations = [];
}
if (!this.cardData.god_inspirations) {
this.cardData.god_inspirations = {
circen: [],
diallos: [],
nihilum: [],
secred: [],
vitor: []
};
}
this.render();
}
splitVariants(content) {
const variants = [];
const lines = content.split('\n');
let currentVariant = [];
let braceCount = 0;
for (let line of lines) {
const trimmed = line.trim();
if (trimmed === '{') {
braceCount++;
if (braceCount === 1) {
currentVariant = [];
}
} else if (trimmed === '},' || trimmed === '}') {
braceCount--;
if (braceCount === 0 && currentVariant.length > 0) {
variants.push(currentVariant.join('\n'));
currentVariant = [];
}
} else if (braceCount === 1) {
currentVariant.push(line);
}
}
return variants;
}
extractFieldValue(content, fieldName) {
// 匹配 fieldName = "value" 格式
const pattern = new RegExp(`${fieldName}\\s*=\\s*"([^"]*)"`, 'i');
const match = content.match(pattern);
if (match) {
return match[1];
}
// 匹配 fieldName = value 格式(数字)
const numPattern = new RegExp(`${fieldName}\\s*=\\s*([\\d.]+)`, 'i');
const numMatch = content.match(numPattern);
if (numMatch) {
return numMatch[1];
}
return '';
}
copyCode() {
const code = this.generateLuaCode();
const textarea = document.createElement('textarea');
textarea.value = code;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
alert('代码已复制到剪贴板!');
}
}
// 初始化函数
function initCardManager() {
console.log('Card Manager: 准备启动');
const manager = new CardManager();
manager.init();
}
// 在页面加载完成后启动
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
mw.loader.using(['mediawiki.api']).then(initCardManager);
});
} else {
mw.loader.using(['mediawiki.api']).then(initCardManager);
}
})();