Merge branch 'custom_bridgeheads' into jgrpp

# Conflicts:
#	docs/landscape.html
#	src/bridge_map.h
#	src/pbs.cpp
#	src/saveload/company_sl.cpp
#	src/saveload/extended_ver_sl.cpp
#	src/settings_type.h
#	src/signal.cpp
#	src/train_cmd.cpp
#	src/tunnel_map.h
#	src/tunnelbridge_cmd.cpp
#	src/tunnelbridge_map.h
#	src/vehicle.cpp
#	src/viewport.cpp
This commit is contained in:
Jonathan G Rennison
2018-07-08 23:04:30 +01:00
29 changed files with 1313 additions and 362 deletions

View File

@@ -1496,9 +1496,75 @@
</tr> </tr>
</table> </table>
</li> </li>
<li style="color: blue">m2 bits 2..0: rail bridge heads track reserved for pbs
<table style="color: blue">
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>not reserved</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>X direction</td>
</tr>
<tr>
<td><tt>2</tt>&nbsp; </td>
<td>Y direction</td>
</tr>
<tr>
<td><tt>3</tt>&nbsp; </td>
<td>north corner (W-E)</td>
</tr>
<tr>
<td><tt>4</tt>&nbsp; </td>
<td>south corner (W-E)</td>
</tr>
<tr>
<td><tt>5</tt>&nbsp; </td>
<td>west corner (N-S)</td>
</tr>
<tr>
<td><tt>6</tt>&nbsp; </td>
<td>east corner (N-S)</td>
</tr>
</table>
</li>
<li style="color: blue">m2 bit 3: rail bridge heads opposite track is reserved, too</li>
<li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of tram</li> <li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of tram</li>
<li style="color: purple">m3 bits 3..0: bits 3..0 of <a href="#TrackType">track type</a> for railway</li> <li style="color: purple">m3 bits 3..0: bits 3..0 of <a href="#TrackType">track type</a> for railway</li>
<li>m5 bit 4: pbs reservation state for railway</li> <li style="color: blue">m4 bits 5..0: rail bridge heads track layout: bit set = track present:
<table style="color: blue">
<tr>
<td nowrap valign=top>bit 0: </td>
<td align=left>in the X direction</td>
</tr>
<tr>
<td nowrap valign=top>bit 1: </td>
<td align=left>in the Y direction</td>
</tr>
<tr>
<td nowrap valign=top>bit 2: </td>
<td align=left>in the north corner (direction W-E)</td>
</tr>
<tr>
<td nowrap valign=top>bit 3: </td>
<td align=left>in the south corner (direction W-E)</td>
</tr>
<tr>
<td nowrap valign=top>bit 4: </td>
<td align=left>in the west corner (direction N-S)</td>
</tr>
<tr>
<td nowrap valign=top>bit 5: </td>
<td align=left>in the east corner (direction N-S)</td>
</tr>
</table>
</li>
<li>m5 bit 4: pbs reservation state for railway (tunnel only)</li>
<li>m5 bit 7 clear: tunnel entrance/exit</li> <li>m5 bit 7 clear: tunnel entrance/exit</li>
<li>m5 bit 7 set: bridge ramp <li>m5 bit 7 set: bridge ramp
<ul> <ul>

View File

@@ -345,9 +345,9 @@ the array so you can quickly see what is used and what is not.
<td class="bits">-inherit-</td> <td class="bits">-inherit-</td>
<td class="bits">-inherit-</td> <td class="bits">-inherit-</td>
<td class="bits">-inherit-</td> <td class="bits">-inherit-</td>
<td class="bits"><span class="used_p">PPPP PPPP PPPP</span> <span class="free">OOOO</span></td> <td class="bits"><span class="used_p">PPPP PPPP PPPP PPPP</span></td>
<td class="bits">-inherit-</td>
<td class="bits">-inherit-</td> <td class="bits">-inherit-</td>
<td class="bits"><span class="free">OO</span><span class="used_p">PP PPPP</span></td>
<td class="bits">-inherit-</td> <td class="bits">-inherit-</td>
<td class="bits"><span class="free">O</span><span class="used_p">P</span>XX XX<span class="used_p">PP</span></td> <td class="bits"><span class="free">O</span><span class="used_p">P</span>XX XX<span class="used_p">PP</span></td>
<td class="bits">-inherit-</td> <td class="bits">-inherit-</td>

View File

@@ -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) 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 m2_backup = _m[t].m2;
auto m4_backup = _m[t].m4;
auto m5_backup = _m[t].m5; auto m5_backup = _m[t].m5;
auto m6_backup = _me[t].m6; auto m6_backup = _me[t].m6;
MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL, r); MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL, r);
// Restore bridge signal data if we're upgrading an existing bridge.
if (upgrade) { if (upgrade) {
/* Restore bridge signal and custom bridgehead data if we're upgrading an existing bridge. */
_m[t].m2 = m2_backup; _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(_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, 0, 2, GB(m6_backup, 0, 2));
SB(_me[t].m6, 6, 1, GB(m6_backup, 6, 1)); 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 */ #endif /* BRIDGE_MAP_H */

View File

@@ -104,7 +104,7 @@ static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
if (override != NULL && (IsTunnel(t) || GetTunnelBridgeLength(t, GetOtherBridgeEnd(t)) > 0)) { if (override != NULL && (IsTunnel(t) || GetTunnelBridgeLength(t, GetOtherBridgeEnd(t)) > 0)) {
*override = 1 << GetTunnelBridgeDirection(t); *override = 1 << GetTunnelBridgeDirection(t);
} }
return DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)); return GetTunnelBridgeTrackBits(t);
case MP_ROAD: case MP_ROAD:
if (!IsLevelCrossing(t)) return TRACK_BIT_NONE; if (!IsLevelCrossing(t)) return TRACK_BIT_NONE;
@@ -200,6 +200,8 @@ static void AdjustTileh(TileIndex tile, Slope *tileh)
if (IsTileType(tile, MP_TUNNELBRIDGE)) { if (IsTileType(tile, MP_TUNNELBRIDGE)) {
if (IsTunnel(tile)) { if (IsTunnel(tile)) {
*tileh = SLOPE_STEEP; // XXX - Hack to make tunnel entrances to always have a pylon *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) { } else if (*tileh != SLOPE_FLAT) {
*tileh = SLOPE_FLAT; *tileh = SLOPE_FLAT;
} else { } else {

View File

@@ -353,7 +353,7 @@ Slope GetFoundationSlope(TileIndex tile, int *z)
bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here) 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; 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) 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; int z;

View File

@@ -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 :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_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 STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change setting value

View File

@@ -209,7 +209,7 @@ static uint32 GetRailContinuationInfo(TileIndex tile)
/* With tunnels and bridges the tile has tracks, but they are not necessarily connected /* 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. */ * 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; continue;
} }

View File

