Files @ r28822:57cd1bc354de
Branch filter:

Location: cpp/openttd-patchpack/source/src/track_func.h

Peter Nelson
Codechange: Add `GetVisibleRangeIterators()` to `Scrollbar`.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
/*
 * This file is part of OpenTTD.
 * 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.
 * 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.
 * 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/>.
 */

/** @file track_func.h Different conversion functions from one kind of track to another. */

#ifndef TRACK_FUNC_H
#define TRACK_FUNC_H

#include "core/bitmath_func.hpp"
#include "track_type.h"
#include "direction_func.h"
#include "slope_func.h"

using SetTrackBitIterator = SetBitIterator<Track, TrackBits>;
using SetTrackdirBitIterator = SetBitIterator<Trackdir, TrackdirBits>;

/**
 * Checks if a Track is valid.
 *
 * @param track The value to check
 * @return true if the given value is a valid track.
 * @note Use this in an assert()
 */
inline bool IsValidTrack(Track track)
{
	return track < TRACK_END;
}

/**
 * Checks if a Trackdir is valid for road vehicles.
 *
 * @param trackdir The value to check
 * @return true if the given value is a valid Trackdir
 * @note Use this in an assert()
 */
inline bool IsValidTrackdirForRoadVehicle(Trackdir trackdir)
{
	return trackdir < TRACKDIR_END;
}

/**
 * Checks if a Trackdir is valid for non-road vehicles.
 *
 * @param trackdir The value to check
 * @return true if the given value is a valid Trackdir
 * @note Use this in an assert()
 */
inline bool IsValidTrackdir(Trackdir trackdir)
{
	return trackdir != INVALID_TRACKDIR && ((1 << trackdir & TRACKDIR_BIT_MASK) != TRACKDIR_BIT_NONE);
}

/**
 * Convert an Axis to the corresponding Track
 * AXIS_X -> TRACK_X
 * AXIS_Y -> TRACK_Y
 * Uses the fact that they share the same internal encoding
 *
 * @param a the axis to convert
 * @return the track corresponding to the axis
 */
inline Track AxisToTrack(Axis a)
{
	assert(IsValidAxis(a));
	return (Track)a;
}

/**
 * Maps a Track to the corresponding TrackBits value
 * @param track the track to convert
 * @return the converted TrackBits value of the track
 */
inline TrackBits TrackToTrackBits(Track track)
{
	assert(IsValidTrack(track));
	return (TrackBits)(1 << track);
}

/**
 * Maps an Axis to the corresponding TrackBits value
 * @param a the axis to convert
 * @return the converted TrackBits value of the axis
 */
inline TrackBits AxisToTrackBits(Axis a)
{
	return TrackToTrackBits(AxisToTrack(a));
}

/**
 * Returns a single horizontal/vertical trackbit that is in a specific tile corner.
 *
 * @param corner The corner of a tile.
 * @return The TrackBits of the track in the corner.
 */
inline TrackBits CornerToTrackBits(Corner corner)
{
	extern const TrackBits _corner_to_trackbits[];
	assert(IsValidCorner(corner));
	return _corner_to_trackbits[corner];
}

/**
 * Maps a Trackdir to the corresponding TrackdirBits value
 * @param trackdir the track direction to convert
 * @return the converted TrackdirBits value
 */
inline TrackdirBits TrackdirToTrackdirBits(Trackdir trackdir)
{
	assert(IsValidTrackdir(trackdir));
	return (TrackdirBits)(1 << trackdir);
}

/**
 * Removes first Track from TrackBits and returns it
 *
 * This function searches for the first bit in the TrackBits,
 * remove this bit from the parameter and returns the found
 * bit as Track value. It returns INVALID_TRACK if the
 * parameter was TRACK_BIT_NONE or INVALID_TRACK_BIT. This
 * is basically used in while-loops to get up to 6 possible
 * tracks on a tile until the parameter becomes TRACK_BIT_NONE.
 *
 * @param tracks The value with the TrackBits
 * @return The first Track from the TrackBits value
 * @see FindFirstTrack
 */
