Skip to content

Commit

Permalink
fix scatterbug program
Browse files Browse the repository at this point in the history
  • Loading branch information
Gin committed Oct 1, 2023
1 parent 67b6fd4 commit d8c1640
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,8 @@ bool MapFlyMenuDetector::detect(const ImageViewRGB32& screen) const{

MapFlyMenuWatcher::~MapFlyMenuWatcher() = default;

MapFlyMenuWatcher::MapFlyMenuWatcher(Color color, VideoOverlay& overlay)
MapFlyMenuWatcher::MapFlyMenuWatcher(Color color)
: VisualInferenceCallback("MapFlyMenuWatcher")
, m_overlay(overlay)
, m_detector(color)
{}

Expand Down Expand Up @@ -81,9 +80,8 @@ bool MapDestinationMenuDetector::detect(const ImageViewRGB32& screen) const{

MapDestinationMenuWatcher::~MapDestinationMenuWatcher() = default;

MapDestinationMenuWatcher::MapDestinationMenuWatcher(Color color, VideoOverlay& overlay)
MapDestinationMenuWatcher::MapDestinationMenuWatcher(Color color)
: VisualInferenceCallback("MapDestinationMenuWatcher")
, m_overlay(overlay)
, m_detector(color)
{}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,13 @@ class MapFlyMenuDetector : public StaticScreenDetector{
// Watch for the "Fly here" menu to open on map
class MapFlyMenuWatcher : public VisualInferenceCallback{
public:
MapFlyMenuWatcher(Color color, VideoOverlay& overlay);
MapFlyMenuWatcher(Color color);
virtual ~MapFlyMenuWatcher();

virtual void make_overlays(VideoOverlaySet& items) const override;
virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override;

protected:
VideoOverlay& m_overlay;
MapFlyMenuDetector m_detector;
};

Expand All @@ -77,13 +76,12 @@ class MapDestinationMenuDetector : public StaticScreenDetector{
class MapDestinationMenuWatcher : public VisualInferenceCallback{
public:
~MapDestinationMenuWatcher();
MapDestinationMenuWatcher(Color color, VideoOverlay& overlay);
MapDestinationMenuWatcher(Color color);

virtual void make_overlays(VideoOverlaySet& items) const override;
virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override;

protected:
VideoOverlay& m_overlay;
MapDestinationMenuDetector m_detector;
};

Expand Down
118 changes: 77 additions & 41 deletions SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "PokemonSV/PokemonSV_Settings.h"
#include "PokemonSV/Inference/Dialogs/PokemonSV_DialogDetector.h"
#include "PokemonSV/Inference/Map/PokemonSV_MapDetector.h"
#include "PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.h"
#include "PokemonSV/Inference/Map/PokemonSV_MapPokeCenterIconDetector.h"
#include "PokemonSV/Inference/Picnics/PokemonSV_PicnicDetector.h"
#include "PokemonSV/Inference/PokemonSV_MainMenuDetector.h"
Expand All @@ -30,8 +31,6 @@ namespace NintendoSwitch{
namespace PokemonSV{




void set_time_to_12am_from_home(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
DateReader reader;
VideoOverlaySet overlays(console.overlay());
Expand Down Expand Up @@ -63,6 +62,27 @@ void day_skip_from_overworld(ConsoleHandle& console, BotBaseContext& context){
resume_game_from_home(console, context);
}

void press_Bs_to_back_to_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
context.wait_for_all_requests();
OverworldWatcher overworld(COLOR_RED);
int ret = run_until(
console, context,
[](BotBaseContext& context){
for (size_t c = 0; c < 10; c++){
pbf_press_button(context, BUTTON_B, 20, 230);
}
},
{overworld}
);
if (ret < 0){
throw OperationFailedException(
ErrorReport::SEND_ERROR_REPORT, console,
"press_Bs_to_back_to_overworld(): Unable to detect overworld after 10 button B presses.",
true
);
}
}

void open_map_from_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
{
OverworldWatcher overworld(COLOR_CYAN);
Expand Down Expand Up @@ -143,7 +163,7 @@ void open_map_from_overworld(const ProgramInfo& info, ConsoleHandle& console, Bo
}
}

void fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
bool fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context, bool check_fly_menuitem){
context.wait_for_all_requests();
// Press A to bring up the promp dialog on choosing "Fly here", "Set as destination", "Never mind".
pbf_press_button(context, BUTTON_A, 20, 130);
Expand All @@ -153,45 +173,51 @@ void fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console,
if (current_time() - start > std::chrono::minutes(2)){
throw OperationFailedException(
ErrorReport::SEND_ERROR_REPORT, console,
"fly_to_overworld_from_map(): Failed to open map after 2 minutes.",
"fly_to_overworld_from_map(): Failed to fly from map after 2 minutes.",
true
);
}

int ret = 0;
{
OverworldWatcher overworld(COLOR_CYAN);
WhiteButtonWatcher map(COLOR_RED, WhiteButton::ButtonY, {0.800, 0.118, 0.030, 0.060});
GradientArrowWatcher spot_dialog_watcher(COLOR_YELLOW, GradientArrowType::RIGHT, {0.469, 0.500, 0.215, 0.150});
PromptDialogWatcher confirm_watcher(COLOR_BLUE, {0.686, 0.494, 0.171, 0.163});
OverworldWatcher overworld(COLOR_CYAN);
WhiteButtonWatcher map(COLOR_RED, WhiteButton::ButtonY, {0.800, 0.118, 0.030, 0.060});
GradientArrowWatcher spot_dialog_watcher(COLOR_YELLOW, GradientArrowType::RIGHT, {0.469, 0.500, 0.215, 0.150});
PromptDialogWatcher confirm_watcher(COLOR_BLUE, {0.686, 0.494, 0.171, 0.163});
MapFlyMenuWatcher flyMenuItemWatcher(COLOR_BLACK);
MapDestinationMenuWatcher destinationMenuItemWatcher(COLOR_BLACK);

context.wait_for_all_requests();
ret = wait_until(
console, context,
std::chrono::minutes(2),
{overworld, map, spot_dialog_watcher, confirm_watcher}
);
context.wait_for_all_requests();

std::vector<PeriodicInferenceCallback> callbacks{overworld, map, spot_dialog_watcher, confirm_watcher};
if (check_fly_menuitem){
callbacks[2] = flyMenuItemWatcher;
callbacks.push_back(destinationMenuItemWatcher);
}

ret = wait_until(console, context, std::chrono::minutes(2), callbacks);
context.wait_for(std::chrono::milliseconds(100));
switch (ret){
case 0:
console.log("Detected overworld.");
return;
console.log("Detected overworld. Fly successful.");
return true;
case 1:
console.log("Detected map.");
console.log("Detected map. Pressing A to open map menu.");
// Press A to bring up the promp dialog on choosing "Fly here", "Set as destination", "Never mind".
pbf_press_button(context, BUTTON_A, 20, 130);
continue;
case 2:
console.log("Detected fly here prompt dialog.");
console.overlay().add_log("Fly", COLOR_WHITE);
console.overlay().add_log("Fly");
pbf_press_button(context, BUTTON_A, 20, 130);
continue;
case 3:
console.log("Detected fly confirmation prompt.");
pbf_press_button(context, BUTTON_A, 20, 130);
continue;

case 4:
console.log("Detected no fly spot here.");
console.overlay().add_log("No fly spot", COLOR_RED);
return false;
default:
throw OperationFailedException(
ErrorReport::SEND_ERROR_REPORT, console,
Expand Down Expand Up @@ -380,24 +406,7 @@ void enter_box_system_from_overworld(const ProgramInfo& info, ConsoleHandle& con
void leave_box_system_to_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
context.wait_for_all_requests();
console.log("Leave box system to overworld...");
OverworldWatcher overworld(COLOR_RED);
context.wait_for_all_requests();
int ret = run_until(
console, context,
[](BotBaseContext& context){
for (size_t c = 0; c < 10; c++){
pbf_press_button(context, BUTTON_B, 20, 230);
}
},
{overworld}
);
if (ret < 0){
throw OperationFailedException(
ErrorReport::SEND_ERROR_REPORT, console,
"leave_box_system_to_overworld(): Unknown state after 10 button B presses.",
true
);
}
press_Bs_to_back_to_overworld(info, console, context);
}


Expand Down Expand Up @@ -493,9 +502,9 @@ void leave_phone_to_overworld(const ProgramInfo& info, ConsoleHandle& console, B
}
}

// While in the current map zoom level, detect pokecenter icons and fly to the closest one.
// While in the current map zoom level, detect pokecenter icons and move the map cursor there.
// Return true if succeed. Return false if no visible pokcenter on map
bool fly_to_visible_closest_pokecenter_cur_zoom_level(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
bool detect_closest_pokecenter_and_move_map_cursor_there(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
context.wait_for_all_requests();
const auto snapshot_frame = console.video().snapshot().frame;
const size_t screen_width = snapshot_frame->width();
Expand Down Expand Up @@ -542,10 +551,37 @@ bool fly_to_visible_closest_pokecenter_cur_zoom_level(const ProgramInfo& info, C
console.overlay().add_log("Move Cursor to PokeCenter", COLOR_WHITE);
const uint16_t push_time = std::max(uint16_t(magnitude * scale + 0.5), uint16_t(3));
pbf_move_left_joystick(context, move_x, move_y, push_time, 30);
fly_to_overworld_from_map(info, console, context);
context.wait_for_all_requests();
return true;
}

// While in the current map zoom level, detect pokecenter icons and fly to the closest one.
// Return true if succeed. Return false if no visible pokcenter on map
bool fly_to_visible_closest_pokecenter_cur_zoom_level(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
const int max_try_count = 3;
for(int try_count = 0; try_count < max_try_count; ++try_count){
if (!detect_closest_pokecenter_and_move_map_cursor_there(info, console, context)){
return false;
}
bool check_fly_menuitem = true;
const bool success = fly_to_overworld_from_map(info, console, context, check_fly_menuitem);
if (success){
return true;
}
if (try_count + 1 < max_try_count){ // if this is not the last try:
console.log("Failed to find the fly menuitem. Restart the closest Pokecenter travel process.");
press_Bs_to_back_to_overworld(info, console, context);
open_map_from_overworld(info, console, context);
}
}
// last try failed:
throw OperationFailedException(
ErrorReport::SEND_ERROR_REPORT, console,
"fly_to_visible_closest_pokecenter_cur_zoom_level(): tried three times to fly to pokecenter, but no \"Fly\" menuitem.",
true
);
}


void fly_to_closest_pokecenter_on_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){
// Zoom in one level onto the map.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ void day_skip_from_overworld(ConsoleHandle& console, BotBaseContext& context);
void open_map_from_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context);

// From map, press A to fly to a travel spot.
void fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context);
// check_fly_menuitem == true: will detect if the "Fly" menuitem is available. Return false if no "Fly" menuitem. Return
// true if flying successful.
// check_fly_menuitem == false: will use GradientArrowDetector to check if a map menu is opened. No "Fly" menuitem check.
// The function always returns true. It throws an error in the case of no "Fly" menuitem. But the error message will be about
// timeout running the function.
bool fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context, bool check_fly_menuitem = false);

// Assume the user can set up picnic at current location, start picnic from overworld.
void picnic_from_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ ShinyHuntScatterbug::ShinyHuntScatterbug()
LockWhileRunning::LOCKED,
false
)
, SKIP_SANDWICH(
"<b>Whether to skip making sandwich:</b><br>"
"This is for debugging the program without waiting for sandwich making.",
LockWhileRunning::UNLOCKED,
false
)
, NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600))
, NOTIFICATIONS({
&NOTIFICATION_STATUS_UPDATE,
Expand All @@ -121,6 +127,7 @@ ShinyHuntScatterbug::ShinyHuntScatterbug()
if (PreloadSettings::instance().DEVELOPER_MODE){
PA_ADD_OPTION(SAVE_DEBUG_VIDEO);
PA_ADD_OPTION(DEBUG_WARP_TO_POKECENTER);
PA_ADD_OPTION(SKIP_SANDWICH);
}
PA_ADD_OPTION(LANGUAGE);
PA_ADD_OPTION(SANDWICH_OPTIONS);
Expand Down Expand Up @@ -205,7 +212,8 @@ void ShinyHuntScatterbug::handle_battles_and_back_to_pokecenter(SingleSwitchProg
bool first_iteration = true;
// a flag for the case that the action has finished but not yet returned to pokecenter
bool returned_to_pokecenter = false;
while(action_finished == false && returned_to_pokecenter == false){
while(action_finished == false || returned_to_pokecenter == false){
// env.console.overlay().add_log("Calculate what to do next");
int ret = run_until(
env.console, context,
[&](BotBaseContext& context){
Expand Down Expand Up @@ -240,6 +248,7 @@ void ShinyHuntScatterbug::handle_battles_and_back_to_pokecenter(SingleSwitchProg
m_encounter_watcher->throw_if_no_sound();
if (ret >= 0){
env.console.log("Detected battle.", COLOR_PURPLE);
env.console.overlay().add_log("Detected battle");
try{
bool caught, should_save;
m_encounter_tracker->process_battle(
Expand All @@ -255,7 +264,7 @@ void ShinyHuntScatterbug::handle_battles_and_back_to_pokecenter(SingleSwitchProg
}
}
// Back on the overworld.
} // end while(action_finished == false)
} // end while(action_finished == false || returned_to_pokecenter == false)
}

// Start at Mesagoza South Gate pokecenter, make a sandwich, then use let's go repeatedly until 30 min passes.
Expand Down Expand Up @@ -293,15 +302,20 @@ void ShinyHuntScatterbug::run_one_sandwich_iteration(SingleSwitchProgramEnvironm
pbf_press_button(context, BUTTON_L, 50, 40);
// Move forward
pbf_move_left_joystick(context, 128, 0, 180, 0);
picnic_from_overworld(env.program_info(), env.console, context);

pbf_move_left_joystick(context, 128, 0, 30, 40);
enter_sandwich_recipe_list(env.program_info(), env.console, context);
run_sandwich_maker(env, context, SANDWICH_OPTIONS);
last_sandwich_time = current_time();
leave_picnic(env.program_info(), env.console, context);
if (!SKIP_SANDWICH){
picnic_from_overworld(env.program_info(), env.console, context);

pbf_move_left_joystick(context, 128, 0, 30, 40);
enter_sandwich_recipe_list(env.program_info(), env.console, context);
run_sandwich_maker(env, context, SANDWICH_OPTIONS);
last_sandwich_time = current_time();
leave_picnic(env.program_info(), env.console, context);
} else {
last_sandwich_time = current_time();
}
}
);
env.console.overlay().add_log("Started Let's Go Paths");
save_if_needed();

// Which path to choose starting at the PokeCenter.
Expand All @@ -314,11 +328,12 @@ void ShinyHuntScatterbug::run_one_sandwich_iteration(SingleSwitchProgramEnvironm
for (;;path_id = (path_id + 1) % num_paths){
if (last_sandwich_time + std::chrono::minutes(30) < current_time()){
env.log("Sandwich expires.");
env.console.overlay().add_log("Sandwich expires", COLOR_WHITE);
env.console.overlay().add_log("Sandwich expires");
return;
}

env.console.log("Starting Let's Go hunting path...", COLOR_PURPLE);
env.console.overlay().add_log("Let's Go on Path " + std::to_string(path_id));

double hp = hp_watcher.last_known_value() * 100;
if (0 < hp){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class ShinyHuntScatterbug : public SingleSwitchProgramInstance{
// Debug options
BooleanCheckBoxOption SAVE_DEBUG_VIDEO;
BooleanCheckBoxOption DEBUG_WARP_TO_POKECENTER;
BooleanCheckBoxOption SKIP_SANDWICH;

EventNotificationOption NOTIFICATION_STATUS_UPDATE;
EventNotificationsOption NOTIFICATIONS;
Expand Down

0 comments on commit d8c1640

Please sign in to comment.