Skip to content

Commit

Permalink
Feature: Multi-tile docks and docking points.
Browse files Browse the repository at this point in the history
  • Loading branch information
PeterN authored and nielsmh committed Jun 30, 2019
1 parent f1c3915 commit f538179
Show file tree
Hide file tree
Showing 27 changed files with 471 additions and 80 deletions.
4 changes: 4 additions & 0 deletions docs/landscape.html
Expand Up @@ -249,6 +249,7 @@ <h3><a name="Landscape">Landscape</a></h3>
<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>
Expand Down Expand Up @@ -871,6 +872,7 @@ <h3><a name="Landscape">Landscape</a></h3>
<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>
Expand Down Expand Up @@ -1008,6 +1010,7 @@ <h3><a name="Landscape">Landscape</a></h3>
<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>
Expand Down Expand Up @@ -1459,6 +1462,7 @@ <h3><a name="Landscape">Landscape</a></h3>
<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>
Expand Down
8 changes: 4 additions & 4 deletions docs/landscape_grid.html
Expand Up @@ -100,7 +100,7 @@ <h3 style="font-weight: bold;">Landscape</h3>
<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>
Expand Down Expand Up @@ -208,7 +208,7 @@ <h3 style="font-weight: bold;">Landscape</h3>
<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>
Expand Down Expand Up @@ -300,7 +300,7 @@ <h3 style="font-weight: bold;">Landscape</h3>
<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>
Expand Down Expand Up @@ -354,7 +354,7 @@ <h3 style="font-weight: bold;">Landscape</h3>
<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>
Expand Down
2 changes: 2 additions & 0 deletions src/bridge_map.h
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions src/industry_cmd.cpp
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/order_cmd.cpp
Expand Up @@ -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;
}

Expand Down
22 changes: 17 additions & 5 deletions src/pathfinder/npf/npf.cpp
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
14 changes: 13 additions & 1 deletion src/pathfinder/yapf/yapf_node_ship.hpp
Expand Up @@ -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;
Expand Down
94 changes: 86 additions & 8 deletions src/pathfinder/yapf/yapf_ship.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -75,14 +158,12 @@ class CYapfFollowShipT

/* 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);

Expand Down Expand Up @@ -124,14 +205,11 @@ class CYapfFollowShipT
*/
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;

Expand Down Expand Up @@ -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
};
Expand Down
8 changes: 7 additions & 1 deletion src/rail_cmd.cpp
Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions src/rail_map.h
Expand Up @@ -17,6 +17,7 @@
#include "signal_func.h"
#include "track_func.h"
#include "tile_map.h"
#include "water_map.h"
#include "signal_type.h"


Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit f538179

Please sign in to comment.