diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 6f52554eea..7a017f4726 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2646,6 +2646,50 @@ static bool TryReserveSafeTrack(const Vehicle* v, TileIndex tile, Trackdir td, b } } +/** + * Query the next order after a certain order index. + * + * @param v The vehicle + * @param order_index [in/out] Index of the current order, returns index of the chosen order + * @return Pointer to the order or NULL if the order chain was completely followed + * without finding a suitable order. + */ +static const Order* GetNextTrainOrder(const Vehicle *v, VehicleOrderID *order_index) +{ + ++(*order_index); + + do { + /* Wrap around. */ + if (*order_index >= v->num_orders) *order_index = 0; + + Order *order = GetVehicleOrder(v, *order_index); + assert(order != NULL); + + switch (order->GetType()) { + case OT_GOTO_DEPOT: + /* Skip service in depot orders when the train doesn't need service. */ + if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) break; + case OT_GOTO_STATION: + case OT_GOTO_WAYPOINT: + return order; + case OT_CONDITIONAL: { + VehicleOrderID next = ProcessConditionalOrder(order, v); + if (next != INVALID_VEH_ORDER_ID) { + *order_index = next; + continue; + } + break; + } + default: + break; + } + + ++(*order_index); + } while (*order_index != v->cur_order_index); + + return NULL; +} + /* choose a track */ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck) { @@ -2739,6 +2783,49 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir if (got_reservation != NULL) *got_reservation = true; + /* Reservation target found and free, check if it is safe. */ + Order cur_order = v->current_order; + TileIndex cur_dest_tile = v->dest_tile; + VehicleOrderID order_index = v->cur_order_index; + while (!IsSafeWaitingPosition(v, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) { + /* Extend reservation until we have found a safe position. */ + DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir); + TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir); + TrackBits reachable = TrackdirBitsToTrackBits((TrackdirBits)(GetTileTrackStatus(next_tile, TRANSPORT_RAIL, 0))) & DiagdirReachesTracks(exitdir); + if (_settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg) { + reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir)); + } + + /* Get next order with destination. */ + const Order *order = GetNextTrainOrder(v, &order_index); + if (order != NULL) { + v->current_order = *order; + UpdateOrderDest(v, order); + PBSTileInfo cur_dest; + DoTrainPathfind(v, next_tile, exitdir, reachable, NULL, true, &cur_dest); + if (cur_dest.tile != INVALID_TILE) { + res_dest = cur_dest; + if (res_dest.okay) continue; + /* Path found, but could not be reserved. */ + FreeTrainTrackReservation(v); + if (mark_stuck) MarkTrainAsStuck(v); + if (got_reservation != NULL) *got_reservation = false; + changed_signal = false; + break; + } + } + /* No order or no safe position found, try any position. */ + if (!TryReserveSafeTrack(v, res_dest.tile, res_dest.trackdir, true)) { + FreeTrainTrackReservation(v); + if (mark_stuck) MarkTrainAsStuck(v); + if (got_reservation != NULL) *got_reservation = false; + changed_signal = false; + } + break; + } + v->current_order = cur_order; + v->dest_tile = cur_dest_tile; + TryReserveRailTrack(v->tile, TrackdirToTrack(GetVehicleTrackdir(v))); if (changed_signal) MarkTileDirtyByTile(tile);