|
20 | 20 | #include "Pokemon/Resources/Pokemon_PokemonNames.h" |
21 | 21 | #include "Pokemon/Inference/Pokemon_NameReader.h" |
22 | 22 | #include "PokemonLA/PokemonLA_TravelLocations.h" |
23 | | -#include "PokemonLA/Inference/PokemonLA_DialogDetector.h" |
24 | 23 | #include "PokemonLA/Inference/Map/PokemonLA_MapDetector.h" |
25 | 24 | #include "PokemonLA/Inference/Map/PokemonLA_SelectedRegionDetector.h" |
26 | 25 | #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" |
31 | 27 | #include "PokemonLA/Programs/PokemonLA_GameEntry.h" |
32 | 28 | #include "PokemonLA/Programs/PokemonLA_GameSave.h" |
| 29 | +#include "PokemonLA/Programs/PokemonLA_RegionNavigation.h" |
33 | 30 | #include "PokemonLA/Resources/PokemonLA_AvailablePokemon.h" |
34 | 31 | #include "PokemonLA/Resources/PokemonLA_PokemonSprites.h" |
35 | 32 | #include "PokemonLA/Inference/Objects/PokemonLA_ButtonDetector.h" |
36 | 33 | #include "PokemonLA/Inference/Objects/PokemonLA_MMOQuestionMarkDetector.h" |
| 34 | +#include "PokemonLA_MMORoutines.h" |
37 | 35 | #include "PokemonLA_OutbreakFinder.h" |
38 | | -#include "PokemonLA/Programs/PokemonLA_RegionNavigation.h" |
39 | | -#include "PokemonLA/PokemonLA_Settings.h" |
| 36 | + |
40 | 37 |
|
41 | 38 | #include <sstream> |
42 | 39 |
|
@@ -102,36 +99,6 @@ const StringSelectDatabase& MMO_DATABASE(){ |
102 | 99 | } |
103 | 100 |
|
104 | 101 |
|
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 | | - |
135 | 102 | OutbreakFinder_Descriptor::OutbreakFinder_Descriptor() |
136 | 103 | : SingleSwitchProgramDescriptor( |
137 | 104 | "PokemonLA:OutbreakFinder", |
@@ -435,244 +402,6 @@ void OutbreakFinder::goto_region_and_return(SingleSwitchProgramEnvironment& env, |
435 | 402 | mash_A_to_change_region(env, env.console, context); |
436 | 403 | } |
437 | 404 |
|
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 | | - |
676 | 405 |
|
677 | 406 | std::vector<std::string> OutbreakFinder::run_iteration( |
678 | 407 | SingleSwitchProgramEnvironment& env, ProControllerContext& context, |
@@ -735,7 +464,16 @@ std::vector<std::string> OutbreakFinder::run_iteration( |
735 | 464 | save_game_from_overworld(env, env.console, context); |
736 | 465 |
|
737 | 466 | 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 | + |
739 | 477 | if (found_pokemon.size() > 0){ |
740 | 478 | stats.matches += found_pokemon.size(); |
741 | 479 | std::ostringstream os; |
|
0 commit comments