TeamBuilder.js:修订间差异
来自卡厄思梦境WIKI
创建页面,内容为“(function() { 'use strict'; // 等待 DOM 加载 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initTeamBuilder); } else { initTeamBuilder(); } function initTeamBuilder() { // 只在包含 team-builder 的页面初始化 if (!document.getElementById('team-builder')) return; const state = { selectedCharacter: null, se…” |
无编辑摘要 |
||
| (未显示同一用户的4个中间版本) | |||
| 第1行: | 第1行: | ||
(function() { | (function() { | ||
'use strict'; | 'use strict'; | ||
mw.loader.load( mw.util.getUrl( 'MediaWiki:TeamBuilder.css', { action: 'raw', ctype: 'text/css' } ), 'text/css' ); | |||
// 等待 DOM 加载 | // 等待 DOM 加载 | ||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
| 第20行: | 第20行: | ||
selectedRing: null, | selectedRing: null, | ||
deckCards: [], | deckCards: [], | ||
availableCards: [] | availableCards: [], | ||
currentEquipmentType: null // 记录当前选择的装备类型 | |||
}; | }; | ||
// 装备缓存 | |||
const equipmentCache = {}; | |||
// 模态框通用函数 | // 模态框通用函数 | ||
| 第37行: | 第41行: | ||
modal.style.justifyContent = 'center'; | modal.style.justifyContent = 'center'; | ||
modal.style.zIndex = '10000'; | modal.style.zIndex = '10000'; | ||
document.body.style.overflow = 'hidden'; | |||
} | } | ||
} | } | ||
| 第42行: | 第47行: | ||
function hideModal(modalId) { | function hideModal(modalId) { | ||
const modal = document.getElementById(modalId); | const modal = document.getElementById(modalId); | ||
if (modal) modal.style.display = 'none'; | if (modal) { | ||
modal.style.display = 'none'; | |||
document.body.style.overflow = 'auto'; | |||
} | |||
} | } | ||
| 第58行: | 第66行: | ||
partnerSlot.addEventListener('click', function() { | partnerSlot.addEventListener('click', function() { | ||
showModal('partner-modal'); | showModal('partner-modal'); | ||
}); | |||
} | |||
// 卡牌区域点击 | |||
const deckArea = document.getElementById('deck-area'); | |||
if (deckArea) { | |||
deckArea.addEventListener('click', function(e) { | |||
// 只有点击背景区域才打开模态框,不包括已选卡牌 | |||
if (e.target === this || e.target.closest('.deck-area') === this) { | |||
if (state.selectedCharacter) { | |||
showModal('card-modal'); | |||
} else { | |||
alert('请先选择战斗员'); | |||
} | |||
} | |||
}); | }); | ||
} | } | ||
| 第65行: | 第88行: | ||
slot.addEventListener('click', function() { | slot.addEventListener('click', function() { | ||
const type = this.getAttribute('data-type'); | const type = this.getAttribute('data-type'); | ||
state.currentEquipmentType = type; // 记录当前类型 | |||
loadEquipmentList(type); | loadEquipmentList(type); | ||
document.getElementById('equipment-modal-title').textContent = '选择' + type; | document.getElementById('equipment-modal-title').textContent = '选择' + type; | ||
| 第75行: | 第99行: | ||
btn.addEventListener('click', function() { | btn.addEventListener('click', function() { | ||
this.closest('.tb-modal').style.display = 'none'; | this.closest('.tb-modal').style.display = 'none'; | ||
document.body.style.overflow = 'auto'; | |||
}); | }); | ||
}); | }); | ||
| 第83行: | 第108行: | ||
if (e.target === this) { | if (e.target === this) { | ||
this.style.display = 'none'; | this.style.display = 'none'; | ||
document.body.style.overflow = 'auto'; | |||
} | } | ||
}); | }); | ||
| 第94行: | 第120行: | ||
if (card) { | if (card) { | ||
const name = card.getAttribute('data-character-name'); | const name = card.getAttribute('data-character-name'); | ||
selectCharacter(name); | |||
selectCharacter(name | |||
hideModal('character-modal'); | hideModal('character-modal'); | ||
} | } | ||
| 第108行: | 第133行: | ||
if (card) { | if (card) { | ||
const name = card.getAttribute('data-partner-name'); | const name = card.getAttribute('data-partner-name'); | ||
const id = card.getAttribute('data-partner-id'); | const id = card.getAttribute('data-partner-id'); | ||
selectPartner(name | selectPartner(name, id); | ||
hideModal('partner-modal'); | hideModal('partner-modal'); | ||
} | } | ||
| 第117行: | 第141行: | ||
// 选择战斗员 | // 选择战斗员 | ||
function selectCharacter(name | function selectCharacter(name) { | ||
state.selectedCharacter = | state.selectedCharacter = name; | ||
const slot = document.getElementById('character-slot'); | const slot = document.getElementById('character-slot'); | ||
// 使用 MediaWiki 文件路径 | |||
const imgPath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/战斗员图鉴_' + encodeURIComponent(name) + '.png'; | |||
slot.innerHTML = ` | slot.innerHTML = ` | ||
<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"> | <div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"> | ||
<img src="${ | <img src="${imgPath}" style="width: 100%; height: 100%; object-fit: cover;" | ||
onerror="this.src='${mw.config.get('wgScriptPath')}/resources/assets/file-type-icons/fileicon-image.png';" /> | |||
</div> | </div> | ||
`; | `; | ||
// 清空卡组和可用卡牌 | |||
state.deckCards = []; | |||
state.availableCards = []; | |||
updateDeckDisplay(); | |||
// 加载角色卡牌 | |||
loadCharacterCards(name); | loadCharacterCards(name); | ||
} | } | ||
// | // 选择伙伴(修复:使用正确的文件名前缀) | ||
function selectPartner(name | function selectPartner(name, id) { | ||
state.selectedPartner = { name | state.selectedPartner = { name, id }; | ||
const slot = document.getElementById('partner-slot'); | const slot = document.getElementById('partner-slot'); | ||
const | |||
// 修正:使用 portrait_character_wide_ 而不是 face_character_wide_ | |||
const imgPath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/portrait_character_wide_' + encodeURIComponent(id) + '.png'; | |||
slot.innerHTML = ` | slot.innerHTML = ` | ||
<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"> | <div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"> | ||
<img src=" | <img src="${imgPath}" style="width: 100%; height: 100%; object-fit: cover;" | ||
onerror="this.src='${mw.config.get('wgScriptPath')}/resources/assets/file-type-icons/fileicon-image.png';" /> | |||
</div> | </div> | ||
`; | `; | ||
} | } | ||
// | // 加载装备列表(优化:添加缓存) | ||
function loadEquipmentList(type) { | function loadEquipmentList(type) { | ||
const listContainer = document.getElementById('equipment-list'); | const listContainer = document.getElementById('equipment-list'); | ||
listContainer.innerHTML = '<p style="color: white;">加载中...</p>'; | listContainer.innerHTML = '<p style="color: white;">加载中...</p>'; | ||
// 使用 API 调用 Lua | // 检查缓存 | ||
new mw.Api(). | const cacheKey = 'all_equipment'; | ||
if (equipmentCache[cacheKey]) { | |||
filterAndDisplayEquipment(equipmentCache[cacheKey], type, listContainer); | |||
return; | |||
} | |||
// 使用 API 调用 Lua 模块,获取筛选后的装备 | |||
new mw.Api().post({ | |||
action: 'parse', | action: 'parse', | ||
text: '{{#invoke:装备|generateCards}}', | text: '{{#invoke:装备|generateCards}}', | ||
contentmodel: 'wikitext', | contentmodel: 'wikitext', | ||
prop: 'text' | prop: 'text', | ||
disablelimitreport: 1, | |||
disableeditsection: 1 | |||
}).done(function(data) { | }).done(function(data) { | ||
if (data.parse && data.parse.text) { | if (data.parse && data.parse.text) { | ||
// 缓存结果 | |||
equipmentCache[cacheKey] = data.parse.text['*']; | |||
filterAndDisplayEquipment(equipmentCache[cacheKey], type, listContainer); | |||
} | } | ||
}).fail(function() { | |||
listContainer.innerHTML = '<p style="color: red;">加载失败</p>'; | |||
}); | }); | ||
} | |||
// 新增函数:过滤并显示装备 | |||
function filterAndDisplayEquipment(htmlContent, type, container) { | |||
const tempDiv = document.createElement('div'); | |||
tempDiv.innerHTML = htmlContent; | |||
const allEquips = tempDiv.querySelectorAll('.equipment-wrapper'); | |||
container.innerHTML = ''; | |||
allEquips.forEach(function(equipWrapper) { | |||
const equipType = equipWrapper.getAttribute('data-param3'); | |||
if (equipType === type) { | |||
container.appendChild(equipWrapper.cloneNode(true)); | |||
} | |||
}); | |||
if (container.children.length === 0) { | |||
container.innerHTML = '<p style="color: white;">没有找到该类型装备</p>'; | |||
} | |||
attachEquipmentClickHandlers(); | |||
} | } | ||
// 装备点击处理 | // 装备点击处理 | ||
function attachEquipmentClickHandlers( | function attachEquipmentClickHandlers() { | ||
document.querySelectorAll('.equipment-card').forEach(card => { | document.querySelectorAll('#equipment-list .equipment-card').forEach(card => { | ||
card.style.cursor = 'pointer'; | |||
card.addEventListener('click', function() { | card.addEventListener('click', function() { | ||
const name = this.getAttribute('data-equipment'); | const name = this.getAttribute('data-equipment'); | ||
selectEquipment( | const rarity = this.closest('.equipment-wrapper').getAttribute('data-param1'); | ||
selectEquipment(state.currentEquipmentType, name, rarity); | |||
hideModal('equipment-modal'); | hideModal('equipment-modal'); | ||
}); | }); | ||
| 第173行: | 第247行: | ||
// 选择装备 | // 选择装备 | ||
function selectEquipment(type, name) { | function selectEquipment(type, name, rarity) { | ||
// 根据类型确定目标槽位 | |||
const slotMap = { | const slotMap = { | ||
'武器': 'weapon-slot', | '武器': 'weapon-slot', | ||
| 第180行: | 第255行: | ||
}; | }; | ||
const slotId = slotMap[type]; | const slotId = slotMap[type]; | ||
if (!slotId) return; | |||
const slot = document.getElementById(slotId); | const slot = document.getElementById(slotId); | ||
if (!slot) return; | |||
// 保存到状态 | |||
if (type === '武器') state.selectedWeapon = name; | |||
else if (type === '装甲') state.selectedArmor = name; | |||
else if (type === '戒指') state.selectedRing = name; | |||
// | // 获取装备 ID(需要查询) | ||
new mw.Api().get({ | new mw.Api().get({ | ||
action: 'parse', | action: 'parse', | ||
page: name, | |||
prop: 'wikitext' | |||
}).done(function(data) { | }).done(function(data) { | ||
if (data.parse && data.parse. | if (data.parse && data.parse.wikitext) { | ||
const wikitext = data.parse.wikitext['*']; | |||
const idMatch = wikitext.match(/\|id\s*=\s*([^\n|]+)/); | |||
const equipId = idMatch ? idMatch[1].trim() : '0000'; | |||
// 格式化 ID | |||
let formattedId = equipId; | |||
if (!equipId.includes('_')) { | |||
const numId = parseInt(equipId); | |||
if (!isNaN(numId)) { | |||
formattedId = numId.toString().padStart(4, '0'); | |||
} | |||
} | |||
const imgPath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/relic_' + formattedId + '.png'; | |||
const framePath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/frame_item_rarity_' + encodeURIComponent(rarity) + '.png'; | |||
// 描边效果(简单文本阴影) | |||
const textShadow = '-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000'; | |||
slot.innerHTML = ` | |||
<div style="position:relative;width:124px;height:124px"> | |||
<div style="position:absolute;top:0px;left:0px"> | |||
<img src="${framePath}" style="width:124px;height:124px;" /> | |||
</div> | |||
<div style="position:absolute;top:8px;left:8px;width:108px;height:108px;border:1px solid #fff;"></div> | |||
<div style="position:absolute;top:6px;left:6px;"> | |||
<img src="${imgPath}" style="width:112px;height:112px;" | |||
onerror="this.src='${mw.config.get('wgScriptPath')}/resources/assets/file-type-icons/fileicon-image.png';" /> | |||
</div> | |||
<div style="position:absolute;bottom:2px;left:6px;color:white;font-size:12px;text-shadow:${textShadow};">${mw.html.escape(name)}</div> | |||
</div> | |||
`; | |||
} | } | ||
}).fail(function() { | |||
// 失败时使用默认显示 | |||
slot.innerHTML = ` | |||
<div style="position:relative;width:124px;height:124px;display:flex;align-items:center;justify-content:center;"> | |||
<span style="color:white;font-size:12px;text-align:center;">${mw.html.escape(name)}</span> | |||
</div> | |||
`; | |||
}); | }); | ||
} | } | ||
// 加载角色卡牌 | // 加载角色卡牌 | ||
function loadCharacterCards(characterName) { | function loadCharacterCards(characterName) { | ||
const | const listContainer = document.getElementById('available-cards-list'); | ||
listContainer.innerHTML = '<p style="color: white;">加载卡牌...</p>'; | |||
// 获取角色页面数据 | // 获取角色页面数据 | ||
| 第215行: | 第330行: | ||
parseCharacterCards(wikitext, characterName); | parseCharacterCards(wikitext, characterName); | ||
} | } | ||
}).fail(function() { | |||
listContainer.innerHTML = '<p style="color: red;">加载失败</p>'; | |||
}); | }); | ||
} | } | ||
| 第224行: | 第341行: | ||
// 提取卡牌信息 | // 提取卡牌信息 | ||
const patterns = { | const patterns = { | ||
selfAware: /\|自我意识技能=([^\n]+)/, | selfAware: /\|自我意识技能\s*=\s*([^\n]+)/, | ||
startCards: [ | startCards: [ | ||
/\|起始卡牌_1=([^\n]+)/, | /\|起始卡牌_1\s*=\s*([^\n]+)/, | ||
/\|起始卡牌_2=([^\n]+)/, | /\|起始卡牌_2\s*=\s*([^\n]+)/, | ||
/\|起始卡牌_3=([^\n]+)/, | /\|起始卡牌_3\s*=\s*([^\n]+)/, | ||
/\|起始卡牌_4=([^\n]+)/ | /\|起始卡牌_4\s*=\s*([^\n]+)/ | ||
], | ], | ||
uniqueCards: [ | uniqueCards: [ | ||
/\|独特卡牌_1=([^\n]+)/, | /\|独特卡牌_1\s*=\s*([^\n]+)/, | ||
/\|独特卡牌_2=([^\n]+)/, | /\|独特卡牌_2\s*=\s*([^\n]+)/, | ||
/\|独特卡牌_3=([^\n]+)/, | /\|独特卡牌_3\s*=\s*([^\n]+)/, | ||
/\|独特卡牌_4=([^\n]+)/ | /\|独特卡牌_4\s*=\s*([^\n]+)/ | ||
] | ] | ||
}; | }; | ||
| 第241行: | 第358行: | ||
// 提取自我意识技能 | // 提取自我意识技能 | ||
const selfAwareMatch = wikitext.match(patterns.selfAware); | const selfAwareMatch = wikitext.match(patterns.selfAware); | ||
if (selfAwareMatch) { | if (selfAwareMatch && selfAwareMatch[1].trim()) { | ||
cards.push({ type: 'self', name: selfAwareMatch[1].trim() }); | cards.push({ type: 'self', name: selfAwareMatch[1].trim() }); | ||
} | } | ||
| 第261行: | 第378行: | ||
}); | }); | ||
state.availableCards = cards; | |||
displayAvailableCards(cards, characterName); | displayAvailableCards(cards, characterName); | ||
} | } | ||
// | // 显示可用卡牌(支持变体) | ||
function displayAvailableCards(cards, characterName) { | function displayAvailableCards(cards, characterName) { | ||
const | const listContainer = document.getElementById('available-cards-list'); | ||
listContainer.innerHTML = ''; | |||
if (cards.length === 0) { | |||
listContainer.innerHTML = '<p style="color: white;">该角色没有卡牌</p>'; | |||
return; | |||
} | |||
// 添加变体选择界面 | |||
const variantSelector = document.createElement('div'); | |||
variantSelector.style.cssText = 'width: 100%; padding: 10px; margin-bottom: 20px; background: rgba(255,255,255,0.1); border-radius: 5px;'; | |||
variantSelector.innerHTML = ` | |||
<div style="color: white; margin-bottom: 10px; font-weight: bold;">卡牌变体选择:</div> | |||
<label style="color: white; margin-right: 15px; cursor: pointer;"> | |||
<input type="checkbox" id="enable-inspiration" style="margin-right: 5px;"/> 启用灵光一闪 | |||
</label> | |||
<label style="color: white; cursor: pointer;"> | |||
<input type="checkbox" id="enable-god-inspiration" style="margin-right: 5px;"/> 启用神光一闪 | |||
</label> | |||
`; | |||
listContainer.appendChild(variantSelector); | |||
const cardsContainer = document.createElement('div'); | |||
cardsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;'; | |||
listContainer.appendChild(cardsContainer); | |||
cards.forEach(card => { | cards.forEach(card => { | ||
const | const cardWrapper = document.createElement('div'); | ||
cardWrapper.style.cursor = 'pointer'; | |||
cardWrapper.style.position = 'relative'; | |||
cardWrapper.setAttribute('data-card-name', card.name); | |||
// | // 渲染基础卡牌 | ||
renderCard(characterName, card.name, null, null, null, function(html) { | |||
cardWrapper.innerHTML = html; | |||
// 添加点击事件处理 | |||
cardWrapper.addEventListener('click', function(e) { | |||
}).done(function(data) { | e.stopPropagation(); | ||
// 检查是否启用变体 | |||
const inspirationEnabled = document.getElementById('enable-inspiration')?.checked; | |||
const godInspirationEnabled = document.getElementById('enable-god-inspiration')?.checked; | |||
if (inspirationEnabled || godInspirationEnabled) { | |||
// 显示变体选择界面 | |||
showVariantSelection(characterName, card.name, inspirationEnabled, godInspirationEnabled); | |||
} else { | |||
// 添加基础卡牌 | |||
addCardToDeck(card.name, characterName, html); | |||
} | |||
}); | |||
cardsContainer.appendChild(cardWrapper); | |||
}); | |||
}); | |||
} | |||
// 新增函数:渲染单个卡牌 | |||
function renderCard(characterName, cardName, variantType, variantParam, variantIndex, callback) { | |||
let invokeText = `{{#invoke:卡牌|main|${characterName}|${cardName}`; | |||
if (variantType) { | |||
invokeText += `|${variantType}`; | |||
if (variantParam) { | |||
invokeText += `|${variantParam}`; | |||
if (variantIndex) { | |||
invokeText += `|${variantIndex}`; | |||
} | |||
} | |||
} | |||
invokeText += '}}'; | |||
new mw.Api().post({ | |||
action: 'parse', | |||
text: invokeText, | |||
contentmodel: 'wikitext', | |||
prop: 'text', | |||
disablelimitreport: 1, | |||
disableeditsection: 1 | |||
}).done(function(data) { | |||
if (data.parse && data.parse.text) { | |||
callback(data.parse.text['*']); | |||
} | |||
}).fail(function() { | |||
callback('<div style="color:red;padding:10px;">加载失败</div>'); | |||
}); | |||
} | |||
// 新增函数:显示变体选择 | |||
function showVariantSelection(characterName, cardName, inspirationEnabled, godInspirationEnabled) { | |||
const modal = document.createElement('div'); | |||
modal.style.cssText = 'position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.9); z-index: 20000; display: flex; align-items: center; justify-content: center;'; | |||
const content = document.createElement('div'); | |||
content.style.cssText = 'background: #2a2a2a; padding: 20px; border-radius: 8px; max-width: 90vw; max-height: 90vh; overflow: auto; position: relative;'; | |||
content.innerHTML = '<h3 style="color: white; margin-bottom: 20px;">选择卡牌变体</h3>'; | |||
const variantsContainer = document.createElement('div'); | |||
variantsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; justify-content: center;'; | |||
// 添加基础卡牌 | |||
const baseCardWrapper = document.createElement('div'); | |||
baseCardWrapper.style.cssText = 'border: 2px solid transparent; cursor: pointer; transition: all 0.3s;'; | |||
baseCardWrapper.onmouseover = function() { this.style.borderColor = '#4da6cc'; }; | |||
baseCardWrapper.onmouseout = function() { this.style.borderColor = 'transparent'; }; | |||
renderCard(characterName, cardName, null, null, null, function(html) { | |||
const container = document.createElement('div'); | |||
container.innerHTML = '<div style="text-align: center; color: white; margin-bottom: 5px; font-weight: bold;">基础卡牌</div>' + html; | |||
baseCardWrapper.appendChild(container); | |||
baseCardWrapper.addEventListener('click', function() { | |||
addCardToDeck(cardName, characterName, html); | |||
document.body.removeChild(modal); | |||
}); | |||
variantsContainer.appendChild(baseCardWrapper); | |||
}); | |||
// 添加灵光一闪变体(如果启用) | |||
if (inspirationEnabled) { | |||
// 尝试加载最多3个灵光一闪变体 | |||
for (let i = 1; i <= 3; i++) { | |||
(function(index) { | |||
const inspirationWrapper = document.createElement('div'); | |||
inspirationWrapper.style.cssText = 'border: 2px solid transparent; cursor: pointer; transition: all 0.3s;'; | |||
inspirationWrapper.onmouseover = function() { this.style.borderColor = '#6fd8fe'; }; | |||
inspirationWrapper.onmouseout = function() { this.style.borderColor = 'transparent'; }; | |||
/ | renderCard(characterName, cardName, '灵光一闪', index.toString(), null, function(html) { | ||
if (!html.includes('错误')) { | |||
const container = document.createElement('div'); | |||
container.innerHTML = '<div style="text-align: center; color: #6fd8fe; margin-bottom: 5px; font-weight: bold;">灵光一闪 ' + index + '</div>' + html; | |||
inspirationWrapper.appendChild(container); | |||
inspirationWrapper.addEventListener('click', function() { | |||
addCardToDeck(cardName + ' (灵光' + index + ')', characterName, html); | |||
document.body.removeChild(modal); | |||
}); | |||
variantsContainer.appendChild(inspirationWrapper); | |||
} | |||
}); | }); | ||
})(i); | |||
} | |||
} | |||
// 添加神光一闪变体(如果启用) | |||
if (godInspirationEnabled) { | |||
// 神光一闪需要指定战斗员 | |||
const godCharacters = ['circen', 'diallos', 'nihilum', 'secred', 'vitor']; | |||
godCharacters.forEach(function(godChar) { | |||
// 尝试加载每个战斗员的神光一闪 | |||
for (let i = 1; i <= 2; i++) { | |||
(function(char, index) { | |||
const godWrapper = document.createElement('div'); | |||
godWrapper.style.cssText = 'border: 2px solid transparent; cursor: pointer; transition: all 0.3s;'; | |||
godWrapper.onmouseover = function() { this.style.borderColor = '#ffd36a'; }; | |||
godWrapper.onmouseout = function() { this.style.borderColor = 'transparent'; }; | |||
renderCard(characterName, cardName, '神光一闪', char, index.toString(), function(html) { | |||
if (!html.includes('错误')) { | |||
const container = document.createElement('div'); | |||
container.innerHTML = '<div style="text-align: center; color: #ffd36a; margin-bottom: 5px; font-weight: bold;">神光一闪 (' + char + ' ' + index + ')</div>' + html; | |||
godWrapper.appendChild(container); | |||
godWrapper.addEventListener('click', function() { | |||
addCardToDeck(cardName + ' (神光-' + char + index + ')', characterName, html); | |||
document.body.removeChild(modal); | |||
}); | |||
variantsContainer.appendChild(godWrapper); | |||
} | |||
}); | |||
})(godChar, i); | |||
} | } | ||
}); | }); | ||
} | |||
// 添加关闭按钮 | |||
const closeBtn = document.createElement('div'); | |||
closeBtn.innerHTML = '×'; | |||
closeBtn.style.cssText = 'position: absolute; top: 10px; right: 10px; color: white; font-size: 32px; cursor: pointer; transition: all 0.3s;'; | |||
closeBtn.onmouseover = function() { this.style.color = '#ff4444'; }; | |||
closeBtn.onmouseout = function() { this.style.color = 'white'; }; | |||
closeBtn.addEventListener('click', function() { | |||
document.body.removeChild(modal); | |||
}); | |||
content.appendChild(closeBtn); | |||
content.appendChild(variantsContainer); | |||
modal.appendChild(content); | |||
document.body.appendChild(modal); | |||
// 点击背景关闭 | |||
modal.addEventListener('click', function(e) { | |||
if (e.target === modal) { | |||
document.body.removeChild(modal); | |||
} | |||
}); | }); | ||
} | } | ||
// 添加卡牌到卡组 | // 添加卡牌到卡组 | ||
function addCardToDeck(cardName, characterName) { | function addCardToDeck(cardName, characterName, cardHtml) { | ||
state.deckCards.push({ cardName, characterName }); | state.deckCards.push({ cardName, characterName, cardHtml }); | ||
hideModal('card-modal'); | |||
updateDeckDisplay(); | updateDeckDisplay(); | ||
} | } | ||
| 第302行: | 第590行: | ||
// 更新卡组显示 | // 更新卡组显示 | ||
function updateDeckDisplay() { | function updateDeckDisplay() { | ||
const | const deckArea = document.getElementById('deck-area'); | ||
const deckCards = document.getElementById('deck-cards'); | |||
const plusSign = deckArea.querySelector('span'); | |||
deckCards.innerHTML = ''; | |||
if (state.deckCards.length === 0) { | |||
deckCards.style.display = 'none'; | |||
if (plusSign) plusSign.style.display = 'block'; | |||
return; | |||
} | |||
deckCards.style.display = 'flex'; | |||
if (plusSign) plusSign.style.display = 'none'; | |||
state.deckCards.forEach((card, index) => { | state.deckCards.forEach((card, index) => { | ||
const cardDiv = document.createElement('div'); | const cardDiv = document.createElement('div'); | ||
cardDiv.style.position = 'relative'; | cardDiv.style.position = 'relative'; | ||
cardDiv.innerHTML = card.cardHtml; | |||
// | // 添加删除按钮 | ||
const removeBtn = document.createElement('div'); | |||
removeBtn.className = 'tb-remove-btn'; | |||
removeBtn.innerHTML = '×'; | |||
removeBtn.style.cssText = 'position: absolute; top: 5px; right: 5px; background: #ff4444; color: white; border-radius: 50%; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 100; font-size: 20px; font-weight: bold; box-shadow: 0 2px 4px rgba(0,0,0,0.3);'; | |||
removeBtn.addEventListener('click', function(e) { | |||
e.stopPropagation(); | |||
state.deckCards.splice(index, 1); | |||
updateDeckDisplay(); | |||
}); | }); | ||
cardDiv.appendChild(removeBtn); | |||
deckCards.appendChild(cardDiv); | |||
}); | }); | ||
} | } | ||
2025年10月18日 (六) 19:26的最新版本
(function() {
'use strict';
mw.loader.load( mw.util.getUrl( 'MediaWiki:TeamBuilder.css', { action: 'raw', ctype: 'text/css' } ), 'text/css' );
// 等待 DOM 加载
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTeamBuilder);
} else {
initTeamBuilder();
}
function initTeamBuilder() {
// 只在包含 team-builder 的页面初始化
if (!document.getElementById('team-builder')) return;
const state = {
selectedCharacter: null,
selectedPartner: null,
selectedWeapon: null,
selectedArmor: null,
selectedRing: null,
deckCards: [],
availableCards: [],
currentEquipmentType: null // 记录当前选择的装备类型
};
// 装备缓存
const equipmentCache = {};
// 模态框通用函数
function showModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'flex';
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100vw';
modal.style.height = '100vh';
modal.style.backgroundColor = 'rgba(0,0,0,0.8)';
modal.style.alignItems = 'center';
modal.style.justifyContent = 'center';
modal.style.zIndex = '10000';
document.body.style.overflow = 'hidden';
}
}
function hideModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'none';
document.body.style.overflow = 'auto';
}
}
// 战斗员选择
const characterSlot = document.getElementById('character-slot');
if (characterSlot) {
characterSlot.addEventListener('click', function() {
showModal('character-modal');
});
}
// 伙伴选择
const partnerSlot = document.getElementById('partner-slot');
if (partnerSlot) {
partnerSlot.addEventListener('click', function() {
showModal('partner-modal');
});
}
// 卡牌区域点击
const deckArea = document.getElementById('deck-area');
if (deckArea) {
deckArea.addEventListener('click', function(e) {
// 只有点击背景区域才打开模态框,不包括已选卡牌
if (e.target === this || e.target.closest('.deck-area') === this) {
if (state.selectedCharacter) {
showModal('card-modal');
} else {
alert('请先选择战斗员');
}
}
});
}
// 装备选择
document.querySelectorAll('.equip-slot').forEach(slot => {
slot.addEventListener('click', function() {
const type = this.getAttribute('data-type');
state.currentEquipmentType = type; // 记录当前类型
loadEquipmentList(type);
document.getElementById('equipment-modal-title').textContent = '选择' + type;
showModal('equipment-modal');
});
});
// 关闭模态框
document.querySelectorAll('.tb-modal-close').forEach(btn => {
btn.addEventListener('click', function() {
this.closest('.tb-modal').style.display = 'none';
document.body.style.overflow = 'auto';
});
});
// 点击模态框背景关闭
document.querySelectorAll('.tb-modal').forEach(modal => {
modal.addEventListener('click', function(e) {
if (e.target === this) {
this.style.display = 'none';
document.body.style.overflow = 'auto';
}
});
});
// 战斗员列表点击事件(事件委托)
const characterList = document.getElementById('character-list');
if (characterList) {
characterList.addEventListener('click', function(e) {
const card = e.target.closest('[data-character-name]');
if (card) {
const name = card.getAttribute('data-character-name');
selectCharacter(name);
hideModal('character-modal');
}
});
}
// 伙伴列表点击事件
const partnerList = document.getElementById('partner-list');
if (partnerList) {
partnerList.addEventListener('click', function(e) {
const card = e.target.closest('[data-partner-name]');
if (card) {
const name = card.getAttribute('data-partner-name');
const id = card.getAttribute('data-partner-id');
selectPartner(name, id);
hideModal('partner-modal');
}
});
}
// 选择战斗员
function selectCharacter(name) {
state.selectedCharacter = name;
const slot = document.getElementById('character-slot');
// 使用 MediaWiki 文件路径
const imgPath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/战斗员图鉴_' + encodeURIComponent(name) + '.png';
slot.innerHTML = `
<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">
<img src="${imgPath}" style="width: 100%; height: 100%; object-fit: cover;"
onerror="this.src='${mw.config.get('wgScriptPath')}/resources/assets/file-type-icons/fileicon-image.png';" />
</div>
`;
// 清空卡组和可用卡牌
state.deckCards = [];
state.availableCards = [];
updateDeckDisplay();
// 加载角色卡牌
loadCharacterCards(name);
}
// 选择伙伴(修复:使用正确的文件名前缀)
function selectPartner(name, id) {
state.selectedPartner = { name, id };
const slot = document.getElementById('partner-slot');
// 修正:使用 portrait_character_wide_ 而不是 face_character_wide_
const imgPath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/portrait_character_wide_' + encodeURIComponent(id) + '.png';
slot.innerHTML = `
<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">
<img src="${imgPath}" style="width: 100%; height: 100%; object-fit: cover;"
onerror="this.src='${mw.config.get('wgScriptPath')}/resources/assets/file-type-icons/fileicon-image.png';" />
</div>
`;
}
// 加载装备列表(优化:添加缓存)
function loadEquipmentList(type) {
const listContainer = document.getElementById('equipment-list');
listContainer.innerHTML = '<p style="color: white;">加载中...</p>';
// 检查缓存
const cacheKey = 'all_equipment';
if (equipmentCache[cacheKey]) {
filterAndDisplayEquipment(equipmentCache[cacheKey], type, listContainer);
return;
}
// 使用 API 调用 Lua 模块,获取筛选后的装备
new mw.Api().post({
action: 'parse',
text: '{{#invoke:装备|generateCards}}',
contentmodel: 'wikitext',
prop: 'text',
disablelimitreport: 1,
disableeditsection: 1
}).done(function(data) {
if (data.parse && data.parse.text) {
// 缓存结果
equipmentCache[cacheKey] = data.parse.text['*'];
filterAndDisplayEquipment(equipmentCache[cacheKey], type, listContainer);
}
}).fail(function() {
listContainer.innerHTML = '<p style="color: red;">加载失败</p>';
});
}
// 新增函数:过滤并显示装备
function filterAndDisplayEquipment(htmlContent, type, container) {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlContent;
const allEquips = tempDiv.querySelectorAll('.equipment-wrapper');
container.innerHTML = '';
allEquips.forEach(function(equipWrapper) {
const equipType = equipWrapper.getAttribute('data-param3');
if (equipType === type) {
container.appendChild(equipWrapper.cloneNode(true));
}
});
if (container.children.length === 0) {
container.innerHTML = '<p style="color: white;">没有找到该类型装备</p>';
}
attachEquipmentClickHandlers();
}
// 装备点击处理
function attachEquipmentClickHandlers() {
document.querySelectorAll('#equipment-list .equipment-card').forEach(card => {
card.style.cursor = 'pointer';
card.addEventListener('click', function() {
const name = this.getAttribute('data-equipment');
const rarity = this.closest('.equipment-wrapper').getAttribute('data-param1');
selectEquipment(state.currentEquipmentType, name, rarity);
hideModal('equipment-modal');
});
});
}
// 选择装备
function selectEquipment(type, name, rarity) {
// 根据类型确定目标槽位
const slotMap = {
'武器': 'weapon-slot',
'装甲': 'armor-slot',
'戒指': 'ring-slot'
};
const slotId = slotMap[type];
if (!slotId) return;
const slot = document.getElementById(slotId);
if (!slot) return;
// 保存到状态
if (type === '武器') state.selectedWeapon = name;
else if (type === '装甲') state.selectedArmor = name;
else if (type === '戒指') state.selectedRing = name;
// 获取装备 ID(需要查询)
new mw.Api().get({
action: 'parse',
page: name,
prop: 'wikitext'
}).done(function(data) {
if (data.parse && data.parse.wikitext) {
const wikitext = data.parse.wikitext['*'];
const idMatch = wikitext.match(/\|id\s*=\s*([^\n|]+)/);
const equipId = idMatch ? idMatch[1].trim() : '0000';
// 格式化 ID
let formattedId = equipId;
if (!equipId.includes('_')) {
const numId = parseInt(equipId);
if (!isNaN(numId)) {
formattedId = numId.toString().padStart(4, '0');
}
}
const imgPath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/relic_' + formattedId + '.png';
const framePath = mw.config.get('wgScriptPath') + '/index.php?title=Special:Redirect/file/frame_item_rarity_' + encodeURIComponent(rarity) + '.png';
// 描边效果(简单文本阴影)
const textShadow = '-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000';
slot.innerHTML = `
<div style="position:relative;width:124px;height:124px">
<div style="position:absolute;top:0px;left:0px">
<img src="${framePath}" style="width:124px;height:124px;" />
</div>
<div style="position:absolute;top:8px;left:8px;width:108px;height:108px;border:1px solid #fff;"></div>
<div style="position:absolute;top:6px;left:6px;">
<img src="${imgPath}" style="width:112px;height:112px;"
onerror="this.src='${mw.config.get('wgScriptPath')}/resources/assets/file-type-icons/fileicon-image.png';" />
</div>
<div style="position:absolute;bottom:2px;left:6px;color:white;font-size:12px;text-shadow:${textShadow};">${mw.html.escape(name)}</div>
</div>
`;
}
}).fail(function() {
// 失败时使用默认显示
slot.innerHTML = `
<div style="position:relative;width:124px;height:124px;display:flex;align-items:center;justify-content:center;">
<span style="color:white;font-size:12px;text-align:center;">${mw.html.escape(name)}</span>
</div>
`;
});
}
// 加载角色卡牌
function loadCharacterCards(characterName) {
const listContainer = document.getElementById('available-cards-list');
listContainer.innerHTML = '<p style="color: white;">加载卡牌...</p>';
// 获取角色页面数据
new mw.Api().get({
action: 'parse',
page: characterName,
prop: 'wikitext'
}).done(function(data) {
if (data.parse && data.parse.wikitext) {
const wikitext = data.parse.wikitext['*'];
parseCharacterCards(wikitext, characterName);
}
}).fail(function() {
listContainer.innerHTML = '<p style="color: red;">加载失败</p>';
});
}
// 解析角色卡牌
function parseCharacterCards(wikitext, characterName) {
const cards = [];
// 提取卡牌信息
const patterns = {
selfAware: /\|自我意识技能\s*=\s*([^\n]+)/,
startCards: [
/\|起始卡牌_1\s*=\s*([^\n]+)/,
/\|起始卡牌_2\s*=\s*([^\n]+)/,
/\|起始卡牌_3\s*=\s*([^\n]+)/,
/\|起始卡牌_4\s*=\s*([^\n]+)/
],
uniqueCards: [
/\|独特卡牌_1\s*=\s*([^\n]+)/,
/\|独特卡牌_2\s*=\s*([^\n]+)/,
/\|独特卡牌_3\s*=\s*([^\n]+)/,
/\|独特卡牌_4\s*=\s*([^\n]+)/
]
};
// 提取自我意识技能
const selfAwareMatch = wikitext.match(patterns.selfAware);
if (selfAwareMatch && selfAwareMatch[1].trim()) {
cards.push({ type: 'self', name: selfAwareMatch[1].trim() });
}
// 提取起始卡牌
patterns.startCards.forEach(pattern => {
const match = wikitext.match(pattern);
if (match && match[1].trim()) {
cards.push({ type: 'start', name: match[1].trim() });
}
});
// 提取独特卡牌
patterns.uniqueCards.forEach(pattern => {
const match = wikitext.match(pattern);
if (match && match[1].trim()) {
cards.push({ type: 'unique', name: match[1].trim() });
}
});
state.availableCards = cards;
displayAvailableCards(cards, characterName);
}
// 显示可用卡牌(支持变体)
function displayAvailableCards(cards, characterName) {
const listContainer = document.getElementById('available-cards-list');
listContainer.innerHTML = '';
if (cards.length === 0) {
listContainer.innerHTML = '<p style="color: white;">该角色没有卡牌</p>';
return;
}
// 添加变体选择界面
const variantSelector = document.createElement('div');
variantSelector.style.cssText = 'width: 100%; padding: 10px; margin-bottom: 20px; background: rgba(255,255,255,0.1); border-radius: 5px;';
variantSelector.innerHTML = `
<div style="color: white; margin-bottom: 10px; font-weight: bold;">卡牌变体选择:</div>
<label style="color: white; margin-right: 15px; cursor: pointer;">
<input type="checkbox" id="enable-inspiration" style="margin-right: 5px;"/> 启用灵光一闪
</label>
<label style="color: white; cursor: pointer;">
<input type="checkbox" id="enable-god-inspiration" style="margin-right: 5px;"/> 启用神光一闪
</label>
`;
listContainer.appendChild(variantSelector);
const cardsContainer = document.createElement('div');
cardsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;';
listContainer.appendChild(cardsContainer);
cards.forEach(card => {
const cardWrapper = document.createElement('div');
cardWrapper.style.cursor = 'pointer';
cardWrapper.style.position = 'relative';
cardWrapper.setAttribute('data-card-name', card.name);
// 渲染基础卡牌
renderCard(characterName, card.name, null, null, null, function(html) {
cardWrapper.innerHTML = html;
// 添加点击事件处理
cardWrapper.addEventListener('click', function(e) {
e.stopPropagation();
// 检查是否启用变体
const inspirationEnabled = document.getElementById('enable-inspiration')?.checked;
const godInspirationEnabled = document.getElementById('enable-god-inspiration')?.checked;
if (inspirationEnabled || godInspirationEnabled) {
// 显示变体选择界面
showVariantSelection(characterName, card.name, inspirationEnabled, godInspirationEnabled);
} else {
// 添加基础卡牌
addCardToDeck(card.name, characterName, html);
}
});
cardsContainer.appendChild(cardWrapper);
});
});
}
// 新增函数:渲染单个卡牌
function renderCard(characterName, cardName, variantType, variantParam, variantIndex, callback) {
let invokeText = `{{#invoke:卡牌|main|${characterName}|${cardName}`;
if (variantType) {
invokeText += `|${variantType}`;
if (variantParam) {
invokeText += `|${variantParam}`;
if (variantIndex) {
invokeText += `|${variantIndex}`;
}
}
}
invokeText += '}}';
new mw.Api().post({
action: 'parse',
text: invokeText,
contentmodel: 'wikitext',
prop: 'text',
disablelimitreport: 1,
disableeditsection: 1
}).done(function(data) {
if (data.parse && data.parse.text) {
callback(data.parse.text['*']);
}
}).fail(function() {
callback('<div style="color:red;padding:10px;">加载失败</div>');
});
}
// 新增函数:显示变体选择
function showVariantSelection(characterName, cardName, inspirationEnabled, godInspirationEnabled) {
const modal = document.createElement('div');
modal.style.cssText = 'position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.9); z-index: 20000; display: flex; align-items: center; justify-content: center;';
const content = document.createElement('div');
content.style.cssText = 'background: #2a2a2a; padding: 20px; border-radius: 8px; max-width: 90vw; max-height: 90vh; overflow: auto; position: relative;';
content.innerHTML = '<h3 style="color: white; margin-bottom: 20px;">选择卡牌变体</h3>';
const variantsContainer = document.createElement('div');
variantsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; justify-content: center;';
// 添加基础卡牌
const baseCardWrapper = document.createElement('div');
baseCardWrapper.style.cssText = 'border: 2px solid transparent; cursor: pointer; transition: all 0.3s;';
baseCardWrapper.onmouseover = function() { this.style.borderColor = '#4da6cc'; };
baseCardWrapper.onmouseout = function() { this.style.borderColor = 'transparent'; };
renderCard(characterName, cardName, null, null, null, function(html) {
const container = document.createElement('div');
container.innerHTML = '<div style="text-align: center; color: white; margin-bottom: 5px; font-weight: bold;">基础卡牌</div>' + html;
baseCardWrapper.appendChild(container);
baseCardWrapper.addEventListener('click', function() {
addCardToDeck(cardName, characterName, html);
document.body.removeChild(modal);
});
variantsContainer.appendChild(baseCardWrapper);
});
// 添加灵光一闪变体(如果启用)
if (inspirationEnabled) {
// 尝试加载最多3个灵光一闪变体
for (let i = 1; i <= 3; i++) {
(function(index) {
const inspirationWrapper = document.createElement('div');
inspirationWrapper.style.cssText = 'border: 2px solid transparent; cursor: pointer; transition: all 0.3s;';
inspirationWrapper.onmouseover = function() { this.style.borderColor = '#6fd8fe'; };
inspirationWrapper.onmouseout = function() { this.style.borderColor = 'transparent'; };
renderCard(characterName, cardName, '灵光一闪', index.toString(), null, function(html) {
if (!html.includes('错误')) {
const container = document.createElement('div');
container.innerHTML = '<div style="text-align: center; color: #6fd8fe; margin-bottom: 5px; font-weight: bold;">灵光一闪 ' + index + '</div>' + html;
inspirationWrapper.appendChild(container);
inspirationWrapper.addEventListener('click', function() {
addCardToDeck(cardName + ' (灵光' + index + ')', characterName, html);
document.body.removeChild(modal);
});
variantsContainer.appendChild(inspirationWrapper);
}
});
})(i);
}
}
// 添加神光一闪变体(如果启用)
if (godInspirationEnabled) {
// 神光一闪需要指定战斗员
const godCharacters = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
godCharacters.forEach(function(godChar) {
// 尝试加载每个战斗员的神光一闪
for (let i = 1; i <= 2; i++) {
(function(char, index) {
const godWrapper = document.createElement('div');
godWrapper.style.cssText = 'border: 2px solid transparent; cursor: pointer; transition: all 0.3s;';
godWrapper.onmouseover = function() { this.style.borderColor = '#ffd36a'; };
godWrapper.onmouseout = function() { this.style.borderColor = 'transparent'; };
renderCard(characterName, cardName, '神光一闪', char, index.toString(), function(html) {
if (!html.includes('错误')) {
const container = document.createElement('div');
container.innerHTML = '<div style="text-align: center; color: #ffd36a; margin-bottom: 5px; font-weight: bold;">神光一闪 (' + char + ' ' + index + ')</div>' + html;
godWrapper.appendChild(container);
godWrapper.addEventListener('click', function() {
addCardToDeck(cardName + ' (神光-' + char + index + ')', characterName, html);
document.body.removeChild(modal);
});
variantsContainer.appendChild(godWrapper);
}
});
})(godChar, i);
}
});
}
// 添加关闭按钮
const closeBtn = document.createElement('div');
closeBtn.innerHTML = '×';
closeBtn.style.cssText = 'position: absolute; top: 10px; right: 10px; color: white; font-size: 32px; cursor: pointer; transition: all 0.3s;';
closeBtn.onmouseover = function() { this.style.color = '#ff4444'; };
closeBtn.onmouseout = function() { this.style.color = 'white'; };
closeBtn.addEventListener('click', function() {
document.body.removeChild(modal);
});
content.appendChild(closeBtn);
content.appendChild(variantsContainer);
modal.appendChild(content);
document.body.appendChild(modal);
// 点击背景关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
document.body.removeChild(modal);
}
});
}
// 添加卡牌到卡组
function addCardToDeck(cardName, characterName, cardHtml) {
state.deckCards.push({ cardName, characterName, cardHtml });
hideModal('card-modal');
updateDeckDisplay();
}
// 更新卡组显示
function updateDeckDisplay() {
const deckArea = document.getElementById('deck-area');
const deckCards = document.getElementById('deck-cards');
const plusSign = deckArea.querySelector('span');
deckCards.innerHTML = '';
if (state.deckCards.length === 0) {
deckCards.style.display = 'none';
if (plusSign) plusSign.style.display = 'block';
return;
}
deckCards.style.display = 'flex';
if (plusSign) plusSign.style.display = 'none';
state.deckCards.forEach((card, index) => {
const cardDiv = document.createElement('div');
cardDiv.style.position = 'relative';
cardDiv.innerHTML = card.cardHtml;
// 添加删除按钮
const removeBtn = document.createElement('div');
removeBtn.className = 'tb-remove-btn';
removeBtn.innerHTML = '×';
removeBtn.style.cssText = 'position: absolute; top: 5px; right: 5px; background: #ff4444; color: white; border-radius: 50%; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 100; font-size: 20px; font-weight: bold; box-shadow: 0 2px 4px rgba(0,0,0,0.3);';
removeBtn.addEventListener('click', function(e) {
e.stopPropagation();
state.deckCards.splice(index, 1);
updateDeckDisplay();
});
cardDiv.appendChild(removeBtn);
deckCards.appendChild(cardDiv);
});
}
}
// MediaWiki hook
if (typeof mw !== 'undefined' && mw.hook) {
mw.hook('wikipage.content').add(initTeamBuilder);
}
})();