Skip to content

Commit

Permalink
Improved generate + supported editmap usage
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrikLundell committed May 30, 2024
1 parent acc2445 commit b469783
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 128 deletions.
56 changes: 30 additions & 26 deletions src/editmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1844,10 +1844,10 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu )
// Copy to store the original value, to restore it upon canceling
const oter_id orig_oters = omt_ref;
overmap_buffer.ter_set( omt_pos, oter_id( gmenu.ret ) );
tinymap tmpmap;
smallmap tmpmap;
// TODO: add a do-not-save-generated-submaps parameter
// TODO: keep track of generated submaps to delete them properly and to avoid memory leaks
tmpmap.generate( omt_pos, calendar::turn );
tmpmap.generate( omt_pos, calendar::turn, false );

gmenu.border_color = c_light_gray;
gmenu.hilight_color = c_black_white;
Expand Down Expand Up @@ -1891,7 +1891,7 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu )
overmap_buffer.ter_set( omt_pos, oter_id( gmenu.selected ) );
cleartmpmap( tmpmap );
tmpmap.generate( omt_pos,
calendar::turn );
calendar::turn, false );
}

if( showpreview ) {
Expand All @@ -1916,7 +1916,7 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu )
if( gpmenu.ret == 0 ) {
cleartmpmap( tmpmap );
tmpmap.generate( omt_pos,
calendar::turn );
calendar::turn, false );
} else if( gpmenu.ret == 1 ) {
tmpmap.rotate( 1 );
} else if( gpmenu.ret == 2 ) {
Expand All @@ -1932,27 +1932,29 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu )

for( int x = 0; x < 2; x++ ) {
for( int y = 0; y < 2; y++ ) {
// Apply previewed mapgen to map. Since this is a function for testing, we try avoid triggering
// functions that would alter the results
const tripoint dest_pos = target_sub + tripoint( x, y, target.z() );
const tripoint src_pos = tripoint{ x, y, target.z()};

submap *destsm = here.get_submap_at_grid( dest_pos );
submap *srcsm = tmpmap.get_submap_at_grid( src_pos );
if( srcsm == nullptr || destsm == nullptr ) {
debugmsg( "Tried to apply previewed mapgen at (%d,%d,%d) but the submap is not loaded", src_pos.x,
src_pos.y, src_pos.z );
continue;
}
for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
// Apply previewed mapgen to map. Since this is a function for testing, we try avoid triggering
// functions that would alter the results
const tripoint dest_pos = target_sub + tripoint( x, y, z );
const tripoint src_pos = tripoint{ x, y, z };

submap *destsm = here.get_submap_at_grid( dest_pos );
submap *srcsm = tmpmap.get_submap_at_grid( src_pos );
if( srcsm == nullptr || destsm == nullptr ) {
debugmsg( "Tried to apply previewed mapgen at (%d,%d,%d) but the submap is not loaded", src_pos.x,
src_pos.y, src_pos.z );
continue;
}

std::swap( *destsm, *srcsm );
std::swap( *destsm, *srcsm );

for( auto &veh : destsm->vehicles ) {
veh->sm_pos = dest_pos;
}
for( auto &veh : destsm->vehicles ) {
veh->sm_pos = dest_pos;
}

if( !destsm->spawns.empty() ) { // trigger spawnpoints
here.spawn_monsters( true );
if( !destsm->spawns.empty() ) { // trigger spawnpoints
here.spawn_monsters( true );
}
}
}
}
Expand Down Expand Up @@ -2216,8 +2218,10 @@ void editmap::cleartmpmap( tinymap &tmpmap ) const
smap = nullptr;
}

level_cache &ch = tmpmap.get_cache( target.z() );
ch.clear_vehicle_cache();
ch.vehicle_list.clear();
ch.zone_vehicles.clear();
for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
level_cache &ch = tmpmap.get_cache( z );
ch.clear_vehicle_cache();
ch.vehicle_list.clear();
ch.zone_vehicles.clear();
}
}
2 changes: 1 addition & 1 deletion src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8373,7 +8373,7 @@ void map::loadn( const tripoint &grid, const bool update_vehicles )

smallmap tmp_map;
tmp_map.main_cleanup_override( false );
tmp_map.generate( grid_abs_omt, calendar::turn );
tmp_map.generate( grid_abs_omt, calendar::turn, true );
_main_requires_cleanup |= main_inbounds && tmp_map.is_main_cleanup_queued();
}

