Allow placing routing restrictions on tunnel/bridge entrance/exit signals

No reserve through support
pull/336/head
Jonathan G Rennison 3 years ago
parent 1ee59c8333
commit 74bfe4e6d2

@ -1735,6 +1735,7 @@
<ul>
<li>m2 bit 15: for bridge entrances only: storage for visual red/green state of signals starting from 15 is allocated outside the map array</li>
<li>m2 bits 14..4: for bridge entrances only: for signals 0..10 on bridge, signal is visually red if corresponding bit in 4..14 is set</li>
<li>m3 bit 6: the entrance and/or exit signals on this tile are restricted (tracerestrict patch)</li>
<li>m3 bits 5..3: entrance signal aspect (only valid if signal is present and not red, and multi-aspect signalling is in effect)</li>
<li>m3 bits 2..0: exit signal aspect (only valid if signal is present and not red, and multi-aspect signalling is in effect)</li>
<li>m6 bit 7: set = exit signal shows green, clear = exit signal shows red</li>

@ -287,7 +287,7 @@ the array so you can quickly see what is used and what is not.
<td class="caption">tunnel entrance</td>
<td class="bits" rowspan=3><span class="free">OOO</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits"><span class="patch-pool" title="Tunnel index on pool (or overflow sentinel)">PPPP PPPP PPPP PPPP</span></td>
<td class="bits" rowspan=4><span class="rearrange" title="Owner of tram (road only; a rearrangement can free some of these bits)">XXXX</span> <span class="free">OOOO</span><br /><span class="free">OO</span> <span class="patch" title="Entrance/exit signal aspects (rail only)">PPPPPP</span></td>
<td class="bits" rowspan=4><span class="rearrange" title="Owner of tram (road only; a rearrangement can free some of these bits)">XXXX</span> <span class="free">OOOO</span><br /><span class="free">O</span><span class="patch" title="Routing restrictions present on entrance/exit signal(s)">P</span> <span class="patch" title="Entrance/exit signal aspects (rail only)">PPPPPP</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Road type">XX XXXX</span></td>
<td class="bits"><span class="used" title="Bridge or tunnel bit">O</span><span class="patch" title="Signal simulation mode (rail only)">PP</span><span class="rearrange" title="PBS reservation (rail; a rearrangement can free some of these bits)">X</span> <span class="used" title="Transport type">XX</span> <span class="used" title="Direction of the tunnel/bridge">XX</span></td>
<td class="bits"><span class="patch" title="PBS mode, exit signal state">PP</span><span class="free">OO OO</span><span class="patch" title="Semaphore/light mode, entrance signal state">PP</span></td>

@ -572,6 +572,7 @@ void DumpMapStats(char *b, const char *last)
if (IsTunnelBridgeWithSignalSimulation(t)) {
bucket |= TBB_SIGNALLED;
if (IsTunnelBridgeSignalSimulationBidirectional(t)) bucket |= TBB_SIGNALLED_BIDI;
if (IsTunnelBridgeRestrictedSignal(t)) restricted_signals++;
}
if (GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD) {
if (HasTileRoadType(t, RTT_ROAD)) bucket |= TBB_ROAD;

@ -176,6 +176,13 @@ private:
IsRestrictedSignal(tile);
}
// returns true if ExecuteTunnelBridgeTraceRestrict should be called
inline bool ShouldCheckTunnelBridgeTraceRestrict(Node& n, TileIndex tile)
{
return n.m_num_signals_passed < m_sig_look_ahead_costs.size() &&
IsTunnelBridgeRestrictedSignal(tile);
}
/**
* This is called to retrieve the previous signal, as required
* This is not run all the time as it is somewhat expensive and most restrictions will not test for the previous signal
@ -312,6 +319,22 @@ private:
return false;
}
// returns true if dead end bit has been set
inline bool ExecuteTunnelBridgeTraceRestrict(Node& n, TileIndex tile, Trackdir trackdir, int& cost, TraceRestrictProgramResult &out)
{
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, TrackdirToTrack(trackdir));
TraceRestrictProgramActionsUsedFlags flags_to_check = TRPAUF_PF;
if (prog && prog->actions_used_flags & flags_to_check) {
prog->Execute(Yapf().GetVehicle(), TraceRestrictProgramInput(tile, trackdir, &TraceRestrictPreviousSignalCallback, &n), out);
if (out.flags & TRPRF_DENY) {
n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
return true;
}
cost += out.penalty;
}
return false;
}
public:
int SignalCost(Node &n, TileIndex tile, Trackdir trackdir)
{
@ -417,17 +440,27 @@ public:
}
}
}
if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExitOnly(tile) && TrackdirEntersTunnelBridge(tile, trackdir)) {
/* Entering a signalled bridge/tunnel from the wrong side, equivalent to encountering a one-way signal from the wrong side */
n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
}
if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgeEffectivelyPBS(tile) && TrackdirExitsTunnelBridge(tile, trackdir)) {
/* Exiting a PBS signalled tunnel/bridge, record the last non-reserve through signal */
n.m_last_non_reserve_through_signal_tile = tile;
n.m_last_non_reserve_through_signal_td = trackdir;
}
if (n.flags_u.flags_s.m_reverse_pending && IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationEntrance(tile)) {
n.m_segment->m_end_segment_reason |= ESRB_SAFE_TILE;
if (IsTunnelBridgeWithSignalSimulation(tile)) {
const bool entering = TrackdirEntersTunnelBridge(tile, trackdir);
const bool exiting = TrackdirExitsTunnelBridge(tile, trackdir);
if (IsTunnelBridgeSignalSimulationExitOnly(tile) && entering) {
/* Entering a signalled bridge/tunnel from the wrong side, equivalent to encountering a one-way signal from the wrong side */
n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
}
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgeEffectivelyPBS(tile) && exiting) {
/* Exiting a PBS signalled tunnel/bridge, record the last non-reserve through signal */
n.m_last_non_reserve_through_signal_tile = tile;
n.m_last_non_reserve_through_signal_td = trackdir;
}
if (ShouldCheckTunnelBridgeTraceRestrict(n, tile)) {
TraceRestrictProgramResult out;
if (ExecuteTunnelBridgeTraceRestrict(n, tile, trackdir, cost, out)) {
return -1;
}
}
if (n.flags_u.flags_s.m_reverse_pending && entering && IsTunnelBridgeSignalSimulationEntrance(tile)) {
n.m_segment->m_end_segment_reason |= ESRB_SAFE_TILE;
}
}
return cost;
}

