Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
174f751
FRLG AutoStory sim tests: a row's steps on the sim in a file of their…
dayfine Nov 3, 2026
4c1eebf
FRLG AutoStory 00.21 and 00.22: Route 6 to Vermilion, the S.S. Anne
dayfine Nov 3, 2026
4761f75
FRLG: image test for WhiteDialogDetector on the S.S. Anne's white hull
dayfine Nov 3, 2026
7fd42c2
FRLG: WhiteDialogDetector checks the dialog's blue frame
dayfine Nov 3, 2026
ea22fd8
FRLG: the win-chance sim plays the battle loop's Antidote, logs its l…
dayfine Nov 3, 2026
2fe142e
FRLG battle loop: real YES/NOs only, Disable over "no PP", the sim's …
dayfine Nov 4, 2026
6f17c46
FRLG AutoStory 00.23-00.25: Cut, Vermilion Gym's trash cans, Lt. Surge
dayfine Nov 4, 2026
370a0f5
Merge branch 'frlg/stage6-vermilion' of https://github.com/dayfine/Ar…
dayfine Nov 4, 2026
ee9fcf1
FRLG AutoStory 00.23-00.24: the sim's Surge numbers after the merge
dayfine Nov 4, 2026
43ee40b
FRLG summary: read the Oddish's moves; an unnamed move keeps its row
dayfine Nov 4, 2026
7268069
FRLG teach: red tests for the TM's "Machine set!" scene
dayfine Nov 4, 2026
010db08
FRLG teach: A through the TM's scene, never B
dayfine Nov 4, 2026
b992332
FRLG teach: red tests for "Poof!" read as "learned" and a stuck scene
dayfine Nov 4, 2026
212854c
FRLG teach: A on every look of the TM's scene; leave() waits for it once
dayfine Nov 4, 2026
00a2077
FRLG walk: hold a warp a tile further after an open leg over a connec…
dayfine Nov 4, 2026
027ae80
FRLG 00.24: check Lt. Surge's win chance in the Center before the grind
dayfine Nov 4, 2026
d8b375f
FRLG sim: Double Team's AI score and two-turn moves (Lt. Surge, m73)
dayfine Nov 4, 2026
b48afbc
FRLG 00.25: to the nurse off Oak's aide's row; stop a battle with no …
dayfine Nov 4, 2026
130344d
Merge commit '7b6a28cebae30896aa10d00378d84ebba6322d6b' into HEAD
dayfine Nov 5, 2026
36455d1
Merge origin/frlg/stage5-bill into frlg/stage6-vermilion (align with …
dayfine Nov 6, 2026
f527922
Merge origin/frlg/stage5-bill into frlg/stage6-vermilion
dayfine Nov 6, 2026
d034ec7
Use string views for Vermilion Gym test checkpoint lookups
dayfine Nov 7, 2026
89f9e03
Merge origin/frlg/stage5-bill into frlg/stage6-vermilion
dayfine Nov 7, 2026
7ca8178
Merge remote-tracking branch 'origin/frlg/stage5-bill' into frlg/stag…
dayfine Nov 8, 2026
823228a
Merge frlg/stage5-bill into frlg/stage6-vermilion
dayfine Nov 8, 2026
dfb943a
Merge remote-tracking branch 'origin/frlg/stage5-bill' into HEAD
dayfine Nov 9, 2026
26649fe
Merge frlg/stage5-bill into frlg/stage6-vermilion
dayfine Nov 9, 2026
e250707
Merge frlg/stage5-bill (story action table) into frlg/stage6-vermilion
dayfine Nov 9, 2026
703f32f
FRLG AutoStory: GAME steps as rows of the story action table
dayfine Nov 9, 2026
c82b79b
Merge frlg/stage5-bill into frlg/stage6-vermilion
dayfine Nov 9, 2026
8698c80
Merge frlg/stage5-bill: check the save stands at the checkpoint's sta…
dayfine Nov 9, 2026
37008e1
Merge frlg/stage5-bill: keep the simulation tests' line endings
dayfine Nov 9, 2026
db0cc2a
Merge frlg/stage5-bill: time an interrupt's step from its screen events
dayfine Nov 9, 2026
14d8a5c
FRLG AutoStory sim: run the story through the action table
dayfine Nov 9, 2026
9839daf
Merge frlg/stage5-bill: check a loaded save's start against a kept frame
dayfine Nov 9, 2026
b0f7e3f
Merge 2ab5af7b0 (#122 via parent) into frlg/stage6-vermilion
dayfine Nov 9, 2026
52103a2
Merge FRLG between-step interrupts (b93d1d36e) into #222
dayfine Nov 9, 2026
effe4c0
Merge FRLG between-step interrupts (82e3821bf) into #222
dayfine Nov 9, 2026
1417971
FRLG AutoStory sim: clear dialogs between steps as the console does
dayfine Nov 9, 2026
fe3210e
Merge FRLG interrupt actions move (12c744e6a) into #222
dayfine Nov 9, 2026
ff71c61
Merge frlg/stage5-bill into frlg/stage6-vermilion (hold watchers for …
dayfine Nov 9, 2026
592d740
Merge frlg/stage5-bill into frlg/stage6-vermilion
dayfine Nov 9, 2026
b6421e4
Merge frlg/stage5-bill into frlg/stage6-vermilion (wait for a battle'…
dayfine Nov 9, 2026
edc2002
Merge frlg/stage5-bill into frlg/stage6-vermilion (scene ends after i…
dayfine Nov 9, 2026
8885f95
Merge frlg/stage5-bill into frlg/stage6-vermilion: a step on the Swit…
dayfine Nov 9, 2026
ab46733
Merge frlg/stage5-bill into frlg/stage6-vermilion
dayfine Nov 10, 2026
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
Expand Up @@ -28,22 +28,30 @@ WhiteDialogDetector::WhiteDialogDetector(Color color)
: m_right_box(0.923385, 0.748077, 0.00615385, 0.204577)
, m_top_box(0.0704615, 0.741846, 0.859077, 0.00623077)
, m_bottom_box(0.0716923, 0.943308, 0.851692, 0.00934615)
, m_frame_box(0.0704615, 0.7288, 0.859077, 0.0048)
{}
void WhiteDialogDetector::make_overlays(VideoOverlaySet& items) const{
const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX;
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_right_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_top_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_bottom_box));
items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_frame_box));
}
bool WhiteDialogDetector::detect(const ImageViewRGB32& screen){
ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX);

ImageViewRGB32 right_image = extract_box_reference(game_screen, m_right_box);
ImageViewRGB32 top_image = extract_box_reference(game_screen, m_top_box);
ImageViewRGB32 bottom_image = extract_box_reference(game_screen, m_bottom_box);
ImageViewRGB32 frame_image = extract_box_reference(game_screen, m_frame_box);
// The dialog's top frame is light blue, about (165, 214, 230). A white area with no
// frame, like the S.S. Anne's hull, is pure white there. The default is_white() would
// accept the light blue too, so this asks for a brighter white than any frame color.
const bool frame_is_white = is_white(frame_image, 720);
if (is_white(right_image)
&& is_white(top_image)
&& is_white(bottom_image)
&& !frame_is_white
){
return true;
}
Expand Down Expand Up @@ -127,6 +135,44 @@ bool SelectionDialogDetector::detect(const ImageViewRGB32& screen){
return false;
}

class Test_WhiteDialogDetector : public ImageUnitTest{
public:

Test_WhiteDialogDetector(
const std::string& image,
bool expected
)
: ImageUnitTest("PokemonFRLG::WhiteDialogDetector - " + image, {image})
, m_expected(expected)
{}

virtual UnitTestResult run_on_images(
Logger& logger, CancellableScope& scope,
const std::vector<ImageRGB32>& images
) const override{
WhiteDialogDetector detector(COLOR_RED);
const ImageRGB32& image = images[0];
return detector.detect(image) == m_expected;
};

private:
bool m_expected;
};

void add_tests_WhiteDialogDetector(UnitTestDatabase& database){
// The S.S. Anne's white hull fills the dialog area, with no frame around it.
database.add<Test_WhiteDialogDetector>("PokemonFRLG/WhiteDialogDetector/SSAnne1F_Corridor_False.png", false);
database.add<Test_WhiteDialogDetector>("PokemonFRLG/WhiteDialogDetector/SSAnne1F_CorridorWorker_False.png", false);
// Real dialogs, from the other dialog detectors' folders.
database.add<Test_WhiteDialogDetector>("PokemonFRLG/AdvanceWhiteDialogDetector/English-Bulba_True.png", true);
database.add<Test_WhiteDialogDetector>("PokemonFRLG/AdvanceWhiteDialogDetector/Ger-KennyExile-Sq_True.png", true);
database.add<Test_WhiteDialogDetector>("PokemonFRLG/AdvanceWhiteDialogDetector/MacOS-not-ili-Char_True.png", true);
database.add<Test_WhiteDialogDetector>("PokemonFRLG/AdvanceWhiteDialogDetector/Spanish-AlejaKaiser-Char_True.png", true);
database.add<Test_WhiteDialogDetector>("PokemonFRLG/Dialog/white_dialog_jap-112434221413.png", true);
database.add<Test_WhiteDialogDetector>("PokemonFRLG/Dialog/white_dialog_red_text_spa-205200755327.png", true);
database.add<Test_WhiteDialogDetector>("PokemonFRLG/SelectionDialogDetector/English-Bulba-720p_True.png", true);
}

class Test_AdvanceWhiteDialogDetector : public ImageUnitTest{
public:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class WhiteDialogDetector : public StaticScreenDetector{
ImageFloatBox m_right_box;
ImageFloatBox m_top_box;
ImageFloatBox m_bottom_box;
ImageFloatBox m_frame_box;
};
class WhiteDialogWatcher : public DetectorToFinder<WhiteDialogDetector>{
public:
Expand Down Expand Up @@ -134,11 +135,15 @@ class SelectionDialogWatcher : public DetectorToFinder<SelectionDialogDetector>{
{}
};

// Register white dialog detector unit tests.
void add_tests_WhiteDialogDetector(UnitTestDatabase& database);
// Register advance white dialog detector unit tests.
void add_tests_AdvanceWhiteDialogDetector(UnitTestDatabase& database);
// Register selection dialog detector unit tests.
void add_tests_SelectionDialogDetector(UnitTestDatabase& database);

}
}
}
} // namespace PokemonFRLG
} // namespace NintendoSwitch
} // namespace PokemonAutomation

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class PlaceNameMatcher : public OCR::SmallDictionaryMatcher{
{"cerulean-city", "Cerulean City"},
{"route-24", "Route 24"},
{"route-25", "Route 25"},
{"vermilion-city", "Vermilion City"},
}){
JsonArray names;
names.push_back(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ ImageFloatBox save_card_title_box();

// The place on the save card: "pallet-town", "viridian-city",
// "viridian-forest", "pewter-city", "route-1" to "route-4", "cerulean-city",
// "route-24", "route-25"; empty if it can't be read, as in a language the
// reader has no names for.
// "route-24", "route-25", "vermilion-city"; empty if it can't be read, as
// in a language the reader has no names for.
std::string read_save_card_location(Logger& logger, Language language, const ImageViewRGB32& screen);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,23 @@ namespace{
constexpr double ROW_STEP = 28;
constexpr double NAME_TOP = 22;
constexpr double PP_TOP = 33;
// A move's type badge, x 123 to 154 and y 22 to 33 on the top row: an
// empty slot shows the pane's light background there instead.
constexpr double BADGE_LEFT = 125;
constexpr double BADGE_TOP = 24;
constexpr double BADGE_WIDTH = 27;
constexpr double BADGE_HEIGHT = 8;
// The cursor box's left edge, x 120 to 121, from y 20 to 44 on the top
// row: (255, 72, 0).
constexpr double CURSOR_EDGE_LEFT = 120;
constexpr double CURSOR_EDGE_TOP = 24;
constexpr double CURSOR_EDGE_HEIGHT = 16;

// The level-up moves of Squirtle's and Magikarp's lines (pret
// The level-up moves of Squirtle's, Magikarp's and Oddish's lines (pret
// pokefirered src/data/pokemon/level_up_learnsets.h), then the TMs'
// and HMs' moves not among them (src/data/items.h; Hyper Beam, Protect
// and Rain Dance are above), as the game writes them.
// and Rain Dance are above), as the game writes them
// (src/data/text/move_names.h).
constexpr std::pair<const char*, const char*> MOVE_NAMES[] = {
{"tackle", "Tackle"},
{"tail-whip", "Tail Whip"},
Expand All @@ -64,6 +71,17 @@ constexpr std::pair<const char*, const char*> MOVE_NAMES[] = {
{"leer", "Leer"},
{"twister", "Twister"},
{"dragon-dance", "Dragon Dance"},
{"absorb", "Absorb"},
{"sweet-scent", "Sweet Scent"},
{"poison-powder", "PoisonPowder"},
{"stun-spore", "Stun Spore"},
{"sleep-powder", "Sleep Powder"},
{"acid", "Acid"},
{"moonlight", "Moonlight"},
{"petal-dance", "Petal Dance"},
{"aromatherapy", "Aromatherapy"},
{"mega-drain", "Mega Drain"},
{"magical-leaf", "Magical Leaf"},
{"hyper-beam", "Hyper Beam"},
{"focus-punch", "Focus Punch"},
{"dragon-claw", "Dragon Claw"},
Expand Down Expand Up @@ -172,6 +190,24 @@ bool cursor_red(uint32_t pixel){
return r > 200 && g < 140 && b < 90;
}

// Whether row `row` holds a move: its type badge shows, mostly darker
// than the pane's light background.
bool row_has_move(const ImageViewRGB32& screen, size_t row){
const ImageFloatBox box(
BADGE_LEFT / 240, (BADGE_TOP + ROW_STEP * double(row)) / 160, BADGE_WIDTH / 240, BADGE_HEIGHT / 160
);
const ImageViewRGB32 badge = extract_box_reference(game_box(screen), box);
size_t dark = 0;
for (size_t y = 0; y < badge.height(); y++){
for (size_t x = 0; x < badge.width(); x++){
const uint32_t pixel = badge.pixel(x, y);
dark += ((pixel >> 16) & 0xff) + ((pixel >> 8) & 0xff) + (pixel & 0xff) < 600 ? 1 : 0;
}
}
// Measured: a third to three quarters of a badge; none of an empty slot.
return dark * 5 > badge.width() * badge.height();
}

int read_pp(Logger& logger, const ImageViewRGB32& screen, const ImageFloatBox& box){
const int pp = read_digits_waterfill_template(logger, extract_box_reference(game_box(screen), box), DigitTemplateType::StatBox);
// A move has at most 64 PP (40 with three PP Ups).
Expand Down Expand Up @@ -202,12 +238,12 @@ std::string read_move_name(Logger& logger, Language language, const ImageViewRGB
std::vector<SummaryMove> read_summary_moves(Logger& logger, Language language, const ImageViewRGB32& screen){
std::vector<SummaryMove> moves;
for (size_t row = 0; row < SUMMARY_MOVE_ROWS - 1; row++){
std::string move = read_summary_move_name(logger, language, screen, row);
if (move.empty()){
// The game packs the moves to the top: the first empty slot ends them.
if (!row_has_move(screen, row)){
break;
}
moves.push_back(SummaryMove{
.move = std::move(move),
.move = read_summary_move_name(logger, language, screen, row),
.pp = read_pp(logger, screen, summary_move_pp_box(row)),
.max_pp = read_pp(logger, screen, summary_move_max_pp_box(row)),
});
Expand Down Expand Up @@ -247,7 +283,8 @@ namespace{
// A capture of a summary page, named <anything>_<language code>.png. Its
// golden file holds the page shown; on the first page the species and the
// level; on KNOWN MOVES, or a move being picked, each move's name, PP and
// full PP, then the cursor's row (from 1) or "none".
// full PP ("unknown" for a move with no name read), then the cursor's
// row (from 1) or "none".
class Test_SummaryScreen : public ImageUnitTest{
public:
Test_SummaryScreen(const std::string& image)
Expand Down Expand Up @@ -285,7 +322,7 @@ class Test_SummaryScreen : public ImageUnitTest{
const std::vector<SummaryMove> known = read_summary_moves(logger, language, screen);
for (size_t c = 0; c < known.size(); c++){
labels.push_back(std::format("move {}", c + 1));
values.push_back(known[c].move);
values.push_back(known[c].move.empty() ? "unknown" : known[c].move);
labels.push_back(std::format("move {} PP", c + 1));
values.push_back(std::to_string(known[c].pp));
labels.push_back(std::format("move {} full PP", c + 1));
Expand All @@ -311,6 +348,9 @@ void add_tests_SummaryMovesReader(UnitTestDatabase& database){
database.add<Test_SummaryScreen>("PokemonFRLG/SummaryMovesReader/MoveDetail_eng.png");
// The party menu: no summary page.
database.add<Test_SummaryScreen>("PokemonFRLG/SummaryMovesReader/PartyMenu_eng.png");
// FireRed on console B, 2026-11-03: the Oddish caught for Cut, Lv12, with
// ABSORB, SWEET SCENT and two empty slots.
database.add<Test_SummaryScreen>("PokemonFRLG/SummaryMovesReader/Oddish_KnownMoves_eng.png");
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,16 @@ std::string read_move_name(Logger& logger, Language language, const ImageViewRGB

// A single move entry read from the summary moves screen.
struct SummaryMove{
// Empty for a move the reader has no name for.
std::string move;
// -1 if not read. The game draws a move's PP in color once it is down
// to half, which the digit templates may not read.
int pp = -1;
int max_pp = -1;
};

// The moves from the top, up to the first row whose name can't be read.
// The moves from the top, up to the first empty slot. A move whose name
// isn't read keeps its row, with its name empty.
std::vector<SummaryMove> read_summary_moves(Logger& logger, Language language, const ImageViewRGB32& screen);

// The row (0 to 4) the red cursor box is on, if one shows.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Teach Screen Reader

Check warning on line 1 in SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/Menus/PokemonFRLG_TeachScreenReader.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

<file>: file_lines=542 (advisory above 500, limit 1000)
*
* From: https://github.com/PokemonAutomation/
*
Expand Down Expand Up @@ -57,6 +57,12 @@
return euclidean_distance(stats.average, color) < 40 && stats.stddev.sum() < 40;
}

// The box is a fine pattern of light colors averaging near `color`.
bool patterned(const ImageViewRGB32& screen, const GameBox& box, const FloatPixel& color){
const ImageStats stats = image_stats(game_crop(screen, box));
return euclidean_distance(stats.average, color) < 30 && stats.stddev.sum() < 70;
}

// Measured on the captures (console B, 2026-11-03). Left of the title, the
// frame's color tells the screen: the bag's orange, the TM CASE's mint,
// the party menu's dark teal.
Expand All @@ -65,6 +71,11 @@
const FloatPixel TM_CASE_MINT(193, 234, 207);
const FloatPixel PARTY_TEAL(18, 109, 107);
const FloatPixel WHITE(255, 255, 255);
// The TM's scene: pale blue diamonds on both sides of the member, around
// (198, 234, 255) with a stddev near 30 for the pattern.
constexpr GameBox SCENE_LEFT{0, 20, 16, 90};
constexpr GameBox SCENE_RIGHT{224, 20, 16, 90};
const FloatPixel SCENE_BLUE(198, 234, 255);

// The item menu's box, between its rows (OPEN at y 109, REGISTER 125,
// CANCEL 141); blue text box or list below it when it is closed.
Expand Down Expand Up @@ -202,6 +213,8 @@
{"stop-question", "Stop trying to teach"},
{"did-not-learn", "did not learn the"},
{"not-compatible", "are not compatible"},
{"machine-set", "Machine set"},
{"learned", "learned"},
};
return matcher;
}
Expand All @@ -216,6 +229,8 @@
{"stop-question", TeachText::STOP_QUESTION},
{"did-not-learn", TeachText::DID_NOT_LEARN},
{"not-compatible", TeachText::NOT_COMPATIBLE},
{"machine-set", TeachText::MACHINE_SET},
{"learned", TeachText::LEARNED},
};
for (const auto& [name, text] : texts){
if (name == slug){
Expand Down Expand Up @@ -271,6 +286,7 @@
case TeachScreen::PARTY_TEXT: return "party_text";
case TeachScreen::PARTY_YES_NO: return "party_yes_no";
case TeachScreen::KNOWN_MOVES: return "known_moves";
case TeachScreen::TM_SCENE: return "tm_scene";
}
return "?";
}
Expand All @@ -286,6 +302,8 @@
case TeachText::STOP_QUESTION: return "stop_question";
case TeachText::DID_NOT_LEARN: return "did_not_learn";
case TeachText::NOT_COMPATIBLE: return "not_compatible";
case TeachText::MACHINE_SET: return "machine_set";
case TeachText::LEARNED: return "learned";
}
return "?";
}
Expand All @@ -301,6 +319,9 @@
if (solid(screen, CORNER, PARTY_TEAL)){
return read_party_screen(screen);
}
if (patterned(screen, SCENE_LEFT, SCENE_BLUE) && patterned(screen, SCENE_RIGHT, SCENE_BLUE)){
return TeachScreen::TM_SCENE;
}
if (read_summary_move_cursor(screen)){
return TeachScreen::KNOWN_MOVES;
}
Expand Down Expand Up @@ -350,7 +371,7 @@
if (kind == TeachScreen::PARTY_PROMPT){
return teach_text(text_matcher().read(logger, language, game_crop(screen, PROMPT_LINE), DARK_TEXT_FILTERS()));
}
if (kind != TeachScreen::PARTY_TEXT && kind != TeachScreen::PARTY_YES_NO){
if (kind != TeachScreen::PARTY_TEXT && kind != TeachScreen::PARTY_YES_NO && kind != TeachScreen::TM_SCENE){
return TeachText::UNKNOWN;
}
// The phrase either line matches best.
Expand Down Expand Up @@ -415,8 +436,8 @@
// then: in the bag, its pocket, the key items from the top (KEY ITEMS
// only) and the cursor's row (from 1, or "none"); in the TM CASE, its
// moves from the top and the cursor's row; in an item menu or YES/NO, the
// cursor's row; over the party menu, the message; on KNOWN MOVES, the five
// rows' moves and the cursor's row.
// cursor's row; over the party menu or in the TM's scene, the message; on
// KNOWN MOVES, the five rows' moves and the cursor's row.
class Test_TeachScreen : public ImageUnitTest{
public:
Test_TeachScreen(const std::string& image)
Expand All @@ -440,7 +461,7 @@
}

private:
static void add_details(

Check warning on line 464 in SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/Menus/PokemonFRLG_TeachScreenReader.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::NintendoSwitch::PokemonFRLG::Test_TeachScreen::add_details( Logger & logger , Language language , const ImageViewRGB32 & screen , TeachScreen kind , std :: vector<std::string> & labels , std :: vector<std::string> & values): function_lines=44 (advisory above 25, limit 50)
Logger& logger, Language language, const ImageViewRGB32& screen, TeachScreen kind,
std::vector<std::string>& labels, std::vector<std::string>& values
){
Expand All @@ -467,6 +488,7 @@
[[fallthrough]];
case TeachScreen::PARTY_PROMPT:
case TeachScreen::PARTY_TEXT:
case TeachScreen::TM_SCENE:
labels.push_back("text");
values.push_back(teach_text_name(read_teach_text(logger, language, screen)));
return;
Expand Down Expand Up @@ -506,6 +528,8 @@
"KnownMoves_eng.png",
"StopQuestion_eng.png",
"DidNotLearn_eng.png",
// HM01 to an Oddish with two free slots (00.23, console B).
"MachineSet_eng.png",
}){
database.add<Test_TeachScreen>(std::string("PokemonFRLG/TeachScreenReader/") + image);
}
Expand Down
Loading
Loading