inline Track RemoveFirstTrack(TrackBits *tracks)
{
	if (*tracks != TRACK_BIT_NONE && *tracks != INVALID_TRACK_BIT) {
		assert((*tracks & ~TRACK_BIT_MASK) == TRACK_BIT_NONE);
		Track first = (Track)FindFirstBit(*tracks);
		ClrBit(*tracks, first);
		return first;
	}
	return INVALID_TRACK;
}

/**
 * Removes first Trackdir from TrackdirBits and returns it
 *
 * This function searches for the first bit in the TrackdirBits parameter,
 * remove this bit from the parameter and returns the fnound bit as
 * Trackdir value. It returns INVALID_TRACKDIR if the trackdirs is
 * TRACKDIR_BIT_NONE or INVALID_TRACKDIR_BIT. This is basically used in a
 * while-loop to get all track-directions step by step until the value
 * reaches TRACKDIR_BIT_NONE.
 *
 * @param trackdirs The value with the TrackdirBits
 * @return The first Trackdir from the TrackdirBits value
 * @see FindFirstTrackdir
 */
inline Trackdir RemoveFirstTrackdir(TrackdirBits *trackdirs)
{
	if (*trackdirs != TRACKDIR_BIT_NONE && *trackdirs != INVALID_TRACKDIR_BIT) {
		assert((*trackdirs & ~TRACKDIR_BIT_MASK) == TRACKDIR_BIT_NONE);
		Trackdir first = (Trackdir)FindFirstBit(*trackdirs);
		ClrBit(*trackdirs, first);
		return first;
	}
	return INVALID_TRACKDIR;
}

/**
 * Returns first Track from TrackBits or INVALID_TRACK
 *
 * This function returns the first Track found in the TrackBits value as Track-value.
 * It returns INVALID_TRACK if the parameter is TRACK_BIT_NONE or INVALID_TRACK_BIT.
 *
 * @param tracks The TrackBits value
 * @return The first Track found or INVALID_TRACK
 * @see RemoveFirstTrack
 */
inline Track FindFirstTrack(TrackBits tracks)
{
	return (tracks != TRACK_BIT_NONE && tracks != INVALID_TRACK_BIT) ? (Track)FindFirstBit(tracks) : INVALID_TRACK;
}

/**
 * Converts TrackBits to Track.
 *
 * This function converts a TrackBits value to a Track value. As it
 * is not possible to convert two or more tracks to one track the
 * parameter must contain only one track or be the INVALID_TRACK_BIT value.
 *
 * @param tracks The TrackBits value to convert
 * @return The Track from the value or INVALID_TRACK
 * @pre tracks must contains only one Track or be INVALID_TRACK_BIT
 */
inline Track TrackBitsToTrack(TrackBits tracks)
{
	assert(tracks == INVALID_TRACK_BIT || (tracks != TRACK_BIT_NONE && KillFirstBit(tracks & TRACK_BIT_MASK) == TRACK_BIT_NONE));
	return tracks != INVALID_TRACK_BIT ? (Track)FindFirstBit(tracks & TRACK_BIT_MASK) : INVALID_TRACK;
}

/**
 * Returns first Trackdir from TrackdirBits or INVALID_TRACKDIR
 *
 * This function returns the first Trackdir in the given TrackdirBits value or
 * INVALID_TRACKDIR if the value is TRACKDIR_BIT_NONE. The TrackdirBits must
 * not be INVALID_TRACKDIR_BIT.
 *
 * @param trackdirs The TrackdirBits value
 * @return The first Trackdir from the TrackdirBits or INVALID_TRACKDIR on TRACKDIR_BIT_NONE.
 * @pre trackdirs must not be INVALID_TRACKDIR_BIT
 * @see RemoveFirstTrackdir
 */
inline Trackdir FindFirstTrackdir(TrackdirBits trackdirs)
{
	assert((trackdirs & ~TRACKDIR_BIT_MASK) == TRACKDIR_BIT_NONE);
	return (trackdirs != TRACKDIR_BIT_NONE) ? (Trackdir)FindFirstBit(trackdirs) : INVALID_TRACKDIR;
}

/*
 * Functions describing logical relations between Tracks, TrackBits, Trackdirs
 * TrackdirBits, Direction and DiagDirections.
 */

