diff --git a/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.cpp b/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.cpp index c38c9c944..67d11e9aa 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.cpp @@ -34,7 +34,7 @@ StatsReset_Descriptor::StatsReset_Descriptor() "PokemonLZA:StatsReset", STRING_POKEMON + " LZA", "Stats Reset", "Programs/PokemonLZA/StatsReset.html", - "Repeatedly receive gift " + STRING_POKEMON + " until you get the stats you want.", + "Repeatedly catch/receive " + STRING_POKEMON + " until you get the stats you want.", ProgramControllerClass::StandardController_NoRestrictions, FeedbackType::REQUIRED, AllowCommandsWhenRunning::DISABLE_COMMANDS @@ -75,6 +75,7 @@ StatsReset::StatsReset() "Gift " + STRING_POKEMON + ":", { {GiftPokemon::FLOETTE, "floette", "Floette" }, + {GiftPokemon::GENESECT, "genesect", "Genesect" }, {GiftPokemon::MAGEARNA, "magearna", "Magearna"}, {GiftPokemon::MELTAN, "meltan", "Meltan" }, {GiftPokemon::MELMETAL, "melmetal", "Melmetal"}, @@ -105,6 +106,12 @@ StatsReset::StatsReset() LockMode::UNLOCK_WHILE_RUNNING, "6000 ms" ) + , DOWN_SCROLLS( + "Donut Down-Scrolls:
" + "Scroll this many donuts down. Input a negative number to scroll up instead.", + LockMode::UNLOCK_WHILE_RUNNING, + 0, -50, 50 + ) , HP("HP:") , ATTACK("Attack:", IvJudgeFilter::NoGood) , DEFENSE("Defense:") @@ -125,6 +132,7 @@ StatsReset::StatsReset() PA_ADD_OPTION(SCROLL_HOLD); PA_ADD_OPTION(SCROLL_RELEASE); PA_ADD_OPTION(POST_THROW_WAIT); + PA_ADD_OPTION(DOWN_SCROLLS); PA_ADD_OPTION(HP); PA_ADD_OPTION(ATTACK); PA_ADD_OPTION(DEFENSE); @@ -143,13 +151,105 @@ StatsReset::~StatsReset(){ } void StatsReset::on_config_value_changed(void* object){ - ConfigOptionState state = POKEMON == GiftPokemon::MELTAN - ? ConfigOptionState::ENABLED - : ConfigOptionState::HIDDEN; - RIGHT_SCROLLS.set_visibility(state); - SCROLL_HOLD.set_visibility(state); - SCROLL_RELEASE.set_visibility(state); - POST_THROW_WAIT.set_visibility(state); + ConfigOptionState state_ball = (POKEMON == GiftPokemon::GENESECT || POKEMON == GiftPokemon::MELTAN) + ? ConfigOptionState::ENABLED : ConfigOptionState::HIDDEN; + ConfigOptionState state_donut = (POKEMON == GiftPokemon::GENESECT || POKEMON == GiftPokemon::MELMETAL) + ? ConfigOptionState::ENABLED : ConfigOptionState::HIDDEN; + RIGHT_SCROLLS.set_visibility(state_ball); + SCROLL_HOLD.set_visibility(state_ball); + SCROLL_RELEASE.set_visibility(state_ball); + POST_THROW_WAIT.set_visibility(state_ball); + DOWN_SCROLLS.set_visibility(state_donut); +} + +void StatsReset::enter_portal(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + pbf_press_button(context, BUTTON_A, 50ms, 1s); + int8_t scrolls = DOWN_SCROLLS; + DpadPosition direction; + if (scrolls >= 0){ + direction = DPAD_DOWN; + }else{ + direction = DPAD_UP; + scrolls = -scrolls; + } + while (scrolls != 0){ + pbf_press_dpad(context, direction, 50ms, 500ms); + scrolls--; + } + pbf_mash_button(context, BUTTON_A, 5s); + pbf_press_button(context, BUTTON_PLUS, 5s, 500ms); + + OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay()); + + context.wait_for_all_requests(); + + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + pbf_mash_button(context, BUTTON_B, 30s); + }, + { + overworld, + } + ); + + if (ret == 0){ + env.log("Detected overworld"); + context.wait_for_all_requests(); + } +} + +void StatsReset::run_battle(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool attempt_move){ + RunFromBattleWatcher battle_menu(COLOR_GREEN, &env.console.overlay(), 10ms); + + context.wait_for_all_requests(); + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + pbf_mash_button(context, BUTTON_B, 120s); + }, + { + battle_menu, + } + ); + + if (ret == 0){ + env.log("Detected battle menu"); + if (attempt_move){ + pbf_press_button(context, BUTTON_Y, 50ms, 500ms); + ssf_press_button(context, BUTTON_ZL, 0ms, 4s, 200ms); + pbf_mash_button(context, BUTTON_A, 4s); + } + context.wait_for_all_requests(); + } +} + +void StatsReset::run_catch(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + int8_t scrolls = RIGHT_SCROLLS; + DpadPosition direction; + if (scrolls >= 0){ + direction = DPAD_RIGHT; + }else{ + direction = DPAD_LEFT; + scrolls = -scrolls; + } + + Milliseconds hold = SCROLL_HOLD; + Milliseconds cool = SCROLL_RELEASE; + + ssf_press_button( + context, + BUTTON_ZL | BUTTON_ZR, + 500ms, 500ms + (hold + cool) * scrolls, + 0ms + ); + + while (scrolls != 0){ + pbf_press_dpad(context, direction, hold, cool); + scrolls--; + } + + pbf_wait(context, POST_THROW_WAIT); } void StatsReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ @@ -220,79 +320,15 @@ void StatsReset::program(SingleSwitchProgramEnvironment& env, ProControllerConte pbf_move_left_joystick(context, {0, +1}, 8s, 500ms); if (POKEMON == GiftPokemon::MELTAN){ // start battle, knock out in one move - RunFromBattleWatcher battle_menu(COLOR_GREEN, &env.console.overlay(), 10ms); - - context.wait_for_all_requests(); - int ret = run_until( - env.console, context, - [](ProControllerContext& context){ - pbf_mash_button(context, BUTTON_B, 120s); - }, - { - battle_menu, - } - ); - - if (ret == 0){ - env.log("Detected battle menu"); - pbf_press_button(context, BUTTON_Y, 50ms, 500ms); - ssf_press_button(context, BUTTON_ZL, 0ms, 4s, 200ms); - pbf_mash_button(context, BUTTON_A, 4s); - context.wait_for_all_requests(); - } + run_battle(env, context, true); // expected knock out, throw ball - int8_t scrolls = RIGHT_SCROLLS; - DpadPosition direction; - if (scrolls >= 0){ - direction = DPAD_RIGHT; - }else{ - direction = DPAD_LEFT; - scrolls = -scrolls; - } - - Milliseconds hold = SCROLL_HOLD; - Milliseconds cool = SCROLL_RELEASE; + run_catch(env, context); - ssf_press_button( - context, - BUTTON_ZL | BUTTON_ZR, - 500ms, 500ms + (hold + cool) * scrolls, - 0ms - ); - - while (scrolls != 0){ - pbf_press_dpad(context, direction, hold, cool); - scrolls--; - } - - pbf_wait(context, POST_THROW_WAIT); pbf_mash_button(context, BUTTON_A, 10s); } if (POKEMON == GiftPokemon::MELMETAL){ - // enter portal - pbf_press_button(context, BUTTON_A, 50ms, 1s); - pbf_press_dpad(context, DPAD_UP, 50ms, 500ms); - pbf_mash_button(context, BUTTON_A, 5s); - pbf_press_button(context, BUTTON_PLUS, 5s, 500ms); - - OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay()); - - context.wait_for_all_requests(); - int ret = run_until( - env.console, context, - [](ProControllerContext& context){ - pbf_mash_button(context, BUTTON_B, 30s); - }, - { - overworld, - } - ); - - if (ret == 0){ - env.log("Detected overworld"); - context.wait_for_all_requests(); - } + enter_portal(env, context); pbf_press_button(context, BUTTON_L, 50ms, 500ms); ssf_press_button(context, BUTTON_B, 0ms, 1000ms, 0ms); @@ -310,10 +346,51 @@ void StatsReset::program(SingleSwitchProgramEnvironment& env, ProControllerConte } } + if (POKEMON == GiftPokemon::GENESECT){ + // fly to wild zone 13 + FastTravelState travel_status = open_map_and_fly_to(env.console, context, LANGUAGE, Location::WILD_ZONE_13); + if (travel_status != FastTravelState::SUCCESS){ + stats.errors++; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to travel to Wild Zone 13", + env.console + ); + } + context.wait_for(100ms); + env.log("Detected overworld. Fast traveled to Wild Zone 13"); + + // move to the portal + run_towards_gate_with_A_button(env.console, context, 0, +1, Seconds(5)); + pbf_mash_button(context, BUTTON_A, 4s); + + ssf_press_button(context, BUTTON_B, 0ms, 1000ms, 0ms); + pbf_move_left_joystick(context, {0.6, +1}, 2500ms, 500ms); + + enter_portal(env, context); + pbf_wait(context, 5s); + + // initiate battle + pbf_press_button(context, BUTTON_L, 50ms, 500ms); + ssf_press_button(context, BUTTON_B, 0ms, 1000ms, 0ms); + pbf_move_left_joystick(context, {0, +1}, 15s, 1s); + pbf_move_left_joystick(context, {-1, 0}, 200ms, 500ms); + pbf_press_button(context, BUTTON_L, 50ms, 500ms); + ssf_press_button(context, BUTTON_B, 0ms, 1000ms, 0ms); + pbf_move_left_joystick(context, {0.2, +1}, 15s, 1s); + + run_battle(env, context); + + run_catch(env, context); + pbf_mash_button(context, BUTTON_A, 5s); + } + context.wait_for_all_requests(); { BlackScreenOverWatcher detector; OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay()); + RunFromBattleWatcher battle_menu(COLOR_GREEN, &env.console.overlay(), 10ms); int result = run_until( env.console, context, [this](ProControllerContext& context){ @@ -328,6 +405,7 @@ void StatsReset::program(SingleSwitchProgramEnvironment& env, ProControllerConte { detector, overworld, + battle_menu } ); switch (result){ @@ -340,6 +418,12 @@ void StatsReset::program(SingleSwitchProgramEnvironment& env, ProControllerConte go_home(env.console, context); reset_game_from_home(env, env.console, context, true); continue; + case 2: + env.log("Still in battle, catch failed", COLOR_PURPLE); + stats.catch_fail++; + go_home(env.console, context); + reset_game_from_home(env, env.console, context, true); + continue; default: env.log(STRING_POKEMON + " dialog timed out.", COLOR_RED); // fail safely and start over diff --git a/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.h b/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.h index 53677cc5f..08120c46b 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.h +++ b/SerialPrograms/Source/PokemonLZA/Programs/NonShinyHunting/PokemonLZA_StatsReset.h @@ -41,6 +41,9 @@ class StatsReset : public SingleSwitchProgramInstance, public ConfigOption::List virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; private: + virtual void enter_portal(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + virtual void run_battle(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool attempt_move = false); + virtual void run_catch(SingleSwitchProgramEnvironment& env, ProControllerContext& context); virtual void on_config_value_changed(void* object) override; StartInGripOrGameOption START_LOCATION; GoHomeWhenDoneOption GO_HOME_WHEN_DONE; @@ -49,6 +52,7 @@ class StatsReset : public SingleSwitchProgramInstance, public ConfigOption::List enum class GiftPokemon{ FLOETTE, + GENESECT, MAGEARNA, MELTAN, MELMETAL, @@ -60,6 +64,8 @@ class StatsReset : public SingleSwitchProgramInstance, public ConfigOption::List MillisecondsOption SCROLL_RELEASE; MillisecondsOption POST_THROW_WAIT; + SimpleIntegerOption DOWN_SCROLLS; + IVJudgeFilterOption HP; IVJudgeFilterOption ATTACK; IVJudgeFilterOption DEFENSE;