1
0
Fork 0
pull/13171/merge
Kuhnovic 2025-03-12 20:06:42 +00:00 committed by GitHub
commit 144c7c5f8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 88 additions and 113 deletions

View File

@ -20,11 +20,10 @@
/** /**
* Finds the best path for given ship using YAPF. * Finds the best path for given ship using YAPF.
* @param v the ship that needs to find a path * @param v the ship that needs to find a path
* @param tile the tile to find the path from (should be next tile the ship is about to enter)
* @param path_found [out] Whether a path has been found (true) or has been guessed (false) * @param path_found [out] Whether a path has been found (true) or has been guessed (false)
* @return the best trackdir for next turn or INVALID_TRACK if the path could not be found * @return the best trackdir for next turn. This includes potential reverse directions.
*/ */
Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipPathCache &path_cache); Trackdir YapfShipChooseTrack(const Ship *v, bool &path_found, ShipPathCache &path_cache);
/** /**
* Returns true if it is better to reverse the ship before leaving depot using YAPF. * Returns true if it is better to reverse the ship before leaving depot using YAPF.
@ -32,7 +31,7 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipP
* @param trackdir [out] the best of all possible reversed trackdirs * @param trackdir [out] the best of all possible reversed trackdirs
* @return true if reversing is better * @return true if reversing is better
*/ */
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir); bool YapfShipCheckReverse(const Ship *v, Trackdir &trackdir);
/** /**
* Finds the best path for given road vehicle using YAPF. * Finds the best path for given road vehicle using YAPF.

View File

@ -25,7 +25,9 @@ public:
protected: protected:
TileIndex origin_tile; ///< origin tile TileIndex origin_tile; ///< origin tile
TrackdirBits origin_trackdirs; ///< origin trackdir mask TrackdirBits origin_forward_trackdirs; ///< trackdirs considered for forward motion
TrackdirBits origin_reverse_trackdirs; ///< trackdirs considered for reversal
int reverse_penalty; ///< Cost penalty added for the reverse directions
/** to access inherited path finder */ /** to access inherited path finder */
inline Tpf &Yapf() inline Tpf &Yapf()
@ -35,21 +37,29 @@ protected:
public: public:
/** Set origin tile / trackdir mask */ /** Set origin tile / trackdir mask */
void SetOrigin(TileIndex tile, TrackdirBits trackdirs) void SetOrigin(TileIndex tile, TrackdirBits forward_trackdirs, TrackdirBits reverse_trackdirs = TRACKDIR_BIT_NONE, int reverse_penalty = 0)
{ {
assert((forward_trackdirs & reverse_trackdirs) == TRACKDIR_BIT_NONE); // Forward dirs can't also be reverse dirs and vice versa
this->origin_tile = tile; this->origin_tile = tile;
this->origin_trackdirs = trackdirs; this->origin_forward_trackdirs = forward_trackdirs;
this->origin_reverse_trackdirs = reverse_trackdirs;
this->reverse_penalty = reverse_penalty;
} }
/** Called when YAPF needs to place origin nodes into open list */ /** Called when YAPF needs to place origin nodes into open list */
void PfSetStartupNodes() void PfSetStartupNodes()
{ {
bool is_choice = (KillFirstBit(this->origin_trackdirs) != TRACKDIR_BIT_NONE); const bool is_choice = CountBits(this->origin_forward_trackdirs | this->origin_reverse_trackdirs) > 1;
for (TrackdirBits tdb = this->origin_trackdirs; tdb != TRACKDIR_BIT_NONE; tdb = KillFirstBit(tdb)) { for (Trackdir td : SetTrackdirBitIterator(this->origin_forward_trackdirs)) {
Trackdir td = (Trackdir)FindFirstBit(tdb); Node &node = Yapf().CreateNewNode();
Node &n1 = Yapf().CreateNewNode(); node.Set(nullptr, this->origin_tile, td, is_choice);
n1.Set(nullptr, this->origin_tile, td, is_choice); Yapf().AddStartupNode(node);
Yapf().AddStartupNode(n1); }
for (Trackdir td : SetTrackdirBitIterator(this->origin_reverse_trackdirs)) {
Node &node = Yapf().CreateNewNode();
node.Set(nullptr, this->origin_tile, td, is_choice);
node.cost = reverse_penalty;
Yapf().AddStartupNode(node);
} }
} }
}; };

View File

