diff --git a/src/lang/english.txt b/src/lang/english.txt index c3a8947633..2f72113b4e 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1705,6 +1705,9 @@ STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change setting value +STR_CONFIG_SETTING_ADJACENT_CROSSINGS :Close adjacent level crossings: {STRING2} +STR_CONFIG_SETTING_ADJACENT_CROSSINGS_HELPTEXT :Closes all adjacent level crossings on parallel tracks whenever one or more is occupied + # Config errors STR_CONFIG_ERROR :{WHITE}Error with the configuration file... STR_CONFIG_ERROR_ARRAY :{WHITE}... error in array '{RAW_STRING}' diff --git a/src/pbs.cpp b/src/pbs.cpp index 133293909f..139ded7f34 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -107,8 +107,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) case MP_ROAD: if (IsLevelCrossing(tile) && !HasCrossingReservation(tile)) { SetCrossingReservation(tile, true); - BarCrossing(tile); - MarkTileDirtyByTile(tile); // crossing barred, make tile dirty + UpdateLevelCrossing(tile, false); return true; } break; diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index aa445eb91a..eb06e7af65 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -1638,6 +1638,14 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u trackdirbits = TrackBitsToTrackdirBits(AxisToTrackBits(axis)); if (IsCrossingBarred(tile)) red_signals = trackdirbits; + if (IsLevelCrossingTile(TileAddByDiagDir(tile, AxisToDiagDir(axis))) && + IsCrossingBarred(TileAddByDiagDir(tile, AxisToDiagDir(axis)))) { + red_signals &= (TrackdirBits)0x0102; // magic value. I think TRACKBIT_X_SW and TRACKBIT_X_NE should be swapped + } + if (IsLevelCrossingTile(TileAddByDiagDir(tile, ReverseDiagDir(AxisToDiagDir(axis)))) && + IsCrossingBarred(TileAddByDiagDir(tile, ReverseDiagDir(AxisToDiagDir(axis))))) { + red_signals &= (TrackdirBits)0x0201; // inverse of above magic value + } break; } diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 77a2a8255f..8ff1176942 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,8 +45,9 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { - { XSLFI_TRACE_RESTRICT, XSCF_NULL, 1, 1, "tracerestrict", NULL, NULL, "TRRM,TRRP" }, - { XSLFI_PROG_SIGS, XSCF_NULL, 1, 1, "programmable_signals", NULL, NULL, "SPRG" }, + { XSLFI_TRACE_RESTRICT, XSCF_NULL, 1, 1, "tracerestrict", NULL, NULL, "TRRM,TRRP" }, + { XSLFI_PROG_SIGS, XSCF_NULL, 1, 1, "programmable_signals", NULL, NULL, "SPRG" }, + { XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", NULL, NULL, NULL }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index f705eef0eb..90d6f2427a 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -23,6 +23,7 @@ enum SlXvFeatureIndex { XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use XSLFI_TRACE_RESTRICT, ///< Trace restrict XSLFI_PROG_SIGS, ///< programmable signals patch + XSLFI_ADJACENT_CROSSINGS, ///< Adjacent level crossings closure patch XSLFI_SIZE, ///< Total count of features, including null feature }; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 6133f2ee73..c9f28737c5 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1626,6 +1626,7 @@ static SettingsContainer &GetSettingsTree() vehicles->Add(new SettingEntry("order.no_servicing_if_no_breakdowns")); vehicles->Add(new SettingEntry("order.serviceathelipad")); + vehicles->Add(new SettingEntry("vehicle.adjacent_crossings")); } SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS)); diff --git a/src/settings_type.h b/src/settings_type.h index 0f02c35806..e0c98dcfcd 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -463,6 +463,7 @@ struct VehicleSettings { byte extend_vehicle_life; ///< extend vehicle life by this many years byte road_side; ///< the side of the road vehicles drive on uint8 plane_crashes; ///< number of plane crashes, 0 = none, 1 = reduced, 2 = normal + bool adjacent_crossings; ///< enable closing of adjacent level crossings }; /** Settings related to the economy. */ diff --git a/src/table/settings.ini b/src/table/settings.ini index 86a39d4617..9bc99e4317 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2521,6 +2521,16 @@ def = NULL proc = RedrawScreen cat = SC_BASIC +[SDT_BOOL] +base = GameSettings +var = vehicle.adjacent_crossings +guiflags = SGF_NO_NETWORK +def = true +str = STR_CONFIG_SETTING_ADJACENT_CROSSINGS +strhelp = STR_CONFIG_SETTING_ADJACENT_CROSSINGS_HELPTEXT +cat = SC_BASIC +patxname = ""adjacent_crossings.vehicle.adjacent_crossings"" + ;*************************************************************************** ; Unsaved setting variables. diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 8f20973b35..b44a69c7bb 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1676,19 +1676,31 @@ static bool TrainApproachingCrossing(TileIndex tile) return HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum); } +/** Check if the crossing should be closed + * @return train on crossing || train approaching crossing || reserved + */ +static inline bool CheckLevelCrossing(TileIndex tile) +{ + return HasCrossingReservation(tile) || HasVehicleOnPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile); +} /** * Sets correct crossing state * @param tile tile to update * @param sound should we play sound? + * @param force_state force close the crossing due to an adjacent tile * @pre tile is a rail-road crossing */ -void UpdateLevelCrossing(TileIndex tile, bool sound) +static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_state = false) { assert(IsLevelCrossingTile(tile)); + bool new_state; - /* train on crossing || train approaching crossing || reserved */ - bool new_state = HasVehicleOnPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile) || HasCrossingReservation(tile); + if (force_state) { + new_state = force_state; + } else { + new_state = CheckLevelCrossing(tile); + } if (new_state != IsCrossingBarred(tile)) { if (new_state && sound) { @@ -1699,6 +1711,35 @@ void UpdateLevelCrossing(TileIndex tile, bool sound) } } +/** + * Cycles the adjacent crossings and sets their state + * @param tile tile to update + * @param sound should we play sound? + */ +void UpdateLevelCrossing(TileIndex tile, bool sound) +{ + bool is_forced = false; + if (!IsLevelCrossingTile(tile)) return; + + Axis axis = GetCrossingRoadAxis(tile); + + if (_settings_game.vehicle.adjacent_crossings) { + for (TileIndex t = tile; !is_forced && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, AxisToDiagDir(GetCrossingRoadAxis(t)))) { + is_forced |= CheckLevelCrossing(t); + } + for (TileIndex t = tile; !is_forced && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, ReverseDiagDir(AxisToDiagDir(GetCrossingRoadAxis(t))))) { + is_forced |= CheckLevelCrossing(t); + } + } + + for (TileIndex t = tile; IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, AxisToDiagDir(GetCrossingRoadAxis(t)))) { + UpdateLevelCrossingTile(t, sound, is_forced); + } + for (TileIndex t = tile; IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, ReverseDiagDir(AxisToDiagDir(GetCrossingRoadAxis(t))))) { + UpdateLevelCrossingTile(t, sound, is_forced); + } +} + /** * Bars crossing and plays ding-ding sound if not barred already @@ -1708,9 +1749,8 @@ void UpdateLevelCrossing(TileIndex tile, bool sound) static inline void MaybeBarCrossingWithSound(TileIndex tile) { if (!IsCrossingBarred(tile)) { - BarCrossing(tile); - if (_settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile); - MarkTileDirtyByTile(tile); + SetCrossingReservation(tile, true); + UpdateLevelCrossing(tile, true); } }