/**
 * Find the opposite track to a given track.
 *
 * TRACK_LOWER -> TRACK_UPPER and vice versa, likewise for left/right.
 * TRACK_X is mapped to TRACK_Y and reversed.
 *
 * @param t the track to convert
 * @return the opposite track
 */
inline Track TrackToOppositeTrack(Track t)
{
	assert(IsValidTrack(t));
	return (Track)(t ^ 1);
}

/**
 * Maps a trackdir to the reverse trackdir.
 *
 * Returns the reverse trackdir of a Trackdir value. The reverse trackdir
 * is the same track with the other direction on it.
 *
 * @param trackdir The Trackdir value
 * @return The reverse trackdir
 * @pre trackdir must not be INVALID_TRACKDIR
 */
inline Trackdir ReverseTrackdir(Trackdir trackdir)
{
	assert(IsValidTrackdirForRoadVehicle(trackdir));
	return (Trackdir)(trackdir ^ 8);
}

/**
 * Returns the Track that a given Trackdir represents
 *
 * This function filters the Track which is used in the Trackdir value and
 * returns it as a Track value.
 *
 * @param trackdir The trackdir value
 * @return The Track which is used in the value
 */
inline Track TrackdirToTrack(Trackdir trackdir)
{
	assert(IsValidTrackdir(trackdir));
	return (Track)(trackdir & 0x7);
}

/**
 * Returns a Trackdir for the given Track
 *
 * Since every Track corresponds to two Trackdirs, we choose the
 * one which points between NE and S. Note that the actual
 * implementation is quite futile, but this might change
 * in the future.
 *
 * @param track The given Track
 * @return The Trackdir from the given Track
 */
inline Trackdir TrackToTrackdir(Track track)
{
	assert(IsValidTrack(track));
	return (Trackdir)track;
}

/**
 * Returns a TrackdirBit mask from a given Track
 *
 * The TrackdirBit mask contains the two TrackdirBits that
 * correspond with the given Track (one for each direction).
 *
 * @param track The track to get the TrackdirBits from
 * @return The TrackdirBits which the selected tracks
 */
inline TrackdirBits TrackToTrackdirBits(Track track)
{
	Trackdir td = TrackToTrackdir(track);
	return (TrackdirBits)(TrackdirToTrackdirBits(td) | TrackdirToTrackdirBits(ReverseTrackdir(td)));
}

/**
 * Discards all directional information from a TrackdirBits value
 *
 * Any Track which is present in either direction will be present in the result.
 *
 * @param bits The TrackdirBits to get the TrackBits from
 * @return The TrackBits
 */
inline TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
{
	return (TrackBits)((bits | (bits >> 8)) & TRACK_BIT_MASK);
}

/**
 * Converts TrackBits to TrackdirBits while allowing both directions.
 *
 * @param bits The TrackBits
 * @return The TrackdirBits containing of bits in both directions.
 */
inline TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
{
	return (TrackdirBits)(bits * 0x101);
}

/**
 * Checks whether a TrackBits has a given Track.
 * @param tracks The track bits.
 * @param track The track to check.
 */
inline bool HasTrack(TrackBits tracks, Track track)
{
	assert(IsValidTrack(track));
	return HasBit(tracks, track);
}

/**
 * Checks whether a TrackdirBits has a given Trackdir.
 * @param trackdirs The trackdir bits.
 * @param trackdir The trackdir to check.
 */
inline bool HasTrackdir(TrackdirBits trackdirs, Trackdir trackdir)
{
	assert(IsValidTrackdir(trackdir));
	return HasBit(trackdirs, trackdir);
}

/**
 * Returns the present-trackdir-information of a TrackStatus.
 *
 * @param ts The TrackStatus returned by GetTileTrackStatus()
 * @return the present trackdirs
 */
inline TrackdirBits TrackStatusToTrackdirBits(TrackStatus ts)
{
	return (TrackdirBits)(ts & TRACKDIR_BIT_MASK);
}

/**
 * Returns the present-track-information of a TrackStatus.
 *
 * @param ts The TrackStatus returned by GetTileTrackStatus()
 * @return the present tracks
 */
inline TrackBits TrackStatusToTrackBits(TrackStatus ts)
{
	return TrackdirBitsToTrackBits(TrackStatusToTrackdirBits(ts));
}

