OpenTTD Source 20260512-master-g20b387b91f
vehicle_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "debug.h"
12#include "company_func.h"
13#include "gui.h"
14#include "textbuf_gui.h"
15#include "command_func.h"
16#include "vehicle_gui_base.h"
17#include "viewport_func.h"
18#include "newgrf_text.h"
19#include "newgrf_debug.h"
20#include "roadveh.h"
21#include "train.h"
22#include "aircraft.h"
23#include "depot_map.h"
24#include "group_gui.h"
25#include "strings_func.h"
26#include "vehicle_func.h"
27#include "autoreplace_gui.h"
28#include "string_func.h"
29#include "dropdown_type.h"
30#include "dropdown_func.h"
31#include "timetable.h"
35#include "company_base.h"
36#include "engine_func.h"
37#include "station_base.h"
38#include "tilehighlight_func.h"
39#include "zoom_func.h"
40#include "depot_cmd.h"
41#include "vehicle_cmd.h"
42#include "order_cmd.h"
43#include "roadveh_cmd.h"
44#include "train_cmd.h"
45#include "hotkeys.h"
46#include "group_cmd.h"
47
48#include "table/strings.h"
49
50#include "safeguards.h"
51
52
53static std::array<VehicleTypeIndexArray<BaseVehicleListWindow::GroupBy>, VLT_END> _grouping{};
54static std::array<Sorting, BaseVehicleListWindow::GB_END> _sorting{};
55
56static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNumberSorter;
57static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNameSorter;
58static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleAgeSorter;
59static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitThisYearSorter;
60static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitLastYearSorter;
61static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleCargoSorter;
62static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleReliabilitySorter;
63static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleMaxSpeedSorter;
64static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleModelSorter;
65static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleValueSorter;
66static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleLengthSorter;
67static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimeToLiveSorter;
68static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimetableDelaySorter;
69static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupLengthSorter;
70static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitThisYearSorter;
71static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitLastYearSorter;
72static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitThisYearSorter;
73static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitLastYearSorter;
74
76template <BaseVehicleListWindow::VehicleIndividualSortFunction func>
78{
79 return func(*(a.vehicles_begin), *(b.vehicles_begin));
80}
81
82const std::initializer_list<BaseVehicleListWindow::VehicleGroupSortFunction * const> BaseVehicleListWindow::vehicle_group_none_sorter_funcs = {
96};
97
98const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_none_sorter_names_calendar = {
99 STR_SORT_BY_NUMBER,
100 STR_SORT_BY_NAME,
101 STR_SORT_BY_AGE,
102 STR_SORT_BY_PROFIT_THIS_YEAR,
103 STR_SORT_BY_PROFIT_LAST_YEAR,
104 STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
105 STR_SORT_BY_RELIABILITY,
106 STR_SORT_BY_MAX_SPEED,
107 STR_SORT_BY_MODEL,
108 STR_SORT_BY_VALUE,
109 STR_SORT_BY_LENGTH,
110 STR_SORT_BY_LIFE_TIME,
111 STR_SORT_BY_TIMETABLE_DELAY,
112};
113
114const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_none_sorter_names_wallclock = {
115 STR_SORT_BY_NUMBER,
116 STR_SORT_BY_NAME,
117 STR_SORT_BY_AGE,
118 STR_SORT_BY_PROFIT_THIS_PERIOD,
119 STR_SORT_BY_PROFIT_LAST_PERIOD,
120 STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
121 STR_SORT_BY_RELIABILITY,
122 STR_SORT_BY_MAX_SPEED,
123 STR_SORT_BY_MODEL,
124 STR_SORT_BY_VALUE,
125 STR_SORT_BY_LENGTH,
126 STR_SORT_BY_LIFE_TIME,
127 STR_SORT_BY_TIMETABLE_DELAY,
128};
129
130const std::initializer_list<BaseVehicleListWindow::VehicleGroupSortFunction * const> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_funcs = {
131 &VehicleGroupLengthSorter,
132 &VehicleGroupTotalProfitThisYearSorter,
133 &VehicleGroupTotalProfitLastYearSorter,
134 &VehicleGroupAverageProfitThisYearSorter,
135 &VehicleGroupAverageProfitLastYearSorter,
136};
137
138const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_names_calendar = {
139 STR_SORT_BY_NUM_VEHICLES,
140 STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR,
141 STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR,
142 STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR,
143 STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR,
144};
145
146const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_names_wallclock = {
147 STR_SORT_BY_NUM_VEHICLES,
148 STR_SORT_BY_TOTAL_PROFIT_THIS_PERIOD,
149 STR_SORT_BY_TOTAL_PROFIT_LAST_PERIOD,
150 STR_SORT_BY_AVERAGE_PROFIT_THIS_PERIOD,
151 STR_SORT_BY_AVERAGE_PROFIT_LAST_PERIOD,
152};
153
154const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_by_names = {
155 STR_GROUP_BY_NONE,
156 STR_GROUP_BY_SHARED_ORDERS,
157};
158
161 STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT,
162 STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT,
163 STR_VEHICLE_LIST_SEND_SHIP_TO_DEPOT,
164 STR_VEHICLE_LIST_SEND_AIRCRAFT_TO_HANGAR
165};
166
167BaseVehicleListWindow::BaseVehicleListWindow(WindowDesc &desc, const VehicleListIdentifier &vli) : Window(desc), vli(vli)
168{
169 this->vehicle_sel = VehicleID::Invalid();
170 this->grouping = _grouping[vli.type][vli.vtype];
171 this->UpdateSortingFromGrouping();
172}
173
174std::span<const StringID> BaseVehicleListWindow::GetVehicleSorterNames() const
175{
176 switch (this->grouping) {
177 case GB_NONE:
178 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_none_sorter_names_wallclock : vehicle_group_none_sorter_names_calendar;
179 case GB_SHARED_ORDERS:
180 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_shared_orders_sorter_names_wallclock : vehicle_group_shared_orders_sorter_names_calendar;
181 default:
182 NOT_REACHED();
183 }
184}
185
192{
193 if (number >= 10000) return 5;
194 if (number >= 1000) return 4;
195 if (number >= 100) return 3;
196
197 /*
198 * When the smallest unit number is less than 10, it is
199 * quite likely that it will expand to become more than
200 * 10 quite soon.
201 */
202 return 2;
203}
204
211{
212 uint unitnumber = 0;
213 for (const Vehicle *v : vehicles) {
214 unitnumber = std::max<uint>(unitnumber, v->unitnumber);
215 }
216
217 return CountDigitsForAllocatingSpace(unitnumber);
218}
219
220void BaseVehicleListWindow::BuildVehicleList()
221{
222 if (!this->vehgroups.NeedRebuild()) return;
223
224 Debug(misc, 3, "Building vehicle list type {} for company {} given index {}", this->vli.type, this->vli.company, this->vli.index);
225
226 this->vehgroups.clear();
227
228 GenerateVehicleSortList(&this->vehicles, this->vli);
229
230 CargoTypes used{};
231 for (const Vehicle *v : this->vehicles) {
232 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
233 if (u->cargo_cap > 0) used.Set(u->cargo_type);
234 }
235 }
236 this->used_cargoes = used;
237
238 if (this->grouping == GB_NONE) {
239 uint max_unitnumber = 0;
240 for (auto it = this->vehicles.begin(); it != this->vehicles.end(); ++it) {
241 this->vehgroups.emplace_back(it, it + 1);
242
243 max_unitnumber = std::max<uint>(max_unitnumber, (*it)->unitnumber);
244 }
245 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber);
246 } else {
247 /* Sort by the primary vehicle; we just want all vehicles that share the same orders to form a contiguous range. */
248 std::stable_sort(this->vehicles.begin(), this->vehicles.end(), [](const Vehicle * const &u, const Vehicle * const &v) {
249 return u->FirstShared() < v->FirstShared();
250 });
251
252 uint max_num_vehicles = 0;
253
254 VehicleList::const_iterator begin = this->vehicles.begin();
255 while (begin != this->vehicles.end()) {
256 VehicleList::const_iterator end = std::find_if_not(begin, this->vehicles.cend(), [first_shared = (*begin)->FirstShared()](const Vehicle * const &v) {
257 return v->FirstShared() == first_shared;
258 });
259
260 this->vehgroups.emplace_back(begin, end);
261
262 max_num_vehicles = std::max<uint>(max_num_vehicles, static_cast<uint>(end - begin));
263
264 begin = end;
265 }
266
267 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_num_vehicles);
268 }
269 this->FilterVehicleList();
270
271 this->vehgroups.RebuildDone();
272 this->vscroll->SetCount(this->vehgroups.size());
273}
274
282static bool CargoFilterSingle(const Vehicle *v, const CargoType cargo_type)
283{
284 if (cargo_type == CargoFilterCriteria::CF_ANY) {
285 return true;
286 } else if (cargo_type == CargoFilterCriteria::CF_NONE) {
287 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
288 if (w->cargo_cap > 0) {
289 return false;
290 }
291 }
292 return true;
293 } else if (cargo_type == CargoFilterCriteria::CF_FREIGHT) {
294 bool have_capacity = false;
295 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
296 if (w->cargo_cap > 0) {
297 if (IsCargoInClass(w->cargo_type, CargoClass::Passengers)) {
298 return false;
299 } else {
300 have_capacity = true;
301 }
302 }
303 }
304 return have_capacity;
305 } else {
306 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
307 if (w->cargo_cap > 0 && w->cargo_type == cargo_type) {
308 return true;
309 }
310 }
311 return false;
312 }
313}
314
322static bool CargoFilter(const GUIVehicleGroup *vehgroup, const CargoType cargo_type)
323{
324 auto it = vehgroup->vehicles_begin;
325
326 /* Check if any vehicle in the group matches; if so, the whole group does. */
327 for (; it != vehgroup->vehicles_end; it++) {
328 if (CargoFilterSingle(*it, cargo_type)) return true;
329 }
330
331 return false;
332}
333
339{
341}
342
350void AddCargoIconOverlay(std::vector<CargoIconOverlay> &overlays, int x, int width, const Vehicle *v)
351{
352 bool rtl = _current_text_dir == TD_RTL;
353 if (!v->IsArticulatedPart() || v->cargo_type != v->Previous()->cargo_type) {
354 /* Add new overlay slot. */
355 overlays.emplace_back(rtl ? x - width : x, rtl ? x : x + width, v->cargo_type, v->cargo_cap);
356 } else {
357 /* This is an articulated part with the same cargo type, adjust left or right of last overlay slot. */
358 if (rtl) {
359 overlays.back().left -= width;
360 } else {
361 overlays.back().right += width;
362 }
363 overlays.back().cargo_cap += v->cargo_cap;
364 }
365}
366
373void DrawCargoIconOverlay(int x, int y, CargoType cargo_type)
374{
375 if (!ShowCargoIconOverlay()) return;
376 if (!IsValidCargoType(cargo_type)) return;
377
378 const CargoSpec *cs = CargoSpec::Get(cargo_type);
379
380 SpriteID spr = cs->GetCargoIcon();
381 if (spr == 0) return;
382
383 Dimension d = GetSpriteSize(spr);
384 d.width /= 2;
385 d.height /= 2;
386 int one = ScaleGUITrad(1);
387
388 /* Draw the cargo icon in black shifted 4 times to create the outline. */
389 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width - one, y - d.height);
390 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width + one, y - d.height);
391 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height - one);
392 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height + one);
393 /* Draw the cargo icon normally. */
394 DrawSprite(spr, PAL_NONE, x - d.width, y - d.height);
395}
396
402void DrawCargoIconOverlays(std::span<const CargoIconOverlay> overlays, int y)
403{
404 for (const auto &cio : overlays) {
405 if (cio.cargo_cap == 0) continue;
406 DrawCargoIconOverlay((cio.left + cio.right) / 2, y, cio.cargo_type);
407 }
408}
409
410static GUIVehicleGroupList::FilterFunction * const _vehicle_group_filter_funcs[] = {
412};
413
419{
420 if (this->cargo_filter_criteria != cargo_type) {
421 this->cargo_filter_criteria = cargo_type;
422 /* Deactivate filter if criteria is 'Show All', activate it otherwise. */
423 this->vehgroups.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
424 this->vehgroups.SetFilterType(0);
425 this->vehgroups.ForceRebuild();
426 }
427}
428
433{
435 this->vehgroups.SetFilterFuncs(_vehicle_group_filter_funcs);
436 this->vehgroups.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
437}
438
443{
444 this->vehgroups.Filter(this->cargo_filter_criteria);
445 if (this->vehicles.empty()) {
446 /* No vehicle passed through the filter, invalidate the previously selected vehicle */
447 this->vehicle_sel = VehicleID::Invalid();
448 } else if (this->vehicle_sel != VehicleID::Invalid() && std::ranges::find(this->vehicles, Vehicle::Get(this->vehicle_sel)) == this->vehicles.end()) { // previously selected engine didn't pass the filter, remove selection
449 this->vehicle_sel = VehicleID::Invalid();
450 }
451}
452
460Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create)
461{
462 Dimension d = {0, 0};
463
464 if (show_autoreplace) d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_REPLACE_VEHICLES));
465 d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_SEND_FOR_SERVICING));
466 d = maxdim(d, GetStringBoundingBox(this->vehicle_depot_name[this->vli.vtype]));
467
468 if (show_group) {
469 d = maxdim(d, GetStringBoundingBox(STR_GROUP_ADD_SHARED_VEHICLE));
470 d = maxdim(d, GetStringBoundingBox(STR_GROUP_REMOVE_ALL_VEHICLES));
471 } else if (show_create) {
472 d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_CREATE_GROUP));
473 }
474
475 return d;
476}
477
479{
480 this->order_arrow_width = std::max(GetStringBoundingBox(STR_JUST_LEFT_ARROW, FontSize::Small).width, GetStringBoundingBox(STR_JUST_RIGHT_ARROW, FontSize::Small).width);
481 this->SetCargoFilterArray();
482}
483
484StringID BaseVehicleListWindow::GetCargoFilterLabel(CargoType cargo_type) const
485{
486 switch (cargo_type) {
487 case CargoFilterCriteria::CF_ANY: return STR_CARGO_TYPE_FILTER_ALL;
488 case CargoFilterCriteria::CF_FREIGHT: return STR_CARGO_TYPE_FILTER_FREIGHT;
489 case CargoFilterCriteria::CF_NONE: return STR_CARGO_TYPE_FILTER_NONE;
490 default: return CargoSpec::Get(cargo_type)->name;
491 }
492}
493
500{
501 DropDownList list;
502
503 /* Add item for disabling filtering. */
505 /* Add item for freight (i.e. vehicles with cargo capacity and with no passenger capacity). */
507 /* Add item for vehicles not carrying anything, e.g. train engines. */
509
510 /* Add cargos */
512 for (const CargoSpec *cs : _sorted_cargo_specs) {
513 if (!full && !this->used_cargoes.Test(cs->Index())) continue;
514 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index(), false, !this->used_cargoes.Test(cs->Index())));
515 }
516
517 return list;
518}
519
527DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create)
528{
529 DropDownList list;
530
531 /* Autoreplace actions. */
532 if (show_autoreplace) {
533 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_REPLACE_VEHICLES, ADI_REPLACE));
534 list.push_back(MakeDropDownListDividerItem());
535 }
536
537 /* Group actions. */
538 if (show_group) {
539 list.push_back(MakeDropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE, ADI_ADD_SHARED));
540 list.push_back(MakeDropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, ADI_REMOVE_ALL));
541 list.push_back(MakeDropDownListDividerItem());
542 } else if (show_create) {
543 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_CREATE_GROUP, ADI_CREATE_GROUP));
544 list.push_back(MakeDropDownListDividerItem());
545 }
546
547 /* Depot actions. */
548 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_SEND_FOR_SERVICING, ADI_SERVICE));
549 list.push_back(MakeDropDownListStringItem(this->vehicle_depot_name[this->vli.vtype], ADI_DEPOT));
550
551 return list;
552}
553
555static const Vehicle *_last_vehicle[2] = { nullptr, nullptr };
556
557void BaseVehicleListWindow::SortVehicleList()
558{
559 if (this->vehgroups.Sort()) return;
560
561 /* invalidate cached values for name sorter - vehicle names could change */
562 _last_vehicle[0] = _last_vehicle[1] = nullptr;
563}
564
565void DepotSortList(VehicleList *list)
566{
567 if (list->size() < 2) return;
568 std::sort(list->begin(), list->end(), &VehicleNumberSorter);
569}
570
579static void DrawVehicleProfitButton(TimerGameEconomy::Date age, Money display_profit_last_year, uint num_vehicles, int x, int y)
580{
581 SpriteID spr;
582
583 /* draw profit-based coloured icons */
584 if (age <= VEHICLE_PROFIT_MIN_AGE) {
585 spr = SPR_PROFIT_NA;
586 } else if (display_profit_last_year < 0) {
587 spr = SPR_PROFIT_NEGATIVE;
588 } else if (display_profit_last_year < VEHICLE_PROFIT_THRESHOLD * num_vehicles) {
589 spr = SPR_PROFIT_SOME;
590 } else {
591 spr = SPR_PROFIT_LOT;
592 }
593 DrawSprite(spr, PAL_NONE, x, y);
594}
595
597static const uint MAX_REFIT_CYCLE = 256;
598
608uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_cargo_type)
609{
610 v_from = v_from->GetFirstEnginePart();
611 v_for = v_for->GetFirstEnginePart();
612
613 /* Create a list of subtypes used by the various parts of v_for */
614 static std::vector<StringID> subtypes;
615 subtypes.clear();
616 for (; v_from != nullptr; v_from = v_from->HasArticulatedPart() ? v_from->GetNextArticulatedPart() : nullptr) {
617 const Engine *e_from = v_from->GetEngine();
618 if (!e_from->CanCarryCargo() || !e_from->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
619 include(subtypes, GetCargoSubtypeText(v_from));
620 }
621
622 uint8_t ret_refit_cyc = 0;
623 bool success = false;
624 if (!subtypes.empty()) {
625 /* Check whether any articulated part is refittable to 'dest_cargo_type' with a subtype listed in 'subtypes' */
626 for (Vehicle *v = v_for; v != nullptr; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr) {
627 const Engine *e = v->GetEngine();
628 if (!e->CanCarryCargo() || !e->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
629 if (!e->info.refit_mask.Test(dest_cargo_type) && v->cargo_type != dest_cargo_type) continue;
630
631 CargoType old_cargo_type = v->cargo_type;
632 uint8_t old_cargo_subtype = v->cargo_subtype;
633
634 /* Set the 'destination' cargo */
635 v->cargo_type = dest_cargo_type;
636
637 /* Cycle through the refits */
638 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
639 v->cargo_subtype = refit_cyc;
640
641 /* Make sure we don't pick up anything cached. */
642 v->First()->InvalidateNewGRFCache();
643 v->InvalidateNewGRFCache();
644
645 StringID subtype = GetCargoSubtypeText(v);
646 if (subtype == STR_EMPTY) break;
647
648 if (std::ranges::find(subtypes, subtype) == subtypes.end()) continue;
649
650 /* We found something matching. */
651 ret_refit_cyc = refit_cyc;
652 success = true;
653 break;
654 }
655
656 /* Reset the vehicle's cargo type */
657 v->cargo_type = old_cargo_type;
658 v->cargo_subtype = old_cargo_subtype;
659
660 /* Make sure we don't taint the vehicle. */
661 v->First()->InvalidateNewGRFCache();
662 v->InvalidateNewGRFCache();
663
664 if (success) break;
665 }
666 }
667
668 return ret_refit_cyc;
669}
670
674 uint8_t subtype;
676
682 inline bool operator == (const RefitOption &other) const
683 {
684 return other.cargo == this->cargo && other.string == this->string;
685 }
686};
687
688using RefitOptions = std::map<CargoType, std::vector<RefitOption>, CargoTypeComparator>;
689
699static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption *sel, uint pos, uint rows, uint delta, const Rect &r)
700{
701 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
702 uint current = 0;
703
704 bool rtl = _current_text_dir == TD_RTL;
705 uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
706 uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
708
709 int iconleft = rtl ? ir.right - iconwidth : ir.left;
710 int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;
711 int iconinner = rtl ? ir.right - iconwidth : ir.left + iconwidth;
712
713 Rect tr = ir.Indent(iconwidth + WidgetDimensions::scaled.hsep_wide, rtl);
714
715 /* Draw the list of subtypes for each cargo, and find the selected refit option (by its position). */
716 for (const auto &pair : refits) {
717 bool has_subtypes = pair.second.size() > 1;
718 for (const RefitOption &refit : pair.second) {
719 if (current >= pos + rows) break;
720
721 /* Hide subtypes if selected cargo type does not match */
722 if ((sel == nullptr || sel->cargo != refit.cargo) && refit.subtype != UINT8_MAX) continue;
723
724 /* Refit options with a position smaller than pos don't have to be drawn. */
725 if (current < pos) {
726 current++;
727 continue;
728 }
729
730 if (has_subtypes) {
731 if (refit.subtype != UINT8_MAX) {
732 /* Draw tree lines */
733 int ycenter = tr.top + GetCharacterHeight(FontSize::Normal) / 2;
734 GfxDrawLine(iconcenter, tr.top - WidgetDimensions::scaled.matrix.top, iconcenter, (&refit == &pair.second.back()) ? ycenter : tr.top - WidgetDimensions::scaled.matrix.top + delta - 1, linecolour);
735 GfxDrawLine(iconcenter, ycenter, iconinner, ycenter, linecolour);
736 } else {
737 /* Draw expand/collapse icon */
738 DrawSprite((sel != nullptr && sel->cargo == refit.cargo) ? SPR_CIRCLE_UNFOLDED : SPR_CIRCLE_FOLDED, PAL_NONE, iconleft, tr.top + (GetCharacterHeight(FontSize::Normal) - iconheight) / 2);
739 }
740 }
741
742 TextColour colour = (sel != nullptr && sel->cargo == refit.cargo && sel->subtype == refit.subtype) ? TC_WHITE : TC_BLACK;
743 /* Get the cargo name. */
744 DrawString(tr, GetString(STR_JUST_STRING_STRING, CargoSpec::Get(refit.cargo)->name, refit.string), colour);
745
746 tr.top += delta;
747 current++;
748 }
749 }
750}
751
753struct RefitWindow : public Window {
754 const RefitOption *selected_refit = nullptr;
758 Scrollbar *vscroll = nullptr;
759 Scrollbar *hscroll = nullptr;
761 int sprite_left = 0;
762 int sprite_right = 0;
763 uint vehicle_margin = 0;
764 int click_x = 0;
766 uint8_t num_vehicles = 0;
767 bool auto_refit = false;
768
773 {
774 /* Store the currently selected RefitOption. */
775 std::optional<RefitOption> current_refit_option;
776 if (this->selected_refit != nullptr) current_refit_option = *(this->selected_refit);
777 this->selected_refit = nullptr;
778
779 this->refit_list.clear();
781
782 /* Check only the selected vehicles. */
783 VehicleSet vehicles_to_refit;
784 GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
785
786 do {
787 if (v->type == VehicleType::Train && std::ranges::find(vehicles_to_refit, v->index) == vehicles_to_refit.end()) continue;
788 const Engine *e = v->GetEngine();
789 CargoTypes cmask = e->info.refit_mask;
790 VehicleCallbackMasks callback_mask = e->info.callback_mask;
791
792 /* Skip this engine if it does not carry anything */
793 if (!e->CanCarryCargo()) continue;
794 /* Skip this engine if we build the list for auto-refitting and engine doesn't allow it. */
795 if (this->auto_refit && !e->info.misc_flags.Test(EngineMiscFlag::AutoRefit)) continue;
796
797 /* Loop through all cargoes in the refit mask */
798 for (const auto &cs : _sorted_cargo_specs) {
799 CargoType cargo_type = cs->Index();
800 /* Skip cargo type if it's not listed */
801 if (!cmask.Test(cargo_type)) continue;
802
803 auto &list = this->refit_list[cargo_type];
804 bool first_vehicle = list.empty();
805 if (first_vehicle) {
806 /* Keeping the current subtype is always an option. It also serves as the option in case of no subtypes */
807 list.emplace_back(cargo_type, UINT8_MAX, STR_EMPTY);
808 }
809
810 /* Check the vehicle's callback mask for cargo suffixes.
811 * This is not supported for ordered refits, since subtypes only have a meaning
812 * for a specific vehicle at a specific point in time, which conflicts with shared orders,
813 * autoreplace, autorenew, clone, order restoration, ... */
814 if (this->order == INVALID_VEH_ORDER_ID && callback_mask.Test(VehicleCallbackMask::CargoSuffix)) {
815 /* Make a note of the original cargo type. It has to be
816 * changed to test the cargo & subtype... */
817 CargoType temp_cargo = v->cargo_type;
818 uint8_t temp_subtype = v->cargo_subtype;
819
820 v->cargo_type = cargo_type;
821
822 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
823 v->cargo_subtype = refit_cyc;
824
825 /* Make sure we don't pick up anything cached. */
828
829 StringID subtype = GetCargoSubtypeText(v);
830
831 if (first_vehicle) {
832 /* Append new subtype (don't add duplicates though) */
833 if (subtype == STR_EMPTY) break;
834
835 RefitOption option{cargo_type, static_cast<uint8_t>(refit_cyc), subtype};
836 include(list, option);
837 } else {
838 /* Intersect the subtypes of earlier vehicles with the subtypes of this vehicle */
839 if (subtype == STR_EMPTY) {
840 /* No more subtypes for this vehicle, delete all subtypes >= refit_cyc */
841 /* UINT8_MAX item is in front, other subtypes are sorted. So just truncate the list in the right spot */
842 for (uint i = 1; i < list.size(); i++) {
843 if (list[i].subtype >= refit_cyc) {
844 list.erase(list.begin() + i, list.end());
845 break;
846 }
847 }
848 break;
849 } else {
850 /* Check whether the subtype matches with the subtype of earlier vehicles. */
851 uint pos = 1;
852 while (pos < list.size() && list[pos].subtype != refit_cyc) pos++;
853 if (pos < list.size() && list[pos].string != subtype) {
854 /* String mismatch, remove item keeping the order */
855 list.erase(list.begin() + pos);
856 }
857 }
858 }
859 }
860
861 /* Reset the vehicle's cargo type */
862 v->cargo_type = temp_cargo;
863 v->cargo_subtype = temp_subtype;
864
865 /* And make sure we haven't tainted the cache */
868 }
869 }
870 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
871
872 /* Restore the previously selected RefitOption. */
873 if (current_refit_option.has_value()) {
874 for (const auto &pair : this->refit_list) {
875 for (const auto &refit : pair.second) {
876 if (refit.cargo == current_refit_option->cargo && refit.subtype == current_refit_option->subtype) {
877 this->selected_refit = &refit;
878 break;
879 }
880 }
881 if (this->selected_refit != nullptr) break;
882 }
883 }
884
885 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
886 }
887
892 {
893 size_t scroll_row = 0;
894 size_t rows = 0;
895 CargoType cargo = this->selected_refit == nullptr ? INVALID_CARGO : this->selected_refit->cargo;
896
897 for (const auto &pair : this->refit_list) {
898 if (pair.first == cargo) {
899 /* selected_refit points to an element in the vector so no need to search for it. */
900 scroll_row = rows + (this->selected_refit - pair.second.data());
901 rows += pair.second.size();
902 } else {
903 rows++; /* Unselected cargo type is collapsed into one row. */
904 }
905 }
906
907 this->vscroll->SetCount(rows);
908 this->vscroll->ScrollTowards(static_cast<int>(scroll_row));
909 }
910
915 void SetSelection(uint click_row)
916 {
917 uint row = 0;
918
919 for (const auto &pair : refit_list) {
920 for (const RefitOption &refit : pair.second) {
921 if (row == click_row) {
922 this->selected_refit = &refit;
923 return;
924 }
925 row++;
926 /* If this cargo type is not already selected then its subtypes are not visible, so skip the rest. */
927 if (this->selected_refit == nullptr || this->selected_refit->cargo != refit.cargo) break;
928 }
929 }
930
931 /* No selection made */
932 this->selected_refit = nullptr;
933 }
934
935 RefitWindow(WindowDesc &desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window(desc)
936 {
937 this->auto_refit = auto_refit;
938 this->order = order;
939 this->CreateNestedTree();
940
941 this->vscroll = this->GetScrollbar(WID_VR_SCROLLBAR);
942 this->hscroll = (v->IsGroundVehicle() ? this->GetScrollbar(WID_VR_HSCROLLBAR) : nullptr);
943 this->GetWidget<NWidgetCore>(WID_VR_SELECT_HEADER)->SetToolTip(STR_REFIT_TRAIN_LIST_TOOLTIP + to_underlying(v->type));
944 this->GetWidget<NWidgetCore>(WID_VR_MATRIX)->SetToolTip(STR_REFIT_TRAIN_LIST_TOOLTIP + to_underlying(v->type));
946 nwi->SetStringTip(STR_REFIT_TRAIN_REFIT_BUTTON + to_underlying(v->type), STR_REFIT_TRAIN_REFIT_TOOLTIP + to_underlying(v->type));
948 this->GetWidget<NWidgetCore>(WID_VR_VEHICLE_PANEL_DISPLAY)->SetToolTip((v->type == VehicleType::Train) ? STR_REFIT_SELECT_VEHICLES_TOOLTIP : STR_NULL);
949
950 this->FinishInitNested(v->index);
951 this->owner = v->owner;
952
953 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
954 }
955
956 void OnInit() override
957 {
958 /* (Re)build the refit list */
960 }
961
962 void OnPaint() override
963 {
964 /* Determine amount of items for scroller. */
965 if (this->hscroll != nullptr) this->hscroll->SetCount(this->vehicle_width);
966
967 /* Calculate sprite position. */
969 int sprite_width = std::max(0, ((int)vehicle_panel_display->current_x - this->vehicle_width) / 2);
970 this->sprite_left = vehicle_panel_display->pos_x;
971 this->sprite_right = vehicle_panel_display->pos_x + vehicle_panel_display->current_x - 1;
972 if (_current_text_dir == TD_RTL) {
973 this->sprite_right -= sprite_width;
974 this->vehicle_margin = vehicle_panel_display->current_x - sprite_right;
975 } else {
976 this->sprite_left += sprite_width;
977 this->vehicle_margin = sprite_left;
978 }
979
980 this->DrawWidgets();
981 }
982
983 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
984 {
985 switch (widget) {
986 case WID_VR_MATRIX:
987 fill.height = resize.height = GetCharacterHeight(FontSize::Normal) + padding.height;
988 size.height = resize.height * 8;
989 break;
990
992 size.height = ScaleGUITrad(GetVehicleHeight(Vehicle::Get(this->window_number)->type));
993 break;
994
995 case WID_VR_INFO:
996 size.width = this->information_width + padding.height;
997 break;
998 }
999 }
1000
1001 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1002 {
1003 if (widget == WID_VR_CAPTION) return GetString(STR_REFIT_CAPTION, Vehicle::Get(this->window_number)->index);
1004
1005 return this->Window::GetWidgetString(widget, stringid);
1006 }
1007
1014 std::string GetCapacityString(const RefitOption &option) const
1015 {
1017 auto [cost, refit_capacity, mail_capacity, cargo_capacities] = Command<Commands::RefitVehicle>::Do(DoCommandFlag::QueryCost, this->selected_vehicle, option.cargo, option.subtype, this->auto_refit, false, this->num_vehicles);
1018
1019 if (cost.Failed()) return {};
1020
1021 Money money = cost.GetCost();
1022 if (mail_capacity > 0) {
1023 if (this->order != INVALID_VEH_ORDER_ID) {
1024 /* No predictable cost */
1025 return GetString(STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity);
1026 }
1027
1028 if (money <= 0) {
1029 return GetString(STR_REFIT_NEW_CAPACITY_INCOME_FROM_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, -money);
1030 }
1031
1032 return GetString(STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, money);
1033 }
1034
1035 if (this->order != INVALID_VEH_ORDER_ID) {
1036 /* No predictable cost */
1037 return GetString(STR_PURCHASE_INFO_CAPACITY, option.cargo, refit_capacity, STR_EMPTY);
1038 }
1039
1040 if (money <= 0) {
1041 return GetString(STR_REFIT_NEW_CAPACITY_INCOME_FROM_REFIT, option.cargo, refit_capacity, -money);
1042 }
1043
1044 return GetString(STR_REFIT_NEW_CAPACITY_COST_OF_REFIT, option.cargo, refit_capacity, money);
1045 }
1046
1047 void DrawWidget(const Rect &r, WidgetID widget) const override
1048 {
1049 switch (widget) {
1052 DrawVehicleImage(v, r.WithX(this->sprite_left, this->sprite_right),
1053 VehicleID::Invalid(), EIT_IN_DETAILS, this->hscroll != nullptr ? this->hscroll->GetPosition() : 0);
1054
1055 /* Highlight selected vehicles. */
1056 if (this->order != INVALID_VEH_ORDER_ID) break;
1057 int x = 0;
1058 switch (v->type) {
1059 case VehicleType::Train: {
1060 VehicleSet vehicles_to_refit;
1061 GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
1062
1063 int left = INT32_MIN;
1064 int width = 0;
1065
1066 /* Determine top & bottom position of the highlight.*/
1067 const int height = ScaleSpriteTrad(12);
1068 const int highlight_top = CentreBounds(r.top, r.bottom, height);
1069 const int highlight_bottom = highlight_top + height - 1;
1070
1071 for (Train *u = Train::From(v); u != nullptr; u = u->Next()) {
1072 /* Start checking. */
1073 const bool contained = std::ranges::find(vehicles_to_refit, u->index) != vehicles_to_refit.end();
1074 if (contained && left == INT32_MIN) {
1075 left = x - this->hscroll->GetPosition() + r.left + this->vehicle_margin;
1076 width = 0;
1077 }
1078
1079 /* Draw a selection. */
1080 if ((!contained || u->Next() == nullptr) && left != INT32_MIN) {
1081 if (u->Next() == nullptr && contained) {
1082 int current_width = u->GetDisplayImageWidth();
1083 width += current_width;
1084 x += current_width;
1085 }
1086
1087 int right = Clamp(left + width, 0, r.right);
1088 left = std::max(0, left);
1089
1090 if (_current_text_dir == TD_RTL) {
1091 right = r.Width() - left;
1092 left = right - width;
1093 }
1094
1095 if (left != right) {
1096 Rect hr = {left, highlight_top, right, highlight_bottom};
1098 }
1099
1100 left = INT32_MIN;
1101 }
1102
1103 int current_width = u->GetDisplayImageWidth();
1104 width += current_width;
1105 x += current_width;
1106 }
1107 break;
1108 }
1109
1110 default: break;
1111 }
1112 break;
1113 }
1114
1115 case WID_VR_MATRIX:
1116 DrawVehicleRefitWindow(this->refit_list, this->selected_refit, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->resize.step_height, r);
1117 break;
1118
1119 case WID_VR_INFO:
1120 if (this->selected_refit != nullptr) {
1121 std::string string = this->GetCapacityString(*this->selected_refit);
1122 if (!string.empty()) {
1124 }
1125 }
1126 break;
1127 }
1128 }
1129
1135 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1136 {
1137 switch (data) {
1138 case VIWD_AUTOREPLACE: // Autoreplace replaced the vehicle; selected_vehicle became invalid.
1139 case VIWD_CONSIST_CHANGED: { // The consist has changed; rebuild the entire list.
1140 /* Clear the selection. */
1142 this->selected_vehicle = v->index;
1143 this->num_vehicles = UINT8_MAX;
1144 [[fallthrough]];
1145 }
1146
1147 case 2: { // The vehicle selection has changed; rebuild the entire list.
1148 if (!gui_scope) break;
1149 this->BuildRefitList();
1150
1151 /* The vehicle width has changed too. */
1152 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1153 uint max_width = 0;
1154
1155 /* Check the width of all cargo information strings. */
1156 for (const auto &list : this->refit_list) {
1157 for (const RefitOption &refit : list.second) {
1158 std::string string = this->GetCapacityString(refit);
1159 if (!string.empty()) {
1160 Dimension dim = GetStringBoundingBox(string);
1161 max_width = std::max(dim.width, max_width);
1162 }
1163 }
1164 }
1165
1166 if (this->information_width < max_width) {
1167 this->information_width = max_width;
1168 this->ReInit();
1169 }
1170 [[fallthrough]];
1171 }
1172
1173 case 1: // A new cargo has been selected.
1174 if (!gui_scope) break;
1175 this->RefreshScrollbar();
1176 break;
1177 }
1178 }
1179
1180 int GetClickPosition(int click_x)
1181 {
1183 if (_current_text_dir == TD_RTL) click_x = matrix_widget->current_x - click_x;
1184 click_x -= this->vehicle_margin;
1185 if (this->hscroll != nullptr) click_x += this->hscroll->GetPosition();
1186
1187 return click_x;
1188 }
1189
1190 void SetSelectedVehicles(int drag_x)
1191 {
1192 drag_x = GetClickPosition(drag_x);
1193
1194 int left_x = std::min(this->click_x, drag_x);
1195 int right_x = std::max(this->click_x, drag_x);
1196 this->num_vehicles = 0;
1197
1199 /* Find the vehicle part that was clicked. */
1200 switch (v->type) {
1201 case VehicleType::Train: {
1202 /* Don't select anything if we are not clicking in the vehicle. */
1203 if (left_x >= 0) {
1204 const Train *u = Train::From(v);
1205 bool start_counting = false;
1206 for (; u != nullptr; u = u->Next()) {
1207 int current_width = u->GetDisplayImageWidth();
1208 left_x -= current_width;
1209 right_x -= current_width;
1210
1211 if (left_x < 0 && !start_counting) {
1212 this->selected_vehicle = u->index;
1213 start_counting = true;
1214
1215 /* Count the first vehicle, even if articulated part */
1216 this->num_vehicles++;
1217 } else if (start_counting && !u->IsArticulatedPart()) {
1218 /* Do not count articulated parts */
1219 this->num_vehicles++;
1220 }
1221
1222 if (right_x < 0) break;
1223 }
1224 }
1225
1226 /* If the selection is not correct, clear it. */
1227 if (this->num_vehicles != 0) {
1228 if (_ctrl_pressed) this->num_vehicles = UINT8_MAX;
1229 break;
1230 }
1231 [[fallthrough]];
1232 }
1233
1234 default:
1235 /* Clear the selection. */
1236 this->selected_vehicle = v->index;
1237 this->num_vehicles = UINT8_MAX;
1238 break;
1239 }
1240 }
1241
1242 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1243 {
1244 switch (widget) {
1245 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1246 if (this->order != INVALID_VEH_ORDER_ID) break;
1248 this->click_x = GetClickPosition(pt.x - nwi->pos_x);
1249 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1251 if (!_ctrl_pressed) {
1253 } else {
1254 /* The vehicle selection has changed. */
1255 this->InvalidateData(2);
1256 }
1257 break;
1258 }
1259
1260 case WID_VR_MATRIX: { // listbox
1261 this->SetSelection(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_VR_MATRIX));
1262 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
1263 this->InvalidateData(1);
1264
1265 if (click_count == 1) break;
1266 [[fallthrough]];
1267 }
1268
1269 case WID_VR_REFIT: // refit button
1270 if (this->selected_refit != nullptr) {
1271 const Vehicle *v = Vehicle::Get(this->window_number);
1272
1273 if (this->order == INVALID_VEH_ORDER_ID) {
1274 bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
1275 if (Command<Commands::RefitVehicle>::Post(GetCmdRefitVehMsg(v), v->tile, this->selected_vehicle, this->selected_refit->cargo, this->selected_refit->subtype, false, false, this->num_vehicles) && delete_window) this->Close();
1276 } else {
1277 if (Command<Commands::OrderRefit>::Post(v->tile, v->index, this->order, this->selected_refit->cargo)) this->Close();
1278 }
1279 }
1280 break;
1281 }
1282 }
1283
1284 void OnMouseDrag(Point pt, WidgetID widget) override
1285 {
1286 switch (widget) {
1287 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1288 if (this->order != INVALID_VEH_ORDER_ID) break;
1290 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1292 break;
1293 }
1294 }
1295 }
1296
1297 void OnDragDrop(Point pt, WidgetID widget) override
1298 {
1299 switch (widget) {
1300 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1301 if (this->order != INVALID_VEH_ORDER_ID) break;
1303 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1304 this->InvalidateData(2);
1305 break;
1306 }
1307 }
1308 }
1309
1310 void OnResize() override
1311 {
1312 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1313 this->vscroll->SetCapacityFromWidget(this, WID_VR_MATRIX);
1314 if (this->hscroll != nullptr) this->hscroll->SetCapacityFromWidget(this, WID_VR_VEHICLE_PANEL_DISPLAY);
1315 }
1316};
1317
1318static constexpr std::initializer_list<NWidgetPart> _nested_vehicle_refit_widgets = {
1323 EndContainer(),
1324 /* Vehicle display + scrollbar. */
1329 EndContainer(),
1330 EndContainer(),
1332 /* Matrix + scrollbar. */
1336 EndContainer(),
1341 EndContainer(),
1342};
1343
1346 WindowPosition::Automatic, "view_vehicle_refit", 240, 174,
1349 _nested_vehicle_refit_widgets
1350);
1351
1359void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
1360{
1362 RefitWindow *w = new RefitWindow(_vehicle_refit_desc, v, order, auto_refit);
1363 w->parent = parent;
1364}
1365
1374uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
1375{
1376 /* List of cargo types of this engine */
1377 CargoTypes present = GetUnionOfArticulatedRefitMasks(engine, false);
1378
1379 /* Draw nothing if the engine is not refittable */
1380 if (HasAtMostOneBit(present.base())) return y;
1381
1382 std::string str;
1383 if (present == _cargo_mask) {
1384 /* Engine can be refitted to all types in this climate */
1385 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_TYPES, std::monostate{});
1386 } else {
1387 /* Check if we are able to refit to more cargo types and unable to. If
1388 * so, invert the cargo types to list those that we can't refit to. */
1389 CargoTypes excluded = CargoTypes{present}.Flip(_cargo_mask);
1390 uint num_excluded = excluded.Count();
1391 if (num_excluded < present.Count() && num_excluded <= 7) {
1392 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_BUT, excluded);
1393 } else {
1394 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_JUST_CARGO_LIST, present);
1395 }
1396 }
1397
1398 return DrawStringMultiLine(left, right, y, INT32_MAX, str);
1399}
1400
1407{
1408 if (!EngInfo(v->engine_type)->callback_mask.Test(VehicleCallbackMask::CargoSuffix)) return STR_EMPTY;
1409 std::array<int32_t, 1> regs100;
1410 uint16_t cb = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v, regs100);
1411 if (v->GetGRF()->grf_version < 8 && cb == 0xFF) return STR_EMPTY;
1412 if (cb == CALLBACK_FAILED || cb == 0x400) return STR_EMPTY;
1413 if (cb == 0x40F) {
1414 return GetGRFStringID(v->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1415 }
1416 if (cb > 0x400) {
1418 return STR_EMPTY;
1419 }
1420 return GetGRFStringID(v->GetGRFID(), GRFSTR_MISC_GRF_TEXT + cb);
1421}
1422
1424static bool VehicleGroupLengthSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1425{
1426 return a.NumVehicles() < b.NumVehicles();
1427}
1428
1430static bool VehicleGroupTotalProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1431{
1432 return a.GetDisplayProfitThisYear() < b.GetDisplayProfitThisYear();
1433}
1434
1436static bool VehicleGroupTotalProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1437{
1438 return a.GetDisplayProfitLastYear() < b.GetDisplayProfitLastYear();
1439}
1440
1442static bool VehicleGroupAverageProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1443{
1444 return a.GetDisplayProfitThisYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitThisYear() * static_cast<uint>(a.NumVehicles());
1445}
1446
1448static bool VehicleGroupAverageProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1449{
1450 return a.GetDisplayProfitLastYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitLastYear() * static_cast<uint>(a.NumVehicles());
1451}
1452
1454static bool VehicleNumberSorter(const Vehicle * const &a, const Vehicle * const &b)
1455{
1456 return a->unitnumber < b->unitnumber;
1457}
1458
1460static bool VehicleNameSorter(const Vehicle * const &a, const Vehicle * const &b)
1461{
1462 static std::string last_name[2] = { {}, {} };
1463
1464 if (a != _last_vehicle[0]) {
1465 _last_vehicle[0] = a;
1466 last_name[0] = GetString(STR_VEHICLE_NAME, a->index);
1467 }
1468
1469 if (b != _last_vehicle[1]) {
1470 _last_vehicle[1] = b;
1471 last_name[1] = GetString(STR_VEHICLE_NAME, b->index);
1472 }
1473
1474 int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
1475 return (r != 0) ? r < 0: VehicleNumberSorter(a, b);
1476}
1477
1479static bool VehicleAgeSorter(const Vehicle * const &a, const Vehicle * const &b)
1480{
1481 auto r = a->age - b->age;
1482 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1483}
1484
1486static bool VehicleProfitThisYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1487{
1489 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1490}
1491
1493static bool VehicleProfitLastYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1494{
1496 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1497}
1498
1500static bool VehicleCargoSorter(const Vehicle * const &a, const Vehicle * const &b)
1501{
1502 const Vehicle *v;
1503 CargoArray diff{};
1504
1505 /* Append the cargo of the connected waggons */
1506 for (v = a; v != nullptr; v = v->Next()) diff[v->cargo_type] += v->cargo_cap;
1507 for (v = b; v != nullptr; v = v->Next()) diff[v->cargo_type] -= v->cargo_cap;
1508
1509 int r = 0;
1510 for (uint d : diff) {
1511 r = d;
1512 if (r != 0) break;
1513 }
1514
1515 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1516}
1517
1519static bool VehicleReliabilitySorter(const Vehicle * const &a, const Vehicle * const &b)
1520{
1521 int r = a->reliability - b->reliability;
1522 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1523}
1524
1526static bool VehicleMaxSpeedSorter(const Vehicle * const &a, const Vehicle * const &b)
1527{
1529 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1530}
1531
1533static bool VehicleModelSorter(const Vehicle * const &a, const Vehicle * const &b)
1534{
1535 int r = a->engine_type.base() - b->engine_type.base();
1536 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1537}
1538
1540static bool VehicleValueSorter(const Vehicle * const &a, const Vehicle * const &b)
1541{
1542 const Vehicle *u;
1543 Money diff = 0;
1544
1545 for (u = a; u != nullptr; u = u->Next()) diff += u->value;
1546 for (u = b; u != nullptr; u = u->Next()) diff -= u->value;
1547
1548 int r = ClampTo<int32_t>(diff);
1549 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1550}
1551
1553static bool VehicleLengthSorter(const Vehicle * const &a, const Vehicle * const &b)
1554{
1556 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1557}
1558
1560static bool VehicleTimeToLiveSorter(const Vehicle * const &a, const Vehicle * const &b)
1561{
1562 int r = ClampTo<int32_t>((a->max_age - a->age) - (b->max_age - b->age));
1563 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1564}
1565
1567static bool VehicleTimetableDelaySorter(const Vehicle * const &a, const Vehicle * const &b)
1568{
1569 int r = a->lateness_counter - b->lateness_counter;
1570 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1571}
1572
1573void InitializeGUI()
1574{
1575 _grouping = {};
1576 _sorting = {};
1577}
1578
1585static inline void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
1586{
1587 Window *w = FindWindowById(window_class, from_index);
1588 if (w != nullptr) {
1589 /* Update window_number */
1590 w->window_number = to_index;
1591 if (w->viewport != nullptr) w->viewport->follow_vehicle = to_index;
1592
1593 /* Update vehicle drag data */
1594 if (_thd.window_class == window_class && _thd.window_number == from_index) {
1595 _thd.window_number = to_index;
1596 }
1597
1598 /* Notify the window. */
1600 }
1601}
1602
1609{
1610 ChangeVehicleWindow(WC_VEHICLE_VIEW, from_index, to_index);
1611 ChangeVehicleWindow(WC_VEHICLE_ORDERS, from_index, to_index);
1612 ChangeVehicleWindow(WC_VEHICLE_REFIT, from_index, to_index);
1613 ChangeVehicleWindow(WC_VEHICLE_DETAILS, from_index, to_index);
1614 ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index);
1615}
1616
1617static constexpr std::initializer_list<NWidgetPart> _nested_vehicle_list = {
1624 NWidget(WWT_PUSHTXTBTN, Colours::Grey, WID_VL_ORDER_VIEW), SetMinimalSize(61, 14), SetStringTip(STR_GOTO_ORDER_VIEW, STR_GOTO_ORDER_VIEW_TOOLTIP),
1625 EndContainer(),
1626 EndContainer(),
1630 EndContainer(),
1631
1634 NWidget(WWT_TEXTBTN, Colours::Grey, WID_VL_GROUP_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
1635 NWidget(WWT_PUSHTXTBTN, Colours::Grey, WID_VL_SORT_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1636 EndContainer(),
1639 NWidget(WWT_DROPDOWN, Colours::Grey, WID_VL_SORT_BY_PULLDOWN), SetMinimalSize(0, 12), SetFill(1, 1), SetToolTip(STR_TOOLTIP_SORT_CRITERIA),
1640 EndContainer(),
1645 NWidget(WWT_DROPDOWN, Colours::Grey, WID_VL_FILTER_BY_CARGO), SetMinimalSize(0, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1646 EndContainer(),
1648 EndContainer(),
1649 EndContainer(),
1650 EndContainer(),
1651
1655 EndContainer(),
1656
1661 SetToolTip(STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
1664 SetStringTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
1665 NWidget(WWT_PUSHIMGBTN, Colours::Grey, WID_VL_STOP_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1666 SetSpriteTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
1667 NWidget(WWT_PUSHIMGBTN, Colours::Grey, WID_VL_START_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1668 SetSpriteTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
1669 EndContainer(),
1670 /* Widget to be shown for other companies hiding the previous 5 widgets. */
1672 EndContainer(),
1674 EndContainer(),
1675};
1676
1677static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, uint order_arrow_width, VehicleOrderID start)
1678{
1679 auto orders = v->Orders();
1680 if (orders.empty()) return;
1681
1682 bool rtl = _current_text_dir == TD_RTL;
1683 int l_offset = rtl ? 0 : order_arrow_width;
1684 int r_offset = rtl ? order_arrow_width : 0;
1685 int i = 0;
1686 VehicleOrderID oid = start;
1687
1688 do {
1689 if (oid == v->cur_real_order_index) DrawString(left, right, y, rtl ? STR_JUST_LEFT_ARROW : STR_JUST_RIGHT_ARROW, TC_BLACK, SA_LEFT, false, FontSize::Small);
1690
1691 if (orders[oid].IsType(OT_GOTO_STATION)) {
1692 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, orders[oid].GetDestination()), TC_BLACK, SA_LEFT, false, FontSize::Small);
1693
1695 if (++i == 4) break;
1696 }
1697
1698 oid = v->orders->GetNext(oid);
1699 } while (oid != start);
1700}
1701
1710static void DrawSmallOrderList(const OrderList *orderlist, int left, int right, int y, uint order_arrow_width)
1711{
1712 if (orderlist == nullptr) return;
1713
1714 bool rtl = _current_text_dir == TD_RTL;
1715 int l_offset = rtl ? 0 : order_arrow_width;
1716 int r_offset = rtl ? order_arrow_width : 0;
1717 int i = 0;
1718
1719 for (const Order &order : orderlist->GetOrders()) {
1720 if (order.IsType(OT_GOTO_STATION)) {
1721 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order.GetDestination()), TC_BLACK, SA_LEFT, false, FontSize::Small);
1722
1724 if (++i == 4) break;
1725 }
1726 }
1727}
1728
1737void DrawVehicleImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip)
1738{
1739 switch (v->type) {
1740 case VehicleType::Train: DrawTrainImage(Train::From(v), r, selection, image_type, skip); break;
1741 case VehicleType::Road: DrawRoadVehImage(v, r, selection, image_type, skip); break;
1742 case VehicleType::Ship: DrawShipImage(v, r, selection, image_type); break;
1743 case VehicleType::Aircraft: DrawAircraftImage(v, r, selection, image_type); break;
1744 default: NOT_REACHED();
1745 }
1746}
1747
1754uint GetVehicleListHeight(VehicleType type, uint divisor)
1755{
1756 /* Name + vehicle + profit */
1757 uint base = ScaleGUITrad(GetVehicleHeight(type)) + 2 * GetCharacterHeight(FontSize::Small) + WidgetDimensions::scaled.matrix.Vertical();
1758 /* Drawing of the 4 small orders + profit*/
1759 if (type >= VehicleType::Ship) base = std::max(base, 6U * GetCharacterHeight(FontSize::Small) + WidgetDimensions::scaled.matrix.Vertical());
1760
1761 if (divisor == 1) return base;
1762
1763 /* Make sure the height is dividable by divisor */
1764 uint rem = base % divisor;
1765 return base + (rem == 0 ? 0 : divisor - rem);
1766}
1767
1773static int GetUnitNumberWidth(int digits)
1774{
1775 return GetStringBoundingBox(GetString(STR_JUST_COMMA, GetParamMaxDigits(digits))).width;
1776}
1777
1784void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
1785{
1786 Rect ir = r.WithHeight(line_height).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
1787 bool rtl = _current_text_dir == TD_RTL;
1788
1789 Dimension profit = GetSpriteSize(SPR_PROFIT_LOT);
1790 int text_offset = std::max<int>(profit.width, GetUnitNumberWidth(this->unitnumber_digits)) + WidgetDimensions::scaled.hsep_normal;
1791 Rect tr = ir.Indent(text_offset, rtl);
1792
1793 bool show_orderlist = this->vli.vtype >= VehicleType::Ship;
1794 Rect olr = ir.Indent(std::max(ScaleGUITrad(100) + text_offset, ir.Width() / 2), rtl);
1795
1796 int image_left = (rtl && show_orderlist) ? olr.right : tr.left;
1797 int image_right = (!rtl && show_orderlist) ? olr.left : tr.right;
1798
1799 int vehicle_button_x = rtl ? ir.right - profit.width : ir.left;
1800
1801 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->vehgroups);
1802 for (auto it = first; it != last; ++it) {
1803 const GUIVehicleGroup &vehgroup = *it;
1804
1805 DrawString(tr.left, tr.right, ir.bottom - GetCharacterHeight(FontSize::Small) - WidgetDimensions::scaled.framerect.bottom,
1806 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_LIST_PROFIT_THIS_PERIOD_LAST_PERIOD : STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR,
1807 vehgroup.GetDisplayProfitThisYear(),
1808 vehgroup.GetDisplayProfitLastYear()));
1809
1810 DrawVehicleProfitButton(vehgroup.GetOldestVehicleAge(), vehgroup.GetDisplayProfitLastYear(), vehgroup.NumVehicles(), vehicle_button_x, ir.top + GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal);
1811
1812 switch (this->grouping) {
1813 case GB_NONE: {
1814 const Vehicle *v = vehgroup.GetSingleVehicle();
1815
1817 DrawSprite(SPR_WARNING_SIGN, PAL_NONE, vehicle_button_x, ir.top + GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal + profit.height);
1818 }
1819
1820 DrawVehicleImage(v, ir.WithX(image_left, image_right), selected_vehicle, EIT_IN_LIST, 0);
1821
1822 if (_settings_client.gui.show_cargo_in_vehicle_lists) {
1823 /* Get the cargoes the vehicle can carry */
1824 CargoTypes vehicle_cargoes{};
1825
1826 for (auto u = v; u != nullptr; u = u->Next()) {
1827 if (u->cargo_cap == 0) continue;
1828
1829 vehicle_cargoes.Set(u->cargo_type);
1830 }
1831
1832 if (!v->name.empty()) {
1833 /* The vehicle got a name so we will print it and the cargoes */
1834 DrawString(tr.left, tr.right, ir.top,
1835 GetString(STR_VEHICLE_LIST_NAME_AND_CARGO, STR_VEHICLE_NAME, v->index, STR_VEHICLE_LIST_CARGO, vehicle_cargoes),
1836 TC_BLACK, SA_LEFT, false, FontSize::Small);
1837 } else if (v->group_id != DEFAULT_GROUP) {
1838 /* The vehicle has no name, but is member of a group, so print group name and the cargoes */
1839 DrawString(tr.left, tr.right, ir.top,
1840 GetString(STR_VEHICLE_LIST_NAME_AND_CARGO, STR_GROUP_NAME, v->group_id, STR_VEHICLE_LIST_CARGO, vehicle_cargoes),
1841 TC_BLACK, SA_LEFT, false, FontSize::Small);
1842 } else {
1843 /* The vehicle has no name, and is not a member of a group, so just print the cargoes */
1844 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_LIST_CARGO, vehicle_cargoes), TC_BLACK, SA_LEFT, false, FontSize::Small);
1845 }
1846 } else if (!v->name.empty()) {
1847 /* The vehicle got a name so we will print it */
1848 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_NAME, v->index), TC_BLACK, SA_LEFT, false, FontSize::Small);
1849 } else if (v->group_id != DEFAULT_GROUP) {
1850 /* The vehicle has no name, but is member of a group, so print group name */
1851 DrawString(tr.left, tr.right, ir.top, GetString(STR_GROUP_NAME, v->group_id), TC_BLACK, SA_LEFT, false, FontSize::Small);
1852 }
1853
1854 if (show_orderlist) DrawSmallOrderList(v, olr.left, olr.right, ir.top + GetCharacterHeight(FontSize::Small), this->order_arrow_width, v->cur_real_order_index);
1855
1856 TextColour tc;
1857 if (v->IsChainInDepot()) {
1858 tc = TC_BLUE;
1859 } else {
1860 tc = (v->age > v->max_age - CalendarTime::DAYS_IN_LEAP_YEAR) ? TC_RED : TC_BLACK;
1861 }
1862
1863 DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, v->unitnumber), tc);
1864 break;
1865 }
1866
1867 case GB_SHARED_ORDERS:
1868 assert(vehgroup.NumVehicles() > 0);
1869
1870 for (int i = 0; i < static_cast<int>(vehgroup.NumVehicles()); ++i) {
1871 if (image_left + WidgetDimensions::scaled.hsep_wide * i >= image_right) break; // Break if there is no more space to draw any more vehicles anyway.
1872 DrawVehicleImage(vehgroup.vehicles_begin[i], ir.WithX(image_left + WidgetDimensions::scaled.hsep_wide * i, image_right), selected_vehicle, EIT_IN_LIST, 0);
1873 }
1874
1875 if (show_orderlist) DrawSmallOrderList(vehgroup.vehicles_begin[0]->orders, olr.left, olr.right, ir.top + GetCharacterHeight(FontSize::Small), this->order_arrow_width);
1876
1877 DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, vehgroup.NumVehicles()), TC_BLACK);
1878 break;
1879
1880 default:
1881 NOT_REACHED();
1882 }
1883
1884 ir = ir.Translate(0, line_height);
1885 }
1886}
1887
1888void BaseVehicleListWindow::UpdateSortingFromGrouping()
1889{
1890 /* Set up sorting. Make the window-specific _sorting variable
1891 * point to the correct global _sorting struct so we are freed
1892 * from having conditionals during window operation */
1893 switch (this->vli.vtype) {
1894 case VehicleType::Train: this->sorting = &_sorting[this->grouping].train; break;
1895 case VehicleType::Road: this->sorting = &_sorting[this->grouping].roadveh; break;
1896 case VehicleType::Ship: this->sorting = &_sorting[this->grouping].ship; break;
1897 case VehicleType::Aircraft: this->sorting = &_sorting[this->grouping].aircraft; break;
1898 default: NOT_REACHED();
1899 }
1900 this->vehgroups.SetSortFuncs(this->GetVehicleSorterFuncs());
1901 this->vehgroups.SetListing(*this->sorting);
1902 this->vehgroups.ForceRebuild();
1903 this->vehgroups.NeedResort();
1904}
1905
1906void BaseVehicleListWindow::UpdateVehicleGroupBy(GroupBy group_by)
1907{
1908 if (this->grouping != group_by) {
1909 /* Save the old sorting option, so that if we change the grouping option back later on,
1910 * UpdateSortingFromGrouping() will automatically restore the saved sorting option. */
1911 *this->sorting = this->vehgroups.GetListing();
1912
1913 this->grouping = group_by;
1914 _grouping[this->vli.type][this->vli.vtype] = group_by;
1915 this->UpdateSortingFromGrouping();
1916 }
1917}
1918
1923struct VehicleListWindow : public BaseVehicleListWindow {
1924private:
1930
1936
1937public:
1939 {
1940 this->CreateNestedTree();
1941
1942 this->GetWidget<NWidgetStacked>(WID_VL_FILTER_BY_CARGO_SEL)->SetDisplayedPlane((this->vli.type == VL_SHARED_ORDERS) ? SZSP_NONE : 0);
1943
1944 this->vscroll = this->GetScrollbar(WID_VL_SCROLLBAR);
1945
1946 /* Set up the window widgets */
1947 this->GetWidget<NWidgetCore>(WID_VL_LIST)->SetToolTip(STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + to_underlying(this->vli.vtype));
1948
1950 if (this->vli.type == VL_SHARED_ORDERS) {
1951 this->GetWidget<NWidgetCore>(WID_VL_CAPTION_SHARED_ORDERS)->SetString(STR_VEHICLE_LIST_SHARED_ORDERS_LIST_CAPTION);
1952 /* If we are in the shared orders window, then disable the group-by dropdown menu.
1953 * Remove this when the group-by dropdown menu has another option apart from grouping by shared orders. */
1957 } else {
1958 this->GetWidget<NWidgetCore>(WID_VL_CAPTION)->SetString(STR_VEHICLE_LIST_TRAIN_CAPTION + to_underlying(this->vli.vtype));
1960 }
1961
1963 if (this->vli.company != OWNER_NONE) this->owner = this->vli.company;
1964
1965 this->BuildVehicleList();
1966 this->SortVehicleList();
1967 }
1968
1971 {
1972 *this->sorting = this->vehgroups.GetListing();
1973 }
1974
1975 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1976 {
1977 switch (widget) {
1978 case WID_VL_LIST:
1979 fill.height = resize.height = GetVehicleListHeight(this->vli.vtype, 1);
1980
1981 switch (this->vli.vtype) {
1982 case VehicleType::Train:
1983 case VehicleType::Road:
1984 size.height = 6 * resize.height;
1985 break;
1986 case VehicleType::Ship:
1988 size.height = 4 * resize.height;
1989 break;
1990 default: NOT_REACHED();
1991 }
1992 break;
1993
1994 case WID_VL_SORT_ORDER: {
1996 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1997 d.height += padding.height;
1998 size = maxdim(size, d);
1999 break;
2000 }
2001
2003 size.width = GetStringListWidth(this->vehicle_group_by_names) + padding.width;
2004 break;
2005
2007 size.width = GetStringListWidth(this->vehicle_group_none_sorter_names_calendar);
2008 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_none_sorter_names_wallclock));
2009 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_calendar));
2010 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_wallclock));
2011 size.width += padding.width;
2012 break;
2013
2015 size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList(true)).width + padding.width);
2016 break;
2017
2019 Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false, true);
2020 d.height += padding.height;
2021 d.width += padding.width;
2022 size = maxdim(size, d);
2023 break;
2024 }
2025 }
2026 }
2027
2028 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2029 {
2030 switch (widget) {
2032 return GetString(STR_VEHICLE_LIST_AVAILABLE_TRAINS + to_underlying(this->vli.vtype));
2033
2035 return GetString(std::data(this->vehicle_group_by_names)[this->grouping]);
2036
2038 return GetString(this->GetVehicleSorterNames()[this->vehgroups.SortType()]);
2039
2041 return GetString(this->GetCargoFilterLabel(this->cargo_filter_criteria));
2042
2043 case WID_VL_CAPTION:
2045 switch (this->vli.type) {
2046 case VL_SHARED_ORDERS: // Shared Orders
2047 return GetString(stringid, this->vehicles.size());
2048
2049 case VL_STANDARD: // Company Name
2050 return GetString(stringid, STR_COMPANY_NAME, this->vli.ToCompanyID(), std::monostate{}, this->vehicles.size());
2051
2052 case VL_STATION_LIST: // Station/Waypoint Name
2053 return GetString(stringid, Station::IsExpected(BaseStation::Get(this->vli.ToStationID())) ? STR_STATION_NAME : STR_WAYPOINT_NAME, this->vli.ToStationID(), std::monostate{}, this->vehicles.size());
2054
2055 case VL_DEPOT_LIST:
2056 return GetString(stringid, STR_DEPOT_CAPTION, this->vli.vtype, this->vli.ToDestinationID(), this->vehicles.size());
2057
2058 default: NOT_REACHED();
2059 }
2060 }
2061
2062 default:
2063 return this->Window::GetWidgetString(widget, stringid);
2064 }
2065 }
2066
2067 void DrawWidget(const Rect &r, WidgetID widget) const override
2068 {
2069 switch (widget) {
2070 case WID_VL_SORT_ORDER:
2071 /* draw arrow pointing up/down for ascending/descending sorting */
2072 this->DrawSortButtonState(widget, this->vehgroups.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
2073 break;
2074
2075 case WID_VL_LIST:
2076 this->DrawVehicleListItems(VehicleID::Invalid(), this->resize.step_height, r);
2077 break;
2078 }
2079 }
2080
2081 void OnPaint() override
2082 {
2083 this->BuildVehicleList();
2084 this->SortVehicleList();
2085
2086 if (this->vehicles.empty() && this->IsWidgetLowered(WID_VL_MANAGE_VEHICLES_DROPDOWN)) {
2088 }
2089
2090 /* Hide the widgets that we will not use in this window
2091 * Some windows contains actions only fit for the owner */
2092 int plane_to_show = (this->owner == _local_company) ? BP_SHOW_BUTTONS : BP_HIDE_BUTTONS;
2094 if (plane_to_show != nwi->shown_plane) {
2095 nwi->SetDisplayedPlane(plane_to_show);
2096 nwi->SetDirty(this);
2097 }
2098 if (this->owner == _local_company) {
2100 this->SetWidgetsDisabledState(this->vehicles.empty(),
2104 }
2105
2106 this->DrawWidgets();
2107 }
2108
2109 bool last_overlay_state = false;
2110 void OnMouseLoop() override
2111 {
2112 if (last_overlay_state != ShowCargoIconOverlay()) {
2113 last_overlay_state = ShowCargoIconOverlay();
2114 this->SetDirty();
2115 }
2116 }
2117
2118 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2119 {
2120 switch (widget) {
2121 case WID_VL_ORDER_VIEW: // Open the shared orders window
2122 assert(this->vli.type == VL_SHARED_ORDERS);
2123 assert(!this->vehicles.empty());
2124 ShowOrdersWindow(this->vehicles[0]);
2125 break;
2126
2127 case WID_VL_SORT_ORDER: // Flip sorting method ascending/descending
2128 this->vehgroups.ToggleSortOrder();
2129 this->SetDirty();
2130 break;
2131
2132 case WID_VL_GROUP_BY_PULLDOWN: // Select sorting criteria dropdown menu
2133 ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_VL_GROUP_BY_PULLDOWN, 0, 0);
2134 return;
2135
2136 case WID_VL_SORT_BY_PULLDOWN: // Select sorting criteria dropdown menu
2137 ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0,
2138 (this->vli.vtype == VehicleType::Train || this->vli.vtype == VehicleType::Road) ? 0 : (1 << 10));
2139 return;
2140
2141 case WID_VL_FILTER_BY_CARGO: { // Cargo filter dropdown
2142 static std::string cargo_filter;
2143 ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget, 0, DropDownOption::Filterable, &cargo_filter);
2144 break;
2145 }
2146
2147 case WID_VL_LIST: { // Matrix to show vehicles
2148 auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_VL_LIST);
2149 if (it == this->vehgroups.end()) return; // click out of list bound
2150
2151 const GUIVehicleGroup &vehgroup = *it;
2152 switch (this->grouping) {
2153 case GB_NONE: {
2154 const Vehicle *v = vehgroup.GetSingleVehicle();
2155 if (!VehicleClicked(v)) {
2156 if (_ctrl_pressed) {
2158 } else {
2160 }
2161 }
2162 break;
2163 }
2164
2165 case GB_SHARED_ORDERS: {
2166 assert(vehgroup.NumVehicles() > 0);
2167 if (!VehicleClicked(vehgroup)) {
2168 const Vehicle *v = vehgroup.vehicles_begin[0];
2169 if (_ctrl_pressed) {
2170 ShowOrdersWindow(v);
2171 } else {
2172 if (vehgroup.NumVehicles() == 1) {
2174 } else {
2175 ShowVehicleListWindow(v);
2176 }
2177 }
2178 }
2179 break;
2180 }
2181
2182 default: NOT_REACHED();
2183 }
2184
2185 break;
2186 }
2187
2189 ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
2190 break;
2191
2194 break;
2195 }
2196
2197 case WID_VL_STOP_ALL:
2198 case WID_VL_START_ALL:
2199 Command<Commands::MassStartStop>::Post(TileIndex{}, widget == WID_VL_START_ALL, true, this->vli);
2200 break;
2201 }
2202 }
2203
2204 void OnDropdownSelect(WidgetID widget, int index, int) override
2205 {
2206 switch (widget) {
2208 this->UpdateVehicleGroupBy(static_cast<GroupBy>(index));
2209 break;
2210
2212 this->vehgroups.SetSortType(index);
2213 break;
2214
2216 this->SetCargoFilter(static_cast<CargoType>(index));
2217 break;
2218
2220 assert(!this->vehicles.empty());
2221
2222 switch (index) {
2223 case ADI_REPLACE: // Replace window
2225 break;
2226 case ADI_SERVICE: // Send for servicing
2227 case ADI_DEPOT: // Send to Depots
2228 Command<Commands::SendVehicleToDepot>::Post(GetCmdSendToDepotMsg(this->vli.vtype), VehicleID::Invalid(), (index == ADI_SERVICE ? DepotCommandFlag::Service : DepotCommandFlags{}) | DepotCommandFlag::MassSend, this->vli);
2229 break;
2230
2231 case ADI_CREATE_GROUP: // Create group
2232 Command<Commands::AddVehicleToGroup>::Post(CcAddVehicleNewGroup, NEW_GROUP, VehicleID::Invalid(), false, this->vli);
2233 break;
2234
2235 default: NOT_REACHED();
2236 }
2237 break;
2238
2239 default: NOT_REACHED();
2240 }
2241 this->SetDirty();
2242 }
2243
2244 void OnGameTick() override
2245 {
2246 if (this->vehgroups.NeedResort()) {
2247 StationID station = (this->vli.type == VL_STATION_LIST) ? this->vli.ToStationID() : StationID::Invalid();
2248
2249 Debug(misc, 3, "Periodic resort {} list company {} at station {}", this->vli.vtype, this->owner, station);
2250 this->SetDirty();
2251 }
2252 }
2253
2254 void OnResize() override
2255 {
2256 this->vscroll->SetCapacityFromWidget(this, WID_VL_LIST);
2257 }
2258
2264 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2265 {
2266 if (!gui_scope && HasBit(data, 31) && this->vli.type == VL_SHARED_ORDERS) {
2267 /* Needs to be done in command-scope, so everything stays valid */
2268 this->vli.SetIndex(GB(data, 0, 20));
2269 this->window_number = this->vli.ToWindowNumber();
2270 this->vehgroups.ForceRebuild();
2271 return;
2272 }
2273
2274 if (data == 0) {
2275 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
2276 this->vehgroups.ForceRebuild();
2277 } else {
2278 this->vehgroups.ForceResort();
2279 }
2280 }
2281};
2282
2285 WindowDesc{
2286 WindowPosition::Automatic, "list_vehicles_train", 325, 246,
2288 {},
2289 _nested_vehicle_list
2290 },
2291 WindowDesc{
2292 WindowPosition::Automatic, "list_vehicles_roadveh", 260, 246,
2294 {},
2295 _nested_vehicle_list
2296 },
2297 WindowDesc{
2298 WindowPosition::Automatic, "list_vehicles_ship", 260, 246,
2300 {},
2301 _nested_vehicle_list
2302 },
2303 WindowDesc{
2304 WindowPosition::Automatic, "list_vehicles_aircraft", 260, 246,
2306 {},
2307 _nested_vehicle_list
2308 }
2309}};
2310
2311static void ShowVehicleListWindowLocal(CompanyID company, VehicleListType vlt, VehicleType vehicle_type, uint32_t unique_number)
2312{
2313 if (!Company::IsValidID(company) && company != OWNER_NONE) return;
2314
2315 assert(IsCompanyBuildableVehicleType(vehicle_type));
2316 VehicleListIdentifier vli(vlt, vehicle_type, company, unique_number);
2318}
2319
2320void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type)
2321{
2322 /* If _settings_client.gui.advanced_vehicle_list > 1, display the Advanced list
2323 * if _settings_client.gui.advanced_vehicle_list == 1, display Advanced list only for local company
2324 * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
2325 */
2326
2327 if ((_settings_client.gui.advanced_vehicle_list > (uint)(company != _local_company)) != _ctrl_pressed) {
2328 ShowCompanyGroup(company, vehicle_type);
2329 } else {
2330 ShowVehicleListWindowLocal(company, VL_STANDARD, vehicle_type, company.base());
2331 }
2332}
2333
2334void ShowVehicleListWindow(const Vehicle *v)
2335{
2336 ShowVehicleListWindowLocal(v->owner, VL_SHARED_ORDERS, v->type, v->FirstShared()->index.base());
2337}
2338
2339void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station)
2340{
2341 ShowVehicleListWindowLocal(company, VL_STATION_LIST, vehicle_type, station.base());
2342}
2343
2344void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
2345{
2346 ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, GetDepotDestinationIndex(depot_tile).base());
2347}
2348
2349
2350/* Unified vehicle GUI - Vehicle Details Window */
2351
2356
2379
2381static constexpr std::initializer_list<NWidgetPart> _nested_train_vehicle_details_widgets = {
2384 NWidget(WWT_CAPTION, Colours::Grey, WID_VD_CAPTION), SetStringTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2388 EndContainer(),
2393 EndContainer(),
2400 SetStringTip(STR_EMPTY, STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP),
2402 EndContainer(),
2405 SetStringTip(STR_VEHICLE_DETAIL_TAB_CARGO, STR_VEHICLE_DETAILS_TRAIN_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2407 SetStringTip(STR_VEHICLE_DETAIL_TAB_INFORMATION, STR_VEHICLE_DETAILS_TRAIN_INFORMATION_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2409 SetStringTip(STR_VEHICLE_DETAIL_TAB_CAPACITIES, STR_VEHICLE_DETAILS_TRAIN_CAPACITIES_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2411 SetStringTip(STR_VEHICLE_DETAIL_TAB_TOTAL_CARGO, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2413 EndContainer(),
2414};
2415
2416
2418extern void DrawTrainDetails(const Train *v, const Rect &r, int vscroll_pos, uint16_t vscroll_cap, TrainDetailsWindowTabs det_tab);
2419extern void DrawRoadVehDetails(const Vehicle *v, const Rect &r);
2420extern void DrawShipDetails(const Vehicle *v, const Rect &r);
2421extern void DrawAircraftDetails(const Aircraft *v, const Rect &r);
2422
2423static const StringID _service_interval_dropdown_calendar[] = {
2424 STR_VEHICLE_DETAILS_DEFAULT,
2425 STR_VEHICLE_DETAILS_DAYS,
2426 STR_VEHICLE_DETAILS_PERCENT,
2427};
2428
2429static const StringID _service_interval_dropdown_wallclock[] = {
2430 STR_VEHICLE_DETAILS_DEFAULT,
2431 STR_VEHICLE_DETAILS_MINUTES,
2432 STR_VEHICLE_DETAILS_PERCENT,
2433};
2434
2438 Scrollbar *vscroll = nullptr;
2439
2446 {
2448
2449 this->CreateNestedTree();
2450 this->vscroll = (v->type == VehicleType::Train ? this->GetScrollbar(WID_VD_SCROLLBAR) : nullptr);
2451 this->FinishInitNested(window_number);
2452
2453 this->owner = v->owner;
2454 }
2455
2461 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2462 {
2463 if (data == VIWD_AUTOREPLACE) {
2464 /* Autoreplace replaced the vehicle.
2465 * Nothing to do for this window. */
2466 return;
2467 }
2468 if (!gui_scope) return;
2469 const Vehicle *v = Vehicle::Get(this->window_number);
2470 if (v->type == VehicleType::Road) {
2472 uint aimed_height = this->GetRoadVehDetailsHeight(v);
2473 /* If the number of articulated parts changes, the size of the window must change too. */
2474 if (aimed_height != nwid_info->current_y) {
2475 this->ReInit();
2476 }
2477 }
2478 }
2479
2486 {
2487 uint desired_height;
2488 if (v->HasArticulatedPart()) {
2489 /* An articulated RV has its text drawn under the sprite instead of after it, hence 15 pixels extra. */
2490 desired_height = ScaleGUITrad(15) + 3 * GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal * 2;
2491 /* Add space for the cargo amount for each part. */
2492 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
2493 if (u->cargo_cap != 0) desired_height += GetCharacterHeight(FontSize::Normal);
2494 }
2495 } else {
2496 desired_height = 4 * GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal * 2;
2497 }
2498 return desired_height;
2499 }
2500
2501 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2502 {
2503 switch (widget) {
2504 case WID_VD_TOP_DETAILS: {
2505 Dimension dim = { 0, 0 };
2506 size.height = 4 * GetCharacterHeight(FontSize::Normal) + padding.height;
2507
2508 uint64_t max_value = GetParamMaxValue(INT16_MAX);
2509 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_MAX_SPEED, max_value)));
2510 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, max_value, max_value, max_value)));
2511 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE, max_value, max_value, max_value, max_value)));
2512 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE, max_value, max_value, max_value)));
2513 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD_MIN_PERFORMANCE, max_value, max_value, max_value)));
2514 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS, max_value, max_value, max_value)));
2515 dim = maxdim(dim, GetStringBoundingBox(GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_AGE_RUNNING_COST_PERIOD : STR_VEHICLE_INFO_AGE_RUNNING_COST_YR, STR_VEHICLE_INFO_AGE, max_value, max_value, max_value)));
2516 size.width = dim.width + padding.width;
2517 break;
2518 }
2519
2520 case WID_VD_MIDDLE_DETAILS: {
2521 const Vehicle *v = Vehicle::Get(this->window_number);
2522 switch (v->type) {
2523 case VehicleType::Road:
2524 size.height = this->GetRoadVehDetailsHeight(v) + padding.height;
2525 break;
2526
2527 case VehicleType::Ship:
2528 size.height = 4 * GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2529 break;
2530
2532 size.height = 5 * GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2533 break;
2534
2535 default:
2536 NOT_REACHED(); // Train uses WID_VD_MATRIX instead.
2537 }
2538 break;
2539 }
2540
2541 case WID_VD_MATRIX:
2542 fill.height = resize.height = std::max<uint>(ScaleGUITrad(14), GetCharacterHeight(FontSize::Normal) + padding.height);
2543 size.height = 4 * resize.height;
2544 break;
2545
2547 Dimension d = maxdim(GetStringListBoundingBox(_service_interval_dropdown_calendar), GetStringListBoundingBox(_service_interval_dropdown_wallclock));
2548 d.width += padding.width;
2549 d.height += padding.height;
2550 size = maxdim(size, d);
2551 break;
2552 }
2553
2555 /* Do we show the last serviced value as a date or minutes since service? */
2557 ? MakeParameters(GetParamMaxValue(MAX_SERVINT_DAYS), STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO, EconomyTime::MAX_DATE)
2558 : MakeParameters(GetParamMaxValue(MAX_SERVINT_DAYS), STR_VEHICLE_DETAILS_LAST_SERVICE_DATE, TimerGameEconomy::DateAtStartOfYear(EconomyTime::MAX_YEAR));
2559
2560 size.width = std::max(size.width, GetStringBoundingBox(GetStringWithArgs(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT, params)).width);
2561 PrepareArgsForNextRun(params);
2562 size.width = std::max(size.width, GetStringBoundingBox(GetStringWithArgs(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS, params)).width);
2563
2564 size.width += padding.width;
2565 size.height = GetCharacterHeight(FontSize::Normal) + padding.height;
2566 break;
2567 }
2568 }
2569
2576 static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type, CompanyID company_id)
2577 {
2578 if (_local_company != company_id) return false;
2579
2580 const VehicleDefaultSettings *vds = &Company::Get(company_id)->settings.vehicle;
2581 switch (vehicle_type) {
2582 default: NOT_REACHED();
2583 case VehicleType::Train: return vds->servint_trains != 0;
2584 case VehicleType::Road: return vds->servint_roadveh != 0;
2585 case VehicleType::Ship: return vds->servint_ships != 0;
2586 case VehicleType::Aircraft: return vds->servint_aircraft != 0;
2587 }
2588 }
2589
2599 static void DrawVehicleDetails(const Vehicle *v, const Rect &r, int vscroll_pos, uint vscroll_cap, TrainDetailsWindowTabs det_tab)
2600 {
2601 switch (v->type) {
2602 case VehicleType::Train: DrawTrainDetails(Train::From(v), r, vscroll_pos, vscroll_cap, det_tab); break;
2603 case VehicleType::Road: DrawRoadVehDetails(v, r); break;
2604 case VehicleType::Ship: DrawShipDetails(v, r); break;
2606 default: NOT_REACHED();
2607 }
2608 }
2609
2610 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2611 {
2612 if (widget == WID_VD_CAPTION) return GetString(STR_VEHICLE_DETAILS_CAPTION, Vehicle::Get(this->window_number)->index);
2613
2614 return this->Window::GetWidgetString(widget, stringid);
2615 }
2616
2617 void DrawWidget(const Rect &r, WidgetID widget) const override
2618 {
2619 const Vehicle *v = Vehicle::Get(this->window_number);
2620
2621 switch (widget) {
2622 case WID_VD_TOP_DETAILS: {
2623 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2624
2625 /* Draw running cost */
2626 DrawString(tr,
2627 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_AGE_RUNNING_COST_PERIOD : STR_VEHICLE_INFO_AGE_RUNNING_COST_YR,
2628 (v->age + CalendarTime::DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_INFO_AGE : STR_VEHICLE_INFO_AGE_RED,
2631 v->GetDisplayRunningCost()));
2633
2634 /* Draw max speed */
2635 uint64_t max_speed = PackVelocity(v->GetDisplayMaxSpeed(), v->type);
2636 if (v->type == VehicleType::Train ||
2637 (v->type == VehicleType::Road && _settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL)) {
2638 const GroundVehicleCache *gcache = v->GetGroundVehicleCache();
2639 if (v->type == VehicleType::Train && (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ||
2640 Train::From(v)->GetAccelerationType() == VehicleAccelerationModel::Maglev)) {
2641 DrawString(tr, GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, gcache->cached_weight, gcache->cached_power, max_speed));
2642 } else {
2643 DrawString(tr, GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE, gcache->cached_weight, gcache->cached_power, max_speed, gcache->cached_max_te));
2644 }
2645 } else if (v->type == VehicleType::Aircraft) {
2646 StringID type = v->GetEngine()->GetAircraftTypeText();
2647 if (Aircraft::From(v)->GetRange() > 0) {
2648 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE, max_speed, type, Aircraft::From(v)->GetRange()));
2649 } else {
2650 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED_TYPE, max_speed, type));
2651 }
2652 } else {
2653 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED, max_speed));
2654 }
2656
2657 /* Draw profit */
2658 if (v->IsGroundVehicle()) {
2659 DrawString(tr,
2660 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD_MIN_PERFORMANCE : STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE,
2664 } else {
2665 DrawString(tr,
2666 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD : STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR,
2669 }
2671
2672 /* Draw breakdown & reliability */
2673 DrawString(tr, GetString(STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS, ToPercent16(v->reliability), ToPercent16(v->GetEngine()->reliability), v->breakdowns_since_last_service));
2674 break;
2675 }
2676
2677 case WID_VD_MATRIX: {
2678 /* For trains only. */
2679 DrawVehicleDetails(v, r.Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero).WithHeight(this->resize.step_height), this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->tab);
2680 break;
2681 }
2682
2683 case WID_VD_MIDDLE_DETAILS: {
2684 /* For other vehicles, at the place of the matrix. */
2685 bool rtl = _current_text_dir == TD_RTL;
2686 uint sprite_width = GetSingleVehicleWidth(v, EIT_IN_DETAILS) + WidgetDimensions::scaled.framerect.Horizontal();
2687 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2688
2689 /* Articulated road vehicles use a complete line. */
2690 if (v->type == VehicleType::Road && v->HasArticulatedPart()) {
2691 DrawVehicleImage(v, tr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2692 } else {
2693 Rect sr = tr.WithWidth(sprite_width, rtl);
2694 DrawVehicleImage(v, sr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2695 }
2696
2697 DrawVehicleDetails(v, tr.Indent(sprite_width, rtl), 0, 0, this->tab);
2698 break;
2699 }
2700
2702 /* Draw service interval text */
2703 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2704
2705 /* We're using wallclock units. Show minutes since last serviced. */
2707 int minutes_since_serviced = (TimerGameEconomy::date - v->date_of_last_service).base() / EconomyTime::DAYS_IN_ECONOMY_MONTH;
2708 DrawString(tr.left, tr.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FontSize::Normal)),
2709 GetString(v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_MINUTES,
2710 v->GetServiceInterval(), STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO, minutes_since_serviced));
2711 break;
2712 }
2713
2714 /* We're using calendar dates. Show the date of last service. */
2715 DrawString(tr.left, tr.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FontSize::Normal)),
2716 GetString(v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS,
2717 v->GetServiceInterval(), STR_VEHICLE_DETAILS_LAST_SERVICE_DATE, v->date_of_last_service));
2718 break;
2719 }
2720 }
2721 }
2722
2724 void OnPaint() override
2725 {
2726 const Vehicle *v = Vehicle::Get(this->window_number);
2727
2728 if (v->type == VehicleType::Train) {
2729 this->LowerWidget(WID_VD_DETAILS_CARGO_CARRIED + this->tab);
2730 this->vscroll->SetCount(GetTrainDetailsWndVScroll(v->index, this->tab));
2731 }
2732
2733 /* Disable service-scroller when interval is set to disabled */
2737
2738 StringID str =
2739 !v->ServiceIntervalIsCustom() ? STR_VEHICLE_DETAILS_DEFAULT :
2740 v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT :
2741 TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_DETAILS_MINUTES : STR_VEHICLE_DETAILS_DAYS;
2744
2745 this->DrawWidgets();
2746 }
2747
2748 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2749 {
2750 switch (widget) {
2751 case WID_VD_INCREASE_SERVICING_INTERVAL: // increase int
2752 case WID_VD_DECREASE_SERVICING_INTERVAL: { // decrease int
2753 const Vehicle *v = Vehicle::Get(this->window_number);
2754 int mod;
2755 if (!v->ServiceIntervalIsPercent() && TimerGameEconomy::UsingWallclockUnits()) {
2756 mod = _ctrl_pressed ? 1 : 5;
2757 } else {
2758 mod = _ctrl_pressed ? 5 : 10;
2759 }
2760
2761 mod = (widget == WID_VD_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
2762 mod = GetServiceIntervalClamped(mod + v->GetServiceInterval(), v->ServiceIntervalIsPercent());
2763 if (mod == v->GetServiceInterval()) return;
2764
2765 Command<Commands::ChangeServiceInterval>::Post(STR_ERROR_CAN_T_CHANGE_SERVICING, v->index, mod, true, v->ServiceIntervalIsPercent());
2766 break;
2767 }
2768
2770 const Vehicle *v = Vehicle::Get(this->window_number);
2771 ShowDropDownMenu(this,
2772 TimerGameEconomy::UsingWallclockUnits() ? _service_interval_dropdown_wallclock : _service_interval_dropdown_calendar,
2773 v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0);
2774 break;
2775 }
2776
2781 this->SetWidgetsLoweredState(false,
2786
2788 this->SetDirty();
2789 break;
2790 }
2791 }
2792
2793 bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
2794 {
2796 const Vehicle *v = Vehicle::Get(this->window_number);
2797 StringID tool_tip;
2798 if (v->ServiceIntervalIsPercent()) {
2799 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_PERCENT : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_PERCENT;
2801 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_MINUTES : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_MINUTES;
2802 } else {
2803 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_DAYS : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_DAYS;
2804 }
2805 GuiShowTooltips(this, GetEncodedString(tool_tip), close_cond);
2806 return true;
2807 }
2808
2809 return false;
2810 }
2811
2812 void OnDropdownSelect(WidgetID widget, int index, int) override
2813 {
2814 switch (widget) {
2816 const Vehicle *v = Vehicle::Get(this->window_number);
2817 bool iscustom = index != 0;
2818 bool ispercent = iscustom ? (index == 2) : Company::Get(v->owner)->settings.vehicle.servint_ispercent;
2819 uint16_t interval = GetServiceIntervalClamped(v->GetServiceInterval(), ispercent);
2820 Command<Commands::ChangeServiceInterval>::Post(STR_ERROR_CAN_T_CHANGE_SERVICING, v->index, interval, iscustom, ispercent);
2821 break;
2822 }
2823 }
2824 }
2825
2826 void OnResize() override
2827 {
2829 if (nwi != nullptr) {
2830 this->vscroll->SetCapacityFromWidget(this, WID_VD_MATRIX);
2831 }
2832 }
2833};
2834
2837 WindowPosition::Automatic, "view_vehicle_details_train", 405, 178,
2839 {},
2841);
2842
2845 WindowPosition::Automatic, "view_vehicle_details", 405, 113,
2847 {},
2849);
2850
2861
2862
2863/* Unified vehicle GUI - Vehicle View Window */
2864
2866static constexpr std::initializer_list<NWidgetPart> _nested_vehicle_view_widgets = {
2869 NWidget(WWT_PUSHIMGBTN, Colours::Grey, WID_VV_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME),
2871 NWidget(WWT_PUSHIMGBTN, Colours::Grey, WID_VV_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION),
2876 EndContainer(),
2881 EndContainer(),
2882 EndContainer(),
2885 NWidget(WWT_IMGBTN, Colours::Grey, WID_VV_GOTO_DEPOT), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2886 NWidget(WWT_PUSHIMGBTN, Colours::Grey, WID_VV_CLONE), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2887 EndContainer(),
2888 /* For trains only, 'ignore signal' button. */
2891 SetSpriteTip(SPR_IGNORE_SIGNALS, STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP),
2892 EndContainer(),
2896 SetSpriteTip(SPR_FORCE_VEHICLE_TURN, STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP),
2897 EndContainer(),
2901 EndContainer(),
2902 EndContainer(),
2905 NWidget(WWT_PUSHIMGBTN, Colours::Grey, WID_VV_ORDER_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION, STR_VEHICLE_VIEW_ORDER_LOCATION_TOOLTIP),
2907 EndContainer(),
2908};
2909
2910/* Just to make sure, nobody has changed the vehicle type constants, as we are
2911 using them for array indexing in a number of places here. */
2912static_assert(to_underlying(VehicleType::Train) == 0);
2913static_assert(to_underlying(VehicleType::Road) == 1);
2914static_assert(to_underlying(VehicleType::Ship) == 2);
2915static_assert(to_underlying(VehicleType::Aircraft) == 3);
2916
2924
2927static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
2928static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
2931
2934 VCT_CMD_START_STOP = 0,
2935 VCT_CMD_CLONE_VEH,
2936 VCT_CMD_TURN_AROUND,
2937};
2938
2941 { // VCT_CMD_START_STOP
2942 STR_ERROR_CAN_T_STOP_START_TRAIN,
2943 STR_ERROR_CAN_T_STOP_START_ROAD_VEHICLE,
2944 STR_ERROR_CAN_T_STOP_START_SHIP,
2945 STR_ERROR_CAN_T_STOP_START_AIRCRAFT
2946 },
2947 { // VCT_CMD_CLONE_VEH
2948 STR_ERROR_CAN_T_BUY_TRAIN,
2949 STR_ERROR_CAN_T_BUY_ROAD_VEHICLE,
2950 STR_ERROR_CAN_T_BUY_SHIP,
2951 STR_ERROR_CAN_T_BUY_AIRCRAFT
2952 },
2953 { // VCT_CMD_TURN_AROUND
2954 STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN,
2955 STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN,
2956 INVALID_STRING_ID, // invalid for ships
2957 INVALID_STRING_ID // invalid for aircraft
2958 },
2959};
2960
2966void CcStartStopVehicle(Commands, const CommandCost &result, VehicleID veh_id, bool)
2967{
2968 if (result.Failed()) return;
2969
2970 const Vehicle *v = Vehicle::GetIfValid(veh_id);
2971 if (v == nullptr || !IsCompanyBuildableVehicleType(v) || !v->IsPrimaryVehicle() || v->owner != _local_company) return;
2972
2973 StringID msg = v->vehstatus.Test(VehState::Stopped) ? STR_VEHICLE_COMMAND_STOPPED : STR_VEHICLE_COMMAND_STARTED;
2974 const Vehicle *moving_front = v->GetMovingFront();
2975 Point pt = RemapCoords(moving_front->x_pos, moving_front->y_pos, moving_front->z_pos);
2976 AddTextEffect(GetEncodedString(msg), pt.x, pt.y, Ticks::DAY_TICKS, TE_RISING);
2977}
2978
2984void StartStopVehicle(const Vehicle *v, bool texteffect)
2985{
2986 assert(v->IsPrimaryVehicle());
2987 Command<Commands::StartStopVehicle>::Post(_vehicle_msg_translation_table[VCT_CMD_START_STOP][v->type], texteffect ? CcStartStopVehicle : nullptr, v->tile, v->index, false);
2988}
2989
2995static bool IsVehicleRefittable(const Vehicle *v)
2996{
2997 if (!v->IsStoppedInDepot()) return false;
2998
2999 do {
3000 if (IsEngineRefittable(v->engine_type)) return true;
3001 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
3002
3003 return false;
3004}
3005
3007struct VehicleViewWindow : Window {
3008private:
3020 bool mouse_over_start_stop = false;
3021
3027 {
3028 switch (plane) {
3029 case SEL_DC_GOTO_DEPOT:
3030 case SEL_DC_CLONE:
3031 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE)->SetDisplayedPlane(plane - SEL_DC_BASEPLANE);
3032 break;
3033
3034 case SEL_RT_REFIT:
3035 case SEL_RT_TURN_AROUND:
3036 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_REFIT_TURN)->SetDisplayedPlane(plane - SEL_RT_BASEPLANE);
3037 break;
3038
3039 default:
3040 NOT_REACHED();
3041 }
3042 }
3043
3044public:
3046 {
3048 this->CreateNestedTree();
3049
3050 /* Sprites for the 'send to depot' button indexed by vehicle type. */
3051 static constexpr VehicleTypeIndexArray<const SpriteID> vehicle_view_goto_depot_sprites = {
3052 SPR_SEND_TRAIN_TODEPOT,
3053 SPR_SEND_ROADVEH_TODEPOT,
3054 SPR_SEND_SHIP_TODEPOT,
3055 SPR_SEND_AIRCRAFT_TODEPOT,
3056 };
3058 this->GetWidget<NWidgetCore>(WID_VV_GOTO_DEPOT)->SetSprite(vehicle_view_goto_depot_sprites[v->type]);
3059
3060 /* Sprites for the 'clone vehicle' button indexed by vehicle type. */
3061 static constexpr VehicleTypeIndexArray<const SpriteID> vehicle_view_clone_sprites = {
3063 SPR_CLONE_ROADVEH,
3064 SPR_CLONE_SHIP,
3065 SPR_CLONE_AIRCRAFT,
3066 };
3067 this->GetWidget<NWidgetCore>(WID_VV_CLONE)->SetSprite(vehicle_view_clone_sprites[v->type]);
3068
3069 switch (v->type) {
3070 case VehicleType::Train:
3071 this->GetWidget<NWidgetCore>(WID_VV_TURN_AROUND)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_REVERSE_TOOLTIP);
3072 this->GetWidget<NWidgetStacked>(WID_VV_FORCE_PROCEED_SEL)->SetDisplayedPlane(0);
3073 break;
3074
3075 case VehicleType::Road:
3077 break;
3078
3079 case VehicleType::Ship:
3083 break;
3084
3085 default: NOT_REACHED();
3086 }
3088 this->owner = v->owner;
3089 this->GetWidget<NWidgetViewport>(WID_VV_VIEWPORT)->InitializeViewport(this, static_cast<VehicleID>(this->window_number), ScaleZoomGUI(_vehicle_view_zoom_levels[v->type]));
3090
3091 this->GetWidget<NWidgetCore>(WID_VV_START_STOP)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP + to_underlying(v->type));
3092 this->GetWidget<NWidgetCore>(WID_VV_RENAME)->SetToolTip(STR_VEHICLE_DETAILS_TRAIN_RENAME + to_underlying(v->type));
3093 this->GetWidget<NWidgetCore>(WID_VV_LOCATION)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP + to_underlying(v->type));
3094 this->GetWidget<NWidgetCore>(WID_VV_REFIT)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP + to_underlying(v->type));
3095 this->GetWidget<NWidgetCore>(WID_VV_GOTO_DEPOT)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP + to_underlying(v->type));
3096 this->GetWidget<NWidgetCore>(WID_VV_SHOW_ORDERS)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP + to_underlying(v->type));
3097 this->GetWidget<NWidgetCore>(WID_VV_SHOW_DETAILS)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP + to_underlying(v->type));
3098 this->GetWidget<NWidgetCore>(WID_VV_CLONE)->SetToolTip(STR_VEHICLE_VIEW_CLONE_TRAIN_INFO + to_underlying(v->type));
3099
3100 this->UpdatePlanes();
3101 this->UpdateButtons();
3102 }
3103
3104 void Close([[maybe_unused]] int data = 0) override
3105 {
3106 CloseWindowById(WC_VEHICLE_ORDERS, this->window_number, false);
3107 CloseWindowById(WC_VEHICLE_REFIT, this->window_number, false);
3108 CloseWindowById(WC_VEHICLE_DETAILS, this->window_number, false);
3109 CloseWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false);
3110 this->Window::Close();
3111 }
3112
3113 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
3114 {
3115 const Vehicle *v = Vehicle::Get(this->window_number);
3116 switch (widget) {
3117 case WID_VV_START_STOP:
3118 size.height = std::max<uint>({size.height, (uint)GetCharacterHeight(FontSize::Normal), GetScaledSpriteSize(SPR_WARNING_SIGN).height, GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED).height, GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING).height}) + padding.height;
3119 break;
3120
3122 if (v->type != VehicleType::Train) {
3123 size.height = 0;
3124 size.width = 0;
3125 }
3126 break;
3127
3128 case WID_VV_VIEWPORT:
3129 size.width = VV_INITIAL_VIEWPORT_WIDTH;
3131 break;
3132 }
3133 }
3134
3137 {
3138 const Vehicle *v = Vehicle::Get(this->window_number);
3139 bool is_localcompany = v->owner == _local_company;
3140 bool refittable_and_stopped_in_depot = IsVehicleRefittable(v);
3141
3142 this->SetWidgetDisabledState(WID_VV_RENAME, !is_localcompany);
3143 this->SetWidgetDisabledState(WID_VV_GOTO_DEPOT, !is_localcompany);
3144 this->SetWidgetDisabledState(WID_VV_REFIT, !refittable_and_stopped_in_depot || !is_localcompany);
3145 this->SetWidgetDisabledState(WID_VV_CLONE, !is_localcompany);
3146
3147 /* Lower the Send To Depot button when clicking it would cause the
3148 * vehicle to NOT go to the depot. */
3150
3151 if (v->type == VehicleType::Train) {
3153 this->SetWidgetDisabledState(WID_VV_FORCE_PROCEED, !is_localcompany);
3154 }
3155
3156 if (v->type == VehicleType::Train || v->type == VehicleType::Road) {
3157 this->SetWidgetDisabledState(WID_VV_TURN_AROUND, !is_localcompany);
3158 }
3159
3161 }
3162
3163 void OnPaint() override
3164 {
3165 const Vehicle *v = Vehicle::Get(this->window_number);
3166
3167 const Window *mainwindow = GetMainWindow();
3168 if (mainwindow->viewport->follow_vehicle == v->index) {
3170 }
3171
3172 this->DrawWidgets();
3173 }
3174
3175 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
3176 {
3177 if (widget != WID_VV_CAPTION) return this->Window::GetWidgetString(widget, stringid);
3178
3179 const Vehicle *v = Vehicle::Get(this->window_number);
3180 return GetString(STR_VEHICLE_VIEW_CAPTION, v->index);
3181 }
3182
3183 std::string GetVehicleStatusString(const Vehicle *v, TextColour &text_colour) const
3184 {
3185 text_colour = TC_BLACK;
3186
3187 if (v->vehstatus.Test(VehState::Crashed)) return GetString(STR_VEHICLE_STATUS_CRASHED);
3188
3189 if (v->type != VehicleType::Aircraft && v->breakdown_ctr == 1) return GetString(STR_VEHICLE_STATUS_BROKEN_DOWN);
3190
3191 if (v->vehstatus.Test(VehState::Stopped) && (!mouse_over_start_stop || v->IsStoppedInDepot())) {
3192 if (v->type != VehicleType::Train) return GetString(STR_VEHICLE_STATUS_STOPPED);
3193 if (v->cur_speed != 0) return GetString(STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL, PackVelocity(v->GetDisplaySpeed(), v->type));
3194 if (Train::From(v)->gcache.cached_power == 0) return GetString(STR_VEHICLE_STATUS_TRAIN_NO_POWER);
3195 return GetString(STR_VEHICLE_STATUS_STOPPED);
3196 }
3197
3198 if (v->IsInDepot() && v->IsWaitingForUnbunching()) return GetString(STR_VEHICLE_STATUS_WAITING_UNBUNCHING);
3199
3200 if (v->type == VehicleType::Train && Train::From(v)->flags.Test(VehicleRailFlag::Stuck) && !v->current_order.IsType(OT_LOADING)) return GetString(STR_VEHICLE_STATUS_TRAIN_STUCK);
3201
3202 if (v->type == VehicleType::Aircraft && Aircraft::From(v)->flags.Test(VehicleAirFlag::DestinationTooFar) && !v->current_order.IsType(OT_LOADING)) return GetString(STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR);
3203
3204 /* Vehicle is in a "normal" state, show current order. */
3205 if (mouse_over_start_stop) {
3207 text_colour = TC_RED | TC_FORCED;
3208 } else if (v->type == VehicleType::Train && Train::From(v)->flags.Test(VehicleRailFlag::Stuck) && !v->current_order.IsType(OT_LOADING)) {
3209 text_colour = TC_ORANGE | TC_FORCED;
3210 }
3211 }
3212
3213 switch (v->current_order.GetType()) {
3214 case OT_GOTO_STATION:
3215 return GetString(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_STATION_VEL : STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL,
3217
3218 case OT_GOTO_DEPOT: {
3219 /* This case *only* happens when multiple nearest depot orders
3220 * follow each other (including an order list only one order: a
3221 * nearest depot order) and there are no reachable depots.
3222 * It is primarily to guard for the case that there is no
3223 * depot with index 0, which would be used as fallback for
3224 * evaluating the string in the status bar. */
3225 if (v->current_order.GetDestination() == DepotID::Invalid()) return {};
3226
3229 return GetStringWithArgs(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL, params);
3230 }
3231
3233 return GetStringWithArgs(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_UNBUNCH_VEL, params);
3234 }
3235
3236 return GetStringWithArgs(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL, params);
3237 }
3238
3239 case OT_LOADING:
3240 return GetString(STR_VEHICLE_STATUS_LOADING_UNLOADING);
3241
3242 case OT_GOTO_WAYPOINT:
3243 assert(v->type == VehicleType::Train || v->type == VehicleType::Road || v->type == VehicleType::Ship);
3244 return GetString(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_WAYPOINT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL,
3246
3247 case OT_LEAVESTATION:
3248 if (v->type != VehicleType::Aircraft) {
3249 return GetString(STR_VEHICLE_STATUS_LEAVING);
3250 }
3251 [[fallthrough]];
3252
3253 default:
3254 if (v->GetNumManualOrders() == 0) {
3255 return GetString(STR_VEHICLE_STATUS_NO_ORDERS_VEL, PackVelocity(v->GetDisplaySpeed(), v->type));
3256 }
3257
3258 return {};
3259 }
3260 }
3261
3262 void DrawWidget(const Rect &r, WidgetID widget) const override
3263 {
3264 if (widget != WID_VV_START_STOP) return;
3265
3266 /* Draw the flag plus orders. */
3267 bool rtl = (_current_text_dir == TD_RTL);
3268 uint icon_width = std::max({GetScaledSpriteSize(SPR_WARNING_SIGN).width, GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED).width, GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING).width});
3269 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
3270
3271 const Vehicle *v = Vehicle::Get(this->window_number);
3272 SpriteID image = v->vehstatus.Test(VehState::Stopped) ? SPR_FLAG_VEH_STOPPED : (v->vehicle_flags.Test(VehicleFlag::PathfinderLost)) ? SPR_WARNING_SIGN : SPR_FLAG_VEH_RUNNING;
3273 DrawSpriteIgnorePadding(image, PAL_NONE, tr.WithWidth(icon_width, rtl), SA_CENTER);
3274
3275 tr = tr.Indent(icon_width + WidgetDimensions::scaled.imgbtn.Horizontal(), rtl);
3276
3277 TextColour text_colour = TC_FROMSTRING;
3278 std::string str = GetVehicleStatusString(v, text_colour);
3279 DrawString(tr.left, tr.right, CentreBounds(tr.top, tr.bottom, GetCharacterHeight(FontSize::Normal)), str, text_colour, SA_HOR_CENTER);
3280 }
3281
3282 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
3283 {
3284 const Vehicle *v = Vehicle::Get(this->window_number);
3285
3286 switch (widget) {
3287 case WID_VV_RENAME: { // rename
3288 ShowQueryString(GetString(STR_VEHICLE_NAME, v->index), STR_QUERY_RENAME_TRAIN_CAPTION + to_underlying(v->type),
3289 MAX_LENGTH_VEHICLE_NAME_CHARS, this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars});
3290 break;
3291 }
3292
3293 case WID_VV_START_STOP: // start stop
3294 StartStopVehicle(v, false);
3295 break;
3296
3297 case WID_VV_ORDER_LOCATION: {
3298 /* Scroll to current order destination */
3299 TileIndex tile = v->current_order.GetLocation(v);
3300 if (tile == INVALID_TILE) break;
3301
3302 if (_ctrl_pressed) {
3304 } else {
3306 }
3307 break;
3308 }
3309
3310 case WID_VV_LOCATION: // center main view
3311 if (_ctrl_pressed) {
3313 } else {
3314 const Window *mainwindow = GetMainWindow();
3315 if (click_count > 1) {
3316 /* main window 'follows' vehicle */
3317 mainwindow->viewport->follow_vehicle = v->index;
3318 } else {
3319 if (mainwindow->viewport->follow_vehicle == v->index) mainwindow->viewport->follow_vehicle = VehicleID::Invalid();
3320 const Vehicle *moving_front = v->GetMovingFront();
3321 ScrollMainWindowTo(moving_front->x_pos, moving_front->y_pos, moving_front->z_pos);
3322 }
3323 }
3324 break;
3325
3326 case WID_VV_GOTO_DEPOT: // goto hangar
3327 Command<Commands::SendVehicleToDepot>::Post(GetCmdSendToDepotMsg(v), v->index, _ctrl_pressed ? DepotCommandFlag::Service : DepotCommandFlags{}, {});
3328 break;
3329 case WID_VV_REFIT: // refit
3331 break;
3332 case WID_VV_SHOW_ORDERS: // show orders
3333 if (_ctrl_pressed) {
3335 } else {
3336 ShowOrdersWindow(v);
3337 }
3338 break;
3339 case WID_VV_SHOW_DETAILS: // show details
3340 if (_ctrl_pressed) {
3342 } else {
3344 }
3345 break;
3346 case WID_VV_CLONE: // clone vehicle
3347 /* Suppress the vehicle GUI when share-cloning.
3348 * There is no point to it except for starting the vehicle.
3349 * For starting the vehicle the player has to open the depot GUI, which is
3350 * most likely already open, but is also visible in the vehicle viewport. */
3351 Command<Commands::CloneVehicle>::Post(_vehicle_msg_translation_table[VCT_CMD_CLONE_VEH][v->type],
3352 _ctrl_pressed ? nullptr : CcCloneVehicle,
3353 v->tile, v->index, _ctrl_pressed);
3354 break;
3355 case WID_VV_TURN_AROUND: // turn around
3356 assert(v->IsGroundVehicle());
3357 if (v->type == VehicleType::Road) {
3358 Command<Commands::TurnRoadVehicle>::Post(_vehicle_msg_translation_table[VCT_CMD_TURN_AROUND][v->type], v->tile, v->index);
3359 } else {
3360 Command<Commands::ReverseTrainDirection>::Post(_vehicle_msg_translation_table[VCT_CMD_TURN_AROUND][v->type], v->tile, v->index, false);
3361 }
3362 break;
3363 case WID_VV_FORCE_PROCEED: // force proceed
3364 assert(v->type == VehicleType::Train);
3365 Command<Commands::ForceTrainProceed>::Post(STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL, v->tile, v->index);
3366 break;
3367 }
3368 }
3369
3370 EventState OnHotkey(int hotkey) override
3371 {
3372 /* If the hotkey is not for any widget in the UI (i.e. for honking) */
3373 if (hotkey == WID_VV_HONK_HORN) {
3374 const Window *mainwindow = GetMainWindow();
3376 /* Only play the sound if we're following this vehicle */
3377 if (mainwindow->viewport->follow_vehicle == v->index) {
3378 v->PlayLeaveStationSound(true);
3379 }
3380 }
3381 return Window::OnHotkey(hotkey);
3382 }
3383
3384 void OnQueryTextFinished(std::optional<std::string> str) override
3385 {
3386 if (!str.has_value()) return;
3387
3388 Command<Commands::RenameVehicle>::Post(STR_ERROR_CAN_T_RENAME_TRAIN + to_underlying(Vehicle::Get(this->window_number)->type), static_cast<VehicleID>(this->window_number), *str);
3389 }
3390
3391 void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
3392 {
3393 bool start_stop = widget == WID_VV_START_STOP;
3394 if (start_stop != mouse_over_start_stop) {
3395 mouse_over_start_stop = start_stop;
3397 }
3398 }
3399
3400 void OnMouseWheel(int wheel, WidgetID widget) override
3401 {
3402 if (widget != WID_VV_VIEWPORT) return;
3403 if (_settings_client.gui.scrollwheel_scrolling != ScrollWheelScrolling::Off) {
3404 DoZoomInOutWindow(wheel < 0 ? ZOOM_IN : ZOOM_OUT, this);
3405 }
3406 }
3407
3408 void OnResize() override
3409 {
3410 if (this->viewport != nullptr) {
3412 nvp->UpdateViewportCoordinates(this);
3413 }
3414 }
3415
3418 {
3419 const Vehicle *v = Vehicle::Get(this->window_number);
3420 bool veh_stopped = v->IsStoppedInDepot();
3421
3422 /* Widget WID_VV_GOTO_DEPOT must be hidden if the vehicle is already stopped in depot.
3423 * Widget WID_VV_CLONE_VEH should then be shown, since cloning is allowed only while in depot and stopped.
3424 */
3425 PlaneSelections plane = veh_stopped ? SEL_DC_CLONE : SEL_DC_GOTO_DEPOT;
3426 NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE); // Selection widget 'send to depot' / 'clone'.
3427 if (nwi->shown_plane + SEL_DC_BASEPLANE != plane) {
3428 this->SelectPlane(plane);
3429 }
3430 /* The same system applies to widget WID_VV_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
3431 if (v->IsGroundVehicle()) {
3432 plane = veh_stopped ? SEL_RT_REFIT : SEL_RT_TURN_AROUND;
3434 if (nwi->shown_plane + SEL_RT_BASEPLANE != plane) {
3435 this->SelectPlane(plane);
3436 }
3437 }
3438 }
3439
3445 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
3446 {
3447 if (data == VIWD_AUTOREPLACE) {
3448 /* Autoreplace replaced the vehicle.
3449 * Nothing to do for this window. */
3450 return;
3451 }
3452
3453 this->UpdatePlanes();
3454 this->UpdateButtons();
3455 this->SetDirty();
3456 }
3457
3458 bool IsNewGRFInspectable() const override
3459 {
3460 return ::IsNewGRFInspectable(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3461 }
3462
3463 void ShowNewGRFInspectWindow() const override
3464 {
3465 ::ShowNewGRFInspectWindow(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3466 }
3467
3468 static inline HotkeyList hotkeys{"vehicleview", {
3469 Hotkey('H', "honk", WID_VV_HONK_HORN),
3470 }};
3471};
3472
3475 WindowPosition::Automatic, "view_vehicle", 250, 116,
3477 {},
3479 &VehicleViewWindow::hotkeys
3480);
3481
3487 WindowPosition::Automatic, "view_vehicle_train", 250, 134,
3489 {},
3491 &VehicleViewWindow::hotkeys
3492);
3493
3502
3509{
3510 assert(v != nullptr);
3511 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3512
3513 v = v->First();
3514 if (!v->IsPrimaryVehicle()) return false;
3515
3516 return _thd.GetCallbackWnd()->OnVehicleSelect(v);
3517}
3518
3525bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end)
3526{
3527 assert(begin != end);
3528 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3529
3530 /* If there is only one vehicle in the group, act as if we clicked a single vehicle */
3531 if (begin + 1 == end) return _thd.GetCallbackWnd()->OnVehicleSelect(*begin);
3532
3533 return _thd.GetCallbackWnd()->OnVehicleSelect(begin, end);
3534}
3535
3541bool VehicleClicked(const GUIVehicleGroup &vehgroup)
3542{
3543 return VehicleClicked(vehgroup.vehicles_begin, vehgroup.vehicles_end);
3544}
3545
3546void StopGlobalFollowVehicle(const Vehicle *v)
3547{
3548 Window *w = GetMainWindow();
3549 if (w->viewport->follow_vehicle == v->index) {
3550 const Vehicle *moving_front = v->GetMovingFront();
3551 ScrollMainWindowTo(moving_front->x_pos, moving_front->y_pos, moving_front->z_pos, true); // lock the main view on the vehicle's last position
3552 w->viewport->CancelFollow(*w);
3553 }
3554}
3555
3556
3562void CcBuildPrimaryVehicle(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray)
3563{
3564 if (result.Failed()) return;
3565
3566 const Vehicle *v = Vehicle::Get(new_veh_id);
3568}
3569
3577{
3578 switch (v->type) {
3579 case VehicleType::Train:
3580 return Train::From(v)->GetDisplayImageWidth();
3581
3582 case VehicleType::Road:
3584
3585 default:
3586 bool rtl = _current_text_dir == TD_RTL;
3587 VehicleSpriteSeq seq;
3588 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3589 Rect rec;
3590 seq.GetBounds(&rec);
3591 return UnScaleGUI(rec.Width());
3592 }
3593}
3594
3601int GetVehicleWidth(const Vehicle *v, EngineImageType image_type)
3602{
3603 if (v->type == VehicleType::Train || v->type == VehicleType::Road) {
3604 int vehicle_width = 0;
3605 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
3606 vehicle_width += GetSingleVehicleWidth(u, image_type);
3607 }
3608 return vehicle_width;
3609 } else {
3610 return GetSingleVehicleWidth(v, image_type);
3611 }
3612}
3613
3620{
3621 bool rtl = _current_text_dir == TD_RTL;
3622
3623 _cursor.sprites.clear();
3624 int total_width = 0;
3625 int y_offset = 0;
3626 bool rotor_seq = false; // Whether to draw the rotor of the vehicle in this step.
3627 bool is_ground_vehicle = v->IsGroundVehicle();
3628
3629 while (v != nullptr) {
3630 if (total_width >= ScaleSpriteTrad(2 * (int)VEHICLEINFO_FULL_VEHICLE_WIDTH)) break;
3631
3633 VehicleSpriteSeq seq;
3634
3635 if (rotor_seq) {
3636 GetCustomRotorSprite(Aircraft::From(v), image_type, &seq);
3637 if (!seq.IsValid()) seq.Set(SPR_ROTOR_STOPPED);
3638 y_offset = -ScaleSpriteTrad(5);
3639 } else {
3640 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3641 }
3642
3643 int x_offs = 0;
3644 if (v->type == VehicleType::Train) x_offs = Train::From(v)->GetCursorImageOffset();
3645
3646 for (uint i = 0; i < seq.count; ++i) {
3647 PaletteID pal2 = v->vehstatus.Test(VehState::Crashed) || !seq.seq[i].pal ? pal : seq.seq[i].pal;
3648 _cursor.sprites.emplace_back(seq.seq[i].sprite, pal2, rtl ? (-total_width + x_offs) : (total_width + x_offs), y_offset);
3649 }
3650
3651 if (v->type == VehicleType::Aircraft && v->subtype == AIR_HELICOPTER && !rotor_seq) {
3652 /* Draw rotor part in the next step. */
3653 rotor_seq = true;
3654 } else {
3655 total_width += GetSingleVehicleWidth(v, image_type);
3656 v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr;
3657 }
3658 }
3659
3660 if (is_ground_vehicle) {
3661 /* Center trains and road vehicles on the front vehicle */
3662 int offs = (ScaleSpriteTrad(VEHICLEINFO_FULL_VEHICLE_WIDTH) - total_width) / 2;
3663 if (rtl) offs = -offs;
3664 for (auto &cs : _cursor.sprites) {
3665 cs.pos.x += offs;
3666 }
3667 }
3668
3670}
Base for aircraft.
@ DestinationTooFar
Next destination is too far away.
Definition aircraft.h:37
@ AIR_HELICOPTER
an helicopter
Definition aircraft.h:29
void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type)
Draws an image of an aircraft.
CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
Ors the refit_masks of all articulated parts.
Functions related to articulated vehicles.
void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
Show the autoreplace configuration window for a particular group.
Functions related to the autoreplace GUIs.
@ PathfinderLost
Vehicle's pathfinder is lost.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr bool HasAtMostOneBit(T value)
Test whether value has at most 1 bit set.
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
Dimension GetLargestCargoIconSize()
Get dimensions of largest cargo icon.
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
CargoTypes _cargo_mask
Bitmask of cargo types available.
Definition cargotype.cpp:30
@ Passengers
Passengers.
Definition cargotype.h:51
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
uint Count() const
Count the number of set bits.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr Timpl & Set()
Set all bits.
Common return value for all commands.
bool Failed() const
Did this command fail?
static constexpr int DAYS_IN_ECONOMY_MONTH
Days in an economy month, when in wallclock timekeeping mode.
StringID GetAircraftTypeText() const
Get the name of the aircraft type for display purposes.
Definition engine.cpp:493
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:193
bool(const GUIVehicleGroup *item, CargoType filter) FilterFunction
Baseclass for nested widgets.
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:951
uint current_x
Current horizontal size (after resizing).
int pos_x
Horizontal position of top-left corner of the widget in the window.
uint current_y
Current vertical size (after resizing).
Base class for a 'real' widget.
void SetStringTip(StringID string, StringID tool_tip)
Set string and tool tip of the nested widget.
Definition widget.cpp:1195
Stacked widgets, widgets all occupying the same space in the window.
int shown_plane
Plane being displayed (for NWID_SELECTION only).
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1453
Nested widget to display a viewport in a window.
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition widget.cpp:2435
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.
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2458
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:2532
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
size_type GetPosition() const
Gets the position of the first visible element in the list.
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static constexpr TimerGame< struct Economy >::Date MAX_DATE
static constexpr TimerGame< struct Economy >::Year MAX_YEAR
static Date date
Current date in days (day counter).
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static constexpr Year DateToYear(Date date)
static constexpr Date DateAtStartOfYear(Year year)
StrongType::Typedef< int32_t, DateTag< struct Economy >, StrongType::Compare, StrongType::Integer > Date
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
Functions related to commands.
@ QueryCost
query cost only, don't build.
Commands
List of commands.
Definition of stuff that is very close to a company, like the company struct itself.
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.
static constexpr Owner OWNER_NONE
The tile has no ownership.
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Command definitions related to depots.
void CcCloneVehicle(Commands, const CommandCost &result, VehicleID veh_id)
This is the Callback method after the cloning attempt of a vehicle.
Map related accessors for depots.
DestinationID GetDepotDestinationIndex(Tile t)
Get the destination index of a 'depot'.
Definition depot_map.h:69
@ DIR_W
West.
@ DIR_E
East.
void ShowDropDownMenu(Window *w, std::span< const StringID > strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width, DropDownOptions options, std::string *const persistent_filter_text)
Show a dropdown menu window near a widget of the parent window.
Definition dropdown.cpp:629
std::unique_ptr< DropDownListItem > MakeDropDownListDividerItem()
Creates new DropDownListDividerItem.
Definition dropdown.cpp:36
std::unique_ptr< DropDownListItem > MakeDropDownListIconItem(SpriteID sprite, PaletteID palette, StringID str, int value, bool masked, bool shaded)
Creates new DropDownListIconItem.
Definition dropdown.cpp:70
std::unique_ptr< DropDownListItem > MakeDropDownListStringItem(StringID str, int value, bool masked, bool shaded)
Creates new DropDownListStringItem.
Definition dropdown.cpp:49
Dimension GetDropDownListDimension(const DropDownList &list)
Determine width and height required to fully display a DropDownList.
Definition dropdown.cpp:547
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options, std::string *const persistent_filter_text)
Show a drop down list.
Definition dropdown.cpp:587
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.
@ Filterable
Set if the dropdown is filterable.
bool IsEngineRefittable(EngineID engine)
Check if an engine is refittable.
Definition engine.cpp:1315
Functions related to engines.
@ AutoRefit
Automatic refitting is allowed.
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
@ Maglev
Maglev acceleration model.
Definition engine_type.h:50
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:88
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:972
void UpdateCursorSize()
Update cursor dimension.
Definition gfx.cpp:1623
bool _shift_pressed
Is Shift pressed?
Definition gfx.cpp:40
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:900
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:939
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:669
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
uint GetStringListWidth(std::span< const StringID > list, FontSize fontsize)
Get maximum width of a list of strings.
Definition gfx.cpp:924
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1038
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:788
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:70
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ Small
Index of the small font in the font tables.
Definition gfx_type.h:250
@ Normal
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_LEFT
Left align the text.
Definition gfx_type.h:388
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
@ White
White.
Definition gfx_type.h:300
@ Invalid
Invalid marker.
Definition gfx_type.h:302
@ Orange
Orange.
Definition gfx_type.h:297
@ Grey
Grey.
Definition gfx_type.h:299
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:332
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 SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FontSize::Normal)
Widget part function for setting the minimal text lines.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetArrowWidgetTypeTip(ArrowWidgetType widget_type, StringID tip={})
Widget part function for setting the arrow widget type and tooltip.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Command definitions related to engine groups.
void CcAddVehicleNewGroup(Commands, const CommandCost &result, GroupID new_group, GroupID, VehicleID, bool, const VehicleListIdentifier &)
Open rename window after adding a vehicle to a new group via drag and drop.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:980
void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type, GroupID group)
Show the group window for the given company and vehicle type.
void ShowCompanyGroupForVehicle(const Vehicle *v)
Show the group window for the given vehicle.
Functions/definitions that have something to do with groups.
static constexpr GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:18
static constexpr GroupID ALL_GROUP
All vehicles are in this group.
Definition group_type.h:17
static constexpr GroupID NEW_GROUP
Sentinel for a to-be-created group.
Definition group_type.h:16
GUI functions that shouldn't be here.
void ShowExtraViewportWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Hotkey related functions.
static bool CargoFilter(const Industry *const *industry, const std::pair< CargoType, CargoType > &cargoes)
Cargo filter functions.
Point RemapCoords(int x, int y, int z)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:81
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
Zooms a viewport in a window in or out.
Definition main_gui.cpp:93
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:407
constexpr uint ToPercent16(uint i)
Converts a "fract" value 0..65535 to "percent" value 0..100.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
constexpr To ClampTo(From value)
Clamp the given value down to lie within the requested type.
void GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:690
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_FREIGHT
Show only vehicles which carry any freight (non-passenger) cargo.
Definition cargo_type.h:98
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_ANY
Show all items independent of carried cargo (i.e. no filtering).
Definition cargo_type.h:95
GrfSpecFeature GetGrfSpecFeature(VehicleType type)
Get the GrfSpecFeature associated with a VehicleType.
Definition newgrf.cpp:1905
@ CargoSuffix
Show suffix after cargo name.
@ CBID_VEHICLE_CARGO_SUFFIX
Determine the cargo "suffixes" for each refit possibility of a cargo.
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.
Functions/types related to NewGRF debugging.
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.
StringID GetGRFStringID(uint32_t grfid, GRFStringID stringid)
Returns the index for this stringid associated with its grfID.
Header of Action 04 "universal holder" structure and functions.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer > GRFStringID
Type for GRF-internal string IDs.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
@ Vehicle
Vehicle news item. (new engine available).
Definition news_type.h:81
uint16_t GetServiceIntervalClamped(int interval, bool ispercent)
Clamp the service interval to the correct min/max.
Command definitions related to orders.
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related).
Definition order_type.h:18
@ Halt
Service the vehicle and then halt it.
Definition order_type.h:118
@ Unbunch
Service the vehicle and then unbunch it.
Definition order_type.h:120
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel).
Definition order_type.h:39
PixelColour GetColourGradient(Colours colour, Shade shade)
Get colour gradient palette index.
Definition palette.cpp:393
@ Normal
Normal colour shade.
Road vehicle states.
Command definitions related to road vehicles.
void DrawRoadVehImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip)
Draws an image of a road vehicle chain.
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
@ Off
Scroll wheel has no effect.
void DrawShipImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type)
Draws an image of a ship.
Definition ship_gui.cpp:30
bool ScrollMainWindowTo(int x, int y, int z, bool instant)
Scrolls the main window to given coordinates.
static const PaletteID PALETTE_ALL_BLACK
Exchange any colour by black, needed for painting fictive tiles outside map.
Definition sprites.h:1625
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1404
static const SpriteID SPR_CLONE_TRAIN
Clone vehicles stuff.
Definition sprites.h:100
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:429
Functions related to low-level strings.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition string_type.h:25
uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:236
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:218
Functions related to OTTD's strings.
static void PrepareArgsForNextRun(std::span< StringParameter > args)
Prepare the string parameters for the next formatting run, resetting the type information.
auto MakeParameters(Args &&... args)
Helper to create the StringParameters with its own buffer with the given parameter values.
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.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames).
@ TD_RTL
Text is written right-to-left by default.
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:73
VehicleAirFlags flags
Aircraft flags.
Definition aircraft.h:82
std::string name
Name of vehicle.
VehicleOrderID cur_real_order_index
The index to the current real (non-implicit) order.
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
TimerGameTick::Ticks lateness_counter
How many ticks late (or early if negative) this vehicle is.
void SetCargoFilter(CargoType cargo_type)
Set cargo filter for the vehicle group list.
VehicleListIdentifier vli
Identifier of the vehicle list we want to currently show.
static const VehicleTypeIndexArray< const StringID > vehicle_depot_name
List of depot name strings for each VehicleType.
void DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
Draw all the vehicle list items.
VehicleID vehicle_sel
Selected vehicle.
Listing * sorting
Pointer to the vehicle type related sorting.
GroupBy grouping
How we want to group the list.
Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create)
Compute the size for the Action dropdown.
uint order_arrow_width
Width of the arrow in the small order list.
uint8_t unitnumber_digits
The number of digits of the highest unit number.
DropDownList BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create)
Display the Action dropdown window.
VehicleList vehicles
List of vehicles. This is the buffer for vehgroups to point into; if this is structurally modified,...
GUIVehicleGroupList vehgroups
List of (groups of) vehicles. This stores iterators of vehicles, and should be rebuilt if vehicles is...
void SetCargoFilterArray()
Populate the filter list and set the cargo filter criteria.
DropDownList BuildCargoDropDownList(bool full) const
Build drop down list for cargo filter selection.
void OnInit() override
Notification that the nested widget tree gets initialized.
CargoType cargo_filter_criteria
Selected cargo filter index.
void FilterVehicleList()
Filter the engine list against the currently selected cargo filter.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:117
Specification of a cargo type.
Definition cargotype.h:75
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:139
SpriteID GetCargoIcon() const
Get sprite for showing cargo of this type.
StringID name
Name of this type of cargo.
Definition cargotype.h:92
Comparator to sort CargoType by according to desired order.
Definition cargotype.h:242
T y
Y coordinate.
T x
X coordinate.
Dimensions (a width and height) of a rectangle in 2D.
EngineMiscFlags misc_flags
Miscellaneous flags.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
VehicleList::const_iterator vehicles_end
Pointer to past-the-end element of this vehicle group.
VehicleList::const_iterator vehicles_begin
Pointer to beginning element of this vehicle group.
Cached, frequently calculated values.
uint32_t cached_weight
Total weight of the consist (valid only for the first engine).
uint32_t cached_power
Total power of the consist (valid only for the first engine).
uint16_t cached_total_length
Length of the whole vehicle (valid only for the first engine).
uint32_t cached_max_te
Maximum tractive effort of consist (valid only for the first engine).
List of hotkeys for a window.
Definition hotkeys.h:46
All data for a single hotkey.
Definition hotkeys.h:22
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:384
VehicleOrderID GetNext(VehicleOrderID cur) const
Get the order after the given one or the first one, if the given one is the last one.
Definition order_base.h:476
If you change this, keep in mind that it is also saved in 2 other places:
Definition order_base.h:34
TileIndex GetLocation(const Vehicle *v, bool airport=false) const
Returns a tile somewhat representing the order destination (not suitable for pathfinding).
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:100
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:73
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:176
Colour for pixel/line drawing.
Definition gfx_type.h:414
static Vehicle * Get(auto index)
static Vehicle * GetIfValid(auto index)
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
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.
Rect WithX(int new_left, int new_right) const
Create a new Rect, replacing the left and right coordinates.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Option to refit a vehicle chain.
StringID string
GRF-local String to display for the cargo.
CargoType cargo
Cargo to refit to.
uint8_t subtype
Subcargo to use.
bool operator==(const RefitOption &other) const
Equality operator for RefitOption.
Refit cargo window.
void OnInit() override
Notification that the nested widget tree gets initialized.
uint information_width
Width required for correctly displaying all cargoes in the information panel.
void SetSelection(uint click_row)
Select a row.
void BuildRefitList()
Collects all (cargo, subcargo) refit options of a vehicle chain.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
int sprite_left
Left position of the vehicle sprite.
void RefreshScrollbar()
Refresh scrollbar after selection changed.
RefitOptions refit_list
List of refit subtypes available for each sorted cargo.
void OnMouseDrag(Point pt, WidgetID widget) override
An 'object' is being dragged at the provided position, highlight the target if possible.
void OnResize() override
Called after the window got resized.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
const RefitOption * selected_refit
Selected refit option.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
uint vehicle_margin
Margin to use while selecting vehicles when the vehicle image is centered.
uint8_t num_vehicles
Number of selected vehicles.
bool auto_refit
Select cargo for auto-refitting.
int sprite_right
Right position of the vehicle sprite.
VehicleOrderID order
If not INVALID_VEH_ORDER_ID, selection is part of a refit order (rather than execute directly).
VehicleID selected_vehicle
First vehicle in the current selection.
void OnPaint() override
The window must be repainted.
void OnDragDrop(Point pt, WidgetID widget) override
A dragged 'object' has been released.
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.
Scrollbar * hscroll
Only used for long vehicles.
int vehicle_width
Width of the vehicle being drawn.
Scrollbar * vscroll
The main scrollbar.
int click_x
Position of the first click while dragging.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
std::string GetCapacityString(const RefitOption &option) const
Gets the StringID to use for displaying capacity.
int GetDisplayImageWidth(Point *offset=nullptr) const
Get the width of a road vehicle image in the GUI.
static bool IsExpected(const BaseStation *st)
T * Next() const
Get next vehicle in the chain.
static Train * From(Vehicle *v)
'Train' is either a loco or a wagon.
Definition train.h:97
int GetDisplayImageWidth(Point *offset=nullptr) const
Get the width of a train vehicle image in the GUI.
VehicleRailFlags flags
Which flags has this train currently set.
Definition train.h:98
int GetCursorImageOffset() const
Get the offset for train image when it is used as cursor.
uint16_t cached_max_speed
Maximum speed of the consist (minimum of the max speed of all vehicles in the consist).
Default settings for vehicles.
uint16_t servint_aircraft
service interval for aircraft
uint16_t servint_roadveh
service interval for road vehicles
uint16_t servint_ships
service interval for ships
uint16_t servint_trains
service interval for trains
bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
uint GetRoadVehDetailsHeight(const Vehicle *v)
Gets the desired height for the road vehicle details panel.
static void DrawVehicleDetails(const Vehicle *v, const Rect &r, int vscroll_pos, uint vscroll_cap, TrainDetailsWindowTabs det_tab)
Draw the details for the given vehicle at the position of the Details windows.
void OnPaint() override
Repaint vehicle details window.
static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type, CompanyID company_id)
Checks whether service interval is enabled for the vehicle.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
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.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnResize() override
Called after the window got resized.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
VehicleDetailsWindow(WindowDesc &desc, WindowNumber window_number)
Initialize a newly created vehicle details window.
TrainDetailsWindowTabs tab
For train vehicles: which tab is displayed.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
The information about a vehicle list.
Definition vehiclelist.h:32
VehicleListType type
The type of vehicle list.
Definition vehiclelist.h:33
WindowNumber ToWindowNumber() const
Pack a VehicleListIdentifier in 32 bits so it can be used as unique WindowNumber.
VehicleType vtype
The vehicle type associated with this list.
Definition vehiclelist.h:34
Window for the (old) vehicle listing.
void OnMouseLoop() override
Called for every mouse loop run, which is at least once per (game) tick.
void OnResize() override
Called after the window got resized.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnPaint() override
The window must be repainted.
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.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
CaptionPlanes
Enumeration of planes of the title row at the top.
@ BP_SHARED_ORDERS
Show the normal caption.
@ BP_NORMAL
Show shared orders caption and buttons.
void OnGameTick() override
Called once per (game) tick.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
~VehicleListWindow() override
Save the last sorting state.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
ButtonPlanes
Enumeration of planes of the button row at the bottom.
@ BP_HIDE_BUTTONS
Show the empty panel.
@ BP_SHOW_BUTTONS
Show the buttons.
Sprite sequence for a vehicle part.
bool IsValid() const
Check whether the sequence contains any sprites.
void GetBounds(Rect *bounds) const
Determine shared bounds of all sprites.
Definition vehicle.cpp:123
void Set(SpriteID sprite)
Assign a single sprite to the sequence.
Window manager class for viewing a vehicle.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
void OnResize() override
Called after the window got resized.
void ShowNewGRFInspectWindow() const override
Show the NewGRF inspection window.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void SelectPlane(PlaneSelections plane)
Display a plane in the window.
PlaneSelections
Display planes available in the vehicle view window.
@ SEL_RT_BASEPLANE
First plane of the WID_VV_SELECT_REFIT_TURN stacked widget.
@ SEL_RT_TURN_AROUND
Display 'turn around' button in WID_VV_SELECT_REFIT_TURN stacked widget.
@ SEL_DC_BASEPLANE
First plane of the WID_VV_SELECT_DEPOT_CLONE stacked widget.
@ SEL_RT_REFIT
Display 'refit' button in WID_VV_SELECT_REFIT_TURN stacked widget.
@ SEL_DC_GOTO_DEPOT
Display 'goto depot' button in WID_VV_SELECT_DEPOT_CLONE stacked widget.
@ SEL_DC_CLONE
Display 'clone vehicle' button in WID_VV_SELECT_DEPOT_CLONE stacked widget.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnMouseOver(Point pt, WidgetID widget) override
The mouse is currently moving over the window or has just moved outside of the window.
void UpdatePlanes()
Selects appropriate plane for current state of the shown vehicle.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
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.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void OnPaint() override
The window must be repainted.
EventState OnHotkey(int hotkey) override
A hotkey has been pressed.
void UpdateButtons()
Update buttons state to match shown vehicle.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnMouseWheel(int wheel, WidgetID widget) override
The mouse wheel has been turned.
bool IsNewGRFInspectable() const override
Is the data related to this window NewGRF inspectable?
Vehicle data structure.
Money GetDisplayProfitThisYear() const
Gets the profit vehicle had this year.
EngineID engine_type
The type of engine used for this vehicle.
virtual int GetDisplaySpeed() const
Gets the speed in km-ish/h that can be sent into string parameters for string processing.
int32_t z_pos
z coordinate.
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:748
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
Vehicle * GetNextArticulatedPart() const
Get the next part of an articulated engine.
virtual bool IsChainInDepot() const
Check whether the whole vehicle chain is in the depot.
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
uint16_t cargo_cap
total capacity
Vehicle * GetFirstEnginePart()
Get the first part of an articulated engine.
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
bool HasArticulatedPart() const
Check if an engine has an articulated part.
uint8_t breakdown_ctr
Counter for managing breakdown events.
GroupID group_id
Index of group Pool array.
virtual int GetDisplayMaxSpeed() const
Gets the maximum speed in km-ish/h that can be sent into string parameters for string processing.
bool IsGroundVehicle() const
Check if the vehicle is a ground vehicle.
VehStates vehstatus
Status.
bool IsArticulatedPart() const
Check if the vehicle is an articulated part of an engine.
CargoType cargo_type
type of cargo this vehicle is carrying
VehicleOrderID GetNumManualOrders() const
Get the number of manually added orders this vehicle has.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
Order current_order
The current order (+ status, like: loading).
Vehicle * Next() const
Get the next vehicle of this vehicle.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
const GRFFile * GetGRF() const
Retrieve the NewGRF the vehicle is tied to.
Definition vehicle.cpp:758
OrderList * orders
Pointer to the order list for this vehicle.
uint32_t GetDisplayMinPowerToWeight() const
Calculates the minimum power-to-weight ratio using the maximum weight of the ground vehicle.
Definition vehicle.cpp:3295
Vehicle * GetMovingFront() const
Get the moving front of the vehicle chain.
Money value
Value of the vehicle.
void InvalidateNewGRFCache()
Invalidates cached NewGRF variables.
VehicleCache vcache
Cache of often used vehicle values.
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the vehicle is tied to.
Definition vehicle.cpp:768
GroundVehicleCache * GetGroundVehicleCache()
Access the ground vehicle cache of the vehicle.
Definition vehicle.cpp:3191
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
Money GetDisplayProfitLastYear() const
Gets the profit vehicle had last year.
uint16_t cur_speed
current speed
uint8_t cargo_subtype
Used for livery refits (NewGRF variations).
TimerGameCalendar::Date age
Age in calendar days.
bool IsWaitingForUnbunching() const
Check whether a vehicle inside a depot is waiting for unbunching.
Definition vehicle.cpp:2573
Money GetDisplayRunningCost() const
Gets the running cost of a vehicle that can be sent into string parameters for string processing.
uint8_t breakdowns_since_last_service
Counter for the amount of breakdowns.
TimerGameCalendar::Date max_age
Maximum age.
uint16_t reliability
Reliability.
Vehicle * FirstShared() const
Get the first vehicle of this vehicle chain.
Vehicle * Previous() const
Get the previous vehicle of this vehicle.
virtual void PlayLeaveStationSound(bool force=false) const
Play the sound associated with leaving the station.
virtual bool IsInDepot() const
Check whether the vehicle is in the depot.
TileIndex tile
Current tile index.
Owner owner
Which company owns the vehicle?
UnitID unitnumber
unit number, for display purposes only
virtual void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const
Gets the sprite to show for the given direction.
High level window description.
Definition window_gui.h:168
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:274
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:992
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1117
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:835
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1822
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:786
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3262
Window * parent
Parent window.
Definition window_gui.h:329
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:570
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:319
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:518
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:818
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1089
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:516
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1812
void SetWidgetsLoweredState(bool lowered_stat, Args... widgets)
Sets the lowered/raised status of a list of widgets.
Definition window_gui.h:527
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:317
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:442
int left
x position of left edge of the window
Definition window_gui.h:310
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1846
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:990
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:461
WindowFlags flags
Window flags.
Definition window_gui.h:301
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:327
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:584
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
int height
Height of the window (number of pixels down in y direction).
Definition window_gui.h:313
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
Stuff related to the text buffer GUI.
@ TE_RISING
Make the text effect slowly go upwards.
Definition texteff.hpp:22
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
Functions related to tile highlights.
void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Window *w)
Change the cursor and mouse click/drag handling to a mode for performing special operations like tile...
@ HT_DRAG
dragging items in the depot windows
@ HT_VEHICLE
vehicle is accepted as target as well (bitmask)
Functions related to time tabling.
void ShowTimetableWindow(const Vehicle *v)
Show the timetable for a given vehicle.
Base for the train class.
@ Stuck
Train can't get a path reservation.
Definition train.h:32
@ TFP_SIGNAL
Ignore next signal, after the signal ignore being stuck.
Definition train.h:42
Command definitions related to trains.
void DrawTrainImage(const Train *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip, VehicleID drag_dest)
Draws an image of a whole train.
Definition train_gui.cpp:99
PaletteID GetVehiclePalette(const Vehicle *v)
Get the colour map for a vehicle.
Definition vehicle.cpp:2170
void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8_t num_vehicles)
Calculates the set of vehicles that will be affected by a given selection.
Definition vehicle.cpp:3254
@ Crashed
Vehicle is crashed.
@ Stopped
Vehicle is stopped by the player.
Command definitions for vehicles.
void CcStartStopVehicle(Commands, const CommandCost &result, VehicleID veh_id, bool)
This is the Callback method after attempting to start/stop a vehicle.
Functions related to vehicles.
static const TimerGameEconomy::Date VEHICLE_PROFIT_MIN_AGE
Only vehicles older than this have a meaningful profit.
static const Money VEHICLE_PROFIT_THRESHOLD
Threshold for a vehicle to be considered making good profit.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
void DrawAircraftDetails(const Aircraft *v, const Rect &r)
Draw the details for the given vehicle at the given position.
static bool CargoFilter(const GUIVehicleGroup *vehgroup, const CargoType cargo_type)
Check whether a vehicle can carry a specific cargo.
int GetSingleVehicleWidth(const Vehicle *v, EngineImageType image_type)
Get the width of a vehicle (part) in pixels.
void DrawShipDetails(const Vehicle *v, const Rect &r)
Draw the details for the given vehicle at the given position.
Definition ship_gui.cpp:63
static const Vehicle * _last_vehicle[2]
Cached values for VehicleNameSorter to spare many GetString() calls.
int GetVehicleWidth(const Vehicle *v, EngineImageType image_type)
Get the width of a vehicle (including all parts of the consist) in pixels.
StringID GetCargoSubtypeText(const Vehicle *v)
Get the cargo subtype text from NewGRF for the vehicle details window.
static WindowDesc _vehicle_view_desc(WindowPosition::Automatic, "view_vehicle", 250, 116, WC_VEHICLE_VIEW, WC_NONE, {}, _nested_vehicle_view_widgets, &VehicleViewWindow::hotkeys)
Vehicle view window descriptor for all vehicles but trains.
static void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
Assign a vehicle window a new vehicle.
static VehicleTypeIndexArray< WindowDesc > _vehicle_list_desc
Window definitions for the vehicle list windows.
static bool IsVehicleRefittable(const Vehicle *v)
Checks whether the vehicle may be refitted at the moment.
static constexpr std::initializer_list< NWidgetPart > _nested_train_vehicle_details_widgets
Train details widgets.
static constexpr VehicleTypeIndexArray< const ZoomLevel > _vehicle_view_zoom_levels
Zoom levels for vehicle views indexed by vehicle type.
void CcBuildPrimaryVehicle(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray)
This is the Callback method after the construction attempt of a primary vehicle.
static const int VV_INITIAL_VIEWPORT_HEIGHT
static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN
uint GetUnitNumberDigits(VehicleList &vehicles)
Get the number of digits the biggest unit number of a set of vehicles has.
static constexpr VehicleTypeIndexArray< const StringID > _vehicle_msg_translation_table[]
Command codes for the shared buttons indexed by VehicleCommandTranslation and vehicle type.
void ShowVehicleViewWindow(const Vehicle *v)
Shows the vehicle view window of the given vehicle.
static WindowDesc _train_vehicle_details_desc(WindowPosition::Automatic, "view_vehicle_details_train", 405, 178, WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW, {}, _nested_train_vehicle_details_widgets)
Vehicle details window descriptor.
uint CountDigitsForAllocatingSpace(uint number)
Get the number of digits of space required for the given number.
static const uint MAX_REFIT_CYCLE
Maximum number of refit cycles we try, to prevent infinite loops.
static WindowDesc _nontrain_vehicle_details_desc(WindowPosition::Automatic, "view_vehicle_details", 405, 113, WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW, {}, _nested_nontrain_vehicle_details_widgets)
Vehicle details window descriptor for other vehicles than a train.
VehicleCommandTranslation
Command indices for the _vehicle_command_translation_table.
uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
Display list of cargo types of the engine, for the purchase information window.
void DrawVehicleImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip)
Draws an image of a vehicle chain.
void DrawTrainDetails(const Train *v, const Rect &r, int vscroll_pos, uint16_t vscroll_cap, TrainDetailsWindowTabs det_tab)
Draw the details for the given vehicle at the given position.
static void DrawVehicleProfitButton(TimerGameEconomy::Date age, Money display_profit_last_year, uint num_vehicles, int x, int y)
Draw the vehicle profit button in the vehicle list window.
static int GetUnitNumberWidth(int digits)
Get width required for the formatted unit number display.
static constexpr std::initializer_list< NWidgetPart > _nested_vehicle_view_widgets
Vehicle view widgets.
static WindowDesc _vehicle_refit_desc(WindowPosition::Automatic, "view_vehicle_refit", 240, 174, WC_VEHICLE_REFIT, WC_VEHICLE_VIEW, WindowDefaultFlag::Construction, _nested_vehicle_refit_widgets)
Window definition for the vehicle refit window.
static bool CargoFilterSingle(const Vehicle *v, const CargoType cargo_type)
Check whether a single vehicle should pass the filter.
uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_cargo_type)
Get the best fitting subtype when 'cloning'/'replacing' v_from with v_for.
void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index)
Report a change in vehicle IDs (due to autoreplace) to affected vehicle windows.
int GetTrainDetailsWndVScroll(VehicleID veh_id, TrainDetailsWindowTabs det_tab)
Determines the number of lines in the train details window.
static const int VV_INITIAL_VIEWPORT_WIDTH
bool VehicleClicked(const Vehicle *v)
Dispatch a "vehicle selected" event if any window waits for it.
uint GetVehicleListHeight(VehicleType type, uint divisor)
Get the height of a vehicle in the vehicle list GUIs.
void StartStopVehicle(const Vehicle *v, bool texteffect)
Executes Commands::StartStopVehicle for given vehicle.
void SetMouseCursorVehicle(const Vehicle *v, EngineImageType image_type)
Set the mouse cursor to look like a vehicle.
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
Show the refit window for a vehicle.
void DrawCargoIconOverlays(std::span< const CargoIconOverlay > overlays, int y)
Draw a list of cargo icon overlays.
void AddCargoIconOverlay(std::vector< CargoIconOverlay > &overlays, int x, int width, const Vehicle *v)
Add a cargo icon to the list of overlays.
static constexpr std::initializer_list< NWidgetPart > _nested_nontrain_vehicle_details_widgets
Vehicle details widgets (other than train).
static bool VehicleIndividualToGroupSorterWrapper(GUIVehicleGroup const &a, GUIVehicleGroup const &b)
Wrapper to convert a VehicleIndividualSortFunction to a VehicleGroupSortFunction.
void DrawCargoIconOverlay(int x, int y, CargoType cargo_type)
Draw a cargo icon overlaying an existing sprite, with a black contrast outline.
bool ShowCargoIconOverlay()
Test if cargo icon overlays should be drawn.
static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption *sel, uint pos, uint rows, uint delta, const Rect &r)
Draw the list of available refit options for a consist and highlight the selected refit option (if an...
static void ShowVehicleDetailsWindow(const Vehicle *v)
Shows the vehicle details window of the given vehicle.
void DrawRoadVehDetails(const Vehicle *v, const Rect &r)
Draw the details for the given vehicle at the given position.
std::map< CargoType, std::vector< RefitOption >, CargoTypeComparator > RefitOptions
Available refit options (subtype and string) associated with each cargo type.
void CcStartStopVehicle(Commands, const CommandCost &result, VehicleID veh_id, bool)
This is the Callback method after attempting to start/stop a vehicle.
static WindowDesc _train_view_desc(WindowPosition::Automatic, "view_vehicle_train", 250, 134, WC_VEHICLE_VIEW, WC_NONE, {}, _nested_vehicle_view_widgets, &VehicleViewWindow::hotkeys)
Vehicle view window descriptor for trains.
@ VIWD_CONSIST_CHANGED
Vehicle composition was changed.
Definition vehicle_gui.h:37
@ VIWD_AUTOREPLACE
Autoreplace replaced the vehicle.
Definition vehicle_gui.h:38
uint GetVehicleHeight(VehicleType type)
Get the height of a single vehicle in the GUIs.
Definition vehicle_gui.h:74
TrainDetailsWindowTabs
The tabs in the train details window.
Definition vehicle_gui.h:25
@ TDW_TAB_CAPACITY
Tab with cargo capacity of the vehicles.
Definition vehicle_gui.h:28
@ TDW_TAB_TOTALS
Tab with sum of total cargo transported.
Definition vehicle_gui.h:29
@ TDW_TAB_INFO
Tab with name and value of the vehicles.
Definition vehicle_gui.h:27
@ TDW_TAB_CARGO
Tab with cargo carried by the vehicles.
Definition vehicle_gui.h:26
Functions/classes shared between the different vehicle list GUIs.
EngineImageType
Visualisation contexts of vehicles and engines.
@ EIT_IN_DETAILS
Vehicle drawn in vehicle details, refit window, ...
@ EIT_IN_LIST
Vehicle drawn in vehicle list, group list, ...
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
VehicleType
Available vehicle types.
@ Ship
Ship vehicle type.
@ Aircraft
Aircraft vehicle type.
@ Road
Road vehicle type.
@ Train
Train vehicle type.
@ MassSend
Tells that it's a mass send to depot command (type in VLW flag).
@ Service
The vehicle will leave the depot right after arrival (service only).
EnumIndexArray< T, VehicleType, Tend > VehicleTypeIndexArray
Array with VehicleType as index.
static const uint MAX_LENGTH_VEHICLE_NAME_CHARS
The maximum length of a vehicle name in characters including '\0'.
@ WID_VR_MATRIX
Options to refit to.
@ WID_VR_SCROLLBAR
Scrollbar for the refit options.
@ WID_VR_SHOW_HSCROLLBAR
Selection widget for the horizontal scrollbar.
@ WID_VR_VEHICLE_PANEL_DISPLAY
Display with a representation of the vehicle to refit.
@ WID_VR_HSCROLLBAR
Horizontal scrollbar or the vehicle display.
@ WID_VR_REFIT
Perform the refit.
@ WID_VR_SELECT_HEADER
Header with question about the cargo to carry.
@ WID_VR_INFO
Information about the currently selected refit option.
@ WID_VR_CAPTION
Caption of window.
@ WID_VL_SORT_BY_PULLDOWN
Sort by dropdown list.
@ WID_VL_START_ALL
Start all button.
@ WID_VL_FILTER_BY_CARGO_SEL
Cargo filter dropdown list panel selector.
@ WID_VL_MANAGE_VEHICLES_DROPDOWN
Manage vehicles dropdown list.
@ WID_VL_HIDE_BUTTONS
Selection to hide the buttons.
@ WID_VL_GROUP_BY_PULLDOWN
Group by dropdown list.
@ WID_VL_AVAILABLE_VEHICLES
Available vehicles.
@ WID_VL_STOP_ALL
Stop all button.
@ WID_VL_CAPTION_SELECTION
Selection for caption.
@ WID_VL_FILTER_BY_CARGO
Cargo filter dropdown list.
@ WID_VL_SORT_ORDER
Sort order.
@ WID_VL_GROUP_ORDER
Group order.
@ WID_VL_SCROLLBAR
Scrollbar for the list.
@ WID_VL_CAPTION
Caption of window (for non shared orders windows).
@ WID_VL_LIST
List of the vehicles.
@ WID_VL_CAPTION_SHARED_ORDERS
Caption of window (for shared orders windows).
@ WID_VL_ORDER_VIEW
Button to open order window (for shared orders windows).
@ WID_VV_GOTO_DEPOT
Order this vehicle to go to the depot.
@ WID_VV_LOCATION
Center the main view on this vehicle.
@ WID_VV_TURN_AROUND
Turn this vehicle around.
@ WID_VV_SELECT_DEPOT_CLONE
Selection widget between 'goto depot', and 'clone vehicle' buttons.
@ WID_VV_FORCE_PROCEED
Force this vehicle to pass a signal at danger.
@ WID_VV_RENAME
Rename vehicle.
@ WID_VV_ORDER_LOCATION
Center the main view on the order's target location.
@ WID_VV_SHOW_ORDERS
Show the orders of this vehicle.
@ WID_VV_CLONE
Clone this vehicle.
@ WID_VV_CAPTION
Caption of window.
@ WID_VV_REFIT
Open the refit window.
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
@ WID_VV_SELECT_REFIT_TURN
Selection widget between 'refit' and 'turn around' buttons.
@ WID_VV_SHOW_DETAILS
Show details of this vehicle.
@ WID_VV_VIEWPORT
Viewport widget.
@ WID_VV_HONK_HORN
Honk the vehicles horn (not drawn on UI, only used for hotkey).
@ WID_VV_FORCE_PROCEED_SEL
Container for 'force proceed' button, which can be hidden.
@ WID_VD_DECREASE_SERVICING_INTERVAL
Decrease the servicing interval.
@ WID_VD_CAPTION
Caption of window.
@ WID_VD_SERVICE_INTERVAL_DROPDOWN
Dropdown to select default/days/percent service interval.
@ WID_VD_INCREASE_SERVICING_INTERVAL
Increase the servicing interval.
@ WID_VD_MATRIX
List of details for trains.
@ WID_VD_SERVICING_INTERVAL
Information about the servicing interval.
@ WID_VD_DETAILS_TRAIN_VEHICLES
Show all parts of the train with their description.
@ WID_VD_SCROLLBAR
Scrollbar for train details.
@ WID_VD_DETAILS_TOTAL_CARGO
Show the capacity and carried cargo amounts aggregated per cargo of the train.
@ WID_VD_TOP_DETAILS
Panel with generic details.
@ WID_VD_MIDDLE_DETAILS
Details for non-trains.
@ WID_VD_DETAILS_CAPACITY_OF_EACH
Show the capacity of all train parts.
@ WID_VD_DETAILS_CARGO_CARRIED
Show carried cargo per part of the train.
bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &vli)
Generate a list of vehicles based on window type.
std::vector< const Vehicle * > VehicleList
A list of vehicles.
Definition vehiclelist.h:68
VehicleListType
Vehicle List type flags.
Definition vehiclelist.h:22
@ VL_STANDARD
Index is the company.
Definition vehiclelist.h:23
@ VL_STATION_LIST
Index is the station.
Definition vehiclelist.h:25
@ VL_DEPOT_LIST
Index is the destination (station for hangar of aircraft, depot for others).
Definition vehiclelist.h:26
@ VL_SHARED_ORDERS
Index is the first vehicle of the shared orders.
Definition vehiclelist.h:24
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Functions related to (drawing on) viewports.
@ ZOOM_IN
Zoom in (get more detailed view).
@ ZOOM_OUT
Zoom out (get helicopter view).
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:309
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:40
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:41
@ WWT_PUSHBTN
Normal push-button (no toggle button) with custom drawing.
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:75
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:73
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:61
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX).
Definition widget_type.h:54
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ SZSP_NONE
Display plane with zero size in both directions (none filling and resizing).
@ EqualSize
Containers should keep all their (resizing) children equally large.
@ Decrease
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:20
@ Increase
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:21
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:1209
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1195
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1166
@ Construction
This window is used for construction; close it whenever changing company.
Definition window_gui.h:153
@ BorderOnly
Draw border only, no background.
Definition window_gui.h:26
Twindow * AllocateWindowDescFront(WindowDesc &desc, WindowNumber window_number, Targs... extra_arguments)
Open a new window.
@ DisableVpScroll
Window does not do autoscroll,.
Definition window_gui.h:234
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:219
@ SBS_UP
Sort descending.
Definition window_gui.h:220
@ Automatic
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:21
EventState
State of handling an event.
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:51
@ WC_SHIPS_LIST
Ships list; Window numbers:
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_VEHICLE_REFIT
Vehicle refit; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_VEHICLE_DETAILS
Vehicle details; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
@ WC_VEHICLE_TIMETABLE
Vehicle timetable; Window numbers:
@ WC_AIRCRAFT_LIST
Aircraft list; 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
ZoomLevel ScaleZoomGUI(ZoomLevel value)
Scale zoom level relative to GUI zoom.
Definition zoom_func.h:87
int UnScaleGUI(int value)
Short-hand to apply GUI zoom level.
Definition zoom_func.h:77
@ Ship
Default zoom level for the ship view.
Definition zoom_type.h:39
@ RoadVehicle
Default zoom level for the road vehicle view.
Definition zoom_type.h:41
@ Aircraft
Default zoom level for the aircraft view.
Definition zoom_type.h:38
@ Train
Default zoom level for the train view.
Definition zoom_type.h:40