00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../../stdafx.h"
00013 #include "../../debug.h"
00014 #include "../../landscape.h"
00015 #include "../../depot_base.h"
00016 #include "../../network/network.h"
00017 #include "../../tunnelbridge_map.h"
00018 #include "../../functions.h"
00019 #include "../../tunnelbridge.h"
00020 #include "../../pbs.h"
00021 #include "../../roadveh.h"
00022 #include "../../ship.h"
00023 #include "../../train.h"
00024 #include "../../roadstop_base.h"
00025 #include "../pathfinder_func.h"
00026 #include "../pathfinder_type.h"
00027 #include "../follow_track.hpp"
00028 #include "aystar.h"
00029
00030 enum {
00031 NPF_HASH_BITS = 12,
00032
00033 NPF_HASH_SIZE = 1 << NPF_HASH_BITS,
00034 NPF_HASH_HALFBITS = NPF_HASH_BITS / 2,
00035 NPF_HASH_HALFMASK = (1 << NPF_HASH_HALFBITS) - 1
00036 };
00037
00038
00039 struct NPFFindStationOrTileData {
00040 TileIndex dest_coords;
00041 StationID station_index;
00042 bool reserve_path;
00043 StationType station_type;
00044 bool not_articulated;
00045 const Vehicle *v;
00046 };
00047
00048
00049 enum {
00050 NPF_TYPE = 0,
00051 NPF_SUB_TYPE,
00052 NPF_OWNER,
00053 NPF_RAILTYPES,
00054 };
00055
00056
00057 enum {
00058 NPF_TRACKDIR_CHOICE = 0,
00059 NPF_NODE_FLAGS,
00060 };
00061
00062
00063 enum NPFNodeFlag {
00064 NPF_FLAG_SEEN_SIGNAL,
00065 NPF_FLAG_2ND_SIGNAL,
00066 NPF_FLAG_3RD_SIGNAL,
00067 NPF_FLAG_REVERSE,
00068 NPF_FLAG_LAST_SIGNAL_RED,
00069 NPF_FLAG_LAST_SIGNAL_BLOCK,
00070 NPF_FLAG_IGNORE_START_TILE,
00071 NPF_FLAG_TARGET_RESERVED,
00072 NPF_FLAG_IGNORE_RESERVED,
00073 };
00074
00075
00076 struct NPFFoundTargetData {
00077 uint best_bird_dist;
00078 uint best_path_dist;
00079 Trackdir best_trackdir;
00080 AyStarNode node;
00081 bool res_okay;
00082 };
00083
00084 static AyStar _npf_aystar;
00085
00086
00087
00088
00089 #define NPF_STRAIGHT_LENGTH (uint)(NPF_TILE_LENGTH * STRAIGHT_TRACK_LENGTH)
00090 static const uint _trackdir_length[TRACKDIR_END] = {
00091 NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH,
00092 0, 0,
00093 NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH
00094 };
00095
00099 static inline bool NPFGetFlag(const AyStarNode *node, NPFNodeFlag flag)
00100 {
00101 return HasBit(node->user_data[NPF_NODE_FLAGS], flag);
00102 }
00103
00107 static inline void NPFSetFlag(AyStarNode *node, NPFNodeFlag flag, bool value)
00108 {
00109 SB(node->user_data[NPF_NODE_FLAGS], flag, 1, value);
00110 }
00111
00118 static uint NPFDistanceTrack(TileIndex t0, TileIndex t1)
00119 {
00120 const uint dx = Delta(TileX(t0), TileX(t1));
00121 const uint dy = Delta(TileY(t0), TileY(t1));
00122
00123 const uint straightTracks = 2 * min(dx, dy);
00124
00125
00126
00127
00128 const uint diagTracks = dx + dy - straightTracks;
00129
00130
00131
00132 return diagTracks * NPF_TILE_LENGTH + straightTracks * NPF_TILE_LENGTH * STRAIGHT_TRACK_LENGTH;
00133 }
00134
00135
00136 #if 0
00137 static uint NTPHash(uint key1, uint key2)
00138 {
00139
00140 return PATHFIND_HASH_TILE(key1);
00141 }
00142 #endif
00143
00151 static uint NPFHash(uint key1, uint key2)
00152 {
00153
00154 uint part1 = TileX(key1) & NPF_HASH_HALFMASK;
00155 uint part2 = TileY(key1) & NPF_HASH_HALFMASK;
00156
00157 assert(IsValidTrackdir((Trackdir)key2));
00158 assert(IsValidTile(key1));
00159 return ((part1 << NPF_HASH_HALFBITS | part2) + (NPF_HASH_SIZE * key2 / TRACKDIR_END)) % NPF_HASH_SIZE;
00160 }
00161
00162 static int32 NPFCalcZero(AyStar *as, AyStarNode *current, OpenListNode *parent)
00163 {
00164 return 0;
00165 }
00166
00167
00168
00169
00170 static int32 NPFCalcStationOrTileHeuristic(AyStar *as, AyStarNode *current, OpenListNode *parent)
00171 {
00172 NPFFindStationOrTileData *fstd = (NPFFindStationOrTileData*)as->user_target;
00173 NPFFoundTargetData *ftd = (NPFFoundTargetData*)as->user_path;
00174 TileIndex from = current->tile;
00175 TileIndex to = fstd->dest_coords;
00176 uint dist;
00177
00178
00179 if (as->user_data[NPF_TYPE] != TRANSPORT_WATER && fstd->station_index != INVALID_STATION)
00180 to = CalcClosestStationTile(fstd->station_index, from, fstd->station_type);
00181
00182 if (as->user_data[NPF_TYPE] == TRANSPORT_ROAD) {
00183
00184 dist = DistanceManhattan(from, to) * NPF_TILE_LENGTH;
00185 } else {
00186
00187 dist = NPFDistanceTrack(from, to);
00188 }
00189
00190 DEBUG(npf, 4, "Calculating H for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), dist);
00191
00192 if (dist < ftd->best_bird_dist) {
00193 ftd->best_bird_dist = dist;
00194 ftd->best_trackdir = (Trackdir)current->user_data[NPF_TRACKDIR_CHOICE];
00195 }
00196 return dist;
00197 }
00198
00199
00200
00201
00202
00203 static void NPFFillTrackdirChoice(AyStarNode *current, OpenListNode *parent)
00204 {
00205 if (parent->path.parent == NULL) {
00206 Trackdir trackdir = current->direction;
00207
00208
00209 current->user_data[NPF_TRACKDIR_CHOICE] = trackdir;
00210 DEBUG(npf, 6, "Saving trackdir: 0x%X", trackdir);
00211 } else {
00212
00213 current->user_data[NPF_TRACKDIR_CHOICE] = parent->path.node.user_data[NPF_TRACKDIR_CHOICE];
00214 }
00215 }
00216
00217
00218
00219
00220 static uint NPFTunnelCost(AyStarNode *current)
00221 {
00222 DiagDirection exitdir = TrackdirToExitdir(current->direction);
00223 TileIndex tile = current->tile;
00224 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(exitdir)) {
00225
00226
00227 return NPF_TILE_LENGTH * (GetTunnelBridgeLength(current->tile, GetOtherTunnelEnd(current->tile)) + 1);
00228
00229 } else {
00230
00231
00232 return NPF_TILE_LENGTH;
00233 }
00234 }
00235
00236 static inline uint NPFBridgeCost(AyStarNode *current)
00237 {
00238 return NPF_TILE_LENGTH * GetTunnelBridgeLength(current->tile, GetOtherBridgeEnd(current->tile));
00239 }
00240
00241 static uint NPFSlopeCost(AyStarNode *current)
00242 {
00243 TileIndex next = current->tile + TileOffsByDiagDir(TrackdirToExitdir(current->direction));
00244
00245
00246 int x1 = TileX(current->tile) * TILE_SIZE + TILE_SIZE / 2;
00247 int y1 = TileY(current->tile) * TILE_SIZE + TILE_SIZE / 2;
00248 int x2 = TileX(next) * TILE_SIZE + TILE_SIZE / 2;
00249 int y2 = TileY(next) * TILE_SIZE + TILE_SIZE / 2;
00250
00251 int dx4 = (x2 - x1) / 4;
00252 int dy4 = (y2 - y1) / 4;
00253
00254
00255
00256
00257 int z1 = GetSlopeZ(x1 + dx4, y1 + dy4);
00258 int z2 = GetSlopeZ(x2 - dx4, y2 - dy4);
00259
00260 if (z2 - z1 > 1) {
00261
00262 return _settings_game.pf.npf.npf_rail_slope_penalty;
00263 }
00264 return 0;
00265
00266
00267
00268 }
00269
00270 static uint NPFReservedTrackCost(AyStarNode *current)
00271 {
00272 TileIndex tile = current->tile;
00273 TrackBits track = TrackToTrackBits(TrackdirToTrack(current->direction));
00274 TrackBits res = GetReservedTrackbits(tile);
00275
00276 if (NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL) || NPFGetFlag(current, NPF_FLAG_LAST_SIGNAL_BLOCK) || ((res & track) == TRACK_BIT_NONE && !TracksOverlap(res | track))) return 0;
00277
00278 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
00279 DiagDirection exitdir = TrackdirToExitdir(current->direction);
00280 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(exitdir)) {
00281 return _settings_game.pf.npf.npf_rail_pbs_cross_penalty * (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 1);
00282 }
00283 }
00284 return _settings_game.pf.npf.npf_rail_pbs_cross_penalty;
00285 }
00286
00291 static void NPFMarkTile(TileIndex tile)
00292 {
00293 #ifndef NO_DEBUG_MESSAGES
00294 if (_debug_npf_level < 1 || _networking) return;
00295 switch (GetTileType(tile)) {
00296 case MP_RAILWAY:
00297
00298 if (!IsRailDepot(tile)) {
00299 SetRailGroundType(tile, RAIL_GROUND_BARREN);
00300 MarkTileDirtyByTile(tile);
00301 }
00302 break;
00303
00304 case MP_ROAD:
00305 if (!IsRoadDepot(tile)) {
00306 SetRoadside(tile, ROADSIDE_BARREN);
00307 MarkTileDirtyByTile(tile);
00308 }
00309 break;
00310
00311 default:
00312 break;
00313 }
00314 #endif
00315 }
00316
00317 static int32 NPFWaterPathCost(AyStar *as, AyStarNode *current, OpenListNode *parent)
00318 {
00319
00320 int32 cost = 0;
00321 Trackdir trackdir = current->direction;
00322
00323 cost = _trackdir_length[trackdir];
00324
00325 if (IsBuoyTile(current->tile) && IsDiagonalTrackdir(trackdir))
00326 cost += _settings_game.pf.npf.npf_buoy_penalty;
00327
00328 if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction))
00329 cost += _settings_game.pf.npf.npf_water_curve_penalty;
00330
00331
00332
00333 return cost;
00334 }
00335
00336
00337 static int32 NPFRoadPathCost(AyStar *as, AyStarNode *current, OpenListNode *parent)
00338 {
00339 TileIndex tile = current->tile;
00340 int32 cost = 0;
00341
00342
00343 switch (GetTileType(tile)) {
00344 case MP_TUNNELBRIDGE:
00345 cost = IsTunnel(tile) ? NPFTunnelCost(current) : NPFBridgeCost(current);
00346 break;
00347
00348 case MP_ROAD:
00349 cost = NPF_TILE_LENGTH;
00350
00351 if (IsLevelCrossing(tile)) cost += _settings_game.pf.npf.npf_crossing_penalty;
00352 break;
00353
00354 case MP_STATION: {
00355 cost = NPF_TILE_LENGTH;
00356 const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
00357 if (IsDriveThroughStopTile(tile)) {
00358
00359 cost += _settings_game.pf.npf.npf_road_drive_through_penalty;
00360 DiagDirection dir = TrackdirToExitdir(current->direction);
00361 if (!RoadStop::IsDriveThroughRoadStopContinuation(tile, tile - TileOffsByDiagDir(dir))) {
00362
00363
00364 const RoadStop::Entry *entry = rs->GetEntry(dir);
00365 cost += entry->GetOccupied() * _settings_game.pf.npf.npf_road_dt_occupied_penalty / entry->GetLength();
00366 }
00367 } else {
00368
00369 cost += _settings_game.pf.npf.npf_road_bay_occupied_penalty * (!rs->IsFreeBay(0) + !rs->IsFreeBay(1)) / 2;
00370 }
00371 } break;
00372
00373 default:
00374 break;
00375 }
00376
00377
00378
00379
00380 cost += NPFSlopeCost(current);
00381
00382
00383
00384 if (!IsDiagonalTrackdir(current->direction))
00385 cost += _settings_game.pf.npf.npf_road_curve_penalty;
00386
00387 NPFMarkTile(tile);
00388 DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
00389 return cost;
00390 }
00391
00392
00393
00394 static int32 NPFRailPathCost(AyStar *as, AyStarNode *current, OpenListNode *parent)
00395 {
00396 TileIndex tile = current->tile;
00397 Trackdir trackdir = current->direction;
00398 int32 cost = 0;
00399
00400 OpenListNode new_node;
00401
00402
00403 switch (GetTileType(tile)) {
00404 case MP_TUNNELBRIDGE:
00405 cost = IsTunnel(tile) ? NPFTunnelCost(current) : NPFBridgeCost(current);
00406 break;
00407
00408 case MP_RAILWAY:
00409 cost = _trackdir_length[trackdir];
00410 break;
00411
00412 case MP_ROAD:
00413 cost = NPF_TILE_LENGTH;
00414 break;
00415
00416 case MP_STATION:
00417
00418
00419
00420
00421
00422
00423 cost = NPF_TILE_LENGTH + _settings_game.pf.npf.npf_rail_station_penalty;
00424
00425 if (IsRailWaypoint(tile)) {
00426 NPFFindStationOrTileData *fstd = (NPFFindStationOrTileData*)as->user_target;
00427 if (fstd->v->current_order.IsType(OT_GOTO_WAYPOINT) && GetStationIndex(tile) == fstd->v->current_order.GetDestination()) {
00428
00429
00430
00431 const Train *train = Train::From(fstd->v);
00432 CFollowTrackRail ft(train);
00433 TileIndex t = tile;
00434 Trackdir td = trackdir;
00435 while (ft.Follow(t, td)) {
00436 assert(t != ft.m_new_tile);
00437 t = ft.m_new_tile;
00438 if (KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) {
00439
00440
00441
00442 td = INVALID_TRACKDIR;
00443 break;
00444 }
00445 td = RemoveFirstTrackdir(&ft.m_new_td_bits);
00446
00447 if (IsSafeWaitingPosition(train, t, td, true, _settings_game.pf.forbid_90_deg)) break;
00448 }
00449 if (td == INVALID_TRACKDIR ||
00450 !IsSafeWaitingPosition(train, t, td, true, _settings_game.pf.forbid_90_deg) ||
00451 !IsWaitingPositionFree(train, t, td, _settings_game.pf.forbid_90_deg)) {
00452 cost += _settings_game.pf.npf.npf_rail_lastred_penalty;
00453 }
00454 }
00455 }
00456 break;
00457
00458 default:
00459 break;
00460 }
00461
00462
00463
00464
00465 if (IsTileType(tile, MP_RAILWAY)) {
00466 if (HasSignalOnTrackdir(tile, trackdir)) {
00467 SignalType sigtype = GetSignalType(tile, TrackdirToTrack(trackdir));
00468
00469 if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) {
00470
00471 if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
00472
00473
00474
00475
00476 if (!IsPbsSignal(sigtype)) {
00477 if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) {
00478
00479 cost += _settings_game.pf.npf.npf_rail_firstred_exit_penalty;
00480 } else {
00481 cost += _settings_game.pf.npf.npf_rail_firstred_penalty;
00482 }
00483 }
00484 }
00485
00486 NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
00487 } else {
00488
00489 NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
00490 }
00491 if (NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
00492 if (NPFGetFlag(current, NPF_FLAG_2ND_SIGNAL)) {
00493 NPFSetFlag(current, NPF_FLAG_3RD_SIGNAL, true);
00494 } else {
00495 NPFSetFlag(current, NPF_FLAG_2ND_SIGNAL, true);
00496 }
00497 } else {
00498 NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
00499 }
00500 NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_BLOCK, !IsPbsSignal(sigtype));
00501 }
00502
00503 if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(trackdir)) && !NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL)) {
00504 cost += _settings_game.pf.npf.npf_rail_pbs_signal_back_penalty;
00505 }
00506 }
00507
00508
00509
00510
00511
00512 new_node.path.node = *current;
00513 if (as->EndNodeCheck(as, &new_node) == AYSTAR_FOUND_END_NODE && NPFGetFlag(current, NPF_FLAG_LAST_SIGNAL_RED))
00514 cost += _settings_game.pf.npf.npf_rail_lastred_penalty;
00515
00516
00517 cost += NPFSlopeCost(current);
00518
00519
00520 if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction))
00521 cost += _settings_game.pf.npf.npf_rail_curve_penalty;
00522
00523
00524
00525
00526 if (IsRailDepotTile(tile) && as->EndNodeCheck(as, &new_node) != AYSTAR_FOUND_END_NODE) {
00527
00528
00529
00530 cost += _settings_game.pf.npf.npf_rail_depot_reverse_penalty;
00531 }
00532
00533
00534 cost += NPFReservedTrackCost(current);
00535
00536 NPFMarkTile(tile);
00537 DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
00538 return cost;
00539 }
00540
00541
00542 static int32 NPFFindDepot(AyStar *as, OpenListNode *current)
00543 {
00544
00545
00546 return IsDepotTypeTile(current->path.node.tile, (TransportType)as->user_data[NPF_TYPE]) ?
00547 AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
00548 }
00549
00551 static int32 NPFFindSafeTile(AyStar *as, OpenListNode *current)
00552 {
00553 const Train *v = Train::From(((NPFFindStationOrTileData *)as->user_target)->v);
00554
00555 return
00556 IsSafeWaitingPosition(v, current->path.node.tile, current->path.node.direction, true, _settings_game.pf.forbid_90_deg) &&
00557 IsWaitingPositionFree(v, current->path.node.tile, current->path.node.direction, _settings_game.pf.forbid_90_deg) ?
00558 AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
00559 }
00560
00561
00562 static int32 NPFFindStationOrTile(AyStar *as, OpenListNode *current)
00563 {
00564 NPFFindStationOrTileData *fstd = (NPFFindStationOrTileData*)as->user_target;
00565 AyStarNode *node = ¤t->path.node;
00566 TileIndex tile = node->tile;
00567
00568 if (fstd->station_index == INVALID_STATION && tile == fstd->dest_coords) return AYSTAR_FOUND_END_NODE;
00569
00570 if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) {
00571 if (fstd->v->type == VEH_TRAIN) return AYSTAR_FOUND_END_NODE;
00572
00573 assert(fstd->v->type == VEH_ROAD);
00574
00575 if (GetStationType(tile) == fstd->station_type && (fstd->not_articulated || IsDriveThroughStopTile(tile))) return AYSTAR_FOUND_END_NODE;
00576 }
00577 return AYSTAR_DONE;
00578 }
00579
00587 static const PathNode *FindSafePosition(PathNode *path, const Train *v)
00588 {
00589
00590 PathNode *sig = path;
00591
00592 for (; path->parent != NULL; path = path->parent) {
00593 if (IsSafeWaitingPosition(v, path->node.tile, path->node.direction, true, _settings_game.pf.forbid_90_deg)) {
00594 sig = path;
00595 }
00596 }
00597
00598 return sig;
00599 }
00600
00604 static void ClearPathReservation(const PathNode *start, const PathNode *end)
00605 {
00606 bool first_run = true;
00607 for (; start != end; start = start->parent) {
00608 if (IsRailStationTile(start->node.tile) && first_run) {
00609 SetRailStationPlatformReservation(start->node.tile, TrackdirToExitdir(start->node.direction), false);
00610 } else {
00611 UnreserveRailTrack(start->node.tile, TrackdirToTrack(start->node.direction));
00612 }
00613 first_run = false;
00614 }
00615 }
00616
00623 static void NPFSaveTargetData(AyStar *as, OpenListNode *current)
00624 {
00625 NPFFoundTargetData *ftd = (NPFFoundTargetData*)as->user_path;
00626 ftd->best_trackdir = (Trackdir)current->path.node.user_data[NPF_TRACKDIR_CHOICE];
00627 ftd->best_path_dist = current->g;
00628 ftd->best_bird_dist = 0;
00629 ftd->node = current->path.node;
00630 ftd->res_okay = false;
00631
00632 if (as->user_target != NULL && ((NPFFindStationOrTileData*)as->user_target)->reserve_path && as->user_data[NPF_TYPE] == TRANSPORT_RAIL) {
00633
00634 const Train *v = Train::From(((NPFFindStationOrTileData *)as->user_target)->v);
00635
00636 const PathNode *target = FindSafePosition(¤t->path, v);
00637 ftd->node = target->node;
00638
00639
00640 if (IsRailStationTile(target->node.tile)) {
00641 DiagDirection dir = TrackdirToExitdir(target->node.direction);
00642 uint len = Station::GetByTile(target->node.tile)->GetPlatformLength(target->node.tile, dir);
00643 TileIndex end_tile = TILE_ADD(target->node.tile, (len - 1) * TileOffsByDiagDir(dir));
00644
00645
00646 ftd->node.tile = end_tile;
00647 if (!IsWaitingPositionFree(v, end_tile, target->node.direction, _settings_game.pf.forbid_90_deg)) return;
00648 SetRailStationPlatformReservation(target->node.tile, dir, true);
00649 SetRailStationReservation(target->node.tile, false);
00650 } else {
00651 if (!IsWaitingPositionFree(v, target->node.tile, target->node.direction, _settings_game.pf.forbid_90_deg)) return;
00652 }
00653
00654 for (const PathNode *cur = target; cur->parent != NULL; cur = cur->parent) {
00655 if (!TryReserveRailTrack(cur->node.tile, TrackdirToTrack(cur->node.direction))) {
00656
00657 ClearPathReservation(target, cur);
00658 return;
00659 }
00660 }
00661
00662 ftd->res_okay = true;
00663 }
00664 }
00665
00675 static bool CanEnterTileOwnerCheck(Owner owner, TileIndex tile, DiagDirection enterdir)
00676 {
00677 if (IsTileType(tile, MP_RAILWAY) ||
00678 HasStationTileRail(tile) ||
00679 IsRoadDepotTile(tile) ||
00680 IsStandardRoadStopTile(tile)) {
00681 return IsTileOwner(tile, owner);
00682 }
00683
00684 switch (GetTileType(tile)) {
00685 case MP_ROAD:
00686
00687 if (IsLevelCrossing(tile) &&
00688 DiagDirToAxis(enterdir) != GetCrossingRoadAxis(tile)) {
00689 return IsTileOwner(tile, owner);
00690 }
00691 break;
00692
00693 case MP_TUNNELBRIDGE:
00694 if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
00695 return IsTileOwner(tile, owner);
00696 }
00697 break;
00698
00699 default:
00700 break;
00701 }
00702
00703 return true;
00704 }
00705
00706
00710 static DiagDirection GetDepotDirection(TileIndex tile, TransportType type)
00711 {
00712 assert(IsDepotTypeTile(tile, type));
00713
00714 switch (type) {
00715 case TRANSPORT_RAIL: return GetRailDepotDirection(tile);
00716 case TRANSPORT_ROAD: return GetRoadDepotDirection(tile);
00717 case TRANSPORT_WATER: return GetShipDepotDirection(tile);
00718 default: return INVALID_DIAGDIR;
00719 }
00720 }
00721
00723 static DiagDirection GetSingleTramBit(TileIndex tile)
00724 {
00725 if (IsNormalRoadTile(tile)) {
00726 RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM);
00727 switch (rb) {
00728 case ROAD_NW: return DIAGDIR_NW;
00729 case ROAD_SW: return DIAGDIR_SW;
00730 case ROAD_SE: return DIAGDIR_SE;
00731 case ROAD_NE: return DIAGDIR_NE;
00732 default: break;
00733 }
00734 }
00735 return INVALID_DIAGDIR;
00736 }
00737
00748 static DiagDirection GetTileSingleEntry(TileIndex tile, TransportType type, uint subtype)
00749 {
00750 if (type != TRANSPORT_WATER && IsDepotTypeTile(tile, type)) return GetDepotDirection(tile, type);
00751
00752 if (type == TRANSPORT_ROAD) {
00753 if (IsStandardRoadStopTile(tile)) return GetRoadStopDir(tile);
00754 if (HasBit(subtype, ROADTYPE_TRAM)) return GetSingleTramBit(tile);
00755 }
00756
00757 return INVALID_DIAGDIR;
00758 }
00759
00769 static inline bool ForceReverse(TileIndex tile, DiagDirection dir, TransportType type, uint subtype)
00770 {
00771 DiagDirection single_entry = GetTileSingleEntry(tile, type, subtype);
00772 return single_entry != INVALID_DIAGDIR && single_entry != dir;
00773 }
00774
00786 static bool CanEnterTile(TileIndex tile, DiagDirection dir, TransportType type, uint subtype, RailTypes railtypes, Owner owner)
00787 {
00788
00789 if (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(tile) != dir) return false;
00790
00791
00792 if (!CanEnterTileOwnerCheck(owner, tile, dir)) return false;
00793
00794
00795 if (type == TRANSPORT_RAIL) {
00796 RailType rail_type = GetTileRailType(tile);
00797 if (!HasBit(railtypes, rail_type)) return false;
00798 }
00799
00800
00801 DiagDirection single_entry = GetTileSingleEntry(tile, type, subtype);
00802 if (single_entry != INVALID_DIAGDIR && single_entry != ReverseDiagDir(dir)) return false;
00803
00804 return true;
00805 }
00806
00818 static TrackdirBits GetDriveableTrackdirBits(TileIndex dst_tile, Trackdir src_trackdir, TransportType type, uint subtype)
00819 {
00820 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(GetTileTrackStatus(dst_tile, type, subtype));
00821
00822 if (trackdirbits == 0 && type == TRANSPORT_ROAD && HasBit(subtype, ROADTYPE_TRAM)) {
00823
00824
00825 switch (GetSingleTramBit(dst_tile)) {
00826 case DIAGDIR_NE:
00827 case DIAGDIR_SW:
00828 trackdirbits = TRACKDIR_BIT_X_NE | TRACKDIR_BIT_X_SW;
00829 break;
00830
00831 case DIAGDIR_NW:
00832 case DIAGDIR_SE:
00833 trackdirbits = TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_Y_SE;
00834 break;
00835
00836 default: break;
00837 }
00838 }
00839
00840 DEBUG(npf, 4, "Next node: (%d, %d) [%d], possible trackdirs: 0x%X", TileX(dst_tile), TileY(dst_tile), dst_tile, trackdirbits);
00841
00842
00843 trackdirbits &= TrackdirReachesTrackdirs(src_trackdir);
00844
00845
00846 if (_settings_game.pf.forbid_90_deg && (type == TRANSPORT_RAIL || type == TRANSPORT_WATER)) trackdirbits &= ~TrackdirCrossesTrackdirs(src_trackdir);
00847
00848 DEBUG(npf, 6, "After filtering: (%d, %d), possible trackdirs: 0x%X", TileX(dst_tile), TileY(dst_tile), trackdirbits);
00849
00850 return trackdirbits;
00851 }
00852
00853
00854
00855
00856
00857
00858
00859
00860 static void NPFFollowTrack(AyStar *aystar, OpenListNode *current)
00861 {
00862
00863 Trackdir src_trackdir = current->path.node.direction;
00864 TileIndex src_tile = current->path.node.tile;
00865 DiagDirection src_exitdir = TrackdirToExitdir(src_trackdir);
00866
00867
00868
00869
00870 bool ignore_src_tile = (current->path.parent == NULL && NPFGetFlag(¤t->path.node, NPF_FLAG_IGNORE_START_TILE));
00871
00872
00873 TransportType type = (TransportType)aystar->user_data[NPF_TYPE];
00874 uint subtype = aystar->user_data[NPF_SUB_TYPE];
00875
00876
00877 aystar->num_neighbours = 0;
00878 DEBUG(npf, 4, "Expanding: (%d, %d, %d) [%d]", TileX(src_tile), TileY(src_tile), src_trackdir, src_tile);
00879
00880
00881 TileIndex dst_tile;
00882 TrackdirBits trackdirbits;
00883
00884
00885 if (ignore_src_tile) {
00886
00887 dst_tile = src_tile + TileOffsByDiagDir(src_exitdir);
00888 trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);
00889 } else if (IsTileType(src_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(src_tile) == src_exitdir) {
00890
00891 dst_tile = GetOtherTunnelBridgeEnd(src_tile);
00892 trackdirbits = TrackdirToTrackdirBits(src_trackdir);
00893 } else if (ForceReverse(src_tile, src_exitdir, type, subtype)) {
00894
00895 dst_tile = src_tile;
00896 src_trackdir = ReverseTrackdir(src_trackdir);
00897 trackdirbits = TrackdirToTrackdirBits(src_trackdir);
00898 } else {
00899
00900 dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(src_exitdir));
00901
00902 if (dst_tile != INVALID_TILE && !CanEnterTile(dst_tile, src_exitdir, type, subtype, (RailTypes)aystar->user_data[NPF_RAILTYPES], (Owner)aystar->user_data[NPF_OWNER])) dst_tile = INVALID_TILE;
00903
00904 if (dst_tile == INVALID_TILE) {
00905
00906 if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;
00907
00908 dst_tile = src_tile;
00909 src_trackdir = ReverseTrackdir(src_trackdir);
00910 }
00911
00912 trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);
00913
00914 if (trackdirbits == 0) {
00915
00916 if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;
00917
00918 dst_tile = src_tile;
00919 src_trackdir = ReverseTrackdir(src_trackdir);
00920
00921 trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);
00922 }
00923 }
00924
00925 if (NPFGetFlag(¤t->path.node, NPF_FLAG_IGNORE_RESERVED)) {
00926
00927 TrackBits reserved = GetReservedTrackbits(dst_tile);
00928 trackdirbits &= ~TrackBitsToTrackdirBits(reserved);
00929
00930 uint bits = TrackdirBitsToTrackBits(trackdirbits);
00931 int i;
00932 FOR_EACH_SET_BIT(i, bits) {
00933 if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) trackdirbits &= ~TrackToTrackdirBits((Track)i);
00934 }
00935 }
00936
00937
00938 uint i = 0;
00939 while (trackdirbits != 0) {
00940 Trackdir dst_trackdir = RemoveFirstTrackdir(&trackdirbits);
00941 DEBUG(npf, 5, "Expanded into trackdir: %d, remaining trackdirs: 0x%X", dst_trackdir, trackdirbits);
00942
00943
00944 if (IsTileType(dst_tile, MP_RAILWAY) && GetRailTileType(dst_tile) == RAIL_TILE_SIGNALS) {
00945 if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(dst_trackdir)) && !HasSignalOnTrackdir(dst_tile, dst_trackdir) && IsOnewaySignal(dst_tile, TrackdirToTrack(dst_trackdir)))
00946
00947 break;
00948 }
00949 {
00950
00951 AyStarNode *neighbour = &aystar->neighbours[i];
00952 neighbour->tile = dst_tile;
00953 neighbour->direction = dst_trackdir;
00954
00955 neighbour->user_data[NPF_NODE_FLAGS] = current->path.node.user_data[NPF_NODE_FLAGS];
00956 NPFFillTrackdirChoice(neighbour, current);
00957 }
00958 i++;
00959 }
00960 aystar->num_neighbours = i;
00961 }
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973 static NPFFoundTargetData NPFRouteInternal(AyStarNode *start1, bool ignore_start_tile1, AyStarNode *start2, bool ignore_start_tile2, NPFFindStationOrTileData *target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, uint sub_type, Owner owner, RailTypes railtypes, uint reverse_penalty)
00974 {
00975 int r;
00976 NPFFoundTargetData result;
00977
00978
00979 _npf_aystar.CalculateH = heuristic_proc;
00980 _npf_aystar.EndNodeCheck = target_proc;
00981 _npf_aystar.FoundEndNode = NPFSaveTargetData;
00982 _npf_aystar.GetNeighbours = NPFFollowTrack;
00983 switch (type) {
00984 default: NOT_REACHED();
00985 case TRANSPORT_RAIL: _npf_aystar.CalculateG = NPFRailPathCost; break;
00986 case TRANSPORT_ROAD: _npf_aystar.CalculateG = NPFRoadPathCost; break;
00987 case TRANSPORT_WATER: _npf_aystar.CalculateG = NPFWaterPathCost; break;
00988 }
00989
00990
00991 start1->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
00992 start1->user_data[NPF_NODE_FLAGS] = 0;
00993 NPFSetFlag(start1, NPF_FLAG_IGNORE_START_TILE, ignore_start_tile1);
00994 _npf_aystar.addstart(&_npf_aystar, start1, 0);
00995 if (start2) {
00996 start2->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
00997 start2->user_data[NPF_NODE_FLAGS] = 0;
00998 NPFSetFlag(start2, NPF_FLAG_IGNORE_START_TILE, ignore_start_tile2);
00999 NPFSetFlag(start2, NPF_FLAG_REVERSE, true);
01000 _npf_aystar.addstart(&_npf_aystar, start2, reverse_penalty);
01001 }
01002
01003
01004 result.best_bird_dist = UINT_MAX;
01005 result.best_path_dist = UINT_MAX;
01006 result.best_trackdir = INVALID_TRACKDIR;
01007 result.node.tile = INVALID_TILE;
01008 result.res_okay = false;
01009 _npf_aystar.user_path = &result;
01010
01011
01012 _npf_aystar.user_target = target;
01013
01014
01015 _npf_aystar.user_data[NPF_TYPE] = type;
01016 _npf_aystar.user_data[NPF_SUB_TYPE] = sub_type;
01017 _npf_aystar.user_data[NPF_OWNER] = owner;
01018 _npf_aystar.user_data[NPF_RAILTYPES] = railtypes;
01019
01020
01021 r = AyStarMain_Main(&_npf_aystar);
01022 assert(r != AYSTAR_STILL_BUSY);
01023
01024 if (result.best_bird_dist != 0) {
01025 if (target != NULL) {
01026 DEBUG(npf, 1, "Could not find route to tile 0x%X from 0x%X.", target->dest_coords, start1->tile);
01027 } else {
01028
01029 DEBUG(npf, 1, "Could not find route to a depot from tile 0x%X.", start1->tile);
01030 }
01031
01032 }
01033 return result;
01034 }
01035
01036
01037
01038
01039 static NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, bool ignore_start_tile1, TileIndex tile2, Trackdir trackdir2, bool ignore_start_tile2, NPFFindStationOrTileData *target, TransportType type, uint sub_type, Owner owner, RailTypes railtypes)
01040 {
01041 AyStarNode start1;
01042 AyStarNode start2;
01043
01044 start1.tile = tile1;
01045 start2.tile = tile2;
01046
01047
01048 start1.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
01049 start1.direction = trackdir1;
01050 start2.direction = trackdir2;
01051 start2.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
01052
01053 return NPFRouteInternal(&start1, ignore_start_tile1, (IsValidTile(tile2) ? &start2 : NULL), ignore_start_tile2, target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, sub_type, owner, railtypes, 0);
01054 }
01055
01056
01057
01058
01059 static NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, bool ignore_start_tile, NPFFindStationOrTileData *target, TransportType type, uint sub_type, Owner owner, RailTypes railtypes)
01060 {
01061 return NPFRouteToStationOrTileTwoWay(tile, trackdir, ignore_start_tile, INVALID_TILE, INVALID_TRACKDIR, false, target, type, sub_type, owner, railtypes);
01062 }
01063
01064
01065
01066
01067
01068
01069
01070
01071 static NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, bool ignore_start_tile1, TileIndex tile2, Trackdir trackdir2, bool ignore_start_tile2, TransportType type, uint sub_type, Owner owner, RailTypes railtypes, uint reverse_penalty)
01072 {
01073 AyStarNode start1;
01074 AyStarNode start2;
01075
01076 start1.tile = tile1;
01077 start2.tile = tile2;
01078
01079
01080 start1.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
01081 start1.direction = trackdir1;
01082 start2.direction = trackdir2;
01083 start2.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
01084
01085
01086
01087 return NPFRouteInternal(&start1, ignore_start_tile1, (IsValidTile(tile2) ? &start2 : NULL), ignore_start_tile2, NULL, NPFFindDepot, NPFCalcZero, type, sub_type, owner, railtypes, reverse_penalty);
01088 }
01089
01090 void InitializeNPF()
01091 {
01092 static bool first_init = true;
01093 if (first_init) {
01094 first_init = false;
01095 init_AyStar(&_npf_aystar, NPFHash, NPF_HASH_SIZE);
01096 } else {
01097 AyStarMain_Clear(&_npf_aystar);
01098 }
01099 _npf_aystar.loops_per_tick = 0;
01100 _npf_aystar.max_path_cost = 0;
01101
01102
01103
01104 _npf_aystar.max_search_nodes = _settings_game.pf.npf.npf_max_search_nodes;
01105 }
01106
01107 static void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, const Vehicle *v, bool reserve_path = false)
01108 {
01109
01110
01111
01112
01113
01114
01115 if (v->type != VEH_SHIP && (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_WAYPOINT))) {
01116 assert(v->type == VEH_TRAIN || v->type == VEH_ROAD);
01117 fstd->station_index = v->current_order.GetDestination();
01118 fstd->station_type = (v->type == VEH_TRAIN) ? (v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT) : (RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK);
01119 fstd->not_articulated = v->type == VEH_ROAD && !RoadVehicle::From(v)->HasArticulatedPart();
01120
01121 fstd->dest_coords = CalcClosestStationTile(fstd->station_index, v->tile, fstd->station_type);
01122 } else {
01123 fstd->dest_coords = v->dest_tile;
01124 fstd->station_index = INVALID_STATION;
01125 }
01126 fstd->reserve_path = reserve_path;
01127 fstd->v = v;
01128 }
01129
01130
01131
01132 FindDepotData NPFRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_penalty)
01133 {
01134 Trackdir trackdir = v->GetVehicleTrackdir();
01135
01136 NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, v->tile, ReverseTrackdir(trackdir), false, TRANSPORT_ROAD, v->compatible_roadtypes, v->owner, INVALID_RAILTYPES, 0);
01137
01138 if (ftd.best_bird_dist != 0) return FindDepotData();
01139
01140
01141
01142
01143
01144
01145 return FindDepotData(ftd.node.tile, ftd.best_path_dist);
01146 }
01147
01148 Trackdir NPFRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, TrackdirBits trackdirs)
01149 {
01150 NPFFindStationOrTileData fstd;
01151
01152 NPFFillWithOrderData(&fstd, v);
01153 Trackdir trackdir = DiagDirToDiagTrackdir(enterdir);
01154
01155 NPFFoundTargetData ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_ROAD, v->compatible_roadtypes, v->owner, INVALID_RAILTYPES);
01156 if (ftd.best_trackdir == INVALID_TRACKDIR) {
01157
01158
01159
01160 return (Trackdir)FindFirstBit2x64(trackdirs);
01161 }
01162
01163
01164
01165
01166
01167 return ftd.best_trackdir;
01168 }
01169
01170
01171
01172 Track NPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
01173 {
01174 NPFFindStationOrTileData fstd;
01175 Trackdir trackdir = v->GetVehicleTrackdir();
01176 assert(trackdir != INVALID_TRACKDIR);
01177
01178 NPFFillWithOrderData(&fstd, v);
01179
01180 NPFFoundTargetData ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_WATER, 0, v->owner, INVALID_RAILTYPES);
01181
01182
01183
01184
01185
01186 if (ftd.best_trackdir == 0xff) return INVALID_TRACK;
01187 return TrackdirToTrack(ftd.best_trackdir);
01188 }
01189
01190
01191
01192 FindDepotData NPFTrainFindNearestDepot(const Train *v, int max_penalty)
01193 {
01194 const Train *last = v->Last();
01195 Trackdir trackdir = v->GetVehicleTrackdir();
01196 Trackdir trackdir_rev = ReverseTrackdir(last->GetVehicleTrackdir());
01197
01198 assert(trackdir != INVALID_TRACKDIR);
01199 NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, last->tile, trackdir_rev, false, TRANSPORT_RAIL, 0, v->owner, v->compatible_railtypes, NPF_INFINITE_PENALTY);
01200 if (ftd.best_bird_dist != 0) return FindDepotData();
01201
01202
01203
01204
01205
01206
01207 return FindDepotData(ftd.node.tile, ftd.best_path_dist, NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE));
01208 }
01209
01210 bool NPFTrainFindNearestSafeTile(const Train *v, TileIndex tile, Trackdir trackdir, bool override_railtype)
01211 {
01212 assert(v->type == VEH_TRAIN);
01213
01214 NPFFindStationOrTileData fstd;
01215 fstd.v = v;
01216 fstd.reserve_path = true;
01217
01218 AyStarNode start1;
01219 start1.tile = tile;
01220
01221
01222 start1.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
01223 start1.direction = trackdir;
01224 NPFSetFlag(&start1, NPF_FLAG_IGNORE_RESERVED, true);
01225
01226 RailTypes railtypes = v->compatible_railtypes;
01227 if (override_railtype) railtypes |= GetRailTypeInfo(v->railtype)->compatible_railtypes;
01228
01229
01230
01231 return NPFRouteInternal(&start1, true, NULL, false, &fstd, NPFFindSafeTile, NPFCalcZero, TRANSPORT_RAIL, 0, v->owner, railtypes, 0).res_okay;
01232 }
01233
01234 bool NPFTrainCheckReverse(const Train *v)
01235 {
01236 NPFFindStationOrTileData fstd;
01237 NPFFoundTargetData ftd;
01238 const Train *last = v->Last();
01239
01240 NPFFillWithOrderData(&fstd, v);
01241
01242 Trackdir trackdir = v->GetVehicleTrackdir();
01243 Trackdir trackdir_rev = ReverseTrackdir(last->GetVehicleTrackdir());
01244 assert(trackdir != INVALID_TRACKDIR);
01245 assert(trackdir_rev != INVALID_TRACKDIR);
01246
01247 ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, false, last->tile, trackdir_rev, false, &fstd, TRANSPORT_RAIL, 0, v->owner, v->compatible_railtypes);
01248
01249 return ftd.best_bird_dist != 0 && NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE);
01250 }
01251
01252 Track NPFTrainChooseTrack(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, struct PBSTileInfo *target)
01253 {
01254 NPFFindStationOrTileData fstd;
01255 NPFFillWithOrderData(&fstd, v, reserve_track);
01256
01257 PBSTileInfo origin = FollowTrainReservation(v);
01258 assert(IsValidTrackdir(origin.trackdir));
01259
01260 NPFFoundTargetData ftd = NPFRouteToStationOrTile(origin.tile, origin.trackdir, true, &fstd, TRANSPORT_RAIL, 0, v->owner, v->compatible_railtypes);
01261
01262 if (target != NULL) {
01263 target->tile = ftd.node.tile;
01264 target->trackdir = (Trackdir)ftd.node.direction;
01265 target->okay = ftd.res_okay;
01266 }
01267
01268 if (ftd.best_trackdir == INVALID_TRACKDIR) {
01269
01270
01271
01272 if (path_not_found) *path_not_found = false;
01273 return FindFirstTrack(tracks);
01274 }
01275
01276
01277
01278
01279
01280 if (path_not_found != NULL) *path_not_found = (ftd.best_bird_dist != 0);
01281
01282 return TrackdirToTrack(ftd.best_trackdir);
01283 }