/**
 * Returns the red-signal-information of a TrackStatus.
 *
 * Note: The result may contain red signals for non-present tracks.
 *
 * @param ts The TrackStatus returned by GetTileTrackStatus()
 * @return the The trackdirs that are blocked by red-signals
 */
inline TrackdirBits TrackStatusToRedSignals(TrackStatus ts)
{
	return (TrackdirBits)((ts >> 16) & TRACKDIR_BIT_MASK);
}

/**
 * Builds a TrackStatus
 *
 * @param trackdirbits present trackdirs
 * @param red_signals red signals
 * @return the TrackStatus representing the given information
 */
inline TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, TrackdirBits red_signals)
{
	return (TrackStatus)(trackdirbits | (red_signals << 16));
}

/**
 * Maps a trackdir to the trackdir that you will end up on if you go straight
 * ahead.
 *
 * This will be the same trackdir for diagonal trackdirs, but a
 * different (alternating) one for straight trackdirs
 *
 * @param trackdir The given trackdir
 * @return The next Trackdir value of the next tile.
 */
inline Trackdir NextTrackdir(Trackdir trackdir)
{
	assert(IsValidTrackdir(trackdir));
	extern const Trackdir _next_trackdir[TRACKDIR_END];
	return _next_trackdir[trackdir];
}

/**
 * Maps a track to all tracks that make 90 deg turns with it.
 *
 * For the diagonal directions these are the complement of the
 * direction, for the straight directions these are the
 * two vertical or horizontal tracks, depend on the given direction
 *
 * @param track The given track
 * @return The TrackBits with the tracks marked which cross the given track by 90 deg.
 */
inline TrackBits TrackCrossesTracks(Track track)
{
	assert(IsValidTrack(track));
	extern const TrackBits _track_crosses_tracks[TRACK_END];
	return _track_crosses_tracks[track];
}

/**
 * Maps a trackdir to the (4-way) direction the tile is exited when following
 * that trackdir.
 *
 * For the diagonal directions these are the same directions. For
 * the straight directions these are the directions from the imagined
 * base-tile to the bordering tile which will be joined if the given
 * straight direction is leaved from the base-tile.
 *
 * @param trackdir The given track direction
 * @return The direction which points to the resulting tile if following the Trackdir
 */
inline DiagDirection TrackdirToExitdir(Trackdir trackdir)
{
	assert(IsValidTrackdirForRoadVehicle(trackdir));
	extern const DiagDirection _trackdir_to_exitdir[TRACKDIR_END];
	return _trackdir_to_exitdir[trackdir];
}

/**
 * Maps a track and an (4-way) dir to the trackdir that represents the track
 * with the exit in the given direction.
 *
 * For the diagonal tracks the resulting track direction are clear for a given
 * DiagDirection. It either matches the direction or it returns INVALID_TRACKDIR,
 * as a TRACK_X cannot be applied with DIAG_SE.
 * For the straight tracks the resulting track direction will be the
 * direction which the DiagDirection is pointing. But this will be INVALID_TRACKDIR
 * if the DiagDirection is pointing 'away' the track.
 *
 * @param track The track to apply an direction on
 * @param diagdir The DiagDirection to apply on
 * @return The resulting track direction or INVALID_TRACKDIR if not possible.
 */
inline Trackdir TrackExitdirToTrackdir(Track track, DiagDirection diagdir)
{
	assert(IsValidTrack(track));
	assert(IsValidDiagDirection(diagdir));
	extern const Trackdir _track_exitdir_to_trackdir[TRACK_END][DIAGDIR_END];
	return _track_exitdir_to_trackdir[track][diagdir];
}

/**
 * Maps a track and an (4-way) dir to the trackdir that represents the track
 * with the entry in the given direction.
 *
 * For the diagonal tracks the return value is clear, its either the matching
 * track direction or INVALID_TRACKDIR.
 * For the straight tracks this returns the track direction which results if
 * you follow the DiagDirection and then turn by 45 deg left or right on the
 * next tile. The new direction on the new track will be the returning Trackdir
 * value. If the parameters makes no sense like the track TRACK_UPPER and the
 * direction DIAGDIR_NE (target track cannot be reached) this function returns
 * INVALID_TRACKDIR.
 *
 * @param track The target track
 * @param diagdir The direction to "come from"
 * @return the resulting Trackdir or INVALID_TRACKDIR if not possible.
 */
