Skip to content

Commit 17099f9

Browse files
author
Gin
committed
refactor LA MMO Finder code
1 parent 9586fda commit 17099f9

File tree

3 files changed

+19
-288
lines changed

3 files changed

+19
-288
lines changed

SerialPrograms/CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -896,16 +896,16 @@ file(GLOB MAIN_SOURCES
896896
Source/NintendoSwitch/Controllers/SerialPABotBase/NintendoSwitch_SerialPABotBase_WirelessJoycon.h
897897
Source/NintendoSwitch/Controllers/SerialPABotBase/NintendoSwitch_SerialPABotBase_WirelessProController.cpp
898898
Source/NintendoSwitch/Controllers/SerialPABotBase/NintendoSwitch_SerialPABotBase_WirelessProController.h
899+
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase3_ControllerState.h
900+
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase3_ProController.cpp
901+
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase3_ProController.h
899902
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_Connection.cpp
900903
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_Connection.h
901904
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_Descriptor.cpp
902905
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_Descriptor.h
903906
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_ProController.cpp
904907
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_ProController.h
905908
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_SelectorWidget.h
906-
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase3_ControllerState.h
907-
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase3_ProController.cpp
908-
Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase3_ProController.h
909909
Source/NintendoSwitch/DevPrograms/BoxDraw.cpp
910910
Source/NintendoSwitch/DevPrograms/BoxDraw.h
911911
Source/NintendoSwitch/DevPrograms/JoyconProgram.cpp
@@ -1342,6 +1342,8 @@ file(GLOB MAIN_SOURCES
13421342
Source/PokemonLA/Programs/General/PokemonLA_ClothingBuyer.h
13431343
Source/PokemonLA/Programs/General/PokemonLA_DistortionWaiter.cpp
13441344
Source/PokemonLA/Programs/General/PokemonLA_DistortionWaiter.h
1345+
Source/PokemonLA/Programs/General/PokemonLA_MMORoutines.cpp
1346+
Source/PokemonLA/Programs/General/PokemonLA_MMORoutines.h
13451347
Source/PokemonLA/Programs/General/PokemonLA_OutbreakFinder.cpp
13461348
Source/PokemonLA/Programs/General/PokemonLA_OutbreakFinder.h
13471349
Source/PokemonLA/Programs/General/PokemonLA_PokedexTasksReader.cpp

SerialPrograms/Source/PokemonLA/Programs/General/PokemonLA_OutbreakFinder.cpp

Lines changed: 14 additions & 276 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,20 @@
2020
#include "Pokemon/Resources/Pokemon_PokemonNames.h"
2121
#include "Pokemon/Inference/Pokemon_NameReader.h"
2222
#include "PokemonLA/PokemonLA_TravelLocations.h"
23-
#include "PokemonLA/Inference/PokemonLA_DialogDetector.h"
2423
#include "PokemonLA/Inference/Map/PokemonLA_MapDetector.h"
2524
#include "PokemonLA/Inference/Map/PokemonLA_SelectedRegionDetector.h"
2625
#include "PokemonLA/Inference/Map/PokemonLA_OutbreakReader.h"
27-
#include "PokemonLA/Inference/Map/PokemonLA_MapMissionTabReader.h"
28-
#include "PokemonLA/Inference/Map/PokemonLA_MapZoomLevelReader.h"
29-
#include "PokemonLA/Inference/Map/PokemonLA_MMOSpriteStarSymbolDetector.h"
30-
#include "PokemonLA/Inference/Map/PokemonLA_PokemonMapSpriteReader.h"
26+
#include "PokemonLA/PokemonLA_Settings.h"
3127
#include "PokemonLA/Programs/PokemonLA_GameEntry.h"
3228
#include "PokemonLA/Programs/PokemonLA_GameSave.h"
29+
#include "PokemonLA/Programs/PokemonLA_RegionNavigation.h"
3330
#include "PokemonLA/Resources/PokemonLA_AvailablePokemon.h"
3431
#include "PokemonLA/Resources/PokemonLA_PokemonSprites.h"
3532
#include "PokemonLA/Inference/Objects/PokemonLA_ButtonDetector.h"
3633
#include "PokemonLA/Inference/Objects/PokemonLA_MMOQuestionMarkDetector.h"
34+
#include "PokemonLA_MMORoutines.h"
3735
#include "PokemonLA_OutbreakFinder.h"
38-
#include "PokemonLA/Programs/PokemonLA_RegionNavigation.h"
39-
#include "PokemonLA/PokemonLA_Settings.h"
36+
4037

4138
#include <sstream>
4239

@@ -102,36 +99,6 @@ const StringSelectDatabase& MMO_DATABASE(){
10299
}
103100

104101

105-
106-
namespace{
107-
108-
109-
110-
std::vector<std::string> load_mmo_names(){
111-
return {
112-
"fieldlands-mmo",
113-
"mirelands-mmo",
114-
"coastlands-mmo",
115-
"highlands-mmo",
116-
"icelands-mmo"
117-
};
118-
}
119-
120-
// The name of each MMO event happening at each region. Their slugs are:
121-
// - "fieldlands-mmo"
122-
// - "mirelands-mmo"
123-
// - "coastlands-mmo"
124-
// - "highlands-mmo"
125-
// - "icelands-mmo"
126-
const std::vector<std::string>& MMO_NAMES(){
127-
const static std::vector<std::string> mmo_names = load_mmo_names();
128-
return mmo_names;
129-
}
130-
131-
132-
} // anonymous namespace
133-
134-
135102
OutbreakFinder_Descriptor::OutbreakFinder_Descriptor()
136103
: SingleSwitchProgramDescriptor(
137104
"PokemonLA:OutbreakFinder",
@@ -435,244 +402,6 @@ void OutbreakFinder::goto_region_and_return(SingleSwitchProgramEnvironment& env,
435402
mash_A_to_change_region(env, env.console, context);
436403
}
437404

438-
std::set<std::string> OutbreakFinder::enter_region_and_read_MMO(
439-
SingleSwitchProgramEnvironment& env, ProControllerContext& context,
440-
const std::string& mmo_name,
441-
const std::set<std::string>& desired_MMOs,
442-
const std::set<std::string>& desired_star_MMOs
443-
){
444-
OutbreakFinder_Descriptor::Stats& stats = env.current_stats<OutbreakFinder_Descriptor::Stats>();
445-
446-
MapRegion region = MapRegion::NONE;
447-
TravelLocation location = TravelLocations::instance().Fieldlands_Fieldlands;
448-
Camp camp = Camp::FIELDLANDS_FIELDLANDS;
449-
for(size_t i = 0; i < 5; i++){
450-
if (mmo_name == MMO_NAMES()[i]){
451-
switch (i){
452-
case 0:
453-
region = MapRegion::FIELDLANDS;
454-
location = TravelLocations::instance().Fieldlands_Fieldlands;
455-
camp = Camp::FIELDLANDS_FIELDLANDS;
456-
break;
457-
case 1:
458-
region = MapRegion::MIRELANDS;
459-
location = TravelLocations::instance().Mirelands_Mirelands;
460-
camp = Camp::MIRELANDS_MIRELANDS;
461-
break;
462-
case 2:
463-
region = MapRegion::COASTLANDS;
464-
location = TravelLocations::instance().Coastlands_Beachside;
465-
camp = Camp::COASTLANDS_BEACHSIDE;
466-
break;
467-
case 3:
468-
region = MapRegion::HIGHLANDS;
469-
location = TravelLocations::instance().Highlands_Highlands;
470-
camp = Camp::HIGHLANDS_HIGHLANDS;
471-
break;
472-
case 4:
473-
region = MapRegion::ICELANDS;
474-
location = TravelLocations::instance().Icelands_Snowfields;
475-
camp = Camp::ICELANDS_SNOWFIELDS;
476-
break;
477-
}
478-
}
479-
}
480-
if (region == MapRegion::NONE){
481-
throw InternalProgramError(&env.console.logger(), PA_CURRENT_FUNCTION, "No MMO region name found.");
482-
}
483-
484-
env.log("Go to " + std::string(MAP_REGION_NAMES[int(region)]) + " to check MMO.");
485-
goto_camp_from_jubilife(env, env.console, context, location);
486-
487-
// Open map
488-
pbf_press_button(context, BUTTON_MINUS, 50, 100);
489-
context.wait_for_all_requests();
490-
491-
// Take a photo of the map before
492-
VideoSnapshot question_mark_image = env.console.video().snapshot();
493-
494-
// Fix zoom level:
495-
const int zoom_level = read_map_zoom_level(question_mark_image);
496-
if (zoom_level < 0){
497-
OperationFailedException::fire(
498-
ErrorReport::SEND_ERROR_REPORT,
499-
"Canot read map zoom level.",
500-
env.console
501-
);
502-
}
503-
504-
if (zoom_level == 0){
505-
pbf_press_button(context, BUTTON_ZR, 50, 50);
506-
context.wait_for_all_requests();
507-
question_mark_image = env.console.video().snapshot();
508-
}else if (zoom_level == 2){
509-
pbf_press_button(context, BUTTON_ZL, 50, 50);
510-
context.wait_for_all_requests();
511-
question_mark_image = env.console.video().snapshot();
512-
}
513-
514-
// Move cursor away so that it does not show a text box that occludes MMO sprites.
515-
pbf_move_left_joystick(context, 0, 0, 300, 30);
516-
context.wait_for_all_requests();
517-
518-
// Fix Missions & Requests tab:
519-
if (is_map_mission_tab_raised(question_mark_image)){
520-
pbf_press_button(context, BUTTON_R, 50, 100);
521-
context.wait_for_all_requests();
522-
question_mark_image = env.console.video().snapshot();
523-
}
524-
525-
// Now detect question marks:
526-
MMOQuestionMarkDetector question_mark_detector(env.logger());
527-
528-
const auto quest_results = question_mark_detector.detect_MMOs_on_region_map(question_mark_image);
529-
env.log("Detected MMO question marks:");
530-
for(const auto& box : quest_results){
531-
std::ostringstream os;
532-
os << "- " << box.center_x() << ", " << box.center_y() << " " << box.width() << " x " << box.height();
533-
env.log(os.str());
534-
}
535-
536-
// Clean the detected boxes, make them square.
537-
std::vector<ImagePixelBox> new_boxes;
538-
for (size_t i = 0; i < quest_results.size(); i++){
539-
const auto& box = quest_results[i];
540-
541-
pxint_t radius = (pxint_t)((box.width() + box.height()) / 4 + 0.5);
542-
pxint_t center_x = (pxint_t)box.center_x();
543-
pxint_t center_y = (pxint_t)box.center_y();
544-
auto new_box = ImagePixelBox(center_x - radius, center_y - radius, center_x + radius, center_y + radius);
545-
new_boxes.push_back(new_box);
546-
}
547-
548-
// Leave map view, back to overworld
549-
pbf_press_button(context, BUTTON_B, 20, 50);
550-
551-
// Now go to Mai to see the reviewed map
552-
goto_Mai_from_camp(env.logger(), context, camp);
553-
554-
pbf_mash_button(context, BUTTON_A, 350);
555-
context.wait_for_all_requests();
556-
557-
// Wait for the last dialog box before the MMO pokemon sprites are revealed.
558-
{
559-
EventDialogDetector event_dialog_detector(env.logger(), env.console.overlay(), true);
560-
int ret = wait_until(env.console, context, std::chrono::seconds(10), {{event_dialog_detector}});
561-
if (ret < 0){
562-
OperationFailedException::fire(
563-
ErrorReport::SEND_ERROR_REPORT,
564-
"Dialog box not detected when waiting for MMO map.",
565-
env.console
566-
);
567-
}
568-
}
569-
pbf_press_button(context, BUTTON_B, 50, 50);
570-
571-
while (true){
572-
EventDialogDetector event_dialog_detector(env.logger(), env.console.overlay(), true);
573-
MapDetector map_detector;
574-
context.wait_for_all_requests();
575-
int ret = wait_until(
576-
env.console, context, std::chrono::seconds(10),
577-
{event_dialog_detector, map_detector}
578-
);
579-
switch (ret){
580-
case 0:
581-
env.console.log("Detected dialog.");
582-
pbf_press_button(context, BUTTON_B, 20, 105);
583-
continue;
584-
case 1:
585-
env.console.log("Found revealed map thanks to Munchlax!");
586-
break;
587-
default:
588-
OperationFailedException::fire(
589-
ErrorReport::SEND_ERROR_REPORT,
590-
"Map not detected after talking to Mai.",
591-
env.console
592-
);
593-
}
594-
break;
595-
}
596-
597-
#if 0
598-
MapDetector map_detector;
599-
ret = wait_until(env.console, context, std::chrono::seconds(5), {{map_detector}});
600-
if (ret < 0){
601-
OperationFailedException::fire(
602-
env.console, ErrorReport::SEND_ERROR_REPORT,
603-
"Map not detected after talking to Mai.",
604-
true
605-
);
606-
}
607-
env.console.log("Found revealed map thanks to Munchlax!");
608-
#endif
609-
610-
VideoOverlaySet mmo_sprites_overlay(env.console);
611-
for (size_t i = 0; i < new_boxes.size(); i++){
612-
mmo_sprites_overlay.add(COLOR_BLUE, pixelbox_to_floatbox(question_mark_image, new_boxes[i]));
613-
}
614-
615-
// Move cursor away so that it does not show a text box that occludes MMO sprites.
616-
pbf_move_left_joystick(context, 0, 0, 300, 30);
617-
context.wait_for_all_requests();
618-
619-
std::set<std::string> found;
620-
621-
// Check MMO results:
622-
std::vector<std::string> sprites;
623-
VideoSnapshot sprites_screen = env.console.video().snapshot();
624-
for (size_t i = 0; i < new_boxes.size(); i++){
625-
auto result = match_sprite_on_map(env.logger(), sprites_screen, new_boxes[i], region, DEBUG_MODE);
626-
env.console.log("Found MMO sprite " + result.slug);
627-
stats.mmo_pokemon++;
628-
629-
sprites.push_back(result.slug);
630-
if (desired_MMOs.find(result.slug) != desired_MMOs.end()){
631-
found.insert(result.slug);
632-
}
633-
}
634-
635-
// Check star MMO results:
636-
std::vector<ImagePixelBox> star_boxes;
637-
for (size_t i = 0; i < new_boxes.size(); i++){
638-
const auto& sprite_box = new_boxes[i];
639-
pxint_t radius = (pxint_t)sprite_box.width() / 2;
640-
pxint_t center_x = (pxint_t)sprite_box.center_x();
641-
pxint_t center_y = (pxint_t)sprite_box.center_y();
642-
ImagePixelBox star_box(
643-
center_x + radius/10,
644-
center_y - radius*16/10,
645-
center_x + radius * 5/4,
646-
center_y
647-
);
648-
star_boxes.push_back(std::move(star_box));
649-
}
650-
651-
MMOSpriteStarSymbolDetector star_detector(sprites_screen, star_boxes);
652-
653-
env.log("Detect star symbols...");
654-
wait_until(env.console, context, std::chrono::seconds(5), {{star_detector}});
655-
for (size_t i = 0; i < new_boxes.size(); i++){
656-
std::ostringstream os;
657-
os << "- " << sprites[i] << " box [" << star_boxes[i].min_x << ", " << star_boxes[i].min_y
658-
<< star_boxes[i].max_x << ", " << star_boxes[i].max_y << "]"
659-
<< " motion: " << star_detector.animation_value(i)
660-
<< " color: " << star_detector.symbol_color(i);
661-
if (star_detector.is_star(i)){
662-
stats.stars++;
663-
os << ", has star";
664-
if (desired_star_MMOs.find(sprites[i]) != desired_star_MMOs.end()){
665-
found.insert(sprites[i]);
666-
}
667-
}
668-
env.log(os.str());
669-
}
670-
671-
env.update_stats();
672-
673-
return found;
674-
}
675-
676405

677406
std::vector<std::string> OutbreakFinder::run_iteration(
678407
SingleSwitchProgramEnvironment& env, ProControllerContext& context,
@@ -735,7 +464,16 @@ std::vector<std::string> OutbreakFinder::run_iteration(
735464
save_game_from_overworld(env, env.console, context);
736465

737466
for(const auto& mmo_name: found_hisui_map_events){
738-
std::set<std::string> found_pokemon = enter_region_and_read_MMO(env, context, mmo_name, desired_MMOs, desired_star_MMOs);
467+
OutbreakFinder_Descriptor::Stats& stats = env.current_stats<OutbreakFinder_Descriptor::Stats>();
468+
int num_new_mmo_pokemon_found = 0;
469+
int num_new_star_mmo_found = 0;
470+
std::set<std::string> found_pokemon = enter_region_and_read_MMO(
471+
env, context, mmo_name, desired_MMOs, desired_star_MMOs, DEBUG_MODE,
472+
num_new_mmo_pokemon_found, num_new_star_mmo_found);
473+
stats.mmo_pokemon += num_new_mmo_pokemon_found;
474+
stats.stars += num_new_star_mmo_found;
475+
env.update_stats();
476+
739477
if (found_pokemon.size() > 0){
740478
stats.matches += found_pokemon.size();
741479
std::ostringstream os;

SerialPrograms/Source/PokemonLA/Programs/General/PokemonLA_OutbreakFinder.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,6 @@ class OutbreakFinder : public SingleSwitchProgramInstance{
6868
const std::set<std::string>& desired_events
6969
);
7070

71-
// Enter a map with MMO and read names of the pokemon appearing in the MMO.
72-
// - mmo_name: MMO event slug, e.g. "fieldlands-mmo"
73-
std::set<std::string> enter_region_and_read_MMO(
74-
SingleSwitchProgramEnvironment& env, ProControllerContext& context,
75-
const std::string& mmo_name,
76-
const std::set<std::string>& desired_MMOs,
77-
const std::set<std::string>& desired_star_MMOs
78-
);
79-
8071
// Go to a random wild region and return to refresh outbreaks.
8172
// If `inside_map` is true, the function is called when the game is inside the travel map.
8273
// Otherwise, the function is called when the player character is standing at the program start location.

0 commit comments

Comments
 (0)