@ -20,7 +20,7 @@
constexpr int NUMBER_OR_WATER_REGIONS_LOOKAHEAD = 4; constexpr int NUMBER_OR_WATER_REGIONS_LOOKAHEAD = 4;
constexpr int MAX_SHIP_PF_NODES = (NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1) * WATER_REGION_NUMBER_OF_TILES * 4; // 4 possible exit dirs per tile. constexpr int MAX_SHIP_PF_NODES = (NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1) * WATER_REGION_NUMBER_OF_TILES * 4; // 4 possible exit dirs per tile.
constexpr int SHIP_REVERSE_PENALTY = 10 * YAPF_TILE_LENGTH;
constexpr int SHIP_LOST_PATH_LENGTH = 8; // The length of the (aimless) path assigned when a ship is lost. constexpr int SHIP_LOST_PATH_LENGTH = 8; // The length of the (aimless) path assigned when a ship is lost.
template <class Types> template <class Types>
@ -178,7 +178,7 @@ public:
return FindFirstTrackdir(trackdirs); return FindFirstTrackdir(trackdirs);
} }
/** Returns a random tile/trackdir that can be reached from the current tile/trackdir, or tile/INVALID_TRACK if none is available. */ /** Returns a random tile/trackdir that can be reached from the current tile/trackdir, or tile/INVALID_TRACKDIR if none is available. */
static std::pair<TileIndex, Trackdir> GetRandomFollowUpTileTrackdir(const Ship *v, TileIndex tile, Trackdir dir) static std::pair<TileIndex, Trackdir> GetRandomFollowUpTileTrackdir(const Ship *v, TileIndex tile, Trackdir dir)
{ {
TrackFollower follower(v); TrackFollower follower(v);
@ -198,10 +198,10 @@ public:
for (int i = 0; i < path_length; ++i) { for (int i = 0; i < path_length; ++i) {
tile_dir = GetRandomFollowUpTileTrackdir(v, tile_dir.first, tile_dir.second); tile_dir = GetRandomFollowUpTileTrackdir(v, tile_dir.first, tile_dir.second);
if (tile_dir.second == INVALID_TRACKDIR) break; if (tile_dir.second == INVALID_TRACKDIR) break;
path_cache.push_back(tile_dir.second); path_cache.emplace_back(tile_dir.second);
} }
if (path_cache.empty()) return INVALID_TRACKDIR; if (path_cache.empty()) return ReverseTrackdir(v->GetVehicleTrackdir());
/* Reverse the path so we can take from the end. */ /* Reverse the path so we can take from the end. */
std::reverse(std::begin(path_cache), std::end(path_cache)); std::reverse(std::begin(path_cache), std::end(path_cache));
@ -211,10 +211,15 @@ public:
return result; return result;
} }
static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs, static Trackdir ChooseShipTrack(const Ship *v, TrackdirBits forward_dirs, TrackdirBits reverse_dirs,
bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir) bool &path_found, ShipPathCache &path_cache)
{ {
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1); assert(HasBit(forward_dirs, v->GetVehicleTrackdir()));
assert(HasBit(reverse_dirs, ReverseTrackdir(v->GetVehicleTrackdir())));
assert((forward_dirs & reverse_dirs) == TRACKDIR_BIT_NONE);
assert((TrackdirReachesTrackdirs(v->GetVehicleTrackdir()) & reverse_dirs) == TRACKDIR_BIT_NONE);
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, v->tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
if (high_level_path.empty()) { if (high_level_path.empty()) {
path_found = false; path_found = false;
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */ /* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
@ -228,7 +233,7 @@ public:
Tpf pf(MAX_SHIP_PF_NODES); Tpf pf(MAX_SHIP_PF_NODES);
/* Set origin and destination nodes */ /* Set origin and destination nodes */
pf.SetOrigin(v->tile, forward_dirs | reverse_dirs); pf.SetOrigin(v->tile, forward_dirs, reverse_dirs, SHIP_REVERSE_PENALTY);
pf.SetDestination(v); pf.SetDestination(v);
const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1; const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back()); if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back());
@ -249,17 +254,16 @@ public:
* to the final destination tile. The low-level pathfinder might actually prefer a different docking tile in a nearby region. Without * to the final destination tile. The low-level pathfinder might actually prefer a different docking tile in a nearby region. Without
* caching the full path the ship can get stuck in a loop. */ * caching the full path the ship can get stuck in a loop. */
const WaterRegionPatchDesc end_water_patch = GetWaterRegionPatchInfo(node->GetTile()); const WaterRegionPatchDesc end_water_patch = GetWaterRegionPatchInfo(node->GetTile());
assert(GetWaterRegionPatchInfo(tile) == high_level_path.front()); assert(GetWaterRegionPatchInfo(v->tile) == high_level_path.front());
const WaterRegionPatchDesc start_water_patch = high_level_path.front(); const WaterRegionPatchDesc start_patch = high_level_path.front();
while (node->parent) { while (node->parent) {
const WaterRegionPatchDesc node_water_patch = GetWaterRegionPatchInfo(node->GetTile()); const WaterRegionPatchDesc current_patch = GetWaterRegionPatchInfo(node->parent->GetTile());
const bool current_patch_on_high_level_path = std::ranges::find(high_level_path, current_patch) != high_level_path.end();
const bool node_water_patch_on_high_level_path = std::ranges::find(high_level_path, node_water_patch) != high_level_path.end(); const bool add_full_path = !is_intermediate_destination && current_patch != end_water_patch;
const bool add_full_path = !is_intermediate_destination && node_water_patch != end_water_patch;
/* The cached path must always lead to a region patch that's on the high level path. /* The cached path must always lead to a region patch that's on the high level path.
* This is what can happen when that's not the case https://github.com/OpenTTD/OpenTTD/issues/12176. */ * This is what can happen when that's not the case https://github.com/OpenTTD/OpenTTD/issues/12176. */
if (add_full_path || !node_water_patch_on_high_level_path || node_water_patch == start_water_patch) { if (add_full_path || !current_patch_on_high_level_path || current_patch == start_patch) {
path_cache.push_back(node->GetTrackdir()); path_cache.push_back(node->GetTrackdir());
} else { } else {
path_cache.clear(); path_cache.clear();
@ -268,11 +272,10 @@ public:
} }
assert(node->GetTile() == v->tile); assert(node->GetTile() == v->tile);
/* Return INVALID_TRACKDIR to trigger a ship reversal if that is the best option. */ /* Return a reverse direction if that is the best option. */
best_origin_dir = node->GetTrackdir(); if (HasBit(reverse_dirs, node->GetTrackdir())) {
if ((TrackdirToTrackdirBits(best_origin_dir) & forward_dirs) == TRACKDIR_BIT_NONE) {
path_cache.clear(); path_cache.clear();
return INVALID_TRACKDIR; return node->GetTrackdir();
} }
/* A empty path means we are already at the destination. The pathfinder shouldn't have been called at all. /* A empty path means we are already at the destination. The pathfinder shouldn't have been called at all.
@ -284,42 +287,12 @@ public:
path_cache.pop_back(); path_cache.pop_back();
/* Clear path cache when in final water region patch. This is to allow ships to spread over different docking tiles dynamically. */ /* Clear path cache when in final water region patch. This is to allow ships to spread over different docking tiles dynamically. */
if (start_water_patch == end_water_patch) path_cache.clear(); if (start_patch == end_water_patch) path_cache.clear();
return result; return result;
} }
return INVALID_TRACKDIR; NOT_REACHED();
}
/**
* Check whether a ship should reverse to reach its destination.
* Called when leaving depot.
* @param v Ship.
* @param trackdir [out] the best of all possible reversed trackdirs.
* @return true if the reverse direction is better.
*/
static bool CheckShipReverse(const Ship *v, Trackdir *trackdir)
{
bool path_found = false;
ShipPathCache dummy_cache;
Trackdir best_origin_dir = INVALID_TRACKDIR;
if (trackdir == nullptr) {
/* The normal case, typically called when ships leave a dock. */
const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir());
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(reverse_dir);
(void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, path_found, dummy_cache, best_origin_dir);
return path_found && best_origin_dir == reverse_dir;
} else {
/* This gets called when a ship suddenly can't move forward, e.g. due to terraforming. */
const DiagDirection entry = ReverseDiagDir(VehicleExitDir(v->direction, v->state));
const TrackdirBits reverse_dirs = DiagdirReachesTrackdirs(entry) & TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0, entry));
(void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, path_found, dummy_cache, best_origin_dir);
*trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs);
return true;
}
} }
}; };
@ -424,16 +397,27 @@ struct CYapfShip : CYapfT<CYapfShip_TypesT<CYapfShip, CFollowTrackWater, CShipNo
explicit CYapfShip(int max_nodes) { this->max_search_nodes = max_nodes; } explicit CYapfShip(int max_nodes) { this->max_search_nodes = max_nodes; }
}; };
/** Ship controller helper - path finder invoker. */ Trackdir YapfShipChooseTrack(const Ship *v, bool &path_found, ShipPathCache &path_cache)
Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipPathCache &path_cache)
{ {
Trackdir best_origin_dir = INVALID_TRACKDIR; /* This is always called when a ship is about to exit a tile, hence we add up to three reverse directions. */
const TrackdirBits origin_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir()); const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, path_found, path_cache, best_origin_dir); const TrackdirBits all_water_dirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0));
return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK; const TrackdirBits reverse_dirs = all_water_dirs & DiagdirReachesTrackdirs(ReverseDiagDir(TrackdirToExitdir(v->GetVehicleTrackdir())));
const Trackdir trackdir = CYapfShip::ChooseShipTrack(v, forward_dirs, reverse_dirs, path_found, path_cache);
assert(trackdir != INVALID_TRACKDIR);
return trackdir;
} }
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir) bool YapfShipCheckReverse(const Ship *v, Trackdir &trackdir)
{ {
return CYapfShip::CheckShipReverse(v, trackdir); /* It is not clear where the ship is within its current track, so we only add the forward (current) and reverse direction. */
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(ReverseTrackdir(v->GetVehicleTrackdir()));
bool path_found = false;
ShipPathCache dummy_cache;
trackdir = CYapfShip::ChooseShipTrack(v, forward_dirs, reverse_dirs, path_found, dummy_cache);
assert(trackdir != INVALID_TRACKDIR);
return path_found && HasBit(reverse_dirs, trackdir);
} }

