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