diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 38ba2f104a..be29ccdd19 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -479,6 +479,7 @@ add_files(
train.h
train_cmd.cpp
train_gui.cpp
+ train_speed_adaptation.h
transparency.h
transparency_gui.cpp
transparency_gui.h
diff --git a/src/date.cpp b/src/date.cpp
index 1ba02b22a9..9e00334ee7 100644
--- a/src/date.cpp
+++ b/src/date.cpp
@@ -39,6 +39,8 @@ YearMonthDay _game_load_cur_date_ymd;
DateFract _game_load_date_fract;
uint8 _game_load_tick_skip_counter;
+extern void ClearOutOfDateSignalSpeedRestrictions();
+
/**
* Set the date.
* @param date New date
@@ -283,6 +285,7 @@ static void OnNewDay()
SetWindowWidgetDirty(WC_STATUS_BAR, 0, WID_S_LEFT);
}
EnginesDailyLoop();
+ ClearOutOfDateSignalSpeedRestrictions();
/* Refresh after possible snowline change */
SetWindowClassesDirty(WC_TOWN_VIEW);
diff --git a/src/lang/english.txt b/src/lang/english.txt
index cf10df3237..416621def3 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1818,6 +1818,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_ADAPTATION :Enable train speed adaptation: {STRING2}
+STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTATION_HELPTEXT :When enabled, faster trains adjust their speed to match slower trains in front.
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/lang/german.txt b/src/lang/german.txt
index 04ffeb6cfc..ed15610468 100644
--- a/src/lang/german.txt
+++ b/src/lang/german.txt
@@ -1743,6 +1743,10 @@ STR_CONFIG_SETTING_NOSERVICE :Wartung deaktiv
STR_CONFIG_SETTING_NOSERVICE_HELPTEXT :Schicke Fahrzeuge nicht zur Wartung, wenn Pannen ausgeschaltet sind
STR_CONFIG_SETTING_WAGONSPEEDLIMITS :Berücksichtige Waggonhöchstgeschwindigkeit: {STRING}
STR_CONFIG_SETTING_WAGONSPEEDLIMITS_HELPTEXT :Begrenze die Höchstgeschwindigkeit eines Zuges durch die jeweiligen Höchstgeschwindigkeiten der mitgeführten Waggons
+STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTATION :Zuggeschwindigkeitsanpassung aktivieren: {STRING}
+STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTATION_HELPTEXT :Wenn aktiviert, passen schnellere Züge hinter langsameren Zügen ihre Geschwindigkeit an.
+STR_CONFIG_SETTING_SLOW_ROAD_VEHICLES_IN_CURVES :Straßenfahrzeuge werden in Kurven langsamer: {STRING}
+STR_CONFIG_SETTING_SLOW_ROAD_VEHICLES_IN_CURVES_HELPTEXT :Wenn aktiviert, verlangsamen Straßenfahrzeuge in Kurven. (Nur im Beschleunigungsmodell „Realistisch“)
STR_CONFIG_SETTING_DISABLE_ELRAILS :Deaktiviere elektrifizierte Strecken: {STRING}
STR_CONFIG_SETTING_DISABLE_ELRAILS_HELPTEXT :Erlaube Elektrolokomotiven das Fahren auf nicht elektrifizierten Gleisen
diff --git a/src/misc.cpp b/src/misc.cpp
index b72d98ef9f..279df3f8cf 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -43,6 +43,7 @@
extern TileIndex _cur_tileloop_tile;
+extern void ClearAllSignalSpeedRestrictions();
extern void MakeNewgameSettingsLive();
void InitializeSound();
@@ -118,6 +119,8 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
FreeSignalPrograms();
FreeSignalDependencies();
+ ClearAllSignalSpeedRestrictions();
+
ClearZoningCaches();
IntialiseOrderDestinationRefcountMap();
diff --git a/src/openttd.cpp b/src/openttd.cpp
index 9909c5e719..eceee5c07c 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -439,6 +439,9 @@ static void ShutdownGame()
FreeSignalPrograms();
FreeSignalDependencies();
+ extern void ClearAllSignalSpeedRestrictions();
+ ClearAllSignalSpeedRestrictions();
+
ClearZoningCaches();
ClearOrderDestinationRefcountMap();
diff --git a/src/saveload/CMakeLists.txt b/src/saveload/CMakeLists.txt
index 0fb209b624..af8909fcde 100644
--- a/src/saveload/CMakeLists.txt
+++ b/src/saveload/CMakeLists.txt
@@ -48,6 +48,7 @@ add_files(
tbtr_template_veh_sl.cpp
town_sl.cpp
tracerestrict_sl.cpp
+ train_speed_adaptation.cpp
tunnel_sl.cpp
vehicle_sl.cpp
waypoint_sl.cpp
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index 89cc76bed3..2fe10c3ca0 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -938,6 +938,10 @@ bool AfterLoadGame()
_settings_game.vehicle.train_braking_model = TBM_ORIGINAL;
}
+ if (SlXvIsFeatureMissing(XSLFI_TRAIN_SPEED_ADAPTATION)) {
+ _settings_game.vehicle.train_speed_adaptation = false;
+ }
+
AfterLoadEngines();
/* Update all vehicles */
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index 552119c5b7..ddf2fa57cd 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -152,6 +152,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_MORE_HOUSES, XSCF_NULL, 2, 2, "more_houses", nullptr, nullptr, nullptr },
{ XSLFI_CUSTOM_TOWN_ZONE, XSCF_IGNORABLE_UNKNOWN, 1, 1, "custom_town_zone", nullptr, nullptr, nullptr },
{ XSLFI_STATION_CARGO_HISTORY, XSCF_NULL, 1, 1, "station_cargo_history", nullptr, nullptr, nullptr },
+ { XSLFI_TRAIN_SPEED_ADAPTATION, XSCF_NULL, 1, 1, "train_speed_adaptation", nullptr, nullptr, "TSAS" },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
};
diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h
index bd347e754f..54921e8dd4 100644
--- a/src/saveload/extended_ver_sl.h
+++ b/src/saveload/extended_ver_sl.h
@@ -106,6 +106,7 @@ enum SlXvFeatureIndex {
XSLFI_MORE_HOUSES, ///< More house types
XSLFI_CUSTOM_TOWN_ZONE, ///< Custom town zones
XSLFI_STATION_CARGO_HISTORY, ///< Station waiting cargo history
+ XSLFI_TRAIN_SPEED_ADAPTATION, ///< Train speed adaptation
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/saveload/saveload.cpp b/src/saveload/saveload.cpp
index 20efcc972d..193cf1dea4 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -288,6 +288,7 @@ extern const ChunkHandler _template_replacement_chunk_handlers[];
extern const ChunkHandler _template_vehicle_chunk_handlers[];
extern const ChunkHandler _bridge_signal_chunk_handlers[];
extern const ChunkHandler _tunnel_chunk_handlers[];
+extern const ChunkHandler _train_speed_adaptation_chunk_handlers[];
extern const ChunkHandler _debug_chunk_handlers[];
/** Array of all chunks in a savegame, \c nullptr terminated. */
@@ -333,6 +334,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
_template_vehicle_chunk_handlers,
_bridge_signal_chunk_handlers,
_tunnel_chunk_handlers,
+ _train_speed_adaptation_chunk_handlers,
_debug_chunk_handlers,
nullptr,
};
diff --git a/src/saveload/train_speed_adaptation.cpp b/src/saveload/train_speed_adaptation.cpp
new file mode 100644
index 0000000000..21dfa39441
--- /dev/null
+++ b/src/saveload/train_speed_adaptation.cpp
@@ -0,0 +1,51 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file train_speed_adaptation.cpp Code handling saving and loading of data for train speed adaptation */
+
+#include "../stdafx.h"
+#include "../train_speed_adaptation.h"
+#include "saveload.h"
+
+using SignalSpeedType = std::pair;
+
+static const SaveLoad _train_speed_adaptation_map_desc[] = {
+ SLE_VAR(SignalSpeedType, first.signal_track, SLE_UINT8),
+ SLE_VAR(SignalSpeedType, first.last_passing_train_dir, SLE_UINT8),
+ SLE_VAR(SignalSpeedType, second.train_speed, SLE_UINT16),
+ SLE_VAR(SignalSpeedType, second.time_stamp, SLE_UINT64),
+ SLE_END()
+};
+
+static void Load_TSAS()
+{
+ int index;
+ SignalSpeedType data;
+ while ((index = SlIterateArray()) != -1) {
+ const_cast(data.first).signal_tile = index;
+ SlObject(&data, _train_speed_adaptation_map_desc);
+ _signal_speeds.insert(data);
+ }
+}
+
+static void RealSave_TSAS(SignalSpeedType *data)
+{
+ SlObject(data, _train_speed_adaptation_map_desc);
+}
+
+static void Save_TSAS()
+{
+ for (auto &it : _signal_speeds) {
+ SlSetArrayIndex(it.first.signal_tile);
+ SignalSpeedType *data = ⁢
+ SlAutolength((AutolengthProc*) RealSave_TSAS, data);
+ }
+}
+
+extern const ChunkHandler _train_speed_adaptation_chunk_handlers[] = {
+ { 'TSAS', Save_TSAS, Load_TSAS, nullptr, nullptr, CH_SPARSE_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp
index 7583249a7b..c9d348bbef 100644
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -807,6 +807,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
SLE_CONDNULL(11, SLV_2, SLV_144), // old reserved space
SLE_CONDVAR_X(Train, reverse_distance, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REVERSE_AT_WAYPOINT)),
SLE_CONDVAR_X(Train, speed_restriction, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPEED_RESTRICTION)),
+ SLE_CONDVAR_X(Train, signal_speed_restriction, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_SPEED_ADAPTATION)),
SLE_CONDVAR_X(Train, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 2)),
SLE_END()
diff --git a/src/settings.cpp b/src/settings.cpp
index 1b42726e7d..a3d7d1b744 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1541,6 +1541,15 @@ static bool PublicRoadsSettingChange(int32 p1) {
return true;
}
+static bool TrainSpeedAdaptationChanged(int32 p1) {
+ extern void ClearAllSignalSpeedRestrictions();
+ ClearAllSignalSpeedRestrictions();
+ for (Train *t : Train::Iterate()) {
+ t->signal_speed_restriction = 0;
+ }
+ return true;
+}
+
/** Checks if any settings are set to incorrect values, and sets them to correct values in that case. */
static void ValidateSettings()
{
@@ -1784,7 +1793,12 @@ static bool ImprovedBreakdownsSettingChanged(int32 p1)
static bool DayLengthChanged(int32 p1)
{
+ const DateTicksScaled old_scaled_date_ticks = _scaled_date_ticks;
SetScaledTickVariables();
+
+ extern void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaled delta);
+ AdjustAllSignalSpeedRestrictionTickValues(_scaled_date_ticks - old_scaled_date_ticks);
+
MarkWholeScreenDirty();
return true;
}
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 3d05450606..6a98b06080 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1924,6 +1924,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_adaptation"));
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 f1d5c56c57..de2c002c39 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -573,6 +573,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_adaptation; ///< Faster trains slow down when behind slower trains
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/newgrf_debug_data.h b/src/table/newgrf_debug_data.h
index 779761f666..d27392adaa 100644
--- a/src/table/newgrf_debug_data.h
+++ b/src/table/newgrf_debug_data.h
@@ -162,8 +162,11 @@ class NIHVehicle : public NIHelper {
seprintf(buffer, lastof(buffer), " T cache: veh weight: %u, user data: %u, curve speed: %u",
t->tcache.cached_veh_weight, t->tcache.user_def_data, t->tcache.cached_max_curve_speed);
print(buffer);
- seprintf(buffer, lastof(buffer), " Wait counter: %u, rev distance: %u, TBSN: %u, speed restriction: %u",
- t->wait_counter, t->reverse_distance, t->tunnel_bridge_signal_num, t->speed_restriction);
+ seprintf(buffer, lastof(buffer), " Wait counter: %u, rev distance: %u, TBSN: %u",
+ t->wait_counter, t->reverse_distance, t->tunnel_bridge_signal_num);
+ print(buffer);
+ seprintf(buffer, lastof(buffer), " Speed restriction: %u, signal speed restriction (ATC): %u",
+ t->speed_restriction, t->signal_speed_restriction);
print(buffer);
seprintf(buffer, lastof(buffer), " Railtype: %u, compatible_railtypes: 0x" OTTD_PRINTFHEX64,
t->railtype, t->compatible_railtypes);
diff --git a/src/table/settings.ini b/src/table/settings.ini
index 82ae75b8d5..790e3952c4 100644
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -64,6 +64,7 @@ static bool ClimateThresholdModeChanged(int32 p1);
static bool VelocityUnitsChanged(int32 p1);
static bool ChangeTrackTypeSortMode(int32 p1);
static bool PublicRoadsSettingChange(int32 p1);
+static bool TrainSpeedAdaptationChanged(int32 p1);
static bool UpdateClientName(int32 p1);
static bool UpdateServerPassword(int32 p1);
@@ -1675,10 +1676,19 @@ strhelp = STR_CONFIG_SETTING_SLOW_ROAD_VEHICLES_IN_CURVES_HELPTEXT
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_adaptation""
+
+[SDT_BOOL]
+base = GameSettings
+var = vehicle.train_speed_adaptation
+def = false
+str = STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTATION
+strhelp = STR_CONFIG_SETTING_TRAIN_SPEED_ADAPTATION_HELPTEXT
+cat = SC_EXPERT
+proc = TrainSpeedAdaptationChanged
+patxname = ""train_speed_adaptation.vehicle.train_speed_adaptation""
[SDT_BOOL]
base = GameSettings
diff --git a/src/train.h b/src/train.h
index ee831d1f79..66386b5c6a 100644
--- a/src/train.h
+++ b/src/train.h
@@ -147,6 +147,7 @@ struct Train FINAL : public GroundVehicle {
uint16 reverse_distance;
uint16 tunnel_bridge_signal_num;
uint16 speed_restriction;
+ uint16 signal_speed_restriction;
/** We don't want GCC to zero our struct! It already is zeroed and has an index! */
Train() : GroundVehicleBase() {}
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index 2e0a813910..a208e32851 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -44,6 +44,7 @@
#include "scope.h"
#include "core/checksum_func.hpp"
#include "debug_settings.h"
+#include "train_speed_adaptation.h"
#include "table/strings.h"
#include "table/train_cmd.h"
@@ -76,6 +77,8 @@ enum ChooseTrainTrackFlags {
};
DECLARE_ENUM_AS_BIT_SET(ChooseTrainTrackFlags)
+std::unordered_map _signal_speeds(1 << 16);
+
static void TryLongReserveChooseTrainTrackFromReservationEnd(Train *v, bool no_reserve_vehicle_tile = false);
static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, ChooseTrainTrackFlags flags, bool *p_got_reservation, ChooseTrainTrackLookAheadState lookahead_state = {});
static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse);
@@ -92,6 +95,52 @@ static void TrainEnterStation(Train *v, StationID station);
static void UnreserveBridgeTunnelTile(TileIndex tile);
static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile);
+/** Return the scaled date ticks by which the speed restriction
+ * at the current position of the train is going to be invalid */
+static DateTicksScaled GetSpeedRestrictionTimeout(const Train *t)
+{
+ const int64 velocity = std::max(25, t->cur_speed);
+ const int64 look_ahead_distance = Clamp(t->cur_speed / 8, 6, 16); // In tiles, varying between 6 and 16 depending on current speed
+
+ // This assumes travel along the X or Y map axis, not diagonally. See GetAdvanceDistance, GetAdvanceSpeed.
+ const int64 ticks_per_tile = (192 * 16 * 4 / 3) / velocity;
+
+ const int64 ticks = ticks_per_tile * look_ahead_distance;
+
+ return _scaled_date_ticks + ticks;
+}
+
+/** Checks if the timeout of the specified signal speed restriction value has passed */
+static bool IsOutOfDate(const SignalSpeedValue& value)
+{
+ return _scaled_date_ticks > value.time_stamp;
+}
+
+/** Removes all speed restrictions from all signals */
+void ClearAllSignalSpeedRestrictions()
+{
+ _signal_speeds.clear();
+}
+
+void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaled delta)
+{
+ for (auto &it : _signal_speeds) {
+ it.second.time_stamp += delta;
+ }
+}
+
+/** Removes all speed restrictions which have passed their timeout from all signals */
+void ClearOutOfDateSignalSpeedRestrictions()
+{
+ for(auto key_value_pair = _signal_speeds.begin(); key_value_pair != _signal_speeds.end(); ) {
+ if (IsOutOfDate(key_value_pair->second)) {
+ key_value_pair = _signal_speeds.erase(key_value_pair);
+ } else {
+ ++key_value_pair;
+ }
+ }
+}
+
inline void ClearLookAheadIfInvalid(Train *v)
{
if (v->lookahead != nullptr && !ValidateLookAhead(v)) v->lookahead.reset();
@@ -1024,6 +1073,9 @@ Train::MaxSpeedInfo Train::GetCurrentMaxSpeedInfoInternal(bool update_state) con
if (this->speed_restriction != 0) {
advisory_max_speed = std::min(advisory_max_speed, this->speed_restriction);
}
+ if (this->signal_speed_restriction != 0 && _settings_game.vehicle.train_speed_adaptation) {
+ advisory_max_speed = std::min(advisory_max_speed, this->signal_speed_restriction);
+ }
if (this->reverse_distance > 1) {
advisory_max_speed = std::min(advisory_max_speed, ReversingDistanceTargetSpeed(this));
}
@@ -1328,6 +1380,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const
v->vehstatus = VS_HIDDEN | VS_DEFPAL;
v->reverse_distance = 0;
v->speed_restriction = 0;
+ v->signal_speed_restriction = 0;
v->SetWagon();
@@ -4444,6 +4497,7 @@ static void TrainEnterStation(Train *v, StationID station)
v->current_order.MakeWaiting();
v->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
v->cur_speed = 0;
+ v->signal_speed_restriction = 0;
return;
}
@@ -5500,7 +5554,33 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
if (update_signals_crossing) {
+
if (v->IsFrontEngine()) {
+ if (_settings_game.vehicle.train_speed_adaptation && IsTileType(gp.old_tile, MP_RAILWAY) && HasSignals(gp.old_tile)) {
+ const TrackdirBits rev_tracks = TrackBitsToTrackdirBits(GetTrackBits(gp.old_tile)) & DiagdirReachesTrackdirs(ReverseDiagDir(enterdir));
+ const Trackdir rev_trackdir = FindFirstTrackdir(rev_tracks);
+ if (HasSignalOnTrackdir(gp.old_tile, ReverseTrackdir(rev_trackdir))) {
+ const Track track = TrackdirToTrack(rev_trackdir);
+ SignalSpeedKey speed_key = {
+ speed_key.signal_tile = gp.old_tile,
+ speed_key.signal_track = track,
+ speed_key.last_passing_train_dir = v->GetVehicleTrackdir()
+ };
+ const auto found_speed_restriction = _signal_speeds.find(speed_key);
+
+ if (found_speed_restriction != _signal_speeds.end()) {
+ if (IsOutOfDate(found_speed_restriction->second)) {
+ _signal_speeds.erase(found_speed_restriction);
+ v->signal_speed_restriction = 0;
+ } else {
+ v->signal_speed_restriction = std::max(25, found_speed_restriction->second.train_speed);
+ }
+ } else {
+ v->signal_speed_restriction = 0;
+ }
+ }
+ }
+
switch (TrainMovedChangeSignal(v, gp.new_tile, enterdir, true)) {
case CHANGED_NORMAL_TO_PBS_BLOCK:
/* We are entering a block with PBS signals right now, but
@@ -5538,17 +5618,33 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
TrainMovedChangeSignal(v, gp.old_tile, ReverseDiagDir(enterdir), false);
if (IsLevelCrossingTile(gp.old_tile)) UpdateLevelCrossing(gp.old_tile);
- if (IsTileType(gp.old_tile, MP_RAILWAY) && HasSignals(gp.old_tile) && IsRestrictedSignal(gp.old_tile)) {
+ if (IsTileType(gp.old_tile, MP_RAILWAY) && HasSignals(gp.old_tile)) {
const TrackdirBits rev_tracks = TrackBitsToTrackdirBits(GetTrackBits(gp.old_tile)) & DiagdirReachesTrackdirs(ReverseDiagDir(enterdir));
const Trackdir rev_trackdir = FindFirstTrackdir(rev_tracks);
const Track track = TrackdirToTrack(rev_trackdir);
+
+ if (_settings_game.vehicle.train_speed_adaptation && HasSignalOnTrackdir(gp.old_tile, ReverseTrackdir(rev_trackdir))) {
+ SignalSpeedKey speed_key = {
+ speed_key.signal_tile = gp.old_tile,
+ speed_key.signal_track = track,
+ speed_key.last_passing_train_dir = v->GetVehicleTrackdir()
+ };
+ SignalSpeedValue speed_value = {
+ speed_value.train_speed = v->First()->cur_speed,
+ speed_value.time_stamp = GetSpeedRestrictionTimeout(v->First())
+ };
+ _signal_speeds[speed_key] = speed_value;
+ }
+
if (HasSignalOnTrack(gp.old_tile, track)) {
- const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.old_tile, track);
- if (prog && prog->actions_used_flags & TRPAUF_SLOT_RELEASE_BACK) {
- TraceRestrictProgramResult out;
- TraceRestrictProgramInput input(gp.old_tile, ReverseTrackdir(rev_trackdir), nullptr, nullptr);
- input.permitted_slot_operations = TRPISP_RELEASE_BACK;
- prog->Execute(first, input, out);
+ if (IsRestrictedSignal(gp.old_tile)) {
+ const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.old_tile, track);
+ if (prog && prog->actions_used_flags & TRPAUF_SLOT_RELEASE_BACK) {
+ TraceRestrictProgramResult out;
+ TraceRestrictProgramInput input(gp.old_tile, ReverseTrackdir(rev_trackdir), nullptr, nullptr);
+ input.permitted_slot_operations = TRPISP_RELEASE_BACK;
+ prog->Execute(first, input, out);
+ }
}
}
}
diff --git a/src/train_speed_adaptation.h b/src/train_speed_adaptation.h
new file mode 100644
index 0000000000..4f4c3e20b2
--- /dev/null
+++ b/src/train_speed_adaptation.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file train_speed_adaptation.h Train speed adaptation data structures. */
+
+#ifndef TRAIN_SPEED_ADAPTATION_H
+#define TRAIN_SPEED_ADAPTATION_H
+
+#include "date_type.h"
+#include "track_type.h"
+#include "tile_type.h"
+
+#include
+
+struct SignalSpeedKey
+{
+ TileIndex signal_tile;
+ Track signal_track;
+ Trackdir last_passing_train_dir;
+
+ bool operator==(const SignalSpeedKey& other) const
+ {
+ return signal_tile == other.signal_tile &&
+ signal_track == other.signal_track &&
+ last_passing_train_dir == other.last_passing_train_dir;
+ }
+};
+
+struct SignalSpeedValue
+{
+ uint16 train_speed;
+ DateTicksScaled time_stamp;
+};
+
+struct SignalSpeedKeyHashFunc
+{
+ std::size_t operator() (const SignalSpeedKey &key) const
+ {
+ const std::size_t h1 = std::hash()(key.signal_tile);
+ const std::size_t h2 = std::hash()(key.last_passing_train_dir);
+ const std::size_t h3 = std::hash