OpenTTD Source 20260208-master-g43af8e94d0
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "engine_base.h"
12#include "engine_func.h"
13#include "station_base.h"
14#include "network/network.h"
16#include "textbuf_gui.h"
17#include "command_func.h"
18#include "company_func.h"
19#include "vehicle_gui.h"
20#include "newgrf_badge.h"
21#include "newgrf_badge_config.h"
22#include "newgrf_badge_gui.h"
23#include "newgrf_engine.h"
24#include "newgrf_text.h"
25#include "group.h"
26#include "string_func.h"
27#include "strings_func.h"
28#include "window_func.h"
30#include "vehicle_func.h"
31#include "dropdown_type.h"
32#include "dropdown_func.h"
33#include "engine_gui.h"
34#include "cargotype.h"
36#include "autoreplace_func.h"
37#include "engine_cmd.h"
38#include "train_cmd.h"
39#include "vehicle_cmd.h"
40#include "zoom_func.h"
41#include "querystring_gui.h"
42#include "stringfilter_type.h"
43#include "hotkeys.h"
44
46
47#include "table/strings.h"
48
49#include "safeguards.h"
50
57{
58 return std::max<uint>(GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.matrix.Vertical(), GetVehicleImageCellSize(type, EIT_PURCHASE).height);
59}
60
61static constexpr std::initializer_list<NWidgetPart> _nested_build_vehicle_widgets = {
63 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
64 NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetTextStyle(TC_WHITE),
65 NWidget(WWT_SHADEBOX, COLOUR_GREY),
66 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
67 NWidget(WWT_STICKYBOX, COLOUR_GREY),
71 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SORT_ASCENDING_DESCENDING), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
72 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetToolTip(STR_TOOLTIP_SORT_CRITERIA),
76 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
77 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_BV_CONFIGURE_BADGES), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetResize(0, 0), SetFill(0, 1), SetSpriteTip(SPR_EXTRA_MENU, STR_BADGE_CONFIG_MENU_TOOLTIP),
79 NWidget(WWT_PANEL, COLOUR_GREY),
80 NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BV_FILTER), SetResize(1, 0), SetFill(1, 0), SetPadding(2), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
85 /* Vehicle list. */
90 /* Panel with details. */
91 NWidget(WWT_PANEL, COLOUR_GREY, WID_BV_PANEL), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
92 /* Build/rename buttons, resize button. */
94 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BV_BUILD_SEL),
95 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_BUILD), SetResize(1, 0), SetFill(1, 0),
97 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDE), SetResize(1, 0), SetFill(1, 0),
98 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_RENAME), SetResize(1, 0), SetFill(1, 0),
99 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
100 EndContainer(),
101};
102
103
105uint8_t _engine_sort_last_criteria[] = {0, 0, 0, 0};
106bool _engine_sort_last_order[] = {false, false, false, false};
107bool _engine_sort_show_hidden_engines[] = {false, false, false, false};
109
117{
118 int r = Engine::Get(a.engine_id)->list_position - Engine::Get(b.engine_id)->list_position;
119
120 return _engine_sort_direction ? r > 0 : r < 0;
121}
122
130{
131 const auto va = Engine::Get(a.engine_id)->intro_date;
132 const auto vb = Engine::Get(b.engine_id)->intro_date;
133 const auto r = va - vb;
134
135 /* Use EngineID to sort instead since we want consistent sorting */
136 if (r == 0) return EngineNumberSorter(a, b);
137 return _engine_sort_direction ? r > 0 : r < 0;
138}
139
140/* cached values for EngineNameSorter to spare many GetString() calls */
141static EngineID _last_engine[2] = { EngineID::Invalid(), EngineID::Invalid() };
142
150{
151 static std::string last_name[2] = { {}, {} };
152
153 if (a.engine_id != _last_engine[0]) {
154 _last_engine[0] = a.engine_id;
155 last_name[0] = GetString(STR_ENGINE_NAME, PackEngineNameDParam(a.engine_id, EngineNameContext::PurchaseList));
156 }
157
158 if (b.engine_id != _last_engine[1]) {
159 _last_engine[1] = b.engine_id;
160 last_name[1] = GetString(STR_ENGINE_NAME, PackEngineNameDParam(b.engine_id, EngineNameContext::PurchaseList));
161 }
162
163 int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
164
165 /* Use EngineID to sort instead since we want consistent sorting */
166 if (r == 0) return EngineNumberSorter(a, b);
167 return _engine_sort_direction ? r > 0 : r < 0;
168}
169
177{
178 const int va = Engine::Get(a.engine_id)->reliability;
179 const int vb = Engine::Get(b.engine_id)->reliability;
180 const int r = va - vb;
181
182 /* Use EngineID to sort instead since we want consistent sorting */
183 if (r == 0) return EngineNumberSorter(a, b);
184 return _engine_sort_direction ? r > 0 : r < 0;
185}
186
194{
195 Money va = Engine::Get(a.engine_id)->GetCost();
196 Money vb = Engine::Get(b.engine_id)->GetCost();
197 int r = ClampTo<int32_t>(va - vb);
198
199 /* Use EngineID to sort instead since we want consistent sorting */
200 if (r == 0) return EngineNumberSorter(a, b);
201 return _engine_sort_direction ? r > 0 : r < 0;
202}
203
211{
212 int va = Engine::Get(a.engine_id)->GetDisplayMaxSpeed();
213 int vb = Engine::Get(b.engine_id)->GetDisplayMaxSpeed();
214 int r = va - vb;
215
216 /* Use EngineID to sort instead since we want consistent sorting */
217 if (r == 0) return EngineNumberSorter(a, b);
218 return _engine_sort_direction ? r > 0 : r < 0;
219}
220
228{
229 int va = Engine::Get(a.engine_id)->GetPower();
230 int vb = Engine::Get(b.engine_id)->GetPower();
231 int r = va - vb;
232
233 /* Use EngineID to sort instead since we want consistent sorting */
234 if (r == 0) return EngineNumberSorter(a, b);
235 return _engine_sort_direction ? r > 0 : r < 0;
236}
237
245{
246 int va = Engine::Get(a.engine_id)->GetDisplayMaxTractiveEffort();
247 int vb = Engine::Get(b.engine_id)->GetDisplayMaxTractiveEffort();
248 int r = va - vb;
249
250 /* Use EngineID to sort instead since we want consistent sorting */
251 if (r == 0) return EngineNumberSorter(a, b);
252 return _engine_sort_direction ? r > 0 : r < 0;
253}
254
262{
263 Money va = Engine::Get(a.engine_id)->GetRunningCost();
264 Money vb = Engine::Get(b.engine_id)->GetRunningCost();
265 int r = ClampTo<int32_t>(va - vb);
266
267 /* Use EngineID to sort instead since we want consistent sorting */
268 if (r == 0) return EngineNumberSorter(a, b);
269 return _engine_sort_direction ? r > 0 : r < 0;
270}
271
279{
280 const Engine *e_a = Engine::Get(a.engine_id);
281 const Engine *e_b = Engine::Get(b.engine_id);
282 uint p_a = e_a->GetPower();
283 uint p_b = e_b->GetPower();
284 Money r_a = e_a->GetRunningCost();
285 Money r_b = e_b->GetRunningCost();
286 /* Check if running cost is zero in one or both engines.
287 * If only one of them is zero then that one has higher value,
288 * else if both have zero cost then compare powers. */
289 if (r_a == 0) {
290 if (r_b == 0) {
291 /* If it is ambiguous which to return go with their ID */
292 if (p_a == p_b) return EngineNumberSorter(a, b);
293 return _engine_sort_direction != (p_a < p_b);
294 }
296 }
297 if (r_b == 0) return _engine_sort_direction;
298 /* Using double for more precision when comparing close values.
299 * This shouldn't have any major effects in performance nor in keeping
300 * the game in sync between players since it's used in GUI only in client side */
301 double v_a = (double)p_a / (double)r_a;
302 double v_b = (double)p_b / (double)r_b;
303 /* Use EngineID to sort if both have same power/running cost,
304 * since we want consistent sorting.
305 * Also if both have no power then sort with reverse of running cost to simulate
306 * previous sorting behaviour for wagons. */
307 if (v_a == 0 && v_b == 0) return EngineRunningCostSorter(b, a);
308 if (v_a == v_b) return EngineNumberSorter(a, b);
309 return _engine_sort_direction != (v_a < v_b);
310}
311
312/* Train sorting functions */
313
321{
322 const RailVehicleInfo *rvi_a = RailVehInfo(a.engine_id);
323 const RailVehicleInfo *rvi_b = RailVehInfo(b.engine_id);
324
325 int va = GetTotalCapacityOfArticulatedParts(a.engine_id) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
326 int vb = GetTotalCapacityOfArticulatedParts(b.engine_id) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
327 int r = va - vb;
328
329 /* Use EngineID to sort instead since we want consistent sorting */
330 if (r == 0) return EngineNumberSorter(a, b);
331 return _engine_sort_direction ? r > 0 : r < 0;
332}
333
341{
342 int val_a = (RailVehInfo(a.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
343 int val_b = (RailVehInfo(b.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
344 int r = val_a - val_b;
345
346 /* Use EngineID to sort instead since we want consistent sorting */
347 if (r == 0) return EngineNumberSorter(a, b);
348 return _engine_sort_direction ? r > 0 : r < 0;
349}
350
351/* Road vehicle sorting functions */
352
360{
363 int r = va - vb;
364
365 /* Use EngineID to sort instead since we want consistent sorting */
366 if (r == 0) return EngineNumberSorter(a, b);
367 return _engine_sort_direction ? r > 0 : r < 0;
368}
369
370/* Ship vehicle sorting functions */
371
379{
380 const Engine *e_a = Engine::Get(a.engine_id);
381 const Engine *e_b = Engine::Get(b.engine_id);
382
383 int va = e_a->GetDisplayDefaultCapacity();
384 int vb = e_b->GetDisplayDefaultCapacity();
385 int r = va - vb;
386
387 /* Use EngineID to sort instead since we want consistent sorting */
388 if (r == 0) return EngineNumberSorter(a, b);
389 return _engine_sort_direction ? r > 0 : r < 0;
390}
391
392/* Aircraft sorting functions */
393
401{
402 const Engine *e_a = Engine::Get(a.engine_id);
403 const Engine *e_b = Engine::Get(b.engine_id);
404
405 uint16_t mail_a, mail_b;
406 int va = e_a->GetDisplayDefaultCapacity(&mail_a);
407 int vb = e_b->GetDisplayDefaultCapacity(&mail_b);
408 int r = va - vb;
409
410 if (r == 0) {
411 /* The planes have the same passenger capacity. Check mail capacity instead */
412 r = mail_a - mail_b;
413
414 if (r == 0) {
415 /* Use EngineID to sort instead since we want consistent sorting */
416 return EngineNumberSorter(a, b);
417 }
418 }
419 return _engine_sort_direction ? r > 0 : r < 0;
420}
421
429{
430 uint16_t r_a = Engine::Get(a.engine_id)->GetRange();
431 uint16_t r_b = Engine::Get(b.engine_id)->GetRange();
432
433 int r = r_a - r_b;
434
435 /* Use EngineID to sort instead since we want consistent sorting */
436 if (r == 0) return EngineNumberSorter(a, b);
437 return _engine_sort_direction ? r > 0 : r < 0;
438}
439
489
491const std::initializer_list<const StringID> _engine_sort_listing[] = {{
492 /* Trains */
493 STR_SORT_BY_ENGINE_ID,
494 STR_SORT_BY_COST,
495 STR_SORT_BY_MAX_SPEED,
496 STR_SORT_BY_POWER,
497 STR_SORT_BY_TRACTIVE_EFFORT,
498 STR_SORT_BY_INTRO_DATE,
499 STR_SORT_BY_NAME,
500 STR_SORT_BY_RUNNING_COST,
501 STR_SORT_BY_POWER_VS_RUNNING_COST,
502 STR_SORT_BY_RELIABILITY,
503 STR_SORT_BY_CARGO_CAPACITY,
504}, {
505 /* Road vehicles */
506 STR_SORT_BY_ENGINE_ID,
507 STR_SORT_BY_COST,
508 STR_SORT_BY_MAX_SPEED,
509 STR_SORT_BY_POWER,
510 STR_SORT_BY_TRACTIVE_EFFORT,
511 STR_SORT_BY_INTRO_DATE,
512 STR_SORT_BY_NAME,
513 STR_SORT_BY_RUNNING_COST,
514 STR_SORT_BY_POWER_VS_RUNNING_COST,
515 STR_SORT_BY_RELIABILITY,
516 STR_SORT_BY_CARGO_CAPACITY,
517}, {
518 /* Ships */
519 STR_SORT_BY_ENGINE_ID,
520 STR_SORT_BY_COST,
521 STR_SORT_BY_MAX_SPEED,
522 STR_SORT_BY_INTRO_DATE,
523 STR_SORT_BY_NAME,
524 STR_SORT_BY_RUNNING_COST,
525 STR_SORT_BY_RELIABILITY,
526 STR_SORT_BY_CARGO_CAPACITY,
527}, {
528 /* Aircraft */
529 STR_SORT_BY_ENGINE_ID,
530 STR_SORT_BY_COST,
531 STR_SORT_BY_MAX_SPEED,
532 STR_SORT_BY_INTRO_DATE,
533 STR_SORT_BY_NAME,
534 STR_SORT_BY_RUNNING_COST,
535 STR_SORT_BY_RELIABILITY,
536 STR_SORT_BY_CARGO_CAPACITY,
537 STR_SORT_BY_RANGE,
538}};
539
541static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoType cargo_type)
542{
543 if (cargo_type == CargoFilterCriteria::CF_ANY) {
544 return true;
545 } else if (cargo_type == CargoFilterCriteria::CF_ENGINES) {
546 return Engine::Get(item->engine_id)->GetPower() != 0;
547 } else {
548 CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask;
549 return (cargo_type == CargoFilterCriteria::CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cargo_type));
550 }
551}
552
553static GUIEngineList::FilterFunction * const _engine_filter_funcs[] = {
555};
556
557static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype)
558{
559 uint weight = 0;
560 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
561 if (cap[cargo] != 0) {
562 if (vtype == VEH_TRAIN) {
563 weight += CargoSpec::Get(cargo)->WeightOfNUnitsInTrain(cap[cargo]);
564 } else {
565 weight += CargoSpec::Get(cargo)->WeightOfNUnits(cap[cargo]);
566 }
567 }
568 }
569 return weight;
570}
571
572static int DrawCargoCapacityInfo(int left, int right, int y, TestedEngineDetails &te, bool refittable)
573{
574 for (const CargoSpec *cs : _sorted_cargo_specs) {
575 CargoType cargo_type = cs->Index();
576 if (te.all_capacities[cargo_type] == 0) continue;
577
578 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, cargo_type, te.all_capacities[cargo_type], refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY));
580 }
581
582 return y;
583}
584
585/* Draw rail wagon specific details */
586static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi, TestedEngineDetails &te)
587{
588 const Engine *e = Engine::Get(engine_number);
589
590 /* Purchase cost */
591 if (te.cost != 0) {
592 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost));
593 } else {
594 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost()));
595 }
597
598 /* Wagon weight - (including cargo) */
599 uint weight = e->GetDisplayWeight();
600 DrawString(left, right, y,
601 GetString(STR_PURCHASE_INFO_WEIGHT_CWEIGHT, weight, GetCargoWeight(te.all_capacities, VEH_TRAIN) + weight));
603
604 /* Wagon speed limit, displayed if above zero */
605 if (_settings_game.vehicle.wagon_speed_limits) {
606 uint max_speed = e->GetDisplayMaxSpeed();
607 if (max_speed > 0) {
608 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED, PackVelocity(max_speed, e->type)));
610 }
611 }
612
613 /* Running cost */
614 if (rvi->running_cost_class != Price::Invalid) {
615 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
617 }
618
619 return y;
620}
621
622/* Draw locomotive specific details */
623static int DrawRailEnginePurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi, TestedEngineDetails &te)
624{
625 const Engine *e = Engine::Get(engine_number);
626
627 /* Purchase Cost - Engine weight */
628 if (te.cost != 0) {
629 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_WEIGHT, e->GetCost() + te.cost, te.cost, e->GetDisplayWeight()));
630 } else {
631 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_WEIGHT, e->GetCost(), e->GetDisplayWeight()));
632 }
634
635 /* Supported rail types */
636 std::string railtypes{};
637 std::string_view list_separator = GetListSeparator();
638
639 for (const auto &rt : _sorted_railtypes) {
640 if (!rvi->railtypes.Test(rt)) continue;
641
642 if (!railtypes.empty()) railtypes += list_separator;
643 AppendStringInPlace(railtypes, GetRailTypeInfo(rt)->strings.name);
644 }
645 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_RAILTYPES, railtypes));
647
648 /* Max speed - Engine power */
649 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_POWER, PackVelocity(e->GetDisplayMaxSpeed(), e->type), e->GetPower()));
651
652 /* Max tractive effort - not applicable if old acceleration or maglev */
653 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
654 bool is_maglev = true;
655 for (RailType rt : rvi->railtypes) {
657 }
658 if (!is_maglev) {
659 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_MAX_TE, e->GetDisplayMaxTractiveEffort()));
661 }
662 }
663
664 /* Running cost */
665 if (rvi->running_cost_class != Price::Invalid) {
666 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
668 }
669
670 /* Powered wagons power - Powered wagons extra weight */
671 if (rvi->pow_wag_power != 0) {
672 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT, rvi->pow_wag_power, rvi->pow_wag_weight));
674 }
675
676 return y;
677}
678
679/* Draw road vehicle specific details */
680static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_number, TestedEngineDetails &te)
681{
682 const Engine *e = Engine::Get(engine_number);
683
684 if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) {
685 /* Purchase Cost */
686 if (te.cost != 0) {
687 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost));
688 } else {
689 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost()));
690 }
692
693 /* Road vehicle weight - (including cargo) */
694 int16_t weight = e->GetDisplayWeight();
695 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_WEIGHT_CWEIGHT, weight, GetCargoWeight(te.all_capacities, VEH_ROAD) + weight));
697
698 /* Max speed - Engine power */
699 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_POWER, PackVelocity(e->GetDisplayMaxSpeed(), e->type), e->GetPower()));
701
702 /* Max tractive effort */
703 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_MAX_TE, e->GetDisplayMaxTractiveEffort()));
705 } else {
706 /* Purchase cost - Max speed */
707 if (te.cost != 0) {
708 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
709 } else {
710 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
711 }
713 }
714
715 /* Running cost */
716 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
718
719 return y;
720}
721
722/* Draw ship specific details */
723static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable, TestedEngineDetails &te)
724{
725 const Engine *e = Engine::Get(engine_number);
726
727 /* Purchase cost - Max speed */
728 uint raw_speed = e->GetDisplayMaxSpeed();
729 uint ocean_speed = e->VehInfo<ShipVehicleInfo>().ApplyWaterClassSpeedFrac(raw_speed, true);
730 uint canal_speed = e->VehInfo<ShipVehicleInfo>().ApplyWaterClassSpeedFrac(raw_speed, false);
731
732 if (ocean_speed == canal_speed) {
733 if (te.cost != 0) {
734 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(ocean_speed, e->type)));
735 } else {
736 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(ocean_speed, e->type)));
737 }
739 } else {
740 if (te.cost != 0) {
741 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost));
742 } else {
743 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost()));
744 }
746
747 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_OCEAN, PackVelocity(ocean_speed, e->type)));
749
750 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_CANAL, PackVelocity(canal_speed, e->type)));
752 }
753
754 /* Cargo type + capacity */
755 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, te.cargo, te.capacity, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY));
757
758 /* Running cost */
759 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
761
762 return y;
763}
764
775static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable, TestedEngineDetails &te)
776{
777 const Engine *e = Engine::Get(engine_number);
778
779 /* Purchase cost - Max speed */
780 if (te.cost != 0) {
781 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
782 } else {
783 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
784 }
786
787 /* Cargo capacity */
788 if (te.mail_capacity > 0) {
789 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, te.cargo, te.capacity, GetCargoTypeByLabel(CT_MAIL), te.mail_capacity));
790 } else {
791 /* Note, if the default capacity is selected by the refit capacity
792 * callback, then the capacity shown is likely to be incorrect. */
793 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, te.cargo, te.capacity, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY));
794 }
796
797 /* Running cost */
798 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
800
801 /* Aircraft type */
802 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_TYPE, e->GetAircraftTypeText()));
804
805 /* Aircraft range, if available. */
806 uint16_t range = e->GetRange();
807 if (range != 0) {
808 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_RANGE, range));
810 }
811
812 return y;
813}
814
815
821static std::optional<std::string> GetNewGRFAdditionalText(EngineID engine)
822{
823 std::array<int32_t, 16> regs100;
824 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, nullptr, regs100);
825 if (callback == CALLBACK_FAILED || callback == 0x400) return std::nullopt;
826 const GRFFile *grffile = Engine::Get(engine)->GetGRF();
827 assert(grffile != nullptr);
828 if (callback == 0x40F) {
829 return GetGRFStringWithTextStack(grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
830 }
831 if (callback > 0x400) {
833 return std::nullopt;
834 }
835
836 return GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, regs100);
837}
838
847static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
848{
849 auto text = GetNewGRFAdditionalText(engine);
850 if (!text) return y;
851 return DrawStringMultiLine(left, right, y, INT32_MAX, *text, TC_BLACK);
852}
853
854void TestedEngineDetails::FillDefaultCapacities(const Engine *e)
855{
856 this->cargo = e->GetDefaultCargoType();
857 if (e->type == VEH_TRAIN || e->type == VEH_ROAD) {
859 this->capacity = this->all_capacities[this->cargo];
860 this->mail_capacity = 0;
861 } else {
863 this->all_capacities[this->cargo] = this->capacity;
864 if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) {
865 this->all_capacities[GetCargoTypeByLabel(CT_MAIL)] = this->mail_capacity;
866 } else {
867 this->mail_capacity = 0;
868 }
869 }
870 if (this->all_capacities.GetCount() == 0) this->cargo = INVALID_CARGO;
871}
872
880int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, TestedEngineDetails &te)
881{
882 const Engine *e = Engine::Get(engine_number);
883 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(e->intro_date);
884 bool refittable = IsArticulatedVehicleRefittable(engine_number);
885 bool articulated_cargo = false;
886
887 switch (e->type) {
888 default: NOT_REACHED();
889 case VEH_TRAIN:
890 if (e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) {
891 y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->VehInfo<RailVehicleInfo>(), te);
892 } else {
893 y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->VehInfo<RailVehicleInfo>(), te);
894 }
895 articulated_cargo = true;
896 break;
897
898 case VEH_ROAD:
899 y = DrawRoadVehPurchaseInfo(left, right, y, engine_number, te);
900 articulated_cargo = true;
901 break;
902
903 case VEH_SHIP:
904 y = DrawShipPurchaseInfo(left, right, y, engine_number, refittable, te);
905 break;
906
907 case VEH_AIRCRAFT:
908 y = DrawAircraftPurchaseInfo(left, right, y, engine_number, refittable, te);
909 break;
910 }
911
912 if (articulated_cargo) {
913 /* Cargo type + capacity, or N/A */
914 int new_y = DrawCargoCapacityInfo(left, right, y, te, refittable);
915
916 if (new_y == y) {
917 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, INVALID_CARGO, 0, STR_EMPTY));
919 } else {
920 y = new_y;
921 }
922 }
923
924 /* Draw details that apply to all types except rail wagons. */
925 if (e->type != VEH_TRAIN || e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON) {
926 /* Design date - Life length */
927 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_DESIGNED_LIFE, ymd.year, TimerGameCalendar::DateToYear(e->GetLifeLengthInDays())));
929
930 /* Reliability */
931 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_RELIABILITY, ToPercent16(e->reliability)));
933 }
934
935 if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
936
937 y = DrawBadgeNameList({left, y, right, INT16_MAX}, e->badges, static_cast<GrfSpecFeature>(GSF_TRAINS + e->type));
938
939 /* Additional text from NewGRF */
940 y = ShowAdditionalText(left, right, y, engine_number);
941
942 /* The NewGRF's name which the vehicle comes from */
943 const GRFConfig *config = GetGRFConfig(e->GetGRFID());
944 if (_settings_client.gui.show_newgrf_name && config != nullptr)
945 {
946 DrawString(left, right, y, config->GetName(), TC_BLACK);
948 }
949
950 return y;
951}
952
953static void DrawEngineBadgeColumn(const Rect &r, int column_group, const GUIBadgeClasses &badge_classes, const Engine *e, PaletteID remap)
954{
955 DrawBadgeColumn(r, column_group, badge_classes, e->badges, static_cast<GrfSpecFeature>(GSF_TRAINS + e->type), e->info.base_intro, remap);
956}
957
969void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group, const GUIBadgeClasses &badge_classes)
970{
971 static const std::array<int8_t, VehicleType::VEH_COMPANY_END> sprite_y_offsets = { 0, 0, -1, -1 };
972
973 auto [first, last] = sb.GetVisibleRangeIterators(eng_list);
974
975 bool rtl = _current_text_dir == TD_RTL;
976 int step_size = GetEngineListHeight(type);
977 int sprite_left = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_left;
978 int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
979 int sprite_width = sprite_left + sprite_right;
980 int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
981 PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
982
983 auto badge_column_widths = badge_classes.GetColumnWidths();
984
985 Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
986 int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
987
988 Dimension replace_icon = {0, 0};
989 int count_width = 0;
990 if (show_count) {
991 replace_icon = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
992
993 uint biggest_num_engines = 0;
994 for (auto it = first; it != last; ++it) {
995 const uint num_engines = GetGroupNumEngines(_local_company, selected_group, it->engine_id);
996 biggest_num_engines = std::max(biggest_num_engines, num_engines);
997 }
998
999 count_width = GetStringBoundingBox(GetString(STR_JUST_COMMA, biggest_num_engines), FS_SMALL).width;
1000 }
1001
1002 const int text_row_height = ir.Shrink(WidgetDimensions::scaled.matrix).Height();
1003 const int normal_text_y_offset = (text_row_height - GetCharacterHeight(FS_NORMAL)) / 2;
1004 const int small_text_y_offset = text_row_height - GetCharacterHeight(FS_SMALL);
1005
1006 const int offset = (rtl ? -circle_width : circle_width) / 2;
1007 const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
1008
1009 for (auto it = first; it != last; ++it) {
1010 const auto &item = *it;
1011 const Engine *e = Engine::Get(item.engine_id);
1012
1013 uint indent = item.indent * WidgetDimensions::scaled.hsep_indent;
1014 bool has_variants = item.flags.Test(EngineDisplayFlag::HasVariants);
1015 bool is_folded = item.flags.Test(EngineDisplayFlag::IsFolded);
1016 bool shaded = item.flags.Test(EngineDisplayFlag::Shaded);
1017
1018 Rect textr = ir.Shrink(WidgetDimensions::scaled.matrix);
1019 Rect tr = ir.Indent(indent, rtl);
1020
1021 if (item.indent > 0) {
1022 /* Draw tree continuation lines. */
1023 int tx = (rtl ? ir.right : ir.left) + offset;
1024 for (uint lvl = 1; lvl <= item.indent; ++lvl) {
1025 if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ir.top, tx, ir.bottom, linecolour, WidgetDimensions::scaled.fullbevel.top);
1026 if (lvl < item.indent) tx += level_width;
1027 }
1028 /* Draw our node in the tree. */
1029 int ycentre = CentreBounds(textr.top, textr.bottom, WidgetDimensions::scaled.fullbevel.top);
1030 if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ir.top, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
1031 GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
1032 }
1033
1034 if (has_variants) {
1035 Rect fr = tr.WithWidth(circle_width, rtl);
1036 DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, fr.WithY(textr), SA_CENTER);
1037 }
1038
1039 tr = tr.Indent(circle_width + WidgetDimensions::scaled.hsep_normal, rtl);
1040
1041 /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
1042 const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
1043 const PaletteID pal = (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company);
1044
1045 if (badge_column_widths.size() >= 1 && badge_column_widths[0] > 0) {
1046 Rect br = tr.WithWidth(badge_column_widths[0], rtl);
1047 DrawEngineBadgeColumn(br, 0, badge_classes, e, pal);
1048 tr = tr.Indent(badge_column_widths[0], rtl);
1049 }
1050
1051 int sprite_x = tr.WithWidth(sprite_width, rtl).left + sprite_left;
1052 DrawVehicleEngine(r.left, r.right, sprite_x, tr.top + sprite_y_offset, item.engine_id, pal, EIT_PURCHASE);
1053
1054 tr = tr.Indent(sprite_width + WidgetDimensions::scaled.hsep_wide, rtl);
1055
1056 if (badge_column_widths.size() >= 2 && badge_column_widths[1] > 0) {
1057 Rect br = tr.WithWidth(badge_column_widths[1], rtl);
1058 DrawEngineBadgeColumn(br, 1, badge_classes, e, pal);
1059 tr = tr.Indent(badge_column_widths[1], rtl);
1060 }
1061
1062 if (show_count) {
1063 /* Rect for replace-protection icon. */
1064 Rect rr = tr.WithWidth(replace_icon.width, !rtl);
1065 tr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_normal, !rtl);
1066 /* Rect for engine type count text. */
1067 Rect cr = tr.WithWidth(count_width, !rtl);
1068 tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal, !rtl);
1069
1070 DrawString(cr.left, cr.right, textr.top + small_text_y_offset, GetString(STR_JUST_COMMA, num_engines), TC_BLACK, SA_RIGHT | SA_FORCE, false, FS_SMALL);
1071
1072 if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) {
1073 DrawSpriteIgnorePadding(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr, SA_CENTER);
1074 }
1075 }
1076
1077 if (badge_column_widths.size() >= 3 && badge_column_widths[2] > 0) {
1078 Rect br = tr.WithWidth(badge_column_widths[2], !rtl).Indent(WidgetDimensions::scaled.hsep_wide, rtl);
1079 DrawEngineBadgeColumn(br, 2, badge_classes, e, pal);
1080 tr = tr.Indent(badge_column_widths[2], !rtl);
1081 }
1082
1083 bool hidden = e->company_hidden.Test(_local_company);
1084 StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
1085 TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : ((hidden | shaded) ? (TC_GREY | TC_FORCED | TC_NO_SHADE) : TC_BLACK);
1086
1087 /* If the count is visible then this is part of in-use autoreplace list. */
1088 auto engine_name = PackEngineNameDParam(item.engine_id, show_count ? EngineNameContext::AutoreplaceVehicleInUse : EngineNameContext::PurchaseList, item.indent);
1089 DrawString(tr.left, tr.right, textr.top + normal_text_y_offset,GetString(str, engine_name), tc);
1090
1091 ir = ir.Translate(0, step_size);
1092 }
1093}
1094
1102void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, WidgetID button)
1103{
1104 uint32_t hidden_mask = 0;
1105 /* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
1106 if (vehicle_type == VEH_ROAD && _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
1107 SetBit(hidden_mask, 3); // power
1108 SetBit(hidden_mask, 4); // tractive effort
1109 SetBit(hidden_mask, 8); // power by running costs
1110 }
1111 /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
1112 if (vehicle_type == VEH_TRAIN && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
1113 SetBit(hidden_mask, 4); // tractive effort
1114 }
1115 ShowDropDownMenu(w, _engine_sort_listing[vehicle_type], selected, button, 0, hidden_mask);
1116}
1117
1125void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent, uint8_t indent)
1126{
1127 for (const auto &item : src) {
1128 if (item.variant_id != parent || item.engine_id == parent) continue;
1129
1130 const Engine *e = Engine::Get(item.engine_id);
1131 EngineDisplayFlags flags = item.flags;
1132 if (e->display_last_variant != EngineID::Invalid()) flags.Reset(EngineDisplayFlag::Shaded);
1133 dst.emplace_back(e->display_last_variant == EngineID::Invalid() ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
1134
1135 /* Add variants if not folded */
1136 if (item.flags.Test(EngineDisplayFlag::HasVariants) && !item.flags.Test(EngineDisplayFlag::IsFolded)) {
1137 /* Add this engine again as a child */
1138 if (!item.flags.Test(EngineDisplayFlag::Shaded)) {
1139 dst.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags{}, indent + 1);
1140 }
1141 GUIEngineListAddChildren(dst, src, item.engine_id, indent + 1);
1142 }
1143 }
1144
1145 if (indent > 0 || dst.empty()) return;
1146
1147 /* Hierarchy is complete, traverse in reverse to find where indentation levels continue. */
1148 uint16_t level_mask = 0;
1149 for (auto it = std::rbegin(dst); std::next(it) != std::rend(dst); ++it) {
1150 auto next_it = std::next(it);
1151 SB(level_mask, it->indent, 1, it->indent <= next_it->indent);
1152 next_it->level_mask = level_mask;
1153 }
1154}
1155
1157struct BuildVehicleWindow : Window {
1159 union {
1164 uint8_t sort_criteria = 0;
1165 bool show_hidden_engines = false;
1166 bool listview_mode = false;
1167 EngineID sel_engine = EngineID::Invalid();
1168 EngineID rename_engine = EngineID::Invalid();
1169 GUIEngineList eng_list{};
1172 Scrollbar *vscroll = nullptr;
1174 GUIBadgeClasses badge_classes{};
1175
1176 static constexpr int BADGE_COLUMNS = 3;
1177
1180
1181 std::pair<WidgetID, WidgetID> badge_filters{};
1182 BadgeFilterChoices badge_filter_choices{};
1183
1184 void SetBuyVehicleText()
1185 {
1186 NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_BUILD);
1187
1189 if (refit) refit = Engine::Get(this->sel_engine)->GetDefaultCargoType() != this->cargo_filter_criteria;
1190
1191 if (refit) {
1192 widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type, STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP + this->vehicle_type);
1193 } else {
1194 widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type, STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + this->vehicle_type);
1195 }
1196 }
1197
1199 {
1200 this->vehicle_type = type;
1201 this->listview_mode = tile == INVALID_TILE;
1202 this->window_number = this->listview_mode ? (int)type : tile.base();
1203
1207
1208 this->UpdateFilterByTile();
1209
1210 this->CreateNestedTree();
1211
1212 this->vscroll = this->GetScrollbar(WID_BV_SCROLLBAR);
1213
1214 /* If we are just viewing the list of vehicles, we do not need the Build button.
1215 * So we just hide it, and enlarge the Rename button by the now vacant place. */
1216 if (this->listview_mode) this->GetWidget<NWidgetStacked>(WID_BV_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE);
1217
1218 NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_LIST);
1219 widget->SetToolTip(STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type);
1220
1222 widget->SetToolTip(STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP + type);
1223
1224 widget = this->GetWidget<NWidgetCore>(WID_BV_RENAME);
1225 widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type, STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type);
1226
1228 widget->SetStringTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + type, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + type);
1229 widget->SetLowered(this->show_hidden_engines);
1230
1231 this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
1232
1233 if (tile == INVALID_TILE) {
1234 this->FinishInitNested(type);
1235 } else {
1236 this->FinishInitNested(tile);
1237 }
1238
1240 this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR;
1241
1242 this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
1243
1244 this->eng_list.ForceRebuild();
1245 this->GenerateBuildList(); // generate the list, since we need it in the next line
1246
1247 /* Select the first unshaded engine in the list as default when opening the window */
1248 EngineID engine = EngineID::Invalid();
1249 auto it = std::ranges::find_if(this->eng_list, [](const GUIEngineListItem &item) { return !item.flags.Test(EngineDisplayFlag::Shaded); });
1250 if (it != this->eng_list.end()) engine = it->engine_id;
1251 this->SelectEngine(engine);
1252 }
1253
1256 {
1257 switch (this->vehicle_type) {
1258 default: NOT_REACHED();
1259 case VEH_TRAIN:
1260 if (this->listview_mode) {
1261 this->filter.railtype = INVALID_RAILTYPE;
1262 } else {
1263 this->filter.railtype = GetRailType(this->window_number);
1264 }
1265 break;
1266
1267 case VEH_ROAD:
1268 if (this->listview_mode) {
1269 this->filter.roadtype = INVALID_ROADTYPE;
1270 } else {
1271 this->filter.roadtype = GetRoadTypeRoad(this->window_number);
1272 if (this->filter.roadtype == INVALID_ROADTYPE) {
1273 this->filter.roadtype = GetRoadTypeTram(this->window_number);
1274 }
1275 }
1276 break;
1277
1278 case VEH_SHIP:
1279 case VEH_AIRCRAFT:
1280 break;
1281 }
1282 }
1283
1284 StringID GetCargoFilterLabel(CargoType cargo_type) const
1285 {
1286 switch (cargo_type) {
1287 case CargoFilterCriteria::CF_ANY: return STR_PURCHASE_INFO_ALL_TYPES;
1288 case CargoFilterCriteria::CF_ENGINES: return STR_PURCHASE_INFO_ENGINES_ONLY;
1289 case CargoFilterCriteria::CF_NONE: return STR_PURCHASE_INFO_NONE;
1290 default: return CargoSpec::Get(cargo_type)->name;
1291 }
1292 }
1293
1296 {
1297 /* Set the last cargo filter criteria. */
1298 this->cargo_filter_criteria = _engine_sort_last_cargo_criteria[this->vehicle_type];
1299 if (this->cargo_filter_criteria < NUM_CARGO && !HasBit(_standard_cargo_mask, this->cargo_filter_criteria)) this->cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
1300
1301 this->eng_list.SetFilterFuncs(_engine_filter_funcs);
1302 this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
1303 }
1304
1305 void SelectEngine(EngineID engine)
1306 {
1307 CargoType cargo = this->cargo_filter_criteria;
1308 if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
1309
1310 this->sel_engine = engine;
1311 this->SetBuyVehicleText();
1312
1313 if (this->sel_engine == EngineID::Invalid()) return;
1314
1315 const Engine *e = Engine::Get(this->sel_engine);
1316
1317 if (!this->listview_mode) {
1318 /* Query for cost and refitted capacity */
1319 auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<Commands::BuildVehicle>::Do(DoCommandFlag::QueryCost, TileIndex(this->window_number), this->sel_engine, true, cargo, INVALID_CLIENT_ID);
1320 if (ret.Succeeded()) {
1321 this->te.cost = ret.GetCost() - e->GetCost();
1322 this->te.capacity = refit_capacity;
1323 this->te.mail_capacity = refit_mail;
1324 this->te.cargo = !IsValidCargoType(cargo) ? e->GetDefaultCargoType() : cargo;
1325 this->te.all_capacities = cargo_capacities;
1326 return;
1327 }
1328 }
1329
1330 /* Purchase test was not possible or failed, fill in the defaults instead. */
1331 this->te.cost = 0;
1332 this->te.FillDefaultCapacities(e);
1333 }
1334
1335 void OnInit() override
1336 {
1337 this->badge_classes = GUIBadgeClasses(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type));
1338 this->SetCargoFilterArray();
1339
1340 this->badge_filters = AddBadgeDropdownFilters(this, WID_BV_BADGE_FILTER, WID_BV_BADGE_FILTER, COLOUR_GREY, static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type));
1341
1342 this->widget_lookup.clear();
1343 this->nested_root->FillWidgetLookup(this->widget_lookup);
1344 }
1345
1348 {
1349 this->eng_list.Filter(this->cargo_filter_criteria);
1350 if (0 == this->eng_list.size()) { // no engine passed through the filter, invalidate the previously selected engine
1351 this->SelectEngine(EngineID::Invalid());
1352 } else if (std::ranges::find(this->eng_list, this->sel_engine, &GUIEngineListItem::engine_id) == this->eng_list.end()) { // previously selected engine didn't pass the filter, select the first engine of the list
1353 this->SelectEngine(this->eng_list[0].engine_id);
1354 }
1355 }
1356
1359 {
1360 GUIEngineListItem item = {eid, eid, EngineDisplayFlags{}, 0};
1361 return CargoAndEngineFilter(&item, this->cargo_filter_criteria);
1362 }
1363
1365 bool FilterByText(const Engine *e)
1366 {
1367 /* Do not filter if the filter text box is empty */
1368 if (this->string_filter.IsEmpty()) return true;
1369
1370 /* Filter engine name */
1371 this->string_filter.ResetState();
1372 this->string_filter.AddLine(GetString(STR_ENGINE_NAME, PackEngineNameDParam(e->index, EngineNameContext::PurchaseList)));
1373
1374 /* Filter NewGRF extra text */
1375 auto text = GetNewGRFAdditionalText(e->index);
1376 if (text) this->string_filter.AddLine(*text);
1377
1378 return this->string_filter.GetState();
1379 }
1380
1381 /* Figure out what train EngineIDs to put in the list */
1382 void GenerateBuildTrainList(GUIEngineList &list)
1383 {
1384 FlatSet<EngineID> variants;
1385 EngineID sel_id = EngineID::Invalid();
1386 size_t num_engines = 0;
1387
1388 list.clear();
1389
1390 BadgeTextFilter btf(this->string_filter, GSF_TRAINS);
1391 BadgeDropdownFilter bdf(this->badge_filter_choices);
1392
1393 /* Make list of all available train engines and wagons.
1394 * Also check to see if the previously selected engine is still available,
1395 * and if not, reset selection to EngineID::Invalid(). This could be the case
1396 * when engines become obsolete and are removed */
1397 for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
1398 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1399 EngineID eid = e->index;
1400 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
1401
1402 if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtypes, this->filter.railtype)) continue;
1403 if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
1404
1405 /* Filter now! So num_engines and num_wagons is valid */
1406 if (!FilterSingleEngine(eid)) continue;
1407
1408 if (!bdf.Filter(e->badges)) continue;
1409
1410 /* Filter by name or NewGRF extra text */
1411 if (!FilterByText(e) && !btf.Filter(e->badges)) 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 != EngineID::Invalid() && variants.insert(parent).second) {
1420 parent = Engine::Get(parent)->info.variant_id;
1421 }
1422
1423 if (eid == this->sel_engine) sel_id = eid;
1424 }
1425
1426 /* ensure primary engine of variant group is in list */
1427 for (const auto &variant : variants) {
1428 if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) {
1429 const Engine *e = Engine::Get(variant);
1430 list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0);
1431 if (e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON) num_engines++;
1432 }
1433 }
1434
1435 this->SelectEngine(sel_id);
1436
1437 /* invalidate cached values for name sorter - engine names could change */
1438 _last_engine[0] = _last_engine[1] = EngineID::Invalid();
1439
1440 /* make engines first, and then wagons, sorted by selected sort_criteria */
1441 _engine_sort_direction = false;
1443
1444 /* and then sort engines */
1446 EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
1447
1448 /* and finally sort wagons */
1449 EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], num_engines, list.size() - num_engines);
1450 }
1451
1452 /* Figure out what road vehicle EngineIDs to put in the list */
1453 void GenerateBuildRoadVehList()
1454 {
1455 EngineID sel_id = EngineID::Invalid();
1456
1457 this->eng_list.clear();
1458
1459 BadgeTextFilter btf(this->string_filter, GSF_ROADVEHICLES);
1460 BadgeDropdownFilter bdf(this->badge_filter_choices);
1461
1462 for (const Engine *e : Engine::IterateType(VEH_ROAD)) {
1463 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1464 EngineID eid = e->index;
1465 if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
1466 if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->VehInfo<RoadVehicleInfo>().roadtype, this->filter.roadtype)) continue;
1467 if (!bdf.Filter(e->badges)) continue;
1468
1469 /* Filter by name or NewGRF extra text */
1470 if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
1471
1472 this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1473
1474 if (eid == this->sel_engine) sel_id = eid;
1475 }
1476 this->SelectEngine(sel_id);
1477 }
1478
1479 /* Figure out what ship EngineIDs to put in the list */
1480 void GenerateBuildShipList()
1481 {
1482 EngineID sel_id = EngineID::Invalid();
1483 this->eng_list.clear();
1484
1485 BadgeTextFilter btf(this->string_filter, GSF_SHIPS);
1486 BadgeDropdownFilter bdf(this->badge_filter_choices);
1487
1488 for (const Engine *e : Engine::IterateType(VEH_SHIP)) {
1489 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1490 EngineID eid = e->index;
1491 if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
1492 if (!bdf.Filter(e->badges)) continue;
1493
1494 /* Filter by name or NewGRF extra text */
1495 if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
1496
1497 this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1498
1499 if (eid == this->sel_engine) sel_id = eid;
1500 }
1501 this->SelectEngine(sel_id);
1502 }
1503
1504 /* Figure out what aircraft EngineIDs to put in the list */
1505 void GenerateBuildAircraftList()
1506 {
1507 EngineID sel_id = EngineID::Invalid();
1508
1509 this->eng_list.clear();
1510
1511 const Station *st = this->listview_mode ? nullptr : Station::GetByTile(TileIndex(this->window_number));
1512
1513 BadgeTextFilter btf(this->string_filter, GSF_AIRCRAFT);
1514 BadgeDropdownFilter bdf(this->badge_filter_choices);
1515
1516 /* Make list of all available planes.
1517 * Also check to see if the previously selected plane is still available,
1518 * and if not, reset selection to EngineID::Invalid(). This could be the case
1519 * when planes become obsolete and are removed */
1520 for (const Engine *e : Engine::IterateType(VEH_AIRCRAFT)) {
1521 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1522 EngineID eid = e->index;
1523 if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue;
1524 /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
1525 if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
1526 if (!bdf.Filter(e->badges)) continue;
1527
1528 /* Filter by name or NewGRF extra text */
1529 if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
1530
1531 this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1532
1533 if (eid == this->sel_engine) sel_id = eid;
1534 }
1535
1536 this->SelectEngine(sel_id);
1537 }
1538
1539 /* Generate the list of vehicles */
1540 void GenerateBuildList()
1541 {
1542 if (!this->eng_list.NeedRebuild()) return;
1543
1544 /* Update filter type in case the road/railtype of the depot got converted */
1545 this->UpdateFilterByTile();
1546
1547 this->eng_list.clear();
1548
1549 GUIEngineList list;
1550
1551 switch (this->vehicle_type) {
1552 default: NOT_REACHED();
1553 case VEH_TRAIN:
1554 this->GenerateBuildTrainList(list);
1555 GUIEngineListAddChildren(this->eng_list, list);
1556 this->eng_list.RebuildDone();
1557 return;
1558 case VEH_ROAD:
1559 this->GenerateBuildRoadVehList();
1560 break;
1561 case VEH_SHIP:
1562 this->GenerateBuildShipList();
1563 break;
1564 case VEH_AIRCRAFT:
1565 this->GenerateBuildAircraftList();
1566 break;
1567 }
1568
1569 this->FilterEngineList();
1570
1571 /* ensure primary engine of variant group is in list after filtering */
1572 FlatSet<EngineID> variants;
1573 for (const auto &item : this->eng_list) {
1574 EngineID parent = item.variant_id;
1575 while (parent != EngineID::Invalid() && variants.insert(parent).second) {
1577 }
1578 }
1579
1580 for (const auto &variant : variants) {
1581 if (std::ranges::find(this->eng_list, variant, &GUIEngineListItem::engine_id) == this->eng_list.end()) {
1582 const Engine *e = Engine::Get(variant);
1583 this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0);
1584 }
1585 }
1586
1588 EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
1589
1590 this->eng_list.swap(list);
1591 GUIEngineListAddChildren(this->eng_list, list, EngineID::Invalid(), 0);
1592 this->eng_list.RebuildDone();
1593 }
1594
1595 DropDownList BuildCargoDropDownList() const
1596 {
1597 DropDownList list;
1598
1599 /* Add item for disabling filtering. */
1600 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
1601 /* Specific filters for trains. */
1602 if (this->vehicle_type == VEH_TRAIN) {
1603 /* Add item for locomotives only in case of trains. */
1604 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ENGINES), CargoFilterCriteria::CF_ENGINES));
1605 /* Add item for vehicles not carrying anything, e.g. train engines.
1606 * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
1607 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
1608 }
1609
1610 /* Add cargos */
1611 Dimension d = GetLargestCargoIconSize();
1612 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1613 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
1614 }
1615
1616 return list;
1617 }
1618
1619 DropDownList BuildBadgeConfigurationList() const
1620 {
1621 static const auto separators = {STR_BADGE_CONFIG_PREVIEW, STR_BADGE_CONFIG_NAME};
1622 return BuildBadgeClassConfigurationList(this->badge_classes, BADGE_COLUMNS, separators, COLOUR_GREY);
1623 }
1624
1625 void BuildVehicle()
1626 {
1627 EngineID sel_eng = this->sel_engine;
1628 if (sel_eng == EngineID::Invalid()) return;
1629
1630 CargoType cargo = this->cargo_filter_criteria;
1631 if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
1632 if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
1633 Command<Commands::BuildVehicle>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, TileIndex(this->window_number), sel_eng, true, cargo, INVALID_CLIENT_ID);
1634 } else {
1635 Command<Commands::BuildVehicle>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, TileIndex(this->window_number), sel_eng, true, cargo, INVALID_CLIENT_ID);
1636 }
1637
1638 /* Update last used variant in hierarchy and refresh if necessary. */
1639 bool refresh = false;
1640 EngineID parent = sel_eng;
1641 while (parent != EngineID::Invalid()) {
1642 Engine *e = Engine::Get(parent);
1643 refresh |= (e->display_last_variant != sel_eng);
1644 e->display_last_variant = sel_eng;
1645 parent = e->info.variant_id;
1646 }
1647
1648 if (refresh) {
1649 InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
1650 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
1651 }
1652 }
1653
1654 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1655 {
1656 switch (widget) {
1658 this->descending_sort_order ^= true;
1659 _engine_sort_last_order[this->vehicle_type] = this->descending_sort_order;
1660 this->eng_list.ForceRebuild();
1661 this->SetDirty();
1662 break;
1663
1665 this->show_hidden_engines ^= true;
1666 _engine_sort_show_hidden_engines[this->vehicle_type] = this->show_hidden_engines;
1667 this->eng_list.ForceRebuild();
1668 this->SetWidgetLoweredState(widget, this->show_hidden_engines);
1669 this->SetDirty();
1670 break;
1671
1672 case WID_BV_LIST: {
1673 EngineID e = EngineID::Invalid();
1674 const auto it = this->vscroll->GetScrolledItemFromWidget(this->eng_list, pt.y, this, WID_BV_LIST);
1675 if (it != this->eng_list.end()) {
1676 const auto &item = *it;
1677 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);
1678 if (item.flags.Test(EngineDisplayFlag::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) {
1679 /* toggle folded flag on engine */
1680 assert(item.variant_id != EngineID::Invalid());
1681 Engine *engine = Engine::Get(item.variant_id);
1683
1684 InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
1685 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
1686 return;
1687 }
1688 if (!item.flags.Test(EngineDisplayFlag::Shaded)) e = item.engine_id;
1689 }
1690 this->SelectEngine(e);
1691 this->SetDirty();
1692 if (_ctrl_pressed) {
1693 this->OnClick(pt, WID_BV_SHOW_HIDE, 1);
1694 } else if (click_count > 1 && !this->listview_mode) {
1695 this->OnClick(pt, WID_BV_BUILD, 1);
1696 }
1697 break;
1698 }
1699
1700 case WID_BV_SORT_DROPDOWN: // Select sorting criteria dropdown menu
1701 DisplayVehicleSortDropDown(this, this->vehicle_type, this->sort_criteria, WID_BV_SORT_DROPDOWN);
1702 break;
1703
1704 case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
1705 ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria, widget);
1706 break;
1707
1709 if (this->badge_classes.GetClasses().empty()) break;
1710 ShowDropDownList(this, this->BuildBadgeConfigurationList(), -1, widget, 0, DropDownOption::Persist);
1711 break;
1712
1713 case WID_BV_SHOW_HIDE: {
1714 const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine);
1715 if (e != nullptr) {
1716 Command<Commands::SetVehicleVisibility>::Post(this->sel_engine, !e->IsHidden(_current_company));
1717 }
1718 break;
1719 }
1720
1721 case WID_BV_BUILD:
1722 this->BuildVehicle();
1723 break;
1724
1725 case WID_BV_RENAME: {
1726 EngineID sel_eng = this->sel_engine;
1727 if (sel_eng != EngineID::Invalid()) {
1728 this->rename_engine = sel_eng;
1730 }
1731 break;
1732 }
1733
1734 default:
1735 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
1736 PaletteID palette = SPR_2CCMAP_BASE + Company::Get(_local_company)->GetCompanyRecolourOffset(LS_DEFAULT);
1737 ShowDropDownList(this, this->GetWidget<NWidgetBadgeFilter>(widget)->GetDropDownList(palette), -1, widget, 0);
1738 }
1739 break;
1740 }
1741 }
1742
1748 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1749 {
1750 if (!gui_scope) return;
1751 /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
1752 if (this->vehicle_type == VEH_ROAD &&
1753 _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL &&
1754 this->sort_criteria > 7) {
1755 this->sort_criteria = 0;
1757 }
1758 this->eng_list.ForceRebuild();
1759 }
1760
1761 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1762 {
1763 switch (widget) {
1764 case WID_BV_CAPTION:
1765 if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
1766 const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
1767 return GetString(rti->strings.build_caption);
1768 }
1769 if (this->vehicle_type == VEH_ROAD && !this->listview_mode) {
1770 const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype);
1771 return GetString(rti->strings.build_caption);
1772 }
1773 return GetString((this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
1774
1776 return GetString(std::data(_engine_sort_listing[this->vehicle_type])[this->sort_criteria]);
1777
1779 return GetString(this->GetCargoFilterLabel(this->cargo_filter_criteria));
1780
1781 case WID_BV_SHOW_HIDE: {
1782 const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine);
1783 if (e != nullptr && e->IsHidden(_local_company)) {
1784 return GetString(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type);
1785 }
1786 return GetString(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
1787 }
1788
1789 default:
1790 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
1791 return this->GetWidget<NWidgetBadgeFilter>(widget)->GetStringParameter(this->badge_filter_choices);
1792 }
1793
1794 return this->Window::GetWidgetString(widget, stringid);
1795 }
1796 }
1797
1798 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1799 {
1800 switch (widget) {
1801 case WID_BV_LIST:
1802 fill.height = resize.height = GetEngineListHeight(this->vehicle_type);
1803 size.height = 3 * resize.height;
1804 size.width = std::max(size.width, this->badge_classes.GetTotalColumnsWidth() + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
1805 break;
1806
1807 case WID_BV_PANEL:
1808 size.height = GetCharacterHeight(FS_NORMAL) * this->details_height + padding.height;
1809 break;
1810
1813 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1814 d.height += padding.height;
1815 size = maxdim(size, d);
1816 break;
1817 }
1818
1820 size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width);
1821 break;
1822
1824 /* Hide the configuration button if no configurable badges are present. */
1825 if (this->badge_classes.GetClasses().empty()) size = {0, 0};
1826 break;
1827
1828 case WID_BV_BUILD:
1829 size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type);
1830 size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type));
1831 size.width += padding.width;
1832 size.height += padding.height;
1833 break;
1834
1835 case WID_BV_SHOW_HIDE:
1836 size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
1837 size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type));
1838 size.width += padding.width;
1839 size.height += padding.height;
1840 break;
1841 }
1842 }
1843
1844 void DrawWidget(const Rect &r, WidgetID widget) const override
1845 {
1846 switch (widget) {
1847 case WID_BV_LIST:
1849 this->vehicle_type,
1850 r,
1851 this->eng_list,
1852 *this->vscroll,
1853 this->sel_engine,
1854 false,
1856 this->badge_classes
1857 );
1858 break;
1859
1861 this->DrawSortButtonState(WID_BV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
1862 break;
1863 }
1864 }
1865
1866 void OnPaint() override
1867 {
1868 this->GenerateBuildList();
1869 this->vscroll->SetCount(this->eng_list.size());
1870
1871 this->SetWidgetsDisabledState(this->sel_engine == EngineID::Invalid(), WID_BV_SHOW_HIDE, WID_BV_BUILD);
1872
1873 /* Disable renaming engines in network games if you are not the server. */
1874 this->SetWidgetDisabledState(WID_BV_RENAME, this->sel_engine == EngineID::Invalid() || (_networking && !_network_server));
1875
1876 this->DrawWidgets();
1877
1878 if (!this->IsShaded()) {
1879 int needed_height = this->details_height;
1880 /* Draw details panels. */
1881 if (this->sel_engine != EngineID::Invalid()) {
1882 const Rect r = this->GetWidget<NWidgetBase>(WID_BV_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
1883 int text_end = DrawVehiclePurchaseInfo(r.left, r.right, r.top, this->sel_engine, this->te);
1884 needed_height = std::max(needed_height, (text_end - r.top) / GetCharacterHeight(FS_NORMAL));
1885 }
1886 if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
1887 int resize = needed_height - this->details_height;
1888 this->details_height = needed_height;
1889 this->ReInit(0, resize * GetCharacterHeight(FS_NORMAL));
1890 return;
1891 }
1892 }
1893 }
1894
1895 void OnQueryTextFinished(std::optional<std::string> str) override
1896 {
1897 if (!str.has_value()) return;
1898
1899 Command<Commands::RenameEngine>::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, *str);
1900 }
1901
1902 void OnDropdownSelect(WidgetID widget, int index, int click_result) override
1903 {
1904 switch (widget) {
1906 if (this->sort_criteria != index) {
1907 this->sort_criteria = index;
1908 _engine_sort_last_criteria[this->vehicle_type] = this->sort_criteria;
1909 this->eng_list.ForceRebuild();
1910 }
1911 break;
1912
1913 case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
1914 if (this->cargo_filter_criteria != index) {
1915 this->cargo_filter_criteria = index;
1916 _engine_sort_last_cargo_criteria[this->vehicle_type] = this->cargo_filter_criteria;
1917 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1918 this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
1919 this->eng_list.ForceRebuild();
1920 this->SelectEngine(this->sel_engine);
1921 }
1922 break;
1923
1925 bool reopen = HandleBadgeConfigurationDropDownClick(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type), BADGE_COLUMNS, index, click_result, this->badge_filter_choices);
1926
1927 this->ReInit();
1928
1929 if (reopen) {
1930 ReplaceDropDownList(this, this->BuildBadgeConfigurationList(), -1);
1931 } else {
1933 }
1934
1935 /* We need to refresh if a filter is removed. */
1936 this->eng_list.ForceRebuild();
1937 break;
1938 }
1939
1940 default:
1941 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
1942 if (index < 0) {
1943 ResetBadgeFilter(this->badge_filter_choices, this->GetWidget<NWidgetBadgeFilter>(widget)->GetBadgeClassID());
1944 } else {
1945 SetBadgeFilter(this->badge_filter_choices, BadgeID(index));
1946 }
1947 this->eng_list.ForceRebuild();
1948 }
1949 break;
1950 }
1951 this->SetDirty();
1952 }
1953
1954 void OnResize() override
1955 {
1956 this->vscroll->SetCapacityFromWidget(this, WID_BV_LIST);
1957 }
1958
1959 void OnEditboxChanged(WidgetID wid) override
1960 {
1961 if (wid == WID_BV_FILTER) {
1962 this->string_filter.SetFilterTerm(this->vehicle_editbox.text.GetText());
1963 this->InvalidateData();
1964 }
1965 }
1966
1967 static inline HotkeyList hotkeys{"buildvehicle", {
1968 Hotkey('F', "focus_filter_box", WID_BV_FILTER),
1969 }};
1970};
1971
1972static WindowDesc _build_vehicle_desc(
1973 WDP_AUTO, "build_vehicle", 240, 268,
1976 _nested_build_vehicle_widgets,
1977 &BuildVehicleWindow::hotkeys
1978);
1979
1980void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
1981{
1982 /* We want to be able to open both Available Train as Available Ships,
1983 * so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
1984 * As it always is a low value, it won't collide with any real tile
1985 * number. */
1986 uint num = (tile == INVALID_TILE) ? (int)type : tile.base();
1987
1988 assert(IsCompanyBuildableVehicleType(type));
1989
1991
1992 new BuildVehicleWindow(_build_vehicle_desc, tile, type);
1993}
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 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.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
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.
static CargoType _engine_sort_last_cargo_criteria[]
Last set filter criteria, for each vehicle type.
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.
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group, const GUIBadgeClasses &badge_classes)
Engine drawing loop.
static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoType cargo_type)
Filters vehicles by cargo and engine (in case of rail vehicle).
static std::optional< std::string > GetNewGRFAdditionalText(EngineID engine)
Try to get the NewGRF engine additional text callback as an optional std::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.
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 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.
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_CONFIGURE_BADGES
Button to configure badges.
@ WID_BV_BADGE_FILTER
Container for dropdown badge filters.
@ 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 CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:104
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:73
Dimension GetLargestCargoIconSize()
Get dimensions of largest cargo icon.
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
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.
Types/functions related to cargoes.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
StringID GetAircraftTypeText() const
Get the name of the aircraft type for display purposes.
Definition engine.cpp:470
uint GetPower() const
Returns the power of the engine for display and sorting purposes.
Definition engine.cpp:393
uint16_t GetRange() const
Get the range of an aircraft type.
Definition engine.cpp:456
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:160
Money GetCost() const
Return how much a new engine costs.
Definition engine.cpp:321
TimerGameCalendar::Date intro_date
Date of introduction of the engine.
Definition engine_base.h:46
static Pool::IterateWrapperFiltered< Engine, EngineTypeFilter > IterateType(VehicleType vt, size_t from=0)
Returns an iterable ensemble of all valid engines of the given type.
uint GetDisplayMaxSpeed() const
Returns max speed of the engine for display purposes.
Definition engine.cpp:361
EngineDisplayFlags display_flags
NOSAVE client-side-only display flags for build engine list.
Definition engine_base.h:64
uint GetDisplayWeight() const
Returns the weight of the engine for display purposes.
Definition engine.cpp:411
VehicleType type
Vehicle type, ie VEH_ROAD, VEH_TRAIN, etc.
Definition engine_base.h:62
bool IsVariantHidden(CompanyID c) const
Check whether the engine variant chain is hidden in the GUI for the given company.
Definition engine.cpp:490
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:446
uint GetDisplayDefaultCapacity(uint16_t *mail_capacity=nullptr) const
Determines the default cargo capacity of an engine for display purposes.
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:94
CompanyMask company_hidden
Bit for each company whether the engine is normally hidden in the build gui for that company.
Definition engine_base.h:41
Money GetRunningCost() const
Return how much the running costs of this engine are.
Definition engine.cpp:284
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
uint GetDisplayMaxTractiveEffort() const
Returns the tractive effort of the engine for display purposes.
Definition engine.cpp:429
bool IsHidden(CompanyID c) const
Check whether the engine is hidden in the GUI for the given company.
EngineID display_last_variant
NOSAVE client-side-only last variant selected.
Definition engine_base.h:65
Flat set implementation that uses a sorted vector for storage.
std::pair< const_iterator, bool > insert(const Tkey &key)
Insert a key into the set, if it does not already exist.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void SetFilterState(bool state)
Enable or disable the filter.
bool(const GUIEngineListItem *, CargoType) FilterFunction
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
void SetToolTip(StringID tool_tip)
Set the tool tip of the nested widget.
Definition widget.cpp:1225
void SetLowered(bool lowered)
Lower or raise the widget.
void SetStringTip(StringID string, StringID tool_tip)
Set string and tool tip of the nested widget.
Definition widget.cpp:1165
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:115
struct RailTypeInfo::@157247141350136173143103254227157213063052244122 strings
Strings associated with the rail type.
VehicleAccelerationModel acceleration_type
Acceleration type of this rail type.
Definition rail.h:215
StringID name
Name of this rail type.
Definition rail.h:165
StringID build_caption
Caption of the build vehicle GUI for this rail type.
Definition rail.h:168
struct RoadTypeInfo::@070000167274302256150317022075324310363002361255 strings
Strings associated with the rail type.
StringID build_caption
Caption of the build vehicle GUI for this rail type.
Definition road.h:80
Scrollbar data structure.
void SetCount(size_t num)
Sets the number of elements in the list.
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.
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:2500
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
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)
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
Functions related to commands.
@ QueryCost
query cost only, don't build.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
VehicleCellSize GetVehicleImageCellSize(VehicleType type, EngineImageType image_type)
Get the GUI cell size for a vehicle image.
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:459
Dimension GetDropDownListDimension(const DropDownList &list)
Determine width and height required to fully display a DropDownList.
Definition dropdown.cpp:381
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options)
Show a drop down list.
Definition dropdown.cpp:419
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.
@ Persist
Set if this dropdown should stay open after an option is selected.
@ Invalid
Invalid base price.
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1251
Base class for engines.
@ HasVariants
Set if engine has variants.
Definition engine_base.h:28
@ IsFolded
Set if display of variants should be folded (hidden).
Definition engine_base.h:29
@ Shaded
Set if engine should be masked.
Definition engine_base.h:30
Command definitions related to engines.
Functions related to engines.
uint GetTotalCapacityOfArticulatedParts(EngineID engine)
Get the capacity of an engine with articulated parts.
void EngList_Sort(GUIEngineList &el, EngList_SortTypeFunction compare)
Sort all items using quick sort and given 'CompareItems' function.
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>).
void DrawVehicleEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
Draw an engine.
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:33
static const uint MAX_LENGTH_ENGINE_NAME_CHARS
The maximum length of an engine name in characters including '\0'.
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
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.
@ PurchaseList
Name is shown in the purchase list (including autoreplace window 'Available vehicles' panel).
@ Generic
No specific context available.
@ AutoreplaceVehicleInUse
Name is show in the autoreplace window 'Vehicles in use' panel.
@ Maglev
Maglev acceleration model.
Definition engine_type.h:50
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition engine_type.h:33
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:972
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:900
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:669
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
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:788
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:250
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:390
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:400
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:332
@ TC_NO_SHADE
Do not add shading to this text colour.
Definition gfx_type.h:331
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.
constexpr NWidgetPart SetMatrixDataTip(uint32_t cols, uint32_t rows, StringID tip={})
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetSpriteTip(SpriteID sprite, StringID tip={})
Widget part function for setting the sprite and tooltip.
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
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.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetAspect(float ratio, AspectFlags flags=AspectFlag::ResizeX)
Widget part function for setting the aspect ratio.
constexpr NWidgetPart SetTextStyle(TextColour colour, FontSize size=FS_NORMAL)
Widget part function for setting the text style.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:969
static constexpr GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:18
Hotkey related functions.
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr uint ToPercent16(uint i)
Converts a "fract" value 0..65535 to "percent" value 0..100.
constexpr To ClampTo(From value)
Clamp the given value down to lie within the requested type.
void ShowQueryString(std::string_view str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
static constexpr CargoType CF_NONE
Show only items which do not carry cargo (e.g. train engines).
Definition cargo_type.h:94
static constexpr CargoType CF_ENGINES
Show only engines (for rail vehicles only).
Definition cargo_type.h:95
static constexpr CargoType CF_ANY
Show all items independent of carried cargo (i.e. no filtering).
Definition cargo_type.h:93
bool _networking
are we in networking mode?
Definition network.cpp:66
bool _network_server
network-server is active
Definition network.cpp:67
Basic functions/variables used all over the place.
@ INVALID_CLIENT_ID
Client is not part of anything.
GrfSpecFeature
Definition newgrf.h:71
Functions related to NewGRF badges.
Functions related to NewGRF badge configuration.
int DrawBadgeNameList(Rect r, std::span< const BadgeID > badges, GrfSpecFeature)
Draw names for a list of badge labels.
void DrawBadgeColumn(Rect r, int column_group, const GUIBadgeClasses &gui_classes, std::span< const BadgeID > badges, GrfSpecFeature feature, std::optional< TimerGameCalendar::Date > introduction_date, PaletteID remap)
Draw a badge column group.
std::pair< WidgetID, WidgetID > AddBadgeDropdownFilters(Window *window, WidgetID container_id, WidgetID widget, Colours colour, GrfSpecFeature feature)
Add badge drop down filter widgets.
bool HandleBadgeConfigurationDropDownClick(GrfSpecFeature feature, uint columns, int result, int click_result, BadgeFilterChoices &choices)
Handle the badge configuration drop down selection.
void SetBadgeFilter(BadgeFilterChoices &choices, BadgeID badge_index)
Set badge filter choice for a class.
void ResetBadgeFilter(BadgeFilterChoices &choices, BadgeClassID badge_class_index)
Reset badge filter choice for a class.
GUI functions related to NewGRF badges.
@ 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, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles.
Functions for NewGRF engines.
std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, std::span< const int32_t > textstack)
Format a GRF string using the text ref stack for parameters.
Header of Action 04 "universal holder" structure and functions.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer > GRFStringID
Type for GRF-internal string IDs.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
Base for the GUIs that have an edit box in them.
std::vector< RailType > _sorted_railtypes
Sorted list of rail types.
Definition rail_cmd.cpp:47
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:376
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
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:25
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
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:230
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:215
RoadType
The different roadtypes we support.
Definition road_type.h:23
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
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:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
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:427
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.
std::string_view GetListSeparator()
Get the list separator string for the current language.
Definition strings.cpp:299
void AppendStringInPlace(std::string &result, StringID string)
Resolve the given StringID and append in place into an existing std::string with formatting but no pa...
Definition strings.cpp:434
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
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.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
static const int MAX_CHAR_LENGTH
Max. length of UTF-8 encoded unicode character.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
GUI for building vehicles.
VehicleType vehicle_type
Type of vehicles shown in the window.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
static constexpr int BADGE_COLUMNS
Number of columns available for badges (0 = left of image, 1 = between image and name,...
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 OnDropdownSelect(WidgetID widget, int index, int click_result) override
A dropdown option associated to this window has been selected.
CargoType cargo_filter_criteria
Selected cargo filter.
void SetCargoFilterArray()
Populate the filter list and set the cargo filter criteria.
RailType railtype
Rail type to show, or INVALID_RAILTYPE.
union BuildVehicleWindow::@015063063316140361220303015310112114302264111156 filter
Filter to apply.
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.
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.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
TestedEngineDetails te
Tested cost and capacity after refit.
std::pair< WidgetID, WidgetID > badge_filters
First and last widgets IDs of badge filters.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnEditboxChanged(WidgetID wid) override
The text in an editbox has been edited.
StringFilter string_filter
Filter for vehicle name.
uint8_t sort_criteria
Current sort criterium.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
bool FilterSingleEngine(EngineID eid)
Filter a single engine.
EngineID sel_engine
Currently selected engine, or EngineID::Invalid().
RoadType roadtype
Road type to show, or INVALID_ROADTYPE.
void OnInit() override
Notification that the nested widget tree gets initialized.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
Class for storing amounts of cargo.
Definition cargo_type.h:111
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
StringID name
Name of this type of cargo.
Definition cargotype.h:91
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.
TimerGameCalendar::Date base_intro
Basic date of engine introduction (without random parts).
Information about GRF, used in the game and (part of it) in savegames.
std::string GetName() const
Get the name of this grf.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:117
EngineDisplayFlags flags
Flags for toggling/drawing (un)folded status and controlling indentation.
Definition engine_gui.h:24
EngineID variant_id
Variant group of the engine.
Definition engine_gui.h:23
EngineID engine_id
Engine to display in build purchase list.
Definition engine_gui.h:22
uint8_t indent
Display indentation level.
Definition engine_gui.h:25
List of hotkeys for a window.
Definition hotkeys.h:37
All data for a single hotkey.
Definition hotkeys.h:21
Colour for pixel/line drawing.
Definition gfx_type.h:405
static Engine * Get(auto index)
Data stored about a string that can be modified in the GUI.
static const int ACTION_CLEAR
Clear editbox.
Information about a rail vehicle.
Definition engine_type.h:74
RailTypes railtypes
Railtypes, mangled if elrail is disabled.
Definition engine_type.h:78
uint16_t pow_wag_power
Extra power applied to consist if wagon should be powered.
Definition engine_type.h:88
uint8_t pow_wag_weight
Extra weight applied to consist if wagon should be powered.
Definition engine_type.h:89
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.
Rect WithY(int new_top, int new_bottom) const
Create a new Rect, replacing the top and bottom coordiates.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
Information about a ship vehicle.
Definition engine_type.h:99
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(std::string_view str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
Extra information about refitted cargo and capacity.
Definition vehicle_gui.h:42
CargoType 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
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
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
High level window description.
Definition window_gui.h:168
Data structure for an opened window.
Definition window_gui.h:274
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:981
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:815
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1812
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:766
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3250
Window * parent
Parent window.
Definition window_gui.h:329
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:507
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:798
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1078
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:516
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1802
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:317
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:442
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:560
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:323
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1835
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:986
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:316
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:322
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
Stuff related to the text buffer GUI.
@ EnableDefault
enable the 'Default' button ("\0" is returned)
Definition textbuf_gui.h:20
@ LengthIsInChars
the length of the string is counted in characters
Definition textbuf_gui.h:21
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
Definition of the game-calendar-timer.
Command definitions related to trains.
void CcBuildWagon(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray, TileIndex tile, EngineID, bool, CargoType, 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:2136
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition vehicle.cpp:3060
Command definitions for vehicles.
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.
Functions related to vehicles.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
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, ...
VehicleType
Available vehicle types.
@ VEH_INVALID
Non-existing type of vehicle.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
static const uint MAX_LENGTH_VEHICLE_NAME_CHARS
The maximum length of a vehicle name in characters including '\0'.
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:41
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:61
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ SZSP_NONE
Display plane with zero size in both directions (none filling and resizing).
NWidContainerFlag
Nested widget container flags,.
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:1198
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:3310
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:3328
Window functions not directly related to making/drawing windows.
@ Construction
This window is used for construction; close it whenever changing company.
Definition window_gui.h:153
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:219
@ SBS_UP
Sort descending.
Definition window_gui.h:220
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:20
@ WC_REPLACE_VEHICLE
Replace vehicle window; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers:
@ Station
station encountered (could be a target next time)
Definition yapf_type.hpp:26
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107