// ==UserScript== // @name [Pokeclicker] Enhanced Auto Hatchery // @namespace Pokeclicker Scripts // @author Ephenia (Original/Credit: Drak + Ivan Lay) // @description Automatically hatches eggs at 100% completion. Adds an On/Off button for auto hatching as well as an option for automatically hatching store bought eggs and dug up fossils. // @copyright https://github.com/Ephenia // @license GPL-3.0 License // @version 2.4.2 // @homepageURL https://github.com/Ephenia/Pokeclicker-Scripts/ // @supportURL https://github.com/Ephenia/Pokeclicker-Scripts/issues // @match https://www.pokeclicker.com/ // @icon https://www.google.com/s2/favicons?domain=pokeclicker.com // @grant none // @run-at document-idle // @downloadURL none // ==/UserScript== var scriptName = 'enhancedautohatchery'; var hatchState; var awaitAutoHatch; var autoHatchLoop; var updateHatchStateLoop; var randFossilEgg; var eggState; var fossilState; var shinyFossilState; var hatcherySortVal; var hatcherySortDir; var hatcherySortSync; var pkrsState; var pkrsStrict; function initAutoHatch() { const breedingDisplay = document.getElementById('breedingDisplay'); const breedingModal = document.getElementById('breedingModal'); breedingDisplay.querySelector('.card-header').outerHTML += `` breedingModal.querySelector('.modal-header').querySelectorAll('button')[1].outerHTML += ` ` document.getElementById('auto-hatch-start').addEventListener('click', event => { toggleAutoHatch(event); }); document.getElementById('sort-sync').addEventListener('click', event => { changesortsync(event); }); document.getElementById('auto-egg').addEventListener('click', event => { toggleEgg(event); }); document.getElementById('auto-fossil').addEventListener('click', event => { toggleFossil(event); }); document.getElementById('shiny-fossils').addEventListener('click', event => { toggleShinyFossil(event); }); document.getElementById('pkrs-mode').addEventListener('click', event => { togglePKRS(event); }); document.getElementById('pkrs-strict').addEventListener('click', event => { togglePKRSStrict(event); }); addGlobalStyle('.eggSlot.disabled { pointer-events: unset !important; }'); if (hatchState) { autoHatcher(); } } function toggleAutoHatch(event) { const element = event.target; hatchState = !hatchState; hatchState ? autoHatcher() : clearInterval(autoHatchLoop); hatchState ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger'); element.textContent = `Auto Hatch [${hatchState ? 'ON' : 'OFF'}]`; localStorage.setItem('autoHatchState', hatchState); } function changesortsync(event) { const element = event.target; hatcherySortSync = !hatcherySortSync; hatcherySortSync ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger'); element.textContent = `Pokemon List Sync [${hatcherySortSync ? 'ON' : 'OFF'}]`; localStorage.setItem('hatcherySortSync', hatcherySortSync); } function toggleEgg(event) { const element = event.target; eggState = !eggState; eggState ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger'); element.textContent = `Auto Egg [${eggState ? 'ON' : 'OFF'}]`; localStorage.setItem('autoEgg', eggState); } function toggleFossil(event) { const element = event.target; fossilState = !fossilState; fossilState ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger'); element.textContent = `Auto Fossil [${fossilState ? 'ON' : 'OFF'}]`; localStorage.setItem('autoFossil', fossilState); } function toggleShinyFossil(event) { const element = event.target; shinyFossilState = !shinyFossilState; shinyFossilState ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger'); element.textContent = `Shiny Fossils [${shinyFossilState ? 'ON' : 'OFF'}]`; localStorage.setItem('shinyFossil', shinyFossilState); } function togglePKRS(event) { const element = event.target; pkrsState = !pkrsState; pkrsState ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger'); element.textContent = `PKRS Mode [${pkrsState ? 'ON' : 'OFF'}]`; localStorage.setItem('pokerusModeState', pkrsState); } function togglePKRSStrict(event) { const element = event.target; pkrsStrict = !pkrsStrict; pkrsStrict ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger'); element.textContent = `PKRS Strict [${pkrsStrict ? 'ON' : 'OFF'}]`; localStorage.setItem('pokerusModeStrict', pkrsStrict); } function autoHatcher() { // Update the hatchery sort list every second so getHatcheryStory() is always up to date updateHatchStateLoop = setInterval(function () { let region = App.game.challenges.list.regionalAttackDebuff.active() ? BreedingController.regionalAttackDebuff() : -1; PartyController.hatcherySortedList = [...App.game.party.caughtPokemon]; PartyController.hatcherySortedList.sort(PartyController.compareBy(Settings.getSetting('hatcherySort').observableValue(), Settings.getSetting('hatcherySortDirection').observableValue(), region)); }, 1000); autoHatchLoop = setInterval(function () { //change daycare sorting if (hatcherySortSync) { const pS = Settings.getSetting('partySort'); const hS = Settings.getSetting('hatcherySort'); if (pS.observableValue() != hatcherySortVal) { hS.observableValue(pS.observableValue()) hatcherySortVal = pS.observableValue() localStorage.setItem("hatcherySortVal", hatcherySortVal); } if (hS.observableValue() != hatcherySortVal) { hatcherySortVal = hS.observableValue() pS.observableValue(hS.observableValue()) localStorage.setItem("hatcherySortVal", hatcherySortVal); } const pSD = Settings.getSetting('partySortDirection'); const hSD = Settings.getSetting('hatcherySortDirection'); if (pSD.observableValue() != hatcherySortDir) { hatcherySortDir = pSD.observableValue() hSD.observableValue(pSD.observableValue()) localStorage.setItem("hatcherySortDir", hatcherySortDir); } if (hSD.observableValue() != hatcherySortDir) { hatcherySortDir = hSD.observableValue() pSD.observableValue(hSD.observableValue()) localStorage.setItem("hatcherySortDir", hatcherySortDir); } } // Attempt to hatch each egg. If the egg is at 100% it will succeed [0, 1, 2, 3].forEach((index) => App.game.breeding.hatchPokemonEgg(index)); // Now add eggs to empty slots if we can while ( App.game.breeding.canAccess() == true && // Can access the Hatchery App.game.party.hasMaxLevelPokemon() && // Don't run if you don't have any level 100 Pokemon App.game.breeding.hasFreeEggSlot() // Has an open egg slot ) { var hasEgg; var hasFossil; if (eggState) { var randEggIndex; var storedEggName = []; const eggTypesLength = GameConstants.EggItemType[0].length; const eggTypes = GameConstants.EggItemType; for (var i = 0; i < eggTypesLength; i++) { const selEgg = eggTypes[i] if (player._itemList[selEgg]() > 0) { storedEggName.push(selEgg) } } if (storedEggName.length != 0) { randEggIndex = ((Math.floor(Math.random() * storedEggName.length) + 1) - 1) hasEgg = true; } else { hasEgg = false } } let randFossilIndex; const storedFossilID = []; if (fossilState) { const storedFossilName = []; const treasureLength = player.mineInventory().length; for (var e = 0; e < treasureLength; e++) { const valueType = player.mineInventory()[e].valueType; //valueType 3 equals fossil or old "Mine Egg" type const itemAmount = player.mineInventory()[e].amount() if (valueType == 3 && itemAmount > 0) { const fossilName = player.mineInventory()[e].name; const fossilID = player.mineInventory()[e].id; const fossilePoke = GameConstants.FossilToPokemon[fossilName]; // 0 = Not caught yet, 1 = Non-Shiny, 2 = Already Shiny const checkShiny = PartyController.getCaughtStatusByName(fossilePoke); const pokeRegion = PokemonHelper.calcNativeRegion(fossilePoke) const validFossil = pokeRegion <= player.highestRegion(); const shinyFossilize = shinyFossilState && checkShiny != 2; if (validFossil && shinyFossilize || validFossil && !shinyFossilState) { storedFossilName.push(fossilName) storedFossilID.push(fossilID) } } } if (storedFossilID.length != 0) { randFossilIndex = ((Math.floor(Math.random() * storedFossilID.length) + 1) - 1) hasFossil = true; } else { hasFossil = false; } } if (eggState || fossilState) { if (hasEgg == true && hasFossil == true) { const isEggFossil = (Math.floor(Math.random() * 2) + 1) if (isEggFossil == 1) { ItemList[storedEggName[randEggIndex]].use() return true; } else { Underground.sellMineItem(storedFossilID[randFossilIndex]) return true; } } else if (hasEgg == true) { ItemList[storedEggName[randEggIndex]].use() return true; } else if (hasFossil == true) { Underground.sellMineItem(+storedFossilID[randFossilIndex]) return true; } } // Filter the sorted list of Pokemon based on the parameters set in the Hatchery screen let filteredEggList = PartyController.hatcherySortedList.filter((partyPokemon) => { // Only breedable Pokemon if (partyPokemon.breeding || partyPokemon.level < 100) { return false; } // Check based on category if (BreedingFilters.category.value() >= 0) { if (partyPokemon.category !== BreedingFilters.category.value()) { return false; } } // Check based on shiny status if (BreedingFilters.shinyStatus.value() >= 0) { if (+partyPokemon.shiny !== BreedingFilters.shinyStatus.value()) { return false; } } // Check based on pokerus if (BreedingFilters.pokerus.value() >= 0) { if (+partyPokemon.pokerus !== BreedingFilters.pokerus.value()) { return false; } } // Check based on native region const useRegion = BreedingFilters.region.value() == 2 ** (player.highestRegion() + 1) - 1; if (!useRegion) { const regionVal = [1, 2, 4, 8, 16, 32, 64, 128]; const pokeNatRegion = PokemonHelper.calcNativeRegion(partyPokemon.name); if (regionVal[pokeNatRegion] !== BreedingFilters.region.value()) { return false; } } // Check based on Mega status const uniqueTransformation = BreedingFilters.uniqueTransformation.value(); // Only Base Pokémon with Mega available if (uniqueTransformation == 'mega-available' && !PokemonHelper.hasMegaEvolution(partyPokemon.name)) { return false; } // Only Base Pokémon without Mega Evolution if (uniqueTransformation == 'mega-unobtained' && !(PokemonHelper.hasMegaEvolution(partyPokemon.name) && partyPokemon.evolutions?.some((e) => !App.game.party.alreadyCaughtPokemonByName(e.evolvedPokemon)))) { return false; } // Only Mega Pokémon if (uniqueTransformation == 'mega-evolution' && !(PokemonHelper.getPokemonPrevolution(partyPokemon.name)?.some((e) => PokemonHelper.hasMegaEvolution(e.basePokemon)))) { return false; } // Check if either of the types match const type1 = BreedingFilters.type1.value() > -2 ? BreedingFilters.type1.value() : null; const type2 = BreedingFilters.type2.value() > -2 ? BreedingFilters.type2.value() : 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; }); const hasPKRS = App.game.keyItems.hasKeyItem(KeyItemType.Pokerus_virus); const starterID = GameConstants.RegionalStarters[GameConstants.Region.kanto][player.regionStarters[GameConstants.Region.kanto]()]; const starterPKMN = PartyController.getSortedList().filter(p => p.id == starterID)[0]; const virusReady = PartyController.getSortedList().filter(e => e._level() == 100 && e.breeding == false && e.pokerus == false); if (pkrsState && hasPKRS && virusReady.length != 0) { if (starterPKMN._level() == 100 && !starterPKMN.breeding) { App.game.breeding.addPokemonToHatchery(starterPKMN); return true; } else if (starterPKMN._level() == 100 && starterPKMN.breeding) { App.game.breeding.addPokemonToHatchery(virusReady[0]); return true; } if (pkrsStrict) { return true; } else { basicHatchery(); } } else { basicHatchery(); } function basicHatchery() { try { App.game.breeding.addPokemonToHatchery(filteredEggList[0]); } catch (err) { const canBreed = PartyController.getSortedList().filter(e => e._level() == 100 && e.breeding == false); const randBreed = getRandomInt(canBreed.length); App.game.breeding.addPokemonToHatchery(canBreed[randBreed]); } } } }, 50); // Runs every game tick } if (!validParse(localStorage.getItem('autoHatchState'))) { localStorage.setItem("autoHatchState", false); } if (!validParse(localStorage.getItem('autoEgg'))) { localStorage.setItem("autoEgg", false); } if (!validParse(localStorage.getItem('autoFossil'))) { localStorage.setItem("autoFossil", false); } if (!validParse(localStorage.getItem('shinyFossil'))) { localStorage.setItem("shinyFossil", false); } if (!validParse(localStorage.getItem('hatcherySortVal'))) { localStorage.setItem("hatcherySortVal", 0); } if (!validParse(localStorage.getItem('hatcherySortDir'))) { localStorage.setItem("hatcherySortDir", true); } if (!validParse(localStorage.getItem('hatcherySortSync'))) { localStorage.setItem("hatcherySortSync", false); } if (!validParse(localStorage.getItem('pokerusModeState'))) { localStorage.setItem("pokerusModeState", false); } if (!validParse(localStorage.getItem('pokerusModeStrict'))) { localStorage.setItem("pokerusModeStrict", false); } hatchState = JSON.parse(localStorage.getItem('autoHatchState')); eggState = JSON.parse(localStorage.getItem('autoEgg')); fossilState = JSON.parse(localStorage.getItem('autoFossil')); shinyFossilState = JSON.parse(localStorage.getItem('shinyFossil')); hatcherySortVal = JSON.parse(localStorage.getItem('hatcherySortVal')); hatcherySortDir = JSON.parse(localStorage.getItem('hatcherySortDir')); hatcherySortSync = JSON.parse(localStorage.getItem('hatcherySortSync')); pkrsState = JSON.parse(localStorage.getItem('pokerusModeState')); pkrsStrict = JSON.parse(localStorage.getItem('pokerusModeStrict')); function loadScript() { var oldInit = Preload.hideSplashScreen Preload.hideSplashScreen = function () { var result = oldInit.apply(this, arguments) initAutoHatch() return result } } function getRandomInt(max) { return Math.floor(Math.random() * max); } function validParse(key) { try { if (key === null) { throw new Error; } JSON.parse(key); return true; } catch (e) { return false; } } function addGlobalStyle(css) { var head, style; head = document.getElementsByTagName('head')[0]; if (!head) { return; } style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; head.appendChild(style); } if (!App.isUsingClient || localStorage.getItem(scriptName) === 'true') { loadScript(); }