diff options
-rw-r--r-- | docs/landscape.html | 4 | ||||
-rw-r--r-- | docs/landscape_grid.html | 8 | ||||
-rw-r--r-- | src/bridge_map.h | 2 | ||||
-rw-r--r-- | src/industry_cmd.cpp | 7 | ||||
-rw-r--r-- | src/order_cmd.cpp | 2 | ||||
-rw-r--r-- | src/pathfinder/npf/npf.cpp | 22 | ||||
-rw-r--r-- | src/pathfinder/yapf/yapf_node_ship.hpp | 14 | ||||
-rw-r--r-- | src/pathfinder/yapf/yapf_ship.cpp | 94 | ||||
-rw-r--r-- | src/rail_cmd.cpp | 8 | ||||
-rw-r--r-- | src/rail_map.h | 3 | ||||
-rw-r--r-- | src/saveload/afterload.cpp | 23 | ||||
-rw-r--r-- | src/saveload/oldloader_sl.cpp | 2 | ||||
-rw-r--r-- | src/saveload/saveload.h | 1 | ||||
-rw-r--r-- | src/saveload/station_sl.cpp | 12 | ||||
-rw-r--r-- | src/script/api/script_order.cpp | 6 | ||||
-rw-r--r-- | src/ship.h | 2 | ||||
-rw-r--r-- | src/ship_cmd.cpp | 66 | ||||
-rw-r--r-- | src/station.cpp | 6 | ||||
-rw-r--r-- | src/station_base.h | 5 | ||||
-rw-r--r-- | src/station_cmd.cpp | 165 | ||||
-rw-r--r-- | src/station_func.h | 3 | ||||
-rw-r--r-- | src/station_map.h | 1 | ||||
-rw-r--r-- | src/tunnelbridge_cmd.cpp | 11 | ||||
-rw-r--r-- | src/water.h | 1 | ||||
-rw-r--r-- | src/water_cmd.cpp | 55 | ||||
-rw-r--r-- | src/water_map.h | 27 | ||||
-rw-r--r-- | src/waypoint_cmd.cpp | 1 |
27 files changed, 471 insertions, 80 deletions
diff --git a/docs/landscape.html b/docs/landscape.html index 1a6cd8386..4ee0da47e 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -249,6 +249,7 @@ <td valign=top nowrap> </td> <td> <ul> + <li>m1 bit 7: Ship docking tile status (for half-tile with water)</li> <li>m1 bits 4..0: <a href="#OwnershipInfo">owner</a> of the tile</li> <li>m2: see signals</li> <li>m3 bits 7..4: see signals</li> @@ -871,6 +872,7 @@ <td valign=top nowrap> </td> <td> <ul> + <li>m1 bit 7: Ship docking tile status (for buoys)</li> <li>m1 bits 6..5: water class for buoys, water part of docks and for airport tiles</li> <li>m1 bits 4..0: <a href="#OwnershipInfo">owner</a> of the station</li> <li>m2: index into the array of stations</li> @@ -1008,6 +1010,7 @@ <td valign=top nowrap> </td> <td> <ul> + <li>m1 bit 7: Ship docking tile status</li> <li>m1 bits 6..5 : Water class (sea, canal or river) <li>m1 bits 4..0: <a href="#OwnershipInfo">owner</a> (for sea, rivers, and coasts normally <tt>11</tt>)</li> <li>m2: Depot index (for depots only)</li> @@ -1459,6 +1462,7 @@ <td valign=top nowrap> </td> <td> <ul> + <li>m1 bit 7: Ship docking tile status (for aqueducts)</li> <li>m1 bits 4..0: <a href="#OwnershipInfo">owner</a></li> <li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of tram</li> <li>m4: <a href="#RoadType">Roadtype</a></li> diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 5d65214cb..d4a88d0bb 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -100,7 +100,7 @@ the array so you can quickly see what is used and what is not. <td class="caption">rail</td> <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> - <td class="bits"><span class="free">OOO</span>X XXXX</td> + <td class="bits">X<span class="free">OO</span>X XXXX</td> <td class="bits"><span class="free">OOOO</span> XXXX <span class="free">OOOO OOOO</span></td> <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits"><span class="free">OOOO</span> XXXX</td> @@ -208,7 +208,7 @@ the array so you can quickly see what is used and what is not. <td class="caption">rail station</td> <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> - <td class="bits"><span class="free">O</span>XXX XXXX</td> + <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX XXXX XXXX</td> <td class="bits">XXXX <span class="free">OOOO</span></td> <td class="bits">XXXX XXXX</td> @@ -300,7 +300,7 @@ the array so you can quickly see what is used and what is not. <td class="caption">sea, shore</td> <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> - <td class="bits"><span class="free">O</span>XXX XXXX</td> + <td class="bits">XXXX XXXX</td> <td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td> <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits"><span class="free">OOOO OOOO</span></td> @@ -354,7 +354,7 @@ the array so you can quickly see what is used and what is not. <td class="caption">tunnel entrance</td> <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> - <td class="bits"><span class="free">OOO</span>X XXXX</td> + <td class="bits">X<span class="free">OO</span>X XXXX</td> <td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td> <td class="bits">XXXX <span class="free">OOOO</span></td> <td class="bits"><span class="free">OO</span>XX XXXX</td> diff --git a/src/bridge_map.h b/src/bridge_map.h index 1461df13a..be37dfd71 100644 --- a/src/bridge_map.h +++ b/src/bridge_map.h @@ -15,6 +15,7 @@ #include "rail_map.h" #include "road_map.h" #include "bridge.h" +#include "water_map.h" /** * Checks if this is a bridge, instead of a tunnel @@ -130,6 +131,7 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); + SetDockingTile(t, false); _m[t].m2 = 0; _m[t].m3 = 0; _m[t].m4 = INVALID_ROADTYPE; diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 06c6cde56..4e4ff5c82 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -155,6 +155,13 @@ Industry::~Industry() } } + if (this->neutral_station != nullptr) { + /* Remove possible docking tiles */ + TILE_AREA_LOOP(tile_cur, this->location) { + ClearDockingTilesCheckingNeighbours(tile_cur); + } + } + if (GetIndustrySpec(this->type)->behaviour & INDUSTRYBEH_PLANT_FIELDS) { TileArea ta = TileArea(this->location.tile, 0, 0).Expand(21); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 26b999af7..ed2f444cc 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -2198,7 +2198,7 @@ bool ProcessOrders(Vehicle *v) /* If it is unchanged, keep it. */ if (order->Equals(v->current_order) && (v->type == VEH_AIRCRAFT || v->dest_tile != 0) && - (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || Station::Get(order->GetDestination())->dock_tile != INVALID_TILE)) { + (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || Station::Get(order->GetDestination())->ship_station.tile != INVALID_TILE > 0)) { return false; } diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 2cf333890..53f84f2a7 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -165,8 +165,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) { + /* aim for the closest station tile */ + if (fstd->station_index != INVALID_STATION) { to = CalcClosestStationTile(fstd->station_index, from, fstd->station_type); } @@ -563,6 +563,12 @@ static int32 NPFFindStationOrTile(const AyStar *as, const OpenListNode *current) if (fstd->station_index == INVALID_STATION && tile == fstd->dest_coords) return AYSTAR_FOUND_END_NODE; + if (fstd->v->type == VEH_SHIP) { + /* Ships do not actually reach the destination station, so we check for a docking tile instead. */ + if (IsDockingTile(tile) && IsShipDestinationTile(tile, fstd->station_index)) return AYSTAR_FOUND_END_NODE; + return AYSTAR_DONE; + } + if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) { if (fstd->v->type == VEH_TRAIN) return AYSTAR_FOUND_END_NODE; @@ -1111,10 +1117,16 @@ static void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, const Vehicle * * 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()); + if (v->current_order.IsType(OT_GOTO_STATION) || 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); + if (v->type == VEH_TRAIN) { + fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT; + } else if (v->type == VEH_ROAD) { + fstd->station_type = RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK; + } else if (v->type == VEH_SHIP) { + fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_DOCK : STATION_BUOY; + } + 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/yapf/yapf_node_ship.hpp b/src/pathfinder/yapf/yapf_node_ship.hpp index df4254fd9..a8f827071 100644 --- a/src/pathfinder/yapf/yapf_node_ship.hpp +++ b/src/pathfinder/yapf/yapf_node_ship.hpp @@ -14,7 +14,19 @@ /** Yapf Node for ships */ template <class Tkey_> -struct CYapfShipNodeT : CYapfNodeT<Tkey_, CYapfShipNodeT<Tkey_> > { }; +struct CYapfShipNodeT : CYapfNodeT<Tkey_, CYapfShipNodeT<Tkey_> > { + typedef CYapfNodeT<Tkey_, CYapfShipNodeT<Tkey_> > base; + + TileIndex m_segment_last_tile; + Trackdir m_segment_last_td; + + void Set(CYapfShipNodeT *parent, TileIndex tile, Trackdir td, bool is_choice) + { + base::Set(parent, tile, td, is_choice); + m_segment_last_tile = tile; + m_segment_last_td = td; + } +}; /* now define two major node types (that differ by key type) */ typedef CYapfShipNodeT<CYapfNodeKeyExitDir> CYapfShipNodeExitDir; diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index 09a4fc76a..c665fc53e 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -11,12 +11,95 @@ #include "../../stdafx.h" #include "../../ship.h" +#include "../../industry.h" #include "yapf.hpp" #include "yapf_node_ship.hpp" #include "../../safeguards.h" +template <class Types> +class CYapfDestinationTileWaterT +{ +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 + +protected: + TileIndex m_destTile; + TrackdirBits m_destTrackdirs; + StationID m_destStation; + +public: + 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); + m_destTrackdirs = INVALID_TRACKDIR_BIT; + } else { + m_destStation = INVALID_STATION; + m_destTile = v->dest_tile; + m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0)); + } + } + +protected: + /** to access inherited path finder */ + inline 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) + { + return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td); + } + + inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir) + { + if (m_destStation != INVALID_STATION) { + return IsDockingTile(tile) && IsShipDestinationTile(tile, m_destStation); + } + + return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE); + } + + /** + * 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.m_segment_last_tile; + DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td); + 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; + } +}; + + /** Node Follower module of YAPF for ships */ template <class Types> class CYapfFollowShipT @@ -75,14 +158,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); @@ -124,14 +205,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; @@ -230,7 +308,7 @@ struct CYapfShip_TypesT typedef CYapfBaseT<Types> PfBase; // base pathfinder class typedef CYapfFollowShipT<Types> PfFollow; // node follower typedef CYapfOriginTileT<Types> PfOrigin; // origin provider - typedef CYapfDestinationTileT<Types> PfDestination; // destination/distance provider + typedef CYapfDestinationTileWaterT<Types> PfDestination; // destination/distance provider typedef CYapfSegmentCostCacheNoneT<Types> PfCache; // segment cost cache provider typedef CYapfCostShipT<Types> PfCost; // cost provider }; diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index ab87fc40f..df862d4f2 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -570,6 +570,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u default: { /* Will there be flat water on the lower halftile? */ bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); + bool docking = IsPossibleDockingTile(tile) && IsDockingTile(tile); CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile); if (ret.Failed()) return ret; @@ -586,7 +587,10 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (flags & DC_EXEC) { MakeRailNormal(tile, _current_company, trackbit, railtype); - if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); + if (water_ground) { + SetRailGroundType(tile, RAIL_GROUND_WATER); + SetDockingTile(tile, docking); + } Company::Get(_current_company)->infrastructure.rail[railtype]++; DirtyCompanyInfrastructureWindows(_current_company); } @@ -708,7 +712,9 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, Slope tileh = GetTileSlope(tile); /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */ if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)) { + bool docking = IsDockingTile(tile); MakeShore(tile); + SetDockingTile(tile, docking); } else { DoClearSquare(tile); } diff --git a/src/rail_map.h b/src/rail_map.h index 74afe5ace..0142559e7 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -17,6 +17,7 @@ #include "signal_func.h" #include "track_func.h" #include "tile_map.h" +#include "water_map.h" #include "signal_type.h" @@ -521,6 +522,7 @@ static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r) { SetTileType(t, MP_RAILWAY); SetTileOwner(t, o); + SetDockingTile(t, false); _m[t].m2 = 0; _m[t].m3 = 0; _m[t].m4 = 0; @@ -535,6 +537,7 @@ static inline void MakeRailDepot(TileIndex t, Owner o, DepotID did, DiagDirectio { SetTileType(t, MP_RAILWAY); SetTileOwner(t, o); + SetDockingTile(t, false); _m[t].m2 = did; _m[t].m3 = 0; _m[t].m4 = 0; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 662f5a77b..770237d53 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -57,6 +57,7 @@ #include "../error.h" #include "../disaster_vehicle.h" #include "../ship.h" +#include "../water.h" #include "saveload_internal.h" @@ -675,7 +676,6 @@ bool AfterLoadGame() Station *st; FOR_ALL_STATIONS(st) { if (st->airport.tile == 0) st->airport.tile = INVALID_TILE; - if (st->dock_tile == 0) st->dock_tile = INVALID_TILE; if (st->train_station.tile == 0) st->train_station.tile = INVALID_TILE; } @@ -3177,6 +3177,27 @@ bool AfterLoadGame() } } + /* Update structures for multitile docks */ + if (IsSavegameVersionBefore(SLV_MULTITILE_DOCKS)) { + for (TileIndex t = 0; t < map_size; t++) { + /* Clear docking tile flag from relevant tiles as it + * was not previously cleared. */ + if (IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)) { + SetDockingTile(t, false); + } + /* Add docks and oilrigs to Station::ship_station. */ + if (IsTileType(t, MP_STATION)) { + if (IsDock(t) || IsOilRig(t)) Station::GetByTile(t)->ship_station.Add(t); + } + } + + /* Scan for docking tiles */ + Station *st; + FOR_ALL_STATIONS(st) { + if (st->ship_station.tile != INVALID_TILE) UpdateStationDockingTiles(st); + } + } + /* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */ Station::RecomputeCatchmentForAll(); diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index f7309a474..2287d30a1 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -725,7 +725,7 @@ static const OldChunks station_chunk[] = { OCL_NULL( 4 ), ///< bus/lorry tile OCL_SVAR( OC_TILE, Station, train_station.tile ), OCL_SVAR( OC_TILE, Station, airport.tile ), - OCL_SVAR( OC_TILE, Station, dock_tile ), + OCL_NULL( 4 ), ///< dock tile OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Station, train_station.w ), OCL_NULL( 1 ), ///< sort-index, no longer in use diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index fd8b3695f..323662f3f 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -301,6 +301,7 @@ enum SaveLoadVersion : uint16 { SLV_ROAD_TYPES, ///< 214 PR#6811 NewGRF road types. SLV_SCRIPT_MEMLIMIT, ///< 215 PR#7516 Limit on AI/GS memory consumption. + SLV_MULTITILE_DOCKS, ///< 216 PR#7380 Multiple docks per station. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 5e94de1a2..b9dcbd9e8 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -174,8 +174,8 @@ static const SaveLoad _old_station_desc[] = { SLE_CONDVAR(Station, train_station.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), SLE_CONDVAR(Station, airport.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), SLE_CONDVAR(Station, airport.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), - SLE_CONDVAR(Station, dock_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6), - SLE_CONDVAR(Station, dock_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION), + SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), + SLE_CONDNULL(4, SLV_6, SLV_MULTITILE_DOCKS), SLE_REF(Station, town, REF_TOWN), SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), SLE_CONDVAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION), @@ -423,7 +423,13 @@ static const SaveLoad _station_desc[] = { SLE_REF(Station, bus_stops, REF_ROADSTOPS), SLE_REF(Station, truck_stops, REF_ROADSTOPS), - SLE_VAR(Station, dock_tile, SLE_UINT32), + SLE_CONDNULL(4, SL_MIN_VERSION, SLV_MULTITILE_DOCKS), + SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), + SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), SLE_VAR(Station, airport.tile, SLE_UINT32), SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), diff --git a/src/script/api/script_order.cpp b/src/script/api/script_order.cpp index 04f7c69f4..a72245d04 100644 --- a/src/script/api/script_order.cpp +++ b/src/script/api/script_order.cpp @@ -261,8 +261,10 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr TILE_AREA_LOOP(t, st->train_station) { if (st->TileBelongsToRailStation(t)) return t; } - } else if (st->dock_tile != INVALID_TILE) { - return st->dock_tile; + } else if (st->ship_station.tile != INVALID_TILE) { + TILE_AREA_LOOP(t, st->ship_station) { + if (IsDockTile(t) && GetStationIndex(t) == st->index) return t; + } } else if (st->bus_stops != nullptr) { return st->bus_stops->xy; } else if (st->truck_stops != nullptr) { diff --git a/src/ship.h b/src/ship.h index fff0aee5c..ef009046a 100644 --- a/src/ship.h +++ b/src/ship.h @@ -57,6 +57,8 @@ struct Ship FINAL : public SpecializedVehicle<Ship, VEH_SHIP> { void SetDestTile(TileIndex tile); }; +bool IsShipDestinationTile(TileIndex tile, StationID station); + /** * Iterate over all ships. * @param var The variable used for iteration. diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 08b07a07d..73f2ee97f 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -34,6 +34,8 @@ #include "tunnelbridge_map.h" #include "zoom_func.h" #include "framerate_type.h" +#include "industry.h" +#include "industry_map.h" #include "table/strings.h" @@ -289,8 +291,8 @@ 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->dock_tile != INVALID_TILE) { - return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile))); + if (CanVehicleUseStation(this, st)) { + return st->xy; } else { this->IncrementRealOrderIndex(); return 0; @@ -597,6 +599,28 @@ static bool ShipMoveUpDownOnLock(Ship *v) return true; } +/** + * Test if a tile is a docking tile for the given station. + * @param tile Docking tile to test. + * @param station Destination station. + * @return true iff docking tile is next to station. + */ +bool IsShipDestinationTile(TileIndex tile, StationID station) +{ + assert(IsDockingTile(tile)); + /* Check each tile adjacent to docking tile. */ + for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) { + TileIndex t = tile + TileOffsByDiagDir(d); + if (!IsValidTile(t)) continue; + if (IsDockTile(t) && GetStationIndex(t) == station) return true; + if (IsTileType(t, MP_INDUSTRY)) { + const Industry *i = Industry::GetByTile(t); + if (i->neutral_station != nullptr && i->neutral_station->index == station) return true; + } + } + return false; +} + static void ShipController(Ship *v) { uint32 r; @@ -665,26 +689,24 @@ 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(); - - /* 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(); - } + } else if (v->current_order.IsType(OT_GOTO_DEPOT) && + v->dest_tile == gp.new_tile) { + /* Depot orders really need to reach the tile */ + if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { + VehicleEnterDepot(v); + return; + } + } else if (v->current_order.IsType(OT_GOTO_STATION) && IsDockingTile(gp.new_tile)) { + /* Process station in the orderlist. */ + Station *st = Station::Get(v->current_order.GetDestination()); + if (st->docking_station.Contains(gp.new_tile) && IsShipDestinationTile(gp.new_tile, st->index)) { + v->last_station_visited = st->index; + 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 096a3c373..1d2eea4ca 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -71,7 +71,7 @@ Station::Station(TileIndex tile) : SpecializedStation<Station, false>(tile), bus_station(INVALID_TILE, 0, 0), truck_station(INVALID_TILE, 0, 0), - dock_tile(INVALID_TILE), + ship_station(INVALID_TILE, 0, 0), indtype(IT_INVALID), time_since_load(255), time_since_unload(255), @@ -329,10 +329,10 @@ uint Station::GetCatchmentRadius() const if (this->bus_stops != nullptr) ret = max<uint>(ret, CA_BUS); if (this->truck_stops != nullptr) ret = max<uint>(ret, CA_TRUCK); if (this->train_station.tile != INVALID_TILE) ret = max<uint>(ret, CA_TRAIN); - if (this->dock_tile != INVALID_TILE) ret = max<uint>(ret, CA_DOCK); + if (this->ship_station.tile != INVALID_TILE) ret = max<uint>(ret, CA_DOCK); if (this->airport.tile != INVALID_TILE) ret = max<uint>(ret, this->airport.GetSpec()->catchment); } else { - if (this->bus_stops != nullptr || this->truck_stops != nullptr || this->train_station.tile != INVALID_TILE || this->dock_tile != INVALID_TILE || this->airport.tile != INVALID_TILE) { + if (this->bus_stops != nullptr || this->truck_stops != nullptr || this->train_station.tile != INVALID_TILE || this->ship_station.tile != INVALID_TILE || this->airport.tile != INVALID_TILE) { ret = CA_UNMODIFIED; } } diff --git a/src/station_base.h b/src/station_base.h index a0cb2c015..1f597a289 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -463,8 +463,9 @@ public: RoadStop *truck_stops; ///< All the truck stops TileArea truck_station; ///< Tile area the truck 'station' part covers - Airport airport; ///< Tile area the airport covers - TileIndex dock_tile; ///< The location of the dock + Airport airport; ///< Tile area the airport covers + TileArea ship_station; ///< Tile area the ship 'station' part covers + TileArea docking_station; ///< Tile area the docking tiles cover IndustryType indtype; ///< Industry type to get the name from diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 9dbe356e7..a7d986676 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -56,6 +56,7 @@ #include "linkgraph/linkgraph_base.h" #include "linkgraph/refresh.h" #include "widgets/station_widget.h" +#include "tunnelbridge_map.h" #include "table/strings.h" @@ -401,7 +402,7 @@ void Station::GetTileArea(TileArea *ta, StationType type) const case STATION_DOCK: case STATION_OILRIG: - ta->tile = this->dock_tile; + *ta = this->docking_station; break; default: NOT_REACHED(); @@ -1459,16 +1460,14 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 return cost; } -static void MakeRailStationAreaSmaller(BaseStation *st) +static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex)) { - TileArea ta = st->train_station; - restart: /* too small? */ if (ta.w != 0 && ta.h != 0) { /* check the left side, x = constant, y changes */ - for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(0, i));) { + for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) { /* the left side is unused? */ if (++i == ta.h) { ta.tile += TileDiffXY(1, 0); @@ -1478,7 +1477,7 @@ restart: } /* check the right side, x = constant, y changes */ - for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(ta.w - 1, i));) { + for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) { /* the right side is unused? */ if (++i == ta.h) { ta.w--; @@ -1487,7 +1486,7 @@ restart: } /* check the upper side, y = constant, x changes */ - for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, 0));) { + for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) { /* the left side is unused? */ if (++i == ta.w) { ta.tile += TileDiffXY(0, 1); @@ -1497,7 +1496,7 @@ restart: } /* check the lower side, y = constant, x changes */ - for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, ta.h - 1));) { + for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) { /* the left side is unused? */ if (++i == ta.w) { ta.h--; @@ -1508,7 +1507,28 @@ restart: ta.Clear(); } - st->train_station = ta; + return ta; +} + +static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile) +{ + return st->TileBelongsToRailStation(tile); +} + +static void MakeRailStationAreaSmaller(BaseStation *st) +{ + st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation); +} + +static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile) +{ + return IsDockTile(tile) && GetStationIndex(tile) == st->index; +} + +static void MakeShipStationAreaSmaller(Station *st) +{ + st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation); + UpdateStationDockingTiles(st); } /** @@ -2553,10 +2573,9 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK); if (ret.Failed()) return ret; - if (st != nullptr && st->dock_tile != INVALID_TILE) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK); - if (flags & DC_EXEC) { - st->dock_tile = tile; + st->ship_station.Add(tile); + st->ship_station.Add(tile + TileOffsByDiagDir(direction)); st->AddFacility(FACIL_DOCK, tile); st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY); @@ -2569,6 +2588,7 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 Company::Get(st->owner)->infrastructure.station += 2; MakeDock(tile, st->owner, st->index, direction, wc); + UpdateStationDockingTiles(st); st->AfterStationTileSetChange(true, STATION_DOCK); } @@ -2576,6 +2596,63 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]); } +void RemoveDockingTile(TileIndex t) +{ + for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) { + TileIndex tile = t + TileOffsByDiagDir(d); + if (!IsValidTile(tile)) continue; + + if (IsTileType(tile, MP_STATION)) { + UpdateStationDockingTiles(Station::GetByTile(tile)); + } else if (IsTileType(tile, MP_INDUSTRY)) { + UpdateStationDockingTiles(Industry::GetByTile(tile)->neutral_station); + } + } +} + +/** + * Clear docking tile status from tiles around a removed dock, if the tile has + * no neighbours which would keep it as a docking tile. + * @param tile Ex-dock tile to check. + */ +void ClearDockingTilesCheckingNeighbours(TileIndex tile) +{ + assert(IsValidTile(tile)); + + /* Clear and maybe re-set docking tile */ + for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) { + TileIndex docking_tile = tile + TileOffsByDiagDir(d); + if (!IsValidTile(docking_tile)) continue; + + if (IsPossibleDockingTile(docking_tile)) { + SetDockingTile(docking_tile, false); + CheckForDockingTile(docking_tile); + } + } +} + +/** + * Find the part of a dock that is land-based + * @param t Dock tile to find land part of + * @return tile of land part of dock + */ +static TileIndex FindDockLandPart(TileIndex t) +{ + assert(IsDockTile(t)); + + StationGfx gfx = GetStationGfx(t); + if (gfx < GFX_DOCK_BASE_WATER_PART) return t; + + for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) { + TileIndex tile = t + TileOffsByDiagDir(d); + if (!IsValidTile(tile)) continue; + if (!IsDockTile(tile)) continue; + if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile; + } + + return INVALID_TILE; +} + /** * Remove a dock * @param tile TileIndex been queried @@ -2588,9 +2665,10 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags) CommandCost ret = CheckOwnership(st->owner); if (ret.Failed()) return ret; - TileIndex docking_location = TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile))); + if (!IsDockTile(tile)) return CMD_ERROR; - TileIndex tile1 = st->dock_tile; + TileIndex tile1 = FindDockLandPart(tile); + if (tile1 == INVALID_TILE) return CMD_ERROR; TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1)); ret = EnsureNoVehicleOnGround(tile1); @@ -2605,26 +2683,34 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags) st->rect.AfterRemoveTile(st, tile1); st->rect.AfterRemoveTile(st, tile2); - st->dock_tile = INVALID_TILE; - st->facilities &= ~FACIL_DOCK; + MakeShipStationAreaSmaller(st); + if (st->ship_station.tile == INVALID_TILE) { + st->ship_station.Clear(); + st->docking_station.Clear(); + st->facilities &= ~FACIL_DOCK; + } Company::Get(st->owner)->infrastructure.station -= 2; st->AfterStationTileSetChange(false, STATION_DOCK); + ClearDockingTilesCheckingNeighbours(tile1); + ClearDockingTilesCheckingNeighbours(tile2); + /* All ships that were going to our station, can't go to it anymore. * Just clear the order, then automatically the next appropriate order * will be selected and in case of no appropriate order it will just * wander around the world. */ - Ship *s; - FOR_ALL_SHIPS(s) { - if (s->current_order.IsType(OT_LOADING) && s->tile == docking_location) { - s->LeaveStation(); - } + if (!(st->facilities & FACIL_DOCK)) { + Ship *s; + FOR_ALL_SHIPS(s) { + if (s->current_order.IsType(OT_LOADING) && s->current_order.GetDestination() == st->index) { + s->LeaveStation(); + } - if (s->dest_tile == docking_location) { - s->SetDestTile(0); - s->current_order.Free(); + if (s->current_order.IsType(OT_GOTO_STATION) && s->current_order.GetDestination() == st->index) { + s->SetDestTile(s->GetOrderStationLocation(st->index)); + } } } } @@ -2873,7 +2959,7 @@ draw_default_foundation: } else { assert(IsDock(ti->tile)); TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile)); - WaterClass wc = GetWaterClass(water_tile); + WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WATER_CLASS_INVALID; if (wc == WATER_CLASS_SEA) { DrawShoreTile(ti->tileh); } else { @@ -3991,6 +4077,32 @@ uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, Sourc return moved + UpdateStationWaiting(st2, type, worst_cargo, source_type, source_id); } +void UpdateStationDockingTiles(Station *st) +{ + st->docking_station.Clear(); + + /* For neutral stations, start with the industry area instead of dock area */ + const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station; + + if (area->tile == INVALID_TILE) return; + + int x = TileX(area->tile); + int y = TileY(area->tile); + + /* Expand the area by a tile on each side while + * making sure that we remain inside the map. */ + int x2 = min(x + area->w + 1, MapSizeX()); + int x1 = max(x - 1, 0); + + int y2 = min(y + area->h + 1, MapSizeY()); + int y1 = max(y - 1, 0); + + TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1)); + TILE_AREA_LOOP(tile, ta) { + if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile); + } +} + void BuildOilRig(TileIndex tile) { if (!Station::CanAllocateItem()) { @@ -4014,9 +4126,10 @@ void BuildOilRig(TileIndex tile) st->owner = OWNER_NONE; st->airport.type = AT_OILRIG; st->airport.Add(tile); - st->dock_tile = tile; + st->ship_station.Add(tile); st->facilities = FACIL_AIRPORT | FACIL_DOCK; st->build_date = _date; + UpdateStationDockingTiles(st); st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE); diff --git a/src/station_func.h b/src/station_func.h index a6d082b93..44aec087f 100644 --- a/src/station_func.h +++ b/src/station_func.h @@ -40,6 +40,9 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro bool HasStationInUse(StationID station, bool include_company, CompanyID company); void DeleteOilRig(TileIndex t); +void UpdateStationDockingTiles(Station *st); +void RemoveDockingTile(TileIndex t); +void ClearDockingTilesCheckingNeighbours(TileIndex tile); /* Check if a rail station tile is traversable. */ bool IsStationTileBlocked(TileIndex tile); diff --git a/src/station_map.h b/src/station_map.h index 35766ec69..98f8f288b 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -536,6 +536,7 @@ static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType SetTileType(t, MP_STATION); SetTileOwner(t, o); SetWaterClass(t, wc); + SetDockingTile(t, false); _m[t].m2 = sid; _m[t].m3 = 0; _m[t].m4 = 0; diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 83cec4fd3..fce0855d2 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -41,6 +41,7 @@ #include "object_base.h" #include "water.h" #include "company_gui.h" +#include "station_func.h" #include "table/strings.h" #include "table/bridge_land.h" @@ -533,6 +534,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u if (is_new_owner && c != nullptr) c->infrastructure.water += (bridge_len + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR; MakeAqueductBridgeRamp(tile_start, owner, dir); MakeAqueductBridgeRamp(tile_end, owner, ReverseDiagDir(dir)); + CheckForDockingTile(tile_start); + CheckForDockingTile(tile_end); break; default: @@ -944,6 +947,9 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags) if (v != nullptr) FreeTrainTrackReservation(v); } + bool removetile = false; + bool removeendtile = false; + /* Update company infrastructure counts. */ if (rail) { if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; @@ -953,11 +959,16 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags) UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR)); } else { // Aqueduct if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; + removetile = IsDockingTile(tile); + removeendtile = IsDockingTile(endtile); } DirtyCompanyInfrastructureWindows(owner); DoClearSquare(tile); DoClearSquare(endtile); + + if (removetile) RemoveDockingTile(tile); + if (removeendtile) RemoveDockingTile(endtile); for (TileIndex c = tile + delta; c != endtile; c += delta) { /* do not let trees appear from 'nowhere' after removing bridge */ if (IsNormalRoadTile(c) && GetRoadside(c) == ROADSIDE_TREES) { diff --git a/src/water.h b/src/water.h index 1b804720f..9e2c23418 100644 --- a/src/water.h +++ b/src/water.h @@ -38,6 +38,7 @@ void DrawWaterClassGround(const struct TileInfo *ti); void DrawShoreTile(Slope tileh); void MakeWaterKeepingClass(TileIndex tile, Owner o); +void CheckForDockingTile(TileIndex t); bool RiverModifyDesertZone(TileIndex tile, void *data); static const uint RIVER_OFFSET_DESERT_DISTANCE = 5; ///< Circular tile search radius to create non-desert around a river tile. diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index b603d7411..158bd13f0 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -39,6 +39,7 @@ #include "company_base.h" #include "company_gui.h" #include "newgrf_generic.h" +#include "industry.h" #include "table/strings.h" @@ -148,6 +149,8 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1); MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2); + CheckForDockingTile(tile); + CheckForDockingTile(tile2); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile2); MakeDefaultName(depot); @@ -156,6 +159,48 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui return cost; } +bool IsPossibleDockingTile(TileIndex t) +{ + assert(IsValidTile(t)); + switch (GetTileType(t)) { + case MP_WATER: + if (IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) return false; + FALLTHROUGH; + case MP_RAILWAY: + case MP_STATION: + case MP_TUNNELBRIDGE: + return TrackStatusToTrackBits(GetTileTrackStatus(t, TRANSPORT_WATER, 0)) != TRACK_BIT_NONE; + + default: + return false; + } +} + +/** + * Mark the supplied tile as a docking tile if it is suitable for docking. + * Tiles surrounding the tile are tested to be docks with correct orientation. + * @param t Tile to test. + */ +void CheckForDockingTile(TileIndex t) +{ + for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) { + TileIndex tile = t + TileOffsByDiagDir(d); + if (!IsValidTile(tile)) continue; + + if (IsDockTile(tile)) { + Station::GetByTile(tile)->docking_station.Add(t); + SetDockingTile(t, true); + } + if (IsTileType(tile, MP_INDUSTRY)) { + Station *st = Industry::GetByTile(tile)->neutral_station; + if (st != nullptr) { + st->docking_station.Add(t); + SetDockingTile(t, true); + } + } + } +} + void MakeWaterKeepingClass(TileIndex tile, Owner o) { WaterClass wc = GetWaterClass(tile); @@ -204,6 +249,7 @@ void MakeWaterKeepingClass(TileIndex tile, Owner o) default: break; } + if (wc != WATER_CLASS_INVALID) CheckForDockingTile(tile); MarkTileDirtyByTile(tile); } @@ -303,6 +349,8 @@ static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag } MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle); + CheckForDockingTile(tile - delta); + CheckForDockingTile(tile + delta); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile - delta); MarkTileDirtyByTile(tile + delta); @@ -449,6 +497,7 @@ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } MarkTileDirtyByTile(tile); MarkCanalsAndRiversAroundDirty(tile); + CheckForDockingTile(tile); } cost.AddCost(_price[PR_BUILD_CANAL]); @@ -489,8 +538,10 @@ static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags) Company::Get(owner)->infrastructure.water--; DirtyCompanyInfrastructureWindows(owner); } + bool remove = IsDockingTile(tile); DoClearSquare(tile); MarkCanalsAndRiversAroundDirty(tile); + if (remove) RemoveDockingTile(tile); } return CommandCost(EXPENSES_CONSTRUCTION, base_cost); @@ -504,8 +555,10 @@ static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags) if (ret.Failed()) return ret; if (flags & DC_EXEC) { + bool remove = IsDockingTile(tile); DoClearSquare(tile); MarkCanalsAndRiversAroundDirty(tile); + if (remove) RemoveDockingTile(tile); } if (IsSlopeWithOneCornerRaised(slope)) { return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]); @@ -1095,6 +1148,8 @@ void DoFloodTile(TileIndex target) /* update signals if needed */ UpdateSignalsInBuffer(); + + if (IsPossibleDockingTile(target)) CheckForDockingTile(target); } cur_company.Restore(); diff --git a/src/water_map.h b/src/water_map.h index 5d84d5ba6..375c80e6c 100644 --- a/src/water_map.h +++ b/src/water_map.h @@ -69,6 +69,8 @@ enum LockPart { LOCK_PART_UPPER = 2, ///< Upper part of a lock. }; +bool IsPossibleDockingTile(TileIndex t); + /** * Get the water tile type at a tile. * @param t Water tile to query. @@ -346,6 +348,27 @@ static inline bool HasTileWaterGround(TileIndex t) return HasTileWaterClass(t) && IsTileOnWater(t) && !IsCoastTile(t); } +/** + * Set the docking tile state of a tile. This is used by pathfinders to reach their destination. + * As well as water tiles, half-rail tiles, buoys and aqueduct ends can also be docking tiles. + * @param t the tile + * @param b the docking tile state + */ +static inline void SetDockingTile(TileIndex t, bool b) +{ + assert(IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)); + SB(_m[t].m1, 7, 1, b ? 1 : 0); +} + +/** + * Checks whether the tile is marked as a dockling tile. + * @return true iff the tile is marked as a docking tile. + */ +static inline bool IsDockingTile(TileIndex t) +{ + return (IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)) && HasBit(_m[t].m1, 7); +} + /** * Helper function to make a coast tile. @@ -356,6 +379,7 @@ static inline void MakeShore(TileIndex t) SetTileType(t, MP_WATER); SetTileOwner(t, OWNER_WATER); SetWaterClass(t, WATER_CLASS_SEA); + SetDockingTile(t, false); _m[t].m2 = 0; _m[t].m3 = 0; _m[t].m4 = 0; @@ -376,6 +400,7 @@ static inline void MakeWater(TileIndex t, Owner o, WaterClass wc, uint8 random_b SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, wc); + SetDockingTile(t, false); _m[t].m2 = 0; _m[t].m3 = 0; _m[t].m4 = random_bits; @@ -429,6 +454,7 @@ static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart pa SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, original_water_class); + SetDockingTile(t, false); _m[t].m2 = did; _m[t].m3 = 0; _m[t].m4 = 0; @@ -451,6 +477,7 @@ static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirecti SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, original_water_class); + SetDockingTile(t, false); _m[t].m2 = 0; _m[t].m3 = 0; _m[t].m4 = 0; diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index 76326a3a4..20e600b5e 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -332,6 +332,7 @@ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (wp->town == nullptr) MakeDefaultName(wp); MakeBuoy(tile, wp->index, GetWaterClass(tile)); + CheckForDockingTile(tile); MarkTileDirtyByTile(tile); wp->UpdateVirtCoord(); |