// ==UserScript==
// @name [Pokechill] 自定义木桩测试
// @namespace https://play-pokechill.github.io/
// @version 2.0
// @description 木桩技能池基于所选属性动态过滤,包含所有可学技能。重置按钮同时清空已选技能,修复卡片图片显示。
// @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/69afa253d4763.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();
}
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: 6, atk: 4, def: 2, satk: 4, sdef: 2, 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: []
};
}
}
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: 300px;
color: var(--dark1, #36342F);
font-family: 'Courier New', monospace;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
`;
panel.innerHTML = `
配置木桩
`;
document.body.appendChild(panel);
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));
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.moves = { slot1: undefined, slot2: undefined, slot3: undefined, slot4: undefined };
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;
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;
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;
}
};
}
})();