Changeset - r26015:62da647d3454
[Not reviewed]
master
0 5 0
glx22 - 3 years ago 2021-10-05 15:58:19
glx@openttd.org
Fix: Try all possible reverse directions when a ship reaches a dead end
5 files changed with 49 insertions and 15 deletions:
0 comments (0 inline, 0 general)
src/pathfinder/npf/npf.cpp
Show inline comments
 
@@ -1202,38 +1202,52 @@ Track NPFShipChooseTrack(const Ship *v, 
 
	NPFFoundTargetData ftd = NPFRouteToStationOrTile(v->tile, trackdir, true, &fstd, &user);
 

	
 
	assert(ftd.best_trackdir != INVALID_TRACKDIR);
 

	
 
	/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
 
	 * the direction we need to take to get there, if ftd.best_bird_dist is not 0,
 
	 * we did not find our target, but ftd.best_trackdir contains the direction leading
 
	 * to the tile closest to our target. */
 
	path_found = (ftd.best_bird_dist == 0);
 
	return TrackdirToTrack(ftd.best_trackdir);
 
}
 

	
 
bool NPFShipCheckReverse(const Ship *v)
 
bool NPFShipCheckReverse(const Ship *v, Trackdir *best_td)
 
{
 
	NPFFindStationOrTileData fstd;
 
	NPFFoundTargetData ftd;
 

	
 
	NPFFillWithOrderData(&fstd, v);
 

	
 
	Trackdir trackdir = v->GetVehicleTrackdir();
 
	Trackdir trackdir_rev = ReverseTrackdir(trackdir);
 
	assert(trackdir != INVALID_TRACKDIR);
 
	assert(trackdir_rev != INVALID_TRACKDIR);
 

	
 
	AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE, 0 };
 
	ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, false, v->tile, trackdir_rev, false, &fstd, &user);
 
	if (best_td != nullptr) {
 
		TrackdirBits rtds = DiagdirReachesTrackdirs(ReverseDiagDir(VehicleExitDir(v->direction, v->state)));
 
		Trackdir best = (Trackdir)FindFirstBit2x64(rtds);
 
		for (rtds = KillFirstBit(rtds); rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
 
			Trackdir td = (Trackdir)FindFirstBit2x64(rtds);
 
			ftd = NPFRouteToStationOrTileTwoWay(v->tile, best, false, v->tile, td, false, &fstd, &user);
 
			if (ftd.best_bird_dist == 0 && NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE)) best = td;
 
		}
 
		if (ftd.best_bird_dist == 0) {
 
			*best_td = best;
 
			return true;
 
		}
 
	} else {
 
		ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, false, v->tile, trackdir_rev, false, &fstd, &user);
 
	}
 
	/* If we didn't find anything, just keep on going straight ahead, otherwise take the reverse flag */
 
	return ftd.best_bird_dist == 0 && NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE);
 
}
 

	
 
/*** Trains ***/
 

	
 
FindDepotData NPFTrainFindNearestDepot(const Train *v, int max_penalty)
 
{
 
	const Train *last = v->Last();
 
	Trackdir trackdir = v->GetVehicleTrackdir();
 
	Trackdir trackdir_rev = ReverseTrackdir(last->GetVehicleTrackdir());
 
	NPFFindStationOrTileData fstd;
src/pathfinder/npf/npf_func.h
Show inline comments
 
@@ -37,27 +37,28 @@ Trackdir NPFRoadVehicleChooseTrack(const
 

	
 
/**
 
 * Finds the best path for given ship using NPF.
 
 * @param v        the ship that needs to find a path
 
 * @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
 
 */
 
Track NPFShipChooseTrack(const Ship *v, bool &path_found);
 

	
 
/**
 
 * Returns true if it is better to reverse the ship before leaving depot using NPF.
 
 * @param v the ship leaving the depot
 
 * @param trackdir [out] the best of all possible reversed trackdirs
 
 * @return true if reversing is better
 
 */
 
bool NPFShipCheckReverse(const Ship *v);
 
bool NPFShipCheckReverse(const Ship *v, Trackdir *trackdir);
 

	
 
/**
 
 * Used when user sends train to the nearest depot or if train needs servicing using NPF
 
 * @param v            train that needs to go to some depot
 
 * @param max_penalty  max max_penalty (in pathfinder penalty) from the current train position
 
 *                     (used also as optimization - the pathfinder can stop path finding if max_penalty
 
 *                     was reached and no depot was seen)
 
 * @return             the data about the depot
 
 */
 
FindDepotData NPFTrainFindNearestDepot(const Train *v, int max_penalty);
 

	
 
/**
src/pathfinder/yapf/yapf.h
Show inline comments
 
@@ -22,27 +22,28 @@
 
 * @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 enterdir diagonal direction which the ship will enter this new tile from
 
 * @param tracks   available tracks on the new tile (to choose from)
 
 * @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
 
 */
 
Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, ShipPathCache &path_cache);
 

	
 
/**
 
 * Returns true if it is better to reverse the ship before leaving depot using YAPF.
 
 * @param v the ship leaving the depot
 
 * @param trackdir [out] the best of all possible reversed trackdirs
 
 * @return true if reversing is better
 
 */
 
bool YapfShipCheckReverse(const Ship *v);
 
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir);
 

	
 
