Improve pathfinder support for multiple docks

The pathfinder can now find docks other than the Manhattan closest one.

Based on Cirdan commits:
afc7969c13d7ca59afe4dae4bf88122ba8d27df2
4067190dedcd3e5f668ea4f49b1dd8dfed10b2a7
d9be6b712d2ae4fb1b5ae844dde4919dd24c4fb2
This commit is contained in:
Jonathan G Rennison
2018-02-05 18:21:05 +00:00
parent ed04845514
commit 955126efac
8 changed files with 137 additions and 150 deletions

View File

@@ -14,6 +14,7 @@
#include "station_type.h" #include "station_type.h"
#include "tile_type.h" #include "tile_type.h"
#include "map_func.h"
#include "core/pool_type.hpp" #include "core/pool_type.hpp"
typedef Pool<Dock, DockID, 32, 64000> DockPool; typedef Pool<Dock, DockID, 32, 64000> DockPool;
@@ -31,6 +32,11 @@ struct Dock : DockPool::PoolItem<&_dock_pool> {
inline Dock *GetNextDock() const { return this->next; } inline Dock *GetNextDock() const { return this->next; }
inline TileIndex GetDockingTile() const
{
return this->flat + TileOffsByDiagDir(DiagdirBetweenTiles(this->sloped, this->flat));
}
static Dock *GetByTile(TileIndex tile); static Dock *GetByTile(TileIndex tile);
}; };

View File

@@ -160,8 +160,8 @@ static int32 NPFCalcStationOrTileHeuristic(AyStar *as, AyStarNode *current, Open
uint dist; uint dist;
AyStarUserData *user = (AyStarUserData *)as->user_data; AyStarUserData *user = (AyStarUserData *)as->user_data;
/* for train-stations, we are going to aim for the closest station tile */ /* for stations, we are going to aim for the closest station tile */
if (user->type != TRANSPORT_WATER && fstd->station_index != INVALID_STATION) { if (fstd->station_index != INVALID_STATION) {
to = CalcClosestStationTile(fstd->station_index, from, fstd->station_type); to = CalcClosestStationTile(fstd->station_index, from, fstd->station_type);
} }
@@ -558,16 +558,16 @@ static int32 NPFFindStationOrTile(AyStar *as, OpenListNode *current)
AyStarNode *node = &current->path.node; AyStarNode *node = &current->path.node;
TileIndex tile = node->tile; TileIndex tile = node->tile;
if (fstd->station_index == INVALID_STATION && tile == fstd->dest_coords) return AYSTAR_FOUND_END_NODE; if (fstd->station_index == INVALID_STATION) {
return (tile == fstd->dest_coords) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) { }
if (fstd->v->type == VEH_TRAIN) return AYSTAR_FOUND_END_NODE;
switch (fstd->v->type) {
assert(fstd->v->type == VEH_ROAD); default: NOT_REACHED();
/* Only if it is a valid station *and* we can stop there */ case VEH_TRAIN: return (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
if (GetStationType(tile) == fstd->station_type && (fstd->not_articulated || IsDriveThroughStopTile(tile))) return AYSTAR_FOUND_END_NODE; case VEH_ROAD: return (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index && GetStationType(tile) == fstd->station_type && (fstd->not_articulated || IsDriveThroughStopTile(tile))) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
case VEH_SHIP: return Station::Get(fstd->station_index)->IsDockingTile(tile) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
} }
return AYSTAR_DONE;
} }
/** /**
@@ -1099,16 +1099,12 @@ void InitializeNPF()
static void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, const Vehicle *v, bool reserve_path = false) static void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, const Vehicle *v, bool reserve_path = false)
{ {
/* Ships don't really reach their stations, but the tile in front. So don't /* Fill station_index for station orders, else only dest_coords. */
* save the station id for ships. For roadvehs we don't store it either, if (v->current_order.IsType(OT_GOTO_STATION) || (v->type != VEH_SHIP && v->current_order.IsType(OT_GOTO_WAYPOINT))) {
* because multistop depends on vehicles actually reaching the exact
* dest_tile, not just any stop of that station.
* So only for train orders to stations we fill fstd->station_index, for all
* others only dest_coords */
if (v->type != VEH_SHIP && (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_WAYPOINT))) {
assert(v->IsGroundVehicle());
fstd->station_index = v->current_order.GetDestination(); fstd->station_index = v->current_order.GetDestination();
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); fstd->station_type = (v->type == VEH_SHIP) ? STATION_DOCK :
(v->type == VEH_TRAIN) ? (v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT) :
(RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK);
fstd->not_articulated = v->type == VEH_ROAD && !RoadVehicle::From(v)->HasArticulatedPart(); fstd->not_articulated = v->type == VEH_ROAD && !RoadVehicle::From(v)->HasArticulatedPart();
/* Let's take the closest tile of the station as our target for vehicles */ /* Let's take the closest tile of the station as our target for vehicles */
fstd->dest_coords = CalcClosestStationTile(fstd->station_index, v->tile, fstd->station_type); fstd->dest_coords = CalcClosestStationTile(fstd->station_index, v->tile, fstd->station_type);

