diff --git a/src/lang/english.txt b/src/lang/english.txt index 1688e4077d..be2595216c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1307,6 +1307,9 @@ STR_CONFIG_SETTING_FLIP_DIRECTION_ALL_TRAINS_HELPTEXT :Enable flipping 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_DRIVE_THROUGH_TRAIN_DEPOT :Drive-through train depots: {STRING2} +STR_CONFIG_SETTING_DRIVE_THROUGH_TRAIN_DEPOT_HELPTEXT :Enable emulation of drive-through train depots. Trains can move between depots placed back to back, optionally with intermediary depots in between. Requires the YAPF pathfinder. + STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR :Starting company colour: {STRING2} STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR_HELPTEXT :Choose starting colour for the company @@ -4610,6 +4613,7 @@ STR_VEHICLE_STATUS_TRAIN_STUCK_WAIT_RESTRICTION :{ORANGE}Waiting STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR :{ORANGE}Too far to next destination STR_VEHICLE_STATUS_TRAIN_WAITING_TIMETABLE :{ORANGE}Waiting due to timetable STR_VEHICLE_STATUS_TRAIN_REVERSING :{ORANGE}Reversing, {VELOCITY} +STR_VEHICLE_STATUS_TRAIN_MOVING_DEPOT :{ORANGE}Moving in depot STR_BREAKDOWN_TYPE_CRITICAL :Mechanical failure STR_BREAKDOWN_TYPE_EM_STOP :Emergency stop diff --git a/src/pathfinder/yapf/yapf.h b/src/pathfinder/yapf/yapf.h index af5e966e7f..e5f3d7ccf7 100644 --- a/src/pathfinder/yapf/yapf.h +++ b/src/pathfinder/yapf/yapf.h @@ -86,6 +86,8 @@ FindDepotData YapfTrainFindNearestDepot(const Train *v, int max_distance); */ bool YapfTrainCheckReverse(const Train *v); +bool YapfTrainCheckDepotReverse(const Train *v, TileIndex forward_depot, TileIndex reverse_depot); + /** * Try to extend the reserved path of a train to the nearest safe tile using YAPF. * diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 60b163e958..09c163c73b 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -724,6 +724,22 @@ bool YapfTrainCheckReverse(const Train *v) return reverse; } +bool YapfTrainCheckDepotReverse(const Train *v, TileIndex forward_depot, TileIndex reverse_depot) +{ + typedef bool (*PfnCheckReverseTrain)(const Train*, TileIndex, Trackdir, TileIndex, Trackdir, int); + PfnCheckReverseTrain pfnCheckReverseTrain = CYapfRail1::stCheckReverseTrain; + + /* check if non-default YAPF type needed */ + if (_settings_game.pf.forbid_90_deg) { + pfnCheckReverseTrain = &CYapfRail2::stCheckReverseTrain; // Trackdir, forbid 90-deg + } + + bool reverse = pfnCheckReverseTrain(v, forward_depot, DiagDirToDiagTrackdir(GetRailDepotDirection(forward_depot)), + reverse_depot, DiagDirToDiagTrackdir(GetRailDepotDirection(reverse_depot)), 1); + + return reverse; +} + FindDepotData YapfTrainFindNearestDepot(const Train *v, int max_penalty) { const Train *last_veh = v->Last(); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index be0117c06b..3f6e690e4e 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -133,6 +133,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_SHIP_LOST_COUNTER, XSCF_NULL, 1, 1, "ship_lost_counter", nullptr, nullptr, nullptr }, { XSLFI_BUILD_OBJECT_RATE_LIMIT,XSCF_NULL, 1, 1, "build_object_rate_limit", nullptr, nullptr, nullptr }, { XSLFI_LOCAL_COMPANY, XSCF_IGNORABLE_ALL, 1, 1, "local_company", saveLC, loadLC, nullptr }, + { XSLFI_THROUGH_TRAIN_DEPOT, XSCF_NULL, 1, 1, "drive_through_train_depot", 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 03056bb73f..ec06ab9fae 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -87,6 +87,7 @@ enum SlXvFeatureIndex { XSLFI_SHIP_LOST_COUNTER, ///< Ship lost counter XSLFI_BUILD_OBJECT_RATE_LIMIT, ///< Build object rate limit XSLFI_LOCAL_COMPANY, ///< Local company ID + XSLFI_THROUGH_TRAIN_DEPOT, ///< Drive-through train depots 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 6e2b41a546..96c1c3ac48 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1830,6 +1830,7 @@ static SettingsContainer &GetSettingsTree() routing->Add(new SettingEntry("pf.pathfinder_for_roadvehs")); routing->Add(new SettingEntry("pf.pathfinder_for_ships")); routing->Add(new SettingEntry("pf.reroute_rv_on_layout_change")); + routing->Add(new SettingEntry("vehicle.drive_through_train_depot")); } vehicles->Add(new SettingEntry("order.no_servicing_if_no_breakdowns")); diff --git a/src/settings_type.h b/src/settings_type.h index 098957dc5c..030902c319 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -561,6 +561,7 @@ struct VehicleSettings { 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 + bool drive_through_train_depot; ///< enable drive-through train depot emulation }; /** Settings related to the economy. */ diff --git a/src/table/settings.ini b/src/table/settings.ini index ee5984d9a2..410c8b54f9 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -1706,6 +1706,14 @@ strhelp = STR_CONFIG_SETTING_ROADVEH_ARTICULATED_OVERTAKING_HELPTEXT patxname = ""roadveh_articulated_overtaking.vehicle.roadveh_articulated_overtaking"" cat = SC_BASIC +[SDT_BOOL] +base = GameSettings +var = vehicle.drive_through_train_depot +def = false +str = STR_CONFIG_SETTING_DRIVE_THROUGH_TRAIN_DEPOT +strhelp = STR_CONFIG_SETTING_DRIVE_THROUGH_TRAIN_DEPOT_HELPTEXT +patxname = ""drive_through_train_depot.vehicle.drive_through_train_depot"" + ; station.join_stations [SDT_NULL] length = 1 diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index a89a9bb037..6b72519ddc 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2598,6 +2598,12 @@ static bool CheckTrainStayInDepot(Train *v) return true; } + if (v->reverse_distance > 0) { + v->reverse_distance--; + if (v->reverse_distance == 0) SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); + return true; + } + SigSegState seg_state; if (v->force_proceed == TFP_NONE) { @@ -2628,6 +2634,53 @@ static bool CheckTrainStayInDepot(Train *v) return true; } + if (_settings_game.pf.pathfinder_for_trains == VPF_YAPF && _settings_game.vehicle.drive_through_train_depot) { + const TileIndex depot_tile = v->tile; + const DiagDirection depot_dir = GetRailDepotDirection(depot_tile); + const DiagDirection behind_depot_dir = ReverseDiagDir(depot_dir); + const int depot_z = GetTileMaxZ(depot_tile); + const TileIndexDiffC tile_diff = TileIndexDiffCByDiagDir(behind_depot_dir); + + TileIndex behind_depot_tile = depot_tile; + uint skipped = 0; + + while (true) { + TileIndex tile = AddTileIndexDiffCWrap(behind_depot_tile, tile_diff); + if (tile == INVALID_TILE) break; + if (!IsRailDepotTile(tile)) break; + DiagDirection dir = GetRailDepotDirection(tile); + if (dir != depot_dir && dir != behind_depot_dir) break; + if (!HasBit(v->compatible_railtypes, GetRailType(tile))) break; + if (GetTileMaxZ(tile) != depot_z) break; + behind_depot_tile = tile; + skipped++; + } + + if (skipped > 0 && GetRailDepotDirection(behind_depot_tile) == behind_depot_dir && + YapfTrainCheckDepotReverse(v, depot_tile, behind_depot_tile)) { + Direction direction = DiagDirToDir(behind_depot_dir); + int x = TileX(behind_depot_tile) * TILE_SIZE | _vehicle_initial_x_fract[behind_depot_dir]; + int y = TileY(behind_depot_tile) * TILE_SIZE | _vehicle_initial_y_fract[behind_depot_dir]; + if (v->gcache.cached_total_length < skipped * TILE_SIZE) { + int delta = (skipped * TILE_SIZE) - v->gcache.cached_total_length; + int speed = max(1, v->GetCurrentMaxSpeed()); + v->reverse_distance = (1 + (((192 * 3 / 2) * delta) / speed)); + SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); + } + + for (Train *u = v; u != nullptr; u = u->Next()) { + u->tile = behind_depot_tile; + u->direction = direction; + u->x_pos = x; + u->y_pos = y; + } + + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, behind_depot_tile); + return true; + } + } + /* Only leave when we can reserve a path to our destination. */ if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->force_proceed == TFP_NONE) { /* No path and no force proceed. */ diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 19627d07a4..f670574615 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -3356,8 +3356,12 @@ public: } else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING) && !mouse_over_start_stop) { str = HasBit(Train::From(v)->flags, VRF_WAITING_RESTRICTION) ? STR_VEHICLE_STATUS_TRAIN_STUCK_WAIT_RESTRICTION : STR_VEHICLE_STATUS_TRAIN_STUCK; } else if (v->type == VEH_TRAIN && Train::From(v)->reverse_distance > 1) { - str = STR_VEHICLE_STATUS_TRAIN_REVERSING; - SetDParam(0, v->GetDisplaySpeed()); + if (Train::From(v)->track == TRACK_BIT_DEPOT) { + str = STR_VEHICLE_STATUS_TRAIN_MOVING_DEPOT; + } else { + str = STR_VEHICLE_STATUS_TRAIN_REVERSING; + SetDParam(0, v->GetDisplaySpeed()); + } } else if (v->type == VEH_AIRCRAFT && HasBit(Aircraft::From(v)->flags, VAF_DEST_TOO_FAR) && !v->current_order.IsType(OT_LOADING)) { str = STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR; } else { // vehicle is in a "normal" state, show current order