2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file yapf_rail.cpp The rail pathfinding. */
10 #include "../../stdafx.h"
13 #include "yapf_cache.h"
14 #include "yapf_node_rail.hpp"
15 #include "yapf_costrail.hpp"
16 #include "yapf_destrail.hpp"
17 #include "../../viewport_func.h"
18 #include "../../newgrf_station.h"
20 #include "../../safeguards.h"
22 template <typename Tpf
> void DumpState(Tpf
&pf1
, Tpf
&pf2
)
24 DumpTarget dmp1
, dmp2
;
27 auto f1
= FileHandle::Open("yapf1.txt", "wt");
28 auto f2
= FileHandle::Open("yapf2.txt", "wt");
29 assert(f1
.has_value());
30 assert(f2
.has_value());
31 fwrite(dmp1
.m_out
.c_str(), 1, dmp1
.m_out
.size(), *f1
);
32 fwrite(dmp2
.m_out
.c_str(), 1, dmp2
.m_out
.size(), *f2
);
35 template <class Types
>
36 class CYapfReserveTrack
39 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
40 typedef typename
Types::TrackFollower TrackFollower
;
41 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
44 /** to access inherited pathfinder */
47 return *static_cast<Tpf
*>(this);
51 TileIndex m_res_dest
; ///< The reservation target tile
52 Trackdir m_res_dest_td
; ///< The reservation target trackdir
53 Node
*m_res_node
; ///< The reservation target node
54 TileIndex m_res_fail_tile
; ///< The tile where the reservation failed
55 Trackdir m_res_fail_td
; ///< The trackdir where the reservation failed
56 TileIndex m_origin_tile
; ///< Tile our reservation will originate from
58 std::vector
<std::pair
<TileIndex
, Trackdir
>> m_signals_set_to_red
; ///< List of signals turned red during a path reservation.
60 bool FindSafePositionProc(TileIndex tile
, Trackdir td
)
62 if (IsSafeWaitingPosition(Yapf().GetVehicle(), tile
, td
, true, !TrackFollower::Allow90degTurns())) {
65 return false; // Stop iterating segment
70 /** Reserve a railway platform. Tile contains the failed tile on abort. */
71 bool ReserveRailStationPlatform(TileIndex
&tile
, DiagDirection dir
)
73 TileIndex start
= tile
;
74 TileIndexDiff diff
= TileOffsByDiagDir(dir
);
77 if (HasStationReservation(tile
)) return false;
78 SetRailStationReservation(tile
, true);
79 MarkTileDirtyByTile(tile
);
80 tile
= TileAdd(tile
, diff
);
81 } while (IsCompatibleTrainStationTile(tile
, start
) && tile
!= m_origin_tile
);
83 TriggerStationRandomisation(nullptr, start
, SRT_PATH_RESERVATION
);
88 /** Try to reserve a single track/platform. */
89 bool ReserveSingleTrack(TileIndex tile
, Trackdir td
)
91 Trackdir rev_td
= ReverseTrackdir(td
);
92 if (IsRailStationTile(tile
)) {
93 if (!ReserveRailStationPlatform(tile
, TrackdirToExitdir(rev_td
))) {
94 /* Platform could not be reserved, undo. */
95 m_res_fail_tile
= tile
;
99 if (!TryReserveRailTrack(tile
, TrackdirToTrack(td
))) {
100 /* Tile couldn't be reserved, undo. */
101 m_res_fail_tile
= tile
;
106 /* Green path signal opposing the path? Turn to red. */
107 if (HasPbsSignalOnTrackdir(tile
, rev_td
) && GetSignalStateByTrackdir(tile
, rev_td
) == SIGNAL_STATE_GREEN
) {
108 m_signals_set_to_red
.emplace_back(tile
, rev_td
);
109 SetSignalStateByTrackdir(tile
, rev_td
, SIGNAL_STATE_RED
);
110 MarkTileDirtyByTile(tile
);
114 return tile
!= m_res_dest
|| td
!= m_res_dest_td
;
117 /** Unreserve a single track/platform. Stops when the previous failer is reached. */
118 bool UnreserveSingleTrack(TileIndex tile
, Trackdir td
)
120 if (IsRailStationTile(tile
)) {
121 TileIndex start
= tile
;
122 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(td
)));
123 while ((tile
!= m_res_fail_tile
|| td
!= m_res_fail_td
) && IsCompatibleTrainStationTile(tile
, start
)) {
124 SetRailStationReservation(tile
, false);
125 tile
= TileAdd(tile
, diff
);
127 } else if (tile
!= m_res_fail_tile
|| td
!= m_res_fail_td
) {
128 UnreserveRailTrack(tile
, TrackdirToTrack(td
));
130 return (tile
!= m_res_dest
|| td
!= m_res_dest_td
) && (tile
!= m_res_fail_tile
|| td
!= m_res_fail_td
);
134 /** Set the target to where the reservation should be extended. */
135 inline void SetReservationTarget(Node
*node
, TileIndex tile
, Trackdir td
)
142 /** Check the node for a possible reservation target. */
143 inline void FindSafePositionOnNode(Node
*node
)
145 assert(node
->m_parent
!= nullptr);
147 /* We will never pass more than two signals, no need to check for a safe tile. */
148 if (node
->m_parent
->m_num_signals_passed
>= 2) return;
150 if (!node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::FindSafePositionProc
)) {
155 /** Try to reserve the path till the reservation target. */
156 bool TryReservePath(PBSTileInfo
*target
, TileIndex origin
)
158 m_res_fail_tile
= INVALID_TILE
;
159 m_origin_tile
= origin
;
161 if (target
!= nullptr) {
162 target
->tile
= m_res_dest
;
163 target
->trackdir
= m_res_dest_td
;
164 target
->okay
= false;
167 /* Don't bother if the target is reserved. */
168 if (!IsWaitingPositionFree(Yapf().GetVehicle(), m_res_dest
, m_res_dest_td
)) return false;
170 m_signals_set_to_red
.clear();
171 for (Node
*node
= m_res_node
; node
->m_parent
!= nullptr; node
= node
->m_parent
) {
172 node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::ReserveSingleTrack
);
173 if (m_res_fail_tile
!= INVALID_TILE
) {
174 /* Reservation failed, undo. */
175 Node
*fail_node
= m_res_node
;
176 TileIndex stop_tile
= m_res_fail_tile
;
178 /* If this is the node that failed, stop at the failed tile. */
179 m_res_fail_tile
= fail_node
== node
? stop_tile
: INVALID_TILE
;
180 fail_node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::UnreserveSingleTrack
);
181 } while (fail_node
!= node
&& (fail_node
= fail_node
->m_parent
) != nullptr);
183 /* Re-instate green path signals we turned to red. */
184 for (auto [sig_tile
, td
] : m_signals_set_to_red
) {
185 SetSignalStateByTrackdir(sig_tile
, td
, SIGNAL_STATE_GREEN
);
192 if (target
!= nullptr) target
->okay
= true;
194 if (Yapf().CanUseGlobalCache(*m_res_node
)) {
195 YapfNotifyTrackLayoutChange(INVALID_TILE
, INVALID_TRACK
);
202 template <class Types
>
203 class CYapfFollowAnyDepotRailT
206 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
207 typedef typename
Types::TrackFollower TrackFollower
;
208 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
209 typedef typename
Node::Key Key
; ///< key to hash tables
212 /** to access inherited path finder */
215 return *static_cast<Tpf
*>(this);
220 * Called by YAPF to move from the given node to the next tile. For each
221 * reachable trackdir on the new tile creates new node, initializes it
222 * and adds it to the open list by calling Yapf().AddNewNode(n)
224 inline void PfFollowNode(Node
&old_node
)
226 TrackFollower
F(Yapf().GetVehicle());
227 if (F
.Follow(old_node
.GetLastTile(), old_node
.GetLastTrackdir())) {
228 Yapf().AddMultipleNodes(&old_node
, F
);
232 /** return debug report character to identify the transportation type */
233 inline char TransportTypeChar() const
238 static FindDepotData
stFindNearestDepotTwoWay(const Train
*v
, TileIndex t1
, Trackdir td1
, TileIndex t2
, Trackdir td2
, int max_penalty
, int reverse_penalty
)
242 * With caching enabled it simply cannot get a reliable result when you
243 * have limited the distance a train may travel. This means that the
244 * cached result does not match uncached result in all cases and that
245 * causes desyncs. So disable caching when finding for a depot that is
246 * nearby. This only happens with automatic servicing of vehicles,
247 * so it will only impact performance when you do not manually set
248 * depot orders and you do not disable automatic servicing.
250 if (max_penalty
!= 0) pf1
.DisableCache(true);
251 FindDepotData result1
= pf1
.FindNearestDepotTwoWay(v
, t1
, td1
, t2
, td2
, max_penalty
, reverse_penalty
);
253 if (_debug_desync_level
>= 2) {
255 pf2
.DisableCache(true);
256 FindDepotData result2
= pf2
.FindNearestDepotTwoWay(v
, t1
, td1
, t2
, td2
, max_penalty
, reverse_penalty
);
257 if (result1
.tile
!= result2
.tile
|| (result1
.reverse
!= result2
.reverse
)) {
258 Debug(desync
, 2, "warning: FindNearestDepotTwoWay cache mismatch: {} vs {}",
259 result1
.tile
!= INVALID_TILE
? "T" : "F",
260 result2
.tile
!= INVALID_TILE
? "T" : "F");
268 inline FindDepotData
FindNearestDepotTwoWay(const Train
*v
, TileIndex t1
, Trackdir td1
, TileIndex t2
, Trackdir td2
, int max_penalty
, int reverse_penalty
)
270 /* set origin and destination nodes */
271 Yapf().SetOrigin(t1
, td1
, t2
, td2
, reverse_penalty
, true);
272 Yapf().SetDestination(v
);
273 Yapf().SetMaxCost(max_penalty
);
275 /* find the best path */
276 if (!Yapf().FindPath(v
)) return FindDepotData();
278 /* Some path found. */
279 Node
*n
= Yapf().GetBestNode();
281 /* walk through the path back to the origin */
283 while (pNode
->m_parent
!= nullptr) {
284 pNode
= pNode
->m_parent
;
287 /* if the origin node is our front vehicle tile/Trackdir then we didn't reverse
288 * but we can also look at the cost (== 0 -> not reversed, == reverse_penalty -> reversed) */
289 return FindDepotData(n
->GetLastTile(), n
->m_cost
, pNode
->m_cost
!= 0);
293 template <class Types
>
294 class CYapfFollowAnySafeTileRailT
: public CYapfReserveTrack
<Types
>
297 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
298 typedef typename
Types::TrackFollower TrackFollower
;
299 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
300 typedef typename
Node::Key Key
; ///< key to hash tables
303 /** to access inherited path finder */
306 return *static_cast<Tpf
*>(this);
311 * Called by YAPF to move from the given node to the next tile. For each
312 * reachable trackdir on the new tile creates new node, initializes it
313 * and adds it to the open list by calling Yapf().AddNewNode(n)
315 inline void PfFollowNode(Node
&old_node
)
317 TrackFollower
F(Yapf().GetVehicle(), Yapf().GetCompatibleRailTypes());
318 if (F
.Follow(old_node
.GetLastTile(), old_node
.GetLastTrackdir()) && F
.MaskReservedTracks()) {
319 Yapf().AddMultipleNodes(&old_node
, F
);
323 /** Return debug report character to identify the transportation type */
324 inline char TransportTypeChar() const
329 static bool stFindNearestSafeTile(const Train
*v
, TileIndex t1
, Trackdir td
, bool override_railtype
)
331 /* Create pathfinder instance */
334 if (_debug_desync_level
< 2) {
335 result1
= pf1
.FindNearestSafeTile(v
, t1
, td
, override_railtype
, false);
337 bool result2
= pf1
.FindNearestSafeTile(v
, t1
, td
, override_railtype
, true);
339 pf2
.DisableCache(true);
340 result1
= pf2
.FindNearestSafeTile(v
, t1
, td
, override_railtype
, false);
341 if (result1
!= result2
) {
342 Debug(desync
, 2, "warning: FindSafeTile cache mismatch: {} vs {}", result2
? "T" : "F", result1
? "T" : "F");
350 bool FindNearestSafeTile(const Train
*v
, TileIndex t1
, Trackdir td
, bool override_railtype
, bool dont_reserve
)
352 /* Set origin and destination. */
353 Yapf().SetOrigin(t1
, td
);
354 Yapf().SetDestination(v
, override_railtype
);
356 bool bFound
= Yapf().FindPath(v
);
357 if (!bFound
) return false;
359 /* Found a destination, set as reservation target. */
360 Node
*pNode
= Yapf().GetBestNode();
361 this->SetReservationTarget(pNode
, pNode
->GetLastTile(), pNode
->GetLastTrackdir());
363 /* Walk through the path back to the origin. */
364 Node
*pPrev
= nullptr;
365 while (pNode
->m_parent
!= nullptr) {
367 pNode
= pNode
->m_parent
;
369 this->FindSafePositionOnNode(pPrev
);
372 return dont_reserve
|| this->TryReservePath(nullptr, pNode
->GetLastTile());
376 template <class Types
>
377 class CYapfFollowRailT
: public CYapfReserveTrack
<Types
>
380 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
381 typedef typename
Types::TrackFollower TrackFollower
;
382 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
383 typedef typename
Node::Key Key
; ///< key to hash tables
386 /** to access inherited path finder */
389 return *static_cast<Tpf
*>(this);
394 * Called by YAPF to move from the given node to the next tile. For each
395 * reachable trackdir on the new tile creates new node, initializes it
396 * and adds it to the open list by calling Yapf().AddNewNode(n)
398 inline void PfFollowNode(Node
&old_node
)
400 TrackFollower
F(Yapf().GetVehicle());
401 if (F
.Follow(old_node
.GetLastTile(), old_node
.GetLastTrackdir())) {
402 Yapf().AddMultipleNodes(&old_node
, F
);
406 /** return debug report character to identify the transportation type */
407 inline char TransportTypeChar() const
412 static Trackdir
stChooseRailTrack(const Train
*v
, TileIndex tile
, DiagDirection enterdir
, TrackBits tracks
, bool &path_found
, bool reserve_track
, PBSTileInfo
*target
, TileIndex
*dest
)
414 /* create pathfinder instance */
418 if (_debug_desync_level
< 2) {
419 result1
= pf1
.ChooseRailTrack(v
, tile
, enterdir
, tracks
, path_found
, reserve_track
, target
, dest
);
421 result1
= pf1
.ChooseRailTrack(v
, tile
, enterdir
, tracks
, path_found
, false, nullptr, nullptr);
423 pf2
.DisableCache(true);
424 Trackdir result2
= pf2
.ChooseRailTrack(v
, tile
, enterdir
, tracks
, path_found
, reserve_track
, target
, dest
);
425 if (result1
!= result2
) {
426 Debug(desync
, 2, "warning: ChooseRailTrack cache mismatch: {} vs {}", result1
, result2
);
434 inline Trackdir
ChooseRailTrack(const Train
*v
, TileIndex
, DiagDirection
, TrackBits
, bool &path_found
, bool reserve_track
, PBSTileInfo
*target
, TileIndex
*dest
)
436 if (target
!= nullptr) target
->tile
= INVALID_TILE
;
437 if (dest
!= nullptr) *dest
= INVALID_TILE
;
439 /* set origin and destination nodes */
440 PBSTileInfo origin
= FollowTrainReservation(v
);
441 Yapf().SetOrigin(origin
.tile
, origin
.trackdir
, INVALID_TILE
, INVALID_TRACKDIR
, 1, true);
442 Yapf().SetDestination(v
);
444 /* find the best path */
445 path_found
= Yapf().FindPath(v
);
447 /* if path not found - return INVALID_TRACKDIR */
448 Trackdir next_trackdir
= INVALID_TRACKDIR
;
449 Node
*pNode
= Yapf().GetBestNode();
450 if (pNode
!= nullptr) {
451 /* reserve till end of path */
452 this->SetReservationTarget(pNode
, pNode
->GetLastTile(), pNode
->GetLastTrackdir());
454 /* path was found or at least suggested
455 * walk through the path back to the origin */
456 Node
*pPrev
= nullptr;
457 while (pNode
->m_parent
!= nullptr) {
459 pNode
= pNode
->m_parent
;
461 this->FindSafePositionOnNode(pPrev
);
464 /* If the best PF node has no parent, then there is no (valid) best next trackdir to return.
465 * This occurs when the PF is called while the train is already at its destination. */
466 if (pPrev
== nullptr) return INVALID_TRACKDIR
;
468 /* return trackdir from the best origin node (one of start nodes) */
469 Node
&best_next_node
= *pPrev
;
470 next_trackdir
= best_next_node
.GetTrackdir();
472 if (reserve_track
&& path_found
) {
473 if (dest
!= nullptr) *dest
= Yapf().GetBestNode()->GetLastTile();
474 this->TryReservePath(target
, pNode
->GetLastTile());
478 /* Treat the path as found if stopped on the first two way signal(s). */
479 path_found
|= Yapf().m_stopped_on_first_two_way_signal
;
480 return next_trackdir
;
483 static bool stCheckReverseTrain(const Train
*v
, TileIndex t1
, Trackdir td1
, TileIndex t2
, Trackdir td2
, int reverse_penalty
)
486 bool result1
= pf1
.CheckReverseTrain(v
, t1
, td1
, t2
, td2
, reverse_penalty
);
488 if (_debug_desync_level
>= 2) {
490 pf2
.DisableCache(true);
491 bool result2
= pf2
.CheckReverseTrain(v
, t1
, td1
, t2
, td2
, reverse_penalty
);
492 if (result1
!= result2
) {
493 Debug(desync
, 2, "warning: CheckReverseTrain cache mismatch: {} vs {}", result1
? "T" : "F", result2
? "T" : "F");
501 inline bool CheckReverseTrain(const Train
*v
, TileIndex t1
, Trackdir td1
, TileIndex t2
, Trackdir td2
, int reverse_penalty
)
503 /* create pathfinder instance
504 * set origin and destination nodes */
505 Yapf().SetOrigin(t1
, td1
, t2
, td2
, reverse_penalty
, false);
506 Yapf().SetDestination(v
);
508 /* find the best path */
509 bool bFound
= Yapf().FindPath(v
);
511 if (!bFound
) return false;
514 * walk through the path back to the origin */
515 Node
*pNode
= Yapf().GetBestNode();
516 while (pNode
->m_parent
!= nullptr) {
517 pNode
= pNode
->m_parent
;
520 /* check if it was reversed origin */
521 Node
&best_org_node
= *pNode
;
522 bool reversed
= (best_org_node
.m_cost
!= 0);
527 template <class Tpf_
, class Ttrack_follower
, class Tnode_list
, template <class Types
> class TdestinationT
, template <class Types
> class TfollowT
>
528 struct CYapfRail_TypesT
530 typedef CYapfRail_TypesT
<Tpf_
, Ttrack_follower
, Tnode_list
, TdestinationT
, TfollowT
> Types
;
533 typedef Ttrack_follower TrackFollower
;
534 typedef Tnode_list NodeList
;
535 typedef Train VehicleType
;
536 typedef CYapfBaseT
<Types
> PfBase
;
537 typedef TfollowT
<Types
> PfFollow
;
538 typedef CYapfOriginTileTwoWayT
<Types
> PfOrigin
;
539 typedef TdestinationT
<Types
> PfDestination
;
540 typedef CYapfSegmentCostCacheGlobalT
<Types
> PfCache
;
541 typedef CYapfCostRailT
<Types
> PfCost
;
544 struct CYapfRail1
: CYapfT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail
, CRailNodeListTrackDir
, CYapfDestinationTileOrStationRailT
, CYapfFollowRailT
> > {};
545 struct CYapfRail2
: CYapfT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
, CRailNodeListTrackDir
, CYapfDestinationTileOrStationRailT
, CYapfFollowRailT
> > {};
547 struct CYapfAnyDepotRail1
: CYapfT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail
, CRailNodeListTrackDir
, CYapfDestinationAnyDepotRailT
, CYapfFollowAnyDepotRailT
> > {};
548 struct CYapfAnyDepotRail2
: CYapfT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
, CRailNodeListTrackDir
, CYapfDestinationAnyDepotRailT
, CYapfFollowAnyDepotRailT
> > {};
550 struct CYapfAnySafeTileRail1
: CYapfT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail
, CRailNodeListTrackDir
, CYapfDestinationAnySafeTileRailT
, CYapfFollowAnySafeTileRailT
> > {};
551 struct CYapfAnySafeTileRail2
: CYapfT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
, CRailNodeListTrackDir
, CYapfDestinationAnySafeTileRailT
, CYapfFollowAnySafeTileRailT
> > {};
554 Track
YapfTrainChooseTrack(const Train
*v
, TileIndex tile
, DiagDirection enterdir
, TrackBits tracks
, bool &path_found
, bool reserve_track
, PBSTileInfo
*target
, TileIndex
*dest
)
556 /* default is YAPF type 2 */
557 typedef Trackdir (*PfnChooseRailTrack
)(const Train
*, TileIndex
, DiagDirection
, TrackBits
, bool&, bool, PBSTileInfo
*, TileIndex
*);
558 PfnChooseRailTrack pfnChooseRailTrack
= &CYapfRail1::stChooseRailTrack
;
560 /* check if non-default YAPF type needed */
561 if (_settings_game
.pf
.forbid_90_deg
) {
562 pfnChooseRailTrack
= &CYapfRail2::stChooseRailTrack
; // Trackdir, forbid 90-deg
565 Trackdir td_ret
= pfnChooseRailTrack(v
, tile
, enterdir
, tracks
, path_found
, reserve_track
, target
, dest
);
566 return (td_ret
!= INVALID_TRACKDIR
) ? TrackdirToTrack(td_ret
) : FindFirstTrack(tracks
);
569 bool YapfTrainCheckReverse(const Train
*v
)
571 const Train
*last_veh
= v
->Last();
573 /* get trackdirs of both ends */
574 Trackdir td
= v
->GetVehicleTrackdir();
575 Trackdir td_rev
= ReverseTrackdir(last_veh
->GetVehicleTrackdir());
577 /* tiles where front and back are */
578 TileIndex tile
= v
->tile
;
579 TileIndex tile_rev
= last_veh
->tile
;
581 int reverse_penalty
= 0;
583 if (v
->track
== TRACK_BIT_WORMHOLE
) {
584 /* front in tunnel / on bridge */
585 DiagDirection dir_into_wormhole
= GetTunnelBridgeDirection(tile
);
587 if (TrackdirToExitdir(td
) == dir_into_wormhole
) tile
= GetOtherTunnelBridgeEnd(tile
);
588 /* Now 'tile' is the tunnel entry/bridge ramp the train will reach when driving forward */
590 /* Current position of the train in the wormhole */
591 TileIndex cur_tile
= TileVirtXY(v
->x_pos
, v
->y_pos
);
593 /* Add distance to drive in the wormhole as penalty for the forward path, i.e. bonus for the reverse path
594 * Note: Negative penalties are ok for the start tile. */
595 reverse_penalty
-= DistanceManhattan(cur_tile
, tile
) * YAPF_TILE_LENGTH
;
598 if (last_veh
->track
== TRACK_BIT_WORMHOLE
) {
599 /* back in tunnel / on bridge */
600 DiagDirection dir_into_wormhole
= GetTunnelBridgeDirection(tile_rev
);
602 if (TrackdirToExitdir(td_rev
) == dir_into_wormhole
) tile_rev
= GetOtherTunnelBridgeEnd(tile_rev
);
603 /* Now 'tile_rev' is the tunnel entry/bridge ramp the train will reach when reversing */
605 /* Current position of the last wagon in the wormhole */
606 TileIndex cur_tile
= TileVirtXY(last_veh
->x_pos
, last_veh
->y_pos
);
608 /* Add distance to drive in the wormhole as penalty for the revere path. */
609 reverse_penalty
+= DistanceManhattan(cur_tile
, tile_rev
) * YAPF_TILE_LENGTH
;
612 typedef bool (*PfnCheckReverseTrain
)(const Train
*, TileIndex
, Trackdir
, TileIndex
, Trackdir
, int);
613 PfnCheckReverseTrain pfnCheckReverseTrain
= CYapfRail1::stCheckReverseTrain
;
615 /* check if non-default YAPF type needed */
616 if (_settings_game
.pf
.forbid_90_deg
) {
617 pfnCheckReverseTrain
= &CYapfRail2::stCheckReverseTrain
; // Trackdir, forbid 90-deg
620 /* slightly hackish: If the pathfinders finds a path, the cost of the first node is tested to distinguish between forward- and reverse-path. */
621 if (reverse_penalty
== 0) reverse_penalty
= 1;
623 bool reverse
= pfnCheckReverseTrain(v
, tile
, td
, tile_rev
, td_rev
, reverse_penalty
);
628 FindDepotData
YapfTrainFindNearestDepot(const Train
*v
, int max_penalty
)
630 const Train
*last_veh
= v
->Last();
632 PBSTileInfo origin
= FollowTrainReservation(v
);
633 TileIndex last_tile
= last_veh
->tile
;
634 Trackdir td_rev
= ReverseTrackdir(last_veh
->GetVehicleTrackdir());
636 typedef FindDepotData (*PfnFindNearestDepotTwoWay
)(const Train
*, TileIndex
, Trackdir
, TileIndex
, Trackdir
, int, int);
637 PfnFindNearestDepotTwoWay pfnFindNearestDepotTwoWay
= &CYapfAnyDepotRail1::stFindNearestDepotTwoWay
;
639 /* check if non-default YAPF type needed */
640 if (_settings_game
.pf
.forbid_90_deg
) {
641 pfnFindNearestDepotTwoWay
= &CYapfAnyDepotRail2::stFindNearestDepotTwoWay
; // Trackdir, forbid 90-deg
644 return pfnFindNearestDepotTwoWay(v
, origin
.tile
, origin
.trackdir
, last_tile
, td_rev
, max_penalty
, YAPF_INFINITE_PENALTY
);
647 bool YapfTrainFindNearestSafeTile(const Train
*v
, TileIndex tile
, Trackdir td
, bool override_railtype
)
649 typedef bool (*PfnFindNearestSafeTile
)(const Train
*, TileIndex
, Trackdir
, bool);
650 PfnFindNearestSafeTile pfnFindNearestSafeTile
= CYapfAnySafeTileRail1::stFindNearestSafeTile
;
652 /* check if non-default YAPF type needed */
653 if (_settings_game
.pf
.forbid_90_deg
) {
654 pfnFindNearestSafeTile
= &CYapfAnySafeTileRail2::stFindNearestSafeTile
;
657 return pfnFindNearestSafeTile(v
, tile
, td
, override_railtype
);
660 /** if any track changes, this counter is incremented - that will invalidate segment cost cache */
661 int CSegmentCostCacheBase::s_rail_change_counter
= 0;
663 void YapfNotifyTrackLayoutChange(TileIndex tile
, Track track
)
665 CSegmentCostCacheBase::NotifyTrackLayoutChange(tile
, track
);