Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ StateMachineAction run_state_iteration(
global_state,
runtime.console_settings[console_index],
battle_menu.dmaxed(),
battle_menu.cheer()
battle_menu.cheer(),
runtime.actions
);
case 5:
console.log("Current State: Catch Select");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class EndBattleDecider{
const PathStats& path_stats,
bool any_shiny, bool boss_is_shiny
) const = 0;

virtual bool stop_for_non_boss(const std::string& slug) const = 0;
};


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/* Max Lair Non-Boss Action
*
* From: https://github.com/PokemonAutomation/
*
*/

#include "Pokemon/Pokemon_Strings.h"
#include "Pokemon/Resources/Pokemon_PokemonNames.h"
#include "PokemonSwSh/Resources/PokemonSwSh_PokemonSprites.h"
#include "PokemonSwSh/Resources/PokemonSwSh_MaxLairDatabase.h"
#include "PokemonSwSh_MaxLair_Options_NonBossAction.h"

//#include <iostream>
//using std::cout;
//using std::endl;

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonSwSh{
namespace MaxLairInternal{




const EnumDropdownDatabase<NonBossAction>& NonBossAction_Database(){
static const EnumDropdownDatabase<NonBossAction> database({
{NonBossAction::IGNORE, "ignore", "Ignore"},
{NonBossAction::STOP_PROGRAM, "stop", "Stop program"},
});
return database;
}

static std::string format_display_name(const std::string& slug) {
static const std::vector<std::pair<std::string, std::string>> region_map = {

{"-alola", "Alolan "},
{"-galar", "Galarian "}
};

std::string base_slug = slug;
std::string prefix;

for (const auto& pair : region_map) {
size_t pos = slug.find(pair.first);
if (pos != std::string::npos) {
base_slug = slug.substr(0, pos);

prefix = pair.second;
break;
}
}

// Get base Pokemon name
std::string base_name = get_pokemon_name(base_slug).display_name();
return prefix + base_name;
}

NonBossActionRow::NonBossActionRow(std::string slug, const std::string& name_slug, const std::string& sprite_slug)
: StaticTableRow(slug)
, pokemon(
LockMode::UNLOCK_WHILE_RUNNING,
format_display_name(slug),
ALL_POKEMON_SPRITES().get_throw(sprite_slug).icon
)
, action(
NonBossAction_Database(),
LockMode::UNLOCK_WHILE_RUNNING,
NonBossAction::IGNORE
)
{
PA_ADD_STATIC(pokemon);
add_option(action, "Action");
}


NonBossActionTable::NonBossActionTable()
: StaticTableOption("<b>Other Pokemon Actions:</b>", LockMode::UNLOCK_WHILE_RUNNING)
{

// Limited mode: only display Alolan-Raichu and Alolan-Marowak by default. If the developer wants, it can also add the full list
const bool LIMITED_MODE = true;

if (LIMITED_MODE) {
std::vector<std::string> slugs = {"raichu-alola", "marowak-alola"};
for (const std::string& slug : slugs) {
const MaxLairSlugs& slugs_info = get_maxlair_slugs(slug);
const std::string& sprite_slug = *slugs_info.sprite_slugs.begin();
const std::string& name_slug = slugs_info.name_slug;
add_row(std::make_unique<NonBossActionRow>(slug, name_slug, sprite_slug));
}
} else {
std::set<std::string> added_grouped_base; // Track base slugs for grouped forms

for (const auto& item : all_rentals_by_dex()){
const std::string& full_slug = item.second;
const MaxLairSlugs& slugs = get_maxlair_slugs(full_slug);
const std::string& sprite_slug = *slugs.sprite_slugs.begin();
const std::string& name_slug = slugs.name_slug;

if (full_slug == name_slug) {
add_row(std::make_unique<NonBossActionRow>(full_slug, name_slug, sprite_slug));
continue;
}

// Check Alola and Galar variants
if (full_slug.size() > 6 && full_slug.substr(full_slug.size() - 6) == "-alola") {
add_row(std::make_unique<NonBossActionRow>(full_slug, name_slug, sprite_slug));
continue;
}
if (full_slug.size() > 6 && full_slug.substr(full_slug.size() - 6) == "-galar") {
add_row(std::make_unique<NonBossActionRow>(full_slug, name_slug, sprite_slug));
continue;
}

// All other pokemon with suffixes (e.g. Basculin) are grouped under one name

if (added_grouped_base.find(name_slug) == added_grouped_base.end()) {
add_row(std::make_unique<NonBossActionRow>(name_slug, name_slug, sprite_slug));
added_grouped_base.insert(name_slug);
}
}
}
finish_construction();
}

std::vector<std::string> NonBossActionTable::make_header() const{
std::vector<std::string> ret{
STRING_POKEMON,
"Action",
};
return ret;
}






}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Max Lair Boss Action
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_PokemonSwSh_MaxLair_Options_NonBossAction_H
#define PokemonAutomation_PokemonSwSh_MaxLair_Options_NonBossAction_H

#include "Common/Cpp/Options/EnumDropdownOption.h"
#include "Common/Cpp/Options/StaticTableOption.h"
#include "CommonFramework/Options/LabelCellOption.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonSwSh{
namespace MaxLairInternal{


enum class NonBossAction{
IGNORE,
STOP_PROGRAM,
};

class NonBossActionRow : public StaticTableRow{
public:
NonBossActionRow(std::string slug, const std::string& name_slug, const std::string& sprite_slug);

LabelCellOption pokemon;
EnumDropdownCell<NonBossAction> action;
};

class NonBossActionTable : public StaticTableOption{
public:
NonBossActionTable();
virtual std::vector<std::string> make_header() const;
};





}
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ MaxLairBossFinder::MaxLairBossFinder()

PA_ADD_OPTION(CONSOLES);
PA_ADD_OPTION(BOSS_LIST);
PA_ADD_OPTION(NON_BOSS_LIST);
PA_ADD_OPTION(HOSTING);

PA_ADD_OPTION(TOUCH_DATE_INTERVAL);
Expand Down Expand Up @@ -119,9 +120,10 @@ void MaxLairBossFinder::update_active_consoles(size_t switch_count){

class EndBattleDecider_BossFinder : public EndBattleDecider{
public:
EndBattleDecider_BossFinder(const Consoles& consoles, const BossActionTable& boss_list)
EndBattleDecider_BossFinder(const Consoles& consoles, const BossActionTable& boss_list, NonBossActionTable& non_boss_list)
: m_consoles(consoles)
, m_boss_list(boss_list)
, m_non_boss_list(non_boss_list)
{}
virtual const std::string& normal_ball(
size_t console_index
Expand Down Expand Up @@ -151,6 +153,28 @@ class EndBattleDecider_BossFinder : public EndBattleDecider{
}
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Invalid enum.");
}
virtual bool stop_for_non_boss(const std::string& slug) const override {
for (const StaticTableRow* row : m_non_boss_list.table()) {
const NonBossActionRow* nb_row = static_cast<const NonBossActionRow*>(row);

const std::string& row_slug = nb_row->slug();

// Exact match
if (slug == row_slug) {
return nb_row->action == NonBossAction::STOP_PROGRAM;
}

// If it has a specific variant, match any OCR slug that starts with it and has a hyphen (a form)
if (row_slug.find('-') == std::string::npos) {
if (slug.size() > row_slug.size() &&
slug.substr(0, row_slug.size()) == row_slug &&
slug[row_slug.size()] == '-') {
return nb_row->action == NonBossAction::STOP_PROGRAM;
}
}
}
return false;
}


private:
Expand All @@ -171,6 +195,7 @@ class EndBattleDecider_BossFinder : public EndBattleDecider{

const Consoles& m_consoles;
const BossActionTable& m_boss_list;
const NonBossActionTable& m_non_boss_list;

};

Expand All @@ -194,7 +219,7 @@ void MaxLairBossFinder::program(MultiSwitchProgramEnvironment& env, CancellableS
}
});

EndBattleDecider_BossFinder decider(CONSOLES, BOSS_LIST);
EndBattleDecider_BossFinder decider(CONSOLES, BOSS_LIST, NON_BOSS_LIST);

loop_adventures(
env, scope, CONSOLES,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Options/PokemonSwSh_MaxLair_Options_Consoles.h"
#include "Options/PokemonSwSh_MaxLair_Options_Hosting.h"
#include "Options/PokemonSwSh_MaxLair_Options_BossAction.h"
#include "Options/PokemonSwSh_MaxLair_Options_NonBossAction.h"


namespace PokemonAutomation{
Expand Down Expand Up @@ -46,6 +47,7 @@ class MaxLairBossFinder : public MultiSwitchProgramInstance{

MaxLairInternal::Consoles CONSOLES;
MaxLairInternal::BossActionTable BOSS_LIST;
MaxLairInternal::NonBossActionTable NON_BOSS_LIST;
MaxLairInternal::HostingSettings HOSTING;

TouchDateIntervalOption TOUCH_DATE_INTERVAL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ class EndBattleDecider_Standard : public EndBattleDecider{
}
return actions.no_shinies;
}
virtual bool stop_for_non_boss(const std::string& slug) const override {
return false;
}

private:
const MaxLairStandard_ConsoleOptions& console(size_t index) const{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ class EndBattleDecider_StrongBoss : public EndBattleDecider{
return CaughtScreenAction::TAKE_NON_BOSS_SHINY_AND_CONTINUE;
}
}
virtual bool stop_for_non_boss(const std::string& slug) const override {
return false;
}

private:
const MaxLairStrongBoss_ConsoleOptions& console(size_t index) const{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ StateMachineAction run_move_select(
OcrFailureWatchdog& ocr_watchdog,
GlobalStateTracker& state_tracker,
const ConsoleSpecificOptions& settings,
bool currently_dmaxed, bool cheer_only
bool currently_dmaxed, bool cheer_only,
const EndBattleDecider& decider
){
GlobalState& state = state_tracker[console_index];
size_t player_index = state.find_player_index(console_index);
Expand All @@ -216,6 +217,14 @@ StateMachineAction run_move_select(
)){
return StateMachineAction::RESET_RECOVER;
}

if (state.wins < 4) {
const std::string& opponent = state.opponent.empty() ? "" : *state.opponent.begin();
if (!opponent.empty() && decider.stop_for_non_boss(opponent)) {
stream.log("Stopping program as " + opponent + " was encountered.", COLOR_PURPLE);
return StateMachineAction::STOP_PROGRAM;
}
}


GlobalState inferred = state_tracker.synchronize(stream.logger(), console_index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ StateMachineAction run_move_select(
OcrFailureWatchdog& ocr_watchdog,
GlobalStateTracker& state_tracker,
const ConsoleSpecificOptions& settings,
bool currently_dmaxed, bool cheer_only
bool currently_dmaxed, bool cheer_only,
const EndBattleDecider& decider
);

StateMachineAction throw_balls(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ struct MaxLairDatabase{
std::map<std::string, MaxLairMon> m_bosses;

std::map<size_t, std::string> m_bosses_by_dex;
std::multimap<size_t, std::string> m_rentals_by_dex;

static MaxLairDatabase& instance(){
static MaxLairDatabase data;
Expand All @@ -218,6 +219,15 @@ struct MaxLairDatabase{
}
m_bosses_by_dex[iter->second] = item.first;
}

for (const auto& item : m_rentals){
const MaxLairSlugs& slugs = get_maxlair_slugs(item.first);
auto iter = national_dex.find(slugs.name_slug);
if (iter == national_dex.end()){
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Rental slug not found in national dex: " + slugs.name_slug);
}
m_rentals_by_dex.insert({iter->second, item.first});
}

#if 0
for (const auto& item : m_bosses_by_dex){
Expand All @@ -237,6 +247,10 @@ const std::map<size_t, std::string>& all_bosses_by_dex(){
const MaxLairDatabase& database = MaxLairDatabase::instance();
return database.m_bosses_by_dex;
}
const std::multimap<size_t, std::string>& all_rentals_by_dex(){
const MaxLairDatabase& database = MaxLairDatabase::instance();
return database.m_rentals_by_dex;
}
bool is_boss(const std::string& slug){
const MaxLairDatabase& database = MaxLairDatabase::instance();
auto iter = database.m_bosses.find(slug);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct MaxLairMon{
};

const std::map<size_t, std::string>& all_bosses_by_dex();
const std::multimap<size_t, std::string>& all_rentals_by_dex();
bool is_boss(const std::string& slug);

const MaxLairMon& get_maxlair_mon(const std::string& slug);
Expand Down
2 changes: 2 additions & 0 deletions SerialPrograms/cmake/SourceFiles.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2440,6 +2440,8 @@ file(GLOB LIBRARY_SOURCES
Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_Consoles.h
Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_Hosting.cpp
Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_Hosting.h
Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_NonBossAction.cpp
Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_NonBossAction.h
Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.cpp
Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.h
Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_Standard.cpp
Expand Down