diff --git a/docs/landscape.html b/docs/landscape.html
index 0b9b112891..4d7b193ffb 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -1453,6 +1453,36 @@
- m1 bit 7: bit 4 of track type for railway (more rail types patch)
- m1 bits 4..0: owner
+ - m2 bits 7..0: custom road bridge heads (flat bridge heads only)
+ Road/tram bits below are XORed with the axial bridge direction road bits.
+ A non-custom bridge head configuration will have a value of 0.
+
+ - m2 bits 7..4: Road bits
+ Valid only if m7 bit 6 is set
+
+ - m2 bits 3..0: Tram bits
+ Valid only if m7 bit 7 is set
+
+
+
+
+ | bit 0: |
+ NW piece |
+
+
+ | bit 1: |
+ SW piece |
+
+
+ | bit 2: |
+ SE piece |
+
+
+ | bit 3: |
+ NE piece |
+
+
+
- m3 bits 7..4: owner of tram
- m3 bits 3..0: bits 3..0 of track type for railway
- m5 bit 4: pbs reservation state for railway
diff --git a/src/bridge_map.h b/src/bridge_map.h
index 99ff2440a0..9247ec1dfe 100644
--- a/src/bridge_map.h
+++ b/src/bridge_map.h
@@ -186,4 +186,72 @@ static inline void MakeAqueductBridgeRamp(TileIndex t, Owner o, DiagDirection d)
MakeBridgeRamp(t, o, 0, d, TRANSPORT_WATER, 0);
}
+/**
+ * Checks if this road bridge head is a custom bridge head
+ * @param t The tile to analyze
+ * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD
+ * @return true if it is a custom bridge head
+ */
+static inline bool IsRoadCustomBridgeHead(TileIndex t)
+{
+ assert(IsBridgeTile(t) && (TransportType)GB(_m[t].m5, 2, 2) == TRANSPORT_ROAD);
+ return GB(_m[t].m2, 0, 8) != 0;
+}
+
+/**
+ * Checks if this tile is a road bridge head with a custom bridge head
+ * @param t The tile to analyze
+ * @return true if it is a road bridge head with a custom bridge head
+ */
+static inline bool IsRoadCustomBridgeHeadTile(TileIndex t)
+{
+ return IsBridgeTile(t) && (TransportType)GB(_m[t].m5, 2, 2) == TRANSPORT_ROAD && IsRoadCustomBridgeHead(t);
+}
+
+/**
+ * Returns the road bits for a (possibly custom) road bridge head
+ * @param t The tile to analyze
+ * @param rt Road type.
+ * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD
+ * @return road bits for the bridge head
+ */
+static inline RoadBits GetCustomBridgeHeadRoadBits(TileIndex t, RoadType rt)
+{
+ assert(IsBridgeTile(t));
+ if (!HasTileRoadType(t, rt)) return (RoadBits) 0;
+ RoadBits bits = (GB(_m[t].m5, 0, 1) ? ROAD_Y : ROAD_X) ^ (RoadBits) GB(_m[t].m2, rt == ROADTYPE_TRAM ? 4 : 0, 4);
+ return bits;
+}
+
+/**
+ * Returns the road bits for a (possibly custom) road bridge head, for all road types
+ * @param t The tile to analyze
+ * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD
+ * @return road bits for the bridge head
+ */
+static inline RoadBits GetCustomBridgeHeadAllRoadBits(TileIndex t)
+{
+ return GetCustomBridgeHeadRoadBits(t, ROADTYPE_ROAD) | GetCustomBridgeHeadRoadBits(t, ROADTYPE_TRAM);
+}
+
+/**
+ * Sets the road bits for a (possibly custom) road bridge head
+ * @param t The tile to modify
+ * @param rt Road type.
+ * @param bits The road bits.
+ * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD
+ * @pre HasTileRoadType() must be set correctly before calling this
+ */
+static inline void SetCustomBridgeHeadRoadBits(TileIndex t, RoadType rt, RoadBits bits)
+{
+ assert(IsBridgeTile(t));
+ if (HasTileRoadType(t, rt)) {
+ assert(bits != ROAD_NONE);
+ SB(_m[t].m2, rt == ROADTYPE_TRAM ? 4 : 0, 4, bits ^ (GB(_m[t].m5, 0, 1) ? ROAD_Y : ROAD_X));
+ } else {
+ assert(bits == ROAD_NONE);
+ SB(_m[t].m2, rt == ROADTYPE_TRAM ? 4 : 0, 4, 0);
+ }
+}
+
#endif /* BRIDGE_MAP_H */
diff --git a/src/company_gui.cpp b/src/company_gui.cpp
index eb99cb9359..8a5f1b69d3 100644
--- a/src/company_gui.cpp
+++ b/src/company_gui.cpp
@@ -2478,6 +2478,15 @@ void DirtyCompanyInfrastructureWindows(CompanyID company)
SetWindowDirty(WC_COMPANY_INFRASTRUCTURE, company);
}
+/**
+ * Redraw all windows with all company infrastructure counts.
+ */
+void DirtyAllCompanyInfrastructureWindows()
+{
+ SetWindowClassesDirty(WC_COMPANY);
+ SetWindowClassesDirty(WC_COMPANY_INFRASTRUCTURE);
+}
+
struct BuyCompanyWindow : Window {
BuyCompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
{
diff --git a/src/company_gui.h b/src/company_gui.h
index f4964f3dc7..ed6326dfa4 100644
--- a/src/company_gui.h
+++ b/src/company_gui.h
@@ -25,5 +25,6 @@ void ShowCompany(CompanyID company);
void InvalidateCompanyWindows(const Company *c);
void DeleteCompanyWindows(CompanyID company);
void DirtyCompanyInfrastructureWindows(CompanyID company);
+void DirtyAllCompanyInfrastructureWindows();
#endif /* COMPANY_GUI_H */
diff --git a/src/landscape.cpp b/src/landscape.cpp
index 4247753dbf..17dfca85a0 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -30,6 +30,7 @@
#include "core/random_func.hpp"
#include "object_base.h"
#include "company_func.h"
+#include "tunnelbridge_map.h"
#include "pathfinder/npf/aystar.h"
#include "saveload/saveload.h"
#include
@@ -351,6 +352,8 @@ Slope GetFoundationSlope(TileIndex tile, int *z)
bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here)
{
+ if (IsRoadCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NW) return false;
+
int z;
int z_W_here = z_here;
@@ -368,6 +371,8 @@ bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here)
bool HasFoundationNE(TileIndex tile, Slope slope_here, uint z_here)
{
+ if (IsRoadCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NE) return false;
+
int z;
int z_E_here = z_here;
diff --git a/src/lang/english.txt b/src/lang/english.txt
index a93080c12a..0219e46345 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1832,6 +1832,9 @@ STR_CONFIG_SETTING_PATHFINDER_FOR_SHIPS_HELPTEXT :Path finder to
STR_CONFIG_SETTING_REVERSE_AT_SIGNALS :Automatic reversing at signals: {STRING2}
STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to reverse on a signal, if they waited there a long time
+STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS :Enable road custom bridge heads: {STRING2}
+STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS_HELPTEXT :Allow road bridges to have custom, non-straight flat entry/exit tiles
+
STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change setting value
STR_CONFIG_SETTING_ADJACENT_CROSSINGS :Close adjacent level crossings: {STRING2}
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 4d2912efb3..312f392086 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -102,8 +102,9 @@ struct CFollowTrackT
{
assert(IsTram()); // this function shouldn't be called in other cases
- if (IsNormalRoadTile(tile)) {
- RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM);
+ const bool is_bridge = IsRoadCustomBridgeHeadTile(tile);
+ if (is_bridge || IsNormalRoadTile(tile)) {
+ RoadBits rb = is_bridge ? GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM) : GetRoadBits(tile, ROADTYPE_TRAM);
switch (rb) {
case ROAD_NW: return DIAGDIR_NW;
case ROAD_SW: return DIAGDIR_SW;
@@ -216,7 +217,7 @@ protected:
m_tiles_skipped = GetTunnelBridgeLength(m_new_tile, m_old_tile);
return;
}
- assert(ReverseDiagDir(enterdir) == m_exitdir);
+ if (!IsRoadCustomBridgeHeadTile(m_old_tile)) assert(ReverseDiagDir(enterdir) == m_exitdir);
}
/* normal or station tile, do one step */
@@ -370,7 +371,12 @@ protected:
}
}
} else { // IsBridge(m_new_tile)
- if (!m_is_bridge) {
+ if (!m_is_bridge && IsRoadTT() && IsRoadCustomBridgeHeadTile(m_new_tile)) {
+ if (!(DiagDirToRoadBits(ReverseDiagDir(m_exitdir)) & GetCustomBridgeHeadRoadBits(m_new_tile, IsTram() ? ROADTYPE_TRAM : ROADTYPE_ROAD))) {
+ m_err = EC_NO_WAY;
+ return false;
+ }
+ } else if (!m_is_bridge) {
DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile);
if (ramp_enderdir != m_exitdir) {
m_err = EC_NO_WAY;
diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp
index 214fe2b502..72de255d08 100644
--- a/src/pathfinder/yapf/yapf_road.cpp
+++ b/src/pathfinder/yapf/yapf_road.cpp
@@ -112,7 +112,10 @@ public:
*/
inline bool PfCalcCost(Node &n, const TrackFollower *tf)
{
- int segment_cost = 0;
+ /* this is to handle the case where the starting tile is a junction custom bridge head,
+ * and we have advanced across the bridge in the initial step */
+ int segment_cost = tf->m_tiles_skipped * YAPF_TILE_LENGTH;
+
uint tiles = 0;
/* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */
TileIndex tile = n.m_key.m_tile;
@@ -135,6 +138,11 @@ public:
TrackFollower F(Yapf().GetVehicle());
if (!F.Follow(tile, trackdir)) break;
+ /* if we skipped some tunnel tiles, add their cost */
+ /* with custom bridge heads, this cost must be added before checking if the segment has ended */
+ segment_cost += F.m_tiles_skipped * YAPF_TILE_LENGTH;
+ tiles += F.m_tiles_skipped + 1;
+
/* if there are more trackdirs available & reachable, we are at the end of segment */
if (KillFirstBit(F.m_new_td_bits) != TRACKDIR_BIT_NONE) break;
@@ -143,10 +151,6 @@ public:
/* stop if RV is on simple loop with no junctions */
if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) return false;
- /* if we skipped some tunnel tiles, add their cost */
- segment_cost += F.m_tiles_skipped * YAPF_TILE_LENGTH;
- tiles += F.m_tiles_skipped + 1;
-
/* add hilly terrain penalty */
segment_cost += Yapf().SlopeCost(tile, F.m_new_tile, trackdir);
diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp
index 6cfeb3fd71..5a7b356d4a 100644
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -53,7 +53,7 @@ bool RoadVehiclesAreBuilt()
}
/** Invalid RoadBits on slopes. */
-static const RoadBits _invalid_tileh_slopes_road[2][15] = {
+extern const RoadBits _invalid_tileh_slopes_road[2][15] = {
/* The inverse of the mixable RoadBits on a leveled slope */
{
ROAD_NONE, // SLOPE_FLAT
@@ -213,36 +213,75 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
if (ret.Failed()) return ret;
if (!IsTileType(tile, MP_ROAD)) {
+ const bool custom_bridge_head = IsBridgeTile(tile) &&
+ HasBridgeFlatRamp(GetTileSlope(tile), DiagDirToAxis(GetTunnelBridgeDirection(tile))) &&
+ (_settings_game.construction.road_custom_bridge_heads || IsRoadCustomBridgeHead(tile));
+
/* If it's the last roadtype, just clear the whole tile */
- if (rts == RoadTypeToRoadTypes(rt)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
+ if (!custom_bridge_head && rts == RoadTypeToRoadTypes(rt)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
CommandCost cost(EXPENSES_CONSTRUCTION);
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
- /* Removing any roadbit in the bridge axis removes the roadtype (that's the behaviour remove-long-roads needs) */
- if ((AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) & pieces) == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
+ const RoadBits entrance_piece = DiagDirToRoadBits(GetTunnelBridgeDirection(tile));
+ const RoadBits axial_pieces = AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile)));
+ const RoadBits existing = IsBridge(tile) ? GetCustomBridgeHeadRoadBits(tile, rt) : axial_pieces;
+ const RoadType other_rt = (rt == ROADTYPE_ROAD) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
- TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
- /* Pay for *every* tile of the bridge or tunnel */
- uint len = GetTunnelBridgeLength(other_end, tile) + 2;
- cost.AddCost(len * _price[PR_CLEAR_ROAD]);
+ /* handle case where we would otherwise leave a single bridge entrance piece */
+ if ((existing & ~pieces) == entrance_piece) {
+ pieces |= entrance_piece;
+ }
+
+ /* Removing any roadbit in the bridge axis removes the roadtype (that's the behaviour remove-long-roads needs) */
+ if ((existing & pieces) == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
+
+ if (!custom_bridge_head) pieces |= axial_pieces;
+
+ const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
+ const uint middle_len = GetTunnelBridgeLength(other_end, tile);
+ uint pieces_count = 0;
+
+ const RoadBits other_end_existing = IsBridge(other_end) ? GetCustomBridgeHeadRoadBits(other_end, rt) : axial_pieces;
+ RoadBits other_end_pieces = ROAD_NONE;
+ if (pieces & entrance_piece) {
+ other_end_pieces |= MirrorRoadBits(entrance_piece);
+ /* if removing the other end entrance would only leave one piece, remove that too */
+ if (CountBits(other_end_existing & ~other_end_pieces) == 1) {
+ other_end_pieces |= other_end_existing;
+ }
+ pieces_count += middle_len * 2;
+ if ((GetCustomBridgeHeadRoadBits(tile, other_rt) & entrance_piece) == ROAD_NONE) {
+ /* can't leave no entrance pieces for any road type */
+ return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
+ }
+ }
+ pieces_count += CountBits(pieces & existing);
+ pieces_count += CountBits(other_end_pieces & other_end_existing);
+
+ cost.AddCost(pieces_count * _price[PR_CLEAR_ROAD]);
if (flags & DC_EXEC) {
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != NULL) {
- /* A full diagonal road tile has two road bits. */
- c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
- DirtyCompanyInfrastructureWindows(c->index);
+ SubtractRoadTunnelBridgeInfrastructure(tile, other_end);
+
+ const RoadBits bits = existing & ~pieces;
+ const RoadBits other_bits = other_end_existing & ~other_end_pieces;
+
+ if (bits == ROAD_NONE) SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt));
+ if (other_bits == ROAD_NONE) SetRoadTypes(other_end, GetRoadTypes(other_end) & ~RoadTypeToRoadTypes(rt));
+
+ if (IsBridge(tile)) {
+ SetCustomBridgeHeadRoadBits(tile, rt, bits);
+ SetCustomBridgeHeadRoadBits(other_end, rt, other_bits);
}
- SetRoadTypes(other_end, GetRoadTypes(other_end) & ~RoadTypeToRoadTypes(rt));
- SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt));
+ if (bits == ROAD_NONE && other_bits == ROAD_NONE) {
+ /* If the owner of the bridge sells all its road, also move the ownership
+ * to the owner of the other roadtype, unless the bridge owner is a town. */
- /* If the owner of the bridge sells all its road, also move the ownership
- * to the owner of the other roadtype, unless the bridge owner is a town. */
- RoadType other_rt = (rt == ROADTYPE_ROAD) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
- Owner other_owner = GetRoadOwner(tile, other_rt);
- if (!IsTileOwner(tile, other_owner) && !IsTileOwner(tile, OWNER_TOWN)) {
- SetTileOwner(tile, other_owner);
- SetTileOwner(other_end, other_owner);
+ Owner other_owner = GetRoadOwner(tile, other_rt);
+ if (!IsTileOwner(tile, other_owner) && !IsTileOwner(tile, OWNER_TOWN)) {
+ SetTileOwner(tile, other_owner);
+ SetTileOwner(other_end, other_owner);
+ }
}
/* Mark tiles dirty that have been repaved */
@@ -252,6 +291,9 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
MarkTileDirtyByTile(tile);
MarkTileDirtyByTile(other_end);
}
+
+ AddRoadTunnelBridgeInfrastructure(tile, other_end);
+ DirtyAllCompanyInfrastructureWindows();
}
} else {
assert(IsDriveThroughStopTile(tile));
@@ -670,12 +712,95 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
case MP_TUNNELBRIDGE: {
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) goto do_clear;
- /* Only allow building the outern roadbit, so building long roads stops at existing bridges */
- if (MirrorRoadBits(DiagDirToRoadBits(GetTunnelBridgeDirection(tile))) != pieces) goto do_clear;
- if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
- /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */
- CommandCost ret = TunnelBridgeIsFree(tile, GetOtherTunnelBridgeEnd(tile));
- if (ret.Failed()) return ret;
+
+ const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
+
+ if (IsBridge(tile)) {
+ const DiagDirection entrance_dir = GetTunnelBridgeDirection(tile);
+ const RoadBits entrance_piece = DiagDirToRoadBits(entrance_dir);
+ const RoadBits axial_pieces = AxisToRoadBits(DiagDirToAxis(entrance_dir));
+ existing = GetCustomBridgeHeadRoadBits(tile, rt);
+
+ if (!(_settings_game.construction.road_custom_bridge_heads && HasBridgeFlatRamp(tileh, DiagDirToAxis(entrance_dir)))) {
+ /* Ordinary bridge heads only */
+ /* Only allow building the outer roadbit, so building long roads stops at existing bridges */
+ if (MirrorRoadBits(entrance_piece) != pieces) goto do_clear;
+ pieces = axial_pieces;
+ }
+ if ((existing & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ if ((pieces & ~axial_pieces) && !_settings_game.construction.build_on_slopes) {
+ return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
+ }
+ if ((_invalid_tileh_slopes_road[0][tileh] & (pieces & ~entrance_piece)) != ROAD_NONE) {
+ return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
+ }
+
+ /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */
+ CommandCost ret = TunnelBridgeIsFree(tile, other_end);
+ if (ret.Failed()) return ret;
+
+
+ if ((existing | pieces) == entrance_piece) {
+ /*
+ * Don't allow the custom bridge head bits to be only the entrance piece
+ * as this makes road vehicles go haywire
+ */
+ pieces = axial_pieces;
+ }
+
+ RoadBits added_pieces = (existing | pieces) & ~existing;
+ uint added_pieces_count = CountBits(added_pieces);
+ RoadBits other_end_added_pieces = ROAD_NONE;
+ RoadBits other_end_existing = ROAD_NONE;
+
+ if (added_pieces & entrance_piece) {
+ /* adding road to whole bridge */
+
+ other_end_added_pieces = MirrorRoadBits(entrance_piece);
+ added_pieces_count += 1 + (GetTunnelBridgeLength(tile, other_end) * 2);
+
+ other_end_existing = GetCustomBridgeHeadRoadBits(other_end, rt);
+ assert((other_end_added_pieces & other_end_existing) == ROAD_NONE);
+
+ if (other_end_existing == ROAD_NONE) {
+ /*
+ * Don't allow the other end custom bridge head bits to be only the entrance piece
+ * as this makes road vehicles go haywire
+ */
+ other_end_added_pieces = axial_pieces;
+ added_pieces_count++;
+ }
+ }
+
+ cost.AddCost(added_pieces_count * _price[PR_BUILD_ROAD]);
+
+ if (flags & DC_EXEC) {
+ SubtractRoadTunnelBridgeInfrastructure(tile, other_end);
+
+ SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt));
+ if (!existing) SetRoadOwner(tile, rt, company);
+ SetCustomBridgeHeadRoadBits(tile, rt, existing | pieces);
+ if (other_end_added_pieces) {
+ SetRoadTypes(other_end, GetRoadTypes(other_end) | RoadTypeToRoadTypes(rt));
+ if (!other_end_existing) SetRoadOwner(other_end, rt, company);
+ SetCustomBridgeHeadRoadBits(other_end, rt, other_end_existing | other_end_added_pieces);
+ }
+
+ MarkBridgeDirty(tile);
+
+ AddRoadTunnelBridgeInfrastructure(tile, other_end);
+ DirtyAllCompanyInfrastructureWindows();
+ }
+
+ return cost;
+ } else { // IsTunnel(tile)
+ /* Only allow building the outer roadbit, so building long roads stops at existing bridges */
+ if (MirrorRoadBits(DiagDirToRoadBits(GetTunnelBridgeDirection(tile))) != pieces) goto do_clear;
+ if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */
+ CommandCost ret = TunnelBridgeIsFree(tile, other_end);
+ if (ret.Failed()) return ret;
+ }
break;
}
@@ -762,7 +887,7 @@ do_clear:;
/* Mark tiles dirty that have been repaved */
if (IsBridge(tile)) {
- MarkBridgeDirty(tile);
+ NOT_REACHED();
} else {
MarkTileDirtyByTile(other_end);
MarkTileDirtyByTile(tile);
@@ -864,8 +989,6 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost last_error = CMD_ERROR;
TileIndex tile = start_tile;
- bool had_bridge = false;
- bool had_tunnel = false;
bool had_success = false;
bool is_ai = HasBit(p2, 6);
@@ -896,27 +1019,21 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
}
} else {
had_success = true;
- /* Only pay for the upgrade on one side of the bridges and tunnels */
- if (IsTileType(tile, MP_TUNNELBRIDGE)) {
- if (IsBridge(tile)) {
- if (!had_bridge || GetTunnelBridgeDirection(tile) == dir) {
- cost.AddCost(ret);
- }
- had_bridge = true;
- } else { // IsTunnel(tile)
- if (!had_tunnel || GetTunnelBridgeDirection(tile) == dir) {
- cost.AddCost(ret);
- }
- had_tunnel = true;
- }
- } else {
- cost.AddCost(ret);
- }
+ cost.AddCost(ret);
+ }
+ /* Do not run into or across bridges/tunnels */
+ if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+ if (GetTunnelBridgeDirection(tile) == dir) break;
}
if (tile == end_tile) break;
tile += TileOffsByDiagDir(dir);
+
+ /* Do not run onto a bridge/tunnel tile from below/above */
+ if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+ if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) break;
+ }
}
return had_success ? cost : last_error;
@@ -1239,16 +1356,17 @@ static void DrawRoadDetail(SpriteID img, const TileInfo *ti, int dx, int dy, int
* Draw ground sprite and road pieces
* @param ti TileInfo
*/
-static void DrawRoadBits(TileInfo *ti)
+void DrawRoadBits(TileInfo *ti)
{
- RoadBits road = GetRoadBits(ti->tile, ROADTYPE_ROAD);
- RoadBits tram = GetRoadBits(ti->tile, ROADTYPE_TRAM);
+ const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE);
+ RoadBits road = is_bridge ? GetCustomBridgeHeadRoadBits(ti->tile, ROADTYPE_ROAD) : GetRoadBits(ti->tile, ROADTYPE_ROAD);
+ RoadBits tram = is_bridge ? GetCustomBridgeHeadRoadBits(ti->tile, ROADTYPE_TRAM) : GetRoadBits(ti->tile, ROADTYPE_TRAM);
SpriteID image = 0;
PaletteID pal = PAL_NONE;
if (ti->tileh != SLOPE_FLAT) {
- DrawFoundation(ti, GetRoadFoundation(ti->tileh, road | tram));
+ DrawFoundation(ti, is_bridge ? FOUNDATION_LEVELED : GetRoadFoundation(ti->tileh, road | tram));
/* DrawFoundation() modifies ti.
* Default sloped sprites.. */
@@ -1257,7 +1375,7 @@ static void DrawRoadBits(TileInfo *ti)
if (image == 0) image = _road_tile_sprites_1[road != ROAD_NONE ? road : tram];
- Roadside roadside = GetRoadside(ti->tile);
+ Roadside roadside = is_bridge ? ROADSIDE_PAVED : GetRoadside(ti->tile);
if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
image += 19;
@@ -1285,14 +1403,14 @@ static void DrawRoadBits(TileInfo *ti)
DrawGroundSprite(image, pal);
}
- if (road != ROAD_NONE) {
+ if (!is_bridge && road != ROAD_NONE) {
DisallowedRoadDirections drd = GetDisallowedRoadDirections(ti->tile);
if (drd != DRD_NONE) {
DrawGroundSpriteAt(SPR_ONEWAY_BASE + drd - 1 + ((road == ROAD_X) ? 0 : 3), PAL_NONE, 8, 8, GetPartialPixelZ(8, 8, ti->tileh));
}
}
- if (HasRoadWorks(ti->tile)) {
+ if (!is_bridge && HasRoadWorks(ti->tile)) {
/* Road works */
DrawGroundSprite((road | tram) & ROAD_X ? SPR_EXCAVATION_X : SPR_EXCAVATION_Y, PAL_NONE);
return;
@@ -1587,7 +1705,7 @@ static bool ClickTile_Road(TileIndex tile)
}
/* Converts RoadBits to TrackBits */
-static const TrackBits _road_trackbits[16] = {
+extern const TrackBits _road_trackbits[16] = {
TRACK_BIT_NONE, // ROAD_NONE
TRACK_BIT_NONE, // ROAD_NW
TRACK_BIT_NONE, // ROAD_SW
diff --git a/src/road_map.cpp b/src/road_map.cpp
index 4984117bab..7d6a541fa8 100644
--- a/src/road_map.cpp
+++ b/src/road_map.cpp
@@ -52,6 +52,7 @@ RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge
case MP_TUNNELBRIDGE:
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) return ROAD_NONE;
+ if (IsRoadCustomBridgeHeadTile(tile)) return GetCustomBridgeHeadRoadBits(tile, rt);
return straight_tunnel_bridge_entrance ?
AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) :
DiagDirToRoadBits(ReverseDiagDir(GetTunnelBridgeDirection(tile)));
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index abe0405e47..d42efc2376 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -1151,6 +1151,8 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
* by the previous vehicle in the chain when it gets to the right place. */
if (v->IsInDepot()) return true;
+ bool no_advance_tile = false;
+
if (v->state == RVSB_WORMHOLE) {
/* Vehicle is entering a depot or is on a bridge or in a tunnel */
GetNewVehiclePosResult gp = GetNewVehiclePos(v);
@@ -1164,19 +1166,24 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
}
if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
- /* Vehicle has just entered a bridge or tunnel */
+ if (IsRoadCustomBridgeHeadTile(gp.new_tile)) {
+ v->frame = 15;
+ no_advance_tile = true;
+ } else {
+ /* Vehicle has just entered a bridge or tunnel */
+ v->x_pos = gp.x;
+ v->y_pos = gp.y;
+ v->UpdatePosition();
+ v->UpdateInclination(true, true);
+ return true;
+ }
+ } else {
v->x_pos = gp.x;
v->y_pos = gp.y;
v->UpdatePosition();
- v->UpdateInclination(true, true);
+ if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true);
return true;
}
-
- v->x_pos = gp.x;
- v->y_pos = gp.y;
- v->UpdatePosition();
- if (v->IsDrawn()) v->Vehicle::UpdateViewport(true);
- return true;
}
/* Get move position data for next frame.
@@ -1187,12 +1194,16 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1];
if (rd.x & RDE_NEXT_TILE) {
- TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3));
+ TileIndex tile = v->tile;
+ if (!no_advance_tile) tile += TileOffsByDiagDir((DiagDirection)(rd.x & 3));
Trackdir dir;
if (v->IsFrontEngine()) {
/* If this is the front engine, look for the right path. */
dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3));
+ } else if (no_advance_tile) {
+ /* Follow previous vehicle out of custom bridge wormhole */
+ dir = (Trackdir) prev->state;
} else {
dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false);
}
@@ -1209,6 +1220,10 @@ again:
/* When turning around we can't be overtaking. */
v->overtaking = 0;
+ if (no_advance_tile) {
+ DEBUG(misc, 0, "Road vehicle attempted to turn around on a single road piece bridge head");
+ }
+
/* Turning around */
if (v->roadtype == ROADTYPE_TRAM) {
/* Determine the road bits the tram needs to be able to turn around
@@ -1221,9 +1236,16 @@ again:
case TRACKDIR_RVREV_SW: needed = ROAD_NE; break;
case TRACKDIR_RVREV_NW: needed = ROAD_SE; break;
}
- if ((v->Previous() != NULL && v->Previous()->tile == tile) ||
- (v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) &&
- (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE)) {
+ auto tile_turn_ok = [&]() -> bool {
+ if (IsNormalRoadTile(tile)) {
+ return !HasRoadWorks(tile) && (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE;
+ } else if (IsRoadCustomBridgeHeadTile(tile)) {
+ return (needed & GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE;
+ } else {
+ return false;
+ }
+ };
+ if ((v->Previous() != NULL && v->Previous()->tile == tile) || tile_turn_ok()) {
/*
* Taking the 'big' corner for trams only happens when:
* - The previous vehicle in this (articulated) tram chain is
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index a9313d79b6..61b932e3ff 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -3268,6 +3268,15 @@ bool AfterLoadGame()
}
}
+ if (SlXvIsFeatureMissing(XSLFI_CUSTOM_BRIDGE_HEADS)) {
+ /* ensure that previously unused custom bridge-head bits are cleared */
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD) {
+ SB(_m[t].m2, 0, 8, 0);
+ }
+ }
+ }
+
/* Station acceptance is some kind of cache */
if (IsSavegameVersionBefore(127)) {
Station *st;
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
index 7e4943f652..75d6c44d07 100644
--- a/src/saveload/company_sl.cpp
+++ b/src/saveload/company_sl.cpp
@@ -200,7 +200,8 @@ void AfterLoadCompanyStats()
if (tile < other_end) {
/* Count each tunnel/bridge TUNNELBRIDGE_TRACKBIT_FACTOR times to simulate
* the higher structural maintenance needs, and don't forget the end tiles. */
- uint len = (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ const uint middle_len = GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ const uint len = middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR);
switch (GetTunnelBridgeTransportType(tile)) {
case TRANSPORT_RAIL:
@@ -214,12 +215,7 @@ void AfterLoadCompanyStats()
break;
case TRANSPORT_ROAD: {
- /* Iterate all present road types as each can have a different owner. */
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != NULL) c->infrastructure.road[rt] += len * 2; // A full diagonal road has two road bits.
- }
+ AddRoadTunnelBridgeInfrastructure(tile, other_end);
break;
}
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index 0d2ab22d37..3480e15c5e 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -73,6 +73,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 2, 2, "cargo_type_orders", NULL, NULL, "ORDX,VEOX" },
{ XSLFI_EXTENDED_GAMELOG, XSCF_NULL, 1, 1, "extended_gamelog", NULL, NULL, NULL },
{ XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", NULL, NULL, NULL },
+ { XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 1, 1, "custom_bridge_heads", 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 938672918b..36a82104f6 100644
--- a/src/saveload/extended_ver_sl.h
+++ b/src/saveload/extended_ver_sl.h
@@ -47,6 +47,7 @@ enum SlXvFeatureIndex {
XSLFI_CARGO_TYPE_ORDERS, ///< Cargo-specific load/unload order flags
XSLFI_EXTENDED_GAMELOG, ///< Extended gamelog
XSLFI_STATION_CATCHMENT_INC, ///< Station catchment radius increase
+ XSLFI_CUSTOM_BRIDGE_HEADS, ///< Custom bridge heads
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/settings_gui.cpp b/src/settings_gui.cpp
index fcc7dc7eb6..bf6cb3706c 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1715,6 +1715,7 @@ static SettingsContainer &GetSettingsTree()
limitations->Add(new SettingEntry("vehicle.disable_elrails"));
limitations->Add(new SettingEntry("construction.maximum_signal_evaluations"));
limitations->Add(new SettingEntry("construction.enable_build_river"));
+ limitations->Add(new SettingEntry("construction.road_custom_bridge_heads"));
}
SettingsPage *disasters = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCIDENTS));
diff --git a/src/settings_type.h b/src/settings_type.h
index 3529fa16ce..316118898a 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -354,6 +354,7 @@ struct ConstructionSettings {
uint16 maximum_signal_evaluations; ///< maximum number of programmable signals which may be evaluated in one pass
byte simulated_wormhole_signals; ///< simulate signals in tunnel
bool enable_build_river; ///< enable building rivers in-game
+ uint8 road_custom_bridge_heads; ///< allow construction of road custom bridge heads
uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames?
uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed?
diff --git a/src/table/settings.ini b/src/table/settings.ini
index 446203d138..68d8ecfb28 100644
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -1413,6 +1413,15 @@ str = STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD
strhelp = STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT
cat = SC_BASIC
+[SDT_BOOL]
+base = GameSettings
+var = construction.road_custom_bridge_heads
+def = true
+cat = SC_BASIC
+str = STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS
+strhelp = STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS_HELPTEXT
+patxname = ""custom_bridge_heads.construction.road_custom_bridge_heads""
+
[SDT_BOOL]
base = GameSettings
var = station.adjacent_stations
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index a8207f0018..b852d43042 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -55,6 +55,8 @@ TileIndex _build_tunnel_endtile; ///< The end of a tunnel; as hidden return from
/** Z position of the bridge sprites relative to bridge height (downwards) */
static const int BRIDGE_Z_START = 3;
+extern void DrawRoadBits(TileInfo *ti);
+extern const RoadBits _invalid_tileh_slopes_road[2][15];
/**
* Mark bridge tiles dirty.
@@ -962,19 +964,11 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
}
}
} else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) {
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != NULL) {
- /* A full diagonal road tile has two road bits. */
- c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
- DirtyCompanyInfrastructureWindows(c->index);
- }
- }
+ SubtractRoadTunnelBridgeInfrastructure(tile, endtile);
} else { // Aqueduct
if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
}
- DirtyCompanyInfrastructureWindows(owner);
+ DirtyAllCompanyInfrastructureWindows();
if (IsTunnelBridgeSignalSimulationEntrance(tile)) ClearBridgeEntranceSimulatedSignals(tile);
if (IsTunnelBridgeSignalSimulationEntrance(endtile)) ClearBridgeEntranceSimulatedSignals(endtile);
@@ -1403,6 +1397,12 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
DrawBridgeMiddle(ti);
} else { // IsBridge(ti->tile)
+ if (transport_type == TRANSPORT_ROAD && IsRoadCustomBridgeHead(ti->tile)) {
+ DrawRoadBits(ti);
+ DrawBridgeMiddle(ti);
+ return;
+ }
+
const PalSpriteID *psid;
int base_offset;
bool ice = HasTunnelBridgeSnowOrDesert(ti->tile);
@@ -1627,11 +1627,19 @@ void DrawBridgeMiddle(const TileInfo *ti)
psid++;
if (transport_type == TRANSPORT_ROAD) {
- RoadTypes rts = GetRoadTypes(rampsouth);
+ const RoadTypes rts = GetRoadTypes(rampsouth);
- if (HasBit(rts, ROADTYPE_TRAM)) {
+ bool has_tram = HasBit(rts, ROADTYPE_TRAM);
+ bool has_road = HasBit(rts, ROADTYPE_ROAD);
+ if (IsRoadCustomBridgeHeadTile(rampsouth)) {
+ RoadBits entrance_bit = DiagDirToRoadBits(GetTunnelBridgeDirection(rampsouth));
+ has_tram = has_tram && (GetCustomBridgeHeadRoadBits(rampsouth, ROADTYPE_TRAM) & entrance_bit);
+ has_road = has_road && (GetCustomBridgeHeadRoadBits(rampsouth, ROADTYPE_ROAD) & entrance_bit);
+ }
+
+ if (has_tram) {
/* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */
- DrawBridgeTramBits(x, y, bridge_z, axis ^ 1, HasBit(rts, ROADTYPE_ROAD), false);
+ DrawBridgeTramBits(x, y, bridge_z, axis ^ 1, has_road, false);
} else {
EndSpriteCombine();
StartSpriteCombine();
@@ -1716,6 +1724,10 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y)
/* In the tunnel entrance? */
if (5 <= pos && pos <= 10) return z;
} else { // IsBridge(tile)
+ if (IsRoadCustomBridgeHeadTile(tile)) {
+ return z + TILE_HEIGHT;
+ }
+
DiagDirection dir = GetTunnelBridgeDirection(tile);
uint pos = (DiagDirToAxis(dir) == AXIS_X ? y : x);
@@ -1743,6 +1755,7 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y)
static Foundation GetFoundation_TunnelBridge(TileIndex tile, Slope tileh)
{
+ if (IsRoadCustomBridgeHeadTile(tile)) return FOUNDATION_LEVELED;
return IsTunnel(tile) ? FOUNDATION_NONE : GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(tile)));
}
@@ -1843,42 +1856,102 @@ static bool ClickTile_TunnelBridge(TileIndex tile)
return false;
}
+extern const TrackBits _road_trackbits[16];
+
static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
{
TransportType transport_type = GetTunnelBridgeTransportType(tile);
if (transport_type != mode || (transport_type == TRANSPORT_ROAD && (GetRoadTypes(tile) & sub_mode) == 0)) return 0;
DiagDirection dir = GetTunnelBridgeDirection(tile);
+
+ if (mode == TRANSPORT_ROAD && IsRoadCustomBridgeHeadTile(tile)) {
+ if (side != INVALID_DIAGDIR && side == dir) return 0;
+ TrackBits bits = TRACK_BIT_NONE;
+ if (sub_mode & ROADTYPES_TRAM) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM)];
+ if (sub_mode & ROADTYPES_ROAD) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_ROAD)];
+ return CombineTrackStatus(TrackBitsToTrackdirBits(bits), TRACKDIR_BIT_NONE);
+ }
if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0;
return CombineTrackStatus(TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE);
}
+static void UpdateRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end, bool add) {
+ /* A full diagonal road has two road bits. */
+ const uint middle_len = 2 * GetTunnelBridgeLength(begin, end) * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ const uint len = middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR);
+
+ /* Iterate all present road types as each can have a different owner. */
+ RoadType rt;
+ FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(begin)) {
+ Company * const c = Company::GetIfValid(GetRoadOwner(begin, rt));
+ if (c != NULL) {
+ uint infra = 0;
+ if (IsBridge(begin)) {
+ const RoadBits bits = GetCustomBridgeHeadRoadBits(begin, rt);
+ infra += CountBits(bits) * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ if (bits & DiagDirToRoadBits(GetTunnelBridgeDirection(begin))) {
+ infra += middle_len;
+ }
+ } else {
+ infra += len;
+ }
+ if (add) {
+ c->infrastructure.road[rt] += infra;
+ } else {
+ c->infrastructure.road[rt] -= infra;
+ }
+ }
+ }
+ FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(end)) {
+ Company * const c = Company::GetIfValid(GetRoadOwner(end, rt));
+ if (c != NULL) {
+ uint infra = 0;
+ if (IsBridge(end)) {
+ const RoadBits bits = GetCustomBridgeHeadRoadBits(end, rt);
+ infra += CountBits(bits) * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ }
+ if (add) {
+ c->infrastructure.road[rt] += infra;
+ } else {
+ c->infrastructure.road[rt] -= infra;
+ }
+ }
+ }
+}
+
+void AddRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) {
+ UpdateRoadTunnelBridgeInfrastructure(begin, end, true);
+}
+
+void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) {
+ UpdateRoadTunnelBridgeInfrastructure(begin, end, false);
+}
+
static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner)
{
- TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
+ const TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
/* Set number of pieces to zero if it's the southern tile as we
* don't want to update the infrastructure counts twice. */
- uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
+ const uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
+ const TransportType tt = GetTunnelBridgeTransportType(tile);
+
+ if (tt == TRANSPORT_ROAD) SubtractRoadTunnelBridgeInfrastructure(tile, other_end);
for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
/* Update all roadtypes, no matter if they are present */
if (GetRoadOwner(tile, rt) == old_owner) {
- if (HasBit(GetRoadTypes(tile), rt)) {
- /* Update company infrastructure counts. A full diagonal road tile has two road bits.
- * No need to dirty windows here, we'll redraw the whole screen anyway. */
- Company::Get(old_owner)->infrastructure.road[rt] -= num_pieces * 2;
- if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_pieces * 2;
- }
-
SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
}
}
+ if (tt == TRANSPORT_ROAD) AddRoadTunnelBridgeInfrastructure(tile, other_end);
+
if (!IsTileOwner(tile, old_owner)) return;
/* Update company infrastructure counts for rail and water as well.
* No need to dirty windows here, we'll redraw the whole screen anyway. */
- TransportType tt = GetTunnelBridgeTransportType(tile);
+
Company *old = Company::Get(old_owner);
if (tt == TRANSPORT_RAIL) {
old->infrastructure.rail[GetRailType(tile)] -= num_pieces;
@@ -2082,6 +2155,14 @@ static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlag flag
int z_old;
Slope tileh_old = GetTileSlope(tile, &z_old);
+ if (IsRoadCustomBridgeHeadTile(tile)) {
+ const RoadBits pieces = GetCustomBridgeHeadAllRoadBits(tile);
+ const RoadBits entrance_piece = DiagDirToRoadBits(direction);
+ if ((_invalid_tileh_slopes_road[0][tileh_new] & (pieces & ~entrance_piece)) != ROAD_NONE) {
+ return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
+ }
+ }
+
/* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
if ((direction == DIAGDIR_NW) || (direction == DIAGDIR_NE)) {
CheckBridgeSlopeSouth(axis, &tileh_old, &z_old);
diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h
index 7d35df7c83..150bf80a0b 100644
--- a/src/tunnelbridge_map.h
+++ b/src/tunnelbridge_map.h
@@ -260,4 +260,7 @@ static inline void SetTunnelBridgePBS(TileIndex t, bool is_pbs)
SB(_me[t].m6, 6, 1, is_pbs ? 1 : 0);
}
+void AddRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end);
+void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end);
+
#endif /* TUNNELBRIDGE_MAP_H */