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

feat: reliable finale generation, stubbed in existing rarer buildings for finales #5898

Merged
merged 45 commits into from
Feb 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
38b4520
added very first stab at reliable finale generation, stubbed in elect…
Najones19746 Jan 7, 2025
8a9dda6
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 7, 2025
b98752c
got reliable finale spawning per city
Najones19746 Jan 7, 2025
6f6f839
added city retries, stronger looping via passing struct by reference
Najones19746 Jan 11, 2025
78acb0f
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 11, 2025
b18335c
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Jan 11, 2025
1c992c3
bonked the linter changes for now
Najones19746 Jan 11, 2025
529cb4c
swapped to megastore for static test, larger tileset
Najones19746 Jan 12, 2025
d4e9bcc
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Jan 12, 2025
733f582
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 12, 2025
2fcd25b
added first stab at regional settings for finales. Stubbed in dummy f…
Najones19746 Jan 12, 2025
e0629c1
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Jan 12, 2025
cae5145
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 12, 2025
c4bb1c5
added building selection from building_bin's
Najones19746 Jan 14, 2025
b18ff2b
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Jan 14, 2025
cfb031f
added comment for town pass by reference
Najones19746 Jan 17, 2025
287fa22
added error bounding on finale placement
Najones19746 Jan 17, 2025
ddc7cee
added finales to test_regions
Najones19746 Jan 17, 2025
76f508b
changed finales to not generate in small towns, since they end up at …
Najones19746 Jan 17, 2025
d1c41a9
fixed if statements :)
Najones19746 Jan 17, 2025
07c9711
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 17, 2025
f6d1722
added list of starter finales to be replaced later
Najones19746 Jan 18, 2025
6999f14
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Jan 18, 2025
991c42c
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 18, 2025
20f7bd2
removed local cmake presets and notes
Najones19746 Jan 18, 2025
19b525f
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Jan 18, 2025
cab29bd
removed accidental files
Najones19746 Jan 18, 2025
c326ba9
Merge branch 'main' into killorank/mapgen_guarantees
scarf005 Jan 19, 2025
2de53ac
added some guard rails around map generation if finales are empty
Najones19746 Jan 27, 2025
ed15d4a
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 27, 2025
45567ac
refactored finale generation to work on world sets without finales. A…
Najones19746 Jan 31, 2025
9c57ca9
style(autofix.ci): automated formatting
autofix-ci[bot] Jan 31, 2025
a2ea843
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Jan 31, 2025
cf67c4c
Merge remote-tracking branch 'upstream/main' into pr/5898
chaosvolt Feb 10, 2025
64b18a6
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Feb 11, 2025
6a4b1b6
bugfixed checking finale bin and map generation
Najones19746 Feb 11, 2025
3c9ab49
used built in rng method, changed finale counter depending on town size
Najones19746 Feb 15, 2025
4aa8b57
updated finale list
Najones19746 Feb 15, 2025
f8d4a29
style(autofix.ci): automated formatting
autofix-ci[bot] Feb 15, 2025
acaac9e
updated loop settings for city generation, changed bottom of finale s…
Najones19746 Feb 16, 2025
a525132
style(autofix.ci): automated formatting
autofix-ci[bot] Feb 16, 2025
a74c00f
swapped to higher level variable for finale max tries
Najones19746 Feb 16, 2025
bc6ca14
Merge branch 'killorank/mapgen_guarantees' of github.com:killorank2/C…
Najones19746 Feb 16, 2025
f9c0154
changed layer backup/restore strategy
Najones19746 Feb 16, 2025
37422d3
style(autofix.ci): automated formatting
autofix-ci[bot] Feb 16, 2025
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
13 changes: 13 additions & 0 deletions data/json/regional_map_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,19 @@
"homeless_shelter": 100,
"parking_garage": 200,
"afs_augmentation_clinic_1": 200
},
"finales": {
"s_electronics_2": 200,
"s_gun_2": 200,
"megastore": 200,
"town_hall": 200,
"police_1": 200,
"s_gun_1": 200,
"fire_station": 100,
"hospital": 100,
"house_prepper": 50,
"office_tower_hiddenlab": 50,
"pawn_pf": 50
}
},
"weather": {
Expand Down
3 changes: 2 additions & 1 deletion data/json/test_regions.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@
"s_hardware": 2,
"police": 1,
"s_lot": 6
}
},
"finales": { "Military Outpost": 200 }
},
"weather": {
"spring_temp": 16,
Expand Down
130 changes: 98 additions & 32 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2841,7 +2841,7 @@
copy.insert( nested.second );
std::optional<overmap_special_id> recures = check_recursion( nested.second, copy );
if( recures ) {
return *recures;

Check warning on line 2844 in src/overmap.cpp

View workflow job for this annotation

GitHub Actions / build

conversion from 'std::optional<string_id<overmap_special>>' into 'string_id<overmap_special>' and back into 'std::optional<string_id<overmap_special>>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion]
}
}
}
Expand Down Expand Up @@ -2944,6 +2944,10 @@
layer[k].visible[i][j] = false;
layer[k].explored[i][j] = false;
layer[k].path[i][j] = false;
layer_backup[k].terrain[i][j] = tid;
layer_backup[k].visible[i][j] = false;
layer_backup[k].explored[i][j] = false;
layer_backup[k].path[i][j] = false;
}
}
}
Expand Down Expand Up @@ -3291,7 +3295,7 @@
map_layer &this_layer = layer[z + OVERMAP_DEPTH];
for( const auto &extra : this_layer.extras ) {
const std::string extra_text = extra.id.c_str();
if( match_include_exclude( extra_text, text ) ) {

Check warning on line 3298 in src/overmap.cpp

View workflow job for this annotation

GitHub Actions / build

1st argument 'extra_text' (passed to 'text') looks like it might be swapped with the 2nd, 'text' (passed to 'filter') [readability-suspicious-call-argument]
extra_locations.push_back( project_combine( pos(), extra.p ) );
}
}
Expand Down Expand Up @@ -4632,7 +4636,7 @@
return;
}
int op_city_spacing = get_option<int>( "CITY_SPACING" );

