Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows setting fixed special scoped parameters for start locations #72558

Merged
merged 6 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 10 additions & 2 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,16 +850,21 @@ 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<std::string, std::string> associated_parameters;
const bool select_starting_city = get_option<bool>( "SELECT_STARTING_CITY" );
do {
if( select_starting_city ) {
if( !u.starting_city.has_value() ) {
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 ) {

Expand All @@ -873,6 +878,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() ) {
Expand Down
10 changes: 8 additions & 2 deletions src/mapgendata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<mapgen_arguments> *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<overmap_special_id> 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",
Expand Down
2 changes: 2 additions & 0 deletions src/overmapbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 & );

Expand Down
103 changes: 85 additions & 18 deletions src/start_location.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string, ot_match_type> 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
Expand Down Expand Up @@ -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<std::string, std::string> 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<ot_match_type>( "om_terrain_match_type", ter_match_type );
if( jot.has_string( "om_terrain_match_type" ) ) {
ter_match_type = jot.get_enum_value<ot_match_type>( "om_terrain_match_type", ter_match_type );
}
if( jot.has_object( "parameters" ) ) {
std::unordered_map<std::string, std::string> 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 );
Expand Down Expand Up @@ -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<tripoint_abs_omt, std::unordered_map<std::string, std::string>>
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<tripoint_abs_omt, std::unordered_map<std::string, std::string>>
start_location::find_player_initial_location( const city &origin ) const
{
overmap &omap = overmap_buffer.get( origin.pos_om );
std::vector<tripoint_om_omt> valid;
std::vector<std::pair<tripoint_om_omt, omt_types_parameters>> 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<tripoint_om_omt, omt_types_parameters> 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<std::string, std::string> &parameters_to_set ) const
{
if( parameters_to_set.empty() ) {
return;
}
overmap_buffer.externally_set_args = true;
std::optional<mapgen_arguments> *maybe_args = overmap_buffer.mapgen_args( omtstart );
if( !maybe_args ) {
debugmsg( "No overmap special args at start location." );
return;
}
std::optional<overmap_special_id> 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 &params = special.get_params();
mapgen_arguments args;
for( const auto &param_to_set : parameters_to_set ) {
const std::string &param_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 &param = 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<std::string> 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
Expand Down
21 changes: 17 additions & 4 deletions src/start_location.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ struct start_location_placement_constraints {
numeric_interval<int> allowed_z_levels{ -OVERMAP_DEPTH, OVERMAP_HEIGHT };
};

struct omt_types_parameters {
std::string omt;
ot_match_type omt_type;
std::unordered_map<std::string, std::string> parameters;
};

class start_location
{
public:
Expand All @@ -39,7 +45,7 @@ class start_location

std::string name() const;
int targets_count() const;
std::pair<std::string, ot_match_type> random_target() const;
omt_types_parameters random_target() const;
const std::set<std::string> &flags() const;

/**
Expand All @@ -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<tripoint_abs_omt, std::unordered_map<std::string, std::string>>
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<tripoint_abs_omt, std::unordered_map<std::string, std::string>>
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<std::string, std::string> &parameters_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.
Expand Down Expand Up @@ -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<std::pair<std::string, ot_match_type>> _omt_types;
std::vector<omt_types_parameters> _locations;
std::set<std::string> _flags;
start_location_placement_constraints constraints_;

Expand Down
Loading