Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@
*
*/

#include <array>
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
#include "CommonTools/OCR/OCR_Routines.h"
#include "PokemonLZA_LocationNameReader.h"


namespace PokemonAutomation{
namespace Pokemon{
namespace NintendoSwitch{
namespace PokemonLZA{


LocationNameReader& LocationNameReader::instance(){
static LocationNameReader reader;
LocationNameOCR& LocationNameOCR::instance(){
static LocationNameOCR reader;
return reader;
}


LocationNameReader::LocationNameReader()
LocationNameOCR::LocationNameOCR()
: SmallDictionaryMatcher("PokemonLZA/LocationName.json")
{}

OCR::StringMatchResult LocationNameReader::read_substring(
OCR::StringMatchResult LocationNameOCR::read_substring(
Logger& logger,
Language language,
const ImageViewRGB32& image,
Expand All @@ -34,7 +38,45 @@ OCR::StringMatchResult LocationNameReader::read_substring(
);
}

LocationNameReader::LocationNameReader(Color color)
: m_color(color)
, m_box_location_name(get_all_box_locations(ImageFloatBox(0.035, 0.254, 0.287, 0.050)))
{}

void LocationNameReader::make_overlays(VideoOverlaySet& items) const{
for (size_t c = 0; c < PAGE_SIZE; c++){
items.add(m_color, m_box_location_name[c]);
}
}

std::array<ImageFloatBox, LocationNameReader::PAGE_SIZE> LocationNameReader::get_all_box_locations(ImageFloatBox initial_box){
std::array<ImageFloatBox, LocationNameReader::PAGE_SIZE> material_boxes;
double x = initial_box.x;
double width = initial_box.width;
double height = initial_box.height;
double initial_y = initial_box.y;
double y_spacing = 0.078;
for (size_t i = 0; i < LocationNameReader::PAGE_SIZE; i++){
double y = initial_y + i*y_spacing;
material_boxes[i] = ImageFloatBox(x, y, width, height);
}
return material_boxes;
}

OCR::StringMatchResult LocationNameReader::read_location_name(
const ImageViewRGB32& screen,
Logger& logger,
Language language,
size_t index
) const{
ImageViewRGB32 image = extract_box_reference(screen, m_box_location_name[index]);

OCR::StringMatchResult results;
results = LocationNameOCR::instance().read_substring(logger, language, image, OCR::BLACK_OR_WHITE_TEXT_FILTERS());

return results;
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@
#ifndef PokemonAutomation_Pokemon_LocationNameReader_H
#define PokemonAutomation_Pokemon_LocationNameReader_H

#include <array>
#include "CommonFramework/ImageTools/ImageBoxes.h"
#include "CommonTools/OCR/OCR_SmallDictionaryMatcher.h"
#include "CommonTools/VisualDetector.h"

namespace PokemonAutomation{
namespace Pokemon{
namespace NintendoSwitch{
namespace PokemonLZA{


class LocationNameReader : public OCR::SmallDictionaryMatcher{
class LocationNameOCR : public OCR::SmallDictionaryMatcher{
public:
static constexpr double MAX_LOG10P = -1.40;
static constexpr double MAX_LOG10P_SPREAD = 0.50;

public:
LocationNameReader();
LocationNameOCR();

static LocationNameReader& instance();
static LocationNameOCR& instance();

OCR::StringMatchResult read_substring(
Logger& logger,
Expand All @@ -31,7 +36,30 @@ class LocationNameReader : public OCR::SmallDictionaryMatcher{
) const;
};

class LocationNameReader{
public:
LocationNameReader(Color color = COLOR_WHITE);
static constexpr size_t PAGE_SIZE = 7;

void make_overlays(VideoOverlaySet& items) const;

// Read a single location display name via OCR given by the index
OCR::StringMatchResult read_location_name(
const ImageViewRGB32& screen,
Logger& logger,
Language language,
size_t index
) const;

private:
Color m_color;
// Store the boxes that contain each location name in the fast travel menu
std::array<ImageFloatBox, PAGE_SIZE> m_box_location_name;
// Get the location of all location boxes based on the initial box location
std::array<ImageFloatBox, PAGE_SIZE> get_all_box_locations(ImageFloatBox initial_box);
};

}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ GenerateLocationNameOCR_Descriptor::GenerateLocationNameOCR_Descriptor()
GenerateLocationNameOCR::GenerateLocationNameOCR()
: LANGUAGE(
"<b>Game Language:</b>",
LocationNameReader::instance().languages(),
LocationNameOCR::instance().languages(),
LockMode::LOCK_WHILE_RUNNING
)
, MODE(
Expand All @@ -66,7 +66,7 @@ void GenerateLocationNameOCR::read(
Logger& logger,
const ImageViewRGB32& image
) const{
OCR::StringMatchResult result = LocationNameReader::instance().read_substring(
OCR::StringMatchResult result = LocationNameOCR::instance().read_substring(
logger, LANGUAGE, image,
OCR::BLACK_OR_WHITE_TEXT_FILTERS()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "PokemonLZA/Programs/PokemonLZA_GameEntry.h"
#include "Pokemon/Pokemon_Strings.h"
#include "PokemonLZA/Programs/PokemonLZA_BasicNavigation.h"
#include "PokemonLZA/Programs/PokemonLZA_FastTravelNavigation.h"
#include "PokemonLZA/Programs/PokemonLZA_DonutBerrySession.h"
#include "PokemonLZA_DonutMaker.h"
#include <format>
Expand Down Expand Up @@ -403,44 +404,6 @@ void DonutMaker::open_berry_menu_from_ansha(SingleSwitchProgramEnvironment& env,
);
}

// A generic function to fast travel to an index in the fast travel menu and watch for overworld
void fast_travel_to_index(SingleSwitchProgramEnvironment& env, ProControllerContext& context, int location_index=0){
DonutMaker_Descriptor::Stats& stats = env.current_stats<DonutMaker_Descriptor::Stats>();

env.log("Fast traveling to location at index " + std::to_string(location_index));
bool zoom_to_max = false;
const bool require_icons = false;
open_map(env.console, context, zoom_to_max, require_icons);

// Press Y to load fast travel locaiton menu
pbf_press_button(context, BUTTON_Y, 100ms, 500ms);
context.wait_for_all_requests();

OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay());
int ret = run_until<ProControllerContext>(
env.console, context,
[&](ProControllerContext& context){
// Move cursor to desired location index
for (int i = 0; i < location_index; i++){
pbf_press_dpad(context, DPAD_DOWN, 50ms, 500ms);
}
pbf_mash_button(context, BUTTON_A, Seconds(10));
pbf_wait(context, Seconds(30)); // 30 sec to wait out potential day night change
},
{overworld}
);
if (ret != 0){
stats.errors++;
env.update_stats();
OperationFailedException::fire(
ErrorReport::SEND_ERROR_REPORT,
"donut_maker(): Unable to find overworld after fast traveling to location index " + std::to_string(location_index),
env.console
);
}
env.log("Detected overworld. Fast traveled to location index " + std::to_string(location_index));
}

// Exit the game and load the backup save
void load_backup_save(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
DonutMaker_Descriptor::Stats& stats = env.current_stats<DonutMaker_Descriptor::Stats>();
Expand Down Expand Up @@ -489,12 +452,8 @@ void reset_map_filter_state(SingleSwitchProgramEnvironment& env, ProControllerCo
env.log("Resetting fast travel map filters.");

open_map(env.console, context, false, false);
// Press Y and - to open fast travel filter menu
pbf_press_button(context, BUTTON_Y, 100ms, 500ms);
pbf_press_button(context, BUTTON_MINUS, 100ms, 500ms);
// Press Down and A to select "Facilities" filter
pbf_press_dpad(context, DPAD_DOWN, 100ms, 500ms);
pbf_press_button(context, BUTTON_A, 100ms, 500ms);
open_fast_travel_menu(env.console, context);
set_fast_travel_menu_filter(env.console, context, FAST_TRAVEL_FILTER::ALL_TRAVEL_SPOTS);

// Close out of map
exit_menu_to_overworld(env, context);
Expand All @@ -505,10 +464,19 @@ void reset_map_filter_state(SingleSwitchProgramEnvironment& env, ProControllerCo
}

// Move to in front of Ansha with button A shown
void move_to_ansha(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
void DonutMaker::move_to_ansha(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
DonutMaker_Descriptor::Stats& stats = env.current_stats<DonutMaker_Descriptor::Stats>();

fast_travel_to_index(env, context, 3); // Fast travel to Hotel Z
FastTravelState travel_status = open_map_and_fly_to(env.console, context, LANGUAGE, Location::HOTEL_Z);
if (travel_status != FastTravelState::SUCCESS){
stats.errors++;
env.update_stats();
OperationFailedException::fire(
ErrorReport::SEND_ERROR_REPORT,
"donut_maker(): Cannot fast travel to Hotel Z.",
env.console
);
}
context.wait_for(100ms); // Wait for player control to return
env.log("Detected overworld. Fast traveled to Hotel Zone");

Expand Down Expand Up @@ -564,18 +532,14 @@ void move_to_ansha(SingleSwitchProgramEnvironment& env, ProControllerContext& co
}

// Create a new backup save after making a donut to keep
void save_donut(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
void DonutMaker::save_donut(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
// DonutMaker_Descriptor::Stats& stats = env.current_stats<DonutMaker_Descriptor::Stats>();

env.log("Creating new backup save to keep the last made donut.");

// Stop talking to Ansha
exit_menu_to_overworld(env, context);
context.wait_for_all_requests();

// Fast travel to anywhere to set a new backup save after making a donut to keep
// Removed this since it's likely redundant because the program always fast travels to Hotel Z before making a donut
// fast_travel_to_index(env, context, 0, 3000ms);
}

// Check if all user defined limits are reached or the global max keepers limit is reached
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class DonutMaker : public SingleSwitchProgramInstance{
void animation_to_donut(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
void add_berries_and_make_donut(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
void open_berry_menu_from_ansha(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
void move_to_ansha(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
void save_donut(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
bool donut_iteration(SingleSwitchProgramEnvironment& env, ProControllerContext& context, std::vector<uint16_t>& match_counts);

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,76 +272,81 @@ FastTravelState open_map_and_fly_in_place(ConsoleHandle& console, ProControllerC
}


void move_map_cursor_from_entrance_to_zone(ConsoleHandle& console, ProControllerContext& context, WildZone zone){
void move_map_cursor_from_entrance_to_zone(ConsoleHandle& console, ProControllerContext& context, Location zone){
pbf_wait(context, 300ms);
switch(zone){
case WildZone::WILD_ZONE_1:
case Location::WILD_ZONE_1:
pbf_move_left_joystick(context, {-1, -0.173}, 120ms, 0ms);
break;
case WildZone::WILD_ZONE_2:
case Location::WILD_ZONE_2:
pbf_move_left_joystick(context, {-0.062, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_3:
case Location::WILD_ZONE_3:
pbf_move_left_joystick(context, {0, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_4:
case Location::WILD_ZONE_4:
pbf_move_left_joystick(context, {+1, 0}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_5:
case Location::WILD_ZONE_5:
pbf_move_left_joystick(context, {+0.331, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_6:
case Location::WILD_ZONE_6:
pbf_move_left_joystick(context, {-0.375, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_7:
case Location::WILD_ZONE_7:
pbf_move_left_joystick(context, {-1, +0.219}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_8:
case Location::WILD_ZONE_8:
pbf_move_left_joystick(context, {-1, -0.252}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_9:
case Location::WILD_ZONE_9:
pbf_move_left_joystick(context, {-0.453, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_10:
case Location::WILD_ZONE_10:
pbf_move_left_joystick(context, {+1, +0.297}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_11:
case Location::WILD_ZONE_11:
pbf_move_left_joystick(context, {-1, +0.688}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_12:
case Location::WILD_ZONE_12:
pbf_move_left_joystick(context, {-0.844, +1}, 150ms, 0ms);
break;
case WildZone::WILD_ZONE_13:
case Location::WILD_ZONE_13:
pbf_move_left_joystick(context, {-1, -0.252}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_14:
case Location::WILD_ZONE_14:
pbf_move_left_joystick(context, {-0.141, -1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_15:
case Location::WILD_ZONE_15:
pbf_move_left_joystick(context, {-1, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_16:
case Location::WILD_ZONE_16:
pbf_move_left_joystick(context, {+0.724, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_17:
case Location::WILD_ZONE_17:
pbf_move_left_joystick(context, {+0.646, +1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_18:
case Location::WILD_ZONE_18:
pbf_move_left_joystick(context, {-0.844, -1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_19:
case Location::WILD_ZONE_19:
pbf_move_left_joystick(context, {-0.375, -1}, 100ms, 0ms);
break;
case WildZone::WILD_ZONE_20_NO_DISTORTION:
case Location::WILD_ZONE_20_NO_DISTORTION:
pbf_move_left_joystick(context, {-1, +0.297}, 140ms, 0ms);
break;
case WildZone::WILD_ZONE_20_WITH_DISTORTION:
case Location::WILD_ZONE_20_WITH_DISTORTION:
// During the distortion happening on top of Lumiose Tower as part
// of the Mega Dimension DLC story, the wild zone 20 fast travel
// symbol on the map is moved to the entrance gate. So we only
// need a tiny left joystick push.
pbf_move_left_joystick(context, {-0.219, +0.219}, 100ms, 0ms);
break;
default:
throw InternalProgramError(
nullptr, PA_CURRENT_FUNCTION,
"move_map_cursor_from_entrance_to_zone(): Unsupported zone location."
);
}
pbf_wait(context, 300ms);
}
Expand Down
Loading