@@ -127,7 +127,7 @@ struct CFollowTrackT
m_old_td = old_td; m_old_td = old_td;
m_err = EC_NONE; m_err = EC_NONE;
assert(((TrackStatusToTrackdirBits(GetTileTrackStatus(m_old_tile, TT(), IsRoadTT() ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)) & TrackdirToTrackdirBits(m_old_td)) != 0) || 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); m_exitdir = TrackdirToExitdir(m_old_td);
if (ForcedReverse()) return true; if (ForcedReverse()) return true;
if (!CanExitOldTile()) return false; if (!CanExitOldTile()) return false;
@@ -155,7 +155,7 @@ struct CFollowTrackT
return false; return false;
} }
if (!Allow90degTurns()) { if (!Allow90degTurns() && m_tiles_skipped == 0) {
m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td); m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td);
if (m_new_td_bits == TRACKDIR_BIT_NONE) { if (m_new_td_bits == TRACKDIR_BIT_NONE) {
m_err = EC_90DEG; m_err = EC_90DEG;
@@ -218,7 +218,7 @@ protected:
m_tiles_skipped = GetTunnelBridgeLength(m_new_tile, m_old_tile); m_tiles_skipped = GetTunnelBridgeLength(m_new_tile, m_old_tile);
return; 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 */ /* normal or station tile, do one step */
@@ -369,13 +369,22 @@ protected:
} }
} }
} else { // IsBridge(m_new_tile) } 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 (!m_is_bridge && IsRoadTT() && IsRoadCustomBridgeHeadTile(m_new_tile)) {
if (!(DiagDirToRoadBits(ReverseDiagDir(m_exitdir)) & GetCustomBridgeHeadRoadBits(m_new_tile, IsTram() ? ROADTYPE_TRAM : ROADTYPE_ROAD))) { if (!(DiagDirToRoadBits(ReverseDiagDir(m_exitdir)) & GetCustomBridgeHeadRoadBits(m_new_tile, IsTram() ? ROADTYPE_TRAM : ROADTYPE_ROAD))) {
m_err = EC_NO_WAY; m_err = EC_NO_WAY;
return false; 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) { } else if (!m_is_bridge) {
DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile);
if (ramp_enderdir != m_exitdir) { if (ramp_enderdir != m_exitdir) {
m_err = EC_NO_WAY; m_err = EC_NO_WAY;
return false; return false;

View File

@@ -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 */ /* 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; n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
} }

View File

@@ -646,7 +646,7 @@ bool YapfTrainCheckReverse(const Train *v)
int reverse_penalty = 0; int reverse_penalty = 0;
if (v->track == TRACK_BIT_WORMHOLE) { if (v->track & TRACK_BIT_WORMHOLE) {
/* front in tunnel / on bridge */ /* front in tunnel / on bridge */
DiagDirection dir_into_wormhole = GetTunnelBridgeDirection(tile); DiagDirection dir_into_wormhole = GetTunnelBridgeDirection(tile);
@@ -661,7 +661,7 @@ bool YapfTrainCheckReverse(const Train *v)
reverse_penalty -= DistanceManhattan(cur_tile, tile) * YAPF_TILE_LENGTH; 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 */ /* back in tunnel / on bridge */
DiagDirection dir_into_wormhole = GetTunnelBridgeDirection(tile_rev); DiagDirection dir_into_wormhole = GetTunnelBridgeDirection(tile_rev);

View File

@@ -100,7 +100,8 @@ bool TryReserveRailTrackdir(TileIndex tile, Trackdir td, bool trigger_stations)
*/ */
bool TryReserveRailTrack(TileIndex tile, Track t, 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) { if (_settings_client.gui.show_track_reservation) {
/* show the reserved rail if needed */ /* show the reserved rail if needed */
@@ -153,11 +154,19 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
break; break;
case MP_TUNNELBRIDGE: case MP_TUNNELBRIDGE:
if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && !GetTunnelBridgeReservationTrackBits(tile)) { if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
SetTunnelBridgeReservation(tile, true); if (IsTunnel(tile) && !HasTunnelReservation(tile)) {
SetTunnelReservation(tile, true);
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
return true; return true;
} }
if (IsBridge(tile)) {
if (TryReserveRailBridgeHead(tile, t)) {
MarkBridgeOrTunnelDirtyOnReservationChange(tile, ZOOM_LVL_DRAW_MAP);
return true;
}
}
}
break; break;
default: default:
@@ -187,7 +196,7 @@ void UnreserveRailTrackdir(TileIndex tile, Trackdir td)
*/ */
void UnreserveRailTrack(TileIndex tile, Track t) 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 (_settings_client.gui.show_track_reservation) {
if (IsBridgeTile(tile)) { if (IsBridgeTile(tile)) {
@@ -223,9 +232,13 @@ void UnreserveRailTrack(TileIndex tile, Track t)
case MP_TUNNELBRIDGE: case MP_TUNNELBRIDGE:
if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) { if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
SetTunnelBridgeReservation(tile, false); if (IsTunnel(tile)) {
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED); SetTunnelReservation(tile, false);
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } else {
UnreserveRailBridgeHeadTrack(tile, t);
}
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile) && IsTrackAcrossTunnelBridge(tile, t)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
MarkBridgeOrTunnelDirtyOnReservationChange(tile, ZOOM_LVL_DRAW_MAP);
} }
break; break;
@@ -295,7 +308,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
if (IsRailDepotTile(tile)) break; if (IsRailDepotTile(tile)) break;
/* Non-pbs signal? Reservation can't continue. */ /* 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_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); 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; if (v->type != VEH_TRAIN || (v->vehstatus & VS_CRASHED)) return NULL;
Train *t = Train::From(v); 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. /* Do not find trains inside signalled bridge/tunnels.
* Trains on the ramp/entrance itself are found though. * Trains on the ramp/entrance itself are found though.
*/ */
@@ -328,7 +341,7 @@ static Vehicle *FindTrainOnTrackEnum(Vehicle *v, void *data)
return NULL; 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(); t = t->First();
/* ALWAYS return the lowest ID (anti-desync!) */ /* 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 (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. */ /* The target tile is a bridge/tunnel, also check the other end tile. */
FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum); FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != NULL) *train_on_res = ftoti.best->First(); 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. */ /* 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); FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != NULL) return ftoti.best; 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 (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)) { if (IsTunnelBridgeSignalSimulationEntrance(tile)) {
return true; return true;
} }
@@ -533,6 +546,7 @@ bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bo
return include_line_end; return include_line_end;
} }
if (IsTileType(ft.m_new_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(ft.m_new_tile) == TRANSPORT_RAIL && 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)) { IsTunnelBridgeSignalSimulationExitOnly(ft.m_new_tile) && IsTunnelBridgePBS(ft.m_new_tile)) {
return include_line_end; 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); 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)) { if (IsTunnelBridgeSignalSimulationBidirectional(tile)) {
TileIndex other_end = GetOtherTunnelBridgeEnd(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)); Direction dir = DiagDirToDir(GetTunnelBridgeDirection(other_end));
if (HasVehicleOnPos(other_end, &dir, [](Vehicle *v, void *data) -> Vehicle * { if (HasVehicleOnPos(other_end, &dir, [](Vehicle *v, void *data) -> Vehicle * {
if (v->type != VEH_TRAIN) return nullptr; if (v->type != VEH_TRAIN) return nullptr;
if (v->direction != *((Direction *) data)) return nullptr; DirDiff diff = DirDifference(v->direction, *((Direction *) data));
return v; 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 false;
} }
return true; return true;

View File

@@ -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++) #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 */ #endif /* RAIL_H */

View File

@@ -56,18 +56,6 @@ RailType _sorted_railtypes[RAILTYPE_END];
uint8 _sorted_railtypes_size; 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. 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. * 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); 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 */ /* Validate functions for rail building */
static inline bool ValParamTrackOrientation(Track track) static inline bool ValParamTrackOrientation(Track track)
{ {
@@ -454,6 +461,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
{ {
RailType railtype = Extract<RailType, 0, 5>(p1); RailType railtype = Extract<RailType, 0, 5>(p1);
Track track = Extract<Track, 0, 3>(p2); Track track = Extract<Track, 0, 3>(p2);
bool disable_custom_bridge_heads = HasBit(p2, 4);
CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost cost(EXPENSES_CONSTRUCTION);
_rail_track_endtile = INVALID_TILE; _rail_track_endtile = INVALID_TILE;
@@ -513,6 +521,52 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
break; break;
} }
case MP_TUNNELBRIDGE: {
CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret;
if (disable_custom_bridge_heads || !_settings_game.construction.rail_custom_bridge_heads || !IsFlatRailBridgeHeadTile(tile)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); // just get appropriate error message
if (!IsCompatibleRail(GetRailType(tile), railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) return_cmd_error(STR_ERROR_CAN_T_CONVERT_RAIL);
const DiagDirection entrance_dir = GetTunnelBridgeDirection(tile);
const TrackBits axial_track = DiagDirToDiagTrackBits(entrance_dir);
const TrackBits existing = GetCustomBridgeHeadTrackBits(tile);
const TrackBits future = existing | trackbit;
if (existing == future) return_cmd_error(STR_ERROR_ALREADY_BUILT);
if (flags & DC_NO_RAIL_OVERLAP || IsTunnelBridgeWithSignalSimulation(tile)) {
if (future != TRACK_BIT_HORZ && future != TRACK_BIT_VERT) {
return_cmd_error((flags & DC_NO_RAIL_OVERLAP) ? STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION : STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
}
}
if ((trackbit & ~axial_track) && !_settings_game.construction.build_on_slopes) {
return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
}
/* Steep slopes behave the same as slopes with one corner raised. */
const Slope normalised_tileh = IsSteepSlope(tileh) ? SlopeWithOneCornerRaised(GetHighestSlopeCorner(tileh)) : tileh;
if (!IsValidFlatRailBridgeHeadTrackBits(normalised_tileh, GetTunnelBridgeDirection(tile), future)) {
return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
}
const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
ret = TunnelBridgeIsFree(tile, other_end);
if (ret.Failed()) return ret;
if (flags & DC_EXEC) {
SetCustomBridgeHeadTrackBits(tile, future);
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(future) - GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(existing);
DirtyCompanyInfrastructureWindows(_current_company);
}
break;
}
case MP_ROAD: { case MP_ROAD: {
/* Level crossings may only be built on these slopes */ /* Level crossings may only be built on these slopes */
if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
@@ -731,6 +785,44 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
break; break;
} }
case MP_TUNNELBRIDGE: {
CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret;
if (!IsFlatRailBridgeHeadTile(tile) || GetCustomBridgeHeadTrackBits(tile) == DiagDirToDiagTrackBits(GetTunnelBridgeDirection(tile))) {
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); // just get appropriate error message
}
const TrackBits present = GetCustomBridgeHeadTrackBits(tile);
if ((present & trackbit) == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true;
const TrackBits future = present ^ trackbit;
if ((GetAcrossBridgePossibleTrackBits(tile) & future) == 0) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); // just get appropriate error message
const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
ret = TunnelBridgeIsFree(tile, other_end);
if (ret.Failed()) return ret;
cost.AddCost(RailClearCost(GetRailType(tile)));
if (flags & DC_EXEC) {
owner = GetTileOwner(tile);
if (HasReservedTracks(tile, trackbit)) {
v = GetTrainForReservation(tile, track);
if (v != NULL) FreeTrainTrackReservation(v);
}
SetCustomBridgeHeadTrackBits(tile, future);
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(present) - GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(future);
DirtyCompanyInfrastructureWindows(_current_company);
}
break;
}
default: return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); default: return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
} }
@@ -1084,6 +1176,9 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
/* You can only build signals on plain rail tiles or tunnel/bridges, and the selected track must exist */ /* You can only build signals on plain rail tiles or tunnel/bridges, and the selected track must exist */
if (IsTileType(tile, MP_TUNNELBRIDGE)) { if (IsTileType(tile, MP_TUNNELBRIDGE)) {
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return CMD_ERROR; if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return CMD_ERROR;
if (!ValParamTrackOrientation(track) || !IsTrackAcrossTunnelBridge(tile, track)) {
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track); CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
ret = EnsureNoTrainOnTrack(tile, track); ret = EnsureNoTrainOnTrack(tile, track);
@@ -1100,19 +1195,24 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
CommandCost cost; CommandCost cost;
/* handle signals simulation on tunnel/bridge. */ /* handle signals simulation on tunnel/bridge. */
if (IsTileType(tile, MP_TUNNELBRIDGE)) { if (IsTileType(tile, MP_TUNNELBRIDGE)) {
if (TracksOverlap(GetTunnelBridgeTrackBits(tile))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
bool bidirectional = HasBit(p1, 18) && (sigtype == SIGTYPE_PBS); bool bidirectional = HasBit(p1, 18) && (sigtype == SIGTYPE_PBS);
TileIndex tile_exit = GetOtherTunnelBridgeEnd(tile); TileIndex tile_exit = GetOtherTunnelBridgeEnd(tile);
cost = CommandCost(); cost = CommandCost();
bool flip_variant = false; bool flip_variant = false;
bool is_pbs = (sigtype == SIGTYPE_PBS) || (sigtype == SIGTYPE_PBS_ONEWAY); bool is_pbs = (sigtype == SIGTYPE_PBS) || (sigtype == SIGTYPE_PBS_ONEWAY);
Trackdir entrance_td = TrackExitdirToTrackdir(track, GetTunnelBridgeDirection(tile));
bool p2_signal_in = p2 & SignalAlongTrackdir(entrance_td);
bool p2_signal_out = p2 & SignalAgainstTrackdir(entrance_td);
bool p2_active = p2_signal_in || p2_signal_out;
if (!IsTunnelBridgeWithSignalSimulation(tile)) { // toggle signal zero costs. if (!IsTunnelBridgeWithSignalSimulation(tile)) { // toggle signal zero costs.
if (convert_signal) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); if (convert_signal) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
if (p2 != 12) cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2) * (bidirectional ? 2 : 1)); // minimal 1 if (!(p2_signal_in && p2_signal_out)) cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2) * (bidirectional ? 2 : 1)); // minimal 1
} else { } else {
if (HasBit(p1, 17)) return CommandCost(); if (HasBit(p1, 17)) return CommandCost();
bool is_bidi = IsTunnelBridgeSignalSimulationBidirectional(tile); bool is_bidi = IsTunnelBridgeSignalSimulationBidirectional(tile);
bool will_be_bidi = is_bidi; bool will_be_bidi = is_bidi;
if (p2 == 0) { if (!p2_active) {
if (convert_signal) { if (convert_signal) {
will_be_bidi = bidirectional && !ctrl_pressed; will_be_bidi = bidirectional && !ctrl_pressed;
} else if (ctrl_pressed) { } else if (ctrl_pressed) {
@@ -1121,7 +1221,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
} else if (!is_pbs) { } else if (!is_pbs) {
will_be_bidi = false; will_be_bidi = false;
} }
if ((p2 != 0 && (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)) || if ((!p2_active && (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)) ||
(convert_signal && (ctrl_pressed || (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)))) { (convert_signal && (ctrl_pressed || (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)))) {
flip_variant = true; flip_variant = true;
cost = CommandCost(EXPENSES_CONSTRUCTION, ((_price[PR_BUILD_SIGNALS] * (will_be_bidi ? 2 : 1)) + (_price[PR_CLEAR_SIGNALS] * (is_bidi ? 2 : 1))) * cost = CommandCost(EXPENSES_CONSTRUCTION, ((_price[PR_BUILD_SIGNALS] * (will_be_bidi ? 2 : 1)) + (_price[PR_CLEAR_SIGNALS] * (is_bidi ? 2 : 1))) *
@@ -1144,7 +1244,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
Company * const c = Company::Get(GetTileOwner(tile)); Company * const c = Company::Get(GetTileOwner(tile));
if (IsTunnelBridgeWithSignalSimulation(tile)) c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, tile_exit); if (IsTunnelBridgeWithSignalSimulation(tile)) c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, tile_exit);
if (p2 == 0 && IsTunnelBridgeWithSignalSimulation(tile)) { // Toggle signal if already signals present. if (!p2_active && IsTunnelBridgeWithSignalSimulation(tile)) { // Toggle signal if already signals present.
if (convert_signal) { if (convert_signal) {
if (flip_variant) { if (flip_variant) {
SetTunnelBridgeSemaphore(tile, !IsTunnelBridgeSemaphore(tile)); SetTunnelBridgeSemaphore(tile, !IsTunnelBridgeSemaphore(tile));
@@ -1175,18 +1275,16 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
} }
} else { } else {
/* Create one direction tunnel/bridge if required. */ /* Create one direction tunnel/bridge if required. */
if (p2 == 0) { if (!p2_active) {
if (bidirectional) { if (bidirectional) {
set_bidi(tile); set_bidi(tile);
set_bidi(tile_exit); set_bidi(tile_exit);
} else { } else {
SetupBridgeTunnelSignalSimulation(tile, tile_exit); SetupBridgeTunnelSignalSimulation(tile, tile_exit);
} }
} else if (p2 == 4 || p2 == 8) { } else if (p2_signal_in != p2_signal_out) {
DiagDirection tbdir = GetTunnelBridgeDirection(tile);
/* If signal only on one side build accoringly one-way tunnel/bridge. */ /* If signal only on one side build accoringly one-way tunnel/bridge. */
if ((p2 == 8 && (tbdir == DIAGDIR_NE || tbdir == DIAGDIR_SE)) || if (p2_signal_in) {
(p2 == 4 && (tbdir == DIAGDIR_SW || tbdir == DIAGDIR_NW))) {
ClearBridgeTunnelSignalSimulation(tile_exit, tile); ClearBridgeTunnelSignalSimulation(tile_exit, tile);
SetupBridgeTunnelSignalSimulation(tile, tile_exit); SetupBridgeTunnelSignalSimulation(tile, tile_exit);
} else { } else {
@@ -1194,7 +1292,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
SetupBridgeTunnelSignalSimulation(tile_exit, tile); SetupBridgeTunnelSignalSimulation(tile_exit, tile);
} }
} }
if (p2 == 0 || p2 == 4 || p2 == 8) { if (!(p2_signal_in && p2_signal_out)) {
SetTunnelBridgeSemaphore(tile, sigvar == SIG_SEMAPHORE); SetTunnelBridgeSemaphore(tile, sigvar == SIG_SEMAPHORE);
SetTunnelBridgeSemaphore(tile_exit, sigvar == SIG_SEMAPHORE); SetTunnelBridgeSemaphore(tile_exit, sigvar == SIG_SEMAPHORE);
SetTunnelBridgePBS(tile, is_pbs); SetTunnelBridgePBS(tile, is_pbs);
@@ -1202,8 +1300,8 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
if (!IsTunnelBridgePBS(tile)) remove_pbs_bidi(); if (!IsTunnelBridgePBS(tile)) remove_pbs_bidi();
} }
} }
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile) && !HasTunnelBridgeReservation(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED); if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile) && !HasAcrossTunnelBridgeReservation(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
if (IsTunnelBridgeSignalSimulationExit(tile_exit) && IsTunnelBridgePBS(tile_exit) && !HasTunnelBridgeReservation(tile_exit)) SetTunnelBridgeExitSignalState(tile_exit, SIGNAL_STATE_RED); if (IsTunnelBridgeSignalSimulationExit(tile_exit) && IsTunnelBridgePBS(tile_exit) && !HasAcrossTunnelBridgeReservation(tile_exit)) SetTunnelBridgeExitSignalState(tile_exit, SIGNAL_STATE_RED);
MarkBridgeOrTunnelDirty(tile); MarkBridgeOrTunnelDirty(tile);
AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile)); AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile));
AddSideToSignalBuffer(tile_exit, INVALID_DIAGDIR, GetTileOwner(tile)); AddSideToSignalBuffer(tile_exit, INVALID_DIAGDIR, GetTileOwner(tile));
@@ -1393,13 +1491,27 @@ static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal
TileIndex orig_tile = tile; // backup old value TileIndex orig_tile = tile; // backup old value
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false; if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false; signal_ctr += IsDiagonalTrackdir(trackdir) ? 2 : 1;
if (GetTunnelBridgeDirection(tile) == TrackdirToExitdir(trackdir)) {
/* Skip to end of tunnel or bridge /* Skip to end of tunnel or bridge
* note that tile is a parameter by reference, so it must be updated */ * note that tile is a parameter by reference, so it must be updated */
tile = GetOtherTunnelBridgeEnd(tile); tile = GetOtherTunnelBridgeEnd(tile);
signal_ctr += GetTunnelBridgeLength(orig_tile, tile) * 2;
signal_ctr += (GetTunnelBridgeLength(orig_tile, tile) + 2) * 2; /* Check for track bits on the new tile */
trackdirbits = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0));
if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
trackdirbits &= TrackdirReachesTrackdirs(trackdir);
/* Get the first track dir */
trackdir = RemoveFirstTrackdir(&trackdirbits);
/* Any left? It's a junction so we stop */
if (trackdirbits != TRACKDIR_BIT_NONE) return false;
signal_ctr += IsDiagonalTrackdir(trackdir) ? 2 : 1;
}
return true; return true;
} }
@@ -1823,6 +1935,24 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
SmallVector<Train *, 2> vehicles_affected; SmallVector<Train *, 2> vehicles_affected;
auto find_train_reservations = [&vehicles_affected, &totype](TileIndex tile, TrackBits reserved) {
Track track;
while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
Train *v = GetTrainForReservation(tile, track);
if (v != NULL && !HasPowerOnRail(v->railtype, totype)) {
/* No power on new rail type, reroute. */
FreeTrainTrackReservation(v);
*vehicles_affected.Append() = v;
}
}
};
auto yapf_notify_track_change = [](TileIndex tile, TrackBits tracks) {
while (tracks != TRACK_BIT_NONE) {
YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
}
};
/* Vehicle on the tile when not converting Rail <-> ElRail /* Vehicle on the tile when not converting Rail <-> ElRail
* Tunnels and bridges have special check later */ * Tunnels and bridges have special check later */
if (tt != MP_TUNNELBRIDGE) { if (tt != MP_TUNNELBRIDGE) {
@@ -1834,16 +1964,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
} }
} }
if (flags & DC_EXEC) { // we can safely convert, too if (flags & DC_EXEC) { // we can safely convert, too
TrackBits reserved = GetReservedTrackbits(tile); find_train_reservations(tile, GetReservedTrackbits(tile));
Track track;
while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
Train *v = GetTrainForReservation(tile, track);
if (v != NULL && !HasPowerOnRail(v->railtype, totype)) {
/* No power on new rail type, reroute. */
FreeTrainTrackReservation(v);
*vehicles_affected.Append() = v;
}
}
/* Update the company infrastructure counters. */ /* Update the company infrastructure counters. */
if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) { if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
@@ -1884,10 +2005,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
/* notify YAPF about the track layout change */ /* notify YAPF about the track layout change */
TrackBits tracks = GetTrackBits(tile); yapf_notify_track_change(tile, GetTrackBits(tile));
while (tracks != TRACK_BIT_NONE) {
YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
}
} }
cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile))); cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
break; break;
@@ -1916,22 +2034,18 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
} }
} }
uint middle_len = GetTunnelBridgeLength(tile, endtile);
uint num_raw_pieces = middle_len + CountBits(GetTunnelBridgeTrackBits(tile)) + CountBits(GetTunnelBridgeTrackBits(endtile));
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile)); find_train_reservations(tile, GetTunnelBridgeReservationTrackBits(tile));
if (HasTunnelBridgeReservation(tile)) { find_train_reservations(endtile, GetTunnelBridgeReservationTrackBits(endtile));
Train *v = GetTrainForReservation(tile, track);
if (v != NULL && !HasPowerOnRail(v->railtype, totype)) {
/* No power on new rail type, reroute. */
FreeTrainTrackReservation(v);
*vehicles_affected.Append() = v;
}
}
/* Update the company infrastructure counters. */ /* Update the company infrastructure counters. */
uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR; uint num_infra_pieces = (middle_len* TUNNELBRIDGE_TRACKBIT_FACTOR) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(endtile);
Company *c = Company::Get(GetTileOwner(tile)); Company *c = Company::Get(GetTileOwner(tile));
c->infrastructure.rail[GetRailType(tile)] -= num_pieces; c->infrastructure.rail[GetRailType(tile)] -= num_infra_pieces;
c->infrastructure.rail[totype] += num_pieces; c->infrastructure.rail[totype] += num_infra_pieces;
DirtyCompanyInfrastructureWindows(c->index); DirtyCompanyInfrastructureWindows(c->index);
SetRailType(tile, totype); SetRailType(tile, totype);
@@ -1940,8 +2054,9 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc); FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc);
FindVehicleOnPos(endtile, &affected_trains, &UpdateTrainPowerProc); FindVehicleOnPos(endtile, &affected_trains, &UpdateTrainPowerProc);
YapfNotifyTrackLayoutChange(tile, track); /* notify YAPF about the track layout change */
YapfNotifyTrackLayoutChange(endtile, track); yapf_notify_track_change(tile, GetTunnelBridgeTrackBits(tile));
yapf_notify_track_change(endtile, GetTunnelBridgeTrackBits(endtile));
if (IsBridge(tile)) { if (IsBridge(tile)) {
MarkBridgeDirty(tile, ZOOM_LVL_DRAW_MAP); MarkBridgeDirty(tile, ZOOM_LVL_DRAW_MAP);
@@ -1951,7 +2066,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
} }
} }
cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype)); cost.AddCost(num_raw_pieces * RailConvertCost(type, totype));
break; break;
} }
@@ -2089,7 +2204,7 @@ static uint GetSaveSlopeZ(uint x, uint y, Track track)
return GetSlopePixelZ(x, y); return GetSlopePixelZ(x, y);
} }
static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos) void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos, SignalType type, SignalVariant variant, bool show_restricted)
{ {
bool side; bool side;
switch (_settings_game.construction.train_signal_side) { switch (_settings_game.construction.train_signal_side) {
@@ -2114,11 +2229,6 @@ static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track trac
uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x; uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x;
uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y; uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y;
SignalType type = GetSignalType(tile, track);
SignalVariant variant = GetSignalVariant(tile, track);
bool show_restricted = (variant == SIG_ELECTRIC) && IsRestrictedSignal(tile) && (GetExistingTraceRestrictProgram(tile, track) != NULL);
SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition); SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
bool is_custom_sprite = (sprite != 0); bool is_custom_sprite = (sprite != 0);
if (sprite != 0) { if (sprite != 0) {
@@ -2164,6 +2274,15 @@ static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track trac
} }
} }
static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
{
SignalType type = GetSignalType(tile, track);
SignalVariant variant = GetSignalVariant(tile, track);
bool show_restricted = (variant == SIG_ELECTRIC) && IsRestrictedSignal(tile) && (GetExistingTraceRestrictProgram(tile, track) != NULL);
DrawSingleSignal(tile, rti, track, condition, image, pos, type, variant, show_restricted);
}
static uint32 _drawtile_track_palette; static uint32 _drawtile_track_palette;
@@ -2325,10 +2444,19 @@ static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInf
DrawGroundSprite(sprite, pal, NULL, 0, (ti->tileh & s) ? -8 : 0); DrawGroundSprite(sprite, pal, NULL, 0, (ti->tileh & s) ? -8 : 0);
} }
static RailGroundType GetRailOrBridgeGroundType(TileInfo *ti) {
if (IsTileType(ti->tile, MP_TUNNELBRIDGE)) {
return HasTunnelBridgeSnowOrDesert(ti->tile) ? RAIL_GROUND_ICE_DESERT : RAIL_GROUND_GRASS;
} else {
return GetRailGroundType(ti->tile);
}
}
static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeInfo *rti) static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeInfo *rti)
{ {
RailGroundType rgt = GetRailGroundType(ti->tile); const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE);
Foundation f = GetRailFoundation(ti->tileh, track); RailGroundType rgt = GetRailOrBridgeGroundType(ti);
Foundation f = is_bridge ? FOUNDATION_LEVELED : GetRailFoundation(ti->tileh, track);
Corner halftile_corner = CORNER_INVALID; Corner halftile_corner = CORNER_INVALID;
if (IsNonContinuousFoundation(f)) { if (IsNonContinuousFoundation(f)) {
@@ -2367,7 +2495,10 @@ static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeIn
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY); SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND); SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE; TrackBits pbs = TRACK_BIT_NONE;
if (_settings_client.gui.show_track_reservation) {
pbs = is_bridge ? GetTunnelBridgeReservationTrackBits(ti->tile) : GetRailReservationTrackBits(ti->tile);
}
if (track == TRACK_BIT_NONE) { if (track == TRACK_BIT_NONE) {
/* Half-tile foundation, no track here? */ /* Half-tile foundation, no track here? */
@@ -2477,7 +2608,7 @@ static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeIn
* @param ti TileInfo * @param ti TileInfo
* @param track TrackBits to draw * @param track TrackBits to draw
*/ */
static void DrawTrackBits(TileInfo *ti, TrackBits track) void DrawTrackBits(TileInfo *ti, TrackBits track)
{ {
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
@@ -2486,8 +2617,9 @@ static void DrawTrackBits(TileInfo *ti, TrackBits track)
return; return;
} }
RailGroundType rgt = GetRailGroundType(ti->tile); const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE);
Foundation f = GetRailFoundation(ti->tileh, track); RailGroundType rgt = GetRailOrBridgeGroundType(ti);
Foundation f = is_bridge ? FOUNDATION_LEVELED : GetRailFoundation(ti->tileh, track);
Corner halftile_corner = CORNER_INVALID; Corner halftile_corner = CORNER_INVALID;
if (IsNonContinuousFoundation(f)) { if (IsNonContinuousFoundation(f)) {
@@ -2578,7 +2710,7 @@ static void DrawTrackBits(TileInfo *ti, TrackBits track)
/* PBS debugging, draw reserved tracks darker */ /* PBS debugging, draw reserved tracks darker */
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) { if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) {
/* Get reservation, but mask track on halftile slope */ /* Get reservation, but mask track on halftile slope */
TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track; TrackBits pbs = (is_bridge ? GetTunnelBridgeReservationTrackBits(ti->tile) : GetRailReservationTrackBits(ti->tile)) & track;
if (pbs & TRACK_BIT_X) { if (pbs & TRACK_BIT_X) {
if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) { if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH);

View File

@@ -486,7 +486,7 @@ static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td)
return true; return true;
} }
if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExitOnly(tile) && if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExitOnly(tile) &&
DiagDirToDiagTrackdir(GetTunnelBridgeDirection(tile)) == td) { TrackdirEntersTunnelBridge(tile, td)) {
return true; return true;
} }
return false; return false;

