From 75799d3be52840481ceae11472a2706949438390 Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Thu, 15 Jun 2023 21:24:56 +0100 Subject: [PATCH 1/5] Start refactor of ride window --- src/openrct2-ui/windows/Ride.cpp | 11967 ++++++++++++++--------------- 1 file changed, 5902 insertions(+), 6065 deletions(-) diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 39287c80b3..121f4cbe26 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -431,108 +431,6 @@ static constexpr const uint64_t window_ride_page_hold_down_widgets[] = { #pragma region Events -static void WindowRideInitViewport(WindowBase* w); - -static void WindowRideMainMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideMainResize(WindowBase* w); -static void WindowRideMainMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideMainDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowRideMainUpdate(WindowBase* w); -static void WindowRideMainTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); -static void WindowRideMainViewportRotate(WindowBase* w); -static void WindowRideMainInvalidate(WindowBase* w); -static void WindowRideMainPaint(WindowBase* w, DrawPixelInfo& dpi); -static void WindowRideMainFollowRide(WindowBase* w); - -static void WindowRideVehicleMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideVehicleResize(WindowBase* w); -static void WindowRideVehicleMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideVehicleDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowRideVehicleUpdate(WindowBase* w); -static OpenRCT2String WindowRideVehicleTooltip(WindowBase* const w, const WidgetIndex widgetIndex, StringId fallback); -static void WindowRideVehicleInvalidate(WindowBase* w); -static void WindowRideVehiclePaint(WindowBase* w, DrawPixelInfo& dpi); -static void WindowRideVehicleScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex); - -static void WindowRideOperatingMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideOperatingResize(WindowBase* w); -static void WindowRideOperatingMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideOperatingLengthWindow(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideOperatingTweakTextInput(WindowBase* w, const Ride& ride); -static void WindowRideOperatingDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowRideOperatingUpdate(WindowBase* w); -static void WindowRideOperatingTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); -static void WindowRideOperatingInvalidate(WindowBase* w); -static void WindowRideOperatingPaint(WindowBase* w, DrawPixelInfo& dpi); - -static void WindowRideMaintenanceMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideMaintenanceResize(WindowBase* w); -static void WindowRideMaintenanceMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideMaintenanceDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowRideMaintenanceUpdate(WindowBase* w); -static void WindowRideMaintenanceInvalidate(WindowBase* w); -static void WindowRideMaintenancePaint(WindowBase* w, DrawPixelInfo& dpi); - -static void WindowRideColourClose(WindowBase* w); -static void WindowRideColourMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideColourResize(WindowBase* w); -static void WindowRideColourMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideColourDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowRideColourUpdate(WindowBase* w); -static void WindowRideColourTooldown(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -static void WindowRideColourTooldrag(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -static void WindowRideColourInvalidate(WindowBase* w); -static void WindowRideColourPaint(WindowBase* w, DrawPixelInfo& dpi); -static void WindowRideColourScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex); - -static void WindowRideMusicMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideMusicResize(WindowBase* w); -static void WindowRideMusicMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideMusicDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowRideMusicUpdate(WindowBase* w); -static void WindowRideMusicInvalidate(WindowBase* w); -static void WindowRideMusicPaint(WindowBase* w, DrawPixelInfo& dpi); - -static void WindowRideMeasurementsClose(WindowBase* w); -static void WindowRideMeasurementsMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideMeasurementsResize(WindowBase* w); -static void WindowRideMeasurementsMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideMeasurementsDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowRideMeasurementsUpdate(WindowBase* w); -static void WindowRideMeasurementsTooldown(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -static void WindowRideMeasurementsTooldrag(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -static void WindowRideMeasurementsToolabort(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideMeasurementsInvalidate(WindowBase* w); -static void WindowRideMeasurementsPaint(WindowBase* w, DrawPixelInfo& dpi); - -static void WindowRideGraphsMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideGraphsResize(WindowBase* w); -static void WindowRideGraphsMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideGraphsUpdate(WindowBase* w); -static void WindowRideGraphsScrollgetheight(WindowBase* w, int32_t scrollIndex, int32_t* width, int32_t* height); -static void WindowRideGraphs15(WindowBase* w, int32_t scrollIndex, int32_t scrollAreaType); -static OpenRCT2String WindowRideGraphsTooltip(WindowBase* w, const WidgetIndex widgetIndex, const StringId fallback); -static void WindowRideGraphsInvalidate(WindowBase* w); -static void WindowRideGraphsPaint(WindowBase* w, DrawPixelInfo& dpi); -static void WindowRideGraphsScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex); - -static void WindowRideIncomeMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideIncomeResize(WindowBase* w); -static void WindowRideIncomeMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); -static void WindowRideIncomeUpdate(WindowBase* w); -static void WindowRideIncomeTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); -static void WindowRideIncomeInvalidate(WindowBase* w); -static void WindowRideIncomePaint(WindowBase* w, DrawPixelInfo& dpi); -static bool WindowRideIncomeCanModifyPrimaryPrice(WindowBase* w); - -static void WindowRideCustomerMouseup(WindowBase* w, WidgetIndex widgetIndex); -static void WindowRideCustomerResize(WindowBase* w); -static void WindowRideCustomerUpdate(WindowBase* w); -static void WindowRideCustomerInvalidate(WindowBase* w); -static void WindowRideCustomerPaint(WindowBase* w, DrawPixelInfo& dpi); - -static void WindowRideSetPage(WindowBase* w, int32_t page); - // 0x0098DFD4 static WindowEventList window_ride_main_events([](auto& events) { events.mouse_up = &WindowRideMainMouseup; @@ -864,87 +762,2798 @@ static const RideObjectEntry* VehicleDropdownRideType = nullptr; static bool VehicleDropdownExpanded = false; static std::vector VehicleDropdownData; -static void WindowRideDrawTabImage(DrawPixelInfo& dpi, WindowBase* w, int32_t page, int32_t spriteIndex) +class RideWindow final : public Window { - WidgetIndex widgetIndex = WIDX_TAB_1 + page; - - if (!WidgetIsDisabled(*w, widgetIndex)) + RideWindow(const Ride& ride) { - if (w->page == page) - { - int32_t frame = w->frame_no / window_ride_tab_animation_divisor[w->page]; - spriteIndex += (frame % window_ride_tab_animation_frames[w->page]); - } + rideId = ride.id; + widgets = window_ride_page_widgets[WINDOW_RIDE_PAGE_MAIN]; + hold_down_widgets = window_ride_page_hold_down_widgets[WINDOW_RIDE_PAGE_MAIN]; - const auto& widget = w->widgets[widgetIndex]; - GfxDrawSprite(dpi, ImageId(spriteIndex), w->windowPos + ScreenCoordsXY{ widget.left, widget.top }); + page = WINDOW_RIDE_PAGE_MAIN; + vehicleIndex = 0; + frame_no = 0; + list_information_type = 0; + picked_peep_frame = 0; + ride_colour = 0; + DisableTabs(); + min_width = 316; + min_height = 180; + max_width = 500; + max_height = 450; + + UpdateOverallView(ride); + + PopulateVehicleTypeDropdown(ride, true); } -} -/** - * - * rct2: 0x006B2E88 - */ -static void WindowRideDrawTabMain(DrawPixelInfo& dpi, WindowBase* w) -{ - WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(WINDOW_RIDE_PAGE_MAIN); - if (!WidgetIsDisabled(*w, widgetIndex)) +private: + void DrawTabImage(DrawPixelInfo& dpi, int32_t tab, int32_t spriteIndex) { - auto ride = GetRide(w->rideId); - if (ride != nullptr) + WidgetIndex widgetIndex = WIDX_TAB_1 + page; + + if (!WidgetIsDisabled(*this, widgetIndex)) { - int32_t spriteIndex = 0; - switch (ride->GetClassification()) + if (page == tab) { - case RideClassification::Ride: - spriteIndex = SPR_TAB_RIDE_0; - if (w->page == WINDOW_RIDE_PAGE_MAIN) - spriteIndex += (w->frame_no / 4) % 16; - break; - case RideClassification::ShopOrStall: - spriteIndex = SPR_TAB_SHOPS_AND_STALLS_0; - if (w->page == WINDOW_RIDE_PAGE_MAIN) - spriteIndex += (w->frame_no / 4) % 16; - break; - case RideClassification::KioskOrFacility: - spriteIndex = SPR_TAB_KIOSKS_AND_FACILITIES_0; - if (w->page == WINDOW_RIDE_PAGE_MAIN) - spriteIndex += (w->frame_no / 4) % 8; - break; + int32_t frame = frame_no / window_ride_tab_animation_divisor[page]; + spriteIndex += (frame % window_ride_tab_animation_frames[page]); } - const auto& widget = w->widgets[widgetIndex]; - GfxDrawSprite(dpi, ImageId(spriteIndex), w->windowPos + ScreenCoordsXY{ widget.left, widget.top }); + const auto& widget = widgets[widgetIndex]; + GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget.left, widget.top }); } } -} -/** - * - * rct2: 0x006B2B68 - */ -static void WindowRideDrawTabVehicle(DrawPixelInfo& dpi, WindowBase* w) -{ - WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(WINDOW_RIDE_PAGE_VEHICLE); - const auto& widget = w->widgets[widgetIndex]; - - if (!WidgetIsDisabled(*w, widgetIndex)) + void DrawTabMain(DrawPixelInfo& dpi) { - auto screenCoords = ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; - int32_t width = widget.right - screenCoords.x; - int32_t height = widget.bottom - 3 - screenCoords.y; - if (w->page == WINDOW_RIDE_PAGE_VEHICLE) - height += 4; - - screenCoords += w->windowPos; - - DrawPixelInfo clipDPI; - if (!ClipDrawPixelInfo(clipDPI, dpi, screenCoords, width, height)) + WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(WINDOW_RIDE_PAGE_MAIN); + if (!WidgetIsDisabled(*this, widgetIndex)) { + auto ride = GetRide(rideId); + if (ride != nullptr) + { + int32_t spriteIndex = 0; + switch (ride->GetClassification()) + { + case RideClassification::Ride: + spriteIndex = SPR_TAB_RIDE_0; + if (page == WINDOW_RIDE_PAGE_MAIN) + spriteIndex += (frame_no / 4) % 16; + break; + case RideClassification::ShopOrStall: + spriteIndex = SPR_TAB_SHOPS_AND_STALLS_0; + if (page == WINDOW_RIDE_PAGE_MAIN) + spriteIndex += (frame_no / 4) % 16; + break; + case RideClassification::KioskOrFacility: + spriteIndex = SPR_TAB_KIOSKS_AND_FACILITIES_0; + if (page == WINDOW_RIDE_PAGE_MAIN) + spriteIndex += (frame_no / 4) % 8; + break; + } + + const auto& widget = widgets[widgetIndex]; + GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget.left, widget.top }); + } + } + } + + void DrawTabVehicle(DrawPixelInfo& dpi) + { + WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(WINDOW_RIDE_PAGE_VEHICLE); + const auto& widget = widgets[widgetIndex]; + + if (!WidgetIsDisabled(*this, widgetIndex)) + { + auto screenCoords = ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; + int32_t width = widget.right - screenCoords.x; + int32_t height = widget.bottom - 3 - screenCoords.y; + if (page == WINDOW_RIDE_PAGE_VEHICLE) + height += 4; + + screenCoords += windowPos; + + DrawPixelInfo clipDPI; + if (!ClipDrawPixelInfo(clipDPI, dpi, screenCoords, width, height)) + { + return; + } + + screenCoords = ScreenCoordsXY{ widget.width() / 2, widget.height() - 12 }; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + if (rideEntry->flags & RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF) + { + clipDPI.zoom_level = ZoomLevel{ 1 }; + clipDPI.width *= 2; + clipDPI.height *= 2; + screenCoords.x *= 2; + screenCoords.y *= 2; + clipDPI.x *= 2; + clipDPI.y *= 2; + } + + // For any suspended rides, move image higher in the vehicle tab on the rides window + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SUSPENDED)) + { + screenCoords.y /= 4; + } + + const auto vehicle = RideEntryGetVehicleAtPosition(ride->subtype, ride->num_cars_per_train, rideEntry->TabCar); + const auto& carEntry = rideEntry->Cars[vehicle]; + + auto vehicleId = ((ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_VEHICLE) ? rideEntry->TabCar : 0; + VehicleColour vehicleColour = RideGetVehicleColour(*ride, vehicleId); + + // imageIndex represents a precision of 64 + auto imageIndex = OpenRCT2::Entity::Yaw::YawFrom4(2) * 2; + if (page == WINDOW_RIDE_PAGE_VEHICLE) + imageIndex += frame_no; + imageIndex = carEntry.SpriteByYaw(imageIndex / 2, SpriteGroupType::SlopeFlat); + imageIndex &= carEntry.TabRotationMask; + imageIndex *= carEntry.base_num_frames; + imageIndex += carEntry.base_image_id; + auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary); + GfxDrawSprite(clipDPI, imageId, screenCoords); + } + } + + void DrawTabCustomer(DrawPixelInfo& dpi) + { + WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(WINDOW_RIDE_PAGE_CUSTOMER); + + if (!WidgetIsDisabled(*this, widgetIndex)) + { + const auto& widget = widgets[widgetIndex]; + int32_t spriteIndex = 0; + if (page == WINDOW_RIDE_PAGE_CUSTOMER) + spriteIndex = picked_peep_frame & ~3; + + spriteIndex += GetPeepAnimation(PeepSpriteType::Normal).base_image + 1; + + GfxDrawSprite( + dpi, ImageId(spriteIndex, COLOUR_BRIGHT_RED, COLOUR_TEAL), + windowPos + ScreenCoordsXY{ widget.midX(), widget.bottom - 6 }); + } + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + DrawTabVehicle(dpi); + DrawTabImage(dpi, WINDOW_RIDE_PAGE_OPERATING, SPR_TAB_GEARS_0); + DrawTabImage(dpi, WINDOW_RIDE_PAGE_MAINTENANCE, SPR_TAB_WRENCH_0); + DrawTabImage(dpi, WINDOW_RIDE_PAGE_INCOME, SPR_TAB_ADMISSION_0); + DrawTabMain(dpi); + DrawTabImage(dpi, WINDOW_RIDE_PAGE_MEASUREMENTS, SPR_TAB_TIMER_0); + DrawTabImage(dpi, WINDOW_RIDE_PAGE_COLOUR, SPR_TAB_PAINT_0); + DrawTabImage(dpi, WINDOW_RIDE_PAGE_GRAPHS, SPR_TAB_GRAPH_A_0); + DrawTabCustomer(dpi); + DrawTabImage(dpi, WINDOW_RIDE_PAGE_MUSIC, SPR_TAB_MUSIC_0); + } + + void DisableTabs() + { + uint32_t disabled_tabs = 0; + auto ride = GetRide(rideId); + if (ride == nullptr) return; + + const auto& rtd = ride->GetRideTypeDescriptor(); + + if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) + disabled_tabs |= (1uLL << WIDX_TAB_8); // 0x800 + + if (ride->type == RIDE_TYPE_MINI_GOLF) + disabled_tabs |= (1uLL << WIDX_TAB_2 | 1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4); // 0xE0 + + if (rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) + disabled_tabs |= (1uLL << WIDX_TAB_2); // 0x20 + + if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL) + && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS) + && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT)) + { + disabled_tabs |= (1uLL << WIDX_TAB_5); // 0x100 } - screenCoords = ScreenCoordsXY{ widget.width() / 2, widget.height() - 12 }; + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) + disabled_tabs |= (1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_7); // 0x4C0 + + if (!rtd.HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC)) + { + disabled_tabs |= (1uLL << WIDX_TAB_6); // 0x200 + } + + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_CASH_MACHINE) || rtd.HasFlag(RIDE_TYPE_FLAG_IS_FIRST_AID) + || (gParkFlags & PARK_FLAGS_NO_MONEY) != 0) + disabled_tabs |= (1uLL << WIDX_TAB_9); // 0x1000 + + if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) != 0) + disabled_tabs |= (1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_6 | 1uLL << WIDX_TAB_9 | 1uLL << WIDX_TAB_10); // 0x3280 + + const auto* rideEntry = GetRideEntryByIndex(ride->subtype); + + if (rideEntry == nullptr) + { + disabled_tabs |= 1uLL << WIDX_TAB_2 | 1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_5 + | 1uLL << WIDX_TAB_6 | 1uLL << WIDX_TAB_7 | 1uLL << WIDX_TAB_8 | 1uLL << WIDX_TAB_9 | 1uLL << WIDX_TAB_10; + } + else if ((rideEntry->flags & RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB) != 0) + { + disabled_tabs |= (1uLL << WIDX_TAB_5); + } + + disabled_widgets = disabled_tabs; + } + + void UpdateOverallView(const Ride& ride) const + { + // Calculate x, y, z bounds of the entire ride using its track elements + TileElementIterator it; + + TileElementIteratorBegin(&it); + + CoordsXYZ min = { std::numeric_limits::max(), std::numeric_limits::max(), + std::numeric_limits::max() }; + CoordsXYZ max = { std::numeric_limits::min(), std::numeric_limits::min(), + std::numeric_limits::min() }; + + while (TileElementIteratorNext(&it)) + { + if (it.element->GetType() != TileElementType::Track) + continue; + + if (it.element->AsTrack()->GetRideIndex() != ride.id) + continue; + + auto location = TileCoordsXY(it.x, it.y).ToCoordsXY(); + int32_t baseZ = it.element->GetBaseZ(); + int32_t clearZ = it.element->GetClearanceZ(); + + min.x = std::min(min.x, location.x); + min.y = std::min(min.y, location.y); + min.z = std::min(min.z, baseZ); + + max.x = std::max(max.x, location.x); + max.y = std::max(max.y, location.y); + max.z = std::max(max.z, clearZ); + } + + const auto rideIndex = ride.id.ToUnderlying(); + if (rideIndex >= ride_overall_views.size()) + { + ride_overall_views.resize(rideIndex + 1); + } + + auto& view = ride_overall_views[rideIndex]; + view.loc = CoordsXYZ{ (min.x + max.x) / 2, (min.y + max.y) / 2, (min.z + max.z) / 2 } + CoordsXYZ{ 16, 16, -8 }; + + // Calculate size to determine from how far away to view the ride + const auto diff = max - min; + + const int32_t size = static_cast(std::sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z)); + + if (size >= 80) + { + // Each farther zoom level shows twice as many tiles (log) + // Appropriate zoom is lowered by one to fill the entire view with the ride + const auto zoomValue = static_cast(std::ceil(std::log(size / 80)) - 1); + view.zoom = std::clamp(ZoomLevel{ zoomValue }, ZoomLevel{ 0 }, ZoomLevel::max()); + } + else + { + // Small rides or stalls are zoomed in all the way. + view.zoom = ZoomLevel{ 0 }; + } + } + + void SetPage(int32_t newPage) + { + int32_t listen; + + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) + ToolCancel(); + + if (newPage == WINDOW_RIDE_PAGE_VEHICLE) + { + auto constructionWindow = WindowFindByClass(WindowClass::RideConstruction); + if (constructionWindow != nullptr && constructionWindow->number == number) + { + WindowCloseByClass(WindowClass::RideConstruction); + // Closing the construction window sets the tab to the first page, which we don't want here, + // as user just clicked the Vehicle page + SetPage(WINDOW_RIDE_PAGE_VEHICLE); + } + } + + // Set listen only to viewport + listen = 0; + if (newPage == WINDOW_RIDE_PAGE_MAIN && page == WINDOW_RIDE_PAGE_MAIN && viewport != nullptr + && !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) + listen++; + + page = newPage; + frame_no = 0; + picked_peep_frame = 0; + + // There doesn't seem to be any need for this call, and it can sometimes modify the reported number of cars per train, + // so I've removed it if (newPage == WINDOW_RIDE_PAGE_VEHICLE) { ride_update_max_vehicles(ride); + //} + + RemoveViewport(); + + hold_down_widgets = window_ride_page_hold_down_widgets[page]; + event_handlers = window_ride_page_events[page]; + pressed_widgets = 0; + widgets = window_ride_page_widgets[page]; + DisableTabs(); + Invalidate(); + + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); + + if (listen != 0 && viewport != nullptr) + viewport->flags |= VIEWPORT_FLAG_SOUND_ON; + } + + void SetPressedTab() + { + int32_t i; + for (i = 0; i < WINDOW_RIDE_PAGE_COUNT; i++) + pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); + pressed_widgets |= 1LL << (WIDX_TAB_1 + page); + } + + void AnchorBorderWidgets() + { + ResizeFrameWithPage(); + } + +#pragma region Main + + std::optional GetStationIndexFromViewSelection() const + { + const auto* ride = GetRide(RideId::FromUnderlying(number)); + if (ride == nullptr) + return std::nullopt; + + int32_t viewSelectionIndex = this->ride.view - 1 - ride->NumTrains; + if (viewSelectionIndex < 0) + { + return std::nullopt; + } + + for (const auto& station : ride->GetStations()) + { + if (!station.Start.IsNull() && viewSelectionIndex-- == 0) + { + const auto stationIndex = ride->GetStationIndex(&station); + return std::make_optional(stationIndex); + } + } + return std::nullopt; + } + + void InitViewport() + { + if (page != WINDOW_RIDE_PAGE_MAIN) + return; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + int32_t viewSelectionIndex = this->ride.view - 1; + + std::optional newFocus; + + if (viewSelectionIndex >= 0 && viewSelectionIndex < ride->NumTrains && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) + { + auto vehId = ride->vehicles[viewSelectionIndex]; + const auto* rideEntry = ride->GetRideEntry(); + if (rideEntry != nullptr && rideEntry->TabCar != 0) + { + Vehicle* vehicle = GetEntity(vehId); + if (vehicle == nullptr) + { + vehId = EntityId::GetNull(); + } + else if (!vehicle->next_vehicle_on_train.IsNull()) + { + vehId = vehicle->next_vehicle_on_train; + } + } + if (!vehId.IsNull()) + { + newFocus = Focus(vehId); + } + } + else if (viewSelectionIndex >= ride->NumTrains && viewSelectionIndex < (ride->NumTrains + ride->num_stations)) + { + auto stationIndex = GetStationIndexFromViewSelection(); + if (stationIndex) + { + const auto location = ride->GetStation(*stationIndex).GetStart(); + newFocus = Focus(location); + } + } + else + { + if (viewSelectionIndex > 0) + { + this->ride.view = 0; + } + if (number < ride_overall_views.size()) + { + const auto& view = ride_overall_views[number]; + newFocus = Focus(view.loc, view.zoom); + } + } + + uint16_t viewport_flags = 0; + if (viewport != nullptr) + { + if (focus == newFocus) + { + return; + } + viewport_flags = viewport->flags; + RemoveViewport(); + } + else if (gConfigGeneral.AlwaysShowGridlines) + { + viewport_flags |= VIEWPORT_FLAG_GRIDLINES; + } + + OnPrepareDraw(); + + focus = newFocus; + + // rct2: 0x006aec9c only used here so brought it into the function + if (viewport == nullptr && !ride->overall_view.IsNull() && focus.has_value()) + { + const auto& view_widget = widgets[WIDX_VIEWPORT]; + + auto screenPos = windowPos + ScreenCoordsXY{ view_widget.left + 1, view_widget.top + 1 }; + int32_t width = view_widget.width() - 1; + int32_t height = view_widget.height() - 1; + + ViewportCreate(this, screenPos, width, height, focus.value()); + + flags |= WF_NO_SCROLLING; + Invalidate(); + } + if (viewport != nullptr) + { + viewport->flags = viewport_flags; + Invalidate(); + } + } + + void Rename() + { + auto ride = GetRide(rideId); + if (ride != nullptr) + { + auto rideName = ride->GetName(); + WindowTextInputRawOpen( + this, WIDX_RENAME, STR_RIDE_ATTRACTION_NAME, STR_ENTER_NEW_NAME_FOR_THIS_RIDE_ATTRACTION, {}, rideName.c_str(), + 32); + } + } + + void MainMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + return; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(widgetIndex - WIDX_TAB_1); + break; + case WIDX_CONSTRUCTION: + { + auto ride = GetRide(rideId); + if (ride != nullptr) + { + RideConstructionStart(*ride); + if (WindowFindByNumber(WindowClass::RideConstruction, ride->id.ToUnderlying()) != nullptr) + { + Close(); + return; + } + } + break; + } + case WIDX_RENAME: + Rename(); + break; + case WIDX_DEMOLISH: + ContextOpenDetailWindow(WD_DEMOLISH_RIDE, number); + break; + case WIDX_CLOSE_LIGHT: + case WIDX_SIMULATE_LIGHT: + case WIDX_TEST_LIGHT: + case WIDX_OPEN_LIGHT: + { + auto ride = GetRide(rideId); + if (ride != nullptr) + { + RideStatus status; + switch (widgetIndex) + { + default: + case WIDX_CLOSE_LIGHT: + status = RideStatus::Closed; + break; + case WIDX_SIMULATE_LIGHT: + status = RideStatus::Simulating; + break; + case WIDX_TEST_LIGHT: + status = RideStatus::Testing; + break; + case WIDX_OPEN_LIGHT: + status = RideStatus::Open; + break; + } + auto gameAction = RideSetStatusAction(ride->id, status); + GameActions::Execute(&gameAction); + } + break; + } + } + } + + void MainResize() + { + int32_t minHeight = 180; + if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) + { + minHeight += 20 + RCT1_LIGHT_OFFSET; + + auto ride = GetRide(rideId); + if (ride != nullptr) + { +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + if (ride->SupportsStatus(RideStatus::Simulating)) + { + minHeight += 14; + } +#endif + if (ride->SupportsStatus(RideStatus::Testing)) + { + minHeight += 14; + } + } + } + if (gCheatsAllowArbitraryRideTypeChanges) + { + minHeight += 15; + } + + flags |= WF_RESIZABLE; + WindowSetResize(*this, 316, minHeight, 500, 450); + // Unlike with other windows, the focus needs to be recentred so it’s best to just reset it. + focus = std::nullopt; + InitViewport(); + } + + size_t GetNumPeepsInTrain(const Ride& ride, int32_t trainIndex) const + { + auto numPeepsInTrain = 0; + const auto* vehicle = TryGetVehicle(ride.vehicles[trainIndex]); + while (vehicle != nullptr) + { + numPeepsInTrain += vehicle->num_peeps; + vehicle = TryGetVehicle(vehicle->next_vehicle_on_train); + } + return numPeepsInTrain; + } + + bool TrainMustBeHidden(const Ride& ride, int32_t trainIndex) const + { + if (!(ride.lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + return true; + + const auto* rideEntry = ride.GetRideEntry(); + if (rideEntry == nullptr) + return false; + + if (!(rideEntry->flags & RIDE_ENTRY_FLAG_HIDE_EMPTY_TRAINS)) + return false; + + return GetNumPeepsInTrain(ride, trainIndex) == 0; + } + + void ShowViewDropdown(Widget* widget) + { + Widget* dropdownWidget = widget - 1; + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + const auto& rtd = ride->GetRideTypeDescriptor(); + + int32_t numItems = 1; + if (!rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) + { + numItems += ride->num_stations; + numItems += ride->NumTrains; + } + + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], + 0, 0, numItems, widget->right - dropdownWidget->left); + + // First item + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_OVERALL_VIEW; + int32_t currentItem = 1; + + // Vehicles + int32_t name = GetRideComponentName(rtd.NameConvention.vehicle).number; + for (int32_t i = 0; i < ride->NumTrains; i++) + { + gDropdownItems[currentItem].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[currentItem].Args = name | (currentItem << 16); + if (TrainMustBeHidden(*ride, i)) + { + Dropdown::SetDisabled(currentItem, true); + } + currentItem++; + } + + // Stations + name = GetRideComponentName(rtd.NameConvention.station).number; + for (int32_t i = 1; i <= ride->num_stations; i++) + { + gDropdownItems[currentItem].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[currentItem].Args = name | (i << 16); + currentItem++; + } + + // Set checked item + Dropdown::SetChecked(this->ride.view, true); + } + + RideStatus GetNextDefaultStatus(const Ride& ride) const + { + switch (ride.status) + { + default: + case RideStatus::Closed: + if ((ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED) + || (ride.lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE)) + { + return RideStatus::Closed; + } + if (ride.SupportsStatus(RideStatus::Testing) && !(ride.lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + { + return RideStatus::Testing; + } + return RideStatus::Open; + case RideStatus::Simulating: + return RideStatus::Testing; + case RideStatus::Testing: + return (ride.lifecycle_flags & RIDE_LIFECYCLE_TESTED) ? RideStatus::Open : RideStatus::Closed; + case RideStatus::Open: + return RideStatus::Closed; + } + } + + struct RideStatusDropdownInfo + { + struct Ride* Ride{}; + RideStatus CurrentStatus{}; + RideStatus DefaultStatus{}; + + int32_t NumItems{}; + int32_t CheckedIndex = -1; + int32_t DefaultIndex = -1; + }; + + void SetDropdown(RideStatusDropdownInfo& info, RideStatus status, StringId text) const + { + if (info.Ride->SupportsStatus(status)) + { + auto index = info.NumItems; + gDropdownItems[index].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[index].Args = text; + if (info.CurrentStatus == status) + { + info.CheckedIndex = index; + } + if (info.DefaultStatus == status) + { + info.DefaultIndex = index; + } + info.NumItems++; + } + } + + void ShowOpenDropdown(Widget* widget) + { + RideStatusDropdownInfo info; + info.Ride = GetRide(rideId); + if (info.Ride == nullptr) + return; + + info.CurrentStatus = info.Ride->status; + info.DefaultStatus = GetNextDefaultStatus(*info.Ride); + SetDropdown(info, RideStatus::Closed, STR_CLOSE_RIDE); +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + SetDropdown(info, RideStatus::Simulating, STR_SIMULATE_RIDE); +#endif + SetDropdown(info, RideStatus::Testing, STR_TEST_RIDE); + SetDropdown(info, RideStatus::Open, STR_OPEN_RIDE); + WindowDropdownShowText( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, info.NumItems); + Dropdown::SetChecked(info.CheckedIndex, true); + gDropdownDefaultIndex = info.DefaultIndex; + } + + static constexpr StringId GetRideTypeNameForDropdown(ride_type_t rideType) + { + switch (rideType) + { + case RIDE_TYPE_1D: + return STR_RIDE_NAME_1D; + case RIDE_TYPE_1F: + return STR_RIDE_NAME_1F; + case RIDE_TYPE_22: + return STR_RIDE_NAME_22; + case RIDE_TYPE_50: + return STR_RIDE_NAME_50; + case RIDE_TYPE_52: + return STR_RIDE_NAME_52; + case RIDE_TYPE_53: + return STR_RIDE_NAME_53; + case RIDE_TYPE_54: + return STR_RIDE_NAME_54; + case RIDE_TYPE_55: + return STR_RIDE_NAME_55; + case RIDE_TYPE_59: + return STR_RIDE_NAME_59; + default: + return GetRideTypeDescriptor(rideType).Naming.Name; + } + } + + void PopulateRideTypeDropdown() const + { + auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); + if (RideDropdownDataLanguage == ls.GetCurrentLanguage()) + return; + + RideDropdownData.clear(); + + for (uint8_t i = 0; i < RIDE_TYPE_COUNT; i++) + { + auto name = GetRideTypeNameForDropdown(i); + RideDropdownData.push_back({ i, name, ls.GetString(name) }); + } + + std::sort(RideDropdownData.begin(), RideDropdownData.end(), [](auto& a, auto& b) { + return String::Compare(a.label_string, b.label_string, true) < 0; + }); + + RideDropdownDataLanguage = ls.GetCurrentLanguage(); + } + + void ShowRideTypeDropdown(Widget* widget) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + PopulateRideTypeDropdown(); + + for (size_t i = 0; i < RideDropdownData.size(); i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = RideDropdownData[i].label_id; + } + + Widget* dropdownWidget = widget - 1; + WindowDropdownShowText( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], + Dropdown::Flag::StayOpen, RIDE_TYPE_COUNT); + + // Find the current ride type in the ordered list. + int32_t pos = 0; + for (int32_t i = 0; i < RIDE_TYPE_COUNT; i++) + { + if (RideDropdownData[i].ride_type_id == ride->type) + { + pos = i; + break; + } + } + + gDropdownHighlightedIndex = pos; + gDropdownDefaultIndex = pos; + Dropdown::SetChecked(pos, true); + } + + void ShowLocateDropdown(Widget* widget) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + gDropdownItems[0].Format = STR_LOCATE_SUBJECT_TIP; + gDropdownItems[1].Format = STR_FOLLOW_SUBJECT_TIP; + + WindowDropdownShowText( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, 2); + gDropdownDefaultIndex = 0; + if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK) || this->ride.view == 0 + || this->ride.view > ride->NumTrains) + { + // Disable if we're a flat ride, 'overall view' is selected or a station is selected + Dropdown::SetDisabled(1, true); + } + } + + void MainFollowRide() + { + auto* ride = GetRide(rideId); + if (ride != nullptr) + { + if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) + { + if (this->ride.view > 0) + { + if (this->ride.view <= ride->NumTrains) + { + Vehicle* vehicle = GetEntity(ride->vehicles[this->ride.view - 1]); + if (vehicle != nullptr) + { + auto headVehicleSpriteIndex = vehicle->Id; + WindowBase* w_main = WindowGetMain(); + WindowFollowSprite(*w_main, headVehicleSpriteIndex); + } + } + } + } + } + } + + void PopulateVehicleTypeDropdown(const Ride& ride, bool forceRefresh = false) const + { + auto& objManager = GetContext()->GetObjectManager(); + const auto* rideEntry = ride.GetRideEntry(); + + bool selectionShouldBeExpanded; + int32_t rideTypeIterator, rideTypeIteratorMax; + + const auto& rtd = ride.GetRideTypeDescriptor(); + if (gCheatsShowVehiclesFromOtherTrackTypes + && !( + rtd.HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE) || rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE) + || ride.type == RIDE_TYPE_MINI_GOLF)) + { + selectionShouldBeExpanded = true; + rideTypeIterator = 0; + rideTypeIteratorMax = RIDE_TYPE_COUNT - 1; + } + else + { + selectionShouldBeExpanded = false; + rideTypeIterator = ride.type; + rideTypeIteratorMax = ride.type; + } + + // Don't repopulate the list if we just did. + auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); + if (!forceRefresh && VehicleDropdownExpanded == selectionShouldBeExpanded && VehicleDropdownRideType == rideEntry + && VehicleDropdownDataLanguage == ls.GetCurrentLanguage()) + return; + + VehicleDropdownData.clear(); + + for (; rideTypeIterator <= rideTypeIteratorMax; rideTypeIterator++) + { + const auto& rtdIterator = GetRideTypeDescriptor(rideTypeIterator); + if (selectionShouldBeExpanded && rtdIterator.HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE)) + continue; + if (selectionShouldBeExpanded + && (rtdIterator.HasFlag(RIDE_TYPE_FLAG_IS_MAZE) || rideTypeIterator == RIDE_TYPE_MINI_GOLF)) + continue; + + auto& rideEntries = objManager.GetAllRideEntries(rideTypeIterator); + for (auto rideEntryIndex : rideEntries) + { + const auto* currentRideEntry = GetRideEntryByIndex(rideEntryIndex); + if (currentRideEntry == nullptr) + continue; + + // Skip if vehicle type has not been invented yet + if (!RideEntryIsInvented(rideEntryIndex) && !gCheatsIgnoreResearchStatus) + continue; + + VehicleDropdownData.push_back( + { rideEntryIndex, currentRideEntry->naming.Name, ls.GetString(currentRideEntry->naming.Name) }); + } + } + + std::sort(VehicleDropdownData.begin(), VehicleDropdownData.end(), [](auto& a, auto& b) { + return String::Compare(a.label_string, b.label_string, true) < 0; + }); + + VehicleDropdownExpanded = selectionShouldBeExpanded; + VehicleDropdownRideType = rideEntry; + VehicleDropdownDataLanguage = ls.GetCurrentLanguage(); + } + + void ShowVehicleTypeDropdown(Widget* widget) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + PopulateVehicleTypeDropdown(*ride); + + size_t numItems = std::min(VehicleDropdownData.size(), Dropdown::ItemsMaxSize); + + for (size_t i = 0; i < numItems; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = VehicleDropdownData[i].label_id; + } + + Widget* dropdownWidget = widget - 1; + auto width = WindowDropDownHasMultipleColumns(numItems) ? dropdownWidget->width() - 24 : dropdownWidget->width(); + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], + 0, Dropdown::Flag::StayOpen, numItems, width); + + // Find the current vehicle type in the ordered list. + int32_t pos = 0; + for (int32_t i = 0; i < static_cast(VehicleDropdownData.size()); i++) + { + if (VehicleDropdownData[i].subtype_id == ride->subtype) + { + pos = i; + break; + } + } + + gDropdownHighlightedIndex = pos; + gDropdownDefaultIndex = pos; + Dropdown::SetChecked(pos, true); + } + + void MainMousedown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_VIEW_DROPDOWN: + ShowViewDropdown(widget); + break; + case WIDX_OPEN: + ShowOpenDropdown(widget); + break; + case WIDX_RIDE_TYPE_DROPDOWN: + ShowRideTypeDropdown(widget); + break; + case WIDX_LOCATE: + ShowLocateDropdown(widget); + break; + } + } + + void MainDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_VIEW_DROPDOWN: + if (dropdownIndex == -1) + { + dropdownIndex = ride.view + 1; + auto ride = GetRide(rideId); + if (ride != nullptr) + { + if (dropdownIndex != 0 && dropdownIndex <= ride->NumTrains + && !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + { + dropdownIndex = ride->NumTrains + 1; + } + if (dropdownIndex >= gDropdownNumItems) + { + dropdownIndex = 0; + } + } + } + + ride.view = dropdownIndex; + InitViewport(); + Invalidate(); + break; + case WIDX_OPEN: + { + auto ride = GetRide(rideId); + if (ride != nullptr) + { + auto status = RideStatus::Closed; + if (dropdownIndex < 0) + { + dropdownIndex = gDropdownHighlightedIndex; + } + if (dropdownIndex < static_cast(std::size(gDropdownItems))) + { + switch (gDropdownItems[dropdownIndex].Args) + { + case STR_CLOSE_RIDE: + status = RideStatus::Closed; + break; + case STR_SIMULATE_RIDE: + status = RideStatus::Simulating; + break; + case STR_TEST_RIDE: + status = RideStatus::Testing; + break; + case STR_OPEN_RIDE: + status = RideStatus::Open; + break; + } + } + auto gameAction = RideSetStatusAction(ride->id, status); + GameActions::Execute(&gameAction); + } + break; + } + case WIDX_RIDE_TYPE_DROPDOWN: + if (dropdownIndex != -1 && dropdownIndex < RIDE_TYPE_COUNT) + { + auto rideLabelId = std::clamp(dropdownIndex, 0, RIDE_TYPE_COUNT - 1); + auto rideType = RideDropdownData[rideLabelId].ride_type_id; + if (rideType < RIDE_TYPE_COUNT) + { + auto rideSetSetting = RideSetSettingAction(rideId, RideSetSetting::RideType, rideType); + rideSetSetting.SetCallback([](const GameAction* ga, const GameActions::Result* result) { + // Reset ghost track if ride construction window is open, prevents a crash + // Will get set to the correct Alternative variable during set_default_next_piece. + // TODO: Rework construction window to prevent the need for this. + _currentTrackAlternative = RIDE_TYPE_NO_ALTERNATIVES; + RideConstructionSetDefaultNextPiece(); + }); + GameActions::Execute(&rideSetSetting); + } + } + break; + case WIDX_LOCATE: + { + if (dropdownIndex == 0) + { + ScrollToViewport(); + } + else if (dropdownIndex == 1) + { + MainFollowRide(); + } + break; + } + } + } + + void MainUpdate() + { + // Update tab animation + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_1); + + // Update status + auto ride = GetRide(rideId); + if (ride != nullptr) + { + if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) + { + if (this->ride.view == 0) + return; + + if (this->ride.view <= ride->NumTrains) + { + Vehicle* vehicle = GetEntity(ride->vehicles[this->ride.view - 1]); + if (vehicle == nullptr + || (vehicle->status != Vehicle::Status::Travelling + && vehicle->status != Vehicle::Status::TravellingCableLift + && vehicle->status != Vehicle::Status::TravellingDodgems + && vehicle->status != Vehicle::Status::TravellingBoat)) + { + return; + } + } + } + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAIN; + } + WidgetInvalidate(*this, WIDX_STATUS); + } + + void MainTextinput(WidgetIndex widgetIndex, const char* text) + { + if (widgetIndex != WIDX_RENAME || text == nullptr) + return; + + auto ride = GetRide(rideId); + if (ride != nullptr) + { + auto gameAction = RideSetNameAction(ride->id, text); + GameActions::Execute(&gameAction); + } + } + + void MainViewportRotate() + { + InitViewport(); + } + + void MainOnPrepareDraw() + { + Widget* newWidgets; + int32_t i, widgetHeight; + + newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + disabled_widgets &= ~((1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_CONSTRUCTION)); + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_INDESTRUCTIBLE | RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) + && !gCheatsMakeAllDestructible) + disabled_widgets |= (1uLL << WIDX_DEMOLISH); + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + uint32_t spriteIds[] = { + SPR_CLOSED, + SPR_OPEN, + SPR_TESTING, + SPR_G2_SIMULATE, + }; + window_ride_main_widgets[WIDX_OPEN].image = ImageId(spriteIds[EnumValue(ride->status)]); + +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2 + + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT); + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0 + + (ride->status == RideStatus::Simulating) * 2 + WidgetIsPressed(*w, WIDX_SIMULATE_LIGHT); + window_ride_main_widgets[WIDX_TEST_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0 + (ride->status == RideStatus::Testing) * 2 + + WidgetIsPressed(*this, WIDX_TEST_LIGHT); +#else + const auto closeLightImage = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2 + + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT); + window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = ImageId(closeLightImage); + + auto baseSprite = ride->status == RideStatus::Simulating ? SPR_G2_RCT1_SIMULATE_BUTTON_0 : SPR_G2_RCT1_TEST_BUTTON_0; + const auto testLightImage = baseSprite + + (ride->status == RideStatus::Testing || ride->status == RideStatus::Simulating) * 2 + + WidgetIsPressed(*this, WIDX_TEST_LIGHT); + window_ride_main_widgets[WIDX_TEST_LIGHT].image = ImageId(testLightImage); +#endif + const auto openLightImage = SPR_G2_RCT1_OPEN_BUTTON_0 + (ride->status == RideStatus::Open) * 2 + + WidgetIsPressed(*this, WIDX_OPEN_LIGHT); + window_ride_main_widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); + + AnchorBorderWidgets(); + + const int32_t offset = gCheatsAllowArbitraryRideTypeChanges ? 15 : 0; + // Anchor main page specific widgets + window_ride_main_widgets[WIDX_VIEWPORT].right = width - 26; + window_ride_main_widgets[WIDX_VIEWPORT].bottom = height - (14 + offset); + window_ride_main_widgets[WIDX_STATUS].right = width - 26; + window_ride_main_widgets[WIDX_STATUS].top = height - (13 + offset); + window_ride_main_widgets[WIDX_STATUS].bottom = height - (3 + offset); + window_ride_main_widgets[WIDX_VIEW].right = width - 60; + window_ride_main_widgets[WIDX_VIEW_DROPDOWN].right = width - 61; + window_ride_main_widgets[WIDX_VIEW_DROPDOWN].left = width - 71; + window_ride_main_widgets[WIDX_RIDE_TYPE].right = width - 26; + window_ride_main_widgets[WIDX_RIDE_TYPE].top = height - 17; + window_ride_main_widgets[WIDX_RIDE_TYPE].bottom = height - 4; + window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].left = width - 37; + window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].right = width - 27; + window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].top = height - 16; + window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].bottom = height - 5; + + if (!gCheatsAllowArbitraryRideTypeChanges) + { + window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::Empty; + window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Empty; + } + else + { + window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::DropdownMenu; + window_ride_main_widgets[WIDX_RIDE_TYPE].text = ride->GetRideTypeDescriptor().Naming.Name; + window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Button; + } + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + + if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) + { + window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::Empty; + window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + if (ride->SupportsStatus(RideStatus::Simulating)) + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::ImgBtn; +#endif + window_ride_main_widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RideStatus::Testing) + ? WindowWidgetType::ImgBtn + : WindowWidgetType::Empty; + window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; + + widgetHeight = 62; + if (window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type != WindowWidgetType::Empty) + { + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].top = widgetHeight; + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].bottom = widgetHeight + 13; + widgetHeight += 14; + } + if (window_ride_main_widgets[WIDX_TEST_LIGHT].type != WindowWidgetType::Empty) + { + window_ride_main_widgets[WIDX_TEST_LIGHT].top = widgetHeight; + window_ride_main_widgets[WIDX_TEST_LIGHT].bottom = widgetHeight + 13; + widgetHeight += 14; + } + window_ride_main_widgets[WIDX_OPEN_LIGHT].top = widgetHeight; + window_ride_main_widgets[WIDX_OPEN_LIGHT].bottom = widgetHeight + 13; + widgetHeight += 14 - 24 + RCT1_LIGHT_OFFSET; + } + else + { + window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::FlatBtn; + window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; + window_ride_main_widgets[WIDX_TEST_LIGHT].type = WindowWidgetType::Empty; + window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; + widgetHeight = 46; + } + for (i = WIDX_CLOSE_LIGHT; i <= WIDX_OPEN_LIGHT; i++) + { + window_ride_main_widgets[i].left = width - 20; + window_ride_main_widgets[i].right = width - 7; + } + for (i = WIDX_OPEN; i <= WIDX_DEMOLISH; i++, widgetHeight += 24) + { + window_ride_main_widgets[i].left = width - 25; + window_ride_main_widgets[i].right = width - 2; + window_ride_main_widgets[i].top = widgetHeight; + window_ride_main_widgets[i].bottom = widgetHeight + 23; + } + } + + StringId GetStatusOverallView(Formatter& ft) const + { + auto stringId = STR_NONE; + auto ride = GetRide(rideId); + if (ride != nullptr) + { + ride->FormatStatusTo(ft); + stringId = STR_BLACK_STRING; + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) + { + stringId = STR_RED_OUTLINED_STRING; + } + } + return stringId; + } + + StringId GetStatusVehicle(Formatter& ft) const + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return STR_EMPTY; + + auto vehicle = GetEntity(ride->vehicles[this->ride.view - 1]); + if (vehicle == nullptr) + return STR_EMPTY; + + if (vehicle->status != Vehicle::Status::Crashing && vehicle->status != Vehicle::Status::Crashed) + { + auto trackType = vehicle->GetTrackType(); + if (trackType == TrackElemType::BlockBrakes || trackType == TrackElemType::CableLiftHill + || trackType == TrackElemType::Up25ToFlat || trackType == TrackElemType::Up60ToFlat + || trackType == TrackElemType::DiagUp25ToFlat || trackType == TrackElemType::DiagUp60ToFlat) + { + if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_BLOCK_BRAKES) && vehicle->velocity == 0) + { + ft.Add(STR_STOPPED_BY_BLOCK_BRAKES); + return STR_BLACK_STRING; + } + } + } + + if (ride->type == RIDE_TYPE_MINI_GOLF) + return STR_EMPTY; + + auto stringId = VehicleStatusNames[static_cast(vehicle->status)]; + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SINGLE_SESSION) + && vehicle->status <= Vehicle::Status::UnloadingPassengers) + { + stringId = SingleSessionVehicleStatusNames[static_cast(vehicle->status)]; + } + + ft.Add(stringId); + uint16_t speedInMph = (abs(vehicle->velocity) * 9) >> 18; + ft.Add(speedInMph); + const RideComponentName stationName = GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station); + ft.Add(ride->num_stations > 1 ? stationName.number : stationName.singular); + ft.Add(vehicle->current_station.ToUnderlying() + 1); + return stringId != STR_CRASHING && stringId != STR_CRASHED_0 ? STR_BLACK_STRING : STR_RED_OUTLINED_STRING; + } + + StringId GetStatusStation(Formatter& ft) const + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return STR_NONE; + + const auto stationIndex = GetStationIndexFromViewSelection(); + if (!stationIndex) + { + return STR_NONE; + } + + const auto& station = ride->GetStation(*stationIndex); + StringId stringId = STR_EMPTY; + // Entrance / exit + if (ride->status == RideStatus::Closed) + { + if (station.Entrance.IsNull()) + stringId = STR_NO_ENTRANCE; + else if (station.Exit.IsNull()) + stringId = STR_NO_EXIT; + } + else + { + if (station.Entrance.IsNull()) + stringId = STR_EXIT_ONLY; + } + // Queue length + if (stringId == STR_EMPTY) + { + stringId = STR_QUEUE_EMPTY; + uint16_t queueLength = ride->GetStation(*stationIndex).QueueLength; + if (queueLength == 1) + stringId = STR_QUEUE_ONE_PERSON; + else if (queueLength > 1) + stringId = STR_QUEUE_PEOPLE; + + ft.Add(stringId); + ft.Add(queueLength); + } + else + { + ft.Add(stringId); + } + + return STR_BLACK_STRING; + } + + StringId GetStatus(Formatter& ft) const + { + auto ride = GetRide(rideId); + if (this->ride.view == 0) + return GetStatusOverallView(ft); + if (ride != nullptr && this->ride.view <= ride->NumTrains) + return GetStatusVehicle(ft); + if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + return GetStatusOverallView(ft); + return GetStatusStation(ft); + } + + void MainOnPaint(DrawPixelInfo& dpi) + { + Widget* widget; + + WindowDrawWidgets(*this, dpi); + DrawTabImages(dpi); + + // Viewport and ear icon + if (viewport != nullptr) + { + WindowDrawViewport(dpi, *this); + if (viewport->flags & VIEWPORT_FLAG_SOUND_ON) + GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this)); + } + + // View dropdown + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto ft = Formatter(); + if (this->ride.view != 0) + { + if (this->ride.view > ride->NumTrains) + { + ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station).number); + ft.Add(this->ride.view - ride->NumTrains); + } + else + { + ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number); + ft.Add(this->ride.view); + } + } + else + { + ft.Add(STR_OVERALL_VIEW); + } + + widget = &window_ride_main_widgets[WIDX_VIEW]; + DrawTextBasic( + dpi, { windowPos.x + (widget->left + widget->right - 11) / 2, windowPos.y + widget->top }, + STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::CENTRE }); + + // Status + ft = Formatter(); + widget = &window_ride_main_widgets[WIDX_STATUS]; + StringId rideStatus = GetStatus(ft); + DrawTextEllipsised( + dpi, windowPos + ScreenCoordsXY{ (widget->left + widget->right) / 2, widget->top }, widget->width(), rideStatus, ft, + { TextAlignment::CENTRE }); + } + +#pragma endregion + +#pragma region Vehicle + + void VehicleMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + return; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(widgetIndex - WIDX_TAB_1); + break; + } + } + + void VehicleResize() + { + WindowSetResize(*this, 316, 221, 316, 221); + } + + void VehicleMousedown(WidgetIndex widgetIndex, Widget* widget) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + switch (widgetIndex) + { + case WIDX_VEHICLE_TYPE_DROPDOWN: + ShowVehicleTypeDropdown(&widgets[widgetIndex]); + break; + case WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX: + ride->SetReversedTrains(!ride->HasLifecycleFlag(RIDE_LIFECYCLE_REVERSED_TRAINS)); + break; + case WIDX_VEHICLE_TRAINS_INCREASE: + if (ride->NumTrains < OpenRCT2::Limits::MaxTrainsPerRide) + ride->SetNumTrains(ride->NumTrains + 1); + break; + case WIDX_VEHICLE_TRAINS_DECREASE: + if (ride->NumTrains > 1) + ride->SetNumTrains(ride->NumTrains - 1); + break; + case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE: + if (ride->num_cars_per_train < OpenRCT2::Limits::MaxCarsPerTrain) + ride->SetNumCarsPerVehicle(ride->num_cars_per_train + 1); + break; + case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE: + if (ride->num_cars_per_train > 1) + ride->SetNumCarsPerVehicle(ride->num_cars_per_train - 1); + break; + } + } + + void VehicleDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + return; + + switch (widgetIndex) + { + case WIDX_VEHICLE_TYPE_DROPDOWN: + if (dropdownIndex >= 0 && static_cast(dropdownIndex) < VehicleDropdownData.size()) + { + auto ride = GetRide(rideId); + if (ride != nullptr) + { + auto newRideType = VehicleDropdownData[dropdownIndex].subtype_id; + ride->SetRideEntry(newRideType); + } + } + break; + } + } + + void VehicleUpdate() + { + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_2); + } + + OpenRCT2String VehicleTooltip(WindowBase* const w, const WidgetIndex widgetIndex, StringId fallback) + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return { STR_NONE, {} }; + + switch (widgetIndex) + { + case WIDX_VEHICLE_TRAINS: + case WIDX_VEHICLE_TRAINS_DECREASE: + case WIDX_VEHICLE_TRAINS_INCREASE: + { + auto ft = Formatter(); + ft.Increment(12); + + RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle; + StringId stringId = GetRideComponentName(vehicleType).count; + if (ride->max_trains > 1) + { + stringId = GetRideComponentName(vehicleType).count_plural; + } + ft.Add(stringId); + ft.Add(ride->max_trains); + return { fallback, ft }; + } + case WIDX_VEHICLE_CARS_PER_TRAIN: + case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE: + case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE: + { + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return { STR_NONE, {} }; + + auto ft = Formatter(); + ft.Increment(16); + ft.Add(std::max(uint8_t(1), ride->MaxCarsPerTrain) - rideEntry->zero_cars); + + StringId stringId = GetRideComponentName(RideComponentType::Car).singular; + if (ride->MaxCarsPerTrain - rideEntry->zero_cars > 1) + { + stringId = GetRideComponentName(RideComponentType::Car).plural; + } + ft.Add(stringId); + return { fallback, ft }; + } + } + return { fallback, {} }; + } + + void VehicleOnPrepareDraw() + { + StringId stringId; + int32_t carsPerTrain; + + auto* newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + const auto* rideEntry = ride->GetRideEntry(); + + widgets[WIDX_TITLE].text = STR_ARG_20_STRINGID; + + // Widget setup + carsPerTrain = ride->num_cars_per_train - rideEntry->zero_cars; + + // Vehicle type + window_ride_vehicle_widgets[WIDX_VEHICLE_TYPE].text = rideEntry->naming.Name; + + // Trains + if (rideEntry->cars_per_flat_ride > 1 || gCheatsDisableTrainLengthLimit) + { + window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Spinner; + window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Button; + window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Button; + } + else + { + window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Empty; + window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Empty; + window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Empty; + } + + // Cars per train + if (rideEntry->zero_cars + 1 < rideEntry->max_cars_in_train || gCheatsDisableTrainLengthLimit) + { + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Spinner; + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Button; + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Button; + } + else + { + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Empty; + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Empty; + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Empty; + } + + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_ALLOW_REVERSED_TRAINS) + || (gCheatsDisableTrainLengthLimit && !ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))) + { + window_ride_vehicle_widgets[WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX].type = WindowWidgetType::Checkbox; + if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_REVERSED_TRAINS)) + { + pressed_widgets |= (1uLL << WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX); + } + else + { + pressed_widgets &= ~(1uLL << WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX); + } + } + else + { + window_ride_vehicle_widgets[WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX].type = WindowWidgetType::Empty; + } + + auto ft = Formatter::Common(); + ft.Increment(6); + ft.Add(carsPerTrain); + RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle; + stringId = GetRideComponentName(vehicleType).count; + if (ride->NumTrains > 1) + { + stringId = GetRideComponentName(vehicleType).count_plural; + } + ft.Add(stringId); + ft.Add(ride->NumTrains); + + ft.Increment(8); + + ride->FormatNameTo(ft); + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + + if (abs(ride->num_cars_per_train - rideEntry->zero_cars) == 1) + { + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_1_CAR_PER_TRAIN; + } + else + { + window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_X_CARS_PER_TRAIN; + } + } + + void VehiclePaint(DrawPixelInfo& dpi) + { + WindowDrawWidgets(*this, dpi); + DrawTabImages(dpi); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto screenCoords = windowPos + ScreenCoordsXY{ 8, 64 }; + + // Description + auto ft = Formatter(); + ft.Add(rideEntry->naming.Description); + screenCoords.y += DrawTextWrapped(dpi, screenCoords, 300, STR_BLACK_STRING, ft, { TextAlignment::LEFT }); + screenCoords.y += 2; + + // Capacity + ft = Formatter(); + ft.Add(rideEntry->capacity); + DrawTextBasic(dpi, screenCoords, STR_CAPACITY, ft); + + // Excitement Factor + if (rideEntry->excitement_multiplier != 0) + { + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(abs(rideEntry->excitement_multiplier)); + StringId stringId = rideEntry->excitement_multiplier > 0 ? STR_EXCITEMENT_FACTOR : STR_EXCITEMENT_FACTOR_NEGATIVE; + DrawTextBasic(dpi, screenCoords, stringId, ft); + } + + // Intensity Factor + if (rideEntry->intensity_multiplier != 0) + { + int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + if (lineHeight != 10) + screenCoords.x += 150; + else + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(abs(rideEntry->intensity_multiplier)); + StringId stringId = rideEntry->intensity_multiplier > 0 ? STR_INTENSITY_FACTOR : STR_INTENSITY_FACTOR_NEGATIVE; + DrawTextBasic(dpi, screenCoords, stringId, ft); + + if (lineHeight != 10) + screenCoords.x -= 150; + } + + // Nausea Factor + if (rideEntry->nausea_multiplier != 0) + { + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(abs(rideEntry->nausea_multiplier)); + StringId stringId = rideEntry->nausea_multiplier > 0 ? STR_NAUSEA_FACTOR : STR_NAUSEA_FACTOR_NEGATIVE; + DrawTextBasic(dpi, screenCoords, stringId, ft); + } + } + + struct VehicleDrawInfo + { + int16_t x; + int16_t y; + ImageId imageId; + }; + + void VehicleScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + const auto* rideEntry = ride->GetRideEntry(); + + // Background + GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } }, PALETTE_INDEX_12); + + Widget* widget = &window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_PREVIEW]; + int32_t startX = std::max(2, (widget->width() - ((ride->NumTrains - 1) * 36)) / 2 - 25); + int32_t startY = widget->height() - 4; + + bool isReversed = ride->HasLifecycleFlag(RIDE_LIFECYCLE_REVERSED_TRAINS); + int32_t carIndex = (isReversed) ? ride->num_cars_per_train - 1 : 0; + + const auto& firstCarEntry = rideEntry->Cars[RideEntryGetVehicleAtPosition( + ride->subtype, ride->num_cars_per_train, carIndex)]; + startY += firstCarEntry.tab_height; + + // For each train + for (int32_t i = 0; i < ride->NumTrains; i++) + { + VehicleDrawInfo trainCarImages[OpenRCT2::Limits::MaxCarsPerTrain]; + VehicleDrawInfo* nextSpriteToDraw = trainCarImages; + int32_t x = startX; + int32_t y = startY; + + // For each car in train + static_assert(std::numeric_limitsnum_cars_per_train)>::max() <= std::size(trainCarImages)); + for (int32_t j = 0; j < ride->num_cars_per_train; j++) + { + carIndex = (isReversed) ? (ride->num_cars_per_train - 1) - j : j; + + const auto& carEntry = rideEntry->Cars[RideEntryGetVehicleAtPosition( + ride->subtype, ride->num_cars_per_train, carIndex)]; + x += carEntry.spacing / 17432; + y -= (carEntry.spacing / 2) / 17432; + + // Get colour of vehicle + int32_t vehicleColourIndex = 0; + switch (ride->colour_scheme_type & 3) + { + case VEHICLE_COLOUR_SCHEME_SAME: + vehicleColourIndex = 0; + break; + case VEHICLE_COLOUR_SCHEME_PER_TRAIN: + vehicleColourIndex = i; + break; + case VEHICLE_COLOUR_SCHEME_PER_VEHICLE: + vehicleColourIndex = carIndex; + break; + } + VehicleColour vehicleColour = RideGetVehicleColour(*ride, vehicleColourIndex); + + ImageIndex imageIndex = carEntry.SpriteByYaw( + OpenRCT2::Entity::Yaw::BaseRotation / 2, SpriteGroupType::SlopeFlat); + if (isReversed) + { + auto baseRotation = carEntry.NumRotationSprites(SpriteGroupType::SlopeFlat); + imageIndex = carEntry.SpriteByYaw( + (imageIndex + (baseRotation / 2)) & (baseRotation - 1), SpriteGroupType::SlopeFlat); + } + + imageIndex &= carEntry.TabRotationMask; + imageIndex *= carEntry.base_num_frames; + imageIndex += carEntry.base_image_id; + + auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary); + + nextSpriteToDraw->x = x; + nextSpriteToDraw->y = y; + nextSpriteToDraw->imageId = imageId; + nextSpriteToDraw++; + + x += carEntry.spacing / 17432; + y -= (carEntry.spacing / 2) / 17432; + } + + if (ride->type == RIDE_TYPE_REVERSER_ROLLER_COASTER) + { + VehicleDrawInfo tmp = *(nextSpriteToDraw - 1); + *(nextSpriteToDraw - 1) = *(nextSpriteToDraw - 2); + *(nextSpriteToDraw - 2) = tmp; + } + + VehicleDrawInfo* current = nextSpriteToDraw; + while (--current >= trainCarImages) + GfxDrawSprite(dpi, current->imageId, { current->x, current->y }); + + startX += 36; + } + } + +#pragma endregion + +#pragma region Operating + + /** + * + * rct2: 0x006B11D5 + */ + void ModeTweakIncrease() + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; + uint8_t maxValue = operatingSettings.MaxValue; + uint8_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; + + if (gCheatsUnlockOperatingLimits) + { + maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; + } + + uint8_t increment = ride->mode == RideMode::Dodgems ? 10 : 1; + + SetOperatingSetting( + w->rideId, RideSetSetting::Operation, std::clamp(ride->operation_option + increment, minValue, maxValue)); + } + + /** + * + * rct2: 0x006B120A + */ + void ModeTweakDecrease() + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; + uint8_t maxValue = operatingSettings.MaxValue; + uint8_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; + if (gCheatsUnlockOperatingLimits) + { + maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; + } + + uint8_t decrement = ride->mode == RideMode::Dodgems ? 10 : 1; + + SetOperatingSetting( + w->rideId, RideSetSetting::Operation, std::clamp(ride->operation_option - decrement, minValue, maxValue)); + } + + /** + * + * rct2: 0x006B1631 + */ + void ModeDropdown(Widget* widget) + { + Widget* dropdownWidget; + + dropdownWidget = widget - 1; + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto availableModes = ride->GetAvailableModes(); + + // Create dropdown list + auto numAvailableModes = 0; + auto checkedIndex = -1; + for (auto i = 0; i < static_cast(RideMode::Count); i++) + { + if (availableModes & (1uLL << i)) + { + gDropdownItems[numAvailableModes].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numAvailableModes].Args = RideModeNames[i]; + + if (ride->mode == static_cast(i)) + checkedIndex = numAvailableModes; + + numAvailableModes++; + } + } + + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + w->colours[1], 0, Dropdown::Flag::StayOpen, numAvailableModes, widget->right - dropdownWidget->left); + + if (checkedIndex != -1) + { + Dropdown::SetChecked(checkedIndex, true); + } + } + + /** + * + * rct2: 0x006B15C0 + */ + void LoadDropdown(Widget* widget) + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto dropdownWidget = widget - 1; + for (auto i = 0; i < 5; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = VehicleLoadNames[i]; + } + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + w->colours[1], 0, Dropdown::Flag::StayOpen, 5, widget->right - dropdownWidget->left); + + Dropdown::SetChecked(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK, true); + } + + /** + * + * rct2: 0x006B10A7 + */ + void OperatingMouseup(WidgetIndex widgetIndex) + { + const auto rideId = w->rideId; + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + case WIDX_LOAD_CHECKBOX: + SetOperatingSetting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_LOAD); + break; + case WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX: + SetOperatingSetting( + rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES); + break; + case WIDX_MINIMUM_LENGTH_CHECKBOX: + SetOperatingSetting( + rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH); + break; + case WIDX_MAXIMUM_LENGTH_CHECKBOX: + SetOperatingSetting( + rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH); + break; + case WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX: + SetOperatingSetting( + rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS); + break; + } + } + + /** + * + * rct2: 0x006B1715 + */ + void OperatingResize() + { + WindowSetResize(*w, 316, 186, 316, 186); + } + + /** + * + * rct2: 0x006B10F4 + */ + void OperatingMousedown(WidgetIndex widgetIndex, Widget* widget) + { + const auto rideId = w->rideId; + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + uint8_t upper_bound, lower_bound; + switch (widgetIndex) + { + case WIDX_MODE_TWEAK: + OperatingTweakTextInput(w, *ride); + break; + case WIDX_MODE_TWEAK_INCREASE: + ModeTweakIncrease(w); + break; + case WIDX_MODE_TWEAK_DECREASE: + ModeTweakDecrease(w); + break; + case WIDX_LIFT_HILL_SPEED_INCREASE: + upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : ride->GetRideTypeDescriptor().LiftData.maximum_speed; + lower_bound = gCheatsUnlockOperatingLimits ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed; + SetOperatingSetting( + rideId, RideSetSetting::LiftHillSpeed, + std::clamp(ride->lift_hill_speed + 1, lower_bound, upper_bound)); + break; + case WIDX_LIFT_HILL_SPEED_DECREASE: + upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : ride->GetRideTypeDescriptor().LiftData.maximum_speed; + lower_bound = gCheatsUnlockOperatingLimits ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed; + SetOperatingSetting( + rideId, RideSetSetting::LiftHillSpeed, + std::clamp(ride->lift_hill_speed - 1, lower_bound, upper_bound)); + break; + case WIDX_MINIMUM_LENGTH: + OperatingLengthWindow(w, WIDX_MINIMUM_LENGTH); + break; + case WIDX_MAXIMUM_LENGTH: + OperatingLengthWindow(w, WIDX_MAXIMUM_LENGTH); + break; + case WIDX_MINIMUM_LENGTH_INCREASE: + upper_bound = OpenRCT2::Limits::MaxWaitingTime; + lower_bound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MinWaitingTime, + std::clamp(ride->min_waiting_time + 1, lower_bound, upper_bound)); + break; + case WIDX_MINIMUM_LENGTH_DECREASE: + upper_bound = OpenRCT2::Limits::MaxWaitingTime; + lower_bound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MinWaitingTime, + std::clamp(ride->min_waiting_time - 1, lower_bound, upper_bound)); + break; + case WIDX_MAXIMUM_LENGTH_INCREASE: + upper_bound = OpenRCT2::Limits::MaxWaitingTime; + lower_bound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MaxWaitingTime, + std::clamp(ride->max_waiting_time + 1, lower_bound, upper_bound)); + break; + case WIDX_MAXIMUM_LENGTH_DECREASE: + upper_bound = OpenRCT2::Limits::MaxWaitingTime; + lower_bound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MaxWaitingTime, + std::clamp(ride->max_waiting_time - 1, lower_bound, upper_bound)); + break; + case WIDX_MODE_DROPDOWN: + ModeDropdown(w, widget); + break; + case WIDX_LOAD_DROPDOWN: + LoadDropdown(w, widget); + break; + case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE: + upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : OpenRCT2::Limits::MaxCircuitsPerRide; + lower_bound = 1; + SetOperatingSetting( + rideId, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits + 1, lower_bound, upper_bound)); + break; + case WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE: + upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : OpenRCT2::Limits::MaxCircuitsPerRide; + lower_bound = 1; + SetOperatingSetting( + rideId, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits - 1, lower_bound, upper_bound)); + break; + } + } + + void OperatingLengthWindow(WidgetIndex widgetIndex) + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + uint8_t upperBound = OpenRCT2::Limits::MaxWaitingTime; + uint8_t lowerBound = 0; + Formatter ft; + ft.Add(lowerBound); + ft.Add(upperBound); + auto title = (widgetIndex == WIDX_MINIMUM_LENGTH) ? STR_MINIMUM_WAITING_TIME : STR_MAXIMUM_WAITING_TIME; + auto currentValue = (widgetIndex == WIDX_MINIMUM_LENGTH) ? ride->min_waiting_time : ride->max_waiting_time; + char buffer[5]{}; + snprintf(buffer, std::size(buffer), "%u", currentValue); + WindowTextInputRawOpen(w, widgetIndex, title, STR_ENTER_VALUE, ft, buffer, 4); + } + + void OperatingTweakTextInput(const Ride& ride) + { + switch (ride.mode) + { + case RideMode::PoweredLaunchPasstrough: + case RideMode::PoweredLaunch: + case RideMode::UpwardLaunch: + case RideMode::PoweredLaunchBlockSectioned: + case RideMode::StationToStation: + case RideMode::Dodgems: + return; + default: + break; + } + + const auto& operatingSettings = ride.GetRideTypeDescriptor().OperatingSettings; + int16_t maxValue = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : operatingSettings.MaxValue; + int16_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; + + const auto& title = window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].text; + Formatter ft; + ft.Add(minValue * operatingSettings.OperatingSettingMultiplier); + ft.Add(maxValue * operatingSettings.OperatingSettingMultiplier); + + uint16_t currentValue = static_cast(ride.operation_option) * operatingSettings.OperatingSettingMultiplier; + char buffer[6]{}; + snprintf(buffer, std::size(buffer), "%u", currentValue); + + WindowTextInputRawOpen(w, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4); + } + + /** + * + * rct2: 0x006B1165 + */ + void OperatingDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + return; + + const auto rideId = w->rideId; + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + switch (widgetIndex) + { + case WIDX_MODE_DROPDOWN: + { + RideMode rideMode = RideMode::NullMode; + auto availableModes = ride->GetAvailableModes(); + auto modeInDropdownIndex = -1; + for (RideMode rideModeIndex = RideMode::Normal; rideModeIndex < RideMode::Count; rideModeIndex++) + { + if (availableModes & EnumToFlag(rideModeIndex)) + { + modeInDropdownIndex++; + if (modeInDropdownIndex == dropdownIndex) + { + rideMode = rideModeIndex; + break; + } + } + } + if (rideMode != RideMode::NullMode) + SetOperatingSetting(rideId, RideSetSetting::Mode, static_cast(rideMode)); + break; + } + case WIDX_LOAD_DROPDOWN: + SetOperatingSetting( + rideId, RideSetSetting::Departure, (ride->depart_flags & ~RIDE_DEPART_WAIT_FOR_LOAD_MASK) | dropdownIndex); + break; + } + } + + /** + * + * rct2: 0x006B178E + */ + void OperatingUpdate() + { + w->frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_3); + + auto ride = GetRide(w->rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) + { + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING; + w->Invalidate(); + } + } + + void OperatingTextinput(WidgetIndex widgetIndex, const char* text) + { + if (text == nullptr) + return; + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + if (widgetIndex == WIDX_MODE_TWEAK) + { + const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; + uint32_t maxValue = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : operatingSettings.MaxValue; + uint32_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; + auto multiplier = ride->GetRideTypeDescriptor().OperatingSettings.OperatingSettingMultiplier; + + try + { + uint32_t origSize = std::stol(std::string(text)) / multiplier; + uint8_t size = static_cast(std::clamp(origSize, minValue, maxValue)); + SetOperatingSetting(ride->id, RideSetSetting::Operation, size); + } + catch (const std::logic_error&) + { + // std::stol can throw std::out_of_range or std::invalid_argument + } + } + else if (widgetIndex == WIDX_MINIMUM_LENGTH || widgetIndex == WIDX_MAXIMUM_LENGTH) + { + try + { + auto rideSetSetting = widgetIndex == WIDX_MINIMUM_LENGTH ? RideSetSetting::MinWaitingTime + : RideSetSetting::MaxWaitingTime; + + uint16_t upperBound = OpenRCT2::Limits::MaxWaitingTime; + uint16_t lowerBound = 0; + uint16_t size = std::stol(std::string(text)); + size = std::clamp(size, lowerBound, upperBound); + SetOperatingSetting(ride->id, rideSetSetting, size); + } + catch (const std::logic_error&) + { + // std::stol can throw std::out_of_range or std::invalid_argument + } + } + } + + /** + * + * rct2: 0x006B0B30 + */ + void OperatingOnPrepareDraw() + { + Widget* widgets; + StringId format, caption, tooltip; + + widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); + } + + SetPressedTab(w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + // Widget setup + w->pressed_widgets &= ~( + (1uLL << WIDX_LOAD_CHECKBOX) | (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX) + | (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX) | (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX) + | (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX)); + + // Sometimes, only one of the alternatives support lift hill pieces. Make sure to check both. + bool hasAlternativeType = ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE); + if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_LIFT_HILL) + || (hasAlternativeType + && GetRideTypeDescriptor(ride->GetRideTypeDescriptor().AlternateType).SupportsTrackPiece(TRACK_LIFT_HILL))) + { + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Label; + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Spinner; + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Button; + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Button; + ft.Rewind(); + ft.Increment(20); + ft.Add(ride->lift_hill_speed); + } + else + { + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Empty; + } + + // Number of circuits + if (ride->CanHaveMultipleCircuits()) + { + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Label; + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Spinner; + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Button; + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Button; + ft.Rewind(); + ft.Increment(22); + ft.Add(ride->num_circuits); + } + else + { + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Empty; + } + + // Leave if another vehicle arrives at station + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LEAVE_WHEN_ANOTHER_VEHICLE_ARRIVES_AT_STATION) + && ride->NumTrains > 1 && !ride->IsBlockSectioned()) + { + window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Checkbox; + window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].tooltip + = STR_LEAVE_IF_ANOTHER_VEHICLE_ARRIVES_TIP; + window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].text = ride->GetRideTypeDescriptor() + .NameConvention.vehicle + == RideComponentType::Boat + ? STR_LEAVE_IF_ANOTHER_BOAT_ARRIVES + : STR_LEAVE_IF_ANOTHER_TRAIN_ARRIVES; + } + else + { + window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Empty; + } + + // Synchronise with adjacent stations + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_CAN_SYNCHRONISE_ADJACENT_STATIONS)) + { + window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Checkbox; + window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].text + = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS; + window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].tooltip + = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP; + } + else + { + window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Empty; + } + + // Mode + window_ride_operating_widgets[WIDX_MODE].text = RideModeNames[static_cast(ride->mode)]; + + // Waiting + window_ride_operating_widgets[WIDX_LOAD].text = VehicleLoadNames[(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK)]; + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS)) + { + window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Checkbox; + window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::DropdownMenu; + window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Button; + + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Spinner; + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button; + + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Spinner; + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button; + + ft.Rewind(); + ft.Increment(10); + ft.Add(STR_FORMAT_SECONDS); + ft.Add(ride->min_waiting_time); + ft.Add(STR_FORMAT_SECONDS); + ft.Add(ride->max_waiting_time); + + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD) + w->pressed_widgets |= (1uLL << WIDX_LOAD_CHECKBOX); + } + else + { + window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Empty; + + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; + + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; + } + + if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES) + w->pressed_widgets |= (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); + if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) + w->pressed_widgets |= (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX); + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) + w->pressed_widgets |= (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX); + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH) + w->pressed_widgets |= (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX); + + // Mode specific functionality + auto multiplier = ride->GetRideTypeDescriptor().OperatingSettings.OperatingSettingMultiplier; + ft.Rewind(); + ft.Increment(18); + ft.Add(static_cast(ride->operation_option) * multiplier); + switch (ride->mode) + { + case RideMode::PoweredLaunchPasstrough: + case RideMode::PoweredLaunch: + case RideMode::UpwardLaunch: + case RideMode::PoweredLaunchBlockSectioned: + ft.Rewind(); + ft.Increment(18); + ft.Add((ride->launch_speed * 9) / 4); + format = STR_RIDE_MODE_SPEED_VALUE; + caption = STR_LAUNCH_SPEED; + tooltip = STR_LAUNCH_SPEED_TIP; + break; + case RideMode::StationToStation: + ft.Rewind(); + ft.Increment(18); + ft.Add((ride->speed * 9) / 4); + format = STR_RIDE_MODE_SPEED_VALUE; + caption = STR_SPEED; + tooltip = STR_SPEED_TIP; + break; + case RideMode::Race: + ft.Rewind(); + ft.Increment(18); + ft.Add(ride->NumLaps); + format = STR_NUMBER_OF_LAPS_VALUE; + caption = STR_NUMBER_OF_LAPS; + tooltip = STR_NUMBER_OF_LAPS_TIP; + break; + case RideMode::Dodgems: + format = STR_RIDE_MODE_TIME_LIMIT_VALUE; + caption = STR_TIME_LIMIT; + tooltip = STR_TIME_LIMIT_TIP; + break; + case RideMode::Swing: + format = STR_RIDE_MODE_NUMBER_OF_SWINGS_VALUE; + caption = STR_NUMBER_OF_SWINGS; + tooltip = STR_NUMBER_OF_SWINGS_TIP; + break; + case RideMode::Rotation: + case RideMode::ForwardRotation: + case RideMode::BackwardRotation: + format = STR_NUMBER_OF_ROTATIONS_VALUE; + caption = STR_NUMBER_OF_ROTATIONS; + tooltip = STR_NUMBER_OF_ROTATIONS_TIP; + break; + default: + format = STR_MAX_PEOPLE_ON_RIDE_VALUE; + caption = STR_MAX_PEOPLE_ON_RIDE; + tooltip = STR_MAX_PEOPLE_ON_RIDE_TIP; + if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) + format = 0; + break; + } + + if (format != 0) + { + window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Label; + window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].text = caption; + window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].tooltip = tooltip; + window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Spinner; + window_ride_operating_widgets[WIDX_MODE_TWEAK].text = format; + window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Button; + window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Button; + w->pressed_widgets &= ~(1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); + } + else + { + window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Empty; + window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Empty; + } + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + } + + /** + * + * rct2: 0x006B1001 + */ + void OperatingPaint(DrawPixelInfo& dpi) + { + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + // Horizontal rule between mode settings and depart settings + GfxFillRectInset( + dpi, + { w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 }, + w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } }, + w->colours[1], INSET_RECT_FLAG_BORDER_INSET); + + // Number of block sections + if (ride->IsBlockSectioned()) + { + auto ft = Formatter(); + ft.Add(ride->num_block_brakes + ride->num_stations); + DrawTextBasic( + dpi, w->windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 }, + STR_BLOCK_SECTIONS, ft, COLOUR_BLACK); + } + } + +#pragma endregion + +#pragma region Maintenance + + /** + * + * rct2: 0x006B1AE4 + */ + void LocateMechanic() + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + // First check if there is a mechanic assigned + Peep* mechanic = RideGetAssignedMechanic(*ride); + + // Otherwise find the closest mechanic + if (mechanic == nullptr) + mechanic = RideFindClosestMechanic(*ride, 1); + + if (mechanic == nullptr) + ContextShowError(STR_UNABLE_TO_LOCATE_MECHANIC, STR_NONE, {}); + else + { + auto intent = Intent(WindowClass::Peep); + intent.PutExtra(INTENT_EXTRA_PEEP, mechanic); + ContextOpenIntent(&intent); + } + } + + /** + * + * rct2: 0x006B7D08 + */ + void MaintenanceDrawBar( + WindowBase* w, DrawPixelInfo& dpi, const ScreenCoordsXY& coords, int32_t value, int32_t colour) + { + GfxFillRectInset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, w->colours[1], INSET_RECT_F_30); + if (colour & BAR_BLINK) + { + colour &= ~BAR_BLINK; + if (GameIsNotPaused() && (gCurrentRealTimeTicks & 8)) + return; + } + + value = ((186 * ((value * 2) & 0xFF)) >> 8) & 0xFF; + if (value > 2) + { + GfxFillRectInset(dpi, { coords + ScreenCoordsXY{ 2, 1 }, coords + ScreenCoordsXY{ value + 1, 7 } }, colour, 0); + } + } + + /** + * + * rct2: 0x006B1AAD + */ + void MaintenanceMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + case WIDX_LOCATE_MECHANIC: + LocateMechanic(w); + break; + case WIDX_REFURBISH_RIDE: + ContextOpenDetailWindow(WD_REFURBISH_RIDE, w->number); + break; + } + } + + /** + * + * rct2: 0x006B1D70 + */ + void MaintenanceResize() + { + WindowSetResize(*w, 316, 135, 316, 135); + } + + /** + * + * rct2: 0x006B1ACE + */ + void MaintenanceMousedown(WidgetIndex widgetIndex, Widget* widget) + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + Widget* dropdownWidget = widget; + int32_t j, num_items; + + switch (widgetIndex) + { + case WIDX_INSPECTION_INTERVAL_DROPDOWN: + dropdownWidget--; + for (int32_t i = 0; i < 7; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = RideInspectionIntervalNames[i]; + } + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, 7, + widget->right - dropdownWidget->left); + + Dropdown::SetChecked(ride->inspection_interval, true); + break; + + case WIDX_FORCE_BREAKDOWN: + num_items = 1; + for (j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++) + { + if (rideEntry->ride_type[j] != RIDE_TYPE_NULL) + break; + } + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_DEBUG_FIX_RIDE; + for (int32_t i = 0; i < 8; i++) + { + assert(j < static_cast(std::size(rideEntry->ride_type))); + if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast(1 << i)) + { + if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned()) + { + if (ride->NumTrains != 1) + continue; + } + gDropdownItems[num_items].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[num_items].Args = RideBreakdownReasonNames[i]; + num_items++; + } + } + if (num_items == 1) + { + ContextShowError(STR_DEBUG_NO_BREAKDOWNS_AVAILABLE, STR_NONE, {}); + } + else + { + WindowDropdownShowText( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, w->colours[1], Dropdown::Flag::StayOpen, num_items); + + num_items = 1; + int32_t breakdownReason = ride->breakdown_reason_pending; + if (breakdownReason != BREAKDOWN_NONE && (ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING)) + { + for (int32_t i = 0; i < 8; i++) + { + if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns + & static_cast(1 << i)) + { + if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned()) + { + if (ride->NumTrains != 1) + continue; + } + if (i == breakdownReason) + { + Dropdown::SetChecked(num_items, true); + break; + } + gDropdownItems[num_items].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[num_items].Args = RideBreakdownReasonNames[i]; + num_items++; + } + } + } + + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING) == 0) + { + Dropdown::SetDisabled(0, true); + } + } + break; + } + } + + /** + * + * rct2: 0x006B1AD9 + */ + void MaintenanceDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + return; auto ride = GetRide(w->rideId); if (ride == nullptr) @@ -954,208 +3563,3170 @@ static void WindowRideDrawTabVehicle(DrawPixelInfo& dpi, WindowBase* w) if (rideEntry == nullptr) return; - if (rideEntry->flags & RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF) + switch (widgetIndex) { - clipDPI.zoom_level = ZoomLevel{ 1 }; - clipDPI.width *= 2; - clipDPI.height *= 2; - screenCoords.x *= 2; - screenCoords.y *= 2; - clipDPI.x *= 2; - clipDPI.y *= 2; + case WIDX_INSPECTION_INTERVAL_DROPDOWN: + SetOperatingSetting(w->rideId, RideSetSetting::InspectionInterval, dropdownIndex); + break; + + case WIDX_FORCE_BREAKDOWN: + if (dropdownIndex == 0) + { + Vehicle* vehicle; + switch (ride->breakdown_reason_pending) + { + case BREAKDOWN_SAFETY_CUT_OUT: + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + break; + for (int32_t i = 0; i < ride->NumTrains; ++i) + { + for (vehicle = GetEntity(ride->vehicles[i]); vehicle != nullptr; + vehicle = GetEntity(vehicle->next_vehicle_on_train)) + { + vehicle->ClearFlag( + VehicleFlags::CarIsBroken | VehicleFlags::StoppedOnLift | VehicleFlags::TrainIsBroken); + } + } + break; + case BREAKDOWN_RESTRAINTS_STUCK_CLOSED: + case BREAKDOWN_RESTRAINTS_STUCK_OPEN: + case BREAKDOWN_DOORS_STUCK_CLOSED: + case BREAKDOWN_DOORS_STUCK_OPEN: + vehicle = GetEntity(ride->vehicles[ride->broken_vehicle]); + if (vehicle != nullptr) + { + vehicle->ClearFlag(VehicleFlags::CarIsBroken); + } + break; + case BREAKDOWN_VEHICLE_MALFUNCTION: + vehicle = GetEntity(ride->vehicles[ride->broken_vehicle]); + if (vehicle != nullptr) + { + vehicle->ClearFlag(VehicleFlags::TrainIsBroken); + } + break; + } + ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN); + WindowInvalidateByNumber(WindowClass::Ride, w->number); + break; + } + if (ride->lifecycle_flags + & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + { + ContextShowError(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_ALREADY_BROKEN, {}); + } + else if (ride->status == RideStatus::Closed) + { + ContextShowError(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_IS_CLOSED, {}); + } + else + { + int32_t j; + for (j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++) + { + if (rideEntry->ride_type[j] != RIDE_TYPE_NULL) + break; + } + int32_t i; + int32_t num_items = 1; + for (i = 0; i < BREAKDOWN_COUNT; i++) + { + assert(j < static_cast(std::size(rideEntry->ride_type))); + if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast(1 << i)) + { + if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned()) + { + if (ride->NumTrains != 1) + continue; + } + if (num_items == dropdownIndex) + break; + num_items++; + } + } + RidePrepareBreakdown(*ride, i); + } + break; + } + } + + /** + * + * rct2: 0x006B1D37 + */ + void MaintenanceUpdate() + { + w->frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_4); + + auto ride = GetRide(w->rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE) + { + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE; + w->Invalidate(); + } + } + + /** + * + * rct2: 0x006B17C8 + */ + void MaintenanceOnPrepareDraw() + { + auto widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); } - // For any suspended rides, move image higher in the vehicle tab on the rides window - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SUSPENDED)) + SetPressedTab(w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval]; + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + + if (gConfigGeneral.DebuggingTools && NetworkGetMode() == NETWORK_MODE_NONE) { - screenCoords.y /= 4; + window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::FlatBtn; + } + else + { + window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::Empty; } - const auto vehicle = RideEntryGetVehicleAtPosition(ride->subtype, ride->num_cars_per_train, rideEntry->TabCar); - const auto& carEntry = rideEntry->Cars[vehicle]; + if (ride->GetRideTypeDescriptor().AvailableBreakdowns == 0 + || !(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) + { + w->disabled_widgets |= (1uLL << WIDX_REFURBISH_RIDE); + window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_CANT_REFURBISH_NOT_NEEDED; + } + else + { + w->disabled_widgets &= ~(1uLL << WIDX_REFURBISH_RIDE); + window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_REFURBISH_RIDE_TIP; + } + } - auto vehicleId = ((ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_VEHICLE) ? rideEntry->TabCar : 0; - VehicleColour vehicleColour = RideGetVehicleColour(*ride, vehicleId); + /** + * + * rct2: 0x006B1877 + */ + void MaintenancePaint(DrawPixelInfo& dpi) + { + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); - // imageIndex represents a precision of 64 - auto imageIndex = OpenRCT2::Entity::Yaw::YawFrom4(2) * 2; - if (w->page == WINDOW_RIDE_PAGE_VEHICLE) - imageIndex += w->frame_no; - imageIndex = carEntry.SpriteByYaw(imageIndex / 2, SpriteGroupType::SlopeFlat); + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + // Locate mechanic button image + Widget* widget = &window_ride_maintenance_widgets[WIDX_LOCATE_MECHANIC]; + auto screenCoords = w->windowPos + ScreenCoordsXY{ widget->left, widget->top }; + auto image = ImageId(SPR_MECHANIC, COLOUR_BLACK, gStaffMechanicColour); + GfxDrawSprite(dpi, image, screenCoords); + + // Inspection label + widget = &window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL]; + screenCoords = w->windowPos + ScreenCoordsXY{ 4, widget->top + 1 }; + DrawTextBasic(dpi, screenCoords, STR_INSPECTION); + + // Reliability + widget = &window_ride_maintenance_widgets[WIDX_PAGE_BACKGROUND]; + screenCoords = w->windowPos + ScreenCoordsXY{ widget->left + 4, widget->top + 4 }; + + uint16_t reliability = ride->reliability_percentage; + auto ft = Formatter(); + ft.Add(reliability); + DrawTextBasic(dpi, screenCoords, STR_RELIABILITY_LABEL_1757, ft); + MaintenanceDrawBar( + w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, std::max(10, reliability), COLOUR_BRIGHT_GREEN); + screenCoords.y += 11; + + uint16_t downTime = ride->downtime; + ft = Formatter(); + ft.Add(downTime); + DrawTextBasic(dpi, screenCoords, STR_DOWN_TIME_LABEL_1889, ft); + MaintenanceDrawBar(w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, downTime, COLOUR_BRIGHT_RED); + screenCoords.y += 26; + + // Last inspection + StringId stringId; + if (ride->last_inspection <= 1) + stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTE; + else if (ride->last_inspection <= 240) + stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTES; + else + stringId = STR_TIME_SINCE_LAST_INSPECTION_MORE_THAN_4_HOURS; + + ft = Formatter(); + ft.Add(ride->last_inspection); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += 12; + + // Last / current breakdown + if (ride->breakdown_reason == BREAKDOWN_NONE) + return; + + stringId = (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) ? STR_CURRENT_BREAKDOWN : STR_LAST_BREAKDOWN; + ft = Formatter(); + ft.Add(RideBreakdownReasonNames[ride->breakdown_reason]); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += 12; + + // Mechanic status + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + { + switch (ride->mechanic_status) + { + case RIDE_MECHANIC_STATUS_CALLING: + { + stringId = STR_NO_MECHANICS_ARE_HIRED_MESSAGE; + + for (auto peep : EntityList()) + { + if (peep->IsMechanic()) + { + stringId = STR_CALLING_MECHANIC; + break; + } + } + break; + } + case RIDE_MECHANIC_STATUS_HEADING: + stringId = STR_MEHCANIC_IS_HEADING_FOR_THE_RIDE; + break; + case RIDE_MECHANIC_STATUS_FIXING: + case RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES: + stringId = STR_MEHCANIC_IS_FIXING_THE_RIDE; + break; + default: + stringId = STR_EMPTY; + break; + } + + if (stringId != STR_EMPTY) + { + if (stringId == STR_CALLING_MECHANIC || stringId == STR_NO_MECHANICS_ARE_HIRED_MESSAGE) + { + DrawTextWrapped(dpi, screenCoords, 280, stringId, {}, { TextAlignment::LEFT }); + } + else + { + auto staff = GetEntity(ride->mechanic); + if (staff != nullptr && staff->IsMechanic()) + { + ft = Formatter(); + staff->FormatNameTo(ft); + DrawTextWrapped(dpi, screenCoords, 280, stringId, ft, { TextAlignment::LEFT }); + } + } + } + } + } + +#pragma endregion + +#pragma region Colour + + int32_t HasTrackColour(const Ride& ride, int32_t trackColour) + { + // Get station flags (shops don't have them) + auto stationObjFlags = 0; + if (!ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) + { + auto stationObj = ride.GetStationObject(); + if (stationObj != nullptr) + { + stationObjFlags = stationObj->Flags; + } + } + + switch (trackColour) + { + case 0: + return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR) + || ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN); + case 1: + return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR) + || ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL); + case 2: + return ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS); + default: + return 0; + } + } + + void SetTrackColourScheme(const ScreenCoordsXY& screenPos) + { + auto newColourScheme = static_cast(w->ride_colour); + auto info = GetMapCoordinatesFromPos(screenPos, EnumsToFlags(ViewportInteractionItem::Ride)); + + if (info.SpriteType != ViewportInteractionItem::Ride) + return; + if (info.Element->GetType() != TileElementType::Track) + return; + if (info.Element->AsTrack()->GetRideIndex() != w->rideId) + return; + if (info.Element->AsTrack()->GetColourScheme() == newColourScheme) + return; + + auto z = info.Element->GetBaseZ(); + auto direction = info.Element->GetDirection(); + auto gameAction = RideSetColourSchemeAction( + CoordsXYZD{ info.Loc, z, static_cast(direction) }, info.Element->AsTrack()->GetTrackType(), + newColourScheme); + GameActions::Execute(&gameAction); + } + + /** + * + * rct2: 0x006B04FA + */ + void ColourClose() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return; + + if (gCurrentToolWidget.window_classification != w->classification) + return; + + if (gCurrentToolWidget.window_number != w->number) + return; + + ToolCancel(); + } + + /** + * + * rct2: 0x006B02A1 + */ + void ColourMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + case WIDX_PAINT_INDIVIDUAL_AREA: + ToolSet(*w, WIDX_PAINT_INDIVIDUAL_AREA, Tool::PaintDown); + break; + case WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX: + auto ride = GetRide(w->rideId); + if (ride != nullptr) + { + const bool currentlyEnabled = ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS); + auto rideSetAppearanceAction = RideSetAppearanceAction( + w->rideId, RideSetAppearanceType::SellingItemColourIsRandom, currentlyEnabled ? 0 : 1, 0); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + } + } + + /** + * + * rct2: 0x006B0AB6 + */ + void ColourResize() + { + WindowSetResize(*w, 316, 207, 316, 207); + } + + /** + * + * rct2: 0x006B02C6 + */ + void ColourMousedown(WidgetIndex widgetIndex, Widget* widget) + { + VehicleColour vehicleColour; + int32_t i, numItems; + StringId stringId; + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto colourSchemeIndex = w->ride_colour; + auto dropdownWidget = widget - 1; + + switch (widgetIndex) + { + case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: + for (i = 0; i < OpenRCT2::Limits::NumColourSchemes; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = ColourSchemeNames[i]; + } + + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, 4, + widget->right - dropdownWidget->left); + + Dropdown::SetChecked(colourSchemeIndex, true); + break; + case WIDX_TRACK_MAIN_COLOUR: + WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].main); + break; + case WIDX_TRACK_ADDITIONAL_COLOUR: + WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].additional); + break; + case WIDX_TRACK_SUPPORT_COLOUR: + WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].supports); + break; + case WIDX_MAZE_STYLE_DROPDOWN: + for (i = 0; i < 4; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = MazeOptions[i].text; + } + + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, 4, + widget->right - dropdownWidget->left); + + Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true); + break; + case WIDX_ENTRANCE_STYLE_DROPDOWN: + { + auto ddIndex = 0; + auto& objManager = GetContext()->GetObjectManager(); + for (i = 0; i < MAX_STATION_OBJECTS; i++) + { + auto stationObj = static_cast(objManager.GetLoadedObject(ObjectType::Station, i)); + if (stationObj != nullptr) + { + gDropdownItems[ddIndex].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[ddIndex].Args = stationObj->NameStringId; + if (ride->entrance_style == i) + { + gDropdownItems[ddIndex].Format = STR_DROPDOWN_MENU_LABEL_SELECTED; + } + ddIndex++; + } + } + + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, + widget->right - dropdownWidget->left); + break; + } + case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: + for (i = 0; i < 3; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = (GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle) + .singular + << 16) + | VehicleColourSchemeNames[i]; + } + + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, + rideEntry->max_cars_in_train > 1 ? 3 : 2, widget->right - dropdownWidget->left); + + Dropdown::SetChecked(ride->colour_scheme_type & 3, true); + break; + case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: + numItems = ride->NumTrains; + if ((ride->colour_scheme_type & 3) != VEHICLE_COLOUR_SCHEME_PER_TRAIN) + numItems = ride->num_cars_per_train; + + stringId = (ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_TRAIN ? STR_RIDE_COLOUR_TRAIN_OPTION + : STR_RIDE_COLOUR_VEHICLE_OPTION; + for (i = 0; i < std::min(numItems, Dropdown::ItemsMaxSize); i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = (static_cast(i + 1) << 32) + | ((GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised) << 16) + | stringId; + } + + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, + widget->right - dropdownWidget->left); + + Dropdown::SetChecked(w->vehicleIndex, true); + break; + case WIDX_VEHICLE_BODY_COLOUR: + vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); + WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Body); + break; + case WIDX_VEHICLE_TRIM_COLOUR: + vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); + WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Trim); + break; + case WIDX_VEHICLE_TERNARY_COLOUR: + vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); + WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Tertiary); + break; + } + } + + /** + * + * rct2: 0x006B0331 + */ + void ColourDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + return; + + const auto rideId = w->rideId; + switch (widgetIndex) + { + case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: + w->ride_colour = static_cast(dropdownIndex); + w->Invalidate(); + break; + case WIDX_TRACK_MAIN_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::TrackColourMain, ColourDropDownIndexToColour(dropdownIndex), w->ride_colour); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_TRACK_ADDITIONAL_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::TrackColourAdditional, ColourDropDownIndexToColour(dropdownIndex), + w->ride_colour); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_TRACK_SUPPORT_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::TrackColourSupports, ColourDropDownIndexToColour(dropdownIndex), + w->ride_colour); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_MAZE_STYLE_DROPDOWN: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::MazeStyle, dropdownIndex, w->ride_colour); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_ENTRANCE_STYLE_DROPDOWN: + { + auto ddIndex = 0; + auto& objManager = GetContext()->GetObjectManager(); + for (auto i = 0; i < MAX_STATION_OBJECTS; i++) + { + auto stationObj = static_cast(objManager.GetLoadedObject(ObjectType::Station, i)); + if (stationObj != nullptr) + { + if (ddIndex == dropdownIndex) + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::EntranceStyle, ddIndex, 0); + rideSetAppearanceAction.SetCallback([ddIndex](const GameAction*, const GameActions::Result* res) { + if (res->Error != GameActions::Status::Ok) + return; + gLastEntranceStyle = ddIndex; + }); + GameActions::Execute(&rideSetAppearanceAction); + break; + } + ddIndex++; + } + } + break; + } + case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourScheme, dropdownIndex, 0); + GameActions::Execute(&rideSetAppearanceAction); + w->vehicleIndex = 0; + } + break; + case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: + w->vehicleIndex = dropdownIndex; + w->Invalidate(); + break; + case WIDX_VEHICLE_BODY_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourBody, ColourDropDownIndexToColour(dropdownIndex), + w->vehicleIndex); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_VEHICLE_TRIM_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourTrim, ColourDropDownIndexToColour(dropdownIndex), + w->vehicleIndex); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_VEHICLE_TERNARY_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourTernary, ColourDropDownIndexToColour(dropdownIndex), + w->vehicleIndex); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + } + } + + /** + * + * rct2: 0x006B0A8F + */ + void ColourUpdate() + { + w->frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_5); + WidgetInvalidate(*w, WIDX_VEHICLE_PREVIEW); + } + + /** + * + * rct2: 0x006B04EC + */ + void ColourTooldown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) + SetTrackColourScheme(w, screenCoords); + } + + /** + * + * rct2: 0x006B04F3 + */ + void ColourTooldrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) + SetTrackColourScheme(w, screenCoords); + } + + /** + * + * rct2: 0x006AFB36 + */ + void ColourOnPrepareDraw() + { + TrackColour trackColour; + VehicleColour vehicleColour; + + auto widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); + } + + SetPressedTab(w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + w->widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; + auto ft = Formatter::Common(); + ft.Increment(16); + ride->FormatNameTo(ft); + + // Track colours + int32_t colourScheme = w->ride_colour; + trackColour = ride->track_colour[colourScheme]; + + // Maze style + const auto& rtd = ride->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::DropdownMenu; + window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Button; + window_ride_colour_widgets[WIDX_MAZE_STYLE].text = MazeOptions[trackColour.supports].text; + } + else + { + window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Empty; + } + + // Track, multiple colour schemes + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR)) + { + window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; + window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; + window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::FlatBtn; + } + else + { + window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::Empty; + } + + // Track main colour + if (HasTrackColour(*ride, 0)) + { + window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::ColourBtn; + window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].image = GetColourButtonImage(trackColour.main); + } + else + { + window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::Empty; + } + + // Track additional colour + if (HasTrackColour(*ride, 1)) + { + window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::ColourBtn; + window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].image = GetColourButtonImage(trackColour.additional); + } + else + { + window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::Empty; + } + + // Selling item random colour checkbox + if (ride->HasRecolourableShopItems()) + { + window_ride_colour_widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Checkbox; + if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS)) + { + w->pressed_widgets |= (1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); + } + else + { + w->pressed_widgets &= ~(1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); + } + } + else + { + window_ride_colour_widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Empty; + } + + // Track supports colour + if (HasTrackColour(*ride, 2) && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::ColourBtn; + window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].image = GetColourButtonImage(trackColour.supports); + } + else + { + window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::Empty; + } + + // Track preview + if (ride->GetRideTypeDescriptor().HasFlag( + RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL + | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS)) + window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Spinner; + else + window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Empty; + + // Entrance style + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT)) + { + window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Spinner; + window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::DropdownMenu; + window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Button; + + auto stringId = STR_NONE; + auto stationObj = ride->GetStationObject(); + if (stationObj != nullptr) + { + stringId = stationObj->NameStringId; + } + window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].text = stringId; + } + else + { + window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Empty; + } + + // Vehicle colours + if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES) + && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS)) + { + int32_t vehicleColourSchemeType = ride->colour_scheme_type & 3; + if (vehicleColourSchemeType == 0) + w->vehicleIndex = 0; + + vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); + + window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Scroll; + window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::ColourBtn; + window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOUR].image = GetColourButtonImage(vehicleColour.Body); + + bool allowChangingTrimColour = false; + bool allowChangingTernaryColour = false; + + for (int32_t i = 0; i < ride->num_cars_per_train; i++) + { + uint8_t vehicleTypeIndex = RideEntryGetVehicleAtPosition(ride->subtype, ride->num_cars_per_train, i); + + if (rideEntry->Cars[vehicleTypeIndex].flags & CAR_ENTRY_FLAG_ENABLE_TRIM_COLOUR) + { + allowChangingTrimColour = true; + } + if (rideEntry->Cars[vehicleTypeIndex].flags & CAR_ENTRY_FLAG_ENABLE_TERNARY_COLOUR) + { + allowChangingTernaryColour = true; + } + } + + // Additional colours + if (allowChangingTrimColour) + { + window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::ColourBtn; + window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].image = GetColourButtonImage(vehicleColour.Trim); + if (allowChangingTernaryColour) + { + window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::ColourBtn; + window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].image = GetColourButtonImage( + vehicleColour.Tertiary); + } + else + { + window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; + } + } + else + { + window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; + } + + // Vehicle colour scheme type + if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_VEHICLE_IS_INTEGRAL) + && (ride->num_cars_per_train | ride->NumTrains) > 1) + { + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; + } + else + { + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; + } + ft.Rewind(); + ft.Increment(6); + ft.Add(VehicleColourSchemeNames[vehicleColourSchemeType]); + ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular); + ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised); + ft.Add(w->vehicleIndex + 1); + + // Vehicle index + if (vehicleColourSchemeType != 0) + { + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::DropdownMenu; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Button; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].text = vehicleColourSchemeType == 1 + ? STR_RIDE_COLOUR_TRAIN_VALUE + : STR_RIDE_COLOUR_VEHICLE_VALUE; + } + else + { + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; + } + } + else + { + window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty; + window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; + } + + ft.Rewind(); + ft.Increment(14); + ft.Add(ColourSchemeNames[colourScheme]); + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + } + + /** + * + * rct2: 0x006AFF3E + */ + void ColourPaint(DrawPixelInfo& dpi) + { + // TODO: This should use lists and identified sprites + DrawPixelInfo clippedDpi; + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); + + // Track / shop item preview + const auto& trackPreviewWidget = window_ride_colour_widgets[WIDX_TRACK_PREVIEW]; + if (trackPreviewWidget.type != WindowWidgetType::Empty) + GfxFillRect( + dpi, + { { w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left + 1, trackPreviewWidget.top + 1 } }, + { w->windowPos + ScreenCoordsXY{ trackPreviewWidget.right - 1, trackPreviewWidget.bottom - 1 } } }, + PALETTE_INDEX_12); + + auto trackColour = ride->track_colour[w->ride_colour]; + + // + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr || rideEntry->shop_item[0] == ShopItem::None) + { + auto screenCoords = w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left, trackPreviewWidget.top }; + + // Track + const auto& rtd = ride->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + GfxDrawSprite(dpi, ImageId(MazeOptions[trackColour.supports].sprite), screenCoords); + } + else + { + auto typeDescriptor = ride->GetRideTypeDescriptor(); + int32_t spriteIndex = typeDescriptor.ColourPreview.Track; + if (spriteIndex != 0) + { + GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.main, trackColour.additional), screenCoords); + } + + // Supports + spriteIndex = typeDescriptor.ColourPreview.Supports; + if (spriteIndex != 0) + { + GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.supports), screenCoords); + } + } + } + else + { + auto screenCoords = w->windowPos + + ScreenCoordsXY{ (trackPreviewWidget.left + trackPreviewWidget.right) / 2 - 8, + (trackPreviewWidget.bottom + trackPreviewWidget.top) / 2 - 6 }; + + ShopItem shopItem = rideEntry->shop_item[1] == ShopItem::None ? rideEntry->shop_item[0] : rideEntry->shop_item[1]; + if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS)) + { + colour_t spriteColour = COLOUR_BLACK; + // Limit update rate of preview to avoid making people dizzy. + if ((gCurrentTicks % 64) == 0) + { + spriteColour++; + if (spriteColour >= COLOUR_NUM_NORMAL) + { + spriteColour = COLOUR_BLACK; + } + } + + GfxDrawSprite(dpi, ImageId(GetShopItemDescriptor(shopItem).Image, spriteColour), screenCoords); + } + else + { + GfxDrawSprite(dpi, ImageId(GetShopItemDescriptor(shopItem).Image, ride->track_colour[0].main), screenCoords); + } + } + + // Entrance preview + trackColour = ride->track_colour[0]; + const auto& entrancePreviewWidget = w->widgets[WIDX_ENTRANCE_PREVIEW]; + if (entrancePreviewWidget.type != WindowWidgetType::Empty) + { + if (ClipDrawPixelInfo( + clippedDpi, dpi, + w->windowPos + ScreenCoordsXY{ entrancePreviewWidget.left + 1, entrancePreviewWidget.top + 1 }, + entrancePreviewWidget.width(), entrancePreviewWidget.height())) + { + GfxClear(&clippedDpi, PALETTE_INDEX_12); + + auto stationObj = ride->GetStationObject(); + if (stationObj != nullptr && stationObj->BaseImageId != ImageIndexUndefined) + { + auto imageId = ImageId(stationObj->BaseImageId, trackColour.main, trackColour.additional); + + // Back + GfxDrawSprite(clippedDpi, imageId, { 34, 20 }); + + // Front + GfxDrawSprite(clippedDpi, imageId.WithIndexOffset(4), { 34, 20 }); + + // Glass + if (stationObj->Flags & STATION_OBJECT_FLAGS::IS_TRANSPARENT) + { + auto glassImageId = ImageId(stationObj->BaseImageId + 20).WithTransparency(trackColour.main); + GfxDrawSprite(clippedDpi, glassImageId, { 34, 20 }); + } + } + } + + DrawTextEllipsised(dpi, { w->windowPos.x + 3, w->windowPos.y + 103 }, 97, STR_STATION_STYLE, {}); + } + } + + /** + * + * rct2: 0x006B0192 + */ + void ColourScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto vehiclePreviewWidget = &window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW]; + auto vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); + + // Background colour + GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, PALETTE_INDEX_12); + + // ? + auto screenCoords = ScreenCoordsXY{ vehiclePreviewWidget->width() / 2, vehiclePreviewWidget->height() - 15 }; + + // ? + auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR ? w->vehicleIndex + : rideEntry->TabCar; + + const auto& carEntry = rideEntry->Cars[RideEntryGetVehicleAtPosition( + ride->subtype, ride->num_cars_per_train, trainCarIndex)]; + + screenCoords.y += carEntry.tab_height; + + // Draw the coloured spinning vehicle + // w->frame_no represents a SpritePrecision of 64 + ImageIndex imageIndex = carEntry.SpriteByYaw(w->frame_no / 2, SpriteGroupType::SlopeFlat); imageIndex &= carEntry.TabRotationMask; imageIndex *= carEntry.base_num_frames; imageIndex += carEntry.base_image_id; auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary); - GfxDrawSprite(clipDPI, imageId, screenCoords); + GfxDrawSprite(dpi, imageId, screenCoords); } -} -/** - * - * rct2: 0x006B2F42 - */ -static void WindowRideDrawTabCustomer(DrawPixelInfo& dpi, WindowBase* w) -{ - WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(WINDOW_RIDE_PAGE_CUSTOMER); +#pragma endregion - if (!WidgetIsDisabled(*w, widgetIndex)) +#pragma region Music + std::vector window_ride_current_music_style_order; + + /** + * + * rct2: 0x006B215D + */ + void ToggleMusic() { - const auto& widget = w->widgets[widgetIndex]; - int32_t spriteIndex = 0; - if (w->page == WINDOW_RIDE_PAGE_CUSTOMER) - spriteIndex = w->picked_peep_frame & ~3; - - spriteIndex += GetPeepAnimation(PeepSpriteType::Normal).base_image + 1; - - GfxDrawSprite( - dpi, ImageId(spriteIndex, COLOUR_BRIGHT_RED, COLOUR_TEAL), - w->windowPos + ScreenCoordsXY{ widget.midX(), widget.bottom - 6 }); + auto ride = GetRide(w->rideId); + if (ride != nullptr) + { + int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; + SetOperatingSetting(w->rideId, RideSetSetting::Music, activateMusic); + } } -} -/** - * - * rct2: 0x006B2B35 - */ -static void WindowRideDrawTabImages(DrawPixelInfo& dpi, WindowBase* w) -{ - WindowRideDrawTabVehicle(dpi, w); - WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_OPERATING, SPR_TAB_GEARS_0); - WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_MAINTENANCE, SPR_TAB_WRENCH_0); - WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_INCOME, SPR_TAB_ADMISSION_0); - WindowRideDrawTabMain(dpi, w); - WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_MEASUREMENTS, SPR_TAB_TIMER_0); - WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_COLOUR, SPR_TAB_PAINT_0); - WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_GRAPHS, SPR_TAB_GRAPH_A_0); - WindowRideDrawTabCustomer(dpi, w); - WindowRideDrawTabImage(dpi, w, WINDOW_RIDE_PAGE_MUSIC, SPR_TAB_MUSIC_0); -} - -/** - * - * rct2: 0x006AEB9F - */ -static void WindowRideDisableTabs(WindowBase* w) -{ - uint32_t disabled_tabs = 0; - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - const auto& rtd = ride->GetRideTypeDescriptor(); - - if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) - disabled_tabs |= (1uLL << WIDX_TAB_8); // 0x800 - - if (ride->type == RIDE_TYPE_MINI_GOLF) - disabled_tabs |= (1uLL << WIDX_TAB_2 | 1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4); // 0xE0 - - if (rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) - disabled_tabs |= (1uLL << WIDX_TAB_2); // 0x20 - - if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL) - && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS) && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS) - && !rtd.HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT)) + /** + * + * rct2: 0x006B1ED7 + */ + void MusicMouseup(WidgetIndex widgetIndex) { - disabled_tabs |= (1uLL << WIDX_TAB_5); // 0x100 + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + case WIDX_PLAY_MUSIC: + ToggleMusic(w); + break; + } } - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) - disabled_tabs |= (1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_7); // 0x4C0 - - if (!rtd.HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC)) + /** + * + * rct2: 0x006AF4A2 + */ + void MusicResize() { - disabled_tabs |= (1uLL << WIDX_TAB_6); // 0x200 + w->flags |= WF_RESIZABLE; + WindowSetResize(*w, 316, 81, 316, 81); } - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_CASH_MACHINE) || rtd.HasFlag(RIDE_TYPE_FLAG_IS_FIRST_AID) - || (gParkFlags & PARK_FLAGS_NO_MONEY) != 0) - disabled_tabs |= (1uLL << WIDX_TAB_9); // 0x1000 - - if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) != 0) - disabled_tabs |= (1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_6 | 1uLL << WIDX_TAB_9 | 1uLL << WIDX_TAB_10); // 0x3280 - - const auto* rideEntry = GetRideEntryByIndex(ride->subtype); - - if (rideEntry == nullptr) + std::string GetMusicString(ObjectEntryIndex musicObjectIndex) { - disabled_tabs |= 1uLL << WIDX_TAB_2 | 1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_5 | 1uLL << WIDX_TAB_6 - | 1uLL << WIDX_TAB_7 | 1uLL << WIDX_TAB_8 | 1uLL << WIDX_TAB_9 | 1uLL << WIDX_TAB_10; + auto& objManager = GetContext()->GetObjectManager(); + auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, musicObjectIndex)); + + auto name = musicObj->NameStringId; + return FormatStringID(name, {}); } - else if ((rideEntry->flags & RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB) != 0) + + /** + * + * rct2: 0x006B1EFC + */ + void MusicMousedown(WidgetIndex widgetIndex, Widget* widget) { - disabled_tabs |= (1uLL << WIDX_TAB_5); + if (widgetIndex != WIDX_MUSIC_DROPDOWN) + return; + + auto dropdownWidget = widget - 1; + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + // Construct list of available music + auto& musicOrder = window_ride_current_music_style_order; + musicOrder.clear(); + auto& objManager = GetContext()->GetObjectManager(); + for (ObjectEntryIndex i = 0; i < MAX_MUSIC_OBJECTS; i++) + { + auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, i)); + if (musicObj != nullptr) + { + // Hide custom music if the WAV file does not exist + auto originalStyleId = musicObj->GetOriginalStyleId(); + if (originalStyleId.has_value() + && (originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_1 || originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_2)) + { + auto numTracks = musicObj->GetTrackCount(); + if (numTracks != 0) + { + auto track0 = musicObj->GetTrack(0); + if (!track0->Asset.IsAvailable()) + { + continue; + } + } + else + { + continue; + } + } + + if (gCheatsUnlockOperatingLimits || musicObj->SupportsRideType(ride->type)) + { + musicOrder.push_back(i); + } + } + } + + // Sort available music by the alphabetical order + std::stable_sort(musicOrder.begin(), musicOrder.end(), [](const ObjectEntryIndex& a, const ObjectEntryIndex& b) { + return String::Compare(GetMusicString(b), GetMusicString(a), false) > 0; + }); + + // Setup dropdown list + auto numItems = musicOrder.size(); + for (size_t i = 0; i < numItems; i++) + { + auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, musicOrder[i])); + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = musicObj->NameStringId; + } + + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); + + // Set currently checked item + for (size_t i = 0; i < numItems; i++) + { + if (musicOrder[i] == ride->music) + { + Dropdown::SetChecked(static_cast(i), true); + } + } } - w->disabled_widgets = disabled_tabs; -} - -static void WindowRideUpdateOverallView(const Ride& ride) -{ - // Calculate x, y, z bounds of the entire ride using its track elements - TileElementIterator it; - - TileElementIteratorBegin(&it); - - CoordsXYZ min = { std::numeric_limits::max(), std::numeric_limits::max(), - std::numeric_limits::max() }; - CoordsXYZ max = { std::numeric_limits::min(), std::numeric_limits::min(), - std::numeric_limits::min() }; - - while (TileElementIteratorNext(&it)) + /** + * + * rct2: 0x006B1F03 + */ + void MusicDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { - if (it.element->GetType() != TileElementType::Track) - continue; - - if (it.element->AsTrack()->GetRideIndex() != ride.id) - continue; - - auto location = TileCoordsXY(it.x, it.y).ToCoordsXY(); - int32_t baseZ = it.element->GetBaseZ(); - int32_t clearZ = it.element->GetClearanceZ(); - - min.x = std::min(min.x, location.x); - min.y = std::min(min.y, location.y); - min.z = std::min(min.z, baseZ); - - max.x = std::max(max.x, location.x); - max.y = std::max(max.y, location.y); - max.z = std::max(max.z, clearZ); + if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0 + && static_cast(dropdownIndex) < window_ride_current_music_style_order.size()) + { + auto musicStyle = window_ride_current_music_style_order[dropdownIndex]; + SetOperatingSetting(w->rideId, RideSetSetting::MusicType, musicStyle); + } } - const auto rideIndex = ride.id.ToUnderlying(); - if (rideIndex >= ride_overall_views.size()) + /** + * + * rct2: 0x006B2198 + */ + void MusicUpdate() { - ride_overall_views.resize(rideIndex + 1); + w->frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_6); } - auto& view = ride_overall_views[rideIndex]; - view.loc = CoordsXYZ{ (min.x + max.x) / 2, (min.y + max.y) / 2, (min.z + max.z) / 2 } + CoordsXYZ{ 16, 16, -8 }; - - // Calculate size to determine from how far away to view the ride - const auto diff = max - min; - - const int32_t size = static_cast(std::sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z)); - - if (size >= 80) + /** + * + * rct2: 0x006B1DEA + */ + void MusicOnPrepareDraw() { - // Each farther zoom level shows twice as many tiles (log) - // Appropriate zoom is lowered by one to fill the entire view with the ride - const auto zoomValue = static_cast(std::ceil(std::log(size / 80)) - 1); - view.zoom = std::clamp(ZoomLevel{ zoomValue }, ZoomLevel{ 0 }, ZoomLevel::max()); + auto widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); + } + + SetPressedTab(w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + // Set selected music + StringId musicName = STR_NONE; + auto& objManager = GetContext()->GetObjectManager(); + auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, ride->music)); + if (musicObj != nullptr) + { + musicName = musicObj->NameStringId; + } + window_ride_music_widgets[WIDX_MUSIC].text = musicName; + + // Set music activated + auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0; + if (isMusicActivated) + { + w->pressed_widgets |= (1uLL << WIDX_PLAY_MUSIC); + w->disabled_widgets &= ~(1uLL << WIDX_MUSIC); + w->disabled_widgets &= ~(1uLL << WIDX_MUSIC_DROPDOWN); + } + else + { + w->pressed_widgets &= ~(1uLL << WIDX_PLAY_MUSIC); + w->disabled_widgets |= (1uLL << WIDX_MUSIC); + w->disabled_widgets |= (1uLL << WIDX_MUSIC_DROPDOWN); + } + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); } - else + + /** + * + * rct2: 0x006B1ECC + */ + void MusicPaint(DrawPixelInfo& dpi) { - // Small rides or stalls are zoomed in all the way. - view.zoom = ZoomLevel{ 0 }; + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); } -} + +#pragma endregion + +#pragma region Measurements + + StringId GetRatingName(ride_rating rating) + { + int32_t index = std::clamp(rating >> 8, 0, static_cast(std::size(RatingNames)) - 1); + return RatingNames[index]; + } + + /** + * + * rct2: 0x006D2804 + when al == 0*/ + void CancelScenerySelection() + { + gGamePaused &= ~GAME_PAUSED_SAVING_TRACK; + gTrackDesignSaveMode = false; + OpenRCT2::Audio::Resume(); + + WindowBase* main_w = WindowGetMain(); + if (main_w != nullptr) + { + main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); + } + + GfxInvalidateScreen(); + ToolCancel(); + } + + /** + * + * rct2: 0x006D27A3 + */ + void SetupScenerySelection() + { + if (gTrackDesignSaveMode) + { + CancelScenerySelection(); + } + + while (ToolSet(*w, WIDX_BACKGROUND, Tool::Crosshair)) + ; + + gTrackDesignSaveRideIndex = w->rideId; + + TrackDesignSaveInit(); + gGamePaused |= GAME_PAUSED_SAVING_TRACK; + gTrackDesignSaveMode = true; + + OpenRCT2::Audio::StopAll(); + + WindowBase* w_main = WindowGetMain(); + if (w_main != nullptr) + { + w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); + } + + GfxInvalidateScreen(); + } + + /** + * + * rct2: 0x006D3026 + */ + void MeasurementsDesignReset() + { + TrackDesignSaveResetScenery(); + } + + /** + * + * rct2: 0x006D303D + */ + void MeasurementsDesignSelectNearbyScenery() + { + TrackDesignSaveSelectNearbyScenery(gTrackDesignSaveRideIndex); + } + + /** + * + * rct2: 0x006AD4DA + */ + void MeasurementsDesignCancel() + { + if (gTrackDesignSaveMode) + { + CancelScenerySelection(); + } + } + + void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) + { + if (result == MODAL_RESULT_OK) + { + TrackRepositoryScan(); + } + GfxInvalidateScreen(); + }; + + /** + * + * rct2: 0x006AD4CD + */ + void MeasurementsDesignSave() + { + TrackDesignState tds{}; + + Ride* ride = GetRide(w->rideId); + _trackDesign = ride->SaveToTrackDesign(tds); + if (!_trackDesign) + { + return; + } + + if (gTrackDesignSaveMode) + { + auto errMessage = _trackDesign->CreateTrackDesignScenery(tds); + if (!errMessage.Successful) + { + ContextShowError(STR_CANT_SAVE_TRACK_DESIGN, errMessage.Message, {}); + return; + } + if (errMessage.HasMessage()) + { + ContextShowError(errMessage.Message, STR_EMPTY, {}); + } + } + + auto trackName = ride->GetName(); + auto intent = Intent(WindowClass::Loadsave); + intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); + intent.PutExtra(INTENT_EXTRA_TRACK_DESIGN, _trackDesign.get()); + intent.PutExtra(INTENT_EXTRA_PATH, trackName); + intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(&TrackDesignCallback)); + + ContextOpenIntent(&intent); + } + + /** + * + * rct2: 0x006AD4DA + */ + void MeasurementsClose() + { + MeasurementsDesignCancel(); + } + + /** + * + * rct2: 0x006AD478 + */ + void MeasurementsMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + case WIDX_SELECT_NEARBY_SCENERY: + MeasurementsDesignSelectNearbyScenery(); + break; + case WIDX_RESET_SELECTION: + MeasurementsDesignReset(); + break; + case WIDX_SAVE_DESIGN: + MeasurementsDesignSave(w); + break; + case WIDX_CANCEL_DESIGN: + MeasurementsDesignCancel(); + break; + } + } + + /** + * + * rct2: 0x006AD564 + */ + void MeasurementsResize() + { + WindowSetResize(*w, 316, 234, 316, 234); + } + + /** + * + * rct2: 0x006AD4AB + */ + void MeasurementsMousedown(WidgetIndex widgetIndex, Widget* widget) + { + if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) + return; + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + gDropdownItems[0].Format = STR_SAVE_TRACK_DESIGN_ITEM; + gDropdownItems[1].Format = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM; + + WindowDropdownShowText( + { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], + Dropdown::Flag::StayOpen, 2); + gDropdownDefaultIndex = 0; + if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) + { + // Disable saving without scenery if we're a flat ride + Dropdown::SetDisabled(0, true); + gDropdownDefaultIndex = 1; + } + } + + /** + * + * rct2: 0x006AD4B2 + */ + void MeasurementsDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) + return; + + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; + + if (dropdownIndex == 0) + { + MeasurementsDesignSave(w); + } + else + SetupScenerySelection(w); + } + + /** + * + * rct2: 0x006AD5DD + */ + void MeasurementsUpdate() + { + w->frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_7); + } + + /** + * + * rct2: 0x006D2AE7 + */ + void MeasurementsTooldown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + _lastSceneryX = screenCoords.x; + _lastSceneryY = screenCoords.y; + _collectTrackDesignScenery = true; // Default to true in case user does not select anything valid + + constexpr auto flags = EnumsToFlags( + ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, + ViewportInteractionItem::LargeScenery); + auto info = GetMapCoordinatesFromPos(screenCoords, flags); + switch (info.SpriteType) + { + case ViewportInteractionItem::Scenery: + case ViewportInteractionItem::LargeScenery: + case ViewportInteractionItem::Wall: + case ViewportInteractionItem::Footpath: + _collectTrackDesignScenery = !TrackDesignSaveContainsTileElement(info.Element); + TrackDesignSaveSelectTileElement(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery); + break; + default: + break; + } + } + + void MeasurementsTooldrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (screenCoords.x == _lastSceneryX && screenCoords.y == _lastSceneryY) + return; + _lastSceneryX = screenCoords.x; + _lastSceneryY = screenCoords.y; + + auto flags = EnumsToFlags( + ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, + ViewportInteractionItem::LargeScenery); + auto info = GetMapCoordinatesFromPos(screenCoords, flags); + switch (info.SpriteType) + { + case ViewportInteractionItem::Scenery: + case ViewportInteractionItem::LargeScenery: + case ViewportInteractionItem::Wall: + case ViewportInteractionItem::Footpath: + TrackDesignSaveSelectTileElement(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery); + break; + default: + break; + } + } + + /** + * + * rct2: 0x006AD4DA + */ + void MeasurementsToolabort(WidgetIndex widgetIndex) + { + MeasurementsDesignCancel(); + } + + /** + * + * rct2: 0x006ACDBC + */ + void MeasurementsOnPrepareDraw() + { + auto widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); + } + + SetPressedTab(w); + + const auto rideId = w->rideId; + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE; + window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::Empty; + if (gTrackDesignSaveMode && gTrackDesignSaveRideIndex == rideId) + { + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Button; + window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Button; + window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Button; + window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Button; + } + else + { + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Empty; + window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Empty; + window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Empty; + window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Empty; + + window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::FlatBtn; + w->disabled_widgets |= (1uLL << WIDX_SAVE_TRACK_DESIGN); + if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) + { + if (ride->excitement != RIDE_RATING_UNDEFINED) + { + w->disabled_widgets &= ~(1uLL << WIDX_SAVE_TRACK_DESIGN); + window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN; + } + } + } + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + } + + /** + * + * rct2: 0x006ACF07 + */ + void MeasurementsPaint(DrawPixelInfo& dpi) + { + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); + + if (window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type == WindowWidgetType::Button) + { + Widget* widget = &window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND]; + + ScreenCoordsXY widgetCoords(w->windowPos.x + widget->width() / 2, w->windowPos.y + widget->top + 40); + DrawTextWrapped( + dpi, widgetCoords, w->width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, {}, { TextAlignment::CENTRE }); + + widgetCoords.x = w->windowPos.x + 4; + widgetCoords.y = w->windowPos.y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; + GfxFillRectInset( + dpi, { widgetCoords, { w->windowPos.x + 312, widgetCoords.y + 1 } }, w->colours[1], + INSET_RECT_FLAG_BORDER_INSET); + } + else + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto screenCoords = w->windowPos + + ScreenCoordsXY{ window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].left + 4, + window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + + if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) + { + // Excitement + StringId ratingName = GetRatingName(ride->excitement); + auto ft = Formatter(); + ft.Add(ride->excitement); + ft.Add(ratingName); + StringId stringId = !RideHasRatings(*ride) ? STR_EXCITEMENT_RATING_NOT_YET_AVAILABLE : STR_EXCITEMENT_RATING; + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Intensity + ratingName = GetRatingName(ride->intensity); + ft = Formatter(); + ft.Add(ride->intensity); + ft.Add(ratingName); + + stringId = STR_INTENSITY_RATING; + if (!RideHasRatings(*ride)) + stringId = STR_INTENSITY_RATING_NOT_YET_AVAILABLE; + else if (ride->intensity >= RIDE_RATING(10, 00)) + stringId = STR_INTENSITY_RATING_RED; + + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Nausea + ratingName = GetRatingName(ride->nausea); + ft = Formatter(); + ft.Add(ride->nausea); + ft.Add(ratingName); + stringId = !RideHasRatings(*ride) ? STR_NAUSEA_RATING_NOT_YET_AVAILABLE : STR_NAUSEA_RATING; + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += 2 * LIST_ROW_HEIGHT; + + // Horizontal rule + GfxFillRectInset( + dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 303, -5 } }, w->colours[1], + INSET_RECT_FLAG_BORDER_INSET); + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_NO_RAW_STATS)) + { + if (ride->type == RIDE_TYPE_MINI_GOLF) + { + // Holes + ft = Formatter(); + ft.Add(ride->holes); + DrawTextBasic(dpi, screenCoords, STR_HOLES, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + else + { + // Max speed + ft = Formatter(); + ft.Add((ride->max_speed * 9) >> 18); + DrawTextBasic(dpi, screenCoords, STR_MAX_SPEED, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Average speed + ft = Formatter(); + ft.Add((ride->average_speed * 9) >> 18); + DrawTextBasic(dpi, screenCoords, STR_AVERAGE_SPEED, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Ride time + ft = Formatter(); + int32_t numTimes = 0; + // TODO: STR_RIDE_TIME only takes up to 4 stations modify to take more + // also if modified may need to be split into multiple format strings + // as formatter cannot take more than 256 bytes + for (int32_t i = 0; i < std::min(ride->num_stations, 4); i++) + { + StationIndex stationIndex = StationIndex::FromUnderlying(numTimes); + auto time = ride->GetStation(stationIndex).SegmentTime; + if (time != 0) + { + ft.Add(STR_RIDE_TIME_ENTRY_WITH_SEPARATOR); + ft.Add(time); + numTimes++; + } + } + if (numTimes == 0) + { + ft.Add(STR_RIDE_TIME_ENTRY); + ft.Add(0); + numTimes++; + } + else + { + // sadly, STR_RIDE_TIME_ENTRY_WITH_SEPARATOR are defined with the separator AFTER an entry + // therefore we set the last entry to use the no-separator format now, post-format + ft.Rewind(); + ft.Increment((numTimes - 1) * 4); + ft.Add(STR_RIDE_TIME_ENTRY); + } + ft.Rewind(); + ft.Increment(numTimes * 4); + ft.Add(0); + ft.Add(0); + ft.Add(0); + ft.Add(0); + DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_TIME, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + // Ride length + ft = Formatter(); + int32_t numLengths = 0; + // TODO: see above STR_RIDE_LENGTH is also only able to display max 4 + for (int32_t i = 0; i < std::min(ride->num_stations, 4); i++) + { + StationIndex stationIndex = StationIndex::FromUnderlying(i); + auto length = ride->GetStation(stationIndex).SegmentLength; + if (length != 0) + { + length >>= 16; + ft.Add(STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR); + ft.Add(length & 0xFFFF); + numLengths++; + } + } + if (numLengths == 0) + { + ft.Add(STR_RIDE_LENGTH_ENTRY); + ft.Add(0); + numLengths++; + } + else + { + // sadly, STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR are defined with the separator AFTER an entry + // therefore we set the last entry to use the no-separator format now, post-format + ft.Rewind(); + ft.Increment((numLengths - 1) * 4); + ft.Add(STR_RIDE_LENGTH_ENTRY); + } + ft.Rewind(); + ft.Increment(numLengths * 4); + ft.Add(0); + ft.Add(0); + ft.Add(0); + ft.Add(0); + DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_LENGTH, ft); + + screenCoords.y += LIST_ROW_HEIGHT; + + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) + { + // Max. positive vertical G's + stringId = STR_MAX_POSITIVE_VERTICAL_G; + + ft = Formatter(); + ft.Add(ride->max_positive_vertical_g); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Max. negative vertical G's + stringId = ride->max_negative_vertical_g <= RIDE_G_FORCES_RED_NEG_VERTICAL + ? STR_MAX_NEGATIVE_VERTICAL_G_RED + : STR_MAX_NEGATIVE_VERTICAL_G; + ft = Formatter(); + ft.Add(ride->max_negative_vertical_g); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Max lateral G's + stringId = ride->max_lateral_g > RIDE_G_FORCES_RED_LATERAL ? STR_MAX_LATERAL_G_RED : STR_MAX_LATERAL_G; + ft = Formatter(); + ft.Add(ride->max_lateral_g); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Total 'air' time + ft = Formatter(); + ft.Add(ride->total_air_time * 3); + DrawTextBasic(dpi, screenCoords, STR_TOTAL_AIR_TIME, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_DROPS)) + { + // Drops + auto drops = ride->drops & 0x3F; + ft = Formatter(); + ft.Add(drops); + DrawTextBasic(dpi, screenCoords, STR_DROPS, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Highest drop height + auto highestDropHeight = (ride->highest_drop_height * 3) / 4; + ft = Formatter(); + ft.Add(highestDropHeight); + DrawTextBasic(dpi, screenCoords, STR_HIGHEST_DROP_HEIGHT, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + if (ride->type != RIDE_TYPE_MINI_GOLF) + { + // Inversions + if (ride->inversions != 0) + { + ft = Formatter(); + ft.Add(ride->inversions); + DrawTextBasic(dpi, screenCoords, STR_INVERSIONS, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + } + } + } + else + { + DrawTextBasic(dpi, screenCoords, STR_NO_TEST_RESULTS_YET); + } + } + } + +#pragma endregion + +#pragma region Graphs + + enum + { + GRAPH_VELOCITY, + GRAPH_ALTITUDE, + GRAPH_VERTICAL, + GRAPH_LATERAL + }; + + /** + * + * rct2: 0x006AE8A6 + */ + void SetGraph(int32_t type) + { + if ((w->list_information_type & 0xFF) == type) + { + w->list_information_type ^= 0x8000; + } + else + { + w->list_information_type &= 0xFF00; + w->list_information_type |= type; + } + w->Invalidate(); + } + + /** + * + * rct2: 0x006AE85D + */ + void GraphsMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + } + } + + /** + * + * rct2: 0x006AE8DA + */ + void GraphsResize() + { + WindowSetResize(*w, 316, 182, 500, 450); + } + + /** + * + * rct2: 0x006AE878 + */ + void GraphsMousedown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_GRAPH_VELOCITY: + SetGraph(w, GRAPH_VELOCITY); + break; + case WIDX_GRAPH_ALTITUDE: + SetGraph(w, GRAPH_ALTITUDE); + break; + case WIDX_GRAPH_VERTICAL: + SetGraph(w, GRAPH_VERTICAL); + break; + case WIDX_GRAPH_LATERAL: + SetGraph(w, GRAPH_LATERAL); + break; + } + } + + /** + * + * rct2: 0x006AE95D + */ + void GraphsUpdate() + { + Widget* widget; + int32_t x; + + w->frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_8); + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_GRAPH); + + widget = &window_ride_graphs_widgets[WIDX_GRAPH]; + x = w->scrolls[0].h_left; + if (!(w->list_information_type & 0x8000)) + { + auto ride = GetRide(w->rideId); + if (ride != nullptr) + { + RideMeasurement* measurement{}; + std::tie(measurement, std::ignore) = ride->GetMeasurement(); + x = measurement == nullptr ? 0 : measurement->current_item - ((widget->width() / 4) * 3); + } + } + + w->scrolls[0].h_left = std::clamp(x, 0, w->scrolls[0].h_right - (widget->width() - 2)); + WidgetScrollUpdateThumbs(*w, WIDX_GRAPH); + } + + /** + * + * rct2: 0x006AEA75 + */ + void GraphsScrollgetheight(int32_t scrollIndex, int32_t* width, int32_t* height) + { + OnPrepareDraw(); + + // Set minimum size + *width = window_ride_graphs_widgets[WIDX_GRAPH].width() - 2; + + // Get measurement size + auto ride = GetRide(w->rideId); + if (ride != nullptr) + { + RideMeasurement* measurement{}; + std::tie(measurement, std::ignore) = ride->GetMeasurement(); + if (measurement != nullptr) + { + *width = std::max(*width, measurement->num_items); + } + } + } + + /** + * + * rct2: 0x006AE953 + */ + void Graphs15(int32_t scrollIndex, int32_t scrollAreaType) + { + w->list_information_type |= 0x8000; + } + + /** + * + * rct2: 0x006AEA05 + */ + OpenRCT2String GraphsTooltip(const WidgetIndex widgetIndex, const StringId fallback) + { + if (widgetIndex == WIDX_GRAPH) + { + auto ride = GetRide(w->rideId); + if (ride != nullptr) + { + auto [measurement, message] = ride->GetMeasurement(); + if (measurement != nullptr && (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING)) + { + auto ft = Formatter(); + ft.Increment(2); + ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number); + ft.Add(measurement->vehicle_index + 1); + return { fallback, ft }; + } + + return message; + } + } + else + { + return { STR_NONE, {} }; + } + return { fallback, {} }; + } + + /** + * + * rct2: 0x006AE372 + */ + void GraphsOnPrepareDraw() + { + auto widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); + } + + SetPressedTab(w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + // Set pressed graph button type + w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_VELOCITY); + w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_ALTITUDE); + w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_VERTICAL); + w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_LATERAL); + w->pressed_widgets |= (1LL << (WIDX_GRAPH_VELOCITY + (w->list_information_type & 0xFF))); + + // Hide graph buttons that are not applicable + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) + { + window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Button; + window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Button; + } + else + { + window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Empty; + window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Empty; + } + + // Anchor graph widget + auto x = w->width - 4; + auto y = w->height - BUTTON_FACE_HEIGHT - 8; + + window_ride_graphs_widgets[WIDX_GRAPH].right = x; + window_ride_graphs_widgets[WIDX_GRAPH].bottom = y; + y += 3; + window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].top = y; + window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].top = y; + window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].top = y; + window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].top = y; + y += BUTTON_FACE_HEIGHT + 1; + window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].bottom = y; + window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].bottom = y; + window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].bottom = y; + window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].bottom = y; + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + } + + /** + * + * rct2: 0x006AE4BC + */ + void GraphsPaint(DrawPixelInfo& dpi) + { + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); + } + + /** + * + * rct2: 0x006AE4C7 + */ + void GraphsScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) + { + GfxClear(&dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker); + + auto widget = &window_ride_graphs_widgets[WIDX_GRAPH]; + auto ride = GetRide(w->rideId); + if (ride == nullptr) + { + return; + } + + auto [measurement, message] = ride->GetMeasurement(); + + if (measurement == nullptr) + { + // No measurement message + ScreenCoordsXY stringCoords(widget->width() / 2, widget->height() / 2 - 5); + int32_t width = widget->width() - 2; + DrawTextWrapped(dpi, stringCoords, width, message.str, message.args, { TextAlignment::CENTRE }); + return; + } + + // Vertical grid lines + const uint8_t lightColour = ColourMapA[COLOUR_SATURATED_GREEN].mid_light; + const uint8_t darkColour = ColourMapA[COLOUR_SATURATED_GREEN].mid_dark; + + int32_t time = 0; + for (int32_t x = 0; x < dpi.x + dpi.width; x += 80) + { + if (x + 80 >= dpi.x) + { + auto coord1 = ScreenCoordsXY{ x, dpi.y }; + auto coord2 = ScreenCoordsXY{ x, dpi.y + dpi.height - 1 }; + GfxFillRect(dpi, { coord1, coord2 }, lightColour); + GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 16, 0 }, coord2 + ScreenCoordsXY{ 16, 0 } }, darkColour); + GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 32, 0 }, coord2 + ScreenCoordsXY{ 32, 0 } }, darkColour); + GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 48, 0 }, coord2 + ScreenCoordsXY{ 48, 0 } }, darkColour); + GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 64, 0 }, coord2 + ScreenCoordsXY{ 64, 0 } }, darkColour); + } + time += 5; + } + + // Horizontal grid lines + int32_t listType = w->list_information_type & 0xFF; + int16_t yUnit = window_graphs_y_axi[listType].unit; + StringId stringID = window_graphs_y_axi[listType].label; + int16_t yUnitInterval = window_graphs_y_axi[listType].unit_interval; + int16_t yInterval = window_graphs_y_axi[listType].interval; + + // Scale modifier + if (listType == GRAPH_ALTITUDE) + { + yUnit -= gMapBaseZ * 3; + } + + for (int32_t y = widget->height() - 13; y >= 8; y -= yInterval, yUnit += yUnitInterval) + { + // Minor / major line + int32_t colour = yUnit == 0 ? lightColour : darkColour; + GfxFillRect(dpi, { { dpi.x, y }, { dpi.x + dpi.width - 1, y } }, colour); + + int16_t scaled_yUnit = yUnit; + // Scale modifier + if (listType == GRAPH_ALTITUDE) + scaled_yUnit /= 2; + + auto ft = Formatter(); + ft.Add(scaled_yUnit); + + DrawTextBasic(dpi, { w->scrolls[0].h_left + 1, y - 4 }, stringID, ft, { FontStyle::Small }); + } + + // Time marks + time = 0; + for (int32_t x = 0; x < dpi.x + dpi.width; x += 80) + { + auto ft = Formatter(); + ft.Add(time); + if (x + 80 >= dpi.x) + DrawTextBasic(dpi, { x + 2, 1 }, STR_RIDE_STATS_TIME, ft, { FontStyle::Small }); + time += 5; + } + + // Plot + int32_t x = dpi.x; + int32_t firstPoint, secondPoint; + // Uses the force limits (used to draw extreme G's in red on measurement tab) to determine if line should be drawn red. + int32_t intensityThresholdPositive = 0; + int32_t intensityThresholdNegative = 0; + for (int32_t width = 0; width < dpi.width; width++, x++) + { + if (x < 0 || x >= measurement->num_items - 1) + continue; + + constexpr int32_t VerticalGraphHeightOffset = 39; + constexpr int32_t LateralGraphHeightOffset = 52; + + switch (listType) + { + case GRAPH_VELOCITY: + firstPoint = measurement->velocity[x] / 2; + secondPoint = measurement->velocity[x + 1] / 2; + break; + case GRAPH_ALTITUDE: + firstPoint = measurement->altitude[x]; + secondPoint = measurement->altitude[x + 1]; + break; + case GRAPH_VERTICAL: + firstPoint = measurement->vertical[x] + VerticalGraphHeightOffset; + secondPoint = measurement->vertical[x + 1] + VerticalGraphHeightOffset; + intensityThresholdNegative = (RIDE_G_FORCES_RED_NEG_VERTICAL / 8) + VerticalGraphHeightOffset; + break; + case GRAPH_LATERAL: + firstPoint = measurement->lateral[x] + LateralGraphHeightOffset; + secondPoint = measurement->lateral[x + 1] + LateralGraphHeightOffset; + intensityThresholdPositive = (RIDE_G_FORCES_RED_LATERAL / 8) + LateralGraphHeightOffset; + intensityThresholdNegative = -(RIDE_G_FORCES_RED_LATERAL / 8) + LateralGraphHeightOffset; + break; + default: + LOG_ERROR("Wrong graph type %d", listType); + firstPoint = secondPoint = 0; + break; + } + + // Adjust line to match graph widget position. + firstPoint = widget->height() - firstPoint - 13; + secondPoint = widget->height() - secondPoint - 13; + if (firstPoint > secondPoint) + { + std::swap(firstPoint, secondPoint); + } + + // Adjust threshold line position as well + if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL) + { + intensityThresholdPositive = widget->height() - intensityThresholdPositive - 13; + intensityThresholdNegative = widget->height() - intensityThresholdNegative - 13; + } + + const bool previousMeasurement = x > measurement->current_item; + + // Draw the current line in grey. + GfxFillRect( + dpi, { { x, firstPoint }, { x, secondPoint } }, previousMeasurement ? PALETTE_INDEX_17 : PALETTE_INDEX_21); + + // Draw red over extreme values (if supported by graph type). + if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL) + { + const auto redLineColour = previousMeasurement ? PALETTE_INDEX_171 : PALETTE_INDEX_173; + + // Line exceeds negative threshold (at bottom of graph). + if (secondPoint >= intensityThresholdNegative) + { + const auto redLineTop = ScreenCoordsXY{ x, std::max(firstPoint, intensityThresholdNegative) }; + const auto redLineBottom = ScreenCoordsXY{ x, std::max(secondPoint, intensityThresholdNegative) }; + GfxFillRect(dpi, { redLineTop, redLineBottom }, redLineColour); + } + + // Line exceeds positive threshold (at top of graph). + if (listType == GRAPH_LATERAL && firstPoint < intensityThresholdPositive) + { + const auto redLineTop = ScreenCoordsXY{ x, std::min(firstPoint, intensityThresholdPositive) }; + const auto redLineBottom = ScreenCoordsXY{ x, std::min(secondPoint, intensityThresholdPositive) }; + GfxFillRect(dpi, { redLineTop, redLineBottom }, redLineColour); + } + } + } + } + +#pragma endregion + +#pragma region Income + + static utf8 _moneyInputText[MONEY_STRING_MAXLENGTH]; + + static void UpdateSamePriceThroughoutFlags(ShopItem shop_item) + { + if (GetShopItemDescriptor(shop_item).IsPhoto()) + { + auto newFlags = gSamePriceThroughoutPark; + if (gSamePriceThroughoutPark & EnumToFlag(shop_item)) + newFlags &= ~EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); + else + newFlags |= EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); + auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); + GameActions::Execute(&parkSetParameter); + } + else + { + auto newFlags = gSamePriceThroughoutPark; + newFlags ^= EnumToFlag(shop_item); + auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); + GameActions::Execute(&parkSetParameter); + } + } + + /** + * + * rct2: 0x006ADEFD + */ + void IncomeTogglePrimaryPrice() + { + const auto rideId = w->rideId; + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + ShopItem shop_item; + const auto& rtd = ride->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET)) + { + shop_item = ShopItem::Admission; + } + else + { + auto rideEntry = GetRideEntryByIndex(ride->subtype); + if (rideEntry != nullptr) + { + shop_item = rideEntry->shop_item[0]; + if (shop_item == ShopItem::None) + return; + } + else + { + return; + } + } + + UpdateSamePriceThroughoutFlags(shop_item); + + auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[0], true); + GameActions::Execute(&rideSetPriceAction); + } + + /** + * + * rct2: 0x006AE06E + */ + void IncomeToggleSecondaryPrice() + { + const auto rideId = w->rideId; + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = GetRideEntryByIndex(ride->subtype); + if (rideEntry == nullptr) + return; + + auto shop_item = rideEntry->shop_item[1]; + if (shop_item == ShopItem::None) + shop_item = ride->GetRideTypeDescriptor().PhotoItem; + + UpdateSamePriceThroughoutFlags(shop_item); + + auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[1], false); + GameActions::Execute(&rideSetPriceAction); + } + + void IncomeSetPrimaryPrice(money64 price) + { + auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, true); + GameActions::Execute(&rideSetPriceAction); + } + + /** + * + * rct2: 0x006AE1E4 + */ + void IncomeIncreasePrimaryPrice() + { + if (!IncomeCanModifyPrimaryPrice(w)) + return; + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto price = ride->price[0]; + if (price < 20.00_GBP) + price++; + + IncomeSetPrimaryPrice(w, price); + } + + /** + * + * rct2: 0x006AE237 + */ + void IncomeDecreasePrimaryPrice() + { + if (!IncomeCanModifyPrimaryPrice(w)) + return; + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto price = ride->price[0]; + if (price > 0.00_GBP) + price--; + + IncomeSetPrimaryPrice(w, price); + } + + money64 IncomeGetSecondaryPrice() + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return 0; + + return ride->price[1]; + } + + void IncomeSetSecondaryPrice(money64 price) + { + auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, false); + GameActions::Execute(&rideSetPriceAction); + } + + bool IncomeCanModifyPrimaryPrice() + { + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return false; + + auto rideEntry = ride->GetRideEntry(); + const auto& rtd = ride->GetRideTypeDescriptor(); + return ParkRidePricesUnlocked() || rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET) + || (rideEntry != nullptr && rideEntry->shop_item[0] != ShopItem::None); + } + + /** + * + * rct2: 0x006AE269 + */ + void IncomeIncreaseSecondaryPrice() + { + auto price = IncomeGetSecondaryPrice(w); + + if (price < 20.00_GBP) + price++; + + IncomeSetSecondaryPrice(w, price); + } + + /** + * + * rct2: 0x006AE28D + */ + void IncomeDecreaseSecondaryPrice() + { + auto price = IncomeGetSecondaryPrice(w); + + if (price > 0.00_GBP) + price--; + + IncomeSetSecondaryPrice(w, price); + } + + /** + * + * rct2: 0x006ADEA9 + */ + void IncomeMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + case WIDX_PRIMARY_PRICE: + { + if (!IncomeCanModifyPrimaryPrice(w)) + return; + + auto ride = GetRide(w->rideId); + if (ride != nullptr) + { + MoneyToString(ride->price[0], _moneyInputText, MONEY_STRING_MAXLENGTH, true); + WindowTextInputRawOpen( + w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, + MONEY_STRING_MAXLENGTH); + } + break; + } + case WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK: + IncomeTogglePrimaryPrice(w); + break; + case WIDX_SECONDARY_PRICE: + { + auto price64 = IncomeGetSecondaryPrice(w); + + MoneyToString(price64, _moneyInputText, MONEY_STRING_MAXLENGTH, true); + WindowTextInputRawOpen( + w, WIDX_SECONDARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, + MONEY_STRING_MAXLENGTH); + } + break; + case WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK: + IncomeToggleSecondaryPrice(w); + break; + } + } + + /** + * + * rct2: 0x006AE2F8 + */ + void IncomeResize() + { + WindowSetResize(*w, 316, 194, 316, 194); + } + + /** + * + * rct2: 0x006ADED4 + */ + void IncomeMousedown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_PRIMARY_PRICE_INCREASE: + IncomeIncreasePrimaryPrice(w); + break; + case WIDX_PRIMARY_PRICE_DECREASE: + IncomeDecreasePrimaryPrice(w); + break; + case WIDX_SECONDARY_PRICE_INCREASE: + IncomeIncreaseSecondaryPrice(w); + break; + case WIDX_SECONDARY_PRICE_DECREASE: + IncomeDecreaseSecondaryPrice(w); + break; + } + } + + /** + * + * rct2: 0x006AE2BF + */ + void IncomeUpdate() + { + w->frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_9); + + auto ride = GetRide(w->rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) + { + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME; + w->Invalidate(); + } + } + + void IncomeTextinput(WidgetIndex widgetIndex, const char* text) + { + if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text == nullptr) + return; + + money64 price = StringToMoney(text); + if (price == MONEY64_UNDEFINED) + { + return; + } + + price = std::clamp(price, 0.00_GBP, 20.00_GBP); + + if (widgetIndex == WIDX_PRIMARY_PRICE) + { + IncomeSetPrimaryPrice(w, price); + } + else + { + IncomeSetSecondaryPrice(w, price); + } + } + + /** + * + * rct2: 0x006ADAA3 + */ + void IncomeOnPrepareDraw() + { + auto widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); + } + + SetPressedTab(w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + w->widgets[WIDX_TITLE].text = STR_ARG_18_STRINGID; + + auto ft = Formatter::Common(); + ft.Increment(18); + ride->FormatNameTo(ft); + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + // Primary item + w->pressed_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); + w->disabled_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE); + + window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_NONE; + window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_NONE; + + // If ride prices are locked, do not allow setting the price, unless we're dealing with a shop or toilet. + const auto& rtd = ride->GetRideTypeDescriptor(); + if (!ParkRidePricesUnlocked() && rideEntry->shop_item[0] == ShopItem::None && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET)) + { + w->disabled_widgets |= (1uLL << WIDX_PRIMARY_PRICE); + window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; + window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; + } + + window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = STR_RIDE_INCOME_ADMISSION_PRICE; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO; + window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; + + window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_BOTTOM_TOOLBAR_CASH; + auto ridePrimaryPrice = RideGetPrice(*ride); + ft.Rewind(); + ft.Add(ridePrimaryPrice); + if (ridePrimaryPrice == 0) + window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_FREE; + + ShopItem primaryItem = ShopItem::Admission; + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET) || ((primaryItem = rideEntry->shop_item[0]) != ShopItem::None)) + { + window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; + + if (ShopItemHasCommonPrice(primaryItem)) + w->pressed_widgets |= (1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); + + window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = GetShopItemDescriptor(primaryItem).Naming.PriceLabel; + } + + // Get secondary item + auto secondaryItem = ride->GetRideTypeDescriptor().PhotoItem; + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) + { + if ((secondaryItem = rideEntry->shop_item[1]) != ShopItem::None) + { + window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = GetShopItemDescriptor(secondaryItem) + .Naming.PriceLabel; + } + } + + if (secondaryItem == ShopItem::None) + { + // Hide secondary item widgets + window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Empty; + window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Empty; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Empty; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Empty; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; + } + else + { + // Set same price throughout park checkbox + w->pressed_widgets &= ~(1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); + if (ShopItemHasCommonPrice(secondaryItem)) + w->pressed_widgets |= (1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); + + // Show widgets + window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Label; + window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Spinner; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Button; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Button; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; + + // Set secondary item price + window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_RIDE_SECONDARY_PRICE_VALUE; + ft.Rewind(); + ft.Increment(10); + ft.Add(ride->price[1]); + if (ride->price[1] == 0) + window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_FREE; + } + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + } + + /** + * + * rct2: 0x006ADCE5 + */ + void IncomePaint(DrawPixelInfo& dpi) + { + StringId stringId; + money64 profit; + ShopItem primaryItem, secondaryItem; + + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto screenCoords = w->windowPos + + ScreenCoordsXY{ window_ride_income_widgets[WIDX_PAGE_BACKGROUND].left + 4, + window_ride_income_widgets[WIDX_PAGE_BACKGROUND].top + 33 }; + + // Primary item profit / loss per item sold + primaryItem = rideEntry->shop_item[0]; + if (primaryItem != ShopItem::None) + { + profit = ride->price[0]; + + stringId = STR_PROFIT_PER_ITEM_SOLD; + profit -= GetShopItemDescriptor(primaryItem).Cost; + if (profit < 0) + { + profit *= -1; + stringId = STR_LOSS_PER_ITEM_SOLD; + } + + auto ft = Formatter(); + ft.Add(profit); + + DrawTextBasic(dpi, screenCoords, stringId, ft); + } + screenCoords.y += 44; + + // Secondary item profit / loss per item sold + secondaryItem = ride->GetRideTypeDescriptor().PhotoItem; + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) + secondaryItem = rideEntry->shop_item[1]; + + if (secondaryItem != ShopItem::None) + { + profit = ride->price[1]; + + stringId = STR_PROFIT_PER_ITEM_SOLD; + profit -= GetShopItemDescriptor(secondaryItem).Cost; + if (profit < 0) + { + profit *= -1; + stringId = STR_LOSS_PER_ITEM_SOLD; + } + + auto ft = Formatter(); + ft.Add(profit); + + DrawTextBasic(dpi, screenCoords, stringId, ft); + } + screenCoords.y += 18; + + // Income per hour + if (ride->income_per_hour != MONEY64_UNDEFINED) + { + auto ft = Formatter(); + ft.Add(ride->income_per_hour); + + DrawTextBasic(dpi, screenCoords, STR_INCOME_PER_HOUR, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + // Running cost per hour + money64 costPerHour = ride->upkeep_cost * 16; + stringId = ride->upkeep_cost == MONEY64_UNDEFINED ? STR_RUNNING_COST_UNKNOWN : STR_RUNNING_COST_PER_HOUR; + auto ft = Formatter(); + ft.Add(costPerHour); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Profit per hour + if (ride->profit != MONEY64_UNDEFINED) + { + ft = Formatter(); + ft.Add(ride->profit); + DrawTextBasic(dpi, screenCoords, STR_PROFIT_PER_HOUR, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + screenCoords.y += 5; + + // Total profit + ft = Formatter(); + ft.Add(ride->total_profit); + DrawTextBasic(dpi, screenCoords, STR_TOTAL_PROFIT, ft); + } + +#pragma endregion + +#pragma region Customer + + /** + * + * rct2: 0x006AD986 + */ + void CustomerMouseup(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*w); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + case WIDX_TAB_5: + case WIDX_TAB_6: + case WIDX_TAB_7: + case WIDX_TAB_8: + case WIDX_TAB_9: + case WIDX_TAB_10: + SetPage(w, widgetIndex - WIDX_TAB_1); + break; + case WIDX_SHOW_GUESTS_THOUGHTS: + { + auto intent = Intent(WindowClass::GuestList); + intent.PutExtra( + INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsThinkingAboutRide)); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); + ContextOpenIntent(&intent); + break; + } + case WIDX_SHOW_GUESTS_ON_RIDE: + { + auto intent = Intent(WindowClass::GuestList); + intent.PutExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsOnRide)); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); + ContextOpenIntent(&intent); + break; + } + case WIDX_SHOW_GUESTS_QUEUING: + { + auto intent = Intent(WindowClass::GuestList); + intent.PutExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsInQueue)); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); + ContextOpenIntent(&intent); + break; + } + } + } + + /** + * + * rct2: 0x006ADA29 + */ + void CustomerResize() + { + w->flags |= WF_RESIZABLE; + WindowSetResize(*w, 316, 163, 316, 163); + } + + /** + * + * rct2: 0x006AD9DD + */ + void CustomerUpdate() + { + w->picked_peep_frame++; + if (w->picked_peep_frame >= 24) + w->picked_peep_frame = 0; + + OnPrepareDraw(); + WidgetInvalidate(*w, WIDX_TAB_10); + + auto ride = GetRide(w->rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER) + { + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER; + w->Invalidate(); + } + } + + /** + * + * rct2: 0x006AD5F8 + */ + void CustomerOnPrepareDraw() + { + auto widgets = window_ride_page_widgets[w->page]; + if (w->widgets != widgets) + { + w->widgets = widgets; + WindowInitScrollWidgets(*w); + } + + SetPressedTab(w); + + auto ride = GetRide(w->rideId); + if (ride != nullptr) + { + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + window_ride_customer_widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WindowWidgetType::FlatBtn; + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) + { + window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::Empty; + window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::Empty; + } + else + { + window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::FlatBtn; + window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::FlatBtn; + } + + AnchorBorderWidgets(w); + WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + } + } + + /** + * + * rct2: 0x006AD6CD + */ + void CustomerPaint(DrawPixelInfo& dpi) + { + ShopItem shopItem; + int16_t popularity, satisfaction, queueTime; + StringId stringId; + + WindowDrawWidgets(*w, dpi); + DrawTabImages(dpi, w); + + auto ride = GetRide(w->rideId); + if (ride == nullptr) + return; + + auto screenCoords = w->windowPos + + ScreenCoordsXY{ window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].left + 4, + window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + + // Customers currently on ride + if (ride->IsRide()) + { + auto ft = Formatter(); + ft.Add(ride->num_riders); + DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_ON_RIDE, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + // Customers per hour + auto ft = Formatter(); + ft.Add(RideCustomersPerHour(*ride)); + DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_PER_HOUR, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Popularity + popularity = ride->popularity; + if (popularity == 255) + { + stringId = STR_POPULARITY_UNKNOWN; + } + else + { + stringId = STR_POPULARITY_PERCENT; + popularity *= 4; + } + ft = Formatter(); + ft.Add(popularity); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Satisfaction + satisfaction = ride->satisfaction; + if (satisfaction == 255) + { + stringId = STR_SATISFACTION_UNKNOWN; + } + else + { + stringId = STR_SATISFACTION_PERCENT; + satisfaction *= 5; + } + ft = Formatter(); + ft.Add(satisfaction); + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Queue time + if (ride->IsRide()) + { + queueTime = ride->GetMaxQueueTime(); + stringId = queueTime == 1 ? STR_QUEUE_TIME_MINUTE : STR_QUEUE_TIME_MINUTES; + ft = Formatter(); + ft.Add(queueTime); + screenCoords.y += DrawTextWrapped(dpi, screenCoords, 308, stringId, ft, { TextAlignment::LEFT }); + screenCoords.y += 5; + } + + // Primary shop items sold + shopItem = ride->GetRideEntry()->shop_item[0]; + if (shopItem != ShopItem::None) + { + ft = Formatter(); + ft.Add(GetShopItemDescriptor(shopItem).Naming.Plural); + ft.Add(ride->no_primary_items_sold); + DrawTextBasic(dpi, screenCoords, STR_ITEMS_SOLD, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + // Secondary shop items sold / on-ride photos sold + shopItem = (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? ride->GetRideTypeDescriptor().PhotoItem + : ride->GetRideEntry()->shop_item[1]; + if (shopItem != ShopItem::None) + { + ft = Formatter(); + ft.Add(GetShopItemDescriptor(shopItem).Naming.Plural); + ft.Add(ride->no_secondary_items_sold); + DrawTextBasic(dpi, screenCoords, STR_ITEMS_SOLD, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + // Total customers + ft = Formatter(); + ft.Add(ride->total_customers); + DrawTextBasic(dpi, screenCoords, STR_TOTAL_CUSTOMERS, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Guests favourite + if (ride->IsRide()) + { + ft = Formatter(); + ft.Add(ride->guests_favourite); + stringId = ride->guests_favourite == 1 ? STR_FAVOURITE_RIDE_OF_GUEST : STR_FAVOURITE_RIDE_OF_GUESTS; + DrawTextBasic(dpi, screenCoords, stringId, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + screenCoords.y += 2; + + // Age + // If the ride has a build date that is in the future, show it as built this year. + int16_t age = std::max(DateGetYear(ride->GetAge()), 0); + stringId = age == 0 ? STR_BUILT_THIS_YEAR : age == 1 ? STR_BUILT_LAST_YEAR : STR_BUILT_YEARS_AGO; + ft = Formatter(); + ft.Add(age); + DrawTextBasic(dpi, screenCoords, stringId, ft); + } + +#pragma endregion +}; /** * * rct2: 0x006AEAB4 */ -static WindowBase* WindowRideOpen(const Ride& ride) +static WindowBase* Open(const Ride& ride) { WindowBase* w; @@ -1170,13 +6741,13 @@ static WindowBase* WindowRideOpen(const Ride& ride) w->list_information_type = 0; w->picked_peep_frame = 0; w->ride_colour = 0; - WindowRideDisableTabs(w); + DisableTabs(w); w->min_width = 316; w->min_height = 180; w->max_width = 500; w->max_height = 450; - WindowRideUpdateOverallView(ride); + UpdateOverallView(ride); PopulateVehicleTypeDropdown(ride, true); @@ -1216,10 +6787,10 @@ WindowBase* WindowRideMainOpen(const Ride& ride) if (w->page != WINDOW_RIDE_PAGE_MAIN) { - WindowRideSetPage(w, WINDOW_RIDE_PAGE_MAIN); + SetPage(w, WINDOW_RIDE_PAGE_MAIN); } - WindowRideInitViewport(w); + InitViewport(w); return w; } @@ -1257,7 +6828,7 @@ static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationI w->hold_down_widgets = window_ride_page_hold_down_widgets[w->page]; w->event_handlers = window_ride_page_events[w->page]; w->pressed_widgets = 0; - WindowRideDisableTabs(w); + DisableTabs(w); WindowInitScrollWidgets(*w); // View @@ -1270,7 +6841,7 @@ static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationI } w->ride.view = 1 + ride.NumTrains + stationIndex.ToUnderlying(); - WindowRideInitViewport(w); + InitViewport(w); return w; } @@ -1394,5746 +6965,12 @@ WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) w->hold_down_widgets = window_ride_page_hold_down_widgets[w->page]; w->event_handlers = window_ride_page_events[w->page]; w->pressed_widgets = 0; - WindowRideDisableTabs(w); + DisableTabs(w); WindowInitScrollWidgets(*w); w->ride.view = view; - WindowRideInitViewport(w); + InitViewport(w); w->Invalidate(); return w; } - -/** - * - * rct2: 0x006AF1D2 - */ -static void WindowRideSetPage(WindowBase* w, int32_t page) -{ - int32_t listen; - - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - if (w->classification == gCurrentToolWidget.window_classification && w->number == gCurrentToolWidget.window_number) - ToolCancel(); - - if (page == WINDOW_RIDE_PAGE_VEHICLE) - { - auto constructionWindow = WindowFindByClass(WindowClass::RideConstruction); - if (constructionWindow != nullptr && constructionWindow->number == w->number) - { - WindowCloseByClass(WindowClass::RideConstruction); - // Closing the construction window sets the tab to the first page, which we don't want here, - // as user just clicked the Vehicle page - WindowRideSetPage(w, WINDOW_RIDE_PAGE_VEHICLE); - } - } - - // Set listen only to viewport - listen = 0; - if (page == WINDOW_RIDE_PAGE_MAIN && w->page == WINDOW_RIDE_PAGE_MAIN && w->viewport != nullptr - && !(w->viewport->flags & VIEWPORT_FLAG_SOUND_ON)) - listen++; - - w->page = page; - w->frame_no = 0; - w->picked_peep_frame = 0; - - // There doesn't seem to be any need for this call, and it can sometimes modify the reported number of cars per train, so - // I've removed it if (page == WINDOW_RIDE_PAGE_VEHICLE) { ride_update_max_vehicles(ride); - //} - - w->RemoveViewport(); - - w->hold_down_widgets = window_ride_page_hold_down_widgets[page]; - w->event_handlers = window_ride_page_events[page]; - w->pressed_widgets = 0; - w->widgets = window_ride_page_widgets[page]; - WindowRideDisableTabs(w); - w->Invalidate(); - - WindowEventResizeCall(w); - WindowEventInvalidateCall(w); - WindowInitScrollWidgets(*w); - w->Invalidate(); - - if (listen != 0 && w->viewport != nullptr) - w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON; -} - -static void WindowRideSetPressedTab(WindowBase* w) -{ - int32_t i; - for (i = 0; i < WINDOW_RIDE_PAGE_COUNT; i++) - w->pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); - w->pressed_widgets |= 1LL << (WIDX_TAB_1 + w->page); -} - -static void WindowRideAnchorBorderWidgets(WindowBase* w) -{ - w->ResizeFrameWithPage(); -} - -#pragma region Main - -static std::optional GetStationIndexFromViewSelection(const WindowBase& w) -{ - const auto* ride = GetRide(RideId::FromUnderlying(w.number)); - if (ride == nullptr) - return std::nullopt; - - int32_t viewSelectionIndex = w.ride.view - 1 - ride->NumTrains; - if (viewSelectionIndex < 0) - { - return std::nullopt; - } - - for (const auto& station : ride->GetStations()) - { - if (!station.Start.IsNull() && viewSelectionIndex-- == 0) - { - const auto stationIndex = ride->GetStationIndex(&station); - return std::make_optional(stationIndex); - } - } - return std::nullopt; -} - -/** - * - * rct2: 0x006AF994 - */ -static void WindowRideInitViewport(WindowBase* w) -{ - if (w->page != WINDOW_RIDE_PAGE_MAIN) - return; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - int32_t viewSelectionIndex = w->ride.view - 1; - - std::optional focus; - - if (viewSelectionIndex >= 0 && viewSelectionIndex < ride->NumTrains && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) - { - auto vehId = ride->vehicles[viewSelectionIndex]; - const auto* rideEntry = ride->GetRideEntry(); - if (rideEntry != nullptr && rideEntry->TabCar != 0) - { - Vehicle* vehicle = GetEntity(vehId); - if (vehicle == nullptr) - { - vehId = EntityId::GetNull(); - } - else if (!vehicle->next_vehicle_on_train.IsNull()) - { - vehId = vehicle->next_vehicle_on_train; - } - } - if (!vehId.IsNull()) - { - focus = Focus(vehId); - } - } - else if (viewSelectionIndex >= ride->NumTrains && viewSelectionIndex < (ride->NumTrains + ride->num_stations)) - { - auto stationIndex = GetStationIndexFromViewSelection(*w); - if (stationIndex) - { - const auto location = ride->GetStation(*stationIndex).GetStart(); - focus = Focus(location); - } - } - else - { - if (viewSelectionIndex > 0) - { - w->ride.view = 0; - } - if (w->number < ride_overall_views.size()) - { - const auto& view = ride_overall_views[w->number]; - focus = Focus(view.loc, view.zoom); - } - } - - uint16_t viewport_flags = 0; - if (w->viewport != nullptr) - { - if (focus == w->focus) - { - return; - } - viewport_flags = w->viewport->flags; - w->RemoveViewport(); - } - else if (gConfigGeneral.AlwaysShowGridlines) - { - viewport_flags |= VIEWPORT_FLAG_GRIDLINES; - } - - WindowEventInvalidateCall(w); - - w->focus = focus; - - // rct2: 0x006aec9c only used here so brought it into the function - if (w->viewport == nullptr && !ride->overall_view.IsNull() && w->focus.has_value()) - { - const auto& view_widget = w->widgets[WIDX_VIEWPORT]; - - auto screenPos = w->windowPos + ScreenCoordsXY{ view_widget.left + 1, view_widget.top + 1 }; - int32_t width = view_widget.width() - 1; - int32_t height = view_widget.height() - 1; - - ViewportCreate(w, screenPos, width, height, w->focus.value()); - - w->flags |= WF_NO_SCROLLING; - w->Invalidate(); - } - if (w->viewport != nullptr) - { - w->viewport->flags = viewport_flags; - w->Invalidate(); - } -} - -/** - * - * rct2: 0x006AF315 - */ -static void WindowRideRename(WindowBase* w) -{ - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - auto rideName = ride->GetName(); - WindowTextInputRawOpen( - w, WIDX_RENAME, STR_RIDE_ATTRACTION_NAME, STR_ENTER_NEW_NAME_FOR_THIS_RIDE_ATTRACTION, {}, rideName.c_str(), 32); - } -} - -/** - * - * rct2: 0x006AF17E - */ -static void WindowRideMainMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_CONSTRUCTION: - { - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - RideConstructionStart(*ride); - if (WindowFindByNumber(WindowClass::RideConstruction, ride->id.ToUnderlying()) != nullptr) - { - WindowClose(*w); - } - } - break; - } - case WIDX_RENAME: - WindowRideRename(w); - break; - case WIDX_DEMOLISH: - ContextOpenDetailWindow(WD_DEMOLISH_RIDE, w->number); - break; - case WIDX_CLOSE_LIGHT: - case WIDX_SIMULATE_LIGHT: - case WIDX_TEST_LIGHT: - case WIDX_OPEN_LIGHT: - { - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - RideStatus status; - switch (widgetIndex) - { - default: - case WIDX_CLOSE_LIGHT: - status = RideStatus::Closed; - break; - case WIDX_SIMULATE_LIGHT: - status = RideStatus::Simulating; - break; - case WIDX_TEST_LIGHT: - status = RideStatus::Testing; - break; - case WIDX_OPEN_LIGHT: - status = RideStatus::Open; - break; - } - auto gameAction = RideSetStatusAction(ride->id, status); - GameActions::Execute(&gameAction); - } - break; - } - } -} - -/** - * - * rct2: 0x006AF4A2 - */ -static void WindowRideMainResize(WindowBase* w) -{ - int32_t minHeight = 180; - if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) - { - minHeight += 20 + RCT1_LIGHT_OFFSET; - - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { -#ifdef __SIMULATE_IN_RIDE_WINDOW__ - if (ride->SupportsStatus(RideStatus::Simulating)) - { - minHeight += 14; - } -#endif - if (ride->SupportsStatus(RideStatus::Testing)) - { - minHeight += 14; - } - } - } - if (gCheatsAllowArbitraryRideTypeChanges) - { - minHeight += 15; - } - - w->flags |= WF_RESIZABLE; - WindowSetResize(*w, 316, minHeight, 500, 450); - // Unlike with other windows, the focus needs to be recentred so it’s best to just reset it. - w->focus = std::nullopt; - WindowRideInitViewport(w); -} - -static size_t GetNumPeepsInTrain(const Ride& ride, int32_t trainIndex) -{ - auto numPeepsInTrain = 0; - const auto* vehicle = TryGetVehicle(ride.vehicles[trainIndex]); - while (vehicle != nullptr) - { - numPeepsInTrain += vehicle->num_peeps; - vehicle = TryGetVehicle(vehicle->next_vehicle_on_train); - } - return numPeepsInTrain; -} - -static bool TrainMustBeHidden(const Ride& ride, int32_t trainIndex) -{ - if (!(ride.lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) - return true; - - const auto* rideEntry = ride.GetRideEntry(); - if (rideEntry == nullptr) - return false; - - if (!(rideEntry->flags & RIDE_ENTRY_FLAG_HIDE_EMPTY_TRAINS)) - return false; - - return GetNumPeepsInTrain(ride, trainIndex) == 0; -} - -/** - * - * rct2: 0x006AF825 - */ -static void WindowRideShowViewDropdown(WindowBase* w, Widget* widget) -{ - Widget* dropdownWidget = widget - 1; - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - const auto& rtd = ride->GetRideTypeDescriptor(); - - int32_t numItems = 1; - if (!rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) - { - numItems += ride->num_stations; - numItems += ride->NumTrains; - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, 0, numItems, widget->right - dropdownWidget->left); - - // First item - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_OVERALL_VIEW; - int32_t currentItem = 1; - - // Vehicles - int32_t name = GetRideComponentName(rtd.NameConvention.vehicle).number; - for (int32_t i = 0; i < ride->NumTrains; i++) - { - gDropdownItems[currentItem].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[currentItem].Args = name | (currentItem << 16); - if (TrainMustBeHidden(*ride, i)) - { - Dropdown::SetDisabled(currentItem, true); - } - currentItem++; - } - - // Stations - name = GetRideComponentName(rtd.NameConvention.station).number; - for (int32_t i = 1; i <= ride->num_stations; i++) - { - gDropdownItems[currentItem].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[currentItem].Args = name | (i << 16); - currentItem++; - } - - // Set checked item - Dropdown::SetChecked(w->ride.view, true); -} - -static RideStatus WindowRideGetNextDefaultStatus(const Ride& ride) -{ - switch (ride.status) - { - default: - case RideStatus::Closed: - if ((ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED) || (ride.lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE)) - { - return RideStatus::Closed; - } - if (ride.SupportsStatus(RideStatus::Testing) && !(ride.lifecycle_flags & RIDE_LIFECYCLE_TESTED)) - { - return RideStatus::Testing; - } - return RideStatus::Open; - case RideStatus::Simulating: - return RideStatus::Testing; - case RideStatus::Testing: - return (ride.lifecycle_flags & RIDE_LIFECYCLE_TESTED) ? RideStatus::Open : RideStatus::Closed; - case RideStatus::Open: - return RideStatus::Closed; - } -} - -struct RideStatusDropdownInfo -{ - struct Ride* Ride{}; - RideStatus CurrentStatus{}; - RideStatus DefaultStatus{}; - - int32_t NumItems{}; - int32_t CheckedIndex = -1; - int32_t DefaultIndex = -1; -}; - -static void WindowRideSetDropdown(RideStatusDropdownInfo& info, RideStatus status, StringId text) -{ - if (info.Ride->SupportsStatus(status)) - { - auto index = info.NumItems; - gDropdownItems[index].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[index].Args = text; - if (info.CurrentStatus == status) - { - info.CheckedIndex = index; - } - if (info.DefaultStatus == status) - { - info.DefaultIndex = index; - } - info.NumItems++; - } -} - -static void WindowRideShowOpenDropdown(WindowBase* w, Widget* widget) -{ - RideStatusDropdownInfo info; - info.Ride = GetRide(w->rideId); - if (info.Ride == nullptr) - return; - - info.CurrentStatus = info.Ride->status; - info.DefaultStatus = WindowRideGetNextDefaultStatus(*info.Ride); - WindowRideSetDropdown(info, RideStatus::Closed, STR_CLOSE_RIDE); -#ifdef __SIMULATE_IN_RIDE_WINDOW__ - window_ride_set_dropdown(info, RideStatus::Simulating, STR_SIMULATE_RIDE); -#endif - WindowRideSetDropdown(info, RideStatus::Testing, STR_TEST_RIDE); - WindowRideSetDropdown(info, RideStatus::Open, STR_OPEN_RIDE); - WindowDropdownShowText( - { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0, info.NumItems); - Dropdown::SetChecked(info.CheckedIndex, true); - gDropdownDefaultIndex = info.DefaultIndex; -} - -static StringId GetRideTypeNameForDropdown(ride_type_t rideType) -{ - switch (rideType) - { - case RIDE_TYPE_1D: - return STR_RIDE_NAME_1D; - case RIDE_TYPE_1F: - return STR_RIDE_NAME_1F; - case RIDE_TYPE_22: - return STR_RIDE_NAME_22; - case RIDE_TYPE_50: - return STR_RIDE_NAME_50; - case RIDE_TYPE_52: - return STR_RIDE_NAME_52; - case RIDE_TYPE_53: - return STR_RIDE_NAME_53; - case RIDE_TYPE_54: - return STR_RIDE_NAME_54; - case RIDE_TYPE_55: - return STR_RIDE_NAME_55; - case RIDE_TYPE_59: - return STR_RIDE_NAME_59; - default: - return GetRideTypeDescriptor(rideType).Naming.Name; - } -} - -static void PopulateRideTypeDropdown() -{ - auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); - if (RideDropdownDataLanguage == ls.GetCurrentLanguage()) - return; - - RideDropdownData.clear(); - - for (uint8_t i = 0; i < RIDE_TYPE_COUNT; i++) - { - auto name = GetRideTypeNameForDropdown(i); - RideDropdownData.push_back({ i, name, ls.GetString(name) }); - } - - std::sort(RideDropdownData.begin(), RideDropdownData.end(), [](auto& a, auto& b) { - return String::Compare(a.label_string, b.label_string, true) < 0; - }); - - RideDropdownDataLanguage = ls.GetCurrentLanguage(); -} - -static void WindowRideShowRideTypeDropdown(WindowBase* w, Widget* widget) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - PopulateRideTypeDropdown(); - - for (size_t i = 0; i < RideDropdownData.size(); i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = RideDropdownData[i].label_id; - } - - Widget* dropdownWidget = widget - 1; - WindowDropdownShowText( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], Dropdown::Flag::StayOpen, RIDE_TYPE_COUNT); - - // Find the current ride type in the ordered list. - int32_t pos = 0; - for (int32_t i = 0; i < RIDE_TYPE_COUNT; i++) - { - if (RideDropdownData[i].ride_type_id == ride->type) - { - pos = i; - break; - } - } - - gDropdownHighlightedIndex = pos; - gDropdownDefaultIndex = pos; - Dropdown::SetChecked(pos, true); -} - -static void WindowRideShowLocateDropdown(WindowBase* w, Widget* widget) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - gDropdownItems[0].Format = STR_LOCATE_SUBJECT_TIP; - gDropdownItems[1].Format = STR_FOLLOW_SUBJECT_TIP; - - WindowDropdownShowText( - { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0, 2); - gDropdownDefaultIndex = 0; - if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK) || w->ride.view == 0 || w->ride.view > ride->NumTrains) - { - // Disable if we're a flat ride, 'overall view' is selected or a station is selected - Dropdown::SetDisabled(1, true); - } -} - -static void WindowRideMainFollowRide(WindowBase* w) -{ - auto* ride = GetRide(w->rideId); - if (ride != nullptr) - { - if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) - { - if (w->ride.view > 0) - { - if (w->ride.view <= ride->NumTrains) - { - Vehicle* vehicle = GetEntity(ride->vehicles[w->ride.view - 1]); - if (vehicle != nullptr) - { - auto headVehicleSpriteIndex = vehicle->Id; - WindowBase* w_main = WindowGetMain(); - WindowFollowSprite(*w_main, headVehicleSpriteIndex); - } - } - } - } - } -} - -static void PopulateVehicleTypeDropdown(const Ride& ride, bool forceRefresh) -{ - auto& objManager = GetContext()->GetObjectManager(); - const auto* rideEntry = ride.GetRideEntry(); - - bool selectionShouldBeExpanded; - int32_t rideTypeIterator, rideTypeIteratorMax; - - const auto& rtd = ride.GetRideTypeDescriptor(); - if (gCheatsShowVehiclesFromOtherTrackTypes - && !(rtd.HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE) || rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE) || ride.type == RIDE_TYPE_MINI_GOLF)) - { - selectionShouldBeExpanded = true; - rideTypeIterator = 0; - rideTypeIteratorMax = RIDE_TYPE_COUNT - 1; - } - else - { - selectionShouldBeExpanded = false; - rideTypeIterator = ride.type; - rideTypeIteratorMax = ride.type; - } - - // Don't repopulate the list if we just did. - auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); - if (!forceRefresh && VehicleDropdownExpanded == selectionShouldBeExpanded && VehicleDropdownRideType == rideEntry - && VehicleDropdownDataLanguage == ls.GetCurrentLanguage()) - return; - - VehicleDropdownData.clear(); - - for (; rideTypeIterator <= rideTypeIteratorMax; rideTypeIterator++) - { - const auto& rtdIterator = GetRideTypeDescriptor(rideTypeIterator); - if (selectionShouldBeExpanded && rtdIterator.HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE)) - continue; - if (selectionShouldBeExpanded - && (rtdIterator.HasFlag(RIDE_TYPE_FLAG_IS_MAZE) || rideTypeIterator == RIDE_TYPE_MINI_GOLF)) - continue; - - auto& rideEntries = objManager.GetAllRideEntries(rideTypeIterator); - for (auto rideEntryIndex : rideEntries) - { - const auto* currentRideEntry = GetRideEntryByIndex(rideEntryIndex); - if (currentRideEntry == nullptr) - continue; - - // Skip if vehicle type has not been invented yet - if (!RideEntryIsInvented(rideEntryIndex) && !gCheatsIgnoreResearchStatus) - continue; - - VehicleDropdownData.push_back( - { rideEntryIndex, currentRideEntry->naming.Name, ls.GetString(currentRideEntry->naming.Name) }); - } - } - - std::sort(VehicleDropdownData.begin(), VehicleDropdownData.end(), [](auto& a, auto& b) { - return String::Compare(a.label_string, b.label_string, true) < 0; - }); - - VehicleDropdownExpanded = selectionShouldBeExpanded; - VehicleDropdownRideType = rideEntry; - VehicleDropdownDataLanguage = ls.GetCurrentLanguage(); -} - -static void WindowRideShowVehicleTypeDropdown(WindowBase* w, Widget* widget) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - PopulateVehicleTypeDropdown(*ride); - - size_t numItems = std::min(VehicleDropdownData.size(), Dropdown::ItemsMaxSize); - - for (size_t i = 0; i < numItems; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = VehicleDropdownData[i].label_id; - } - - Widget* dropdownWidget = widget - 1; - auto width = WindowDropDownHasMultipleColumns(numItems) ? dropdownWidget->width() - 24 : dropdownWidget->width(); - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, width); - - // Find the current vehicle type in the ordered list. - int32_t pos = 0; - for (int32_t i = 0; i < static_cast(VehicleDropdownData.size()); i++) - { - if (VehicleDropdownData[i].subtype_id == ride->subtype) - { - pos = i; - break; - } - } - - gDropdownHighlightedIndex = pos; - gDropdownDefaultIndex = pos; - Dropdown::SetChecked(pos, true); -} - -/** - * - * rct2: 0x006AF1BD - */ -static void WindowRideMainMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - switch (widgetIndex) - { - case WIDX_VIEW_DROPDOWN: - WindowRideShowViewDropdown(w, widget); - break; - case WIDX_OPEN: - WindowRideShowOpenDropdown(w, widget); - break; - case WIDX_RIDE_TYPE_DROPDOWN: - WindowRideShowRideTypeDropdown(w, widget); - break; - case WIDX_LOCATE: - WindowRideShowLocateDropdown(w, widget); - break; - } -} - -/** - * - * rct2: 0x006AF300 - */ -static void WindowRideMainDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - switch (widgetIndex) - { - case WIDX_VIEW_DROPDOWN: - if (dropdownIndex == -1) - { - dropdownIndex = w->ride.view + 1; - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - if (dropdownIndex != 0 && dropdownIndex <= ride->NumTrains - && !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) - { - dropdownIndex = ride->NumTrains + 1; - } - if (dropdownIndex >= gDropdownNumItems) - { - dropdownIndex = 0; - } - } - } - - w->ride.view = dropdownIndex; - WindowRideInitViewport(w); - w->Invalidate(); - break; - case WIDX_OPEN: - { - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - auto status = RideStatus::Closed; - if (dropdownIndex < 0) - { - dropdownIndex = gDropdownHighlightedIndex; - } - if (dropdownIndex < static_cast(std::size(gDropdownItems))) - { - switch (gDropdownItems[dropdownIndex].Args) - { - case STR_CLOSE_RIDE: - status = RideStatus::Closed; - break; - case STR_SIMULATE_RIDE: - status = RideStatus::Simulating; - break; - case STR_TEST_RIDE: - status = RideStatus::Testing; - break; - case STR_OPEN_RIDE: - status = RideStatus::Open; - break; - } - } - auto gameAction = RideSetStatusAction(ride->id, status); - GameActions::Execute(&gameAction); - } - break; - } - case WIDX_RIDE_TYPE_DROPDOWN: - if (dropdownIndex != -1 && dropdownIndex < RIDE_TYPE_COUNT) - { - auto rideLabelId = std::clamp(dropdownIndex, 0, RIDE_TYPE_COUNT - 1); - auto rideType = RideDropdownData[rideLabelId].ride_type_id; - if (rideType < RIDE_TYPE_COUNT) - { - auto rideSetSetting = RideSetSettingAction(w->rideId, RideSetSetting::RideType, rideType); - rideSetSetting.SetCallback([](const GameAction* ga, const GameActions::Result* result) { - // Reset ghost track if ride construction window is open, prevents a crash - // Will get set to the correct Alternative variable during set_default_next_piece. - // TODO: Rework construction window to prevent the need for this. - _currentTrackAlternative = RIDE_TYPE_NO_ALTERNATIVES; - RideConstructionSetDefaultNextPiece(); - }); - GameActions::Execute(&rideSetSetting); - } - } - break; - case WIDX_LOCATE: - { - if (dropdownIndex == 0) - { - w->ScrollToViewport(); - } - else if (dropdownIndex == 1) - { - WindowRideMainFollowRide(w); - } - break; - } - } -} - -/** - * - * rct2: 0x006AF40F - */ -static void WindowRideMainUpdate(WindowBase* w) -{ - // Update tab animation - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_1); - - // Update status - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) - { - if (w->ride.view == 0) - return; - - if (w->ride.view <= ride->NumTrains) - { - Vehicle* vehicle = GetEntity(ride->vehicles[w->ride.view - 1]); - if (vehicle == nullptr - || (vehicle->status != Vehicle::Status::Travelling - && vehicle->status != Vehicle::Status::TravellingCableLift - && vehicle->status != Vehicle::Status::TravellingDodgems - && vehicle->status != Vehicle::Status::TravellingBoat)) - { - return; - } - } - } - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAIN; - } - WidgetInvalidate(*w, WIDX_STATUS); -} - -/** - * - * rct2: 0x006AF2F9 - */ -static void WindowRideMainTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) -{ - if (widgetIndex != WIDX_RENAME || text == nullptr) - return; - - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - auto gameAction = RideSetNameAction(ride->id, text); - GameActions::Execute(&gameAction); - } -} - -/** - * - * rct2: 0x006AF55A - */ -static void WindowRideMainViewportRotate(WindowBase* w) -{ - WindowRideInitViewport(w); -} - -/** - * - * rct2: 0x006AECF6 - */ -static void WindowRideMainInvalidate(WindowBase* w) -{ - Widget* widgets; - int32_t i, height; - - widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - w->disabled_widgets &= ~((1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_CONSTRUCTION)); - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_INDESTRUCTIBLE | RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) - && !gCheatsMakeAllDestructible) - w->disabled_widgets |= (1uLL << WIDX_DEMOLISH); - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - uint32_t spriteIds[] = { - SPR_CLOSED, - SPR_OPEN, - SPR_TESTING, - SPR_G2_SIMULATE, - }; - window_ride_main_widgets[WIDX_OPEN].image = ImageId(spriteIds[EnumValue(ride->status)]); - -#ifdef __SIMULATE_IN_RIDE_WINDOW__ - window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2 - + WidgetIsPressed(*w, WIDX_CLOSE_LIGHT); - window_ride_main_widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0 - + (ride->status == RideStatus::Simulating) * 2 + WidgetIsPressed(*w, WIDX_SIMULATE_LIGHT); - window_ride_main_widgets[WIDX_TEST_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0 + (ride->status == RideStatus::Testing) * 2 - + WidgetIsPressed(*w, WIDX_TEST_LIGHT); -#else - const auto closeLightImage = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2 - + WidgetIsPressed(*w, WIDX_CLOSE_LIGHT); - window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = ImageId(closeLightImage); - - auto baseSprite = ride->status == RideStatus::Simulating ? SPR_G2_RCT1_SIMULATE_BUTTON_0 : SPR_G2_RCT1_TEST_BUTTON_0; - const auto testLightImage = baseSprite + (ride->status == RideStatus::Testing || ride->status == RideStatus::Simulating) * 2 - + WidgetIsPressed(*w, WIDX_TEST_LIGHT); - window_ride_main_widgets[WIDX_TEST_LIGHT].image = ImageId(testLightImage); -#endif - const auto openLightImage = SPR_G2_RCT1_OPEN_BUTTON_0 + (ride->status == RideStatus::Open) * 2 - + WidgetIsPressed(*w, WIDX_OPEN_LIGHT); - window_ride_main_widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); - - WindowRideAnchorBorderWidgets(w); - - const int32_t offset = gCheatsAllowArbitraryRideTypeChanges ? 15 : 0; - // Anchor main page specific widgets - window_ride_main_widgets[WIDX_VIEWPORT].right = w->width - 26; - window_ride_main_widgets[WIDX_VIEWPORT].bottom = w->height - (14 + offset); - window_ride_main_widgets[WIDX_STATUS].right = w->width - 26; - window_ride_main_widgets[WIDX_STATUS].top = w->height - (13 + offset); - window_ride_main_widgets[WIDX_STATUS].bottom = w->height - (3 + offset); - window_ride_main_widgets[WIDX_VIEW].right = w->width - 60; - window_ride_main_widgets[WIDX_VIEW_DROPDOWN].right = w->width - 61; - window_ride_main_widgets[WIDX_VIEW_DROPDOWN].left = w->width - 71; - window_ride_main_widgets[WIDX_RIDE_TYPE].right = w->width - 26; - window_ride_main_widgets[WIDX_RIDE_TYPE].top = w->height - 17; - window_ride_main_widgets[WIDX_RIDE_TYPE].bottom = w->height - 4; - window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].left = w->width - 37; - window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].right = w->width - 27; - window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].top = w->height - 16; - window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].bottom = w->height - 5; - - if (!gCheatsAllowArbitraryRideTypeChanges) - { - window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::Empty; - window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Empty; - } - else - { - window_ride_main_widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::DropdownMenu; - window_ride_main_widgets[WIDX_RIDE_TYPE].text = ride->GetRideTypeDescriptor().Naming.Name; - window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Button; - } - - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); - - if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) - { - window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::Empty; - window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; - window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; -#ifdef __SIMULATE_IN_RIDE_WINDOW__ - if (ride->SupportsStatus(RideStatus::Simulating)) - window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::ImgBtn; -#endif - window_ride_main_widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RideStatus::Testing) ? WindowWidgetType::ImgBtn - : WindowWidgetType::Empty; - window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; - - height = 62; - if (window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type != WindowWidgetType::Empty) - { - window_ride_main_widgets[WIDX_SIMULATE_LIGHT].top = height; - window_ride_main_widgets[WIDX_SIMULATE_LIGHT].bottom = height + 13; - height += 14; - } - if (window_ride_main_widgets[WIDX_TEST_LIGHT].type != WindowWidgetType::Empty) - { - window_ride_main_widgets[WIDX_TEST_LIGHT].top = height; - window_ride_main_widgets[WIDX_TEST_LIGHT].bottom = height + 13; - height += 14; - } - window_ride_main_widgets[WIDX_OPEN_LIGHT].top = height; - window_ride_main_widgets[WIDX_OPEN_LIGHT].bottom = height + 13; - height += 14 - 24 + RCT1_LIGHT_OFFSET; - } - else - { - window_ride_main_widgets[WIDX_OPEN].type = WindowWidgetType::FlatBtn; - window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; - window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; - window_ride_main_widgets[WIDX_TEST_LIGHT].type = WindowWidgetType::Empty; - window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; - height = 46; - } - for (i = WIDX_CLOSE_LIGHT; i <= WIDX_OPEN_LIGHT; i++) - { - window_ride_main_widgets[i].left = w->width - 20; - window_ride_main_widgets[i].right = w->width - 7; - } - for (i = WIDX_OPEN; i <= WIDX_DEMOLISH; i++, height += 24) - { - window_ride_main_widgets[i].left = w->width - 25; - window_ride_main_widgets[i].right = w->width - 2; - window_ride_main_widgets[i].top = height; - window_ride_main_widgets[i].bottom = height + 23; - } -} - -/** - * - * rct2: 0x006AF10A - */ -static StringId WindowRideGetStatusOverallView(WindowBase* w, Formatter& ft) -{ - auto stringId = STR_NONE; - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - ride->FormatStatusTo(ft); - stringId = STR_BLACK_STRING; - if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) - { - stringId = STR_RED_OUTLINED_STRING; - } - } - return stringId; -} - -/** - * - * rct2: 0x006AEFEF - */ -static StringId WindowRideGetStatusVehicle(WindowBase* w, Formatter& ft) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return STR_EMPTY; - - auto vehicle = GetEntity(ride->vehicles[w->ride.view - 1]); - if (vehicle == nullptr) - return STR_EMPTY; - - if (vehicle->status != Vehicle::Status::Crashing && vehicle->status != Vehicle::Status::Crashed) - { - auto trackType = vehicle->GetTrackType(); - if (trackType == TrackElemType::BlockBrakes || trackType == TrackElemType::CableLiftHill - || trackType == TrackElemType::Up25ToFlat || trackType == TrackElemType::Up60ToFlat - || trackType == TrackElemType::DiagUp25ToFlat || trackType == TrackElemType::DiagUp60ToFlat) - { - if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_BLOCK_BRAKES) && vehicle->velocity == 0) - { - ft.Add(STR_STOPPED_BY_BLOCK_BRAKES); - return STR_BLACK_STRING; - } - } - } - - if (ride->type == RIDE_TYPE_MINI_GOLF) - return STR_EMPTY; - - auto stringId = VehicleStatusNames[static_cast(vehicle->status)]; - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SINGLE_SESSION) - && vehicle->status <= Vehicle::Status::UnloadingPassengers) - { - stringId = SingleSessionVehicleStatusNames[static_cast(vehicle->status)]; - } - - ft.Add(stringId); - uint16_t speedInMph = (abs(vehicle->velocity) * 9) >> 18; - ft.Add(speedInMph); - const RideComponentName stationName = GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station); - ft.Add(ride->num_stations > 1 ? stationName.number : stationName.singular); - ft.Add(vehicle->current_station.ToUnderlying() + 1); - return stringId != STR_CRASHING && stringId != STR_CRASHED_0 ? STR_BLACK_STRING : STR_RED_OUTLINED_STRING; -} - -/** - * - * rct2: 0x006AEF65 - */ -static StringId WindowRideGetStatusStation(WindowBase* w, Formatter& ft) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return STR_NONE; - - const auto stationIndex = GetStationIndexFromViewSelection(*w); - if (!stationIndex) - { - return STR_NONE; - } - - const auto& station = ride->GetStation(*stationIndex); - StringId stringId = STR_EMPTY; - // Entrance / exit - if (ride->status == RideStatus::Closed) - { - if (station.Entrance.IsNull()) - stringId = STR_NO_ENTRANCE; - else if (station.Exit.IsNull()) - stringId = STR_NO_EXIT; - } - else - { - if (station.Entrance.IsNull()) - stringId = STR_EXIT_ONLY; - } - // Queue length - if (stringId == STR_EMPTY) - { - stringId = STR_QUEUE_EMPTY; - uint16_t queueLength = ride->GetStation(*stationIndex).QueueLength; - if (queueLength == 1) - stringId = STR_QUEUE_ONE_PERSON; - else if (queueLength > 1) - stringId = STR_QUEUE_PEOPLE; - - ft.Add(stringId); - ft.Add(queueLength); - } - else - { - ft.Add(stringId); - } - - return STR_BLACK_STRING; -} - -/** - * - * rct2: 0x006AEE73 - */ -static StringId WindowRideGetStatus(WindowBase* w, Formatter& ft) -{ - auto ride = GetRide(w->rideId); - if (w->ride.view == 0) - return WindowRideGetStatusOverallView(w, ft); - if (ride != nullptr && w->ride.view <= ride->NumTrains) - return WindowRideGetStatusVehicle(w, ft); - if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) - return WindowRideGetStatusOverallView(w, ft); - return WindowRideGetStatusStation(w, ft); -} - -/** - * - * rct2: 0x006AEE73 - */ -static void WindowRideMainPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - Widget* widget; - - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - // Viewport and ear icon - if (w->viewport != nullptr) - { - WindowDrawViewport(dpi, *w); - if (w->viewport->flags & VIEWPORT_FLAG_SOUND_ON) - GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*w)); - } - - // View dropdown - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto ft = Formatter(); - if (w->ride.view != 0) - { - if (w->ride.view > ride->NumTrains) - { - ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station).number); - ft.Add(w->ride.view - ride->NumTrains); - } - else - { - ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number); - ft.Add(w->ride.view); - } - } - else - { - ft.Add(STR_OVERALL_VIEW); - } - - widget = &window_ride_main_widgets[WIDX_VIEW]; - DrawTextBasic( - dpi, { w->windowPos.x + (widget->left + widget->right - 11) / 2, w->windowPos.y + widget->top }, - STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::CENTRE }); - - // Status - ft = Formatter(); - widget = &window_ride_main_widgets[WIDX_STATUS]; - StringId rideStatus = WindowRideGetStatus(w, ft); - DrawTextEllipsised( - dpi, w->windowPos + ScreenCoordsXY{ (widget->left + widget->right) / 2, widget->top }, widget->width(), rideStatus, ft, - { TextAlignment::CENTRE }); -} - -#pragma endregion - -#pragma region Vehicle - -/** - * - * rct2: 0x006B272D - */ -static void WindowRideVehicleMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - } -} - -/** - * - * rct2: 0x006B2ABB - */ -static void WindowRideVehicleResize(WindowBase* w) -{ - WindowSetResize(*w, 316, 221, 316, 221); -} - -/** - * - * rct2: 0x006B2748 - */ -static void WindowRideVehicleMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - switch (widgetIndex) - { - case WIDX_VEHICLE_TYPE_DROPDOWN: - WindowRideShowVehicleTypeDropdown(w, &w->widgets[widgetIndex]); - break; - case WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX: - ride->SetReversedTrains(!ride->HasLifecycleFlag(RIDE_LIFECYCLE_REVERSED_TRAINS)); - break; - case WIDX_VEHICLE_TRAINS_INCREASE: - if (ride->NumTrains < OpenRCT2::Limits::MaxTrainsPerRide) - ride->SetNumTrains(ride->NumTrains + 1); - break; - case WIDX_VEHICLE_TRAINS_DECREASE: - if (ride->NumTrains > 1) - ride->SetNumTrains(ride->NumTrains - 1); - break; - case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE: - if (ride->num_cars_per_train < OpenRCT2::Limits::MaxCarsPerTrain) - ride->SetNumCarsPerVehicle(ride->num_cars_per_train + 1); - break; - case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE: - if (ride->num_cars_per_train > 1) - ride->SetNumCarsPerVehicle(ride->num_cars_per_train - 1); - break; - } -} - -/** - * - * rct2: 0x006B2767 - */ -static void WindowRideVehicleDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (dropdownIndex == -1) - return; - - switch (widgetIndex) - { - case WIDX_VEHICLE_TYPE_DROPDOWN: - if (dropdownIndex >= 0 && static_cast(dropdownIndex) < VehicleDropdownData.size()) - { - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - auto newRideType = VehicleDropdownData[dropdownIndex].subtype_id; - ride->SetRideEntry(newRideType); - } - } - break; - } -} - -/** - * - * rct2: 0x006B2AA1 - */ -static void WindowRideVehicleUpdate(WindowBase* w) -{ - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_2); -} - -static OpenRCT2String WindowRideVehicleTooltip(WindowBase* const w, const WidgetIndex widgetIndex, StringId fallback) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return { STR_NONE, {} }; - - switch (widgetIndex) - { - case WIDX_VEHICLE_TRAINS: - case WIDX_VEHICLE_TRAINS_DECREASE: - case WIDX_VEHICLE_TRAINS_INCREASE: - { - auto ft = Formatter(); - ft.Increment(12); - - RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle; - StringId stringId = GetRideComponentName(vehicleType).count; - if (ride->max_trains > 1) - { - stringId = GetRideComponentName(vehicleType).count_plural; - } - ft.Add(stringId); - ft.Add(ride->max_trains); - return { fallback, ft }; - } - case WIDX_VEHICLE_CARS_PER_TRAIN: - case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE: - case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE: - { - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return { STR_NONE, {} }; - - auto ft = Formatter(); - ft.Increment(16); - ft.Add(std::max(uint8_t(1), ride->MaxCarsPerTrain) - rideEntry->zero_cars); - - StringId stringId = GetRideComponentName(RideComponentType::Car).singular; - if (ride->MaxCarsPerTrain - rideEntry->zero_cars > 1) - { - stringId = GetRideComponentName(RideComponentType::Car).plural; - } - ft.Add(stringId); - return { fallback, ft }; - } - } - return { fallback, {} }; -} - -/** - * - * rct2: 0x006B222C - */ -static void WindowRideVehicleInvalidate(WindowBase* w) -{ - Widget* widgets; - StringId stringId; - int32_t carsPerTrain; - - widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - const auto* rideEntry = ride->GetRideEntry(); - - w->widgets[WIDX_TITLE].text = STR_ARG_20_STRINGID; - - // Widget setup - carsPerTrain = ride->num_cars_per_train - rideEntry->zero_cars; - - // Vehicle type - window_ride_vehicle_widgets[WIDX_VEHICLE_TYPE].text = rideEntry->naming.Name; - - // Trains - if (rideEntry->cars_per_flat_ride > 1 || gCheatsDisableTrainLengthLimit) - { - window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Spinner; - window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Button; - window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Button; - } - else - { - window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Empty; - window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Empty; - window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Empty; - } - - // Cars per train - if (rideEntry->zero_cars + 1 < rideEntry->max_cars_in_train || gCheatsDisableTrainLengthLimit) - { - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Spinner; - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Button; - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Button; - } - else - { - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Empty; - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Empty; - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Empty; - } - - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_ALLOW_REVERSED_TRAINS) - || (gCheatsDisableTrainLengthLimit && !ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))) - { - window_ride_vehicle_widgets[WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX].type = WindowWidgetType::Checkbox; - if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_REVERSED_TRAINS)) - { - w->pressed_widgets |= (1uLL << WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX); - } - else - { - w->pressed_widgets &= ~(1uLL << WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX); - } - } - else - { - window_ride_vehicle_widgets[WIDX_VEHICLE_REVERSED_TRAINS_CHECKBOX].type = WindowWidgetType::Empty; - } - - auto ft = Formatter::Common(); - ft.Increment(6); - ft.Add(carsPerTrain); - RideComponentType vehicleType = ride->GetRideTypeDescriptor().NameConvention.vehicle; - stringId = GetRideComponentName(vehicleType).count; - if (ride->NumTrains > 1) - { - stringId = GetRideComponentName(vehicleType).count_plural; - } - ft.Add(stringId); - ft.Add(ride->NumTrains); - - ft.Increment(8); - - ride->FormatNameTo(ft); - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); - - if (abs(ride->num_cars_per_train - rideEntry->zero_cars) == 1) - { - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_1_CAR_PER_TRAIN; - } - else - { - window_ride_vehicle_widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_X_CARS_PER_TRAIN; - } -} - -/** - * - * rct2: 0x006B23DC - */ -static void WindowRideVehiclePaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - auto screenCoords = w->windowPos + ScreenCoordsXY{ 8, 64 }; - - // Description - auto ft = Formatter(); - ft.Add(rideEntry->naming.Description); - screenCoords.y += DrawTextWrapped(dpi, screenCoords, 300, STR_BLACK_STRING, ft, { TextAlignment::LEFT }); - screenCoords.y += 2; - - // Capacity - ft = Formatter(); - ft.Add(rideEntry->capacity); - DrawTextBasic(dpi, screenCoords, STR_CAPACITY, ft); - - // Excitement Factor - if (rideEntry->excitement_multiplier != 0) - { - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(abs(rideEntry->excitement_multiplier)); - StringId stringId = rideEntry->excitement_multiplier > 0 ? STR_EXCITEMENT_FACTOR : STR_EXCITEMENT_FACTOR_NEGATIVE; - DrawTextBasic(dpi, screenCoords, stringId, ft); - } - - // Intensity Factor - if (rideEntry->intensity_multiplier != 0) - { - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - if (lineHeight != 10) - screenCoords.x += 150; - else - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(abs(rideEntry->intensity_multiplier)); - StringId stringId = rideEntry->intensity_multiplier > 0 ? STR_INTENSITY_FACTOR : STR_INTENSITY_FACTOR_NEGATIVE; - DrawTextBasic(dpi, screenCoords, stringId, ft); - - if (lineHeight != 10) - screenCoords.x -= 150; - } - - // Nausea Factor - if (rideEntry->nausea_multiplier != 0) - { - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(abs(rideEntry->nausea_multiplier)); - StringId stringId = rideEntry->nausea_multiplier > 0 ? STR_NAUSEA_FACTOR : STR_NAUSEA_FACTOR_NEGATIVE; - DrawTextBasic(dpi, screenCoords, stringId, ft); - } -} - -struct VehicleDrawInfo -{ - int16_t x; - int16_t y; - ImageId imageId; -}; - -/** - * - * rct2: 0x006B2502 - */ -static void WindowRideVehicleScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - const auto* rideEntry = ride->GetRideEntry(); - - // Background - GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } }, PALETTE_INDEX_12); - - Widget* widget = &window_ride_vehicle_widgets[WIDX_VEHICLE_TRAINS_PREVIEW]; - int32_t startX = std::max(2, (widget->width() - ((ride->NumTrains - 1) * 36)) / 2 - 25); - int32_t startY = widget->height() - 4; - - bool isReversed = ride->HasLifecycleFlag(RIDE_LIFECYCLE_REVERSED_TRAINS); - int32_t carIndex = (isReversed) ? ride->num_cars_per_train - 1 : 0; - - const auto& firstCarEntry = rideEntry - ->Cars[RideEntryGetVehicleAtPosition(ride->subtype, ride->num_cars_per_train, carIndex)]; - startY += firstCarEntry.tab_height; - - // For each train - for (int32_t i = 0; i < ride->NumTrains; i++) - { - VehicleDrawInfo trainCarImages[OpenRCT2::Limits::MaxCarsPerTrain]; - VehicleDrawInfo* nextSpriteToDraw = trainCarImages; - int32_t x = startX; - int32_t y = startY; - - // For each car in train - static_assert(std::numeric_limitsnum_cars_per_train)>::max() <= std::size(trainCarImages)); - for (int32_t j = 0; j < ride->num_cars_per_train; j++) - { - carIndex = (isReversed) ? (ride->num_cars_per_train - 1) - j : j; - - const auto& carEntry = rideEntry - ->Cars[RideEntryGetVehicleAtPosition(ride->subtype, ride->num_cars_per_train, carIndex)]; - x += carEntry.spacing / 17432; - y -= (carEntry.spacing / 2) / 17432; - - // Get colour of vehicle - int32_t vehicleColourIndex = 0; - switch (ride->colour_scheme_type & 3) - { - case VEHICLE_COLOUR_SCHEME_SAME: - vehicleColourIndex = 0; - break; - case VEHICLE_COLOUR_SCHEME_PER_TRAIN: - vehicleColourIndex = i; - break; - case VEHICLE_COLOUR_SCHEME_PER_VEHICLE: - vehicleColourIndex = carIndex; - break; - } - VehicleColour vehicleColour = RideGetVehicleColour(*ride, vehicleColourIndex); - - ImageIndex imageIndex = carEntry.SpriteByYaw(OpenRCT2::Entity::Yaw::BaseRotation / 2, SpriteGroupType::SlopeFlat); - if (isReversed) - { - auto baseRotation = carEntry.NumRotationSprites(SpriteGroupType::SlopeFlat); - imageIndex = carEntry.SpriteByYaw( - (imageIndex + (baseRotation / 2)) & (baseRotation - 1), SpriteGroupType::SlopeFlat); - } - - imageIndex &= carEntry.TabRotationMask; - imageIndex *= carEntry.base_num_frames; - imageIndex += carEntry.base_image_id; - - auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary); - - nextSpriteToDraw->x = x; - nextSpriteToDraw->y = y; - nextSpriteToDraw->imageId = imageId; - nextSpriteToDraw++; - - x += carEntry.spacing / 17432; - y -= (carEntry.spacing / 2) / 17432; - } - - if (ride->type == RIDE_TYPE_REVERSER_ROLLER_COASTER) - { - VehicleDrawInfo tmp = *(nextSpriteToDraw - 1); - *(nextSpriteToDraw - 1) = *(nextSpriteToDraw - 2); - *(nextSpriteToDraw - 2) = tmp; - } - - VehicleDrawInfo* current = nextSpriteToDraw; - while (--current >= trainCarImages) - GfxDrawSprite(dpi, current->imageId, { current->x, current->y }); - - startX += 36; - } -} - -#pragma endregion - -#pragma region Operating - -/** - * - * rct2: 0x006B11D5 - */ -static void WindowRideModeTweakIncrease(WindowBase* w) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; - uint8_t maxValue = operatingSettings.MaxValue; - uint8_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; - - if (gCheatsUnlockOperatingLimits) - { - maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; - } - - uint8_t increment = ride->mode == RideMode::Dodgems ? 10 : 1; - - SetOperatingSetting( - w->rideId, RideSetSetting::Operation, std::clamp(ride->operation_option + increment, minValue, maxValue)); -} - -/** - * - * rct2: 0x006B120A - */ -static void WindowRideModeTweakDecrease(WindowBase* w) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; - uint8_t maxValue = operatingSettings.MaxValue; - uint8_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; - if (gCheatsUnlockOperatingLimits) - { - maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; - } - - uint8_t decrement = ride->mode == RideMode::Dodgems ? 10 : 1; - - SetOperatingSetting( - w->rideId, RideSetSetting::Operation, std::clamp(ride->operation_option - decrement, minValue, maxValue)); -} - -/** - * - * rct2: 0x006B1631 - */ -static void WindowRideModeDropdown(WindowBase* w, Widget* widget) -{ - Widget* dropdownWidget; - - dropdownWidget = widget - 1; - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto availableModes = ride->GetAvailableModes(); - - // Create dropdown list - auto numAvailableModes = 0; - auto checkedIndex = -1; - for (auto i = 0; i < static_cast(RideMode::Count); i++) - { - if (availableModes & (1uLL << i)) - { - gDropdownItems[numAvailableModes].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numAvailableModes].Args = RideModeNames[i]; - - if (ride->mode == static_cast(i)) - checkedIndex = numAvailableModes; - - numAvailableModes++; - } - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, numAvailableModes, widget->right - dropdownWidget->left); - - if (checkedIndex != -1) - { - Dropdown::SetChecked(checkedIndex, true); - } -} - -/** - * - * rct2: 0x006B15C0 - */ -static void WindowRideLoadDropdown(WindowBase* w, Widget* widget) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto dropdownWidget = widget - 1; - for (auto i = 0; i < 5; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = VehicleLoadNames[i]; - } - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, 5, widget->right - dropdownWidget->left); - - Dropdown::SetChecked(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK, true); -} - -/** - * - * rct2: 0x006B10A7 - */ -static void WindowRideOperatingMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - const auto rideId = w->rideId; - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_LOAD_CHECKBOX: - SetOperatingSetting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_LOAD); - break; - case WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX: - SetOperatingSetting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES); - break; - case WIDX_MINIMUM_LENGTH_CHECKBOX: - SetOperatingSetting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH); - break; - case WIDX_MAXIMUM_LENGTH_CHECKBOX: - SetOperatingSetting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH); - break; - case WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX: - SetOperatingSetting( - rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS); - break; - } -} - -/** - * - * rct2: 0x006B1715 - */ -static void WindowRideOperatingResize(WindowBase* w) -{ - WindowSetResize(*w, 316, 186, 316, 186); -} - -/** - * - * rct2: 0x006B10F4 - */ -static void WindowRideOperatingMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - const auto rideId = w->rideId; - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - uint8_t upper_bound, lower_bound; - switch (widgetIndex) - { - case WIDX_MODE_TWEAK: - WindowRideOperatingTweakTextInput(w, *ride); - break; - case WIDX_MODE_TWEAK_INCREASE: - WindowRideModeTweakIncrease(w); - break; - case WIDX_MODE_TWEAK_DECREASE: - WindowRideModeTweakDecrease(w); - break; - case WIDX_LIFT_HILL_SPEED_INCREASE: - upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : ride->GetRideTypeDescriptor().LiftData.maximum_speed; - lower_bound = gCheatsUnlockOperatingLimits ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed; - SetOperatingSetting( - rideId, RideSetSetting::LiftHillSpeed, - std::clamp(ride->lift_hill_speed + 1, lower_bound, upper_bound)); - break; - case WIDX_LIFT_HILL_SPEED_DECREASE: - upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : ride->GetRideTypeDescriptor().LiftData.maximum_speed; - lower_bound = gCheatsUnlockOperatingLimits ? 0 : ride->GetRideTypeDescriptor().LiftData.minimum_speed; - SetOperatingSetting( - rideId, RideSetSetting::LiftHillSpeed, - std::clamp(ride->lift_hill_speed - 1, lower_bound, upper_bound)); - break; - case WIDX_MINIMUM_LENGTH: - WindowRideOperatingLengthWindow(w, WIDX_MINIMUM_LENGTH); - break; - case WIDX_MAXIMUM_LENGTH: - WindowRideOperatingLengthWindow(w, WIDX_MAXIMUM_LENGTH); - break; - case WIDX_MINIMUM_LENGTH_INCREASE: - upper_bound = OpenRCT2::Limits::MaxWaitingTime; - lower_bound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MinWaitingTime, - std::clamp(ride->min_waiting_time + 1, lower_bound, upper_bound)); - break; - case WIDX_MINIMUM_LENGTH_DECREASE: - upper_bound = OpenRCT2::Limits::MaxWaitingTime; - lower_bound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MinWaitingTime, - std::clamp(ride->min_waiting_time - 1, lower_bound, upper_bound)); - break; - case WIDX_MAXIMUM_LENGTH_INCREASE: - upper_bound = OpenRCT2::Limits::MaxWaitingTime; - lower_bound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MaxWaitingTime, - std::clamp(ride->max_waiting_time + 1, lower_bound, upper_bound)); - break; - case WIDX_MAXIMUM_LENGTH_DECREASE: - upper_bound = OpenRCT2::Limits::MaxWaitingTime; - lower_bound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MaxWaitingTime, - std::clamp(ride->max_waiting_time - 1, lower_bound, upper_bound)); - break; - case WIDX_MODE_DROPDOWN: - WindowRideModeDropdown(w, widget); - break; - case WIDX_LOAD_DROPDOWN: - WindowRideLoadDropdown(w, widget); - break; - case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE: - upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : OpenRCT2::Limits::MaxCircuitsPerRide; - lower_bound = 1; - SetOperatingSetting( - rideId, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits + 1, lower_bound, upper_bound)); - break; - case WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE: - upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : OpenRCT2::Limits::MaxCircuitsPerRide; - lower_bound = 1; - SetOperatingSetting( - rideId, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits - 1, lower_bound, upper_bound)); - break; - } -} - -static void WindowRideOperatingLengthWindow(WindowBase* w, WidgetIndex widgetIndex) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - uint8_t upperBound = OpenRCT2::Limits::MaxWaitingTime; - uint8_t lowerBound = 0; - Formatter ft; - ft.Add(lowerBound); - ft.Add(upperBound); - auto title = (widgetIndex == WIDX_MINIMUM_LENGTH) ? STR_MINIMUM_WAITING_TIME : STR_MAXIMUM_WAITING_TIME; - auto currentValue = (widgetIndex == WIDX_MINIMUM_LENGTH) ? ride->min_waiting_time : ride->max_waiting_time; - char buffer[5]{}; - snprintf(buffer, std::size(buffer), "%u", currentValue); - WindowTextInputRawOpen(w, widgetIndex, title, STR_ENTER_VALUE, ft, buffer, 4); -} - -static void WindowRideOperatingTweakTextInput(WindowBase* w, const Ride& ride) -{ - switch (ride.mode) - { - case RideMode::PoweredLaunchPasstrough: - case RideMode::PoweredLaunch: - case RideMode::UpwardLaunch: - case RideMode::PoweredLaunchBlockSectioned: - case RideMode::StationToStation: - case RideMode::Dodgems: - return; - default: - break; - } - - const auto& operatingSettings = ride.GetRideTypeDescriptor().OperatingSettings; - int16_t maxValue = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit : operatingSettings.MaxValue; - int16_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; - - const auto& title = window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].text; - Formatter ft; - ft.Add(minValue * operatingSettings.OperatingSettingMultiplier); - ft.Add(maxValue * operatingSettings.OperatingSettingMultiplier); - - uint16_t currentValue = static_cast(ride.operation_option) * operatingSettings.OperatingSettingMultiplier; - char buffer[6]{}; - snprintf(buffer, std::size(buffer), "%u", currentValue); - - WindowTextInputRawOpen(w, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4); -} - -/** - * - * rct2: 0x006B1165 - */ -static void WindowRideOperatingDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (dropdownIndex == -1) - return; - - const auto rideId = w->rideId; - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - switch (widgetIndex) - { - case WIDX_MODE_DROPDOWN: - { - RideMode rideMode = RideMode::NullMode; - auto availableModes = ride->GetAvailableModes(); - auto modeInDropdownIndex = -1; - for (RideMode rideModeIndex = RideMode::Normal; rideModeIndex < RideMode::Count; rideModeIndex++) - { - if (availableModes & EnumToFlag(rideModeIndex)) - { - modeInDropdownIndex++; - if (modeInDropdownIndex == dropdownIndex) - { - rideMode = rideModeIndex; - break; - } - } - } - if (rideMode != RideMode::NullMode) - SetOperatingSetting(rideId, RideSetSetting::Mode, static_cast(rideMode)); - break; - } - case WIDX_LOAD_DROPDOWN: - SetOperatingSetting( - rideId, RideSetSetting::Departure, (ride->depart_flags & ~RIDE_DEPART_WAIT_FOR_LOAD_MASK) | dropdownIndex); - break; - } -} - -/** - * - * rct2: 0x006B178E - */ -static void WindowRideOperatingUpdate(WindowBase* w) -{ - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_3); - - auto ride = GetRide(w->rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) - { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING; - w->Invalidate(); - } -} - -static void WindowRideOperatingTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) -{ - if (text == nullptr) - return; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - if (widgetIndex == WIDX_MODE_TWEAK) - { - const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; - uint32_t maxValue = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : operatingSettings.MaxValue; - uint32_t minValue = gCheatsUnlockOperatingLimits ? 0 : operatingSettings.MinValue; - auto multiplier = ride->GetRideTypeDescriptor().OperatingSettings.OperatingSettingMultiplier; - - try - { - uint32_t origSize = std::stol(std::string(text)) / multiplier; - uint8_t size = static_cast(std::clamp(origSize, minValue, maxValue)); - SetOperatingSetting(ride->id, RideSetSetting::Operation, size); - } - catch (const std::logic_error&) - { - // std::stol can throw std::out_of_range or std::invalid_argument - } - } - else if (widgetIndex == WIDX_MINIMUM_LENGTH || widgetIndex == WIDX_MAXIMUM_LENGTH) - { - try - { - auto rideSetSetting = widgetIndex == WIDX_MINIMUM_LENGTH ? RideSetSetting::MinWaitingTime - : RideSetSetting::MaxWaitingTime; - - uint16_t upperBound = OpenRCT2::Limits::MaxWaitingTime; - uint16_t lowerBound = 0; - uint16_t size = std::stol(std::string(text)); - size = std::clamp(size, lowerBound, upperBound); - SetOperatingSetting(ride->id, rideSetSetting, size); - } - catch (const std::logic_error&) - { - // std::stol can throw std::out_of_range or std::invalid_argument - } - } -} - -/** - * - * rct2: 0x006B0B30 - */ -static void WindowRideOperatingInvalidate(WindowBase* w) -{ - Widget* widgets; - StringId format, caption, tooltip; - - widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - // Widget setup - w->pressed_widgets &= ~( - (1uLL << WIDX_LOAD_CHECKBOX) | (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX) - | (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX) | (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX) - | (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX)); - - // Sometimes, only one of the alternatives support lift hill pieces. Make sure to check both. - bool hasAlternativeType = ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE); - if (ride->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_LIFT_HILL) - || (hasAlternativeType - && GetRideTypeDescriptor(ride->GetRideTypeDescriptor().AlternateType).SupportsTrackPiece(TRACK_LIFT_HILL))) - { - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Label; - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Spinner; - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Button; - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Button; - ft.Rewind(); - ft.Increment(20); - ft.Add(ride->lift_hill_speed); - } - else - { - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Empty; - } - - // Number of circuits - if (ride->CanHaveMultipleCircuits()) - { - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Label; - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Spinner; - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Button; - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Button; - ft.Rewind(); - ft.Increment(22); - ft.Add(ride->num_circuits); - } - else - { - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Empty; - } - - // Leave if another vehicle arrives at station - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LEAVE_WHEN_ANOTHER_VEHICLE_ARRIVES_AT_STATION) - && ride->NumTrains > 1 && !ride->IsBlockSectioned()) - { - window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Checkbox; - window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].tooltip - = STR_LEAVE_IF_ANOTHER_VEHICLE_ARRIVES_TIP; - window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].text = ride->GetRideTypeDescriptor() - .NameConvention.vehicle - == RideComponentType::Boat - ? STR_LEAVE_IF_ANOTHER_BOAT_ARRIVES - : STR_LEAVE_IF_ANOTHER_TRAIN_ARRIVES; - } - else - { - window_ride_operating_widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Empty; - } - - // Synchronise with adjacent stations - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_CAN_SYNCHRONISE_ADJACENT_STATIONS)) - { - window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Checkbox; - window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].text - = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS; - window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].tooltip - = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP; - } - else - { - window_ride_operating_widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Empty; - } - - // Mode - window_ride_operating_widgets[WIDX_MODE].text = RideModeNames[static_cast(ride->mode)]; - - // Waiting - window_ride_operating_widgets[WIDX_LOAD].text = VehicleLoadNames[(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK)]; - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS)) - { - window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Checkbox; - window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::DropdownMenu; - window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Button; - - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Spinner; - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button; - - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Spinner; - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button; - - ft.Rewind(); - ft.Increment(10); - ft.Add(STR_FORMAT_SECONDS); - ft.Add(ride->min_waiting_time); - ft.Add(STR_FORMAT_SECONDS); - ft.Add(ride->max_waiting_time); - - if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD) - w->pressed_widgets |= (1uLL << WIDX_LOAD_CHECKBOX); - } - else - { - window_ride_operating_widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_LOAD].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Empty; - - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; - - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; - } - - if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES) - w->pressed_widgets |= (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); - if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) - w->pressed_widgets |= (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX); - if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) - w->pressed_widgets |= (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX); - if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH) - w->pressed_widgets |= (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX); - - // Mode specific functionality - auto multiplier = ride->GetRideTypeDescriptor().OperatingSettings.OperatingSettingMultiplier; - ft.Rewind(); - ft.Increment(18); - ft.Add(static_cast(ride->operation_option) * multiplier); - switch (ride->mode) - { - case RideMode::PoweredLaunchPasstrough: - case RideMode::PoweredLaunch: - case RideMode::UpwardLaunch: - case RideMode::PoweredLaunchBlockSectioned: - ft.Rewind(); - ft.Increment(18); - ft.Add((ride->launch_speed * 9) / 4); - format = STR_RIDE_MODE_SPEED_VALUE; - caption = STR_LAUNCH_SPEED; - tooltip = STR_LAUNCH_SPEED_TIP; - break; - case RideMode::StationToStation: - ft.Rewind(); - ft.Increment(18); - ft.Add((ride->speed * 9) / 4); - format = STR_RIDE_MODE_SPEED_VALUE; - caption = STR_SPEED; - tooltip = STR_SPEED_TIP; - break; - case RideMode::Race: - ft.Rewind(); - ft.Increment(18); - ft.Add(ride->NumLaps); - format = STR_NUMBER_OF_LAPS_VALUE; - caption = STR_NUMBER_OF_LAPS; - tooltip = STR_NUMBER_OF_LAPS_TIP; - break; - case RideMode::Dodgems: - format = STR_RIDE_MODE_TIME_LIMIT_VALUE; - caption = STR_TIME_LIMIT; - tooltip = STR_TIME_LIMIT_TIP; - break; - case RideMode::Swing: - format = STR_RIDE_MODE_NUMBER_OF_SWINGS_VALUE; - caption = STR_NUMBER_OF_SWINGS; - tooltip = STR_NUMBER_OF_SWINGS_TIP; - break; - case RideMode::Rotation: - case RideMode::ForwardRotation: - case RideMode::BackwardRotation: - format = STR_NUMBER_OF_ROTATIONS_VALUE; - caption = STR_NUMBER_OF_ROTATIONS; - tooltip = STR_NUMBER_OF_ROTATIONS_TIP; - break; - default: - format = STR_MAX_PEOPLE_ON_RIDE_VALUE; - caption = STR_MAX_PEOPLE_ON_RIDE; - tooltip = STR_MAX_PEOPLE_ON_RIDE_TIP; - if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) - format = 0; - break; - } - - if (format != 0) - { - window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Label; - window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].text = caption; - window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].tooltip = tooltip; - window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Spinner; - window_ride_operating_widgets[WIDX_MODE_TWEAK].text = format; - window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Button; - window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Button; - w->pressed_widgets &= ~(1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); - } - else - { - window_ride_operating_widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Empty; - window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Empty; - } - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); -} - -/** - * - * rct2: 0x006B1001 - */ -static void WindowRideOperatingPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - // Horizontal rule between mode settings and depart settings - GfxFillRectInset( - dpi, - { w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 }, - w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } }, - w->colours[1], INSET_RECT_FLAG_BORDER_INSET); - - // Number of block sections - if (ride->IsBlockSectioned()) - { - auto ft = Formatter(); - ft.Add(ride->num_block_brakes + ride->num_stations); - DrawTextBasic( - dpi, w->windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 }, - STR_BLOCK_SECTIONS, ft, COLOUR_BLACK); - } -} - -#pragma endregion - -#pragma region Maintenance - -/** - * - * rct2: 0x006B1AE4 - */ -static void WindowRideLocateMechanic(WindowBase* w) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - // First check if there is a mechanic assigned - Peep* mechanic = RideGetAssignedMechanic(*ride); - - // Otherwise find the closest mechanic - if (mechanic == nullptr) - mechanic = RideFindClosestMechanic(*ride, 1); - - if (mechanic == nullptr) - ContextShowError(STR_UNABLE_TO_LOCATE_MECHANIC, STR_NONE, {}); - else - { - auto intent = Intent(WindowClass::Peep); - intent.PutExtra(INTENT_EXTRA_PEEP, mechanic); - ContextOpenIntent(&intent); - } -} - -/** - * - * rct2: 0x006B7D08 - */ -static void WindowRideMaintenanceDrawBar( - WindowBase* w, DrawPixelInfo& dpi, const ScreenCoordsXY& coords, int32_t value, int32_t colour) -{ - GfxFillRectInset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, w->colours[1], INSET_RECT_F_30); - if (colour & BAR_BLINK) - { - colour &= ~BAR_BLINK; - if (GameIsNotPaused() && (gCurrentRealTimeTicks & 8)) - return; - } - - value = ((186 * ((value * 2) & 0xFF)) >> 8) & 0xFF; - if (value > 2) - { - GfxFillRectInset(dpi, { coords + ScreenCoordsXY{ 2, 1 }, coords + ScreenCoordsXY{ value + 1, 7 } }, colour, 0); - } -} - -/** - * - * rct2: 0x006B1AAD - */ -static void WindowRideMaintenanceMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_LOCATE_MECHANIC: - WindowRideLocateMechanic(w); - break; - case WIDX_REFURBISH_RIDE: - ContextOpenDetailWindow(WD_REFURBISH_RIDE, w->number); - break; - } -} - -/** - * - * rct2: 0x006B1D70 - */ -static void WindowRideMaintenanceResize(WindowBase* w) -{ - WindowSetResize(*w, 316, 135, 316, 135); -} - -/** - * - * rct2: 0x006B1ACE - */ -static void WindowRideMaintenanceMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - Widget* dropdownWidget = widget; - int32_t j, num_items; - - switch (widgetIndex) - { - case WIDX_INSPECTION_INTERVAL_DROPDOWN: - dropdownWidget--; - for (int32_t i = 0; i < 7; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = RideInspectionIntervalNames[i]; - } - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, 7, widget->right - dropdownWidget->left); - - Dropdown::SetChecked(ride->inspection_interval, true); - break; - - case WIDX_FORCE_BREAKDOWN: - num_items = 1; - for (j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++) - { - if (rideEntry->ride_type[j] != RIDE_TYPE_NULL) - break; - } - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_DEBUG_FIX_RIDE; - for (int32_t i = 0; i < 8; i++) - { - assert(j < static_cast(std::size(rideEntry->ride_type))); - if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast(1 << i)) - { - if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned()) - { - if (ride->NumTrains != 1) - continue; - } - gDropdownItems[num_items].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[num_items].Args = RideBreakdownReasonNames[i]; - num_items++; - } - } - if (num_items == 1) - { - ContextShowError(STR_DEBUG_NO_BREAKDOWNS_AVAILABLE, STR_NONE, {}); - } - else - { - WindowDropdownShowText( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], Dropdown::Flag::StayOpen, num_items); - - num_items = 1; - int32_t breakdownReason = ride->breakdown_reason_pending; - if (breakdownReason != BREAKDOWN_NONE && (ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING)) - { - for (int32_t i = 0; i < 8; i++) - { - if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast(1 << i)) - { - if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned()) - { - if (ride->NumTrains != 1) - continue; - } - if (i == breakdownReason) - { - Dropdown::SetChecked(num_items, true); - break; - } - gDropdownItems[num_items].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[num_items].Args = RideBreakdownReasonNames[i]; - num_items++; - } - } - } - - if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING) == 0) - { - Dropdown::SetDisabled(0, true); - } - } - break; - } -} - -/** - * - * rct2: 0x006B1AD9 - */ -static void WindowRideMaintenanceDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (dropdownIndex == -1) - return; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - switch (widgetIndex) - { - case WIDX_INSPECTION_INTERVAL_DROPDOWN: - SetOperatingSetting(w->rideId, RideSetSetting::InspectionInterval, dropdownIndex); - break; - - case WIDX_FORCE_BREAKDOWN: - if (dropdownIndex == 0) - { - Vehicle* vehicle; - switch (ride->breakdown_reason_pending) - { - case BREAKDOWN_SAFETY_CUT_OUT: - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) - break; - for (int32_t i = 0; i < ride->NumTrains; ++i) - { - for (vehicle = GetEntity(ride->vehicles[i]); vehicle != nullptr; - vehicle = GetEntity(vehicle->next_vehicle_on_train)) - { - vehicle->ClearFlag( - VehicleFlags::CarIsBroken | VehicleFlags::StoppedOnLift | VehicleFlags::TrainIsBroken); - } - } - break; - case BREAKDOWN_RESTRAINTS_STUCK_CLOSED: - case BREAKDOWN_RESTRAINTS_STUCK_OPEN: - case BREAKDOWN_DOORS_STUCK_CLOSED: - case BREAKDOWN_DOORS_STUCK_OPEN: - vehicle = GetEntity(ride->vehicles[ride->broken_vehicle]); - if (vehicle != nullptr) - { - vehicle->ClearFlag(VehicleFlags::CarIsBroken); - } - break; - case BREAKDOWN_VEHICLE_MALFUNCTION: - vehicle = GetEntity(ride->vehicles[ride->broken_vehicle]); - if (vehicle != nullptr) - { - vehicle->ClearFlag(VehicleFlags::TrainIsBroken); - } - break; - } - ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN); - WindowInvalidateByNumber(WindowClass::Ride, w->number); - break; - } - if (ride->lifecycle_flags - & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - { - ContextShowError(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_ALREADY_BROKEN, {}); - } - else if (ride->status == RideStatus::Closed) - { - ContextShowError(STR_DEBUG_CANT_FORCE_BREAKDOWN, STR_DEBUG_RIDE_IS_CLOSED, {}); - } - else - { - int32_t j; - for (j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++) - { - if (rideEntry->ride_type[j] != RIDE_TYPE_NULL) - break; - } - int32_t i; - int32_t num_items = 1; - for (i = 0; i < BREAKDOWN_COUNT; i++) - { - assert(j < static_cast(std::size(rideEntry->ride_type))); - if (GetRideTypeDescriptor(rideEntry->ride_type[j]).AvailableBreakdowns & static_cast(1 << i)) - { - if (i == BREAKDOWN_BRAKES_FAILURE && ride->IsBlockSectioned()) - { - if (ride->NumTrains != 1) - continue; - } - if (num_items == dropdownIndex) - break; - num_items++; - } - } - RidePrepareBreakdown(*ride, i); - } - break; - } -} - -/** - * - * rct2: 0x006B1D37 - */ -static void WindowRideMaintenanceUpdate(WindowBase* w) -{ - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_4); - - auto ride = GetRide(w->rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE) - { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE; - w->Invalidate(); - } -} - -/** - * - * rct2: 0x006B17C8 - */ -static void WindowRideMaintenanceInvalidate(WindowBase* w) -{ - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval]; - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); - - if (gConfigGeneral.DebuggingTools && NetworkGetMode() == NETWORK_MODE_NONE) - { - window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::FlatBtn; - } - else - { - window_ride_maintenance_widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::Empty; - } - - if (ride->GetRideTypeDescriptor().AvailableBreakdowns == 0 || !(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) - { - w->disabled_widgets |= (1uLL << WIDX_REFURBISH_RIDE); - window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_CANT_REFURBISH_NOT_NEEDED; - } - else - { - w->disabled_widgets &= ~(1uLL << WIDX_REFURBISH_RIDE); - window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_REFURBISH_RIDE_TIP; - } -} - -/** - * - * rct2: 0x006B1877 - */ -static void WindowRideMaintenancePaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - // Locate mechanic button image - Widget* widget = &window_ride_maintenance_widgets[WIDX_LOCATE_MECHANIC]; - auto screenCoords = w->windowPos + ScreenCoordsXY{ widget->left, widget->top }; - auto image = ImageId(SPR_MECHANIC, COLOUR_BLACK, gStaffMechanicColour); - GfxDrawSprite(dpi, image, screenCoords); - - // Inspection label - widget = &window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL]; - screenCoords = w->windowPos + ScreenCoordsXY{ 4, widget->top + 1 }; - DrawTextBasic(dpi, screenCoords, STR_INSPECTION); - - // Reliability - widget = &window_ride_maintenance_widgets[WIDX_PAGE_BACKGROUND]; - screenCoords = w->windowPos + ScreenCoordsXY{ widget->left + 4, widget->top + 4 }; - - uint16_t reliability = ride->reliability_percentage; - auto ft = Formatter(); - ft.Add(reliability); - DrawTextBasic(dpi, screenCoords, STR_RELIABILITY_LABEL_1757, ft); - WindowRideMaintenanceDrawBar( - w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, std::max(10, reliability), COLOUR_BRIGHT_GREEN); - screenCoords.y += 11; - - uint16_t downTime = ride->downtime; - ft = Formatter(); - ft.Add(downTime); - DrawTextBasic(dpi, screenCoords, STR_DOWN_TIME_LABEL_1889, ft); - WindowRideMaintenanceDrawBar(w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, downTime, COLOUR_BRIGHT_RED); - screenCoords.y += 26; - - // Last inspection - StringId stringId; - if (ride->last_inspection <= 1) - stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTE; - else if (ride->last_inspection <= 240) - stringId = STR_TIME_SINCE_LAST_INSPECTION_MINUTES; - else - stringId = STR_TIME_SINCE_LAST_INSPECTION_MORE_THAN_4_HOURS; - - ft = Formatter(); - ft.Add(ride->last_inspection); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += 12; - - // Last / current breakdown - if (ride->breakdown_reason == BREAKDOWN_NONE) - return; - - stringId = (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) ? STR_CURRENT_BREAKDOWN : STR_LAST_BREAKDOWN; - ft = Formatter(); - ft.Add(RideBreakdownReasonNames[ride->breakdown_reason]); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += 12; - - // Mechanic status - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) - { - switch (ride->mechanic_status) - { - case RIDE_MECHANIC_STATUS_CALLING: - { - stringId = STR_NO_MECHANICS_ARE_HIRED_MESSAGE; - - for (auto peep : EntityList()) - { - if (peep->IsMechanic()) - { - stringId = STR_CALLING_MECHANIC; - break; - } - } - break; - } - case RIDE_MECHANIC_STATUS_HEADING: - stringId = STR_MEHCANIC_IS_HEADING_FOR_THE_RIDE; - break; - case RIDE_MECHANIC_STATUS_FIXING: - case RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES: - stringId = STR_MEHCANIC_IS_FIXING_THE_RIDE; - break; - default: - stringId = STR_EMPTY; - break; - } - - if (stringId != STR_EMPTY) - { - if (stringId == STR_CALLING_MECHANIC || stringId == STR_NO_MECHANICS_ARE_HIRED_MESSAGE) - { - DrawTextWrapped(dpi, screenCoords, 280, stringId, {}, { TextAlignment::LEFT }); - } - else - { - auto staff = GetEntity(ride->mechanic); - if (staff != nullptr && staff->IsMechanic()) - { - ft = Formatter(); - staff->FormatNameTo(ft); - DrawTextWrapped(dpi, screenCoords, 280, stringId, ft, { TextAlignment::LEFT }); - } - } - } - } -} - -#pragma endregion - -#pragma region Colour - -static int32_t WindowRideHasTrackColour(const Ride& ride, int32_t trackColour) -{ - // Get station flags (shops don't have them) - auto stationObjFlags = 0; - if (!ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) - { - auto stationObj = ride.GetStationObject(); - if (stationObj != nullptr) - { - stationObjFlags = stationObj->Flags; - } - } - - switch (trackColour) - { - case 0: - return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR) - || ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN); - case 1: - return (stationObjFlags & STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR) - || ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL); - case 2: - return ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS); - default: - return 0; - } -} - -static void WindowRideSetTrackColourScheme(WindowBase* w, const ScreenCoordsXY& screenPos) -{ - auto newColourScheme = static_cast(w->ride_colour); - auto info = GetMapCoordinatesFromPos(screenPos, EnumsToFlags(ViewportInteractionItem::Ride)); - - if (info.SpriteType != ViewportInteractionItem::Ride) - return; - if (info.Element->GetType() != TileElementType::Track) - return; - if (info.Element->AsTrack()->GetRideIndex() != w->rideId) - return; - if (info.Element->AsTrack()->GetColourScheme() == newColourScheme) - return; - - auto z = info.Element->GetBaseZ(); - auto direction = info.Element->GetDirection(); - auto gameAction = RideSetColourSchemeAction( - CoordsXYZD{ info.Loc, z, static_cast(direction) }, info.Element->AsTrack()->GetTrackType(), newColourScheme); - GameActions::Execute(&gameAction); -} - -/** - * - * rct2: 0x006B04FA - */ -static void WindowRideColourClose(WindowBase* w) -{ - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return; - - if (gCurrentToolWidget.window_classification != w->classification) - return; - - if (gCurrentToolWidget.window_number != w->number) - return; - - ToolCancel(); -} - -/** - * - * rct2: 0x006B02A1 - */ -static void WindowRideColourMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_PAINT_INDIVIDUAL_AREA: - ToolSet(*w, WIDX_PAINT_INDIVIDUAL_AREA, Tool::PaintDown); - break; - case WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX: - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - const bool currentlyEnabled = ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS); - auto rideSetAppearanceAction = RideSetAppearanceAction( - w->rideId, RideSetAppearanceType::SellingItemColourIsRandom, currentlyEnabled ? 0 : 1, 0); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - } -} - -/** - * - * rct2: 0x006B0AB6 - */ -static void WindowRideColourResize(WindowBase* w) -{ - WindowSetResize(*w, 316, 207, 316, 207); -} - -/** - * - * rct2: 0x006B02C6 - */ -static void WindowRideColourMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - VehicleColour vehicleColour; - int32_t i, numItems; - StringId stringId; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - auto colourSchemeIndex = w->ride_colour; - auto dropdownWidget = widget - 1; - - switch (widgetIndex) - { - case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: - for (i = 0; i < OpenRCT2::Limits::NumColourSchemes; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = ColourSchemeNames[i]; - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left); - - Dropdown::SetChecked(colourSchemeIndex, true); - break; - case WIDX_TRACK_MAIN_COLOUR: - WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].main); - break; - case WIDX_TRACK_ADDITIONAL_COLOUR: - WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].additional); - break; - case WIDX_TRACK_SUPPORT_COLOUR: - WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].supports); - break; - case WIDX_MAZE_STYLE_DROPDOWN: - for (i = 0; i < 4; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = MazeOptions[i].text; - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left); - - Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true); - break; - case WIDX_ENTRANCE_STYLE_DROPDOWN: - { - auto ddIndex = 0; - auto& objManager = GetContext()->GetObjectManager(); - for (i = 0; i < MAX_STATION_OBJECTS; i++) - { - auto stationObj = static_cast(objManager.GetLoadedObject(ObjectType::Station, i)); - if (stationObj != nullptr) - { - gDropdownItems[ddIndex].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[ddIndex].Args = stationObj->NameStringId; - if (ride->entrance_style == i) - { - gDropdownItems[ddIndex].Format = STR_DROPDOWN_MENU_LABEL_SELECTED; - } - ddIndex++; - } - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, widget->right - dropdownWidget->left); - break; - } - case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: - for (i = 0; i < 3; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = (GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular - << 16) - | VehicleColourSchemeNames[i]; - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, rideEntry->max_cars_in_train > 1 ? 3 : 2, - widget->right - dropdownWidget->left); - - Dropdown::SetChecked(ride->colour_scheme_type & 3, true); - break; - case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: - numItems = ride->NumTrains; - if ((ride->colour_scheme_type & 3) != VEHICLE_COLOUR_SCHEME_PER_TRAIN) - numItems = ride->num_cars_per_train; - - stringId = (ride->colour_scheme_type & 3) == VEHICLE_COLOUR_SCHEME_PER_TRAIN ? STR_RIDE_COLOUR_TRAIN_OPTION - : STR_RIDE_COLOUR_VEHICLE_OPTION; - for (i = 0; i < std::min(numItems, Dropdown::ItemsMaxSize); i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = (static_cast(i + 1) << 32) - | ((GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised) << 16) - | stringId; - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); - - Dropdown::SetChecked(w->vehicleIndex, true); - break; - case WIDX_VEHICLE_BODY_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Body); - break; - case WIDX_VEHICLE_TRIM_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Trim); - break; - case WIDX_VEHICLE_TERNARY_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Tertiary); - break; - } -} - -/** - * - * rct2: 0x006B0331 - */ -static void WindowRideColourDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (dropdownIndex == -1) - return; - - const auto rideId = w->rideId; - switch (widgetIndex) - { - case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: - w->ride_colour = static_cast(dropdownIndex); - w->Invalidate(); - break; - case WIDX_TRACK_MAIN_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::TrackColourMain, ColourDropDownIndexToColour(dropdownIndex), w->ride_colour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_TRACK_ADDITIONAL_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::TrackColourAdditional, ColourDropDownIndexToColour(dropdownIndex), - w->ride_colour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_TRACK_SUPPORT_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::TrackColourSupports, ColourDropDownIndexToColour(dropdownIndex), w->ride_colour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_MAZE_STYLE_DROPDOWN: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::MazeStyle, dropdownIndex, w->ride_colour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_ENTRANCE_STYLE_DROPDOWN: - { - auto ddIndex = 0; - auto& objManager = GetContext()->GetObjectManager(); - for (auto i = 0; i < MAX_STATION_OBJECTS; i++) - { - auto stationObj = static_cast(objManager.GetLoadedObject(ObjectType::Station, i)); - if (stationObj != nullptr) - { - if (ddIndex == dropdownIndex) - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::EntranceStyle, ddIndex, 0); - rideSetAppearanceAction.SetCallback([ddIndex](const GameAction*, const GameActions::Result* res) { - if (res->Error != GameActions::Status::Ok) - return; - gLastEntranceStyle = ddIndex; - }); - GameActions::Execute(&rideSetAppearanceAction); - break; - } - ddIndex++; - } - } - break; - } - case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourScheme, dropdownIndex, 0); - GameActions::Execute(&rideSetAppearanceAction); - w->vehicleIndex = 0; - } - break; - case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: - w->vehicleIndex = dropdownIndex; - w->Invalidate(); - break; - case WIDX_VEHICLE_BODY_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourBody, ColourDropDownIndexToColour(dropdownIndex), w->vehicleIndex); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_VEHICLE_TRIM_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourTrim, ColourDropDownIndexToColour(dropdownIndex), w->vehicleIndex); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_VEHICLE_TERNARY_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourTernary, ColourDropDownIndexToColour(dropdownIndex), - w->vehicleIndex); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - } -} - -/** - * - * rct2: 0x006B0A8F - */ -static void WindowRideColourUpdate(WindowBase* w) -{ - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_5); - WidgetInvalidate(*w, WIDX_VEHICLE_PREVIEW); -} - -/** - * - * rct2: 0x006B04EC - */ -static void WindowRideColourTooldown(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) - WindowRideSetTrackColourScheme(w, screenCoords); -} - -/** - * - * rct2: 0x006B04F3 - */ -static void WindowRideColourTooldrag(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) - WindowRideSetTrackColourScheme(w, screenCoords); -} - -/** - * - * rct2: 0x006AFB36 - */ -static void WindowRideColourInvalidate(WindowBase* w) -{ - TrackColour trackColour; - VehicleColour vehicleColour; - - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - w->widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; - auto ft = Formatter::Common(); - ft.Increment(16); - ride->FormatNameTo(ft); - - // Track colours - int32_t colourScheme = w->ride_colour; - trackColour = ride->track_colour[colourScheme]; - - // Maze style - const auto& rtd = ride->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::DropdownMenu; - window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Button; - window_ride_colour_widgets[WIDX_MAZE_STYLE].text = MazeOptions[trackColour.supports].text; - } - else - { - window_ride_colour_widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Empty; - } - - // Track, multiple colour schemes - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR)) - { - window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; - window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; - window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::FlatBtn; - } - else - { - window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::Empty; - } - - // Track main colour - if (WindowRideHasTrackColour(*ride, 0)) - { - window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::ColourBtn; - window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].image = GetColourButtonImage(trackColour.main); - } - else - { - window_ride_colour_widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::Empty; - } - - // Track additional colour - if (WindowRideHasTrackColour(*ride, 1)) - { - window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::ColourBtn; - window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].image = GetColourButtonImage(trackColour.additional); - } - else - { - window_ride_colour_widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::Empty; - } - - // Selling item random colour checkbox - if (ride->HasRecolourableShopItems()) - { - window_ride_colour_widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Checkbox; - if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS)) - { - w->pressed_widgets |= (1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); - } - else - { - w->pressed_widgets &= ~(1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); - } - } - else - { - window_ride_colour_widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Empty; - } - - // Track supports colour - if (WindowRideHasTrackColour(*ride, 2) && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::ColourBtn; - window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].image = GetColourButtonImage(trackColour.supports); - } - else - { - window_ride_colour_widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::Empty; - } - - // Track preview - if (ride->GetRideTypeDescriptor().HasFlag( - RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_ADDITIONAL - | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_SUPPORTS)) - window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Spinner; - else - window_ride_colour_widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Empty; - - // Entrance style - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT)) - { - window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Spinner; - window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::DropdownMenu; - window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Button; - - auto stringId = STR_NONE; - auto stationObj = ride->GetStationObject(); - if (stationObj != nullptr) - { - stringId = stationObj->NameStringId; - } - window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].text = stringId; - } - else - { - window_ride_colour_widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Empty; - } - - // Vehicle colours - if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES) - && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_VEHICLE_COLOURS)) - { - int32_t vehicleColourSchemeType = ride->colour_scheme_type & 3; - if (vehicleColourSchemeType == 0) - w->vehicleIndex = 0; - - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - - window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Scroll; - window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::ColourBtn; - window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOUR].image = GetColourButtonImage(vehicleColour.Body); - - bool allowChangingTrimColour = false; - bool allowChangingTernaryColour = false; - - for (int32_t i = 0; i < ride->num_cars_per_train; i++) - { - uint8_t vehicleTypeIndex = RideEntryGetVehicleAtPosition(ride->subtype, ride->num_cars_per_train, i); - - if (rideEntry->Cars[vehicleTypeIndex].flags & CAR_ENTRY_FLAG_ENABLE_TRIM_COLOUR) - { - allowChangingTrimColour = true; - } - if (rideEntry->Cars[vehicleTypeIndex].flags & CAR_ENTRY_FLAG_ENABLE_TERNARY_COLOUR) - { - allowChangingTernaryColour = true; - } - } - - // Additional colours - if (allowChangingTrimColour) - { - window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::ColourBtn; - window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].image = GetColourButtonImage(vehicleColour.Trim); - if (allowChangingTernaryColour) - { - window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::ColourBtn; - window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].image = GetColourButtonImage(vehicleColour.Tertiary); - } - else - { - window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; - } - } - else - { - window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; - } - - // Vehicle colour scheme type - if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_VEHICLE_IS_INTEGRAL) - && (ride->num_cars_per_train | ride->NumTrains) > 1) - { - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; - } - else - { - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; - } - ft.Rewind(); - ft.Increment(6); - ft.Add(VehicleColourSchemeNames[vehicleColourSchemeType]); - ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular); - ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised); - ft.Add(w->vehicleIndex + 1); - - // Vehicle index - if (vehicleColourSchemeType != 0) - { - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::DropdownMenu; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Button; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].text = vehicleColourSchemeType == 1 - ? STR_RIDE_COLOUR_TRAIN_VALUE - : STR_RIDE_COLOUR_VEHICLE_VALUE; - } - else - { - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; - } - } - else - { - window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty; - window_ride_colour_widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; - } - - ft.Rewind(); - ft.Increment(14); - ft.Add(ColourSchemeNames[colourScheme]); - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); -} - -/** - * - * rct2: 0x006AFF3E - */ -static void WindowRideColourPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - // TODO: This should use lists and identified sprites - DrawPixelInfo clippedDpi; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - // Track / shop item preview - const auto& trackPreviewWidget = window_ride_colour_widgets[WIDX_TRACK_PREVIEW]; - if (trackPreviewWidget.type != WindowWidgetType::Empty) - GfxFillRect( - dpi, - { { w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left + 1, trackPreviewWidget.top + 1 } }, - { w->windowPos + ScreenCoordsXY{ trackPreviewWidget.right - 1, trackPreviewWidget.bottom - 1 } } }, - PALETTE_INDEX_12); - - auto trackColour = ride->track_colour[w->ride_colour]; - - // - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr || rideEntry->shop_item[0] == ShopItem::None) - { - auto screenCoords = w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left, trackPreviewWidget.top }; - - // Track - const auto& rtd = ride->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - GfxDrawSprite(dpi, ImageId(MazeOptions[trackColour.supports].sprite), screenCoords); - } - else - { - auto typeDescriptor = ride->GetRideTypeDescriptor(); - int32_t spriteIndex = typeDescriptor.ColourPreview.Track; - if (spriteIndex != 0) - { - GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.main, trackColour.additional), screenCoords); - } - - // Supports - spriteIndex = typeDescriptor.ColourPreview.Supports; - if (spriteIndex != 0) - { - GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.supports), screenCoords); - } - } - } - else - { - auto screenCoords = w->windowPos - + ScreenCoordsXY{ (trackPreviewWidget.left + trackPreviewWidget.right) / 2 - 8, - (trackPreviewWidget.bottom + trackPreviewWidget.top) / 2 - 6 }; - - ShopItem shopItem = rideEntry->shop_item[1] == ShopItem::None ? rideEntry->shop_item[0] : rideEntry->shop_item[1]; - if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS)) - { - static colour_t spriteColour = COLOUR_BLACK; - // Limit update rate of preview to avoid making people dizzy. - if ((gCurrentTicks % 64) == 0) - { - spriteColour++; - if (spriteColour >= COLOUR_NUM_NORMAL) - { - spriteColour = COLOUR_BLACK; - } - } - - GfxDrawSprite(dpi, ImageId(GetShopItemDescriptor(shopItem).Image, spriteColour), screenCoords); - } - else - { - GfxDrawSprite(dpi, ImageId(GetShopItemDescriptor(shopItem).Image, ride->track_colour[0].main), screenCoords); - } - } - - // Entrance preview - trackColour = ride->track_colour[0]; - const auto& entrancePreviewWidget = w->widgets[WIDX_ENTRANCE_PREVIEW]; - if (entrancePreviewWidget.type != WindowWidgetType::Empty) - { - if (ClipDrawPixelInfo( - clippedDpi, dpi, w->windowPos + ScreenCoordsXY{ entrancePreviewWidget.left + 1, entrancePreviewWidget.top + 1 }, - entrancePreviewWidget.width(), entrancePreviewWidget.height())) - { - GfxClear(&clippedDpi, PALETTE_INDEX_12); - - auto stationObj = ride->GetStationObject(); - if (stationObj != nullptr && stationObj->BaseImageId != ImageIndexUndefined) - { - auto imageId = ImageId(stationObj->BaseImageId, trackColour.main, trackColour.additional); - - // Back - GfxDrawSprite(clippedDpi, imageId, { 34, 20 }); - - // Front - GfxDrawSprite(clippedDpi, imageId.WithIndexOffset(4), { 34, 20 }); - - // Glass - if (stationObj->Flags & STATION_OBJECT_FLAGS::IS_TRANSPARENT) - { - auto glassImageId = ImageId(stationObj->BaseImageId + 20).WithTransparency(trackColour.main); - GfxDrawSprite(clippedDpi, glassImageId, { 34, 20 }); - } - } - } - - DrawTextEllipsised(dpi, { w->windowPos.x + 3, w->windowPos.y + 103 }, 97, STR_STATION_STYLE, {}); - } -} - -/** - * - * rct2: 0x006B0192 - */ -static void WindowRideColourScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - auto vehiclePreviewWidget = &window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW]; - auto vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - - // Background colour - GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, PALETTE_INDEX_12); - - // ? - auto screenCoords = ScreenCoordsXY{ vehiclePreviewWidget->width() / 2, vehiclePreviewWidget->height() - 15 }; - - // ? - auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR ? w->vehicleIndex - : rideEntry->TabCar; - - const auto& carEntry = rideEntry - ->Cars[RideEntryGetVehicleAtPosition(ride->subtype, ride->num_cars_per_train, trainCarIndex)]; - - screenCoords.y += carEntry.tab_height; - - // Draw the coloured spinning vehicle - // w->frame_no represents a SpritePrecision of 64 - ImageIndex imageIndex = carEntry.SpriteByYaw(w->frame_no / 2, SpriteGroupType::SlopeFlat); - imageIndex &= carEntry.TabRotationMask; - imageIndex *= carEntry.base_num_frames; - imageIndex += carEntry.base_image_id; - auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary); - GfxDrawSprite(dpi, imageId, screenCoords); -} - -#pragma endregion - -#pragma region Music -static std::vector window_ride_current_music_style_order; - -/** - * - * rct2: 0x006B215D - */ -static void WindowRideToggleMusic(WindowBase* w) -{ - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; - SetOperatingSetting(w->rideId, RideSetSetting::Music, activateMusic); - } -} - -/** - * - * rct2: 0x006B1ED7 - */ -static void WindowRideMusicMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_PLAY_MUSIC: - WindowRideToggleMusic(w); - break; - } -} - -/** - * - * rct2: 0x006AF4A2 - */ -static void WindowRideMusicResize(WindowBase* w) -{ - w->flags |= WF_RESIZABLE; - WindowSetResize(*w, 316, 81, 316, 81); -} - -static std::string GetMusicString(ObjectEntryIndex musicObjectIndex) -{ - auto& objManager = GetContext()->GetObjectManager(); - auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, musicObjectIndex)); - - auto name = musicObj->NameStringId; - return FormatStringID(name, {}); -} - -/** - * - * rct2: 0x006B1EFC - */ -static void WindowRideMusicMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - if (widgetIndex != WIDX_MUSIC_DROPDOWN) - return; - - auto dropdownWidget = widget - 1; - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - // Construct list of available music - auto& musicOrder = window_ride_current_music_style_order; - musicOrder.clear(); - auto& objManager = GetContext()->GetObjectManager(); - for (ObjectEntryIndex i = 0; i < MAX_MUSIC_OBJECTS; i++) - { - auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, i)); - if (musicObj != nullptr) - { - // Hide custom music if the WAV file does not exist - auto originalStyleId = musicObj->GetOriginalStyleId(); - if (originalStyleId.has_value() - && (originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_1 || originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_2)) - { - auto numTracks = musicObj->GetTrackCount(); - if (numTracks != 0) - { - auto track0 = musicObj->GetTrack(0); - if (!track0->Asset.IsAvailable()) - { - continue; - } - } - else - { - continue; - } - } - - if (gCheatsUnlockOperatingLimits || musicObj->SupportsRideType(ride->type)) - { - musicOrder.push_back(i); - } - } - } - - // Sort available music by the alphabetical order - std::stable_sort(musicOrder.begin(), musicOrder.end(), [](const ObjectEntryIndex& a, const ObjectEntryIndex& b) { - return String::Compare(GetMusicString(b), GetMusicString(a), false) > 0; - }); - - // Setup dropdown list - auto numItems = musicOrder.size(); - for (size_t i = 0; i < numItems; i++) - { - auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, musicOrder[i])); - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = musicObj->NameStringId; - } - - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); - - // Set currently checked item - for (size_t i = 0; i < numItems; i++) - { - if (musicOrder[i] == ride->music) - { - Dropdown::SetChecked(static_cast(i), true); - } - } -} - -/** - * - * rct2: 0x006B1F03 - */ -static void WindowRideMusicDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0 - && static_cast(dropdownIndex) < window_ride_current_music_style_order.size()) - { - auto musicStyle = window_ride_current_music_style_order[dropdownIndex]; - SetOperatingSetting(w->rideId, RideSetSetting::MusicType, musicStyle); - } -} - -/** - * - * rct2: 0x006B2198 - */ -static void WindowRideMusicUpdate(WindowBase* w) -{ - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_6); -} - -/** - * - * rct2: 0x006B1DEA - */ -static void WindowRideMusicInvalidate(WindowBase* w) -{ - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - // Set selected music - StringId musicName = STR_NONE; - auto& objManager = GetContext()->GetObjectManager(); - auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, ride->music)); - if (musicObj != nullptr) - { - musicName = musicObj->NameStringId; - } - window_ride_music_widgets[WIDX_MUSIC].text = musicName; - - // Set music activated - auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0; - if (isMusicActivated) - { - w->pressed_widgets |= (1uLL << WIDX_PLAY_MUSIC); - w->disabled_widgets &= ~(1uLL << WIDX_MUSIC); - w->disabled_widgets &= ~(1uLL << WIDX_MUSIC_DROPDOWN); - } - else - { - w->pressed_widgets &= ~(1uLL << WIDX_PLAY_MUSIC); - w->disabled_widgets |= (1uLL << WIDX_MUSIC); - w->disabled_widgets |= (1uLL << WIDX_MUSIC_DROPDOWN); - } - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); -} - -/** - * - * rct2: 0x006B1ECC - */ -static void WindowRideMusicPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); -} - -#pragma endregion - -#pragma region Measurements - -static StringId GetRatingName(ride_rating rating) -{ - int32_t index = std::clamp(rating >> 8, 0, static_cast(std::size(RatingNames)) - 1); - return RatingNames[index]; -} - -/** - * - * rct2: 0x006D2804 - when al == 0*/ -static void CancelScenerySelection() -{ - gGamePaused &= ~GAME_PAUSED_SAVING_TRACK; - gTrackDesignSaveMode = false; - OpenRCT2::Audio::Resume(); - - WindowBase* main_w = WindowGetMain(); - if (main_w != nullptr) - { - main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); - } - - GfxInvalidateScreen(); - ToolCancel(); -} - -/** - * - * rct2: 0x006D27A3 - */ -static void SetupScenerySelection(WindowBase* w) -{ - if (gTrackDesignSaveMode) - { - CancelScenerySelection(); - } - - while (ToolSet(*w, WIDX_BACKGROUND, Tool::Crosshair)) - ; - - gTrackDesignSaveRideIndex = w->rideId; - - TrackDesignSaveInit(); - gGamePaused |= GAME_PAUSED_SAVING_TRACK; - gTrackDesignSaveMode = true; - - OpenRCT2::Audio::StopAll(); - - WindowBase* w_main = WindowGetMain(); - if (w_main != nullptr) - { - w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); - } - - GfxInvalidateScreen(); -} - -/** - * - * rct2: 0x006D3026 - */ -static void WindowRideMeasurementsDesignReset() -{ - TrackDesignSaveResetScenery(); -} - -/** - * - * rct2: 0x006D303D - */ -static void WindowRideMeasurementsDesignSelectNearbyScenery() -{ - TrackDesignSaveSelectNearbyScenery(gTrackDesignSaveRideIndex); -} - -/** - * - * rct2: 0x006AD4DA - */ -void WindowRideMeasurementsDesignCancel() -{ - if (gTrackDesignSaveMode) - { - CancelScenerySelection(); - } -} - -static void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) -{ - if (result == MODAL_RESULT_OK) - { - TrackRepositoryScan(); - } - GfxInvalidateScreen(); -}; - -/** - * - * rct2: 0x006AD4CD - */ -static void WindowRideMeasurementsDesignSave(WindowBase* w) -{ - TrackDesignState tds{}; - - Ride* ride = GetRide(w->rideId); - _trackDesign = ride->SaveToTrackDesign(tds); - if (!_trackDesign) - { - return; - } - - if (gTrackDesignSaveMode) - { - auto errMessage = _trackDesign->CreateTrackDesignScenery(tds); - if (!errMessage.Successful) - { - ContextShowError(STR_CANT_SAVE_TRACK_DESIGN, errMessage.Message, {}); - return; - } - if (errMessage.HasMessage()) - { - ContextShowError(errMessage.Message, STR_EMPTY, {}); - } - } - - auto trackName = ride->GetName(); - auto intent = Intent(WindowClass::Loadsave); - intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); - intent.PutExtra(INTENT_EXTRA_TRACK_DESIGN, _trackDesign.get()); - intent.PutExtra(INTENT_EXTRA_PATH, trackName); - intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(&TrackDesignCallback)); - - ContextOpenIntent(&intent); -} - -/** - * - * rct2: 0x006AD4DA - */ -static void WindowRideMeasurementsClose(WindowBase* w) -{ - WindowRideMeasurementsDesignCancel(); -} - -/** - * - * rct2: 0x006AD478 - */ -static void WindowRideMeasurementsMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_SELECT_NEARBY_SCENERY: - WindowRideMeasurementsDesignSelectNearbyScenery(); - break; - case WIDX_RESET_SELECTION: - WindowRideMeasurementsDesignReset(); - break; - case WIDX_SAVE_DESIGN: - WindowRideMeasurementsDesignSave(w); - break; - case WIDX_CANCEL_DESIGN: - WindowRideMeasurementsDesignCancel(); - break; - } -} - -/** - * - * rct2: 0x006AD564 - */ -static void WindowRideMeasurementsResize(WindowBase* w) -{ - WindowSetResize(*w, 316, 234, 316, 234); -} - -/** - * - * rct2: 0x006AD4AB - */ -static void WindowRideMeasurementsMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) - return; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - gDropdownItems[0].Format = STR_SAVE_TRACK_DESIGN_ITEM; - gDropdownItems[1].Format = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM; - - WindowDropdownShowText( - { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], - Dropdown::Flag::StayOpen, 2); - gDropdownDefaultIndex = 0; - if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) - { - // Disable saving without scenery if we're a flat ride - Dropdown::SetDisabled(0, true); - gDropdownDefaultIndex = 1; - } -} - -/** - * - * rct2: 0x006AD4B2 - */ -static void WindowRideMeasurementsDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) - return; - - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - if (dropdownIndex == 0) - { - WindowRideMeasurementsDesignSave(w); - } - else - SetupScenerySelection(w); -} - -/** - * - * rct2: 0x006AD5DD - */ -static void WindowRideMeasurementsUpdate(WindowBase* w) -{ - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_7); -} - -/** - * - * rct2: 0x006D2AE7 - */ -static void WindowRideMeasurementsTooldown(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - _lastSceneryX = screenCoords.x; - _lastSceneryY = screenCoords.y; - _collectTrackDesignScenery = true; // Default to true in case user does not select anything valid - - constexpr auto flags = EnumsToFlags( - ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, - ViewportInteractionItem::LargeScenery); - auto info = GetMapCoordinatesFromPos(screenCoords, flags); - switch (info.SpriteType) - { - case ViewportInteractionItem::Scenery: - case ViewportInteractionItem::LargeScenery: - case ViewportInteractionItem::Wall: - case ViewportInteractionItem::Footpath: - _collectTrackDesignScenery = !TrackDesignSaveContainsTileElement(info.Element); - TrackDesignSaveSelectTileElement(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery); - break; - default: - break; - } -} - -static void WindowRideMeasurementsTooldrag(WindowBase* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - if (screenCoords.x == _lastSceneryX && screenCoords.y == _lastSceneryY) - return; - _lastSceneryX = screenCoords.x; - _lastSceneryY = screenCoords.y; - - auto flags = EnumsToFlags( - ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, - ViewportInteractionItem::LargeScenery); - auto info = GetMapCoordinatesFromPos(screenCoords, flags); - switch (info.SpriteType) - { - case ViewportInteractionItem::Scenery: - case ViewportInteractionItem::LargeScenery: - case ViewportInteractionItem::Wall: - case ViewportInteractionItem::Footpath: - TrackDesignSaveSelectTileElement(info.SpriteType, info.Loc, info.Element, _collectTrackDesignScenery); - break; - default: - break; - } -} - -/** - * - * rct2: 0x006AD4DA - */ -static void WindowRideMeasurementsToolabort(WindowBase* w, WidgetIndex widgetIndex) -{ - WindowRideMeasurementsDesignCancel(); -} - -/** - * - * rct2: 0x006ACDBC - */ -static void WindowRideMeasurementsInvalidate(WindowBase* w) -{ - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - const auto rideId = w->rideId; - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE; - window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::Empty; - if (gTrackDesignSaveMode && gTrackDesignSaveRideIndex == rideId) - { - window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Button; - window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Button; - window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Button; - window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Button; - } - else - { - window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Empty; - window_ride_measurements_widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Empty; - window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Empty; - window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Empty; - - window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::FlatBtn; - w->disabled_widgets |= (1uLL << WIDX_SAVE_TRACK_DESIGN); - if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) - { - if (ride->excitement != RIDE_RATING_UNDEFINED) - { - w->disabled_widgets &= ~(1uLL << WIDX_SAVE_TRACK_DESIGN); - window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN; - } - } - } - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); -} - -/** - * - * rct2: 0x006ACF07 - */ -static void WindowRideMeasurementsPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - if (window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type == WindowWidgetType::Button) - { - Widget* widget = &window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND]; - - ScreenCoordsXY widgetCoords(w->windowPos.x + widget->width() / 2, w->windowPos.y + widget->top + 40); - DrawTextWrapped(dpi, widgetCoords, w->width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, {}, { TextAlignment::CENTRE }); - - widgetCoords.x = w->windowPos.x + 4; - widgetCoords.y = w->windowPos.y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; - GfxFillRectInset( - dpi, { widgetCoords, { w->windowPos.x + 312, widgetCoords.y + 1 } }, w->colours[1], INSET_RECT_FLAG_BORDER_INSET); - } - else - { - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto screenCoords = w->windowPos - + ScreenCoordsXY{ window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].left + 4, - window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - - if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) - { - // Excitement - StringId ratingName = GetRatingName(ride->excitement); - auto ft = Formatter(); - ft.Add(ride->excitement); - ft.Add(ratingName); - StringId stringId = !RideHasRatings(*ride) ? STR_EXCITEMENT_RATING_NOT_YET_AVAILABLE : STR_EXCITEMENT_RATING; - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Intensity - ratingName = GetRatingName(ride->intensity); - ft = Formatter(); - ft.Add(ride->intensity); - ft.Add(ratingName); - - stringId = STR_INTENSITY_RATING; - if (!RideHasRatings(*ride)) - stringId = STR_INTENSITY_RATING_NOT_YET_AVAILABLE; - else if (ride->intensity >= RIDE_RATING(10, 00)) - stringId = STR_INTENSITY_RATING_RED; - - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Nausea - ratingName = GetRatingName(ride->nausea); - ft = Formatter(); - ft.Add(ride->nausea); - ft.Add(ratingName); - stringId = !RideHasRatings(*ride) ? STR_NAUSEA_RATING_NOT_YET_AVAILABLE : STR_NAUSEA_RATING; - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += 2 * LIST_ROW_HEIGHT; - - // Horizontal rule - GfxFillRectInset( - dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 303, -5 } }, w->colours[1], - INSET_RECT_FLAG_BORDER_INSET); - - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_NO_RAW_STATS)) - { - if (ride->type == RIDE_TYPE_MINI_GOLF) - { - // Holes - ft = Formatter(); - ft.Add(ride->holes); - DrawTextBasic(dpi, screenCoords, STR_HOLES, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - else - { - // Max speed - ft = Formatter(); - ft.Add((ride->max_speed * 9) >> 18); - DrawTextBasic(dpi, screenCoords, STR_MAX_SPEED, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Average speed - ft = Formatter(); - ft.Add((ride->average_speed * 9) >> 18); - DrawTextBasic(dpi, screenCoords, STR_AVERAGE_SPEED, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Ride time - ft = Formatter(); - int32_t numTimes = 0; - // TODO: STR_RIDE_TIME only takes up to 4 stations modify to take more - // also if modified may need to be split into multiple format strings - // as formatter cannot take more than 256 bytes - for (int32_t i = 0; i < std::min(ride->num_stations, 4); i++) - { - StationIndex stationIndex = StationIndex::FromUnderlying(numTimes); - auto time = ride->GetStation(stationIndex).SegmentTime; - if (time != 0) - { - ft.Add(STR_RIDE_TIME_ENTRY_WITH_SEPARATOR); - ft.Add(time); - numTimes++; - } - } - if (numTimes == 0) - { - ft.Add(STR_RIDE_TIME_ENTRY); - ft.Add(0); - numTimes++; - } - else - { - // sadly, STR_RIDE_TIME_ENTRY_WITH_SEPARATOR are defined with the separator AFTER an entry - // therefore we set the last entry to use the no-separator format now, post-format - ft.Rewind(); - ft.Increment((numTimes - 1) * 4); - ft.Add(STR_RIDE_TIME_ENTRY); - } - ft.Rewind(); - ft.Increment(numTimes * 4); - ft.Add(0); - ft.Add(0); - ft.Add(0); - ft.Add(0); - DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_TIME, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - // Ride length - ft = Formatter(); - int32_t numLengths = 0; - // TODO: see above STR_RIDE_LENGTH is also only able to display max 4 - for (int32_t i = 0; i < std::min(ride->num_stations, 4); i++) - { - StationIndex stationIndex = StationIndex::FromUnderlying(i); - auto length = ride->GetStation(stationIndex).SegmentLength; - if (length != 0) - { - length >>= 16; - ft.Add(STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR); - ft.Add(length & 0xFFFF); - numLengths++; - } - } - if (numLengths == 0) - { - ft.Add(STR_RIDE_LENGTH_ENTRY); - ft.Add(0); - numLengths++; - } - else - { - // sadly, STR_RIDE_LENGTH_ENTRY_WITH_SEPARATOR are defined with the separator AFTER an entry - // therefore we set the last entry to use the no-separator format now, post-format - ft.Rewind(); - ft.Increment((numLengths - 1) * 4); - ft.Add(STR_RIDE_LENGTH_ENTRY); - } - ft.Rewind(); - ft.Increment(numLengths * 4); - ft.Add(0); - ft.Add(0); - ft.Add(0); - ft.Add(0); - DrawTextEllipsised(dpi, screenCoords, 308, STR_RIDE_LENGTH, ft); - - screenCoords.y += LIST_ROW_HEIGHT; - - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) - { - // Max. positive vertical G's - stringId = STR_MAX_POSITIVE_VERTICAL_G; - - ft = Formatter(); - ft.Add(ride->max_positive_vertical_g); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Max. negative vertical G's - stringId = ride->max_negative_vertical_g <= RIDE_G_FORCES_RED_NEG_VERTICAL ? STR_MAX_NEGATIVE_VERTICAL_G_RED - : STR_MAX_NEGATIVE_VERTICAL_G; - ft = Formatter(); - ft.Add(ride->max_negative_vertical_g); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Max lateral G's - stringId = ride->max_lateral_g > RIDE_G_FORCES_RED_LATERAL ? STR_MAX_LATERAL_G_RED : STR_MAX_LATERAL_G; - ft = Formatter(); - ft.Add(ride->max_lateral_g); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Total 'air' time - ft = Formatter(); - ft.Add(ride->total_air_time * 3); - DrawTextBasic(dpi, screenCoords, STR_TOTAL_AIR_TIME, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_DROPS)) - { - // Drops - auto drops = ride->drops & 0x3F; - ft = Formatter(); - ft.Add(drops); - DrawTextBasic(dpi, screenCoords, STR_DROPS, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Highest drop height - auto highestDropHeight = (ride->highest_drop_height * 3) / 4; - ft = Formatter(); - ft.Add(highestDropHeight); - DrawTextBasic(dpi, screenCoords, STR_HIGHEST_DROP_HEIGHT, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - if (ride->type != RIDE_TYPE_MINI_GOLF) - { - // Inversions - if (ride->inversions != 0) - { - ft = Formatter(); - ft.Add(ride->inversions); - DrawTextBasic(dpi, screenCoords, STR_INVERSIONS, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - } - } - } - else - { - DrawTextBasic(dpi, screenCoords, STR_NO_TEST_RESULTS_YET); - } - } -} - -#pragma endregion - -#pragma region Graphs - -enum -{ - GRAPH_VELOCITY, - GRAPH_ALTITUDE, - GRAPH_VERTICAL, - GRAPH_LATERAL -}; - -/** - * - * rct2: 0x006AE8A6 - */ -static void WindowRideSetGraph(WindowBase* w, int32_t type) -{ - if ((w->list_information_type & 0xFF) == type) - { - w->list_information_type ^= 0x8000; - } - else - { - w->list_information_type &= 0xFF00; - w->list_information_type |= type; - } - w->Invalidate(); -} - -/** - * - * rct2: 0x006AE85D - */ -static void WindowRideGraphsMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - } -} - -/** - * - * rct2: 0x006AE8DA - */ -static void WindowRideGraphsResize(WindowBase* w) -{ - WindowSetResize(*w, 316, 182, 500, 450); -} - -/** - * - * rct2: 0x006AE878 - */ -static void WindowRideGraphsMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - switch (widgetIndex) - { - case WIDX_GRAPH_VELOCITY: - WindowRideSetGraph(w, GRAPH_VELOCITY); - break; - case WIDX_GRAPH_ALTITUDE: - WindowRideSetGraph(w, GRAPH_ALTITUDE); - break; - case WIDX_GRAPH_VERTICAL: - WindowRideSetGraph(w, GRAPH_VERTICAL); - break; - case WIDX_GRAPH_LATERAL: - WindowRideSetGraph(w, GRAPH_LATERAL); - break; - } -} - -/** - * - * rct2: 0x006AE95D - */ -static void WindowRideGraphsUpdate(WindowBase* w) -{ - Widget* widget; - int32_t x; - - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_8); - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_GRAPH); - - widget = &window_ride_graphs_widgets[WIDX_GRAPH]; - x = w->scrolls[0].h_left; - if (!(w->list_information_type & 0x8000)) - { - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - RideMeasurement* measurement{}; - std::tie(measurement, std::ignore) = ride->GetMeasurement(); - x = measurement == nullptr ? 0 : measurement->current_item - ((widget->width() / 4) * 3); - } - } - - w->scrolls[0].h_left = std::clamp(x, 0, w->scrolls[0].h_right - (widget->width() - 2)); - WidgetScrollUpdateThumbs(*w, WIDX_GRAPH); -} - -/** - * - * rct2: 0x006AEA75 - */ -static void WindowRideGraphsScrollgetheight(WindowBase* w, int32_t scrollIndex, int32_t* width, int32_t* height) -{ - WindowEventInvalidateCall(w); - - // Set minimum size - *width = window_ride_graphs_widgets[WIDX_GRAPH].width() - 2; - - // Get measurement size - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - RideMeasurement* measurement{}; - std::tie(measurement, std::ignore) = ride->GetMeasurement(); - if (measurement != nullptr) - { - *width = std::max(*width, measurement->num_items); - } - } -} - -/** - * - * rct2: 0x006AE953 - */ -static void WindowRideGraphs15(WindowBase* w, int32_t scrollIndex, int32_t scrollAreaType) -{ - w->list_information_type |= 0x8000; -} - -/** - * - * rct2: 0x006AEA05 - */ -static OpenRCT2String WindowRideGraphsTooltip(WindowBase* w, const WidgetIndex widgetIndex, const StringId fallback) -{ - if (widgetIndex == WIDX_GRAPH) - { - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - auto [measurement, message] = ride->GetMeasurement(); - if (measurement != nullptr && (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING)) - { - auto ft = Formatter(); - ft.Increment(2); - ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number); - ft.Add(measurement->vehicle_index + 1); - return { fallback, ft }; - } - - return message; - } - } - else - { - return { STR_NONE, {} }; - } - return { fallback, {} }; -} - -/** - * - * rct2: 0x006AE372 - */ -static void WindowRideGraphsInvalidate(WindowBase* w) -{ - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - // Set pressed graph button type - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_VELOCITY); - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_ALTITUDE); - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_VERTICAL); - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_LATERAL); - w->pressed_widgets |= (1LL << (WIDX_GRAPH_VELOCITY + (w->list_information_type & 0xFF))); - - // Hide graph buttons that are not applicable - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) - { - window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Button; - window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Button; - } - else - { - window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Empty; - window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Empty; - } - - // Anchor graph widget - auto x = w->width - 4; - auto y = w->height - BUTTON_FACE_HEIGHT - 8; - - window_ride_graphs_widgets[WIDX_GRAPH].right = x; - window_ride_graphs_widgets[WIDX_GRAPH].bottom = y; - y += 3; - window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].top = y; - window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].top = y; - window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].top = y; - window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].top = y; - y += BUTTON_FACE_HEIGHT + 1; - window_ride_graphs_widgets[WIDX_GRAPH_VELOCITY].bottom = y; - window_ride_graphs_widgets[WIDX_GRAPH_ALTITUDE].bottom = y; - window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].bottom = y; - window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].bottom = y; - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); -} - -/** - * - * rct2: 0x006AE4BC - */ -static void WindowRideGraphsPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); -} - -/** - * - * rct2: 0x006AE4C7 - */ -static void WindowRideGraphsScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex) -{ - GfxClear(&dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker); - - auto widget = &window_ride_graphs_widgets[WIDX_GRAPH]; - auto ride = GetRide(w->rideId); - if (ride == nullptr) - { - return; - } - - auto [measurement, message] = ride->GetMeasurement(); - - if (measurement == nullptr) - { - // No measurement message - ScreenCoordsXY stringCoords(widget->width() / 2, widget->height() / 2 - 5); - int32_t width = widget->width() - 2; - DrawTextWrapped(dpi, stringCoords, width, message.str, message.args, { TextAlignment::CENTRE }); - return; - } - - // Vertical grid lines - const uint8_t lightColour = ColourMapA[COLOUR_SATURATED_GREEN].mid_light; - const uint8_t darkColour = ColourMapA[COLOUR_SATURATED_GREEN].mid_dark; - - int32_t time = 0; - for (int32_t x = 0; x < dpi.x + dpi.width; x += 80) - { - if (x + 80 >= dpi.x) - { - auto coord1 = ScreenCoordsXY{ x, dpi.y }; - auto coord2 = ScreenCoordsXY{ x, dpi.y + dpi.height - 1 }; - GfxFillRect(dpi, { coord1, coord2 }, lightColour); - GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 16, 0 }, coord2 + ScreenCoordsXY{ 16, 0 } }, darkColour); - GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 32, 0 }, coord2 + ScreenCoordsXY{ 32, 0 } }, darkColour); - GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 48, 0 }, coord2 + ScreenCoordsXY{ 48, 0 } }, darkColour); - GfxFillRect(dpi, { coord1 + ScreenCoordsXY{ 64, 0 }, coord2 + ScreenCoordsXY{ 64, 0 } }, darkColour); - } - time += 5; - } - - // Horizontal grid lines - int32_t listType = w->list_information_type & 0xFF; - int16_t yUnit = window_graphs_y_axi[listType].unit; - StringId stringID = window_graphs_y_axi[listType].label; - int16_t yUnitInterval = window_graphs_y_axi[listType].unit_interval; - int16_t yInterval = window_graphs_y_axi[listType].interval; - - // Scale modifier - if (listType == GRAPH_ALTITUDE) - { - yUnit -= gMapBaseZ * 3; - } - - for (int32_t y = widget->height() - 13; y >= 8; y -= yInterval, yUnit += yUnitInterval) - { - // Minor / major line - int32_t colour = yUnit == 0 ? lightColour : darkColour; - GfxFillRect(dpi, { { dpi.x, y }, { dpi.x + dpi.width - 1, y } }, colour); - - int16_t scaled_yUnit = yUnit; - // Scale modifier - if (listType == GRAPH_ALTITUDE) - scaled_yUnit /= 2; - - auto ft = Formatter(); - ft.Add(scaled_yUnit); - - DrawTextBasic(dpi, { w->scrolls[0].h_left + 1, y - 4 }, stringID, ft, { FontStyle::Small }); - } - - // Time marks - time = 0; - for (int32_t x = 0; x < dpi.x + dpi.width; x += 80) - { - auto ft = Formatter(); - ft.Add(time); - if (x + 80 >= dpi.x) - DrawTextBasic(dpi, { x + 2, 1 }, STR_RIDE_STATS_TIME, ft, { FontStyle::Small }); - time += 5; - } - - // Plot - int32_t x = dpi.x; - int32_t firstPoint, secondPoint; - // Uses the force limits (used to draw extreme G's in red on measurement tab) to determine if line should be drawn red. - int32_t intensityThresholdPositive = 0; - int32_t intensityThresholdNegative = 0; - for (int32_t width = 0; width < dpi.width; width++, x++) - { - if (x < 0 || x >= measurement->num_items - 1) - continue; - - constexpr int32_t VerticalGraphHeightOffset = 39; - constexpr int32_t LateralGraphHeightOffset = 52; - - switch (listType) - { - case GRAPH_VELOCITY: - firstPoint = measurement->velocity[x] / 2; - secondPoint = measurement->velocity[x + 1] / 2; - break; - case GRAPH_ALTITUDE: - firstPoint = measurement->altitude[x]; - secondPoint = measurement->altitude[x + 1]; - break; - case GRAPH_VERTICAL: - firstPoint = measurement->vertical[x] + VerticalGraphHeightOffset; - secondPoint = measurement->vertical[x + 1] + VerticalGraphHeightOffset; - intensityThresholdNegative = (RIDE_G_FORCES_RED_NEG_VERTICAL / 8) + VerticalGraphHeightOffset; - break; - case GRAPH_LATERAL: - firstPoint = measurement->lateral[x] + LateralGraphHeightOffset; - secondPoint = measurement->lateral[x + 1] + LateralGraphHeightOffset; - intensityThresholdPositive = (RIDE_G_FORCES_RED_LATERAL / 8) + LateralGraphHeightOffset; - intensityThresholdNegative = -(RIDE_G_FORCES_RED_LATERAL / 8) + LateralGraphHeightOffset; - break; - default: - LOG_ERROR("Wrong graph type %d", listType); - firstPoint = secondPoint = 0; - break; - } - - // Adjust line to match graph widget position. - firstPoint = widget->height() - firstPoint - 13; - secondPoint = widget->height() - secondPoint - 13; - if (firstPoint > secondPoint) - { - std::swap(firstPoint, secondPoint); - } - - // Adjust threshold line position as well - if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL) - { - intensityThresholdPositive = widget->height() - intensityThresholdPositive - 13; - intensityThresholdNegative = widget->height() - intensityThresholdNegative - 13; - } - - const bool previousMeasurement = x > measurement->current_item; - - // Draw the current line in grey. - GfxFillRect(dpi, { { x, firstPoint }, { x, secondPoint } }, previousMeasurement ? PALETTE_INDEX_17 : PALETTE_INDEX_21); - - // Draw red over extreme values (if supported by graph type). - if (listType == GRAPH_VERTICAL || listType == GRAPH_LATERAL) - { - const auto redLineColour = previousMeasurement ? PALETTE_INDEX_171 : PALETTE_INDEX_173; - - // Line exceeds negative threshold (at bottom of graph). - if (secondPoint >= intensityThresholdNegative) - { - const auto redLineTop = ScreenCoordsXY{ x, std::max(firstPoint, intensityThresholdNegative) }; - const auto redLineBottom = ScreenCoordsXY{ x, std::max(secondPoint, intensityThresholdNegative) }; - GfxFillRect(dpi, { redLineTop, redLineBottom }, redLineColour); - } - - // Line exceeds positive threshold (at top of graph). - if (listType == GRAPH_LATERAL && firstPoint < intensityThresholdPositive) - { - const auto redLineTop = ScreenCoordsXY{ x, std::min(firstPoint, intensityThresholdPositive) }; - const auto redLineBottom = ScreenCoordsXY{ x, std::min(secondPoint, intensityThresholdPositive) }; - GfxFillRect(dpi, { redLineTop, redLineBottom }, redLineColour); - } - } - } -} - -#pragma endregion - -#pragma region Income - -static utf8 _moneyInputText[MONEY_STRING_MAXLENGTH]; - -static void UpdateSamePriceThroughoutFlags(ShopItem shop_item) -{ - if (GetShopItemDescriptor(shop_item).IsPhoto()) - { - auto newFlags = gSamePriceThroughoutPark; - if (gSamePriceThroughoutPark & EnumToFlag(shop_item)) - newFlags &= ~EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); - else - newFlags |= EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); - auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); - GameActions::Execute(&parkSetParameter); - } - else - { - auto newFlags = gSamePriceThroughoutPark; - newFlags ^= EnumToFlag(shop_item); - auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); - GameActions::Execute(&parkSetParameter); - } -} - -/** - * - * rct2: 0x006ADEFD - */ -static void WindowRideIncomeTogglePrimaryPrice(WindowBase* w) -{ - const auto rideId = w->rideId; - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - ShopItem shop_item; - const auto& rtd = ride->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET)) - { - shop_item = ShopItem::Admission; - } - else - { - auto rideEntry = GetRideEntryByIndex(ride->subtype); - if (rideEntry != nullptr) - { - shop_item = rideEntry->shop_item[0]; - if (shop_item == ShopItem::None) - return; - } - else - { - return; - } - } - - UpdateSamePriceThroughoutFlags(shop_item); - - auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[0], true); - GameActions::Execute(&rideSetPriceAction); -} - -/** - * - * rct2: 0x006AE06E - */ -static void WindowRideIncomeToggleSecondaryPrice(WindowBase* w) -{ - const auto rideId = w->rideId; - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto rideEntry = GetRideEntryByIndex(ride->subtype); - if (rideEntry == nullptr) - return; - - auto shop_item = rideEntry->shop_item[1]; - if (shop_item == ShopItem::None) - shop_item = ride->GetRideTypeDescriptor().PhotoItem; - - UpdateSamePriceThroughoutFlags(shop_item); - - auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[1], false); - GameActions::Execute(&rideSetPriceAction); -} - -static void WindowRideIncomeSetPrimaryPrice(WindowBase* w, money64 price) -{ - auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, true); - GameActions::Execute(&rideSetPriceAction); -} - -/** - * - * rct2: 0x006AE1E4 - */ -static void WindowRideIncomeIncreasePrimaryPrice(WindowBase* w) -{ - if (!WindowRideIncomeCanModifyPrimaryPrice(w)) - return; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto price = ride->price[0]; - if (price < 20.00_GBP) - price++; - - WindowRideIncomeSetPrimaryPrice(w, price); -} - -/** - * - * rct2: 0x006AE237 - */ -static void WindowRideIncomeDecreasePrimaryPrice(WindowBase* w) -{ - if (!WindowRideIncomeCanModifyPrimaryPrice(w)) - return; - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto price = ride->price[0]; - if (price > 0.00_GBP) - price--; - - WindowRideIncomeSetPrimaryPrice(w, price); -} - -static money64 WindowRideIncomeGetSecondaryPrice(WindowBase* w) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return 0; - - return ride->price[1]; -} - -static void WindowRideIncomeSetSecondaryPrice(WindowBase* w, money64 price) -{ - auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, false); - GameActions::Execute(&rideSetPriceAction); -} - -static bool WindowRideIncomeCanModifyPrimaryPrice(WindowBase* w) -{ - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return false; - - auto rideEntry = ride->GetRideEntry(); - const auto& rtd = ride->GetRideTypeDescriptor(); - return ParkRidePricesUnlocked() || rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET) - || (rideEntry != nullptr && rideEntry->shop_item[0] != ShopItem::None); -} - -/** - * - * rct2: 0x006AE269 - */ -static void WindowRideIncomeIncreaseSecondaryPrice(WindowBase* w) -{ - auto price = WindowRideIncomeGetSecondaryPrice(w); - - if (price < 20.00_GBP) - price++; - - WindowRideIncomeSetSecondaryPrice(w, price); -} - -/** - * - * rct2: 0x006AE28D - */ -static void WindowRideIncomeDecreaseSecondaryPrice(WindowBase* w) -{ - auto price = WindowRideIncomeGetSecondaryPrice(w); - - if (price > 0.00_GBP) - price--; - - WindowRideIncomeSetSecondaryPrice(w, price); -} - -/** - * - * rct2: 0x006ADEA9 - */ -static void WindowRideIncomeMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_PRIMARY_PRICE: - { - if (!WindowRideIncomeCanModifyPrimaryPrice(w)) - return; - - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - MoneyToString(ride->price[0], _moneyInputText, MONEY_STRING_MAXLENGTH, true); - WindowTextInputRawOpen( - w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, - MONEY_STRING_MAXLENGTH); - } - break; - } - case WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK: - WindowRideIncomeTogglePrimaryPrice(w); - break; - case WIDX_SECONDARY_PRICE: - { - auto price64 = WindowRideIncomeGetSecondaryPrice(w); - - MoneyToString(price64, _moneyInputText, MONEY_STRING_MAXLENGTH, true); - WindowTextInputRawOpen( - w, WIDX_SECONDARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, MONEY_STRING_MAXLENGTH); - } - break; - case WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK: - WindowRideIncomeToggleSecondaryPrice(w); - break; - } -} - -/** - * - * rct2: 0x006AE2F8 - */ -static void WindowRideIncomeResize(WindowBase* w) -{ - WindowSetResize(*w, 316, 194, 316, 194); -} - -/** - * - * rct2: 0x006ADED4 - */ -static void WindowRideIncomeMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - switch (widgetIndex) - { - case WIDX_PRIMARY_PRICE_INCREASE: - WindowRideIncomeIncreasePrimaryPrice(w); - break; - case WIDX_PRIMARY_PRICE_DECREASE: - WindowRideIncomeDecreasePrimaryPrice(w); - break; - case WIDX_SECONDARY_PRICE_INCREASE: - WindowRideIncomeIncreaseSecondaryPrice(w); - break; - case WIDX_SECONDARY_PRICE_DECREASE: - WindowRideIncomeDecreaseSecondaryPrice(w); - break; - } -} - -/** - * - * rct2: 0x006AE2BF - */ -static void WindowRideIncomeUpdate(WindowBase* w) -{ - w->frame_no++; - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_9); - - auto ride = GetRide(w->rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) - { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME; - w->Invalidate(); - } -} - -static void WindowRideIncomeTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) -{ - if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text == nullptr) - return; - - money64 price = StringToMoney(text); - if (price == MONEY64_UNDEFINED) - { - return; - } - - price = std::clamp(price, 0.00_GBP, 20.00_GBP); - - if (widgetIndex == WIDX_PRIMARY_PRICE) - { - WindowRideIncomeSetPrimaryPrice(w, price); - } - else - { - WindowRideIncomeSetSecondaryPrice(w, price); - } -} - -/** - * - * rct2: 0x006ADAA3 - */ -static void WindowRideIncomeInvalidate(WindowBase* w) -{ - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - w->widgets[WIDX_TITLE].text = STR_ARG_18_STRINGID; - - auto ft = Formatter::Common(); - ft.Increment(18); - ride->FormatNameTo(ft); - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - // Primary item - w->pressed_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); - w->disabled_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE); - - window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_NONE; - window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_NONE; - - // If ride prices are locked, do not allow setting the price, unless we're dealing with a shop or toilet. - const auto& rtd = ride->GetRideTypeDescriptor(); - if (!ParkRidePricesUnlocked() && rideEntry->shop_item[0] == ShopItem::None && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET)) - { - w->disabled_widgets |= (1uLL << WIDX_PRIMARY_PRICE); - window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; - window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; - } - - window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = STR_RIDE_INCOME_ADMISSION_PRICE; - window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO; - window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; - - window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_BOTTOM_TOOLBAR_CASH; - auto ridePrimaryPrice = RideGetPrice(*ride); - ft.Rewind(); - ft.Add(ridePrimaryPrice); - if (ridePrimaryPrice == 0) - window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_FREE; - - ShopItem primaryItem = ShopItem::Admission; - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET) || ((primaryItem = rideEntry->shop_item[0]) != ShopItem::None)) - { - window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; - - if (ShopItemHasCommonPrice(primaryItem)) - w->pressed_widgets |= (1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); - - window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = GetShopItemDescriptor(primaryItem).Naming.PriceLabel; - } - - // Get secondary item - auto secondaryItem = ride->GetRideTypeDescriptor().PhotoItem; - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) - { - if ((secondaryItem = rideEntry->shop_item[1]) != ShopItem::None) - { - window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = GetShopItemDescriptor(secondaryItem) - .Naming.PriceLabel; - } - } - - if (secondaryItem == ShopItem::None) - { - // Hide secondary item widgets - window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Empty; - window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Empty; - window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Empty; - window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Empty; - window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; - } - else - { - // Set same price throughout park checkbox - w->pressed_widgets &= ~(1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); - if (ShopItemHasCommonPrice(secondaryItem)) - w->pressed_widgets |= (1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); - - // Show widgets - window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Label; - window_ride_income_widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Spinner; - window_ride_income_widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Button; - window_ride_income_widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Button; - window_ride_income_widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; - - // Set secondary item price - window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_RIDE_SECONDARY_PRICE_VALUE; - ft.Rewind(); - ft.Increment(10); - ft.Add(ride->price[1]); - if (ride->price[1] == 0) - window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_FREE; - } - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); -} - -/** - * - * rct2: 0x006ADCE5 - */ -static void WindowRideIncomePaint(WindowBase* w, DrawPixelInfo& dpi) -{ - StringId stringId; - money64 profit; - ShopItem primaryItem, secondaryItem; - - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - auto screenCoords = w->windowPos - + ScreenCoordsXY{ window_ride_income_widgets[WIDX_PAGE_BACKGROUND].left + 4, - window_ride_income_widgets[WIDX_PAGE_BACKGROUND].top + 33 }; - - // Primary item profit / loss per item sold - primaryItem = rideEntry->shop_item[0]; - if (primaryItem != ShopItem::None) - { - profit = ride->price[0]; - - stringId = STR_PROFIT_PER_ITEM_SOLD; - profit -= GetShopItemDescriptor(primaryItem).Cost; - if (profit < 0) - { - profit *= -1; - stringId = STR_LOSS_PER_ITEM_SOLD; - } - - auto ft = Formatter(); - ft.Add(profit); - - DrawTextBasic(dpi, screenCoords, stringId, ft); - } - screenCoords.y += 44; - - // Secondary item profit / loss per item sold - secondaryItem = ride->GetRideTypeDescriptor().PhotoItem; - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) - secondaryItem = rideEntry->shop_item[1]; - - if (secondaryItem != ShopItem::None) - { - profit = ride->price[1]; - - stringId = STR_PROFIT_PER_ITEM_SOLD; - profit -= GetShopItemDescriptor(secondaryItem).Cost; - if (profit < 0) - { - profit *= -1; - stringId = STR_LOSS_PER_ITEM_SOLD; - } - - auto ft = Formatter(); - ft.Add(profit); - - DrawTextBasic(dpi, screenCoords, stringId, ft); - } - screenCoords.y += 18; - - // Income per hour - if (ride->income_per_hour != MONEY64_UNDEFINED) - { - auto ft = Formatter(); - ft.Add(ride->income_per_hour); - - DrawTextBasic(dpi, screenCoords, STR_INCOME_PER_HOUR, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - // Running cost per hour - money64 costPerHour = ride->upkeep_cost * 16; - stringId = ride->upkeep_cost == MONEY64_UNDEFINED ? STR_RUNNING_COST_UNKNOWN : STR_RUNNING_COST_PER_HOUR; - auto ft = Formatter(); - ft.Add(costPerHour); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Profit per hour - if (ride->profit != MONEY64_UNDEFINED) - { - ft = Formatter(); - ft.Add(ride->profit); - DrawTextBasic(dpi, screenCoords, STR_PROFIT_PER_HOUR, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - screenCoords.y += 5; - - // Total profit - ft = Formatter(); - ft.Add(ride->total_profit); - DrawTextBasic(dpi, screenCoords, STR_TOTAL_PROFIT, ft); -} - -#pragma endregion - -#pragma region Customer - -/** - * - * rct2: 0x006AD986 - */ -static void WindowRideCustomerMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - case WIDX_TAB_5: - case WIDX_TAB_6: - case WIDX_TAB_7: - case WIDX_TAB_8: - case WIDX_TAB_9: - case WIDX_TAB_10: - WindowRideSetPage(w, widgetIndex - WIDX_TAB_1); - break; - case WIDX_SHOW_GUESTS_THOUGHTS: - { - auto intent = Intent(WindowClass::GuestList); - intent.PutExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsThinkingAboutRide)); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); - ContextOpenIntent(&intent); - break; - } - case WIDX_SHOW_GUESTS_ON_RIDE: - { - auto intent = Intent(WindowClass::GuestList); - intent.PutExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsOnRide)); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); - ContextOpenIntent(&intent); - break; - } - case WIDX_SHOW_GUESTS_QUEUING: - { - auto intent = Intent(WindowClass::GuestList); - intent.PutExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsInQueue)); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); - ContextOpenIntent(&intent); - break; - } - } -} - -/** - * - * rct2: 0x006ADA29 - */ -static void WindowRideCustomerResize(WindowBase* w) -{ - w->flags |= WF_RESIZABLE; - WindowSetResize(*w, 316, 163, 316, 163); -} - -/** - * - * rct2: 0x006AD9DD - */ -static void WindowRideCustomerUpdate(WindowBase* w) -{ - w->picked_peep_frame++; - if (w->picked_peep_frame >= 24) - w->picked_peep_frame = 0; - - WindowEventInvalidateCall(w); - WidgetInvalidate(*w, WIDX_TAB_10); - - auto ride = GetRide(w->rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER) - { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER; - w->Invalidate(); - } -} - -/** - * - * rct2: 0x006AD5F8 - */ -static void WindowRideCustomerInvalidate(WindowBase* w) -{ - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) - { - w->widgets = widgets; - WindowInitScrollWidgets(*w); - } - - WindowRideSetPressedTab(w); - - auto ride = GetRide(w->rideId); - if (ride != nullptr) - { - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - window_ride_customer_widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WindowWidgetType::FlatBtn; - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) - { - window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::Empty; - window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::Empty; - } - else - { - window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::FlatBtn; - window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::FlatBtn; - } - - WindowRideAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); - } -} - -/** - * - * rct2: 0x006AD6CD - */ -static void WindowRideCustomerPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - ShopItem shopItem; - int16_t popularity, satisfaction, queueTime; - StringId stringId; - - WindowDrawWidgets(*w, dpi); - WindowRideDrawTabImages(dpi, w); - - auto ride = GetRide(w->rideId); - if (ride == nullptr) - return; - - auto screenCoords = w->windowPos - + ScreenCoordsXY{ window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].left + 4, - window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - - // Customers currently on ride - if (ride->IsRide()) - { - auto ft = Formatter(); - ft.Add(ride->num_riders); - DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_ON_RIDE, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - // Customers per hour - auto ft = Formatter(); - ft.Add(RideCustomersPerHour(*ride)); - DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_PER_HOUR, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Popularity - popularity = ride->popularity; - if (popularity == 255) - { - stringId = STR_POPULARITY_UNKNOWN; - } - else - { - stringId = STR_POPULARITY_PERCENT; - popularity *= 4; - } - ft = Formatter(); - ft.Add(popularity); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Satisfaction - satisfaction = ride->satisfaction; - if (satisfaction == 255) - { - stringId = STR_SATISFACTION_UNKNOWN; - } - else - { - stringId = STR_SATISFACTION_PERCENT; - satisfaction *= 5; - } - ft = Formatter(); - ft.Add(satisfaction); - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Queue time - if (ride->IsRide()) - { - queueTime = ride->GetMaxQueueTime(); - stringId = queueTime == 1 ? STR_QUEUE_TIME_MINUTE : STR_QUEUE_TIME_MINUTES; - ft = Formatter(); - ft.Add(queueTime); - screenCoords.y += DrawTextWrapped(dpi, screenCoords, 308, stringId, ft, { TextAlignment::LEFT }); - screenCoords.y += 5; - } - - // Primary shop items sold - shopItem = ride->GetRideEntry()->shop_item[0]; - if (shopItem != ShopItem::None) - { - ft = Formatter(); - ft.Add(GetShopItemDescriptor(shopItem).Naming.Plural); - ft.Add(ride->no_primary_items_sold); - DrawTextBasic(dpi, screenCoords, STR_ITEMS_SOLD, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - // Secondary shop items sold / on-ride photos sold - shopItem = (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? ride->GetRideTypeDescriptor().PhotoItem - : ride->GetRideEntry()->shop_item[1]; - if (shopItem != ShopItem::None) - { - ft = Formatter(); - ft.Add(GetShopItemDescriptor(shopItem).Naming.Plural); - ft.Add(ride->no_secondary_items_sold); - DrawTextBasic(dpi, screenCoords, STR_ITEMS_SOLD, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - // Total customers - ft = Formatter(); - ft.Add(ride->total_customers); - DrawTextBasic(dpi, screenCoords, STR_TOTAL_CUSTOMERS, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - // Guests favourite - if (ride->IsRide()) - { - ft = Formatter(); - ft.Add(ride->guests_favourite); - stringId = ride->guests_favourite == 1 ? STR_FAVOURITE_RIDE_OF_GUEST : STR_FAVOURITE_RIDE_OF_GUESTS; - DrawTextBasic(dpi, screenCoords, stringId, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - screenCoords.y += 2; - - // Age - // If the ride has a build date that is in the future, show it as built this year. - int16_t age = std::max(DateGetYear(ride->GetAge()), 0); - stringId = age == 0 ? STR_BUILT_THIS_YEAR : age == 1 ? STR_BUILT_LAST_YEAR : STR_BUILT_YEARS_AGO; - ft = Formatter(); - ft.Add(age); - DrawTextBasic(dpi, screenCoords, stringId, ft); -} - -#pragma endregion From 825634360b77daa76a5d5289c1573be744b2f653 Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Fri, 16 Jun 2023 08:13:20 +0100 Subject: [PATCH 2/5] Finish initial sweep --- src/openrct2-ui/windows/Ride.cpp | 1075 ++++++++++-------------------- 1 file changed, 366 insertions(+), 709 deletions(-) diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 121f4cbe26..39a46ab0ab 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -2645,13 +2645,9 @@ private: #pragma region Operating - /** - * - * rct2: 0x006B11D5 - */ void ModeTweakIncrease() { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -2667,16 +2663,12 @@ private: uint8_t increment = ride->mode == RideMode::Dodgems ? 10 : 1; SetOperatingSetting( - w->rideId, RideSetSetting::Operation, std::clamp(ride->operation_option + increment, minValue, maxValue)); + rideId, RideSetSetting::Operation, std::clamp(ride->operation_option + increment, minValue, maxValue)); } - /** - * - * rct2: 0x006B120A - */ void ModeTweakDecrease() { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -2691,19 +2683,15 @@ private: uint8_t decrement = ride->mode == RideMode::Dodgems ? 10 : 1; SetOperatingSetting( - w->rideId, RideSetSetting::Operation, std::clamp(ride->operation_option - decrement, minValue, maxValue)); + rideId, RideSetSetting::Operation, std::clamp(ride->operation_option - decrement, minValue, maxValue)); } - /** - * - * rct2: 0x006B1631 - */ void ModeDropdown(Widget* widget) { Widget* dropdownWidget; dropdownWidget = widget - 1; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -2727,8 +2715,8 @@ private: } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, numAvailableModes, widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], + 0, Dropdown::Flag::StayOpen, numAvailableModes, widget->right - dropdownWidget->left); if (checkedIndex != -1) { @@ -2736,13 +2724,9 @@ private: } } - /** - * - * rct2: 0x006B15C0 - */ void LoadDropdown(Widget* widget) { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -2753,19 +2737,14 @@ private: gDropdownItems[i].Args = VehicleLoadNames[i]; } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, 5, widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], + 0, Dropdown::Flag::StayOpen, 5, widget->right - dropdownWidget->left); Dropdown::SetChecked(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK, true); } - /** - * - * rct2: 0x006B10A7 - */ void OperatingMouseup(WidgetIndex widgetIndex) { - const auto rideId = w->rideId; auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -2773,8 +2752,8 @@ private: switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -2785,7 +2764,7 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; case WIDX_LOAD_CHECKBOX: SetOperatingSetting(rideId, RideSetSetting::Departure, ride->depart_flags ^ RIDE_DEPART_WAIT_FOR_LOAD); @@ -2809,22 +2788,13 @@ private: } } - /** - * - * rct2: 0x006B1715 - */ void OperatingResize() { - WindowSetResize(*w, 316, 186, 316, 186); + WindowSetResize(*this, 316, 186, 316, 186); } - /** - * - * rct2: 0x006B10F4 - */ void OperatingMousedown(WidgetIndex widgetIndex, Widget* widget) { - const auto rideId = w->rideId; auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -2833,13 +2803,13 @@ private: switch (widgetIndex) { case WIDX_MODE_TWEAK: - OperatingTweakTextInput(w, *ride); + OperatingTweakTextInput(*ride); break; case WIDX_MODE_TWEAK_INCREASE: - ModeTweakIncrease(w); + ModeTweakIncrease(); break; case WIDX_MODE_TWEAK_DECREASE: - ModeTweakDecrease(w); + ModeTweakDecrease(); break; case WIDX_LIFT_HILL_SPEED_INCREASE: upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit @@ -2858,10 +2828,10 @@ private: std::clamp(ride->lift_hill_speed - 1, lower_bound, upper_bound)); break; case WIDX_MINIMUM_LENGTH: - OperatingLengthWindow(w, WIDX_MINIMUM_LENGTH); + OperatingLengthWindow(WIDX_MINIMUM_LENGTH); break; case WIDX_MAXIMUM_LENGTH: - OperatingLengthWindow(w, WIDX_MAXIMUM_LENGTH); + OperatingLengthWindow(WIDX_MAXIMUM_LENGTH); break; case WIDX_MINIMUM_LENGTH_INCREASE: upper_bound = OpenRCT2::Limits::MaxWaitingTime; @@ -2892,10 +2862,10 @@ private: std::clamp(ride->max_waiting_time - 1, lower_bound, upper_bound)); break; case WIDX_MODE_DROPDOWN: - ModeDropdown(w, widget); + ModeDropdown(widget); break; case WIDX_LOAD_DROPDOWN: - LoadDropdown(w, widget); + LoadDropdown(widget); break; case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE: upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit @@ -2916,7 +2886,7 @@ private: void OperatingLengthWindow(WidgetIndex widgetIndex) { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -2929,7 +2899,7 @@ private: auto currentValue = (widgetIndex == WIDX_MINIMUM_LENGTH) ? ride->min_waiting_time : ride->max_waiting_time; char buffer[5]{}; snprintf(buffer, std::size(buffer), "%u", currentValue); - WindowTextInputRawOpen(w, widgetIndex, title, STR_ENTER_VALUE, ft, buffer, 4); + WindowTextInputRawOpen(this, widgetIndex, title, STR_ENTER_VALUE, ft, buffer, 4); } void OperatingTweakTextInput(const Ride& ride) @@ -2961,19 +2931,14 @@ private: char buffer[6]{}; snprintf(buffer, std::size(buffer), "%u", currentValue); - WindowTextInputRawOpen(w, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4); + WindowTextInputRawOpen(this, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4); } - /** - * - * rct2: 0x006B1165 - */ void OperatingDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; - const auto rideId = w->rideId; auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3008,21 +2973,17 @@ private: } } - /** - * - * rct2: 0x006B178E - */ void OperatingUpdate() { - w->frame_no++; + frame_no++; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_3); + WidgetInvalidate(*this, WIDX_TAB_3); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING; - w->Invalidate(); + Invalidate(); } } @@ -3031,7 +2992,7 @@ private: if (text == nullptr) return; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3074,25 +3035,20 @@ private: } } - /** - * - * rct2: 0x006B0B30 - */ void OperatingOnPrepareDraw() { - Widget* widgets; StringId format, caption, tooltip; - widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto* newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = newWidgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3100,7 +3056,7 @@ private: ride->FormatNameTo(ft); // Widget setup - w->pressed_widgets &= ~( + pressed_widgets &= ~( (1uLL << WIDX_LOAD_CHECKBOX) | (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX) | (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX) | (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX) | (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX)); @@ -3207,7 +3163,7 @@ private: ft.Add(ride->max_waiting_time); if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD) - w->pressed_widgets |= (1uLL << WIDX_LOAD_CHECKBOX); + pressed_widgets |= (1uLL << WIDX_LOAD_CHECKBOX); } else { @@ -3227,13 +3183,13 @@ private: } if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES) - w->pressed_widgets |= (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); + pressed_widgets |= (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) - w->pressed_widgets |= (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX); + pressed_widgets |= (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX); if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) - w->pressed_widgets |= (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX); + pressed_widgets |= (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX); if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH) - w->pressed_widgets |= (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX); + pressed_widgets |= (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX); // Mode specific functionality auto multiplier = ride->GetRideTypeDescriptor().OperatingSettings.OperatingSettingMultiplier; @@ -3304,7 +3260,7 @@ private: window_ride_operating_widgets[WIDX_MODE_TWEAK].text = format; window_ride_operating_widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Button; window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Button; - w->pressed_widgets &= ~(1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); + pressed_widgets &= ~(1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); } else { @@ -3314,29 +3270,25 @@ private: window_ride_operating_widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Empty; } - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - /** - * - * rct2: 0x006B1001 - */ void OperatingPaint(DrawPixelInfo& dpi) { - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; // Horizontal rule between mode settings and depart settings GfxFillRectInset( dpi, - { w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 }, - w->windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } }, - w->colours[1], INSET_RECT_FLAG_BORDER_INSET); + { windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 }, + windowPos + ScreenCoordsXY{ window_ride_operating_widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } }, + colours[1], INSET_RECT_FLAG_BORDER_INSET); // Number of block sections if (ride->IsBlockSectioned()) @@ -3344,7 +3296,7 @@ private: auto ft = Formatter(); ft.Add(ride->num_block_brakes + ride->num_stations); DrawTextBasic( - dpi, w->windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 }, + dpi, windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 }, STR_BLOCK_SECTIONS, ft, COLOUR_BLACK); } } @@ -3353,13 +3305,9 @@ private: #pragma region Maintenance - /** - * - * rct2: 0x006B1AE4 - */ void LocateMechanic() { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3380,14 +3328,9 @@ private: } } - /** - * - * rct2: 0x006B7D08 - */ - void MaintenanceDrawBar( - WindowBase* w, DrawPixelInfo& dpi, const ScreenCoordsXY& coords, int32_t value, int32_t colour) + void MaintenanceDrawBar(DrawPixelInfo& dpi, const ScreenCoordsXY& coords, int32_t value, int32_t colour) const { - GfxFillRectInset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, w->colours[1], INSET_RECT_F_30); + GfxFillRectInset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, colours[1], INSET_RECT_F_30); if (colour & BAR_BLINK) { colour &= ~BAR_BLINK; @@ -3402,17 +3345,13 @@ private: } } - /** - * - * rct2: 0x006B1AAD - */ void MaintenanceMouseup(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -3423,33 +3362,25 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; case WIDX_LOCATE_MECHANIC: - LocateMechanic(w); + LocateMechanic(); break; case WIDX_REFURBISH_RIDE: - ContextOpenDetailWindow(WD_REFURBISH_RIDE, w->number); + ContextOpenDetailWindow(WD_REFURBISH_RIDE, number); break; } } - /** - * - * rct2: 0x006B1D70 - */ void MaintenanceResize() { - WindowSetResize(*w, 316, 135, 316, 135); + WindowSetResize(*this, 316, 135, 316, 135); } - /** - * - * rct2: 0x006B1ACE - */ void MaintenanceMousedown(WidgetIndex widgetIndex, Widget* widget) { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3470,9 +3401,8 @@ private: gDropdownItems[i].Args = RideInspectionIntervalNames[i]; } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, 7, - widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 7, widget->right - dropdownWidget->left); Dropdown::SetChecked(ride->inspection_interval, true); break; @@ -3508,8 +3438,8 @@ private: else { WindowDropdownShowText( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], Dropdown::Flag::StayOpen, num_items); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], Dropdown::Flag::StayOpen, num_items); num_items = 1; int32_t breakdownReason = ride->breakdown_reason_pending; @@ -3546,16 +3476,12 @@ private: } } - /** - * - * rct2: 0x006B1AD9 - */ void MaintenanceDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3566,7 +3492,7 @@ private: switch (widgetIndex) { case WIDX_INSPECTION_INTERVAL_DROPDOWN: - SetOperatingSetting(w->rideId, RideSetSetting::InspectionInterval, dropdownIndex); + SetOperatingSetting(rideId, RideSetSetting::InspectionInterval, dropdownIndex); break; case WIDX_FORCE_BREAKDOWN: @@ -3607,7 +3533,7 @@ private: break; } ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN); - WindowInvalidateByNumber(WindowClass::Ride, w->number); + WindowInvalidateByNumber(WindowClass::Ride, number); break; } if (ride->lifecycle_flags @@ -3650,40 +3576,32 @@ private: } } - /** - * - * rct2: 0x006B1D37 - */ void MaintenanceUpdate() { - w->frame_no++; + frame_no++; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_4); + WidgetInvalidate(*this, WIDX_TAB_4); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE; - w->Invalidate(); + Invalidate(); } } - /** - * - * rct2: 0x006B17C8 - */ void MaintenanceOnPrepareDraw() { - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = newWidgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3692,8 +3610,8 @@ private: window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval]; - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); if (gConfigGeneral.DebuggingTools && NetworkGetMode() == NETWORK_MODE_NONE) { @@ -3707,57 +3625,53 @@ private: if (ride->GetRideTypeDescriptor().AvailableBreakdowns == 0 || !(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) { - w->disabled_widgets |= (1uLL << WIDX_REFURBISH_RIDE); + disabled_widgets |= (1uLL << WIDX_REFURBISH_RIDE); window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_CANT_REFURBISH_NOT_NEEDED; } else { - w->disabled_widgets &= ~(1uLL << WIDX_REFURBISH_RIDE); + disabled_widgets &= ~(1uLL << WIDX_REFURBISH_RIDE); window_ride_maintenance_widgets[WIDX_REFURBISH_RIDE].tooltip = STR_REFURBISH_RIDE_TIP; } } - /** - * - * rct2: 0x006B1877 - */ void MaintenancePaint(DrawPixelInfo& dpi) { - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; // Locate mechanic button image Widget* widget = &window_ride_maintenance_widgets[WIDX_LOCATE_MECHANIC]; - auto screenCoords = w->windowPos + ScreenCoordsXY{ widget->left, widget->top }; + auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; auto image = ImageId(SPR_MECHANIC, COLOUR_BLACK, gStaffMechanicColour); GfxDrawSprite(dpi, image, screenCoords); // Inspection label widget = &window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL]; - screenCoords = w->windowPos + ScreenCoordsXY{ 4, widget->top + 1 }; + screenCoords = windowPos + ScreenCoordsXY{ 4, widget->top + 1 }; DrawTextBasic(dpi, screenCoords, STR_INSPECTION); // Reliability widget = &window_ride_maintenance_widgets[WIDX_PAGE_BACKGROUND]; - screenCoords = w->windowPos + ScreenCoordsXY{ widget->left + 4, widget->top + 4 }; + screenCoords = windowPos + ScreenCoordsXY{ widget->left + 4, widget->top + 4 }; uint16_t reliability = ride->reliability_percentage; auto ft = Formatter(); ft.Add(reliability); DrawTextBasic(dpi, screenCoords, STR_RELIABILITY_LABEL_1757, ft); MaintenanceDrawBar( - w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, std::max(10, reliability), COLOUR_BRIGHT_GREEN); + dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, std::max(10, reliability), COLOUR_BRIGHT_GREEN); screenCoords.y += 11; uint16_t downTime = ride->downtime; ft = Formatter(); ft.Add(downTime); DrawTextBasic(dpi, screenCoords, STR_DOWN_TIME_LABEL_1889, ft); - MaintenanceDrawBar(w, dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, downTime, COLOUR_BRIGHT_RED); + MaintenanceDrawBar(dpi, screenCoords + ScreenCoordsXY{ 103, 0 }, downTime, COLOUR_BRIGHT_RED); screenCoords.y += 26; // Last inspection @@ -3869,14 +3783,14 @@ private: void SetTrackColourScheme(const ScreenCoordsXY& screenPos) { - auto newColourScheme = static_cast(w->ride_colour); + auto newColourScheme = static_cast(ride_colour); auto info = GetMapCoordinatesFromPos(screenPos, EnumsToFlags(ViewportInteractionItem::Ride)); if (info.SpriteType != ViewportInteractionItem::Ride) return; if (info.Element->GetType() != TileElementType::Track) return; - if (info.Element->AsTrack()->GetRideIndex() != w->rideId) + if (info.Element->AsTrack()->GetRideIndex() != rideId) return; if (info.Element->AsTrack()->GetColourScheme() == newColourScheme) return; @@ -3889,35 +3803,27 @@ private: GameActions::Execute(&gameAction); } - /** - * - * rct2: 0x006B04FA - */ void ColourClose() { if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) return; - if (gCurrentToolWidget.window_classification != w->classification) + if (gCurrentToolWidget.window_classification != classification) return; - if (gCurrentToolWidget.window_number != w->number) + if (gCurrentToolWidget.window_number != number) return; ToolCancel(); } - /** - * - * rct2: 0x006B02A1 - */ void ColourMouseup(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -3928,44 +3834,36 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; case WIDX_PAINT_INDIVIDUAL_AREA: - ToolSet(*w, WIDX_PAINT_INDIVIDUAL_AREA, Tool::PaintDown); + ToolSet(*this, WIDX_PAINT_INDIVIDUAL_AREA, Tool::PaintDown); break; case WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX: - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr) { const bool currentlyEnabled = ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS); auto rideSetAppearanceAction = RideSetAppearanceAction( - w->rideId, RideSetAppearanceType::SellingItemColourIsRandom, currentlyEnabled ? 0 : 1, 0); + rideId, RideSetAppearanceType::SellingItemColourIsRandom, currentlyEnabled ? 0 : 1, 0); GameActions::Execute(&rideSetAppearanceAction); } break; } } - /** - * - * rct2: 0x006B0AB6 - */ void ColourResize() { - WindowSetResize(*w, 316, 207, 316, 207); + WindowSetResize(*this, 316, 207, 316, 207); } - /** - * - * rct2: 0x006B02C6 - */ void ColourMousedown(WidgetIndex widgetIndex, Widget* widget) { VehicleColour vehicleColour; int32_t i, numItems; StringId stringId; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -3973,7 +3871,7 @@ private: if (rideEntry == nullptr) return; - auto colourSchemeIndex = w->ride_colour; + auto colourSchemeIndex = ride_colour; auto dropdownWidget = widget - 1; switch (widgetIndex) @@ -3986,20 +3884,19 @@ private: } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, 4, - widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left); Dropdown::SetChecked(colourSchemeIndex, true); break; case WIDX_TRACK_MAIN_COLOUR: - WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].main); + WindowDropdownShowColour(this, widget, colours[1], ride->track_colour[colourSchemeIndex].main); break; case WIDX_TRACK_ADDITIONAL_COLOUR: - WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].additional); + WindowDropdownShowColour(this, widget, colours[1], ride->track_colour[colourSchemeIndex].additional); break; case WIDX_TRACK_SUPPORT_COLOUR: - WindowDropdownShowColour(w, widget, w->colours[1], ride->track_colour[colourSchemeIndex].supports); + WindowDropdownShowColour(this, widget, colours[1], ride->track_colour[colourSchemeIndex].supports); break; case WIDX_MAZE_STYLE_DROPDOWN: for (i = 0; i < 4; i++) @@ -4009,9 +3906,8 @@ private: } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, 4, - widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left); Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true); break; @@ -4035,9 +3931,8 @@ private: } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, - widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, widget->right - dropdownWidget->left); break; } case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: @@ -4051,9 +3946,9 @@ private: } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, - rideEntry->max_cars_in_train > 1 ? 3 : 2, widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, rideEntry->max_cars_in_train > 1 ? 3 : 2, + widget->right - dropdownWidget->left); Dropdown::SetChecked(ride->colour_scheme_type & 3, true); break; @@ -4073,47 +3968,41 @@ private: } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, - dropdownWidget->height() + 1, w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, - widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); - Dropdown::SetChecked(w->vehicleIndex, true); + Dropdown::SetChecked(vehicleIndex, true); break; case WIDX_VEHICLE_BODY_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Body); + vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); + WindowDropdownShowColour(this, widget, colours[1], vehicleColour.Body); break; case WIDX_VEHICLE_TRIM_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Trim); + vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); + WindowDropdownShowColour(this, widget, colours[1], vehicleColour.Trim); break; case WIDX_VEHICLE_TERNARY_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); - WindowDropdownShowColour(w, widget, w->colours[1], vehicleColour.Tertiary); + vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); + WindowDropdownShowColour(this, widget, colours[1], vehicleColour.Tertiary); break; } } - /** - * - * rct2: 0x006B0331 - */ void ColourDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; - const auto rideId = w->rideId; switch (widgetIndex) { case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: - w->ride_colour = static_cast(dropdownIndex); - w->Invalidate(); + ride_colour = static_cast(dropdownIndex); + Invalidate(); break; case WIDX_TRACK_MAIN_COLOUR: { auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::TrackColourMain, ColourDropDownIndexToColour(dropdownIndex), w->ride_colour); + rideId, RideSetAppearanceType::TrackColourMain, ColourDropDownIndexToColour(dropdownIndex), ride_colour); GameActions::Execute(&rideSetAppearanceAction); } break; @@ -4121,7 +4010,7 @@ private: { auto rideSetAppearanceAction = RideSetAppearanceAction( rideId, RideSetAppearanceType::TrackColourAdditional, ColourDropDownIndexToColour(dropdownIndex), - w->ride_colour); + ride_colour); GameActions::Execute(&rideSetAppearanceAction); } break; @@ -4129,14 +4018,14 @@ private: { auto rideSetAppearanceAction = RideSetAppearanceAction( rideId, RideSetAppearanceType::TrackColourSupports, ColourDropDownIndexToColour(dropdownIndex), - w->ride_colour); + ride_colour); GameActions::Execute(&rideSetAppearanceAction); } break; case WIDX_MAZE_STYLE_DROPDOWN: { auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::MazeStyle, dropdownIndex, w->ride_colour); + rideId, RideSetAppearanceType::MazeStyle, dropdownIndex, ride_colour); GameActions::Execute(&rideSetAppearanceAction); } break; @@ -4171,26 +4060,24 @@ private: auto rideSetAppearanceAction = RideSetAppearanceAction( rideId, RideSetAppearanceType::VehicleColourScheme, dropdownIndex, 0); GameActions::Execute(&rideSetAppearanceAction); - w->vehicleIndex = 0; + vehicleIndex = 0; } break; case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: - w->vehicleIndex = dropdownIndex; - w->Invalidate(); + vehicleIndex = dropdownIndex; + Invalidate(); break; case WIDX_VEHICLE_BODY_COLOUR: { auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourBody, ColourDropDownIndexToColour(dropdownIndex), - w->vehicleIndex); + rideId, RideSetAppearanceType::VehicleColourBody, ColourDropDownIndexToColour(dropdownIndex), vehicleIndex); GameActions::Execute(&rideSetAppearanceAction); } break; case WIDX_VEHICLE_TRIM_COLOUR: { auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourTrim, ColourDropDownIndexToColour(dropdownIndex), - w->vehicleIndex); + rideId, RideSetAppearanceType::VehicleColourTrim, ColourDropDownIndexToColour(dropdownIndex), vehicleIndex); GameActions::Execute(&rideSetAppearanceAction); } break; @@ -4198,64 +4085,48 @@ private: { auto rideSetAppearanceAction = RideSetAppearanceAction( rideId, RideSetAppearanceType::VehicleColourTernary, ColourDropDownIndexToColour(dropdownIndex), - w->vehicleIndex); + vehicleIndex); GameActions::Execute(&rideSetAppearanceAction); } break; } } - /** - * - * rct2: 0x006B0A8F - */ void ColourUpdate() { - w->frame_no++; + frame_no++; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_5); - WidgetInvalidate(*w, WIDX_VEHICLE_PREVIEW); + WidgetInvalidate(*this, WIDX_TAB_5); + WidgetInvalidate(*this, WIDX_VEHICLE_PREVIEW); } - /** - * - * rct2: 0x006B04EC - */ void ColourTooldown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) { if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) - SetTrackColourScheme(w, screenCoords); + SetTrackColourScheme(screenCoords); } - /** - * - * rct2: 0x006B04F3 - */ void ColourTooldrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) { if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) - SetTrackColourScheme(w, screenCoords); + SetTrackColourScheme(screenCoords); } - /** - * - * rct2: 0x006AFB36 - */ void ColourOnPrepareDraw() { TrackColour trackColour; VehicleColour vehicleColour; - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = newWidgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -4263,13 +4134,13 @@ private: if (rideEntry == nullptr) return; - w->widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; + widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; auto ft = Formatter::Common(); ft.Increment(16); ride->FormatNameTo(ft); // Track colours - int32_t colourScheme = w->ride_colour; + int32_t colourScheme = ride_colour; trackColour = ride->track_colour[colourScheme]; // Maze style @@ -4328,11 +4199,11 @@ private: window_ride_colour_widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Checkbox; if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS)) { - w->pressed_widgets |= (1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); + pressed_widgets |= (1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); } else { - w->pressed_widgets &= ~(1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); + pressed_widgets &= ~(1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); } } else @@ -4387,9 +4258,9 @@ private: { int32_t vehicleColourSchemeType = ride->colour_scheme_type & 3; if (vehicleColourSchemeType == 0) - w->vehicleIndex = 0; + vehicleIndex = 0; - vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); + vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Scroll; window_ride_colour_widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::ColourBtn; @@ -4451,7 +4322,7 @@ private: ft.Add(VehicleColourSchemeNames[vehicleColourSchemeType]); ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).singular); ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).capitalised); - ft.Add(w->vehicleIndex + 1); + ft.Add(vehicleIndex + 1); // Vehicle index if (vehicleColourSchemeType != 0) @@ -4484,42 +4355,39 @@ private: ft.Increment(14); ft.Add(ColourSchemeNames[colourScheme]); - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - /** - * - * rct2: 0x006AFF3E - */ + void ColourPaint(DrawPixelInfo& dpi) { // TODO: This should use lists and identified sprites DrawPixelInfo clippedDpi; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); // Track / shop item preview const auto& trackPreviewWidget = window_ride_colour_widgets[WIDX_TRACK_PREVIEW]; if (trackPreviewWidget.type != WindowWidgetType::Empty) GfxFillRect( dpi, - { { w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left + 1, trackPreviewWidget.top + 1 } }, - { w->windowPos + ScreenCoordsXY{ trackPreviewWidget.right - 1, trackPreviewWidget.bottom - 1 } } }, + { { windowPos + ScreenCoordsXY{ trackPreviewWidget.left + 1, trackPreviewWidget.top + 1 } }, + { windowPos + ScreenCoordsXY{ trackPreviewWidget.right - 1, trackPreviewWidget.bottom - 1 } } }, PALETTE_INDEX_12); - auto trackColour = ride->track_colour[w->ride_colour]; + auto trackColour = ride->track_colour[ride_colour]; // auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr || rideEntry->shop_item[0] == ShopItem::None) { - auto screenCoords = w->windowPos + ScreenCoordsXY{ trackPreviewWidget.left, trackPreviewWidget.top }; + auto screenCoords = windowPos + ScreenCoordsXY{ trackPreviewWidget.left, trackPreviewWidget.top }; // Track const auto& rtd = ride->GetRideTypeDescriptor(); @@ -4546,7 +4414,7 @@ private: } else { - auto screenCoords = w->windowPos + auto screenCoords = windowPos + ScreenCoordsXY{ (trackPreviewWidget.left + trackPreviewWidget.right) / 2 - 8, (trackPreviewWidget.bottom + trackPreviewWidget.top) / 2 - 6 }; @@ -4574,12 +4442,12 @@ private: // Entrance preview trackColour = ride->track_colour[0]; - const auto& entrancePreviewWidget = w->widgets[WIDX_ENTRANCE_PREVIEW]; + const auto& entrancePreviewWidget = widgets[WIDX_ENTRANCE_PREVIEW]; if (entrancePreviewWidget.type != WindowWidgetType::Empty) { if (ClipDrawPixelInfo( clippedDpi, dpi, - w->windowPos + ScreenCoordsXY{ entrancePreviewWidget.left + 1, entrancePreviewWidget.top + 1 }, + windowPos + ScreenCoordsXY{ entrancePreviewWidget.left + 1, entrancePreviewWidget.top + 1 }, entrancePreviewWidget.width(), entrancePreviewWidget.height())) { GfxClear(&clippedDpi, PALETTE_INDEX_12); @@ -4604,17 +4472,13 @@ private: } } - DrawTextEllipsised(dpi, { w->windowPos.x + 3, w->windowPos.y + 103 }, 97, STR_STATION_STYLE, {}); + DrawTextEllipsised(dpi, { windowPos.x + 3, windowPos.y + 103 }, 97, STR_STATION_STYLE, {}); } } - /** - * - * rct2: 0x006B0192 - */ - void ColourScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) + void ColourScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) const { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -4623,7 +4487,7 @@ private: return; auto vehiclePreviewWidget = &window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW]; - auto vehicleColour = RideGetVehicleColour(*ride, w->vehicleIndex); + auto vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); // Background colour GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, PALETTE_INDEX_12); @@ -4632,7 +4496,7 @@ private: auto screenCoords = ScreenCoordsXY{ vehiclePreviewWidget->width() / 2, vehiclePreviewWidget->height() - 15 }; // ? - auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR ? w->vehicleIndex + auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR ? vehicleIndex : rideEntry->TabCar; const auto& carEntry = rideEntry->Cars[RideEntryGetVehicleAtPosition( @@ -4641,8 +4505,8 @@ private: screenCoords.y += carEntry.tab_height; // Draw the coloured spinning vehicle - // w->frame_no represents a SpritePrecision of 64 - ImageIndex imageIndex = carEntry.SpriteByYaw(w->frame_no / 2, SpriteGroupType::SlopeFlat); + // frame_no represents a SpritePrecision of 64 + ImageIndex imageIndex = carEntry.SpriteByYaw(frame_no / 2, SpriteGroupType::SlopeFlat); imageIndex &= carEntry.TabRotationMask; imageIndex *= carEntry.base_num_frames; imageIndex += carEntry.base_image_id; @@ -4655,31 +4519,23 @@ private: #pragma region Music std::vector window_ride_current_music_style_order; - /** - * - * rct2: 0x006B215D - */ void ToggleMusic() { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr) { int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; - SetOperatingSetting(w->rideId, RideSetSetting::Music, activateMusic); + SetOperatingSetting(rideId, RideSetSetting::Music, activateMusic); } } - /** - * - * rct2: 0x006B1ED7 - */ void MusicMouseup(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -4690,25 +4546,21 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; case WIDX_PLAY_MUSIC: - ToggleMusic(w); + ToggleMusic(); break; } } - /** - * - * rct2: 0x006AF4A2 - */ void MusicResize() { - w->flags |= WF_RESIZABLE; - WindowSetResize(*w, 316, 81, 316, 81); + flags |= WF_RESIZABLE; + WindowSetResize(*this, 316, 81, 316, 81); } - std::string GetMusicString(ObjectEntryIndex musicObjectIndex) + static std::string GetMusicString(ObjectEntryIndex musicObjectIndex) { auto& objManager = GetContext()->GetObjectManager(); auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, musicObjectIndex)); @@ -4727,7 +4579,7 @@ private: return; auto dropdownWidget = widget - 1; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -4782,8 +4634,8 @@ private: } WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); // Set currently checked item for (size_t i = 0; i < numItems; i++) @@ -4795,47 +4647,35 @@ private: } } - /** - * - * rct2: 0x006B1F03 - */ void MusicDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0 && static_cast(dropdownIndex) < window_ride_current_music_style_order.size()) { auto musicStyle = window_ride_current_music_style_order[dropdownIndex]; - SetOperatingSetting(w->rideId, RideSetSetting::MusicType, musicStyle); + SetOperatingSetting(rideId, RideSetSetting::MusicType, musicStyle); } } - /** - * - * rct2: 0x006B2198 - */ void MusicUpdate() { - w->frame_no++; + frame_no++; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_6); + WidgetInvalidate(*this, WIDX_TAB_6); } - /** - * - * rct2: 0x006B1DEA - */ void MusicOnPrepareDraw() { - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = newWidgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -4856,45 +4696,37 @@ private: auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0; if (isMusicActivated) { - w->pressed_widgets |= (1uLL << WIDX_PLAY_MUSIC); - w->disabled_widgets &= ~(1uLL << WIDX_MUSIC); - w->disabled_widgets &= ~(1uLL << WIDX_MUSIC_DROPDOWN); + pressed_widgets |= (1uLL << WIDX_PLAY_MUSIC); + disabled_widgets &= ~(1uLL << WIDX_MUSIC); + disabled_widgets &= ~(1uLL << WIDX_MUSIC_DROPDOWN); } else { - w->pressed_widgets &= ~(1uLL << WIDX_PLAY_MUSIC); - w->disabled_widgets |= (1uLL << WIDX_MUSIC); - w->disabled_widgets |= (1uLL << WIDX_MUSIC_DROPDOWN); + pressed_widgets &= ~(1uLL << WIDX_PLAY_MUSIC); + disabled_widgets |= (1uLL << WIDX_MUSIC); + disabled_widgets |= (1uLL << WIDX_MUSIC_DROPDOWN); } - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - /** - * - * rct2: 0x006B1ECC - */ void MusicPaint(DrawPixelInfo& dpi) { - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); } #pragma endregion #pragma region Measurements - StringId GetRatingName(ride_rating rating) + static constexpr StringId GetRatingName(ride_rating rating) { int32_t index = std::clamp(rating >> 8, 0, static_cast(std::size(RatingNames)) - 1); return RatingNames[index]; } - /** - * - * rct2: 0x006D2804 - when al == 0*/ void CancelScenerySelection() { gGamePaused &= ~GAME_PAUSED_SAVING_TRACK; @@ -4911,10 +4743,6 @@ private: ToolCancel(); } - /** - * - * rct2: 0x006D27A3 - */ void SetupScenerySelection() { if (gTrackDesignSaveMode) @@ -4922,10 +4750,10 @@ private: CancelScenerySelection(); } - while (ToolSet(*w, WIDX_BACKGROUND, Tool::Crosshair)) + while (ToolSet(*this, WIDX_BACKGROUND, Tool::Crosshair)) ; - gTrackDesignSaveRideIndex = w->rideId; + gTrackDesignSaveRideIndex = rideId; TrackDesignSaveInit(); gGamePaused |= GAME_PAUSED_SAVING_TRACK; @@ -4942,28 +4770,16 @@ private: GfxInvalidateScreen(); } - /** - * - * rct2: 0x006D3026 - */ void MeasurementsDesignReset() { TrackDesignSaveResetScenery(); } - /** - * - * rct2: 0x006D303D - */ void MeasurementsDesignSelectNearbyScenery() { TrackDesignSaveSelectNearbyScenery(gTrackDesignSaveRideIndex); } - /** - * - * rct2: 0x006AD4DA - */ void MeasurementsDesignCancel() { if (gTrackDesignSaveMode) @@ -4972,7 +4788,7 @@ private: } } - void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) + static void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) { if (result == MODAL_RESULT_OK) { @@ -4981,15 +4797,11 @@ private: GfxInvalidateScreen(); }; - /** - * - * rct2: 0x006AD4CD - */ void MeasurementsDesignSave() { TrackDesignState tds{}; - Ride* ride = GetRide(w->rideId); + Ride* ride = GetRide(rideId); _trackDesign = ride->SaveToTrackDesign(tds); if (!_trackDesign) { @@ -5020,26 +4832,18 @@ private: ContextOpenIntent(&intent); } - /** - * - * rct2: 0x006AD4DA - */ void MeasurementsClose() { MeasurementsDesignCancel(); } - /** - * - * rct2: 0x006AD478 - */ void MeasurementsMouseup(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -5050,7 +4854,7 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; case WIDX_SELECT_NEARBY_SCENERY: MeasurementsDesignSelectNearbyScenery(); @@ -5059,7 +4863,7 @@ private: MeasurementsDesignReset(); break; case WIDX_SAVE_DESIGN: - MeasurementsDesignSave(w); + MeasurementsDesignSave(); break; case WIDX_CANCEL_DESIGN: MeasurementsDesignCancel(); @@ -5067,25 +4871,17 @@ private: } } - /** - * - * rct2: 0x006AD564 - */ void MeasurementsResize() { - WindowSetResize(*w, 316, 234, 316, 234); + WindowSetResize(*this, 316, 234, 316, 234); } - /** - * - * rct2: 0x006AD4AB - */ void MeasurementsMousedown(WidgetIndex widgetIndex, Widget* widget) { if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) return; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -5093,7 +4889,7 @@ private: gDropdownItems[1].Format = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM; WindowDropdownShowText( - { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], Dropdown::Flag::StayOpen, 2); gDropdownDefaultIndex = 0; if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) @@ -5104,10 +4900,6 @@ private: } } - /** - * - * rct2: 0x006AD4B2 - */ void MeasurementsDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) @@ -5118,27 +4910,19 @@ private: if (dropdownIndex == 0) { - MeasurementsDesignSave(w); + MeasurementsDesignSave(); } else - SetupScenerySelection(w); + SetupScenerySelection(); } - /** - * - * rct2: 0x006AD5DD - */ void MeasurementsUpdate() { - w->frame_no++; + frame_no++; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_7); + WidgetInvalidate(*this, WIDX_TAB_7); } - /** - * - * rct2: 0x006D2AE7 - */ void MeasurementsTooldown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) { _lastSceneryX = screenCoords.x; @@ -5187,31 +4971,22 @@ private: } } - /** - * - * rct2: 0x006AD4DA - */ void MeasurementsToolabort(WidgetIndex widgetIndex) { MeasurementsDesignCancel(); } - /** - * - * rct2: 0x006ACDBC - */ void MeasurementsOnPrepareDraw() { - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto widgets = window_ride_page_widgets[page]; + if (widgets != widgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = widgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - const auto rideId = w->rideId; auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -5236,51 +5011,47 @@ private: window_ride_measurements_widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Empty; window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::FlatBtn; - w->disabled_widgets |= (1uLL << WIDX_SAVE_TRACK_DESIGN); + disabled_widgets |= (1uLL << WIDX_SAVE_TRACK_DESIGN); if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) { if (ride->excitement != RIDE_RATING_UNDEFINED) { - w->disabled_widgets &= ~(1uLL << WIDX_SAVE_TRACK_DESIGN); + disabled_widgets &= ~(1uLL << WIDX_SAVE_TRACK_DESIGN); window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN; } } } - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - /** - * - * rct2: 0x006ACF07 - */ void MeasurementsPaint(DrawPixelInfo& dpi) { - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); if (window_ride_measurements_widgets[WIDX_SAVE_DESIGN].type == WindowWidgetType::Button) { Widget* widget = &window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND]; - ScreenCoordsXY widgetCoords(w->windowPos.x + widget->width() / 2, w->windowPos.y + widget->top + 40); + ScreenCoordsXY widgetCoords(windowPos.x + widget->width() / 2, windowPos.y + widget->top + 40); DrawTextWrapped( - dpi, widgetCoords, w->width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, {}, { TextAlignment::CENTRE }); + dpi, widgetCoords, width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, {}, { TextAlignment::CENTRE }); - widgetCoords.x = w->windowPos.x + 4; - widgetCoords.y = w->windowPos.y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; + widgetCoords.x = windowPos.x + 4; + widgetCoords.y = windowPos.y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; GfxFillRectInset( - dpi, { widgetCoords, { w->windowPos.x + 312, widgetCoords.y + 1 } }, w->colours[1], + dpi, { widgetCoords, { windowPos.x + 312, widgetCoords.y + 1 } }, colours[1], INSET_RECT_FLAG_BORDER_INSET); } else { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; - auto screenCoords = w->windowPos + auto screenCoords = windowPos + ScreenCoordsXY{ window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].left + 4, window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; @@ -5321,7 +5092,7 @@ private: // Horizontal rule GfxFillRectInset( - dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 303, -5 } }, w->colours[1], + dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 303, -5 } }, colours[1], INSET_RECT_FLAG_BORDER_INSET); if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_NO_RAW_STATS)) @@ -5511,35 +5282,27 @@ private: GRAPH_LATERAL }; - /** - * - * rct2: 0x006AE8A6 - */ void SetGraph(int32_t type) { - if ((w->list_information_type & 0xFF) == type) + if ((list_information_type & 0xFF) == type) { - w->list_information_type ^= 0x8000; + list_information_type ^= 0x8000; } else { - w->list_information_type &= 0xFF00; - w->list_information_type |= type; + list_information_type &= 0xFF00; + list_information_type |= type; } - w->Invalidate(); + Invalidate(); } - /** - * - * rct2: 0x006AE85D - */ void GraphsMouseup(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -5550,63 +5313,51 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; } } - /** - * - * rct2: 0x006AE8DA - */ void GraphsResize() { - WindowSetResize(*w, 316, 182, 500, 450); + WindowSetResize(*this, 316, 182, 500, 450); } - /** - * - * rct2: 0x006AE878 - */ void GraphsMousedown(WidgetIndex widgetIndex, Widget* widget) { switch (widgetIndex) { case WIDX_GRAPH_VELOCITY: - SetGraph(w, GRAPH_VELOCITY); + SetGraph(GRAPH_VELOCITY); break; case WIDX_GRAPH_ALTITUDE: - SetGraph(w, GRAPH_ALTITUDE); + SetGraph(GRAPH_ALTITUDE); break; case WIDX_GRAPH_VERTICAL: - SetGraph(w, GRAPH_VERTICAL); + SetGraph(GRAPH_VERTICAL); break; case WIDX_GRAPH_LATERAL: - SetGraph(w, GRAPH_LATERAL); + SetGraph(GRAPH_LATERAL); break; } } - /** - * - * rct2: 0x006AE95D - */ void GraphsUpdate() { Widget* widget; int32_t x; - w->frame_no++; + frame_no++; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_8); + WidgetInvalidate(*this, WIDX_TAB_8); OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_GRAPH); + WidgetInvalidate(*this, WIDX_GRAPH); widget = &window_ride_graphs_widgets[WIDX_GRAPH]; - x = w->scrolls[0].h_left; - if (!(w->list_information_type & 0x8000)) + x = scrolls[0].h_left; + if (!(list_information_type & 0x8000)) { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr) { RideMeasurement* measurement{}; @@ -5615,14 +5366,10 @@ private: } } - w->scrolls[0].h_left = std::clamp(x, 0, w->scrolls[0].h_right - (widget->width() - 2)); - WidgetScrollUpdateThumbs(*w, WIDX_GRAPH); + scrolls[0].h_left = std::clamp(x, 0, scrolls[0].h_right - (widget->width() - 2)); + WidgetScrollUpdateThumbs(*this, WIDX_GRAPH); } - /** - * - * rct2: 0x006AEA75 - */ void GraphsScrollgetheight(int32_t scrollIndex, int32_t* width, int32_t* height) { OnPrepareDraw(); @@ -5631,7 +5378,7 @@ private: *width = window_ride_graphs_widgets[WIDX_GRAPH].width() - 2; // Get measurement size - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr) { RideMeasurement* measurement{}; @@ -5643,24 +5390,16 @@ private: } } - /** - * - * rct2: 0x006AE953 - */ void Graphs15(int32_t scrollIndex, int32_t scrollAreaType) { - w->list_information_type |= 0x8000; + list_information_type |= 0x8000; } - /** - * - * rct2: 0x006AEA05 - */ OpenRCT2String GraphsTooltip(const WidgetIndex widgetIndex, const StringId fallback) { if (widgetIndex == WIDX_GRAPH) { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr) { auto [measurement, message] = ride->GetMeasurement(); @@ -5683,22 +5422,18 @@ private: return { fallback, {} }; } - /** - * - * rct2: 0x006AE372 - */ void GraphsOnPrepareDraw() { - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = newWidgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -5706,11 +5441,11 @@ private: ride->FormatNameTo(ft); // Set pressed graph button type - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_VELOCITY); - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_ALTITUDE); - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_VERTICAL); - w->pressed_widgets &= ~(1uLL << WIDX_GRAPH_LATERAL); - w->pressed_widgets |= (1LL << (WIDX_GRAPH_VELOCITY + (w->list_information_type & 0xFF))); + pressed_widgets &= ~(1uLL << WIDX_GRAPH_VELOCITY); + pressed_widgets &= ~(1uLL << WIDX_GRAPH_ALTITUDE); + pressed_widgets &= ~(1uLL << WIDX_GRAPH_VERTICAL); + pressed_widgets &= ~(1uLL << WIDX_GRAPH_LATERAL); + pressed_widgets |= (1LL << (WIDX_GRAPH_VELOCITY + (list_information_type & 0xFF))); // Hide graph buttons that are not applicable if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) @@ -5725,8 +5460,8 @@ private: } // Anchor graph widget - auto x = w->width - 4; - auto y = w->height - BUTTON_FACE_HEIGHT - 8; + auto x = width - 4; + auto y = height - BUTTON_FACE_HEIGHT - 8; window_ride_graphs_widgets[WIDX_GRAPH].right = x; window_ride_graphs_widgets[WIDX_GRAPH].bottom = y; @@ -5741,30 +5476,22 @@ private: window_ride_graphs_widgets[WIDX_GRAPH_VERTICAL].bottom = y; window_ride_graphs_widgets[WIDX_GRAPH_LATERAL].bottom = y; - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - /** - * - * rct2: 0x006AE4BC - */ void GraphsPaint(DrawPixelInfo& dpi) { - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); } - /** - * - * rct2: 0x006AE4C7 - */ void GraphsScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) { GfxClear(&dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker); auto widget = &window_ride_graphs_widgets[WIDX_GRAPH]; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) { return; @@ -5802,7 +5529,7 @@ private: } // Horizontal grid lines - int32_t listType = w->list_information_type & 0xFF; + int32_t listType = list_information_type & 0xFF; int16_t yUnit = window_graphs_y_axi[listType].unit; StringId stringID = window_graphs_y_axi[listType].label; int16_t yUnitInterval = window_graphs_y_axi[listType].unit_interval; @@ -5828,7 +5555,7 @@ private: auto ft = Formatter(); ft.Add(scaled_yUnit); - DrawTextBasic(dpi, { w->scrolls[0].h_left + 1, y - 4 }, stringID, ft, { FontStyle::Small }); + DrawTextBasic(dpi, { scrolls[0].h_left + 1, y - 4 }, stringID, ft, { FontStyle::Small }); } // Time marks @@ -5955,13 +5682,8 @@ private: } } - /** - * - * rct2: 0x006ADEFD - */ void IncomeTogglePrimaryPrice() { - const auto rideId = w->rideId; auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -5993,13 +5715,8 @@ private: GameActions::Execute(&rideSetPriceAction); } - /** - * - * rct2: 0x006AE06E - */ void IncomeToggleSecondaryPrice() { - const auto rideId = w->rideId; auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -6020,20 +5737,16 @@ private: void IncomeSetPrimaryPrice(money64 price) { - auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, true); + auto rideSetPriceAction = RideSetPriceAction(rideId, price, true); GameActions::Execute(&rideSetPriceAction); } - /** - * - * rct2: 0x006AE1E4 - */ void IncomeIncreasePrimaryPrice() { - if (!IncomeCanModifyPrimaryPrice(w)) + if (!IncomeCanModifyPrimaryPrice()) return; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -6041,19 +5754,15 @@ private: if (price < 20.00_GBP) price++; - IncomeSetPrimaryPrice(w, price); + IncomeSetPrimaryPrice(price); } - /** - * - * rct2: 0x006AE237 - */ void IncomeDecreasePrimaryPrice() { - if (!IncomeCanModifyPrimaryPrice(w)) + if (!IncomeCanModifyPrimaryPrice()) return; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -6061,12 +5770,12 @@ private: if (price > 0.00_GBP) price--; - IncomeSetPrimaryPrice(w, price); + IncomeSetPrimaryPrice(price); } money64 IncomeGetSecondaryPrice() { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return 0; @@ -6075,13 +5784,13 @@ private: void IncomeSetSecondaryPrice(money64 price) { - auto rideSetPriceAction = RideSetPriceAction(w->rideId, price, false); + auto rideSetPriceAction = RideSetPriceAction(rideId, price, false); GameActions::Execute(&rideSetPriceAction); } bool IncomeCanModifyPrimaryPrice() { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return false; @@ -6091,45 +5800,33 @@ private: || (rideEntry != nullptr && rideEntry->shop_item[0] != ShopItem::None); } - /** - * - * rct2: 0x006AE269 - */ void IncomeIncreaseSecondaryPrice() { - auto price = IncomeGetSecondaryPrice(w); + auto price = IncomeGetSecondaryPrice(); if (price < 20.00_GBP) price++; - IncomeSetSecondaryPrice(w, price); + IncomeSetSecondaryPrice(price); } - /** - * - * rct2: 0x006AE28D - */ void IncomeDecreaseSecondaryPrice() { - auto price = IncomeGetSecondaryPrice(w); + auto price = IncomeGetSecondaryPrice(); if (price > 0.00_GBP) price--; - IncomeSetSecondaryPrice(w, price); + IncomeSetSecondaryPrice(price); } - /** - * - * rct2: 0x006ADEA9 - */ void IncomeMouseup(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -6140,89 +5837,77 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; case WIDX_PRIMARY_PRICE: { - if (!IncomeCanModifyPrimaryPrice(w)) + if (!IncomeCanModifyPrimaryPrice()) return; - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr) { MoneyToString(ride->price[0], _moneyInputText, MONEY_STRING_MAXLENGTH, true); WindowTextInputRawOpen( - w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, + this, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, MONEY_STRING_MAXLENGTH); } break; } case WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK: - IncomeTogglePrimaryPrice(w); + IncomeTogglePrimaryPrice(); break; case WIDX_SECONDARY_PRICE: { - auto price64 = IncomeGetSecondaryPrice(w); + auto price64 = IncomeGetSecondaryPrice(); MoneyToString(price64, _moneyInputText, MONEY_STRING_MAXLENGTH, true); WindowTextInputRawOpen( - w, WIDX_SECONDARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, + this, WIDX_SECONDARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneyInputText, MONEY_STRING_MAXLENGTH); } break; case WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK: - IncomeToggleSecondaryPrice(w); + IncomeToggleSecondaryPrice(); break; } } - /** - * - * rct2: 0x006AE2F8 - */ void IncomeResize() { - WindowSetResize(*w, 316, 194, 316, 194); + WindowSetResize(*this, 316, 194, 316, 194); } - /** - * - * rct2: 0x006ADED4 - */ void IncomeMousedown(WidgetIndex widgetIndex, Widget* widget) { switch (widgetIndex) { case WIDX_PRIMARY_PRICE_INCREASE: - IncomeIncreasePrimaryPrice(w); + IncomeIncreasePrimaryPrice(); break; case WIDX_PRIMARY_PRICE_DECREASE: - IncomeDecreasePrimaryPrice(w); + IncomeDecreasePrimaryPrice(); break; case WIDX_SECONDARY_PRICE_INCREASE: - IncomeIncreaseSecondaryPrice(w); + IncomeIncreaseSecondaryPrice(); break; case WIDX_SECONDARY_PRICE_DECREASE: - IncomeDecreaseSecondaryPrice(w); + IncomeDecreaseSecondaryPrice(); break; } } - /** - * - * rct2: 0x006AE2BF - */ void IncomeUpdate() { - w->frame_no++; + frame_no++; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_9); + WidgetInvalidate(*this, WIDX_TAB_9); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME; - w->Invalidate(); + Invalidate(); } } @@ -6241,34 +5926,30 @@ private: if (widgetIndex == WIDX_PRIMARY_PRICE) { - IncomeSetPrimaryPrice(w, price); + IncomeSetPrimaryPrice(price); } else { - IncomeSetSecondaryPrice(w, price); + IncomeSetSecondaryPrice(price); } } - /** - * - * rct2: 0x006ADAA3 - */ void IncomeOnPrepareDraw() { - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = newWidgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; - w->widgets[WIDX_TITLE].text = STR_ARG_18_STRINGID; + widgets[WIDX_TITLE].text = STR_ARG_18_STRINGID; auto ft = Formatter::Common(); ft.Increment(18); @@ -6279,8 +5960,8 @@ private: return; // Primary item - w->pressed_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); - w->disabled_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE); + pressed_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); + disabled_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE); window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_NONE; window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_NONE; @@ -6289,7 +5970,7 @@ private: const auto& rtd = ride->GetRideTypeDescriptor(); if (!ParkRidePricesUnlocked() && rideEntry->shop_item[0] == ShopItem::None && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET)) { - w->disabled_widgets |= (1uLL << WIDX_PRIMARY_PRICE); + disabled_widgets |= (1uLL << WIDX_PRIMARY_PRICE); window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; window_ride_income_widgets[WIDX_PRIMARY_PRICE].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; } @@ -6311,7 +5992,7 @@ private: window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; if (ShopItemHasCommonPrice(primaryItem)) - w->pressed_widgets |= (1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); + pressed_widgets |= (1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = GetShopItemDescriptor(primaryItem).Naming.PriceLabel; } @@ -6339,9 +6020,9 @@ private: else { // Set same price throughout park checkbox - w->pressed_widgets &= ~(1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); + pressed_widgets &= ~(1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); if (ShopItemHasCommonPrice(secondaryItem)) - w->pressed_widgets |= (1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); + pressed_widgets |= (1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); // Show widgets window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Label; @@ -6359,24 +6040,20 @@ private: window_ride_income_widgets[WIDX_SECONDARY_PRICE].text = STR_FREE; } - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - /** - * - * rct2: 0x006ADCE5 - */ void IncomePaint(DrawPixelInfo& dpi) { StringId stringId; money64 profit; ShopItem primaryItem, secondaryItem; - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -6384,7 +6061,7 @@ private: if (rideEntry == nullptr) return; - auto screenCoords = w->windowPos + auto screenCoords = windowPos + ScreenCoordsXY{ window_ride_income_widgets[WIDX_PAGE_BACKGROUND].left + 4, window_ride_income_widgets[WIDX_PAGE_BACKGROUND].top + 33 }; @@ -6471,17 +6148,13 @@ private: #pragma region Customer - /** - * - * rct2: 0x006AD986 - */ void CustomerMouseup(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - WindowClose(*w); - break; + Close(); + return; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: @@ -6492,14 +6165,14 @@ private: case WIDX_TAB_8: case WIDX_TAB_9: case WIDX_TAB_10: - SetPage(w, widgetIndex - WIDX_TAB_1); + SetPage(widgetIndex - WIDX_TAB_1); break; case WIDX_SHOW_GUESTS_THOUGHTS: { auto intent = Intent(WindowClass::GuestList); intent.PutExtra( INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsThinkingAboutRide)); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, number); ContextOpenIntent(&intent); break; } @@ -6507,7 +6180,7 @@ private: { auto intent = Intent(WindowClass::GuestList); intent.PutExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsOnRide)); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, number); ContextOpenIntent(&intent); break; } @@ -6515,60 +6188,48 @@ private: { auto intent = Intent(WindowClass::GuestList); intent.PutExtra(INTENT_EXTRA_GUEST_LIST_FILTER, static_cast(GuestListFilterType::GuestsInQueue)); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, w->number); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, number); ContextOpenIntent(&intent); break; } } } - /** - * - * rct2: 0x006ADA29 - */ void CustomerResize() { - w->flags |= WF_RESIZABLE; - WindowSetResize(*w, 316, 163, 316, 163); + flags |= WF_RESIZABLE; + WindowSetResize(*this, 316, 163, 316, 163); } - /** - * - * rct2: 0x006AD9DD - */ void CustomerUpdate() { - w->picked_peep_frame++; - if (w->picked_peep_frame >= 24) - w->picked_peep_frame = 0; + picked_peep_frame++; + if (picked_peep_frame >= 24) + picked_peep_frame = 0; OnPrepareDraw(); - WidgetInvalidate(*w, WIDX_TAB_10); + WidgetInvalidate(*this, WIDX_TAB_10); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER; - w->Invalidate(); + Invalidate(); } } - /** - * - * rct2: 0x006AD5F8 - */ void CustomerOnPrepareDraw() { - auto widgets = window_ride_page_widgets[w->page]; - if (w->widgets != widgets) + auto newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - w->widgets = widgets; - WindowInitScrollWidgets(*w); + widgets = newWidgets; + InitScrollWidgets(); } - SetPressedTab(w); + SetPressedTab(); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride != nullptr) { auto ft = Formatter::Common(); @@ -6586,29 +6247,25 @@ private: window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::FlatBtn; } - AnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB_1, WIDX_TAB_10); + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } } - /** - * - * rct2: 0x006AD6CD - */ void CustomerPaint(DrawPixelInfo& dpi) { ShopItem shopItem; int16_t popularity, satisfaction, queueTime; StringId stringId; - WindowDrawWidgets(*w, dpi); - DrawTabImages(dpi, w); + DrawWidgets(dpi); + DrawTabImages(dpi); - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return; - auto screenCoords = w->windowPos + auto screenCoords = windowPos + ScreenCoordsXY{ window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].left + 4, window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; @@ -6829,7 +6486,7 @@ static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationI w->event_handlers = window_ride_page_events[w->page]; w->pressed_widgets = 0; DisableTabs(w); - WindowInitScrollWidgets(*w); + InitScrollWidgets(); // View for (int32_t i = stationIndex.ToUnderlying(); i >= 0; i--) @@ -6966,7 +6623,7 @@ WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) w->event_handlers = window_ride_page_events[w->page]; w->pressed_widgets = 0; DisableTabs(w); - WindowInitScrollWidgets(*w); + InitScrollWidgets(); w->ride.view = view; InitViewport(w); From 5e71fd47961eb857b1bf52a48e51173b268d803d Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Fri, 16 Jun 2023 08:59:17 +0100 Subject: [PATCH 3/5] Hook up events --- src/openrct2-ui/windows/Ride.cpp | 686 ++++++++++++++++++++----------- 1 file changed, 451 insertions(+), 235 deletions(-) diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 39a46ab0ab..c20ea54052 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -72,8 +72,6 @@ static constexpr const StringId WINDOW_TITLE = STR_RIDE_WINDOW_TITLE; static constexpr const int32_t WH = 207; static constexpr const int32_t WW = 316; -static void PopulateVehicleTypeDropdown(const Ride& ride, bool forceRefresh = false); - enum { WINDOW_RIDE_PAGE_MAIN, @@ -429,149 +427,6 @@ static constexpr const uint64_t window_ride_page_hold_down_widgets[] = { #pragma endregion -#pragma region Events - -// 0x0098DFD4 -static WindowEventList window_ride_main_events([](auto& events) { - events.mouse_up = &WindowRideMainMouseup; - events.resize = &WindowRideMainResize; - events.mouse_down = &WindowRideMainMousedown; - events.dropdown = &WindowRideMainDropdown; - events.update = &WindowRideMainUpdate; - events.text_input = &WindowRideMainTextinput; - events.viewport_rotate = &WindowRideMainViewportRotate; - events.invalidate = &WindowRideMainInvalidate; - events.paint = &WindowRideMainPaint; -}); - -// 0x0098E204 -static WindowEventList window_ride_vehicle_events([](auto& events) { - events.mouse_up = &WindowRideVehicleMouseup; - events.resize = &WindowRideVehicleResize; - events.mouse_down = &WindowRideVehicleMousedown; - events.dropdown = &WindowRideVehicleDropdown; - events.update = &WindowRideVehicleUpdate; - events.tooltip = &WindowRideVehicleTooltip; - events.invalidate = &WindowRideVehicleInvalidate; - events.paint = &WindowRideVehiclePaint; - events.scroll_paint = &WindowRideVehicleScrollpaint; -}); - -// 0x0098E0B4 -static WindowEventList window_ride_operating_events([](auto& events) { - events.mouse_up = &WindowRideOperatingMouseup; - events.resize = &WindowRideOperatingResize; - events.mouse_down = &WindowRideOperatingMousedown; - events.dropdown = &WindowRideOperatingDropdown; - events.update = &WindowRideOperatingUpdate; - events.text_input = &WindowRideOperatingTextinput; - events.invalidate = &WindowRideOperatingInvalidate; - events.paint = &WindowRideOperatingPaint; -}); - -// 0x0098E124 -static WindowEventList window_ride_maintenance_events([](auto& events) { - events.mouse_up = &WindowRideMaintenanceMouseup; - events.resize = &WindowRideMaintenanceResize; - events.mouse_down = &WindowRideMaintenanceMousedown; - events.dropdown = &WindowRideMaintenanceDropdown; - events.update = &WindowRideMaintenanceUpdate; - events.invalidate = &WindowRideMaintenanceInvalidate; - events.paint = &WindowRideMaintenancePaint; -}); - -// 0x0098E044 -static WindowEventList window_ride_colour_events([](auto& events) { - events.close = &WindowRideColourClose; - events.mouse_up = &WindowRideColourMouseup; - events.resize = &WindowRideColourResize; - events.mouse_down = &WindowRideColourMousedown; - events.dropdown = &WindowRideColourDropdown; - events.update = &WindowRideColourUpdate; - events.tool_down = &WindowRideColourTooldown; - events.tool_drag = &WindowRideColourTooldrag; - events.invalidate = &WindowRideColourInvalidate; - events.paint = &WindowRideColourPaint; - events.scroll_paint = &WindowRideColourScrollpaint; -}); - -// 0x0098E194 -static WindowEventList window_ride_music_events([](auto& events) { - events.mouse_up = &WindowRideMusicMouseup; - events.resize = &WindowRideMusicResize; - events.mouse_down = &WindowRideMusicMousedown; - events.dropdown = &WindowRideMusicDropdown; - events.update = &WindowRideMusicUpdate; - events.invalidate = &WindowRideMusicInvalidate; - events.paint = &WindowRideMusicPaint; -}); - -// 0x0098DE14 -static WindowEventList window_ride_measurements_events([](auto& events) { - events.close = &WindowRideMeasurementsClose; - events.mouse_up = &WindowRideMeasurementsMouseup; - events.resize = &WindowRideMeasurementsResize; - events.mouse_down = &WindowRideMeasurementsMousedown; - events.dropdown = &WindowRideMeasurementsDropdown; - events.update = &WindowRideMeasurementsUpdate; - events.tool_down = &WindowRideMeasurementsTooldown; - events.tool_drag = &WindowRideMeasurementsTooldrag; - events.tool_abort = &WindowRideMeasurementsToolabort; - events.invalidate = &WindowRideMeasurementsInvalidate; - events.paint = &WindowRideMeasurementsPaint; -}); - -// 0x0098DF64 -static WindowEventList window_ride_graphs_events([](auto& events) { - events.mouse_up = &WindowRideGraphsMouseup; - events.resize = &WindowRideGraphsResize; - events.mouse_down = &WindowRideGraphsMousedown; - events.update = &WindowRideGraphsUpdate; - events.get_scroll_size = &WindowRideGraphsScrollgetheight; - events.scroll_select = &WindowRideGraphs15; - events.tooltip = &WindowRideGraphsTooltip; - events.invalidate = &WindowRideGraphsInvalidate; - events.paint = &WindowRideGraphsPaint; - events.scroll_paint = &WindowRideGraphsScrollpaint; -}); - -// 0x0098DEF4 -static WindowEventList window_ride_income_events([](auto& events) { - events.mouse_up = &WindowRideIncomeMouseup; - events.resize = &WindowRideIncomeResize; - events.mouse_down = &WindowRideIncomeMousedown; - events.update = &WindowRideIncomeUpdate; - events.text_input = &WindowRideIncomeTextinput; - events.invalidate = &WindowRideIncomeInvalidate; - events.paint = &WindowRideIncomePaint; -}); - -// 0x0098DE84 -static WindowEventList window_ride_customer_events([](auto& events) { - events.mouse_up = &WindowRideCustomerMouseup; - events.resize = &WindowRideCustomerResize; - events.update = &WindowRideCustomerUpdate; - events.invalidate = &WindowRideCustomerInvalidate; - events.paint = &WindowRideCustomerPaint; -}); - -// clang-format off -static WindowEventList *window_ride_page_events[] = { - &window_ride_main_events, - &window_ride_vehicle_events, - &window_ride_operating_events, - &window_ride_maintenance_events, - &window_ride_colour_events, - &window_ride_music_events, - &window_ride_measurements_events, - &window_ride_graphs_events, - &window_ride_income_events, - &window_ride_customer_events, -}; -// clang-format on - -#pragma endregion - static bool _collectTrackDesignScenery = false; static int32_t _lastSceneryX = 0; static int32_t _lastSceneryY = 0; @@ -764,6 +619,7 @@ static std::vector VehicleDropdownData; class RideWindow final : public Window { +public: RideWindow(const Ride& ride) { rideId = ride.id; @@ -787,6 +643,362 @@ class RideWindow final : public Window PopulateVehicleTypeDropdown(ride, true); } + virtual void OnClose() override + { + switch (page) + { + case WINDOW_RIDE_PAGE_COLOUR: + ColourClose(); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsClose(); + break; + } + } + virtual void OnResize() override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainResize(); + break; + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleResize(); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingResize(); + break; + case WINDOW_RIDE_PAGE_MAINTENANCE: + MaintenanceResize(); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourResize(); + break; + case WINDOW_RIDE_PAGE_MUSIC: + MusicResize(); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsResize(); + break; + case WINDOW_RIDE_PAGE_GRAPHS: + GraphsResize(); + break; + case WINDOW_RIDE_PAGE_INCOME: + IncomeResize(); + break; + case WINDOW_RIDE_PAGE_CUSTOMER: + CustomerResize(); + break; + } + } + virtual void OnUpdate() override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainUpdate(); + break; + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleUpdate(); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingUpdate(); + break; + case WINDOW_RIDE_PAGE_MAINTENANCE: + MaintenanceUpdate(); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourUpdate(); + break; + case WINDOW_RIDE_PAGE_MUSIC: + MusicUpdate(); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsUpdate(); + break; + case WINDOW_RIDE_PAGE_GRAPHS: + GraphsUpdate(); + break; + case WINDOW_RIDE_PAGE_INCOME: + IncomeUpdate(); + break; + case WINDOW_RIDE_PAGE_CUSTOMER: + CustomerUpdate(); + break; + } + } + + virtual void OnPrepareDraw() override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_MAINTENANCE: + MaintenanceOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_MUSIC: + MusicOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_GRAPHS: + GraphsOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_INCOME: + IncomeOnPrepareDraw(); + break; + case WINDOW_RIDE_PAGE_CUSTOMER: + CustomerOnPrepareDraw(); + break; + } + } + virtual void OnDraw(DrawPixelInfo& dpi) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_MAINTENANCE: + MaintenanceOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_MUSIC: + MusicOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_GRAPHS: + GraphsOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_INCOME: + IncomeOnDraw(dpi); + break; + case WINDOW_RIDE_PAGE_CUSTOMER: + CustomerOnDraw(dpi); + break; + } + } + + virtual OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_VEHICLE: + return VehicleTooltip(widgetIndex, fallback); + case WINDOW_RIDE_PAGE_GRAPHS: + return GraphsTooltip(widgetIndex, fallback); + } + return { fallback, {} }; + } + virtual void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_MAINTENANCE: + MaintenanceOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_MUSIC: + MusicOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_GRAPHS: + GraphsOnMouseDown(widgetIndex); + break; + case WINDOW_RIDE_PAGE_INCOME: + IncomeOnMouseDown(widgetIndex); + break; + } + } + virtual void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_MAINTENANCE: + MaintenanceOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_MUSIC: + MusicOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_GRAPHS: + GraphsOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_INCOME: + IncomeOnMouseUp(widgetIndex); + break; + case WINDOW_RIDE_PAGE_CUSTOMER: + CustomerOnMouseUp(widgetIndex); + break; + } + } + virtual void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainOnDropdown(widgetIndex, selectedIndex); + break; + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleOnDropdown(widgetIndex, selectedIndex); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingOnDropdown(widgetIndex, selectedIndex); + break; + case WINDOW_RIDE_PAGE_MAINTENANCE: + MaintenanceOnDropdown(widgetIndex, selectedIndex); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnDropdown(widgetIndex, selectedIndex); + break; + case WINDOW_RIDE_PAGE_MUSIC: + MusicOnDropdown(widgetIndex, selectedIndex); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnDropdown(widgetIndex, selectedIndex); + break; + } + } + virtual void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainOnTextInput(widgetIndex, text); + break; + case WINDOW_RIDE_PAGE_OPERATING: + OperatingOnTextInput(widgetIndex, text); + break; + case WINDOW_RIDE_PAGE_INCOME: + IncomeOnTextInput(widgetIndex, text); + break; + } + } + virtual ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_GRAPHS: + return GraphsScrollGetSize(scrollIndex); + } + return {}; + } + virtual void OnScrollSelect(int32_t scrollIndex, int32_t scrollAreaType) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_GRAPHS: + Graphs15(scrollIndex, scrollAreaType); + break; + } + } + virtual void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_VEHICLE: + VehicleOnScrollDraw(dpi, scrollIndex); + break; + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnScrollDraw(dpi, scrollIndex); + break; + case WINDOW_RIDE_PAGE_GRAPHS: + GraphsOnScrollDraw(dpi, scrollIndex); + break; + } + } + virtual void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnToolDown(widgetIndex, screenCoords); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnToolDown(widgetIndex, screenCoords); + break; + } + } + virtual void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_COLOUR: + ColourOnToolDrag(widgetIndex, screenCoords); + break; + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnToolDrag(widgetIndex, screenCoords); + break; + } + } + virtual void OnToolAbort(WidgetIndex widgetIndex) override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MEASUREMENTS: + MeasurementsOnToolAbort(widgetIndex); + break; + } + } + virtual void OnViewportRotate() override + { + switch (page) + { + case WINDOW_RIDE_PAGE_MAIN: + MainViewportRotate(); + break; + } + } + private: void DrawTabImage(DrawPixelInfo& dpi, int32_t tab, int32_t spriteIndex) { @@ -1092,7 +1304,6 @@ private: RemoveViewport(); hold_down_widgets = window_ride_page_hold_down_widgets[page]; - event_handlers = window_ride_page_events[page]; pressed_widgets = 0; widgets = window_ride_page_widgets[page]; DisableTabs(); @@ -1253,7 +1464,7 @@ private: } } - void MainMouseup(WidgetIndex widgetIndex) + void MainOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -1745,26 +1956,26 @@ private: Dropdown::SetChecked(pos, true); } - void MainMousedown(WidgetIndex widgetIndex, Widget* widget) + void MainOnMouseDown(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_VIEW_DROPDOWN: - ShowViewDropdown(widget); + ShowViewDropdown(&widgets[widgetIndex]); break; case WIDX_OPEN: - ShowOpenDropdown(widget); + ShowOpenDropdown(&widgets[widgetIndex]); break; case WIDX_RIDE_TYPE_DROPDOWN: - ShowRideTypeDropdown(widget); + ShowRideTypeDropdown(&widgets[widgetIndex]); break; case WIDX_LOCATE: - ShowLocateDropdown(widget); + ShowLocateDropdown(&widgets[widgetIndex]); break; } } - void MainDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + void MainOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { switch (widgetIndex) { @@ -1892,15 +2103,16 @@ private: WidgetInvalidate(*this, WIDX_STATUS); } - void MainTextinput(WidgetIndex widgetIndex, const char* text) + void MainOnTextInput(WidgetIndex widgetIndex, std::string_view text) { - if (widgetIndex != WIDX_RENAME || text == nullptr) + if (widgetIndex != WIDX_RENAME || text.empty()) return; auto ride = GetRide(rideId); if (ride != nullptr) { - auto gameAction = RideSetNameAction(ride->id, text); + auto strText = std::string(text); + auto gameAction = RideSetNameAction(ride->id, strText); GameActions::Execute(&gameAction); } } @@ -2174,7 +2386,7 @@ private: return GetStatusStation(ft); } - void MainOnPaint(DrawPixelInfo& dpi) + void MainOnDraw(DrawPixelInfo& dpi) { Widget* widget; @@ -2231,7 +2443,7 @@ private: #pragma region Vehicle - void VehicleMouseup(WidgetIndex widgetIndex) + void VehicleOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -2258,7 +2470,7 @@ private: WindowSetResize(*this, 316, 221, 316, 221); } - void VehicleMousedown(WidgetIndex widgetIndex, Widget* widget) + void VehicleOnMouseDown(WidgetIndex widgetIndex) { auto ride = GetRide(rideId); if (ride == nullptr) @@ -2291,7 +2503,7 @@ private: } } - void VehicleDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + void VehicleOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; @@ -2319,9 +2531,9 @@ private: WidgetInvalidate(*this, WIDX_TAB_2); } - OpenRCT2String VehicleTooltip(WindowBase* const w, const WidgetIndex widgetIndex, StringId fallback) + OpenRCT2String VehicleTooltip(const WidgetIndex widgetIndex, StringId fallback) { - auto ride = GetRide(w->rideId); + auto ride = GetRide(rideId); if (ride == nullptr) return { STR_NONE, {} }; @@ -2471,7 +2683,7 @@ private: } } - void VehiclePaint(DrawPixelInfo& dpi) + void VehicleOnDraw(DrawPixelInfo& dpi) { WindowDrawWidgets(*this, dpi); DrawTabImages(dpi); @@ -2545,7 +2757,7 @@ private: ImageId imageId; }; - void VehicleScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) + void VehicleOnScrollDraw(DrawPixelInfo& dpi, int32_t scrollIndex) { auto ride = GetRide(rideId); if (ride == nullptr) @@ -2743,7 +2955,7 @@ private: Dropdown::SetChecked(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK, true); } - void OperatingMouseup(WidgetIndex widgetIndex) + void OperatingOnMouseUp(WidgetIndex widgetIndex) { auto ride = GetRide(rideId); if (ride == nullptr) @@ -2793,7 +3005,7 @@ private: WindowSetResize(*this, 316, 186, 316, 186); } - void OperatingMousedown(WidgetIndex widgetIndex, Widget* widget) + void OperatingOnMouseDown(WidgetIndex widgetIndex) { auto ride = GetRide(rideId); if (ride == nullptr) @@ -2862,10 +3074,10 @@ private: std::clamp(ride->max_waiting_time - 1, lower_bound, upper_bound)); break; case WIDX_MODE_DROPDOWN: - ModeDropdown(widget); + ModeDropdown(&widgets[widgetIndex]); break; case WIDX_LOAD_DROPDOWN: - LoadDropdown(widget); + LoadDropdown(&widgets[widgetIndex]); break; case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE: upper_bound = gCheatsUnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit @@ -2934,7 +3146,7 @@ private: WindowTextInputRawOpen(this, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4); } - void OperatingDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + void OperatingOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; @@ -2987,9 +3199,9 @@ private: } } - void OperatingTextinput(WidgetIndex widgetIndex, const char* text) + void OperatingOnTextInput(WidgetIndex widgetIndex, std::string_view text) { - if (text == nullptr) + if (text.empty()) return; auto ride = GetRide(rideId); @@ -3274,7 +3486,7 @@ private: WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - void OperatingPaint(DrawPixelInfo& dpi) + void OperatingOnDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); @@ -3345,7 +3557,7 @@ private: } } - void MaintenanceMouseup(WidgetIndex widgetIndex) + void MaintenanceOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -3378,7 +3590,7 @@ private: WindowSetResize(*this, 316, 135, 316, 135); } - void MaintenanceMousedown(WidgetIndex widgetIndex, Widget* widget) + void MaintenanceOnMouseDown(WidgetIndex widgetIndex) { auto ride = GetRide(rideId); if (ride == nullptr) @@ -3388,7 +3600,7 @@ private: if (rideEntry == nullptr) return; - Widget* dropdownWidget = widget; + Widget* dropdownWidget = &widgets[widgetIndex]; int32_t j, num_items; switch (widgetIndex) @@ -3402,7 +3614,7 @@ private: } WindowDropdownShowTextCustomWidth( { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, 7, widget->right - dropdownWidget->left); + colours[1], 0, Dropdown::Flag::StayOpen, 7, widgets[widgetIndex].right - dropdownWidget->left); Dropdown::SetChecked(ride->inspection_interval, true); break; @@ -3476,7 +3688,7 @@ private: } } - void MaintenanceDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + void MaintenanceOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; @@ -3635,7 +3847,7 @@ private: } } - void MaintenancePaint(DrawPixelInfo& dpi) + void MaintenanceOnDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); @@ -3817,7 +4029,7 @@ private: ToolCancel(); } - void ColourMouseup(WidgetIndex widgetIndex) + void ColourOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -3857,7 +4069,7 @@ private: WindowSetResize(*this, 316, 207, 316, 207); } - void ColourMousedown(WidgetIndex widgetIndex, Widget* widget) + void ColourOnMouseDown(WidgetIndex widgetIndex) { VehicleColour vehicleColour; int32_t i, numItems; @@ -3872,7 +4084,7 @@ private: return; auto colourSchemeIndex = ride_colour; - auto dropdownWidget = widget - 1; + auto dropdownWidget = &widgets[widgetIndex] - 1; switch (widgetIndex) { @@ -3885,18 +4097,20 @@ private: WindowDropdownShowTextCustomWidth( { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left); + colours[1], 0, Dropdown::Flag::StayOpen, 4, widgets[widgetIndex].right - dropdownWidget->left); Dropdown::SetChecked(colourSchemeIndex, true); break; case WIDX_TRACK_MAIN_COLOUR: - WindowDropdownShowColour(this, widget, colours[1], ride->track_colour[colourSchemeIndex].main); + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].main); break; case WIDX_TRACK_ADDITIONAL_COLOUR: - WindowDropdownShowColour(this, widget, colours[1], ride->track_colour[colourSchemeIndex].additional); + WindowDropdownShowColour( + this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].additional); break; case WIDX_TRACK_SUPPORT_COLOUR: - WindowDropdownShowColour(this, widget, colours[1], ride->track_colour[colourSchemeIndex].supports); + WindowDropdownShowColour( + this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].supports); break; case WIDX_MAZE_STYLE_DROPDOWN: for (i = 0; i < 4; i++) @@ -3907,7 +4121,7 @@ private: WindowDropdownShowTextCustomWidth( { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, 4, widget->right - dropdownWidget->left); + colours[1], 0, Dropdown::Flag::StayOpen, 4, widgets[widgetIndex].right - dropdownWidget->left); Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true); break; @@ -3932,7 +4146,7 @@ private: WindowDropdownShowTextCustomWidth( { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, widget->right - dropdownWidget->left); + colours[1], 0, Dropdown::Flag::StayOpen, ddIndex, widgets[widgetIndex].right - dropdownWidget->left); break; } case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: @@ -3948,7 +4162,7 @@ private: WindowDropdownShowTextCustomWidth( { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], 0, Dropdown::Flag::StayOpen, rideEntry->max_cars_in_train > 1 ? 3 : 2, - widget->right - dropdownWidget->left); + widgets[widgetIndex].right - dropdownWidget->left); Dropdown::SetChecked(ride->colour_scheme_type & 3, true); break; @@ -3969,26 +4183,26 @@ private: WindowDropdownShowTextCustomWidth( { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); + colours[1], 0, Dropdown::Flag::StayOpen, numItems, widgets[widgetIndex].right - dropdownWidget->left); Dropdown::SetChecked(vehicleIndex, true); break; case WIDX_VEHICLE_BODY_COLOUR: vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); - WindowDropdownShowColour(this, widget, colours[1], vehicleColour.Body); + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], vehicleColour.Body); break; case WIDX_VEHICLE_TRIM_COLOUR: vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); - WindowDropdownShowColour(this, widget, colours[1], vehicleColour.Trim); + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], vehicleColour.Trim); break; case WIDX_VEHICLE_TERNARY_COLOUR: vehicleColour = RideGetVehicleColour(*ride, vehicleIndex); - WindowDropdownShowColour(this, widget, colours[1], vehicleColour.Tertiary); + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], vehicleColour.Tertiary); break; } } - void ColourDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + void ColourOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (dropdownIndex == -1) return; @@ -4100,13 +4314,13 @@ private: WidgetInvalidate(*this, WIDX_VEHICLE_PREVIEW); } - void ColourTooldown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + void ColourOnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) { if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) SetTrackColourScheme(screenCoords); } - void ColourTooldrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + void ColourOnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) { if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) SetTrackColourScheme(screenCoords); @@ -4359,8 +4573,7 @@ private: WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - - void ColourPaint(DrawPixelInfo& dpi) + void ColourOnDraw(DrawPixelInfo& dpi) { // TODO: This should use lists and identified sprites DrawPixelInfo clippedDpi; @@ -4476,7 +4689,7 @@ private: } } - void ColourScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) const + void ColourOnScrollDraw(DrawPixelInfo& dpi, int32_t scrollIndex) const { auto ride = GetRide(rideId); if (ride == nullptr) @@ -4529,12 +4742,12 @@ private: } } - void MusicMouseup(WidgetIndex widgetIndex) + void MusicOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - Close(); + Close(); return; case WIDX_TAB_1: case WIDX_TAB_2: @@ -4573,12 +4786,12 @@ private: * * rct2: 0x006B1EFC */ - void MusicMousedown(WidgetIndex widgetIndex, Widget* widget) + void MusicOnMouseDown(WidgetIndex widgetIndex) { if (widgetIndex != WIDX_MUSIC_DROPDOWN) return; - auto dropdownWidget = widget - 1; + auto dropdownWidget = &widgets[widgetIndex] - 1; auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -4634,8 +4847,8 @@ private: } WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left); + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], + 0, Dropdown::Flag::StayOpen, numItems, widgets[widgetIndex].right - dropdownWidget->left); // Set currently checked item for (size_t i = 0; i < numItems; i++) @@ -4647,7 +4860,7 @@ private: } } - void MusicDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + void MusicOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0 && static_cast(dropdownIndex) < window_ride_current_music_style_order.size()) @@ -4711,7 +4924,7 @@ private: WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - void MusicPaint(DrawPixelInfo& dpi) + void MusicOnDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); @@ -4837,7 +5050,7 @@ private: MeasurementsDesignCancel(); } - void MeasurementsMouseup(WidgetIndex widgetIndex) + void MeasurementsOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -4876,7 +5089,7 @@ private: WindowSetResize(*this, 316, 234, 316, 234); } - void MeasurementsMousedown(WidgetIndex widgetIndex, Widget* widget) + void MeasurementsOnMouseDown(WidgetIndex widgetIndex) { if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) return; @@ -4889,7 +5102,9 @@ private: gDropdownItems[1].Format = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM; WindowDropdownShowText( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], + { windowPos.x +widgets[widgetIndex].left, windowPos.y + widgets[widgetIndex].top }, + widgets[widgetIndex].height() + 1, + colours[1], Dropdown::Flag::StayOpen, 2); gDropdownDefaultIndex = 0; if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) @@ -4900,7 +5115,7 @@ private: } } - void MeasurementsDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + void MeasurementsOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) return; @@ -4923,7 +5138,7 @@ private: WidgetInvalidate(*this, WIDX_TAB_7); } - void MeasurementsTooldown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + void MeasurementsOnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) { _lastSceneryX = screenCoords.x; _lastSceneryY = screenCoords.y; @@ -4947,7 +5162,7 @@ private: } } - void MeasurementsTooldrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + void MeasurementsOnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) { if (screenCoords.x == _lastSceneryX && screenCoords.y == _lastSceneryY) return; @@ -4971,7 +5186,7 @@ private: } } - void MeasurementsToolabort(WidgetIndex widgetIndex) + void MeasurementsOnToolAbort(WidgetIndex widgetIndex) { MeasurementsDesignCancel(); } @@ -5026,7 +5241,7 @@ private: WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - void MeasurementsPaint(DrawPixelInfo& dpi) + void MeasurementsOnDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); @@ -5036,14 +5251,12 @@ private: Widget* widget = &window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND]; ScreenCoordsXY widgetCoords(windowPos.x + widget->width() / 2, windowPos.y + widget->top + 40); - DrawTextWrapped( - dpi, widgetCoords, width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, {}, { TextAlignment::CENTRE }); + DrawTextWrapped(dpi, widgetCoords, width - 8, STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT, {}, { TextAlignment::CENTRE }); widgetCoords.x = windowPos.x + 4; widgetCoords.y = windowPos.y + window_ride_measurements_widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; GfxFillRectInset( - dpi, { widgetCoords, { windowPos.x + 312, widgetCoords.y + 1 } }, colours[1], - INSET_RECT_FLAG_BORDER_INSET); + dpi, { widgetCoords, { windowPos.x + 312, widgetCoords.y + 1 } }, colours[1], INSET_RECT_FLAG_BORDER_INSET); } else { @@ -5296,7 +5509,7 @@ private: Invalidate(); } - void GraphsMouseup(WidgetIndex widgetIndex) + void GraphsOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -5323,7 +5536,7 @@ private: WindowSetResize(*this, 316, 182, 500, 450); } - void GraphsMousedown(WidgetIndex widgetIndex, Widget* widget) + void GraphsOnMouseDown(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -5370,12 +5583,13 @@ private: WidgetScrollUpdateThumbs(*this, WIDX_GRAPH); } - void GraphsScrollgetheight(int32_t scrollIndex, int32_t* width, int32_t* height) + ScreenSize GraphsScrollGetSize(int32_t scrollIndex) { OnPrepareDraw(); + ScreenSize size{}; // Set minimum size - *width = window_ride_graphs_widgets[WIDX_GRAPH].width() - 2; + size.width = window_ride_graphs_widgets[WIDX_GRAPH].width() - 2; // Get measurement size auto ride = GetRide(rideId); @@ -5385,9 +5599,10 @@ private: std::tie(measurement, std::ignore) = ride->GetMeasurement(); if (measurement != nullptr) { - *width = std::max(*width, measurement->num_items); + size.width = std::max(size.width, measurement->num_items); } } + return size; } void Graphs15(int32_t scrollIndex, int32_t scrollAreaType) @@ -5480,13 +5695,13 @@ private: WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - void GraphsPaint(DrawPixelInfo& dpi) + void GraphsOnDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); } - void GraphsScrollpaint(DrawPixelInfo& dpi, int32_t scrollIndex) + void GraphsOnScrollDraw(DrawPixelInfo& dpi, int32_t scrollIndex) { GfxClear(&dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker); @@ -5820,12 +6035,12 @@ private: IncomeSetSecondaryPrice(price); } - void IncomeMouseup(WidgetIndex widgetIndex) + void IncomeOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - Close(); + Close(); return; case WIDX_TAB_1: case WIDX_TAB_2: @@ -5878,7 +6093,7 @@ private: WindowSetResize(*this, 316, 194, 316, 194); } - void IncomeMousedown(WidgetIndex widgetIndex, Widget* widget) + void IncomeOnMouseDown(WidgetIndex widgetIndex) { switch (widgetIndex) { @@ -5911,12 +6126,13 @@ private: } } - void IncomeTextinput(WidgetIndex widgetIndex, const char* text) + void IncomeOnTextInput(WidgetIndex widgetIndex, std::string_view text) { - if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text == nullptr) + if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text.empty()) return; - money64 price = StringToMoney(text); + std::string strText{ text }; + money64 price = StringToMoney(strText.c_str()); if (price == MONEY64_UNDEFINED) { return; @@ -6044,7 +6260,7 @@ private: WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - void IncomePaint(DrawPixelInfo& dpi) + void IncomeOnDraw(DrawPixelInfo& dpi) { StringId stringId; money64 profit; @@ -6148,12 +6364,12 @@ private: #pragma region Customer - void CustomerMouseup(WidgetIndex widgetIndex) + void CustomerOnMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: - Close(); + Close(); return; case WIDX_TAB_1: case WIDX_TAB_2: @@ -6252,7 +6468,7 @@ private: } } - void CustomerPaint(DrawPixelInfo& dpi) + void CustomerOnDraw(DrawPixelInfo& dpi) { ShopItem shopItem; int16_t popularity, satisfaction, queueTime; From 9b8b672507f2b49572db48b24d9beee0694adcb6 Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Tue, 20 Jun 2023 08:42:25 +0100 Subject: [PATCH 4/5] Finish linking up parts --- src/openrct2-ui/WindowManager.cpp | 17 +- src/openrct2-ui/windows/Ride.cpp | 348 +++++++++++------------ src/openrct2-ui/windows/Window.h | 1 + src/openrct2/interface/Window.h | 6 - src/openrct2/interface/Window_internal.h | 1 - 5 files changed, 172 insertions(+), 201 deletions(-) diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 3ede2e6698..abdaa10535 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -455,19 +455,10 @@ public: case INTENT_ACTION_INVALIDATE_VEHICLE_WINDOW: { auto vehicle = static_cast(intent.GetPointerExtra(INTENT_EXTRA_VEHICLE)); - auto* w = WindowFindByNumber(WindowClass::Ride, vehicle->ride.ToUnderlying()); - if (w == nullptr) - return; - - auto ride = vehicle->GetRide(); - auto viewVehicleIndex = w->ride.view - 1; - if (ride == nullptr || viewVehicleIndex < 0 || viewVehicleIndex >= ride->NumTrains) - return; - - if (vehicle->Id != ride->vehicles[viewVehicleIndex]) - return; - - w->Invalidate(); + if (vehicle != nullptr) + { + WindowRideInvalidateVehicle(*vehicle); + } break; } diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index c20ea54052..08d73c7cdf 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -427,6 +427,8 @@ static constexpr const uint64_t window_ride_page_hold_down_widgets[] = { #pragma endregion +static void CancelScenerySelection(); + static bool _collectTrackDesignScenery = false; static int32_t _lastSceneryX = 0; static int32_t _lastSceneryY = 0; @@ -619,6 +621,8 @@ static std::vector VehicleDropdownData; class RideWindow final : public Window { + int16_t _viewIndex; + public: RideWindow(const Ride& ride) { @@ -999,6 +1003,67 @@ public: } } + void SetPage(int32_t newPage) + { + int32_t listen; + + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) + ToolCancel(); + + if (newPage == WINDOW_RIDE_PAGE_VEHICLE) + { + auto constructionWindow = WindowFindByClass(WindowClass::RideConstruction); + if (constructionWindow != nullptr && constructionWindow->number == number) + { + WindowCloseByClass(WindowClass::RideConstruction); + // Closing the construction window sets the tab to the first page, which we don't want here, + // as user just clicked the Vehicle page + SetPage(WINDOW_RIDE_PAGE_VEHICLE); + } + } + + // Set listen only to viewport + listen = 0; + if (newPage == WINDOW_RIDE_PAGE_MAIN && page == WINDOW_RIDE_PAGE_MAIN && viewport != nullptr + && !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) + listen++; + + page = newPage; + frame_no = 0; + picked_peep_frame = 0; + + // There doesn't seem to be any need for this call, and it can sometimes modify the reported number of cars per train, + // so I've removed it if (newPage == WINDOW_RIDE_PAGE_VEHICLE) { ride_update_max_vehicles(ride); + //} + + RemoveViewport(); + + hold_down_widgets = window_ride_page_hold_down_widgets[page]; + pressed_widgets = 0; + widgets = window_ride_page_widgets[page]; + DisableTabs(); + Invalidate(); + + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); + + if (listen != 0 && viewport != nullptr) + viewport->flags |= VIEWPORT_FLAG_SOUND_ON; + } + + void SetViewIndex(int16_t newIndex) + { + _viewIndex = newIndex; + OnViewportRotate(); + } + int16_t GetViewIndex() const + { + return _viewIndex; + } + private: void DrawTabImage(DrawPixelInfo& dpi, int32_t tab, int32_t spriteIndex) { @@ -1059,15 +1124,15 @@ private: if (!WidgetIsDisabled(*this, widgetIndex)) { auto screenCoords = ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; - int32_t width = widget.right - screenCoords.x; - int32_t height = widget.bottom - 3 - screenCoords.y; + int32_t clipWidth = widget.right - screenCoords.x; + int32_t clipHeight = widget.bottom - 3 - screenCoords.y; if (page == WINDOW_RIDE_PAGE_VEHICLE) - height += 4; + clipHeight += 4; screenCoords += windowPos; DrawPixelInfo clipDPI; - if (!ClipDrawPixelInfo(clipDPI, dpi, screenCoords, width, height)) + if (!ClipDrawPixelInfo(clipDPI, dpi, screenCoords, clipWidth, clipHeight)) { return; } @@ -1267,57 +1332,6 @@ private: } } - void SetPage(int32_t newPage) - { - int32_t listen; - - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) - ToolCancel(); - - if (newPage == WINDOW_RIDE_PAGE_VEHICLE) - { - auto constructionWindow = WindowFindByClass(WindowClass::RideConstruction); - if (constructionWindow != nullptr && constructionWindow->number == number) - { - WindowCloseByClass(WindowClass::RideConstruction); - // Closing the construction window sets the tab to the first page, which we don't want here, - // as user just clicked the Vehicle page - SetPage(WINDOW_RIDE_PAGE_VEHICLE); - } - } - - // Set listen only to viewport - listen = 0; - if (newPage == WINDOW_RIDE_PAGE_MAIN && page == WINDOW_RIDE_PAGE_MAIN && viewport != nullptr - && !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) - listen++; - - page = newPage; - frame_no = 0; - picked_peep_frame = 0; - - // There doesn't seem to be any need for this call, and it can sometimes modify the reported number of cars per train, - // so I've removed it if (newPage == WINDOW_RIDE_PAGE_VEHICLE) { ride_update_max_vehicles(ride); - //} - - RemoveViewport(); - - hold_down_widgets = window_ride_page_hold_down_widgets[page]; - pressed_widgets = 0; - widgets = window_ride_page_widgets[page]; - DisableTabs(); - Invalidate(); - - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - Invalidate(); - - if (listen != 0 && viewport != nullptr) - viewport->flags |= VIEWPORT_FLAG_SOUND_ON; - } - void SetPressedTab() { int32_t i; @@ -1339,7 +1353,7 @@ private: if (ride == nullptr) return std::nullopt; - int32_t viewSelectionIndex = this->ride.view - 1 - ride->NumTrains; + int32_t viewSelectionIndex = this->_viewIndex - 1 - ride->NumTrains; if (viewSelectionIndex < 0) { return std::nullopt; @@ -1365,7 +1379,7 @@ private: if (ride == nullptr) return; - int32_t viewSelectionIndex = this->ride.view - 1; + int32_t viewSelectionIndex = this->_viewIndex - 1; std::optional newFocus; @@ -1403,7 +1417,7 @@ private: { if (viewSelectionIndex > 0) { - this->ride.view = 0; + this->_viewIndex = 0; } if (number < ride_overall_views.size()) { @@ -1437,10 +1451,10 @@ private: const auto& view_widget = widgets[WIDX_VIEWPORT]; auto screenPos = windowPos + ScreenCoordsXY{ view_widget.left + 1, view_widget.top + 1 }; - int32_t width = view_widget.width() - 1; - int32_t height = view_widget.height() - 1; + int32_t viewWidth = view_widget.width() - 1; + int32_t viewHeight = view_widget.height() - 1; - ViewportCreate(this, screenPos, width, height, focus.value()); + ViewportCreate(this, screenPos, viewWidth, viewHeight, focus.value()); flags |= WF_NO_SCROLLING; Invalidate(); @@ -1645,7 +1659,7 @@ private: } // Set checked item - Dropdown::SetChecked(this->ride.view, true); + Dropdown::SetChecked(this->_viewIndex, true); } RideStatus GetNextDefaultStatus(const Ride& ride) const @@ -1819,8 +1833,8 @@ private: WindowDropdownShowText( { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, 2); gDropdownDefaultIndex = 0; - if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK) || this->ride.view == 0 - || this->ride.view > ride->NumTrains) + if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK) || this->_viewIndex == 0 + || this->_viewIndex > ride->NumTrains) { // Disable if we're a flat ride, 'overall view' is selected or a station is selected Dropdown::SetDisabled(1, true); @@ -1834,11 +1848,11 @@ private: { if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) { - if (this->ride.view > 0) + if (this->_viewIndex > 0) { - if (this->ride.view <= ride->NumTrains) + if (this->_viewIndex <= ride->NumTrains) { - Vehicle* vehicle = GetEntity(ride->vehicles[this->ride.view - 1]); + Vehicle* vehicle = GetEntity(ride->vehicles[this->_viewIndex - 1]); if (vehicle != nullptr) { auto headVehicleSpriteIndex = vehicle->Id; @@ -1935,10 +1949,10 @@ private: } Widget* dropdownWidget = widget - 1; - auto width = WindowDropDownHasMultipleColumns(numItems) ? dropdownWidget->width() - 24 : dropdownWidget->width(); + auto ddWidth = WindowDropDownHasMultipleColumns(numItems) ? dropdownWidget->width() - 24 : dropdownWidget->width(); WindowDropdownShowTextCustomWidth( { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], - 0, Dropdown::Flag::StayOpen, numItems, width); + 0, Dropdown::Flag::StayOpen, numItems, ddWidth); // Find the current vehicle type in the ordered list. int32_t pos = 0; @@ -1982,7 +1996,7 @@ private: case WIDX_VIEW_DROPDOWN: if (dropdownIndex == -1) { - dropdownIndex = ride.view + 1; + dropdownIndex = _viewIndex + 1; auto ride = GetRide(rideId); if (ride != nullptr) { @@ -1998,7 +2012,7 @@ private: } } - ride.view = dropdownIndex; + _viewIndex = dropdownIndex; InitViewport(); Invalidate(); break; @@ -2082,12 +2096,12 @@ private: { if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) { - if (this->ride.view == 0) + if (this->_viewIndex == 0) return; - if (this->ride.view <= ride->NumTrains) + if (this->_viewIndex <= ride->NumTrains) { - Vehicle* vehicle = GetEntity(ride->vehicles[this->ride.view - 1]); + Vehicle* vehicle = GetEntity(ride->vehicles[this->_viewIndex - 1]); if (vehicle == nullptr || (vehicle->status != Vehicle::Status::Travelling && vehicle->status != Vehicle::Status::TravellingCableLift @@ -2288,7 +2302,7 @@ private: if (ride == nullptr) return STR_EMPTY; - auto vehicle = GetEntity(ride->vehicles[this->ride.view - 1]); + auto vehicle = GetEntity(ride->vehicles[this->_viewIndex - 1]); if (vehicle == nullptr) return STR_EMPTY; @@ -2377,9 +2391,9 @@ private: StringId GetStatus(Formatter& ft) const { auto ride = GetRide(rideId); - if (this->ride.view == 0) + if (this->_viewIndex == 0) return GetStatusOverallView(ft); - if (ride != nullptr && this->ride.view <= ride->NumTrains) + if (ride != nullptr && this->_viewIndex <= ride->NumTrains) return GetStatusVehicle(ft); if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) return GetStatusOverallView(ft); @@ -2407,17 +2421,17 @@ private: return; auto ft = Formatter(); - if (this->ride.view != 0) + if (this->_viewIndex != 0) { - if (this->ride.view > ride->NumTrains) + if (this->_viewIndex > ride->NumTrains) { ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station).number); - ft.Add(this->ride.view - ride->NumTrains); + ft.Add(this->_viewIndex - ride->NumTrains); } else { ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number); - ft.Add(this->ride.view); + ft.Add(this->_viewIndex); } } else @@ -4940,22 +4954,6 @@ private: return RatingNames[index]; } - void CancelScenerySelection() - { - gGamePaused &= ~GAME_PAUSED_SAVING_TRACK; - gTrackDesignSaveMode = false; - OpenRCT2::Audio::Resume(); - - WindowBase* main_w = WindowGetMain(); - if (main_w != nullptr) - { - main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); - } - - GfxInvalidateScreen(); - ToolCancel(); - } - void SetupScenerySelection() { if (gTrackDesignSaveMode) @@ -5102,10 +5100,8 @@ private: gDropdownItems[1].Format = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM; WindowDropdownShowText( - { windowPos.x +widgets[widgetIndex].left, windowPos.y + widgets[widgetIndex].top }, - widgets[widgetIndex].height() + 1, - colours[1], - Dropdown::Flag::StayOpen, 2); + { windowPos.x + widgets[widgetIndex].left, windowPos.y + widgets[widgetIndex].top }, + widgets[widgetIndex].height() + 1, colours[1], Dropdown::Flag::StayOpen, 2); gDropdownDefaultIndex = 0; if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) { @@ -5144,10 +5140,10 @@ private: _lastSceneryY = screenCoords.y; _collectTrackDesignScenery = true; // Default to true in case user does not select anything valid - constexpr auto flags = EnumsToFlags( + constexpr auto interactionFlags = EnumsToFlags( ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery); - auto info = GetMapCoordinatesFromPos(screenCoords, flags); + auto info = GetMapCoordinatesFromPos(screenCoords, interactionFlags); switch (info.SpriteType) { case ViewportInteractionItem::Scenery: @@ -5169,10 +5165,10 @@ private: _lastSceneryX = screenCoords.x; _lastSceneryY = screenCoords.y; - auto flags = EnumsToFlags( + auto interactionFlags = EnumsToFlags( ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery); - auto info = GetMapCoordinatesFromPos(screenCoords, flags); + auto info = GetMapCoordinatesFromPos(screenCoords, interactionFlags); switch (info.SpriteType) { case ViewportInteractionItem::Scenery: @@ -5193,10 +5189,10 @@ private: void MeasurementsOnPrepareDraw() { - auto widgets = window_ride_page_widgets[page]; - if (widgets != widgets) + auto newWidgets = window_ride_page_widgets[page]; + if (widgets != newWidgets) { - widgets = widgets; + widgets = newWidgets; InitScrollWidgets(); } @@ -5718,8 +5714,8 @@ private: { // No measurement message ScreenCoordsXY stringCoords(widget->width() / 2, widget->height() / 2 - 5); - int32_t width = widget->width() - 2; - DrawTextWrapped(dpi, stringCoords, width, message.str, message.args, { TextAlignment::CENTRE }); + int32_t txtWidth = widget->width() - 2; + DrawTextWrapped(dpi, stringCoords, txtWidth, message.str, message.args, { TextAlignment::CENTRE }); return; } @@ -5790,7 +5786,7 @@ private: // Uses the force limits (used to draw extreme G's in red on measurement tab) to determine if line should be drawn red. int32_t intensityThresholdPositive = 0; int32_t intensityThresholdNegative = 0; - for (int32_t width = 0; width < dpi.width; width++, x++) + for (int32_t graphWidth = 0; graphWidth < dpi.width; graphWidth++, x++) { if (x < 0 || x >= measurement->num_items - 1) continue; @@ -5874,8 +5870,6 @@ private: #pragma region Income - static utf8 _moneyInputText[MONEY_STRING_MAXLENGTH]; - static void UpdateSamePriceThroughoutFlags(ShopItem shop_item) { if (GetShopItemDescriptor(shop_item).IsPhoto()) @@ -6037,6 +6031,8 @@ private: void IncomeOnMouseUp(WidgetIndex widgetIndex) { + utf8 _moneyInputText[MONEY_STRING_MAXLENGTH] = {}; + switch (widgetIndex) { case WIDX_CLOSE: @@ -6599,32 +6595,9 @@ private: * * rct2: 0x006AEAB4 */ -static WindowBase* Open(const Ride& ride) +static RideWindow* WindowRideOpen(const Ride& ride) { - WindowBase* w; - - w = WindowCreateAutoPos(316, 207, window_ride_page_events[0], WindowClass::Ride, WF_10 | WF_RESIZABLE); - w->widgets = window_ride_page_widgets[WINDOW_RIDE_PAGE_MAIN]; - w->hold_down_widgets = window_ride_page_hold_down_widgets[WINDOW_RIDE_PAGE_MAIN]; - w->rideId = ride.id; - - w->page = WINDOW_RIDE_PAGE_MAIN; - w->vehicleIndex = 0; - w->frame_no = 0; - w->list_information_type = 0; - w->picked_peep_frame = 0; - w->ride_colour = 0; - DisableTabs(w); - w->min_width = 316; - w->min_height = 180; - w->max_width = 500; - w->max_height = 450; - - UpdateOverallView(ride); - - PopulateVehicleTypeDropdown(ride, true); - - return w; + return WindowCreate(WindowClass::Ride, 316, 207, WF_10 | WF_RESIZABLE, ride); } /** @@ -6638,16 +6611,15 @@ WindowBase* WindowRideMainOpen(const Ride& ride) return nullptr; } - WindowBase* w = WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying()); + RideWindow* w = static_cast(WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying())); if (w == nullptr) { w = WindowRideOpen(ride); - w->ride.var_482 = -1; - w->ride.view = 0; + w->SetViewIndex(0); } - else if (w->ride.view >= (1 + ride.NumTrains + ride.num_stations)) + else if (w->GetViewIndex() >= (1 + ride.NumTrains + ride.num_stations)) { - w->ride.view = 0; + w->SetViewIndex(0); } if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) @@ -6660,10 +6632,10 @@ WindowBase* WindowRideMainOpen(const Ride& ride) if (w->page != WINDOW_RIDE_PAGE_MAIN) { - SetPage(w, WINDOW_RIDE_PAGE_MAIN); + w->SetPage(WINDOW_RIDE_PAGE_MAIN); } - InitViewport(w); + w->OnViewportRotate(); return w; } @@ -6679,11 +6651,10 @@ static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationI if (ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) return WindowRideMainOpen(ride); - auto w = WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying()); + auto* w = static_cast(WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying())); if (w == nullptr) { w = WindowRideOpen(ride); - w->ride.var_482 = -1; } if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification @@ -6692,18 +6663,6 @@ static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationI ToolCancel(); } - w->page = WINDOW_RIDE_PAGE_MAIN; - w->width = 316; - w->height = 180; - w->Invalidate(); - - w->widgets = window_ride_page_widgets[w->page]; - w->hold_down_widgets = window_ride_page_hold_down_widgets[w->page]; - w->event_handlers = window_ride_page_events[w->page]; - w->pressed_widgets = 0; - DisableTabs(w); - InitScrollWidgets(); - // View for (int32_t i = stationIndex.ToUnderlying(); i >= 0; i--) { @@ -6713,8 +6672,7 @@ static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationI } } - w->ride.view = 1 + ride.NumTrains + stationIndex.ToUnderlying(); - InitViewport(w); + w->SetViewIndex(1 + ride.NumTrains + stationIndex.ToUnderlying()); return w; } @@ -6775,7 +6733,7 @@ WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) return nullptr; // Get view index - int32_t view = 1; + int16_t view = 1; for (int32_t i = 0; i <= OpenRCT2::Limits::MaxTrainsPerRide; i++) { if (ride->vehicles[i] == headVehicleSpriteIndex) @@ -6784,7 +6742,7 @@ WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) view++; } - WindowBase* w = WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying()); + auto* w = static_cast(WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying())); if (w != nullptr) { w->Invalidate(); @@ -6796,7 +6754,7 @@ WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) } int32_t openedPeepWindow = 0; - if (w->ride.view == view) + if (w->GetViewIndex() == view) { int32_t numPeepsLeft = vehicle->num_peeps; for (int32_t i = 0; i < 32 && numPeepsLeft > 0; i++) @@ -6819,31 +6777,59 @@ WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) } } - w = openedPeepWindow ? WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying()) - : WindowBringToFrontByNumber(WindowClass::Ride, ride->id.ToUnderlying()); + w = static_cast( + openedPeepWindow ? WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying()) + : WindowBringToFrontByNumber(WindowClass::Ride, ride->id.ToUnderlying())); } if (w == nullptr) { w = WindowRideOpen(*ride); - w->ride.var_482 = -1; } - w->page = WINDOW_RIDE_PAGE_MAIN; - w->width = 316; - w->height = 180; - w->Invalidate(); - - w->widgets = window_ride_page_widgets[w->page]; - w->hold_down_widgets = window_ride_page_hold_down_widgets[w->page]; - w->event_handlers = window_ride_page_events[w->page]; - w->pressed_widgets = 0; - DisableTabs(w); - InitScrollWidgets(); - - w->ride.view = view; - InitViewport(w); + w->SetViewIndex(view); w->Invalidate(); return w; } + +void WindowRideInvalidateVehicle(const Vehicle& vehicle) +{ + auto* w = static_cast(WindowFindByNumber(WindowClass::Ride, vehicle.ride.ToUnderlying())); + if (w == nullptr) + return; + + auto ride = vehicle.GetRide(); + auto viewVehicleIndex = w->GetViewIndex() - 1; + if (ride == nullptr || viewVehicleIndex < 0 || viewVehicleIndex >= ride->NumTrains) + return; + + if (vehicle.Id != ride->vehicles[viewVehicleIndex]) + return; + + w->Invalidate(); +} + +static void CancelScenerySelection() +{ + gGamePaused &= ~GAME_PAUSED_SAVING_TRACK; + gTrackDesignSaveMode = false; + OpenRCT2::Audio::Resume(); + + WindowBase* main_w = WindowGetMain(); + if (main_w != nullptr) + { + main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); + } + + GfxInvalidateScreen(); + ToolCancel(); +} + +void WindowRideMeasurementsDesignCancel() +{ + if (gTrackDesignSaveMode) + { + CancelScenerySelection(); + } +} diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index fbca14f94b..0bb21aea4f 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -140,6 +140,7 @@ void WindowRideListRefreshList(WindowBase* w); WindowBase* WindowRideMainOpen(const Ride& ride); WindowBase* WindowRideOpenTrack(TileElement* tileElement); WindowBase* WindowRideOpenVehicle(Vehicle* vehicle); +void WindowRideInvalidateVehicle(const Vehicle& vehicle); void WindowRideMeasurementsDesignCancel(); // rct2: 0x00F635EE diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 06e366958a..7c18d88e18 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -280,12 +280,6 @@ struct NewRideVariables uint16_t selected_ride_countdown; // 488 }; -struct RideVariables -{ - int16_t view; - int32_t var_482; - int32_t var_486; -}; struct TrackListVariables { diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index 92d8827a27..1947840acf 100644 --- a/src/openrct2/interface/Window_internal.h +++ b/src/openrct2/interface/Window_internal.h @@ -59,7 +59,6 @@ struct WindowBase { CampaignVariables campaign; NewRideVariables new_ride; - RideVariables ride; TrackListVariables track_list; ErrorVariables error; void* custom_info; From f0658202ef26f43c5fb4b845ae4974731d822894 Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Tue, 20 Jun 2023 08:52:00 +0100 Subject: [PATCH 5/5] Fix mistake --- src/openrct2-ui/windows/Ride.cpp | 2 +- src/openrct2/interface/Window.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 08d73c7cdf..60a9c39952 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -1067,7 +1067,7 @@ public: private: void DrawTabImage(DrawPixelInfo& dpi, int32_t tab, int32_t spriteIndex) { - WidgetIndex widgetIndex = WIDX_TAB_1 + page; + WidgetIndex widgetIndex = WIDX_TAB_1 + tab; if (!WidgetIsDisabled(*this, widgetIndex)) { diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 7c18d88e18..9be1c449c9 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -280,7 +280,6 @@ struct NewRideVariables uint16_t selected_ride_countdown; // 488 }; - struct TrackListVariables { bool track_list_being_updated;