diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 9580086799..f537812a7f 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -57,6 +57,8 @@ private: Trackdir m_res_fail_td; ///< The trackdir where the reservation failed TileIndex m_origin_tile; ///< Tile our reservation will originate from + std::vector> m_signals_set_to_red; ///< List of signals turned red during a path reservation. + bool FindSafePositionProc(TileIndex tile, Trackdir td) { if (IsSafeWaitingPosition(Yapf().GetVehicle(), tile, td, true, !TrackFollower::Allow90degTurns())) { @@ -88,8 +90,9 @@ private: /** Try to reserve a single track/platform. */ bool ReserveSingleTrack(TileIndex tile, Trackdir td) { + Trackdir rev_td = ReverseTrackdir(td); if (IsRailStationTile(tile)) { - if (!ReserveRailStationPlatform(tile, TrackdirToExitdir(ReverseTrackdir(td)))) { + if (!ReserveRailStationPlatform(tile, TrackdirToExitdir(rev_td))) { /* Platform could not be reserved, undo. */ m_res_fail_tile = tile; m_res_fail_td = td; @@ -101,6 +104,13 @@ private: m_res_fail_td = td; return false; } + + /* Green path signal opposing the path? Turn to red. */ + if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) { + m_signals_set_to_red.emplace_back(tile, rev_td); + SetSignalStateByTrackdir(tile, rev_td, SIGNAL_STATE_RED); + MarkTileDirtyByTile(tile); + } } return tile != m_res_dest || td != m_res_dest_td; @@ -159,6 +169,7 @@ public: /* Don't bother if the target is reserved. */ if (!IsWaitingPositionFree(Yapf().GetVehicle(), m_res_dest, m_res_dest_td)) return false; + m_signals_set_to_red.clear(); for (Node *node = m_res_node; node->m_parent != nullptr; node = node->m_parent) { node->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack::ReserveSingleTrack); if (m_res_fail_tile != INVALID_TILE) { @@ -171,6 +182,11 @@ public: fail_node->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack::UnreserveSingleTrack); } while (fail_node != node && (fail_node = fail_node->m_parent) != nullptr); + /* Re-instate green path signals we turned to red. */ + for (auto [sig_tile, td] : m_signals_set_to_red) { + SetSignalStateByTrackdir(sig_tile, td, SIGNAL_STATE_GREEN); + } + return false; } } diff --git a/src/signal.cpp b/src/signal.cpp index d060c58d22..315d59ab98 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -15,6 +15,7 @@ #include "viewport_func.h" #include "train.h" #include "company_base.h" +#include "pbs.h" #include "safeguards.h" @@ -251,6 +252,9 @@ enum SigFlags { SF_GREEN2 = 1 << 4, ///< two or more green exits found SF_FULL = 1 << 5, ///< some of buffers was full, do not continue SF_PBS = 1 << 6, ///< pbs signal found + SF_SPLIT = 1 << 7, ///< track merge/split found + SF_ENTER = 1 << 8, ///< signal entering the block found + SF_ENTER2 = 1 << 9, ///< two or more signals entering the block found }; DECLARE_ENUM_AS_BIT_SET(SigFlags) @@ -305,6 +309,9 @@ static SigFlags ExploreSegment(Owner owner) if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN; } + /* Is this a track merge or split? */ + if (!HasAtMostOneBit(tracks)) flags |= SF_SPLIT; + if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile Track track = TrackBitsToTrack(tracks_masked); // mask TRACK_BIT_X and Y too if (HasSignalOnTrack(tile, track)) { // now check whole track, not trackdir @@ -315,11 +322,11 @@ static SigFlags ExploreSegment(Owner owner) * ANY conventional signal in REVERSE direction * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */ if (HasSignalOnTrackdir(tile, reversedir)) { - if (IsPbsSignal(sig)) { - flags |= SF_PBS; - } else if (!_tbuset.Add(tile, reversedir)) { - return flags | SF_FULL; - } + if (IsPbsSignal(sig)) flags |= SF_PBS; + if (flags & SF_ENTER) flags |= SF_ENTER2; + flags |= SF_ENTER; + + if (!_tbuset.Add(tile, reversedir)) return flags | SF_FULL; } if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) flags |= SF_PBS; @@ -411,13 +418,20 @@ static void UpdateSignalsAroundSegment(SigFlags flags) while (_tbuset.Get(&tile, &trackdir)) { assert(HasSignalOnTrackdir(tile, trackdir)); - SignalType sig = GetSignalType(tile, TrackdirToTrack(trackdir)); + Track track = TrackdirToTrack(trackdir); + SignalType sig = GetSignalType(tile, track); SignalState newstate = SIGNAL_STATE_GREEN; + /* Signal state of reserved path signals is handled by the reserve/unreserve process. */ + if (IsPbsSignal(sig) && (GetRailReservationTrackBits(tile) & TrackToTrackBits(track)) != TRACK_BIT_NONE) continue; + /* determine whether the new state is red */ if (flags & SF_TRAIN) { /* train in the segment */ newstate = SIGNAL_STATE_RED; + } else if (IsPbsSignal(sig) && (flags & (SF_SPLIT | SF_ENTER2))) { + /* Turn path signals red if the segment has a junction or more than one way in. */ + newstate = SIGNAL_STATE_RED; } else { /* is it a bidir combo? - then do not count its other signal direction as exit */ if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) { diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index a9fb14ee54..2b58b6b212 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2430,6 +2430,9 @@ void FreeTrainTrackReservation(const Train *v) SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_RED); MarkTileDirtyByTile(tile); } + } else if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) { + /* Reservation passes an opposing path signal. Mark signal for update to re-establish the proper default state. */ + AddSideToSignalBuffer(tile, TrackdirToExitdir(ReverseTrackdir(td)), v->owner); } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) { break; } @@ -2440,6 +2443,8 @@ void FreeTrainTrackReservation(const Train *v) free_tile = true; } + + UpdateSignalsInBuffer(); } static const uint8_t _initial_tile_subcoord[6][4][3] = { @@ -2481,6 +2486,8 @@ static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, CFollowTrackRail ft(v); + std::vector> signals_set_to_red; + TileIndex tile = origin.tile; Trackdir cur_td = origin.trackdir; while (ft.Follow(tile, cur_td)) { @@ -2518,14 +2525,28 @@ static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, tile = ft.m_new_tile; cur_td = FindFirstTrackdir(ft.m_new_td_bits); + Trackdir rev_td = ReverseTrackdir(cur_td); if (IsSafeWaitingPosition(v, tile, cur_td, true, _settings_game.pf.forbid_90_deg)) { bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _settings_game.pf.forbid_90_deg); if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break; + /* Green path signal opposing the path? Turn to red. */ + if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) { + signals_set_to_red.emplace_back(tile, rev_td); + SetSignalStateByTrackdir(tile, rev_td, SIGNAL_STATE_RED); + MarkTileDirtyByTile(tile); + } /* Safe position is all good, path valid and okay. */ return PBSTileInfo(tile, cur_td, true); } if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break; + + /* Green path signal opposing the path? Turn to red. */ + if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) { + signals_set_to_red.emplace_back(tile, rev_td); + SetSignalStateByTrackdir(tile, rev_td, SIGNAL_STATE_RED); + MarkTileDirtyByTile(tile); + } } if (ft.m_err == CFollowTrackRail::EC_OWNER || ft.m_err == CFollowTrackRail::EC_NO_WAY) { @@ -2553,6 +2574,11 @@ static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, UnreserveRailTrack(tile, TrackdirToTrack(cur_td)); } + /* Re-instate green signals we turned to red. */ + for (auto [sig_tile, td] : signals_set_to_red) { + SetSignalStateByTrackdir(sig_tile, td, SIGNAL_STATE_GREEN); + } + /* Path invalid. */ return PBSTileInfo(); }