File diff r27420:b37b70eb3169 → r27421:e8c2cdc1e8e6
src/industry_gui.cpp
Show inline comments
 
@@ -137,72 +137,72 @@ static void GetCargoSuffix(uint cargo, C
 

	
 
enum CargoSuffixInOut {
 
	CARGOSUFFIX_OUT = 0,
 
	CARGOSUFFIX_IN  = 1,
 
};
 

	
 
/**
 
 * Gets all strings to display after the cargoes of industries (using callback 37)
 
 * @param use_input get suffixes for output cargoes or input cargoes?
 
 * @param cst the cargo suffix type (for which window is it requested). @see CargoSuffixType
 
 * @param ind the industry (nullptr if in fund window)
 
 * @param ind_type the industry type
 
 * @param indspec the industry spec
 
 * @param cargoes array with cargotypes. for CT_INVALID no suffix will be determined
 
 * @param suffixes is filled with the suffixes
 
 */
 
template <typename TC, typename TS>
 
static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
 
{
 
	static_assert(lengthof(cargoes) <= lengthof(suffixes));
 

	
 
	if (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) {
 
		/* Reworked behaviour with new many-in-many-out scheme */
 
		for (uint j = 0; j < lengthof(suffixes); j++) {
 
			if (cargoes[j] != CT_INVALID) {
 
			if (IsValidCargoID(cargoes[j])) {
 
				byte local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
 
				uint cargotype = local_id << 16 | use_input;
 
				GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
 
			} else {
 
				suffixes[j].text[0] = '\0';
 
				suffixes[j].display = CSD_CARGO;
 
			}
 
		}
 
	} else {
 
		/* Compatible behaviour with old 3-in-2-out scheme */
 
		for (uint j = 0; j < lengthof(suffixes); j++) {
 
			suffixes[j].text[0] = '\0';
 
			suffixes[j].display = CSD_CARGO;
 
		}
 
		switch (use_input) {
 
			case CARGOSUFFIX_OUT:
 
				if (cargoes[0] != CT_INVALID) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (cargoes[1] != CT_INVALID) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
 
				if (IsValidCargoID(cargoes[0])) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (IsValidCargoID(cargoes[1])) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
 
				break;
 
			case CARGOSUFFIX_IN:
 
				if (cargoes[0] != CT_INVALID) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (cargoes[1] != CT_INVALID) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
 
				if (cargoes[2] != CT_INVALID) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
 
				if (IsValidCargoID(cargoes[0])) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (IsValidCargoID(cargoes[1])) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
 
				if (IsValidCargoID(cargoes[2])) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
 
				break;
 
			default:
 
				NOT_REACHED();
 
		}
 
	}
 
}
 

	
 
std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types; ///< Industry types sorted by name.
 

	
 
/** Sort industry types by their name. */
 
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
 
{
 
	int r = StrNaturalCompare(GetString(GetIndustrySpec(a)->name), GetString(GetIndustrySpec(b)->name)); // Sort by name (natural sorting).
 

	
 
	/* If the names are equal, sort by industry type. */
 
	return (r != 0) ? r < 0 : (a < b);
 
}
 

	
 
/**
 
 * Initialize the list of sorted industry types.
 
 */
 
void SortIndustryTypes()
 
{
 
	/* Add each industry type to the list. */
 
@@ -325,49 +325,49 @@ class BuildIndustryWindow : public Windo
 
	{
 
		this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != INVALID_INDUSTRYTYPE && !this->enabled);
 
		this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == INVALID_INDUSTRYTYPE && this->enabled);
 
	}
 

	
 
	/**
 
	 * Build a string of cargo names with suffixes attached.
 
	 * This is distinct from the CARGO_LIST string formatting code in two ways:
 
	 *  - This cargo list uses the order defined by the industry, rather than alphabetic.
 
	 *  - NewGRF-supplied suffix strings can be attached to each cargo.
 
	 *
 
	 * @param cargolist    Array of CargoID to display
 
	 * @param cargo_suffix Array of suffixes to attach to each cargo
 
	 * @param cargolistlen Length of arrays
 
	 * @param prefixstr    String to use for the first item
 
	 * @return A formatted raw string
 
	 */
 
	std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const
 
	{
 
		std::string cargostring;
 
		int numcargo = 0;
 
		int firstcargo = -1;
 

	
 
		for (int j = 0; j < cargolistlen; j++) {
 
			if (cargolist[j] == CT_INVALID) continue;
 
			if (!IsValidCargoID(cargolist[j])) continue;
 
			numcargo++;
 
			if (firstcargo < 0) {
 
				firstcargo = j;
 
				continue;
 
			}
 
			SetDParam(0, CargoSpec::Get(cargolist[j])->name);
 
			SetDParamStr(1, cargo_suffix[j].text);
 
			cargostring += GetString(STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION);
 
		}
 

	
 
		if (numcargo > 0) {
 
			SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name);
 
			SetDParamStr(1, cargo_suffix[firstcargo].text);
 
			cargostring = GetString(prefixstr) + cargostring;
 
		} else {
 
			SetDParam(0, STR_JUST_NOTHING);
 
			SetDParamStr(1, "");
 
			cargostring = GetString(prefixstr);
 
		}
 

	
 
		return cargostring;
 
	}
 

	
 
public:
 