View File

@@ -1372,6 +1372,29 @@ bool AfterLoadGame()
} }
} }
if (SlXvIsFeaturePresent(XSLFI_SIG_TUNNEL_BRIDGE, 1, 6)) {
/* m2 signal state bit allocation has shrunk */
for (TileIndex t = 0; t < map_size; t++) {
if (IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL && IsBridge(t) && IsTunnelBridgeSignalSimulationEntrance(t)) {
extern void ShiftBridgeEntranceSimulatedSignalsExtended(TileIndex t, int shift, uint64 in);
const uint shift = 15 - BRIDGE_M2_SIGNAL_STATE_COUNT;
ShiftBridgeEntranceSimulatedSignalsExtended(t, shift, GB(_m[t].m2, BRIDGE_M2_SIGNAL_STATE_COUNT, shift));
SB(_m[t].m2, 0, 15, GB(_m[t].m2, 0, 15) << shift);
}
}
}
if (!SlXvIsFeaturePresent(XSLFI_CUSTOM_BRIDGE_HEADS, 2)) {
/* change map bits for rail bridge heads */
for (TileIndex t = 0; t < map_size; t++) {
if (IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) {
SetCustomBridgeHeadTrackBits(t, DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)));
SetBridgeReservationTrackBits(t, HasBit(_m[t].m5, 4) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE);
ClrBit(_m[t].m5, 4);
}
}
}
/* Elrails got added in rev 24 */ /* Elrails got added in rev 24 */
if (IsSavegameVersionBefore(24)) { if (IsSavegameVersionBefore(24)) {
RailType min_rail = RAILTYPE_ELECTRIC; RailType min_rail = RAILTYPE_ELECTRIC;
@@ -2109,7 +2132,7 @@ bool AfterLoadGame()
break; break;
case MP_TUNNELBRIDGE: // Clear PBS reservation on tunnels/bridges case MP_TUNNELBRIDGE: // Clear PBS reservation on tunnels/bridges
if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) SetTunnelBridgeReservation(t, false); if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) UnreserveAcrossRailTunnelBridge(t);
break; break;
default: break; default: break;
@@ -3376,17 +3399,6 @@ bool AfterLoadGame()
} }
} }
} }
if (SlXvIsFeaturePresent(XSLFI_SIG_TUNNEL_BRIDGE, 1, 6)) {
/* m2 signal state bit allocation has shrunk */
for (TileIndex t = 0; t < map_size; t++) {
if (IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL && IsBridge(t) && IsTunnelBridgeSignalSimulationEntrance(t)) {
extern void ShiftBridgeEntranceSimulatedSignalsExtended(TileIndex t, int shift, uint64 in);
const uint shift = 15 - BRIDGE_M2_SIGNAL_STATE_COUNT;
ShiftBridgeEntranceSimulatedSignalsExtended(t, shift, GB(_m[t].m2, BRIDGE_M2_SIGNAL_STATE_COUNT, shift));
SB(_m[t].m2, 0, 15, GB(_m[t].m2, 0, 15) << shift);
}
}
}
if (SlXvIsFeatureMissing(XSLFI_CUSTOM_BRIDGE_HEADS)) { if (SlXvIsFeatureMissing(XSLFI_CUSTOM_BRIDGE_HEADS)) {
/* ensure that previously unused custom bridge-head bits are cleared */ /* ensure that previously unused custom bridge-head bits are cleared */

View File

@@ -202,13 +202,12 @@ void AfterLoadCompanyStats()
/* Count each tunnel/bridge TUNNELBRIDGE_TRACKBIT_FACTOR times to simulate /* Count each tunnel/bridge TUNNELBRIDGE_TRACKBIT_FACTOR times to simulate
* the higher structural maintenance needs, and don't forget the end tiles. */ * the higher structural maintenance needs, and don't forget the end tiles. */
const uint middle_len = GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR; const uint middle_len = GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR;
const uint len = middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR);
switch (GetTunnelBridgeTransportType(tile)) { switch (GetTunnelBridgeTransportType(tile)) {
case TRANSPORT_RAIL: case TRANSPORT_RAIL:
c = Company::GetIfValid(GetTileOwner(tile)); c = Company::GetIfValid(GetTileOwner(tile));
if (c != NULL) { if (c != NULL) {
c->infrastructure.rail[GetRailType(tile)] += len; c->infrastructure.rail[GetRailType(tile)] += middle_len + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(other_end);
if (IsTunnelBridgeWithSignalSimulation(tile)) { if (IsTunnelBridgeWithSignalSimulation(tile)) {
c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(tile, other_end); c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(tile, other_end);
} }
@@ -222,7 +221,7 @@ void AfterLoadCompanyStats()
case TRANSPORT_WATER: case TRANSPORT_WATER:
c = Company::GetIfValid(GetTileOwner(tile)); c = Company::GetIfValid(GetTileOwner(tile));
if (c != NULL) c->infrastructure.water += len; if (c != NULL) c->infrastructure.water += middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR);
break; break;
default: default:

View File

@@ -75,7 +75,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 3, 3, "cargo_type_orders", NULL, NULL, "ORDX,VEOX" }, { XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 3, 3, "cargo_type_orders", NULL, NULL, "ORDX,VEOX" },
{ XSLFI_EXTENDED_GAMELOG, XSCF_NULL, 1, 1, "extended_gamelog", NULL, NULL, NULL }, { XSLFI_EXTENDED_GAMELOG, XSCF_NULL, 1, 1, "extended_gamelog", NULL, NULL, NULL },
{ XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", NULL, NULL, NULL }, { XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", NULL, NULL, NULL },
{ XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 1, 1, "custom_bridge_heads", NULL, NULL, NULL }, { XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 2, 2, "custom_bridge_heads", NULL, NULL, NULL },
{ XSLFI_CHUNNEL, XSCF_NULL, 1, 1, "chunnel", NULL, NULL, "TUNN" }, { XSLFI_CHUNNEL, XSCF_NULL, 1, 1, "chunnel", NULL, NULL, "TUNN" },
{ XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 1, 1, "scheduled_dispatch", NULL, NULL, NULL }, { XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 1, 1, "scheduled_dispatch", NULL, NULL, NULL },
{ XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL, 1, 1, "more_town_growth_rates", NULL, NULL, NULL }, { XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL, 1, 1, "more_town_growth_rates", NULL, NULL, NULL },

View File

@@ -375,6 +375,7 @@ struct ConstructionSettings {
bool enable_remove_water; ///< enable removing sea and rivers in-game bool enable_remove_water; ///< enable removing sea and rivers in-game
uint8 road_custom_bridge_heads; ///< allow construction of road custom bridge heads uint8 road_custom_bridge_heads; ///< allow construction of road custom bridge heads
bool chunnel; ///< allow construction of tunnels under water bool chunnel; ///< allow construction of tunnels under water
uint8 rail_custom_bridge_heads; ///< allow construction of rail custom bridge heads
uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames? uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames?
uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed? uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed?

View File

@@ -217,7 +217,9 @@ static Vehicle *TrainInWormholeTileEnum(Vehicle *v, void *data)
{ {
/* Only look for front engine or last wagon. */ /* Only look for front engine or last wagon. */
if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL; if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL;
if (*(TileIndex *)data != TileVirtXY(v->x_pos, v->y_pos)) return NULL; TileIndex tile = *(TileIndex *)data;
if (tile != TileVirtXY(v->x_pos, v->y_pos)) return NULL;
if (!(Train::From(v)->track & TRACK_BIT_WORMHOLE) && !(Train::From(v)->track & GetAcrossTunnelBridgeTrackBits(tile))) return NULL;
return v; return v;
} }
@@ -407,11 +409,29 @@ static SigInfo ExploreSegment(Owner owner)
case MP_TUNNELBRIDGE: { case MP_TUNNELBRIDGE: {
if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue; if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
DiagDirection dir = GetTunnelBridgeDirection(tile); DiagDirection tunnel_bridge_dir = GetTunnelBridgeDirection(tile);
TrackBits tracks = GetTunnelBridgeTrackBits(tile);
TrackBits across_tracks = GetAcrossTunnelBridgeTrackBits(tile);
auto check_train_present = [tile, tracks, across_tracks](DiagDirection enterdir) -> bool {
if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) {
if (_enterdir_to_trackbits[enterdir] & across_tracks) {
return EnsureNoTrainOnTrackBits(tile, TRACK_BIT_WORMHOLE | across_tracks).Failed();
} else {
return EnsureNoTrainOnTrackBits(tile, tracks & (~across_tracks)).Failed();
}
} else {
return HasVehicleOnPos(tile, NULL, &TrainOnTileEnum);
}
};
TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir == INVALID_DIAGDIR ? tunnel_bridge_dir : enterdir]); // only incidating trackbits
if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) tracks = tracks_masked;
if (IsTunnelBridgeWithSignalSimulation(tile)) { if (IsTunnelBridgeWithSignalSimulation(tile)) {
if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole if (enterdir == INVALID_DIAGDIR) {
if (!(info.flags & SF_TRAIN) && IsTunnelBridgeSignalSimulationExit(tile)) { // tunnel entrence is ignored // incoming from the wormhole, onto signal
if (!(info.flags & SF_TRAIN) && IsTunnelBridgeSignalSimulationExit(tile)) { // tunnel entrance is ignored
if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN; if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN;
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN; if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN;
} }
@@ -419,11 +439,13 @@ static SigInfo ExploreSegment(Owner owner)
info.flags |= SF_FULL; info.flags |= SF_FULL;
return info; return info;
} }
enterdir = dir; Trackdir exit_track = TrackEnterdirToTrackdir(FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)), ReverseDiagDir(tunnel_bridge_dir));
exitdir = ReverseDiagDir(dir); exitdir = TrackdirToExitdir(exit_track);
enterdir = ReverseDiagDir(exitdir);
tile += TileOffsByDiagDir(exitdir); // just skip to next tile tile += TileOffsByDiagDir(exitdir); // just skip to next tile
} else { // NOT incoming from the wormhole! break;
if (ReverseDiagDir(enterdir) != dir) continue; } else if (_enterdir_to_trackbits[enterdir] & GetAcrossTunnelBridgeTrackBits(tile)) {
// NOT incoming from the wormhole!
if (IsTunnelBridgeSignalSimulationExit(tile)) { if (IsTunnelBridgeSignalSimulationExit(tile)) {
if (IsTunnelBridgePBS(tile)) { if (IsTunnelBridgePBS(tile)) {
info.flags |= SF_PBS; info.flags |= SF_PBS;
@@ -440,21 +462,32 @@ static SigInfo ExploreSegment(Owner owner)
} }
continue; continue;
} }
} else { }
if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN; if (!(info.flags & SF_TRAIN) && check_train_present(tunnel_bridge_dir)) info.flags |= SF_TRAIN;
enterdir = dir; enterdir = tunnel_bridge_dir;
exitdir = ReverseDiagDir(dir); } else if (enterdir != tunnel_bridge_dir) { // NOT incoming from the wormhole!
tile += TileOffsByDiagDir(exitdir); // just skip to next tile if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track
} else { // NOT incoming from the wormhole! if (!(info.flags & SF_TRAIN) && check_train_present(enterdir)) info.flags |= SF_TRAIN;
if (ReverseDiagDir(enterdir) != dir) continue; }
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN; for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { // test all possible exit directions
tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile if (dir != enterdir && (tracks & _enterdir_to_trackbits[dir])) { // any track incidating?
enterdir = INVALID_DIAGDIR; if (dir == tunnel_bridge_dir) {
exitdir = INVALID_DIAGDIR; if (!MaybeAddToTodoSet(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR, tile, INVALID_DIAGDIR)) {
info.flags |= SF_FULL;
return info;
}
} else {
TileIndex newtile = tile + TileOffsByDiagDir(dir); // new tile to check
DiagDirection newdir = ReverseDiagDir(dir); // direction we are entering from
if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) {
info.flags |= SF_FULL;
return info;
} }
} }
break; }
}
continue; // continue the while() loop
} }
default: default:
@@ -593,18 +626,23 @@ static SigSegState UpdateSignalsInBuffer(Owner owner)
* train entering/leaving block, train leaving depot... * train entering/leaving block, train leaving depot...
*/ */
switch (GetTileType(tile)) { switch (GetTileType(tile)) {
case MP_TUNNELBRIDGE: case MP_TUNNELBRIDGE: {
/* 'optimization assert' - do not try to update signals when it is not needed */ /* 'optimization assert' - do not try to update signals when it is not needed */
assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL); assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile))); if (IsTunnel(tile)) assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile)));
TrackBits across = GetAcrossTunnelBridgeTrackBits(tile);
if (dir == INVALID_DIAGDIR || _enterdir_to_trackbits[dir] & across) {
_tbdset.Add(tile, INVALID_DIAGDIR); // we can safely start from wormhole centre _tbdset.Add(tile, INVALID_DIAGDIR); // we can safely start from wormhole centre
if (!IsTunnelBridgeWithSignalSimulation(tile)) { // Don't worry with other side of tunnel. if (!IsTunnelBridgeWithSignalSimulation(tile)) { // Don't worry with other side of tunnel.
_tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR); _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
} }
break; break;
}
}
FALLTHROUGH;
case MP_RAILWAY: case MP_RAILWAY:
if (IsRailDepot(tile)) { if (IsRailDepotTile(tile)) {
/* 'optimization assert' do not try to update signals in other cases */ /* 'optimization assert' do not try to update signals in other cases */
assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile)); assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile));
_tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside _tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside
@@ -704,8 +742,13 @@ void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
_last_owner = owner; _last_owner = owner;
_globset.Add(tile, _search_dir_1[track]); DiagDirection wormhole_dir = IsTileType(tile, MP_TUNNELBRIDGE) ? GetTunnelBridgeDirection(tile) : INVALID_DIAGDIR;
_globset.Add(tile, _search_dir_2[track]);
auto add_dir = [&](DiagDirection dir) {
_globset.Add(tile, dir == wormhole_dir ? INVALID_DIAGDIR : dir);
};
add_dir(_search_dir_1[track]);
add_dir(_search_dir_2[track]);
if (_globset.Items() >= SIG_GLOB_UPDATE) { if (_globset.Items() >= SIG_GLOB_UPDATE) {
/* too many items, force update */ /* too many items, force update */

View File

@@ -1464,6 +1464,15 @@ str = STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS
strhelp = STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS_HELPTEXT strhelp = STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS_HELPTEXT
patxname = ""custom_bridge_heads.construction.road_custom_bridge_heads"" patxname = ""custom_bridge_heads.construction.road_custom_bridge_heads""
[SDT_BOOL]
base = GameSettings
var = construction.rail_custom_bridge_heads
def = true
cat = SC_BASIC
str = STR_CONFIG_SETTING_ENABLE_RAIL_CUSTOM_BRIDGE_HEADS
strhelp = STR_CONFIG_SETTING_ENABLE_RAIL_CUSTOM_BRIDGE_HEADS_HELPTEXT
patxname = ""custom_bridge_heads.construction.rail_custom_bridge_heads""
[SDT_BOOL] [SDT_BOOL]
base = GameSettings base = GameSettings
var = station.adjacent_stations var = station.adjacent_stations

View File

@@ -308,7 +308,7 @@ protected: // These functions should not be called outside acceleration code.
inline byte GetAirDragArea() const inline byte GetAirDragArea() const
{ {
/* Air drag is higher in tunnels due to the limited cross-section. */ /* Air drag is higher in tunnels due to the limited cross-section. */
return (this->track == TRACK_BIT_WORMHOLE && this->vehstatus & VS_HIDDEN) ? 28 : 14; return (this->track & TRACK_BIT_WORMHOLE && this->vehstatus & VS_HIDDEN) ? 28 : 14;
} }
/** /**

View File

@@ -591,7 +591,7 @@ int Train::GetCurrentMaxSpeed() const
} }
/* Vehicle is on the middle part of a bridge. */ /* Vehicle is on the middle part of a bridge. */
if (u->track == TRACK_BIT_WORMHOLE && !(u->vehstatus & VS_HIDDEN)) { if (u->track & TRACK_BIT_WORMHOLE && !(u->vehstatus & VS_HIDDEN)) {
max_speed = min(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed); max_speed = min(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
} }
} }
@@ -1792,17 +1792,17 @@ static void UpdateStatusAfterSwap(Train *v)
if (v->track != TRACK_BIT_DEPOT) v->direction = ReverseDir(v->direction); if (v->track != TRACK_BIT_DEPOT) v->direction = ReverseDir(v->direction);
/* Call the proper EnterTile function unless we are in a wormhole. */ /* Call the proper EnterTile function unless we are in a wormhole. */
if (v->track != TRACK_BIT_WORMHOLE) { if (!(v->track & TRACK_BIT_WORMHOLE)) {
VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos); VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
} else { } else {
/* VehicleEnter_TunnelBridge() sets TRACK_BIT_WORMHOLE when the vehicle /* VehicleEnter_TunnelBridge() may set TRACK_BIT_WORMHOLE when the vehicle
* is on the last bit of the bridge head (frame == TILE_SIZE - 1). * is on the last bit of the bridge head (frame == TILE_SIZE - 1).
* If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE, * If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE,
* when we shouldn't have. Check if this is the case. */ * when we shouldn't have. Check if this is the case. */
TileIndex vt = TileVirtXY(v->x_pos, v->y_pos); TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
if (IsTileType(vt, MP_TUNNELBRIDGE)) { if (IsTileType(vt, MP_TUNNELBRIDGE)) {
VehicleEnterTile(v, vt, v->x_pos, v->y_pos); VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
if (v->track != TRACK_BIT_WORMHOLE && IsBridgeTile(v->tile)) { if (!(v->track & TRACK_BIT_WORMHOLE) && IsBridgeTile(v->tile)) {
/* We have just left the wormhole, possibly set the /* We have just left the wormhole, possibly set the
* "goingdown" bit. UpdateInclination() can be used * "goingdown" bit. UpdateInclination() can be used
* because we are at the border of the tile. */ * because we are at the border of the tile. */
@@ -1814,7 +1814,7 @@ static void UpdateStatusAfterSwap(Train *v)
} }
v->UpdatePosition(); v->UpdatePosition();
if (v->track == TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true); if (v->track & TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true);
v->UpdateViewport(true, true); v->UpdateViewport(true, true);
} }
@@ -2182,7 +2182,7 @@ void ReverseTrainDirection(Train *v)
} }
/* We are inside tunnel/bridge with signals, reversing will close the entrance. */ /* We are inside tunnel/bridge with signals, reversing will close the entrance. */
if (IsTunnelBridgeWithSignalSimulation(v->tile)) { if (IsTunnelBridgeWithSignalSimulation(v->tile) && IsTunnelBridgeSignalSimulationEntrance(v->tile)) {
/* Flip signal on tunnel entrance tile red. */ /* Flip signal on tunnel entrance tile red. */
SetTunnelBridgeEntranceSignalState(v->tile, SIGNAL_STATE_RED); SetTunnelBridgeEntranceSignalState(v->tile, SIGNAL_STATE_RED);
MarkTileDirtyByTile(v->tile); MarkTileDirtyByTile(v->tile);
@@ -2195,7 +2195,7 @@ void ReverseTrainDirection(Train *v)
/* VehicleExitDir does not always produce the desired dir for depots and /* VehicleExitDir does not always produce the desired dir for depots and
* tunnels/bridges that is needed for UpdateSignalsOnSegment. */ * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
DiagDirection dir = VehicleExitDir(v->direction, v->track); DiagDirection dir = VehicleExitDir(v->direction, v->track);
if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR; if (IsRailDepotTile(v->tile) || (IsTileType(v->tile, MP_TUNNELBRIDGE) && (v->track & TRACK_BIT_WORMHOLE || dir == GetTunnelBridgeDirection(v->tile)))) dir = INVALID_DIAGDIR;
if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) { if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
/* If we are currently on a tile with conventional signals, we can't treat the /* If we are currently on a tile with conventional signals, we can't treat the
@@ -2597,7 +2597,7 @@ static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDir
static void UnreserveBridgeTunnelTile(TileIndex tile) static void UnreserveBridgeTunnelTile(TileIndex tile)
{ {
SetTunnelBridgeReservation(tile, false); UnreserveAcrossRailTunnelBridge(tile);
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED); if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
} }
@@ -2610,27 +2610,36 @@ static void UnreserveBridgeTunnelTile(TileIndex tile)
*/ */
static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir, bool tunbridge_clear_unsignaled_other_end = false) static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir, bool tunbridge_clear_unsignaled_other_end = false)
{ {
DiagDirection dir = TrackdirToExitdir(track_dir);
if (IsTileType(tile, MP_TUNNELBRIDGE)) { if (IsTileType(tile, MP_TUNNELBRIDGE)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile); if (IsTrackAcrossTunnelBridge(tile, TrackdirToTrack(track_dir))) {
UnreserveBridgeTunnelTile(tile); UnreserveBridgeTunnelTile(tile);
if (IsTunnelBridgeWithSignalSimulation(tile)) { if (IsTunnelBridgeWithSignalSimulation(tile)) {
/* Are we just leaving a tunnel/bridge? */ /* Are we just leaving a tunnel/bridge? */
if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) { if (TrackdirExitsTunnelBridge(tile, track_dir)) {
bool free = TunnelBridgeIsFree(tile, end, v).Succeeded(); TileIndex end = GetOtherTunnelBridgeEnd(tile);
HandleLastTunnelBridgeSignals(tile, end, dir, free); bool free = TunnelBridgeIsFree(tile, end, v, true).Succeeded();
HandleLastTunnelBridgeSignals(tile, end, ReverseDiagDir(GetTunnelBridgeDirection(tile)), free);
} }
} else if (tunbridge_clear_unsignaled_other_end) { } else if (tunbridge_clear_unsignaled_other_end) {
UnreserveBridgeTunnelTile(end); TileIndex end = GetOtherTunnelBridgeEnd(tile);
UnreserveAcrossRailTunnelBridge(end);
if (_settings_client.gui.show_track_reservation) {
MarkTileDirtyByTile(end, ZOOM_LVL_DRAW_MAP);
}
} }
if (_settings_client.gui.show_track_reservation) {
MarkBridgeOrTunnelDirtyOnReservationChange(tile, ZOOM_LVL_DRAW_MAP);
}
} else {
UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
if (_settings_client.gui.show_track_reservation) { if (_settings_client.gui.show_track_reservation) {
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
MarkTileDirtyByTile(end, ZOOM_LVL_DRAW_MAP); }
} }
} else if (IsRailStationTile(tile)) { } else if (IsRailStationTile(tile)) {
DiagDirection dir = TrackdirToExitdir(track_dir);
TileIndex new_tile = TileAddByDiagDir(tile, dir); TileIndex new_tile = TileAddByDiagDir(tile, dir);
/* If the new tile is not a further tile of the same station, we /* If the new tile is not a further tile of the same station, we
* clear the reservation for the whole platform. */ * clear the reservation for the whole platform. */
@@ -2696,11 +2705,10 @@ void FreeTrainTrackReservation(const Train *v, TileIndex origin, Trackdir orig_t
} else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) { } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
break; break;
} }
} else if (IsTunnelBridgeWithSignalSimulation(tile)) { } else if (IsTunnelBridgeWithSignalSimulation(tile) && TrackdirExitsTunnelBridge(tile, td)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile); TileIndex end = GetOtherTunnelBridgeEnd(tile);
bool free = TunnelBridgeIsFree(tile, end, v).Succeeded(); bool free = TunnelBridgeIsFree(tile, end, v, true).Succeeded();
if (!free) break;
if (!free && GetTunnelBridgeDirection(tile) == ReverseDiagDir(TrackdirToExitdir(td))) break;
} }
/* Don't free first station/bridge/tunnel if we are on it. */ /* Don't free first station/bridge/tunnel if we are on it. */
@@ -3189,7 +3197,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
} }
if (IsTileType(v->tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExitOnly(v->tile) && if (IsTileType(v->tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExitOnly(v->tile) &&
DiagDirToDiagTrackBits(GetTunnelBridgeDirection(v->tile)) == v->track) { (GetAcrossTunnelBridgeTrackBits(v->tile) & v->track)) {
// prevent any attempt to reserve the wrong way onto a tunnel/bridge exit // prevent any attempt to reserve the wrong way onto a tunnel/bridge exit
return false; return false;
} }
@@ -3246,7 +3254,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
static bool CheckReverseTrain(const Train *v) static bool CheckReverseTrain(const Train *v)
{ {
if (_settings_game.difficulty.line_reverse_mode != 0 || if (_settings_game.difficulty.line_reverse_mode != 0 ||
v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT || v->track & TRACK_BIT_WORMHOLE ||
!(v->direction & 1)) { !(v->direction & 1)) {
return false; return false;
} }
@@ -3441,15 +3449,18 @@ static TrainMovedChangeSignalEnum TrainMovedChangeSignal(Train* v, TileIndex til
void Train::ReserveTrackUnderConsist() const void Train::ReserveTrackUnderConsist() const
{ {
for (const Train *u = this; u != NULL; u = u->Next()) { for (const Train *u = this; u != NULL; u = u->Next()) {
switch (u->track) { if (u->track & TRACK_BIT_WORMHOLE) {
case TRACK_BIT_WORMHOLE: if (IsRailCustomBridgeHeadTile(u->tile)) {
/* reserve the first available track */
TrackBits bits = GetAcrossTunnelBridgeTrackBits(u->tile);
Track first_track = RemoveFirstTrack(&bits);
assert(IsValidTrack(first_track));
TryReserveRailTrack(u->tile, first_track);
} else {
TryReserveRailTrack(u->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(u->tile))); TryReserveRailTrack(u->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(u->tile)));
break; }
case TRACK_BIT_DEPOT: } else if (u->track != TRACK_BIT_DEPOT) {
break;
default:
TryReserveRailTrack(u->tile, TrackBitsToTrack(u->track)); TryReserveRailTrack(u->tile, TrackBitsToTrack(u->track));
break;
} }
} }
} }
@@ -3578,14 +3589,14 @@ static bool CheckTrainCollision(Train *v)
/* can't collide in depot */ /* can't collide in depot */
if (v->track == TRACK_BIT_DEPOT) return false; if (v->track == TRACK_BIT_DEPOT) return false;
assert(v->track == TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile); assert(v->track & TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
TrainCollideChecker tcc; TrainCollideChecker tcc;
tcc.v = v; tcc.v = v;
tcc.num = 0; tcc.num = 0;
/* find colliding vehicles */ /* find colliding vehicles */
if (v->track == TRACK_BIT_WORMHOLE) { if (v->track & TRACK_BIT_WORMHOLE) {
FindVehicleOnPos(v->tile, &tcc, FindTrainCollideEnum); FindVehicleOnPos(v->tile, &tcc, FindTrainCollideEnum);
FindVehicleOnPos(GetOtherTunnelBridgeEnd(v->tile), &tcc, FindTrainCollideEnum); FindVehicleOnPos(GetOtherTunnelBridgeEnd(v->tile), &tcc, FindTrainCollideEnum);
} else { } else {
@@ -3621,7 +3632,7 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data)
struct FindSpaceBetweenTrainsChecker { struct FindSpaceBetweenTrainsChecker {
int32 pos; int32 pos;
uint16 distance; uint16 distance;
DirectionByte direction; DiagDirectionByte direction;
}; };
/** Find train in front and keep distance between trains in tunnel/bridge. */ /** Find train in front and keep distance between trains in tunnel/bridge. */
@@ -3630,15 +3641,20 @@ static Vehicle *FindSpaceBetweenTrainsEnum(Vehicle *v, void *data)
/* Don't look at wagons between front and back of train. */ /* Don't look at wagons between front and back of train. */
if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL; if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL;
if (!IsDiagonalDirection(v->direction)) {
/* Check for vehicles on non-across track pieces of custom bridge head */
if ((GetAcrossTunnelBridgeTrackBits(v->tile) & Train::From(v)->track & TRACK_BIT_ALL) == TRACK_BIT_NONE) return NULL;
}
const FindSpaceBetweenTrainsChecker *checker = (FindSpaceBetweenTrainsChecker*) data; const FindSpaceBetweenTrainsChecker *checker = (FindSpaceBetweenTrainsChecker*) data;
int32 a, b = 0; int32 a, b = 0;
switch (checker->direction) { switch (checker->direction) {
default: NOT_REACHED(); default: NOT_REACHED();
case DIR_NE: a = checker->pos; b = v->x_pos; break; case DIAGDIR_NE: a = checker->pos; b = v->x_pos; break;
case DIR_SE: a = v->y_pos; b = checker->pos; break; case DIAGDIR_SE: a = v->y_pos; b = checker->pos; break;
case DIR_SW: a = v->x_pos; b = checker->pos; break; case DIAGDIR_SW: a = v->x_pos; b = checker->pos; break;
case DIR_NW: a = checker->pos; b = v->y_pos; break; case DIAGDIR_NW: a = checker->pos; b = v->y_pos; break;
} }
if (a > b && a <= (b + (int)(checker->distance)) + (int)(TILE_SIZE) - 1) return v; if (a > b && a <= (b + (int)(checker->distance)) + (int)(TILE_SIZE) - 1) return v;
@@ -3653,18 +3669,18 @@ static bool IsTooCloseBehindTrain(Vehicle *v, TileIndex tile, uint16 distance, b
FindSpaceBetweenTrainsChecker checker; FindSpaceBetweenTrainsChecker checker;
checker.distance = distance; checker.distance = distance;
checker.direction = t->direction; checker.direction = DirToDiagDirAlongAxis(t->direction, DiagDirToAxis(GetTunnelBridgeDirection(t->tile)));
switch (checker.direction) { switch (checker.direction) {
default: NOT_REACHED(); default: NOT_REACHED();
case DIR_NE: checker.pos = (TileX(tile) * TILE_SIZE) + TILE_UNIT_MASK; break; case DIAGDIR_NE: checker.pos = (TileX(tile) * TILE_SIZE) + TILE_UNIT_MASK; break;
case DIR_SE: checker.pos = (TileY(tile) * TILE_SIZE); break; case DIAGDIR_SE: checker.pos = (TileY(tile) * TILE_SIZE); break;
case DIR_SW: checker.pos = (TileX(tile) * TILE_SIZE); break; case DIAGDIR_SW: checker.pos = (TileX(tile) * TILE_SIZE); break;
case DIR_NW: checker.pos = (TileY(tile) * TILE_SIZE) + TILE_UNIT_MASK; break; case DIAGDIR_NW: checker.pos = (TileY(tile) * TILE_SIZE) + TILE_UNIT_MASK; break;
} }
if (HasVehicleOnPos(t->tile, &checker, &FindSpaceBetweenTrainsEnum)) { if (HasVehicleOnPos(t->tile, &checker, &FindSpaceBetweenTrainsEnum)) {
/* Revert train if not going with tunnel direction. */ /* Revert train if not going with tunnel direction. */
if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) { if (checker.direction != GetTunnelBridgeDirection(t->tile)) {
SetBit(t->flags, VRF_REVERSING); SetBit(t->flags, VRF_REVERSING);
} }
return true; return true;
@@ -3673,7 +3689,7 @@ static bool IsTooCloseBehindTrain(Vehicle *v, TileIndex tile, uint16 distance, b
if (check_endtile){ if (check_endtile){
if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(t->tile), &checker, &FindSpaceBetweenTrainsEnum)) { if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(t->tile), &checker, &FindSpaceBetweenTrainsEnum)) {
/* Revert train if not going with tunnel direction. */ /* Revert train if not going with tunnel direction. */
if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) { if (checker.direction != GetTunnelBridgeDirection(t->tile)) {
SetBit(t->flags, VRF_REVERSING); SetBit(t->flags, VRF_REVERSING);
} }
return true; return true;
@@ -3685,19 +3701,30 @@ static bool IsTooCloseBehindTrain(Vehicle *v, TileIndex tile, uint16 distance, b
static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile) static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
{ {
TileIndex veh_orig = t->tile; Trackdir td = TrackEnterdirToTrackdir(FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)), ReverseDiagDir(GetTunnelBridgeDirection(tile)));
TileIndex veh_orig_tile = t->tile;
TrackBits veh_orig_track = t->track;
Direction veh_orig_direction = t->direction;
t->tile = tile; t->tile = tile;
t->track = TRACK_BIT_WORMHOLE;
t->direction = TrackdirToDirection(td);
CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(t->railtype)->compatible_railtypes); CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(t->railtype)->compatible_railtypes);
if (ft.Follow(tile, DiagDirToDiagTrackdir(ReverseDiagDir(GetTunnelBridgeDirection(tile))))) { if (ft.Follow(tile, td)) {
TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile)); TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile));
if (reserved == TRACKDIR_BIT_NONE) { if (reserved == TRACKDIR_BIT_NONE) {
/* next tile is not reserved, so reserve the exit tile */ /* next tile is not reserved, so reserve the exit tile */
SetTunnelBridgeReservation(tile, true); if (IsBridge(tile)) {
TryReserveRailBridgeHead(tile, FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)));
} else {
SetTunnelReservation(tile, true);
}
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
} }
} }
bool ok = TryPathReserve(t); bool ok = TryPathReserve(t);
t->tile = veh_orig; t->tile = veh_orig_tile;
t->track = veh_orig_track;
t->direction = veh_orig_direction;
if (ok && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN); if (ok && IsTunnelBridgePBS(tile)) SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN);
return ok; return ok;
} }
@@ -3715,7 +3742,7 @@ static bool CheckTrainStayInWormHole(Train *t, TileIndex tile)
SigSegState seg_state = (_settings_game.pf.reserve_paths || IsTunnelBridgePBS(tile)) ? SIGSEG_PBS : UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, t->owner); SigSegState seg_state = (_settings_game.pf.reserve_paths || IsTunnelBridgePBS(tile)) ? SIGSEG_PBS : UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, t->owner);
if (seg_state != SIGSEG_PBS) { if (seg_state != SIGSEG_PBS) {
CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(t->railtype)->compatible_railtypes); CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(t->railtype)->compatible_railtypes);
if (ft.Follow(tile, DiagDirToDiagTrackdir(ReverseDiagDir(GetTunnelBridgeDirection(tile))))) { if (ft.Follow(tile, TrackEnterdirToTrackdir(FindFirstTrack(GetAcrossTunnelBridgeTrackBits(tile)), ReverseDiagDir(GetTunnelBridgeDirection(tile))))) {
if (ft.m_new_td_bits != TRACKDIR_BIT_NONE && KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) { if (ft.m_new_td_bits != TRACKDIR_BIT_NONE && KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) {
Trackdir td = FindFirstTrackdir(ft.m_new_td_bits); Trackdir td = FindFirstTrackdir(ft.m_new_td_bits);
if (HasPbsSignalOnTrackdir(ft.m_new_tile, td)) { if (HasPbsSignalOnTrackdir(ft.m_new_tile, td)) {
@@ -3779,6 +3806,18 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
Train *first = v->First(); Train *first = v->First();
Train *prev; Train *prev;
bool direction_changed = false; // has direction of any part changed? bool direction_changed = false; // has direction of any part changed?
bool update_signal_tunbridge_exit = false;
Direction old_direction = INVALID_DIR;
TrackBits old_trackbits = INVALID_TRACK_BIT;
auto notify_direction_changed = [&](Direction old_direction, Direction new_direction) {
if (prev == NULL && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
const AccelerationSlowdownParams *asp = &_accel_slowdown[GetRailTypeInfo(v->railtype)->acceleration_type];
DirDiff diff = DirDifference(old_direction, new_direction);
v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
}
direction_changed = true;
};
if (reverse && v->reverse_distance == 1) { if (reverse && v->reverse_distance == 1) {
goto reverse_train_direction; goto reverse_train_direction;
@@ -3791,11 +3830,24 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* For every vehicle after and including the given vehicle */ /* For every vehicle after and including the given vehicle */
for (prev = v->Previous(); v != nomove; prev = v, v = v->Next()) { for (prev = v->Previous(); v != nomove; prev = v, v = v->Next()) {
old_direction = v->direction;
old_trackbits = v->track;
DiagDirection enterdir = DIAGDIR_BEGIN; DiagDirection enterdir = DIAGDIR_BEGIN;
bool update_signals_crossing = false; // will we update signals or crossing state? bool update_signals_crossing = false; // will we update signals or crossing state?
GetNewVehiclePosResult gp = GetNewVehiclePos(v); GetNewVehiclePosResult gp = GetNewVehiclePos(v);
if (v->track != TRACK_BIT_WORMHOLE) { if (!(v->track & TRACK_BIT_WORMHOLE) && gp.old_tile != gp.new_tile &&
IsRailBridgeHeadTile(gp.old_tile) && DiagdirBetweenTiles(gp.old_tile, gp.new_tile) == GetTunnelBridgeDirection(gp.old_tile)) {
/* left a bridge headtile into a wormhole */
Direction old_direction = v->direction;
uint32 r = VehicleEnterTile(v, gp.old_tile, gp.x, gp.y); // NB: old tile, the bridge head which the train just left
if (HasBit(r, VETS_CANNOT_ENTER)) {
goto invalid_rail;
}
if (old_direction != v->direction) notify_direction_changed(old_direction, v->direction);
}
if (!(v->track & TRACK_BIT_WORMHOLE)) {
/* Not inside tunnel */ /* Not inside tunnel */
if (gp.old_tile == gp.new_tile) { if (gp.old_tile == gp.new_tile) {
/* Staying in the old tile */ /* Staying in the old tile */
@@ -3818,6 +3870,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* The new position is the end of the platform */ /* The new position is the end of the platform */
TrainEnterStation(v->First(), r >> VETS_STATION_ID_OFFSET); TrainEnterStation(v->First(), r >> VETS_STATION_ID_OFFSET);
} }
if (old_direction != v->direction) notify_direction_changed(old_direction, v->direction);
} }
} else { } else {
/* A new tile is about to be entered. */ /* A new tile is about to be entered. */
@@ -3826,9 +3879,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile); enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
assert(IsValidDiagDirection(enterdir)); assert(IsValidDiagDirection(enterdir));
enter_new_tile:
/* Get the status of the tracks in the new tile and mask /* Get the status of the tracks in the new tile and mask
* away the bits that aren't reachable. */ * away the bits that aren't reachable. */
TrackStatus ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL, 0, ReverseDiagDir(enterdir)); TrackStatus ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL, 0, (v->track & TRACK_BIT_WORMHOLE) ? INVALID_DIAGDIR : ReverseDiagDir(enterdir));
TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir); TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs; TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
@@ -3838,7 +3893,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
if (_settings_game.pf.forbid_90_deg && prev == NULL) { if (_settings_game.pf.forbid_90_deg && prev == NULL) {
/* We allow wagons to make 90 deg turns, because forbid_90_deg /* We allow wagons to make 90 deg turns, because forbid_90_deg
* can be switched on halfway a turn */ * can be switched on halfway a turn */
if (!(v->track & TRACK_BIT_WORMHOLE)) {
bits &= ~TrackCrossesTracks(FindFirstTrack(v->track)); bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
} else if (v->track & TRACK_BIT_MASK) {
bits &= ~TrackCrossesTracks(FindFirstTrack(v->track & TRACK_BIT_MASK));
}
} }
if (bits == TRACK_BIT_NONE) goto invalid_rail; if (bits == TRACK_BIT_NONE) goto invalid_rail;
@@ -3926,9 +3985,9 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
} }
} else { } else {
/* The wagon is active, simply follow the prev vehicle. */ /* The wagon is active, simply follow the prev vehicle. */
if (prev->tile == gp.new_tile) { if (TileVirtXY(prev->x_pos, prev->y_pos) == gp.new_tile) {
/* Choose the same track as prev */ /* Choose the same track as prev */
if (prev->track == TRACK_BIT_WORMHOLE) { if (prev->track & TRACK_BIT_WORMHOLE) {
/* Vehicles entering tunnels enter the wormhole earlier than for bridges. /* Vehicles entering tunnels enter the wormhole earlier than for bridges.
* However, just choose the track into the wormhole. */ * However, just choose the track into the wormhole. */
assert(IsTunnel(prev->tile)); assert(IsTunnel(prev->tile));
@@ -3950,7 +4009,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
{TRACK_BIT_NONE, TRACK_BIT_RIGHT, TRACK_BIT_X, TRACK_BIT_UPPER}, {TRACK_BIT_NONE, TRACK_BIT_RIGHT, TRACK_BIT_X, TRACK_BIT_UPPER},
{TRACK_BIT_RIGHT, TRACK_BIT_NONE, TRACK_BIT_LOWER, TRACK_BIT_Y } {TRACK_BIT_RIGHT, TRACK_BIT_NONE, TRACK_BIT_LOWER, TRACK_BIT_Y }
}; };
DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile); DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, TileVirtXY(prev->x_pos, prev->y_pos));
assert(IsValidDiagDirection(exitdir)); assert(IsValidDiagDirection(exitdir));
chosen_track = _connecting_track[enterdir][exitdir]; chosen_track = _connecting_track[enterdir][exitdir];
} }
@@ -3970,12 +4029,12 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
Direction chosen_dir = (Direction)b[2]; Direction chosen_dir = (Direction)b[2];
/* Call the landscape function and tell it that the vehicle entered the tile */ /* Call the landscape function and tell it that the vehicle entered the tile */
uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); uint32 r = (v->track & TRACK_BIT_WORMHOLE) ? 0 : VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
if (HasBit(r, VETS_CANNOT_ENTER)) { if (HasBit(r, VETS_CANNOT_ENTER)) {
goto invalid_rail; goto invalid_rail;
} }
if (IsTunnelBridgeWithSignalSimulation(gp.new_tile)) { if (!(v->track & TRACK_BIT_WORMHOLE) && IsTunnelBridgeWithSignalSimulation(gp.new_tile) && (GetAcrossTunnelBridgeTrackBits(gp.new_tile) & chosen_track)) {
/* If red signal stop. */ /* If red signal stop. */
if (v->IsFrontEngine() && v->force_proceed == 0) { if (v->IsFrontEngine() && v->force_proceed == 0) {
if (IsTunnelBridgeSignalSimulationEntrance(gp.new_tile) && GetTunnelBridgeEntranceSignalState(gp.new_tile) == SIGNAL_STATE_RED) { if (IsTunnelBridgeSignalSimulationEntrance(gp.new_tile) && GetTunnelBridgeEntranceSignalState(gp.new_tile) == SIGNAL_STATE_RED) {
@@ -4008,7 +4067,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
} }
/* Clear any track reservation when the last vehicle leaves the tile */ /* Clear any track reservation when the last vehicle leaves the tile */
if (v->Next() == NULL) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir(), true); if (v->Next() == NULL && !(v->track & TRACK_BIT_WORMHOLE)) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir(), true);
v->tile = gp.new_tile; v->tile = gp.new_tile;
@@ -4025,12 +4084,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
update_signals_crossing = true; update_signals_crossing = true;
if (chosen_dir != v->direction) { if (chosen_dir != v->direction) {
if (prev == NULL && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) { notify_direction_changed(v->direction, chosen_dir);
const AccelerationSlowdownParams *asp = &_accel_slowdown[GetRailTypeInfo(v->railtype)->acceleration_type];
DirDiff diff = DirDifference(v->direction, chosen_dir);
v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
}
direction_changed = true;
v->direction = chosen_dir; v->direction = chosen_dir;
} }
@@ -4106,19 +4160,16 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
v->force_proceed = 0; v->force_proceed = 0;
v->wait_counter = 0; v->wait_counter = 0;
v->tunnel_bridge_signal_num = 0; v->tunnel_bridge_signal_num = 0;
v->x_pos = gp.x; update_signal_tunbridge_exit = true;
v->y_pos = gp.y;
v->UpdatePosition();
v->UpdateViewport(false, false);
UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner);
continue;
} }
} }
if (old_tile == gp.new_tile && IsTunnelBridgeWithSignalSimulation(v->tile) && v->IsFrontEngine()) { if (old_tile == gp.new_tile && IsTunnelBridgeWithSignalSimulation(v->tile) && v->IsFrontEngine()) {
TileIndex next_tile = old_tile + TileOffsByDir(v->direction); Axis axis = DiagDirToAxis(GetTunnelBridgeDirection(v->tile));
DiagDirection axial_dir = DirToDiagDirAlongAxis(v->direction, axis);
TileIndex next_tile = old_tile + TileOffsByDiagDir(axial_dir);
bool is_exit = false; bool is_exit = false;
if (IsTileType(next_tile, MP_TUNNELBRIDGE) && IsTunnelBridgeWithSignalSimulation(next_tile) && if (IsTileType(next_tile, MP_TUNNELBRIDGE) && IsTunnelBridgeWithSignalSimulation(next_tile) &&
ReverseDiagDir(GetTunnelBridgeDirection(next_tile)) == DirToDiagDir(v->direction)) { ReverseDiagDir(GetTunnelBridgeDirection(next_tile)) == axial_dir) {
if (IsBridge(next_tile) && IsBridge(v->tile)) { if (IsBridge(next_tile) && IsBridge(v->tile)) {
// bridge ramp facing towards us // bridge ramp facing towards us
is_exit = true; is_exit = true;
@@ -4140,6 +4191,10 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
/* Perform look-ahead on tunnel exit. */ /* Perform look-ahead on tunnel exit. */
if (IsRailCustomBridgeHeadTile(gp.new_tile)) {
enterdir = ReverseDiagDir(GetTunnelBridgeDirection(gp.new_tile));
goto enter_new_tile;
}
if (v->IsFrontEngine()) { if (v->IsFrontEngine()) {
TryReserveRailTrack(gp.new_tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(gp.new_tile))); TryReserveRailTrack(gp.new_tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(gp.new_tile)));
CheckNextTrainTile(v); CheckNextTrainTile(v);
@@ -4163,6 +4218,10 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
} }
} }
if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); if (v->IsDrawn()) v->Vehicle::UpdateViewport(true);
if (update_signal_tunbridge_exit) {
UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner);
update_signal_tunbridge_exit = false;
}
continue; continue;
} }
} }
@@ -4185,6 +4244,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
AffectSpeedByZChange(v, old_z); AffectSpeedByZChange(v, old_z);
} }
if (update_signal_tunbridge_exit) {
UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner);
update_signal_tunbridge_exit = false;
}
if (update_signals_crossing) { if (update_signals_crossing) {
if (v->IsFrontEngine()) { if (v->IsFrontEngine()) {
switch (TrainMovedChangeSignal(v, gp.new_tile, enterdir)) { switch (TrainMovedChangeSignal(v, gp.new_tile, enterdir)) {
@@ -4264,6 +4328,11 @@ invalid_rail:
if (prev != NULL) error("Disconnecting train"); if (prev != NULL) error("Disconnecting train");
reverse_train_direction: reverse_train_direction:
if (old_trackbits != INVALID_TRACK_BIT && (v->track ^ old_trackbits) & TRACK_BIT_WORMHOLE) {
/* Entering/exiting wormhole failed/aborted, back out changes to vehicle direction and track */
v->track = old_trackbits;
v->direction = old_direction;
}
if (reverse) { if (reverse) {
v->wait_counter = 0; v->wait_counter = 0;
v->cur_speed = 0; v->cur_speed = 0;
@@ -4274,6 +4343,21 @@ reverse_train_direction:
return false; return false;
} }
static TrackBits GetTrackbitsFromCrashedVehicle(Train *t)
{
TrackBits train_tbits = t->track;
if (train_tbits & TRACK_BIT_WORMHOLE) {
/* Vehicle is inside a wormhole, v->track contains no useful value then. */
train_tbits = GetAcrossTunnelBridgeReservationTrackBits(t->tile);
if (train_tbits != TRACK_BIT_NONE) return train_tbits;
/* Pick the first available tunnel/bridge head track which could be reserved */
train_tbits = GetAcrossTunnelBridgeTrackBits(t->tile);
return train_tbits ^ KillFirstBit(train_tbits);
} else {
return train_tbits;
}
}
/** /**
* Collect trackbits of all crashed train vehicles on a tile * Collect trackbits of all crashed train vehicles on a tile
* @param v Vehicle passed from Find/HasVehicleOnPos() * @param v Vehicle passed from Find/HasVehicleOnPos()
@@ -4285,18 +4369,34 @@ static Vehicle *CollectTrackbitsFromCrashedVehiclesEnum(Vehicle *v, void *data)
TrackBits *trackbits = (TrackBits *)data; TrackBits *trackbits = (TrackBits *)data;
if (v->type == VEH_TRAIN && (v->vehstatus & VS_CRASHED) != 0) { if (v->type == VEH_TRAIN && (v->vehstatus & VS_CRASHED) != 0) {
TrackBits train_tbits = Train::From(v)->track; if (Train::From(v)->track != TRACK_BIT_DEPOT) {
if (train_tbits == TRACK_BIT_WORMHOLE) { *trackbits |= GetTrackbitsFromCrashedVehicle(Train::From(v));
/* Vehicle is inside a wormhole, v->track contains no useful value then. */
*trackbits |= DiagDirToDiagTrackBits(GetTunnelBridgeDirection(v->tile));
} else if (train_tbits != TRACK_BIT_DEPOT) {
*trackbits |= train_tbits;
} }
} }
return NULL; return NULL;
} }
static void SetSignalledBridgeTunnelGreenIfClear(TileIndex tile, TileIndex end)
{
if (TunnelBridgeIsFree(tile, end, nullptr, true).Succeeded()) {
auto process_tile = [](TileIndex t) {
if (IsTunnelBridgeSignalSimulationEntrance(t)) {
if (IsBridge(t)) {
SetAllBridgeEntranceSimulatedSignalsGreen(t);
MarkBridgeDirty(t, ZOOM_LVL_DRAW_MAP);
}
if (IsTunnelBridgeSignalSimulationEntrance(t) && GetTunnelBridgeEntranceSignalState(t) == SIGNAL_STATE_RED) {
SetTunnelBridgeEntranceSignalState(t, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(t, ZOOM_LVL_DRAW_MAP);
}
}
};
process_tile(tile);
process_tile(end);
}
}
/** /**
* Deletes/Clears the last wagon of a crashed train. It takes the engine of the * Deletes/Clears the last wagon of a crashed train. It takes the engine of the
* train, then goes to the last wagon and deletes that. Each call to this function * train, then goes to the last wagon and deletes that. Each call to this function
@@ -4327,18 +4427,14 @@ static void DeleteLastWagon(Train *v)
} }
/* 'v' shouldn't be accessed after it has been deleted */ /* 'v' shouldn't be accessed after it has been deleted */
TrackBits trackbits = v->track; const TrackBits orig_trackbits = v->track;
TileIndex tile = v->tile; TrackBits trackbits = GetTrackbitsFromCrashedVehicle(v);
Owner owner = v->owner; const TileIndex tile = v->tile;
const Owner owner = v->owner;
delete v; delete v;
v = NULL; // make sure nobody will try to read 'v' anymore v = NULL; // make sure nobody will try to read 'v' anymore
if (trackbits == TRACK_BIT_WORMHOLE) {
/* Vehicle is inside a wormhole, v->track contains no useful value then. */
trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(tile));
}
Track track = TrackBitsToTrack(trackbits); Track track = TrackBitsToTrack(trackbits);
if (HasReservedTracks(tile, trackbits)) { if (HasReservedTracks(tile, trackbits)) {
UnreserveRailTrack(tile, track); UnreserveRailTrack(tile, track);
@@ -4357,29 +4453,12 @@ static void DeleteLastWagon(Train *v)
if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile); if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
/* Update signals */ /* Update signals */
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
if (IsTunnelBridgeWithSignalSimulation(tile)) { if (IsTunnelBridgeWithSignalSimulation(tile)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile); TileIndex end = GetOtherTunnelBridgeEnd(tile);
UpdateSignalsOnSegment(end, INVALID_DIAGDIR, owner); UpdateSignalsOnSegment(end, INVALID_DIAGDIR, owner);
if (TunnelBridgeIsFree(tile, end, nullptr).Succeeded()) { SetSignalledBridgeTunnelGreenIfClear(tile, end);
auto process_tile = [](TileIndex t) {
if (IsTunnelBridgeSignalSimulationEntrance(t)) {
if (IsBridge(t)) {
SetAllBridgeEntranceSimulatedSignalsGreen(t);
MarkBridgeDirty(t, ZOOM_LVL_DRAW_MAP);
} }
if (IsTunnelBridgeSignalSimulationEntrance(t) && GetTunnelBridgeEntranceSignalState(t) == SIGNAL_STATE_RED) { if ((orig_trackbits & TRACK_BIT_WORMHOLE) || IsRailDepotTile(tile)) {
SetTunnelBridgeEntranceSignalState(t, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(t, ZOOM_LVL_DRAW_MAP);
}
}
};
process_tile(tile);
process_tile(end);
}
}
} else if (IsRailDepotTile(tile)) {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner); UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
} else { } else {
SetSignalsOnBothDir(tile, track, owner); SetSignalsOnBothDir(tile, track, owner);
@@ -4403,7 +4482,7 @@ static void ChangeTrainDirRandomly(Train *v)
/* Refrain from updating the z position of the vehicle when on /* Refrain from updating the z position of the vehicle when on
* a bridge, because UpdateInclination() will put the vehicle under * a bridge, because UpdateInclination() will put the vehicle under
* the bridge in that case */ * the bridge in that case */
if (v->track != TRACK_BIT_WORMHOLE) { if (!(v->track & TRACK_BIT_WORMHOLE)) {
v->UpdatePosition(); v->UpdatePosition();
v->UpdateInclination(false, true); v->UpdateInclination(false, true);
} else { } else {
@@ -4518,7 +4597,7 @@ static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse)
static bool TrainCanLeaveTile(const Train *v) static bool TrainCanLeaveTile(const Train *v)
{ {
/* Exit if inside a tunnel/bridge or a depot */ /* Exit if inside a tunnel/bridge or a depot */
if (v->track == TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT) return false; if (v->track & TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT) return false;
TileIndex tile = v->tile; TileIndex tile = v->tile;
@@ -4526,6 +4605,13 @@ static bool TrainCanLeaveTile(const Train *v)
if (IsTileType(tile, MP_TUNNELBRIDGE)) { if (IsTileType(tile, MP_TUNNELBRIDGE)) {
DiagDirection dir = GetTunnelBridgeDirection(tile); DiagDirection dir = GetTunnelBridgeDirection(tile);
if (DiagDirToDir(dir) == v->direction) return false; if (DiagDirToDir(dir) == v->direction) return false;
if (IsRailCustomBridgeHeadTile(tile) && VehicleExitDir(v->direction, v->track) == dir) {
if (_settings_game.pf.forbid_90_deg && v->Previous() == NULL && GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) == 0) {
/* Check for 90 degree turn on zero-length bridge span */
if (!(GetCustomBridgeHeadTrackBits(tile) & ~TrackCrossesTracks(FindFirstTrack(v->track)))) return true;
}
return false;
}
} }
/* entering a depot? */ /* entering a depot? */
@@ -4926,7 +5012,13 @@ Trackdir Train::GetVehicleTrackdir() const
if (this->track == TRACK_BIT_WORMHOLE) { if (this->track == TRACK_BIT_WORMHOLE) {
/* train in tunnel or on bridge, so just use his direction and assume a diagonal track */ /* train in tunnel or on bridge, so just use his direction and assume a diagonal track */
return DiagDirToDiagTrackdir(DirToDiagDir(this->direction)); TrackBits tracks = GetAcrossTunnelBridgeReservationTrackBits(this->tile);
if (!tracks) tracks = GetAcrossTunnelBridgeTrackBits(this->tile);
Trackdir td = TrackExitdirToTrackdir(FindFirstTrack(tracks), GetTunnelBridgeDirection(this->tile));
if (GetTunnelBridgeDirection(this->tile) != DirToDiagDir(this->direction)) td = ReverseTrackdir(td);
return td;
} else if (this->track & TRACK_BIT_WORMHOLE) {
return TrackDirectionToTrackdir(FindFirstTrack(this->track & TRACK_BIT_MASK), this->direction);
} }
return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction); return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
@@ -4957,23 +5049,28 @@ void DeleteVisibleTrain(Train *v)
/* 'u' shouldn't be accessed after it has been deleted */ /* 'u' shouldn't be accessed after it has been deleted */
TileIndex tile = u->tile; TileIndex tile = u->tile;
TrackBits trackbits = u->track; TrackBits trackbits = u->track;
bool in_wormhole = trackbits & TRACK_BIT_WORMHOLE;
delete u; delete u;
if (trackbits == TRACK_BIT_WORMHOLE) { if (in_wormhole) {
/* Vehicle is inside a wormhole, u->track contains no useful value then. */ /* Vehicle is inside a wormhole, u->track contains no useful value then. */
trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(tile)); if (IsTunnelBridgeWithSignalSimulation(tile)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
UpdateSignalsOnSegment(end, INVALID_DIAGDIR, GetTileOwner(tile));
SetSignalledBridgeTunnelGreenIfClear(tile, end);
} }
} else {
Track track = TrackBitsToTrack(trackbits); Track track = TrackBitsToTrack(trackbits);
if (HasReservedTracks(tile, trackbits)) UnreserveRailTrack(tile, track); if (HasReservedTracks(tile, trackbits)) UnreserveRailTrack(tile, track);
if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile); if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
}
/* Update signals */ /* Update signals */
if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) { if (in_wormhole || IsRailDepotTile(tile)) {
AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile)); AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile));
} else { } else {
AddTrackToSignalBuffer(tile, track, GetTileOwner(tile)); AddTrackToSignalBuffer(tile, TrackBitsToTrack(trackbits), GetTileOwner(tile));
} }
} while (prev != NULL); } while (prev != NULL);