View File

@@ -13,6 +13,7 @@
#include "../../tunnelbridge_map.h" #include "../../tunnelbridge_map.h"
#include "../../tunnelbridge.h" #include "../../tunnelbridge.h"
#include "../../ship.h" #include "../../ship.h"
#include "../../station_base.h"
#include "../../core/random_func.hpp" #include "../../core/random_func.hpp"
#include "../../safeguards.h" #include "../../safeguards.h"
@@ -25,6 +26,7 @@ struct RememberData {
struct TrackPathFinder { struct TrackPathFinder {
TileIndex skiptile; TileIndex skiptile;
StationID dest_station;
TileIndex dest_coords; TileIndex dest_coords;
uint best_bird_dist; uint best_bird_dist;
uint best_length; uint best_length;
@@ -35,7 +37,14 @@ struct TrackPathFinder {
static bool ShipTrackFollower(TileIndex tile, TrackPathFinder *pfs, uint length) static bool ShipTrackFollower(TileIndex tile, TrackPathFinder *pfs, uint length)
{ {
/* Found dest? */ /* Found dest? */
if (tile == pfs->dest_coords) { if (pfs->dest_station != INVALID_STATION) {
if (Station::Get(pfs->dest_station)->IsDockingTile(tile)) {
pfs->best_bird_dist = 0;
pfs->best_length = minu(pfs->best_length, length);
return true;
}
} else if (tile == pfs->dest_coords) {
pfs->best_bird_dist = 0; pfs->best_bird_dist = 0;
pfs->best_length = minu(pfs->best_length, length); pfs->best_length = minu(pfs->best_length, length);
@@ -140,6 +149,7 @@ static uint FindShipTrack(const Ship *v, TileIndex tile, DiagDirection dir, Trac
uint best_length = 0; uint best_length = 0;
byte ship_dir = v->direction & 3; byte ship_dir = v->direction & 3;
pfs.dest_station = v->current_order.IsType(OT_GOTO_STATION) ? v->current_order.GetDestination() : INVALID_STATION;
pfs.dest_coords = v->dest_tile; pfs.dest_coords = v->dest_tile;
pfs.skiptile = skiptile; pfs.skiptile = skiptile;

View File

@@ -110,72 +110,6 @@ public:
} }
}; };
/** YAPF destination provider base class - used when destination is single tile / multiple trackdirs */
template <class Types>
class CYapfDestinationTileT
{
public:
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class)
typedef typename Types::NodeList::Titem Node; ///< this will be our node type
typedef typename Node::Key Key; ///< key to hash tables
protected:
TileIndex m_destTile; ///< destination tile
TrackdirBits m_destTrackdirs; ///< destination trackdir mask
public:
/** set the destination tile / more trackdirs */
void SetDestination(TileIndex tile, TrackdirBits trackdirs)
{
m_destTile = tile;
m_destTrackdirs = trackdirs;
}
protected:
/** to access inherited path finder */
Tpf& Yapf()
{
return *static_cast<Tpf *>(this);
}
public:
/** Called by YAPF to detect if node ends in the desired destination */
inline bool PfDetectDestination(Node &n)
{
bool bDest = (n.m_key.m_tile == m_destTile) && ((m_destTrackdirs & TrackdirToTrackdirBits(n.GetTrackdir())) != TRACKDIR_BIT_NONE);
return bDest;
}
/**
* Called by YAPF to calculate cost estimate. Calculates distance to the destination
* adds it to the actual cost from origin and stores the sum to the Node::m_estimate
*/
inline bool PfCalcEstimate(Node &n)
{
static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
if (PfDetectDestination(n)) {
n.m_estimate = n.m_cost;
return true;
}
TileIndex tile = n.GetTile();
DiagDirection exitdir = TrackdirToExitdir(n.GetTrackdir());
int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
int x2 = 2 * TileX(m_destTile);
int y2 = 2 * TileY(m_destTile);
int dx = abs(x1 - x2);
int dy = abs(y1 - y2);
int dmin = min(dx, dy);
int dxy = abs(dx - dy);
int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
n.m_estimate = n.m_cost + d;
assert(n.m_estimate >= n.m_parent->m_estimate);
return true;
}
};
/** /**
* YAPF template that uses Ttypes template argument to determine all YAPF * YAPF template that uses Ttypes template argument to determine all YAPF
* components (base classes) from which the actual YAPF is composed. * components (base classes) from which the actual YAPF is composed.

View File

@@ -75,14 +75,12 @@ public:
/* convert origin trackdir to TrackdirBits */ /* convert origin trackdir to TrackdirBits */
TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir); TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir);
/* get available trackdirs on the destination tile */
TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
/* create pathfinder instance */ /* create pathfinder instance */
Tpf pf; Tpf pf;
/* set origin and destination nodes */ /* set origin and destination nodes */
pf.SetOrigin(src_tile, trackdirs); pf.SetOrigin(src_tile, trackdirs);
pf.SetDestination(v->dest_tile, dest_trackdirs); pf.SetDestination(v);
/* find best path */ /* find best path */
path_found = pf.FindPath(v); path_found = pf.FindPath(v);
@@ -115,14 +113,11 @@ public:
*/ */
static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2) static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2)
{ {
/* get available trackdirs on the destination tile */
TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
/* create pathfinder instance */ /* create pathfinder instance */
Tpf pf; Tpf pf;
/* set origin and destination nodes */ /* set origin and destination nodes */
pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2)); pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2));
pf.SetDestination(v->dest_tile, dest_trackdirs); pf.SetDestination(v);
/* find best path */ /* find best path */
if (!pf.FindPath(v)) return false; if (!pf.FindPath(v)) return false;
@@ -188,6 +183,80 @@ public:
} }
}; };
/** YAPF destination provider for ships */
template <class Types>
class CYapfDestinationShipT
{
public:
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class)
typedef typename Types::NodeList::Titem Node; ///< this will be our node type
typedef typename Node::Key Key; ///< key to hash tables
protected:
StationID m_destStation; ///< destinatin station
TileIndex m_destTile; ///< destination tile
public:
/** set the destination */
void SetDestination(const Ship *v)
{
if (v->current_order.IsType(OT_GOTO_STATION)) {
m_destStation = v->current_order.GetDestination();
m_destTile = CalcClosestStationTile(m_destStation, v->tile, STATION_DOCK);
} else {
m_destStation = INVALID_STATION;
m_destTile = v->dest_tile;
}
}
protected:
/** to access inherited path finder */
Tpf& Yapf()
{
return *static_cast<Tpf *>(this);
}
public:
/** Called by YAPF to detect if node ends in the desired destination */
inline bool PfDetectDestination(Node &n)
{
if (m_destStation == INVALID_STATION) {
return n.m_key.m_tile == m_destTile;
} else {
return Station::Get(m_destStation)->IsDockingTile(n.m_key.m_tile);
}
}
/**
* Called by YAPF to calculate cost estimate. Calculates distance to the destination
* adds it to the actual cost from origin and stores the sum to the Node::m_estimate
*/
inline bool PfCalcEstimate(Node &n)
{
static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
if (PfDetectDestination(n)) {
n.m_estimate = n.m_cost;
return true;
}
TileIndex tile = n.GetTile();
DiagDirection exitdir = TrackdirToExitdir(n.GetTrackdir());
int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
int x2 = 2 * TileX(m_destTile);
int y2 = 2 * TileY(m_destTile);
int dx = abs(x1 - x2);
int dy = abs(y1 - y2);
int dmin = min(dx, dy);
int dxy = abs(dx - dy);
int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
n.m_estimate = n.m_cost + d;
assert(n.m_estimate >= n.m_parent->m_estimate);
return true;
}
};
/** /**
* Config struct of YAPF for ships. * Config struct of YAPF for ships.
* Defines all 6 base YAPF modules as classes providing services for CYapfBaseT. * Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
@@ -209,7 +278,7 @@ struct CYapfShip_TypesT
typedef CYapfBaseT<Types> PfBase; // base pathfinder class typedef CYapfBaseT<Types> PfBase; // base pathfinder class
typedef CYapfFollowShipT<Types> PfFollow; // node follower typedef CYapfFollowShipT<Types> PfFollow; // node follower
typedef CYapfOriginTileT<Types> PfOrigin; // origin provider typedef CYapfOriginTileT<Types> PfOrigin; // origin provider
typedef CYapfDestinationTileT<Types> PfDestination; // destination/distance provider typedef CYapfDestinationShipT<Types> PfDestination; // destination/distance provider
typedef CYapfSegmentCostCacheNoneT<Types> PfCache; // segment cost cache provider typedef CYapfSegmentCostCacheNoneT<Types> PfCache; // segment cost cache provider
typedef CYapfCostShipT<Types> PfCost; // cost provider typedef CYapfCostShipT<Types> PfCost; // cost provider
}; };

View File

@@ -284,48 +284,13 @@ void Ship::PlayLeaveStationSound() const
PlayShipSound(this); PlayShipSound(this);
} }
/**
* Of all the docks a station has, return the best destination for a ship.
* @param v The ship.
* @param st Station the ship \a v is heading for.
* @return The free and closest (if none is free, just closest) dock of station \a st to ship \a v.
*/
const Dock* GetBestDock(const Ship *v, const Station *st)
{
assert(st != NULL && st->HasFacilities(FACIL_DOCK) && st->docks != NULL);
if (st->docks->next == NULL) return st->docks;
Dock *best_dock = NULL;
uint best_distance = UINT_MAX;
for (Dock *dock = st->docks; dock != NULL; dock = dock->next) {
uint new_distance = DistanceManhattan(v->tile, dock->flat);
if (new_distance < best_distance) {
best_dock = dock;
best_distance = new_distance;
}
}
assert(best_dock != NULL);
return best_dock;
}
TileIndex Ship::GetOrderStationLocation(StationID station) TileIndex Ship::GetOrderStationLocation(StationID station)
{ {
if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION; if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
const Station *st = Station::Get(station); const Station *st = Station::Get(station);
if (st->HasFacilities(FACIL_DOCK)) { if (st->HasFacilities(FACIL_DOCK)) {
if (st->docks == NULL) { return st->xy;
return st->xy; // A buoy
} else {
const Dock* dock = GetBestDock(this, st);
DiagDirection direction = DiagdirBetweenTiles(dock->sloped, dock->flat);
return dock->flat + TileOffsByDiagDir(direction);
}
} else { } else {
this->IncrementRealOrderIndex(); this->IncrementRealOrderIndex();
return 0; return 0;
@@ -732,26 +697,23 @@ static void ShipController(Ship *v)
UpdateVehicleTimetable(v, true); UpdateVehicleTimetable(v, true);
v->IncrementRealOrderIndex(); v->IncrementRealOrderIndex();
v->current_order.MakeDummy(); v->current_order.MakeDummy();
} else { } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
/* Non-buoy orders really need to reach the tile */ if (v->dest_tile == gp.new_tile && (gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
if (v->dest_tile == gp.new_tile) { VehicleEnterDepot(v);
if (v->current_order.IsType(OT_GOTO_DEPOT)) { return;
if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { }
VehicleEnterDepot(v); } else if (v->current_order.IsType(OT_GOTO_STATION)) {
return; Station *st = Station::Get(v->current_order.GetDestination());
} if (st->IsDockingTile(gp.new_tile)) {
} else if (v->current_order.IsType(OT_GOTO_STATION)) { v->last_station_visited = v->current_order.GetDestination();
v->last_station_visited = v->current_order.GetDestination();
/* Process station in the orderlist. */ /* Process station in the orderlist. */
Station *st = Station::Get(v->current_order.GetDestination()); if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations ShipArrivesAt(v, st);
ShipArrivesAt(v, st); v->BeginLoading();
v->BeginLoading(); } else { // leave stations without docks right aways
} else { // leave stations without docks right aways v->current_order.MakeLeaveStation();
v->current_order.MakeLeaveStation(); v->IncrementRealOrderIndex();
v->IncrementRealOrderIndex();
}
} }
} }
} }

