// ==UserScript== // @name [Pokechill] 调试模式(队伍编辑 + 物品管理器)优化版 // @namespace https://play-pokechill.github.io/ // @version 1.3 // @description 队伍编辑(可修改等级、特性、技能、性格) + 物品管理器(查看/修改所有物品),统一UI,优化性能 // @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/* // @require https://cdn.jsdelivr.net/npm/fuse.js@7.1.0 // @grant none // ==/UserScript== (function() { 'use strict'; // ---------- 工具函数:获取中文名称 ---------- function getChineseName(id) { if (!id) return ''; const engName = typeof format === 'function' ? format(id) : id; if (window.EN_CN_DICT && window.EN_CN_DICT[engName]) { return window.EN_CN_DICT[engName]; } return engName; } // 性格中文名映射 const natureChinese = { adamant: '固执', modest: '内敛', jolly: '爽朗', relaxed: '悠闲', quiet: '冷静', bold: '大胆' }; // ---------- 等待游戏核心加载 ---------- function waitForGame() { if (typeof saved === 'undefined' || typeof pkmn === 'undefined' || typeof move === 'undefined' || typeof ability === 'undefined' || typeof item === 'undefined' || typeof nature === 'undefined') { setTimeout(waitForGame, 300); return; } if (!window.EN_CN_DICT) { setTimeout(waitForGame, 300); return; } initTeamEditor(); initItemManager(); } // ========== 队伍编辑模块(含性格) ========== function initTeamEditor() { addTeamEditorButton(); createTeamEditorPanel(); bindTeamEditorEvents(); } function addTeamEditorButton() { 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 createTeamEditorPanel() { 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'; } 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; const currentNature = pokeData?.nature || ''; 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, .editor-ability-input').forEach(el => { el.addEventListener('click', (e) => { e.stopPropagation(); const slot = el.dataset.slot; showAbilitySearch(slot); }); }); // 绑定性格 container.querySelectorAll('.editor-nature-btn, .editor-nature-input').forEach(el => { el.addEventListener('click', (e) => { e.stopPropagation(); const slot = el.dataset.slot; showNatureSearch(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); }); }); } 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 getMemoryAbilities() { const memorySet = new Set(); for (let itemId in item) { const it = item[itemId]; if (it.type === 'memory' && it.ability) { memorySet.add(it.ability); } } return memorySet; } 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 memoryAbilities = getMemoryAbilities(); const abilityList = []; for (let abId in ability) { const ab = ability[abId]; if (ab.type || memoryAbilities.has(abId)) { 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 showNatureSearch(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 natureList = []; for (let natId in nature) { const displayName = natureChinese[natId] || natId.charAt(0).toUpperCase() + natId.slice(1); natureList.push({ id: natId, name: displayName, }); } const fuse = new Fuse(natureList, { keys: ['name', 'id'], threshold: 0.3, includeScore: true }); showSearchPopup('选择性格', natureList, fuse, (selectedId) => { poke.nature = selectedId; const input = document.querySelector(`.editor-nature-input[data-slot="${slot}"]`); if (input) { input.value = natureChinese[selectedId] || 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 moveList = []; for (let moveId in move) { const mv = move[moveId]; if (!mv.moveset) continue; 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 bindTeamEditorEvents() { 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(); } }); } // ========== 物品管理器模块(优化版) ========== function initItemManager() { addItemManagerButton(); createItemManagerPanel(); bindItemManagerEvents(); } function addItemManagerButton() { const menuItems = document.getElementById('menu-items'); if (!menuItems) return; if (document.getElementById('item-manager-menu-btn')) return; const btn = document.createElement('div'); btn.id = 'item-manager-menu-btn'; btn.className = 'menu-item'; btn.innerHTML = ` 物品管理器 `; btn.addEventListener('click', (e) => { e.stopPropagation(); openItemManager(); }); menuItems.appendChild(btn); } function createItemManagerPanel() { if (document.getElementById('item-manager-panel')) return; const panel = document.createElement('div'); panel.id = 'item-manager-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 = `

📦 物品管理器