@ -353,6 +353,39 @@ static int LookaheadTileHeightForChunnel(int length, int offset)
return 0;
}
static uint16 ApplyTunnelBridgeLookaheadSignalSpeedRestriction(TileIndex tile, Trackdir trackdir, const Train *v,
uint16 initial_speed_restriction, TrainReservationLookAhead *lookahead, int offset, int16 z)
{
uint16 speed_restriction = initial_speed_restriction;
if (v != nullptr && IsTunnelBridgeRestrictedSignal(tile)) {
if (trackdir == INVALID_TRACKDIR) {
trackdir = GetTunnelBridgeExitTrackdir(tile);
}
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, TrackdirToTrack(trackdir));
if (prog && prog->actions_used_flags & TRPAUF_SPEED_RESTRICTION) {
TraceRestrictProgramResult out;
TraceRestrictProgramInput input(tile, trackdir, nullptr, nullptr);
prog->Execute(v, input, out);
if (out.flags & TRPRF_SPEED_RESTRICTION_SET) {
int duration;
if (TrackdirEntersTunnelBridge(tile, trackdir)) {
duration = 4 + (IsDiagonalTrackdir(trackdir) ? 16 : 8);
} else {
duration = 4;
}
lookahead->AddSpeedRestriction(out.speed_restriction, offset, duration, z);
if (out.speed_restriction != 0 && (speed_restriction == 0 || out.speed_restriction < speed_restriction)) {
/* lower of the speed restrictions before or after the signal */
speed_restriction = out.speed_restriction;
}
}
}
}
return speed_restriction;
}
/** Follow a reservation starting from a specific tile to the end. */
static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Trackdir trackdir, FollowReservationFlags flags, const Train *v, TrainReservationLookAhead *lookahead)
{
@ -525,8 +558,10 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
const int spacing = GetTunnelBridgeSignalSimulationSpacing(tile);
const int signals = length / spacing;
uint16 speed_restriction = ApplyTunnelBridgeLookaheadSignalSpeedRestriction(tile, trackdir, v, lookahead->speed_restriction, lookahead, 0, z);
uint16 signal_speed = GetRailTypeInfo(rt)->max_speed;
if (signal_speed == 0 || (lookahead->speed_restriction != 0 && lookahead->speed_restriction < signal_speed)) signal_speed = lookahead->speed_restriction;
if (signal_speed == 0 || (speed_restriction != 0 && speed_restriction < signal_speed)) signal_speed = speed_restriction;
if (signal_speed == 0 || (bridge_speed != 0 && bridge_speed < signal_speed)) signal_speed = bridge_speed;
/* Entrance signal */
@ -548,7 +583,16 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
}
/* Exit signal */
const int end_offset = start_offset + (TILE_SIZE * length) /* + ((DiagDirToDiagTrackBits(GetTunnelBridgeDirection(end)) & GetTunnelBridgeTrackBits(end)) ? 16 : 8)*/;
const int end_offset = start_offset + (TILE_SIZE * length);
uint16 exit_speed_restriction = ApplyTunnelBridgeLookaheadSignalSpeedRestriction(end, INVALID_TRACKDIR, v, lookahead->speed_restriction, lookahead, end_offset, z);
if (exit_speed_restriction != speed_restriction) {
speed_restriction = exit_speed_restriction;
signal_speed = GetRailTypeInfo(rt)->max_speed;
if (signal_speed == 0 || (speed_restriction != 0 && speed_restriction < signal_speed)) signal_speed = speed_restriction;
if (signal_speed == 0 || (bridge_speed != 0 && bridge_speed < signal_speed)) signal_speed = bridge_speed;
}
lookahead->AddSignal(signal_speed, end_offset, z);
lookahead->SetNextExtendPositionIfUnset();
@ -580,7 +624,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
lookahead->AddReverse(z);
}
if (out.flags & TRPRF_SPEED_RESTRICTION_SET) {
lookahead->AddSpeedRestriction(out.speed_restriction, z);
lookahead->AddSpeedRestriction(out.speed_restriction, 0, 0, z);
if (out.speed_restriction != 0 && (speed_restriction == 0 || out.speed_restriction < speed_restriction)) {
/* lower of the speed restrictions before or after the signal */
speed_restriction = out.speed_restriction;
@ -869,12 +913,14 @@ void TryCreateLookAheadForTrainInTunnelBridge(Train *t)
if (IsTunnel(t->tile) && Tunnel::GetByTile(t->tile)->is_chunnel) SetBit(t->lookahead->flags, TRLF_CHUNNEL);
if (IsTunnelBridgeSignalSimulationEntrance(t->tile)) {
uint16 bridge_speed = IsBridge(t->tile) ? GetBridgeSpec(GetBridgeType(t->tile))->speed : 0;
const int length = GetTunnelBridgeLength(t->tile, GetOtherTunnelBridgeEnd(t->tile));
const uint16 bridge_speed = IsBridge(t->tile) ? GetBridgeSpec(GetBridgeType(t->tile))->speed : 0;
const TileIndex end = GetOtherTunnelBridgeEnd(t->tile);
const int length = GetTunnelBridgeLength(t->tile, end);
const int spacing = GetTunnelBridgeSignalSimulationSpacing(t->tile);
const int signals = length / spacing;
uint16 signal_speed = GetRailTypeInfo(GetRailTypeByTrack(t->tile, TrackdirToTrack(t->lookahead->reservation_end_trackdir)))->max_speed;
const RailType rt = GetRailTypeByTrack(t->tile, TrackdirToTrack(t->lookahead->reservation_end_trackdir));
uint16 signal_speed = GetRailTypeInfo(rt)->max_speed;
if (signal_speed == 0 || (t->speed_restriction != 0 && t->speed_restriction < signal_speed)) signal_speed = t->speed_restriction;
if (signal_speed == 0 || (bridge_speed != 0 && bridge_speed < signal_speed)) signal_speed = bridge_speed;
@ -889,6 +935,14 @@ void TryCreateLookAheadForTrainInTunnelBridge(Train *t)
/* Exit signal */
const int end_offset = TILE_SIZE * length;
uint16 exit_speed_restriction = ApplyTunnelBridgeLookaheadSignalSpeedRestriction(end, INVALID_TRACKDIR, t, t->speed_restriction, t->lookahead.get(), end_offset, z);
if (exit_speed_restriction != t->speed_restriction) {
signal_speed = GetRailTypeInfo(rt)->max_speed;
if (signal_speed == 0 || (exit_speed_restriction != 0 && exit_speed_restriction < signal_speed)) signal_speed = exit_speed_restriction;
if (signal_speed == 0 || (bridge_speed != 0 && bridge_speed < signal_speed)) signal_speed = bridge_speed;
}
t->lookahead->AddSignal(signal_speed, end_offset, z);
t->lookahead->SetNextExtendPositionIfUnset();
@ -1157,10 +1211,8 @@ bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bo
if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
}
if (IsRailTunnelBridgeTile(tile) && IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(trackdir))) {
if (IsTunnelBridgeSignalSimulationEntrance(tile)) {
return true;
}
if (IsTunnelBridgeSignalSimulationEntranceTile(tile) && IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(trackdir))) {
return true;
}
/* Check next tile. For performance reasons, we check for 90 degree turns ourself. */
@ -1229,8 +1281,8 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo
/* Not reserved and depot or not a pbs signal -> free. */
if (IsRailDepotTile(tile)) return true;
auto pbs_res_end_wait_test = [v, restricted_signal_info](TileIndex t, Trackdir td) -> bool {
if (IsRestrictedSignal(t)) {
auto pbs_res_end_wait_test = [v, restricted_signal_info](TileIndex t, Trackdir td, bool tunnel_bridge) -> bool {
if (tunnel_bridge ? IsTunnelBridgeRestrictedSignal(t) : IsRestrictedSignal(t)) {
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(t, TrackdirToTrack(td));
if (restricted_signal_info && prog) {
restricted_signal_info->tile = t;
@ -1250,11 +1302,12 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo
};
if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) {
return pbs_res_end_wait_test(tile, trackdir);
return pbs_res_end_wait_test(tile, trackdir, false);
}
if (IsTunnelBridgeSignalSimulationEntranceTile(tile) && IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(trackdir))) {
if (IsTunnelBridgeSignalSimulationBidirectional(tile)) {
bool free = pbs_res_end_wait_test(tile, trackdir, true);
if (free && IsTunnelBridgeSignalSimulationBidirectional(tile)) {
TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
if (HasAcrossTunnelBridgeReservation(other_end) && GetTunnelBridgeExitSignalState(other_end) == SIGNAL_STATE_RED) return false;
Direction dir = DiagDirToDir(GetTunnelBridgeDirection(other_end));
@ -1267,7 +1320,7 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo
return nullptr;
})) return false;
}
return true;
return free;
}
/* Check the next tile, if it's a PBS signal, it has to be free as well. */
@ -1285,7 +1338,7 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo
Trackdir td = FindFirstTrackdir(ft.m_new_td_bits);
/* PBS signal on next trackdir? */
if (HasPbsSignalOnTrackdir(ft.m_new_tile, td)) {
return pbs_res_end_wait_test(ft.m_new_tile, td);
return pbs_res_end_wait_test(ft.m_new_tile, td, false);
}
}

