Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/* Battle Dialog Detector
*
* From: https://github.com/PokemonAutomation/
*
*/

#include "CommonTools/Images/SolidColorTest.h"
#include "CommonTools/Images/ImageFilter.h"
#include "CommonFramework/ImageTools/ImageBoxes.h"
#include "CommonFramework/ImageTypes/ImageRGB32.h"
#include "CommonFramework/ImageTools/ImageStats.h"
#include "CommonFramework/ImageTypes/ImageViewRGB32.h"
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
#include "PokemonRSE/PokemonRSE_Settings.h"
#include "PokemonRSE_BattleDialogs.h"

//#include <iostream>
//using std::cout;
//using std::endl;

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonRSE{

/*
BattleDialogDetector::BattleDialogDetector(Color color)
: m_dialog_top_box(0.049224, 0.738530, 0.901359, 0.008023)
, m_dialog_right_box(0.943471, 0.746553, 0.007111, 0.212602)
, m_dialog_top_jpn_box(0.068780, 0.738530, 0.856913, 0.012034)
, m_dialog_right_jpn_box(0.918582, 0.747890, 0.007111, 0.208590)
{}
void BattleDialogDetector::make_overlays(VideoOverlaySet& items) const{
const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX;
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_jpn_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_jpn_box));
}
bool BattleDialogDetector::detect(const ImageViewRGB32& screen){
ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX);

ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box);
ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box);
ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_jpn_box);
ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_jpn_box);

if ((is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20))
||
(is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20))
){
return true;
}
return false;
}
*/

BattleMenuDetector::BattleMenuDetector(Color color)
: m_menu_top_box(0.599462, 0.738530, 0.366233, 0.004011) //top of the white box
, m_menu_bottom_box(0.602128, 0.947120, 0.368010, 0.006686)
, m_dialog_top_box(0.068780, 0.739867, 0.488903, 0.008023) //teal boxes
, m_dialog_right_box(0.553238, 0.739867, 0.006222, 0.215276)

, m_menu_top_eme_box(0.534571, 0.746553, 0.434679, 0.008023)
, m_menu_right_eme_box(0.962138, 0.747890, 0.007111, 0.205916)
, m_dialog_top_eme_box(0.046557, 0.741204, 0.442679, 0.008023)
, m_dialog_right_eme_box(0.487458, 0.742541, 0.004445, 0.216613)
{}
void BattleMenuDetector::make_overlays(VideoOverlaySet& items) const{
const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX;
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_top_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_bottom_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box));

items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_top_eme_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_right_eme_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_eme_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_eme_box));
}
bool BattleMenuDetector::detect(const ImageViewRGB32& screen){
ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX);

//Menu is white
ImageViewRGB32 menu_top_image = extract_box_reference(game_screen, m_menu_top_box);
ImageViewRGB32 menu_bottom_image = extract_box_reference(game_screen, m_menu_bottom_box);
ImageViewRGB32 menu_top_jpn_image = extract_box_reference(game_screen, m_menu_top_eme_box);
ImageViewRGB32 menu_right_jpn_image = extract_box_reference(game_screen, m_menu_right_eme_box);

//Background dialog is teal
ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box);
ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box);
ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_eme_box);
ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_eme_box);

if ((is_white(menu_top_image) //Ruby/Sapphire, all languages. Emerald Japan.
&& is_white(menu_bottom_image)
&& is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20))
||
(is_white(menu_top_jpn_image) //Emerald, all languages except Japan.
&& is_white(menu_right_jpn_image)
&& is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20))
){
return true;
}
return false;
}


AdvanceBattleDialogDetector::AdvanceBattleDialogDetector(Color color)
: m_dialog_box(0.043890, 0.737193, 0.913804, 0.227310)
, m_dialog_top_box(0.049224, 0.738530, 0.901359, 0.008023)
, m_dialog_right_box(0.943471, 0.746553, 0.007111, 0.212602)
, m_dialog_jpn_box(0.059891, 0.742541, 0.872914, 0.216613)
, m_dialog_top_jpn_box(0.068780, 0.738530, 0.856913, 0.012034)
, m_dialog_right_jpn_box(0.918582, 0.747890, 0.007111, 0.208590)
{}
void AdvanceBattleDialogDetector::make_overlays(VideoOverlaySet& items) const{
const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX;
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_jpn_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_jpn_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_jpn_box));
}
bool AdvanceBattleDialogDetector::detect(const ImageViewRGB32& screen){
ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX);

const bool replace_color_within_range = false;

//Filter out background
ImageRGB32 filtered_region = filter_rgb32_range(
extract_box_reference(game_screen, m_dialog_box),
combine_rgb(164, 0, 0), combine_rgb(255, 114, 87), Color(0), replace_color_within_range
);
ImageStats stats = image_stats(filtered_region);

/*
filtered_region.save("./filtered_only.png");
cout << stats.average.r << endl;
cout << stats.average.g << endl;
cout << stats.average.b << endl;
*/

