follow_track.hpp

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

Generated on Mon May 11 15:48:09 2009 for OpenTTD by  doxygen 1.5.6