OpenTTD Source 20250528-master-g3aca5d62a8
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 "table/strings.h"
50
51#include "safeguards.h"
52
53
54static std::array<std::array<BaseVehicleListWindow::GroupBy, VEH_COMPANY_END>, VLT_END> _grouping{};
55static std::array<Sorting, BaseVehicleListWindow::GB_END> _sorting{};
56
57static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNumberSorter;
58static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNameSorter;
59static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleAgeSorter;
60static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitThisYearSorter;
61static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitLastYearSorter;
62static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleCargoSorter;
63static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleReliabilitySorter;
64static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleMaxSpeedSorter;
65static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleModelSorter;
66static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleValueSorter;
67static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleLengthSorter;
68static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimeToLiveSorter;
69static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimetableDelaySorter;
70static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupLengthSorter;
71static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitThisYearSorter;
72static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitLastYearSorter;
73static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitThisYearSorter;
74static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitLastYearSorter;
75
77template <BaseVehicleListWindow::VehicleIndividualSortFunction func>
79{
80 return func(*(a.vehicles_begin), *(b.vehicles_begin));
81}
82
83const std::initializer_list<BaseVehicleListWindow::VehicleGroupSortFunction * const> BaseVehicleListWindow::vehicle_group_none_sorter_funcs = {
84 &VehicleIndividualToGroupSorterWrapper<VehicleNumberSorter>,
85 &VehicleIndividualToGroupSorterWrapper<VehicleNameSorter>,
86 &VehicleIndividualToGroupSorterWrapper<VehicleAgeSorter>,
87 &VehicleIndividualToGroupSorterWrapper<VehicleProfitThisYearSorter>,
88 &VehicleIndividualToGroupSorterWrapper<VehicleProfitLastYearSorter>,
89 &VehicleIndividualToGroupSorterWrapper<VehicleCargoSorter>,
90 &VehicleIndividualToGroupSorterWrapper<VehicleReliabilitySorter>,
91 &VehicleIndividualToGroupSorterWrapper<VehicleMaxSpeedSorter>,
92 &VehicleIndividualToGroupSorterWrapper<VehicleModelSorter>,
93 &VehicleIndividualToGroupSorterWrapper<VehicleValueSorter>,
94 &VehicleIndividualToGroupSorterWrapper<VehicleLengthSorter>,
95 &VehicleIndividualToGroupSorterWrapper<VehicleTimeToLiveSorter>,
96 &VehicleIndividualToGroupSorterWrapper<VehicleTimetableDelaySorter>,
97};
98
99const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_none_sorter_names_calendar = {
100 STR_SORT_BY_NUMBER,
101 STR_SORT_BY_NAME,
102 STR_SORT_BY_AGE,
103 STR_SORT_BY_PROFIT_THIS_YEAR,
104 STR_SORT_BY_PROFIT_LAST_YEAR,
105 STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
106 STR_SORT_BY_RELIABILITY,
107 STR_SORT_BY_MAX_SPEED,
108 STR_SORT_BY_MODEL,
109 STR_SORT_BY_VALUE,
110 STR_SORT_BY_LENGTH,
111 STR_SORT_BY_LIFE_TIME,
112 STR_SORT_BY_TIMETABLE_DELAY,
113};
114
115const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_none_sorter_names_wallclock = {
116 STR_SORT_BY_NUMBER,
117 STR_SORT_BY_NAME,
118 STR_SORT_BY_AGE,
119 STR_SORT_BY_PROFIT_THIS_PERIOD,
120 STR_SORT_BY_PROFIT_LAST_PERIOD,
121 STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
122 STR_SORT_BY_RELIABILITY,
123 STR_SORT_BY_MAX_SPEED,
124 STR_SORT_BY_MODEL,
125 STR_SORT_BY_VALUE,
126 STR_SORT_BY_LENGTH,
127 STR_SORT_BY_LIFE_TIME,
128 STR_SORT_BY_TIMETABLE_DELAY,
129};
130
131const std::initializer_list<BaseVehicleListWindow::VehicleGroupSortFunction * const> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_funcs = {
132 &VehicleGroupLengthSorter,
133 &VehicleGroupTotalProfitThisYearSorter,
134 &VehicleGroupTotalProfitLastYearSorter,
135 &VehicleGroupAverageProfitThisYearSorter,
136 &VehicleGroupAverageProfitLastYearSorter,
137};
138
139const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_names_calendar = {
140 STR_SORT_BY_NUM_VEHICLES,
141 STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR,
142 STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR,
143 STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR,
144 STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR,
145};
146
147const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_shared_orders_sorter_names_wallclock = {
148 STR_SORT_BY_NUM_VEHICLES,
149 STR_SORT_BY_TOTAL_PROFIT_THIS_PERIOD,
150 STR_SORT_BY_TOTAL_PROFIT_LAST_PERIOD,
151 STR_SORT_BY_AVERAGE_PROFIT_THIS_PERIOD,
152 STR_SORT_BY_AVERAGE_PROFIT_LAST_PERIOD,
153};
154
155const std::initializer_list<const StringID> BaseVehicleListWindow::vehicle_group_by_names = {
156 STR_GROUP_BY_NONE,
157 STR_GROUP_BY_SHARED_ORDERS,
158};
159
160const StringID BaseVehicleListWindow::vehicle_depot_name[] = {
161 STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT,
162 STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT,
163 STR_VEHICLE_LIST_SEND_SHIP_TO_DEPOT,
164 STR_VEHICLE_LIST_SEND_AIRCRAFT_TO_HANGAR
165};
166
167BaseVehicleListWindow::BaseVehicleListWindow(WindowDesc &desc, const VehicleListIdentifier &vli) : Window(desc), vli(vli)
168{
169 this->vehicle_sel = VehicleID::Invalid();
170 this->grouping = _grouping[vli.type][vli.vtype];
171 this->UpdateSortingFromGrouping();
172}
173
174std::span<const StringID> BaseVehicleListWindow::GetVehicleSorterNames() const
175{
176 switch (this->grouping) {
177 case GB_NONE:
178 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_none_sorter_names_wallclock : vehicle_group_none_sorter_names_calendar;
179 case GB_SHARED_ORDERS:
180 return TimerGameEconomy::UsingWallclockUnits() ? vehicle_group_shared_orders_sorter_names_wallclock : vehicle_group_shared_orders_sorter_names_calendar;
181 default:
182 NOT_REACHED();
183 }
184}
185
192{
193 if (number >= 10000) return 5;
194 if (number >= 1000) return 4;
195 if (number >= 100) return 3;
196
197 /*
198 * When the smallest unit number is less than 10, it is
199 * quite likely that it will expand to become more than
200 * 10 quite soon.
201 */
202 return 2;
203}
204
211{
212 uint unitnumber = 0;
213 for (const Vehicle *v : vehicles) {
214 unitnumber = std::max<uint>(unitnumber, v->unitnumber);
215 }
216
217 return CountDigitsForAllocatingSpace(unitnumber);
218}
219
220void BaseVehicleListWindow::BuildVehicleList()
221{
222 if (!this->vehgroups.NeedRebuild()) return;
223
224 Debug(misc, 3, "Building vehicle list type {} for company {} given index {}", this->vli.type, this->vli.company, this->vli.index);
225
226 this->vehgroups.clear();
227
228 GenerateVehicleSortList(&this->vehicles, this->vli);
229
230 CargoTypes used = 0;
231 for (const Vehicle *v : this->vehicles) {
232 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
233 if (u->cargo_cap > 0) SetBit(used, u->cargo_type);
234 }
235 }
236 this->used_cargoes = used;
237
238 if (this->grouping == GB_NONE) {
239 uint max_unitnumber = 0;
240 for (auto it = this->vehicles.begin(); it != this->vehicles.end(); ++it) {
241 this->vehgroups.emplace_back(it, it + 1);
242
243 max_unitnumber = std::max<uint>(max_unitnumber, (*it)->unitnumber);
244 }
245 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber);
246 } else {
247 /* Sort by the primary vehicle; we just want all vehicles that share the same orders to form a contiguous range. */
248 std::stable_sort(this->vehicles.begin(), this->vehicles.end(), [](const Vehicle * const &u, const Vehicle * const &v) {
249 return u->FirstShared() < v->FirstShared();
250 });
251
252 uint max_num_vehicles = 0;
253
254 VehicleList::const_iterator begin = this->vehicles.begin();
255 while (begin != this->vehicles.end()) {
256 VehicleList::const_iterator end = std::find_if_not(begin, this->vehicles.cend(), [first_shared = (*begin)->FirstShared()](const Vehicle * const &v) {
257 return v->FirstShared() == first_shared;
258 });
259
260 this->vehgroups.emplace_back(begin, end);
261
262 max_num_vehicles = std::max<uint>(max_num_vehicles, static_cast<uint>(end - begin));
263
264 begin = end;
265 }
266
267 this->unitnumber_digits = CountDigitsForAllocatingSpace(max_num_vehicles);
268 }
269 this->FilterVehicleList();
270
271 this->vehgroups.RebuildDone();
272 this->vscroll->SetCount(this->vehgroups.size());
273}
274
282static bool CargoFilterSingle(const Vehicle *v, const CargoType cargo_type)
283{
284 if (cargo_type == CargoFilterCriteria::CF_ANY) {
285 return true;
286 } else if (cargo_type == CargoFilterCriteria::CF_NONE) {
287 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
288 if (w->cargo_cap > 0) {
289 return false;
290 }
291 }
292 return true;
293 } else if (cargo_type == CargoFilterCriteria::CF_FREIGHT) {
294 bool have_capacity = false;
295 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
296 if (w->cargo_cap > 0) {
297 if (IsCargoInClass(w->cargo_type, CargoClass::Passengers)) {
298 return false;
299 } else {
300 have_capacity = true;
301 }
302 }
303 }
304 return have_capacity;
305 } else {
306 for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
307 if (w->cargo_cap > 0 && w->cargo_type == cargo_type) {
308 return true;
309 }
310 }
311 return false;
312 }
313}
314
322static bool CargoFilter(const GUIVehicleGroup *vehgroup, const CargoType cargo_type)
323{
324 auto it = vehgroup->vehicles_begin;
325
326 /* Check if any vehicle in the group matches; if so, the whole group does. */
327 for (; it != vehgroup->vehicles_end; it++) {
328 if (CargoFilterSingle(*it, cargo_type)) return true;
329 }
330
331 return false;
332}
333
339{
341}
342
350void AddCargoIconOverlay(std::vector<CargoIconOverlay> &overlays, int x, int width, const Vehicle *v)
351{
352 bool rtl = _current_text_dir == TD_RTL;
353 if (!v->IsArticulatedPart() || v->cargo_type != v->Previous()->cargo_type) {
354 /* Add new overlay slot. */
355 overlays.emplace_back(rtl ? x - width : x, rtl ? x : x + width, v->cargo_type, v->cargo_cap);
356 } else {
357 /* This is an articulated part with the same cargo type, adjust left or right of last overlay slot. */
358 if (rtl) {
359 overlays.back().left -= width;
360 } else {
361 overlays.back().right += width;
362 }
363 overlays.back().cargo_cap += v->cargo_cap;
364 }
365}
366
373void DrawCargoIconOverlay(int x, int y, CargoType cargo_type)
374{
375 if (!ShowCargoIconOverlay()) return;
376 if (!IsValidCargoType(cargo_type)) return;
377
378 const CargoSpec *cs = CargoSpec::Get(cargo_type);
379
380 SpriteID spr = cs->GetCargoIcon();
381 if (spr == 0) return;
382
383 Dimension d = GetSpriteSize(spr);
384 d.width /= 2;
385 d.height /= 2;
386 int one = ScaleGUITrad(1);
387
388 /* Draw the cargo icon in black shifted 4 times to create the outline. */
389 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width - one, y - d.height);
390 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width + one, y - d.height);
391 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height - one);
392 DrawSprite(spr, PALETTE_ALL_BLACK, x - d.width, y - d.height + one);
393 /* Draw the cargo icon normally. */
394 DrawSprite(spr, PAL_NONE, x - d.width, y - d.height);
395}
396
402void DrawCargoIconOverlays(std::span<const CargoIconOverlay> overlays, int y)
403{
404 for (const auto &cio : overlays) {
405 if (cio.cargo_cap == 0) continue;
406 DrawCargoIconOverlay((cio.left + cio.right) / 2, y, cio.cargo_type);
407 }
408}
409
410static GUIVehicleGroupList::FilterFunction * const _vehicle_group_filter_funcs[] = {
412};
413
419{
420 if (this->cargo_filter_criteria != cargo_type) {
421 this->cargo_filter_criteria = cargo_type;
422 /* Deactivate filter if criteria is 'Show All', activate it otherwise. */
424 this->vehgroups.SetFilterType(0);
425 this->vehgroups.ForceRebuild();
426 }
427}
428
438
443{
445 if (this->vehicles.empty()) {
446 /* No vehicle passed through the filter, invalidate the previously selected vehicle */
447 this->vehicle_sel = VehicleID::Invalid();
448 } else if (this->vehicle_sel != VehicleID::Invalid() && std::ranges::find(this->vehicles, Vehicle::Get(this->vehicle_sel)) == this->vehicles.end()) { // previously selected engine didn't pass the filter, remove selection
449 this->vehicle_sel = VehicleID::Invalid();
450 }
451}
452
460Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create)
461{
462 Dimension d = {0, 0};
463
464 if (show_autoreplace) d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_REPLACE_VEHICLES));
465 d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_SEND_FOR_SERVICING));
466 d = maxdim(d, GetStringBoundingBox(this->vehicle_depot_name[this->vli.vtype]));
467
468 if (show_group) {
469 d = maxdim(d, GetStringBoundingBox(STR_GROUP_ADD_SHARED_VEHICLE));
470 d = maxdim(d, GetStringBoundingBox(STR_GROUP_REMOVE_ALL_VEHICLES));
471 } else if (show_create) {
472 d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_CREATE_GROUP));
473 }
474
475 return d;
476}
477
479{
480 this->order_arrow_width = std::max(GetStringBoundingBox(STR_JUST_LEFT_ARROW, FS_SMALL).width, GetStringBoundingBox(STR_JUST_RIGHT_ARROW, FS_SMALL).width);
481 this->SetCargoFilterArray();
482}
483
484StringID BaseVehicleListWindow::GetCargoFilterLabel(CargoType cargo_type) const
485{
486 switch (cargo_type) {
487 case CargoFilterCriteria::CF_ANY: return STR_CARGO_TYPE_FILTER_ALL;
488 case CargoFilterCriteria::CF_FREIGHT: return STR_CARGO_TYPE_FILTER_FREIGHT;
489 case CargoFilterCriteria::CF_NONE: return STR_CARGO_TYPE_FILTER_NONE;
490 default: return CargoSpec::Get(cargo_type)->name;
491 }
492}
493
500{
501 DropDownList list;
502
503 /* Add item for disabling filtering. */
504 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
505 /* Add item for freight (i.e. vehicles with cargo capacity and with no passenger capacity). */
506 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_FREIGHT), CargoFilterCriteria::CF_FREIGHT));
507 /* Add item for vehicles not carrying anything, e.g. train engines. */
508 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
509
510 /* Add cargos */
512 for (const CargoSpec *cs : _sorted_cargo_specs) {
513 if (!full && !HasBit(this->used_cargoes, cs->Index())) continue;
514 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index(), false, !HasBit(this->used_cargoes, cs->Index())));
515 }
516
517 return list;
518}
519
527DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create)
528{
529 DropDownList list;
530
531 /* Autoreplace actions. */
532 if (show_autoreplace) {
533 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_REPLACE_VEHICLES, ADI_REPLACE));
534 list.push_back(MakeDropDownListDividerItem());
535 }
536
537 /* Group actions. */
538 if (show_group) {
539 list.push_back(MakeDropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE, ADI_ADD_SHARED));
540 list.push_back(MakeDropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, ADI_REMOVE_ALL));
541 list.push_back(MakeDropDownListDividerItem());
542 } else if (show_create) {
543 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_CREATE_GROUP, ADI_CREATE_GROUP));
544 list.push_back(MakeDropDownListDividerItem());
545 }
546
547 /* Depot actions. */
548 list.push_back(MakeDropDownListStringItem(STR_VEHICLE_LIST_SEND_FOR_SERVICING, ADI_SERVICE));
549 list.push_back(MakeDropDownListStringItem(this->vehicle_depot_name[this->vli.vtype], ADI_DEPOT));
550
551 return list;
552}
553
554/* cached values for VehicleNameSorter to spare many GetString() calls */
555static const Vehicle *_last_vehicle[2] = { nullptr, nullptr };
556
557void BaseVehicleListWindow::SortVehicleList()
558{
559 if (this->vehgroups.Sort()) return;
560
561 /* invalidate cached values for name sorter - vehicle names could change */
562 _last_vehicle[0] = _last_vehicle[1] = nullptr;
563}
564
565void DepotSortList(VehicleList *list)
566{
567 if (list->size() < 2) return;
568 std::sort(list->begin(), list->end(), &VehicleNumberSorter);
569}
570
572static void DrawVehicleProfitButton(TimerGameEconomy::Date age, Money display_profit_last_year, uint num_vehicles, int x, int y)
573{
574 SpriteID spr;
575
576 /* draw profit-based coloured icons */
577 if (age <= VEHICLE_PROFIT_MIN_AGE) {
578 spr = SPR_PROFIT_NA;
579 } else if (display_profit_last_year < 0) {
580 spr = SPR_PROFIT_NEGATIVE;
581 } else if (display_profit_last_year < VEHICLE_PROFIT_THRESHOLD * num_vehicles) {
582 spr = SPR_PROFIT_SOME;
583 } else {
584 spr = SPR_PROFIT_LOT;
585 }
586 DrawSprite(spr, PAL_NONE, x, y);
587}
588
590static const uint MAX_REFIT_CYCLE = 256;
591
601uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_cargo_type)
602{
603 v_from = v_from->GetFirstEnginePart();
604 v_for = v_for->GetFirstEnginePart();
605
606 /* Create a list of subtypes used by the various parts of v_for */
607 static std::vector<StringID> subtypes;
608 subtypes.clear();
609 for (; v_from != nullptr; v_from = v_from->HasArticulatedPart() ? v_from->GetNextArticulatedPart() : nullptr) {
610 const Engine *e_from = v_from->GetEngine();
611 if (!e_from->CanCarryCargo() || !e_from->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
612 include(subtypes, GetCargoSubtypeText(v_from));
613 }
614
615 uint8_t ret_refit_cyc = 0;
616 bool success = false;
617 if (!subtypes.empty()) {
618 /* Check whether any articulated part is refittable to 'dest_cargo_type' with a subtype listed in 'subtypes' */
619 for (Vehicle *v = v_for; v != nullptr; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr) {
620 const Engine *e = v->GetEngine();
621 if (!e->CanCarryCargo() || !e->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
622 if (!HasBit(e->info.refit_mask, dest_cargo_type) && v->cargo_type != dest_cargo_type) continue;
623
624 CargoType old_cargo_type = v->cargo_type;
625 uint8_t old_cargo_subtype = v->cargo_subtype;
626
627 /* Set the 'destination' cargo */
628 v->cargo_type = dest_cargo_type;
629
630 /* Cycle through the refits */
631 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
632 v->cargo_subtype = refit_cyc;
633
634 /* Make sure we don't pick up anything cached. */
635 v->First()->InvalidateNewGRFCache();
636 v->InvalidateNewGRFCache();
637
638 StringID subtype = GetCargoSubtypeText(v);
639 if (subtype == STR_EMPTY) break;
640
641 if (std::ranges::find(subtypes, subtype) == subtypes.end()) continue;
642
643 /* We found something matching. */
644 ret_refit_cyc = refit_cyc;
645 success = true;
646 break;
647 }
648
649 /* Reset the vehicle's cargo type */
650 v->cargo_type = old_cargo_type;
651 v->cargo_subtype = old_cargo_subtype;
652
653 /* Make sure we don't taint the vehicle. */
654 v->First()->InvalidateNewGRFCache();
655 v->InvalidateNewGRFCache();
656
657 if (success) break;
658 }
659 }
660
661 return ret_refit_cyc;
662}
663
667 uint8_t subtype;
669
675 inline bool operator == (const RefitOption &other) const
676 {
677 return other.cargo == this->cargo && other.string == this->string;
678 }
679};
680
681using RefitOptions = std::map<CargoType, std::vector<RefitOption>, CargoTypeComparator>;
682
692static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption *sel, uint pos, uint rows, uint delta, const Rect &r)
693{
694 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
695 uint current = 0;
696
697 bool rtl = _current_text_dir == TD_RTL;
698 uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
699 uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
700 int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
701
702 int iconleft = rtl ? ir.right - iconwidth : ir.left;
703 int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;
704 int iconinner = rtl ? ir.right - iconwidth : ir.left + iconwidth;
705
706 Rect tr = ir.Indent(iconwidth + WidgetDimensions::scaled.hsep_wide, rtl);
707
708 /* Draw the list of subtypes for each cargo, and find the selected refit option (by its position). */
709 for (const auto &pair : refits) {
710 bool has_subtypes = pair.second.size() > 1;
711 for (const RefitOption &refit : pair.second) {
712 if (current >= pos + rows) break;
713
714 /* Hide subtypes if selected cargo type does not match */
715 if ((sel == nullptr || sel->cargo != refit.cargo) && refit.subtype != UINT8_MAX) continue;
716
717 /* Refit options with a position smaller than pos don't have to be drawn. */
718 if (current < pos) {
719 current++;
720 continue;
721 }
722
723 if (has_subtypes) {
724 if (refit.subtype != UINT8_MAX) {
725 /* Draw tree lines */
726 int ycenter = tr.top + GetCharacterHeight(FS_NORMAL) / 2;
727 GfxDrawLine(iconcenter, tr.top - WidgetDimensions::scaled.matrix.top, iconcenter, (&refit == &pair.second.back()) ? ycenter : tr.top - WidgetDimensions::scaled.matrix.top + delta - 1, linecolour);
728 GfxDrawLine(iconcenter, ycenter, iconinner, ycenter, linecolour);
729 } else {
730 /* Draw expand/collapse icon */
731 DrawSprite((sel != nullptr && sel->cargo == refit.cargo) ? SPR_CIRCLE_UNFOLDED : SPR_CIRCLE_FOLDED, PAL_NONE, iconleft, tr.top + (GetCharacterHeight(FS_NORMAL) - iconheight) / 2);
732 }
733 }
734
735 TextColour colour = (sel != nullptr && sel->cargo == refit.cargo && sel->subtype == refit.subtype) ? TC_WHITE : TC_BLACK;
736 /* Get the cargo name. */
737 DrawString(tr, GetString(STR_JUST_STRING_STRING, CargoSpec::Get(refit.cargo)->name, refit.string), colour);
738
739 tr.top += delta;
740 current++;
741 }
742 }
743}
744
746struct RefitWindow : public Window {
747 const RefitOption *selected_refit = nullptr;
751 Scrollbar *vscroll = nullptr;
752 Scrollbar *hscroll = nullptr;
754 int sprite_left = 0;
755 int sprite_right = 0;
756 uint vehicle_margin = 0;
757 int click_x = 0;
759 uint8_t num_vehicles = 0;
760 bool auto_refit = false;
761
766 {
767 /* Store the currently selected RefitOption. */
768 std::optional<RefitOption> current_refit_option;
769 if (this->selected_refit != nullptr) current_refit_option = *(this->selected_refit);
770 this->selected_refit = nullptr;
771
772 this->refit_list.clear();
774
775 /* Check only the selected vehicles. */
776 VehicleSet vehicles_to_refit;
777 GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
778
779 do {
780 if (v->type == VEH_TRAIN && std::ranges::find(vehicles_to_refit, v->index) == vehicles_to_refit.end()) continue;
781 const Engine *e = v->GetEngine();
782 CargoTypes cmask = e->info.refit_mask;
783 VehicleCallbackMasks callback_mask = e->info.callback_mask;
784
785 /* Skip this engine if it does not carry anything */
786 if (!e->CanCarryCargo()) continue;
787 /* Skip this engine if we build the list for auto-refitting and engine doesn't allow it. */
788 if (this->auto_refit && !e->info.misc_flags.Test(EngineMiscFlag::AutoRefit)) continue;
789
790 /* Loop through all cargoes in the refit mask */
791 for (const auto &cs : _sorted_cargo_specs) {
792 CargoType cargo_type = cs->Index();
793 /* Skip cargo type if it's not listed */
794 if (!HasBit(cmask, cargo_type)) continue;
795
796 auto &list = this->refit_list[cargo_type];
797 bool first_vehicle = list.empty();
798 if (first_vehicle) {
799 /* Keeping the current subtype is always an option. It also serves as the option in case of no subtypes */
800 list.emplace_back(cargo_type, UINT8_MAX, STR_EMPTY);
801 }
802
803 /* Check the vehicle's callback mask for cargo suffixes.
804 * This is not supported for ordered refits, since subtypes only have a meaning
805 * for a specific vehicle at a specific point in time, which conflicts with shared orders,
806 * autoreplace, autorenew, clone, order restoration, ... */
807 if (this->order == INVALID_VEH_ORDER_ID && callback_mask.Test(VehicleCallbackMask::CargoSuffix)) {
808 /* Make a note of the original cargo type. It has to be
809 * changed to test the cargo & subtype... */
810 CargoType temp_cargo = v->cargo_type;
811 uint8_t temp_subtype = v->cargo_subtype;
812
813 v->cargo_type = cargo_type;
814
815 for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
816 v->cargo_subtype = refit_cyc;
817
818 /* Make sure we don't pick up anything cached. */
821
822 StringID subtype = GetCargoSubtypeText(v);
823
824 if (first_vehicle) {
825 /* Append new subtype (don't add duplicates though) */
826 if (subtype == STR_EMPTY) break;
827
828 RefitOption option{cargo_type, static_cast<uint8_t>(refit_cyc), subtype};
829 include(list, option);
830 } else {
831 /* Intersect the subtypes of earlier vehicles with the subtypes of this vehicle */
832 if (subtype == STR_EMPTY) {
833 /* No more subtypes for this vehicle, delete all subtypes >= refit_cyc */
834 /* UINT8_MAX item is in front, other subtypes are sorted. So just truncate the list in the right spot */
835 for (uint i = 1; i < list.size(); i++) {
836 if (list[i].subtype >= refit_cyc) {
837 list.erase(list.begin() + i, list.end());
838 break;
839 }
840 }
841 break;
842 } else {
843 /* Check whether the subtype matches with the subtype of earlier vehicles. */
844 uint pos = 1;
845 while (pos < list.size() && list[pos].subtype != refit_cyc) pos++;
846 if (pos < list.size() && list[pos].string != subtype) {
847 /* String mismatch, remove item keeping the order */
848 list.erase(list.begin() + pos);
849 }
850 }
851 }
852 }
853
854 /* Reset the vehicle's cargo type */
855 v->cargo_type = temp_cargo;
856 v->cargo_subtype = temp_subtype;
857
858 /* And make sure we haven't tainted the cache */
861 }
862 }
863 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
864
865 /* Restore the previously selected RefitOption. */
866 if (current_refit_option.has_value()) {
867 for (const auto &pair : this->refit_list) {
868 for (const auto &refit : pair.second) {
869 if (refit.cargo == current_refit_option->cargo && refit.subtype == current_refit_option->subtype) {
870 this->selected_refit = &refit;
871 break;
872 }
873 }
874 if (this->selected_refit != nullptr) break;
875 }
876 }
877
878 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
879 }
880
885 {
886 size_t scroll_row = 0;
887 size_t rows = 0;
888 CargoType cargo = this->selected_refit == nullptr ? INVALID_CARGO : this->selected_refit->cargo;
889
890 for (const auto &pair : this->refit_list) {
891 if (pair.first == cargo) {
892 /* selected_refit points to an element in the vector so no need to search for it. */
893 scroll_row = rows + (this->selected_refit - pair.second.data());
894 rows += pair.second.size();
895 } else {
896 rows++; /* Unselected cargo type is collapsed into one row. */
897 }
898 }
899
900 this->vscroll->SetCount(rows);
901 this->vscroll->ScrollTowards(static_cast<int>(scroll_row));
902 }
903
908 void SetSelection(uint click_row)
909 {
910 uint row = 0;
911
912 for (const auto &pair : refit_list) {
913 for (const RefitOption &refit : pair.second) {
914 if (row == click_row) {
915 this->selected_refit = &refit;
916 return;
917 }
918 row++;
919 /* If this cargo type is not already selected then its subtypes are not visible, so skip the rest. */
920 if (this->selected_refit == nullptr || this->selected_refit->cargo != refit.cargo) break;
921 }
922 }
923
924 /* No selection made */
925 this->selected_refit = nullptr;
926 }
927
928 RefitWindow(WindowDesc &desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window(desc)
929 {
930 this->auto_refit = auto_refit;
931 this->order = order;
932 this->CreateNestedTree();
933
934 this->vscroll = this->GetScrollbar(WID_VR_SCROLLBAR);
935 this->hscroll = (v->IsGroundVehicle() ? this->GetScrollbar(WID_VR_HSCROLLBAR) : nullptr);
936 this->GetWidget<NWidgetCore>(WID_VR_SELECT_HEADER)->SetToolTip(STR_REFIT_TRAIN_LIST_TOOLTIP + v->type);
937 this->GetWidget<NWidgetCore>(WID_VR_MATRIX)->SetToolTip(STR_REFIT_TRAIN_LIST_TOOLTIP + v->type);
938 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_VR_REFIT);
939 nwi->SetStringTip(STR_REFIT_TRAIN_REFIT_BUTTON + v->type, STR_REFIT_TRAIN_REFIT_TOOLTIP + v->type);
940 this->GetWidget<NWidgetStacked>(WID_VR_SHOW_HSCROLLBAR)->SetDisplayedPlane(v->IsGroundVehicle() ? 0 : SZSP_HORIZONTAL);
941 this->GetWidget<NWidgetCore>(WID_VR_VEHICLE_PANEL_DISPLAY)->SetToolTip((v->type == VEH_TRAIN) ? STR_REFIT_SELECT_VEHICLES_TOOLTIP : STR_NULL);
942
943 this->FinishInitNested(v->index);
944 this->owner = v->owner;
945
946 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
947 }
948
949 void OnInit() override
950 {
951 /* (Re)build the refit list */
953 }
954
955 void OnPaint() override
956 {
957 /* Determine amount of items for scroller. */
958 if (this->hscroll != nullptr) this->hscroll->SetCount(this->vehicle_width);
959
960 /* Calculate sprite position. */
961 NWidgetCore *vehicle_panel_display = this->GetWidget<NWidgetCore>(WID_VR_VEHICLE_PANEL_DISPLAY);
962 int sprite_width = std::max(0, ((int)vehicle_panel_display->current_x - this->vehicle_width) / 2);
963 this->sprite_left = vehicle_panel_display->pos_x;
964 this->sprite_right = vehicle_panel_display->pos_x + vehicle_panel_display->current_x - 1;
965 if (_current_text_dir == TD_RTL) {
966 this->sprite_right -= sprite_width;
967 this->vehicle_margin = vehicle_panel_display->current_x - sprite_right;
968 } else {
969 this->sprite_left += sprite_width;
970 this->vehicle_margin = sprite_left;
971 }
972
973 this->DrawWidgets();
974 }
975
976 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
977 {
978 switch (widget) {
979 case WID_VR_MATRIX:
980 resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
981 size.height = resize.height * 8;
982 break;
983
985 size.height = ScaleGUITrad(GetVehicleHeight(Vehicle::Get(this->window_number)->type));
986 break;
987
988 case WID_VR_INFO:
989 size.width = this->information_width + padding.height;
990 break;
991 }
992 }
993
994 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
995 {
996 if (widget == WID_VR_CAPTION) return GetString(STR_REFIT_CAPTION, Vehicle::Get(this->window_number)->index);
997
998 return this->Window::GetWidgetString(widget, stringid);
999 }
1000
1007 std::string GetCapacityString(const RefitOption &option) const
1008 {
1010 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);
1011
1012 if (cost.Failed()) return {};
1013
1014 Money money = cost.GetCost();
1015 if (mail_capacity > 0) {
1016 if (this->order != INVALID_VEH_ORDER_ID) {
1017 /* No predictable cost */
1018 return GetString(STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity);
1019 }
1020
1021 if (money <= 0) {
1022 return GetString(STR_REFIT_NEW_CAPACITY_INCOME_FROM_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, -money);
1023 }
1024
1025 return GetString(STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT, option.cargo, refit_capacity, GetCargoTypeByLabel(CT_MAIL), mail_capacity, money);
1026 }
1027
1028 if (this->order != INVALID_VEH_ORDER_ID) {
1029 /* No predictable cost */
1030 return GetString(STR_PURCHASE_INFO_CAPACITY, option.cargo, refit_capacity, STR_EMPTY);
1031 }
1032
1033 if (money <= 0) {
1034 return GetString(STR_REFIT_NEW_CAPACITY_INCOME_FROM_REFIT, option.cargo, refit_capacity, -money);
1035 }
1036
1037 return GetString(STR_REFIT_NEW_CAPACITY_COST_OF_REFIT, option.cargo, refit_capacity, money);
1038 }
1039
1040 void DrawWidget(const Rect &r, WidgetID widget) const override
1041 {
1042 switch (widget) {
1045 DrawVehicleImage(v, {this->sprite_left, r.top, this->sprite_right, r.bottom},
1046 VehicleID::Invalid(), EIT_IN_DETAILS, this->hscroll != nullptr ? this->hscroll->GetPosition() : 0);
1047
1048 /* Highlight selected vehicles. */
1049 if (this->order != INVALID_VEH_ORDER_ID) break;
1050 int x = 0;
1051 switch (v->type) {
1052 case VEH_TRAIN: {
1053 VehicleSet vehicles_to_refit;
1054 GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
1055
1056 int left = INT32_MIN;
1057 int width = 0;
1058
1059 /* Determine top & bottom position of the highlight.*/
1060 const int height = ScaleSpriteTrad(12);
1061 const int highlight_top = CentreBounds(r.top, r.bottom, height);
1062 const int highlight_bottom = highlight_top + height - 1;
1063
1064 for (Train *u = Train::From(v); u != nullptr; u = u->Next()) {
1065 /* Start checking. */
1066 const bool contained = std::ranges::find(vehicles_to_refit, u->index) != vehicles_to_refit.end();
1067 if (contained && left == INT32_MIN) {
1068 left = x - this->hscroll->GetPosition() + r.left + this->vehicle_margin;
1069 width = 0;
1070 }
1071
1072 /* Draw a selection. */
1073 if ((!contained || u->Next() == nullptr) && left != INT32_MIN) {
1074 if (u->Next() == nullptr && contained) {
1075 int current_width = u->GetDisplayImageWidth();
1076 width += current_width;
1077 x += current_width;
1078 }
1079
1080 int right = Clamp(left + width, 0, r.right);
1081 left = std::max(0, left);
1082
1083 if (_current_text_dir == TD_RTL) {
1084 right = r.Width() - left;
1085 left = right - width;
1086 }
1087
1088 if (left != right) {
1089 Rect hr = {left, highlight_top, right, highlight_bottom};
1091 }
1092
1093 left = INT32_MIN;
1094 }
1095
1096 int current_width = u->GetDisplayImageWidth();
1097 width += current_width;
1098 x += current_width;
1099 }
1100 break;
1101 }
1102
1103 default: break;
1104 }
1105 break;
1106 }
1107
1108 case WID_VR_MATRIX:
1109 DrawVehicleRefitWindow(this->refit_list, this->selected_refit, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->resize.step_height, r);
1110 break;
1111
1112 case WID_VR_INFO:
1113 if (this->selected_refit != nullptr) {
1114 std::string string = this->GetCapacityString(*this->selected_refit);
1115 if (!string.empty()) {
1117 }
1118 }
1119 break;
1120 }
1121 }
1122
1128 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1129 {
1130 switch (data) {
1131 case VIWD_AUTOREPLACE: // Autoreplace replaced the vehicle; selected_vehicle became invalid.
1132 case VIWD_CONSIST_CHANGED: { // The consist has changed; rebuild the entire list.
1133 /* Clear the selection. */
1135 this->selected_vehicle = v->index;
1136 this->num_vehicles = UINT8_MAX;
1137 [[fallthrough]];
1138 }
1139
1140 case 2: { // The vehicle selection has changed; rebuild the entire list.
1141 if (!gui_scope) break;
1142 this->BuildRefitList();
1143
1144 /* The vehicle width has changed too. */
1145 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1146 uint max_width = 0;
1147
1148 /* Check the width of all cargo information strings. */
1149 for (const auto &list : this->refit_list) {
1150 for (const RefitOption &refit : list.second) {
1151 std::string string = this->GetCapacityString(refit);
1152 if (!string.empty()) {
1153 Dimension dim = GetStringBoundingBox(string);
1154 max_width = std::max(dim.width, max_width);
1155 }
1156 }
1157 }
1158
1159 if (this->information_width < max_width) {
1160 this->information_width = max_width;
1161 this->ReInit();
1162 }
1163 [[fallthrough]];
1164 }
1165
1166 case 1: // A new cargo has been selected.
1167 if (!gui_scope) break;
1168 this->RefreshScrollbar();
1169 break;
1170 }
1171 }
1172
1173 int GetClickPosition(int click_x)
1174 {
1175 const NWidgetCore *matrix_widget = this->GetWidget<NWidgetCore>(WID_VR_VEHICLE_PANEL_DISPLAY);
1176 if (_current_text_dir == TD_RTL) click_x = matrix_widget->current_x - click_x;
1177 click_x -= this->vehicle_margin;
1178 if (this->hscroll != nullptr) click_x += this->hscroll->GetPosition();
1179
1180 return click_x;
1181 }
1182
1183 void SetSelectedVehicles(int drag_x)
1184 {
1185 drag_x = GetClickPosition(drag_x);
1186
1187 int left_x = std::min(this->click_x, drag_x);
1188 int right_x = std::max(this->click_x, drag_x);
1189 this->num_vehicles = 0;
1190
1192 /* Find the vehicle part that was clicked. */
1193 switch (v->type) {
1194 case VEH_TRAIN: {
1195 /* Don't select anything if we are not clicking in the vehicle. */
1196 if (left_x >= 0) {
1197 const Train *u = Train::From(v);
1198 bool start_counting = false;
1199 for (; u != nullptr; u = u->Next()) {
1200 int current_width = u->GetDisplayImageWidth();
1201 left_x -= current_width;
1202 right_x -= current_width;
1203
1204 if (left_x < 0 && !start_counting) {
1205 this->selected_vehicle = u->index;
1206 start_counting = true;
1207
1208 /* Count the first vehicle, even if articulated part */
1209 this->num_vehicles++;
1210 } else if (start_counting && !u->IsArticulatedPart()) {
1211 /* Do not count articulated parts */
1212 this->num_vehicles++;
1213 }
1214
1215 if (right_x < 0) break;
1216 }
1217 }
1218
1219 /* If the selection is not correct, clear it. */
1220 if (this->num_vehicles != 0) {
1221 if (_ctrl_pressed) this->num_vehicles = UINT8_MAX;
1222 break;
1223 }
1224 [[fallthrough]];
1225 }
1226
1227 default:
1228 /* Clear the selection. */
1229 this->selected_vehicle = v->index;
1230 this->num_vehicles = UINT8_MAX;
1231 break;
1232 }
1233 }
1234
1235 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1236 {
1237 switch (widget) {
1238 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1239 if (this->order != INVALID_VEH_ORDER_ID) break;
1240 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_VR_VEHICLE_PANEL_DISPLAY);
1241 this->click_x = GetClickPosition(pt.x - nwi->pos_x);
1242 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1244 if (!_ctrl_pressed) {
1246 } else {
1247 /* The vehicle selection has changed. */
1248 this->InvalidateData(2);
1249 }
1250 break;
1251 }
1252
1253 case WID_VR_MATRIX: { // listbox
1254 this->SetSelection(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_VR_MATRIX));
1255 this->SetWidgetDisabledState(WID_VR_REFIT, this->selected_refit == nullptr);
1256 this->InvalidateData(1);
1257
1258 if (click_count == 1) break;
1259 [[fallthrough]];
1260 }
1261
1262 case WID_VR_REFIT: // refit button
1263 if (this->selected_refit != nullptr) {
1264 const Vehicle *v = Vehicle::Get(this->window_number);
1265
1266 if (this->order == INVALID_VEH_ORDER_ID) {
1267 bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
1268 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();
1269 } else {
1270 if (Command<CMD_ORDER_REFIT>::Post(v->tile, v->index, this->order, this->selected_refit->cargo)) this->Close();
1271 }
1272 }
1273 break;
1274 }
1275 }
1276
1277 void OnMouseDrag(Point pt, WidgetID widget) override
1278 {
1279 switch (widget) {
1280 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1281 if (this->order != INVALID_VEH_ORDER_ID) break;
1282 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_VR_VEHICLE_PANEL_DISPLAY);
1283 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1285 break;
1286 }
1287 }
1288 }
1289
1290 void OnDragDrop(Point pt, WidgetID widget) override
1291 {
1292 switch (widget) {
1293 case WID_VR_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
1294 if (this->order != INVALID_VEH_ORDER_ID) break;
1295 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_VR_VEHICLE_PANEL_DISPLAY);
1296 this->SetSelectedVehicles(pt.x - nwi->pos_x);
1297 this->InvalidateData(2);
1298 break;
1299 }
1300 }
1301 }
1302
1303 void OnResize() override
1304 {
1305 this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
1306 this->vscroll->SetCapacityFromWidget(this, WID_VR_MATRIX);
1307 if (this->hscroll != nullptr) this->hscroll->SetCapacityFromWidget(this, WID_VR_VEHICLE_PANEL_DISPLAY);
1308 }
1309};
1310
1311static constexpr NWidgetPart _nested_vehicle_refit_widgets[] = {
1313 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1314 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VR_CAPTION),
1315 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1316 EndContainer(),
1317 /* Vehicle display + scrollbar. */
1322 EndContainer(),
1323 EndContainer(),
1324 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_VR_SELECT_HEADER), SetStringTip(STR_REFIT_TITLE), SetResize(1, 0),
1325 /* Matrix + scrollbar. */
1329 EndContainer(),
1330 NWidget(WWT_PANEL, COLOUR_GREY, WID_VR_INFO), SetMinimalTextLines(2, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(),
1332 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VR_REFIT), SetFill(1, 0), SetResize(1, 0),
1333 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1334 EndContainer(),
1335};
1336
1337static WindowDesc _vehicle_refit_desc(
1338 WDP_AUTO, "view_vehicle_refit", 240, 174,
1341 _nested_vehicle_refit_widgets
1342);
1343
1351void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
1352{
1354 RefitWindow *w = new RefitWindow(_vehicle_refit_desc, v, order, auto_refit);
1355 w->parent = parent;
1356}
1357
1359uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
1360{
1361 /* List of cargo types of this engine */
1362 CargoTypes cmask = GetUnionOfArticulatedRefitMasks(engine, false);
1363 /* List of cargo types available in this climate */
1364 CargoTypes lmask = _cargo_mask;
1365
1366 /* Draw nothing if the engine is not refittable */
1367 if (HasAtMostOneBit(cmask)) return y;
1368
1369 std::string str;
1370 if (cmask == lmask) {
1371 /* Engine can be refitted to all types in this climate */
1372 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_TYPES, std::monostate{});
1373 } else {
1374 /* Check if we are able to refit to more cargo types and unable to. If
1375 * so, invert the cargo types to list those that we can't refit to. */
1376 if (CountBits(cmask ^ lmask) < CountBits(cmask) && CountBits(cmask ^ lmask) <= 7) {
1377 cmask ^= lmask;
1378 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_BUT, cmask);
1379 } else {
1380 str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_JUST_CARGO_LIST, cmask);
1381 }
1382 }
1383
1384 return DrawStringMultiLine(left, right, y, INT32_MAX, str);
1385}
1386
1389{
1390 if (!EngInfo(v->engine_type)->callback_mask.Test(VehicleCallbackMask::CargoSuffix)) return STR_EMPTY;
1391 std::array<int32_t, 1> regs100;
1392 uint16_t cb = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v, regs100);
1393 if (v->GetGRF()->grf_version < 8 && cb == 0xFF) return STR_EMPTY;
1394 if (cb == CALLBACK_FAILED || cb == 0x400) return STR_EMPTY;
1395 if (cb == 0x40F) {
1396 return GetGRFStringID(v->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1397 }
1398 if (cb > 0x400) {
1400 return STR_EMPTY;
1401 }
1402 return GetGRFStringID(v->GetGRFID(), GRFSTR_MISC_GRF_TEXT + cb);
1403}
1404
1406static bool VehicleGroupLengthSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1407{
1408 return a.NumVehicles() < b.NumVehicles();
1409}
1410
1412static bool VehicleGroupTotalProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1413{
1414 return a.GetDisplayProfitThisYear() < b.GetDisplayProfitThisYear();
1415}
1416
1418static bool VehicleGroupTotalProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1419{
1420 return a.GetDisplayProfitLastYear() < b.GetDisplayProfitLastYear();
1421}
1422
1424static bool VehicleGroupAverageProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1425{
1426 return a.GetDisplayProfitThisYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitThisYear() * static_cast<uint>(a.NumVehicles());
1427}
1428
1430static bool VehicleGroupAverageProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
1431{
1432 return a.GetDisplayProfitLastYear() * static_cast<uint>(b.NumVehicles()) < b.GetDisplayProfitLastYear() * static_cast<uint>(a.NumVehicles());
1433}
1434
1436static bool VehicleNumberSorter(const Vehicle * const &a, const Vehicle * const &b)
1437{
1438 return a->unitnumber < b->unitnumber;
1439}
1440
1442static bool VehicleNameSorter(const Vehicle * const &a, const Vehicle * const &b)
1443{
1444 static std::string last_name[2] = { {}, {} };
1445
1446 if (a != _last_vehicle[0]) {
1447 _last_vehicle[0] = a;
1448 last_name[0] = GetString(STR_VEHICLE_NAME, a->index);
1449 }
1450
1451 if (b != _last_vehicle[1]) {
1452 _last_vehicle[1] = b;
1453 last_name[1] = GetString(STR_VEHICLE_NAME, b->index);
1454 }
1455
1456 int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
1457 return (r != 0) ? r < 0: VehicleNumberSorter(a, b);
1458}
1459
1461static bool VehicleAgeSorter(const Vehicle * const &a, const Vehicle * const &b)
1462{
1463 auto r = a->age - b->age;
1464 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1465}
1466
1468static bool VehicleProfitThisYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1469{
1470 int r = ClampTo<int32_t>(a->GetDisplayProfitThisYear() - b->GetDisplayProfitThisYear());
1471 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1472}
1473
1475static bool VehicleProfitLastYearSorter(const Vehicle * const &a, const Vehicle * const &b)
1476{
1477 int r = ClampTo<int32_t>(a->GetDisplayProfitLastYear() - b->GetDisplayProfitLastYear());
1478 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1479}
1480
1482static bool VehicleCargoSorter(const Vehicle * const &a, const Vehicle * const &b)
1483{
1484 const Vehicle *v;
1485 CargoArray diff{};
1486
1487 /* Append the cargo of the connected waggons */
1488 for (v = a; v != nullptr; v = v->Next()) diff[v->cargo_type] += v->cargo_cap;
1489 for (v = b; v != nullptr; v = v->Next()) diff[v->cargo_type] -= v->cargo_cap;
1490
1491 int r = 0;
1492 for (uint d : diff) {
1493 r = d;
1494 if (r != 0) break;
1495 }
1496
1497 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1498}
1499
1501static bool VehicleReliabilitySorter(const Vehicle * const &a, const Vehicle * const &b)
1502{
1503 int r = a->reliability - b->reliability;
1504 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1505}
1506
1508static bool VehicleMaxSpeedSorter(const Vehicle * const &a, const Vehicle * const &b)
1509{
1511 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1512}
1513
1515static bool VehicleModelSorter(const Vehicle * const &a, const Vehicle * const &b)
1516{
1517 int r = a->engine_type.base() - b->engine_type.base();
1518 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1519}
1520
1522static bool VehicleValueSorter(const Vehicle * const &a, const Vehicle * const &b)
1523{
1524 const Vehicle *u;
1525 Money diff = 0;
1526
1527 for (u = a; u != nullptr; u = u->Next()) diff += u->value;
1528 for (u = b; u != nullptr; u = u->Next()) diff -= u->value;
1529
1530 int r = ClampTo<int32_t>(diff);
1531 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1532}
1533
1535static bool VehicleLengthSorter(const Vehicle * const &a, const Vehicle * const &b)
1536{
1538 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1539}
1540
1542static bool VehicleTimeToLiveSorter(const Vehicle * const &a, const Vehicle * const &b)
1543{
1544 int r = ClampTo<int32_t>((a->max_age - a->age) - (b->max_age - b->age));
1545 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1546}
1547
1549static bool VehicleTimetableDelaySorter(const Vehicle * const &a, const Vehicle * const &b)
1550{
1551 int r = a->lateness_counter - b->lateness_counter;
1552 return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
1553}
1554
1555void InitializeGUI()
1556{
1557 _grouping = {};
1558 _sorting = {};
1559}
1560
1567static inline void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
1568{
1569 Window *w = FindWindowById(window_class, from_index);
1570 if (w != nullptr) {
1571 /* Update window_number */
1572 w->window_number = to_index;
1573 if (w->viewport != nullptr) w->viewport->follow_vehicle = to_index;
1574
1575 /* Update vehicle drag data */
1576 if (_thd.window_class == window_class && _thd.window_number == from_index) {
1577 _thd.window_number = to_index;
1578 }
1579
1580 /* Notify the window. */
1582 }
1583}
1584
1591{
1592 ChangeVehicleWindow(WC_VEHICLE_VIEW, from_index, to_index);
1593 ChangeVehicleWindow(WC_VEHICLE_ORDERS, from_index, to_index);
1594 ChangeVehicleWindow(WC_VEHICLE_REFIT, from_index, to_index);
1595 ChangeVehicleWindow(WC_VEHICLE_DETAILS, from_index, to_index);
1596 ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index);
1597}
1598
1599static constexpr NWidgetPart _nested_vehicle_list[] = {
1601 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1603 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VL_CAPTION),
1606 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_ORDER_VIEW), SetMinimalSize(61, 14), SetStringTip(STR_GOTO_ORDER_VIEW, STR_GOTO_ORDER_VIEW_TOOLTIP),
1607 EndContainer(),
1608 EndContainer(),
1609 NWidget(WWT_SHADEBOX, COLOUR_GREY),
1610 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1611 NWidget(WWT_STICKYBOX, COLOUR_GREY),
1612 EndContainer(),
1613
1616 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_VL_GROUP_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
1617 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_SORT_ORDER), SetMinimalSize(0, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1618 EndContainer(),
1620 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_GROUP_BY_PULLDOWN), SetMinimalSize(0, 12), SetFill(1, 1), SetToolTip(STR_TOOLTIP_GROUP_ORDER),
1621 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_SORT_BY_PULLDOWN), SetMinimalSize(0, 12), SetFill(1, 1), SetToolTip(STR_TOOLTIP_SORT_CRITERIA),
1622 EndContainer(),
1624 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1627 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_FILTER_BY_CARGO), SetMinimalSize(0, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1628 EndContainer(),
1629 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1630 EndContainer(),
1631 EndContainer(),
1632 EndContainer(),
1633
1637 EndContainer(),
1638
1640 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_VL_HIDE_BUTTONS),
1643 SetToolTip(STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
1644 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetResize(1, 0), SetFill(1, 1), EndContainer(),
1646 SetStringTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
1647 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VL_STOP_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1648 SetSpriteTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
1649 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VL_START_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
1650 SetSpriteTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
1651 EndContainer(),
1652 /* Widget to be shown for other companies hiding the previous 5 widgets. */
1653 NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(),
1654 EndContainer(),
1655 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1656 EndContainer(),
1657};
1658
1659static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, uint order_arrow_width, VehicleOrderID start)
1660{
1661 auto orders = v->Orders();
1662 if (orders.empty()) return;
1663
1664 bool rtl = _current_text_dir == TD_RTL;
1665 int l_offset = rtl ? 0 : order_arrow_width;
1666 int r_offset = rtl ? order_arrow_width : 0;
1667 int i = 0;
1668 VehicleOrderID oid = start;
1669
1670 do {
1671 if (oid == v->cur_real_order_index) DrawString(left, right, y, rtl ? STR_JUST_LEFT_ARROW : STR_JUST_RIGHT_ARROW, TC_BLACK, SA_LEFT, false, FS_SMALL);
1672
1673 if (orders[oid].IsType(OT_GOTO_STATION)) {
1674 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, orders[oid].GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
1675
1677 if (++i == 4) break;
1678 }
1679
1680 oid = v->orders->GetNext(oid);
1681 } while (oid != start);
1682}
1683
1685static void DrawSmallOrderList(const OrderList &orderlist, int left, int right, int y, uint order_arrow_width)
1686{
1687 bool rtl = _current_text_dir == TD_RTL;
1688 int l_offset = rtl ? 0 : order_arrow_width;
1689 int r_offset = rtl ? order_arrow_width : 0;
1690 int i = 0;
1691
1692 for (const Order &order : orderlist.GetOrders()) {
1693 if (order.IsType(OT_GOTO_STATION)) {
1694 DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order.GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
1695
1697 if (++i == 4) break;
1698 }
1699 }
1700}
1701
1709void DrawVehicleImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type, int skip)
1710{
1711 switch (v->type) {
1712 case VEH_TRAIN: DrawTrainImage(Train::From(v), r, selection, image_type, skip); break;
1713 case VEH_ROAD: DrawRoadVehImage(v, r, selection, image_type, skip); break;
1714 case VEH_SHIP: DrawShipImage(v, r, selection, image_type); break;
1715 case VEH_AIRCRAFT: DrawAircraftImage(v, r, selection, image_type); break;
1716 default: NOT_REACHED();
1717 }
1718}
1719
1726uint GetVehicleListHeight(VehicleType type, uint divisor)
1727{
1728 /* Name + vehicle + profit */
1730 /* Drawing of the 4 small orders + profit*/
1731 if (type >= VEH_SHIP) base = std::max(base, 6U * GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.matrix.Vertical());
1732
1733 if (divisor == 1) return base;
1734
1735 /* Make sure the height is dividable by divisor */
1736 uint rem = base % divisor;
1737 return base + (rem == 0 ? 0 : divisor - rem);
1738}
1739
1745static int GetUnitNumberWidth(int digits)
1746{
1747 return GetStringBoundingBox(GetString(STR_JUST_COMMA, GetParamMaxDigits(digits))).width;
1748}
1749
1756void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
1757{
1758 Rect ir = r.WithHeight(line_height).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
1759 bool rtl = _current_text_dir == TD_RTL;
1760
1761 Dimension profit = GetSpriteSize(SPR_PROFIT_LOT);
1762 int text_offset = std::max<int>(profit.width, GetUnitNumberWidth(this->unitnumber_digits)) + WidgetDimensions::scaled.hsep_normal;
1763 Rect tr = ir.Indent(text_offset, rtl);
1764
1765 bool show_orderlist = this->vli.vtype >= VEH_SHIP;
1766 Rect olr = ir.Indent(std::max(ScaleGUITrad(100) + text_offset, ir.Width() / 2), rtl);
1767
1768 int image_left = (rtl && show_orderlist) ? olr.right : tr.left;
1769 int image_right = (!rtl && show_orderlist) ? olr.left : tr.right;
1770
1771 int vehicle_button_x = rtl ? ir.right - profit.width : ir.left;
1772
1773 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->vehgroups);
1774 for (auto it = first; it != last; ++it) {
1775 const GUIVehicleGroup &vehgroup = *it;
1776
1777 DrawString(tr.left, tr.right, ir.bottom - GetCharacterHeight(FS_SMALL) - WidgetDimensions::scaled.framerect.bottom,
1778 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_LIST_PROFIT_THIS_PERIOD_LAST_PERIOD : STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR,
1779 vehgroup.GetDisplayProfitThisYear(),
1780 vehgroup.GetDisplayProfitLastYear()));
1781
1782 DrawVehicleProfitButton(vehgroup.GetOldestVehicleAge(), vehgroup.GetDisplayProfitLastYear(), vehgroup.NumVehicles(), vehicle_button_x, ir.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal);
1783
1784 switch (this->grouping) {
1785 case GB_NONE: {
1786 const Vehicle *v = vehgroup.GetSingleVehicle();
1787
1789 DrawSprite(SPR_WARNING_SIGN, PAL_NONE, vehicle_button_x, ir.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal + profit.height);
1790 }
1791
1792 DrawVehicleImage(v, {image_left, ir.top, image_right, ir.bottom}, selected_vehicle, EIT_IN_LIST, 0);
1793
1795 /* Get the cargoes the vehicle can carry */
1796 CargoTypes vehicle_cargoes = 0;
1797
1798 for (auto u = v; u != nullptr; u = u->Next()) {
1799 if (u->cargo_cap == 0) continue;
1800
1801 SetBit(vehicle_cargoes, u->cargo_type);
1802 }
1803
1804 if (!v->name.empty()) {
1805 /* The vehicle got a name so we will print it and the cargoes */
1806 DrawString(tr.left, tr.right, ir.top,
1807 GetString(STR_VEHICLE_LIST_NAME_AND_CARGO, STR_VEHICLE_NAME, v->index, STR_VEHICLE_LIST_CARGO, vehicle_cargoes),
1808 TC_BLACK, SA_LEFT, false, FS_SMALL);
1809 } else if (v->group_id != DEFAULT_GROUP) {
1810 /* The vehicle has no name, but is member of a group, so print group name and the cargoes */
1811 DrawString(tr.left, tr.right, ir.top,
1812 GetString(STR_VEHICLE_LIST_NAME_AND_CARGO, STR_GROUP_NAME, v->group_id, STR_VEHICLE_LIST_CARGO, vehicle_cargoes),
1813 TC_BLACK, SA_LEFT, false, FS_SMALL);
1814 } else {
1815 /* The vehicle has no name, and is not a member of a group, so just print the cargoes */
1816 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_LIST_CARGO, vehicle_cargoes), TC_BLACK, SA_LEFT, false, FS_SMALL);
1817 }
1818 } else if (!v->name.empty()) {
1819 /* The vehicle got a name so we will print it */
1820 DrawString(tr.left, tr.right, ir.top, GetString(STR_VEHICLE_NAME, v->index), TC_BLACK, SA_LEFT, false, FS_SMALL);
1821 } else if (v->group_id != DEFAULT_GROUP) {
1822 /* The vehicle has no name, but is member of a group, so print group name */
1823 DrawString(tr.left, tr.right, ir.top, GetString(STR_GROUP_NAME, v->group_id), TC_BLACK, SA_LEFT, false, FS_SMALL);
1824 }
1825
1826 if (show_orderlist) DrawSmallOrderList(v, olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width, v->cur_real_order_index);
1827
1828 TextColour tc;
1829 if (v->IsChainInDepot()) {
1830 tc = TC_BLUE;
1831 } else {
1832 tc = (v->age > v->max_age - CalendarTime::DAYS_IN_LEAP_YEAR) ? TC_RED : TC_BLACK;
1833 }
1834
1835 DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, v->unitnumber), tc);
1836 break;
1837 }
1838
1839 case GB_SHARED_ORDERS:
1840 assert(vehgroup.NumVehicles() > 0);
1841
1842 for (int i = 0; i < static_cast<int>(vehgroup.NumVehicles()); ++i) {
1843 if (image_left + WidgetDimensions::scaled.hsep_wide * i >= image_right) break; // Break if there is no more space to draw any more vehicles anyway.
1844 DrawVehicleImage(vehgroup.vehicles_begin[i], {image_left + WidgetDimensions::scaled.hsep_wide * i, ir.top, image_right, ir.bottom}, selected_vehicle, EIT_IN_LIST, 0);
1845 }
1846
1847 if (show_orderlist) DrawSmallOrderList(*(vehgroup.vehicles_begin[0])->orders, olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width);
1848
1849 DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, vehgroup.NumVehicles()), TC_BLACK);
1850 break;
1851
1852 default:
1853 NOT_REACHED();
1854 }
1855
1856 ir = ir.Translate(0, line_height);
1857 }
1858}
1859
1860void BaseVehicleListWindow::UpdateSortingFromGrouping()
1861{
1862 /* Set up sorting. Make the window-specific _sorting variable
1863 * point to the correct global _sorting struct so we are freed
1864 * from having conditionals during window operation */
1865 switch (this->vli.vtype) {
1866 case VEH_TRAIN: this->sorting = &_sorting[this->grouping].train; break;
1867 case VEH_ROAD: this->sorting = &_sorting[this->grouping].roadveh; break;
1868 case VEH_SHIP: this->sorting = &_sorting[this->grouping].ship; break;
1869 case VEH_AIRCRAFT: this->sorting = &_sorting[this->grouping].aircraft; break;
1870 default: NOT_REACHED();
1871 }
1872 this->vehgroups.SetSortFuncs(this->GetVehicleSorterFuncs());
1873 this->vehgroups.SetListing(*this->sorting);
1874 this->vehgroups.ForceRebuild();
1875 this->vehgroups.NeedResort();
1876}
1877
1878void BaseVehicleListWindow::UpdateVehicleGroupBy(GroupBy group_by)
1879{
1880 if (this->grouping != group_by) {
1881 /* Save the old sorting option, so that if we change the grouping option back later on,
1882 * UpdateSortingFromGrouping() will automatically restore the saved sorting option. */
1883 *this->sorting = this->vehgroups.GetListing();
1884
1885 this->grouping = group_by;
1886 _grouping[this->vli.type][this->vli.vtype] = group_by;
1887 this->UpdateSortingFromGrouping();
1888 }
1889}
1890
1896private:
1902
1908
1909public:
1911 {
1912 this->CreateNestedTree();
1913
1914 this->GetWidget<NWidgetStacked>(WID_VL_FILTER_BY_CARGO_SEL)->SetDisplayedPlane((this->vli.type == VL_SHARED_ORDERS) ? SZSP_NONE : 0);
1915
1916 this->vscroll = this->GetScrollbar(WID_VL_SCROLLBAR);
1917
1918 /* Set up the window widgets */
1919 this->GetWidget<NWidgetCore>(WID_VL_LIST)->SetToolTip(STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + this->vli.vtype);
1920
1921 NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(WID_VL_CAPTION_SELECTION);
1922 if (this->vli.type == VL_SHARED_ORDERS) {
1923 this->GetWidget<NWidgetCore>(WID_VL_CAPTION_SHARED_ORDERS)->SetString(STR_VEHICLE_LIST_SHARED_ORDERS_LIST_CAPTION);
1924 /* If we are in the shared orders window, then disable the group-by dropdown menu.
1925 * Remove this when the group-by dropdown menu has another option apart from grouping by shared orders. */
1929 } else {
1930 this->GetWidget<NWidgetCore>(WID_VL_CAPTION)->SetString(STR_VEHICLE_LIST_TRAIN_CAPTION + this->vli.vtype);
1932 }
1933
1934 this->FinishInitNested(window_number);
1935 if (this->vli.company != OWNER_NONE) this->owner = this->vli.company;
1936
1937 this->BuildVehicleList();
1938 this->SortVehicleList();
1939 }
1940
1942 {
1943 *this->sorting = this->vehgroups.GetListing();
1944 }
1945
1946 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1947 {
1948 switch (widget) {
1949 case WID_VL_LIST:
1950 resize.height = GetVehicleListHeight(this->vli.vtype, 1);
1951
1952 switch (this->vli.vtype) {
1953 case VEH_TRAIN:
1954 case VEH_ROAD:
1955 size.height = 6 * resize.height;
1956 break;
1957 case VEH_SHIP:
1958 case VEH_AIRCRAFT:
1959 size.height = 4 * resize.height;
1960 break;
1961 default: NOT_REACHED();
1962 }
1963 break;
1964
1965 case WID_VL_SORT_ORDER: {
1966 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->GetString());
1967 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1968 d.height += padding.height;
1969 size = maxdim(size, d);
1970 break;
1971 }
1972
1974 size.width = GetStringListWidth(this->vehicle_group_by_names) + padding.width;
1975 break;
1976
1978 size.width = GetStringListWidth(this->vehicle_group_none_sorter_names_calendar);
1979 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_none_sorter_names_wallclock));
1980 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_calendar));
1981 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_wallclock));
1982 size.width += padding.width;
1983 break;
1984
1986 size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList(true)).width + padding.width);
1987 break;
1988
1990 Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false, true);
1991 d.height += padding.height;
1992 d.width += padding.width;
1993 size = maxdim(size, d);
1994 break;
1995 }
1996 }
1997 }
1998
1999 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2000 {
2001 switch (widget) {
2003 return GetString(STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vli.vtype);
2004
2006 return GetString(std::data(this->vehicle_group_by_names)[this->grouping]);
2007
2009 return GetString(this->GetVehicleSorterNames()[this->vehgroups.SortType()]);
2010
2012 return GetString(this->GetCargoFilterLabel(this->cargo_filter_criteria));
2013
2014 case WID_VL_CAPTION:
2016 switch (this->vli.type) {
2017 case VL_SHARED_ORDERS: // Shared Orders
2018 return GetString(stringid, this->vehicles.size());
2019
2020 case VL_STANDARD: // Company Name
2021 return GetString(stringid, STR_COMPANY_NAME, this->vli.ToCompanyID(), std::monostate{}, this->vehicles.size());
2022
2023 case VL_STATION_LIST: // Station/Waypoint Name
2024 return GetString(stringid, Station::IsExpected(BaseStation::Get(this->vli.ToStationID())) ? STR_STATION_NAME : STR_WAYPOINT_NAME, this->vli.ToStationID(), std::monostate{}, this->vehicles.size());
2025
2026 case VL_DEPOT_LIST:
2027 return GetString(stringid, STR_DEPOT_CAPTION, this->vli.vtype, this->vli.ToDestinationID(), this->vehicles.size());
2028
2029 default: NOT_REACHED();
2030 }
2031 }
2032
2033 default:
2034 return this->Window::GetWidgetString(widget, stringid);
2035 }
2036 }
2037
2038 void DrawWidget(const Rect &r, WidgetID widget) const override
2039 {
2040 switch (widget) {
2041 case WID_VL_SORT_ORDER:
2042 /* draw arrow pointing up/down for ascending/descending sorting */
2043 this->DrawSortButtonState(widget, this->vehgroups.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
2044 break;
2045
2046 case WID_VL_LIST:
2047 this->DrawVehicleListItems(VehicleID::Invalid(), this->resize.step_height, r);
2048 break;
2049 }
2050 }
2051
2052 void OnPaint() override
2053 {
2054 this->BuildVehicleList();
2055 this->SortVehicleList();
2056
2057 if (this->vehicles.empty() && this->IsWidgetLowered(WID_VL_MANAGE_VEHICLES_DROPDOWN)) {
2059 }
2060
2061 /* Hide the widgets that we will not use in this window
2062 * Some windows contains actions only fit for the owner */
2063 int plane_to_show = (this->owner == _local_company) ? BP_SHOW_BUTTONS : BP_HIDE_BUTTONS;
2064 NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(WID_VL_HIDE_BUTTONS);
2065 if (plane_to_show != nwi->shown_plane) {
2066 nwi->SetDisplayedPlane(plane_to_show);
2067 nwi->SetDirty(this);
2068 }
2069 if (this->owner == _local_company) {
2071 this->SetWidgetsDisabledState(this->vehicles.empty(),
2075 }
2076
2077 this->DrawWidgets();
2078 }
2079
2080 bool last_overlay_state = false;
2081 void OnMouseLoop() override
2082 {
2083 if (last_overlay_state != ShowCargoIconOverlay()) {
2084 last_overlay_state = ShowCargoIconOverlay();
2085 this->SetDirty();
2086 }
2087 }
2088
2089 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2090 {
2091 switch (widget) {
2092 case WID_VL_ORDER_VIEW: // Open the shared orders window
2093 assert(this->vli.type == VL_SHARED_ORDERS);
2094 assert(!this->vehicles.empty());
2095 ShowOrdersWindow(this->vehicles[0]);
2096 break;
2097
2098 case WID_VL_SORT_ORDER: // Flip sorting method ascending/descending
2099 this->vehgroups.ToggleSortOrder();
2100 this->SetDirty();
2101 break;
2102
2103 case WID_VL_GROUP_BY_PULLDOWN: // Select sorting criteria dropdown menu
2104 ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_VL_GROUP_BY_PULLDOWN, 0, 0);
2105 return;
2106
2107 case WID_VL_SORT_BY_PULLDOWN: // Select sorting criteria dropdown menu
2108 ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0,
2109 (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
2110 return;
2111
2112 case WID_VL_FILTER_BY_CARGO: // Cargo filter dropdown
2113 ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget);
2114 break;
2115
2116 case WID_VL_LIST: { // Matrix to show vehicles
2117 auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_VL_LIST);
2118 if (it == this->vehgroups.end()) return; // click out of list bound
2119
2120 const GUIVehicleGroup &vehgroup = *it;
2121 switch (this->grouping) {
2122 case GB_NONE: {
2123 const Vehicle *v = vehgroup.GetSingleVehicle();
2124 if (!VehicleClicked(v)) {
2125 if (_ctrl_pressed) {
2127 } else {
2129 }
2130 }
2131 break;
2132 }
2133
2134 case GB_SHARED_ORDERS: {
2135 assert(vehgroup.NumVehicles() > 0);
2136 if (!VehicleClicked(vehgroup)) {
2137 const Vehicle *v = vehgroup.vehicles_begin[0];
2138 if (_ctrl_pressed) {
2139 ShowOrdersWindow(v);
2140 } else {
2141 if (vehgroup.NumVehicles() == 1) {
2143 } else {
2144 ShowVehicleListWindow(v);
2145 }
2146 }
2147 }
2148 break;
2149 }
2150
2151 default: NOT_REACHED();
2152 }
2153
2154 break;
2155 }
2156
2158 ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
2159 break;
2160
2163 break;
2164 }
2165
2166 case WID_VL_STOP_ALL:
2167 case WID_VL_START_ALL:
2169 break;
2170 }
2171 }
2172
2173 void OnDropdownSelect(WidgetID widget, int index, int) override
2174 {
2175 switch (widget) {
2177 this->UpdateVehicleGroupBy(static_cast<GroupBy>(index));
2178 break;
2179
2181 this->vehgroups.SetSortType(index);
2182 break;
2183
2185 this->SetCargoFilter(index);
2186 break;
2187
2189 assert(!this->vehicles.empty());
2190
2191 switch (index) {
2192 case ADI_REPLACE: // Replace window
2194 break;
2195 case ADI_SERVICE: // Send for servicing
2196 case ADI_DEPOT: // Send to Depots
2197 Command<CMD_SEND_VEHICLE_TO_DEPOT>::Post(GetCmdSendToDepotMsg(this->vli.vtype), VehicleID::Invalid(), (index == ADI_SERVICE ? DepotCommandFlag::Service : DepotCommandFlags{}) | DepotCommandFlag::MassSend, this->vli);
2198 break;
2199
2200 case ADI_CREATE_GROUP: // Create group
2201 Command<CMD_ADD_VEHICLE_GROUP>::Post(CcAddVehicleNewGroup, NEW_GROUP, VehicleID::Invalid(), false, this->vli);
2202 break;
2203
2204 default: NOT_REACHED();
2205 }
2206 break;
2207
2208 default: NOT_REACHED();
2209 }
2210 this->SetDirty();
2211 }
2212
2213 void OnGameTick() override
2214 {
2215 if (this->vehgroups.NeedResort()) {
2216 StationID station = (this->vli.type == VL_STATION_LIST) ? this->vli.ToStationID() : StationID::Invalid();
2217
2218 Debug(misc, 3, "Periodic resort {} list company {} at station {}", this->vli.vtype, this->owner, station);
2219 this->SetDirty();
2220 }
2221 }
2222
2223 void OnResize() override
2224 {
2225 this->vscroll->SetCapacityFromWidget(this, WID_VL_LIST);
2226 }
2227
2233 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2234 {
2235 if (!gui_scope && HasBit(data, 31) && this->vli.type == VL_SHARED_ORDERS) {
2236 /* Needs to be done in command-scope, so everything stays valid */
2237 this->vli.SetIndex(GB(data, 0, 20));
2238 this->window_number = this->vli.ToWindowNumber();
2239 this->vehgroups.ForceRebuild();
2240 return;
2241 }
2242
2243 if (data == 0) {
2244 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
2245 this->vehgroups.ForceRebuild();
2246 } else {
2247 this->vehgroups.ForceResort();
2248 }
2249 }
2250};
2251
2252static WindowDesc _vehicle_list_desc[] = {
2253 {
2254 WDP_AUTO, "list_vehicles_train", 325, 246,
2256 {},
2257 _nested_vehicle_list
2258 },
2259 {
2260 WDP_AUTO, "list_vehicles_roadveh", 260, 246,
2262 {},
2263 _nested_vehicle_list
2264 },
2265 {
2266 WDP_AUTO, "list_vehicles_ship", 260, 246,
2268 {},
2269 _nested_vehicle_list
2270 },
2271 {
2272 WDP_AUTO, "list_vehicles_aircraft", 260, 246,
2274 {},
2275 _nested_vehicle_list
2276 }
2277};
2278
2279static void ShowVehicleListWindowLocal(CompanyID company, VehicleListType vlt, VehicleType vehicle_type, uint32_t unique_number)
2280{
2281 if (!Company::IsValidID(company) && company != OWNER_NONE) return;
2282
2283 assert(vehicle_type < std::size(_vehicle_list_desc));
2284 VehicleListIdentifier vli(vlt, vehicle_type, company, unique_number);
2285 AllocateWindowDescFront<VehicleListWindow>(_vehicle_list_desc[vehicle_type], vli.ToWindowNumber(), vli);
2286}
2287
2288void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type)
2289{
2290 /* If _settings_client.gui.advanced_vehicle_list > 1, display the Advanced list
2291 * if _settings_client.gui.advanced_vehicle_list == 1, display Advanced list only for local company
2292 * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
2293 */
2294
2296 ShowCompanyGroup(company, vehicle_type);
2297 } else {
2298 ShowVehicleListWindowLocal(company, VL_STANDARD, vehicle_type, company.base());
2299 }
2300}
2301
2302void ShowVehicleListWindow(const Vehicle *v)
2303{
2304 ShowVehicleListWindowLocal(v->owner, VL_SHARED_ORDERS, v->type, v->FirstShared()->index.base());
2305}
2306
2307void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station)
2308{
2309 ShowVehicleListWindowLocal(company, VL_STATION_LIST, vehicle_type, station.base());
2310}
2311
2312void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
2313{
2314 ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, GetDepotDestinationIndex(depot_tile).base());
2315}
2316
2317
2318/* Unified vehicle GUI - Vehicle Details Window */
2319
2324
2328 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2329 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VD_CAPTION),
2330 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2331 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2332 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2333 EndContainer(),
2334 NWidget(WWT_PANEL, COLOUR_GREY, WID_VD_TOP_DETAILS), SetMinimalSize(405, 42), SetResize(1, 0), EndContainer(),
2342 SetStringTip(STR_EMPTY, STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP),
2344 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2345 EndContainer(),
2346};
2347
2351 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2352 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VD_CAPTION), SetStringTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2353 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2354 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2355 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2356 EndContainer(),
2357 NWidget(WWT_PANEL, COLOUR_GREY, WID_VD_TOP_DETAILS), SetResize(1, 0), SetMinimalSize(405, 42), EndContainer(),
2361 EndContainer(),
2368 SetStringTip(STR_EMPTY, STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP),
2370 EndContainer(),
2373 SetStringTip(STR_VEHICLE_DETAIL_TAB_CARGO, STR_VEHICLE_DETAILS_TRAIN_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2375 SetStringTip(STR_VEHICLE_DETAIL_TAB_INFORMATION, STR_VEHICLE_DETAILS_TRAIN_INFORMATION_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2377 SetStringTip(STR_VEHICLE_DETAIL_TAB_CAPACITIES, STR_VEHICLE_DETAILS_TRAIN_CAPACITIES_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2379 SetStringTip(STR_VEHICLE_DETAIL_TAB_TOTAL_CARGO, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
2380 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2381 EndContainer(),
2382};
2383
2384
2386extern void DrawTrainDetails(const Train *v, const Rect &r, int vscroll_pos, uint16_t vscroll_cap, TrainDetailsWindowTabs det_tab);
2387extern void DrawRoadVehDetails(const Vehicle *v, const Rect &r);
2388extern void DrawShipDetails(const Vehicle *v, const Rect &r);
2389extern void DrawAircraftDetails(const Aircraft *v, const Rect &r);
2390
2391static const StringID _service_interval_dropdown_calendar[] = {
2392 STR_VEHICLE_DETAILS_DEFAULT,
2393 STR_VEHICLE_DETAILS_DAYS,
2394 STR_VEHICLE_DETAILS_PERCENT,
2395};
2396
2397static const StringID _service_interval_dropdown_wallclock[] = {
2398 STR_VEHICLE_DETAILS_DEFAULT,
2399 STR_VEHICLE_DETAILS_MINUTES,
2400 STR_VEHICLE_DETAILS_PERCENT,
2401};
2402
2406 Scrollbar *vscroll = nullptr;
2407
2410 {
2412
2413 this->CreateNestedTree();
2414 this->vscroll = (v->type == VEH_TRAIN ? this->GetScrollbar(WID_VD_SCROLLBAR) : nullptr);
2415 this->FinishInitNested(window_number);
2416
2417 this->owner = v->owner;
2418 }
2419
2425 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2426 {
2427 if (data == VIWD_AUTOREPLACE) {
2428 /* Autoreplace replaced the vehicle.
2429 * Nothing to do for this window. */
2430 return;
2431 }
2432 if (!gui_scope) return;
2433 const Vehicle *v = Vehicle::Get(this->window_number);
2434 if (v->type == VEH_ROAD) {
2435 const NWidgetBase *nwid_info = this->GetWidget<NWidgetBase>(WID_VD_MIDDLE_DETAILS);
2436 uint aimed_height = this->GetRoadVehDetailsHeight(v);
2437 /* If the number of articulated parts changes, the size of the window must change too. */
2438 if (aimed_height != nwid_info->current_y) {
2439 this->ReInit();
2440 }
2441 }
2442 }
2443
2450 {
2451 uint desired_height;
2452 if (v->HasArticulatedPart()) {
2453 /* An articulated RV has its text drawn under the sprite instead of after it, hence 15 pixels extra. */
2455 /* Add space for the cargo amount for each part. */
2456 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
2457 if (u->cargo_cap != 0) desired_height += GetCharacterHeight(FS_NORMAL);
2458 }
2459 } else {
2461 }
2462 return desired_height;
2463 }
2464
2465 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2466 {
2467 switch (widget) {
2468 case WID_VD_TOP_DETAILS: {
2469 Dimension dim = { 0, 0 };
2470 size.height = 4 * GetCharacterHeight(FS_NORMAL) + padding.height;
2471
2472 uint64_t max_value = GetParamMaxValue(INT16_MAX);
2473 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_MAX_SPEED, max_value)));
2474 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, max_value, max_value, max_value)));
2475 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE, max_value, max_value, max_value, max_value)));
2476 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE, max_value, max_value, max_value)));
2477 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD_MIN_PERFORMANCE, max_value, max_value, max_value)));
2478 dim = maxdim(dim, GetStringBoundingBox(GetString(STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS, max_value, max_value)));
2479 dim = maxdim(dim, GetStringBoundingBox(GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_AGE_RUNNING_COST_PERIOD : STR_VEHICLE_INFO_AGE_RUNNING_COST_YR, STR_VEHICLE_INFO_AGE, max_value, max_value, max_value)));
2480 size.width = dim.width + padding.width;
2481 break;
2482 }
2483
2484 case WID_VD_MIDDLE_DETAILS: {
2485 const Vehicle *v = Vehicle::Get(this->window_number);
2486 switch (v->type) {
2487 case VEH_ROAD:
2488 size.height = this->GetRoadVehDetailsHeight(v) + padding.height;
2489 break;
2490
2491 case VEH_SHIP:
2492 size.height = 4 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2493 break;
2494
2495 case VEH_AIRCRAFT:
2496 size.height = 5 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
2497 break;
2498
2499 default:
2500 NOT_REACHED(); // Train uses WID_VD_MATRIX instead.
2501 }
2502 break;
2503 }
2504
2505 case WID_VD_MATRIX:
2506 resize.height = std::max<uint>(ScaleGUITrad(14), GetCharacterHeight(FS_NORMAL) + padding.height);
2507 size.height = 4 * resize.height;
2508 break;
2509
2511 Dimension d = maxdim(GetStringListBoundingBox(_service_interval_dropdown_calendar), GetStringListBoundingBox(_service_interval_dropdown_wallclock));
2512 d.width += padding.width;
2513 d.height += padding.height;
2514 size = maxdim(size, d);
2515 break;
2516 }
2517
2519 /* Do we show the last serviced value as a date or minutes since service? */
2521 ? MakeParameters(GetParamMaxValue(MAX_SERVINT_DAYS), STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO, EconomyTime::MAX_DATE)
2522 : MakeParameters(GetParamMaxValue(MAX_SERVINT_DAYS), STR_VEHICLE_DETAILS_LAST_SERVICE_DATE, TimerGameEconomy::DateAtStartOfYear(EconomyTime::MAX_YEAR));
2523
2524 size.width = std::max(size.width, GetStringBoundingBox(GetStringWithArgs(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT, params)).width);
2525 PrepareArgsForNextRun(params);
2526 size.width = std::max(size.width, GetStringBoundingBox(GetStringWithArgs(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS, params)).width);
2527
2528 size.width += padding.width;
2529 size.height = GetCharacterHeight(FS_NORMAL) + padding.height;
2530 break;
2531 }
2532 }
2533
2535 static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type, CompanyID company_id)
2536 {
2537 if (_local_company != company_id) return false;
2538
2539 const VehicleDefaultSettings *vds = &Company::Get(company_id)->settings.vehicle;
2540 switch (vehicle_type) {
2541 default: NOT_REACHED();
2542 case VEH_TRAIN: return vds->servint_trains != 0;
2543 case VEH_ROAD: return vds->servint_roadveh != 0;
2544 case VEH_SHIP: return vds->servint_ships != 0;
2545 case VEH_AIRCRAFT: return vds->servint_aircraft != 0;
2546 }
2547 }
2548
2558 static void DrawVehicleDetails(const Vehicle *v, const Rect &r, int vscroll_pos, uint vscroll_cap, TrainDetailsWindowTabs det_tab)
2559 {
2560 switch (v->type) {
2561 case VEH_TRAIN: DrawTrainDetails(Train::From(v), r, vscroll_pos, vscroll_cap, det_tab); break;
2562 case VEH_ROAD: DrawRoadVehDetails(v, r); break;
2563 case VEH_SHIP: DrawShipDetails(v, r); break;
2565 default: NOT_REACHED();
2566 }
2567 }
2568
2569 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2570 {
2571 if (widget == WID_VD_CAPTION) return GetString(STR_VEHICLE_DETAILS_CAPTION, Vehicle::Get(this->window_number)->index);
2572
2573 return this->Window::GetWidgetString(widget, stringid);
2574 }
2575
2576 void DrawWidget(const Rect &r, WidgetID widget) const override
2577 {
2578 const Vehicle *v = Vehicle::Get(this->window_number);
2579
2580 switch (widget) {
2581 case WID_VD_TOP_DETAILS: {
2582 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2583
2584 /* Draw running cost */
2585 DrawString(tr,
2586 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_AGE_RUNNING_COST_PERIOD : STR_VEHICLE_INFO_AGE_RUNNING_COST_YR,
2587 (v->age + CalendarTime::DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_INFO_AGE : STR_VEHICLE_INFO_AGE_RED,
2590 v->GetDisplayRunningCost()));
2591 tr.top += GetCharacterHeight(FS_NORMAL);
2592
2593 /* Draw max speed */
2594 uint64_t max_speed = PackVelocity(v->GetDisplayMaxSpeed(), v->type);
2595 if (v->type == VEH_TRAIN ||
2597 const GroundVehicleCache *gcache = v->GetGroundVehicleCache();
2598 if (v->type == VEH_TRAIN && (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ||
2599 GetRailTypeInfo(Train::From(v)->railtype)->acceleration_type == 2)) {
2600 DrawString(tr, GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, gcache->cached_weight, gcache->cached_power, max_speed));
2601 } else {
2602 DrawString(tr, GetString(STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE, gcache->cached_weight, gcache->cached_power, max_speed, gcache->cached_max_te));
2603 }
2604 } else if (v->type == VEH_AIRCRAFT) {
2605 StringID type = v->GetEngine()->GetAircraftTypeText();
2606 if (Aircraft::From(v)->GetRange() > 0) {
2607 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE, max_speed, type, Aircraft::From(v)->GetRange()));
2608 } else {
2609 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED_TYPE, max_speed, type));
2610 }
2611 } else {
2612 DrawString(tr, GetString(STR_VEHICLE_INFO_MAX_SPEED, max_speed));
2613 }
2614 tr.top += GetCharacterHeight(FS_NORMAL);
2615
2616 /* Draw profit */
2617 if (v->IsGroundVehicle()) {
2618 DrawString(tr,
2619 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD_MIN_PERFORMANCE : STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE,
2623 } else {
2624 DrawString(tr,
2625 GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_INFO_PROFIT_THIS_PERIOD_LAST_PERIOD : STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR,
2628 }
2629 tr.top += GetCharacterHeight(FS_NORMAL);
2630
2631 /* Draw breakdown & reliability */
2632 DrawString(tr, GetString(STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS, ToPercent16(v->reliability), v->breakdowns_since_last_service));
2633 break;
2634 }
2635
2636 case WID_VD_MATRIX: {
2637 /* For trains only. */
2638 DrawVehicleDetails(v, r.Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero).WithHeight(this->resize.step_height), this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->tab);
2639 break;
2640 }
2641
2642 case WID_VD_MIDDLE_DETAILS: {
2643 /* For other vehicles, at the place of the matrix. */
2644 bool rtl = _current_text_dir == TD_RTL;
2646 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2647
2648 /* Articulated road vehicles use a complete line. */
2649 if (v->type == VEH_ROAD && v->HasArticulatedPart()) {
2650 DrawVehicleImage(v, tr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2651 } else {
2652 Rect sr = tr.WithWidth(sprite_width, rtl);
2653 DrawVehicleImage(v, sr.WithHeight(ScaleGUITrad(GetVehicleHeight(v->type)), false), VehicleID::Invalid(), EIT_IN_DETAILS, 0);
2654 }
2655
2656 DrawVehicleDetails(v, tr.Indent(sprite_width, rtl), 0, 0, this->tab);
2657 break;
2658 }
2659
2661 /* Draw service interval text */
2662 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2663
2664 /* We're using wallclock units. Show minutes since last serviced. */
2666 int minutes_since_serviced = (TimerGameEconomy::date - v->date_of_last_service).base() / EconomyTime::DAYS_IN_ECONOMY_MONTH;
2667 DrawString(tr.left, tr.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
2668 GetString(v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_MINUTES,
2669 v->GetServiceInterval(), STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO, minutes_since_serviced));
2670 break;
2671 }
2672
2673 /* We're using calendar dates. Show the date of last service. */
2674 DrawString(tr.left, tr.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
2675 GetString(v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS,
2676 v->GetServiceInterval(), STR_VEHICLE_DETAILS_LAST_SERVICE_DATE, v->date_of_last_service));
2677 break;
2678 }
2679 }
2680 }
2681
2683 void OnPaint() override
2684 {
2685 const Vehicle *v = Vehicle::Get(this->window_number);
2686
2687 if (v->type == VEH_TRAIN) {
2688 this->LowerWidget(WID_VD_DETAILS_CARGO_CARRIED + this->tab);
2689 this->vscroll->SetCount(GetTrainDetailsWndVScroll(v->index, this->tab));
2690 }
2691
2692 /* Disable service-scroller when interval is set to disabled */
2696
2697 StringID str =
2698 !v->ServiceIntervalIsCustom() ? STR_VEHICLE_DETAILS_DEFAULT :
2699 v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT :
2700 TimerGameEconomy::UsingWallclockUnits() ? STR_VEHICLE_DETAILS_MINUTES : STR_VEHICLE_DETAILS_DAYS;
2701 this->GetWidget<NWidgetCore>(WID_VD_SERVICE_INTERVAL_DROPDOWN)->SetString(str);
2703
2704 this->DrawWidgets();
2705 }
2706
2707 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2708 {
2709 switch (widget) {
2710 case WID_VD_INCREASE_SERVICING_INTERVAL: // increase int
2711 case WID_VD_DECREASE_SERVICING_INTERVAL: { // decrease int
2712 const Vehicle *v = Vehicle::Get(this->window_number);
2713 int mod;
2714 if (!v->ServiceIntervalIsPercent() && TimerGameEconomy::UsingWallclockUnits()) {
2715 mod = _ctrl_pressed ? 1 : 5;
2716 } else {
2717 mod = _ctrl_pressed ? 5 : 10;
2718 }
2719
2720 mod = (widget == WID_VD_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
2721 mod = GetServiceIntervalClamped(mod + v->GetServiceInterval(), v->ServiceIntervalIsPercent());
2722 if (mod == v->GetServiceInterval()) return;
2723
2724 Command<CMD_CHANGE_SERVICE_INT>::Post(STR_ERROR_CAN_T_CHANGE_SERVICING, v->index, mod, true, v->ServiceIntervalIsPercent());
2725 break;
2726 }
2727
2729 const Vehicle *v = Vehicle::Get(this->window_number);
2730 ShowDropDownMenu(this,
2731 TimerGameEconomy::UsingWallclockUnits() ? _service_interval_dropdown_wallclock : _service_interval_dropdown_calendar,
2732 v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0);
2733 break;
2734 }
2735
2740 this->SetWidgetsLoweredState(false,
2745
2747 this->SetDirty();
2748 break;
2749 }
2750 }
2751
2752 bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
2753 {
2755 const Vehicle *v = Vehicle::Get(this->window_number);
2756 StringID tool_tip;
2757 if (v->ServiceIntervalIsPercent()) {
2758 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_PERCENT : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_PERCENT;
2760 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_MINUTES : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_MINUTES;
2761 } else {
2762 tool_tip = widget == WID_VD_INCREASE_SERVICING_INTERVAL ? STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP_DAYS : STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP_DAYS;
2763 }
2764 GuiShowTooltips(this, GetEncodedString(tool_tip), close_cond);
2765 return true;
2766 }
2767
2768 return false;
2769 }
2770
2771 void OnDropdownSelect(WidgetID widget, int index, int) override
2772 {
2773 switch (widget) {
2775 const Vehicle *v = Vehicle::Get(this->window_number);
2776 bool iscustom = index != 0;
2777 bool ispercent = iscustom ? (index == 2) : Company::Get(v->owner)->settings.vehicle.servint_ispercent;
2778 uint16_t interval = GetServiceIntervalClamped(v->GetServiceInterval(), ispercent);
2779 Command<CMD_CHANGE_SERVICE_INT>::Post(STR_ERROR_CAN_T_CHANGE_SERVICING, v->index, interval, iscustom, ispercent);
2780 break;
2781 }
2782 }
2783 }
2784
2785 void OnResize() override
2786 {
2787 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_VD_MATRIX);
2788 if (nwi != nullptr) {
2789 this->vscroll->SetCapacityFromWidget(this, WID_VD_MATRIX);
2790 }
2791 }
2792};
2793
2796 WDP_AUTO, "view_vehicle_details_train", 405, 178,
2798 {},
2800);
2801
2804 WDP_AUTO, "view_vehicle_details", 405, 113,
2806 {},
2808);
2809
2812{
2815 AllocateWindowDescFront<VehicleDetailsWindow>((v->type == VEH_TRAIN) ? _train_vehicle_details_desc : _nontrain_vehicle_details_desc, v->index);
2816}
2817
2818
2819/* Unified vehicle GUI - Vehicle View Window */
2820
2824 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2825 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME),
2826 NWidget(WWT_CAPTION, COLOUR_GREY, WID_VV_CAPTION),
2827 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION),
2828 NWidget(WWT_DEBUGBOX, COLOUR_GREY),
2829 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2830 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2831 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2832 EndContainer(),
2834 NWidget(WWT_PANEL, COLOUR_GREY),
2835 NWidget(WWT_INSET, COLOUR_GREY), SetPadding(2, 2, 2, 2),
2836 NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_VV_VIEWPORT), SetMinimalSize(226, 84), SetResize(1, 1),
2837 EndContainer(),
2838 EndContainer(),
2841 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_GOTO_DEPOT), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2842 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_CLONE), SetMinimalSize(18, 18), SetSpriteTip(SPR_EMPTY /* filled later */),
2843 EndContainer(),
2844 /* For trains only, 'ignore signal' button. */
2846 SetSpriteTip(SPR_IGNORE_SIGNALS, STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP),
2848 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_REFIT), SetMinimalSize(18, 18), SetSpriteTip(SPR_REFIT_VEHICLE),
2850 SetSpriteTip(SPR_FORCE_VEHICLE_TURN, STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP),
2851 EndContainer(),
2852 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_SHOW_ORDERS), SetMinimalSize(18, 18), SetSpriteTip(SPR_SHOW_ORDERS),
2853 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_SHOW_DETAILS), SetMinimalSize(18, 18), SetSpriteTip(SPR_SHOW_VEHICLE_DETAILS),
2854 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(18, 0), SetResize(0, 1), EndContainer(),
2855 EndContainer(),
2856 EndContainer(),
2858 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_VV_START_STOP), SetResize(1, 0), SetFill(1, 0),
2859 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_ORDER_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION, STR_VEHICLE_VIEW_ORDER_LOCATION_TOOLTIP),
2860 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2861 EndContainer(),
2862};
2863
2864/* Just to make sure, nobody has changed the vehicle type constants, as we are
2865 using them for array indexing in a number of places here. */
2866static_assert(VEH_TRAIN == 0);
2867static_assert(VEH_ROAD == 1);
2868static_assert(VEH_SHIP == 2);
2869static_assert(VEH_AIRCRAFT == 3);
2870
2878
2879/* Constants for geometry of vehicle view viewport */
2880static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
2881static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
2882static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN = 102;
2883
2886 VCT_CMD_START_STOP = 0,
2887 VCT_CMD_CLONE_VEH,
2888 VCT_CMD_TURN_AROUND,
2889};
2890
2893 { // VCT_CMD_START_STOP
2894 STR_ERROR_CAN_T_STOP_START_TRAIN,
2895 STR_ERROR_CAN_T_STOP_START_ROAD_VEHICLE,
2896 STR_ERROR_CAN_T_STOP_START_SHIP,
2897 STR_ERROR_CAN_T_STOP_START_AIRCRAFT
2898 },
2899 { // VCT_CMD_CLONE_VEH
2900 STR_ERROR_CAN_T_BUY_TRAIN,
2901 STR_ERROR_CAN_T_BUY_ROAD_VEHICLE,
2902 STR_ERROR_CAN_T_BUY_SHIP,
2903 STR_ERROR_CAN_T_BUY_AIRCRAFT
2904 },
2905 { // VCT_CMD_TURN_AROUND
2906 STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN,
2907 STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN,
2908 INVALID_STRING_ID, // invalid for ships
2909 INVALID_STRING_ID // invalid for aircraft
2910 },
2911};
2912
2918void CcStartStopVehicle(Commands, const CommandCost &result, VehicleID veh_id, bool)
2919{
2920 if (result.Failed()) return;
2921
2922 const Vehicle *v = Vehicle::GetIfValid(veh_id);
2923 if (v == nullptr || !v->IsPrimaryVehicle() || v->owner != _local_company) return;
2924
2925 StringID msg = v->vehstatus.Test(VehState::Stopped) ? STR_VEHICLE_COMMAND_STOPPED : STR_VEHICLE_COMMAND_STARTED;
2926 Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos);
2927 AddTextEffect(GetEncodedString(msg), pt.x, pt.y, Ticks::DAY_TICKS, TE_RISING);
2928}
2929
2935void StartStopVehicle(const Vehicle *v, bool texteffect)
2936{
2937 assert(v->IsPrimaryVehicle());
2938 Command<CMD_START_STOP_VEHICLE>::Post(_vehicle_msg_translation_table[VCT_CMD_START_STOP][v->type], texteffect ? CcStartStopVehicle : nullptr, v->tile, v->index, false);
2939}
2940
2942static bool IsVehicleRefitable(const Vehicle *v)
2943{
2944 if (!v->IsStoppedInDepot()) return false;
2945
2946 do {
2947 if (IsEngineRefittable(v->engine_type)) return true;
2948 } while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
2949
2950 return false;
2951}
2952
2955private:
2967 bool mouse_over_start_stop = false;
2968
2974 {
2975 switch (plane) {
2976 case SEL_DC_GOTO_DEPOT:
2977 case SEL_DC_CLONE:
2978 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE)->SetDisplayedPlane(plane - SEL_DC_BASEPLANE);
2979 break;
2980
2981 case SEL_RT_REFIT:
2982 case SEL_RT_TURN_AROUND:
2983 this->GetWidget<NWidgetStacked>(WID_VV_SELECT_REFIT_TURN)->SetDisplayedPlane(plane - SEL_RT_BASEPLANE);
2984 break;
2985
2986 default:
2987 NOT_REACHED();
2988 }
2989 }
2990
2991public:
2993 {
2995 this->CreateNestedTree();
2996
2997 /* Sprites for the 'send to depot' button indexed by vehicle type. */
2998 static const SpriteID vehicle_view_goto_depot_sprites[] = {
2999 SPR_SEND_TRAIN_TODEPOT,
3000 SPR_SEND_ROADVEH_TODEPOT,
3001 SPR_SEND_SHIP_TODEPOT,
3002 SPR_SEND_AIRCRAFT_TODEPOT,
3003 };
3005 this->GetWidget<NWidgetCore>(WID_VV_GOTO_DEPOT)->SetSprite(vehicle_view_goto_depot_sprites[v->type]);
3006
3007 /* Sprites for the 'clone vehicle' button indexed by vehicle type. */
3008 static const SpriteID vehicle_view_clone_sprites[] = {
3010 SPR_CLONE_ROADVEH,
3011 SPR_CLONE_SHIP,
3012 SPR_CLONE_AIRCRAFT,
3013 };
3014 this->GetWidget<NWidgetCore>(WID_VV_CLONE)->SetSprite(vehicle_view_clone_sprites[v->type]);
3015
3016 switch (v->type) {
3017 case VEH_TRAIN:
3018 this->GetWidget<NWidgetCore>(WID_VV_TURN_AROUND)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_REVERSE_TOOLTIP);
3019 break;
3020
3021 case VEH_ROAD:
3022 break;
3023
3024 case VEH_SHIP:
3025 case VEH_AIRCRAFT:
3027 break;
3028
3029 default: NOT_REACHED();
3030 }
3031 this->FinishInitNested(window_number);
3032 this->owner = v->owner;
3033 this->GetWidget<NWidgetViewport>(WID_VV_VIEWPORT)->InitializeViewport(this, static_cast<VehicleID>(this->window_number), ScaleZoomGUI(_vehicle_view_zoom_levels[v->type]));
3034
3035 this->GetWidget<NWidgetCore>(WID_VV_START_STOP)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP + v->type);
3036 this->GetWidget<NWidgetCore>(WID_VV_RENAME)->SetToolTip(STR_VEHICLE_DETAILS_TRAIN_RENAME + v->type);
3037 this->GetWidget<NWidgetCore>(WID_VV_LOCATION)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP + v->type);
3038 this->GetWidget<NWidgetCore>(WID_VV_REFIT)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP + v->type);
3039 this->GetWidget<NWidgetCore>(WID_VV_GOTO_DEPOT)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP + v->type);
3040 this->GetWidget<NWidgetCore>(WID_VV_SHOW_ORDERS)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP + v->type);
3041 this->GetWidget<NWidgetCore>(WID_VV_SHOW_DETAILS)->SetToolTip(STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP + v->type);
3042 this->GetWidget<NWidgetCore>(WID_VV_CLONE)->SetToolTip(STR_VEHICLE_VIEW_CLONE_TRAIN_INFO + v->type);
3043
3044 this->UpdateButtonStatus();
3045 }
3046
3047 void Close([[maybe_unused]] int data = 0) override
3048 {
3049 CloseWindowById(WC_VEHICLE_ORDERS, this->window_number, false);
3050 CloseWindowById(WC_VEHICLE_REFIT, this->window_number, false);
3051 CloseWindowById(WC_VEHICLE_DETAILS, this->window_number, false);
3052 CloseWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false);
3053 this->Window::Close();
3054 }
3055
3056 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
3057 {
3058 const Vehicle *v = Vehicle::Get(this->window_number);
3059 switch (widget) {
3060 case WID_VV_START_STOP:
3061 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;
3062 break;
3063
3065 if (v->type != VEH_TRAIN) {
3066 size.height = 0;
3067 size.width = 0;
3068 }
3069 break;
3070
3071 case WID_VV_VIEWPORT:
3072 size.width = VV_INITIAL_VIEWPORT_WIDTH;
3073 size.height = (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT;
3074 break;
3075 }
3076 }
3077
3078 void OnPaint() override
3079 {
3080 const Vehicle *v = Vehicle::Get(this->window_number);
3081 bool is_localcompany = v->owner == _local_company;
3082 bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
3083
3084 this->SetWidgetDisabledState(WID_VV_RENAME, !is_localcompany);
3085 this->SetWidgetDisabledState(WID_VV_GOTO_DEPOT, !is_localcompany);
3086 this->SetWidgetDisabledState(WID_VV_REFIT, !refitable_and_stopped_in_depot || !is_localcompany);
3087 this->SetWidgetDisabledState(WID_VV_CLONE, !is_localcompany);
3088
3089 if (v->type == VEH_TRAIN) {
3091 this->SetWidgetDisabledState(WID_VV_FORCE_PROCEED, !is_localcompany);
3092 }
3093
3094 if (v->type == VEH_TRAIN || v->type == VEH_ROAD) {
3095 this->SetWidgetDisabledState(WID_VV_TURN_AROUND, !is_localcompany);
3096 }
3097
3099
3100 const Window *mainwindow = GetMainWindow();
3101 if (mainwindow->viewport->follow_vehicle == v->index) {
3103 }
3104
3105 this->DrawWidgets();
3106 }
3107
3108 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
3109 {
3110 if (widget != WID_VV_CAPTION) return this->Window::GetWidgetString(widget, stringid);
3111
3112 const Vehicle *v = Vehicle::Get(this->window_number);
3113 return GetString(STR_VEHICLE_VIEW_CAPTION, v->index);
3114 }
3115
3116 std::string GetVehicleStatusString(const Vehicle *v, TextColour &text_colour) const
3117 {
3118 text_colour = TC_BLACK;
3119
3120 if (v->vehstatus.Test(VehState::Crashed)) return GetString(STR_VEHICLE_STATUS_CRASHED);
3121
3122 if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) return GetString(STR_VEHICLE_STATUS_BROKEN_DOWN);
3123
3124 if (v->vehstatus.Test(VehState::Stopped) && (!mouse_over_start_stop || v->IsStoppedInDepot())) {
3125 if (v->type != VEH_TRAIN) return GetString(STR_VEHICLE_STATUS_STOPPED);
3126 if (v->cur_speed != 0) return GetString(STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL, PackVelocity(v->GetDisplaySpeed(), v->type));
3127 if (Train::From(v)->gcache.cached_power == 0) return GetString(STR_VEHICLE_STATUS_TRAIN_NO_POWER);
3128 return GetString(STR_VEHICLE_STATUS_STOPPED);
3129 }
3130
3131 if (v->IsInDepot() && v->IsWaitingForUnbunching()) return GetString(STR_VEHICLE_STATUS_WAITING_UNBUNCHING);
3132
3133 if (v->type == VEH_TRAIN && Train::From(v)->flags.Test(VehicleRailFlag::Stuck) && !v->current_order.IsType(OT_LOADING)) return GetString(STR_VEHICLE_STATUS_TRAIN_STUCK);
3134
3135 if (v->type == VEH_AIRCRAFT && HasBit(Aircraft::From(v)->flags, VAF_DEST_TOO_FAR) && !v->current_order.IsType(OT_LOADING)) return GetString(STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR);
3136
3137 /* Vehicle is in a "normal" state, show current order. */
3138 if (mouse_over_start_stop) {
3140 text_colour = TC_RED | TC_FORCED;
3141 } else if (v->type == VEH_TRAIN && Train::From(v)->flags.Test(VehicleRailFlag::Stuck) && !v->current_order.IsType(OT_LOADING)) {
3142 text_colour = TC_ORANGE | TC_FORCED;
3143 }
3144 }
3145
3146 switch (v->current_order.GetType()) {
3147 case OT_GOTO_STATION:
3148 return GetString(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_STATION_VEL : STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL,
3149 v->current_order.GetDestination(), PackVelocity(v->GetDisplaySpeed(), v->type));
3150
3151 case OT_GOTO_DEPOT: {
3152 /* This case *only* happens when multiple nearest depot orders
3153 * follow each other (including an order list only one order: a
3154 * nearest depot order) and there are no reachable depots.
3155 * It is primarily to guard for the case that there is no
3156 * depot with index 0, which would be used as fallback for
3157 * evaluating the string in the status bar. */
3158 if (v->current_order.GetDestination() == DepotID::Invalid()) return {};
3159
3162 return GetStringWithArgs(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL, params);
3163 }
3164
3166 return GetStringWithArgs(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_UNBUNCH_VEL, params);
3167 }
3168
3169 return GetStringWithArgs(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL, params);
3170 }
3171
3172 case OT_LOADING:
3173 return GetString(STR_VEHICLE_STATUS_LOADING_UNLOADING);
3174
3175 case OT_GOTO_WAYPOINT:
3176 assert(v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP);
3177 return GetString(v->vehicle_flags.Test(VehicleFlag::PathfinderLost) ? STR_VEHICLE_STATUS_CANNOT_REACH_WAYPOINT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL,
3178 v->current_order.GetDestination(),PackVelocity(v->GetDisplaySpeed(), v->type));
3179
3180 case OT_LEAVESTATION:
3181 if (v->type != VEH_AIRCRAFT) {
3182 return GetString(STR_VEHICLE_STATUS_LEAVING);
3183 }
3184 [[fallthrough]];
3185
3186 default:
3187 if (v->GetNumManualOrders() == 0) {
3188 return GetString(STR_VEHICLE_STATUS_NO_ORDERS_VEL, PackVelocity(v->GetDisplaySpeed(), v->type));
3189 }
3190
3191 return {};
3192 }
3193 }
3194
3195 void DrawWidget(const Rect &r, WidgetID widget) const override
3196 {
3197 if (widget != WID_VV_START_STOP) return;
3198
3199 /* Draw the flag plus orders. */
3200 bool rtl = (_current_text_dir == TD_RTL);
3201 uint icon_width = std::max({GetScaledSpriteSize(SPR_WARNING_SIGN).width, GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED).width, GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING).width});
3202 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
3203
3204 const Vehicle *v = Vehicle::Get(this->window_number);
3205 SpriteID image = v->vehstatus.Test(VehState::Stopped) ? SPR_FLAG_VEH_STOPPED : (v->vehicle_flags.Test(VehicleFlag::PathfinderLost)) ? SPR_WARNING_SIGN : SPR_FLAG_VEH_RUNNING;
3206 DrawSpriteIgnorePadding(image, PAL_NONE, tr.WithWidth(icon_width, rtl), SA_CENTER);
3207
3208 tr = tr.Indent(icon_width + WidgetDimensions::scaled.imgbtn.Horizontal(), rtl);
3209
3210 TextColour text_colour = TC_FROMSTRING;
3211 std::string str = GetVehicleStatusString(v, text_colour);
3212 DrawString(tr.left, tr.right, CentreBounds(tr.top, tr.bottom, GetCharacterHeight(FS_NORMAL)), str, text_colour, SA_HOR_CENTER);
3213 }
3214
3215 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
3216 {
3217 const Vehicle *v = Vehicle::Get(this->window_number);
3218
3219 switch (widget) {
3220 case WID_VV_RENAME: { // rename
3221 ShowQueryString(GetString(STR_VEHICLE_NAME, v->index), STR_QUERY_RENAME_TRAIN_CAPTION + v->type,
3222 MAX_LENGTH_VEHICLE_NAME_CHARS, this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars});
3223 break;
3224 }
3225
3226 case WID_VV_START_STOP: // start stop
3227 StartStopVehicle(v, false);
3228 break;
3229
3230 case WID_VV_ORDER_LOCATION: {
3231 /* Scroll to current order destination */
3232 TileIndex tile = v->current_order.GetLocation(v);
3233 if (tile == INVALID_TILE) break;
3234
3235 if (_ctrl_pressed) {
3237 } else {
3239 }
3240 break;
3241 }
3242
3243 case WID_VV_LOCATION: // center main view
3244 if (_ctrl_pressed) {
3246 } else {
3247 const Window *mainwindow = GetMainWindow();
3248 if (click_count > 1) {
3249 /* main window 'follows' vehicle */
3250 mainwindow->viewport->follow_vehicle = v->index;
3251 } else {
3252 if (mainwindow->viewport->follow_vehicle == v->index) mainwindow->viewport->follow_vehicle = VehicleID::Invalid();
3253 ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos);
3254 }
3255 }
3256 break;
3257
3258 case WID_VV_GOTO_DEPOT: // goto hangar
3260 break;
3261 case WID_VV_REFIT: // refit
3263 break;
3264 case WID_VV_SHOW_ORDERS: // show orders
3265 if (_ctrl_pressed) {
3267 } else {
3268 ShowOrdersWindow(v);
3269 }
3270 break;
3271 case WID_VV_SHOW_DETAILS: // show details
3272 if (_ctrl_pressed) {
3274 } else {
3276 }
3277 break;
3278 case WID_VV_CLONE: // clone vehicle
3279 /* Suppress the vehicle GUI when share-cloning.
3280 * There is no point to it except for starting the vehicle.
3281 * For starting the vehicle the player has to open the depot GUI, which is
3282 * most likely already open, but is also visible in the vehicle viewport. */
3284 _ctrl_pressed ? nullptr : CcCloneVehicle,
3285 v->tile, v->index, _ctrl_pressed);
3286 break;
3287 case WID_VV_TURN_AROUND: // turn around
3288 assert(v->IsGroundVehicle());
3289 if (v->type == VEH_ROAD) {
3291 } else {
3293 }
3294 break;
3295 case WID_VV_FORCE_PROCEED: // force proceed
3296 assert(v->type == VEH_TRAIN);
3297 Command<CMD_FORCE_TRAIN_PROCEED>::Post(STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL, v->tile, v->index);
3298 break;
3299 }
3300 }
3301
3302 EventState OnHotkey(int hotkey) override
3303 {
3304 /* If the hotkey is not for any widget in the UI (i.e. for honking) */
3305 if (hotkey == WID_VV_HONK_HORN) {
3306 const Window *mainwindow = GetMainWindow();
3308 /* Only play the sound if we're following this vehicle */
3309 if (mainwindow->viewport->follow_vehicle == v->index) {
3310 v->PlayLeaveStationSound(true);
3311 }
3312 }
3313 return Window::OnHotkey(hotkey);
3314 }
3315
3316 void OnQueryTextFinished(std::optional<std::string> str) override
3317 {
3318 if (!str.has_value()) return;
3319
3320 Command<CMD_RENAME_VEHICLE>::Post(STR_ERROR_CAN_T_RENAME_TRAIN + Vehicle::Get(this->window_number)->type, static_cast<VehicleID>(this->window_number), *str);
3321 }
3322
3323 void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
3324 {
3325 bool start_stop = widget == WID_VV_START_STOP;
3326 if (start_stop != mouse_over_start_stop) {
3327 mouse_over_start_stop = start_stop;
3329 }
3330 }
3331
3332 void OnMouseWheel(int wheel, WidgetID widget) override
3333 {
3334 if (widget != WID_VV_VIEWPORT) return;
3336 DoZoomInOutWindow(wheel < 0 ? ZOOM_IN : ZOOM_OUT, this);
3337 }
3338 }
3339
3340 void OnResize() override
3341 {
3342 if (this->viewport != nullptr) {
3343 NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_VV_VIEWPORT);
3344 nvp->UpdateViewportCoordinates(this);
3345 }
3346 }
3347
3348 void UpdateButtonStatus()
3349 {
3350 const Vehicle *v = Vehicle::Get(this->window_number);
3351 bool veh_stopped = v->IsStoppedInDepot();
3352
3353 /* Widget WID_VV_GOTO_DEPOT must be hidden if the vehicle is already stopped in depot.
3354 * Widget WID_VV_CLONE_VEH should then be shown, since cloning is allowed only while in depot and stopped.
3355 */
3356 PlaneSelections plane = veh_stopped ? SEL_DC_CLONE : SEL_DC_GOTO_DEPOT;
3357 NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(WID_VV_SELECT_DEPOT_CLONE); // Selection widget 'send to depot' / 'clone'.
3358 if (nwi->shown_plane + SEL_DC_BASEPLANE != plane) {
3359 this->SelectPlane(plane);
3361 }
3362 /* The same system applies to widget WID_VV_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
3363 if (v->IsGroundVehicle()) {
3364 plane = veh_stopped ? SEL_RT_REFIT : SEL_RT_TURN_AROUND;
3365 nwi = this->GetWidget<NWidgetStacked>(WID_VV_SELECT_REFIT_TURN);
3366 if (nwi->shown_plane + SEL_RT_BASEPLANE != plane) {
3367 this->SelectPlane(plane);
3369 }
3370 }
3371 }
3372
3378 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
3379 {
3380 if (data == VIWD_AUTOREPLACE) {
3381 /* Autoreplace replaced the vehicle.
3382 * Nothing to do for this window. */
3383 return;
3384 }
3385
3386 this->UpdateButtonStatus();
3387 }
3388
3389 bool IsNewGRFInspectable() const override
3390 {
3391 return ::IsNewGRFInspectable(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3392 }
3393
3394 void ShowNewGRFInspectWindow() const override
3395 {
3396 ::ShowNewGRFInspectWindow(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
3397 }
3398
3399 static inline HotkeyList hotkeys{"vehicleview", {
3400 Hotkey('H', "honk", WID_VV_HONK_HORN),
3401 }};
3402};
3403
3406 WDP_AUTO, "view_vehicle", 250, 116,
3408 {},
3410 &VehicleViewWindow::hotkeys
3411);
3412
3418 WDP_AUTO, "view_vehicle_train", 250, 134,
3420 {},
3422 &VehicleViewWindow::hotkeys
3423);
3424
3427{
3428 AllocateWindowDescFront<VehicleViewWindow>((v->type == VEH_TRAIN) ? _train_view_desc : _vehicle_view_desc, v->index);
3429}
3430
3437{
3438 assert(v != nullptr);
3439 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3440
3441 v = v->First();
3442 if (!v->IsPrimaryVehicle()) return false;
3443
3444 return _thd.GetCallbackWnd()->OnVehicleSelect(v);
3445}
3446
3453bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end)
3454{
3455 assert(begin != end);
3456 if (!(_thd.place_mode & HT_VEHICLE)) return false;
3457
3458 /* If there is only one vehicle in the group, act as if we clicked a single vehicle */
3459 if (begin + 1 == end) return _thd.GetCallbackWnd()->OnVehicleSelect(*begin);
3460
3461 return _thd.GetCallbackWnd()->OnVehicleSelect(begin, end);
3462}
3463
3469bool VehicleClicked(const GUIVehicleGroup &vehgroup)
3470{
3471 return VehicleClicked(vehgroup.vehicles_begin, vehgroup.vehicles_end);
3472}
3473
3474void StopGlobalFollowVehicle(const Vehicle *v)
3475{
3476 Window *w = GetMainWindow();
3477 if (w->viewport->follow_vehicle == v->index) {
3478 ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos, true); // lock the main view on the vehicle's last position
3479 w->viewport->CancelFollow(*w);
3480 }
3481}
3482
3483
3489void CcBuildPrimaryVehicle(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray)
3490{
3491 if (result.Failed()) return;
3492
3493 const Vehicle *v = Vehicle::Get(new_veh_id);
3495}
3496
3503{
3504 switch (v->type) {
3505 case VEH_TRAIN:
3506 return Train::From(v)->GetDisplayImageWidth();
3507
3508 case VEH_ROAD:
3509 return RoadVehicle::From(v)->GetDisplayImageWidth();
3510
3511 default:
3512 bool rtl = _current_text_dir == TD_RTL;
3513 VehicleSpriteSeq seq;
3514 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3515 Rect rec;
3516 seq.GetBounds(&rec);
3517 return UnScaleGUI(rec.Width());
3518 }
3519}
3520
3526int GetVehicleWidth(const Vehicle *v, EngineImageType image_type)
3527{
3528 if (v->type == VEH_TRAIN || v->type == VEH_ROAD) {
3529 int vehicle_width = 0;
3530 for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
3531 vehicle_width += GetSingleVehicleWidth(u, image_type);
3532 }
3533 return vehicle_width;
3534 } else {
3535 return GetSingleVehicleWidth(v, image_type);
3536 }
3537}
3538
3545{
3546 bool rtl = _current_text_dir == TD_RTL;
3547
3548 _cursor.sprites.clear();
3549 int total_width = 0;
3550 int y_offset = 0;
3551 bool rotor_seq = false; // Whether to draw the rotor of the vehicle in this step.
3552 bool is_ground_vehicle = v->IsGroundVehicle();
3553
3554 while (v != nullptr) {
3555 if (total_width >= ScaleSpriteTrad(2 * (int)VEHICLEINFO_FULL_VEHICLE_WIDTH)) break;
3556
3558 VehicleSpriteSeq seq;
3559
3560 if (rotor_seq) {
3561 GetCustomRotorSprite(Aircraft::From(v), image_type, &seq);
3562 if (!seq.IsValid()) seq.Set(SPR_ROTOR_STOPPED);
3563 y_offset = -ScaleSpriteTrad(5);
3564 } else {
3565 v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
3566 }
3567
3568 int x_offs = 0;
3569 if (v->type == VEH_TRAIN) x_offs = Train::From(v)->GetCursorImageOffset();
3570
3571 for (uint i = 0; i < seq.count; ++i) {
3572 PaletteID pal2 = v->vehstatus.Test(VehState::Crashed) || !seq.seq[i].pal ? pal : seq.seq[i].pal;
3573 _cursor.sprites.emplace_back(seq.seq[i].sprite, pal2, rtl ? (-total_width + x_offs) : (total_width + x_offs), y_offset);
3574 }
3575
3576 if (v->type == VEH_AIRCRAFT && v->subtype == AIR_HELICOPTER && !rotor_seq) {
3577 /* Draw rotor part in the next step. */
3578 rotor_seq = true;
3579 } else {
3580 total_width += GetSingleVehicleWidth(v, image_type);
3581 v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr;
3582 }
3583 }
3584
3585 if (is_ground_vehicle) {
3586 /* Center trains and road vehicles on the front vehicle */
3587 int offs = (ScaleSpriteTrad(VEHICLEINFO_FULL_VEHICLE_WIDTH) - total_width) / 2;
3588 if (rtl) offs = -offs;
3589 for (auto &cs : _cursor.sprites) {
3590 cs.pos.x += offs;
3591 }
3592 }
3593
3595}
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.
@ PathfinderLost
Vehicle's pathfinder is lost.
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 cargo)
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:31
@ Passengers
Passengers.
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
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.
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:938
uint current_x
Current horizontal size (after resizing).
int pos_x
Horizontal position of top-left corner of the widget in the window.
uint current_y
Current vertical size (after resizing).
Base class for a 'real' widget.
void SetStringTip(StringID string, StringID tool_tip)
Set string and tool tip of the nested widget.
Definition widget.cpp:1171
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:1443
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:2459
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:2482
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition widget.cpp:2556
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:30
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:58
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
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:451
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:414
Dimension GetDropDownListDimension(const DropDownList &list)
Determine width and height required to fully display a DropDownList.
Definition dropdown.cpp:373
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
bool IsEngineRefittable(EngineID engine)
Check if an engine is refittable.
Definition engine.cpp:1287
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.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:958
void UpdateCursorSize()
Update cursor dimension.
Definition gfx.cpp:1604
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:887
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:925
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:910
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:1024
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:775
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
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:252
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:251
@ SA_LEFT
Left align the text.
Definition gfx_type.h:383
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:384
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:393
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:302
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:327
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:955
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 GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:688
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, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles.
StringID GetGRFStringID(uint32_t grfid, GRFStringID stringid)
Returns the index for this stringid associated with its grfID.
Header of Action 04 "universal holder" structure and functions.
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:121
@ ODATFB_HALT
Service the vehicle and then halt it.
Definition order_type.h:119
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel)
Definition order_type.h:39
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
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:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
@ 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:1617
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1611
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1396
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:425
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:237
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:327
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:91
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:415
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:57
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:219
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.
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
TimerGameTick::Ticks lateness_counter
How many ticks late (or early if negative) this vehicle is.
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:137
Dimensions (a width and height) of a rectangle in 2D.
EngineMiscFlags misc_flags
Miscellaneous flags.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
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.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:264
VehicleOrderID GetNext(VehicleOrderID cur) const
Get the order after the given one or the first one, if the given one is the last one.
Definition order_base.h:352
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:99
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:72
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:142
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.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Option to refit a vehicle chain.
StringID string
GRF-local String to display for the cargo.
CargoType cargo
Cargo to refit to.
uint8_t subtype
Subcargo to use.
bool operator==(const RefitOption &other) const
Equality operator for RefitOption.
Refit cargo window.
void OnInit() override
Notification that the nested widget tree gets initialized.
uint information_width
Width required for correctly displaying all cargoes in the information panel.
void SetSelection(uint click_row)
Select a row.
void BuildRefitList()
Collects all (cargo, subcargo) refit options of a vehicle chain.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
int sprite_left
Left position of the vehicle sprite.
void RefreshScrollbar()
Refresh scrollbar after selection changed.
RefitOptions refit_list
List of refit subtypes available for each sorted cargo.
void OnMouseDrag(Point pt, WidgetID widget) override
An 'object' is being dragged at the provided position, highlight the target if possible.
void OnResize() override
Called after the window got resized.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
const RefitOption * selected_refit
Selected refit option.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
uint vehicle_margin
Margin to use while selecting vehicles when the vehicle image is centered.
uint8_t num_vehicles
Number of selected vehicles.
bool auto_refit
Select cargo for auto-refitting.
int sprite_right
Right position of the vehicle sprite.
VehicleOrderID order
If not INVALID_VEH_ORDER_ID, selection is part of a refit order (rather than execute directly).
VehicleID selected_vehicle
First vehicle in the current selection.
void OnPaint() override
The window must be repainted.
void OnDragDrop(Point pt, WidgetID widget) override
A dragged 'object' has been released.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
Scrollbar * hscroll
Only used for long vehicles.
int vehicle_width
Width of the vehicle being drawn.
Scrollbar * vscroll
The main scrollbar.
int click_x
Position of the first click while dragging.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
std::string GetCapacityString(const RefitOption &option) const
Gets the StringID to use for displaying capacity.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:212
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.
Templated helper to make a type-safe 'typedef' representing a single POD value.
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:91
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.
uint16_t servint_aircraft
service interval for aircraft
uint16_t servint_roadveh
service interval for road vehicles
uint16_t servint_ships
service interval for ships
uint16_t servint_trains
service interval for trains
Class for managing the vehicle details window.
bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
uint GetRoadVehDetailsHeight(const Vehicle *v)
Gets the desired height for the road vehicle details panel.
static void DrawVehicleDetails(const Vehicle *v, const Rect &r, int vscroll_pos, uint vscroll_cap, TrainDetailsWindowTabs det_tab)
Draw the details for the given vehicle at the position of the Details windows.
void OnPaint() override
Repaint vehicle details window.
static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type, CompanyID company_id)
Checks whether service interval is enabled for the vehicle.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnResize() override
Called after the window got resized.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
VehicleDetailsWindow(WindowDesc &desc, WindowNumber window_number)
Initialize a newly created vehicle details window.
TrainDetailsWindowTabs tab
For train vehicles: which tab is displayed.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
The information about a vehicle list.
Definition vehiclelist.h:32
VehicleListType type
The type of vehicle list.
Definition vehiclelist.h:33
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 OnResize() override
Called after the window got resized.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnPaint() override
The window must be repainted.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
CaptionPlanes
Enumeration of planes of the title row at the top.
@ BP_SHARED_ORDERS
Show the normal caption.
@ BP_NORMAL
Show shared orders caption and buttons.
void OnGameTick() override
Called once per (game) tick.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
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:114
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 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.
void OnMouseWheel(int wheel, WidgetID widget) override
The mouse wheel has been turned.
bool IsNewGRFInspectable() const override
Is the data related to this window NewGRF inspectable?
Vehicle data structure.
Money GetDisplayProfitThisYear() const
Gets the profit vehicle had this year.
EngineID engine_type
The type of engine used for this vehicle.
virtual int GetDisplaySpeed() const
Gets the speed in km-ish/h that can be sent into string parameters for string processing.
int32_t z_pos
z coordinate.
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:718
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
Vehicle * GetNextArticulatedPart() const
Get the next part of an articulated engine.
virtual bool IsChainInDepot() const
Check whether the whole vehicle chain is in the depot.
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
uint16_t cargo_cap
total capacity
Vehicle * GetFirstEnginePart()
Get the first part of an articulated engine.
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
bool HasArticulatedPart() const
Check if an engine has an articulated part.
uint8_t breakdown_ctr
Counter for managing breakdown events.
GroupID group_id
Index of group Pool array.
virtual int GetDisplayMaxSpeed() const
Gets the maximum speed in km-ish/h that can be sent into string parameters for string processing.
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:728
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:3220
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:738
GroundVehicleCache * GetGroundVehicleCache()
Access the ground vehicle cache of the vehicle.
Definition vehicle.cpp:3116
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:2522
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.
High level window description.
Definition window_gui.h:167
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:967
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1091
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:826
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1778
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:777
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3205
Window * parent
Parent window.
Definition window_gui.h:328
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:555
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:503
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:809
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1064
ResizeInfo resize
Resize information.
Definition window_gui.h:314
virtual bool OnVehicleSelect(const struct Vehicle *v)
The user clicked on a vehicle while HT_VEHICLE has been set.
Definition window_gui.h:808
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:515
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1768
void SetWidgetsLoweredState(bool lowered_stat, Args... widgets)
Sets the lowered/raised status of a list of widgets.
Definition window_gui.h:526
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:316
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:441
int left
x position of left edge of the window
Definition window_gui.h:309
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:460
WindowFlags flags
Window flags.
Definition window_gui.h:300
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:312
virtual EventState OnHotkey(int hotkey)
A hotkey has been pressed.
Definition window.cpp:569
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:381
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
Stuff related to the text buffer GUI.
@ 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.
@ 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:41
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:2122
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:3179
@ Crashed
Vehicle is crashed.
@ Stopped
Vehicle is stopped by the player.
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.
@ 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:298
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h: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:67
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:45
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:58
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:51
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:56
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:53
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:77
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:69
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:61
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:76
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:60
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:57
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:74
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:62
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition widget_type.h:55
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:72
@ 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:1182
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1169
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1140
@ 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:218
@ SBS_UP
Sort descending.
Definition window_gui.h:219
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
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
@ Ship
Default zoom level for the ship view.
@ RoadVehicle
Default zoom level for the road vehicle view.
@ Aircraft
Default zoom level for the aircraft view.
@ Train
Default zoom level for the train view.