diff --git a/src/lang/english.txt b/src/lang/english.txt index eba78574a4..01d86510c5 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1811,6 +1811,8 @@ STR_CONFIG_SETTING_NOSERVICE :Disable servici STR_CONFIG_SETTING_NOSERVICE_HELPTEXT :When enabled, vehicles do not get serviced if they cannot break down STR_CONFIG_SETTING_WAGONSPEEDLIMITS :Enable wagon speed limits: {STRING2} STR_CONFIG_SETTING_WAGONSPEEDLIMITS_HELPTEXT :When enabled, also use speed limits of wagons for deciding the maximum speed of a train +STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTION :Enable train speed adaption: {STRING2} +STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTION_HELPTEXT :If enabled, faster trains behind slower trains adjust their speed. STR_CONFIG_SETTING_SLOW_ROAD_VEHICLES_IN_CURVES :Road vehicles slow down in curves: {STRING2} STR_CONFIG_SETTING_SLOW_ROAD_VEHICLES_IN_CURVES_HELPTEXT :When enabled, road vehicles slow down in curves. (Only with realistic acceleration) STR_CONFIG_SETTING_DISABLE_ELRAILS :Disable electric rails: {STRING2} diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 48416d33a3..179a12e642 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1923,6 +1923,7 @@ static SettingsContainer &GetSettingsTree() physics->Add(new SettingEntry("vehicle.train_braking_model")); physics->Add(new SettingEntry("vehicle.train_slope_steepness")); physics->Add(new SettingEntry("vehicle.wagon_speed_limits")); + physics->Add(new SettingEntry("vehicle.train_speed_adaption")); physics->Add(new SettingEntry("vehicle.freight_trains")); physics->Add(new SettingEntry("vehicle.roadveh_acceleration_model")); physics->Add(new SettingEntry("vehicle.roadveh_slope_steepness")); diff --git a/src/settings_type.h b/src/settings_type.h index e2f245a7d1..95fbe4b1f8 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -572,6 +572,7 @@ struct VehicleSettings { uint8 train_slope_steepness; ///< Steepness of hills for trains when using realistic acceleration uint8 roadveh_slope_steepness; ///< Steepness of hills for road vehicles when using realistic acceleration bool wagon_speed_limits; ///< enable wagon speed limits + bool train_speed_adaption; ///< Faster trains behind slower trains slow down bool slow_road_vehicles_in_curves; ///< Road vehicles slow down in curves. bool disable_elrails; ///< when true, the elrails are disabled UnitID max_trains; ///< max trains in game per company diff --git a/src/table/settings.ini b/src/table/settings.ini index f1c3aec765..007fe47a88 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -1676,9 +1676,18 @@ cat = SC_BASIC patxname = ""slow_road_vehicles_in_curves.vehicle.slow_road_vehicles_in_curves"" ;; vehicle.train_speed_adaption -[SDT_NULL] -length = 1 +[SDT_XREF] extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP) +xref = ""vehicle.train_speed_adaption"" + +[SDT_BOOL] +base = GameSettings +var = vehicle.train_speed_adaption +def = true +str = STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTION +strhelp = STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTION_HELPTEXT +cat = SC_EXPERT +patxname = ""train_speed_adaption.vehicle.train_speed_adaption"" [SDT_BOOL] base = GameSettings diff --git a/src/train.h b/src/train.h index 6fd5186dd6..c33f7317e4 100644 --- a/src/train.h +++ b/src/train.h @@ -190,6 +190,7 @@ struct Train FINAL : public GroundVehicle { }; private: + int GetAtcMaxSpeed(int current_max_speed) const; MaxSpeedInfo GetCurrentMaxSpeedInfoInternal(bool update_state) const; public: diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 2e0a813910..6238fb13b8 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -951,6 +951,80 @@ static void AdvanceLookAheadPosition(Train *v) } } +/** + * Calculates the maximum speed based on any train in front of this train. + */ +int Train::GetAtcMaxSpeed(int current_max_speed) const +{ + if (!(this->vehstatus & VS_CRASHED) && _settings_game.vehicle.train_speed_adaption) { + int atc_speed = current_max_speed; + + CFollowTrackRail ft(this); + Trackdir old_td = this->GetVehicleTrackdir(); + + if (ft.Follow(this->tile, this->GetVehicleTrackdir())) { + /* Basic idea: Follow the track for 20 tiles or 3 signals (i.e. at most two signal blocks) looking for other trains. */ + /* If we find one (that meets certain restrictions), we limit the max speed to the speed of that train. */ + int num_tiles = 0; + int num_signals = 0; + + do { + old_td = ft.m_old_td; + + /* If we are on a depot or rail station tile stop searching */ + if (IsDepotTile(ft.m_new_tile) || IsRailStationTile(ft.m_new_tile)) + break; + + /* Increment signal counter if we're on a signal */ + if (IsTileType(ft.m_new_tile, MP_RAILWAY) && ///< Tile has rails + KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE && ///< Tile has exactly *one* track + HasSignalOnTrack(ft.m_new_tile, TrackBitsToTrack(TrackdirBitsToTrackBits(ft.m_new_td_bits)))) { ///< Tile has signal + num_signals++; + } + + /* Check if tile has train/is reserved */ + if (KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE && ///< Tile has exactly *one* track + HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits))) { ///< Tile is reserved + Train* other_train = GetTrainForReservation(ft.m_new_tile, TrackBitsToTrack(TrackdirBitsToTrackBits(ft.m_new_td_bits))); + + + if (other_train != nullptr && + other_train != this && ///< Other train is not this train + other_train->GetAccelerationStatus() != AS_BRAKE) { ///< Other train is not braking + atc_speed = other_train->GetCurrentSpeed(); + break; + } + } + + /* Decide what in direction to continue: reservation, straight or "first/only" direction. */ + /* Abort if there's no reservation even though the tile contains multiple tracks. */ + const TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile)); + + if (reserved != TRACKDIR_BIT_NONE) { + // There is a reservation to follow. + old_td = FindFirstTrackdir(reserved); + } + else if (KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) { + // Tile has more than one track and we have no reservation. Bail out. + break; + } + else { + // There was no reservation but there is only one direction to follow, so follow it. + old_td = FindFirstTrackdir(ft.m_new_td_bits); + } + + num_tiles++; + } while (num_tiles < 20 && num_signals < 3 && ft.Follow(ft.m_new_tile, old_td)); + } + + /* Check that the ATC speed is sufficiently large. + Avoids assertion error in UpdateSpeed(). */ + current_max_speed = std::max(25, std::min(current_max_speed, atc_speed)); + } + + return current_max_speed; +} + /** * Calculates the maximum speed information of the vehicle under its current conditions. * @return Maximum speed information of the vehicle. @@ -961,6 +1035,8 @@ Train::MaxSpeedInfo Train::GetCurrentMaxSpeedInfoInternal(bool update_state) con this->gcache.cached_max_track_speed : std::min(this->tcache.cached_max_curve_speed, this->gcache.cached_max_track_speed); + max_speed = GetAtcMaxSpeed(max_speed); + if (this->current_order.IsType(OT_LOADING_ADVANCE)) max_speed = std::min(max_speed, 15); int advisory_max_speed = max_speed;