diff --git a/src/lang/extra/english.txt b/src/lang/extra/english.txt index 9f8275ff5c..2791537979 100644 --- a/src/lang/extra/english.txt +++ b/src/lang/extra/english.txt @@ -1442,6 +1442,8 @@ STR_QUERY_RENAME_TRAIN_TYPE_WAGON_CAPTION :{WHITE}Rename w STR_VEHICLE_VIEW_SEND_TO_DEPOT_TOOLTIP_SHIFT :{STRING}. Shift+Click to select which STR_VEHICLE_VIEW_SEND_TO_DEPOT_MENU :{BLACK}Cancel send vehicle to depot/hangar. Ctrl+Click for menu. Shift+Click to select depot/hangar +STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP_EXTRA :{STRING}{BLACK}{}Shift+Click to toggle showing route overlay even when window is not focused + STR_VEHICLE_STATUS_LOADING_UNLOADING_ADVANCE :{STRING}, {VELOCITY} STR_VEHICLE_STATUS_BROKEN_DOWN_VEL :{RED}Broken down - {STRING2}, {LTBLUE} {VELOCITY} STR_VEHICLE_STATUS_BROKEN_DOWN_VEL_SHORT :{RED}Broken down - {STRING2} diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 4ae87857f3..bad1eff8e9 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -462,7 +462,7 @@ struct MainWindow : Window case GHK_SWITCH_VIEWPORT_ROUTE_OVERLAY_MODE: if (_settings_client.gui.show_vehicle_route_mode != 0) { _settings_client.gui.show_vehicle_route_mode ^= 3; - CheckMarkDirtyFocusedRoutePaths(); + CheckMarkDirtyViewportRoutePaths(); SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_SETTINGS); } break; diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index e797f15b06..487e3987a0 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1345,7 +1345,7 @@ CommandCost CmdInsertOrderIntl(DoCommandFlag flags, Vehicle *v, VehicleOrderID s Order *new_o = new Order(); new_o->AssignOrder(new_order); InsertOrder(v, new_o, sel_ord); - CheckMarkDirtyFocusedRoutePaths(v); + CheckMarkDirtyViewportRoutePaths(v); } return CommandCost(); @@ -1444,7 +1444,7 @@ static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags) InvalidateVehicleOrder(dst, VIWD_REMOVE_ALL_ORDERS); InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); - CheckMarkDirtyFocusedRoutePaths(dst); + CheckMarkDirtyViewportRoutePaths(dst); } return CommandCost(); } @@ -1489,7 +1489,7 @@ CommandCost CmdDeleteOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (flags & DC_EXEC) { DeleteOrder(v, sel_ord); - CheckMarkDirtyFocusedRoutePaths(v); + CheckMarkDirtyViewportRoutePaths(v); } return CommandCost(); } @@ -1728,7 +1728,7 @@ CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 /* Make sure to rebuild the whole list */ InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); - CheckMarkDirtyFocusedRoutePaths(v); + CheckMarkDirtyViewportRoutePaths(v); } return CommandCost(); @@ -2402,7 +2402,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } InvalidateVehicleOrder(u, VIWD_MODIFY_ORDERS); } - CheckMarkDirtyFocusedRoutePaths(v); + CheckMarkDirtyViewportRoutePaths(v); } return CommandCost(); @@ -2586,7 +2586,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); - CheckMarkDirtyFocusedRoutePaths(dst); + CheckMarkDirtyViewportRoutePaths(dst); CheckAdvanceVehicleOrdersAfterClone(dst, flags); } @@ -2685,7 +2685,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); - CheckMarkDirtyFocusedRoutePaths(dst); + CheckMarkDirtyViewportRoutePaths(dst); CheckAdvanceVehicleOrdersAfterClone(dst, flags); } @@ -2750,7 +2750,7 @@ CommandCost CmdOrderRefit(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 u->current_order.SetRefit(cargo); } } - CheckMarkDirtyFocusedRoutePaths(v); + CheckMarkDirtyViewportRoutePaths(v); } return CommandCost(); @@ -3683,7 +3683,7 @@ CommandCost CmdMassChangeOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, u } index++; } - if (changed) CheckMarkDirtyFocusedRoutePaths(v); + if (changed) CheckMarkDirtyViewportRoutePaths(v); } } } diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index a3fd4255cc..80a81fe781 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1736,6 +1736,7 @@ void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index) ChangeVehicleWindow(WC_VEHICLE_REFIT, from_index, to_index); ChangeVehicleWindow(WC_VEHICLE_DETAILS, from_index, to_index); ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index); + ChangeFixedViewportRoutePath(from_index, to_index); } static const NWidgetPart _nested_vehicle_list[] = { @@ -3443,7 +3444,7 @@ static const NWidgetPart _nested_vehicle_view_widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_RENAME), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NULL /* filled in later */), NWidget(WWT_CAPTION, COLOUR_GREY, WID_VV_CAPTION), SetDataTip(STR_VEHICLE_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VV_LOCATION), SetMinimalSize(12, 14), SetDataTip(SPR_GOTO_LOCATION, STR_NULL /* filled in later */), + NWidget(WWT_IMGBTN, COLOUR_GREY, WID_VV_LOCATION), SetMinimalSize(12, 14), SetDataTip(SPR_GOTO_LOCATION, STR_NULL /* filled in later */), NWidget(WWT_DEBUGBOX, COLOUR_GREY), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), @@ -3583,6 +3584,7 @@ struct VehicleViewWindow : Window { private: bool depot_select_active = false; bool depot_select_ctrl_pressed = false; + bool fixed_route_overlay_active = false; /** Display planes available in the vehicle view window. */ enum PlaneSelections { @@ -3665,7 +3667,6 @@ public: this->GetWidget(WID_VV_START_STOP)->tool_tip = STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP + v->type; this->GetWidget(WID_VV_RENAME)->tool_tip = STR_VEHICLE_DETAILS_TRAIN_RENAME + v->type; - this->GetWidget(WID_VV_LOCATION)->tool_tip = STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP + v->type; this->GetWidget(WID_VV_REFIT)->tool_tip = STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP + v->type; this->GetWidget(WID_VV_SHOW_ORDERS)->tool_tip = STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP + v->type; this->GetWidget(WID_VV_SHOW_DETAILS)->tool_tip = STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP + v->type; @@ -3684,6 +3685,10 @@ public: DeleteWindowById(WC_VEHICLE_REFIT, this->window_number, false); DeleteWindowById(WC_VEHICLE_DETAILS, this->window_number, false); DeleteWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false); + + if (this->fixed_route_overlay_active) { + RemoveFixedViewportRoutePath(this->window_number); + } } virtual void OnFocus(Window *previously_focused_window) override @@ -3958,6 +3963,16 @@ public: case WID_VV_LOCATION: // center main view if (_ctrl_pressed) { ShowExtraViewportWindow(TileVirtXY(v->x_pos, v->y_pos)); + this->HandleButtonClick(widget); + } else if (_shift_pressed) { + this->fixed_route_overlay_active = !this->fixed_route_overlay_active; + this->SetWidgetLoweredState(widget, this->fixed_route_overlay_active); + this->SetWidgetDirty(widget); + if (this->fixed_route_overlay_active) { + AddFixedViewportRoutePath(this->window_number); + } else { + RemoveFixedViewportRoutePath(this->window_number); + } } else { const Window *mainwindow = GetMainWindow(); if (click_count > 1 && mainwindow->viewport->zoom < ZOOM_LVL_DRAW_MAP) { @@ -3966,6 +3981,7 @@ public: } else { ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos); } + this->HandleButtonClick(widget); } break; @@ -4064,6 +4080,10 @@ public: this->RaiseWidget(WID_VV_GOTO_DEPOT); this->SetWidgetDirty(WID_VV_GOTO_DEPOT); } + if (!this->fixed_route_overlay_active) { + this->RaiseWidget(WID_VV_LOCATION); + this->SetWidgetDirty(WID_VV_LOCATION); + } } virtual void OnPlaceObject(Point pt, TileIndex tile) override @@ -4110,6 +4130,11 @@ public: GuiShowTooltips(this, STR_VEHICLE_VIEW_SEND_TO_DEPOT_TOOLTIP_SHIFT, 1, &arg, TCC_HOVER); } } + if (widget == WID_VV_LOCATION) { + const Vehicle *v = Vehicle::Get(this->window_number); + uint64 args[] = { STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP + v->type }; + GuiShowTooltips(this, STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP_EXTRA, lengthof(args), args, TCC_HOVER); + } } void OnMouseOver(Point pt, int widget) override diff --git a/src/viewport.cpp b/src/viewport.cpp index 026a07eeb0..b19693e735 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -390,6 +390,12 @@ public: }; static ViewportRouteOverlay _vp_focused_window_route_overlay; +struct FixedVehicleViewportRouteOverlay : public ViewportRouteOverlay { + VehicleID veh; + bool enabled = false; +}; +static std::vector _vp_fixed_route_overlays; + static void MarkRoutePathsDirty(const std::vector &lines); TileHighlightData _thd; @@ -2454,6 +2460,9 @@ void ViewportRouteOverlay::DrawVehicleRoutePath(const Viewport *vp, ViewportDraw static void ViewportDrawVehicleRoutePath(const Viewport *vp, ViewportDrawerDynamic *vdd) { _vp_focused_window_route_overlay.DrawVehicleRoutePath(vp, vdd); + for (auto &it : _vp_fixed_route_overlays) { + if (it.enabled) it.DrawVehicleRoutePath(vp, vdd); + } } static inline void DrawRouteStep(const Viewport * const vp, const TileIndex tile, const RankOrderTypeList list) @@ -2568,7 +2577,13 @@ void ViewportPrepareVehicleRoute() if (_settings_client.gui.show_vehicle_route_mode == 0) return; if (!_settings_client.gui.show_vehicle_route_steps && !_settings_client.gui.show_vehicle_route) return; - _vp_focused_window_route_overlay.PrepareVehicleRoute(GetVehicleFromWindow(_focused_window)); + const Vehicle *focused_veh = GetVehicleFromWindow(_focused_window); + _vp_focused_window_route_overlay.PrepareVehicleRoute(focused_veh); + for (auto &it : _vp_fixed_route_overlays) { + const Vehicle *v = Vehicle::GetIfValid(it.veh); + it.PrepareVehicleRoute(v); + it.enabled = !(v != nullptr && focused_veh != nullptr && v->FirstShared() == focused_veh->FirstShared()); + } } void ViewportRouteOverlay::PrepareVehicleRoute(const Vehicle *veh) @@ -2617,13 +2632,16 @@ void ViewportRouteOverlay::DrawVehicleRouteSteps(const Viewport *vp) static bool ViewportDrawHasVehicleRouteSteps() { - return _vp_focused_window_route_overlay.HasVehicleRouteSteps(); + return _vp_focused_window_route_overlay.HasVehicleRouteSteps() || !_vp_fixed_route_overlays.empty(); } /** Draw the route steps of a vehicle. */ static void ViewportDrawVehicleRouteSteps(const Viewport * const vp) { _vp_focused_window_route_overlay.DrawVehicleRouteSteps(vp); + for (auto &it : _vp_fixed_route_overlays) { + if (it.enabled) it.DrawVehicleRouteSteps(vp); + } } void ViewportDrawPlans(const Viewport *vp) @@ -4411,20 +4429,48 @@ void MarkDirtyFocusedRoutePaths(const Vehicle *veh) _vp_focused_window_route_overlay.MarkAllDirty(veh); } -void CheckMarkDirtyFocusedRoutePaths(const Vehicle *veh) +void CheckMarkDirtyViewportRoutePaths(const Vehicle *veh) { + if (veh == nullptr) return; + const Vehicle *focused_veh = GetVehicleFromWindow(_focused_window); if (focused_veh != nullptr && veh == focused_veh) { MarkDirtyFocusedRoutePaths(veh); } + for (auto &it : _vp_fixed_route_overlays) { + if (it.veh == veh->index) it.MarkAllDirty(veh); + } } -void CheckMarkDirtyFocusedRoutePaths() +void CheckMarkDirtyViewportRoutePaths() { const Vehicle *focused_veh = GetVehicleFromWindow(_focused_window); if (focused_veh != nullptr) { MarkDirtyFocusedRoutePaths(focused_veh); } + for (auto &it : _vp_fixed_route_overlays) { + it.MarkAllDirty(Vehicle::GetIfValid(it.veh)); + } +} + +void AddFixedViewportRoutePath(VehicleID veh) +{ + FixedVehicleViewportRouteOverlay &overlay = _vp_fixed_route_overlays.emplace_back(); + overlay.veh = veh; +} + +void RemoveFixedViewportRoutePath(VehicleID veh) +{ + container_unordered_remove_if(_vp_fixed_route_overlays, [&](const FixedVehicleViewportRouteOverlay &it) -> bool { + return it.veh == veh; + }); +} + +void ChangeFixedViewportRoutePath(VehicleID from, VehicleID to) +{ + for (auto &it : _vp_fixed_route_overlays) { + if (it.veh == from) it.veh = to; + } } /** diff --git a/src/viewport_func.h b/src/viewport_func.h index 4d17ea48b0..ebcc75b558 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -44,8 +44,11 @@ void MarkAllViewportOverlayStationLinksDirty(const Station *st); void MarkViewportLineDirty(Viewport * const vp, const Point from_pt, const Point to_pt, const int block_radius, ViewportMarkDirtyFlags flags); void MarkTileLineDirty(const TileIndex from_tile, const TileIndex to_tile, ViewportMarkDirtyFlags flags); void MarkDirtyFocusedRoutePaths(const Vehicle *veh); -void CheckMarkDirtyFocusedRoutePaths(const Vehicle *veh); -void CheckMarkDirtyFocusedRoutePaths(); +void CheckMarkDirtyViewportRoutePaths(const Vehicle *veh); +void CheckMarkDirtyViewportRoutePaths(); +void AddFixedViewportRoutePath(VehicleID veh); +void RemoveFixedViewportRoutePath(VehicleID veh); +void ChangeFixedViewportRoutePath(VehicleID from, VehicleID to); bool DoZoomInOutWindow(ZoomStateChange how, Window *w); void ZoomInOrOutToCursorWindow(bool in, Window * w);