// ==UserScript== // @name [Pokechill] 自定义木桩测试-龟龟 // @namespace https://play-pokechill.github.io/ // @version 2.1 // @description 木桩技能池基于所选属性动态过滤,包含所有可学技能。重置按钮同时清空已选技能,修复卡片图片显示。新增种族值星级编辑(0-6星)。移动端适配优化。 // @author 人民当家做主 // @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'; const dummyAreaId = 'dummyCustom'; const dummyPkmnId = 'dummy_custom'; // 自定义木桩图片(如果图片失效,请替换为有效链接) const DUMMY_SPRITE_URL = 'https://picui.ogmua.cn/s1/2026/03/10/69afd44fed40b.webp'; function waitForGame() { if (typeof areas === 'undefined' || typeof pkmn === 'undefined' || typeof move === 'undefined' || typeof ability === 'undefined') { setTimeout(waitForGame, 200); return; } console.log('[DummyCustom] 游戏核心已加载,开始注入...'); initDummyPokemon(); injectDummyTarget(); createConfigPanel(); setupImageErrorHandler(); addMobileStyles(); // 添加移动端适配样式 } waitForGame(); // 全局图片错误处理:将木桩相关的图片请求替换为自定义图片 function setupImageErrorHandler() { document.addEventListener('error', function(e) { const img = e.target; if (img.tagName !== 'IMG') return; if (img.src.includes(`/sprite/${dummyPkmnId}.png`) || img.src.includes(`/sprite/${dummyPkmnId}.gif`) || img.src.includes(`/trainers/.png`)) { if (img.src.includes('/trainers/.png')) { img.style.display = 'none'; e.preventDefault(); return; } img.src = DUMMY_SPRITE_URL; img.onerror = null; console.log('[DummyCustom] 替换图片为自定义木桩图片'); } }, true); } function initDummyPokemon() { if (!pkmn[dummyPkmnId]) { if (!ability.none) { ability.none = { id: 'none', rename: '无', rarity: 1, type: ['all'], info: function() { return '没有任何效果。'; } }; } pkmn[dummyPkmnId] = { id: dummyPkmnId, rename: '龟龟', type: ['normal'], bst: { hp: 4, atk: 4, def: 4, satk: 4, sdef: 4, spe: 4 }, level: 100, exp: 0, caught: 0, shiny: false, ability: 'none', hiddenAbility: undefined, hiddenAbilityUnlocked: false, movepool: [], moves: { slot1: undefined, slot2: undefined, slot3: undefined, slot4: undefined }, ivs: { hp: 0, atk: 0, def: 0, satk: 0, sdef: 0, spe: 0 }, pokerus: false, ribbons: [], newMoves: [], newPokemon: undefined, newEvolution: undefined, tag: undefined, lockHp: true }; } } // 根据当前属性刷新可学技能池(基于 moveset 过滤) function refreshDummyMovepool() { const dummy = pkmn[dummyPkmnId]; if (!dummy) return; const types = dummy.type; const newMovepool = []; for (const moveId in move) { const m = move[moveId]; if (m.moveset) { if (m.moveset.includes('all') || types.some(t => m.moveset.includes(t))) { newMovepool.push(moveId); } } } dummy.movepool = newMovepool; console.log(`[DummyCustom] 刷新技能池,共 ${newMovepool.length} 个技能(基于属性 ${types.join('/')})`); } function ensureDummyArea() { if (!areas[dummyAreaId]) { areas[dummyAreaId] = { name: `木桩测试 (可配置)`, background: 'gym', trainer: true, type: 'vs', level: 100, team: { slot1: pkmn[dummyPkmnId], slot1Moves: [undefined, undefined, undefined, undefined] }, dummy: true, defeated: false, unlockRequirement: () => true, reward: [] }; } } // 生成0-6星选项的HTML function generateStarOptions(selected) { let options = ''; for (let i = 0; i <= 6; i++) { options += ``; } return options; } // 添加移动端适配样式 function addMobileStyles() { const style = document.createElement('style'); style.textContent = ` @media (max-width: 768px) { #dummy-config-panel { width: 95vw !important; min-width: unset !important; padding: 1rem !important; font-size: 14px !important; } #dummy-config-panel select, #dummy-config-panel input[type="number"] { font-size: 16px !important; /* 防止iOS缩放 */ padding: 0.5rem !important; } #dummy-config-panel button { padding: 0.8rem 1rem !important; font-size: 16px !important; } #dummy-config-panel .vs-card { max-width: 100% !important; } /* 种族值两列布局在移动端保持两列,但可适当调整间距 */ #dummy-config-panel [style*="grid-template-columns: repeat(2, 1fr)"] { gap: 0.8rem !important; } /* 锁血和技能按钮行改为垂直排列(如果空间不足) */ #dummy-config-panel > div > div[style*="justify-content:space-between"] { flex-direction: column; align-items: stretch !important; gap: 0.8rem; } #dummy-config-panel > div > div[style*="justify-content:space-between"] button { width: 100%; } } `; document.head.appendChild(style); } function createConfigPanel() { if (document.getElementById('dummy-config-panel')) return; const panel = document.createElement('div'); panel.id = 'dummy-config-panel'; panel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: var(--light2, #ECDEB7); border: 2px solid var(--light1, #94886B); border-radius: 0.5rem; padding: 1.5rem; z-index: 10000; display: none; flex-direction: column; gap: 1rem; min-width: 350px; max-width: 90vw; max-height: 90vh; overflow-y: auto; color: var(--dark1, #36342F); font-family: 'Courier New', monospace; box-shadow: 0 0 20px rgba(0,0,0,0.5); `; // 预读取当前木桩的种族值用于初始化下拉框(实际在打开面板时设置) const dummy = pkmn[dummyPkmnId]; const bst = dummy ? dummy.bst : { hp:6, atk:4, def:2, satk:4, sdef:2, spe:4 }; panel.innerHTML = `

