@@ -24,6 +24,39 @@ namespace PokemonLZA{
2424
2525using namespace Pokemon ;
2626
27+ StallBuyerRow::StallBuyerRow (int index, std::string&& ordinal)
28+ : StaticTableRow(ordinal)
29+ , item(LockMode::LOCK_WHILE_RUNNING, " The number of the " + ordinal + " item you want to purchase." )
30+ , quantity(LockMode::LOCK_WHILE_RUNNING, 0 , 0 , 999 )
31+ , ordinal(std::move(ordinal))
32+ , index(index)
33+ {
34+ PA_ADD_STATIC (item);
35+ PA_ADD_OPTION (quantity);
36+ }
37+
38+ StallBuyerTable::StallBuyerTable ()
39+ : StaticTableOption(" <b>Number to Purchase:</b><br>The number of items you want to purchase." , LockMode::LOCK_WHILE_RUNNING)
40+ {
41+ add_row (std::make_unique<StallBuyerRow>(0 , " first" ));
42+ add_row (std::make_unique<StallBuyerRow>(1 , " second" ));
43+ add_row (std::make_unique<StallBuyerRow>(2 , " third" ));
44+ add_row (std::make_unique<StallBuyerRow>(3 , " fourth" ));
45+ add_row (std::make_unique<StallBuyerRow>(4 , " fifth" ));
46+ add_row (std::make_unique<StallBuyerRow>(5 , " sixth" ));
47+ add_row (std::make_unique<StallBuyerRow>(6 , " seventh" ));
48+
49+ finish_construction ();
50+ }
51+ std::vector<std::string> StallBuyerTable::make_header () const {
52+ std::vector<std::string> ret{
53+ " Item" ,
54+ " Quantity"
55+ };
56+ return ret;
57+ }
58+
59+
2760StallBuyer_Descriptor::StallBuyer_Descriptor ()
2861 : SingleSwitchProgramDescriptor(
2962 " PokemonLZA:StallBuyer" ,
@@ -55,34 +88,14 @@ std::unique_ptr<StatsTracker> StallBuyer_Descriptor::make_stats() const{
5588
5689
5790StallBuyer::StallBuyer ()
58- : ITEM_POSITION(
59- " <b>Item Position to Purchase:</b>" ,
60- {
61- {ItemPosition::FirstItem, " FirstItem" , " First Item" },
62- {ItemPosition::SecondItem, " SecondItem" , " Second Item" },
63- {ItemPosition::ThirdItem, " ThirdItem" , " Third Item" },
64- {ItemPosition::FourthItem, " FourthItem" , " Fourth Item" },
65- {ItemPosition::FifthItem, " FifthItem" , " Fifth Item" },
66- {ItemPosition::SixthItem, " SixthItem" , " Sixth Item" },
67- {ItemPosition::SeventhItem, " SeventhItem" , " Seventh Item" },
68- },
69- LockMode::LOCK_WHILE_RUNNING,
70- ItemPosition::FirstItem
71- )
72- , NUM_PURCHASE(
73- " <b>Number to Purchase:</b><br>The number of items you want to purchase." ,
74- LockMode::LOCK_WHILE_RUNNING,
75- 100 , 1 , 999
76- )
77- , GO_HOME_WHEN_DONE(false )
91+ : GO_HOME_WHEN_DONE(false )
7892 , NOTIFICATION_STATUS_UPDATE(" Status Update" , true , false , std::chrono::seconds(3600 ))
7993 , NOTIFICATIONS({
8094 &NOTIFICATION_STATUS_UPDATE,
8195 &NOTIFICATION_PROGRAM_FINISH,
8296 &NOTIFICATION_ERROR_FATAL,
8397 })
8498{
85- PA_ADD_OPTION (ITEM_POSITION);
8699 PA_ADD_OPTION (NUM_PURCHASE);
87100 PA_ADD_OPTION (GO_HOME_WHEN_DONE);
88101 PA_ADD_OPTION (NOTIFICATIONS);
@@ -146,88 +159,109 @@ void StallBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerConte
146159 StallBuyer_Descriptor::Stats& stats = env.current_stats <StallBuyer_Descriptor::Stats>();
147160 assert_16_9_720p_min (env.logger (), env.console );
148161
149- while (true ) {
150- context.wait_for_all_requests ();
162+ std::optional<int > stall_amount_item;
151163
152- ButtonWatcher buttonA (
153- COLOR_RED,
154- ButtonType::ButtonA,
155- {0.1 , 0.1 , 0.8 , 0.8 },
156- &env.console .overlay ()
157- );
158- SelectionArrowWatcher select (
159- COLOR_YELLOW, &env.console .overlay (),
160- SelectionArrowType::RIGHT,
161- {0.715 , 0.165 , 0.045 , 0.440 }
162- );
163- SelectionArrowWatcher confirm (
164- COLOR_YELLOW, &env.console .overlay (),
165- SelectionArrowType::RIGHT,
166- {0.665 , 0.600 , 0.145 , 0.080 }
167- );
168- FlatWhiteDialogWatcher dialog (COLOR_RED, &env.console .overlay ());
164+ for (StaticTableRow* row : NUM_PURCHASE.table ()){
165+ StallBuyerRow& stall_buyer_row = static_cast <StallBuyerRow&>(*row);
169166
170- int ret = wait_until (
171- env.console , context,
172- 30000ms,
173- {
174- buttonA,
175- select,
176- confirm,
177- dialog,
178- }
167+ for (int purchases = 0 ; purchases < stall_buyer_row.quantity ; /* Do not increment, it will be done in the loop*/ ){
168+ context.wait_for_all_requests ();
169+
170+ ButtonWatcher buttonA (
171+ COLOR_RED,
172+ ButtonType::ButtonA,
173+ {0.1 , 0.1 , 0.8 , 0.8 },
174+ &env.console .overlay ()
179175 );
180- context.wait_for (100ms);
181-
182- switch (ret){
183- case 0 :
184- env.log (" Detected A button." );
185- pbf_press_button (context, BUTTON_A, 160ms, 80ms);
186- continue ;
187-
188- case 1 :
189- {
190- env.log (" Detected item selection screen." );
191- int stall_amount_item = detect_stall_amount_item (env, stats);
192- env.log (" Detected stall with " + std::to_string (stall_amount_item) + " items to sell." );
193- auto [direction, presses] = compute_needed_inputs (static_cast <int >(ITEM_POSITION.get ()), stall_amount_item);
194- for (int i = 0 ; i < presses; i++){
195- pbf_press_dpad (context, direction, 160ms, 80ms);
196- }
197- pbf_press_button (context, BUTTON_A, 160ms, 80ms);
198- continue ;
199- }
200- case 2 :
201- env.log (" Detected purchase confirm screen." );
202- pbf_press_button (context, BUTTON_A, 160ms, 80ms);
203- stats.purchases ++;
204- env.update_stats ();
205-
206- if (stats.purchases == NUM_PURCHASE) {
207- // pbf_mash_button(context, BUTTON_B, 5000ms);
208- // intentionally don't leave the purchase menu to not get attacked
209- GO_HOME_WHEN_DONE.run_end_of_program (context);
210- send_program_finished_notification (env, NOTIFICATION_PROGRAM_FINISH);
211- return ;
176+ SelectionArrowWatcher select (
177+ COLOR_YELLOW, &env.console .overlay (),
178+ SelectionArrowType::RIGHT,
179+ {0.715 , 0.165 , 0.045 , 0.440 }
180+ );
181+ SelectionArrowWatcher confirm (
182+ COLOR_YELLOW, &env.console .overlay (),
183+ SelectionArrowType::RIGHT,
184+ {0.665 , 0.600 , 0.145 , 0.080 }
185+ );
186+ FlatWhiteDialogWatcher dialog (COLOR_RED, &env.console .overlay ());
187+
188+ int ret = wait_until (
189+ env.console , context,
190+ 30000ms,
191+ {
192+ buttonA,
193+ select,
194+ confirm,
195+ dialog,
196+ }
197+ );
198+ context.wait_for (100ms);
199+
200+ switch (ret){
201+ case 0 :
202+ env.log (" Detected A button." );
203+ pbf_press_button (context, BUTTON_A, 160ms, 80ms);
204+ continue ;
205+
206+ case 1 :
207+ {
208+ env.log (" Detected item selection screen." );
209+ if (!stall_amount_item.has_value ()){
210+ stall_amount_item = detect_stall_amount_item (env, stats);
211+ env.log (" Detected stall with " + std::to_string (stall_amount_item.value ()) + " items to sell." );
212+ for (StaticTableRow* item : NUM_PURCHASE.table ()){
213+ StallBuyerRow& casted_item = static_cast <StallBuyerRow&>(*item);
214+ if (casted_item.index >= stall_amount_item.value () && casted_item.quantity > 0 ){
215+ throw UserSetupError (
216+ env.logger (),
217+ " Stall is of size " + std::to_string (stall_amount_item.value ()) + " and Number to Purchase for " +
218+ casted_item.ordinal + " item is " + std::to_string (casted_item.quantity ) + " ."
219+ );
220+ }
221+ }
222+ }
223+ auto [direction, presses] = compute_needed_inputs (stall_buyer_row.index , stall_amount_item.value ());
224+ for (int i = 0 ; i < presses; i++){
225+ pbf_press_dpad (context, direction, 160ms, 80ms);
226+ }
227+ pbf_press_button (context, BUTTON_A, 160ms, 80ms);
228+ continue ;
212229 }
230+ case 2 :
231+ env.log (" Detected purchase confirm screen." );
232+ pbf_press_button (context, BUTTON_A, 160ms, 80ms);
233+ stats.purchases ++;
234+ purchases++;
235+ env.update_stats ();
213236
214- continue ;
237+ if (purchases == stall_buyer_row.quantity ){
238+ std::stringstream ss;
239+ ss << " Purchased " << stall_buyer_row.quantity << " of " << stall_buyer_row.ordinal << " item." ;
240+ send_program_status_notification (env, NOTIFICATION_STATUS_UPDATE, ss.str ());
241+ break ;
242+ }
215243
216- case 3 :
217- env.log (" Detected white dialog." );
218- pbf_press_button (context, BUTTON_A, 160ms, 80ms);
219- continue ;
244+ continue ;
220245
221- default :
222- stats.errors ++;
223- env.update_stats ();
224- OperationFailedException::fire (
225- ErrorReport::SEND_ERROR_REPORT,
226- " No recognized state after 30 seconds." ,
227- env.console
228- );
246+ case 3 :
247+ env.log (" Detected white dialog." );
248+ pbf_press_button (context, BUTTON_A, 160ms, 80ms);
249+ continue ;
250+
251+ default :
252+ stats.errors ++;
253+ env.update_stats ();
254+ OperationFailedException::fire (
255+ ErrorReport::SEND_ERROR_REPORT,
256+ " No recognized state after 30 seconds." ,
257+ env.console
258+ );
259+ }
229260 }
230261 }
262+ // intentionally don't leave the purchase menu to not get attacked
263+ GO_HOME_WHEN_DONE.run_end_of_program (context);
264+ send_program_finished_notification (env, NOTIFICATION_PROGRAM_FINISH);
231265}
232266
233267
0 commit comments