View File

@@ -313,6 +313,14 @@ Rect Station::GetCatchmentRectUsingRadius(uint catchment_radius) const
return ret; return ret;
} }
bool Station::IsDockingTile(TileIndex tile) const
{
for (const Dock *d = this->docks; d != NULL; d = d->next) {
if (tile == d->GetDockingTile()) return true;
}
return false;
}
/** Rect and pointer to IndustryVector */ /** Rect and pointer to IndustryVector */
struct RectAndIndustryVector { struct RectAndIndustryVector {
Rect rect; ///< The rectangle to search the industries in. Rect rect; ///< The rectangle to search the industries in.

View File

@@ -510,6 +510,8 @@ public:
return IsAirportTile(tile) && GetStationIndex(tile) == this->index; return IsAirportTile(tile) && GetStationIndex(tile) == this->index;
} }
bool IsDockingTile(TileIndex tile) const;
/* virtual */ uint32 GetNewGRFVariable(const ResolverObject &object, byte variable, byte parameter, bool *available) const; /* virtual */ uint32 GetNewGRFVariable(const ResolverObject &object, byte variable, byte parameter, bool *available) const;
/* virtual */ void GetTileArea(TileArea *ta, StationType type) const; /* virtual */ void GetTileArea(TileArea *ta, StationType type) const;