@ -114,10 +114,10 @@ struct TrainReservationLookAhead {
this->items.push_back({ end + offset, end + offset + duration, z_pos, speed, TRLIT_TRACK_SPEED });
}
void AddSpeedRestriction(uint16 speed, int16 z_pos)
void AddSpeedRestriction(uint16 speed, int offset, int duration, int16 z_pos)
{
int end = this->RealEndPosition();
this->items.push_back({ end, end, z_pos, speed, TRLIT_SPEED_RESTRICTION });
this->items.push_back({ end + offset, end + offset + duration, z_pos, speed, TRLIT_SPEED_RESTRICTION });
this->speed_restriction = speed;
}

@ -2110,6 +2110,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1
}
}
if (flags & DC_EXEC) {
Track end_track = FindFirstTrack(GetAcrossTunnelBridgeTrackBits(end));
Company *c = Company::Get(GetTileOwner(tile));
c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, end);
ClearBridgeTunnelSignalSimulation(end, tile);
@ -2118,7 +2119,9 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1
AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile));
AddSideToSignalBuffer(end, INVALID_DIAGDIR, GetTileOwner(tile));
YapfNotifyTrackLayoutChange(tile, track);
YapfNotifyTrackLayoutChange(end, track);
YapfNotifyTrackLayoutChange(end, end_track);
TraceRestrictNotifySignalRemoval(tile, track);
TraceRestrictNotifySignalRemoval(end, end_track);
DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
for (Train *v : re_reserve_trains) {
ReReserveTrainPath(v);

@ -248,6 +248,9 @@ static void GenericPlaceSignals(TileIndex tile)
if (IsPlainRailTile(tile) && HasTrack(tile, track) && HasSignalOnTrack(tile, track)) {
ShowTraceRestrictProgramWindow(tile, track);
}
if (IsTunnelBridgeWithSignalSimulation(tile) && HasTrack(GetAcrossTunnelBridgeTrackBits(tile), track)) {
ShowTraceRestrictProgramWindow(tile, track);
}
return;
}

