Feature: Add default road and tram selection (#15585)

- Merged with the existing default rail type selection
This commit is contained in:
Matt C
2026-06-06 14:23:08 +01:00
committed by GitHub
parent a85d10bf7c
commit 6bc486a578
11 changed files with 108 additions and 24 deletions
+3 -1
View File
@@ -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) {
+5 -5
View File
@@ -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
+4 -4
View File
@@ -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<uint, RAILTYPE_END> 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<RailType>::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<RailType>::const_reverse_iterator it = std::find_if(_sorted_railtypes.rbegin(), _sorted_railtypes.rend(),
[](RailType r){ return HasRailTypeAvail(_local_company, r); });
+46
View File
@@ -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]<typename It>(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<uint, ROADTYPE_END> count{};
for (RoadType rt : GetMaskForRoadTramType(rtt)) {
count[rt] = c->infrastructure.road[rt];
}
auto best = static_cast<RoadType>(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);
}
+1
View File
@@ -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 */
+1 -1
View File
@@ -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"));
+28
View File
@@ -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<int32_t> 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<int64_t>(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<DefaultRailRoadType>(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");
+2
View File
@@ -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<int32_t> 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;
+8 -1
View File
@@ -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 \<ENTER\>, 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
+10 -8
View File
@@ -20,6 +20,7 @@ static void SpriteZoomMinChanged(int32_t new_value);
static constexpr std::initializer_list<std::string_view> _osk_activation{"disabled"sv, "double"sv, "single"sv, "immediately"sv};
static constexpr std::initializer_list<std::string_view> _savegame_date{"long"sv, "short"sv, "iso"sv};
static constexpr std::initializer_list<std::string_view> _right_click_close{"no"sv, "yes"sv, "except sticky"sv};
static constexpr std::initializer_list<std::string_view> _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]
-4
View File
@@ -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 {