@@ -824,89 +824,89 @@ public:
 
	/**
 
	 * Draw the text in the #WID_IV_INFO panel.
 
	 * @param r Rectangle of the panel.
 
	 * @return Expected position of the bottom edge of the panel.
 
	 */
 
	int DrawInfo(const Rect &r)
 
	{
 
		bool rtl = _current_text_dir == TD_RTL;
 
		Industry *i = Industry::Get(this->window_number);
 
		const IndustrySpec *ind = GetIndustrySpec(i->type);
 
		Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
 
		bool first = true;
 
		bool has_accept = false;
 

	
 
		if (i->prod_level == PRODLEVEL_CLOSURE) {
 
			DrawString(ir, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
 
			ir.top += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
 
		}
 

	
 
		CargoSuffix cargo_suffix[lengthof(i->accepts_cargo)];
 
		GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
 
		bool stockpiling = HasBit(ind->callback_mask, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_mask, CBM_IND_PRODUCTION_256_TICKS);
 

	
 
		for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
 
			if (i->accepts_cargo[j] == CT_INVALID) continue;
 
			if (!IsValidCargoID(i->accepts_cargo[j])) continue;
 
			has_accept = true;
 
			if (first) {
 
				DrawString(ir, STR_INDUSTRY_VIEW_REQUIRES);
 
				ir.top += FONT_HEIGHT_NORMAL;
 
				first = false;
 
			}
 
			SetDParam(0, CargoSpec::Get(i->accepts_cargo[j])->name);
 
			SetDParam(1, i->accepts_cargo[j]);
 
			SetDParam(2, i->incoming_cargo_waiting[j]);
 
			SetDParamStr(3, "");
 
			StringID str = STR_NULL;
 
			switch (cargo_suffix[j].display) {
 
				case CSD_CARGO_AMOUNT_TEXT:
 
					SetDParamStr(3, cargo_suffix[j].text);
 
					FALLTHROUGH;
 
				case CSD_CARGO_AMOUNT:
 
					str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
 
					break;
 

	
 
				case CSD_CARGO_TEXT:
 
					SetDParamStr(3, cargo_suffix[j].text);
 
					FALLTHROUGH;
 
				case CSD_CARGO:
 
					str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
 
					break;
 

	
 
				default:
 
					NOT_REACHED();
 
			}
 
			DrawString(ir.Indent(WidgetDimensions::scaled.hsep_indent, rtl), str);
 
			ir.top += FONT_HEIGHT_NORMAL;
 
		}
 

	
 
		GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
 
		int line_height = this->editable == EA_RATE ? this->cheat_line_height : FONT_HEIGHT_NORMAL;
 
		int text_y_offset = (line_height - FONT_HEIGHT_NORMAL) / 2;
 
		int button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
 
		first = true;
 
		for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
			if (i->produced_cargo[j] == CT_INVALID) continue;
 
			if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
			if (first) {
 
				if (has_accept) ir.top += WidgetDimensions::scaled.vsep_wide;
 
				DrawString(ir, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
 
				ir.top += FONT_HEIGHT_NORMAL;
 
				if (this->editable == EA_RATE) this->production_offset_y = ir.top;
 
				first = false;
 
			}
 

	
 
			SetDParam(0, i->produced_cargo[j]);
 
			SetDParam(1, i->last_month_production[j]);
 
			SetDParamStr(2, cargo_suffix[j].text);
 
			SetDParam(3, ToPercent8(i->last_month_pct_transported[j]));
 
			DrawString(ir.Indent(WidgetDimensions::scaled.hsep_indent + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal : 0), rtl).Translate(0, text_y_offset), STR_INDUSTRY_VIEW_TRANSPORTED);
 
			/* Let's put out those buttons.. */
 
			if (this->editable == EA_RATE) {
 
				DrawArrowButtons(ir.Indent(WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
 
						i->production_rate[j] > 0, i->production_rate[j] < 255);
 
			}
 
			ir.top += line_height;
 
		}
 

	
 
		/* Display production multiplier if editable */
 
		if (this->editable == EA_MULTIPLIER) {
 
			line_height = this->cheat_line_height;
 
@@ -960,49 +960,49 @@ public:
 

	
 
	void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
 
	{
 
		if (widget == WID_IV_INFO) size->height = this->info_height;
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_IV_INFO: {
 
				Industry *i = Industry::Get(this->window_number);
 
				InfoLine line = IL_NONE;
 

	
 
				switch (this->editable) {
 
					case EA_NONE: break;
 

	
 
					case EA_MULTIPLIER:
 
						if (IsInsideBS(pt.y, this->production_offset_y, this->cheat_line_height)) line = IL_MULTIPLIER;
 
						break;
 

	
 
					case EA_RATE:
 
						if (pt.y >= this->production_offset_y) {
 
							int row = (pt.y - this->production_offset_y) / this->cheat_line_height;
 
							for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
 
								if (i->produced_cargo[j] == CT_INVALID) continue;
 
								if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
								row--;
 
								if (row < 0) {
 
									line = (InfoLine)(IL_RATE1 + j);
 
									break;
 
								}
 
							}
 
						}
 
						break;
 
				}
 
				if (line == IL_NONE) return;
 

	
 
				bool rtl = _current_text_dir == TD_RTL;
 
				Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect).Indent(WidgetDimensions::scaled.hsep_indent, rtl);
 

	
 
				if (r.WithWidth(SETTING_BUTTON_WIDTH, rtl).Contains(pt)) {
 
					/* Clicked buttons, decrease or increase production */
 
					bool decrease = r.WithWidth(SETTING_BUTTON_WIDTH / 2, rtl).Contains(pt);
 
					switch (this->editable) {
 
						case EA_MULTIPLIER:
 
							if (decrease) {
 
								if (i->prod_level <= PRODLEVEL_MINIMUM) return;
 
								i->prod_level = static_cast<byte>(std::max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM));
 
							} else {
 
								if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
 
@@ -1119,49 +1119,49 @@ public:
 
			const IndustrySpec *ind = GetIndustrySpec(i->type);
 
			this->editable = ind->UsesOriginalEconomy() ? EA_MULTIPLIER : EA_RATE;
 
		} else {
 
			this->editable = EA_NONE;
 
		}
 
	}
 

	
 
	bool IsNewGRFInspectable() const override
 
	{
 
		return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
 
	}
 

	
 
	void ShowNewGRFInspectWindow() const override
 
	{
 
		::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
 
	}
 
};
 

	
 
static void UpdateIndustryProduction(Industry *i)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 
	if (indspec->UsesOriginalEconomy()) i->RecomputeProductionMultipliers();
 

	
 
	for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
		if (i->produced_cargo[j] != CT_INVALID) {
 
		if (IsValidCargoID(i->produced_cargo[j])) {
 
			i->last_month_production[j] = 8 * i->production_rate[j];
 
		}
 
	}
 
}
 

	
 
