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_road.cpp The road pathfinding. */
10 #include "../../stdafx.h"
12 #include "yapf_node_road.hpp"
13 #include "../../roadstop_base.h"
15 #include "../../safeguards.h"
18 template <class Types
>
22 typedef typename
Types::Tpf Tpf
; ///< pathfinder (derived from THIS class)
23 typedef typename
Types::TrackFollower TrackFollower
; ///< track follower helper
24 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
25 typedef typename
Node::Key Key
; ///< key to hash tables
30 CYapfCostRoadT() : m_max_cost(0) {};
32 /** to access inherited path finder */
35 return *static_cast<Tpf
*>(this);
38 int SlopeCost(TileIndex tile
, TileIndex next_tile
, Trackdir trackdir
)
40 /* height of the center of the current tile */
41 int x1
= TileX(tile
) * TILE_SIZE
;
42 int y1
= TileY(tile
) * TILE_SIZE
;
43 int z1
= GetSlopePixelZ(x1
+ TILE_SIZE
/ 2, y1
+ TILE_SIZE
/ 2);
45 /* height of the center of the next tile */
46 int x2
= TileX(next_tile
) * TILE_SIZE
;
47 int y2
= TileY(next_tile
) * TILE_SIZE
;
48 int z2
= GetSlopePixelZ(x2
+ TILE_SIZE
/ 2, y2
+ TILE_SIZE
/ 2);
52 return Yapf().PfGetSettings().road_slope_penalty
;
57 /** return one tile cost */
58 inline int OneTileCost(TileIndex tile
, Trackdir trackdir
)
62 if (IsDiagonalTrackdir(trackdir
)) {
63 cost
+= YAPF_TILE_LENGTH
;
64 switch (GetTileType(tile
)) {
66 /* Increase the cost for level crossings */
67 if (IsLevelCrossing(tile
)) {
68 cost
+= Yapf().PfGetSettings().road_crossing_penalty
;
73 const RoadStop
*rs
= RoadStop::GetByTile(tile
, GetRoadStopType(tile
));
74 if (IsDriveThroughStopTile(tile
)) {
75 /* Increase the cost for drive-through road stops */
76 cost
+= Yapf().PfGetSettings().road_stop_penalty
;
77 DiagDirection dir
= TrackdirToExitdir(trackdir
);
78 if (!RoadStop::IsDriveThroughRoadStopContinuation(tile
, tile
- TileOffsByDiagDir(dir
))) {
79 /* When we're the first road stop in a 'queue' of them we increase
80 * cost based on the fill percentage of the whole queue. */
81 const RoadStop::Entry
*entry
= rs
->GetEntry(dir
);
82 cost
+= entry
->GetOccupied() * Yapf().PfGetSettings().road_stop_occupied_penalty
/ entry
->GetLength();
85 /* Increase cost for filled road stops */
86 cost
+= Yapf().PfGetSettings().road_stop_bay_occupied_penalty
* (!rs
->IsFreeBay(0) + !rs
->IsFreeBay(1)) / 2;
95 /* non-diagonal trackdir */
96 cost
= YAPF_TILE_CORNER_LENGTH
+ Yapf().PfGetSettings().road_curve_penalty
;
102 inline void SetMaxCost(int max_cost
)
104 m_max_cost
= max_cost
;
108 * Called by YAPF to calculate the cost from the origin to the given node.
109 * Calculates only the cost of given node, adds it to the parent node cost
110 * and stores the result into Node::m_cost member
112 inline bool PfCalcCost(Node
&n
, const TrackFollower
*tf
)
114 int segment_cost
= 0;
116 /* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */
117 TileIndex tile
= n
.m_key
.m_tile
;
118 Trackdir trackdir
= n
.m_key
.m_td
;
119 int parent_cost
= (n
.m_parent
!= nullptr) ? n
.m_parent
->m_cost
: 0;
122 /* base tile cost depending on distance between edges */
123 segment_cost
+= Yapf().OneTileCost(tile
, trackdir
);
125 const RoadVehicle
*v
= Yapf().GetVehicle();
126 /* we have reached the vehicle's destination - segment should end here to avoid target skipping */
127 if (Yapf().PfDetectDestinationTile(tile
, trackdir
)) break;
129 /* Finish if we already exceeded the maximum path cost (i.e. when
130 * searching for the nearest depot). */
131 if (m_max_cost
> 0 && (parent_cost
+ segment_cost
) > m_max_cost
) {
135 /* stop if we have just entered the depot */
136 if (IsRoadDepotTile(tile
) && trackdir
== DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile
)))) {
137 /* next time we will reverse and leave the depot */
141 /* if there are no reachable trackdirs on new tile, we have end of road */
142 TrackFollower
F(Yapf().GetVehicle());
143 if (!F
.Follow(tile
, trackdir
)) break;
145 /* if there are more trackdirs available & reachable, we are at the end of segment */
146 if (KillFirstBit(F
.m_new_td_bits
) != TRACKDIR_BIT_NONE
) break;
148 Trackdir new_td
= (Trackdir
)FindFirstBit2x64(F
.m_new_td_bits
);
150 /* stop if RV is on simple loop with no junctions */
151 if (F
.m_new_tile
== n
.m_key
.m_tile
&& new_td
== n
.m_key
.m_td
) return false;
153 /* if we skipped some tunnel tiles, add their cost */
154 segment_cost
+= F
.m_tiles_skipped
* YAPF_TILE_LENGTH
;
155 tiles
+= F
.m_tiles_skipped
+ 1;
157 /* add hilly terrain penalty */
158 segment_cost
+= Yapf().SlopeCost(tile
, F
.m_new_tile
, trackdir
);
160 /* add min/max speed penalties */
162 int max_veh_speed
= v
->GetDisplayMaxSpeed();
163 int max_speed
= F
.GetSpeedLimit(&min_speed
);
164 if (max_speed
< max_veh_speed
) segment_cost
+= 1 * (max_veh_speed
- max_speed
);
165 if (min_speed
> max_veh_speed
) segment_cost
+= 10 * (min_speed
- max_veh_speed
);
167 /* move to the next tile */
170 if (tiles
> MAX_MAP_SIZE
) break;
173 /* save end of segment back to the node */
174 n
.m_segment_last_tile
= tile
;
175 n
.m_segment_last_td
= trackdir
;
177 /* save also tile cost */
178 n
.m_cost
= parent_cost
+ segment_cost
;
184 template <class Types
>
185 class CYapfDestinationAnyDepotRoadT
188 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
189 typedef typename
Types::TrackFollower TrackFollower
;
190 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
191 typedef typename
Node::Key Key
; ///< key to hash tables
193 /** to access inherited path finder */
196 return *static_cast<Tpf
*>(this);
199 /** Called by YAPF to detect if node ends in the desired destination */
200 inline bool PfDetectDestination(Node
&n
)
202 return IsRoadDepotTile(n
.m_segment_last_tile
);
205 inline bool PfDetectDestinationTile(TileIndex tile
, Trackdir trackdir
)
207 return IsRoadDepotTile(tile
);
211 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
212 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
214 inline bool PfCalcEstimate(Node
&n
)
216 n
.m_estimate
= n
.m_cost
;
222 template <class Types
>
223 class CYapfDestinationTileRoadT
226 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
227 typedef typename
Types::TrackFollower TrackFollower
;
228 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
229 typedef typename
Node::Key Key
; ///< key to hash tables
232 TileIndex m_destTile
;
233 TrackdirBits m_destTrackdirs
;
234 StationID m_dest_station
;
239 void SetDestination(const RoadVehicle
*v
)
241 if (v
->current_order
.IsType(OT_GOTO_STATION
)) {
242 m_dest_station
= v
->current_order
.GetDestination();
244 m_destTile
= CalcClosestStationTile(m_dest_station
, v
->tile
, m_bus
? STATION_BUS
: STATION_TRUCK
);
245 m_non_artic
= !v
->HasArticulatedPart();
246 m_destTrackdirs
= INVALID_TRACKDIR_BIT
;
248 m_dest_station
= INVALID_STATION
;
249 m_destTile
= v
->dest_tile
;
250 m_destTrackdirs
= TrackStatusToTrackdirBits(GetTileTrackStatus(v
->dest_tile
, TRANSPORT_ROAD
, GetRoadTramType(v
->roadtype
)));
254 const Station
*GetDestinationStation() const
256 return m_dest_station
!= INVALID_STATION
? Station::GetIfValid(m_dest_station
) : nullptr;
260 /** to access inherited path finder */
263 return *static_cast<Tpf
*>(this);
267 /** Called by YAPF to detect if node ends in the desired destination */
268 inline bool PfDetectDestination(Node
&n
)
270 return PfDetectDestinationTile(n
.m_segment_last_tile
, n
.m_segment_last_td
);
273 inline bool PfDetectDestinationTile(TileIndex tile
, Trackdir trackdir
)
275 if (m_dest_station
!= INVALID_STATION
) {
276 return IsTileType(tile
, MP_STATION
) &&
277 GetStationIndex(tile
) == m_dest_station
&&
278 (m_bus
? IsBusStop(tile
) : IsTruckStop(tile
)) &&
279 (m_non_artic
|| IsDriveThroughStopTile(tile
));
282 return tile
== m_destTile
&& HasTrackdir(m_destTrackdirs
, trackdir
);
286 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
287 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
289 inline bool PfCalcEstimate(Node
&n
)
291 static const int dg_dir_to_x_offs
[] = {-1, 0, 1, 0};
292 static const int dg_dir_to_y_offs
[] = {0, 1, 0, -1};
293 if (PfDetectDestination(n
)) {
294 n
.m_estimate
= n
.m_cost
;
298 TileIndex tile
= n
.m_segment_last_tile
;
299 DiagDirection exitdir
= TrackdirToExitdir(n
.m_segment_last_td
);
300 int x1
= 2 * TileX(tile
) + dg_dir_to_x_offs
[(int)exitdir
];
301 int y1
= 2 * TileY(tile
) + dg_dir_to_y_offs
[(int)exitdir
];
302 int x2
= 2 * TileX(m_destTile
);
303 int y2
= 2 * TileY(m_destTile
);
304 int dx
= abs(x1
- x2
);
305 int dy
= abs(y1
- y2
);
306 int dmin
= min(dx
, dy
);
307 int dxy
= abs(dx
- dy
);
308 int d
= dmin
* YAPF_TILE_CORNER_LENGTH
+ (dxy
- 1) * (YAPF_TILE_LENGTH
/ 2);
309 n
.m_estimate
= n
.m_cost
+ d
;
310 assert(n
.m_estimate
>= n
.m_parent
->m_estimate
);
317 template <class Types
>
318 class CYapfFollowRoadT
321 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
322 typedef typename
Types::TrackFollower TrackFollower
;
323 typedef typename
Types::NodeList::Titem Node
; ///< this will be our node type
324 typedef typename
Node::Key Key
; ///< key to hash tables
327 /** to access inherited path finder */
330 return *static_cast<Tpf
*>(this);
336 * Called by YAPF to move from the given node to the next tile. For each
337 * reachable trackdir on the new tile creates new node, initializes it
338 * and adds it to the open list by calling Yapf().AddNewNode(n)
340 inline void PfFollowNode(Node
&old_node
)
342 TrackFollower
F(Yapf().GetVehicle());
343 if (F
.Follow(old_node
.m_segment_last_tile
, old_node
.m_segment_last_td
)) {
344 Yapf().AddMultipleNodes(&old_node
, F
);
348 /** return debug report character to identify the transportation type */
349 inline char TransportTypeChar() const
354 static Trackdir
stChooseRoadTrack(const RoadVehicle
*v
, TileIndex tile
, DiagDirection enterdir
, bool &path_found
, RoadVehPathCache
&path_cache
)
357 return pf
.ChooseRoadTrack(v
, tile
, enterdir
, path_found
, path_cache
);
360 inline Trackdir
ChooseRoadTrack(const RoadVehicle
*v
, TileIndex tile
, DiagDirection enterdir
, bool &path_found
, RoadVehPathCache
&path_cache
)
362 /* Handle special case - when next tile is destination tile.
363 * However, when going to a station the (initial) destination
364 * tile might not be a station, but a junction, in which case
365 * this method forces the vehicle to jump in circles. */
366 if (tile
== v
->dest_tile
&& !v
->current_order
.IsType(OT_GOTO_STATION
)) {
367 /* choose diagonal trackdir reachable from enterdir */
368 return DiagDirToDiagTrackdir(enterdir
);
370 /* our source tile will be the next vehicle tile (should be the given one) */
371 TileIndex src_tile
= tile
;
372 /* get available trackdirs on the start tile */
373 TrackdirBits src_trackdirs
= TrackStatusToTrackdirBits(GetTileTrackStatus(tile
, TRANSPORT_ROAD
, GetRoadTramType(v
->roadtype
)));
374 /* select reachable trackdirs only */
375 src_trackdirs
&= DiagdirReachesTrackdirs(enterdir
);
377 /* set origin and destination nodes */
378 Yapf().SetOrigin(src_tile
, src_trackdirs
);
379 Yapf().SetDestination(v
);
381 /* find the best path */
382 path_found
= Yapf().FindPath(v
);
384 /* if path not found - return INVALID_TRACKDIR */
385 Trackdir next_trackdir
= INVALID_TRACKDIR
;
386 Node
*pNode
= Yapf().GetBestNode();
387 if (pNode
!= nullptr) {
389 for (Node
*n
= pNode
; n
->m_parent
!= nullptr; n
= n
->m_parent
) steps
++;
391 /* path was found or at least suggested
392 * walk through the path back to its origin */
393 while (pNode
->m_parent
!= nullptr) {
395 if (pNode
->GetIsChoice() && steps
< YAPF_ROADVEH_PATH_CACHE_SEGMENTS
) {
396 path_cache
.td
.push_front(pNode
->GetTrackdir());
397 path_cache
.tile
.push_front(pNode
->GetTile());
399 pNode
= pNode
->m_parent
;
401 /* return trackdir from the best origin node (one of start nodes) */
402 Node
&best_next_node
= *pNode
;
403 assert(best_next_node
.GetTile() == tile
);
404 next_trackdir
= best_next_node
.GetTrackdir();
405 /* remove last element for the special case when tile == dest_tile */
406 if (path_found
&& !path_cache
.empty() && tile
== v
->dest_tile
) {
407 path_cache
.td
.pop_back();
408 path_cache
.tile
.pop_back();
411 /* Check if target is a station, and cached path ends within 8 tiles of the dest tile */
412 const Station
*st
= Yapf().GetDestinationStation();
414 const RoadStop
*stop
= st
->GetPrimaryRoadStop(v
);
415 if (stop
!= nullptr && (IsDriveThroughStopTile(stop
->xy
) || stop
->GetNextRoadStop(v
) != nullptr)) {
416 /* Destination station has at least 2 usable road stops, or first is a drive-through stop,
417 * trim end of path cache within a number of tiles of road stop tile area */
418 TileArea non_cached_area
= v
->IsBus() ? st
->bus_station
: st
->truck_station
;
419 non_cached_area
.Expand(YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT
);
420 while (!path_cache
.empty() && non_cached_area
.Contains(path_cache
.tile
.back())) {
421 path_cache
.td
.pop_back();
422 path_cache
.tile
.pop_back();
427 return next_trackdir
;
430 static uint
stDistanceToTile(const RoadVehicle
*v
, TileIndex tile
)
433 return pf
.DistanceToTile(v
, tile
);
436 inline uint
DistanceToTile(const RoadVehicle
*v
, TileIndex dst_tile
)
438 /* handle special case - when current tile is the destination tile */
439 if (dst_tile
== v
->tile
) {
440 /* distance is zero in this case */
444 if (!SetOriginFromVehiclePos(v
)) return UINT_MAX
;
446 /* get available trackdirs on the destination tile */
447 Yapf().SetDestination(v
);
449 /* if path not found - return distance = UINT_MAX */
450 uint dist
= UINT_MAX
;
452 /* find the best path */
453 if (!Yapf().FindPath(v
)) return dist
;
455 Node
*pNode
= Yapf().GetBestNode();
456 if (pNode
!= nullptr) {
458 * get the path cost estimate */
459 dist
= pNode
->GetCostEstimate();
465 /** Return true if the valid origin (tile/trackdir) was set from the current vehicle position. */
466 inline bool SetOriginFromVehiclePos(const RoadVehicle
*v
)
468 /* set origin (tile, trackdir) */
469 TileIndex src_tile
= v
->tile
;
470 Trackdir src_td
= v
->GetVehicleTrackdir();
471 if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile
, TRANSPORT_ROAD
, this->IsTram() ? RTT_TRAM
: RTT_ROAD
)), src_td
)) {
472 /* sometimes the roadveh is not on the road (it resides on non-existing track)
473 * how should we handle that situation? */
476 Yapf().SetOrigin(src_tile
, TrackdirToTrackdirBits(src_td
));
480 static FindDepotData
stFindNearestDepot(const RoadVehicle
*v
, TileIndex tile
, Trackdir td
, int max_distance
)
483 return pf
.FindNearestDepot(v
, tile
, td
, max_distance
);
487 * Find the best depot for a road vehicle.
489 * @param tile Tile of the vehicle.
490 * @param td Trackdir of the vehicle.
491 * @param max_distance max length (penalty) for paths.
493 inline FindDepotData
FindNearestDepot(const RoadVehicle
*v
, TileIndex tile
, Trackdir td
, int max_distance
)
496 Yapf().SetOrigin(tile
, TrackdirToTrackdirBits(td
));
497 Yapf().SetMaxCost(max_distance
);
499 /* Find the best path and return if no depot is found. */
500 if (!Yapf().FindPath(v
)) return FindDepotData();
502 /* Return the cost of the best path and its depot. */
503 Node
*n
= Yapf().GetBestNode();
504 return FindDepotData(n
->m_segment_last_tile
, n
->m_cost
);
508 template <class Tpf_
, class Tnode_list
, template <class Types
> class Tdestination
>
509 struct CYapfRoad_TypesT
511 typedef CYapfRoad_TypesT
<Tpf_
, Tnode_list
, Tdestination
> Types
;
514 typedef CFollowTrackRoad TrackFollower
;
515 typedef Tnode_list NodeList
;
516 typedef RoadVehicle VehicleType
;
517 typedef CYapfBaseT
<Types
> PfBase
;
518 typedef CYapfFollowRoadT
<Types
> PfFollow
;
519 typedef CYapfOriginTileT
<Types
> PfOrigin
;
520 typedef Tdestination
<Types
> PfDestination
;
521 typedef CYapfSegmentCostCacheNoneT
<Types
> PfCache
;
522 typedef CYapfCostRoadT
<Types
> PfCost
;
525 struct CYapfRoad1
: CYapfT
<CYapfRoad_TypesT
<CYapfRoad1
, CRoadNodeListTrackDir
, CYapfDestinationTileRoadT
> > {};
526 struct CYapfRoad2
: CYapfT
<CYapfRoad_TypesT
<CYapfRoad2
, CRoadNodeListExitDir
, CYapfDestinationTileRoadT
> > {};
528 struct CYapfRoadAnyDepot1
: CYapfT
<CYapfRoad_TypesT
<CYapfRoadAnyDepot1
, CRoadNodeListTrackDir
, CYapfDestinationAnyDepotRoadT
> > {};
529 struct CYapfRoadAnyDepot2
: CYapfT
<CYapfRoad_TypesT
<CYapfRoadAnyDepot2
, CRoadNodeListExitDir
, CYapfDestinationAnyDepotRoadT
> > {};
532 Trackdir
YapfRoadVehicleChooseTrack(const RoadVehicle
*v
, TileIndex tile
, DiagDirection enterdir
, TrackdirBits trackdirs
, bool &path_found
, RoadVehPathCache
&path_cache
)
534 /* default is YAPF type 2 */
535 typedef Trackdir (*PfnChooseRoadTrack
)(const RoadVehicle
*, TileIndex
, DiagDirection
, bool &path_found
, RoadVehPathCache
&path_cache
);
536 PfnChooseRoadTrack pfnChooseRoadTrack
= &CYapfRoad2::stChooseRoadTrack
; // default: ExitDir, allow 90-deg
538 /* check if non-default YAPF type should be used */
539 if (_settings_game
.pf
.yapf
.disable_node_optimization
) {
540 pfnChooseRoadTrack
= &CYapfRoad1::stChooseRoadTrack
; // Trackdir
543 Trackdir td_ret
= pfnChooseRoadTrack(v
, tile
, enterdir
, path_found
, path_cache
);
544 return (td_ret
!= INVALID_TRACKDIR
) ? td_ret
: (Trackdir
)FindFirstBit2x64(trackdirs
);
547 FindDepotData
YapfRoadVehicleFindNearestDepot(const RoadVehicle
*v
, int max_distance
)
549 TileIndex tile
= v
->tile
;
550 Trackdir trackdir
= v
->GetVehicleTrackdir();
551 if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(tile
, TRANSPORT_ROAD
, GetRoadTramType(v
->roadtype
))), trackdir
)) {
552 return FindDepotData();
555 /* default is YAPF type 2 */
556 typedef FindDepotData (*PfnFindNearestDepot
)(const RoadVehicle
*, TileIndex
, Trackdir
, int);
557 PfnFindNearestDepot pfnFindNearestDepot
= &CYapfRoadAnyDepot2::stFindNearestDepot
;
559 /* check if non-default YAPF type should be used */
560 if (_settings_game
.pf
.yapf
.disable_node_optimization
) {
561 pfnFindNearestDepot
= &CYapfRoadAnyDepot1::stFindNearestDepot
; // Trackdir
564 return pfnFindNearestDepot(v
, tile
, trackdir
, max_distance
);