OpenTTD Source 20260311-master-g511d3794ce
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<std::array<BaseVehicleListWindow::GroupBy, VEH_COMPANY_END>, 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
159const StringID BaseVehicleListWindow::vehicle_depot_name[] = {
160 STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT,
161 STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT,
162 STR_VEHICLE_LIST_SEND_SHIP_TO_DEPOT,
163 STR_VEHICLE_LIST_SEND_AIRCRAFT_TO_HANGAR
164};
165
166BaseVehicleListWindow::BaseVehicleListWindow(WindowDesc &desc, const VehicleListIdentifier &vli) : Window(desc), vli(vli)
167{
168 this->vehicle_sel = VehicleID::Invalid();
169 this->grouping = _grouping[vli.type][vli.vtype];
170 this->UpdateSortingFromGrouping();
171}
172
173std::span<const StringID> BaseVehicleListWindow::GetVehicleSorterNames() const
174{
175 switch (this->grouping) {
176 case GB_NONE:
177 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_none_sorter_names_wallclock : vehicle_group_none_sorter_names_calendar;
178 case GB_SHARED_ORDERS:
179 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_shared_orders_sorter_names_wallclock : vehicle_group_shared_orders_sorter_names_calendar;
180 default:
181 NOT_REACHED();
182 }
183}
184
191{
192 if (number >= 10000) return 5;
193 if (number >= 1000) return 4;
194 if (number >= 100) return 3;
195
196 /*
197 * When the smallest unit number is less than 10, it is
198 * quite likely that it will expand to become more than
199 * 10 quite soon.
200 */
201 return 2;
202}
203
210{
211 uint unitnumber = 0;
212 for (const Vehicle *v : vehicles) {
213 unitnumber = std::max<uint>(unitnumber, v->unitnumber);
214 }
215
216 return CountDigitsForAllocatingSpace(unitnumber);
217}
218
219void BaseVehicleListWindow::BuildVehicleList()
220{
221 if (!this->vehgroups.NeedRebuild()) return;
222
223 Debug(misc, 3, "Building vehicle list type {} for company {} given index {}", this->vli.type, this->vli.company, this->vli.index);
224
225 this->vehgroups.clear();
226
227 GenerateVehicleSortList(&this->vehicles, this->vli);
228
229 CargoTypes used = 0;
230 for (const Vehicle *v : this->vehicles) {
231 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
232 if (u->cargo_cap > 0) SetBit(used, u->cargo_type);
233 }
234 }
235 this->used_cargoes = used;
236
237 if (this->grouping == GB_NONE) {
238 uint max_unitnumber = 0;
239 for (auto it = this->vehicles.begin(); it != this->vehicles.end(); ++it) {
240 this->vehgroups.emplace_back(it, it + 1);
241
242 max_unitnumber = std::max<uint>(max_unitnumber, (*it)->unitnumber);
243 }
244 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber);
245 } else {
246 /* Sort by the primary vehicle; we just want all vehicles that share the same orders to form a contiguous range. */
247 std::stable_sort(this->vehicles.begin(), this->vehicles.end(), [](const Vehicle * const &u, const Vehicle * const &v) {
248 return u->FirstShared() < v->FirstShared();
249 });
250
251 uint max_num_vehicles = 0;
252
253 VehicleList::const_iterator begin = this->vehicles.begin();
254 while (begin != this->vehicles.end()) {
255 VehicleList::const_iterator end = std::find_if_not(begin, this->vehicles.cend(), [first_shared = (*begin)->FirstShared()](const Vehicle * const &v) {
256 return v->FirstShared() == first_shared;
257 });
258
259 this->vehgroups.emplace_back(begin, end);
260
261 max_num_vehicles = std::max<uint>(max_num_vehicles, static_cast<uint>(end - begin));
262
263 begin = end;
264 }
265
266 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_num_vehicles);
267 }
268 this->FilterVehicleList();
269
270 this->vehgroups.RebuildDone();
271 this->vscroll->SetCount(this->vehgroups.size());
272}
273
281static bool CargoFilterSingle(const Vehicle *v, const CargoType cargo_type)
282{
283 if (cargo_type == CargoFilterCriteria::CF_ANY) {
284 return true;
285 } else if (cargo_type == CargoFilterCriteria::CF_NONE) {
286 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
287 if (w->cargo_cap > 0) {
288 return false;
289 }
290 }
291 return true;
292 } else if (cargo_type == CargoFilterCriteria::CF_FREIGHT) {
293 bool have_capacity = false;
294 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
295 if (w->cargo_cap > 0) {
296 if (IsCargoInClass(w->cargo_type, CargoClass::Passengers)) {
297 return false;
298 } else {
299 have_capacity = true;
300 }
301 }
302 }
303 return have_capacity;
304 } else {
305 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
306 if (w->cargo_cap > 0 && w->cargo_type == cargo_type) {
307 return true;
308 }
309 }
310 return false;
311 }
312}
313
321static bool CargoFilter(const GUIVehicleGroup *vehgroup, const CargoType cargo_type)
322{
323 auto it = vehgroup->vehicles_begin;
324
325 /* Check if any vehicle in the group matches; if so, the whole group does. */
326 for (; it != vehgroup->vehicles_end; it++) {
327 if (CargoFilterSingle(*it, cargo_type)) return true;
328 }
329
330 return false;
331}
332
338{
340}
341
349void AddCargoIconOverlay(std::vector<CargoIconOverlay> &overlays, int x, int width, const Vehicle *v)
350{
351 bool rtl = _current_text_dir == TD_RTL;
352 if (!v->IsArticulatedPart() || v->cargo_type != v->Previous()->cargo_type) {
353 /* Add new overlay slot. */
354 overlays.emplace_back(rtl ? x - width : x, rtl ? x : x + width, v->cargo_type, v->cargo_cap);
355 } else {
356 /* This is an articulated part with the same cargo type, adjust left or right of last overlay slot. */
357 if (rtl) {
358 overlays.back().left -= width;
359 } else {
360 overlays.back().right += width;
361 }
362 overlays.back().cargo_cap += v->cargo_cap;
363 }
364}
365
372void DrawCargoIconOverlay(int x, int y, CargoType cargo_type)
373{
374 if (!ShowCargoIconOverlay()) return;
375 if (!IsValidCargoType(cargo_type)) return;
376
377 const CargoSpec *cs = CargoSpec::Get(cargo_type);
378
379 SpriteID spr = cs->GetCargoIcon();
380 if (spr == 0) return;
381
382 Dimension d = GetSpriteSize(spr);
383 d.width /= 2;
384 d.height /= 2;
385 int one = ScaleGUITrad(1);
386
387 /* Draw the cargo icon in black shifted 4 times to create the outline. */
388 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width - one, y - d.height);
389 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width + one, y - d.height);
390 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height - one);
391 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height + one);
392 /* Draw the cargo icon normally. */
393 DrawSprite(spr, PAL_NONE, x - d.width, y - d.height);
394}
395
401void DrawCargoIconOverlays(std::span<const CargoIconOverlay> overlays, int y)
402{
403 for (const auto &cio : overlays) {
404 if (cio.cargo_cap == 0) continue;
405 DrawCargoIconOverlay((cio.left + cio.right) / 2, y, cio.cargo_type);
406 }
407}
408
409static GUIVehicleGroupList::FilterFunction * const _vehicle_group_filter_funcs[] = {
411};
412
418{
419 if (this->cargo_filter_criteria != cargo_type) {
420 this->cargo_filter_criteria = cargo_type;
421 /* Deactivate filter if criteria is 'Show All', activate it otherwise. */
422 this->vehgroups.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
423 this->vehgroups.SetFilterType(0);
424 this->vehgroups.ForceRebuild();
425 }
426}
427
432{
434 this->vehgroups.SetFilterFuncs(_vehicle_group_filter_funcs);
435 this->vehgroups.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
436}
437
442{
443 this->vehgroups.Filter(this->cargo_filter_criteria);
444 if (this->vehicles.empty()) {
445 /* No vehicle passed through the filter, invalidate the previously selected vehicle */
446 this->vehicle_sel = VehicleID::Invalid();
447 } 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
448 this->vehicle_sel = VehicleID::Invalid();
449 }
450}
451
459Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create)
460{
461 Dimension d = {0, 0};
462
463 if (show_autoreplace) d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_REPLACE_VEHICLES));
464 d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_SEND_FOR_SERVICING));
465 d = maxdim(d, GetStringBoundingBox(this->vehicle_depot_name[this->vli.vtype]));
466
467 if (show_group) {
468 d = maxdim(d, GetStringBoundingBox(STR_GROUP_ADD_SHARED_VEHICLE));
469 d = maxdim(d, GetStringBoundingBox(STR_GROUP_REMOVE_ALL_VEHICLES));
470 } else if (show_create) {
471 d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_CREATE_GROUP));
472 }
473
474 return d;
475}
476
478{
479 this->order_arrow_width = std::max(GetStringBoundingBox(STR_JUST_LEFT_ARROW, FS_SMALL).width, GetStringBoundingBox(STR_JUST_RIGHT_ARROW, FS_SMALL).width);
480 this->SetCargoFilterArray();
481}
482
483StringID BaseVehicleListWindow::GetCargoFilterLabel(CargoType cargo_type) const
484{
485 switch (cargo_type) {
486 case CargoFilterCriteria::CF_ANY: return STR_CARGO_TYPE_FILTER_ALL;
487 case CargoFilterCriteria::CF_FREIGHT: return STR_CARGO_TYPE_FILTER_FREIGHT;
488 case CargoFilterCriteria::CF_NONE: return STR_CARGO_TYPE_FILTER_NONE;
489 default: return CargoSpec::Get(cargo_type)->name;
490 }
491}
492
499{
500 DropDownList list;
501
502 /* Add item for disabling filtering. */
504 /* Add item for freight (i.e. vehicles with cargo capacity and with no passenger capacity). */
506 /* Add item for vehicles not carrying anything, e.g. train engines. */
508
509 /* Add cargos */
511 for (const CargoSpec *cs : _sorted_cargo_specs) {
512 if (!full && !HasBit(this->used_cargoes, cs->Index())) continue;
513 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index(), false, !HasBit(this->used_cargoes, cs->Index())));
514 }
515
516 return list;
517}
518
526DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create)
527{
528 DropDownList list;
529
530 /* Autoreplace actions. */
531 if (show_autoreplace) {
532 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_REPLACE_VEHICLES, ADI_REPLACE));
533 list.push_back(MakeDropDownListDividerItem());
534 }
535
536 /* Group actions. */
537 if (show_group) {
538 list.push_back(MakeDropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE, ADI_ADD_SHARED));
539 list.push_back(MakeDropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, ADI_REMOVE_ALL));
540 list.push_back(MakeDropDownListDividerItem());
541 } else if (show_create) {
542 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_CREATE_GROUP, ADI_CREATE_GROUP));
543 list.push_back(MakeDropDownListDividerItem());
544 }
545
546 /* Depot actions. */
547 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_SEND_FOR_SERVICING, ADI_SERVICE));
548 list.push_back(MakeDropDownListStringItem(this->vehicle_depot_name[this->vli.vtype], ADI_DEPOT));
549
550 return list;
551}
552
554static const Vehicle *_last_vehicle[2] = { nullptr, nullptr };
555
556void BaseVehicleListWindow::SortVehicleList()
557{
558 if (this->vehgroups.Sort()) return;
559
560 /* invalidate cached values for name sorter - vehicle names could change */
561 _last_vehicle[0] = _last_vehicle[1] = nullptr;
562}
563
564void DepotSortList(VehicleList *list)
565{
566 if (list->size() < 2) return;
567 std::sort(list->begin(), list->end(), &VehicleNumberSorter);
568}
569
578static void DrawVehicleProfitButton(TimerGameEconomy::Date age, Money display_profit_last_year, uint num_vehicles, int x, int y)
579{
580 SpriteID spr;
581
582 /* draw profit-based coloured icons */
583 if (age <= VEHICLE_PROFIT_MIN_AGE) {
584 spr = SPR_PROFIT_NA;
585 } else if (display_profit_last_year < 0) {
586 spr = SPR_PROFIT_NEGATIVE;
587 } else if (display_profit_last_year < VEHICLE_PROFIT_THRESHOLD * num_vehicles) {
588 spr = SPR_PROFIT_SOME;
589 } else {
590 spr = SPR_PROFIT_LOT;
591 }
592 DrawSprite(spr, PAL_NONE, x, y);
593}
594
596static const uint MAX_REFIT_CYCLE = 256;
597
607uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_cargo_type)
608{
609 v_from = v_from->GetFirstEnginePart();
610 v_for = v_for->GetFirstEnginePart();
611
612 /* Create a list of subtypes used by the various parts of v_for */
613 static std::vector<StringID> subtypes;
614 subtypes.clear();
615 for (; v_from != nullptr; v_from = v_from->HasArticulatedPart() ? v_from->GetNextArticulatedPart() : nullptr) {
616 const Engine *e_from = v_from->GetEngine();
617 if (!e_from->CanCarryCargo() || !e_from->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
618 include(subtypes, GetCargoSubtypeText(v_from));
619 }
620
621 uint8_t ret_refit_cyc = 0;
622 bool success = false;
623 if (!subtypes.empty()) {
624 /* Check whether any articulated part is refittable to 'dest_cargo_type' with a subtype listed in 'subtypes' */
625 for (Vehicle *v = v_for; v != nullptr; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr) {
626 const Engine *e = v->GetEngine();
627 if (!e->CanCarryCargo() || !e->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
628 if (!HasBit(e->info.refit_mask, dest_cargo_type) && v->cargo_type != dest_cargo_type) continue;
629
630 CargoType old_cargo_type = v->cargo_type;
631 uint8_t old_cargo_subtype = v->cargo_subtype;
632
633 /* Set the 'destination' cargo */
634 v->cargo_type = dest_cargo_type;
635
636 /* Cycle through the refits */
637 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
638 v->cargo_subtype = refit_cyc;
639
640 /* Make sure we don't pick up anything cached. */
641 v->First()->InvalidateNewGRFCache();
642 v->InvalidateNewGRFCache();
643
644 StringID subtype = GetCargoSubtypeText(v);
645 if (subtype == STR_EMPTY) break;
646
647 if (std::ranges::find(subtypes, subtype) == subtypes.end()) continue;
648
649 /* We found something matching. */
650 ret_refit_cyc = refit_cyc;
651 success = true;
652 break;
653 }
654
655 /* Reset the vehicle's cargo type */
656 v->cargo_type = old_cargo_type;
657 v->cargo_subtype = old_cargo_subtype;
658
659 /* Make sure we don't taint the vehicle. */
660 v->First()->InvalidateNewGRFCache();
661 v->InvalidateNewGRFCache();
662
663 if (success) break;
664 }
665 }
666
667 return ret_refit_cyc;
668}
669
673 uint8_t subtype;
675
681 inline bool operator == (const RefitOption &other) const
682 {
683 return other.cargo == this->cargo && other.string == this->string;
684 }
685};
686
687using RefitOptions = std::map<CargoType, std::vector<RefitOption>, CargoTypeComparator>;
688
698static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption *sel, uint pos, uint rows, uint delta, const Rect &r)
699{
700 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
701 uint current = 0;
702
703 bool rtl = _current_text_dir == TD_RTL;
704 uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
705 uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
706 PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
707
708 int iconleft = rtl ? ir.right - iconwidth : ir.left;
709 int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;
710 int iconinner = rtl ? ir.right - iconwidth : ir.left + iconwidth;
711
712 Rect tr = ir.Indent(iconwidth + WidgetDimensions::scaled.hsep_wide, rtl);
713
714 /* Draw the list of subtypes for each cargo, and find the selected refit option (by its position). */
715 for (const auto &pair : refits) {
716 bool has_subtypes = pair.second.size() > 1;
717 for (const RefitOption &refit : pair.second) {
718 if (current >= pos + rows) break;
719
720 /* Hide subtypes if selected cargo type does not match */
721 if ((sel == nullptr || sel->cargo != refit.cargo) && refit.subtype != UINT8_MAX) continue;
722
723 /* Refit options with a position smaller than pos don't have to be drawn. */
724 if (current < pos) {
725 current++;
726 continue;
727 }
728
729 if (has_subtypes) {
730 if (refit.subtype != UINT8_MAX) {
731 /* Draw tree lines */
732 int ycenter = tr.top + GetCharacterHeight(FS_NORMAL) / 2;
733 GfxDrawLine(iconcenter, tr.top - WidgetDimensions::scaled.matrix.top, iconcenter, (&refit == &pair.second.back()) ? ycenter : tr.top - WidgetDimensions::scaled.matrix.top + delta - 1, linecolour);
734 GfxDrawLine(iconcenter, ycenter, iconinner, ycenter, linecolour);
735 } else {
736 /* Draw expand/collapse icon */
737 DrawSprite((sel != nullptr && sel->cargo == refit.cargo) ? SPR_CIRCLE_UNFOLDED : SPR_CIRCLE_FOLDED, PAL_NONE, iconleft, tr.top + (GetCharacterHeight(FS_NORMAL) - iconheight) / 2);
738 }
739 }
740
741 TextColour colour = (sel != nullptr && sel->cargo == refit.cargo && sel->subtype == refit.subtype) ? TC_WHITE : TC_BLACK;
742 /* Get the cargo name. */
743 DrawString(tr, GetString(STR_JUST_STRING_STRING, CargoSpec::Get(refit.cargo)->name, refit.string), colour);
744
745 tr.top += delta;
746 current++;
747 }
748 }
749}
750
752struct RefitWindow : public Window {
753 const RefitOption *selected_refit = nullptr;
757 Scrollbar *vscroll = nullptr;
758 Scrollbar *hscroll = nullptr;
760 int sprite_left = 0;
761 int sprite_right = 0;
762 uint vehicle_margin = 0;
763 int click_x = 0;
765 uint8_t num_vehicles = 0;
766 bool auto_refit = false;
767
772 {
773 /* Store the currently selected RefitOption. */
774 std::optional<RefitOption> current_refit_option;
775 if (this->selected_refit != nullptr) current_refit_option = *(this->selected_refit);
776 this->selected_refit = nullptr;
777
778 this->refit_list.clear();
780
781 /* Check only the selected vehicles. */
782 VehicleSet vehicles_to_refit;
783 GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
784
785 do {
786 if (v->type == VEH_TRAIN && std::ranges::find(vehicles_to_refit, v->index) == vehicles_to_refit.end()) continue;
787 const Engine *e = v->GetEngine();
788 CargoTypes cmask = e->info.refit_mask;
789 VehicleCallbackMasks callback_mask = e->info.callback_mask;
790
791 /* Skip this engine if it does not carry anything */
792 if (!e->CanCarryCargo()) continue;
793 /* Skip this engine if we build the list for auto-refitting and engine doesn't allow it. */
794 if (this->auto_refit && !e->info.misc_flags.Test(EngineMiscFlag::AutoRefit)) continue;
795
796 /* Loop through all cargoes in the refit mask */
797 for (const auto &cs : _sorted_cargo_specs) {
798 CargoType cargo_type = cs->Index();
799 /* Skip cargo type if it's not listed */
800 if (!HasBit(cmask, cargo_type)) continue;
801
802 auto &list = this->refit_list[cargo_type];
803 bool first_vehicle = list.empty();
804 if (first_vehicle) {
805 /* Keeping the current subtype is always an option. It also serves as the option in case of no subtypes */
806 list.emplace_back(cargo_type, UINT8_MAX, STR_EMPTY);
807 }
808
809 /* Check the vehicle's callback mask for cargo suffixes.
810 * This is not supported for ordered refits, since subtypes only have a meaning
811 * for a specific vehicle at a specific point in time, which conflicts with shared orders,
812 * autoreplace, autorenew, clone, order restoration, ... */
813 if (this->order == INVALID_VEH_ORDER_ID && callback_mask.Test(VehicleCallbackMask::CargoSuffix)) {
814 /* Make a note of the original cargo type. It has to be
815 * changed to test the cargo & subtype... */
816 CargoType temp_cargo = v->cargo_type;
817 uint8_t temp_subtype = v->cargo_subtype;
818
819 v->cargo_type = cargo_type;
820
821 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
822 v->cargo_subtype = refit_cyc;
823
824 /* Make sure we don't pick up anything cached. */
827
828 StringID subtype = GetCargoSubtypeText(v);
829
830 if (first_vehicle) {
831 /* Append new subtype (don't add duplicates though) */
832 if (subtype == STR_EMPTY) break;
833
834 RefitOption option{cargo_type, static_cast<uint8_t>(refit_cyc), subtype};
835 include(list, option);
836 } else {
837 /* Intersect the subtypes of earlier vehicles with the subtypes of this vehicle */
838 if (subtype == STR_EMPTY) {
839 /* No more subtypes for this vehicle, delete all subtypes >= refit_cyc */
840 /* UINT8_MAX item is in front, other subtypes are sorted. So just truncate the list in the right spot */
841 for (uint i = 1; i < list.size(); i++) {
842 if (list[i].subtype >= refit_cyc) {
843 list.erase(list.begin() + i, list.end());
844 break;
845 }
846 }
847 break;
848 } else {
849 /* Check whether the subtype matches with the subtype of earlier vehicles. */
850 uint pos = 1;
851 while (pos < list.size() && list[pos].subtype != refit_cyc) pos++;
852 if (pos < list.size() && list[pos].string != subtype) {
853 /* String mismatch, remove item keeping the order */
854 list.erase(list.begin() + pos);
855 }
856 }
857 }
858 }
859
860 /* Reset the vehicle's cargo type */
861 v->cargo_type = temp_cargo;
862 v->cargo_subtype = temp_subtype;
863
864 /* And make sure we haven't tainted the cache */
867 }
868 }
869 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
870
871 /* Restore the previously selected RefitOption. */
872 if (current_refit_option.has_value()) {
873 for (const auto &pair : this->refit_list) {
874 for (const auto &refit : pair.second) {
875 if (refit.cargo == current_refit_option->cargo && refit.subtype == current_refit_option->subtype) {
876 this->selected_refit = &refit;
877 break;
878 }
879 }
880 if (this->selected_refit != nullptr) break;
881 }
882 }
883
884 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
885 }
886
891 {
892 size_t scroll_row = 0;
893 size_t rows = 0;
894 CargoType cargo = this->selected_refit == nullptr ? INVALID_CARGO : this->selected_refit->cargo;
895
896 for (const auto &pair : this->refit_list) {
897 if (pair.first == cargo) {
898 /* selected_refit points to an element in the vector so no need to search for it. */
899 scroll_row = rows + (this->selected_refit - pair.second.data());
900 rows += pair.second.size();
901 } else {
902 rows++; /* Unselected cargo type is collapsed into one row. */
903 }
904 }
905
906 this->vscroll->SetCount(rows);
907 this->vscroll->ScrollTowards(static_cast<int>(scroll_row));
908 }
909
914 void SetSelection(uint click_row)
915 {
916 uint row = 0;
917
918 for (const auto &pair : refit_list) {
919 for (const RefitOption &refit : pair.second) {
920 if (row == click_row) {
921 this->selected_refit = &refit;
922 return;
923 }
924 row++;
925 /* If this cargo type is not already selected then its subtypes are not visible, so skip the rest. */
926 if (this->selected_refit == nullptr || this->selected_refit->cargo != refit.cargo) break;
927 }
928 }
929
930 /* No selection made */
931 this->selected_refit = nullptr;
932 }
933
934 RefitWindow(WindowDesc &desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window(desc)
935 {
936 this->auto_refit = auto_refit;
937 this->order = order;
938 this->CreateNestedTree();
939
940 this->vscroll = this->GetScrollbar(WID_VR_SCROLLBAR);
941 this->hscroll = (v->IsGroundVehicle() ? this->GetScrollbar(WID_VR_HSCROLLBAR) : nullptr);
942 this->GetWidget<NWidgetCore>(WID_VR_SELECT_HEADER)->SetToolTip(STR_REFIT_TRAIN_LIST_TOOLTIP + v->type);
943 this->GetWidget<NWidgetCore>(WID_VR_MATRIX)->SetToolTip(STR_REFIT_TRAIN_LIST_TOOLTIP + v->type);
945 nwi->SetStringTip(STR_REFIT_TRAIN_REFIT_BUTTON + v->type, STR_REFIT_TRAIN_REFIT_TOOLTIP + v->type);
947 this->GetWidget<NWidgetCore>(WID_VR_VEHICLE_PANEL_DISPLAY)->SetToolTip((v->type == VEH_TRAIN) ? STR_REFIT_SELECT_VEHICLES_TOOLTIP : STR_NULL);
948
949 this->FinishInitNested(v->index);
950 this->owner = v->owner;
951
952 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
953 }
954
955 void OnInit() override
956 {
957 /* (Re)build the refit list */
959 }
960
961 void OnPaint() override
962 {
963 /* Determine amount of items for scroller. */
964 if (this->hscroll != nullptr) this->hscroll->SetCount(this->vehicle_width);
965
966 /* Calculate sprite position. */
968 int sprite_width = std::max(0, ((int)vehicle_panel_display->current_x - this->vehicle_width) / 2);
969 this->sprite_left = vehicle_panel_display->pos_x;
970 this->sprite_right = vehicle_panel_display->pos_x + vehicle_panel_display->current_x - 1;
971 if (_current_text_dir == TD_RTL) {
972 this->sprite_right -= sprite_width;
973 this->vehicle_margin = vehicle_panel_display->current_x - sprite_right;
974 } else {
975 this->sprite_left += sprite_width;
976 this->vehicle_margin = sprite_left;
977 }
978
979 this->DrawWidgets();
980 }
981
982 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
983 {
984 switch (widget) {
985 case WID_VR_MATRIX:
986 fill.height = resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
987 size.height = resize.height * 8;
988 break;
989
991 size.height = ScaleGUITrad(GetVehicleHeight(Vehicle::Get(this->window_number)->type));
992 break;
993
994 case WID_VR_INFO:
995 size.width = this->information_width + padding.height;
996 break;
997 }
998 }
999
1000 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1001 {
1002 if (widget == WID_VR_CAPTION) return GetString(STR_REFIT_CAPTION, Vehicle::Get(this->window_number)->index);
1003
1004 return this->Window::GetWidgetString(widget, stringid);
1005 }
1006
1013 std::string GetCapacityString(const RefitOption &option) const
1014 {
1016 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);
1017
1018 if (cost.Failed()) return {};
1019
1020 Money money = cost.GetCost();
1021 if (mail_capacity > 0) {
1022 if (this->order != INVALID_VEH_ORDER_ID) {
1023 /* No predictable cost */
1024 return GetString(STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity);
1025 }
1026
1027 if (money <= 0) {
1028 return GetString(STR_REFIT_NEW_CAPACITY_INCOME_FROM_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, -money);
1029 }
1030
1031 return GetString(STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, money);
1032 }
1033
1034 if (this->order != INVALID_VEH_ORDER_ID) {
1035 /* No predictable cost */
1036 return GetString(STR_PURCHASE_INFO_CAPACITY, option.cargo, refit_capacity, STR_EMPTY);
1037 }
1038
1039 if (money <= 0) {
1040 return GetString(STR_REFIT_NEW_CAPACITY_INCOME_FROM_REFIT, option.cargo, refit_capacity, -money);
1041 }
1042
1043 return GetString(STR_REFIT_NEW_CAPACITY_COST_OF_REFIT, option.cargo, refit_capacity, money);
1044 }
1045
1046 void DrawWidget(const Rect &r, WidgetID widget) const override
1047 {
1048 switch (widget) {
1051 DrawVehicleImage(v, r.WithX(this->sprite_left, this->sprite_right),
1052 VehicleID::Invalid(), EIT_IN_DETAILS, this->hscroll != nullptr ? this->hscroll->GetPosition() : 0);
1053
1054 /* Highlight selected vehicles. */
1055 if (this->order != INVALID_VEH_ORDER_ID) break;
1056 int x = 0;
1057 switch (v->type) {
1058 case VEH_TRAIN: {
1059 VehicleSet vehicles_to_refit;
1060 GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
1061
1062 int left = INT32_MIN;
1063 int width = 0;
1064
1065 /* Determine top & bottom position of the highlight.*/
1066 const int height = ScaleSpriteTrad(12);
1067 const int highlight_top = CentreBounds(r.top, r.bottom, height);
1068 const int highlight_bottom = highlight_top + height - 1;
1069
1070 for (Train *u = Train::From(v); u != nullptr; u = u->Next()) {
1071 /* Start checking. */
1072 const bool contained = std::ranges::find(vehicles_to_refit, u->index) != vehicles_to_refit.end();
1073 if (contained && left == INT32_MIN) {
1074 left = x - this->hscroll->GetPosition() + r.left + this->vehicle_margin;
1075 width = 0;
1076 }
1077
1078 /* Draw a selection. */
1079 if ((!contained || u->Next() == nullptr) && left != INT32_MIN) {
1080 if (u->Next() == nullptr && contained) {
1081 int current_width = u->GetDisplayImageWidth();
1082 width += current_width;
1083 x += current_width;
1084 }
1085
1086 int right = Clamp(left + width, 0, r.right);
1087 left = std::max(0, left);
1088
1089 if (_current_text_dir == TD_RTL) {
1090 right = r.Width() - left;
1091 left = right - width;
1092 }
1093
1094 if (left != right) {
1095 Rect hr = {left, highlight_top, right, highlight_bottom};
1097 }
1098
1099 left = INT32_MIN;
1100 }
1101
1102 int current_width = u->GetDisplayImageWidth();
1103 width += current_width;
1104 x += current_width;
1105 }
1106 break;
1107 }
1108
1109 default: break;
1110 }
1111 break;
1112 }
1113
1114 case WID_VR_MATRIX:
1115 DrawVehicleRefitWindow(this->refit_list, this->selected_refit, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->resize.step_height, r);
1116 break;
1117
1118 case WID_VR_INFO:
1119 if (this->selected_refit != nullptr) {
1120 std::string string = this->GetCapacityString(*this->selected_refit);
1121 if (!string.empty()) {
1123 }
1124 }
1125 break;
1126 }
1127 }
1128
1134 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1135 {
1136 switch (data) {
1137 case VIWD_AUTOREPLACE: // Autoreplace replaced the vehicle; selected_vehicle became invalid.
1138 case VIWD_CONSIST_CHANGED: { // The consist has changed; rebuild the entire list.
1139 /* Clear the selection. */
1141 this->selected_vehicle = v->index;
1142 this->num_vehicles = UINT8_MAX;
1143 [[fallthrough]];
1144 }
1145
1146 case 2: { // The vehicle selection has changed; rebuild the entire list.
1147 if (!gui_scope) break;
1148 this->BuildRefitList();
1149
1150 /* The vehicle width has changed too. */
1151 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1152 uint max_width = 0;
1153
1154 /* Check the width of all cargo information strings. */
1155 for (const auto &list : this->refit_list) {
1156 for (const RefitOption &refit : list.second) {
1157 std::string string = this->GetCapacityString(refit);
1158 if (!string.empty()) {
1159 Dimension dim = GetStringBoundingBox(string);
1160 max_width = std::max(dim.width, max_width);
1161 }
1162 }
1163 }
1164
1165 if (this->information_width < max_width) {
1166 this->information_width = max_width;
1167 this->ReInit();
1168 }
1169 [[fallthrough]];
1170 }
1171
1172 case 1: // A new cargo has been selected.
1173 if (!gui_scope) break;
1174 this->RefreshScrollbar();
1175 break;
1176 }
1177 }
1178
1179 int GetClickPosition(int click_x)
1180 {
1182 if (_current_text_dir == TD_RTL) click_x = matrix_widget->current_x - click_x;
1183 click_x -= this->vehicle_margin;
1184 if (this->hscroll != nullptr) click_x += this->hscroll->GetPosition();
1185
1186 return click_x;
1187 }
1188
1189 void SetSelectedVehicles(int drag_x)
1190 {
1191 drag_x = GetClickPosition(drag_x);
1192
1193 int left_x = std::min(this->click_x, drag_x);
1194 int right_x = std::max(this->click_x, drag_x);
1195 this->num_vehicles = 0;
1196
1198 /* Find the vehicle part that was clicked. */
1199 switch (v->type) {
1200 case VEH_TRAIN: {
1201 /* Don't select anything if we are not clicking in the vehicle. */
1202 if (left_x >= 0) {
1203 const Train *u = Train::From(v);
1204 bool start_counting = false;
1205 for (; u != nullptr; u = u->Next()) {
1206 int current_width = u->GetDisplayImageWidth();
1207 left_x -= current_width;
1208 right_x -= current_width;
1209
1210 if (left_x < 0 && !start_counting) {
1211 this->selected_vehicle = u->index;
1212 start_counting = true;
1213
1214 /* Count the first vehicle, even if articulated part */
1215 this->num_vehicles++;
1216 } else if (start_counting && !u->IsArticulatedPart()) {
1217 /* Do not count articulated parts */
1218 this->num_vehicles++;
1219 }
1220
1221 if (right_x < 0) break;
1222 }
1223 }
1224
1225 /* If the selection is not correct, clear it. */
1226 if (this->num_vehicles != 0) {
1227 if (_ctrl_pressed) this->num_vehicles = UINT8_MAX;
1228 break;
1229 }
1230 [[fallthrough]];
1231 }
1232
1233 default:
1234 /* Clear the selection. */
1235 this->selected_vehicle = v->index;
1236 this->num_vehicles = UINT8_MAX;
1237 break;
1238 }
1239 }
1240
1241 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1242 {
1243 switch (widget) {
1244 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1245 if (this->order != INVALID_VEH_ORDER_ID) break;
1247 this->click_x = GetClickPosition(pt.x - nwi->pos_x);
1248 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1250 if (!_ctrl_pressed) {
1252 } else {
1253 /* The vehicle selection has changed. */
1254 this->InvalidateData(2);
1255 }
1256 break;
1257 }
1258
1259 case WID_VR_MATRIX: { // listbox
1260 this->SetSelection(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_VR_MATRIX));
1261 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
1262 this->InvalidateData(1);
1263
1264 if (click_count == 1) break;
1265 [[fallthrough]];
1266 }
1267
1268 case WID_VR_REFIT: // refit button
1269 if (this->selected_refit != nullptr) {
1270 const Vehicle *v = Vehicle::Get(this->window_number);
1271
1272 if (this->order == INVALID_VEH_ORDER_ID) {
1273 bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
1274 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();
1275 } else {
1276 if (Command<Commands::OrderRefit>::Post(v->tile, v->index, this->order, this->selected_refit->cargo)) this->Close();
1277 }
1278 }
1279 break;
1280 }
1281 }
1282
1283 void OnMouseDrag(Point pt, WidgetID widget) override
1284 {
1285 switch (widget) {
1286 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1287 if (this->order != INVALID_VEH_ORDER_ID) break;
1289 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1291 break;
1292 }
1293 }
1294 }
1295
1296 void OnDragDrop(Point pt, WidgetID widget) override
1297 {
1298 switch (widget) {
1299 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1300 if (this->order != INVALID_VEH_ORDER_ID) break;
1302 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1303 this->InvalidateData(2);
1304 break;
1305 }
1306 }
1307 }
1308
1309 void OnResize() override
1310 {
1311 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1312 this->vscroll->SetCapacityFromWidget(this, WID_VR_MATRIX);
1313 if (this->hscroll != nullptr) this->hscroll->SetCapacityFromWidget(this, WID_VR_VEHICLE_PANEL_DISPLAY);
1314 }
1315};
1316
1317static constexpr std::initializer_list<NWidgetPart> _nested_vehicle_refit_widgets = {
1319 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1320 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VR_CAPTION),
1321 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1322 EndContainer(),
1323 /* Vehicle display + scrollbar. */
1328 EndContainer(),
1329 EndContainer(),
1330 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_VR_SELECT_HEADER), SetStringTip(STR_REFIT_TITLE), SetResize(1, 0),
1331 /* Matrix + scrollbar. */
1335 EndContainer(),
1336 NWidget(WWT_PANEL, COLOUR_GREY, WID_VR_INFO), SetMinimalTextLines(2, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(),
1338 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VR_REFIT), SetFill(1, 0), SetResize(1, 0),
1339 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1340 EndContainer(),
1341};
1342
1343static WindowDesc _vehicle_refit_desc(
1344 WDP_AUTO, "view_vehicle_refit", 240, 174,
1347 _nested_vehicle_refit_widgets
1348);
1349
1357void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
1358{
1360 RefitWindow *w = new RefitWindow(_vehicle_refit_desc, v, order, auto_refit);
1361 w->parent = parent;
1362}
1363
1372uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
1373{
1374 /* List of cargo types of this engine */
1375 CargoTypes cmask = GetUnionOfArticulatedRefitMasks(engine, false);
1376 /* List of cargo types available in this climate */
1377 CargoTypes lmask = _cargo_mask;
1378
1379 /* Draw nothing if the engine is not refittable */
1380 if (HasAtMostOneBit(cmask)) return y;
1381
1382 std::string str;
1383 if (cmask == lmask) {
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 if (CountBits(cmask ^ lmask) < CountBits(cmask) && CountBits(cmask ^ lmask) <= 7) {
1390 cmask ^= lmask;
1391 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_BUT, cmask);
1392 } else {
1393 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_JUST_CARGO_LIST, cmask);
1394 }
1395 }
1396
1397 return DrawStringMultiLine(left, right, y, INT32_MAX, str);
1398}
1399
1406{
1407 if (!EngInfo(v->engine_type)->callback_mask.Test(VehicleCallbackMask::CargoSuffix)) return STR_EMPTY;
1408 std::array<int32_t, 1> regs100;
1409 uint16_t cb = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v, regs100);
1410 if (v->GetGRF()->grf_version < 8 && cb == 0xFF) return STR_EMPTY;
1411 if (cb == CALLBACK_FAILED || cb == 0x400) return STR_EMPTY;
1412 if (cb == 0x40F) {
1413 return GetGRFStringID(v->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1414 }
1415 if (cb > 0x400) {
1417 return STR_EMPTY;
1418 }
1419 return GetGRFStringID(v->GetGRFID(), GRFSTR_MISC_GRF_TEXT + cb);
1420}
1421
1423static bool VehicleGroupLengthSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1424{
1425 return a.NumVehicles() < b.NumVehicles();
1426}
1427
1429static bool VehicleGroupTotalProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1430{
1431 return a.GetDisplayProfitThisYear() < b.GetDisplayProfitThisYear();
1432}
1433
1435static bool VehicleGroupTotalProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1436{
1437 return a.GetDisplayProfitLastYear() < b.GetDisplayProfitLastYear();
1438}
1439
1441static bool VehicleGroupAverageProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1442{
1443 return a.GetDisplayProfitThisYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitThisYear() * static_cast<uint>(a.NumVehicles());
1444}
1445
1447static bool VehicleGroupAverageProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1448{
1449 return a.GetDisplayProfitLastYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitLastYear() * static_cast<uint>(a.NumVehicles());
1450}
1451
1453static bool VehicleNumberSorter(const Vehicle * const &a, const Vehicle * const &b)
1454{
1455 return a->unitnumber < b->unitnumber;
1456}
1457
1459static bool VehicleNameSorter(const Vehicle * const &a, const Vehicle * const &b)
1460{
1461 static std::string last_name[2] = { {}, {} };
1462
1463 if (a != _last_vehicle[0]) {
1464 _last_vehicle[0] = a;
1465 last_name[0] = GetString(STR_VEHICLE_NAME, a->index);
1466 }
1467
1468 if (b != _last_vehicle[1]) {
1469 _last_vehicle[1] = b;
1470 last_name[1] = GetString(STR_VEHICLE_NAME, b->index);
1471 }
1472
1473 int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
1474 return (r != 0) ? r < 0: VehicleNumberSorter(a, b);
1475}
1476
1478static bool VehicleAgeSorter(const Vehicle * const &a, const Vehicle * const &b)
1479{
1480 auto r = a->age - b->age;
1481 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1482}
1483
1485static bool VehicleProfitThisYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1486{
1488 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1489}
1490
1492static bool VehicleProfitLastYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1493{
1495 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1496}
1497
1499static bool VehicleCargoSorter(const Vehicle * const &a, const Vehicle * const &b)
1500{
1501 const Vehicle *v;
1502 CargoArray diff{};
1503
1504 /* Append the cargo of the connected waggons */
1505 for (v = a; v != nullptr; v = v->Next()) diff[v->cargo_type] += v->cargo_cap;
1506 for (v = b; v != nullptr; v = v->Next()) diff[v->cargo_type] -= v->cargo_cap;
1507
1508 int r = 0;
1509 for (uint d : diff) {
1510 r = d;
1511 if (r != 0) break;
1512 }
1513
1514 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1515}
1516
1518static bool VehicleReliabilitySorter(const Vehicle * const &a, const Vehicle * const &b)
1519{
1520 int r = a->reliability - b->reliability;
1521 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1522}
1523
1525static bool VehicleMaxSpeedSorter(const Vehicle * const &a, const Vehicle * const &b)
1526{
1528 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1529}
1530
1532static bool VehicleModelSorter(const Vehicle * const &a, const Vehicle * const &b)
1533{
1534 int r = a->engine_type.base() - b->engine_type.base();
1535 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1536}
1537
1539static bool VehicleValueSorter(const Vehicle * const &a, const Vehicle * const &b)
1540{
1541 const Vehicle *u;
1542 Money diff = 0;
1543
1544 for (u = a; u != nullptr; u = u->Next()) diff += u->value;
1545 for (u = b; u != nullptr; u = u->Next()) diff -= u->value;
1546
1547 int r = ClampTo<int32_t>(diff);
1548 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1549}
1550
1552static bool VehicleLengthSorter(const Vehicle * const &a, const Vehicle * const &b)
1553{
1555 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1556}
1557
1559static bool VehicleTimeToLiveSorter(const Vehicle * const &a, const Vehicle * const &b)
1560{
1561 int r = ClampTo<int32_t>((a->max_age - a->age) - (b->max_age - b->age));
1562 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1563}
1564
1566static bool VehicleTimetableDelaySorter(const Vehicle * const &a, const Vehicle * const &b)
1567{
1568 int r = a->lateness_counter - b->lateness_counter;
1569 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1570}
1571
1572void InitializeGUI()
1573{
1574 _grouping = {};
1575 _sorting = {};
1576}
1577
1584static inline void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
1585{
1586 Window *w = FindWindowById(window_class, from_index);
1587 if (w != nullptr) {
1588 /* Update window_number */
1589 w->window_number = to_index;
1590 if (w->viewport != nullptr) w->viewport->follow_vehicle = to_index;
1591
1592 /* Update vehicle drag data */
1593 if (_thd.window_class == window_class && _thd.window_number == from_index) {
1594 _thd.window_number = to_index;
1595 }
1596
1597 /* Notify the window. */
1599 }
1600}
1601
1608{
1609 ChangeVehicleWindow(WC_VEHICLE_VIEW, from_index, to_index);
1610 ChangeVehicleWindow(WC_VEHICLE_ORDERS, from_index, to_index);
1611 ChangeVehicleWindow(WC_VEHICLE_REFIT, from_index, to_index);
1612 ChangeVehicleWindow(WC_VEHICLE_DETAILS, from_index, to_index);
1613 ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index);
1614}
1615
1616static constexpr std::initializer_list<NWidgetPart> _nested_vehicle_list = {
1618 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1620 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VL_CAPTION),
1623 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_ORDER_VIEW), SetMinimalSize(61, 14), SetStringTip(STR_GOTO_ORDER_VIEW, STR_GOTO_ORDER_VIEW_TOOLTIP),
1624 EndContainer(),
1625 EndContainer(),
1626 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1627 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1628 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1629 EndContainer(),
1630
1633 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_VL_GROUP_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
1634 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_SORT_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1635 EndContainer(),
1637 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_GROUP_BY_PULLDOWN), SetMinimalSize(0, 12), SetFill(1, 1), SetToolTip(STR_TOOLTIP_GROUP_ORDER),
1638 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_SORT_BY_PULLDOWN), SetMinimalSize(0, 12), SetFill(1, 1), SetToolTip(STR_TOOLTIP_SORT_CRITERIA),
1639 EndContainer(),
1641 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1644 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_FILTER_BY_CARGO), SetMinimalSize(0, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1645 EndContainer(),
1646 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1647 EndContainer(),
1648 EndContainer(),
1649 EndContainer(),
1650
1654 EndContainer(),
1655
1657 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_VL_HIDE_BUTTONS),
1660 SetToolTip(STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
1661 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetResize(1, 0), SetFill(1, 1), EndContainer(),
1663 SetStringTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
1664 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VL_STOP_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1665 SetSpriteTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
1666 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VL_START_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1667 SetSpriteTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
1668 EndContainer(),
1669 /* Widget to be shown for other companies hiding the previous 5 widgets. */
1670 NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1671 EndContainer(),
1672 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1673 EndContainer(),
1674};
1675
1676static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, uint order_arrow_width, VehicleOrderID start)
1677{
1678 auto orders = v->Orders();
1679 if (orders.empty()) return;
1680
1681 bool rtl = _current_text_dir == TD_RTL;
1682 int l_offset = rtl ? 0 : order_arrow_width;
1683 int r_offset = rtl ? order_arrow_width : 0;
1684 int i = 0;
1685 VehicleOrderID oid = start;
1686
1687 do {
1688 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, FS_SMALL);
1689
1690 if (orders[oid].IsType(OT_GOTO_STATION)) {
1691 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, orders[oid].GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
1692
1694 if (++i == 4) break;
1695 }
1696
1697 oid = v->orders->GetNext(oid);
1698 } while (oid != start);
1699}
1700
1709static void DrawSmallOrderList(const OrderList *orderlist, int left, int right, int y, uint order_arrow_width)
1710{
1711 if (orderlist == nullptr) return;
1712
1713 bool rtl = _current_text_dir == TD_RTL;
1714 int l_offset = rtl ? 0 : order_arrow_width;
1715 int r_offset = rtl ? order_arrow_width : 0;
1716 int i = 0;
1717
1718 for (const Order &order : orderlist->GetOrders()) {
1719 if (order.IsType(OT_GOTO_STATION)) {
1720 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order.GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
1721
1723 if (++i == 4) break;
1724 }
1725 }
1726}
1727
1736void DrawVehicleImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip)
1737{
1738 switch (v->type) {
1739 case VEH_TRAIN: DrawTrainImage(Train::From(v), r, selection, image_type, skip); break;
1740 case VEH_ROAD: DrawRoadVehImage(v, r, selection, image_type, skip); break;
1741 case VEH_SHIP: DrawShipImage(v, r, selection, image_type); break;
1742 case VEH_AIRCRAFT: DrawAircraftImage(v, r, selection, image_type); break;
1743 default: NOT_REACHED();
1744 }
1745}
1746
1753uint GetVehicleListHeight(VehicleType type, uint divisor)
1754{
1755 /* Name + vehicle + profit */
1756 uint base = ScaleGUITrad(GetVehicleHeight(type)) + 2 * GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.matrix.Vertical();
1757 /* Drawing of the 4 small orders + profit*/
1758 if (type >= VEH_SHIP) base = std::max(base, 6U * GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.matrix.Vertical());
1759
1760 if (divisor == 1) return base;
1761
1762 /* Make sure the height is dividable by divisor */
1763 uint rem = base % divisor;
1764 return base + (rem == 0 ? 0 : divisor - rem);
1765}
1766
1772static int GetUnitNumberWidth(int digits)
1773{
1774 return GetStringBoundingBox(GetString(STR_JUST_COMMA, GetParamMaxDigits(digits))).width;
1775}
1776
1783void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
1784{
1785 Rect ir = r.WithHeight(line_height).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
1786 bool rtl = _current_text_dir == TD_RTL;
1787
1788 Dimension profit = GetSpriteSize(SPR_PROFIT_LOT);
1789 int text_offset = std::max<int>(profit.width, GetUnitNumberWidth(this->unitnumber_digits)) + WidgetDimensions::scaled.hsep_normal;
1790 Rect tr = ir.Indent(text_offset, rtl);
1791
1792 bool show_orderlist = this->vli.vtype >= VEH_SHIP;
1793 Rect olr = ir.Indent(std::max(ScaleGUITrad(100) + text_offset, ir.Width() / 2), rtl);
1794
1795 int image_left = (rtl && show_orderlist) ? olr.right : tr.left;
1796 int image_right = (!rtl && show_orderlist) ? olr.left : tr.right;
1797
1798 int vehicle_button_x = rtl ? ir.right - profit.width : ir.left;
1799
1800 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->vehgroups);
1801 for (auto it = first; it != last; ++it) {
1802 const GUIVehicleGroup &vehgroup = *it;
1803
1804 DrawString(tr.left, tr.right, ir.bottom - GetCharacterHeight(FS_SMALL) - WidgetDimensions::scaled.framerect.bottom,
1805 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_LIST_PROFIT_THIS_PERIOD_LAST_PERIOD : STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR,
1806 vehgroup.GetDisplayProfitThisYear(),
1807 vehgroup.GetDisplayProfitLastYear()));
1808
1809 DrawVehicleProfitButton(vehgroup.GetOldestVehicleAge(), vehgroup.GetDisplayProfitLastYear(), vehgroup.NumVehicles(), vehicle_button_x, ir.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal);
1810
1811 switch (this->grouping) {
1812 case GB_NONE: {
1813 const Vehicle *v = vehgroup.GetSingleVehicle();
1814
1816 DrawSprite(SPR_WARNING_SIGN, PAL_NONE, vehicle_button_x, ir.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal + profit.height);
1817 }
1818
1819 DrawVehicleImage(v, ir.WithX(image_left, image_right), selected_vehicle, EIT_IN_LIST, 0);
1820
1821 if (_settings_client.gui.show_cargo_in_vehicle_lists) {
1822 /* Get the cargoes the vehicle can carry */
1823 CargoTypes vehicle_cargoes = 0;
1824
1825 for (auto u = v; u != nullptr; u = u->Next()) {
1826 if (u->cargo_cap == 0) continue;
1827
1828 SetBit(vehicle_cargoes, u->cargo_type);
1829 }
1830
1831 if (!v->name.empty()) {
1832 /* The vehicle got a name so we will print it and the cargoes */
1833 DrawString(tr.left, tr.right, ir.top,
1834 GetString(STR_VEHICLE_LIST_NAME_AND_CARGO, STR_VEHICLE_NAME, v->index, STR_VEHICLE_LIST_CARGO, vehicle_cargoes),
1835 TC_BLACK, SA_LEFT, false, FS_SMALL);
1836 } else if (v->group_id != DEFAULT_GROUP) {
1837 /* The vehicle has no name, but is member of a group, so print group name and the cargoes */
1838 DrawString(tr.left, tr.right, ir.top,
1839 GetString(STR_VEHICLE_LIST_NAME_AND_CARGO, STR_GROUP_NAME, v->group_id, STR_VEHICLE_LIST_CARGO, vehicle_cargoes),
1840 TC_BLACK, SA_LEFT, false, FS_SMALL);
1841 } else {
1842 /* The vehicle has no name, and is not a member of a group, so just print the cargoes */
1843 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_LIST_CARGO, vehicle_cargoes), TC_BLACK, SA_LEFT, false, FS_SMALL);
1844 }
1845 } else if (!v->name.empty()) {
1846 /* The vehicle got a name so we will print it */
1847 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_NAME, v->index), TC_BLACK, SA_LEFT, false, FS_SMALL);
1848 } else if (v->group_id != DEFAULT_GROUP) {
1849 /* The vehicle has no name, but is member of a group, so print group name */
1850 DrawString(tr.left, tr.right, ir.top, GetString(STR_GROUP_NAME, v->group_id), TC_BLACK, SA_LEFT, false, FS_SMALL);
1851 }
1852
1853 if (show_orderlist) DrawSmallOrderList(v, olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width, v->cur_real_order_index);
1854
1855 TextColour tc;
1856 if (v->IsChainInDepot()) {
1857 tc = TC_BLUE;
1858 } else {
1859 tc = (v->age > v->max_age - CalendarTime::DAYS_IN_LEAP_YEAR) ? TC_RED : TC_BLACK;
1860 }
1861
1862 DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, v->unitnumber), tc);
1863 break;
1864 }
1865
1866 case GB_SHARED_ORDERS:
1867 assert(vehgroup.NumVehicles() > 0);
1868
1869 for (int i = 0; i < static_cast<int>(vehgroup.NumVehicles()); ++i) {
1870 if (image_left + WidgetDimensions::scaled.hsep_wide * i >= image_right) break; // Break if there is no more space to draw any more vehicles anyway.
1871 DrawVehicleImage(vehgroup.vehicles_begin[i], ir.WithX(image_left + WidgetDimensions::scaled.hsep_wide * i, image_right), selected_vehicle, EIT_IN_LIST, 0);
1872 }
1873
1874 if (show_orderlist) DrawSmallOrderList(vehgroup.vehicles_begin[0]->orders, olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width);
1875
1876 DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, vehgroup.NumVehicles()), TC_BLACK);
1877 break;
1878
1879 default:
1880 NOT_REACHED();
1881 }
1882
1883 ir = ir.Translate(0, line_height);
1884 }
1885}
1886
1887void BaseVehicleListWindow::UpdateSortingFromGrouping()
1888{
1889 /* Set up sorting. Make the window-specific _sorting variable
1890 * point to the correct global _sorting struct so we are freed
1891 * from having conditionals during window operation */
1892 switch (this->vli.vtype) {
1893 case VEH_TRAIN: this->sorting = &_sorting[this->grouping].train; break;
1894 case VEH_ROAD: this->sorting = &_sorting[this->grouping].roadveh; break;
1895 case VEH_SHIP: this->sorting = &_sorting[this->grouping].ship; break;
1896 case VEH_AIRCRAFT: this->sorting = &_sorting[this->grouping].aircraft; break;
1897 default: NOT_REACHED();
1898 }
1899 this->vehgroups.SetSortFuncs(this->GetVehicleSorterFuncs());
1900 this->vehgroups.SetListing(*this->sorting);
1901 this->vehgroups.ForceRebuild();
1902 this->vehgroups.NeedResort();
1903}
1904
1905void BaseVehicleListWindow::UpdateVehicleGroupBy(GroupBy group_by)
1906{
1907 if (this->grouping != group_by) {
1908 /* Save the old sorting option, so that if we change the grouping option back later on,
1909 * UpdateSortingFromGrouping() will automatically restore the saved sorting option. */
1910 *this->sorting = this->vehgroups.GetListing();
1911
1912 this->grouping = group_by;
1913 _grouping[this->vli.type][this->vli.vtype] = group_by;
1914 this->UpdateSortingFromGrouping();
1915 }
1916}
1917
1922struct VehicleListWindow : public BaseVehicleListWindow {
1923private:
1929
1935
1936public:
1938 {
1939 this->CreateNestedTree();
1940
1941 this->GetWidget<NWidgetStacked>(WID_VL_FILTER_BY_CARGO_SEL)->SetDisplayedPlane((this->vli.type == VL_SHARED_ORDERS) ? SZSP_NONE : 0);
1942
1943 this->vscroll = this->GetScrollbar(WID_VL_SCROLLBAR);
1944
1945 /* Set up the window widgets */
1946 this->GetWidget<NWidgetCore>(WID_VL_LIST)->SetToolTip(STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + this->vli.vtype);
1947
1949 if (this->vli.type == VL_SHARED_ORDERS) {
1950 this->GetWidget<NWidgetCore>(WID_VL_CAPTION_SHARED_ORDERS)->SetString(STR_VEHICLE_LIST_SHARED_ORDERS_LIST_CAPTION);
1951 /* If we are in the shared orders window, then disable the group-by dropdown menu.
1952 * Remove this when the group-by dropdown menu has another option apart from grouping by shared orders. */
1956 } else {
1957 this->GetWidget<NWidgetCore>(WID_VL_CAPTION)->SetString(STR_VEHICLE_LIST_TRAIN_CAPTION + this->vli.vtype);
1959 }
1960
1962 if (this->vli.company != OWNER_NONE) this->owner = this->vli.company;
1963
1964 this->BuildVehicleList();
1965 this->SortVehicleList();
1966 }
1967
1970 {
1971 *this->sorting = this->vehgroups.GetListing();
1972 }
1973
1974 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1975 {
1976 switch (widget) {
1977 case WID_VL_LIST:
1978 fill.height = resize.height = GetVehicleListHeight(this->vli.vtype, 1);
1979
1980 switch (this->vli.vtype) {
1981 case VEH_TRAIN:
1982 case VEH_ROAD:
1983 size.height = 6 * resize.height;
1984 break;
1985 case VEH_SHIP:
1986 case VEH_AIRCRAFT:
1987 size.height = 4 * resize.height;
1988 break;
1989 default: NOT_REACHED();
1990 }
1991 break;
1992
1993 case WID_VL_SORT_ORDER: {
1995 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1996 d.height += padding.height;
1997 size = maxdim(size, d);
1998 break;
1999 }
2000
2002 size.width = GetStringListWidth(this->vehicle_group_by_names) + padding.width;
2003 break;
2004
2006 size.width = GetStringListWidth(this->vehicle_group_none_sorter_names_calendar);
2007 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_none_sorter_names_wallclock));
2008 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_calendar));
2009 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_wallclock));
2010 size.width += padding.width;
2011 break;
2012
2014 size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList(true)).width + padding.width);
2015 break;
2016
2018 Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false, true);
2019 d.height += padding.height;
2020 d.width += padding.width;
2021 size = maxdim(size, d);
2022 break;
2023 }
2024 }
2025 }
2026
2027 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2028 {
2029 switch (widget) {
2031 return GetString(STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vli.vtype);
2032
2034 return GetString(std::data(this->vehicle_group_by_names)[this->grouping]);
2035
2037 return GetString(this->GetVehicleSorterNames()[this->vehgroups.SortType()]);
2038
2040 return GetString(this->GetCargoFilterLabel(this->cargo_filter_criteria));
2041
2042 case WID_VL_CAPTION:
2044 switch (this->vli.type) {
2045 case VL_SHARED_ORDERS: // Shared Orders
2046 return GetString(stringid, this->vehicles.size());
2047
2048 case VL_STANDARD: // Company Name
2049 return GetString(stringid, STR_COMPANY_NAME, this->vli.ToCompanyID(), std::monostate{}, this->vehicles.size());
2050
2051 case VL_STATION_LIST: // Station/Waypoint Name
2052 return GetString(stringid, Station::IsExpected(BaseStation::Get(this->vli.ToStationID())) ? STR_STATION_NAME : STR_WAYPOINT_NAME, this->vli.ToStationID(), std::monostate{}, this->vehicles.size());
2053
2054 case VL_DEPOT_LIST:
2055 return GetString(stringid, STR_DEPOT_CAPTION, this->vli.vtype, this->vli.ToDestinationID(), this->vehicles.size());
2056
2057 default: NOT_REACHED();
2058 }
2059 }
2060
2061 default:
2062 return this->Window::GetWidgetString(widget, stringid);
2063 }
2064 }
2065
2066 void DrawWidget(const Rect &r, WidgetID widget) const override
2067 {
2068 switch (widget) {
2069 case WID_VL_SORT_ORDER:
2070 /* draw arrow pointing up/down for ascending/descending sorting */
2071 this->DrawSortButtonState(widget, this->vehgroups.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
2072 break;
2073
2074 case WID_VL_LIST:
2075 this->DrawVehicleListItems(VehicleID::Invalid(), this->resize.step_height, r);
2076 break;
2077 }
2078 }
2079
2080 void OnPaint() override
2081 {
2082 this->BuildVehicleList();
2083 this->SortVehicleList();
2084
2085 if (this->vehicles.empty() && this->IsWidgetLowered(WID_VL_MANAGE_VEHICLES_DROPDOWN)) {
2087 }
2088
2089 /* Hide the widgets that we will not use in this window
2090 * Some windows contains actions only fit for the owner */
2091 int plane_to_show = (this->owner == _local_company) ? BP_SHOW_BUTTONS : BP_HIDE_BUTTONS;
2093 if (plane_to_show != nwi->shown_plane) {
2094 nwi->SetDisplayedPlane(plane_to_show);
2095 nwi->SetDirty(this);
2096 }
2097 if (this->owner == _local_company) {
2099 this->SetWidgetsDisabledState(this->vehicles.empty(),
2103 }
2104
2105 this->DrawWidgets();
2106 }
2107
2108 bool last_overlay_state = false;
2109 void OnMouseLoop() override
2110 {
2111 if (last_overlay_state != ShowCargoIconOverlay()) {
2112 last_overlay_state = ShowCargoIconOverlay();
2113 this->SetDirty();
2114 }
2115 }
2116
2117 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2118 {
2119 switch (widget) {
2120 case WID_VL_ORDER_VIEW: // Open the shared orders window
2121 assert(this->vli.type == VL_SHARED_ORDERS);
2122 assert(!this->vehicles.empty());
2123 ShowOrdersWindow(this->vehicles[0]);
2124 break;
2125
2126 case WID_VL_SORT_ORDER: // Flip sorting method ascending/descending
2127 this->vehgroups.ToggleSortOrder();
2128 this->SetDirty();
2129 break;
2130
2131 case WID_VL_GROUP_BY_PULLDOWN: // Select sorting criteria dropdown menu
2132 ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_VL_GROUP_BY_PULLDOWN, 0, 0);
2133 return;
2134
2135 case WID_VL_SORT_BY_PULLDOWN: // Select sorting criteria dropdown menu
2136 ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0,
2137 (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
2138 return;
2139
2140 case WID_VL_FILTER_BY_CARGO: { // Cargo filter dropdown
2141 static std::string cargo_filter;
2142 ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget, 0, DropDownOption::Filterable, &cargo_filter);
2143 break;
2144 }
2145
2146 case WID_VL_LIST: { // Matrix to show vehicles
2147 auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_VL_LIST);
2148 if (it == this->vehgroups.end()) return; // click out of list bound
2149
2150 const GUIVehicleGroup &vehgroup = *it;
2151 switch (this->grouping) {
2152 case GB_NONE: {
2153 const Vehicle *v = vehgroup.GetSingleVehicle();
2154 if (!VehicleClicked(v)) {
2155 if (_ctrl_pressed) {
2157 } else {
2159 }
2160 }
2161 break;
2162 }
2163
2164 case GB_SHARED_ORDERS: {
2165 assert(vehgroup.NumVehicles() > 0);
2166 if (!VehicleClicked(vehgroup)) {
2167 const Vehicle *v = vehgroup.vehicles_begin[0];
2168 if (_ctrl_pressed) {
2169 ShowOrdersWindow(v);
2170 } else {
2171 if (vehgroup.NumVehicles() == 1) {
2173 } else {
2174 ShowVehicleListWindow(v);
2175 }
2176 }
2177 }
2178 break;
2179 }
2180
2181 default: NOT_REACHED();
2182 }
2183
2184 break;
2185 }
2186
2188 ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
2189 break;
2190
2193 break;
2194 }
2195
2196 case WID_VL_STOP_ALL:
2197 case WID_VL_START_ALL:
2198 Command<Commands::MassStartStop>::Post(TileIndex{}, widget == WID_VL_START_ALL, true, this->vli);
2199 break;
2200 }
2201 }
2202
2203 void OnDropdownSelect(WidgetID widget, int index, int) override
2204 {
2205 switch (widget) {
2207 this->UpdateVehicleGroupBy(static_cast<GroupBy>(index));
2208 break;
2209
2211 this->vehgroups.SetSortType(index);
2212 break;
2213
2215 this->SetCargoFilter(index);
2216 break;
2217
2219 assert(!this->vehicles.empty());
2220
2221 switch (index) {
2222 case ADI_REPLACE: // Replace window
2224 break;
2225 case ADI_SERVICE: // Send for servicing
2226 case ADI_DEPOT: // Send to Depots
2227 Command<Commands::SendVehicleToDepot>::Post(GetCmdSendToDepotMsg(this->vli.vtype), VehicleID::Invalid(), (index == ADI_SERVICE ? DepotCommandFlag::Service : DepotCommandFlags{}) | DepotCommandFlag::MassSend, this->vli);
2228 break;
2229
2230 case ADI_CREATE_GROUP: // Create group
2231 Command<Commands::AddVehicleToGroup>::Post(CcAddVehicleNewGroup, NEW_GROUP, VehicleID::Invalid(), false, this->vli);
2232 break;
2233
2234 default: NOT_REACHED();
2235 }
2236 break;
2237
2238 default: NOT_REACHED();
2239 }
2240 this->SetDirty();
2241 }
2242
2243 void OnGameTick() override
2244 {
2245 if (this->vehgroups.NeedResort()) {
2246 StationID station = (this->vli.type == VL_STATION_LIST) ? this->vli.ToStationID() : StationID::Invalid();
2247
2248 Debug(misc, 3, "Periodic resort {} list company {} at station {}", this->vli.vtype, this->owner, station);
2249 this->SetDirty();
2250 }
2251 }
2252
2253 void OnResize() override
2254 {
2255 this->vscroll->SetCapacityFromWidget(this, WID_VL_LIST);
2256 }
2257
2263 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2264 {
2265 if (!gui_scope && HasBit(data, 31) && this->vli.type == VL_SHARED_ORDERS) {
2266 /* Needs to be done in command-scope, so everything stays valid */
2267 this->vli.SetIndex(GB(data, 0, 20));
2268 this->window_number = this->vli.ToWindowNumber();
2269 this->vehgroups.ForceRebuild();
2270 return;
2271 }
2272
2273 if (data == 0) {
2274 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
2275 this->vehgroups.ForceRebuild();
2276 } else {
2277 this->vehgroups.ForceResort();
2278 }
2279 }
2280};
2281
2282static WindowDesc _vehicle_list_desc[] = {
2283 {
2284 WDP_AUTO, "list_vehicles_train", 325, 246,
2286 {},
2287 _nested_vehicle_list
2288 },
2289 {
2290 WDP_AUTO, "list_vehicles_roadveh", 260, 246,
2292 {},
2293 _nested_vehicle_list
2294 },
2295 {
2296 WDP_AUTO, "list_vehicles_ship", 260, 246,
2298 {},
2299 _nested_vehicle_list
2300 },
2301 {
2302 WDP_AUTO, "list_vehicles_aircraft", 260, 246,
2304 {},
2305 _nested_vehicle_list
2306 }
2307};
2308
2309static void ShowVehicleListWindowLocal(CompanyID company, VehicleListType vlt, VehicleType vehicle_type, uint32_t unique_number)
2310{
2311 if (!Company::IsValidID(company) && company != OWNER_NONE) return;
2312
2313 assert(vehicle_type < std::size(_vehicle_list_desc));
2314 VehicleListIdentifier vli(vlt, vehicle_type, company, unique_number);
2315 AllocateWindowDescFront<VehicleListWindow>(_vehicle_list_desc[vehicle_type], vli.ToWindowNumber(), vli);
2316}
2317
2318void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type)
2319{
2320 /* If _settings_client.gui.advanced_vehicle_list > 1, display the Advanced list
2321 * if _settings_client.gui.advanced_vehicle_list == 1, display Advanced list only for local company
2322 * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
2323 */
2324
2325 if ((_settings_client.gui.advanced_vehicle_list > (uint)(company != _local_company)) != _ctrl_pressed) {
2326 ShowCompanyGroup(company, vehicle_type);
2327 } else {
2328 ShowVehicleListWindowLocal(company, VL_STANDARD, vehicle_type, company.base());
2329 }
2330}
2331
2332void ShowVehicleListWindow(const Vehicle *v)
2333{
2334 ShowVehicleListWindowLocal(v->owner, VL_SHARED_ORDERS, v->type, v->FirstShared()->index.base());
2335}
2336
2337void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station)
2338{
2339 ShowVehicleListWindowLocal(company, VL_STATION_LIST, vehicle_type, station.base());
2340}
2341
2342void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
2343{
2344 ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, GetDepotDestinationIndex(depot_tile).base());
2345}
2346
2347
2348/* Unified vehicle GUI - Vehicle Details Window */
2349
2354
2356static constexpr std::initializer_list<NWidgetPart> _nested_nontrain_vehicle_details_widgets = {
2358 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2359 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VD_CAPTION),
2360 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2361 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2362 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2363 EndContainer(),
2364 NWidget(WWT_PANEL, COLOUR_GREY, WID_VD_TOP_DETAILS), SetMinimalSize(405, 42), SetResize(1, 0), EndContainer(),
2372 SetStringTip(STR_EMPTY, STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP),
2374 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2375 EndContainer(),
2376};
2377
2379static constexpr std::initializer_list<NWidgetPart> _nested_train_vehicle_details_widgets = {
2381 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2382 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VD_CAPTION), SetStringTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2383 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2384 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2385 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2386 EndContainer(),
2387 NWidget(WWT_PANEL, COLOUR_GREY, WID_VD_TOP_DETAILS), SetResize(1, 0), SetMinimalSize(405, 42), EndContainer(),
2391 EndContainer(),
2398 SetStringTip(STR_EMPTY, STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP),
2400 EndContainer(),
2403 SetStringTip(STR_VEHICLE_DETAIL_TAB_CARGO, STR_VEHICLE_DETAILS_TRAIN_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2405 SetStringTip(STR_VEHICLE_DETAIL_TAB_INFORMATION, STR_VEHICLE_DETAILS_TRAIN_INFORMATION_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2407 SetStringTip(STR_VEHICLE_DETAIL_TAB_CAPACITIES, STR_VEHICLE_DETAILS_TRAIN_CAPACITIES_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2409 SetStringTip(STR_VEHICLE_DETAIL_TAB_TOTAL_CARGO, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2410 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2411 EndContainer(),
2412};
2413
2414
2416extern void DrawTrainDetails(const Train *v, const Rect &r, int vscroll_pos, uint16_t vscroll_cap, TrainDetailsWindowTabs det_tab);
2417extern void DrawRoadVehDetails(const Vehicle *v, const Rect &r);
2418extern void DrawShipDetails(const Vehicle *v, const Rect &r);
2419extern void DrawAircraftDetails(const Aircraft *v, const Rect &r);
2420
2421static const StringID _service_interval_dropdown_calendar[] = {
2422 STR_VEHICLE_DETAILS_DEFAULT,
2423 STR_VEHICLE_DETAILS_DAYS,
2424 STR_VEHICLE_DETAILS_PERCENT,
2425};
2426
2427static const StringID _service_interval_dropdown_wallclock[] = {
2428 STR_VEHICLE_DETAILS_DEFAULT,
2429 STR_VEHICLE_DETAILS_MINUTES,
2430 STR_VEHICLE_DETAILS_PERCENT,
2431};
2432
2436 Scrollbar *vscroll = nullptr;
2437
2444 {
2446
2447 this->CreateNestedTree();
2448 this->vscroll = (v->type == VEH_TRAIN ? this->GetScrollbar(WID_VD_SCROLLBAR) : nullptr);
2449 this->FinishInitNested(window_number);
2450
2451 this->owner = v->owner;
2452 }
2453
2459 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2460 {
2461 if (data == VIWD_AUTOREPLACE) {
2462 /* Autoreplace replaced the vehicle.
2463 * Nothing to do for this window. */
2464 return;
2465 }
2466 if (!gui_scope) return;
2467 const Vehicle *v = Vehicle::Get(this->window_number);
2468 if (v->type == VEH_ROAD) {
2470 uint aimed_height = this->GetRoadVehDetailsHeight(v);
2471 /* If the number of articulated parts changes, the size of the window must change too. */
2472 if (aimed_height != nwid_info->current_y) {
2473 this->ReInit();
2474 }
2475 }
2476 }
2477
2484 {
2485 uint desired_height;
2486 if (v->HasArticulatedPart()) {
2487 /* An articulated RV has its text drawn under the sprite instead of after it, hence 15 pixels extra. */
2488 desired_height = ScaleGUITrad(15) + 3 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2;
2489 /* Add space for the cargo amount for each part. */
2490 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
2491 if (u->cargo_cap != 0) desired_height += GetCharacterHeight(FS_NORMAL);
2492 }
2493 } else {
2494 desired_height = 4 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2;
2495 }
2496 return desired_height;
2497 }
2498
2499 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2500 {
2501 switch (widget) {
2502 case WID_VD_TOP_DETAILS: {
2503 Dimension dim = { 0, 0 };
2504 size.height = 4 * GetCharacterHeight(FS_NORMAL) + padding.height;
2505
2506 uint64_t max_value = GetParamMaxValue(INT16_MAX);
2507 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_MAX_SPEED, max_value)));
2508 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, max_value, max_value, max_value)));
2509 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE, max_value, max_value, max_value, max_value)));
2510 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE, max_value, max_value, max_value)));
2511 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD_MIN_PERFORMANCE, max_value, max_value, max_value)));
2512 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS, max_value, max_value)));
2513 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)));
2514 size.width = dim.width + padding.width;
2515 break;
2516 }
2517
2518 case WID_VD_MIDDLE_DETAILS: {
2519 const Vehicle *v = Vehicle::Get(this->window_number);
2520 switch (v->type) {
2521 case VEH_ROAD:
2522 size.height = this->GetRoadVehDetailsHeight(v) + padding.height;
2523 break;
2524
2525 case VEH_SHIP:
2526 size.height = 4 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2527 break;
2528
2529 case VEH_AIRCRAFT:
2530 size.height = 5 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2531 break;
2532
2533 default:
2534 NOT_REACHED(); // Train uses WID_VD_MATRIX instead.
2535 }
2536 break;
2537 }
2538
2539 case WID_VD_MATRIX:
2540 fill.height = resize.height = std::max<uint>(ScaleGUITrad(14), GetCharacterHeight(FS_NORMAL) + padding.height);
2541 size.height = 4 * resize.height;
2542 break;
2543
2545 Dimension d = maxdim(GetStringListBoundingBox(_service_interval_dropdown_calendar), GetStringListBoundingBox(_service_interval_dropdown_wallclock));
2546 d.width += padding.width;
2547 d.height += padding.height;
2548 size = maxdim(size, d);
2549 break;
2550 }
2551
2553 /* Do we show the last serviced value as a date or minutes since service? */
2555 ? MakeParameters(GetParamMaxValue(MAX_SERVINT_DAYS), STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO, EconomyTime::MAX_DATE)
2556 : MakeParameters(GetParamMaxValue(MAX_SERVINT_DAYS), STR_VEHICLE_DETAILS_LAST_SERVICE_DATE, TimerGameEconomy::DateAtStartOfYear(EconomyTime::MAX_YEAR));
2557
2558 size.width = std::max(size.width, GetStringBoundingBox(GetStringWithArgs(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT, params)).width);
2559 PrepareArgsForNextRun(params);
2560 size.width = std::max(size.width, GetStringBoundingBox(GetStringWithArgs(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS, params)).width);
2561
2562 size.width += padding.width;
2563 size.height = GetCharacterHeight(FS_NORMAL) + padding.height;
2564 break;
2565 }
2566 }
2567
2574 static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type, CompanyID company_id)
2575 {
2576 if (_local_company != company_id) return false;
2577
2578 const VehicleDefaultSettings *vds = &Company::Get(company_id)->settings.vehicle;
2579 switch (vehicle_type) {
2580 default: NOT_REACHED();
2581 case VEH_TRAIN: return vds->servint_trains != 0;
2582 case VEH_ROAD: return vds->servint_roadveh != 0;
2583 case VEH_SHIP: return vds->servint_ships != 0;
2584 case VEH_AIRCRAFT: return vds->servint_aircraft != 0;
2585 }
2586 }
2587
2597 static void DrawVehicleDetails(const Vehicle *v, const Rect &r, int vscroll_pos, uint vscroll_cap, TrainDetailsWindowTabs det_tab)
2598 {
2599 switch (v->type) {
2600 case VEH_TRAIN: DrawTrainDetails(Train::From(v), r, vscroll_pos, vscroll_cap, det_tab); break;
2601 case VEH_ROAD: DrawRoadVehDetails(v, r); break;
2602 case VEH_SHIP: DrawShipDetails(v, r); break;
2604 default: NOT_REACHED();
2605 }
2606 }
2607
2608 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2609 {
2610 if (widget == WID_VD_CAPTION) return GetString(STR_VEHICLE_DETAILS_CAPTION, Vehicle::Get(this->window_number)->index);
2611
2612 return this->Window::GetWidgetString(widget, stringid);
2613 }
2614
2615 void DrawWidget(const Rect &r, WidgetID widget) const override
2616 {
2617 const Vehicle *v = Vehicle::Get(this->window_number);
2618
2619 switch (widget) {
2620 case WID_VD_TOP_DETAILS: {
2621 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2622
2623 /* Draw running cost */
2624 DrawString(tr,
2625 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_AGE_RUNNING_COST_PERIOD : STR_VEHICLE_INFO_AGE_RUNNING_COST_YR,
2626 (v->age + CalendarTime::DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_INFO_AGE : STR_VEHICLE_INFO_AGE_RED,
2629 v->GetDisplayRunningCost()));
2630 tr.top += GetCharacterHeight(FS_NORMAL);
2631
2632 /* Draw max speed */
2633 uint64_t max_speed = PackVelocity(v->GetDisplayMaxSpeed(), v->type);
2634 if (v->type == VEH_TRAIN ||
2635 (v->type == VEH_ROAD && _settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL)) {
2636 const GroundVehicleCache *gcache = v->GetGroundVehicleCache();
2637 if (v->type == VEH_TRAIN && (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ||
2638 Train::From(v)->GetAccelerationType() == VehicleAccelerationModel::Maglev)) {
2639 DrawString(tr, GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, gcache->cached_weight, gcache->cached_power, max_speed));
2640 } else {
2641 DrawString(tr, GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE, gcache->cached_weight, gcache->cached_power, max_speed, gcache->cached_max_te));
2642 }
2643 } else if (v->type == VEH_AIRCRAFT) {
2644 StringID type = v->GetEngine()->GetAircraftTypeText();
2645 if (Aircraft::From(v)->GetRange() > 0) {
2646 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE, max_speed, type, Aircraft::From(v)->GetRange()));
2647 } else {
2648 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED_TYPE, max_speed, type));
2649 }
2650 } else {
2651 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED, max_speed));
2652 }
2653 tr.top += GetCharacterHeight(FS_NORMAL);
2654
2655 /* Draw profit */
2656 if (v->IsGroundVehicle()) {
2657 DrawString(tr,
2658 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD_MIN_PERFORMANCE : STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE,
2662 } else {
2663 DrawString(tr,
2664 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD : STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR,
2667 }
2668 tr.top += GetCharacterHeight(FS_NORMAL);
2669
2670 /* Draw breakdown & reliability */
2671 DrawString(tr, GetString(STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS, ToPercent16(v->reliability), v->breakdowns_since_last_service));
2672 break;
2673 }
2674
2675 case WID_VD_MATRIX: {
2676 /* For trains only. */
2677 DrawVehicleDetails(v, r.Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero).WithHeight(this->resize.step_height), this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->tab);
2678 break;
2679 }
2680
2681 case WID_VD_MIDDLE_DETAILS: {
2682 /* For other vehicles, at the place of the matrix. */
2683 bool rtl = _current_text_dir == TD_RTL;
2684 uint sprite_width = GetSingleVehicleWidth(v, EIT_IN_DETAILS) + WidgetDimensions::scaled.framerect.Horizontal();
2685 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2686
2687 /* Articulated road vehicles use a complete line. */
2688 if (v->type == VEH_ROAD && v->HasArticulatedPart()) {
2689 DrawVehicleImage(v, tr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2690 } else {
2691 Rect sr = tr.WithWidth(sprite_width, rtl);
2692 DrawVehicleImage(v, sr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2693 }
2694
2695 DrawVehicleDetails(v, tr.Indent(sprite_width, rtl), 0, 0, this->tab);
2696 break;
2697 }
2698
2700 /* Draw service interval text */
2701 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2702
2703 /* We're using wallclock units. Show minutes since last serviced. */
2705 int minutes_since_serviced = (TimerGameEconomy::date - v->date_of_last_service).base() / EconomyTime::DAYS_IN_ECONOMY_MONTH;
2706 DrawString(tr.left, tr.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
2707 GetString(v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_MINUTES,
2708 v->GetServiceInterval(), STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO, minutes_since_serviced));
2709 break;
2710 }
2711
2712 /* We're using calendar dates. Show the date of last service. */
2713 DrawString(tr.left, tr.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
2714 GetString(v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS,
2715 v->GetServiceInterval(), STR_VEHICLE_DETAILS_LAST_SERVICE_DATE, v->date_of_last_service));
2716 break;
2717 }
2718 }
2719 }
2720
2722 void OnPaint() override
2723 {
2724 const Vehicle *v = Vehicle::Get(this->window_number);
2725
2726 if (v->type == VEH_TRAIN) {
2727 this->LowerWidget(WID_VD_DETAILS_CARGO_CARRIED + this->tab);
2728 this->vscroll->SetCount(GetTrainDetailsWndVScroll(v->index, this->tab));
2729 }
2730
2731 /* Disable service-scroller when interval is set to disabled */
2735
2736 StringID str =
2737 !v->ServiceIntervalIsCustom() ? STR_VEHICLE_DETAILS_DEFAULT :
2738 v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT :
2739 TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_DETAILS_MINUTES : STR_VEHICLE_DETAILS_DAYS;
2742
2743 this->DrawWidgets();
2744 }
2745
2746 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2747 {
2748 switch (widget) {
2749 case WID_VD_INCREASE_SERVICING_INTERVAL: // increase int
2750 case WID_VD_DECREASE_SERVICING_INTERVAL: { // decrease int
2751 const Vehicle *v = Vehicle::Get(this->window_number);
2752 int mod;
2753 if (!v->ServiceIntervalIsPercent() && TimerGameEconomy::UsingWallclockUnits()) {
2754 mod = _ctrl_pressed ? 1 : 5;
2755 } else {
2756 mod = _ctrl_pressed ? 5 : 10;
2757 }
2758
2759 mod = (widget == WID_VD_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
2760 mod = GetServiceIntervalClamped(mod + v->GetServiceInterval(), v->ServiceIntervalIsPercent());
2761 if (mod == v->GetServiceInterval()) return;
2762
2763 Command<Commands::ChangeServiceInterval>::Post(STR_ERROR_CAN_T_CHANGE_SERVICING, v->index, mod, true, v->ServiceIntervalIsPercent());
2764 break;
2765 }
2766
2768 const Vehicle *v = Vehicle::Get(this->window_number);
2769 ShowDropDownMenu(this,
2770 TimerGameEconomy::UsingWallclockUnits() ? _service_interval_dropdown_wallclock : _service_interval_dropdown_calendar,
2771 v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0);
2772 break;
2773 }
2774
2779 this->SetWidgetsLoweredState(false,
2784
2786 this->SetDirty();
2787 break;
2788 }
2789 }
2790
2791 bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
2792 {
2794 const Vehicle *v = Vehicle::Get(this->window_number);
2795 StringID tool_tip;
2796 if (v->ServiceIntervalIsPercent()) {
2797 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_PERCENT : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_PERCENT;
2799 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_MINUTES : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_MINUTES;
2800 } else {
2801 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_DAYS : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_DAYS;
2802 }
2803 GuiShowTooltips(this, GetEncodedString(tool_tip), close_cond);
2804 return true;
2805 }
2806
2807 return false;
2808 }
2809
2810 void OnDropdownSelect(WidgetID widget, int index, int) override
2811 {
2812 switch (widget) {
2814 const Vehicle *v = Vehicle::Get(this->window_number);
2815 bool iscustom = index != 0;
2816 bool ispercent = iscustom ? (index == 2) : Company::Get(v->owner)->settings.vehicle.servint_ispercent;
2817 uint16_t interval = GetServiceIntervalClamped(v->GetServiceInterval(), ispercent);
2818 Command<Commands::ChangeServiceInterval>::Post(STR_ERROR_CAN_T_CHANGE_SERVICING, v->index, interval, iscustom, ispercent);
2819 break;
2820 }
2821 }
2822 }
2823
2824 void OnResize() override
2825 {
2827 if (nwi != nullptr) {
2828 this->vscroll->SetCapacityFromWidget(this, WID_VD_MATRIX);
2829 }
2830 }
2831};
2832
2835 WDP_AUTO, "view_vehicle_details_train", 405, 178,
2837 {},
2839);
2840
2843 WDP_AUTO, "view_vehicle_details", 405, 113,
2845 {},
2847);
2848
2859
2860
2861/* Unified vehicle GUI - Vehicle View Window */
2862
2864static constexpr std::initializer_list<NWidgetPart> _nested_vehicle_view_widgets = {
2866 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2867 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME),
2868 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VV_CAPTION),
2869 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION),
2870 NWidget(WWT_DEBUGBOX, COLOUR_GREY),
2871 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2872 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2873 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2874 EndContainer(),
2876 NWidget(WWT_PANEL, COLOUR_GREY),
2877 NWidget(WWT_INSET, COLOUR_GREY), SetPadding(2, 2, 2, 2),
2878 NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_VV_VIEWPORT), SetMinimalSize(226, 84), SetResize(1, 1),
2879 EndContainer(),
2880 EndContainer(),
2883 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_GOTO_DEPOT), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2884 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_CLONE), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2885 EndContainer(),
2886 /* For trains only, 'ignore signal' button. */
2888 NWidget(WWT_IMGBTN, COLOUR_GREY, WID_VV_FORCE_PROCEED), SetMinimalSize(18, 18),
2889 SetSpriteTip(SPR_IGNORE_SIGNALS, STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP),
2890 EndContainer(),
2892 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_REFIT), SetMinimalSize(18, 18), SetSpriteTip(SPR_REFIT_VEHICLE),
2894 SetSpriteTip(SPR_FORCE_VEHICLE_TURN, STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP),
2895 EndContainer(),
2896 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_SHOW_ORDERS), SetMinimalSize(18, 18), SetSpriteTip(SPR_SHOW_ORDERS),
2897 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_SHOW_DETAILS), SetMinimalSize(18, 18), SetSpriteTip(SPR_SHOW_VEHICLE_DETAILS),
2898 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(18, 0), SetResize(0, 1), EndContainer(),
2899 EndContainer(),
2900 EndContainer(),
2902 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_VV_START_STOP), SetResize(1, 0), SetFill(1, 0),
2903 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_ORDER_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION, STR_VEHICLE_VIEW_ORDER_LOCATION_TOOLTIP),
2904 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2905 EndContainer(),
2906};
2907
2908/* Just to make sure, nobody has changed the vehicle type constants, as we are
2909 using them for array indexing in a number of places here. */
2910static_assert(VEH_TRAIN == 0);
2911static_assert(VEH_ROAD == 1);
2912static_assert(VEH_SHIP == 2);
2913static_assert(VEH_AIRCRAFT == 3);
2914
2922
2925static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
2926static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
2929
2932 VCT_CMD_START_STOP = 0,
2933 VCT_CMD_CLONE_VEH,
2934 VCT_CMD_TURN_AROUND,
2935};
2936
2939 { // VCT_CMD_START_STOP
2940 STR_ERROR_CAN_T_STOP_START_TRAIN,
2941 STR_ERROR_CAN_T_STOP_START_ROAD_VEHICLE,
2942 STR_ERROR_CAN_T_STOP_START_SHIP,
2943 STR_ERROR_CAN_T_STOP_START_AIRCRAFT
2944 },
2945 { // VCT_CMD_CLONE_VEH
2946 STR_ERROR_CAN_T_BUY_TRAIN,
2947 STR_ERROR_CAN_T_BUY_ROAD_VEHICLE,
2948 STR_ERROR_CAN_T_BUY_SHIP,
2949 STR_ERROR_CAN_T_BUY_AIRCRAFT
2950 },
2951 { // VCT_CMD_TURN_AROUND
2952 STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN,
2953 STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN,
2954 INVALID_STRING_ID, // invalid for ships
2955 INVALID_STRING_ID // invalid for aircraft
2956 },
2957};
2958
2964void CcStartStopVehicle(Commands, const CommandCost &result, VehicleID veh_id, bool)
2965{
2966 if (result.Failed()) return;
2967
2968 const Vehicle *v = Vehicle::GetIfValid(veh_id);
2969 if (v == nullptr || !v->IsPrimaryVehicle() || v->owner != _local_company) return;
2970
2971 StringID msg = v->vehstatus.Test(VehState::Stopped) ? STR_VEHICLE_COMMAND_STOPPED : STR_VEHICLE_COMMAND_STARTED;
2972 Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos);
2973 AddTextEffect(GetEncodedString(msg), pt.x, pt.y, Ticks::DAY_TICKS, TE_RISING);
2974}
2975
2981void StartStopVehicle(const Vehicle *v, bool texteffect)
2982{
2983 assert(v->IsPrimaryVehicle());
2984 Command<Commands::StartStopVehicle>::Post(_vehicle_msg_translation_table[VCT_CMD_START_STOP][v->type], texteffect ? CcStartStopVehicle : nullptr, v->tile, v->index, false);
2985}
2986
2992static bool IsVehicleRefittable(const Vehicle *v)
2993{
2994 if (!v->IsStoppedInDepot()) return false;
2995
2996 do {
2997 if (IsEngineRefittable(v->engine_type)) return true;
2998 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
2999
3000 return false;
3001}
3002
3004struct VehicleViewWindow : Window {
3005private:
3017 bool mouse_over_start_stop = false;
3018
3024 {
3025 switch (plane) {
3026 case SEL_DC_GOTO_DEPOT:
3027 case SEL_DC_CLONE:
3028 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE)->SetDisplayedPlane(plane - SEL_DC_BASEPLANE);
3029 break;
3030
3031 case SEL_RT_REFIT:
3032 case SEL_RT_TURN_AROUND:
3033 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_REFIT_TURN)->SetDisplayedPlane(plane - SEL_RT_BASEPLANE);
3034 break;
3035
3036 default:
3037 NOT_REACHED();
3038 }
3039 }
3040
3041public:
3043 {
3045 this->CreateNestedTree();
3046
3047 /* Sprites for the 'send to depot' button indexed by vehicle type. */
3048 static const SpriteID vehicle_view_goto_depot_sprites[] = {
3049 SPR_SEND_TRAIN_TODEPOT,
3050 SPR_SEND_ROADVEH_TODEPOT,
3051 SPR_SEND_SHIP_TODEPOT,
3052 SPR_SEND_AIRCRAFT_TODEPOT,
3053 };
3055 this->GetWidget<NWidgetCore>(WID_VV_GOTO_DEPOT)->SetSprite(vehicle_view_goto_depot_sprites[v->type]);
3056
3057 /* Sprites for the 'clone vehicle' button indexed by vehicle type. */
3058 static const SpriteID vehicle_view_clone_sprites[] = {
3060 SPR_CLONE_ROADVEH,
3061 SPR_CLONE_SHIP,
3062 SPR_CLONE_AIRCRAFT,
3063 };
3064 this->GetWidget<NWidgetCore>(WID_VV_CLONE)->SetSprite(vehicle_view_clone_sprites[v->type]);
3065
3066 switch (v->type) {
3067 case VEH_TRAIN:
3068 this->GetWidget<NWidgetCore>(WID_VV_TURN_AROUND)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_REVERSE_TOOLTIP);
3069 this->GetWidget<NWidgetStacked>(WID_VV_FORCE_PROCEED_SEL)->SetDisplayedPlane(0);
3070 break;
3071
3072 case VEH_ROAD:
3074 break;
3075
3076 case VEH_SHIP:
3077 case VEH_AIRCRAFT:
3080 break;
3081
3082 default: NOT_REACHED();
3083 }
3085 this->owner = v->owner;
3086 this->GetWidget<NWidgetViewport>(WID_VV_VIEWPORT)->InitializeViewport(this, static_cast<VehicleID>(this->window_number), ScaleZoomGUI(_vehicle_view_zoom_levels[v->type]));
3087
3088 this->GetWidget<NWidgetCore>(WID_VV_START_STOP)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP + v->type);
3089 this->GetWidget<NWidgetCore>(WID_VV_RENAME)->SetToolTip(STR_VEHICLE_DETAILS_TRAIN_RENAME + v->type);
3090 this->GetWidget<NWidgetCore>(WID_VV_LOCATION)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP + v->type);
3091 this->GetWidget<NWidgetCore>(WID_VV_REFIT)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP + v->type);
3092 this->GetWidget<NWidgetCore>(WID_VV_GOTO_DEPOT)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP + v->type);
3093 this->GetWidget<NWidgetCore>(WID_VV_SHOW_ORDERS)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP + v->type);
3094 this->GetWidget<NWidgetCore>(WID_VV_SHOW_DETAILS)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP + v->type);
3095 this->GetWidget<NWidgetCore>(WID_VV_CLONE)->SetToolTip(STR_VEHICLE_VIEW_CLONE_TRAIN_INFO + v->type);
3096
3097 this->UpdatePlanes();
3098 this->UpdateButtons();
3099 }
3100
3101 void Close([[maybe_unused]] int data = 0) override
3102 {
3103 CloseWindowById(WC_VEHICLE_ORDERS, this->window_number, false);
3104 CloseWindowById(WC_VEHICLE_REFIT, this->window_number, false);
3105 CloseWindowById(WC_VEHICLE_DETAILS, this->window_number, false);
3106 CloseWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false);
3107 this->Window::Close();
3108 }
3109
3110 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
3111 {
3112 const Vehicle *v = Vehicle::Get(this->window_number);
3113 switch (widget) {
3114 case WID_VV_START_STOP:
3115 size.height = std::max<uint>({size.height, (uint)GetCharacterHeight(FS_NORMAL), GetScaledSpriteSize(SPR_WARNING_SIGN).height, GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED).height, GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING).height}) + padding.height;
3116 break;
3117
3119 if (v->type != VEH_TRAIN) {
3120 size.height = 0;
3121 size.width = 0;
3122 }
3123 break;
3124
3125 case WID_VV_VIEWPORT:
3126 size.width = VV_INITIAL_VIEWPORT_WIDTH;
3128 break;
3129 }
3130 }
3131
3134 {
3135 const Vehicle *v = Vehicle::Get(this->window_number);
3136 bool is_localcompany = v->owner == _local_company;
3137 bool refittable_and_stopped_in_depot = IsVehicleRefittable(v);
3138
3139 this->SetWidgetDisabledState(WID_VV_RENAME, !is_localcompany);
3140 this->SetWidgetDisabledState(WID_VV_GOTO_DEPOT, !is_localcompany);
3141 this->SetWidgetDisabledState(WID_VV_REFIT, !refittable_and_stopped_in_depot || !is_localcompany);
3142 this->SetWidgetDisabledState(WID_VV_CLONE, !is_localcompany);
3143
3144 if (v->type == VEH_TRAIN) {
3146 this->SetWidgetDisabledState(WID_VV_FORCE_PROCEED, !is_localcompany);
3147 }
3148
3149 if (v->type == VEH_TRAIN || v->type == VEH_ROAD) {
3150 this->SetWidgetDisabledState(WID_VV_TURN_AROUND, !is_localcompany);
3151 }
3152
3154 }
3155
3156 void OnPaint() override
3157 {
3158 const Vehicle *v = Vehicle::Get(this->window_number);
3159
3160 const Window *mainwindow = GetMainWindow();
3161 if (mainwindow->viewport->follow_vehicle == v->index) {
3163 }
3164
3165 this->DrawWidgets();
3166 }
3167
3168 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
3169 {
3170 if (widget != WID_VV_CAPTION) return this->Window::GetWidgetString(widget, stringid);
3171
3172 const Vehicle *v = Vehicle::Get(this->window_number);
3173 return GetString(STR_VEHICLE_VIEW_CAPTION, v->index);
3174 }
3175
3176 std::string GetVehicleStatusString(const Vehicle *v, TextColour &text_colour) const
3177 {
3178 text_colour = TC_BLACK;
3179
3180 if (v->vehstatus.Test(VehState::Crashed)) return GetString(STR_VEHICLE_STATUS_CRASHED);
3181
3182 if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) return GetString(STR_VEHICLE_STATUS_BROKEN_DOWN);
3183
3184 if (v->vehstatus.Test(VehState::Stopped) && (!mouse_over_start_stop || v->IsStoppedInDepot())) {
3185 if (v->type != VEH_TRAIN) return GetString(STR_VEHICLE_STATUS_STOPPED);
3186 if (v->cur_speed != 0) return GetString(STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL, PackVelocity(v->GetDisplaySpeed(), v->type));
3187 if (Train::From(v)->gcache.cached_power == 0) return GetString(STR_VEHICLE_STATUS_TRAIN_NO_POWER);
3188 return GetString(STR_VEHICLE_STATUS_STOPPED);
3189 }
3190
3191 if (v->IsInDepot() && v->IsWaitingForUnbunching()) return GetString(STR_VEHICLE_STATUS_WAITING_UNBUNCHING);
3192
3193 if (v->type == VEH_TRAIN && Train::From(v)->flags.Test(VehicleRailFlag::Stuck) && !v->current_order.IsType(OT_LOADING)) return GetString(STR_VEHICLE_STATUS_TRAIN_STUCK);
3194
3195 if (v->type == VEH_AIRCRAFT && Aircraft::From(v)->flags.Test(VehicleAirFlag::DestinationTooFar) && !v->current_order.IsType(OT_LOADING)) return GetString(STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR);
3196
3197 /* Vehicle is in a "normal" state, show current order. */
3198 if (mouse_over_start_stop) {
3200 text_colour = TC_RED | TC_FORCED;
3201 } else if (v->type == VEH_TRAIN && Train::From(v)->flags.Test(VehicleRailFlag::Stuck) && !v->current_order.IsType(OT_LOADING)) {
3202 text_colour = TC_ORANGE | TC_FORCED;
3203 }
3204 }
3205
3206 switch (v->current_order.GetType()) {
3207 case OT_GOTO_STATION:
3208 return GetString(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_STATION_VEL : STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL,
3210
3211 case OT_GOTO_DEPOT: {
3212 /* This case *only* happens when multiple nearest depot orders
3213 * follow each other (including an order list only one order: a
3214 * nearest depot order) and there are no reachable depots.
3215 * It is primarily to guard for the case that there is no
3216 * depot with index 0, which would be used as fallback for
3217 * evaluating the string in the status bar. */
3218 if (v->current_order.GetDestination() == DepotID::Invalid()) return {};
3219
3222 return GetStringWithArgs(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL, params);
3223 }
3224
3226 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);
3227 }
3228
3229 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);
3230 }
3231
3232 case OT_LOADING:
3233 return GetString(STR_VEHICLE_STATUS_LOADING_UNLOADING);
3234
3235 case OT_GOTO_WAYPOINT:
3236 assert(v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP);
3237 return GetString(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_WAYPOINT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL,
3239
3240 case OT_LEAVESTATION:
3241 if (v->type != VEH_AIRCRAFT) {
3242 return GetString(STR_VEHICLE_STATUS_LEAVING);
3243 }
3244 [[fallthrough]];
3245
3246 default:
3247 if (v->GetNumManualOrders() == 0) {
3248 return GetString(STR_VEHICLE_STATUS_NO_ORDERS_VEL, PackVelocity(v->GetDisplaySpeed(), v->type));
3249 }
3250
3251 return {};
3252 }
3253 }
3254
3255 void DrawWidget(const Rect &r, WidgetID widget) const override
3256 {
3257 if (widget != WID_VV_START_STOP) return;
3258
3259 /* Draw the flag plus orders. */
3260 bool rtl = (_current_text_dir == TD_RTL);
3261 uint icon_width = std::max({GetScaledSpriteSize(SPR_WARNING_SIGN).width, GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED).width, GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING).width});
3262 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
3263
3264 const Vehicle *v = Vehicle::Get(this->window_number);
3265 SpriteID image = v->vehstatus.Test(VehState::Stopped) ? SPR_FLAG_VEH_STOPPED : (v->vehicle_flags.Test(VehicleFlag::PathfinderLost)) ? SPR_WARNING_SIGN : SPR_FLAG_VEH_RUNNING;
3266 DrawSpriteIgnorePadding(image, PAL_NONE, tr.WithWidth(icon_width, rtl), SA_CENTER);
3267
3268 tr = tr.Indent(icon_width + WidgetDimensions::scaled.imgbtn.Horizontal(), rtl);
3269
3270 TextColour text_colour = TC_FROMSTRING;
3271 std::string str = GetVehicleStatusString(v, text_colour);
3272 DrawString(tr.left, tr.right, CentreBounds(tr.top, tr.bottom, GetCharacterHeight(FS_NORMAL)), str, text_colour, SA_HOR_CENTER);
3273 }
3274
3275 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
3276 {
3277 const Vehicle *v = Vehicle::Get(this->window_number);
3278
3279 switch (widget) {
3280 case WID_VV_RENAME: { // rename
3281 ShowQueryString(GetString(STR_VEHICLE_NAME, v->index), STR_QUERY_RENAME_TRAIN_CAPTION + v->type,
3282 MAX_LENGTH_VEHICLE_NAME_CHARS, this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars});
3283 break;
3284 }
3285
3286 case WID_VV_START_STOP: // start stop
3287 StartStopVehicle(v, false);
3288 break;
3289
3290 case WID_VV_ORDER_LOCATION: {
3291 /* Scroll to current order destination */
3292 TileIndex tile = v->current_order.GetLocation(v);
3293 if (tile == INVALID_TILE) break;
3294
3295 if (_ctrl_pressed) {
3297 } else {
3299 }
3300 break;
3301 }
3302
3303 case WID_VV_LOCATION: // center main view
3304 if (_ctrl_pressed) {
3306 } else {
3307 const Window *mainwindow = GetMainWindow();
3308 if (click_count > 1) {
3309 /* main window 'follows' vehicle */
3310 mainwindow->viewport->follow_vehicle = v->index;
3311 } else {
3312 if (mainwindow->viewport->follow_vehicle == v->index) mainwindow->viewport->follow_vehicle = VehicleID::Invalid();
3313 ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos);
3314 }
3315 }
3316 break;
3317
3318 case WID_VV_GOTO_DEPOT: // goto hangar
3319 Command<Commands::SendVehicleToDepot>::Post(GetCmdSendToDepotMsg(v), v->index, _ctrl_pressed ? DepotCommandFlag::Service : DepotCommandFlags{}, {});
3320 break;
3321 case WID_VV_REFIT: // refit
3323 break;
3324 case WID_VV_SHOW_ORDERS: // show orders
3325 if (_ctrl_pressed) {
3327 } else {
3328 ShowOrdersWindow(v);
3329 }
3330 break;
3331 case WID_VV_SHOW_DETAILS: // show details
3332 if (_ctrl_pressed) {
3334 } else {
3336 }
3337 break;
3338 case WID_VV_CLONE: // clone vehicle
3339 /* Suppress the vehicle GUI when share-cloning.
3340 * There is no point to it except for starting the vehicle.
3341 * For starting the vehicle the player has to open the depot GUI, which is
3342 * most likely already open, but is also visible in the vehicle viewport. */
3343 Command<Commands::CloneVehicle>::Post(_vehicle_msg_translation_table[VCT_CMD_CLONE_VEH][v->type],
3344 _ctrl_pressed ? nullptr : CcCloneVehicle,
3345 v->tile, v->index, _ctrl_pressed);
3346 break;
3347 case WID_VV_TURN_AROUND: // turn around
3348 assert(v->IsGroundVehicle());
3349 if (v->type == VEH_ROAD) {
3350 Command<Commands::TurnRoadVehicle>::Post(_vehicle_msg_translation_table[VCT_CMD_TURN_AROUND][v->type], v->tile, v->index);
3351 } else {
3352 Command<Commands::ReverseTrainDirection>::Post(_vehicle_msg_translation_table[VCT_CMD_TURN_AROUND][v->type], v->tile, v->index, false);
3353 }
3354 break;
3355 case WID_VV_FORCE_PROCEED: // force proceed
3356 assert(v->type == VEH_TRAIN);
3357 Command<Commands::ForceTrainProceed>::Post(STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL, v->tile, v->index);
3358 break;
3359 }
3360 }
3361
3362 EventState OnHotkey(int hotkey) override
3363 {
3364 /* If the hotkey is not for any widget in the UI (i.e. for honking) */
3365 if (hotkey == WID_VV_HONK_HORN) {
3366 const Window *mainwindow = GetMainWindow();
3368 /* Only play the sound if we're following this vehicle */
3369 if (mainwindow->viewport->follow_vehicle == v->index) {
3370 v->PlayLeaveStationSound(true);
3371 }
3372 }
3373 return Window::OnHotkey(hotkey);
3374 }
3375
3376 void OnQueryTextFinished(std::optional<std::string> str) override
3377 {
3378 if (!str.has_value()) return;
3379
3380 Command<Commands::RenameVehicle>::Post(STR_ERROR_CAN_T_RENAME_TRAIN + Vehicle::Get(this->window_number)->type, static_cast<VehicleID>(this->window_number), *str);
3381 }
3382
3383 void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
3384 {
3385 bool start_stop = widget == WID_VV_START_STOP;
3386 if (start_stop != mouse_over_start_stop) {
3387 mouse_over_start_stop = start_stop;
3389 }
3390 }
3391
3392 void OnMouseWheel(int wheel, WidgetID widget) override
3393 {
3394 if (widget != WID_VV_VIEWPORT) return;
3395 if (_settings_client.gui.scrollwheel_scrolling != ScrollWheelScrolling::Off) {
3396 DoZoomInOutWindow(wheel < 0 ? ZOOM_IN : ZOOM_OUT, this);
3397 }
3398 }
3399
3400 void OnResize() override
3401 {
3402 if (this->viewport != nullptr) {
3404 nvp->UpdateViewportCoordinates(this);
3405 }
3406 }
3407
3410 {
3411 const Vehicle *v = Vehicle::Get(this->window_number);
3412 bool veh_stopped = v->IsStoppedInDepot();
3413
3414 /* Widget WID_VV_GOTO_DEPOT must be hidden if the vehicle is already stopped in depot.
3415 * Widget WID_VV_CLONE_VEH should then be shown, since cloning is allowed only while in depot and stopped.
3416 */
3417 PlaneSelections plane = veh_stopped ? SEL_DC_CLONE : SEL_DC_GOTO_DEPOT;
3418 NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE); // Selection widget 'send to depot' / 'clone'.
3419 if (nwi->shown_plane + SEL_DC_BASEPLANE != plane) {
3420 this->SelectPlane(plane);
3421 }
3422 /* The same system applies to widget WID_VV_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
3423 if (v->IsGroundVehicle()) {
3424 plane = veh_stopped ? SEL_RT_REFIT : SEL_RT_TURN_AROUND;
3426 if (nwi->shown_plane + SEL_RT_BASEPLANE != plane) {
3427 this->SelectPlane(plane);
3428 }
3429 }
3430 }
3431
3437 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
3438 {
3439 if (data == VIWD_AUTOREPLACE) {
3440 /* Autoreplace replaced the vehicle.
3441 * Nothing to do for this window. */
3442 return;
3443 }
3444
3445 this->UpdatePlanes();
3446 this->UpdateButtons();
3447 this->SetDirty();
3448 }
3449
3450 bool IsNewGRFInspectable() const override
3451 {
3452 return ::IsNewGRFInspectable(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3453 }
3454
3455 void ShowNewGRFInspectWindow() const override
3456 {
3457 ::ShowNewGRFInspectWindow(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3458 }
3459
3460 static inline HotkeyList hotkeys{"vehicleview", {
3461 Hotkey('H', "honk", WID_VV_HONK_HORN),
3462 }};
3463};
3464
3467 WDP_AUTO, "view_vehicle", 250, 116,
3469 {},
3471 &VehicleViewWindow::hotkeys
3472);
3473
3479 WDP_AUTO, "view_vehicle_train", 250, 134,
3481 {},
3483 &VehicleViewWindow::hotkeys
3484);
3485
3494
3501{
3502 assert(v != nullptr);
3503 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3504
3505 v = v->First();
3506 if (!v->IsPrimaryVehicle()) return false;
3507
3508 return _thd.GetCallbackWnd()->OnVehicleSelect(v);
3509}
3510
3517bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end)
3518{
3519 assert(begin != end);
3520 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3521
3522 /* If there is only one vehicle in the group, act as if we clicked a single vehicle */
3523 if (begin + 1 == end) return _thd.GetCallbackWnd()->OnVehicleSelect(*begin);
3524
3525 return _thd.GetCallbackWnd()->OnVehicleSelect(begin, end);
3526}
3527
3533bool VehicleClicked(const GUIVehicleGroup &vehgroup)
3534{
3535 return VehicleClicked(vehgroup.vehicles_begin, vehgroup.vehicles_end);
3536}
3537
3538void StopGlobalFollowVehicle(const Vehicle *v)
3539{
3540 Window *w = GetMainWindow();
3541 if (w->viewport->follow_vehicle == v->index) {
3542 ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos, true); // lock the main view on the vehicle's last position
3543 w->viewport->CancelFollow(*w);
3544 }
3545}
3546
3547
3553void CcBuildPrimaryVehicle(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray)
3554{
3555 if (result.Failed()) return;
3556
3557 const Vehicle *v = Vehicle::Get(new_veh_id);
3559}
3560
3568{
3569 switch (v->type) {
3570 case VEH_TRAIN:
3571 return Train::From(v)->GetDisplayImageWidth();
3572
3573 case VEH_ROAD:
3575
3576 default:
3577 bool rtl = _current_text_dir == TD_RTL;
3578 VehicleSpriteSeq seq;
3579 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3580 Rect rec;
3581 seq.GetBounds(&rec);
3582 return UnScaleGUI(rec.Width());
3583 }
3584}
3585
3592int GetVehicleWidth(const Vehicle *v, EngineImageType image_type)
3593{
3594 if (v->type == VEH_TRAIN || v->type == VEH_ROAD) {
3595 int vehicle_width = 0;
3596 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
3597 vehicle_width += GetSingleVehicleWidth(u, image_type);
3598 }
3599 return vehicle_width;
3600 } else {
3601 return GetSingleVehicleWidth(v, image_type);
3602 }
3603}
3604
3611{
3612 bool rtl = _current_text_dir == TD_RTL;
3613
3614 _cursor.sprites.clear();
3615 int total_width = 0;
3616 int y_offset = 0;
3617 bool rotor_seq = false; // Whether to draw the rotor of the vehicle in this step.
3618 bool is_ground_vehicle = v->IsGroundVehicle();
3619
3620 while (v != nullptr) {
3621 if (total_width >= ScaleSpriteTrad(2 * (int)VEHICLEINFO_FULL_VEHICLE_WIDTH)) break;
3622
3624 VehicleSpriteSeq seq;
3625
3626 if (rotor_seq) {
3627 GetCustomRotorSprite(Aircraft::From(v), image_type, &seq);
3628 if (!seq.IsValid()) seq.Set(SPR_ROTOR_STOPPED);
3629 y_offset = -ScaleSpriteTrad(5);
3630 } else {
3631 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3632 }
3633
3634 int x_offs = 0;
3635 if (v->type == VEH_TRAIN) x_offs = Train::From(v)->GetCursorImageOffset();
3636
3637 for (uint i = 0; i < seq.count; ++i) {
3638 PaletteID pal2 = v->vehstatus.Test(VehState::Crashed) || !seq.seq[i].pal ? pal : seq.seq[i].pal;
3639 _cursor.sprites.emplace_back(seq.seq[i].sprite, pal2, rtl ? (-total_width + x_offs) : (total_width + x_offs), y_offset);
3640 }
3641
3642 if (v->type == VEH_AIRCRAFT && v->subtype == AIR_HELICOPTER && !rotor_seq) {
3643 /* Draw rotor part in the next step. */
3644 rotor_seq = true;
3645 } else {
3646 total_width += GetSingleVehicleWidth(v, image_type);
3647 v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr;
3648 }
3649 }
3650
3651 if (is_ground_vehicle) {
3652 /* Center trains and road vehicles on the front vehicle */
3653 int offs = (ScaleSpriteTrad(VEHICLEINFO_FULL_VEHICLE_WIDTH) - total_width) / 2;
3654 if (rtl) offs = -offs;
3655 for (auto &cs : _cursor.sprites) {
3656 cs.pos.x += offs;
3657 }
3658 }
3659
3661}
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 T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
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.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:108
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:31
@ Passengers
Passengers.
Definition cargotype.h:50
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:235
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is 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:470
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:171
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:933
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:1167
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:1425
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:2407
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:2430
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:2504
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)
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:627
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:545
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:585
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:1292
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
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:972
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
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:250
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_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
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 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 SetArrowWidgetTypeTip(ArrowWidgetValues widget_type, StringID tip={})
Widget part function for setting the arrow widget type and tooltip.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
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:695
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:96
static constexpr CargoType CF_NONE
Show only items which do not carry cargo (e.g. train engines).
Definition cargo_type.h:94
static constexpr CargoType CF_ANY
Show all items independent of carried cargo (i.e. no filtering).
Definition cargo_type.h:93
@ 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.
GrfSpecFeature GetGrfSpecFeature(TileIndex tile)
Get the GrfSpecFeature associated with the tile.
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, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:393
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.
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:115
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:138
SpriteID GetCargoIcon() const
Get sprite for showing cargo of this type.
StringID name
Name of this type of cargo.
Definition cargotype.h:91
Comparator to sort CargoType by according to desired order.
Definition cargotype.h:243
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:405
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 coordiates.
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 apropriate 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:747
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:757
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:3290
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:767
GroundVehicleCache * GetGroundVehicleCache()
Access the ground vehicle cache of the vehicle.
Definition vehicle.cpp:3186
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:2576
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:817
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:768
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3261
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:800
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:1845
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:2174
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:3249
@ 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.
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 _train_view_desc(WDP_AUTO, "view_vehicle_train", 250, 134, WC_VEHICLE_VIEW, WC_NONE, {}, _nested_vehicle_view_widgets, &VehicleViewWindow::hotkeys)
Vehicle view window descriptor for trains.
static void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
Assign a vehicle window a new vehicle.
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.
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.
void ShowVehicleViewWindow(const Vehicle *v)
Shows the vehicle view window of the given vehicle.
static const StringID _vehicle_msg_translation_table[][4]
Command codes for the shared buttons indexed by VehicleCommandTranslation and vehicle type.
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.
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 const ZoomLevel _vehicle_view_zoom_levels[]
Zoom levels for vehicle views indexed by vehicle type.
static WindowDesc _vehicle_view_desc(WDP_AUTO, "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 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 bool CargoFilterSingle(const Vehicle *v, const CargoType cargo_type)
Check whether a single vehicle should pass the filter.
static WindowDesc _train_vehicle_details_desc(WDP_AUTO, "view_vehicle_details_train", 405, 178, WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW, {}, _nested_train_vehicle_details_widgets)
Vehicle details window descriptor.
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.
static WindowDesc _nontrain_vehicle_details_desc(WDP_AUTO, "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.
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.
@ 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.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_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).
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:291
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.
@ AWV_DECREASE
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:20
@ AWV_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
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ 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:50
@ 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
ZoomLevel
All zoom levels we know.
Definition zoom_type.h:20
@ 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