diff --git a/src/lang/english.txt b/src/lang/english.txt index e7553de198..b365d73654 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1295,6 +1295,9 @@ STR_CONFIG_SETTING_NO_TRAIN_CRASH_OTHER_COMPANY_HELPTEXT :This setting is STR_CONFIG_SETTING_FLIP_DIRECTION_ALL_TRAINS :All train vehicles may be direction flipped in depot: {STRING2} STR_CONFIG_SETTING_FLIP_DIRECTION_ALL_TRAINS_HELPTEXT :Enable flipping the direction of all train vehicles in depots, even if it is not enabled by the NewGRF. +STR_CONFIG_SETTING_ROADVEH_ARTICULATED_OVERTAKING :Articulated road vehicles may overtake: {STRING2} +STR_CONFIG_SETTING_ROADVEH_ARTICULATED_OVERTAKING_HELPTEXT :Enable articulated road vehicles to overtake other road vehicles. + STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR :Starting company colour: {STRING2} STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR_HELPTEXT :Choose starting colour for the company diff --git a/src/roadveh.h b/src/roadveh.h index 36e97ff76b..d8a2df47e8 100644 --- a/src/roadveh.h +++ b/src/roadveh.h @@ -166,6 +166,18 @@ struct RoadVehicle FINAL : public GroundVehicle { return !this->IsRoadVehicleOnLevelCrossing(); } + inline uint GetOvertakingCounterThreshold() const + { + return RV_OVERTAKE_TIMEOUT + (this->gcache.cached_total_length / 2) - (VEHICLE_LENGTH / 2); + } + + inline void SetRoadVehicleOvertaking(byte overtaking) + { + for (RoadVehicle *u = this; u != nullptr; u = u->Next()) { + u->overtaking = overtaking; + } + } + protected: // These functions should not be called outside acceleration code. /** diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 0c2e79a935..3b2ac82211 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -852,10 +852,10 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u) if (v->roadtype == ROADTYPE_TRAM) return; /* Don't overtake in stations */ - if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return; + if (IsTileType(u->tile, MP_STATION)) return; - /* For now, articulated road vehicles can't overtake anything. */ - if (v->HasArticulatedPart()) return; + /* If not permitted, articulated road vehicles can't overtake anything. */ + if (!_settings_game.vehicle.roadveh_articulated_overtaking && v->HasArticulatedPart()) return; /* Don't overtake if the vehicle is broken or about to break down */ if (v->breakdown_ctr != 0) return; @@ -863,8 +863,19 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u) /* Vehicles are not driving in same direction || direction is not a diagonal direction */ if (v->direction != u->direction || !(v->direction & 1)) return; - /* Check if vehicle is in a road stop, depot, tunnel or bridge or not on a straight road */ - if (v->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(v->state & RVSB_TRACKDIR_MASK))) return; + /* Vehicles chain is too long to overtake */ + if (v->GetOvertakingCounterThreshold() > 255) return; + + for (RoadVehicle *w = v; w != nullptr; w = w->Next()) { + /* Don't overtake in stations */ + if (IsTileType(w->tile, MP_STATION)) return; + + /* Don't overtake if vehicle parts not all in same direction */ + if (w->direction != v->direction) return; + + /* Check if vehicle is in a road stop, depot, tunnel or bridge or not on a straight road */ + if (w->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(w->state & RVSB_TRACKDIR_MASK))) return; + } /* Can't overtake a vehicle that is moving faster than us. If the vehicle in front is * accelerating, take the maximum speed for the comparison, else the current speed. @@ -884,16 +895,24 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u) * - No barred levelcrossing * - No other vehicles in the way */ - od.tile = v->tile; - if (CheckRoadBlockedForOvertaking(&od)) return; - - od.tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction)); - if (CheckRoadBlockedForOvertaking(&od)) return; + uint tile_count = 1 + CeilDiv(v->gcache.cached_total_length, TILE_SIZE); + TileIndex check_tile = v->tile; + TileIndexDiff check_tile_diff = TileOffsByDiagDir(DirToDiagDir(v->direction)); + for (; tile_count != 0; tile_count--, check_tile += check_tile_diff) { + od.tile = check_tile; + if (CheckRoadBlockedForOvertaking(&od)) return; + } + tile_count = v->gcache.cached_total_length / TILE_SIZE; + check_tile = v->tile - check_tile_diff; + for (; tile_count != 0; tile_count--, check_tile -= check_tile_diff) { + od.tile = check_tile; + if (CheckRoadBlockedForOvertaking(&od)) return; + } /* When the vehicle in front of us is stopped we may only take * half the time to pass it than when the vehicle is moving. */ v->overtaking_ctr = (od.u->cur_speed == 0 || od.u->IsRoadVehicleStopped()) ? RV_OVERTAKE_TIMEOUT / 2 : 0; - v->overtaking = RVSB_DRIVE_SIDE; + v->SetRoadVehicleOvertaking(RVSB_DRIVE_SIDE); } static void RoadZPosAffectSpeed(RoadVehicle *v, int old_z) @@ -1187,16 +1206,19 @@ static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadBits r) bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) { SCOPE_INFO_FMT([&], "IndividualRoadVehicleController: %s, %s", scope_dumper().VehicleInfo(v), scope_dumper().VehicleInfo(prev)); - if (v->overtaking != 0) { + if (v->overtaking != 0 && v->IsFrontEngine()) { if (IsTileType(v->tile, MP_STATION)) { /* Force us to be not overtaking! */ - v->overtaking = 0; + v->SetRoadVehicleOvertaking(0); + } else if (v->HasArticulatedPart() && !IsStraightRoadTrackdir((Trackdir)v->state)) { + /* Articulated RVs may not overtake on corners */ + 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 * overtake if we are on straight roads */ - if (v->state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->state)) { - v->overtaking = 0; + if (v->overtaking_ctr >= v->GetOvertakingCounterThreshold() && v->state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->state)) { + v->SetRoadVehicleOvertaking(0); } } } @@ -1274,7 +1296,7 @@ again: uint start_frame = RVC_DEFAULT_START_FRAME; if (IsReversingRoadTrackdir(dir)) { /* When turning around we can't be overtaking. */ - v->overtaking = 0; + v->SetRoadVehicleOvertaking(0); if (no_advance_tile) { DEBUG(misc, 0, "Road vehicle attempted to turn around on a single road piece bridge head"); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 7e1f5c8b53..19aa3ccb70 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -103,6 +103,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_DUAL_RAIL_TYPES, XSCF_NULL, 1, 1, "dual_rail_types", nullptr, nullptr, nullptr }, { XSLFI_CONSIST_SPEED_RD_FLAG, XSCF_NULL, 1, 1, "consist_speed_rd_flag", nullptr, nullptr, nullptr }, { XSLFI_SAVEGAME_UNIQUE_ID, XSCF_IGNORABLE_ALL, 1, 1, "savegame_unique_id", nullptr, nullptr, nullptr }, + { XSLFI_RV_OVERTAKING, XSCF_NULL, 1, 1, "roadveh_overtaking", 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 7be0db3401..e1027a1a82 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -70,6 +70,7 @@ enum SlXvFeatureIndex { XSLFI_DUAL_RAIL_TYPES, ///< Two rail-types per tile XSLFI_CONSIST_SPEED_RD_FLAG, ///< Consist speed reduction flag XSLFI_SAVEGAME_UNIQUE_ID, ///< Savegame unique ID + XSLFI_RV_OVERTAKING, ///< Roadvehicle overtaking 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_gui.cpp b/src/settings_gui.cpp index 86905b2fbd..e21ef5445c 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1718,6 +1718,7 @@ static SettingsContainer &GetSettingsTree() physics->Add(new SettingEntry("vehicle.smoke_amount")); physics->Add(new SettingEntry("vehicle.plane_speed")); physics->Add(new SettingEntry("vehicle.ship_collision_avoidance")); + physics->Add(new SettingEntry("vehicle.roadveh_articulated_overtaking")); } SettingsPage *routing = vehicles->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES_ROUTING)); diff --git a/src/settings_type.h b/src/settings_type.h index 0e808de2d6..dfb7707772 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -540,6 +540,7 @@ struct VehicleSettings { bool ship_collision_avoidance; ///< ships try to avoid colliding with each other bool no_train_crash_other_company; ///< trains cannot crash with trains from other companies bool flip_direction_all_trains; ///< enable flipping direction in depot for all train engine types + bool roadveh_articulated_overtaking; ///< enable articulated road vehicles overtaking other vehicles }; /** Settings related to the economy. */ diff --git a/src/table/settings.ini b/src/table/settings.ini index faa008f65e..56e1a0b2d1 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -1381,6 +1381,15 @@ strhelp = STR_CONFIG_SETTING_FLIP_DIRECTION_ALL_TRAINS_HELPTEXT patxname = ""flip_direction_all_trains.vehicle.flip_direction_all_trains"" cat = SC_EXPERT +[SDT_BOOL] +base = GameSettings +var = vehicle.roadveh_articulated_overtaking +def = true +str = STR_CONFIG_SETTING_ROADVEH_ARTICULATED_OVERTAKING +strhelp = STR_CONFIG_SETTING_ROADVEH_ARTICULATED_OVERTAKING_HELPTEXT +patxname = ""roadveh_articulated_overtaking.vehicle.roadveh_articulated_overtaking"" +cat = SC_BASIC + ; station.join_stations [SDT_NULL] length = 1