diff --git a/src/game.cpp b/src/game.cpp index e420bdfc96f79..aede303b0b57a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -852,6 +852,7 @@ bool game::start_game() const start_location &start_loc = u.random_start_location ? scen->random_start_location().obj() : u.start_location.obj(); tripoint_abs_omt omtstart = overmap::invalid_tripoint; + std::unordered_map associated_parameters; const bool select_starting_city = get_option( "SELECT_STARTING_CITY" ); do { if( select_starting_city ) { @@ -859,9 +860,13 @@ bool game::start_game() u.starting_city = random_entry( city::get_all() ); u.world_origin = u.starting_city->pos_om; } - omtstart = start_loc.find_player_initial_location( u.starting_city.value() ); + auto ret = start_loc.find_player_initial_location( u.starting_city.value() ); + omtstart = ret.first; + associated_parameters = ret.second; } else { - omtstart = start_loc.find_player_initial_location( u.world_origin.value_or( point_abs_om() ) ); + auto ret = start_loc.find_player_initial_location( u.world_origin.value_or( point_abs_om() ) ); + omtstart = ret.first; + associated_parameters = ret.second; } if( omtstart == overmap::invalid_tripoint ) { @@ -875,6 +880,9 @@ bool game::start_game() } } while( omtstart == overmap::invalid_tripoint ); + // Set parameter(s) if specified in chosen start_loc + start_loc.set_parameters( omtstart, associated_parameters ); + start_loc.prepare_map( omtstart ); if( scen->has_map_extra() ) { diff --git a/src/mapgendata.cpp b/src/mapgendata.cpp index c56f9f96aa1fa..fd7e5916a11ec 100644 --- a/src/mapgendata.cpp +++ b/src/mapgendata.cpp @@ -79,14 +79,20 @@ mapgendata::mapgendata( const tripoint_abs_omt &over, map &mp, const float densi set_neighbour( 6, direction::SOUTHWEST ); set_neighbour( 7, direction::NORTHWEST ); if( std::optional *maybe_args = overmap_buffer.mapgen_args( over ) ) { - if( *maybe_args ) { + if( *maybe_args && !overmap_buffer.externally_set_args ) { mapgen_args_ = **maybe_args; } else { // We are the first omt from this overmap_special to be generated, // so now is the time to generate the arguments if( std::optional s = overmap_buffer.overmap_special_at( over ) ) { const overmap_special &special = **s; - *maybe_args = special.get_args( *this ); + mapgen_arguments internally_set_args = special.get_args( *this ); + if( overmap_buffer.externally_set_args ) { + maybe_args->value().map.merge( internally_set_args.map ); + overmap_buffer.externally_set_args = false; + } else { + *maybe_args = internally_set_args; + } mapgen_args_ = **maybe_args; } else { debugmsg( "mapgen params expected but no overmap special found for terrain %s", diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h index 140ea97bfe641..6b5ab15662ecb 100644 --- a/src/overmapbuffer.h +++ b/src/overmapbuffer.h @@ -144,6 +144,8 @@ class overmapbuffer public: overmapbuffer(); + bool externally_set_args = false; + static cata_path terrain_filename( const point_abs_om & ); static cata_path player_filename( const point_abs_om & ); diff --git a/src/start_location.cpp b/src/start_location.cpp index b144d4143c8a3..0990b37ebf0f8 100644 --- a/src/start_location.cpp +++ b/src/start_location.cpp @@ -68,12 +68,12 @@ std::string start_location::name() const int start_location::targets_count() const { - return _omt_types.size(); + return _locations.size(); } -std::pair start_location::random_target() const +omt_types_parameters start_location::random_target() const { - return random_entry( _omt_types ); + return random_entry( _locations ); } bool start_location::requires_city() const @@ -106,14 +106,21 @@ void start_location::load( const JsonObject &jo, const std::string &src ) std::string ter; for( const JsonValue entry : jo.get_array( "terrain" ) ) { ot_match_type ter_match_type = ot_match_type::type; + std::unordered_map parameter_map; if( entry.test_string() ) { ter = entry.get_string(); } else { JsonObject jot = entry.get_object(); ter = jot.get_string( "om_terrain" ); - ter_match_type = jot.get_enum_value( "om_terrain_match_type", ter_match_type ); + if( jot.has_string( "om_terrain_match_type" ) ) { + ter_match_type = jot.get_enum_value( "om_terrain_match_type", ter_match_type ); + } + if( jot.has_object( "parameters" ) ) { + std::unordered_map parameter_map; + jot.read( "parameters", parameter_map ); + } } - _omt_types.emplace_back( ter, ter_match_type ); + _locations.emplace_back( omt_types_parameters{ ter, ter_match_type, parameter_map } ); } if( jo.has_array( "city_sizes" ) ) { assign( jo, "city_sizes", constraints_.city_size, strict ); @@ -235,49 +242,109 @@ void start_location::prepare_map( tinymap &m ) const } } -tripoint_abs_omt start_location::find_player_initial_location( const point_abs_om &origin ) const +std::pair> + start_location::find_player_initial_location( const point_abs_om &origin ) const { // Spiral out from the world origin scanning for a compatible starting location, // creating overmaps as necessary. const int radius = 3; + const omt_types_parameters chosen_target = random_target(); for( const point_abs_om &omp : closest_points_first( origin, radius ) ) { overmap &omap = overmap_buffer.get( omp ); - const tripoint_om_omt omtstart = omap.find_random_omt( random_target() ); + const tripoint_om_omt omtstart = omap.find_random_omt( std::make_pair( chosen_target.omt, + chosen_target.omt_type ) ); if( omtstart.raw() != tripoint_min ) { - return project_combine( omp, omtstart ); + return std::make_pair( project_combine( omp, omtstart ), chosen_target.parameters ); } } // Should never happen, if it does we messed up. popup( _( "Unable to generate a valid starting location %s [%s] in a radius of %d overmaps, please report this failure." ), name(), id.str(), radius ); - return overmap::invalid_tripoint; + return std::make_pair( overmap::invalid_tripoint, chosen_target.parameters ); } -tripoint_abs_omt start_location::find_player_initial_location( const city &origin ) const +std::pair> + start_location::find_player_initial_location( const city &origin ) const { overmap &omap = overmap_buffer.get( origin.pos_om ); - std::vector valid; + std::vector> valid; for( const point_om_omt &omp : closest_points_first( origin.pos, origin.size ) ) { for( int k = constraints_.allowed_z_levels.min; k <= constraints_.allowed_z_levels.max; k++ ) { tripoint_om_omt p( omp, k ); if( !can_belong_to_city( p, origin ) ) { continue; } - for( const auto &target : _omt_types ) { - if( is_ot_match( target.first, omap.ter( p ), target.second ) ) { - valid.push_back( p ); - } + auto target_is_ot_match = [&]( const omt_types_parameters & target ) { + return is_ot_match( target.omt, omap.ter( p ), target.omt_type ); + }; + auto it = std::find_if( _locations.begin(), _locations.end(), + target_is_ot_match ); + if( it != _locations.end() ) { + valid.emplace_back( p, *it ); } } } - const tripoint_om_omt omtstart = random_entry( valid, tripoint_om_omt( tripoint_min ) ); + const std::pair random_valid = random_entry( valid, + std::make_pair( tripoint_om_omt( tripoint_min ), omt_types_parameters() ) ); + const tripoint_om_omt omtstart = random_valid.first; if( omtstart.raw() != tripoint_min ) { - return project_combine( origin.pos_om, omtstart ); + return std::make_pair( project_combine( origin.pos_om, omtstart ), random_valid.second.parameters ); } // Should never happen, if it does we messed up. popup( _( "Unable to generate a valid starting location %s [%s] in a city [%s], please report this failure." ), name(), id.str(), origin.name ); - return overmap::invalid_tripoint; + return std::make_pair( overmap::invalid_tripoint, random_valid.second.parameters ); +} + +void start_location::set_parameters( const tripoint_abs_omt &omtstart, + const std::unordered_map ¶meters_to_set ) const +{ + if( parameters_to_set.empty() ) { + return; + } + overmap_buffer.externally_set_args = true; + std::optional *maybe_args = overmap_buffer.mapgen_args( omtstart ); + if( !maybe_args ) { + debugmsg( "No overmap special args at start location." ); + return; + } + std::optional s = overmap_buffer.overmap_special_at( omtstart ); + if( !s ) { + debugmsg( "No overmap special at start location from which to fetch parameters." ); + return; + } + const overmap_special &special = **s; + const mapgen_parameters ¶ms = special.get_params(); + mapgen_arguments args; + for( const auto ¶m_to_set : parameters_to_set ) { + const std::string ¶m_name_to_set = param_to_set.first; + const std::string &value_to_set = param_to_set.second; + auto param_it = params.map.find( param_name_to_set ); + if( param_it == params.map.end() ) { + debugmsg( "Parameter %s not found", param_name_to_set ); + continue; + } + const mapgen_parameter ¶m = param_it->second; + if( param.scope() != mapgen_parameter_scope::overmap_special ) { + debugmsg( "Parameter %s is not of scope overmap_special", param_name_to_set ); + continue; + } + std::vector possible_values = param.all_possible_values( params ); + auto value_it = std::find( possible_values.begin(), possible_values.end(), value_to_set ); + if( value_it == possible_values.end() ) { + debugmsg( "Parameter value %s for parameter %s not found", value_to_set, param_name_to_set ); + continue; + } + if( *maybe_args ) { + maybe_args->value().map[param_name_to_set] = + cata_variant::from_string( param.type(), std::move( *value_it ) ); + } else { + mapgen_arguments args; + args.map[param_name_to_set] = + cata_variant::from_string( param.type(), std::move( *value_it ) ); + *maybe_args = args; + } + } } void start_location::prepare_map( const tripoint_abs_omt &omtstart ) const diff --git a/src/start_location.h b/src/start_location.h index de83f3ea1bf3a..400b8da49deee 100644 --- a/src/start_location.h +++ b/src/start_location.h @@ -27,6 +27,12 @@ struct start_location_placement_constraints { numeric_interval allowed_z_levels{ -OVERMAP_DEPTH, OVERMAP_HEIGHT }; }; +struct omt_types_parameters { + std::string omt; + ot_match_type omt_type; + std::unordered_map parameters; +}; + class start_location { public: @@ -39,7 +45,7 @@ class start_location std::string name() const; int targets_count() const; - std::pair random_target() const; + omt_types_parameters random_target() const; const std::set &flags() const; /** @@ -48,14 +54,21 @@ class start_location * It may return `overmap::invalid_tripoint` if no suitable starting location could be found * in the world. */ - tripoint_abs_omt find_player_initial_location( const point_abs_om &origin ) const; + std::pair> + find_player_initial_location( const point_abs_om &origin ) const; /** * Find a suitable start location on the overmap in specific city. * @return Global, absolute overmap terrain coordinates where the player should spawn. * It may return `overmap::invalid_tripoint` if no suitable starting location could be found * in the world. */ - tripoint_abs_omt find_player_initial_location( const city &origin ) const; + std::pair> + find_player_initial_location( const city &origin ) const; + /** + * Set any parameters assigned to the chosen start location + */ + void set_parameters( const tripoint_abs_omt &omtstart, + const std::unordered_map ¶meters_to_set ) const; /** * Initialize the map at players start location using @ref prepare_map. * @param omtstart Global overmap terrain coordinates where the player is to be spawned. @@ -95,7 +108,7 @@ class start_location bool can_belong_to_city( const tripoint_om_omt &p, const city &cit ) const; private: translation _name; - std::vector> _omt_types; + std::vector _locations; std::set _flags; start_location_placement_constraints constraints_;