Skip to content

Commit 3232815

Browse files
authored
FRLG Prize Corner: Redeem multiple prizes per reset (#1147)
* update prize select detector boxes * support multiple redemptions for prize corner * block some invalid redemption options * fixes
1 parent 57b6323 commit 3232815

3 files changed

Lines changed: 107 additions & 28 deletions

File tree

SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_PrizeSelectDetector.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ namespace NintendoSwitch{
1919
namespace PokemonFRLG{
2020

2121
PrizeSelectDetector::PrizeSelectDetector(Color color)
22-
: m_right_box(0.884, 0.749115, 0.016, 0.1755)
23-
, m_top_box(0.1, 0.737692, 0.798769, 0.00519231)
24-
, m_bottom_box(0.102462, 0.925654, 0.793846, 0.00830769)
22+
: m_right_box(0.923385, 0.748077, 0.00615385, 0.204577)
23+
, m_top_box(0.0704615, 0.741846, 0.859077, 0.00623077)
24+
, m_bottom_box(0.0716923, 0.943308, 0.851692, 0.00934615)
2525
, m_selection_box(0.705538, 0.528962, 0.212923, 0.0602308)
2626
{}
2727
void PrizeSelectDetector::make_overlays(VideoOverlaySet& items) const{

SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,17 @@ PrizeCornerReset_Descriptor::PrizeCornerReset_Descriptor()
3838

3939
struct PrizeCornerReset_Descriptor::Stats : public StatsTracker{
4040
Stats()
41-
: resets(m_stats["Resets"])
41+
: prizes(m_stats["Prizes"])
42+
, resets(m_stats["Resets"])
4243
, shinies(m_stats["Shinies"])
4344
, errors(m_stats["Errors"])
4445
{
46+
m_display_order.emplace_back("Prizes");
4547
m_display_order.emplace_back("Resets");
4648
m_display_order.emplace_back("Shinies");
4749
m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO);
4850
}
51+
std::atomic<uint64_t>& prizes;
4952
std::atomic<uint64_t>& resets;
5053
std::atomic<uint64_t>& shinies;
5154
std::atomic<uint64_t>& errors;
@@ -54,6 +57,11 @@ std::unique_ptr<StatsTracker> PrizeCornerReset_Descriptor::make_stats() const{
5457
return std::unique_ptr<StatsTracker>(new Stats());
5558
}
5659

60+
PrizeCornerReset::~PrizeCornerReset(){
61+
SLOT.remove_listener(*this);
62+
NUM_REDEEM.remove_listener(*this);
63+
}
64+
5765
PrizeCornerReset::PrizeCornerReset()
5866
: SLOT(
5967
"<b>Slot:</b><br>Position of the prize in the selection dialog.",
@@ -67,6 +75,15 @@ PrizeCornerReset::PrizeCornerReset()
6775
LockMode::LOCK_WHILE_RUNNING,
6876
0
6977
)
78+
, NUM_REDEEM(
79+
"<b>Number of redemptions:</b><br>How many times to redeem a prize per reset. "
80+
"Make sure your party has enough room, and that you have enough coins for multiple redemptions."
81+
"<br> ex. When buying 3 Dratini (2,800 * 3 = 8,400 coins) in FireRed, have a party of 3, with 3 open slots. "
82+
"This cannot be done in LeafGreen, as Dratini costs more, so the max there would be 2 Dratini (4,600 * 2 = 9,200 coins) and your party size would be 4, with 2 open slots.",
83+
LockMode::LOCK_WHILE_RUNNING,
84+
1, 1, 5
85+
)
86+
, WARNING("")
7087
, TAKE_VIDEO("<b>Take Video:</b><br>Record a video when the shiny is found.", LockMode::UNLOCK_WHILE_RUNNING, true)
7188
, GO_HOME_WHEN_DONE(true)
7289
, NOTIFICATION_SHINY(
@@ -82,9 +99,38 @@ PrizeCornerReset::PrizeCornerReset()
8299
})
83100
{
84101
PA_ADD_OPTION(SLOT);
102+
PA_ADD_OPTION(NUM_REDEEM);
103+
PA_ADD_OPTION(WARNING);
85104
PA_ADD_OPTION(TAKE_VIDEO);
86105
PA_ADD_OPTION(GO_HOME_WHEN_DONE);
87106
PA_ADD_OPTION(NOTIFICATIONS);
107+
108+
PrizeCornerReset::on_config_value_changed(this);
109+
SLOT.add_listener(*this);
110+
NUM_REDEEM.add_listener(*this);
111+
WARNING.set_visibility(ConfigOptionState::HIDDEN);
112+
}
113+
114+
void PrizeCornerReset::on_config_value_changed(void* object){
115+
std::string error = check_amount_redeemed((uint16_t)SLOT.current_value(), NUM_REDEEM);
116+
117+
if (error.empty()){
118+
WARNING.set_visibility(ConfigOptionState::HIDDEN);
119+
}else{
120+
WARNING.set_text("<font color=\"red\">" + error + "</font>");
121+
WARNING.set_visibility(ConfigOptionState::ENABLED);
122+
}
123+
}
124+
125+
std::string PrizeCornerReset::check_amount_redeemed(uint16_t slot_num, uint32_t redeem_num) const{
126+
if (slot_num == 4 && redeem_num != 1) { //Only 1 Porygon in both FR and LG
127+
return "Error: Cannot redeem more than 1 Porygon per reset due to coin case limit.";
128+
} else if (slot_num == 3 && redeem_num > 2) { //2 Dratini LG or 1 Scyther FR
129+
return "Error: Maximum redemption of 2 Dratini in LG or 1 Scyther in FR per reset due to coin case limit.";
130+
} else if (slot_num == 2 && redeem_num > 3) { //Max of 3 for both games
131+
return "Error: Maximum redemption of 3 Dratini/Pinsir due to coin case limit.";
132+
} //Abra/Clefairy will run out of party space first before coins.
133+
return "";
88134
}
89135

90136
void PrizeCornerReset::obtain_prize(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
@@ -134,16 +180,25 @@ void PrizeCornerReset::obtain_prize(SingleSwitchProgramEnvironment& env, ProCont
134180

135181
//Select prize
136182
pbf_press_button(context, BUTTON_A, 320ms, 640ms);
137-
pbf_press_button(context, BUTTON_A, 320ms, 640ms);
183+
pbf_press_button(context, BUTTON_A, 320ms, 640ms); //Yes to redeem
138184

139185
//Exit dialog
140186
pbf_mash_button(context, BUTTON_B, 2000ms);
141187
context.wait_for_all_requests();
188+
189+
stats.prizes++;
190+
env.update_stats();
142191
}
143192

144193
void PrizeCornerReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
145194
PrizeCornerReset_Descriptor::Stats& stats = env.current_stats<PrizeCornerReset_Descriptor::Stats>();
146195

196+
std::string amount_check;
197+
amount_check = check_amount_redeemed((uint16_t)SLOT.current_value(), NUM_REDEEM);
198+
if (amount_check != "") {
199+
throw UserSetupError(env.console, "Invalid number of redemptions for the selected prize. Please check your selected options.");
200+
}
201+
147202
home_black_border_check(env.console, context);
148203

149204
/*
@@ -155,34 +210,51 @@ void PrizeCornerReset::program(SingleSwitchProgramEnvironment& env, ProControlle
155210
bool shiny_found = false;
156211

157212
while (!shiny_found){
158-
obtain_prize(env, context);
213+
for (uint16_t i = 0; i < NUM_REDEEM; i++){
214+
env.log("Obtaining prize.");
215+
obtain_prize(env, context);
216+
}
159217
stats.errors += open_slot_six(env.console, context);
160218
env.update_stats();
161219

162-
VideoSnapshot screen = env.console.video().snapshot();
220+
env.log("Checking prizes.");
221+
for (uint16_t i = 0; i < NUM_REDEEM; i++){
222+
VideoSnapshot screen = env.console.video().snapshot();
163223

164-
ShinySymbolDetector shiny_checker(COLOR_YELLOW);
165-
shiny_found = shiny_checker.read(env.console.logger(), screen);
224+
ShinySymbolDetector shiny_checker(COLOR_YELLOW);
225+
bool check = shiny_checker.read(env.console.logger(), screen);
166226

167-
if (shiny_found){
168-
env.log("Shiny found!");
169-
stats.shinies++;
170-
env.update_stats();
171-
send_program_notification(
172-
env,
173-
NOTIFICATION_SHINY,
174-
COLOR_YELLOW,
175-
"Shiny found!",
176-
{}, "",
177-
screen,
178-
true
179-
);
180-
if (TAKE_VIDEO){
181-
pbf_press_button(context, BUTTON_CAPTURE, 2000ms, 0ms);
227+
if (check){
228+
env.log("Shiny found!");
229+
stats.shinies++;
230+
env.update_stats();
231+
send_program_notification(
232+
env,
233+
NOTIFICATION_SHINY,
234+
COLOR_YELLOW,
235+
"Shiny found!",
236+
{}, "",
237+
screen,
238+
true
239+
);
240+
if (TAKE_VIDEO){
241+
pbf_press_button(context, BUTTON_CAPTURE, 2000ms, 0ms);
242+
}
243+
shiny_found = true;
244+
}else{
245+
env.log("Prize is not shiny.");
182246
}
183-
break;
184-
}else{
185-
env.log("Prize is not shiny.");
247+
248+
if(i < NUM_REDEEM - 1) {
249+
//Check the next pokemon
250+
pbf_press_dpad(context, DPAD_UP, 320ms, 320ms);
251+
pbf_wait(context, 1000ms);
252+
context.wait_for_all_requests();
253+
}
254+
}
255+
256+
if (!shiny_found){
257+
env.log("Out of Pokemon to check.");
186258
env.log("Soft resetting.");
187259
send_program_status_notification(
188260
env, NOTIFICATION_STATUS_UPDATE,

SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ class PrizeCornerReset_Descriptor : public SingleSwitchProgramDescriptor{
2525
virtual std::unique_ptr<StatsTracker> make_stats() const override;
2626
};
2727

28-
class PrizeCornerReset : public SingleSwitchProgramInstance{
28+
class PrizeCornerReset : public SingleSwitchProgramInstance, private ConfigOption::Listener{
2929
public:
30+
~PrizeCornerReset();
3031
PrizeCornerReset();
3132
virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override;
3233

@@ -39,6 +40,8 @@ class PrizeCornerReset : public SingleSwitchProgramInstance{
3940
void obtain_prize(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
4041

4142
IntegerEnumDropdownOption SLOT;
43+
SimpleIntegerOption<uint32_t> NUM_REDEEM;
44+
StaticTextOption WARNING;
4245

4346
BooleanCheckBoxOption TAKE_VIDEO;
4447

@@ -47,6 +50,10 @@ class PrizeCornerReset : public SingleSwitchProgramInstance{
4750
EventNotificationOption NOTIFICATION_SHINY;
4851
EventNotificationOption NOTIFICATION_STATUS_UPDATE;
4952
EventNotificationsOption NOTIFICATIONS;
53+
54+
private:
55+
virtual void on_config_value_changed(void* object) override;
56+
std::string check_amount_redeemed(uint16_t slot_num, uint32_t redeem_num) const;
5057
};
5158

5259
}

0 commit comments

Comments
 (0)