diff --git a/enhancedautoclicker.user.js b/enhancedautoclicker.user.js
index bef3134..da43817 100644
--- a/enhancedautoclicker.user.js
+++ b/enhancedautoclicker.user.js
@@ -1,7 +1,7 @@
// ==UserScript==
// @name [Pokeclicker] Enhanced Auto Clicker
// @namespace Pokeclicker Scripts
-// @author Optimatum (Original/Credit: Ephenia, Ivan Lay, Novie53, andrew951, Kaias26, kevingrillet, MrPandaa)
+// @author Optimatum (Original/Credit: Ephenia, Ivan Lay, Novie53, andrew951, Kaias26, kevingrillet, MrPandaa, VincentPS)
// @description Clicks through battles, with adjustable speed, and provides various insightful statistics. Also includes an automatic gym battler and automatic dungeon explorer with multiple pathfinding modes.
// @copyright https://github.com/Ephenia
// @license GPL-3.0 License
@@ -19,172 +19,133 @@
// ==/UserScript==
class EnhancedAutoClicker {
- // Constants
- static ticksPerSecond = 20;
- static maxClickMultiplier = 5;
- // Auto Clicker
- static autoClickState = ko.observable(
- validateStorage('autoClickState', false)
- );
- static autoClickMultiplier = validateStorage(
- 'autoClickMultiplier',
- 1,
- (v) => Number.isInteger(v) && v >= 1
- );
- static autoClickerLoop;
- // Auto Gym
- static autoGymState = ko.observable(validateStorage('autoGymState', false));
- static autoGymSelect = validateStorage('autoGymSelect', 0, [0, 1, 2, 3, 4]);
- // Auto Dungeon
- static autoDungeonState = ko.observable(
- validateStorage('autoDungeonState', false)
- );
- static autoDungeonFinishBeforeStopping = validateStorage(
- 'autoDungeonFinishBeforeStopping',
- true
- );
- static autoDungeonEncounterMode = validateStorage(
- 'autoDungeonEncounterMode',
- false
- );
- static autoDungeonChestMode = validateStorage('autoDungeonChestMode', false);
- static autoDungeonLootTier = validateStorage('autoDungeonLootTier', 0, [
- ...Object.keys(baseLootTierChance).keys(),
- ]);
- static autoDungeonAlwaysOpenRareChests = validateStorage(
- 'autoDungeonAlwaysOpenRareChests',
- false
- );
- static autoDungeonTracker = {
- ID: 0,
- floor: null,
- floorSize: null,
- flashTier: null,
- flashCols: null,
- coords: null,
- bossCoords: null,
- encounterCoords: null,
- chestCoords: null,
- floorExplored: false,
- floorFinished: false,
- dungeonFinished: false,
- stopAfterFinishing: false,
- };
- //auto Achievement
- static autoAchievementState = ko.observable(
- validateStorage('autoAchievementState', false)
- );
- static autoAchievementSelect = ko.observable(
- validateStorage('autoAchievementSelect', 0, [0, 1, 2])
- );
-
- // Clicker statistics calculator
- static autoClickCalcLoop;
- static autoClickCalcEfficiencyDisplayMode = validateStorage(
- 'autoClickCalcEfficiencyDisplayMode',
- 0,
- [0, 1]
- );
- static autoClickCalcDamageDisplayMode = validateStorage(
- 'autoClickCalcDamageDisplayMode',
- 0,
- [0, 1]
- );
- static autoClickCalcTracker = {
- lastUpdate: null,
- playerState: -1,
- playerLocation: null,
- ticks: null,
- clicks: null,
- enemies: null,
- areaHealth: null,
- };
- // Visual settings
- static gymGraphicsDisabled = ko.observable(
- validateStorage('gymGraphicsDisabled', false)
- );
- static dungeonGraphicsDisabled = ko.observable(
- validateStorage('dungeonGraphicsDisabled', false)
- );
- // Computed observables for visual settings
- static autoGymOn = ko.pureComputed(() => {
- return this.autoClickState() && this.autoGymState();
- });
- static disableAutoGymGraphics = ko.pureComputed(() => {
- return this.gymGraphicsDisabled() && this.autoGymOn();
- });
- static disableAutoDungeonGraphics = ko.pureComputed(() => {
- return (
- this.dungeonGraphicsDisabled() &&
- this.autoClickState() &&
- this.autoDungeonState()
- );
- });
-
- /* Initialization */
-
- static initOverrides() {
- // Add data bindings immediately, before the game initializes Knockout
- this.addGraphicsBindings();
-
- // Override static class methods
- this.overrideGymRunner();
- this.overrideDungeonRunner();
- }
-
- static toggleAutoAchievement(event) {
- const element = event.target;
- this.autoAchievementState(!this.autoAchievementState());
- this.autoAchievementState()
- ? element.classList.replace('btn-danger', 'btn-success')
- : element.classList.replace('btn-success', 'btn-danger');
- element.textContent = `Auto Achievement [${
- this.autoAchievementState() ? 'ON' : 'OFF'
- }]`;
- localStorage.setItem('autoAchievementState', this.autoAchievementState());
-
- // Deactivate AutoGym and AutoDungeon that could have been triggered
- if (this.autoGymState() === true) {
- document.getElementById('auto-gym-start').click();
- }
+ // Constants
+ static ticksPerSecond = 20;
+ static maxClickMultiplier = 5;
+ // Auto Clicker
+ static autoClickState = ko.observable(validateStorage('autoClickState', false));
+ static autoClickMultiplier = validateStorage('autoClickMultiplier', 1, (v) => (Number.isInteger(v) && v >= 1));
+ static autoClickerLoop;
+ // Auto Gym
+ static autoGymState = ko.observable(validateStorage('autoGymState', false));
+ static autoGymSelect = validateStorage('autoGymSelect', 0, [0, 1, 2, 3, 4]);
+ // Auto Dungeon
+ static autoDungeonState = ko.observable(validateStorage('autoDungeonState', false));
+ static autoDungeonFinishBeforeStopping = validateStorage('autoDungeonFinishBeforeStopping', true);
+ static autoDungeonEncounterMode = validateStorage('autoDungeonEncounterMode', false);
+ static autoDungeonChestMode = validateStorage('autoDungeonChestMode', false);
+ static autoDungeonLootTier = validateStorage('autoDungeonLootTier', 0, [...Object.keys(baseLootTierChance).keys()]);
+ static autoDungeonAlwaysOpenRareChests = validateStorage('autoDungeonAlwaysOpenRareChests', false);
+ static autoDungeonTracker = {
+ ID: 0,
+ floor: null,
+ floorSize: null,
+ flashTier: null,
+ flashCols: null,
+ coords: null,
+ bossCoords: null,
+ encounterCoords: null,
+ chestCoords: null,
+ floorExplored: false,
+ floorFinished: false,
+ dungeonFinished: false,
+ stopAfterFinishing: false,
+ };
- if (this.autoDungeonState() === true) {
- document.getElementById('auto-dungeon-start').click();
- }
- }
+ //auto Achievement
+ static autoAchievementSelectTypes = {Routes: 0, Gyms: 1, Dungeons: 2};
+ static autoAchievementState = ko.observable(validateStorage('autoAchievementState', false));
+ static autoAchievementSelect = ko.observable(validateStorage('autoAchievementSelect', 0, [...Object.keys(this.autoAchievementSelectTypes)]));
+
+ // Clicker statistics calculator
+ static autoClickCalcLoop;
+ static autoClickCalcEfficiencyDisplayMode = validateStorage('autoClickCalcEfficiencyDisplayMode', 0, [0, 1]);
+ static autoClickCalcDamageDisplayMode = validateStorage('autoClickCalcDamageDisplayMode', 0, [0, 1]);
+ static autoClickCalcTracker = {
+ lastUpdate: null,
+ playerState: -1,
+ playerLocation: null,
+ ticks: null,
+ clicks: null,
+ enemies: null,
+ areaHealth: null,
+ };
+ // Visual settings
+ static gymGraphicsDisabled = ko.observable(validateStorage('gymGraphicsDisabled', false));
+ static dungeonGraphicsDisabled = ko.observable(validateStorage('dungeonGraphicsDisabled', false));
+ // Computed observables for visual settings
+ static autoGymOn = ko.pureComputed(() => {
+ return this.autoClickState() && this.autoGymState();
+ });
+ static disableAutoGymGraphics = ko.pureComputed(() => {
+ return this.gymGraphicsDisabled() && this.autoGymOn();
+ });
+ static disableAutoDungeonGraphics = ko.pureComputed(() => {
+ return this.dungeonGraphicsDisabled() && this.autoClickState() && this.autoDungeonState();
+ });
+
+ /* Initialization */
+
+ static initOverrides() {
+ // Add data bindings immediately, before the game initializes Knockout
+ this.addGraphicsBindings();
- static changeSelectedAchievement(event) {
- // Deactivate AutoGym and AutoDungeon that could have been triggered
- if (this.autoGymState() === true) {
- document.getElementById('auto-gym-start').click();
+ // Override static class methods
+ this.overrideGymRunner();
+ this.overrideDungeonRunner();
}
- if (this.autoDungeonState() === true) {
- document.getElementById('auto-dungeon-start').click();
+ static toggleAutoAchievement(event) {
+ const element = event.target;
+ this.autoAchievementState(!this.autoAchievementState());
+ this.autoAchievementState()
+ ? element.classList.replace('btn-danger', 'btn-success')
+ : element.classList.replace('btn-success', 'btn-danger');
+ element.textContent = `Auto Achievement [${
+ this.autoAchievementState() ? 'ON' : 'OFF'
+ }]`;
+ localStorage.setItem('autoAchievementState', this.autoAchievementState());
+
+ // Deactivate AutoGym and AutoDungeon that could have been triggered
+ if (this.autoGymState() === true) {
+ document.getElementById('auto-gym-start').click();
+ }
+
+ if (this.autoDungeonState() === true) {
+ document.getElementById('auto-dungeon-start').click();
+ }
}
- const element = event.target;
- if (this.autoAchievementSelect() != +element.value) {
- this.autoAchievementSelect(+element.value);
- localStorage.setItem(
- 'autoAchievementSelect',
- this.autoAchievementSelect()
- );
- document.getElementById('auto-achievement-start').click();
- document.getElementById('auto-achievement-start').click();
+ static changeSelectedAchievement(event) {
+ // Deactivate AutoGym and AutoDungeon that could have been triggered
+ if (this.autoGymState() === true) {
+ document.getElementById('auto-gym-start').click();
+ }
+
+ if (this.autoDungeonState() === true) {
+ document.getElementById('auto-dungeon-start').click();
+ }
+
+ const element = event.target;
+ if (this.autoAchievementSelect() != +element.value) {
+ this.autoAchievementSelect(+element.value);
+ localStorage.setItem(
+ 'autoAchievementSelect',
+ this.autoAchievementSelect()
+ );
+ document.getElementById('auto-achievement-start').click();
+ document.getElementById('auto-achievement-start').click();
+ }
}
- }
- static initAutoClicker() {
- const battleView = document.getElementsByClassName('battle-view')[0];
+ static initAutoClicker() {
+ const battleView = document.getElementsByClassName('battle-view')[0];
- var elemAC = document.createElement('table');
- elemAC.innerHTML = `
+ var elemAC = document.createElement("table");
+ elemAC.innerHTML = `
|
- |
|
-
+
Auto Dungeon [${this.autoDungeonState() ? 'ON' : 'OFF'}]
- 
+
- 
+
-
+
-
+
Auto Gym [${this.autoGymState() ? 'ON' : 'OFF'}]
- |
`;
- battleView.before(elemAC);
- this.resetCalculator(); // initializes calculator display
-
- const settingsBody = createScriptSettingsContainer('Enhanced Auto Clicker');
- const settingsToAdd = [];
-
- // Dropdowns
- let dropdownsToAdd = [
- [
- 'autoClickCalcEfficiencyDisplayMode',
- 'Auto Clicker efficiency display mode',
- this.autoClickCalcEfficiencyDisplayMode,
- ['Percentage', 'Ticks/s'],
- ],
- [
- 'autoClickCalcDamageDisplayMode',
- 'Auto Clicker damage display mode',
- this.autoClickCalcDamageDisplayMode,
- ['Click Attacks', 'Damage'],
- ],
- ];
- dropdownsToAdd.forEach(([name, text, value, options]) => {
- const newSetting = document.createElement('tr');
- settingsToAdd.push(newSetting);
- newSetting.innerHTML = `
+ battleView.before(elemAC);
+ this.resetCalculator(); // initializes calculator display
+
+ const settingsBody = createScriptSettingsContainer('Enhanced Auto Clicker');
+ const settingsToAdd = [];
+
+ // Dropdowns
+ let dropdownsToAdd = [
+ ['autoClickCalcEfficiencyDisplayMode', 'Auto Clicker efficiency display mode', this.autoClickCalcEfficiencyDisplayMode, ['Percentage', 'Ticks/s']],
+ ['autoClickCalcDamageDisplayMode', 'Auto Clicker damage display mode', this.autoClickCalcDamageDisplayMode, ['Click Attacks', 'Damage']]
+ ];
+ dropdownsToAdd.forEach(([name, text, value, options]) => {
+ const newSetting = document.createElement('tr')
+ settingsToAdd.push(newSetting);
+ newSetting.innerHTML = ` |
${text}
|
- ${options
- .map((desc, val) => ``)
- .join('\n')}
+ ${options.map((desc, val) => ``).join('\n')}
| `;
- });
- // Checkboxes
- let checkboxesToAdd = [
- [
- 'autoDungeonFinishBeforeStopping',
- 'Auto Dungeon finishes dungeons before turning off',
- this.autoDungeonFinishBeforeStopping,
- ],
- [
- 'autoDungeonAlwaysOpenRareChests',
- 'Always open visible targeted chests',
- this.autoDungeonAlwaysOpenRareChests,
- ],
- [
- 'autoGymGraphicsDisabled',
- 'Disable Auto Gym graphics',
- this.gymGraphicsDisabled(),
- ],
- [
- 'autoDungeonGraphicsDisabled',
- 'Disable Auto Dungeon graphics',
- this.dungeonGraphicsDisabled(),
- ],
- ];
- checkboxesToAdd.forEach(([name, text, isChecked]) => {
- const newSetting = document.createElement('tr');
- settingsToAdd.push(newSetting);
- newSetting.innerHTML = `
+ });
+ // Checkboxes
+ let checkboxesToAdd = [
+ ['autoDungeonFinishBeforeStopping', 'Auto Dungeon finishes dungeons before turning off', this.autoDungeonFinishBeforeStopping],
+ ['autoDungeonAlwaysOpenRareChests', 'Always open visible targeted chests', this.autoDungeonAlwaysOpenRareChests],
+ ['autoGymGraphicsDisabled', 'Disable Auto Gym graphics', this.gymGraphicsDisabled()],
+ ['autoDungeonGraphicsDisabled', 'Disable Auto Dungeon graphics', this.dungeonGraphicsDisabled()]
+ ];
+ checkboxesToAdd.forEach(([name, text, isChecked]) => {
+ const newSetting = document.createElement('tr')
+ settingsToAdd.push(newSetting);
+ newSetting.innerHTML = ` |
|
| `;
- newSetting.querySelector(`#checkbox-${name}`).checked = isChecked;
- });
+ newSetting.querySelector(`#checkbox-${name}`).checked = isChecked;
+ });
- settingsBody.append(...settingsToAdd);
-
- document.getElementById('achievement-select').value =
- this.autoAchievementSelect();
- document
- .getElementById('auto-achievement-start')
- .addEventListener('click', (event) => {
- this.toggleAutoAchievement(event);
- });
- document
- .getElementById('achievement-select')
- .addEventListener('change', (event) => {
- this.changeSelectedAchievement(event);
- });
-
- document.getElementById('auto-gym-select').value = this.autoGymSelect;
-
- document
- .getElementById('auto-click-start')
- .addEventListener('click', () => {
- EnhancedAutoClicker.toggleAutoClick();
- });
- document
- .getElementById('auto-click-rate')
- .addEventListener('change', (event) => {
- EnhancedAutoClicker.changeClickMultiplier(event);
- });
- document.getElementById('auto-gym-start').addEventListener('click', () => {
- EnhancedAutoClicker.toggleAutoGym();
- });
- document
- .getElementById('auto-gym-select')
- .addEventListener('change', (event) => {
- EnhancedAutoClicker.changeSelectedGym(event);
- });
- document
- .getElementById('auto-dungeon-start')
- .addEventListener('click', () => {
- EnhancedAutoClicker.toggleAutoDungeon(true);
- });
- document
- .getElementById('auto-dungeon-encounter-mode')
- .addEventListener('click', () => {
- EnhancedAutoClicker.toggleAutoDungeonEncounterMode();
- });
- document
- .getElementById('auto-dungeon-chest-mode')
- .addEventListener('click', () => {
- EnhancedAutoClicker.toggleAutoDungeonChestMode();
- });
- document
- .getElementById('checkbox-autoDungeonFinishBeforeStopping')
- .addEventListener('change', () => {
- EnhancedAutoClicker.toggleAutoDungeonFinishBeforeStopping();
- });
- document
- .getElementById('checkbox-autoDungeonAlwaysOpenRareChests')
- .addEventListener('change', () => {
- EnhancedAutoClicker.toggleAutoDungeonAlwaysOpenRareChests();
- });
- document
- .getElementById('checkbox-autoGymGraphicsDisabled')
- .addEventListener('change', () => {
- EnhancedAutoClicker.toggleAutoGymGraphics();
- });
- document
- .getElementById('checkbox-autoDungeonGraphicsDisabled')
- .addEventListener('change', () => {
- EnhancedAutoClicker.toggleAutoDungeonGraphics();
- });
- document
- .getElementById('select-autoClickCalcEfficiencyDisplayMode')
- .addEventListener('change', (event) => {
- EnhancedAutoClicker.changeCalcEfficiencyDisplayMode(event);
- });
- document
- .getElementById('select-autoClickCalcDamageDisplayMode')
- .addEventListener('change', (event) => {
- EnhancedAutoClicker.changeCalcDamageDisplayMode(event);
- });
-
- document
- .querySelectorAll('#auto-dungeon-loottier-dropdown > div')
- .forEach((elem) => {
- elem.addEventListener('click', () => {
- EnhancedAutoClicker.changeAutoDungeonLootTier(
- elem.getAttribute('value')
- );
+ settingsBody.append(...settingsToAdd);
+
+ document.getElementById('achievement-select').value = this.autoAchievementSelect();
+ document.getElementById('auto-achievement-start').addEventListener('click', (event) => {this.toggleAutoAchievement(event);});
+ document.getElementById('achievement-select').addEventListener('change', (event) => {this.changeSelectedAchievement(event);});
+ document.getElementById('auto-gym-select').value = this.autoGymSelect;
+ document.getElementById('auto-click-start').addEventListener('click', () => { EnhancedAutoClicker.toggleAutoClick(); });
+ document.getElementById('auto-click-rate').addEventListener('change', (event) => { EnhancedAutoClicker.changeClickMultiplier(event); });
+ document.getElementById('auto-gym-start').addEventListener('click', () => { EnhancedAutoClicker.toggleAutoGym(); });
+ document.getElementById('auto-gym-select').addEventListener('change', (event) => { EnhancedAutoClicker.changeSelectedGym(event); });
+ document.getElementById('auto-dungeon-start').addEventListener('click', () => { EnhancedAutoClicker.toggleAutoDungeon(true); });
+ document.getElementById('auto-dungeon-encounter-mode').addEventListener('click', () => { EnhancedAutoClicker.toggleAutoDungeonEncounterMode(); });
+ document.getElementById('auto-dungeon-chest-mode').addEventListener('click', () => { EnhancedAutoClicker.toggleAutoDungeonChestMode(); });
+ document.getElementById('checkbox-autoDungeonFinishBeforeStopping').addEventListener('change', () => { EnhancedAutoClicker.toggleAutoDungeonFinishBeforeStopping(); });
+ document.getElementById('checkbox-autoDungeonAlwaysOpenRareChests').addEventListener('change', () => { EnhancedAutoClicker.toggleAutoDungeonAlwaysOpenRareChests(); });
+ document.getElementById('checkbox-autoGymGraphicsDisabled').addEventListener('change', () => { EnhancedAutoClicker.toggleAutoGymGraphics(); });
+ document.getElementById('checkbox-autoDungeonGraphicsDisabled').addEventListener('change', () => { EnhancedAutoClicker.toggleAutoDungeonGraphics(); });
+ document.getElementById('select-autoClickCalcEfficiencyDisplayMode').addEventListener('change', (event) => { EnhancedAutoClicker.changeCalcEfficiencyDisplayMode(event); });
+ document.getElementById('select-autoClickCalcDamageDisplayMode').addEventListener('change', (event) => { EnhancedAutoClicker.changeCalcDamageDisplayMode(event); });
+
+ document.querySelectorAll('#auto-dungeon-loottier-dropdown > div').forEach((elem) => {
+ elem.addEventListener('click', () => { EnhancedAutoClicker.changeAutoDungeonLootTier(elem.getAttribute('value')); });
});
- });
-
- addGlobalStyle(
- '#auto-click-info { display: flex; flex-direction: row; justify-content: center; }'
- );
- addGlobalStyle('#auto-click-info > div { width: 33.3%; }');
- addGlobalStyle(
- '#click-rate-cont { display: flex; flex-direction: column; align-items: stretch; }'
- );
- addGlobalStyle(
- '#auto-dungeon-loottier-dropdown img { max-height: 30px; width: auto; }'
- );
-
- if (this.autoClickState()) {
- this.toggleAutoClickerLoop();
- }
- }
-
- /**
- * Add extra Knockout data bindings to optionally disable (most) gym and dungeon graphics
- */
- static addGraphicsBindings() {
- // Add gymView data bindings
- var gymContainer = document.querySelector(
- 'div[data-bind="if: App.game.gameState === GameConstants.GameState.gym"]'
- );
- var elemsToBind = [
- 'knockout[data-bind*="pokemonNameTemplate"]', // Pokemon name
- 'span[data-bind*="pokemonsDefeatedComputable"]', // Gym Pokemon counter (pt 1)
- 'span[data-bind*="pokemonsUndefeatedComputable"]', // Gym Pokemon counter (pt 2)
- 'knockout[data-bind*="pokemonSpriteTemplate"]', // Pokemon sprite
- 'div.progress.hitpoints', // Pokemon healthbar
- 'div.progress.timer', // Gym timer
- ];
- elemsToBind.forEach((query) => {
- var elem = gymContainer.querySelector(query);
- if (elem) {
- elem.before(
- new Comment('ko ifnot: EnhancedAutoClicker.disableAutoGymGraphics()')
- );
- elem.after(new Comment('/ko'));
- }
- });
- // Always hide stop button during autoGym, even with graphics enabled
- var restartButton = gymContainer.querySelector(
- 'button[data-bind="visible: GymRunner.autoRestart()"]'
- );
- restartButton.setAttribute(
- 'data-bind',
- 'visible: GymRunner.autoRestart() && !EnhancedAutoClicker.autoGymOn()'
- );
-
- // Add dungeonView data bindings
- var dungeonContainer = document.querySelector(
- 'div[data-bind="if: App.game.gameState === GameConstants.GameState.dungeon"]'
- );
- // Title bar contents
- dungeonContainer
- .querySelector('h2.pageItemTitle')
- ?.prepend(
- new Comment(
- 'ko ifnot: EnhancedAutoClicker.disableAutoDungeonGraphics()'
- )
- );
- dungeonContainer
- .querySelector('h2.pageItemTitle')
- ?.append(new Comment('/ko'));
- // Main container sprites etc
- dungeonContainer
- .querySelector('h2.pageItemTitle')
- ?.after(
- new Comment(
- 'ko ifnot: EnhancedAutoClicker.disableAutoDungeonGraphics()'
- )
- );
- dungeonContainer
- .querySelector('h2.pageItemFooter')
- ?.before(new Comment('/ko'));
- }
-
- /* Settings event handlers */
-
- static toggleAutoClick() {
- const element = document.getElementById('auto-click-start');
- this.autoClickState(!this.autoClickState());
- localStorage.setItem('autoClickState', this.autoClickState());
- this.autoClickState()
- ? element.classList.replace('btn-danger', 'btn-success')
- : element.classList.replace('btn-success', 'btn-danger');
- if (!this.autoClickState()) {
- if (this.autoGymState()) {
- this.toggleAutoGym();
- }
- if (this.autoDungeonState()) {
- this.toggleAutoDungeon();
- }
+
+ addGlobalStyle('#auto-click-info { display: flex; flex-direction: row; justify-content: center; }');
+ addGlobalStyle('#auto-click-info > div { width: 33.3%; }');
+ addGlobalStyle('#click-rate-cont { display: flex; flex-direction: column; align-items: stretch; }');
+ addGlobalStyle('#auto-dungeon-loottier-dropdown img { max-height: 30px; width: auto; }');
+
+ if (this.autoClickState()) {
+ this.toggleAutoClickerLoop();
+ }
}
- this.toggleAutoClickerLoop();
- }
-
- static changeClickMultiplier(event) {
- // TODO decide on a better range / function
- const multiplier = +event.target.value;
- if (Number.isInteger(multiplier) && multiplier > 0) {
- this.autoClickMultiplier = multiplier;
- localStorage.setItem('autoClickMultiplier', this.autoClickMultiplier);
- var displayNum = (
- this.ticksPerSecond * this.autoClickMultiplier
- ).toLocaleString('en-US', { maximumFractionDigits: 2 });
- document.getElementById(
- 'auto-click-rate-info'
- ).innerText = `Click Attack Rate: ${displayNum}/s`;
- this.toggleAutoClickerLoop();
+ /**
+ * Add extra Knockout data bindings to optionally disable (most) gym and dungeon graphics
+ */
+ static addGraphicsBindings() {
+ // Add gymView data bindings
+ var gymContainer = document.querySelector('div[data-bind="if: App.game.gameState === GameConstants.GameState.gym"]');
+ var elemsToBind = ['knockout[data-bind*="pokemonNameTemplate"]', // Pokemon name
+ 'span[data-bind*="pokemonsDefeatedComputable"]', // Gym Pokemon counter (pt 1)
+ 'span[data-bind*="pokemonsUndefeatedComputable"]', // Gym Pokemon counter (pt 2)
+ 'knockout[data-bind*="pokemonSpriteTemplate"]', // Pokemon sprite
+ 'div.progress.hitpoints', // Pokemon healthbar
+ 'div.progress.timer' // Gym timer
+ ];
+ elemsToBind.forEach((query) => {
+ var elem = gymContainer.querySelector(query);
+ if (elem) {
+ elem.before(new Comment("ko ifnot: EnhancedAutoClicker.disableAutoGymGraphics()"));
+ elem.after(new Comment("/ko"));
+ }
+ });
+ // Always hide stop button during autoGym, even with graphics enabled
+ var restartButton = gymContainer.querySelector('button[data-bind="visible: GymRunner.autoRestart()"]');
+ restartButton.setAttribute('data-bind', 'visible: GymRunner.autoRestart() && !EnhancedAutoClicker.autoGymOn()');
+
+ // Add dungeonView data bindings
+ var dungeonContainer = document.querySelector('div[data-bind="if: App.game.gameState === GameConstants.GameState.dungeon"]');
+ // Title bar contents
+ dungeonContainer.querySelector('h2.pageItemTitle')?.prepend(new Comment("ko ifnot: EnhancedAutoClicker.disableAutoDungeonGraphics()"));
+ dungeonContainer.querySelector('h2.pageItemTitle')?.append(new Comment("/ko"));
+ // Main container sprites etc
+ dungeonContainer.querySelector('h2.pageItemTitle')?.after(new Comment("ko ifnot: EnhancedAutoClicker.disableAutoDungeonGraphics()"));
+ dungeonContainer.querySelector('h2.pageItemFooter')?.before(new Comment("/ko"));
}
- }
-
- static toggleAutoGym() {
- const element = document.getElementById('auto-gym-start');
- const newState = !this.autoGymState();
- if (newState && !this.canStartAutoGym()) {
- // Don't turn on if there's no gym here
- return;
- } else if (newState && this.autoDungeonState()) {
- // Only one mode may be active at a time
- return;
+
+ /* Settings event handlers */
+
+ static toggleAutoClick() {
+ const element = document.getElementById('auto-click-start');
+ this.autoClickState(!this.autoClickState());
+ localStorage.setItem('autoClickState', this.autoClickState());
+ this.autoClickState() ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger');
+ if (!this.autoClickState()) {
+ if (this.autoGymState()) {
+ this.toggleAutoGym();
+ }
+ if (this.autoDungeonState()) {
+ this.toggleAutoDungeon();
+ }
+ }
+
+ this.toggleAutoClickerLoop();
}
- this.autoGymState(newState);
- localStorage.setItem('autoGymState', this.autoGymState());
- this.autoGymState()
- ? element.classList.replace('btn-danger', 'btn-success')
- : element.classList.replace('btn-success', 'btn-danger');
- element.textContent = `Auto Gym [${this.autoGymState() ? 'ON' : 'OFF'}]`;
- if (this.autoGymState()) {
- if (!this.autoClickState()) {
- // Turn on the auto clicker if necessary
- this.toggleAutoClick();
- }
- } else {
- // Assume we can't reach this point with only the built-in auto restart running, so it's safe to stop it
- GymRunner.autoRestart(false);
+
+ static changeClickMultiplier(event) {
+ // TODO decide on a better range / function
+ const multiplier = +event.target.value;
+ if (Number.isInteger(multiplier) && multiplier > 0) {
+ this.autoClickMultiplier = multiplier;
+ localStorage.setItem("autoClickMultiplier", this.autoClickMultiplier);
+ var displayNum = (this.ticksPerSecond * this.autoClickMultiplier).toLocaleString('en-US', {maximumFractionDigits: 2});
+ document.getElementById('auto-click-rate-info').innerText = `Click Attack Rate: ${displayNum}/s`;
+ this.toggleAutoClickerLoop();
+ }
}
- }
-
- static changeSelectedGym(event) {
- const val = +event.target.value;
- if ([0, 1, 2, 3, 4].includes(val)) {
- this.autoGymSelect = val;
- localStorage.setItem('autoGymSelect', this.autoGymSelect);
- // In case currently fighting a gym
- if (this.autoClickState() && this.autoGymState()) {
- // Only break out of this script's auto restart, not the built-in one
- GymRunner.autoRestart(false);
- }
+
+ static toggleAutoGym() {
+ const element = document.getElementById('auto-gym-start');
+ const newState = !this.autoGymState();
+ if (newState && !this.canStartAutoGym()) {
+ // Don't turn on if there's no gym here
+ return;
+ } else if (newState && this.autoDungeonState()) {
+ // Only one mode may be active at a time
+ return;
+ }
+ this.autoGymState(newState);
+ localStorage.setItem('autoGymState', this.autoGymState());
+ this.autoGymState() ? element.classList.replace('btn-danger', 'btn-success') : element.classList.replace('btn-success', 'btn-danger');
+ element.textContent = `Auto Gym [${this.autoGymState() ? 'ON' : 'OFF'}]`;
+ if (this.autoGymState()) {
+ if (!this.autoClickState()) {
+ // Turn on the auto clicker if necessary
+ this.toggleAutoClick();
+ }
+ } else {
+ // Assume we can't reach this point with only the built-in auto restart running, so it's safe to stop it
+ GymRunner.autoRestart(false);
+ }
}
- }
-
- static toggleAutoDungeon(allowSlowStop = false) {
- const element = document.getElementById('auto-dungeon-start');
- let newState = !this.autoDungeonState();
-
- if (DungeonGuides.hired()) {
- // Auto Dungeon can't run alongside dungeon guides, force stop
- newState = false;
- allowSlowStop = false;
- Notifier.notify({
- type: NotificationConstants.NotificationOption.warning,
- title: 'Enhanced Auto Clicker',
- message: `Auto Dungeon mode is not compatible with Dungeon Guides.`,
- timeout: GameConstants.SECOND * 15,
- });
- } else if (newState && !this.canStartAutoDungeon()) {
- // Don't turn on if there's no dungeon here
- return;
- } else if (newState && this.autoGymState()) {
- // Only one mode may be active at a time
- return;
+
+ static changeSelectedGym(event) {
+ const val = +event.target.value;
+ if ([0, 1, 2, 3, 4].includes(val)) {
+ this.autoGymSelect = val;
+ localStorage.setItem("autoGymSelect", this.autoGymSelect);
+ // In case currently fighting a gym
+ if (this.autoClickState() && this.autoGymState()) {
+ // Only break out of this script's auto restart, not the built-in one
+ GymRunner.autoRestart(false);
+ }
+ }
}
- localStorage.setItem('autoDungeonState', newState);
-
- if (
- allowSlowStop &&
- this.autoDungeonFinishBeforeStopping &&
- App.game.gameState === GameConstants.GameState.dungeon &&
- !newState &&
- !this.autoDungeonTracker.stopAfterFinishing
- ) {
- // Instead of stopping immediately, wait and exit after beating this dungeon
- this.autoDungeonTracker.stopAfterFinishing = true;
- } else {
- this.autoDungeonState(newState);
- this.autoDungeonTracker.stopAfterFinishing = false;
+ static toggleAutoDungeon(allowSlowStop = false) {
+ const element = document.getElementById('auto-dungeon-start');
+ let newState = !this.autoDungeonState();
+
+ if (DungeonGuides.hired()) {
+ // Auto Dungeon can't run alongside dungeon guides, force stop
+ newState = false;
+ allowSlowStop = false;
+ Notifier.notify({
+ type: NotificationConstants.NotificationOption.warning,
+ title: 'Enhanced Auto Clicker',
+ message: `Auto Dungeon mode is not compatible with Dungeon Guides.`,
+ timeout: GameConstants.SECOND * 15,
+ });
+ } else if (newState && !this.canStartAutoDungeon()) {
+ // Don't turn on if there's no dungeon here
+ return;
+ } else if (newState && this.autoGymState()) {
+ // Only one mode may be active at a time
+ return;
+ }
+
+ localStorage.setItem('autoDungeonState', newState);
+
+ if (allowSlowStop && this.autoDungeonFinishBeforeStopping && App.game.gameState === GameConstants.GameState.dungeon &&
+ (!newState && !this.autoDungeonTracker.stopAfterFinishing)) {
+ // Instead of stopping immediately, wait and exit after beating this dungeon
+ this.autoDungeonTracker.stopAfterFinishing = true;
+ } else {
+ this.autoDungeonState(newState);
+ this.autoDungeonTracker.stopAfterFinishing = false;
+ }
+ element.classList.remove('btn-success', 'btn-danger', 'btn-warning');
+ element.classList.add(this.autoDungeonTracker.stopAfterFinishing ? 'btn-warning' : (newState ? 'btn-success' : 'btn-danger'));
+ element.textContent = `Auto Dungeon [${newState ? 'ON' : 'OFF'}]`;
+
+ if (newState) {
+ // Trigger a dungeon scan
+ this.autoDungeonTracker.ID = -1;
+ if (!this.autoClickState()) {
+ // Turn on the auto clicker if necessary
+ this.toggleAutoClick();
+ }
+ }
}
- element.classList.remove('btn-success', 'btn-danger', 'btn-warning');
- element.classList.add(
- this.autoDungeonTracker.stopAfterFinishing
- ? 'btn-warning'
- : newState
- ? 'btn-success'
- : 'btn-danger'
- );
- element.textContent = `Auto Dungeon [${newState ? 'ON' : 'OFF'}]`;
-
- if (newState) {
- // Trigger a dungeon scan
- this.autoDungeonTracker.ID = -1;
- if (!this.autoClickState()) {
- // Turn on the auto clicker if necessary
- this.toggleAutoClick();
- }
+
+ static toggleAutoDungeonEncounterMode() {
+ this.autoDungeonEncounterMode = !this.autoDungeonEncounterMode;
+ $('#auto-dungeon-encounter-mode img').css('filter', `${this.autoDungeonEncounterMode ? '' : 'grayscale(100%)' }`);
+ localStorage.setItem('autoDungeonEncounterMode', this.autoDungeonEncounterMode);
+ this.autoDungeonTracker.coords = null;
}
- }
-
- static toggleAutoDungeonEncounterMode() {
- this.autoDungeonEncounterMode = !this.autoDungeonEncounterMode;
- $('#auto-dungeon-encounter-mode img').css(
- 'filter',
- `${this.autoDungeonEncounterMode ? '' : 'grayscale(100%)'}`
- );
- localStorage.setItem(
- 'autoDungeonEncounterMode',
- this.autoDungeonEncounterMode
- );
- this.autoDungeonTracker.coords = null;
- }
-
- static toggleAutoDungeonChestMode() {
- this.autoDungeonChestMode = !this.autoDungeonChestMode;
- $('#auto-dungeon-chest-mode img').css(
- 'filter',
- `${this.autoDungeonChestMode ? '' : 'grayscale(100%)'}`
- );
- localStorage.setItem('autoDungeonChestMode', this.autoDungeonChestMode);
- this.autoDungeonTracker.coords = null;
- }
-
- static changeAutoDungeonLootTier(tier) {
- const val = +tier;
- if ([...Object.keys(baseLootTierChance).keys()].includes(val)) {
- this.autoDungeonLootTier = val;
- document
- .getElementById('auto-dungeon-loottier-img')
- .setAttribute(
- 'src',
- `assets/images/dungeons/chest-${
- Object.keys(baseLootTierChance)[val]
- }.png`
- );
- localStorage.setItem('autoDungeonLootTier', this.autoDungeonLootTier);
+
+ static toggleAutoDungeonChestMode() {
+ this.autoDungeonChestMode = !this.autoDungeonChestMode;
+ $('#auto-dungeon-chest-mode img').css('filter', `${this.autoDungeonChestMode ? '' : 'grayscale(100%)' }`);
+ localStorage.setItem('autoDungeonChestMode', this.autoDungeonChestMode);
+ this.autoDungeonTracker.coords = null;
}
- }
-
- static toggleAutoDungeonFinishBeforeStopping() {
- this.autoDungeonFinishBeforeStopping =
- !this.autoDungeonFinishBeforeStopping;
- if (
- !this.autoDungeonFinishBeforeStopping &&
- this.autoDungeonTracker.stopAfterFinishing
- ) {
- this.toggleAutoDungeon();
+
+ static changeAutoDungeonLootTier(tier) {
+ const val = +tier;
+ if ([...Object.keys(baseLootTierChance).keys()].includes(val)) {
+ this.autoDungeonLootTier = val;
+ document.getElementById('auto-dungeon-loottier-img').setAttribute('src', `assets/images/dungeons/chest-${Object.keys(baseLootTierChance)[val]}.png`);
+ localStorage.setItem("autoDungeonLootTier", this.autoDungeonLootTier);
+ }
}
- localStorage.setItem(
- 'autoDungeonFinishBeforeStopping',
- this.autoDungeonFinishBeforeStopping
- );
- }
-
- static toggleAutoDungeonAlwaysOpenRareChests() {
- this.autoDungeonAlwaysOpenRareChests =
- !this.autoDungeonAlwaysOpenRareChests;
- localStorage.setItem(
- 'autoDungeonAlwaysOpenRareChests',
- this.autoDungeonAlwaysOpenRareChests
- );
- }
-
- static toggleAutoGymGraphics() {
- this.gymGraphicsDisabled(!this.gymGraphicsDisabled());
- localStorage.setItem('gymGraphicsDisabled', this.gymGraphicsDisabled());
- }
-
- static toggleAutoDungeonGraphics() {
- this.dungeonGraphicsDisabled(!this.dungeonGraphicsDisabled());
- localStorage.setItem(
- 'dungeonGraphicsDisabled',
- this.dungeonGraphicsDisabled()
- );
- }
-
- static changeCalcEfficiencyDisplayMode(event) {
- const val = +event.target.value;
- if (
- val != this.autoClickCalcEfficiencyDisplayMode &&
- [0, 1].includes(val)
- ) {
- this.autoClickCalcEfficiencyDisplayMode = val;
- localStorage.setItem(
- 'autoClickCalcEfficiencyDisplayMode',
- this.autoClickCalcEfficiencyDisplayMode
- );
- this.resetCalculator();
+
+ static toggleAutoDungeonFinishBeforeStopping() {
+ this.autoDungeonFinishBeforeStopping = !this.autoDungeonFinishBeforeStopping;
+ if (!this.autoDungeonFinishBeforeStopping && this.autoDungeonTracker.stopAfterFinishing) {
+ this.toggleAutoDungeon();
+ }
+ localStorage.setItem('autoDungeonFinishBeforeStopping', this.autoDungeonFinishBeforeStopping);
}
- }
-
- static changeCalcDamageDisplayMode(event) {
- const val = +event.target.value;
- if (val != this.autoClickCalcDamageDisplayMode && [0, 1].includes(val)) {
- this.autoClickCalcDamageDisplayMode = val;
- localStorage.setItem(
- 'autoClickCalcDamageDisplayMode',
- this.autoClickCalcDamageDisplayMode
- );
- this.resetCalculator();
+
+ static toggleAutoDungeonAlwaysOpenRareChests() {
+ this.autoDungeonAlwaysOpenRareChests = !this.autoDungeonAlwaysOpenRareChests;
+ localStorage.setItem('autoDungeonAlwaysOpenRareChests', this.autoDungeonAlwaysOpenRareChests);
}
- }
-
- /* Auto Clicker */
-
- /**
- * Resets and, if enabled, restarts autoclicker
- * -While enabled, runs times per second in active battle
- */
- static toggleAutoClickerLoop() {
- var delay = Math.ceil(1000 / this.ticksPerSecond);
- clearInterval(this.autoClickerLoop);
- // Restart stats calculator
- this.resetCalculator();
- // Only use click multiplier while autoclicking
- this.overrideClickAttack(
- this.autoClickState() ? this.autoClickMultiplier : 1
- );
- if (this.autoClickState()) {
- // Start autoclicker loop
- this.autoClickerLoop = setInterval(
- () => EnhancedAutoClicker.autoClicker(),
- delay
- );
- } else {
- if (this.autoGymState()) {
- GymRunner.autoRestart(false);
- }
+
+ static toggleAutoGymGraphics() {
+ this.gymGraphicsDisabled(!this.gymGraphicsDisabled());
+ localStorage.setItem('gymGraphicsDisabled', this.gymGraphicsDisabled());
}
- }
-
- /**
- * One tick of the autoclicker
- * -Performs click attacks while in an active battle
- * -Outside of battle, runs Auto Dungeon and Auto Gym
- */
- static autoClicker() {
- // Click while in a normal battle
- if (App.game.gameState === GameConstants.GameState.fighting) {
- Battle.clickAttack(this.autoClickMultiplier);
+
+ static toggleAutoDungeonGraphics() {
+ this.dungeonGraphicsDisabled(!this.dungeonGraphicsDisabled());
+ localStorage.setItem('dungeonGraphicsDisabled', this.dungeonGraphicsDisabled());
}
- // ...or gym battle
- else if (App.game.gameState === GameConstants.GameState.gym) {
- GymBattle.clickAttack(this.autoClickMultiplier);
+
+ static changeCalcEfficiencyDisplayMode(event) {
+ const val = +event.target.value;
+ if (val != this.autoClickCalcEfficiencyDisplayMode && [0, 1].includes(val)) {
+ this.autoClickCalcEfficiencyDisplayMode = val;
+ localStorage.setItem('autoClickCalcEfficiencyDisplayMode', this.autoClickCalcEfficiencyDisplayMode);
+ this.resetCalculator();
+ }
}
- // ...or dungeon battle
- else if (
- App.game.gameState === GameConstants.GameState.dungeon &&
- DungeonRunner.fighting()
- ) {
- DungeonBattle.clickAttack(this.autoClickMultiplier);
+
+ static changeCalcDamageDisplayMode(event) {
+ const val = +event.target.value;
+ if (val != this.autoClickCalcDamageDisplayMode && [0, 1].includes(val)) {
+ this.autoClickCalcDamageDisplayMode = val;
+ localStorage.setItem('autoClickCalcDamageDisplayMode', this.autoClickCalcDamageDisplayMode);
+ this.resetCalculator();
+ }
}
- // ...or temporary battle
- else if (App.game.gameState === GameConstants.GameState.temporaryBattle) {
- TemporaryBattleBattle.clickAttack(this.autoClickMultiplier);
+
+ /* Auto Clicker */
+
+ /**
+ * Resets and, if enabled, restarts autoclicker
+ * -While enabled, runs times per second in active battle
+ */
+ static toggleAutoClickerLoop() {
+ var delay = Math.ceil(1000 / this.ticksPerSecond);
+ clearInterval(this.autoClickerLoop);
+ // Restart stats calculator
+ this.resetCalculator();
+ // Only use click multiplier while autoclicking
+ this.overrideClickAttack(this.autoClickState() ? this.autoClickMultiplier : 1);
+ if (this.autoClickState()) {
+ // Start autoclicker loop
+ this.autoClickerLoop = setInterval(() => EnhancedAutoClicker.autoClicker(), delay);
+ } else {
+ if (this.autoGymState()) {
+ GymRunner.autoRestart(false);
+ }
+ }
}
- // If not battling, progress through dungeon
- else if (this.autoDungeonState()) {
- this.autoDungeon();
+
+ /**
+ * One tick of the autoclicker
+ * -Performs click attacks while in an active battle
+ * -Outside of battle, runs Auto Dungeon and Auto Gym
+ */
+ static autoClicker() {
+ // Click while in a normal battle
+ if (App.game.gameState === GameConstants.GameState.fighting) {
+ Battle.clickAttack(this.autoClickMultiplier);
+ }
+ // ...or gym battle
+ else if (App.game.gameState === GameConstants.GameState.gym) {
+ GymBattle.clickAttack(this.autoClickMultiplier);
+ }
+ // ...or dungeon battle
+ else if (App.game.gameState === GameConstants.GameState.dungeon && DungeonRunner.fighting()) {
+ DungeonBattle.clickAttack(this.autoClickMultiplier);
+ }
+ // ...or temporary battle
+ else if (App.game.gameState === GameConstants.GameState.temporaryBattle) {
+ TemporaryBattleBattle.clickAttack(this.autoClickMultiplier);
+ }
+ // If not battling, progress through dungeon
+ else if (this.autoDungeonState()) {
+ this.autoDungeon();
+ }
+ // If not battling gym, start battling
+ else if (this.autoGymState()) {
+ this.autoGym();
+ }
+ if (this.autoAchievementState()) {
+ this.autoAchievement();
+ }
+ this.autoClickCalcTracker.ticks[0]++;
}
- // If not battling gym, start battling
- else if (this.autoGymState()) {
- this.autoGym();
+
+ static autoAchievement() {
+ try {
+ let currentRoute = player.route;
+ let currentRegion = player.region;
+ let currentDungeon = player.town.dungeon?.name;
+ let currentGym = player.town.name;
+
+ // can't do anything if we're catching a pokemon
+ if (App.game.gameState === GameConstants.GameState.dungeon && DungeonBattle.catching()) {
+ return;
+ }
+
+ if (this.autoAchievementSelect() === this.autoAchievementSelectTypes.Routes) {
+ let newRoute = this.getNextRoute();
+ if (!newRoute) {
+ return;
+ }
+ currentRoute = player.route;
+ if (
+ (newRoute && newRoute.number !== currentRoute) ||
+ newRoute.region !== currentRegion
+ ) {
+ if (newRoute.region !== player.subregion) {
+ if (newRoute.subRegion === undefined) {
+ player.subregion = 0;
+ } else {
+ player.subregion = newRoute.subRegion;
+ }
+ }
+ MapHelper.moveToRoute(newRoute.number, newRoute.region);
+ }
+ } else if (this.autoAchievementSelect() === this.autoAchievementSelectTypes.Gyms) {
+ //move
+ let gymNumber = 0;
+ let newGym = this.getNextGym();
+ let town = TownList[newGym];
+ if (!newGym) {
+ return;
+ }
+
+ if (town === undefined) {
+ let tempTown = Array.from(Object.entries(TownList)).find((val) =>
+ val[1].content.find((val2) => val2.town === newGym)
+ );
+ let gyms = tempTown[1].content.filter((val) => val instanceof Gym);
+ gymNumber = gyms.findIndex((val) => val.town === newGym);
+ newGym = tempTown[0];
+ town = TownList[newGym];
+ }
+ if (this.autoGymSelect !== gymNumber) {
+ this.autoGymSelect = gymNumber;
+ document.getElementById('auto-gym-select').value = gymNumber;
+ EnhancedAutoClicker.toggleAutoGym();
+ }
+
+ if (!MapHelper.isTownCurrentLocation(currentGym)) {
+ MapHelper.moveToTown(currentGym);
+ }
+ if (newGym && newGym !== currentGym) {
+ currentGym = newGym;
+ if (town.subRegion === undefined) {
+ player.subregion = 0;
+ } else {
+ player.subregion = town.subRegion;
+ }
+ player.subregion = town.subRegion;
+ MapHelper.moveToTown(newGym);
+ }
+ //Toggle Auto Gym ON
+ if (this.autoGymState() === false) {
+ EnhancedAutoClicker.toggleAutoGym();
+ }
+ } else if (this.autoAchievementSelect() === this.autoAchievementSelectTypes.Dungeons) {
+ //Move
+ let newDungeon = this.getNextDungeon();
+ if (!newDungeon) {
+ return;
+ }
+ if (!MapHelper.isTownCurrentLocation(currentDungeon)) {
+ MapHelper.moveToTown(newDungeon);
+ }
+ if (newDungeon && newDungeon !== currentDungeon) {
+ player.subregion = TownList[newDungeon].subRegion;
+ DungeonRunner.dungeonFinished(true);
+ DungeonRunner.fighting(false);
+ DungeonRunner.fightingBoss(false);
+ MapHelper.moveToTown(newDungeon);
+ }
+ //Toggle Auto Dungeon ON
+ if (this.autoDungeonState() === false) {
+ document.getElementById('auto-dungeon-start').click();
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ }
}
- if (this.autoAchievementState()) {
- this.autoAchievement();
+
+ static getNextRoute() {
+ let regionRoutes = Routes.getRoutesByRegion(player.region);
+ for (const region of regionRoutes) {
+ if (
+ this.getDefeatedOnRoute(player.region, region.number) <
+ GameConstants.ACHIEVEMENT_DEFEAT_ROUTE_VALUES[
+ GameConstants.ACHIEVEMENT_DEFEAT_ROUTE_VALUES.length - 1
+ ]
+ ) {
+ return region;
+ }
+ }
+ player.region++;
+ return false;
}
- this.autoClickCalcTracker.ticks[0]++;
- }
- //
- static autoAchievement() {
- try {
- let currentRoute = player.route;
- let currentRegion = player.region;
- let currentDungeon = player.town.dungeon?.name;
- let currentGym = player.town.name;
-
- //Route
- if (this.autoAchievementSelect() == 0) {
- let newRoute = this.getNextRoute();
- if (!newRoute) {
- return;
- }
- currentRoute = player.route;
- if (
- (newRoute && newRoute.number != currentRoute) ||
- newRoute.region != currentRegion
- ) {
- currentRoute = newRoute.number;
- currentRegion = newRoute.region;
-
- if (newRoute.region != player.subregion) {
- if (newRoute.subRegion === undefined) {
- player.subregion = 0;
- } else {
- player.subregion = newRoute.subRegion;
+ static getNextGym() {
+ let gyms = Array.from(Object.values(GymList));
+ let regionGyms = gyms
+ .filter((val) => val.parent)
+ .sort((a, b) => {
+ Math.max(...a.pokemons.map((val) => val.baseHealth)) -
+ Math.max(...b.pokemons.map((val) => val.baseHealth));
+ });
+ for (let j = 0; j < regionGyms.length; j++) {
+ if (
+ this.getDefeatedOnGym(regionGyms[j].town) <
+ GameConstants.ACHIEVEMENT_DEFEAT_GYM_VALUES[
+ GameConstants.ACHIEVEMENT_DEFEAT_GYM_VALUES.length - 1
+ ]
+ ) {
+ if (player.reqion !== regionGyms[j].parent.region) {
+ player.region = regionGyms[j].parent.region;
+ }
+ return regionGyms[j].town;
}
- }
- MapHelper.moveToRoute(newRoute.number, newRoute.region);
- }
- }
- //Gym
- else if (this.autoAchievementSelect() == 1) {
- //move
- let gymNumber = 0;
- let newGym = this.getNextGym();
- let town = TownList[newGym];
- if (!newGym) {
- return;
- }
-
- if (town === undefined) {
- let tempTown = Array.from(Object.entries(TownList)).find((val) =>
- val[1].content.find((val2) => val2.town === newGym)
- );
- let gyms = tempTown[1].content.filter((val) => val instanceof Gym);
- gymNumber = gyms.findIndex((val) => val.town === newGym);
- newGym = tempTown[0];
- town = TownList[newGym];
- }
- if (this.autoGymSelect !== gymNumber) {
- this.autoGymSelect = gymNumber;
- document.getElementById('auto-gym-select').value = gymNumber;
- EnhancedAutoClicker.toggleAutoGym();
- }
-
- if (!MapHelper.isTownCurrentLocation(currentGym)) {
- MapHelper.moveToTown(currentGym);
- }
- if (newGym && newGym != currentGym) {
- currentGym = newGym;
- if (town.subRegion === undefined) {
- player.subregion = 0;
- } else {
- player.subregion = town.subRegion;
- }
- player.subregion = town.subRegion;
- MapHelper.moveToTown(newGym);
- }
- //Toggle Auto Gym ON
- if (this.autoGymState() === false) {
- EnhancedAutoClicker.toggleAutoGym();
- }
- }
-
- //Dungeon
- else if (this.autoAchievementSelect() == 2) {
- //Move
- let newDungeon = this.getNextDungeon();
- if (!newDungeon) {
- return;
- }
- if (!MapHelper.isTownCurrentLocation(currentDungeon)) {
- MapHelper.moveToTown(newDungeon);
- }
- if (newDungeon && newDungeon != currentDungeon) {
- currentDungeon = newDungeon;
- player.subregion = TownList[newDungeon].subRegion;
- DungeonRunner.dungeonFinished(true);
- DungeonRunner.fighting(false);
- DungeonRunner.fightingBoss(false);
- MapHelper.moveToTown(newDungeon);
- }
- //Toggle Auto Dungeon ON
- if (this.autoDungeonState() === false) {
- document.getElementById('auto-dungeon-start').click();
- }
- }
- } catch (e) {
- console.error(e);
+ }
}
- }
-
- static getNextRoute() {
- let regionRoutes = Routes.getRoutesByRegion(player.region);
- for (const region of regionRoutes) {
- if (
- this.getDefeatedOnRoute(player.region, region.number) <
- GameConstants.ACHIEVEMENT_DEFEAT_ROUTE_VALUES[
- GameConstants.ACHIEVEMENT_DEFEAT_ROUTE_VALUES.length - 1
- ]
- ) {
- return region;
- }
+
+ static getNextDungeon() {
+ let regionDungeons = GameConstants.RegionDungeons[player.region];
+ for (let j = 0; j < regionDungeons.length; j++) {
+ if (
+ this.getDefeatedOnDungeon(regionDungeons[j]) <
+ GameConstants.ACHIEVEMENT_DEFEAT_DUNGEON_VALUES[
+ GameConstants.ACHIEVEMENT_DEFEAT_DUNGEON_VALUES.length - 1
+ ]
+ ) {
+ return regionDungeons[j];
+ }
+ }
+ player.region++;
+ return false;
}
- player.region++;
- return false;
- }
-
- static getNextGym() {
- let gyms = Array.from(Object.values(GymList));
- let regionGyms = gyms
- .filter((val) => val.parent)
- .sort((a, b) => {
- Math.max(...a.pokemons.map((val) => val.baseHealth)) -
- Math.max(...b.pokemons.map((val) => val.baseHealth));
- });
- for (let j = 0; j < regionGyms.length; j++) {
- if (
- this.getDefeatedOnGym(regionGyms[j].town) <
- GameConstants.ACHIEVEMENT_DEFEAT_GYM_VALUES[
- GameConstants.ACHIEVEMENT_DEFEAT_GYM_VALUES.length - 1
- ]
- ) {
- if (player.reqion !== regionGyms[j].parent.region) {
- player.region = regionGyms[j].parent.region;
- }
- return regionGyms[j].town;
- }
+
+ static getDefeatedOnRoute(region, route) {
+ return App.game.statistics.routeKills[region][route]();
}
- }
-
- static getNextDungeon() {
- let regionDungeons = GameConstants.RegionDungeons[player.region];
- for (let j = 0; j < regionDungeons.length; j++) {
- if (
- this.getDefeatedOnDungeon(regionDungeons[j]) <
- GameConstants.ACHIEVEMENT_DEFEAT_DUNGEON_VALUES[
- GameConstants.ACHIEVEMENT_DEFEAT_DUNGEON_VALUES.length - 1
- ]
- ) {
- return regionDungeons[j];
- }
+
+ static getDefeatedOnGym(gymName) {
+ return App.game.statistics.gymsDefeated[
+ GameConstants.getGymIndex(gymName)
+ ]();
}
- player.region++;
- return false;
- }
-
- static getDefeatedOnRoute(region, route) {
- return App.game.statistics.routeKills[region][route]();
- }
-
- static getDefeatedOnGym(gymName) {
- return App.game.statistics.gymsDefeated[
- GameConstants.getGymIndex(gymName)
- ]();
- }
-
- static getDefeatedOnDungeon(dungeonName) {
- return App.game.statistics.dungeonsCleared[
- GameConstants.getDungeonIndex(dungeonName)
- ]();
- }
- //
-
- /**
- * Override the game's function for Click Attack to:
- * - make multiple clicks at once via multiplier
- * - support changing the attack speed cap for higher tick speeds
- */
- static overrideClickAttack(clickMultiplier = 1) {
- // Set delay based on the autoclicker's tick rate
- // (lower to give setInterval some wiggle room)
- const delay = Math.min(Math.ceil(1000 / this.ticksPerSecond) - 10, 50);
- Battle.clickAttack = function () {
- // click attacks disabled and we already beat the starter
- if (
- App.game.challenges.list.disableClickAttack.active() &&
- player.regionStarters[GameConstants.Region.kanto]() !=
- GameConstants.Starter.None
- ) {
- return;
- }
- const now = Date.now();
- if (this.lastClickAttack > now - delay) {
- return;
- }
- if (!this.enemyPokemon()?.isAlive()) {
- return;
- }
- this.lastClickAttack = now;
- // Don't autoclick more than needed for lethal
- const clickDamage = App.game.party.calculateClickAttack(true);
- var clicks = Math.min(
- clickMultiplier,
- Math.ceil(this.enemyPokemon().health() / clickDamage)
- );
- GameHelper.incrementObservable(App.game.statistics.clickAttacks, clicks);
- this.enemyPokemon().damage(clickDamage * clicks);
- if (!this.enemyPokemon().isAlive()) {
- this.defeatPokemon();
- }
- };
- }
-
- /* Auto Gym */
-
- /**
- * Starts selected gym with auto restart enabled
- */
- static autoGym() {
- if (this.canStartAutoGym()) {
- // Find all unlocked gyms in the current town
- var gymList = player.town.content.filter(
- (c) => c.constructor.name == 'Gym' && c.isUnlocked()
- );
- if (gymList.length > 0) {
- var gymIndex = Math.min(this.autoGymSelect, gymList.length - 1);
- // Start in auto restart mode
- GymRunner.startGym(gymList[gymIndex], true);
- return;
- }
+
+ static getDefeatedOnDungeon(dungeonName) {
+ return App.game.statistics.dungeonsCleared[
+ GameConstants.getDungeonIndex(dungeonName)
+ ]();
}
- // Disable if we aren't in a location with unlocked gyms
- this.toggleAutoGym();
- }
-
- static canStartAutoGym() {
- return (
- App.game.gameState === GameConstants.GameState.gym ||
- (App.game.gameState === GameConstants.GameState.town &&
- player.town.content.some(
- (c) => c.constructor.name == 'Gym' && c.isUnlocked()
- ))
- );
- }
-
- /**
- * Override GymRunner built-in functions:
- * -Add auto gym equivalent of gymWon() to save on performance by not loading town between
- */
- static overrideGymRunner() {
- GymRunner.gymWonNormal = GymRunner.gymWon;
- // Version with free auto restart
- GymRunner.gymWonAuto = function (gym) {
- if (GymRunner.running()) {
- GymRunner.running(false);
- // First time defeating this gym
- if (!App.game.badgeCase.hasBadge(gym.badgeReward)) {
- gym.firstWinReward();
- }
- GameHelper.incrementObservable(
- App.game.statistics.gymsDefeated[GameConstants.getGymIndex(gym.town)]
- );
- // Award money for defeating gym as we're auto clicking
- App.game.wallet.gainMoney(gym.moneyReward);
-
- if (GymRunner.autoRestart()) {
- // Unlike the original function, autoclicker doesn't charge the player money
- GymRunner.startGym(
- GymRunner.gymObservable(),
- GymRunner.autoRestart(),
- false
- );
- return;
- }
-
- // Send the player back to the town they were in
- player.town = gym.parent;
- App.game.gameState = GameConstants.GameState.town;
- }
- };
- // Only use our version when auto gym is running
- GymRunner.gymWon = function (...args) {
- if (
- EnhancedAutoClicker.autoClickState() &&
- EnhancedAutoClicker.autoGymState()
- ) {
- GymRunner.gymWonAuto(...args);
- } else {
- GymRunner.gymWonNormal(...args);
- }
- };
- }
-
- /* Auto Dungeon */
-
- /**
- * Automatically begins and progresses through dungeons with multiple pathfinding options
- */
- static autoDungeon() {
- // TODO more thoroughly test switching between modes and enabling/disabling within a dungeon
- // Progress through dungeon
- if (App.game.gameState === GameConstants.GameState.dungeon) {
- if (DungeonRunner.fighting() || DungeonBattle.catching()) {
- // Can't do anything while in a battle
- return;
- }
- // Scan each new dungeon floor
- if (
- this.autoDungeonTracker.ID !== DungeonRunner.dungeonID ||
- this.autoDungeonTracker.floor !==
- DungeonRunner.map.playerPosition().floor
- ) {
- this.scanDungeon();
- }
- // If boss has been defeated, wait for the dungeon restart (delayed to allow quest updates)
- if (this.autoDungeonTracker.dungeonFinished) {
- return;
- }
- // Reset pathfinding coordinates to entrance
- if (this.autoDungeonTracker.coords == null) {
- this.autoDungeonTracker.coords = new Point(
- Math.floor(this.autoDungeonTracker.floorSize / 2),
- this.autoDungeonTracker.floorSize - 1,
- this.autoDungeonTracker.floor
- );
- }
- const floorMap = DungeonRunner.map.board()[this.autoDungeonTracker.floor];
-
- // All targets visible, fight enemies / open chests then finish floor
- if (
- floorMap[this.autoDungeonTracker.bossCoords.y][
- this.autoDungeonTracker.bossCoords.x
- ].isVisible &&
- !(
- (this.autoDungeonChestMode || this.autoDungeonEncounterMode) &&
- !this.autoDungeonTracker.floorExplored
- )
- ) {
- this.clearDungeon();
- }
- // Explore dungeon to reveal boss + any target tiles
- else {
- this.exploreDungeon();
- }
+
+ /**
+ * Override the game's function for Click Attack to:
+ * - make multiple clicks at once via multiplier
+ * - support changing the attack speed cap for higher tick speeds
+ */
+ static overrideClickAttack(clickMultiplier = 1) {
+ // Set delay based on the autoclicker's tick rate
+ // (lower to give setInterval some wiggle room)
+ const delay = Math.min(Math.ceil(1000 / this.ticksPerSecond) - 10, 50);
+ Battle.clickAttack = function () {
+ // click attacks disabled and we already beat the starter
+ if (App.game.challenges.list.disableClickAttack.active() && player.regionStarters[GameConstants.Region.kanto]() != GameConstants.Starter.None) {
+ return;
+ }
+ const now = Date.now();
+ if (this.lastClickAttack > now - delay) {
+ return;
+ }
+ if (!this.enemyPokemon()?.isAlive()) {
+ return;
+ }
+ this.lastClickAttack = now;
+ // Don't autoclick more than needed for lethal
+ const clickDamage = App.game.party.calculateClickAttack(true);
+ var clicks = Math.min(clickMultiplier, Math.ceil(this.enemyPokemon().health() / clickDamage));
+ GameHelper.incrementObservable(App.game.statistics.clickAttacks, clicks);
+ this.enemyPokemon().damage(clickDamage * clicks);
+ if (!this.enemyPokemon().isAlive()) {
+ this.defeatPokemon();
+ }
+ }
}
- // Begin dungeon
- else if (this.canStartAutoDungeon()) {
- // Enter dungeon if unlocked and affordable
- DungeonRunner.initializeDungeon(player.town.dungeon);
- } else {
- // Disable if locked, can't afford entry cost, or there's no dungeon here
- this.toggleAutoDungeon();
+
+
+ /* Auto Gym */
+
+ /**
+ * Starts selected gym with auto restart enabled
+ */
+ static autoGym() {
+ if (this.canStartAutoGym()) {
+ // Find all unlocked gyms in the current town
+ var gymList = player.town.content.filter((c) => (c.constructor.name == "Gym" && c.isUnlocked()));
+ if (gymList.length > 0) {
+ var gymIndex = Math.min(this.autoGymSelect, gymList.length - 1);
+ // Start in auto restart mode
+ GymRunner.startGym(gymList[gymIndex], true);
+ return;
+ }
+ }
+ // Disable if we aren't in a location with unlocked gyms
+ this.toggleAutoGym();
}
- }
-
- static canStartAutoDungeon() {
- if (
- !(
- App.game.gameState === GameConstants.GameState.dungeon ||
- (App.game.gameState === GameConstants.GameState.town &&
- player.town instanceof DungeonTown)
- )
- ) {
- return false;
+
+ static canStartAutoGym() {
+ return App.game.gameState === GameConstants.GameState.gym || (App.game.gameState === GameConstants.GameState.town &&
+ player.town.content.some((c) => c.constructor.name == "Gym" && c.isUnlocked())
+ );
}
- if (DungeonGuides.hired()) {
- return false;
+
+ /**
+ * Override GymRunner built-in functions:
+ * -Add auto gym equivalent of gymWon() to save on performance by not loading town between
+ */
+ static overrideGymRunner() {
+ GymRunner.gymWonNormal = GymRunner.gymWon;
+ // Version with free auto restart
+ GymRunner.gymWonAuto = function(gym) {
+ if (GymRunner.running()) {
+ GymRunner.running(false);
+ // First time defeating this gym
+ if (!App.game.badgeCase.hasBadge(gym.badgeReward)) {
+ gym.firstWinReward();
+ }
+ GameHelper.incrementObservable(App.game.statistics.gymsDefeated[GameConstants.getGymIndex(gym.town)]);
+ // Award money for defeating gym as we're auto clicking
+ App.game.wallet.gainMoney(gym.moneyReward);
+
+ if (GymRunner.autoRestart()) {
+ // Unlike the original function, autoclicker doesn't charge the player money
+ GymRunner.startGym(GymRunner.gymObservable(), GymRunner.autoRestart(), false);
+ return;
+ }
+
+ // Send the player back to the town they were in
+ player.town = gym.parent;
+ App.game.gameState = GameConstants.GameState.town;
+ }
+ }
+ // Only use our version when auto gym is running
+ GymRunner.gymWon = function(...args) {
+ if (EnhancedAutoClicker.autoClickState() && EnhancedAutoClicker.autoGymState()) {
+ GymRunner.gymWonAuto(...args);
+ } else {
+ GymRunner.gymWonNormal(...args);
+ }
+ }
}
- if (DungeonGuides.hired()) {
- return false;
+
+ /* Auto Dungeon */
+
+ /**
+ * Automatically begins and progresses through dungeons with multiple pathfinding options
+ */
+ static autoDungeon() { // TODO more thoroughly test switching between modes and enabling/disabling within a dungeon
+ // Progress through dungeon
+ if (App.game.gameState === GameConstants.GameState.dungeon) {
+ if (DungeonRunner.fighting() || DungeonBattle.catching()) {
+ // Can't do anything while in a battle
+ return;
+ }
+ // Scan each new dungeon floor
+ if (this.autoDungeonTracker.ID !== DungeonRunner.dungeonID || this.autoDungeonTracker.floor !== DungeonRunner.map.playerPosition().floor) {
+ this.scanDungeon();
+ }
+ // If boss has been defeated, wait for the dungeon restart (delayed to allow quest updates)
+ if (this.autoDungeonTracker.dungeonFinished) {
+ return;
+ }
+ // Reset pathfinding coordinates to entrance
+ if (this.autoDungeonTracker.coords == null) {
+ this.autoDungeonTracker.coords = new Point(Math.floor(this.autoDungeonTracker.floorSize / 2), this.autoDungeonTracker.floorSize - 1, this.autoDungeonTracker.floor);
+ }
+ const floorMap = DungeonRunner.map.board()[this.autoDungeonTracker.floor];
+
+ // All targets visible, fight enemies / open chests then finish floor
+ if (floorMap[this.autoDungeonTracker.bossCoords.y][this.autoDungeonTracker.bossCoords.x].isVisible &&
+ !((this.autoDungeonChestMode || this.autoDungeonEncounterMode) && !this.autoDungeonTracker.floorExplored)) {
+ this.clearDungeon();
+ }
+ // Explore dungeon to reveal boss + any target tiles
+ else {
+ this.exploreDungeon();
+ }
+ }
+ // Begin dungeon
+ else if (this.canStartAutoDungeon()) {
+ // Enter dungeon if unlocked and affordable
+ DungeonRunner.initializeDungeon(player.town.dungeon);
+ } else {
+ // Disable if locked, can't afford entry cost, or there's no dungeon here
+ this.toggleAutoDungeon();
+ }
}
- const dungeon = player.town.dungeon;
- return (
- dungeon?.isUnlocked() &&
- App.game.wallet.hasAmount(
- new Amount(dungeon.tokenCost, GameConstants.Currency.dungeonToken)
- )
- );
- }
-
- /**
- * Scans current dungeon floor for relevant locations and pathfinding data
- */
- static scanDungeon() {
- // Reset / update tracker values
- this.autoDungeonTracker.ID = DungeonRunner.dungeonID;
- this.autoDungeonTracker.floor = DungeonRunner.map.playerPosition().floor;
- this.autoDungeonTracker.floorSize =
- DungeonRunner.map.floorSizes[DungeonRunner.map.playerPosition().floor];
- this.autoDungeonTracker.encounterCoords = [];
- this.autoDungeonTracker.chestCoords = [];
- this.autoDungeonTracker.coords = null;
- this.autoDungeonTracker.targetCoords = null;
- this.autoDungeonTracker.floorExplored = false;
- this.autoDungeonTracker.floorFinished = false;
- this.autoDungeonTracker.dungeonFinished = false;
-
- // Scan for chest and boss coordinates
- var dungeonBoard = DungeonRunner.map.board()[this.autoDungeonTracker.floor];
- for (var y = 0; y < dungeonBoard.length; y++) {
- for (var x = 0; x < dungeonBoard[y].length; x++) {
- let tile = dungeonBoard[y][x];
- if (tile.type() == GameConstants.DungeonTileType.enemy) {
- this.autoDungeonTracker.encounterCoords.push(
- new Point(x, y, this.autoDungeonTracker.floor)
- );
- } else if (tile.type() == GameConstants.DungeonTileType.chest) {
- let lootTier = Object.keys(baseLootTierChance).indexOf(
- tile.metadata.tier
- );
- this.autoDungeonTracker.chestCoords.push({
- xy: new Point(x, y, this.autoDungeonTracker.floor),
- tier: lootTier,
- });
- } else if (
- tile.type() == GameConstants.DungeonTileType.boss ||
- tile.type() == GameConstants.DungeonTileType.ladder
- ) {
- this.autoDungeonTracker.bossCoords = new Point(
- x,
- y,
- this.autoDungeonTracker.floor
- );
- }
- }
+
+ static canStartAutoDungeon() {
+ if (!(App.game.gameState === GameConstants.GameState.dungeon || (App.game.gameState === GameConstants.GameState.town && player.town instanceof DungeonTown))) {
+ return false;
+ }
+ if (DungeonGuides.hired()) {
+ return false;
+ }
+ const dungeon = player.town.dungeon;
+ return dungeon?.isUnlocked() && App.game.wallet.hasAmount(new Amount(dungeon.tokenCost, GameConstants.Currency.dungeonToken));
}
- // Sort chests by descending rarity
- this.autoDungeonTracker.chestCoords.sort((a, b) => b.tier - a.tier);
-
- // TODO find a more future-proof way to get flash distance
- this.autoDungeonTracker.flashTier = DungeonFlash.tiers.findIndex(
- (tier) => tier === DungeonRunner.map.flash
- );
- this.autoDungeonTracker.flashCols = [];
- let flashRadius = DungeonRunner.map.flash?.playerOffset[0] ?? 0;
- // Calculate minimum columns to fully reveal dungeon with Flash
- if (flashRadius > 0) {
- let cols = new Set();
- cols.add(flashRadius);
- let i = this.autoDungeonTracker.floorSize - flashRadius - 1;
- while (i > flashRadius) {
- cols.add(i);
- i -= flashRadius * 2 + 1;
- }
- this.autoDungeonTracker.flashCols = [...cols].sort();
+
+ /**
+ * Scans current dungeon floor for relevant locations and pathfinding data
+ */
+ static scanDungeon() {
+ // Reset / update tracker values
+ this.autoDungeonTracker.ID = DungeonRunner.dungeonID;
+ this.autoDungeonTracker.floor = DungeonRunner.map.playerPosition().floor;
+ this.autoDungeonTracker.floorSize = DungeonRunner.map.floorSizes[DungeonRunner.map.playerPosition().floor];
+ this.autoDungeonTracker.encounterCoords = [];
+ this.autoDungeonTracker.chestCoords = [];
+ this.autoDungeonTracker.coords = null;
+ this.autoDungeonTracker.targetCoords = null;
+ this.autoDungeonTracker.floorExplored = false;
+ this.autoDungeonTracker.floorFinished = false;
+ this.autoDungeonTracker.dungeonFinished = false;
+
+ // Scan for chest and boss coordinates
+ var dungeonBoard = DungeonRunner.map.board()[this.autoDungeonTracker.floor];
+ for (var y = 0; y < dungeonBoard.length; y++) {
+ for (var x = 0; x < dungeonBoard[y].length; x++) {
+ let tile = dungeonBoard[y][x];
+ if (tile.type() == GameConstants.DungeonTileType.enemy) {
+ this.autoDungeonTracker.encounterCoords.push(new Point(x, y, this.autoDungeonTracker.floor));
+ } else if (tile.type() == GameConstants.DungeonTileType.chest) {
+ let lootTier = Object.keys(baseLootTierChance).indexOf(tile.metadata.tier);
+ this.autoDungeonTracker.chestCoords.push({'xy': new Point(x, y, this.autoDungeonTracker.floor), 'tier': lootTier});
+ } else if (tile.type() == GameConstants.DungeonTileType.boss || tile.type() == GameConstants.DungeonTileType.ladder) {
+ this.autoDungeonTracker.bossCoords = new Point(x, y, this.autoDungeonTracker.floor);
+ }
+ }
+ }
+ // Sort chests by descending rarity
+ this.autoDungeonTracker.chestCoords.sort((a, b) => b.tier - a.tier);
+
+ // TODO find a more future-proof way to get flash distance
+ this.autoDungeonTracker.flashTier = DungeonFlash.tiers.findIndex(tier => tier === DungeonRunner.map.flash);
+ this.autoDungeonTracker.flashCols = [];
+ let flashRadius = DungeonRunner.map.flash?.playerOffset[0] ?? 0;
+ // Calculate minimum columns to fully reveal dungeon with Flash
+ if (flashRadius > 0) {
+ let cols = new Set();
+ cols.add(flashRadius);
+ let i = this.autoDungeonTracker.floorSize - flashRadius - 1;
+ while (i > flashRadius) {
+ cols.add(i);
+ i -= flashRadius * 2 + 1;
+ }
+ this.autoDungeonTracker.flashCols = [...cols].sort();
+ }
}
- }
-
- /**
- * Explores dungeon to reveal tiles, skipping columns that can be efficiently revealed by Flash
- */
- static exploreDungeon() {
- this.autoDungeonTracker.coords = structuredClone(
- DungeonRunner.map.playerPosition()
- );
- const dungeonBoard =
- DungeonRunner.map.board()[this.autoDungeonTracker.floor];
- const tileType = DungeonRunner.map.currentTile().type();
- if (tileType === GameConstants.DungeonTileType.chest) {
- DungeonRunner.openChest();
- const tile = DungeonRunner.map.currentTile();
- const index = this.autoDungeonTracker.chestCoords.findIndex(
- (val) =>
- val.xy.x === this.autoDungeonTracker.coords.x &&
- val.xy.y === this.autoDungeonTracker.coords.y &&
- val.xy.floor === this.autoDungeonTracker.floor
- );
- this.autoDungeonTracker.chestCoords.splice(index, 1);
+
+ /**
+ * Explores dungeon to reveal tiles, skipping columns that can be efficiently revealed by Flash
+ */
+ static exploreDungeon() {
+ const dungeonBoard = DungeonRunner.map.board()[this.autoDungeonTracker.floor];
+ var hasMoved = false;
+ var stuckInLoopCounter = 0;
+ while (!hasMoved) {
+ // End of column
+ if (this.autoDungeonTracker.coords.y == 0) {
+ if (this.autoDungeonTracker.coords.x <= 0 || this.autoDungeonTracker.coords.x === this.autoDungeonTracker.flashCols[0]) {
+ // Done exploring, clearDungeon() will take over from here
+ this.autoDungeonTracker.floorExplored = true;
+ return;
+ }
+ // Move to start of next column
+ this.autoDungeonTracker.coords.y = this.autoDungeonTracker.floorSize - 1;
+ if (this.autoDungeonTracker.coords.x >= this.autoDungeonTracker.floorSize - 1 || this.autoDungeonTracker.coords.x === this.autoDungeonTracker.flashCols.at(-1)) {
+ // Done with this side, move to other side of the entrance
+ this.autoDungeonTracker.coords.x = Math.floor(this.autoDungeonTracker.floorSize / 2) - 1;
+ } else {
+ // Move away from the entrance
+ this.autoDungeonTracker.coords.x += (this.autoDungeonTracker.coords.x >= Math.floor(this.autoDungeonTracker.floorSize / 2) ? 1 : -1);
+ }
+ // Dungeon has Flash unlocked, skip columns not in optimal flash pathing
+ } else if (this.autoDungeonTracker.flashTier > -1
+ && this.autoDungeonTracker.coords.y == (this.autoDungeonTracker.floorSize - 1)
+ && !this.autoDungeonTracker.flashCols.includes(this.autoDungeonTracker.coords.x)) {
+ // Move one column further from the entrance
+ this.autoDungeonTracker.coords.x += (this.autoDungeonTracker.coords.x >= Math.floor(this.autoDungeonTracker.floorSize / 2) ? 1 : -1);
+ }
+ // Move through current column
+ else {
+ this.autoDungeonTracker.coords.y -= 1;
+ }
+ // One move per tick to look more natural
+ if (!dungeonBoard[this.autoDungeonTracker.coords.y][this.autoDungeonTracker.coords.x].isVisited) {
+ DungeonRunner.map.moveToCoordinates(this.autoDungeonTracker.coords.x, this.autoDungeonTracker.coords.y);
+ hasMoved = true;
+ }
+ stuckInLoopCounter++;
+ if (stuckInLoopCounter > 100) {
+ console.warn(`Auto Dungeon got stuck in a loop while moving to tile \'${GameConstants.DungeonTileType[dungeonBoard[this.autoDungeonTracker.targetCoords.y][this.autoDungeonTracker.targetCoords.x].type()]}\' (${this.autoDungeonTracker.targetCoords.x}, ${this.autoDungeonTracker.targetCoords.y})`);
+ this.toggleAutoDungeon();
+ return;
+ }
+ }
}
- var hasMoved = false;
- var stuckInLoopCounter = 0;
- while (!hasMoved) {
- // End of column
- if (this.autoDungeonTracker.coords.y == 0) {
- if (
- this.autoDungeonTracker.coords.x <= 0 ||
- this.autoDungeonTracker.coords.x ===
- this.autoDungeonTracker.flashCols[0]
- ) {
- if (
- dungeonBoard[this.autoDungeonTracker.bossCoords.y][
- this.autoDungeonTracker.bossCoords.x
- ].isVisible
- ) {
- // Done exploring, clearDungeon() will take over from here
- this.autoDungeonTracker.floorExplored = true;
- return;
- }
- }
- // Move to start of next column
- this.autoDungeonTracker.coords.y =
- this.autoDungeonTracker.floorSize - 1;
- if (
- this.autoDungeonTracker.coords.x >=
- this.autoDungeonTracker.floorSize - 1 ||
- this.autoDungeonTracker.coords.x ===
- this.autoDungeonTracker.flashCols.at(-1)
- ) {
- // Done with this side, move to other side of the entrance
- this.autoDungeonTracker.coords.x =
- Math.floor(this.autoDungeonTracker.floorSize / 2) - 1;
- } else {
- // Move away from the entrance
- this.autoDungeonTracker.coords.x +=
- this.autoDungeonTracker.coords.x >=
- Math.floor(this.autoDungeonTracker.floorSize / 2)
- ? 1
- : -1;
- }
- // Dungeon has Flash unlocked, skip columns not in optimal flash pathing
- } else if (
- this.autoDungeonTracker.flashTier > -1 &&
- this.autoDungeonTracker.coords.y ==
- this.autoDungeonTracker.floorSize - 1 &&
- !this.autoDungeonTracker.flashCols.includes(
- this.autoDungeonTracker.coords.x
- )
- ) {
- // Move one column further from the entrance
- this.autoDungeonTracker.coords.x +=
- this.autoDungeonTracker.coords.x >=
- Math.floor(this.autoDungeonTracker.floorSize / 2)
- ? 1
- : -1;
- }
- // Move through current column
- else {
- this.autoDungeonTracker.coords.y -= 1;
- }
- // One move per tick to look more natural
-
- DungeonRunner.map.moveToCoordinates(
- this.autoDungeonTracker.coords.x,
- this.autoDungeonTracker.coords.y
- );
- //TODO check player pos before setting true
- hasMoved = true;
-
- stuckInLoopCounter++;
- if (stuckInLoopCounter > 100) {
- console.warn(
- `Auto Dungeon got stuck in a loop while moving to tile \'${
- GameConstants.DungeonTileType[
- dungeonBoard[this.autoDungeonTracker.targetCoords.y][
- this.autoDungeonTracker.targetCoords.x
- ].type()
- ]
- }\' (${this.autoDungeonTracker.targetCoords.x}, ${
- this.autoDungeonTracker.targetCoords.y
- })`
- );
- this.toggleAutoDungeon();
- return;
- }
+
+ /**
+ * Clears dungeon, visiting all desired tile types. Assumes dungeon has already been explored to reveal desired tiles.
+ */
+ static clearDungeon() {
+ const dungeonBoard = DungeonRunner.map.board()[this.autoDungeonTracker.floor];
+ var hasMoved = false;
+ var stuckInLoopCounter = 0;
+ while (!hasMoved) {
+ // Choose a tile to move towards
+ if (!this.autoDungeonTracker.targetCoords) {
+ this.autoDungeonTracker.targetCoords = this.chooseDungeonTargetTile();
+ }
+ this.autoDungeonTracker.coords = this.pathfindTowardDungeonTarget();
+ if (!this.autoDungeonTracker.coords) {
+ console.warn(`Auto Dungeon could not find path to target tile \'${GameConstants.DungeonTileType[dungeonBoard[this.autoDungeonTracker.targetCoords.y][this.autoDungeonTracker.targetCoords.x].type()]}\' (${this.autoDungeonTracker.targetCoords.x}, ${this.autoDungeonTracker.targetCoords.y})`);
+ this.toggleAutoDungeon();
+ return;
+ }
+ // One move per tick to look more natural
+ if (!(dungeonBoard[this.autoDungeonTracker.coords.y][this.autoDungeonTracker.coords.x] === DungeonRunner.map.currentTile())) {
+ DungeonRunner.map.moveToCoordinates(this.autoDungeonTracker.coords.x, this.autoDungeonTracker.coords.y);
+ hasMoved = true;
+ }
+ // Target tile reached
+ if (this.autoDungeonTracker.coords.x === this.autoDungeonTracker.targetCoords.x && this.autoDungeonTracker.coords.y === this.autoDungeonTracker.targetCoords.y) {
+ this.autoDungeonTracker.targetCoords = null;
+ hasMoved = true;
+ // Take corresponding action
+ const tileType = DungeonRunner.map.currentTile().type();
+ if (tileType === GameConstants.DungeonTileType.enemy) {
+ // Do nothing, fights begin automatically
+ } else if (tileType === GameConstants.DungeonTileType.chest) {
+ DungeonRunner.openChest();
+ } else if (tileType === GameConstants.DungeonTileType.boss) {
+ if (this.autoDungeonTracker.floorFinished) {
+ DungeonRunner.startBossFight();
+ }
+ } else if (tileType === GameConstants.DungeonTileType.ladder) {
+ if (this.autoDungeonTracker.floorFinished) {
+ DungeonRunner.nextFloor();
+ }
+ } else {
+ console.warn(`Auto Dungeon targeted tile type ${GameConstants.DungeonTileType[tileType]}`);
+ }
+ }
+ stuckInLoopCounter++;
+ if (stuckInLoopCounter > 5) {
+ console.warn(`Auto Dungeon got stuck in a loop while moving to tile \'${GameConstants.DungeonTileType[dungeonBoard[this.autoDungeonTracker.targetCoords.y][this.autoDungeonTracker.targetCoords.x].type()]}\' (${this.autoDungeonTracker.targetCoords.x}, ${this.autoDungeonTracker.targetCoords.y})`);
+ this.toggleAutoDungeon();
+ return;
+ }
+ }
}
- }
-
- /**
- * Clears dungeon, visiting all desired tile types. Assumes dungeon has already been explored to reveal desired tiles.
- */
- static clearDungeon() {
- const dungeonBoard =
- DungeonRunner.map.board()[this.autoDungeonTracker.floor];
- var hasMoved = false;
- var stuckInLoopCounter = 0;
- while (!hasMoved) {
- // Choose a tile to move towards
- if (!this.autoDungeonTracker.targetCoords) {
- this.autoDungeonTracker.targetCoords = this.chooseDungeonTargetTile();
- }
- this.autoDungeonTracker.coords = this.pathfindTowardDungeonTarget();
- if (!this.autoDungeonTracker.coords) {
- console.warn(
- `Auto Dungeon could not find path to target tile \'${
- GameConstants.DungeonTileType[
- dungeonBoard[this.autoDungeonTracker.targetCoords.y][
- this.autoDungeonTracker.targetCoords.x
- ].type()
- ]
- }\' (${this.autoDungeonTracker.targetCoords.x}, ${
- this.autoDungeonTracker.targetCoords.y
- })`
- );
- this.toggleAutoDungeon();
- return;
- }
- // One move per tick to look more natural
- if (
- !(
- dungeonBoard[this.autoDungeonTracker.coords.y][
- this.autoDungeonTracker.coords.x
- ] === DungeonRunner.map.currentTile()
- )
- ) {
- DungeonRunner.map.moveToCoordinates(
- this.autoDungeonTracker.coords.x,
- this.autoDungeonTracker.coords.y
- );
- hasMoved = true;
- }
- // Target tile reached
- if (
- this.autoDungeonTracker.coords.x ===
- this.autoDungeonTracker.targetCoords.x &&
- this.autoDungeonTracker.coords.y ===
- this.autoDungeonTracker.targetCoords.y
- ) {
- this.autoDungeonTracker.targetCoords = null;
- hasMoved = true;
- // Take corresponding action
- const tileType = DungeonRunner.map.currentTile().type();
- if (tileType === GameConstants.DungeonTileType.enemy) {
- // Do nothing, fights begin automatically
- } else if (tileType === GameConstants.DungeonTileType.chest) {
- DungeonRunner.openChest();
- } else if (tileType === GameConstants.DungeonTileType.boss) {
- if (this.autoDungeonTracker.floorFinished) {
- DungeonRunner.startBossFight();
- }
- } else if (tileType === GameConstants.DungeonTileType.ladder) {
- if (this.autoDungeonTracker.floorFinished) {
- DungeonRunner.nextFloor();
- }
- } else {
- console.warn(
- `Auto Dungeon targeted tile type ${GameConstants.DungeonTileType[tileType]}`
- );
- }
- }
- stuckInLoopCounter++;
- if (stuckInLoopCounter > 5) {
- console.warn(
- `Auto Dungeon got stuck in a loop while moving to tile \'${
- GameConstants.DungeonTileType[
- dungeonBoard[this.autoDungeonTracker.targetCoords.y][
- this.autoDungeonTracker.targetCoords.x
- ].type()
- ]
- }\' (${this.autoDungeonTracker.targetCoords.x}, ${
- this.autoDungeonTracker.targetCoords.y
- })`
- );
- this.toggleAutoDungeon();
- return;
- }
+
+ /**
+ * Chooses a target tile based on auto dungeon modes and dungeon progress
+ */
+ static chooseDungeonTargetTile() {
+ const dungeonBoard = DungeonRunner.map.board()[this.autoDungeonTracker.floor];
+ let target = null;
+ while (!target) {
+ // Boss tile not yet unlocked
+ if (!dungeonBoard[this.autoDungeonTracker.bossCoords.y][this.autoDungeonTracker.bossCoords.x].isVisited) {
+ target = this.autoDungeonTracker.bossCoords;
+ }
+ // Encounters to fight
+ else if (this.autoDungeonEncounterMode && this.autoDungeonTracker.encounterCoords.length) {
+ // Skip already-fought encounters
+ let encounter = this.autoDungeonTracker.encounterCoords.pop();
+ if (dungeonBoard[encounter.y][encounter.x].type() == GameConstants.DungeonTileType.enemy) {
+ target = encounter;
+ } else {
+ continue;
+ }
+ }
+ // Chests to open
+ else if (this.autoDungeonChestMode && this.autoDungeonTracker.chestCoords.length && this.autoDungeonTracker.chestCoords[0].tier >= this.autoDungeonLootTier) {
+ target = this.autoDungeonTracker.chestCoords.shift().xy;
+ }
+ // Open visible chests of sufficient rarity
+ else if (this.autoDungeonAlwaysOpenRareChests && this.autoDungeonTracker.chestCoords.some((c) => c.tier >= this.autoDungeonLootTier && dungeonBoard[c.xy.y][c.xy.x].isVisible)) {
+ let index = this.autoDungeonTracker.chestCoords.findIndex((c) => c.tier >= this.autoDungeonLootTier && dungeonBoard[c.xy.y][c.xy.x].isVisible);
+ target = this.autoDungeonTracker.chestCoords[index].xy;
+ this.autoDungeonTracker.chestCoords.splice(index, 1);
+ }
+ // Time to fight the boss
+ else {
+ target = this.autoDungeonTracker.bossCoords;
+ this.autoDungeonTracker.floorFinished = true;
+ }
+ }
+ return target;
}
- }
-
- /**
- * Chooses a target tile based on auto dungeon modes and dungeon progress
- */
- static chooseDungeonTargetTile() {
- const dungeonBoard =
- DungeonRunner.map.board()[this.autoDungeonTracker.floor];
- let target = null;
- while (!target) {
- // Boss tile not yet unlocked
- if (
- !dungeonBoard[this.autoDungeonTracker.bossCoords.y][
- this.autoDungeonTracker.bossCoords.x
- ].isVisited
- ) {
- target = this.autoDungeonTracker.bossCoords;
- }
- // Encounters to fight
- else if (
- this.autoDungeonEncounterMode &&
- this.autoDungeonTracker.encounterCoords.length
- ) {
- // Skip already-fought encounters
- let encounter = this.autoDungeonTracker.encounterCoords.pop();
- if (
- dungeonBoard[encounter.y][encounter.x].type() ==
- GameConstants.DungeonTileType.enemy
- ) {
- target = encounter;
- } else {
- continue;
- }
- }
- // Chests to open
- else if (
- this.autoDungeonChestMode &&
- this.autoDungeonTracker.chestCoords.length &&
- this.autoDungeonTracker.chestCoords[0].tier >= this.autoDungeonLootTier
- ) {
- target = this.autoDungeonTracker.chestCoords.shift().xy;
- }
- // Open visible chests of sufficient rarity
- else if (
- this.autoDungeonAlwaysOpenRareChests &&
- this.autoDungeonTracker.chestCoords.some(
- (c) =>
- c.tier >= this.autoDungeonLootTier &&
- dungeonBoard[c.xy.y][c.xy.x].isVisible
- )
- ) {
- let index = this.autoDungeonTracker.chestCoords.findIndex(
- (c) =>
- c.tier >= this.autoDungeonLootTier &&
- dungeonBoard[c.xy.y][c.xy.x].isVisible
- );
- target = this.autoDungeonTracker.chestCoords[index].xy;
- this.autoDungeonTracker.chestCoords.splice(index, 1);
- }
- // Time to fight the boss
- else {
- target = this.autoDungeonTracker.bossCoords;
- this.autoDungeonTracker.floorFinished = true;
- }
+
+ /**
+ * Find next tile on the shortest path towards target via breadth-first search
+ */
+ static pathfindTowardDungeonTarget() {
+ const target = this.autoDungeonTracker.targetCoords;
+ let result = null;
+ if (!target) {
+ return result;
+ }
+ const queue = [target];
+ const visited = new Set(`${target.x}-${target.y}`);
+ while (queue.length) {
+ const p = queue.shift();
+ if (DungeonRunner.map.hasAccessToTile(p)) {
+ result = p;
+ break;
+ }
+ const adjTiles = [[p.x - 1, p.y], [p.x + 1, p.y], [p.x, p.y - 1], [p.x, p.y + 1]];
+ for (let [nx, ny] of adjTiles) {
+ // Enqueue valid tiles not yet considered
+ let xy = `${nx}-${ny}`;
+ if (0 <= nx && nx < this.autoDungeonTracker.floorSize && 0 <= ny && ny < this.autoDungeonTracker.floorSize && !visited.has(xy)) {
+ queue.push(new Point(nx, ny, target.floor));
+ visited.add(xy);
+ }
+ }
+ }
+ return result;
}
- return target;
- }
-
- /**
- * Find next tile on the shortest path towards target via breadth-first search
- */
- static pathfindTowardDungeonTarget() {
- const target = this.autoDungeonTracker.targetCoords;
- let result = null;
- if (!target) {
- return result;
+
+ /**
+ * Restart dungeon, separate from dungeonWon function so it can be called with a delay
+ */
+ static restartDungeon() {
+ if (App.game.gameState !== GameConstants.GameState.dungeon) {
+ return;
+ } else if (!EnhancedAutoClicker.canStartAutoDungeon()) {
+ MapHelper.moveToTown(DungeonRunner.dungeon.name);
+ return;
+ }
+ this.autoDungeonTracker.dungeonFinished = false;
+
+ // Clear old board to force map visuals refresh
+ DungeonRunner.map.board([]);
+ DungeonRunner.initializeDungeon(DungeonRunner.dungeon);
}
- const queue = [target];
- const visited = new Set(`${target.x}-${target.y}`);
- while (queue.length) {
- const p = queue.shift();
- if (DungeonRunner.map.hasAccessToTile(p)) {
- result = p;
- break;
- }
- const adjTiles = [
- [p.x - 1, p.y],
- [p.x + 1, p.y],
- [p.x, p.y - 1],
- [p.x, p.y + 1],
- ];
- for (let [nx, ny] of adjTiles) {
- // Enqueue valid tiles not yet considered
- let xy = `${nx}-${ny}`;
- if (
- 0 <= nx &&
- nx < this.autoDungeonTracker.floorSize &&
- 0 <= ny &&
- ny < this.autoDungeonTracker.floorSize &&
- !visited.has(xy)
- ) {
- queue.push(new Point(nx, ny, target.floor));
- visited.add(xy);
- }
- }
+
+ /**
+ * Override DungeonRunner built-in functions:
+ * -Add dungeon ID tracking to initializeDungeon() for easier mapping
+ * -Add auto dungeon equivalent of dungeonWon() to save on performance by restarting without loading town
+ */
+ static overrideDungeonRunner() {
+ // Differentiate between dungeons for mapping
+ DungeonRunner.dungeonID = 0;
+ const oldInit = DungeonRunner.initializeDungeon.bind(DungeonRunner);
+ DungeonRunner.initializeDungeon = function (...args) {
+ DungeonRunner.dungeonID++;
+ return oldInit(...args);
+ }
+
+ DungeonRunner.dungeonWonNormal = DungeonRunner.dungeonWon;
+ // Version with integrated auto-restart to avoid loading town in between dungeons
+ DungeonRunner.dungeonWonAuto = function () {
+ if (!DungeonRunner.dungeonFinished()) {
+ DungeonRunner.dungeonFinished(true);
+ // First time clearing dungeon
+ if (!App.game.statistics.dungeonsCleared[GameConstants.getDungeonIndex(DungeonRunner.dungeon.name)]()) {
+ DungeonRunner.dungeon.rewardFunction();
+ }
+ GameHelper.incrementObservable(App.game.statistics.dungeonsCleared[GameConstants.getDungeonIndex(DungeonRunner.dungeon.name)]);
+
+ if (EnhancedAutoClicker.autoDungeonTracker.stopAfterFinishing) {
+ EnhancedAutoClicker.toggleAutoDungeon();
+ }
+
+ if (EnhancedAutoClicker.autoDungeonState() && EnhancedAutoClicker.canStartAutoDungeon()) {
+ // Restart the dungeon with a delay, giving Defeat Dungeon Boss quests time to update
+ EnhancedAutoClicker.autoDungeonTracker.dungeonFinished = true;
+ setTimeout(() => EnhancedAutoClicker.restartDungeon(), 50);
+ } else {
+ if (!DungeonRunner.hasEnoughTokens()) {
+ // Notify player if they've run out of tokens
+ Notifier.notify({
+ type: NotificationConstants.NotificationOption.warning,
+ title: 'Enhanced Auto Clicker',
+ message: `Auto Dungeon mode ran out of dungeon tokens.`,
+ timeout: GameConstants.DAY,
+ });
+ }
+ if (EnhancedAutoClicker.autoDungeonState()) {
+ EnhancedAutoClicker.toggleAutoDungeon();
+ }
+ // Can't continue, exit auto dungeon mode
+ MapHelper.moveToTown(DungeonRunner.dungeon.name);
+ }
+ }
+ }
+ // Only use our version when auto dungeon is running
+ DungeonRunner.dungeonWon = function (...args) {
+ if (EnhancedAutoClicker.autoClickState() && EnhancedAutoClicker.autoDungeonState()) {
+ DungeonRunner.dungeonWonAuto(...args);
+ } else {
+ DungeonRunner.dungeonWonNormal(...args);
+ }
+ }
}
- return result;
- }
-
- /**
- * Restart dungeon, separate from dungeonWon function so it can be called with a delay
- */
- static restartDungeon() {
- if (App.game.gameState !== GameConstants.GameState.dungeon) {
- return;
- } else if (!EnhancedAutoClicker.canStartAutoDungeon()) {
- MapHelper.moveToTown(DungeonRunner.dungeon.name);
- return;
- } else if (!EnhancedAutoClicker.canStartAutoDungeon()) {
- MapHelper.moveToTown(DungeonRunner.dungeon.name);
- return;
+
+ /* Clicker statistics calculator */
+
+ /**
+ * Resets calculator loop, stats trackers, and calculator display
+ * -If auto clicker is running, restarts calculator
+ */
+ static resetCalculator() {
+ clearInterval(this.autoClickCalcLoop);
+ this.autoClickCalcTracker.lastUpdate = [Date.now()];
+ this.autoClickCalcTracker.ticks = [0];
+ this.autoClickCalcTracker.clicks = [App.game.statistics.clickAttacks()];
+ this.autoClickCalcTracker.enemies = [App.game.statistics.totalPokemonDefeated()];
+ this.calculateAreaHealth();
+ document.getElementById('auto-click-info').innerHTML = `${this.autoClickCalcEfficiencyDisplayMode == 0 ? 'Clicker Efficiency' : 'Ticks/s'}:
-
+ ${this.autoClickCalcDamageDisplayMode == 0 ? 'Click Attacks/s' : 'DPS'}:
-
+ Req. ${this.autoClickCalcDamageDisplayMode == 0 ? 'Clicks' : 'Click Damage'}:
-
+ `;
+ if (this.autoClickState()) {
+ // Start calculator
+ this.autoClickCalcLoop = setInterval(() => EnhancedAutoClicker.calcClickStats(), 1000);
+ }
}
- this.autoDungeonTracker.dungeonFinished = false;
-
- // Clear old board to force map visuals refresh
- DungeonRunner.map.board([]);
- DungeonRunner.initializeDungeon(DungeonRunner.dungeon);
- }
-
- /**
- * Override DungeonRunner built-in functions:
- * -Add dungeon ID tracking to initializeDungeon() for easier mapping
- * -Add auto dungeon equivalent of dungeonWon() to save on performance by restarting without loading town
- */
- static overrideDungeonRunner() {
- // Differentiate between dungeons for mapping
- DungeonRunner.dungeonID = 0;
- const oldInit = DungeonRunner.initializeDungeon.bind(DungeonRunner);
- DungeonRunner.initializeDungeon = function (...args) {
- DungeonRunner.dungeonID++;
- return oldInit(...args);
- };
- DungeonRunner.dungeonWonNormal = DungeonRunner.dungeonWon;
- // Version with integrated auto-restart to avoid loading town in between dungeons
- DungeonRunner.dungeonWonAuto = function () {
- if (!DungeonRunner.dungeonFinished()) {
- DungeonRunner.dungeonFinished(true);
- // First time clearing dungeon
- if (
- !App.game.statistics.dungeonsCleared[
- GameConstants.getDungeonIndex(DungeonRunner.dungeon.name)
- ]()
- ) {
- DungeonRunner.dungeon.rewardFunction();
- }
- GameHelper.incrementObservable(
- App.game.statistics.dungeonsCleared[
- GameConstants.getDungeonIndex(DungeonRunner.dungeon.name)
- ]
- );
+ /**
+ * Displays the following statistics, averaged over the last (up to) ten seconds:
+ * -Percentage of ticksPerSecond the autoclicker is actually executing
+ * -Clicks per second or damage per second, depending on display mode
+ * -Required number of clicks or click attack damage to one-shot the current location, depending on display mode
+ * --Ignores dungeon bosses and chest health increases
+ * -Enemies defeated per second
+ * Statistics are reset when the player changes locations
+ */
+ static calcClickStats() {
+ if (this.hasPlayerMoved()) {
+ // Reset statistics on area / game state change
+ this.resetCalculator();
+ return;
+ }
+ var elem;
+ var clickDamage = App.game.party.calculateClickAttack(true);
+ var actualElapsed = (Date.now() - this.autoClickCalcTracker.lastUpdate.at(-1)) / (1000 * this.autoClickCalcTracker.lastUpdate.length);
+
+ // Percentage of maximum ticksPerSecond
+ elem = document.getElementById('tick-efficiency');
+ var avgTicks = this.autoClickCalcTracker.ticks.reduce((a, b) => a + b, 0) / this.autoClickCalcTracker.ticks.length;
+ avgTicks = avgTicks / actualElapsed;
+ if (this.autoClickCalcEfficiencyDisplayMode == 1) {
+ // display ticks mode
+ elem.innerHTML = avgTicks.toLocaleString('en-US', {maximumFractionDigits: 1} );
+ elem.style.color = 'gold';
+ } else {
+ // display percentage mode
+ var tickFraction = avgTicks / this.ticksPerSecond;
+ elem.innerHTML = tickFraction.toLocaleString('en-US', {style: 'percent', maximumFractionDigits: 0} );
+ elem.style.color = 'gold';
+ }
- if (EnhancedAutoClicker.autoDungeonTracker.stopAfterFinishing) {
- EnhancedAutoClicker.toggleAutoDungeon();
+ // Average clicks/damage per second
+ elem = document.getElementById('clicks-per-second');
+ var avgClicks = (App.game.statistics.clickAttacks() - this.autoClickCalcTracker.clicks.at(-1)) / this.autoClickCalcTracker.clicks.length;
+ avgClicks = avgClicks / actualElapsed;
+ if (this.autoClickCalcDamageDisplayMode == 1) {
+ // display damage mode
+ var avgDPS = avgClicks * clickDamage;
+ elem.innerHTML = avgDPS.toLocaleString('en-US', {maximumFractionDigits: 0});
+ elem.style.color = 'gold';
+ } else {
+ // display click attacks mode
+ elem.innerHTML = avgClicks.toLocaleString('en-US', {maximumFractionDigits: 1});
+ elem.style.color = 'gold';
}
- if (
- EnhancedAutoClicker.autoDungeonState() &&
- EnhancedAutoClicker.canStartAutoDungeon()
- ) {
- // Restart the dungeon with a delay, giving Defeat Dungeon Boss quests time to update
- EnhancedAutoClicker.autoDungeonTracker.dungeonFinished = true;
- setTimeout(() => EnhancedAutoClicker.restartDungeon(), 50);
+ // Required clicks/click damage
+ elem = document.getElementById('req-clicks');
+ if (this.autoClickCalcTracker.areaHealth == 0) {
+ // can't meaningfully calculate a value for this area/game state
+ elem.innerHTML = '-';
+ elem.style.removeProperty('color');
+ } else if (this.autoClickCalcDamageDisplayMode == 1) {
+ // display damage mode
+ var reqDamage = this.autoClickCalcTracker.areaHealth;
+ elem.innerHTML = reqDamage.toLocaleString('en-US');
+ elem.style.color = (clickDamage * this.autoClickMultiplier >= reqDamage ? 'greenyellow' : 'darkred');
} else {
- if (!DungeonRunner.hasEnoughTokens()) {
- // Notify player if they've run out of tokens
- Notifier.notify({
- type: NotificationConstants.NotificationOption.warning,
- title: 'Enhanced Auto Clicker',
- message: `Auto Dungeon mode ran out of dungeon tokens.`,
- timeout: GameConstants.DAY,
- });
- }
- if (EnhancedAutoClicker.autoDungeonState()) {
- EnhancedAutoClicker.toggleAutoDungeon();
- }
- // Can't continue, exit auto dungeon mode
- MapHelper.moveToTown(DungeonRunner.dungeon.name);
- }
- }
- };
- // Only use our version when auto dungeon is running
- DungeonRunner.dungeonWon = function (...args) {
- if (
- EnhancedAutoClicker.autoClickState() &&
- EnhancedAutoClicker.autoDungeonState()
- ) {
- DungeonRunner.dungeonWonAuto(...args);
- } else {
- DungeonRunner.dungeonWonNormal(...args);
- }
- };
- }
-
- /* Clicker statistics calculator */
-
- /**
- * Resets calculator loop, stats trackers, and calculator display
- * -If auto clicker is running, restarts calculator
- */
- static resetCalculator() {
- clearInterval(this.autoClickCalcLoop);
- this.autoClickCalcTracker.lastUpdate = [Date.now()];
- this.autoClickCalcTracker.ticks = [0];
- this.autoClickCalcTracker.clicks = [App.game.statistics.clickAttacks()];
- this.autoClickCalcTracker.enemies = [
- App.game.statistics.totalPokemonDefeated(),
- ];
- this.calculateAreaHealth();
- document.getElementById('auto-click-info').innerHTML = `${
- this.autoClickCalcEfficiencyDisplayMode == 0
- ? 'Clicker Efficiency'
- : 'Ticks/s'
- }:
-
- ${
- this.autoClickCalcDamageDisplayMode == 0
- ? 'Click Attacks/s'
- : 'DPS'
- }:
-
- Req. ${
- this.autoClickCalcDamageDisplayMode == 0
- ? 'Clicks'
- : 'Click Damage'
- }:
-
- `;
- if (this.autoClickState()) {
- // Start calculator
- this.autoClickCalcLoop = setInterval(
- () => EnhancedAutoClicker.calcClickStats(),
- 1000
- );
- }
- }
-
- /**
- * Displays the following statistics, averaged over the last (up to) ten seconds:
- * -Percentage of ticksPerSecond the autoclicker is actually executing
- * -Clicks per second or damage per second, depending on display mode
- * -Required number of clicks or click attack damage to one-shot the current location, depending on display mode
- * --Ignores dungeon bosses and chest health increases
- * -Enemies defeated per second
- * Statistics are reset when the player changes locations
- */
- static calcClickStats() {
- if (this.hasPlayerMoved()) {
- // Reset statistics on area / game state change
- this.resetCalculator();
- return;
- }
- var elem;
- var clickDamage = App.game.party.calculateClickAttack(true);
- var actualElapsed =
- (Date.now() - this.autoClickCalcTracker.lastUpdate.at(-1)) /
- (1000 * this.autoClickCalcTracker.lastUpdate.length);
-
- // Percentage of maximum ticksPerSecond
- elem = document.getElementById('tick-efficiency');
- var avgTicks =
- this.autoClickCalcTracker.ticks.reduce((a, b) => a + b, 0) /
- this.autoClickCalcTracker.ticks.length;
- avgTicks = avgTicks / actualElapsed;
- if (this.autoClickCalcEfficiencyDisplayMode == 1) {
- // display ticks mode
- elem.innerHTML = avgTicks.toLocaleString('en-US', {
- maximumFractionDigits: 1,
- });
- elem.style.color = 'gold';
- } else {
- // display percentage mode
- var tickFraction = avgTicks / this.ticksPerSecond;
- elem.innerHTML = tickFraction.toLocaleString('en-US', {
- style: 'percent',
- maximumFractionDigits: 0,
- });
- elem.style.color = 'gold';
- }
+ // display clicks mode
+ var reqClicks = Math.max((this.autoClickCalcTracker.areaHealth / clickDamage), 1);
+ reqClicks = Math.ceil(reqClicks * 10) / 10; // round up to one decimal point
+ elem.innerHTML = reqClicks.toLocaleString('en-US', {maximumFractionDigits: 1});
+ elem.style.color = (reqClicks <= this.autoClickMultiplier ? 'greenyellow' : 'darkred');
+ }
- // Average clicks/damage per second
- elem = document.getElementById('clicks-per-second');
- var avgClicks =
- (App.game.statistics.clickAttacks() -
- this.autoClickCalcTracker.clicks.at(-1)) /
- this.autoClickCalcTracker.clicks.length;
- avgClicks = avgClicks / actualElapsed;
- if (this.autoClickCalcDamageDisplayMode == 1) {
- // display damage mode
- var avgDPS = avgClicks * clickDamage;
- elem.innerHTML = avgDPS.toLocaleString('en-US', {
- maximumFractionDigits: 0,
- });
- elem.style.color = 'gold';
- } else {
- // display click attacks mode
- elem.innerHTML = avgClicks.toLocaleString('en-US', {
- maximumFractionDigits: 1,
- });
- elem.style.color = 'gold';
+ // Enemies per second
+ elem = document.getElementById('enemies-per-second');
+ var avgEnemies = (App.game.statistics.totalPokemonDefeated() - this.autoClickCalcTracker.enemies.at(-1)) / this.autoClickCalcTracker.enemies.length;
+ avgEnemies = avgEnemies / actualElapsed;
+ elem.innerHTML = avgEnemies.toLocaleString('en-US', {maximumFractionDigits: 1});
+ elem.style.color = 'gold';
+
+ // Make room for next second's stats tracking
+ // Add new entries to start of array for easier incrementing
+ this.autoClickCalcTracker.ticks.unshift(0);
+ if (this.autoClickCalcTracker.ticks.length > 10) {
+ this.autoClickCalcTracker.ticks.pop();
+ }
+ this.autoClickCalcTracker.clicks.unshift(App.game.statistics.clickAttacks());
+ if (this.autoClickCalcTracker.clicks.length > 10) {
+ this.autoClickCalcTracker.clicks.pop();
+ }
+ this.autoClickCalcTracker.enemies.unshift(App.game.statistics.totalPokemonDefeated());
+ if (this.autoClickCalcTracker.enemies.length > 10) {
+ this.autoClickCalcTracker.enemies.pop();
+ }
+ this.autoClickCalcTracker.lastUpdate.unshift(Date.now());
+ if (this.autoClickCalcTracker.lastUpdate.length > 10) {
+ this.autoClickCalcTracker.lastUpdate.pop();
+ }
}
- // Required clicks/click damage
- elem = document.getElementById('req-clicks');
- if (this.autoClickCalcTracker.areaHealth == 0) {
- // can't meaningfully calculate a value for this area/game state
- elem.innerHTML = '-';
- elem.style.removeProperty('color');
- } else if (this.autoClickCalcDamageDisplayMode == 1) {
- // display damage mode
- var reqDamage = this.autoClickCalcTracker.areaHealth;
- elem.innerHTML = reqDamage.toLocaleString('en-US');
- elem.style.color =
- clickDamage * this.autoClickMultiplier >= reqDamage
- ? 'greenyellow'
- : 'darkred';
- } else {
- // display clicks mode
- var reqClicks = Math.max(
- this.autoClickCalcTracker.areaHealth / clickDamage,
- 1
- );
- reqClicks = Math.ceil(reqClicks * 10) / 10; // round up to one decimal point
- elem.innerHTML = reqClicks.toLocaleString('en-US', {
- maximumFractionDigits: 1,
- });
- elem.style.color =
- reqClicks <= this.autoClickMultiplier ? 'greenyellow' : 'darkred';
- }
+ /**
+ * Check whether player state or location has changed
+ */
+ static hasPlayerMoved() {
+ var moved = false;
+ let newState = App.game.gameState;
- // Enemies per second
- elem = document.getElementById('enemies-per-second');
- var avgEnemies =
- (App.game.statistics.totalPokemonDefeated() -
- this.autoClickCalcTracker.enemies.at(-1)) /
- this.autoClickCalcTracker.enemies.length;
- avgEnemies = avgEnemies / actualElapsed;
- elem.innerHTML = avgEnemies.toLocaleString('en-US', {
- maximumFractionDigits: 1,
- });
- elem.style.color = 'gold';
+ if (this.autoClickCalcTracker.playerState != newState) {
+ this.autoClickCalcTracker.playerState = newState;
+ moved = true;
+ }
+ let newLocation;
+ if (App.game.gameState === GameConstants.GameState.gym) {
+ newLocation = GymRunner.gymObservable().leaderName;
+ } else if (App.game.gameState === GameConstants.GameState.dungeon) {
+ newLocation = DungeonRunner.dungeon.name;
+ } else if (App.game.gameState === GameConstants.GameState.temporaryBattle) {
+ newLocation = TemporaryBattleRunner.battleObservable().name;
+ } else {
+ // Conveniently, player.route = 0 when not on a route
+ newLocation = player.route || player.town.name;
+ }
+ if (this.autoClickCalcTracker.playerLocation != newLocation) {
+ this.autoClickCalcTracker.playerLocation = newLocation;
+ moved = true;
+ }
- // Make room for next second's stats tracking
- // Add new entries to start of array for easier incrementing
- this.autoClickCalcTracker.ticks.unshift(0);
- if (this.autoClickCalcTracker.ticks.length > 10) {
- this.autoClickCalcTracker.ticks.pop();
- }
- this.autoClickCalcTracker.clicks.unshift(
- App.game.statistics.clickAttacks()
- );
- if (this.autoClickCalcTracker.clicks.length > 10) {
- this.autoClickCalcTracker.clicks.pop();
- }
- this.autoClickCalcTracker.enemies.unshift(
- App.game.statistics.totalPokemonDefeated()
- );
- if (this.autoClickCalcTracker.enemies.length > 10) {
- this.autoClickCalcTracker.enemies.pop();
- }
- this.autoClickCalcTracker.lastUpdate.unshift(Date.now());
- if (this.autoClickCalcTracker.lastUpdate.length > 10) {
- this.autoClickCalcTracker.lastUpdate.pop();
- }
- }
-
- /**
- * Check whether player state or location has changed
- */
- static hasPlayerMoved() {
- var moved = false;
- let newState = App.game.gameState;
-
- if (this.autoClickCalcTracker.playerState != newState) {
- this.autoClickCalcTracker.playerState = newState;
- moved = true;
- }
- let newLocation;
- if (App.game.gameState === GameConstants.GameState.gym) {
- newLocation = GymRunner.gymObservable().leaderName;
- } else if (App.game.gameState === GameConstants.GameState.dungeon) {
- newLocation = DungeonRunner.dungeon.name;
- } else if (App.game.gameState === GameConstants.GameState.temporaryBattle) {
- newLocation = TemporaryBattleRunner.battleObservable().name;
- } else {
- // Conveniently, player.route = 0 when not on a route
- newLocation = player.route || player.town.name;
- }
- if (this.autoClickCalcTracker.playerLocation != newLocation) {
- this.autoClickCalcTracker.playerLocation = newLocation;
- moved = true;
+ return moved;
}
- return moved;
- }
-
- /**
- * Calculate max Pokemon health for the current route/gym/dungeon
- * -Ignores dungeon boss HP and chest HP increases
- */
- static calculateAreaHealth() {
- // Calculate area max hp
- if (App.game.gameState === GameConstants.GameState.fighting) {
- this.autoClickCalcTracker.areaHealth = PokemonFactory.routeHealth(
- player.route,
- player.region
- );
- // Adjust for route health variation (adapted from PokemonFactory.generateWildPokemon)
- const pokeHP = [
- ...new Set(
- Object.values(Routes.getRoute(player.region, player.route).pokemon)
- .flat()
- .flatMap((p) => p.pokemon ?? p)
- ),
- ].map((p) => pokemonMap[p].base.hitpoints);
- const averageHP = pokeHP.reduce((s, a) => s + a, 0) / pokeHP.length;
- const highestHP = pokeHP.reduce((m, a) => Math.max(m, a), 0);
- this.autoClickCalcTracker.areaHealth = Math.round(
- this.autoClickCalcTracker.areaHealth *
- (0.9 + highestHP / averageHP / 10)
- );
- } else if (App.game.gameState === GameConstants.GameState.gym) {
- // Get highest health gym pokemon
- this.autoClickCalcTracker.areaHealth = GymRunner.gymObservable()
- .getPokemonList()
- .reduce((a, b) => Math.max(a, b.maxHealth), 0);
- } else if (App.game.gameState === GameConstants.GameState.dungeon) {
- this.autoClickCalcTracker.areaHealth = DungeonRunner.dungeon.baseHealth;
- } else if (App.game.gameState === GameConstants.GameState.temporaryBattle) {
- // Get highest health trainer pokemon
- this.autoClickCalcTracker.areaHealth =
- TemporaryBattleRunner.battleObservable()
- .getPokemonList()
- .reduce((a, b) => Math.max(a, b.maxHealth), 0);
- } else {
- this.autoClickCalcTracker.areaHealth = 0;
+ /**
+ * Calculate max Pokemon health for the current route/gym/dungeon
+ * -Ignores dungeon boss HP and chest HP increases
+ */
+ static calculateAreaHealth() {
+ // Calculate area max hp
+ if (App.game.gameState === GameConstants.GameState.fighting) {
+ this.autoClickCalcTracker.areaHealth = PokemonFactory.routeHealth(player.route, player.region);
+ // Adjust for route health variation (adapted from PokemonFactory.generateWildPokemon)
+ const pokeHP = [...new Set(Object.values(Routes.getRoute(player.region, player.route).pokemon).flat().flatMap(p => p.pokemon ?? p))].map(p => pokemonMap[p].base.hitpoints);
+ const averageHP = pokeHP.reduce((s, a) => s + a, 0) / pokeHP.length;
+ const highestHP = pokeHP.reduce((m, a) => Math.max(m, a), 0);
+ this.autoClickCalcTracker.areaHealth = Math.round(this.autoClickCalcTracker.areaHealth * (0.9 + (highestHP / averageHP) / 10));
+ } else if (App.game.gameState === GameConstants.GameState.gym) {
+ // Get highest health gym pokemon
+ this.autoClickCalcTracker.areaHealth = GymRunner.gymObservable().getPokemonList().reduce((a, b) => Math.max(a, b.maxHealth), 0);
+ } else if (App.game.gameState === GameConstants.GameState.dungeon) {
+ this.autoClickCalcTracker.areaHealth = DungeonRunner.dungeon.baseHealth;
+ } else if (App.game.gameState === GameConstants.GameState.temporaryBattle) {
+ // Get highest health trainer pokemon
+ this.autoClickCalcTracker.areaHealth = TemporaryBattleRunner.battleObservable().getPokemonList().reduce((a, b) => Math.max(a, b.maxHealth), 0);
+ } else {
+ this.autoClickCalcTracker.areaHealth = 0;
+ }
}
- }
}
/**
@@ -1929,165 +1370,143 @@ class EnhancedAutoClicker {
* -Otherwise returns default value
*/
function validateStorage(key, defaultVal, allowed) {
- try {
- var val = localStorage.getItem(key);
- val = JSON.parse(val);
- if (
- val == null ||
- typeof val !== typeof defaultVal ||
- (typeof val == 'object' &&
- val.constructor.name !== defaultVal.constructor.name)
- ) {
- return defaultVal;
- }
- if (allowed != undefined) {
- if (Array.isArray(allowed) && !allowed.includes(val)) {
- return defaultVal;
- }
- if (allowed instanceof Function && !allowed(val)) {
+ try {
+ var val = localStorage.getItem(key);
+ val = JSON.parse(val);
+ if (val == null || typeof val !== typeof defaultVal || (typeof val == 'object' && val.constructor.name !== defaultVal.constructor.name)) {
+ return defaultVal;
+ }
+ if (allowed != undefined) {
+ if (Array.isArray(allowed) && !allowed.includes(val)) {
+ return defaultVal;
+ }
+ if (allowed instanceof Function && !allowed(val)) {
+ return defaultVal;
+ }
+ }
+ return val;
+ } catch {
return defaultVal;
- }
}
- return val;
- } catch {
- return defaultVal;
- }
}
/**
* Creates container for scripts settings in the settings menu, adding scripts tab if it doesn't exist yet
*/
function createScriptSettingsContainer(name) {
- const settingsID = name.replaceAll(/s/g, '').toLowerCase();
- var settingsContainer = document.getElementById('settings-scripts-container');
-
- // Create scripts settings tab if it doesn't exist yet
- if (!settingsContainer) {
- // Fixes the Scripts nav item getting wrapped to the bottom by increasing the max width of the window
- document.querySelector('#settingsModal div').style.maxWidth = '850px';
- // Create and attach script settings tab link
- const settingTabs = document.querySelector('#settingsModal ul.nav-tabs');
- const li = document.createElement('li');
- li.classList.add('nav-item');
- li.innerHTML = `Scripts`;
- settingTabs.appendChild(li);
- // Create and attach script settings tab contents
- const tabContent = document.querySelector('#settingsModal .tab-content');
- scriptSettings = document.createElement('div');
- scriptSettings.classList.add('tab-pane');
- scriptSettings.setAttribute('id', 'settings-scripts');
- tabContent.appendChild(scriptSettings);
- settingsContainer = document.createElement('div');
- settingsContainer.setAttribute('id', 'settings-scripts-container');
- scriptSettings.appendChild(settingsContainer);
- }
-
- // Create settings container
- const settingsTable = document.createElement('table');
- settingsTable.classList.add('table', 'table-striped', 'table-hover', 'm-0');
- const header = document.createElement('thead');
- header.innerHTML = `| ${name} |
`;
- settingsTable.appendChild(header);
- const settingsBody = document.createElement('tbody');
- settingsBody.setAttribute('id', `settings-scripts-${settingsID}`);
- settingsTable.appendChild(settingsBody);
-
- // Insert settings container in alphabetical order
- let settingsList = Array.from(settingsContainer.children);
- let insertBefore = settingsList.find(
- (elem) => elem.querySelector('tbody').id > `settings-scripts-${settingsID}`
- );
- if (insertBefore) {
- insertBefore.before(settingsTable);
- } else {
- settingsContainer.appendChild(settingsTable);
- }
-
- return settingsBody;
+ const settingsID = name.replaceAll(/s/g, '').toLowerCase();
+ var settingsContainer = document.getElementById('settings-scripts-container');
+
+ // Create scripts settings tab if it doesn't exist yet
+ if (!settingsContainer) {
+ // Fixes the Scripts nav item getting wrapped to the bottom by increasing the max width of the window
+ document.querySelector('#settingsModal div').style.maxWidth = '850px';
+ // Create and attach script settings tab link
+ const settingTabs = document.querySelector('#settingsModal ul.nav-tabs');
+ const li = document.createElement('li');
+ li.classList.add('nav-item');
+ li.innerHTML = `Scripts`;
+ settingTabs.appendChild(li);
+ // Create and attach script settings tab contents
+ const tabContent = document.querySelector('#settingsModal .tab-content');
+ scriptSettings = document.createElement('div');
+ scriptSettings.classList.add('tab-pane');
+ scriptSettings.setAttribute('id', 'settings-scripts');
+ tabContent.appendChild(scriptSettings);
+ settingsContainer = document.createElement('div');
+ settingsContainer.setAttribute('id', 'settings-scripts-container');
+ scriptSettings.appendChild(settingsContainer);
+ }
+
+ // Create settings container
+ const settingsTable = document.createElement('table');
+ settingsTable.classList.add('table', 'table-striped', 'table-hover', 'm-0');
+ const header = document.createElement('thead');
+ header.innerHTML = `| ${name} |
`;
+ settingsTable.appendChild(header);
+ const settingsBody = document.createElement('tbody');
+ settingsBody.setAttribute('id', `settings-scripts-${settingsID}`);
+ settingsTable.appendChild(settingsBody);
+
+ // Insert settings container in alphabetical order
+ let settingsList = Array.from(settingsContainer.children);
+ let insertBefore = settingsList.find(elem => elem.querySelector('tbody').id > `settings-scripts-${settingsID}`);
+ if (insertBefore) {
+ insertBefore.before(settingsTable);
+ } else {
+ settingsContainer.appendChild(settingsTable);
+ }
+
+ return settingsBody;
}
function addGlobalStyle(css) {
- var head = document.getElementsByTagName('head')[0];
- if (!head) {
- return;
- }
- var style = document.createElement('style');
- style.type = 'text/css';
- style.innerHTML = css;
- head.appendChild(style);
+ var head = document.getElementsByTagName('head')[0];
+ if (!head) { return; }
+ var style = document.createElement('style');
+ style.type = 'text/css';
+ style.innerHTML = css;
+ head.appendChild(style);
}
function loadEpheniaScript(scriptName, initFunction) {
- 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();
- } catch (e) {
- console.error(
- `Error while initializing '${scriptName}' userscript:\n${e}`
- );
- Notifier.notify({
- type: NotificationConstants.NotificationOption.warning,
- title: scriptName,
- message: `The '${scriptName}' userscript crashed while loading. Check for updates or disable the script, then restart the game.\n\nReport script issues to the script developer, not to the Pokéclicker team.`,
- timeout: GameConstants.DAY,
- });
+ 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();
+ } catch (e) {
+ console.error(`Error while initializing '${scriptName}' userscript:\n${e}`);
+ Notifier.notify({
+ type: NotificationConstants.NotificationOption.warning,
+ title: scriptName,
+ message: `The '${scriptName}' userscript crashed while loading. Check for updates or disable the script, then restart the game.\n\nReport script issues to the script developer, not to the Pokéclicker team.`,
+ timeout: GameConstants.DAY,
+ });
+ }
+ });
+ hasInitialized = true;
}
- }
- );
- hasInitialized = true;
- }
- return result;
- };
- }
-
- // 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,
- });
- let number = 2;
- while (
- windowObject.epheniaScriptInitializers[`${scriptName} ${number}`] !==
- undefined
- ) {
- number++;
+ return result;
+ }
+ }
+
+ // 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,
+ });
+ let number = 2;
+ while (windowObject.epheniaScriptInitializers[`${scriptName} ${number}`] !== undefined) {
+ number++;
+ }
+ scriptName = `${scriptName} ${number}`;
}
- scriptName = `${scriptName} ${number}`;
- }
- // Add initializer for this particular script
- windowObject.epheniaScriptInitializers[scriptName] = initFunction;
+ // Add initializer for this particular script
+ windowObject.epheniaScriptInitializers[scriptName] = initFunction;
}
-if (
- !App.isUsingClient ||
- localStorage.getItem('enhancedautoclicker') === 'true'
-) {
- if (!App.isUsingClient) {
- // Necessary for userscript managers
- unsafeWindow.EnhancedAutoClicker = EnhancedAutoClicker;
- }
- loadEpheniaScript(
- 'enhancedautoclicker',
- EnhancedAutoClicker.initAutoClicker.bind(EnhancedAutoClicker)
- );
- $(document).ready(() => {
- EnhancedAutoClicker.initOverrides();
- });
+if (!App.isUsingClient || localStorage.getItem('enhancedautoclicker') === 'true') {
+ if (!App.isUsingClient) {
+ // Necessary for userscript managers
+ unsafeWindow.EnhancedAutoClicker = EnhancedAutoClicker;
+ }
+ loadEpheniaScript('enhancedautoclicker', EnhancedAutoClicker.initAutoClicker.bind(EnhancedAutoClicker));
+ $(document).ready(() => {
+ EnhancedAutoClicker.initOverrides();
+ });
}