Tracerestrict: Add condition whether reservation passes through tile

pull/484/head
Jonathan G Rennison 1 year ago
parent 07c923eceb
commit 07b7cc5652

@ -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}

@ -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 <typename T>
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.
*

@ -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);

@ -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 },

@ -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<TraceRestrictItem>
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:

@ -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;

@ -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<TraceRestrictOrderCondAuxField>(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);

Loading…
Cancel
Save