OpenTTD
yapf_ship.cpp
Go to the documentation of this file.
1 /* $Id: yapf_ship.cpp 27362 2015-08-08 10:06:24Z alberth $ */
2 
3 /*
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/>.
8  */
9 
12 #include "../../stdafx.h"
13 #include "../../ship.h"
14 
15 #include "yapf.hpp"
16 #include "yapf_node_ship.hpp"
17 
18 #include "../../safeguards.h"
19 
21 template <class Types>
23 {
24 public:
25  typedef typename Types::Tpf Tpf;
26  typedef typename Types::TrackFollower TrackFollower;
27  typedef typename Types::NodeList::Titem Node;
28  typedef typename Node::Key Key;
29 
30 protected:
32  inline Tpf& Yapf()
33  {
34  return *static_cast<Tpf *>(this);
35  }
36 
37 public:
43  inline void PfFollowNode(Node &old_node)
44  {
45  TrackFollower F(Yapf().GetVehicle());
46  if (F.Follow(old_node.m_key.m_tile, old_node.m_key.m_td)) {
47  Yapf().AddMultipleNodes(&old_node, F);
48  }
49  }
50 
52  inline char TransportTypeChar() const
53  {
54  return 'w';
55  }
56 
57  static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
58  {
59  /* handle special case - when next tile is destination tile */
60  if (tile == v->dest_tile) {
61  /* convert tracks to trackdirs */
62  TrackdirBits trackdirs = (TrackdirBits)(tracks | ((int)tracks << 8));
63  /* limit to trackdirs reachable from enterdir */
64  trackdirs &= DiagdirReachesTrackdirs(enterdir);
65 
66  /* use vehicle's current direction if that's possible, otherwise use first usable one. */
67  Trackdir veh_dir = v->GetVehicleTrackdir();
68  return ((trackdirs & TrackdirToTrackdirBits(veh_dir)) != 0) ? veh_dir : (Trackdir)FindFirstBit2x64(trackdirs);
69  }
70 
71  /* move back to the old tile/trackdir (where ship is coming from) */
72  TileIndex src_tile = TILE_ADD(tile, TileOffsByDiagDir(ReverseDiagDir(enterdir)));
73  Trackdir trackdir = v->GetVehicleTrackdir();
74  assert(IsValidTrackdir(trackdir));
75 
76  /* convert origin trackdir to TrackdirBits */
77  TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir);
78  /* get available trackdirs on the destination tile */
80 
81  /* create pathfinder instance */
82  Tpf pf;
83  /* set origin and destination nodes */
84  pf.SetOrigin(src_tile, trackdirs);
85  pf.SetDestination(v->dest_tile, dest_trackdirs);
86  /* find best path */
87  path_found = pf.FindPath(v);
88 
89  Trackdir next_trackdir = INVALID_TRACKDIR; // this would mean "path not found"
90 
91  Node *pNode = pf.GetBestNode();
92  if (pNode != NULL) {
93  /* walk through the path back to the origin */
94  Node *pPrevNode = NULL;
95  while (pNode->m_parent != NULL) {
96  pPrevNode = pNode;
97  pNode = pNode->m_parent;
98  }
99  /* return trackdir from the best next node (direct child of origin) */
100  Node &best_next_node = *pPrevNode;
101  assert(best_next_node.GetTile() == tile);
102  next_trackdir = best_next_node.GetTrackdir();
103  }
104  return next_trackdir;
105  }
106 
116  static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2)
117  {
118  /* get available trackdirs on the destination tile */
120 
121  /* create pathfinder instance */
122  Tpf pf;
123  /* set origin and destination nodes */
124  pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2));
125  pf.SetDestination(v->dest_tile, dest_trackdirs);
126  /* find best path */
127  if (!pf.FindPath(v)) return false;
128 
129  Node *pNode = pf.GetBestNode();
130  if (pNode == NULL) return false;
131 
132  /* path was found
133  * walk through the path back to the origin */
134  while (pNode->m_parent != NULL) {
135  pNode = pNode->m_parent;
136  }
137 
138  Trackdir best_trackdir = pNode->GetTrackdir();
139  assert(best_trackdir == td1 || best_trackdir == td2);
140  return best_trackdir == td2;
141  }
142 };
143 
145 template <class Types>
147 {
148 public:
149  typedef typename Types::Tpf Tpf;
150  typedef typename Types::TrackFollower TrackFollower;
151  typedef typename Types::NodeList::Titem Node;
152  typedef typename Node::Key Key;
153 
154 protected:
157  {
158  return *static_cast<Tpf *>(this);
159  }
160 
161 public:
167  inline bool PfCalcCost(Node &n, const TrackFollower *tf)
168  {
169  /* base tile cost depending on distance */
170  int c = IsDiagonalTrackdir(n.GetTrackdir()) ? YAPF_TILE_LENGTH : YAPF_TILE_CORNER_LENGTH;
171  /* additional penalty for curves */
172  if (n.GetTrackdir() != NextTrackdir(n.m_parent->GetTrackdir())) {
173  /* new trackdir does not match the next one when going straight */
174  c += YAPF_TILE_LENGTH;
175  }
176 
177  /* Skipped tile cost for aqueducts. */
178  c += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
179 
180  /* Ocean/canal speed penalty. */
181  const ShipVehicleInfo *svi = ShipVehInfo(Yapf().GetVehicle()->engine_type);
182  byte speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac;
183  if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->m_tiles_skipped) * speed_frac / (256 - speed_frac);
184 
185  /* apply it */
186  n.m_cost = n.m_parent->m_cost + c;
187  return true;
188  }
189 };
190 
195 template <class Tpf_, class Ttrack_follower, class Tnode_list>
197 {
200 
202  typedef Tpf_ Tpf;
204  typedef Ttrack_follower TrackFollower;
206  typedef Tnode_list NodeList;
207  typedef Ship VehicleType;
209  typedef CYapfBaseT<Types> PfBase; // base pathfinder class
210  typedef CYapfFollowShipT<Types> PfFollow; // node follower
211  typedef CYapfOriginTileT<Types> PfOrigin; // origin provider
212  typedef CYapfDestinationTileT<Types> PfDestination; // destination/distance provider
213  typedef CYapfSegmentCostCacheNoneT<Types> PfCache; // segment cost cache provider
214  typedef CYapfCostShipT<Types> PfCost; // cost provider
215 };
216 
217 /* YAPF type 1 - uses TileIndex/Trackdir as Node key, allows 90-deg turns */
218 struct CYapfShip1 : CYapfT<CYapfShip_TypesT<CYapfShip1, CFollowTrackWater , CShipNodeListTrackDir> > {};
219 /* YAPF type 2 - uses TileIndex/DiagDirection as Node key, allows 90-deg turns */
220 struct CYapfShip2 : CYapfT<CYapfShip_TypesT<CYapfShip2, CFollowTrackWater , CShipNodeListExitDir > > {};
221 /* YAPF type 3 - uses TileIndex/Trackdir as Node key, forbids 90-deg turns */
222 struct CYapfShip3 : CYapfT<CYapfShip_TypesT<CYapfShip3, CFollowTrackWaterNo90, CShipNodeListTrackDir> > {};
223 
225 Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
226 {
227  /* default is YAPF type 2 */
228  typedef Trackdir (*PfnChooseShipTrack)(const Ship*, TileIndex, DiagDirection, TrackBits, bool &path_found);
229  PfnChooseShipTrack pfnChooseShipTrack = CYapfShip2::ChooseShipTrack; // default: ExitDir, allow 90-deg
230 
231  /* check if non-default YAPF type needed */
233  pfnChooseShipTrack = &CYapfShip3::ChooseShipTrack; // Trackdir, forbid 90-deg
235  pfnChooseShipTrack = &CYapfShip1::ChooseShipTrack; // Trackdir, allow 90-deg
236  }
237 
238  Trackdir td_ret = pfnChooseShipTrack(v, tile, enterdir, tracks, path_found);
239  return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
240 }
241 
243 {
244  Trackdir td = v->GetVehicleTrackdir();
245  Trackdir td_rev = ReverseTrackdir(td);
246  TileIndex tile = v->tile;
247 
248  typedef bool (*PfnCheckReverseShip)(const Ship*, TileIndex, Trackdir, Trackdir);
249  PfnCheckReverseShip pfnCheckReverseShip = CYapfShip2::CheckShipReverse; // default: ExitDir, allow 90-deg
250 
251  /* check if non-default YAPF type needed */
253  pfnCheckReverseShip = &CYapfShip3::CheckShipReverse; // Trackdir, forbid 90-deg
255  pfnCheckReverseShip = &CYapfShip1::CheckShipReverse; // Trackdir, allow 90-deg
256  }
257 
258  bool reverse = pfnCheckReverseShip(v, tile, td, td_rev);
259 
260  return reverse;
261 }