`; document.body.appendChild(panel); } function openItemManager() { const panel = document.getElementById('item-manager-panel'); if (!panel) return; if (typeof closeTooltip === 'function') closeTooltip(); populateItemList(); panel.style.display = 'flex'; } function closeItemManager() { const panel = document.getElementById('item-manager-panel'); if (panel) panel.style.display = 'none'; } function getItemCategory(itemId, itemData) { if (itemData.type === 'held') { if (itemData.sort === 'gem') return 'gem'; if (itemData.sort === 'berry') return 'berry'; return 'held'; } if (itemData.type === 'tm') return 'tm'; if (itemData.type === 'memory') return 'memory'; if (itemData.type === 'decor') return 'decor'; if (itemData.evo) return 'evo'; if (itemData.type === 'key') return 'key'; return 'key'; } // 防抖函数 function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // 物品列表生成(优化:使用事件委托,防抖搜索) function populateItemList() { const container = document.getElementById('item-list'); if (!container) return; const searchTerm = document.getElementById('item-search')?.value.toLowerCase() || ''; const category = document.getElementById('item-category-filter')?.value || 'all'; const allItems = []; for (let id in item) { const it = item[id]; if (it.hidden) continue; const cat = getItemCategory(id, it); if (category !== 'all' && cat !== category) continue; const chineseName = getChineseName(id); if (searchTerm) { const lowerId = id.toLowerCase(); const lowerName = chineseName.toLowerCase(); if (!lowerId.includes(searchTerm) && !lowerName.includes(searchTerm)) { continue; } } allItems.push({ id: id, name: chineseName, got: it.got || 0, category: cat, data: it }); } allItems.sort((a, b) => a.id.localeCompare(b.id)); // 使用DocumentFragment批量构建 const fragment = document.createDocumentFragment(); container.innerHTML = ''; // 清空 if (allItems.length === 0) { container.innerHTML = '
没有找到物品
'; return; } allItems.forEach(itemInfo => { const row = document.createElement('div'); row.className = 'item-row'; row.dataset.id = itemInfo.id; row.style.cssText = ` display: flex; align-items: center; gap: 0.5rem; padding: 0.3rem 0.5rem; background: var(--dark2); border-radius: 0.5rem; border: 1px solid var(--light1); `; let iconHtml = ''; if (itemInfo.data.type === 'tm') { const moveId = itemInfo.data.move; const moveType = move[moveId]?.type || 'normal'; iconHtml = ``; } else if (itemInfo.data.type === 'memory') { const imgColor = itemInfo.data.image || 'dark'; iconHtml = ``; } else if (itemInfo.data.type === 'decor') { iconHtml = ``; } else { iconHtml = ``; } const nameHtml = `
${itemInfo.name}
${itemInfo.id}
`; const countHtml = `
${itemInfo.got}
`; const controlsHtml = `
`; row.innerHTML = iconHtml + nameHtml + countHtml + controlsHtml; fragment.appendChild(row); }); container.appendChild(fragment); } // 使用事件委托处理物品数量的增减 function handleItemListClick(e) { const target = e.target; const row = target.closest('.item-row'); if (!row) return; const id = row.dataset.id; if (!id || !item[id]) return; if (target.classList.contains('item-dec')) { const newVal = Math.max(0, (item[id].got || 0) - 1); item[id].got = newVal; const input = row.querySelector('.item-count-input'); const display = row.querySelector('.item-count-display'); if (input) input.value = newVal; if (display) display.textContent = newVal; } else if (target.classList.contains('item-inc')) { const newVal = (item[id].got || 0) + 1; item[id].got = newVal; const input = row.querySelector('.item-count-input'); const display = row.querySelector('.item-count-display'); if (input) input.value = newVal; if (display) display.textContent = newVal; } else if (target.classList.contains('item-count-input')) { // input change 事件单独处理 } } function handleItemListInput(e) { const target = e.target; if (!target.classList.contains('item-count-input')) return; const row = target.closest('.item-row'); if (!row) return; const id = row.dataset.id; if (!id || !item[id]) return; let val = parseInt(target.value, 10); if (isNaN(val) || val < 0) val = 0; item[id].got = val; const display = row.querySelector('.item-count-display'); if (display) display.textContent = val; } function bindItemManagerEvents() { const panel = document.getElementById('item-manager-panel'); if (!panel) return; // 关闭按钮 panel.querySelector('#item-manager-close').addEventListener('click', closeItemManager); // 刷新按钮 panel.querySelector('#item-refresh').addEventListener('click', populateItemList); // 搜索输入(防抖) const searchInput = panel.querySelector('#item-search'); const categorySelect = panel.querySelector('#item-category-filter'); const debouncedPopulate = debounce(populateItemList, 300); searchInput.addEventListener('input', debouncedPopulate); categorySelect.addEventListener('change', populateItemList); // 分类切换无需防抖 // 事件委托:点击增减按钮 const itemList = panel.querySelector('#item-list'); itemList.addEventListener('click', handleItemListClick); itemList.addEventListener('input', handleItemListInput); // 处理输入框直接修改 } // ========== 通用搜索弹窗 ========== 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 }); } // 启动 waitForGame(); })();