Feature: Multi-tile docks and docking points.

pull/104/head
peter1138 5 years ago committed by Niels Martin Hansen
parent f1c3915341
commit f538179878

@ -249,6 +249,7 @@
<td valign=top nowrap>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>

@ -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>

@ -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;

@ -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);

@ -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;
}

@ -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);

@ -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;

@ -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
};

@ -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);
}

@ -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;

@ -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();

@ -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

@ -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
};

@ -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),

@ -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) {

@ -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.

@ -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();
}
}
}

@ -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;
}
}

@ -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

@ -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);

@ -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);

@ -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;

@ -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) {

@ -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.

@ -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();

@ -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;

@ -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();

Loading…
Cancel
Save