diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp
index 1662f64793..d8f9cd7f6a 100644
--- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp
+++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp
@@ -17,6 +17,10 @@
#include "CommonTools/StartupChecks/StartProgramChecks.h"
#include "Pokemon/Pokemon_Strings.h"
#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
+#include "NintendoSwitch/NintendoSwitch_Settings.h"
+#include "NintendoSwitch/Inference/NintendoSwitch_HomeMenuDetector.h"
+#include "NintendoSwitch/Inference/NintendoSwitch_UpdatePopupDetector.h"
+#include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h"
#include "PokemonFRLG/Inference/PokemonFRLG_SelectionArrowDetector.h"
#include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h"
#include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h"
@@ -97,6 +101,16 @@ RngHelper::RngHelper()
LockMode::UNLOCK_WHILE_RUNNING,
1, 0 // default, min
)
+ , SEED_BUTTON(
+ "Seed Button:
",
+ {
+ {SeedButton::A, "A", "A"},
+ {SeedButton::Start, "Start", "Start"},
+ {SeedButton::L, "L", "L (L=A)"},
+ },
+ LockMode::LOCK_WHILE_RUNNING,
+ SeedButton::A
+ )
, SEED_DELAY(
"Seed Delay Time (ms):
The delay between starting the game and advancing past the title screen. Set this to match your target seed.",
LockMode::LOCK_WHILE_RUNNING,
@@ -107,33 +121,35 @@ RngHelper::RngHelper()
LockMode::UNLOCK_WHILE_RUNNING,
0 // default
)
- , LOAD_ADVANCES(
- "Load Screen Advances (frames):
The number of frames to advance before loading the game.
These pass at the \"normal\" rate compared to other consoles.",
+ , CONTINUE_SCREEN_FRAMES(
+ "Continue Screen Frames:
The number of RNG advances before loading the game.
These pass at the \"normal\" rate compared to other consoles.",
LockMode::LOCK_WHILE_RUNNING,
1000, 192 // default, min
)
- , LOAD_CALIBRATION(
- "Load Screen Advances Calibration (frames):
A \"fine adjustment\" that modifies the frame advances passed on the load screen.
",
+ , CONTINUE_SCREEN_CALIBRATION(
+ "Continue Screen Frames Calibration:
A \"fine adjustment\" that modifies the RNG advances passed on the Continue Screen.
"
+ "Example: if your target advance was 10000 and you hit 10025, you can decrease your calibration value by 25.",
LockMode::UNLOCK_WHILE_RUNNING,
0 // default
)
, INGAME_ADVANCES(
- "In-Game Advances (frames):
The number of frames to advance before triggering the gift/encounter.
These pass at double the rate compared to other consoles, where every 2nd frame is skipped.
Warning: this needs to be long enough to accomodate all in-game button presses prior to the gift/encounter",
+ "In-Game Advances:
The number of in-game RNG advances before triggering the gift/encounter.
These pass at double the rate compared to other consoles, where every frame results in 2 advances.
Warning: this needs to be long enough to accomodate all in-game button presses prior to the gift/encounter",
LockMode::LOCK_WHILE_RUNNING,
12345, 480 // default, min
)
, INGAME_CALIBRATION(
- "In-Game Advances Calibration (frames):
A \"coarse adjustment\" that modifies the frame advances passed after loading the game.
",
+ "In-Game Advances Calibration:
A \"coarse adjustment\" that modifies the RNG advances passed after loading the game.
"
+ "Example: if your target advance was 10000 and you hit 8500, you can increase your calibration value by 1500.",
LockMode::UNLOCK_WHILE_RUNNING,
0 // default
)
, USE_COPYRIGHT_TEXT(
- "Detect Copyright Text:
Start the seed timer only after detecting the copyright text. Can be helpful for improving seed consistency.",
+ "Detect Copyright Text:
Start the seed timer only after detecting the copyright text. Can be helpful if your seeds are inconsistent.",
LockMode::LOCK_WHILE_RUNNING,
true // default
)
, USE_TEACHY_TV(
- "Use Teachy TV:
Opens the Teachy TV to quickly advance in-game frames at 313x speed.
Warning: can result in larger misses.",
+ "Use Teachy TV:
Opens the Teachy TV to quickly advance the RNG at 313x speed.
Warning: can result in larger misses.",
LockMode::LOCK_WHILE_RUNNING,
false // default
)
@@ -157,10 +173,11 @@ RngHelper::RngHelper()
{
PA_ADD_OPTION(TARGET);
PA_ADD_OPTION(NUM_RESETS);
+ PA_ADD_OPTION(SEED_BUTTON);
PA_ADD_OPTION(SEED_DELAY);
PA_ADD_OPTION(SEED_CALIBRATION);
- PA_ADD_OPTION(LOAD_ADVANCES);
- PA_ADD_OPTION(LOAD_CALIBRATION);
+ PA_ADD_OPTION(CONTINUE_SCREEN_FRAMES);
+ PA_ADD_OPTION(CONTINUE_SCREEN_CALIBRATION);
PA_ADD_OPTION(INGAME_ADVANCES);
PA_ADD_OPTION(INGAME_CALIBRATION);
PA_ADD_OPTION(USE_COPYRIGHT_TEXT);
@@ -172,105 +189,7 @@ RngHelper::RngHelper()
namespace{
-void hard_reset(ProControllerContext& context){
- // close the game
- pbf_press_button(context, BUTTON_HOME, 200ms, 1300ms);
- pbf_press_button(context, BUTTON_X, 200ms, 1300ms);
- pbf_press_button(context, BUTTON_A, 200ms, 2800ms);
- // press A to select game
- pbf_press_button(context, BUTTON_A, 200ms, 2300ms);
- // press A to select profile and launch the game
- pbf_press_button(context, BUTTON_A, 200ms, 300ms);
- // return to HOME menu and wait a moment
- pbf_press_button(context, BUTTON_HOME, 125ms, 2000ms);
- // open game
- pbf_press_button(context, BUTTON_A, 125ms, 0ms);
-}
-
-
-uint64_t wait_for_copyright_text(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
- // wait for copyright text to appear
- BlackScreenWatcher black_screen(COLOR_RED);
- context.wait_for_all_requests();
- WallClock start_time = current_time(); // immediately (more or less) after the button press to enter the game
- int black_ret = wait_until(
- env.console, context, 10000ms,
- {black_screen},
- 1ms
- );
- if (black_ret < 0){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Black screen not detected within 10 seconds of starting game.",
- env.console
- );
- }
- BlackScreenOverWatcher copyright_detected(COLOR_RED);
- int ret = wait_until(
- env.console, context, 10000ms,
- {copyright_detected },
- 1ms
- );
- if (ret < 0){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Black screen detected for more than 10 seconds after starting game.",
- env.console
- );
- }
- auto elapsed = current_time() - start_time; // when the copyright text appears
- return std::chrono::duration_cast(elapsed).count();
-}
-
-void set_seed_after_delay(ProControllerContext& context, SimpleIntegerOption& SEED_DELAY, SimpleIntegerOption& SEED_CALIBRATION, int64_t& FIXED_SEED_OFFSET){
- // wait on title screen for the specified delay
- pbf_wait(context, std::chrono::milliseconds(SEED_DELAY + SEED_CALIBRATION + FIXED_SEED_OFFSET));
- // hold A for a few seconds through the transition to the load screen
- pbf_press_button(context, BUTTON_A, 3000ms, 0ms);
-}
-
-void load_game_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& LOAD_DELAY){
- if (LOAD_DELAY < 3200){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "The load screen delay cannot be less than 3200ms (192 frames). Check your load screen calibration.",
- env.console
- );
- }
- pbf_wait(context, std::chrono::milliseconds(LOAD_DELAY - 3000));
- pbf_press_button(context, BUTTON_A, 33ms, 1467ms);
- // skip recap
- pbf_press_button(context, BUTTON_B, 33ms, 2467ms);
- // need to later subtract 4000ms from delay to hit desired number of advances
-}
-
-void wait_with_teachy_tv(ProControllerContext& context, const uint64_t& TEACHY_DELAY){
- // open start menu -> bag -> key items -> Teachy TV -> use
- pbf_press_button(context, BUTTON_PLUS, 200ms, 300ms);
- pbf_move_left_joystick(context, {0, -1}, 200ms, 300ms);
- pbf_move_left_joystick(context, {0, -1}, 200ms, 300ms);
- pbf_press_button(context, BUTTON_A, 200ms, 2300ms);
- pbf_move_left_joystick(context, {+1, 0}, 200ms, 2300ms);
- pbf_press_button(context, BUTTON_A, 200ms, 300ms);
- pbf_press_button(context, BUTTON_A, 200ms, std::chrono::milliseconds(TEACHY_DELAY));
- // close teachy tv -> close bag -> reset start menu cursor position - > close start menu
- pbf_press_button(context, BUTTON_B, 200ms, 2300ms);
- pbf_press_button(context, BUTTON_B, 200ms, 2300ms);
- pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms);
- pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms);
- pbf_press_button(context, BUTTON_B, 200ms, 300ms);
- // total non-teachy delay duration: 13700ms
- // if used in the Safari Zone: 14200ms
-}
-
-void collect_starter_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 7500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Starters: the in-game delay cannot be less than 7500ms (900 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_starter_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// Advance through starter dialogue and wait on "really quite energetic!"
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
@@ -282,17 +201,9 @@ void collect_starter_after_delay(SingleSwitchProgramEnvironment& env, ProControl
// Advance through rival choice
pbf_mash_button(context, BUTTON_B, 5000ms);
context.wait_for_all_requests();
- env.log("Starter collected.");
}
-void collect_magikarp_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 7500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Magikarp: the in-game delay cannot be less than 7500ms (900 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_magikarp_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// Advance through starter dialogue and wait on YES/NO
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
@@ -302,17 +213,9 @@ void collect_magikarp_after_delay(SingleSwitchProgramEnvironment& env, ProContro
// Decline nickname
pbf_mash_button(context, BUTTON_B, 2000ms);
context.wait_for_all_requests();
- env.log("Magikarp collected.");
}
-void collect_hitmon_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 4500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Hitmonchan/Hitmonlee: the in-game delay cannot be less than 4500ms (540 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_hitmon_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// One dialog before accepting
pbf_press_button(context, BUTTON_A, 200ms, std::chrono::milliseconds(INGAME_DELAY - 4200)); // 4000ms + 200ms
// Confirm selection
@@ -320,17 +223,9 @@ void collect_hitmon_after_delay(SingleSwitchProgramEnvironment& env, ProControll
// Decline nickname
pbf_mash_button(context, BUTTON_B, 2000ms);
context.wait_for_all_requests();
- env.log("Hitmonchan/Hitmonlee collected.");
}
-void collect_eevee_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 4000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Eevee: the in-game delay cannot be less than 4000ms (480 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_eevee_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// No dialogue to advance through -- just wait
pbf_wait(context, std::chrono::milliseconds(INGAME_DELAY - 4000));
// Interact with the pokeball
@@ -338,17 +233,9 @@ void collect_eevee_after_delay(SingleSwitchProgramEnvironment& env, ProControlle
// Decline nickname
pbf_mash_button(context, BUTTON_B, 2000ms);
context.wait_for_all_requests();
- env.log("Eevee collected.");
}
-void collect_lapras_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 7500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Lapras: the in-game delay cannot be less than 7500ms (900 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_lapras_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// 3 dialog presses
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
@@ -358,17 +245,9 @@ void collect_lapras_after_delay(SingleSwitchProgramEnvironment& env, ProControll
// Decline nickname and exit dialog
pbf_mash_button(context, BUTTON_B, 7500ms);
context.wait_for_all_requests();
- env.log("Lapras collected.");
}
-void collect_fossil_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 6000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Fossils: the in-game delay cannot be less than 6000ms (720 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_fossil_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// 2 dialog presses
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, std::chrono::milliseconds(INGAME_DELAY - 5700)); // 4000ms + 1500ms + 200ms
@@ -377,17 +256,9 @@ void collect_fossil_after_delay(SingleSwitchProgramEnvironment& env, ProControll
// Decline nickname
pbf_mash_button(context, BUTTON_B, 2000ms);
context.wait_for_all_requests();
- env.log("Fossil Pokemon collected.");
}
-void collect_gamecorner_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY, int SLOT){
- if (INGAME_DELAY < 8500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Game Corner: the in-game delay cannot be less than 8500ms (1020 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_gamecorner_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY, int SLOT){
// 2 dialog presses
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
@@ -405,17 +276,9 @@ void collect_gamecorner_after_delay(SingleSwitchProgramEnvironment& env, ProCont
// decline nickname
pbf_mash_button(context, BUTTON_B, 2000ms);
context.wait_for_all_requests();
- env.log("Game corner prize collected.");
}
-void collect_togepi_egg_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 12000) {
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Togepi: the in-game delay cannot be less than 12000ms (1440 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void collect_togepi_egg_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// 6 dialog presses
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
@@ -428,17 +291,9 @@ void collect_togepi_egg_after_delay(SingleSwitchProgramEnvironment& env, ProCont
// exit dialogue
pbf_mash_button(context, BUTTON_B, 2500ms);
context.wait_for_all_requests();
- env.log("Togepi egg collected.");
}
-void encounter_static_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 5000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Static Encounter: the in-game delay cannot be less than 5000ms (600 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void encounter_static_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// No dialogue to advance through -- just wait in the start menu (avoids extra RNG advances by boulders Mt Ember and Seafoam Islands)
pbf_press_button(context, BUTTON_PLUS, 200ms, 300ms);
pbf_wait(context, std::chrono::milliseconds(INGAME_DELAY - 5000)); // 4000ms + 1000ms
@@ -447,66 +302,34 @@ void encounter_static_after_delay(SingleSwitchProgramEnvironment& env, ProContro
pbf_press_button(context, BUTTON_A, 200ms, 800ms);
pbf_mash_button(context, BUTTON_A, 1000ms); // finishes dialog for the legendary birds
context.wait_for_all_requests();
- env.log("Static encounter started.");
}
-void encounter_snorlax_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 16000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Snorlax: the in-game delay cannot be less than 16000ms (1920 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void encounter_snorlax_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// Interact with Snorlax, YES to PokeFlute, wait on "woke up!"
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, 9800ms); // PokeFlute tune
pbf_press_button(context, BUTTON_A, 200ms, std::chrono::milliseconds(INGAME_DELAY - 15700)); // 4000ms + 1500ms + 10000ms + 200ms
pbf_press_button(context, BUTTON_A, 200ms, 200ms);
context.wait_for_all_requests();
- env.log("Snorlax encounter started.");
}
-void encounter_mewtwo_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 4500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Mewtwo: the in-game delay cannot be less than 4500ms (540 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void encounter_mewtwo_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// one dialogue before the encounter happens
pbf_press_button(context, BUTTON_A, 200ms, std::chrono::milliseconds(INGAME_DELAY - 4200)); // 4000ms + 200ms
// Initiate encounter
pbf_press_button(context, BUTTON_A, 200ms, 200ms);
context.wait_for_all_requests();
- env.log("Hypno encounter started.");
}
-void encounter_hooh_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 4000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Ho-oh: the in-game delay cannot be less than 4000ms (480 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void encounter_hooh_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// No dialogue to advance through -- just wait
pbf_wait(context, std::chrono::milliseconds(INGAME_DELAY - 4000));
// Trigger the encounter (WALK UP)
pbf_move_left_joystick(context, {0, +1}, 800ms, 700ms);
context.wait_for_all_requests();
- env.log("Ho-oh encounter started.");
}
-void encounter_hypno_after_delay(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 13000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Hypno: the in-game delay cannot be less than 13000ms (1560 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void encounter_hypno_after_delay(ProControllerContext& context, const uint64_t& INGAME_DELAY){
// 5 dialog advances, with the 5th needing some extra time
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
pbf_press_button(context, BUTTON_A, 200ms, 1300ms);
@@ -518,37 +341,9 @@ void encounter_hypno_after_delay(SingleSwitchProgramEnvironment& env, ProControl
// Initiate encounter
pbf_press_button(context, BUTTON_A, 200ms, 200ms);
context.wait_for_all_requests();
- env.log("Hypno encounter started.");
}
-void open_party_menu_from_overworld(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
- pbf_press_button(context, BUTTON_PLUS, 200ms, 800ms);
- int ret = move_cursor_to_position(env.console, context, SelectionArrowPositionStartMenu::POKEMON);
- if (ret < 0){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Failed to navigate to POKEMON on the Start menu.",
- env.console
- );
- }
- pbf_press_button(context, BUTTON_A, 200ms, 1800ms);
- context.wait_for_all_requests();
-}
-
-void use_sweet_scent(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE = false){
- if (!SAFARI_ZONE && INGAME_DELAY < 8500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Sweet Scent: the in-game delay cannot be less than 8500ms (1020 frames). Check your in-game advances and calibration.",
- env.console
- );
- }else if (SAFARI_ZONE && INGAME_DELAY < 9500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Sweet Scent: the in-game delay cannot be less than 9500ms (1140 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
+void use_sweet_scent(ProControllerContext& context, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE = false){
// navigate to last party slot
pbf_press_button(context, BUTTON_PLUS, 200ms, 300ms);
pbf_move_left_joystick(context, {0, -1}, 200ms, 300ms);
@@ -563,18 +358,9 @@ void use_sweet_scent(SingleSwitchProgramEnvironment& env, ProControllerContext&
pbf_move_left_joystick(context, {0, -1}, 200ms, std::chrono::milliseconds(INGAME_DELAY - (SAFARI_ZONE ? 7900 : 7400)));
pbf_press_button(context, BUTTON_A, 200ms, 800ms);
context.wait_for_all_requests();
- env.log("Sweet Scent used.");
}
-void use_registered_fishing_rod(SingleSwitchProgramEnvironment& env, ProControllerContext& context, const uint64_t& INGAME_DELAY){
- if (INGAME_DELAY < 5500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Fishing: the in-game delay cannot be less than 5500ms (660 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
- // n
+void use_registered_fishing_rod(ProControllerContext& context, const uint64_t& INGAME_DELAY){
uint32_t rng_wait = 50 * random_u32(0, 20); // helps avoid always hitting "Not even a nibble" (?)
pbf_wait(context, std::chrono::milliseconds(rng_wait));
pbf_press_button(context, BUTTON_MINUS, 200ms, std::chrono::milliseconds(INGAME_DELAY - rng_wait - 4200));
@@ -584,7 +370,7 @@ void use_registered_fishing_rod(SingleSwitchProgramEnvironment& env, ProControll
void go_to_starter_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
// Navigate to summary (1st party slot)
- open_party_menu_from_overworld(env, context);
+ open_party_menu_from_overworld(env.console, context);
pbf_press_button(context, BUTTON_A, 200ms, 1000ms);
pbf_press_button(context, BUTTON_A, 200ms, 2300ms);
}
@@ -599,7 +385,7 @@ bool shiny_check_starter_summary(SingleSwitchProgramEnvironment& env, ProControl
void go_to_last_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
// navigate to summary (last party slot)
- open_party_menu_from_overworld(env, context);
+ open_party_menu_from_overworld(env.console, context);
pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms);
pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms);
// open summary
@@ -754,6 +540,524 @@ void walk_to_safarizonewest(ProControllerContext& context){
} // namespace
+void RngHelper::set_seed_after_delay(ProControllerContext& context, int64_t& FIXED_SEED_OFFSET){
+ // wait on title screen for the specified delay
+ pbf_wait(context, std::chrono::milliseconds(SEED_DELAY + SEED_CALIBRATION + FIXED_SEED_OFFSET));
+ // hold the specified button for a few seconds through the transition to the Continue Screen
+ Button button;
+ switch (SEED_BUTTON){
+ case SeedButton::A:
+ button = BUTTON_A;
+ break;
+ case SeedButton::Start:
+ button = BUTTON_PLUS;
+ break;
+ case SeedButton::L:
+ button = BUTTON_L;
+ break;
+ default:
+ button = BUTTON_A;
+ break;
+ }
+ pbf_press_button(context, button, 3000ms, 0ms);
+}
+
+void RngHelper::load_game_after_delay(ProControllerContext& context, const uint64_t& CONTINUE_SCREEN_DELAY){
+ pbf_wait(context, std::chrono::milliseconds(CONTINUE_SCREEN_DELAY - 3000));
+ pbf_press_button(context, BUTTON_A, 33ms, 1467ms);
+ // skip recap
+ pbf_press_button(context, BUTTON_B, 33ms, 2467ms);
+ // need to later subtract 4000ms from delay to hit desired number of advances
+}
+
+void RngHelper::wait_with_teachy_tv(ProControllerContext& context, const uint64_t& TEACHY_DELAY){
+ // open start menu -> bag -> key items -> Teachy TV -> use
+ pbf_press_button(context, BUTTON_PLUS, 200ms, 300ms);
+ pbf_move_left_joystick(context, {0, -1}, 200ms, 300ms);
+ pbf_move_left_joystick(context, {0, -1}, 200ms, 300ms);
+ pbf_press_button(context, BUTTON_A, 200ms, 2300ms);
+ pbf_move_left_joystick(context, {+1, 0}, 200ms, 2300ms);
+ pbf_press_button(context, BUTTON_A, 200ms, 300ms);
+ pbf_press_button(context, BUTTON_A, 200ms, std::chrono::milliseconds(TEACHY_DELAY));
+ // close teachy tv -> close bag -> reset start menu cursor position - > close start menu
+ pbf_press_button(context, BUTTON_B, 200ms, 2300ms);
+ pbf_press_button(context, BUTTON_B, 200ms, 2300ms);
+ pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms);
+ pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms);
+ pbf_press_button(context, BUTTON_B, 200ms, 300ms);
+ // total non-teachy delay duration: 13700ms
+}
+
+void RngHelper::check_timings(SingleSwitchProgramEnvironment& env, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE){
+ if (CONTINUE_SCREEN_DELAY < 3200){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "The Continue Screen delay cannot be less than 3200ms (192 advances). Check your Continue Screen calibration.",
+ env.console
+ );
+ }
+ if (SEED_DELAY + SEED_CALIBRATION + FIXED_SEED_OFFSET < 28000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "The title screen delay cannot be less than 28000ms. Check your seed calibration.",
+ env.console
+ );
+ }
+
+ switch (TARGET){
+ case Target::starters:
+ if (INGAME_DELAY < 7500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Starters: the in-game delay cannot be less than 7500ms (900 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::magikarp:
+ if (INGAME_DELAY < 7500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Magikarp: the in-game delay cannot be less than 7500ms (900 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::hitmon:
+ if (INGAME_DELAY < 4500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Hitmonchan/Hitmonlee: the in-game delay cannot be less than 4500ms (540 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::eevee:
+ if (INGAME_DELAY < 4000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Eevee: the in-game delay cannot be less than 4000ms (480 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::lapras:
+ if (INGAME_DELAY < 7500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Lapras: the in-game delay cannot be less than 7500ms (900 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::fossils:
+ if (INGAME_DELAY < 6000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Fossils: the in-game delay cannot be less than 6000ms (720 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::gamecornerabra:
+ case Target::gamecornerclefairy:
+ case Target::gamecornerdratini:
+ case Target::gamecornerbug:
+ case Target::gamecornerporygon:
+ if (INGAME_DELAY < 8500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Game Corner: the in-game delay cannot be less than 8500ms (1020 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::togepi:
+ if (INGAME_DELAY < 12000) {
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Togepi: the in-game delay cannot be less than 12000ms (1440 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::staticencounter:
+ if (INGAME_DELAY < 5000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Static Encounter: the in-game delay cannot be less than 5000ms (600 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::snorlax:
+ if (INGAME_DELAY < 16000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Snorlax: the in-game delay cannot be less than 16000ms (1920 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::mewtwo:
+ if (INGAME_DELAY < 4500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Mewtwo: the in-game delay cannot be less than 4500ms (540 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::hooh:
+ if (INGAME_DELAY < 4000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Ho-oh: the in-game delay cannot be less than 4000ms (480 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::hypno:
+ if (INGAME_DELAY < 13000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Hypno: the in-game delay cannot be less than 13000ms (1560 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::sweetscent:
+ if (!SAFARI_ZONE && INGAME_DELAY < 8500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Sweet Scent: the in-game delay cannot be less than 8500ms (1020 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }else if (SAFARI_ZONE && INGAME_DELAY < 9500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Sweet Scent: the in-game delay cannot be less than 9500ms (1140 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::fishing:
+ if (INGAME_DELAY < 5500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Fishing: the in-game delay cannot be less than 5500ms (1800 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::safarizonecenter:
+ if (INGAME_DELAY < 30500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Safari Zone Center: in-game delay cannot be less than 30500ms (3660 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::safarizoneeast:
+ if (INGAME_DELAY < 36500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Safari Zone East: in-game delay cannot be less than 36500ms (4380 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::safarizonenorth:
+ if (INGAME_DELAY < 47500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Safari Zone North: in-game delay cannot be less than 47500ms (5700 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::safarizonewest:
+ if (INGAME_DELAY < 61500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Safari Zone West: in-game delay cannot be less than 52000ms (7380 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::safarizonesurf:
+ if (INGAME_DELAY < 40500){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Safari Zone Surfing: in-game delay cannot be less than 40500ms (4860 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ case Target::safarizonefish:
+ if (INGAME_DELAY < 30000){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Safari Zone Fishing: in-game delay cannot be less than 30000ms (3600 advances). Check your in-game advances and calibration.",
+ env.console
+ );
+ }
+ return;
+ default:
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Option not yet implemented.",
+ env.console
+ );
+ }
+}
+
+void RngHelper::perform_blind_sequence(ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE){
+ pbf_press_button(context, BUTTON_A, 80ms, 0ms); // start the game from the Home screen
+ set_seed_after_delay(context, FIXED_SEED_OFFSET);
+ load_game_after_delay(context, CONTINUE_SCREEN_DELAY);
+ if (TEACHY_DELAY > 0){
+ wait_with_teachy_tv(context, TEACHY_DELAY);
+ }
+
+ uint64_t MODIFIED_INGAME_DELAY;
+ switch (TARGET){
+ case Target::starters:
+ collect_starter_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::magikarp:
+ collect_magikarp_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::hitmon:
+ collect_hitmon_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::eevee:
+ collect_eevee_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::lapras:
+ collect_lapras_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::fossils:
+ collect_fossil_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::gamecornerabra:
+ collect_gamecorner_after_delay(context, INGAME_DELAY, 0);
+ return;
+ case Target::gamecornerclefairy:
+ collect_gamecorner_after_delay(context, INGAME_DELAY, 1);
+ return;
+ case Target::gamecornerdratini:
+ collect_gamecorner_after_delay(context, INGAME_DELAY, 2);
+ return;
+ case Target::gamecornerbug:
+ collect_gamecorner_after_delay(context, INGAME_DELAY, 3);
+ return;
+ case Target::gamecornerporygon:
+ collect_gamecorner_after_delay(context, INGAME_DELAY, 4);
+ return;
+ case Target::togepi:
+ collect_togepi_egg_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::staticencounter:
+ encounter_static_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::snorlax:
+ encounter_snorlax_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::mewtwo:
+ encounter_mewtwo_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::hooh:
+ encounter_hooh_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::hypno:
+ encounter_hypno_after_delay(context, INGAME_DELAY);
+ return;
+ case Target::sweetscent:
+ use_sweet_scent(context, INGAME_DELAY, SAFARI_ZONE);
+ return;
+ case Target::fishing:
+ use_registered_fishing_rod(context, INGAME_DELAY);
+ return;
+ case Target::safarizonecenter:
+ MODIFIED_INGAME_DELAY = INGAME_DELAY - 20670;
+ walk_to_safarizonecenter(context);
+ use_sweet_scent(context, MODIFIED_INGAME_DELAY, true);
+ return;
+ case Target::safarizoneeast:
+ MODIFIED_INGAME_DELAY = INGAME_DELAY - 36160;
+ walk_to_safarizoneeast(context);
+ use_sweet_scent(context, MODIFIED_INGAME_DELAY, true);
+ return;
+ case Target::safarizonenorth:
+ MODIFIED_INGAME_DELAY = INGAME_DELAY - 37410;
+ walk_to_safarizonenorth(context);
+ use_sweet_scent(context, MODIFIED_INGAME_DELAY, true);
+ return;
+ case Target::safarizonewest:
+ MODIFIED_INGAME_DELAY = INGAME_DELAY - 51430;
+ walk_to_safarizonewest(context);
+ use_sweet_scent(context, MODIFIED_INGAME_DELAY, true);
+ case Target::safarizonesurf:
+ MODIFIED_INGAME_DELAY = INGAME_DELAY - 30300;
+ walk_to_safarizonesurf(context);
+ use_sweet_scent(context, MODIFIED_INGAME_DELAY, true);
+ return;
+ case Target::safarizonefish:
+ MODIFIED_INGAME_DELAY = INGAME_DELAY - 30300;
+ walk_to_safarizonefish(context);
+ use_registered_fishing_rod(context, MODIFIED_INGAME_DELAY);
+ return;
+ }
+}
+
+void RngHelper::reset_and_perform_blind_sequence(SingleSwitchProgramEnvironment& env, ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE){
+ go_home(env.console, context);
+ close_game_from_home(env.console, context);
+ start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), uint8_t(0)); // TODO: add option for user slot if needed
+ go_home(env.console, context); // happens as soon as a black screen is detected
+
+ // attempt to resume the game and perform the blind sequence
+ // by this point, the license check should be over, so we don't need to worry about it when resuming the game
+ // profile selection is also already taken care of
+ uint8_t attempts = 0;
+ while(true){
+ if (attempts >= 5){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "RngHelper(): Failed to resume the game 5 times in a row.",
+ env.console
+ );
+ }
+ env.log("Starting blind button presses...");
+ UpdateMenuWatcher update_detector(env.console);
+ // any other fail conditions should be added here
+ context.wait_for_all_requests();
+ int ret = run_until(
+ env.console, context,
+ [this, FIXED_SEED_OFFSET, CONTINUE_SCREEN_DELAY, TEACHY_DELAY, INGAME_DELAY, SAFARI_ZONE](ProControllerContext& context) {
+ perform_blind_sequence(context, FIXED_SEED_OFFSET, CONTINUE_SCREEN_DELAY, TEACHY_DELAY, INGAME_DELAY, SAFARI_ZONE);
+ },
+ { update_detector }
+ );
+
+ switch (ret){
+ case 0:
+ attempts++;
+ env.log("Detected update window.", COLOR_RED);
+ pbf_press_dpad(context, DPAD_UP, 40ms, 0ms);
+ pbf_press_button(context, BUTTON_A, 80ms, 4000ms);
+ context.wait_for_all_requests();
+ continue;
+ default:
+ return;
+ }
+ }
+}
+
+void RngHelper::reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
+ go_home(env.console, context);
+ close_game_from_home(env.console, context);
+ start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), uint8_t(0)); // TODO: add option for user slot if needed
+ go_home(env.console, context); // happens as soon as a black screen is detected
+
+ uint8_t attempts = 0;
+ while(true){
+ if (attempts >= 5){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "RngHelper(): Failed to resume the game 5 times in a row.",
+ env.console
+ );
+ }
+
+ UpdateMenuWatcher update_detector(env.console);
+ BlackScreenWatcher blackscreen_detector(COLOR_RED);
+ context.wait_for_all_requests();
+ int ret = run_until(
+ env.console, context,
+ [](ProControllerContext& context) {
+ pbf_press_button(context, BUTTON_A, 80ms, 9920ms);
+ },
+ { update_detector, blackscreen_detector },
+ 1ms
+ );
+
+ BlackScreenOverWatcher copyright_detector(COLOR_RED);
+ int ret2;
+ switch (ret){
+ case 0:
+ attempts++;
+ env.log("Detected update window.", COLOR_RED);
+ pbf_press_dpad(context, DPAD_UP, 40ms, 0ms);
+ pbf_press_button(context, BUTTON_A, 80ms, 4000ms);
+ context.wait_for_all_requests();
+ continue;
+ case 1:
+ context.wait_for_all_requests();
+ ret2 = wait_until(
+ env.console, context, 10000ms,
+ {copyright_detector },
+ 1ms
+ );
+ if (ret2 < 0){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Black screen detected for more than 10 seconds after starting game.",
+ env.console
+ );
+ }
+ return;
+ default:
+ env.log("No black screen or update popup detected. Pressing A again...");
+ continue;
+ }
+ }
+
+}
+
+bool RngHelper::check_for_shiny(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
+ switch (TARGET){
+ case Target::starters:
+ return shiny_check_starter_summary(env, context);
+ case Target::togepi:
+ hatch_togepi_egg(env, context);
+ case Target::magikarp:
+ case Target::hitmon:
+ case Target::eevee:
+ case Target::lapras:
+ case Target::fossils:
+ case Target::gamecornerabra:
+ case Target::gamecornerclefairy:
+ case Target::gamecornerdratini:
+ case Target::gamecornerbug:
+ case Target::gamecornerporygon:
+ return shiny_check_summary(env, context);
+ case Target::staticencounter:
+ case Target::snorlax:
+ case Target::mewtwo:
+ case Target::hooh:
+ case Target::hypno:
+ case Target::sweetscent:
+ case Target::fishing:
+ case Target::safarizonecenter:
+ case Target::safarizoneeast:
+ case Target::safarizonenorth:
+ case Target::safarizonewest:
+ case Target::safarizonesurf:
+ case Target::safarizonefish:
+ return watch_for_shiny_encounter(env, context) == 1;
+ default:
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "Option not yet implemented.",
+ env.console
+ );
+ }
+}
+
+
void RngHelper::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
/*
* Settings: Text Speed fast
@@ -765,14 +1069,14 @@ void RngHelper::program(SingleSwitchProgramEnvironment& env, ProControllerContex
bool shiny_found = false;
- double FRAMERATE = 59.999977; // FPS. from Dhruv (don't know original source)
+ double FRAMERATE = 59.999977; // FPS
double FRAME_DURATION = 1000 / FRAMERATE;
- int64_t FIXED_SEED_OFFSET = USE_COPYRIGHT_TEXT ? -2048 : -800; // milliseconds. approximate, might be console-specific (?)
- int64_t FIXED_ADVANCES_OFFSET = 0; // frames
+ int64_t FIXED_SEED_OFFSET = USE_COPYRIGHT_TEXT ? -2140 : -845; // milliseconds. approximate
while (!shiny_found){
- double MODIFIED_INGAME_ADVANCES = INGAME_ADVANCES + FIXED_ADVANCES_OFFSET + INGAME_CALIBRATION;
+ // prepare timings
+ double MODIFIED_INGAME_ADVANCES = INGAME_ADVANCES + INGAME_CALIBRATION;
if (MODIFIED_INGAME_ADVANCES < 0) {
OperationFailedException::fire(
ErrorReport::SEND_ERROR_REPORT,
@@ -792,217 +1096,35 @@ void RngHelper::program(SingleSwitchProgramEnvironment& env, ProControllerContex
uint64_t TEACHY_TV_BUFFER = SAFARI_ZONE ? 12000 : 5000; // Safari zone targets need extra time to walk to the right position
- bool should_use_teachy_tv = USE_TEACHY_TV && MODIFIED_INGAME_ADVANCES > TEACHY_TV_BUFFER; // don't use Teachy TV for short in-game advance targets
+ bool should_use_teachy_tv = USE_TEACHY_TV && (TARGET != Target::starters) && (MODIFIED_INGAME_ADVANCES > TEACHY_TV_BUFFER); // don't use Teachy TV for short in-game advance targets
if (should_use_teachy_tv) {
TEACHY_ADVANCES = uint64_t((int)std::floor((MODIFIED_INGAME_ADVANCES - TEACHY_TV_BUFFER) / 313) * 313);
}
- const uint64_t LOAD_DELAY = uint64_t((LOAD_ADVANCES + LOAD_CALIBRATION) * FRAME_DURATION);
+ const uint64_t CONTINUE_SCREEN_DELAY = uint64_t((CONTINUE_SCREEN_FRAMES + CONTINUE_SCREEN_CALIBRATION) * FRAME_DURATION);
const uint64_t TEACHY_DELAY = uint64_t(TEACHY_ADVANCES * FRAME_DURATION / 313);
const uint64_t INGAME_DELAY = uint64_t((MODIFIED_INGAME_ADVANCES - TEACHY_ADVANCES) * FRAME_DURATION / 2) - (should_use_teachy_tv ? 13700 : 0);
- env.log("Load screen delay: " + std::to_string(LOAD_DELAY) + "ms");
+ env.log("Continue Screen delay: " + std::to_string(CONTINUE_SCREEN_DELAY) + "ms");
env.log("In-game delay: " + std::to_string(INGAME_DELAY) + "ms");
env.log("Teachy TV delay: " + std::to_string(TEACHY_DELAY) + "ms");
- env.log("Total time: " + std::to_string(SEED_DELAY + SEED_CALIBRATION + FIXED_SEED_OFFSET + LOAD_DELAY + INGAME_DELAY + TEACHY_DELAY) + "ms");
-
- if (SEED_DELAY + SEED_CALIBRATION + FIXED_SEED_OFFSET < 28000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "The title screen delay cannot be less than 28000ms. Check your seed calibration.",
- env.console
- );
- }
-
- hard_reset(context);
-
- if (USE_COPYRIGHT_TEXT) {
- uint64_t STARTUP_DELAY = wait_for_copyright_text(env, context);
- env.log("Startup delay: " + std::to_string(STARTUP_DELAY) + "ms");
- }
+ env.log("Total time: " + std::to_string(SEED_DELAY + SEED_CALIBRATION + FIXED_SEED_OFFSET + CONTINUE_SCREEN_DELAY + INGAME_DELAY + TEACHY_DELAY) + "ms");
- set_seed_after_delay(context, SEED_DELAY, SEED_CALIBRATION, FIXED_SEED_OFFSET);
- load_game_after_delay(env, context, LOAD_DELAY);
- if (should_use_teachy_tv){
- wait_with_teachy_tv(context, TEACHY_DELAY);
- }
+ check_timings(env, FIXED_SEED_OFFSET, CONTINUE_SCREEN_DELAY, INGAME_DELAY, SAFARI_ZONE);
- int ret;
- uint64_t MODIFIED_INGAME_DELAY;
- switch (TARGET){
- case Target::starters:
- collect_starter_after_delay(env, context, INGAME_DELAY);
- shiny_found = shiny_check_starter_summary(env, context);
- break;
- case Target::magikarp:
- collect_magikarp_after_delay(env, context, INGAME_DELAY);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::hitmon:
- collect_hitmon_after_delay(env, context, INGAME_DELAY);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::eevee:
- collect_eevee_after_delay(env, context, INGAME_DELAY);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::lapras:
- collect_lapras_after_delay(env, context, INGAME_DELAY);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::fossils:
- collect_fossil_after_delay(env, context, INGAME_DELAY);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::gamecornerabra:
- collect_gamecorner_after_delay(env, context, INGAME_DELAY, 0);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::gamecornerclefairy:
- collect_gamecorner_after_delay(env, context, INGAME_DELAY, 1);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::gamecornerdratini:
- collect_gamecorner_after_delay(env, context, INGAME_DELAY, 2);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::gamecornerbug:
- collect_gamecorner_after_delay(env, context, INGAME_DELAY, 3);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::gamecornerporygon:
- collect_gamecorner_after_delay(env, context, INGAME_DELAY, 4);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::togepi:
- collect_togepi_egg_after_delay(env, context, INGAME_DELAY);
- hatch_togepi_egg(env, context);
- shiny_found = shiny_check_summary(env, context);
- break;
- case Target::staticencounter:
- encounter_static_after_delay(env, context, INGAME_DELAY);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::snorlax:
- encounter_snorlax_after_delay(env, context, INGAME_DELAY);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::mewtwo:
- encounter_mewtwo_after_delay(env, context, INGAME_DELAY);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::hooh:
- encounter_hooh_after_delay(env, context, INGAME_DELAY);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::hypno:
- encounter_hypno_after_delay(env, context, INGAME_DELAY);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::sweetscent:
- use_sweet_scent(env, context, INGAME_DELAY);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::fishing:
- use_registered_fishing_rod(env, context, INGAME_DELAY);
- ret = watch_for_shiny_encounter(env, context);
- if (ret < 0 ){
- continue;
- // keep going if "Not even a nibble..."
- }
- shiny_found = ret == 1;
- break;
- case Target::safarizonecenter:
- if (INGAME_DELAY < 21000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Safari Zone Center: in-game delay cannot be less than 21000ms (2520 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
- MODIFIED_INGAME_DELAY = INGAME_DELAY - 20670;
- walk_to_safarizonecenter(context);
- use_sweet_scent(env, context, MODIFIED_INGAME_DELAY, true);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::safarizoneeast:
- if (INGAME_DELAY < 36500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Safari Zone East: in-game delay cannot be less than 36500ms (4380 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
- MODIFIED_INGAME_DELAY = INGAME_DELAY - 36160;
- walk_to_safarizoneeast(context);
- use_sweet_scent(env, context, MODIFIED_INGAME_DELAY, true);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::safarizonenorth:
- if (INGAME_DELAY < 38000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Safari Zone North: in-game delay cannot be less than 38000ms (4560 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
- MODIFIED_INGAME_DELAY = INGAME_DELAY - 37410;
- walk_to_safarizonenorth(context);
- use_sweet_scent(env, context, MODIFIED_INGAME_DELAY, true);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::safarizonewest:
- if (INGAME_DELAY < 52000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Safari Zone West: in-game delay cannot be less than 52000ms (6240 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
- MODIFIED_INGAME_DELAY = INGAME_DELAY - 51430;
- walk_to_safarizonewest(context);
- use_sweet_scent(env, context, MODIFIED_INGAME_DELAY, true);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::safarizonesurf:
- if (INGAME_DELAY < 31000){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Safari Zone Surfing: in-game delay cannot be less than 31000ms (3720 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
- MODIFIED_INGAME_DELAY = INGAME_DELAY - 30300;
- walk_to_safarizonesurf(context);
- use_sweet_scent(env, context, MODIFIED_INGAME_DELAY, true);
- shiny_found = watch_for_shiny_encounter(env, context) == 1;
- break;
- case Target::safarizonefish:
- if (INGAME_DELAY < 24500){
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Safari Zone Fishing: in-game delay cannot be less than 24500ms (2940 frames). Check your in-game advances and calibration.",
- env.console
- );
- }
- MODIFIED_INGAME_DELAY = INGAME_DELAY - 30300;
- walk_to_safarizonefish(context);
- use_registered_fishing_rod(env, context, MODIFIED_INGAME_DELAY);
- ret = watch_for_shiny_encounter(env, context);
- if (ret < 0 ){
- continue;
- // keep going if "Not even a nibble..."
- }
- shiny_found = ret == 1;
- break;
- default:
- OperationFailedException::fire(
- ErrorReport::SEND_ERROR_REPORT,
- "Option not yet implemented.",
- env.console
- );
+
+ // handle the blind part
+ if (USE_COPYRIGHT_TEXT){
+ reset_and_detect_copyright_text(env, context);
+ env.log("Starting blind button presses...");
+ perform_blind_sequence(context, FIXED_SEED_OFFSET, CONTINUE_SCREEN_DELAY, TEACHY_DELAY, INGAME_DELAY, SAFARI_ZONE);
+ }else{
+ reset_and_perform_blind_sequence(env, context, FIXED_SEED_OFFSET, CONTINUE_SCREEN_DELAY, TEACHY_DELAY, INGAME_DELAY, SAFARI_ZONE);
}
-
+ env.log("Blind button presses complete.");
stats.resets++;
+
+ // detect shinies
+ shiny_found = check_for_shiny(env, context);
if (shiny_found){
env.log("Shiny found!");
stats.shinies++;
diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h
index 1716d34c78..e5fa50b3e3 100644
--- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h
+++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h
@@ -35,10 +35,6 @@ class RngHelper : public SingleSwitchProgramInstance{
) override{}
private:
- enum class ResetType{
- hard,
- soft,
- };
enum class Target{
starters,
magikarp,
@@ -68,15 +64,33 @@ class RngHelper : public SingleSwitchProgramInstance{
// roaming
};
+ enum class SeedButton{
+ A,
+ Start,
+ L
+ };
+
+ void set_seed_after_delay(ProControllerContext& context, int64_t& FIXED_SEED_OFFSET);
+ void load_game_after_delay(ProControllerContext& context, const uint64_t& LOAD_DELAY);
+ void wait_with_teachy_tv(ProControllerContext& context, const uint64_t& TEACHY_DELAY);
+
+
+ void check_timings(SingleSwitchProgramEnvironment& env, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE);
+ void perform_blind_sequence(ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE);
+ void reset_and_perform_blind_sequence(SingleSwitchProgramEnvironment& env, ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE);
+ void reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
+ bool check_for_shiny(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
+
EnumDropdownOption TARGET;
SimpleIntegerOption NUM_RESETS;
+ EnumDropdownOption SEED_BUTTON;
SimpleIntegerOption SEED_DELAY;
SimpleIntegerOption SEED_CALIBRATION;
- SimpleIntegerOption LOAD_ADVANCES;
- FloatingPointOption LOAD_CALIBRATION;
+ SimpleIntegerOption CONTINUE_SCREEN_FRAMES;
+ FloatingPointOption CONTINUE_SCREEN_CALIBRATION;
SimpleIntegerOption INGAME_ADVANCES;
FloatingPointOption INGAME_CALIBRATION;