/** Widget definition of the view industry gui */
 
static const NWidgetPart _nested_industry_view_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
 
		NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_PUSHIMGBTN, COLOUR_CREAM, WID_IV_GOTO), SetMinimalSize(12, 14), SetDataTip(SPR_GOTO_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
 
		NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
 
		NWidget(WWT_SHADEBOX, COLOUR_CREAM),
 
		NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
 
		NWidget(WWT_STICKYBOX, COLOUR_CREAM),
 
	EndContainer(),
 
	NWidget(WWT_PANEL, COLOUR_CREAM),
 
		NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
 
			NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1),
 
		EndContainer(),
 
	EndContainer(),
 
	NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0),
 
	EndContainer(),
 
	NWidget(NWID_HORIZONTAL),
 
@@ -1223,68 +1223,68 @@ enum CargoFilterSpecialType {
 
	CF_NONE = CT_INVALID,  ///< Show only industries which do not produce/accept cargo
 
};
 

	
 
/** Cargo filter functions */
 
/**
 
 * Check whether an industry accepts and produces a certain cargo pair.
 
 * @param industry The industry whose cargoes will being checked.
 
 * @param cargoes The accepted and produced cargo pair to look for.
 
 * @return bool Whether the given cargoes accepted and produced by the industry.
 
 */
 
static bool CDECL CargoFilter(const Industry * const *industry, const std::pair<CargoID, CargoID> &cargoes)
 
{
 
	auto accepted_cargo = cargoes.first;
 
	auto produced_cargo = cargoes.second;
 

	
 
	bool accepted_cargo_matches;
 

	
 
	switch (accepted_cargo) {
 
		case CF_ANY:
 
			accepted_cargo_matches = true;
 
			break;
 

	
 
		case CF_NONE:
 
			accepted_cargo_matches = std::all_of(std::begin((*industry)->accepts_cargo), std::end((*industry)->accepts_cargo), [](CargoID cargo) {
 
				return cargo == CT_INVALID;
 
				return !IsValidCargoID(cargo);
 
			});
 
			break;
 

	
 
		default:
 
			const auto &ac = (*industry)->accepts_cargo;
 
			accepted_cargo_matches = std::find(std::begin(ac), std::end(ac), accepted_cargo) != std::end(ac);
 
			break;
 
	}
 

	
 
	bool produced_cargo_matches;
 

	
 
	switch (produced_cargo) {
 
		case CF_ANY:
 
			produced_cargo_matches = true;
 
			break;
 

	
 
		case CF_NONE:
 
			produced_cargo_matches = std::all_of(std::begin((*industry)->produced_cargo), std::end((*industry)->produced_cargo), [](CargoID cargo) {
 
				return cargo == CT_INVALID;
 
				return !IsValidCargoID(cargo);
 
			});
 
			break;
 

	
 
		default:
 
			const auto &pc = (*industry)->produced_cargo;
 
			produced_cargo_matches = std::find(std::begin(pc), std::end(pc), produced_cargo) != std::end(pc);
 
			break;
 
	}
 

	
 
	return accepted_cargo_matches && produced_cargo_matches;
 
}
 

	
 
static GUIIndustryList::FilterFunction * const _filter_funcs[] = { &CargoFilter };
 

	
 

	
 
/**
 
 * The list of industries.
 
 */
 
class IndustryDirectoryWindow : public Window {
 
protected:
 
	/* Runtime saved values */
 
	static Listing last_sorting;
 

	
 
	/* Constants for sorting industries */
 
@@ -1404,49 +1404,49 @@ protected:
 
		auto filter = std::make_pair(this->cargo_filter[this->accepted_cargo_filter_criteria],
 
		                             this->cargo_filter[this->produced_cargo_filter_criteria]);
 

	
 
		this->industries.Filter(filter);
 

	
 
		IndustryDirectoryWindow::produced_cargo_filter = this->cargo_filter[this->produced_cargo_filter_criteria];
 
		this->industries.Sort();
 

	
 
		this->vscroll->SetCount(this->industries.size()); // Update scrollbar as well.
 

	
 
