diff --git a/src/lang/extra/english.txt b/src/lang/extra/english.txt index 0a0c17b939..7cd3b979ed 100644 --- a/src/lang/extra/english.txt +++ b/src/lang/extra/english.txt @@ -1025,6 +1025,8 @@ STR_TRACE_RESTRICT_TRAIN_STATUS_REQUIRES_SERVICE :requires servic STR_TRACE_RESTRICT_TRAIN_STATUS_STOPPING_AT_STATION_WAYPOINT :stopping at station/waypoint STR_TRACE_RESTRICT_REVERSE_SIG :Reverse behind signal STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL :Cancel reverse behind signal +STR_TRACE_RESTRICT_REVERSE_AT_SIG :Reverse at PBS signal +STR_TRACE_RESTRICT_REVERSE_AT_SIG_CANCEL :Cancel reverse at PBS signal STR_TRACE_RESTRICT_SET_SPEED_RESTRICTION :Restrict train speed to: {VELOCITY} STR_TRACE_RESTRICT_REMOVE_SPEED_RESTRICTION :Remove train speed restriction STR_TRACE_RESTRICT_TRAIN_NOT_STUCK :Train is not stuck, do not show news reports about waiting at this PBS signal diff --git a/src/sl/extended_ver_sl.cpp b/src/sl/extended_ver_sl.cpp index 2b881c900f..953b16894d 100644 --- a/src/sl/extended_ver_sl.cpp +++ b/src/sl/extended_ver_sl.cpp @@ -80,7 +80,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_TRACE_RESTRICT_OWNER, XSCF_NULL, 1, 1, "tracerestrict_owner", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_ORDRCND, XSCF_NULL, 4, 4, "tracerestrict_order_cond", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_STATUSCND, XSCF_NULL, 2, 2, "tracerestrict_status_cond", nullptr, nullptr, nullptr }, - { XSLFI_TRACE_RESTRICT_REVERSE, XSCF_NULL, 1, 1, "tracerestrict_reverse", nullptr, nullptr, nullptr }, + { XSLFI_TRACE_RESTRICT_REVERSE, XSCF_NULL, 2, 2, "tracerestrict_reverse", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_NEWSCTRL, XSCF_NULL, 1, 1, "tracerestrict_newsctrl", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_COUNTER, XSCF_NULL, 1, 1, "tracerestrict_counter", nullptr, nullptr, "TRRC" }, { XSLFI_TRACE_RESTRICT_TIMEDATE, XSCF_NULL, 2, 2, "tracerestrict_timedate", nullptr, nullptr, nullptr }, diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 8219ccbd60..2073ef9501 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -2279,6 +2279,7 @@ class NIHTraceRestrict : public NIHelper { CA(RESERVE_THROUGH_ALWAYS) CA(CMB_SIGNAL_MODE_CTRL) CA(ORDER_CONDITIONALS) + CA(REVERSE_AT) #undef CA output.print(""); diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 55cf4d0ea9..9d1e38df46 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -804,6 +804,13 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp case TRRVF_CANCEL_REVERSE_BEHIND: out.flags &= ~TRPRF_REVERSE_BEHIND; break; + + case TRRVF_REVERSE_AT: + out.flags |= TRPRF_REVERSE_AT; + break; + + case TRRVF_CANCEL_REVERSE_AT: + out.flags &= ~TRPRF_REVERSE_AT; break; default: @@ -1410,6 +1417,14 @@ CommandCost TraceRestrictProgram::Validate(const std::vector if (condstack.empty()) actions_used_flags &= ~TRPAUF_REVERSE_BEHIND; break; + case TRRVF_REVERSE_AT: + actions_used_flags |= TRPAUF_REVERSE_AT; + break; + + case TRRVF_CANCEL_REVERSE_AT: + if (condstack.empty()) actions_used_flags &= ~TRPAUF_REVERSE_AT; + break; + default: return unknown_instruction(); } diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 40ea16cccb..6fffd71c56 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -174,7 +174,7 @@ enum TraceRestrictItemType : uint8_t { TRIT_COND_RESERVATION_THROUGH = 32, ///< Test if train reservation passes through tile TRIT_COND_END = 48, ///< End (exclusive) of conditional item types, note that this has the same value as TRIT_REVERSE - TRIT_REVERSE = 48, ///< Reverse behind signal + TRIT_REVERSE = 48, ///< Reverse behind/at signal TRIT_SPEED_RESTRICTION = 49, ///< Speed restriction TRIT_NEWS_CONTROL = 50, ///< News control TRIT_COUNTER = 51, ///< Change counter value @@ -312,6 +312,8 @@ enum TraceRestrictWaitAtPbsValueField : uint8_t { enum TraceRestrictReverseValueField : uint8_t { TRRVF_REVERSE_BEHIND = 0, ///< Reverse behind signal TRRVF_CANCEL_REVERSE_BEHIND = 1, ///< Cancel reverse behind signal + TRRVF_REVERSE_AT = 2, ///< Reverse at PBS signal + TRRVF_CANCEL_REVERSE_AT = 3, ///< Cancel reverse at PBS signal }; /** @@ -448,6 +450,7 @@ enum TraceRestrictProgramResultFlags : uint16_t { TRPRF_RM_SPEED_ADAPT_EXEMPT = 1 << 10, ///< Remove speed adaptation exemption TRPRF_SIGNAL_MODE_NORMAL = 1 << 11, ///< Combined normal/shunt signal mode control: normal TRPRF_SIGNAL_MODE_SHUNT = 1 << 12, ///< Combined normal/shunt signal mode control: shunt + TRPRF_REVERSE_AT = 1 << 13, ///< Reverse at PBS signal is set }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags) @@ -476,6 +479,7 @@ enum TraceRestrictProgramActionsUsedFlags : uint32_t { TRPAUF_RESERVE_THROUGH_ALWAYS = 1 << 17, ///< Reserve through action is unconditionally set TRPAUF_CMB_SIGNAL_MODE_CTRL = 1 << 18, ///< Combined normal/shunt signal mode control TRPAUF_ORDER_CONDITIONALS = 1 << 19, ///< Order conditionals are present + TRPAUF_REVERSE_AT = 1 << 20, ///< Reverse at signal }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramActionsUsedFlags) diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index daae56b5b8..24215f27d7 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -335,10 +335,14 @@ static const TraceRestrictDropDownListSet _train_status_value = { static const StringID _reverse_value_str[] = { STR_TRACE_RESTRICT_REVERSE_SIG, STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL, + STR_TRACE_RESTRICT_REVERSE_AT_SIG, + STR_TRACE_RESTRICT_REVERSE_AT_SIG_CANCEL, }; static const uint _reverse_value_val[] = { TRRVF_REVERSE_BEHIND, TRRVF_CANCEL_REVERSE_BEHIND, + TRRVF_REVERSE_AT, + TRRVF_CANCEL_REVERSE_AT, }; /** value drop down list for reverse types strings and values */ @@ -1614,6 +1618,14 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric instruction_string = STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL; break; + case TRRVF_REVERSE_AT: + instruction_string = STR_TRACE_RESTRICT_REVERSE_AT_SIG; + break; + + case TRRVF_CANCEL_REVERSE_AT: + instruction_string = STR_TRACE_RESTRICT_REVERSE_AT_SIG_CANCEL; + break; + default: NOT_REACHED(); break; diff --git a/src/train.h b/src/train.h index f86aba2598..437f366bf3 100644 --- a/src/train.h +++ b/src/train.h @@ -86,6 +86,7 @@ void FreeTrainTrackReservation(Train *v, TileIndex origin = INVALID_TILE, Trackd enum TryPathReserveResultFlags { TPRRF_NONE = 0, ///< No flags TPRRF_RESERVATION_OK = 0x01, ///< Reservation OK + TPRRF_REVERSE_AT_SIGNAL = 0x02, ///< Reverse at signal }; DECLARE_ENUM_AS_BIT_SET(TryPathReserveResultFlags) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 9c0569ff87..1ec97c3b4c 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -84,6 +84,7 @@ DECLARE_ENUM_AS_BIT_SET(ChooseTrainTrackFlags) enum ChooseTrainTrackResultFlags { CTTRF_NONE = 0, ///< No flags CTTRF_RESERVATION_MADE = 0x01, ///< A reservation was made + CTTRF_REVERSE_AT_SIGNAL = 0x02, ///< Reverse at signal }; DECLARE_ENUM_AS_BIT_SET(ChooseTrainTrackResultFlags) @@ -4329,12 +4330,12 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td long_reserve = (out.flags & TRPRF_LONG_RESERVE); } if (!long_reserve) return; - if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE)) { + if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_REVERSE_AT)) { TraceRestrictProgramResult out; TraceRestrictProgramInput input(exit_tile, exit_td, nullptr, nullptr); input.permitted_slot_operations = TRPISP_ACQUIRE; prog->Execute(v, input, out); - if (out.flags & TRPRF_WAIT_AT_PBS) { + if (out.flags & (TRPRF_WAIT_AT_PBS | TRPRF_REVERSE_AT)) { return; } } @@ -4436,7 +4437,7 @@ static ChooseTrainTrackResult ChooseTrainTrack(Train *v, TileIndex tile, DiagDir if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir)) && !IsNoEntrySignal(tile, track)) { if (IsRestrictedSignal(tile) && v->force_proceed != TFP_SIGNAL) { const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, track); - if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_TRAIN_NOT_STUCK)) { + if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_TRAIN_NOT_STUCK | TRPAUF_REVERSE_AT)) { TraceRestrictProgramResult out; TraceRestrictProgramInput input(tile, TrackEnterdirToTrackdir(track, enterdir), nullptr, nullptr); input.permitted_slot_operations = TRPISP_ACQUIRE; @@ -4444,7 +4445,10 @@ static ChooseTrainTrackResult ChooseTrainTrack(Train *v, TileIndex tile, DiagDir if (out.flags & TRPRF_TRAIN_NOT_STUCK && !(v->track & TRACK_BIT_WORMHOLE) && !(v->track == TRACK_BIT_DEPOT)) { v->wait_counter = 0; } - if (out.flags & TRPRF_WAIT_AT_PBS) { + if (out.flags & TRPRF_REVERSE_AT) { + result_flags |= CTTRF_REVERSE_AT_SIGNAL; + } + if (out.flags & (TRPRF_WAIT_AT_PBS | TRPRF_REVERSE_AT)) { if (mark_stuck) MarkTrainAsStuck(v, true); return { track, result_flags }; } @@ -4672,7 +4676,7 @@ static ChooseTrainTrackResult ChooseTrainTrack(Train *v, TileIndex tile, DiagDir * @param first_tile_okay True if no path should be reserved if the current tile is a safe position. * @return Result flags. */ -TryPathReserveResultFlags TryPathReserveResultFlags(Train *v, bool mark_as_stuck, bool first_tile_okay) +TryPathReserveResultFlags TryPathReserveWithResultFlags(Train *v, bool mark_as_stuck, bool first_tile_okay) { dbg_assert(v->IsFrontEngine()); @@ -4767,16 +4771,20 @@ TryPathReserveResultFlags TryPathReserveResultFlags(Train *v, bool mark_as_stuck if (Rail90DegTurnDisallowedTilesFromDiagDir(origin.tile, new_tile, exitdir)) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir)); - bool res_made = false; + TryPathReserveResultFlags result_flags = TPRRF_NONE; if (reachable != TRACK_BIT_NONE) { ChooseTrainTrackResult result = ChooseTrainTrack(v, new_tile, exitdir, reachable, CTTF_FORCE_RES | (mark_as_stuck ? CTTF_MARK_STUCK : CTTF_NONE)); - if (result.ctt_flags & CTTRF_RESERVATION_MADE) res_made = true; + if (result.ctt_flags & CTTRF_RESERVATION_MADE) { + result_flags |= TPRRF_RESERVATION_OK; + } else if (result.ctt_flags & CTTRF_REVERSE_AT_SIGNAL) { + result_flags |= TPRRF_REVERSE_AT_SIGNAL; + } } - if (!res_made) { + if ((result_flags & TPRRF_RESERVATION_OK) == 0) { /* Free the depot reservation as well. */ if (v->track == TRACK_BIT_DEPOT && v->tile == origin.tile) SetDepotReservation(v->tile, false); - return false; + return result_flags; } if (HasBit(v->flags, VRF_TRAIN_STUCK)) { @@ -4785,7 +4793,7 @@ TryPathReserveResultFlags TryPathReserveResultFlags(Train *v, bool mark_as_stuck } ClrBit(v->flags, VRF_TRAIN_STUCK); if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) FillTrainReservationLookAhead(v); - return true; + return result_flags; } @@ -5679,10 +5687,13 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) if (!CheckCompatibleRail(v, gp.new_tile, enterdir)) goto invalid_rail; TrackBits chosen_track; + bool reverse_at_signal = false; if (prev == nullptr) { /* Currently the locomotive is active. Determine which one of the * available tracks to choose */ - chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, CTTF_MARK_STUCK | CTTF_NON_LOOKAHEAD).track); + ChooseTrainTrackResult result = ChooseTrainTrack(v, gp.new_tile, enterdir, bits, CTTF_MARK_STUCK | CTTF_NON_LOOKAHEAD); + chosen_track = TrackToTrackBits(result.track); + reverse_at_signal = (result.ctt_flags & CTTRF_REVERSE_AT_SIGNAL); dbg_assert_msg_tile(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)), gp.new_tile, "0x%X, 0x%X, 0x%X", chosen_track, bits, GetReservedTrackbits(gp.new_tile)); if (v->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) { @@ -5706,6 +5717,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) /* In front of a red signal */ Trackdir i = FindFirstTrackdir(trackdirbits); + if (reverse_at_signal) { + ClrBit(v->flags, VRF_TRAIN_STUCK); + goto reverse_train_direction; + } + /* Don't handle stuck trains here. */ if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false; @@ -6706,9 +6722,10 @@ static bool TrainLocoHandler(Train *v, bool mode) bool turn_around = v->wait_counter % (_settings_game.pf.wait_for_pbs_path * DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals; if (!turn_around && v->wait_counter % _settings_game.pf.path_backoff_interval != 0 && v->force_proceed == TFP_NONE) return true; - if (!TryPathReserve(v)) { + TryPathReserveResultFlags path_result = TryPathReserveWithResultFlags(v); + if ((path_result & TPRRF_RESERVATION_OK) == 0) { /* Still stuck. */ - if (turn_around) ReverseTrainDirection(v); + if (turn_around || (path_result & TPRRF_REVERSE_AT_SIGNAL)) ReverseTrainDirection(v); if (HasBit(v->flags, VRF_TRAIN_STUCK) && v->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * DAY_TICKS) { /* Show message to player. */