View File

@@ -55,6 +55,40 @@ static inline TunnelID GetTunnelIndex(TileIndex t)
return map_id == TUNNEL_ID_MAP_LOOKUP ? GetTunnelIndexByLookup(t) : map_id; return map_id == TUNNEL_ID_MAP_LOOKUP ? GetTunnelIndexByLookup(t) : map_id;
} }
/**
* Checks if this tile is a rail tunnel
* @param t the tile that might be a rail tunnel
* @return true if it is a rail tunnel
*/
static inline bool IsRailTunnelTile(TileIndex t)
{
return IsTunnelTile(t) && (TransportType)GB(_m[t].m5, 2, 2) == TRANSPORT_RAIL;
}
/**
* Get the reservation state of the rail tunnel
* @pre IsRailTunnelTile(t)
* @param t the tile
* @return reservation state
*/
static inline bool HasTunnelReservation(TileIndex t)
{
assert(IsRailTunnelTile(t));
return HasBit(_m[t].m5, 4);
}
/**
* Set the reservation state of the rail tunnel
* @pre IsRailTunnelTile(t)
* @param t the tile
* @param b the reservation state
*/
static inline void SetTunnelReservation(TileIndex t, bool b)
{
assert(IsRailTunnelTile(t));
SB(_m[t].m5, 4, 1, b ? 1 : 0);
}
TileIndex GetOtherTunnelEnd(TileIndex); TileIndex GetOtherTunnelEnd(TileIndex);
/** Flags for miscellaneous industry tile specialities */ /** Flags for miscellaneous industry tile specialities */

