diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 6b72dfeac5..ee22d0403a 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -2265,6 +2265,9 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u 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); break; } diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index b560fe0a15..3e779bbdd3 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -373,9 +373,78 @@ bool RoadVehicle::FindClosestDepot(TileIndex *location, DestinationID *destinati return true; } +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 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) && GetDisallowedRoadDirections(tile) != DRD_NONE) return true; + 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; } @@ -863,8 +932,20 @@ static bool CheckRoadInfraUnsuitableForOvertaking(OvertakeData *od) TrackdirBits red_signals = TrackStatusToRedSignals(ts); // barred level crossing TrackBits trackbits = TrackdirBitsToTrackBits(trackdirbits); - /* Track does not continue along overtaking direction || track has junction || levelcrossing is barred */ - if (!HasBit(trackdirbits, od->trackdir) || (trackbits & ~TRACK_BIT_CROSS) || (red_signals != TRACKDIR_BIT_NONE)) return true; + /* Track does not continue along overtaking direction || levelcrossing is barred */ + 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)) { + 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 false; } @@ -1404,6 +1485,9 @@ 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)) { + /* No turning to/from overtaking lane on one way side road junctions */ + v->SetRoadVehicleOvertaking(0); } else if (++v->overtaking_ctr >= RV_OVERTAKE_TIMEOUT) { /* If overtaking just aborts at a random moment, we can have a out-of-bound problem, * if the vehicle started a corner. To protect that, only allow an abort of