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
4 changes: 2 additions & 2 deletions SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#include "PokemonFRLG_Settings.h"

#include "Programs/ShinyHunting/PokemonFRLG_StarterReset.h"
#include "Programs/ShinyHunting/PokemonFRLG_GiftReset.h"
#include "Programs/TestPrograms/PokemonFRLG_SoundListener.h"

namespace PokemonAutomation{
Expand All @@ -32,7 +32,7 @@ std::vector<PanelEntry> PanelListFactory::make_panels() const{
//ret.emplace_back("---- General ----");

ret.emplace_back("---- Shiny Hunting ----");
ret.emplace_back(make_single_switch_program<StarterReset_Descriptor, StarterReset>());
ret.emplace_back(make_single_switch_program<GiftReset_Descriptor, GiftReset>());


if (PreloadSettings::instance().DEVELOPER_MODE){
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Starter Reset
/* Gift Reset
*
* From: https://github.com/PokemonAutomation/
*
Expand All @@ -18,66 +18,79 @@
#include "PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h"
#include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h"
#include "PokemonFRLG/PokemonFRLG_Navigation.h"
#include "PokemonFRLG_StarterReset.h"
#include "PokemonFRLG_GiftReset.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonFRLG{

StarterReset_Descriptor::StarterReset_Descriptor()
GiftReset_Descriptor::GiftReset_Descriptor()
: SingleSwitchProgramDescriptor(
"PokemonFRLG:StarterReset",
Pokemon::STRING_POKEMON + " FRLG", "Starter Reset",
"Programs/PokemonFRLG/StarterReset.html",
"Soft reset for a shiny starter.",
"PokemonFRLG:GiftReset",
Pokemon::STRING_POKEMON + " FRLG", "Gift Reset",
"Programs/PokemonFRLG/GiftReset.html",
"Soft reset for a shiny gift Pokemon.",
ProgramControllerClass::StandardController_RequiresPrecision,
FeedbackType::REQUIRED,
AllowCommandsWhenRunning::DISABLE_COMMANDS
)
{}

struct StarterReset_Descriptor::Stats : public StatsTracker{
struct GiftReset_Descriptor::Stats : public StatsTracker{
Stats()
: resets(m_stats["Resets"])
, shinystarter(m_stats["Shiny Starter"])
, shinies(m_stats["Shinies"])
, errors(m_stats["Errors"])
{
m_display_order.emplace_back("Resets");
m_display_order.emplace_back("Shiny Starter");
m_display_order.emplace_back("Shinies");
m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO);
}
std::atomic<uint64_t>& resets;
std::atomic<uint64_t>& shinystarter;
std::atomic<uint64_t>& shinies;
std::atomic<uint64_t>& errors;
};
std::unique_ptr<StatsTracker> StarterReset_Descriptor::make_stats() const{
std::unique_ptr<StatsTracker> GiftReset_Descriptor::make_stats() const{
return std::unique_ptr<StatsTracker>(new Stats());
}

StarterReset::StarterReset()
: GO_HOME_WHEN_DONE(true)
, NOTIFICATION_SHINY_STARTER(
"Shiny Starter",
GiftReset::GiftReset()
: TARGET(
"<b>Target:</b><br>",
{
{Target::starters, "starters", "Bulbasaur / Squirtle / Charmander"},
{Target::hitmon, "hitmon", "Magikarp, Hitmonlee / Hitmonchan"},
{Target::eevee, "eevee", "Eevee"},
{Target::lapras, "lapras", "Lapras"},
{Target::fossils, "fossils", "Omanyte / Kabuto / Aerodactyl "},
},
LockMode::LOCK_WHILE_RUNNING,
Target::starters
)
, GO_HOME_WHEN_DONE(true)
, NOTIFICATION_SHINY(
"Shiny",
true, true, ImageAttachmentMode::JPG,
{"Notifs", "Showcase"}
)
, NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600))
, NOTIFICATIONS({
&NOTIFICATION_SHINY_STARTER,
&NOTIFICATION_SHINY,
&NOTIFICATION_STATUS_UPDATE,
&NOTIFICATION_PROGRAM_FINISH,
})
{
PA_ADD_OPTION(TARGET);
PA_ADD_OPTION(GO_HOME_WHEN_DONE);
PA_ADD_OPTION(NOTIFICATIONS);
}

//Pick up starter, say no to nickname
void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
StarterReset_Descriptor::Stats& stats = env.current_stats<StarterReset_Descriptor::Stats>();
void GiftReset::obtain_pokemon(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
GiftReset_Descriptor::Stats& stats = env.current_stats<GiftReset_Descriptor::Stats>();

/*
Ah, STARTER is your choice... red adv arrow
Ah, starter is your choice... red adv arrow
So you're claiming... YES/NO box
rly quite energetic... no arrow
player received the starter from... no arrow, jingle must complete
Expand All @@ -86,7 +99,7 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro
rival received the ... no arrow DONE
*/

env.log("Obtaining starter.");
env.log("Obtaining Pokemon.");
pbf_press_button(context, BUTTON_A, 320ms, 640ms);

bool seen_selection_arrow = false;
Expand Down Expand Up @@ -117,25 +130,37 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro
case 1:
env.log("Detected Selection Dialog. Pressing A.");
if (!seen_selection_arrow) {
env.log("First selection box detected. YES to starter.");
seen_selection_arrow = true;
pbf_press_button(context, BUTTON_A, 320ms, 640ms);

//Skip past energetic and jingle
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_wait(context, 500ms);
context.wait_for_all_requests();
if (TARGET == Target::starters || TARGET == Target::hitmon) {
env.log("First selection box detected. YES to starter.");
seen_selection_arrow = true;
pbf_press_button(context, BUTTON_A, 320ms, 640ms);
} else {
env.log("Selection box detected. NO to nickname.");
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
context.wait_for_all_requests();
return;
}

if (TARGET == Target::starters) {
//Skip past energetic and jingle
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_wait(context, 500ms);
context.wait_for_all_requests();
}
pbf_press_button(context, BUTTON_B, 320ms, 640ms);

} else {
env.log("Second selection box detected. NO to nickname.");
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
//seen_nickname_arrow = true;

//Press B some to try and skip the rival's pickup
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
if (TARGET == Target::starters) {
//Press B some to try and skip the rival's pickup
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
}
context.wait_for_all_requests();
return;
}
Expand All @@ -159,19 +184,39 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro
context.wait_for_all_requests();
}

//Different as lapras has multiple no-red-arrow dialog boxes
void GiftReset::obtain_lapras(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
env.log("Obtaining Lapras.");
pbf_press_button(context, BUTTON_A, 320ms, 640ms);

//At least 9 lines of dialog/No to nickname for Lapras
//At least 4 lines of dialog for fossils
//This takes care of the entire conversion+nickname+exit dialog
int limit = 10;
if (TARGET == Target::fossils) {
limit = 5;
}
for (int i = 0; i < limit; i++) {
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_wait(context, 100ms);
context.wait_for_all_requests();
}
context.wait_for_all_requests();
}

//After declining to nickname, clear rival pickup and open your starter's summary
void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
StarterReset_Descriptor::Stats& stats = env.current_stats<StarterReset_Descriptor::Stats>();
void GiftReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
GiftReset_Descriptor::Stats& stats = env.current_stats<GiftReset_Descriptor::Stats>();

//From no to nickname to overworld
StartMenuWatcher start_menu(COLOR_RED);

int ret = run_until<ProControllerContext>(
env.console, context,
[](ProControllerContext& context) {
for (int i = 0; i < 5; i++) {
for (int i = 0; i < 10; i++) {
pbf_press_button(context, BUTTON_B, 320ms, 640ms);
pbf_wait(context, 500ms);
pbf_wait(context, 100ms);
context.wait_for_all_requests();
pbf_press_button(context, BUTTON_PLUS, 320ms, 640ms);
}
Expand All @@ -184,12 +229,21 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll
env.update_stats();
OperationFailedException::fire(
ErrorReport::SEND_ERROR_REPORT,
"open_summary(): Unable to open Start menu.",
"open_summary(): Unable to open Start menu after 10 attempts.",
env.console
);
}

//No Pokedex yet, so 1 A press to open party menu
if (TARGET != Target::starters) {
//Pokedex, Pokemon, Bag, Trainer, Save, Option, Exit
env.log("Navigating to party menu.");
pbf_wait(context, 200ms);
context.wait_for_all_requests();
pbf_press_dpad(context, DPAD_DOWN, 320ms, 320ms);
context.wait_for_all_requests();
} //For starters, no Pokedex yet, do Pokemon is on top and we skip this

//Open party menu
pbf_press_button(context, BUTTON_A, 320ms, 640ms);

BlackScreenOverWatcher blk1(COLOR_RED, {0.282, 0.064, 0.448, 0.871});
Expand All @@ -210,9 +264,15 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll
}
context.wait_for_all_requests();

//Press up twice to get to the last slot
if (TARGET != Target::starters) {
pbf_press_dpad(context, DPAD_UP, 320ms, 320ms);
pbf_press_dpad(context, DPAD_UP, 320ms, 320ms);
}

//Two presses to open summary
pbf_press_button(context, BUTTON_A, 320ms, 640ms);
pbf_press_button(context, BUTTON_A, 320ms, 640ms);
pbf_press_button(context, BUTTON_A, 320ms, 320ms);
pbf_press_button(context, BUTTON_A, 320ms, 320ms);

BlackScreenOverWatcher blk2(COLOR_RED, {0.282, 0.064, 0.448, 0.871});
int ret2 = wait_until(
Expand All @@ -236,20 +296,28 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll
}


void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
void GiftReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
//StartProgramChecks::check_performance_class_wired_or_wireless(context);

StarterReset_Descriptor::Stats& stats = env.current_stats<StarterReset_Descriptor::Stats>();
GiftReset_Descriptor::Stats& stats = env.current_stats<GiftReset_Descriptor::Stats>();

/*
* Settings: Text Speed fast. Default borders.
* Setup: Stand in front of the starter you want. Save the game.
* Setup: 5 pokemon in your party. None for starter. Stand in front of the pokemon. Save the game.
* For non-starters: move menu cursor back to the top (POKEDEX)!
* for starters, pokemon menu will be on top as it is added after picking
* For magikarp: you need money to buy it
* fossils: need to corner the scientist
*/

bool shiny_starter = false;

while (!shiny_starter) {
obtain_starter(env, context);
if (TARGET != Target::lapras && TARGET != Target::fossils) {
obtain_pokemon(env, context);
} else {
obtain_lapras(env, context);
}
open_summary(env, context);

VideoSnapshot screen = env.console.video().snapshot();
Expand All @@ -258,19 +326,20 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon
shiny_starter = shiny_checker.read(env.console.logger(), screen);

if (shiny_starter) {
env.log("Shiny starter detected!");
stats.shinystarter++;
send_program_notification(env, NOTIFICATION_SHINY_STARTER, COLOR_YELLOW, "Shiny starter found!", {}, "", screen, true);
env.log("Shiny found!");
stats.shinies++;
send_program_notification(env, NOTIFICATION_SHINY, COLOR_YELLOW, "Shiny found!", {}, "", screen, true);
break;
} else {
env.log("Starter is not shiny.");
env.log("Pokemon is not shiny.");
env.log("Soft resetting.");
send_program_status_notification(
env, NOTIFICATION_STATUS_UPDATE,
"Soft resetting."
);
soft_reset(env.program_info(), env.console, context);
stats.resets++;
env.update_stats();
context.wait_for_all_requests();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* Starter Reset
/* Gift Reset
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_PokemonFRLG_StarterReset_H
#define PokemonAutomation_PokemonFRLG_StarterReset_H
#ifndef PokemonAutomation_PokemonFRLG_GiftReset_H
#define PokemonAutomation_PokemonFRLG_GiftReset_H

#include "CommonFramework/Notifications/EventNotificationsTable.h"
#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h"
Expand All @@ -15,16 +15,16 @@ namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonFRLG{

class StarterReset_Descriptor : public SingleSwitchProgramDescriptor{
class GiftReset_Descriptor : public SingleSwitchProgramDescriptor{
public:
StarterReset_Descriptor();
GiftReset_Descriptor();
struct Stats;
virtual std::unique_ptr<StatsTracker> make_stats() const override;
};

class StarterReset : public SingleSwitchProgramInstance{
class GiftReset : public SingleSwitchProgramInstance{
public:
StarterReset();
GiftReset();
virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override;

virtual void start_program_border_check(
Expand All @@ -33,12 +33,22 @@ class StarterReset : public SingleSwitchProgramInstance{
) override{}

private:
void obtain_starter(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
void obtain_pokemon(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
void obtain_lapras(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
void open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context);

enum class Target{
starters,
hitmon,
eevee,
lapras,
fossils,
};
EnumDropdownOption<Target> TARGET;

GoHomeWhenDoneOption GO_HOME_WHEN_DONE;

EventNotificationOption NOTIFICATION_SHINY_STARTER;
EventNotificationOption NOTIFICATION_SHINY;
EventNotificationOption NOTIFICATION_STATUS_UPDATE;
EventNotificationsOption NOTIFICATIONS;
};
Expand Down
4 changes: 2 additions & 2 deletions SerialPrograms/cmake/SourceFiles.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -1413,8 +1413,8 @@ file(GLOB LIBRARY_SOURCES
Source/PokemonFRLG/PokemonFRLG_Panels.h
Source/PokemonFRLG/PokemonFRLG_Settings.cpp
Source/PokemonFRLG/PokemonFRLG_Settings.h
Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp
Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h
Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp
Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.h
Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp
Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h
Source/PokemonHome/Inference/PokemonHome_BallReader.cpp
Expand Down