diff --git a/src/dock_base.h b/src/dock_base.h index 95dae6e9ed..3ed9fd5b54 100644 --- a/src/dock_base.h +++ b/src/dock_base.h @@ -14,6 +14,7 @@ #include "station_type.h" #include "tile_type.h" +#include "map_func.h" #include "core/pool_type.hpp" typedef Pool DockPool; @@ -31,6 +32,11 @@ struct Dock : DockPool::PoolItem<&_dock_pool> { 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); }; diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 258541d46f..ead4e4fa19 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -160,8 +160,8 @@ static int32 NPFCalcStationOrTileHeuristic(AyStar *as, AyStarNode *current, Open uint dist; AyStarUserData *user = (AyStarUserData *)as->user_data; - /* for train-stations, we are going to aim for the closest station tile */ - if (user->type != TRANSPORT_WATER && fstd->station_index != INVALID_STATION) { + /* for stations, we are going to aim for the closest station tile */ + if (fstd->station_index != INVALID_STATION) { to = CalcClosestStationTile(fstd->station_index, from, fstd->station_type); } @@ -558,16 +558,16 @@ static int32 NPFFindStationOrTile(AyStar *as, OpenListNode *current) AyStarNode *node = ¤t->path.node; TileIndex tile = node->tile; - if (fstd->station_index == INVALID_STATION && tile == fstd->dest_coords) return AYSTAR_FOUND_END_NODE; - - if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) { - if (fstd->v->type == VEH_TRAIN) return AYSTAR_FOUND_END_NODE; - - assert(fstd->v->type == VEH_ROAD); - /* Only if it is a valid station *and* we can stop there */ - if (GetStationType(tile) == fstd->station_type && (fstd->not_articulated || IsDriveThroughStopTile(tile))) return AYSTAR_FOUND_END_NODE; + if (fstd->station_index == INVALID_STATION) { + return (tile == fstd->dest_coords) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE; + } + + switch (fstd->v->type) { + default: NOT_REACHED(); + case VEH_TRAIN: return (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE; + 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) { - /* Ships don't really reach their stations, but the tile in front. So don't - * save the station id for ships. For roadvehs we don't store it either, - * 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()); + /* Fill station_index for station orders, else only dest_coords. */ + if (v->current_order.IsType(OT_GOTO_STATION) || (v->type != VEH_SHIP && v->current_order.IsType(OT_GOTO_WAYPOINT))) { 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(); /* 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); diff --git a/src/pathfinder/opf/opf_ship.cpp b/src/pathfinder/opf/opf_ship.cpp index 31a1e30167..27bd3a1975 100644 --- a/src/pathfinder/opf/opf_ship.cpp +++ b/src/pathfinder/opf/opf_ship.cpp @@ -13,6 +13,7 @@ #include "../../tunnelbridge_map.h" #include "../../tunnelbridge.h" #include "../../ship.h" +#include "../../station_base.h" #include "../../core/random_func.hpp" #include "../../safeguards.h" @@ -25,6 +26,7 @@ struct RememberData { struct TrackPathFinder { TileIndex skiptile; + StationID dest_station; TileIndex dest_coords; uint best_bird_dist; uint best_length; @@ -35,7 +37,14 @@ struct TrackPathFinder { static bool ShipTrackFollower(TileIndex tile, TrackPathFinder *pfs, uint length) { /* 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_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; 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.skiptile = skiptile; diff --git a/src/pathfinder/yapf/yapf_common.hpp b/src/pathfinder/yapf/yapf_common.hpp index 660c231161..9e48333dc7 100644 --- a/src/pathfinder/yapf/yapf_common.hpp +++ b/src/pathfinder/yapf/yapf_common.hpp @@ -110,72 +110,6 @@ public: } }; -/** YAPF destination provider base class - used when destination is single tile / multiple trackdirs */ -template -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(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 * components (base classes) from which the actual YAPF is composed. diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index e4b99462d2..0cdc0fe0d9 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -75,14 +75,12 @@ public: /* convert origin trackdir to TrackdirBits */ 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 */ Tpf pf; /* set origin and destination nodes */ pf.SetOrigin(src_tile, trackdirs); - pf.SetDestination(v->dest_tile, dest_trackdirs); + pf.SetDestination(v); /* find best path */ path_found = pf.FindPath(v); @@ -115,14 +113,11 @@ public: */ 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 */ Tpf pf; /* set origin and destination nodes */ pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2)); - pf.SetDestination(v->dest_tile, dest_trackdirs); + pf.SetDestination(v); /* find best path */ if (!pf.FindPath(v)) return false; @@ -188,6 +183,80 @@ public: } }; +/** YAPF destination provider for ships */ +template +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(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. * Defines all 6 base YAPF modules as classes providing services for CYapfBaseT. @@ -209,7 +278,7 @@ struct CYapfShip_TypesT typedef CYapfBaseT PfBase; // base pathfinder class typedef CYapfFollowShipT PfFollow; // node follower typedef CYapfOriginTileT PfOrigin; // origin provider - typedef CYapfDestinationTileT PfDestination; // destination/distance provider + typedef CYapfDestinationShipT PfDestination; // destination/distance provider typedef CYapfSegmentCostCacheNoneT PfCache; // segment cost cache provider typedef CYapfCostShipT PfCost; // cost provider }; diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index af87339f23..73cf915829 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -284,48 +284,13 @@ void Ship::PlayLeaveStationSound() const 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) { if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION; const Station *st = Station::Get(station); if (st->HasFacilities(FACIL_DOCK)) { - if (st->docks == NULL) { - return st->xy; // A buoy - } else { - const Dock* dock = GetBestDock(this, st); - - DiagDirection direction = DiagdirBetweenTiles(dock->sloped, dock->flat); - return dock->flat + TileOffsByDiagDir(direction); - } + return st->xy; } else { this->IncrementRealOrderIndex(); return 0; @@ -732,26 +697,23 @@ static void ShipController(Ship *v) UpdateVehicleTimetable(v, true); v->IncrementRealOrderIndex(); v->current_order.MakeDummy(); - } else { - /* Non-buoy orders really need to reach the tile */ - if (v->dest_tile == gp.new_tile) { - if (v->current_order.IsType(OT_GOTO_DEPOT)) { - if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { - VehicleEnterDepot(v); - return; - } - } else if (v->current_order.IsType(OT_GOTO_STATION)) { - v->last_station_visited = v->current_order.GetDestination(); + } else if (v->current_order.IsType(OT_GOTO_DEPOT)) { + if (v->dest_tile == gp.new_tile && (gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { + VehicleEnterDepot(v); + return; + } + } else if (v->current_order.IsType(OT_GOTO_STATION)) { + Station *st = Station::Get(v->current_order.GetDestination()); + if (st->IsDockingTile(gp.new_tile)) { + v->last_station_visited = v->current_order.GetDestination(); - /* 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 - ShipArrivesAt(v, st); - v->BeginLoading(); - } else { // leave stations without docks right aways - v->current_order.MakeLeaveStation(); - v->IncrementRealOrderIndex(); - } + /* Process station in the orderlist. */ + if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations + ShipArrivesAt(v, st); + v->BeginLoading(); + } else { // leave stations without docks right aways + v->current_order.MakeLeaveStation(); + v->IncrementRealOrderIndex(); } } } diff --git a/src/station.cpp b/src/station.cpp index e2597c8442..e09d21ef4f 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -313,6 +313,14 @@ Rect Station::GetCatchmentRectUsingRadius(uint catchment_radius) const 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 */ struct RectAndIndustryVector { Rect rect; ///< The rectangle to search the industries in. diff --git a/src/station_base.h b/src/station_base.h index 1b752924d8..bd451475ba 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -510,6 +510,8 @@ public: 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 */ void GetTileArea(TileArea *ta, StationType type) const;