Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ab9f99e
Merge remote-tracking branch 'origin/frlg/rng-target-search' into frl…
dayfine Nov 2, 2026
6f65a95
FRLG AutoStory: plan the Starter RNG attempts from SID Helper's candi…
dayfine Nov 2, 2026
143a931
FRLG AutoStory: move the RNG plan options into their own group
dayfine Nov 2, 2026
1c1c15a
FRLG AutoStory: read the TID off the Trainer Card; log RngPlan lines
dayfine Nov 2, 2026
ee189ed
FRLG AutoStory: read the TID and plan inside the Starter RNG stop
dayfine Nov 2, 2026
9355ad6
FRLG AutoStory: plan Starter RNG before 00.04 saves under the ball
dayfine Nov 2, 2026
2cbaca5
Merge branch 'frlg/autostory-starter-rng' into frlg/autostory-rng-plan
dayfine Nov 2, 2026
a14182e
Merge branch 'frlg/autostory-starter-rng' into frlg/autostory-rng-plan
dayfine Nov 3, 2026
73573d0
FRLG AutoStory: RNG plan result lines with the try, the Pokemon and t…
dayfine Nov 3, 2026
df47081
FRLG AutoStory: only a shiny on the target narrows the SIDs; misses m…
dayfine Nov 3, 2026
16cacaf
FRLG AutoStory: plan only seeds Starter RNG can wait for on the title…
dayfine Nov 3, 2026
282394f
FRLG AutoStory: an RNG plan attempt that can't run moves on
dayfine Nov 3, 2026
abb34b2
FRLG AutoStory: plan Starter RNG targets with room in both waits
dayfine Nov 3, 2026
4c6d0df
FRLG: move the RNG plan into RngManipulation modules
dayfine Nov 3, 2026
681b5ff
Merge remote-tracking branch 'origin/frlg/autostory-starter-rng' into…
dayfine Nov 3, 2026
e824506
Merge frlg/rng-plan-base into frlg/autostory-rng-plan
dayfine Nov 3, 2026
a4d6123
Merge branch 'frlg/rng-plan-base' into frlg/autostory-rng-plan
dayfine Nov 3, 2026
f337351
Merge branch 'frlg/rng-plan-base' into frlg/autostory-rng-plan
dayfine Nov 3, 2026
fbcd907
Merge branch 'frlg/rng-plan-base' into frlg/autostory-rng-plan
dayfine Nov 3, 2026
23099b6
Merge branch 'frlg/rng-plan-base' into frlg/autostory-rng-plan
dayfine Nov 3, 2026
29e1cac
Merge remote-tracking branch 'origin/frlg/rng-plan-base' into HEAD
dayfine Nov 3, 2026
40ae6d5
Merge frlg/rng-plan-base into frlg/autostory-rng-plan
dayfine Nov 3, 2026
8e7c411
Merge frlg/rng-plan-base into frlg/autostory-rng-plan
dayfine Nov 3, 2026
0399aef
FRLG AutoStory: keep this console's RNG calibration in the RNG plan
dayfine Nov 3, 2026
c67c899
Merge frlg/rng-plan-base into frlg/autostory-rng-plan
dayfine Nov 3, 2026
4463fac
Merge frlg/rng-plan-base (96d61b8c9, #119 recovery) into frlg/autosto…
dayfine Nov 3, 2026
5652ca3
Merge frlg/rng-plan-base into frlg/autostory-rng-plan
dayfine Nov 3, 2026
398ba92
Merge remote-tracking branch 'origin/frlg/rng-plan-base' into HEAD
dayfine Nov 3, 2026
dc799bc
Merge remote-tracking branch 'origin/frlg/rng-plan-base' into HEAD
dayfine Nov 3, 2026
debd380
Merge frlg/rng-plan-base into frlg/autostory-rng-plan
dayfine Nov 3, 2026
d14910c
Merge frlg/rng-plan-base (battle loop: end on the overworld after RUN…
dayfine Nov 3, 2026
0728391
Merge frlg/rng-plan-base (a disabled move, and a move list that never…
dayfine Nov 3, 2026
52a6bd9
Merge frlg/autostory-mvp (matchup attacks in every battle) into frlg/…
dayfine Nov 3, 2026
6fcf858
Merge frlg/autostory-mvp (HP bar share, nurse for yellow HP, move pro…
dayfine Nov 3, 2026
a3dbfdd
Merge frlg/rng-plan-base (dc5e9edcb: verify mode from #122) into frlg…
dayfine Nov 3, 2026
2884d20
Merge frlg/rng-plan-base (953b9a36f: battle sim from #120) into frlg/…
dayfine Nov 3, 2026
ea25e23
Merge frlg/rng-plan-base into frlg/autostory-rng-plan
dayfine Nov 3, 2026
258e78f
Merge frlg/rng-plan-base (TM moves in the summary reader) into frlg/a…
dayfine Nov 3, 2026
215b8e0
Merge frlg/rng-plan-base (bag rows below an item the reader can't nam…
dayfine Nov 3, 2026
e767ab1
FRLG AutoStory: program() within code health's 50 lines
dayfine Nov 3, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@
*/

#include <format>
#include <functional>
#include <optional>
#include "Common/Cpp/Exceptions.h"
#include "CommonFramework/Exceptions/OperationFailedException.h"
#include "CommonFramework/Exceptions/OperationFailedExceptionWithScreenshot.h"
#include "CommonFramework/Notifications/ProgramNotifications.h"
#include "CommonFramework/VideoPipeline/VideoOverlay.h"
#include "Pokemon/AutoStory/Pokemon_AutoStoryStats.h"
#include "Pokemon/AutoStory/Pokemon_StorySegments.h"
#include "Pokemon/Pokemon_Strings.h"
#include "VideoGames/PokemonFRLG/PokemonFRLG_Navigation.h"
#include "VideoGames/PokemonFRLG/Programs/PokemonFRLG_NamingScreenKeyboard.h"
#include "VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.h"
#include "VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngPlanLog.h"
#include "PokemonFRLG_AutoStoryEvents.h"
#include "PokemonFRLG_AutoStoryTable.h"
#include "PokemonFRLG_AutoStory.h"
Expand Down Expand Up @@ -53,7 +57,7 @@

}

AutoStory::AutoStory()

Check warning on line 60 in SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/AutoStory/PokemonFRLG_AutoStory.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::NintendoSwitch::PokemonFRLG::AutoStory::AutoStory(): function_lines=45 (advisory above 25, limit 50)
: RUN(checkpoint_database())
, NEW_GAME_FROM_TITLE(
"<b>New game from the title screen:</b><br>The game is on its title screen with no save data. "
Expand All @@ -80,7 +84,7 @@
, STOP_FOR_STARTER_RNG(
"<b>Stop for Starter RNG:</b><br>Off by default. At the end of 00.04, save south of the table facing Squirtle's ball "
"and stop, where the Starter RNG program starts. After Starter RNG, save right after the rival's battle without moving "
"and start AutoStory at 00.05.<br>"
"and start AutoStory at 00.05, listing the targets to try from the RNG plan.<br>"
"With this off, 00.04 and 00.04b run as one checkpoint, as before.",
LockMode::LOCK_WHILE_RUNNING,
false
Expand All @@ -95,6 +99,7 @@
PA_ADD_OPTION(PLAYER_NAME);
PA_ADD_OPTION(STOP_FOR_SID_HELPER);
PA_ADD_OPTION(STOP_FOR_STARTER_RNG);
PA_ADD_OPTION(RNG_PLAN);
PA_ADD_OPTION(NOTIFICATIONS);
}

Expand Down Expand Up @@ -273,14 +278,108 @@
}

// The run stopped for an RNG program, as its option asked: the user's
// turn, with what to do next.
static void stop_for_handoff(SingleSwitchProgramEnvironment& env, EventNotificationOption& notification, const std::string& message){
// turn, with what to do next, and the lines that go with it (the RNG
// plan's attempts).
static void stop_for_handoff(
SingleSwitchProgramEnvironment& env, EventNotificationOption& notification,
const std::string& message, const std::vector<std::string>& lines = {}
){
env.console.log(message, COLOR_BLUE);
env.console.overlay().add_log(message);
send_program_finished_notification(env, notification, message);
std::string full = message;
for (const std::string& line : lines){
env.console.log(line, COLOR_BLUE);
env.console.overlay().add_log(line);
full += "\n" + line;
}
send_program_finished_notification(env, notification, full);
}

// SID Helper's set_sid_from_name_screen(), still file-static in
// PokemonFRLG_SidHelper.cpp, could run here instead of stopping.
static void stop_for_sid_helper(
SingleSwitchProgramEnvironment& env, ProControllerContext& context,
EventNotificationOption& notification, const std::string& player_name
){
play_new_game_to_player_name(env.console, context, player_name);
stop_for_handoff(env, notification,
"Stopped for SID Helper: the player's name is typed and OK is not pressed. Run SID Helper now, "
"enter its Target Advances and # Candidate SIDs in the RNG plan, then start AutoStory at 00.01b."
);
}

// The TID for the Starter RNG plan: the RNG plan's, or read off the
// Trainer Card (START, the player's entry), which is then closed again.
// Before the first Pokemon, as under the ball, the player's entry is second.
static uint16_t starter_rng_tid(
SingleSwitchProgramEnvironment& env, ProControllerContext& context,
const StarterRngPlanOptions& options, Language language
){
std::optional<uint16_t> tid = options.tid_override(env.console);
if (tid){
env.console.log(rng_log_tid(*tid, false));
return *tid;
}
navigate_to_trainer_card(env.console, context);
tid = try_read_trainer_card_tid(env.console, context, language);
if (!tid){
OperationFailedExceptionWithScreenshot::fire(
ErrorReportMode::NO_ERROR_REPORT,
"Couldn't read the TID on the Trainer Card, so there is no Starter RNG plan. Nothing was saved: "
"type the TID into the RNG plan's TID option and start AutoStory at 00.04 again.",
env.console
);
}
close_trainer_card(env.console, context);
env.console.log(rng_log_tid(*tid, true));
return *tid;
}

// The Starter RNG attempts, as lines for the stop. Run under the ball before
// 00.04 saves there, so a failure ends the run with 00.03's save kept.
static std::vector<std::string> starter_rng_attempts(
SingleSwitchProgramEnvironment& env, ProControllerContext& context,
const StarterRngPlanOptions& options, Language language
){
const uint16_t tid = starter_rng_tid(env, context, options, language);
try{
return rng_attempt_lines(options.plan(env.console, language, tid));
}catch (UserSetupError& e){
throw UserSetupError(env.console, e.message() + " Nothing was saved: fix the RNG plan and start AutoStory at 00.04 again.");
}
}

// Has the last checkpoint of `segment` call `before` just before it saves.
static void before_last_save(Pokemon::Segment& segment, std::function<void()> before){
Pokemon::CheckpointSpec& spec = segment.checkpoints.back();
spec.build = [build = std::move(spec.build), before]() mutable{
Pokemon::Checkpoint checkpoint = build();
checkpoint.save = [save = std::move(checkpoint.save), before](Pokemon::RunState& state) mutable{
before();
return save(state);
};
return checkpoint;
};
}

// Starter RNG runs its own reset and calibration loop in its program(), so
// the attempts are the user's to run until that loop can be called from here.
// TODO: LeafGreen and Bulbasaur/Charmander, with AutoStory's starter choice.
static void stop_for_starter_rng(
SingleSwitchProgramEnvironment& env, EventNotificationOption& notification,
const std::string& sound, const std::vector<std::string>& attempts
){
stop_for_handoff(env, notification,
"Stopped for Starter RNG: saved south of the table, facing Squirtle's ball. "
"Run Starter RNG (Squirtle, FireRed, Sound " + sound + ") with attempt 1's Target Seed and Advances. "
"If it hits the target and it isn't shiny, load this save and run the next attempt. If it is shiny, "
"its SID is the one listed (or one of them): enter it as the RNG plan's SID. "
"Then save right after the rival's battle, without moving, and start AutoStory at 00.05.",
attempts
);
}

void AutoStory::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){

Check warning on line 382 in SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/AutoStory/PokemonFRLG_AutoStory.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::NintendoSwitch::PokemonFRLG::AutoStory::program( SingleSwitchProgramEnvironment & env , ProControllerContext & context): function_lines=50 (advisory above 25, limit 50)
if (const std::optional<Pokemon::VerifyPolicy> verify = RUN.VERIFY.policy()){
verify_story_checkpoint(env, context, RUN, *verify, NOTIFICATION_PROGRAM_FINISH);
return;
Expand All @@ -294,24 +393,28 @@
checkpoints, first, last, StoryHandoffs{.sid_helper = STOP_FOR_SID_HELPER, .starter_rng = STOP_FOR_STARTER_RNG}
);
if (plan.stop == StoryHandoff::SID_HELPER){
play_new_game_to_player_name(env.console, context, player_name);
stop_for_handoff(env, NOTIFICATION_PROGRAM_FINISH,
"Stopped for SID Helper: the player's name is typed and OK is not pressed. "
"Run SID Helper now, then start AutoStory at 00.01b."
);
stop_for_sid_helper(env, context, NOTIFICATION_PROGRAM_FINISH, player_name);
return;
}
if (plan.stop == StoryHandoff::STARTER_RNG){
// The options' text, before anything is pressed.
RNG_PLAN.check(env.console);
}

StoryEnv story{env.console, context, checkpoints[first].supplies, checkpoints[first].supplies, false, RUN.checks()};
story.player_name = player_name;
prepare_start(env, context, checkpoints, first, story);

StoryExecutor executor(story);
executor.set_checks(RUN.checks());
executor.set_language(RUN.language());
Pokemon::Segment segment = make_story_segment(plan.rows, executor);
std::vector<std::string> rng_attempts;
if (plan.stop == StoryHandoff::STARTER_RNG){
before_last_save(segment, [&]{
rng_attempts = starter_rng_attempts(env, context, RNG_PLAN, RUN.language());
});
}
Pokemon::SegmentHooks hooks = story_hooks(env, story);

Pokemon::RunState state;
const Pokemon::SegmentReport report = run_segment(
state, segment, Pokemon::SegmentRange{plan.rows.front().id, plan.rows.back().id}, RUN.policy(), hooks
Expand All @@ -320,10 +423,7 @@
env.update_stats();
throw_if_failed(report);
if (plan.stop == StoryHandoff::STARTER_RNG){
stop_for_handoff(env, NOTIFICATION_PROGRAM_FINISH,
"Stopped for Starter RNG: saved south of the table, facing Squirtle's ball. Run Starter RNG now. "
"Then save right after the rival's battle, without moving, and start AutoStory at 00.05."
);
stop_for_starter_rng(env, NOTIFICATION_PROGRAM_FINISH, RNG_PLAN.sound_name(), rng_attempts);
return;
}
env.console.log("AutoStory: done through " + plan.rows.back().id + ".");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h"
#include "Pokemon/Options/Pokemon_StoryRunOptions.h"
#include "PokemonFRLG_AutoStoryActions.h"
#include "PokemonFRLG_AutoStoryRngOptions.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
Expand Down Expand Up @@ -50,6 +51,7 @@ class AutoStory : public SingleSwitchProgramInstance{
// The RNG programs the run stops for (StoryHandoff).
BooleanCheckBoxOption STOP_FOR_SID_HELPER;
BooleanCheckBoxOption STOP_FOR_STARTER_RNG;
StarterRngPlanOptions RNG_PLAN;
EventNotificationsOption NOTIFICATIONS;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/* AutoStory RNG Options
*
* From: https://github.com/PokemonAutomation/
*
*/

#include <cmath>
#include "Common/Cpp/Exceptions.h"
#include "VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngPlanInputs.h"
#include "VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngPlanLog.h"
#include "PokemonFRLG_AutoStoryRngOptions.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonFRLG{


RngStopCalibrationOptions::RngStopCalibrationOptions()
: GroupOption("RNG Calibration (this console)", LockMode::LOCK_WHILE_RUNNING)
, SEED(
"<b>Seed Calibration (ms):</b><br>As in Starter RNG's RNG Calibration. Each RNG stop starts from it, "
"and saves what it learns after every reset.",
LockMode::LOCK_WHILE_RUNNING, 0
)
, CSF(
"<b>Continue Screen Frames Calibration:</b><br>As in Starter RNG's RNG Calibration, kept as the seed's is.",
LockMode::LOCK_WHILE_RUNNING, 0.0
)
{
PA_ADD_OPTION(SEED);
PA_ADD_OPTION(CSF);
}

RngCalibrations RngStopCalibrationOptions::calibration(const FloatingPointOption& ingame) const{
return RngCalibrations{
.seed_offset = double(int64_t(SEED)) / FRLG_FRAME_DURATION,
.csf_offset = CSF,
.ingame_offset = ingame,
};
}

void RngStopCalibrationOptions::save(const RngCalibrations& calibration, FloatingPointOption& ingame){
// In ms, as RngCalibrationDisplay::set_calibrations() shows it.
SEED.set(int64_t(std::round(calibration.seed_offset * FRLG_FRAME_DURATION)));
CSF.set(calibration.csf_offset);
ingame.set(calibration.ingame_offset);
}


StarterRngPlanOptions::StarterRngPlanOptions()

Check warning on line 50 in SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/AutoStory/PokemonFRLG_AutoStoryRngOptions.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::NintendoSwitch::PokemonFRLG::StarterRngPlanOptions::StarterRngPlanOptions(): function_lines=47 (advisory above 25, limit 50)
: GroupOption("RNG Plan (Stop for Starter RNG)", LockMode::LOCK_WHILE_RUNNING)
, SOUND(
"<b>Sound:</b><br>The in-game sound setting, as in Starter RNG. It picks the seeds.",
{{SoundSetting::Mono, "mono", "Mono"}, {SoundSetting::Stereo, "stereo", "Stereo"}},
LockMode::LOCK_WHILE_RUNNING, SoundSetting::Mono
)
, TID(
false, "<b>TID:</b><br>Empty: read from the Trainer Card at the stop. Set it only if that read fails.",
LockMode::LOCK_WHILE_RUNNING, "", ""
)
, SID(
false,
"<b>SID:</b><br>The SID, once known. Empty: plan for the SIDs SID Helper lists, from the two options below.",
LockMode::LOCK_WHILE_RUNNING, "", ""
)
, SID_HELPER_ADVANCES(
"<b>SID Helper's Target Advances:</b><br>As set in SID Helper.",
LockMode::LOCK_WHILE_RUNNING, 3001, 2275
)
, SID_HELPER_CANDIDATES(
"<b>SID Helper's # Candidate SIDs:</b><br>As set in SID Helper. The middle one counts as the likeliest.",
LockMode::LOCK_WHILE_RUNNING, 5, 1, 11
)
, SHINY_REQUIRED(
"<b>Shiny required:</b><br>Only targets shiny for a SID candidate, tried until one comes out shiny. "
"Off: the quickest target that passes the natures and IVs.",
LockMode::LOCK_WHILE_RUNNING, true
)
, NATURES(false, "<b>Natures:</b><br>Separated by commas. Empty: any nature.", LockMode::LOCK_WHILE_RUNNING, "", "Modest, Timid")
, MIN_IVS(
false, "<b>Minimum IVs:</b><br>HP/Atk/Def/SpA/SpD/Spe.",
LockMode::LOCK_WHILE_RUNNING, "0/0/0/0/0/0", "0/0/0/0/0/0"
)
, MAX_ATTEMPTS("<b>Max attempts:</b><br>The most targets to list.", LockMode::LOCK_WHILE_RUNNING, 5, 1, 20)
{
PA_ADD_OPTION(SOUND);
PA_ADD_OPTION(TID);
PA_ADD_OPTION(SID);
PA_ADD_OPTION(SID_HELPER_ADVANCES);
PA_ADD_OPTION(SID_HELPER_CANDIDATES);
PA_ADD_OPTION(SHINY_REQUIRED);
PA_ADD_OPTION(NATURES);
PA_ADD_OPTION(MIN_IVS);
PA_ADD_OPTION(MAX_ATTEMPTS);
PA_ADD_OPTION(CALIBRATION);
}

std::string StarterRngPlanOptions::sound_name() const{
return SOUND == SoundSetting::Stereo ? "Stereo" : "Mono";
}

std::optional<uint16_t> StarterRngPlanOptions::tid_override(Logger& logger) const{
const std::string text = TID;
if (text.empty()){
return std::nullopt;
}
const std::optional<uint16_t> tid = parse_trainer_id(text);
if (!tid){
throw UserSetupError(logger, "RNG plan: the TID must be a number from 0 to 65535, or empty.");
}
return tid;
}

std::optional<uint16_t> StarterRngPlanOptions::sid(Logger& logger) const{
const std::string text = SID;
if (text.empty()){
return std::nullopt;
}
const std::optional<uint16_t> sid = parse_trainer_id(text);
if (!sid){
throw UserSetupError(logger, "RNG plan: the SID must be a number from 0 to 65535, or empty.");
}
return sid;
}

RngTargetFilters StarterRngPlanOptions::filters(Logger& logger) const{
const std::optional<std::vector<Pokemon::AdvNature>> natures = parse_natures(NATURES);
if (!natures){
throw UserSetupError(logger, "RNG plan: natures must be nature names separated by commas, e.g. \"Modest, Timid\".");
}
const std::optional<Pokemon::AdvIVs> min_ivs = parse_ivs(MIN_IVS);
if (!min_ivs){
throw UserSetupError(logger, "RNG plan: minimum IVs must be six numbers from 0 to 31, e.g. \"0/0/0/31/0/31\".");
}
RngTargetFilters filters;
filters.natures = *natures;
filters.min_ivs = *min_ivs;
filters.shiny_required = SHINY_REQUIRED;
return filters;
}

void StarterRngPlanOptions::check(Logger& logger) const{
tid_override(logger);
sid(logger);
filters(logger);
}

std::vector<RngAttempt> StarterRngPlanOptions::plan(Logger& logger, Language language, uint16_t tid) const{

Check warning on line 148 in SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/AutoStory/PokemonFRLG_AutoStoryRngOptions.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::NintendoSwitch::PokemonFRLG::StarterRngPlanOptions::plan( Logger & logger , Language language , uint16_t tid) const: function_lines=30 (advisory above 25, limit 50)
// SID Helper's candidates are logged even with the SID known, to place
// it among them (rng_log_confirmed()).
const std::vector<SidCandidate> helper = sid_helper_candidates(tid, SID_HELPER_ADVANCES, SID_HELPER_CANDIDATES);
logger.log(rng_log_sid_helper(tid, SID_HELPER_ADVANCES, helper));
const std::optional<uint16_t> known = sid(logger);
if (known){
logger.log(rng_log_confirmed(tid, *known, helper));
}

const std::vector<RngAttempt> attempts = plan_starter_rng(StarterRngPlanInputs{
// TODO: LeafGreen, once AutoStory plays it.
.firered = true,
.language = language,
.sound = SOUND,
.tid = tid,
.candidates = known ? std::vector<SidCandidate>{{*known, 1}} : helper,
.filters = filters(logger),
.max_attempts = MAX_ATTEMPTS,
});
if (attempts.empty()){
throw UserSetupError(logger,
"RNG plan: no Starter RNG target passes the natures, minimum IVs and shiny options for TID " + std::to_string(tid) + "."
);
}
for (size_t c = 0; c < attempts.size(); c++){
logger.log(rng_log_attempt(c, attempts[c]));
}
return attempts;
}



}
}
}
Loading
Loading