OpenTTD
industry_gui.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include "error.h"
12 #include "gui.h"
13 #include "settings_gui.h"
14 #include "sound_func.h"
15 #include "window_func.h"
16 #include "textbuf_gui.h"
17 #include "command_func.h"
18 #include "viewport_func.h"
19 #include "industry.h"
20 #include "town.h"
21 #include "cheat_type.h"
22 #include "newgrf_industries.h"
23 #include "newgrf_text.h"
24 #include "newgrf_debug.h"
25 #include "network/network.h"
26 #include "strings_func.h"
27 #include "company_func.h"
28 #include "tilehighlight_func.h"
29 #include "string_func.h"
30 #include "sortlist_type.h"
31 #include "widgets/dropdown_func.h"
32 #include "company_base.h"
33 #include "core/geometry_func.hpp"
34 #include "core/random_func.hpp"
35 #include "core/backup_type.hpp"
36 #include "genworld.h"
37 #include "smallmap_gui.h"
38 #include "widgets/dropdown_type.h"
40 
41 #include "table/strings.h"
42 
43 #include <bitset>
44 
45 #include "safeguards.h"
46 
47 bool _ignore_restrictions;
48 std::bitset<NUM_INDUSTRYTYPES> _displayed_industries;
49 
55 };
56 
63 };
64 
66 struct CargoSuffix {
68  char text[512];
69 };
70 
71 static void ShowIndustryCargoesWindow(IndustryType id);
72 
82 static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
83 {
84  suffix.text[0] = '\0';
85  suffix.display = CSD_CARGO_AMOUNT;
86 
87  if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) {
88  TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE;
89  uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, t);
90  if (callback == CALLBACK_FAILED) return;
91 
92  if (indspec->grf_prop.grffile->grf_version < 8) {
93  if (GB(callback, 0, 8) == 0xFF) return;
94  if (callback < 0x400) {
96  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text));
99  return;
100  }
102  return;
103 
104  } else { // GRF version 8 or higher.
105  if (callback == 0x400) return;
106  if (callback == 0x401) {
107  suffix.display = CSD_CARGO;
108  return;
109  }
110  if (callback < 0x400) {
112  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text));
115  return;
116  }
117  if (callback >= 0x800 && callback < 0xC00) {
119  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback), lastof(suffix.text));
121  suffix.display = CSD_CARGO_TEXT;
122  return;
123  }
125  return;
126  }
127  }
128 }
129 
130 enum CargoSuffixInOut {
131  CARGOSUFFIX_OUT = 0,
132  CARGOSUFFIX_IN = 1,
133 };
134 
145 template <typename TC, typename TS>
146 static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
147 {
148  assert_compile(lengthof(cargoes) <= lengthof(suffixes));
149 
151  /* Reworked behaviour with new many-in-many-out scheme */
152  for (uint j = 0; j < lengthof(suffixes); j++) {
153  if (cargoes[j] != CT_INVALID) {
154  byte local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
155  uint cargotype = local_id << 16 | use_input;
156  GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
157  } else {
158  suffixes[j].text[0] = '\0';
159  suffixes[j].display = CSD_CARGO;
160  }
161  }
162  } else {
163  /* Compatible behaviour with old 3-in-2-out scheme */
164  for (uint j = 0; j < lengthof(suffixes); j++) {
165  suffixes[j].text[0] = '\0';
166  suffixes[j].display = CSD_CARGO;
167  }
168  switch (use_input) {
169  case CARGOSUFFIX_OUT:
170  if (cargoes[0] != CT_INVALID) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
171  if (cargoes[1] != CT_INVALID) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
172  break;
173  case CARGOSUFFIX_IN:
174  if (cargoes[0] != CT_INVALID) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
175  if (cargoes[1] != CT_INVALID) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
176  if (cargoes[2] != CT_INVALID) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
177  break;
178  default:
179  NOT_REACHED();
180  }
181  }
182 }
183 
184 std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types;
185 
187 static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
188 {
189  static char industry_name[2][64];
190 
191  const IndustrySpec *indsp1 = GetIndustrySpec(a);
192  GetString(industry_name[0], indsp1->name, lastof(industry_name[0]));
193 
194  const IndustrySpec *indsp2 = GetIndustrySpec(b);
195  GetString(industry_name[1], indsp2->name, lastof(industry_name[1]));
196 
197  int r = strnatcmp(industry_name[0], industry_name[1]); // Sort by name (natural sorting).
198 
199  /* If the names are equal, sort by industry type. */
200  return (r != 0) ? r < 0 : (a < b);
201 }
202 
207 {
208  /* Add each industry type to the list. */
209  for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
210  _sorted_industry_types[i] = i;
211  }
212 
213  /* Sort industry types by name. */
215 }
216 
225 void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
226 {
227  if (result.Succeeded()) return;
228 
229  uint8 indtype = GB(p1, 0, 8);
230  if (indtype < NUM_INDUSTRYTYPES) {
231  const IndustrySpec *indsp = GetIndustrySpec(indtype);
232  if (indsp->enabled) {
233  SetDParam(0, indsp->name);
234  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
235  }
236  }
237 }
238 
239 static const NWidgetPart _nested_build_industry_widgets[] = {
241  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
242  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
243  NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
244  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
245  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
246  EndContainer(),
248  NWidget(WWT_MATRIX, COLOUR_DARK_GREEN, WID_DPI_MATRIX_WIDGET), SetMatrixDataTip(1, 0, STR_FUND_INDUSTRY_SELECTION_TOOLTIP), SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_DPI_SCROLLBAR),
249  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
250  EndContainer(),
251  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
252  EndContainer(),
254  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
255  SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
256  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
257  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
258  EndContainer(),
259 };
260 
263  WDP_AUTO, "build_industry", 170, 212,
266  _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets)
267 );
268 
270 class BuildIndustryWindow : public Window {
272  IndustryType selected_type;
273  uint16 callback_timer;
275  uint16 count;
276  IndustryType index[NUM_INDUSTRYTYPES + 1];
277  bool enabled[NUM_INDUSTRYTYPES + 1];
278  Scrollbar *vscroll;
279 
281  static const int MATRIX_TEXT_OFFSET = 17;
283  static const int MAX_MINWIDTH_LINEHEIGHTS = 20;
284 
285  void SetupArrays()
286  {
287  this->count = 0;
288 
289  for (uint i = 0; i < lengthof(this->index); i++) {
290  this->index[i] = INVALID_INDUSTRYTYPE;
291  this->enabled[i] = false;
292  }
293 
294  if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
295  this->index[this->count] = INVALID_INDUSTRYTYPE;
296  this->enabled[this->count] = true;
297  this->count++;
298  this->timer_enabled = false;
299  }
300  /* Fill the arrays with industries.
301  * The tests performed after the enabled allow to load the industries
302  * In the same way they are inserted by grf (if any)
303  */
304  for (IndustryType ind : _sorted_industry_types) {
305  const IndustrySpec *indsp = GetIndustrySpec(ind);
306  if (indsp->enabled) {
307  /* Rule is that editor mode loads all industries.
308  * In game mode, all non raw industries are loaded too
309  * and raw ones are loaded only when setting allows it */
310  if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
311  /* Unselect if the industry is no longer in the list */
312  if (this->selected_type == ind) this->selected_index = -1;
313  continue;
314  }
315  this->index[this->count] = ind;
316  this->enabled[this->count] = (_game_mode == GM_EDITOR) || GetIndustryProbabilityCallback(ind, IACT_USERCREATION, 1) > 0;
317  /* Keep the selection to the correct line */
318  if (this->selected_type == ind) this->selected_index = this->count;
319  this->count++;
320  }
321  }
322 
323  /* first industry type is selected if the current selection is invalid.
324  * I'll be damned if there are none available ;) */
325  if (this->selected_index == -1) {
326  this->selected_index = 0;
327  this->selected_type = this->index[0];
328  }
329 
330  this->vscroll->SetCount(this->count);
331  }
332 
334  void SetButtons()
335  {
336  this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != INVALID_INDUSTRYTYPE && !this->enabled[this->selected_index]);
337  this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == INVALID_INDUSTRYTYPE && this->enabled[this->selected_index]);
338  }
339 
352  std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const
353  {
354  std::string cargostring;
355  char buf[1024];
356  int numcargo = 0;
357  int firstcargo = -1;
358 
359  for (byte j = 0; j < cargolistlen; j++) {
360  if (cargolist[j] == CT_INVALID) continue;
361  numcargo++;
362  if (firstcargo < 0) {
363  firstcargo = j;
364  continue;
365  }
366  SetDParam(0, CargoSpec::Get(cargolist[j])->name);
367  SetDParamStr(1, cargo_suffix[j].text);
368  GetString(buf, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION, lastof(buf));
369  cargostring += buf;
370  }
371 
372  if (numcargo > 0) {
373  SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name);
374  SetDParamStr(1, cargo_suffix[firstcargo].text);
375  GetString(buf, prefixstr, lastof(buf));
376  cargostring = std::string(buf) + cargostring;
377  } else {
378  SetDParam(0, STR_JUST_NOTHING);
379  SetDParamStr(1, "");
380  GetString(buf, prefixstr, lastof(buf));
381  cargostring = std::string(buf);
382  }
383 
384  return cargostring;
385  }
386 
387 public:
388  BuildIndustryWindow() : Window(&_build_industry_desc)
389  {
390  this->timer_enabled = _loaded_newgrf_features.has_newindustries;
391 
392  this->selected_index = -1;
393  this->selected_type = INVALID_INDUSTRYTYPE;
394 
395  this->callback_timer = DAY_TICKS;
396 
397  this->CreateNestedTree();
398  this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
399  this->FinishInitNested(0);
400 
401  this->SetButtons();
402  }
403 
404  void OnInit() override
405  {
406  this->SetupArrays();
407  }
408 
409  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
410  {
411  switch (widget) {
412  case WID_DPI_MATRIX_WIDGET: {
413  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES);
414  for (byte i = 0; i < this->count; i++) {
415  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
416  d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(this->index[i])->name));
417  }
419  d.width += MATRIX_TEXT_OFFSET + padding.width;
420  d.height = 5 * resize->height;
421  *size = maxdim(*size, d);
422  break;
423  }
424 
425  case WID_DPI_INFOPANEL: {
426  /* Extra line for cost outside of editor + extra lines for 'extra' information for NewGRFs. */
427  int height = 2 + (_game_mode == GM_EDITOR ? 0 : 1) + (_loaded_newgrf_features.has_newindustries ? 4 : 0);
428  uint extra_lines_req = 0;
429  uint extra_lines_prd = 0;
430  uint max_minwidth = FONT_HEIGHT_NORMAL * MAX_MINWIDTH_LINEHEIGHTS;
431  Dimension d = {0, 0};
432  for (byte i = 0; i < this->count; i++) {
433  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
434 
435  const IndustrySpec *indsp = GetIndustrySpec(this->index[i]);
436  CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
437 
438  /* Measure the accepted cargoes, if any. */
439  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, this->index[i], indsp, indsp->accepts_cargo, cargo_suffix);
440  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, lengthof(indsp->accepts_cargo), STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
441  Dimension strdim = GetStringBoundingBox(cargostring.c_str());
442  if (strdim.width > max_minwidth) {
443  extra_lines_req = max(extra_lines_req, strdim.width / max_minwidth + 1);
444  strdim.width = max_minwidth;
445  }
446  d = maxdim(d, strdim);
447 
448  /* Measure the produced cargoes, if any. */
449  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, this->index[i], indsp, indsp->produced_cargo, cargo_suffix);
450  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, lengthof(indsp->produced_cargo), STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
451  strdim = GetStringBoundingBox(cargostring.c_str());
452  if (strdim.width > max_minwidth) {
453  extra_lines_prd = max(extra_lines_prd, strdim.width / max_minwidth + 1);
454  strdim.width = max_minwidth;
455  }
456  d = maxdim(d, strdim);
457  }
458 
459  /* Set it to something more sane :) */
460  height += extra_lines_prd + extra_lines_req;
461  size->height = height * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
462  size->width = d.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
463  break;
464  }
465 
466  case WID_DPI_FUND_WIDGET: {
467  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
468  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
469  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
470  d.width += padding.width;
471  d.height += padding.height;
472  *size = maxdim(*size, d);
473  break;
474  }
475  }
476  }
477 
478  void SetStringParameters(int widget) const override
479  {
480  switch (widget) {
481  case WID_DPI_FUND_WIDGET:
482  /* Raw industries might be prospected. Show this fact by changing the string
483  * In Editor, you just build, while ingame, or you fund or you prospect */
484  if (_game_mode == GM_EDITOR) {
485  /* We've chosen many random industries but no industries have been specified */
486  SetDParam(0, STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
487  } else {
488  const IndustrySpec *indsp = GetIndustrySpec(this->index[this->selected_index]);
489  SetDParam(0, (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
490  }
491  break;
492  }
493  }
494 
495  void DrawWidget(const Rect &r, int widget) const override
496  {
497  switch (widget) {
498  case WID_DPI_MATRIX_WIDGET: {
499  uint text_left, text_right, icon_left, icon_right;
500  if (_current_text_dir == TD_RTL) {
501  icon_right = r.right - WD_MATRIX_RIGHT;
502  icon_left = icon_right - 10;
503  text_right = icon_right - BuildIndustryWindow::MATRIX_TEXT_OFFSET;
504  text_left = r.left + WD_MATRIX_LEFT;
505  } else {
506  icon_left = r.left + WD_MATRIX_LEFT;
507  icon_right = icon_left + 10;
508  text_left = icon_left + BuildIndustryWindow::MATRIX_TEXT_OFFSET;
509  text_right = r.right - WD_MATRIX_RIGHT;
510  }
511 
512  for (byte i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->count; i++) {
513  int y = r.top + WD_MATRIX_TOP + i * this->resize.step_height;
514  bool selected = this->selected_index == i + this->vscroll->GetPosition();
515 
516  if (this->index[i + this->vscroll->GetPosition()] == INVALID_INDUSTRYTYPE) {
517  DrawString(text_left, text_right, y, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE);
518  continue;
519  }
520  const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll->GetPosition()]);
521 
522  /* Draw the name of the industry in white is selected, otherwise, in orange */
523  DrawString(text_left, text_right, y, indsp->name, selected ? TC_WHITE : TC_ORANGE);
524  GfxFillRect(icon_left, y + 1, icon_right, y + 7, selected ? PC_WHITE : PC_BLACK);
525  GfxFillRect(icon_left + 1, y + 2, icon_right - 1, y + 6, indsp->map_colour);
526  }
527  break;
528  }
529 
530  case WID_DPI_INFOPANEL: {
531  int y = r.top + WD_FRAMERECT_TOP;
532  int bottom = r.bottom - WD_FRAMERECT_BOTTOM;
533  int left = r.left + WD_FRAMERECT_LEFT;
534  int right = r.right - WD_FRAMERECT_RIGHT;
535 
536  if (this->selected_type == INVALID_INDUSTRYTYPE) {
537  DrawStringMultiLine(left, right, y, bottom, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
538  break;
539  }
540 
541  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
542 
543  if (_game_mode != GM_EDITOR) {
544  SetDParam(0, indsp->GetConstructionCost());
545  DrawString(left, right, y, STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST);
546  y += FONT_HEIGHT_NORMAL;
547  }
548 
549  CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
550 
551  /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
552  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
553  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, lengthof(indsp->accepts_cargo), STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
554  y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str());
555 
556  /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
557  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
558  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, lengthof(indsp->produced_cargo), STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
559  y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str());
560 
561  /* Get the additional purchase info text, if it has not already been queried. */
563  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, nullptr, this->selected_type, INVALID_TILE);
564  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
565  if (callback_res > 0x400) {
567  } else {
568  StringID str = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res); // No. here's the new string
569  if (str != STR_UNDEFINED) {
571  DrawStringMultiLine(left, right, y, bottom, str, TC_YELLOW);
573  }
574  }
575  }
576  }
577  break;
578  }
579  }
580  }
581 
582  void OnClick(Point pt, int widget, int click_count) override
583  {
584  switch (widget) {
585  case WID_DPI_MATRIX_WIDGET: {
586  int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_DPI_MATRIX_WIDGET);
587  if (y < this->count) { // Is it within the boundaries of available data?
588  this->selected_index = y;
589  this->selected_type = this->index[y];
590  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? nullptr : GetIndustrySpec(this->selected_type);
591 
592  this->SetDirty();
593 
594  if (_thd.GetCallbackWnd() == this &&
595  ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != nullptr && indsp->IsRawIndustry()) ||
596  this->selected_type == INVALID_INDUSTRYTYPE ||
597  !this->enabled[this->selected_index])) {
598  /* Reset the button state if going to prospecting or "build many industries" */
599  this->RaiseButtons();
601  }
602 
603  this->SetButtons();
604  if (this->enabled[this->selected_index] && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
605  }
606  break;
607  }
608 
610  if (this->selected_type != INVALID_INDUSTRYTYPE) ShowIndustryCargoesWindow(this->selected_type);
611  break;
612 
613  case WID_DPI_FUND_WIDGET: {
614  if (this->selected_type == INVALID_INDUSTRYTYPE) {
615  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
616 
617  if (Town::GetNumItems() == 0) {
618  ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO);
619  } else {
620  extern void GenerateIndustries();
621  _generating_world = true;
623  _generating_world = false;
624  }
625  } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
626  DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
627  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
628  } else {
629  HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
630  }
631  break;
632  }
633  }
634  }
635 
636  void OnResize() override
637  {
638  /* Adjust the number of items in the matrix depending of the resize */
639  this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
640  }
641 
642  void OnPlaceObject(Point pt, TileIndex tile) override
643  {
644  bool success = true;
645  /* We do not need to protect ourselves against "Random Many Industries" in this mode */
646  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
647  uint32 seed = InteractiveRandom();
648  uint32 layout_index = InteractiveRandomRange((uint32)indsp->layouts.size());
649 
650  if (_game_mode == GM_EDITOR) {
651  /* Show error if no town exists at all */
652  if (Town::GetNumItems() == 0) {
653  SetDParam(0, indsp->name);
654  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
655  return;
656  }
657 
658  Backup<CompanyID> cur_company(_current_company, OWNER_NONE, FILE_LINE);
659  _generating_world = true;
660  _ignore_restrictions = true;
661 
662  DoCommandP(tile, (layout_index << 8) | this->selected_type, seed,
663  CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY), &CcBuildIndustry);
664 
665  cur_company.Restore();
666  _ignore_restrictions = false;
667  _generating_world = false;
668  } else {
669  success = DoCommandP(tile, (layout_index << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
670  }
671 
672  /* If an industry has been built, just reset the cursor and the system */
674  }
675 
676  void OnGameTick() override
677  {
678  if (!this->timer_enabled) return;
679  if (--this->callback_timer == 0) {
680  /* We have just passed another day.
681  * See if we need to update availability of currently selected industry */
682  this->callback_timer = DAY_TICKS; // restart counter
683 
684  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
685 
686  if (indsp->enabled) {
687  bool call_back_result = GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0;
688 
689  /* Only if result does match the previous state would it require a redraw. */
690  if (call_back_result != this->enabled[this->selected_index]) {
691  this->enabled[this->selected_index] = call_back_result;
692  this->SetButtons();
693  this->SetDirty();
694  }
695  }
696  }
697  }
698 
699  void OnTimeout() override
700  {
701  this->RaiseButtons();
702  }
703 
704  void OnPlaceObjectAbort() override
705  {
706  this->RaiseButtons();
707  }
708 
714  void OnInvalidateData(int data = 0, bool gui_scope = true) override
715  {
716  if (!gui_scope) return;
717  this->SetupArrays();
718 
719  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? nullptr : GetIndustrySpec(this->selected_type);
720  if (indsp == nullptr) this->enabled[this->selected_index] = _settings_game.difficulty.industry_density != ID_FUND_ONLY;
721  this->SetButtons();
722  }
723 };
724 
725 void ShowBuildIndustryWindow()
726 {
727  if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
729  new BuildIndustryWindow();
730 }
731 
732 static void UpdateIndustryProduction(Industry *i);
733 
734 static inline bool IsProductionAlterable(const Industry *i)
735 {
736  const IndustrySpec *is = GetIndustrySpec(i->type);
737  bool has_prod = false;
738  for (size_t j = 0; j < lengthof(is->production_rate); j++) {
739  if (is->production_rate[j] != 0) {
740  has_prod = true;
741  break;
742  }
743  }
744  return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
745  (has_prod || is->IsRawIndustry()) &&
746  !_networking);
747 }
748 
750 {
752  enum Editability {
756  };
757 
759  enum InfoLine {
764  };
765 
772 
773 public:
774  IndustryViewWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
775  {
776  this->flags |= WF_DISABLE_VP_SCROLL;
777  this->editbox_line = IL_NONE;
778  this->clicked_line = IL_NONE;
779  this->clicked_button = 0;
780  this->info_height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + 1; // Info panel has at least two lines text.
781 
782  this->InitNested(window_number);
783  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
784  nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ZOOM_LVL_INDUSTRY);
785 
786  this->InvalidateData();
787  }
788 
789  void OnPaint() override
790  {
791  this->DrawWidgets();
792 
793  if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
794 
795  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_IV_INFO);
796  uint expected = this->DrawInfo(nwi->pos_x, nwi->pos_x + nwi->current_x - 1, nwi->pos_y) - nwi->pos_y;
797  if (expected > nwi->current_y - 1) {
798  this->info_height = expected + 1;
799  this->ReInit();
800  return;
801  }
802  }
803 
811  int DrawInfo(uint left, uint right, uint top)
812  {
813  Industry *i = Industry::Get(this->window_number);
814  const IndustrySpec *ind = GetIndustrySpec(i->type);
815  int y = top + WD_FRAMERECT_TOP;
816  bool first = true;
817  bool has_accept = false;
818 
819  if (i->prod_level == PRODLEVEL_CLOSURE) {
820  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
821  y += 2 * FONT_HEIGHT_NORMAL;
822  }
823 
824  CargoSuffix cargo_suffix[lengthof(i->accepts_cargo)];
825  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
827 
828  uint left_side = left + WD_FRAMERECT_LEFT * 4; // Indent accepted cargoes.
829  for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
830  if (i->accepts_cargo[j] == CT_INVALID) continue;
831  has_accept = true;
832  if (first) {
833  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_REQUIRES);
834  y += FONT_HEIGHT_NORMAL;
835  first = false;
836  }
838  SetDParam(1, i->accepts_cargo[j]);
840  SetDParamStr(3, "");
841  StringID str = STR_NULL;
842  switch (cargo_suffix[j].display) {
844  SetDParamStr(3, cargo_suffix[j].text);
845  FALLTHROUGH;
846  case CSD_CARGO_AMOUNT:
847  str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
848  break;
849 
850  case CSD_CARGO_TEXT:
851  SetDParamStr(3, cargo_suffix[j].text);
852  FALLTHROUGH;
853  case CSD_CARGO:
854  str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
855  break;
856 
857  default:
858  NOT_REACHED();
859  }
860  DrawString(left_side, right - WD_FRAMERECT_RIGHT, y, str);
861  y += FONT_HEIGHT_NORMAL;
862  }
863 
864  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
865  first = true;
866  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
867  if (i->produced_cargo[j] == CT_INVALID) continue;
868  if (first) {
869  if (has_accept) y += WD_PAR_VSEP_WIDE;
870  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
871  y += FONT_HEIGHT_NORMAL;
872  if (this->editable == EA_RATE) this->production_offset_y = y;
873  first = false;
874  }
875 
876  SetDParam(0, i->produced_cargo[j]);
878  SetDParamStr(2, cargo_suffix[j].text);
880  uint x = left + WD_FRAMETEXT_LEFT + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + 10 : 0);
881  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_TRANSPORTED);
882  /* Let's put out those buttons.. */
883  if (this->editable == EA_RATE) {
884  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
885  i->production_rate[j] > 0, i->production_rate[j] < 255);
886  }
887  y += FONT_HEIGHT_NORMAL;
888  }
889 
890  /* Display production multiplier if editable */
891  if (this->editable == EA_MULTIPLIER) {
892  y += WD_PAR_VSEP_WIDE;
893  this->production_offset_y = y;
895  uint x = left + WD_FRAMETEXT_LEFT + SETTING_BUTTON_WIDTH + 10;
896  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LEVEL);
897  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
899  y += FONT_HEIGHT_NORMAL;
900  }
901 
902  /* Get the extra message for the GUI */
904  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
905  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
906  if (callback_res > 0x400) {
908  } else {
909  StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
910  if (message != STR_NULL && message != STR_UNDEFINED) {
911  y += WD_PAR_VSEP_WIDE;
912 
914  /* Use all the available space left from where we stand up to the
915  * end of the window. We ALSO enlarge the window if needed, so we
916  * can 'go' wild with the bottom of the window. */
917  y = DrawStringMultiLine(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, UINT16_MAX, message, TC_BLACK);
919  }
920  }
921  }
922  }
923  return y + WD_FRAMERECT_BOTTOM;
924  }
925 
926  void SetStringParameters(int widget) const override
927  {
928  if (widget == WID_IV_CAPTION) SetDParam(0, this->window_number);
929  }
930 
931  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
932  {
933  if (widget == WID_IV_INFO) size->height = this->info_height;
934  }
935 
936  void OnClick(Point pt, int widget, int click_count) override
937  {
938  switch (widget) {
939  case WID_IV_INFO: {
940  Industry *i = Industry::Get(this->window_number);
941  InfoLine line = IL_NONE;
942 
943  switch (this->editable) {
944  case EA_NONE: break;
945 
946  case EA_MULTIPLIER:
947  if (IsInsideBS(pt.y, this->production_offset_y, FONT_HEIGHT_NORMAL)) line = IL_MULTIPLIER;
948  break;
949 
950  case EA_RATE:
951  if (pt.y >= this->production_offset_y) {
952  int row = (pt.y - this->production_offset_y) / FONT_HEIGHT_NORMAL;
953  for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
954  if (i->produced_cargo[j] == CT_INVALID) continue;
955  row--;
956  if (row < 0) {
957  line = (InfoLine)(IL_RATE1 + j);
958  break;
959  }
960  }
961  }
962  break;
963  }
964  if (line == IL_NONE) return;
965 
966  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(widget);
967  int left = nwi->pos_x + WD_FRAMETEXT_LEFT;
968  int right = nwi->pos_x + nwi->current_x - 1 - WD_FRAMERECT_RIGHT;
969  if (IsInsideMM(pt.x, left, left + SETTING_BUTTON_WIDTH)) {
970  /* Clicked buttons, decrease or increase production */
971  byte button = (pt.x < left + SETTING_BUTTON_WIDTH / 2) ? 1 : 2;
972  switch (this->editable) {
973  case EA_MULTIPLIER:
974  if (button == 1) {
975  if (i->prod_level <= PRODLEVEL_MINIMUM) return;
976  i->prod_level = max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM);
977  } else {
978  if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
980  }
981  break;
982 
983  case EA_RATE:
984  if (button == 1) {
985  if (i->production_rate[line - IL_RATE1] <= 0) return;
986  i->production_rate[line - IL_RATE1] = max(i->production_rate[line - IL_RATE1] / 2, 0);
987  } else {
988  if (i->production_rate[line - IL_RATE1] >= 255) return;
989  /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
990  int new_prod = i->production_rate[line - IL_RATE1] == 0 ? 1 : i->production_rate[line - IL_RATE1] * 2;
991  i->production_rate[line - IL_RATE1] = minu(new_prod, 255);
992  }
993  break;
994 
995  default: NOT_REACHED();
996  }
997 
998  UpdateIndustryProduction(i);
999  this->SetDirty();
1000  this->SetTimeout();
1001  this->clicked_line = line;
1002  this->clicked_button = button;
1003  } else if (IsInsideMM(pt.x, left + SETTING_BUTTON_WIDTH + 10, right)) {
1004  /* clicked the text */
1005  this->editbox_line = line;
1006  switch (this->editable) {
1007  case EA_MULTIPLIER:
1009  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1010  break;
1011 
1012  case EA_RATE:
1013  SetDParam(0, i->production_rate[line - IL_RATE1] * 8);
1014  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1015  break;
1016 
1017  default: NOT_REACHED();
1018  }
1019  }
1020  break;
1021  }
1022 
1023  case WID_IV_GOTO: {
1024  Industry *i = Industry::Get(this->window_number);
1025  if (_ctrl_pressed) {
1027  } else {
1029  }
1030  break;
1031  }
1032 
1033  case WID_IV_DISPLAY: {
1034  Industry *i = Industry::Get(this->window_number);
1036  break;
1037  }
1038  }
1039  }
1040 
1041  void OnTimeout() override
1042  {
1043  this->clicked_line = IL_NONE;
1044  this->clicked_button = 0;
1045  this->SetDirty();
1046  }
1047 
1048  void OnResize() override
1049  {
1050  if (this->viewport != nullptr) {
1051  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
1052  nvp->UpdateViewportCoordinates(this);
1053 
1054  ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
1055  }
1056  }
1057 
1058  void OnQueryTextFinished(char *str) override
1059  {
1060  if (StrEmpty(str)) return;
1061 
1062  Industry *i = Industry::Get(this->window_number);
1063  uint value = atoi(str);
1064  switch (this->editbox_line) {
1065  case IL_NONE: NOT_REACHED();
1066 
1067  case IL_MULTIPLIER:
1069  break;
1070 
1071  default:
1072  i->production_rate[this->editbox_line - IL_RATE1] = ClampU(RoundDivSU(value, 8), 0, 255);
1073  break;
1074  }
1075  UpdateIndustryProduction(i);
1076  this->SetDirty();
1077  }
1078 
1084  void OnInvalidateData(int data = 0, bool gui_scope = true) override
1085  {
1086  if (!gui_scope) return;
1087  const Industry *i = Industry::Get(this->window_number);
1088  if (IsProductionAlterable(i)) {
1089  const IndustrySpec *ind = GetIndustrySpec(i->type);
1090  this->editable = ind->UsesSmoothEconomy() ? EA_RATE : EA_MULTIPLIER;
1091  } else {
1092  this->editable = EA_NONE;
1093  }
1094  }
1095 
1096  bool IsNewGRFInspectable() const override
1097  {
1098  return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
1099  }
1100 
1101  void ShowNewGRFInspectWindow() const override
1102  {
1103  ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
1104  }
1105 };
1106 
1107 static void UpdateIndustryProduction(Industry *i)
1108 {
1109  const IndustrySpec *indspec = GetIndustrySpec(i->type);
1110  if (!indspec->UsesSmoothEconomy()) i->RecomputeProductionMultipliers();
1111 
1112  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1113  if (i->produced_cargo[j] != CT_INVALID) {
1114  i->last_month_production[j] = 8 * i->production_rate[j];
1115  }
1116  }
1117 }
1118 
1122  NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
1123  NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1124  NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
1125  NWidget(WWT_SHADEBOX, COLOUR_CREAM),
1126  NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
1127  NWidget(WWT_STICKYBOX, COLOUR_CREAM),
1128  EndContainer(),
1129  NWidget(WWT_PANEL, COLOUR_CREAM),
1130  NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
1131  NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetPadding(1, 1, 1, 1), SetResize(1, 1),
1132  EndContainer(),
1133  EndContainer(),
1134  NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 2), SetResize(1, 0),
1135  EndContainer(),
1137  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GOTO), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1138  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1139  NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
1140  EndContainer(),
1141 };
1142 
1145  WDP_AUTO, "view_industry", 260, 120,
1147  0,
1148  _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets)
1149 );
1150 
1151 void ShowIndustryViewWindow(int industry)
1152 {
1153  AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
1154 }
1155 
1159  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1160  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_INDUSTRY_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1161  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1162  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1163  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1164  EndContainer(),
1168  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1169  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
1170  NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
1171  EndContainer(),
1172  NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_SCROLLBAR), EndContainer(),
1173  EndContainer(),
1175  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_ID_SCROLLBAR),
1176  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1177  EndContainer(),
1178  EndContainer(),
1179 };
1180 
1182 
1183 
1188 protected:
1189  /* Runtime saved values */
1190  static Listing last_sorting;
1191  static const Industry *last_industry;
1192 
1193  /* Constants for sorting stations */
1194  static const StringID sorter_names[];
1195  static GUIIndustryList::SortFunction * const sorter_funcs[];
1196 
1197  GUIIndustryList industries;
1198  Scrollbar *vscroll;
1199 
1202  {
1203  if (this->industries.NeedRebuild()) {
1204  this->industries.clear();
1205 
1206  for (const Industry *i : Industry::Iterate()) {
1207  this->industries.push_back(i);
1208  }
1209 
1210  this->industries.shrink_to_fit();
1211  this->industries.RebuildDone();
1212  this->vscroll->SetCount((uint)this->industries.size()); // Update scrollbar as well.
1213  }
1214 
1215  if (!this->industries.Sort()) return;
1216  IndustryDirectoryWindow::last_industry = nullptr; // Reset name sorter sort cache
1217  this->SetWidgetDirty(WID_ID_INDUSTRY_LIST); // Set the modified widget dirty
1218  }
1219 
1227  static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
1228  {
1229  assert(id < lengthof(i->produced_cargo));
1230 
1231  if (i->produced_cargo[id] == CT_INVALID) return 101;
1232  return ToPercent8(i->last_month_pct_transported[id]);
1233  }
1234 
1243  {
1244  int p1 = GetCargoTransportedPercentsIfValid(i, 0);
1245  int p2 = GetCargoTransportedPercentsIfValid(i, 1);
1246 
1247  if (p1 > p2) Swap(p1, p2); // lower value has higher priority
1248 
1249  return (p1 << 8) + p2;
1250  }
1251 
1253  static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b)
1254  {
1255  static char buf_cache[96];
1256  static char buf[96];
1257 
1258  SetDParam(0, a->index);
1259  GetString(buf, STR_INDUSTRY_NAME, lastof(buf));
1260 
1261  if (b != last_industry) {
1262  last_industry = b;
1263  SetDParam(0, b->index);
1264  GetString(buf_cache, STR_INDUSTRY_NAME, lastof(buf_cache));
1265  }
1266 
1267  return strnatcmp(buf, buf_cache) < 0; // Sort by name (natural sorting).
1268  }
1269 
1271  static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b)
1272  {
1273  int it_a = 0;
1274  while (it_a != NUM_INDUSTRYTYPES && a->type != _sorted_industry_types[it_a]) it_a++;
1275  int it_b = 0;
1276  while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
1277  int r = it_a - it_b;
1278  return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
1279  }
1280 
1282  static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b)
1283  {
1284  uint prod_a = 0, prod_b = 0;
1285  for (uint i = 0; i < lengthof(a->produced_cargo); i++) {
1286  if (a->produced_cargo[i] != CT_INVALID) prod_a += a->last_month_production[i];
1287  if (b->produced_cargo[i] != CT_INVALID) prod_b += b->last_month_production[i];
1288  }
1289  int r = prod_a - prod_b;
1290 
1291  return (r == 0) ? IndustryTypeSorter(a, b) : r < 0;
1292  }
1293 
1295  static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b)
1296  {
1297  int r = GetCargoTransportedSortValue(a) - GetCargoTransportedSortValue(b);
1298  return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
1299  }
1300 
1307  {
1308  const IndustrySpec *indsp = GetIndustrySpec(i->type);
1309  byte p = 0;
1310 
1311  /* Industry name */
1312  SetDParam(p++, i->index);
1313 
1314  static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
1315  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
1316 
1317  /* Get industry productions (CargoID, production, suffix, transported) */
1318  typedef std::tuple<CargoID, uint16, const char*, uint> CargoInfo;
1319  std::vector<CargoInfo> cargos;
1320 
1321  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1322  if (i->produced_cargo[j] == CT_INVALID) continue;
1323  cargos.emplace_back(i->produced_cargo[j], i->last_month_production[j], cargo_suffix[j].text, ToPercent8(i->last_month_pct_transported[j]));
1324  }
1325 
1326  /* Sort by descending production, then descending transported */
1327  std::sort(cargos.begin(), cargos.end(), [](const CargoInfo a, const CargoInfo b) {
1328  if (std::get<1>(a) != std::get<1>(b)) return std::get<1>(a) > std::get<1>(b);
1329  return std::get<3>(a) > std::get<3>(b);
1330  });
1331 
1332  /* Display first 3 cargos */
1333  for (size_t j = 0; j < min<size_t>(3, cargos.size()); j++) {
1334  CargoInfo ci = cargos[j];
1335  SetDParam(p++, STR_INDUSTRY_DIRECTORY_ITEM_INFO);
1336  SetDParam(p++, std::get<0>(ci));
1337  SetDParam(p++, std::get<1>(ci));
1338  SetDParamStr(p++, std::get<2>(ci));
1339  SetDParam(p++, std::get<3>(ci));
1340  }
1341 
1342  /* Undisplayed cargos if any */
1343  SetDParam(p++, cargos.size() - 3);
1344 
1345  /* Drawing the right string */
1346  switch (cargos.size()) {
1347  case 0: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1348  case 1: return STR_INDUSTRY_DIRECTORY_ITEM_PROD1;
1349  case 2: return STR_INDUSTRY_DIRECTORY_ITEM_PROD2;
1350  case 3: return STR_INDUSTRY_DIRECTORY_ITEM_PROD3;
1351  default: return STR_INDUSTRY_DIRECTORY_ITEM_PRODMORE;
1352  }
1353  }
1354 
1355 public:
1356  IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
1357  {
1358  this->CreateNestedTree();
1359  this->vscroll = this->GetScrollbar(WID_ID_SCROLLBAR);
1360 
1361  this->industries.SetListing(this->last_sorting);
1362  this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1363  this->industries.ForceRebuild();
1364  this->BuildSortIndustriesList();
1365 
1366  this->FinishInitNested(0);
1367  }
1368 
1370  {
1371  this->last_sorting = this->industries.GetListing();
1372  }
1373 
1374  void SetStringParameters(int widget) const override
1375  {
1376  if (widget == WID_ID_DROPDOWN_CRITERIA) SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1377  }
1378 
1379  void DrawWidget(const Rect &r, int widget) const override
1380  {
1381  switch (widget) {
1382  case WID_ID_DROPDOWN_ORDER:
1383  this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1384  break;
1385 
1386  case WID_ID_INDUSTRY_LIST: {
1387  int n = 0;
1388  int y = r.top + WD_FRAMERECT_TOP;
1389  if (this->industries.size() == 0) {
1390  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_DIRECTORY_NONE);
1391  break;
1392  }
1393  for (uint i = this->vscroll->GetPosition(); i < this->industries.size(); i++) {
1394  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, this->GetIndustryString(this->industries[i]));
1395 
1396  y += this->resize.step_height;
1397  if (++n == this->vscroll->GetCapacity()) break; // max number of industries in 1 window
1398  }
1399  break;
1400  }
1401  }
1402  }
1403 
1404  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
1405  {
1406  switch (widget) {
1407  case WID_ID_DROPDOWN_ORDER: {
1408  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1409  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1410  d.height += padding.height;
1411  *size = maxdim(*size, d);
1412  break;
1413  }
1414 
1415  case WID_ID_DROPDOWN_CRITERIA: {
1416  Dimension d = {0, 0};
1417  for (uint i = 0; IndustryDirectoryWindow::sorter_names[i] != INVALID_STRING_ID; i++) {
1418  d = maxdim(d, GetStringBoundingBox(IndustryDirectoryWindow::sorter_names[i]));
1419  }
1420  d.width += padding.width;
1421  d.height += padding.height;
1422  *size = maxdim(*size, d);
1423  break;
1424  }
1425 
1426  case WID_ID_INDUSTRY_LIST: {
1427  Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1428  for (uint i = 0; i < this->industries.size(); i++) {
1429  d = maxdim(d, GetStringBoundingBox(this->GetIndustryString(this->industries[i])));
1430  }
1431  resize->height = d.height;
1432  d.height *= 5;
1433  d.width += padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1434  d.height += padding.height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1435  *size = maxdim(*size, d);
1436  break;
1437  }
1438  }
1439  }
1440 
1441 
1442  void OnClick(Point pt, int widget, int click_count) override
1443  {
1444  switch (widget) {
1445  case WID_ID_DROPDOWN_ORDER:
1446  this->industries.ToggleSortOrder();
1447  this->SetDirty();
1448  break;
1449 
1451  ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1452  break;
1453 
1454  case WID_ID_INDUSTRY_LIST: {
1455  uint p = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_ID_INDUSTRY_LIST, WD_FRAMERECT_TOP);
1456  if (p < this->industries.size()) {
1457  if (_ctrl_pressed) {
1458  ShowExtraViewPortWindow(this->industries[p]->location.tile);
1459  } else {
1460  ScrollMainWindowToTile(this->industries[p]->location.tile);
1461  }
1462  }
1463  break;
1464  }
1465  }
1466  }
1467 
1468  void OnDropdownSelect(int widget, int index) override
1469  {
1470  if (this->industries.SortType() != index) {
1471  this->industries.SetSortType(index);
1472  this->BuildSortIndustriesList();
1473  }
1474  }
1475 
1476  void OnResize() override
1477  {
1478  this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST);
1479  }
1480 
1481  void OnPaint() override
1482  {
1483  if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1484  this->DrawWidgets();
1485  }
1486 
1487  void OnHundredthTick() override
1488  {
1489  this->industries.ForceResort();
1490  this->BuildSortIndustriesList();
1491  }
1492 
1498  void OnInvalidateData(int data = 0, bool gui_scope = true) override
1499  {
1500  if (data == 0) {
1501  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
1502  this->industries.ForceRebuild();
1503  } else {
1504  this->industries.ForceResort();
1505  }
1506  }
1507 };
1508 
1509 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1510 const Industry *IndustryDirectoryWindow::last_industry = nullptr;
1511 
1512 /* Available station sorting functions. */
1513 GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
1514  &IndustryNameSorter,
1515  &IndustryTypeSorter,
1516  &IndustryProductionSorter,
1517  &IndustryTransportedCargoSorter
1518 };
1519 
1520 /* Names of the sorting functions */
1521 const StringID IndustryDirectoryWindow::sorter_names[] = {
1522  STR_SORT_BY_NAME,
1523  STR_SORT_BY_TYPE,
1524  STR_SORT_BY_PRODUCTION,
1525  STR_SORT_BY_TRANSPORTED,
1527 };
1528 
1529 
1532  WDP_AUTO, "list_industries", 428, 190,
1534  0,
1535  _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets)
1536 );
1537 
1538 void ShowIndustryDirectory()
1539 {
1540  AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
1541 }
1542 
1546  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1547  NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION), SetDataTip(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1548  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1549  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1550  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1551  EndContainer(),
1556  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
1557  SetDataTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1558  NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
1559  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1560  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1561  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1562  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1563  EndContainer(),
1564  EndContainer(),
1566  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_IC_SCROLLBAR),
1567  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1568  EndContainer(),
1569  EndContainer(),
1570 };
1571 
1574  WDP_AUTO, "industry_cargoes", 300, 210,
1576  0,
1577  _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets)
1578 );
1579 
1588 };
1589 
1590 static const uint MAX_CARGOES = 16;
1591 
1594  static const int VERT_INTER_INDUSTRY_SPACE;
1595  static const int HOR_CARGO_BORDER_SPACE;
1596  static const int CARGO_STUB_WIDTH;
1597  static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE;
1598  static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE;
1599  static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT;
1600 
1601  static const int INDUSTRY_LINE_COLOUR;
1602  static const int CARGO_LINE_COLOUR;
1603 
1604  static int small_height, normal_height;
1605  static int cargo_field_width;
1606  static int industry_width;
1607  static uint max_cargoes;
1608 
1610  union {
1611  struct {
1612  IndustryType ind_type;
1613  CargoID other_produced[MAX_CARGOES];
1614  CargoID other_accepted[MAX_CARGOES];
1615  } industry;
1616  struct {
1617  CargoID vertical_cargoes[MAX_CARGOES];
1619  CargoID supp_cargoes[MAX_CARGOES];
1620  byte top_end;
1621  CargoID cust_cargoes[MAX_CARGOES];
1622  byte bottom_end;
1623  } cargo;
1624  struct {
1626  bool left_align;
1627  } cargo_label;
1629  } u; // Data for each type.
1630 
1636  {
1637  this->type = type;
1638  }
1639 
1645  void MakeIndustry(IndustryType ind_type)
1646  {
1647  this->type = CFT_INDUSTRY;
1648  this->u.industry.ind_type = ind_type;
1649  MemSetT(this->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
1650  MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
1651  }
1652 
1659  int ConnectCargo(CargoID cargo, bool producer)
1660  {
1661  assert(this->type == CFT_CARGO);
1662  if (cargo == INVALID_CARGO) return -1;
1663 
1664  /* Find the vertical cargo column carrying the cargo. */
1665  int column = -1;
1666  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1667  if (cargo == this->u.cargo.vertical_cargoes[i]) {
1668  column = i;
1669  break;
1670  }
1671  }
1672  if (column < 0) return -1;
1673 
1674  if (producer) {
1675  assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
1676  this->u.cargo.supp_cargoes[column] = column;
1677  } else {
1678  assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
1679  this->u.cargo.cust_cargoes[column] = column;
1680  }
1681  return column;
1682  }
1683 
1689  {
1690  assert(this->type == CFT_CARGO);
1691 
1692  for (uint i = 0; i < MAX_CARGOES; i++) {
1693  if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
1694  if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
1695  }
1696  return false;
1697  }
1698 
1708  void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
1709  {
1710  this->type = CFT_CARGO;
1711  uint i;
1712  uint num = 0;
1713  for (i = 0; i < MAX_CARGOES && i < length; i++) {
1714  if (cargoes[i] != INVALID_CARGO) {
1715  this->u.cargo.vertical_cargoes[num] = cargoes[i];
1716  num++;
1717  }
1718  }
1719  this->u.cargo.num_cargoes = (count < 0) ? num : count;
1720  for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
1721  this->u.cargo.top_end = top_end;
1722  this->u.cargo.bottom_end = bottom_end;
1723  MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
1724  MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
1725  }
1726 
1733  void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
1734  {
1735  this->type = CFT_CARGO_LABEL;
1736  uint i;
1737  for (i = 0; i < MAX_CARGOES && i < length; i++) this->u.cargo_label.cargoes[i] = cargoes[i];
1738  for (; i < MAX_CARGOES; i++) this->u.cargo_label.cargoes[i] = INVALID_CARGO;
1739  this->u.cargo_label.left_align = left_align;
1740  }
1741 
1746  void MakeHeader(StringID textid)
1747  {
1748  this->type = CFT_HEADER;
1749  this->u.header = textid;
1750  }
1751 
1757  int GetCargoBase(int xpos) const
1758  {
1759  assert(this->type == CFT_CARGO);
1760  int n = this->u.cargo.num_cargoes;
1761 
1762  if (n % 2 == 0) {
1763  return xpos + cargo_field_width / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE / 2) * (n / 2);
1764  } else {
1765  return xpos + cargo_field_width / 2 - HOR_CARGO_WIDTH / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE) * (n / 2);
1766  }
1767  }
1768 
1774  void Draw(int xpos, int ypos) const
1775  {
1776  switch (this->type) {
1777  case CFT_EMPTY:
1778  case CFT_SMALL_EMPTY:
1779  break;
1780 
1781  case CFT_HEADER:
1782  ypos += (small_height - FONT_HEIGHT_NORMAL) / 2;
1783  DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
1784  break;
1785 
1786  case CFT_INDUSTRY: {
1787  int ypos1 = ypos + VERT_INTER_INDUSTRY_SPACE / 2;
1788  int ypos2 = ypos + normal_height - 1 - VERT_INTER_INDUSTRY_SPACE / 2;
1789  int xpos2 = xpos + industry_width - 1;
1790  GfxDrawLine(xpos, ypos1, xpos2, ypos1, INDUSTRY_LINE_COLOUR);
1791  GfxDrawLine(xpos, ypos1, xpos, ypos2, INDUSTRY_LINE_COLOUR);
1792  GfxDrawLine(xpos, ypos2, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1793  GfxDrawLine(xpos2, ypos1, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1794  ypos += (normal_height - FONT_HEIGHT_NORMAL) / 2;
1795  if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
1796  const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
1797  DrawString(xpos, xpos2, ypos, indsp->name, TC_WHITE, SA_HOR_CENTER);
1798 
1799  /* Draw the industry legend. */
1800  int blob_left, blob_right;
1801  if (_current_text_dir == TD_RTL) {
1802  blob_right = xpos2 - BLOB_DISTANCE;
1803  blob_left = blob_right - BLOB_WIDTH;
1804  } else {
1805  blob_left = xpos + BLOB_DISTANCE;
1806  blob_right = blob_left + BLOB_WIDTH;
1807  }
1808  GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border
1809  GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour);
1810  } else {
1811  DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
1812  }
1813 
1814  /* Draw the other_produced/other_accepted cargoes. */
1815  const CargoID *other_right, *other_left;
1816  if (_current_text_dir == TD_RTL) {
1817  other_right = this->u.industry.other_accepted;
1818  other_left = this->u.industry.other_produced;
1819  } else {
1820  other_right = this->u.industry.other_produced;
1821  other_left = this->u.industry.other_accepted;
1822  }
1823  ypos1 += VERT_CARGO_EDGE;
1824  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
1825  if (other_right[i] != INVALID_CARGO) {
1826  const CargoSpec *csp = CargoSpec::Get(other_right[i]);
1827  int xp = xpos + industry_width + CARGO_STUB_WIDTH;
1828  DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
1829  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1830  }
1831  if (other_left[i] != INVALID_CARGO) {
1832  const CargoSpec *csp = CargoSpec::Get(other_left[i]);
1833  int xp = xpos - CARGO_STUB_WIDTH;
1834  DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
1835  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1836  }
1837  ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1838  }
1839  break;
1840  }
1841 
1842  case CFT_CARGO: {
1843  int cargo_base = this->GetCargoBase(xpos);
1844  int top = ypos + (this->u.cargo.top_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0);
1845  int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1;
1846  int colpos = cargo_base;
1847  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1848  if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR);
1849  if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR);
1850  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1851  colpos++;
1852  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
1853  GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
1854  colpos += HOR_CARGO_WIDTH - 2;
1855  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1856  colpos += 1 + HOR_CARGO_SPACE;
1857  }
1858 
1859  const CargoID *hor_left, *hor_right;
1860  if (_current_text_dir == TD_RTL) {
1861  hor_left = this->u.cargo.cust_cargoes;
1862  hor_right = this->u.cargo.supp_cargoes;
1863  } else {
1864  hor_left = this->u.cargo.supp_cargoes;
1865  hor_right = this->u.cargo.cust_cargoes;
1866  }
1867  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1868  for (uint i = 0; i < MAX_CARGOES; i++) {
1869  if (hor_left[i] != INVALID_CARGO) {
1870  int col = hor_left[i];
1871  int dx = 0;
1872  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1873  for (; col > 0; col--) {
1874  int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE;
1875  DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp);
1876  dx = 1;
1877  }
1878  DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
1879  }
1880  if (hor_right[i] != INVALID_CARGO) {
1881  int col = hor_right[i];
1882  int dx = 0;
1883  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1884  for (; col < this->u.cargo.num_cargoes - 1; col++) {
1885  int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE;
1886  DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp);
1887  dx = 1;
1888  }
1889  DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
1890  }
1891  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1892  }
1893  break;
1894  }
1895 
1896  case CFT_CARGO_LABEL:
1897  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1898  for (uint i = 0; i < MAX_CARGOES; i++) {
1899  if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
1900  const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
1901  DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE,
1902  (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
1903  }
1904  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1905  }
1906  break;
1907 
1908  default:
1909  NOT_REACHED();
1910  }
1911  }
1912 
1920  CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
1921  {
1922  assert(this->type == CFT_CARGO);
1923 
1924  /* Vertical matching. */
1925  int cpos = this->GetCargoBase(0);
1926  uint col;
1927  for (col = 0; col < this->u.cargo.num_cargoes; col++) {
1928  if (pt.x < cpos) break;
1929  if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col];
1931  }
1932  /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
1933 
1934  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1935  uint row;
1936  for (row = 0; row < MAX_CARGOES; row++) {
1937  if (pt.y < vpos) return INVALID_CARGO;
1938  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1939  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1940  }
1941  if (row == MAX_CARGOES) return INVALID_CARGO;
1942 
1943  /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
1944  if (col == 0) {
1945  if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
1946  if (left != nullptr) {
1947  if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
1948  if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
1949  }
1950  return INVALID_CARGO;
1951  }
1952  if (col == this->u.cargo.num_cargoes) {
1953  if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
1954  if (right != nullptr) {
1955  if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
1956  if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
1957  }
1958  return INVALID_CARGO;
1959  }
1960  if (row >= col) {
1961  /* Clicked somewhere in-between vertical cargo connection.
1962  * Since the horizontal connection is made in the same order as the vertical list, the above condition
1963  * ensures we are left-below the main diagonal, thus at the supplying side.
1964  */
1965  return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
1966  } else {
1967  /* Clicked at a customer connection. */
1968  return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
1969  }
1970  }
1971 
1978  {
1979  assert(this->type == CFT_CARGO_LABEL);
1980 
1981  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1982  uint row;
1983  for (row = 0; row < MAX_CARGOES; row++) {
1984  if (pt.y < vpos) return INVALID_CARGO;
1985  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1986  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1987  }
1988  if (row == MAX_CARGOES) return INVALID_CARGO;
1989  return this->u.cargo_label.cargoes[row];
1990  }
1991 
1992 private:
2000  static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
2001  {
2002  GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
2003  GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE);
2004  GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
2005  }
2006 };
2007 
2008 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo));
2009 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo));
2010 
2017 
2018 const int CargoesField::HOR_CARGO_BORDER_SPACE = 15;
2019 const int CargoesField::CARGO_STUB_WIDTH = 10;
2020 const int CargoesField::HOR_CARGO_WIDTH = 15;
2021 const int CargoesField::HOR_CARGO_SPACE = 5;
2022 const int CargoesField::VERT_CARGO_EDGE = 4;
2023 const int CargoesField::VERT_CARGO_SPACE = 4;
2024 
2025 const int CargoesField::BLOB_DISTANCE = 5;
2026 const int CargoesField::BLOB_WIDTH = 12;
2027 const int CargoesField::BLOB_HEIGHT = 9;
2028 
2031 
2033 struct CargoesRow {
2034  CargoesField columns[5];
2035 
2040  void ConnectIndustryProduced(int column)
2041  {
2042  CargoesField *ind_fld = this->columns + column;
2043  CargoesField *cargo_fld = this->columns + column + 1;
2044  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2045 
2046  MemSetT(ind_fld->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
2047 
2048  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2049  CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
2050  int other_count = 0;
2051 
2052  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2053  assert(CargoesField::max_cargoes <= lengthof(indsp->produced_cargo));
2054  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2055  int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
2056  if (col < 0) others[other_count++] = indsp->produced_cargo[i];
2057  }
2058 
2059  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2060  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2061  if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
2062  }
2063  } else {
2064  /* Houses only display what is demanded. */
2065  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2066  CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
2067  if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
2068  }
2069  }
2070  }
2071 
2077  void MakeCargoLabel(int column, bool accepting)
2078  {
2079  CargoID cargoes[MAX_CARGOES];
2080  MemSetT(cargoes, INVALID_CARGO, lengthof(cargoes));
2081 
2082  CargoesField *label_fld = this->columns + column;
2083  CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2084 
2085  assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
2086  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2087  int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2088  if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2089  }
2090  label_fld->MakeCargoLabel(cargoes, lengthof(cargoes), accepting);
2091  }
2092 
2093 
2098  void ConnectIndustryAccepted(int column)
2099  {
2100  CargoesField *ind_fld = this->columns + column;
2101  CargoesField *cargo_fld = this->columns + column - 1;
2102  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2103 
2104  MemSetT(ind_fld->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
2105 
2106  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2107  CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2108  int other_count = 0;
2109 
2110  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2112  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2113  int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2114  if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2115  }
2116 
2117  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2118  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2119  if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2120  }
2121  } else {
2122  /* Houses only display what is demanded. */
2123  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2124  for (uint h = 0; h < NUM_HOUSES; h++) {
2125  HouseSpec *hs = HouseSpec::Get(h);
2126  if (!hs->enabled) continue;
2127 
2128  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2129  if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
2130  cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2131  goto next_cargo;
2132  }
2133  }
2134  }
2135 next_cargo: ;
2136  }
2137  }
2138  }
2139 };
2140 
2141 
2170  static const int HOR_TEXT_PADDING, VERT_TEXT_PADDING;
2171 
2172  typedef std::vector<CargoesRow> Fields;
2173 
2174  Fields fields;
2175  uint ind_cargo;
2178  Scrollbar *vscroll;
2179 
2180  IndustryCargoesWindow(int id) : Window(&_industry_cargoes_desc)
2181  {
2182  this->OnInit();
2183  this->CreateNestedTree();
2184  this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2185  this->FinishInitNested(0);
2186  this->OnInvalidateData(id);
2187  }
2188 
2189  void OnInit() override
2190  {
2191  /* Initialize static CargoesField size variables. */
2192  Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2193  d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2195  d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
2196  CargoesField::small_height = d.height;
2197 
2198  /* Decide about the size of the box holding the text of an industry type. */
2199  this->ind_textsize.width = 0;
2200  this->ind_textsize.height = 0;
2202  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2203  const IndustrySpec *indsp = GetIndustrySpec(it);
2204  if (!indsp->enabled) continue;
2205  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
2207  CargoesField::max_cargoes = max<uint>(CargoesField::max_cargoes, std::count_if(indsp->produced_cargo, endof(indsp->produced_cargo), IsCargoIDValid));
2208  }
2209  d.width = max(d.width, this->ind_textsize.width);
2210  d.height = this->ind_textsize.height;
2211  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2212 
2213  /* Compute max size of the cargo texts. */
2214  this->cargo_textsize.width = 0;
2215  this->cargo_textsize.height = 0;
2216  for (uint i = 0; i < NUM_CARGO; i++) {
2217  const CargoSpec *csp = CargoSpec::Get(i);
2218  if (!csp->IsValid()) continue;
2219  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2220  }
2221  d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2222  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2223 
2224  d.width += 2 * HOR_TEXT_PADDING;
2225  /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2227  d.height = max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height);
2228 
2229  CargoesField::industry_width = d.width;
2231 
2232  /* Width of a #CFT_CARGO field. */
2234  }
2235 
2236  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
2237  {
2238  switch (widget) {
2239  case WID_IC_PANEL:
2241  break;
2242 
2243  case WID_IC_IND_DROPDOWN:
2244  size->width = max(size->width, this->ind_textsize.width + padding.width);
2245  break;
2246 
2247  case WID_IC_CARGO_DROPDOWN:
2248  size->width = max(size->width, this->cargo_textsize.width + padding.width);
2249  break;
2250  }
2251  }
2252 
2253 
2255  void SetStringParameters (int widget) const override
2256  {
2257  if (widget != WID_IC_CAPTION) return;
2258 
2259  if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2260  const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2261  SetDParam(0, indsp->name);
2262  } else {
2263  const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2264  SetDParam(0, csp->name);
2265  }
2266  }
2267 
2276  static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
2277  {
2278  while (length1 > 0) {
2279  if (*cargoes1 != INVALID_CARGO) {
2280  for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
2281  }
2282  cargoes1++;
2283  length1--;
2284  }
2285  return false;
2286  }
2287 
2294  static bool HousesCanSupply(const CargoID *cargoes, uint length)
2295  {
2296  for (uint i = 0; i < length; i++) {
2297  if (cargoes[i] == INVALID_CARGO) continue;
2298  if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
2299  }
2300  return false;
2301  }
2302 
2309  static bool HousesCanAccept(const CargoID *cargoes, uint length)
2310  {
2311  HouseZones climate_mask;
2313  case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
2314  case LT_ARCTIC: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
2315  case LT_TROPIC: climate_mask = HZ_SUBTROPIC; break;
2316  case LT_TOYLAND: climate_mask = HZ_TOYLND; break;
2317  default: NOT_REACHED();
2318  }
2319  for (uint i = 0; i < length; i++) {
2320  if (cargoes[i] == INVALID_CARGO) continue;
2321 
2322  for (uint h = 0; h < NUM_HOUSES; h++) {
2323  HouseSpec *hs = HouseSpec::Get(h);
2324  if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
2325 
2326  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2327  if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
2328  }
2329  }
2330  }
2331  return false;
2332  }
2333 
2340  static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
2341  {
2342  int count = 0;
2343  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2344  const IndustrySpec *indsp = GetIndustrySpec(it);
2345  if (!indsp->enabled) continue;
2346 
2347  if (HasCommonValidCargo(cargoes, length, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) count++;
2348  }
2349  return count;
2350  }
2351 
2358  static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
2359  {
2360  int count = 0;
2361  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2362  const IndustrySpec *indsp = GetIndustrySpec(it);
2363  if (!indsp->enabled) continue;
2364 
2365  if (HasCommonValidCargo(cargoes, length, indsp->produced_cargo, lengthof(indsp->produced_cargo))) count++;
2366  }
2367  return count;
2368  }
2369 
2376  void ShortenCargoColumn(int column, int top, int bottom)
2377  {
2378  while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2379  this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
2380  top++;
2381  }
2382  this->fields[top].columns[column].u.cargo.top_end = true;
2383 
2384  while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2385  this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
2386  bottom--;
2387  }
2388  this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2389  }
2390 
2397  void PlaceIndustry(int row, int col, IndustryType it)
2398  {
2399  assert(this->fields[row].columns[col].type == CFT_EMPTY);
2400  this->fields[row].columns[col].MakeIndustry(it);
2401  if (col == 0) {
2402  this->fields[row].ConnectIndustryProduced(col);
2403  } else {
2404  this->fields[row].ConnectIndustryAccepted(col);
2405  }
2406  }
2407 
2412  {
2413  if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2414 
2415  /* Only notify the smallmap window if it exists. In particular, do not
2416  * bring it to the front to prevent messing up any nice layout of the user. */
2418  }
2419 
2424  void ComputeIndustryDisplay(IndustryType it)
2425  {
2426  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION;
2427  this->ind_cargo = it;
2428  _displayed_industries.reset();
2429  _displayed_industries.set(it);
2430 
2431  this->fields.clear();
2432  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2433  CargoesRow &row = this->fields.back();
2434  row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2438  row.columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2439 
2440  const IndustrySpec *central_sp = GetIndustrySpec(it);
2441  bool houses_supply = HousesCanSupply(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2442  bool houses_accept = HousesCanAccept(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2443  /* Make a field consisting of two cargo columns. */
2444  int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo)) + houses_supply;
2445  int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo, lengthof(central_sp->produced_cargo)) + houses_accept;
2446  int num_indrows = max(3, max(num_supp, num_cust)); // One is needed for the 'it' industry, and 2 for the cargo labels.
2447  for (int i = 0; i < num_indrows; i++) {
2448  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2449  CargoesRow &row = this->fields.back();
2450  row.columns[0].MakeEmpty(CFT_EMPTY);
2451  row.columns[1].MakeCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2452  row.columns[2].MakeEmpty(CFT_EMPTY);
2453  row.columns[3].MakeCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2454  row.columns[4].MakeEmpty(CFT_EMPTY);
2455  }
2456  /* Add central industry. */
2457  int central_row = 1 + num_indrows / 2;
2458  this->fields[central_row].columns[2].MakeIndustry(it);
2459  this->fields[central_row].ConnectIndustryProduced(2);
2460  this->fields[central_row].ConnectIndustryAccepted(2);
2461 
2462  /* Add cargo labels. */
2463  this->fields[central_row - 1].MakeCargoLabel(2, true);
2464  this->fields[central_row + 1].MakeCargoLabel(2, false);
2465 
2466  /* Add suppliers and customers of the 'it' industry. */
2467  int supp_count = 0;
2468  int cust_count = 0;
2469  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2470  const IndustrySpec *indsp = GetIndustrySpec(it);
2471  if (!indsp->enabled) continue;
2472 
2473  if (HasCommonValidCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo), indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2474  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2475  _displayed_industries.set(it);
2476  supp_count++;
2477  }
2478  if (HasCommonValidCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo), indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2479  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2480  _displayed_industries.set(it);
2481  cust_count++;
2482  }
2483  }
2484  if (houses_supply) {
2485  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2486  supp_count++;
2487  }
2488  if (houses_accept) {
2489  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2490  cust_count++;
2491  }
2492 
2493  this->ShortenCargoColumn(1, 1, num_indrows);
2494  this->ShortenCargoColumn(3, 1, num_indrows);
2495  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2497  this->SetDirty();
2498  this->NotifySmallmap();
2499  }
2500 
2506  {
2507  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_CARGO_CAPTION;
2508  this->ind_cargo = cid + NUM_INDUSTRYTYPES;
2509  _displayed_industries.reset();
2510 
2511  this->fields.clear();
2512  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2513  CargoesRow &row = this->fields.back();
2514  row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2516  row.columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2519 
2520  bool houses_supply = HousesCanSupply(&cid, 1);
2521  bool houses_accept = HousesCanAccept(&cid, 1);
2522  int num_supp = CountMatchingProducingIndustries(&cid, 1) + houses_supply + 1; // Ensure room for the cargo label.
2523  int num_cust = CountMatchingAcceptingIndustries(&cid, 1) + houses_accept;
2524  int num_indrows = max(num_supp, num_cust);
2525  for (int i = 0; i < num_indrows; i++) {
2526  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2527  CargoesRow &row = this->fields.back();
2528  row.columns[0].MakeEmpty(CFT_EMPTY);
2529  row.columns[1].MakeCargo(&cid, 1);
2530  row.columns[2].MakeEmpty(CFT_EMPTY);
2531  row.columns[3].MakeEmpty(CFT_EMPTY);
2532  row.columns[4].MakeEmpty(CFT_EMPTY);
2533  }
2534 
2535  this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2536 
2537  /* Add suppliers and customers of the cargo. */
2538  int supp_count = 0;
2539  int cust_count = 0;
2540  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2541  const IndustrySpec *indsp = GetIndustrySpec(it);
2542  if (!indsp->enabled) continue;
2543 
2544  if (HasCommonValidCargo(&cid, 1, indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2545  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2546  _displayed_industries.set(it);
2547  supp_count++;
2548  }
2549  if (HasCommonValidCargo(&cid, 1, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2550  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2551  _displayed_industries.set(it);
2552  cust_count++;
2553  }
2554  }
2555  if (houses_supply) {
2556  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2557  supp_count++;
2558  }
2559  if (houses_accept) {
2560  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2561  cust_count++;
2562  }
2563 
2564  this->ShortenCargoColumn(1, 1, num_indrows);
2565  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2567  this->SetDirty();
2568  this->NotifySmallmap();
2569  }
2570 
2578  void OnInvalidateData(int data = 0, bool gui_scope = true) override
2579  {
2580  if (!gui_scope) return;
2581  if (data == NUM_INDUSTRYTYPES) {
2582  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2583  this->RaiseWidget(WID_IC_NOTIFY);
2584  this->SetWidgetDirty(WID_IC_NOTIFY);
2585  }
2586  return;
2587  }
2588 
2589  assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2590  this->ComputeIndustryDisplay(data);
2591  }
2592 
2593  void DrawWidget(const Rect &r, int widget) const override
2594  {
2595  if (widget != WID_IC_PANEL) return;
2596 
2597  DrawPixelInfo tmp_dpi, *old_dpi;
2598  int width = r.right - r.left + 1;
2599  int height = r.bottom - r.top + 1 - WD_FRAMERECT_TOP - WD_FRAMERECT_BOTTOM;
2600  if (!FillDrawPixelInfo(&tmp_dpi, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, width, height)) return;
2601  old_dpi = _cur_dpi;
2602  _cur_dpi = &tmp_dpi;
2603 
2604  int left_pos = WD_FRAMERECT_LEFT;
2605  if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
2606  int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2607 
2608  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2609  int vpos = -this->vscroll->GetPosition() * nwp->resize_y;
2610  for (uint i = 0; i < this->fields.size(); i++) {
2611  int row_height = (i == 0) ? CargoesField::small_height : CargoesField::normal_height;
2612  if (vpos + row_height >= 0) {
2613  int xpos = left_pos;
2614  int col, dir;
2615  if (_current_text_dir == TD_RTL) {
2616  col = last_column;
2617  dir = -1;
2618  } else {
2619  col = 0;
2620  dir = 1;
2621  }
2622  while (col >= 0 && col <= last_column) {
2623  this->fields[i].columns[col].Draw(xpos, vpos);
2625  col += dir;
2626  }
2627  }
2628  vpos += row_height;
2629  if (vpos >= height) break;
2630  }
2631 
2632  _cur_dpi = old_dpi;
2633  }
2634 
2642  bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
2643  {
2644  const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2645  pt.x -= nw->pos_x;
2646  pt.y -= nw->pos_y;
2647 
2648  int vpos = WD_FRAMERECT_TOP + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
2649  if (pt.y < vpos) return false;
2650 
2651  int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
2652  if (row + 1 >= (int)this->fields.size()) return false;
2653  vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
2654  row++; // rebase row to match index of this->fields.
2655 
2656  int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::cargo_field_width) / 2);
2657  if (pt.x < xpos) return false;
2658  int column;
2659  for (column = 0; column <= 5; column++) {
2661  if (pt.x < xpos + width) break;
2662  xpos += width;
2663  }
2664  int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2665  if (column > num_columns) return false;
2666  xpos = pt.x - xpos;
2667 
2668  /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
2669  fieldxy->y = row;
2670  xy->y = vpos;
2671  if (_current_text_dir == TD_RTL) {
2672  fieldxy->x = num_columns - column;
2673  xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
2674  } else {
2675  fieldxy->x = column;
2676  xy->x = xpos;
2677  }
2678  return true;
2679  }
2680 
2681  void OnClick(Point pt, int widget, int click_count) override
2682  {
2683  switch (widget) {
2684  case WID_IC_PANEL: {
2685  Point fieldxy, xy;
2686  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
2687 
2688  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2689  switch (fld->type) {
2690  case CFT_INDUSTRY:
2691  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
2692  break;
2693 
2694  case CFT_CARGO: {
2695  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
2696  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
2697  CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
2698  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2699  break;
2700  }
2701 
2702  case CFT_CARGO_LABEL: {
2703  CargoID cid = fld->CargoLabelClickedAt(xy);
2704  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2705  break;
2706  }
2707 
2708  default:
2709  break;
2710  }
2711  break;
2712  }
2713 
2714  case WID_IC_NOTIFY:
2715  this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
2716  this->SetWidgetDirty(WID_IC_NOTIFY);
2717  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
2718 
2719  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2720  if (FindWindowByClass(WC_SMALLMAP) == nullptr) ShowSmallMap();
2721  this->NotifySmallmap();
2722  }
2723  break;
2724 
2725  case WID_IC_CARGO_DROPDOWN: {
2726  DropDownList lst;
2727  const CargoSpec *cs;
2729  lst.emplace_back(new DropDownListStringItem(cs->name, cs->Index(), false));
2730  }
2731  if (!lst.empty()) {
2732  int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
2733  ShowDropDownList(this, std::move(lst), selected, WID_IC_CARGO_DROPDOWN, 0, true);
2734  }
2735  break;
2736  }
2737 
2738  case WID_IC_IND_DROPDOWN: {
2739  DropDownList lst;
2740  for (IndustryType ind : _sorted_industry_types) {
2741  const IndustrySpec *indsp = GetIndustrySpec(ind);
2742  if (!indsp->enabled) continue;
2743  lst.emplace_back(new DropDownListStringItem(indsp->name, ind, false));
2744  }
2745  if (!lst.empty()) {
2746  int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
2747  ShowDropDownList(this, std::move(lst), selected, WID_IC_IND_DROPDOWN, 0, true);
2748  }
2749  break;
2750  }
2751  }
2752  }
2753 
2754  void OnDropdownSelect(int widget, int index) override
2755  {
2756  if (index < 0) return;
2757 
2758  switch (widget) {
2759  case WID_IC_CARGO_DROPDOWN:
2760  this->ComputeCargoDisplay(index);
2761  break;
2762 
2763  case WID_IC_IND_DROPDOWN:
2764  this->ComputeIndustryDisplay(index);
2765  break;
2766  }
2767  }
2768 
2769  bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override
2770  {
2771  if (widget != WID_IC_PANEL) return false;
2772 
2773  Point fieldxy, xy;
2774  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return false;
2775 
2776  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2777  CargoID cid = INVALID_CARGO;
2778  switch (fld->type) {
2779  case CFT_CARGO: {
2780  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
2781  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
2782  cid = fld->CargoClickedAt(lft, rgt, xy);
2783  break;
2784  }
2785 
2786  case CFT_CARGO_LABEL: {
2787  cid = fld->CargoLabelClickedAt(xy);
2788  break;
2789  }
2790 
2791  case CFT_INDUSTRY:
2792  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
2793  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, 0, nullptr, close_cond);
2794  }
2795  return true;
2796 
2797  default:
2798  break;
2799  }
2800  if (cid != INVALID_CARGO && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
2801  const CargoSpec *csp = CargoSpec::Get(cid);
2802  uint64 params[5];
2803  params[0] = csp->name;
2804  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, 1, params, close_cond);
2805  return true;
2806  }
2807 
2808  return false;
2809  }
2810 
2811  void OnResize() override
2812  {
2813  this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL);
2814  }
2815 };
2816 
2819 
2824 static void ShowIndustryCargoesWindow(IndustryType id)
2825 {
2826  if (id >= NUM_INDUSTRYTYPES) {
2827  for (IndustryType ind : _sorted_industry_types) {
2828  const IndustrySpec *indsp = GetIndustrySpec(ind);
2829  if (indsp->enabled) {
2830  id = ind;
2831  break;
2832  }
2833  }
2834  if (id >= NUM_INDUSTRYTYPES) return;
2835  }
2836 
2838  if (w != nullptr) {
2839  w->InvalidateData(id);
2840  return;
2841  }
2842  new IndustryCargoesWindow(id);
2843 }
2844 
2847 {
2849 }
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]
16 accepted cargoes.
Definition: industrytype.h:120
Nested widget containing a viewport.
Definition: widget_type.h:79
Display chain button.
bool enabled
the house is available to build (true by default, but can be disabled by newgrf)
Definition: house.h:111
Functions related to OTTD&#39;s strings.
void NotifySmallmap()
Notify smallmap that new displayed industries have been selected (in _displayed_industries).
void GenerateIndustries()
This function will create random industries during game creation.
static const int HOR_CARGO_BORDER_SPACE
Amount of space between the left/right edge of a CFT_CARGO field, and the left/right most vertical ca...
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
Functions/types related to NewGRF debugging.
static void Swap(T &a, T &b)
Type safe swap operation.
Definition: math_func.hpp:275
Transfer storage of cargo suffix information.
Base types for having sorted lists in GUIs.
void RebuildDone()
Notify the sortlist that the rebuild is done.
Matrix of the industries.
static const uint MAX_CARGOES
Maximum number of cargoes carried in a CFT_CARGO field in CargoesField.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:79
Definition of stuff that is very close to a company, like the company struct itself.
bool _networking
are we in networking mode?
Definition: network.cpp:52
static int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
Returns percents of cargo transported if industry produces this cargo, else -1.
static uint minu(const uint a, const uint b)
Returns the minimum of two unsigned integers.
Definition: math_func.hpp:68
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
bool enabled
entity still available (by default true).newgrf can disable it, though
Definition: industrytype.h:139
Build (fund or prospect) a new industry,.
byte production_rate[INDUSTRY_NUM_OUTPUTS]
production rate for each cargo
Definition: industry.h:47
void SortIndustryTypes()
Initialize the list of sorted industry types.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
Data about how and where to blit pixels.
Definition: gfx_type.h:154
static const uint8 PC_WHITE
White palette colour.
Definition: gfx_func.h:206
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Dropdown for the criteria of the sort.
Horizontally center the text.
Definition: gfx_func.h:95
void OnInit() override
Notification that the nested widget tree gets initialized.
static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
Count how many industries have accepted cargoes in common with one of the supplied set...
static NWidgetPart SetResize(int16 dx, int16 dy)
Widget part function for setting the resize step.
Definition: widget_type.h:928
uint8 raw_industry_construction
type of (raw) industry construction (none, "normal", prospecting)
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen...
Definition: gfx.cpp:110
Offset at right of a matrix cell.
Definition: window_gui.h:77
byte landscape
the landscape we&#39;re currently in
Viewport of the industry.
High level window description.
Definition: window_gui.h:166
byte map_colour
colour used for the small map
Definition: industrytype.h:125
static int industry_width
Width of an industry field.
uint16 count
How many industries are loaded.
Goto button.
void MakeIndustry(IndustryType ind_type)
Make an industry type field.
below this level, the industry is set to be closing
Definition: industry.h:32
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:291
static const int VERT_CARGO_SPACE
Amount of vertical space between two connected cargoes at an industry.
int info_height
Height needed for the WID_IV_INFO panel.
Scrollbar data structure.
Definition: widget_type.h:587
Functions for NewGRF industries.
static const int INDUSTRY_LINE_COLOUR
Line colour of the industry type box.
void MakeEmpty(CargoesFieldType type)
Make one of the empty fields (CFT_EMPTY or CFT_SMALL_EMPTY).
void PlaceIndustry(int row, int col, IndustryType it)
Place an industry in the fields.
Offset at top to draw the frame rectangular area.
Definition: window_gui.h:62
void OnGameTick() override
Called once per (game) tick.
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition: widget.cpp:1934
Horizontal container.
Definition: widget_type.h:73
void ShowSmallMap()
Show the smallmap window.
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX) ...
Definition: widget_type.h:61
void SetSortFuncs(SortFunction *const *n_funcs)
Hand the array of sort function pointers to the sort list.
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
void ShowQueryString(StringID str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
Definition: misc_gui.cpp:1119
uint32 GetIndustryProbabilityCallback(IndustryType type, IndustryAvailabilityCallType creation_type, uint32 default_prob)
Check with callback CBID_INDUSTRY_PROBABILITY whether the industry can be built.
Maximal number of cargo types in a game.
Definition: cargo_type.h:64
CargoSuffixDisplay
Ways of displaying the cargo.
byte cargo_acceptance[HOUSE_NUM_ACCEPTS]
acceptance level for the cargo slots
Definition: house.h:107
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
void BuildSortIndustriesList()
(Re)Build industries list
Specification of a cargo type.
Definition: cargotype.h:55
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
void GuiShowTooltips(Window *parent, StringID str, uint paramcount, const uint64 params[], TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition: misc_gui.cpp:764
Display the cargo without sub-type (cb37 result 401).
from the Fund/build window
default level set when the industry is created
Definition: industry.h:33
Row of buttons at the bottom.
static bool IndustryTypeSorter(const Industry *const &a, const Industry *const &b)
Sort industries by type and name.
static int small_height
Height of the header row.
Editability editable
Mode for changing production.
CargoID accepts_cargo[HOUSE_NUM_ACCEPTS]
input cargo slots
Definition: house.h:108
Resize box (normally at bottom-right of a window)
Definition: widget_type.h:66
Pressed (inset) panel, most commonly used as combo box text area.
Definition: widget_type.h:49
uint16 callback_mask
Bitmask of industry callbacks that have to be called.
Definition: industrytype.h:137
struct CargoesField::@17::@19 cargo
Cargo data (for CFT_CARGO).
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition: map_func.h:205
static const int CARGO_LINE_COLOUR
Line colour around the cargo.
static bool HousesCanSupply(const CargoID *cargoes, uint length)
Can houses be used to supply one of the cargoes?
signal set to actually close the industry
Definition: industry.h:31
Defines the internal data of a functional industry.
Definition: industry.h:40
static const int BLOB_HEIGHT
Height of the industry legend colour, including border.
void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
Command callback.
DifficultySettings difficulty
settings related to the difficulty
Industry-directory window.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:189
void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x=0, int y=0, const GRFFile *textref_stack_grffile=nullptr, uint textref_stack_size=0, const uint32 *textref_stack=nullptr)
Display an error message in a window.
Definition: error_gui.cpp:380
static const int DAY_TICKS
1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885.
Definition: date_type.h:28
Close box (at top-left of a window)
Definition: widget_type.h:67
Offset at top of a matrix cell.
Definition: window_gui.h:78
InfoLine
Specific lines in the info panel.
View-industry window.
Display cargo labels.
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:48
StringID GetGRFStringID(uint32 grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
Functions related to world/map generation.
static void ShowIndustryCargoesWindow(IndustryType id)
Open the industry and cargoes window.
Stuff related to the text buffer GUI.
static const int MATRIX_TEXT_OFFSET
The offset for the text in the matrix.
void InitializeViewport(Window *w, uint32 follow_flags, ZoomLevel zoom)
Initialize the viewport of the window.
Definition: widget.cpp:1925
static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
Count how many industries have produced cargoes in common with one of the supplied set...
void OnDropdownSelect(int widget, int index) override
A dropdown option associated to this window has been selected.
bool persistent_buildingtools
keep the building tools active after usage
void ComputeCargoDisplay(CargoID cid)
Compute what and where to display for cargo id cid.
Common return value for all commands.
Definition: command_type.h:23
Dimension ind_textsize
Size to hold any industry type text, as well as STR_INDUSTRY_CARGOES_SELECT_INDUSTRY.
bool IsCargoIDValid(CargoID t)
Test whether cargo type is not CT_INVALID.
Definition: cargo_type.h:74
void MakeHeader(StringID textid)
Make a header above an industry column.
static T max(const T a, const T b)
Returns the maximum of two values.
Definition: math_func.hpp:24
std::vector< IndustryTileLayout > layouts
List of possible tile layouts for the industry.
Definition: industrytype.h:107
the industry is running at full speed
Definition: industry.h:34
Nested widget to display a viewport in a window.
Definition: widget_type.h:573
Common string list item.
Definition: dropdown_type.h:39
void OnDropdownSelect(int widget, int index) override
A dropdown option associated to this window has been selected.
void SetListing(Listing l)
Import sort conditions.
Display industry.
Large amount of vertical space between two paragraphs of text.
Definition: window_gui.h:138
int DrawInfo(uint left, uint right, uint top)
Draw the text in the WID_IV_INFO panel.
Allow changing the production rates.
Window * GetCallbackWnd()
Get the window that started the current highlighting.
Definition: viewport.cpp:2469
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:13
StringID name
Name of this type of cargo.
Definition: cargotype.h:70
Industry directory; Window numbers:
Definition: window_type.h:259
static const HouseID NUM_HOUSES
Total number of houses.
Definition: house.h:29
void OnPaint() override
The window must be repainted.
StringID name
Displayed name of the industry.
Definition: industrytype.h:126
static int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
Definition: math_func.hpp:336
HouseZones
Definition: house.h:71
bool NeedRebuild() const
Check if a rebuild is needed.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static int cargo_field_width
Width of a cargo field.
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition: gfx.cpp:1481
Called to determine more text in the fund industry window.
Display the cargo and amount (if useful), but no sub-type (cb37 result 400 or fail).
static bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
Definition: math_func.hpp:248
static uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
Definition: math_func.hpp:182
StringID GetIndustryString(const Industry *i) const
Get the StringID to draw and set the appropriate DParams.
The list of industries.
HouseZones building_availability
where can it be built (climates, zones)
Definition: house.h:110
Class to backup a specific variable and restore it later.
Definition: backup_type.hpp:21
Info panel about the industry.
void SetCount(int num)
Sets the number of elements in the list.
Definition: widget_type.h:668
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:908
Functions related to (drawing on) viewports.
Pseudo random number generator.
void ForceRebuild()
Force that a rebuild is needed.
static bool IndustryTransportedCargoSorter(const Industry *const &a, const Industry *const &b)
Sort industries by transported cargo and name.
Data structure for an opened window.
Definition: window_gui.h:276
Invalid cargo type.
Definition: cargo_type.h:68
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:35
static NWidgetPart SetMatrixDataTip(uint8 cols, uint8 rows, StringID tip)
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
Definition: widget_type.h:1030
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static NWidgetPart SetPadding(uint8 top, uint8 right, uint8 bottom, uint8 left)
Widget part function for setting additional space around a widget.
Definition: widget_type.h:1044
static bool IsInsideMM(const T x, const size_t min, const size_t max)
Checks if a value is in an interval.
Definition: math_func.hpp:264
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition: window.cpp:3334
Bottom offset of the text of the frame.
Definition: window_gui.h:73
Header of Action 04 "universal holder" structure and functions.
void SetDParamStr(uint n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition: strings.cpp:279
Header text.
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
Functions related to low-level strings.
The tile has no ownership.
Definition: company_type.h:25
void OnResize() override
Called after the window got resized.
Types related to cheating.
StringID header
Header text (for CFT_HEADER).
void OnTimeout() override
Called when this window&#39;s timeout has been reached.
Functions related to errors.
void OnResize() override
Called after the window got resized.
Offset at bottom of a matrix cell.
Definition: window_gui.h:79
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX) ...
Definition: widget_type.h:63
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
void ShowNewGRFInspectWindow() const override
Show the NewGRF inspection window.
int GetScrolledRowFromWidget(int clickpos, const Window *const w, int widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition: widget.cpp:1957
Caption of the window.
void OnQueryTextFinished(char *str) override
The query window opened from this window has closed.
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:208
Fill rectangle with a single colour.
Definition: gfx_type.h:282
SoundSettings sound
sound effect settings
std::array< IndustryType, NUM_INDUSTRYTYPES > _sorted_industry_types
Industry types sorted by name.
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
Listing GetListing() const
Export current sort conditions.
static const NWidgetPart _nested_industry_cargoes_widgets[]
Widgets of the industry cargoes window.
uint current_y
Current vertical size (after resizing).
Definition: widget_type.h:173
Sort descending.
Definition: window_gui.h:225
void MakeCargoLabel(int column, bool accepting)
Construct a CFT_CARGO_LABEL field.
StringID GetErrorMessage() const
Returns the error message of a command.
Definition: command_type.h:140
Caption of the window.
Small map; Window numbers:
Definition: window_type.h:97
#define FONT_HEIGHT_NORMAL
Height of characters in the normal (FS_NORMAL) font.
Definition: gfx_func.h:176
static NWidgetPart SetDataTip(uint32 data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1012
void OnResize() override
Called after the window got resized.
CargoSuffixDisplay display
How to display the cargo and text.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:78
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
static NWidgetPart SetMinimalSize(int16 x, int16 y)
Widget part function for setting the minimal size.
Definition: widget_type.h:945
bool Succeeded() const
Did this command succeed?
Definition: command_type.h:150
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Data about a single field in the IndustryCargoesWindow panel.
void OnPlaceObject(Point pt, TileIndex tile) override
The user clicked some place on the map when a tile highlight mode has been set.
Definition of base types and functions in a cross-platform compatible way.
TileIndex GetCenterTile() const
Get the center tile.
Definition: tilearea_type.h:57
char text[512]
Cargo suffix text.
bool UsesSmoothEconomy() const
Determines whether this industrytype uses smooth economy or whether it uses standard/newgrf productio...
A number of safeguards to prevent using unsafe methods.
CargoesFieldType
Available types of field.
bool value
tells if the bool cheat is active or not
Definition: cheat_type.h:18
static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
Draw a horizontal cargo connection.
IndustryType type
type of industry.
Definition: industry.h:57
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:102
Geometry functions.
rectangle (stations, depots, ...)
Simple depressed panel.
Definition: widget_type.h:48
static const int VERT_TEXT_PADDING
Vertical padding around the industry type text.
static uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
Definition: math_func.hpp:314
Fields fields
Fields to display in the WID_IC_PANEL.
Cheat setup_prod
setup raw-material production in game
Definition: cheat_type.h:35
void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
Make a field displaying cargo type names.
TileArea location
Location of the industry.
Definition: industry.h:41
additional text in industry window
static const int BLOB_DISTANCE
Distance of the industry legend colour from the edge of the industry box.
uint16 last_month_production[INDUSTRY_NUM_OUTPUTS]
total units produced per cargo in the last full month
Definition: industry.h:53
void Draw(int xpos, int ypos) const
Draw the field.
static const int HOR_CARGO_WIDTH
Width of a vertical cargo column (inclusive the border line).
static const int VERT_INTER_INDUSTRY_SPACE
Amount of space between two industries in a column.
CargoID produced_cargo[INDUSTRY_NUM_OUTPUTS]
16 production cargo slots
Definition: industry.h:44
int pos_x
Horizontal position of top-left corner of the widget in the window.
Definition: widget_type.h:175
Offset at left of a matrix cell.
Definition: window_gui.h:76
static const NWidgetPart _nested_industry_view_widgets[]
Widget definition of the view industry gui.
static WindowDesc _build_industry_desc(WDP_AUTO, "build_industry", 170, 212, WC_BUILD_INDUSTRY, WC_NONE, WDF_CONSTRUCTION, _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets))
Window definition of the dynamic place industries gui.
byte bottom_end
Stop at the bottom of the vertical cargoes.
Default zoom level for the industry view.
Definition: zoom_type.h:35
Defines the data structure for constructing industry.
Definition: industrytype.h:106
InfoLine clicked_line
The line of the button that has been clicked.
static WindowDesc _industry_directory_desc(WDP_AUTO, "list_industries", 428, 190, WC_INDUSTRY_DIRECTORY, WC_NONE, 0, _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets))
Window definition of the industry directory gui.
static NWidgetPart NWidget(WidgetType tp, Colours col, int16 idx=-1)
Widget part function for starting a new &#39;real&#39; widget.
Definition: widget_type.h:1112
CargoesFieldType type
Type of field.
Offset at bottom to draw the frame rectangular area.
Definition: window_gui.h:63
std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const
Build a string of cargo names with suffixes attached.
Baseclass for nested widgets.
Definition: widget_type.h:124
Money GetConstructionCost() const
Get the cost for constructing this industry.
int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition: gfx.cpp:498
additional text in fund window
Basic functions/variables used all over the place.
static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
Do the two sets of cargoes have a valid cargo in common?
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]
16 input cargo slots
Definition: industry.h:49
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
Empty small field (for the header).
Right offset of the text of the frame.
Definition: window_gui.h:71
bool DoCommandP(const CommandContainer *container, bool my_cmd)
Shortcut for the long DoCommandP when having a container with the data.
Definition: command.cpp:532
Industry view; Window numbers:
Definition: window_type.h:356
uint8 cargo_map[NUM_CARGO]
Inverse cargo translation table (CargoID -> local ID)
Definition: newgrf.h:127
uint16 incoming_cargo_waiting[INDUSTRY_NUM_INPUTS]
incoming cargo waiting to be processed
Definition: industry.h:46
static const int CARGO_STUB_WIDTH
Width of a cargo not carried in the column (should be less than HOR_CARGO_BORDER_SPACE).
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:40
IndustryBehaviour behaviour
How this industry will behave, and how others entities can use it.
Definition: industrytype.h:124
byte prod_level
general production level
Definition: industry.h:48
Select cargo dropdown.
uint resize_y
Vertical resize step (0 means not resizable).
Definition: widget_type.h:165
GRFFileProps grf_prop
properties related to the grf file
Definition: industrytype.h:140
Grid of rows and columns.
Definition: widget_type.h:57
#define FOR_ALL_SORTED_STANDARD_CARGOSPECS(var)
Loop header for iterating over &#39;real&#39; cargoes, sorted by name.
Definition: cargotype.h:171
Dimension cargo_textsize
Size to hold any cargo text, as well as STR_INDUSTRY_CARGOES_SELECT_CARGO.
Top offset of the text of the frame.
Definition: window_gui.h:72
Left offset of the text of the frame.
Definition: window_gui.h:70
bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
Definition: viewport.cpp:2385
Functions related to sound.
void DrawArrowButtons(int x, int y, Colours button_colour, byte state, bool clickable_left, bool clickable_right)
Draw [<][>] boxes.
void SetSortType(uint8 n_type)
Set the sorttype of the list.
bool Sort(SortFunction *compare)
Sort the list.
void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
Start using the TTDP compatible string code parsing.
The game does not build industries.
Definition: settings_type.h:42
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition: widget.cpp:656
bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
Calculate in which field was clicked, and within the field, at what position.
uint32 StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
void OnTimeout() override
Called when this window&#39;s timeout has been reached.
IndustryType selected_type
industry corresponding to the above index
static const uint8 PC_BLACK
Black palette colour.
Definition: gfx_func.h:203
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
Definition: settings_gui.h:17
12 1000 can appear in temperate climate
Definition: house.h:80
IndustryType ind_type
Industry type (NUM_INDUSTRYTYPES means &#39;houses&#39;).
Empty field.
static const int HOR_CARGO_SPACE
Amount of horizontal space between two vertical cargoes.
static bool IndustryNameSorter(const Industry *const &a, const Industry *const &b)
Sort industries by name.
Display then cargo, amount, and string (cb37 result 000-3FF).
int GetCargoBase(int xpos) const
For a CFT_CARGO, compute the left position of the left-most vertical cargo connection.
static int normal_height
Height of the non-header rows.
bool IsNewGRFInspectable() const override
Is the data related to this window NewGRF inspectable?
Build industry; Window numbers:
Definition: window_type.h:428
void ShowExtraViewPortWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Allow changing the production multiplier.
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:700
bool timer_enabled
timer can be used
TileIndex tile
The base tile of the area.
Definition: tilearea_type.h:17
14 4000 can appear in subtropical climate
Definition: house.h:82
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:38
std::bitset< NUM_INDUSTRYTYPES > _displayed_industries
Communication from the industry chain window to the smallmap window about what industries to display...
13 2000 can appear in sub-arctic climate below the snow line
Definition: house.h:81
void ConnectIndustryProduced(int column)
Connect industry production cargoes to the cargo column after it.
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
Sort industry types by their name.
static bool HousesCanAccept(const CargoID *cargoes, uint length)
Can houses be used as customers of the produced cargoes?
static const uint8 PC_YELLOW
Yellow palette colour.
Definition: gfx_func.h:216
Smallmap GUI functions.
Functions related to companies.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static WindowDesc _industry_cargoes_desc(WDP_AUTO, "industry_cargoes", 300, 210, WC_INDUSTRY_CARGOES, WC_NONE, 0, _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets))
Window description for the industry cargoes window.
bool IsNewGRFInspectable(GrfSpecFeature feature, uint index)
Can we inspect the data given a certain feature and index.
void ErrorUnknownCallbackResult(uint32 grfid, uint16 cbid, uint16 cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
bool _generating_world
Whether we are generating the map or not.
Definition: genworld.cpp:60
15 8000 can appear in toyland climate
Definition: house.h:83
Both numeric and alphabetic and spaces and stuff.
Definition: string_type.h:27
static const IndustryType INVALID_INDUSTRYTYPE
one above amount is considered invalid
Definition: industry_type.h:27
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
GUISettings gui
settings related to the GUI
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:340
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:59
Scrollbar of the panel.
int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition: string.cpp:578
Display cargo connections.
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition: string_func.h:57
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:117
static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
Gets the string to display after the cargo name (using callback 37)
byte clicked_button
The button that has been clicked (to raise)
Types related to the industry widgets.
Editability
Modes for changing production.
void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index, const uint32 grfid=0)
Show the inspect window for a given feature and index.
uint32 TileIndex
The index/ID of a Tile.
Definition: tile_type.h:78
bool HandlePlacePushButton(Window *w, int widget, CursorID cursor, HighLightStyle mode)
This code is shared for the majority of the pushbuttons.
Definition: main_gui.cpp:98
#define cpp_lengthof(base, variable)
Gets the length of an array variable within a class.
Definition: stdafx.h:413
static int GetCargoTransportedSortValue(const Industry *i)
Returns value representing industry&#39;s transported cargo percentage for industry sorting.
static uint ToPercent8(uint i)
Converts a "fract" value 0..255 to "percent" value 0..100.
Definition: math_func.hpp:287
Production rate of cargo 1.
static size_t GetNumItems()
Returns number of valid items in the pool.
Definition: pool_type.hpp:321
uint ind_cargo
If less than NUM_INDUSTRYTYPES, an industry type, else a cargo id + NUM_INDUSTRYTYPES.
CargoesField columns[5]
One row of fields.
void RecomputeProductionMultipliers()
Recompute production_rate for current prod_level.
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:48
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here...
A single row of CargoesField.
void ConnectIndustryAccepted(int column)
Connect industry accepted cargoes to the cargo column before it.
byte num_cargoes
Number of cargoes.
Industry list.
bool has_newindustries
Set if there are any newindustries loaded.
Definition: newgrf.h:178
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like INV...
Definition: industry_type.h:26
Sort ascending.
Definition: window_gui.h:224
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition: map_func.h:215
Display then cargo and supplied string (cb37 result 800-BFF).
Vertical container.
Definition: widget_type.h:75
Scrollbar of the matrix.
Allow produced/accepted cargoes callbacks to supply more than 2 and 3 types.
Definition: industrytype.h:82
Functions for setting GUIs.
static NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME, WWT_INSET, or WWT_PANEL).
Definition: widget_type.h:997
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
Select industry dropdown.
static const byte INVALID_CARGO
Constant representing invalid cargo.
Definition: cargotype.h:52
uint current_x
Current horizontal size (after resizing).
Definition: widget_type.h:172
static Industry * PlaceIndustry(IndustryType type, IndustryAvailabilityCallType creation_type, bool try_hard)
Try to place the industry in the game.
#define endof(x)
Get the end element of an fixed size array.
Definition: stdafx.h:384
Window displaying the cargo connections around an industry (or cargo).
call production callback when cargo arrives at the industry
void OnInit() override
Notification that the nested widget tree gets initialized.
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition: window.cpp:1146
cargo sub-type display
Panel that shows the chain.
CargoSuffixType
Cargo suffix type (for which window is it requested)
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp:2396
Scrollbar of the list.
Functions related to commands.
Fund-industry window.
Coordinates of a point in 2D.
byte top_end
Stop at the top of the vertical cargoes.
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:45
CargoID Index() const
Determines index of this cargospec.
Definition: cargotype.h:88
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:280
Data structure describing how to show the list (what sort direction and criteria).
Definition: sortlist_type.h:31
Drop down list.
Definition: widget_type.h:68
static const int BLOB_WIDTH
Width of the industry legend colour, including border.
uint16 GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:620
bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
Window does not do autoscroll,.
Definition: window_gui.h:239
ConstructionSettings construction
construction of things in-game
void OnHundredthTick() override
Called once every 100 (game) ticks.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Definition: strings_type.h:17
Base of all industries.
Called to determine text to display after cargo name.
bool left_align
Align all cargo texts to the left (else align to the right).
bool HasConnection()
Does this CFT_CARGO field have a horizontal connection?
Offset at right to draw the frame rectangular area.
Definition: window_gui.h:61
void OnPaint() override
The window must be repainted.
const struct GRFFile * grffile
grf file that introduced this entity
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:64
Used for DoCommand-like (and some non-fatal AI GUI) errors/information.
Definition: error.h:21
static NWidgetPart SetFill(uint fill_x, uint fill_y)
Widget part function for setting filling.
Definition: widget_type.h:981
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
bool IsValid() const
Tests for validity of this cargospec.
Definition: cargotype.h:98
Dropdown for the order of the sort.
CargoesFieldType type
Type of field.
CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
Decide which cargo was clicked at in a CFT_CARGO field.
void Restore()
Restore the variable.
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
static const TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition: tile_type.h:83
static void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
Gets all strings to display after the cargoes of industries (using callback 37)
Base of the town class.
bool IsDescSortOrder() const
Check if the sort order is descending.
#define CMD_MSG(x)
Used to combine a StringID with the command.
Definition: command_type.h:368
void OnPlaceObjectAbort() override
The user cancelled a tile highlight mode that has been set.
int32 WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:705
GameCreationSettings game_creation
settings used during the creation of a game (map)
void SetCapacityFromWidget(Window *w, int widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget. ...
Definition: widget.cpp:1971
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows)...
Definition: viewport.cpp:3353
Specification of a rectangle with absolute coordinates of all edges.
Vertical scrollbar.
Definition: widget_type.h:82
byte CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:20
InfoLine editbox_line
The line clicked to open the edit box.
Text is written right-to-left by default.
Definition: strings_type.h:24
Right align the text (must be a single bit).
Definition: gfx_func.h:96
Called to determine more text in the industry window.
Info of the industry.
Left align the text.
Definition: gfx_func.h:94
Functions related to tile highlights.
Window functions not directly related to making/drawing windows.
int ConnectCargo(CargoID cargo, bool producer)
Connect a cargo from an industry to the CFT_CARGO column.
GRFLoadedFeatures _loaded_newgrf_features
Indicates which are the newgrf features currently loaded ingame.
Definition: newgrf.cpp:76
Find a place automatically.
Definition: window_gui.h:154
byte industry_density
The industry density.
Definition: settings_type.h:56
void MakeCargo(const CargoID *cargoes, uint length, int count=-1, bool top_end=false, bool bottom_end=false)
Make a piece of cargo column.
byte last_month_pct_transported[INDUSTRY_NUM_OUTPUTS]
percentage transported per cargo in the last full month
Definition: industry.h:52
static bool IndustryProductionSorter(const Industry *const &a, const Industry *const &b)
Sort industries by production and name.
GUI functions that shouldn&#39;t be here.
void OnResize() override
Called after the window got resized.
CargoID CargoLabelClickedAt(Point pt) const
Decide what cargo the user clicked in the cargo label field.
bool SortFunction(const T &, const T &)
Signature of sort function.
Definition: sortlist_type.h:49
void SetButtons()
Update status of the fund and display-chain widgets.
call production callback every 256 ticks
struct CargoesField::@17::@18 industry
Industry data (for CFT_INDUSTRY).
int production_offset_y
The offset of the production texts/buttons.
Industry cargoes chain; Window numbers:
Definition: window_type.h:504
void ComputeIndustryDisplay(IndustryType it)
Compute what and where to display for industry type it.
static NWidgetPart SetScrollbar(int index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1093
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:44
static WindowDesc _industry_view_desc(WDP_AUTO, "view_industry", 260, 120, WC_INDUSTRY_VIEW, WC_NONE, 0, _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets))
Window definition of the view industry gui.
Dimensions (a width and height) of a rectangle in 2D.
bool click_beep
Beep on a random selection of buttons.
Offset at left to draw the frame rectangular area.
Definition: window_gui.h:60
Class for backupping variables and making sure they are restored later.
void ShortenCargoColumn(int column, int top, int bottom)
Shorten the cargo column to just the part between industries.
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition: window.cpp:1259
static const int HOR_TEXT_PADDING
Horizontal padding around the industry type text.
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:62
Display chain button.
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
build a new industry
Definition: command_type.h:232
int pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:176
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window&#39;s data as invalid (in need of re-computing)
Definition: window.cpp:3256
struct CargoesField::@17::@20 cargo_label
Label data (for CFT_CARGO_LABEL).
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition: gfx.cpp:621
(Toggle) Button with text
Definition: widget_type.h:53
static const int VERT_CARGO_EDGE
Amount of vertical space between top/bottom and the top/bottom connected cargo at an industry...
static void MemSetT(T *ptr, byte value, size_t num=1)
Type-safe version of memset().
Definition: mem_func.hpp:49
Cheats _cheats
All the cheats.
Definition: cheat.cpp:16
uint16 GetIndustryCallback(CallbackID callback, uint32 param1, uint32 param2, Industry *industry, IndustryType type, TileIndex tile)
Perform an industry callback.
int selected_index
index of the element in the matrix
11 800 can appear in sub-arctic climate above the snow line
Definition: house.h:79
uint16 GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:629
static uint max_cargoes
Largest number of cargoes actually on any industry.
static const NWidgetPart _nested_industry_directory_widgets[]
Widget definition of the industry directory gui.
Production rate of cargo 2.
uint8 SortType() const
Get the sorttype of the list.
Definition: sortlist_type.h:94
static void SetDParam(uint n, uint64 v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings_func.h:199
uint16 callback_timer
timer counter for callback eventual verification