2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
8 /** @file follow_track.hpp Template function for track followers */
10 #ifndef FOLLOW_TRACK_HPP
11 #define FOLLOW_TRACK_HPP
14 #include "../roadveh.h"
15 #include "../station_base.h"
17 #include "../tunnelbridge.h"
18 #include "../tunnelbridge_map.h"
19 #include "../depot_map.h"
20 #include "pathfinder_func.h"
23 * Track follower helper template class (can serve pathfinders and vehicle
24 * controllers). See 6 different typedefs below for 3 different transport
25 * types w/ or w/o 90-deg turns allowed
27 template <TransportType Ttr_type_
, typename VehicleType
, bool T90deg_turns_allowed_
= true, bool Tmask_reserved_tracks
= false>
39 const VehicleType
*veh
; ///< moving vehicle
40 Owner veh_owner
; ///< owner of the vehicle
41 TileIndex old_tile
; ///< the origin (vehicle moved from) before move
42 Trackdir old_td
; ///< the trackdir (the vehicle was on) before move
43 TileIndex new_tile
; ///< the new tile (the vehicle has entered)
44 TrackdirBits new_td_bits
; ///< the new set of available trackdirs
45 DiagDirection exitdir
; ///< exit direction (leaving the old tile)
46 bool is_tunnel
; ///< last turn passed tunnel
47 bool is_bridge
; ///< last turn passed bridge ramp
48 bool is_station
; ///< last turn passed station
49 int tiles_skipped
; ///< number of skipped tunnel or station tiles
53 inline CFollowTrackT(const VehicleType
*v
= nullptr, RailTypes railtype_override
= INVALID_RAILTYPES
)
55 Init(v
, railtype_override
);
58 inline CFollowTrackT(Owner o
, RailTypes railtype_override
= INVALID_RAILTYPES
)
62 Init(o
, railtype_override
);
65 inline void Init(const VehicleType
*v
, RailTypes railtype_override
)
67 assert(!IsRailTT() || (v
!= nullptr && v
->type
== VEH_TRAIN
));
69 Init(v
!= nullptr ? v
->owner
: INVALID_OWNER
, IsRailTT() && railtype_override
== INVALID_RAILTYPES
? Train::From(v
)->compatible_railtypes
: railtype_override
);
72 inline void Init(Owner o
, RailTypes railtype_override
)
74 assert(!IsRoadTT() || this->veh
!= nullptr);
75 assert(!IsRailTT() || railtype_override
!= INVALID_RAILTYPES
);
77 /* don't worry, all is inlined so compiler should remove unnecessary initializations */
78 this->old_tile
= INVALID_TILE
;
79 this->old_td
= INVALID_TRACKDIR
;
80 this->new_tile
= INVALID_TILE
;
81 this->new_td_bits
= TRACKDIR_BIT_NONE
;
82 this->exitdir
= INVALID_DIAGDIR
;
83 this->is_station
= false;
84 this->is_bridge
= false;
85 this->is_tunnel
= false;
86 this->tiles_skipped
= 0;
88 this->railtypes
= railtype_override
;
91 debug_inline
static TransportType
TT() { return Ttr_type_
; }
92 debug_inline
static bool IsWaterTT() { return TT() == TRANSPORT_WATER
; }
93 debug_inline
static bool IsRailTT() { return TT() == TRANSPORT_RAIL
; }
94 inline bool IsTram() { return IsRoadTT() && RoadTypeIsTram(RoadVehicle::From(this->veh
)->roadtype
); }
95 debug_inline
static bool IsRoadTT() { return TT() == TRANSPORT_ROAD
; }
96 inline static bool Allow90degTurns() { return T90deg_turns_allowed_
; }
97 inline static bool DoTrackMasking() { return Tmask_reserved_tracks
; }
99 /** Tests if a tile is a road tile with a single tramtrack (tram can reverse) */
100 inline DiagDirection
GetSingleTramBit(TileIndex tile
)
102 assert(this->IsTram()); // this function shouldn't be called in other cases
104 if (IsNormalRoadTile(tile
)) {
105 RoadBits rb
= GetRoadBits(tile
, RTT_TRAM
);
107 case ROAD_NW
: return DIAGDIR_NW
;
108 case ROAD_SW
: return DIAGDIR_SW
;
109 case ROAD_SE
: return DIAGDIR_SE
;
110 case ROAD_NE
: return DIAGDIR_NE
;
114 return INVALID_DIAGDIR
;
118 * main follower routine. Fills all members and return true on success.
119 * Otherwise returns false if track can't be followed.
121 inline bool Follow(TileIndex old_tile
, Trackdir old_td
)
123 this->old_tile
= old_tile
;
124 this->old_td
= old_td
;
128 if (this->IsTram() && this->GetSingleTramBit(this->old_tile
) != INVALID_DIAGDIR
) return true; // Skip the check for single tram bits
129 const uint sub_mode
= (IsRoadTT() && this->veh
!= nullptr) ? (this->IsTram() ? RTT_TRAM
: RTT_ROAD
) : 0;
130 const TrackdirBits old_tile_valid_dirs
= TrackStatusToTrackdirBits(GetTileTrackStatus(this->old_tile
, TT(), sub_mode
));
131 return (old_tile_valid_dirs
& TrackdirToTrackdirBits(this->old_td
)) != TRACKDIR_BIT_NONE
;
134 this->exitdir
= TrackdirToExitdir(this->old_td
);
135 if (this->ForcedReverse()) return true;
136 if (!this->CanExitOldTile()) return false;
137 this->FollowTileExit();
138 if (!this->QueryNewTileTrackStatus()) return TryReverse();
139 this->new_td_bits
&= DiagdirReachesTrackdirs(this->exitdir
);
140 if (this->new_td_bits
== TRACKDIR_BIT_NONE
|| !this->CanEnterNewTile()) {
141 /* In case we can't enter the next tile, but are
142 * a normal road vehicle, then we can actually
143 * try to reverse as this is the end of the road.
144 * Trams can only turn on the appropriate bits in
145 * which case reaching this would mean a dead end
146 * near a building and in that case there would
147 * a "false" QueryNewTileTrackStatus result and
148 * as such reversing is already tried. The fact
149 * that function failed can have to do with a
150 * missing road bit, or inability to connect the
151 * different bits due to slopes. */
152 if (IsRoadTT() && !this->IsTram() && this->TryReverse()) return true;
154 /* CanEnterNewTile already set a reason.
155 * Do NOT overwrite it (important for example for EC_RAIL_ROAD_TYPE).
156 * Only set a reason if CanEnterNewTile was not called */
157 if (this->new_td_bits
== TRACKDIR_BIT_NONE
) this->err
= EC_NO_WAY
;
161 if ((!IsRailTT() && !Allow90degTurns()) || (IsRailTT() && Rail90DegTurnDisallowed(GetTileRailType(this->old_tile
), GetTileRailType(this->new_tile
), !Allow90degTurns()))) {
162 this->new_td_bits
&= (TrackdirBits
)~(int)TrackdirCrossesTrackdirs(this->old_td
);
163 if (this->new_td_bits
== TRACKDIR_BIT_NONE
) {
164 this->err
= EC_90DEG
;
171 inline bool MaskReservedTracks()
173 if (!DoTrackMasking()) return true;
175 if (this->is_station
) {
176 /* Check skipped station tiles as well. */
177 TileIndexDiff diff
= TileOffsByDiagDir(this->exitdir
);
178 for (TileIndex tile
= this->new_tile
- diff
* this->tiles_skipped
; tile
!= this->new_tile
; tile
+= diff
) {
179 if (HasStationReservation(tile
)) {
180 this->new_td_bits
= TRACKDIR_BIT_NONE
;
181 this->err
= EC_RESERVED
;
187 TrackBits reserved
= GetReservedTrackbits(this->new_tile
);
188 /* Mask already reserved trackdirs. */
189 this->new_td_bits
&= ~TrackBitsToTrackdirBits(reserved
);
190 /* Mask out all trackdirs that conflict with the reservation. */
191 for (Track t
: SetTrackBitIterator(TrackdirBitsToTrackBits(this->new_td_bits
))) {
192 if (TracksOverlap(reserved
| TrackToTrackBits(t
))) this->new_td_bits
&= ~TrackToTrackdirBits(t
);
194 if (this->new_td_bits
== TRACKDIR_BIT_NONE
) {
195 this->err
= EC_RESERVED
;
202 /** Follow the exitdir from old_tile and fill new_tile and tiles_skipped */
203 inline void FollowTileExit()
205 this->is_station
= false;
206 this->is_bridge
= false;
207 this->is_tunnel
= false;
208 this->tiles_skipped
= 0;
210 /* extra handling for tunnels and bridges in our direction */
211 if (IsTileType(this->old_tile
, MP_TUNNELBRIDGE
)) {
212 DiagDirection enterdir
= GetTunnelBridgeDirection(this->old_tile
);
213 if (enterdir
== this->exitdir
) {
214 /* we are entering the tunnel / bridge */
215 if (IsTunnel(this->old_tile
)) {
216 this->is_tunnel
= true;
217 this->new_tile
= GetOtherTunnelEnd(this->old_tile
);
218 } else { // IsBridge(old_tile)
219 this->is_bridge
= true;
220 this->new_tile
= GetOtherBridgeEnd(this->old_tile
);
222 this->tiles_skipped
= GetTunnelBridgeLength(this->new_tile
, this->old_tile
);
225 assert(ReverseDiagDir(enterdir
) == this->exitdir
);
228 /* normal or station tile, do one step */
229 this->new_tile
= TileAddByDiagDir(this->old_tile
, this->exitdir
);
231 /* special handling for stations */
232 if (IsRailTT() && HasStationTileRail(this->new_tile
)) {
233 this->is_station
= true;
234 } else if (IsRoadTT() && IsStationRoadStopTile(this->new_tile
)) {
235 this->is_station
= true;
239 /** stores track status (available trackdirs) for the new tile into new_td_bits */
240 inline bool QueryNewTileTrackStatus()
242 if (IsRailTT() && IsPlainRailTile(this->new_tile
)) {
243 this->new_td_bits
= (TrackdirBits
)(GetTrackBits(this->new_tile
) * 0x101);
244 } else if (IsRoadTT()) {
245 this->new_td_bits
= GetTrackdirBitsForRoad(this->new_tile
, this->IsTram() ? RTT_TRAM
: RTT_ROAD
);
247 this->new_td_bits
= TrackStatusToTrackdirBits(GetTileTrackStatus(this->new_tile
, TT(), 0));
249 return (this->new_td_bits
!= TRACKDIR_BIT_NONE
);
252 /** return true if we can leave old_tile in exitdir */
253 inline bool CanExitOldTile()
255 /* road stop can be left at one direction only unless it's a drive-through stop */
256 if (IsRoadTT() && IsBayRoadStopTile(this->old_tile
)) {
257 DiagDirection exitdir
= GetBayRoadStopDir(this->old_tile
);
258 if (exitdir
!= this->exitdir
) {
259 this->err
= EC_NO_WAY
;
264 /* single tram bits can only be left in one direction */
265 if (this->IsTram()) {
266 DiagDirection single_tram
= GetSingleTramBit(this->old_tile
);
267 if (single_tram
!= INVALID_DIAGDIR
&& single_tram
!= this->exitdir
) {
268 this->err
= EC_NO_WAY
;
273 /* road depots can be also left in one direction only */
274 if (IsRoadTT() && IsDepotTypeTile(this->old_tile
, TT())) {
275 DiagDirection exitdir
= GetRoadDepotDirection(this->old_tile
);
276 if (exitdir
!= this->exitdir
) {
277 this->err
= EC_NO_WAY
;
284 /** return true if we can enter new_tile from exitdir */
285 inline bool CanEnterNewTile()
287 if (IsRoadTT() && IsBayRoadStopTile(this->new_tile
)) {
288 /* road stop can be entered from one direction only unless it's a drive-through stop */
289 DiagDirection exitdir
= GetBayRoadStopDir(this->new_tile
);
290 if (ReverseDiagDir(exitdir
) != this->exitdir
) {
291 this->err
= EC_NO_WAY
;
296 /* single tram bits can only be entered from one direction */
297 if (this->IsTram()) {
298 DiagDirection single_tram
= this->GetSingleTramBit(this->new_tile
);
299 if (single_tram
!= INVALID_DIAGDIR
&& single_tram
!= ReverseDiagDir(this->exitdir
)) {
300 this->err
= EC_NO_WAY
;
305 /* road and rail depots can also be entered from one direction only */
306 if (IsRoadTT() && IsDepotTypeTile(this->new_tile
, TT())) {
307 DiagDirection exitdir
= GetRoadDepotDirection(this->new_tile
);
308 if (ReverseDiagDir(exitdir
) != this->exitdir
) {
309 this->err
= EC_NO_WAY
;
312 /* don't try to enter other company's depots */
313 if (GetTileOwner(this->new_tile
) != this->veh_owner
) {
314 this->err
= EC_OWNER
;
318 if (IsRailTT() && IsDepotTypeTile(this->new_tile
, TT())) {
319 DiagDirection exitdir
= GetRailDepotDirection(this->new_tile
);
320 if (ReverseDiagDir(exitdir
) != this->exitdir
) {
321 this->err
= EC_NO_WAY
;
326 /* rail transport is possible only on tiles with the same owner as vehicle */
327 if (IsRailTT() && GetTileOwner(this->new_tile
) != this->veh_owner
) {
328 /* different owner */
329 this->err
= EC_NO_WAY
;
333 /* rail transport is possible only on compatible rail types */
335 RailType rail_type
= GetTileRailType(this->new_tile
);
336 if (!HasBit(this->railtypes
, rail_type
)) {
337 /* incompatible rail type */
338 this->err
= EC_RAIL_ROAD_TYPE
;
343 /* road transport is possible only on compatible road types */
345 const RoadVehicle
*v
= RoadVehicle::From(this->veh
);
346 RoadType roadtype
= GetRoadType(this->new_tile
, GetRoadTramType(v
->roadtype
));
347 if (!HasBit(v
->compatible_roadtypes
, roadtype
)) {
348 /* incompatible road type */
349 this->err
= EC_RAIL_ROAD_TYPE
;
354 /* tunnel holes and bridge ramps can be entered only from proper direction */
355 if (IsTileType(this->new_tile
, MP_TUNNELBRIDGE
)) {
356 if (IsTunnel(this->new_tile
)) {
357 if (!this->is_tunnel
) {
358 DiagDirection tunnel_enterdir
= GetTunnelBridgeDirection(this->new_tile
);
359 if (tunnel_enterdir
!= this->exitdir
) {
360 this->err
= EC_NO_WAY
;
364 } else { // IsBridge(new_tile)
365 if (!this->is_bridge
) {
366 DiagDirection ramp_enderdir
= GetTunnelBridgeDirection(this->new_tile
);
367 if (ramp_enderdir
!= this->exitdir
) {
368 this->err
= EC_NO_WAY
;
375 /* special handling for rail stations - get to the end of platform */
376 if (IsRailTT() && this->is_station
) {
377 /* entered railway station
378 * get platform length */
379 uint length
= BaseStation::GetByTile(this->new_tile
)->GetPlatformLength(this->new_tile
, TrackdirToExitdir(this->old_td
));
380 /* how big step we must do to get to the last platform tile? */
381 this->tiles_skipped
= length
- 1;
382 /* move to the platform end */
383 TileIndexDiff diff
= TileOffsByDiagDir(this->exitdir
);
384 diff
*= this->tiles_skipped
;
385 this->new_tile
= TileAdd(this->new_tile
, diff
);
392 /** return true if we must reverse (in depots and single tram bits) */
393 inline bool ForcedReverse()
395 /* rail and road depots cause reversing */
396 if (!IsWaterTT() && IsDepotTypeTile(this->old_tile
, TT())) {
397 DiagDirection exitdir
= IsRailTT() ? GetRailDepotDirection(this->old_tile
) : GetRoadDepotDirection(this->old_tile
);
398 if (exitdir
!= this->exitdir
) {
400 this->new_tile
= this->old_tile
;
401 this->new_td_bits
= TrackdirToTrackdirBits(ReverseTrackdir(this->old_td
));
402 this->exitdir
= exitdir
;
403 this->tiles_skipped
= 0;
404 this->is_tunnel
= false;
405 this->is_bridge
= false;
406 this->is_station
= false;
411 /* Single tram bits and standard road stops cause reversing. */
412 if (IsRoadTT() && ((this->IsTram() && GetSingleTramBit(this->old_tile
) == ReverseDiagDir(this->exitdir
)) ||
413 (IsBayRoadStopTile(this->old_tile
) && GetBayRoadStopDir(this->old_tile
) == ReverseDiagDir(this->exitdir
)))) {
415 this->new_tile
= this->old_tile
;
416 this->new_td_bits
= TrackdirToTrackdirBits(ReverseTrackdir(this->old_td
));
417 this->exitdir
= ReverseDiagDir(this->exitdir
);
418 this->tiles_skipped
= 0;
419 this->is_tunnel
= false;
420 this->is_bridge
= false;
421 this->is_station
= false;
428 /** return true if we successfully reversed at end of road/track */
429 inline bool TryReverse()
431 if (IsRoadTT() && !this->IsTram()) {
432 /* if we reached the end of road, we can reverse the RV and continue moving */
433 this->exitdir
= ReverseDiagDir(this->exitdir
);
434 /* new tile will be the same as old one */
435 this->new_tile
= this->old_tile
;
436 /* set new trackdir bits to all reachable trackdirs */
437 QueryNewTileTrackStatus();
438 this->new_td_bits
&= DiagdirReachesTrackdirs(this->exitdir
);
439 if (this->new_td_bits
!= TRACKDIR_BIT_NONE
) {
440 /* we have some trackdirs reachable after reversal */
444 this->err
= EC_NO_WAY
;
449 /** Helper for pathfinders - get min/max speed on the old_tile/old_td */
450 int GetSpeedLimit(int *pmin_speed
= nullptr) const
453 int max_speed
= INT_MAX
; // no limit
455 /* Check for on-bridge speed limit */
456 if (!IsWaterTT() && IsBridgeTile(this->old_tile
)) {
457 int spd
= GetBridgeSpec(GetBridgeType(this->old_tile
))->speed
;
458 if (IsRoadTT()) spd
*= 2;
459 max_speed
= std::min(max_speed
, spd
);
461 /* Check for speed limit imposed by railtype */
463 uint16_t rail_speed
= GetRailTypeInfo(GetRailType(this->old_tile
))->max_speed
;
464 if (rail_speed
> 0) max_speed
= std::min
<int>(max_speed
, rail_speed
);
467 /* max_speed is already in roadvehicle units, no need to further modify (divide by 2) */
468 uint16_t road_speed
= GetRoadTypeInfo(GetRoadType(this->old_tile
, GetRoadTramType(RoadVehicle::From(this->veh
)->roadtype
)))->max_speed
;
469 if (road_speed
> 0) max_speed
= std::min
<int>(max_speed
, road_speed
);
472 /* if min speed was requested, return it */
473 if (pmin_speed
!= nullptr) *pmin_speed
= min_speed
;
478 typedef CFollowTrackT
<TRANSPORT_WATER
, Ship
, true > CFollowTrackWater
;
479 typedef CFollowTrackT
<TRANSPORT_ROAD
, RoadVehicle
, true > CFollowTrackRoad
;
480 typedef CFollowTrackT
<TRANSPORT_RAIL
, Train
, true > CFollowTrackRail
;
482 typedef CFollowTrackT
<TRANSPORT_RAIL
, Train
, false> CFollowTrackRailNo90
;
484 typedef CFollowTrackT
<TRANSPORT_RAIL
, Train
, true, true > CFollowTrackFreeRail
;
485 typedef CFollowTrackT
<TRANSPORT_RAIL
, Train
, false, true > CFollowTrackFreeRailNo90
;
487 #endif /* FOLLOW_TRACK_HPP */