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

Fix duplicate OMTs, take two #68398

Merged
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
35 changes: 26 additions & 9 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2887,21 +2887,38 @@ void overmap::ter_set( const tripoint_om_omt &p, const oter_id &id )
return;
}

oter_id &val = layer[p.z() + OVERMAP_DEPTH].terrain[p.xy()];
oter_id &current_oter = layer[p.z() + OVERMAP_DEPTH].terrain[p.xy()];
const oter_type_str_id &current_type_id = current_oter->get_type_id();
const oter_type_str_id &incoming_type_id = id->get_type_id();
const bool current_type_same = current_type_id == incoming_type_id;

// Mapgen refinement can push multiple different roads over each other.
// Roads require a predecessor. A road pushed over a road might cause a
// road to be a predecessor to another road. That causes too many spawns
// to happen. So when pushing a predecessor, if the predecessor to-be-pushed
// is linear and the previous predecessor is linear, overwrite it.
// This way only the 'last' rotation/variation generated is kept.
if( id->has_flag( oter_flags::requires_predecessor ) ) {
// Stops linear fallback_predecessor maps (roads etc) spawning over themselves
std::vector<oter_id> &om_predecessors = predecessors_[p];
if( om_predecessors.empty() || !val->is_linear() ) {
om_predecessors.push_back( val );
} else {
// Collapse and keep the last linear oter of matching type
if( om_predecessors.empty() || ( !current_oter->is_linear() && !current_type_same ) ) {
// If we need a predecessor, we must have a predecessor no matter what.
// Or, if the oter to-be-pushed is not linear, push it only if the incoming oter is different.
om_predecessors.push_back( current_oter );
} else if( !current_type_same ) {
// Current oter is linear, incoming oter is different from current.
// If the last predecessor is the same type as the current type, overwrite.
// Else push the current type.
oter_id &last_predecessor = om_predecessors.back();
if( last_predecessor->get_type_id() == val->get_type_id() ) {
last_predecessor = val;
if( last_predecessor->get_type_id() == current_type_id ) {
last_predecessor = current_oter;
} else {
om_predecessors.push_back( current_oter );
}
}
// We had a predecessor, and it was the same type as the incoming one
// Don't push another copy.
}
val = id;
current_oter = id;
}

const oter_id &overmap::ter( const tripoint_om_omt &p ) const
Expand Down
133 changes: 81 additions & 52 deletions src/savegame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,46 +411,48 @@ void overmap::unserialize( const JsonObject &jsobj )
mapgen_args_index.emplace( p.first, &*it );
}
}
for( JsonMember om_member : jsobj ) {
const std::string name = om_member.name();
if( name == "layers" ) {
std::unordered_map<tripoint_om_omt, std::string> oter_id_migrations;
JsonArray layers_json = om_member;
// Extract layers first so predecessor deduplication can happen.
if( jsobj.has_member( "layers" ) ) {
std::unordered_map<tripoint_om_omt, std::string> oter_id_migrations;
JsonArray layers_json = jsobj.get_array( "layers" );

for( int z = 0; z < OVERMAP_LAYERS; ++z ) {
JsonArray layer_json = layers_json.next_array();
int count = 0;
std::string tmp_ter;
oter_id tmp_otid( 0 );
for( int j = 0; j < OMAPY; j++ ) {
for( int i = 0; i < OMAPX; i++ ) {
if( count == 0 ) {
{
JsonArray rle_terrain = layer_json.next_array();
tmp_ter = rle_terrain.next_string();
count = rle_terrain.next_int();
if( rle_terrain.has_more() ) {
rle_terrain.throw_error( 2, "Unexpected value in RLE encoding" );
}
for( int z = 0; z < OVERMAP_LAYERS; ++z ) {
JsonArray layer_json = layers_json.next_array();
int count = 0;
std::string tmp_ter;
oter_id tmp_otid( 0 );
for( int j = 0; j < OMAPY; j++ ) {
for( int i = 0; i < OMAPX; i++ ) {
if( count == 0 ) {
{
JsonArray rle_terrain = layer_json.next_array();
tmp_ter = rle_terrain.next_string();
count = rle_terrain.next_int();
if( rle_terrain.has_more() ) {
rle_terrain.throw_error( 2, "Unexpected value in RLE encoding" );
}
if( is_oter_id_obsolete( tmp_ter ) ) {
for( int p = i; p < i + count; p++ ) {
oter_id_migrations.emplace( tripoint_om_omt( p, j, z - OVERMAP_DEPTH ), tmp_ter );
}
} else if( oter_str_id( tmp_ter ).is_valid() ) {
tmp_otid = oter_id( tmp_ter );
} else {
debugmsg( "Loaded invalid oter_id '%s'", tmp_ter.c_str() );
tmp_otid = oter_omt_obsolete;
}
if( is_oter_id_obsolete( tmp_ter ) ) {
for( int p = i; p < i + count; p++ ) {
oter_id_migrations.emplace( tripoint_om_omt( p, j, z - OVERMAP_DEPTH ), tmp_ter );
}
} else if( oter_str_id( tmp_ter ).is_valid() ) {
tmp_otid = oter_id( tmp_ter );
} else {
debugmsg( "Loaded invalid oter_id '%s'", tmp_ter.c_str() );
tmp_otid = oter_omt_obsolete;
}
count--;
layer[z].terrain[i][j] = tmp_otid;
}
count--;
layer[z].terrain[i][j] = tmp_otid;
}
}
migrate_oter_ids( oter_id_migrations );
} else if( name == "region_id" ) {
}
migrate_oter_ids( oter_id_migrations );
}
for( JsonMember om_member : jsobj ) {
const std::string name = om_member.name();
if( name == "region_id" ) {
std::string new_region_id;
om_member.read( new_region_id );
if( settings->id != new_region_id ) {
Expand Down Expand Up @@ -657,30 +659,57 @@ void overmap::unserialize( const JsonObject &jsobj )
} else if( name == "predecessors" ) {
std::vector<std::pair<tripoint_om_omt, std::vector<oter_id>>> flattened_predecessors;
om_member.read( flattened_predecessors, true );
std::vector<oter_id> deduped_predecessors;
for( std::pair<tripoint_om_omt, std::vector<oter_id>> &p : flattened_predecessors ) {
// TODO remove after 0.H release.
// JSONizing roads caused some bad mapgen data to get saved to disk. Repeated redundant linear
// type omts were all saved. The new logic only pushes a linear predecessor if the predecessors
// list is empty or the incoming omt is not linear. Fixup bad saves to conform.
deduped_predecessors.reserve( p.second.size() );
for( const oter_id &id : p.second ) {
if( deduped_predecessors.empty() || !id->is_linear() ) {
deduped_predecessors.push_back( id );
} else {
oter_id &last_predecessor = deduped_predecessors.back();
if( last_predecessor->get_type_id() == id->get_type_id() ) {
last_predecessor = id;
} else {
deduped_predecessors.push_back( id );
std::vector<oter_id> om_predecessors;

for( auto& [p, serialized_predecessors] : flattened_predecessors ) {
if( !serialized_predecessors.empty() ) {
// TODO remove after 0.H release.
// JSONizing roads caused some bad mapgen data to get saved to disk. Fixup bad saves to conform.
// The logic to do this is to emulate setting overmap::set_ter repeatedly. The difference is the
// 'original' terrain is lost, all we have is a chain of predecessors.
// This doesn't matter for the sake of deduplicating predecessors.
//
// Mapgen refinement can push multiple different roads over each other.
// Roads require a predecessor. A road pushed over a road might cause a
// road to be a predecessor to another road. That causes too many spawns
// to happen. So when pushing a predecessor, if the predecessor to-be-pushed
// is linear and the previous predecessor is linear, overwrite it.
// This way only the 'last' rotation/variation generated is kept.
om_predecessors.reserve( serialized_predecessors.size() );
oter_id current_oter;
auto local_set_ter = [&]( oter_id & id ) {
const oter_type_str_id &current_type_id = current_oter->get_type_id();
const oter_type_str_id &incoming_type_id = id->get_type_id();
const bool current_type_same = current_type_id == incoming_type_id;
if( om_predecessors.empty() || ( !current_oter->is_linear() && !current_type_same ) ) {
// If we need a predecessor, we must have a predecessor no matter what.
// Or, if the oter to-be-pushed is not linear, push it only if the incoming oter is different.
om_predecessors.push_back( current_oter );
} else if( !current_type_same ) {
// Current oter is linear, incoming oter is different from current.
// If the last predecessor is the same type as the current type, overwrite.
// Else push the current type.
oter_id &last_predecessor = om_predecessors.back();
if( last_predecessor->get_type_id() == current_type_id ) {
last_predecessor = current_oter;
} else {
om_predecessors.push_back( current_oter );
}
}
current_oter = id;
};

current_oter = serialized_predecessors.front();
for( size_t i = 1; i < serialized_predecessors.size(); ++i ) {
local_set_ter( serialized_predecessors[i] );
}
local_set_ter( layer[p.z() + OVERMAP_DEPTH].terrain[p.xy()] );
}
predecessors_.insert_or_assign( p.first, std::move( deduped_predecessors ) );
predecessors_.insert_or_assign( p, std::move( om_predecessors ) );

// Reuse allocations because it's a good habit.
deduped_predecessors = std::move( p.second );
deduped_predecessors.clear();
om_predecessors = std::move( serialized_predecessors );
om_predecessors.clear();
}
}
}
Expand Down
Loading