/**
 
 * Finds the best path for given road vehicle using YAPF.
 
 * @param v         the RV that needs to find a path
 
 * @param tile      the tile to find the path from (should be next tile the RV is about to enter)
 
 * @param enterdir  diagonal direction which the RV will enter this new tile from
 
 * @param trackdirs available trackdirs on the new tile (to choose from)
 
 * @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_TRACKDIR if the path could not be found
 
 */
 
Trackdir YapfRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, TrackdirBits trackdirs, bool &path_found, RoadVehPathCache &path_cache);
 

	
src/pathfinder/yapf/yapf_ship.cpp
Show inline comments
 
@@ -195,48 +195,53 @@ public:
 
			if (path_found && !path_cache.empty()) path_cache.pop_back();
 
		}
 
		return next_trackdir;
 
	}
 

	
 
	/**
 
	 * Check whether a ship should reverse to reach its destination.
 
	 * Called when leaving depot.
 
	 * @param v Ship
 
	 * @param tile Current position
 
	 * @param td1 Forward direction
 
	 * @param td2 Reverse direction
 
	 * @param trackdir [out] the best of all possible reversed trackdirs
 
	 * @return true if the reverse direction is better
 
	 */
 
	static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2)
 
	static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2, Trackdir *trackdir)
 
	{
 
		/* create pathfinder instance */
 
		Tpf pf;
 
		/* set origin and destination nodes */
 
		pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2));
 
		pf.SetOrigin(tile, trackdir == nullptr ? TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2) : DiagdirReachesTrackdirs(ReverseDiagDir(VehicleExitDir(v->direction, v->state))));
 
		pf.SetDestination(v);
 
		/* find best path */
 
		if (!pf.FindPath(v)) return false;
 

	
 
		Node *pNode = pf.GetBestNode();
 
		if (pNode == nullptr) return false;
 

	
 
		/* path was found
 
		 * walk through the path back to the origin */
 
		while (pNode->m_parent != nullptr) {
 
			pNode = pNode->m_parent;
 
		}
 

	
 
		Trackdir best_trackdir = pNode->GetTrackdir();
 
		assert(best_trackdir == td1 || best_trackdir == td2);
 
		return best_trackdir == td2;
 
		if (trackdir != nullptr) {
 
			*trackdir = best_trackdir;
 
		} else {
 
			assert(best_trackdir == td1 || best_trackdir == td2);
 
		}
 
		return best_trackdir != td1;
 
	}
 
};
 

	
 
/** Cost Provider module of YAPF for ships */
 
template <class Types>
 
class CYapfCostShipT
 
{
 
public:
 
	typedef typename Types::Tpf Tpf;              ///< the pathfinder class (derived from THIS class)
 
	typedef typename Types::TrackFollower TrackFollower;
 
	typedef typename Types::NodeList::Titem Node; ///< this will be our node type
 
	typedef typename Node::Key Key;               ///< key to hash tables
 
@@ -344,30 +349,30 @@ Track YapfShipChooseTrack(const Ship *v,
 
	typedef Trackdir (*PfnChooseShipTrack)(const Ship*, TileIndex, DiagDirection, TrackBits, bool &path_found, ShipPathCache &path_cache);
 
	PfnChooseShipTrack pfnChooseShipTrack = CYapfShip2::ChooseShipTrack; // default: ExitDir
 

	
 
	/* check if non-default YAPF type needed */
 
	if (_settings_game.pf.yapf.disable_node_optimization) {
 
		pfnChooseShipTrack = &CYapfShip1::ChooseShipTrack; // Trackdir
 
	}
 

	
 
	Trackdir td_ret = pfnChooseShipTrack(v, tile, enterdir, tracks, path_found, path_cache);
 
	return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
 
}
 

	
 
bool YapfShipCheckReverse(const Ship *v)
 
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir)
 
{
 
	Trackdir td = v->GetVehicleTrackdir();
 
	Trackdir td_rev = ReverseTrackdir(td);
 
	TileIndex tile = v->tile;
 

	
 
	typedef bool (*PfnCheckReverseShip)(const Ship*, TileIndex, Trackdir, Trackdir);
 
	typedef bool (*PfnCheckReverseShip)(const Ship*, TileIndex, Trackdir, Trackdir, Trackdir*);
 
	PfnCheckReverseShip pfnCheckReverseShip = CYapfShip2::CheckShipReverse; // default: ExitDir
 

	
 
	/* check if non-default YAPF type needed */
 
	if (_settings_game.pf.yapf.disable_node_optimization) {
 
		pfnCheckReverseShip = &CYapfShip1::CheckShipReverse; // Trackdir
 
	}
 

	
 
	bool reverse = pfnCheckReverseShip(v, tile, td, td_rev);
 
	bool reverse = pfnCheckReverseShip(v, tile, td, td_rev, trackdir);
 

	
 
	return reverse;
 
}
src/ship_cmd.cpp
Show inline comments
 
