# yapf_ship.cpp

Go to the documentation of this file.

    00001 /* $Id: yapf_ship.cpp 24964 2013-02-03 14:16:45Z rubidium $ */
    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       /* limit to trackdirs reachable from enterdir */
    00062       trackdirs &= DiagdirReachesTrackdirs(enterdir);
    00063
    00064       /* use vehicle's current direction if that's possible, otherwise use first usable one. */
    00065       Trackdir veh_dir = v->GetVehicleTrackdir();
    00066       return ((trackdirs & TrackdirToTrackdirBits(veh_dir)) != 0) ? veh_dir : (Trackdir)FindFirstBit2x64(trackdirs);
    00067     }
    00068
    00069     /* move back to the old tile/trackdir (where ship is coming from) */
    00070     TileIndex src_tile = TILE_ADD(tile, TileOffsByDiagDir(ReverseDiagDir(enterdir)));
    00071     Trackdir trackdir = v->GetVehicleTrackdir();
    00072     assert(IsValidTrackdir(trackdir));
    00073
    00074     /* convert origin trackdir to TrackdirBits */
    00075     TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir);
    00076     /* get available trackdirs on the destination tile */
    00077     TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
    00078
    00079     /* create pathfinder instance */
    00080     Tpf pf;
    00081     /* set origin and destination nodes */
    00082     pf.SetOrigin(src_tile, trackdirs);
    00083     pf.SetDestination(v->dest_tile, dest_trackdirs);
    00084     /* find best path */
    00085     path_found = pf.FindPath(v);
    00086
    00087     Trackdir next_trackdir = INVALID_TRACKDIR; // this would mean "path not found"
    00088
    00089     Node *pNode = pf.GetBestNode();
    00090     if (pNode != NULL) {
    00091       /* walk through the path back to the origin */
    00092       Node *pPrevNode = NULL;
    00093       while (pNode->m_parent != NULL) {
    00094         pPrevNode = pNode;
    00095         pNode = pNode->m_parent;
    00096       }
    00097       /* return trackdir from the best next node (direct child of origin) */
    00098       Node& best_next_node = *pPrevNode;
    00099       assert(best_next_node.GetTile() == tile);
    00100       next_trackdir = best_next_node.GetTrackdir();
    00101     }
    00102     return next_trackdir;
    00103   }
    00104
    00114   static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2)
    00115   {
    00116     /* get available trackdirs on the destination tile */
    00117     TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
    00118
    00119     /* create pathfinder instance */
    00120     Tpf pf;
    00121     /* set origin and destination nodes */
    00122     pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2));
    00123     pf.SetDestination(v->dest_tile, dest_trackdirs);
    00124     /* find best path */
    00125     if (!pf.FindPath(v)) return false;
    00126
    00127     Node *pNode = pf.GetBestNode();
    00128     if (pNode == NULL) return false;
    00129
    00130     /* path was found
    00131      * walk through the path back to the origin */
    00132     while (pNode->m_parent != NULL) {
    00133       pNode = pNode->m_parent;
    00134     }
    00135
    00136     Trackdir best_trackdir = pNode->GetTrackdir();
    00137     assert(best_trackdir == td1 || best_trackdir == td2);
    00138     return best_trackdir == td2;
    00139   }
    00140 };
    00141
    00143 template <class Types>
    00144 class CYapfCostShipT
    00145 {
    00146 public:
    00147   typedef typename Types::Tpf Tpf;
    00148   typedef typename Types::TrackFollower TrackFollower;
    00149   typedef typename Types::NodeList::Titem Node;
    00150   typedef typename Node::Key Key;
    00151
    00152 protected:
    00154   Tpf& Yapf()
    00155   {
    00156     return *static_cast<Tpf*>(this);
    00157   }
    00158
    00159 public:
    00165   inline bool PfCalcCost(Node& n, const TrackFollower *tf)
    00166   {
    00167     /* base tile cost depending on distance */
    00168     int c = IsDiagonalTrackdir(n.GetTrackdir()) ? YAPF_TILE_LENGTH : YAPF_TILE_CORNER_LENGTH;
    00169     /* additional penalty for curves */
    00170     if (n.m_parent != NULL && n.GetTrackdir() != NextTrackdir(n.m_parent->GetTrackdir())) {
    00171       /* new trackdir does not match the next one when going straight */
    00172       c += YAPF_TILE_LENGTH;
    00173     }
    00174
    00175     /* Skipped tile cost for aqueducts. */
    00176     c += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
    00177
    00178     /* Ocean/canal speed penalty. */
    00179     const ShipVehicleInfo *svi = ShipVehInfo(Yapf().GetVehicle()->engine_type);
    00180     byte speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac;
    00181     if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->m_tiles_skipped) * speed_frac / (256 - speed_frac);
    00182
    00183     /* apply it */
    00184     n.m_cost = n.m_parent->m_cost + c;
    00185     return true;
    00186   }
    00187 };
    00188
    00193 template <class Tpf_, class Ttrack_follower, class Tnode_list>
    00194 struct CYapfShip_TypesT
    00195 {
    00197   typedef CYapfShip_TypesT<Tpf_, Ttrack_follower, Tnode_list>  Types;
    00198
    00200   typedef Tpf_                              Tpf;
    00202   typedef Ttrack_follower                   TrackFollower;
    00204   typedef Tnode_list                        NodeList;
    00205   typedef Ship                              VehicleType;
    00207   typedef CYapfBaseT<Types>                 PfBase;        // base pathfinder class
    00208   typedef CYapfFollowShipT<Types>           PfFollow;      // node follower
    00209   typedef CYapfOriginTileT<Types>           PfOrigin;      // origin provider
    00210   typedef CYapfDestinationTileT<Types>      PfDestination; // destination/distance provider
    00211   typedef CYapfSegmentCostCacheNoneT<Types> PfCache;       // segment cost cache provider
    00212   typedef CYapfCostShipT<Types>             PfCost;        // cost provider
    00213 };
    00214
    00215 /* YAPF type 1 - uses TileIndex/Trackdir as Node key, allows 90-deg turns */
    00216 struct CYapfShip1 : CYapfT<CYapfShip_TypesT<CYapfShip1, CFollowTrackWater    , CShipNodeListTrackDir> > {};
    00217 /* YAPF type 2 - uses TileIndex/DiagDirection as Node key, allows 90-deg turns */
    00218 struct CYapfShip2 : CYapfT<CYapfShip_TypesT<CYapfShip2, CFollowTrackWater    , CShipNodeListExitDir > > {};
    00219 /* YAPF type 3 - uses TileIndex/Trackdir as Node key, forbids 90-deg turns */
    00220 struct CYapfShip3 : CYapfT<CYapfShip_TypesT<CYapfShip3, CFollowTrackWaterNo90, CShipNodeListTrackDir> > {};
    00221
    00223 Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
    00224 {
    00225   /* default is YAPF type 2 */
    00226   typedef Trackdir (*PfnChooseShipTrack)(const Ship*, TileIndex, DiagDirection, TrackBits, bool &path_found);
    00227   PfnChooseShipTrack pfnChooseShipTrack = CYapfShip2::ChooseShipTrack; // default: ExitDir, allow 90-deg
    00228
    00229   /* check if non-default YAPF type needed */
    00230   if (_settings_game.pf.forbid_90_deg) {
    00231     pfnChooseShipTrack = &CYapfShip3::ChooseShipTrack; // Trackdir, forbid 90-deg
    00232   } else if (_settings_game.pf.yapf.disable_node_optimization) {
    00233     pfnChooseShipTrack = &CYapfShip1::ChooseShipTrack; // Trackdir, allow 90-deg
    00234   }
    00235
    00236   Trackdir td_ret = pfnChooseShipTrack(v, tile, enterdir, tracks, path_found);
    00237   return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
    00238 }
    00239
    00240 bool YapfShipCheckReverse(const Ship *v)
    00241 {
    00242   Trackdir td = v->GetVehicleTrackdir();
    00243   Trackdir td_rev = ReverseTrackdir(td);
    00244   TileIndex tile = v->tile;
    00245
    00246   typedef bool (*PfnCheckReverseShip)(const Ship*, TileIndex, Trackdir, Trackdir);
    00247   PfnCheckReverseShip pfnCheckReverseShip = CYapfShip2::CheckShipReverse; // default: ExitDir, allow 90-deg
    00248
    00249   /* check if non-default YAPF type needed */
    00250   if (_settings_game.pf.forbid_90_deg) {
    00251     pfnCheckReverseShip = &CYapfShip3::CheckShipReverse; // Trackdir, forbid 90-deg
    00252   } else if (_settings_game.pf.yapf.disable_node_optimization) {
    00253     pfnCheckReverseShip = &CYapfShip1::CheckShipReverse; // Trackdir, allow 90-deg
    00254   }
    00255
    00256   bool reverse = pfnCheckReverseShip(v, tile, td, td_rev);
    00257
    00258   return reverse;
    00259 }