# follow_track.hpp

Go to the documentation of this file.

    00001 /* $Id: follow_track.hpp 14421 2008-09-30 20:39:50Z 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 */