Merge branch 'master' into jgrpp-nrt

Merge trunk multiple docks implementation

# Conflicts:
#	docs/landscape_grid.html
#	src/order_cmd.cpp
#	src/pathfinder/npf/npf.cpp
#	src/pathfinder/yapf/yapf_ship.cpp
#	src/rail_cmd.cpp
#	src/saveload/afterload.cpp
#	src/saveload/oldloader_sl.cpp
#	src/saveload/station_sl.cpp
#	src/script/api/script_order.cpp
#	src/ship_cmd.cpp
#	src/station.cpp
#	src/station_base.h
#	src/station_cmd.cpp
#	src/tunnelbridge_cmd.cpp
This commit is contained in:
Jonathan G Rennison
2019-07-13 20:34:52 +01:00
50 changed files with 783 additions and 543 deletions

View File

@@ -40,7 +40,6 @@
#include "station_base.h"
#include "station_kdtree.h"
#include "roadstop_base.h"
#include "dock_base.h"
#include "newgrf_railtype.h"
#include "newgrf_roadtype.h"
#include "waypoint_base.h"
@@ -58,6 +57,7 @@
#include "linkgraph/refresh.h"
#include "widgets/station_widget.h"
#include "zoning.h"
#include "tunnelbridge_map.h"
#include "table/strings.h"
@@ -403,7 +403,7 @@ void Station::GetTileArea(TileArea *ta, StationType type) const
case STATION_DOCK:
case STATION_OILRIG:
*ta = this->dock_station;
*ta = this->docking_station;
break;
default: NOT_REACHED();
@@ -1578,16 +1578,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);
@@ -1597,7 +1595,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--;
@@ -1606,7 +1604,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);
@@ -1616,7 +1614,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--;
@@ -1627,7 +1625,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);
}
/**
@@ -2756,20 +2775,13 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
/* Distant join */
if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
if (!Dock::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_DOCKS);
ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
if (ret.Failed()) return ret;
if (flags & DC_EXEC) {
/* Create the dock and insert it into the list of docks. */
Dock *dock = new Dock(slope_tile, flat_tile);
dock->next = st->docks;
st->docks = dock;
st->dock_station.Add(slope_tile);
st->dock_station.Add(flat_tile);
st->AddFacility(FACIL_DOCK, slope_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);
@@ -2780,7 +2792,8 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
Company::Get(st->owner)->infrastructure.station += 2;
MakeDock(slope_tile, st->owner, st->index, direction, wc);
MakeDock(tile, st->owner, st->index, direction, wc);
UpdateStationDockingTiles(st);
st->AfterStationTileSetChange(true, STATION_DOCK);
ZoningMarkDirtyStationCoverageArea(st);
@@ -2789,6 +2802,85 @@ 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);
}
}
}
/**
* Check if a dock tile can be docked from the given direction.
* @param t Tile index of dock.
* @param d DiagDirection adjacent to dock being tested.
* @return True iff the dock can be docked from the given direction.
*/
bool IsValidDockingDirectionForDock(TileIndex t, DiagDirection d)
{
assert(IsDockTile(t));
/** Bitmap of valid directions for each dock tile part. */
static const uint8 _valid_docking_tile[] = {
0, 0, 0, 0, // No docking against the slope part.
1 << DIAGDIR_NE | 1 << DIAGDIR_SW, // Docking permitted at the end
1 << DIAGDIR_NW | 1 << DIAGDIR_SE, // of the flat piers.
};
StationGfx gfx = GetStationGfx(t);
assert(gfx < lengthof(_valid_docking_tile));
return HasBit(_valid_docking_tile[gfx], d);
}
/**
* 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
@@ -2801,14 +2893,11 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
CommandCost ret = CheckOwnership(st->owner);
if (ret.Failed()) return ret;
Dock *removing_dock = Dock::GetByTile(tile);
assert(removing_dock != nullptr);
if (!IsDockTile(tile)) return CMD_ERROR;
TileIndex tile1 = removing_dock->sloped;
TileIndex tile2 = removing_dock->flat;
DiagDirection direction = DiagdirBetweenTiles(removing_dock->sloped, removing_dock->flat);
TileIndex docking_location = removing_dock->flat + TileOffsByDiagDir(direction);
TileIndex tile1 = FindDockLandPart(tile);
if (tile1 == INVALID_TILE) return CMD_ERROR;
TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
ret = EnsureNoVehicleOnGround(tile1);
if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
@@ -2817,22 +2906,6 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
if (flags & DC_EXEC) {
ZoningMarkDirtyStationCoverageArea(st);
if (st->docks == removing_dock) {
/* The first dock in the list is removed. */
st->docks = removing_dock->next;
/* Last dock is removed. */
if (st->docks == nullptr) {
st->facilities &= ~FACIL_DOCK;
}
} else {
/* Tell the predecessor in the list to skip this dock. */
Dock *pred = st->docks;
while (pred->next != removing_dock) pred = pred->next;
pred->next = removing_dock->next;
}
delete removing_dock;
DoClearSquare(tile1);
MarkTileDirtyByTile(tile1);
MakeWaterKeepingClass(tile2, st->owner);
@@ -2840,29 +2913,35 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
st->rect.AfterRemoveTile(st, tile1);
st->rect.AfterRemoveTile(st, tile2);
st->dock_station.Clear();
for (Dock *dock = st->docks; dock != nullptr; dock = dock->next) {
st->dock_station.Add(dock->flat);
st->dock_station.Add(dock->sloped);
MakeShipStationAreaSmaller(st);
if (st->ship_station.tile == INVALID_TILE) {
st->ship_station.Clear();
st->docking_station.Clear();
st->docking_tiles.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));
}
}
}
}
@@ -3111,7 +3190,7 @@ draw_default_foundation:
} else {
assert_tile(IsDock(ti->tile), 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 {
@@ -4243,6 +4322,33 @@ 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();
st->docking_tiles.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()) {
@@ -4266,18 +4372,10 @@ void BuildOilRig(TileIndex tile)
st->owner = OWNER_NONE;
st->airport.type = AT_OILRIG;
st->airport.Add(tile);
st->dock_station.tile = tile;
st->facilities = FACIL_AIRPORT;
if (!Dock::CanAllocateItem()) {
DEBUG(misc, 0, "Can't allocate dock for oilrig at 0x%X, reverting to oilrig with airport only", tile);
} else {
st->docks = new Dock(tile, tile + ToTileIndexDiff({1, 0}));
st->dock_station.tile = tile;
st->facilities |= FACIL_DOCK;
}
st->ship_station.Add(tile);
st->facilities = FACIL_AIRPORT | FACIL_DOCK;
st->build_date = _date;
UpdateStationDockingTiles(st);
st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
@@ -4295,20 +4393,46 @@ void DeleteOilRig(TileIndex tile)
MakeWaterKeepingClass(tile, OWNER_NONE);
st->dock_station.tile = INVALID_TILE;
if (st->docks != nullptr) {
delete st->docks;
st->docks = nullptr;
assert(st->facilities == (FACIL_AIRPORT | FACIL_DOCK) && st->airport.type == AT_OILRIG);
delete st;
return;
MakeShipStationAreaSmaller(st);
if (st->ship_station.tile == INVALID_TILE) {
st->ship_station.Clear();
st->docking_station.Clear();
st->docking_tiles.clear();
st->facilities &= ~FACIL_DOCK;
}
st->airport.Clear();
st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
st->facilities &= ~FACIL_AIRPORT;
st->airport.flags = 0;
st->rect.AfterRemoveTile(st, tile);
st->UpdateVirtCoord();
st->RecomputeCatchment();
if (!st->IsInUse()) delete st;
if (!st->IsInUse()) {
delete st;
} else {
st->industry = nullptr;
/* 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. */
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->current_order.IsType(OT_GOTO_STATION) && s->current_order.GetDestination() == st->index) {
s->SetDestTile(s->GetOrderStationLocation(st->index));
}
}
}
}
}
static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)