inline Trackdir TrackEnterdirToTrackdir(Track track, DiagDirection diagdir)
{
	assert(IsValidTrack(track));
	assert(IsValidDiagDirection(diagdir));
	extern const Trackdir _track_enterdir_to_trackdir[TRACK_END][DIAGDIR_END];
	return _track_enterdir_to_trackdir[track][diagdir];
}

/**
 * Maps a track and a full (8-way) direction to the trackdir that represents
 * the track running in the given direction.
 */
inline Trackdir TrackDirectionToTrackdir(Track track, Direction dir)
{
	assert(IsValidTrack(track));
	assert(IsValidDirection(dir));
	extern const Trackdir _track_direction_to_trackdir[TRACK_END][DIR_END];
	return _track_direction_to_trackdir[track][dir];
}

/**
 * Maps a (4-way) direction to the diagonal track incidating with that diagdir
 *
 * @param diagdir The direction
 * @return The resulting Track
 */
inline Track DiagDirToDiagTrack(DiagDirection diagdir)
{
	assert(IsValidDiagDirection(diagdir));
	return (Track)(diagdir & 1);
}

/**
 * Maps a (4-way) direction to the diagonal track bits incidating with that diagdir
 *
 * @param diagdir The direction
 * @return The resulting TrackBits
 */
inline TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
{
	assert(IsValidDiagDirection(diagdir));
	return TrackToTrackBits(DiagDirToDiagTrack(diagdir));
}

/**
 * Maps a (4-way) direction to the diagonal trackdir that runs in that
 * direction.
 *
 * @param diagdir The direction
 * @return The resulting Trackdir direction
 */
inline Trackdir DiagDirToDiagTrackdir(DiagDirection diagdir)
{
	assert(IsValidDiagDirection(diagdir));
	extern const Trackdir _dir_to_diag_trackdir[DIAGDIR_END];
	return _dir_to_diag_trackdir[diagdir];
}

/**
 * Returns all trackdirs that can be reached when entering a tile from a given
 * (diagonal) direction.
 *
 * This will obviously include 90 degree turns, since no information is available
 * about the exact angle of entering
 *
 * @param diagdir The joining direction
 * @return The TrackdirBits which can be used from the given direction
 * @see DiagdirReachesTracks
 */
inline TrackdirBits DiagdirReachesTrackdirs(DiagDirection diagdir)
{
	assert(IsValidDiagDirection(diagdir));
	extern const TrackdirBits _exitdir_reaches_trackdirs[DIAGDIR_END];
	return _exitdir_reaches_trackdirs[diagdir];
}

/**
 * Returns all tracks that can be reached when entering a tile from a given
 * (diagonal) direction.
 *
 * This will obviously include 90 degree turns, since no
 * information is available about the exact angle of entering
 *
 * @param diagdir The joining direction
 * @return The tracks which can be used
 * @see DiagdirReachesTrackdirs
 */
inline TrackBits DiagdirReachesTracks(DiagDirection diagdir) { return TrackdirBitsToTrackBits(DiagdirReachesTrackdirs(diagdir)); }

/**
 * Maps a trackdir to the trackdirs that can be reached from it (ie, when
 * entering the next tile.
 *
 * This will include 90 degree turns!
 *
 * @param trackdir The track direction which will be leaved
 * @return The track directions which can be used from this direction (in the next tile)
 */
inline TrackdirBits TrackdirReachesTrackdirs(Trackdir trackdir)
{
	assert(IsValidTrackdir(trackdir));
	extern const TrackdirBits _exitdir_reaches_trackdirs[DIAGDIR_END];
	return _exitdir_reaches_trackdirs[TrackdirToExitdir(trackdir)];
}
/* Note that there is no direct table for this function (there used to be),
 * but it uses two simpler tables to achieve the result */