@ -3971,6 +3971,14 @@ bool AfterLoadGame()
}
}
if (SlXvIsFeatureMissing(XSLFI_TRACE_RESTRICT_TUNBRIDGE)) {
for (TileIndex t = 0; t < map_size; t++) {
if (IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL && IsTunnelBridgeWithSignalSimulation(t)) {
SetTunnelBridgeRestrictedSignal(t, false);
}
}
}
InitializeRoadGUI();
/* This needs to be done after conversion. */

@ -80,6 +80,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_TRACE_RESTRICT_BRKCND, XSCF_NULL, 2, 2, "tracerestrict_braking_cond",nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_CTGRYCND,XSCF_NULL, 1, 1, "tracerestrict_ctgry_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_PENCTRL, XSCF_NULL, 1, 1, "tracerestrict_pfpenctrl", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_TUNBRIDGE,XSCF_NULL, 1, 1, "tracerestrict_sigtunbridge",nullptr, nullptr, nullptr },
{ XSLFI_PROG_SIGS, XSCF_NULL, 2, 2, "programmable_signals", nullptr, nullptr, "SPRG" },
{ XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", nullptr, nullptr, nullptr },
{ XSLFI_SAFER_CROSSINGS, XSCF_NULL, 1, 1, "safer_crossings", nullptr, nullptr, nullptr },

@ -34,6 +34,7 @@ enum SlXvFeatureIndex {
XSLFI_TRACE_RESTRICT_BRKCND, ///< Trace restrict: realistic braking related conditionals
XSLFI_TRACE_RESTRICT_CTGRYCND, ///< Trace restrict: category conditionals
XSLFI_TRACE_RESTRICT_PENCTRL, ///< Trace restrict: PF penalty control
XSLFI_TRACE_RESTRICT_TUNBRIDGE, ///< Trace restrict: restricted signalled tunnel/bridge support
XSLFI_PROG_SIGS, ///< programmable pre-signals patch
XSLFI_ADJACENT_CROSSINGS, ///< Adjacent level crossings closure patch
XSLFI_SAFER_CROSSINGS, ///< Safer level crossings

@ -1124,7 +1124,18 @@ void TraceRestrictSetIsSignalRestrictedBit(TileIndex t)
TraceRestrictMapping::iterator upper_bound = _tracerestrictprogram_mapping.lower_bound(MakeTraceRestrictRefId(t + 1, static_cast<Track>(0)));
// If iterators are the same, there are no mappings for this tile
SetRestrictedSignal(t, lower_bound != upper_bound);
switch (GetTileType(t)) {
case MP_RAILWAY:
SetRestrictedSignal(t, lower_bound != upper_bound);
break;
case MP_TUNNELBRIDGE:
SetTunnelBridgeRestrictedSignal(t, lower_bound != upper_bound);
break;
default:
NOT_REACHED();
}
}
/**
@ -1251,11 +1262,27 @@ void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommand
static CommandCost TraceRestrictCheckTileIsUsable(TileIndex tile, Track track)
{
// Check that there actually is a signal here
if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
if (!HasSignalOnTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
switch (GetTileType(tile)) {
case MP_RAILWAY:
if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
if (!HasSignalOnTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
}
break;
case MP_TUNNELBRIDGE:
if (!IsRailTunnelBridgeTile(tile) || !HasBit(GetTunnelBridgeTrackBits(tile), track)) {
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
if (!IsTunnelBridgeWithSignalSimulation(tile) || !IsTrackAcrossTunnelBridge(tile, track)) {
return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
}
break;
default:
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
// Check tile ownership, do this afterwards to avoid tripping up on house/industry tiles

@ -875,12 +875,24 @@ TraceRestrictProgram *GetTraceRestrictProgram(TraceRestrictRefId ref, bool creat
void TraceRestrictNotifySignalRemoval(TileIndex tile, Track track);
static inline bool IsRestrictedSignalTile(TileIndex t)
{
switch (GetTileType(t)) {
case MP_RAILWAY:
return IsRestrictedSignal(t);
case MP_TUNNELBRIDGE:
return IsTunnelBridgeRestrictedSignal(t);
default:
return false;
}
}
/**
* Gets the existing signal program for the tile identified by @p t and @p track, or nullptr
*/
static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIndex t, Track track)
{
if (IsRestrictedSignal(t)) {
if (IsRestrictedSignalTile(t)) {
return GetTraceRestrictProgram(MakeTraceRestrictRefId(t, track), false);
} else {
return nullptr;

@ -2093,7 +2093,7 @@ public:
*/
void OnPlaceObjectSignal(Point pt, TileIndex source_tile, int widget, int error_message)
{
if (!IsPlainRailTile(source_tile)) {
if (!IsPlainRailTile(source_tile) && !IsRailTunnelBridgeTile(source_tile)) {
ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
return;
}
@ -2112,14 +2112,26 @@ public:
return;
}
if (!HasTrack(source_tile, source_track)) {
ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
return;
}
if (IsTileType(source_tile, MP_RAILWAY)) {
if (!HasTrack(source_tile, source_track)) {
ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
return;
}
if (!HasSignalOnTrack(source_tile, source_track)) {
ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO);
return;
if (!HasSignalOnTrack(source_tile, source_track)) {
ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO);
return;
}
} else {
if (!HasTrack(GetTunnelBridgeTrackBits(source_tile), source_track)) {
ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
return;
}
if (!IsTunnelBridgeWithSignalSimulation(source_tile) || !HasTrack(GetAcrossTunnelBridgeTrackBits(source_tile), source_track)) {
ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO);
return;
}
}
switch (widget) {

@ -4001,11 +4001,12 @@ static bool HasLongReservePbsSignalOnTrackdir(Train* v, TileIndex tile, Trackdir
return false;
}
static TileIndex CheckLongReservePbsTunnelBridgeOnTrackdir(Train* v, TileIndex tile, Trackdir trackdir)
static TileIndex CheckLongReservePbsTunnelBridgeOnTrackdir(Train* v, TileIndex tile, Trackdir trackdir, bool restricted_only = false)
{
if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC && IsTunnelBridgeSignalSimulationEntranceTile(tile) && TrackdirEntersTunnelBridge(tile, trackdir)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
if (restricted_only && !IsTunnelBridgeRestrictedSignal(end)) return INVALID_TILE;
int raw_free_tiles;
if (v->lookahead != nullptr && v->lookahead->reservation_end_tile == tile && v->lookahead->reservation_end_trackdir == trackdir) { // TODO fix loop case
if (HasBit(v->lookahead->flags, TRLF_TB_EXIT_FREE)) {
@ -4033,7 +4034,7 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td
const bool long_enough = IsReservationLookAheadLongEnough(v, lookahead_state);
// We reserved up to a unoccupied signalled tunnel/bridge, reserve past it as well. recursion
TileIndex exit_tile = long_enough ? INVALID_TILE : CheckLongReservePbsTunnelBridgeOnTrackdir(v, tile, td);
TileIndex exit_tile = CheckLongReservePbsTunnelBridgeOnTrackdir(v, tile, td, long_enough);
if (exit_tile != INVALID_TILE) {
CFollowTrackRail ft(v);
Trackdir exit_td = GetTunnelBridgeExitTrackdir(exit_tile);
@ -4042,6 +4043,23 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td
if ((ft.m_new_td_bits & TrackBitsToTrackdirBits(reserved_bits)) == TRACKDIR_BIT_NONE) {
/* next tile is not reserved */
bool long_reserve = !long_enough;
if (IsTunnelBridgeRestrictedSignal(exit_tile)) {
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(exit_tile, TrackdirToTrack(exit_td));
if (prog && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_ACQUIRE_ON_RES | TRPAUF_LONG_RESERVE)) {
TraceRestrictProgramResult out;
if (long_reserve) out.flags |= TRPRF_LONG_RESERVE;
TraceRestrictProgramInput input(exit_tile, exit_td, nullptr, nullptr);
input.permitted_slot_operations = TRPISP_ACQUIRE | TRPISP_ACQUIRE_ON_RES;
prog->Execute(v, input, out);
if (out.flags & TRPRF_WAIT_AT_PBS) {
return;
}
long_reserve = (out.flags & TRPRF_LONG_RESERVE);
}
}
if (!long_reserve) return;
SignalState exit_state = GetTunnelBridgeExitSignalState(exit_tile);
/* reserve exit to make contiguous reservation */
@ -4051,10 +4069,6 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td
SetTunnelReservation(exit_tile, true);
}
SetTunnelBridgeExitSignalState(exit_tile, SIGNAL_STATE_GREEN);
if (_extra_aspects > 0) {
SetTunnelBridgeExitSignalAspect(exit_tile, 0);
UpdateAspectDeferred(exit_tile, GetTunnelBridgeExitTrackdir(exit_tile));
}
ChooseTrainTrack(v, ft.m_new_tile, ft.m_exitdir, TrackdirBitsToTrackBits(ft.m_new_td_bits), CTTF_NO_LOOKAHEAD_VALIDATE | (force_res ? CTTF_FORCE_RES : CTTF_NONE), nullptr, lookahead_state);
@ -4067,6 +4081,10 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td
}
SetTunnelBridgeExitSignalState(exit_tile, exit_state);
} else {
if (_extra_aspects > 0) {
SetTunnelBridgeExitSignalAspect(exit_tile, 0);
UpdateAspectDeferred(exit_tile, GetTunnelBridgeExitTrackdir(exit_tile));
}
MarkTileDirtyByTile(exit_tile, VMDF_NOT_MAP_MODE);
}
}
@ -4995,6 +5013,23 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
}
}
auto try_exit_reservation = [&]() -> bool {
if (IsTunnelBridgeRestrictedSignal(tile)) {
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, TrackdirToTrack(td));
if (prog && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_ACQUIRE_ON_RES)) {
TraceRestrictProgramResult out;
TraceRestrictProgramInput input(tile, td, nullptr, nullptr);
input.permitted_slot_operations = TRPISP_ACQUIRE | TRPISP_ACQUIRE_ON_RES;
prog->Execute(t, input, out);
if (out.flags & TRPRF_WAIT_AT_PBS) {
return false;
}
}
}
return TryPathReserve(t);
};
if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) {
if (unlikely(t->lookahead == nullptr)) {
FillTrainReservationLookAhead(t);
@ -5004,10 +5039,6 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
if (t->lookahead->reservation_end_tile == t->tile && t->lookahead->reservation_end_position - t->lookahead->current_position <= (int)TILE_SIZE && !HasBit(t->lookahead->flags, TRLF_TB_EXIT_FREE)) return false;
SignalState exit_state = GetTunnelBridgeExitSignalState(tile);
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN);
if (_extra_aspects > 0) {
SetTunnelBridgeExitSignalAspect(tile, 0);
UpdateAspectDeferred(tile, GetTunnelBridgeExitTrackdir(tile));
}
/* Get tile margin before changing vehicle direction */
const int tile_margin = GetTileMarginInFrontOfTrain(t);
@ -5018,7 +5049,7 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
t->tile = tile;
t->track = TRACK_BIT_WORMHOLE;
t->direction = TrackdirToDirection(td);
bool ok = TryPathReserve(t);
bool ok = try_exit_reservation();
if (!ok && (t->lookahead->reservation_end_position >= t->lookahead->current_position && t->lookahead->reservation_end_position > t->lookahead->current_position + tile_margin)) {
/* Reservation was made previously and was valid then.
* To avoid unexpected braking due to stopping short of the lookahead end,
@ -5026,6 +5057,10 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
ok = true;
}
if (ok) {
if (_extra_aspects > 0) {
SetTunnelBridgeExitSignalAspect(tile, 0);
UpdateAspectDeferred(tile, GetTunnelBridgeExitTrackdir(tile));
}
mark_dirty = true;
if (t->lookahead->reservation_end_tile == veh_orig_tile && t->lookahead->reservation_end_position - t->lookahead->current_position <= (int)TILE_SIZE) {
/* Less than a tile of lookahead, advance tile */
@ -5057,7 +5092,7 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
t->tile = tile;
t->track = TRACK_BIT_WORMHOLE;
t->direction = TrackdirToDirection(td);
bool ok = TryPathReserve(t);
bool ok = try_exit_reservation();
t->tile = veh_orig_tile;
t->track = veh_orig_track;
t->direction = veh_orig_direction;
@ -5170,6 +5205,21 @@ void DecrementPendingSpeedRestrictions(Train *v)
}
}
void HandleTraceRestrictSpeedRestrictionAction(const TraceRestrictProgramResult &out, Train *v, Trackdir signal_td)
{
if (out.flags & TRPRF_SPEED_RESTRICTION_SET) {
SetBit(v->flags, VRF_PENDING_SPEED_RESTRICTION);
auto range = pending_speed_restriction_change_map.equal_range(v->index);
for (auto it = range.first; it != range.second; ++it) {
if ((uint16) (out.speed_restriction + 0xFFFF) < (uint16) (it->second.new_speed + 0xFFFF)) it->second.new_speed = out.speed_restriction;
}
uint16 flags = 0;
if (IsDiagonalTrack(TrackdirToTrack(signal_td))) SetBit(flags, PSRCF_DIAGONAL);
pending_speed_restriction_change_map.insert({ v->index, { (uint16) (v->gcache.cached_total_length + (HasBit(flags, PSRCF_DIAGONAL) ? 8 : 4)), out.speed_restriction, v->speed_restriction, flags } });
if ((uint16) (out.speed_restriction + 0xFFFF) < (uint16) (v->speed_restriction + 0xFFFF)) v->speed_restriction = out.speed_restriction;
}
}
/**
* Move a vehicle chain one movement stop forwards.
* @param v First vehicle to move.
@ -5366,17 +5416,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
v->reverse_distance = v->gcache.cached_total_length + (IsDiagonalTrack(TrackdirToTrack(dir)) ? 16 : 8);
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
}
if (out.flags & TRPRF_SPEED_RESTRICTION_SET) {
SetBit(v->flags, VRF_PENDING_SPEED_RESTRICTION);
auto range = pending_speed_restriction_change_map.equal_range(v->index);
for (auto it = range.first; it != range.second; ++it) {
if ((uint16) (out.speed_restriction + 0xFFFF) < (uint16) (it->second.new_speed + 0xFFFF)) it->second.new_speed = out.speed_restriction;
}
uint16 flags = 0;
if (IsDiagonalTrack(TrackdirToTrack(dir))) SetBit(flags, PSRCF_DIAGONAL);
pending_speed_restriction_change_map.insert({ v->index, { (uint16) (v->gcache.cached_total_length + (HasBit(flags, PSRCF_DIAGONAL) ? 8 : 4)), out.speed_restriction, v->speed_restriction, flags } });
if ((uint16) (out.speed_restriction + 0xFFFF) < (uint16) (v->speed_restriction + 0xFFFF)) v->speed_restriction = out.speed_restriction;
}
HandleTraceRestrictSpeedRestrictionAction(out, v, dir);
}
}
}
@ -5519,6 +5559,31 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* Entered wormhole set counters. */
v->wait_counter = (TILE_SIZE * simulated_wormhole_signals) - TILE_SIZE;
v->tunnel_bridge_signal_num = 0;
if (v->IsFrontEngine() && IsTunnelBridgeSignalSimulationEntrance(old_tile) && IsTunnelBridgeRestrictedSignal(old_tile)) {
const Trackdir trackdir = GetTunnelBridgeEntranceTrackdir(old_tile);
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(old_tile, TrackdirToTrack(trackdir));
if (prog && prog->actions_used_flags & (TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_RELEASE_FRONT | TRPAUF_SPEED_RESTRICTION | TRPAUF_CHANGE_COUNTER)) {
TraceRestrictProgramResult out;
TraceRestrictProgramInput input(old_tile, trackdir, nullptr, nullptr);
input.permitted_slot_operations = TRPISP_ACQUIRE | TRPISP_RELEASE_FRONT | TRPISP_CHANGE_COUNTER;
prog->Execute(v, input, out);
HandleTraceRestrictSpeedRestrictionAction(out, v, trackdir);
}
}
if (v->Next() == nullptr && IsTunnelBridgeSignalSimulationEntrance(old_tile) && IsTunnelBridgeRestrictedSignal(old_tile)) {
const Trackdir trackdir = GetTunnelBridgeEntranceTrackdir(old_tile);
const Track track = TrackdirToTrack(trackdir);
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(old_tile, track);
if (prog && prog->actions_used_flags & TRPAUF_SLOT_RELEASE_BACK) {
TraceRestrictProgramResult out;
TraceRestrictProgramInput input(old_tile, trackdir, nullptr, nullptr);
input.permitted_slot_operations = TRPISP_RELEASE_BACK;
prog->Execute(first, input, out);
}
}
}
uint distance = v->wait_counter;
@ -5534,6 +5599,17 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
return false;
}
leaving = true;
if (IsTunnelBridgeRestrictedSignal(gp.new_tile) && IsTunnelBridgeSignalSimulationExit(gp.new_tile)) {
const Trackdir trackdir = GetTunnelBridgeExitTrackdir(gp.new_tile);
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.new_tile, TrackdirToTrack(trackdir));
if (prog && prog->actions_used_flags & (TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_RELEASE_FRONT | TRPAUF_SPEED_RESTRICTION | TRPAUF_CHANGE_COUNTER)) {
TraceRestrictProgramResult out;
TraceRestrictProgramInput input(gp.new_tile, trackdir, nullptr, nullptr);
input.permitted_slot_operations = TRPISP_ACQUIRE | TRPISP_RELEASE_FRONT | TRPISP_CHANGE_COUNTER;
prog->Execute(v, input, out);
HandleTraceRestrictSpeedRestrictionAction(out, v, trackdir);
}
}
} else {
if (IsTooCloseBehindTrain(v, gp.new_tile, v->wait_counter, distance == 0)) {
if (distance == 0) v->wait_counter = 0;
@ -5765,6 +5841,22 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
}
}
if (IsTileType(gp.old_tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExit(gp.old_tile) && IsTunnelBridgeRestrictedSignal(gp.old_tile)) {
const TrackdirBits rev_tracks = TrackBitsToTrackdirBits(GetTunnelBridgeTrackBits(gp.old_tile)) & DiagdirReachesTrackdirs(ReverseDiagDir(enterdir));
const Trackdir rev_trackdir = FindFirstTrackdir(rev_tracks);
const Track track = TrackdirToTrack(rev_trackdir);
if (TrackdirEntersTunnelBridge(gp.old_tile, rev_trackdir)) {
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.old_tile, track);
if (prog && prog->actions_used_flags & TRPAUF_SLOT_RELEASE_BACK) {
TraceRestrictProgramResult out;
TraceRestrictProgramInput input(gp.old_tile, ReverseTrackdir(rev_trackdir), nullptr, nullptr);
input.permitted_slot_operations = TRPISP_RELEASE_BACK;
prog->Execute(first, input, out);
}
}
}
}
}