const city_settings &city_spec = settings->city_spec;
// spacing dictates how much of the map is covered in cities
// city | cities | size N cities per overmap
// spacing | % of map | 2 | 4 | 8 | 12 | 16
Expand Down Expand Up @@ -4662,54 +4666,81 @@
// is (1 - 1/(OMAPX * OMAPY))^MAX_PLACEMENT_ATTEMTPS = approx. 36% for the OMAPX=OMAPY=180 and MAX_PLACEMENT_ATTEMTPS=OMAPX * OMAPY
const int MAX_PLACEMENT_ATTEMTPS = OMAPX * OMAPY;
int placement_attempts = 0;

int finale_distance = 1;
// place a seed for NUM_CITIES cities, and maybe one more
while( cities.size() < static_cast<size_t>( NUM_CITIES ) &&
placement_attempts < MAX_PLACEMENT_ATTEMTPS ) {
placement_attempts++;

city tmp;
// randomly make some cities smaller or larger
// guarantee placement of a finale/vault tile in large/huge cities
int size = rng( op_city_size - 1, op_city_size + 1 );
if( one_in( 3 ) ) { // 33% tiny
tmp.attempt_finale = false;
size = 1;
} else if( one_in( 2 ) ) { // 33% small
tmp.attempt_finale = false;
size = size * 2 / 3;
} else if( one_in( 2 ) ) { // 17% large
tmp.attempt_finale = true;
size = size * 3 / 2;
finale_distance = 5;
} else { // 17% huge
tmp.attempt_finale = true;
size = size * 2;
finale_distance = 15;
}
//also avoid a finale if the city spec has none
if( !city_spec.finales.finalized ) {
tmp.attempt_finale = false;
}
size = std::max( size, 1 );

// TODO: put cities closer to the edge when they can span overmaps
// don't draw cities across the edge of the map, they will get clipped
const tripoint_om_omt p{ rng( size - 1, OMAPX - size ), rng( size - 1, OMAPY - size ), 0 };
//make a backup of the map
std::copy( std::begin( layer ), std::end( layer ), std::begin( layer_backup ) );
tmp.finale_placed = false;
int finale_attempts = 0;
int finale_max_tries = 1500;
//attempt to generate a city with a finale if it's not tiny. If it's tiny just run once via a do while.
do {
//std::unordered_map<tripoint_om_omt, std::string> oter_id_migrations;
if( ter( p ) == settings->default_oter ) {
placement_attempts = 0;
ter_set( p, oter_id( "road_nesw_manhole" ) ); // every city starts with an intersection
ter_set( p + tripoint_below, oter_id( "sewer_isolated" ) );
tmp.pos = p.xy();
tmp.size = size;
;
tmp.finale_counter = rng( 0, finale_distance );
cities.push_back( tmp );

const auto start_dir = om_direction::random();
auto cur_dir = start_dir;
std::vector<tripoint_om_omt> sewers;

do {
build_city_street( local_road, tmp.pos, size, cur_dir, tmp, sewers );
} while( ( cur_dir = om_direction::turn_right( cur_dir ) ) != start_dir );
for( const tripoint_om_omt &p : sewers ) {
build_connection( tmp.pos, p.xy(), p.z(), *sewer_tunnel, false );
}

if( ter( p ) == settings->default_oter ) {
placement_attempts = 0;
ter_set( p, oter_id( "road_nesw_manhole" ) ); // every city starts with an intersection
ter_set( p + tripoint_below, oter_id( "sewer_isolated" ) );
city tmp;
tmp.pos = p.xy();
tmp.size = size;
cities.push_back( tmp );

const auto start_dir = om_direction::random();
auto cur_dir = start_dir;
std::vector<tripoint_om_omt> sewers;

do {
build_city_street( local_road, tmp.pos, size, cur_dir, tmp, sewers );
} while( ( cur_dir = om_direction::turn_right( cur_dir ) ) != start_dir );

for( const tripoint_om_omt &p : sewers ) {
build_connection( tmp.pos, p.xy(), p.z(), *sewer_tunnel, false );
//if the city finale failed to place, restore from last backup and try again at the top of the loop
if( !tmp.finale_placed && tmp.attempt_finale && finale_attempts < finale_max_tries ) {
std::copy( std::begin( layer_backup ), std::end( layer_backup ), std::begin( layer ) );
}
}
}
finale_attempts++;
} while( ( tmp.attempt_finale && !tmp.finale_placed &&
finale_attempts < finale_max_tries ) );
}
}

