diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index e149d07b47..8403cc44ce 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -265,6 +265,8 @@ static void SortSaveGameList(FileList &file_list) QSortT(file_list.Get(sort_start), s_amount, CompareFiosItems); } +void SaveGameConfirmationCallback(Window *w, bool confirmed); + struct SaveLoadWindow : public Window { private: static const uint EDITBOX_MAX_SIZE = 50; @@ -281,6 +283,8 @@ private: QueryString filter_editbox; ///< Filter editbox; SmallVector fios_items_shown; ///< Map of the filtered out fios items + friend void SaveGameConfirmationCallback(Window *w, bool confirmed); + public: /** Generate a default save filename. */ @@ -463,6 +467,14 @@ public: y = DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, r.bottom - WD_FRAMERECT_BOTTOM, _load_check_data.error, TC_RED); } else { + /* Warning if save unique id differ when saving */ + if (this->fop == SLO_SAVE && + _load_check_data.settings.game_creation.generation_unique_id != 0 && /* Don't warn if the save has no id (old save) */ + _load_check_data.settings.game_creation.generation_unique_id != _settings_game.game_creation.generation_unique_id) { + y = DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, + y, r.bottom - WD_FRAMERECT_BOTTOM, STR_SAVELOAD_DIFFERENT_ID); + } + /* Mapsize */ SetDParam(0, _load_check_data.map_size_x); SetDParam(1, _load_check_data.map_size_y); @@ -731,8 +743,15 @@ public: } } else if (this->IsWidgetLowered(WID_SL_SAVE_GAME)) { // Save button clicked if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) { - _switch_mode = SM_SAVE_GAME; - FiosMakeSavegameName(_file_to_saveload.name, this->filename_editbox.text.buf, lastof(_file_to_saveload.name)); + if (_load_check_data.settings.game_creation.generation_unique_id != 0 && /* Don't warn if the save has no id (old save) */ + _load_check_data.settings.game_creation.generation_unique_id != _settings_game.game_creation.generation_unique_id) { + /* The save has a different id to the current game */ + /* Show a caption box asking whether the user is sure to overwrite the save */ + ShowQuery(STR_SAVEGAME_UNMATCHING_ID_CAPTION, STR_SAVEGAME_UNMATCHING_ID_CONFIRMATION_TEXT, this, SaveGameConfirmationCallback); + } else { + /* We can safely overwrite the save */ + SaveGameConfirmationCallback(this, true); + } } else { _switch_mode = SM_SAVE_HEIGHTMAP; FiosMakeHeightmapName(_file_to_saveload.name, this->filename_editbox.text.buf, lastof(_file_to_saveload.name)); @@ -864,6 +883,19 @@ static WindowDesc _save_dialog_desc( _nested_save_dialog_widgets, lengthof(_nested_save_dialog_widgets) ); +/** + * Callback function for the savegame 'are you sure you want to overwrite save' window + * @param w Window which is calling this callback + * @param confirmed boolean value, true when yes was clicked, false otherwise + */ +void SaveGameConfirmationCallback(Window *w, bool confirmed) +{ + if (confirmed) { + _switch_mode = SM_SAVE_GAME; + FiosMakeSavegameName(_file_to_saveload.name, dynamic_cast(w)->filename_editbox.text.buf, lastof(_file_to_saveload.name)); + } +} + /** * Launch save/load dialog in the given mode. * @param abstract_filetype Kind of file to handle. diff --git a/src/genworld.cpp b/src/genworld.cpp index 5cdb129b9c..89f13622c2 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -105,6 +105,11 @@ static void _GenerateWorld(void *) /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); _random.SetSeed(_settings_game.game_creation.generation_seed); + + /* Generates a unique id for the savegame, to avoid accidentally overwriting a save */ + /* We keep id 0 for old savegames that don't have an id */ + _settings_game.game_creation.generation_unique_id = _interactive_random.Next(UINT32_MAX - 1) + 1; /* Generates between [1,UINT32_MAX] */ + SetGeneratingWorldProgress(GWP_MAP_INIT, 2); SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); diff --git a/src/lang/english.txt b/src/lang/english.txt index 9d20dd8609..3501c52283 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3297,6 +3297,7 @@ STR_SAVELOAD_DETAIL_NOT_AVAILABLE :{BLACK}No infor STR_SAVELOAD_DETAIL_COMPANY_INDEX :{SILVER}{COMMA}: {WHITE}{STRING1} STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: {WHITE}{STRING} STR_SAVELOAD_FILTER_TITLE :{BLACK}Filter string: +STR_SAVELOAD_DIFFERENT_ID :{RED}Not the same game as you are saving. STR_SAVELOAD_OSKTITLE :{BLACK}Enter a name for the savegame @@ -4994,6 +4995,8 @@ STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE :File not writea STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED :Data integrity check failed STR_GAME_SAVELOAD_NOT_AVAILABLE : STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}Game was saved in version without tram support. All trams have been removed +STR_SAVEGAME_UNMATCHING_ID_CAPTION :{WHITE}Caution! +STR_SAVEGAME_UNMATCHING_ID_CONFIRMATION_TEXT :{YELLOW}The savegame you are about to overwrite is not the same as the current one.{}Are you sure you want to do this? STR_GAME_SAVELOAD_ERROR_HUGE_AIRPORTS_PRESENT :Savegame uses huge airports STR_GAME_SAVELOAD_ERROR_HELI_OILRIG_BUG :Savegame has a helicopter on approach to a buggy oil rig diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 2f6ad4e882..74e412342c 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3597,6 +3597,12 @@ bool AfterLoadGame() } } + if (SlXvIsFeatureMissing(XSLFI_SAVEGAME_UNIQUE_ID)) { + /* Generate a random id for savegames that didn't have one */ + /* We keep id 0 for old savegames that don't have an id */ + _settings_game.game_creation.generation_unique_id = _interactive_random.Next(UINT32_MAX-1) + 1; /* Generates between [1;UINT32_MAX] */ + } + /* Road stops is 'only' updating some caches */ AfterLoadRoadStops(); AfterLoadLabelMaps(); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 827c369359..fcad9fc173 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -93,6 +93,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_BUY_LAND_RATE_LIMIT, XSCF_NULL, 1, 1, "buy_land_rate_limit", NULL, NULL, NULL }, { XSLFI_DUAL_RAIL_TYPES, XSCF_NULL, 1, 1, "dual_rail_types", NULL, NULL, NULL }, { XSLFI_CONSIST_SPEED_RD_FLAG, XSCF_NULL, 1, 1, "consist_speed_rd_flag", NULL, NULL, NULL }, + { XSLFI_SAVEGAME_UNIQUE_ID, XSCF_IGNORABLE_ALL, 1, 1, "savegame_unique_id", NULL, NULL, NULL }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 41d2c58320..6ed72facd6 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -67,6 +67,7 @@ enum SlXvFeatureIndex { XSLFI_BUY_LAND_RATE_LIMIT, ///< Buy land rate limit XSLFI_DUAL_RAIL_TYPES, ///< Two rail-types per tile XSLFI_CONSIST_SPEED_RD_FLAG, ///< Consist speed reduction flag + XSLFI_SAVEGAME_UNIQUE_ID, ///< Savegame unique ID XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk diff --git a/src/settings_type.h b/src/settings_type.h index 588f4c60a2..bc0e656917 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -330,6 +330,7 @@ struct NetworkSettings { /** Settings related to the creation of games. */ struct GameCreationSettings { uint32 generation_seed; ///< noise seed for world generation + uint32 generation_unique_id; ///< random id to differentiate savegames Year starting_year; ///< starting date uint8 map_x; ///< X size of map uint8 map_y; ///< Y size of map diff --git a/src/table/settings.ini b/src/table/settings.ini index bed9855335..d11c5698a1 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2848,6 +2848,16 @@ min = 0 max = UINT32_MAX cat = SC_EXPERT +[SDT_VAR] +base = GameSettings +var = game_creation.generation_unique_id +type = SLE_UINT32 +from = 0 +def = 0 +min = 0 +max = UINT32_MAX +patxname = ""savegame_unique_id.game_creation.generation_unique_id"" + [SDT_VAR] base = GameSettings var = game_creation.tree_placer