OpenTTD Source 20250312-master-gcdcc6b491d
vehicle_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "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"
33#include "spritecache.h"
36#include "company_base.h"
37#include "engine_func.h"
38#include "station_base.h"
39#include "tilehighlight_func.h"
40#include "zoom_func.h"
41#include "depot_cmd.h"
42#include "vehicle_cmd.h"
43#include "order_cmd.h"
44#include "roadveh_cmd.h"
45#include "train_cmd.h"
46#include "hotkeys.h"
47#include "group_cmd.h"
48
49#include "safeguards.h"
50
51
52BaseVehicleListWindow::GroupBy _grouping[VLT_END][VEH_COMPANY_END];
53Sorting _sorting[BaseVehicleListWindow::GB_END];
54
55static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNumberSorter;
56static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNameSorter;
57static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleAgeSorter;
58static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitThisYearSorter;
59static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitLastYearSorter;
60static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleCargoSorter;
61static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleReliabilitySorter;
62static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleMaxSpeedSorter;
63static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleModelSorter;
64static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleValueSorter;
65static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleLengthSorter;
66static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimeToLiveSorter;
67static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimetableDelaySorter;
68static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupLengthSorter;
69static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitThisYearSorter;
70static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitLastYearSorter;
71static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitThisYearSorter;
72static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitLastYearSorter;
73
75template <BaseVehicleListWindow::VehicleIndividualSortFunction func>
77{
78 return func(*(a.vehicles_begin), *(b.vehicles_begin));
79}
80
81const std::initializer_list<BaseVehicleListWindow::VehicleGroupSortFunction * const> BaseVehicleListWindow::vehicle_group_none_sorter_funcs = {
82 &VehicleIndividualToGroupSorterWrapper<VehicleNumberSorter>,
83 &VehicleIndividualToGroupSorterWrapper<VehicleNameSorter>,
84 &VehicleIndividualToGroupSorterWrapper<VehicleAgeSorter>,
85 &VehicleIndividualToGroupSorterWrapper<VehicleProfitThisYearSorter>,
86 &VehicleIndividualToGroupSorterWrapper<VehicleProfitLastYearSorter>,
87 &VehicleIndividualToGroupSorterWrapper<VehicleCargoSorter>,
88 &VehicleIndividualToGroupSorterWrapper<VehicleReliabilitySorter>,
89 &VehicleIndividualToGroupSorterWrapper<VehicleMaxSpeedSorter>,
90 &VehicleIndividualToGroupSorterWrapper<VehicleModelSorter>,
91 &VehicleIndividualToGroupSorterWrapper<VehicleValueSorter>,
92 &VehicleIndividualToGroupSorterWrapper<VehicleLengthSorter>,
93 &VehicleIndividualToGroupSorterWrapper<VehicleTimeToLiveSorter>,
94 &VehicleIndividualToGroupSorterWrapper<VehicleTimetableDelaySorter>,
95};
96
97const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_none_sorter_names_calendar = {
98 STR_SORT_BY_NUMBER,
99 STR_SORT_BY_NAME,
100 STR_SORT_BY_AGE,
101 STR_SORT_BY_PROFIT_THIS_YEAR,
102 STR_SORT_BY_PROFIT_LAST_YEAR,
103 STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
104 STR_SORT_BY_RELIABILITY,
105 STR_SORT_BY_MAX_SPEED,
106 STR_SORT_BY_MODEL,
107 STR_SORT_BY_VALUE,
108 STR_SORT_BY_LENGTH,
109 STR_SORT_BY_LIFE_TIME,
110 STR_SORT_BY_TIMETABLE_DELAY,
111};
112
113const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_none_sorter_names_wallclock = {
114 STR_SORT_BY_NUMBER,
115 STR_SORT_BY_NAME,
116 STR_SORT_BY_AGE,
117 STR_SORT_BY_PROFIT_THIS_PERIOD,
118 STR_SORT_BY_PROFIT_LAST_PERIOD,
119 STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
120 STR_SORT_BY_RELIABILITY,
121 STR_SORT_BY_MAX_SPEED,
122 STR_SORT_BY_MODEL,
123 STR_SORT_BY_VALUE,
124 STR_SORT_BY_LENGTH,
125 STR_SORT_BY_LIFE_TIME,
126 STR_SORT_BY_TIMETABLE_DELAY,
127};
128
129const std::initializer_list<BaseVehicleListWindow::VehicleGroupSortFunction * const> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_funcs = {
130 &VehicleGroupLengthSorter,
131 &VehicleGroupTotalProfitThisYearSorter,
132 &VehicleGroupTotalProfitLastYearSorter,
133 &VehicleGroupAverageProfitThisYearSorter,
134 &VehicleGroupAverageProfitLastYearSorter,
135};
136
137const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_names_calendar = {
138 STR_SORT_BY_NUM_VEHICLES,
139 STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR,
140 STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR,
141 STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR,
142 STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR,
143};
144
145const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_names_wallclock = {
146 STR_SORT_BY_NUM_VEHICLES,
147 STR_SORT_BY_TOTAL_PROFIT_THIS_PERIOD,
148 STR_SORT_BY_TOTAL_PROFIT_LAST_PERIOD,
149 STR_SORT_BY_AVERAGE_PROFIT_THIS_PERIOD,
150 STR_SORT_BY_AVERAGE_PROFIT_LAST_PERIOD,
151};
152
153const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_by_names = {
154 STR_GROUP_BY_NONE,
155 STR_GROUP_BY_SHARED_ORDERS,
156};
157
158const StringID BaseVehicleListWindow::vehicle_depot_name[] = {
159 STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT,
160 STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT,
161 STR_VEHICLE_LIST_SEND_SHIP_TO_DEPOT,
162 STR_VEHICLE_LIST_SEND_AIRCRAFT_TO_HANGAR
163};
164
165BaseVehicleListWindow::BaseVehicleListWindow(WindowDesc &desc, const VehicleListIdentifier &vli) : Window(desc), vli(vli)
166{
167 this->vehicle_sel = VehicleID::Invalid();
168 this->grouping = _grouping[vli.type][vli.vtype];
169 this->UpdateSortingFromGrouping();
170}
171
172std::span<const StringID> BaseVehicleListWindow::GetVehicleSorterNames() const
173{
174 switch (this->grouping) {
175 case GB_NONE:
176 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_none_sorter_names_wallclock : vehicle_group_none_sorter_names_calendar;
177 case GB_SHARED_ORDERS:
178 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_shared_orders_sorter_names_wallclock : vehicle_group_shared_orders_sorter_names_calendar;
179 default:
180 NOT_REACHED();
181 }
182}
183
190{
191 if (number >= 10000) return 5;
192 if (number >= 1000) return 4;
193 if (number >= 100) return 3;
194
195 /*
196 * When the smallest unit number is less than 10, it is
197 * quite likely that it will expand to become more than
198 * 10 quite soon.
199 */
200 return 2;
201}
202
209{
210 uint unitnumber = 0;
211 for (const Vehicle *v : vehicles) {
212 unitnumber = std::max<uint>(unitnumber, v->unitnumber);
213 }
214
215 return CountDigitsForAllocatingSpace(unitnumber);
216}
217
218void BaseVehicleListWindow::BuildVehicleList()
219{
220 if (!this->vehgroups.NeedRebuild()) return;
221
222 Debug(misc, 3, "Building vehicle list type {} for company {} given index {}", this->vli.type, this->vli.company, this->vli.index);
223
224 this->vehgroups.clear();
225
226 GenerateVehicleSortList(&this->vehicles, this->vli);
227
228 CargoTypes used = 0;
229 for (const Vehicle *v : this->vehicles) {
230 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
231 if (u->cargo_cap > 0) SetBit(used, u->cargo_type);
232 }
233 }
234 this->used_cargoes = used;
235
236 if (this->grouping == GB_NONE) {
237 uint max_unitnumber = 0;
238 for (auto it = this->vehicles.begin(); it != this->vehicles.end(); ++it) {
239 this->vehgroups.emplace_back(it, it + 1);
240
241 max_unitnumber = std::max<uint>(max_unitnumber, (*it)->unitnumber);
242 }
243 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber);
244 } else {
245 /* Sort by the primary vehicle; we just want all vehicles that share the same orders to form a contiguous range. */
246 std::stable_sort(this->vehicles.begin(), this->vehicles.end(), [](const Vehicle * const &u, const Vehicle * const &v) {
247 return u->FirstShared() < v->FirstShared();
248 });
249
250 uint max_num_vehicles = 0;
251
252 VehicleList::const_iterator begin = this->vehicles.begin();
253 while (begin != this->vehicles.end()) {
254 VehicleList::const_iterator end = std::find_if_not(begin, this->vehicles.cend(), [first_shared = (*begin)->FirstShared()](const Vehicle * const &v) {
255 return v->FirstShared() == first_shared;
256 });
257
258 this->vehgroups.emplace_back(begin, end);
259
260 max_num_vehicles = std::max<uint>(max_num_vehicles, static_cast<uint>(end - begin));
261
262 begin = end;
263 }
264
265 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_num_vehicles);
266 }
267 this->FilterVehicleList();
268
269 this->vehgroups.RebuildDone();
270 this->vscroll->SetCount(this->vehgroups.size());
271}
272
280static bool CargoFilterSingle(const Vehicle *v, const CargoType cargo_type)
281{
282 if (cargo_type == CargoFilterCriteria::CF_ANY) {
283 return true;
284 } else if (cargo_type == CargoFilterCriteria::CF_NONE) {
285 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
286 if (w->cargo_cap > 0) {
287 return false;
288 }
289 }
290 return true;
291 } else if (cargo_type == CargoFilterCriteria::CF_FREIGHT) {
292 bool have_capacity = false;
293 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
294 if (w->cargo_cap > 0) {
295 if (IsCargoInClass(w->cargo_type, CargoClass::Passengers)) {
296 return false;
297 } else {
298 have_capacity = true;
299 }
300 }
301 }
302 return have_capacity;
303 } else {
304 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
305 if (w->cargo_cap > 0 && w->cargo_type == cargo_type) {
306 return true;
307 }
308 }
309 return false;
310 }
311}
312
320static bool CargoFilter(const GUIVehicleGroup *vehgroup, const CargoType cargo_type)
321{
322 auto it = vehgroup->vehicles_begin;
323
324 /* Check if any vehicle in the group matches; if so, the whole group does. */
325 for (; it != vehgroup->vehicles_end; it++) {
326 if (CargoFilterSingle(*it, cargo_type)) return true;
327 }
328
329 return false;
330}
331
337{
339}
340
348void AddCargoIconOverlay(std::vector<CargoIconOverlay> &overlays, int x, int width, const Vehicle *v)
349{
350 bool rtl = _current_text_dir == TD_RTL;
351 if (!v->IsArticulatedPart() || v->cargo_type != v->Previous()->cargo_type) {
352 /* Add new overlay slot. */
353 overlays.emplace_back(rtl ? x - width : x, rtl ? x : x + width, v->cargo_type, v->cargo_cap);
354 } else {
355 /* This is an articulated part with the same cargo type, adjust left or right of last overlay slot. */
356 if (rtl) {
357 overlays.back().left -= width;
358 } else {
359 overlays.back().right += width;
360 }
361 overlays.back().cargo_cap += v->cargo_cap;
362 }
363}
364
371void DrawCargoIconOverlay(int x, int y, CargoType cargo_type)
372{
373 if (!ShowCargoIconOverlay()) return;
374 if (!IsValidCargoType(cargo_type)) return;
375
376 const CargoSpec *cs = CargoSpec::Get(cargo_type);
377
378 SpriteID spr = cs->GetCargoIcon();
379 if (spr == 0) return;
380
381 Dimension d = GetSpriteSize(spr);
382 d.width /= 2;
383 d.height /= 2;
384 int one = ScaleGUITrad(1);
385
386 /* Draw the cargo icon in black shifted 4 times to create the outline. */
387 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width - one, y - d.height);
388 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width + one, y - d.height);
389 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height - one);
390 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height + one);
391 /* Draw the cargo icon normally. */
392 DrawSprite(spr, PAL_NONE, x - d.width, y - d.height);
393}
394
400void DrawCargoIconOverlays(std::span<const CargoIconOverlay> overlays, int y)
401{
402 for (const auto &cio : overlays) {
403 if (cio.cargo_cap == 0) continue;
404 DrawCargoIconOverlay((cio.left + cio.right) / 2, y, cio.cargo_type);
405 }
406}
407
408static GUIVehicleGroupList::FilterFunction * const _vehicle_group_filter_funcs[] = {
410};
411
417{
418 if (this->cargo_filter_criteria != cargo_type) {
419 this->cargo_filter_criteria = cargo_type;
420 /* Deactivate filter if criteria is 'Show All', activate it otherwise. */
422 this->vehgroups.SetFilterType(0);
423 this->vehgroups.ForceRebuild();
424 }
425}
426
436
441{
443 if (this->vehicles.empty()) {
444 /* No vehicle passed through the filter, invalidate the previously selected vehicle */
445 this->vehicle_sel = VehicleID::Invalid();
446 } 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
447 this->vehicle_sel = VehicleID::Invalid();
448 }
449}
450
458Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create)
459{
460 Dimension d = {0, 0};
461
464 d = maxdim(d, GetStringBoundingBox(this->vehicle_depot_name[this->vli.vtype]));
465
466 if (show_group) {
469 } else if (show_create) {
471 }
472
473 return d;
474}
475
481
482StringID BaseVehicleListWindow::GetCargoFilterLabel(CargoType cargo_type) const
483{
484 switch (cargo_type) {
488 default: return CargoSpec::Get(cargo_type)->name;
489 }
490}
491
498{
499 DropDownList list;
500
501 /* Add item for disabling filtering. */
502 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
503 /* Add item for freight (i.e. vehicles with cargo capacity and with no passenger capacity). */
504 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_FREIGHT), CargoFilterCriteria::CF_FREIGHT));
505 /* Add item for vehicles not carrying anything, e.g. train engines. */
506 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
507
508 /* Add cargos */
510 for (const CargoSpec *cs : _sorted_cargo_specs) {
511 if (!full && !HasBit(this->used_cargoes, cs->Index())) continue;
512 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index(), false, !HasBit(this->used_cargoes, cs->Index())));
513 }
514
515 return list;
516}
517
525DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create)
526{
527 DropDownList list;
528
529 /* Autoreplace actions. */
530 if (show_autoreplace) {
531 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_REPLACE_VEHICLES, ADI_REPLACE));
532 list.push_back(MakeDropDownListDividerItem());
533 }
534
535 /* Group actions. */
536 if (show_group) {
537 list.push_back(MakeDropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE, ADI_ADD_SHARED));
538 list.push_back(MakeDropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, ADI_REMOVE_ALL));
539 list.push_back(MakeDropDownListDividerItem());
540 } else if (show_create) {
541 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_CREATE_GROUP, ADI_CREATE_GROUP));
542 list.push_back(MakeDropDownListDividerItem());
543 }
544
545 /* Depot actions. */
546 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_SEND_FOR_SERVICING, ADI_SERVICE));
547 list.push_back(MakeDropDownListStringItem(this->vehicle_depot_name[this->vli.vtype], ADI_DEPOT));
548
549 return list;
550}
551
552/* cached values for VehicleNameSorter to spare many GetString() calls */
553static const Vehicle *_last_vehicle[2] = { nullptr, nullptr };
554
555void BaseVehicleListWindow::SortVehicleList()
556{
557 if (this->vehgroups.Sort()) return;
558
559 /* invalidate cached values for name sorter - vehicle names could change */
560 _last_vehicle[0] = _last_vehicle[1] = nullptr;
561}
562
563void DepotSortList(VehicleList *list)
564{
565 if (list->size() < 2) return;
566 std::sort(list->begin(), list->end(), &VehicleNumberSorter);
567}
568
570static void DrawVehicleProfitButton(TimerGameEconomy::Date age, Money display_profit_last_year, uint num_vehicles, int x, int y)
571{
572 SpriteID spr;
573
574 /* draw profit-based coloured icons */
575 if (age <= VEHICLE_PROFIT_MIN_AGE) {
576 spr = SPR_PROFIT_NA;
577 } else if (display_profit_last_year < 0) {
578 spr = SPR_PROFIT_NEGATIVE;
579 } else if (display_profit_last_year < VEHICLE_PROFIT_THRESHOLD * num_vehicles) {
580 spr = SPR_PROFIT_SOME;
581 } else {
582 spr = SPR_PROFIT_LOT;
583 }
584 DrawSprite(spr, PAL_NONE, x, y);
585}
586
588static const uint MAX_REFIT_CYCLE = 256;
589
599uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_cargo_type)
600{
601 v_from = v_from->GetFirstEnginePart();
602 v_for = v_for->GetFirstEnginePart();
603
604 /* Create a list of subtypes used by the various parts of v_for */
605 static std::vector<StringID> subtypes;
606 subtypes.clear();
607 for (; v_from != nullptr; v_from = v_from->HasArticulatedPart() ? v_from->GetNextArticulatedPart() : nullptr) {
608 const Engine *e_from = v_from->GetEngine();
609 if (!e_from->CanCarryCargo() || !e_from->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
610 include(subtypes, GetCargoSubtypeText(v_from));
611 }
612
613 uint8_t ret_refit_cyc = 0;
614 bool success = false;
615 if (!subtypes.empty()) {
616 /* Check whether any articulated part is refittable to 'dest_cargo_type' with a subtype listed in 'subtypes' */
617 for (Vehicle *v = v_for; v != nullptr; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr) {
618 const Engine *e = v->GetEngine();
619 if (!e->CanCarryCargo() || !e->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
620 if (!HasBit(e->info.refit_mask, dest_cargo_type) && v->cargo_type != dest_cargo_type) continue;
621
622 CargoType old_cargo_type = v->cargo_type;
623 uint8_t old_cargo_subtype = v->cargo_subtype;
624
625 /* Set the 'destination' cargo */
626 v->cargo_type = dest_cargo_type;
627
628 /* Cycle through the refits */
629 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
630 v->cargo_subtype = refit_cyc;
631
632 /* Make sure we don't pick up anything cached. */
633 v->First()->InvalidateNewGRFCache();
634 v->InvalidateNewGRFCache();
635
636 StringID subtype = GetCargoSubtypeText(v);
637 if (subtype == STR_EMPTY) break;
638
639 if (std::ranges::find(subtypes, subtype) == subtypes.end()) continue;
640
641 /* We found something matching. */
642 ret_refit_cyc = refit_cyc;
643 success = true;
644 break;
645 }
646
647 /* Reset the vehicle's cargo type */
648 v->cargo_type = old_cargo_type;
649 v->cargo_subtype = old_cargo_subtype;
650
651 /* Make sure we don't taint the vehicle. */
652 v->First()->InvalidateNewGRFCache();
653 v->InvalidateNewGRFCache();
654
655 if (success) break;
656 }
657 }
658
659 return ret_refit_cyc;
660}
661
665 uint8_t subtype;
667
673 inline bool operator == (const RefitOption &other) const
674 {
675 return other.cargo == this->cargo && other.string == this->string;
676 }
677};
678
679using RefitOptions = std::map<CargoType, std::vector<RefitOption>, CargoTypeComparator>;
680
690static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption *sel, uint pos, uint rows, uint delta, const Rect &r)
691{
692 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
693 uint current = 0;
694
695 bool rtl = _current_text_dir == TD_RTL;
696 uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
697 uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
698 int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
699
700 int iconleft = rtl ? ir.right - iconwidth : ir.left;
701 int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;
702 int iconinner = rtl ? ir.right - iconwidth : ir.left + iconwidth;
703
704 Rect tr = ir.Indent(iconwidth + WidgetDimensions::scaled.hsep_wide, rtl);
705
706 /* Draw the list of subtypes for each cargo, and find the selected refit option (by its position). */
707 for (const auto &pair : refits) {
708 bool has_subtypes = pair.second.size() > 1;
709 for (const RefitOption &refit : pair.second) {
710 if (current >= pos + rows) break;
711
712 /* Hide subtypes if selected cargo type does not match */
713 if ((sel == nullptr || sel->cargo != refit.cargo) && refit.subtype != UINT8_MAX) continue;
714
715 /* Refit options with a position smaller than pos don't have to be drawn. */
716 if (current < pos) {
717 current++;
718 continue;
719 }
720
721 if (has_subtypes) {
722 if (refit.subtype != UINT8_MAX) {
723 /* Draw tree lines */
724 int ycenter = tr.top + GetCharacterHeight(FS_NORMAL) / 2;
725 GfxDrawLine(iconcenter, tr.top - WidgetDimensions::scaled.matrix.top, iconcenter, (&refit == &pair.second.back()) ? ycenter : tr.top - WidgetDimensions::scaled.matrix.top + delta - 1, linecolour);
726 GfxDrawLine(iconcenter, ycenter, iconinner, ycenter, linecolour);
727 } else {
728 /* Draw expand/collapse icon */
729 DrawSprite((sel != nullptr && sel->cargo == refit.cargo) ? SPR_CIRCLE_UNFOLDED : SPR_CIRCLE_FOLDED, PAL_NONE, iconleft, tr.top + (GetCharacterHeight(FS_NORMAL) - iconheight) / 2);
730 }
731 }
732
733 TextColour colour = (sel != nullptr && sel->cargo == refit.cargo && sel->subtype == refit.subtype) ? TC_WHITE : TC_BLACK;
734 /* Get the cargo name. */
735 DrawString(tr, GetString(STR_JUST_STRING_STRING, CargoSpec::Get(refit.cargo)->name, refit.string), colour);
736
737 tr.top += delta;
738 current++;
739 }
740 }
741}
742
744struct RefitWindow : public Window {
745 const RefitOption *selected_refit = nullptr;
749 Scrollbar *vscroll = nullptr;
750 Scrollbar *hscroll = nullptr;
752 int sprite_left = 0;
753 int sprite_right = 0;
754 uint vehicle_margin = 0;
755 int click_x = 0;
758 bool auto_refit = false;
759
764 {
765 /* Store the currently selected RefitOption. */
766 std::optional<RefitOption> current_refit_option;
767 if (this->selected_refit != nullptr) current_refit_option = *(this->selected_refit);
768 this->selected_refit = nullptr;
769
770 this->refit_list.clear();
772
773 /* Check only the selected vehicles. */
774 VehicleSet vehicles_to_refit;
776
777 do {
778 if (v->type == VEH_TRAIN && std::ranges::find(vehicles_to_refit, v->index) == vehicles_to_refit.end()) continue;
779 const Engine *e = v->GetEngine();
780 CargoTypes cmask = e->info.refit_mask;
781 VehicleCallbackMasks callback_mask = e->info.callback_mask;
782
783 /* Skip this engine if it does not carry anything */
784 if (!e->CanCarryCargo()) continue;
785 /* Skip this engine if we build the list for auto-refitting and engine doesn't allow it. */
786 if (this->auto_refit && !e->info.misc_flags.Test(EngineMiscFlag::AutoRefit)) continue;
787
788 /* Loop through all cargoes in the refit mask */
789 for (const auto &cs : _sorted_cargo_specs) {
790 CargoType cargo_type = cs->Index();
791 /* Skip cargo type if it's not listed */
792 if (!HasBit(cmask, cargo_type)) continue;
793
794 auto &list = this->refit_list[cargo_type];
795 bool first_vehicle = list.empty();
796 if (first_vehicle) {
797 /* Keeping the current subtype is always an option. It also serves as the option in case of no subtypes */
798 list.push_back({cargo_type, UINT8_MAX, STR_EMPTY});
799 }
800
801 /* Check the vehicle's callback mask for cargo suffixes.
802 * This is not supported for ordered refits, since subtypes only have a meaning
803 * for a specific vehicle at a specific point in time, which conflicts with shared orders,
804 * autoreplace, autorenew, clone, order restoration, ... */
805 if (this->order == INVALID_VEH_ORDER_ID && callback_mask.Test(VehicleCallbackMask::CargoSuffix)) {
806 /* Make a note of the original cargo type. It has to be
807 * changed to test the cargo & subtype... */
810
811 v->cargo_type = cargo_type;
812
813 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
815
816 /* Make sure we don't pick up anything cached. */
819
820 StringID subtype = GetCargoSubtypeText(v);
821
822 if (first_vehicle) {
823 /* Append new subtype (don't add duplicates though) */
824 if (subtype == STR_EMPTY) break;
825
827 option.cargo = cargo_type;
828 option.subtype = refit_cyc;
829 option.string = subtype;
830 include(list, option);
831 } else {
832 /* Intersect the subtypes of earlier vehicles with the subtypes of this vehicle */
833 if (subtype == STR_EMPTY) {
834 /* No more subtypes for this vehicle, delete all subtypes >= refit_cyc */
835 /* UINT8_MAX item is in front, other subtypes are sorted. So just truncate the list in the right spot */
836 for (uint i = 1; i < list.size(); i++) {
837 if (list[i].subtype >= refit_cyc) {
838 list.resize(i);
839 break;
840 }
841 }
842 break;
843 } else {
844 /* Check whether the subtype matches with the subtype of earlier vehicles. */
845 uint pos = 1;
846 while (pos < list.size() && list[pos].subtype != refit_cyc) pos++;
847 if (pos < list.size() && list[pos].string != subtype) {
848 /* String mismatch, remove item keeping the order */
849 list.erase(list.begin() + pos);
850 }
851 }
852 }
853 }
854
855 /* Reset the vehicle's cargo type */
858
859 /* And make sure we haven't tainted the cache */
862 }
863 }
864 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
865
866 /* Restore the previously selected RefitOption. */
867 if (current_refit_option.has_value()) {
868 for (const auto &pair : this->refit_list) {
869 for (const auto &refit : pair.second) {
870 if (refit.cargo == current_refit_option->cargo && refit.subtype == current_refit_option->subtype) {
871 this->selected_refit = &refit;
872 break;
873 }
874 }
875 if (this->selected_refit != nullptr) break;
876 }
877 }
878
879 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
880 }
881
886 {
887 size_t scroll_row = 0;
888 size_t rows = 0;
889 CargoType cargo = this->selected_refit == nullptr ? INVALID_CARGO : this->selected_refit->cargo;
890
891 for (const auto &pair : this->refit_list) {
892 if (pair.first == cargo) {
893 /* selected_refit points to an element in the vector so no need to search for it. */
894 scroll_row = rows + (this->selected_refit - pair.second.data());
895 rows += pair.second.size();
896 } else {
897 rows++; /* Unselected cargo type is collapsed into one row. */
898 }
899 }
900
901 this->vscroll->SetCount(rows);
902 this->vscroll->ScrollTowards(static_cast<int>(scroll_row));
903 }
904
910 {
911 uint row = 0;
912
913 for (const auto &pair : refit_list) {
914 for (const RefitOption &refit : pair.second) {
915 if (row == click_row) {
916 this->selected_refit = &refit;
917 return;
918 }
919 row++;
920 /* If this cargo type is not already selected then its subtypes are not visible, so skip the rest. */
921 if (this->selected_refit == nullptr || this->selected_refit->cargo != refit.cargo) break;
922 }
923 }
924
925 /* No selection made */
926 this->selected_refit = nullptr;
927 }
928
929 RefitWindow(WindowDesc &desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window(desc)
930 {
931 this->auto_refit = auto_refit;
932 this->order = order;
933 this->CreateNestedTree();
934
935 this->vscroll = this->GetScrollbar(WID_VR_SCROLLBAR);
936 this->hscroll = (v->IsGroundVehicle() ? this->GetScrollbar(WID_VR_HSCROLLBAR) : nullptr);
943
944 this->FinishInitNested(v->index);
945 this->owner = v->owner;
946
947 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
948 }
949
950 void OnInit() override
951 {
952 /* (Re)build the refit list */
954 }
955
956 void OnPaint() override
957 {
958 /* Determine amount of items for scroller. */
959 if (this->hscroll != nullptr) this->hscroll->SetCount(this->vehicle_width);
960
961 /* Calculate sprite position. */
963 int sprite_width = std::max(0, ((int)vehicle_panel_display->current_x - this->vehicle_width) / 2);
964 this->sprite_left = vehicle_panel_display->pos_x;
965 this->sprite_right = vehicle_panel_display->pos_x + vehicle_panel_display->current_x - 1;
966 if (_current_text_dir == TD_RTL) {
967 this->sprite_right -= sprite_width;
968 this->vehicle_margin = vehicle_panel_display->current_x - sprite_right;
969 } else {
970 this->sprite_left += sprite_width;
971 this->vehicle_margin = sprite_left;
972 }
973
974 this->DrawWidgets();
975 }
976
978 {
979 switch (widget) {
980 case WID_VR_MATRIX:
981 resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
982 size.height = resize.height * 8;
983 break;
984
986 size.height = ScaleGUITrad(GetVehicleHeight(Vehicle::Get(this->window_number)->type));
987 break;
988
989 case WID_VR_INFO:
990 size.width = this->information_width + padding.height;
991 break;
992 }
993 }
994
995 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
996 {
997 if (widget == WID_VR_CAPTION) return GetString(STR_REFIT_CAPTION, Vehicle::Get(this->window_number)->index);
998
999 return this->Window::GetWidgetString(widget, stringid);
1000 }
1001
1008 std::string GetCapacityString(const RefitOption &option) const
1009 {
1011 auto [cost, refit_capacity, mail_capacity, cargo_capacities] = Command<CMD_REFIT_VEHICLE>::Do(DoCommandFlag::QueryCost, this->selected_vehicle, option.cargo, option.subtype, this->auto_refit, false, this->num_vehicles);
1012
1013 if (cost.Failed()) return {};
1014
1015 Money money = cost.GetCost();
1016 if (mail_capacity > 0) {
1017 if (this->order != INVALID_VEH_ORDER_ID) {
1018 /* No predictable cost */
1019 return GetString(STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity);
1020 }
1021
1022 if (money <= 0) {
1023 return GetString(STR_REFIT_NEW_CAPACITY_INCOME_FROM_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, -money);
1024 }
1025
1026 return GetString(STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, money);
1027 }
1028
1029 if (this->order != INVALID_VEH_ORDER_ID) {
1030 /* No predictable cost */
1032 }
1033
1034 if (money <= 0) {
1036 }
1037
1039 }
1040
1041 void DrawWidget(const Rect &r, WidgetID widget) const override
1042 {
1043 switch (widget) {
1046 DrawVehicleImage(v, {this->sprite_left, r.top, this->sprite_right, r.bottom},
1047 VehicleID::Invalid(), EIT_IN_DETAILS, this->hscroll != nullptr ? this->hscroll->GetPosition() : 0);
1048
1049 /* Highlight selected vehicles. */
1050 if (this->order != INVALID_VEH_ORDER_ID) break;
1051 int x = 0;
1052 switch (v->type) {
1053 case VEH_TRAIN: {
1054 VehicleSet vehicles_to_refit;
1055 GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
1056
1057 int left = INT32_MIN;
1058 int width = 0;
1059
1060 /* Determine top & bottom position of the highlight.*/
1061 const int height = ScaleSpriteTrad(12);
1062 const int highlight_top = CenterBounds(r.top, r.bottom, height);
1063 const int highlight_bottom = highlight_top + height - 1;
1064
1065 for (Train *u = Train::From(v); u != nullptr; u = u->Next()) {
1066 /* Start checking. */
1067 const bool contained = std::ranges::find(vehicles_to_refit, u->index) != vehicles_to_refit.end();
1068 if (contained && left == INT32_MIN) {
1069 left = x - this->hscroll->GetPosition() + r.left + this->vehicle_margin;
1070 width = 0;
1071 }
1072
1073 /* Draw a selection. */
1074 if ((!contained || u->Next() == nullptr) && left != INT32_MIN) {
1075 if (u->Next() == nullptr && contained) {
1076 int current_width = u->GetDisplayImageWidth();
1078 x += current_width;
1079 }
1080
1081 int right = Clamp(left + width, 0, r.right);
1082 left = std::max(0, left);
1083
1084 if (_current_text_dir == TD_RTL) {
1085 right = r.Width() - left;
1086 left = right - width;
1087 }
1088
1089 if (left != right) {
1092 }
1093
1094 left = INT32_MIN;
1095 }
1096
1097 int current_width = u->GetDisplayImageWidth();
1099 x += current_width;
1100 }
1101 break;
1102 }
1103
1104 default: break;
1105 }
1106 break;
1107 }
1108
1109 case WID_VR_MATRIX:
1110 DrawVehicleRefitWindow(this->refit_list, this->selected_refit, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->resize.step_height, r);
1111 break;
1112
1113 case WID_VR_INFO:
1114 if (this->selected_refit != nullptr) {
1115 std::string string = this->GetCapacityString(*this->selected_refit);
1116 if (!string.empty()) {
1118 }
1119 }
1120 break;
1121 }
1122 }
1123
1129 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1130 {
1131 switch (data) {
1132 case VIWD_AUTOREPLACE: // Autoreplace replaced the vehicle; selected_vehicle became invalid.
1133 case VIWD_CONSIST_CHANGED: { // The consist has changed; rebuild the entire list.
1134 /* Clear the selection. */
1136 this->selected_vehicle = v->index;
1137 this->num_vehicles = UINT8_MAX;
1138 [[fallthrough]];
1139 }
1140
1141 case 2: { // The vehicle selection has changed; rebuild the entire list.
1142 if (!gui_scope) break;
1143 this->BuildRefitList();
1144
1145 /* The vehicle width has changed too. */
1146 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1147 uint max_width = 0;
1148
1149 /* Check the width of all cargo information strings. */
1150 for (const auto &list : this->refit_list) {
1151 for (const RefitOption &refit : list.second) {
1152 std::string string = this->GetCapacityString(refit);
1153 if (!string.empty()) {
1154 Dimension dim = GetStringBoundingBox(string);
1155 max_width = std::max(dim.width, max_width);
1156 }
1157 }
1158 }
1159
1160 if (this->information_width < max_width) {
1161 this->information_width = max_width;
1162 this->ReInit();
1163 }
1164 [[fallthrough]];
1165 }
1166
1167 case 1: // A new cargo has been selected.
1168 if (!gui_scope) break;
1169 this->RefreshScrollbar();
1170 break;
1171 }
1172 }
1173
1174 int GetClickPosition(int click_x)
1175 {
1177 if (_current_text_dir == TD_RTL) click_x = matrix_widget->current_x - click_x;
1178 click_x -= this->vehicle_margin;
1179 if (this->hscroll != nullptr) click_x += this->hscroll->GetPosition();
1180
1181 return click_x;
1182 }
1183
1184 void SetSelectedVehicles(int drag_x)
1185 {
1186 drag_x = GetClickPosition(drag_x);
1187
1188 int left_x = std::min(this->click_x, drag_x);
1189 int right_x = std::max(this->click_x, drag_x);
1190 this->num_vehicles = 0;
1191
1193 /* Find the vehicle part that was clicked. */
1194 switch (v->type) {
1195 case VEH_TRAIN: {
1196 /* Don't select anything if we are not clicking in the vehicle. */
1197 if (left_x >= 0) {
1198 const Train *u = Train::From(v);
1199 bool start_counting = false;
1200 for (; u != nullptr; u = u->Next()) {
1204
1205 if (left_x < 0 && !start_counting) {
1206 this->selected_vehicle = u->index;
1207 start_counting = true;
1208
1209 /* Count the first vehicle, even if articulated part */
1210 this->num_vehicles++;
1211 } else if (start_counting && !u->IsArticulatedPart()) {
1212 /* Do not count articulated parts */
1213 this->num_vehicles++;
1214 }
1215
1216 if (right_x < 0) break;
1217 }
1218 }
1219
1220 /* If the selection is not correct, clear it. */
1221 if (this->num_vehicles != 0) {
1222 if (_ctrl_pressed) this->num_vehicles = UINT8_MAX;
1223 break;
1224 }
1225 [[fallthrough]];
1226 }
1227
1228 default:
1229 /* Clear the selection. */
1230 this->selected_vehicle = v->index;
1231 this->num_vehicles = UINT8_MAX;
1232 break;
1233 }
1234 }
1235
1236 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1237 {
1238 switch (widget) {
1239 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1240 if (this->order != INVALID_VEH_ORDER_ID) break;
1242 this->click_x = GetClickPosition(pt.x - nwi->pos_x);
1243 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1245 if (!_ctrl_pressed) {
1247 } else {
1248 /* The vehicle selection has changed. */
1249 this->InvalidateData(2);
1250 }
1251 break;
1252 }
1253
1254 case WID_VR_MATRIX: { // listbox
1255 this->SetSelection(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_VR_MATRIX));
1256 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
1257 this->InvalidateData(1);
1258
1259 if (click_count == 1) break;
1260 [[fallthrough]];
1261 }
1262
1263 case WID_VR_REFIT: // refit button
1264 if (this->selected_refit != nullptr) {
1265 const Vehicle *v = Vehicle::Get(this->window_number);
1266
1267 if (this->order == INVALID_VEH_ORDER_ID) {
1268 bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
1269 if (Command<CMD_REFIT_VEHICLE>::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();
1270 } else {
1271 if (Command<CMD_ORDER_REFIT>::Post(v->tile, v->index, this->order, this->selected_refit->cargo)) this->Close();
1272 }
1273 }
1274 break;
1275 }
1276 }
1277
1278 void OnMouseDrag(Point pt, WidgetID widget) override
1279 {
1280 switch (widget) {
1281 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1282 if (this->order != INVALID_VEH_ORDER_ID) break;
1284 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1286 break;
1287 }
1288 }
1289 }
1290
1291 void OnDragDrop(Point pt, WidgetID widget) override
1292 {
1293 switch (widget) {
1294 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1295 if (this->order != INVALID_VEH_ORDER_ID) break;
1297 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1298 this->InvalidateData(2);
1299 break;
1300 }
1301 }
1302 }
1303
1304 void OnResize() override
1305 {
1306 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1307 this->vscroll->SetCapacityFromWidget(this, WID_VR_MATRIX);
1308 if (this->hscroll != nullptr) this->hscroll->SetCapacityFromWidget(this, WID_VR_VEHICLE_PANEL_DISPLAY);
1309 }
1310};
1311
1312static constexpr NWidgetPart _nested_vehicle_refit_widgets[] = {
1314 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1315 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VR_CAPTION),
1316 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1317 EndContainer(),
1318 /* Vehicle display + scrollbar. */
1323 EndContainer(),
1324 EndContainer(),
1325 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_VR_SELECT_HEADER), SetStringTip(STR_REFIT_TITLE), SetResize(1, 0),
1326 /* Matrix + scrollbar. */
1330 EndContainer(),
1331 NWidget(WWT_PANEL, COLOUR_GREY, WID_VR_INFO), SetMinimalTextLines(2, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(),
1333 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VR_REFIT), SetFill(1, 0), SetResize(1, 0),
1334 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1335 EndContainer(),
1336};
1337
1338static WindowDesc _vehicle_refit_desc(
1339 WDP_AUTO, "view_vehicle_refit", 240, 174,
1342 _nested_vehicle_refit_widgets
1343);
1344
1352void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
1353{
1355 RefitWindow *w = new RefitWindow(_vehicle_refit_desc, v, order, auto_refit);
1356 w->parent = parent;
1357}
1358
1360uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
1361{
1362 /* List of cargo types of this engine */
1363 CargoTypes cmask = GetUnionOfArticulatedRefitMasks(engine, false);
1364 /* List of cargo types available in this climate */
1365 CargoTypes lmask = _cargo_mask;
1366
1367 /* Draw nothing if the engine is not refittable */
1368 if (HasAtMostOneBit(cmask)) return y;
1369
1370 std::string str;
1371 if (cmask == lmask) {
1372 /* Engine can be refitted to all types in this climate */
1373 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_TYPES, std::monostate{});
1374 } else {
1375 /* Check if we are able to refit to more cargo types and unable to. If
1376 * so, invert the cargo types to list those that we can't refit to. */
1377 if (CountBits(cmask ^ lmask) < CountBits(cmask) && CountBits(cmask ^ lmask) <= 7) {
1378 cmask ^= lmask;
1379 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_BUT, cmask);
1380 } else {
1381 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_JUST_CARGO_LIST, cmask);
1382 }
1383 }
1384
1385 return DrawStringMultiLine(left, right, y, INT32_MAX, str);
1386}
1387
1390{
1392 uint16_t cb = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v);
1393 if (cb != CALLBACK_FAILED) {
1395 if (cb >= 0x400 || (v->GetGRF()->grf_version < 8 && cb == 0xFF)) cb = CALLBACK_FAILED;
1396 }
1397 if (cb != CALLBACK_FAILED) {
1398 return GetGRFStringID(v->GetGRFID(), GRFSTR_MISC_GRF_TEXT + cb);
1399 }
1400 }
1401 return STR_EMPTY;
1402}
1403
1405static bool VehicleGroupLengthSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1406{
1407 return a.NumVehicles() < b.NumVehicles();
1408}
1409
1411static bool VehicleGroupTotalProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1412{
1413 return a.GetDisplayProfitThisYear() < b.GetDisplayProfitThisYear();
1414}
1415
1417static bool VehicleGroupTotalProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1418{
1419 return a.GetDisplayProfitLastYear() < b.GetDisplayProfitLastYear();
1420}
1421
1423static bool VehicleGroupAverageProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1424{
1425 return a.GetDisplayProfitThisYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitThisYear() * static_cast<uint>(a.NumVehicles());
1426}
1427
1429static bool VehicleGroupAverageProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1430{
1431 return a.GetDisplayProfitLastYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitLastYear() * static_cast<uint>(a.NumVehicles());
1432}
1433
1435static bool VehicleNumberSorter(const Vehicle * const &a, const Vehicle * const &b)
1436{
1437 return a->unitnumber < b->unitnumber;
1438}
1439
1441static bool VehicleNameSorter(const Vehicle * const &a, const Vehicle * const &b)
1442{
1443 static std::string last_name[2] = { {}, {} };
1444
1445 if (a != _last_vehicle[0]) {
1446 _last_vehicle[0] = a;
1447 last_name[0] = GetString(STR_VEHICLE_NAME, a->index);
1448 }
1449
1450 if (b != _last_vehicle[1]) {
1451 _last_vehicle[1] = b;
1452 last_name[1] = GetString(STR_VEHICLE_NAME, b->index);
1453 }
1454
1455 int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
1456 return (r != 0) ? r < 0: VehicleNumberSorter(a, b);
1457}
1458
1460static bool VehicleAgeSorter(const Vehicle * const &a, const Vehicle * const &b)
1461{
1462 auto r = a->age - b->age;
1463 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1464}
1465
1467static bool VehicleProfitThisYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1468{
1469 int r = ClampTo<int32_t>(a->GetDisplayProfitThisYear() - b->GetDisplayProfitThisYear());
1470 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1471}
1472
1474static bool VehicleProfitLastYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1475{
1476 int r = ClampTo<int32_t>(a->GetDisplayProfitLastYear() - b->GetDisplayProfitLastYear());
1477 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1478}
1479
1481static bool VehicleCargoSorter(const Vehicle * const &a, const Vehicle * const &b)
1482{
1483 const Vehicle *v;
1484 CargoArray diff{};
1485
1486 /* Append the cargo of the connected waggons */
1487 for (v = a; v != nullptr; v = v->Next()) diff[v->cargo_type] += v->cargo_cap;
1488 for (v = b; v != nullptr; v = v->Next()) diff[v->cargo_type] -= v->cargo_cap;
1489
1490 int r = 0;
1491 for (uint d : diff) {
1492 r = d;
1493 if (r != 0) break;
1494 }
1495
1496 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1497}
1498
1500static bool VehicleReliabilitySorter(const Vehicle * const &a, const Vehicle * const &b)
1501{
1502 int r = a->reliability - b->reliability;
1503 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1504}
1505
1507static bool VehicleMaxSpeedSorter(const Vehicle * const &a, const Vehicle * const &b)
1508{
1510 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1511}
1512
1514static bool VehicleModelSorter(const Vehicle * const &a, const Vehicle * const &b)
1515{
1516 int r = a->engine_type.base() - b->engine_type.base();
1517 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1518}
1519
1521static bool VehicleValueSorter(const Vehicle * const &a, const Vehicle * const &b)
1522{
1523 const Vehicle *u;
1524 Money diff = 0;
1525
1526 for (u = a; u != nullptr; u = u->Next()) diff += u->value;
1527 for (u = b; u != nullptr; u = u->Next()) diff -= u->value;
1528
1529 int r = ClampTo<int32_t>(diff);
1530 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1531}
1532
1534static bool VehicleLengthSorter(const Vehicle * const &a, const Vehicle * const &b)
1535{
1537 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1538}
1539
1541static bool VehicleTimeToLiveSorter(const Vehicle * const &a, const Vehicle * const &b)
1542{
1543 int r = ClampTo<int32_t>((a->max_age - a->age) - (b->max_age - b->age));
1544 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1545}
1546
1548static bool VehicleTimetableDelaySorter(const Vehicle * const &a, const Vehicle * const &b)
1549{
1550 int r = a->lateness_counter - b->lateness_counter;
1551 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1552}
1553
1554void InitializeGUI()
1555{
1556 MemSetT(&_grouping, 0);
1557 MemSetT(&_sorting, 0);
1558}
1559
1566static inline void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
1567{
1568 Window *w = FindWindowById(window_class, from_index);
1569 if (w != nullptr) {
1570 /* Update window_number */
1571 w->window_number = to_index;
1572 if (w->viewport != nullptr) w->viewport->follow_vehicle = to_index;
1573
1574 /* Update vehicle drag data */
1575 if (_thd.window_class == window_class && _thd.window_number == from_index) {
1576 _thd.window_number = to_index;
1577 }
1578
1579 /* Notify the window. */
1581 }
1582}
1583
1590{
1591 ChangeVehicleWindow(WC_VEHICLE_VIEW, from_index, to_index);
1592 ChangeVehicleWindow(WC_VEHICLE_ORDERS, from_index, to_index);
1593 ChangeVehicleWindow(WC_VEHICLE_REFIT, from_index, to_index);
1594 ChangeVehicleWindow(WC_VEHICLE_DETAILS, from_index, to_index);
1595 ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index);
1596}
1597
1598static constexpr NWidgetPart _nested_vehicle_list[] = {
1600 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1602 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VL_CAPTION),
1605 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_ORDER_VIEW), SetMinimalSize(61, 14), SetStringTip(STR_GOTO_ORDER_VIEW, STR_GOTO_ORDER_VIEW_TOOLTIP),
1606 EndContainer(),
1607 EndContainer(),
1608 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1609 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1610 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1611 EndContainer(),
1612
1615 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_VL_GROUP_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
1616 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_SORT_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1617 EndContainer(),
1619 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_GROUP_BY_PULLDOWN), SetMinimalSize(0, 12), SetFill(1, 1), SetToolTip(STR_TOOLTIP_GROUP_ORDER),
1620 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_SORT_BY_PULLDOWN), SetMinimalSize(0, 12), SetFill(1, 1), SetToolTip(STR_TOOLTIP_SORT_CRITERIA),
1621 EndContainer(),
1623 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1626 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_FILTER_BY_CARGO), SetMinimalSize(0, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1627 EndContainer(),
1628 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1629 EndContainer(),
1630 EndContainer(),
1631 EndContainer(),
1632
1636 EndContainer(),
1637
1639 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_VL_HIDE_BUTTONS),
1642 SetToolTip(STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
1643 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetResize(1, 0), SetFill(1, 1), EndContainer(),
1645 SetStringTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
1646 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VL_STOP_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1647 SetSpriteTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
1648 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VL_START_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1649 SetSpriteTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
1650 EndContainer(),
1651 /* Widget to be shown for other companies hiding the previous 5 widgets. */
1652 NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1653 EndContainer(),
1654 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1655 EndContainer(),
1656};
1657
1658static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, uint order_arrow_width, VehicleOrderID start)
1659{
1660 const Order *order = v->GetOrder(start);
1661 if (order == nullptr) return;
1662
1663 bool rtl = _current_text_dir == TD_RTL;
1664 int l_offset = rtl ? 0 : order_arrow_width;
1665 int r_offset = rtl ? order_arrow_width : 0;
1666 int i = 0;
1667 VehicleOrderID oid = start;
1668
1669 do {
1670 if (oid == v->cur_real_order_index) DrawString(left, right, y, STR_JUST_RIGHT_ARROW, TC_BLACK, SA_LEFT, false, FS_SMALL);
1671
1672 if (order->IsType(OT_GOTO_STATION)) {
1673 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order->GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
1674
1676 if (++i == 4) break;
1677 }
1678
1679 oid++;
1680 order = order->next;
1681 if (order == nullptr) {
1682 order = v->orders->GetFirstOrder();
1683 oid = 0;
1684 }
1685 } while (oid != start);
1686}
1687
1689static void DrawSmallOrderList(const Order *order, int left, int right, int y, uint order_arrow_width)
1690{
1691 bool rtl = _current_text_dir == TD_RTL;
1692 int l_offset = rtl ? 0 : order_arrow_width;
1693 int r_offset = rtl ? order_arrow_width : 0;
1694 int i = 0;
1695 while (order != nullptr) {
1696 if (order->IsType(OT_GOTO_STATION)) {
1697 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order->GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
1698
1700 if (++i == 4) break;
1701 }
1702 order = order->next;
1703 }
1704}
1705
1713void DrawVehicleImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip)
1714{
1715 switch (v->type) {
1716 case VEH_TRAIN: DrawTrainImage(Train::From(v), r, selection, image_type, skip); break;
1717 case VEH_ROAD: DrawRoadVehImage(v, r, selection, image_type, skip); break;
1718 case VEH_SHIP: DrawShipImage(v, r, selection, image_type); break;
1719 case VEH_AIRCRAFT: DrawAircraftImage(v, r, selection, image_type); break;
1720 default: NOT_REACHED();
1721 }
1722}
1723
1730uint GetVehicleListHeight(VehicleType type, uint divisor)
1731{
1732 /* Name + vehicle + profit */
1734 /* Drawing of the 4 small orders + profit*/
1735 if (type >= VEH_SHIP) base = std::max(base, 6U * GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.matrix.Vertical());
1736
1737 if (divisor == 1) return base;
1738
1739 /* Make sure the height is dividable by divisor */
1740 uint rem = base % divisor;
1741 return base + (rem == 0 ? 0 : divisor - rem);
1742}
1743
1749static int GetUnitNumberWidth(int digits)
1750{
1751 return GetStringBoundingBox(GetString(STR_JUST_COMMA, GetParamMaxDigits(digits))).width;
1752}
1753
1760void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
1761{
1762 Rect ir = r.WithHeight(line_height).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
1763 bool rtl = _current_text_dir == TD_RTL;
1764
1765 Dimension profit = GetSpriteSize(SPR_PROFIT_LOT);
1766 int text_offset = std::max<int>(profit.width, GetUnitNumberWidth(this->unitnumber_digits)) + WidgetDimensions::scaled.hsep_normal;
1767 Rect tr = ir.Indent(text_offset, rtl);
1768
1769 bool show_orderlist = this->vli.vtype >= VEH_SHIP;
1770 Rect olr = ir.Indent(std::max(ScaleGUITrad(100) + text_offset, ir.Width() / 2), rtl);
1771
1772 int image_left = (rtl && show_orderlist) ? olr.right : tr.left;
1773 int image_right = (!rtl && show_orderlist) ? olr.left : tr.right;
1774
1775 int vehicle_button_x = rtl ? ir.right - profit.width : ir.left;
1776
1777 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->vehgroups);
1778 for (auto it = first; it != last; ++it) {
1779 const GUIVehicleGroup &vehgroup = *it;
1780
1781 DrawString(tr.left, tr.right, ir.bottom - GetCharacterHeight(FS_SMALL) - WidgetDimensions::scaled.framerect.bottom,
1783 vehgroup.GetDisplayProfitThisYear(),
1784 vehgroup.GetDisplayProfitLastYear()));
1785
1786 DrawVehicleProfitButton(vehgroup.GetOldestVehicleAge(), vehgroup.GetDisplayProfitLastYear(), vehgroup.NumVehicles(), vehicle_button_x, ir.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal);
1787
1788 switch (this->grouping) {
1789 case GB_NONE: {
1790 const Vehicle *v = vehgroup.GetSingleVehicle();
1791
1793 DrawSprite(SPR_WARNING_SIGN, PAL_NONE, vehicle_button_x, ir.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal + profit.height);
1794 }
1795
1796 DrawVehicleImage(v, {image_left, ir.top, image_right, ir.bottom}, selected_vehicle, EIT_IN_LIST, 0);
1797
1799 /* Get the cargoes the vehicle can carry */
1800 CargoTypes vehicle_cargoes = 0;
1801
1802 for (auto u = v; u != nullptr; u = u->Next()) {
1803 if (u->cargo_cap == 0) continue;
1804
1806 }
1807
1808 if (!v->name.empty()) {
1809 /* The vehicle got a name so we will print it and the cargoes */
1810 DrawString(tr.left, tr.right, ir.top,
1812 TC_BLACK, SA_LEFT, false, FS_SMALL);
1813 } else if (v->group_id != DEFAULT_GROUP) {
1814 /* The vehicle has no name, but is member of a group, so print group name and the cargoes */
1815 DrawString(tr.left, tr.right, ir.top,
1817 TC_BLACK, SA_LEFT, false, FS_SMALL);
1818 } else {
1819 /* The vehicle has no name, and is not a member of a group, so just print the cargoes */
1820 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_LIST_CARGO, vehicle_cargoes), TC_BLACK, SA_LEFT, false, FS_SMALL);
1821 }
1822 } else if (!v->name.empty()) {
1823 /* The vehicle got a name so we will print it */
1824 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_NAME, v->index), TC_BLACK, SA_LEFT, false, FS_SMALL);
1825 } else if (v->group_id != DEFAULT_GROUP) {
1826 /* The vehicle has no name, but is member of a group, so print group name */
1827 DrawString(tr.left, tr.right, ir.top, GetString(STR_GROUP_NAME, v->group_id), TC_BLACK, SA_LEFT, false, FS_SMALL);
1828 }
1829
1830 if (show_orderlist) DrawSmallOrderList(v, olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width, v->cur_real_order_index);
1831
1832 TextColour tc;
1833 if (v->IsChainInDepot()) {
1834 tc = TC_BLUE;
1835 } else {
1836 tc = (v->age > v->max_age - CalendarTime::DAYS_IN_LEAP_YEAR) ? TC_RED : TC_BLACK;
1837 }
1838
1840 break;
1841 }
1842
1843 case GB_SHARED_ORDERS:
1844 assert(vehgroup.NumVehicles() > 0);
1845
1846 for (int i = 0; i < static_cast<int>(vehgroup.NumVehicles()); ++i) {
1847 if (image_left + WidgetDimensions::scaled.hsep_wide * i >= image_right) break; // Break if there is no more space to draw any more vehicles anyway.
1848 DrawVehicleImage(vehgroup.vehicles_begin[i], {image_left + WidgetDimensions::scaled.hsep_wide * i, ir.top, image_right, ir.bottom}, selected_vehicle, EIT_IN_LIST, 0);
1849 }
1850
1851 if (show_orderlist) DrawSmallOrderList((vehgroup.vehicles_begin[0])->GetFirstOrder(), olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width);
1852
1853 DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, vehgroup.NumVehicles()), TC_BLACK);
1854 break;
1855
1856 default:
1857 NOT_REACHED();
1858 }
1859
1860 ir = ir.Translate(0, line_height);
1861 }
1862}
1863
1864void BaseVehicleListWindow::UpdateSortingFromGrouping()
1865{
1866 /* Set up sorting. Make the window-specific _sorting variable
1867 * point to the correct global _sorting struct so we are freed
1868 * from having conditionals during window operation */
1869 switch (this->vli.vtype) {
1870 case VEH_TRAIN: this->sorting = &_sorting[this->grouping].train; break;
1871 case VEH_ROAD: this->sorting = &_sorting[this->grouping].roadveh; break;
1872 case VEH_SHIP: this->sorting = &_sorting[this->grouping].ship; break;
1873 case VEH_AIRCRAFT: this->sorting = &_sorting[this->grouping].aircraft; break;
1874 default: NOT_REACHED();
1875 }
1876 this->vehgroups.SetSortFuncs(this->GetVehicleSorterFuncs());
1877 this->vehgroups.SetListing(*this->sorting);
1878 this->vehgroups.ForceRebuild();
1879 this->vehgroups.NeedResort();
1880}
1881
1882void BaseVehicleListWindow::UpdateVehicleGroupBy(GroupBy group_by)
1883{
1884 if (this->grouping != group_by) {
1885 /* Save the old sorting option, so that if we change the grouping option back later on,
1886 * UpdateSortingFromGrouping() will automatically restore the saved sorting option. */
1887 *this->sorting = this->vehgroups.GetListing();
1888
1889 this->grouping = group_by;
1890 _grouping[this->vli.type][this->vli.vtype] = group_by;
1891 this->UpdateSortingFromGrouping();
1892 }
1893}
1894
1900private:
1906
1912
1913public:
1915 {
1916 this->CreateNestedTree();
1917
1918 this->GetWidget<NWidgetStacked>(WID_VL_FILTER_BY_CARGO_SEL)->SetDisplayedPlane((this->vli.type == VL_SHARED_ORDERS) ? SZSP_NONE : 0);
1919
1920 this->vscroll = this->GetScrollbar(WID_VL_SCROLLBAR);
1921
1922 /* Set up the window widgets */
1924
1926 if (this->vli.type == VL_SHARED_ORDERS) {
1928 /* If we are in the shared orders window, then disable the group-by dropdown menu.
1929 * Remove this when the group-by dropdown menu has another option apart from grouping by shared orders. */
1933 } else {
1935 nwi->SetDisplayedPlane(BP_NORMAL);
1936 }
1937
1938 this->FinishInitNested(window_number);
1939 if (this->vli.company != OWNER_NONE) this->owner = this->vli.company;
1940
1941 this->BuildVehicleList();
1942 this->SortVehicleList();
1943 }
1944
1946 {
1947 *this->sorting = this->vehgroups.GetListing();
1948 }
1949
1951 {
1952 switch (widget) {
1953 case WID_VL_LIST:
1954 resize.height = GetVehicleListHeight(this->vli.vtype, 1);
1955
1956 switch (this->vli.vtype) {
1957 case VEH_TRAIN:
1958 case VEH_ROAD:
1959 size.height = 6 * resize.height;
1960 break;
1961 case VEH_SHIP:
1962 case VEH_AIRCRAFT:
1963 size.height = 4 * resize.height;
1964 break;
1965 default: NOT_REACHED();
1966 }
1967 break;
1968
1969 case WID_VL_SORT_ORDER: {
1971 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1972 d.height += padding.height;
1973 size = maxdim(size, d);
1974 break;
1975 }
1976
1978 size.width = GetStringListWidth(this->vehicle_group_by_names) + padding.width;
1979 break;
1980
1982 size.width = GetStringListWidth(this->vehicle_group_none_sorter_names_calendar);
1983 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_none_sorter_names_wallclock));
1984 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_calendar));
1985 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_wallclock));
1986 size.width += padding.width;
1987 break;
1988
1990 size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList(true)).width + padding.width);
1991 break;
1992
1994 Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false, true);
1995 d.height += padding.height;
1996 d.width += padding.width;
1997 size = maxdim(size, d);
1998 break;
1999 }
2000 }
2001 }
2002
2003 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2004 {
2005 switch (widget) {
2008
2010 return GetString(std::data(this->vehicle_group_by_names)[this->grouping]);
2011
2013 return GetString(this->GetVehicleSorterNames()[this->vehgroups.SortType()]);
2014
2016 return GetString(this->GetCargoFilterLabel(this->cargo_filter_criteria));
2017
2018 case WID_VL_CAPTION:
2020 switch (this->vli.type) {
2021 case VL_SHARED_ORDERS: // Shared Orders
2022 return GetString(stringid, this->vehicles.size());
2023
2024 case VL_STANDARD: // Company Name
2025 return GetString(stringid, STR_COMPANY_NAME, this->vli.ToCompanyID(), std::monostate{}, this->vehicles.size());
2026
2027 case VL_STATION_LIST: // Station/Waypoint Name
2028 return GetString(stringid, Station::IsExpected(BaseStation::Get(this->vli.ToStationID())) ? STR_STATION_NAME : STR_WAYPOINT_NAME, this->vli.ToStationID(), std::monostate{}, this->vehicles.size());
2029
2030 case VL_DEPOT_LIST:
2031 return GetString(stringid, STR_DEPOT_CAPTION, this->vli.vtype, this->vli.ToDestinationID(), this->vehicles.size());
2032
2033 default: NOT_REACHED();
2034 }
2035 }
2036
2037 default:
2038 return this->Window::GetWidgetString(widget, stringid);
2039 }
2040 }
2041
2042 void DrawWidget(const Rect &r, WidgetID widget) const override
2043 {
2044 switch (widget) {
2045 case WID_VL_SORT_ORDER:
2046 /* draw arrow pointing up/down for ascending/descending sorting */
2047 this->DrawSortButtonState(widget, this->vehgroups.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
2048 break;
2049
2050 case WID_VL_LIST:
2051 this->DrawVehicleListItems(VehicleID::Invalid(), this->resize.step_height, r);
2052 break;
2053 }
2054 }
2055
2056 void OnPaint() override
2057 {
2058 this->BuildVehicleList();
2059 this->SortVehicleList();
2060
2061 if (this->vehicles.empty() && this->IsWidgetLowered(WID_VL_MANAGE_VEHICLES_DROPDOWN)) {
2063 }
2064
2065 /* Hide the widgets that we will not use in this window
2066 * Some windows contains actions only fit for the owner */
2069 if (plane_to_show != nwi->shown_plane) {
2070 nwi->SetDisplayedPlane(plane_to_show);
2071 nwi->SetDirty(this);
2072 }
2073 if (this->owner == _local_company) {
2075 this->SetWidgetsDisabledState(this->vehicles.empty(),
2079 }
2080
2081 this->DrawWidgets();
2082 }
2083
2084 bool last_overlay_state = false;
2085 void OnMouseLoop() override
2086 {
2087 if (last_overlay_state != ShowCargoIconOverlay()) {
2088 last_overlay_state = ShowCargoIconOverlay();
2089 this->SetDirty();
2090 }
2091 }
2092
2093 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2094 {
2095 switch (widget) {
2096 case WID_VL_ORDER_VIEW: // Open the shared orders window
2097 assert(this->vli.type == VL_SHARED_ORDERS);
2098 assert(!this->vehicles.empty());
2099 ShowOrdersWindow(this->vehicles[0]);
2100 break;
2101
2102 case WID_VL_SORT_ORDER: // Flip sorting method ascending/descending
2103 this->vehgroups.ToggleSortOrder();
2104 this->SetDirty();
2105 break;
2106
2107 case WID_VL_GROUP_BY_PULLDOWN: // Select sorting criteria dropdown menu
2108 ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_VL_GROUP_BY_PULLDOWN, 0, 0);
2109 return;
2110
2111 case WID_VL_SORT_BY_PULLDOWN: // Select sorting criteria dropdown menu
2112 ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0,
2113 (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
2114 return;
2115
2116 case WID_VL_FILTER_BY_CARGO: // Cargo filter dropdown
2117 ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget);
2118 break;
2119
2120 case WID_VL_LIST: { // Matrix to show vehicles
2121 auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_VL_LIST);
2122 if (it == this->vehgroups.end()) return; // click out of list bound
2123
2124 const GUIVehicleGroup &vehgroup = *it;
2125 switch (this->grouping) {
2126 case GB_NONE: {
2127 const Vehicle *v = vehgroup.GetSingleVehicle();
2128 if (!VehicleClicked(v)) {
2129 if (_ctrl_pressed) {
2131 } else {
2133 }
2134 }
2135 break;
2136 }
2137
2138 case GB_SHARED_ORDERS: {
2139 assert(vehgroup.NumVehicles() > 0);
2140 if (!VehicleClicked(vehgroup)) {
2141 const Vehicle *v = vehgroup.vehicles_begin[0];
2142 if (_ctrl_pressed) {
2143 ShowOrdersWindow(v);
2144 } else {
2145 if (vehgroup.NumVehicles() == 1) {
2147 } else {
2148 ShowVehicleListWindow(v);
2149 }
2150 }
2151 }
2152 break;
2153 }
2154
2155 default: NOT_REACHED();
2156 }
2157
2158 break;
2159 }
2160
2162 ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
2163 break;
2164
2167 break;
2168 }
2169
2170 case WID_VL_STOP_ALL:
2171 case WID_VL_START_ALL:
2173 break;
2174 }
2175 }
2176
2177 void OnDropdownSelect(WidgetID widget, int index) override
2178 {
2179 switch (widget) {
2181 this->UpdateVehicleGroupBy(static_cast<GroupBy>(index));
2182 break;
2183
2185 this->vehgroups.SetSortType(index);
2186 break;
2187
2189 this->SetCargoFilter(index);
2190 break;
2191
2193 assert(!this->vehicles.empty());
2194
2195 switch (index) {
2196 case ADI_REPLACE: // Replace window
2198 break;
2199 case ADI_SERVICE: // Send for servicing
2200 case ADI_DEPOT: // Send to Depots
2201 Command<CMD_SEND_VEHICLE_TO_DEPOT>::Post(GetCmdSendToDepotMsg(this->vli.vtype), VehicleID::Invalid(), (index == ADI_SERVICE ? DepotCommandFlag::Service : DepotCommandFlags{}) | DepotCommandFlag::MassSend, this->vli);
2202 break;
2203
2204 case ADI_CREATE_GROUP: // Create group
2205 Command<CMD_ADD_VEHICLE_GROUP>::Post(CcAddVehicleNewGroup, NEW_GROUP, VehicleID::Invalid(), false, this->vli);
2206 break;
2207
2208 default: NOT_REACHED();
2209 }
2210 break;
2211
2212 default: NOT_REACHED();
2213 }
2214 this->SetDirty();
2215 }
2216
2217 void OnGameTick() override
2218 {
2219 if (this->vehgroups.NeedResort()) {
2220 StationID station = (this->vli.type == VL_STATION_LIST) ? this->vli.ToStationID() : StationID::Invalid();
2221
2222 Debug(misc, 3, "Periodic resort {} list company {} at station {}", this->vli.vtype, this->owner, station);
2223 this->SetDirty();
2224 }
2225 }
2226
2227 void OnResize() override
2228 {
2229 this->vscroll->SetCapacityFromWidget(this, WID_VL_LIST);
2230 }
2231
2237 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2238 {
2239 if (!gui_scope && HasBit(data, 31) && this->vli.type == VL_SHARED_ORDERS) {
2240 /* Needs to be done in command-scope, so everything stays valid */
2241 this->vli.SetIndex(GB(data, 0, 20));
2242 this->window_number = this->vli.ToWindowNumber();
2243 this->vehgroups.ForceRebuild();
2244 return;
2245 }
2246
2247 if (data == 0) {
2248 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
2249 this->vehgroups.ForceRebuild();
2250 } else {
2251 this->vehgroups.ForceResort();
2252 }
2253 }
2254};
2255
2256static WindowDesc _vehicle_list_desc[] = {
2257 {
2258 WDP_AUTO, "list_vehicles_train", 325, 246,
2260 {},
2261 _nested_vehicle_list
2262 },
2263 {
2264 WDP_AUTO, "list_vehicles_roadveh", 260, 246,
2266 {},
2267 _nested_vehicle_list
2268 },
2269 {
2270 WDP_AUTO, "list_vehicles_ship", 260, 246,
2272 {},
2273 _nested_vehicle_list
2274 },
2275 {
2276 WDP_AUTO, "list_vehicles_aircraft", 260, 246,
2278 {},
2279 _nested_vehicle_list
2280 }
2281};
2282
2283static void ShowVehicleListWindowLocal(CompanyID company, VehicleListType vlt, VehicleType vehicle_type, uint32_t unique_number)
2284{
2285 if (!Company::IsValidID(company) && company != OWNER_NONE) return;
2286
2287 assert(vehicle_type < std::size(_vehicle_list_desc));
2288 VehicleListIdentifier vli(vlt, vehicle_type, company, unique_number);
2289 AllocateWindowDescFront<VehicleListWindow>(_vehicle_list_desc[vehicle_type], vli.ToWindowNumber(), vli);
2290}
2291
2292void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type)
2293{
2294 /* If _settings_client.gui.advanced_vehicle_list > 1, display the Advanced list
2295 * if _settings_client.gui.advanced_vehicle_list == 1, display Advanced list only for local company
2296 * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
2297 */
2298
2300 ShowCompanyGroup(company, vehicle_type);
2301 } else {
2302 ShowVehicleListWindowLocal(company, VL_STANDARD, vehicle_type, company.base());
2303 }
2304}
2305
2306void ShowVehicleListWindow(const Vehicle *v)
2307{
2308 ShowVehicleListWindowLocal(v->owner, VL_SHARED_ORDERS, v->type, v->FirstShared()->index.base());
2309}
2310
2311void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station)
2312{
2313 ShowVehicleListWindowLocal(company, VL_STATION_LIST, vehicle_type, station.base());
2314}
2315
2316void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
2317{
2318 ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, GetDepotDestinationIndex(depot_tile).base());
2319}
2320
2321
2322/* Unified vehicle GUI - Vehicle Details Window */
2323
2328
2332 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2333 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VD_CAPTION),
2334 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2335 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2336 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2337 EndContainer(),
2338 NWidget(WWT_PANEL, COLOUR_GREY, WID_VD_TOP_DETAILS), SetMinimalSize(405, 42), SetResize(1, 0), EndContainer(),
2346 SetStringTip(STR_EMPTY, STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP),
2348 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2349 EndContainer(),
2350};
2351
2355 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2356 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VD_CAPTION), SetStringTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2357 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2358 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2359 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2360 EndContainer(),
2361 NWidget(WWT_PANEL, COLOUR_GREY, WID_VD_TOP_DETAILS), SetResize(1, 0), SetMinimalSize(405, 42), EndContainer(),
2365 EndContainer(),
2372 SetStringTip(STR_EMPTY, STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP),
2374 EndContainer(),
2377 SetStringTip(STR_VEHICLE_DETAIL_TAB_CARGO, STR_VEHICLE_DETAILS_TRAIN_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2379 SetStringTip(STR_VEHICLE_DETAIL_TAB_INFORMATION, STR_VEHICLE_DETAILS_TRAIN_INFORMATION_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2381 SetStringTip(STR_VEHICLE_DETAIL_TAB_CAPACITIES, STR_VEHICLE_DETAILS_TRAIN_CAPACITIES_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2383 SetStringTip(STR_VEHICLE_DETAIL_TAB_TOTAL_CARGO, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2384 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2385 EndContainer(),
2386};
2387
2388
2390extern void DrawTrainDetails(const Train *v, const Rect &r, int vscroll_pos, uint16_t vscroll_cap, TrainDetailsWindowTabs det_tab);
2391extern void DrawRoadVehDetails(const Vehicle *v, const Rect &r);
2392extern void DrawShipDetails(const Vehicle *v, const Rect &r);
2393extern void DrawAircraftDetails(const Aircraft *v, const Rect &r);
2394
2395static StringID _service_interval_dropdown_calendar[] = {
2396 STR_VEHICLE_DETAILS_DEFAULT,
2397 STR_VEHICLE_DETAILS_DAYS,
2398 STR_VEHICLE_DETAILS_PERCENT,
2399};
2400
2401static StringID _service_interval_dropdown_wallclock[] = {
2402 STR_VEHICLE_DETAILS_DEFAULT,
2403 STR_VEHICLE_DETAILS_MINUTES,
2404 STR_VEHICLE_DETAILS_PERCENT,
2405};
2406
2410 Scrollbar *vscroll = nullptr;
2411
2414 {
2416
2417 this->CreateNestedTree();
2418 this->vscroll = (v->type == VEH_TRAIN ? this->GetScrollbar(WID_VD_SCROLLBAR) : nullptr);
2419 this->FinishInitNested(window_number);
2420
2421 this->owner = v->owner;
2422 }
2423
2429 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2430 {
2431 if (data == VIWD_AUTOREPLACE) {
2432 /* Autoreplace replaced the vehicle.
2433 * Nothing to do for this window. */
2434 return;
2435 }
2436 if (!gui_scope) return;
2437 const Vehicle *v = Vehicle::Get(this->window_number);
2438 if (v->type == VEH_ROAD) {
2440 uint aimed_height = this->GetRoadVehDetailsHeight(v);
2441 /* If the number of articulated parts changes, the size of the window must change too. */
2442 if (aimed_height != nwid_info->current_y) {
2443 this->ReInit();
2444 }
2445 }
2446 }
2447
2454 {
2455 uint desired_height;
2456 if (v->HasArticulatedPart()) {
2457 /* An articulated RV has its text drawn under the sprite instead of after it, hence 15 pixels extra. */
2459 /* Add space for the cargo amount for each part. */
2460 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
2462 }
2463 } else {
2465 }
2466 return desired_height;
2467 }
2468
2470 {
2471 switch (widget) {
2472 case WID_VD_TOP_DETAILS: {
2473 Dimension dim = { 0, 0 };
2474 size.height = 4 * GetCharacterHeight(FS_NORMAL) + padding.height;
2475
2476 uint64_t max_value = GetParamMaxValue(INT16_MAX);
2478 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, max_value, max_value, max_value)));
2479 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE, max_value, max_value, max_value, max_value)));
2484 size.width = dim.width + padding.width;
2485 break;
2486 }
2487
2488 case WID_VD_MIDDLE_DETAILS: {
2489 const Vehicle *v = Vehicle::Get(this->window_number);
2490 switch (v->type) {
2491 case VEH_ROAD:
2492 size.height = this->GetRoadVehDetailsHeight(v) + padding.height;
2493 break;
2494
2495 case VEH_SHIP:
2496 size.height = 4 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2497 break;
2498
2499 case VEH_AIRCRAFT:
2500 size.height = 5 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2501 break;
2502
2503 default:
2504 NOT_REACHED(); // Train uses WID_VD_MATRIX instead.
2505 }
2506 break;
2507 }
2508
2509 case WID_VD_MATRIX:
2510 resize.height = std::max<uint>(ScaleGUITrad(14), GetCharacterHeight(FS_NORMAL) + padding.height);
2511 size.height = 4 * resize.height;
2512 break;
2513
2515 Dimension d = maxdim(GetStringListBoundingBox(_service_interval_dropdown_calendar), GetStringListBoundingBox(_service_interval_dropdown_wallclock));
2516 d.width += padding.width;
2517 d.height += padding.height;
2518 size = maxdim(size, d);
2519 break;
2520 }
2521
2523 /* Do we show the last serviced value as a date or minutes since service? */
2527
2529 PrepareArgsForNextRun(params);
2531
2532 size.width += padding.width;
2533 size.height = GetCharacterHeight(FS_NORMAL) + padding.height;
2534 break;
2535 }
2536 }
2537
2540 {
2541 if (_local_company != company_id) return false;
2542
2543 const VehicleDefaultSettings *vds = &Company::Get(company_id)->settings.vehicle;
2544 switch (vehicle_type) {
2545 default: NOT_REACHED();
2546 case VEH_TRAIN: return vds->servint_trains != 0;
2547 case VEH_ROAD: return vds->servint_roadveh != 0;
2548 case VEH_SHIP: return vds->servint_ships != 0;
2549 case VEH_AIRCRAFT: return vds->servint_aircraft != 0;
2550 }
2551 }
2552
2563 {
2564 switch (v->type) {
2566 case VEH_ROAD: DrawRoadVehDetails(v, r); break;
2567 case VEH_SHIP: DrawShipDetails(v, r); break;
2569 default: NOT_REACHED();
2570 }
2571 }
2572
2573 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2574 {
2575 if (widget == WID_VD_CAPTION) return GetString(STR_VEHICLE_DETAILS_CAPTION, Vehicle::Get(this->window_number)->index);
2576
2577 return this->Window::GetWidgetString(widget, stringid);
2578 }
2579
2580 void DrawWidget(const Rect &r, WidgetID widget) const override
2581 {
2582 const Vehicle *v = Vehicle::Get(this->window_number);
2583
2584 switch (widget) {
2585 case WID_VD_TOP_DETAILS: {
2586 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2587
2588 /* Draw running cost */
2589 DrawString(tr,
2594 v->GetDisplayRunningCost()));
2596
2597 /* Draw max speed */
2598 uint64_t max_speed = PackVelocity(v->GetDisplayMaxSpeed(), v->type);
2599 if (v->type == VEH_TRAIN ||
2601 const GroundVehicleCache *gcache = v->GetGroundVehicleCache();
2602 if (v->type == VEH_TRAIN && (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ||
2603 GetRailTypeInfo(Train::From(v)->railtype)->acceleration_type == 2)) {
2605 } else {
2607 }
2608 } else if (v->type == VEH_AIRCRAFT) {
2609 StringID type = v->GetEngine()->GetAircraftTypeText();
2610 if (Aircraft::From(v)->GetRange() > 0) {
2612 } else {
2614 }
2615 } else {
2617 }
2619
2620 /* Draw profit */
2621 if (v->IsGroundVehicle()) {
2622 DrawString(tr,
2627 } else {
2628 DrawString(tr,
2632 }
2634
2635 /* Draw breakdown & reliability */
2637 break;
2638 }
2639
2640 case WID_VD_MATRIX: {
2641 /* For trains only. */
2642 DrawVehicleDetails(v, r.Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero).WithHeight(this->resize.step_height), this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->tab);
2643 break;
2644 }
2645
2646 case WID_VD_MIDDLE_DETAILS: {
2647 /* For other vehicles, at the place of the matrix. */
2648 bool rtl = _current_text_dir == TD_RTL;
2650 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2651
2652 /* Articulated road vehicles use a complete line. */
2653 if (v->type == VEH_ROAD && v->HasArticulatedPart()) {
2654 DrawVehicleImage(v, tr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2655 } else {
2656 Rect sr = tr.WithWidth(sprite_width, rtl);
2657 DrawVehicleImage(v, sr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2658 }
2659
2660 DrawVehicleDetails(v, tr.Indent(sprite_width, rtl), 0, 0, this->tab);
2661 break;
2662 }
2663
2665 /* Draw service interval text */
2666 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2667
2668 /* We're using wallclock units. Show minutes since last serviced. */
2671 DrawString(tr.left, tr.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
2674 break;
2675 }
2676
2677 /* We're using calendar dates. Show the date of last service. */
2678 DrawString(tr.left, tr.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
2680 v->GetServiceInterval(), STR_VEHICLE_DETAILS_LAST_SERVICE_DATE, v->date_of_last_service));
2681 break;
2682 }
2683 }
2684 }
2685
2687 void OnPaint() override
2688 {
2689 const Vehicle *v = Vehicle::Get(this->window_number);
2690
2691 if (v->type == VEH_TRAIN) {
2692 this->LowerWidget(WID_VD_DETAILS_CARGO_CARRIED + this->tab);
2693 this->vscroll->SetCount(GetTrainDetailsWndVScroll(v->index, this->tab));
2694 }
2695
2696 /* Disable service-scroller when interval is set to disabled */
2700
2701 StringID str =
2702 !v->ServiceIntervalIsCustom() ? STR_VEHICLE_DETAILS_DEFAULT :
2703 v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT :
2707
2708 this->DrawWidgets();
2709 }
2710
2711 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2712 {
2713 switch (widget) {
2714 case WID_VD_INCREASE_SERVICING_INTERVAL: // increase int
2715 case WID_VD_DECREASE_SERVICING_INTERVAL: { // decrease int
2716 const Vehicle *v = Vehicle::Get(this->window_number);
2717 int mod;
2718 if (!v->ServiceIntervalIsPercent() && TimerGameEconomy::UsingWallclockUnits()) {
2719 mod = _ctrl_pressed ? 1 : 5;
2720 } else {
2721 mod = _ctrl_pressed ? 5 : 10;
2722 }
2723
2724 mod = (widget == WID_VD_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
2725 mod = GetServiceIntervalClamped(mod + v->GetServiceInterval(), v->ServiceIntervalIsPercent());
2726 if (mod == v->GetServiceInterval()) return;
2727
2728 Command<CMD_CHANGE_SERVICE_INT>::Post(STR_ERROR_CAN_T_CHANGE_SERVICING, v->index, mod, true, v->ServiceIntervalIsPercent());
2729 break;
2730 }
2731
2733 const Vehicle *v = Vehicle::Get(this->window_number);
2734 ShowDropDownMenu(this,
2735 TimerGameEconomy::UsingWallclockUnits() ? _service_interval_dropdown_wallclock : _service_interval_dropdown_calendar,
2736 v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0);
2737 break;
2738 }
2739
2744 this->SetWidgetsLoweredState(false,
2749
2751 this->SetDirty();
2752 break;
2753 }
2754 }
2755
2774
2775 void OnDropdownSelect(WidgetID widget, int index) override
2776 {
2777 switch (widget) {
2779 const Vehicle *v = Vehicle::Get(this->window_number);
2780 bool iscustom = index != 0;
2781 bool ispercent = iscustom ? (index == 2) : Company::Get(v->owner)->settings.vehicle.servint_ispercent;
2782 uint16_t interval = GetServiceIntervalClamped(v->GetServiceInterval(), ispercent);
2784 break;
2785 }
2786 }
2787 }
2788
2789 void OnResize() override
2790 {
2792 if (nwi != nullptr) {
2793 this->vscroll->SetCapacityFromWidget(this, WID_VD_MATRIX);
2794 }
2795 }
2796};
2797
2800 WDP_AUTO, "view_vehicle_details_train", 405, 178,
2802 {},
2804);
2805
2808 WDP_AUTO, "view_vehicle_details", 405, 113,
2810 {},
2812);
2813
2816{
2819 AllocateWindowDescFront<VehicleDetailsWindow>((v->type == VEH_TRAIN) ? _train_vehicle_details_desc : _nontrain_vehicle_details_desc, v->index);
2820}
2821
2822
2823/* Unified vehicle GUI - Vehicle View Window */
2824
2828 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2829 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME),
2830 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VV_CAPTION),
2831 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION),
2832 NWidget(WWT_DEBUGBOX, COLOUR_GREY),
2833 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2834 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2835 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2836 EndContainer(),
2838 NWidget(WWT_PANEL, COLOUR_GREY),
2839 NWidget(WWT_INSET, COLOUR_GREY), SetPadding(2, 2, 2, 2),
2840 NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_VV_VIEWPORT), SetMinimalSize(226, 84), SetResize(1, 1),
2841 EndContainer(),
2842 EndContainer(),
2845 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_GOTO_DEPOT), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2846 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_CLONE), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2847 EndContainer(),
2848 /* For trains only, 'ignore signal' button. */
2850 SetSpriteTip(SPR_IGNORE_SIGNALS, STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP),
2852 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_REFIT), SetMinimalSize(18, 18), SetSpriteTip(SPR_REFIT_VEHICLE),
2854 SetSpriteTip(SPR_FORCE_VEHICLE_TURN, STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP),
2855 EndContainer(),
2856 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_SHOW_ORDERS), SetMinimalSize(18, 18), SetSpriteTip(SPR_SHOW_ORDERS),
2857 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_SHOW_DETAILS), SetMinimalSize(18, 18), SetSpriteTip(SPR_SHOW_VEHICLE_DETAILS),
2858 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(18, 0), SetResize(0, 1), EndContainer(),
2859 EndContainer(),
2860 EndContainer(),
2862 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_VV_START_STOP), SetResize(1, 0), SetFill(1, 0),
2863 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_ORDER_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION, STR_VEHICLE_VIEW_ORDER_LOCATION_TOOLTIP),
2864 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2865 EndContainer(),
2866};
2867
2868/* Just to make sure, nobody has changed the vehicle type constants, as we are
2869 using them for array indexing in a number of places here. */
2870static_assert(VEH_TRAIN == 0);
2871static_assert(VEH_ROAD == 1);
2872static_assert(VEH_SHIP == 2);
2873static_assert(VEH_AIRCRAFT == 3);
2874
2882
2883/* Constants for geometry of vehicle view viewport */
2884static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
2885static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
2886static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN = 102;
2887
2890 VCT_CMD_START_STOP = 0,
2891 VCT_CMD_CLONE_VEH,
2892 VCT_CMD_TURN_AROUND,
2893};
2894
2897 { // VCT_CMD_START_STOP
2898 STR_ERROR_CAN_T_STOP_START_TRAIN,
2899 STR_ERROR_CAN_T_STOP_START_ROAD_VEHICLE,
2900 STR_ERROR_CAN_T_STOP_START_SHIP,
2901 STR_ERROR_CAN_T_STOP_START_AIRCRAFT
2902 },
2903 { // VCT_CMD_CLONE_VEH
2904 STR_ERROR_CAN_T_BUY_TRAIN,
2905 STR_ERROR_CAN_T_BUY_ROAD_VEHICLE,
2906 STR_ERROR_CAN_T_BUY_SHIP,
2907 STR_ERROR_CAN_T_BUY_AIRCRAFT
2908 },
2909 { // VCT_CMD_TURN_AROUND
2910 STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN,
2911 STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN,
2912 INVALID_STRING_ID, // invalid for ships
2913 INVALID_STRING_ID // invalid for aircraft
2914 },
2915};
2916
2922void CcStartStopVehicle(Commands, const CommandCost &result, VehicleID veh_id, bool)
2923{
2924 if (result.Failed()) return;
2925
2926 const Vehicle *v = Vehicle::GetIfValid(veh_id);
2927 if (v == nullptr || !v->IsPrimaryVehicle() || v->owner != _local_company) return;
2928
2929 StringID msg = v->vehstatus.Test(VehState::Stopped) ? STR_VEHICLE_COMMAND_STOPPED : STR_VEHICLE_COMMAND_STARTED;
2930 Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos);
2931 AddTextEffect(GetEncodedString(msg), pt.x, pt.y, Ticks::DAY_TICKS, TE_RISING);
2932}
2933
2939void StartStopVehicle(const Vehicle *v, bool texteffect)
2940{
2941 assert(v->IsPrimaryVehicle());
2942 Command<CMD_START_STOP_VEHICLE>::Post(_vehicle_msg_translation_table[VCT_CMD_START_STOP][v->type], texteffect ? CcStartStopVehicle : nullptr, v->tile, v->index, false);
2943}
2944
2946static bool IsVehicleRefitable(const Vehicle *v)
2947{
2948 if (!v->IsStoppedInDepot()) return false;
2949
2950 do {
2951 if (IsEngineRefittable(v->engine_type)) return true;
2952 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
2953
2954 return false;
2955}
2956
2959private:
2971 bool mouse_over_start_stop = false;
2972
2978 {
2979 switch (plane) {
2980 case SEL_DC_GOTO_DEPOT:
2981 case SEL_DC_CLONE:
2982 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE)->SetDisplayedPlane(plane - SEL_DC_BASEPLANE);
2983 break;
2984
2985 case SEL_RT_REFIT:
2986 case SEL_RT_TURN_AROUND:
2987 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_REFIT_TURN)->SetDisplayedPlane(plane - SEL_RT_BASEPLANE);
2988 break;
2989
2990 default:
2991 NOT_REACHED();
2992 }
2993 }
2994
2995public:
2997 {
2999 this->CreateNestedTree();
3000
3001 /* Sprites for the 'send to depot' button indexed by vehicle type. */
3002 static const SpriteID vehicle_view_goto_depot_sprites[] = {
3003 SPR_SEND_TRAIN_TODEPOT,
3004 SPR_SEND_ROADVEH_TODEPOT,
3005 SPR_SEND_SHIP_TODEPOT,
3006 SPR_SEND_AIRCRAFT_TODEPOT,
3007 };
3009 this->GetWidget<NWidgetCore>(WID_VV_GOTO_DEPOT)->SetSprite(vehicle_view_goto_depot_sprites[v->type]);
3010
3011 /* Sprites for the 'clone vehicle' button indexed by vehicle type. */
3012 static const SpriteID vehicle_view_clone_sprites[] = {
3014 SPR_CLONE_ROADVEH,
3015 SPR_CLONE_SHIP,
3016 SPR_CLONE_AIRCRAFT,
3017 };
3018 this->GetWidget<NWidgetCore>(WID_VV_CLONE)->SetSprite(vehicle_view_clone_sprites[v->type]);
3019
3020 switch (v->type) {
3021 case VEH_TRAIN:
3023 break;
3024
3025 case VEH_ROAD:
3026 break;
3027
3028 case VEH_SHIP:
3029 case VEH_AIRCRAFT:
3031 break;
3032
3033 default: NOT_REACHED();
3034 }
3035 this->FinishInitNested(window_number);
3036 this->owner = v->owner;
3037 this->GetWidget<NWidgetViewport>(WID_VV_VIEWPORT)->InitializeViewport(this, static_cast<VehicleID>(this->window_number), ScaleZoomGUI(_vehicle_view_zoom_levels[v->type]));
3038
3047
3048 this->UpdateButtonStatus();
3049 }
3050
3051 void Close([[maybe_unused]] int data = 0) override
3052 {
3053 CloseWindowById(WC_VEHICLE_ORDERS, this->window_number, false);
3054 CloseWindowById(WC_VEHICLE_REFIT, this->window_number, false);
3055 CloseWindowById(WC_VEHICLE_DETAILS, this->window_number, false);
3056 CloseWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false);
3057 this->Window::Close();
3058 }
3059
3061 {
3062 const Vehicle *v = Vehicle::Get(this->window_number);
3063 switch (widget) {
3064 case WID_VV_START_STOP:
3065 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;
3066 break;
3067
3069 if (v->type != VEH_TRAIN) {
3070 size.height = 0;
3071 size.width = 0;
3072 }
3073 break;
3074
3075 case WID_VV_VIEWPORT:
3076 size.width = VV_INITIAL_VIEWPORT_WIDTH;
3077 size.height = (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT;
3078 break;
3079 }
3080 }
3081
3082 void OnPaint() override
3083 {
3084 const Vehicle *v = Vehicle::Get(this->window_number);
3087
3088 this->SetWidgetDisabledState(WID_VV_RENAME, !is_localcompany);
3089 this->SetWidgetDisabledState(WID_VV_GOTO_DEPOT, !is_localcompany);
3090 this->SetWidgetDisabledState(WID_VV_REFIT, !refitable_and_stopped_in_depot || !is_localcompany);
3091 this->SetWidgetDisabledState(WID_VV_CLONE, !is_localcompany);
3092
3093 if (v->type == VEH_TRAIN) {
3095 this->SetWidgetDisabledState(WID_VV_FORCE_PROCEED, !is_localcompany);
3096 }
3097
3098 if (v->type == VEH_TRAIN || v->type == VEH_ROAD) {
3099 this->SetWidgetDisabledState(WID_VV_TURN_AROUND, !is_localcompany);
3100 }
3101
3103
3104 const Window *mainwindow = GetMainWindow();
3105 if (mainwindow->viewport->follow_vehicle == v->index) {
3107 }
3108
3109 this->DrawWidgets();
3110 }
3111
3112 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
3113 {
3114 if (widget != WID_VV_CAPTION) return this->Window::GetWidgetString(widget, stringid);
3115
3116 const Vehicle *v = Vehicle::Get(this->window_number);
3118 }
3119
3120 std::string GetVehicleStatusString(const Vehicle *v, TextColour &text_colour) const
3121 {
3122 text_colour = TC_BLACK;
3123
3125
3127
3128 if (v->vehstatus.Test(VehState::Stopped) && (!mouse_over_start_stop || v->IsStoppedInDepot())) {
3131 if (Train::From(v)->gcache.cached_power == 0) return GetString(STR_VEHICLE_STATUS_TRAIN_NO_POWER);
3133 }
3134
3136
3138
3140
3141 /* Vehicle is in a "normal" state, show current order. */
3142 if (mouse_over_start_stop) {
3144 text_colour = TC_RED | TC_FORCED;
3145 } else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING)) {
3146 text_colour = TC_ORANGE | TC_FORCED;
3147 }
3148 }
3149
3150 switch (v->current_order.GetType()) {
3151 case OT_GOTO_STATION:
3153 v->current_order.GetDestination(), PackVelocity(v->GetDisplaySpeed(), v->type));
3154
3155 case OT_GOTO_DEPOT: {
3156 /* This case *only* happens when multiple nearest depot orders
3157 * follow each other (including an order list only one order: a
3158 * nearest depot order) and there are no reachable depots.
3159 * It is primarily to guard for the case that there is no
3160 * depot with index 0, which would be used as fallback for
3161 * evaluating the string in the status bar. */
3162 if (v->current_order.GetDestination() == DepotID::Invalid()) return {};
3163
3167 }
3168
3171 }
3172
3174 }
3175
3176 case OT_LOADING:
3178
3179 case OT_GOTO_WAYPOINT:
3180 assert(v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP);
3182 v->current_order.GetDestination(),PackVelocity(v->GetDisplaySpeed(), v->type));
3183
3184 case OT_LEAVESTATION:
3185 if (v->type != VEH_AIRCRAFT) {
3187 }
3188 [[fallthrough]];
3189
3190 default:
3191 if (v->GetNumManualOrders() == 0) {
3193 }
3194
3195 return {};
3196 }
3197 }
3198
3199 void DrawWidget(const Rect &r, WidgetID widget) const override
3200 {
3201 if (widget != WID_VV_START_STOP) return;
3202
3203 /* Draw the flag plus orders. */
3204 bool rtl = (_current_text_dir == TD_RTL);
3205 uint icon_width = std::max({GetScaledSpriteSize(SPR_WARNING_SIGN).width, GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED).width, GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING).width});
3206 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
3207
3208 const Vehicle *v = Vehicle::Get(this->window_number);
3209 SpriteID image = v->vehstatus.Test(VehState::Stopped) ? SPR_FLAG_VEH_STOPPED : (HasBit(v->vehicle_flags, VF_PATHFINDER_LOST)) ? SPR_WARNING_SIGN : SPR_FLAG_VEH_RUNNING;
3210 DrawSpriteIgnorePadding(image, PAL_NONE, tr.WithWidth(icon_width, rtl), SA_CENTER);
3211
3212 tr = tr.Indent(icon_width + WidgetDimensions::scaled.imgbtn.Horizontal(), rtl);
3213
3214 TextColour text_colour = TC_FROMSTRING;
3215 std::string str = GetVehicleStatusString(v, text_colour);
3216 DrawString(tr.left, tr.right, CenterBounds(tr.top, tr.bottom, GetCharacterHeight(FS_NORMAL)), str, text_colour, SA_HOR_CENTER);
3217 }
3218
3219 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
3220 {
3221 const Vehicle *v = Vehicle::Get(this->window_number);
3222
3223 switch (widget) {
3224 case WID_VV_RENAME: { // rename
3226 MAX_LENGTH_VEHICLE_NAME_CHARS, this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars});
3227 break;
3228 }
3229
3230 case WID_VV_START_STOP: // start stop
3231 StartStopVehicle(v, false);
3232 break;
3233
3234 case WID_VV_ORDER_LOCATION: {
3235 /* Scroll to current order destination */
3236 TileIndex tile = v->current_order.GetLocation(v);
3237 if (tile == INVALID_TILE) break;
3238
3239 if (_ctrl_pressed) {
3241 } else {
3243 }
3244 break;
3245 }
3246
3247 case WID_VV_LOCATION: // center main view
3248 if (_ctrl_pressed) {
3250 } else {
3251 const Window *mainwindow = GetMainWindow();
3252 if (click_count > 1) {
3253 /* main window 'follows' vehicle */
3254 mainwindow->viewport->follow_vehicle = v->index;
3255 } else {
3256 if (mainwindow->viewport->follow_vehicle == v->index) mainwindow->viewport->follow_vehicle = VehicleID::Invalid();
3257 ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos);
3258 }
3259 }
3260 break;
3261
3262 case WID_VV_GOTO_DEPOT: // goto hangar
3264 break;
3265 case WID_VV_REFIT: // refit
3267 break;
3268 case WID_VV_SHOW_ORDERS: // show orders
3269 if (_ctrl_pressed) {
3271 } else {
3272 ShowOrdersWindow(v);
3273 }
3274 break;
3275 case WID_VV_SHOW_DETAILS: // show details
3276 if (_ctrl_pressed) {
3278 } else {
3280 }
3281 break;
3282 case WID_VV_CLONE: // clone vehicle
3283 /* Suppress the vehicle GUI when share-cloning.
3284 * There is no point to it except for starting the vehicle.
3285 * For starting the vehicle the player has to open the depot GUI, which is
3286 * most likely already open, but is also visible in the vehicle viewport. */
3288 _ctrl_pressed ? nullptr : CcCloneVehicle,
3289 v->tile, v->index, _ctrl_pressed);
3290 break;
3291 case WID_VV_TURN_AROUND: // turn around
3292 assert(v->IsGroundVehicle());
3293 if (v->type == VEH_ROAD) {
3295 } else {
3297 }
3298 break;
3299 case WID_VV_FORCE_PROCEED: // force proceed
3300 assert(v->type == VEH_TRAIN);
3302 break;
3303 }
3304 }
3305
3307 {
3308 /* If the hotkey is not for any widget in the UI (i.e. for honking) */
3309 if (hotkey == WID_VV_HONK_HORN) {
3310 const Window *mainwindow = GetMainWindow();
3312 /* Only play the sound if we're following this vehicle */
3313 if (mainwindow->viewport->follow_vehicle == v->index) {
3314 v->PlayLeaveStationSound(true);
3315 }
3316 }
3317 return Window::OnHotkey(hotkey);
3318 }
3319
3320 void OnQueryTextFinished(std::optional<std::string> str) override
3321 {
3322 if (!str.has_value()) return;
3323
3324 Command<CMD_RENAME_VEHICLE>::Post(STR_ERROR_CAN_T_RENAME_TRAIN + Vehicle::Get(this->window_number)->type, static_cast<VehicleID>(this->window_number), *str);
3325 }
3326
3327 void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
3328 {
3329 bool start_stop = widget == WID_VV_START_STOP;
3330 if (start_stop != mouse_over_start_stop) {
3331 mouse_over_start_stop = start_stop;
3333 }
3334 }
3335
3336 void OnMouseWheel(int wheel) override
3337 {
3339 DoZoomInOutWindow(wheel < 0 ? ZOOM_IN : ZOOM_OUT, this);
3340 }
3341 }
3342
3343 void OnResize() override
3344 {
3345 if (this->viewport != nullptr) {
3347 nvp->UpdateViewportCoordinates(this);
3348 }
3349 }
3350
3351 void UpdateButtonStatus()
3352 {
3353 const Vehicle *v = Vehicle::Get(this->window_number);
3354 bool veh_stopped = v->IsStoppedInDepot();
3355
3356 /* Widget WID_VV_GOTO_DEPOT must be hidden if the vehicle is already stopped in depot.
3357 * Widget WID_VV_CLONE_VEH should then be shown, since cloning is allowed only while in depot and stopped.
3358 */
3360 NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE); // Selection widget 'send to depot' / 'clone'.
3361 if (nwi->shown_plane + SEL_DC_BASEPLANE != plane) {
3362 this->SelectPlane(plane);
3364 }
3365 /* The same system applies to widget WID_VV_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
3366 if (v->IsGroundVehicle()) {
3369 if (nwi->shown_plane + SEL_RT_BASEPLANE != plane) {
3370 this->SelectPlane(plane);
3372 }
3373 }
3374 }
3375
3381 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
3382 {
3383 if (data == VIWD_AUTOREPLACE) {
3384 /* Autoreplace replaced the vehicle.
3385 * Nothing to do for this window. */
3386 return;
3387 }
3388
3389 this->UpdateButtonStatus();
3390 }
3391
3392 bool IsNewGRFInspectable() const override
3393 {
3394 return ::IsNewGRFInspectable(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3395 }
3396
3397 void ShowNewGRFInspectWindow() const override
3398 {
3399 ::ShowNewGRFInspectWindow(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3400 }
3401
3402 static inline HotkeyList hotkeys{"vehicleview", {
3403 Hotkey('H', "honk", WID_VV_HONK_HORN),
3404 }};
3405};
3406
3409 WDP_AUTO, "view_vehicle", 250, 116,
3411 {},
3413 &VehicleViewWindow::hotkeys
3414);
3415
3421 WDP_AUTO, "view_vehicle_train", 250, 134,
3423 {},
3425 &VehicleViewWindow::hotkeys
3426);
3427
3430{
3431 AllocateWindowDescFront<VehicleViewWindow>((v->type == VEH_TRAIN) ? _train_view_desc : _vehicle_view_desc, v->index);
3432}
3433
3440{
3441 assert(v != nullptr);
3442 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3443
3444 v = v->First();
3445 if (!v->IsPrimaryVehicle()) return false;
3446
3447 return _thd.GetCallbackWnd()->OnVehicleSelect(v);
3448}
3449
3456bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end)
3457{
3458 assert(begin != end);
3459 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3460
3461 /* If there is only one vehicle in the group, act as if we clicked a single vehicle */
3462 if (begin + 1 == end) return _thd.GetCallbackWnd()->OnVehicleSelect(*begin);
3463
3464 return _thd.GetCallbackWnd()->OnVehicleSelect(begin, end);
3465}
3466
3472bool VehicleClicked(const GUIVehicleGroup &vehgroup)
3473{
3474 return VehicleClicked(vehgroup.vehicles_begin, vehgroup.vehicles_end);
3475}
3476
3477void StopGlobalFollowVehicle(const Vehicle *v)
3478{
3479 Window *w = GetMainWindow();
3480 if (w->viewport->follow_vehicle == v->index) {
3481 ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos, true); // lock the main view on the vehicle's last position
3482 w->viewport->CancelFollow(*w);
3483 }
3484}
3485
3486
3492void CcBuildPrimaryVehicle(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray)
3493{
3494 if (result.Failed()) return;
3495
3496 const Vehicle *v = Vehicle::Get(new_veh_id);
3498}
3499
3506{
3507 switch (v->type) {
3508 case VEH_TRAIN:
3509 return Train::From(v)->GetDisplayImageWidth();
3510
3511 case VEH_ROAD:
3512 return RoadVehicle::From(v)->GetDisplayImageWidth();
3513
3514 default:
3515 bool rtl = _current_text_dir == TD_RTL;
3516 VehicleSpriteSeq seq;
3517 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3518 Rect rec;
3519 seq.GetBounds(&rec);
3520 return UnScaleGUI(rec.Width());
3521 }
3522}
3523
3529int GetVehicleWidth(const Vehicle *v, EngineImageType image_type)
3530{
3531 if (v->type == VEH_TRAIN || v->type == VEH_ROAD) {
3532 int vehicle_width = 0;
3533 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
3534 vehicle_width += GetSingleVehicleWidth(u, image_type);
3535 }
3536 return vehicle_width;
3537 } else {
3538 return GetSingleVehicleWidth(v, image_type);
3539 }
3540}
3541
3548{
3549 bool rtl = _current_text_dir == TD_RTL;
3550
3551 _cursor.sprites.clear();
3552 int total_width = 0;
3553 int y_offset = 0;
3554 bool rotor_seq = false; // Whether to draw the rotor of the vehicle in this step.
3555 bool is_ground_vehicle = v->IsGroundVehicle();
3556
3557 while (v != nullptr) {
3558 if (total_width >= ScaleSpriteTrad(2 * (int)VEHICLEINFO_FULL_VEHICLE_WIDTH)) break;
3559
3561 VehicleSpriteSeq seq;
3562
3563 if (rotor_seq) {
3564 GetCustomRotorSprite(Aircraft::From(v), image_type, &seq);
3565 if (!seq.IsValid()) seq.Set(SPR_ROTOR_STOPPED);
3566 y_offset = -ScaleSpriteTrad(5);
3567 } else {
3568 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3569 }
3570
3571 int x_offs = 0;
3572 if (v->type == VEH_TRAIN) x_offs = Train::From(v)->GetCursorImageOffset();
3573
3574 for (uint i = 0; i < seq.count; ++i) {
3575 PaletteID pal2 = v->vehstatus.Test(VehState::Crashed) || !seq.seq[i].pal ? pal : seq.seq[i].pal;
3576 _cursor.sprites.emplace_back(seq.seq[i].sprite, pal2, rtl ? (-total_width + x_offs) : (total_width + x_offs), y_offset);
3577 }
3578
3579 if (v->type == VEH_AIRCRAFT && v->subtype == AIR_HELICOPTER && !rotor_seq) {
3580 /* Draw rotor part in the next step. */
3581 rotor_seq = true;
3582 } else {
3583 total_width += GetSingleVehicleWidth(v, image_type);
3584 v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr;
3585 }
3586 }
3587
3588 if (is_ground_vehicle) {
3589 /* Center trains and road vehicles on the front vehicle */
3590 int offs = (ScaleSpriteTrad(VEHICLEINFO_FULL_VEHICLE_WIDTH) - total_width) / 2;
3591 if (rtl) offs = -offs;
3592 for (auto &cs : _cursor.sprites) {
3593 cs.pos.x += offs;
3594 }
3595 }
3596
3598}
Base for aircraft.
@ VAF_DEST_TOO_FAR
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.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
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 HasAtMostOneBit(T value)
Test whether value has at most 1 bit set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:23
bool IsValidCargoType(CargoType t)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
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:33
bool IsCargoInClass(CargoType c, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
@ Passengers
Passengers.
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.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
bool IsDescSortOrder() const
Check if the sort order is descending.
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here.
bool(const GUIVehicleGroup *, CargoType) FilterFunction
Signature of filter function.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void SetFilterType(uint8_t n_type)
Set the filtertype of the list.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
uint8_t SortType() const
Get the sorttype of the list.
Listing GetListing() const
Export current sort conditions.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
bool NeedResort()
Check if a resort is needed next loop If used the resort timer will decrease every call till 0.
void SetSortType(uint8_t n_type)
Set the sorttype of the list.
Baseclass for nested widgets.
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:1138
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:1410
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:2424
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:2447
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:2521
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
The date of the last day of the max year.
static constexpr int DAYS_IN_YEAR
days per year
static constexpr TimerGame< struct Economy >::Year MAX_YEAR
MAX_YEAR, nicely rounded value of the number of years that can be encoded in a single 32 bits date,...
static constexpr int DAYS_IN_LEAP_YEAR
sometimes, you need one day more...
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)
Calculate the year of a given date.
static constexpr Date DateAtStartOfYear(Year year)
Calculate the date of the first day of a given year.
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:29
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:58
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:94
RectPadding matrix
Padding of WWT_MATRIX items.
Definition window_gui.h:42
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:61
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
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 cmd, 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:66
@ 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)
Show a dropdown menu window near a widget of the parent window.
Definition dropdown.cpp:441
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:404
Dimension GetDropDownListDimension(const DropDownList &list)
Determine width and height required to fully display a DropDownList.
Definition dropdown.cpp:363
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
bool IsEngineRefittable(EngineID engine)
Check if an engine is refittable.
Definition engine.cpp:1296
Functions related to engines.
@ AutoRefit
Automatic refitting is allowed.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:923
void UpdateCursorSize()
Update cursor dimension.
Definition gfx.cpp:1569
bool _shift_pressed
Is Shift pressed?
Definition gfx.cpp:39
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:852
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:890
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:658
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
uint GetStringListWidth(std::span< const StringID > list, FontSize fontsize)
Get maximum width of a list of strings.
Definition gfx.cpp:875
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:989
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:775
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:67
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
Definition gfx_func.h:166
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:244
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:243
@ SA_LEFT
Left align the text.
Definition gfx_type.h:375
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:376
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:385
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:294
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:319
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 NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart SetArrowWidgetTypeTip(ArrowWidgetValues widget_type, StringID tip={})
Widget part function for setting the arrow widget type and tooltip.
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 cmd, const CommandCost &result, GroupID new_group, GroupID, VehicleID veh_id, 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:943
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.
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:79
bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
Zooms a viewport in a window in or out.
Definition main_gui.cpp:93
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:403
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
void MemSetT(T *ptr, uint8_t value, size_t num=1)
Type-safe version of memset().
Definition mem_func.hpp:49
void GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:690
void ShowQueryString(std::string_view str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
static constexpr CargoType CF_FREIGHT
Show only vehicles which carry any freight (non-passenger) cargo.
Definition cargo_type.h:98
static constexpr CargoType CF_NONE
Show only items which do not carry cargo (e.g. train engines)
Definition cargo_type.h:96
static constexpr CargoType CF_ANY
Show all items independent of carried cargo (i.e. no filtering)
Definition cargo_type.h:95
@ 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)
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.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
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
@ ODATFB_UNBUNCH
Service the vehicle and then unbunch it.
Definition order_type.h:122
@ ODATFB_HALT
Service the vehicle and then halt it.
Definition order_type.h:120
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel)
Definition order_type.h:40
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:387
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
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:58
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:57
@ SWS_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.
Functions to cache sprites in memory.
static const PaletteID PALETTE_ALL_BLACK
Exchange any color by black, needed for painting fictive tiles outside map.
Definition sprites.h:1615
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1609
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1394
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:607
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:248
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:338
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:426
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:230
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:72
std::string name
Name of vehicle.
VehicleOrderID cur_real_order_index
The index to the current real (non-implicit) order.
TimerGameTick::Ticks lateness_counter
How many ticks late (or early if negative) this vehicle is.
uint16_t vehicle_flags
Used for gradual loading and other miscellaneous things (.
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.
void SetCargoFilter(uint8_t index)
Set cargo filter for the vehicle group list.
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:113
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
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:244
GUISettings gui
settings related to the GUI
std::vector< CursorSprite > sprites
Sprites comprising cursor.
Definition gfx_type.h:135
Dimensions (a width and height) of a rectangle in 2D.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
StringID GetAircraftTypeText() const
Get the name of the aircraft type for display purposes.
Definition engine.cpp:467
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:168
bool show_cargo_in_vehicle_lists
Show the cargoes the vehicles can carry in the list windows.
uint8_t scrollwheel_scrolling
scrolling using the scroll wheel?
uint8_t advanced_vehicle_list
use the "advanced" vehicle list
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.
VehicleSettings vehicle
options for vehicles
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:37
All data for a single hotkey.
Definition hotkeys.h:21
Partial widget specification to allow NWidgets to be written nested.
Order * GetFirstOrder() const
Get the first order of the order chain.
Definition order_base.h:294
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:103
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:70
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:76
Order * next
Pointer to next order. If nullptr, end of list.
Definition order_base.h:59
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:146
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed)
Definition gfx_type.h:24
Coordinates of a point in 2D.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(auto index)
Returns Titem with given index.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
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.
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.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:213
static bool IsExpected(const BaseStation *st)
Helper for checking whether the given station is of this type.
T * Next() const
Get next vehicle in the chain.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
Window * GetCallbackWnd()
Get the window that started the current highlighting.
HighLightStyle place_mode
Method which is used to place the selection.
WindowClass window_class
The WindowClass of the window that is responsible for the selection mode.
WindowNumber window_number
The WindowNumber of the window that is responsible for the selection mode.
'Train' is either a loco or a wagon.
Definition train.h:90
int GetDisplayImageWidth(Point *offset=nullptr) const
Get the width of a train vehicle image in the GUI.
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.
Class for managing the vehicle details window.
bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
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.
The information about a vehicle list.
Definition vehiclelist.h:32
VehicleListType type
The type of vehicle list.
Definition vehiclelist.h:33
CompanyID company
The company associated with this list.
Definition vehiclelist.h:35
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 OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
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.
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.
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.
uint8_t roadveh_acceleration_model
realistic acceleration for road vehicles
uint8_t train_acceleration_model
realistic acceleration for trains
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:103
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 OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnMouseWheel(int wheel) override
The mouse wheel has been turned.
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 DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
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:744
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
Vehicle * GetNextArticulatedPart() const
Get the next part of an articulated engine.
Order * GetOrder(int index) const
Returns order 'index' of a vehicle or nullptr when it doesn't exists.
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.
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:754
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:3248
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:764
GroundVehicleCache * GetGroundVehicleCache()
Access the ground vehicle cache of the vehicle.
Definition vehicle.cpp:3144
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:2552
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.
debug_inline bool IsGroundVehicle() const
Check if the vehicle is a ground vehicle.
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.
void CancelFollow(const Window &viewport_window)
Cancel viewport vehicle following, and raise follow location widget if needed.
VehicleID follow_vehicle
VehicleID to follow if following a vehicle, VehicleID::Invalid() otherwise.
Definition window_gui.h:252
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:955
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1050
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:793
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1736
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:744
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3164
Window * parent
Parent window.
Definition window_gui.h:329
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:554
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:502
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:776
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1038
ResizeInfo resize
Resize information.
Definition window_gui.h:315
virtual bool OnVehicleSelect(const struct Vehicle *v)
The user clicked on a vehicle while HT_VEHICLE has been set.
Definition window_gui.h:799
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:1726
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:319
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
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:973
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:311
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:568
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
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
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.
@ VRF_TRAIN_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:40
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:93
PaletteID GetVehiclePalette(const Vehicle *v)
Get the colour map for a vehicle.
Definition vehicle.cpp:2143
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:3207
@ Crashed
Vehicle is crashed.
@ Stopped
Vehicle is stopped by the player.
@ VF_PATHFINDER_LOST
Vehicle's pathfinder is lost.
Command definitions for vehicles.
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
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.
void CcBuildPrimaryVehicle(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray)
This is the Callback method after the construction attempt of a primary vehicle.
uint GetUnitNumberDigits(VehicleList &vehicles)
Get the number of digits the biggest unit number of a set of vehicles has.
static constexpr NWidgetPart _nested_vehicle_view_widgets[]
Vehicle view widgets.
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 constexpr NWidgetPart _nested_nontrain_vehicle_details_widgets[]
Vehicle details widgets (other than train).
static bool IsVehicleRefitable(const Vehicle *v)
Checks whether the vehicle may be refitted at the moment.
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 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.
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 CMD_START_STOP_VEHICLE for given vehicle.
static constexpr NWidgetPart _nested_train_vehicle_details_widgets[]
Train details widgets.
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 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, ...
VehicleType
Available vehicle types.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
@ VEH_COMPANY_END
Last company-ownable 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_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:296
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:48
@ 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: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:65
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:45
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:56
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:49
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:54
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:51
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:75
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:67
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:59
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:74
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:58
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:55
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:72
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:60
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition widget_type.h:53
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:70
@ 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:21
@ AWV_INCREASE
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:22
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:1143
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1130
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1101
@ Construction
This window is used for construction; close it whenever changing company.
@ BorderOnly
Draw border only, no background.
@ DisableVpScroll
Window does not do autoscroll,.
@ 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:145
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
WindowClass
Window classes.
Definition window_type.h:46
@ 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:47
@ 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:16
@ ZOOM_LVL_ROADVEH
Default zoom level for the road vehicle view.
Definition zoom_type.h:35
@ ZOOM_LVL_AIRCRAFT
Default zoom level for the aircraft view.
Definition zoom_type.h:32
@ ZOOM_LVL_TRAIN
Default zoom level for the train view.
Definition zoom_type.h:34
@ ZOOM_LVL_SHIP
Default zoom level for the ship view.
Definition zoom_type.h:33