View File

@@ -17,6 +17,7 @@
void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END); void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END);
void MarkBridgeDirty(TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END); void MarkBridgeDirty(TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END);
void MarkBridgeOrTunnelDirty(TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END); void MarkBridgeOrTunnelDirty(TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END);
void MarkBridgeOrTunnelDirtyOnReservationChange(TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END);
uint GetTunnelBridgeSignalSimulationSignalCount(TileIndex begin, TileIndex end); uint GetTunnelBridgeSignalSimulationSignalCount(TileIndex begin, TileIndex end);
/** /**

View File

@@ -59,6 +59,7 @@ TileIndex _build_tunnel_endtile; ///< The end of a tunnel; as hidden return from
/** Z position of the bridge sprites relative to bridge height (downwards) */ /** Z position of the bridge sprites relative to bridge height (downwards) */
static const int BRIDGE_Z_START = 3; static const int BRIDGE_Z_START = 3;
extern void DrawTrackBits(TileInfo *ti, TrackBits track);
extern void DrawRoadBits(TileInfo *ti); extern void DrawRoadBits(TileInfo *ti);
extern const RoadBits _invalid_tileh_slopes_road[2][15]; extern const RoadBits _invalid_tileh_slopes_road[2][15];
@@ -102,6 +103,21 @@ void MarkBridgeOrTunnelDirty(TileIndex tile, const ZoomLevel mark_dirty_if_zooml
} }
} }
/**
* Mark bridge or tunnel tiles dirty on tunnel/bridge head reservation change
* @param tile Bridge head or tunnel entrance.
*/
void MarkBridgeOrTunnelDirtyOnReservationChange(TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below)
{
if (IsTunnelBridgeWithSignalSimulation(tile)) {
MarkTileDirtyByTile(tile, mark_dirty_if_zoomlevel_is_below);
} else if (IsBridge(tile)) {
MarkBridgeDirty(tile, mark_dirty_if_zoomlevel_is_below);
} else {
MarkTileDirtyByTile(tile, mark_dirty_if_zoomlevel_is_below);
}
}
/** /**
* Get number of signals on bridge or tunnel with signal simulation. * Get number of signals on bridge or tunnel with signal simulation.
* @param begin The begin of the tunnel or bridge. * @param begin The begin of the tunnel or bridge.
@@ -515,9 +531,9 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
switch (transport_type) { switch (transport_type) {
case TRANSPORT_RAIL: case TRANSPORT_RAIL:
/* Add to company infrastructure count if required. */ /* Add to company infrastructure count if required. */
if (is_new_owner && c != NULL) c->infrastructure.rail[railtype] += (bridge_len + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
MakeRailBridgeRamp(tile_start, owner, bridge_type, dir, railtype, is_upgrade); MakeRailBridgeRamp(tile_start, owner, bridge_type, dir, railtype, is_upgrade);
MakeRailBridgeRamp(tile_end, owner, bridge_type, ReverseDiagDir(dir), railtype, is_upgrade); MakeRailBridgeRamp(tile_end, owner, bridge_type, ReverseDiagDir(dir), railtype, is_upgrade);
if (is_new_owner && c != NULL) c->infrastructure.rail[railtype] += (bridge_len * TUNNELBRIDGE_TRACKBIT_FACTOR) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile_start) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile_end);
break; break;
case TRANSPORT_ROAD: { case TRANSPORT_ROAD: {
@@ -982,7 +998,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
Owner owner = GetTileOwner(tile); Owner owner = GetTileOwner(tile);
Train *v = NULL; Train *v = NULL;
if (HasTunnelBridgeReservation(tile)) { if (HasTunnelReservation(tile)) {
v = GetTrainForReservation(tile, track); v = GetTrainForReservation(tile, track);
if (v != NULL) FreeTrainTrackReservation(v); if (v != NULL) FreeTrainTrackReservation(v);
} }
@@ -1066,25 +1082,49 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM, flags); ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM, flags);
} }
CommandCost cost(EXPENSES_CONSTRUCTION);
const bool rail = GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL;
TrackBits tile_tracks = TRACK_BIT_NONE;
TrackBits endtile_tracks = TRACK_BIT_NONE;
if (rail) {
tile_tracks = GetCustomBridgeHeadTrackBits(tile);
endtile_tracks = GetCustomBridgeHeadTrackBits(endtile);
cost.AddCost(RailClearCost(GetRailType(tile)) * (CountBits(tile_tracks) + CountBits(endtile_tracks) - 2));
}
Money base_cost = (GetTunnelBridgeTransportType(tile) != TRANSPORT_WATER) ? _price[PR_CLEAR_BRIDGE] : _price[PR_CLEAR_AQUEDUCT]; Money base_cost = (GetTunnelBridgeTransportType(tile) != TRANSPORT_WATER) ? _price[PR_CLEAR_BRIDGE] : _price[PR_CLEAR_AQUEDUCT];
uint len = GetTunnelBridgeLength(tile, endtile) + 2; // Don't forget the end tiles. uint middle_len = GetTunnelBridgeLength(tile, endtile);
uint len = middle_len + 2; // Don't forget the end tiles.
cost.AddCost(len * base_cost);
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
/* read this value before actual removal of bridge */ /* read this value before actual removal of bridge */
bool rail = GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL;
Owner owner = GetTileOwner(tile); Owner owner = GetTileOwner(tile);
int height = GetBridgeHeight(tile); int height = GetBridgeHeight(tile);
Train *v = NULL; SmallVector<Train *, 2> vehicles_affected;
if (rail && HasTunnelBridgeReservation(tile)) { if (rail) {
v = GetTrainForReservation(tile, DiagDirToDiagTrack(direction)); auto find_train_reservations = [&vehicles_affected](TileIndex tile) {
if (v != NULL) FreeTrainTrackReservation(v); TrackBits reserved = GetBridgeReservationTrackBits(tile);
Track track;
while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
Train *v = GetTrainForReservation(tile, track);
if (v != NULL) {
FreeTrainTrackReservation(v);
*vehicles_affected.Append() = v;
}
}
};
find_train_reservations(tile);
find_train_reservations(endtile);
} }
/* Update company infrastructure counts. */ /* Update company infrastructure counts. */
if (rail) { if (rail) {
if (Company::IsValidID(owner)) { if (Company::IsValidID(owner)) {
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= (middle_len * TUNNELBRIDGE_TRACKBIT_FACTOR) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(endtile);
if (IsTunnelBridgeWithSignalSimulation(tile)) { // handle tunnel/bridge signals. if (IsTunnelBridgeWithSignalSimulation(tile)) { // handle tunnel/bridge signals.
Company::Get(GetTileOwner(tile))->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, endtile); Company::Get(GetTileOwner(tile))->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, endtile);
} }
@@ -1113,18 +1153,28 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
if (rail) { if (rail) {
/* cannot use INVALID_DIAGDIR for signal update because the bridge doesn't exist anymore */ /* cannot use INVALID_DIAGDIR for signal update because the bridge doesn't exist anymore */
AddSideToSignalBuffer(tile, ReverseDiagDir(direction), owner);
AddSideToSignalBuffer(endtile, direction, owner);
Track track = DiagDirToDiagTrack(direction); auto notify_track_change = [owner](TileIndex tile, DiagDirection direction, TrackBits tracks) {
YapfNotifyTrackLayoutChange(tile, track); auto check_dir = [&](DiagDirection d) {
YapfNotifyTrackLayoutChange(endtile, track); if (DiagdirReachesTracks(d) & tracks) AddSideToSignalBuffer(tile, d, owner);
};
check_dir(ChangeDiagDir(direction, DIAGDIRDIFF_90RIGHT));
check_dir(ChangeDiagDir(direction, DIAGDIRDIFF_REVERSE));
check_dir(ChangeDiagDir(direction, DIAGDIRDIFF_90LEFT));
while (tracks != TRACK_BIT_NONE) {
YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
}
};
notify_track_change(tile, direction, tile_tracks);
notify_track_change(endtile, ReverseDiagDir(direction), endtile_tracks);
if (v != NULL) TryPathReserve(v, true); for (uint i = 0; i < vehicles_affected.Length(); ++i) {
TryPathReserve(vehicles_affected[i], true);
}
} }
} }
return CommandCost(EXPENSES_CONSTRUCTION, len * base_cost); return cost;
} }
/** /**
@@ -1489,7 +1539,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
} }
/* PBS debugging, draw reserved tracks darker */ /* PBS debugging, draw reserved tracks darker */
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) { if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelReservation(ti->tile)) {
if (rti->UsesOverlay()) { if (rti->UsesOverlay()) {
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY); SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
DrawGroundSprite(overlay + RTO_X + DiagDirToAxis(tunnelbridge_direction), PALETTE_CRASH); DrawGroundSprite(overlay + RTO_X + DiagDirToAxis(tunnelbridge_direction), PALETTE_CRASH);
@@ -1531,6 +1581,52 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
DrawBridgeMiddle(ti); DrawBridgeMiddle(ti);
return; return;
} }
if (transport_type == TRANSPORT_RAIL && IsRailCustomBridgeHead(ti->tile)) {
DrawTrackBits(ti, GetCustomBridgeHeadTrackBits(ti->tile));
if (HasRailCatenaryDrawn(GetRailType(ti->tile))) {
DrawRailCatenary(ti);
}
if (IsTunnelBridgeWithSignalSimulation(ti->tile)) {
extern void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition,
SignalOffsets image, uint pos, SignalType type, SignalVariant variant, bool show_restricted);
DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
Track t = FindFirstTrack(GetAcrossTunnelBridgeTrackBits(ti->tile));
auto draw_signals = [&](uint position, SignalOffsets image, DiagDirection towards) {
if (dir == towards) {
/* flip signal directions */
position ^= 1;
image = (SignalOffsets)(image ^ 1);
}
if (IsTunnelBridgeSignalSimulationEntrance(ti->tile)) {
DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeEntranceSignalState(ti->tile), image, position, SIGTYPE_NORMAL, variant, 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);
}
};
switch (t) {
default: NOT_REACHED();
case TRACK_X: draw_signals( 8, SIGNAL_TO_SOUTHWEST, DIAGDIR_SW); break;
case TRACK_Y: draw_signals(10, SIGNAL_TO_SOUTHEAST, DIAGDIR_NW); break;
case TRACK_UPPER: draw_signals( 4, SIGNAL_TO_WEST, DIAGDIR_NW); break;
case TRACK_LOWER: draw_signals( 6, SIGNAL_TO_WEST, DIAGDIR_SW); break;
case TRACK_LEFT: draw_signals( 0, SIGNAL_TO_NORTH, DIAGDIR_NW); break;
case TRACK_RIGHT: draw_signals( 2, SIGNAL_TO_NORTH, DIAGDIR_NE); break;
}
}
DrawBridgeMiddle(ti);
return;
}
const PalSpriteID *psid; const PalSpriteID *psid;
int base_offset; int base_offset;
@@ -1614,7 +1710,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
} }
/* PBS debugging, draw reserved tracks darker */ /* PBS debugging, draw reserved tracks darker */
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) { if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && GetBridgeReservationTrackBits(ti->tile) != TRACK_BIT_NONE) {
if (rti->UsesOverlay()) { if (rti->UsesOverlay()) {
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY); SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) { if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
@@ -1782,8 +1878,8 @@ void DrawBridgeMiddle(const TileInfo *ti)
} }
} }
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && !IsInvisibilitySet(TO_BRIDGES) && HasTunnelBridgeReservation(rampnorth) if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && !IsInvisibilitySet(TO_BRIDGES)
&& !IsTunnelBridgeWithSignalSimulation(rampnorth)) { && !IsTunnelBridgeWithSignalSimulation(rampnorth) && (HasAcrossBridgeReservation(rampnorth) || HasAcrossBridgeReservation(rampsouth))) {
if (rti->UsesOverlay()) { if (rti->UsesOverlay()) {
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY); SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
AddSortableSpriteToDraw(overlay + RTO_X + axis, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, bridge_z, IsTransparencySet(TO_BRIDGES)); AddSortableSpriteToDraw(overlay + RTO_X + axis, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, bridge_z, IsTransparencySet(TO_BRIDGES));
@@ -1852,7 +1948,7 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y)
/* In the tunnel entrance? */ /* In the tunnel entrance? */
if (5 <= pos && pos <= 10) return z; if (5 <= pos && pos <= 10) return z;
} else { // IsBridge(tile) } else { // IsBridge(tile)
if (IsRoadCustomBridgeHeadTile(tile)) { if (IsCustomBridgeHeadTile(tile)) {
return z + TILE_HEIGHT + (IsSteepSlope(tileh) ? TILE_HEIGHT : 0); return z + TILE_HEIGHT + (IsSteepSlope(tileh) ? TILE_HEIGHT : 0);
} }
@@ -1883,7 +1979,7 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y)
static Foundation GetFoundation_TunnelBridge(TileIndex tile, Slope tileh) static Foundation GetFoundation_TunnelBridge(TileIndex tile, Slope tileh)
{ {
if (IsRoadCustomBridgeHeadTile(tile)) return FOUNDATION_LEVELED; if (IsCustomBridgeHeadTile(tile)) return FOUNDATION_LEVELED;
return IsTunnel(tile) ? FOUNDATION_NONE : GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(tile))); return IsTunnel(tile) ? FOUNDATION_NONE : GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(tile)));
} }
@@ -1997,15 +2093,14 @@ static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType
DiagDirection dir = GetTunnelBridgeDirection(tile); DiagDirection dir = GetTunnelBridgeDirection(tile);
if (mode == TRANSPORT_ROAD && IsRoadCustomBridgeHeadTile(tile)) {
if (side != INVALID_DIAGDIR && side == dir) return 0; if (side != INVALID_DIAGDIR && side == dir) return 0;
if (mode == TRANSPORT_ROAD && IsRoadCustomBridgeHeadTile(tile)) {
TrackBits bits = TRACK_BIT_NONE; TrackBits bits = TRACK_BIT_NONE;
if (sub_mode & ROADTYPES_TRAM) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM)]; if (sub_mode & ROADTYPES_TRAM) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM)];
if (sub_mode & ROADTYPES_ROAD) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_ROAD)]; if (sub_mode & ROADTYPES_ROAD) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_ROAD)];
return CombineTrackStatus(TrackBitsToTrackdirBits(bits), TRACKDIR_BIT_NONE); return CombineTrackStatus(TrackBitsToTrackdirBits(bits), TRACKDIR_BIT_NONE);
} }
if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0; return CombineTrackStatus(TrackBitsToTrackdirBits(mode == TRANSPORT_RAIL ? GetTunnelBridgeTrackBits(tile) : DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE);
return CombineTrackStatus(TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE);
} }
static void UpdateRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end, bool add) { static void UpdateRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end, bool add) {
@@ -2063,9 +2158,6 @@ void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) {
static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner) static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner)
{ {
const TileIndex other_end = GetOtherTunnelBridgeEnd(tile); const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
/* Set number of pieces to zero if it's the southern tile as we
* don't want to update the infrastructure counts twice. */
const uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
const TransportType tt = GetTunnelBridgeTransportType(tile); const TransportType tt = GetTunnelBridgeTransportType(tile);
if (tt == TRANSPORT_ROAD && tile < other_end) { if (tt == TRANSPORT_ROAD && tile < other_end) {
@@ -2092,9 +2184,15 @@ static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner
Company *old = Company::Get(old_owner); Company *old = Company::Get(old_owner);
if (tt == TRANSPORT_RAIL) { if (tt == TRANSPORT_RAIL) {
/* Set number of middle pieces to zero if it's the southern tile as we
* don't want to update the infrastructure counts twice. */
const uint num_pieces = GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + (tile < other_end ? GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0);
old->infrastructure.rail[GetRailType(tile)] -= num_pieces; old->infrastructure.rail[GetRailType(tile)] -= num_pieces;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.rail[GetRailType(tile)] += num_pieces; if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.rail[GetRailType(tile)] += num_pieces;
} else if (tt == TRANSPORT_WATER) { } else if (tt == TRANSPORT_WATER) {
/* Set number of pieces to zero if it's the southern tile as we
* don't want to update the infrastructure counts twice. */
const uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
old->infrastructure.water -= num_pieces; old->infrastructure.water -= num_pieces;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.water += num_pieces; if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.water += num_pieces;
} }
@@ -2141,23 +2239,31 @@ extern const byte _tunnel_turnaround_pre_visibility_frame[DIAGDIR_END] = {31, 27
static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y) static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y)
{ {
/* Direction into the wormhole */
const DiagDirection dir = GetTunnelBridgeDirection(tile);
/* New position of the vehicle on the tile */
int pos = (DiagDirToAxis(dir) == AXIS_X ? x - (TileX(tile) * TILE_SIZE) : y - (TileY(tile) * TILE_SIZE));
/* Number of units moved by the vehicle since entering the tile */
int frame = (dir == DIAGDIR_NE || dir == DIAGDIR_NW) ? TILE_SIZE - 1 - pos : pos;
if (frame > (int) TILE_SIZE || frame < 0) return VETSB_CANNOT_ENTER;
if (frame == TILE_SIZE) {
TileIndexDiffC offset = TileIndexDiffCByDiagDir(ReverseDiagDir(dir));
x += offset.x;
y += offset.y;
}
int z = GetSlopePixelZ(x, y) - v->z_pos; int z = GetSlopePixelZ(x, y) - v->z_pos;
if (abs(z) > 2) return VETSB_CANNOT_ENTER; if (abs(z) > 2) return VETSB_CANNOT_ENTER;
/* Direction into the wormhole */
const DiagDirection dir = GetTunnelBridgeDirection(tile);
/* Direction of the vehicle */
const DiagDirection vdir = DirToDiagDir(v->direction);
/* New position of the vehicle on the tile */
byte pos = (DiagDirToAxis(vdir) == AXIS_X ? x : y) & TILE_UNIT_MASK;
/* Number of units moved by the vehicle since entering the tile */
byte frame = (vdir == DIAGDIR_NE || vdir == DIAGDIR_NW) ? TILE_SIZE - 1 - pos : pos;
if (IsTunnel(tile)) { if (IsTunnel(tile)) {
/* Direction of the vehicle */
const DiagDirection vdir = DirToDiagDir(v->direction);
if (v->type == VEH_TRAIN) { if (v->type == VEH_TRAIN) {
Train *t = Train::From(v); Train *t = Train::From(v);
if (t->track != TRACK_BIT_WORMHOLE && dir == vdir) { if (!(t->track & TRACK_BIT_WORMHOLE) && dir == vdir) {
if (t->IsFrontEngine() && frame == TUNNEL_SOUND_FRAME) { if (t->IsFrontEngine() && frame == TUNNEL_SOUND_FRAME) {
if (!PlayVehicleSound(t, VSE_TUNNEL) && RailVehInfo(t->engine_type)->engclass == 0) { if (!PlayVehicleSound(t, VSE_TUNNEL) && RailVehInfo(t->engine_type)->engclass == 0) {
SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v); SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
@@ -2173,7 +2279,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
} }
} }
if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) { if (dir == ReverseDiagDir(vdir) && frame == (int) (_tunnel_visibility_frame[dir] - 1) && z == 0) {
/* We're at the tunnel exit ?? */ /* We're at the tunnel exit ?? */
if (t->tile != tile && GetOtherTunnelEnd(t->tile) != tile) return VETSB_CONTINUE; // In chunnel if (t->tile != tile && GetOtherTunnelEnd(t->tile) != tile) return VETSB_CONTINUE; // In chunnel
t->tile = tile; t->tile = tile;
@@ -2203,7 +2309,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
} }
/* We're at the tunnel exit ?? */ /* We're at the tunnel exit ?? */
if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) { if (dir == ReverseDiagDir(vdir) && frame == (int) (_tunnel_visibility_frame[dir] - 1) && z == 0) {
if (rv->tile != tile && GetOtherTunnelEnd(rv->tile) != tile) return VETSB_CONTINUE; // In chunnel if (rv->tile != tile && GetOtherTunnelEnd(rv->tile) != tile) return VETSB_CONTINUE; // In chunnel
rv->tile = tile; rv->tile = tile;
rv->cur_image_valid_dir = INVALID_DIR; rv->cur_image_valid_dir = INVALID_DIR;
@@ -2224,11 +2330,12 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
first->cur_speed = min(first->cur_speed, spd); first->cur_speed = min(first->cur_speed, spd);
} }
if (vdir == dir) { const Direction bridge_dir = DiagDirToDir(dir);
/* Vehicle enters bridge at the last frame inside this tile. */ if (v->direction == bridge_dir) {
if (frame != TILE_SIZE - 1) return VETSB_CONTINUE;
switch (v->type) { switch (v->type) {
case VEH_TRAIN: { case VEH_TRAIN: {
/* Trains enter bridge at the first frame beyond this tile. */
if (frame != TILE_SIZE) return VETSB_CONTINUE;
Train *t = Train::From(v); Train *t = Train::From(v);
t->track = TRACK_BIT_WORMHOLE; t->track = TRACK_BIT_WORMHOLE;
ClrBit(t->gv_flags, GVF_GOINGUP_BIT); ClrBit(t->gv_flags, GVF_GOINGUP_BIT);
@@ -2237,6 +2344,8 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
} }
case VEH_ROAD: { case VEH_ROAD: {
/* Non-train vehicles enter bridge at the last frame inside this tile. */
if (frame != TILE_SIZE - 1) return VETSB_CONTINUE;
RoadVehicle *rv = RoadVehicle::From(v); RoadVehicle *rv = RoadVehicle::From(v);
if (IsRoadCustomBridgeHeadTile(tile)) { if (IsRoadCustomBridgeHeadTile(tile)) {
RoadBits bits = ROAD_NONE; RoadBits bits = ROAD_NONE;
@@ -2253,29 +2362,36 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
} }
case VEH_SHIP: case VEH_SHIP:
/* Non-train vehicles enter bridge at the last frame inside this tile. */
if (frame != TILE_SIZE - 1) return VETSB_CONTINUE;
Ship::From(v)->state = TRACK_BIT_WORMHOLE; Ship::From(v)->state = TRACK_BIT_WORMHOLE;
break; break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
return VETSB_ENTERED_WORMHOLE; return VETSB_ENTERED_WORMHOLE;
} else if (vdir == ReverseDiagDir(dir)) { } else if (v->direction == ReverseDir(bridge_dir)) {
v->tile = tile;
switch (v->type) { switch (v->type) {
case VEH_TRAIN: { case VEH_TRAIN: {
Train *t = Train::From(v); Train *t = Train::From(v);
if (t->track == TRACK_BIT_WORMHOLE) { if (t->track & TRACK_BIT_WORMHOLE) {
t->track = DiagDirToDiagTrackBits(vdir); if (IsRailCustomBridgeHeadTile(tile)) {
return VETSB_ENTERED_WORMHOLE;
} else {
v->tile = tile;
t->track = DiagDirToDiagTrackBits(DirToDiagDir(v->direction));
}
return VETSB_ENTERED_WORMHOLE; return VETSB_ENTERED_WORMHOLE;
} }
break; break;
} }
case VEH_ROAD: { case VEH_ROAD: {
v->tile = tile;
RoadVehicle *rv = RoadVehicle::From(v); RoadVehicle *rv = RoadVehicle::From(v);
if (rv->state == RVSB_WORMHOLE) { if (rv->state == RVSB_WORMHOLE) {
rv->cur_image_valid_dir = INVALID_DIR; rv->cur_image_valid_dir = INVALID_DIR;
rv->state = DiagDirToDiagTrackdir(vdir); rv->state = DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
rv->frame = 0; rv->frame = 0;
return VETSB_ENTERED_WORMHOLE; return VETSB_ENTERED_WORMHOLE;
} }
@@ -2283,9 +2399,10 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
} }
case VEH_SHIP: { case VEH_SHIP: {
v->tile = tile;
Ship *ship = Ship::From(v); Ship *ship = Ship::From(v);
if (ship->state == TRACK_BIT_WORMHOLE) { if (ship->state == TRACK_BIT_WORMHOLE) {
ship->state = DiagDirToDiagTrackBits(vdir); ship->state = DiagDirToDiagTrackBits(DirToDiagDir(v->direction));
return VETSB_ENTERED_WORMHOLE; return VETSB_ENTERED_WORMHOLE;
} }
break; break;
@@ -2293,6 +2410,29 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
default: NOT_REACHED(); default: NOT_REACHED();
} }
} else if (v->type == VEH_TRAIN && IsRailCustomBridgeHeadTile(tile)) {
DirDiff dir_diff = DirDifference(v->direction, bridge_dir);
DirDiff reverse_dir_diff = DirDifference(v->direction, ReverseDir(bridge_dir));
if (dir_diff == DIRDIFF_45RIGHT || dir_diff == DIRDIFF_45LEFT) {
if (frame != TILE_SIZE) return VETSB_CONTINUE;
Train *t = Train::From(v);
TileIndex other = GetOtherTunnelBridgeEnd(tile);
if (GetTunnelBridgeLength(tile, other) == 0 && IsRailCustomBridgeHead(other)) {
t->track |= TRACK_BIT_WORMHOLE;
} else {
t->direction = bridge_dir;
t->track = TRACK_BIT_WORMHOLE;
}
ClrBit(t->gv_flags, GVF_GOINGUP_BIT);
ClrBit(t->gv_flags, GVF_GOINGDOWN_BIT);
return VETSB_ENTERED_WORMHOLE;
}
if (reverse_dir_diff == DIRDIFF_45RIGHT || reverse_dir_diff == DIRDIFF_45LEFT) {
Train *t = Train::From(v);
if (t->track & TRACK_BIT_WORMHOLE) return VETSB_ENTERED_WORMHOLE;
}
} }
} }
return VETSB_CONTINUE; return VETSB_CONTINUE;
@@ -2318,6 +2458,16 @@ static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlag flag
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
} }
} }
if (IsRailCustomBridgeHeadTile(tile)) {
extern bool IsValidFlatRailBridgeHeadTrackBits(Slope normalised_slope, DiagDirection bridge_direction, TrackBits tracks);
/* Steep slopes behave the same as slopes with one corner raised. */
const Slope normalised_tileh_new = IsSteepSlope(tileh_new) ? SlopeWithOneCornerRaised(GetHighestSlopeCorner(tileh_new)) : tileh_new;
if (!IsValidFlatRailBridgeHeadTrackBits(normalised_tileh_new, direction, GetCustomBridgeHeadTrackBits(tile))) {
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
}
}
/* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */ /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
if ((direction == DIAGDIR_NW) || (direction == DIAGDIR_NE)) { if ((direction == DIAGDIR_NW) || (direction == DIAGDIR_NE)) {

View File

@@ -16,6 +16,9 @@
#include "tunnel_base.h" #include "tunnel_base.h"
#include "cmd_helper.h" #include "cmd_helper.h"
#include "signal_type.h" #include "signal_type.h"
#include "tunnel_map.h"
#include "track_func.h"
#include "core/bitmath_func.hpp"
/** /**
@@ -98,31 +101,34 @@ static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t)
return IsTunnel(t) ? GetOtherTunnelEnd(t) : GetOtherBridgeEnd(t); return IsTunnel(t) ? GetOtherTunnelEnd(t) : GetOtherBridgeEnd(t);
} }
/** /**
* Get the reservation state of the rail tunnel/bridge * Get the track bits for a rail tunnel/bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile * @param t the tile
* @return reservation state * @return reserved track bits
*/ */
static inline bool HasTunnelBridgeReservation(TileIndex t) static inline TrackBits GetTunnelBridgeTrackBits(TileIndex t)
{ {
assert(IsTileType(t, MP_TUNNELBRIDGE)); if (IsTunnel(t)) {
assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); return DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t));
return HasBit(_m[t].m5, 4); } else {
return GetCustomBridgeHeadTrackBits(t);
}
} }
/** /**
* Set the reservation state of the rail tunnel/bridge * Get the track bits for a rail tunnel/bridge onto/across the tunnel/bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile * @param t the tile
* @param b the reservation state * @return reserved track bits
*/ */
static inline void SetTunnelBridgeReservation(TileIndex t, bool b) static inline TrackBits GetAcrossTunnelBridgeTrackBits(TileIndex t)
{ {
assert(IsTileType(t, MP_TUNNELBRIDGE)); if (IsTunnel(t)) {
assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); return DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t));
SB(_m[t].m5, 4, 1, b ? 1 : 0); } else {
return GetCustomBridgeHeadTrackBits(t) & GetAcrossBridgePossibleTrackBits(t);
}
} }
/** /**
@@ -133,7 +139,122 @@ static inline void SetTunnelBridgeReservation(TileIndex t, bool b)
*/ */
static inline TrackBits GetTunnelBridgeReservationTrackBits(TileIndex t) static inline TrackBits GetTunnelBridgeReservationTrackBits(TileIndex t)
{ {
return HasTunnelBridgeReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE; if (IsTunnel(t)) {
return HasTunnelReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE;
} else {
return GetBridgeReservationTrackBits(t);
}
}
/**
* Get the reserved track bits for a rail tunnel/bridge onto/across the tunnel/bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile
* @return reserved track bits
*/
static inline TrackBits GetAcrossTunnelBridgeReservationTrackBits(TileIndex t)
{
if (IsTunnel(t)) {
return HasTunnelReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE;
} else {
return GetAcrossBridgeReservationTrackBits(t);
}
}
/**
* Get whether there are reserved track bits for a rail tunnel/bridge onto/across the tunnel/bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile
* @return whether there are reserved track bits
*/
static inline bool HasAcrossTunnelBridgeReservation(TileIndex t)
{
if (IsTunnel(t)) {
return HasTunnelReservation(t);
} else {
return GetAcrossBridgeReservationTrackBits(t) != TRACK_BIT_NONE;
}
}
/**
* Get the rail infrastructure count of a rail tunnel/bridge head tile (excluding the tunnel/bridge middle)
* @param bits the track bits
* @return rail infrastructure count
*/
static inline uint GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(TrackBits bits)
{
uint pieces = CountBits(bits);
if (TracksOverlap(bits)) pieces *= pieces;
return (TUNNELBRIDGE_TRACKBIT_FACTOR / 2) * (1 + pieces);
}
/**
* Get the rail infrastructure count of a rail tunnel/bridge head tile (excluding the tunnel/bridge middle)
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile
* @return rail infrastructure count
*/
static inline uint GetTunnelBridgeHeadOnlyRailInfrastructureCount(TileIndex t)
{
return IsBridge(t) ? GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(GetTunnelBridgeTrackBits(t)) : TUNNELBRIDGE_TRACKBIT_FACTOR;
}
/**
* Check if the given track direction on a rail bridge head tile enters the bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile
* @param td track direction
* @return reservation state
*/
static inline bool TrackdirEntersTunnelBridge(TileIndex t, Trackdir td)
{
assert(IsTileType(t, MP_TUNNELBRIDGE));
assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL);
return TrackdirToExitdir(td) == GetTunnelBridgeDirection(t);
}
/**
* Check if the given track direction on a rail bridge head tile exits the bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile
* @param td track direction
* @return reservation state
*/
static inline bool TrackdirExitsTunnelBridge(TileIndex t, Trackdir td)
{
assert(IsTileType(t, MP_TUNNELBRIDGE));
assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL);
return TrackdirToExitdir(ReverseTrackdir(td)) == GetTunnelBridgeDirection(t);
}
/**
* Check if the given track on a rail bridge head tile enters/exits the bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param tile the tile
* @param t track
* @return reservation state
*/
static inline bool IsTrackAcrossTunnelBridge(TileIndex tile, Track t)
{
assert(IsTileType(tile, MP_TUNNELBRIDGE));
assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
return DiagdirReachesTracks(ReverseDiagDir(GetTunnelBridgeDirection(tile))) & TrackToTrackBits(t);
}
/**
* Lift the reservation of a specific track on a tunnel or rail bridge head tile
* @pre IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL
* @param tile the tile
*/
static inline void UnreserveAcrossRailTunnelBridge(TileIndex tile)
{
assert(IsTileType(tile, MP_TUNNELBRIDGE));
assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
if (IsTunnel(tile)) {
SetTunnelReservation(tile, false);
} else {
UnreserveAcrossRailBridgeHead(tile);
}
} }
/** /**

View File

@@ -640,11 +640,23 @@ CommandCost EnsureNoRoadVehicleOnGround(TileIndex tile)
return CommandCost(); return CommandCost();
} }
struct GetVehicleTunnelBridgeProcData {
const Vehicle *v;
TileIndex t;
bool across_only;
};
/** Procedure called for every vehicle found in tunnel/bridge in the hash map */ /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data) static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
{ {
const GetVehicleTunnelBridgeProcData *info = (GetVehicleTunnelBridgeProcData*) data;
if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL; if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
if (v == (const Vehicle *)data) return NULL; if (v == info->v) return NULL;
if (v->type == VEH_TRAIN && info->across_only && IsBridge(info->t)) {
TrackBits vehicle_track = Train::From(v)->track;
if (!(vehicle_track & TRACK_BIT_WORMHOLE) && !(GetAcrossBridgePossibleTrackBits(info->t) & vehicle_track)) return NULL;
}
return v; return v;
} }
@@ -654,16 +666,24 @@ static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
* @param tile first end * @param tile first end
* @param endtile second end * @param endtile second end
* @param ignore Ignore this vehicle when searching * @param ignore Ignore this vehicle when searching
* @param across_only Only find vehicles which are passing across the bridge/tunnel or on connecting bridge head track pieces
* @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge). * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
*/ */
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore) CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore, bool across_only)
{ {
/* Value v is not safe in MP games, however, it is used to generate a local /* Value v is not safe in MP games, however, it is used to generate a local
* error message only (which may be different for different machines). * error message only (which may be different for different machines).
* Such a message does not affect MP synchronisation. * Such a message does not affect MP synchronisation.
*/ */
Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true); GetVehicleTunnelBridgeProcData data;
if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true); data.v = ignore;
data.t = tile;
data.across_only = across_only;
Vehicle *v = VehicleFromPos(tile, &data, &GetVehicleTunnelBridgeProc, true);
if (v == NULL) {
data.t = endtile;
v = VehicleFromPos(endtile, &data, &GetVehicleTunnelBridgeProc, true);
}
if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type); if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
return CommandCost(); return CommandCost();
@@ -676,6 +696,12 @@ static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
if (v->type != VEH_TRAIN) return NULL; if (v->type != VEH_TRAIN) return NULL;
Train *t = Train::From(v); Train *t = Train::From(v);
if (rail_bits & TRACK_BIT_WORMHOLE) {
if (t->track & TRACK_BIT_WORMHOLE) return v;
rail_bits &= ~TRACK_BIT_WORMHOLE;
} else if (t->track & TRACK_BIT_WORMHOLE) {
return NULL;
}
if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL; if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
return v; return v;

