1+ /* Nugget Bridge Farmer
2+ *
3+ * From: https://github.com/PokemonAutomation/
4+ *
5+ */
6+
7+ #include " CommonFramework/Notifications/ProgramNotifications.h"
8+ #include " CommonFramework/ProgramStats/StatsTracking.h"
9+ #include " CommonTools/Async/InferenceRoutines.h"
10+ #include " NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
11+ #include " NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h"
12+ #include " Pokemon/Pokemon_Strings.h"
13+ #include " PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h"
14+ #include " PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h"
15+ #include " PokemonFRLG/PokemonFRLG_Navigation.h"
16+ #include " PokemonFRLG_NuggetBridgeFarmer.h"
17+
18+ namespace PokemonAutomation {
19+ namespace NintendoSwitch {
20+ namespace PokemonFRLG {
21+
22+ NuggetBridgeFarmer_Descriptor::NuggetBridgeFarmer_Descriptor ()
23+ : SingleSwitchProgramDescriptor(
24+ " PokemonFRLG:NuggetBridgeFarmer" ,
25+ Pokemon::STRING_POKEMON + " FRLG" , " Nugget Bridge Farmer" ,
26+ " Programs/PokemonFRLG/NuggetBridgeFarmer.html" ,
27+ " Farm the Nugget Bridge for money." ,
28+ ProgramControllerClass::StandardController_RequiresPrecision,
29+ FeedbackType::REQUIRED,
30+ AllowCommandsWhenRunning::DISABLE_COMMANDS
31+ )
32+ {}
33+
34+ struct NuggetBridgeFarmer_Descriptor ::Stats : public StatsTracker {
35+ public:
36+ Stats ()
37+ : nuggets(m_stats[" Nuggets" ])
38+ , errors(m_stats[" Errors" ])
39+ {
40+ m_display_order.emplace_back (" Nuggets" );
41+ m_display_order.emplace_back (" Errors" , HIDDEN_IF_ZERO);
42+ }
43+
44+ std::atomic<uint64_t >& nuggets;
45+ std::atomic<uint64_t >& errors;
46+ };
47+
48+ std::unique_ptr<StatsTracker> NuggetBridgeFarmer_Descriptor::make_stats () const {
49+ return std::unique_ptr<StatsTracker>(new Stats ());
50+ }
51+
52+ NuggetBridgeFarmer::NuggetBridgeFarmer ()
53+ : STOP_AFTER_CURRENT(" Nugget" )
54+ , NUM_NUGGETS(
55+ " <b>Number of Nuggets:</b><br>"
56+ " Zero will run until 'Stop after Current Nugget' is pressed or the program is manually stopped." ,
57+ LockMode::UNLOCK_WHILE_RUNNING,
58+ 120 , // About 2 hours of farming.
59+ 0
60+ )
61+ , GO_HOME_WHEN_DONE(false )
62+ /* , PERIODIC_SAVE(
63+ "<b>Periodically Save:</b><br>"
64+ "Save the game every this many nuggets. This reduces the loss to game crashes. Set to zero to disable.",
65+ LockMode::UNLOCK_WHILE_RUNNING,
66+ 10,
67+ 0
68+ )*/
69+ , NOTIFICATION_STATUS_UPDATE(" Status Update" , true , false , std::chrono::seconds(3600 ))
70+ , NOTIFICATIONS({
71+ &NOTIFICATION_STATUS_UPDATE,
72+ &NOTIFICATION_PROGRAM_FINISH,
73+ &NOTIFICATION_ERROR_FATAL,
74+ })
75+ {
76+ PA_ADD_OPTION (STOP_AFTER_CURRENT);
77+
78+ PA_ADD_OPTION (NUM_NUGGETS);
79+ PA_ADD_OPTION (GO_HOME_WHEN_DONE);
80+ // PA_ADD_OPTION(PERIODIC_SAVE);
81+
82+ PA_ADD_OPTION (NOTIFICATIONS);
83+ }
84+
85+ void NuggetBridgeFarmer::program (SingleSwitchProgramEnvironment& env, ProControllerContext& context) {
86+
87+ home_black_border_check (env.console , context);
88+
89+ NuggetBridgeFarmer_Descriptor::Stats& stats = env.current_stats <NuggetBridgeFarmer_Descriptor::Stats>();
90+ DeferredStopButtonOption::ResetOnExit reset_on_exit (STOP_AFTER_CURRENT);
91+
92+ // for (uint32_t nuggets_since_last_save = 0;; nuggets_since_last_save++) {
93+ while (true ) {
94+ send_program_status_notification (env, NOTIFICATION_STATUS_UPDATE);
95+ if (NUM_NUGGETS != 0 && stats.nuggets >= NUM_NUGGETS) {
96+ break ;
97+ }
98+
99+ env.console .log (" Exiting Pokemon Center..." );
100+ while (true ){
101+ BlackScreenWatcher pokemon_ceter_exit (COLOR_RED);
102+
103+ int ret = run_until<ProControllerContext>(
104+ env.console , context,
105+ [](ProControllerContext& context) {
106+ pbf_press_dpad (context, DPAD_DOWN, 2000ms, 0ms);
107+ },
108+ { pokemon_ceter_exit }
109+ );
110+
111+ if (ret == 0 ) {
112+ break ;
113+ }
114+ }
115+
116+ env.console .log (" Detecting overworld..." );
117+ while (true ) {
118+ BlackScreenOverWatcher overworld_entered (COLOR_RED);
119+
120+ int ret = wait_until (
121+ env.console , context,
122+ std::chrono::milliseconds (2000 ),
123+ {overworld_entered}
124+ );
125+
126+ if (ret == 0 ) {
127+ break ;
128+ }
129+ }
130+
131+ // There is a small delay from seeing the overworld to being able to a actually move.
132+ pbf_wait (context, 1000ms);
133+ context.wait_for_all_requests ();
134+
135+ env.console .log (" Navigating to Team Rocket member..." );
136+
137+ ssf_press_button (context, BUTTON_B, 0ms, 12000ms);
138+ pbf_press_dpad (context, DPAD_LEFT, 590ms, 0ms);
139+ pbf_press_dpad (context, DPAD_UP, 510ms, 0ms);
140+ pbf_press_dpad (context, DPAD_LEFT, 1550ms, 0ms);
141+ pbf_press_dpad (context, DPAD_UP, 1175ms, 0ms);
142+ pbf_press_dpad (context, DPAD_RIGHT, 1950ms, 0ms);
143+ pbf_press_dpad (context, DPAD_UP, 5300ms, 0ms);
144+
145+ context.wait_for_all_requests ();
146+
147+ env.console .log (" Starting battle..." );
148+ while (true ){
149+ BattleMenuWatcher battle_menu (COLOR_RED);
150+
151+ int ret = run_until<ProControllerContext>(
152+ env.console , context,
153+ [](ProControllerContext& context) {
154+ pbf_mash_button (context, BUTTON_B, 2000ms);
155+ },
156+ { battle_menu }
157+ );
158+
159+ if (ret == 0 ) {
160+ break ;
161+ }
162+ }
163+
164+ env.console .log (" Loosing battle..." );
165+ while (true ){
166+ BlackScreenWatcher battle_lost (COLOR_RED);
167+
168+ int ret = run_until<ProControllerContext>(
169+ env.console , context,
170+ [](ProControllerContext& context) {
171+ pbf_mash_button (context, BUTTON_A, 2000ms);
172+ },
173+ { battle_lost }
174+ );
175+
176+ if (ret == 0 ) {
177+ break ;
178+ }
179+ }
180+
181+ env.console .log (" Talking to nurse joy..." );
182+ while (true ){
183+ WhiteDialogWatcher nurse_joy_dialog (COLOR_RED);
184+ int ret = run_until<ProControllerContext>(
185+ env.console , context,
186+ [](ProControllerContext& context) {
187+ pbf_mash_button (context, BUTTON_B, 2000ms);
188+ },
189+ { nurse_joy_dialog }
190+ );
191+
192+ if (ret == 0 ) {
193+ break ;
194+ }
195+ }
196+
197+ // Spam B till nurse joy stops talking
198+ while (true ){
199+ WhiteDialogWatcher nurse_joy_dialog (COLOR_RED);
200+ int ret = run_until<ProControllerContext>(
201+ env.console , context,
202+ [](ProControllerContext& context) {
203+ pbf_mash_button (context, BUTTON_B, 2000ms);
204+ },
205+ { nurse_joy_dialog }
206+ );
207+
208+ if (ret != 0 ) {
209+ break ;
210+ }
211+ }
212+
213+ // TODO: Implement periodic saving. The Save Menu keeps the last cursor position. Need to implement arrow dectection on the correct option
214+ /* if (PERIODIC_SAVE != 0 && nuggets_since_last_save >= PERIODIC_SAVE) {
215+ StartMenuWatcher start_menu = StartMenuWatcher(COLOR_RED);
216+
217+ env.console.log("Saving game...");
218+
219+ while (true)
220+ {
221+ int ret = run_until<ProControllerContext>(
222+ env.console, context,
223+ [](ProControllerContext& context) {
224+ pbf_press_button(context, BUTTON_PLUS, 320ms, 640ms);
225+ pbf_wait(context, 100ms);
226+ context.wait_for_all_requests();
227+ },
228+ { start_menu }
229+ );
230+
231+ if (ret == 0) {
232+ break;
233+ }
234+ }
235+
236+ pbf_press_dpad(context, DPAD_DOWN, 320ms, 320ms);
237+ pbf_press_dpad(context, DPAD_DOWN, 320ms, 320ms);
238+ pbf_press_dpad(context, DPAD_DOWN, 320ms, 320ms);
239+ pbf_press_dpad(context, DPAD_DOWN, 320ms, 320ms);
240+
241+ pbf_press_button(context, BUTTON_A, 320ms, 320ms);
242+ pbf_press_button(context, BUTTON_A, 320ms, 320ms);
243+
244+ pbf_mash_button(context, BUTTON_B, 2000ms);
245+ nuggets_since_last_save = 0;
246+ }*/
247+
248+ stats.nuggets ++;
249+ env.update_stats ();
250+
251+ if (STOP_AFTER_CURRENT.should_stop ()) {
252+ break ;
253+ }
254+ }
255+
256+ send_program_finished_notification (env, NOTIFICATION_PROGRAM_FINISH);
257+ GO_HOME_WHEN_DONE.run_end_of_program (context);
258+ }
259+
260+
261+ }
262+ }
263+ }
0 commit comments