1+ /* Pokemon LA MMO Routines
2+ *
3+ * Functions to run MMO related tasks
4+ *
5+ * From: https://github.com/PokemonAutomation/
6+ *
7+ */
8+
9+ #include " CommonFramework/Exceptions/OperationFailedException.h"
10+ #include " CommonFramework/ImageTools/ImageBoxes.h"
11+ #include " CommonFramework/VideoPipeline/VideoFeed.h"
12+ #include " CommonTools/Async/InferenceRoutines.h"
13+ #include " NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
14+ #include " NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h"
15+ #include " PokemonLA/Inference/Map/PokemonLA_PokemonMapSpriteReader.h"
16+ #include " PokemonLA/Inference/Map/PokemonLA_MapDetector.h"
17+ #include " PokemonLA/Inference/Map/PokemonLA_MapMissionTabReader.h"
18+ #include " PokemonLA/Inference/Map/PokemonLA_MapZoomLevelReader.h"
19+ #include " PokemonLA/Inference/Map/PokemonLA_MMOSpriteStarSymbolDetector.h"
20+ #include " PokemonLA/Inference/Objects/PokemonLA_MMOQuestionMarkDetector.h"
21+ #include " PokemonLA/Inference/PokemonLA_DialogDetector.h"
22+ #include " PokemonLA/PokemonLA_Locations.h"
23+ #include " PokemonLA/PokemonLA_TravelLocations.h"
24+ #include " PokemonLA/Programs/PokemonLA_RegionNavigation.h"
25+ #include < sstream>
26+
27+ namespace PokemonAutomation {
28+ namespace NintendoSwitch {
29+ namespace PokemonLA {
30+
31+
32+ std::vector<std::string> load_mmo_names (){
33+ return {
34+ " fieldlands-mmo" ,
35+ " mirelands-mmo" ,
36+ " coastlands-mmo" ,
37+ " highlands-mmo" ,
38+ " icelands-mmo"
39+ };
40+ }
41+
42+ const std::vector<std::string>& MMO_NAMES (){
43+ const static std::vector<std::string> mmo_names = load_mmo_names ();
44+ return mmo_names;
45+ }
46+
47+
48+ std::set<std::string> enter_region_and_read_MMO (
49+ SingleSwitchProgramEnvironment& env, ProControllerContext& context,
50+ const std::string& mmo_name,
51+ const std::set<std::string>& desired_MMOs,
52+ const std::set<std::string>& desired_star_MMOs,
53+ bool debug_mode,
54+ int & num_mmo_pokemon_found,
55+ int & num_star_mmo_found
56+ ){
57+ MapRegion region = MapRegion::NONE;
58+ TravelLocation location = TravelLocations::instance ().Fieldlands_Fieldlands ;
59+ Camp camp = Camp::FIELDLANDS_FIELDLANDS;
60+ for (size_t i = 0 ; i < 5 ; i++){
61+ if (mmo_name == MMO_NAMES ()[i]){
62+ switch (i){
63+ case 0 :
64+ region = MapRegion::FIELDLANDS;
65+ location = TravelLocations::instance ().Fieldlands_Fieldlands ;
66+ camp = Camp::FIELDLANDS_FIELDLANDS;
67+ break ;
68+ case 1 :
69+ region = MapRegion::MIRELANDS;
70+ location = TravelLocations::instance ().Mirelands_Mirelands ;
71+ camp = Camp::MIRELANDS_MIRELANDS;
72+ break ;
73+ case 2 :
74+ region = MapRegion::COASTLANDS;
75+ location = TravelLocations::instance ().Coastlands_Beachside ;
76+ camp = Camp::COASTLANDS_BEACHSIDE;
77+ break ;
78+ case 3 :
79+ region = MapRegion::HIGHLANDS;
80+ location = TravelLocations::instance ().Highlands_Highlands ;
81+ camp = Camp::HIGHLANDS_HIGHLANDS;
82+ break ;
83+ case 4 :
84+ region = MapRegion::ICELANDS;
85+ location = TravelLocations::instance ().Icelands_Snowfields ;
86+ camp = Camp::ICELANDS_SNOWFIELDS;
87+ break ;
88+ }
89+ }
90+ }
91+ if (region == MapRegion::NONE){
92+ throw InternalProgramError (&env.console .logger (), PA_CURRENT_FUNCTION, " No MMO region name found." );
93+ }
94+
95+ env.log (" Go to " + std::string (MAP_REGION_NAMES[int (region)]) + " to check MMO." );
96+ goto_camp_from_jubilife (env, env.console , context, location);
97+
98+ // Open map
99+ pbf_press_button (context, BUTTON_MINUS, 50 , 100 );
100+ context.wait_for_all_requests ();
101+
102+ // Take a photo of the map before
103+ VideoSnapshot question_mark_image = env.console .video ().snapshot ();
104+
105+ // Fix zoom level:
106+ const int zoom_level = read_map_zoom_level (question_mark_image);
107+ if (zoom_level < 0 ){
108+ OperationFailedException::fire (
109+ ErrorReport::SEND_ERROR_REPORT,
110+ " Canot read map zoom level." ,
111+ env.console
112+ );
113+ }
114+
115+ if (zoom_level == 0 ){
116+ pbf_press_button (context, BUTTON_ZR, 50 , 50 );
117+ context.wait_for_all_requests ();
118+ question_mark_image = env.console .video ().snapshot ();
119+ }else if (zoom_level == 2 ){
120+ pbf_press_button (context, BUTTON_ZL, 50 , 50 );
121+ context.wait_for_all_requests ();
122+ question_mark_image = env.console .video ().snapshot ();
123+ }
124+
125+ // Move cursor away so that it does not show a text box that occludes MMO sprites.
126+ pbf_move_left_joystick (context, 0 , 0 , 300 , 30 );
127+ context.wait_for_all_requests ();
128+
129+ // Fix Missions & Requests tab:
130+ if (is_map_mission_tab_raised (question_mark_image)){
131+ pbf_press_button (context, BUTTON_R, 50 , 100 );
132+ context.wait_for_all_requests ();
133+ question_mark_image = env.console .video ().snapshot ();
134+ }
135+
136+ // Now detect question marks:
137+ MMOQuestionMarkDetector question_mark_detector (env.logger ());
138+
139+ const auto quest_results = question_mark_detector.detect_MMOs_on_region_map (question_mark_image);
140+ env.log (" Detected MMO question marks:" );
141+ for (const auto & box : quest_results){
142+ std::ostringstream os;
143+ os << " - " << box.center_x () << " , " << box.center_y () << " " << box.width () << " x " << box.height ();
144+ env.log (os.str ());
145+ }
146+
147+ // Clean the detected boxes, make them square.
148+ std::vector<ImagePixelBox> new_boxes;
149+ for (size_t i = 0 ; i < quest_results.size (); i++){
150+ const auto & box = quest_results[i];
151+
152+ pxint_t radius = (pxint_t )((box.width () + box.height ()) / 4 + 0.5 );
153+ pxint_t center_x = (pxint_t )box.center_x ();
154+ pxint_t center_y = (pxint_t )box.center_y ();
155+ auto new_box = ImagePixelBox (center_x - radius, center_y - radius, center_x + radius, center_y + radius);
156+ new_boxes.push_back (new_box);
157+ }
158+
159+ // Leave map view, back to overworld
160+ pbf_press_button (context, BUTTON_B, 20 , 50 );
161+
162+ // Now go to Mai to see the reviewed map
163+ goto_Mai_from_camp (env.logger (), context, camp);
164+
165+ pbf_mash_button (context, BUTTON_A, 350 );
166+ context.wait_for_all_requests ();
167+
168+ // Wait for the last dialog box before the MMO pokemon sprites are revealed.
169+ {
170+ EventDialogDetector event_dialog_detector (env.logger (), env.console .overlay (), true );
171+ int ret = wait_until (env.console , context, std::chrono::seconds (10 ), {{event_dialog_detector}});
172+ if (ret < 0 ){
173+ OperationFailedException::fire (
174+ ErrorReport::SEND_ERROR_REPORT,
175+ " Dialog box not detected when waiting for MMO map." ,
176+ env.console
177+ );
178+ }
179+ }
180+ pbf_press_button (context, BUTTON_B, 50 , 50 );
181+
182+ while (true ){
183+ EventDialogDetector event_dialog_detector (env.logger (), env.console .overlay (), true );
184+ MapDetector map_detector;
185+ context.wait_for_all_requests ();
186+ int ret = wait_until (
187+ env.console , context, std::chrono::seconds (10 ),
188+ {event_dialog_detector, map_detector}
189+ );
190+ switch (ret){
191+ case 0 :
192+ env.console .log (" Detected dialog." );
193+ pbf_press_button (context, BUTTON_B, 20 , 105 );
194+ continue ;
195+ case 1 :
196+ env.console .log (" Found revealed map thanks to Munchlax!" );
197+ break ;
198+ default :
199+ OperationFailedException::fire (
200+ ErrorReport::SEND_ERROR_REPORT,
201+ " Map not detected after talking to Mai." ,
202+ env.console
203+ );
204+ }
205+ break ;
206+ }
207+
208+ #if 0
209+ MapDetector map_detector;
210+ ret = wait_until(env.console, context, std::chrono::seconds(5), {{map_detector}});
211+ if (ret < 0){
212+ OperationFailedException::fire(
213+ env.console, ErrorReport::SEND_ERROR_REPORT,
214+ "Map not detected after talking to Mai.",
215+ true
216+ );
217+ }
218+ env.console.log("Found revealed map thanks to Munchlax!");
219+ #endif
220+
221+ VideoOverlaySet mmo_sprites_overlay (env.console );
222+ for (size_t i = 0 ; i < new_boxes.size (); i++){
223+ mmo_sprites_overlay.add (COLOR_BLUE, pixelbox_to_floatbox (question_mark_image, new_boxes[i]));
224+ }
225+
226+ // Move cursor away so that it does not show a text box that occludes MMO sprites.
227+ pbf_move_left_joystick (context, 0 , 0 , 300 , 30 );
228+ context.wait_for_all_requests ();
229+
230+ std::set<std::string> found;
231+
232+ // Check MMO results:
233+ std::vector<std::string> sprites;
234+ VideoSnapshot sprites_screen = env.console .video ().snapshot ();
235+ for (size_t i = 0 ; i < new_boxes.size (); i++){
236+ auto result = match_sprite_on_map (env.logger (), sprites_screen, new_boxes[i], region, debug_mode);
237+ env.console .log (" Found MMO sprite " + result.slug );
238+ num_mmo_pokemon_found++;
239+
240+ sprites.push_back (result.slug );
241+ if (desired_MMOs.find (result.slug ) != desired_MMOs.end ()){
242+ found.insert (result.slug );
243+ }
244+ }
245+
246+ // Check star MMO results:
247+ std::vector<ImagePixelBox> star_boxes;
248+ for (size_t i = 0 ; i < new_boxes.size (); i++){
249+ const auto & sprite_box = new_boxes[i];
250+ pxint_t radius = (pxint_t )sprite_box.width () / 2 ;
251+ pxint_t center_x = (pxint_t )sprite_box.center_x ();
252+ pxint_t center_y = (pxint_t )sprite_box.center_y ();
253+ ImagePixelBox star_box (
254+ center_x + radius/10 ,
255+ center_y - radius*16 /10 ,
256+ center_x + radius * 5 /4 ,
257+ center_y
258+ );
259+ star_boxes.push_back (std::move (star_box));
260+ }
261+
262+ MMOSpriteStarSymbolDetector star_detector (sprites_screen, star_boxes);
263+
264+ env.log (" Detect star symbols..." );
265+ wait_until (env.console , context, std::chrono::seconds (5 ), {{star_detector}});
266+ for (size_t i = 0 ; i < new_boxes.size (); i++){
267+ std::ostringstream os;
268+ os << " - " << sprites[i] << " box [" << star_boxes[i].min_x << " , " << star_boxes[i].min_y
269+ << star_boxes[i].max_x << " , " << star_boxes[i].max_y << " ]"
270+ << " motion: " << star_detector.animation_value (i)
271+ << " color: " << star_detector.symbol_color (i);
272+ if (star_detector.is_star (i)){
273+ num_star_mmo_found++;
274+ os << " , has star" ;
275+ if (desired_star_MMOs.find (sprites[i]) != desired_star_MMOs.end ()){
276+ found.insert (sprites[i]);
277+ }
278+ }
279+ env.log (os.str ());
280+ }
281+ return found;
282+ }
283+
284+
285+
286+ }
287+ }
288+ }
0 commit comments