2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file engine_gui.cpp GUI to show engine related information. */
11 #include "window_gui.h"
12 #include "engine_base.h"
13 #include "command_func.h"
14 #include "strings_func.h"
15 #include "engine_gui.h"
16 #include "articulated_vehicles.h"
17 #include "vehicle_func.h"
18 #include "company_func.h"
21 #include "settings_type.h"
26 #include "engine_cmd.h"
27 #include "zoom_func.h"
29 #include "widgets/engine_widget.h"
31 #include "table/strings.h"
33 #include "safeguards.h"
36 * Return the category of an engine.
37 * @param engine Engine to examine.
38 * @return String describing the category ("road veh", "train". "airplane", or "ship") of the engine.
40 StringID
GetEngineCategoryName(EngineID engine
)
42 const Engine
*e
= Engine::Get(engine
);
44 default: NOT_REACHED();
46 return GetRoadTypeInfo(e
->u
.road
.roadtype
)->strings
.new_engine
;
47 case VEH_AIRCRAFT
: return STR_ENGINE_PREVIEW_AIRCRAFT
;
48 case VEH_SHIP
: return STR_ENGINE_PREVIEW_SHIP
;
50 return GetRailTypeInfo(e
->u
.rail
.railtype
)->strings
.new_loco
;
54 static constexpr NWidgetPart _nested_engine_preview_widgets
[] = {
55 NWidget(NWID_HORIZONTAL
),
56 NWidget(WWT_CLOSEBOX
, COLOUR_LIGHT_BLUE
),
57 NWidget(WWT_CAPTION
, COLOUR_LIGHT_BLUE
), SetDataTip(STR_ENGINE_PREVIEW_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
59 NWidget(WWT_PANEL
, COLOUR_LIGHT_BLUE
),
60 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_wide
, 0), SetPadding(WidgetDimensions::unscaled
.modalpopup
),
61 NWidget(WWT_EMPTY
, INVALID_COLOUR
, WID_EP_QUESTION
), SetMinimalSize(300, 0), SetFill(1, 0),
62 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(85, WidgetDimensions::unscaled
.hsep_wide
, 85),
63 NWidget(WWT_PUSHTXTBTN
, COLOUR_LIGHT_BLUE
, WID_EP_NO
), SetDataTip(STR_QUIT_NO
, STR_NULL
), SetFill(1, 0),
64 NWidget(WWT_PUSHTXTBTN
, COLOUR_LIGHT_BLUE
, WID_EP_YES
), SetDataTip(STR_QUIT_YES
, STR_NULL
), SetFill(1, 0),
70 struct EnginePreviewWindow
: Window
{
71 int vehicle_space
; // The space to show the vehicle image
73 EnginePreviewWindow(WindowDesc
&desc
, WindowNumber window_number
) : Window(desc
)
75 this->InitNested(window_number
);
77 /* There is no way to recover the window; so disallow closure via DEL; unless SHIFT+DEL */
78 this->flags
|= WF_STICKY
;
81 void UpdateWidgetSize(WidgetID widget
, Dimension
&size
, [[maybe_unused
]] const Dimension
&padding
, [[maybe_unused
]] Dimension
&fill
, [[maybe_unused
]] Dimension
&resize
) override
83 if (widget
!= WID_EP_QUESTION
) return;
85 /* Get size of engine sprite, on loan from depot_gui.cpp */
86 EngineID engine
= this->window_number
;
87 EngineImageType image_type
= EIT_PURCHASE
;
91 const Engine
*e
= Engine::Get(engine
);
93 default: NOT_REACHED();
94 case VEH_TRAIN
: GetTrainSpriteSize( engine
, x
, y
, x_offs
, y_offs
, image_type
); break;
95 case VEH_ROAD
: GetRoadVehSpriteSize( engine
, x
, y
, x_offs
, y_offs
, image_type
); break;
96 case VEH_SHIP
: GetShipSpriteSize( engine
, x
, y
, x_offs
, y_offs
, image_type
); break;
97 case VEH_AIRCRAFT
: GetAircraftSpriteSize(engine
, x
, y
, x_offs
, y_offs
, image_type
); break;
99 this->vehicle_space
= std::max
<int>(ScaleSpriteTrad(40), y
- y_offs
);
101 size
.width
= std::max(size
.width
, x
+ std::abs(x_offs
));
102 SetDParam(0, GetEngineCategoryName(engine
));
103 size
.height
= GetStringHeight(STR_ENGINE_PREVIEW_MESSAGE
, size
.width
) + WidgetDimensions::scaled
.vsep_wide
+ GetCharacterHeight(FS_NORMAL
) + this->vehicle_space
;
104 SetDParam(0, engine
);
105 size
.height
+= GetStringHeight(GetEngineInfoString(engine
), size
.width
);
108 void DrawWidget(const Rect
&r
, WidgetID widget
) const override
110 if (widget
!= WID_EP_QUESTION
) return;
112 EngineID engine
= this->window_number
;
113 SetDParam(0, GetEngineCategoryName(engine
));
114 int y
= DrawStringMultiLine(r
, STR_ENGINE_PREVIEW_MESSAGE
, TC_FROMSTRING
, SA_HOR_CENTER
| SA_TOP
) + WidgetDimensions::scaled
.vsep_wide
;
116 SetDParam(0, PackEngineNameDParam(engine
, EngineNameContext::PreviewNews
));
117 DrawString(r
.left
, r
.right
, y
, STR_ENGINE_NAME
, TC_BLACK
, SA_HOR_CENTER
);
118 y
+= GetCharacterHeight(FS_NORMAL
);
120 DrawVehicleEngine(r
.left
, r
.right
, this->width
>> 1, y
+ this->vehicle_space
/ 2, engine
, GetEnginePalette(engine
, _local_company
), EIT_PREVIEW
);
122 y
+= this->vehicle_space
;
123 DrawStringMultiLine(r
.left
, r
.right
, y
, r
.bottom
, GetEngineInfoString(engine
), TC_FROMSTRING
, SA_CENTER
);
126 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
130 Command
<CMD_WANT_ENGINE_PREVIEW
>::Post(this->window_number
);
133 if (!_shift_pressed
) this->Close();
138 void OnInvalidateData([[maybe_unused
]] int data
= 0, [[maybe_unused
]] bool gui_scope
= true) override
140 if (!gui_scope
) return;
142 EngineID engine
= this->window_number
;
143 if (Engine::Get(engine
)->preview_company
!= _local_company
) this->Close();
147 static WindowDesc
_engine_preview_desc(
148 WDP_CENTER
, nullptr, 0, 0,
149 WC_ENGINE_PREVIEW
, WC_NONE
,
151 _nested_engine_preview_widgets
155 void ShowEnginePreviewWindow(EngineID engine
)
157 AllocateWindowDescFront
<EnginePreviewWindow
>(_engine_preview_desc
, engine
);
161 * Get the capacity of an engine with articulated parts.
162 * @param engine The engine to get the capacity of.
163 * @return The capacity.
165 uint
GetTotalCapacityOfArticulatedParts(EngineID engine
)
167 CargoArray cap
= GetCapacityOfArticulatedParts(engine
);
168 return cap
.GetSum
<uint
>();
171 static StringID
GetTrainEngineInfoString(const Engine
*e
)
173 SetDParam(0, STR_ENGINE_PREVIEW_COST_WEIGHT
);
174 SetDParam(1, e
->GetCost());
175 SetDParam(2, e
->GetDisplayWeight());
177 SetDParam(3, (_settings_game
.vehicle
.train_acceleration_model
!= AM_ORIGINAL
&& GetRailTypeInfo(e
->u
.rail
.railtype
)->acceleration_type
!= 2) ? STR_ENGINE_PREVIEW_SPEED_POWER_MAX_TE
: STR_ENGINE_PREVIEW_SPEED_POWER
);
178 SetDParam(4, PackVelocity(e
->GetDisplayMaxSpeed(), e
->type
));
179 SetDParam(5, e
->GetPower());
180 SetDParam(6, e
->GetDisplayMaxTractiveEffort());
182 SetDParam(7, TimerGameEconomy::UsingWallclockUnits() ? STR_ENGINE_PREVIEW_RUNCOST_PERIOD
: STR_ENGINE_PREVIEW_RUNCOST_YEAR
);
183 SetDParam(8, e
->GetRunningCost());
185 SetDParam(9, STR_ENGINE_PREVIEW_CAPACITY
);
186 uint capacity
= GetTotalCapacityOfArticulatedParts(e
->index
);
187 SetDParam(10, capacity
!= 0 ? e
->GetDefaultCargoType() : INVALID_CARGO
);
188 SetDParam(11, capacity
);
190 return STR_ENGINE_PREVIEW_TEXT4
;
193 static StringID
GetAircraftEngineInfoString(const Engine
*e
)
195 CargoID cargo
= e
->GetDefaultCargoType();
196 uint16_t mail_capacity
;
197 uint capacity
= e
->GetDisplayDefaultCapacity(&mail_capacity
);
198 uint16_t range
= e
->GetRange();
200 SetDParam(0, STR_ENGINE_PREVIEW_COST_MAX_SPEED
);
201 SetDParam(1, e
->GetCost());
202 SetDParam(2, PackVelocity(e
->GetDisplayMaxSpeed(), e
->type
));
204 SetDParam(3, range
> 0 ? STR_ENGINE_PREVIEW_TYPE_RANGE
: STR_ENGINE_PREVIEW_TYPE
);
205 SetDParam(4, e
->GetAircraftTypeText());
208 SetDParam(7, TimerGameEconomy::UsingWallclockUnits() ? STR_ENGINE_PREVIEW_RUNCOST_PERIOD
: STR_ENGINE_PREVIEW_RUNCOST_YEAR
);
209 SetDParam(8, e
->GetRunningCost());
211 SetDParam(9, mail_capacity
> 0 ? STR_ENGINE_PREVIEW_CAPACITY_2
: STR_ENGINE_PREVIEW_CAPACITY
);
212 SetDParam(10, cargo
);
213 SetDParam(11, capacity
);
214 SetDParam(12, GetCargoIDByLabel(CT_MAIL
));
215 SetDParam(13, mail_capacity
);
217 return STR_ENGINE_PREVIEW_TEXT4
;
220 static StringID
GetRoadVehEngineInfoString(const Engine
*e
)
222 SetDParam(7, TimerGameEconomy::UsingWallclockUnits() ? STR_ENGINE_PREVIEW_RUNCOST_PERIOD
: STR_ENGINE_PREVIEW_RUNCOST_YEAR
);
223 SetDParam(8, e
->GetRunningCost());
225 SetDParam(9, STR_ENGINE_PREVIEW_CAPACITY
);
226 uint capacity
= GetTotalCapacityOfArticulatedParts(e
->index
);
227 SetDParam(10, capacity
!= 0 ? e
->GetDefaultCargoType() : INVALID_CARGO
);
228 SetDParam(11, capacity
);
230 if (_settings_game
.vehicle
.roadveh_acceleration_model
== AM_ORIGINAL
) {
231 SetDParam(0, STR_ENGINE_PREVIEW_COST_MAX_SPEED
);
232 SetDParam(1, e
->GetCost());
233 SetDParam(2, PackVelocity(e
->GetDisplayMaxSpeed(), e
->type
));
235 return STR_ENGINE_PREVIEW_TEXT3
;
237 SetDParam(0, STR_ENGINE_PREVIEW_COST_WEIGHT
);
238 SetDParam(1, e
->GetCost());
239 SetDParam(2, e
->GetDisplayWeight());
241 SetDParam(3, STR_ENGINE_PREVIEW_SPEED_POWER_MAX_TE
);
242 SetDParam(4, PackVelocity(e
->GetDisplayMaxSpeed(), e
->type
));
243 SetDParam(5, e
->GetPower());
244 SetDParam(6, e
->GetDisplayMaxTractiveEffort());
246 return STR_ENGINE_PREVIEW_TEXT4
;
250 static StringID
GetShipEngineInfoString(const Engine
*e
)
252 SetDParam(0, STR_ENGINE_PREVIEW_COST_MAX_SPEED
);
253 SetDParam(1, e
->GetCost());
254 SetDParam(2, PackVelocity(e
->GetDisplayMaxSpeed(), e
->type
));
256 SetDParam(7, TimerGameEconomy::UsingWallclockUnits() ? STR_ENGINE_PREVIEW_RUNCOST_PERIOD
: STR_ENGINE_PREVIEW_RUNCOST_YEAR
);
257 SetDParam(8, e
->GetRunningCost());
259 SetDParam(9, STR_ENGINE_PREVIEW_CAPACITY
);
260 SetDParam(10, e
->GetDefaultCargoType());
261 SetDParam(11, e
->GetDisplayDefaultCapacity());
263 return STR_ENGINE_PREVIEW_TEXT3
;
268 * Get a multi-line string with some technical data, describing the engine.
269 * @param engine Engine to describe.
270 * @return String describing the engine.
271 * @post \c DParam array is set up for printing the string.
273 StringID
GetEngineInfoString(EngineID engine
)
275 const Engine
*e
= Engine::Get(engine
);
279 return GetTrainEngineInfoString(e
);
282 return GetRoadVehEngineInfoString(e
);
285 return GetShipEngineInfoString(e
);
288 return GetAircraftEngineInfoString(e
);
290 default: NOT_REACHED();
296 * @param left Minimum horizontal position to use for drawing the engine
297 * @param right Maximum horizontal position to use for drawing the engine
298 * @param preferred_x Horizontal position to use for drawing the engine.
299 * @param y Vertical position to use for drawing the engine.
300 * @param engine Engine to draw.
301 * @param pal Palette to use for drawing.
303 void DrawVehicleEngine(int left
, int right
, int preferred_x
, int y
, EngineID engine
, PaletteID pal
, EngineImageType image_type
)
305 const Engine
*e
= Engine::Get(engine
);
309 DrawTrainEngine(left
, right
, preferred_x
, y
, engine
, pal
, image_type
);
313 DrawRoadVehEngine(left
, right
, preferred_x
, y
, engine
, pal
, image_type
);
317 DrawShipEngine(left
, right
, preferred_x
, y
, engine
, pal
, image_type
);
321 DrawAircraftEngine(left
, right
, preferred_x
, y
, engine
, pal
, image_type
);
324 default: NOT_REACHED();
329 * Sort all items using quick sort and given 'CompareItems' function
330 * @param el list to be sorted
331 * @param compare function for evaluation of the quicksort
333 void EngList_Sort(GUIEngineList
&el
, EngList_SortTypeFunction compare
)
335 if (el
.size() < 2) return;
336 std::sort(el
.begin(), el
.end(), compare
);
340 * Sort selected range of items (on indices @ <begin, begin+num_items-1>)
341 * @param el list to be sorted
342 * @param compare function for evaluation of the quicksort
343 * @param begin start of sorting
344 * @param num_items count of items to be sorted
346 void EngList_SortPartial(GUIEngineList
&el
, EngList_SortTypeFunction compare
, size_t begin
, size_t num_items
)
348 if (num_items
< 2) return;
349 assert(begin
< el
.size());
350 assert(begin
+ num_items
<= el
.size());
351 std::sort(el
.begin() + begin
, el
.begin() + begin
+ num_items
, compare
);