@@ -109,12 +109,57 @@ static TrackBits GetRailTrackBitsUnivers
default:
return TRACK_BIT_NONE;
}
/**
* Masks out track bits when neighbouring tiles are unelectrified.
*/
static TrackBits MaskWireBits(TileIndex t, TrackBits tracks)
{
if (!IsTileType(t, MP_RAILWAY) || !IsPlainRailTile(t)) return tracks;
TrackdirBits neighbour_tdb = TRACKDIR_BIT_NONE;
for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
/* If the neighbor tile is either not electrified or has no tracks that can be reached
* from this tile, mark all trackdirs that can be reached from the neighbour tile
* as needing no catenary. */
RailType rt = GetTileRailType(TileAddByDiagDir(t, d));
if (rt == INVALID_RAILTYPE || !HasCatenary(rt) || (TrackStatusToTrackBits(GetTileTrackStatus(TileAddByDiagDir(t, d), TRANSPORT_RAIL, 0)) & DiagdirReachesTracks(d)) == TRACK_BIT_NONE) {
neighbour_tdb |= DiagdirReachesTrackdirs(ReverseDiagDir(d));
/* If the tracks from either a diagonal crossing or don't overlap, both
* trackdirs have to be marked to mask the corresponding track bit. Else
* one marked trackdir is enough the mask the track bit. */
TrackBits mask;
if (tracks == TRACK_BIT_CROSS || !TracksOverlap(tracks)) {
/* If the tracks form either a diagonal crossing or don't overlap, both
* trackdirs have to be marked to mask the corresponding track bit. */
mask = ~(TrackBits)((neighbour_tdb & (neighbour_tdb >> 8)) & TRACK_BIT_MASK);
/* If that results in no masked tracks and it is not a diagonal crossing,
* require only one marked trackdir to mask. */
if (tracks != TRACK_BIT_CROSS && (mask & TRACK_BIT_MASK) == TRACK_BIT_MASK) mask = ~TrackdirBitsToTrackBits(neighbour_tdb);
} else {
/* Require only one marked trackdir to mask the track. */
mask = ~TrackdirBitsToTrackBits(neighbour_tdb);
/* If that results in an empty set, require both trackdirs for diagonal track. */
if ((tracks & mask) == TRACK_BIT_NONE) {
if ((neighbour_tdb & TRACKDIR_BIT_X_NE) == 0 || (neighbour_tdb & TRACKDIR_BIT_X_SW) == 0) mask |= TRACK_BIT_X;
if ((neighbour_tdb & TRACKDIR_BIT_Y_NW) == 0 || (neighbour_tdb & TRACKDIR_BIT_Y_SE) == 0) mask |= TRACK_BIT_Y;
/* If that still is not enough, require both trackdirs for any track. */
if ((tracks & mask) == TRACK_BIT_NONE) mask = ~(TrackBits)((neighbour_tdb & (neighbour_tdb >> 8)) & TRACK_BIT_MASK);
/* Mask the tracks only if at least one track bit would remain. */
return (tracks & mask) != TRACK_BIT_NONE ? tracks & mask : tracks;
* Get the base wire sprite to use.
static inline SpriteID GetWireBase(TileIndex tile)
return SPR_WIRE_BASE;
@@ -209,12 +254,13 @@ void DrawCatenaryOnTunnel(const TileInfo
static void DrawCatenaryRailway(const TileInfo *ti)
/* Pylons are placed on a tile edge, so we need to take into account
* the track configuration of 2 adjacent tiles. trackconfig[0] stores the
* current tile (home tile) while [1] holds the neighbour */
TrackBits trackconfig[TS_END];
TrackBits wireconfig[TS_END];
bool isflat[TS_END];
/* Note that ti->tileh has already been adjusted for Foundations */
Slope tileh[TS_END] = { ti->tileh, SLOPE_FLAT };
/* Half tile slopes coincide only with horizontal/vertical track.
* Faking a flat slope results in the correct sprites on positions. */
@@ -230,33 +276,35 @@ static void DrawCatenaryRailway(const Ti
* We don't draw a pylon:
* 1) INSIDE a tunnel (we wouldn't see it anyway)
* 2) on the "far" end of a bridge head (the one that connects to bridge middle),
* because that one is drawn on the bridge. Exception is for length 0 bridges
* which have no middle tiles */
trackconfig[TS_HOME] = GetRailTrackBitsUniversal(ti->tile, &OverridePCP);
wireconfig[TS_HOME] = MaskWireBits(ti->tile, trackconfig[TS_HOME]);
/* If a track bit is present that is not in the main direction, the track is level */
isflat[TS_HOME] = ((trackconfig[TS_HOME] & (TRACK_BIT_HORZ | TRACK_BIT_VERT)) != 0);
AdjustTileh(ti->tile, &tileh[TS_HOME]);
SpriteID pylon_base = GetPylonBase(ti->tile);
for (DiagDirection i = DIAGDIR_NE; i < DIAGDIR_END; i++) {
for (DiagDirection i = DIAGDIR_BEGIN; i < DIAGDIR_END; i++) {
TileIndex neighbour = ti->tile + TileOffsByDiagDir(i);
Foundation foundation = FOUNDATION_NONE;
byte elevation = GetPCPElevation(ti->tile, i);
/* Here's one of the main headaches. GetTileSlope does not correct for possibly
* existing foundataions, so we do have to do that manually later on.*/
tileh[TS_NEIGHBOUR] = GetTileSlope(neighbour, NULL);
trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL);
if (IsTunnelTile(neighbour) && i != GetTunnelBridgeDirection(neighbour)) trackconfig[TS_NEIGHBOUR] = TRACK_BIT_NONE;
wireconfig[TS_NEIGHBOUR] = MaskWireBits(neighbour, trackconfig[TS_NEIGHBOUR]);
if (IsTunnelTile(neighbour) && i != GetTunnelBridgeDirection(neighbour)) wireconfig[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] = TRACK_BIT_NONE;
/* If the neighboured tile does not smoothly connect to the current tile (because of a foundation),
* we have to draw all pillars on the current tile. */
if (elevation != GetPCPElevation(neighbour, ReverseDiagDir(i))) trackconfig[TS_NEIGHBOUR] = TRACK_BIT_NONE;
if (elevation != GetPCPElevation(neighbour, ReverseDiagDir(i))) wireconfig[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] = TRACK_BIT_NONE;
isflat[TS_NEIGHBOUR] = ((trackconfig[TS_NEIGHBOUR] & (TRACK_BIT_HORZ | TRACK_BIT_VERT)) != 0);
PPPpreferred[i] = 0xFF; // We start with preferring everything (end-of-line in any direction)
PPPallowed[i] = AllowedPPPonPCP[i];
@@ -269,18 +317,21 @@ static void DrawCatenaryRailway(const Ti
GetTunnelBridgeDirection(neighbour) == ReverseDiagDir(i)) {
continue;
/* We check whether the track in question (k) is present in the tile
* (TrackSourceTile) */
if (HasBit(trackconfig[TrackSourceTile[i][k]], TracksAtPCP[i][k])) {
DiagDirection PCPpos = i;
if (HasBit(wireconfig[TrackSourceTile[i][k]], TracksAtPCP[i][k])) {
/* track found, if track is in the neighbour tile, adjust the number
* of the PCP for preferred/allowed determination*/
DiagDirection PCPpos = (TrackSourceTile[i][k] == TS_HOME) ? i : ReverseDiagDir(i);
PCPpos = (TrackSourceTile[i][k] == TS_HOME) ? i : ReverseDiagDir(i);
SetBit(PCPstatus, i); // This PCP is in use
PPPpreferred[i] &= PreferredPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
PPPallowed[i] &= ~DisallowedPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
/* Deactivate all PPPs if PCP is not used */
@@ -339,13 +390,13 @@ static void DrawCatenaryRailway(const Ti
uint x = ti->x + x_pcp_offsets[i] + x_ppp_offsets[temp];
uint y = ti->y + y_pcp_offsets[i] + y_ppp_offsets[temp];
/* Don't build the pylon if it would be outside the tile */
if (!HasBit(OwnedPPPonPCP[i], temp)) {
/* We have a neighour that will draw it, bail out */
if (trackconfig[TS_NEIGHBOUR] != 0) break;
if (trackconfig[TS_NEIGHBOUR] != TRACK_BIT_NONE) break;
continue; /* No neighbour, go looking for a better position */
AddSortableSpriteToDraw(pylon_base + pylon_sprites[temp], PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE,
elevation, IsTransparencySet(TO_CATENARY), -1, -1);
@@ -366,13 +417,13 @@ static void DrawCatenaryRailway(const Ti
SpriteID wire_base = GetWireBase(ti->tile);
/* Drawing of pylons is finished, now draw the wires */
for (Track t = TRACK_BEGIN; t < TRACK_END; t++) {
if (HasBit(trackconfig[TS_HOME], t)) {
if (HasBit(wireconfig[TS_HOME], t)) {
byte PCPconfig = HasBit(PCPstatus, PCPpositions[t][0]) +
(HasBit(PCPstatus, PCPpositions[t][1]) << 1);
const SortableSpriteStruct *sss;
int tileh_selector = !(tileh[TS_HOME] % 3) * tileh[TS_HOME] / 3; /* tileh for the slopes, 0 otherwise */
Status change: