/**
* Build a dock/haven.
* @param tile tile where dock will be built
* @param flags operation to perform
* @param p1 (bit 0) - allow docks directly adjacent to other docks.
* @param p2 bit 16-31: station ID to join (NEW_STATION if build new one)
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
StationID station_to_join = GB(p2, 16, 16);
bool reuse = (station_to_join != NEW_STATION);
if (!reuse) station_to_join = INVALID_STATION;
bool distant_join = (station_to_join != INVALID_STATION);
if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile));
if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
direction = ReverseDiagDir(direction);
/* Docks cannot be placed on rapids */
if (HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
if (ret.Failed()) return ret;
if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
cost.AddCost(ret);
/* Move to second tile. */
TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
if (IsBridgeAbove(tile_cur)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
// if (!HasTileWaterGround(tile_cur)) {
// return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
// }
WaterClass wc = HasTileWaterGround(tile_cur) ? GetWaterClass(tile_cur) : WATER_CLASS_CANAL;
Owner oc = HasTileWaterGround(tile_cur) && GetWaterClass(tile_cur) == WATER_CLASS_CANAL ? GetCanalOwner(tile_cur) : _current_company;
bool river = HasTileCanalOnRiver(tile_cur);
bool add_cost = !IsWaterTile(tile_cur);
/* At this point we got a tile_cur with no bridge over it. Check for ownership */
if (IsWaterTile(tile_cur) && IsCanal(tile_cur)) {
ret = EnsureNoVehicleOnGround(tile_cur);
if (ret.Failed()) return ret;
if (oc != OWNER_NONE) {
ret = CheckTileOwnership(tile_cur);
if (ret.Failed() && !_settings_game.construction.build_on_competitor_canal) return ret;
}
} else {
ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
if (add_cost) {
cost.AddCost(ret);
if (wc == WATER_CLASS_CANAL) cost.AddCost(_price[PR_BUILD_CANAL]);
}
}
if (!IsTileFlat(tile_cur)) {
int z_tile = GetTileMaxZ(tile);
int z_tile_cur = GetTileMaxZ(tile_cur);
bool z = Delta(z_tile_cur, z_tile) == 0;
Slope slope = GetTileSlope(tile_cur);
/* Mark the tile as already cleared for the terraform command.
* Do this for all tiles (like trees), not only objects. */
ClearedObjectArea *coa = FindClearedObject(tile_cur);
if (coa == NULL) {
coa = _cleared_object_areas.Append();
coa->first_tile = tile_cur;
coa->area = TileArea(tile_cur, 1, 1);
}
/* Hide the tile from the terraforming command */
TileIndex tile_cur_before = coa->first_tile;
coa->first_tile = INVALID_TILE;
ret = DoCommand(tile_cur, z ? slope : ComplementSlope(slope), z ? 0 : 1, flags | DC_NO_WATER, CMD_TERRAFORM_LAND);
// Slope slope_after = GetTileSlope(tile_cur);
coa->first_tile = tile_cur_before;
if (ret.Failed()) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
cost.AddCost(ret);
// return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
}
/* Move to third tile. */
tile_cur += TileOffsByDiagDir(direction);
// if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) {
// return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
// }
TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
_dock_w_chk[direction], _dock_h_chk[direction]);
/* middle */
Station *st = NULL;
ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p1, 0), dock_area, &st);
if (ret.Failed()) return ret;
/* Distant join */
if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join);
ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
if (ret.Failed()) return ret;
if (st != NULL && st->dock_tile != INVALID_TILE) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK);
if (flags & DC_EXEC) {
st->dock_tile = tile;
st->AddFacility(FACIL_DOCK, tile);
st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
if (add_cost && wc == WATER_CLASS_CANAL) Company::Get(st->owner)->infrastructure.water++;
Company::Get(st->owner)->infrastructure.station += 2;
DirtyCompanyInfrastructureWindows(st->owner);
MakeDock(tile, st->owner, oc, st->index, direction, wc);
if (river) SetCanalOnRiver(tile + TileOffsByDiagDir(direction));
MarkTileDirtyByTile(tile + TileOffsByDiagDir(direction), 0);
st->UpdateVirtCoord();
UpdateStationAcceptance(st, false);
st->RecomputeIndustriesNear();
InvalidateWindowData(WC_SELECT_STATION, 0, 0);
InvalidateWindowData(WC_STATION_LIST, st->owner, 0);
SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_SHIPS);
}
return cost;
}