Card.js:修订间差异
来自卡厄思梦境WIKI
无编辑摘要 |
无编辑摘要 |
||
| 第5行: | 第5行: | ||
mw.loader.load('/index.php?title=Mediawiki:Card.css&action=raw&ctype=text/css', 'text/css'); | mw.loader.load('/index.php?title=Mediawiki:Card.css&action=raw&ctype=text/css', 'text/css'); | ||
console.log('Card Manager: 脚本已加载'); | console.log('Card Manager: 脚本已加载'); | ||
class CardManager { | class CardManager { | ||
constructor() { | constructor() { | ||
| 第15行: | 第13行: | ||
this.cards = {}; | this.cards = {}; | ||
this.currentCard = null; | this.currentCard = null; | ||
this.fighters = ['示例战斗员1', '示例战斗员2']; | this.fighters = ['示例战斗员1', '示例战斗员2']; | ||
this.defaultData = { | this.defaultData = { | ||
| 第76行: | 第74行: | ||
console.log('Card Manager: 开始渲染'); | console.log('Card Manager: 开始渲染'); | ||
const contentDiv = document.getElementById('mw-content-text'); | const contentDiv = document.getElementById('mw-content-text'); | ||
if (!contentDiv) { | if (!contentDiv) { | ||
| 第83行: | 第80行: | ||
} | } | ||
contentDiv.innerHTML = ''; | contentDiv.innerHTML = ''; | ||
const container = document.createElement('div'); | const container = document.createElement('div'); | ||
container.className = 'card-manager'; | container.className = 'card-manager'; | ||
const leftPanel = document.createElement('div'); | const leftPanel = document.createElement('div'); | ||
leftPanel.className = 'card-input-panel'; | leftPanel.className = 'card-input-panel'; | ||
| 第97行: | 第91行: | ||
leftPanel.appendChild(this.renderCardInput()); | leftPanel.appendChild(this.renderCardInput()); | ||
const middlePanel = document.createElement('div'); | const middlePanel = document.createElement('div'); | ||
middlePanel.className = 'card-list-panel'; | middlePanel.className = 'card-list-panel'; | ||
middlePanel.appendChild(this.renderCardLists()); | middlePanel.appendChild(this.renderCardLists()); | ||
const rightPanel = document.createElement('div'); | const rightPanel = document.createElement('div'); | ||
rightPanel.className = 'card-preview-panel'; | rightPanel.className = 'card-preview-panel'; | ||
| 第146行: | 第138行: | ||
option.classList.add('selected'); | option.classList.add('selected'); | ||
} | } | ||
option.onclick = () => { | option.onclick = async () => { | ||
this.currentFighter = fighter; | this.currentFighter = fighter; | ||
display.firstChild.textContent = fighter; | display.firstChild.textContent = fighter; | ||
| 第152行: | 第144行: | ||
option.classList.add('selected'); | option.classList.add('selected'); | ||
dropdown.classList.remove('active'); | dropdown.classList.remove('active'); | ||
this.loadFighterData(); | await this.loadFighterData(); | ||
}; | }; | ||
dropdown.appendChild(option); | dropdown.appendChild(option); | ||
| 第184行: | 第176行: | ||
container.appendChild(title); | container.appendChild(title); | ||
const orderGroup = document.createElement('div'); | const orderGroup = document.createElement('div'); | ||
orderGroup.className = 'form-group'; | orderGroup.className = 'form-group'; | ||
| 第195行: | 第186行: | ||
orderInput.className = 'custom-input'; | orderInput.className = 'custom-input'; | ||
orderInput.contentEditable = true; | orderInput.contentEditable = true; | ||
orderInput.id = 'order-input'; | |||
orderInput.textContent = this.defaultData.order.join(','); | orderInput.textContent = this.defaultData.order.join(','); | ||
orderInput.addEventListener('input', () => { | orderInput.addEventListener('input', () => { | ||
| 第205行: | 第197行: | ||
container.appendChild(orderGroup); | container.appendChild(orderGroup); | ||
const egoGroup = document.createElement('div'); | const egoGroup = document.createElement('div'); | ||
egoGroup.className = 'form-group'; | egoGroup.className = 'form-group'; | ||
| 第216行: | 第207行: | ||
egoInput.className = 'custom-input'; | egoInput.className = 'custom-input'; | ||
egoInput.contentEditable = true; | egoInput.contentEditable = true; | ||
egoInput.id = 'ego-input'; | |||
egoInput.textContent = this.defaultData.ego; | egoInput.textContent = this.defaultData.ego; | ||
egoInput.addEventListener('input', () => { | egoInput.addEventListener('input', () => { | ||
| 第237行: | 第229行: | ||
container.appendChild(title); | container.appendChild(title); | ||
container.appendChild(this.createInputField('卡牌名称', 'name')); | container.appendChild(this.createInputField('卡牌名称', 'name')); | ||
container.appendChild(this.createInputField('显示名称(displayname)', 'displayname')); | container.appendChild(this.createInputField('显示名称(displayname)', 'displayname')); | ||
container.appendChild(this.createInputField('图片(art)', 'art')); | container.appendChild(this.createInputField('图片(art)', 'art')); | ||
container.appendChild(this.createGroupSelect()); | container.appendChild(this.createGroupSelect()); | ||
container.appendChild(this.createRaritySelect()); | container.appendChild(this.createRaritySelect()); | ||
container.appendChild(this.createGodSelect()); | container.appendChild(this.createGodSelect()); | ||
container.appendChild(this.createInputField('AP', 'ap')); | container.appendChild(this.createInputField('AP', 'ap')); | ||
container.appendChild(this.createTypeSelect()); | container.appendChild(this.createTypeSelect()); | ||
container.appendChild(this.createInputField('机制(dict)', 'dict')); | container.appendChild(this.createInputField('机制(dict)', 'dict')); | ||
const descGroup = document.createElement('div'); | const descGroup = document.createElement('div'); | ||
descGroup.className = 'form-group'; | descGroup.className = 'form-group'; | ||
| 第289行: | 第263行: | ||
container.appendChild(descGroup); | container.appendChild(descGroup); | ||
container.appendChild(this.createInputField('衍生卡牌(sub)', 'sub')); | container.appendChild(this.createInputField('衍生卡牌(sub)', 'sub')); | ||
container.appendChild(this.createCheckboxField('是否存在灵光一闪', 'isinspiration')); | container.appendChild(this.createCheckboxField('是否存在灵光一闪', 'isinspiration')); | ||
container.appendChild(this.createCheckboxField('是否存在神光一闪', 'isgod_inspiration')); | container.appendChild(this.createCheckboxField('是否存在神光一闪', 'isgod_inspiration')); | ||
const btnGroup = document.createElement('div'); | const btnGroup = document.createElement('div'); | ||
btnGroup.className = 'btn-group'; | btnGroup.className = 'btn-group'; | ||
| 第330行: | 第298行: | ||
input.className = 'custom-input'; | input.className = 'custom-input'; | ||
input.contentEditable = true; | input.contentEditable = true; | ||
input.dataset.field = field; | |||
input.textContent = this.cardData[field] || ''; | input.textContent = this.cardData[field] || ''; | ||
input.addEventListener('input', () => { | input.addEventListener('input', () => { | ||
| 第355行: | 第324行: | ||
const checkbox = document.createElement('div'); | const checkbox = document.createElement('div'); | ||
checkbox.className = 'custom-checkbox'; | checkbox.className = 'custom-checkbox'; | ||
checkbox.dataset.field = field; | |||
if (this.cardData[field]) { | if (this.cardData[field]) { | ||
checkbox.classList.add('checked'); | checkbox.classList.add('checked'); | ||
| 第406行: | 第376行: | ||
} | } | ||
insertTemplate( | insertTemplate(element, template) { | ||
element.focus(); | |||
const selection = window.getSelection(); | 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>') { | if (template === '<br>') { | ||
document.execCommand('insertHTML', false, | insertText = '<br>'; | ||
document.execCommand('insertHTML', false, insertText); | |||
} else { | } else { | ||
// 如果有选中文本,包裹它;否则插入模板 | |||
document.execCommand('insertText', false, | insertText = template.replace('%s', selectedText || '选择文字'); | ||
document.execCommand('insertText', false, insertText); | |||
} | } | ||
this.cardData.desc_global = | // 更新数据 | ||
this.cardData.desc_global = element.textContent; | |||
this.updatePreview(); | this.updatePreview(); | ||
} | } | ||
| 第730行: | 第716行: | ||
if (!this.defaultData.order.includes(this.cardData.name)) { | if (!this.defaultData.order.includes(this.cardData.name)) { | ||
this.defaultData.order.push(this.cardData.name); | this.defaultData.order.push(this.cardData.name); | ||
// 更新order输入框 | |||
const orderInput = document.getElementById('order-input'); | |||
if (orderInput) { | |||
orderInput.textContent = this.defaultData.order.join(','); | |||
} | |||
} | } | ||
| 第796行: | 第787行: | ||
const page = Object.values(pages)[0]; | const page = Object.values(pages)[0]; | ||
if (page.revisions) { | if (page.revisions && page.revisions[0]) { | ||
const content = page.revisions[0].slots.main['*']; | const content = page.revisions[0].slots.main['*']; | ||
console.log('Card Manager: 加载了战斗员数据'); | console.log('Card Manager: 加载了战斗员数据'); | ||
// 解析Lua代码 | |||
this.parseLuaContent(content); | |||
// 重新渲染界面 | |||
this.render(); | |||
alert(`成功加载战斗员"${this.currentFighter}"的数据!`); | |||
} else { | |||
console.log('Card Manager: 该战斗员暂无数据'); | |||
alert(`战斗员"${this.currentFighter}"暂无卡牌数据`); | |||
} | } | ||
} catch (error) { | } catch (error) { | ||
console.error('Card Manager: 加载战斗员数据失败:', error); | console.error('Card Manager: 加载战斗员数据失败:', error); | ||
alert('加载失败: ' + error.message); | |||
} | } | ||
} | |||
parseLuaContent(content) { | |||
// 重置数据 | |||
this.cards = {}; | |||
this.defaultData = { order: [], ego: '' }; | |||
// 解析 card.order | |||
const orderMatch = content.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/); | |||
if (orderMatch) { | |||
this.defaultData.order = orderMatch[1].split(',').map(s => s.trim()); | |||
} | |||
// 解析 card.info.ego | |||
const egoMatch = content.match(/ego\s*=\s*"([^"]+)"/); | |||
if (egoMatch) { | |||
this.defaultData.ego = egoMatch[1]; | |||
} | |||
// 解析卡牌数据 | |||
const cardRegex = /card$$"([^"]+)"$$\s*=\s*\{[\s\S]*?base\s*=\s*\{([\s\S]*?)\}/g; | |||
let match; | |||
while ((match = cardRegex.exec(content)) !== null) { | |||
const cardName = match[1]; | |||
const baseContent = match[2]; | |||
const card = { | |||
name: cardName, | |||
displayname: this.extractValue(baseContent, 'displayname'), | |||
art: this.extractValue(baseContent, 'art'), | |||
group: this.extractValue(baseContent, 'group'), | |||
rarity: this.extractValue(baseContent, 'rarity'), | |||
god: this.extractValue(baseContent, 'god'), | |||
ap: this.extractValue(baseContent, 'ap'), | |||
type: this.extractValue(baseContent, 'type') || '攻击', | |||
dict: this.extractValue(baseContent, 'dict'), | |||
desc_global: this.extractValue(baseContent, 'desc_global').replace(/<br>/g, '\n'), | |||
sub: this.extractValue(baseContent, 'sub'), | |||
isinspiration: baseContent.includes('isinspiration = 1'), | |||
isgod_inspiration: baseContent.includes('isgod_inspiration = 1'), | |||
inspirations: [], | |||
god_inspirations: { | |||
circen: [], | |||
diallos: [], | |||
nihilum: [], | |||
secred: [], | |||
vitor: [] | |||
} | |||
}; | |||
this.cards[cardName] = card; | |||
} | |||
console.log('Card Manager: 解析完成,共', Object.keys(this.cards).length, '张卡牌'); | |||
} | |||
extractValue(content, key) { | |||
// 匹配字符串值 | |||
const stringMatch = content.match(new RegExp(`${key}\\s*=\\s*"([^"]*)"`, 'i')); | |||
if (stringMatch) { | |||
return stringMatch[1]; | |||
} | |||
// 匹配数字值 | |||
const numberMatch = content.match(new RegExp(`${key}\\s*=\\s*([\\d.]+)`, 'i')); | |||
if (numberMatch) { | |||
return numberMatch[1]; | |||
} | |||
return ''; | |||
} | } | ||
2025年10月23日 (四) 16:16的版本
(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'));
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;
}
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 listContainer = document.createElement('div');
listContainer.className = 'list-container';
listContainer.id = 'card-list';
this.updateCardList(listContainer);
container.appendChild(listContainer);
return container;
}
updateCardList(container) {
container.innerHTML = '';
const cardNames = Object.keys(this.cards);
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.updateCardList(container);
this.updatePreview();
if (this.currentCard === name) {
this.newCard();
}
}
};
item.appendChild(info);
item.appendChild(deleteBtn);
item.onclick = () => {
this.loadCard(name);
container.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';
code += '}\n\n';
return code;
}
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);
// 更新order输入框
const orderInput = document.getElementById('order-input');
if (orderInput) {
orderInput.textContent = this.defaultData.order.join(',');
}
}
const listContainer = document.getElementById('card-list');
if (listContainer) {
this.updateCardList(listContainer);
}
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: '' };
// 解析 card.order
const orderMatch = content.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/);
if (orderMatch) {
this.defaultData.order = orderMatch[1].split(',').map(s => s.trim());
}
// 解析 card.info.ego
const egoMatch = content.match(/ego\s*=\s*"([^"]+)"/);
if (egoMatch) {
this.defaultData.ego = egoMatch[1];
}
// 解析卡牌数据
const cardRegex = /card$$"([^"]+)"$$\s*=\s*\{[\s\S]*?base\s*=\s*\{([\s\S]*?)\}/g;
let match;
while ((match = cardRegex.exec(content)) !== null) {
const cardName = match[1];
const baseContent = match[2];
const card = {
name: cardName,
displayname: this.extractValue(baseContent, 'displayname'),
art: this.extractValue(baseContent, 'art'),
group: this.extractValue(baseContent, 'group'),
rarity: this.extractValue(baseContent, 'rarity'),
god: this.extractValue(baseContent, 'god'),
ap: this.extractValue(baseContent, 'ap'),
type: this.extractValue(baseContent, 'type') || '攻击',
dict: this.extractValue(baseContent, 'dict'),
desc_global: this.extractValue(baseContent, 'desc_global').replace(/<br>/g, '\n'),
sub: this.extractValue(baseContent, 'sub'),
isinspiration: baseContent.includes('isinspiration = 1'),
isgod_inspiration: baseContent.includes('isgod_inspiration = 1'),
inspirations: [],
god_inspirations: {
circen: [],
diallos: [],
nihilum: [],
secred: [],
vitor: []
}
};
this.cards[cardName] = card;
}
console.log('Card Manager: 解析完成,共', Object.keys(this.cards).length, '张卡牌');
}
extractValue(content, key) {
// 匹配字符串值
const stringMatch = content.match(new RegExp(`${key}\\s*=\\s*"([^"]*)"`, 'i'));
if (stringMatch) {
return stringMatch[1];
}
// 匹配数字值
const numberMatch = content.match(new RegExp(`${key}\\s*=\\s*([\\d.]+)`, 'i'));
if (numberMatch) {
return numberMatch[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);
}
})();