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()(key.signal_track); + + return (h1 ^ h2) ^ h3; + } +}; + +extern std::unordered_map _signal_speeds; + +#endif /* TRAIN_SPEED_ADAPTATION_H */ diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 995a6e0410..67d9ac3681 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2417,6 +2417,7 @@ void VehicleEnterDepot(Vehicle *v) ClrBit(t->flags, VRF_TOGGLE_REVERSE); t->ConsistChanged(CCF_ARRANGE); t->reverse_distance = 0; + t->signal_speed_restriction = 0; t->lookahead.reset(); if (!(t->vehstatus & VS_CRASHED)) { t->crash_anim_pos = 0; @@ -4309,4 +4310,7 @@ void ShiftVehicleDates(int interval) for (Vehicle *v : Vehicle::Iterate()) { v->date_of_last_service += interval; } + + extern void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaled delta); + AdjustAllSignalSpeedRestrictionTickValues(interval * DAY_TICKS * _settings_game.economy.day_length_factor); }