diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index e1cba148a07..2f76880ae5c 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -41,6 +41,7 @@ Manatee #include "report/charts.hpp" #include "report/highchart.hpp" #include "sc_enums.hpp" +#include "sim/profileset_control.hpp" #include @@ -2223,7 +2224,7 @@ struct auto_attack_t : public monk_melee_attack_t action_t *damage; template - thunderfist_t( monk_t *player, Args &&...args ) : TBase( player, std::forward( args )... ) + thunderfist_t( monk_t *player, Args &&...args ) : TBase( player, std::forward( args )... ), damage( nullptr ) { if ( !player->talent.windwalker.thunderfist->ok() ) return; @@ -5509,26 +5510,29 @@ bool monk_t::validate_actor() return false; } - int expected = 13; - for ( const auto &hero_tree : player_sub_trees ) + if ( !sim->parent && !sim->profileset_enabled ) { - int count = as( range::count_if( - player_traits, [ is_ptr = is_ptr(), hero_tree ]( std::tuple entry ) { - if ( std::get( entry ) != talent_tree::HERO ) - return false; - const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); - if ( !trait ) - return false; - return static_cast( trait->id_sub_tree ) == hero_tree; - } ) ); - - // Report without counting the hidden talent that activates the subtree - count -= 1; - if ( count < expected ) + int expected = 13; + for ( const auto &hero_tree : player_sub_trees ) { - sim->error( SEVERE, "Invalid Hero Talent tree, possibly low level. Found {} talents, expected {}.", count, - expected ); - return false; + int count = as( range::count_if( + player_traits, [ is_ptr = is_ptr(), hero_tree ]( std::tuple entry ) { + if ( std::get( entry ) != talent_tree::HERO ) + return false; + const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); + if ( !trait ) + return false; + return static_cast( trait->id_sub_tree ) == hero_tree; + } ) ); + + // Report without counting the hidden talent that activates the subtree + count -= 1; + if ( count < expected && count != 0 ) + { + sim->error( SEVERE, "Invalid Hero Talent tree, possibly low level. Found {} talents, expected {}.", count, + expected ); + return false; + } } } @@ -7018,6 +7022,11 @@ void monk_t::init_finished() { base_t::init_finished(); parse_player_effects(); + + profileset_controller_t::register_controller( + "valid_talents", profileset_controller::create_fn_pair() ); + std::vector rhs = { fmt::format( "player={},count=13", name() ) }; + sim->profileset_controller_options.emplace( "valid_talents", rhs ); } void monk_t::reset() @@ -7345,6 +7354,80 @@ void monk_t::create_actions() buff.aspect_of_harmony.construct_actions( this ); } +namespace profileset_control +{ +valid_talents_t::valid_talents_t( sim_t *sim, unsigned int id ) : profileset_controller_t( sim, id ) +{ +} + +const std::string valid_talents_t::name() const +{ + return "valid_talents"; +} + +std::function )> matching_talent( player_t *player, + unsigned hero_tree ) +{ + return [ = ]( std::tuple entry ) { + if ( std::get( entry ) != talent_tree::HERO ) + return false; + const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), player->is_ptr() ); + if ( !trait ) + return false; + return static_cast( trait->id_sub_tree ) == hero_tree; + }; +} + +std::function has_expected_count( player_t *player, unsigned expected_count ) +{ + return [ = ]( unsigned hero_tree ) { + unsigned count = range::count_if( player->player_traits, matching_talent( player, hero_tree ) ); + return count > expected_count; + }; +} + +bool valid_talents_t::evaluate_post_init() +{ + if ( !player || sim->enable_all_talents ) + return true; + + switch ( player->specialization() ) + { + case MONK_BREWMASTER: + case MONK_WINDWALKER: + return range::all_of( player->player_sub_trees, has_expected_count( player, count ) ); + default: + break; + } + + return true; +} + +const std::string valid_talents_t::reason() const +{ + return fmt::format( "player {} does not have {} talents selected in hero tree", player->name(), count ); +} + +void valid_talents_t::create_options() +{ + add_option( opt_func( "count", [ this ]( sim_t *, util::string_view, util::string_view value ) { + this->count = util::to_unsigned( value ); + return true; + } ) ); + add_option( opt_func( "player", [ this ]( sim_t *sim, util::string_view, util::string_view value ) { + for ( auto &player : sim->player_list ) + { + if ( util::str_compare_ci( player->name(), value ) ) + { + this->player = player; + return true; + } + } + return false; + } ) ); +} +} // namespace profileset_control + std::unique_ptr monk_t::create_expression( std::string_view name_str ) { auto splits = util::string_split( name_str, "." ); diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index c96afdd6741..982f1a4765e 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -16,6 +16,7 @@ #include "sc_enums.hpp" #include "sc_stagger.hpp" #include "sim/proc.hpp" +#include "sim/profileset_control.hpp" #include "util/timeline.hpp" #include @@ -1216,6 +1217,24 @@ struct monk_t : public stagger_t void trigger_celestial_fortune( action_state_t * ); }; +namespace profileset_control +{ +struct valid_talents_t : profileset_controller_t +{ + using data_t = profileset_controller_data_t; + + player_t *player; + unsigned int count; + + valid_talents_t( sim_t *sim, unsigned int id ); + + const std::string name() const override; + bool evaluate_post_init() override; + const std::string reason() const override; + void create_options() override; +}; +} // namespace profileset_control + namespace events { // based on implementation from sc_demon_hunter.cpp diff --git a/engine/sim/profileset.cpp b/engine/sim/profileset.cpp index 4828b634131..11ee87109fa 100644 --- a/engine/sim/profileset.cpp +++ b/engine/sim/profileset.cpp @@ -403,7 +403,7 @@ void worker_t::execute() { try { - m_sim = new sim_t( m_parent, 0, m_profileset->options() ); + m_sim = new sim_t( m_parent, 0, m_profileset->options(), m_profileset->name() ); simulate_profileset( m_parent, *m_profileset, m_sim ); } @@ -501,7 +501,7 @@ void profilesets_t::generate_work( sim_t* parent, std::unique_ptr try { - sim_t* profile_sim = new sim_t( parent ); + sim_t* profile_sim = new sim_t( parent, 0, ptr_set->name() ); parent->control = original_opts; simulate_profileset( parent, *ptr_set, profile_sim ); diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index 1a4df649088..0f83dfcfcd0 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -105,7 +105,10 @@ void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) bool profileset_controller_t::register_controller( std::string key, profileset_controller_t::factory_fn_pair_t&& value ) { - return factory.try_emplace( key, std::move( value ) ).second; + if ( factory.find( key ) != factory.end() ) + return false; + factory.emplace( key, value ); + return true; } bool profileset_controller_t::controller_exists( std::string key ) @@ -140,7 +143,7 @@ void profileset_controller_t::evaluate( sim_t* sim, call_point_e call_point ) assert( controller->parent == sim->parent ); controller->set_exit_reason( - { sim->parent->profilesets->current_profileset_name(), call_point, controller->reason() } ); + { sim->profileset_name, call_point, controller->reason() } ); sim->canceled = true; sim->error( error_level_e::TRIVIAL, "{}", controller->message( call_point ) ); @@ -161,7 +164,7 @@ profileset_controller_t::profileset_controller_t( sim_t* sim, unsigned int id ) const std::string profileset_controller_t::message( call_point_e call_point ) { std::string msg = fmt::format( "Profileset {} was canceled by Profileset Controller {} after {}", - parent->profilesets->current_profileset_name(), name(), + sim->profileset_name, name(), profileset_controller::call_point_string( call_point ) ); if ( call_point == POST_ITER ) msg += std::to_string( sim->current_iteration ); diff --git a/engine/sim/profileset_control.hpp b/engine/sim/profileset_control.hpp index 02b97c0dece..4105dc0771f 100644 --- a/engine/sim/profileset_control.hpp +++ b/engine/sim/profileset_control.hpp @@ -34,7 +34,7 @@ struct data_wrapper_t struct exit_reason_t { - const std::string profileset_name; + const std::string_view profileset_name; const call_point_e exit_point; const std::string exit_reason; }; diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index 4e38c8612cf..a0b9f48770f 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -1579,7 +1579,8 @@ sim_t::sim_t() profileset_enabled( false ), profileset_work_threads( 0 ), profileset_init_threads( 1 ), - profilesets( std::make_unique() ) + profilesets( std::make_unique() ), + profileset_name() { item_db_sources.assign( std::begin( default_item_db_sources ), std::end( default_item_db_sources ) ); @@ -1592,10 +1593,13 @@ sim_t::sim_t() profileset::create_options( this ); } -sim_t::sim_t( sim_t* p, int index ) : sim_t() +sim_t::sim_t( sim_t* p, int index, std::string_view profileset_name ) + : sim_t() { assert( p ); + this->profileset_name = profileset_name; + parent = p; thread_index = index; @@ -1619,10 +1623,13 @@ sim_t::sim_t( sim_t* p, int index ) : sim_t() parent -> add_relative( this ); } -sim_t::sim_t( sim_t* p, int index, sim_control_t* control ) : sim_t() +sim_t::sim_t( sim_t* p, int index, sim_control_t* control, std::string_view profileset_name ) + : sim_t() { assert( p && control ); + this->profileset_name = profileset_name; + parent = p; thread_index = index; @@ -3387,7 +3394,7 @@ void sim_t::partition() for ( int i = 0; i < num_children; i++ ) { - auto child = new sim_t( this, i + 1, child_control ); + auto child = new sim_t( this, i + 1, child_control, child_control->combat.name ); assert( child ); children.push_back( child ); diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index b7e3ea4dfed..3510453227d 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -651,10 +651,11 @@ struct sim_t : private sc_thread_t bool profileset_enabled; int profileset_work_threads, profileset_init_threads; std::unique_ptr profilesets; + std::string_view profileset_name; sim_t(); - explicit sim_t( sim_t* parent, int thread_index = 0 ); - sim_t( sim_t* parent, int thread_index, sim_control_t* control ); + explicit sim_t( sim_t* parent, int thread_index = 0, std::string_view profileset_name = {} ); + sim_t( sim_t* parent, int thread_index, sim_control_t* control, std::string_view profileset_name = {} ); ~sim_t() override; void run() override;