Skip to content
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 @@ -77,7 +77,18 @@ class ButtonWatcher : public DetectorToFinder<ButtonDetector>{
: DetectorToFinder("ButtonWatcher", hold_duration, color, button_type, box, overlay)
{}
};

class ButtonGoneWatcher : public DetectorToFinder<ButtonDetector>{
public:
ButtonGoneWatcher(
Color color,
ButtonType button_type,
const ImageFloatBox& box,
VideoOverlay* overlay = nullptr,
std::chrono::milliseconds hold_duration = std::chrono::milliseconds(250)
)
: DetectorToFinder("ButtonGoneWatcher", FinderType::GONE, hold_duration, color, button_type, box, overlay)
{}
};



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class SelectionArrowDetector : public StaticScreenDetector{
const ImageFloatBox& box
);

const ImageFloatBox& last_detected() const { return m_last_detected; }

virtual void make_overlays(VideoOverlaySet& items) const override;

// This is not const so that detectors can save/cache state.
Expand Down
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 @@ -415,48 +416,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, 100ms, 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 @@ -504,13 +463,9 @@ void exit_menu_to_overworld(SingleSwitchProgramEnvironment& env, ProControllerCo
void reset_map_filter_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
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_map(env.console, context, true, false);
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 @@ -521,10 +476,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 @@ -580,18 +544,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 @@ -50,11 +50,9 @@ 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);
bool donut_iteration(
SingleSwitchProgramEnvironment& env,
ProControllerContext& context,
std::vector<uint16_t>& kept_counts
);
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>& kept_counts);

private:

Expand Down
Loading