From 07b7cc5652ccd835b7b32f5f8f7812f68fe98ffc Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 6 Feb 2023 19:54:14 +0000 Subject: [PATCH] Tracerestrict: Add condition whether reservation passes through tile --- src/lang/english.txt | 5 ++ src/pbs.cpp | 118 +++++++++++++++++++++++++++++++ src/pbs.h | 1 + src/saveload/extended_ver_sl.cpp | 2 +- src/tracerestrict.cpp | 11 +++ src/tracerestrict.h | 10 ++- src/tracerestrict_gui.cpp | 52 ++++++++++++-- 7 files changed, 192 insertions(+), 7 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 476ade1a93..6fe37519d3 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3260,6 +3260,8 @@ STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_EQUALS :can carry STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_NOT_EQUALS :can't carry STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_HAS_STATUS :has STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_DOESNT_HAVE_STATUS :doesn't have +STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_PASS :passes through +STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_DOESNT_PASS :doesn't pass through STR_TRACE_RESTRICT_CONDITIONAL_IF :If STR_TRACE_RESTRICT_CONDITIONAL_ELIF :Else if STR_TRACE_RESTRICT_CONDITIONAL_ORIF :Or if @@ -3297,6 +3299,8 @@ STR_TRACE_RESTRICT_VARIABLE_TRAIN_OWNER :train owner STR_TRACE_RESTRICT_VARIABLE_TRAIN_STATUS :train status STR_TRACE_RESTRICT_VARIABLE_TRAIN_ENGINE_CLASS :engine class STR_TRACE_RESTRICT_VARIABLE_ORDER_TARGET_DIRECTION :direction of order target +STR_TRACE_RESTRICT_VARIABLE_RESERVATION_THROUGH :PBS reservation passes tile +STR_TRACE_RESTRICT_VARIABLE_RESERVATION_THROUGH_SHORT :PBS reservation STR_TRACE_RESTRICT_VARIABLE_UNDEFINED :undefined STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED :{PUSH_COLOUR}{RED}undefined{POP_COLOUR} STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER :{STRING} {STRING} {STRING} {COMMA} then @@ -3327,6 +3331,7 @@ STR_TRACE_RESTRICT_CONDITIONAL_COUNTER :{STRING} value STR_TRACE_RESTRICT_CONDITIONAL_COUNTER_STR :{STRING} value of counter: {STRING} {BLACK}{STRING} {STRING} {COMMA} then STR_TRACE_RESTRICT_CONDITIONAL_ENGINE_CLASSES :{STRING} train {STRING}: {STRING} then STR_TRACE_RESTRICT_CONDITIONAL_TARGET_DIRECTION :{STRING} tile of {STRING} {STRING} further {STRING} than this signal tile then +STR_TRACE_RESTRICT_CONDITIONAL_PASSES_TILE_INDEX :{STRING} {STRING} {STRING} {NUM} x {NUM} then STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED :{STRING} {STRING} {STRING} {RED}undefined {BLACK}{STRING}then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED :{STRING} {RED}undefined {BLACK}{STRING}then STR_TRACE_RESTRICT_PF_PENALTY_ITEM :Add pathfinder penalty: {COMMA} diff --git a/src/pbs.cpp b/src/pbs.cpp index 6c950335c5..e7d0473933 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -707,6 +707,108 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra return PBSTileInfo(tile, trackdir, false); } +/** Follow a reservation starting from a specific tile to the end. */ +template +static void FollowReservationEnumerate(Owner o, RailTypes rts, TileIndex tile, Trackdir trackdir, FollowReservationFlags flags, T handler) +{ + TileIndex start_tile = tile; + Trackdir start_trackdir = trackdir; + bool first_loop = true; + + /* Start track not reserved? This can happen if two trains + * are on the same tile. The reservation on the next tile + * is not ours in this case, so exit. */ + if (!(flags & FRF_TB_EXIT_FREE) && !HasReservedTracks(tile, TrackToTrackBits(TrackdirToTrack(trackdir)))) return; + + if (handler(start_tile, start_trackdir)) return; + + /* Do not disallow 90 deg turns as the setting might have changed between reserving and now. */ + CFollowTrackRail ft(o, rts); + auto check_tunnel_bridge = [&]() -> bool { + if (IsTunnelBridgeWithSignalSimulation(tile) && TrackdirEntersTunnelBridge(tile, trackdir)) { + if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC && IsTunnelBridgeSignalSimulationEntrance(tile)) { + TileIndex end = GetOtherTunnelBridgeEnd(tile); + if (HasAcrossTunnelBridgeReservation(end) && GetTunnelBridgeExitSignalState(end) == SIGNAL_STATE_GREEN && + ((flags & FRF_TB_EXIT_FREE) || TunnelBridgeIsFree(tile, end, nullptr, true).Succeeded())) { + /* skip far end */ + Trackdir end_trackdir = GetTunnelBridgeExitTrackdir(end); + tile = end; + trackdir = end_trackdir; + if (handler(tile, trackdir)) return false; + return true; + } + } + if ((flags & FRF_IGNORE_ONEWAY) && _settings_game.vehicle.train_braking_model == TBM_REALISTIC && IsTunnelBridgeSignalSimulationExit(tile) && + GetTunnelBridgeExitSignalState(tile) == SIGNAL_STATE_GREEN) { + TileIndex end = GetOtherTunnelBridgeEnd(tile); + if (HasAcrossTunnelBridgeReservation(end) && TunnelBridgeIsFree(tile, end, nullptr, true).Succeeded()) { + /* skip far end */ + tile = end; + trackdir = GetTunnelBridgeExitTrackdir(tile); + if (handler(tile, trackdir)) return false; + return true; + } + } + return false; + } + return true; + }; + while (check_tunnel_bridge() && ft.Follow(tile, trackdir)) { + flags &= ~FRF_TB_EXIT_FREE; + TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile)); + + if (ft.m_is_station) { + /* Check skipped station tiles as well, maybe our reservation ends inside the station. */ + TileIndexDiff diff = TileOffsByDiagDir(ft.m_exitdir); + TileIndex t = ft.m_new_tile - (ft.m_tiles_skipped * diff); + while (ft.m_tiles_skipped-- > 0) { + if (HasStationReservation(t)) { + if (handler(t, DiagDirToDiagTrackdir(ft.m_exitdir))) return; + } else { + break; + } + t += diff; + } + } + + /* No reservation --> path end found */ + if (reserved == TRACKDIR_BIT_NONE) { + break; + } + + /* Can't have more than one reserved trackdir */ + Trackdir new_trackdir = FindFirstTrackdir(reserved); + + /* One-way signal against us. The reservation can't be ours as it is not + * a safe position from our direction and we can never pass the signal. */ + if (!(flags & FRF_IGNORE_ONEWAY) && HasOnewaySignalBlockingTrackdir(ft.m_new_tile, new_trackdir)) break; + + tile = ft.m_new_tile; + trackdir = new_trackdir; + + if (handler(tile, trackdir)) return; + + if (first_loop) { + /* Update the start tile after we followed the track the first + * time. This is necessary because the track follower can skip + * tiles (in stations for example) which means that we might + * never visit our original starting tile again. */ + start_tile = tile; + start_trackdir = trackdir; + first_loop = false; + } else { + /* Loop encountered? */ + if (tile == start_tile && trackdir == start_trackdir) break; + } + /* Depot tile? Can't continue. */ + if (IsRailDepotTile(tile)) { + break; + } + /* Non-pbs signal? Reservation can't continue. */ + if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break; + } +} + /** * Helper struct for finding the best matching vehicle on a specific track. */ @@ -1302,6 +1404,22 @@ TileIndex VehiclePosTraceRestrictPreviousSignalCallback(const Train *v, const vo } } +/** + * Test whether a train's reservation passes through a given tile. + */ +bool TrainReservationPassesThroughTile(const Train *v, TileIndex search_tile) +{ + bool found = false; + FollowReservationEnumerate(v->owner, GetRailTypeInfo(v->railtype)->all_compatible_railtypes, v->tile, v->GetVehicleTrackdir(), FRF_NONE, [&](TileIndex tile, Trackdir trackdir) -> bool { + if (tile == search_tile) { + found = true; + return true; + } + return false; + }); + return found; +} + /** * Determine whether a certain track on a tile is a safe position to end a path. * diff --git a/src/pbs.h b/src/pbs.h index 20ac6eaa82..0f6726bc27 100644 --- a/src/pbs.h +++ b/src/pbs.h @@ -166,6 +166,7 @@ void ApplyAvailableFreeTunnelBridgeTiles(TrainReservationLookAhead *lookahead, i void TryCreateLookAheadForTrainInTunnelBridge(Train *t); void SetTrainReservationLookaheadEnd(Train *v); void FillTrainReservationLookAhead(Train *v); +bool TrainReservationPassesThroughTile(const Train *v, TileIndex search_tile); bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg = false); bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg = false, PBSWaitingPositionRestrictedSignalInfo *restricted_signal_info = nullptr); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 33a638d265..dd79fbfee1 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -70,7 +70,7 @@ static uint32 saveLC(const SlxiSubChunkInfo *info, bool dry_run); const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_VERSION_LABEL, XSCF_IGNORABLE_ALL, 1, 1, "version_label", saveVL, loadVL, nullptr }, - { XSLFI_TRACE_RESTRICT, XSCF_NULL, 14, 14, "tracerestrict", nullptr, nullptr, "TRRM,TRRP,TRRS" }, + { XSLFI_TRACE_RESTRICT, XSCF_NULL, 15, 15, "tracerestrict", nullptr, nullptr, "TRRM,TRRP,TRRS" }, { 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, 1, 1, "tracerestrict_status_cond", nullptr, nullptr, nullptr }, diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index d420985889..7ff81450ff 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -612,6 +612,14 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp break; } + case TRIT_COND_RESERVATION_THROUGH: { + // TRIT_COND_RESERVATION_THROUGH value type uses the next slot + i++; + uint32_t test_tile = this->items[i]; + result = TestBinaryConditionCommon(item, TrainReservationPassesThroughTile(v, test_tile)); + break; + } + default: NOT_REACHED(); } @@ -1002,6 +1010,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector case TRIT_COND_TIME_DATE_VALUE: case TRIT_COND_RESERVED_TILES: case TRIT_COND_CATEGORY: + case TRIT_COND_RESERVATION_THROUGH: break; case TRIT_COND_CURRENT_ORDER: @@ -1209,6 +1218,7 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp case TRVT_DENY: case TRVT_SPEED: case TRVT_TILE_INDEX: + case TRVT_TILE_INDEX_THROUGH: case TRVT_RESERVE_THROUGH: case TRVT_LONG_RESERVE: case TRVT_WEIGHT: @@ -1547,6 +1557,7 @@ static uint32 GetDualInstructionInitialValue(TraceRestrictItem item) { switch (GetTraceRestrictType(item)) { case TRIT_COND_PBS_ENTRY_SIGNAL: + case TRIT_COND_RESERVATION_THROUGH: return INVALID_TILE; case TRIT_COND_SLOT_OCCUPANCY: diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 2c4ce52999..21e51694b5 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -154,6 +154,7 @@ enum TraceRestrictItemType { TRIT_COND_RESERVED_TILES = 29, ///< Test reserved tiles ahead of train TRIT_COND_CATEGORY = 30, ///< Test train category TRIT_COND_TARGET_DIRECTION = 31, ///< Test direction of order target tile relative to this signal tile + 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 @@ -666,7 +667,8 @@ static inline bool IsTraceRestrictConditional(TraceRestrictItem item) static inline bool IsTraceRestrictDoubleItem(TraceRestrictItem item) { const TraceRestrictItemType type = GetTraceRestrictType(item); - return type == TRIT_COND_PBS_ENTRY_SIGNAL || type == TRIT_COND_SLOT_OCCUPANCY || type == TRIT_COUNTER || type == TRIT_COND_COUNTER_VALUE || type == TRIT_COND_TIME_DATE_VALUE; + return type == TRIT_COND_PBS_ENTRY_SIGNAL || type == TRIT_COND_SLOT_OCCUPANCY || type == TRIT_COUNTER || + type == TRIT_COND_COUNTER_VALUE || type == TRIT_COND_TIME_DATE_VALUE || type == TRIT_COND_RESERVATION_THROUGH; } /** @@ -717,6 +719,7 @@ enum TraceRestrictValueType { TRVT_SPEED_ADAPTATION_CONTROL = 48,///< takes a TraceRestrictSpeedAdaptationControlField TRVT_SIGNAL_MODE_CONTROL = 49,///< takes a TraceRestrictSignalModeControlField TRVT_ORDER_TARGET_DIAGDIR = 50,///< takes a DiagDirection, and the order type in the auxiliary field + TRVT_TILE_INDEX_THROUGH = 51,///< takes a TileIndex in the next item slot (passes through) }; /** @@ -871,6 +874,11 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.cond_type = TRCOT_BINARY; break; + case TRIT_COND_RESERVATION_THROUGH: + out.value_type = TRVT_TILE_INDEX_THROUGH; + out.cond_type = TRCOT_BINARY; + break; + default: NOT_REACHED(); break; diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 97b6f5c26d..90b2cd0438 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -589,6 +589,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG STR_TRACE_RESTRICT_VARIABLE_RESERVED_TILES_AHEAD, STR_TRACE_RESTRICT_VARIABLE_PBS_RES_END_TILE, STR_TRACE_RESTRICT_VARIABLE_ORDER_TARGET_DIRECTION, + STR_TRACE_RESTRICT_VARIABLE_RESERVATION_THROUGH, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; @@ -620,6 +621,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG TRIT_COND_RESERVED_TILES, // 0x1000000 TRIT_COND_PBS_ENTRY_SIGNAL | (TRPESAF_RES_END_TILE << 16), TRIT_COND_TARGET_DIRECTION, + TRIT_COND_RESERVATION_THROUGH, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { @@ -631,7 +633,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG if (_settings_client.gui.show_adv_tracerestrict_features) { *hide_mask = 0; } else { - *hide_mask = is_conditional ? 0x1FE0000 : 0xEF0; + *hide_mask = is_conditional ? 0x9FE0000 : 0xEF0; } if (is_conditional && _settings_game.vehicle.train_braking_model != TBM_REALISTIC) *hide_mask |= 0x1040000; if (!is_conditional && !_settings_game.vehicle.train_speed_adaptation) *hide_mask |= 0x800; @@ -828,6 +830,20 @@ static const TraceRestrictDropDownListSet _train_status_cond_ops = { _train_status_cond_ops_str, _train_status_cond_ops_val, }; +static const StringID _passes_through_cond_ops_str[] = { + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_PASS, + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_DOESNT_PASS, + INVALID_STRING_ID, +}; +static const uint _passes_through_cond_ops_val[] = { + TRCO_IS, + TRCO_ISNOT, +}; +/** passes through conditional operators dropdown list set */ +static const TraceRestrictDropDownListSet _passes_through_cond_ops = { + _passes_through_cond_ops_str, _passes_through_cond_ops_val, +}; + static const StringID _slot_op_cond_ops_str[] = { STR_TRACE_RESTRICT_SLOT_ACQUIRE_WAIT, STR_TRACE_RESTRICT_SLOT_TRY_ACQUIRE, @@ -930,6 +946,7 @@ static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestric if (properties.value_type == TRVT_CARGO_ID) return &_cargo_cond_ops; if (properties.value_type == TRVT_TRAIN_STATUS) return &_train_status_cond_ops; if (properties.value_type == TRVT_ENGINE_CLASS) return &_train_status_cond_ops; + if (properties.value_type == TRVT_TILE_INDEX_THROUGH) return &_passes_through_cond_ops; switch (properties.cond_type) { case TRCOT_NONE: @@ -1334,6 +1351,23 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric break; } + case TRVT_TILE_INDEX_THROUGH: { + assert(prog != nullptr); + assert(GetTraceRestrictType(item) == TRIT_COND_RESERVATION_THROUGH); + TileIndex tile = *(TraceRestrictProgram::InstructionAt(prog->items, index - 1) + 1); + if (tile == INVALID_TILE) { + DrawInstructionStringConditionalInvalidValue(item, properties, instruction_string, selected); + } else { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_PASSES_TILE_INDEX; + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); + SetDParam(3, TileX(tile)); + SetDParam(4, TileY(tile)); + } + SetDParam(1, STR_TRACE_RESTRICT_VARIABLE_RESERVATION_THROUGH_SHORT); + break; + } + case TRVT_GROUP_INDEX: { assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); @@ -1792,7 +1826,8 @@ public: if (sel == -1) return; TraceRestrictItem item = this->GetItem(this->GetProgram(), sel); - if (GetTraceRestrictTypeProperties(item).value_type == TRVT_ORDER) { + TraceRestrictValueType val_type = GetTraceRestrictTypeProperties(item).value_type; + if (val_type == TRVT_ORDER) { switch (static_cast(GetTraceRestrictAuxField(item))) { case TROCAF_STATION: case TROCAF_WAYPOINT: { @@ -1811,7 +1846,7 @@ public: break; } } - } else if (GetTraceRestrictTypeProperties(item).value_type == TRVT_TILE_INDEX) { + } else if (val_type == TRVT_TILE_INDEX || val_type == TRVT_TILE_INDEX_THROUGH) { TileIndex tile = *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, sel - 1) + 1); if (tile != INVALID_TILE) { ScrollMainWindowToTile(tile); @@ -2469,7 +2504,8 @@ public: void OnPlaceObjectSignalTileValue(Point pt, TileIndex tile, int widget, int error_message) { TraceRestrictItem item = GetSelected(); - if (GetTraceRestrictTypeProperties(item).value_type != TRVT_TILE_INDEX) return; + TraceRestrictValueType val_type = GetTraceRestrictTypeProperties(item).value_type; + if (val_type != TRVT_TILE_INDEX && val_type != TRVT_TILE_INDEX_THROUGH) return; if (!IsInfraTileUsageAllowed(VEH_TRAIN, _local_company, tile)) { ShowErrorMessage(error_message, STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, WL_INFO); @@ -2501,7 +2537,8 @@ public: void OnPlaceObjectTileValue(Point pt, TileIndex tile, int widget, int error_message) { TraceRestrictItem item = GetSelected(); - if (GetTraceRestrictTypeProperties(item).value_type != TRVT_TILE_INDEX) return; + TraceRestrictValueType val_type = GetTraceRestrictTypeProperties(item).value_type; + if (val_type != TRVT_TILE_INDEX && val_type != TRVT_TILE_INDEX_THROUGH) return; TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_DUAL_ITEM, this->selected_instruction - 1, tile, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); } @@ -3030,6 +3067,11 @@ private: } break; + case TRVT_TILE_INDEX_THROUGH: + right_sel->SetDisplayedPlane(DPR_VALUE_TILE); + this->EnableWidget(TR_WIDGET_VALUE_TILE); + break; + case TRVT_PF_PENALTY: right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);