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