From ffe3c769a3d99010d5781558f525d05081ecfcf4 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 26 Oct 2020 22:17:25 +0000 Subject: [PATCH] Add road tile cached one way state, one way road section detection --- docs/landscape.html | 17 +- docs/landscape_grid.html | 10 +- src/console_cmds.cpp | 14 ++ src/rail_cmd.cpp | 2 + src/road_cmd.cpp | 296 ++++++++++++++++++++++++++++++- src/road_func.h | 2 + src/road_map.h | 37 +++- src/roadveh_cmd.cpp | 93 ++-------- src/saveload/afterload.cpp | 5 + src/saveload/extended_ver_sl.cpp | 1 + src/saveload/extended_ver_sl.h | 1 + src/settings.cpp | 8 +- src/station_cmd.cpp | 13 +- src/tunnelbridge_cmd.cpp | 14 ++ src/zoning.h | 1 + src/zoning_cmd.cpp | 33 +++- 16 files changed, 442 insertions(+), 105 deletions(-) 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 @@
  • 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 @@ -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); - - extern TrackdirBits MaskOneWaySideJunctionRoad(TileIndex tile, RoadBits bits); - if (rtt == RTT_ROAD && HasExactlyOneBit(bits ^ ROAD_ALL)) trackdirbits &= MaskOneWaySideJunctionRoad(tile, bits); + 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; + + 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); -} - -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 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; + return MayHaveRoad(tile) && (GetRoadCachedOneWayState(tile) == RCOWS_SIDE_JUNCTION || GetRoadCachedOneWayState(tile) == RCOWS_SIDE_JUNCTION_NO_EXIT); } -static bool IsOneWayRoadTile(TileIndex tile) +static bool MayReverseOnOneWayRoadTile(TileIndex tile, DiagDirection dir) { - 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); }