// ==UserScript== // @name [Pokechill] 队伍编辑 // @namespace https://play-pokechill.github.io/ // @version 1.0 // @description 队伍编辑,技能/特性完美中文显示,支持中文搜索 // @author 人民当家做主 // @license MIT // @icon https://play-pokechill.github.io/img/icons/icon.png // @match https://play-pokechill.github.io/* // @match https://g1tyx.github.io/play-pokechill/* // @grant none // ==/UserScript== (function() { 'use strict'; // ---------- 获取中文翻译 ---------- function getChineseName(id) { if (!id) return ''; // 1. 用 format 得到英文显示名(如 "Blaze") const engName = typeof format === 'function' ? format(id) : id; // 2. 用英文名查汉化字典 if (window.EN_CN_DICT && window.EN_CN_DICT[engName]) { return window.EN_CN_DICT[engName]; } // 3. 保底返回原ID return engName; } // ---------- 等待游戏核心加载 ---------- function waitForGame() { if (typeof saved === 'undefined' || typeof pkmn === 'undefined' || typeof move === 'undefined' || typeof ability === 'undefined') { setTimeout(waitForGame, 300); return; } // 额外等待汉化字典加载 if (!window.EN_CN_DICT) { setTimeout(waitForGame, 300); return; } initTeamEditor(); } // ---------- 初始化 ---------- function initTeamEditor() { addMenuButton(); createEditorPanel(); bindEvents(); } // ---------- 添加菜单按钮 ---------- function addMenuButton() { const menuItems = document.getElementById('menu-items'); if (!menuItems) return; if (document.getElementById('team-editor-menu-btn')) return; const btn = document.createElement('div'); btn.id = 'team-editor-menu-btn'; btn.className = 'menu-item'; btn.innerHTML = ` 队伍编辑 `; btn.addEventListener('click', (e) => { e.stopPropagation(); openTeamEditor(); }); menuItems.appendChild(btn); } // ---------- 创建编辑器面板 ---------- function createEditorPanel() { if (document.getElementById('team-editor-panel')) return; const panel = document.createElement('div'); panel.id = 'team-editor-panel'; panel.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); z-index: 10000; display: none; justify-content: center; align-items: center; backdrop-filter: blur(5px); `; panel.innerHTML = `

✎ 队伍编辑 (当前队伍: 队伍1)

