OpenTTD Source  20241108-master-g80f628063a
build_vehicle_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 "engine_base.h"
12 #include "engine_func.h"
13 #include "station_base.h"
14 #include "network/network.h"
15 #include "articulated_vehicles.h"
16 #include "textbuf_gui.h"
17 #include "command_func.h"
18 #include "company_func.h"
19 #include "vehicle_gui.h"
20 #include "newgrf_engine.h"
21 #include "newgrf_text.h"
22 #include "group.h"
23 #include "string_func.h"
24 #include "strings_func.h"
25 #include "window_func.h"
27 #include "vehicle_func.h"
28 #include "dropdown_type.h"
29 #include "dropdown_func.h"
30 #include "engine_gui.h"
31 #include "cargotype.h"
32 #include "core/geometry_func.hpp"
33 #include "autoreplace_func.h"
34 #include "engine_cmd.h"
35 #include "train_cmd.h"
36 #include "vehicle_cmd.h"
37 #include "zoom_func.h"
38 #include "querystring_gui.h"
39 #include "stringfilter_type.h"
40 #include "hotkeys.h"
41 
43 
44 #include "table/strings.h"
45 
46 #include "safeguards.h"
47 
54 {
55  return std::max<uint>(GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.matrix.Vertical(), GetVehicleImageCellSize(type, EIT_PURCHASE).height);
56 }
57 
58 static constexpr NWidgetPart _nested_build_vehicle_widgets[] = {
60  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
61  NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), SetTextStyle(TC_WHITE),
62  NWidget(WWT_SHADEBOX, COLOUR_GREY),
63  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
64  NWidget(WWT_STICKYBOX, COLOUR_GREY),
65  EndContainer(),
68  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
69  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
70  EndContainer(),
73  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
74  EndContainer(),
75  NWidget(WWT_PANEL, COLOUR_GREY),
76  NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BV_FILTER), SetResize(1, 0), SetFill(1, 0), SetPadding(2), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
77  EndContainer(),
78  EndContainer(),
79  /* Vehicle list. */
81  NWidget(WWT_MATRIX, COLOUR_GREY, WID_BV_LIST), SetResize(1, 1), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_BV_SCROLLBAR),
83  EndContainer(),
84  /* Panel with details. */
85  NWidget(WWT_PANEL, COLOUR_GREY, WID_BV_PANEL), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
86  /* Build/rename buttons, resize button. */
88  NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BV_BUILD_SEL),
89  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_BUILD), SetResize(1, 0), SetFill(1, 0),
90  EndContainer(),
91  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
92  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_RENAME), SetResize(1, 0), SetFill(1, 0),
93  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
94  EndContainer(),
95 };
96 
97 
99 uint8_t _engine_sort_last_criteria[] = {0, 0, 0, 0};
100 bool _engine_sort_last_order[] = {false, false, false, false};
101 bool _engine_sort_show_hidden_engines[] = {false, false, false, false};
103 
111 {
112  int r = Engine::Get(a.engine_id)->list_position - Engine::Get(b.engine_id)->list_position;
113 
114  return _engine_sort_direction ? r > 0 : r < 0;
115 }
116 
124 {
125  const auto va = Engine::Get(a.engine_id)->intro_date;
126  const auto vb = Engine::Get(b.engine_id)->intro_date;
127  const auto r = va - vb;
128 
129  /* Use EngineID to sort instead since we want consistent sorting */
130  if (r == 0) return EngineNumberSorter(a, b);
131  return _engine_sort_direction ? r > 0 : r < 0;
132 }
133 
134 /* cached values for EngineNameSorter to spare many GetString() calls */
135 static EngineID _last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
136 
143 static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
144 {
145  static std::string last_name[2] = { {}, {} };
146 
147  if (a.engine_id != _last_engine[0]) {
148  _last_engine[0] = a.engine_id;
150  last_name[0] = GetString(STR_ENGINE_NAME);
151  }
152 
153  if (b.engine_id != _last_engine[1]) {
154  _last_engine[1] = b.engine_id;
156  last_name[1] = GetString(STR_ENGINE_NAME);
157  }
158 
159  int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
160 
161  /* Use EngineID to sort instead since we want consistent sorting */
162  if (r == 0) return EngineNumberSorter(a, b);
163  return _engine_sort_direction ? r > 0 : r < 0;
164 }
165 
173 {
174  const int va = Engine::Get(a.engine_id)->reliability;
175  const int vb = Engine::Get(b.engine_id)->reliability;
176  const int r = va - vb;
177 
178  /* Use EngineID to sort instead since we want consistent sorting */
179  if (r == 0) return EngineNumberSorter(a, b);
180  return _engine_sort_direction ? r > 0 : r < 0;
181 }
182 
189 static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
190 {
191  Money va = Engine::Get(a.engine_id)->GetCost();
192  Money vb = Engine::Get(b.engine_id)->GetCost();
193  int r = ClampTo<int32_t>(va - vb);
194 
195  /* Use EngineID to sort instead since we want consistent sorting */
196  if (r == 0) return EngineNumberSorter(a, b);
197  return _engine_sort_direction ? r > 0 : r < 0;
198 }
199 
207 {
208  int va = Engine::Get(a.engine_id)->GetDisplayMaxSpeed();
209  int vb = Engine::Get(b.engine_id)->GetDisplayMaxSpeed();
210  int r = va - vb;
211 
212  /* Use EngineID to sort instead since we want consistent sorting */
213  if (r == 0) return EngineNumberSorter(a, b);
214  return _engine_sort_direction ? r > 0 : r < 0;
215 }
216 
224 {
225  int va = Engine::Get(a.engine_id)->GetPower();
226  int vb = Engine::Get(b.engine_id)->GetPower();
227  int r = va - vb;
228 
229  /* Use EngineID to sort instead since we want consistent sorting */
230  if (r == 0) return EngineNumberSorter(a, b);
231  return _engine_sort_direction ? r > 0 : r < 0;
232 }
233 
241 {
242  int va = Engine::Get(a.engine_id)->GetDisplayMaxTractiveEffort();
243  int vb = Engine::Get(b.engine_id)->GetDisplayMaxTractiveEffort();
244  int r = va - vb;
245 
246  /* Use EngineID to sort instead since we want consistent sorting */
247  if (r == 0) return EngineNumberSorter(a, b);
248  return _engine_sort_direction ? r > 0 : r < 0;
249 }
250 
258 {
259  Money va = Engine::Get(a.engine_id)->GetRunningCost();
260  Money vb = Engine::Get(b.engine_id)->GetRunningCost();
261  int r = ClampTo<int32_t>(va - vb);
262 
263  /* Use EngineID to sort instead since we want consistent sorting */
264  if (r == 0) return EngineNumberSorter(a, b);
265  return _engine_sort_direction ? r > 0 : r < 0;
266 }
267 
275 {
276  const Engine *e_a = Engine::Get(a.engine_id);
277  const Engine *e_b = Engine::Get(b.engine_id);
278  uint p_a = e_a->GetPower();
279  uint p_b = e_b->GetPower();
280  Money r_a = e_a->GetRunningCost();
281  Money r_b = e_b->GetRunningCost();
282  /* Check if running cost is zero in one or both engines.
283  * If only one of them is zero then that one has higher value,
284  * else if both have zero cost then compare powers. */
285  if (r_a == 0) {
286  if (r_b == 0) {
287  /* If it is ambiguous which to return go with their ID */
288  if (p_a == p_b) return EngineNumberSorter(a, b);
289  return _engine_sort_direction != (p_a < p_b);
290  }
291  return !_engine_sort_direction;
292  }
293  if (r_b == 0) return _engine_sort_direction;
294  /* Using double for more precision when comparing close values.
295  * This shouldn't have any major effects in performance nor in keeping
296  * the game in sync between players since it's used in GUI only in client side */
297  double v_a = (double)p_a / (double)r_a;
298  double v_b = (double)p_b / (double)r_b;
299  /* Use EngineID to sort if both have same power/running cost,
300  * since we want consistent sorting.
301  * Also if both have no power then sort with reverse of running cost to simulate
302  * previous sorting behaviour for wagons. */
303  if (v_a == 0 && v_b == 0) return EngineRunningCostSorter(b, a);
304  if (v_a == v_b) return EngineNumberSorter(a, b);
305  return _engine_sort_direction != (v_a < v_b);
306 }
307 
308 /* Train sorting functions */
309 
317 {
318  const RailVehicleInfo *rvi_a = RailVehInfo(a.engine_id);
319  const RailVehicleInfo *rvi_b = RailVehInfo(b.engine_id);
320 
321  int va = GetTotalCapacityOfArticulatedParts(a.engine_id) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
322  int vb = GetTotalCapacityOfArticulatedParts(b.engine_id) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
323  int r = va - vb;
324 
325  /* Use EngineID to sort instead since we want consistent sorting */
326  if (r == 0) return EngineNumberSorter(a, b);
327  return _engine_sort_direction ? r > 0 : r < 0;
328 }
329 
337 {
338  int val_a = (RailVehInfo(a.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
339  int val_b = (RailVehInfo(b.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
340  int r = val_a - val_b;
341 
342  /* Use EngineID to sort instead since we want consistent sorting */
343  if (r == 0) return EngineNumberSorter(a, b);
344  return _engine_sort_direction ? r > 0 : r < 0;
345 }
346 
347 /* Road vehicle sorting functions */
348 
356 {
359  int r = va - vb;
360 
361  /* Use EngineID to sort instead since we want consistent sorting */
362  if (r == 0) return EngineNumberSorter(a, b);
363  return _engine_sort_direction ? r > 0 : r < 0;
364 }
365 
366 /* Ship vehicle sorting functions */
367 
375 {
376  const Engine *e_a = Engine::Get(a.engine_id);
377  const Engine *e_b = Engine::Get(b.engine_id);
378 
379  int va = e_a->GetDisplayDefaultCapacity();
380  int vb = e_b->GetDisplayDefaultCapacity();
381  int r = va - vb;
382 
383  /* Use EngineID to sort instead since we want consistent sorting */
384  if (r == 0) return EngineNumberSorter(a, b);
385  return _engine_sort_direction ? r > 0 : r < 0;
386 }
387 
388 /* Aircraft sorting functions */
389 
397 {
398  const Engine *e_a = Engine::Get(a.engine_id);
399  const Engine *e_b = Engine::Get(b.engine_id);
400 
401  uint16_t mail_a, mail_b;
402  int va = e_a->GetDisplayDefaultCapacity(&mail_a);
403  int vb = e_b->GetDisplayDefaultCapacity(&mail_b);
404  int r = va - vb;
405 
406  if (r == 0) {
407  /* The planes have the same passenger capacity. Check mail capacity instead */
408  r = mail_a - mail_b;
409 
410  if (r == 0) {
411  /* Use EngineID to sort instead since we want consistent sorting */
412  return EngineNumberSorter(a, b);
413  }
414  }
415  return _engine_sort_direction ? r > 0 : r < 0;
416 }
417 
425 {
426  uint16_t r_a = Engine::Get(a.engine_id)->GetRange();
427  uint16_t r_b = Engine::Get(b.engine_id)->GetRange();
428 
429  int r = r_a - r_b;
430 
431  /* Use EngineID to sort instead since we want consistent sorting */
432  if (r == 0) return EngineNumberSorter(a, b);
433  return _engine_sort_direction ? r > 0 : r < 0;
434 }
435 
438  /* Trains */
450 }, {
451  /* Road vehicles */
463 }, {
464  /* Ships */
473 }, {
474  /* Aircraft */
484 }};
485 
487 const std::initializer_list<const StringID> _engine_sort_listing[] = {{
488  /* Trains */
489  STR_SORT_BY_ENGINE_ID,
490  STR_SORT_BY_COST,
491  STR_SORT_BY_MAX_SPEED,
492  STR_SORT_BY_POWER,
493  STR_SORT_BY_TRACTIVE_EFFORT,
494  STR_SORT_BY_INTRO_DATE,
495  STR_SORT_BY_NAME,
496  STR_SORT_BY_RUNNING_COST,
497  STR_SORT_BY_POWER_VS_RUNNING_COST,
498  STR_SORT_BY_RELIABILITY,
499  STR_SORT_BY_CARGO_CAPACITY,
500 }, {
501  /* Road vehicles */
502  STR_SORT_BY_ENGINE_ID,
503  STR_SORT_BY_COST,
504  STR_SORT_BY_MAX_SPEED,
505  STR_SORT_BY_POWER,
506  STR_SORT_BY_TRACTIVE_EFFORT,
507  STR_SORT_BY_INTRO_DATE,
508  STR_SORT_BY_NAME,
509  STR_SORT_BY_RUNNING_COST,
510  STR_SORT_BY_POWER_VS_RUNNING_COST,
511  STR_SORT_BY_RELIABILITY,
512  STR_SORT_BY_CARGO_CAPACITY,
513 }, {
514  /* Ships */
515  STR_SORT_BY_ENGINE_ID,
516  STR_SORT_BY_COST,
517  STR_SORT_BY_MAX_SPEED,
518  STR_SORT_BY_INTRO_DATE,
519  STR_SORT_BY_NAME,
520  STR_SORT_BY_RUNNING_COST,
521  STR_SORT_BY_RELIABILITY,
522  STR_SORT_BY_CARGO_CAPACITY,
523 }, {
524  /* Aircraft */
525  STR_SORT_BY_ENGINE_ID,
526  STR_SORT_BY_COST,
527  STR_SORT_BY_MAX_SPEED,
528  STR_SORT_BY_INTRO_DATE,
529  STR_SORT_BY_NAME,
530  STR_SORT_BY_RUNNING_COST,
531  STR_SORT_BY_RELIABILITY,
532  STR_SORT_BY_CARGO_CAPACITY,
533  STR_SORT_BY_RANGE,
534 }};
535 
537 static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoID cid)
538 {
539  if (cid == CargoFilterCriteria::CF_ANY) {
540  return true;
541  } else if (cid == CargoFilterCriteria::CF_ENGINES) {
542  return Engine::Get(item->engine_id)->GetPower() != 0;
543  } else {
544  CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask;
545  return (cid == CargoFilterCriteria::CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
546  }
547 }
548 
549 static GUIEngineList::FilterFunction * const _engine_filter_funcs[] = {
551 };
552 
553 static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype)
554 {
555  uint weight = 0;
556  for (CargoID c = 0; c < NUM_CARGO; c++) {
557  if (cap[c] != 0) {
558  if (vtype == VEH_TRAIN) {
559  weight += CargoSpec::Get(c)->WeightOfNUnitsInTrain(cap[c]);
560  } else {
561  weight += CargoSpec::Get(c)->WeightOfNUnits(cap[c]);
562  }
563  }
564  }
565  return weight;
566 }
567 
568 static int DrawCargoCapacityInfo(int left, int right, int y, TestedEngineDetails &te, bool refittable)
569 {
570  for (const CargoSpec *cs : _sorted_cargo_specs) {
571  CargoID cid = cs->Index();
572  if (te.all_capacities[cid] == 0) continue;
573 
574  SetDParam(0, cid);
575  SetDParam(1, te.all_capacities[cid]);
576  SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
577  DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
579  }
580 
581  return y;
582 }
583 
584 /* Draw rail wagon specific details */
585 static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi, TestedEngineDetails &te)
586 {
587  const Engine *e = Engine::Get(engine_number);
588 
589  /* Purchase cost */
590  if (te.cost != 0) {
591  SetDParam(0, e->GetCost() + te.cost);
592  SetDParam(1, te.cost);
593  DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT);
594  } else {
595  SetDParam(0, e->GetCost());
596  DrawString(left, right, y, STR_PURCHASE_INFO_COST);
597  }
599 
600  /* Wagon weight - (including cargo) */
601  uint weight = e->GetDisplayWeight();
602  SetDParam(0, weight);
603  SetDParam(1, GetCargoWeight(te.all_capacities, VEH_TRAIN) + weight);
604  DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
606 
607  /* Wagon speed limit, displayed if above zero */
609  uint max_speed = e->GetDisplayMaxSpeed();
610  if (max_speed > 0) {
611  SetDParam(0, PackVelocity(max_speed, e->type));
612  DrawString(left, right, y, STR_PURCHASE_INFO_SPEED);
614  }
615  }
616 
617  /* Running cost */
618  if (rvi->running_cost_class != INVALID_PRICE) {
619  SetDParam(0, e->GetRunningCost());
620  DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR);
622  }
623 
624  return y;
625 }
626 
627 /* Draw locomotive specific details */
628 static int DrawRailEnginePurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi, TestedEngineDetails &te)
629 {
630  const Engine *e = Engine::Get(engine_number);
631 
632  /* Purchase Cost - Engine weight */
633  if (te.cost != 0) {
634  SetDParam(0, e->GetCost() + te.cost);
635  SetDParam(1, te.cost);
636  SetDParam(2, e->GetDisplayWeight());
637  DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_WEIGHT);
638  } else {
639  SetDParam(0, e->GetCost());
640  SetDParam(1, e->GetDisplayWeight());
641  DrawString(left, right, y, STR_PURCHASE_INFO_COST_WEIGHT);
642  }
644 
645  /* Max speed - Engine power */
647  SetDParam(1, e->GetPower());
648  DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER);
650 
651  /* Max tractive effort - not applicable if old acceleration or maglev */
654  DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE);
656  }
657 
658  /* Running cost */
659  if (rvi->running_cost_class != INVALID_PRICE) {
660  SetDParam(0, e->GetRunningCost());
661  DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR);
663  }
664 
665  /* Powered wagons power - Powered wagons extra weight */
666  if (rvi->pow_wag_power != 0) {
667  SetDParam(0, rvi->pow_wag_power);
668  SetDParam(1, rvi->pow_wag_weight);
669  DrawString(left, right, y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT);
671  }
672 
673  return y;
674 }
675 
676 /* Draw road vehicle specific details */
677 static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_number, TestedEngineDetails &te)
678 {
679  const Engine *e = Engine::Get(engine_number);
680 
682  /* Purchase Cost */
683  if (te.cost != 0) {
684  SetDParam(0, e->GetCost() + te.cost);
685  SetDParam(1, te.cost);
686  DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT);
687  } else {
688  SetDParam(0, e->GetCost());
689  DrawString(left, right, y, STR_PURCHASE_INFO_COST);
690  }
692 
693  /* Road vehicle weight - (including cargo) */
694  int16_t weight = e->GetDisplayWeight();
695  SetDParam(0, weight);
696  SetDParam(1, GetCargoWeight(te.all_capacities, VEH_ROAD) + weight);
697  DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
699 
700  /* Max speed - Engine power */
702  SetDParam(1, e->GetPower());
703  DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER);
705 
706  /* Max tractive effort */
708  DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE);
710  } else {
711  /* Purchase cost - Max speed */
712  if (te.cost != 0) {
713  SetDParam(0, e->GetCost() + te.cost);
714  SetDParam(1, te.cost);
716  DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_SPEED);
717  } else {
718  SetDParam(0, e->GetCost());
720  DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
721  }
723  }
724 
725  /* Running cost */
726  SetDParam(0, e->GetRunningCost());
727  DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR);
729 
730  return y;
731 }
732 
733 /* Draw ship specific details */
734 static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable, TestedEngineDetails &te)
735 {
736  const Engine *e = Engine::Get(engine_number);
737 
738  /* Purchase cost - Max speed */
739  uint raw_speed = e->GetDisplayMaxSpeed();
740  uint ocean_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, true);
741  uint canal_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, false);
742 
743  if (ocean_speed == canal_speed) {
744  if (te.cost != 0) {
745  SetDParam(0, e->GetCost() + te.cost);
746  SetDParam(1, te.cost);
747  SetDParam(2, PackVelocity(ocean_speed, e->type));
748  DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_SPEED);
749  } else {
750  SetDParam(0, e->GetCost());
751  SetDParam(1, PackVelocity(ocean_speed, e->type));
752  DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
753  }
755  } else {
756  if (te.cost != 0) {
757  SetDParam(0, e->GetCost() + te.cost);
758  SetDParam(1, te.cost);
759  DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT);
760  } else {
761  SetDParam(0, e->GetCost());
762  DrawString(left, right, y, STR_PURCHASE_INFO_COST);
763  }
765 
766  SetDParam(0, PackVelocity(ocean_speed, e->type));
767  DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_OCEAN);
769 
770  SetDParam(0, PackVelocity(canal_speed, e->type));
771  DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_CANAL);
773  }
774 
775  /* Cargo type + capacity */
776  SetDParam(0, te.cargo);
777  SetDParam(1, te.capacity);
778  SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
779  DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
781 
782  /* Running cost */
783  SetDParam(0, e->GetRunningCost());
784  DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR);
786 
787  return y;
788 }
789 
799 static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable, TestedEngineDetails &te)
800 {
801  const Engine *e = Engine::Get(engine_number);
802 
803  /* Purchase cost - Max speed */
804  if (te.cost != 0) {
805  SetDParam(0, e->GetCost() + te.cost);
806  SetDParam(1, te.cost);
808  DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_SPEED);
809  } else {
810  SetDParam(0, e->GetCost());
812  DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
813  }
815 
816  /* Cargo capacity */
817  if (te.mail_capacity > 0) {
818  SetDParam(0, te.cargo);
819  SetDParam(1, te.capacity);
820  SetDParam(2, GetCargoIDByLabel(CT_MAIL));
821  SetDParam(3, te.mail_capacity);
822  DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY);
823  } else {
824  /* Note, if the default capacity is selected by the refit capacity
825  * callback, then the capacity shown is likely to be incorrect. */
826  SetDParam(0, te.cargo);
827  SetDParam(1, te.capacity);
828  SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
829  DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
830  }
832 
833  /* Running cost */
834  SetDParam(0, e->GetRunningCost());
835  DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR);
837 
838  /* Aircraft type */
840  DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_TYPE);
842 
843  /* Aircraft range, if available. */
844  uint16_t range = e->GetRange();
845  if (range != 0) {
846  SetDParam(0, range);
847  DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_RANGE);
849  }
850 
851  return y;
852 }
853 
854 
860 static std::optional<std::string> GetNewGRFAdditionalText(EngineID engine)
861 {
862  uint16_t callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, nullptr);
863  if (callback == CALLBACK_FAILED || callback == 0x400) return std::nullopt;
864  const GRFFile *grffile = Engine::Get(engine)->GetGRF();
865  assert(grffile != nullptr);
866  if (callback > 0x400) {
867  ErrorUnknownCallbackResult(grffile->grfid, CBID_VEHICLE_ADDITIONAL_TEXT, callback);
868  return std::nullopt;
869  }
870 
871  StartTextRefStackUsage(grffile, 6);
872  std::string result = GetString(GetGRFStringID(grffile->grfid, 0xD000 + callback));
874  return result;
875 }
876 
885 static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
886 {
887  auto text = GetNewGRFAdditionalText(engine);
888  if (!text) return y;
889  return DrawStringMultiLine(left, right, y, INT32_MAX, *text, TC_BLACK);
890 }
891 
892 void TestedEngineDetails::FillDefaultCapacities(const Engine *e)
893 {
894  this->cargo = e->GetDefaultCargoType();
895  if (e->type == VEH_TRAIN || e->type == VEH_ROAD) {
897  this->capacity = this->all_capacities[this->cargo];
898  this->mail_capacity = 0;
899  } else {
901  this->all_capacities[this->cargo] = this->capacity;
902  if (IsValidCargoID(GetCargoIDByLabel(CT_MAIL))) {
903  this->all_capacities[GetCargoIDByLabel(CT_MAIL)] = this->mail_capacity;
904  } else {
905  this->mail_capacity = 0;
906  }
907  }
908  if (this->all_capacities.GetCount() == 0) this->cargo = INVALID_CARGO;
909 }
910 
917 int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, TestedEngineDetails &te)
918 {
919  const Engine *e = Engine::Get(engine_number);
920  TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(e->intro_date);
921  bool refittable = IsArticulatedVehicleRefittable(engine_number);
922  bool articulated_cargo = false;
923 
924  switch (e->type) {
925  default: NOT_REACHED();
926  case VEH_TRAIN:
927  if (e->u.rail.railveh_type == RAILVEH_WAGON) {
928  y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->u.rail, te);
929  } else {
930  y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->u.rail, te);
931  }
932  articulated_cargo = true;
933  break;
934 
935  case VEH_ROAD:
936  y = DrawRoadVehPurchaseInfo(left, right, y, engine_number, te);
937  articulated_cargo = true;
938  break;
939 
940  case VEH_SHIP:
941  y = DrawShipPurchaseInfo(left, right, y, engine_number, refittable, te);
942  break;
943 
944  case VEH_AIRCRAFT:
945  y = DrawAircraftPurchaseInfo(left, right, y, engine_number, refittable, te);
946  break;
947  }
948 
949  if (articulated_cargo) {
950  /* Cargo type + capacity, or N/A */
951  int new_y = DrawCargoCapacityInfo(left, right, y, te, refittable);
952 
953  if (new_y == y) {
954  SetDParam(0, INVALID_CARGO);
955  SetDParam(2, STR_EMPTY);
956  DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
958  } else {
959  y = new_y;
960  }
961  }
962 
963  /* Draw details that apply to all types except rail wagons. */
964  if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
965  /* Design date - Life length */
966  SetDParam(0, ymd.year);
968  DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE);
970 
971  /* Reliability */
973  DrawString(left, right, y, STR_PURCHASE_INFO_RELIABILITY);
975  }
976 
977  if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
978 
979  /* Additional text from NewGRF */
980  y = ShowAdditionalText(left, right, y, engine_number);
981 
982  /* The NewGRF's name which the vehicle comes from */
983  const GRFConfig *config = GetGRFConfig(e->GetGRFID());
984  if (_settings_client.gui.show_newgrf_name && config != nullptr)
985  {
986  DrawString(left, right, y, config->GetName(), TC_BLACK);
988  }
989 
990  return y;
991 }
992 
1003 void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group)
1004 {
1005  static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
1006 
1007  auto [first, last] = sb.GetVisibleRangeIterators(eng_list);
1008 
1009  bool rtl = _current_text_dir == TD_RTL;
1010  int step_size = GetEngineListHeight(type);
1011  int sprite_left = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_left;
1012  int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
1013  int sprite_width = sprite_left + sprite_right;
1014  int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
1015  int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
1016 
1017  Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix);
1018  int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
1019 
1020  Dimension replace_icon = {0, 0};
1021  int count_width = 0;
1022  if (show_count) {
1023  replace_icon = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
1024 
1025  uint biggest_num_engines = 0;
1026  for (auto it = first; it != last; ++it) {
1027  const uint num_engines = GetGroupNumEngines(_local_company, selected_group, it->engine_id);
1028  biggest_num_engines = std::max(biggest_num_engines, num_engines);
1029  }
1030 
1031  SetDParam(0, biggest_num_engines);
1032  count_width = GetStringBoundingBox(STR_JUST_COMMA, FS_SMALL).width;
1033  }
1034 
1035  Rect tr = ir.Indent(circle_width + WidgetDimensions::scaled.hsep_normal + sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
1036  Rect cr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl).WithWidth(count_width, !rtl); // Count position
1037  Rect rr = tr.WithWidth(replace_icon.width, !rtl); // Replace icon position
1038  if (show_count) tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal + replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl);
1039 
1040  int normal_text_y_offset = (ir.Height() - GetCharacterHeight(FS_NORMAL)) / 2;
1041  int small_text_y_offset = ir.Height() - GetCharacterHeight(FS_SMALL);
1042  int replace_icon_y_offset = (ir.Height() - replace_icon.height) / 2;
1043 
1044  const int offset = (rtl ? -circle_width : circle_width) / 2;
1046 
1047  int y = ir.top;
1048  for (auto it = first; it != last; ++it) {
1049  const auto &item = *it;
1050  uint indent = item.indent * WidgetDimensions::scaled.hsep_indent;
1051  bool has_variants = HasFlag(item.flags, EngineDisplayFlags::HasVariants);
1052  bool is_folded = HasFlag(item.flags, EngineDisplayFlags::IsFolded);
1053  bool shaded = HasFlag(item.flags, EngineDisplayFlags::Shaded);
1054 
1055  if (item.indent > 0) {
1056  /* Draw tree continuation lines. */
1057  int tx = (rtl ? ir.right : ir.left) + offset;
1058  int ty = y - WidgetDimensions::scaled.matrix.top;
1059  for (uint lvl = 1; lvl <= item.indent; ++lvl) {
1060  if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ty, tx, ty + step_size - 1, linecolour, WidgetDimensions::scaled.fullbevel.top);
1061  if (lvl < item.indent) tx += level_width;
1062  }
1063  /* Draw our node in the tree. */
1064  int ycentre = y + normal_text_y_offset + GetCharacterHeight(FS_NORMAL) / 2 - 1;
1065  if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ty, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
1066  GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
1067  }
1068 
1069  /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
1070  const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
1071 
1072  const Engine *e = Engine::Get(item.engine_id);
1073  bool hidden = HasBit(e->company_hidden, _local_company);
1074  StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
1075  TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : ((hidden | shaded) ? (TC_GREY | TC_FORCED | TC_NO_SHADE) : TC_BLACK);
1076 
1077  if (show_count) {
1078  /* relies on show_count to find 'Vehicle in use' panel of autoreplace window */
1080  } else {
1081  SetDParam(0, PackEngineNameDParam(item.engine_id, EngineNameContext::PurchaseList, item.indent));
1082  }
1083  Rect itr = tr.Indent(indent, rtl);
1084  DrawString(itr.left, itr.right, y + normal_text_y_offset, str, tc);
1085  int sprite_x = ir.Indent(indent + circle_width + WidgetDimensions::scaled.hsep_normal, rtl).WithWidth(sprite_width, rtl).left + sprite_left;
1086  DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, item.engine_id, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company), EIT_PURCHASE);
1087  if (show_count) {
1088  SetDParam(0, num_engines);
1089  DrawString(cr.left, cr.right, y + small_text_y_offset, STR_JUST_COMMA, TC_BLACK, SA_RIGHT | SA_FORCE, false, FS_SMALL);
1090  if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset);
1091  }
1092  if (has_variants) {
1093  Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl);
1094  DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, SA_CENTER);
1095  }
1096  y += step_size;
1097  }
1098 }
1099 
1107 void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, WidgetID button)
1108 {
1109  uint32_t hidden_mask = 0;
1110  /* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
1111  if (vehicle_type == VEH_ROAD && _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
1112  SetBit(hidden_mask, 3); // power
1113  SetBit(hidden_mask, 4); // tractive effort
1114  SetBit(hidden_mask, 8); // power by running costs
1115  }
1116  /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
1117  if (vehicle_type == VEH_TRAIN && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
1118  SetBit(hidden_mask, 4); // tractive effort
1119  }
1120  ShowDropDownMenu(w, _engine_sort_listing[vehicle_type], selected, button, 0, hidden_mask);
1121 }
1122 
1130 void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent, uint8_t indent)
1131 {
1132  for (const auto &item : src) {
1133  if (item.variant_id != parent || item.engine_id == parent) continue;
1134 
1135  const Engine *e = Engine::Get(item.engine_id);
1136  EngineDisplayFlags flags = item.flags;
1137  if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
1138  dst.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
1139 
1140  /* Add variants if not folded */
1142  /* Add this engine again as a child */
1143  if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) {
1144  dst.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
1145  }
1146  GUIEngineListAddChildren(dst, src, item.engine_id, indent + 1);
1147  }
1148  }
1149 
1150  if (indent > 0 || dst.empty()) return;
1151 
1152  /* Hierarchy is complete, traverse in reverse to find where indentation levels continue. */
1153  uint16_t level_mask = 0;
1154  for (auto it = std::rbegin(dst); std::next(it) != std::rend(dst); ++it) {
1155  auto next_it = std::next(it);
1156  SB(level_mask, it->indent, 1, it->indent <= next_it->indent);
1157  next_it->level_mask = level_mask;
1158  }
1159 }
1160 
1164 };
1165 
1169  union {
1174  uint8_t sort_criteria;
1179  GUIEngineList eng_list;
1182  Scrollbar *vscroll;
1184 
1187 
1188  void SetBuyVehicleText()
1189  {
1190  NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_BUILD);
1191 
1192  bool refit = this->sel_engine != INVALID_ENGINE && this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY && this->cargo_filter_criteria != CargoFilterCriteria::CF_NONE && this->cargo_filter_criteria != CargoFilterCriteria::CF_ENGINES;
1193  if (refit) refit = Engine::Get(this->sel_engine)->GetDefaultCargoType() != this->cargo_filter_criteria;
1194 
1195  if (refit) {
1196  widget->widget_data = STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type;
1197  widget->tool_tip = STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP + this->vehicle_type;
1198  } else {
1199  widget->widget_data = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type;
1200  widget->tool_tip = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + this->vehicle_type;
1201  }
1202  }
1203 
1205  {
1206  this->vehicle_type = type;
1207  this->listview_mode = tile == INVALID_TILE;
1208  this->window_number = this->listview_mode ? (int)type : tile.base();
1209 
1210  this->sel_engine = INVALID_ENGINE;
1211 
1212  this->sort_criteria = _engine_sort_last_criteria[type];
1213  this->descending_sort_order = _engine_sort_last_order[type];
1214  this->show_hidden_engines = _engine_sort_show_hidden_engines[type];
1215 
1216  this->UpdateFilterByTile();
1217 
1218  this->CreateNestedTree();
1219 
1220  this->vscroll = this->GetScrollbar(WID_BV_SCROLLBAR);
1221 
1222  /* If we are just viewing the list of vehicles, we do not need the Build button.
1223  * So we just hide it, and enlarge the Rename button by the now vacant place. */
1224  if (this->listview_mode) this->GetWidget<NWidgetStacked>(WID_BV_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE);
1225 
1226  NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_LIST);
1227  widget->tool_tip = STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type;
1228 
1229  widget = this->GetWidget<NWidgetCore>(WID_BV_SHOW_HIDE);
1230  widget->tool_tip = STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP + type;
1231 
1232  widget = this->GetWidget<NWidgetCore>(WID_BV_RENAME);
1233  widget->widget_data = STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type;
1234  widget->tool_tip = STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type;
1235 
1236  widget = this->GetWidget<NWidgetCore>(WID_BV_SHOW_HIDDEN_ENGINES);
1237  widget->widget_data = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + type;
1238  widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + type;
1239  widget->SetLowered(this->show_hidden_engines);
1240 
1241  this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
1242 
1243  if (tile == INVALID_TILE) {
1244  this->FinishInitNested(type);
1245  } else {
1246  this->FinishInitNested(tile);
1247  }
1248 
1249  this->querystrings[WID_BV_FILTER] = &this->vehicle_editbox;
1250  this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR;
1251 
1252  this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
1253 
1254  this->eng_list.ForceRebuild();
1255  this->GenerateBuildList(); // generate the list, since we need it in the next line
1256 
1257  /* Select the first unshaded engine in the list as default when opening the window */
1258  EngineID engine = INVALID_ENGINE;
1259  auto it = std::find_if(this->eng_list.begin(), this->eng_list.end(), [&](GUIEngineListItem &item) { return !HasFlag(item.flags, EngineDisplayFlags::Shaded); });
1260  if (it != this->eng_list.end()) engine = it->engine_id;
1261  this->SelectEngine(engine);
1262  }
1263 
1266  {
1267  switch (this->vehicle_type) {
1268  default: NOT_REACHED();
1269  case VEH_TRAIN:
1270  if (this->listview_mode) {
1271  this->filter.railtype = INVALID_RAILTYPE;
1272  } else {
1273  this->filter.railtype = GetRailType(this->window_number);
1274  }
1275  break;
1276 
1277  case VEH_ROAD:
1278  if (this->listview_mode) {
1279  this->filter.roadtype = INVALID_ROADTYPE;
1280  } else {
1281  this->filter.roadtype = GetRoadTypeRoad(this->window_number);
1282  if (this->filter.roadtype == INVALID_ROADTYPE) {
1283  this->filter.roadtype = GetRoadTypeTram(this->window_number);
1284  }
1285  }
1286  break;
1287 
1288  case VEH_SHIP:
1289  case VEH_AIRCRAFT:
1290  break;
1291  }
1292  }
1293 
1294  StringID GetCargoFilterLabel(CargoID cid) const
1295  {
1296  switch (cid) {
1297  case CargoFilterCriteria::CF_ANY: return STR_PURCHASE_INFO_ALL_TYPES;
1298  case CargoFilterCriteria::CF_ENGINES: return STR_PURCHASE_INFO_ENGINES_ONLY;
1299  case CargoFilterCriteria::CF_NONE: return STR_PURCHASE_INFO_NONE;
1300  default: return CargoSpec::Get(cid)->name;
1301  }
1302  }
1303 
1306  {
1307  /* Set the last cargo filter criteria. */
1308  this->cargo_filter_criteria = _engine_sort_last_cargo_criteria[this->vehicle_type];
1309  if (this->cargo_filter_criteria < NUM_CARGO && !HasBit(_standard_cargo_mask, this->cargo_filter_criteria)) this->cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
1310 
1311  this->eng_list.SetFilterFuncs(_engine_filter_funcs);
1312  this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
1313  }
1314 
1315  void SelectEngine(EngineID engine)
1316  {
1317  CargoID cargo = this->cargo_filter_criteria;
1318  if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
1319 
1320  this->sel_engine = engine;
1321  this->SetBuyVehicleText();
1322 
1323  if (this->sel_engine == INVALID_ENGINE) return;
1324 
1325  const Engine *e = Engine::Get(this->sel_engine);
1326 
1327  if (!this->listview_mode) {
1328  /* Query for cost and refitted capacity */
1329  auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
1330  if (ret.Succeeded()) {
1331  this->te.cost = ret.GetCost() - e->GetCost();
1332  this->te.capacity = refit_capacity;
1333  this->te.mail_capacity = refit_mail;
1334  this->te.cargo = !IsValidCargoID(cargo) ? e->GetDefaultCargoType() : cargo;
1335  this->te.all_capacities = cargo_capacities;
1336  return;
1337  }
1338  }
1339 
1340  /* Purchase test was not possible or failed, fill in the defaults instead. */
1341  this->te.cost = 0;
1342  this->te.FillDefaultCapacities(e);
1343  }
1344 
1345  void OnInit() override
1346  {
1347  this->SetCargoFilterArray();
1348  }
1349 
1352  {
1353  this->eng_list.Filter(this->cargo_filter_criteria);
1354  if (0 == this->eng_list.size()) { // no engine passed through the filter, invalidate the previously selected engine
1355  this->SelectEngine(INVALID_ENGINE);
1356  } else if (std::find(this->eng_list.begin(), this->eng_list.end(), this->sel_engine) == this->eng_list.end()) { // previously selected engine didn't pass the filter, select the first engine of the list
1357  this->SelectEngine(this->eng_list[0].engine_id);
1358  }
1359  }
1360 
1363  {
1364  GUIEngineListItem item = {eid, eid, EngineDisplayFlags::None, 0};
1365  return CargoAndEngineFilter(&item, this->cargo_filter_criteria);
1366  }
1367 
1369  bool FilterByText(const Engine *e)
1370  {
1371  /* Do not filter if the filter text box is empty */
1372  if (this->string_filter.IsEmpty()) return true;
1373 
1374  /* Filter engine name */
1375  this->string_filter.ResetState();
1377  this->string_filter.AddLine(GetString(STR_ENGINE_NAME));
1378 
1379  /* Filter NewGRF extra text */
1380  auto text = GetNewGRFAdditionalText(e->index);
1381  if (text) this->string_filter.AddLine(*text);
1382 
1383  return this->string_filter.GetState();
1384  }
1385 
1386  /* Figure out what train EngineIDs to put in the list */
1387  void GenerateBuildTrainList(GUIEngineList &list)
1388  {
1389  std::vector<EngineID> variants;
1390  EngineID sel_id = INVALID_ENGINE;
1391  size_t num_engines = 0;
1392 
1393  list.clear();
1394 
1395  /* Make list of all available train engines and wagons.
1396  * Also check to see if the previously selected engine is still available,
1397  * and if not, reset selection to INVALID_ENGINE. This could be the case
1398  * when engines become obsolete and are removed */
1399  for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
1400  if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1401  EngineID eid = e->index;
1402  const RailVehicleInfo *rvi = &e->u.rail;
1403 
1404  if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
1405  if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
1406 
1407  /* Filter now! So num_engines and num_wagons is valid */
1408  if (!FilterSingleEngine(eid)) continue;
1409 
1410  /* Filter by name or NewGRF extra text */
1411  if (!FilterByText(e)) continue;
1412 
1413  list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1414 
1415  if (rvi->railveh_type != RAILVEH_WAGON) num_engines++;
1416 
1417  /* Add all parent variants of this engine to the variant list */
1418  EngineID parent = e->info.variant_id;
1419  while (parent != INVALID_ENGINE) {
1420  variants.push_back(parent);
1421  parent = Engine::Get(parent)->info.variant_id;
1422  }
1423 
1424  if (eid == this->sel_engine) sel_id = eid;
1425  }
1426 
1427  /* ensure primary engine of variant group is in list */
1428  for (const auto &variant : variants) {
1429  if (std::find(list.begin(), list.end(), variant) == list.end()) {
1430  const Engine *e = Engine::Get(variant);
1431  list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
1432  if (e->u.rail.railveh_type != RAILVEH_WAGON) num_engines++;
1433  }
1434  }
1435 
1436  this->SelectEngine(sel_id);
1437 
1438  /* invalidate cached values for name sorter - engine names could change */
1439  _last_engine[0] = _last_engine[1] = INVALID_ENGINE;
1440 
1441  /* make engines first, and then wagons, sorted by selected sort_criteria */
1442  _engine_sort_direction = false;
1444 
1445  /* and then sort engines */
1447  EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
1448 
1449  /* and finally sort wagons */
1450  EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], num_engines, list.size() - num_engines);
1451  }
1452 
1453  /* Figure out what road vehicle EngineIDs to put in the list */
1454  void GenerateBuildRoadVehList()
1455  {
1456  EngineID sel_id = INVALID_ENGINE;
1457 
1458  this->eng_list.clear();
1459 
1460  for (const Engine *e : Engine::IterateType(VEH_ROAD)) {
1461  if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1462  EngineID eid = e->index;
1463  if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
1464  if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue;
1465 
1466  /* Filter by name or NewGRF extra text */
1467  if (!FilterByText(e)) continue;
1468 
1469  this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1470 
1471  if (eid == this->sel_engine) sel_id = eid;
1472  }
1473  this->SelectEngine(sel_id);
1474  }
1475 
1476  /* Figure out what ship EngineIDs to put in the list */
1477  void GenerateBuildShipList()
1478  {
1479  EngineID sel_id = INVALID_ENGINE;
1480  this->eng_list.clear();
1481 
1482  for (const Engine *e : Engine::IterateType(VEH_SHIP)) {
1483  if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1484  EngineID eid = e->index;
1485  if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
1486 
1487  /* Filter by name or NewGRF extra text */
1488  if (!FilterByText(e)) continue;
1489 
1490  this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1491 
1492  if (eid == this->sel_engine) sel_id = eid;
1493  }
1494  this->SelectEngine(sel_id);
1495  }
1496 
1497  /* Figure out what aircraft EngineIDs to put in the list */
1498  void GenerateBuildAircraftList()
1499  {
1500  EngineID sel_id = INVALID_ENGINE;
1501 
1502  this->eng_list.clear();
1503 
1504  const Station *st = this->listview_mode ? nullptr : Station::GetByTile(this->window_number);
1505 
1506  /* Make list of all available planes.
1507  * Also check to see if the previously selected plane is still available,
1508  * and if not, reset selection to INVALID_ENGINE. This could be the case
1509  * when planes become obsolete and are removed */
1510  for (const Engine *e : Engine::IterateType(VEH_AIRCRAFT)) {
1511  if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1512  EngineID eid = e->index;
1513  if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue;
1514  /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
1515  if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
1516 
1517  /* Filter by name or NewGRF extra text */
1518  if (!FilterByText(e)) continue;
1519 
1520  this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1521 
1522  if (eid == this->sel_engine) sel_id = eid;
1523  }
1524 
1525  this->SelectEngine(sel_id);
1526  }
1527 
1528  /* Generate the list of vehicles */
1529  void GenerateBuildList()
1530  {
1531  if (!this->eng_list.NeedRebuild()) return;
1532 
1533  /* Update filter type in case the road/railtype of the depot got converted */
1534  this->UpdateFilterByTile();
1535 
1536  this->eng_list.clear();
1537 
1538  GUIEngineList list;
1539 
1540  switch (this->vehicle_type) {
1541  default: NOT_REACHED();
1542  case VEH_TRAIN:
1543  this->GenerateBuildTrainList(list);
1544  GUIEngineListAddChildren(this->eng_list, list);
1545  this->eng_list.RebuildDone();
1546  return;
1547  case VEH_ROAD:
1548  this->GenerateBuildRoadVehList();
1549  break;
1550  case VEH_SHIP:
1551  this->GenerateBuildShipList();
1552  break;
1553  case VEH_AIRCRAFT:
1554  this->GenerateBuildAircraftList();
1555  break;
1556  }
1557 
1558  this->FilterEngineList();
1559 
1560  /* ensure primary engine of variant group is in list after filtering */
1561  std::vector<EngineID> variants;
1562  for (const auto &item : this->eng_list) {
1563  EngineID parent = item.variant_id;
1564  while (parent != INVALID_ENGINE) {
1565  variants.push_back(parent);
1566  parent = Engine::Get(parent)->info.variant_id;
1567  }
1568  }
1569 
1570  for (const auto &variant : variants) {
1571  if (std::find(this->eng_list.begin(), this->eng_list.end(), variant) == this->eng_list.end()) {
1572  const Engine *e = Engine::Get(variant);
1573  this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
1574  }
1575  }
1576 
1578  EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
1579 
1580  this->eng_list.swap(list);
1581  GUIEngineListAddChildren(this->eng_list, list, INVALID_ENGINE, 0);
1582  this->eng_list.RebuildDone();
1583  }
1584 
1585  DropDownList BuildCargoDropDownList() const
1586  {
1587  DropDownList list;
1588 
1589  /* Add item for disabling filtering. */
1590  list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
1591  /* Specific filters for trains. */
1592  if (this->vehicle_type == VEH_TRAIN) {
1593  /* Add item for locomotives only in case of trains. */
1594  list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ENGINES), CargoFilterCriteria::CF_ENGINES));
1595  /* Add item for vehicles not carrying anything, e.g. train engines.
1596  * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
1597  list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
1598  }
1599 
1600  /* Add cargos */
1602  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1603  list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
1604  }
1605 
1606  return list;
1607  }
1608 
1609  void BuildVehicle()
1610  {
1611  EngineID sel_eng = this->sel_engine;
1612  if (sel_eng == INVALID_ENGINE) return;
1613 
1614  CargoID cargo = this->cargo_filter_criteria;
1615  if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
1616  if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
1617  Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
1618  } else {
1619  Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
1620  }
1621 
1622  /* Update last used variant in hierarchy and refresh if necessary. */
1623  bool refresh = false;
1624  EngineID parent = sel_eng;
1625  while (parent != INVALID_ENGINE) {
1626  Engine *e = Engine::Get(parent);
1627  refresh |= (e->display_last_variant != sel_eng);
1628  e->display_last_variant = sel_eng;
1629  parent = e->info.variant_id;
1630  }
1631 
1632  if (refresh) {
1633  InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
1634  InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
1635  }
1636  }
1637 
1638  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1639  {
1640  switch (widget) {
1642  this->descending_sort_order ^= true;
1644  this->eng_list.ForceRebuild();
1645  this->SetDirty();
1646  break;
1647 
1649  this->show_hidden_engines ^= true;
1651  this->eng_list.ForceRebuild();
1652  this->SetWidgetLoweredState(widget, this->show_hidden_engines);
1653  this->SetDirty();
1654  break;
1655 
1656  case WID_BV_LIST: {
1658  const auto it = this->vscroll->GetScrolledItemFromWidget(this->eng_list, pt.y, this, WID_BV_LIST);
1659  if (it != this->eng_list.end()) {
1660  const auto &item = *it;
1661  const Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
1662  if (HasFlag(item.flags, EngineDisplayFlags::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) {
1663  /* toggle folded flag on engine */
1664  assert(item.variant_id != INVALID_ENGINE);
1665  Engine *engine = Engine::Get(item.variant_id);
1667 
1668  InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
1669  InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
1670  return;
1671  }
1672  if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) e = item.engine_id;
1673  }
1674  this->SelectEngine(e);
1675  this->SetDirty();
1676  if (_ctrl_pressed) {
1677  this->OnClick(pt, WID_BV_SHOW_HIDE, 1);
1678  } else if (click_count > 1 && !this->listview_mode) {
1679  this->OnClick(pt, WID_BV_BUILD, 1);
1680  }
1681  break;
1682  }
1683 
1684  case WID_BV_SORT_DROPDOWN: // Select sorting criteria dropdown menu
1685  DisplayVehicleSortDropDown(this, this->vehicle_type, this->sort_criteria, WID_BV_SORT_DROPDOWN);
1686  break;
1687 
1688  case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
1689  ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria, widget);
1690  break;
1691 
1692  case WID_BV_SHOW_HIDE: {
1693  const Engine *e = (this->sel_engine == INVALID_ENGINE) ? nullptr : Engine::Get(this->sel_engine);
1694  if (e != nullptr) {
1696  }
1697  break;
1698  }
1699 
1700  case WID_BV_BUILD:
1701  this->BuildVehicle();
1702  break;
1703 
1704  case WID_BV_RENAME: {
1705  EngineID sel_eng = this->sel_engine;
1706  if (sel_eng != INVALID_ENGINE) {
1707  this->rename_engine = sel_eng;
1709  ShowQueryString(STR_ENGINE_NAME, STR_QUERY_RENAME_TRAIN_TYPE_CAPTION + this->vehicle_type, MAX_LENGTH_ENGINE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
1710  }
1711  break;
1712  }
1713  }
1714  }
1715 
1721  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1722  {
1723  if (!gui_scope) return;
1724  /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
1725  if (this->vehicle_type == VEH_ROAD &&
1727  this->sort_criteria > 7) {
1728  this->sort_criteria = 0;
1730  }
1731  this->eng_list.ForceRebuild();
1732  }
1733 
1734  void SetStringParameters(WidgetID widget) const override
1735  {
1736  switch (widget) {
1737  case WID_BV_CAPTION:
1738  if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
1739  const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
1740  SetDParam(0, rti->strings.build_caption);
1741  } else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) {
1742  const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype);
1743  SetDParam(0, rti->strings.build_caption);
1744  } else {
1745  SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
1746  }
1747  break;
1748 
1749  case WID_BV_SORT_DROPDOWN:
1750  SetDParam(0, std::data(_engine_sort_listing[this->vehicle_type])[this->sort_criteria]);
1751  break;
1752 
1754  SetDParam(0, this->GetCargoFilterLabel(this->cargo_filter_criteria));
1755  break;
1756 
1757  case WID_BV_SHOW_HIDE: {
1758  const Engine *e = (this->sel_engine == INVALID_ENGINE) ? nullptr : Engine::Get(this->sel_engine);
1759  if (e != nullptr && e->IsHidden(_local_company)) {
1760  SetDParam(0, STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type);
1761  } else {
1762  SetDParam(0, STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
1763  }
1764  break;
1765  }
1766  }
1767  }
1768 
1769  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1770  {
1771  switch (widget) {
1772  case WID_BV_LIST:
1773  resize.height = GetEngineListHeight(this->vehicle_type);
1774  size.height = 3 * resize.height;
1775  size.width = std::max(size.width, GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
1776  break;
1777 
1778  case WID_BV_PANEL:
1779  size.height = GetCharacterHeight(FS_NORMAL) * this->details_height + padding.height;
1780  break;
1781 
1783  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1784  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1785  d.height += padding.height;
1786  size = maxdim(size, d);
1787  break;
1788  }
1789 
1791  size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width);
1792  break;
1793 
1794  case WID_BV_BUILD:
1795  size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type);
1796  size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type));
1797  size.width += padding.width;
1798  size.height += padding.height;
1799  break;
1800 
1801  case WID_BV_SHOW_HIDE:
1802  size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
1803  size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type));
1804  size.width += padding.width;
1805  size.height += padding.height;
1806  break;
1807  }
1808  }
1809 
1810  void DrawWidget(const Rect &r, WidgetID widget) const override
1811  {
1812  switch (widget) {
1813  case WID_BV_LIST:
1815  this->vehicle_type,
1816  r,
1817  this->eng_list,
1818  *this->vscroll,
1819  this->sel_engine,
1820  false,
1822  );
1823  break;
1824 
1826  this->DrawSortButtonState(WID_BV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
1827  break;
1828  }
1829  }
1830 
1831  void OnPaint() override
1832  {
1833  this->GenerateBuildList();
1834  this->vscroll->SetCount(this->eng_list.size());
1835 
1837 
1838  /* Disable renaming engines in network games if you are not the server. */
1839  this->SetWidgetDisabledState(WID_BV_RENAME, this->sel_engine == INVALID_ENGINE || (_networking && !_network_server));
1840 
1841  this->DrawWidgets();
1842 
1843  if (!this->IsShaded()) {
1844  int needed_height = this->details_height;
1845  /* Draw details panels. */
1846  if (this->sel_engine != INVALID_ENGINE) {
1847  const Rect r = this->GetWidget<NWidgetBase>(WID_BV_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
1848  int text_end = DrawVehiclePurchaseInfo(r.left, r.right, r.top, this->sel_engine, this->te);
1849  needed_height = std::max(needed_height, (text_end - r.top) / GetCharacterHeight(FS_NORMAL));
1850  }
1851  if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
1852  int resize = needed_height - this->details_height;
1853  this->details_height = needed_height;
1854  this->ReInit(0, resize * GetCharacterHeight(FS_NORMAL));
1855  return;
1856  }
1857  }
1858  }
1859 
1860  void OnQueryTextFinished(std::optional<std::string> str) override
1861  {
1862  if (!str.has_value()) return;
1863 
1864  Command<CMD_RENAME_ENGINE>::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, *str);
1865  }
1866 
1867  void OnDropdownSelect(WidgetID widget, int index) override
1868  {
1869  switch (widget) {
1870  case WID_BV_SORT_DROPDOWN:
1871  if (this->sort_criteria != index) {
1872  this->sort_criteria = index;
1874  this->eng_list.ForceRebuild();
1875  }
1876  break;
1877 
1878  case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
1879  if (this->cargo_filter_criteria != index) {
1880  this->cargo_filter_criteria = index;
1882  /* deactivate filter if criteria is 'Show All', activate it otherwise */
1883  this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
1884  this->eng_list.ForceRebuild();
1885  this->SelectEngine(this->sel_engine);
1886  }
1887  break;
1888  }
1889  this->SetDirty();
1890  }
1891 
1892  void OnResize() override
1893  {
1894  this->vscroll->SetCapacityFromWidget(this, WID_BV_LIST);
1895  }
1896 
1897  void OnEditboxChanged(WidgetID wid) override
1898  {
1899  if (wid == WID_BV_FILTER) {
1900  this->string_filter.SetFilterTerm(this->vehicle_editbox.text.buf);
1901  this->InvalidateData();
1902  }
1903  }
1904 
1905  EventState OnHotkey(int hotkey) override
1906  {
1907  switch (hotkey) {
1908  case BVHK_FOCUS_FILTER_BOX:
1910  SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
1911  return ES_HANDLED;
1912 
1913  default:
1914  return ES_NOT_HANDLED;
1915  }
1916 
1917  return ES_HANDLED;
1918  }
1919 
1920  static inline HotkeyList hotkeys{"buildvehicle", {
1921  Hotkey('F', "focus_filter_box", BVHK_FOCUS_FILTER_BOX),
1922  }};
1923 };
1924 
1925 static WindowDesc _build_vehicle_desc(
1926  WDP_AUTO, "build_vehicle", 240, 268,
1929  _nested_build_vehicle_widgets,
1930  &BuildVehicleWindow::hotkeys
1931 );
1932 
1933 void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
1934 {
1935  /* We want to be able to open both Available Train as Available Ships,
1936  * so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
1937  * As it always is a low value, it won't collide with any real tile
1938  * number. */
1939  uint num = (tile == INVALID_TILE) ? (int)type : tile.base();
1940 
1941  assert(IsCompanyBuildableVehicleType(type));
1942 
1944 
1945  new BuildVehicleWindow(_build_vehicle_desc, tile, type);
1946 }
CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
Ors the refit_masks of all articulated parts.
CargoArray GetCapacityOfArticulatedParts(EngineID engine)
Get the capacity of the parts of a given engine.
bool IsArticulatedVehicleRefittable(EngineID engine)
Checks whether any of the articulated parts is refittable.
Functions related to articulated vehicles.
Functions related to autoreplacing.
bool EngineHasReplacementForCompany(const Company *c, EngineID engine, GroupID group)
Check if a company has a replacement set up for the given engine.
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by introduction date.
static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by purchase cost.
void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent, uint8_t indent)
Add children to GUI engine list to build a hierarchical tree.
static bool TrainEnginesThenWagonsSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of train engines by engine / wagon.
static std::optional< std::string > GetNewGRFAdditionalText(EngineID engine)
Try to get the NewGRF engine additional text callback as an optional std::string.
BuildVehicleHotkeys
Enum referring to the Hotkeys in the build vehicle window.
@ BVHK_FOCUS_FILTER_BOX
Focus the edit box for editing the filter string.
static bool AircraftEngineCargoSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of aircraft by cargo.
bool _engine_sort_last_order[]
Last set direction of the sort order, for each vehicle type.
static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by engineID.
static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoID cid)
Filters vehicles by cargo and engine (in case of rail vehicle).
uint GetEngineListHeight(VehicleType type)
Get the height of a single 'entry' in the engine lists.
static bool TrainEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of train engines by capacity.
static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable, TestedEngineDetails &te)
Draw aircraft specific details in the buy window.
static bool EnginePowerVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by running costs.
static bool EngineReliabilitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by reliability.
const std::initializer_list< const StringID > _engine_sort_listing[]
Dropdown menu strings for the vehicle sort criteria.
uint8_t _engine_sort_last_criteria[]
Last set sort criteria, for each vehicle type.
static bool ShipEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of ships by capacity.
int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, TestedEngineDetails &te)
Draw the purchase info details of a vehicle at a given location.
static bool EngineTractiveEffortSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by tractive effort.
EngList_SortTypeFunction *const _engine_sort_functions[][11]
Sort functions for the vehicle sort criteria, for each vehicle type.
void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, WidgetID button)
Display the dropdown for the vehicle sort criteria.
static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
Display additional text from NewGRF in the purchase information window.
static bool EngineSpeedSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by speed.
bool _engine_sort_direction
false = descending, true = ascending.
static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by name.
static bool EnginePowerSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by power.
static bool EngineRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by running costs.
static bool AircraftRangeSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of aircraft by range.
static CargoID _engine_sort_last_cargo_criteria[]
Last set filter criteria, for each vehicle type.
static bool RoadVehEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of road vehicles by capacity.
bool _engine_sort_show_hidden_engines[]
Last set 'show hidden engines' setting for each vehicle type.
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group)
Engine drawing loop.
Types related to the build_vehicle widgets.
@ WID_BV_BUILD
Build panel.
@ WID_BV_SHOW_HIDE
Button to hide or show the selected engine.
@ WID_BV_SORT_ASCENDING_DESCENDING
Sort direction.
@ WID_BV_CAPTION
Caption of window.
@ WID_BV_SHOW_HIDDEN_ENGINES
Toggle whether to display the hidden vehicles.
@ WID_BV_LIST
List of vehicles.
@ WID_BV_SORT_DROPDOWN
Criteria of sorting dropdown.
@ WID_BV_RENAME
Rename button.
@ WID_BV_SCROLLBAR
Scrollbar of list.
@ WID_BV_BUILD_SEL
Build button.
@ WID_BV_PANEL
Button panel.
@ WID_BV_FILTER
Filter by name.
@ WID_BV_CARGO_FILTER_DROPDOWN
Cargo filter dropdown.
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:22
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition: cargo_type.h:107
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
Definition: cargo_type.h:74
Dimension GetLargestCargoIconSize()
Get dimensions of largest cargo icon.
Definition: cargotype.cpp:153
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:181
CargoTypes _standard_cargo_mask
Bitmask of real cargo types available.
Definition: cargotype.cpp:36
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:180
Types/functions related to cargoes.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetFilterState(bool state)
Enable or disable the filter.
bool(const GUIEngineListItem *, CargoID) FilterFunction
Signature of filter function.
Definition: sortlist_type.h:50
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
Base class for a 'real' widget.
Definition: widget_type.h:370
uint32_t widget_data
Data of the widget.
Definition: widget_type.h:393
void SetLowered(bool lowered)
Lower or raise the widget.
Definition: widget_type.h:428
StringID tool_tip
Tooltip of the widget.
Definition: widget_type.h:394
This struct contains all the info that is needed to draw and construct tracks.
Definition: rail.h:127
struct RailTypeInfo::@26 strings
Strings associated with the rail type.
StringID build_caption
Caption of the build vehicle GUI for this rail type.
Definition: rail.h:179
uint8_t acceleration_type
Acceleration type of this rail type.
Definition: rail.h:226
struct RoadTypeInfo::@29 strings
Strings associated with the rail type.
StringID build_caption
Caption of the build vehicle GUI for this rail type.
Definition: road.h:106
Scrollbar data structure.
Definition: widget_type.h:694
void SetCount(size_t num)
Sets the number of elements in the list.
Definition: widget_type.h:780
auto GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Return an iterator pointing to the element of a scrolled widget that a user clicked in.
Definition: widget_type.h:879
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition: widget.cpp:2394
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
Definition: widget_type.h:860
static YearMonthDay ConvertDateToYMD(Date date)
Converts a Date to a Year, Month & Day.
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static constexpr Year DateToYear(Date date)
Calculate the year of a given date.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition: window_gui.h:68
int hsep_wide
Wide horizontal spacing.
Definition: window_gui.h:64
RectPadding matrix
Padding of WWT_MATRIX items.
Definition: window_gui.h:44
int hsep_indent
Width of identation for tree layouts.
Definition: window_gui.h:65
Functions related to commands.
@ DC_QUERY_COST
query cost only, don't build.
Definition: command_type.h:378
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:52
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:53
Functions related to companies.
VehicleCellSize GetVehicleImageCellSize(VehicleType type, EngineImageType image_type)
Get the GUI cell size for a vehicle image.
Definition: depot_gui.cpp:161
void ShowDropDownMenu(Window *w, std::span< const StringID > strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
Show a dropdown menu window near a widget of the parent window.
Definition: dropdown.cpp:441
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition: dropdown.cpp:404
Dimension GetDropDownListDimension(const DropDownList &list)
Determine width and height required to fully display a DropDownList.
Definition: dropdown.cpp:363
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
Definition: dropdown_type.h:50
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition: engine.cpp:1218
Base class for engines.
EngineDisplayFlags
Flags used client-side in the purchase/autorenew engine list.
Definition: engine_base.h:26
@ HasVariants
Set if engine has variants.
@ IsFolded
Set if display of variants should be folded (hidden).
@ None
No flag set.
@ Shaded
Set if engine should be masked.
Command definitions related to engines.
Functions related to engines.
uint GetTotalCapacityOfArticulatedParts(EngineID engine)
Get the capacity of an engine with articulated parts.
Definition: engine_gui.cpp:165
void EngList_Sort(GUIEngineList &el, EngList_SortTypeFunction compare)
Sort all items using quick sort and given 'CompareItems' function.
Definition: engine_gui.cpp:333
void EngList_SortPartial(GUIEngineList &el, EngList_SortTypeFunction compare, size_t begin, size_t num_items)
Sort selected range of items (on indices @ <begin, begin+num_items-1>)
Definition: engine_gui.cpp:346
void DrawVehicleEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
Draw an engine.
Definition: engine_gui.cpp:303
Engine GUI functions, used by build_vehicle_gui and autoreplace_gui
bool EngList_SortTypeFunction(const GUIEngineListItem &, const GUIEngineListItem &)
argument type for EngList_Sort.
Definition: engine_gui.h:35
@ RAILVEH_WAGON
simple wagon, not motorized
Definition: engine_type.h:29
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition: engine_type.h:28
static const uint MAX_LENGTH_ENGINE_NAME_CHARS
The maximum length of an engine name in characters including '\0'.
Definition: engine_type.h:204
static const EngineID INVALID_ENGINE
Constant denoting an invalid engine.
Definition: engine_type.h:206
uint16_t EngineID
Unique identification number of an engine.
Definition: engine_type.h:21
uint64_t PackEngineNameDParam(EngineID engine_id, EngineNameContext context, uint32_t extra_data=0)
Combine an engine ID and a name context to an engine name dparam.
Definition: engine_type.h:199
@ AutoreplaceVehicleInUse
Name is show in the autoreplace window 'Vehicles in use' panel.
Definition: engine_type.h:195
@ Generic
No specific context available.
Definition: engine_type.h:191
@ PurchaseList
Name is shown in the purchase list (including autoreplace window 'Available vehicles' panel).
Definition: engine_type.h:193
constexpr debug_inline bool HasFlag(const T x, const T y)
Checks if a value in a bitset enum is set.
Definition: enum_type.hpp:58
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition: fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition: gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:851
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition: gfx.cpp:657
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:38
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition: gfx.cpp:988
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition: gfx.cpp:774
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition: widget.cpp:54
@ SA_RIGHT
Right align the text (must be a single bit).
Definition: gfx_type.h:345
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition: gfx_type.h:355
@ SA_CENTER
Center both horizontally and vertically.
Definition: gfx_type.h:353
@ FS_SMALL
Index of the small font in the font tables.
Definition: gfx_type.h:210
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:209
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition: gfx_type.h:260
@ TC_FORCED
Ignore colour changes from strings.
Definition: gfx_type.h:285
@ TC_NO_SHADE
Do not add shading to this text colour.
Definition: gfx_type.h:284
Base class for groups and group functions.
uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e)
Get the number of engines with EngineID id_e in the group with GroupID id_g and its sub-groups.
Definition: group_cmd.cpp:799
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
Definition: widget_type.h:1181
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1284
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
Definition: widget_type.h:1228
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1202
constexpr NWidgetPart SetTextStyle(TextColour colour, FontSize size=FS_NORMAL)
Widget part function for setting the text style.
Definition: widget_type.h:1160
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
Definition: widget_type.h:1137
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
Definition: widget_type.h:1309
constexpr NWidgetPart SetMatrixDataTip(uint8_t cols, uint8_t rows, StringID tip)
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
Definition: widget_type.h:1214
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition: widget_type.h:1191
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition: widget_type.h:1126
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition: window.cpp:940
uint16_t GroupID
Type for all group identifiers.
Definition: group_type.h:13
static const GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition: group_type.h:17
Hotkey related functions.
constexpr uint ToPercent16(uint i)
Converts a "fract" value 0..65535 to "percent" value 0..100.
Definition: math_func.hpp:306
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
Definition: math_func.hpp:268
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:1079
static constexpr CargoID CF_NONE
Show only items which do not carry cargo (e.g. train engines)
Definition: cargo_type.h:95
static constexpr CargoID CF_ANY
Show all items independent of carried cargo (i.e. no filtering)
Definition: cargo_type.h:94
static constexpr CargoID CF_ENGINES
Show only engines (for rail vehicles only)
Definition: cargo_type.h:96
bool _networking
are we in networking mode?
Definition: network.cpp:65
bool _network_server
network-server is active
Definition: network.cpp:66
Basic functions/variables used all over the place.
@ INVALID_CLIENT_ID
Client is not part of anything.
Definition: network_type.h:50
@ CBID_VEHICLE_ADDITIONAL_TEXT
This callback is called from vehicle purchase lists.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
GRFConfig * GetGRFConfig(uint32_t grfid, uint32_t mask)
Retrieve a NewGRF from the current config by its grfid.
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v)
Evaluate a newgrf callback for vehicles.
Functions for NewGRF engines.
void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values)
Start using the TTDP compatible string code parsing.
StringID GetGRFStringID(uint32_t grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
Header of Action 04 "universal holder" structure and functions.
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition: palette.cpp:314
Base for the GUIs that have an edit box in them.
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition: rail.h:307
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition: rail.h:335
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition: rail_map.h:115
RailType
Enumeration for all possible railtypes.
Definition: rail_type.h:27
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition: rail_type.h:34
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition: road.h:227
bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
Checks if an engine of the given RoadType got power on a tile with a given RoadType.
Definition: road.h:242
RoadType
The different roadtypes we support.
Definition: road_type.h:25
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition: road_type.h:30
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:57
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:56
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition: sprites.h:1605
Base classes/functions for stations.
Definition of base types and functions in a cross-platform compatible way.
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition: string.cpp:589
Functions related to low-level strings.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition: string_type.h:25
Searching and filtering using a stringterm.
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings.cpp:104
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition: strings.cpp:319
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:56
Functions related to OTTD's strings.
int64_t PackVelocity(uint speed, VehicleType type)
Pack velocity and vehicle type for use with SCC_VELOCITY string parameter.
Definition: strings_func.h:75
@ TD_RTL
Text is written right-to-left by default.
Definition: strings_type.h:24
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
static const int MAX_CHAR_LENGTH
Max. length of UTF-8 encoded unicode character.
Definition: strings_type.h:18
GUI for building vehicles.
CargoID cargo_filter_criteria
Selected cargo filter.
VehicleType vehicle_type
Type of vehicles shown in the window.
bool FilterByText(const Engine *e)
Filter by name and NewGRF extra text.
void UpdateFilterByTile()
Set the filter type according to the depot type.
bool descending_sort_order
Sort direction,.
bool listview_mode
If set, only display the available vehicles and do not show a 'build' button.
void SetCargoFilterArray()
Populate the filter list and set the cargo filter criteria.
RailType railtype
Rail type to show, or INVALID_RAILTYPE.
void OnPaint() override
The window must be repainted.
int details_height
Minimal needed height of the details panels, in text lines (found so far).
QueryString vehicle_editbox
Filter editbox.
EventState OnHotkey(int hotkey) override
A hotkey has been pressed.
union BuildVehicleWindow::@0 filter
Filter to apply.
void OnResize() override
Called after the window got resized.
bool show_hidden_engines
State of the 'show hidden engines' button.
void FilterEngineList()
Filter the engine list against the currently selected cargo filter.
EngineID rename_engine
Engine being renamed.
TestedEngineDetails te
Tested cost and capacity after refit.
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
StringFilter string_filter
Filter for vehicle name.
uint8_t sort_criteria
Current sort criterium.
bool FilterSingleEngine(EngineID eid)
Filter a single engine.
EngineID sel_engine
Currently selected engine, or INVALID_ENGINE.
RoadType roadtype
Road type to show, or INVALID_ROADTYPE.
void OnInit() override
Notification that the nested widget tree gets initialized.
Class for storing amounts of cargo.
Definition: cargo_type.h:114
uint GetCount() const
Get the amount of cargos that have an amount.
Definition: cargo_type.h:129
Specification of a cargo type.
Definition: cargotype.h:71
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:134
StringID name
Name of this type of cargo.
Definition: cargotype.h:88
GUISettings gui
settings related to the GUI
Dimensions (a width and height) of a rectangle in 2D.
EngineID variant_id
Engine variant ID. If set, will be treated specially in purchase lists.
Definition: engine_type.h:160
StringID GetAircraftTypeText() const
Get the name of the aircraft type for display purposes.
Definition: engine.cpp:467
uint GetPower() const
Returns the power of the engine for display and sorting purposes.
Definition: engine.cpp:390
CargoID GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition: engine_base.h:96
uint16_t GetRange() const
Get the range of an aircraft type.
Definition: engine.cpp:453
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition: engine.cpp:157
Money GetCost() const
Return how much a new engine costs.
Definition: engine.cpp:318
TimerGameCalendar::Date intro_date
Date of introduction of the engine.
Definition: engine_base.h:39
uint GetDisplayMaxSpeed() const
Returns max speed of the engine for display purposes.
Definition: engine.cpp:358
EngineDisplayFlags display_flags
NOSAVE client-side-only display flags for build engine list.
Definition: engine_base.h:58
uint GetDisplayWeight() const
Returns the weight of the engine for display purposes.
Definition: engine.cpp:408
VehicleType type
Vehicle type, ie VEH_ROAD, VEH_TRAIN, etc.
Definition: engine_base.h:56
bool IsVariantHidden(CompanyID c) const
Check whether the engine variant chain is hidden in the GUI for the given company.
Definition: engine.cpp:487
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition: engine.cpp:443
uint GetDisplayDefaultCapacity(uint16_t *mail_capacity=nullptr) const
Determines the default cargo capacity of an engine for display purposes.
Definition: engine_base.h:116
CompanyMask company_hidden
Bit for each company whether the engine is normally hidden in the build gui for that company.
Definition: engine_base.h:54
Money GetRunningCost() const
Return how much the running costs of this engine are.
Definition: engine.cpp:281
uint16_t reliability
Current reliability of the engine.
Definition: engine_base.h:41
uint GetDisplayMaxTractiveEffort() const
Returns the tractive effort of the engine for display purposes.
Definition: engine.cpp:426
bool IsHidden(CompanyID c) const
Check whether the engine is hidden in the GUI for the given company.
Definition: engine_base.h:136
static Pool::IterateWrapperFiltered< Engine, EngineTypeFilter > IterateType(VehicleType vt, size_t from=0)
Returns an iterable ensemble of all valid engines of the given type.
Definition: engine_base.h:186
EngineID display_last_variant
NOSAVE client-side-only last variant selected.
Definition: engine_base.h:59
Information about GRF, used in the game and (part of it) in savegames.
const char * GetName() const
Get the name of this grf.
Dynamic data of a loaded NewGRF.
Definition: newgrf.h:108
EngineID engine_id
Engine to display in build purchase list.
Definition: engine_gui.h:21
bool show_newgrf_name
Show the name of the NewGRF in the build vehicle window.
VehicleSettings vehicle
options for vehicles
List of hotkeys for a window.
Definition: hotkeys.h:37
All data for a single hotkey.
Definition: hotkeys.h:21
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:1075
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
Data stored about a string that can be modified in the GUI.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
static const int ACTION_CLEAR
Clear editbox.
Information about a rail vehicle.
Definition: engine_type.h:42
uint16_t pow_wag_power
Extra power applied to consist if wagon should be powered.
Definition: engine_type.h:56
RailType railtype
Railtype, mangled if elrail is disabled.
Definition: engine_type.h:46
uint8_t pow_wag_weight
Extra weight applied to consist if wagon should be powered.
Definition: engine_type.h:57
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
int Height() const
Get height of Rect.
RoadType roadtype
Road type.
Definition: engine_type.h:128
uint ApplyWaterClassSpeedFrac(uint raw_speed, bool is_ocean) const
Apply ocean/canal speed fraction to a velocity.
Definition: engine_type.h:81
static Station * GetByTile(TileIndex tile)
Get the station belonging to a specific tile.
Station data structure.
Definition: station_base.h:439
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(const char *str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
void AddLine(const char *str)
Pass another text line from the current item to the filter.
bool GetState() const
Get the matching state of the current item.
Extra information about refitted cargo and capacity.
Definition: vehicle_gui.h:42
CargoID cargo
Cargo type.
Definition: vehicle_gui.h:44
Money cost
Refit cost.
Definition: vehicle_gui.h:43
CargoArray all_capacities
Capacities for all cargoes.
Definition: vehicle_gui.h:47
uint16_t mail_capacity
Mail capacity if available.
Definition: vehicle_gui.h:46
uint capacity
Cargo capacity.
Definition: vehicle_gui.h:45
char *const buf
buffer in which text is saved
Definition: textbuf_type.h:32
uint extend_left
Extend of the cell to the left.
Definition: vehicle_gui.h:85
uint extend_right
Extend of the cell to the right.
Definition: vehicle_gui.h:86
uint8_t roadveh_acceleration_model
realistic acceleration for road vehicles
uint8_t train_acceleration_model
realistic acceleration for trains
bool wagon_speed_limits
enable wagon speed limits
High level window description.
Definition: window_gui.h:159
Data structure for an opened window.
Definition: window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition: window.cpp:952
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition: widget.cpp:780
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition: window.cpp:1733
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition: window_gui.h:320
void DrawWidgets() const
Paint all widgets of a window.
Definition: widget.cpp:731
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition: window.cpp:3151
Window * parent
Parent window.
Definition: window_gui.h:328
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition: widget.cpp:763
ResizeInfo resize
Resize information.
Definition: window_gui.h:314
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition: window_gui.h:521
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition: window.cpp:1723
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition: window.cpp:486
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition: window_gui.h:316
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition: window_gui.h:447
bool IsShaded() const
Is window shaded currently?
Definition: window_gui.h:563
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition: window.cpp:314
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition: window_gui.h:387
int width
width of the window (number of pixels to the right in x direction)
Definition: window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition: window_gui.h:302
Stuff related to the text buffer GUI.
@ QSF_ENABLE_DEFAULT
enable the 'Default' button ("\0" is returned)
Definition: textbuf_gui.h:21
@ QSF_LEN_IN_CHARS
the length of the string is counted in characters
Definition: textbuf_gui.h:22
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition: tile_map.h:178
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition: tile_type.h:95
Definition of the game-calendar-timer.
Command definitions related to trains.
void CcBuildWagon(Commands cmd, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray, TileIndex tile, EngineID, bool, CargoID, ClientID)
Callback for building wagons.
Definition: train_gui.cpp:29
PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
Get the colour map for an engine.
Definition: vehicle.cpp:2142
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition: vehicle.cpp:3058
Command definitions for vehicles.
Functions related to vehicles.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
Definition: vehicle_func.h:91
void CcBuildPrimaryVehicle(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray)
This is the Callback method after the construction attempt of a primary vehicle.
uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
Display list of cargo types of the engine, for the purchase information window.
Functions related to the vehicle's GUIs.
@ EIT_PURCHASE
Vehicle drawn in purchase list, autoreplace gui, ...
Definition: vehicle_type.h:83
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
@ VEH_ROAD
Road vehicle type.
Definition: vehicle_type.h:25
@ VEH_AIRCRAFT
Aircraft vehicle type.
Definition: vehicle_type.h:27
@ VEH_SHIP
Ship vehicle type.
Definition: vehicle_type.h:26
@ VEH_TRAIN
Train vehicle type.
Definition: vehicle_type.h:24
static const uint MAX_LENGTH_VEHICLE_NAME_CHARS
The maximum length of a vehicle name in characters including '\0'.
Definition: vehicle_type.h:66
@ SZSP_NONE
Display plane with zero size in both directions (none filling and resizing).
Definition: widget_type.h:483
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:112
@ WWT_EDITBOX
a textbox for typing
Definition: widget_type.h:71
@ NWID_HORIZONTAL
Horizontal container.
Definition: widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition: widget_type.h:55
@ WWT_PANEL
Simple depressed panel.
Definition: widget_type.h:50
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:66
@ WWT_MATRIX
Grid of rows and columns.
Definition: widget_type.h:59
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition: widget_type.h:84
@ NWID_VERTICAL
Vertical container.
Definition: widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition: widget_type.h:69
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition: widget_type.h:68
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition: widget_type.h:65
@ WWT_DROPDOWN
Drop down list.
Definition: widget_type.h:70
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition: widget_type.h:80
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition: window.cpp:1140
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition: window.cpp:422
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition: window.cpp:3211
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:3228
Window functions not directly related to making/drawing windows.
@ SBS_DOWN
Sort ascending.
Definition: window_gui.h:220
@ SBS_UP
Sort descending.
Definition: window_gui.h:221
@ WDF_CONSTRUCTION
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:203
@ WDP_AUTO
Find a place automatically.
Definition: window_gui.h:147
int WidgetID
Widget ID.
Definition: window_type.h:18
EventState
State of handling an event.
Definition: window_type.h:743
@ ES_HANDLED
The passed event is handled.
Definition: window_type.h:744
@ ES_NOT_HANDLED
The passed event is not handled.
Definition: window_type.h:745
@ WC_REPLACE_VEHICLE
Replace vehicle window; Window numbers:
Definition: window_type.h:218
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:45
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers:
Definition: window_type.h:389
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition: zoom_func.h:107