Fix #12973: Don't exclude high score after using sandbox
[openttd-github.git] / src / road_gui.cpp
blobcbbd132cdcc6200557eedfb4055734528acdc8cc
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file road_gui.cpp GUI for building roads. */
10 #include "stdafx.h"
11 #include "gui.h"
12 #include "window_gui.h"
13 #include "station_gui.h"
14 #include "terraform_gui.h"
15 #include "viewport_func.h"
16 #include "command_func.h"
17 #include "road_cmd.h"
18 #include "station_func.h"
19 #include "waypoint_func.h"
20 #include "window_func.h"
21 #include "vehicle_func.h"
22 #include "sound_func.h"
23 #include "company_func.h"
24 #include "tunnelbridge.h"
25 #include "tunnelbridge_map.h"
26 #include "tilehighlight_func.h"
27 #include "company_base.h"
28 #include "hotkeys.h"
29 #include "road_gui.h"
30 #include "zoom_func.h"
31 #include "dropdown_type.h"
32 #include "dropdown_func.h"
33 #include "engine_base.h"
34 #include "station_base.h"
35 #include "waypoint_base.h"
36 #include "strings_func.h"
37 #include "core/geometry_func.hpp"
38 #include "station_cmd.h"
39 #include "waypoint_cmd.h"
40 #include "road_cmd.h"
41 #include "tunnelbridge_cmd.h"
42 #include "newgrf_roadstop.h"
43 #include "picker_gui.h"
44 #include "timer/timer.h"
45 #include "timer/timer_game_calendar.h"
47 #include "widgets/road_widget.h"
49 #include "table/strings.h"
51 #include "safeguards.h"
53 static void ShowRVStationPicker(Window *parent, RoadStopType rs);
54 static void ShowRoadDepotPicker(Window *parent);
55 static void ShowBuildRoadWaypointPicker(Window *parent);
57 static bool _remove_button_clicked;
58 static bool _one_way_button_clicked;
60 static Axis _place_road_dir;
61 static bool _place_road_start_half_x;
62 static bool _place_road_start_half_y;
63 static bool _place_road_end_half;
65 static RoadType _cur_roadtype;
67 static DiagDirection _road_depot_orientation;
69 struct RoadWaypointPickerSelection {
70 RoadStopClassID sel_class; ///< Selected road waypoint class.
71 uint16_t sel_type; ///< Selected road waypoint type within the class.
73 static RoadWaypointPickerSelection _waypoint_gui; ///< Settings of the road waypoint picker.
75 struct RoadStopPickerSelection {
76 RoadStopClassID sel_class; ///< Selected road stop class.
77 uint16_t sel_type; ///< Selected road stop type within the class.
78 DiagDirection orientation; ///< Selected orientation of the road stop.
80 static RoadStopPickerSelection _roadstop_gui;
82 static bool IsRoadStopEverAvailable(const RoadStopSpec *spec, StationType type)
84 if (spec == nullptr) return true;
86 if (HasBit(spec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(_cur_roadtype)) return false;
87 if (HasBit(spec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(_cur_roadtype)) return false;
89 switch (spec->stop_type) {
90 case ROADSTOPTYPE_ALL: return true;
91 case ROADSTOPTYPE_PASSENGER: return type == STATION_BUS;
92 case ROADSTOPTYPE_FREIGHT: return type == STATION_TRUCK;
93 default: NOT_REACHED();
97 /**
98 * Check whether a road stop type can be built.
99 * @return true if building is allowed.
101 static bool IsRoadStopAvailable(const RoadStopSpec *spec, StationType type)
103 if (spec == nullptr) return true;
104 if (!IsRoadStopEverAvailable(spec, type)) return false;
106 if (!HasBit(spec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true;
108 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, spec, nullptr, INVALID_TILE, _cur_roadtype, type, 0);
109 if (cb_res == CALLBACK_FAILED) return true;
111 return Convert8bitBooleanCallback(spec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res);
114 void CcPlaySound_CONSTRUCTION_OTHER(Commands, const CommandCost &result, TileIndex tile)
116 if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
120 * Callback to start placing a bridge.
121 * @param tile Start tile of the bridge.
123 static void PlaceRoad_Bridge(TileIndex tile, Window *w)
125 if (IsBridgeTile(tile)) {
126 TileIndex other_tile = GetOtherTunnelBridgeEnd(tile);
127 Point pt = {0, 0};
128 w->OnPlaceMouseUp(VPM_X_OR_Y, DDSP_BUILD_BRIDGE, pt, other_tile, tile);
129 } else {
130 VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_BUILD_BRIDGE);
135 * Callback executed after a build road tunnel command has been called.
137 * @param result Whether the build succeeded.
138 * @param start_tile Starting tile of the tunnel.
140 void CcBuildRoadTunnel(Commands, const CommandCost &result, TileIndex start_tile)
142 if (result.Succeeded()) {
143 if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, start_tile);
144 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
146 DiagDirection start_direction = ReverseDiagDir(GetTunnelBridgeDirection(start_tile));
147 ConnectRoadToStructure(start_tile, start_direction);
149 TileIndex end_tile = GetOtherTunnelBridgeEnd(start_tile);
150 DiagDirection end_direction = ReverseDiagDir(GetTunnelBridgeDirection(end_tile));
151 ConnectRoadToStructure(end_tile, end_direction);
152 } else {
153 SetRedErrorSquare(_build_tunnel_endtile);
158 * If required, connects a new structure to an existing road or tram by building the missing roadbit.
159 * @param tile Tile containing the structure to connect.
160 * @param direction Direction to check.
162 void ConnectRoadToStructure(TileIndex tile, DiagDirection direction)
164 tile += TileOffsByDiagDir(direction);
165 /* if there is a roadpiece just outside of the station entrance, build a connecting route */
166 if (IsNormalRoadTile(tile)) {
167 if (GetRoadBits(tile, GetRoadTramType(_cur_roadtype)) != ROAD_NONE) {
168 Command<CMD_BUILD_ROAD>::Post(tile, DiagDirToRoadBits(ReverseDiagDir(direction)), _cur_roadtype, DRD_NONE, 0);
173 void CcRoadDepot(Commands, const CommandCost &result, TileIndex tile, RoadType, DiagDirection dir)
175 if (result.Failed()) return;
177 if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
178 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
179 ConnectRoadToStructure(tile, dir);
183 * Command callback for building road stops.
184 * @param result Result of the build road stop command.
185 * @param tile Start tile.
186 * @param width Width of the road stop.
187 * @param length Length of the road stop.
188 * @param is_drive_through False for normal stops, true for drive-through.
189 * @param dir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops.
190 * @param spec_class Road stop spec class.
191 * @param spec_index Road stop spec index.
192 * @see CmdBuildRoadStop
194 void CcRoadStop(Commands, const CommandCost &result, TileIndex tile, uint8_t width, uint8_t length, RoadStopType, bool is_drive_through,
195 DiagDirection dir, RoadType, RoadStopClassID spec_class, uint16_t spec_index, StationID, bool)
197 if (result.Failed()) return;
199 if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
200 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
202 bool connect_to_road = true;
203 if ((uint)spec_class < RoadStopClass::GetClassCount() && spec_index < RoadStopClass::Get(spec_class)->GetSpecCount()) {
204 const RoadStopSpec *roadstopspec = RoadStopClass::Get(spec_class)->GetSpec(spec_index);
205 if (roadstopspec != nullptr && HasBit(roadstopspec->flags, RSF_NO_AUTO_ROAD_CONNECTION)) connect_to_road = false;
208 if (connect_to_road) {
209 TileArea roadstop_area(tile, width, length);
210 for (TileIndex cur_tile : roadstop_area) {
211 ConnectRoadToStructure(cur_tile, dir);
212 /* For a drive-through road stop build connecting road for other entrance. */
213 if (is_drive_through) ConnectRoadToStructure(cur_tile, ReverseDiagDir(dir));
219 * Place a new road stop.
220 * @param start_tile First tile of the area.
221 * @param end_tile Last tile of the area.
222 * @param stop_type Type of stop (bus/truck).
223 * @param adjacent Allow stations directly adjacent to other stations.
224 * @param rt The roadtypes.
225 * @param err_msg Error message to show.
226 * @see CcRoadStop()
228 static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg)
230 TileArea ta(start_tile, end_tile);
231 DiagDirection ddir = _roadstop_gui.orientation;
232 bool drive_through = ddir >= DIAGDIR_END;
233 if (drive_through) ddir = static_cast<DiagDirection>(ddir - DIAGDIR_END); // Adjust picker result to actual direction.
234 RoadStopClassID spec_class = _roadstop_gui.sel_class;
235 uint16_t spec_index = _roadstop_gui.sel_type;
237 auto proc = [=](bool test, StationID to_join) -> bool {
238 if (test) {
239 return Command<CMD_BUILD_ROAD_STOP>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_ROAD_STOP>()), ta.tile, ta.w, ta.h, stop_type, drive_through,
240 ddir, rt, spec_class, spec_index, INVALID_STATION, adjacent).Succeeded();
241 } else {
242 return Command<CMD_BUILD_ROAD_STOP>::Post(err_msg, CcRoadStop, ta.tile, ta.w, ta.h, stop_type, drive_through,
243 ddir, rt, spec_class, spec_index, to_join, adjacent);
247 ShowSelectStationIfNeeded(ta, proc);
251 * Place a road waypoint.
252 * @param tile Position to start dragging a waypoint.
254 static void PlaceRoad_Waypoint(TileIndex tile)
256 if (_remove_button_clicked) {
257 VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_ROAD_WAYPOINT);
258 return;
261 Axis axis = GetAxisForNewRoadWaypoint(tile);
262 if (IsValidAxis(axis)) {
263 /* Valid tile for waypoints */
264 VpStartPlaceSizing(tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_ROAD_WAYPOINT);
265 VpSetPlaceSizingLimit(_settings_game.station.station_spread);
266 } else {
267 /* Tile where we can't build road waypoints. This is always going to fail,
268 * but provides the user with a proper error message. */
269 Command<CMD_BUILD_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, tile, AXIS_X, 1, 1, ROADSTOP_CLASS_WAYP, 0, INVALID_STATION, false);
274 * Callback for placing a bus station.
275 * @param tile Position to place the station.
277 static void PlaceRoad_BusStation(TileIndex tile)
279 if (_remove_button_clicked) {
280 VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP);
281 } else {
282 if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop.
283 VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP);
284 } else {
285 VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP);
287 VpSetPlaceSizingLimit(_settings_game.station.station_spread);
292 * Callback for placing a truck station.
293 * @param tile Position to place the station.
295 static void PlaceRoad_TruckStation(TileIndex tile)
297 if (_remove_button_clicked) {
298 VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_TRUCKSTOP);
299 } else {
300 if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop.
301 VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
302 } else {
303 VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
305 VpSetPlaceSizingLimit(_settings_game.station.station_spread);
309 typedef void OnButtonClick(Window *w);
312 * Toggles state of the Remove button of Build road toolbar
313 * @param w window the button belongs to
315 static void ToggleRoadButton_Remove(Window *w)
317 w->ToggleWidgetLoweredState(WID_ROT_REMOVE);
318 w->SetWidgetDirty(WID_ROT_REMOVE);
319 _remove_button_clicked = w->IsWidgetLowered(WID_ROT_REMOVE);
320 SetSelectionRed(_remove_button_clicked);
324 * Updates the Remove button because of Ctrl state change
325 * @param w window the button belongs to
326 * @return true iff the remove button was changed
328 static bool RoadToolbar_CtrlChanged(Window *w)
330 if (w->IsWidgetDisabled(WID_ROT_REMOVE)) return false;
332 /* allow ctrl to switch remove mode only for these widgets */
333 for (WidgetID i = WID_ROT_ROAD_X; i <= WID_ROT_AUTOROAD; i++) {
334 if (w->IsWidgetLowered(i)) {
335 ToggleRoadButton_Remove(w);
336 return true;
340 return false;
343 /** Road toolbar window handler. */
344 struct BuildRoadToolbarWindow : Window {
345 RoadType roadtype; ///< Road type to build.
346 const RoadTypeInfo *rti; ///< Information about current road type
347 int last_started_action; ///< Last started user action.
349 BuildRoadToolbarWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
351 this->Initialize(_cur_roadtype);
352 this->CreateNestedTree();
353 this->SetupRoadToolbar();
354 this->FinishInitNested(window_number);
355 this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
357 if (RoadTypeIsRoad(this->roadtype)) {
358 this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true);
361 this->OnInvalidateData();
362 this->last_started_action = INVALID_WID_ROT;
364 if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this);
367 void Close([[maybe_unused]] int data = 0) override
369 if (_game_mode == GM_NORMAL && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true);
370 if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
371 this->Window::Close();
375 * Some data on this window has become invalid.
376 * @param data Information about the changed data.
377 * @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.
379 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
381 if (!gui_scope) return;
382 RoadTramType rtt = GetRoadTramType(this->roadtype);
384 bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD, rtt);
385 this->SetWidgetsDisabledState(!can_build,
386 WID_ROT_DEPOT,
387 WID_ROT_BUILD_WAYPOINT,
388 WID_ROT_BUS_STATION,
389 WID_ROT_TRUCK_STATION);
390 if (!can_build) {
391 CloseWindowById(WC_BUS_STATION, TRANSPORT_ROAD);
392 CloseWindowById(WC_TRUCK_STATION, TRANSPORT_ROAD);
393 CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
394 CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD);
397 if (_game_mode != GM_EDITOR) {
398 if (!can_build) {
399 /* Show in the tooltip why this button is disabled. */
400 this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
401 this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
402 this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
403 this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
404 } else {
405 this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT);
406 this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT : STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT);
407 this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION);
408 this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION);
413 void Initialize(RoadType roadtype)
415 assert(roadtype < ROADTYPE_END);
416 this->roadtype = roadtype;
417 this->rti = GetRoadTypeInfo(this->roadtype);
421 * Configures the road toolbar for roadtype given
422 * @param roadtype the roadtype to display
424 void SetupRoadToolbar()
426 this->GetWidget<NWidgetCore>(WID_ROT_ROAD_X)->widget_data = rti->gui_sprites.build_x_road;
427 this->GetWidget<NWidgetCore>(WID_ROT_ROAD_Y)->widget_data = rti->gui_sprites.build_y_road;
428 this->GetWidget<NWidgetCore>(WID_ROT_AUTOROAD)->widget_data = rti->gui_sprites.auto_road;
429 if (_game_mode != GM_EDITOR) {
430 this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot;
432 this->GetWidget<NWidgetCore>(WID_ROT_CONVERT_ROAD)->widget_data = rti->gui_sprites.convert_road;
433 this->GetWidget<NWidgetCore>(WID_ROT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel;
437 * Switch to another road type.
438 * @param roadtype New road type.
440 void ModifyRoadType(RoadType roadtype)
442 this->Initialize(roadtype);
443 this->SetupRoadToolbar();
444 this->ReInit();
447 void SetStringParameters(WidgetID widget) const override
449 if (widget == WID_ROT_CAPTION) {
450 if (this->rti->max_speed > 0) {
451 SetDParam(0, STR_TOOLBAR_RAILTYPE_VELOCITY);
452 SetDParam(1, this->rti->strings.toolbar_caption);
453 SetDParam(2, PackVelocity(this->rti->max_speed / 2, VEH_ROAD));
454 } else {
455 SetDParam(0, this->rti->strings.toolbar_caption);
461 * Update the remove button lowered state of the road toolbar
463 * @param clicked_widget The widget which the client clicked just now
465 void UpdateOptionWidgetStatus(RoadToolbarWidgets clicked_widget)
467 /* The remove and the one way button state is driven
468 * by the other buttons so they don't act on themselves.
469 * Both are only valid if they are able to apply as options. */
470 switch (clicked_widget) {
471 case WID_ROT_REMOVE:
472 if (RoadTypeIsRoad(this->roadtype)) {
473 this->RaiseWidget(WID_ROT_ONE_WAY);
474 this->SetWidgetDirty(WID_ROT_ONE_WAY);
477 break;
479 case WID_ROT_ONE_WAY:
480 this->RaiseWidget(WID_ROT_REMOVE);
481 this->SetWidgetDirty(WID_ROT_REMOVE);
482 break;
484 case WID_ROT_BUS_STATION:
485 case WID_ROT_TRUCK_STATION:
486 case WID_ROT_BUILD_WAYPOINT:
487 if (RoadTypeIsRoad(this->roadtype)) this->DisableWidget(WID_ROT_ONE_WAY);
488 this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget));
489 break;
491 case WID_ROT_ROAD_X:
492 case WID_ROT_ROAD_Y:
493 case WID_ROT_AUTOROAD:
494 this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget));
495 if (RoadTypeIsRoad(this->roadtype)) {
496 this->SetWidgetDisabledState(WID_ROT_ONE_WAY, !this->IsWidgetLowered(clicked_widget));
498 break;
500 default:
501 /* When any other buttons than road/station, raise and
502 * disable the removal button */
503 this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
504 this->SetWidgetLoweredState(WID_ROT_REMOVE, false);
506 if (RoadTypeIsRoad(this->roadtype)) {
507 this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true);
508 this->SetWidgetLoweredState(WID_ROT_ONE_WAY, false);
511 break;
515 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
517 _remove_button_clicked = false;
518 _one_way_button_clicked = false;
519 switch (widget) {
520 case WID_ROT_ROAD_X:
521 HandlePlacePushButton(this, WID_ROT_ROAD_X, this->rti->cursor.road_nwse, HT_RECT);
522 this->last_started_action = widget;
523 break;
525 case WID_ROT_ROAD_Y:
526 HandlePlacePushButton(this, WID_ROT_ROAD_Y, this->rti->cursor.road_swne, HT_RECT);
527 this->last_started_action = widget;
528 break;
530 case WID_ROT_AUTOROAD:
531 HandlePlacePushButton(this, WID_ROT_AUTOROAD, this->rti->cursor.autoroad, HT_RECT);
532 this->last_started_action = widget;
533 break;
535 case WID_ROT_DEMOLISH:
536 HandlePlacePushButton(this, WID_ROT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL);
537 this->last_started_action = widget;
538 break;
540 case WID_ROT_DEPOT:
541 if (HandlePlacePushButton(this, WID_ROT_DEPOT, this->rti->cursor.depot, HT_RECT)) {
542 ShowRoadDepotPicker(this);
543 this->last_started_action = widget;
545 break;
547 case WID_ROT_BUILD_WAYPOINT:
548 this->last_started_action = widget;
549 if (HandlePlacePushButton(this, WID_ROT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT)) {
550 ShowBuildRoadWaypointPicker(this);
552 break;
554 case WID_ROT_BUS_STATION:
555 if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT)) {
556 ShowRVStationPicker(this, ROADSTOP_BUS);
557 this->last_started_action = widget;
559 break;
561 case WID_ROT_TRUCK_STATION:
562 if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, HT_RECT)) {
563 ShowRVStationPicker(this, ROADSTOP_TRUCK);
564 this->last_started_action = widget;
566 break;
568 case WID_ROT_ONE_WAY:
569 if (this->IsWidgetDisabled(WID_ROT_ONE_WAY)) return;
570 this->SetDirty();
571 this->ToggleWidgetLoweredState(WID_ROT_ONE_WAY);
572 SetSelectionRed(false);
573 break;
575 case WID_ROT_BUILD_BRIDGE:
576 HandlePlacePushButton(this, WID_ROT_BUILD_BRIDGE, SPR_CURSOR_BRIDGE, HT_RECT);
577 this->last_started_action = widget;
578 break;
580 case WID_ROT_BUILD_TUNNEL:
581 HandlePlacePushButton(this, WID_ROT_BUILD_TUNNEL, this->rti->cursor.tunnel, HT_SPECIAL);
582 this->last_started_action = widget;
583 break;
585 case WID_ROT_REMOVE:
586 if (this->IsWidgetDisabled(WID_ROT_REMOVE)) return;
588 CloseWindowById(WC_SELECT_STATION, 0);
589 ToggleRoadButton_Remove(this);
590 if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
591 break;
593 case WID_ROT_CONVERT_ROAD:
594 HandlePlacePushButton(this, WID_ROT_CONVERT_ROAD, this->rti->cursor.convert_road, HT_RECT);
595 this->last_started_action = widget;
596 break;
598 default: NOT_REACHED();
600 this->UpdateOptionWidgetStatus((RoadToolbarWidgets)widget);
601 if (_ctrl_pressed) RoadToolbar_CtrlChanged(this);
604 EventState OnHotkey(int hotkey) override
606 MarkTileDirtyByTile(TileVirtXY(_thd.pos.x, _thd.pos.y)); // redraw tile selection
607 return Window::OnHotkey(hotkey);
610 void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
612 _remove_button_clicked = this->IsWidgetLowered(WID_ROT_REMOVE);
613 _one_way_button_clicked = RoadTypeIsRoad(this->roadtype) ? this->IsWidgetLowered(WID_ROT_ONE_WAY) : false;
614 switch (this->last_started_action) {
615 case WID_ROT_ROAD_X:
616 _place_road_dir = AXIS_X;
617 _place_road_start_half_x = _tile_fract_coords.x >= 8;
618 VpStartPlaceSizing(tile, VPM_FIX_Y, DDSP_PLACE_ROAD_X_DIR);
619 break;
621 case WID_ROT_ROAD_Y:
622 _place_road_dir = AXIS_Y;
623 _place_road_start_half_y = _tile_fract_coords.y >= 8;
624 VpStartPlaceSizing(tile, VPM_FIX_X, DDSP_PLACE_ROAD_Y_DIR);
625 break;
627 case WID_ROT_AUTOROAD:
628 _place_road_dir = INVALID_AXIS;
629 _place_road_start_half_x = _tile_fract_coords.x >= 8;
630 _place_road_start_half_y = _tile_fract_coords.y >= 8;
631 VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_PLACE_AUTOROAD);
632 break;
634 case WID_ROT_DEMOLISH:
635 PlaceProc_DemolishArea(tile);
636 break;
638 case WID_ROT_DEPOT:
639 Command<CMD_BUILD_ROAD_DEPOT>::Post(this->rti->strings.err_depot, CcRoadDepot,
640 tile, _cur_roadtype, _road_depot_orientation);
641 break;
643 case WID_ROT_BUILD_WAYPOINT:
644 PlaceRoad_Waypoint(tile);
645 break;
647 case WID_ROT_BUS_STATION:
648 PlaceRoad_BusStation(tile);
649 break;
651 case WID_ROT_TRUCK_STATION:
652 PlaceRoad_TruckStation(tile);
653 break;
655 case WID_ROT_BUILD_BRIDGE:
656 PlaceRoad_Bridge(tile, this);
657 break;
659 case WID_ROT_BUILD_TUNNEL:
660 Command<CMD_BUILD_TUNNEL>::Post(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRoadTunnel,
661 tile, TRANSPORT_ROAD, _cur_roadtype);
662 break;
664 case WID_ROT_CONVERT_ROAD:
665 VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_ROAD);
666 break;
668 default: NOT_REACHED();
672 void OnPlaceObjectAbort() override
674 if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true);
676 this->RaiseButtons();
677 this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
678 this->SetWidgetDirty(WID_ROT_REMOVE);
680 if (RoadTypeIsRoad(this->roadtype)) {
681 this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true);
682 this->SetWidgetDirty(WID_ROT_ONE_WAY);
685 CloseWindowById(WC_BUS_STATION, TRANSPORT_ROAD);
686 CloseWindowById(WC_TRUCK_STATION, TRANSPORT_ROAD);
687 CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
688 CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD);
689 CloseWindowById(WC_SELECT_STATION, 0);
690 CloseWindowByClass(WC_BUILD_BRIDGE);
693 void OnPlaceDrag(ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt) override
695 /* Here we update the end tile flags
696 * of the road placement actions.
697 * At first we reset the end halfroad
698 * bits and if needed we set them again. */
699 switch (select_proc) {
700 case DDSP_PLACE_ROAD_X_DIR:
701 _place_road_end_half = pt.x & 8;
702 break;
704 case DDSP_PLACE_ROAD_Y_DIR:
705 _place_road_end_half = pt.y & 8;
706 break;
708 case DDSP_PLACE_AUTOROAD:
709 /* For autoroad we need to update the
710 * direction of the road */
711 if (_thd.size.x > _thd.size.y || (_thd.size.x == _thd.size.y &&
712 ( (_tile_fract_coords.x < _tile_fract_coords.y && (_tile_fract_coords.x + _tile_fract_coords.y) < 16) ||
713 (_tile_fract_coords.x > _tile_fract_coords.y && (_tile_fract_coords.x + _tile_fract_coords.y) > 16) ))) {
714 /* Set dir = X */
715 _place_road_dir = AXIS_X;
716 _place_road_end_half = pt.x & 8;
717 } else {
718 /* Set dir = Y */
719 _place_road_dir = AXIS_Y;
720 _place_road_end_half = pt.y & 8;
723 break;
725 default:
726 break;
729 VpSelectTilesWithMethod(pt.x, pt.y, select_method);
732 void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override
734 if (pt.x != -1) {
735 switch (select_proc) {
736 default: NOT_REACHED();
737 case DDSP_BUILD_BRIDGE:
738 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
739 ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, _cur_roadtype);
740 break;
742 case DDSP_DEMOLISH_AREA:
743 GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
744 break;
746 case DDSP_PLACE_ROAD_X_DIR:
747 case DDSP_PLACE_ROAD_Y_DIR:
748 case DDSP_PLACE_AUTOROAD: {
749 bool start_half = _place_road_dir == AXIS_Y ? _place_road_start_half_y : _place_road_start_half_x;
751 if (_remove_button_clicked) {
752 Command<CMD_REMOVE_LONG_ROAD>::Post(this->rti->strings.err_remove_road, CcPlaySound_CONSTRUCTION_OTHER,
753 end_tile, start_tile, _cur_roadtype, _place_road_dir, start_half, _place_road_end_half);
754 } else {
755 Command<CMD_BUILD_LONG_ROAD>::Post(this->rti->strings.err_build_road, CcPlaySound_CONSTRUCTION_OTHER,
756 end_tile, start_tile, _cur_roadtype, _place_road_dir, _one_way_button_clicked ? DRD_NORTHBOUND : DRD_NONE, start_half, _place_road_end_half, false);
758 break;
761 case DDSP_BUILD_ROAD_WAYPOINT:
762 case DDSP_REMOVE_ROAD_WAYPOINT:
763 if (this->IsWidgetLowered(WID_ROT_BUILD_WAYPOINT)) {
764 if (_remove_button_clicked) {
765 Command<CMD_REMOVE_FROM_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile);
766 } else {
767 TileArea ta(start_tile, end_tile);
768 Axis axis = select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y;
769 bool adjacent = _ctrl_pressed;
771 auto proc = [=](bool test, StationID to_join) -> bool {
772 if (test) {
773 return Command<CMD_BUILD_ROAD_WAYPOINT>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_ROAD_WAYPOINT>()), ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, INVALID_STATION, adjacent).Succeeded();
774 } else {
775 return Command<CMD_BUILD_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, to_join, adjacent);
779 ShowSelectRoadWaypointIfNeeded(ta, proc);
782 break;
784 case DDSP_BUILD_BUSSTOP:
785 case DDSP_REMOVE_BUSSTOP:
786 if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui.sel_class), ROADSTOP_BUS, _cur_roadtype)) {
787 if (_remove_button_clicked) {
788 TileArea ta(start_tile, end_tile);
789 Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_BUS], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_BUS, _ctrl_pressed);
790 } else {
791 PlaceRoadStop(start_tile, end_tile, ROADSTOP_BUS, _ctrl_pressed, _cur_roadtype, this->rti->strings.err_build_station[ROADSTOP_BUS]);
794 break;
796 case DDSP_BUILD_TRUCKSTOP:
797 case DDSP_REMOVE_TRUCKSTOP:
798 if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui.sel_class), ROADSTOP_TRUCK, _cur_roadtype)) {
799 if (_remove_button_clicked) {
800 TileArea ta(start_tile, end_tile);
801 Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_TRUCK], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_TRUCK, _ctrl_pressed);
802 } else {
803 PlaceRoadStop(start_tile, end_tile, ROADSTOP_TRUCK, _ctrl_pressed, _cur_roadtype, this->rti->strings.err_build_station[ROADSTOP_TRUCK]);
806 break;
808 case DDSP_CONVERT_ROAD:
809 Command<CMD_CONVERT_ROAD>::Post(rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype);
810 break;
815 void OnPlacePresize([[maybe_unused]] Point pt, TileIndex tile) override
817 Command<CMD_BUILD_TUNNEL>::Do(DC_AUTO, tile, TRANSPORT_ROAD, _cur_roadtype);
818 VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile);
821 EventState OnCTRLStateChange() override
823 if (RoadToolbar_CtrlChanged(this)) return ES_HANDLED;
824 return ES_NOT_HANDLED;
827 void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
829 if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_ROT_BUILD_WAYPOINT)) CheckRedrawRoadWaypointCoverage(this);
833 * Handler for global hotkeys of the BuildRoadToolbarWindow.
834 * @param hotkey Hotkey
835 * @param last_build Last build road type
836 * @return ES_HANDLED if hotkey was accepted.
838 static EventState RoadTramToolbarGlobalHotkeys(int hotkey, RoadType last_build, RoadTramType rtt)
840 Window *w = nullptr;
841 switch (_game_mode) {
842 case GM_NORMAL:
843 w = ShowBuildRoadToolbar(last_build);
844 break;
846 case GM_EDITOR:
847 if ((GetRoadTypes(true) & ((rtt == RTT_ROAD) ? ~_roadtypes_type : _roadtypes_type)) == ROADTYPES_NONE) return ES_NOT_HANDLED;
848 w = ShowBuildRoadScenToolbar(last_build);
849 break;
851 default:
852 break;
855 if (w == nullptr) return ES_NOT_HANDLED;
856 return w->OnHotkey(hotkey);
859 static EventState RoadToolbarGlobalHotkeys(int hotkey)
861 extern RoadType _last_built_roadtype;
862 return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_roadtype, RTT_ROAD);
865 static EventState TramToolbarGlobalHotkeys(int hotkey)
867 extern RoadType _last_built_tramtype;
868 return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_tramtype, RTT_TRAM);
871 static inline HotkeyList road_hotkeys{"roadtoolbar", {
872 Hotkey('1', "build_x", WID_ROT_ROAD_X),
873 Hotkey('2', "build_y", WID_ROT_ROAD_Y),
874 Hotkey('3', "autoroad", WID_ROT_AUTOROAD),
875 Hotkey('4', "demolish", WID_ROT_DEMOLISH),
876 Hotkey('5', "depot", WID_ROT_DEPOT),
877 Hotkey('6', "bus_station", WID_ROT_BUS_STATION),
878 Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION),
879 Hotkey('8', "oneway", WID_ROT_ONE_WAY),
880 Hotkey('9', "waypoint", WID_ROT_BUILD_WAYPOINT),
881 Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE),
882 Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL),
883 Hotkey('R', "remove", WID_ROT_REMOVE),
884 Hotkey('C', "convert", WID_ROT_CONVERT_ROAD),
885 }, RoadToolbarGlobalHotkeys};
887 static inline HotkeyList tram_hotkeys{"tramtoolbar", {
888 Hotkey('1', "build_x", WID_ROT_ROAD_X),
889 Hotkey('2', "build_y", WID_ROT_ROAD_Y),
890 Hotkey('3', "autoroad", WID_ROT_AUTOROAD),
891 Hotkey('4', "demolish", WID_ROT_DEMOLISH),
892 Hotkey('5', "depot", WID_ROT_DEPOT),
893 Hotkey('6', "bus_station", WID_ROT_BUS_STATION),
894 Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION),
895 Hotkey('9', "waypoint", WID_ROT_BUILD_WAYPOINT),
896 Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE),
897 Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL),
898 Hotkey('R', "remove", WID_ROT_REMOVE),
899 Hotkey('C', "convert", WID_ROT_CONVERT_ROAD),
900 }, TramToolbarGlobalHotkeys};
903 static constexpr NWidgetPart _nested_build_road_widgets[] = {
904 NWidget(NWID_HORIZONTAL),
905 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
906 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_JUST_STRING2, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), SetTextStyle(TC_WHITE),
907 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
908 EndContainer(),
909 NWidget(NWID_HORIZONTAL),
910 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_X),
911 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_X_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION),
912 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_Y),
913 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_Y_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION),
914 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_AUTOROAD),
915 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOROAD, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD),
916 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH),
917 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
918 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT),
919 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT),
920 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
921 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT),
922 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
923 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION),
924 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION),
925 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRUCK_BAY, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY),
926 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
927 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ONE_WAY),
928 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_ONE_WAY, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_ONE_WAY_ROAD),
929 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE),
930 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_BRIDGE),
931 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL),
932 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL),
933 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
934 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD),
935 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
936 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD),
937 EndContainer(),
940 static WindowDesc _build_road_desc(
941 WDP_ALIGN_TOOLBAR, "toolbar_road", 0, 0,
942 WC_BUILD_TOOLBAR, WC_NONE,
943 WDF_CONSTRUCTION,
944 _nested_build_road_widgets,
945 &BuildRoadToolbarWindow::road_hotkeys
948 static constexpr NWidgetPart _nested_build_tramway_widgets[] = {
949 NWidget(NWID_HORIZONTAL),
950 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
951 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_JUST_STRING2, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), SetTextStyle(TC_WHITE),
952 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
953 EndContainer(),
954 NWidget(NWID_HORIZONTAL),
955 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_X),
956 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_X_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION),
957 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_Y),
958 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_Y_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION),
959 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_AUTOROAD),
960 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM),
961 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH),
962 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
963 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT),
964 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT),
965 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
966 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT),
967 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
968 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION),
969 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION),
970 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRUCK_BAY, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION),
971 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
972 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE),
973 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE),
974 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL),
975 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL),
976 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
977 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS),
978 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
979 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM),
980 EndContainer(),
983 static WindowDesc _build_tramway_desc(
984 WDP_ALIGN_TOOLBAR, "toolbar_tramway", 0, 0,
985 WC_BUILD_TOOLBAR, WC_NONE,
986 WDF_CONSTRUCTION,
987 _nested_build_tramway_widgets,
988 &BuildRoadToolbarWindow::tram_hotkeys
992 * Open the build road toolbar window
994 * If the terraform toolbar is linked to the toolbar, that window is also opened.
996 * @return newly opened road toolbar, or nullptr if the toolbar could not be opened.
998 Window *ShowBuildRoadToolbar(RoadType roadtype)
1000 if (!Company::IsValidID(_local_company)) return nullptr;
1001 if (!ValParamRoadType(roadtype)) return nullptr;
1003 CloseWindowByClass(WC_BUILD_TOOLBAR);
1004 _cur_roadtype = roadtype;
1006 return AllocateWindowDescFront<BuildRoadToolbarWindow>(RoadTypeIsRoad(_cur_roadtype) ? _build_road_desc : _build_tramway_desc, TRANSPORT_ROAD);
1009 static constexpr NWidgetPart _nested_build_road_scen_widgets[] = {
1010 NWidget(NWID_HORIZONTAL),
1011 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1012 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_JUST_STRING2, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), SetTextStyle(TC_WHITE),
1013 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
1014 EndContainer(),
1015 NWidget(NWID_HORIZONTAL),
1016 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_X),
1017 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_X_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION),
1018 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_Y),
1019 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_Y_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION),
1020 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_AUTOROAD),
1021 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOROAD, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD),
1022 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH),
1023 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
1024 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
1025 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ONE_WAY),
1026 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_ONE_WAY, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_ONE_WAY_ROAD),
1027 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE),
1028 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_BRIDGE),
1029 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL),
1030 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL),
1031 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
1032 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD),
1033 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
1034 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD),
1035 EndContainer(),
1038 static WindowDesc _build_road_scen_desc(
1039 WDP_AUTO, "toolbar_road_scen", 0, 0,
1040 WC_SCEN_BUILD_TOOLBAR, WC_NONE,
1041 WDF_CONSTRUCTION,
1042 _nested_build_road_scen_widgets,
1043 &BuildRoadToolbarWindow::road_hotkeys
1046 static constexpr NWidgetPart _nested_build_tramway_scen_widgets[] = {
1047 NWidget(NWID_HORIZONTAL),
1048 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1049 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_JUST_STRING2, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), SetTextStyle(TC_WHITE),
1050 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
1051 EndContainer(),
1052 NWidget(NWID_HORIZONTAL),
1053 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_X),
1054 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_X_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION),
1055 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_Y),
1056 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_Y_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION),
1057 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_AUTOROAD),
1058 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM),
1059 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH),
1060 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
1061 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
1062 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE),
1063 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE),
1064 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL),
1065 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL),
1066 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
1067 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS),
1068 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
1069 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM),
1070 EndContainer(),
1073 static WindowDesc _build_tramway_scen_desc(
1074 WDP_AUTO, "toolbar_tram_scen", 0, 0,
1075 WC_SCEN_BUILD_TOOLBAR, WC_NONE,
1076 WDF_CONSTRUCTION,
1077 _nested_build_tramway_scen_widgets,
1078 &BuildRoadToolbarWindow::tram_hotkeys
1082 * Show the road building toolbar in the scenario editor.
1083 * @return The just opened toolbar, or \c nullptr if the toolbar was already open.
1085 Window *ShowBuildRoadScenToolbar(RoadType roadtype)
1087 CloseWindowById(WC_SCEN_BUILD_TOOLBAR, TRANSPORT_ROAD);
1088 _cur_roadtype = roadtype;
1090 return AllocateWindowDescFront<BuildRoadToolbarWindow>(RoadTypeIsRoad(_cur_roadtype) ? _build_road_scen_desc : _build_tramway_scen_desc, TRANSPORT_ROAD);
1093 struct BuildRoadDepotWindow : public PickerWindowBase {
1094 BuildRoadDepotWindow(WindowDesc &desc, Window *parent) : PickerWindowBase(desc, parent)
1096 this->CreateNestedTree();
1098 this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
1099 if (RoadTypeIsTram(_cur_roadtype)) {
1100 this->GetWidget<NWidgetCore>(WID_BROD_CAPTION)->widget_data = STR_BUILD_DEPOT_TRAM_ORIENTATION_CAPTION;
1101 for (WidgetID i = WID_BROD_DEPOT_NE; i <= WID_BROD_DEPOT_NW; i++) {
1102 this->GetWidget<NWidgetCore>(i)->tool_tip = STR_BUILD_DEPOT_TRAM_ORIENTATION_SELECT_TOOLTIP;
1106 this->FinishInitNested(TRANSPORT_ROAD);
1109 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1111 if (!IsInsideMM(widget, WID_BROD_DEPOT_NE, WID_BROD_DEPOT_NW + 1)) return;
1113 size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal();
1114 size.height = ScaleGUITrad(48) + WidgetDimensions::scaled.fullbevel.Vertical();
1117 void DrawWidget(const Rect &r, WidgetID widget) const override
1119 if (!IsInsideMM(widget, WID_BROD_DEPOT_NE, WID_BROD_DEPOT_NW + 1)) return;
1121 DrawPixelInfo tmp_dpi;
1122 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
1123 if (FillDrawPixelInfo(&tmp_dpi, ir)) {
1124 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
1125 int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
1126 int y = (ir.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
1127 DrawRoadDepotSprite(x, y, (DiagDirection)(widget - WID_BROD_DEPOT_NE + DIAGDIR_NE), _cur_roadtype);
1131 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1133 switch (widget) {
1134 case WID_BROD_DEPOT_NW:
1135 case WID_BROD_DEPOT_NE:
1136 case WID_BROD_DEPOT_SW:
1137 case WID_BROD_DEPOT_SE:
1138 this->RaiseWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
1139 _road_depot_orientation = (DiagDirection)(widget - WID_BROD_DEPOT_NE);
1140 this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
1141 if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1142 this->SetDirty();
1143 break;
1145 default:
1146 break;
1151 static constexpr NWidgetPart _nested_build_road_depot_widgets[] = {
1152 NWidget(NWID_HORIZONTAL),
1153 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1154 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROD_CAPTION), SetDataTip(STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1155 EndContainer(),
1156 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
1157 NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled.picker),
1158 NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
1159 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_NW), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP),
1160 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SW), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP),
1161 EndContainer(),
1162 NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
1163 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_NE), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP),
1164 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SE), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP),
1165 EndContainer(),
1166 EndContainer(),
1167 EndContainer(),
1170 static WindowDesc _build_road_depot_desc(
1171 WDP_AUTO, nullptr, 0, 0,
1172 WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
1173 WDF_CONSTRUCTION,
1174 _nested_build_road_depot_widgets
1177 static void ShowRoadDepotPicker(Window *parent)
1179 new BuildRoadDepotWindow(_build_road_depot_desc, parent);
1182 template <RoadStopType roadstoptype>
1183 class RoadStopPickerCallbacks : public PickerCallbacksNewGRFClass<RoadStopClass> {
1184 public:
1185 RoadStopPickerCallbacks(const std::string &ini_group) : PickerCallbacksNewGRFClass<RoadStopClass>(ini_group) {}
1187 StringID GetClassTooltip() const override;
1188 StringID GetTypeTooltip() const override;
1190 bool IsActive() const override
1192 for (const auto &cls : RoadStopClass::Classes()) {
1193 if (IsWaypointClass(cls)) continue;
1194 for (const auto *spec : cls.Specs()) {
1195 if (spec == nullptr) continue;
1196 if (roadstoptype == ROADSTOP_TRUCK && spec->stop_type != ROADSTOPTYPE_FREIGHT && spec->stop_type != ROADSTOPTYPE_ALL) continue;
1197 if (roadstoptype == ROADSTOP_BUS && spec->stop_type != ROADSTOPTYPE_PASSENGER && spec->stop_type != ROADSTOPTYPE_ALL) continue;
1198 return true;
1201 return false;
1204 static bool IsClassChoice(const RoadStopClass &cls)
1206 return !IsWaypointClass(cls) && GetIfClassHasNewStopsByType(&cls, roadstoptype, _cur_roadtype);
1209 bool HasClassChoice() const override
1211 return std::count_if(std::begin(RoadStopClass::Classes()), std::end(RoadStopClass::Classes()), IsClassChoice);
1214 int GetSelectedClass() const override { return _roadstop_gui.sel_class; }
1215 void SetSelectedClass(int id) const override { _roadstop_gui.sel_class = this->GetClassIndex(id); }
1217 StringID GetClassName(int id) const override
1219 const auto *rsc = this->GetClass(id);
1220 if (!IsClassChoice(*rsc)) return INVALID_STRING_ID;
1221 return rsc->name;
1224 int GetSelectedType() const override { return _roadstop_gui.sel_type; }
1225 void SetSelectedType(int id) const override { _roadstop_gui.sel_type = id; }
1227 StringID GetTypeName(int cls_id, int id) const override
1229 const auto *spec = this->GetSpec(cls_id, id);
1230 if (!IsRoadStopEverAvailable(spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK)) return INVALID_STRING_ID;
1231 return (spec == nullptr) ? STR_STATION_CLASS_DFLT_ROADSTOP : spec->name;
1234 bool IsTypeAvailable(int cls_id, int id) const override
1236 const auto *spec = this->GetSpec(cls_id, id);
1237 return IsRoadStopAvailable(spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK);
1240 void DrawType(int x, int y, int cls_id, int id) const override
1242 const auto *spec = this->GetSpec(cls_id, id);
1243 if (spec == nullptr) {
1244 StationPickerDrawSprite(x, y, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui.orientation);
1245 } else {
1246 DiagDirection orientation = _roadstop_gui.orientation;
1247 if (orientation < DIAGDIR_END && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) orientation = DIAGDIR_END;
1248 DrawRoadStopTile(x, y, _cur_roadtype, spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, (uint8_t)orientation);
1252 void FillUsedItems(std::set<PickerItem> &items) override
1254 for (const Station *st : Station::Iterate()) {
1255 if (st->owner != _local_company) continue;
1256 if (roadstoptype == ROADSTOP_TRUCK && !(st->facilities & FACIL_TRUCK_STOP)) continue;
1257 if (roadstoptype == ROADSTOP_BUS && !(st->facilities & FACIL_BUS_STOP)) continue;
1258 items.insert({0, 0, ROADSTOP_CLASS_DFLT, 0}); // We would need to scan the map to find out if default is used.
1259 for (const auto &sm : st->roadstop_speclist) {
1260 if (sm.spec == nullptr) continue;
1261 if (roadstoptype == ROADSTOP_TRUCK && sm.spec->stop_type != ROADSTOPTYPE_FREIGHT && sm.spec->stop_type != ROADSTOPTYPE_ALL) continue;
1262 if (roadstoptype == ROADSTOP_BUS && sm.spec->stop_type != ROADSTOPTYPE_PASSENGER && sm.spec->stop_type != ROADSTOPTYPE_ALL) continue;
1263 items.insert({sm.grfid, sm.localidx, sm.spec->class_index, sm.spec->index});
1269 template <> StringID RoadStopPickerCallbacks<ROADSTOP_BUS>::GetClassTooltip() const { return STR_PICKER_ROADSTOP_BUS_CLASS_TOOLTIP; }
1270 template <> StringID RoadStopPickerCallbacks<ROADSTOP_BUS>::GetTypeTooltip() const { return STR_PICKER_ROADSTOP_BUS_TYPE_TOOLTIP; }
1272 template <> StringID RoadStopPickerCallbacks<ROADSTOP_TRUCK>::GetClassTooltip() const { return STR_PICKER_ROADSTOP_TRUCK_CLASS_TOOLTIP; }
1273 template <> StringID RoadStopPickerCallbacks<ROADSTOP_TRUCK>::GetTypeTooltip() const { return STR_PICKER_ROADSTOP_TRUCK_TYPE_TOOLTIP; }
1275 static RoadStopPickerCallbacks<ROADSTOP_BUS> _bus_callback_instance("fav_passenger_roadstops");
1276 static RoadStopPickerCallbacks<ROADSTOP_TRUCK> _truck_callback_instance("fav_freight_roadstops");
1278 static PickerCallbacks &GetRoadStopPickerCallbacks(RoadStopType rs)
1280 return rs == ROADSTOP_BUS ? static_cast<PickerCallbacks &>(_bus_callback_instance) : static_cast<PickerCallbacks &>(_truck_callback_instance);
1283 struct BuildRoadStationWindow : public PickerWindow {
1284 private:
1285 uint coverage_height; ///< Height of the coverage texts.
1287 void CheckOrientationValid()
1289 const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type);
1291 /* Raise and lower to ensure the correct widget is lowered after changing displayed orientation plane. */
1292 if (RoadTypeIsRoad(_cur_roadtype)) {
1293 this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
1294 this->GetWidget<NWidgetStacked>(WID_BROS_AVAILABLE_ORIENTATIONS)->SetDisplayedPlane((spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) ? 1 : 0);
1295 this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
1298 if (_roadstop_gui.orientation >= DIAGDIR_END) return;
1300 if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) {
1301 this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
1302 _roadstop_gui.orientation = DIAGDIR_END;
1303 this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
1304 this->SetDirty();
1305 CloseWindowById(WC_SELECT_STATION, 0);
1309 public:
1310 BuildRoadStationWindow(WindowDesc &desc, Window *parent, RoadStopType rs) : PickerWindow(desc, parent, TRANSPORT_ROAD, GetRoadStopPickerCallbacks(rs))
1312 this->coverage_height = 2 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
1314 /* Trams don't have non-drivethrough stations */
1315 if (RoadTypeIsTram(_cur_roadtype) && _roadstop_gui.orientation < DIAGDIR_END) {
1316 _roadstop_gui.orientation = DIAGDIR_END;
1318 this->ConstructWindow();
1320 const RoadTypeInfo *rti = GetRoadTypeInfo(_cur_roadtype);
1321 this->GetWidget<NWidgetCore>(WID_BROS_CAPTION)->widget_data = rti->strings.picker_title[rs];
1323 for (WidgetID i = RoadTypeIsTram(_cur_roadtype) ? WID_BROS_STATION_X : WID_BROS_STATION_NE; i < WID_BROS_LT_OFF; i++) {
1324 this->GetWidget<NWidgetCore>(i)->tool_tip = rti->strings.picker_tooltip[rs];
1327 this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
1328 this->LowerWidget(WID_BROS_LT_OFF + _settings_client.gui.station_show_coverage);
1330 this->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION;
1333 void Close([[maybe_unused]] int data = 0) override
1335 CloseWindowById(WC_SELECT_STATION, 0);
1336 this->PickerWindow::Close();
1339 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1341 this->PickerWindow::OnInvalidateData(data, gui_scope);
1343 if (gui_scope) {
1344 this->CheckOrientationValid();
1348 void OnPaint() override
1350 this->DrawWidgets();
1352 int rad = _settings_game.station.modified_catchment ? ((this->window_class == WC_BUS_STATION) ? CA_BUS : CA_TRUCK) : CA_UNMODIFIED;
1353 if (_settings_client.gui.station_show_coverage) {
1354 SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
1355 } else {
1356 SetTileSelectSize(1, 1);
1359 if (this->IsShaded()) return;
1361 /* 'Accepts' and 'Supplies' texts. */
1362 StationCoverageType sct = (this->window_class == WC_BUS_STATION) ? SCT_PASSENGERS_ONLY : SCT_NON_PASSENGERS_ONLY;
1363 Rect r = this->GetWidget<NWidgetBase>(WID_BROS_ACCEPTANCE)->GetCurrentRect();
1364 int top = r.top;
1365 top = DrawStationCoverageAreaText(r.left, r.right, top, sct, rad, false) + WidgetDimensions::scaled.vsep_normal;
1366 top = DrawStationCoverageAreaText(r.left, r.right, top, sct, rad, true);
1367 /* Resize background if the window is too small.
1368 * Never make the window smaller to avoid oscillating if the size change affects the acceptance.
1369 * (This is the case, if making the window bigger moves the mouse into the window.) */
1370 if (top > r.bottom) {
1371 this->coverage_height += top - r.bottom;
1372 this->ReInit();
1376 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1378 switch (widget) {
1379 case WID_BROS_STATION_NE:
1380 case WID_BROS_STATION_SE:
1381 case WID_BROS_STATION_SW:
1382 case WID_BROS_STATION_NW:
1383 case WID_BROS_STATION_X:
1384 case WID_BROS_STATION_Y:
1385 size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
1386 size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical();
1387 break;
1389 case WID_BROS_ACCEPTANCE:
1390 size.height = this->coverage_height;
1391 break;
1393 default:
1394 this->PickerWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
1395 break;
1400 * Simply to have a easier way to get the StationType for bus, truck and trams from the WindowClass.
1402 StationType GetRoadStationTypeByWindowClass(WindowClass window_class) const
1404 switch (window_class) {
1405 case WC_BUS_STATION: return STATION_BUS;
1406 case WC_TRUCK_STATION: return STATION_TRUCK;
1407 default: NOT_REACHED();
1411 void DrawWidget(const Rect &r, WidgetID widget) const override
1413 switch (widget) {
1414 case WID_BROS_STATION_NE:
1415 case WID_BROS_STATION_SE:
1416 case WID_BROS_STATION_SW:
1417 case WID_BROS_STATION_NW:
1418 case WID_BROS_STATION_X:
1419 case WID_BROS_STATION_Y: {
1420 StationType st = GetRoadStationTypeByWindowClass(this->window_class);
1421 const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type);
1422 DrawPixelInfo tmp_dpi;
1423 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
1424 if (FillDrawPixelInfo(&tmp_dpi, ir)) {
1425 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
1426 int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT);
1427 int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM);
1428 if (spec == nullptr) {
1429 StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE);
1430 } else {
1431 DrawRoadStopTile(x, y, _cur_roadtype, spec, st, widget - WID_BROS_STATION_NE);
1434 break;
1437 default:
1438 this->PickerWindow::DrawWidget(r, widget);
1439 break;
1443 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1445 switch (widget) {
1446 case WID_BROS_STATION_NE:
1447 case WID_BROS_STATION_SE:
1448 case WID_BROS_STATION_SW:
1449 case WID_BROS_STATION_NW:
1450 case WID_BROS_STATION_X:
1451 case WID_BROS_STATION_Y:
1452 if (widget < WID_BROS_STATION_X) {
1453 const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type);
1454 if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) return;
1456 this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
1457 _roadstop_gui.orientation = (DiagDirection)(widget - WID_BROS_STATION_NE);
1458 this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
1459 if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1460 this->SetDirty();
1461 CloseWindowById(WC_SELECT_STATION, 0);
1462 break;
1464 case WID_BROS_LT_OFF:
1465 case WID_BROS_LT_ON:
1466 this->RaiseWidget(_settings_client.gui.station_show_coverage + WID_BROS_LT_OFF);
1467 _settings_client.gui.station_show_coverage = (widget != WID_BROS_LT_OFF);
1468 this->LowerWidget(_settings_client.gui.station_show_coverage + WID_BROS_LT_OFF);
1469 if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1470 this->SetDirty();
1471 SetViewportCatchmentStation(nullptr, true);
1472 break;
1474 default:
1475 this->PickerWindow::OnClick(pt, widget, click_count);
1476 break;
1480 void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
1482 CheckRedrawStationCoverage(this);
1485 static inline HotkeyList road_hotkeys{"buildroadstop", {
1486 Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX),
1489 static inline HotkeyList tram_hotkeys{"buildtramstop", {
1490 Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX),
1494 /** Widget definition of the build road station window */
1495 static constexpr NWidgetPart _nested_road_station_picker_widgets[] = {
1496 NWidget(NWID_HORIZONTAL),
1497 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1498 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION),
1499 NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
1500 NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
1501 EndContainer(),
1502 NWidget(NWID_HORIZONTAL),
1503 NWidget(NWID_VERTICAL),
1504 NWidgetFunction(MakePickerClassWidgets),
1505 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
1506 NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker),
1507 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_AVAILABLE_ORIENTATIONS),
1508 /* 6-orientation plane. */
1509 NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
1510 NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
1511 NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
1512 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NW), SetFill(0, 0), EndContainer(),
1513 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NE), SetFill(0, 0), EndContainer(),
1514 EndContainer(),
1515 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
1516 EndContainer(),
1517 NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
1518 NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
1519 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SW), SetFill(0, 0), EndContainer(),
1520 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SE), SetFill(0, 0), EndContainer(),
1521 EndContainer(),
1522 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
1523 EndContainer(),
1524 EndContainer(),
1525 /* 2-orientation plane. */
1526 NWidget(NWID_VERTICAL), SetPIPRatio(0, 0, 1),
1527 NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
1528 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
1529 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
1530 EndContainer(),
1531 EndContainer(),
1532 EndContainer(),
1533 NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
1534 NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
1535 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
1536 SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
1537 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
1538 SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
1539 EndContainer(),
1540 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, 0),
1541 EndContainer(),
1542 EndContainer(),
1543 EndContainer(),
1544 NWidgetFunction(MakePickerTypeWidgets),
1545 EndContainer(),
1548 static WindowDesc _road_station_picker_desc(
1549 WDP_AUTO, "build_station_road", 0, 0,
1550 WC_BUS_STATION, WC_BUILD_TOOLBAR,
1551 WDF_CONSTRUCTION,
1552 _nested_road_station_picker_widgets,
1553 &BuildRoadStationWindow::road_hotkeys
1556 /** Widget definition of the build tram station window */
1557 static constexpr NWidgetPart _nested_tram_station_picker_widgets[] = {
1558 NWidget(NWID_HORIZONTAL),
1559 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1560 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION),
1561 NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
1562 NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
1563 EndContainer(),
1564 NWidget(NWID_HORIZONTAL),
1565 NWidget(NWID_VERTICAL),
1566 NWidgetFunction(MakePickerClassWidgets),
1567 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
1568 NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker),
1569 NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
1570 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
1571 NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
1572 EndContainer(),
1573 NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
1574 NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
1575 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
1576 SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
1577 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
1578 SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
1579 EndContainer(),
1580 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, 0),
1581 EndContainer(),
1582 EndContainer(),
1583 EndContainer(),
1584 NWidgetFunction(MakePickerTypeWidgets),
1585 EndContainer(),
1588 static WindowDesc _tram_station_picker_desc(
1589 WDP_AUTO, "build_station_tram", 0, 0,
1590 WC_BUS_STATION, WC_BUILD_TOOLBAR,
1591 WDF_CONSTRUCTION,
1592 _nested_tram_station_picker_widgets,
1593 &BuildRoadStationWindow::tram_hotkeys
1596 static void ShowRVStationPicker(Window *parent, RoadStopType rs)
1598 new BuildRoadStationWindow(RoadTypeIsRoad(_cur_roadtype) ? _road_station_picker_desc : _tram_station_picker_desc, parent, rs);
1601 class RoadWaypointPickerCallbacks : public PickerCallbacksNewGRFClass<RoadStopClass> {
1602 public:
1603 RoadWaypointPickerCallbacks() : PickerCallbacksNewGRFClass<RoadStopClass>("fav_road_waypoints") {}
1605 StringID GetClassTooltip() const override { return STR_PICKER_WAYPOINT_CLASS_TOOLTIP; }
1606 StringID GetTypeTooltip() const override { return STR_PICKER_WAYPOINT_TYPE_TOOLTIP; }
1608 bool IsActive() const override
1610 for (const auto &cls : RoadStopClass::Classes()) {
1611 if (!IsWaypointClass(cls)) continue;
1612 for (const auto *spec : cls.Specs()) {
1613 if (spec != nullptr) return true;
1616 return false;
1619 bool HasClassChoice() const override
1621 return std::count_if(std::begin(RoadStopClass::Classes()), std::end(RoadStopClass::Classes()), IsWaypointClass) > 1;
1624 void Close(int) override { ResetObjectToPlace(); }
1625 int GetSelectedClass() const override { return _waypoint_gui.sel_class; }
1626 void SetSelectedClass(int id) const override { _waypoint_gui.sel_class = this->GetClassIndex(id); }
1628 StringID GetClassName(int id) const override
1630 const auto *sc = GetClass(id);
1631 if (!IsWaypointClass(*sc)) return INVALID_STRING_ID;
1632 return sc->name;
1635 int GetSelectedType() const override { return _waypoint_gui.sel_type; }
1636 void SetSelectedType(int id) const override { _waypoint_gui.sel_type = id; }
1638 StringID GetTypeName(int cls_id, int id) const override
1640 const auto *spec = this->GetSpec(cls_id, id);
1641 return (spec == nullptr) ? STR_STATION_CLASS_WAYP_WAYPOINT : spec->name;
1644 bool IsTypeAvailable(int cls_id, int id) const override
1646 return IsRoadStopAvailable(this->GetSpec(cls_id, id), STATION_ROADWAYPOINT);
1649 void DrawType(int x, int y, int cls_id, int id) const override
1651 const auto *spec = this->GetSpec(cls_id, id);
1652 if (spec == nullptr) {
1653 StationPickerDrawSprite(x, y, STATION_ROADWAYPOINT, INVALID_RAILTYPE, _cur_roadtype, RSV_DRIVE_THROUGH_X);
1654 } else {
1655 DrawRoadStopTile(x, y, _cur_roadtype, spec, STATION_ROADWAYPOINT, RSV_DRIVE_THROUGH_X);
1659 void FillUsedItems(std::set<PickerItem> &items) override
1661 for (const Waypoint *wp : Waypoint::Iterate()) {
1662 if (wp->owner != _local_company || !HasBit(wp->waypoint_flags, WPF_ROAD)) continue;
1663 items.insert({0, 0, ROADSTOP_CLASS_WAYP, 0}); // We would need to scan the map to find out if default is used.
1664 for (const auto &sm : wp->roadstop_speclist) {
1665 if (sm.spec == nullptr) continue;
1666 items.insert({sm.grfid, sm.localidx, sm.spec->class_index, sm.spec->index});
1671 static RoadWaypointPickerCallbacks instance;
1673 /* static */ RoadWaypointPickerCallbacks RoadWaypointPickerCallbacks::instance;
1675 struct BuildRoadWaypointWindow : public PickerWindow {
1676 BuildRoadWaypointWindow(WindowDesc &desc, Window *parent) : PickerWindow(desc, parent, TRANSPORT_ROAD, RoadWaypointPickerCallbacks::instance)
1678 this->ConstructWindow();
1679 this->InvalidateData();
1682 static inline HotkeyList hotkeys{"buildroadwaypoint", {
1683 Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX),
1687 /** Nested widget definition for the build NewGRF road waypoint window */
1688 static constexpr NWidgetPart _nested_build_road_waypoint_widgets[] = {
1689 NWidget(NWID_HORIZONTAL),
1690 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1691 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_WAYPOINT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1692 NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
1693 NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
1694 EndContainer(),
1695 NWidget(NWID_HORIZONTAL),
1696 NWidgetFunction(MakePickerClassWidgets),
1697 NWidgetFunction(MakePickerTypeWidgets),
1698 EndContainer(),
1701 static WindowDesc _build_road_waypoint_desc(
1702 WDP_AUTO, "build_road_waypoint", 0, 0,
1703 WC_BUILD_WAYPOINT, WC_BUILD_TOOLBAR,
1704 WDF_CONSTRUCTION,
1705 _nested_build_road_waypoint_widgets,
1706 &BuildRoadWaypointWindow::hotkeys
1709 static void ShowBuildRoadWaypointPicker(Window *parent)
1711 if (!RoadWaypointPickerCallbacks::instance.IsActive()) return;
1712 new BuildRoadWaypointWindow(_build_road_waypoint_desc, parent);
1715 void InitializeRoadGui()
1717 _road_depot_orientation = DIAGDIR_NW;
1718 _roadstop_gui.orientation = DIAGDIR_NW;
1719 _waypoint_gui.sel_class = RoadStopClassID::ROADSTOP_CLASS_WAYP;
1720 _waypoint_gui.sel_type = 0;
1724 * I really don't know why rail_gui.cpp has this too, shouldn't be included in the other one?
1726 void InitializeRoadGUI()
1728 BuildRoadToolbarWindow *w = dynamic_cast<BuildRoadToolbarWindow *>(FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_ROAD));
1729 if (w != nullptr) w->ModifyRoadType(_cur_roadtype);
1732 DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement, bool all_option)
1734 RoadTypes used_roadtypes;
1735 RoadTypes avail_roadtypes;
1737 const Company *c = Company::Get(_local_company);
1739 /* Find the used roadtypes. */
1740 if (for_replacement) {
1741 avail_roadtypes = GetCompanyRoadTypes(c->index, false);
1742 used_roadtypes = GetRoadTypes(false);
1743 } else {
1744 avail_roadtypes = c->avail_roadtypes;
1745 used_roadtypes = GetRoadTypes(true);
1748 /* Filter listed road types */
1749 if (!HasBit(rtts, RTT_ROAD)) used_roadtypes &= _roadtypes_type;
1750 if (!HasBit(rtts, RTT_TRAM)) used_roadtypes &= ~_roadtypes_type;
1752 DropDownList list;
1754 if (all_option) {
1755 list.push_back(MakeDropDownListStringItem(STR_REPLACE_ALL_ROADTYPE, INVALID_ROADTYPE));
1758 Dimension d = { 0, 0 };
1759 /* Get largest icon size, to ensure text is aligned on each menu item. */
1760 if (!for_replacement) {
1761 for (const auto &rt : _sorted_roadtypes) {
1762 if (!HasBit(used_roadtypes, rt)) continue;
1763 const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
1764 d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road));
1768 for (const auto &rt : _sorted_roadtypes) {
1769 /* If it's not used ever, don't show it to the user. */
1770 if (!HasBit(used_roadtypes, rt)) continue;
1772 const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
1774 SetDParam(0, rti->strings.menu_text);
1775 SetDParam(1, rti->max_speed / 2);
1776 if (for_replacement) {
1777 list.push_back(MakeDropDownListStringItem(rti->strings.replace_text, rt, !HasBit(avail_roadtypes, rt)));
1778 } else {
1779 StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING;
1780 list.push_back(MakeDropDownListIconItem(d, rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)));
1784 if (list.empty()) {
1785 /* Empty dropdowns are not allowed */
1786 list.push_back(MakeDropDownListStringItem(STR_NONE, INVALID_ROADTYPE, true));
1789 return list;
1792 DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts)
1794 RoadTypes avail_roadtypes = GetRoadTypes(false);
1795 avail_roadtypes = AddDateIntroducedRoadTypes(avail_roadtypes, TimerGameCalendar::date);
1796 RoadTypes used_roadtypes = GetRoadTypes(true);
1798 /* Filter listed road types */
1799 if (!HasBit(rtts, RTT_ROAD)) used_roadtypes &= _roadtypes_type;
1800 if (!HasBit(rtts, RTT_TRAM)) used_roadtypes &= ~_roadtypes_type;
1802 DropDownList list;
1804 /* If it's not used ever, don't show it to the user. */
1805 Dimension d = { 0, 0 };
1806 for (const auto &rt : _sorted_roadtypes) {
1807 if (!HasBit(used_roadtypes, rt)) continue;
1808 const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
1809 d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road));
1811 for (const auto &rt : _sorted_roadtypes) {
1812 if (!HasBit(used_roadtypes, rt)) continue;
1814 const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
1816 SetDParam(0, rti->strings.menu_text);
1817 SetDParam(1, rti->max_speed / 2);
1818 StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING;
1819 list.push_back(MakeDropDownListIconItem(d, rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)));
1822 if (list.empty()) {
1823 /* Empty dropdowns are not allowed */
1824 list.push_back(MakeDropDownListStringItem(STR_NONE, -1, true));
1827 return list;