@ -48,6 +48,7 @@
#include "object_map.h"
#include "newgrf_station.h"
#include "station_func.h"
#include "tracerestrict.h"
#include "table/strings.h"
#include "table/bridge_land.h"
@ -1275,6 +1276,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
c->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
if (IsTunnelBridgeWithSignalSimulation(tile)) { // handle tunnel/bridge signals.
c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, endtile);
TraceRestrictNotifySignalRemoval(tile, track);
TraceRestrictNotifySignalRemoval(endtile, track);
}
DirtyCompanyInfrastructureWindows(owner);
}
@ -1410,6 +1413,10 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
/* Update company infrastructure counts. */
if (rail) {
SubtractRailTunnelBridgeInfrastructure(tile, endtile);
if (IsTunnelBridgeWithSignalSimulation(tile)) {
TraceRestrictNotifySignalRemoval(tile, FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)));
TraceRestrictNotifySignalRemoval(endtile, FindFirstTrack(GetAcrossTunnelBridgeTrackBits(endtile)));
}
} else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) {
SubtractRoadTunnelBridgeInfrastructure(tile, endtile);
if (RoadLayoutChangeNotificationEnabled(false)) {
@ -1747,7 +1754,9 @@ static void DrawTunnelBridgeRampSingleSignal(const TileInfo *ti, bool is_green,
aspect = 1;
}
}
PalSpriteID sprite = GetCustomSignalSprite(rti, ti->tile, type, variant, aspect).sprite;
bool show_restricted = IsTunnelBridgeRestrictedSignal(ti->tile);
const CustomSignalSpriteResult result = GetCustomSignalSprite(rti, ti->tile, type, variant, aspect, false, show_restricted);
PalSpriteID sprite = result.sprite;
bool is_custom_sprite = (sprite.sprite != 0);
if (is_custom_sprite) {
@ -1762,7 +1771,26 @@ static void DrawTunnelBridgeRampSingleSignal(const TileInfo *ti, bool is_green,
}
}
AddSortableSpriteToDraw(sprite.sprite, sprite.pal, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR);
if (is_custom_sprite && show_restricted && _settings_client.gui.show_restricted_signal_default && !result.restricted_valid && variant == SIG_ELECTRIC) {
/* Use duplicate sprite block, instead of GRF-specified signals */
sprite = { (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) ? SPR_DUP_ORIGINAL_SIGNALS_BASE : SPR_DUP_SIGNALS_BASE - 16, PAL_NONE };
sprite.sprite += type * 16 + variant * 64 + position * 2 + is_green + (IsSignalSpritePBS(type) ? 64 : 0);
is_custom_sprite = false;
}
if (!is_custom_sprite && show_restricted && variant == SIG_ELECTRIC) {
if (type == SIGTYPE_PBS || type == SIGTYPE_PBS_ONEWAY) {
static const SubSprite lower_part = { -50, -10, 50, 50 };
static const SubSprite upper_part = { -50, -50, 50, -11 };
AddSortableSpriteToDraw(sprite.sprite, SPR_TRACERESTRICT_BASE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR, &lower_part);
AddSortableSpriteToDraw(sprite.sprite, PAL_NONE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR, &upper_part);
} else {
AddSortableSpriteToDraw(sprite.sprite, SPR_TRACERESTRICT_BASE + (type == SIGTYPE_NO_ENTRY ? 0 : 1), x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR);
}
} else {
AddSortableSpriteToDraw(sprite.sprite, sprite.pal, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR);
}
}
/* Draws a signal on tunnel / bridge entrance tile. */
@ -2120,14 +2148,14 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params)
image = (SignalOffsets)(image ^ 1);
}
if (IsTunnelBridgeSignalSimulationEntrance(ti->tile)) {
DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeEntranceSignalState(ti->tile), image, position, SIGTYPE_NORMAL, variant, false, false);
DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeEntranceSignalState(ti->tile), image, position, SIGTYPE_NORMAL, variant, IsTunnelBridgeRestrictedSignal(ti->tile), false);
}
if (IsTunnelBridgeSignalSimulationExit(ti->tile)) {
SignalType type = SIGTYPE_NORMAL;
if (IsTunnelBridgePBS(ti->tile)) {
type = IsTunnelBridgeSignalSimulationEntrance(ti->tile) ? SIGTYPE_PBS : SIGTYPE_PBS_ONEWAY;
}
DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeExitSignalState(ti->tile), (SignalOffsets)(image ^ 1), position ^ 1, type, variant, false, true);
DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeExitSignalState(ti->tile), (SignalOffsets)(image ^ 1), position ^ 1, type, variant, IsTunnelBridgeRestrictedSignal(ti->tile), true);
}
};
switch (t) {
@ -2524,6 +2552,10 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
} else { // IsBridge(tile)
td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : IsTunnelBridgeWithSignalSimulation(tile) ? STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt];
}
if (IsTunnelBridgeWithSignalSimulation(tile) && IsTunnelBridgeRestrictedSignal(tile)) {
SetDParamX(td->dparam, 0, td->str);
td->str = STR_LAI_RAIL_DESCRIPTION_RESTRICTED_SIGNAL;
}
td->owner[0] = GetTileOwner(tile);
if (tt == TRANSPORT_ROAD) {
@ -2718,6 +2750,24 @@ static void TileLoop_TunnelBridge(TileIndex tile)
static bool ClickTile_TunnelBridge(TileIndex tile)
{
if (_ctrl_pressed && IsTunnelBridgeWithSignalSimulation(tile)) {
TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0));
if (trackbits & TRACK_BIT_VERT) { // N-S direction
trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT;
}
if (trackbits & TRACK_BIT_HORZ) { // E-W direction
trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER;
}
Track track = FindFirstTrack(trackbits);
if (HasTrack(GetAcrossTunnelBridgeTrackBits(tile), track)) {
ShowTraceRestrictProgramWindow(tile, track);
return true;
}
}
/* Show vehicles found in tunnel. */
if (IsTunnelTile(tile)) {
int count = 0;

@ -561,6 +561,26 @@ static inline void SetTunnelBridgeSignalSimulationSpacing(TileIndex t, uint spac
SB(_me[t].m8, 12, 4, spacing - 1);
}
/**
* Does tunnel/bridge signal tile have "one or more trace restrict mappings present" bit set
* @param tile the tile to check
*/
static inline bool IsTunnelBridgeRestrictedSignal(TileIndex tile)
{
assert_tile(IsTunnelBridgeWithSignalSimulation(tile), tile);
return (bool) GB(_m[tile].m3, 6, 1);
}
/**
* Set tunnel/bridge signal tile "one or more trace restrict mappings present" bit
* @param tile the tile to set
*/
static inline void SetTunnelBridgeRestrictedSignal(TileIndex tile, bool is_restricted)
{
assert_tile(IsTunnelBridgeWithSignalSimulation(tile), tile);
SB(_m[tile].m3, 6, 1, is_restricted);
}
static inline Trackdir GetTunnelBridgeExitTrackdir(TileIndex t, DiagDirection tunnel_bridge_dir)
{
return TrackEnterdirToTrackdir((Track)FIND_FIRST_BIT(GetAcrossTunnelBridgeTrackBits(t)), ReverseDiagDir(tunnel_bridge_dir));

@ -253,6 +253,9 @@ SpriteID TileZoneCheckTraceRestrictEvaluation(TileIndex tile, Owner owner)
if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile) && IsRestrictedSignal(tile)) {
return SPR_ZONING_INNER_HIGHLIGHT_RED;
}
if (IsTunnelBridgeWithSignalSimulation(tile)) {
return SPR_ZONING_INNER_HIGHLIGHT_RED;
}
if (unlikely(HasBit(_misc_debug_flags, MDF_ZONING_RS_WATER_FLOOD_STATE)) && IsNonFloodingWaterTile(tile)) {
return SPR_ZONING_INNER_HIGHLIGHT_YELLOW;
}

Loading…
Cancel
Save