`; document.body.appendChild(panel); } // ---------- 打开编辑器 ---------- function openTeamEditor() { const panel = document.getElementById('team-editor-panel'); if (!panel) return; if (typeof closeTooltip === 'function') closeTooltip(); populateTeamSlots(); panel.style.display = 'flex'; } // ---------- 关闭编辑器 ---------- function closeTeamEditor() { const panel = document.getElementById('team-editor-panel'); if (panel) panel.style.display = 'none'; } // ---------- 填充6个槽位 ---------- function populateTeamSlots() { const container = document.getElementById('team-editor-slots'); if (!container) return; const teamName = saved.currentPreviewTeam || 'preview1'; const team = saved.previewTeams[teamName]; if (!team) { container.innerHTML = '
无法读取队伍数据
'; return; } document.getElementById('editor-current-team-name').innerText = teamName.replace('preview', '队伍'); let html = ''; for (let i = 1; i <= 6; i++) { const slot = team[`slot${i}`]; const pokeId = slot?.pkmn; const pokeData = pokeId ? pkmn[pokeId] : null; html += `
${pokeData ? `` : '❌'}
${pokeData ? getChineseName(pokeId) : '空'} ${pokeId || ''}
${pokeData ? `
等级
` : ''}
${pokeData ? `
特性
${[1,2,3,4].map(m => { const moveId = pokeData.moves?.[`slot${m}`]; return `
${moveId ? getChineseName(moveId) : '--'}
`; }).join('')}
` : '
空位,无法编辑
'}
`; } container.innerHTML = html; // 绑定事件 container.querySelectorAll('.editor-ability-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const slot = btn.dataset.slot; showAbilitySearch(slot); }); }); container.querySelectorAll('.editor-move-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const slot = btn.dataset.slot; const moveSlot = btn.dataset.moveSlot; showMoveSearch(slot, moveSlot); }); }); container.querySelectorAll('.editor-level').forEach(input => { input.addEventListener('change', function() { const slot = this.dataset.slot; const val = parseInt(this.value, 10); updatePokemonLevel(slot, val); }); }); container.querySelectorAll('.editor-ability-input').forEach(inp => { inp.addEventListener('click', (e) => { e.stopPropagation(); const slot = inp.dataset.slot; showAbilitySearch(slot); }); }); } // ---------- 更新等级 ---------- function updatePokemonLevel(slot, newLevel) { const teamName = saved.currentPreviewTeam || 'preview1'; const team = saved.previewTeams[teamName]; const slotKey = `slot${slot}`; const pokeId = team[slotKey]?.pkmn; if (!pokeId) return; const poke = pkmn[pokeId]; if (!poke) return; poke.level = Math.min(100, Math.max(1, newLevel)); } // ---------- 显示特性搜索 ---------- function showAbilitySearch(slot) { const teamName = saved.currentPreviewTeam || 'preview1'; const team = saved.previewTeams[teamName]; const slotKey = `slot${slot}`; const pokeId = team[slotKey]?.pkmn; if (!pokeId) return; const poke = pkmn[pokeId]; if (!poke) return; const types = poke.type; const abilityList = []; for (let abId in ability) { const ab = ability[abId]; if (!ab.type) continue; if (ab.type.includes('all') || types.some(t => ab.type.includes(t))) { abilityList.push({ id: abId, name: getChineseName(abId), data: ab }); } } const fuse = new Fuse(abilityList, { keys: ['name', 'id'], threshold: 0.3, includeScore: true }); showSearchPopup('选择特性', abilityList, fuse, (selectedId) => { poke.ability = selectedId; if (poke.hiddenAbility && poke.hiddenAbility.id === selectedId) { poke.hiddenAbilityUnlocked = true; } const input = document.querySelector(`.editor-ability-input[data-slot="${slot}"]`); if (input) input.value = getChineseName(selectedId); }); } // ---------- 显示技能搜索 ---------- function showMoveSearch(slot, moveSlot) { const teamName = saved.currentPreviewTeam || 'preview1'; const team = saved.previewTeams[teamName]; const slotKey = `slot${slot}`; const pokeId = team[slotKey]?.pkmn; if (!pokeId) return; const poke = pkmn[pokeId]; if (!poke) return; const types = poke.type; const moveList = []; for (let moveId in move) { const mv = move[moveId]; if (!mv.moveset) continue; if (mv.moveset.includes('all') || types.some(t => mv.moveset.includes(t))) { moveList.push({ id: moveId, name: getChineseName(moveId), data: mv }); } } const fuse = new Fuse(moveList, { keys: ['name', 'id'], threshold: 0.3, includeScore: true }); showSearchPopup(`选择技能 (槽位${moveSlot})`, moveList, fuse, (selectedId) => { if (!poke.movepool.includes(selectedId)) { poke.movepool.push(selectedId); } poke.moves[`slot${moveSlot}`] = selectedId; // 更新显示 const container = document.querySelector(`.team-editor-slot[data-slot-index="${slot}"] .editor-moves`); if (container) { const moveDivs = container.querySelectorAll('.pkmn-movebox'); if (moveDivs[moveSlot-1]) { moveDivs[moveSlot-1].querySelector('span').textContent = getChineseName(selectedId); const img = moveDivs[moveSlot-1].querySelector('img'); img.src = `img/icons/${move[selectedId].type}.svg`; img.style.backgroundColor = `var(--type-${move[selectedId].type})`; } } }); } // ---------- 通用搜索弹窗 ---------- function showSearchPopup(title, items, fuseInstance, onSelect) { const existing = document.getElementById('team-editor-search-popup'); if (existing) existing.remove(); const popup = document.createElement('div'); popup.id = 'team-editor-search-popup'; popup.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; max-width: 90%; max-height: 80%; background: var(--dark1); border: 2px solid var(--light1); border-radius: 1rem; padding: 1rem; z-index: 20000; display: flex; flex-direction: column; gap: 0.8rem; color: white; box-shadow: 0 0 30px black; `; popup.innerHTML = `

${title}

`; document.body.appendChild(popup); const input = document.getElementById('search-popup-input'); const resultsDiv = document.getElementById('search-popup-results'); const closeBtn = document.getElementById('search-popup-close'); function renderResults(list) { resultsDiv.innerHTML = ''; if (!list.length) { resultsDiv.innerHTML = '
无结果
'; return; } list.slice(0, 50).forEach(item => { const div = document.createElement('div'); div.style.cssText = ` padding: 0.5rem; background: var(--dark2); border-radius: 0.3rem; cursor: pointer; display: flex; justify-content: space-between; `; div.innerHTML = `${item.name}${item.id}`; div.addEventListener('click', () => { onSelect(item.id); popup.remove(); }); resultsDiv.appendChild(div); }); } renderResults(items); input.addEventListener('input', () => { const query = input.value.trim(); if (!query) { renderResults(items); return; } const results = fuseInstance.search(query).map(r => r.item); renderResults(results); }); closeBtn.addEventListener('click', () => popup.remove()); const onKeyDown = (e) => { if (e.key === 'Escape') popup.remove(); }; window.addEventListener('keydown', onKeyDown, { once: true }); } // ---------- 绑定事件 ---------- function bindEvents() { document.addEventListener('click', (e) => { const closeBtn = e.target.closest('#team-editor-close'); if (closeBtn) closeTeamEditor(); const refreshBtn = e.target.closest('#team-editor-refresh'); if (refreshBtn) populateTeamSlots(); const saveBtn = e.target.closest('#team-editor-save'); if (saveBtn) { if (typeof updatePreviewTeam === 'function') updatePreviewTeam(); closeTeamEditor(); } }); } waitForGame(); })();