follow_track.hpp

Go to the documentation of this file.
00001 /* $Id: follow_track.hpp 12252 2008-02-25 15:09:22Z KUDr $ */
00002 
00005 #ifndef  FOLLOW_TRACK_HPP
00006 #define  FOLLOW_TRACK_HPP
00007 
00008 #include "yapf.hpp"
00009 
00010 
00014 template <TransportType Ttr_type_, bool T90deg_turns_allowed_ = true>
00015 struct CFollowTrackT
00016 {
00017   enum ErrorCode {
00018     EC_NONE,
00019     EC_OWNER,
00020     EC_RAIL_TYPE,
00021     EC_90DEG,
00022     EC_NO_WAY,
00023   };
00024 
00025   const Vehicle*      m_veh;           
00026   TileIndex           m_old_tile;      
00027   Trackdir            m_old_td;        
00028   TileIndex           m_new_tile;      
00029   TrackdirBits        m_new_td_bits;   
00030   DiagDirection       m_exitdir;       
00031   bool                m_is_tunnel;     
00032   bool                m_is_bridge;     
00033   bool                m_is_station;    
00034   int                 m_tiles_skipped; 
00035   ErrorCode           m_err;
00036   CPerformanceTimer* m_pPerf;
00037 
00038   FORCEINLINE CFollowTrackT(const Vehicle* v = NULL, CPerformanceTimer* pPerf = NULL)
00039   {
00040     Init(v, pPerf);
00041   }
00042 
00043   FORCEINLINE void Init(const Vehicle* v, CPerformanceTimer* pPerf)
00044   {
00045     assert(!IsRailTT() || (v != NULL && v->type == VEH_TRAIN));
00046     m_veh = v;
00047     m_pPerf = pPerf;
00048     // don't worry, all is inlined so compiler should remove unnecessary initializations
00049     m_new_tile = INVALID_TILE;
00050     m_new_td_bits = TRACKDIR_BIT_NONE;
00051     m_exitdir = INVALID_DIAGDIR;
00052     m_is_station = m_is_bridge = m_is_tunnel = false;
00053     m_tiles_skipped = 0;
00054     m_err = EC_NONE;
00055   }
00056 
00057   FORCEINLINE static TransportType TT() {return Ttr_type_;}
00058   FORCEINLINE static bool IsWaterTT() {return TT() == TRANSPORT_WATER;}
00059   FORCEINLINE static bool IsRailTT() {return TT() == TRANSPORT_RAIL;}
00060   FORCEINLINE bool IsTram() {return IsRoadTT() && HasBit(m_veh->u.road.compatible_roadtypes, ROADTYPE_TRAM);}
00061   FORCEINLINE static bool IsRoadTT() {return TT() == TRANSPORT_ROAD;}
00062   FORCEINLINE static bool Allow90degTurns() {return T90deg_turns_allowed_;}
00063 
00065   FORCEINLINE DiagDirection GetSingleTramBit(TileIndex tile)
00066   {
00067     if (IsTram() && IsNormalRoadTile(tile)) {
00068       RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM);
00069       switch (rb) {
00070         case ROAD_NW: return DIAGDIR_NW;
00071         case ROAD_SW: return DIAGDIR_SW;
00072         case ROAD_SE: return DIAGDIR_SE;
00073         case ROAD_NE: return DIAGDIR_NE;
00074         default: break;
00075       }
00076     }
00077     return INVALID_DIAGDIR;
00078   }
00079 
00082   FORCEINLINE bool Follow(TileIndex old_tile, Trackdir old_td)
00083   {
00084     m_old_tile = old_tile;
00085     m_old_td = old_td;
00086     m_err = EC_NONE;
00087     assert(((TrackStatusToTrackdirBits(GetTileTrackStatus(m_old_tile, TT(), m_veh->u.road.compatible_roadtypes)) & TrackdirToTrackdirBits(m_old_td)) != 0) ||
00088            (GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR)); // Disable the assertion for single tram bits
00089     m_exitdir = TrackdirToExitdir(m_old_td);
00090     if (ForcedReverse()) return true;
00091     if (!CanExitOldTile()) return false;
00092     FollowTileExit();
00093     if (!QueryNewTileTrackStatus()) return TryReverse();
00094     if (!CanEnterNewTile()) return false;
00095     m_new_td_bits &= DiagdirReachesTrackdirs(m_exitdir);
00096     if (m_new_td_bits == TRACKDIR_BIT_NONE) {
00097       m_err = EC_NO_WAY;
00098       return false;
00099     }
00100     if (!Allow90degTurns()) {
00101       m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td);
00102       if (m_new_td_bits == TRACKDIR_BIT_NONE) {
00103         m_err = EC_90DEG;
00104         return false;
00105       }
00106     }
00107     return true;
00108   }
00109 
00110 protected:
00112   FORCEINLINE void FollowTileExit()
00113   {
00114     m_is_station = m_is_bridge = m_is_tunnel = false;
00115     m_tiles_skipped = 0;
00116 
00117     // extra handling for tunnels and bridges in our direction
00118     if (IsTileType(m_old_tile, MP_TUNNELBRIDGE)) {
00119       DiagDirection enterdir = GetTunnelBridgeDirection(m_old_tile);
00120       if (enterdir == m_exitdir) {
00121         // we are entering the tunnel / bridge
00122         if (IsTunnel(m_old_tile)) {
00123           m_is_tunnel = true;
00124           m_new_tile = GetOtherTunnelEnd(m_old_tile);
00125         } else { // IsBridge(m_old_tile)
00126           m_is_bridge = true;
00127           m_new_tile = GetOtherBridgeEnd(m_old_tile);
00128         }
00129         m_tiles_skipped = GetTunnelBridgeLength(m_new_tile, m_old_tile);
00130         return;
00131       }
00132       assert(ReverseDiagDir(enterdir) == m_exitdir);
00133     }
00134 
00135     // normal or station tile, do one step
00136     TileIndexDiff diff = TileOffsByDiagDir(m_exitdir);
00137     m_new_tile = TILE_ADD(m_old_tile, diff);
00138 
00139     // special handling for stations
00140     if (IsRailTT() && IsRailwayStationTile(m_new_tile)) {
00141       m_is_station = true;
00142     } else if (IsRoadTT() && IsRoadStopTile(m_new_tile)) {
00143       m_is_station = true;
00144     } else {
00145       m_is_station = false;
00146     }
00147   }
00148 
00150   FORCEINLINE bool QueryNewTileTrackStatus()
00151   {
00152     CPerfStart perf(*m_pPerf);
00153     if (IsRailTT() && GetTileType(m_new_tile) == MP_RAILWAY && IsPlainRailTile(m_new_tile)) {
00154       m_new_td_bits = (TrackdirBits)(GetTrackBits(m_new_tile) * 0x101);
00155     } else {
00156       m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), m_veh->u.road.compatible_roadtypes));
00157 
00158       if (m_new_td_bits == 0) {
00159         /* GetTileTrackStatus() returns 0 for single tram bits.
00160          * As we cannot change it there (easily) without breaking something, change it here */
00161         switch (GetSingleTramBit(m_new_tile)) {
00162           case DIAGDIR_NE:
00163           case DIAGDIR_SW:
00164             m_new_td_bits = TRACKDIR_BIT_X_NE | TRACKDIR_BIT_X_SW;
00165             break;
00166 
00167           case DIAGDIR_NW:
00168           case DIAGDIR_SE:
00169             m_new_td_bits = TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_Y_SE;
00170             break;
00171 
00172           default: break;
00173         }
00174       }
00175     }
00176     return (m_new_td_bits != TRACKDIR_BIT_NONE);
00177   }
00178 
00180   FORCEINLINE bool CanExitOldTile()
00181   {
00182     // road stop can be left at one direction only unless it's a drive-through stop
00183     if (IsRoadTT() && IsStandardRoadStopTile(m_old_tile)) {
00184       DiagDirection exitdir = GetRoadStopDir(m_old_tile);
00185       if (exitdir != m_exitdir) {
00186         m_err = EC_NO_WAY;
00187         return false;
00188       }
00189     }
00190 
00191     /* single tram bits can only be left in one direction */
00192     DiagDirection single_tram = GetSingleTramBit(m_old_tile);
00193     if (single_tram != INVALID_DIAGDIR && single_tram != m_exitdir) {
00194       m_err = EC_NO_WAY;
00195       return false;
00196     }
00197 
00198     // road depots can be also left in one direction only
00199     if (IsRoadTT() && IsTileDepotType(m_old_tile, TT())) {
00200       DiagDirection exitdir = GetRoadDepotDirection(m_old_tile);
00201       if (exitdir != m_exitdir) {
00202         m_err = EC_NO_WAY;
00203         return false;
00204       }
00205     }
00206     return true;
00207   }
00208 
00210   FORCEINLINE bool CanEnterNewTile()
00211   {
00212     if (IsRoadTT() && IsStandardRoadStopTile(m_new_tile)) {
00213       // road stop can be entered from one direction only unless it's a drive-through stop
00214       DiagDirection exitdir = GetRoadStopDir(m_new_tile);
00215       if (ReverseDiagDir(exitdir) != m_exitdir) {
00216         m_err = EC_NO_WAY;
00217         return false;
00218       }
00219     }
00220 
00221     /* single tram bits can only be entered from one direction */
00222     DiagDirection single_tram = GetSingleTramBit(m_new_tile);
00223     if (single_tram != INVALID_DIAGDIR && single_tram != ReverseDiagDir(m_exitdir)) {
00224       m_err = EC_NO_WAY;
00225       return false;
00226     }
00227 
00228     // road and rail depots can also be entered from one direction only
00229     if (IsRoadTT() && IsTileDepotType(m_new_tile, TT())) {
00230       DiagDirection exitdir = GetRoadDepotDirection(m_new_tile);
00231       if (ReverseDiagDir(exitdir) != m_exitdir) {
00232         m_err = EC_NO_WAY;
00233         return false;
00234       }
00235       // don't try to enter other player's depots
00236       if (GetTileOwner(m_new_tile) != m_veh->owner) {
00237         m_err = EC_OWNER;
00238         return false;
00239       }
00240     }
00241     if (IsRailTT() && IsTileDepotType(m_new_tile, TT())) {
00242       DiagDirection exitdir = GetRailDepotDirection(m_new_tile);
00243       if (ReverseDiagDir(exitdir) != m_exitdir) {
00244         m_err = EC_NO_WAY;
00245         return false;
00246       }
00247     }
00248 
00249     // rail transport is possible only on tiles with the same owner as vehicle
00250     if (IsRailTT() && GetTileOwner(m_new_tile) != m_veh->owner) {
00251       // different owner
00252       m_err = EC_NO_WAY;
00253       return false;
00254     }
00255 
00256     // rail transport is possible only on compatible rail types
00257     if (IsRailTT()) {
00258       RailType rail_type = GetTileRailType(m_new_tile);
00259       if (!HasBit(m_veh->u.rail.compatible_railtypes, rail_type)) {
00260         // incompatible rail type
00261         m_err = EC_RAIL_TYPE;
00262         return false;
00263       }
00264     }
00265 
00266     // tunnel holes and bridge ramps can be entered only from proper direction
00267     if (!IsWaterTT() && IsTileType(m_new_tile, MP_TUNNELBRIDGE)) {
00268       if (IsTunnel(m_new_tile)) {
00269         if (!m_is_tunnel) {
00270           DiagDirection tunnel_enterdir = GetTunnelBridgeDirection(m_new_tile);
00271           if (tunnel_enterdir != m_exitdir) {
00272             m_err = EC_NO_WAY;
00273             return false;
00274           }
00275         }
00276       } else { // IsBridge(m_new_tile)
00277         if (!m_is_bridge) {
00278           DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile);
00279           if (ramp_enderdir != m_exitdir) {
00280             m_err = EC_NO_WAY;
00281             return false;
00282           }
00283         }
00284       }
00285     }
00286 
00287     // special handling for rail stations - get to the end of platform
00288     if (IsRailTT() && m_is_station) {
00289       // entered railway station
00290       // get platform length
00291       uint length = GetStationByTile(m_new_tile)->GetPlatformLength(m_new_tile, TrackdirToExitdir(m_old_td));
00292       // how big step we must do to get to the last platform tile;
00293       m_tiles_skipped = length - 1;
00294       // move to the platform end
00295       TileIndexDiff diff = TileOffsByDiagDir(m_exitdir);
00296       diff *= m_tiles_skipped;
00297       m_new_tile = TILE_ADD(m_new_tile, diff);
00298       return true;
00299     }
00300 
00301     return true;
00302   }
00303 
00305   FORCEINLINE bool ForcedReverse()
00306   {
00307     // rail and road depots cause reversing
00308     if (!IsWaterTT() && IsTileDepotType(m_old_tile, TT())) {
00309       DiagDirection exitdir = IsRailTT() ? GetRailDepotDirection(m_old_tile) : GetRoadDepotDirection(m_old_tile);
00310       if (exitdir != m_exitdir) {
00311         // reverse
00312         m_new_tile = m_old_tile;
00313         m_new_td_bits = TrackdirToTrackdirBits(ReverseTrackdir(m_old_td));
00314         m_exitdir = exitdir;
00315         m_tiles_skipped = 0;
00316         m_is_tunnel = m_is_bridge = m_is_station = false;
00317         return true;
00318       }
00319     }
00320 
00321     // single tram bits cause reversing
00322     if (GetSingleTramBit(m_old_tile) == ReverseDiagDir(m_exitdir)) {
00323       // reverse
00324       m_new_tile = m_old_tile;
00325       m_new_td_bits = TrackdirToTrackdirBits(ReverseTrackdir(m_old_td));
00326       m_exitdir = ReverseDiagDir(m_exitdir);
00327       m_tiles_skipped = 0;
00328       m_is_tunnel = m_is_bridge = m_is_station = false;
00329       return true;
00330     }
00331 
00332     return false;
00333   }
00334 
00336   FORCEINLINE bool TryReverse()
00337   {
00338     if (IsRoadTT() && !IsTram()) {
00339       // if we reached the end of road, we can reverse the RV and continue moving
00340       m_exitdir = ReverseDiagDir(m_exitdir);
00341       // new tile will be the same as old one
00342       m_new_tile = m_old_tile;
00343       // set new trackdir bits to all reachable trackdirs
00344       QueryNewTileTrackStatus();
00345       m_new_td_bits &= DiagdirReachesTrackdirs(m_exitdir);
00346       if (m_new_td_bits != TRACKDIR_BIT_NONE) {
00347         // we have some trackdirs reachable after reversal
00348         return true;
00349       }
00350     }
00351     m_err = EC_NO_WAY;
00352     return false;
00353   }
00354 
00355 public:
00357   int GetSpeedLimit(int *pmin_speed = NULL) const
00358   {
00359     int min_speed = 0;
00360     int max_speed = INT_MAX; // no limit
00361 
00362     // for now we handle only on-bridge speed limit
00363     if (!IsWaterTT() && IsBridgeTile(m_old_tile)) {
00364       int spd = GetBridgeSpec(GetBridgeType(m_old_tile))->speed;
00365       if (IsRoadTT()) spd *= 2;
00366       if (max_speed > spd) max_speed = spd;
00367     }
00368 
00369     // if min speed was requested, return it
00370     if (pmin_speed) *pmin_speed = min_speed;
00371     return max_speed;
00372   }
00373 };
00374 
00375 typedef CFollowTrackT<TRANSPORT_WATER, true > CFollowTrackWater;
00376 typedef CFollowTrackT<TRANSPORT_ROAD , true > CFollowTrackRoad;
00377 typedef CFollowTrackT<TRANSPORT_RAIL , true > CFollowTrackRail;
00378 
00379 typedef CFollowTrackT<TRANSPORT_WATER, false> CFollowTrackWaterNo90;
00380 typedef CFollowTrackT<TRANSPORT_ROAD , false> CFollowTrackRoadNo90;
00381 typedef CFollowTrackT<TRANSPORT_RAIL , false> CFollowTrackRailNo90;
00382 
00383 #endif /* FOLLOW_TRACK_HPP */

Generated on Wed Oct 1 17:03:25 2008 for openttd by  doxygen 1.5.6