Initial implementation of two rail types per tile

pull/73/head
Jonathan G Rennison 6 years ago
parent 8128d027c0
commit 65b9a103ad

@ -249,6 +249,7 @@
</tr>
</table>
</li>
<li style="color: blue">m8 bits 11..6 = <a name="TrackType">secondary track type</a> (used for lower or right track when two parallel tracks on tile)</li>
<li>m4 bits 7..4: see signals</li>
<li>m4 bits 3..0: Ground type (values with fences are not valid for depots and checkpoints)
<table>
@ -1729,6 +1730,7 @@
<li>m7 bit 5 set = on snow or desert</li>
<li>m7 bits 7..6: present road types for road</li>
<li>m8 bits 5..0: <a href="#TrackType">track type</a> for railway</li>
<li style="color: blue">m8 bits 11..6 = <a name="TrackType">secondary track type</a> for railway (used for bridge-bypassing track when two parallel tracks on custom bridge head)</li>
</ul>
</td>
</tr>

@ -109,7 +109,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">XXXX XXXX</td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO OO</span>XX XXXX</td>
<td class="bits"><span class="free">OOOO</span> <span class="used_p">PPPP PP</span>XX XXXX</td>
</tr>
<tr>
<td class="caption">rail with signals</td>
@ -135,7 +135,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">XX<span class="free">O</span>X <span class="free">OO</span>XX</td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits">-inherit-</td>
<td class="bits"><span class="free">OOOO OOOO OO</span>XX XXXX</td>
</tr>
<tr>
<td rowspan=3>2</td>
@ -376,7 +376,7 @@ the array so you can quickly see what is used and what is not.
<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">-inherit-</td>
<td class="bits">-inherit-</td>
<td class="bits"><span class="free">OOOO</span> <span class="used_p">PPPP PP</span>XX XXXX</td>
</tr>
<tr>
<td rowspan=2>A</td>

@ -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(

@ -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);

@ -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);

@ -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);
}

@ -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);
}

@ -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;
}

@ -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)

@ -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

@ -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];
}

