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