4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file build_vehicle_gui.cpp GUI for building vehicles. */
13 #include "engine_base.h"
14 #include "engine_func.h"
15 #include "station_base.h"
16 #include "network/network.h"
17 #include "articulated_vehicles.h"
18 #include "textbuf_gui.h"
19 #include "command_func.h"
20 #include "company_func.h"
21 #include "vehicle_gui.h"
22 #include "newgrf_engine.h"
23 #include "newgrf_text.h"
25 #include "string_func.h"
26 #include "strings_func.h"
27 #include "window_func.h"
28 #include "date_func.h"
29 #include "vehicle_func.h"
30 #include "widgets/dropdown_func.h"
31 #include "engine_gui.h"
32 #include "cargotype.h"
33 #include "core/geometry_func.hpp"
34 #include "autoreplace_func.h"
37 #include "widgets/build_vehicle_widget.h"
39 #include "table/strings.h"
41 #include "safeguards.h"
44 * Get the height of a single 'entry' in the engine lists.
45 * @param type the vehicle type to get the height of
46 * @return the height for the entry
48 uint
GetEngineListHeight(VehicleType type
)
50 return max
<uint
>(FONT_HEIGHT_NORMAL
+ WD_MATRIX_TOP
+ WD_MATRIX_BOTTOM
, GetVehicleImageCellSize(type
, EIT_PURCHASE
).height
);
54 * Normal layout for roadvehicles, ships and airplanes.
56 static const NWidgetPart _nested_build_vehicle_widgets
[] = {
57 NWidget(NWID_HORIZONTAL
),
58 NWidget(WWT_CLOSEBOX
, COLOUR_GREY
),
59 NWidget(WWT_CAPTION
, COLOUR_GREY
, WID_BV_CAPTION
), SetDataTip(STR_WHITE_STRING
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
60 NWidget(WWT_SHADEBOX
, COLOUR_GREY
),
61 NWidget(WWT_DEFSIZEBOX
, COLOUR_GREY
),
62 NWidget(WWT_STICKYBOX
, COLOUR_GREY
),
64 NWidget(WWT_PANEL
, COLOUR_GREY
),
65 NWidget(NWID_VERTICAL
),
66 NWidget(NWID_HORIZONTAL
),
67 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_SORT_ASCENDING_DESCENDING
), SetDataTip(STR_BUTTON_SORT_BY
, STR_TOOLTIP_SORT_ORDER
),
68 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_BV_SORT_DROPDOWN
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_SORT_CRITERIA
),
70 NWidget(NWID_HORIZONTAL
),
71 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BV_SHOW_HIDDEN_ENGINES
),
72 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_BV_CARGO_FILTER_DROPDOWN
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_FILTER_CRITERIA
),
77 NWidget(NWID_HORIZONTAL
),
78 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_BV_LIST
), SetResize(1, 1), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_NULL
), SetScrollbar(WID_BV_SCROLLBAR
),
79 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_BV_SCROLLBAR
),
81 /* Panel with details. */
82 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BV_PANEL
), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
83 /* Build/rename buttons, resize button. */
84 NWidget(NWID_HORIZONTAL
),
85 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BV_BUILD_SEL
),
86 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_BUILD
), SetResize(1, 0), SetFill(1, 0),
88 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_SHOW_HIDE
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_NULL
),
89 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_RENAME
), SetResize(1, 0), SetFill(1, 0),
90 NWidget(WWT_RESIZEBOX
, COLOUR_GREY
),
94 /* Advanced layout for trains. */
96 static const NWidgetPart _nested_build_vehicle_widgets_train_advanced
[] = {
97 NWidget(NWID_HORIZONTAL
),
98 NWidget(WWT_CLOSEBOX
, COLOUR_GREY
),
99 NWidget(WWT_CAPTION
, COLOUR_GREY
, WID_BV_CAPTION
), SetDataTip(STR_WHITE_STRING
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
100 NWidget(WWT_SHADEBOX
, COLOUR_GREY
),
101 NWidget(WWT_DEFSIZEBOX
, COLOUR_GREY
),
102 NWidget(WWT_STICKYBOX
, COLOUR_GREY
),
105 NWidget(NWID_HORIZONTAL
),
106 /* First half of the window contains locomotives. */
107 NWidget(NWID_VERTICAL
),
108 NWidget(NWID_HORIZONTAL
),
109 NWidget(WWT_PANEL
, COLOUR_GREY
), SetFill(1, 0),
110 NWidget(WWT_LABEL
, COLOUR_GREY
, WID_BV_CAPTION_LOCO
), SetDataTip(STR_WHITE_STRING
, STR_NULL
), SetResize(1, 0), SetFill(1, 0),
113 NWidget(WWT_PANEL
, COLOUR_GREY
),
114 NWidget(NWID_VERTICAL
),
115 NWidget(NWID_HORIZONTAL
),
116 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_SORT_ASSENDING_DESCENDING_LOCO
), SetDataTip(STR_BUTTON_SORT_BY
, STR_TOOLTIP_SORT_ORDER
), SetFill(1, 0),
117 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_BV_SORT_DROPDOWN_LOCO
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_SORT_CRITERIA
),
119 NWidget(NWID_HORIZONTAL
),
120 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BV_SHOW_HIDDEN_LOCOS
),
121 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_BV_CARGO_FILTER_DROPDOWN_LOCO
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_FILTER_CRITERIA
),
125 /* Vehicle list for locomotives. */
126 NWidget(NWID_HORIZONTAL
),
127 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_BV_LIST_LOCO
), SetResize(1, 1), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_NULL
), SetScrollbar(WID_BV_SCROLLBAR_LOCO
),
128 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_BV_SCROLLBAR_LOCO
),
130 /* Panel with details for locomotives. */
131 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BV_PANEL_LOCO
), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
132 /* Build/rename buttons, resize button for locomotives. */
133 NWidget(NWID_HORIZONTAL
),
134 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BV_BUILD_SEL_LOCO
),
135 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_BUILD_LOCO
), SetMinimalSize(50, 1), SetResize(1, 0), SetFill(1, 0),
137 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_SHOW_HIDE_LOCO
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_NULL
),
138 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_RENAME_LOCO
), SetResize(1, 0), SetFill(1, 0),
142 /* Second half of the window contains wagons. */
143 NWidget(NWID_VERTICAL
),
144 NWidget(NWID_HORIZONTAL
),
145 NWidget(WWT_PANEL
, COLOUR_GREY
), SetFill(1, 0),
146 NWidget(WWT_LABEL
, COLOUR_GREY
, WID_BV_CAPTION_WAGON
), SetDataTip(STR_WHITE_STRING
, STR_NULL
), SetResize(1, 0), SetFill(1, 0),
149 NWidget(WWT_PANEL
, COLOUR_GREY
),
150 NWidget(NWID_VERTICAL
),
151 NWidget(NWID_HORIZONTAL
),
152 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_SORT_ASSENDING_DESCENDING_WAGON
), SetDataTip(STR_BUTTON_SORT_BY
, STR_TOOLTIP_SORT_ORDER
), SetFill(1, 0),
153 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_BV_SORT_DROPDOWN_WAGON
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_SORT_CRITERIA
),
155 NWidget(NWID_HORIZONTAL
),
156 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BV_SHOW_HIDDEN_WAGONS
),
157 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_BV_CARGO_FILTER_DROPDOWN_WAGON
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_FILTER_CRITERIA
),
161 /* Vehicle list for wagons. */
162 NWidget(NWID_HORIZONTAL
),
163 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_BV_LIST_WAGON
), SetResize(1, 1), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_NULL
), SetScrollbar(WID_BV_SCROLLBAR_WAGON
),
164 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_BV_SCROLLBAR_WAGON
),
166 /* Panel with details for wagons. */
167 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BV_PANEL_WAGON
), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
168 /* Build/rename buttons, resize button for wagons. */
169 NWidget(NWID_HORIZONTAL
),
170 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BV_BUILD_SEL_WAGON
),
171 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_BUILD_WAGON
), SetMinimalSize(50, 1), SetResize(1, 0), SetFill(1, 0),
173 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_SHOW_HIDE_WAGON
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_NULL
),
174 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_BV_RENAME_WAGON
), SetResize(1, 0), SetFill(1, 0),
175 NWidget(WWT_RESIZEBOX
, COLOUR_GREY
),
181 /** Special cargo filter criteria */
182 static const CargoID CF_ANY
= CT_NO_REFIT
; ///< Show all vehicles independent of carried cargo (i.e. no filtering)
183 static const CargoID CF_NONE
= CT_INVALID
; ///< Show only vehicles which do not carry cargo (e.g. train engines)
185 bool _engine_sort_direction
; ///< \c false = descending, \c true = ascending.
186 byte _engine_sort_last_criteria
[] = {0, 0, 0, 0}; ///< Last set sort criteria, for each vehicle type.
187 bool _engine_sort_last_order
[] = {false, false, false, false}; ///< Last set direction of the sort order, for each vehicle type.
188 bool _engine_sort_show_hidden_engines
[] = {false, false, false, false}; ///< Last set 'show hidden engines' setting for each vehicle type.
189 bool _engine_sort_show_hidden_locos
= false; ///< Last set 'show hidden locos' setting.
190 bool _engine_sort_show_hidden_wagons
= false; ///< Last set 'show hidden wagons' setting.
192 static CargoID _engine_sort_last_cargo_criteria
[] = {CF_ANY
, CF_ANY
, CF_ANY
, CF_ANY
}; ///< Last set filter criteria, for each vehicle type.
194 static bool _internal_sort_order_loco
; ///< false = descending, true = ascending
195 static byte _last_sort_criteria_loco
= 0;
196 static bool _last_sort_order_loco
= false;
197 static CargoID _last_filter_criteria_loco
= CF_ANY
;
199 static bool _internal_sort_order_wagon
; ///< false = descending, true = ascending
200 static byte _last_sort_criteria_wagon
= 0;
201 static bool _last_sort_order_wagon
= false;
202 static CargoID _last_filter_criteria_wagon
= CF_ANY
;
205 * Determines order of engines by engineID
206 * @param *a first engine to compare
207 * @param *b second engine to compare
208 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
210 static int CDECL
EngineNumberSorter(const EngineID
*a
, const EngineID
*b
)
212 int r
= Engine::Get(*a
)->list_position
- Engine::Get(*b
)->list_position
;
214 return _engine_sort_direction
? -r
: r
;
218 * Determines order of engines by introduction date
219 * @param *a first engine to compare
220 * @param *b second engine to compare
221 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
223 static int CDECL
EngineIntroDateSorter(const EngineID
*a
, const EngineID
*b
)
225 const int va
= Engine::Get(*a
)->intro_date
;
226 const int vb
= Engine::Get(*b
)->intro_date
;
227 const int r
= va
- vb
;
229 /* Use EngineID to sort instead since we want consistent sorting */
230 if (r
== 0) return EngineNumberSorter(a
, b
);
231 return _engine_sort_direction
? -r
: r
;
235 * Determines order of engines by name
236 * @param *a first engine to compare
237 * @param *b second engine to compare
238 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
240 static int CDECL
EngineNameSorter(const EngineID
*a
, const EngineID
*b
)
242 static EngineID last_engine
[2] = { INVALID_ENGINE
, INVALID_ENGINE
};
243 static char last_name
[2][64] = { "\0", "\0" };
245 const EngineID va
= *a
;
246 const EngineID vb
= *b
;
248 if (va
!= last_engine
[0]) {
251 GetString(last_name
[0], STR_ENGINE_NAME
, lastof(last_name
[0]));
254 if (vb
!= last_engine
[1]) {
257 GetString(last_name
[1], STR_ENGINE_NAME
, lastof(last_name
[1]));
260 int r
= strnatcmp(last_name
[0], last_name
[1]); // Sort by name (natural sorting).
262 /* Use EngineID to sort instead since we want consistent sorting */
263 if (r
== 0) return EngineNumberSorter(a
, b
);
264 return _engine_sort_direction
? -r
: r
;
268 * Determines order of engines by reliability
269 * @param *a first engine to compare
270 * @param *b second engine to compare
271 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
273 static int CDECL
EngineReliabilitySorter(const EngineID
*a
, const EngineID
*b
)
275 const int va
= Engine::Get(*a
)->reliability
;
276 const int vb
= Engine::Get(*b
)->reliability
;
277 const int r
= va
- vb
;
279 /* Use EngineID to sort instead since we want consistent sorting */
280 if (r
== 0) return EngineNumberSorter(a
, b
);
281 return _engine_sort_direction
? -r
: r
;
285 * Determines order of engines by purchase cost
286 * @param *a first engine to compare
287 * @param *b second engine to compare
288 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
290 static int CDECL
EngineCostSorter(const EngineID
*a
, const EngineID
*b
)
292 Money va
= Engine::Get(*a
)->GetCost();
293 Money vb
= Engine::Get(*b
)->GetCost();
294 int r
= ClampToI32(va
- vb
);
296 /* Use EngineID to sort instead since we want consistent sorting */
297 if (r
== 0) return EngineNumberSorter(a
, b
);
298 return _engine_sort_direction
? -r
: r
;
302 * Determines order of engines by speed
303 * @param *a first engine to compare
304 * @param *b second engine to compare
305 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
307 static int CDECL
EngineSpeedSorter(const EngineID
*a
, const EngineID
*b
)
309 int va
= Engine::Get(*a
)->GetDisplayMaxSpeed();
310 int vb
= Engine::Get(*b
)->GetDisplayMaxSpeed();
313 /* Use EngineID to sort instead since we want consistent sorting */
314 if (r
== 0) return EngineNumberSorter(a
, b
);
315 return _engine_sort_direction
? -r
: r
;
319 * Determines order of engines by power
320 * @param *a first engine to compare
321 * @param *b second engine to compare
322 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
324 static int CDECL
EnginePowerSorter(const EngineID
*a
, const EngineID
*b
)
326 int va
= Engine::Get(*a
)->GetPower();
327 int vb
= Engine::Get(*b
)->GetPower();
330 /* Use EngineID to sort instead since we want consistent sorting */
331 if (r
== 0) return EngineNumberSorter(a
, b
);
332 return _engine_sort_direction
? -r
: r
;
336 * Determines order of engines by tractive effort
337 * @param *a first engine to compare
338 * @param *b second engine to compare
339 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
341 static int CDECL
EngineTractiveEffortSorter(const EngineID
*a
, const EngineID
*b
)
343 int va
= Engine::Get(*a
)->GetDisplayMaxTractiveEffort();
344 int vb
= Engine::Get(*b
)->GetDisplayMaxTractiveEffort();
347 /* Use EngineID to sort instead since we want consistent sorting */
348 if (r
== 0) return EngineNumberSorter(a
, b
);
349 return _engine_sort_direction
? -r
: r
;
353 * Determines order of engines by running costs
354 * @param *a first engine to compare
355 * @param *b second engine to compare
356 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
358 static int CDECL
EngineRunningCostSorter(const EngineID
*a
, const EngineID
*b
)
360 Money va
= Engine::Get(*a
)->GetRunningCost();
361 Money vb
= Engine::Get(*b
)->GetRunningCost();
362 int r
= ClampToI32(va
- vb
);
364 /* Use EngineID to sort instead since we want consistent sorting */
365 if (r
== 0) return EngineNumberSorter(a
, b
);
366 return _engine_sort_direction
? -r
: r
;
370 * Determines order of engines by running costs
371 * @param *a first engine to compare
372 * @param *b second engine to compare
373 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
375 static int CDECL
EnginePowerVsRunningCostSorter(const EngineID
*a
, const EngineID
*b
)
377 const Engine
*e_a
= Engine::Get(*a
);
378 const Engine
*e_b
= Engine::Get(*b
);
380 /* Here we are using a few tricks to get the right sort.
381 * We want power/running cost, but since we usually got higher running cost than power and we store the result in an int,
382 * we will actually calculate cunning cost/power (to make it more than 1).
383 * Because of this, the return value have to be reversed as well and we return b - a instead of a - b.
384 * Another thing is that both power and running costs should be doubled for multiheaded engines.
385 * Since it would be multiplying with 2 in both numerator and denominator, it will even themselves out and we skip checking for multiheaded. */
386 Money va
= (e_a
->GetRunningCost()) / max(1U, (uint
)e_a
->GetPower());
387 Money vb
= (e_b
->GetRunningCost()) / max(1U, (uint
)e_b
->GetPower());
388 int r
= ClampToI32(vb
- va
);
390 /* Use EngineID to sort instead since we want consistent sorting */
391 if (r
== 0) return EngineNumberSorter(a
, b
);
392 return _engine_sort_direction
? -r
: r
;
395 /* Train sorting functions */
398 * Determines order of train engines by capacity
399 * @param *a first engine to compare
400 * @param *b second engine to compare
401 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
403 static int CDECL
TrainEngineCapacitySorter(const EngineID
*a
, const EngineID
*b
)
405 const RailVehicleInfo
*rvi_a
= RailVehInfo(*a
);
406 const RailVehicleInfo
*rvi_b
= RailVehInfo(*b
);
408 int va
= GetTotalCapacityOfArticulatedParts(*a
) * (rvi_a
->railveh_type
== RAILVEH_MULTIHEAD
? 2 : 1);
409 int vb
= GetTotalCapacityOfArticulatedParts(*b
) * (rvi_b
->railveh_type
== RAILVEH_MULTIHEAD
? 2 : 1);
412 /* Use EngineID to sort instead since we want consistent sorting */
413 if (r
== 0) return EngineNumberSorter(a
, b
);
414 return _engine_sort_direction
? -r
: r
;
418 * Determines order of train engines by engine / wagon
419 * @param *a first engine to compare
420 * @param *b second engine to compare
421 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
423 static int CDECL
TrainEnginesThenWagonsSorter(const EngineID
*a
, const EngineID
*b
)
425 int val_a
= (RailVehInfo(*a
)->railveh_type
== RAILVEH_WAGON
? 1 : 0);
426 int val_b
= (RailVehInfo(*b
)->railveh_type
== RAILVEH_WAGON
? 1 : 0);
427 int r
= val_a
- val_b
;
429 /* Use EngineID to sort instead since we want consistent sorting */
430 if (r
== 0) return EngineNumberSorter(a
, b
);
431 return _engine_sort_direction
? -r
: r
;
434 /* Road vehicle sorting functions */
437 * Determines order of road vehicles by capacity
438 * @param *a first engine to compare
439 * @param *b second engine to compare
440 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
442 static int CDECL
RoadVehEngineCapacitySorter(const EngineID
*a
, const EngineID
*b
)
444 int va
= GetTotalCapacityOfArticulatedParts(*a
);
445 int vb
= GetTotalCapacityOfArticulatedParts(*b
);
448 /* Use EngineID to sort instead since we want consistent sorting */
449 if (r
== 0) return EngineNumberSorter(a
, b
);
450 return _engine_sort_direction
? -r
: r
;
453 /* Ship vehicle sorting functions */
456 * Determines order of ships by capacity
457 * @param *a first engine to compare
458 * @param *b second engine to compare
459 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
461 static int CDECL
ShipEngineCapacitySorter(const EngineID
*a
, const EngineID
*b
)
463 const Engine
*e_a
= Engine::Get(*a
);
464 const Engine
*e_b
= Engine::Get(*b
);
466 int va
= e_a
->GetDisplayDefaultCapacity();
467 int vb
= e_b
->GetDisplayDefaultCapacity();
470 /* Use EngineID to sort instead since we want consistent sorting */
471 if (r
== 0) return EngineNumberSorter(a
, b
);
472 return _engine_sort_direction
? -r
: r
;
475 /* Aircraft sorting functions */
478 * Determines order of aircraft by cargo
479 * @param *a first engine to compare
480 * @param *b second engine to compare
481 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
483 static int CDECL
AircraftEngineCargoSorter(const EngineID
*a
, const EngineID
*b
)
485 const Engine
*e_a
= Engine::Get(*a
);
486 const Engine
*e_b
= Engine::Get(*b
);
488 uint16 mail_a
, mail_b
;
489 int va
= e_a
->GetDisplayDefaultCapacity(&mail_a
);
490 int vb
= e_b
->GetDisplayDefaultCapacity(&mail_b
);
494 /* The planes have the same passenger capacity. Check mail capacity instead */
498 /* Use EngineID to sort instead since we want consistent sorting */
499 return EngineNumberSorter(a
, b
);
502 return _engine_sort_direction
? -r
: r
;
506 * Determines order of aircraft by range.
507 * @param *a first engine to compare.
508 * @param *b second engine to compare.
509 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal.
511 static int CDECL
AircraftRangeSorter(const EngineID
*a
, const EngineID
*b
)
513 uint16 r_a
= Engine::Get(*a
)->GetRange();
514 uint16 r_b
= Engine::Get(*b
)->GetRange();
518 /* Use EngineID to sort instead since we want consistent sorting */
519 if (r
== 0) return EngineNumberSorter(a
, b
);
520 return _engine_sort_direction
? -r
: r
;
523 /* Locomotive sorting functions. */
525 * Determines order of locomotives by engineID
526 * @param *a first engine to compare
527 * @param *b second engine to compare
528 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
530 static int CDECL
EngineNumberSorterLoco(const EngineID
*a
, const EngineID
*b
)
532 int r
= Engine::Get(*a
)->list_position
- Engine::Get(*b
)->list_position
;
534 return _internal_sort_order_loco
? -r
: r
;
538 * Determines order of locomotives by introduction date
539 * @param *a first engine to compare
540 * @param *b second engine to compare
541 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
543 static int CDECL
EngineIntroDateSorterLoco(const EngineID
*a
, const EngineID
*b
)
545 const int va
= Engine::Get(*a
)->intro_date
;
546 const int vb
= Engine::Get(*b
)->intro_date
;
547 const int r
= va
- vb
;
549 /* Use EngineID to sort instead since we want consistent sorting */
550 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
551 return _internal_sort_order_loco
? -r
: r
;
555 * Determines order of locomotives by name
556 * @param *a first engine to compare
557 * @param *b second engine to compare
558 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
560 static int CDECL
EngineNameSorterLoco(const EngineID
*a
, const EngineID
*b
)
562 static EngineID last_engine
[2] = { INVALID_ENGINE
, INVALID_ENGINE
};
563 static char last_name
[2][64] = { "\0", "\0" };
565 const EngineID va
= *a
;
566 const EngineID vb
= *b
;
568 if (va
!= last_engine
[0]) {
571 GetString(last_name
[0], STR_ENGINE_NAME
, lastof(last_name
[0]));
574 if (vb
!= last_engine
[1]) {
577 GetString(last_name
[1], STR_ENGINE_NAME
, lastof(last_name
[1]));
580 int r
= strnatcmp(last_name
[0], last_name
[1]); // Sort by name (natural sorting).
582 /* Use EngineID to sort instead since we want consistent sorting */
583 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
584 return _internal_sort_order_loco
? -r
: r
;
588 * Determines order of locomotives by reliability
589 * @param *a first engine to compare
590 * @param *b second engine to compare
591 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
593 static int CDECL
EngineReliabilitySorterLoco(const EngineID
*a
, const EngineID
*b
)
595 const int va
= Engine::Get(*a
)->reliability
;
596 const int vb
= Engine::Get(*b
)->reliability
;
597 const int r
= va
- vb
;
599 /* Use EngineID to sort instead since we want consistent sorting */
600 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
601 return _internal_sort_order_loco
? -r
: r
;
605 * Determines order of locomotives by purchase cost
606 * @param *a first engine to compare
607 * @param *b second engine to compare
608 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
610 static int CDECL
EngineCostSorterLoco(const EngineID
*a
, const EngineID
*b
)
612 Money va
= Engine::Get(*a
)->GetCost();
613 Money vb
= Engine::Get(*b
)->GetCost();
614 int r
= ClampToI32(va
- vb
);
616 /* Use EngineID to sort instead since we want consistent sorting */
617 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
618 return _internal_sort_order_loco
? -r
: r
;
622 * Determines order of locomotives by speed
623 * @param *a first engine to compare
624 * @param *b second engine to compare
625 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
627 static int CDECL
EngineSpeedSorterLoco(const EngineID
*a
, const EngineID
*b
)
629 int va
= Engine::Get(*a
)->GetDisplayMaxSpeed();
630 int vb
= Engine::Get(*b
)->GetDisplayMaxSpeed();
633 /* Use EngineID to sort instead since we want consistent sorting */
634 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
635 return _internal_sort_order_loco
? -r
: r
;
639 * Determines order of locomotives by power
640 * @param *a first engine to compare
641 * @param *b second engine to compare
642 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
644 static int CDECL
EnginePowerSorterLoco(const EngineID
*a
, const EngineID
*b
)
646 int va
= Engine::Get(*a
)->GetPower();
647 int vb
= Engine::Get(*b
)->GetPower();
650 /* Use EngineID to sort instead since we want consistent sorting */
651 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
652 return _internal_sort_order_loco
? -r
: r
;
656 * Determines order of locomotives by tractive effort
657 * @param *a first engine to compare
658 * @param *b second engine to compare
659 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
661 static int CDECL
EngineTractiveEffortSorterLoco(const EngineID
*a
, const EngineID
*b
)
663 int va
= Engine::Get(*a
)->GetDisplayMaxTractiveEffort();
664 int vb
= Engine::Get(*b
)->GetDisplayMaxTractiveEffort();
667 /* Use EngineID to sort instead since we want consistent sorting */
668 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
669 return _internal_sort_order_loco
? -r
: r
;
673 * Determines order of locomotives by running costs
674 * @param *a first engine to compare
675 * @param *b second engine to compare
676 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
678 static int CDECL
EngineRunningCostSorterLoco(const EngineID
*a
, const EngineID
*b
)
680 Money va
= Engine::Get(*a
)->GetRunningCost();
681 Money vb
= Engine::Get(*b
)->GetRunningCost();
682 int r
= ClampToI32(va
- vb
);
684 /* Use EngineID to sort instead since we want consistent sorting */
685 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
686 return _internal_sort_order_loco
? -r
: r
;
690 * Determines order of locomotives by running costs
691 * @param *a first engine to compare
692 * @param *b second engine to compare
693 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
695 static int CDECL
EnginePowerVsRunningCostSorterLoco(const EngineID
*a
, const EngineID
*b
)
697 const Engine
*e_a
= Engine::Get(*a
);
698 const Engine
*e_b
= Engine::Get(*b
);
700 /* Here we are using a few tricks to get the right sort.
701 * We want power/running cost, but since we usually got higher running cost than power and we store the result in an int,
702 * we will actually calculate cunning cost/power (to make it more than 1).
703 * Because of this, the return value have to be reversed as well and we return b - a instead of a - b.
704 * Another thing is that both power and running costs should be doubled for multiheaded engines.
705 * Since it would be multiplying with 2 in both numerator and denominator, it will even themselves out and we skip checking for multiheaded. */
706 Money va
= (e_a
->GetRunningCost()) / max(1U, (uint
)e_a
->GetPower());
707 Money vb
= (e_b
->GetRunningCost()) / max(1U, (uint
)e_b
->GetPower());
708 int r
= ClampToI32(vb
- va
);
710 /* Use EngineID to sort instead since we want consistent sorting */
711 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
712 return _internal_sort_order_loco
? -r
: r
;
716 * Determines order of train locomotives by capacity
717 * @param *a first engine to compare
718 * @param *b second engine to compare
719 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
721 static int CDECL
TrainEngineCapacitySorterLoco(const EngineID
*a
, const EngineID
*b
)
723 const RailVehicleInfo
*rvi_a
= RailVehInfo(*a
);
724 const RailVehicleInfo
*rvi_b
= RailVehInfo(*b
);
726 int va
= GetTotalCapacityOfArticulatedParts(*a
) * (rvi_a
->railveh_type
== RAILVEH_MULTIHEAD
? 2 : 1);
727 int vb
= GetTotalCapacityOfArticulatedParts(*b
) * (rvi_b
->railveh_type
== RAILVEH_MULTIHEAD
? 2 : 1);
730 /* Use EngineID to sort instead since we want consistent sorting */
731 if (r
== 0) return EngineNumberSorterLoco(a
, b
);
732 return _internal_sort_order_loco
? -r
: r
;
735 /* Wagon sorting functions. */
738 * Determines order of wagons by engineID
739 * @param *a first engine to compare
740 * @param *b second engine to compare
741 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
743 static int CDECL
EngineNumberSorterWagon(const EngineID
*a
, const EngineID
*b
)
745 int r
= Engine::Get(*a
)->list_position
- Engine::Get(*b
)->list_position
;
747 return _internal_sort_order_wagon
? -r
: r
;
751 * Determines order of wagons by introduction date
752 * @param *a first engine to compare
753 * @param *b second engine to compare
754 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
756 static int CDECL
EngineIntroDateSorterWagon(const EngineID
*a
, const EngineID
*b
)
758 const int va
= Engine::Get(*a
)->intro_date
;
759 const int vb
= Engine::Get(*b
)->intro_date
;
760 const int r
= va
- vb
;
762 /* Use EngineID to sort instead since we want consistent sorting */
763 if (r
== 0) return EngineNumberSorterWagon(a
, b
);
764 return _internal_sort_order_wagon
? -r
: r
;
768 * Determines order of wagons by name
769 * @param *a first engine to compare
770 * @param *b second engine to compare
771 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
773 static int CDECL
EngineNameSorterWagon(const EngineID
*a
, const EngineID
*b
)
775 static EngineID last_engine
[2] = { INVALID_ENGINE
, INVALID_ENGINE
};
776 static char last_name
[2][64] = { "\0", "\0" };
778 const EngineID va
= *a
;
779 const EngineID vb
= *b
;
781 if (va
!= last_engine
[0]) {
784 GetString(last_name
[0], STR_ENGINE_NAME
, lastof(last_name
[0]));
787 if (vb
!= last_engine
[1]) {
790 GetString(last_name
[1], STR_ENGINE_NAME
, lastof(last_name
[1]));
793 int r
= strnatcmp(last_name
[0], last_name
[1]); // Sort by name (natural sorting).
795 /* Use EngineID to sort instead since we want consistent sorting */
796 if (r
== 0) return EngineNumberSorterWagon(a
, b
);
797 return _internal_sort_order_wagon
? -r
: r
;
801 * Determines order of wagons by purchase cost
802 * @param *a first engine to compare
803 * @param *b second engine to compare
804 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
806 static int CDECL
EngineCostSorterWagon(const EngineID
*a
, const EngineID
*b
)
808 Money va
= Engine::Get(*a
)->GetCost();
809 Money vb
= Engine::Get(*b
)->GetCost();
810 int r
= ClampToI32(va
- vb
);
812 /* Use EngineID to sort instead since we want consistent sorting */
813 if (r
== 0) return EngineNumberSorterWagon(a
, b
);
814 return _internal_sort_order_wagon
? -r
: r
;
818 * Determines order of wagons by speed
819 * @param *a first engine to compare
820 * @param *b second engine to compare
821 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
823 static int CDECL
EngineSpeedSorterWagon(const EngineID
*a
, const EngineID
*b
)
825 int va
= Engine::Get(*a
)->GetDisplayMaxSpeed();
826 int vb
= Engine::Get(*b
)->GetDisplayMaxSpeed();
829 /* Use EngineID to sort instead since we want consistent sorting */
830 if (r
== 0) return EngineNumberSorterWagon(a
, b
);
831 return _internal_sort_order_wagon
? -r
: r
;
835 * Determines order of wagons by running costs
836 * @param *a first engine to compare
837 * @param *b second engine to compare
838 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
840 static int CDECL
EngineRunningCostSorterWagon(const EngineID
*a
, const EngineID
*b
)
842 Money va
= Engine::Get(*a
)->GetRunningCost();
843 Money vb
= Engine::Get(*b
)->GetRunningCost();
844 int r
= ClampToI32(va
- vb
);
846 /* Use EngineID to sort instead since we want consistent sorting */
847 if (r
== 0) return EngineNumberSorterWagon(a
, b
);
848 return _internal_sort_order_wagon
? -r
: r
;
852 * Determines order of train wagons by capacity
853 * @param *a first engine to compare
854 * @param *b second engine to compare
855 * @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
857 static int CDECL
TrainEngineCapacitySorterWagon(const EngineID
*a
, const EngineID
*b
)
859 const RailVehicleInfo
*rvi_a
= RailVehInfo(*a
);
860 const RailVehicleInfo
*rvi_b
= RailVehInfo(*b
);
862 int va
= GetTotalCapacityOfArticulatedParts(*a
) * (rvi_a
->railveh_type
== RAILVEH_MULTIHEAD
? 2 : 1);
863 int vb
= GetTotalCapacityOfArticulatedParts(*b
) * (rvi_b
->railveh_type
== RAILVEH_MULTIHEAD
? 2 : 1);
866 /* Use EngineID to sort instead since we want consistent sorting */
867 if (r
== 0) return EngineNumberSorterWagon(a
, b
);
868 return _internal_sort_order_wagon
? -r
: r
;
871 EngList_SortTypeFunction
* const _engine_sort_functions
[][11] = { {
877 &EngineTractiveEffortSorter
,
878 &EngineIntroDateSorter
,
880 &EngineRunningCostSorter
,
881 &EnginePowerVsRunningCostSorter
,
882 &EngineReliabilitySorter
,
883 &TrainEngineCapacitySorter
,
890 &EngineTractiveEffortSorter
,
891 &EngineIntroDateSorter
,
893 &EngineRunningCostSorter
,
894 &EnginePowerVsRunningCostSorter
,
895 &EngineReliabilitySorter
,
896 &RoadVehEngineCapacitySorter
,
902 &EngineIntroDateSorter
,
904 &EngineRunningCostSorter
,
905 &EngineReliabilitySorter
,
906 &ShipEngineCapacitySorter
,
912 &EngineIntroDateSorter
,
914 &EngineRunningCostSorter
,
915 &EngineReliabilitySorter
,
916 &AircraftEngineCargoSorter
,
917 &AircraftRangeSorter
,
920 /** Dropdown menu strings for the vehicle sort criteria. */
921 const StringID _engine_sort_listing
[][12] = { {
923 STR_SORT_BY_ENGINE_ID
,
925 STR_SORT_BY_MAX_SPEED
,
927 STR_SORT_BY_TRACTIVE_EFFORT
,
928 STR_SORT_BY_INTRO_DATE
,
930 STR_SORT_BY_RUNNING_COST
,
931 STR_SORT_BY_POWER_VS_RUNNING_COST
,
932 STR_SORT_BY_RELIABILITY
,
933 STR_SORT_BY_CARGO_CAPACITY
,
937 STR_SORT_BY_ENGINE_ID
,
939 STR_SORT_BY_MAX_SPEED
,
941 STR_SORT_BY_TRACTIVE_EFFORT
,
942 STR_SORT_BY_INTRO_DATE
,
944 STR_SORT_BY_RUNNING_COST
,
945 STR_SORT_BY_POWER_VS_RUNNING_COST
,
946 STR_SORT_BY_RELIABILITY
,
947 STR_SORT_BY_CARGO_CAPACITY
,
951 STR_SORT_BY_ENGINE_ID
,
953 STR_SORT_BY_MAX_SPEED
,
954 STR_SORT_BY_INTRO_DATE
,
956 STR_SORT_BY_RUNNING_COST
,
957 STR_SORT_BY_RELIABILITY
,
958 STR_SORT_BY_CARGO_CAPACITY
,
962 STR_SORT_BY_ENGINE_ID
,
964 STR_SORT_BY_MAX_SPEED
,
965 STR_SORT_BY_INTRO_DATE
,
967 STR_SORT_BY_RUNNING_COST
,
968 STR_SORT_BY_RELIABILITY
,
969 STR_SORT_BY_CARGO_CAPACITY
,
974 static EngList_SortTypeFunction
* const _sorter_loco
[11] = {
976 &EngineNumberSorterLoco
,
977 &EngineCostSorterLoco
,
978 &EngineSpeedSorterLoco
,
979 &EnginePowerSorterLoco
,
980 &EngineTractiveEffortSorterLoco
,
981 &EngineIntroDateSorterLoco
,
982 &EngineNameSorterLoco
,
983 &EngineRunningCostSorterLoco
,
984 &EnginePowerVsRunningCostSorterLoco
,
985 &EngineReliabilitySorter
,
986 &TrainEngineCapacitySorter
989 static EngList_SortTypeFunction
* const _sorter_wagon
[7] = {
991 &EngineNumberSorterWagon
,
992 &EngineCostSorterWagon
,
993 &EngineSpeedSorterWagon
,
994 &EngineIntroDateSorterWagon
,
995 &EngineNameSorterWagon
,
996 &EngineRunningCostSorterWagon
,
997 &TrainEngineCapacitySorterWagon
1000 static const StringID _sort_listing_loco
[12] = {
1002 STR_SORT_BY_ENGINE_ID
,
1004 STR_SORT_BY_MAX_SPEED
,
1006 STR_SORT_BY_TRACTIVE_EFFORT
,
1007 STR_SORT_BY_INTRO_DATE
,
1009 STR_SORT_BY_RUNNING_COST
,
1010 STR_SORT_BY_POWER_VS_RUNNING_COST
,
1011 STR_SORT_BY_RELIABILITY
,
1012 STR_SORT_BY_CARGO_CAPACITY
,
1016 static const StringID _sort_listing_wagon
[8] = {
1018 STR_SORT_BY_ENGINE_ID
,
1020 STR_SORT_BY_MAX_SPEED
,
1021 STR_SORT_BY_INTRO_DATE
,
1023 STR_SORT_BY_RUNNING_COST
,
1024 STR_SORT_BY_CARGO_CAPACITY
,
1028 /** Cargo filter functions */
1029 static bool CDECL
CargoFilter(const EngineID
*eid
, const CargoID cid
)
1031 if (cid
== CF_ANY
) return true;
1032 uint32 refit_mask
= GetUnionOfArticulatedRefitMasks(*eid
, true) & _standard_cargo_mask
;
1033 return (cid
== CF_NONE
? refit_mask
== 0 : HasBit(refit_mask
, cid
));
1036 static GUIEngineList::FilterFunction
* const _filter_funcs
[] = {
1040 static int DrawCargoCapacityInfo(int left
, int right
, int y
, EngineID engine
)
1044 GetArticulatedVehicleCargoesAndRefits(engine
, &cap
, &refits
);
1046 for (CargoID c
= 0; c
< NUM_CARGO
; c
++) {
1047 if (cap
[c
] == 0) continue;
1050 SetDParam(1, cap
[c
]);
1051 SetDParam(2, HasBit(refits
, c
) ? STR_PURCHASE_INFO_REFITTABLE
: STR_EMPTY
);
1052 DrawString(left
, right
, y
, STR_PURCHASE_INFO_CAPACITY
);
1053 y
+= FONT_HEIGHT_NORMAL
;
1059 /* Draw rail wagon specific details */
1060 static int DrawRailWagonPurchaseInfo(int left
, int right
, int y
, EngineID engine_number
, const RailVehicleInfo
*rvi
)
1062 const Engine
*e
= Engine::Get(engine_number
);
1065 SetDParam(0, e
->GetCost());
1066 DrawString(left
, right
, y
, STR_PURCHASE_INFO_COST
);
1067 y
+= FONT_HEIGHT_NORMAL
;
1069 /* Wagon weight - (including cargo) */
1070 uint weight
= e
->GetDisplayWeight();
1071 SetDParam(0, weight
);
1072 uint cargo_weight
= (e
->CanCarryCargo() ? CargoSpec::Get(e
->GetDefaultCargoType())->weight
* GetTotalCapacityOfArticulatedParts(engine_number
) / 16 : 0);
1073 SetDParam(1, cargo_weight
+ weight
);
1074 DrawString(left
, right
, y
, STR_PURCHASE_INFO_WEIGHT_CWEIGHT
);
1075 y
+= FONT_HEIGHT_NORMAL
;
1077 /* Wagon speed limit, displayed if above zero */
1078 if (_settings_game
.vehicle
.wagon_speed_limits
) {
1079 uint max_speed
= e
->GetDisplayMaxSpeed();
1080 if (max_speed
> 0) {
1081 SetDParam(0, max_speed
);
1082 DrawString(left
, right
, y
, STR_PURCHASE_INFO_SPEED
);
1083 y
+= FONT_HEIGHT_NORMAL
;
1088 if (rvi
->running_cost_class
!= INVALID_PRICE
) {
1089 SetDParam(0, e
->GetDisplayRunningCost());
1090 DrawString(left
, right
, y
, STR_PURCHASE_INFO_RUNNINGCOST
);
1091 y
+= FONT_HEIGHT_NORMAL
;
1097 /* Draw locomotive specific details */
1098 static int DrawRailEnginePurchaseInfo(int left
, int right
, int y
, EngineID engine_number
, const RailVehicleInfo
*rvi
)
1100 const Engine
*e
= Engine::Get(engine_number
);
1102 /* Purchase Cost - Engine weight */
1103 SetDParam(0, e
->GetCost());
1104 SetDParam(1, e
->GetDisplayWeight());
1105 DrawString(left
, right
, y
, STR_PURCHASE_INFO_COST_WEIGHT
);
1106 y
+= FONT_HEIGHT_NORMAL
;
1108 /* Max speed - Engine power */
1109 SetDParam(0, e
->GetDisplayMaxSpeed());
1110 SetDParam(1, e
->GetPower());
1111 DrawString(left
, right
, y
, STR_PURCHASE_INFO_SPEED_POWER
);
1112 y
+= FONT_HEIGHT_NORMAL
;
1114 /* Max tractive effort - not applicable if old acceleration or maglev */
1115 if (_settings_game
.vehicle
.train_acceleration_model
!= AM_ORIGINAL
&& GetRailTypeInfo(rvi
->railtype
)->acceleration_type
!= 2) {
1116 SetDParam(0, e
->GetDisplayMaxTractiveEffort());
1117 DrawString(left
, right
, y
, STR_PURCHASE_INFO_MAX_TE
);
1118 y
+= FONT_HEIGHT_NORMAL
;
1122 if (rvi
->running_cost_class
!= INVALID_PRICE
) {
1123 SetDParam(0, e
->GetDisplayRunningCost());
1124 DrawString(left
, right
, y
, STR_PURCHASE_INFO_RUNNINGCOST
);
1125 y
+= FONT_HEIGHT_NORMAL
;
1128 /* Powered wagons power - Powered wagons extra weight */
1129 if (rvi
->pow_wag_power
!= 0) {
1130 SetDParam(0, rvi
->pow_wag_power
);
1131 SetDParam(1, rvi
->pow_wag_weight
);
1132 DrawString(left
, right
, y
, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT
);
1133 y
+= FONT_HEIGHT_NORMAL
;
1139 /* Draw road vehicle specific details */
1140 static int DrawRoadVehPurchaseInfo(int left
, int right
, int y
, EngineID engine_number
)
1142 const Engine
*e
= Engine::Get(engine_number
);
1144 if (_settings_game
.vehicle
.roadveh_acceleration_model
!= AM_ORIGINAL
) {
1146 SetDParam(0, e
->GetCost());
1147 DrawString(left
, right
, y
, STR_PURCHASE_INFO_COST
);
1148 y
+= FONT_HEIGHT_NORMAL
;
1150 /* Road vehicle weight - (including cargo) */
1151 int16 weight
= e
->GetDisplayWeight();
1152 SetDParam(0, weight
);
1153 uint cargo_weight
= (e
->CanCarryCargo() ? CargoSpec::Get(e
->GetDefaultCargoType())->weight
* GetTotalCapacityOfArticulatedParts(engine_number
) / 16 : 0);
1154 SetDParam(1, cargo_weight
+ weight
);
1155 DrawString(left
, right
, y
, STR_PURCHASE_INFO_WEIGHT_CWEIGHT
);
1156 y
+= FONT_HEIGHT_NORMAL
;
1158 /* Max speed - Engine power */
1159 SetDParam(0, e
->GetDisplayMaxSpeed());
1160 SetDParam(1, e
->GetPower());
1161 DrawString(left
, right
, y
, STR_PURCHASE_INFO_SPEED_POWER
);
1162 y
+= FONT_HEIGHT_NORMAL
;
1164 /* Max tractive effort */
1165 SetDParam(0, e
->GetDisplayMaxTractiveEffort());
1166 DrawString(left
, right
, y
, STR_PURCHASE_INFO_MAX_TE
);
1167 y
+= FONT_HEIGHT_NORMAL
;
1169 /* Purchase cost - Max speed */
1170 SetDParam(0, e
->GetCost());
1171 SetDParam(1, e
->GetDisplayMaxSpeed());
1172 DrawString(left
, right
, y
, STR_PURCHASE_INFO_COST_SPEED
);
1173 y
+= FONT_HEIGHT_NORMAL
;
1177 SetDParam(0, e
->GetDisplayRunningCost());
1178 DrawString(left
, right
, y
, STR_PURCHASE_INFO_RUNNINGCOST
);
1179 y
+= FONT_HEIGHT_NORMAL
;
1184 /* Draw ship specific details */
1185 static int DrawShipPurchaseInfo(int left
, int right
, int y
, EngineID engine_number
, bool refittable
)
1187 const Engine
*e
= Engine::Get(engine_number
);
1189 /* Purchase cost - Max speed */
1190 uint raw_speed
= e
->GetDisplayMaxSpeed();
1191 uint ocean_speed
= e
->u
.ship
.ApplyWaterClassSpeedFrac(raw_speed
, true);
1192 uint canal_speed
= e
->u
.ship
.ApplyWaterClassSpeedFrac(raw_speed
, false);
1194 SetDParam(0, e
->GetCost());
1195 if (ocean_speed
== canal_speed
) {
1196 SetDParam(1, ocean_speed
);
1197 DrawString(left
, right
, y
, STR_PURCHASE_INFO_COST_SPEED
);
1198 y
+= FONT_HEIGHT_NORMAL
;
1200 DrawString(left
, right
, y
, STR_PURCHASE_INFO_COST
);
1201 y
+= FONT_HEIGHT_NORMAL
;
1203 SetDParam(0, ocean_speed
);
1204 DrawString(left
, right
, y
, STR_PURCHASE_INFO_SPEED_OCEAN
);
1205 y
+= FONT_HEIGHT_NORMAL
;
1207 SetDParam(0, canal_speed
);
1208 DrawString(left
, right
, y
, STR_PURCHASE_INFO_SPEED_CANAL
);
1209 y
+= FONT_HEIGHT_NORMAL
;
1212 /* Cargo type + capacity */
1213 SetDParam(0, e
->GetDefaultCargoType());
1214 SetDParam(1, e
->GetDisplayDefaultCapacity());
1215 SetDParam(2, refittable
? STR_PURCHASE_INFO_REFITTABLE
: STR_EMPTY
);
1216 DrawString(left
, right
, y
, STR_PURCHASE_INFO_CAPACITY
);
1217 y
+= FONT_HEIGHT_NORMAL
;
1220 SetDParam(0, e
->GetDisplayRunningCost());
1221 DrawString(left
, right
, y
, STR_PURCHASE_INFO_RUNNINGCOST
);
1222 y
+= FONT_HEIGHT_NORMAL
;
1228 * Draw aircraft specific details in the buy window.
1229 * @param left Left edge of the window to draw in.
1230 * @param right Right edge of the window to draw in.
1231 * @param y Top of the area to draw in.
1232 * @param engine_number Engine to display.
1233 * @param refittable If set, the aircraft can be refitted.
1234 * @return Bottom of the used area.
1236 static int DrawAircraftPurchaseInfo(int left
, int right
, int y
, EngineID engine_number
, bool refittable
)
1238 const Engine
*e
= Engine::Get(engine_number
);
1239 CargoID cargo
= e
->GetDefaultCargoType();
1241 /* Purchase cost - Max speed */
1242 SetDParam(0, e
->GetCost());
1243 SetDParam(1, e
->GetDisplayMaxSpeed());
1244 DrawString(left
, right
, y
, STR_PURCHASE_INFO_COST_SPEED
);
1245 y
+= FONT_HEIGHT_NORMAL
;
1247 /* Cargo capacity */
1248 uint16 mail_capacity
;
1249 uint capacity
= e
->GetDisplayDefaultCapacity(&mail_capacity
);
1250 if (mail_capacity
> 0) {
1251 SetDParam(0, cargo
);
1252 SetDParam(1, capacity
);
1253 SetDParam(2, CT_MAIL
);
1254 SetDParam(3, mail_capacity
);
1255 DrawString(left
, right
, y
, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY
);
1257 /* Note, if the default capacity is selected by the refit capacity
1258 * callback, then the capacity shown is likely to be incorrect. */
1259 SetDParam(0, cargo
);
1260 SetDParam(1, capacity
);
1261 SetDParam(2, refittable
? STR_PURCHASE_INFO_REFITTABLE
: STR_EMPTY
);
1262 DrawString(left
, right
, y
, STR_PURCHASE_INFO_CAPACITY
);
1264 y
+= FONT_HEIGHT_NORMAL
;
1267 SetDParam(0, e
->GetDisplayRunningCost());
1268 DrawString(left
, right
, y
, STR_PURCHASE_INFO_RUNNINGCOST
);
1269 y
+= FONT_HEIGHT_NORMAL
;
1272 SetDParam(0, e
->GetAircraftTypeText());
1273 DrawString(left
, right
, y
, STR_PURCHASE_INFO_AIRCRAFT_TYPE
);
1274 y
+= FONT_HEIGHT_NORMAL
;
1276 /* Aircraft range, if available. */
1277 uint16 range
= e
->GetRange();
1279 SetDParam(0, range
);
1280 DrawString(left
, right
, y
, STR_PURCHASE_INFO_AIRCRAFT_RANGE
);
1281 y
+= FONT_HEIGHT_NORMAL
;
1288 * Display additional text from NewGRF in the purchase information window
1289 * @param left Left border of text bounding box
1290 * @param right Right border of text bounding box
1291 * @param y Top border of text bounding box
1292 * @param engine Engine to query the additional purchase information for
1293 * @return Bottom border of text bounding box
1295 static uint
ShowAdditionalText(int left
, int right
, int y
, EngineID engine
)
1297 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT
, 0, 0, engine
, nullptr);
1298 if (callback
== CALLBACK_FAILED
|| callback
== 0x400) return y
;
1299 const GRFFile
*grffile
= Engine::Get(engine
)->GetGRF();
1300 if (callback
> 0x400) {
1301 ErrorUnknownCallbackResult(grffile
->grfid
, CBID_VEHICLE_ADDITIONAL_TEXT
, callback
);
1305 StartTextRefStackUsage(grffile
, 6);
1306 uint result
= DrawStringMultiLine(left
, right
, y
, INT32_MAX
, GetGRFStringID(grffile
->grfid
, 0xD000 + callback
), TC_BLACK
);
1307 StopTextRefStackUsage();
1312 * Draw the purchase info details of a vehicle at a given location.
1313 * @param left,right,y location where to draw the info
1314 * @param engine_number the engine of which to draw the info of
1315 * @return y after drawing all the text
1317 int DrawVehiclePurchaseInfo(int left
, int right
, int y
, EngineID engine_number
)
1319 const Engine
*e
= Engine::Get(engine_number
);
1321 ConvertDateToYMD(e
->intro_date
, &ymd
);
1322 bool refittable
= IsArticulatedVehicleRefittable(engine_number
);
1323 bool articulated_cargo
= false;
1326 default: NOT_REACHED();
1328 if (e
->u
.rail
.railveh_type
== RAILVEH_WAGON
) {
1329 y
= DrawRailWagonPurchaseInfo(left
, right
, y
, engine_number
, &e
->u
.rail
);
1331 y
= DrawRailEnginePurchaseInfo(left
, right
, y
, engine_number
, &e
->u
.rail
);
1333 articulated_cargo
= true;
1337 y
= DrawRoadVehPurchaseInfo(left
, right
, y
, engine_number
);
1338 articulated_cargo
= true;
1342 y
= DrawShipPurchaseInfo(left
, right
, y
, engine_number
, refittable
);
1346 y
= DrawAircraftPurchaseInfo(left
, right
, y
, engine_number
, refittable
);
1350 if (articulated_cargo
) {
1351 /* Cargo type + capacity, or N/A */
1352 int new_y
= DrawCargoCapacityInfo(left
, right
, y
, engine_number
);
1355 SetDParam(0, CT_INVALID
);
1356 SetDParam(2, STR_EMPTY
);
1357 DrawString(left
, right
, y
, STR_PURCHASE_INFO_CAPACITY
);
1358 y
+= FONT_HEIGHT_NORMAL
;
1364 /* Draw details that apply to all types except rail wagons. */
1365 if (e
->type
!= VEH_TRAIN
|| e
->u
.rail
.railveh_type
!= RAILVEH_WAGON
) {
1366 /* Design date - Life length */
1367 SetDParam(0, ymd
.year
);
1368 SetDParam(1, e
->GetLifeLengthInDays() / DAYS_IN_LEAP_YEAR
);
1369 DrawString(left
, right
, y
, STR_PURCHASE_INFO_DESIGNED_LIFE
);
1370 y
+= FONT_HEIGHT_NORMAL
;
1373 SetDParam(0, ToPercent16(e
->reliability
));
1374 DrawString(left
, right
, y
, STR_PURCHASE_INFO_RELIABILITY
);
1375 y
+= FONT_HEIGHT_NORMAL
;
1378 if (refittable
) y
= ShowRefitOptionsList(left
, right
, y
, engine_number
);
1380 /* Additional text from NewGRF */
1381 y
= ShowAdditionalText(left
, right
, y
, engine_number
);
1387 * Engine drawing loop
1388 * @param type Type of vehicle (VEH_*)
1389 * @param l The left most location of the list
1390 * @param r The right most location of the list
1391 * @param y The top most location of the list
1392 * @param eng_list What engines to draw
1393 * @param min where to start in the list
1394 * @param max where in the list to end
1395 * @param selected_id what engine to highlight as selected, if any
1396 * @param show_count Whether to show the amount of engines or not
1397 * @param selected_group the group to list the engines of
1399 void DrawEngineList(VehicleType type
, int l
, int r
, int y
, const GUIEngineList
*eng_list
, uint16 min
, uint16 max
, EngineID selected_id
, bool show_count
, GroupID selected_group
)
1401 static const int sprite_y_offsets
[] = { -1, -1, -2, -2 };
1403 /* Obligatory sanity checks! */
1404 assert(max
<= eng_list
->Length());
1406 bool rtl
= _current_text_dir
== TD_RTL
;
1407 int step_size
= GetEngineListHeight(type
);
1408 int sprite_left
= GetVehicleImageCellSize(type
, EIT_PURCHASE
).extend_left
;
1409 int sprite_right
= GetVehicleImageCellSize(type
, EIT_PURCHASE
).extend_right
;
1410 int sprite_width
= sprite_left
+ sprite_right
;
1412 int sprite_x
= rtl
? r
- sprite_right
- 1 : l
+ sprite_left
+ 1;
1413 int sprite_y_offset
= sprite_y_offsets
[type
] + step_size
/ 2;
1415 Dimension replace_icon
= {0, 0};
1416 int count_width
= 0;
1418 replace_icon
= GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE
);
1419 SetDParamMaxDigits(0, 3, FS_SMALL
);
1420 count_width
= GetStringBoundingBox(STR_TINY_BLACK_COMA
).width
;
1423 int text_left
= l
+ (rtl
? WD_FRAMERECT_LEFT
+ replace_icon
.width
+ 8 + count_width
: sprite_width
+ WD_FRAMETEXT_LEFT
);
1424 int text_right
= r
- (rtl
? sprite_width
+ WD_FRAMETEXT_RIGHT
: WD_FRAMERECT_RIGHT
+ replace_icon
.width
+ 8 + count_width
);
1425 int replace_icon_left
= rtl
? l
+ WD_FRAMERECT_LEFT
: r
- WD_FRAMERECT_RIGHT
- replace_icon
.width
;
1427 int count_right
= rtl
? text_left
: r
- WD_FRAMERECT_RIGHT
- replace_icon
.width
- 8;
1429 int normal_text_y_offset
= (step_size
- FONT_HEIGHT_NORMAL
) / 2;
1430 int small_text_y_offset
= step_size
- FONT_HEIGHT_SMALL
- WD_FRAMERECT_BOTTOM
- 1;
1431 int replace_icon_y_offset
= (step_size
- replace_icon
.height
) / 2 - 1;
1433 for (; min
< max
; min
++, y
+= step_size
) {
1434 const EngineID engine
= (*eng_list
)[min
];
1435 /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
1436 const uint num_engines
= GetGroupNumEngines(_local_company
, selected_group
, engine
);
1438 const Engine
*e
= Engine::Get(engine
);
1439 bool hidden
= HasBit(e
->company_hidden
, _local_company
);
1440 StringID str
= hidden
? STR_HIDDEN_ENGINE_NAME
: STR_ENGINE_NAME
;
1441 TextColour tc
= (engine
== selected_id
) ? TC_WHITE
: (TC_NO_SHADE
| (hidden
? TC_GREY
: TC_BLACK
));
1443 SetDParam(0, engine
);
1444 DrawString(text_left
, text_right
, y
+ normal_text_y_offset
, str
, tc
);
1445 DrawVehicleEngine(l
, r
, sprite_x
, y
+ sprite_y_offset
, engine
, (show_count
&& num_engines
== 0) ? PALETTE_CRASH
: GetEnginePalette(engine
, _local_company
), EIT_PURCHASE
);
1447 SetDParam(0, num_engines
);
1448 DrawString(count_left
, count_right
, y
+ small_text_y_offset
, STR_TINY_BLACK_COMA
, TC_FROMSTRING
, SA_RIGHT
| SA_FORCE
);
1449 if (EngineHasReplacementForCompany(Company::Get(_local_company
), engine
, selected_group
)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE
, num_engines
== 0 ? PALETTE_CRASH
: PAL_NONE
, replace_icon_left
, y
+ replace_icon_y_offset
);
1455 * Display the dropdown for the vehicle sort criteria.
1456 * @param w Parent window (holds the dropdown button).
1457 * @param vehicle_type %Vehicle type being sorted.
1458 * @param selected Currently selected sort criterium.
1459 * @param button Widget button.
1461 void DisplayVehicleSortDropDown(Window
*w
, VehicleType vehicle_type
, int selected
, int button
)
1463 uint32 hidden_mask
= 0;
1464 /* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
1465 if (vehicle_type
== VEH_ROAD
&& _settings_game
.vehicle
.roadveh_acceleration_model
== AM_ORIGINAL
) {
1466 SetBit(hidden_mask
, 3); // power
1467 SetBit(hidden_mask
, 4); // tractive effort
1468 SetBit(hidden_mask
, 8); // power by running costs
1470 /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
1471 if (vehicle_type
== VEH_TRAIN
&& _settings_game
.vehicle
.train_acceleration_model
== AM_ORIGINAL
) {
1472 SetBit(hidden_mask
, 4); // tractive effort
1474 ShowDropDownMenu(w
, _engine_sort_listing
[vehicle_type
], selected
, button
, 0, hidden_mask
);
1477 /** GUI for building vehicles. */
1478 struct BuildVehicleWindow
: Window
{
1479 VehicleType vehicle_type
; ///< Type of vehicles shown in the window.
1481 RailTypeByte railtype
; ///< Rail type to show, or #RAILTYPE_END.
1482 RoadTypes roadtypes
; ///< Road type to show, or #ROADTYPES_ALL.
1483 } filter
; ///< Filter to apply.
1484 bool descending_sort_order
; ///< Sort direction, @see _engine_sort_direction
1485 byte sort_criteria
; ///< Current sort criterium.
1486 bool show_hidden_engines
; ///< State of the 'show hidden engines' button.
1487 bool listview_mode
; ///< If set, only display the available vehicles and do not show a 'build' button.
1488 EngineID sel_engine
; ///< Currently selected engine, or #INVALID_ENGINE
1489 EngineID rename_engine
; ///< Engine being renamed.
1490 GUIEngineList eng_list
;
1491 CargoID cargo_filter
[NUM_CARGO
+ 2]; ///< Available cargo filters; CargoID or CF_ANY or CF_NONE
1492 StringID cargo_filter_texts
[NUM_CARGO
+ 3]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID
1493 byte cargo_filter_criteria
; ///< Selected cargo filter
1494 int details_height
; ///< Minimal needed height of the details panels (found so far).
1497 BuildVehicleWindow(WindowDesc
*desc
, TileIndex tile
, VehicleType type
) : Window(desc
)
1499 this->vehicle_type
= type
;
1500 this->window_number
= tile
== INVALID_TILE
? (int)type
: tile
;
1502 this->sel_engine
= INVALID_ENGINE
;
1504 this->sort_criteria
= _engine_sort_last_criteria
[type
];
1505 this->descending_sort_order
= _engine_sort_last_order
[type
];
1506 this->show_hidden_engines
= _engine_sort_show_hidden_engines
[type
];
1509 default: NOT_REACHED();
1511 this->filter
.railtype
= (tile
== INVALID_TILE
) ? RAILTYPE_END
: GetRailType(tile
);
1514 this->filter
.roadtypes
= (tile
== INVALID_TILE
) ? ROADTYPES_ALL
: GetRoadTypes(tile
);
1520 this->listview_mode
= (this->window_number
<= VEH_END
);
1522 this->CreateNestedTree();
1524 this->vscroll
= this->GetScrollbar(WID_BV_SCROLLBAR
);
1526 /* If we are just viewing the list of vehicles, we do not need the Build button.
1527 * So we just hide it, and enlarge the Rename button by the now vacant place. */
1528 if (this->listview_mode
) this->GetWidget
<NWidgetStacked
>(WID_BV_BUILD_SEL
)->SetDisplayedPlane(SZSP_NONE
);
1530 NWidgetCore
*widget
= this->GetWidget
<NWidgetCore
>(WID_BV_LIST
);
1531 widget
->tool_tip
= STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP
+ type
;
1533 widget
= this->GetWidget
<NWidgetCore
>(WID_BV_SHOW_HIDE
);
1534 widget
->tool_tip
= STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP
+ type
;
1536 widget
= this->GetWidget
<NWidgetCore
>(WID_BV_BUILD
);
1537 widget
->widget_data
= STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON
+ type
;
1538 widget
->tool_tip
= STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP
+ type
;
1540 widget
= this->GetWidget
<NWidgetCore
>(WID_BV_RENAME
);
1541 widget
->widget_data
= STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON
+ type
;
1542 widget
->tool_tip
= STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP
+ type
;
1544 widget
= this->GetWidget
<NWidgetCore
>(WID_BV_SHOW_HIDDEN_ENGINES
);
1545 widget
->widget_data
= STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN
+ type
;
1546 widget
->tool_tip
= STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP
+ type
;
1547 widget
->SetLowered(this->show_hidden_engines
);
1549 this->details_height
= ((this->vehicle_type
== VEH_TRAIN
) ? 10 : 9) * FONT_HEIGHT_NORMAL
+ WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
;
1551 this->FinishInitNested(tile
== INVALID_TILE
? (int)type
: tile
);
1553 this->owner
= (tile
!= INVALID_TILE
) ? GetTileOwner(tile
) : _local_company
;
1555 this->eng_list
.ForceRebuild();
1556 this->GenerateBuildList(); // generate the list, since we need it in the next line
1557 /* Select the first engine in the list as default when opening the window */
1558 if (this->eng_list
.Length() > 0) this->sel_engine
= this->eng_list
[0];
1561 /** Populate the filter list and set the cargo filter criteria. */
1562 void SetCargoFilterArray()
1564 uint filter_items
= 0;
1566 /* Add item for disabling filtering. */
1567 this->cargo_filter
[filter_items
] = CF_ANY
;
1568 this->cargo_filter_texts
[filter_items
] = STR_PURCHASE_INFO_ALL_TYPES
;
1571 /* Add item for vehicles not carrying anything, e.g. train engines.
1572 * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
1573 if (this->vehicle_type
== VEH_TRAIN
) {
1574 this->cargo_filter
[filter_items
] = CF_NONE
;
1575 this->cargo_filter_texts
[filter_items
] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE
;
1579 /* Collect available cargo types for filtering. */
1580 const CargoSpec
*cs
;
1581 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs
) {
1582 this->cargo_filter
[filter_items
] = cs
->Index();
1583 this->cargo_filter_texts
[filter_items
] = cs
->name
;
1587 /* Terminate the filter list. */
1588 this->cargo_filter_texts
[filter_items
] = INVALID_STRING_ID
;
1590 /* If not found, the cargo criteria will be set to all cargoes. */
1591 this->cargo_filter_criteria
= 0;
1593 /* Find the last cargo filter criteria. */
1594 for (uint i
= 0; i
< filter_items
; i
++) {
1595 if (this->cargo_filter
[i
] == _engine_sort_last_cargo_criteria
[this->vehicle_type
]) {
1596 this->cargo_filter_criteria
= i
;
1601 this->eng_list
.SetFilterFuncs(_filter_funcs
);
1602 this->eng_list
.SetFilterState(this->cargo_filter
[this->cargo_filter_criteria
] != CF_ANY
);
1607 this->SetCargoFilterArray();
1610 /** Filter the engine list against the currently selected cargo filter */
1611 void FilterEngineList()
1613 this->eng_list
.Filter(this->cargo_filter
[this->cargo_filter_criteria
]);
1614 if (0 == this->eng_list
.Length()) { // no engine passed through the filter, invalidate the previously selected engine
1615 this->sel_engine
= INVALID_ENGINE
;
1616 } else if (!this->eng_list
.Contains(this->sel_engine
)) { // previously selected engine didn't pass the filter, select the first engine of the list
1617 this->sel_engine
= this->eng_list
[0];
1621 /** Filter a single engine */
1622 bool FilterSingleEngine(EngineID eid
)
1624 CargoID filter_type
= this->cargo_filter
[this->cargo_filter_criteria
];
1625 return (filter_type
== CF_ANY
|| CargoFilter(&eid
, filter_type
));
1628 /* Figure out what train EngineIDs to put in the list */
1629 void GenerateBuildTrainList()
1631 EngineID sel_id
= INVALID_ENGINE
;
1632 int num_engines
= 0;
1635 this->filter
.railtype
= (this->listview_mode
) ? RAILTYPE_END
: GetRailType(this->window_number
);
1637 this->eng_list
.Clear();
1639 /* Make list of all available train engines and wagons.
1640 * Also check to see if the previously selected engine is still available,
1641 * and if not, reset selection to INVALID_ENGINE. This could be the case
1642 * when engines become obsolete and are removed */
1644 FOR_ALL_ENGINES_OF_TYPE(e
, VEH_TRAIN
) {
1645 if (!this->show_hidden_engines
&& e
->IsHidden(_local_company
)) continue;
1646 EngineID eid
= e
->index
;
1647 const RailVehicleInfo
*rvi
= &e
->u
.rail
;
1649 if (this->filter
.railtype
!= RAILTYPE_END
&& !HasPowerOnRail(rvi
->railtype
, this->filter
.railtype
)) continue;
1650 if (!IsEngineBuildable(eid
, VEH_TRAIN
, _local_company
)) continue;
1652 /* Filter now! So num_engines and num_wagons is valid */
1653 if (!FilterSingleEngine(eid
)) continue;
1655 *this->eng_list
.Append() = eid
;
1657 if (rvi
->railveh_type
!= RAILVEH_WAGON
) {
1663 if (eid
== this->sel_engine
) sel_id
= eid
;
1666 this->sel_engine
= sel_id
;
1668 /* make engines first, and then wagons, sorted by selected sort_criteria */
1669 _engine_sort_direction
= false;
1670 EngList_Sort(&this->eng_list
, TrainEnginesThenWagonsSorter
);
1672 /* and then sort engines */
1673 _engine_sort_direction
= this->descending_sort_order
;
1674 EngList_SortPartial(&this->eng_list
, _engine_sort_functions
[0][this->sort_criteria
], 0, num_engines
);
1676 /* and finally sort wagons */
1677 EngList_SortPartial(&this->eng_list
, _engine_sort_functions
[0][this->sort_criteria
], num_engines
, num_wagons
);
1680 /* Figure out what road vehicle EngineIDs to put in the list */
1681 void GenerateBuildRoadVehList()
1683 EngineID sel_id
= INVALID_ENGINE
;
1685 this->eng_list
.Clear();
1688 FOR_ALL_ENGINES_OF_TYPE(e
, VEH_ROAD
) {
1689 if (!this->show_hidden_engines
&& e
->IsHidden(_local_company
)) continue;
1690 EngineID eid
= e
->index
;
1691 if (!IsEngineBuildable(eid
, VEH_ROAD
, _local_company
)) continue;
1692 if (!HasBit(this->filter
.roadtypes
, HasBit(EngInfo(eid
)->misc_flags
, EF_ROAD_TRAM
) ? ROADTYPE_TRAM
: ROADTYPE_ROAD
)) continue;
1693 *this->eng_list
.Append() = eid
;
1695 if (eid
== this->sel_engine
) sel_id
= eid
;
1697 this->sel_engine
= sel_id
;
1700 /* Figure out what ship EngineIDs to put in the list */
1701 void GenerateBuildShipList()
1703 EngineID sel_id
= INVALID_ENGINE
;
1704 this->eng_list
.Clear();
1707 FOR_ALL_ENGINES_OF_TYPE(e
, VEH_SHIP
) {
1708 if (!this->show_hidden_engines
&& e
->IsHidden(_local_company
)) continue;
1709 EngineID eid
= e
->index
;
1710 if (!IsEngineBuildable(eid
, VEH_SHIP
, _local_company
)) continue;
1711 *this->eng_list
.Append() = eid
;
1713 if (eid
== this->sel_engine
) sel_id
= eid
;
1715 this->sel_engine
= sel_id
;
1718 /* Figure out what aircraft EngineIDs to put in the list */
1719 void GenerateBuildAircraftList()
1721 EngineID sel_id
= INVALID_ENGINE
;
1723 this->eng_list
.Clear();
1725 const Station
*st
= this->listview_mode
? nullptr : Station::GetByTile(this->window_number
);
1727 /* Make list of all available planes.
1728 * Also check to see if the previously selected plane is still available,
1729 * and if not, reset selection to INVALID_ENGINE. This could be the case
1730 * when planes become obsolete and are removed */
1732 FOR_ALL_ENGINES_OF_TYPE(e
, VEH_AIRCRAFT
) {
1733 if (!this->show_hidden_engines
&& e
->IsHidden(_local_company
)) continue;
1734 EngineID eid
= e
->index
;
1735 if (!IsEngineBuildable(eid
, VEH_AIRCRAFT
, _local_company
)) continue;
1736 /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
1737 if (!this->listview_mode
&& !CanVehicleUseStation(eid
, st
)) continue;
1739 *this->eng_list
.Append() = eid
;
1740 if (eid
== this->sel_engine
) sel_id
= eid
;
1743 this->sel_engine
= sel_id
;
1746 /* Generate the list of vehicles */
1747 void GenerateBuildList()
1749 if (!this->eng_list
.NeedRebuild()) return;
1750 switch (this->vehicle_type
) {
1751 default: NOT_REACHED();
1753 this->GenerateBuildTrainList();
1754 this->eng_list
.Compact();
1755 this->eng_list
.RebuildDone();
1756 return; // trains should not reach the last sorting
1758 this->GenerateBuildRoadVehList();
1761 this->GenerateBuildShipList();
1764 this->GenerateBuildAircraftList();
1768 this->FilterEngineList();
1770 _engine_sort_direction
= this->descending_sort_order
;
1771 EngList_Sort(&this->eng_list
, _engine_sort_functions
[this->vehicle_type
][this->sort_criteria
]);
1773 this->eng_list
.Compact();
1774 this->eng_list
.RebuildDone();
1777 void OnClick(Point pt
, int widget
, int click_count
)
1780 case WID_BV_SORT_ASCENDING_DESCENDING
:
1781 this->descending_sort_order
^= true;
1782 _engine_sort_last_order
[this->vehicle_type
] = this->descending_sort_order
;
1783 this->eng_list
.ForceRebuild();
1787 case WID_BV_SHOW_HIDDEN_ENGINES
:
1788 this->show_hidden_engines
^= true;
1789 _engine_sort_show_hidden_engines
[this->vehicle_type
] = this->show_hidden_engines
;
1790 this->eng_list
.ForceRebuild();
1791 this->SetWidgetLoweredState(widget
, this->show_hidden_engines
);
1796 uint i
= this->vscroll
->GetScrolledRowFromWidget(pt
.y
, this, WID_BV_LIST
);
1797 size_t num_items
= this->eng_list
.Length();
1798 this->sel_engine
= (i
< num_items
) ? this->eng_list
[i
] : INVALID_ENGINE
;
1800 if (_ctrl_pressed
) {
1801 this->OnClick(pt
, WID_BV_SHOW_HIDE
, 1);
1802 } else if (click_count
> 1 && !this->listview_mode
) {
1803 this->OnClick(pt
, WID_BV_BUILD
, 1);
1808 case WID_BV_SORT_DROPDOWN
: // Select sorting criteria dropdown menu
1809 DisplayVehicleSortDropDown(this, this->vehicle_type
, this->sort_criteria
, WID_BV_SORT_DROPDOWN
);
1812 case WID_BV_CARGO_FILTER_DROPDOWN
: // Select cargo filtering criteria dropdown menu
1813 ShowDropDownMenu(this, this->cargo_filter_texts
, this->cargo_filter_criteria
, WID_BV_CARGO_FILTER_DROPDOWN
, 0, 0);
1816 case WID_BV_SHOW_HIDE
: {
1817 const Engine
*e
= (this->sel_engine
== INVALID_ENGINE
) ? nullptr : Engine::GetIfValid(this->sel_engine
);
1819 DoCommandP(0, 0, this->sel_engine
| (e
->IsHidden(_current_company
) ? 0 : (1u << 31)), CMD_SET_VEHICLE_VISIBILITY
);
1824 case WID_BV_BUILD
: {
1825 EngineID sel_eng
= this->sel_engine
;
1826 if (sel_eng
!= INVALID_ENGINE
) {
1827 CommandCallback
*callback
= (this->vehicle_type
== VEH_TRAIN
&& RailVehInfo(sel_eng
)->railveh_type
== RAILVEH_WAGON
) ? CcBuildWagon
: CcBuildPrimaryVehicle
;
1828 DoCommandP(this->window_number
, sel_eng
, 0, GetCmdBuildVeh(this->vehicle_type
), callback
);
1833 case WID_BV_RENAME
: {
1834 EngineID sel_eng
= this->sel_engine
;
1835 if (sel_eng
!= INVALID_ENGINE
) {
1836 this->rename_engine
= sel_eng
;
1837 SetDParam(0, sel_eng
);
1838 ShowQueryString(STR_ENGINE_NAME
, STR_QUERY_RENAME_TRAIN_TYPE_CAPTION
+ this->vehicle_type
, MAX_LENGTH_ENGINE_NAME_CHARS
, this, CS_ALPHANUMERAL
, QSF_ENABLE_DEFAULT
| QSF_LEN_IN_CHARS
);
1846 * Some data on this window has become invalid.
1847 * @param data Information about the changed data.
1848 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
1850 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
1852 if (!gui_scope
) return;
1853 /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
1854 if (this->vehicle_type
== VEH_ROAD
&&
1855 _settings_game
.vehicle
.roadveh_acceleration_model
== AM_ORIGINAL
&&
1856 this->sort_criteria
> 7) {
1857 this->sort_criteria
= 0;
1858 _engine_sort_last_criteria
[VEH_ROAD
] = 0;
1860 this->eng_list
.ForceRebuild();
1863 virtual void SetStringParameters(int widget
) const
1866 case WID_BV_CAPTION
:
1867 if (this->vehicle_type
== VEH_TRAIN
&& !this->listview_mode
) {
1868 const RailtypeInfo
*rti
= GetRailTypeInfo(this->filter
.railtype
);
1869 SetDParam(0, rti
->strings
.build_caption
);
1871 SetDParam(0, (this->listview_mode
? STR_VEHICLE_LIST_AVAILABLE_TRAINS
: STR_BUY_VEHICLE_TRAIN_ALL_CAPTION
) + this->vehicle_type
);
1875 case WID_BV_SORT_DROPDOWN
:
1876 SetDParam(0, _engine_sort_listing
[this->vehicle_type
][this->sort_criteria
]);
1879 case WID_BV_CARGO_FILTER_DROPDOWN
:
1880 SetDParam(0, this->cargo_filter_texts
[this->cargo_filter_criteria
]);
1883 case WID_BV_SHOW_HIDE
: {
1884 const Engine
*e
= (this->sel_engine
== INVALID_ENGINE
) ? nullptr : Engine::Get(this->sel_engine
);
1885 if (e
!= nullptr && e
->IsHidden(_local_company
)) {
1886 SetDParam(0, STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON
+ this->vehicle_type
);
1888 SetDParam(0, STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON
+ this->vehicle_type
);
1895 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
1899 resize
->height
= GetEngineListHeight(this->vehicle_type
);
1900 size
->height
= 3 * resize
->height
;
1901 size
->width
= max(size
->width
, GetVehicleImageCellSize(this->vehicle_type
, EIT_PURCHASE
).extend_left
+ GetVehicleImageCellSize(this->vehicle_type
, EIT_PURCHASE
).extend_right
+ 165);
1905 size
->height
= this->details_height
;
1908 case WID_BV_SORT_ASCENDING_DESCENDING
: {
1909 Dimension d
= GetStringBoundingBox(this->GetWidget
<NWidgetCore
>(widget
)->widget_data
);
1910 d
.width
+= padding
.width
+ Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1911 d
.height
+= padding
.height
;
1912 *size
= maxdim(*size
, d
);
1916 case WID_BV_SHOW_HIDE
:
1917 *size
= GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON
+ this->vehicle_type
);
1918 *size
= maxdim(*size
, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON
+ this->vehicle_type
));
1919 size
->width
+= padding
.width
;
1920 size
->height
+= padding
.height
;
1925 virtual void DrawWidget(const Rect
&r
, int widget
) const
1929 DrawEngineList(this->vehicle_type
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, r
.top
+ WD_FRAMERECT_TOP
, &this->eng_list
, this->vscroll
->GetPosition(), min(this->vscroll
->GetPosition() + this->vscroll
->GetCapacity(), this->eng_list
.Length()), this->sel_engine
, false, DEFAULT_GROUP
);
1932 case WID_BV_SORT_ASCENDING_DESCENDING
:
1933 this->DrawSortButtonState(WID_BV_SORT_ASCENDING_DESCENDING
, this->descending_sort_order
? SBS_DOWN
: SBS_UP
);
1938 virtual void OnPaint()
1940 this->GenerateBuildList();
1941 this->vscroll
->SetCount(this->eng_list
.Length());
1943 this->SetWidgetDisabledState(WID_BV_SHOW_HIDE
, this->sel_engine
== INVALID_ENGINE
);
1945 /* disable renaming engines in network games if you are not the server */
1946 this->SetWidgetDisabledState(WID_BV_RENAME
, (this->sel_engine
== INVALID_ENGINE
) || (_networking
&& !_network_server
));
1947 this->SetWidgetDisabledState(WID_BV_BUILD
, this->sel_engine
== INVALID_ENGINE
);
1949 this->DrawWidgets();
1951 if (!this->IsShaded()) {
1952 int needed_height
= this->details_height
;
1953 /* Draw details panels. */
1954 if (this->sel_engine
!= INVALID_ENGINE
) {
1955 NWidgetBase
*nwi
= this->GetWidget
<NWidgetBase
>(WID_BV_PANEL
);
1956 int text_end
= DrawVehiclePurchaseInfo(nwi
->pos_x
+ WD_FRAMETEXT_LEFT
, nwi
->pos_x
+ nwi
->current_x
- WD_FRAMETEXT_RIGHT
,
1957 nwi
->pos_y
+ WD_FRAMERECT_TOP
, this->sel_engine
);
1958 needed_height
= max(needed_height
, text_end
- (int)nwi
->pos_y
+ WD_FRAMERECT_BOTTOM
);
1960 if (needed_height
!= this->details_height
) { // Details window are not high enough, enlarge them.
1961 int resize
= needed_height
- this->details_height
;
1962 this->details_height
= needed_height
;
1963 this->ReInit(0, resize
);
1969 virtual void OnQueryTextFinished(char *str
)
1971 if (str
== nullptr) return;
1973 DoCommandP(0, this->rename_engine
, 0, CMD_RENAME_ENGINE
| CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE
+ this->vehicle_type
), nullptr, str
);
1976 virtual void OnDropdownSelect(int widget
, int index
)
1979 case WID_BV_SORT_DROPDOWN
:
1980 if (this->sort_criteria
!= index
) {
1981 this->sort_criteria
= index
;
1982 _engine_sort_last_criteria
[this->vehicle_type
] = this->sort_criteria
;
1983 this->eng_list
.ForceRebuild();
1987 case WID_BV_CARGO_FILTER_DROPDOWN
: // Select a cargo filter criteria
1988 if (this->cargo_filter_criteria
!= index
) {
1989 this->cargo_filter_criteria
= index
;
1990 _engine_sort_last_cargo_criteria
[this->vehicle_type
] = this->cargo_filter
[this->cargo_filter_criteria
];
1991 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1992 this->eng_list
.SetFilterState(this->cargo_filter
[this->cargo_filter_criteria
] != CF_ANY
);
1993 this->eng_list
.ForceRebuild();
2000 virtual void OnResize()
2002 this->vscroll
->SetCapacityFromWidget(this, WID_BV_LIST
);
2006 /* Advanced window for trains. It is divided into two parts, one for locomotives and one for wagons. */
2008 struct BuildVehicleWindowTrainAdvanced
: Window
{
2010 /* Locomotives and wagons */
2012 VehicleType vehicle_type
;
2013 RailTypeByte railtype
;
2019 bool descending_sort_order_loco
;
2020 byte sort_criteria_loco
;
2021 EngineID sel_engine_loco
;
2022 EngineID rename_engine_loco
;
2023 GUIEngineList eng_list_loco
;
2024 Scrollbar
*vscroll_loco
;
2025 byte cargo_filter_criteria_loco
; ///< Selected cargo filter
2026 bool show_hidden_locos
; ///< State of the 'show hidden locomotives' button.
2027 int details_height_loco
; ///< Minimal needed height of the details panels (found so far).
2028 CargoID cargo_filter_loco
[NUM_CARGO
+ 2]; ///< Available cargo filters; CargoID or CF_ANY or CF_NONE
2029 StringID cargo_filter_texts_loco
[NUM_CARGO
+ 3]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID
2034 bool descending_sort_order_wagon
;
2035 byte sort_criteria_wagon
;
2036 EngineID sel_engine_wagon
;
2037 EngineID rename_engine_wagon
;
2038 GUIEngineList eng_list_wagon
;
2039 Scrollbar
*vscroll_wagon
;
2040 byte cargo_filter_criteria_wagon
; ///< Selected cargo filter
2041 bool show_hidden_wagons
; ///< State of the 'show hidden wagons' button.
2042 int details_height_wagon
; ///< Minimal needed height of the details panels (found so far).
2043 CargoID cargo_filter_wagon
[NUM_CARGO
+ 2]; ///< Available cargo filters; CargoID or CF_ANY or CF_NONE
2044 StringID cargo_filter_texts_wagon
[NUM_CARGO
+ 3]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID
2047 bool virtual_train_mode
; ///< Are we building a virtual train?
2048 Train
**virtual_train_out
; ///< Virtual train ptr
2050 BuildVehicleWindowTrainAdvanced(WindowDesc
*desc
, TileIndex tile
, VehicleType type
, Train
**virtual_train_out
) : Window(desc
)
2053 this->vehicle_type
= type
;
2054 this->window_number
= tile
== INVALID_TILE
? (int)type
: tile
;
2056 this->virtual_train_out
= virtual_train_out
;
2057 this->virtual_train_mode
= (virtual_train_out
!= nullptr);
2058 if (this->virtual_train_mode
) this->window_number
= 0;
2060 this->sel_engine_loco
= INVALID_ENGINE
;
2061 this->sort_criteria_loco
= _last_sort_criteria_loco
;
2062 this->descending_sort_order_loco
= _last_sort_order_loco
;
2063 this->show_hidden_wagons
= _engine_sort_show_hidden_wagons
;
2065 this->sel_engine_wagon
= INVALID_ENGINE
;
2066 this->sort_criteria_wagon
= _last_sort_criteria_wagon
;
2067 this->descending_sort_order_wagon
= _last_sort_order_wagon
;
2068 this->show_hidden_locos
= _engine_sort_show_hidden_locos
;
2070 this->railtype
= (tile
== INVALID_TILE
) ? RAILTYPE_END
: GetRailType(tile
);
2071 this->listview_mode
= !(this->virtual_train_mode
) && (this->window_number
<= VEH_END
);
2073 this->CreateNestedTree();
2075 this->vscroll_loco
= this->GetScrollbar(WID_BV_SCROLLBAR_LOCO
);
2076 this->vscroll_wagon
= this->GetScrollbar(WID_BV_SCROLLBAR_WAGON
);
2078 /* If we are just viewing the list of vehicles, we do not need the Build button.
2079 * So we just hide it, and enlarge the Rename button by the now vacant place. */
2080 if (this->listview_mode
) this->GetWidget
<NWidgetStacked
>(WID_BV_BUILD_SEL_LOCO
)->SetDisplayedPlane(SZSP_NONE
);
2081 if (this->listview_mode
) this->GetWidget
<NWidgetStacked
>(WID_BV_BUILD_SEL_WAGON
)->SetDisplayedPlane(SZSP_NONE
);
2085 NWidgetCore
*widget_loco
= this->GetWidget
<NWidgetCore
>(WID_BV_LIST_LOCO
);
2086 widget_loco
->tool_tip
= STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP
+ type
;
2088 widget_loco
= this->GetWidget
<NWidgetCore
>(WID_BV_SHOW_HIDE_LOCO
);
2089 widget_loco
->tool_tip
= STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP
+ type
;
2091 widget_loco
= this->GetWidget
<NWidgetCore
>(WID_BV_BUILD_LOCO
);
2092 if (this->virtual_train_mode
) {
2093 widget_loco
->widget_data
= STR_TMPL_CONFIRM
;
2094 widget_loco
->tool_tip
= STR_TMPL_CONFIRM
;
2097 widget_loco
->widget_data
= STR_BUY_VEHICLE_TRAIN_BUY_LOCOMOTIVE_BUTTON
;
2098 widget_loco
->tool_tip
= STR_BUY_VEHICLE_TRAIN_BUY_LOCOMOTIVE_TOOLTIP
;
2101 widget_loco
= this->GetWidget
<NWidgetCore
>(WID_BV_RENAME_LOCO
);
2102 widget_loco
->widget_data
= STR_BUY_VEHICLE_TRAIN_RENAME_LOCOMOTIVE_BUTTON
;
2103 widget_loco
->tool_tip
= STR_BUY_VEHICLE_TRAIN_RENAME_LOCOMOTIVE_TOOLTIP
;
2105 widget_loco
= this->GetWidget
<NWidgetCore
>(WID_BV_SHOW_HIDDEN_LOCOS
);
2106 widget_loco
->widget_data
= STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN
+ type
;
2107 widget_loco
->tool_tip
= STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP
+ type
;
2108 widget_loco
->SetLowered(this->show_hidden_locos
);
2113 NWidgetCore
*widget_wagon
= this->GetWidget
<NWidgetCore
>(WID_BV_LIST_WAGON
);
2114 widget_wagon
->tool_tip
= STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP
+ type
;
2116 widget_wagon
= this->GetWidget
<NWidgetCore
>(WID_BV_SHOW_HIDE_WAGON
);
2117 widget_wagon
->tool_tip
= STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP
+ type
;
2119 widget_wagon
= this->GetWidget
<NWidgetCore
>(WID_BV_BUILD_WAGON
);
2120 if (this->virtual_train_mode
) {
2121 widget_wagon
->widget_data
= STR_TMPL_CONFIRM
;
2122 widget_wagon
->tool_tip
= STR_TMPL_CONFIRM
;
2124 widget_wagon
->widget_data
= STR_BUY_VEHICLE_TRAIN_BUY_WAGON_BUTTON
;
2125 widget_wagon
->tool_tip
= STR_BUY_VEHICLE_TRAIN_BUY_WAGON_TOOLTIP
;
2128 widget_wagon
= this->GetWidget
<NWidgetCore
>(WID_BV_RENAME_WAGON
);
2129 widget_wagon
->widget_data
= STR_BUY_VEHICLE_TRAIN_RENAME_WAGON_BUTTON
;
2130 widget_wagon
->tool_tip
= STR_BUY_VEHICLE_TRAIN_RENAME_WAGON_TOOLTIP
;
2132 widget_wagon
= this->GetWidget
<NWidgetCore
>(WID_BV_SHOW_HIDDEN_WAGONS
);
2133 widget_wagon
->widget_data
= STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN
+ type
;
2134 widget_wagon
->tool_tip
= STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP
+ type
;
2135 widget_wagon
->SetLowered(this->show_hidden_wagons
);
2138 this->details_height_loco
= ((this->vehicle_type
== VEH_TRAIN
) ? 10 : 9) * FONT_HEIGHT_NORMAL
+ WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
;
2139 this->details_height_wagon
= ((this->vehicle_type
== VEH_TRAIN
) ? 10 : 9) * FONT_HEIGHT_NORMAL
+ WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
;
2141 this->FinishInitNested(this->window_number
);
2143 this->owner
= (tile
!= INVALID_TILE
) ? GetTileOwner(tile
) : _local_company
;
2145 this->eng_list_loco
.ForceRebuild();
2146 this->eng_list_wagon
.ForceRebuild();
2148 this->GenerateBuildList(); // generate the list, since we need it in the next line
2149 /* Select the first engine in the list as default when opening the window */
2151 if (this->eng_list_loco
.Length() > 0) this->sel_engine_loco
= this->eng_list_loco
[0];
2152 if (this->eng_list_wagon
.Length() > 0) this->sel_engine_wagon
= this->eng_list_wagon
[0];
2156 /** Populate the filter list and set the cargo filter criteria. */
2157 void SetCargoFilterArray()
2161 uint filter_items_loco
= 0;
2163 /* Add item for disabling filtering. */
2164 this->cargo_filter_loco
[filter_items_loco
] = CF_ANY
;
2165 this->cargo_filter_texts_loco
[filter_items_loco
] = STR_PURCHASE_INFO_ALL_TYPES
;
2166 filter_items_loco
++;
2168 /* Add item for vehicles not carrying anything, e.g. train engines. */
2169 this->cargo_filter_loco
[filter_items_loco
] = CF_NONE
;
2170 this->cargo_filter_texts_loco
[filter_items_loco
] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE
;
2171 filter_items_loco
++;
2173 /* Collect available cargo types for filtering. */
2174 const CargoSpec
*cs_loco
;
2175 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs_loco
) {
2176 this->cargo_filter_loco
[filter_items_loco
] = cs_loco
->Index();
2177 this->cargo_filter_texts_loco
[filter_items_loco
] = cs_loco
->name
;
2178 filter_items_loco
++;
2181 /* Terminate the filter list. */
2182 this->cargo_filter_texts_loco
[filter_items_loco
] = INVALID_STRING_ID
;
2184 /* If not found, the cargo criteria will be set to all cargoes. */
2185 this->cargo_filter_criteria_loco
= 0;
2187 /* Find the last cargo filter criteria. */
2188 for (uint i
= 0; i
< filter_items_loco
; i
++) {
2189 if (this->cargo_filter_loco
[i
] == _last_filter_criteria_loco
) {
2190 this->cargo_filter_criteria_loco
= i
;
2195 this->eng_list_loco
.SetFilterFuncs(_filter_funcs
);
2196 this->eng_list_loco
.SetFilterState(this->cargo_filter_loco
[this->cargo_filter_criteria_loco
] != CF_ANY
);
2201 uint filter_items_wagon
= 0;
2203 /* Add item for disabling filtering. */
2204 this->cargo_filter_wagon
[filter_items_wagon
] = CF_ANY
;
2205 this->cargo_filter_texts_wagon
[filter_items_wagon
] = STR_PURCHASE_INFO_ALL_TYPES
;
2206 filter_items_wagon
++;
2208 /* Add item for vehicles not carrying anything, e.g. train engines.
2209 * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
2211 this->cargo_filter_wagon
[filter_items_wagon
] = CF_NONE
;
2212 this->cargo_filter_texts_wagon
[filter_items_wagon
] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE
;
2213 filter_items_wagon
++;
2216 /* Collect available cargo types for filtering. */
2218 const CargoSpec
*cs_wagon
;
2220 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs_wagon
) {
2221 this->cargo_filter_wagon
[filter_items_wagon
] = cs_wagon
->Index();
2222 this->cargo_filter_texts_wagon
[filter_items_wagon
] = cs_wagon
->name
;
2223 filter_items_wagon
++;
2226 /* Terminate the filter list. */
2227 this->cargo_filter_texts_wagon
[filter_items_wagon
] = INVALID_STRING_ID
;
2229 /* If not found, the cargo criteria will be set to all cargoes. */
2230 this->cargo_filter_criteria_wagon
= 0;
2232 /* Find the last cargo filter criteria. */
2233 for (uint i
= 0; i
< filter_items_wagon
; i
++) {
2234 if (this->cargo_filter_wagon
[i
] == _last_filter_criteria_wagon
) {
2235 this->cargo_filter_criteria_wagon
= i
;
2239 this->eng_list_wagon
.SetFilterFuncs(_filter_funcs
);
2240 this->eng_list_wagon
.SetFilterState(this->cargo_filter_wagon
[this->cargo_filter_criteria_wagon
] != CF_ANY
);
2245 this->SetCargoFilterArray();
2248 /** Filter the engine list against the currently selected cargo filter */
2249 void FilterEngineList()
2251 this->eng_list_loco
.Filter(this->cargo_filter_loco
[this->cargo_filter_criteria_loco
]);
2252 if (0 == this->eng_list_loco
.Length()) { // no engine passed through the filter, invalidate the previously selected engine
2253 this->sel_engine_loco
= INVALID_ENGINE
;
2254 } else if (!this->eng_list_loco
.Contains(this->sel_engine_loco
)) { // previously selected engine didn't pass the filter, select the first engine of the list
2255 this->sel_engine_loco
= this->eng_list_loco
[0];
2257 this->eng_list_wagon
.Filter(this->cargo_filter_wagon
[this->cargo_filter_criteria_wagon
]);
2258 if (0 == this->eng_list_wagon
.Length()) { // no engine passed through the filter, invalidate the previously selected engine
2259 this->sel_engine_wagon
= INVALID_ENGINE
;
2260 } else if (!this->eng_list_wagon
.Contains(this->sel_engine_wagon
)) { // previously selected engine didn't pass the filter, select the first engine of the list
2261 this->sel_engine_wagon
= this->eng_list_wagon
[0];
2265 /* Filter a single locomotive */
2266 bool FilterSingleEngineLoco(EngineID eid
)
2268 CargoID filter_type
= this->cargo_filter_loco
[this->cargo_filter_criteria_loco
];
2269 return (filter_type
== CF_ANY
|| CargoFilter(&eid
, filter_type
));
2272 /* Filter a single wagon */
2273 bool FilterSingleEngineWagon(EngineID eid
)
2275 CargoID filter_type
= this->cargo_filter_wagon
[this->cargo_filter_criteria_wagon
];
2276 return (filter_type
== CF_ANY
|| CargoFilter(&eid
, filter_type
));
2279 /* Figure out what train EngineIDs to put in the list */
2280 void GenerateBuildTrainList()
2282 this->railtype
= (this->listview_mode
|| this->virtual_train_mode
) ? RAILTYPE_END
: GetRailType(this->window_number
);
2286 EngineID sel_id_loco
= INVALID_ENGINE
;
2288 int num_engines_loco
= 0;
2289 int num_wagons_loco
= 0;
2291 this->eng_list_loco
.Clear();
2293 /* Make list of all available train engines and wagons.
2294 * Also check to see if the previously selected engine is still available,
2295 * and if not, reset selection to INVALID_ENGINE. This could be the case
2296 * when engines become obsolete and are removed */
2297 const Engine
*e_loco
;
2299 FOR_ALL_ENGINES_OF_TYPE(e_loco
, VEH_TRAIN
) {
2300 if (!this->show_hidden_locos
&& e_loco
->IsHidden(_local_company
)) continue;
2301 EngineID eid
= e_loco
->index
;
2302 const RailVehicleInfo
*rvi
= &e_loco
->u
.rail
;
2304 if (this->railtype
!= RAILTYPE_END
&& !HasPowerOnRail(rvi
->railtype
, this->railtype
)) continue;
2305 if (!IsEngineBuildable(eid
, VEH_TRAIN
, _local_company
)) continue;
2307 /* Filter now! So num_engines and num_wagons is valid */
2308 if (!FilterSingleEngineLoco(eid
)) continue;
2310 if (rvi
->railveh_type
!= RAILVEH_WAGON
) {
2312 *this->eng_list_loco
.Append() = eid
;
2315 if (eid
== this->sel_engine_loco
) sel_id_loco
= eid
;
2318 this->sel_engine_loco
= sel_id_loco
;
2323 EngineID sel_id_wagon
= INVALID_ENGINE
;
2325 int num_engines_wagon
= 0;
2326 int num_wagons_wagon
= 0;
2328 this->eng_list_wagon
.Clear();
2330 /* Make list of all available train engines and wagons.
2331 * Also check to see if the previously selected engine is still available,
2332 * and if not, reset selection to INVALID_ENGINE. This could be the case
2333 * when engines become obsolete and are removed */
2334 const Engine
*e_wagon
;
2336 FOR_ALL_ENGINES_OF_TYPE(e_wagon
, VEH_TRAIN
) {
2337 if (!this->show_hidden_wagons
&& e_wagon
->IsHidden(_local_company
)) continue;
2338 EngineID eid
= e_wagon
->index
;
2339 const RailVehicleInfo
*rvi
= &e_wagon
->u
.rail
;
2341 if (this->railtype
!= RAILTYPE_END
&& !HasPowerOnRail(rvi
->railtype
, this->railtype
)) continue;
2342 if (!IsEngineBuildable(eid
, VEH_TRAIN
, _local_company
)) continue;
2344 /* Filter now! So num_engines and num_wagons is valid */
2345 if (!FilterSingleEngineWagon(eid
)) continue;
2348 if (rvi
->railveh_type
== RAILVEH_WAGON
) {
2349 *this->eng_list_wagon
.Append() = eid
;
2353 if (eid
== this->sel_engine_wagon
) sel_id_wagon
= eid
;
2356 this->sel_engine_wagon
= sel_id_wagon
;
2358 /* Sort locomotives */
2359 _internal_sort_order_loco
= this->descending_sort_order_loco
;
2360 EngList_SortPartial(&this->eng_list_loco
, _sorter_loco
[this->sort_criteria_loco
], 0, num_engines_loco
);
2363 _internal_sort_order_wagon
= this->descending_sort_order_wagon
;
2364 EngList_SortPartial(&this->eng_list_wagon
, _sorter_wagon
[this->sort_criteria_wagon
], num_engines_wagon
, num_wagons_wagon
);
2368 /* Generate the list of vehicles */
2369 void GenerateBuildList()
2371 if (!this->eng_list_loco
.NeedRebuild() && !this->eng_list_wagon
.NeedRebuild()) return;
2373 this->GenerateBuildTrainList();
2374 this->eng_list_loco
.Compact();
2375 this->eng_list_loco
.RebuildDone();
2376 this->eng_list_wagon
.Compact();
2377 this->eng_list_wagon
.RebuildDone();
2381 void OnClick(Point pt
, int widget
, int click_count
)
2388 case WID_BV_SORT_ASSENDING_DESCENDING_LOCO
: {
2389 this->descending_sort_order_loco
^= true;
2390 _last_sort_order_loco
= this->descending_sort_order_loco
;
2391 this->eng_list_loco
.ForceRebuild();
2396 case WID_BV_SHOW_HIDDEN_LOCOS
: {
2397 this->show_hidden_locos
^= true;
2398 _engine_sort_show_hidden_locos
= this->show_hidden_locos
;
2399 this->eng_list_loco
.ForceRebuild();
2400 this->SetWidgetLoweredState(widget
, this->show_hidden_locos
);
2405 case WID_BV_LIST_LOCO
: {
2406 uint i
= this->vscroll_loco
->GetScrolledRowFromWidget(pt
.y
, this, WID_BV_LIST_LOCO
);
2407 size_t num_items
= this->eng_list_loco
.Length();
2408 this->sel_engine_loco
= (i
< num_items
) ? this->eng_list_loco
[i
] : INVALID_ENGINE
;
2411 if (_ctrl_pressed
) {
2412 this->OnClick(pt
, WID_BV_SHOW_HIDE_LOCO
, 1);
2414 else if (click_count
> 1 && !this->listview_mode
) {
2415 this->OnClick(pt
, WID_BV_BUILD_LOCO
, 1);
2420 case WID_BV_SORT_DROPDOWN_LOCO
: { // Select sorting criteria dropdown menu
2421 uint32 hidden_mask
= 0;
2422 /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
2423 if (_settings_game
.vehicle
.train_acceleration_model
== AM_ORIGINAL
) {
2424 SetBit(hidden_mask
, 4); // tractive effort
2426 ShowDropDownMenu(this, _sort_listing_loco
, this->sort_criteria_loco
, WID_BV_SORT_DROPDOWN_LOCO
, 0, hidden_mask
);
2430 case WID_BV_CARGO_FILTER_DROPDOWN_LOCO
: { // Select cargo filtering criteria dropdown menu
2431 ShowDropDownMenu(this, this->cargo_filter_texts_loco
, this->cargo_filter_criteria_loco
, WID_BV_CARGO_FILTER_DROPDOWN_LOCO
, 0, 0);
2435 case WID_BV_SHOW_HIDE_LOCO
: {
2436 const Engine
*e
= (this->sel_engine_loco
== INVALID_ENGINE
) ? nullptr : Engine::GetIfValid(this->sel_engine_loco
);
2438 DoCommandP(0, 0, this->sel_engine_loco
| (e
->IsHidden(_current_company
) ? 0 : (1u << 31)), CMD_SET_VEHICLE_VISIBILITY
);
2443 case WID_BV_BUILD_LOCO
: {
2444 EngineID sel_eng
= this->sel_engine_loco
;
2445 if (sel_eng
!= INVALID_ENGINE
) {
2446 if (this->virtual_train_mode
) {
2447 DoCommandP(0, sel_eng
, 0, CMD_BUILD_VIRTUAL_RAIL_VEHICLE
, CcAddVirtualEngine
);
2450 CommandCallback
*callback
= (this->vehicle_type
== VEH_TRAIN
&& RailVehInfo(sel_eng
)->railveh_type
== RAILVEH_WAGON
) ? CcBuildWagon
: CcBuildPrimaryVehicle
;
2451 DoCommandP(this->window_number
, sel_eng
, 0, GetCmdBuildVeh(this->vehicle_type
), callback
);
2457 case WID_BV_RENAME_LOCO
: {
2458 EngineID sel_eng
= this->sel_engine_loco
;
2459 if (sel_eng
!= INVALID_ENGINE
) {
2460 this->rename_engine_loco
= sel_eng
;
2461 this->rename_engine_wagon
= INVALID_ENGINE
;
2462 SetDParam(0, sel_eng
);
2463 ShowQueryString(STR_ENGINE_NAME
, STR_QUERY_RENAME_TRAIN_TYPE_LOCOMOTIVE_CAPTION
+ this->vehicle_type
, MAX_LENGTH_ENGINE_NAME_CHARS
, this, CS_ALPHANUMERAL
, QSF_ENABLE_DEFAULT
| QSF_LEN_IN_CHARS
);
2471 case WID_BV_SORT_ASSENDING_DESCENDING_WAGON
: {
2472 this->descending_sort_order_wagon
^= true;
2473 _last_sort_order_wagon
= this->descending_sort_order_wagon
;
2474 this->eng_list_wagon
.ForceRebuild();
2479 case WID_BV_SHOW_HIDDEN_WAGONS
: {
2480 this->show_hidden_wagons
^= true;
2481 _engine_sort_show_hidden_wagons
= this->show_hidden_wagons
;
2482 this->eng_list_wagon
.ForceRebuild();
2483 this->SetWidgetLoweredState(widget
, this->show_hidden_wagons
);
2488 case WID_BV_LIST_WAGON
: {
2489 uint i
= this->vscroll_wagon
->GetScrolledRowFromWidget(pt
.y
, this, WID_BV_LIST_WAGON
);
2490 size_t num_items
= this->eng_list_wagon
.Length();
2491 this->sel_engine_wagon
= (i
< num_items
) ? this->eng_list_wagon
[i
] : INVALID_ENGINE
;
2494 if (_ctrl_pressed
) {
2495 this->OnClick(pt
, WID_BV_SHOW_HIDE_WAGON
, 1);
2497 else if (click_count
> 1 && !this->listview_mode
) {
2498 this->OnClick(pt
, WID_BV_BUILD_WAGON
, 1);
2503 case WID_BV_SORT_DROPDOWN_WAGON
: { // Select sorting criteria dropdown menu
2504 uint32 hidden_mask
= 0;
2505 /* Disable sorting by maximum speed when wagon speed is disabled. */
2506 if (!_settings_game
.vehicle
.wagon_speed_limits
) {
2507 SetBit(hidden_mask
, 2); // maximum speed
2509 ShowDropDownMenu(this, _sort_listing_wagon
, this->sort_criteria_wagon
, WID_BV_SORT_DROPDOWN_WAGON
, 0, hidden_mask
);
2513 case WID_BV_CARGO_FILTER_DROPDOWN_WAGON
: { // Select cargo filtering criteria dropdown menu
2514 ShowDropDownMenu(this, this->cargo_filter_texts_wagon
, this->cargo_filter_criteria_wagon
, WID_BV_CARGO_FILTER_DROPDOWN_WAGON
, 0, 0);
2518 case WID_BV_SHOW_HIDE_WAGON
: {
2519 const Engine
*e
= (this->sel_engine_wagon
== INVALID_ENGINE
) ? nullptr : Engine::GetIfValid(this->sel_engine_wagon
);
2521 DoCommandP(0, 0, this->sel_engine_wagon
| (e
->IsHidden(_current_company
) ? 0 : (1u << 31)), CMD_SET_VEHICLE_VISIBILITY
);
2526 case WID_BV_BUILD_WAGON
: {
2527 EngineID sel_eng
= this->sel_engine_wagon
;
2528 if (sel_eng
!= INVALID_ENGINE
) {
2529 if (this->virtual_train_mode
) {
2530 DoCommandP(0, sel_eng
, 0, CMD_BUILD_VIRTUAL_RAIL_VEHICLE
, CcAddVirtualEngine
);
2533 CommandCallback
*callback
= (this->vehicle_type
== VEH_TRAIN
&& RailVehInfo(sel_eng
)->railveh_type
== RAILVEH_WAGON
) ? CcBuildWagon
: CcBuildPrimaryVehicle
;
2534 DoCommandP(this->window_number
, sel_eng
, 0, GetCmdBuildVeh(this->vehicle_type
), callback
);
2540 case WID_BV_RENAME_WAGON
: {
2541 EngineID sel_eng
= this->sel_engine_wagon
;
2542 if (sel_eng
!= INVALID_ENGINE
) {
2543 this->rename_engine_loco
= INVALID_ENGINE
;
2544 this->rename_engine_wagon
= sel_eng
;
2545 SetDParam(0, sel_eng
);
2546 ShowQueryString(STR_ENGINE_NAME
, STR_QUERY_RENAME_TRAIN_TYPE_WAGON_CAPTION
+ this->vehicle_type
, MAX_LENGTH_ENGINE_NAME_CHARS
, this, CS_ALPHANUMERAL
, QSF_ENABLE_DEFAULT
| QSF_LEN_IN_CHARS
);
2554 * Some data on this window has become invalid.
2555 * @param data Information about the changed data.
2556 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
2558 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
2560 if (!gui_scope
) return;
2562 /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
2563 this->eng_list_loco
.ForceRebuild();
2564 this->eng_list_wagon
.ForceRebuild();
2567 virtual void SetStringParameters(int widget
) const
2570 case WID_BV_CAPTION
: {
2571 if (this->vehicle_type
== VEH_TRAIN
&& !this->listview_mode
&& !this->virtual_train_mode
) {
2572 const RailtypeInfo
*rti
= GetRailTypeInfo(this->railtype
);
2573 SetDParam(0, rti
->strings
.build_caption
);
2575 SetDParam(0, (this->listview_mode
? STR_VEHICLE_LIST_AVAILABLE_TRAINS
: STR_BUY_VEHICLE_TRAIN_ALL_CAPTION
) + this->vehicle_type
);
2580 case WID_BV_CAPTION_LOCO
: {
2581 SetDParam(0, STR_BUY_VEHICLE_TRAIN_LOCOMOTIVES
);
2585 case WID_BV_SHOW_HIDE_LOCO
: {
2586 const Engine
*e
= (this->sel_engine_loco
== INVALID_ENGINE
) ? nullptr : Engine::GetIfValid(this->sel_engine_loco
);
2587 if (e
!= nullptr && e
->IsHidden(_local_company
)) {
2588 SetDParam(0, STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON
+ this->vehicle_type
);
2591 SetDParam(0, STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON
+ this->vehicle_type
);
2596 case WID_BV_CAPTION_WAGON
: {
2597 SetDParam(0, STR_BUY_VEHICLE_TRAIN_WAGONS
);
2601 case WID_BV_SORT_DROPDOWN_LOCO
: {
2602 SetDParam(0, _sort_listing_loco
[this->sort_criteria_loco
]);
2606 case WID_BV_CARGO_FILTER_DROPDOWN_LOCO
: {
2607 SetDParam(0, this->cargo_filter_texts_loco
[this->cargo_filter_criteria_loco
]);
2611 case WID_BV_SORT_DROPDOWN_WAGON
: {
2612 SetDParam(0, _sort_listing_wagon
[this->sort_criteria_wagon
]);
2616 case WID_BV_CARGO_FILTER_DROPDOWN_WAGON
: {
2617 SetDParam(0, this->cargo_filter_texts_wagon
[this->cargo_filter_criteria_wagon
]);
2621 case WID_BV_SHOW_HIDE_WAGON
: {
2622 const Engine
*e
= (this->sel_engine_wagon
== INVALID_ENGINE
) ? nullptr : Engine::GetIfValid(this->sel_engine_wagon
);
2623 if (e
!= nullptr && e
->IsHidden(_local_company
)) {
2624 SetDParam(0, STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON
+ this->vehicle_type
);
2627 SetDParam(0, STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON
+ this->vehicle_type
);
2634 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
2637 case WID_BV_LIST_LOCO
: {
2638 resize
->height
= GetEngineListHeight(this->vehicle_type
);
2639 size
->height
= 3 * resize
->height
;
2643 case WID_BV_PANEL_LOCO
: {
2644 size
->height
= this->details_height_loco
;
2648 case WID_BV_SORT_ASSENDING_DESCENDING_LOCO
: {
2649 Dimension d
= GetStringBoundingBox(this->GetWidget
<NWidgetCore
>(widget
)->widget_data
);
2650 d
.width
+= padding
.width
+ Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
2651 d
.height
+= padding
.height
;
2652 *size
= maxdim(*size
, d
);
2656 case WID_BV_LIST_WAGON
: {
2657 resize
->height
= GetEngineListHeight(this->vehicle_type
);
2658 size
->height
= 3 * resize
->height
;
2662 case WID_BV_PANEL_WAGON
: {
2663 size
->height
= this->details_height_wagon
;
2667 case WID_BV_SORT_ASSENDING_DESCENDING_WAGON
: {
2668 Dimension d
= GetStringBoundingBox(this->GetWidget
<NWidgetCore
>(widget
)->widget_data
);
2669 d
.width
+= padding
.width
+ Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
2670 d
.height
+= padding
.height
;
2671 *size
= maxdim(*size
, d
);
2675 case WID_BV_SHOW_HIDE_LOCO
: {
2676 *size
= GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON
+ this->vehicle_type
);
2677 *size
= maxdim(*size
, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON
+ this->vehicle_type
));
2678 size
->width
+= padding
.width
;
2679 size
->height
+= padding
.height
;
2683 case WID_BV_SHOW_HIDE_WAGON
: {
2684 *size
= GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON
+ this->vehicle_type
);
2685 *size
= maxdim(*size
, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON
+ this->vehicle_type
));
2686 size
->width
+= padding
.width
;
2687 size
->height
+= padding
.height
;
2693 virtual void DrawWidget(const Rect
&r
, int widget
) const
2696 case WID_BV_LIST_LOCO
: {
2697 DrawEngineList(this->vehicle_type
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, r
.top
+ WD_FRAMERECT_TOP
, &this->eng_list_loco
, this->vscroll_loco
->GetPosition(), min(this->vscroll_loco
->GetPosition() + this->vscroll_loco
->GetCapacity(), this->eng_list_loco
.Length()), this->sel_engine_loco
, false, DEFAULT_GROUP
);
2701 case WID_BV_SORT_ASSENDING_DESCENDING_LOCO
: {
2702 this->DrawSortButtonState(WID_BV_SORT_ASSENDING_DESCENDING_LOCO
, this->descending_sort_order_loco
? SBS_DOWN
: SBS_UP
);
2706 case WID_BV_LIST_WAGON
: {
2707 DrawEngineList(this->vehicle_type
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, r
.top
+ WD_FRAMERECT_TOP
, &this->eng_list_wagon
, this->vscroll_wagon
->GetPosition(), min(this->vscroll_wagon
->GetPosition() + this->vscroll_wagon
->GetCapacity(), this->eng_list_wagon
.Length()), this->sel_engine_wagon
, false, DEFAULT_GROUP
);
2711 case WID_BV_SORT_ASSENDING_DESCENDING_WAGON
: {
2712 this->DrawSortButtonState(WID_BV_SORT_ASSENDING_DESCENDING_WAGON
, this->descending_sort_order_wagon
? SBS_DOWN
: SBS_UP
);
2718 virtual void OnPaint()
2720 this->GenerateBuildList();
2721 this->vscroll_loco
->SetCount(this->eng_list_loco
.Length());
2722 this->vscroll_wagon
->SetCount(this->eng_list_wagon
.Length());
2724 this->SetWidgetDisabledState(WID_BV_SHOW_HIDE_LOCO
, this->sel_engine_loco
== INVALID_ENGINE
);
2725 this->SetWidgetDisabledState(WID_BV_SHOW_HIDE_WAGON
, this->sel_engine_wagon
== INVALID_ENGINE
);
2727 /* disable renaming engines in network games if you are not the server */
2728 this->SetWidgetDisabledState(WID_BV_RENAME_LOCO
, (this->sel_engine_loco
== INVALID_ENGINE
) || (_networking
&& !_network_server
));
2729 this->SetWidgetDisabledState(WID_BV_BUILD_LOCO
, this->sel_engine_loco
== INVALID_ENGINE
);
2731 /* disable renaming engines in network games if you are not the server */
2732 this->SetWidgetDisabledState(WID_BV_RENAME_WAGON
, (this->sel_engine_wagon
== INVALID_ENGINE
) || (_networking
&& !_network_server
));
2733 this->SetWidgetDisabledState(WID_BV_BUILD_WAGON
, this->sel_engine_wagon
== INVALID_ENGINE
);
2735 this->DrawWidgets();
2737 if (!this->IsShaded()) {
2738 int needed_height_loco
= this->details_height_loco
;
2739 /* Draw details panels. */
2740 if (this->sel_engine_loco
!= INVALID_ENGINE
) {
2741 NWidgetBase
*nwi
= this->GetWidget
<NWidgetBase
>(WID_BV_PANEL_LOCO
);
2742 int text_end
= DrawVehiclePurchaseInfo(nwi
->pos_x
+ WD_FRAMETEXT_LEFT
, nwi
->pos_x
+ nwi
->current_x
- WD_FRAMETEXT_RIGHT
,
2743 nwi
->pos_y
+ WD_FRAMERECT_TOP
, this->sel_engine_loco
);
2744 needed_height_loco
= max(needed_height_loco
, text_end
- (int)nwi
->pos_y
+ WD_FRAMERECT_BOTTOM
);
2746 if (needed_height_loco
!= this->details_height_loco
) { // Details window are not high enough, enlarge them.
2747 int resize
= needed_height_loco
- this->details_height_loco
;
2748 this->details_height_loco
= needed_height_loco
;
2749 this->ReInit(0, resize
);
2753 int needed_height_wagon
= this->details_height_wagon
;
2754 if (this->sel_engine_wagon
!= INVALID_ENGINE
) {
2755 NWidgetBase
*nwi
= this->GetWidget
<NWidgetBase
>(WID_BV_PANEL_WAGON
);
2756 int text_end
= DrawVehiclePurchaseInfo(nwi
->pos_x
+ WD_FRAMETEXT_LEFT
, nwi
->pos_x
+ nwi
->current_x
- WD_FRAMETEXT_RIGHT
,
2757 nwi
->pos_y
+ WD_FRAMERECT_TOP
, this->sel_engine_wagon
);
2758 needed_height_wagon
= max(needed_height_wagon
, text_end
- (int)nwi
->pos_y
+ WD_FRAMERECT_BOTTOM
);
2760 if (needed_height_wagon
!= this->details_height_wagon
) { // Details window are not high enough, enlarge them.
2761 int resize
= needed_height_wagon
- this->details_height_wagon
;
2762 this->details_height_wagon
= needed_height_wagon
;
2763 this->ReInit(0, resize
);
2769 virtual void OnQueryTextFinished(char *str
)
2771 if (str
== nullptr) return;
2772 if(this->rename_engine_loco
!= INVALID_ENGINE
)
2774 DoCommandP(0, this->rename_engine_loco
, 0, CMD_RENAME_ENGINE
| CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE
+ this->vehicle_type
), nullptr, str
);
2778 DoCommandP(0, this->rename_engine_wagon
, 0, CMD_RENAME_ENGINE
| CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE
+ this->vehicle_type
), nullptr, str
);
2782 virtual void OnDropdownSelect(int widget
, int index
)
2785 case WID_BV_SORT_DROPDOWN_LOCO
: {
2786 if (this->sort_criteria_loco
!= index
) {
2787 this->sort_criteria_loco
= index
;
2788 _last_sort_criteria_loco
= this->sort_criteria_loco
;
2789 this->eng_list_loco
.ForceRebuild();
2794 case WID_BV_CARGO_FILTER_DROPDOWN_LOCO
: { // Select a cargo filter criteria
2795 if (this->cargo_filter_criteria_loco
!= index
) {
2796 this->cargo_filter_criteria_loco
= index
;
2797 _last_filter_criteria_loco
= this->cargo_filter_loco
[this->cargo_filter_criteria_loco
];
2798 /* deactivate filter if criteria is 'Show All', activate it otherwise */
2799 this->eng_list_loco
.SetFilterState(this->cargo_filter_loco
[this->cargo_filter_criteria_loco
] != CF_ANY
);
2800 this->eng_list_loco
.ForceRebuild();
2805 case WID_BV_SORT_DROPDOWN_WAGON
: {
2806 if (this->sort_criteria_wagon
!= index
) {
2807 this->sort_criteria_wagon
= index
;
2808 _last_sort_criteria_wagon
= this->sort_criteria_wagon
;
2809 this->eng_list_wagon
.ForceRebuild();
2814 case WID_BV_CARGO_FILTER_DROPDOWN_WAGON
: { // Select a cargo filter criteria
2815 if (this->cargo_filter_criteria_wagon
!= index
) {
2816 this->cargo_filter_criteria_wagon
= index
;
2817 _last_filter_criteria_wagon
= this->cargo_filter_wagon
[this->cargo_filter_criteria_wagon
];
2818 /* deactivate filter if criteria is 'Show All', activate it otherwise */
2819 this->eng_list_wagon
.SetFilterState(this->cargo_filter_wagon
[this->cargo_filter_criteria_wagon
] != CF_ANY
);
2820 this->eng_list_wagon
.ForceRebuild();
2829 virtual void OnResize()
2831 this->vscroll_loco
->SetCapacityFromWidget(this, WID_BV_LIST_LOCO
);
2832 this->vscroll_wagon
->SetCapacityFromWidget(this, WID_BV_LIST_WAGON
);
2835 void AddVirtualEngine(Train
*toadd
)
2837 if (this->virtual_train_out
== nullptr) return;
2839 if (*(this->virtual_train_out
) == nullptr) {
2840 *(this->virtual_train_out
) = toadd
;
2842 VehicleID target
= (*(this->virtual_train_out
))->GetLastUnit()->index
;
2844 DoCommandP(0, (1 << 21) | toadd
->index
, target
, CMD_MOVE_RAIL_VEHICLE
);
2846 InvalidateWindowClassesData(WC_CREATE_TEMPLATE
);
2847 InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN
);
2851 void CcAddVirtualEngine(const CommandCost
&result
, TileIndex tile
, uint32 p1
, uint32 p2
)
2853 if (result
.Failed()) return;
2855 Window
* window
= FindWindowById(WC_BUILD_VIRTUAL_TRAIN
, 0);
2857 Train
* train
= Train::From(Vehicle::Get(_new_vehicle_id
));
2858 ((BuildVehicleWindowTrainAdvanced
*) window
)->AddVirtualEngine(train
);
2862 static WindowDesc
_build_vehicle_desc(
2863 WDP_AUTO
, "build_vehicle", 240, 268,
2864 WC_BUILD_VEHICLE
, WC_NONE
,
2866 _nested_build_vehicle_widgets
, lengthof(_nested_build_vehicle_widgets
)
2869 static WindowDesc
_build_vehicle_desc_train_advanced(
2870 WDP_AUTO
, "build_vehicle", 480, 268,
2871 WC_BUILD_VEHICLE
, WC_NONE
,
2873 _nested_build_vehicle_widgets_train_advanced
, lengthof(_nested_build_vehicle_widgets_train_advanced
)
2876 static WindowDesc
_build_template_vehicle_desc(
2877 WDP_AUTO
, "build_vehicle", 240, 268,
2878 WC_BUILD_VIRTUAL_TRAIN
, WC_CREATE_TEMPLATE
,
2880 _nested_build_vehicle_widgets_train_advanced
, lengthof(_nested_build_vehicle_widgets_train_advanced
)
2883 void ShowBuildVehicleWindow(TileIndex tile
, VehicleType type
)
2885 /* We want to be able to open both Available Train as Available Ships,
2886 * so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
2887 * As it always is a low value, it won't collide with any real tile
2889 uint num
= (tile
== INVALID_TILE
) ? (int)type
: tile
;
2891 assert(IsCompanyBuildableVehicleType(type
));
2893 DeleteWindowById(WC_BUILD_VEHICLE
, num
);
2895 if(type
== VEH_TRAIN
&& _settings_client
.gui
.advanced_train_purchase_window
)
2897 new BuildVehicleWindowTrainAdvanced(&_build_vehicle_desc_train_advanced
, tile
, type
, nullptr);
2901 new BuildVehicleWindow(&_build_vehicle_desc
, tile
, type
);
2905 void ShowTemplateTrainBuildVehicleWindow(Train
**virtual_train
)
2907 assert(IsCompanyBuildableVehicleType(VEH_TRAIN
));
2909 DeleteWindowById(WC_BUILD_VIRTUAL_TRAIN
, 0);
2911 new BuildVehicleWindowTrainAdvanced(&_build_template_vehicle_desc
, INVALID_TILE
, VEH_TRAIN
, virtual_train
);