@ -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<RailType, 0, 6>(p1);
Track track = Extract<Track, 0, 3>(p2);
bool disable_custom_bridge_heads = HasBit(p2, 4);
bool disable_dual_rail_type = HasBit(p2, 5);
CommandCost cost(EXPENSES_CONSTRUCTION);
_rail_track_endtile = INVALID_TILE;
@ -478,10 +564,11 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (!IsPlainRail(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);
ret = CheckTrackCombination(tile, trackbit, flags);
if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
ret = CheckTrackCombination(tile, trackbit, railtype, disable_dual_rail_type, flags);
if (ret.Succeeded()) {
cost.AddCost(ret);
ret = EnsureNoTrainOnTrack(tile, track);
}
if (ret.Failed()) {
if (ret.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) _rail_track_endtile = tile;
return ret;
@ -491,31 +578,23 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (ret.Failed()) return ret;
cost.AddCost(ret);
/* If the rail types don't match, try to convert only if engines of
* the new rail type are not powered on the present rail type and engines of
* the present rail type are powered on the new rail type. */
if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
if (HasPowerOnRail(GetRailType(tile), railtype)) {
ret = DoCommand(tile, tile, railtype, flags, CMD_CONVERT_RAIL);
if (ret.Failed()) return ret;
cost.AddCost(ret);
} else {
return CMD_ERROR;
}
}
if (flags & DC_EXEC) {
SetRailGroundType(tile, RAIL_GROUND_BARREN);
TrackBits bits = GetTrackBits(tile);
SetTrackBits(tile, bits | trackbit);
/* Subtract old infrastructure count. */
uint pieces = CountBits(bits);
if (TracksOverlap(bits)) pieces *= pieces;
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
/* Add new infrastructure count. */
pieces = CountBits(bits | trackbit);
if (TracksOverlap(bits | trackbit)) pieces *= pieces;
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
TrackBits newbits = bits | trackbit;
SetTrackBits(tile, newbits);
if (newbits == TRACK_BIT_HORZ || newbits == TRACK_BIT_VERT) {
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetPlainRailParallelTrackRailTypeByTrackBit(tile, trackbit)]++;
} else {
/* Subtract old infrastructure count. */
uint pieces = CountBits(bits);
if (TracksOverlap(bits)) pieces *= pieces;
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
/* Add new infrastructure count. */
pieces = CountBits(newbits);
if (TracksOverlap(newbits)) pieces *= pieces;
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
}
DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
}
break;
@ -527,14 +606,22 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
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;
const bool secondary_piece = ((future == TRACK_BIT_HORZ || future == TRACK_BIT_VERT) && (future != existing));
if (!secondary_piece && !disable_dual_rail_type) {
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);
if (GetSecondaryTunnelBridgeTrackBits(tile) != TRACK_BIT_NONE) {
if (!IsCompatibleRail(GetSecondaryRailType(tile), railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetSecondaryRailType(tile))) return_cmd_error(STR_ERROR_CAN_T_CONVERT_RAIL);
}
}
if (existing == future) return_cmd_error(STR_ERROR_ALREADY_BUILT);
if (flags & DC_NO_RAIL_OVERLAP || IsTunnelBridgeWithSignalSimulation(tile)) {
@ -555,12 +642,18 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
}
const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
ret = TunnelBridgeIsFree(tile, other_end);
if (ret.Failed()) return ret;
if (!secondary_piece) {
ret = TunnelBridgeIsFree(tile, other_end);
if (ret.Failed()) return ret;
}
if (flags & DC_EXEC) {
SubtractRailTunnelBridgeInfrastructure(tile, other_end);
SetCustomBridgeHeadTrackBits(tile, future);
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(future) - GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(existing);
if (secondary_piece) {
SetSecondaryRailType(tile, railtype);
}
AddRailTunnelBridgeInfrastructure(tile, other_end);
DirtyCompanyInfrastructureWindows(_current_company);
}
@ -741,7 +834,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
if ((present & trackbit) == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true;
cost.AddCost(RailClearCost(GetRailType(tile)));
cost.AddCost(RailClearCost(GetTileRailTypeByTrackBit(tile, trackbit)));
/* Charge extra to remove signals on the track, if they are there */
if (HasSignalOnTrack(tile, track)) {
@ -757,15 +850,21 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
owner = GetTileOwner(tile);
/* Subtract old infrastructure count. */
uint pieces = CountBits(present);
if (TracksOverlap(present)) pieces *= pieces;
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
/* Add new infrastructure count. */
present ^= trackbit;
pieces = CountBits(present);
if (TracksOverlap(present)) pieces *= pieces;
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
if (present == TRACK_BIT_HORZ || present == TRACK_BIT_VERT) {
Company::Get(owner)->infrastructure.rail[GetTileRailTypeByTrackBit(tile, trackbit)]--;
present ^= trackbit;
SetRailType(tile, GetTileRailTypeByTrackBit(tile, present));
} else {
/* Subtract old infrastructure count. */
uint pieces = CountBits(present);
if (TracksOverlap(present)) pieces *= pieces;
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
/* Add new infrastructure count. */
present ^= trackbit;
pieces = CountBits(present);
if (TracksOverlap(present)) pieces *= pieces;
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
}
DirtyCompanyInfrastructureWindows(owner);
if (present == 0) {
@ -802,12 +901,17 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
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 (present == TRACK_BIT_HORZ || present == TRACK_BIT_VERT) {
ret = EnsureNoTrainOnTrack(tile, track);
} else {
ret = TunnelBridgeIsFree(tile, other_end);
}
if (ret.Failed()) return ret;
cost.AddCost(RailClearCost(GetRailType(tile)));
cost.AddCost(RailClearCost(GetTileRailTypeByTrackBit(tile, trackbit)));
if (flags & DC_EXEC) {
SubtractRailTunnelBridgeInfrastructure(tile, other_end);
owner = GetTileOwner(tile);
if (HasReservedTracks(tile, trackbit)) {
@ -816,7 +920,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
}
SetCustomBridgeHeadTrackBits(tile, future);
Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(present) - GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(future);
AddRailTunnelBridgeInfrastructure(tile, other_end);
DirtyCompanyInfrastructureWindows(_current_company);
}
@ -983,6 +1087,7 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint3
bool remove = HasBit(p2, 9);
bool fail_if_obstacle = HasBit(p2, 10);
bool no_custom_bridge_heads = HasBit(p2, 11);
bool no_dual_rail_type = HasBit(p2, 12);
_rail_track_endtile = INVALID_TILE;
@ -998,7 +1103,7 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint3
CommandCost last_error = CMD_ERROR;
for (;;) {
TileIndex last_endtile = _rail_track_endtile;
CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir) | (no_custom_bridge_heads ? 1 << 4 : 0), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir) | (no_custom_bridge_heads ? 1 << 4 : 0) | (no_dual_rail_type ? 1 << 5 : 0), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
if (ret.Failed()) {
last_error = ret;
@ -1924,10 +2029,13 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
/* Original railtype we are converting from */
RailType type = GetRailType(tile);
const RailType type = GetRailType(tile);
const RailType raw_secondary_type = GetTileSecondaryRailTypeIfValid(tile);
const RailType secondary_type = (raw_secondary_type == INVALID_RAILTYPE) ? type : raw_secondary_type;
/* Converting to the same type or converting 'hidden' elrail -> rail */
if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
if ((type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC))
&& (secondary_type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && secondary_type == RAILTYPE_ELECTRIC))) continue;
/* Trying to convert other's rail */
CommandCost ret = CheckTileOwnership(tile);
@ -1959,7 +2067,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
/* Vehicle on the tile when not converting Rail <-> ElRail
* Tunnels and bridges have special check later */
if (tt != MP_TUNNELBRIDGE) {
if (!IsCompatibleRail(type, totype)) {
if (!IsCompatibleRail(type, totype) || !IsCompatibleRail(secondary_type, totype)) {
CommandCost ret = IsPlainRailTile(tile) ? EnsureNoTrainOnTrackBits(tile, GetTrackBits(tile)) : EnsureNoVehicleOnGround(tile);
if (ret.Failed()) {
error = ret;
@ -1975,8 +2083,13 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
if (IsPlainRailTile(tile)) {
TrackBits bits = GetTrackBits(tile);
num_pieces = CountBits(bits);
if (TracksOverlap(bits)) num_pieces *= num_pieces;
if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
c->infrastructure.rail[secondary_type]--;
c->infrastructure.rail[totype]++;
} else {
num_pieces = CountBits(bits);
if (TracksOverlap(bits)) num_pieces *= num_pieces;
}
}
c->infrastructure.rail[type] -= num_pieces;
c->infrastructure.rail[totype] += num_pieces;
@ -1984,6 +2097,8 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
SetRailType(tile, totype);
if (IsPlainRailTile(tile)) SetSecondaryRailType(tile, totype);
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
/* update power of train on this tile */
FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc);
@ -2010,7 +2125,12 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
/* notify YAPF about the track layout change */
yapf_notify_track_change(tile, GetTrackBits(tile));
}
cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
if (raw_secondary_type != INVALID_RAILTYPE) {
cost.AddCost(RailConvertCost(type, totype));
cost.AddCost(RailConvertCost(raw_secondary_type, totype));
} else {
cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
}
break;
}
break;
@ -2029,7 +2149,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
/* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
if (!IsCompatibleRail(GetRailType(tile), totype)) {
if (!IsCompatibleRail(type, totype) || !IsCompatibleRail(secondary_type, totype)) {
CommandCost ret = TunnelBridgeIsFree(tile, endtile);
if (ret.Failed()) {
error = ret;
@ -2037,22 +2157,22 @@ 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));
uint num_primary_pieces = GetTunnelBridgeLength(tile, endtile) + CountBits(GetPrimaryTunnelBridgeTrackBits(tile)) + CountBits(GetPrimaryTunnelBridgeTrackBits(endtile));
cost.AddCost(num_primary_pieces * RailConvertCost(type, totype));
RailType end_secondary_type = GetTileSecondaryRailTypeIfValid(endtile);
if (raw_secondary_type != INVALID_RAILTYPE) cost.AddCost(RailConvertCost(raw_secondary_type, totype));
if (end_secondary_type != INVALID_RAILTYPE) cost.AddCost(RailConvertCost(end_secondary_type, totype));
if (flags & DC_EXEC) {
SubtractRailTunnelBridgeInfrastructure(tile, endtile);
find_train_reservations(tile, GetTunnelBridgeReservationTrackBits(tile));
find_train_reservations(endtile, GetTunnelBridgeReservationTrackBits(endtile));
/* Update the company infrastructure counters. */
uint num_infra_pieces = (middle_len* TUNNELBRIDGE_TRACKBIT_FACTOR) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(endtile);
Company *c = Company::Get(GetTileOwner(tile));
c->infrastructure.rail[GetRailType(tile)] -= num_infra_pieces;
c->infrastructure.rail[totype] += num_infra_pieces;
DirtyCompanyInfrastructureWindows(c->index);
SetRailType(tile, totype);
SetRailType(endtile, totype);
SetSecondaryRailType(tile, totype);
SetSecondaryRailType(endtile, totype);
FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc);
FindVehicleOnPos(endtile, &affected_trains, &UpdateTrainPowerProc);
@ -2067,9 +2187,10 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
MarkTileDirtyByTile(endtile, ZOOM_LVL_DRAW_MAP);
}
}
cost.AddCost(num_raw_pieces * RailConvertCost(type, totype));
AddRailTunnelBridgeInfrastructure(tile, endtile);
DirtyCompanyInfrastructureWindows(Company::Get(GetTileOwner(tile))->index);
}
break;
}
@ -2455,45 +2576,35 @@ static RailGroundType GetRailOrBridgeGroundType(TileInfo *ti) {
}
}
static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeInfo *rti)
static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeInfo *rti, RailGroundType rgt, bool is_bridge, Corner halftile_corner, Corner draw_half_tile)
{
const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE);
RailGroundType rgt = GetRailOrBridgeGroundType(ti);
Foundation f = is_bridge ? FOUNDATION_LEVELED : GetRailFoundation(ti->tileh, track);
Corner halftile_corner = CORNER_INVALID;
if (IsNonContinuousFoundation(f)) {
/* Save halftile corner */
halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f));
/* Draw lower part first */
track &= ~CornerToTrackBits(halftile_corner);
f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE);
}
DrawFoundation(ti, f);
/* DrawFoundation modifies ti */
const SubSprite *sub = NULL;
if (draw_half_tile != CORNER_INVALID) sub = &(_halftile_sub_sprite[draw_half_tile]);
if (halftile_corner != CORNER_INVALID) track &= ~CornerToTrackBits(halftile_corner);
/* Draw ground */
if (rgt == RAIL_GROUND_WATER) {
if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
/* three-corner-raised slope or steep slope with track on upper part */
DrawShoreTile(ti->tileh);
if (halftile_corner != CORNER_INVALID || draw_half_tile == CORNER_INVALID) {
/* Draw ground */
if (rgt == RAIL_GROUND_WATER) {
if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
/* three-corner-raised slope or steep slope with track on upper part */
DrawShoreTile(ti->tileh);
} else {
/* single-corner-raised slope with track on upper part */
DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
}
} else {
/* single-corner-raised slope with track on upper part */
DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
}
} else {
SpriteID image;
SpriteID image;
switch (rgt) {
case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break;
case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
default: image = SPR_FLAT_GRASS_TILE; break;
}
switch (rgt) {
case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break;
case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
default: image = SPR_FLAT_GRASS_TILE; break;
}
image += SlopeToSpriteOffset(ti->tileh);
image += SlopeToSpriteOffset(ti->tileh);
DrawGroundSprite(image, PAL_NONE);
DrawGroundSprite(image, PAL_NONE, sub);
}
}
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
@ -2568,7 +2679,7 @@ static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeIn
if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
}
if (IsValidCorner(halftile_corner)) {
if (IsValidCorner(halftile_corner) && (draw_half_tile == halftile_corner || draw_half_tile == CORNER_INVALID)) {
DrawFoundation(ti, HalftileFoundation(halftile_corner));
overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY, TCX_UPPER_HALFTILE);
ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND, TCX_UPPER_HALFTILE);
@ -2610,39 +2721,31 @@ static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeIn
* Draw ground sprite and track bits
* @param ti TileInfo
* @param track TrackBits to draw
* @param rt Rail type
* @param half_tile Half tile corner
*/
void DrawTrackBits(TileInfo *ti, TrackBits track)
void DrawTrackBits(TileInfo *ti, TrackBits track, RailType rt, RailGroundType rgt, bool is_bridge, Corner halftile_corner, Corner draw_half_tile)
{
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
const RailtypeInfo *rti = GetRailTypeInfo(rt);
if (rti->UsesOverlay()) {
DrawTrackBitsOverlay(ti, track, rti);
DrawTrackBitsOverlay(ti, track, rti, rgt, is_bridge, halftile_corner, draw_half_tile);
return;
}
const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE);
RailGroundType rgt = GetRailOrBridgeGroundType(ti);
Foundation f = is_bridge ? FOUNDATION_LEVELED : GetRailFoundation(ti->tileh, track);
Corner halftile_corner = CORNER_INVALID;
if (IsNonContinuousFoundation(f)) {
/* Save halftile corner */
halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f));
/* Draw lower part first */
track &= ~CornerToTrackBits(halftile_corner);
f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE);
}
DrawFoundation(ti, f);
/* DrawFoundation modifies ti */
SpriteID image;
PaletteID pal = PAL_NONE;
const SubSprite *sub = NULL;
bool junction = false;
if (halftile_corner != CORNER_INVALID) track &= ~CornerToTrackBits(halftile_corner);
if (draw_half_tile != CORNER_INVALID) sub = &(_halftile_sub_sprite[draw_half_tile]);
/* Select the sprite to use. */
if (track == 0) {
if (track == 0 && draw_half_tile != CORNER_INVALID) {
image = 0;
} else if (track == 0) {
/* Clear ground (only track on halftile foundation) */
if (rgt == RAIL_GROUND_WATER) {
if (IsSteepSlope(ti->tileh)) {
@ -2734,7 +2837,7 @@ void DrawTrackBits(TileInfo *ti, TrackBits track)
if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, NULL, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
}
if (IsValidCorner(halftile_corner)) {
if (IsValidCorner(halftile_corner) && (draw_half_tile == halftile_corner || draw_half_tile == CORNER_INVALID)) {
DrawFoundation(ti, HalftileFoundation(halftile_corner));
/* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
@ -2756,6 +2859,62 @@ void DrawTrackBits(TileInfo *ti, TrackBits track)
}
}
void DrawTrackBits(TileInfo *ti, TrackBits track)
{
const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE);
RailGroundType rgt = GetRailOrBridgeGroundType(ti);
Foundation f = is_bridge ? FOUNDATION_LEVELED : GetRailFoundation(ti->tileh, track);
Corner halftile_corner = CORNER_INVALID;
if (IsNonContinuousFoundation(f)) {
/* Save halftile corner */
halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f));
/* Draw lower part first */
f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE);
}
DrawFoundation(ti, f);
/* DrawFoundation modifies ti */
RailType rt1 = GetRailType(ti->tile);
RailType rt2 = GetTileSecondaryRailTypeIfValid(ti->tile);
if (rt2 == INVALID_RAILTYPE || rt1 == rt2) {
DrawTrackBits(ti, track, rt1, rgt, is_bridge, halftile_corner, CORNER_INVALID);
} else {
const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE);
TrackBits primary_track = track & (is_bridge ? GetAcrossBridgePossibleTrackBits(ti->tile) : TRACK_BIT_RT_1);
TrackBits secondary_track = track ^ primary_track;
assert((primary_track & (TRACK_BIT_HORZ | TRACK_BIT_VERT)) == primary_track);
assert((primary_track & (primary_track - 1)) == 0);
Track primary = FindFirstTrack(primary_track);
// TRACK_UPPER 2 -> CORNER_N 3
// TRACK_LOWER 3 -> CORNER_S 1
// TRACK_LEFT 4 -> CORNER_W 0
// TRACK_RIGHT 5 -> CORNER_E 2
Corner primary_corner = (Corner) ((0x870 >> (primary * 2)) & 3);
if (halftile_corner == primary_corner) {
std::swap(primary_track, secondary_track);
std::swap(rt1, rt2);
primary_corner = OppositeCorner(primary_corner);
}
if (halftile_corner == CORNER_INVALID) {
// draw ground sprite
SpriteID image;
switch (rgt) {
case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break;
case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
default: image = SPR_FLAT_GRASS_TILE; break;
}
image += SlopeToSpriteOffset(ti->tileh);
DrawGroundSprite(image, PAL_NONE);
}
DrawTrackBits(ti, primary_track, rt1, rgt, is_bridge, halftile_corner, primary_corner);
DrawTrackBits(ti, secondary_track, rt2, rgt, is_bridge, halftile_corner, OppositeCorner(primary_corner));
}
}
static void DrawSignals(TileIndex tile, TrackBits rails, const RailtypeInfo *rti)
{
#define MAYBE_DRAW_SIGNAL(x, y, z, t) if (IsSignalPresent(tile, x)) DrawSingleSignal(tile, rti, t, GetSingleSignalState(tile, x), y, z)
@ -2801,7 +2960,7 @@ static void DrawTile_Track(TileInfo *ti)
if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti);
if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
if (HasRailCatenaryDrawn(GetRailType(ti->tile), GetTileSecondaryRailTypeIfValid(ti->tile))) DrawRailCatenary(ti);
if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
} else {
@ -3178,9 +3337,16 @@ static bool ClickTile_Track(TileIndex tile)
static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
{
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
RailType rt = GetRailType(tile);
const RailtypeInfo *rti = GetRailTypeInfo(rt);
td->rail_speed = rti->max_speed;
td->railtype = rti->strings.name;
RailType secondary_rt = GetTileSecondaryRailTypeIfValid(tile);
if (secondary_rt != rt && secondary_rt != INVALID_RAILTYPE) {
const RailtypeInfo *secondary_rti = GetRailTypeInfo(secondary_rt);
td->rail_speed2 = secondary_rti->max_speed;
td->railtype2 = secondary_rti->strings.name;
}
td->owner[0] = GetTileOwner(tile);
switch (GetRailTileType(tile)) {
case RAIL_TILE_NORMAL:
@ -3298,8 +3464,14 @@ static void ChangeTileOwner_Track(TileIndex tile, Owner old_owner, Owner new_own
uint num_pieces = 1;
if (IsPlainRail(tile)) {
TrackBits bits = GetTrackBits(tile);
num_pieces = CountBits(bits);
if (TracksOverlap(bits)) num_pieces *= num_pieces;
if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
RailType secondary_rt = GetSecondaryRailType(tile);
Company::Get(old_owner)->infrastructure.rail[secondary_rt]--;
Company::Get(new_owner)->infrastructure.rail[secondary_rt]++;
} else {
num_pieces = CountBits(bits);
if (TracksOverlap(bits)) num_pieces *= num_pieces;
}
}
RailType rt = GetRailType(tile);
Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;

@ -129,6 +129,35 @@ static inline void SetRailType(TileIndex t, RailType r)
SB(_me[t].m8, 0, 6, r);
}
/**
* Gets the second rail type of the given tile
* @param t the tile to get the rail type from
* @return the rail type of the tile
*/
static inline RailType GetSecondaryRailType(TileIndex t)
{
return (RailType)GB(_me[t].m8, 6, 6);
}
/**
* Sets the second rail type of the given tile
* @param t the tile to set the rail type of
* @param r the new rail type for the tile
*/
static inline void SetSecondaryRailType(TileIndex t, RailType r)
{
SB(_me[t].m8, 6, 6, r);
}
/**
* Gets the second rail type of the given tile
* @param t the tile to get the rail type from
* @return the rail type of the tile
*/
static inline RailType GetPlainRailParallelTrackRailTypeByTrackBit(TileIndex t, TrackBits b)
{
return b & TRACK_BIT_RT_1 ? GetRailType(t) : GetSecondaryRailType(t);
}
/**
* Gets the track bits of the given tile
@ -513,6 +542,18 @@ static inline void SetRestrictedSignal(TileIndex tile, bool is_restricted)
RailType GetTileRailType(TileIndex tile);
RailType GenericGetRailTypeByTrack(TileIndex t, Track track, bool return_invalid);
RailType GenericGetRailTypeByTrackBit(TileIndex t, TrackBits track, bool return_invalid);
RailType GenericGetRailTypeByEntryDir(TileIndex t, DiagDirection enterdir, bool return_invalid);
RailType GetTileSecondaryRailTypeIfValid(TileIndex t);
static inline RailType GetTileRailTypeByTrack(TileIndex t, Track track) { return GenericGetRailTypeByTrack(t, track, true); }
static inline RailType GetTileRailTypeByTrackBit(TileIndex t, TrackBits track) { return GenericGetRailTypeByTrackBit(t, track, true); }
static inline RailType GetTileRailTypeByEntryDir(TileIndex t, DiagDirection enterdir) { return GenericGetRailTypeByEntryDir(t, enterdir, true); }
static inline RailType GetRailTypeByTrack(TileIndex t, Track track) { return GenericGetRailTypeByTrack(t, track, false); }
static inline RailType GetRailTypeByTrackBit(TileIndex t, TrackBits track) { return GenericGetRailTypeByTrackBit(t, track, false); }
static inline RailType GetRailTypeByEntryDir(TileIndex t, DiagDirection enterdir) { return GenericGetRailTypeByEntryDir(t, enterdir, false); }
/** The ground 'under' the rail */
enum RailGroundType {

@ -1410,6 +1410,15 @@ bool AfterLoadGame()
}
}
if (SlXvIsFeatureMissing(XSLFI_DUAL_RAIL_TYPES)) {
/* Introduced dual rail types. */
for (TileIndex t = 0; t < map_size; t++) {
if (IsPlainRailTile(t) || (IsRailTunnelBridgeTile(t) && IsBridge(t))) {
SetSecondaryRailType(t, GetRailType(t));
}
}
}
if (SlXvIsFeaturePresent(XSLFI_SIG_TUNNEL_BRIDGE, 1, 6)) {
/* m2 signal state bit allocation has shrunk */
for (TileIndex t = 0; t < map_size; t++) {
@ -3308,7 +3317,7 @@ bool AfterLoadGame()
t->grow_counter = TownTicksToGameTicks(t->grow_counter) + t->index % TOWN_GROWTH_TICKS;
}
}
if (IsSavegameVersionBefore(202)) {
/* Make sure added industry cargo slots are cleared */
Industry *i;

@ -116,8 +116,12 @@ void AfterLoadCompanyStats()
uint pieces = 1;
if (IsPlainRail(tile)) {
TrackBits bits = GetTrackBits(tile);
pieces = CountBits(bits);
if (TracksOverlap(bits)) pieces *= pieces;
if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
c->infrastructure.rail[GetSecondaryRailType(tile)]++;
} else {
pieces = CountBits(bits);
if (TracksOverlap(bits)) pieces *= pieces;
}
}
c->infrastructure.rail[GetRailType(tile)] += pieces;
@ -205,13 +209,7 @@ void AfterLoadCompanyStats()
switch (GetTunnelBridgeTransportType(tile)) {
case TRANSPORT_RAIL:
c = Company::GetIfValid(GetTileOwner(tile));
if (c != NULL) {
c->infrastructure.rail[GetRailType(tile)] += middle_len + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(other_end);
if (IsTunnelBridgeWithSignalSimulation(tile)) {
c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(tile, other_end);
}
}
AddRailTunnelBridgeInfrastructure(tile, other_end);
break;
case TRANSPORT_ROAD: {

@ -90,6 +90,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_ST_LAST_VEH_TYPE, XSCF_NULL, 1, 1, "station_last_veh_type", NULL, NULL, NULL },
{ XSLFI_SELL_AT_DEPOT_ORDER, XSCF_NULL, 1, 1, "sell_at_depot_order", NULL, NULL, NULL },
{ XSLFI_BUY_LAND_RATE_LIMIT, XSCF_NULL, 1, 1, "buy_land_rate_limit", NULL, NULL, NULL },
{ XSLFI_DUAL_RAIL_TYPES, XSCF_NULL, 1, 1, "dual_rail_types", NULL, NULL, NULL },
{ XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker
};

@ -64,6 +64,7 @@ enum SlXvFeatureIndex {
XSLFI_ST_LAST_VEH_TYPE, ///< Per-cargo station last vehicle type
XSLFI_SELL_AT_DEPOT_ORDER, ///< Sell vehicle on arrival at depot orders
XSLFI_BUY_LAND_RATE_LIMIT, ///< Buy land rate limit
XSLFI_DUAL_RAIL_TYPES, ///< Two rail-types per tile
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

@ -63,7 +63,9 @@ struct TileDesc {
const char *grf; ///< newGRF used for the tile contents
uint64 dparam[2]; ///< Parameters of the \a str string
StringID railtype; ///< Type of rail on the tile.
StringID railtype2; ///< Type of second rail on the tile.
uint16 rail_speed; ///< Speed limit of rail (bridges and track)
uint16 rail_speed2; ///< Speed limit of second rail (bridges and track)
uint16 road_speed; ///< Speed limit of road (bridges)
};

@ -54,6 +54,8 @@ enum TrackBits {
TRACK_BIT_3WAY_SW = TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_LEFT, ///< "Arrow" to the south-west
TRACK_BIT_3WAY_NW = TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT, ///< "Arrow" to the north-west
TRACK_BIT_ALL = TRACK_BIT_CROSS | TRACK_BIT_HORZ | TRACK_BIT_VERT, ///< All possible tracks
TRACK_BIT_RT_1 = TRACK_BIT_UPPER | TRACK_BIT_LEFT, ///< Track bits using the primary rail type, if the total track bits are TRACK_BIT_HORZ or TRACK_BIT_VERT
TRACK_BIT_RT_2 = TRACK_BIT_LOWER | TRACK_BIT_RIGHT, ///< Track bits using the secondary rail type, if the total track bits are TRACK_BIT_HORZ or TRACK_BIT_VERT
TRACK_BIT_MASK = 0x3FU, ///< Bitmask for the first 6 bits
TRACK_BIT_WORMHOLE = 0x40U, ///< Bitflag for a wormhole (used for tunnels)
TRACK_BIT_DEPOT = 0x80U, ///< Bitflag for a depot

@ -247,7 +247,7 @@ protected: // These functions should not be called outside acceleration code.
inline uint16 GetPower() const
{
/* Power is not added for articulated parts */
if (!this->IsArticulatedPart() && (this->IsVirtual() || HasPowerOnRail(this->railtype, GetRailType(this->tile)))) {
if (!this->IsArticulatedPart() && (this->IsVirtual() || HasPowerOnRail(this->railtype, GetRailTypeByTrackBit(this->tile, this->track)))) {
uint16 power = GetVehicleProperty(this, PROP_TRAIN_POWER, RailVehInfo(this->engine_type)->power);
/* Halve power for multiheaded parts */
if (this->IsMultiheaded()) power /= 2;
@ -264,7 +264,7 @@ protected: // These functions should not be called outside acceleration code.
inline uint16 GetPoweredPartPower(const Train *head) const
{
/* For powered wagons the engine defines the type of engine (i.e. railtype) */
if (HasBit(this->flags, VRF_POWEREDWAGON) && (head->IsVirtual() || HasPowerOnRail(head->railtype, GetRailType(this->tile)))) {
if (HasBit(this->flags, VRF_POWEREDWAGON) && (head->IsVirtual() || HasPowerOnRail(head->railtype, GetRailTypeByTrackBit(this->tile, this->track)))) {
return RailVehInfo(this->gcache.first_engine)->pow_wag_power;
}
@ -374,7 +374,7 @@ protected: // These functions should not be called outside acceleration code.
*/
inline uint16 GetMaxTrackSpeed() const
{
return GetRailTypeInfo(GetRailType(this->tile))->max_speed;
return GetRailTypeInfo(GetRailTypeByTrackBit(this->tile, this->track))->max_speed;
}
/**

@ -53,7 +53,7 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir,
static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse);
static bool TrainCheckIfLineEnds(Train *v, bool reverse = true);
static bool TrainCanLeaveTile(const Train *v);
static inline bool CheckCompatibleRail(const Train *v, TileIndex tile);
static inline bool CheckCompatibleRail(const Train *v, TileIndex tile, DiagDirection enterdir);
bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp.
static TileIndex TrainApproachingCrossingTile(const Train *v);
static void CheckIfTrainNeedsService(Train *v);
@ -469,7 +469,7 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, Train *v, int *st
bits &= ~TrackCrossesTracks(FindFirstTrack(front->track));
}
if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(front, tile) || IsRailDepotTile(tile) ||
if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(front, tile, dir) || IsRailDepotTile(tile) ||
(KillFirstBit(trackdirbits) == TRACKDIR_BIT_NONE && HasOnewaySignalBlockingTrackdir(tile, FindFirstTrackdir(trackdirbits)))) {
/* next tile is an effective dead end */
int current_platform_remaining = *station_ahead - TILE_SIZE + GetTileMarginInFrontOfTrain(v);
@ -3405,10 +3405,10 @@ static void TrainEnterStation(Train *v, StationID station)
}
/* Check if the vehicle is compatible with the specified tile */
static inline bool CheckCompatibleRail(const Train *v, TileIndex tile)
static inline bool CheckCompatibleRail(const Train *v, TileIndex tile, DiagDirection enterdir)
{
return IsInfraTileUsageAllowed(VEH_TRAIN, v->owner, tile) &&
(!v->IsFrontEngine() || HasBit(v->compatible_railtypes, GetRailType(tile)));
(!v->IsFrontEngine() || HasBit(v->compatible_railtypes, GetRailTypeByEntryDir(tile, enterdir)));
}
/** Data structure for storing engine speed changes of an acceleration type. */
@ -3937,7 +3937,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* Check if the new tile constrains tracks that are compatible
* with the current train, if not, bail out. */
if (!CheckCompatibleRail(v, gp.new_tile)) goto invalid_rail;
if (!CheckCompatibleRail(v, gp.new_tile, enterdir)) goto invalid_rail;
TrackBits chosen_track;
if (prev == NULL) {
@ -4109,7 +4109,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
v->tile = gp.new_tile;
if (GetTileRailType(gp.new_tile) != GetTileRailType(gp.old_tile)) {
if (GetTileRailTypeByTrackBit(gp.new_tile, chosen_track) != GetTileRailTypeByTrackBit(gp.old_tile, v->track)) {
v->First()->ConsistChanged(CCF_TRACK);
}
@ -4682,7 +4682,7 @@ static TileIndex TrainApproachingCrossingTile(const Train *v)
/* not a crossing || wrong axis || unusable rail (wrong type or owner) */
if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
!CheckCompatibleRail(v, tile)) {
!CheckCompatibleRail(v, tile, dir)) {
return INVALID_TILE;
}
@ -4730,7 +4730,7 @@ static bool TrainCheckIfLineEnds(Train *v, bool reverse)
}
/* no suitable trackbits at all || unusable rail (wrong type or owner) */
if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile)) {
if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile, dir)) {
return TrainApproachingLineEnd(v, false, reverse);
}

@ -571,10 +571,11 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
Company *c = Company::GetIfValid(company);
switch (transport_type) {
case TRANSPORT_RAIL:
if (is_upgrade) SubtractRailTunnelBridgeInfrastructure(tile_start, tile_end);
/* Add to company infrastructure count if required. */
MakeRailBridgeRamp(tile_start, owner, bridge_type, 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);
AddRailTunnelBridgeInfrastructure(tile_start, tile_end);
break;
case TRANSPORT_ROAD: {
@ -1145,7 +1146,9 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
if (rail) {
tile_tracks = GetCustomBridgeHeadTrackBits(tile);
endtile_tracks = GetCustomBridgeHeadTrackBits(endtile);
cost.AddCost(RailClearCost(GetRailType(tile)) * (CountBits(tile_tracks) + CountBits(endtile_tracks) - 2));
cost.AddCost(RailClearCost(GetRailType(tile)) * (CountBits(GetPrimaryTunnelBridgeTrackBits(tile)) + CountBits(GetPrimaryTunnelBridgeTrackBits(endtile)) - 2));
if (GetSecondaryTunnelBridgeTrackBits(tile)) cost.AddCost(RailClearCost(GetSecondaryRailType(tile)));
if (GetSecondaryTunnelBridgeTrackBits(endtile)) cost.AddCost(RailClearCost(GetSecondaryRailType(endtile)));
}
Money base_cost = (GetTunnelBridgeTransportType(tile) != TRANSPORT_WATER) ? _price[PR_CLEAR_BRIDGE] : _price[PR_CLEAR_AQUEDUCT];
@ -1178,12 +1181,7 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
/* Update company infrastructure counts. */
if (rail) {
if (Company::IsValidID(owner)) {
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= (middle_len * TUNNELBRIDGE_TRACKBIT_FACTOR) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(tile) + GetTunnelBridgeHeadOnlyRailInfrastructureCount(endtile);
if (IsTunnelBridgeWithSignalSimulation(tile)) { // handle tunnel/bridge signals.
Company::Get(GetTileOwner(tile))->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, endtile);
}
}
SubtractRailTunnelBridgeInfrastructure(tile, endtile);
} else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) {
SubtractRoadTunnelBridgeInfrastructure(tile, endtile);
} else { // Aqueduct
@ -1638,7 +1636,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
}
if (transport_type == TRANSPORT_RAIL && IsRailCustomBridgeHead(ti->tile)) {
DrawTrackBits(ti, GetCustomBridgeHeadTrackBits(ti->tile));
if (HasRailCatenaryDrawn(GetRailType(ti->tile))) {
if (HasRailCatenaryDrawn(GetRailType(ti->tile), GetTileSecondaryRailTypeIfValid(ti->tile))) {
DrawRailCatenary(ti);
}
@ -2103,9 +2101,16 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
}
if (tt == TRANSPORT_RAIL) {
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
RailType rt = GetRailType(tile);
const RailtypeInfo *rti = GetRailTypeInfo(rt);
td->rail_speed = rti->max_speed;
td->railtype = rti->strings.name;
RailType secondary_rt = GetTileSecondaryRailTypeIfValid(tile);
if (secondary_rt != rt && secondary_rt != INVALID_RAILTYPE) {
const RailtypeInfo *secondary_rti = GetRailTypeInfo(secondary_rt);
td->rail_speed2 = secondary_rti->max_speed;
td->railtype2 = secondary_rti->strings.name;
}
if (!IsTunnel(tile)) {
uint16 spd = GetBridgeSpec(GetBridgeType(tile))->speed;
@ -2238,6 +2243,56 @@ void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) {
UpdateRoadTunnelBridgeInfrastructure(begin, end, false);
}
static void UpdateRailTunnelBridgeInfrastructure(Company *c, TileIndex begin, TileIndex end, bool add) {
const uint middle_len = 2 * GetTunnelBridgeLength(begin, end) * TUNNELBRIDGE_TRACKBIT_FACTOR;
if (c != NULL) {
uint primary_count = middle_len + GetTunnelBridgeHeadOnlyPrimaryRailInfrastructureCount(begin) + GetTunnelBridgeHeadOnlyPrimaryRailInfrastructureCount(end);
if (add) {
c->infrastructure.rail[GetRailType(begin)] += primary_count;
} else {
c->infrastructure.rail[GetRailType(begin)] -= primary_count;
}
auto add_secondary_railtype = [&](TileIndex t) {
uint secondary_count = GetTunnelBridgeHeadOnlySecondaryRailInfrastructureCount(t);
if (secondary_count) {
if (add) {
c->infrastructure.rail[GetSecondaryRailType(t)] += secondary_count;
} else {
c->infrastructure.rail[GetSecondaryRailType(t)] -= secondary_count;
}
}
};
add_secondary_railtype(begin);
add_secondary_railtype(end);
if (IsTunnelBridgeWithSignalSimulation(begin)) {
if (add) {
c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(begin, end);
} else {
c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(begin, end);
}
}
}
}
void AddRailTunnelBridgeInfrastructure(Company *c, TileIndex begin, TileIndex end) {
UpdateRailTunnelBridgeInfrastructure(c, begin, end, true);
}
void SubtractRailTunnelBridgeInfrastructure(Company *c, TileIndex begin, TileIndex end) {
UpdateRailTunnelBridgeInfrastructure(c, begin, end, false);
}
void AddRailTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) {
UpdateRailTunnelBridgeInfrastructure(Company::GetIfValid(GetTileOwner(begin)), begin, end, true);
}
void SubtractRailTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) {
UpdateRailTunnelBridgeInfrastructure(Company::GetIfValid(GetTileOwner(begin)), begin, end, false);
}
static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner)
{
const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
@ -2266,13 +2321,12 @@ static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner
* No need to dirty windows here, we'll redraw the whole screen anyway. */
Company *old = Company::Get(old_owner);
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;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.rail[GetRailType(tile)] += num_pieces;
} else if (tt == TRANSPORT_WATER) {
if (tt == TRANSPORT_RAIL && tile < other_end) {
/* Only execute this for one of the two ends */
SubtractRailTunnelBridgeInfrastructure(old, tile, other_end);
if (new_owner != INVALID_OWNER) AddRailTunnelBridgeInfrastructure(Company::Get(new_owner), tile, other_end);
}
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;
@ -2280,12 +2334,6 @@ static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.water += num_pieces;
}
if (IsTunnelBridgeWithSignalSimulation(tile) && tile < other_end) {
uint num_sigs = GetTunnelBridgeSignalSimulationSignalCount(tile, other_end);
Company::Get(old_owner)->infrastructure.signal -= num_sigs;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.signal += num_sigs;
}
if (new_owner != INVALID_OWNER) {
SetTileOwner(tile, new_owner);
} else {

@ -116,6 +116,46 @@ static inline TrackBits GetTunnelBridgeTrackBits(TileIndex t)
}
}
/**
* Get the primary railtype track bits for a rail tunnel/bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile
* @return reserved track bits
*/
static inline TrackBits GetPrimaryTunnelBridgeTrackBits(TileIndex t)
{
if (IsTunnel(t)) {
return DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t));
} else {
TrackBits bits = GetCustomBridgeHeadTrackBits(t);
if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
return bits & GetAcrossBridgePossibleTrackBits(t);
} else {
return bits;
}
}
}
/**
* Get the secondary railtype track bits for a rail tunnel/bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
* @param t the tile
* @return reserved track bits
*/
static inline TrackBits GetSecondaryTunnelBridgeTrackBits(TileIndex t)
{
if (IsTunnel(t)) {
return TRACK_BIT_NONE;
} else {
TrackBits bits = GetCustomBridgeHeadTrackBits(t);
if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
return bits & (~GetAcrossBridgePossibleTrackBits(t));
} else {
return TRACK_BIT_NONE;
}
}
}
/**
* Get the track bits for a rail tunnel/bridge onto/across the tunnel/bridge
* @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
@ -189,14 +229,25 @@ static inline uint GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(T
}
/**
* Get the rail infrastructure count of a rail tunnel/bridge head tile (excluding the tunnel/bridge middle)
* Get the primary railtype 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 GetTunnelBridgeHeadOnlyPrimaryRailInfrastructureCount(TileIndex t)
{
return IsBridge(t) ? GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(GetPrimaryTunnelBridgeTrackBits(t)) : TUNNELBRIDGE_TRACKBIT_FACTOR;
}
/**
* Get the secondary railtype 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)
static inline uint GetTunnelBridgeHeadOnlySecondaryRailInfrastructureCount(TileIndex t)
{
return IsBridge(t) ? GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(GetTunnelBridgeTrackBits(t)) : TUNNELBRIDGE_TRACKBIT_FACTOR;
return IsBridge(t) ? GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(GetSecondaryTunnelBridgeTrackBits(t)) : 0;
}
/**
@ -429,6 +480,10 @@ static inline void SetTunnelBridgePBS(TileIndex t, bool is_pbs)
SB(_me[t].m6, 6, 1, is_pbs ? 1 : 0);
}
void AddRailTunnelBridgeInfrastructure(Company *c, TileIndex begin, TileIndex end);
void SubtractRailTunnelBridgeInfrastructure(Company *c, TileIndex begin, TileIndex end);
void AddRailTunnelBridgeInfrastructure(TileIndex begin, TileIndex end);
void SubtractRailTunnelBridgeInfrastructure(TileIndex begin, TileIndex end);
void AddRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end);
void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end);

@ -3336,7 +3336,7 @@ void Vehicle::ShowVisualEffect() const
IsDepotTile(v->tile) ||
IsTunnelTile(v->tile) ||
(v->type == VEH_TRAIN &&
!HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
!HasPowerOnRail(Train::From(v)->railtype, GetTileRailTypeByTrackBit(v->tile, Train::From(v)->track)))) {
continue;
}

Loading…
Cancel
Save