Codechange: Store animated tile state in map to improve performance.
[openttd-github.git] / src / pathfinder / follow_track.hpp
blob01e90b70ec9b25778b5b9d8fb46a16889b80adc2
1 /*
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/>.
6 */
8 /** @file follow_track.hpp Template function for track followers */
10 #ifndef FOLLOW_TRACK_HPP
11 #define FOLLOW_TRACK_HPP
13 #include "../pbs.h"
14 #include "../roadveh.h"
15 #include "../station_base.h"
16 #include "../train.h"
17 #include "../tunnelbridge.h"
18 #include "../tunnelbridge_map.h"
19 #include "../depot_map.h"
20 #include "pathfinder_func.h"
22 /**
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>
28 struct CFollowTrackT
30 enum ErrorCode {
31 EC_NONE,
32 EC_OWNER,
33 EC_RAIL_ROAD_TYPE,
34 EC_90DEG,
35 EC_NO_WAY,
36 EC_RESERVED,
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
50 ErrorCode err;
51 RailTypes railtypes;
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)
60 assert(IsRailTT());
61 this->veh = nullptr;
62 Init(o, railtype_override);
65 inline void Init(const VehicleType *v, RailTypes railtype_override)
67 assert(!IsRailTT() || (v != nullptr && v->type == VEH_TRAIN));
68 this->veh = v;
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);
76 this->veh_owner = o;
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;
87 this->err = EC_NONE;
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);
106 switch (rb) {
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;
111 default: break;
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;
125 this->err = EC_NONE;
127 assert([&]() {
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;
132 }());
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;
159 return false;
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;
165 return false;
168 return true;
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;
182 return false;
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;
196 return false;
198 return true;
201 protected:
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);
223 return;
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);
246 } else {
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;
260 return false;
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;
269 return false;
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;
278 return false;
281 return true;
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;
292 return false;
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;
301 return false;
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;
310 return false;
312 /* don't try to enter other company's depots */
313 if (GetTileOwner(this->new_tile) != this->veh_owner) {
314 this->err = EC_OWNER;
315 return false;
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;
322 return false;
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;
330 return false;
333 /* rail transport is possible only on compatible rail types */
334 if (IsRailTT()) {
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;
339 return false;
343 /* road transport is possible only on compatible road types */
344 if (IsRoadTT()) {
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;
350 return false;
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;
361 return false;
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;
369 return false;
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);
386 return true;
389 return true;
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) {
399 /* reverse */
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;
407 return true;
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)))) {
414 /* reverse */
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;
422 return true;
425 return 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 */
441 return true;
444 this->err = EC_NO_WAY;
445 return false;
448 public:
449 /** Helper for pathfinders - get min/max speed on the old_tile/old_td */
450 int GetSpeedLimit(int *pmin_speed = nullptr) const
452 int min_speed = 0;
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 */
462 if (IsRailTT()) {
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);
466 if (IsRoadTT()) {
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;
474 return max_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 */