/**
 * Maps a trackdir to all trackdirs that make 90 deg turns with it.
 *
 * For the diagonal tracks this returns the track direction bits
 * of the other axis in both directions, which cannot be joined by
 * the given track direction.
 * For the straight tracks this returns all possible 90 deg turns
 * either on the current tile (which no train can joined) or on the
 * bordering tiles.
 *
 * @param trackdir The track direction
 * @return The TrackdirBits which are (more or less) 90 deg turns.
 */
inline TrackdirBits TrackdirCrossesTrackdirs(Trackdir trackdir)
{
	assert(IsValidTrackdirForRoadVehicle(trackdir));
	extern const TrackdirBits _track_crosses_trackdirs[TRACK_END];
	return _track_crosses_trackdirs[TrackdirToTrack(trackdir)];
}

/**
 * Checks if a given Track is diagonal
 *
 * @param track The given track to check
 * @return true if diagonal, else false
 */
inline bool IsDiagonalTrack(Track track)
{
	assert(IsValidTrack(track));
	return (track == TRACK_X) || (track == TRACK_Y);
}

/**
 * Checks if a given Trackdir is diagonal.
 *
 * @param trackdir The given trackdir
 * @return true if the trackdir use a diagonal track
 */
inline bool IsDiagonalTrackdir(Trackdir trackdir)
{
	assert(IsValidTrackdir(trackdir));
	return IsDiagonalTrack(TrackdirToTrack(trackdir));
}


/**
 * Checks if the given tracks overlap, ie form a crossing. Basically this
 * means when there is more than one track on the tile, except when there are
 * two parallel tracks.
 * @param  bits The tracks present.
 * @return Whether the tracks present overlap in any way.
 */
inline bool TracksOverlap(TrackBits bits)
{
	/* With no, or only one track, there is no overlap */
	if (bits == TRACK_BIT_NONE || KillFirstBit(bits) == TRACK_BIT_NONE) return false;
	/* We know that there are at least two tracks present. When there are more
	 * than 2 tracks, they will surely overlap. When there are two, they will
	 * always overlap unless they are lower & upper or right & left. */
	return bits != TRACK_BIT_HORZ && bits != TRACK_BIT_VERT;
}

/**
 * Check if a given track is contained within or overlaps some other tracks.
 *
 * @param tracks Tracks to be tested against
 * @param track The track to test
 * @return true if the track is already in the tracks or overlaps the tracks.
 */
inline bool TrackOverlapsTracks(TrackBits tracks, Track track)
{
	if (HasBit(tracks, track)) return true;
	return TracksOverlap(tracks | TrackToTrackBits(track));
}

/**
 * Checks whether the trackdir means that we are reversing.
 * @param dir the trackdir to check
 * @return true if it is a reversing road trackdir
 */
inline bool IsReversingRoadTrackdir(Trackdir dir)
{
	assert(IsValidTrackdirForRoadVehicle(dir));
	return (dir & 0x07) >= 6;
}

/**
 * Checks whether the given trackdir is a straight road
 * @param dir the trackdir to check
 * @return true if it is a straight road trackdir
 */
inline bool IsStraightRoadTrackdir(Trackdir dir)
{
	assert(IsValidTrackdirForRoadVehicle(dir));
	return (dir & 0x06) == 0;
}

/**
 * Checks whether a trackdir on a specific slope is going uphill.
 *
 * Valid for rail and road tracks.
 * Valid for tile-slopes (under foundation) and foundation-slopes (on foundation).
 *
 * @param slope The slope of the tile.
 * @param dir The trackdir of interest.
 * @return true iff the track goes upwards.
 */
inline bool IsUphillTrackdir(Slope slope, Trackdir dir)
{
	assert(IsValidTrackdirForRoadVehicle(dir));
	extern const TrackdirBits _uphill_trackdirs[];
	return HasBit(_uphill_trackdirs[RemoveHalftileSlope(slope)], dir);
}

/**
 * Determine the side in which the vehicle will leave the tile
 *
 * @param direction vehicle direction
 * @param track vehicle track bits
 * @return side of tile the vehicle will leave
 */
inline DiagDirection VehicleExitDir(Direction direction, TrackBits track)
{
	static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER };

	DiagDirection diagdir = DirToDiagDir(direction);

	/* Determine the diagonal direction in which we will exit this tile */
	if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) {
		diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT);
	}

	return diagdir;
}

#endif /* TRACK_FUNC_H */