OpenTTD Source 20250529-master-g10c159a79f
build_vehicle_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "engine_base.h"
12#include "engine_func.h"
13#include "station_base.h"
14#include "network/network.h"
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 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),
83 /* Vehicle list. */
88 /* Panel with details. */
89 NWidget(WWT_PANEL, COLOUR_GREY, WID_BV_PANEL), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
90 /* Build/rename buttons, resize button. */
92 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BV_BUILD_SEL),
93 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_BUILD), SetResize(1, 0), SetFill(1, 0),
95 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDE), SetResize(1, 0), SetFill(1, 0),
96 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_RENAME), SetResize(1, 0), SetFill(1, 0),
97 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
99};
100
101
103uint8_t _engine_sort_last_criteria[] = {0, 0, 0, 0};
104bool _engine_sort_last_order[] = {false, false, false, false};
105bool _engine_sort_show_hidden_engines[] = {false, false, false, false};
107
115{
116 int r = Engine::Get(a.engine_id)->list_position - Engine::Get(b.engine_id)->list_position;
117
118 return _engine_sort_direction ? r > 0 : r < 0;
119}
120
128{
129 const auto va = Engine::Get(a.engine_id)->intro_date;
130 const auto vb = Engine::Get(b.engine_id)->intro_date;
131 const auto r = va - vb;
132
133 /* Use EngineID to sort instead since we want consistent sorting */
134 if (r == 0) return EngineNumberSorter(a, b);
135 return _engine_sort_direction ? r > 0 : r < 0;
136}
137
138/* cached values for EngineNameSorter to spare many GetString() calls */
139static EngineID _last_engine[2] = { EngineID::Invalid(), EngineID::Invalid() };
140
148{
149 static std::string last_name[2] = { {}, {} };
150
151 if (a.engine_id != _last_engine[0]) {
152 _last_engine[0] = a.engine_id;
153 last_name[0] = GetString(STR_ENGINE_NAME, PackEngineNameDParam(a.engine_id, EngineNameContext::PurchaseList));
154 }
155
156 if (b.engine_id != _last_engine[1]) {
157 _last_engine[1] = b.engine_id;
158 last_name[1] = GetString(STR_ENGINE_NAME, PackEngineNameDParam(b.engine_id, EngineNameContext::PurchaseList));
159 }
160
161 int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
162
163 /* Use EngineID to sort instead since we want consistent sorting */
164 if (r == 0) return EngineNumberSorter(a, b);
165 return _engine_sort_direction ? r > 0 : r < 0;
166}
167
175{
176 const int va = Engine::Get(a.engine_id)->reliability;
177 const int vb = Engine::Get(b.engine_id)->reliability;
178 const int r = va - vb;
179
180 /* Use EngineID to sort instead since we want consistent sorting */
181 if (r == 0) return EngineNumberSorter(a, b);
182 return _engine_sort_direction ? r > 0 : r < 0;
183}
184
192{
193 Money va = Engine::Get(a.engine_id)->GetCost();
194 Money vb = Engine::Get(b.engine_id)->GetCost();
195 int r = ClampTo<int32_t>(va - vb);
196
197 /* Use EngineID to sort instead since we want consistent sorting */
198 if (r == 0) return EngineNumberSorter(a, b);
199 return _engine_sort_direction ? r > 0 : r < 0;
200}
201
209{
210 int va = Engine::Get(a.engine_id)->GetDisplayMaxSpeed();
211 int vb = Engine::Get(b.engine_id)->GetDisplayMaxSpeed();
212 int r = va - vb;
213
214 /* Use EngineID to sort instead since we want consistent sorting */
215 if (r == 0) return EngineNumberSorter(a, b);
216 return _engine_sort_direction ? r > 0 : r < 0;
217}
218
226{
227 int va = Engine::Get(a.engine_id)->GetPower();
228 int vb = Engine::Get(b.engine_id)->GetPower();
229 int r = va - vb;
230
231 /* Use EngineID to sort instead since we want consistent sorting */
232 if (r == 0) return EngineNumberSorter(a, b);
233 return _engine_sort_direction ? r > 0 : r < 0;
234}
235
243{
244 int va = Engine::Get(a.engine_id)->GetDisplayMaxTractiveEffort();
245 int vb = Engine::Get(b.engine_id)->GetDisplayMaxTractiveEffort();
246 int r = va - vb;
247
248 /* Use EngineID to sort instead since we want consistent sorting */
249 if (r == 0) return EngineNumberSorter(a, b);
250 return _engine_sort_direction ? r > 0 : r < 0;
251}
252
260{
261 Money va = Engine::Get(a.engine_id)->GetRunningCost();
262 Money vb = Engine::Get(b.engine_id)->GetRunningCost();
263 int r = ClampTo<int32_t>(va - vb);
264
265 /* Use EngineID to sort instead since we want consistent sorting */
266 if (r == 0) return EngineNumberSorter(a, b);
267 return _engine_sort_direction ? r > 0 : r < 0;
268}
269
277{
278 const Engine *e_a = Engine::Get(a.engine_id);
279 const Engine *e_b = Engine::Get(b.engine_id);
280 uint p_a = e_a->GetPower();
281 uint p_b = e_b->GetPower();
282 Money r_a = e_a->GetRunningCost();
283 Money r_b = e_b->GetRunningCost();
284 /* Check if running cost is zero in one or both engines.
285 * If only one of them is zero then that one has higher value,
286 * else if both have zero cost then compare powers. */
287 if (r_a == 0) {
288 if (r_b == 0) {
289 /* If it is ambiguous which to return go with their ID */
290 if (p_a == p_b) return EngineNumberSorter(a, b);
291 return _engine_sort_direction != (p_a < p_b);
292 }
294 }
295 if (r_b == 0) return _engine_sort_direction;
296 /* Using double for more precision when comparing close values.
297 * This shouldn't have any major effects in performance nor in keeping
298 * the game in sync between players since it's used in GUI only in client side */
299 double v_a = (double)p_a / (double)r_a;
300 double v_b = (double)p_b / (double)r_b;
301 /* Use EngineID to sort if both have same power/running cost,
302 * since we want consistent sorting.
303 * Also if both have no power then sort with reverse of running cost to simulate
304 * previous sorting behaviour for wagons. */
305 if (v_a == 0 && v_b == 0) return EngineRunningCostSorter(b, a);
306 if (v_a == v_b) return EngineNumberSorter(a, b);
307 return _engine_sort_direction != (v_a < v_b);
308}
309
310/* Train sorting functions */
311
319{
320 const RailVehicleInfo *rvi_a = RailVehInfo(a.engine_id);
321 const RailVehicleInfo *rvi_b = RailVehInfo(b.engine_id);
322
323 int va = GetTotalCapacityOfArticulatedParts(a.engine_id) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
324 int vb = GetTotalCapacityOfArticulatedParts(b.engine_id) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
325 int r = va - vb;
326
327 /* Use EngineID to sort instead since we want consistent sorting */
328 if (r == 0) return EngineNumberSorter(a, b);
329 return _engine_sort_direction ? r > 0 : r < 0;
330}
331
339{
340 int val_a = (RailVehInfo(a.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
341 int val_b = (RailVehInfo(b.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
342 int r = val_a - val_b;
343
344 /* Use EngineID to sort instead since we want consistent sorting */
345 if (r == 0) return EngineNumberSorter(a, b);
346 return _engine_sort_direction ? r > 0 : r < 0;
347}
348
349/* Road vehicle sorting functions */
350
358{
361 int r = va - vb;
362
363 /* Use EngineID to sort instead since we want consistent sorting */
364 if (r == 0) return EngineNumberSorter(a, b);
365 return _engine_sort_direction ? r > 0 : r < 0;
366}
367
368/* Ship vehicle sorting functions */
369
377{
378 const Engine *e_a = Engine::Get(a.engine_id);
379 const Engine *e_b = Engine::Get(b.engine_id);
380
381 int va = e_a->GetDisplayDefaultCapacity();
382 int vb = e_b->GetDisplayDefaultCapacity();
383 int r = va - vb;
384
385 /* Use EngineID to sort instead since we want consistent sorting */
386 if (r == 0) return EngineNumberSorter(a, b);
387 return _engine_sort_direction ? r > 0 : r < 0;
388}
389
390/* Aircraft sorting functions */
391
399{
400 const Engine *e_a = Engine::Get(a.engine_id);
401 const Engine *e_b = Engine::Get(b.engine_id);
402
403 uint16_t mail_a, mail_b;
404 int va = e_a->GetDisplayDefaultCapacity(&mail_a);
405 int vb = e_b->GetDisplayDefaultCapacity(&mail_b);
406 int r = va - vb;
407
408 if (r == 0) {
409 /* The planes have the same passenger capacity. Check mail capacity instead */
410 r = mail_a - mail_b;
411
412 if (r == 0) {
413 /* Use EngineID to sort instead since we want consistent sorting */
414 return EngineNumberSorter(a, b);
415 }
416 }
417 return _engine_sort_direction ? r > 0 : r < 0;
418}
419
427{
428 uint16_t r_a = Engine::Get(a.engine_id)->GetRange();
429 uint16_t r_b = Engine::Get(b.engine_id)->GetRange();
430
431 int r = r_a - r_b;
432
433 /* Use EngineID to sort instead since we want consistent sorting */
434 if (r == 0) return EngineNumberSorter(a, b);
435 return _engine_sort_direction ? r > 0 : r < 0;
436}
437
487
489const std::initializer_list<const StringID> _engine_sort_listing[] = {{
490 /* Trains */
491 STR_SORT_BY_ENGINE_ID,
492 STR_SORT_BY_COST,
493 STR_SORT_BY_MAX_SPEED,
494 STR_SORT_BY_POWER,
495 STR_SORT_BY_TRACTIVE_EFFORT,
496 STR_SORT_BY_INTRO_DATE,
497 STR_SORT_BY_NAME,
498 STR_SORT_BY_RUNNING_COST,
499 STR_SORT_BY_POWER_VS_RUNNING_COST,
500 STR_SORT_BY_RELIABILITY,
501 STR_SORT_BY_CARGO_CAPACITY,
502}, {
503 /* Road vehicles */
504 STR_SORT_BY_ENGINE_ID,
505 STR_SORT_BY_COST,
506 STR_SORT_BY_MAX_SPEED,
507 STR_SORT_BY_POWER,
508 STR_SORT_BY_TRACTIVE_EFFORT,
509 STR_SORT_BY_INTRO_DATE,
510 STR_SORT_BY_NAME,
511 STR_SORT_BY_RUNNING_COST,
512 STR_SORT_BY_POWER_VS_RUNNING_COST,
513 STR_SORT_BY_RELIABILITY,
514 STR_SORT_BY_CARGO_CAPACITY,
515}, {
516 /* Ships */
517 STR_SORT_BY_ENGINE_ID,
518 STR_SORT_BY_COST,
519 STR_SORT_BY_MAX_SPEED,
520 STR_SORT_BY_INTRO_DATE,
521 STR_SORT_BY_NAME,
522 STR_SORT_BY_RUNNING_COST,
523 STR_SORT_BY_RELIABILITY,
524 STR_SORT_BY_CARGO_CAPACITY,
525}, {
526 /* Aircraft */
527 STR_SORT_BY_ENGINE_ID,
528 STR_SORT_BY_COST,
529 STR_SORT_BY_MAX_SPEED,
530 STR_SORT_BY_INTRO_DATE,
531 STR_SORT_BY_NAME,
532 STR_SORT_BY_RUNNING_COST,
533 STR_SORT_BY_RELIABILITY,
534 STR_SORT_BY_CARGO_CAPACITY,
535 STR_SORT_BY_RANGE,
536}};
537
539static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoType cargo_type)
540{
541 if (cargo_type == CargoFilterCriteria::CF_ANY) {
542 return true;
543 } else if (cargo_type == CargoFilterCriteria::CF_ENGINES) {
544 return Engine::Get(item->engine_id)->GetPower() != 0;
545 } else {
546 CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask;
547 return (cargo_type == CargoFilterCriteria::CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cargo_type));
548 }
549}
550
551static GUIEngineList::FilterFunction * const _engine_filter_funcs[] = {
553};
554
555static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype)
556{
557 uint weight = 0;
558 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
559 if (cap[cargo] != 0) {
560 if (vtype == VEH_TRAIN) {
561 weight += CargoSpec::Get(cargo)->WeightOfNUnitsInTrain(cap[cargo]);
562 } else {
563 weight += CargoSpec::Get(cargo)->WeightOfNUnits(cap[cargo]);
564 }
565 }
566 }
567 return weight;
568}
569
570static int DrawCargoCapacityInfo(int left, int right, int y, TestedEngineDetails &te, bool refittable)
571{
572 for (const CargoSpec *cs : _sorted_cargo_specs) {
573 CargoType cargo_type = cs->Index();
574 if (te.all_capacities[cargo_type] == 0) continue;
575
576 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, cargo_type, te.all_capacities[cargo_type], refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY));
578 }
579
580 return y;
581}
582
583/* Draw rail wagon specific details */
584static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi, TestedEngineDetails &te)
585{
586 const Engine *e = Engine::Get(engine_number);
587
588 /* Purchase cost */
589 if (te.cost != 0) {
590 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost));
591 } else {
592 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost()));
593 }
595
596 /* Wagon weight - (including cargo) */
597 uint weight = e->GetDisplayWeight();
598 DrawString(left, right, y,
599 GetString(STR_PURCHASE_INFO_WEIGHT_CWEIGHT, weight, GetCargoWeight(te.all_capacities, VEH_TRAIN) + weight));
601
602 /* Wagon speed limit, displayed if above zero */
604 uint max_speed = e->GetDisplayMaxSpeed();
605 if (max_speed > 0) {
606 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED, PackVelocity(max_speed, e->type)));
608 }
609 }
610
611 /* Running cost */
612 if (rvi->running_cost_class != INVALID_PRICE) {
613 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
615 }
616
617 return y;
618}
619
620/* Draw locomotive specific details */
621static int DrawRailEnginePurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi, TestedEngineDetails &te)
622{
623 const Engine *e = Engine::Get(engine_number);
624
625 /* Purchase Cost - Engine weight */
626 if (te.cost != 0) {
627 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_WEIGHT, e->GetCost() + te.cost, te.cost, e->GetDisplayWeight()));
628 } else {
629 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_WEIGHT, e->GetCost(), e->GetDisplayWeight()));
630 }
632
633 /* Max speed - Engine power */
634 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_POWER, PackVelocity(e->GetDisplayMaxSpeed(), e->type), e->GetPower()));
636
637 /* Max tractive effort - not applicable if old acceleration or maglev */
639 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_MAX_TE, e->GetDisplayMaxTractiveEffort()));
641 }
642
643 /* Running cost */
644 if (rvi->running_cost_class != INVALID_PRICE) {
645 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
647 }
648
649 /* Powered wagons power - Powered wagons extra weight */
650 if (rvi->pow_wag_power != 0) {
651 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT, rvi->pow_wag_power, rvi->pow_wag_weight));
653 }
654
655 return y;
656}
657
658/* Draw road vehicle specific details */
659static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_number, TestedEngineDetails &te)
660{
661 const Engine *e = Engine::Get(engine_number);
662
664 /* Purchase Cost */
665 if (te.cost != 0) {
666 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost));
667 } else {
668 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost()));
669 }
671
672 /* Road vehicle weight - (including cargo) */
673 int16_t weight = e->GetDisplayWeight();
674 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_WEIGHT_CWEIGHT, weight, GetCargoWeight(te.all_capacities, VEH_ROAD) + weight));
676
677 /* Max speed - Engine power */
678 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_POWER, PackVelocity(e->GetDisplayMaxSpeed(), e->type), e->GetPower()));
680
681 /* Max tractive effort */
682 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_MAX_TE, e->GetDisplayMaxTractiveEffort()));
684 } else {
685 /* Purchase cost - Max speed */
686 if (te.cost != 0) {
687 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
688 } else {
689 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
690 }
692 }
693
694 /* Running cost */
695 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
697
698 return y;
699}
700
701/* Draw ship specific details */
702static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable, TestedEngineDetails &te)
703{
704 const Engine *e = Engine::Get(engine_number);
705
706 /* Purchase cost - Max speed */
707 uint raw_speed = e->GetDisplayMaxSpeed();
708 uint ocean_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, true);
709 uint canal_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, false);
710
711 if (ocean_speed == canal_speed) {
712 if (te.cost != 0) {
713 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(ocean_speed, e->type)));
714 } else {
715 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(ocean_speed, e->type)));
716 }
718 } else {
719 if (te.cost != 0) {
720 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost));
721 } else {
722 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost()));
723 }
725
726 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_OCEAN, PackVelocity(ocean_speed, e->type)));
728
729 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_CANAL, PackVelocity(canal_speed, e->type)));
731 }
732
733 /* Cargo type + capacity */
734 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, te.cargo, te.capacity, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY));
736
737 /* Running cost */
738 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
740
741 return y;
742}
743
753static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable, TestedEngineDetails &te)
754{
755 const Engine *e = Engine::Get(engine_number);
756
757 /* Purchase cost - Max speed */
758 if (te.cost != 0) {
759 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
760 } else {
761 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(e->GetDisplayMaxSpeed(), e->type)));
762 }
764
765 /* Cargo capacity */
766 if (te.mail_capacity > 0) {
767 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, te.cargo, te.capacity, GetCargoTypeByLabel(CT_MAIL), te.mail_capacity));
768 } else {
769 /* Note, if the default capacity is selected by the refit capacity
770 * callback, then the capacity shown is likely to be incorrect. */
771 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, te.cargo, te.capacity, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY));
772 }
774
775 /* Running cost */
776 DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost()));
778
779 /* Aircraft type */
780 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_TYPE, e->GetAircraftTypeText()));
782
783 /* Aircraft range, if available. */
784 uint16_t range = e->GetRange();
785 if (range != 0) {
786 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_RANGE, range));
788 }
789
790 return y;
791}
792
793
799static std::optional<std::string> GetNewGRFAdditionalText(EngineID engine)
800{
801 std::array<int32_t, 16> regs100;
802 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, nullptr, regs100);
803 if (callback == CALLBACK_FAILED || callback == 0x400) return std::nullopt;
804 const GRFFile *grffile = Engine::Get(engine)->GetGRF();
805 assert(grffile != nullptr);
806 if (callback == 0x40F) {
807 return GetGRFStringWithTextStack(grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
808 }
809 if (callback > 0x400) {
811 return std::nullopt;
812 }
813
814 return GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, regs100);
815}
816
825static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
826{
827 auto text = GetNewGRFAdditionalText(engine);
828 if (!text) return y;
829 return DrawStringMultiLine(left, right, y, INT32_MAX, *text, TC_BLACK);
830}
831
832void TestedEngineDetails::FillDefaultCapacities(const Engine *e)
833{
834 this->cargo = e->GetDefaultCargoType();
835 if (e->type == VEH_TRAIN || e->type == VEH_ROAD) {
837 this->capacity = this->all_capacities[this->cargo];
838 this->mail_capacity = 0;
839 } else {
841 this->all_capacities[this->cargo] = this->capacity;
842 if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) {
843 this->all_capacities[GetCargoTypeByLabel(CT_MAIL)] = this->mail_capacity;
844 } else {
845 this->mail_capacity = 0;
846 }
847 }
848 if (this->all_capacities.GetCount() == 0) this->cargo = INVALID_CARGO;
849}
850
857int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, TestedEngineDetails &te)
858{
859 const Engine *e = Engine::Get(engine_number);
860 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(e->intro_date);
861 bool refittable = IsArticulatedVehicleRefittable(engine_number);
862 bool articulated_cargo = false;
863
864 switch (e->type) {
865 default: NOT_REACHED();
866 case VEH_TRAIN:
867 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
868 y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->u.rail, te);
869 } else {
870 y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->u.rail, te);
871 }
872 articulated_cargo = true;
873 break;
874
875 case VEH_ROAD:
876 y = DrawRoadVehPurchaseInfo(left, right, y, engine_number, te);
877 articulated_cargo = true;
878 break;
879
880 case VEH_SHIP:
881 y = DrawShipPurchaseInfo(left, right, y, engine_number, refittable, te);
882 break;
883
884 case VEH_AIRCRAFT:
885 y = DrawAircraftPurchaseInfo(left, right, y, engine_number, refittable, te);
886 break;
887 }
888
889 if (articulated_cargo) {
890 /* Cargo type + capacity, or N/A */
891 int new_y = DrawCargoCapacityInfo(left, right, y, te, refittable);
892
893 if (new_y == y) {
894 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, INVALID_CARGO, 0, STR_EMPTY));
896 } else {
897 y = new_y;
898 }
899 }
900
901 /* Draw details that apply to all types except rail wagons. */
902 if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
903 /* Design date - Life length */
904 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_DESIGNED_LIFE, ymd.year, TimerGameCalendar::DateToYear(e->GetLifeLengthInDays())));
906
907 /* Reliability */
908 DrawString(left, right, y, GetString(STR_PURCHASE_INFO_RELIABILITY, ToPercent16(e->reliability)));
910 }
911
912 if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
913
914 y = DrawBadgeNameList({left, y, right, INT16_MAX}, e->badges, static_cast<GrfSpecFeature>(GSF_TRAINS + e->type));
915
916 /* Additional text from NewGRF */
917 y = ShowAdditionalText(left, right, y, engine_number);
918
919 /* The NewGRF's name which the vehicle comes from */
920 const GRFConfig *config = GetGRFConfig(e->GetGRFID());
921 if (_settings_client.gui.show_newgrf_name && config != nullptr)
922 {
923 DrawString(left, right, y, config->GetName(), TC_BLACK);
925 }
926
927 return y;
928}
929
930static void DrawEngineBadgeColumn(const Rect &r, int column_group, const GUIBadgeClasses &badge_classes, const Engine *e, PaletteID remap)
931{
932 DrawBadgeColumn(r, column_group, badge_classes, e->badges, static_cast<GrfSpecFeature>(GSF_TRAINS + e->type), e->info.base_intro, remap);
933}
934
945void 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)
946{
947 static const std::array<int8_t, VehicleType::VEH_COMPANY_END> sprite_y_offsets = { 0, 0, -1, -1 };
948
949 auto [first, last] = sb.GetVisibleRangeIterators(eng_list);
950
951 bool rtl = _current_text_dir == TD_RTL;
952 int step_size = GetEngineListHeight(type);
953 int sprite_left = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_left;
954 int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
955 int sprite_width = sprite_left + sprite_right;
956 int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
957 int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
958
959 auto badge_column_widths = badge_classes.GetColumnWidths();
960
961 Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
962 int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
963
964 Dimension replace_icon = {0, 0};
965 int count_width = 0;
966 if (show_count) {
967 replace_icon = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
968
969 uint biggest_num_engines = 0;
970 for (auto it = first; it != last; ++it) {
971 const uint num_engines = GetGroupNumEngines(_local_company, selected_group, it->engine_id);
972 biggest_num_engines = std::max(biggest_num_engines, num_engines);
973 }
974
975 count_width = GetStringBoundingBox(GetString(STR_JUST_COMMA, biggest_num_engines), FS_SMALL).width;
976 }
977
978 const int text_row_height = ir.Shrink(WidgetDimensions::scaled.matrix).Height();
979 const int normal_text_y_offset = (text_row_height - GetCharacterHeight(FS_NORMAL)) / 2;
980 const int small_text_y_offset = text_row_height - GetCharacterHeight(FS_SMALL);
981
982 const int offset = (rtl ? -circle_width : circle_width) / 2;
984
985 for (auto it = first; it != last; ++it) {
986 const auto &item = *it;
987 const Engine *e = Engine::Get(item.engine_id);
988
989 uint indent = item.indent * WidgetDimensions::scaled.hsep_indent;
990 bool has_variants = item.flags.Test(EngineDisplayFlag::HasVariants);
991 bool is_folded = item.flags.Test(EngineDisplayFlag::IsFolded);
992 bool shaded = item.flags.Test(EngineDisplayFlag::Shaded);
993
994 Rect textr = ir.Shrink(WidgetDimensions::scaled.matrix);
995 Rect tr = ir.Indent(indent, rtl);
996
997 if (item.indent > 0) {
998 /* Draw tree continuation lines. */
999 int tx = (rtl ? ir.right : ir.left) + offset;
1000 for (uint lvl = 1; lvl <= item.indent; ++lvl) {
1001 if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ir.top, tx, ir.bottom, linecolour, WidgetDimensions::scaled.fullbevel.top);
1002 if (lvl < item.indent) tx += level_width;
1003 }
1004 /* Draw our node in the tree. */
1005 int ycentre = CentreBounds(textr.top, textr.bottom, WidgetDimensions::scaled.fullbevel.top);
1006 if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ir.top, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
1007 GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
1008 }
1009
1010 if (has_variants) {
1011 Rect fr = tr.WithWidth(circle_width, rtl);
1012 DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, textr.top, fr.right, textr.bottom}, SA_CENTER);
1013 }
1014
1015 tr = tr.Indent(circle_width + WidgetDimensions::scaled.hsep_normal, rtl);
1016
1017 /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
1018 const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
1019 const PaletteID pal = (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company);
1020
1021 if (badge_column_widths.size() >= 1 && badge_column_widths[0] > 0) {
1022 Rect br = tr.WithWidth(badge_column_widths[0], rtl);
1023 DrawEngineBadgeColumn(br, 0, badge_classes, e, pal);
1024 tr = tr.Indent(badge_column_widths[0], rtl);
1025 }
1026
1027 int sprite_x = tr.WithWidth(sprite_width, rtl).left + sprite_left;
1028 DrawVehicleEngine(r.left, r.right, sprite_x, tr.top + sprite_y_offset, item.engine_id, pal, EIT_PURCHASE);
1029
1030 tr = tr.Indent(sprite_width + WidgetDimensions::scaled.hsep_wide, rtl);
1031
1032 if (badge_column_widths.size() >= 2 && badge_column_widths[1] > 0) {
1033 Rect br = tr.WithWidth(badge_column_widths[1], rtl);
1034 DrawEngineBadgeColumn(br, 1, badge_classes, e, pal);
1035 tr = tr.Indent(badge_column_widths[1], rtl);
1036 }
1037
1038 if (show_count) {
1039 /* Rect for replace-protection icon. */
1040 Rect rr = tr.WithWidth(replace_icon.width, !rtl);
1041 tr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_normal, !rtl);
1042 /* Rect for engine type count text. */
1043 Rect cr = tr.WithWidth(count_width, !rtl);
1044 tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal, !rtl);
1045
1046 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);
1047
1048 if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) {
1049 DrawSpriteIgnorePadding(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr, SA_CENTER);
1050 }
1051 }
1052
1053 if (badge_column_widths.size() >= 3 && badge_column_widths[2] > 0) {
1054 Rect br = tr.WithWidth(badge_column_widths[2], !rtl).Indent(WidgetDimensions::scaled.hsep_wide, rtl);
1055 DrawEngineBadgeColumn(br, 2, badge_classes, e, pal);
1056 tr = tr.Indent(badge_column_widths[2], !rtl);
1057 }
1058
1059 bool hidden = e->company_hidden.Test(_local_company);
1060 StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
1061 TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : ((hidden | shaded) ? (TC_GREY | TC_FORCED | TC_NO_SHADE) : TC_BLACK);
1062
1063 /* If the count is visible then this is part of in-use autoreplace list. */
1064 auto engine_name = PackEngineNameDParam(item.engine_id, show_count ? EngineNameContext::AutoreplaceVehicleInUse : EngineNameContext::PurchaseList, item.indent);
1065 DrawString(tr.left, tr.right, textr.top + normal_text_y_offset,GetString(str, engine_name), tc);
1066
1067 ir = ir.Translate(0, step_size);
1068 }
1069}
1070
1078void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, WidgetID button)
1079{
1080 uint32_t hidden_mask = 0;
1081 /* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
1082 if (vehicle_type == VEH_ROAD && _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
1083 SetBit(hidden_mask, 3); // power
1084 SetBit(hidden_mask, 4); // tractive effort
1085 SetBit(hidden_mask, 8); // power by running costs
1086 }
1087 /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
1088 if (vehicle_type == VEH_TRAIN && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
1089 SetBit(hidden_mask, 4); // tractive effort
1090 }
1091 ShowDropDownMenu(w, _engine_sort_listing[vehicle_type], selected, button, 0, hidden_mask);
1092}
1093
1101void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent, uint8_t indent)
1102{
1103 for (const auto &item : src) {
1104 if (item.variant_id != parent || item.engine_id == parent) continue;
1105
1106 const Engine *e = Engine::Get(item.engine_id);
1107 EngineDisplayFlags flags = item.flags;
1108 if (e->display_last_variant != EngineID::Invalid()) flags.Reset(EngineDisplayFlag::Shaded);
1109 dst.emplace_back(e->display_last_variant == EngineID::Invalid() ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
1110
1111 /* Add variants if not folded */
1112 if (item.flags.Test(EngineDisplayFlag::HasVariants) && !item.flags.Test(EngineDisplayFlag::IsFolded)) {
1113 /* Add this engine again as a child */
1114 if (!item.flags.Test(EngineDisplayFlag::Shaded)) {
1115 dst.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags{}, indent + 1);
1116 }
1117 GUIEngineListAddChildren(dst, src, item.engine_id, indent + 1);
1118 }
1119 }
1120
1121 if (indent > 0 || dst.empty()) return;
1122
1123 /* Hierarchy is complete, traverse in reverse to find where indentation levels continue. */
1124 uint16_t level_mask = 0;
1125 for (auto it = std::rbegin(dst); std::next(it) != std::rend(dst); ++it) {
1126 auto next_it = std::next(it);
1127 SB(level_mask, it->indent, 1, it->indent <= next_it->indent);
1128 next_it->level_mask = level_mask;
1129 }
1130}
1131
1136
1140 union {
1145 uint8_t sort_criteria = 0;
1146 bool show_hidden_engines = false;
1147 bool listview_mode = false;
1148 EngineID sel_engine = EngineID::Invalid();
1149 EngineID rename_engine = EngineID::Invalid();
1150 GUIEngineList eng_list{};
1153 Scrollbar *vscroll = nullptr;
1155 GUIBadgeClasses badge_classes{};
1156
1157 static constexpr int BADGE_COLUMNS = 3;
1158
1161
1162 void SetBuyVehicleText()
1163 {
1164 NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_BUILD);
1165
1166 bool refit = this->sel_engine != EngineID::Invalid() && this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY && this->cargo_filter_criteria != CargoFilterCriteria::CF_NONE && this->cargo_filter_criteria != CargoFilterCriteria::CF_ENGINES;
1167 if (refit) refit = Engine::Get(this->sel_engine)->GetDefaultCargoType() != this->cargo_filter_criteria;
1168
1169 if (refit) {
1170 widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type, STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP + this->vehicle_type);
1171 } else {
1172 widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type, STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + this->vehicle_type);
1173 }
1174 }
1175
1177 {
1178 this->vehicle_type = type;
1179 this->listview_mode = tile == INVALID_TILE;
1180 this->window_number = this->listview_mode ? (int)type : tile.base();
1181
1182 this->sort_criteria = _engine_sort_last_criteria[type];
1183 this->descending_sort_order = _engine_sort_last_order[type];
1184 this->show_hidden_engines = _engine_sort_show_hidden_engines[type];
1185
1186 this->UpdateFilterByTile();
1187
1188 this->CreateNestedTree();
1189
1190 this->vscroll = this->GetScrollbar(WID_BV_SCROLLBAR);
1191
1192 /* If we are just viewing the list of vehicles, we do not need the Build button.
1193 * So we just hide it, and enlarge the Rename button by the now vacant place. */
1194 if (this->listview_mode) this->GetWidget<NWidgetStacked>(WID_BV_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE);
1195
1196 NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_LIST);
1197 widget->SetToolTip(STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type);
1198
1199 widget = this->GetWidget<NWidgetCore>(WID_BV_SHOW_HIDE);
1200 widget->SetToolTip(STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP + type);
1201
1202 widget = this->GetWidget<NWidgetCore>(WID_BV_RENAME);
1203 widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type, STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type);
1204
1205 widget = this->GetWidget<NWidgetCore>(WID_BV_SHOW_HIDDEN_ENGINES);
1206 widget->SetStringTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + type, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + type);
1207 widget->SetLowered(this->show_hidden_engines);
1208
1209 this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
1210
1211 if (tile == INVALID_TILE) {
1212 this->FinishInitNested(type);
1213 } else {
1214 this->FinishInitNested(tile);
1215 }
1216
1218 this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR;
1219
1220 this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
1221
1222 this->eng_list.ForceRebuild();
1223 this->GenerateBuildList(); // generate the list, since we need it in the next line
1224
1225 /* Select the first unshaded engine in the list as default when opening the window */
1226 EngineID engine = EngineID::Invalid();
1227 auto it = std::ranges::find_if(this->eng_list, [](const GUIEngineListItem &item) { return !item.flags.Test(EngineDisplayFlag::Shaded); });
1228 if (it != this->eng_list.end()) engine = it->engine_id;
1229 this->SelectEngine(engine);
1230 }
1231
1234 {
1235 switch (this->vehicle_type) {
1236 default: NOT_REACHED();
1237 case VEH_TRAIN:
1238 if (this->listview_mode) {
1239 this->filter.railtype = INVALID_RAILTYPE;
1240 } else {
1241 this->filter.railtype = GetRailType(this->window_number);
1242 }
1243 break;
1244
1245 case VEH_ROAD:
1246 if (this->listview_mode) {
1247 this->filter.roadtype = INVALID_ROADTYPE;
1248 } else {
1249 this->filter.roadtype = GetRoadTypeRoad(this->window_number);
1250 if (this->filter.roadtype == INVALID_ROADTYPE) {
1251 this->filter.roadtype = GetRoadTypeTram(this->window_number);
1252 }
1253 }
1254 break;
1255
1256 case VEH_SHIP:
1257 case VEH_AIRCRAFT:
1258 break;
1259 }
1260 }
1261
1262 StringID GetCargoFilterLabel(CargoType cargo_type) const
1263 {
1264 switch (cargo_type) {
1265 case CargoFilterCriteria::CF_ANY: return STR_PURCHASE_INFO_ALL_TYPES;
1266 case CargoFilterCriteria::CF_ENGINES: return STR_PURCHASE_INFO_ENGINES_ONLY;
1267 case CargoFilterCriteria::CF_NONE: return STR_PURCHASE_INFO_NONE;
1268 default: return CargoSpec::Get(cargo_type)->name;
1269 }
1270 }
1271
1274 {
1275 /* Set the last cargo filter criteria. */
1278
1279 this->eng_list.SetFilterFuncs(_engine_filter_funcs);
1281 }
1282
1283 void SelectEngine(EngineID engine)
1284 {
1285 CargoType cargo = this->cargo_filter_criteria;
1286 if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
1287
1288 this->sel_engine = engine;
1289 this->SetBuyVehicleText();
1290
1291 if (this->sel_engine == EngineID::Invalid()) return;
1292
1293 const Engine *e = Engine::Get(this->sel_engine);
1294
1295 if (!this->listview_mode) {
1296 /* Query for cost and refitted capacity */
1297 auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<CMD_BUILD_VEHICLE>::Do(DoCommandFlag::QueryCost, TileIndex(this->window_number), this->sel_engine, true, cargo, INVALID_CLIENT_ID);
1298 if (ret.Succeeded()) {
1299 this->te.cost = ret.GetCost() - e->GetCost();
1300 this->te.capacity = refit_capacity;
1301 this->te.mail_capacity = refit_mail;
1302 this->te.cargo = !IsValidCargoType(cargo) ? e->GetDefaultCargoType() : cargo;
1303 this->te.all_capacities = cargo_capacities;
1304 return;
1305 }
1306 }
1307
1308 /* Purchase test was not possible or failed, fill in the defaults instead. */
1309 this->te.cost = 0;
1310 this->te.FillDefaultCapacities(e);
1311 }
1312
1313 void OnInit() override
1314 {
1315 this->badge_classes = GUIBadgeClasses(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type));
1316 this->SetCargoFilterArray();
1317 }
1318
1321 {
1322 this->eng_list.Filter(this->cargo_filter_criteria);
1323 if (0 == this->eng_list.size()) { // no engine passed through the filter, invalidate the previously selected engine
1324 this->SelectEngine(EngineID::Invalid());
1325 } 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
1326 this->SelectEngine(this->eng_list[0].engine_id);
1327 }
1328 }
1329
1332 {
1333 GUIEngineListItem item = {eid, eid, EngineDisplayFlags{}, 0};
1334 return CargoAndEngineFilter(&item, this->cargo_filter_criteria);
1335 }
1336
1338 bool FilterByText(const Engine *e)
1339 {
1340 /* Do not filter if the filter text box is empty */
1341 if (this->string_filter.IsEmpty()) return true;
1342
1343 /* Filter engine name */
1344 this->string_filter.ResetState();
1346
1347 /* Filter NewGRF extra text */
1348 auto text = GetNewGRFAdditionalText(e->index);
1349 if (text) this->string_filter.AddLine(*text);
1350
1351 return this->string_filter.GetState();
1352 }
1353
1354 /* Figure out what train EngineIDs to put in the list */
1355 void GenerateBuildTrainList(GUIEngineList &list)
1356 {
1357 std::vector<EngineID> variants;
1358 EngineID sel_id = EngineID::Invalid();
1359 size_t num_engines = 0;
1360
1361 list.clear();
1362
1363 BadgeTextFilter btf(this->string_filter, GSF_TRAINS);
1364
1365 /* Make list of all available train engines and wagons.
1366 * Also check to see if the previously selected engine is still available,
1367 * and if not, reset selection to EngineID::Invalid(). This could be the case
1368 * when engines become obsolete and are removed */
1369 for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
1370 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1371 EngineID eid = e->index;
1372 const RailVehicleInfo *rvi = &e->u.rail;
1373
1374 if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
1375 if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
1376
1377 /* Filter now! So num_engines and num_wagons is valid */
1378 if (!FilterSingleEngine(eid)) continue;
1379
1380 /* Filter by name or NewGRF extra text */
1381 if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
1382
1383 list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1384
1385 if (rvi->railveh_type != RAILVEH_WAGON) num_engines++;
1386
1387 /* Add all parent variants of this engine to the variant list */
1388 EngineID parent = e->info.variant_id;
1389 while (parent != EngineID::Invalid()) {
1390 variants.push_back(parent);
1391 parent = Engine::Get(parent)->info.variant_id;
1392 }
1393
1394 if (eid == this->sel_engine) sel_id = eid;
1395 }
1396
1397 /* ensure primary engine of variant group is in list */
1398 for (const auto &variant : variants) {
1399 if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) {
1400 const Engine *e = Engine::Get(variant);
1401 list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0);
1402 if (e->u.rail.railveh_type != RAILVEH_WAGON) num_engines++;
1403 }
1404 }
1405
1406 this->SelectEngine(sel_id);
1407
1408 /* invalidate cached values for name sorter - engine names could change */
1409 _last_engine[0] = _last_engine[1] = EngineID::Invalid();
1410
1411 /* make engines first, and then wagons, sorted by selected sort_criteria */
1412 _engine_sort_direction = false;
1414
1415 /* and then sort engines */
1417 EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
1418
1419 /* and finally sort wagons */
1420 EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], num_engines, list.size() - num_engines);
1421 }
1422
1423 /* Figure out what road vehicle EngineIDs to put in the list */
1424 void GenerateBuildRoadVehList()
1425 {
1426 EngineID sel_id = EngineID::Invalid();
1427
1428 this->eng_list.clear();
1429
1430 BadgeTextFilter btf(this->string_filter, GSF_ROADVEHICLES);
1431
1432 for (const Engine *e : Engine::IterateType(VEH_ROAD)) {
1433 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1434 EngineID eid = e->index;
1435 if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
1436 if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue;
1437
1438 /* Filter by name or NewGRF extra text */
1439 if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
1440
1441 this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1442
1443 if (eid == this->sel_engine) sel_id = eid;
1444 }
1445 this->SelectEngine(sel_id);
1446 }
1447
1448 /* Figure out what ship EngineIDs to put in the list */
1449 void GenerateBuildShipList()
1450 {
1451 EngineID sel_id = EngineID::Invalid();
1452 this->eng_list.clear();
1453
1454 BadgeTextFilter btf(this->string_filter, GSF_SHIPS);
1455
1456 for (const Engine *e : Engine::IterateType(VEH_SHIP)) {
1457 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1458 EngineID eid = e->index;
1459 if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
1460
1461 /* Filter by name or NewGRF extra text */
1462 if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
1463
1464 this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1465
1466 if (eid == this->sel_engine) sel_id = eid;
1467 }
1468 this->SelectEngine(sel_id);
1469 }
1470
1471 /* Figure out what aircraft EngineIDs to put in the list */
1472 void GenerateBuildAircraftList()
1473 {
1474 EngineID sel_id = EngineID::Invalid();
1475
1476 this->eng_list.clear();
1477
1478 const Station *st = this->listview_mode ? nullptr : Station::GetByTile(TileIndex(this->window_number));
1479
1480 BadgeTextFilter btf(this->string_filter, GSF_AIRCRAFT);
1481
1482 /* Make list of all available planes.
1483 * Also check to see if the previously selected plane is still available,
1484 * and if not, reset selection to EngineID::Invalid(). This could be the case
1485 * when planes become obsolete and are removed */
1486 for (const Engine *e : Engine::IterateType(VEH_AIRCRAFT)) {
1487 if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
1488 EngineID eid = e->index;
1489 if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue;
1490 /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
1491 if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
1492
1493 /* Filter by name or NewGRF extra text */
1494 if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
1495
1496 this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
1497
1498 if (eid == this->sel_engine) sel_id = eid;
1499 }
1500
1501 this->SelectEngine(sel_id);
1502 }
1503
1504 /* Generate the list of vehicles */
1505 void GenerateBuildList()
1506 {
1507 if (!this->eng_list.NeedRebuild()) return;
1508
1509 /* Update filter type in case the road/railtype of the depot got converted */
1510 this->UpdateFilterByTile();
1511
1512 this->eng_list.clear();
1513
1514 GUIEngineList list;
1515
1516 switch (this->vehicle_type) {
1517 default: NOT_REACHED();
1518 case VEH_TRAIN:
1519 this->GenerateBuildTrainList(list);
1520 GUIEngineListAddChildren(this->eng_list, list);
1521 this->eng_list.RebuildDone();
1522 return;
1523 case VEH_ROAD:
1524 this->GenerateBuildRoadVehList();
1525 break;
1526 case VEH_SHIP:
1527 this->GenerateBuildShipList();
1528 break;
1529 case VEH_AIRCRAFT:
1530 this->GenerateBuildAircraftList();
1531 break;
1532 }
1533
1534 this->FilterEngineList();
1535
1536 /* ensure primary engine of variant group is in list after filtering */
1537 std::vector<EngineID> variants;
1538 for (const auto &item : this->eng_list) {
1539 EngineID parent = item.variant_id;
1540 while (parent != EngineID::Invalid()) {
1541 variants.push_back(parent);
1542 parent = Engine::Get(parent)->info.variant_id;
1543 }
1544 }
1545
1546 for (const auto &variant : variants) {
1547 if (std::ranges::find(this->eng_list, variant, &GUIEngineListItem::engine_id) == this->eng_list.end()) {
1548 const Engine *e = Engine::Get(variant);
1549 this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0);
1550 }
1551 }
1552
1554 EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
1555
1556 this->eng_list.swap(list);
1557 GUIEngineListAddChildren(this->eng_list, list, EngineID::Invalid(), 0);
1558 this->eng_list.RebuildDone();
1559 }
1560
1561 DropDownList BuildCargoDropDownList() const
1562 {
1563 DropDownList list;
1564
1565 /* Add item for disabling filtering. */
1566 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
1567 /* Specific filters for trains. */
1568 if (this->vehicle_type == VEH_TRAIN) {
1569 /* Add item for locomotives only in case of trains. */
1570 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ENGINES), CargoFilterCriteria::CF_ENGINES));
1571 /* Add item for vehicles not carrying anything, e.g. train engines.
1572 * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
1573 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
1574 }
1575
1576 /* Add cargos */
1578 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1579 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
1580 }
1581
1582 return list;
1583 }
1584
1585 DropDownList BuildBadgeConfigurationList() const
1586 {
1587 static const auto separators = {STR_BADGE_CONFIG_PREVIEW, STR_BADGE_CONFIG_NAME};
1588 return BuildBadgeClassConfigurationList(this->badge_classes, BADGE_COLUMNS, separators);
1589 }
1590
1591 void BuildVehicle()
1592 {
1593 EngineID sel_eng = this->sel_engine;
1594 if (sel_eng == EngineID::Invalid()) return;
1595
1596 CargoType cargo = this->cargo_filter_criteria;
1597 if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
1598 if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
1599 Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, TileIndex(this->window_number), sel_eng, true, cargo, INVALID_CLIENT_ID);
1600 } else {
1601 Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, TileIndex(this->window_number), sel_eng, true, cargo, INVALID_CLIENT_ID);
1602 }
1603
1604 /* Update last used variant in hierarchy and refresh if necessary. */
1605 bool refresh = false;
1606 EngineID parent = sel_eng;
1607 while (parent != EngineID::Invalid()) {
1609 refresh |= (e->display_last_variant != sel_eng);
1610 e->display_last_variant = sel_eng;
1611 parent = e->info.variant_id;
1612 }
1613
1614 if (refresh) {
1615 InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
1616 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
1617 }
1618 }
1619
1620 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1621 {
1622 switch (widget) {
1624 this->descending_sort_order ^= true;
1626 this->eng_list.ForceRebuild();
1627 this->SetDirty();
1628 break;
1629
1631 this->show_hidden_engines ^= true;
1633 this->eng_list.ForceRebuild();
1634 this->SetWidgetLoweredState(widget, this->show_hidden_engines);
1635 this->SetDirty();
1636 break;
1637
1638 case WID_BV_LIST: {
1639 EngineID e = EngineID::Invalid();
1640 const auto it = this->vscroll->GetScrolledItemFromWidget(this->eng_list, pt.y, this, WID_BV_LIST);
1641 if (it != this->eng_list.end()) {
1642 const auto &item = *it;
1643 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);
1644 if (item.flags.Test(EngineDisplayFlag::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) {
1645 /* toggle folded flag on engine */
1646 assert(item.variant_id != EngineID::Invalid());
1647 Engine *engine = Engine::Get(item.variant_id);
1649
1650 InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
1651 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
1652 return;
1653 }
1654 if (!item.flags.Test(EngineDisplayFlag::Shaded)) e = item.engine_id;
1655 }
1656 this->SelectEngine(e);
1657 this->SetDirty();
1658 if (_ctrl_pressed) {
1659 this->OnClick(pt, WID_BV_SHOW_HIDE, 1);
1660 } else if (click_count > 1 && !this->listview_mode) {
1661 this->OnClick(pt, WID_BV_BUILD, 1);
1662 }
1663 break;
1664 }
1665
1666 case WID_BV_SORT_DROPDOWN: // Select sorting criteria dropdown menu
1667 DisplayVehicleSortDropDown(this, this->vehicle_type, this->sort_criteria, WID_BV_SORT_DROPDOWN);
1668 break;
1669
1670 case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
1671 ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria, widget);
1672 break;
1673
1675 ShowDropDownList(this, this->BuildBadgeConfigurationList(), -1, widget, 0, false, true);
1676 break;
1677
1678 case WID_BV_SHOW_HIDE: {
1679 const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine);
1680 if (e != nullptr) {
1682 }
1683 break;
1684 }
1685
1686 case WID_BV_BUILD:
1687 this->BuildVehicle();
1688 break;
1689
1690 case WID_BV_RENAME: {
1691 EngineID sel_eng = this->sel_engine;
1692 if (sel_eng != EngineID::Invalid()) {
1693 this->rename_engine = sel_eng;
1695 }
1696 break;
1697 }
1698 }
1699 }
1700
1706 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1707 {
1708 if (!gui_scope) return;
1709 /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
1710 if (this->vehicle_type == VEH_ROAD &&
1712 this->sort_criteria > 7) {
1713 this->sort_criteria = 0;
1715 }
1716 this->eng_list.ForceRebuild();
1717 }
1718
1719 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1720 {
1721 switch (widget) {
1722 case WID_BV_CAPTION:
1723 if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
1724 const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
1725 return GetString(rti->strings.build_caption);
1726 }
1727 if (this->vehicle_type == VEH_ROAD && !this->listview_mode) {
1728 const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype);
1729 return GetString(rti->strings.build_caption);
1730 }
1731 return GetString((this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
1732
1734 return GetString(std::data(_engine_sort_listing[this->vehicle_type])[this->sort_criteria]);
1735
1737 return GetString(this->GetCargoFilterLabel(this->cargo_filter_criteria));
1738
1739 case WID_BV_SHOW_HIDE: {
1740 const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine);
1741 if (e != nullptr && e->IsHidden(_local_company)) {
1742 return GetString(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type);
1743 }
1744 return GetString(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
1745 }
1746
1747 default:
1748 return this->Window::GetWidgetString(widget, stringid);
1749 }
1750 }
1751
1752 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1753 {
1754 switch (widget) {
1755 case WID_BV_LIST:
1756 resize.height = GetEngineListHeight(this->vehicle_type);
1757 size.height = 3 * resize.height;
1758 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;
1759 break;
1760
1761 case WID_BV_PANEL:
1762 size.height = GetCharacterHeight(FS_NORMAL) * this->details_height + padding.height;
1763 break;
1764
1766 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->GetString());
1767 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1768 d.height += padding.height;
1769 size = maxdim(size, d);
1770 break;
1771 }
1772
1774 size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width);
1775 break;
1776
1778 /* Hide the configuration button if no configurable badges are present. */
1779 if (this->badge_classes.GetClasses().empty()) size = {0, 0};
1780 break;
1781
1782 case WID_BV_BUILD:
1783 size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type);
1784 size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type));
1785 size.width += padding.width;
1786 size.height += padding.height;
1787 break;
1788
1789 case WID_BV_SHOW_HIDE:
1790 size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
1791 size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type));
1792 size.width += padding.width;
1793 size.height += padding.height;
1794 break;
1795 }
1796 }
1797
1798 void DrawWidget(const Rect &r, WidgetID widget) const override
1799 {
1800 switch (widget) {
1801 case WID_BV_LIST:
1803 this->vehicle_type,
1804 r,
1805 this->eng_list,
1806 *this->vscroll,
1807 this->sel_engine,
1808 false,
1810 this->badge_classes
1811 );
1812 break;
1813
1815 this->DrawSortButtonState(WID_BV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
1816 break;
1817 }
1818 }
1819
1820 void OnPaint() override
1821 {
1822 this->GenerateBuildList();
1823 this->vscroll->SetCount(this->eng_list.size());
1824
1825 this->SetWidgetsDisabledState(this->sel_engine == EngineID::Invalid(), WID_BV_SHOW_HIDE, WID_BV_BUILD);
1826
1827 /* Disable renaming engines in network games if you are not the server. */
1828 this->SetWidgetDisabledState(WID_BV_RENAME, this->sel_engine == EngineID::Invalid() || (_networking && !_network_server));
1829
1830 this->DrawWidgets();
1831
1832 if (!this->IsShaded()) {
1833 int needed_height = this->details_height;
1834 /* Draw details panels. */
1835 if (this->sel_engine != EngineID::Invalid()) {
1836 const Rect r = this->GetWidget<NWidgetBase>(WID_BV_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
1837 int text_end = DrawVehiclePurchaseInfo(r.left, r.right, r.top, this->sel_engine, this->te);
1838 needed_height = std::max(needed_height, (text_end - r.top) / GetCharacterHeight(FS_NORMAL));
1839 }
1840 if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
1841 int resize = needed_height - this->details_height;
1842 this->details_height = needed_height;
1843 this->ReInit(0, resize * GetCharacterHeight(FS_NORMAL));
1844 return;
1845 }
1846 }
1847 }
1848
1849 void OnQueryTextFinished(std::optional<std::string> str) override
1850 {
1851 if (!str.has_value()) return;
1852
1853 Command<CMD_RENAME_ENGINE>::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, *str);
1854 }
1855
1856 void OnDropdownSelect(WidgetID widget, int index, int click_result) override
1857 {
1858 switch (widget) {
1860 if (this->sort_criteria != index) {
1861 this->sort_criteria = index;
1863 this->eng_list.ForceRebuild();
1864 }
1865 break;
1866
1867 case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
1868 if (this->cargo_filter_criteria != index) {
1869 this->cargo_filter_criteria = index;
1871 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1873 this->eng_list.ForceRebuild();
1874 this->SelectEngine(this->sel_engine);
1875 }
1876 break;
1877
1879 bool reopen = HandleBadgeConfigurationDropDownClick(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type), BADGE_COLUMNS, index, click_result);
1880
1881 this->ReInit();
1882
1883 if (reopen) {
1884 ReplaceDropDownList(this, this->BuildBadgeConfigurationList(), -1);
1885 } else {
1887 }
1888 break;
1889 }
1890 }
1891 this->SetDirty();
1892 }
1893
1894 void OnResize() override
1895 {
1896 this->vscroll->SetCapacityFromWidget(this, WID_BV_LIST);
1897 }
1898
1899 void OnEditboxChanged(WidgetID wid) override
1900 {
1901 if (wid == WID_BV_FILTER) {
1902 this->string_filter.SetFilterTerm(this->vehicle_editbox.text.GetText());
1903 this->InvalidateData();
1904 }
1905 }
1906
1907 EventState OnHotkey(int hotkey) override
1908 {
1909 switch (hotkey) {
1912 SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
1913 return ES_HANDLED;
1914
1915 default:
1916 return ES_NOT_HANDLED;
1917 }
1918
1919 return ES_HANDLED;
1920 }
1921
1922 static inline HotkeyList hotkeys{"buildvehicle", {
1923 Hotkey('F', "focus_filter_box", BVHK_FOCUS_FILTER_BOX),
1924 }};
1925};
1926
1927static WindowDesc _build_vehicle_desc(
1928 WDP_AUTO, "build_vehicle", 240, 268,
1931 _nested_build_vehicle_widgets,
1932 &BuildVehicleWindow::hotkeys
1933);
1934
1935void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
1936{
1937 /* We want to be able to open both Available Train as Available Ships,
1938 * so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
1939 * As it always is a low value, it won't collide with any real tile
1940 * number. */
1941 uint num = (tile == INVALID_TILE) ? (int)type : tile.base();
1942
1943 assert(IsCompanyBuildableVehicleType(type));
1944
1946
1947 new BuildVehicleWindow(_build_vehicle_desc, tile, type);
1948}
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.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by introduction date.
static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
Determines order of engines by purchase cost.
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.
BuildVehicleHotkeys
Enum referring to the Hotkeys in the build vehicle window.
@ BVHK_FOCUS_FILTER_BOX
Focus the edit box for editing the filter string.
static bool 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_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:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
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.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetFilterState(bool state)
Enable or disable the filter.
bool(const GUIEngineListItem *, CargoType) FilterFunction
Signature of filter function.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
Base class for a 'real' widget.
void SetToolTip(StringID tool_tip)
Set the tool tip of the nested widget.
Definition widget.cpp:1231
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:1171
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:118
StringID build_caption
Caption of the build vehicle GUI for this rail type.
Definition rail.h:170
struct RailTypeInfo::@23 strings
Strings associated with the rail type.
uint8_t acceleration_type
Acceleration type of this rail type.
Definition rail.h:217
struct RoadTypeInfo::@26 strings
Strings associated with the rail type.
StringID build_caption
Caption of the build vehicle GUI for this rail type.
Definition road.h:97
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:2556
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)
Calculate the year of a given date.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:39
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:61
int hsep_indent
Width of indentation for tree layouts.
Definition window_gui.h:63
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:451
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:414
Dimension GetDropDownListDimension(const DropDownList &list)
Determine width and height required to fully display a DropDownList.
Definition dropdown.cpp:373
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.
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1246
Base class for engines.
@ HasVariants
Set if engine has variants.
@ IsFolded
Set if display of variants should be folded (hidden).
@ Shaded
Set if engine should be masked.
Command definitions related to engines.
Functions related to engines.
uint GetTotalCapacityOfArticulatedParts(EngineID engine)
Get the capacity of an engine with articulated parts.
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'.
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.
@ 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:77
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:958
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:887
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:658
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
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:775
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:252
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:251
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:385
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:395
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:393
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:302
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:327
@ TC_NO_SHADE
Do not add shading to this text colour.
Definition gfx_type.h:326
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 NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
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 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:955
static constexpr GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:18
Hotkey related functions.
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.
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:96
static constexpr CargoType CF_ENGINES
Show only engines (for rail vehicles only)
Definition cargo_type.h:97
static constexpr CargoType CF_ANY
Show all items independent of carried cargo (i.e. no filtering)
Definition cargo_type.h:95
bool _networking
are we in networking mode?
Definition network.cpp:67
bool _network_server
network-server is active
Definition network.cpp:68
Basic functions/variables used all over the place.
@ INVALID_CLIENT_ID
Client is not part of anything.
GrfSpecFeature
Definition newgrf.h:69
Functions related to NewGRF badges.
Functions related to NewGRF badge configuration.
@ 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.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
uint8_t 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.
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:341
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:258
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:230
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:1611
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:425
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 GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:415
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:57
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.
void OnPaint() override
The window must be repainted.
int details_height
Minimal needed height of the details panels, in text lines (found so far).
QueryString vehicle_editbox
Filter editbox.
EventState OnHotkey(int hotkey) override
A hotkey has been pressed.
union BuildVehicleWindow::@0 filter
Filter to apply.
void OnResize() override
Called after the window got resized.
bool show_hidden_engines
State of the 'show hidden engines' button.
void FilterEngineList()
Filter the engine list against the currently selected cargo filter.
EngineID rename_engine
Engine being renamed.
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::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:113
uint GetCount() const
Get the amount of cargos that have an amount.
Definition cargo_type.h:128
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
GUISettings gui
settings related to the GUI
Dimensions (a width and height) of a rectangle in 2D.
EngineID variant_id
Engine variant ID. If set, will be treated specially in purchase lists.
TimerGameCalendar::Date base_intro
Basic date of engine introduction (without random parts).
StringID GetAircraftTypeText() const
Get the name of the aircraft type for display purposes.
Definition engine.cpp:467
uint GetPower() const
Returns the power of the engine for display and sorting purposes.
Definition engine.cpp:390
uint16_t GetRange() const
Get the range of an aircraft type.
Definition engine.cpp:453
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:157
Money GetCost() const
Return how much a new engine costs.
Definition engine.cpp:318
TimerGameCalendar::Date intro_date
Date of introduction of the engine.
Definition engine_base.h:45
uint GetDisplayMaxSpeed() const
Returns max speed of the engine for display purposes.
Definition engine.cpp:358
EngineDisplayFlags display_flags
NOSAVE client-side-only display flags for build engine list.
Definition engine_base.h:63
uint GetDisplayWeight() const
Returns the weight of the engine for display purposes.
Definition engine.cpp:408
VehicleType type
Vehicle type, ie VEH_ROAD, VEH_TRAIN, etc.
Definition engine_base.h:61
bool IsVariantHidden(CompanyID c) const
Check whether the engine variant chain is hidden in the GUI for the given company.
Definition engine.cpp:487
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:443
uint GetDisplayDefaultCapacity(uint16_t *mail_capacity=nullptr) const
Determines the default cargo capacity of an engine for display purposes.
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:96
CompanyMask company_hidden
Bit for each company whether the engine is normally hidden in the build gui for that company.
Definition engine_base.h:40
Money GetRunningCost() const
Return how much the running costs of this engine are.
Definition engine.cpp:281
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:48
uint GetDisplayMaxTractiveEffort() const
Returns the tractive effort of the engine for display purposes.
Definition engine.cpp:426
bool IsHidden(CompanyID c) const
Check whether the engine is hidden in the GUI for the given company.
EngineID display_last_variant
NOSAVE client-side-only last variant selected.
Definition engine_base.h:64
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:115
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
bool show_newgrf_name
Show the name of the NewGRF in the build vehicle window.
VehicleSettings vehicle
options for vehicles
List of hotkeys for a window.
Definition hotkeys.h:37
All data for a single hotkey.
Definition hotkeys.h:21
Partial widget specification to allow NWidgets to be written nested.
Coordinates of a point in 2D.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
Data stored about a string that can be modified in the GUI.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
static const int ACTION_CLEAR
Clear editbox.
Information about a rail vehicle.
Definition engine_type.h:47
uint16_t pow_wag_power
Extra power applied to consist if wagon should be powered.
Definition engine_type.h:61
RailType railtype
Railtype, mangled if elrail is disabled.
Definition engine_type.h:51
uint8_t pow_wag_weight
Extra weight applied to consist if wagon should be powered.
Definition engine_type.h:62
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 Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
RoadType roadtype
Road type.
uint ApplyWaterClassSpeedFrac(uint raw_speed, bool is_ocean) const
Apply ocean/canal speed fraction to a velocity.
Definition engine_type.h:86
Station data structure.
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
uint8_t roadveh_acceleration_model
realistic acceleration for road vehicles
uint8_t train_acceleration_model
realistic acceleration for trains
bool wagon_speed_limits
enable wagon speed limits
High level window description.
Definition window_gui.h:167
Data structure for an opened window.
Definition window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:967
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:826
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1778
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:320
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:777
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3205
Window * parent
Parent window.
Definition window_gui.h:328
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:503
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:809
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1064
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:515
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1768
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:484
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:316
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:441
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:559
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:312
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:381
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
Stuff related to the text buffer GUI.
@ EnableDefault
enable the 'Default' button ("\0" is returned)
@ LengthIsInChars
the length of the string is counted in characters
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
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:87
Definition of the game-calendar-timer.
Command definitions related to trains.
void CcBuildWagon(Commands cmd, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray, TileIndex tile, EngineID, bool, 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:2112
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition vehicle.cpp:3019
Command definitions for vehicles.
void CcBuildPrimaryVehicle(Commands cmd, 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:42
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:63
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:67
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:45
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:58
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:51
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:56
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:53
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:77
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:69
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:61
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:60
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:57
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:62
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:72
@ SZSP_NONE
Display plane with zero size in both directions (none filling and resizing).
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:1182
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:420
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:3265
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:3282
Window functions not directly related to making/drawing windows.
@ Construction
This window is used for construction; close it whenever changing company.
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:218
@ SBS_UP
Sort descending.
Definition window_gui.h:219
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ WC_REPLACE_VEHICLE
Replace vehicle window; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:47
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers:
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107