diff --git a/docs/landscape.html b/docs/landscape.html
index bcad6e0f7e..0442a72ff1 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -124,10 +124,22 @@
- -
+
-
Bits 11..6: Secondary Railtype. Used for some tiles with rail (rail, tunnelbridge). Used for lower or right track when two parallel tracks on tile.
+ -
+
+ Bits 14..12: Road cached one way state. Used for all tiles with road (road, station, tunnelbridge).
+
+ 00 | road is not one-way |
+ 01 | road is one-way in 'A' direction |
+ 02 | road is one-way in 'B' direction |
+ 03 | road is disallowed in both directions |
+ 04 | road is a one-way side junction |
+ 05 | road is a one-way side junction, with no exit access |
+
+
m7:
@@ -578,6 +590,7 @@
m3 bits 7..4: owner of road type 1 (tram); OWNER_NONE (10) is stored as OWNER_TOWN (0F)
m4 bits 5..0: Roadtype
m7 bit 5 set = on snow or desert
+ m8 bits 14..12: Road cached one way state
m8 bits 11..6: Tramtype
m5 bits 7 clear: road or level-crossing
@@ -1022,6 +1035,7 @@
- m7 bits 4..0: owner of road (road stops)
- m7: animation frame (railway stations/waypoints, airports)
+ - m8 bits 14..12: Road cached one way state
- m8 bits 11..6: Tramtype
- m8 bits 5..0: track type for railway stations/waypoints
@@ -1812,6 +1826,7 @@
+ m8 bits 14..12: Road cached one way state
m8 bits 11..6: Tramtype
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 4fa63eb215..ab8f037ade 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -149,7 +149,7 @@ the array so you can quickly see what is used and what is not.
XXXX XXXX |
OOXX XOOO |
OOXO XXXX |
- OOOO XXXX XXOO OOOO |
+ OPPP XXXX XXOO OOOO |
level crossing |
@@ -162,7 +162,7 @@ the array so you can quickly see what is used and what is not.
XXXX OOPX |
OOXX XOOO |
OOXX XXXX |
- OOOO XXXX XXXX XXXX |
+ OPPP XXXX XXXX XXXX |
road depot |
@@ -243,7 +243,7 @@ the array so you can quickly see what is used and what is not.
~~~~ ~XXX |
OOXX XOOO |
OOOX XXXX |
- OOOO XXXX XXOO OOOO |
+ OPPP XXXX XXOO OOOO |
dock |
@@ -363,7 +363,7 @@ the array so you can quickly see what is used and what is not.
XOOX XXXX |
PPOO OOPP |
PPXX XXXX |
- OOOO XXXX XXXX XXXX |
+ OPPP XXXX XXXX XXXX |
bridge ramp |
@@ -376,7 +376,7 @@ the array so you can quickly see what is used and what is not.
-inherit- |
PPXX XXPP |
-inherit- |
- OOOO PPPP PPXX XXXX |
+ OPPP PPPP PPXX XXXX |
A |
diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp
index c89d872c07..bd15d5d9ef 100644
--- a/src/console_cmds.cpp
+++ b/src/console_cmds.cpp
@@ -2419,6 +2419,19 @@ DEF_CONSOLE_CMD(ConCSleep)
return true;
}
+DEF_CONSOLE_CMD(ConRecalculateRoadCachedOneWayStates)
+{
+ if (argc == 0) {
+ IConsoleHelp("Debug: Recalculate road cached one way states");
+ return true;
+ }
+
+ extern void RecalculateRoadCachedOneWayStates();
+ RecalculateRoadCachedOneWayStates();
+
+ return true;
+}
+
DEF_CONSOLE_CMD(ConDoDisaster)
{
if (argc == 0) {
@@ -2847,6 +2860,7 @@ void IConsoleStdLibRegister()
IConsoleCmdRegister("viewport_mark_dirty_st_overlay", ConViewportMarkStationOverlayDirty, nullptr, true);
IConsoleCmdRegister("gfx_debug", ConGfxDebug, nullptr, true);
IConsoleCmdRegister("csleep", ConCSleep, nullptr, true);
+ IConsoleCmdRegister("recalculate_road_cached_one_way_states", ConRecalculateRoadCachedOneWayStates, ConHookNoNetwork, true);
/* NewGRF development stuff */
IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool);
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index dd3fe0503e..972d63c9c0 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -739,6 +739,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
DirtyCompanyInfrastructureWindows(tram_owner);
}
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
}
break;
}
@@ -847,6 +848,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
DirtyCompanyInfrastructureWindows(owner);
MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypeRoad(tile), GetRoadTypeTram(tile), GetTownIndex(tile), GetRoadOwner(tile, RTT_ROAD), GetRoadOwner(tile, RTT_TRAM));
DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile);
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
}
break;
}
diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp
index ee22d0403a..d77597f914 100644
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -38,6 +38,7 @@
#include "company_gui.h"
#include "road_func.h"
#include "roadstop_base.h"
+#include "scope.h"
#include "table/strings.h"
#include "table/roadtypes.h"
@@ -187,6 +188,229 @@ bool RoadVehiclesAreBuilt()
return !RoadVehicle::Iterate().empty();
}
+static DisallowedRoadDirections GetOneWayRoadTileDisallowedRoadDirections(TileIndex tile)
+{
+ if (IsNormalRoadTile(tile)) return GetDisallowedRoadDirections(tile);
+ if (IsDriveThroughStopTile(tile)) return GetDriveThroughStopDisallowedRoadDirections(tile);
+ return DRD_NONE;
+}
+
+static DiagDirection OneWaySideJunctionRoadRoadBitsToDiagDir(RoadBits bits)
+{
+ /*
+ * Drive on left missing bit:
+ * ROAD_SE (bit 2) -> DIAGDIR_NE (0)
+ * ROAD_SW (bit 1) -> DIAGDIR_SE (1)
+ * ROAD_NW (bit 0) -> DIAGDIR_SW (2)
+ * ROAD_NE (bit 3) -> DIAGDIR_NW (3)
+ */
+ uint8 bit = FIND_FIRST_BIT(bits ^ ROAD_ALL);
+ bit ^= 3;
+ return (DiagDirection)((bit + 3 + (_settings_game.vehicle.road_side * 2)) % 4);
+}
+
+inline bool IsOneWaySideJunctionRoadDRDsPresent(TileIndex tile, DiagDirection dir)
+{
+ const DisallowedRoadDirections diagdir_to_drd[DIAGDIR_END] = { DRD_NORTHBOUND, DRD_NORTHBOUND, DRD_SOUTHBOUND, DRD_SOUTHBOUND };
+
+ TileIndexDiffC ti = TileIndexDiffCByDiagDir(dir);
+ TileIndex ahead = AddTileIndexDiffCWrap(tile, ti);
+ if (ahead == INVALID_TILE || GetOneWayRoadTileDisallowedRoadDirections(ahead) != diagdir_to_drd[dir]) return false;
+ TileIndex behind = AddTileIndexDiffCWrap(tile, { (int16)(-ti.x), (int16)(-ti.y) });
+ if (behind == INVALID_TILE || GetOneWayRoadTileDisallowedRoadDirections(behind) != diagdir_to_drd[dir]) return false;
+ return true;
+}
+
+static btree::btree_set _road_cache_one_way_state_pending_tiles;
+static btree::btree_set _road_cache_one_way_state_pending_interpolate_tiles;
+static bool _defer_update_road_cache_one_way_state = false;
+bool _mark_tile_dirty_on_road_cache_one_way_state_update = false;
+
+static void UpdateTileRoadCachedOneWayState(TileIndex tile)
+{
+ if (unlikely(_mark_tile_dirty_on_road_cache_one_way_state_update)) MarkTileGroundDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+
+ DisallowedRoadDirections drd = GetOneWayRoadTileDisallowedRoadDirections(tile);
+ if (drd != DRD_NONE) {
+ SetRoadCachedOneWayState(tile, (RoadCachedOneWayState)drd);
+ return;
+ }
+ if (IsNormalRoadTile(tile)) {
+ RoadBits bits = GetRoadBits(tile, RTT_ROAD);
+ if (HasExactlyOneBit(bits ^ ROAD_ALL)) {
+ DiagDirection dir = OneWaySideJunctionRoadRoadBitsToDiagDir(bits);
+ if (IsOneWaySideJunctionRoadDRDsPresent(tile, dir)) {
+ DiagDirection side_dir = (DiagDirection)((dir + 3 + (_settings_game.vehicle.road_side * 2)) % 4);
+ TileIndexDiffC ti = TileIndexDiffCByDiagDir(side_dir);
+ TileIndex side = AddTileIndexDiffCWrap(tile, ti);
+
+ const DisallowedRoadDirections diagdir_to_drd[DIAGDIR_END] = { DRD_SOUTHBOUND, DRD_SOUTHBOUND, DRD_NORTHBOUND, DRD_NORTHBOUND };
+ SetRoadCachedOneWayState(tile, (GetOneWayRoadTileDisallowedRoadDirections(side) & diagdir_to_drd[side_dir]) ? RCOWS_SIDE_JUNCTION_NO_EXIT : RCOWS_SIDE_JUNCTION);
+ return;
+ }
+ }
+ }
+ if (!IsTileType(tile, MP_STATION)) _road_cache_one_way_state_pending_interpolate_tiles.insert(tile);
+ SetRoadCachedOneWayState(tile, RCOWS_NORMAL);
+}
+
+/* Do not re-order, see: RoadCachedOneWayState */
+enum InterpolateRoadResult {
+ IRR_NONE,
+ IRR_OUT,
+ IRR_IN
+};
+
+static TileIndex InterpolateRoadFollowTileStep(TileIndex tile, uint8 bit)
+{
+ DiagDirection outgoing = (DiagDirection)(bit ^ 3);
+ if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(tile) == outgoing) {
+ return GetOtherTunnelBridgeEnd(tile);
+ }
+ TileIndexDiffC ti = TileIndexDiffCByDiagDir(outgoing);
+ TileIndex next = AddTileIndexDiffCWrap(tile, ti);
+ if (next == INVALID_TILE) return INVALID_TILE;
+ if (IsTileType(next, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(next) == ReverseDiagDir(outgoing)) {
+ return INVALID_TILE;
+ }
+ return next;
+}
+
+static InterpolateRoadResult InterpolateRoadFollowRoadBit(TileIndex tile, uint8 bit)
+{
+ const TileIndex start = tile;
+ do {
+ TileIndex next = InterpolateRoadFollowTileStep(tile, bit);
+ if (next == INVALID_TILE) return IRR_NONE;
+ DisallowedRoadDirections drd = GetOneWayRoadTileDisallowedRoadDirections(next);
+ if (drd == DRD_BOTH) return IRR_NONE;
+ if (drd != DRD_NONE) {
+ const DisallowedRoadDirections outgoing_drd_by_exit_bit[4] = { DRD_SOUTHBOUND, DRD_SOUTHBOUND, DRD_NORTHBOUND, DRD_NORTHBOUND };
+ return outgoing_drd_by_exit_bit[bit] == drd ? IRR_OUT : IRR_IN;
+ }
+ if (IsTileType(next, MP_STATION)) return IRR_NONE;
+ RoadBits incoming = (RoadBits)(1 << (bit ^ 2));
+ RoadBits rb = GetAnyRoadBits(next, RTT_ROAD, true);
+ if ((incoming & rb) == 0) return IRR_NONE;
+ RoadBits remaining = rb & ~incoming;
+ if (!HasExactlyOneBit(remaining)) return IRR_NONE;
+ tile = next;
+ bit = FIND_FIRST_BIT(remaining);
+ } while (tile != start);
+ return IRR_NONE;
+}
+
+static void InterpolateRoadFollowRoadBitSetState(TileIndex tile, uint8 bit, InterpolateRoadResult irr)
+{
+ const TileIndex start = tile;
+ do {
+ if (irr == IRR_NONE) {
+ SetRoadCachedOneWayState(tile, RCOWS_NORMAL);
+ } else {
+ uint8 inbit = FIND_FIRST_BIT(GetAnyRoadBits(tile, RTT_ROAD, true) & ~(1 << bit));
+ /* inbit bit piece Outgoing Trackdir IRR_IN case
+ *
+ * 0 1 ROAD_W TRACKDIR_LEFT_S RCOWS_NON_JUNCTION_A
+ * 0 2 ROAD_Y TRACKDIR_Y_SE RCOWS_NON_JUNCTION_A
+ * 0 3 ROAD_N TRACKDIR_UPPER_E RCOWS_NON_JUNCTION_A
+ *
+ * 1 0 ROAD_W TRACKDIR_LEFT_N RCOWS_NON_JUNCTION_B
+ * 1 2 ROAD_S TRACKDIR_LOWER_E RCOWS_NON_JUNCTION_A
+ * 1 3 ROAD_X TRACKDIR_X_NE RCOWS_NON_JUNCTION_A
+ *
+ * 2 0 ROAD_Y TRACKDIR_Y_NW RCOWS_NON_JUNCTION_B
+ * 2 1 ROAD_S TRACKDIR_LOWER_W RCOWS_NON_JUNCTION_B
+ * 2 3 ROAD_E TRACKDIR_RIGHT_N RCOWS_NON_JUNCTION_B
+ *
+ * 3 0 ROAD_N TRACKDIR_UPPER_W RCOWS_NON_JUNCTION_B
+ * 3 1 ROAD_X TRACKDIR_X_SW RCOWS_NON_JUNCTION_B
+ * 3 2 ROAD_E TRACKDIR_RIGHT_S RCOWS_NON_JUNCTION_A
+ */
+
+ const uint16 bits_to_rcows = 0x3B10;
+ SetRoadCachedOneWayState(tile, (RoadCachedOneWayState)(irr ^ (HasBit(bits_to_rcows, (inbit << 2) | bit) ? 0 : 3)));
+ }
+ _road_cache_one_way_state_pending_interpolate_tiles.erase(tile);
+ if (unlikely(_mark_tile_dirty_on_road_cache_one_way_state_update)) MarkTileGroundDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ TileIndex next = InterpolateRoadFollowTileStep(tile, bit);
+ if (next == INVALID_TILE) return;
+ DisallowedRoadDirections drd = GetOneWayRoadTileDisallowedRoadDirections(next);
+ if (drd != DRD_NONE) {
+ return;
+ }
+ if (IsTileType(next, MP_STATION)) return;
+ RoadBits incoming = (RoadBits)(1 << (bit ^ 2));
+ RoadBits rb = GetAnyRoadBits(next, RTT_ROAD, true);
+ if ((incoming & rb) == 0) return;
+ RoadBits remaining = rb & ~incoming;
+ if (!HasExactlyOneBit(remaining)) return;
+ tile = next;
+ bit = FIND_FIRST_BIT(remaining);
+ } while (tile != start);
+}
+
+static void InterpolateRoadCachedOneWayStates()
+{
+ while (!_road_cache_one_way_state_pending_interpolate_tiles.empty()) {
+ auto iter = _road_cache_one_way_state_pending_interpolate_tiles.begin();
+ TileIndex tile = *iter;
+ _road_cache_one_way_state_pending_interpolate_tiles.erase(iter);
+
+ const RoadBits bits = GetAnyRoadBits(tile, RTT_ROAD, true);
+ if (CountBits(bits) != 2) continue;
+
+ uint8 first_bit = FIND_FIRST_BIT(bits);
+ uint8 second_bit = FIND_FIRST_BIT(KillFirstBit(bits));
+ InterpolateRoadResult first_irr = InterpolateRoadFollowRoadBit(tile, first_bit);
+ InterpolateRoadResult second_irr = first_irr;
+ if (first_irr != IRR_NONE) {
+ second_irr = InterpolateRoadFollowRoadBit(tile, second_bit);
+ if (second_irr == IRR_NONE || second_irr == first_irr) first_irr = second_irr = IRR_NONE;
+ }
+ InterpolateRoadFollowRoadBitSetState(tile, first_bit, first_irr);
+ InterpolateRoadFollowRoadBitSetState(tile, second_bit, second_irr);
+ }
+}
+
+void RecalculateRoadCachedOneWayStates()
+{
+ for (TileIndex tile = 0; tile != MapSize(); tile++) {
+ if (MayHaveRoad(tile)) UpdateTileRoadCachedOneWayState(tile);
+ }
+ InterpolateRoadCachedOneWayStates();
+}
+
+void UpdateRoadCachedOneWayStatesAroundTile(TileIndex tile)
+{
+ if (_generating_world) return;
+
+ auto check_tile = [](TileIndex t) {
+ if (_defer_update_road_cache_one_way_state) {
+ _road_cache_one_way_state_pending_tiles.insert(t);
+ } else if (MayHaveRoad(t)) {
+ UpdateTileRoadCachedOneWayState(t);
+ }
+ };
+ check_tile(tile);
+ uint x_offset = TileXY(1, 0);
+ if (tile >= x_offset) check_tile(tile - x_offset);
+ if (tile + x_offset < MapSize()) check_tile(tile + x_offset);
+ uint y_offset = TileXY(0, 1);
+ if (tile >= y_offset) check_tile(tile - y_offset);
+ if (tile + y_offset < MapSize()) check_tile(tile + y_offset);
+ if (!_defer_update_road_cache_one_way_state) InterpolateRoadCachedOneWayStates();
+}
+
+void FlushDeferredUpdateRoadCachedOneWayStates()
+{
+ _defer_update_road_cache_one_way_state = false;
+ for (TileIndex t : _road_cache_one_way_state_pending_tiles) {
+ if (MayHaveRoad(t)) UpdateTileRoadCachedOneWayState(t);
+ }
+ _road_cache_one_way_state_pending_tiles.clear();
+ InterpolateRoadCachedOneWayStates();
+}
+
/**
* Update road infrastructure counts for a company.
* @param rt Road type to update count of.
@@ -470,6 +694,10 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
/* Todo: Change this to be more fine-grained if necessary */
NotifyRoadLayoutChanged(false);
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ UpdateRoadCachedOneWayStatesAroundTile(other_end);
+ }
}
} else {
assert_tile(IsDriveThroughStopTile(tile), tile);
@@ -480,6 +708,9 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
SetRoadType(tile, rtt, INVALID_ROADTYPE);
MarkTileDirtyByTile(tile);
NotifyRoadLayoutChanged(false);
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ }
}
}
return cost;
@@ -558,6 +789,9 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
SetRoadBits(tile, present, rtt);
MarkTileDirtyByTile(tile);
}
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ }
}
CommandCost cost(EXPENSES_CONSTRUCTION, CountBits(pieces) * RoadClearCost(existing_rt));
@@ -596,6 +830,9 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
}
MarkTileDirtyByTile(tile);
YapfNotifyTrackLayoutChange(tile, railtrack);
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ }
}
return CommandCost(EXPENSES_CONSTRUCTION, RoadClearCost(existing_rt) * 2);
}
@@ -771,6 +1008,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
SetDisallowedRoadDirections(tile, dis_new);
MarkTileDirtyByTile(tile);
NotifyRoadLayoutChanged(CountBits(dis_existing) > CountBits(dis_new));
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
}
return CommandCost();
}
@@ -866,6 +1104,9 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
SetCrossingReservation(tile, reserved);
UpdateLevelCrossing(tile, false);
if (RoadLayoutChangeNotificationEnabled(true)) NotifyRoadLayoutChangedIfTileNonLeaf(tile, rtt, GetCrossingRoadBits(tile));
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ }
MarkTileDirtyByTile(tile);
}
return CommandCost(EXPENSES_CONSTRUCTION, 2 * RoadBuildCost(rt));
@@ -897,6 +1138,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
rs->ChangeDriveThroughDisallowedRoadDirections(dis_new);
MarkTileDirtyByTile(tile);
NotifyRoadLayoutChanged(CountBits(dis_existing) > CountBits(dis_new));
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
}
return CommandCost();
}
@@ -1028,6 +1270,10 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
AddRoadTunnelBridgeInfrastructure(tile, other_end);
NotifyRoadLayoutChanged(true);
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ UpdateRoadCachedOneWayStatesAroundTile(other_end);
+ }
DirtyAllCompanyInfrastructureWindows();
}
@@ -1144,6 +1390,9 @@ do_clear:;
MarkTileDirtyByTile(other_end);
MarkTileDirtyByTile(tile);
}
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(other_end);
+ }
NotifyRoadLayoutChanged(true);
break;
}
@@ -1171,6 +1420,9 @@ do_clear:;
SetDisallowedRoadDirections(tile, IsStraightRoad(existing) ?
GetDisallowedRoadDirections(tile) ^ toggle_drd : DRD_NONE);
}
+ if (rtt == RTT_ROAD) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ }
MarkTileDirtyByTile(tile);
}
@@ -1252,6 +1504,11 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
bool had_success = false;
bool is_ai = HasBit(p2, 11);
+ _defer_update_road_cache_one_way_state = true;
+ auto guard = scope_guard([]() {
+ FlushDeferredUpdateRoadCachedOneWayStates();
+ });
+
/* Start tile is the first tile clicked by the user. */
for (;;) {
RoadBits bits = AxisToRoadBits(axis);
@@ -1335,6 +1592,11 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32
p2 ^= IsInsideMM(p2 & 3, 1, 3) ? 3 : 0;
}
+ _defer_update_road_cache_one_way_state = true;
+ auto guard = scope_guard([]() {
+ FlushDeferredUpdateRoadCachedOneWayStates();
+ });
+
Money money = GetAvailableMoneyForCommand();
TileIndex tile = start_tile;
CommandCost last_error = CMD_ERROR;
@@ -2258,16 +2520,40 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u
switch (GetRoadTileType(tile)) {
case ROAD_TILE_NORMAL: {
const uint drd_to_multiplier[DRD_END] = { 0x101, 0x100, 0x1, 0x0 };
+ const TrackdirBits left_turns = TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_RIGHT_S;
+ const TrackdirBits right_turns = TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_N;
+ const TrackdirBits no_exit_turns[4] = {
+ TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_E, // ROAD_NW
+ TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_UPPER_E, // ROAD_SW
+ TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_UPPER_W, // ROAD_SE
+ TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_LOWER_W // ROAD_NE
+ };
+
RoadBits bits = GetRoadBits(tile, rtt);
/* no roadbit at this side of tile, return 0 */
if (side != INVALID_DIAGDIR && (DiagDirToRoadBits(side) & bits) == 0) break;
- uint multiplier = drd_to_multiplier[(rtt == RTT_TRAM) ? DRD_NONE : GetDisallowedRoadDirections(tile)];
- if (!HasRoadWorks(tile)) trackdirbits = (TrackdirBits)(_road_trackbits[bits] * multiplier);
+ if (!HasRoadWorks(tile)) {
+ RoadCachedOneWayState rcows = (rtt == RTT_TRAM) ? RCOWS_NORMAL : GetRoadCachedOneWayState(tile);
+ switch (rcows) {
+ case RCOWS_NORMAL:
+ case RCOWS_NON_JUNCTION_A:
+ case RCOWS_NON_JUNCTION_B:
+ case RCOWS_NO_ACCESS:
+ trackdirbits = (TrackdirBits)(_road_trackbits[bits] * drd_to_multiplier[(DisallowedRoadDirections)rcows]);
+ break;
- extern TrackdirBits MaskOneWaySideJunctionRoad(TileIndex tile, RoadBits bits);
- if (rtt == RTT_ROAD && HasExactlyOneBit(bits ^ ROAD_ALL)) trackdirbits &= MaskOneWaySideJunctionRoad(tile, bits);
+ case RCOWS_SIDE_JUNCTION:
+ case RCOWS_SIDE_JUNCTION_NO_EXIT:
+ trackdirbits = (TrackdirBits)((_road_trackbits[bits] * 0x101) & ~(_settings_game.vehicle.road_side ? left_turns : right_turns));
+ if (rcows == RCOWS_SIDE_JUNCTION_NO_EXIT) trackdirbits &= ~no_exit_turns[FIND_FIRST_BIT(bits ^ ROAD_ALL) & 3];
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+ }
break;
}
diff --git a/src/road_func.h b/src/road_func.h
index c98dca732b..0d099fde18 100644
--- a/src/road_func.h
+++ b/src/road_func.h
@@ -156,6 +156,8 @@ RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date);
void UpdateLevelCrossing(TileIndex tile, bool sound = true, bool force_close = false);
bool IsCrossingOccupiedByRoadVehicle(TileIndex t);
+
+void UpdateRoadCachedOneWayStatesAroundTile(TileIndex tile);
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count);
struct TileInfo;
diff --git a/src/road_map.h b/src/road_map.h
index 37a11036ed..df9ec2ab15 100644
--- a/src/road_map.h
+++ b/src/road_map.h
@@ -288,8 +288,8 @@ static inline bool HasTownOwnedRoad(TileIndex t)
/** Which directions are disallowed ? */
enum DisallowedRoadDirections {
DRD_NONE, ///< None of the directions are disallowed
- DRD_SOUTHBOUND, ///< All southbound traffic is disallowed
- DRD_NORTHBOUND, ///< All northbound traffic is disallowed
+ DRD_SOUTHBOUND, ///< All southbound traffic is disallowed (Trackdir 8-13 is allowed)
+ DRD_NORTHBOUND, ///< All northbound traffic is disallowed (Trackdir 0-5 is allowed)
DRD_BOTH, ///< All directions are disallowed
DRD_END, ///< Sentinel
};
@@ -320,6 +320,39 @@ static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirect
SB(_m[t].m5, 4, 2, drd);
}
+enum RoadCachedOneWayState {
+ RCOWS_NORMAL = 0, ///< Road is not one-way
+ RCOWS_NON_JUNCTION_A, ///< Road is one-way in 'A' direction (Trackdir 8-13 is allowed, same as DRD_SOUTHBOUND for straight road pieces)
+ RCOWS_NON_JUNCTION_B, ///< Road is one-way in 'B' direction (Trackdir 0-5 is allowed, same as DRD_NORTHBOUND for straight road pieces)
+ RCOWS_NO_ACCESS, ///< Road is disallowed in both directions
+ RCOWS_SIDE_JUNCTION, ///< Road is a one-way side junction
+ RCOWS_SIDE_JUNCTION_NO_EXIT, ///< Road is a one-way side junction, with no side exit
+};
+
+/**
+ * Get the road cached one-way state
+ * @param t tile to get the state from
+ * @pre MayHaveRoad(t)
+ * @return road cached one way state
+ */
+static inline RoadCachedOneWayState GetRoadCachedOneWayState(TileIndex t)
+{
+ assert(MayHaveRoad(t));
+ return (RoadCachedOneWayState)GB(_me[t].m8, 12, 3);
+}
+
+/**
+ * Set the road cached one-way state
+ * @param t tile to set the state of
+ * @param rcows road cached one way state
+ * @pre MayHaveRoad(t)
+ */
+static inline void SetRoadCachedOneWayState(TileIndex t, RoadCachedOneWayState rcows)
+{
+ assert(MayHaveRoad(t));
+ SB(_me[t].m8, 12, 3, rcows);
+}
+
/**
* Get the road axis of a level crossing.
* @param t The tile to query.
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 492b452e46..acce384087 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -373,90 +373,20 @@ bool RoadVehicle::FindClosestDepot(TileIndex *location, DestinationID *destinati
return true;
}
-static DisallowedRoadDirections GetOneWayRoadTileDisallowedRoadDirections(TileIndex tile)
+inline bool IsOneWayRoadTile(TileIndex tile)
{
- if (IsNormalRoadTile(tile)) return GetDisallowedRoadDirections(tile);
- if (IsDriveThroughStopTile(tile)) return GetDriveThroughStopDisallowedRoadDirections(tile);
- return DRD_NONE;
+ return MayHaveRoad(tile) && GetRoadCachedOneWayState(tile) != RCOWS_NORMAL;
}
-static DiagDirection OneWaySideJunctionRoadRoadBitsToDiagDir(RoadBits bits)
+inline bool IsOneWaySideJunctionRoadTile(TileIndex tile)
{
- /*
- * Drive on left missing bit:
- * ROAD_SE (bit 2) -> DIAGDIR_NE (0)
- * ROAD_SW (bit 1) -> DIAGDIR_SE (1)
- * ROAD_NW (bit 0) -> DIAGDIR_SW (2)
- * ROAD_NE (bit 3) -> DIAGDIR_NW (3)
- */
- uint8 bit = FIND_FIRST_BIT(bits ^ ROAD_ALL);
- bit ^= 3;
- return (DiagDirection)((bit + 3 + (_settings_game.vehicle.road_side * 2)) % 4);
+ return MayHaveRoad(tile) && (GetRoadCachedOneWayState(tile) == RCOWS_SIDE_JUNCTION || GetRoadCachedOneWayState(tile) == RCOWS_SIDE_JUNCTION_NO_EXIT);
}
-inline bool IsOneWaySideJunctionRoadDRDsPresent(TileIndex tile, DiagDirection dir)
+static bool MayReverseOnOneWayRoadTile(TileIndex tile, DiagDirection dir)
{
- const DisallowedRoadDirections diagdir_to_drd[DIAGDIR_END] = { DRD_NORTHBOUND, DRD_NORTHBOUND, DRD_SOUTHBOUND, DRD_SOUTHBOUND };
-
- TileIndexDiffC ti = TileIndexDiffCByDiagDir(dir);
- TileIndex ahead = AddTileIndexDiffCWrap(tile, ti);
- if (ahead == INVALID_TILE || GetOneWayRoadTileDisallowedRoadDirections(ahead) != diagdir_to_drd[dir]) return false;
- TileIndex behind = AddTileIndexDiffCWrap(tile, { (int16)(-ti.x), (int16)(-ti.y) });
- if (behind == INVALID_TILE || GetOneWayRoadTileDisallowedRoadDirections(behind) != diagdir_to_drd[dir]) return false;
- return true;
-}
-
-static bool IsOneWaySideJunctionRoad(TileIndex tile)
-{
- RoadBits bits = GetRoadBits(tile, RTT_ROAD);
- if (!HasExactlyOneBit(bits ^ ROAD_ALL)) return false;
- DiagDirection dir = OneWaySideJunctionRoadRoadBitsToDiagDir(bits);
- return IsOneWaySideJunctionRoadDRDsPresent(tile, dir);
-}
-
-TrackdirBits MaskOneWaySideJunctionRoad(TileIndex tile, RoadBits bits)
-{
- DiagDirection dir = OneWaySideJunctionRoadRoadBitsToDiagDir(bits);
- if (!IsOneWaySideJunctionRoadDRDsPresent(tile, dir)) return ~TRACKDIR_BIT_NONE;
-
- const TrackdirBits left_turns = TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_RIGHT_S;
- const TrackdirBits right_turns = TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_N;
- TrackdirBits remove = (_settings_game.vehicle.road_side ? left_turns : right_turns);
-
- DiagDirection side_dir = (DiagDirection)((dir + 3 + (_settings_game.vehicle.road_side * 2)) % 4);
- TileIndexDiffC ti = TileIndexDiffCByDiagDir(side_dir);
- TileIndex side = AddTileIndexDiffCWrap(tile, ti);
-
- const DisallowedRoadDirections diagdir_to_drd[DIAGDIR_END] = { DRD_SOUTHBOUND, DRD_SOUTHBOUND, DRD_NORTHBOUND, DRD_NORTHBOUND };
- const TrackdirBits remove_side_trackdirs[] = {
- TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_UPPER_E, // DIAGDIR_NE
- TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_E, // DIAGDIR_SE
- TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_LOWER_W, // DIAGDIR_SW
- TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_UPPER_W // DIAGDIR_NW
- };
- if (side != INVALID_TILE && GetOneWayRoadTileDisallowedRoadDirections(side) & diagdir_to_drd[side_dir]) remove |= remove_side_trackdirs[side_dir];
-
- return ~remove;
-}
-
-static bool IsOneWayRoadTile(TileIndex tile)
-{
- if (IsNormalRoadTile(tile)) {
- if (GetDisallowedRoadDirections(tile) != DRD_NONE) return true;
- if (IsOneWaySideJunctionRoad(tile)) return true;
- }
- if (IsDriveThroughStopTile(tile) && GetDriveThroughStopDisallowedRoadDirections(tile) != DRD_NONE) return true;
- return false;
-}
-
-uint8 GetOneWayRoadTileType(TileIndex tile)
-{
- if (IsNormalRoadTile(tile)) {
- if (GetDisallowedRoadDirections(tile) != DRD_NONE) return 1;
- if (IsOneWaySideJunctionRoad(tile)) return 2;
- }
- if (IsDriveThroughStopTile(tile) && GetDriveThroughStopDisallowedRoadDirections(tile) != DRD_NONE) return 3;
- return 0;
+ TrackdirBits bits = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, RTT_ROAD));
+ return bits & DiagdirReachesTrackdirs(ReverseDiagDir(dir));
}
/**
@@ -946,14 +876,15 @@ static bool CheckRoadInfraUnsuitableForOvertaking(OvertakeData *od)
if (!HasBit(trackdirbits, od->trackdir) || (red_signals != TRACKDIR_BIT_NONE)) return true;
/* Track has junction */
if (trackbits & ~TRACK_BIT_CROSS) {
- if (IsNormalRoadTile(od->tile) && IsOneWaySideJunctionRoad(od->tile)) {
+ RoadCachedOneWayState rcows = GetRoadCachedOneWayState(od->tile);
+ if (rcows == RCOWS_SIDE_JUNCTION) {
const RoadVehPathCache &pc = od->v->path;
if (!pc.empty() && pc.tile.front() == od->tile && !IsStraightRoadTrackdir(pc.td.front())) {
/* cached path indicates that we are turning here, do not overtake */
return true;
}
} else {
- return true;
+ return rcows != RCOWS_SIDE_JUNCTION_NO_EXIT;
}
}
@@ -1495,7 +1426,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
} else if (v->HasArticulatedPart() && IsBridgeTile(v->tile) && (IsRoadCustomBridgeHeadTile(v->tile) || IsRoadCustomBridgeHeadTile(GetOtherBridgeEnd(v->tile)))) {
/* Articulated RVs may not overtake on custom bridge heads */
v->SetRoadVehicleOvertaking(0);
- } else if (v->state < RVSB_IN_ROAD_STOP && !IsStraightRoadTrackdir((Trackdir)v->state) && IsOneWaySideJunctionRoad(v->tile)) {
+ } else if (v->state < RVSB_IN_ROAD_STOP && !IsStraightRoadTrackdir((Trackdir)v->state) && IsOneWaySideJunctionRoadTile(v->tile)) {
/* No turning to/from overtaking lane on one way side road junctions */
v->SetRoadVehicleOvertaking(0);
} else if (++v->overtaking_ctr >= RV_OVERTAKE_TIMEOUT) {
@@ -1649,7 +1580,7 @@ again:
v->cur_speed = 0;
return false;
}
- } else if (IsOneWayRoadTile(v->tile)) {
+ } else if (IsOneWayRoadTile(v->tile) && !MayReverseOnOneWayRoadTile(v->tile, (DiagDirection)(rd.x & 3))) {
v->cur_speed = 0;
return false;
} else {
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index c5c69b983f..44170a1d1e 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -3795,6 +3795,11 @@ bool AfterLoadGame()
}
}
+ if (SlXvIsFeatureMissing(XSLFI_ONE_WAY_ROAD_STATE)) {
+ extern void RecalculateRoadCachedOneWayStates();
+ RecalculateRoadCachedOneWayStates();
+ }
+
InitializeRoadGUI();
/* This needs to be done after conversion. */
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index 07ff952abc..ee61e5b4a0 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -137,6 +137,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_MORE_VEHICLE_ORDERS, XSCF_NULL, 1, 1, "more_veh_orders", nullptr, nullptr, nullptr },
{ XSLFI_ORDER_FLAGS_EXTRA, XSCF_NULL, 1, 1, "order_flags_extra", nullptr, nullptr, nullptr },
{ XSLFI_ONE_WAY_DT_ROAD_STOP, XSCF_NULL, 1, 1, "one_way_dt_road_stop", nullptr, nullptr, nullptr },
+ { XSLFI_ONE_WAY_ROAD_STATE, XSCF_NULL, 1, 1, "one_way_road_state", nullptr, nullptr, nullptr },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
};
diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h
index 05bb25c858..627f0ce0bc 100644
--- a/src/saveload/extended_ver_sl.h
+++ b/src/saveload/extended_ver_sl.h
@@ -91,6 +91,7 @@ enum SlXvFeatureIndex {
XSLFI_MORE_VEHICLE_ORDERS, ///< More vehicle orders - VehicleOrderID is 16 bits instead of 8
XSLFI_ORDER_FLAGS_EXTRA, ///< Order flags field extra size
XSLFI_ONE_WAY_DT_ROAD_STOP, ///< One-way drive-through road stops
+ XSLFI_ONE_WAY_ROAD_STATE, ///< One-way road state cache
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.cpp b/src/settings.cpp
index 6d2edb55e8..c71c0146b1 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1412,7 +1412,11 @@ static bool MaxNoAIsChange(int32 i)
static bool CheckRoadSide(int p1)
{
extern bool RoadVehiclesAreBuilt();
- return _game_mode == GM_MENU || !RoadVehiclesAreBuilt();
+ if (_game_mode != GM_MENU && RoadVehiclesAreBuilt()) return false;
+
+ extern void RecalculateRoadCachedOneWayStates();
+ RecalculateRoadCachedOneWayStates();
+ return true;
}
/**
@@ -2032,6 +2036,8 @@ void LoadFromConfig(bool minimal)
ValidateSettings();
+ PostZoningModeChange();
+
/* Display scheduled errors */
extern void ScheduleErrorMessage(ErrorList &datas);
ScheduleErrorMessage(_settings_error_list);
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 5ed0c029c8..79665ba32e 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -2082,6 +2082,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
}
ZoningMarkDirtyStationCoverageArea(st);
NotifyRoadLayoutChanged(true);
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
}
if (st != nullptr) {
@@ -2275,6 +2276,7 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
}
+ if (flags & DC_EXEC) UpdateRoadCachedOneWayStatesAroundTile(cur_tile);
}
return had_success ? cost : last_error;
@@ -4588,6 +4590,13 @@ static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
return true;
}
+static CommandCost RemoveRoadStopAndUpdateRoadCachedOneWayState(TileIndex tile, DoCommandFlag flags)
+{
+ CommandCost cost = RemoveRoadStop(tile, flags);
+ if ((flags & DC_EXEC) && cost.Succeeded()) UpdateRoadCachedOneWayStatesAroundTile(tile);
+ return cost;
+}
+
/**
* Clear a single tile of a station.
* @param tile The tile to clear.
@@ -4620,12 +4629,12 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags)) {
return_cmd_error(STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
}
- return RemoveRoadStop(tile, flags);
+ return RemoveRoadStopAndUpdateRoadCachedOneWayState(tile, flags);
case STATION_BUS:
if (IsDriveThroughStopTile(tile) && !CanRemoveRoadWithStop(tile, flags)) {
return_cmd_error(STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
}
- return RemoveRoadStop(tile, flags);
+ return RemoveRoadStopAndUpdateRoadCachedOneWayState(tile, flags);
case STATION_BUOY: return RemoveBuoy(tile, flags);
case STATION_DOCK: return RemoveDock(tile, flags);
default: break;
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index 86621f90fc..4e350692e5 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -676,6 +676,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(tile_start, tile_end, dir, GetRoadTramType(roadtype));
}
}
+ UpdateRoadCachedOneWayStatesAroundTile(tile_start);
+ UpdateRoadCachedOneWayStatesAroundTile(tile_end);
break;
}
@@ -1039,6 +1041,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
RoadType tram_rt = RoadTypeIsTram(roadtype) ? roadtype : INVALID_ROADTYPE;
MakeRoadTunnel(start_tile, company, t->index, direction, road_rt, tram_rt);
MakeRoadTunnel(end_tile, company, t->index, ReverseDiagDir(direction), road_rt, tram_rt);
+ UpdateRoadCachedOneWayStatesAroundTile(start_tile);
+ UpdateRoadCachedOneWayStatesAroundTile(end_tile);
}
DirtyCompanyInfrastructureWindows(company);
}
@@ -1180,6 +1184,9 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
DoClearSquare(tile);
DoClearSquare(endtile);
+
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ UpdateRoadCachedOneWayStatesAroundTile(endtile);
}
ViewportMapInvalidateTunnelCacheByTile(tile < endtile ? tile : endtile, axis);
}
@@ -1265,6 +1272,7 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
bool removetile = false;
bool removeendtile = false;
+ bool update_road = false;
/* Update company infrastructure counts. */
if (rail) {
@@ -1279,6 +1287,7 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
if (HasRoadTypeTram(tile)) NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(tile, endtile, direction, RTT_TRAM);
}
}
+ update_road = true;
} else { // Aqueduct
if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
removetile = IsDockingTile(tile);
@@ -1325,6 +1334,11 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
TryPathReserve(vehicles_affected[i], true);
}
}
+
+ if (update_road) {
+ UpdateRoadCachedOneWayStatesAroundTile(tile);
+ UpdateRoadCachedOneWayStatesAroundTile(endtile);
+ }
}
return cost;
diff --git a/src/zoning.h b/src/zoning.h
index 2cad5f3a87..c24daefbd6 100644
--- a/src/zoning.h
+++ b/src/zoning.h
@@ -68,6 +68,7 @@ void ZoningStationWindowOpenClose(const Station *st);
void ZoningTownAuthorityRatingChange();
void SetZoningMode(bool inner, ZoningEvaluationMode mode);
+void PostZoningModeChange();
void ClearZoningCaches();
diff --git a/src/zoning_cmd.cpp b/src/zoning_cmd.cpp
index 10fce275db..42f27f709e 100644
--- a/src/zoning_cmd.cpp
+++ b/src/zoning_cmd.cpp
@@ -25,6 +25,7 @@
#include "window_func.h"
#include "zoning.h"
#include "viewport_func.h"
+#include "road_map.h"
#include "3rdparty/cpp-btree/btree_set.h"
Zoning _zoning;
@@ -282,18 +283,27 @@ inline SpriteID TileZoneCheckRoadGridEvaluation(TileIndex tile, uint grid_size)
*/
inline SpriteID TileZoneCheckOneWayRoadEvaluation(TileIndex tile)
{
- extern uint8 GetOneWayRoadTileType(TileIndex tile);
+ if (!MayHaveRoad(tile)) return ZONING_INVALID_SPRITE_ID;
- uint8 type = GetOneWayRoadTileType(tile);
- switch (type) {
+ RoadCachedOneWayState rcows = GetRoadCachedOneWayState(tile);
+ switch (rcows) {
default:
return ZONING_INVALID_SPRITE_ID;
- case 1:
- return SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE;
- case 2:
+ case RCOWS_NO_ACCESS:
+ return SPR_ZONING_INNER_HIGHLIGHT_RED;
+ case RCOWS_NON_JUNCTION_A:
+ case RCOWS_NON_JUNCTION_B:
+ if (IsTileType(tile, MP_STATION)) {
+ return SPR_ZONING_INNER_HIGHLIGHT_GREEN;
+ } else if (IsNormalRoadTile(tile) && GetDisallowedRoadDirections(tile) != DRD_NONE) {
+ return SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE;
+ } else {
+ return SPR_ZONING_INNER_HIGHLIGHT_PURPLE;
+ }
+ case RCOWS_SIDE_JUNCTION:
return SPR_ZONING_INNER_HIGHLIGHT_ORANGE;
- case 3:
- return SPR_ZONING_INNER_HIGHLIGHT_GREEN;
+ case RCOWS_SIDE_JUNCTION_NO_EXIT:
+ return SPR_ZONING_INNER_HIGHLIGHT_YELLOW;
}
}
@@ -486,4 +496,11 @@ void SetZoningMode(bool inner, ZoningEvaluationMode mode)
current_mode = mode;
cache.clear();
MarkWholeNonMapViewportsDirty();
+ PostZoningModeChange();
+}
+
+void PostZoningModeChange()
+{
+ extern bool _mark_tile_dirty_on_road_cache_one_way_state_update;
+ _mark_tile_dirty_on_road_cache_one_way_state_update = (_zoning.inner == ZEM_ONE_WAY_ROAD) || (_zoning.outer == ZEM_ONE_WAY_ROAD);
}