From ff6e72719f52629b2dc49d98ea165be6ff8a16d6 Mon Sep 17 00:00:00 2001
From: goldhaxx <1616671+goldhaxx@users.noreply.github.com>
Date: Sun, 7 Sep 2025 13:06:42 -0700
Subject: [PATCH 1/2] (Filter-Only + Priority IDs)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Enhance Auto Hatchery functionality by introducing new controls for Filter Only, and a user-defined Priority ID queue. Updated the description and version number in the metadata. Added event listeners for new features and improved the auto-hatching logic to prioritize user-defined Pokémon IDs. Implemented local storage handling for priority IDs and filter state. This update expands the script's capabilities for managing hatchery queue.
---
enhancedautohatchery.user.js | 180 ++++++++++++++++++++++++++---------
1 file changed, 136 insertions(+), 44 deletions(-)
diff --git a/enhancedautohatchery.user.js b/enhancedautohatchery.user.js
index 81f4872..ac5d8d7 100644
--- a/enhancedautohatchery.user.js
+++ b/enhancedautohatchery.user.js
@@ -2,10 +2,10 @@
// @name [Pokeclicker] Enhanced Auto Hatchery
// @namespace Pokeclicker Scripts
// @author Ephenia (Original/Credit: Drak + Ivan Lay, Optimatum)
-// @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.
+// @description Auto-hatches eggs at 100% and adds controls: Auto Hatch, PKRS Mode, Auto Egg, Auto Fossil, Shiny Fossils, Filter Only, and a Priority ID queue (e.g., 149,123,552).
// @copyright https://github.com/Ephenia
// @license GPL-3.0 License
-// @version 3.1.4
+// @version 3.3.0-priority
// @homepageURL https://github.com/Ephenia/Pokeclicker-Scripts/
// @supportURL https://github.com/Ephenia/Pokeclicker-Scripts/issues
@@ -23,6 +23,12 @@ var eggState;
var fossilState;
var shinyFossilState;
var pkrsState;
+// Filter Only: restricts loading to current hatchery filter
+var filterOnlyState;
+
+// NEW: user-defined priority Pokémon IDs (array of numbers)
+var priorityIdList = [];
+
var pkrsHatcherySearchTime = 0;
var numMonsWithPkrsCached;
var autoHatcheryCachedList = [];
@@ -36,24 +42,42 @@ function initAutoHatch() {
Auto Hatch [${hatchState ? 'ON' : 'OFF'}]
`
- breedingModal.querySelector('.modal-header').querySelectorAll('button')[1].outerHTML += `
+
+ Filter Only [${filterOnlyState ? 'ON' : 'OFF'}]
+
+
+
+
+ Save Priority
+
+
+ `;
+
+ document.getElementById('auto-hatch-start').addEventListener('click', toggleAutoHatch);
+ document.getElementById('auto-egg').addEventListener('click', toggleEgg);
+ document.getElementById('auto-fossil').addEventListener('click', toggleFossil);
+ document.getElementById('shiny-fossils').addEventListener('click', toggleShinyFossil);
+ document.getElementById('pkrs-mode').addEventListener('click', togglePKRS);
+ document.getElementById('filter-only').addEventListener('click', toggleFilterOnly);
- document.getElementById('auto-hatch-start').addEventListener('click', event => { toggleAutoHatch(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); });
+ // Load/render priority list UI
+ renderPriorityUiFromStorage();
+ document.getElementById('priority-save').addEventListener('click', savePriorityFromInput);
addGlobalStyle('.eggSlot.disabled { pointer-events: unset !important; }');
@@ -61,7 +85,11 @@ function initAutoHatch() {
autoHatcheryCachedList = BreedingController.hatcherySortedFilteredList();
// Immediately refresh the cached list when the filtered list or sort settings change
- const listUpdateObservables = [BreedingController.hatcheryFilteredList, Settings.getSetting('hatcherySort').observableValue, Settings.getSetting('hatcherySortDirection').observableValue];
+ const listUpdateObservables = [
+ BreedingController.hatcheryFilteredList,
+ Settings.getSetting('hatcherySort').observableValue,
+ Settings.getSetting('hatcherySortDirection').observableValue
+ ];
listUpdateObservables.forEach(observable => observable.subscribe(() => {
autoHatcheryCachedList = BreedingController.hatcherySortedFilteredList();
}));
@@ -114,6 +142,74 @@ function togglePKRS(event) {
localStorage.setItem('pokerusModeState', pkrsState);
}
+function toggleFilterOnly(event) {
+ const element = event.target;
+ filterOnlyState = !filterOnlyState;
+ element.classList.replace(...(filterOnlyState ? ['btn-danger', 'btn-success'] : ['btn-success', 'btn-danger']));
+ element.textContent = `Filter Only [${filterOnlyState ? 'ON' : 'OFF'}]`;
+ localStorage.setItem('filterOnly', filterOnlyState);
+}
+
+// ---------- Priority IDs: storage, parsing, UI ----------
+
+function renderPriorityUiFromStorage() {
+ // Load from localStorage
+ const raw = localStorage.getItem('priorityMonIds') || '[]';
+ try {
+ const arr = JSON.parse(raw);
+ if (Array.isArray(arr)) priorityIdList = arr.map(n => Number(n)).filter(n => Number.isFinite(n) && n > 0);
+ } catch {
+ priorityIdList = [];
+ }
+ const input = document.getElementById('priority-ids');
+ const status = document.getElementById('priority-status');
+ input.value = priorityIdList.join(', ');
+ status.textContent = priorityIdList.length ? `Priority: [${priorityIdList.join(', ')}]` : 'Priority: (none)';
+}
+
+function savePriorityFromInput() {
+ const input = document.getElementById('priority-ids');
+ const status = document.getElementById('priority-status');
+ // Accept comma/space separated, tolerate extra chars
+ const parts = input.value.split(/[^0-9]+/).filter(Boolean);
+ const parsed = parts.map(n => Number(n)).filter(n => Number.isFinite(n) && n > 0);
+ // De-duplicate while preserving order
+ const seen = new Set();
+ priorityIdList = parsed.filter(n => (seen.has(n) ? false : (seen.add(n), true)));
+
+ localStorage.setItem('priorityMonIds', JSON.stringify(priorityIdList));
+ status.textContent = priorityIdList.length ? `Priority: [${priorityIdList.join(', ')}]` : 'Priority: (none)';
+ Notifier.notify({
+ type: NotificationConstants.NotificationOption.success,
+ title: '[Auto Hatchery]',
+ message: `Saved priority IDs: ${priorityIdList.length ? priorityIdList.join(', ') : '(none)'}`,
+ timeout: GameConstants.SECOND * 3,
+ });
+}
+
+// Try adding the next available priority mon (highest priority first)
+function autoHatchPriority() {
+ if (!priorityIdList.length) return false;
+
+ // Build quick lookup from party by Pokédex ID -> first hatchable instance
+ // We'll scan per priority to keep order strict.
+ for (let id of priorityIdList) {
+ // Find a hatchable party member whose dex ID matches
+ const mon = App.game.party.caughtPokemon.find(p => {
+ if (!p || p.breeding || !p.isHatchable()) return false;
+ // pokemonMap[p.name].id is available elsewhere in script (type was used earlier)
+ const data = pokemonMap[p.name];
+ return data && data.id === id;
+ });
+ if (mon) {
+ return App.game.breeding.addPokemonToHatchery(mon);
+ }
+ }
+ return false;
+}
+
+// --------------------------------------------------------
+
function bindAutoHatcher() {
const progressEggsOld = Breeding.prototype.progressEggs;
Breeding.prototype.progressEggs = function progressEggs(...args) {
@@ -141,15 +237,18 @@ function autoHatcher() {
}
while (App.game.breeding.hasFreeEggSlot()) {
- // Attempts enabled autoHatch methods in order until one succeeds
- // (subsequent autoHatch methods aren't called due to short-circuiting)
- let success = pkrsState && autoHatchPkrs();
+ // Priority list ALWAYS comes first (strict order)
+ let success = autoHatchPriority();
+
+ // Then PKRS / Eggs / Fossils
+ success ||= pkrsState && autoHatchPkrs();
success ||= eggState && autoHatchEgg();
success ||= fossilState && autoHatchFossil();
+
+ // Finally, standard list (respects Filter Only toggle)
success ||= autoHatchMon();
- if (!success) {
- break;
- }
+
+ if (!success) break;
}
}
@@ -158,11 +257,9 @@ function autoHatchPkrs() {
if (!App.game.keyItems.hasKeyItem(KeyItemType.Pokerus_virus)) {
return false;
}
- // No need to search if we already know there aren't party members to infect
if (numMonsWithPkrsCached == App.game.party.caughtPokemon.length) {
return false;
}
- // If we couldn't find a uninfected/contagious pair, wait a while before trying again
if (Date.now() - pkrsHatcherySearchTime < delayAfterFailure) {
return false;
}
@@ -171,8 +268,6 @@ function autoHatchPkrs() {
let contagious = {};
let foundPair = false;
let infectedCount = 0;
- // Find first uninfected/contagious pair sharing a type
- // Ideally the uninfected mon is dual-type to accelerate future spreading
for (let mon of App.game.party.caughtPokemon) {
infectedCount += mon.pokerus > GameConstants.Pokerus.Uninfected;
if (mon.breeding || mon.level < 100) {
@@ -194,21 +289,17 @@ function autoHatchPkrs() {
checkMatch = true;
}
}
- // Stop searching upon finding a infectable dual-type
if (checkMatch) {
for (let type of types) {
if (type in uninfectedDual && type in contagious) {
foundPair = {'uninfected': uninfectedDual[type], 'contagious': contagious[type]};
}
}
- if (foundPair) {
- break;
- }
+ if (foundPair) break;
}
}
if (!foundPair) {
numMonsWithPkrsCached = infectedCount;
- // No infectable dual-type pokemon found, try a monotype
for (let type of GameHelper.enumNumbers(PokemonType)) {
if (type in uninfectedMono && type in contagious) {
foundPair = {'uninfected': uninfectedMono[type], 'contagious': contagious[type]};
@@ -236,12 +327,11 @@ function autoHatchEgg() {
}
function autoHatchFossil() {
- // Fossils in inventory with amount > 0
let fossilList = UndergroundItems.list.filter(it => it.valueType === UndergroundItemValueType.Fossil && player.itemList[it.itemName]() > 0);
if (fossilList.length == 0) {
return false;
}
- let priorityList = fossilList.filter(f => {
+ let priorityList = fossilList.filter(f => {
const caughtStatus = PartyController.getCaughtStatusByName(GameConstants.FossilToPokemon[f.name]);
return caughtStatus == CaughtStatus.NotCaught || (shinyFossilState && caughtStatus == CaughtStatus.Caught);
});
@@ -249,22 +339,23 @@ function autoHatchFossil() {
fossilList = priorityList;
}
let fossilToUse = fossilList[Math.floor(Math.random() * fossilList.length)];
- // Workaround as sellMineItem returns null
let before = player.amountOfItem(fossilToUse.itemName)
UndergroundController.sellMineItem(fossilToUse);
let after = player.amountOfItem(fossilToUse.itemName);
return before > after;
}
+// Respects Filter Only toggle
function autoHatchMon() {
let toHatch = autoHatcheryCachedList.find(p => p.isHatchable());
- if (!toHatch) {
- // Nothing matches the hatchery filters
- toHatch = App.game.party.caughtPokemon.find(p => p.isHatchable());
+ if (filterOnlyState) {
+ if (!toHatch) return false;
+ return App.game.breeding.addPokemonToHatchery(toHatch);
}
if (!toHatch) {
- return false;
+ toHatch = App.game.party.caughtPokemon.find(p => p.isHatchable());
}
+ if (!toHatch) return false;
return App.game.breeding.addPokemonToHatchery(toHatch);
}
@@ -273,6 +364,14 @@ eggState = loadSetting('autoEgg', false);
fossilState = loadSetting('autoFossil', false);
shinyFossilState = loadSetting('shinyFossil', false);
pkrsState = loadSetting('pokerusModeState', false);
+filterOnlyState = loadSetting('filterOnly', false);
+
+// Load priority list once at boot (UI will re-render it)
+try {
+ const raw = localStorage.getItem('priorityMonIds') || '[]';
+ const arr = JSON.parse(raw);
+ priorityIdList = Array.isArray(arr) ? arr.map(n => Number(n)).filter(n => Number.isFinite(n) && n > 0) : [];
+} catch { priorityIdList = []; }
function loadSetting(key, defaultVal) {
var val;
@@ -309,17 +408,14 @@ function loadEpheniaScript(scriptName, initFunction, priorityFunction) {
});
}
const windowObject = !App.isUsingClient ? unsafeWindow : window;
- // Inject handlers if they don't exist yet
if (windowObject.epheniaScriptInitializers === undefined) {
windowObject.epheniaScriptInitializers = {};
const oldInit = Preload.hideSplashScreen;
var hasInitialized = false;
- // Initializes scripts once enough of the game has loaded
Preload.hideSplashScreen = function (...args) {
var result = oldInit.apply(this, args);
if (App.game && !hasInitialized) {
- // Initialize all attached userscripts
Object.entries(windowObject.epheniaScriptInitializers).forEach(([scriptName, initFunction]) => {
try {
initFunction();
@@ -333,14 +429,13 @@ function loadEpheniaScript(scriptName, initFunction, priorityFunction) {
}
}
- // Prevent issues with duplicate script names
if (windowObject.epheniaScriptInitializers[scriptName] !== undefined) {
console.warn(`Duplicate '${scriptName}' userscripts found!`);
Notifier.notify({
type: NotificationConstants.NotificationOption.warning,
title: scriptName,
message: `Duplicate '${scriptName}' userscripts detected. This could cause unpredictable behavior and is not recommended.`,
- timeout: GameConstants.DAY,
+ timeout: GameConstants.DAY,
});
let number = 2;
while (windowObject.epheniaScriptInitializers[`${scriptName} ${number}`] !== undefined) {
@@ -348,16 +443,13 @@ function loadEpheniaScript(scriptName, initFunction, priorityFunction) {
}
scriptName = `${scriptName} ${number}`;
}
- // Add initializer for this particular script
windowObject.epheniaScriptInitializers[scriptName] = initFunction;
- // Run any functions that need to execute before the game starts
if (priorityFunction) {
$(document).ready(() => {
try {
priorityFunction();
} catch (e) {
reportScriptError(scriptName, e);
- // Remove main initialization function
windowObject.epheniaScriptInitializers[scriptName] = () => null;
}
});
From 6ce1961a32766283a387fae78f755c0deeab1512 Mon Sep 17 00:00:00 2001
From: goldhaxx <1616671+goldhaxx@users.noreply.github.com>
Date: Sat, 20 Sep 2025 15:57:26 -0700
Subject: [PATCH 2/2] Enhance Auto Hatchery to support decimal Priority IDs
Updated the description to reflect the new functionality of supporting decimal IDs in the Priority ID queue. Modified the input field to accept decimal values and adjusted the parsing logic to handle both integer and decimal IDs. This allows for better management of alternate forms in the hatchery queue, improving the overall user experience and flexibility of the script.
---
enhancedautohatchery.user.js | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/enhancedautohatchery.user.js b/enhancedautohatchery.user.js
index ac5d8d7..2e8c7cc 100644
--- a/enhancedautohatchery.user.js
+++ b/enhancedautohatchery.user.js
@@ -2,7 +2,7 @@
// @name [Pokeclicker] Enhanced Auto Hatchery
// @namespace Pokeclicker Scripts
// @author Ephenia (Original/Credit: Drak + Ivan Lay, Optimatum)
-// @description Auto-hatches eggs at 100% and adds controls: Auto Hatch, PKRS Mode, Auto Egg, Auto Fossil, Shiny Fossils, Filter Only, and a Priority ID queue (e.g., 149,123,552).
+// @description Auto-hatches eggs at 100% and adds controls: Auto Hatch, PKRS Mode, Auto Egg, Auto Fossil, Shiny Fossils, Filter Only, and a Priority ID queue (e.g., 149,123,552,479.01) supporting alternate forms.
// @copyright https://github.com/Ephenia
// @license GPL-3.0 License
// @version 3.3.0-priority
@@ -62,7 +62,7 @@ function initAutoHatch() {
-
+
Save Priority
@@ -170,9 +170,9 @@ function renderPriorityUiFromStorage() {
function savePriorityFromInput() {
const input = document.getElementById('priority-ids');
const status = document.getElementById('priority-status');
- // Accept comma/space separated, tolerate extra chars
- const parts = input.value.split(/[^0-9]+/).filter(Boolean);
- const parsed = parts.map(n => Number(n)).filter(n => Number.isFinite(n) && n > 0);
+ // Accept comma/space separated, tolerate extra chars - now supports decimal IDs
+ const parts = input.value.split(/[^0-9.]+/).filter(Boolean);
+ const parsed = parts.map(n => Number(n)).filter(n => Number.isFinite(n) && n > 0 && !isNaN(n));
// De-duplicate while preserving order
const seen = new Set();
priorityIdList = parsed.filter(n => (seen.has(n) ? false : (seen.add(n), true)));
@@ -195,11 +195,21 @@ function autoHatchPriority() {
// We'll scan per priority to keep order strict.
for (let id of priorityIdList) {
// Find a hatchable party member whose dex ID matches
+ // Support both integer IDs (479) and decimal IDs (479.01) for alternate forms
const mon = App.game.party.caughtPokemon.find(p => {
if (!p || p.breeding || !p.isHatchable()) return false;
// pokemonMap[p.name].id is available elsewhere in script (type was used earlier)
const data = pokemonMap[p.name];
- return data && data.id === id;
+ if (!data) return false;
+
+ // Exact match for decimal IDs (e.g., 479.01)
+ if (data.id === id) return true;
+
+ // For integer IDs, match both base forms and alternate forms
+ // This allows 479 to match both 479 (base) and 479.01, 479.02, etc. (alternates)
+ if (Number.isInteger(id) && Math.floor(data.id) === id) return true;
+
+ return false;
});
if (mon) {
return App.game.breeding.addPokemonToHatchery(mon);