配置木桩

`; document.body.appendChild(panel); // 辅助函数:从界面读取种族值并更新木桩对象 function updateBstFromUI() { const dummy = pkmn[dummyPkmnId]; dummy.bst.hp = parseInt(document.getElementById('dummy-bst-hp').value, 10); dummy.bst.atk = parseInt(document.getElementById('dummy-bst-atk').value, 10); dummy.bst.def = parseInt(document.getElementById('dummy-bst-def').value, 10); dummy.bst.satk = parseInt(document.getElementById('dummy-bst-satk').value, 10); dummy.bst.sdef = parseInt(document.getElementById('dummy-bst-sdef').value, 10); dummy.bst.spe = parseInt(document.getElementById('dummy-bst-spe').value, 10); } // 辅助函数:将木桩对象的种族值设置到UI function setBstToUI() { const dummy = pkmn[dummyPkmnId]; document.getElementById('dummy-bst-hp').value = dummy.bst.hp; document.getElementById('dummy-bst-atk').value = dummy.bst.atk; document.getElementById('dummy-bst-def').value = dummy.bst.def; document.getElementById('dummy-bst-satk').value = dummy.bst.satk; document.getElementById('dummy-bst-sdef').value = dummy.bst.sdef; document.getElementById('dummy-bst-spe').value = dummy.bst.spe; } const updateType = () => { const type1 = document.getElementById('dummy-type1').value; const type2 = document.getElementById('dummy-type2').value; pkmn[dummyPkmnId].type = type2 ? [type1, type2] : [type1]; }; document.getElementById('dummy-type1').addEventListener('change', updateType); document.getElementById('dummy-type2').addEventListener('change', updateType); // 配置技能按钮:保存当前所有配置(包括种族值),刷新技能池,打开编辑器 document.getElementById('dummy-config-skills').addEventListener('click', () => { updateType(); const level = parseInt(document.getElementById('dummy-level').value, 10); pkmn[dummyPkmnId].level = Math.min(100, Math.max(1, level)); pkmn[dummyPkmnId].lockHp = document.getElementById('dummy-lockhp').checked; updateBstFromUI(); // 保存种族值 refreshDummyMovepool(); panel.style.display = 'none'; if (typeof tooltipData === 'function') { tooltipData('pkmnEditor', dummyPkmnId); } else { alert('无法打开编辑器'); } }); // 重置按钮:恢复默认值(属性、等级、锁血、种族值、清空技能) document.getElementById('dummy-config-reset').addEventListener('click', () => { document.getElementById('dummy-type1').value = 'normal'; document.getElementById('dummy-type2').value = ''; document.getElementById('dummy-level').value = 100; document.getElementById('dummy-lockhp').checked = true; const dummy = pkmn[dummyPkmnId]; dummy.type = ['normal']; dummy.level = 100; dummy.lockHp = true; // 重置种族值为初始值 dummy.bst = { hp:6, atk:4, def:2, satk:4, sdef:2, spe:4 }; // 清空已选技能 dummy.moves = { slot1: undefined, slot2: undefined, slot3: undefined, slot4: undefined }; // 更新UI中的种族值下拉框 setBstToUI(); refreshDummyMovepool(); console.log('[DummyCustom] 木桩已重置为默认值,技能已清空'); }); // 确定按钮:保存所有配置并进入对战预览 document.getElementById('dummy-config-ok').addEventListener('click', () => { const type1 = document.getElementById('dummy-type1').value; const type2 = document.getElementById('dummy-type2').value; const level = parseInt(document.getElementById('dummy-level').value, 10); const lockHp = document.getElementById('dummy-lockhp').checked; const dummy = pkmn[dummyPkmnId]; dummy.type = type2 ? [type1, type2] : [type1]; dummy.level = Math.min(100, Math.max(1, level)); dummy.lockHp = lockHp; dummy.playerHp = undefined; updateBstFromUI(); // 保存种族值 panel.style.display = 'none'; saved.currentAreaBuffer = dummyAreaId; document.getElementById('preview-team-exit').style.display = 'flex'; document.getElementById('team-menu').style.zIndex = '50'; document.getElementById('team-menu').style.display = 'flex'; document.getElementById('menu-button-parent').style.display = 'none'; updatePreviewTeam(); afkSeconds = 0; document.getElementById('explore-menu').style.display = 'none'; }); document.getElementById('dummy-config-cancel').addEventListener('click', () => { panel.style.display = 'none'; }); } function injectDummyTarget() { ensureDummyArea(); const originalUpdateVS = updateVS; updateVS = function() { originalUpdateVS(); const vsListing = document.getElementById('vs-listing'); if (!vsListing) return; const existingCards = vsListing.querySelectorAll(`[data-trainer="${dummyAreaId}"]`); existingCards.forEach(card => card.remove()); const dummyCard = document.createElement('div'); dummyCard.className = 'vs-card'; dummyCard.dataset.trainer = dummyAreaId; dummyCard.innerHTML = `
自定义木桩 测试木桩
`; dummyCard.addEventListener('click', () => { const panel = document.getElementById('dummy-config-panel'); if (panel) { const dummy = pkmn[dummyPkmnId]; document.getElementById('dummy-type1').value = dummy.type[0] || 'normal'; document.getElementById('dummy-type2').value = dummy.type[1] || ''; document.getElementById('dummy-level').value = dummy.level; document.getElementById('dummy-lockhp').checked = dummy.lockHp; // 同步种族值到UI document.getElementById('dummy-bst-hp').value = dummy.bst.hp; document.getElementById('dummy-bst-atk').value = dummy.bst.atk; document.getElementById('dummy-bst-def').value = dummy.bst.def; document.getElementById('dummy-bst-satk').value = dummy.bst.satk; document.getElementById('dummy-bst-sdef').value = dummy.bst.sdef; document.getElementById('dummy-bst-spe').value = dummy.bst.spe; panel.style.display = 'flex'; } }); vsListing.appendChild(dummyCard); }; } const originalSetWildPkmn = setWildPkmn; setWildPkmn = function() { if (saved.currentArea === dummyAreaId) { const dummy = pkmn[dummyPkmnId]; if (!dummy) return; barProgressWild = 0; exploreCombatWildTurn = 1; ['team-indicator', 'spiraling-indicator', 'factory-indicator', 'training-indicator', 'raid-timer-indicator'] .forEach(id => document.getElementById(id) && (document.getElementById(id).style.display = 'none')); saved.currentPkmn = dummyPkmnId; wildLevel = dummy.level; const hpStars = dummy.bst.hp; wildPkmnHp = (100 + (hpStars * 30) * (1 + dummy.level * 0.2)) * 4; wildPkmnHpMax = wildPkmnHp; document.getElementById('explore-wild-name').innerHTML = (dummy.rename || '木桩') + ` lvl ${dummy.level}`; const sprite = document.getElementById('explore-wild-sprite'); sprite.src = DUMMY_SPRITE_URL; sprite.dataset.dummy = "true"; sprite.onerror = function() { console.warn('木桩图片加载失败'); }; if (pkmn.psyduck?.float) sprite.classList.add('floating-pkmn'); else sprite.classList.remove('floating-pkmn'); document.getElementById('explore-wild-sprite-data').dataset.pkmn = dummyPkmnId; const moves = [dummy.moves.slot1, dummy.moves.slot2, dummy.moves.slot3, dummy.moves.slot4]; const container = document.getElementById('explore-header-moves-wild'); container.innerHTML = ''; for (let i = 0; i < 4; i++) { const moveId = moves[i]; if (!moveId) { const emptyDiv = document.createElement('div'); emptyDiv.className = 'pkmn-movebox'; emptyDiv.style.pointerEvents = 'none'; emptyDiv.style.opacity = '0.3'; container.appendChild(emptyDiv); } else { const moveDiv = document.createElement('div'); moveDiv.className = 'pkmn-movebox'; moveDiv.style.borderColor = returnTypeColor(move[moveId].type); moveDiv.id = `pkmn-movebox-wild-${i+1}`; moveDiv.innerHTML = `
${format(moveId)} `; moveDiv.dataset.move = moveId; container.appendChild(moveDiv); } } updateWildPkmn(); voidAnimation('explore-wild-sprite', 'wildPokemonSpawn 0.5s 1'); return; } const sprite = document.getElementById('explore-wild-sprite'); if (sprite) delete sprite.dataset.dummy; originalSetWildPkmn(); }; const originalExploreCombatPlayer = exploreCombatPlayer; exploreCombatPlayer = function() { originalExploreCombatPlayer(); if (saved.currentArea === dummyAreaId && pkmn[dummyPkmnId] && pkmn[dummyPkmnId].lockHp) { if (wildPkmnHp <= 0) { wildPkmnHp = wildPkmnHpMax; updateWildPkmn(); } } }; const originalExploreCombatWild = exploreCombatWild; exploreCombatWild = function() { originalExploreCombatWild(); if (saved.currentArea === dummyAreaId && pkmn[dummyPkmnId] && pkmn[dummyPkmnId].lockHp) { wildPkmnHp = wildPkmnHpMax; updateWildPkmn(); } }; // 安全包装原函数,忽略跨域错误 if (typeof trimTransparent === 'function') { const originalTrimTransparent = trimTransparent; trimTransparent = function(img) { try { return originalTrimTransparent(img); } catch (e) { if (e.name === 'SecurityError' && e.message.includes('cross-origin')) { console.warn('[DummyCustom] 忽略跨域裁剪错误'); return { width: img.naturalWidth, height: img.naturalHeight }; } throw e; } }; } })();