Expand Down
2 changes: 1 addition & 1 deletion src/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -1862,7 +1862,7 @@ class map
// mapgen.cpp functions
// The code relies on the submap coordinate falling on omt boundaries, so taking a
// tripoint_abs_omt coordinate guarantees this will be fulfilled.
void generate( const tripoint_abs_omt &p, const time_point &when );
void generate( const tripoint_abs_omt &p, const time_point &when, bool save_results );
void place_spawns( const mongroup_id &group, int chance,
const point_bub_ms &p1, const point_bub_ms &p2, int z_level, float density,
bool individual = false, bool friendly = false,
Expand Down
225 changes: 125 additions & 100 deletions src/mapgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,137 +210,162 @@ static constexpr int MON_RADIUS = 3;

static void science_room( map *m, const point &p1, const point &p2, int z, int rotate );

void map::generate( const tripoint_abs_omt &p, const time_point &when )
// Assumptions:
// - The map supplied is empty, i.e. no grid entries are in use
// - The map supports Z levels.
void map::generate( const tripoint_abs_omt &p, const time_point &when, bool save_results )
{
dbg( D_INFO ) << "map::generate( g[" << g.get() << "], p[" << p << "], "
"when[" << to_string( when ) << "] )";

const tripoint_abs_sm p_sm = project_to<coords::sm>( p );
set_abs_sub( p_sm );

// First we have to create new submaps and initialize them to 0 all over
// We create all the submaps, even if we're not a tinymap, so that map
// generation which overflows won't cause a crash. At the bottom of this
// function, we save the upper-left 4 submaps, plus the ones that were
// generated and modified, and delete the rest.

// Prepare the canvas...
for( int gridx = 0; gridx < my_MAPSIZE; gridx++ ) {
for( int gridy = 0; gridy < my_MAPSIZE; gridy++ ) {
for( int gridz = -OVERMAP_DEPTH; gridz <= OVERMAP_HEIGHT; gridz++ ) {
const size_t grid_pos = get_nonant( { gridx, gridy, gridz } );
setsubmap( grid_pos, new submap() );
// TODO: memory leak if the code below throws before the submaps get stored/deleted!
const tripoint pos( gridx, gridy, gridz );
const size_t grid_pos = get_nonant( pos );
if( !save_results || MAPBUFFER.lookup_submap( abs_sub.xy() + pos ) == nullptr ) {
setsubmap( grid_pos, new submap() );
} else {
setsubmap( grid_pos, MAPBUFFER.lookup_submap( abs_sub.xy() + pos ) );
}
}
}
}
oter_id terrain_type = overmap_buffer.ter( p );

// This attempts to scale density of zombies inversely with distance from the nearest city.
// In other words, make city centers dense and perimeters sparse.
float density = 0.0f;
for( int i = -MON_RADIUS; i <= MON_RADIUS; i++ ) {
for( int j = -MON_RADIUS; j <= MON_RADIUS; j++ ) {
density += overmap_buffer.ter( p + point( i, j ) )->get_mondensity();
}
const tripoint_abs_sm p_sm_base = project_to<coords::sm>( p );
std::vector<submap *> saved_overlay;
saved_overlay.reserve( 4 );
for( size_t index = 0; index <= 3; index++ ) {
saved_overlay.emplace_back( nullptr );
}
density = density / 100;

mapgendata dat( p, *this, density, when, nullptr );
draw_map( dat );
// We're generating all Z levels in one go to be able to account for dependencies
// between levels. We iterate from the top down based on the assumption it is
// more common to add overlays on other Z levels upwards than downwards, so
// going downwards we can immediately apply overlays onto the already generated
// map, while overlays further down will have to be reapplied when the basic
// map exists.

// At some point, we should add region information so we can grab the appropriate extras
map_extras &this_ex = region_settings_map["default"].region_extras[terrain_type->get_extras()];
map_extras ex = this_ex.filtered_by( dat );
if( this_ex.chance > 0 && ex.values.empty() && !this_ex.values.empty() ) {
DebugLog( D_WARNING, D_MAP_GEN ) << "Overmap terrain " << terrain_type->get_type_id().str() <<
" (extra type \"" << terrain_type->get_extras() <<
"\") zlevel = " << p.z() <<
" is out of range of all assigned map extras. Skipping map extra generation.";
} else if( ex.chance > 0 && one_in( ex.chance ) ) {
map_extra_id *extra = ex.values.pick();
if( extra == nullptr ) {
debugmsg( "failed to pick extra for type %s (ter = %s)", terrain_type->get_extras(),
terrain_type->get_type_id().str() );
} else {
MapExtras::apply_function( *ex.values.pick(), *this, tripoint_abs_sm( abs_sub ) );
for( int gridz = OVERMAP_HEIGHT; gridz >= -OVERMAP_DEPTH; gridz-- ) {
const tripoint_abs_sm p_sm = {p_sm_base.xy(), gridz};
set_abs_sub( p_sm );

for( int gridx = 0; gridx <= 1; gridx++ ) {
for( int gridy = 0; gridy <= 1; gridy++ ) {
const tripoint pos( gridx, gridy, gridz );
const size_t grid_pos = get_nonant( pos );
if( MAPBUFFER.lookup_submap( abs_sub.xy() + pos ) == nullptr &&
!getsubmap( grid_pos )->is_uniform() ) {
saved_overlay[gridx + gridy * 2] = getsubmap( grid_pos );
setsubmap( grid_pos, new submap() );
}
}
}
}

const overmap_static_spawns &spawns = terrain_type->get_static_spawns();
oter_id terrain_type = overmap_buffer.ter( tripoint_abs_omt( p.xy(), gridz ) );

float spawn_density = 1.0f;
if( MonsterGroupManager::is_animal( spawns.group ) ) {
spawn_density = get_option< float >( "SPAWN_ANIMAL_DENSITY" );
} else {
spawn_density = get_option< float >( "SPAWN_DENSITY" );
}
// This attempts to scale density of zombies inversely with distance from the nearest city.
// In other words, make city centers dense and perimeters sparse.
float density = 0.0f;
for( int i = -MON_RADIUS; i <= MON_RADIUS; i++ ) {
for( int j = -MON_RADIUS; j <= MON_RADIUS; j++ ) {
density += overmap_buffer.ter( p + point( i, j ) )->get_mondensity();
}
}
density = density / 100;

// Apply a multiplier to the number of monsters for really high densities.
float odds_after_density = spawns.chance * spawn_density;
const float max_odds = 100 - ( 100 - spawns.chance ) / 2.0f;
float density_multiplier = 1.0f;
if( odds_after_density > max_odds ) {
density_multiplier = 1.0f * odds_after_density / max_odds;
odds_after_density = max_odds;
}
const int spawn_count = roll_remainder( density_multiplier );
mapgendata dat( { p.xy(), gridz}, *this, density, when, nullptr );
if( !save_results || MAPBUFFER.lookup_submap( p_sm ) == nullptr ) {
draw_map( dat );
}

if( spawns.group && x_in_y( odds_after_density, 100 ) ) {
int pop = spawn_count * rng( spawns.population.min, spawns.population.max );
for( ; pop > 0; pop-- ) {
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( spawns.group, &pop );
for( const MonsterGroupResult &mgr : spawn_details ) {
if( !mgr.name ) {
continue;
}
if( const std::optional<tripoint> pt =
random_point( *this, [this]( const tripoint & n ) {
return passable( n );
} ) ) {
const tripoint_bub_ms pnt = tripoint_bub_ms( pt.value() );
add_spawn( mgr, pnt );
// Merge the overlays generated earlier into the current Z level now we have the base map on it.
for( int gridx = 0; gridx <= 1; gridx++ ) {
for( int gridy = 0; gridy <= 1; gridy++ ) {
const tripoint pos( gridx, gridy, gridz );
const size_t index = gridx + gridy * 2;
if( saved_overlay.at( index ) != nullptr ) {
const size_t grid_pos = get_nonant( pos );
getsubmap( grid_pos )->merge_submaps( saved_overlay.at( index ), true );
delete saved_overlay.at( index );
saved_overlay[index] = nullptr;
}
}
}
}

// Okay, we know who our neighbors are. Let's draw!
// And finally save used submaps and delete the rest.
// At some point, we should add region information so we can grab the appropriate extras
map_extras &this_ex = region_settings_map["default"].region_extras[terrain_type->get_extras()];
map_extras ex = this_ex.filtered_by( dat );
if( this_ex.chance > 0 && ex.values.empty() && !this_ex.values.empty() ) {
DebugLog( D_WARNING, D_MAP_GEN ) << "Overmap terrain " << terrain_type->get_type_id().str() <<
" (extra type \"" << terrain_type->get_extras() <<
"\") zlevel = " << p.z() <<
" is out of range of all assigned map extras. Skipping map extra generation.";
} else if( ex.chance > 0 && one_in( ex.chance ) ) {
map_extra_id *extra = ex.values.pick();
if( extra == nullptr ) {
debugmsg( "failed to pick extra for type %s (ter = %s)", terrain_type->get_extras(),
terrain_type->get_type_id().str() );
} else {
MapExtras::apply_function( *ex.values.pick(), *this, tripoint_abs_sm( abs_sub ) );
}
}

for( int i = 0; i < my_MAPSIZE; i++ ) {
for( int j = 0; j < my_MAPSIZE; j++ ) {
for( int k = -OVERMAP_DEPTH; k <= OVERMAP_HEIGHT; k++ ) {
dbg( D_INFO ) << "map::generate: submap (" << i << "," << j << "," << k << ")";
const overmap_static_spawns &spawns = terrain_type->get_static_spawns();

const tripoint pos( i, j, k );
submap *old_sub = MAPBUFFER.lookup_submap( abs_sub.xy() + tripoint{ i, j, k } );
submap *new_sub = getsubmap( get_nonant( tripoint{ i, j, k } ) );
float spawn_density = 1.0f;
if( MonsterGroupManager::is_animal( spawns.group ) ) {
spawn_density = get_option< float >( "SPAWN_ANIMAL_DENSITY" );
} else {
spawn_density = get_option< float >( "SPAWN_DENSITY" );
}

// We have to merge the data generated now with data generated earlier through
// Z level offsets.
// Apply a multiplier to the number of monsters for really high densities.
float odds_after_density = spawns.chance * spawn_density;
const float max_odds = 100 - ( 100 - spawns.chance ) / 2.0f;
float density_multiplier = 1.0f;
if( odds_after_density > max_odds ) {
density_multiplier = 1.0f * odds_after_density / max_odds;
odds_after_density = max_odds;
}
const int spawn_count = roll_remainder( density_multiplier );

if( old_sub != nullptr && !new_sub->is_uniform() ) {
if( k == p.z() ) {
old_sub->merge_submaps( new_sub, false );
} else {
// We've generated an overlay and merge it with other overlays if this
// is at a higher Z level, or with the base map (possibly overlayed) if below.
old_sub->merge_submaps( new_sub, true );
if( spawns.group && x_in_y( odds_after_density, 100 ) ) {
int pop = spawn_count * rng( spawns.population.min, spawns.population.max );
for( ; pop > 0; pop-- ) {
std::vector<MonsterGroupResult> spawn_details =
MonsterGroupManager::GetResultFromGroup( spawns.group, &pop );
for( const MonsterGroupResult &mgr : spawn_details ) {
if( !mgr.name ) {
continue;
}
if( const std::optional<tripoint> pt =
random_point( *this, [this]( const tripoint & n ) {
return passable( n );
} ) ) {
const tripoint_bub_ms pnt = tripoint_bub_ms( pt.value() );
add_spawn( mgr, pnt );
}
}
}
}
}

if( i <= 1 && j <= 1 && k == p_sm.z() ) {
if( old_sub == nullptr ) {
saven( pos );
} else {
delete new_sub;
}
} else {
if( old_sub == nullptr && !new_sub->is_uniform() ) {
saven( pos );
if( save_results ) {
for( int gridx = 0; gridx < my_MAPSIZE; gridx++ ) {
for( int gridy = 0; gridy < my_MAPSIZE; gridy++ ) {
for( int gridz = -OVERMAP_DEPTH; gridz <= OVERMAP_HEIGHT; gridz++ ) {
const tripoint pos( gridx, gridy, gridz );
const size_t grid_pos = get_nonant( pos );
if( gridx <= 1 && gridy <= 1 ) {
if( MAPBUFFER.lookup_submap( abs_sub.xy() + pos ) == nullptr ) {
saven( {gridx, gridy, gridz} );
}
} else {
delete new_sub;
if( MAPBUFFER.lookup_submap( abs_sub.xy() + pos ) == nullptr ) {
delete getsubmap( grid_pos );
}
}
}
}
Expand Down

0 comments on commit b469783

Please sign in to comment.