yapf_ship.cpp

Go to the documentation of this file.
00001 /* $Id: yapf_ship.cpp 24481 2012-08-18 11:37:47Z frosch $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #include "../../stdafx.h"
00013 #include "../../ship.h"
00014 
00015 #include "yapf.hpp"
00016 #include "yapf_node_ship.hpp"
00017 
00019 template <class Types>
00020 class CYapfFollowShipT
00021 {
00022 public:
00023   typedef typename Types::Tpf Tpf;                     
00024   typedef typename Types::TrackFollower TrackFollower;
00025   typedef typename Types::NodeList::Titem Node;        
00026   typedef typename Node::Key Key;                      
00027 
00028 protected:
00030   inline Tpf& Yapf()
00031   {
00032     return *static_cast<Tpf*>(this);
00033   }
00034 
00035 public:
00041   inline void PfFollowNode(Node& old_node)
00042   {
00043     TrackFollower F(Yapf().GetVehicle());
00044     if (F.Follow(old_node.m_key.m_tile, old_node.m_key.m_td)) {
00045       Yapf().AddMultipleNodes(&old_node, F);
00046     }
00047   }
00048 
00050   inline char TransportTypeChar() const
00051   {
00052     return 'w';
00053   }
00054 
00055   static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
00056   {
00057     /* handle special case - when next tile is destination tile */
00058     if (tile == v->dest_tile) {
00059       /* convert tracks to trackdirs */
00060       TrackdirBits trackdirs = (TrackdirBits)(tracks | ((int)tracks << 8));
00061       /* choose any trackdir reachable from enterdir */
00062       trackdirs &= DiagdirReachesTrackdirs(enterdir);
00063       return (Trackdir)FindFirstBit2x64(trackdirs);
00064     }
00065 
00066     /* move back to the old tile/trackdir (where ship is coming from) */
00067     TileIndex src_tile = TILE_ADD(tile, TileOffsByDiagDir(ReverseDiagDir(enterdir)));
00068     Trackdir trackdir = v->GetVehicleTrackdir();
00069     assert(IsValidTrackdir(trackdir));
00070 
00071     /* convert origin trackdir to TrackdirBits */
00072     TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir);
00073     /* get available trackdirs on the destination tile */
00074     TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
00075 
00076     /* create pathfinder instance */
00077     Tpf pf;
00078     /* set origin and destination nodes */
00079     pf.SetOrigin(src_tile, trackdirs);
00080     pf.SetDestination(v->dest_tile, dest_trackdirs);
00081     /* find best path */
00082     path_found = pf.FindPath(v);
00083 
00084     Trackdir next_trackdir = INVALID_TRACKDIR; // this would mean "path not found"
00085 
00086     Node *pNode = pf.GetBestNode();
00087     if (pNode != NULL) {
00088       /* walk through the path back to the origin */
00089       Node *pPrevNode = NULL;
00090       while (pNode->m_parent != NULL) {
00091         pPrevNode = pNode;
00092         pNode = pNode->m_parent;
00093       }
00094       /* return trackdir from the best next node (direct child of origin) */
00095       Node& best_next_node = *pPrevNode;
00096       assert(best_next_node.GetTile() == tile);
00097       next_trackdir = best_next_node.GetTrackdir();
00098     }
00099     return next_trackdir;
00100   }
00101 
00111   static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2)
00112   {
00113     /* get available trackdirs on the destination tile */
00114     TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
00115 
00116     /* create pathfinder instance */
00117     Tpf pf;
00118     /* set origin and destination nodes */
00119     pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2));
00120     pf.SetDestination(v->dest_tile, dest_trackdirs);
00121     /* find best path */
00122     if (!pf.FindPath(v)) return false;
00123 
00124     Node *pNode = pf.GetBestNode();
00125     if (pNode == NULL) return false;
00126 
00127     /* path was found
00128      * walk through the path back to the origin */
00129     while (pNode->m_parent != NULL) {
00130       pNode = pNode->m_parent;
00131     }
00132 
00133     Trackdir best_trackdir = pNode->GetTrackdir();
00134     assert(best_trackdir == td1 || best_trackdir == td2);
00135     return best_trackdir == td2;
00136   }
00137 };
00138 
00140 template <class Types>
00141 class CYapfCostShipT
00142 {
00143 public:
00144   typedef typename Types::Tpf Tpf;              
00145   typedef typename Types::TrackFollower TrackFollower;
00146   typedef typename Types::NodeList::Titem Node; 
00147   typedef typename Node::Key Key;               
00148 
00149 protected:
00151   Tpf& Yapf()
00152   {
00153     return *static_cast<Tpf*>(this);
00154   }
00155 
00156 public:
00162   inline bool PfCalcCost(Node& n, const TrackFollower *tf)
00163   {
00164     /* base tile cost depending on distance */
00165     int c = IsDiagonalTrackdir(n.GetTrackdir()) ? YAPF_TILE_LENGTH : YAPF_TILE_CORNER_LENGTH;
00166     /* additional penalty for curves */
00167     if (n.m_parent != NULL && n.GetTrackdir() != NextTrackdir(n.m_parent->GetTrackdir())) {
00168       /* new trackdir does not match the next one when going straight */
00169       c += YAPF_TILE_LENGTH;
00170     }
00171 
00172     /* Skipped tile cost for aqueducts. */
00173     c += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
00174 
00175     /* Ocean/canal speed penalty. */
00176     const ShipVehicleInfo *svi = ShipVehInfo(Yapf().GetVehicle()->engine_type);
00177     byte speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac;
00178     if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->m_tiles_skipped) * speed_frac / (256 - speed_frac);
00179 
00180     /* apply it */
00181     n.m_cost = n.m_parent->m_cost + c;
00182     return true;
00183   }
00184 };
00185 
00190 template <class Tpf_, class Ttrack_follower, class Tnode_list>
00191 struct CYapfShip_TypesT
00192 {
00194   typedef CYapfShip_TypesT<Tpf_, Ttrack_follower, Tnode_list>  Types;
00195 
00197   typedef Tpf_                              Tpf;
00199   typedef Ttrack_follower                   TrackFollower;
00201   typedef Tnode_list                        NodeList;
00202   typedef Ship                              VehicleType;
00204   typedef CYapfBaseT<Types>                 PfBase;        // base pathfinder class
00205   typedef CYapfFollowShipT<Types>           PfFollow;      // node follower
00206   typedef CYapfOriginTileT<Types>           PfOrigin;      // origin provider
00207   typedef CYapfDestinationTileT<Types>      PfDestination; // destination/distance provider
00208   typedef CYapfSegmentCostCacheNoneT<Types> PfCache;       // segment cost cache provider
00209   typedef CYapfCostShipT<Types>             PfCost;        // cost provider
00210 };
00211 
00212 /* YAPF type 1 - uses TileIndex/Trackdir as Node key, allows 90-deg turns */
00213 struct CYapfShip1 : CYapfT<CYapfShip_TypesT<CYapfShip1, CFollowTrackWater    , CShipNodeListTrackDir> > {};
00214 /* YAPF type 2 - uses TileIndex/DiagDirection as Node key, allows 90-deg turns */
00215 struct CYapfShip2 : CYapfT<CYapfShip_TypesT<CYapfShip2, CFollowTrackWater    , CShipNodeListExitDir > > {};
00216 /* YAPF type 3 - uses TileIndex/Trackdir as Node key, forbids 90-deg turns */
00217 struct CYapfShip3 : CYapfT<CYapfShip_TypesT<CYapfShip3, CFollowTrackWaterNo90, CShipNodeListTrackDir> > {};
00218 
00220 Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
00221 {
00222   /* default is YAPF type 2 */
00223   typedef Trackdir (*PfnChooseShipTrack)(const Ship*, TileIndex, DiagDirection, TrackBits, bool &path_found);
00224   PfnChooseShipTrack pfnChooseShipTrack = CYapfShip2::ChooseShipTrack; // default: ExitDir, allow 90-deg
00225 
00226   /* check if non-default YAPF type needed */
00227   if (_settings_game.pf.forbid_90_deg) {
00228     pfnChooseShipTrack = &CYapfShip3::ChooseShipTrack; // Trackdir, forbid 90-deg
00229   } else if (_settings_game.pf.yapf.disable_node_optimization) {
00230     pfnChooseShipTrack = &CYapfShip1::ChooseShipTrack; // Trackdir, allow 90-deg
00231   }
00232 
00233   Trackdir td_ret = pfnChooseShipTrack(v, tile, enterdir, tracks, path_found);
00234   return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
00235 }
00236 
00237 bool YapfShipCheckReverse(const Ship *v)
00238 {
00239   Trackdir td = v->GetVehicleTrackdir();
00240   Trackdir td_rev = ReverseTrackdir(td);
00241   TileIndex tile = v->tile;
00242 
00243   typedef bool (*PfnCheckReverseShip)(const Ship*, TileIndex, Trackdir, Trackdir);
00244   PfnCheckReverseShip pfnCheckReverseShip = CYapfShip2::CheckShipReverse; // default: ExitDir, allow 90-deg
00245 
00246   /* check if non-default YAPF type needed */
00247   if (_settings_game.pf.forbid_90_deg) {
00248     pfnCheckReverseShip = &CYapfShip3::CheckShipReverse; // Trackdir, forbid 90-deg
00249   } else if (_settings_game.pf.yapf.disable_node_optimization) {
00250     pfnCheckReverseShip = &CYapfShip1::CheckShipReverse; // Trackdir, allow 90-deg
00251   }
00252 
00253   bool reverse = pfnCheckReverseShip(v, tile, td, td_rev);
00254 
00255   return reverse;
00256 }