		this->SetDirty();
 
	}
 

	
 
	/**
 
	 * Returns percents of cargo transported if industry produces this cargo, else -1
 
	 *
 
	 * @param i industry to check
 
	 * @param id cargo slot
 
	 * @return percents of cargo transported, or -1 if industry doesn't use this cargo slot
 
	 */
 
	static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
 
	{
 
		assert(id < lengthof(i->produced_cargo));
 

	
 
		if (i->produced_cargo[id] == CT_INVALID) return -1;
 
		if (!IsValidCargoID(i->produced_cargo[id])) return -1;
 
		return ToPercent8(i->last_month_pct_transported[id]);
 
	}
 

	
 
	/**
 
	 * Returns value representing industry's transported cargo
 
	 *  percentage for industry sorting
 
	 *
 
	 * @param i industry to check
 
	 * @return value used for sorting
 
	 */
 
	static int GetCargoTransportedSortValue(const Industry *i)
 
	{
 
		CargoID filter = IndustryDirectoryWindow::produced_cargo_filter;
 
		if (filter == CF_NONE) return 0;
 

	
 
		int percentage = 0, produced_cargo_count = 0;
 
		for (uint id = 0; id < lengthof(i->produced_cargo); id++) {
 
			if (filter == CF_ANY) {
 
				int transported = GetCargoTransportedPercentsIfValid(i, id);
 
				if (transported != -1) {
 
					produced_cargo_count++;
 
					percentage += transported;
 
				}
 
				if (produced_cargo_count == 0 && id == lengthof(i->produced_cargo) - 1 && percentage == 0) {
 
@@ -1468,94 +1468,94 @@ protected:
 
		if (r == 0) return a->index < b->index;
 
		return r < 0;
 
	}
 

	
 
	/** Sort industries by type and name */
 
	static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b)
 
	{
 
		int it_a = 0;
 
		while (it_a != NUM_INDUSTRYTYPES && a->type != _sorted_industry_types[it_a]) it_a++;
 
		int it_b = 0;
 
		while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
 
		int r = it_a - it_b;
 
		return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
 
	}
 

	
 
	/** Sort industries by production and name */
 
	static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b)
 
	{
 
		CargoID filter = IndustryDirectoryWindow::produced_cargo_filter;
 
		if (filter == CF_NONE) return IndustryTypeSorter(a, b);
 

	
 
		uint prod_a = 0, prod_b = 0;
 
		for (uint i = 0; i < lengthof(a->produced_cargo); i++) {
 
			if (filter == CF_ANY) {
 
				if (a->produced_cargo[i] != CT_INVALID) prod_a += a->last_month_production[i];
 
				if (b->produced_cargo[i] != CT_INVALID) prod_b += b->last_month_production[i];
 
				if (IsValidCargoID(a->produced_cargo[i])) prod_a += a->last_month_production[i];
 
				if (IsValidCargoID(b->produced_cargo[i])) prod_b += b->last_month_production[i];
 
			} else {
 
				if (a->produced_cargo[i] == filter) prod_a += a->last_month_production[i];
 
				if (b->produced_cargo[i] == filter) prod_b += b->last_month_production[i];
 
			}
 
		}
 
		int r = prod_a - prod_b;
 

	
 
		return (r == 0) ? IndustryTypeSorter(a, b) : r < 0;
 
	}
 

	
 
	/** Sort industries by transported cargo and name */
 
	static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b)
 
	{
 
		int r = GetCargoTransportedSortValue(a) - GetCargoTransportedSortValue(b);
 
		return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
 
	}
 

	
 
	/**
 
	 * Get the StringID to draw and set the appropriate DParams.
 
	 * @param i the industry to get the StringID of.
 
	 * @return the StringID.
 
	 */
 
	StringID GetIndustryString(const Industry *i) const
 
	{
 
		const IndustrySpec *indsp = GetIndustrySpec(i->type);
 
		byte p = 0;
 

	
 
		/* Industry name */
 
		SetDParam(p++, i->index);
 

	
 
		static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
 
		GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
 

	
 
		/* Get industry productions (CargoID, production, suffix, transported) */
 
		struct CargoInfo {
 
			CargoID cargo_id;
 
			uint16 production;
 
			const char *suffix;
 
			uint transported;
 
		};
 
		std::vector<CargoInfo> cargos;
 

	
 
		for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
			if (i->produced_cargo[j] == CT_INVALID) continue;
 
			if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
			cargos.push_back({ i->produced_cargo[j], i->last_month_production[j], cargo_suffix[j].text.c_str(), ToPercent8(i->last_month_pct_transported[j]) });
 
		}
 

	
 
		switch (static_cast<IndustryDirectoryWindow::SorterType>(this->industries.SortType())) {
 
			case IndustryDirectoryWindow::SorterType::ByName:
 
			case IndustryDirectoryWindow::SorterType::ByType:
 
			case IndustryDirectoryWindow::SorterType::ByProduction:
 
				/* Sort by descending production, then descending transported */
 
				std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
 
					if (a.production != b.production) return a.production > b.production;
 
					return a.transported > b.transported;
 
				});
 
				break;
 

	
 
			case IndustryDirectoryWindow::SorterType::ByTransported:
 
				/* Sort by descending transported, then descending production */
 
				std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
 
					if (a.transported != b.transported) return a.transported > b.transported;
 
					return a.production > b.production;
 
				});
 
				break;
 
		}
 

	
 
		/* If the produced cargo filter is active then move the filtered cargo to the beginning of the list,
 
@@ -1948,101 +1948,101 @@ struct CargoesField {
 
	}
 

	
 
	/**
 
	 * Make an industry type field.
 
	 * @param ind_type Industry type (#NUM_INDUSTRYTYPES means 'houses').
 
	 * @note #other_accepted and #other_produced should be filled later.
 
	 */
 
	void MakeIndustry(IndustryType ind_type)
 
	{
 
		this->type = CFT_INDUSTRY;
 
		this->u.industry.ind_type = ind_type;
 
		MemSetT(this->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
 
		MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
 
	}
 

	
 
	/**
 
	 * Connect a cargo from an industry to the #CFT_CARGO column.
 
	 * @param cargo Cargo to connect.
 
	 * @param producer Cargo is produced (if \c false, cargo is assumed to be accepted).
 
	 * @return Horizontal connection index, or \c -1 if not accepted at all.
 
	 */
 
	int ConnectCargo(CargoID cargo, bool producer)
 
	{
 
		assert(this->type == CFT_CARGO);
 
		if (cargo == INVALID_CARGO) return -1;
 
		if (!IsValidCargoID(cargo)) return -1;
 

	
 
		/* Find the vertical cargo column carrying the cargo. */
 
		int column = -1;
 
		for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
 
			if (cargo == this->u.cargo.vertical_cargoes[i]) {
 
				column = i;
 
				break;
 
			}
 
		}
 
		if (column < 0) return -1;
 

	
 
		if (producer) {
 
			assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
 
			assert(!IsValidCargoID(this->u.cargo.supp_cargoes[column]));
 
			this->u.cargo.supp_cargoes[column]  = column;
 
		} else {
 
			assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
 
			assert(!IsValidCargoID(this->u.cargo.cust_cargoes[column]));
 
			this->u.cargo.cust_cargoes[column] = column;
 
		}
 
		return column;
 
	}
 

	
 
	/**
 
	 * Does this #CFT_CARGO field have a horizontal connection?
 
	 * @return \c true if a horizontal connection exists, \c false otherwise.
 
	 */
 
	bool HasConnection()
 
	{
 
		assert(this->type == CFT_CARGO);
 

	
 
		for (uint i = 0; i < MAX_CARGOES; i++) {
 
			if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
 
			if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
 
			if (IsValidCargoID(this->u.cargo.supp_cargoes[i])) return true;
 
			if (IsValidCargoID(this->u.cargo.cust_cargoes[i])) return true;
 
		}
 
		return false;
 
	}
 

	
 
	/**
 
	 * Make a piece of cargo column.
 
	 * @param cargoes    Array of #CargoID (may contain #INVALID_CARGO).
 
	 * @param length     Number of cargoes in \a cargoes.
 
	 * @param count      Number of cargoes to display (should be at least the number of valid cargoes, or \c -1 to let the method compute it).
 
	 * @param top_end    This is the first cargo field of this column.
 
	 * @param bottom_end This is the last cargo field of this column.
 
	 * @note #supp_cargoes and #cust_cargoes should be filled in later.
 
	 */
 
	void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
 
	{
 
		this->type = CFT_CARGO;
 
		uint i;
 
		uint num = 0;
 
		for (i = 0; i < MAX_CARGOES && i < length; i++) {
 
			if (cargoes[i] != INVALID_CARGO) {
 
			if (IsValidCargoID(cargoes[i])) {
 
				this->u.cargo.vertical_cargoes[num] = cargoes[i];
 
				num++;
 
			}
 
		}
 
		this->u.cargo.num_cargoes = (count < 0) ? num : count;
 
		for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
 
		this->u.cargo.top_end = top_end;
 
		this->u.cargo.bottom_end = bottom_end;
 
		MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
 
		MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
 
	}
 

	
 
	/**
 
	 * Make a field displaying cargo type names.
 
	 * @param cargoes    Array of #CargoID (may contain #INVALID_CARGO).
 
	 * @param length     Number of cargoes in \a cargoes.
 
	 * @param left_align ALign texts to the left (else to the right).
 
	 */
 
	void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
 
	{
 
		this->type = CFT_CARGO_LABEL;
 
		uint i;
 
		for (i = 0; i < MAX_CARGOES && i < length; i++) this->u.cargo_label.cargoes[i] = cargoes[i];
 
		for (; i < MAX_CARGOES; i++) this->u.cargo_label.cargoes[i] = INVALID_CARGO;
 
@@ -2107,192 +2107,192 @@ struct CargoesField {
 
					if (_current_text_dir == TD_RTL) {
 
						blob_right = xpos2 - blob_distance;
 
						blob_left  = blob_right - CargoesField::legend.width;
 
					} else {
 
						blob_left  = xpos + blob_distance;
 
						blob_right = blob_left + CargoesField::legend.width;
 
					}
 
					GfxFillRect(blob_left,     ypos2 - blob_distance - CargoesField::legend.height,     blob_right,     ypos2 - blob_distance,     PC_BLACK); // Border
 
					GfxFillRect(blob_left + 1, ypos2 - blob_distance - CargoesField::legend.height + 1, blob_right - 1, ypos2 - blob_distance - 1, indsp->map_colour);
 
				} else {
 
					DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
 
				}
 

	
 
				/* Draw the other_produced/other_accepted cargoes. */
 
				const CargoID *other_right, *other_left;
 
				if (_current_text_dir == TD_RTL) {
 
					other_right = this->u.industry.other_accepted;
 
					other_left  = this->u.industry.other_produced;
 
				} else {
 
					other_right = this->u.industry.other_produced;
 
					other_left  = this->u.industry.other_accepted;
 
				}
 
				ypos1 += CargoesField::cargo_border.height + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2;
 
				for (uint i = 0; i < CargoesField::max_cargoes; i++) {
 
					if (other_right[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(other_right[i])) {
 
						const CargoSpec *csp = CargoSpec::Get(other_right[i]);
 
						int xp = xpos + industry_width + CargoesField::cargo_stub.width;
 
						DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
 
						GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
 
					}
 
					if (other_left[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(other_left[i])) {
 
						const CargoSpec *csp = CargoSpec::Get(other_left[i]);
 
						int xp = xpos - CargoesField::cargo_stub.width;
 
						DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
 
						GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
 
					}
 
					ypos1 += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height;
 
				}
 
				break;
 
			}
 

	
 
			case CFT_CARGO: {
 
				int cargo_base = this->GetCargoBase(xpos);
 
				int top = ypos + (this->u.cargo.top_end ? vert_inter_industry_space / 2 + 1 : 0);
 
				int bot = ypos - (this->u.cargo.bottom_end ? vert_inter_industry_space / 2 + 1 : 0) + normal_height - 1;
 
				int colpos = cargo_base;
 
				for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
 
					if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + CargoesField::cargo_line.width - 1, top - 1, CARGO_LINE_COLOUR);
 
					if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + CargoesField::cargo_line.width - 1, bot + 1, CARGO_LINE_COLOUR);
 
					GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
 
					colpos++;
 
					const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
 
					GfxFillRect(colpos, top, colpos + CargoesField::cargo_line.width - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
 
					colpos += CargoesField::cargo_line.width - 2;
 
					GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
 
					colpos += 1 + CargoesField::cargo_space.width;
 
				}
 

	
 
				const CargoID *hor_left, *hor_right;
 
				if (_current_text_dir == TD_RTL) {
 
					hor_left  = this->u.cargo.cust_cargoes;
 
					hor_right = this->u.cargo.supp_cargoes;
 
				} else {
 
					hor_left  = this->u.cargo.supp_cargoes;
 
					hor_right = this->u.cargo.cust_cargoes;
 
				}
 
				ypos += CargoesField::cargo_border.height + vert_inter_industry_space / 2 + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2;
 
				for (uint i = 0; i < MAX_CARGOES; i++) {
 
					if (hor_left[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(hor_left[i])) {
 
						int col = hor_left[i];
 
						int dx = 0;
 
						const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
 
						for (; col > 0; col--) {
 
							int lf = cargo_base + col * CargoesField::cargo_line.width + (col - 1) * CargoesField::cargo_space.width;
 
							DrawHorConnection(lf, lf + CargoesField::cargo_space.width - dx, ypos, csp);
 
							dx = 1;
 
						}
 
						DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
 
					}
 
					if (hor_right[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(hor_right[i])) {
 
						int col = hor_right[i];
 
						int dx = 0;
 
						const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
 
						for (; col < this->u.cargo.num_cargoes - 1; col++) {
 
							int lf = cargo_base + (col + 1) * CargoesField::cargo_line.width + col * CargoesField::cargo_space.width;
 
							DrawHorConnection(lf + dx - 1, lf + CargoesField::cargo_space.width - 1, ypos, csp);
 
							dx = 1;
 
						}
 
						DrawHorConnection(cargo_base + col * CargoesField::cargo_space.width + (col + 1) * CargoesField::cargo_line.width - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
 
					}
 
					ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height;
 
				}
 
				break;
 
			}
 

	
 
			case CFT_CARGO_LABEL:
 
				ypos += CargoesField::cargo_border.height + vert_inter_industry_space / 2;
 
				for (uint i = 0; i < MAX_CARGOES; i++) {
 
					if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(this->u.cargo_label.cargoes[i])) {
 
						const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
 
						DrawString(xpos + WidgetDimensions::scaled.framerect.left, xpos + industry_width - 1 - WidgetDimensions::scaled.framerect.right, ypos, csp->name, TC_WHITE,
 
								(this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
 
					}
 
					ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height;
 
				}
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
	}
 

	
 
	/**
 
	 * Decide which cargo was clicked at in a #CFT_CARGO field.
 
	 * @param left  Left industry neighbour if available (else \c nullptr should be supplied).
 
	 * @param right Right industry neighbour if available (else \c nullptr should be supplied).
 
	 * @param pt    Click position in the cargo field.
 
	 * @return Cargo clicked at, or #INVALID_CARGO if none.
 
	 */
 
	CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
 
	{
 
		assert(this->type == CFT_CARGO);
 

	
 
		/* Vertical matching. */
 
		int cpos = this->GetCargoBase(0);
 
		uint col;
 
		for (col = 0; col < this->u.cargo.num_cargoes; col++) {
 
			if (pt.x < cpos) break;
 
			if (pt.x < cpos + (int)CargoesField::cargo_line.width) return this->u.cargo.vertical_cargoes[col];
 
			cpos += CargoesField::cargo_line.width + CargoesField::cargo_space.width;
 
		}
 
		/* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
 

	
 
		int vpos = vert_inter_industry_space / 2 + CargoesField::cargo_border.width;
 
		uint row;
 
		for (row = 0; row < MAX_CARGOES; row++) {
 
			if (pt.y < vpos) return INVALID_CARGO;
 
			if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
 
			vpos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.width;
 
		}
 
		if (row == MAX_CARGOES) return INVALID_CARGO;
 

	
 
		/* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
 
		if (col == 0) {
 
			if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
 
			if (IsValidCargoID(this->u.cargo.supp_cargoes[row])) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
 
			if (left != nullptr) {
 
				if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
 
				if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
 
			}
 
			return INVALID_CARGO;
 
		}
 
		if (col == this->u.cargo.num_cargoes) {
 
			if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
 
			if (IsValidCargoID(this->u.cargo.cust_cargoes[row])) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
 
			if (right != nullptr) {
 
				if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
 
				if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
 
			}
 
			return INVALID_CARGO;
 
		}
 
		if (row >= col) {
 
			/* Clicked somewhere in-between vertical cargo connection.
 
			 * Since the horizontal connection is made in the same order as the vertical list, the above condition
 
			 * ensures we are left-below the main diagonal, thus at the supplying side.
 
			 */
 
			return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
 
			return (IsValidCargoID(this->u.cargo.supp_cargoes[row])) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
 
		} else {
 
			/* Clicked at a customer connection. */
 
			return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
 
			return (IsValidCargoID(this->u.cargo.cust_cargoes[row])) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
 
		}
 
	}
 

	
 
	/**
 
	 * Decide what cargo the user clicked in the cargo label field.
 
	 * @param pt Click position in the cargo label field.
 
	 * @return Cargo clicked at, or #INVALID_CARGO if none.
 
	 */
 
	CargoID CargoLabelClickedAt(Point pt) const
 
	{
 
		assert(this->type == CFT_CARGO_LABEL);
 

	
 
		int vpos = vert_inter_industry_space / 2 + CargoesField::cargo_border.height;
 
		uint row;
 
		for (row = 0; row < MAX_CARGOES; row++) {
 
			if (pt.y < vpos) return INVALID_CARGO;
 
			if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
 
			vpos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height;
 
		}
 
		if (row == MAX_CARGOES) return INVALID_CARGO;
 
		return this->u.cargo_label.cargoes[row];
 
	}
 

	
 
private:
 
@@ -2340,49 +2340,49 @@ struct CargoesRow {
 
	 * Connect industry production cargoes to the cargo column after it.
 
	 * @param column Column of the industry.
 
	 */
 
	void ConnectIndustryProduced(int column)
 
	{
 
		CargoesField *ind_fld   = this->columns + column;
 
		CargoesField *cargo_fld = this->columns + column + 1;
 
		assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
 

	
 
		MemSetT(ind_fld->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
 

	
 
		if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
 
			CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
 
			int other_count = 0;
 

	
 
			const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
 
			assert(CargoesField::max_cargoes <= lengthof(indsp->produced_cargo));
 
			for (uint i = 0; i < CargoesField::max_cargoes; i++) {
 
				int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
 
				if (col < 0) others[other_count++] = indsp->produced_cargo[i];
 
			}
 

	
 
			/* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
 
			for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
 
				if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
 
				if (!IsValidCargoID(cargo_fld->u.cargo.supp_cargoes[i])) ind_fld->u.industry.other_produced[i] = others[--other_count];
 
			}
 
		} else {
 
			/* Houses only display what is demanded. */
 
			for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
 
				CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
 
				if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
 
			}
 
		}
 
	}
 

	
 
	/**
 
	 * Construct a #CFT_CARGO_LABEL field.
 
	 * @param column    Column to create the new field.
 
	 * @param accepting Display accepted cargo (if \c false, display produced cargo).
 
	 */
 
	void MakeCargoLabel(int column, bool accepting)
 
	{
 
		CargoID cargoes[MAX_CARGOES];
 
		MemSetT(cargoes, INVALID_CARGO, lengthof(cargoes));
 

	
 
		CargoesField *label_fld = this->columns + column;
 
		CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
 

	
 
		assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
 
@@ -2398,49 +2398,49 @@ struct CargoesRow {
 
	 * Connect industry accepted cargoes to the cargo column before it.
 
	 * @param column Column of the industry.
 
	 */
 
	void ConnectIndustryAccepted(int column)
 
	{
 
		CargoesField *ind_fld   = this->columns + column;
 
		CargoesField *cargo_fld = this->columns + column - 1;
 
		assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
 

	
 
		MemSetT(ind_fld->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
 

	
 
		if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
 
			CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
 
			int other_count = 0;
 

	
 
			const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
 
			assert(CargoesField::max_cargoes <= lengthof(indsp->accepts_cargo));
 
			for (uint i = 0; i < CargoesField::max_cargoes; i++) {
 
				int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
 
				if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
 
			}
 

	
 
			/* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
 
			for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
 
				if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
 
				if (!IsValidCargoID(cargo_fld->u.cargo.cust_cargoes[i])) ind_fld->u.industry.other_accepted[i] = others[--other_count];
 
			}
 
		} else {
 
			/* Houses only display what is demanded. */
 
			for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
 
				for (uint h = 0; h < NUM_HOUSES; h++) {
 
					HouseSpec *hs = HouseSpec::Get(h);
 
					if (!hs->enabled) continue;
 

	
 
					for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
 
						if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
 
							cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
 
							goto next_cargo;
 
						}
 
					}
 
				}
 
next_cargo: ;
 
			}
 
		}
 
	}
 
};
 

	
 

	
 
/**
 
 * Window displaying the cargo connections around an industry (or cargo).
 
@@ -2581,90 +2581,90 @@ struct IndustryCargoesWindow : public Wi
 
	void SetStringParameters  (int widget) const override
 
	{
 
		if (widget != WID_IC_CAPTION) return;
 

	
 
		if (this->ind_cargo < NUM_INDUSTRYTYPES) {
 
			const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
 
			SetDParam(0, indsp->name);
 
		} else {
 
			const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
 
			SetDParam(0, csp->name);
 
		}
 
	}
 

	
 
	/**
 
	 * Do the two sets of cargoes have a valid cargo in common?
 
	 * @param cargoes1 Base address of the first cargo array.
 
	 * @param length1  Number of cargoes in the first cargo array.
 
	 * @param cargoes2 Base address of the second cargo array.
 
	 * @param length2  Number of cargoes in the second cargo array.
 
	 * @return Arrays have at least one valid cargo in common.
 
	 */
 
	static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
 
	{
 
		while (length1 > 0) {
 
			if (*cargoes1 != INVALID_CARGO) {
 
			if (IsValidCargoID(*cargoes1)) {
 
				for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
 
			}
 
			cargoes1++;
 
			length1--;
 
		}
 
		return false;
 
	}
 

	
 
	/**
 
	 * Can houses be used to supply one of the cargoes?
 
	 * @param cargoes Base address of the cargo array.
 
	 * @param length  Number of cargoes in the array.
 
	 * @return Houses can supply at least one of the cargoes.
 
	 */
 
	static bool HousesCanSupply(const CargoID *cargoes, uint length)
 
	{
 
		for (uint i = 0; i < length; i++) {
 
			if (cargoes[i] == INVALID_CARGO) continue;
 
			if (!IsValidCargoID(cargoes[i])) continue;
 
			if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
 
		}
 
		return false;
 
	}
 

	
 
	/**
 
	 * Can houses be used as customers of the produced cargoes?
 
	 * @param cargoes Base address of the cargo array.
 
	 * @param length  Number of cargoes in the array.
 
	 * @return Houses can accept at least one of the cargoes.
 
	 */
 
	static bool HousesCanAccept(const CargoID *cargoes, uint length)
 
	{
 
		HouseZones climate_mask;
 
		switch (_settings_game.game_creation.landscape) {
 
			case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
 
			case LT_ARCTIC:    climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
 
			case LT_TROPIC:    climate_mask = HZ_SUBTROPIC; break;
 
			case LT_TOYLAND:   climate_mask = HZ_TOYLND; break;
 
			default: NOT_REACHED();
 
		}
 
		for (uint i = 0; i < length; i++) {
 
			if (cargoes[i] == INVALID_CARGO) continue;
 
			if (!IsValidCargoID(cargoes[i])) continue;
 

	
 
			for (uint h = 0; h < NUM_HOUSES; h++) {
 
				HouseSpec *hs = HouseSpec::Get(h);
 
				if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
 

	
 
				for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
 
					if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
 
				}
 
			}
 
		}
 
		return false;
 
	}
 

	
 
	/**
 
	 * Count how many industries have accepted cargoes in common with one of the supplied set.
 
	 * @param cargoes Cargoes to search.
 
	 * @param length  Number of cargoes in \a cargoes.
 
	 * @return Number of industries that have an accepted cargo in common with the supplied set.
 
	 */
 
	static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
 
	{
 
		int count = 0;
 
		for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
			const IndustrySpec *indsp = GetIndustrySpec(it);
 
@@ -2991,55 +2991,55 @@ struct IndustryCargoesWindow : public Wi
 
		} else {
 
			fieldxy->x = column;
 
			xy->x = xpos;
 
		}
 
		return true;
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_IC_PANEL: {
 
				Point fieldxy, xy;
 
				if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
 

	
 
				const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
 
				switch (fld->type) {
 
					case CFT_INDUSTRY:
 
						if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
 
						break;
 

	
 
					case CFT_CARGO: {
 
						CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
 
						CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
 
						CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
 
						if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
 
						if (IsValidCargoID(cid)) this->ComputeCargoDisplay(cid);
 
						break;
 
					}
 

	
 
					case CFT_CARGO_LABEL: {
 
						CargoID cid = fld->CargoLabelClickedAt(xy);
 
						if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
 
						if (IsValidCargoID(cid)) this->ComputeCargoDisplay(cid);
 
						break;
 
					}
 

	
 
					default:
 
						break;
 
				}
 
				break;
 
			}
 

	
 
			case WID_IC_NOTIFY:
 
				this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
 
				this->SetWidgetDirty(WID_IC_NOTIFY);
 
				if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
 

	
 
				if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
 
					if (FindWindowByClass(WC_SMALLMAP) == nullptr) ShowSmallMap();
 
					this->NotifySmallmap();
 
				}
 
				break;
 

	
 
			case WID_IC_CARGO_DROPDOWN: {
 
				DropDownList lst;
 
				for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
 
					lst.emplace_back(new DropDownListStringItem(cs->name, cs->Index(), false));
 
@@ -3092,49 +3092,49 @@ struct IndustryCargoesWindow : public Wi
 
		const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
 
		CargoID cid = INVALID_CARGO;
 
		switch (fld->type) {
 
			case CFT_CARGO: {
 
				CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
 
				CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
 
				cid = fld->CargoClickedAt(lft, rgt, xy);
 
				break;
 
			}
 

	
 
			case CFT_CARGO_LABEL: {
 
				cid = fld->CargoLabelClickedAt(xy);
 
				break;
 
			}
 

	
 
			case CFT_INDUSTRY:
 
				if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
 
					GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, 0, nullptr, close_cond);
 
				}
 
				return true;
 

	
 
			default:
 
				break;
 
		}
 
		if (cid != INVALID_CARGO && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
 
		if (IsValidCargoID(cid) && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
 
			const CargoSpec *csp = CargoSpec::Get(cid);
 
			uint64 params[5];
 
			params[0] = csp->name;
 
			GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, 1, params, close_cond);
 
			return true;
 
		}
 

	
 
		return false;
 
	}
 

	
 
	void OnResize() override
 
	{
 
		this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL, WidgetDimensions::scaled.framerect.top + CargoesField::small_height);
 
	}
 
};
 

	
 
/**
 
 * Open the industry and cargoes window.
 
 * @param id Industry type to display, \c NUM_INDUSTRYTYPES selects a default industry type.
 
 */
 
static void ShowIndustryCargoesWindow(IndustryType id)
 
{
 
	if (id >= NUM_INDUSTRYTYPES) {
 
		for (IndustryType ind : _sorted_industry_types) {