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. - + @@ -135,7 +135,7 @@ the array so you can quickly see what is used and what is not. - + @@ -376,7 +376,7 @@ the array so you can quickly see what is used and what is not. - + 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(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; + 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); - 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 */ - - /* 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); + 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, sub); } - } else { - 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); } 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; diff --git a/src/rail_map.h b/src/rail_map.h index 05bac06e6c..27e7e36a2a 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -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 { diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 56315e8f50..0c78881291 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -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; diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 0458d391f3..26ce4cbbed 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -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: { diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index ff062b89f7..641f54fc1e 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -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 }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 12c6ddf7f3..6d14f455b3 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -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 diff --git a/src/tile_cmd.h b/src/tile_cmd.h index ce70232a02..0b02fca0c2 100644 --- a/src/tile_cmd.h +++ b/src/tile_cmd.h @@ -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) }; diff --git a/src/track_type.h b/src/track_type.h index 2982288bba..1852f053e9 100644 --- a/src/track_type.h +++ b/src/track_type.h @@ -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 diff --git a/src/train.h b/src/train.h index 41ef992ef5..485b533436 100644 --- a/src/train.h +++ b/src/train.h @@ -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; } /** diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 5ec8eb8d24..3ad9f4a649 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -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); } diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 060cd7606a..cbbd31d4b7 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -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 { diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index 6ca37fc6bb..2159bcc131 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -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 GetTunnelBridgeHeadOnlyRailInfrastructureCount(TileIndex t) +static inline uint GetTunnelBridgeHeadOnlyPrimaryRailInfrastructureCount(TileIndex t) { - return IsBridge(t) ? GetTunnelBridgeHeadOnlyRailInfrastructureCountFromTrackBits(GetTunnelBridgeTrackBits(t)) : TUNNELBRIDGE_TRACKBIT_FACTOR; + 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 GetTunnelBridgeHeadOnlySecondaryRailInfrastructureCount(TileIndex t) +{ + 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); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 1cd5f7b3c7..becfd95f84 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -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; }
    XXXX XXXX OOOO OOOO OOOO OOOOOOOO OOOO OOXX XXXXOOOO PPPP PPXX XXXX
    rail with signalsXXOX OOXX OOOO OOOO OOOO OOOO-inherit-OOOO OOOO OOXX XXXX
    2-inherit- OPXX XXPP -inherit--inherit-OOOO PPPP PPXX XXXX
    A