diff --git a/docs/landscape.html b/docs/landscape.html
index 135217ed3d..319036acde 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -249,6 +249,7 @@
+
m8 bits 11..6 = secondary track type (used for lower or right track when two parallel tracks on tile)
m4 bits 7..4: see signals
m4 bits 3..0: Ground type (values with fences are not valid for depots and checkpoints)
@@ -1729,6 +1730,7 @@
m7 bit 5 set = on snow or desert
m7 bits 7..6: present road types for road
m8 bits 5..0: track type for railway
+ m8 bits 11..6 = secondary track type for railway (used for bridge-bypassing track when two parallel tracks on custom bridge head)
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index 5fc33c1c15..ce7735dd10 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -109,7 +109,7 @@ the array so you can quickly see what is used and what is not.
XXXX XXXX |
OOOO OOOO |
OOOO OOOO |
- OOOO OOOO OOXX XXXX |
+ OOOO PPPP PPXX XXXX |
rail with signals |
@@ -135,7 +135,7 @@ the array so you can quickly see what is used and what is not.
XXOX OOXX |
OOOO OOOO |
OOOO OOOO |
- -inherit- |
+ OOOO OOOO OOXX XXXX |
2 |
@@ -376,7 +376,7 @@ the array so you can quickly see what is used and what is not.
-inherit- |
OPXX XXPP |
-inherit- |
- -inherit- |
+ OOOO PPPP PPXX XXXX |
A |
diff --git a/src/elrail.cpp b/src/elrail.cpp
index e75ead44ca..490f03dc69 100644
--- a/src/elrail.cpp
+++ b/src/elrail.cpp
@@ -79,46 +79,77 @@ static inline TLG GetTLG(TileIndex t)
return (TLG)((HasBit(TileX(t), 0) << 1) + HasBit(TileY(t), 0));
}
+struct DualTrackBits {
+ TrackBitsByte primary;
+ TrackBitsByte secondary;
+};
+
/**
* Finds which Electrified Rail Bits are present on a given tile.
* @param t tile to check
* @param override pointer to PCP override, can be NULL
* @return trackbits of tile if it is electrified
*/
-static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
+static DualTrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
{
+ DualTrackBits out;
+ out.primary = TRACK_BIT_NONE;
+ out.secondary = TRACK_BIT_NONE;
switch (GetTileType(t)) {
- case MP_RAILWAY:
- if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
+ case MP_RAILWAY: {
switch (GetRailTileType(t)) {
- case RAIL_TILE_NORMAL: case RAIL_TILE_SIGNALS:
- return GetTrackBits(t);
+ case RAIL_TILE_NORMAL: case RAIL_TILE_SIGNALS: {
+ RailType secondary = GetTileSecondaryRailTypeIfValid(t);
+ TrackBits present_bits = GetTrackBits(t);
+ if (secondary != INVALID_RAILTYPE) {
+ if (HasRailCatenary(GetSecondaryRailType(t))) {
+ out.secondary = present_bits & TRACK_BIT_RT_2;
+ }
+ present_bits &= TRACK_BIT_RT_1;
+ }
+ if (HasRailCatenary(GetRailType(t))) {
+ out.primary = present_bits;
+ }
+ break;
+ }
default:
- return TRACK_BIT_NONE;
+ break;
}
break;
+ }
- case MP_TUNNELBRIDGE:
- if (GetTunnelBridgeTransportType(t) != TRANSPORT_RAIL) return TRACK_BIT_NONE;
- if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
- if (override != NULL && (IsTunnel(t) || GetTunnelBridgeLength(t, GetOtherBridgeEnd(t)) > 0)) {
+ case MP_TUNNELBRIDGE: {
+ if (GetTunnelBridgeTransportType(t) != TRANSPORT_RAIL) break;
+ TrackBits primary_bits = GetPrimaryTunnelBridgeTrackBits(t);
+ TrackBits secondary_bits = GetSecondaryTunnelBridgeTrackBits(t);
+ if (HasRailCatenary(GetRailType(t))) {
+ out.primary = primary_bits;
+ }
+ if (secondary_bits && HasRailCatenary(GetSecondaryRailType(t))) {
+ out.secondary = secondary_bits;
+ }
+ if ((out.primary | out.secondary) && override != NULL && (IsTunnel(t) || GetTunnelBridgeLength(t, GetOtherBridgeEnd(t)) > 0)) {
*override = 1 << GetTunnelBridgeDirection(t);
}
- return GetTunnelBridgeTrackBits(t);
+ break;
+ }
case MP_ROAD:
- if (!IsLevelCrossing(t)) return TRACK_BIT_NONE;
- if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
- return GetCrossingRailBits(t);
+ if (!IsLevelCrossing(t)) break;
+ if (!HasRailCatenary(GetRailType(t))) break;
+ out.primary = GetCrossingRailBits(t);
+ break;
case MP_STATION:
- if (!HasStationRail(t)) return TRACK_BIT_NONE;
- if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
- return TrackToTrackBits(GetRailStationTrack(t));
+ if (!HasStationRail(t)) break;
+ if (!HasRailCatenary(GetRailType(t))) break;
+ out.primary = TrackToTrackBits(GetRailStationTrack(t));
+ break;
default:
- return TRACK_BIT_NONE;
+ break;
}
+ return out;
}
/**
@@ -135,7 +166,7 @@ static TrackBits MaskWireBits(TileIndex t, TrackBits tracks)
* as needing no catenary. We make an exception for blocked station tiles with a matching
* axis that still display wires to preserve visual continuity. */
TileIndex next_tile = TileAddByDiagDir(t, d);
- RailType rt = GetTileRailType(next_tile);
+ RailType rt = GetTileRailTypeByEntryDir(next_tile, d);
if (rt == INVALID_RAILTYPE || !HasRailCatenary(rt) ||
((TrackStatusToTrackBits(GetTileTrackStatus(next_tile, TRANSPORT_RAIL, 0)) & DiagdirReachesTracks(d)) == TRACK_BIT_NONE &&
(!HasStationTileRail(next_tile) || GetRailStationAxis(next_tile) != DiagDirToAxis(d) || !CanStationTileHaveWires(next_tile)))) {
@@ -173,9 +204,9 @@ static TrackBits MaskWireBits(TileIndex t, TrackBits tracks)
/**
* Get the base wire sprite to use.
*/
-static inline SpriteID GetWireBase(TileIndex tile, TileContext context = TCX_NORMAL)
+static inline SpriteID GetWireBase(TileIndex tile, RailType rt, TileContext context = TCX_NORMAL)
{
- const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
+ const RailtypeInfo *rti = GetRailTypeInfo(rt);
SpriteID wires = GetCustomRailSprite(rti, tile, RTSG_WIRES, context);
return wires == 0 ? SPR_WIRE_BASE : wires;
}
@@ -183,9 +214,9 @@ static inline SpriteID GetWireBase(TileIndex tile, TileContext context = TCX_NOR
/**
* Get the base pylon sprite to use.
*/
-static inline SpriteID GetPylonBase(TileIndex tile, TileContext context = TCX_NORMAL)
+static inline SpriteID GetPylonBase(TileIndex tile, RailType rt, TileContext context = TCX_NORMAL)
{
- const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
+ const RailtypeInfo *rti = GetRailTypeInfo(rt);
SpriteID pylons = GetCustomRailSprite(rti, tile, RTSG_PYLONS, context);
return pylons == 0 ? SPR_PYLON_BASE : pylons;
}
@@ -256,7 +287,7 @@ void DrawRailCatenaryOnTunnel(const TileInfo *ti)
DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
- SpriteID wire_base = GetWireBase(ti->tile);
+ SpriteID wire_base = GetWireBase(ti->tile, GetRailType(ti->tile));
const SortableSpriteStruct *sss = &RailCatenarySpriteData_Tunnel[dir];
const int *BB_data = _tunnel_wire_BB[dir];
@@ -304,15 +335,52 @@ static void DrawRailCatenaryRailway(const TileInfo *ti)
* 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);
+ DualTrackBits home_track_config = GetRailTrackBitsUniversal(ti->tile, &OverridePCP);
+ trackconfig[TS_HOME] = home_track_config.primary | home_track_config.secondary;
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_normal = GetPylonBase(ti->tile);
- SpriteID pylon_halftile = (halftile_corner != CORNER_INVALID) ? GetPylonBase(ti->tile, TCX_UPPER_HALFTILE) : pylon_normal;
+ SpriteID pylon_normal = 0;
+ SpriteID pylon_halftile = 0;
+ SpriteID pylon_normal_secondary = 0;
+ SpriteID pylon_halftile_secondary = 0;
+
+ auto get_pylon_sprite = [&](DiagDirection edge, bool halftile) -> SpriteID {
+ static const uint edge_tracks[] = {
+ TRACK_BIT_UPPER | TRACK_BIT_RIGHT, // DIAGDIR_NE
+ TRACK_BIT_LOWER | TRACK_BIT_RIGHT, // DIAGDIR_SE
+ TRACK_BIT_LOWER | TRACK_BIT_LEFT, // DIAGDIR_SW
+ TRACK_BIT_UPPER | TRACK_BIT_LEFT, // DIAGDIR_NW
+ };
+ if (home_track_config.secondary && (home_track_config.secondary & edge_tracks[edge])) {
+ if (pylon_normal_secondary == 0) {
+ pylon_normal_secondary = GetPylonBase(ti->tile, GetSecondaryRailType(ti->tile));
+ }
+ if (halftile) {
+ if (pylon_halftile_secondary == 0) {
+ pylon_halftile_secondary = (halftile_corner != CORNER_INVALID) ? GetPylonBase(ti->tile, GetSecondaryRailType(ti->tile), TCX_UPPER_HALFTILE) : pylon_normal_secondary;
+ }
+ return pylon_halftile_secondary;
+ } else {
+ return pylon_normal_secondary;
+ }
+ } else {
+ if (pylon_normal == 0) {
+ pylon_normal = GetPylonBase(ti->tile, GetRailType(ti->tile));
+ }
+ if (halftile) {
+ if (pylon_halftile == 0) {
+ pylon_halftile = (halftile_corner != CORNER_INVALID) ? GetPylonBase(ti->tile, GetRailType(ti->tile), TCX_UPPER_HALFTILE) : pylon_normal;
+ }
+ return pylon_halftile;
+ } else {
+ return pylon_normal;
+ }
+ }
+ };
for (DiagDirection i = DIAGDIR_BEGIN; i < DIAGDIR_END; i++) {
static const uint edge_corners[] = {
@@ -321,14 +389,15 @@ static void DrawRailCatenaryRailway(const TileInfo *ti)
1 << CORNER_S | 1 << CORNER_W, // DIAGDIR_SW
1 << CORNER_N | 1 << CORNER_W, // DIAGDIR_NW
};
- SpriteID pylon_base = (halftile_corner != CORNER_INVALID && HasBit(edge_corners[i], halftile_corner)) ? pylon_halftile : pylon_normal;
+ SpriteID pylon_base = get_pylon_sprite(i, halftile_corner != CORNER_INVALID && HasBit(edge_corners[i], halftile_corner));
TileIndex neighbour = ti->tile + TileOffsByDiagDir(i);
int 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);
- trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL);
+ DualTrackBits neighbour_track_config = GetRailTrackBitsUniversal(neighbour, NULL);
+ trackconfig[TS_NEIGHBOUR] = neighbour_track_config.primary | neighbour_track_config.secondary;
wireconfig[TS_NEIGHBOUR] = MaskWireBits(neighbour, trackconfig[TS_NEIGHBOUR]);
if (IsTunnelTile(neighbour) && i != GetTunnelBridgeDirection(neighbour)) wireconfig[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] = TRACK_BIT_NONE;
@@ -382,7 +451,7 @@ static void DrawRailCatenaryRailway(const TileInfo *ti)
if (IsTileType(neighbour, MP_STATION) || IsTileType(neighbour, MP_ROAD)) tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
/* Read the foundations if they are present, and adjust the tileh */
- if (trackconfig[TS_NEIGHBOUR] != TRACK_BIT_NONE && IsTileType(neighbour, MP_RAILWAY) && HasRailCatenary(GetRailType(neighbour))) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
+ if (trackconfig[TS_NEIGHBOUR] != TRACK_BIT_NONE && IsTileType(neighbour, MP_RAILWAY) && HasRailCatenary(GetTileRailTypeByEntryDir(neighbour, i))) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
if (IsBridgeTile(neighbour)) {
foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], DiagDirToAxis(GetTunnelBridgeDirection(neighbour)));
}
@@ -458,8 +527,6 @@ static void DrawRailCatenaryRailway(const TileInfo *ti)
/* Don't draw a wire if the station tile does not want any */
if (IsRailStationTile(ti->tile) && !CanStationTileHaveWires(ti->tile)) return;
- SpriteID wire_normal = GetWireBase(ti->tile);
- SpriteID wire_halftile = (halftile_corner != CORNER_INVALID) ? GetWireBase(ti->tile, TCX_UPPER_HALFTILE) : wire_normal;
Track halftile_track;
switch (halftile_corner) {
case CORNER_W: halftile_track = TRACK_LEFT; break;
@@ -469,10 +536,43 @@ static void DrawRailCatenaryRailway(const TileInfo *ti)
default: halftile_track = INVALID_TRACK; break;
}
+ SpriteID wire_normal = 0;
+ SpriteID wire_halftile = 0;
+ SpriteID wire_normal_secondary = 0;
+ SpriteID wire_halftile_secondary = 0;
+
+ auto get_wire_sprite = [&](Track track, bool halftile) -> SpriteID {
+ if (home_track_config.secondary && HasTrack(home_track_config.secondary, track)) {
+ if (wire_normal_secondary == 0) {
+ wire_normal_secondary = GetWireBase(ti->tile, GetSecondaryRailType(ti->tile));
+ }
+ if (halftile) {
+ if (wire_halftile_secondary == 0) {
+ wire_halftile_secondary = (halftile_corner != CORNER_INVALID) ? GetWireBase(ti->tile, GetSecondaryRailType(ti->tile), TCX_UPPER_HALFTILE) : wire_normal_secondary;
+ }
+ return wire_halftile_secondary;
+ } else {
+ return wire_normal_secondary;
+ }
+ } else {
+ if (wire_normal == 0) {
+ wire_normal = GetWireBase(ti->tile, GetRailType(ti->tile));
+ }
+ if (halftile) {
+ if (wire_halftile == 0) {
+ wire_halftile = (halftile_corner != CORNER_INVALID) ? GetWireBase(ti->tile, GetRailType(ti->tile), TCX_UPPER_HALFTILE) : wire_normal;
+ }
+ return wire_halftile;
+ } else {
+ return wire_normal;
+ }
+ }
+ };
+
/* Drawing of pylons is finished, now draw the wires */
Track t;
FOR_EACH_SET_TRACK(t, wireconfig[TS_HOME]) {
- SpriteID wire_base = (t == halftile_track) ? wire_halftile : wire_normal;
+ SpriteID wire_base = get_wire_sprite(t, (t == halftile_track));
byte PCPconfig = HasBit(PCPstatus, PCPpositions[t][0]) +
(HasBit(PCPstatus, PCPpositions[t][1]) << 1);
@@ -527,14 +627,14 @@ void DrawRailCatenaryOnBridge(const TileInfo *ti)
height = GetBridgePixelHeight(end);
- SpriteID wire_base = GetWireBase(end, TCX_ON_BRIDGE);
+ SpriteID wire_base = GetWireBase(end, GetRailType(end), TCX_ON_BRIDGE);
AddSortableSpriteToDraw(wire_base + sss->image_offset, PAL_NONE, ti->x + sss->x_offset, ti->y + sss->y_offset,
sss->x_size, sss->y_size, sss->z_size, height + sss->z_offset,
IsTransparencySet(TO_CATENARY)
);
- SpriteID pylon_base = GetPylonBase(end, TCX_ON_BRIDGE);
+ SpriteID pylon_base = GetPylonBase(end, GetRailType(end), TCX_ON_BRIDGE);
/* Finished with wires, draw pylons
* every other tile needs a pylon on the northern end */
@@ -570,7 +670,7 @@ void DrawRailCatenary(const TileInfo *ti)
if (IsRailDepot(ti->tile)) {
const SortableSpriteStruct *sss = &RailCatenarySpriteData_Depot[GetRailDepotDirection(ti->tile)];
- SpriteID wire_base = GetWireBase(ti->tile);
+ SpriteID wire_base = GetWireBase(ti->tile, GetRailType(ti->tile));
/* This wire is not visible with the default depot sprites */
AddSortableSpriteToDraw(
diff --git a/src/elrail_func.h b/src/elrail_func.h
index 5cdae20c54..64316a4f80 100644
--- a/src/elrail_func.h
+++ b/src/elrail_func.h
@@ -29,9 +29,9 @@ static inline bool HasRailCatenary(RailType rt)
* Test if we should draw rail catenary
* @param rt Rail type to test
*/
-static inline bool HasRailCatenaryDrawn(RailType rt)
+static inline bool HasRailCatenaryDrawn(RailType rt, RailType secondary = INVALID_RAILTYPE)
{
- return HasRailCatenary(rt) && !IsInvisibilitySet(TO_CATENARY) && !_settings_game.vehicle.disable_elrails;
+ return !IsInvisibilitySet(TO_CATENARY) && !_settings_game.vehicle.disable_elrails && (HasRailCatenary(rt) || (secondary != INVALID_RAILTYPE && HasRailCatenary(secondary)));
}
void DrawRailCatenary(const TileInfo *ti);
diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp
index 1f70bf9f64..f2e9d3a700 100644
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -173,7 +173,9 @@ public:
td.airport_name = STR_NULL;
td.airport_tile_name = STR_NULL;
td.railtype = STR_NULL;
+ td.railtype2 = STR_NULL;
td.rail_speed = 0;
+ td.rail_speed2 = 0;
td.road_speed = 0;
td.grf = NULL;
@@ -295,6 +297,20 @@ public:
line_nr++;
}
+ /* 2nd Rail type name */
+ if (td.railtype2 != STR_NULL) {
+ SetDParam(0, td.railtype2);
+ GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_RAIL_TYPE, lastof(this->landinfo_data[line_nr]));
+ line_nr++;
+ }
+
+ /* 2nd Rail speed limit */
+ if (td.rail_speed2 != 0) {
+ SetDParam(0, td.rail_speed2);
+ GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT, lastof(this->landinfo_data[line_nr]));
+ line_nr++;
+ }
+
/* Road speed limit */
if (td.road_speed != 0) {
SetDParam(0, td.road_speed);
diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp
index f5ec7c86ef..63817a5e5d 100644
--- a/src/newgrf_engine.cpp
+++ b/src/newgrf_engine.cpp
@@ -609,7 +609,7 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object,
case 0x4A: {
if (v->type != VEH_TRAIN) return 0;
if (Train::From(v)->IsVirtual()) return 0x1FF;
- RailType rt = GetTileRailType(v->tile);
+ RailType rt = GetTileRailTypeByTrackBit(v->tile, Train::From(v)->track);
return (HasPowerOnRail(Train::From(v)->railtype, rt) ? 0x100 : 0) | GetReverseRailTypeTranslation(rt, object->ro.grffile);
}
@@ -707,7 +707,7 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object,
if (u->IsVirtual()) {
has_power = true;
} else {
- RailType railtype = GetRailType(v->tile);
+ RailType railtype = GetRailTypeByTrackBit(v->tile, t->track);
has_power = HasPowerOnRail(u->railtype, railtype);
}
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 8390b33577..90178ec06c 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -355,7 +355,7 @@ protected:
/* rail transport is possible only on compatible rail types */
if (IsRailTT()) {
- RailType rail_type = GetTileRailType(m_new_tile);
+ RailType rail_type = GetTileRailTypeByEntryDir(m_new_tile, m_exitdir);
if (!HasBit(m_railtypes, rail_type)) {
/* incompatible rail type */
m_err = EC_RAIL_TYPE;
@@ -481,7 +481,7 @@ public:
}
/* Check for speed limit imposed by railtype */
if (IsRailTT()) {
- uint16 rail_speed = GetRailTypeInfo(GetRailType(m_old_tile))->max_speed;
+ uint16 rail_speed = GetRailTypeInfo(GetRailTypeByTrack(m_old_tile, TrackdirToTrack(m_old_td)))->max_speed;
if (rail_speed > 0) max_speed = min(max_speed, rail_speed);
}
diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp
index c97a4172f2..40fe2740c0 100644
--- a/src/pathfinder/npf/npf.cpp
+++ b/src/pathfinder/npf/npf.cpp
@@ -788,7 +788,7 @@ static bool CanEnterTile(TileIndex tile, DiagDirection dir, AyStarUserData *user
/* check correct rail type (mono, maglev, etc) */
if (user->type == TRANSPORT_RAIL) {
- RailType rail_type = GetTileRailType(tile);
+ RailType rail_type = GetTileRailTypeByEntryDir(tile, dir);
if (!HasBit(user->railtypes, rail_type)) return false;
}
diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp
index 9f5c04e66a..4056154280 100644
--- a/src/pathfinder/yapf/yapf_costrail.hpp
+++ b/src/pathfinder/yapf/yapf_costrail.hpp
@@ -46,7 +46,7 @@ protected:
this->tile = tile;
this->td = td;
this->tile_type = GetTileType(tile);
- this->rail_type = GetTileRailType(tile);
+ this->rail_type = GetTileRailTypeByTrack(tile, TrackdirToTrack(td));
}
TILE(const TILE &src)
diff --git a/src/rail.cpp b/src/rail.cpp
index 1664f78e9a..5395633edc 100644
--- a/src/rail.cpp
+++ b/src/rail.cpp
@@ -179,6 +179,102 @@ RailType GetTileRailType(TileIndex tile)
return INVALID_RAILTYPE;
}
+/**
+ * Return the rail type of tile and track piece, or INVALID_RAILTYPE if this is no rail tile and return_invalid is true.
+ */
+RailType GenericGetRailTypeByTrack(TileIndex t, Track track, bool return_invalid)
+{
+ if (IsPlainRailTile(t)) {
+ TrackBits bits = GetTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return (TrackToTrackBits(track) & TRACK_BIT_RT_1) ? GetRailType(t) : GetSecondaryRailType(t);
+ } else {
+ return GetRailType(t);
+ }
+ } else if (IsRailTunnelBridgeTile(t)) {
+ TrackBits bits = GetTunnelBridgeTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return (TrackToTrackBits(track) & GetAcrossBridgePossibleTrackBits(t)) ? GetRailType(t) : GetSecondaryRailType(t);
+ } else {
+ return GetRailType(t);
+ }
+ } else {
+ return return_invalid ? GetTileRailType(t) : GetRailType(t);
+ }
+}
+
+/**
+ * Return the rail type of tile and track piece, or INVALID_RAILTYPE if this is no rail tile and return_invalid is true.
+ */
+RailType GenericGetRailTypeByTrackBit(TileIndex t, TrackBits tb, bool return_invalid)
+{
+ if (IsPlainRailTile(t)) {
+ TrackBits bits = GetTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return (tb & TRACK_BIT_RT_1) ? GetRailType(t) : GetSecondaryRailType(t);
+ } else {
+ return GetRailType(t);
+ }
+ } else if (IsRailTunnelBridgeTile(t)) {
+ TrackBits bits = GetTunnelBridgeTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return (tb & GetAcrossBridgePossibleTrackBits(t)) ? GetRailType(t) : GetSecondaryRailType(t);
+ } else {
+ return GetRailType(t);
+ }
+ } else {
+ return return_invalid ? GetTileRailType(t) : GetRailType(t);
+ }
+}
+
+/**
+ * Return the rail type of tile and entrance direction, or INVALID_RAILTYPE if this is no rail tile and return_invalid is true.
+ */
+RailType GenericGetRailTypeByEntryDir(TileIndex t, DiagDirection enterdir, bool return_invalid)
+{
+ if (IsPlainRailTile(t)) {
+ TrackBits bits = GetTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return (bits & DiagdirReachesTracks(enterdir) & TRACK_BIT_RT_1) ? GetRailType(t) : GetSecondaryRailType(t);
+ } else {
+ return GetRailType(t);
+ }
+ } else if (IsRailTunnelBridgeTile(t)) {
+ TrackBits bits = GetTunnelBridgeTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return (bits & DiagdirReachesTracks(enterdir) & GetAcrossBridgePossibleTrackBits(t)) ? GetRailType(t) : GetSecondaryRailType(t);
+ } else {
+ return GetRailType(t);
+ }
+ } else {
+ return return_invalid ? GetTileRailType(t) : GetRailType(t);
+ }
+}
+
+/**
+ * Return the secondary rail type of tile, or INVALID_RAILTYPE if this tile has no secondary rail type
+ */
+RailType GetTileSecondaryRailTypeIfValid(TileIndex t)
+{
+ if (IsPlainRailTile(t)) {
+ TrackBits bits = GetTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return GetSecondaryRailType(t);
+ } else {
+ return INVALID_RAILTYPE;
+ }
+ } else if (IsRailTunnelBridgeTile(t)) {
+ TrackBits bits = GetTunnelBridgeTrackBits(t);
+ if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
+ return GetSecondaryRailType(t);
+ } else {
+ return INVALID_RAILTYPE;
+ }
+ } else {
+ return INVALID_RAILTYPE;
+ }
+}
+
/**
* Finds out if a company has a certain railtype available
* @param company the company in question
diff --git a/src/rail.h b/src/rail.h
index dcd8daed54..ed86447cd2 100644
--- a/src/rail.h
+++ b/src/rail.h
@@ -296,7 +296,7 @@ public:
static inline const RailtypeInfo *GetRailTypeInfo(RailType railtype)
{
extern RailtypeInfo _railtypes[RAILTYPE_END];
- assert(railtype < RAILTYPE_END);
+ assert_msg(railtype < RAILTYPE_END, "%u", railtype);
return &_railtypes[railtype];
}
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 0f33669714..851068bdf5 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -242,10 +242,12 @@ static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
* Check that the new track bits may be built.
* @param tile %Tile to build on.
* @param to_build New track bits.
+ * @param railtype New rail type.
+ * @param disable_dual_rail_type Whether dual rail types are disabled.
* @param flags Flags of the operation.
* @return Succeeded or failed command.
*/
-static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build, uint flags)
+static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build, RailType railtype, bool disable_dual_rail_type, DoCommandFlag flags)
{
if (!IsPlainRail(tile)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
@@ -257,7 +259,25 @@ static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build, uin
/* Are we really building something new? */
if (current == future) {
/* Nothing new is being built */
- return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ if (IsCompatibleRail(GetTileRailTypeByTrackBit(tile, to_build), railtype)) {
+ return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ } else {
+ return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
+ }
+ }
+
+ /* These combinations are always allowed, unless disable_dual_rail_type is set */
+ if ((future == TRACK_BIT_HORZ || future == TRACK_BIT_VERT) && !disable_dual_rail_type) {
+ if (flags & DC_EXEC) {
+ if (to_build & TRACK_BIT_RT_1) {
+ RailType current_rt = GetRailType(tile);
+ SetRailType(tile, railtype);
+ SetSecondaryRailType(tile, current_rt);
+ } else {
+ SetSecondaryRailType(tile, railtype);
+ }
+ }
+ return CommandCost();
}
/* Let's see if we may build this */
@@ -268,8 +288,73 @@ static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build, uin
return_cmd_error((flags & DC_NO_RAIL_OVERLAP) ? STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION : STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
}
}
- /* Normally, we may overlap and any combination is valid */
- return CommandCost();
+
+ RailType rt = INVALID_RAILTYPE;
+ if (current == TRACK_BIT_HORZ || current == TRACK_BIT_VERT) {
+ RailType rt1 = GetRailType(tile);
+ if (!IsCompatibleRail(rt1, railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
+
+ RailType rt2 = GetSecondaryRailType(tile);
+ if (!IsCompatibleRail(rt2, railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
+
+ if (rt1 != rt2) {
+ /* Two different railtypes present */
+ if ((railtype == rt1 || HasPowerOnRail(rt1, railtype)) && (railtype == rt2 || HasPowerOnRail(rt2, railtype))) {
+ rt = railtype;
+ } else if ((railtype == rt1 || HasPowerOnRail(railtype, rt1)) && HasPowerOnRail(rt2, rt1)) {
+ rt = railtype = rt1;
+ } else if ((railtype == rt2 || HasPowerOnRail(railtype, rt2)) && HasPowerOnRail(rt1, rt2)) {
+ rt = railtype = rt2;
+ } else {
+ return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
+ }
+ } else if (railtype == rt1) {
+ /* Nothing to do */
+ rt = INVALID_RAILTYPE;
+ } else if (HasPowerOnRail(railtype, rt1)) {
+ /* Try to keep existing railtype */
+ railtype = rt1;
+ rt = INVALID_RAILTYPE;
+ } else if (HasPowerOnRail(rt1, railtype)) {
+ rt = railtype;
+ } else {
+ return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
+ }
+ } else {
+ rt = GetRailType(tile);
+
+ if (railtype == rt) {
+ /* Nothing to do */
+ rt = INVALID_RAILTYPE;
+ } else if (!IsCompatibleRail(rt, railtype)) {
+ return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
+ } else if (HasPowerOnRail(railtype, rt)) {
+ /* Try to keep existing railtype */
+ railtype = rt;
+ rt = INVALID_RAILTYPE;
+ } else if (HasPowerOnRail(rt, railtype)) {
+ rt = railtype;
+ } else {
+ return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
+ }
+ }
+
+ CommandCost ret;
+ if (rt != INVALID_RAILTYPE) {
+ ret = DoCommand(tile, tile, rt, flags, CMD_CONVERT_RAIL);
+ if (ret.Failed()) return ret;
+ }
+
+ if (HasSignalOnTrack(tile, TRACK_UPPER) || HasSignalOnTrack(tile, TRACK_LOWER)) {
+ return_cmd_error(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
+ }
+
+ if (flags & DC_EXEC) {
+ SetRailType(tile, railtype);
+ SetSecondaryRailType(tile, railtype);
+ }
+
+ return ret;
}
@@ -462,6 +547,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
RailType railtype = Extract(p1);
Track track = Extract