diff --git a/docs/landscape.html b/docs/landscape.html
index 43ef7e8cea..a16ab6c54a 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -1496,9 +1496,75 @@
+
m2 bits 2..0: rail bridge heads track reserved for pbs
+
+
+ 0 |
+ not reserved |
+
+
+ 1 |
+ X direction |
+
+
+ 2 |
+ Y direction |
+
+
+ 3 |
+ north corner (W-E) |
+
+
+ 4 |
+ south corner (W-E) |
+
+
+ 5 |
+ west corner (N-S) |
+
+
+ 6 |
+ east corner (N-S) |
+
+
+
+ m2 bit 3: rail bridge heads opposite track is reserved, too
m3 bits 7..4: owner of tram
m3 bits 3..0: bits 3..0 of track type for railway
- m5 bit 4: pbs reservation state for railway
+ m4 bits 5..0: rail bridge heads track layout: bit set = track present:
+
+
+ bit 0: |
+ in the X direction |
+
+
+
+ bit 1: |
+ in the Y direction |
+
+
+
+ bit 2: |
+ in the north corner (direction W-E) |
+
+
+
+ bit 3: |
+ in the south corner (direction W-E) |
+
+
+
+ bit 4: |
+ in the west corner (direction N-S) |
+
+
+
+ bit 5: |
+ in the east corner (direction N-S) |
+
+
+
+ m5 bit 4: pbs reservation state for railway (tunnel only)
m5 bit 7 clear: tunnel entrance/exit
m5 bit 7 set: bridge ramp
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index 75167ef076..1e094ef8da 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -345,9 +345,9 @@ the array so you can quickly see what is used and what is not.
-inherit- |
-inherit- |
-inherit- |
- PPPP PPPP PPPP OOOO |
- -inherit- |
+ PPPP PPPP PPPP PPPP |
-inherit- |
+ OOPP PPPP |
-inherit- |
OPXX XXPP |
-inherit- |
diff --git a/src/bridge_map.h b/src/bridge_map.h
index a260af4419..c3ae885f0a 100644
--- a/src/bridge_map.h
+++ b/src/bridge_map.h
@@ -180,19 +180,24 @@ static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Ow
*/
static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r, bool upgrade)
{
- // Backup bridge signal data.
+ /* Backup bridge signal and custom bridgehead data. */
auto m2_backup = _m[t].m2;
+ auto m4_backup = _m[t].m4;
auto m5_backup = _m[t].m5;
auto m6_backup = _me[t].m6;
MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL, r);
- // Restore bridge signal data if we're upgrading an existing bridge.
if (upgrade) {
+ /* Restore bridge signal and custom bridgehead data if we're upgrading an existing bridge. */
_m[t].m2 = m2_backup;
+ SB(_m[t].m4, 0, 6, GB(m4_backup, 0, 6));
SB(_m[t].m5, 4, 3, GB(m5_backup, 4, 3));
SB(_me[t].m6, 0, 2, GB(m6_backup, 0, 2));
SB(_me[t].m6, 6, 1, GB(m6_backup, 6, 1));
+ } else {
+ /* Set bridge head tracks to axial track only. */
+ SB(_m[t].m4, 0, 6, DiagDirToDiagTrackBits(d));
}
}
@@ -275,4 +280,195 @@ static inline void SetCustomBridgeHeadRoadBits(TileIndex t, RoadType rt, RoadBit
}
}
+/**
+ * Checks if this tile is a rail bridge head
+ * @param t The tile to analyze
+ * @return true if it is a rail bridge head
+ */
+static inline bool IsRailBridgeHeadTile(TileIndex t)
+{
+ return IsBridgeTile(t) && (TransportType)GB(_m[t].m5, 2, 2) == TRANSPORT_RAIL;
+}
+
+/**
+ * Checks if this tile is a flat rail bridge head
+ * @param t The tile to analyze
+ * @return true if it is a flat rail bridge head
+ */
+static inline bool IsFlatRailBridgeHeadTile(TileIndex t)
+{
+ return IsRailBridgeHeadTile(t) && HasBridgeFlatRamp(GetTileSlope(t), DiagDirToAxis((DiagDirection)GB(_m[t].m5, 0, 2)));
+}
+
+/**
+ * Returns the track bits for a (possibly custom) rail bridge head
+ * @param tile the tile to get the track bits from
+ * @pre IsRailBridgeHeadTile(t)
+ * @return road bits for the bridge head
+ */
+static inline TrackBits GetCustomBridgeHeadTrackBits(TileIndex t)
+{
+ assert(IsRailBridgeHeadTile(t));
+ return (TrackBits)GB(_m[t].m4, 0, 6);
+}
+
+/**
+ * Sets the road track for a (possibly custom) rail bridge head
+ * @param t the tile to set the track bits of
+ * @param b the new track bits for the tile
+ * @pre IsRailBridgeHeadTile(t)
+ */
+static inline void SetCustomBridgeHeadTrackBits(TileIndex t, TrackBits b)
+{
+ assert(IsRailBridgeHeadTile(t));
+ SB(_m[t].m4, 0, 6, b);
+}
+
+/**
+ * Checks if this rail bridge head is a custom bridge head
+ * @param t The tile to analyze
+ * @pre IsRailBridgeHeadTile(t)
+ * @return true if it is a custom bridge head
+ */
+static inline bool IsRailCustomBridgeHead(TileIndex t)
+{
+ assert(IsRailBridgeHeadTile(t));
+ return GetCustomBridgeHeadTrackBits(t) != DiagDirToDiagTrackBits((DiagDirection)GB(_m[t].m5, 0, 2));
+}
+
+/**
+ * Checks if this tile is a rail bridge head with a custom bridge head
+ * @param t The tile to analyze
+ * @return true if it is a rail bridge head with a custom bridge head
+ */
+static inline bool IsRailCustomBridgeHeadTile(TileIndex t)
+{
+ return IsRailBridgeHeadTile(t) && IsRailCustomBridgeHead(t);
+}
+
+/**
+ * Checks if this tile is a bridge head with a custom bridge head
+ * @param t The tile to analyze
+ * @return true if it is a bridge head with a custom bridge head
+ */
+static inline bool IsCustomBridgeHeadTile(TileIndex t)
+{
+ return IsRailCustomBridgeHeadTile(t) || IsRoadCustomBridgeHeadTile(t);
+}
+
+/**
+ * Get the reserved track bits for a rail bridge head
+ * @pre IsRailBridgeHeadTile(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetBridgeReservationTrackBits(TileIndex t)
+{
+ assert(IsRailBridgeHeadTile(t));
+ byte track_b = GB(_m[t].m2, 0, 3);
+ Track track = (Track)(track_b - 1); // map array saves Track+1
+ if (track_b == 0) return TRACK_BIT_NONE;
+ return (TrackBits)(TrackToTrackBits(track) | (HasBit(_m[t].m2, 3) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0));
+}
+
+/**
+ * Sets the reserved track bits of the rail bridge head
+ * @pre IsRailBridgeHeadTile(t)
+ * @param t the tile to change
+ * @param b the track bits
+ */
+static inline void SetBridgeReservationTrackBits(TileIndex t, TrackBits b)
+{
+ assert(IsRailBridgeHeadTile(t));
+ assert(!TracksOverlap(b));
+ Track track = RemoveFirstTrack(&b);
+ SB(_m[t].m2, 0, 3, track == INVALID_TRACK ? 0 : track + 1);
+ SB(_m[t].m2, 3, 1, (byte)(b != TRACK_BIT_NONE));
+}
+
+
+/**
+ * Try to reserve a specific track on a rail bridge head tile
+ * @pre IsRailBridgeHeadTile(t) && HasBit(GetCustomBridgeHeadTrackBits(tile), t)
+ * @param tile the tile
+ * @param t the rack to reserve
+ * @return true if successful
+ */
+static inline bool TryReserveRailBridgeHead(TileIndex tile, Track t)
+{
+ assert(IsRailBridgeHeadTile(tile));
+ assert(HasBit(GetCustomBridgeHeadTrackBits(tile), t));
+ TrackBits bits = TrackToTrackBits(t);
+ TrackBits res = GetBridgeReservationTrackBits(tile);
+ if ((res & bits) != TRACK_BIT_NONE) return false; // already reserved
+ res |= bits;
+ if (TracksOverlap(res)) return false; // crossing reservation present
+ SetBridgeReservationTrackBits(tile, res);
+ return true;
+}
+
+
+/**
+ * Lift the reservation of a specific track on a rail bridge head tile
+ * @pre IsRailBridgeHeadTile(t) && HasBit(GetCustomBridgeHeadTrackBits(tile), t)
+ * @param tile the tile
+ * @param t the track to free
+ */
+static inline void UnreserveRailBridgeHeadTrack(TileIndex tile, Track t)
+{
+ assert(IsRailBridgeHeadTile(tile));
+ assert(HasBit(GetCustomBridgeHeadTrackBits(tile), t));
+ TrackBits res = GetBridgeReservationTrackBits(tile);
+ res &= ~TrackToTrackBits(t);
+ SetBridgeReservationTrackBits(tile, res);
+}
+
+/**
+ * Get the possible track bits of the bridge head tile onto/across the bridge
+ * @pre IsRailBridgeHeadTile(t)
+ * @param t the tile
+ * @return reservation state
+ */
+static inline TrackBits GetAcrossBridgePossibleTrackBits(TileIndex t)
+{
+ assert(IsRailBridgeHeadTile(t));
+ return DiagdirReachesTracks(ReverseDiagDir((DiagDirection)GB(_m[t].m5, 0, 2)));
+}
+
+/**
+ * Get the reserved track bits of the bridge head tile onto/across the bridge
+ * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @return reservation state
+ */
+static inline TrackBits GetAcrossBridgeReservationTrackBits(TileIndex t)
+{
+ return GetBridgeReservationTrackBits(t) & GetAcrossBridgePossibleTrackBits(t);
+}
+
+/**
+ * Get the reservation state of the bridge head tile onto/across the bridge
+ * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @return reservation state
+ */
+static inline bool HasAcrossBridgeReservation(TileIndex t)
+{
+ return GetAcrossBridgeReservationTrackBits(t) != TRACK_BIT_NONE;
+}
+
+/**
+ * Lift the reservation of a specific track on a rail bridge head tile
+ * @pre IsRailBridgeHeadTile(t)
+ * @param tile the tile
+ */
+static inline void UnreserveAcrossRailBridgeHead(TileIndex tile)
+{
+ assert(IsRailBridgeHeadTile(tile));
+ TrackBits res = GetAcrossBridgeReservationTrackBits(tile);
+ if (res != TRACK_BIT_NONE) {
+ SetBridgeReservationTrackBits(tile, GetBridgeReservationTrackBits(tile) & ~res);
+ }
+}
+
#endif /* BRIDGE_MAP_H */
diff --git a/src/elrail.cpp b/src/elrail.cpp
index 37ac9f90bd..e75ead44ca 100644
--- a/src/elrail.cpp
+++ b/src/elrail.cpp
@@ -104,7 +104,7 @@ static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
if (override != NULL && (IsTunnel(t) || GetTunnelBridgeLength(t, GetOtherBridgeEnd(t)) > 0)) {
*override = 1 << GetTunnelBridgeDirection(t);
}
- return DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t));
+ return GetTunnelBridgeTrackBits(t);
case MP_ROAD:
if (!IsLevelCrossing(t)) return TRACK_BIT_NONE;
@@ -200,6 +200,8 @@ static void AdjustTileh(TileIndex tile, Slope *tileh)
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
if (IsTunnel(tile)) {
*tileh = SLOPE_STEEP; // XXX - Hack to make tunnel entrances to always have a pylon
+ } else if (IsRailCustomBridgeHeadTile(tile)) {
+ /* no change */
} else if (*tileh != SLOPE_FLAT) {
*tileh = SLOPE_FLAT;
} else {
diff --git a/src/landscape.cpp b/src/landscape.cpp
index 921de6f153..ce35fc369f 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -353,7 +353,7 @@ Slope GetFoundationSlope(TileIndex tile, int *z)
bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here)
{
- if (IsRoadCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NW) return false;
+ if (IsCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NW) return false;
int z;
@@ -372,7 +372,7 @@ bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here)
bool HasFoundationNE(TileIndex tile, Slope slope_here, uint z_here)
{
- if (IsRoadCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NE) return false;
+ if (IsCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NE) return false;
int z;
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 68f4daa329..a835ddca22 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1878,6 +1878,8 @@ STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to
STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS :Enable road custom bridge heads: {STRING2}
STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS_HELPTEXT :Allow road bridges to have custom, non-straight flat entry/exit tiles
+STR_CONFIG_SETTING_ENABLE_RAIL_CUSTOM_BRIDGE_HEADS :Enable rail custom bridge heads: {STRING2}
+STR_CONFIG_SETTING_ENABLE_RAIL_CUSTOM_BRIDGE_HEADS_HELPTEXT :Allow rail bridges to have custom, non-straight flat entry/exit tiles
STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change setting value
diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp
index 94c7dd6f2e..dfe66a4f58 100644
--- a/src/newgrf_station.cpp
+++ b/src/newgrf_station.cpp
@@ -209,7 +209,7 @@ static uint32 GetRailContinuationInfo(TileIndex tile)
/* With tunnels and bridges the tile has tracks, but they are not necessarily connected
* with the next tile because the ramp is not going in the right direction. */
- if (IsTileType(neighbour_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(neighbour_tile) != *diagdir) {
+ if (IsTileType(neighbour_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(neighbour_tile) == ReverseDiagDir(*diagdir)) {
continue;
}
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 4f901d6ed3..b9131a0bd2 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -127,7 +127,7 @@ struct CFollowTrackT
m_old_td = old_td;
m_err = EC_NONE;
assert(((TrackStatusToTrackdirBits(GetTileTrackStatus(m_old_tile, TT(), IsRoadTT() ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)) & TrackdirToTrackdirBits(m_old_td)) != 0) ||
- (IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR)); // Disable the assertion for single tram bits
+ (IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR) || (IsRailTT() && IsRailCustomBridgeHeadTile(m_old_tile))); // Disable the assertion for single tram bits
m_exitdir = TrackdirToExitdir(m_old_td);
if (ForcedReverse()) return true;
if (!CanExitOldTile()) return false;
@@ -155,7 +155,7 @@ struct CFollowTrackT
return false;
}
- if (!Allow90degTurns()) {
+ if (!Allow90degTurns() && m_tiles_skipped == 0) {
m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td);
if (m_new_td_bits == TRACKDIR_BIT_NONE) {
m_err = EC_90DEG;
@@ -218,7 +218,7 @@ protected:
m_tiles_skipped = GetTunnelBridgeLength(m_new_tile, m_old_tile);
return;
}
- if (!IsRoadCustomBridgeHeadTile(m_old_tile)) assert(ReverseDiagDir(enterdir) == m_exitdir);
+ if (!IsRoadCustomBridgeHeadTile(m_old_tile) && !IsRailCustomBridgeHeadTile(m_old_tile)) assert(ReverseDiagDir(enterdir) == m_exitdir);
}
/* normal or station tile, do one step */
@@ -369,13 +369,22 @@ protected:
}
}
} else { // IsBridge(m_new_tile)
+ DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile);
+ if (!m_is_bridge && ramp_enderdir == ReverseDiagDir(m_exitdir)) {
+ m_err = EC_NO_WAY;
+ return false;
+ }
if (!m_is_bridge && IsRoadTT() && IsRoadCustomBridgeHeadTile(m_new_tile)) {
if (!(DiagDirToRoadBits(ReverseDiagDir(m_exitdir)) & GetCustomBridgeHeadRoadBits(m_new_tile, IsTram() ? ROADTYPE_TRAM : ROADTYPE_ROAD))) {
m_err = EC_NO_WAY;
return false;
}
+ } else if (!m_is_bridge && IsRailTT() && IsRailCustomBridgeHeadTile(m_new_tile)) {
+ if (!(DiagdirReachesTracks(m_exitdir) & GetCustomBridgeHeadTrackBits(m_new_tile))) {
+ m_err = EC_NO_WAY;
+ return false;
+ }
} else if (!m_is_bridge) {
- DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile);
if (ramp_enderdir != m_exitdir) {
m_err = EC_NO_WAY;
return false;
diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp
index 939d6afbe7..a3c18f1d43 100644
--- a/src/pathfinder/yapf/yapf_costrail.hpp
+++ b/src/pathfinder/yapf/yapf_costrail.hpp
@@ -378,7 +378,7 @@ public:
}
}
}
- if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExitOnly(tile) && DiagDirToDiagTrackdir(GetTunnelBridgeDirection(tile)) == trackdir) {
+ 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;
}
diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp
index caa6cb032d..7989450e73 100644
--- a/src/pathfinder/yapf/yapf_rail.cpp
+++ b/src/pathfinder/yapf/yapf_rail.cpp
@@ -646,7 +646,7 @@ bool YapfTrainCheckReverse(const Train *v)
int reverse_penalty = 0;
- if (v->track == TRACK_BIT_WORMHOLE) {
+ if (v->track & TRACK_BIT_WORMHOLE) {
/* front in tunnel / on bridge */
DiagDirection dir_into_wormhole = GetTunnelBridgeDirection(tile);
@@ -661,7 +661,7 @@ bool YapfTrainCheckReverse(const Train *v)
reverse_penalty -= DistanceManhattan(cur_tile, tile) * YAPF_TILE_LENGTH;
}
- if (last_veh->track == TRACK_BIT_WORMHOLE) {
+ if (last_veh->track & TRACK_BIT_WORMHOLE) {
/* back in tunnel / on bridge */
DiagDirection dir_into_wormhole = GetTunnelBridgeDirection(tile_rev);
diff --git a/src/pbs.cpp b/src/pbs.cpp
index 87cfc6c159..ddac679980 100644
--- a/src/pbs.cpp
+++ b/src/pbs.cpp
@@ -100,7 +100,8 @@ bool TryReserveRailTrackdir(TileIndex tile, Trackdir td, bool trigger_stations)
*/
bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
{
- assert(HasTrack(TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)), t));
+ assert_msg((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & TrackToTrackBits(t)) != 0,
+ "%X, %X, %X, %X", TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)), tile, t, TrackToTrackBits(t));
if (_settings_client.gui.show_track_reservation) {
/* show the reserved rail if needed */
@@ -153,10 +154,18 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
break;
case MP_TUNNELBRIDGE:
- if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && !GetTunnelBridgeReservationTrackBits(tile)) {
- SetTunnelBridgeReservation(tile, true);
- MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
- return true;
+ if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
+ if (IsTunnel(tile) && !HasTunnelReservation(tile)) {
+ SetTunnelReservation(tile, true);
+ MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
+ return true;
+ }
+ if (IsBridge(tile)) {
+ if (TryReserveRailBridgeHead(tile, t)) {
+ MarkBridgeOrTunnelDirtyOnReservationChange(tile, ZOOM_LVL_DRAW_MAP);
+ return true;
+ }
+ }
}
break;
@@ -187,7 +196,7 @@ void UnreserveRailTrackdir(TileIndex tile, Trackdir td)
*/
void UnreserveRailTrack(TileIndex tile, Track t)
{
- assert(HasTrack(TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)), t));
+ assert(TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & TrackToTrackBits(t));
if (_settings_client.gui.show_track_reservation) {
if (IsBridgeTile(tile)) {
@@ -223,9 +232,13 @@ void UnreserveRailTrack(TileIndex tile, Track t)
case MP_TUNNELBRIDGE:
if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
- SetTunnelBridgeReservation(tile, false);
- if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
- MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
+ if (IsTunnel(tile)) {
+ SetTunnelReservation(tile, false);
+ } else {
+ UnreserveRailBridgeHeadTrack(tile, t);
+ }
+ if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile) && IsTrackAcrossTunnelBridge(tile, t)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
+ MarkBridgeOrTunnelDirtyOnReservationChange(tile, ZOOM_LVL_DRAW_MAP);
}
break;
@@ -295,7 +308,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
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;
- if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeWithSignalSimulation(tile)) break;
+ if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeWithSignalSimulation(tile) && IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(trackdir))) break;
}
return PBSTileInfo(tile, trackdir, false);
@@ -320,7 +333,7 @@ static Vehicle *FindTrainOnTrackEnum(Vehicle *v, void *data)
if (v->type != VEH_TRAIN || (v->vehstatus & VS_CRASHED)) return NULL;
Train *t = Train::From(v);
- if (t->track == TRACK_BIT_WORMHOLE) {
+ if (t->track & TRACK_BIT_WORMHOLE) {
/* Do not find trains inside signalled bridge/tunnels.
* Trains on the ramp/entrance itself are found though.
*/
@@ -328,7 +341,7 @@ static Vehicle *FindTrainOnTrackEnum(Vehicle *v, void *data)
return NULL;
}
}
- if (t->track == TRACK_BIT_WORMHOLE || HasBit((TrackBits)t->track, TrackdirToTrack(info->res.trackdir))) {
+ if (t->track & TRACK_BIT_WORMHOLE || HasBit((TrackBits)t->track, TrackdirToTrack(info->res.trackdir))) {
t = t->First();
/* ALWAYS return the lowest ID (anti-desync!) */
@@ -372,7 +385,7 @@ PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res)
if (ftoti.best != NULL) *train_on_res = ftoti.best->First();
}
}
- if (*train_on_res == NULL && IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE) && !IsTunnelBridgeWithSignalSimulation(ftoti.res.tile)) {
+ if (*train_on_res == NULL && IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE) && IsTrackAcrossTunnelBridge(ftoti.res.tile, TrackdirToTrack(ftoti.res.trackdir)) && !IsTunnelBridgeWithSignalSimulation(ftoti.res.tile)) {
/* The target tile is a bridge/tunnel, also check the other end tile. */
FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != NULL) *train_on_res = ftoti.best->First();
@@ -419,7 +432,7 @@ Train *GetTrainForReservation(TileIndex tile, Track track)
}
/* Special case for bridges/tunnels: check the other end as well. */
- if (IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) {
+ if (IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE) && IsTrackAcrossTunnelBridge(ftoti.res.tile, TrackdirToTrack(ftoti.res.trackdir))) {
FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != NULL) return ftoti.best;
}
@@ -491,7 +504,7 @@ bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bo
if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
}
- if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
+ if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(trackdir))) {
if (IsTunnelBridgeSignalSimulationEntrance(tile)) {
return true;
}
@@ -533,6 +546,7 @@ bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bo
return include_line_end;
}
if (IsTileType(ft.m_new_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(ft.m_new_tile) == TRANSPORT_RAIL &&
+ IsTrackAcrossTunnelBridge(ft.m_new_tile, TrackdirToTrack(td)) &&
IsTunnelBridgeSignalSimulationExitOnly(ft.m_new_tile) && IsTunnelBridgePBS(ft.m_new_tile)) {
return include_line_end;
}
@@ -585,15 +599,20 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo
return pbs_res_end_wait_test(tile, trackdir);
}
- if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && IsTunnelBridgeSignalSimulationEntrance(tile)) {
+ if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && IsTunnelBridgeSignalSimulationEntrance(tile)
+ && IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(trackdir))) {
if (IsTunnelBridgeSignalSimulationBidirectional(tile)) {
TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
- if (HasTunnelBridgeReservation(other_end) && GetTunnelBridgeExitSignalState(other_end) == SIGNAL_STATE_RED) return false;
+ if (HasAcrossTunnelBridgeReservation(other_end) && GetTunnelBridgeExitSignalState(other_end) == SIGNAL_STATE_RED) return false;
Direction dir = DiagDirToDir(GetTunnelBridgeDirection(other_end));
if (HasVehicleOnPos(other_end, &dir, [](Vehicle *v, void *data) -> Vehicle * {
if (v->type != VEH_TRAIN) return nullptr;
- if (v->direction != *((Direction *) data)) return nullptr;
- return v;
+ DirDiff diff = DirDifference(v->direction, *((Direction *) data));
+ if (diff == DIRDIFF_SAME) return v;
+ if (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT) {
+ if (GetAcrossTunnelBridgeTrackBits(v->tile) & Train::From(v)->track) return v;
+ }
+ return nullptr;
})) return false;
}
return true;
diff --git a/src/rail.h b/src/rail.h
index 1bc403d7fd..d09a076ea2 100644
--- a/src/rail.h
+++ b/src/rail.h
@@ -441,4 +441,16 @@ extern uint8 _sorted_railtypes_size;
*/
#define FOR_ALL_SORTED_RAILTYPES(var) for (uint8 index = 0; index < _sorted_railtypes_size && (var = _sorted_railtypes[index], true) ; index++)
+/** Enum holding the signal offset in the sprite sheet according to the side it is representing. */
+enum SignalOffsets {
+ SIGNAL_TO_SOUTHWEST,
+ SIGNAL_TO_NORTHEAST,
+ SIGNAL_TO_SOUTHEAST,
+ SIGNAL_TO_NORTHWEST,
+ SIGNAL_TO_EAST,
+ SIGNAL_TO_WEST,
+ SIGNAL_TO_SOUTH,
+ SIGNAL_TO_NORTH,
+};
+
#endif /* RAIL_H */
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 259255db59..97df299af1 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -56,18 +56,6 @@ RailType _sorted_railtypes[RAILTYPE_END];
uint8 _sorted_railtypes_size;
TileIndex _rail_track_endtile; ///< The end of a rail track; as hidden return from the rail build/remove command for GUI purposes.
-/** Enum holding the signal offset in the sprite sheet according to the side it is representing. */
-enum SignalOffsets {
- SIGNAL_TO_SOUTHWEST,
- SIGNAL_TO_NORTHEAST,
- SIGNAL_TO_SOUTHEAST,
- SIGNAL_TO_NORTHWEST,
- SIGNAL_TO_EAST,
- SIGNAL_TO_WEST,
- SIGNAL_TO_SOUTH,
- SIGNAL_TO_NORTH,
-};
-
/**
* Reset all rail type information to its default values.
*/
@@ -435,6 +423,25 @@ static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits ex
return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[PR_BUILD_FOUNDATION] : (Money)0);
}
+bool IsValidFlatRailBridgeHeadTrackBits(Slope normalised_slope, DiagDirection bridge_direction, TrackBits tracks)
+{
+ /* bridge_direction c1 c2
+ * 0 0 1
+ * 1 0 3
+ * 2 2 3
+ * 3 2 1
+ */
+ const Corner c1 = (Corner) (bridge_direction & 2);
+ const Corner c2 = (Corner) (((bridge_direction + 1) & 2) + 1);
+ auto test_corner = [&](Corner c) -> bool {
+ if (normalised_slope & SlopeWithOneCornerRaised(c)) return true;
+ Slope effective_slope = normalised_slope | SlopeWithOneCornerRaised(OppositeCorner(c));
+ assert(effective_slope < lengthof(_valid_tracks_on_leveled_foundation));
+ return (_valid_tracks_on_leveled_foundation[effective_slope] & tracks) == tracks;
+ };
+ return test_corner(c1) && test_corner(c2);
+}
+
/* Validate functions for rail building */
static inline bool ValParamTrackOrientation(Track track)
{
@@ -454,6 +461,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
{
RailType railtype = Extract(p1);
Track track = Extract