//japanese
ImageRGB32 filtered_region_jpn = filter_rgb32_range(
extract_box_reference(game_screen, m_dialog_jpn_box),
combine_rgb(164, 0, 0), combine_rgb(255, 114, 87), Color(0), replace_color_within_range
);
ImageStats stats2 = image_stats(filtered_region_jpn);

ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box);
ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box);
ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_jpn_box);
ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_jpn_box);

if ((is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& (stats.average.r > stats.average.b + 180)
&& (stats.average.r > stats.average.g + 180))
||
(is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)
&& (stats2.average.r > stats2.average.b + 180)
&& (stats2.average.r > stats2.average.g + 180))
){
return true;
}
return false;
}




}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* Battle Dialog Detector
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_PokemonRSE_BattleDialogs_H

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How similar are the RSE and FRLG battle menus? Since the gen3 games were built off the same-ish engine, maybe they are similar enough to share battle menu detectors? (among other things?)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly most of the UI elements are too different between games and languages to share detectors.

This one is closest, FRLG+Emerald, both non-Japan:
image
But if I attempt the same with Ruby, its way off:
image
And that's before adding Japan into the mix, both below are Ruby but the padding and positioning are different, and one (jpn) uses an arrow while the other (all other languages) uses a box as a selector.
image
Similar situation for Emerald:
image

I might actually have to adjust some of the menu positioning further for Ruby/Sapphire since like in FRLG some of the positioning is really tight. But that would have to wait for an official release so I know how much wiggle room there is.

Generally the UI Ruby/Sapphire is completely different while Emerald is much closer to FRLG. Which would line up with their release order.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oof

#define PokemonAutomation_PokemonRSE_BattleDialogs_H

#include <chrono>
#include <atomic>
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
#include "Common/Cpp/Color.h"
#include "CommonFramework/ImageTools/ImageBoxes.h"
#include "CommonTools/VisualDetector.h"
#include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h"

namespace PokemonAutomation{
class CancellableScope;
class VideoFeed;
namespace NintendoSwitch{
namespace PokemonRSE{

// Battle dialog boxes are teal
// This is unused right now so isn't tested well
/*
class BattleDialogDetector : public StaticScreenDetector{
public:
BattleDialogDetector(Color color);

virtual void make_overlays(VideoOverlaySet& items) const override;
virtual bool detect(const ImageViewRGB32& screen) override;

private:
ImageFloatBox m_dialog_top_box;
ImageFloatBox m_dialog_right_box;

ImageFloatBox m_dialog_top_jpn_box;
ImageFloatBox m_dialog_right_jpn_box;
};
class BattleDialogWatcher : public DetectorToFinder<BattleDialogDetector>{
public:
BattleDialogWatcher(Color color)
: DetectorToFinder("BattleDialogWatcher", std::chrono::milliseconds(250), color)
{}
};
*/

// Battle menu is a white box on the right
// Teal dialog box remains on the left
// For R/S, the selection arrow is only in JPN ver, other languages use a box
// Positions slightly different on non-JPN Emerald but all langs use an arrow
class BattleMenuDetector : public StaticScreenDetector{
public:
BattleMenuDetector(Color color);

virtual void make_overlays(VideoOverlaySet& items) const override;
virtual bool detect(const ImageViewRGB32& screen) override;

private:
//R/S both JPN and ENG, E for JPN
ImageFloatBox m_menu_top_box;
ImageFloatBox m_menu_bottom_box;
ImageFloatBox m_dialog_top_box;
ImageFloatBox m_dialog_right_box;

//Emerald for non-JPN
ImageFloatBox m_menu_top_eme_box;
ImageFloatBox m_menu_right_eme_box;
ImageFloatBox m_dialog_top_eme_box;
ImageFloatBox m_dialog_right_eme_box;
};
class BattleMenuWatcher : public DetectorToFinder<BattleMenuDetector>{
public:
BattleMenuWatcher(Color color)
: DetectorToFinder("BattleMenuWatcher", std::chrono::milliseconds(250), color)
{}
};



// Detect the red advancement arrow by filtering for red.
// This is the same as BattleDialogDetector apart from the arrow
class AdvanceBattleDialogDetector : public StaticScreenDetector{
public:
AdvanceBattleDialogDetector(Color color);

virtual void make_overlays(VideoOverlaySet& items) const override;
virtual bool detect(const ImageViewRGB32& screen) override;

private:
ImageFloatBox m_dialog_box;
ImageFloatBox m_dialog_top_box;
ImageFloatBox m_dialog_right_box;

ImageFloatBox m_dialog_jpn_box;
ImageFloatBox m_dialog_top_jpn_box;
ImageFloatBox m_dialog_right_jpn_box;
};
class AdvanceBattleDialogWatcher : public DetectorToFinder<AdvanceBattleDialogDetector>{
public:
AdvanceBattleDialogWatcher(Color color)
: DetectorToFinder("AdvanceBattleDialogWatcher", std::chrono::milliseconds(250), color)
{}
};





}
}
}

#endif
Loading