overmap_special_id overmap::pick_random_building_to_place( int town_dist ) const
overmap_special_id overmap::pick_random_building_to_place( int town_dist,
bool attempt_finale_place ) const
{
const city_settings &city_spec = settings->city_spec;
int shop_radius = city_spec.shop_radius;
Expand All @@ -4726,7 +4757,11 @@
int park_normal = std::max( static_cast<int>( normal_roll( park_radius, park_sigma ) ),
park_radius );

if( shop_normal > town_dist ) {
if( attempt_finale_place ) {
//return overmap_special_id("Military Outpost");
//return overmap_special_id( "megastore" );
return city_spec.pick_finale();
} else if( shop_normal > town_dist ) {
return city_spec.pick_shop();
} else if( park_normal > town_dist ) {
return city_spec.pick_park();
Expand All @@ -4735,26 +4770,30 @@
}
}

void overmap::place_building( const tripoint_om_omt &p, om_direction::type dir, const city &town )
bool overmap::place_building( const tripoint_om_omt &p, om_direction::type dir, city &town,
bool attempt_finale_place )
{
const tripoint_om_omt building_pos = p + om_direction::displace( dir );
const om_direction::type building_dir = om_direction::opposite( dir );

const int town_dist = ( trig_dist( building_pos.xy(), town.pos ) * 100 ) / std::max( town.size, 1 );

for( size_t retries = 10; retries > 0; --retries ) {
const overmap_special_id building_tid = pick_random_building_to_place( town_dist );
const overmap_special_id building_tid = pick_random_building_to_place( town_dist,
attempt_finale_place );

if( can_place_special( *building_tid, building_pos, building_dir, false ) ) {
place_special( *building_tid, building_pos, building_dir, town, false, false );
return( true );
break;
}
}
return( false );
}

//city passed by reference as it's modified by the function, which calls itself recursively
void overmap::build_city_street(
const overmap_connection &connection, const point_om_omt &p, int cs,
om_direction::type dir, const city &town, std::vector<tripoint_om_omt> &sewers,
om_direction::type dir, city &town, std::vector<tripoint_om_omt> &sewers,
int block_width )
{
int c = cs;
Expand Down Expand Up @@ -4810,12 +4849,39 @@
sewers.push_back( rp + tripoint_below );
}
}

bool attempt_finale_place = false;
// place a finale somewhere within the first 15 buildings
if( town.finale_counter == 0 && !town.finale_placed && town.attempt_finale ) {
attempt_finale_place = true;
} else {
town.finale_counter--;
}
if( !one_in( BUILDINGCHANCE ) ) {
place_building( rp, om_direction::turn_left( dir ), town );
if( attempt_finale_place && !town.finale_placed ) {
//attempt to place a finale, confirm that it was placed.
if( place_building( rp, om_direction::turn_left( dir ), town, true ) ) {
town.finale_placed = true;
} else { // if the finale fails to place stop trying. This prevents the finale getting placed at the edge of town.
attempt_finale_place = false;
town.finale_counter = -1;
}
} else {
place_building( rp, om_direction::turn_left( dir ), town, false );
}
}
if( !one_in( BUILDINGCHANCE ) ) {
place_building( rp, om_direction::turn_right( dir ), town );

if( attempt_finale_place && !town.finale_placed ) {
//attempt to place a finale, confirm that it was placed.
if( place_building( rp, om_direction::turn_left( dir ), town, true ) ) {
town.finale_placed = true;
} else { // if the finale fails to place stop trying. This prevents the finale getting placed at the edge of town.
attempt_finale_place = false;
town.finale_counter = -1;
}
} else {
place_building( rp, om_direction::turn_right( dir ), town, false );
}
}
}

