diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.cpp
index ad4f381e0..1c27f3320 100644
--- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.cpp
+++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.cpp
@@ -58,7 +58,7 @@ bool check_calorie(
}
// Wait until the warp pad is detected
-void detect_warp_pad(
+void detect_interactable(
ConsoleHandle& console,
ProControllerContext& context
){
@@ -77,7 +77,7 @@ void detect_warp_pad(
if (ret < 0){
OperationFailedException::fire(
ErrorReport::SEND_ERROR_REPORT,
- "detect_warp_pad(): Cannot detect warp pad after 5 seconds",
+ "detect_interactable_pad(): Cannot detect interactable after 5 seconds",
console
);
} else {
diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.h b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.h
index c972e4a82..6781cd848 100644
--- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.h
+++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_HyperspaceNavigation.h
@@ -32,7 +32,7 @@ bool check_calorie(
// Wait until the warp pad is detected on screen
// Throws OperationFailedException if warp pad is not detected within 5 seconds
-void detect_warp_pad(
+void detect_interactable(
ConsoleHandle& console,
ProControllerContext& context
);
diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp
index cbaf44663..c54b3383e 100644
--- a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp
+++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp
@@ -77,6 +77,7 @@ ShinyHunt_HyperspaceLegendary::ShinyHunt_HyperspaceLegendary()
: SHINY_DETECTED("Shiny Detected", "", "2000 ms", ShinySoundDetectedAction::STOP_PROGRAM)
, LEGENDARY("Legendary " + STRING_POKEMON + ":",
{
+ {Legendary::LATIAS, "latias", "Latias"},
{Legendary::LATIOS, "latios", "Latios"},
{Legendary::COBALION, "cobalion", "Cobalion"},
{Legendary::TERRAKION, "terrakion", "Terrakion"},
@@ -110,6 +111,168 @@ ShinyHunt_HyperspaceLegendary::ShinyHunt_HyperspaceLegendary()
namespace {
+// Start at the ladder up to Latias and use it to fix the position and camera angle
+// Run a route to the other side of the roof and begin a shuttle run to respawn Latias repeatedly
+// Route back to Latias to trigger a potential shiny sound when calories are low
+void hunt_latias(
+ SingleSwitchProgramEnvironment& env,
+ ProControllerContext& context,
+ ShinyHunt_HyperspaceLegendary_Descriptor::Stats& stats,
+ SimpleIntegerOption& MIN_CALORIE_TO_CATCH)
+{
+ // Start at the ladder and use it to fix the position and camera angle
+ // Run a route to get to the reset cycle start point
+ detect_interactable(env.console, context); // This should be renamed
+ pbf_press_button(context, BUTTON_A, 160ms, 1200ms);
+ pbf_move_left_joystick(context, {0, -0.5}, 80ms, 1200ms);
+
+ // Roll backwards to align against the edge of the Munna roof
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Roll left on the overhang to get to the far corner
+ ssf_press_left_joystick(context, {-0.5, 0}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ // Slightly longer due to falling off a ledge
+ pbf_press_button(context, BUTTON_Y, 100ms, 1200ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ // Roll to align against the bulkhead
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Roll down the corridor next to the bulkhead
+ ssf_press_left_joystick(context, {0, -0.5}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Roll towards the corner of the Kirlia rooftop
+ ssf_press_left_joystick(context, {-0.5, 0}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Rolling at this angle puts you at the very edge of the lower corner roof, at the start of the reset path
+ ssf_press_left_joystick(context, {-0.20, +0.5}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ // Fall down 2 roof levels
+ pbf_press_button(context, BUTTON_Y, 100ms, 1200ms);
+
+ // Adjust the camera angle to face the direction of the reset path
+ pbf_move_left_joystick(context, {+0.357, +0.5}, 500ms, 500ms);
+ pbf_press_button(context, BUTTON_L, 80ms, 160ms);
+
+ // Climb up from the overhang to the lower corner rooftop
+ pbf_move_left_joystick(context, {0, +1}, 500ms, 1200ms);
+
+ // Start of actual reset cycle
+ while (true){
+ // Roll to the edge of the lower corner rooftop
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Climb onto the upper rooftop
+ pbf_move_left_joystick(context, {0, +1}, 500ms, 1200ms);
+
+ // Roll forward and align against the far chimney
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ // Latias spawns here
+
+ // Roll back to the start on the lower corner rooftop
+ ssf_press_left_joystick(context, {0, -0.5}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1200ms);
+ // Latias despawns here
+
+ // Turn back around to face the chimney
+ ssf_press_left_joystick(context, {0, +0.5}, 0ms, 500ms, 0ms);
+
+ const uint16_t min_calorie = MIN_CALORIE_TO_CATCH + (15 + 30) * 10;
+ if (check_calorie(env.console, context, min_calorie)){
+ break;
+ }
+ }
+
+ // Roll and climb onto the upper rooftop
+ pbf_move_left_joystick(context, {0, +1}, 500ms, 1200ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_move_left_joystick(context, {0, +1}, 500ms, 1200ms);
+
+ // Roll to the right, next to the Kirlia rooftop
+ ssf_press_left_joystick(context, {+0.5, 0}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Climb onto the Kirlia rooftop
+ pbf_move_left_joystick(context, {0, -1}, 500ms, 1200ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Roll right towards the bulkhead
+ ssf_press_left_joystick(context, {+0.5, 0}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Adjust the camera angle back to the original angle
+ pbf_move_left_joystick(context, {-0.357, +0.5}, 80ms, 160ms);
+ pbf_press_button(context, BUTTON_L, 80ms, 160ms);
+
+ // Climb onto the next rooftop
+ ssf_press_left_joystick(context, {+0.5, 0}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Roll towards the Munna rooftop
+ pbf_move_left_joystick(context, {+1, 0}, 500ms, 1200ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Climb onto the Munna rooftop and roll
+ pbf_move_left_joystick(context, {+1, 0}, 500ms, 1200ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ // Roll back towards the ladder
+ ssf_press_left_joystick(context, {0, +0.5}, 0ms, 500ms, 0ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+ pbf_press_button(context, BUTTON_Y, 100ms, 1000ms);
+
+ context.wait_for_all_requests();
+
+ // Snap to the ladder by moving and watching for the A button prompt
+ ButtonWatcher ButtonA(
+ COLOR_RED,
+ ButtonType::ButtonA,
+ {0.4, 0.1, 0.2, 0.8},
+ &env.console.overlay(),
+ Milliseconds(100)
+ );
+ const int ret = run_until(
+ env.console, context,
+ [&](ProControllerContext& context){
+ pbf_move_left_joystick(context, {+0.5, +1}, 5000ms, 0ms);
+ },
+ {{ButtonA}}
+ );
+ if (ret < 0){
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "hunt_latias(): Cannot detect ladder after 5 seconds",
+ env.console
+ );
+ } else {
+ env.console.log("Detected ladder.");
+ }
+
+ // Run to Latias to trigger a potential shiny sound
+ env.log("Move to check Latias.");
+ env.add_overlay_log("To Check Latias");
+ pbf_press_button(context, BUTTON_A, 160ms, 80ms);
+ ssf_press_left_joystick(context, {0, +1}, 0ms, 4000ms, 0ms);
+ pbf_mash_button(context, BUTTON_Y, 4000ms);
+ context.wait_for_all_requests();
+}
// We save at warp pad and spawn Latios only once per game reset
void hunt_latios(
@@ -118,11 +281,11 @@ void hunt_latios(
ShinyHunt_HyperspaceLegendary_Descriptor::Stats& stats,
SimpleIntegerOption& MIN_CALORIE_TO_CATCH)
{
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
// Warp to rooftop to see Latios
pbf_press_button(context, BUTTON_A, 160ms, 80ms);
context.wait_for_all_requests();
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
stats.spawns++;
env.update_stats();
@@ -197,17 +360,17 @@ void hunt_terrakion(
{
while(true){
// Warp away from Terrakion to despawn
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
pbf_press_button(context, BUTTON_A, 160ms, 80ms);
context.wait_for_all_requests();
// Warp towards Terrakion
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
pbf_press_button(context, BUTTON_A, 160ms, 80ms);
context.wait_for_all_requests();
// Roll and roll back on Terrakion's roof to respawn
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
pbf_press_button(context, BUTTON_Y, 100ms, 900ms);
pbf_move_left_joystick(context, {0, -1}, 80ms, 160ms);
pbf_press_button(context, BUTTON_Y, 100ms, 900ms);
@@ -225,14 +388,14 @@ void hunt_terrakion(
}
// Use warp pads to reset position
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
pbf_press_button(context, BUTTON_A, 160ms, 80ms);
context.wait_for_all_requests();
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
pbf_press_button(context, BUTTON_A, 160ms, 80ms);
context.wait_for_all_requests();
- detect_warp_pad(env.console, context);
+ detect_interactable(env.console, context);
// Roll to Terrakion to trigger potential shiny sound
env.log("Move to check Terrakion.");
@@ -458,7 +621,9 @@ void ShinyHunt_HyperspaceLegendary::program(SingleSwitchProgramEnvironment& env,
const int ret = run_until(
env.console, context,
[&](ProControllerContext& context){
- if (LEGENDARY == Legendary::LATIOS){
+ if (LEGENDARY == Legendary::LATIAS){
+ hunt_latias(env, context, stats, MIN_CALORIE_TO_CATCH);
+ } else if (LEGENDARY == Legendary::LATIOS){
hunt_latios(env, context, stats, MIN_CALORIE_TO_CATCH);
} else if (LEGENDARY == Legendary::VIRIZION){
hunt_virizion_rooftop(env, context, stats, MIN_CALORIE_TO_CATCH, use_switch1_only_timings);
diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h
index 1b2db114c..e82dfa829 100644
--- a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h
+++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h
@@ -35,6 +35,7 @@ class ShinyHunt_HyperspaceLegendary : public SingleSwitchProgramInstance{
virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override;
enum class Legendary{
+ LATIAS,
LATIOS,
COBALION,
TERRAKION,