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