diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 877017a48a..f739b9f421 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -41,6 +41,7 @@ #include "timer/timer_game_economy.h" #include "timer/timer_game_tick.h" #include "timer/timer_window.h" +#include "road_gui.h" #include "widgets/statusbar_widget.h" @@ -141,8 +142,9 @@ void SetLocalCompany(CompanyID new_company, bool switching_game) } if (switching_company || switching_game) { - /* Update the default rail based on most used */ + /* Update the default rail and road types */ SetDefaultRailGui(); + SetDefaultRoadGui(); } if (!switching_game) { diff --git a/src/lang/english.txt b/src/lang/english.txt index 8edb01894c..7ef7b8e32e 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1805,12 +1805,12 @@ STR_CONFIG_SETTING_TIMETABLE_SHOW_ARRIVAL_DEPARTURE_HELPTEXT :Display anticip STR_CONFIG_SETTING_QUICKGOTO :Quick creation of vehicle orders: {STRING2} STR_CONFIG_SETTING_QUICKGOTO_HELPTEXT :Pre-select the 'goto cursor' when opening the orders window -STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE :Default rail type (after new game/game load): {STRING2} -STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_HELPTEXT :Rail type to select after starting or loading a game. 'first available' selects the oldest type of tracks, 'last available' selects the newest type of tracks, and 'most used' selects the type which is currently most in use +STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE :Default rail, road, and tram type (after new game/game load): {STRING2} +STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE_HELPTEXT :Rail, road, and tram type to select after starting or loading a game. 'first available' selects the oldest type of tracks, 'last available' selects the newest type of tracks, and 'most used' selects the type which is currently most in use ###length 3 -STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST :First available -STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_LAST :Last available -STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_MOST_USED :Most used +STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE_FIRST :First available +STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE_LAST :Last available +STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE_MOST_USED :Most used STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION :Show path reservations for tracks: {STRING2} STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION_HELPTEXT :Give reserved tracks a different colour to assist in problems with trains refusing to enter path-based blocks diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 98efdd4959..6bdd607784 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1989,8 +1989,8 @@ void SetDefaultRailGui() if (_local_company == COMPANY_SPECTATOR || !Company::IsValidID(_local_company)) return; RailType rt; - switch (_settings_client.gui.default_rail_type) { - case 2: { + switch (_settings_client.gui.default_rail_road_type) { + case DefaultRailRoadType::MostUsed: { /* Find the most used rail type */ std::array count{}; for (const auto t : Map::Iterate()) { @@ -2006,14 +2006,14 @@ void SetDefaultRailGui() /* No rail, just get the first available one */ [[fallthrough]]; } - case 0: { + case DefaultRailRoadType::FirstAvailable: { /* Use first available type */ std::vector::const_iterator it = std::find_if(_sorted_railtypes.begin(), _sorted_railtypes.end(), [](RailType r) { return HasRailTypeAvail(_local_company, r); }); rt = it != _sorted_railtypes.end() ? *it : RAILTYPE_BEGIN; break; } - case 1: { + case DefaultRailRoadType::LastAvailable: { /* Use last available type */ std::vector::const_reverse_iterator it = std::find_if(_sorted_railtypes.rbegin(), _sorted_railtypes.rend(), [](RailType r){ return HasRailTypeAvail(_local_company, r); }); diff --git a/src/road_gui.cpp b/src/road_gui.cpp index bd8906b5e3..12510f66d2 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -1927,3 +1927,49 @@ DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts) return list; } + +/** + * Determine the default road type to use for a given RoadTramType. + * @param rtt Whether to find a road or tram type. + * @return The default RoadType based on the setting. + */ +static RoadType GetDefaultRoadType(RoadTramType rtt) +{ + const auto find_available = [rtt](It begin, It end) { + const RoadTypes mask = GetMaskForRoadTramType(rtt); + auto it = std::find_if(begin, end, [&](RoadType r) { + return HasRoadTypeAvail(_local_company, r) && mask.Test(r); + }); + return it != end ? *it : ROADTYPE_BEGIN; + }; + + switch (_settings_client.gui.default_rail_road_type) { + case DefaultRailRoadType::MostUsed: { + const Company *c = Company::Get(_local_company); + std::array count{}; + for (RoadType rt : GetMaskForRoadTramType(rtt)) { + count[rt] = c->infrastructure.road[rt]; + } + auto best = static_cast(std::distance(std::begin(count), std::ranges::max_element(count))); + if (c->infrastructure.road[best] > 0) return best; + + /* No tile of this kind has been built yet, fall through to first available. */ + [[fallthrough]]; + } + case DefaultRailRoadType::FirstAvailable: + return find_available(_sorted_roadtypes.begin(), _sorted_roadtypes.end()); + case DefaultRailRoadType::LastAvailable: + return find_available(_sorted_roadtypes.rbegin(), _sorted_roadtypes.rend()); + default: + NOT_REACHED(); + } +} + +/** Set the initial (default) road & tram type to use. */ +void SetDefaultRoadGui() +{ + if (_local_company == COMPANY_SPECTATOR || !Company::IsValidID(_local_company)) return; + + _last_built_roadtype = GetDefaultRoadType(RoadTramType::Road); + _last_built_tramtype = GetDefaultRoadType(RoadTramType::Tram); +} diff --git a/src/road_gui.h b/src/road_gui.h index 838832a0dc..61cc083639 100644 --- a/src/road_gui.h +++ b/src/road_gui.h @@ -21,5 +21,6 @@ struct Window *ShowBuildRoadScenToolbar(RoadType roadtype); void ConnectRoadToStructure(TileIndex tile, DiagDirection direction); DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement = false, bool all_option = false); DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts); +void SetDefaultRoadGui(); #endif /* ROAD_GUI_H */ diff --git a/src/settingentry_gui.cpp b/src/settingentry_gui.cpp index 8bff00e9e6..9bcd219d74 100644 --- a/src/settingentry_gui.cpp +++ b/src/settingentry_gui.cpp @@ -647,7 +647,7 @@ SettingsContainer &GetSettingsTree() { construction->Add(new SettingEntry("gui.link_terraform_toolbar")); construction->Add(new SettingEntry("gui.persistent_buildingtools")); - construction->Add(new SettingEntry("gui.default_rail_type")); + construction->Add(new SettingEntry("gui.default_rail_road_type")); construction->Add(new SettingEntry("gui.semaphore_build_before")); construction->Add(new SettingEntry("gui.signal_gui_mode")); construction->Add(new SettingEntry("gui.cycle_signal_types")); diff --git a/src/settings.cpp b/src/settings.cpp index 9b7039c5e0..851bbfea93 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -176,12 +176,35 @@ enum IniFileVersion : uint32_t { IFV_AUTOSAVE_RENAME, ///< 5 PR#11143 Renamed values of autosave to be in minutes. IFV_RIGHT_CLICK_CLOSE, ///< 6 PR#10204 Add alternative right click to close windows setting. IFV_REMOVE_GENERATION_SEED, ///< 7 PR#11927 Remove "generation_seed" from configuration. + IFV_DEFAULT_RAIL_ROAD, ///< 8 PR#15585 Update default rail type setting to support road and tram tiles IFV_MAX_VERSION, ///< Highest possible ini-file version. }; const uint16_t INIFILE_VERSION = (IniFileVersion)(IFV_MAX_VERSION - 1); ///< Current ini-file version of OpenTTD. +/** + * Find whether a string was a valid int setting + * + * @param str the current value of the setting + * @param min the min value for the setting + * @param max the max value for the setting + * @return Either the parsed value, or nullopt if no value found within range. + */ +std::optional IntSettingDesc::ParseSingleValue(std::string_view str, int32_t min, uint32_t max) +{ + StringConsumer consumer{str}; + /* The actual settings value might be int32 or uint32. Read as int64 and just cast away the high bits. */ + auto value = consumer.TryReadIntegerBase(10); + /* check if it's an integer */ + if (!value.has_value()) return std::nullopt; + + if (value < min || value > max) { + return std::nullopt; + } + return value; +} + /** * Find the index value of a ONEofMANY type in a string * @param str the current value of the setting for which a value needs found @@ -1449,6 +1472,11 @@ void LoadFromConfig(bool startup) _settings_client.gui.right_click_wnd_close = old_value.value_or(false) ? RightClickClose::Yes : RightClickClose::No; } + if (generic_version < IFV_DEFAULT_RAIL_ROAD && IsConversionNeeded(generic_ini, "gui", "default_rail_type", "default_rail_road_type", &old_item)) { + auto old_value = IntSettingDesc::ParseSingleValue(*old_item->value, 0, 2); + _settings_client.gui.default_rail_road_type = static_cast(old_value.value_or(0)); + } + _grfconfig_newgame = GRFLoadConfig(generic_ini, "newgrf", false); _grfconfig_static = GRFLoadConfig(generic_ini, "newgrf-static", true); AILoadConfig(generic_ini, "ai_players"); diff --git a/src/settings_internal.h b/src/settings_internal.h index 741a557445..121ef967ad 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -288,6 +288,8 @@ struct IntSettingDesc : SettingDesc { virtual int32_t ParseValue(std::string_view str) const; std::string FormatValue(const void *object) const override; void ParseValue(const IniItem *item, void *object) const override; + static std::optional ParseSingleValue(std::string_view str, int32_t min, uint32_t max); + bool IsSameValue(const IniItem *item, void *object) const override; bool IsDefaultValue(void *object) const override; void ResetToDefault(void *object) const override; diff --git a/src/settings_type.h b/src/settings_type.h index ba1e4f4005..317e5a3525 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -202,6 +202,13 @@ enum class OskActivation : uint8_t { Immediately, ///< Focusing click already opens OSK. }; +/** How to select the default rail/road types */ +enum class DefaultRailRoadType : uint8_t { + FirstAvailable, ///< Use the first available to the player + LastAvailable, ///< Use the latest available to the player + MostUsed, ///< Use the most used by the company controlled by the player +}; + /** Settings related to the GUI and other stuff that is not saved in the savegame. */ struct GUISettings { bool sg_full_load_any; ///< new full load calculation, any cargo must be full read from pre v93 savegames @@ -226,7 +233,7 @@ struct GUISettings { bool prefer_teamchat; ///< choose the chat message target with \, true=all clients, false=your team uint8_t advanced_vehicle_list; ///< use the "advanced" vehicle list uint8_t loading_indicators; ///< show loading indicators - uint8_t default_rail_type; ///< the default rail type for the rail GUI + DefaultRailRoadType default_rail_road_type; ///< the default rail type for the rail/road/tram GUI uint8_t toolbar_pos; ///< position of toolbars, 0=left, 1=center, 2=right uint8_t statusbar_pos; ///< position of statusbar, 0=left, 1=center, 2=right uint8_t window_snap_radius; ///< windows snap at each other if closer than this diff --git a/src/table/settings/gui_settings.ini b/src/table/settings/gui_settings.ini index d068a134a6..ebccab357d 100644 --- a/src/table/settings/gui_settings.ini +++ b/src/table/settings/gui_settings.ini @@ -20,6 +20,7 @@ static void SpriteZoomMinChanged(int32_t new_value); static constexpr std::initializer_list _osk_activation{"disabled"sv, "double"sv, "single"sv, "immediately"sv}; static constexpr std::initializer_list _savegame_date{"long"sv, "short"sv, "iso"sv}; static constexpr std::initializer_list _right_click_close{"no"sv, "yes"sv, "except sticky"sv}; +static constexpr std::initializer_list _default_rail_road_type{"first available"sv, "last available"sv, "most used"sv}; static const SettingVariant _gui_settings_table[] = { [post-amble] @@ -479,16 +480,17 @@ strval = STR_CONFIG_SETTING_COMPANIES_OFF post_cb = [](auto) { MarkWholeScreenDirty(); } cat = SC_BASIC -[SDTC_VAR] -var = gui.default_rail_type +[SDTC_OMANY] +var = gui.default_rail_road_type type = SLE_UINT8 flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync, SettingFlag::GuiDropdown -def = 0 -min = 0 -max = 2 -str = STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE -strhelp = STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_HELPTEXT -strval = STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST +def = DefaultRailRoadType::FirstAvailable +min = DefaultRailRoadType::FirstAvailable +max = DefaultRailRoadType::MostUsed +full = _default_rail_road_type +str = STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE +strhelp = STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE_HELPTEXT +strval = STR_CONFIG_SETTING_DEFAULT_RAIL_ROAD_TYPE_FIRST cat = SC_BASIC [SDTC_VAR] diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index e390477c56..c614156a4c 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -2618,10 +2618,6 @@ static WindowDesc _toolb_scen_desc( /** Allocate the toolbar. */ void AllocateToolbar() { - /* Clean old GUI values; railtype is (re)set by rail_gui.cpp */ - _last_built_roadtype = ROADTYPE_ROAD; - _last_built_tramtype = ROADTYPE_TRAM; - if (_game_mode == GameMode::Editor) { new ScenarioEditorToolbarWindow(_toolb_scen_desc); } else {