@@ -46,6 +46,7 @@ language
4646#include " Pokemon/Resources/Pokemon_PokemonSlugs.h"
4747#include " Pokemon/Pokemon_BoxCursor.h"
4848#include " Pokemon/Pokemon_CollectedPokemonInfo.h"
49+ #include " PokemonHome/Inference/PokemonHome_ButtonDetector.h"
4950#include " PokemonHome/Inference/PokemonHome_BoxGenderDetector.h"
5051#include " PokemonHome/Inference/PokemonHome_BallReader.h"
5152#include " PokemonHome_BoxSorter.h"
@@ -371,17 +372,13 @@ std::array<size_t, 2> find_occupied_slots_in_box(
371372 return first_pokemon_slot;
372373}
373374
374- void BoxSorter::program (SingleSwitchProgramEnvironment& env, ProControllerContext& context){
375- StartProgramChecks::check_performance_class_wired_or_wireless (context);
376-
377- const std::vector<SortingRule> sort_preferences = SORT_TABLE.preferences ();
378- if (sort_preferences.empty ()){
379- throw UserSetupError (env.console , " At least one sorting method selection needs to be made!" );
380- }
381-
382- BoxSorter_Descriptor::Stats& stats = env.current_stats < BoxSorter_Descriptor::Stats>();
375+ // Read the current summary screen and assign various pokemon info into `cur_pokemon-info`
376+ void read_summary_screen (
377+ SingleSwitchProgramEnvironment& env, ProControllerContext& context,
378+ CollectedPokemonInfo& cur_pokemon_info
379+ ){
380+ VideoOverlaySet video_overlay_set (env.console );
383381
384- ImageFloatBox select_check (0.495 , 0.0045 , 0.01 , 0.005 ); // square color to check which mode is active
385382 ImageFloatBox national_dex_number_box (0.448 , 0.245 , 0.049 , 0.04 ); // pokemon national dex number pos
386383 ImageFloatBox shiny_symbol_box (0.702 , 0.09 , 0.04 , 0.06 ); // shiny symbol pos
387384 // TODO: gmax symbol is at the same location as Tera type symbol! Need better detection to tell apart
@@ -396,6 +393,93 @@ void BoxSorter::program(SingleSwitchProgramEnvironment& env, ProControllerContex
396393 ImageFloatBox ability_box (0.158 , 0.838 , 0.213 , 0.042 ); // Ability
397394 ImageFloatBox alpha_box (0.787 , 0.095 , 0.024 , 0.046 ); // Alpha symbol
398395
396+ video_overlay_set.add (COLOR_WHITE, national_dex_number_box);
397+ video_overlay_set.add (COLOR_BLUE, shiny_symbol_box);
398+ video_overlay_set.add (COLOR_RED, gmax_symbol_box);
399+ video_overlay_set.add (COLOR_RED, alpha_box);
400+ video_overlay_set.add (COLOR_DARKGREEN, origin_symbol_box);
401+ video_overlay_set.add (COLOR_DARK_BLUE, pokemon_box);
402+ video_overlay_set.add (COLOR_RED, level_box);
403+ video_overlay_set.add (COLOR_RED, ot_id_box);
404+ video_overlay_set.add (COLOR_RED, ot_box);
405+ video_overlay_set.add (COLOR_RED, nature_box);
406+ video_overlay_set.add (COLOR_RED, ability_box);
407+ BoxGenderDetector::make_overlays (video_overlay_set);
408+
409+
410+ // Wait for the summary screen transition to end
411+ FrozenImageDetector frozen_image_detector (COLOR_GREEN, {0.388 , 0.238 , 0.109 , 0.062 }, Milliseconds (80 ), 20 );
412+ frozen_image_detector.make_overlays (video_overlay_set);
413+ wait_until (env.console , context, 5s, {frozen_image_detector});
414+
415+ VideoSnapshot screen = env.console .video ().snapshot ();
416+
417+ const int dex_number = OCR::read_number_waterfill (env.console , extract_box_reference (screen, national_dex_number_box), 0xff808080 , 0xffffffff );
418+ if (dex_number <= 0 || dex_number > static_cast <int >(NATIONAL_DEX_SLUGS ().size ())) {
419+ OperationFailedException::fire (
420+ ErrorReport::SEND_ERROR_REPORT,
421+ " BoxSorter Check Summary: Unable to read a correct dex number, found: " + std::to_string (dex_number),
422+ env.console
423+ );
424+ }
425+ cur_pokemon_info.dex_number = (uint16_t )dex_number;
426+ cur_pokemon_info.name_slug = NATIONAL_DEX_SLUGS ()[dex_number-1 ];
427+
428+ const int shiny_stddev_value = (int )image_stddev (extract_box_reference (screen, shiny_symbol_box)).sum ();
429+ const bool is_shiny = shiny_stddev_value > 30 ;
430+ cur_pokemon_info.shiny = is_shiny;
431+ env.console .log (" Shiny detection stddev:" + std::to_string (shiny_stddev_value) + " is shiny:" + std::to_string (is_shiny));
432+
433+ const int gmax_stddev_value = (int )image_stddev (extract_box_reference (screen, gmax_symbol_box)).sum ();
434+ const bool is_gmax = gmax_stddev_value > 30 ;
435+ cur_pokemon_info.gmax = is_gmax;
436+ env.console .log (" Gmax detection stddev:" + std::to_string (gmax_stddev_value) + " is gmax:" + std::to_string (is_gmax));
437+
438+ const int alpha_stddev_value = (int )image_stddev (extract_box_reference (screen, alpha_box)).sum ();
439+ const bool is_alpha = alpha_stddev_value > 40 ;
440+ cur_pokemon_info.alpha = is_alpha;
441+ env.console .log (" Alpha detection stddev:" + std::to_string (alpha_stddev_value) + " is alpha:" + std::to_string (is_alpha));
442+
443+ BallReader ball_reader (env.console );
444+ cur_pokemon_info.ball_slug = ball_reader.read_ball (screen);
445+
446+ const StatsHuntGenderFilter gender = BoxGenderDetector::detect (screen);
447+ env.console .log (" Gender: " + gender_to_string (gender), COLOR_GREEN);
448+ cur_pokemon_info.gender = gender;
449+
450+ const int ot_id = OCR::read_number_waterfill (env.console , extract_box_reference (screen, ot_id_box), 0xff808080 , 0xffffffff );
451+ if (ot_id < 0 || ot_id > 999'999 ) {
452+ dump_image (env.console , ProgramInfo (), " ReadSummary_OT" , screen);
453+ }
454+ cur_pokemon_info.ot_id = ot_id;
455+
456+ env.add_overlay_log (create_overlay_info (cur_pokemon_info));
457+ video_overlay_set.clear ();
458+
459+ // NOTE edit when adding new struct members (detections go here likely)
460+
461+ // level_box
462+ // ot_box
463+ // nature_box
464+ // ability_box
465+
466+ // Press button R to go to next summary screen
467+ pbf_press_button (context, BUTTON_R, 80ms, 300ms);
468+ context.wait_for_all_requests ();
469+ }
470+
471+ void BoxSorter::program (SingleSwitchProgramEnvironment& env, ProControllerContext& context){
472+ StartProgramChecks::check_performance_class_wired_or_wireless (context);
473+
474+ const std::vector<SortingRule> sort_preferences = SORT_TABLE.preferences ();
475+ if (sort_preferences.empty ()){
476+ throw UserSetupError (env.console , " At least one sorting method selection needs to be made!" );
477+ }
478+
479+ BoxSorter_Descriptor::Stats& stats = env.current_stats < BoxSorter_Descriptor::Stats>();
480+
481+ ImageFloatBox select_check (0.495 , 0.0045 , 0.01 , 0.005 ); // square color to check which mode is active
482+
399483
400484 // vector that will store data for each slot
401485 std::vector<std::optional<CollectedPokemonInfo>> boxes_data;
@@ -404,16 +488,14 @@ void BoxSorter::program(SingleSwitchProgramEnvironment& env, ProControllerContex
404488
405489 VideoSnapshot screen = env.console .video ().snapshot ();
406490
407- VideoOverlaySet box_render (env.console );
408-
409- std::ostringstream ss;
491+ VideoOverlaySet video_overlay_set (env.console );
410492
411493 FloatPixel image_value = image_stats (extract_box_reference (screen, select_check)).average ;
412494
413495 env.console .log (" Color detected from the select square: " + image_value.to_string ());
414496
415497 // if the correct color is not detected, getting out of every possible menu to make sure the program work no matter where you start it in your pokemon home
416- box_render .add (COLOR_BLUE, select_check);
498+ video_overlay_set .add (COLOR_BLUE, select_check);
417499 if (image_value.r <= image_value.g + image_value.b ){
418500 for (int var = 0 ; var < 5 ; ++var){
419501 pbf_press_button (context, BUTTON_B, 10 , GAME_DELAY+10 );
@@ -441,11 +523,15 @@ void BoxSorter::program(SingleSwitchProgramEnvironment& env, ProControllerContex
441523 }
442524 }
443525
444- box_render .clear ();
526+ video_overlay_set .clear ();
445527
446528 BoxCursor dest_cursor;
447529 BoxCursor nav_cursor = {0 , 0 , 0 };
448530
531+
532+ BoxViewWatcher box_view_watcher (&env.console .overlay ());
533+ SummaryScreenWatcher summary_screen_watcher (&env.console .overlay ());
534+
449535 // cycle through each box
450536 for (size_t box_idx = 0 ; box_idx < BOX_NUMBER; box_idx++){
451537 if (box_idx != 0 ){
@@ -469,7 +555,6 @@ void BoxSorter::program(SingleSwitchProgramEnvironment& env, ProControllerContex
469555
470556 // Check box grid color stddev to find occupied slots and fill boxes_data with placeholder pokemon info.
471557 const std::array<size_t , 2 > first_pokemon_slot = find_occupied_slots_in_box (env, context, boxes_data, sort_preferences);
472- ss.str (" " );
473558
474559 // enter the summary screen to read pokemon info
475560 if (first_pokemon_slot[0 ] != SIZE_MAX){
@@ -481,22 +566,17 @@ void BoxSorter::program(SingleSwitchProgramEnvironment& env, ProControllerContex
481566
482567 pbf_press_button (context, BUTTON_A, 10 , GAME_DELAY);
483568 context.wait_for_all_requests ();
484- box_render .clear ();
569+ video_overlay_set .clear ();
485570 pbf_press_dpad (context, DPAD_DOWN, 10 , GAME_DELAY);
486- pbf_press_button (context, BUTTON_A, 10 , VIDEO_DELAY+ 150 );
571+ pbf_press_button (context, BUTTON_A, 80ms, 100ms );
487572 context.wait_for_all_requests ();
573+ int ret = wait_until (env.console , context, Seconds (5 ), {summary_screen_watcher});
574+ if (ret != 0 ){
575+ OperationFailedException::fire (
576+ ErrorReport::SEND_ERROR_REPORT, " HomeBoxSorter(): does not find summary screen after 5 sec" , env.console
577+ );
578+ }
488579
489- box_render.add (COLOR_WHITE, national_dex_number_box);
490- box_render.add (COLOR_BLUE, shiny_symbol_box);
491- box_render.add (COLOR_RED, gmax_symbol_box);
492- box_render.add (COLOR_RED, alpha_box);
493- box_render.add (COLOR_DARKGREEN, origin_symbol_box);
494- box_render.add (COLOR_DARK_BLUE, pokemon_box);
495- box_render.add (COLOR_RED, level_box);
496- box_render.add (COLOR_RED, ot_id_box);
497- box_render.add (COLOR_RED, ot_box);
498- box_render.add (COLOR_RED, nature_box);
499- box_render.add (COLOR_RED, ability_box);
500580
501581 // cycle through each summary of the current box and fill pokemon information
502582 for (size_t row = 0 ; row < BOX_ROWS; row++){
@@ -506,91 +586,34 @@ void BoxSorter::program(SingleSwitchProgramEnvironment& env, ProControllerContex
506586 continue ;
507587 }
508588
509- // Wait for the summary screen transition to end
510- FrozenImageDetector frozen_image_detector (COLOR_GREEN, {0.388 , 0.238 , 0.109 , 0.062 }, Milliseconds (80 ), 20 );
511- frozen_image_detector.make_overlays (box_render);
512- wait_until (env.console , context, 5s, {frozen_image_detector});
513-
514- auto & cur_pokemon_info = boxes_data[global_idx];
515- screen = env.console .video ().snapshot ();
516-
517- const int dex_number = OCR::read_number_waterfill (env.console , extract_box_reference (screen, national_dex_number_box), 0xff808080 , 0xffffffff );
518- if (dex_number <= 0 || dex_number > static_cast <int >(NATIONAL_DEX_SLUGS ().size ())) {
519- OperationFailedException::fire (
520- ErrorReport::SEND_ERROR_REPORT,
521- " BoxSorter Check Summary: Unable to read a correct dex number, found: " + std::to_string (dex_number),
522- env.console
523- );
524- }
525- cur_pokemon_info->dex_number = (uint16_t )dex_number;
526- cur_pokemon_info->name_slug = NATIONAL_DEX_SLUGS ()[dex_number-1 ];
527-
528- const int shiny_stddev_value = (int )image_stddev (extract_box_reference (screen, shiny_symbol_box)).sum ();
529- const bool is_shiny = shiny_stddev_value > 30 ;
530- cur_pokemon_info->shiny = is_shiny;
531- env.console .log (" Shiny detection stddev:" + std::to_string (shiny_stddev_value) + " is shiny:" + std::to_string (is_shiny));
532-
533- const int gmax_stddev_value = (int )image_stddev (extract_box_reference (screen, gmax_symbol_box)).sum ();
534- const bool is_gmax = gmax_stddev_value > 30 ;
535- cur_pokemon_info->gmax = is_gmax;
536- env.console .log (" Gmax detection stddev:" + std::to_string (gmax_stddev_value) + " is gmax:" + std::to_string (is_gmax));
537-
538- const int alpha_stddev_value = (int )image_stddev (extract_box_reference (screen, alpha_box)).sum ();
539- const bool is_alpha = alpha_stddev_value > 40 ;
540- cur_pokemon_info->alpha = is_alpha;
541- env.console .log (" Alpha detection stddev:" + std::to_string (alpha_stddev_value) + " is alpha:" + std::to_string (is_alpha));
542-
543- BallReader ball_reader (env.console );
544- cur_pokemon_info->ball_slug = ball_reader.read_ball (screen);
545-
546- BoxGenderDetector::make_overlays (box_render);
547- const StatsHuntGenderFilter gender = BoxGenderDetector::detect (screen);
548- env.console .log (" Gender: " + gender_to_string (gender), COLOR_GREEN);
549- cur_pokemon_info->gender = gender;
550-
551- const int ot_id = OCR::read_number_waterfill (env.console , extract_box_reference (screen, ot_id_box), 0xff808080 , 0xffffffff );
552- if (ot_id < 0 || ot_id > 999'999 ) {
553- dump_image (env.console , ProgramInfo (), " ReadSummary_OT" , screen);
554- }
555- cur_pokemon_info->ot_id = ot_id;
556-
557- env.add_overlay_log (create_overlay_info (*cur_pokemon_info));
558-
559- // NOTE edit when adding new struct members (detections go here likely)
560-
561- // level_box
562- // ot_box
563- // nature_box
564- // ability_box
565-
566- // Press button R to go to next summary screen
567- pbf_press_button (context, BUTTON_R, 10 , 40 );
568- context.wait_for_all_requests ();
589+ // Read the summary screen and assign data to boxes_data[global_idx]
590+ read_summary_screen (env, context, boxes_data[global_idx].value ());
569591 }
570592 }
571593
572- box_render. clear ();
573-
594+ // log detailed pokemon infomation of this box
595+ std::ostringstream ss;
574596 ss << std::endl;
575-
576- // print box information
577597 for (size_t row = 0 ; row < BOX_ROWS; row++){
578598 for (size_t column = 0 ; column < BOX_COLS; column++){
579- ss << boxes_data[to_global_index (box_idx, row, column)] << " " ;
599+ ss << " [ " << row << " , " << column << " ]: " << boxes_data[to_global_index (box_idx, row, column)] << std::endl ;
580600 }
581- ss << std::endl;
582601 }
583-
584- env.console .log (ss.str ());
585- ss.str (" " );
602+ env.log (ss.str ());
586603
587604 // get out of summary with a lot of delay because it's slow for some reasons
588- pbf_press_button (context, BUTTON_B, 10 , VIDEO_DELAY+250 );
589- box_render.clear ();
605+ pbf_press_button (context, BUTTON_B, 80ms, 100ms);
590606 context.wait_for_all_requests ();
607+ ret = wait_until (env.console , context, Seconds (5 ), {box_view_watcher});
608+ if (ret != 0 ){
609+ OperationFailedException::fire (
610+ ErrorReport::SEND_ERROR_REPORT, " HomeBoxSorter(): does not find box view after 5 sec" , env.console
611+ );
612+ }
613+ video_overlay_set.clear ();
591614 }
592615
593- box_render .clear ();
616+ video_overlay_set .clear ();
594617
595618 dest_cursor = {0 , 0 , 0 };
596619 nav_cursor = move_cursor_to (env, context, nav_cursor, dest_cursor, GAME_DELAY);
0 commit comments