Skip to content

Commit

Permalink
Merge pull request #74497 from Procyonae/FieldMigration
Browse files Browse the repository at this point in the history
Field type migration
  • Loading branch information
dseguin authored Jun 13, 2024
2 parents 1b66f65 + 535d3c0 commit e72c61c
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 23 deletions.
7 changes: 7 additions & 0 deletions data/mods/TEST_DATA/field_type_migrations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"type": "field_type_migration",
"from_field": "test_fd_migration_old_id",
"to_field": "test_fd_migration_new_id"
}
]
5 changes: 5 additions & 0 deletions data/mods/TEST_DATA/fields.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,10 @@
"display_items": false,
"display_field": true,
"looks_like": "fd_fog"
},
{
"id": "test_fd_migration_new_id",
"type": "field_type",
"intensity_levels": [ { "name": "migrated" } ]
}
]
34 changes: 34 additions & 0 deletions doc/OBSOLETION_AND_MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,40 @@ Move multiple ids that don't need to be unique any more to a single id
}
```

# Field migration

Field migration replaces the provided id as submaps are loaded. You can use `fd_null` with `to_field` to remove the field entirely without creating errors.

```json
{
"type": "field_type_migration", // Mandatory. String. Must be "field_type_migration"
"from_field": "t_old_field", // Mandatory. String. Id of the field to replace.
"to_field": "f_new_field", // Mandatory. String. Id of the new field to place.
},
```

## Examples

Migrate an id

```json
{
"type": "field_type_migration",
"from_field": "fd_being_migrated_id",
"to_field": "fd_new_id"
}
```

Errorlessly obsolete an id

```json
{
"type": "field_type_migration",
"from_field": "fd_being_obsoleted_id",
"to_field": "fd_null"
}
```

# Overmap terrain migration

Overmap terrain migration replaces the location, if it's not generated, and replaces the entry shown on your map even if it's already generated. If you need the map to be removed without alternative, use `omt_obsolete`
Expand Down
5 changes: 4 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ void DynamicDataLoader::initialize()
add( "relic_procgen_data", &relic_procgen_data::load_relic_procgen_data );
add( "effect_on_condition", &effect_on_conditions::load );
add( "field_type", &field_types::load );
add( "field_type_migration", &field_type_migrations::load );
add( "weather_type", &weather_types::load );
add( "ammo_effect", &ammo_effects::load );
add( "emit", &emit::load_emit );
Expand Down Expand Up @@ -582,6 +583,7 @@ void DynamicDataLoader::unload_data()
faction_template::reset();
faults::reset();
field_types::reset();
field_type_migrations::reset();
gates::reset();
harvest_drop_type::reset();
harvest_list::reset();
Expand Down Expand Up @@ -800,6 +802,7 @@ void DynamicDataLoader::check_consistency( loading_ui &ui )
{ _( "Weapon Categories" ), &weapon_category::verify_weapon_categories },
{ _( "Effect on conditions" ), &effect_on_conditions::check_consistency },
{ _( "Field types" ), &field_types::check_consistency },
{ _( "Field type migrations" ), &field_type_migrations::check },
{ _( "Ammo effects" ), &ammo_effects::check_consistency },
{ _( "Emissions" ), &emit::check_consistency },
{ _( "Effect types" ), &effect_type::check_consistency },
Expand All @@ -825,6 +828,7 @@ void DynamicDataLoader::check_consistency( loading_ui &ui )
},
{ _( "Monster groups" ), &MonsterGroupManager::check_group_definitions },
{ _( "Furniture and terrain" ), &check_furniture_and_terrain },
{ _( "Furniture and terrain migrations" ), &ter_furn_migrations::check },
{ _( "Constructions" ), &check_constructions },
{ _( "Crafting recipes" ), &recipe_dictionary::check_consistency },
{ _( "Professions" ), &profession::check_definitions },
Expand All @@ -834,7 +838,6 @@ void DynamicDataLoader::check_consistency( loading_ui &ui )
{ _( "Mutations" ), &mutation_branch::check_consistency },
{ _( "Mutation categories" ), &mutation_category_trait::check_consistency },
{ _( "Region settings" ), check_region_settings },
{ _( "Terrain/Furniture migrations" ), &ter_furn_migrations::check },
{ _( "Overmap land use codes" ), &overmap_land_use_codes::check_consistency },
{ _( "Overmap connections" ), &overmap_connections::check_consistency },
{ _( "Overmap terrain" ), &overmap_terrains::check_consistency },
Expand Down
15 changes: 14 additions & 1 deletion src/mapdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,20 @@ void verify_terrain();
class ter_furn_migrations
{
public:
/** Handler for loading "ter_furn_migrations" type of json object */
/** Handler for loading "ter_furn_migration" type of json object */
static void load( const JsonObject &jo );

/** Clears migration list */
static void reset();

/** Checks migrations */
static void check();
};

class field_type_migrations
{
public:
/** Handler for loading "field_type_migration" type of json object */
static void load( const JsonObject &jo );

/** Clears migration list */
Expand Down
59 changes: 41 additions & 18 deletions src/savegame_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4621,6 +4621,31 @@ void ter_furn_migrations::check()
}
}

static std::unordered_map<field_type_str_id, field_type_str_id> field_migrations;

void field_type_migrations::load( const JsonObject &jo )
{
field_type_str_id from_field;
field_type_str_id to_field;
mandatory( jo, true, "from_field", from_field );
mandatory( jo, true, "to_field", to_field );
field_migrations.insert( std::make_pair( from_field, to_field ) );
}

void field_type_migrations::reset()
{
field_migrations.clear();
}

void field_type_migrations::check()
{
for( const auto &migration : field_migrations ) {
if( !migration.second.is_valid() ) {
debugmsg( "field_type_migration specifies invalid to_field id '%s'", migration.second.c_str() );
}
}
}

void submap::store( JsonOut &jsout ) const
{
jsout.member( "turn_last_touched", last_touched );
Expand Down Expand Up @@ -5007,25 +5032,23 @@ void submap::load( const JsonValue &jv, const std::string &member_name, int vers
int j = fields_json.next_int();
JsonArray field_json = fields_json.next_array();
while( field_json.has_more() ) {
// TODO: Check enum->string migration below
int type_int = 0;
std::string type_str;
JsonValue type_value = field_json.next_value();
if( type_value.test_int() ) {
type_int = type_value.get_int();
} else {
type_str = type_value.get_string();
}
int intensity = field_json.next_int();
int age = field_json.next_int();
field_type_id ft;
if( !type_str.empty() ) {
ft = field_type_id( type_str );
} else {
ft = field_types::get_field_type_by_legacy_enum( type_int ).id;
}
if( m->fld[i][j].add_field( ft, intensity, time_duration::from_turns( age ) ) ) {
field_count++;
if( type_value.test_string() ) {
field_type_str_id ft = field_type_str_id( type_value.get_string() );
const int intensity = field_json.next_int();
const int age = field_json.next_int();
if( auto it = field_migrations.find( ft ); it != field_migrations.end() ) {
ft = it->second;
}
if( !ft.is_valid() ) {
debugmsg( "invalid field_type_str_id '%s'", ft.c_str() );
} else if( ft != field_type_str_id::NULL_ID() &&
m->fld[i][j].add_field( ft.id(), intensity, time_duration::from_turns( age ) ) ) {
field_count++;
}
} else { // Handle removed int enum method
field_json.next_value(); // Skip intensity
field_json.next_value(); // Skip age
}
}
}
Expand Down
56 changes: 53 additions & 3 deletions tests/submap_load_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
static const construction_str_id construction_constr_ground_cable( "constr_ground_cable" );
static const construction_str_id construction_constr_rack_coat( "constr_rack_coat" );

static const field_type_str_id field_test_fd_migration_new_id( "test_fd_migration_new_id" );

static const furn_str_id furn_f_bookcase( "f_bookcase" );
static const furn_str_id furn_f_coffin_c( "f_coffin_c" );
static const furn_str_id furn_f_crate_o( "f_crate_o" );
Expand Down Expand Up @@ -781,7 +783,7 @@ static std::string submap_cosmetic_ss(
" \"computers\": [ ]\n"
"}\n"
);
static std::string submap_pre_migration_ss(
static std::string submap_ter_furn_pre_migration_ss(
"{\n"
" \"version\": 32,\n"
" \"coordinates\": [ 0, 0, 0 ],\n"
Expand All @@ -804,6 +806,25 @@ static std::string submap_pre_migration_ss(
" \"computers\": [ ]\n"
"}\n"
);
static std::string submap_fd_pre_migration_ss(
"{\n"
" \"version\": 32,\n"
" \"coordinates\": [ 0, 0, 0 ],\n"
" \"turn_last_touched\": 0,\n"
" \"temperature\": 0,\n"
" \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
" \"radiation\": [ 0, 144 ],\n"
" \"furniture\": [ ],\n"
" \"items\": [ ],\n"
" \"traps\": [ ],\n"
" \"fields\": [ 0, 0, [ \"test_fd_migration_old_id\", 1, 1997 ] ],\n"
" \"cosmetics\": [ ],\n"
" \"spawns\": [ ],\n"
" \"vehicles\": [ ],\n"
" \"partial_constructions\": [ ],\n"
" \"computers\": [ ]\n"
"}\n"
);

static_assert( SEEX == 12, "Reminder to update submap tests when SEEX changes." );
static_assert( SEEY == 12, "Reminder to update submap tests when SEEY changes." );
Expand All @@ -823,7 +844,9 @@ static JsonValue submap_vehicle = json_loader::from_string( submap_vehicle_ss );
static JsonValue submap_construction = json_loader::from_string( submap_construction_ss );
static JsonValue submap_computer = json_loader::from_string( submap_computer_ss );
static JsonValue submap_cosmetic = json_loader::from_string( submap_cosmetic_ss );
static JsonValue submap_pre_migration = json_loader::from_string( submap_pre_migration_ss );
static JsonValue submap_ter_furn_pre_migration = json_loader::from_string(
submap_ter_furn_pre_migration_ss );
static JsonValue submap_fd_pre_migration = json_loader::from_string( submap_fd_pre_migration_ss );

static void load_from_jsin( submap &sm, const JsonValue &jsin )
{
Expand Down Expand Up @@ -1448,7 +1471,7 @@ TEST_CASE( "submap_computer_load", "[submap][load]" )
TEST_CASE( "submap_ter_furn_migration", "[submap][load]" )
{
submap sm;
load_from_jsin( sm, submap_pre_migration );
load_from_jsin( sm, submap_ter_furn_pre_migration );
submap_checks checks;
checks.terrain = false;
checks.furniture = false;
Expand Down Expand Up @@ -1487,3 +1510,30 @@ TEST_CASE( "submap_ter_furn_migration", "[submap][load]" )
REQUIRE( furn_sw == furn_test_f_migration_new_id );
REQUIRE( furn_se == furn_test_f_migration_new_id );
}

TEST_CASE( "submap_fd_migration", "[submap][load]" )
{
submap sm;
load_from_jsin( sm, submap_fd_pre_migration );
submap_checks checks;
checks.fields = false;

REQUIRE( is_normal_submap( sm, checks ) );

const field field_ne = sm.get_field( corner_ne );

// North east corner should have migrated from test_fd_migration_old_id to test_fd_migration_new_id
std::string fields_list;
bool found_field_new_id = false;
int total_fields = 0;
auto it = field_ne.begin();
while( it != field_ne.end() ) {
const field_type_id &type = it->first;
fields_list += type.id().str() + " ";
found_field_new_id |= type == field_test_fd_migration_new_id;
total_fields++;
it++;
}
INFO( string_format( "%d fields found: %s", total_fields, fields_list ) );
REQUIRE( ( found_field_new_id && total_fields == 1 ) );
}

0 comments on commit e72c61c

Please sign in to comment.