View File

@@ -61,7 +61,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi);
void ViewportMapDrawVehicles(DrawPixelInfo *dpi); void ViewportMapDrawVehicles(DrawPixelInfo *dpi);
void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical); void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical);
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore = NULL); CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore = NULL, bool across_only = false);
void DecreaseVehicleValue(Vehicle *v); void DecreaseVehicleValue(Vehicle *v);
void CheckVehicleBreakdown(Vehicle *v); void CheckVehicleBreakdown(Vehicle *v);

View File

@@ -103,6 +103,7 @@
#include "tunnelbridge_map.h" #include "tunnelbridge_map.h"
#include "gui.h" #include "gui.h"
#include "core/container_func.hpp" #include "core/container_func.hpp"
#include "tunnelbridge_map.h"
#include <map> #include <map>
#include <vector> #include <vector>
@@ -1085,9 +1086,23 @@ static HighLightStyle GetPartOfAutoLine(int px, int py, const Point &selstart, c
static void DrawAutorailSelection(const TileInfo *ti, HighLightStyle autorail_type, PaletteID pal = -1) static void DrawAutorailSelection(const TileInfo *ti, HighLightStyle autorail_type, PaletteID pal = -1)
{ {
SpriteID image; SpriteID image;
int offset;
FoundationPart foundation_part = FOUNDATION_PART_NORMAL; FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
int offset;
bool bridge_head_mode = false;
if (IsFlatRailBridgeHeadTile(ti->tile)) {
extern bool IsValidFlatRailBridgeHeadTrackBits(Slope normalised_slope, DiagDirection bridge_direction, TrackBits tracks);
offset = _AutorailTilehSprite[SLOPE_FLAT][autorail_type];
const Slope real_tileh = GetTileSlope(ti->tile, nullptr);
const Slope normalised_tileh = IsSteepSlope(real_tileh) ? SlopeWithOneCornerRaised(GetHighestSlopeCorner(real_tileh)) : real_tileh;
if (!IsValidFlatRailBridgeHeadTrackBits(normalised_tileh, GetTunnelBridgeDirection(ti->tile), TrackToTrackBits((Track) autorail_type))) {
offset = -offset;
}
if (!IsRailCustomBridgeHead(ti->tile)) {
bridge_head_mode = true;
}
} else {
Slope autorail_tileh = RemoveHalftileSlope(ti->tileh); Slope autorail_tileh = RemoveHalftileSlope(ti->tileh);
if (IsHalftileSlope(ti->tileh)) { if (IsHalftileSlope(ti->tileh)) {
static const HighLightStyle _lower_rail[CORNER_END] = { HT_DIR_VR, HT_DIR_HU, HT_DIR_VL, HT_DIR_HL }; // CORNER_W, CORNER_S, CORNER_E, CORNER_N static const HighLightStyle _lower_rail[CORNER_END] = { HT_DIR_VR, HT_DIR_HU, HT_DIR_VL, HT_DIR_HL }; // CORNER_W, CORNER_S, CORNER_E, CORNER_N
@@ -1098,9 +1113,10 @@ static void DrawAutorailSelection(const TileInfo *ti, HighLightStyle autorail_ty
autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner)); autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
} }
} }
assert(autorail_type < HT_DIR_END); assert(autorail_type < HT_DIR_END);
offset = _AutorailTilehSprite[autorail_tileh][autorail_type]; offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
}
if (offset >= 0) { if (offset >= 0) {
image = SPR_AUTORAIL_BASE + offset; image = SPR_AUTORAIL_BASE + offset;
if (pal == (PaletteID)-1) pal = _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE; if (pal == (PaletteID)-1) pal = _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE;
@@ -1109,8 +1125,12 @@ static void DrawAutorailSelection(const TileInfo *ti, HighLightStyle autorail_ty
if (pal == (PaletteID)-1) pal = PALETTE_SEL_TILE_RED; if (pal == (PaletteID)-1) pal = PALETTE_SEL_TILE_RED;
} }
if (bridge_head_mode) {
AddSortableSpriteToDraw(image, pal, ti->x, ti->y, 16, 16, 0, ti->z + 15);
} else {
DrawSelectionSprite(image, pal, ti, 7, foundation_part); DrawSelectionSprite(image, pal, ti, 7, foundation_part);
} }
}
/** /**
* Checks if the specified tile is selected and if so draws selection using correct selectionstyle. * Checks if the specified tile is selected and if so draws selection using correct selectionstyle.