diff --git a/enhancedautohatchery.user.js b/enhancedautohatchery.user.js
index 81f4872..2e8c7cc 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,479.01) supporting alternate forms.
// @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 += `
+
+
+
+
+
+
+
+ `;
+
+ 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,84 @@ 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 - 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)));
+
+ 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
+ // 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];
+ 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);
+ }
+ }
+ return false;
+}
+
+// --------------------------------------------------------
+
function bindAutoHatcher() {
const progressEggsOld = Breeding.prototype.progressEggs;
Breeding.prototype.progressEggs = function progressEggs(...args) {
@@ -141,15 +247,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 +267,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 +278,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 +299,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 +337,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 +349,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 +374,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 +418,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 +439,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 +453,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;
}
});