|
// ==UserScript== |
|
// @name May's Pokeclicker Tools |
|
// @namespace http://tampermonkey.net/ |
|
// @version 0.7.0 |
|
// @description pokeclicker but idle-r |
|
// @author Corvimae |
|
// @match https://www.pokeclicker.com/ |
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=pokeclicker.com |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
(function() { |
|
'use strict'; |
|
const SettingsKeys = { |
|
AUTO_CLICKER_ENABLED: 'AUTO_CLICKER_ENABLED', |
|
AUTO_DUNGEON_ENABLED: 'AUTO_DUNGEON_ENABLED', |
|
AUTO_DUNGEON_PICK_UP_ITEMS_ENABLED: 'AUTO_DUNGEON_PICK_UP_ITEMS_ENABLED', |
|
AUTO_DUNGEON_AUTO_START: 'AUTO_DUNGEON_AUTO_START', |
|
AUTO_GYM_BATTLE_ENABLED: 'AUTO_GYM_BATTLE_ENABLED', |
|
AUTO_GYM_BATTLE_INDEX: 'AUTO_GYM_BATTLE_INDEX', |
|
AUTO_MINE_ENABLED: 'AUTO_MINE_ENABLED', |
|
AUTO_MINE_ALGORITHM: 'AUTO_MINE_ALGORITHM', |
|
AUTO_MINE_ENERGY_FLOOR: 'AUTO_MINE_ENERGY_FLOOR', |
|
AUTO_MINE_ENERGY_FLOOR: 'AUTO_MINE_ENERGY_FLOOR', |
|
AUTO_EGG_QUEUE_ENABLED: 'AUTO_EGG_QUEUE_ENABLED', |
|
}; |
|
|
|
function formatSettingsKey(key) { |
|
return `POKECLICKER_AUTO_TOOLS_${key}`; |
|
} |
|
|
|
function loadSetting(key, defaultValue, onLoad) { |
|
onLoad(window.localStorage.getItem(formatSettingsKey(key)) ?? defaultValue); |
|
} |
|
|
|
function loadSettingToInput(key, defaultValue, inputSelector, onLoad) { |
|
loadSetting(key, defaultValue, value => { |
|
document.querySelector(inputSelector).setAttribute('value', value); |
|
|
|
onLoad(value); |
|
}); |
|
} |
|
|
|
function loadSettingToSelect(key, defaultValue, inputSelector, onLoad) { |
|
loadSetting(key, defaultValue, value => { |
|
const option = [...document.querySelector(inputSelector).options].find(item => item.value === value); |
|
|
|
if (option) option.selected = true; |
|
|
|
onLoad(value); |
|
}); |
|
} |
|
|
|
function updateSetting(key, value) { |
|
window.localStorage.setItem(formatSettingsKey(key), value); |
|
} |
|
|
|
function setButtonEnabledStatus(className, enabled) { |
|
const elem = document.body.querySelector(`.${className}`); |
|
|
|
if (!elem) return; |
|
|
|
elem.classList.remove('btn-success', 'btn-danger'); |
|
|
|
if (enabled) { |
|
elem.classList.add('btn-success'); |
|
elem.textContent = 'On'; |
|
} else { |
|
elem.classList.add('btn-danger'); |
|
elem.textContent = 'Off'; |
|
} |
|
} |
|
|
|
function createToggleSet(className, settingsKey, defaultValue = false, onToggle = x => x) { |
|
const value = { current: null }; |
|
|
|
function disable() { |
|
onToggle(false); |
|
value.current = false; |
|
updateSetting(settingsKey, false); |
|
setButtonEnabledStatus(className, false); |
|
} |
|
|
|
function enable() { |
|
onToggle(true); |
|
value.current = true; |
|
updateSetting(settingsKey, true); |
|
setButtonEnabledStatus(className, true); |
|
} |
|
|
|
function toggle() { |
|
if (value.current) { |
|
disable(); |
|
} else { |
|
enable(); |
|
} |
|
} |
|
|
|
function initialize() { |
|
loadSetting(settingsKey, defaultValue, value => { |
|
if (value !== 'false') { |
|
enable(); |
|
} else { |
|
disable(); |
|
} |
|
}); |
|
} |
|
|
|
return [value, enable, disable, toggle, initialize]; |
|
} |
|
|
|
function createIntervalToggleSet(className, func, settingsKey, defaultValue = false, intervalMs = 50) { |
|
const interval = { current: null }; |
|
|
|
const [value, enable, disable, toggle, initialize] = createToggleSet( |
|
className, |
|
settingsKey, |
|
defaultValue, |
|
enabled => { |
|
if (interval.current) { |
|
clearInterval(interval.current); |
|
interval.current = null; |
|
} |
|
|
|
if (enabled) { |
|
interval.current = setInterval(func, intervalMs); |
|
} |
|
} |
|
); |
|
|
|
// const interval = { current: null }; |
|
|
|
// function disable() { |
|
// setButtonEnabledStatus(className, false); |
|
|
|
// updateSetting(settingsKey, false); |
|
|
|
// if (interval.current) { |
|
// clearInterval(interval.current); |
|
// interval.current = null; |
|
// } |
|
// } |
|
|
|
// function enable() { |
|
// if (interval.current) { |
|
// clearInterval(interval.current); |
|
// interval.current = null; |
|
// } |
|
|
|
// updateSetting(settingsKey, true); |
|
// setButtonEnabledStatus(className, true); |
|
// interval.current = setInterval(func, intervalMs); |
|
// } |
|
|
|
// function toggle() { |
|
// if (interval.current) { |
|
// disable(); |
|
// } else { |
|
// enable(); |
|
// } |
|
// } |
|
|
|
// function initialize() { |
|
// loadSetting(settingsKey, defaultValue, value => { |
|
// if (value !== 'false') { |
|
// enable(); |
|
// } else { |
|
// disable(); |
|
// } |
|
// }); |
|
// } |
|
|
|
return [enable, disable, toggle, initialize, interval]; |
|
} |
|
|
|
function tickAutoclicker() { |
|
if(!App || !App.game) return; |
|
|
|
// Route |
|
if (App.game.gameState == GameConstants.GameState.fighting && Battle.enemyPokemon() && Battle.enemyPokemon().health() > 0) { |
|
Battle.clickAttack(); |
|
} |
|
|
|
// Gym |
|
if (App.game.gameState == GameConstants.GameState.gym && GymBattle.enemyPokemon() && GymBattle.enemyPokemon().health() > 0) { |
|
GymBattle.clickAttack(); |
|
} |
|
|
|
// Dungeon |
|
if (App.game.gameState == GameConstants.GameState.dungeon && DungeonBattle.enemyPokemon() && DungeonBattle.enemyPokemon().health() > 0) { |
|
DungeonBattle.clickAttack(); |
|
} |
|
} |
|
|
|
const [enableAutoclicker, disableAutoclicker, toggleAutoclicker, initializeAutoclicker] = createIntervalToggleSet('autoclicker', tickAutoclicker, SettingsKeys.AUTO_CLICKER_ENABLED, true, 10); |
|
|
|
const [ |
|
autoDungeonPickupItems, |
|
enableAutoDungeonPickupItems, |
|
disableAutoDungeonPickupItems, |
|
toggleAutoDungeonPickupItems, |
|
initializeAutoDungeonPickupItems, |
|
] = createToggleSet('autodungeonpickup', SettingsKeys.AUTO_DUNGEON_PICK_UP_ITEMS_ENABLED, false); |
|
|
|
const [ |
|
autoDungeonAutoStart, |
|
enableAutoDungeonAutoStart, |
|
disableAutoDungeonAutoStart, |
|
toggleAutoDungeonAutoStart, |
|
initializeAutoDungeonAutoStart, |
|
] = createToggleSet('autodungeonstart', SettingsKeys.AUTO_DUNGEON_AUTO_START, false); |
|
|
|
let queue = []; |
|
|
|
function tickAutoDungeon() { |
|
if (!App || !App.game) return; |
|
|
|
if (App.game.gameState === GameConstants.GameState.town && player.town() instanceof DungeonTown && player.town().dungeon && autoDungeonAutoStart.current) { |
|
DungeonRunner.initializeDungeon(player.town().dungeon); |
|
queue = []; |
|
|
|
return; |
|
} |
|
|
|
if (App.game.gameState === GameConstants.GameState.dungeon) { |
|
const dimensions = DungeonRunner.map.board().length; |
|
const position = DungeonRunner.map.playerPosition() |
|
|
|
if (queue.length === 0) { |
|
// Move to 0, 0 |
|
for (let i = position.x; i >= 0; i -= 1) { |
|
queue.push('left'); |
|
} |
|
|
|
for (let j = position.y; j >= 0; j -= 1) { |
|
queue.push('up'); |
|
} |
|
|
|
queue.push('right'); |
|
|
|
for (let x = 0; x < dimensions - 1; x += 1) { |
|
const startOffset = x < Math.floor(dimensions / 2) - 1 ? 1 : 0; |
|
|
|
for (let y = startOffset; y < dimensions - 1; y += 1) { |
|
queue.push(x % 2 === 0 ? 'down' : 'up'); |
|
} |
|
queue.push('right'); |
|
} |
|
} |
|
|
|
const tileType = DungeonRunner.map.currentTile().type(); |
|
|
|
const isSkippableItem = tileType === GameConstants.DungeonTile.chest && !autoDungeonPickupItems.current |
|
|
|
if (!DungeonBattle.catching() && !DungeonRunner.fighting() ) { |
|
if (tileType === GameConstants.DungeonTile.empty || tileType === GameConstants.DungeonTile.entrance || isSkippableItem) { |
|
const [head] = queue.splice(0, 1); |
|
|
|
if (head) { |
|
switch (head) { |
|
case 'left': |
|
DungeonRunner.map.moveLeft(); |
|
break; |
|
case 'right': |
|
DungeonRunner.map.moveRight(); |
|
break; |
|
case 'up': |
|
DungeonRunner.map.moveUp(); |
|
break; |
|
case 'down': |
|
DungeonRunner.map.moveDown(); |
|
break; |
|
} |
|
} |
|
} else { |
|
DungeonRunner.handleClick(); |
|
} |
|
} |
|
} else { |
|
queue = []; |
|
} |
|
} |
|
|
|
const [enableAutoDungeon, disableAutoDungeon, toggleAutoDungeon, initializeAutoDungeon] = createIntervalToggleSet('autodungeon', tickAutoDungeon, SettingsKeys.AUTO_DUNGEON_ENABLED, false, 50); |
|
|
|
const autoGymBattleIndex = { current: 0 }; |
|
|
|
function tickAutoGymBattle() { |
|
if (!App || !App.game) return; |
|
|
|
if (App.game.gameState === GameConstants.GameState.town) { |
|
const gyms = player.town().content.filter(item => item instanceof Gym); |
|
|
|
const selectedGym = gyms[autoGymBattleIndex.current] ?? gyms[0]; |
|
|
|
if (selectedGym) GymRunner.startGym(selectedGym, false) |
|
} |
|
} |
|
|
|
function setAutoGymBattleIndex(event) { |
|
autoGymBattleIndex.current = Number(event.target.value); |
|
updateSetting(SettingsKeys.AUTO_GYM_BATTLE_INDEX, event.target.value); |
|
} |
|
|
|
const [enableAutoGymBattle, disableAutoGymBattle, toggleAutoGymBattle, initializeAutoGymBattle] = createIntervalToggleSet('autogymbattle', tickAutoGymBattle, SettingsKeys.AUTO_GYM_BATTLE_ENABLED, false, 50); |
|
|
|
let activeMineGrid = { current: null }; |
|
const currentMineCoordinates = { current: null }; |
|
const miningPass = { current: 0 }; |
|
const autoMineAlgorithm = { current: 'checkered' }; |
|
const autoMineEnergyFloor = { current: 0 }; |
|
|
|
function setAutoMineAlgorithm(event) { |
|
autoMineAlgorithm.current = event.target.value; |
|
updateSetting(SettingsKeys.AUTO_MINE_ALGORITHM, event.target.value); |
|
} |
|
|
|
function setAutoMineEnergyFloor(event) { |
|
autoMineEnergyFloor.current = Number(event.target.value); |
|
updateSetting(SettingsKeys.AUTO_MINE_ENERGY_FLOOR, event.target.value); |
|
} |
|
|
|
function tickAutoMine() { |
|
if (!App || !App.game) return; |
|
|
|
if (Mine.loadingNewLayer) return; |
|
|
|
if (activeMineGrid.current !== Mine.grid) { |
|
// New grid, restart from top. |
|
currentMineCoordinates.current = null; |
|
activeMineGrid.current = Mine.grid; |
|
miningPass.current = 0; |
|
} |
|
|
|
if (App.game.underground.energy < Underground.CHISEL_ENERGY + autoMineEnergyFloor.current) return; |
|
|
|
const dimensionsX = activeMineGrid.current[0].length; |
|
const dimensionsY = activeMineGrid.current.length; |
|
|
|
const { x, y } = currentMineCoordinates.current ?? { x: -1, y: -1}; |
|
|
|
const tile = currentMineCoordinates.current ? activeMineGrid.current[y][x]() : 0; |
|
|
|
if (tile > 0) { |
|
Mine.chisel(y, x); // backwards moment woozy emoji |
|
} else { |
|
if (currentMineCoordinates.current === null) { |
|
currentMineCoordinates.current = { x: 0, y: 0 }; |
|
} else { |
|
const isEndOfRow = x + (autoMineAlgorithm.current === 'checkered' ? 2 : 1) >= dimensionsX; |
|
|
|
if (isEndOfRow) { |
|
const isEndOfBoard = y + 1 >= dimensionsY; |
|
|
|
if (isEndOfBoard) { |
|
currentMineCoordinates.current = { x: miningPass.current === 0 ? 1 : 0, y: 0 }; |
|
miningPass.current = miningPass.current === 0 ? 1 : 0; |
|
} else { |
|
const passOffset = miningPass.current === 0 ? 0 : 1; |
|
|
|
console.log(passOffset, autoMineAlgorithm.current === 'checkered', y % 2 === 1); |
|
currentMineCoordinates.current = { |
|
x: passOffset + (autoMineAlgorithm.current === 'checkered' && y % 2 === 0 ? 1 : 0), |
|
y: y + 1 |
|
}; |
|
} |
|
} else { |
|
currentMineCoordinates.current = { |
|
x: x + (autoMineAlgorithm.current === 'checkered' ? 2 : 1), |
|
y, |
|
}; |
|
} |
|
} |
|
} |
|
} |
|
|
|
const [enableAutoMine, disableAutoMine, toggleAutoMine, initializeAutoMine] = createIntervalToggleSet('automine', tickAutoMine, SettingsKeys.AUTO_MINE_ENABLED, false, 50); |
|
|
|
function isPokemonHatchable(partyPokemon) { |
|
if (partyPokemon.breeding || partyPokemon.level < 100) { |
|
return false; |
|
} |
|
|
|
if (!BreedingController.filter.search().test(partyPokemon.name)) { |
|
return false; |
|
} |
|
|
|
// Check based on category |
|
if (BreedingController.filter.category() >= 0) { |
|
if (partyPokemon.category !== BreedingController.filter.category()) { |
|
return false; |
|
} |
|
} |
|
|
|
// Check based on shiny status |
|
if (BreedingController.filter.shinyStatus() >= 0) { |
|
if (+partyPokemon.shiny !== BreedingController.filter.shinyStatus()) { |
|
return false; |
|
} |
|
} |
|
|
|
// Check based on native region |
|
if (BreedingController.filter.region() > -2) { |
|
if (PokemonHelper.calcNativeRegion(partyPokemon.name) !== BreedingController.filter.region()) { |
|
return false; |
|
} |
|
} |
|
|
|
// Check if either of the types match |
|
const type1 = BreedingController.filter.type1() > -2 ? BreedingController.filter.type1() : null; |
|
const type2 = BreedingController.filter.type2() > -2 ? BreedingController.filter.type2() : null; |
|
if (type1 !== null || type2 !== null) { |
|
const { type: types } = pokemonMap[partyPokemon.name]; |
|
if ([type1, type2].includes(PokemonType.None)) { |
|
const type = (type1 == PokemonType.None) ? type2 : type1; |
|
if (!BreedingController.isPureType(partyPokemon, type)) { |
|
return false; |
|
} |
|
} else if ((type1 !== null && !types.includes(type1)) || (type2 !== null && !types.includes(type2))) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
function tickAutoHatcheryQueue() { |
|
if (!App || !App.game) return; |
|
|
|
const queueSize = App.game.breeding.queueList().length; |
|
const queueSlots = 2; // App.game.breeding.queueSlots(); |
|
|
|
if (queueSize < queueSlots) { |
|
const hatcheryList = [...App.game.party.caughtPokemon].sort(PartyController.compareBy(Settings.getSetting('hatcherySort').observableValue(), Settings.getSetting('hatcherySortDirection').observableValue())); |
|
|
|
let offset = 0; |
|
|
|
for (let i = 0; i < queueSlots - queueSize + offset; i += 1) { |
|
const pokemon = hatcheryList[i]; |
|
|
|
if (!pokemon) return; |
|
|
|
if (isPokemonHatchable(pokemon)) { |
|
App.game.breeding.addPokemonToHatchery(pokemon); |
|
} else { |
|
offset += 1; |
|
} |
|
} |
|
} |
|
} |
|
|
|
const [enableAutoHatcheryQueue, disableAutoHatcheryQueue, toggleAutoHatcheryQueue, initializeAutoHatcheryQueue] = createIntervalToggleSet('autohatcheryqueue', tickAutoHatcheryQueue, SettingsKeys.AUTO_EGG_QUEUE_ENABLED, true, 50); |
|
|
|
function createToolUIRow(label, className, majorFeature, rightCell) { |
|
const trElem = document.createElement('tr'); |
|
const labelElem = document.createElement('td'); |
|
const buttonContainerElem = document.createElement('td'); |
|
|
|
labelElem.classList.add(className + '-label', 'text-left', majorFeature ? 'font-weight-bold' : 'font-weight-normal'); |
|
labelElem.textContent = label; |
|
|
|
buttonContainerElem.classList.add('p-1'); |
|
|
|
buttonContainerElem.appendChild(rightCell); |
|
trElem.appendChild(labelElem); |
|
trElem.appendChild(buttonContainerElem); |
|
|
|
return trElem; |
|
} |
|
|
|
function createToolUIToggleRow(label, className, majorFeature, onClick) { |
|
const buttonElem = document.createElement('button'); |
|
|
|
buttonElem.classList.add(className, 'btn', 'btn-block'); |
|
buttonElem.onclick = onClick; |
|
|
|
return createToolUIRow(label, className, majorFeature, buttonElem); |
|
} |
|
|
|
|
|
function createToolUINumberRow(label, className, majorFeature, onChange, defaultValue = 0) { |
|
const inputElem = document.createElement('input'); |
|
|
|
inputElem.classList.add(className, 'form-control'); |
|
inputElem.setAttribute('type', 'number'); |
|
inputElem.setAttribute('value', defaultValue); |
|
inputElem.onchange = onChange; |
|
|
|
return createToolUIRow(label, className, majorFeature, inputElem); |
|
} |
|
|
|
function createToolUISelectRow(label, className, majorFeature, onChange, options) { |
|
const selectElem = document.createElement('select'); |
|
|
|
selectElem.classList.add(className, 'form-control'); |
|
selectElem.onchange = onChange; |
|
|
|
options.forEach(([label, value]) => { |
|
const optionElem = document.createElement('option'); |
|
|
|
optionElem.setAttribute('value', value); |
|
optionElem.textContent = label; |
|
|
|
selectElem.appendChild(optionElem); |
|
}); |
|
|
|
return createToolUIRow(label, className, majorFeature, selectElem); |
|
} |
|
|
|
function createControlBar() { |
|
const controlBarElem = document.createElement('div'); |
|
|
|
controlBarElem.classList.add('control-bar', 'card', 'sortable', 'border-secondary', 'mb-3', 'sortable-chosen'); |
|
|
|
const controlBarTitle = document.createElement('div'); |
|
|
|
controlBarTitle.classList.add('card-header', 'p-0'); |
|
controlBarTitle.setAttribute('data-toggle', 'collapse'); |
|
|
|
const controlBarTitleText = document.createElement('span'); |
|
|
|
controlBarTitleText.textContent = 'Auto Tools'; |
|
|
|
controlBarTitle.appendChild(controlBarTitleText); |
|
|
|
const tableElem = document.createElement('table'); |
|
|
|
tableElem.classList.add('table'); |
|
tableElem.setAttribute('width', '100%'); |
|
|
|
const tableBodyElem = document.createElement('tbody'); |
|
|
|
tableBodyElem.appendChild(createToolUIToggleRow('Auto Clicker', 'autoclicker', true, toggleAutoclicker)); |
|
tableBodyElem.appendChild(createToolUIToggleRow('Auto Dungeon', 'autodungeon', true, toggleAutoDungeon)); |
|
tableBodyElem.appendChild(createToolUIToggleRow('Pick up items?', 'autodungeonpickup', false, toggleAutoDungeonPickupItems)); |
|
tableBodyElem.appendChild(createToolUIToggleRow('Auto start dungeon?', 'autodungeonstart', false, toggleAutoDungeonAutoStart)); |
|
tableBodyElem.appendChild(createToolUIToggleRow('Auto Gym Battle', 'autogymbattle', true, toggleAutoGymBattle)); |
|
tableBodyElem.appendChild(createToolUISelectRow('Elite 4 Selection', 'autogymbattlee4', false, setAutoGymBattleIndex, [ |
|
['1', '0'], |
|
['2', '1'], |
|
['3', '2'], |
|
['4', '3'], |
|
['Champion', '4'], |
|
])); |
|
tableBodyElem.appendChild(createToolUIToggleRow('Auto Mine', 'automine', true, toggleAutoMine)); |
|
tableBodyElem.appendChild(createToolUISelectRow('Mine Algorithm', 'autominealgorithm', false, setAutoMineAlgorithm, [ |
|
['Checkered', 'checkered'], |
|
['Sequential', 'sequential'], |
|
])); |
|
tableBodyElem.appendChild(createToolUINumberRow('Min. Energy', 'automineenergyfloor', false, setAutoMineEnergyFloor)); |
|
tableBodyElem.appendChild(createToolUIToggleRow('Auto Egg Queue', 'autohatcheryqueue', true, toggleAutoHatcheryQueue)); |
|
tableElem.appendChild(tableBodyElem); |
|
|
|
controlBarElem.appendChild(controlBarTitle); |
|
controlBarElem.appendChild(tableElem); |
|
|
|
document.querySelector('#left-column').appendChild(controlBarElem); |
|
} |
|
|
|
const gameStartInterval = { current: null }; |
|
|
|
gameStartInterval.current = setInterval(() => { |
|
if (App && App.game && document.querySelector('#left-column')) { |
|
clearInterval(gameStartInterval.current); |
|
|
|
createControlBar(); |
|
|
|
setTimeout(() => { |
|
initializeAutoclicker(); |
|
initializeAutoDungeon(); |
|
initializeAutoDungeonPickupItems(); |
|
initializeAutoDungeonAutoStart(); |
|
initializeAutoMine(); |
|
initializeAutoGymBattle(); |
|
initializeAutoHatcheryQueue(); |
|
|
|
loadSettingToSelect(SettingsKeys.AUTO_GYM_BATTLE_INDEX, 1, '.autogymbattlee4', index => { |
|
autoGymBattleIndex.value = Number(index); |
|
}); |
|
|
|
loadSettingToSelect(SettingsKeys.AUTO_MINE_ALGORITHM, 'checkered', '.autominealgorithm', algorithm => { |
|
autoMineAlgorithm.current = algorithm; |
|
}); |
|
|
|
loadSettingToInput(SettingsKeys.AUTO_MINE_ENERGY_FLOOR, 0, '.automineenergyfloor', value => { |
|
autoMineEnergyFloor.current = Number(value); |
|
}); |
|
}, 0); |
|
} |
|
}, 50); |
|
})(); |