Expand Down Expand Up @@ -5583,7 +5649,7 @@
auto points = connection_cache->get_closests( elem.connection->id, rp.z(), rp.xy() );
int attempts = 0;
for( const point_om_omt &pos : points ) {
if( ( linked = build_connection( pos, rp.xy(), rp.z(), *elem.connection,

Check warning on line 5652 in src/overmap.cpp

View workflow job for this annotation

GitHub Actions / build

an assignment within an 'if' condition is bug-prone [bugprone-assignment-in-if-condition]
must_be_unexplored, initial_dir ) ) ||
++attempts > 10 ) {
break;
Expand All @@ -5600,7 +5666,7 @@
!check_ot( elem.connection->id->default_terrain.str(), ot_match_type::type, p ) ) {
continue;
}
if( ( linked = build_connection( p.xy(), rp.xy(), rp.z(), *elem.connection,

Check warning on line 5669 in src/overmap.cpp

View workflow job for this annotation

GitHub Actions / build

an assignment within an 'if' condition is bug-prone [bugprone-assignment-in-if-condition]
must_be_unexplored, initial_dir ) ) ||
++attempts > 10 ) {
break;
Expand Down
11 changes: 8 additions & 3 deletions src/overmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ struct city {
// location of the city (in overmap terrain coordinates)
point_om_omt pos;
int size;
int finale_counter;
bool finale_placed;
bool attempt_finale;
std::string name;
city( const point_om_omt &P = point_om_omt(), int S = -1 );

Expand Down Expand Up @@ -359,6 +362,7 @@ class overmap
point_abs_om loc;

std::array<map_layer, OVERMAP_LAYERS> layer;
std::array<map_layer, OVERMAP_LAYERS> layer_backup;
std::unordered_map<tripoint_abs_omt, scent_trace> scents;

// Records the locations where a given overmap special was placed, which
Expand Down Expand Up @@ -432,13 +436,14 @@ class overmap
const overmap *south, const overmap *west );

// City Building
overmap_special_id pick_random_building_to_place( int town_dist ) const;
overmap_special_id pick_random_building_to_place( int town_dist, bool attempt_finale_place ) const;

void place_cities();
void place_building( const tripoint_om_omt &p, om_direction::type dir, const city &town );
bool place_building( const tripoint_om_omt &p, om_direction::type dir, city &town,
bool attempt_finale_place );

void build_city_street( const overmap_connection &connection, const point_om_omt &p, int cs,
om_direction::type dir, const city &town, std::vector<tripoint_om_omt> &sewers,
om_direction::type dir, city &town, std::vector<tripoint_om_omt> &sewers,
int block_width = 2 );

// Connection laying
Expand Down
14 changes: 14 additions & 0 deletions src/regional_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,9 @@ void load_region_settings( const JsonObject &jo )
load_building_types( "houses", new_region.city_spec.houses );
load_building_types( "shops", new_region.city_spec.shops );
load_building_types( "parks", new_region.city_spec.parks );
if( cjo.has_member( "finales" ) ) {
load_building_types( "finales", new_region.city_spec.finales );
}
}

if( !jo.has_object( "weather" ) ) {
Expand Down Expand Up @@ -714,6 +717,9 @@ void apply_region_overlay( const JsonObject &jo, regional_settings &region )
load_building_types( "houses", region.city_spec.houses );
load_building_types( "shops", region.city_spec.shops );
load_building_types( "parks", region.city_spec.parks );
if( cityjo.has_member( "finales" ) ) {
load_building_types( "finales", region.city_spec.finales );
}

load_overmap_feature_flag_settings( jo, region.overmap_feature_flag, false, true );

Expand Down Expand Up @@ -1030,11 +1036,19 @@ overmap_special_id city_settings::pick_park() const
return parks.pick()->id;
}

overmap_special_id city_settings::pick_finale() const
{
return finales.pick()->id;
}

void city_settings::finalize()
{
houses.finalize();
shops.finalize();
parks.finalize();
if( !finales.unfinalized_buildings.empty() ) {
finales.finalize();
}
}

void building_bin::add( const overmap_special_id &building, int weight )
Expand Down
6 changes: 4 additions & 2 deletions src/regional_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class JsonObject;
class building_bin
{
private:
bool finalized = false;
weighted_int_list<overmap_special_id> buildings;
std::map<overmap_special_id, int> unfinalized_buildings;
public:
std::map<overmap_special_id, int> unfinalized_buildings;
bool finalized = false;
building_bin() = default;
void add( const overmap_special_id &building, int weight );
overmap_special_id pick() const;
Expand All @@ -47,10 +47,12 @@ struct city_settings {
building_bin houses;
building_bin shops;
building_bin parks;
building_bin finales;

overmap_special_id pick_house() const;
overmap_special_id pick_shop() const;
overmap_special_id pick_park() const;
overmap_special_id pick_finale() const;

void finalize();
};
Expand Down
Loading