From cc08d4d9a917a939c288c2515ed1cee9da0740ed Mon Sep 17 00:00:00 2001 From: Gin <> Date: Sun, 11 Jan 2026 13:53:32 -0800 Subject: [PATCH 1/4] apply character reduction before unicode normalization --- .../Source/CommonTools/OCR/OCR_StringNormalization.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/SerialPrograms/Source/CommonTools/OCR/OCR_StringNormalization.cpp b/SerialPrograms/Source/CommonTools/OCR/OCR_StringNormalization.cpp index ea0244479..3250a85f1 100644 --- a/SerialPrograms/Source/CommonTools/OCR/OCR_StringNormalization.cpp +++ b/SerialPrograms/Source/CommonTools/OCR/OCR_StringNormalization.cpp @@ -19,6 +19,7 @@ namespace OCR{ std::u32string normalize_utf32(const std::string& text){ QString qstr = QString::fromStdString(text); + qstr = QString::fromStdU32String(run_character_reductions(qstr.toStdU32String())); qstr = qstr.normalized(QString::NormalizationForm_KD); std::u32string u32 = remove_non_alphanumeric(qstr.toStdU32String()); u32 = run_character_reductions(u32); From 3f26219f95cdcde46a21e7325e6b2740d116057d Mon Sep 17 00:00:00 2001 From: Gin <> Date: Sun, 11 Jan 2026 22:38:00 -0800 Subject: [PATCH 2/4] add Latias hunt --- ...kemonLZA_ShinyHunt_HyperspaceLegendary.cpp | 163 ++++++++++++++++++ ...PokemonLZA_ShinyHunt_HyperspaceLegendary.h | 1 + 2 files changed, 164 insertions(+) diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp index cbaf44663..ca115a814 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp @@ -78,6 +78,7 @@ ShinyHunt_HyperspaceLegendary::ShinyHunt_HyperspaceLegendary() , LEGENDARY("Legendary " + STRING_POKEMON + ":", { {Legendary::LATIOS, "latios", "Latios"}, + {Legendary::LATIAS, "latias", "Latias"}, {Legendary::COBALION, "cobalion", "Cobalion"}, {Legendary::TERRAKION, "terrakion", "Terrakion"}, {Legendary::VIRIZION, "virizion", "Virizion"}, @@ -138,6 +139,166 @@ void hunt_latios( } +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 + detect_warp_pad(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 roof edge + 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 + 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 bulkhead + pbf_press_button(context, BUTTON_Y, 100ms, 1000ms); + + // Roll down the corrider 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 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 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); + // Falls down 2 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 rooftop + pbf_move_left_joystick(context, {0, +1}, 500ms, 1200ms); + + // Start of actual reset cycle + while (true){ + // Roll to the edge fo the lower 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); + // Lataias spawns here + + // Roll back to the start + 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 to around + 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 rooftop with the bulkhead + ssf_press_left_joystick(context, {+0.5, 0}, 0ms, 500ms, 0ms); + pbf_press_button(context, BUTTON_Y, 100ms, 1000ms); + + // Climb onto the rooftop with the bulkhead + pbf_move_left_joystick(context, {0, -1}, 500ms, 1200ms); + pbf_press_button(context, BUTTON_Y, 100ms, 1000ms); + + // Roll right towards the next 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); + + // 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 the Munna rooftop and roll forward + 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 detecting 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, + "detect_warp_pad(): Cannot detect ladder after 5 seconds", + env.console + ); + } else { + env.console.log("Detected ladder."); + } + + // Run to Latias to trigger 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(); +} + + void hunt_cobalion( SingleSwitchProgramEnvironment& env, ProControllerContext& context, @@ -460,6 +621,8 @@ void ShinyHunt_HyperspaceLegendary::program(SingleSwitchProgramEnvironment& env, [&](ProControllerContext& context){ if (LEGENDARY == Legendary::LATIOS){ hunt_latios(env, context, stats, MIN_CALORIE_TO_CATCH); + } else if (LEGENDARY == Legendary::LATIAS){ + hunt_latias(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); } else if (LEGENDARY == Legendary::TERRAKION){ diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h index 1b2db114c..39cfcde04 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h +++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.h @@ -36,6 +36,7 @@ class ShinyHunt_HyperspaceLegendary : public SingleSwitchProgramInstance{ enum class Legendary{ LATIOS, + LATIAS, COBALION, TERRAKION, VIRIZION, From 0765e26008d7204b9a8d10692a4f78ad1b1db712 Mon Sep 17 00:00:00 2001 From: Gin <> Date: Sun, 11 Jan 2026 22:42:00 -0800 Subject: [PATCH 3/4] m --- .../PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp index ca115a814..45bfc9682 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp @@ -202,6 +202,10 @@ void hunt_latias( pbf_press_button(context, BUTTON_Y, 100ms, 1000ms); // Lataias spawns here + context.wait_for_all_requests(); + stats.spawns++; + env.update_stats(); + // Roll back to the start ssf_press_left_joystick(context, {0, -0.5}, 0ms, 500ms, 0ms); pbf_press_button(context, BUTTON_Y, 100ms, 1000ms); @@ -263,6 +267,8 @@ void hunt_latias( pbf_press_button(context, BUTTON_Y, 100ms, 1000ms); context.wait_for_all_requests(); + stats.spawns++; + env.update_stats(); // Snap to the ladder by detecting the A button prompt ButtonWatcher ButtonA( From 0912c62eeb20394c2e63535cca507fba057e2e9f Mon Sep 17 00:00:00 2001 From: Gin <> Date: Sun, 11 Jan 2026 22:46:02 -0800 Subject: [PATCH 4/4] fix timing --- .../ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp index 45bfc9682..6b35f7252 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_HyperspaceLegendary.cpp @@ -217,7 +217,7 @@ void hunt_latias( // Turn back to around ssf_press_left_joystick(context, {0, +0.5}, 0ms, 500ms, 0ms); - const uint16_t min_calorie = MIN_CALORIE_TO_CATCH + (15 + 30) * 10; + const uint16_t min_calorie = MIN_CALORIE_TO_CATCH + 55 * 10; if (check_calorie(env.console, context, min_calorie)){ break; }