|
17 | 17 | #include "PokemonLZA/Inference/PokemonLZA_SelectionArrowDetector.h" |
18 | 18 | #include "PokemonLZA/Inference/PokemonLZA_DialogDetector.h" |
19 | 19 | #include "PokemonLZA_StallBuyer.h" |
| 20 | +#include <array> |
20 | 21 |
|
21 | 22 | namespace PokemonAutomation{ |
22 | 23 | namespace NintendoSwitch{ |
23 | 24 | namespace PokemonLZA{ |
24 | 25 |
|
25 | 26 | using namespace Pokemon; |
26 | 27 |
|
| 28 | +StallBuyerRow::StallBuyerRow(int index, std::string&& ordinal) |
| 29 | + : StaticTableRow(ordinal) |
| 30 | + , item(LockMode::LOCK_WHILE_RUNNING, "The number of the " + ordinal + " item you want to purchase.") |
| 31 | + , quantity(LockMode::LOCK_WHILE_RUNNING, 0, 0, 999) |
| 32 | + , ordinal(std::move(ordinal)) |
| 33 | + , index(index) |
| 34 | +{ |
| 35 | + PA_ADD_STATIC(item); |
| 36 | + PA_ADD_OPTION(quantity); |
| 37 | +} |
| 38 | + |
| 39 | +StallBuyerTable::StallBuyerTable() |
| 40 | + : StaticTableOption("<b>Number to Purchase:</b><br>The number of items you want to purchase.", LockMode::LOCK_WHILE_RUNNING) |
| 41 | +{ |
| 42 | + add_row(std::make_unique<StallBuyerRow>(0, "first")); |
| 43 | + add_row(std::make_unique<StallBuyerRow>(1, "second")); |
| 44 | + add_row(std::make_unique<StallBuyerRow>(2, "third")); |
| 45 | + add_row(std::make_unique<StallBuyerRow>(3, "fourth")); |
| 46 | + add_row(std::make_unique<StallBuyerRow>(4, "fifth")); |
| 47 | + add_row(std::make_unique<StallBuyerRow>(5, "sixth")); |
| 48 | + add_row(std::make_unique<StallBuyerRow>(6, "seventh")); |
| 49 | + |
| 50 | + finish_construction(); |
| 51 | +} |
| 52 | +std::vector<std::string> StallBuyerTable::make_header() const{ |
| 53 | + std::vector<std::string> ret{ |
| 54 | + "Item", |
| 55 | + "Quantity" |
| 56 | + }; |
| 57 | + return ret; |
| 58 | +} |
| 59 | + |
| 60 | + |
27 | 61 | StallBuyer_Descriptor::StallBuyer_Descriptor() |
28 | 62 | : SingleSwitchProgramDescriptor( |
29 | 63 | "PokemonLZA:StallBuyer", |
@@ -55,34 +89,14 @@ std::unique_ptr<StatsTracker> StallBuyer_Descriptor::make_stats() const{ |
55 | 89 |
|
56 | 90 |
|
57 | 91 | StallBuyer::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) |
| 92 | + : GO_HOME_WHEN_DONE(false) |
78 | 93 | , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) |
79 | 94 | , NOTIFICATIONS({ |
80 | 95 | &NOTIFICATION_STATUS_UPDATE, |
81 | 96 | &NOTIFICATION_PROGRAM_FINISH, |
82 | 97 | &NOTIFICATION_ERROR_FATAL, |
83 | 98 | }) |
84 | 99 | { |
85 | | - PA_ADD_OPTION(ITEM_POSITION); |
86 | 100 | PA_ADD_OPTION(NUM_PURCHASE); |
87 | 101 | PA_ADD_OPTION(GO_HOME_WHEN_DONE); |
88 | 102 | PA_ADD_OPTION(NOTIFICATIONS); |
@@ -146,88 +160,95 @@ void StallBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerConte |
146 | 160 | StallBuyer_Descriptor::Stats& stats = env.current_stats<StallBuyer_Descriptor::Stats>(); |
147 | 161 | assert_16_9_720p_min(env.logger(), env.console); |
148 | 162 |
|
149 | | - while (true) { |
150 | | - context.wait_for_all_requests(); |
| 163 | + for (StaticTableRow* row : NUM_PURCHASE.table()){ |
| 164 | + StallBuyerRow& stall_buyer_row = static_cast<StallBuyerRow&>(*row); |
151 | 165 |
|
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()); |
| 166 | + for (int purchases = 0; purchases < stall_buyer_row.quantity; /* do not increment, it will be done in the loop*/){ |
| 167 | + context.wait_for_all_requests(); |
169 | 168 |
|
170 | | - int ret = wait_until( |
171 | | - env.console, context, |
172 | | - 30000ms, |
173 | | - { |
174 | | - buttonA, |
175 | | - select, |
176 | | - confirm, |
177 | | - dialog, |
178 | | - } |
| 169 | + ButtonWatcher buttonA( |
| 170 | + COLOR_RED, |
| 171 | + ButtonType::ButtonA, |
| 172 | + {0.1, 0.1, 0.8, 0.8}, |
| 173 | + &env.console.overlay() |
179 | 174 | ); |
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; |
| 175 | + SelectionArrowWatcher select( |
| 176 | + COLOR_YELLOW, &env.console.overlay(), |
| 177 | + SelectionArrowType::RIGHT, |
| 178 | + {0.715, 0.165, 0.045, 0.440} |
| 179 | + ); |
| 180 | + SelectionArrowWatcher confirm( |
| 181 | + COLOR_YELLOW, &env.console.overlay(), |
| 182 | + SelectionArrowType::RIGHT, |
| 183 | + {0.665, 0.600, 0.145, 0.080} |
| 184 | + ); |
| 185 | + FlatWhiteDialogWatcher dialog(COLOR_RED, &env.console.overlay()); |
| 186 | + |
| 187 | + int ret = wait_until( |
| 188 | + env.console, context, |
| 189 | + 30000ms, |
| 190 | + { |
| 191 | + buttonA, |
| 192 | + select, |
| 193 | + confirm, |
| 194 | + dialog, |
| 195 | + } |
| 196 | + ); |
| 197 | + context.wait_for(100ms); |
| 198 | + |
| 199 | + switch (ret){ |
| 200 | + case 0: |
| 201 | + env.log("Detected A button."); |
| 202 | + pbf_press_button(context, BUTTON_A, 160ms, 80ms); |
| 203 | + continue; |
| 204 | + |
| 205 | + case 1: |
| 206 | + { |
| 207 | + env.log("Detected item selection screen."); |
| 208 | + int stall_amount_item = detect_stall_amount_item(env, stats); |
| 209 | + env.log("Detected stall with " + std::to_string(stall_amount_item) + " items to sell."); |
| 210 | + auto [direction, presses] = compute_needed_inputs(stall_buyer_row.index, stall_amount_item); |
| 211 | + for (int i = 0; i < presses; i++){ |
| 212 | + pbf_press_dpad(context, direction, 160ms, 80ms); |
| 213 | + } |
| 214 | + pbf_press_button(context, BUTTON_A, 160ms, 80ms); |
| 215 | + continue; |
212 | 216 | } |
| 217 | + case 2: |
| 218 | + env.log("Detected purchase confirm screen."); |
| 219 | + pbf_press_button(context, BUTTON_A, 160ms, 80ms); |
| 220 | + stats.purchases++; |
| 221 | + purchases++; |
| 222 | + env.update_stats(); |
213 | 223 |
|
214 | | - continue; |
| 224 | + if (purchases == stall_buyer_row.quantity){ |
| 225 | + std::stringstream ss; |
| 226 | + ss << "Purchased " << stall_buyer_row.quantity << " of " << stall_buyer_row.ordinal << " item."; |
| 227 | + send_program_status_notification(env, NOTIFICATION_STATUS_UPDATE, ss.str()); |
| 228 | + break; |
| 229 | + } |
215 | 230 |
|
216 | | - case 3: |
217 | | - env.log("Detected white dialog."); |
218 | | - pbf_press_button(context, BUTTON_A, 160ms, 80ms); |
219 | | - continue; |
| 231 | + continue; |
220 | 232 |
|
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 | | - ); |
| 233 | + case 3: |
| 234 | + env.log("Detected white dialog."); |
| 235 | + pbf_press_button(context, BUTTON_A, 160ms, 80ms); |
| 236 | + continue; |
| 237 | + |
| 238 | + default: |
| 239 | + stats.errors++; |
| 240 | + env.update_stats(); |
| 241 | + OperationFailedException::fire( |
| 242 | + ErrorReport::SEND_ERROR_REPORT, |
| 243 | + "No recognized state after 30 seconds.", |
| 244 | + env.console |
| 245 | + ); |
| 246 | + } |
229 | 247 | } |
230 | 248 | } |
| 249 | + // intentionally don't leave the purchase menu to not get attacked |
| 250 | + GO_HOME_WHEN_DONE.run_end_of_program(context); |
| 251 | + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); |
231 | 252 | } |
232 | 253 |
|
233 | 254 |
|
|
0 commit comments