Add road tile cached one way state, one way road section detection

pull/203/head
Jonathan G Rennison 4 years ago
parent 2fe5d4339b
commit ffe3c769a3

@ -124,10 +124,22 @@
</li>
</ul>
<ul>
<li>
<li style="color: blue">
<a name="RailType"></a>
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.
</li>
<li style="color: blue">
<a name="RoadCachedOneWayState"></a>
Bits 14..12: Road cached one way state. Used for all tiles with road (road, station, tunnelbridge).
<table border="1" style="width: 30em;">
<tr><td style="width: 5em;"><tt>00</tt></td><td align=left>road is not one-way</td></tr>
<tr><td><tt>01</tt></td><td align=left>road is one-way in 'A' direction</td></tr>
<tr><td><tt>02</tt></td><td align=left>road is one-way in 'B' direction</td></tr>
<tr><td><tt>03</tt></td><td align=left>road is disallowed in both directions</td></tr>
<tr><td><tt>04</tt></td><td align=left>road is a one-way side junction</td></tr>
<tr><td><tt>05</tt></td><td align=left>road is a one-way side junction, with no exit access</td></tr>
</table>
</li>
</ul>
</li>
<li><span style="font-weight: bold;">m7:</span><br>
@ -578,6 +590,7 @@
<li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of road type 1 (tram); OWNER_NONE (<tt>10</tt>) is stored as OWNER_TOWN (<tt>0F</tt>)
<li>m4 bits 5..0: <a href="#RoadType">Roadtype</a></li>
<li>m7 bit 5 set = on snow or desert</li>
<li style="color: blue">m8 bits 14..12: <a href="#RoadCachedOneWayState">Road cached one way state</a></li>
<li>m8 bits 11..6: <a href="#TramType">Tramtype</a></li>
<li>m5 bits 7 clear: road or level-crossing
<ul>
@ -1022,6 +1035,7 @@
<li>m7 bits 4..0: <a href="#OwnershipInfo">owner</a> of road (road stops)</li>
<li>m7: animation frame (railway stations/waypoints, airports)</li>
<li style="color: blue">m8 bits 14..12: <a href="#RoadCachedOneWayState">Road cached one way state</a></li>
<li>m8 bits 11..6: <a href="#TramType">Tramtype</a></li>
<li>m8 bits 5..0: <a href="#TrackType">track type</a> for railway stations/waypoints</li>
</ul>
@ -1812,6 +1826,7 @@
</tr>
</table>
</li>
<li style="color: blue">m8 bits 14..12: <a href="#RoadCachedOneWayState">Road cached one way state</a></li>
<li>m8 bits 11..6: <a href="#TramType">Tramtype</a></li>
<li>m8 bits 5..0: <a href="#TrackType">track type</a> for railway</li>
<li style="color: blue">m8 bits 11..6 = <a name="TrackType">secondary track type</a> for railway (used for bridge-bypassing track when two parallel tracks on custom bridge head)</li>

@ -149,7 +149,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">XXXX XXXX</td>
<td class="bits"><span class="free">OO</span>XX X<span class="free">OOO</span></td>
<td class="bits"><span class="free">OO</span>X<span class="free">O</span> XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX XX<span class="free">OO OOOO</span></td>
<td class="bits"><span class="free">O</span><span class="used_p">PPP</span> XXXX XX<span class="free">OO OOOO</span></td>
</tr>
<tr>
<td class="caption">level crossing</td>
@ -162,7 +162,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">XXXX <span class="free">OO</span><span class="used_p">P</span>X</td>
<td class="bits"><span class="free">OO</span>XX X<span class="free">OOO</span></td>
<td class="bits"><span class="free">OO</span>XX XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX XXXX XXXX</td>
<td class="bits"><span class="free">O<span class="used_p">PPP</span></span> XXXX XXXX XXXX</td>
</tr>
<tr>
<td class="caption">road depot</td>
@ -243,7 +243,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="option">~~~~ ~</span>XXX</td>
<td class="bits"><span class="free">OO</span>XX X<span class="free">OOO</span></td>
<td class="bits"><span class="free">OOO</span>X XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX XX<span class="free">OO OOOO</span></td>
<td class="bits"><span class="free">O<span class="used_p">PPP</span></span> XXXX XX<span class="free">OO OOOO</span></td>
</tr>
<tr>
<td class="caption">dock</td>
@ -363,7 +363,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">X<span class="free">OO</span>X XXXX</td>
<td class="bits"><span class="used_p">PP</span><span class="free">OO OO</span><span class="used_p">PP</span></td>
<td class="bits"><span class="used_p">PP</span>XX XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX XXXX XXXX</td>
<td class="bits"><span class="free">O<span class="used_p">PPP</span></span> XXXX XXXX XXXX</td>
</tr>
<tr>
<td>bridge ramp</td>
@ -376,7 +376,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">-inherit-</td>
<td class="bits"><span class="free"></span><span class="used_p">PP</span>XX XX<span class="used_p">PP</span></td>
<td class="bits">-inherit-</td>
<td class="bits"><span class="free">OOOO</span> <span class="abuse">PPPP PP</span>XX XXXX</td>
<td class="bits"><span class="free">O<span class="used_p">PPP</span></span> <span class="abuse">PPPP PP</span>XX XXXX</td>
</tr>
<tr>
<td rowspan=2>A</td>

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

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

@ -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<TileIndex> _road_cache_one_way_state_pending_tiles;
static btree::btree_set<TileIndex> _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;
}

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

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

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

@ -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. */

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

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

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

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

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

@ -68,6 +68,7 @@ void ZoningStationWindowOpenClose(const Station *st);
void ZoningTownAuthorityRatingChange();
void SetZoningMode(bool inner, ZoningEvaluationMode mode);
void PostZoningModeChange();
void ClearZoningCaches();

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

Loading…
Cancel
Save