@@ -326,31 +326,31 @@ void Ship::UpdateDeltaXY()
 
		this->y_offs -= this->y_pos - this->rotation_y_pos;
 
	}
 
}
 

	
 
/**
 
 * Test-procedure for HasVehicleOnPos to check for any ships which are visible and not stopped by the player.
 
 */
 
static Vehicle *EnsureNoMovingShipProc(Vehicle *v, void *data)
 
{
 
	return v->type == VEH_SHIP && (v->vehstatus & (VS_HIDDEN | VS_STOPPED)) == 0 ? v : nullptr;
 
}
 

	
 
static bool CheckReverseShip(const Ship *v)
 
static bool CheckReverseShip(const Ship *v, Trackdir *trackdir = nullptr)
 
{
 
	/* Ask pathfinder for best direction */
 
	bool reverse = false;
 
	switch (_settings_game.pf.pathfinder_for_ships) {
 
		case VPF_NPF: reverse = NPFShipCheckReverse(v); break;
 
		case VPF_YAPF: reverse = YapfShipCheckReverse(v); break;
 
		case VPF_NPF: reverse = NPFShipCheckReverse(v, trackdir); break;
 
		case VPF_YAPF: reverse = YapfShipCheckReverse(v, trackdir); break;
 
		default: NOT_REACHED();
 
	}
 
	return reverse;
 
}
 

	
 
static bool CheckShipLeaveDepot(Ship *v)
 
{
 
	if (!v->IsChainInDepot()) return false;
 

	
 
	/* We are leaving a depot, but have to go to the exact same one; re-enter */
 
	if (v->current_order.IsType(OT_GOTO_DEPOT) &&
 
			IsShipDepotTile(v->tile) && GetDepotIndex(v->tile) == v->current_order.GetDestination()) {
 
@@ -716,25 +716,37 @@ static void ShipController(Ship *v)
 
							}
 
						}
 
					}
 
				}
 
			}
 
		} else {
 
			/* New tile */
 
			if (!IsValidTile(gp.new_tile)) goto reverse_direction;
 

	
 
			DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
 
			assert(diagdir != INVALID_DIAGDIR);
 
			tracks = GetAvailShipTracks(gp.new_tile, diagdir);
 
			if (tracks == TRACK_BIT_NONE) goto reverse_direction;
 
			if (tracks == TRACK_BIT_NONE) {
 
				Trackdir trackdir = INVALID_TRACKDIR;
 
				CheckReverseShip(v, &trackdir);
 
				if (trackdir == INVALID_TRACKDIR) goto reverse_direction;
 
				static const Direction _trackdir_to_direction[] = {
 
					DIR_NE, DIR_SE, DIR_E, DIR_E, DIR_S, DIR_S, INVALID_DIR, INVALID_DIR,
 
					DIR_SW, DIR_NW, DIR_W, DIR_W, DIR_N, DIR_N, INVALID_DIR, INVALID_DIR,
 
				};
 
				v->direction = _trackdir_to_direction[trackdir];
 
				assert(v->direction != INVALID_DIR);
 
				v->state = TrackdirBitsToTrackBits(TrackdirToTrackdirBits(trackdir));
 
				goto direction_changed;
 
			}
 

	
 
			/* Choose a direction, and continue if we find one */
 
			track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
 
			if (track == INVALID_TRACK) goto reverse_direction;
 

	
 
			b = _ship_subcoord[diagdir][track];
 

	
 
			gp.x = (gp.x & ~0xF) | b[0];
 
			gp.y = (gp.y & ~0xF) | b[1];
 

	
 
			/* Call the landscape function and tell it that the vehicle entered the tile */
 
			r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
@@ -787,24 +799,25 @@ static void ShipController(Ship *v)
 

	
 
	/* update image of ship, as well as delta XY */
 
	v->x_pos = gp.x;
 
	v->y_pos = gp.y;
 

	
 
getout:
 
	v->UpdatePosition();
 
	v->UpdateViewport(true, true);
 
	return;
 

	
 
reverse_direction:
 
	v->direction = ReverseDir(v->direction);
 
direction_changed:
 
	/* Remember our current location to avoid movement glitch */
 
	v->rotation_x_pos = v->x_pos;
 
	v->rotation_y_pos = v->y_pos;
 
	v->cur_speed = 0;
 
	v->path.clear();
 
	goto getout;
 
}
 

	
 
bool Ship::Tick()
 
{
 
	PerformanceAccumulator framerate(PFE_GL_SHIPS);
 

	
0 comments (0 inline, 0 general)