View File

@ -367,7 +367,7 @@ static Vehicle *EnsureNoMovingShipProc(Vehicle *v, void *)
return v->type == VEH_SHIP && v->cur_speed != 0 ? v : nullptr; return v->type == VEH_SHIP && v->cur_speed != 0 ? v : nullptr;
} }
static bool CheckReverseShip(const Ship *v, Trackdir *trackdir = nullptr) static bool CheckReverseShip(const Ship *v, Trackdir &trackdir)
{ {
/* Ask pathfinder for best direction */ /* Ask pathfinder for best direction */
return YapfShipCheckReverse(v, trackdir); return YapfShipCheckReverse(v, trackdir);
@ -405,7 +405,8 @@ static bool CheckShipLeaveDepot(Ship *v)
TrackBits north_tracks = DiagdirReachesTracks(north_dir) & GetTileShipTrackStatus(north_neighbour); TrackBits north_tracks = DiagdirReachesTracks(north_dir) & GetTileShipTrackStatus(north_neighbour);
TrackBits south_tracks = DiagdirReachesTracks(south_dir) & GetTileShipTrackStatus(south_neighbour); TrackBits south_tracks = DiagdirReachesTracks(south_dir) & GetTileShipTrackStatus(south_neighbour);
if (north_tracks && south_tracks) { if (north_tracks && south_tracks) {
if (CheckReverseShip(v)) north_tracks = TRACK_BIT_NONE; Trackdir dummy_trackdir;
if (CheckReverseShip(v, dummy_trackdir)) north_tracks = TRACK_BIT_NONE;
} }
if (north_tracks) { if (north_tracks) {
@ -487,54 +488,39 @@ static void ShipArrivesAt(const Vehicle *v, Station *st)
* Runs the pathfinder to choose a track to continue along. * Runs the pathfinder to choose a track to continue along.
* *
* @param v Ship to navigate * @param v Ship to navigate
* @param tile Tile, the ship is about to enter * @param trackdirs Available trackdir choices on the vehicle's type
* @param tracks Available track choices on \a tile * @return Trackdir to choose
* @return Track to choose, or INVALID_TRACK when to reverse.
*/ */
static Track ChooseShipTrack(Ship *v, TileIndex tile, TrackBits tracks) static Trackdir ChooseShipTrack(Ship *v, TrackdirBits trackdirs)
{ {
bool path_found = true; bool path_found = true;
Track track; Trackdir trackdir;
if (v->dest_tile == 0) { if (v->dest_tile == 0) {
/* No destination, don't invoke pathfinder. */ /* No destination, don't invoke pathfinder. */
track = TrackBitsToTrack(v->state); trackdir = FindFirstTrackdir(trackdirs);
if (!IsDiagonalTrack(track)) track = TrackToOppositeTrack(track); if (trackdir == INVALID_TRACKDIR) trackdir = ReverseTrackdir(v->GetVehicleTrackdir());
if (!HasBit(tracks, track)) track = FindFirstTrack(tracks);
path_found = false; path_found = false;
} else { } else {
/* Attempt to follow cached path. */ /* Attempt to follow cached path. */
if (!v->path.empty()) { if (!v->path.empty()) {
track = TrackdirToTrack(v->path.back().trackdir); trackdir = v->path.back().trackdir;
if (HasBit(tracks, track)) { if (HasBit(trackdirs, trackdir)) {
v->path.pop_back(); v->path.pop_back();
/* HandlePathfindResult() is not called here because this is not a new pathfinder result. */ /* HandlePathfindResult() is not called here because this is not a new pathfinder result. */
return track; return trackdir;
} }
/* Cached path is invalid so continue with pathfinder. */ /* Cached path is invalid so continue with pathfinder. */
v->path.clear(); v->path.clear();
} }
track = YapfShipChooseTrack(v, tile, path_found, v->path); trackdir = YapfShipChooseTrack(v, path_found, v->path);
} }
v->HandlePathfindingResult(path_found); v->HandlePathfindingResult(path_found);
return track; return trackdir;
}
/**
* Get the available water tracks on a tile for a ship entering a tile.
* @param tile The tile about to enter.
* @param dir The entry direction.
* @return The available trackbits on the next tile.
*/
static inline TrackBits GetAvailShipTracks(TileIndex tile, DiagDirection dir)
{
TrackBits tracks = GetTileShipTrackStatus(tile) & DiagdirReachesTracks(dir);
return tracks;
} }
/** Structure for ship sub-coordinate data for moving into a new tile via a Diagdir onto a Track. */ /** Structure for ship sub-coordinate data for moving into a new tile via a Diagdir onto a Track. */
@ -704,7 +690,8 @@ static void ShipController(Ship *v)
if (v->vehstatus.Test(VehState::Stopped)) return; if (v->vehstatus.Test(VehState::Stopped)) return;
if (ProcessOrders(v) && CheckReverseShip(v)) return ReverseShip(v); Trackdir best_reverse_dir = INVALID_TRACKDIR;
if (ProcessOrders(v) && CheckReverseShip(v, best_reverse_dir)) return ReverseShipIntoTrackdir(v, best_reverse_dir);
v->HandleLoading(); v->HandleLoading();
@ -792,19 +779,14 @@ static void ShipController(Ship *v)
const DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile); const DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
assert(diagdir != INVALID_DIAGDIR); assert(diagdir != INVALID_DIAGDIR);
const TrackBits tracks = GetAvailShipTracks(gp.new_tile, diagdir); const TrackdirBits forward_trackdirs = TrackBitsToTrackdirBits(GetTileShipTrackStatus(gp.new_tile)) & DiagdirReachesTrackdirs(diagdir);
if (tracks == TRACK_BIT_NONE) {
Trackdir trackdir = INVALID_TRACKDIR;
CheckReverseShip(v, &trackdir);
if (trackdir == INVALID_TRACKDIR) return ReverseShip(v);
return ReverseShipIntoTrackdir(v, trackdir);
}
/* Choose a direction, and continue if we find one */ /* Choose a direction, which might require a reversal */
const Track track = ChooseShipTrack(v, gp.new_tile, tracks); const Trackdir new_trackdir = ChooseShipTrack(v, forward_trackdirs);
if (track == INVALID_TRACK) return ReverseShip(v); assert(new_trackdir != INVALID_TRACKDIR);
if (!HasBit(forward_trackdirs, new_trackdir)) return ReverseShipIntoTrackdir(v, new_trackdir);
const ShipSubcoordData &b = _ship_subcoord[diagdir][track]; const ShipSubcoordData &b = _ship_subcoord[diagdir][TrackdirToTrack(new_trackdir)];
gp.x = (gp.x & ~0xF) | b.x_subcoord; gp.x = (gp.x & ~0xF) | b.x_subcoord;
gp.y = (gp.y & ~0xF) | b.y_subcoord; gp.y = (gp.y & ~0xF) | b.y_subcoord;
@ -815,7 +797,7 @@ static void ShipController(Ship *v)
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
v->tile = gp.new_tile; v->tile = gp.new_tile;
v->state = TrackToTrackBits(track); v->state = TrackToTrackBits(TrackdirToTrack(new_trackdir));
/* Update ship cache when the water class changes. Aqueducts are always canals. */ /* Update ship cache when the water class changes. Aqueducts are always canals. */
if (GetEffectiveWaterClass(gp.old_tile) != GetEffectiveWaterClass(gp.new_tile)) v->UpdateCache(); if (GetEffectiveWaterClass(gp.old_tile) != GetEffectiveWaterClass(gp.new_tile)) v->UpdateCache();