4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file yapf_ship.cpp Implementation of YAPF for ships. */
12 #include "../../stdafx.h"
13 #include "../../ship.h"
16 #include "yapf_node_ship.hpp"
18 /** YAPF class for ships */
19 template <class Types
>
20 class CYapfShipT
: public Types::Astar
23 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
24 typedef typename
Types::TrackFollower TrackFollower
;
25 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
26 typedef typename
Node::Key Key
; ///< key to hash tables
29 /** to access inherited path finder */
32 return *static_cast<Tpf
*>(this);
36 Station
*m_dest_station
;
37 TileIndex m_dest_tile
;
40 /** return debug report character to identify the transportation type */
41 inline char TransportTypeChar() const
46 /** set the destination tile */
47 void SetDestination(const Ship
*v
)
49 if (v
->current_order
.IsType(OT_GOTO_STATION
)) {
50 m_dest_station
= Station::Get(v
->current_order
.GetDestination());
51 m_dest_tile
= m_dest_station
->GetClosestTile(v
->tile
, STATION_DOCK
);
53 m_dest_station
= NULL
;
54 m_dest_tile
= v
->dest_tile
;
59 * Called by YAPF to move from the given node to the next tile. For each
60 * reachable trackdir on the new tile creates new node, initializes it
61 * and adds it to the open list by calling Yapf().AddNewNode(n)
63 inline void PfFollowNode(Node
& old_node
)
65 TrackFollower
F(Yapf().GetVehicle());
66 if (!F
.Follow(old_node
.GetPos())) return;
68 bool is_choice
= !F
.m_new
.is_single();
69 PathPos pos
= F
.m_new
;
70 for (TrackdirBits rtds
= F
.m_new
.trackdirs
; rtds
!= TRACKDIR_BIT_NONE
; rtds
= KillFirstBit(rtds
)) {
71 pos
.td
= FindFirstTrackdir(rtds
);
72 Node
& n
= *Types::Astar::CreateNewNode(&old_node
, pos
, is_choice
);
73 Yapf().AddNewNode(n
, F
);
78 * Called by YAPF to calculate the cost from the origin to the given node.
79 * Calculates only the cost of given node, adds it to the parent node cost
80 * and stores the result into Node::m_cost member
82 inline bool PfCalcCost(Node
& n
, const TrackFollower
*tf
)
84 /* base tile cost depending on distance */
85 int c
= IsDiagonalTrackdir(n
.GetPos().td
) ? YAPF_TILE_LENGTH
: YAPF_TILE_CORNER_LENGTH
;
86 /* additional penalty for curves */
87 if (n
.GetPos().td
!= NextTrackdir(n
.m_parent
->GetPos().td
)) {
88 /* new trackdir does not match the next one when going straight */
89 c
+= YAPF_TILE_LENGTH
;
92 /* Skipped tile cost for aqueducts. */
93 c
+= YAPF_TILE_LENGTH
* tf
->m_tiles_skipped
;
95 /* Ocean/canal speed penalty. */
96 const ShipVehicleInfo
*svi
= ShipVehInfo(Yapf().GetVehicle()->engine_type
);
97 byte speed_frac
= (GetEffectiveWaterClass(n
.GetPos().tile
) == WATER_CLASS_SEA
) ? svi
->ocean_speed_frac
: svi
->canal_speed_frac
;
98 if (speed_frac
> 0) c
+= YAPF_TILE_LENGTH
* (1 + tf
->m_tiles_skipped
) * speed_frac
/ (256 - speed_frac
);
101 n
.m_cost
= n
.m_parent
->m_cost
+ c
;
105 inline bool PfDetectDestination(const PathPos
&pos
)
107 if (pos
.in_wormhole()) return false;
109 if (m_dest_station
== NULL
) return pos
.tile
== m_dest_tile
;
111 return m_dest_station
->IsDockingTile(pos
.tile
);
114 /** Called by YAPF to detect if node ends in the desired destination */
115 inline bool PfDetectDestination(Node
& n
)
117 return PfDetectDestination(n
.GetPos());
121 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
122 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
124 inline bool PfCalcEstimate(Node
& n
)
126 static const int dg_dir_to_x_offs
[] = {-1, 0, 1, 0};
127 static const int dg_dir_to_y_offs
[] = {0, 1, 0, -1};
128 if (PfDetectDestination(n
)) {
129 n
.m_estimate
= n
.m_cost
;
133 TileIndex tile
= n
.GetPos().tile
;
134 DiagDirection exitdir
= TrackdirToExitdir(n
.GetPos().td
);
135 int x1
= 2 * TileX(tile
) + dg_dir_to_x_offs
[(int)exitdir
];
136 int y1
= 2 * TileY(tile
) + dg_dir_to_y_offs
[(int)exitdir
];
137 int x2
= 2 * TileX(m_dest_tile
);
138 int y2
= 2 * TileY(m_dest_tile
);
139 int dx
= abs(x1
- x2
);
140 int dy
= abs(y1
- y2
);
141 int dmin
= min(dx
, dy
);
142 int dxy
= abs(dx
- dy
);
143 int d
= dmin
* YAPF_TILE_CORNER_LENGTH
+ (dxy
- 1) * (YAPF_TILE_LENGTH
/ 2);
144 n
.m_estimate
= n
.m_cost
+ d
;
145 assert(n
.m_estimate
>= n
.m_parent
->m_estimate
);
150 * Called by YAPF to attach cached or local segment cost data to the given node.
151 * @return true if globally cached data were used or false if local data was used
153 inline bool PfNodeCacheFetch(Node
& n
)
159 * Called by YAPF to flush the cached segment cost data back into cache storage.
160 * Current cache implementation doesn't use that.
162 inline void PfNodeCacheFlush(Node
& n
)
168 * Config struct of YAPF for ships.
169 * Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
171 template <class Tpf_
, class Ttrack_follower
, class TAstar
>
172 struct CYapfShip_TypesT
174 /** Types - shortcut for this struct type */
175 typedef CYapfShip_TypesT
<Tpf_
, Ttrack_follower
, TAstar
> Types
;
177 /** Tpf - pathfinder type */
179 /** track follower helper class */
180 typedef Ttrack_follower TrackFollower
;
181 /** node list type */
182 typedef TAstar Astar
;
183 typedef Ship VehicleType
;
186 /* YAPF type 1 - uses TileIndex/Trackdir as Node key, allows 90-deg turns */
188 : CYapfBaseT
<CYapfShip_TypesT
<CYapfShip1
, CFollowTrackWater90
, AstarShipTrackDir
> >
189 , CYapfShipT
<CYapfShip_TypesT
<CYapfShip1
, CFollowTrackWater90
, AstarShipTrackDir
> >
190 , CYapfOriginTileT
<CYapfShip1
>
194 /* YAPF type 2 - uses TileIndex/DiagDirection as Node key, allows 90-deg turns */
196 : CYapfBaseT
<CYapfShip_TypesT
<CYapfShip2
, CFollowTrackWater90
, AstarShipExitDir
> >
197 , CYapfShipT
<CYapfShip_TypesT
<CYapfShip2
, CFollowTrackWater90
, AstarShipExitDir
> >
198 , CYapfOriginTileT
<CYapfShip2
>
202 /* YAPF type 3 - uses TileIndex/Trackdir as Node key, forbids 90-deg turns */
204 : CYapfBaseT
<CYapfShip_TypesT
<CYapfShip3
, CFollowTrackWaterNo90
, AstarShipTrackDir
> >
205 , CYapfShipT
<CYapfShip_TypesT
<CYapfShip3
, CFollowTrackWaterNo90
, AstarShipTrackDir
> >
206 , CYapfOriginTileT
<CYapfShip3
>
212 static Trackdir
ChooseShipTrack(const Ship
*v
, const PathPos
&pos
, DiagDirection enterdir
, TrackdirBits trackdirs
, bool &path_found
)
214 /* create pathfinder instance */
216 /* set origin and destination nodes */
218 pf
.SetDestination(v
);
220 path_found
= pf
.CYapfBaseT
<typename
Tpf::TT
>::FindPath(v
);
222 typename
Tpf::TT::Astar::Node
*n
= pf
.GetBestNode();
223 if (n
== NULL
) return INVALID_TRACKDIR
; // path not found
224 assert (n
->m_parent
!= NULL
);
226 /* walk through the path back to the origin */
227 while (n
->m_parent
->m_parent
!= NULL
) {
231 /* return trackdir from the best next node (direct child of origin) */
232 assert(n
->m_parent
->GetPos().tile
== pos
.tile
);
233 return n
->GetPos().td
;
236 /** Ship controller helper - path finder invoker */
237 Trackdir
YapfShipChooseTrack(const Ship
*v
, TileIndex tile
, DiagDirection enterdir
, TrackdirBits trackdirs
, bool &path_found
)
239 /* move back to the old tile/trackdir (where ship is coming from) */
240 PathPos pos
= v
->GetPos();
241 assert(pos
.tile
== TileAddByDiagDir (tile
, ReverseDiagDir(enterdir
)));
242 assert(IsValidTrackdir(pos
.td
));
244 /* handle special case - when next tile is destination tile */
245 if (tile
== v
->dest_tile
) {
246 /* use vehicle's current direction if that's possible, otherwise use first usable one. */
247 Trackdir veh_dir
= pos
.td
;
248 return ((trackdirs
& TrackdirToTrackdirBits(veh_dir
)) != 0) ? veh_dir
: FindFirstTrackdir(trackdirs
);
251 /* default is YAPF type 2 */
252 typedef Trackdir (*PfnChooseShipTrack
)(const Ship
*, const PathPos
&, DiagDirection
, TrackdirBits
, bool &path_found
);
253 PfnChooseShipTrack pfnChooseShipTrack
= ChooseShipTrack
<CYapfShip2
>; // default: ExitDir, allow 90-deg
255 /* check if non-default YAPF type needed */
256 if (_settings_game
.pf
.forbid_90_deg
) {
257 pfnChooseShipTrack
= &ChooseShipTrack
<CYapfShip3
>; // Trackdir, forbid 90-deg
258 } else if (_settings_game
.pf
.yapf
.disable_node_optimization
) {
259 pfnChooseShipTrack
= &ChooseShipTrack
<CYapfShip1
>; // Trackdir, allow 90-deg
262 return pfnChooseShipTrack(v
, pos
, enterdir
, trackdirs
, path_found
);
267 * Check whether a ship should reverse to reach its destination.
268 * Called when leaving depot.
270 * @param pos Current position
271 * @return true if the reverse direction is better
274 static bool CheckShipReverse(const Ship
*v
, const PathPos
&pos
)
276 /* create pathfinder instance */
278 /* set origin and destination nodes */
279 pf
.SetOrigin(pos
.tile
, TrackdirToTrackdirBits(pos
.td
) | TrackdirToTrackdirBits(ReverseTrackdir(pos
.td
)));
280 pf
.SetDestination(v
);
282 if (!pf
.CYapfBaseT
<typename
Tpf::TT
>::FindPath(v
)) return false;
284 typename
Tpf::TT::Astar::Node
*n
= pf
.GetBestNode();
285 if (n
== NULL
) return false;
287 /* path was found; walk through the path back to the origin */
288 while (n
->m_parent
!= NULL
) {
292 Trackdir best_trackdir
= n
->GetPos().td
;
293 assert(best_trackdir
== pos
.td
|| best_trackdir
== ReverseTrackdir(pos
.td
));
294 return best_trackdir
!= pos
.td
;
297 bool YapfShipCheckReverse(const Ship
*v
)
299 PathPos pos
= v
->GetPos();
301 typedef bool (*PfnCheckReverseShip
)(const Ship
*, const PathPos
&);
302 PfnCheckReverseShip pfnCheckReverseShip
= CheckShipReverse
<CYapfShip2
>; // default: ExitDir, allow 90-deg
304 /* check if non-default YAPF type needed */
305 if (_settings_game
.pf
.forbid_90_deg
) {
306 pfnCheckReverseShip
= &CheckShipReverse
<CYapfShip3
>; // Trackdir, forbid 90-deg
307 } else if (_settings_game
.pf
.yapf
.disable_node_optimization
) {
308 pfnCheckReverseShip
= &CheckShipReverse
<CYapfShip1
>; // Trackdir, allow 90-deg
311 bool reverse
= pfnCheckReverseShip(v
, pos
);