diff --git a/src/lib/BattleFrontier.js b/src/lib/BattleFrontier.js index ad895426..d999c889 100644 --- a/src/lib/BattleFrontier.js +++ b/src/lib/BattleFrontier.js @@ -45,6 +45,12 @@ */ static ForceStop() { + if (App.game.gameState != GameConstants.GameState.battleFrontier) + { + // Nothing to do + return; + } + // Stop the automation this.__internal__toggleBattleFrontierFight(false); diff --git a/src/lib/Dungeon.js b/src/lib/Dungeon.js index ebe928ae..4f3aaad0 100644 --- a/src/lib/Dungeon.js +++ b/src/lib/Dungeon.js @@ -14,9 +14,8 @@ class AutomationDungeon static InternalModes = { None: 0, - StopAfterThisRun: 1, - ForceDungeonCompletion: 2, - ForcePokemonFight: 3 + ForceDungeonCompletion: 1, + ForcePokemonFight: 2 }; static AutomationRequestedMode = this.InternalModes.None; @@ -48,10 +47,20 @@ class AutomationDungeon } } + /** + * @brief Asks the Dungeon automation to stop after the current run + */ + static stopAfterThisRun() + { + this.__internal__stopAfterThisRun = true; + } + /*********************************************************************\ |*** Internal members, should never be used by other classes ***| \*********************************************************************/ + static __internal__stopAfterThisRun = false; + static __internal__CatchModes = { Uncaught: { id: 0, shiny: false, shadow: false }, UncaughtShiny: { id: 1, shiny: true, shadow: false }, @@ -256,9 +265,10 @@ class AutomationDungeon } // Cleanup StopAfterThisRun internal mode that was set while the dungeon was not running - if (this.AutomationRequestedMode == this.InternalModes.StopAfterThisRun) + if (this.__internal__stopAfterThisRun) { this.AutomationRequestedMode = this.InternalModes.None; + this.__internal__stopAfterThisRun = false; } if (enable) @@ -280,6 +290,10 @@ class AutomationDungeon clearInterval(this.__internal__autoDungeonLoop); this.__internal__autoDungeonLoop = null; + // Reset any automation mode + this.AutomationRequestedMode = this.InternalModes.None; + this.__internal__stopAfterThisRun = false; + // Disable automation filter Automation.Utils.Pokeball.disableAutomationFilter(); } @@ -326,11 +340,11 @@ class AutomationDungeon // Reset button status if either: // - it was requested by another module // - the pokedex is full for this dungeon, and it has been ask for - if (!forceDungeonProcessing - && ((this.AutomationRequestedMode == this.InternalModes.StopAfterThisRun) - || this.__internal__playerActionOccured - || ((Automation.Utils.LocalStorage.getValue(this.Settings.StopOnPokedex) === "true") - && this.__internal__isDungeonCompleted()))) + if (this.__internal__stopAfterThisRun + || (!forceDungeonProcessing + && (this.__internal__playerActionOccured + || ((Automation.Utils.LocalStorage.getValue(this.Settings.StopOnPokedex) === "true") + && this.__internal__isDungeonCompleted())))) { if (this.__internal__playerActionOccured) { diff --git a/src/lib/Focus.js b/src/lib/Focus.js index b502c707..448e74f9 100644 --- a/src/lib/Focus.js +++ b/src/lib/Focus.js @@ -58,7 +58,7 @@ class AutomationFocus // Ask the dungeon auto-fight to stop, if the feature is enabled if (Automation.Utils.LocalStorage.getValue(Automation.Dungeon.Settings.FeatureEnabled) === "true") { - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.StopAfterThisRun; + Automation.Dungeon.stopAfterThisRun(); return false; } @@ -399,7 +399,7 @@ class AutomationFocus if (this.__internal__activeFocus.stop !== undefined) { // Reset any dungeon request that might have occured - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.StopAfterThisRun; + Automation.Dungeon.stopAfterThisRun(); this.__internal__activeFocus.stop(); } @@ -485,7 +485,11 @@ class AutomationFocus const isUnlockedCallback = function (){ return App.game.gems.canAccess(); }; this.__internal__addFunctionalitySeparator("==== Gems ====", isUnlockedCallback); - for (const gemType of Array(Gems.nTypes).keys()) + // Sort the types alphabetically + const gemListCopy = [...Array(Gems.nTypes).keys()]; + gemListCopy.sort((a, b) => (PokemonType[a] < PokemonType[b]) ? -1 : 1); + + for (const gemType of gemListCopy) { const gemTypeName = PokemonType[gemType]; diff --git a/src/lib/Focus/Achievements.js b/src/lib/Focus/Achievements.js index 002fa017..ad1ffaf4 100644 --- a/src/lib/Focus/Achievements.js +++ b/src/lib/Focus/Achievements.js @@ -89,7 +89,7 @@ class AutomationFocusAchievements clearInterval(this.__internal__achievementLoop); this.__internal__achievementLoop = null; - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.StopAfterThisRun; + Automation.Dungeon.stopAfterThisRun(); Automation.Menu.forceAutomationState(Automation.Gym.Settings.FeatureEnabled, false); @@ -163,12 +163,12 @@ class AutomationFocusAchievements if (Automation.Utils.isInstanceOf(this.__internal__currentAchievement.property, "RouteKillRequirement")) { - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.StopAfterThisRun; + Automation.Dungeon.stopAfterThisRun(); this.__internal__workOnRouteKillRequirement(); } else if (Automation.Utils.isInstanceOf(this.__internal__currentAchievement.property, "ClearGymRequirement")) { - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.StopAfterThisRun; + Automation.Dungeon.stopAfterThisRun(); this.__internal__workOnClearGymRequirement(); } else if (Automation.Utils.isInstanceOf(this.__internal__currentAchievement.property, "ClearDungeonRequirement")) @@ -258,11 +258,11 @@ class AutomationFocusAchievements return; } - // Bypass user settings like the stop on pokedex one - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.ForceDungeonCompletion; - // Enable auto dungeon fight Automation.Menu.forceAutomationState(Automation.Dungeon.Settings.FeatureEnabled, true); + + // Bypass user settings like the stop on pokedex one + Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.ForceDungeonCompletion; } /** diff --git a/src/lib/Focus/PokerusCure.js b/src/lib/Focus/PokerusCure.js index e17d23be..5c1c4113 100644 --- a/src/lib/Focus/PokerusCure.js +++ b/src/lib/Focus/PokerusCure.js @@ -226,11 +226,11 @@ class AutomationFocusPokerusCure return; } - // Bypass user settings, especially the 'Skip fights' one - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.ForcePokemonFight; - // Enable auto dungeon fight Automation.Menu.forceAutomationState(Automation.Dungeon.Settings.FeatureEnabled, true); + + // Bypass user settings, especially the 'Skip fights' one + Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.ForcePokemonFight; } } diff --git a/src/lib/Focus/Quests.js b/src/lib/Focus/Quests.js index e277c07d..ebb515ce 100644 --- a/src/lib/Focus/Quests.js +++ b/src/lib/Focus/Quests.js @@ -78,6 +78,7 @@ class AutomationFocusQuests this.__internal__questLabels["MineLayersQuest"] = "Mine layer in the Underground."; this.__internal__questLabels["MineItemsQuest"] = "Mine item in the Underground."; this.__internal__questLabels["CatchShiniesQuest"] = "Catch 1 shiny Pokémon."; + this.__internal__questLabels["CatchShadowsQuest"] = "Catch Shadow Pokémon."; this.__internal__questLabels["DefeatGymQuest"] = "Defeat times."; this.__internal__questLabels["DefeatDungeonQuest"] = "Defeat the times."; this.__internal__questLabels["UsePokeballQuest"] = "Use ."; @@ -217,7 +218,7 @@ class AutomationFocusQuests // Reset demands Automation.Farm.ForcePlantBerriesAsked = null; - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.StopAfterThisRun; + Automation.Dungeon.stopAfterThisRun(); // Reset other modes status Automation.Click.toggleAutoClick(); @@ -251,9 +252,6 @@ class AutomationFocusQuests */ static __internal__questLoop() { - // Make sure to always have some balls to catch pokemons - this.__internal__tryBuyBallIfUnderThreshold(Automation.Focus.__pokeballToUseSelectElem.value, 10); - this.__internal__claimCompletedQuests(); this.__internal__selectNewQuests(); @@ -371,7 +369,7 @@ class AutomationFocusQuests // Already fighting, nothing to do for now if ((App.game.gameState != GameConstants.GameState.battleFrontier) && Automation.Utils.isInInstanceState()) { - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.StopAfterThisRun; + Automation.Dungeon.stopAfterThisRun(); return; } @@ -414,9 +412,16 @@ class AutomationFocusQuests { this.__internal__workOnCapturePokemonTypesQuest(quest); } + else if (Automation.Utils.isInstanceOf(quest, "CatchShadowsQuest")) + { + // Use the 1st available dungeon with shadow pokémons for now + const dungeonName = "Phenac City Battles"; + this.__internal__workOnDefeatDungeonQuest(dungeonName, true); + } else if (Automation.Utils.isInstanceOf(quest, "DefeatDungeonQuest")) { - this.__internal__workOnDefeatDungeonQuest(quest); + const catchShadows = currentQuests.some((quest) => Automation.Utils.isInstanceOf(quest, "CatchShadowsQuest")); + this.__internal__workOnDefeatDungeonQuest(quest.dungeon, catchShadows); } else if (Automation.Utils.isInstanceOf(quest, "DefeatGymQuest")) { @@ -512,7 +517,7 @@ class AutomationFocusQuests /** * @brief Works on a CapturePokemonTypesQuest. * - * It will equip balls to catch already caught pokemons + * It will equip balls to catch any pokemon matching the quest type * and move to the most efficient route for the quest requested pokemon type. * * @param quest: The game's quest object @@ -572,34 +577,45 @@ class AutomationFocusQuests * * @see AutomationDungeon class * - * @param quest: The game's quest object + * @param {string} dungeonName: The name of the dungeon to defeat + * @param {boolean} catchShadows: Set to true if the shadow pokémon catch filter needs to be set, false otherwise */ - static __internal__workOnDefeatDungeonQuest(quest) + static __internal__workOnDefeatDungeonQuest(dungeonName, catchShadows) { // If we don't have enough tokens, go farm some - if (TownList[quest.dungeon].dungeon.tokenCost > App.game.wallet.currencies[Currency.dungeonToken]()) + if (TownList[dungeonName].dungeon.tokenCost > App.game.wallet.currencies[Currency.dungeonToken]()) { this.__internal__workOnUsePokeballQuest(Automation.Focus.__pokeballToUseSelectElem.value); return; } - // Restore pokéball filters - Automation.Utils.Pokeball.disableAutomationFilter(); + if (catchShadows) + { + this.__internal__equipOptimizedLoadout(Automation.Utils.OakItem.Setup.PokemonCatch); + Automation.Utils.Pokeball.restrictCaptureToShadow(true); + Automation.Utils.Pokeball.enableAutomationFilter(); + } + else + { + // Disable pokéball filters + Automation.Utils.Pokeball.disableAutomationFilter(); + } // Move to dungeon if needed - if (!Automation.Utils.Route.isPlayerInTown(quest.dungeon)) + if (!Automation.Utils.Route.isPlayerInTown(dungeonName)) { - Automation.Utils.Route.moveToTown(quest.dungeon); + Automation.Utils.Route.moveToTown(dungeonName); // Let a tick for the menu to show up return; } - // Bypass user settings like the stop on pokedex one - Automation.Dungeon.AutomationRequestedMode = Automation.Dungeon.InternalModes.ForceDungeonCompletion; - // Enable auto dungeon fight Automation.Menu.forceAutomationState(Automation.Dungeon.Settings.FeatureEnabled, true); + + // Bypass user settings like the stop on pokedex one + Automation.Dungeon.AutomationRequestedMode = (catchShadows) ? Automation.Dungeon.InternalModes.ForcePokemonFight + : Automation.Dungeon.InternalModes.ForceDungeonCompletion; } /** @@ -888,6 +904,10 @@ class AutomationFocusQuests if (Automation.Utils.isInstanceOf(a, "DefeatDungeonQuest")) return -1; if (Automation.Utils.isInstanceOf(b, "DefeatDungeonQuest")) return 1; + // Select catch shadow quest after the DefeatDungeonQuest in case said dungeon has shadow pokémons + if (Automation.Utils.isInstanceOf(a, "CatchShadowsQuest")) return -1; + if (Automation.Utils.isInstanceOf(b, "CatchShadowsQuest")) return 1; + if (Automation.Utils.isInstanceOf(a, "DefeatGymQuest")) return -1; if (Automation.Utils.isInstanceOf(b, "DefeatGymQuest")) return 1; diff --git a/src/lib/Hatchery.js b/src/lib/Hatchery.js index 7e0b47fe..27bb7c8f 100644 --- a/src/lib/Hatchery.js +++ b/src/lib/Hatchery.js @@ -334,8 +334,12 @@ class AutomationHatchery let previouslySelectedType = Automation.Utils.LocalStorage.getValue(this.Settings.PrioritizedType); + // Sort the types alphabetically + const gemListCopy = [...Array(Gems.nTypes).keys()]; + gemListCopy.sort((a, b) => (PokemonType[a] < PokemonType[b]) ? -1 : 1); + // Populate the list - for (const gemType of Array(Gems.nTypes).keys()) + for (const gemType of gemListCopy) { opt = document.createElement("option"); diff --git a/src/lib/Utils/Pokeball.js b/src/lib/Utils/Pokeball.js index 214ab0f2..e2b00ed4 100644 --- a/src/lib/Utils/Pokeball.js +++ b/src/lib/Utils/Pokeball.js @@ -64,6 +64,24 @@ class AutomationUtilsPokeball this.enableAutomationFilter(); } + /** + * @brief Restricts the pokemon filter to Shadow pokémons + * + * @param includeAlreadyCaught: Already caught shadow pokémons will be considered as well + */ + static restrictCaptureToShadow(includeAlreadyCaught) + { + // Only consider Shadow pokémons + this.__internal__automationFilter.options.shadow = pokeballFilterOptions.shadow.createSetting(); + + // Filter caught shadow + if (!includeAlreadyCaught) + { + this.__internal__automationFilter.options.caughtShadow = pokeballFilterOptions.caughtShadow.createSetting(); + this.__internal__automationFilter.options.caughtShadow.observableValue(false); + } + } + /** * @brief Restricts the pokemon filter to the given @p pokemonType *