From 7a60e7f2e09e002c13765380636e108cd2249a08 Mon Sep 17 00:00:00 2001 From: Duncan Date: Sun, 10 Mar 2024 13:52:35 +0000 Subject: [PATCH] OpenRCT2::Ui::Windows namespace for OpenRCT2 Ui Windows (#21559) * Move all the windows into OpenRCT2::Ui::Windows namespace * Fix missing symbol * Clang format * Call the correct FormatStringID --- src/openrct2-ui/WindowManager.cpp | 1 + src/openrct2-ui/input/InputManager.cpp | 2 +- src/openrct2-ui/input/MouseInput.cpp | 2 + src/openrct2-ui/input/Shortcuts.cpp | 1 + src/openrct2-ui/interface/Dropdown.h | 50 +- src/openrct2-ui/interface/LandTool.cpp | 1 + .../interface/ViewportInteraction.cpp | 1 + src/openrct2-ui/interface/Window.cpp | 3 +- src/openrct2-ui/interface/Window.h | 31 + src/openrct2-ui/windows/About.cpp | 382 +- src/openrct2-ui/windows/AssetPacks.cpp | 529 +- src/openrct2-ui/windows/Banner.cpp | 431 +- src/openrct2-ui/windows/Changelog.cpp | 543 +- src/openrct2-ui/windows/Cheats.cpp | 1456 +- src/openrct2-ui/windows/ClearScenery.cpp | 367 +- src/openrct2-ui/windows/CustomCurrency.cpp | 334 +- src/openrct2-ui/windows/DebugPaint.cpp | 188 +- .../windows/DemolishRidePrompt.cpp | 148 +- src/openrct2-ui/windows/Dropdown.cpp | 865 +- .../windows/EditorBottomToolbar.cpp | 631 +- .../windows/EditorInventionsList.cpp | 1110 +- .../windows/EditorObjectSelection.cpp | 2776 ++-- .../windows/EditorObjectiveOptions.cpp | 1933 +-- .../windows/EditorScenarioOptions.cpp | 1966 +-- src/openrct2-ui/windows/Error.cpp | 261 +- src/openrct2-ui/windows/Finances.cpp | 1489 +- src/openrct2-ui/windows/Footpath.cpp | 2547 ++-- src/openrct2-ui/windows/GameBottomToolbar.cpp | 1151 +- src/openrct2-ui/windows/Guest.cpp | 3364 ++--- src/openrct2-ui/windows/GuestList.cpp | 1690 +-- src/openrct2-ui/windows/InstallTrack.cpp | 653 +- src/openrct2-ui/windows/Land.cpp | 563 +- src/openrct2-ui/windows/LandRights.cpp | 683 +- src/openrct2-ui/windows/LoadSave.cpp | 2075 +-- src/openrct2-ui/windows/Main.cpp | 102 +- src/openrct2-ui/windows/Map.cpp | 2635 ++-- src/openrct2-ui/windows/MapGen.cpp | 2227 +-- src/openrct2-ui/windows/MapTooltip.cpp | 185 +- src/openrct2-ui/windows/MazeConstruction.cpp | 710 +- src/openrct2-ui/windows/Multiplayer.cpp | 1515 +- src/openrct2-ui/windows/NetworkStatus.cpp | 223 +- src/openrct2-ui/windows/NewCampaign.cpp | 644 +- src/openrct2-ui/windows/NewRide.cpp | 1864 +-- src/openrct2-ui/windows/News.cpp | 508 +- src/openrct2-ui/windows/NewsOptions.cpp | 349 +- src/openrct2-ui/windows/ObjectLoadError.cpp | 971 +- src/openrct2-ui/windows/Options.cpp | 3189 ++-- src/openrct2-ui/windows/Park.cpp | 2025 +-- src/openrct2-ui/windows/PatrolArea.cpp | 458 +- src/openrct2-ui/windows/Player.cpp | 1021 +- .../windows/RefurbishRidePrompt.cpp | 148 +- src/openrct2-ui/windows/Research.cpp | 826 +- src/openrct2-ui/windows/Ride.cpp | 12251 ++++++++-------- src/openrct2-ui/windows/RideConstruction.cpp | 8195 ++++++----- src/openrct2-ui/windows/RideList.cpp | 1741 +-- src/openrct2-ui/windows/SavePrompt.cpp | 311 +- src/openrct2-ui/windows/ScenarioSelect.cpp | 1287 +- src/openrct2-ui/windows/Scenery.cpp | 3157 ++-- src/openrct2-ui/windows/SceneryScatter.cpp | 299 +- src/openrct2-ui/windows/ServerList.cpp | 925 +- src/openrct2-ui/windows/ServerStart.cpp | 417 +- src/openrct2-ui/windows/ShortcutKeys.cpp | 988 +- src/openrct2-ui/windows/Sign.cpp | 589 +- src/openrct2-ui/windows/Staff.cpp | 2094 +-- src/openrct2-ui/windows/StaffFirePrompt.cpp | 115 +- src/openrct2-ui/windows/StaffList.cpp | 1264 +- src/openrct2-ui/windows/TextInput.cpp | 726 +- src/openrct2-ui/windows/Themes.cpp | 1278 +- src/openrct2-ui/windows/TileInspector.cpp | 3998 ++--- src/openrct2-ui/windows/TitleExit.cpp | 73 +- src/openrct2-ui/windows/TitleLogo.cpp | 103 +- src/openrct2-ui/windows/TitleMenu.cpp | 405 +- src/openrct2-ui/windows/TitleOptions.cpp | 72 +- src/openrct2-ui/windows/Tooltip.cpp | 320 +- src/openrct2-ui/windows/TopToolbar.cpp | 6528 ++++---- src/openrct2-ui/windows/TrackDesignManage.cpp | 313 +- src/openrct2-ui/windows/TrackDesignPlace.cpp | 1008 +- src/openrct2-ui/windows/TrackList.cpp | 1290 +- src/openrct2-ui/windows/Transparency.cpp | 319 +- src/openrct2-ui/windows/ViewClipping.cpp | 597 +- src/openrct2-ui/windows/Viewport.cpp | 333 +- src/openrct2-ui/windows/Water.cpp | 300 +- src/openrct2-ui/windows/Window.h | 330 +- src/openrct2/interface/Window.h | 25 - src/openrct2/ride/Ride.h | 4 - 85 files changed, 49933 insertions(+), 49549 deletions(-) diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 16466cb4a2..5d47c35b3f 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -28,6 +28,7 @@ #include using namespace OpenRCT2::Ui; +using namespace OpenRCT2::Ui::Windows; class WindowManager final : public IWindowManager { diff --git a/src/openrct2-ui/input/InputManager.cpp b/src/openrct2-ui/input/InputManager.cpp index cf0c612ec6..8c4b32fec6 100644 --- a/src/openrct2-ui/input/InputManager.cpp +++ b/src/openrct2-ui/input/InputManager.cpp @@ -202,7 +202,7 @@ void InputManager::Process(const InputEvent& e) { if (e.State == InputEventState::Release) { - WindowTextInputKey(w, e.Button); + OpenRCT2::Ui::Windows::WindowTextInputKey(w, e.Button); } return; } diff --git a/src/openrct2-ui/input/MouseInput.cpp b/src/openrct2-ui/input/MouseInput.cpp index af5cf6be10..6c55f56e16 100644 --- a/src/openrct2-ui/input/MouseInput.cpp +++ b/src/openrct2-ui/input/MouseInput.cpp @@ -34,6 +34,8 @@ #include #include +using namespace OpenRCT2::Ui::Windows; + struct RCTMouseData { uint32_t x; diff --git a/src/openrct2-ui/input/Shortcuts.cpp b/src/openrct2-ui/input/Shortcuts.cpp index 8081c03527..1f457247e7 100644 --- a/src/openrct2-ui/input/Shortcuts.cpp +++ b/src/openrct2-ui/input/Shortcuts.cpp @@ -47,6 +47,7 @@ using namespace OpenRCT2; using namespace OpenRCT2::Ui; +using namespace OpenRCT2::Ui::Windows; #pragma region Shortcut Commands diff --git a/src/openrct2-ui/interface/Dropdown.h b/src/openrct2-ui/interface/Dropdown.h index 38a52af3d3..d902d37dcc 100644 --- a/src/openrct2-ui/interface/Dropdown.h +++ b/src/openrct2-ui/interface/Dropdown.h @@ -37,29 +37,33 @@ namespace Dropdown void SetImage(int32_t index, ImageId image); } // namespace Dropdown -extern int32_t gDropdownNumItems; -extern Dropdown::Item gDropdownItems[Dropdown::ItemsMaxSize]; -extern bool gDropdownIsColour; -extern int32_t gDropdownLastColourHover; -extern int32_t gDropdownHighlightedIndex; -extern int32_t gDropdownDefaultIndex; +namespace OpenRCT2::Ui::Windows +{ + extern int32_t gDropdownNumItems; + extern Dropdown::Item gDropdownItems[Dropdown::ItemsMaxSize]; + extern bool gDropdownIsColour; + extern int32_t gDropdownLastColourHover; + extern int32_t gDropdownHighlightedIndex; + extern int32_t gDropdownDefaultIndex; -void WindowDropdownShowText(const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t flags, size_t num_items); -void WindowDropdownShowTextCustomWidth( - const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t custom_height, uint8_t flags, size_t num_items, - int32_t width); -void WindowDropdownShowImage( - int32_t x, int32_t y, int32_t extray, uint8_t colour, uint8_t flags, int32_t numItems, int32_t itemWidth, - int32_t itemHeight, int32_t numColumns); -void WindowDropdownClose(); -int32_t DropdownIndexFromPoint(const ScreenCoordsXY& loc, WindowBase* w); -void WindowDropdownShowColour(WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour); -void WindowDropdownShowColourAvailable( - WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour, uint32_t availableColours); -uint32_t DropdownGetAppropriateImageDropdownItemsPerRow(uint32_t numItems); -bool WindowDropDownHasMultipleColumns(size_t numItems); + void WindowDropdownShowText( + const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t flags, size_t num_items); + void WindowDropdownShowTextCustomWidth( + const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t custom_height, uint8_t flags, size_t num_items, + int32_t width); + void WindowDropdownShowImage( + int32_t x, int32_t y, int32_t extray, uint8_t colour, uint8_t flags, int32_t numItems, int32_t itemWidth, + int32_t itemHeight, int32_t numColumns); + void WindowDropdownClose(); + int32_t DropdownIndexFromPoint(const ScreenCoordsXY& loc, WindowBase* w); + void WindowDropdownShowColour(WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour); + void WindowDropdownShowColourAvailable( + WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour, uint32_t availableColours); + uint32_t DropdownGetAppropriateImageDropdownItemsPerRow(uint32_t numItems); + bool WindowDropDownHasMultipleColumns(size_t numItems); -colour_t ColourDropDownIndexToColour(uint8_t ddidx); + colour_t ColourDropDownIndexToColour(uint8_t ddidx); +} // namespace OpenRCT2::Ui::Windows namespace Dropdown { @@ -120,8 +124,8 @@ namespace Dropdown for (int i = 0; i < N; ++i) { const ItemExt& item = items[i]; - gDropdownItems[i].Format = item.itemFormat; - gDropdownItems[i].Args = item.stringId; + OpenRCT2::Ui::Windows::gDropdownItems[i].Format = item.itemFormat; + OpenRCT2::Ui::Windows::gDropdownItems[i].Args = item.stringId; } } diff --git a/src/openrct2-ui/interface/LandTool.cpp b/src/openrct2-ui/interface/LandTool.cpp index 35a224adf9..8298660472 100644 --- a/src/openrct2-ui/interface/LandTool.cpp +++ b/src/openrct2-ui/interface/LandTool.cpp @@ -22,6 +22,7 @@ #include using namespace OpenRCT2; +using namespace OpenRCT2::Ui::Windows; // clang-format off static uint16_t toolSizeSpriteIndices[] = diff --git a/src/openrct2-ui/interface/ViewportInteraction.cpp b/src/openrct2-ui/interface/ViewportInteraction.cpp index 1d08323064..7c763eab7b 100644 --- a/src/openrct2-ui/interface/ViewportInteraction.cpp +++ b/src/openrct2-ui/interface/ViewportInteraction.cpp @@ -53,6 +53,7 @@ #include using namespace OpenRCT2; +using namespace OpenRCT2::Ui::Windows; static void ViewportInteractionRemoveScenery(const SmallSceneryElement& smallSceneryElement, const CoordsXY& mapCoords); static void ViewportInteractionRemoveFootpath(const PathElement& pathElement, const CoordsXY& mapCoords); diff --git a/src/openrct2-ui/interface/Window.cpp b/src/openrct2-ui/interface/Window.cpp index 9f0ee0c34f..006d09f793 100644 --- a/src/openrct2-ui/interface/Window.cpp +++ b/src/openrct2-ui/interface/Window.cpp @@ -807,7 +807,8 @@ void Window::TextInputOpen( WidgetIndex callWidget, StringId title, StringId description, const Formatter& descriptionArgs, StringId existingText, uintptr_t existingArgs, int32_t maxLength) { - WindowTextInputOpen(this, callWidget, title, description, descriptionArgs, existingText, existingArgs, maxLength); + OpenRCT2::Ui::Windows::WindowTextInputOpen( + this, callWidget, title, description, descriptionArgs, existingText, existingArgs, maxLength); } void WindowAlignTabs(WindowBase* w, WidgetIndex start_tab_id, WidgetIndex end_tab_id) diff --git a/src/openrct2-ui/interface/Window.h b/src/openrct2-ui/interface/Window.h index c3a3d18d7e..4bc771e920 100644 --- a/src/openrct2-ui/interface/Window.h +++ b/src/openrct2-ui/interface/Window.h @@ -41,3 +41,34 @@ void WindowAllWheelInput(); void ApplyScreenSaverLockSetting(); void WindowAlignTabs(WindowBase* w, WidgetIndex start_tab_id, WidgetIndex end_tab_id); ScreenCoordsXY WindowGetViewportSoundIconPos(WindowBase& w); + +namespace OpenRCT2::Ui::Windows +{ + void RideConstructionToolupdateEntranceExit(const ScreenCoordsXY& screenCoords); + void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords); + void RideConstructionTooldownConstruct(const ScreenCoordsXY& screenCoords); + void UpdateGhostTrackAndArrow(); + void WindowRideConstructionKeyboardShortcutTurnLeft(); + void WindowRideConstructionKeyboardShortcutTurnRight(); + void WindowRideConstructionKeyboardShortcutUseTrackDefault(); + void WindowRideConstructionKeyboardShortcutSlopeDown(); + void WindowRideConstructionKeyboardShortcutSlopeUp(); + void WindowRideConstructionKeyboardShortcutChainLiftToggle(); + void WindowRideConstructionKeyboardShortcutBankLeft(); + void WindowRideConstructionKeyboardShortcutBankRight(); + void WindowRideConstructionKeyboardShortcutPreviousTrack(); + void WindowRideConstructionKeyboardShortcutNextTrack(); + void WindowRideConstructionKeyboardShortcutBuildCurrent(); + void WindowRideConstructionKeyboardShortcutDemolishCurrent(); + + void WindowFootpathKeyboardShortcutTurnLeft(); + void WindowFootpathKeyboardShortcutTurnRight(); + void WindowFootpathKeyboardShortcutSlopeDown(); + void WindowFootpathKeyboardShortcutSlopeUp(); + void WindowFootpathKeyboardShortcutBuildCurrent(); + void WindowFootpathKeyboardShortcutDemolishCurrent(); + + void WindowTileInspectorKeyboardShortcutToggleInvisibility(); + + extern const StringId ColourSchemeNames[4]; +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/About.cpp b/src/openrct2-ui/windows/About.cpp index 500dd9afcf..d5b358aaa2 100644 --- a/src/openrct2-ui/windows/About.cpp +++ b/src/openrct2-ui/windows/About.cpp @@ -19,14 +19,14 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr int32_t WW = 400; + static constexpr int32_t WH = 450; + static constexpr StringId WINDOW_TITLE = STR_ABOUT; + static constexpr int32_t TABHEIGHT = 50; -static constexpr int32_t WW = 400; -static constexpr int32_t WH = 450; -static constexpr StringId WINDOW_TITLE = STR_ABOUT; -static constexpr int32_t TABHEIGHT = 50; - -// clang-format off + // clang-format off enum { WINDOW_ABOUT_PAGE_OPENRCT2, @@ -83,205 +83,207 @@ static Widget *_windowAboutPageWidgets[] = { _windowAboutRCT2Widgets, }; -// clang-format on + // clang-format on -class AboutWindow final : public Window -{ -public: - void OnOpen() override + class AboutWindow final : public Window { - widgets = _windowAboutOpenRCT2Widgets; - - WindowInitScrollWidgets(*this); - SetPage(WINDOW_ABOUT_PAGE_OPENRCT2); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_TAB_ABOUT_OPENRCT2: - case WIDX_TAB_ABOUT_RCT2: - SetPage(widgetIndex - WIDX_TAB_ABOUT_OPENRCT2); - break; - case WIDX_JOIN_DISCORD: - OpenRCT2::GetContext()->GetUiContext()->OpenURL("https://discord.gg/ZXZd8D8"); - break; - case WIDX_CHANGELOG: - ContextOpenWindow(WindowClass::Changelog); - break; - case WIDX_NEW_VERSION: - ContextOpenWindowView(WV_NEW_VERSION_INFO); - break; - case WIDX_COPY_BUILD_INFO: - SDL_SetClipboardText(gVersionInfoFull); - break; - case WIDX_CONTRIBUTORS_BUTTON: - ContextOpenWindowView(WV_CONTRIBUTORS); - break; + widgets = _windowAboutOpenRCT2Widgets; + + WindowInitScrollWidgets(*this); + SetPage(WINDOW_ABOUT_PAGE_OPENRCT2); } - } - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - const auto& aboutOpenRCT2 = widgets[WIDX_TAB_ABOUT_OPENRCT2]; - const auto& aboutRCT2 = widgets[WIDX_TAB_ABOUT_RCT2]; - - int32_t y = windowPos.y + aboutOpenRCT2.midY() - 3; - ScreenCoordsXY aboutOpenRCT2Coords(windowPos.x + aboutOpenRCT2.left + 45, y); - ScreenCoordsXY aboutRCT2Coords(windowPos.x + aboutRCT2.left + 45, y); - - // Draw tab names + void OnMouseUp(WidgetIndex widgetIndex) override { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB_ABOUT_OPENRCT2: + case WIDX_TAB_ABOUT_RCT2: + SetPage(widgetIndex - WIDX_TAB_ABOUT_OPENRCT2); + break; + case WIDX_JOIN_DISCORD: + OpenRCT2::GetContext()->GetUiContext()->OpenURL("https://discord.gg/ZXZd8D8"); + break; + case WIDX_CHANGELOG: + ContextOpenWindow(WindowClass::Changelog); + break; + case WIDX_NEW_VERSION: + ContextOpenWindowView(WV_NEW_VERSION_INFO); + break; + case WIDX_COPY_BUILD_INFO: + SDL_SetClipboardText(gVersionInfoFull); + break; + case WIDX_CONTRIBUTORS_BUTTON: + ContextOpenWindowView(WV_CONTRIBUTORS); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + const auto& aboutOpenRCT2 = widgets[WIDX_TAB_ABOUT_OPENRCT2]; + const auto& aboutRCT2 = widgets[WIDX_TAB_ABOUT_RCT2]; + + int32_t y = windowPos.y + aboutOpenRCT2.midY() - 3; + ScreenCoordsXY aboutOpenRCT2Coords(windowPos.x + aboutOpenRCT2.left + 45, y); + ScreenCoordsXY aboutRCT2Coords(windowPos.x + aboutRCT2.left + 45, y); + + // Draw tab names + { + auto ft = Formatter(); + ft.Add(STR_TITLE_SEQUENCE_OPENRCT2); + DrawTextWrapped( + dpi, aboutOpenRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, + { COLOUR_AQUAMARINE, TextAlignment::CENTRE }); + } + { + auto ft = Formatter(); + ft.Add(STR_TITLE_SEQUENCE_RCT2); + DrawTextWrapped( + dpi, aboutRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE }); + } + + if (page == WINDOW_ABOUT_PAGE_OPENRCT2) + { + DrawOpenRCT2Info(dpi); + } + else if (page == WINDOW_ABOUT_PAGE_RCT2) + { + DrawRCT2Info(dpi); + } + } + + private: + /** + * @brief Set which tab to show + */ + void SetPage(int32_t p) + { + page = p; + frame_no = 0; + pressed_widgets = 0; + widgets = _windowAboutPageWidgets[p]; + + switch (p) + { + case WINDOW_ABOUT_PAGE_OPENRCT2: + pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_OPENRCT2); + break; + case WINDOW_ABOUT_PAGE_RCT2: + pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_RCT2); + break; + } + + WindowInitScrollWidgets(*this); + Invalidate(); + } + + /** + * @brief Draw OpenRCT2 info on open tab + */ + void DrawOpenRCT2Info(DrawPixelInfo& dpi) + { + // Draw logo on placeholder widget + ScreenCoordsXY logoCoords = windowPos + + ScreenCoordsXY(widgets[WIDX_OPENRCT2_LOGO].left, widgets[WIDX_OPENRCT2_LOGO].top); + GfxDrawSprite(dpi, ImageId(SPR_G2_LOGO), logoCoords); + // Version info + utf8 buffer[256]; + utf8* ch = buffer; + OpenRCT2WriteFullVersionInfo(ch, sizeof(buffer) - (ch - buffer)); auto ft = Formatter(); - ft.Add(STR_TITLE_SEQUENCE_OPENRCT2); - DrawTextWrapped( - dpi, aboutOpenRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE }); - } - { - auto ft = Formatter(); - ft.Add(STR_TITLE_SEQUENCE_RCT2); - DrawTextWrapped( - dpi, aboutRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE }); + ft.Add(buffer); + + auto const& versionPlaceholder = widgets[WIDX_VERSION]; + auto versionPlaceHolderWidth = versionPlaceholder.right - versionPlaceholder.left; + auto centreX = versionPlaceholder.left + versionPlaceHolderWidth / 2; + auto centreY = (versionPlaceholder.top + versionPlaceholder.bottom - FontGetLineHeight(FontStyle::Medium)) / 2; + auto centrePos = windowPos + ScreenCoordsXY(centreX, centreY); + DrawTextWrapped(dpi, centrePos, versionPlaceHolderWidth, STR_STRING, ft, { colours[1], TextAlignment::CENTRE }); + + // Shows the update available button + if (OpenRCT2::GetContext()->HasNewVersionInfo()) + { + widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button; + _windowAboutOpenRCT2Widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button; + } + + // Draw the rest of the text + Formatter ft2{}; + TextPaint tp{ colours[1], TextAlignment::CENTRE }; + auto textCoords = windowPos + ScreenCoordsXY((width / 2) - 1, 240); + auto textWidth = WW - 20; + + textCoords += ScreenCoordsXY( + 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_2, ft2, tp) + 5); // More info + textCoords += ScreenCoordsXY( + 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_3, ft2, tp) + 5); // Copyright + textCoords += ScreenCoordsXY( + 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_TITLE, ft2, tp) + 5); // Title Theme + textCoords += ScreenCoordsXY( + 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_FAIRGROUND_ORGAN, ft2, tp) + 5); // Fairground organ + textCoords += ScreenCoordsXY( + 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_1, ft2, tp)); // Special Thanks + textCoords += ScreenCoordsXY( + 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_2, ft2, tp)); // Company names } - if (page == WINDOW_ABOUT_PAGE_OPENRCT2) + /** + * @brief Draw RCT2 info on open tab + */ + void DrawRCT2Info(DrawPixelInfo& dpi) { - DrawOpenRCT2Info(dpi); - } - else if (page == WINDOW_ABOUT_PAGE_RCT2) - { - DrawRCT2Info(dpi); - } - } + int32_t yPage = windowPos.y + widgets[WIDX_PAGE_BACKGROUND].top + 5; -private: - /** - * @brief Set which tab to show - */ - void SetPage(int32_t p) - { - page = p; - frame_no = 0; - pressed_widgets = 0; - widgets = _windowAboutPageWidgets[p]; + auto screenCoords = ScreenCoordsXY{ windowPos.x + 200, yPage + 5 }; - switch (p) - { - case WINDOW_ABOUT_PAGE_OPENRCT2: - pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_OPENRCT2); - break; - case WINDOW_ABOUT_PAGE_RCT2: - pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_RCT2); - break; + int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + + // Credits + DrawTextBasic(dpi, screenCoords, STR_COPYRIGHT_CS, {}, { TextAlignment::CENTRE }); + screenCoords.y += lineHeight + 74; + DrawTextBasic(dpi, screenCoords, STR_DESIGNED_AND_PROGRAMMED_BY_CS, {}, { TextAlignment::CENTRE }); + screenCoords.y += lineHeight; + DrawTextBasic(dpi, screenCoords, STR_GRAPHICS_BY_SF, {}, { TextAlignment::CENTRE }); + screenCoords.y += lineHeight; + DrawTextBasic(dpi, screenCoords, STR_SOUND_AND_MUSIC_BY_AB, {}, { TextAlignment::CENTRE }); + screenCoords.y += lineHeight; + DrawTextBasic(dpi, screenCoords, STR_ADDITIONAL_SOUNDS_RECORDED_BY_DE, {}, { TextAlignment::CENTRE }); + screenCoords.y += lineHeight + 3; + DrawTextBasic(dpi, screenCoords, STR_REPRESENTATION_BY_JL, {}, { TextAlignment::CENTRE }); + screenCoords.y += 2 * lineHeight + 5; + DrawTextBasic(dpi, screenCoords, STR_THANKS_TO, {}, { TextAlignment::CENTRE }); + screenCoords.y += lineHeight; + DrawTextBasic(dpi, screenCoords, STR_THANKS_TO_PEOPLE, {}, { TextAlignment::CENTRE }); + screenCoords.y += 2 * lineHeight + 5; + DrawTextBasic(dpi, screenCoords, STR_LICENSED_TO_INFOGRAMES_INTERACTIVE_INC, {}, { TextAlignment::CENTRE }); + + // Images + GfxDrawSprite(dpi, ImageId(SPR_CREDITS_CHRIS_SAWYER_SMALL), { windowPos.x + 92, yPage + 24 }); + + // Licence } - WindowInitScrollWidgets(*this); - Invalidate(); - } + void OnResize() override + { + ResizeFrameWithPage(); + } + }; /** - * @brief Draw OpenRCT2 info on open tab + * + * rct2: 0x0066D2AC */ - void DrawOpenRCT2Info(DrawPixelInfo& dpi) + WindowBase* WindowAboutOpen() { - // Draw logo on placeholder widget - ScreenCoordsXY logoCoords = windowPos - + ScreenCoordsXY(widgets[WIDX_OPENRCT2_LOGO].left, widgets[WIDX_OPENRCT2_LOGO].top); - GfxDrawSprite(dpi, ImageId(SPR_G2_LOGO), logoCoords); - // Version info - utf8 buffer[256]; - utf8* ch = buffer; - OpenRCT2WriteFullVersionInfo(ch, sizeof(buffer) - (ch - buffer)); - auto ft = Formatter(); - ft.Add(buffer); - - auto const& versionPlaceholder = widgets[WIDX_VERSION]; - auto versionPlaceHolderWidth = versionPlaceholder.right - versionPlaceholder.left; - auto centreX = versionPlaceholder.left + versionPlaceHolderWidth / 2; - auto centreY = (versionPlaceholder.top + versionPlaceholder.bottom - FontGetLineHeight(FontStyle::Medium)) / 2; - auto centrePos = windowPos + ScreenCoordsXY(centreX, centreY); - DrawTextWrapped(dpi, centrePos, versionPlaceHolderWidth, STR_STRING, ft, { colours[1], TextAlignment::CENTRE }); - - // Shows the update available button - if (OpenRCT2::GetContext()->HasNewVersionInfo()) - { - widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button; - _windowAboutOpenRCT2Widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button; - } - - // Draw the rest of the text - Formatter ft2{}; - TextPaint tp{ colours[1], TextAlignment::CENTRE }; - auto textCoords = windowPos + ScreenCoordsXY((width / 2) - 1, 240); - auto textWidth = WW - 20; - - textCoords += ScreenCoordsXY( - 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_2, ft2, tp) + 5); // More info - textCoords += ScreenCoordsXY( - 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_3, ft2, tp) + 5); // Copyright - textCoords += ScreenCoordsXY( - 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_TITLE, ft2, tp) + 5); // Title Theme - textCoords += ScreenCoordsXY( - 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_FAIRGROUND_ORGAN, ft2, tp) + 5); // Fairground organ - textCoords += ScreenCoordsXY( - 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_1, ft2, tp)); // Special Thanks - textCoords += ScreenCoordsXY( - 0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_2, ft2, tp)); // Company names + return WindowFocusOrCreate(WindowClass::About, WW, WH, WF_CENTRE_SCREEN); } - - /** - * @brief Draw RCT2 info on open tab - */ - void DrawRCT2Info(DrawPixelInfo& dpi) - { - int32_t yPage = windowPos.y + widgets[WIDX_PAGE_BACKGROUND].top + 5; - - auto screenCoords = ScreenCoordsXY{ windowPos.x + 200, yPage + 5 }; - - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - - // Credits - DrawTextBasic(dpi, screenCoords, STR_COPYRIGHT_CS, {}, { TextAlignment::CENTRE }); - screenCoords.y += lineHeight + 74; - DrawTextBasic(dpi, screenCoords, STR_DESIGNED_AND_PROGRAMMED_BY_CS, {}, { TextAlignment::CENTRE }); - screenCoords.y += lineHeight; - DrawTextBasic(dpi, screenCoords, STR_GRAPHICS_BY_SF, {}, { TextAlignment::CENTRE }); - screenCoords.y += lineHeight; - DrawTextBasic(dpi, screenCoords, STR_SOUND_AND_MUSIC_BY_AB, {}, { TextAlignment::CENTRE }); - screenCoords.y += lineHeight; - DrawTextBasic(dpi, screenCoords, STR_ADDITIONAL_SOUNDS_RECORDED_BY_DE, {}, { TextAlignment::CENTRE }); - screenCoords.y += lineHeight + 3; - DrawTextBasic(dpi, screenCoords, STR_REPRESENTATION_BY_JL, {}, { TextAlignment::CENTRE }); - screenCoords.y += 2 * lineHeight + 5; - DrawTextBasic(dpi, screenCoords, STR_THANKS_TO, {}, { TextAlignment::CENTRE }); - screenCoords.y += lineHeight; - DrawTextBasic(dpi, screenCoords, STR_THANKS_TO_PEOPLE, {}, { TextAlignment::CENTRE }); - screenCoords.y += 2 * lineHeight + 5; - DrawTextBasic(dpi, screenCoords, STR_LICENSED_TO_INFOGRAMES_INTERACTIVE_INC, {}, { TextAlignment::CENTRE }); - - // Images - GfxDrawSprite(dpi, ImageId(SPR_CREDITS_CHRIS_SAWYER_SMALL), { windowPos.x + 92, yPage + 24 }); - - // Licence - } - - void OnResize() override - { - ResizeFrameWithPage(); - } -}; - -/** - * - * rct2: 0x0066D2AC - */ -WindowBase* WindowAboutOpen() -{ - return WindowFocusOrCreate(WindowClass::About, WW, WH, WF_CENTRE_SCREEN); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/AssetPacks.cpp b/src/openrct2-ui/windows/AssetPacks.cpp index 6114257720..812c87c31c 100644 --- a/src/openrct2-ui/windows/AssetPacks.cpp +++ b/src/openrct2-ui/windows/AssetPacks.cpp @@ -18,13 +18,13 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_ASSET_PACKS; + static constexpr int32_t WW = 400; + static constexpr int32_t WH = 200; -static constexpr StringId WINDOW_TITLE = STR_ASSET_PACKS; -static constexpr int32_t WW = 400; -static constexpr int32_t WH = 200; - -// clang-format off + // clang-format off enum WindowAssetPacksWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -47,298 +47,301 @@ static Widget WindowAssetPacksWidgets[] = { MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_RELOAD), STR_RELOAD_ASSET_PACKS_TIP), kWidgetsEnd, }; -// clang-format on + // clang-format on -class AssetPacksWindow final : public Window -{ -private: - static constexpr int32_t ItemHeight = SCROLLABLE_ROW_HEIGHT + 1; - static constexpr int32_t ItemCheckBoxSize = ItemHeight - 3; - std::optional _highlightedIndex; - std::optional _selectedIndex; - -public: - void OnOpen() override + class AssetPacksWindow final : public Window { - widgets = WindowAssetPacksWidgets; - WindowInitScrollWidgets(*this); - } + private: + static constexpr int32_t ItemHeight = SCROLLABLE_ROW_HEIGHT + 1; + static constexpr int32_t ItemCheckBoxSize = ItemHeight - 3; + std::optional _highlightedIndex; + std::optional _selectedIndex; - void OnClose() override - { - Apply(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_MOVE_UP: - ReorderSelectedAssetPack(-1); - break; - case WIDX_MOVE_DOWN: - ReorderSelectedAssetPack(1); - break; - case WIDX_APPLY: - Apply(); - break; - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - ScreenSize result; - auto assetPackManager = GetContext()->GetAssetPackManager(); - if (assetPackManager != nullptr) - { - auto numAssetPacks = assetPackManager->GetCount() + 1; // +1 for the base assets item - result.height = static_cast(numAssetPacks * ItemHeight); + widgets = WindowAssetPacksWidgets; + WindowInitScrollWidgets(*this); } - if (_highlightedIndex) + void OnClose() override { - _highlightedIndex = {}; - Invalidate(); + Apply(); } - return result; - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto isCheckBox = false; - auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox); - - // Click on checkbox - if (index && isCheckBox) + void OnMouseUp(WidgetIndex widgetIndex) override { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_MOVE_UP: + ReorderSelectedAssetPack(-1); + break; + case WIDX_MOVE_DOWN: + ReorderSelectedAssetPack(1); + break; + case WIDX_APPLY: + Apply(); + break; + } + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + ScreenSize result; auto assetPackManager = GetContext()->GetAssetPackManager(); if (assetPackManager != nullptr) { - auto assetPack = assetPackManager->GetAssetPack(*index); - if (assetPack != nullptr) + auto numAssetPacks = assetPackManager->GetCount() + 1; // +1 for the base assets item + result.height = static_cast(numAssetPacks * ItemHeight); + } + + if (_highlightedIndex) + { + _highlightedIndex = {}; + Invalidate(); + } + + return result; + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + auto isCheckBox = false; + auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox); + + // Click on checkbox + if (index && isCheckBox) + { + auto assetPackManager = GetContext()->GetAssetPackManager(); + if (assetPackManager != nullptr) { - assetPack->SetEnabled(!assetPack->IsEnabled()); - Invalidate(); + auto assetPack = assetPackManager->GetAssetPack(*index); + if (assetPack != nullptr) + { + assetPack->SetEnabled(!assetPack->IsEnabled()); + Invalidate(); + } } } - } - // Select item - if (_selectedIndex != index) - { - _selectedIndex = index; - Invalidate(); - } - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto isCheckBox = false; - auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox); - if (_highlightedIndex != index) - { - _highlightedIndex = index; - Invalidate(); - } - } - - void OnPrepareDraw() override - { - ResizeFrame(); - - auto& list = widgets[WIDX_LIST]; - list.left = 6; - list.top = 20 + 11 + 3; - list.right = width - 2 - 24 - 1; - list.bottom = height - 6 - 11 - 3; - - widgets[WIDX_HIGH_LABEL].bottom = list.top - 1; - widgets[WIDX_HIGH_LABEL].top = widgets[WIDX_HIGH_LABEL].bottom - 11 - 3; - widgets[WIDX_HIGH_LABEL].left = list.left; - widgets[WIDX_HIGH_LABEL].right = list.right; - widgets[WIDX_LOW_LABEL].top = list.bottom + 1 + 3; - widgets[WIDX_LOW_LABEL].bottom = widgets[WIDX_LOW_LABEL].top + 11 + 3; - widgets[WIDX_LOW_LABEL].left = list.left; - widgets[WIDX_LOW_LABEL].right = list.right; - - auto toolstripY = 20; - auto toolstripRight = width - 2; - auto toolstripLeft = toolstripRight - 24; - for (WidgetIndex i = WIDX_MOVE_UP; i <= WIDX_APPLY; i++) - { - widgets[i].top = toolstripY; - widgets[i].bottom = toolstripY + 24; - widgets[i].left = toolstripLeft; - widgets[i].right = toolstripRight; - toolstripY += 24; - } - - SetWidgetDisabled(WIDX_MOVE_UP, !_selectedIndex || _selectedIndex == 0u); - SetWidgetDisabled(WIDX_MOVE_DOWN, !_selectedIndex || _selectedIndex >= GetNumAssetPacks() - 1); - - widgets[WIDX_APPLY].bottom = widgets[WIDX_LIST].bottom; - widgets[WIDX_APPLY].top = widgets[WIDX_APPLY].bottom - 24; - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; - GfxFillRect( - dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - - auto assetPackManager = GetContext()->GetAssetPackManager(); - if (assetPackManager == nullptr) - return; - - auto numAssetPacks = assetPackManager->GetCount(); - auto y = 0; - for (size_t i = 0; i <= numAssetPacks; i++) - { - if (y > dpi.y + dpi.height) - break; - if (y + 11 < dpi.y) - continue; - - auto isSelected = i == _selectedIndex; - auto isHighlighted = i == _highlightedIndex; - if (i == numAssetPacks) + // Select item + if (_selectedIndex != index) { - auto ft = Formatter(); - ft.Add(STR_BASE_GRAPHICS_MUSIC_SOUND); - PaintItem(dpi, y, ft, true, isSelected, isHighlighted); + _selectedIndex = index; + Invalidate(); } - else + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + auto isCheckBox = false; + auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox); + if (_highlightedIndex != index) { - auto assetPack = assetPackManager->GetAssetPack(i); - if (assetPack != nullptr) + _highlightedIndex = index; + Invalidate(); + } + } + + void OnPrepareDraw() override + { + ResizeFrame(); + + auto& list = widgets[WIDX_LIST]; + list.left = 6; + list.top = 20 + 11 + 3; + list.right = width - 2 - 24 - 1; + list.bottom = height - 6 - 11 - 3; + + widgets[WIDX_HIGH_LABEL].bottom = list.top - 1; + widgets[WIDX_HIGH_LABEL].top = widgets[WIDX_HIGH_LABEL].bottom - 11 - 3; + widgets[WIDX_HIGH_LABEL].left = list.left; + widgets[WIDX_HIGH_LABEL].right = list.right; + widgets[WIDX_LOW_LABEL].top = list.bottom + 1 + 3; + widgets[WIDX_LOW_LABEL].bottom = widgets[WIDX_LOW_LABEL].top + 11 + 3; + widgets[WIDX_LOW_LABEL].left = list.left; + widgets[WIDX_LOW_LABEL].right = list.right; + + auto toolstripY = 20; + auto toolstripRight = width - 2; + auto toolstripLeft = toolstripRight - 24; + for (WidgetIndex i = WIDX_MOVE_UP; i <= WIDX_APPLY; i++) + { + widgets[i].top = toolstripY; + widgets[i].bottom = toolstripY + 24; + widgets[i].left = toolstripLeft; + widgets[i].right = toolstripRight; + toolstripY += 24; + } + + SetWidgetDisabled(WIDX_MOVE_UP, !_selectedIndex || _selectedIndex == 0u); + SetWidgetDisabled(WIDX_MOVE_DOWN, !_selectedIndex || _selectedIndex >= GetNumAssetPacks() - 1); + + widgets[WIDX_APPLY].bottom = widgets[WIDX_LIST].bottom; + widgets[WIDX_APPLY].top = widgets[WIDX_APPLY].bottom - 24; + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; + GfxFillRect( + dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, + ColourMapA[colours[1]].mid_light); + + auto assetPackManager = GetContext()->GetAssetPackManager(); + if (assetPackManager == nullptr) + return; + + auto numAssetPacks = assetPackManager->GetCount(); + auto y = 0; + for (size_t i = 0; i <= numAssetPacks; i++) + { + if (y > dpi.y + dpi.height) + break; + if (y + 11 < dpi.y) + continue; + + auto isSelected = i == _selectedIndex; + auto isHighlighted = i == _highlightedIndex; + if (i == numAssetPacks) { - auto isChecked = assetPack->IsEnabled(); auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(assetPack->Name.c_str()); - PaintItem(dpi, y, ft, isChecked, isSelected, isHighlighted); + ft.Add(STR_BASE_GRAPHICS_MUSIC_SOUND); + PaintItem(dpi, y, ft, true, isSelected, isHighlighted); + } + else + { + auto assetPack = assetPackManager->GetAssetPack(i); + if (assetPack != nullptr) + { + auto isChecked = assetPack->IsEnabled(); + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(assetPack->Name.c_str()); + PaintItem(dpi, y, ft, isChecked, isSelected, isHighlighted); + } + } + y += ItemHeight; + } + } + + private: + void PaintItem(DrawPixelInfo& dpi, int32_t y, Formatter& ft, bool isChecked, bool isSelected, bool isHighlighted) + { + auto listWidth = dpi.width - 1; + auto stringId = STR_BLACK_STRING; + auto fillRectangle = ScreenRect{ { 0, y }, { listWidth, y + ItemHeight - 1 } }; + if (isSelected) + { + GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark); + stringId = STR_WINDOW_COLOUR_2_STRINGID; + } + else if (isHighlighted) + { + GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark); + } + + DrawTextEllipsised(dpi, { 16, y + 1 }, listWidth, stringId, ft); + + auto checkboxSize = ItemHeight - 3; + PaintCheckbox(dpi, { { 2, y + 1 }, { 2 + checkboxSize + 1, y + 1 + checkboxSize } }, isChecked); + } + + void PaintCheckbox(DrawPixelInfo& dpi, const ScreenRect& rect, bool checked) + { + GfxFillRectInset(dpi, rect, colours[1], INSET_RECT_F_E0); + if (checked) + { + auto checkmark = Formatter(); + checkmark.Add(STR_STRING); + checkmark.Add(CheckBoxMarkString); + DrawTextBasic( + dpi, ScreenCoordsXY{ rect.GetLeft() + 1, rect.GetTop() }, STR_WINDOW_COLOUR_2_STRINGID, checkmark); + } + } + + std::optional GetAssetPackIndexFromPosition(const ScreenCoordsXY& pos, bool& isCheckBox) + { + const auto index = pos.y / ItemHeight; + if (index < 0 || static_cast(index) >= GetNumAssetPacks()) + return std::nullopt; + + isCheckBox = pos.x >= 2 && pos.x <= 2 + ItemCheckBoxSize + 1; + return static_cast(index); + } + + size_t GetNumAssetPacks() const + { + auto assetPackManager = GetContext()->GetAssetPackManager(); + if (assetPackManager == nullptr) + return 0; + return assetPackManager->GetCount(); + } + + bool IsSelectedAssetPackEnabled() const + { + if (_selectedIndex) + { + auto assetPackManager = GetContext()->GetAssetPackManager(); + if (assetPackManager != nullptr) + { + auto assetPack = assetPackManager->GetAssetPack(*_selectedIndex); + if (assetPack != nullptr) + { + return assetPack->IsEnabled(); + } } } - y += ItemHeight; - } - } - -private: - void PaintItem(DrawPixelInfo& dpi, int32_t y, Formatter& ft, bool isChecked, bool isSelected, bool isHighlighted) - { - auto listWidth = dpi.width - 1; - auto stringId = STR_BLACK_STRING; - auto fillRectangle = ScreenRect{ { 0, y }, { listWidth, y + ItemHeight - 1 } }; - if (isSelected) - { - GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark); - stringId = STR_WINDOW_COLOUR_2_STRINGID; - } - else if (isHighlighted) - { - GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark); + return false; } - DrawTextEllipsised(dpi, { 16, y + 1 }, listWidth, stringId, ft); - - auto checkboxSize = ItemHeight - 3; - PaintCheckbox(dpi, { { 2, y + 1 }, { 2 + checkboxSize + 1, y + 1 + checkboxSize } }, isChecked); - } - - void PaintCheckbox(DrawPixelInfo& dpi, const ScreenRect& rect, bool checked) - { - GfxFillRectInset(dpi, rect, colours[1], INSET_RECT_F_E0); - if (checked) + void ReorderSelectedAssetPack(int32_t direction) { - auto checkmark = Formatter(); - checkmark.Add(STR_STRING); - checkmark.Add(CheckBoxMarkString); - DrawTextBasic(dpi, ScreenCoordsXY{ rect.GetLeft() + 1, rect.GetTop() }, STR_WINDOW_COLOUR_2_STRINGID, checkmark); + if (!_selectedIndex) + return; + + auto assetPackManager = GetContext()->GetAssetPackManager(); + if (assetPackManager == nullptr) + return; + + if (direction < 0 && *_selectedIndex > 0) + { + assetPackManager->Swap(*_selectedIndex, *_selectedIndex - 1); + (*_selectedIndex)--; + Invalidate(); + } + else if (direction > 0 && *_selectedIndex < assetPackManager->GetCount() - 1) + { + assetPackManager->Swap(*_selectedIndex, *_selectedIndex + 1); + (*_selectedIndex)++; + Invalidate(); + } } - } - std::optional GetAssetPackIndexFromPosition(const ScreenCoordsXY& pos, bool& isCheckBox) - { - const auto index = pos.y / ItemHeight; - if (index < 0 || static_cast(index) >= GetNumAssetPacks()) - return std::nullopt; - - isCheckBox = pos.x >= 2 && pos.x <= 2 + ItemCheckBoxSize + 1; - return static_cast(index); - } - - size_t GetNumAssetPacks() const - { - auto assetPackManager = GetContext()->GetAssetPackManager(); - if (assetPackManager == nullptr) - return 0; - return assetPackManager->GetCount(); - } - - bool IsSelectedAssetPackEnabled() const - { - if (_selectedIndex) + void Apply() { + auto& objectManager = GetContext()->GetObjectManager(); + objectManager.ResetObjects(); + auto assetPackManager = GetContext()->GetAssetPackManager(); if (assetPackManager != nullptr) { - auto assetPack = assetPackManager->GetAssetPack(*_selectedIndex); - if (assetPack != nullptr) - { - return assetPack->IsEnabled(); - } + assetPackManager->SaveEnabledAssetPacks(); } } - return false; - } + }; - void ReorderSelectedAssetPack(int32_t direction) + WindowBase* WindowAssetPacksOpen() { - if (!_selectedIndex) - return; - - auto assetPackManager = GetContext()->GetAssetPackManager(); - if (assetPackManager == nullptr) - return; - - if (direction < 0 && *_selectedIndex > 0) - { - assetPackManager->Swap(*_selectedIndex, *_selectedIndex - 1); - (*_selectedIndex)--; - Invalidate(); - } - else if (direction > 0 && *_selectedIndex < assetPackManager->GetCount() - 1) - { - assetPackManager->Swap(*_selectedIndex, *_selectedIndex + 1); - (*_selectedIndex)++; - Invalidate(); - } + auto flags = WF_AUTO_POSITION | WF_CENTRE_SCREEN; + return WindowFocusOrCreate(WindowClass::AssetPacks, WW, WH, flags); } - - void Apply() - { - auto& objectManager = GetContext()->GetObjectManager(); - objectManager.ResetObjects(); - - auto assetPackManager = GetContext()->GetAssetPackManager(); - if (assetPackManager != nullptr) - { - assetPackManager->SaveEnabledAssetPacks(); - } - } -}; - -WindowBase* WindowAssetPacksOpen() -{ - auto flags = WF_AUTO_POSITION | WF_CENTRE_SCREEN; - return WindowFocusOrCreate(WindowClass::AssetPacks, WW, WH, flags); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Banner.cpp b/src/openrct2-ui/windows/Banner.cpp index 7662621e06..948d39b76f 100644 --- a/src/openrct2-ui/windows/Banner.cpp +++ b/src/openrct2-ui/windows/Banner.cpp @@ -22,12 +22,13 @@ #include #include #include +namespace OpenRCT2::Ui::Windows +{ + static constexpr int32_t WW = 113; + static constexpr int32_t WH = 96; + static constexpr StringId WINDOW_TITLE = STR_BANNER_WINDOW_TITLE; -static constexpr int32_t WW = 113; -static constexpr int32_t WH = 96; -static constexpr StringId WINDOW_TITLE = STR_BANNER_WINDOW_TITLE; - -// clang-format off + // clang-format off enum WindowBannerWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -70,253 +71,255 @@ static Widget window_banner_widgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class BannerWindow final : public Window -{ -private: - CoordsXYZ _bannerViewPos; - - void CreateViewport() + class BannerWindow final : public Window { - Widget* viewportWidget = &window_banner_widgets[WIDX_VIEWPORT]; - ViewportCreate( - this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }, - (viewportWidget->width()) - 1, (viewportWidget->height()) - 1, Focus(_bannerViewPos)); + private: + CoordsXYZ _bannerViewPos; - if (viewport != nullptr) - viewport->flags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE; - Invalidate(); - } - - BannerIndex GetBannerIndex() const - { - return BannerIndex::FromUnderlying(number); - } - - BannerElement* GetBannerElement() - { - auto* banner = GetBanner(GetBannerIndex()); - if (banner == nullptr) + void CreateViewport() { - return nullptr; + Widget* viewportWidget = &window_banner_widgets[WIDX_VIEWPORT]; + ViewportCreate( + this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }, + (viewportWidget->width()) - 1, (viewportWidget->height()) - 1, Focus(_bannerViewPos)); + + if (viewport != nullptr) + viewport->flags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE; + Invalidate(); } - TileElement* tileElement = MapGetFirstElementAt(banner->position); - if (tileElement == nullptr) + BannerIndex GetBannerIndex() const { - return nullptr; + return BannerIndex::FromUnderlying(number); } - do + BannerElement* GetBannerElement() { - auto* bannerElement = tileElement->AsBanner(); - if (bannerElement == nullptr) + auto* banner = GetBanner(GetBannerIndex()); + if (banner == nullptr) { - continue; + return nullptr; } - if (bannerElement->GetIndex() == GetBannerIndex()) + + TileElement* tileElement = MapGetFirstElementAt(banner->position); + if (tileElement == nullptr) { - return bannerElement; + return nullptr; } - } while (!(tileElement++)->IsLastForTile()); - return nullptr; - } - -public: - void OnOpen() override - { - widgets = window_banner_widgets; - WindowInitScrollWidgets(*this); - } - - void Initialise(rct_windownumber _number) - { - number = _number; - auto* banner = GetBanner(BannerIndex::FromUnderlying(number)); - - auto* bannerElement = GetBannerElement(); - if (bannerElement == nullptr) - return; - - _bannerViewPos = CoordsXYZ{ banner->position.ToCoordsXY().ToTileCentre(), bannerElement->GetBaseZ() }; - CreateViewport(); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - Widget* widget = &widgets[widgetIndex]; - auto* banner = GetBanner(GetBannerIndex()); - if (banner == nullptr) - { - Close(); - return; - } - switch (widgetIndex) - { - case WIDX_MAIN_COLOUR: - WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), banner->colour); - break; - case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON: - - for (int32_t i = 0; i < 13; ++i) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = BannerColouredTextFormats[i + 1]; - } - - // Switch to the dropdown box widget. - widget--; - - WindowDropdownShowTextCustomWidth( - { widget->left + windowPos.x, widget->top + windowPos.y }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, 13, widget->width() - 3); - - Dropdown::SetChecked(banner->text_colour - 1, true); - break; - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - auto* banner = GetBanner(GetBannerIndex()); - if (banner == nullptr) - { - Close(); - return; - } - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_BANNER_DEMOLISH: + do { - auto* bannerElement = GetBannerElement(); + auto* bannerElement = tileElement->AsBanner(); if (bannerElement == nullptr) - break; + { + continue; + } + if (bannerElement->GetIndex() == GetBannerIndex()) + { + return bannerElement; + } + } while (!(tileElement++)->IsLastForTile()); - auto bannerRemoveAction = BannerRemoveAction( - { banner->position.ToCoordsXY(), bannerElement->GetBaseZ(), bannerElement->GetPosition() }); - GameActions::Execute(&bannerRemoveAction); - break; - } - case WIDX_BANNER_TEXT: - WindowTextInputRawOpen( - this, WIDX_BANNER_TEXT, STR_BANNER_TEXT, STR_ENTER_BANNER_TEXT, {}, banner->GetText().c_str(), 32); - break; - case WIDX_BANNER_NO_ENTRY: + return nullptr; + } + + public: + void OnOpen() override + { + widgets = window_banner_widgets; + WindowInitScrollWidgets(*this); + } + + void Initialise(rct_windownumber _number) + { + number = _number; + auto* banner = GetBanner(BannerIndex::FromUnderlying(number)); + + auto* bannerElement = GetBannerElement(); + if (bannerElement == nullptr) + return; + + _bannerViewPos = CoordsXYZ{ banner->position.ToCoordsXY().ToTileCentre(), bannerElement->GetBaseZ() }; + CreateViewport(); + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + Widget* widget = &widgets[widgetIndex]; + auto* banner = GetBanner(GetBannerIndex()); + if (banner == nullptr) { - TextinputCancel(); - auto bannerSetStyle = BannerSetStyleAction( - BannerSetStyleType::NoEntry, GetBannerIndex(), banner->flags ^ BANNER_FLAG_NO_ENTRY); - GameActions::Execute(&bannerSetStyle); - break; + Close(); + return; } - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - switch (widgetIndex) - { - case WIDX_MAIN_COLOUR: + switch (widgetIndex) { - if (dropdownIndex == -1) + case WIDX_MAIN_COLOUR: + WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), banner->colour); break; + case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON: - auto bannerSetStyle = BannerSetStyleAction( - BannerSetStyleType::PrimaryColour, GetBannerIndex(), ColourDropDownIndexToColour(dropdownIndex)); - GameActions::Execute(&bannerSetStyle); - break; + for (int32_t i = 0; i < 13; ++i) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = BannerColouredTextFormats[i + 1]; + } + + // Switch to the dropdown box widget. + widget--; + + WindowDropdownShowTextCustomWidth( + { widget->left + windowPos.x, widget->top + windowPos.y }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, 13, widget->width() - 3); + + Dropdown::SetChecked(banner->text_colour - 1, true); + break; } - case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON: + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + auto* banner = GetBanner(GetBannerIndex()); + if (banner == nullptr) { - if (dropdownIndex == -1) + Close(); + return; + } + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); break; - auto bannerSetStyle = BannerSetStyleAction(BannerSetStyleType::TextColour, GetBannerIndex(), dropdownIndex + 1); - GameActions::Execute(&bannerSetStyle); - break; + case WIDX_BANNER_DEMOLISH: + { + auto* bannerElement = GetBannerElement(); + if (bannerElement == nullptr) + break; + + auto bannerRemoveAction = BannerRemoveAction( + { banner->position.ToCoordsXY(), bannerElement->GetBaseZ(), bannerElement->GetPosition() }); + GameActions::Execute(&bannerRemoveAction); + break; + } + case WIDX_BANNER_TEXT: + WindowTextInputRawOpen( + this, WIDX_BANNER_TEXT, STR_BANNER_TEXT, STR_ENTER_BANNER_TEXT, {}, banner->GetText().c_str(), 32); + break; + case WIDX_BANNER_NO_ENTRY: + { + TextinputCancel(); + auto bannerSetStyle = BannerSetStyleAction( + BannerSetStyleType::NoEntry, GetBannerIndex(), banner->flags ^ BANNER_FLAG_NO_ENTRY); + GameActions::Execute(&bannerSetStyle); + break; + } } } - } - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex == WIDX_BANNER_TEXT) + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override { - auto bannerSetNameAction = BannerSetNameAction(GetBannerIndex(), std::string(text)); - GameActions::Execute(&bannerSetNameAction); + switch (widgetIndex) + { + case WIDX_MAIN_COLOUR: + { + if (dropdownIndex == -1) + break; + + auto bannerSetStyle = BannerSetStyleAction( + BannerSetStyleType::PrimaryColour, GetBannerIndex(), ColourDropDownIndexToColour(dropdownIndex)); + GameActions::Execute(&bannerSetStyle); + break; + } + case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON: + { + if (dropdownIndex == -1) + break; + auto bannerSetStyle = BannerSetStyleAction( + BannerSetStyleType::TextColour, GetBannerIndex(), dropdownIndex + 1); + GameActions::Execute(&bannerSetStyle); + break; + } + } } - } - void OnViewportRotate() override - { - RemoveViewport(); - CreateViewport(); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - if (viewport != nullptr) + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { - WindowDrawViewport(dpi, *this); + if (widgetIndex == WIDX_BANNER_TEXT) + { + auto bannerSetNameAction = BannerSetNameAction(GetBannerIndex(), std::string(text)); + GameActions::Execute(&bannerSetNameAction); + } } - } - void OnPrepareDraw() override + void OnViewportRotate() override + { + RemoveViewport(); + CreateViewport(); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + if (viewport != nullptr) + { + WindowDrawViewport(dpi, *this); + } + } + + void OnPrepareDraw() override + { + auto* banner = GetBanner(GetBannerIndex()); + if (banner == nullptr) + { + return; + } + Widget* colourBtn = &window_banner_widgets[WIDX_MAIN_COLOUR]; + colourBtn->type = WindowWidgetType::Empty; + + auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry(banner->type); + if (bannerEntry != nullptr && (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR)) + { + colourBtn->type = WindowWidgetType::ColourBtn; + } + pressed_widgets &= ~(1uLL << WIDX_BANNER_NO_ENTRY); + disabled_widgets &= ~( + (1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON)); + if (banner->flags & BANNER_FLAG_NO_ENTRY) + { + pressed_widgets |= (1uLL << WIDX_BANNER_NO_ENTRY); + disabled_widgets |= (1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN) + | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON); + } + colourBtn->image = GetColourButtonImage(banner->colour); + Widget* dropDownWidget = &window_banner_widgets[WIDX_TEXT_COLOUR_DROPDOWN]; + dropDownWidget->text = BannerColouredTextFormats[banner->text_colour]; + } + + void OnResize() override + { + ResizeFrame(); + } + }; + + /** + * + * rct2: 0x006BA305 + */ + WindowBase* WindowBannerOpen(rct_windownumber number) { - auto* banner = GetBanner(GetBannerIndex()); - if (banner == nullptr) - { - return; - } - Widget* colourBtn = &window_banner_widgets[WIDX_MAIN_COLOUR]; - colourBtn->type = WindowWidgetType::Empty; + auto w = static_cast(WindowBringToFrontByNumber(WindowClass::Banner, number)); - auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry(banner->type); - if (bannerEntry != nullptr && (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR)) - { - colourBtn->type = WindowWidgetType::ColourBtn; - } - pressed_widgets &= ~(1uLL << WIDX_BANNER_NO_ENTRY); - disabled_widgets &= ~( - (1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON)); - if (banner->flags & BANNER_FLAG_NO_ENTRY) - { - pressed_widgets |= (1uLL << WIDX_BANNER_NO_ENTRY); - disabled_widgets |= (1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN) - | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON); - } - colourBtn->image = GetColourButtonImage(banner->colour); - Widget* dropDownWidget = &window_banner_widgets[WIDX_TEXT_COLOUR_DROPDOWN]; - dropDownWidget->text = BannerColouredTextFormats[banner->text_colour]; - } + if (w != nullptr) + return w; - void OnResize() override - { - ResizeFrame(); - } -}; + w = WindowCreate(WindowClass::Banner, WW, WH, 0); -/** - * - * rct2: 0x006BA305 - */ -WindowBase* WindowBannerOpen(rct_windownumber number) -{ - auto w = static_cast(WindowBringToFrontByNumber(WindowClass::Banner, number)); + if (w != nullptr) + w->Initialise(number); - if (w != nullptr) return w; - - w = WindowCreate(WindowClass::Banner, WW, WH, 0); - - if (w != nullptr) - w->Initialise(number); - - return w; -} + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Changelog.cpp b/src/openrct2-ui/windows/Changelog.cpp index 5f51a4c71b..79dbd31113 100644 --- a/src/openrct2-ui/windows/Changelog.cpp +++ b/src/openrct2-ui/windows/Changelog.cpp @@ -25,9 +25,9 @@ #include #include -using namespace OpenRCT2; - -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -51,289 +51,290 @@ static Widget _windowChangelogWidgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class ChangelogWindow final : public Window -{ - const NewVersionInfo* _newVersionInfo; - std::vector _changelogLines; - int32_t _changelogLongestLineWidth = 0; - int _personality = 0; - -public: - /** - * @brief Retrieves the changelog contents. - */ - const std::string GetText(PATHID pathId) + class ChangelogWindow final : public Window { - auto env = GetContext()->GetPlatformEnvironment(); - auto path = env->GetFilePath(pathId); - auto fs = std::ifstream(fs::u8path(path), std::ios::in); - if (!fs.is_open()) + const NewVersionInfo* _newVersionInfo; + std::vector _changelogLines; + int32_t _changelogLongestLineWidth = 0; + int _personality = 0; + + public: + /** + * @brief Retrieves the changelog contents. + */ + const std::string GetText(PATHID pathId) { - throw std::runtime_error("Unable to open " + path); + auto env = GetContext()->GetPlatformEnvironment(); + auto path = env->GetFilePath(pathId); + auto fs = std::ifstream(fs::u8path(path), std::ios::in); + if (!fs.is_open()) + { + throw std::runtime_error("Unable to open " + path); + } + fs.seekg(0, fs.end); + auto length = fs.tellg(); + fs.seekg(0, fs.beg); + std::unique_ptr buffer = std::make_unique(length); + fs.read(buffer.get(), length); + auto result = std::string(buffer.get(), buffer.get() + length); + return result; } - fs.seekg(0, fs.end); - auto length = fs.tellg(); - fs.seekg(0, fs.beg); - std::unique_ptr buffer = std::make_unique(length); - fs.read(buffer.get(), length); - auto result = std::string(buffer.get(), buffer.get() + length); - return result; - } - /** - * @brief Set the Changelog Window's Personality, should be called just after creation. Returns true on success - * - * @param personality - */ - bool SetPersonality(int personality) - { - switch (personality) + /** + * @brief Set the Changelog Window's Personality, should be called just after creation. Returns true on success + * + * @param personality + */ + bool SetPersonality(int personality) { - case WV_NEW_VERSION_INFO: - if (!GetContext()->HasNewVersionInfo()) - { - return false; - } - _personality = WV_NEW_VERSION_INFO; - NewVersionProcessInfo(); - widgets[WIDX_OPEN_URL].type = WindowWidgetType::Button; - return true; + switch (personality) + { + case WV_NEW_VERSION_INFO: + if (!GetContext()->HasNewVersionInfo()) + { + return false; + } + _personality = WV_NEW_VERSION_INFO; + NewVersionProcessInfo(); + widgets[WIDX_OPEN_URL].type = WindowWidgetType::Button; + return true; - case WV_CHANGELOG: - if (!ReadFile(PATHID::CHANGELOG)) - { - return false; - } - _personality = WV_CHANGELOG; - widgets[WIDX_TITLE].text = STR_CHANGELOG_TITLE; - return true; + case WV_CHANGELOG: + if (!ReadFile(PATHID::CHANGELOG)) + { + return false; + } + _personality = WV_CHANGELOG; + widgets[WIDX_TITLE].text = STR_CHANGELOG_TITLE; + return true; - case WV_CONTRIBUTORS: - if (!ReadFile(PATHID::CONTRIBUTORS)) - { - return false; - } - _personality = WV_CONTRIBUTORS; - widgets[WIDX_TITLE].text = STR_CONTRIBUTORS_WINDOW; - return true; + case WV_CONTRIBUTORS: + if (!ReadFile(PATHID::CONTRIBUTORS)) + { + return false; + } + _personality = WV_CONTRIBUTORS; + widgets[WIDX_TITLE].text = STR_CONTRIBUTORS_WINDOW; + return true; - default: - LOG_ERROR("Invalid personality for changelog window: %d", personality); + default: + LOG_ERROR("Invalid personality for changelog window: %d", personality); + return false; + } + } + + void OnOpen() override + { + widgets = _windowChangelogWidgets; + + WindowInitScrollWidgets(*this); + min_width = MIN_WW; + min_height = MIN_WH; + max_width = MIN_WW; + max_height = MIN_WH; + } + + void OnResize() override + { + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + + max_width = (screenWidth * 4) / 5; + max_height = (screenHeight * 4) / 5; + + min_width = MIN_WW; + min_height = MIN_WH; + + auto download_button_width = widgets[WIDX_OPEN_URL].width(); + widgets[WIDX_OPEN_URL].left = (width - download_button_width) / 2; + widgets[WIDX_OPEN_URL].right = widgets[WIDX_OPEN_URL].left + download_button_width; + + if (width < min_width) + { + Invalidate(); + width = min_width; + } + if (height < min_height) + { + Invalidate(); + height = min_height; + } + } + + void OnPrepareDraw() override + { + ResizeFrameWithPage(); + widgets[WIDX_SCROLL].right = width - 3; + widgets[WIDX_SCROLL].bottom = height - 22; + widgets[WIDX_OPEN_URL].bottom = height - 5; + widgets[WIDX_OPEN_URL].top = height - 19; + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_OPEN_URL: + if (_newVersionInfo != nullptr) + { + GetContext()->GetUiContext()->OpenURL(_newVersionInfo->url); + } + else + { + LOG_ERROR("Cannot open URL: NewVersionInfo for ChangelogWindow is undefined!"); + } + break; + } + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + const int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + + ScreenCoordsXY screenCoords(3, 3 - lineHeight); + for (const auto& line : _changelogLines) + { + screenCoords.y += lineHeight; + if (screenCoords.y + lineHeight < dpi.y || screenCoords.y >= dpi.y + dpi.height) + continue; + + GfxDrawString(dpi, screenCoords, line.c_str(), { colours[0] }); + } + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + return ScreenSize( + _changelogLongestLineWidth + 4, + static_cast(_changelogLines.size()) * FontGetLineHeight(FontStyle::Medium)); + } + + // TODO: This probably should be a utility function defined elsewhere for reusability + /** + * @brief Reimplementation of Window's GetCentrePositionForNewWindow for ChangelogWindow. + * + * @return ScreenCoordsXY + */ + static ScreenCoordsXY GetCentrePositionForNewWindow(int32_t width, int32_t height) + { + auto uiContext = GetContext()->GetUiContext(); + auto screenWidth = uiContext->GetWidth(); + auto screenHeight = uiContext->GetHeight(); + return ScreenCoordsXY{ (screenWidth - width) / 2, std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - height) / 2) }; + } + + private: + /** + * @brief Converts NewVersionInfo into changelog lines + * + */ + void NewVersionProcessInfo() + { + _newVersionInfo = GetContext()->GetNewVersionInfo(); + if (_newVersionInfo != nullptr) + { + char version_info[256]; + + const char* version_info_ptr = _newVersionInfo->name.c_str(); + FormatStringLegacy(version_info, 256, STR_NEW_RELEASE_VERSION_INFO, &version_info_ptr); + + _changelogLines.emplace_back(version_info); + _changelogLines.emplace_back(""); + + ProcessText(_newVersionInfo->changelog); + } + else + { + LOG_ERROR("ChangelogWindow: Could not process NewVersionInfo, result was undefined"); + } + } + + /** + * @brief Get the absolute path for the changelog file + * + * @return std::string + */ + std::string GetChangelogPath() + { + auto env = GetContext()->GetPlatformEnvironment(); + return env->GetFilePath(PATHID::CHANGELOG); + } + + /** + * @brief Attempts to read the changelog file, returns true on success + * + */ + bool ReadFile(PATHID pathId) + { + std::string _text; + try + { + _text = GetText(pathId); + } + catch (const std::bad_alloc&) + { + LOG_ERROR("Unable to allocate memory for text file"); return false; + } + catch (const std::exception&) + { + LOG_ERROR("Unable to read text file"); + return false; + } + + ProcessText(_text); + return true; } - } - void OnOpen() override - { - widgets = _windowChangelogWidgets; - - WindowInitScrollWidgets(*this); - min_width = MIN_WW; - min_height = MIN_WH; - max_width = MIN_WW; - max_height = MIN_WH; - } - - void OnResize() override - { - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - - max_width = (screenWidth * 4) / 5; - max_height = (screenHeight * 4) / 5; - - min_width = MIN_WW; - min_height = MIN_WH; - - auto download_button_width = widgets[WIDX_OPEN_URL].width(); - widgets[WIDX_OPEN_URL].left = (width - download_button_width) / 2; - widgets[WIDX_OPEN_URL].right = widgets[WIDX_OPEN_URL].left + download_button_width; - - if (width < min_width) + /** + * @brief Ingests a string of text and splits it into lines for the changelog and updates the longest line width for + * scrolling purposes + * + * @param text + */ + void ProcessText(const std::string& text) { - Invalidate(); - width = min_width; - } - if (height < min_height) - { - Invalidate(); - height = min_height; - } - } + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = text.find('\n', prev)) != std::string::npos) + { + _changelogLines.push_back(text.substr(prev, pos - prev)); + prev = pos + 1; + } - void OnPrepareDraw() override + // To get the last substring (or only, if delimiter is not found) + _changelogLines.push_back(text.substr(prev)); + + _changelogLongestLineWidth = 0; + for (const auto& line : _changelogLines) + { + int32_t linewidth = GfxGetStringWidth(line.c_str(), FontStyle::Medium); + _changelogLongestLineWidth = std::max(linewidth, _changelogLongestLineWidth); + } + } + }; + + WindowBase* WindowChangelogOpen(int personality) { - ResizeFrameWithPage(); - widgets[WIDX_SCROLL].right = width - 3; - widgets[WIDX_SCROLL].bottom = height - 22; - widgets[WIDX_OPEN_URL].bottom = height - 5; - widgets[WIDX_OPEN_URL].top = height - 19; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + auto* window = WindowBringToFrontByClass(WindowClass::Changelog); + if (window == nullptr) { - case WIDX_CLOSE: - Close(); - break; - case WIDX_OPEN_URL: - if (_newVersionInfo != nullptr) - { - GetContext()->GetUiContext()->OpenURL(_newVersionInfo->url); - } - else - { - LOG_ERROR("Cannot open URL: NewVersionInfo for ChangelogWindow is undefined!"); - } - break; + // Create a new centred window + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + int32_t width = (screenWidth * 4) / 5; + int32_t height = (screenHeight * 4) / 5; + + auto pos = ChangelogWindow::GetCentrePositionForNewWindow(width, height); + auto* newWindow = WindowCreate(WindowClass::Changelog, pos, width, height, WF_RESIZABLE); + newWindow->SetPersonality(personality); + return newWindow; } + return window; } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - const int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - - ScreenCoordsXY screenCoords(3, 3 - lineHeight); - for (const auto& line : _changelogLines) - { - screenCoords.y += lineHeight; - if (screenCoords.y + lineHeight < dpi.y || screenCoords.y >= dpi.y + dpi.height) - continue; - - GfxDrawString(dpi, screenCoords, line.c_str(), { colours[0] }); - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - return ScreenSize( - _changelogLongestLineWidth + 4, - static_cast(_changelogLines.size()) * FontGetLineHeight(FontStyle::Medium)); - } - - // TODO: This probably should be a utility function defined elsewhere for reusability - /** - * @brief Reimplementation of Window's GetCentrePositionForNewWindow for ChangelogWindow. - * - * @return ScreenCoordsXY - */ - static ScreenCoordsXY GetCentrePositionForNewWindow(int32_t width, int32_t height) - { - auto uiContext = GetContext()->GetUiContext(); - auto screenWidth = uiContext->GetWidth(); - auto screenHeight = uiContext->GetHeight(); - return ScreenCoordsXY{ (screenWidth - width) / 2, std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - height) / 2) }; - } - -private: - /** - * @brief Converts NewVersionInfo into changelog lines - * - */ - void NewVersionProcessInfo() - { - _newVersionInfo = GetContext()->GetNewVersionInfo(); - if (_newVersionInfo != nullptr) - { - char version_info[256]; - - const char* version_info_ptr = _newVersionInfo->name.c_str(); - FormatStringLegacy(version_info, 256, STR_NEW_RELEASE_VERSION_INFO, &version_info_ptr); - - _changelogLines.emplace_back(version_info); - _changelogLines.emplace_back(""); - - ProcessText(_newVersionInfo->changelog); - } - else - { - LOG_ERROR("ChangelogWindow: Could not process NewVersionInfo, result was undefined"); - } - } - - /** - * @brief Get the absolute path for the changelog file - * - * @return std::string - */ - std::string GetChangelogPath() - { - auto env = GetContext()->GetPlatformEnvironment(); - return env->GetFilePath(PATHID::CHANGELOG); - } - - /** - * @brief Attempts to read the changelog file, returns true on success - * - */ - bool ReadFile(PATHID pathId) - { - std::string _text; - try - { - _text = GetText(pathId); - } - catch (const std::bad_alloc&) - { - LOG_ERROR("Unable to allocate memory for text file"); - return false; - } - catch (const std::exception&) - { - LOG_ERROR("Unable to read text file"); - return false; - } - - ProcessText(_text); - return true; - } - - /** - * @brief Ingests a string of text and splits it into lines for the changelog and updates the longest line width for - * scrolling purposes - * - * @param text - */ - void ProcessText(const std::string& text) - { - std::string::size_type pos = 0; - std::string::size_type prev = 0; - while ((pos = text.find('\n', prev)) != std::string::npos) - { - _changelogLines.push_back(text.substr(prev, pos - prev)); - prev = pos + 1; - } - - // To get the last substring (or only, if delimiter is not found) - _changelogLines.push_back(text.substr(prev)); - - _changelogLongestLineWidth = 0; - for (const auto& line : _changelogLines) - { - int32_t linewidth = GfxGetStringWidth(line.c_str(), FontStyle::Medium); - _changelogLongestLineWidth = std::max(linewidth, _changelogLongestLineWidth); - } - } -}; - -WindowBase* WindowChangelogOpen(int personality) -{ - auto* window = WindowBringToFrontByClass(WindowClass::Changelog); - if (window == nullptr) - { - // Create a new centred window - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - int32_t width = (screenWidth * 4) / 5; - int32_t height = (screenHeight * 4) / 5; - - auto pos = ChangelogWindow::GetCentrePositionForNewWindow(width, height); - auto* newWindow = WindowCreate(WindowClass::Changelog, pos, width, height, WF_RESIZABLE); - newWindow->SetPersonality(personality); - return newWindow; - } - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Cheats.cpp b/src/openrct2-ui/windows/Cheats.cpp index 4d05f99762..80d4c536bf 100644 --- a/src/openrct2-ui/windows/Cheats.cpp +++ b/src/openrct2-ui/windows/Cheats.cpp @@ -28,13 +28,13 @@ #include #include -using namespace OpenRCT2; using OpenRCT2::Date; +namespace OpenRCT2::Ui::Windows +{ + constexpr auto CHEATS_MONEY_DEFAULT = 10000.00_GBP; + constexpr auto CHEATS_MONEY_INCREMENT_DIV = 5000.00_GBP; -constexpr auto CHEATS_MONEY_DEFAULT = 10000.00_GBP; -constexpr auto CHEATS_MONEY_INCREMENT_DIV = 5000.00_GBP; - -// clang-format off + // clang-format off enum { WINDOW_CHEATS_PAGE_MONEY, @@ -355,797 +355,807 @@ static StringId window_cheats_page_titles[] = { STR_CHEAT_TITLE_PARK, STR_CHEAT_TITLE_RIDE, }; -// clang-format on + // clang-format on -class CheatsWindow final : public Window -{ -private: - char _moneySpinnerText[MONEY_STRING_MAXLENGTH]{}; - money64 _moneySpinnerValue = CHEATS_MONEY_DEFAULT; - int32_t _parkRatingSpinnerValue{}; - int32_t _yearSpinnerValue = 1; - int32_t _monthSpinnerValue = 1; - int32_t _daySpinnerValue = 1; - -public: - void OnOpen() override + class CheatsWindow final : public Window { - SetPage(WINDOW_CHEATS_PAGE_MONEY); - _parkRatingSpinnerValue = ParkGetForcedRating() >= 0 ? ParkGetForcedRating() : 999; - } + private: + char _moneySpinnerText[MONEY_STRING_MAXLENGTH]{}; + money64 _moneySpinnerValue = CHEATS_MONEY_DEFAULT; + int32_t _parkRatingSpinnerValue{}; + int32_t _yearSpinnerValue = 1; + int32_t _monthSpinnerValue = 1; + int32_t _daySpinnerValue = 1; - void OnUpdate() override - { - frame_no++; - InvalidateWidget(WIDX_TAB_1 + page); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (page) + public: + void OnOpen() override { - case WINDOW_CHEATS_PAGE_MONEY: - OnMouseDownMoney(widgetIndex); - break; - case WINDOW_CHEATS_PAGE_MISC: - OnMouseDownMisc(widgetIndex); - break; + SetPage(WINDOW_CHEATS_PAGE_MONEY); + _parkRatingSpinnerValue = ParkGetForcedRating() >= 0 ? ParkGetForcedRating() : 999; } - } - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnUpdate() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - SetPage(widgetIndex - WIDX_TAB_1); - break; - default: - switch (page) + frame_no++; + InvalidateWidget(WIDX_TAB_1 + page); + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (page) + { + case WINDOW_CHEATS_PAGE_MONEY: + OnMouseDownMoney(widgetIndex); + break; + case WINDOW_CHEATS_PAGE_MISC: + OnMouseDownMisc(widgetIndex); + break; + } + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + SetPage(widgetIndex - WIDX_TAB_1); + break; + default: + switch (page) + { + case WINDOW_CHEATS_PAGE_MONEY: + OnMouseUpMoney(widgetIndex); + break; + case WINDOW_CHEATS_PAGE_GUESTS: + OnMouseUpGuests(widgetIndex); + break; + case WINDOW_CHEATS_PAGE_MISC: + OnMouseUpMisc(widgetIndex); + break; + case WINDOW_CHEATS_PAGE_RIDES: + OnMouseUpRides(widgetIndex); + break; + } + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + if (page == WINDOW_CHEATS_PAGE_MISC) + { + OnDropdownMisc(widgetIndex, selectedIndex); + } + } + + void OnPrepareDraw() override + { + auto* targetWidgets = window_cheats_page_widgets[page]; + if (widgets != targetWidgets) + { + widgets = targetWidgets; + WindowInitScrollWidgets(*this); + } + + pressed_widgets = 0; + disabled_widgets = 0; + + // Set correct active tab + for (auto i = 0; i < WINDOW_CHEATS_PAGE_COUNT; i++) + SetWidgetPressed(WIDX_TAB_1 + i, false); + SetWidgetPressed(WIDX_TAB_1 + page, true); + + // Set title + widgets[WIDX_TITLE].text = window_cheats_page_titles[page]; + + auto& gameState = GetGameState(); + switch (page) + { + case WINDOW_CHEATS_PAGE_MONEY: { - case WINDOW_CHEATS_PAGE_MONEY: - OnMouseUpMoney(widgetIndex); - break; - case WINDOW_CHEATS_PAGE_GUESTS: - OnMouseUpGuests(widgetIndex); - break; - case WINDOW_CHEATS_PAGE_MISC: - OnMouseUpMisc(widgetIndex); - break; - case WINDOW_CHEATS_PAGE_RIDES: - OnMouseUpRides(widgetIndex); - break; + auto moneyDisabled = (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) != 0; + SetCheckboxValue(WIDX_NO_MONEY, moneyDisabled); + SetWidgetDisabled(WIDX_ADD_SET_MONEY_GROUP, moneyDisabled); + SetWidgetDisabled(WIDX_MONEY_SPINNER, moneyDisabled); + SetWidgetDisabled(WIDX_MONEY_SPINNER_INCREMENT, moneyDisabled); + SetWidgetDisabled(WIDX_MONEY_SPINNER_DECREMENT, moneyDisabled); + SetWidgetDisabled(WIDX_ADD_MONEY, moneyDisabled); + SetWidgetDisabled(WIDX_SET_MONEY, moneyDisabled); + SetWidgetDisabled(WIDX_CLEAR_LOAN, moneyDisabled); + break; } - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (page == WINDOW_CHEATS_PAGE_MISC) - { - OnDropdownMisc(widgetIndex, selectedIndex); - } - } - - void OnPrepareDraw() override - { - auto* targetWidgets = window_cheats_page_widgets[page]; - if (widgets != targetWidgets) - { - widgets = targetWidgets; - WindowInitScrollWidgets(*this); - } - - pressed_widgets = 0; - disabled_widgets = 0; - - // Set correct active tab - for (auto i = 0; i < WINDOW_CHEATS_PAGE_COUNT; i++) - SetWidgetPressed(WIDX_TAB_1 + i, false); - SetWidgetPressed(WIDX_TAB_1 + page, true); - - // Set title - widgets[WIDX_TITLE].text = window_cheats_page_titles[page]; - - auto& gameState = GetGameState(); - switch (page) - { - case WINDOW_CHEATS_PAGE_MONEY: - { - auto moneyDisabled = (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) != 0; - SetCheckboxValue(WIDX_NO_MONEY, moneyDisabled); - SetWidgetDisabled(WIDX_ADD_SET_MONEY_GROUP, moneyDisabled); - SetWidgetDisabled(WIDX_MONEY_SPINNER, moneyDisabled); - SetWidgetDisabled(WIDX_MONEY_SPINNER_INCREMENT, moneyDisabled); - SetWidgetDisabled(WIDX_MONEY_SPINNER_DECREMENT, moneyDisabled); - SetWidgetDisabled(WIDX_ADD_MONEY, moneyDisabled); - SetWidgetDisabled(WIDX_SET_MONEY, moneyDisabled); - SetWidgetDisabled(WIDX_CLEAR_LOAN, moneyDisabled); - break; - } - case WINDOW_CHEATS_PAGE_GUESTS: - { - auto ft = Formatter::Common(); - ft.Add(1000.00_GBP); - SetCheckboxValue(WIDX_GUEST_IGNORE_RIDE_INTENSITY, gameState.Cheats.IgnoreRideIntensity); - SetCheckboxValue(WIDX_DISABLE_VANDALISM, gameState.Cheats.DisableVandalism); - SetCheckboxValue(WIDX_DISABLE_LITTERING, gameState.Cheats.DisableLittering); - break; - } - case WINDOW_CHEATS_PAGE_MISC: - widgets[WIDX_OPEN_CLOSE_PARK].text = (gameState.ParkFlags & PARK_FLAGS_PARK_OPEN) ? STR_CHEAT_CLOSE_PARK - : STR_CHEAT_OPEN_PARK; - SetCheckboxValue(WIDX_FORCE_PARK_RATING, ParkGetForcedRating() >= 0); - SetCheckboxValue(WIDX_FREEZE_WEATHER, gameState.Cheats.FreezeWeather); - SetCheckboxValue(WIDX_NEVERENDING_MARKETING, gameState.Cheats.NeverendingMarketing); - SetCheckboxValue(WIDX_DISABLE_PLANT_AGING, gameState.Cheats.DisablePlantAging); - SetCheckboxValue(WIDX_ALLOW_REGULAR_PATH_AS_QUEUE, gameState.Cheats.AllowRegularPathAsQueue); - SetCheckboxValue(WIDX_ALLOW_SPECIAL_COLOUR_SCHEMES, gameState.Cheats.AllowSpecialColourSchemes); - break; - case WINDOW_CHEATS_PAGE_RIDES: - SetCheckboxValue(WIDX_UNLOCK_OPERATING_LIMITS, gameState.Cheats.UnlockOperatingLimits); - SetCheckboxValue(WIDX_DISABLE_BRAKES_FAILURE, gameState.Cheats.DisableBrakesFailure); - SetCheckboxValue(WIDX_DISABLE_ALL_BREAKDOWNS, gameState.Cheats.DisableAllBreakdowns); - SetCheckboxValue(WIDX_BUILD_IN_PAUSE_MODE, gameState.Cheats.BuildInPauseMode); - SetCheckboxValue(WIDX_SHOW_ALL_OPERATING_MODES, gameState.Cheats.ShowAllOperatingModes); - SetCheckboxValue(WIDX_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES, gameState.Cheats.ShowVehiclesFromOtherTrackTypes); - SetCheckboxValue(WIDX_DISABLE_TRAIN_LENGTH_LIMITS, gameState.Cheats.DisableTrainLengthLimit); - SetCheckboxValue(WIDX_ENABLE_CHAIN_LIFT_ON_ALL_TRACK, gameState.Cheats.EnableChainLiftOnAllTrack); - SetCheckboxValue(WIDX_ENABLE_ARBITRARY_RIDE_TYPE_CHANGES, gameState.Cheats.AllowArbitraryRideTypeChanges); - SetCheckboxValue(WIDX_DISABLE_RIDE_VALUE_AGING, gameState.Cheats.DisableRideValueAging); - SetCheckboxValue(WIDX_IGNORE_RESEARCH_STATUS, gameState.Cheats.IgnoreResearchStatus); - SetCheckboxValue(WIDX_ENABLE_ALL_DRAWABLE_TRACK_PIECES, gameState.Cheats.EnableAllDrawableTrackPieces); - SetCheckboxValue(WIDX_ALLOW_TRACK_PLACE_INVALID_HEIGHTS, gameState.Cheats.AllowTrackPlaceInvalidHeights); - SetCheckboxValue(WIDX_MAKE_DESTRUCTIBLE, gameState.Cheats.MakeAllDestructible); - break; - } - - // Current weather - window_cheats_misc_widgets[WIDX_WEATHER].text = WeatherTypes[EnumValue(gameState.ClimateCurrent.Weather)]; - // Staff speed - window_cheats_misc_widgets[WIDX_STAFF_SPEED].text = _staffSpeedNames[EnumValue(gameState.Cheats.SelectedStaffSpeed)]; - - if (gScreenFlags & SCREEN_FLAGS_EDITOR) - { - SetWidgetDisabled(WIDX_TAB_2, true); - SetWidgetDisabled(WIDX_TAB_3, true); - SetWidgetDisabled(WIDX_NO_MONEY, true); - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - UpdateTabPositions(); - DrawWidgets(dpi); - DrawTabImages(dpi); - - static constexpr int16_t _xLcol = 14; - static constexpr int16_t _xRcol = 208; - - if (page == WINDOW_CHEATS_PAGE_MONEY) - { - auto colour = colours[1]; - auto ft = Formatter(); - ft.Add(_moneySpinnerValue); - if (IsWidgetDisabled(WIDX_MONEY_SPINNER)) - { - colour |= COLOUR_FLAG_INSET; - } - int32_t actual_month = _monthSpinnerValue - 1; - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 93 }, STR_BOTTOM_TOOLBAR_CASH, ft, { colour }); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 198 }, STR_YEAR); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 219 }, STR_MONTH); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 240 }, STR_DAY); - ft = Formatter(); - ft.Add(_yearSpinnerValue); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ _xRcol, 198 }, STR_FORMAT_INTEGER, ft, { colours[1], TextAlignment::RIGHT }); - ft = Formatter(); - ft.Add(actual_month); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ _xRcol, 219 }, STR_FORMAT_MONTH, ft, { colours[1], TextAlignment::RIGHT }); - ft = Formatter(); - ft.Add(_daySpinnerValue); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ _xRcol, 240 }, STR_FORMAT_INTEGER, ft, { colours[1], TextAlignment::RIGHT }); - } - else if (page == WINDOW_CHEATS_PAGE_MISC) - { - { - auto& widget = widgets[WIDX_WEATHER]; - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol - 3, widget.top + 1 }, STR_CHANGE_WEATHER); + case WINDOW_CHEATS_PAGE_GUESTS: + { + auto ft = Formatter::Common(); + ft.Add(1000.00_GBP); + SetCheckboxValue(WIDX_GUEST_IGNORE_RIDE_INTENSITY, gameState.Cheats.IgnoreRideIntensity); + SetCheckboxValue(WIDX_DISABLE_VANDALISM, gameState.Cheats.DisableVandalism); + SetCheckboxValue(WIDX_DISABLE_LITTERING, gameState.Cheats.DisableLittering); + break; + } + case WINDOW_CHEATS_PAGE_MISC: + widgets[WIDX_OPEN_CLOSE_PARK].text = (gameState.ParkFlags & PARK_FLAGS_PARK_OPEN) ? STR_CHEAT_CLOSE_PARK + : STR_CHEAT_OPEN_PARK; + SetCheckboxValue(WIDX_FORCE_PARK_RATING, ParkGetForcedRating() >= 0); + SetCheckboxValue(WIDX_FREEZE_WEATHER, gameState.Cheats.FreezeWeather); + SetCheckboxValue(WIDX_NEVERENDING_MARKETING, gameState.Cheats.NeverendingMarketing); + SetCheckboxValue(WIDX_DISABLE_PLANT_AGING, gameState.Cheats.DisablePlantAging); + SetCheckboxValue(WIDX_ALLOW_REGULAR_PATH_AS_QUEUE, gameState.Cheats.AllowRegularPathAsQueue); + SetCheckboxValue(WIDX_ALLOW_SPECIAL_COLOUR_SCHEMES, gameState.Cheats.AllowSpecialColourSchemes); + break; + case WINDOW_CHEATS_PAGE_RIDES: + SetCheckboxValue(WIDX_UNLOCK_OPERATING_LIMITS, gameState.Cheats.UnlockOperatingLimits); + SetCheckboxValue(WIDX_DISABLE_BRAKES_FAILURE, gameState.Cheats.DisableBrakesFailure); + SetCheckboxValue(WIDX_DISABLE_ALL_BREAKDOWNS, gameState.Cheats.DisableAllBreakdowns); + SetCheckboxValue(WIDX_BUILD_IN_PAUSE_MODE, gameState.Cheats.BuildInPauseMode); + SetCheckboxValue(WIDX_SHOW_ALL_OPERATING_MODES, gameState.Cheats.ShowAllOperatingModes); + SetCheckboxValue( + WIDX_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES, gameState.Cheats.ShowVehiclesFromOtherTrackTypes); + SetCheckboxValue(WIDX_DISABLE_TRAIN_LENGTH_LIMITS, gameState.Cheats.DisableTrainLengthLimit); + SetCheckboxValue(WIDX_ENABLE_CHAIN_LIFT_ON_ALL_TRACK, gameState.Cheats.EnableChainLiftOnAllTrack); + SetCheckboxValue(WIDX_ENABLE_ARBITRARY_RIDE_TYPE_CHANGES, gameState.Cheats.AllowArbitraryRideTypeChanges); + SetCheckboxValue(WIDX_DISABLE_RIDE_VALUE_AGING, gameState.Cheats.DisableRideValueAging); + SetCheckboxValue(WIDX_IGNORE_RESEARCH_STATUS, gameState.Cheats.IgnoreResearchStatus); + SetCheckboxValue(WIDX_ENABLE_ALL_DRAWABLE_TRACK_PIECES, gameState.Cheats.EnableAllDrawableTrackPieces); + SetCheckboxValue(WIDX_ALLOW_TRACK_PLACE_INVALID_HEIGHTS, gameState.Cheats.AllowTrackPlaceInvalidHeights); + SetCheckboxValue(WIDX_MAKE_DESTRUCTIBLE, gameState.Cheats.MakeAllDestructible); + break; } + // Current weather + window_cheats_misc_widgets[WIDX_WEATHER].text = WeatherTypes[EnumValue(gameState.ClimateCurrent.Weather)]; + // Staff speed + window_cheats_misc_widgets[WIDX_STAFF_SPEED].text = _staffSpeedNames[EnumValue( + gameState.Cheats.SelectedStaffSpeed)]; + + if (gScreenFlags & SCREEN_FLAGS_EDITOR) { + SetWidgetDisabled(WIDX_TAB_2, true); + SetWidgetDisabled(WIDX_TAB_3, true); + SetWidgetDisabled(WIDX_NO_MONEY, true); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + UpdateTabPositions(); + DrawWidgets(dpi); + DrawTabImages(dpi); + + static constexpr int16_t _xLcol = 14; + static constexpr int16_t _xRcol = 208; + + if (page == WINDOW_CHEATS_PAGE_MONEY) + { + auto colour = colours[1]; auto ft = Formatter(); - ft.Add(_parkRatingSpinnerValue); - - auto& widget = widgets[WIDX_PARK_RATING_SPINNER]; + ft.Add(_moneySpinnerValue); + if (IsWidgetDisabled(WIDX_MONEY_SPINNER)) + { + colour |= COLOUR_FLAG_INSET; + } + int32_t actual_month = _monthSpinnerValue - 1; + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 93 }, STR_BOTTOM_TOOLBAR_CASH, ft, { colour }); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 198 }, STR_YEAR); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 219 }, STR_MONTH); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 240 }, STR_DAY); + ft = Formatter(); + ft.Add(_yearSpinnerValue); DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 2 }, STR_FORMAT_INTEGER, ft, { colours[1] }); + dpi, windowPos + ScreenCoordsXY{ _xRcol, 198 }, STR_FORMAT_INTEGER, ft, + { colours[1], TextAlignment::RIGHT }); + ft = Formatter(); + ft.Add(actual_month); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ _xRcol, 219 }, STR_FORMAT_MONTH, ft, { colours[1], TextAlignment::RIGHT }); + ft = Formatter(); + ft.Add(_daySpinnerValue); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ _xRcol, 240 }, STR_FORMAT_INTEGER, ft, + { colours[1], TextAlignment::RIGHT }); } - + else if (page == WINDOW_CHEATS_PAGE_MISC) { - auto& widget = widgets[WIDX_STAFF_SPEED]; - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol - 3, widget.top + 1 }, STR_CHEAT_STAFF_SPEED); + { + auto& widget = widgets[WIDX_WEATHER]; + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol - 3, widget.top + 1 }, STR_CHANGE_WEATHER); + } + + { + auto ft = Formatter(); + ft.Add(_parkRatingSpinnerValue); + + auto& widget = widgets[WIDX_PARK_RATING_SPINNER]; + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 2 }, STR_FORMAT_INTEGER, ft, + { colours[1] }); + } + + { + auto& widget = widgets[WIDX_STAFF_SPEED]; + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol - 3, widget.top + 1 }, STR_CHEAT_STAFF_SPEED); + } + } + else if (page == WINDOW_CHEATS_PAGE_GUESTS) + { + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 72 }, STR_CHEAT_GUEST_HAPPINESS); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 93 }, STR_CHEAT_GUEST_ENERGY); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 114 }, STR_CHEAT_GUEST_HUNGER); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 135 }, STR_CHEAT_GUEST_THIRST); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 156 }, STR_CHEAT_GUEST_NAUSEA); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 177 }, STR_CHEAT_GUEST_NAUSEA_TOLERANCE); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 198 }, STR_CHEAT_GUEST_TOILET); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 219 }, STR_CHEAT_GUEST_PREFERRED_INTENSITY); } } - else if (page == WINDOW_CHEATS_PAGE_GUESTS) - { - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 72 }, STR_CHEAT_GUEST_HAPPINESS); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 93 }, STR_CHEAT_GUEST_ENERGY); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 114 }, STR_CHEAT_GUEST_HUNGER); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 135 }, STR_CHEAT_GUEST_THIRST); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 156 }, STR_CHEAT_GUEST_NAUSEA); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 177 }, STR_CHEAT_GUEST_NAUSEA_TOLERANCE); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 198 }, STR_CHEAT_GUEST_TOILET); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ _xLcol, 219 }, STR_CHEAT_GUEST_PREFERRED_INTENSITY); - } - } - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (page == WINDOW_CHEATS_PAGE_MONEY && widgetIndex == WIDX_MONEY_SPINNER) + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { - auto val = StringToMoney(std::string(text).c_str()); - if (val != kMoney64Undefined) + if (page == WINDOW_CHEATS_PAGE_MONEY && widgetIndex == WIDX_MONEY_SPINNER) { - _moneySpinnerValue = val; + auto val = StringToMoney(std::string(text).c_str()); + if (val != kMoney64Undefined) + { + _moneySpinnerValue = val; + } + Invalidate(); } + } + + OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override + { + if (page == WINDOW_CHEATS_PAGE_RIDES && widgetIndex == WIDX_UNLOCK_OPERATING_LIMITS) + { + auto ft = Formatter{}; + ft.Add(255); + return { fallback, ft }; + } + return { fallback, {} }; + } + + private: + void SetPage(int32_t p) + { + page = p; + frame_no = 0; + + hold_down_widgets = window_cheats_page_hold_down_widgets[p]; + pressed_widgets = 0; + widgets = window_cheats_page_widgets[p]; + + auto maxY = 0; + auto* widget = &widgets[WIDX_TAB_CONTENT]; + while (widget->type != WindowWidgetType::Last) + { + maxY = std::max(maxY, widget->bottom); + widget++; + } + maxY += 6; + + Invalidate(); + height = maxY; + widgets[WIDX_BACKGROUND].bottom = maxY - 1; + widgets[WIDX_PAGE_BACKGROUND].bottom = maxY - 1; Invalidate(); } - } - OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override - { - if (page == WINDOW_CHEATS_PAGE_RIDES && widgetIndex == WIDX_UNLOCK_OPERATING_LIMITS) + void UpdateTabPositions() { - auto ft = Formatter{}; - ft.Add(255); - return { fallback, ft }; - } - return { fallback, {} }; - } + constexpr uint16_t tabs[] = { + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_TAB_3, + WIDX_TAB_4, + }; -private: - void SetPage(int32_t p) - { - page = p; - frame_no = 0; - - hold_down_widgets = window_cheats_page_hold_down_widgets[p]; - pressed_widgets = 0; - widgets = window_cheats_page_widgets[p]; - - auto maxY = 0; - auto* widget = &widgets[WIDX_TAB_CONTENT]; - while (widget->type != WindowWidgetType::Last) - { - maxY = std::max(maxY, widget->bottom); - widget++; - } - maxY += 6; - - Invalidate(); - height = maxY; - widgets[WIDX_BACKGROUND].bottom = maxY - 1; - widgets[WIDX_PAGE_BACKGROUND].bottom = maxY - 1; - Invalidate(); - } - - void UpdateTabPositions() - { - constexpr uint16_t tabs[] = { - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_TAB_3, - WIDX_TAB_4, - }; - - auto left = TAB_START; - for (auto tab : tabs) - { - widgets[tab].left = left; - if (!IsWidgetDisabled(tab)) + auto left = TAB_START; + for (auto tab : tabs) { - left += TAB_WIDTH; + widgets[tab].left = left; + if (!IsWidgetDisabled(tab)) + { + left += TAB_WIDTH; + } } } - } - void DrawTabImages(DrawPixelInfo& dpi) - { - // Money tab - if (!IsWidgetDisabled(WIDX_TAB_1)) + void DrawTabImages(DrawPixelInfo& dpi) { - uint32_t sprite_idx = SPR_TAB_FINANCES_SUMMARY_0; - if (page == WINDOW_CHEATS_PAGE_MONEY) - sprite_idx += (frame_no / 2) % 8; - GfxDrawSprite( - dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }); - } - - // Guests tab - if (!IsWidgetDisabled(WIDX_TAB_2)) - { - uint32_t sprite_idx = SPR_TAB_GUESTS_0; - if (page == WINDOW_CHEATS_PAGE_GUESTS) - sprite_idx += (frame_no / 3) % 8; - GfxDrawSprite( - dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); - } - - // Misc tab - if (!IsWidgetDisabled(WIDX_TAB_3)) - { - GfxDrawSprite( - dpi, ImageId(SPR_TAB_PARK), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].left, widgets[WIDX_TAB_3].top }); - } - - // Rides tab - if (!IsWidgetDisabled(WIDX_TAB_4)) - { - uint32_t sprite_idx = SPR_TAB_RIDE_0; - if (page == WINDOW_CHEATS_PAGE_RIDES) - sprite_idx += (frame_no / 4) % 16; - GfxDrawSprite( - dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_4].left, widgets[WIDX_TAB_4].top }); - } - } - - void OnMouseDownMoney(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_MONEY_SPINNER_INCREMENT: - _moneySpinnerValue = AddClamp_money64( - CHEATS_MONEY_INCREMENT_DIV * (_moneySpinnerValue / CHEATS_MONEY_INCREMENT_DIV), CHEATS_MONEY_INCREMENT_DIV); - InvalidateWidget(WIDX_MONEY_SPINNER); - break; - case WIDX_MONEY_SPINNER_DECREMENT: - _moneySpinnerValue = AddClamp_money64( - CHEATS_MONEY_INCREMENT_DIV * (_moneySpinnerValue / CHEATS_MONEY_INCREMENT_DIV), - -CHEATS_MONEY_INCREMENT_DIV); - InvalidateWidget(WIDX_MONEY_SPINNER); - break; - case WIDX_ADD_MONEY: - CheatsSet(CheatType::AddMoney, _moneySpinnerValue); - break; - case WIDX_YEAR_UP: - _yearSpinnerValue++; - _yearSpinnerValue = std::clamp(_yearSpinnerValue, 1, kMaxYear); - InvalidateWidget(WIDX_YEAR_BOX); - break; - case WIDX_YEAR_DOWN: - _yearSpinnerValue--; - _yearSpinnerValue = std::clamp(_yearSpinnerValue, 1, kMaxYear); - InvalidateWidget(WIDX_YEAR_BOX); - break; - case WIDX_MONTH_UP: - _monthSpinnerValue++; - _monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, static_cast(MONTH_COUNT)); - _daySpinnerValue = std::clamp( - _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); - InvalidateWidget(WIDX_MONTH_BOX); - InvalidateWidget(WIDX_DAY_BOX); - break; - case WIDX_MONTH_DOWN: - _monthSpinnerValue--; - _monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, static_cast(MONTH_COUNT)); - _daySpinnerValue = std::clamp( - _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); - InvalidateWidget(WIDX_MONTH_BOX); - InvalidateWidget(WIDX_DAY_BOX); - break; - case WIDX_DAY_UP: - _daySpinnerValue++; - _daySpinnerValue = std::clamp( - _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); - InvalidateWidget(WIDX_DAY_BOX); - break; - case WIDX_DAY_DOWN: - _daySpinnerValue--; - _daySpinnerValue = std::clamp( - _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); - InvalidateWidget(WIDX_DAY_BOX); - break; - case WIDX_DATE_SET: + // Money tab + if (!IsWidgetDisabled(WIDX_TAB_1)) { - auto setDateAction = ParkSetDateAction(_yearSpinnerValue - 1, _monthSpinnerValue - 1, _daySpinnerValue - 1); - GameActions::Execute(&setDateAction); - WindowInvalidateByClass(WindowClass::BottomToolbar); - break; + uint32_t sprite_idx = SPR_TAB_FINANCES_SUMMARY_0; + if (page == WINDOW_CHEATS_PAGE_MONEY) + sprite_idx += (frame_no / 2) % 8; + GfxDrawSprite( + dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }); } - case WIDX_DATE_RESET: + + // Guests tab + if (!IsWidgetDisabled(WIDX_TAB_2)) { - auto setDateAction = ParkSetDateAction(0, 0, 0); - GameActions::Execute(&setDateAction); - WindowInvalidateByClass(WindowClass::BottomToolbar); - InvalidateWidget(WIDX_YEAR_BOX); - InvalidateWidget(WIDX_MONTH_BOX); - InvalidateWidget(WIDX_DAY_BOX); - break; + uint32_t sprite_idx = SPR_TAB_GUESTS_0; + if (page == WINDOW_CHEATS_PAGE_GUESTS) + sprite_idx += (frame_no / 3) % 8; + GfxDrawSprite( + dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); + } + + // Misc tab + if (!IsWidgetDisabled(WIDX_TAB_3)) + { + GfxDrawSprite( + dpi, ImageId(SPR_TAB_PARK), + windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].left, widgets[WIDX_TAB_3].top }); + } + + // Rides tab + if (!IsWidgetDisabled(WIDX_TAB_4)) + { + uint32_t sprite_idx = SPR_TAB_RIDE_0; + if (page == WINDOW_CHEATS_PAGE_RIDES) + sprite_idx += (frame_no / 4) % 16; + GfxDrawSprite( + dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_4].left, widgets[WIDX_TAB_4].top }); } } - } - void OnMouseUpMoney(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void OnMouseDownMoney(WidgetIndex widgetIndex) { - case WIDX_NO_MONEY: - CheatsSet(CheatType::NoMoney, GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY ? 0 : 1); - break; - case WIDX_MONEY_SPINNER: - MoneyToString(_moneySpinnerValue, _moneySpinnerText, MONEY_STRING_MAXLENGTH, false); - WindowTextInputRawOpen( - this, WIDX_MONEY_SPINNER, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneySpinnerText, - MONEY_STRING_MAXLENGTH); - break; - case WIDX_SET_MONEY: - CheatsSet(CheatType::SetMoney, _moneySpinnerValue); - break; - case WIDX_CLEAR_LOAN: - CheatsSet(CheatType::ClearLoan); - break; - } - } - - void OnMouseDownMisc(WidgetIndex widgetIndex) - { - auto* widget = &widgets[widgetIndex]; - auto& gameState = GetGameState(); - switch (widgetIndex) - { - case WIDX_INCREASE_PARK_RATING: - _parkRatingSpinnerValue = std::min(999, 10 * (_parkRatingSpinnerValue / 10 + 1)); - InvalidateWidget(WIDX_PARK_RATING_SPINNER); - if (ParkGetForcedRating() >= 0) - { - auto cheatSetAction = CheatSetAction(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); - GameActions::Execute(&cheatSetAction); - } - break; - case WIDX_DECREASE_PARK_RATING: - _parkRatingSpinnerValue = std::max(0, 10 * (_parkRatingSpinnerValue / 10 - 1)); - InvalidateWidget(WIDX_PARK_RATING_SPINNER); - if (ParkGetForcedRating() >= 0) - { - CheatsSet(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); - } - break; - case WIDX_WEATHER_DROPDOWN_BUTTON: + switch (widgetIndex) { - Widget* dropdownWidget = widget - 1; - - for (size_t i = 0; i < std::size(WeatherTypes); i++) + case WIDX_MONEY_SPINNER_INCREMENT: + _moneySpinnerValue = AddClamp_money64( + CHEATS_MONEY_INCREMENT_DIV * (_moneySpinnerValue / CHEATS_MONEY_INCREMENT_DIV), + CHEATS_MONEY_INCREMENT_DIV); + InvalidateWidget(WIDX_MONEY_SPINNER); + break; + case WIDX_MONEY_SPINNER_DECREMENT: + _moneySpinnerValue = AddClamp_money64( + CHEATS_MONEY_INCREMENT_DIV * (_moneySpinnerValue / CHEATS_MONEY_INCREMENT_DIV), + -CHEATS_MONEY_INCREMENT_DIV); + InvalidateWidget(WIDX_MONEY_SPINNER); + break; + case WIDX_ADD_MONEY: + CheatsSet(CheatType::AddMoney, _moneySpinnerValue); + break; + case WIDX_YEAR_UP: + _yearSpinnerValue++; + _yearSpinnerValue = std::clamp(_yearSpinnerValue, 1, kMaxYear); + InvalidateWidget(WIDX_YEAR_BOX); + break; + case WIDX_YEAR_DOWN: + _yearSpinnerValue--; + _yearSpinnerValue = std::clamp(_yearSpinnerValue, 1, kMaxYear); + InvalidateWidget(WIDX_YEAR_BOX); + break; + case WIDX_MONTH_UP: + _monthSpinnerValue++; + _monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, static_cast(MONTH_COUNT)); + _daySpinnerValue = std::clamp( + _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); + InvalidateWidget(WIDX_MONTH_BOX); + InvalidateWidget(WIDX_DAY_BOX); + break; + case WIDX_MONTH_DOWN: + _monthSpinnerValue--; + _monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, static_cast(MONTH_COUNT)); + _daySpinnerValue = std::clamp( + _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); + InvalidateWidget(WIDX_MONTH_BOX); + InvalidateWidget(WIDX_DAY_BOX); + break; + case WIDX_DAY_UP: + _daySpinnerValue++; + _daySpinnerValue = std::clamp( + _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); + InvalidateWidget(WIDX_DAY_BOX); + break; + case WIDX_DAY_DOWN: + _daySpinnerValue--; + _daySpinnerValue = std::clamp( + _daySpinnerValue, 1, static_cast(Date::GetDaysInMonth(_monthSpinnerValue - 1))); + InvalidateWidget(WIDX_DAY_BOX); + break; + case WIDX_DATE_SET: { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = WeatherTypes[i]; + auto setDateAction = ParkSetDateAction(_yearSpinnerValue - 1, _monthSpinnerValue - 1, _daySpinnerValue - 1); + GameActions::Execute(&setDateAction); + WindowInvalidateByClass(WindowClass::BottomToolbar); + break; } - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, std::size(WeatherTypes), dropdownWidget->width() - 3); - - auto currentWeather = gameState.ClimateCurrent.Weather; - Dropdown::SetChecked(EnumValue(currentWeather), true); - } - break; - case WIDX_STAFF_SPEED_DROPDOWN_BUTTON: - { - Widget* dropdownWidget; - - dropdownWidget = widget - 1; - - for (size_t i = 0; i < std::size(_staffSpeedNames); i++) + case WIDX_DATE_RESET: { - gDropdownItems[i].Args = _staffSpeedNames[i]; - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + auto setDateAction = ParkSetDateAction(0, 0, 0); + GameActions::Execute(&setDateAction); + WindowInvalidateByClass(WindowClass::BottomToolbar); + InvalidateWidget(WIDX_YEAR_BOX); + InvalidateWidget(WIDX_MONTH_BOX); + InvalidateWidget(WIDX_DAY_BOX); + break; } - - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, 3, dropdownWidget->width() - 3); - Dropdown::SetChecked(EnumValue(gameState.Cheats.SelectedStaffSpeed), true); } } - } - void OnMouseUpMisc(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) + void OnMouseUpMoney(WidgetIndex widgetIndex) { - case WIDX_FREEZE_WEATHER: - CheatsSet(CheatType::FreezeWeather, !gameState.Cheats.FreezeWeather); - break; - case WIDX_OPEN_CLOSE_PARK: - CheatsSet(CheatType::OpenClosePark); - break; - case WIDX_CREATE_DUCKS: - CheatsSet(CheatType::CreateDucks, kCheatsDuckIncrement); - break; - case WIDX_REMOVE_DUCKS: - CheatsSet(CheatType::RemoveDucks); - break; - case WIDX_CLEAR_GRASS: - CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_CLEAR_0); - break; - case WIDX_MOWED_GRASS: - CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_MOWED); - break; - case WIDX_WATER_PLANTS: - CheatsSet(CheatType::WaterPlants); - break; - case WIDX_FIX_VANDALISM: - CheatsSet(CheatType::FixVandalism); - break; - case WIDX_REMOVE_LITTER: - CheatsSet(CheatType::RemoveLitter); - break; - case WIDX_DISABLE_PLANT_AGING: - CheatsSet(CheatType::DisablePlantAging, !gameState.Cheats.DisablePlantAging); - break; - case WIDX_WIN_SCENARIO: - CheatsSet(CheatType::WinScenario); - break; - case WIDX_HAVE_FUN: - CheatsSet(CheatType::HaveFun); - break; - case WIDX_OWN_ALL_LAND: - CheatsSet(CheatType::OwnAllLand); - break; - case WIDX_NEVERENDING_MARKETING: - CheatsSet(CheatType::NeverEndingMarketing, !gameState.Cheats.NeverendingMarketing); - break; - case WIDX_FORCE_PARK_RATING: - if (ParkGetForcedRating() >= 0) - { - CheatsSet(CheatType::SetForcedParkRating, -1); - } - else - { - CheatsSet(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); - } - break; - case WIDX_ALLOW_REGULAR_PATH_AS_QUEUE: - CheatsSet(CheatType::AllowRegularPathAsQueue, !gameState.Cheats.AllowRegularPathAsQueue); - break; - case WIDX_ALLOW_SPECIAL_COLOUR_SCHEMES: - CheatsSet(CheatType::AllowSpecialColourSchemes, !gameState.Cheats.AllowSpecialColourSchemes); - break; - } - } - - void OnDropdownMisc(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (dropdownIndex == -1) - { - return; + switch (widgetIndex) + { + case WIDX_NO_MONEY: + CheatsSet(CheatType::NoMoney, GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY ? 0 : 1); + break; + case WIDX_MONEY_SPINNER: + MoneyToString(_moneySpinnerValue, _moneySpinnerText, MONEY_STRING_MAXLENGTH, false); + WindowTextInputRawOpen( + this, WIDX_MONEY_SPINNER, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, {}, _moneySpinnerText, + MONEY_STRING_MAXLENGTH); + break; + case WIDX_SET_MONEY: + CheatsSet(CheatType::SetMoney, _moneySpinnerValue); + break; + case WIDX_CLEAR_LOAN: + CheatsSet(CheatType::ClearLoan); + break; + } } - if (widgetIndex == WIDX_WEATHER_DROPDOWN_BUTTON) + void OnMouseDownMisc(WidgetIndex widgetIndex) { - CheatsSet(CheatType::ForceWeather, dropdownIndex); - } - if (widgetIndex == WIDX_STAFF_SPEED_DROPDOWN_BUTTON) - { - int32_t speed = kCheatsStaffNormalSpeed; + auto* widget = &widgets[widgetIndex]; auto& gameState = GetGameState(); - switch (dropdownIndex) + switch (widgetIndex) { - case 0: - gameState.Cheats.SelectedStaffSpeed = StaffSpeedCheat::None; - speed = kCheatsStaffNormalSpeed; + case WIDX_INCREASE_PARK_RATING: + _parkRatingSpinnerValue = std::min(999, 10 * (_parkRatingSpinnerValue / 10 + 1)); + InvalidateWidget(WIDX_PARK_RATING_SPINNER); + if (ParkGetForcedRating() >= 0) + { + auto cheatSetAction = CheatSetAction(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); + GameActions::Execute(&cheatSetAction); + } break; - - case 1: - gameState.Cheats.SelectedStaffSpeed = StaffSpeedCheat::Frozen; - speed = kCheatsStaffFreezeSpeed; + case WIDX_DECREASE_PARK_RATING: + _parkRatingSpinnerValue = std::max(0, 10 * (_parkRatingSpinnerValue / 10 - 1)); + InvalidateWidget(WIDX_PARK_RATING_SPINNER); + if (ParkGetForcedRating() >= 0) + { + CheatsSet(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); + } break; + case WIDX_WEATHER_DROPDOWN_BUTTON: + { + Widget* dropdownWidget = widget - 1; - case 2: - gameState.Cheats.SelectedStaffSpeed = StaffSpeedCheat::Fast; - speed = kCheatsStaffFastSpeed; + for (size_t i = 0; i < std::size(WeatherTypes); i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = WeatherTypes[i]; + } + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, std::size(WeatherTypes), dropdownWidget->width() - 3); + + auto currentWeather = gameState.ClimateCurrent.Weather; + Dropdown::SetChecked(EnumValue(currentWeather), true); + } + break; + case WIDX_STAFF_SPEED_DROPDOWN_BUTTON: + { + Widget* dropdownWidget; + + dropdownWidget = widget - 1; + + for (size_t i = 0; i < std::size(_staffSpeedNames); i++) + { + gDropdownItems[i].Args = _staffSpeedNames[i]; + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + } + + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 3, dropdownWidget->width() - 3); + Dropdown::SetChecked(EnumValue(gameState.Cheats.SelectedStaffSpeed), true); + } } - CheatsSet(CheatType::SetStaffSpeed, speed); } - } - void OnMouseUpGuests(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) + void OnMouseUpMisc(WidgetIndex widgetIndex) { - case WIDX_GUEST_HAPPINESS_MAX: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HAPPINESS, PEEP_MAX_HAPPINESS); - break; - case WIDX_GUEST_HAPPINESS_MIN: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HAPPINESS, 0); - break; - case WIDX_GUEST_ENERGY_MAX: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_ENERGY, PEEP_MAX_ENERGY); - break; - case WIDX_GUEST_ENERGY_MIN: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_ENERGY, PEEP_MIN_ENERGY); - break; - case WIDX_GUEST_HUNGER_MAX: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HUNGER, 0); - break; - case WIDX_GUEST_HUNGER_MIN: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HUNGER, PEEP_MAX_HUNGER); - break; - case WIDX_GUEST_THIRST_MAX: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_THIRST, 0); - break; - case WIDX_GUEST_THIRST_MIN: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_THIRST, PEEP_MAX_THIRST); - break; - case WIDX_GUEST_NAUSEA_MAX: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA, PEEP_MAX_NAUSEA); - break; - case WIDX_GUEST_NAUSEA_MIN: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA, 0); - break; - case WIDX_GUEST_NAUSEA_TOLERANCE_MAX: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA_TOLERANCE, EnumValue(PeepNauseaTolerance::High)); - break; - case WIDX_GUEST_NAUSEA_TOLERANCE_MIN: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA_TOLERANCE, EnumValue(PeepNauseaTolerance::None)); - break; - case WIDX_GUEST_TOILET_MAX: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_TOILET, PEEP_MAX_TOILET); - break; - case WIDX_GUEST_TOILET_MIN: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_TOILET, 0); - break; - case WIDX_GUEST_RIDE_INTENSITY_MORE_THAN_1: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, 1); - break; - case WIDX_GUEST_RIDE_INTENSITY_LESS_THAN_15: - CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, 0); - break; - case WIDX_TRAM_GUESTS: - CheatsSet(CheatType::GenerateGuests, kCheatsTramIncrement); - break; - case WIDX_REMOVE_ALL_GUESTS: - CheatsSet(CheatType::RemoveAllGuests); - break; - case WIDX_GIVE_GUESTS_MONEY: - CheatsSet(CheatType::GiveAllGuests, OBJECT_MONEY); - break; - case WIDX_GIVE_GUESTS_PARK_MAPS: - CheatsSet(CheatType::GiveAllGuests, OBJECT_PARK_MAP); - break; - case WIDX_GIVE_GUESTS_BALLOONS: - CheatsSet(CheatType::GiveAllGuests, OBJECT_BALLOON); - break; - case WIDX_GIVE_GUESTS_UMBRELLAS: - CheatsSet(CheatType::GiveAllGuests, OBJECT_UMBRELLA); - break; - case WIDX_GUEST_IGNORE_RIDE_INTENSITY: - CheatsSet(CheatType::IgnoreRideIntensity, !gameState.Cheats.IgnoreRideIntensity); - break; - case WIDX_DISABLE_VANDALISM: - CheatsSet(CheatType::DisableVandalism, !gameState.Cheats.DisableVandalism); - break; - case WIDX_DISABLE_LITTERING: - CheatsSet(CheatType::DisableLittering, !gameState.Cheats.DisableLittering); - break; + auto& gameState = GetGameState(); + switch (widgetIndex) + { + case WIDX_FREEZE_WEATHER: + CheatsSet(CheatType::FreezeWeather, !gameState.Cheats.FreezeWeather); + break; + case WIDX_OPEN_CLOSE_PARK: + CheatsSet(CheatType::OpenClosePark); + break; + case WIDX_CREATE_DUCKS: + CheatsSet(CheatType::CreateDucks, kCheatsDuckIncrement); + break; + case WIDX_REMOVE_DUCKS: + CheatsSet(CheatType::RemoveDucks); + break; + case WIDX_CLEAR_GRASS: + CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_CLEAR_0); + break; + case WIDX_MOWED_GRASS: + CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_MOWED); + break; + case WIDX_WATER_PLANTS: + CheatsSet(CheatType::WaterPlants); + break; + case WIDX_FIX_VANDALISM: + CheatsSet(CheatType::FixVandalism); + break; + case WIDX_REMOVE_LITTER: + CheatsSet(CheatType::RemoveLitter); + break; + case WIDX_DISABLE_PLANT_AGING: + CheatsSet(CheatType::DisablePlantAging, !gameState.Cheats.DisablePlantAging); + break; + case WIDX_WIN_SCENARIO: + CheatsSet(CheatType::WinScenario); + break; + case WIDX_HAVE_FUN: + CheatsSet(CheatType::HaveFun); + break; + case WIDX_OWN_ALL_LAND: + CheatsSet(CheatType::OwnAllLand); + break; + case WIDX_NEVERENDING_MARKETING: + CheatsSet(CheatType::NeverEndingMarketing, !gameState.Cheats.NeverendingMarketing); + break; + case WIDX_FORCE_PARK_RATING: + if (ParkGetForcedRating() >= 0) + { + CheatsSet(CheatType::SetForcedParkRating, -1); + } + else + { + CheatsSet(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); + } + break; + case WIDX_ALLOW_REGULAR_PATH_AS_QUEUE: + CheatsSet(CheatType::AllowRegularPathAsQueue, !gameState.Cheats.AllowRegularPathAsQueue); + break; + case WIDX_ALLOW_SPECIAL_COLOUR_SCHEMES: + CheatsSet(CheatType::AllowSpecialColourSchemes, !gameState.Cheats.AllowSpecialColourSchemes); + break; + } } - } - void OnMouseUpRides(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) + void OnDropdownMisc(WidgetIndex widgetIndex, int32_t dropdownIndex) { - case WIDX_RENEW_RIDES: - CheatsSet(CheatType::RenewRides); - break; - case WIDX_MAKE_DESTRUCTIBLE: - CheatsSet(CheatType::MakeDestructible, !gameState.Cheats.MakeAllDestructible); - break; - case WIDX_FIX_ALL: - CheatsSet(CheatType::FixRides); - break; - case WIDX_UNLOCK_OPERATING_LIMITS: - CheatsSet(CheatType::FastLiftHill, !gameState.Cheats.UnlockOperatingLimits); - break; - case WIDX_DISABLE_BRAKES_FAILURE: - CheatsSet(CheatType::DisableBrakesFailure, !gameState.Cheats.DisableBrakesFailure); - break; - case WIDX_DISABLE_ALL_BREAKDOWNS: - CheatsSet(CheatType::DisableAllBreakdowns, !gameState.Cheats.DisableAllBreakdowns); - break; - case WIDX_BUILD_IN_PAUSE_MODE: - CheatsSet(CheatType::BuildInPauseMode, !gameState.Cheats.BuildInPauseMode); - break; - case WIDX_RESET_CRASH_STATUS: - CheatsSet(CheatType::ResetCrashStatus); - break; - case WIDX_10_MINUTE_INSPECTIONS: - CheatsSet(CheatType::TenMinuteInspections); - break; - case WIDX_SHOW_ALL_OPERATING_MODES: + if (dropdownIndex == -1) { - if (!gameState.Cheats.ShowAllOperatingModes) - { - ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); - } - CheatsSet(CheatType::ShowAllOperatingModes, !gameState.Cheats.ShowAllOperatingModes); + return; } - break; - case WIDX_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES: + + if (widgetIndex == WIDX_WEATHER_DROPDOWN_BUTTON) { - if (!gameState.Cheats.ShowVehiclesFromOtherTrackTypes) - { - ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); - } - CheatsSet(CheatType::ShowVehiclesFromOtherTrackTypes, !gameState.Cheats.ShowVehiclesFromOtherTrackTypes); + CheatsSet(CheatType::ForceWeather, dropdownIndex); } - break; - case WIDX_DISABLE_TRAIN_LENGTH_LIMITS: + if (widgetIndex == WIDX_STAFF_SPEED_DROPDOWN_BUTTON) { - if (!gameState.Cheats.DisableTrainLengthLimit) + int32_t speed = kCheatsStaffNormalSpeed; + auto& gameState = GetGameState(); + switch (dropdownIndex) { - ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); + case 0: + gameState.Cheats.SelectedStaffSpeed = StaffSpeedCheat::None; + speed = kCheatsStaffNormalSpeed; + break; + + case 1: + gameState.Cheats.SelectedStaffSpeed = StaffSpeedCheat::Frozen; + speed = kCheatsStaffFreezeSpeed; + break; + + case 2: + gameState.Cheats.SelectedStaffSpeed = StaffSpeedCheat::Fast; + speed = kCheatsStaffFastSpeed; } - CheatsSet(CheatType::DisableTrainLengthLimit, !gameState.Cheats.DisableTrainLengthLimit); + CheatsSet(CheatType::SetStaffSpeed, speed); } - break; - case WIDX_ENABLE_CHAIN_LIFT_ON_ALL_TRACK: - CheatsSet(CheatType::EnableChainLiftOnAllTrack, !gameState.Cheats.EnableChainLiftOnAllTrack); - break; - case WIDX_ENABLE_ARBITRARY_RIDE_TYPE_CHANGES: - { - if (!gameState.Cheats.AllowArbitraryRideTypeChanges) - { - ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); - } - CheatsSet(CheatType::AllowArbitraryRideTypeChanges, !gameState.Cheats.AllowArbitraryRideTypeChanges); - } - break; - case WIDX_DISABLE_RIDE_VALUE_AGING: - CheatsSet(CheatType::DisableRideValueAging, !gameState.Cheats.DisableRideValueAging); - break; - case WIDX_IGNORE_RESEARCH_STATUS: - CheatsSet(CheatType::IgnoreResearchStatus, !gameState.Cheats.IgnoreResearchStatus); - break; - case WIDX_ENABLE_ALL_DRAWABLE_TRACK_PIECES: - CheatsSet(CheatType::EnableAllDrawableTrackPieces, !gameState.Cheats.EnableAllDrawableTrackPieces); - break; - case WIDX_ALLOW_TRACK_PLACE_INVALID_HEIGHTS: - { - if (!gameState.Cheats.AllowTrackPlaceInvalidHeights) - { - ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); - } - CheatsSet(CheatType::AllowTrackPlaceInvalidHeights, !gameState.Cheats.AllowTrackPlaceInvalidHeights); - } - break; } - } - void OnResize() override - { - ResizeFrameWithPage(); - } -}; + void OnMouseUpGuests(WidgetIndex widgetIndex) + { + auto& gameState = GetGameState(); + switch (widgetIndex) + { + case WIDX_GUEST_HAPPINESS_MAX: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HAPPINESS, PEEP_MAX_HAPPINESS); + break; + case WIDX_GUEST_HAPPINESS_MIN: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HAPPINESS, 0); + break; + case WIDX_GUEST_ENERGY_MAX: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_ENERGY, PEEP_MAX_ENERGY); + break; + case WIDX_GUEST_ENERGY_MIN: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_ENERGY, PEEP_MIN_ENERGY); + break; + case WIDX_GUEST_HUNGER_MAX: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HUNGER, 0); + break; + case WIDX_GUEST_HUNGER_MIN: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HUNGER, PEEP_MAX_HUNGER); + break; + case WIDX_GUEST_THIRST_MAX: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_THIRST, 0); + break; + case WIDX_GUEST_THIRST_MIN: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_THIRST, PEEP_MAX_THIRST); + break; + case WIDX_GUEST_NAUSEA_MAX: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA, PEEP_MAX_NAUSEA); + break; + case WIDX_GUEST_NAUSEA_MIN: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA, 0); + break; + case WIDX_GUEST_NAUSEA_TOLERANCE_MAX: + CheatsSet( + CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA_TOLERANCE, EnumValue(PeepNauseaTolerance::High)); + break; + case WIDX_GUEST_NAUSEA_TOLERANCE_MIN: + CheatsSet( + CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA_TOLERANCE, EnumValue(PeepNauseaTolerance::None)); + break; + case WIDX_GUEST_TOILET_MAX: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_TOILET, PEEP_MAX_TOILET); + break; + case WIDX_GUEST_TOILET_MIN: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_TOILET, 0); + break; + case WIDX_GUEST_RIDE_INTENSITY_MORE_THAN_1: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, 1); + break; + case WIDX_GUEST_RIDE_INTENSITY_LESS_THAN_15: + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, 0); + break; + case WIDX_TRAM_GUESTS: + CheatsSet(CheatType::GenerateGuests, kCheatsTramIncrement); + break; + case WIDX_REMOVE_ALL_GUESTS: + CheatsSet(CheatType::RemoveAllGuests); + break; + case WIDX_GIVE_GUESTS_MONEY: + CheatsSet(CheatType::GiveAllGuests, OBJECT_MONEY); + break; + case WIDX_GIVE_GUESTS_PARK_MAPS: + CheatsSet(CheatType::GiveAllGuests, OBJECT_PARK_MAP); + break; + case WIDX_GIVE_GUESTS_BALLOONS: + CheatsSet(CheatType::GiveAllGuests, OBJECT_BALLOON); + break; + case WIDX_GIVE_GUESTS_UMBRELLAS: + CheatsSet(CheatType::GiveAllGuests, OBJECT_UMBRELLA); + break; + case WIDX_GUEST_IGNORE_RIDE_INTENSITY: + CheatsSet(CheatType::IgnoreRideIntensity, !gameState.Cheats.IgnoreRideIntensity); + break; + case WIDX_DISABLE_VANDALISM: + CheatsSet(CheatType::DisableVandalism, !gameState.Cheats.DisableVandalism); + break; + case WIDX_DISABLE_LITTERING: + CheatsSet(CheatType::DisableLittering, !gameState.Cheats.DisableLittering); + break; + } + } -WindowBase* WindowCheatsOpen() -{ - auto* window = WindowBringToFrontByClass(WindowClass::Cheats); - if (window == nullptr) + void OnMouseUpRides(WidgetIndex widgetIndex) + { + auto& gameState = GetGameState(); + switch (widgetIndex) + { + case WIDX_RENEW_RIDES: + CheatsSet(CheatType::RenewRides); + break; + case WIDX_MAKE_DESTRUCTIBLE: + CheatsSet(CheatType::MakeDestructible, !gameState.Cheats.MakeAllDestructible); + break; + case WIDX_FIX_ALL: + CheatsSet(CheatType::FixRides); + break; + case WIDX_UNLOCK_OPERATING_LIMITS: + CheatsSet(CheatType::FastLiftHill, !gameState.Cheats.UnlockOperatingLimits); + break; + case WIDX_DISABLE_BRAKES_FAILURE: + CheatsSet(CheatType::DisableBrakesFailure, !gameState.Cheats.DisableBrakesFailure); + break; + case WIDX_DISABLE_ALL_BREAKDOWNS: + CheatsSet(CheatType::DisableAllBreakdowns, !gameState.Cheats.DisableAllBreakdowns); + break; + case WIDX_BUILD_IN_PAUSE_MODE: + CheatsSet(CheatType::BuildInPauseMode, !gameState.Cheats.BuildInPauseMode); + break; + case WIDX_RESET_CRASH_STATUS: + CheatsSet(CheatType::ResetCrashStatus); + break; + case WIDX_10_MINUTE_INSPECTIONS: + CheatsSet(CheatType::TenMinuteInspections); + break; + case WIDX_SHOW_ALL_OPERATING_MODES: + { + if (!gameState.Cheats.ShowAllOperatingModes) + { + ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); + } + CheatsSet(CheatType::ShowAllOperatingModes, !gameState.Cheats.ShowAllOperatingModes); + } + break; + case WIDX_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES: + { + if (!gameState.Cheats.ShowVehiclesFromOtherTrackTypes) + { + ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); + } + CheatsSet(CheatType::ShowVehiclesFromOtherTrackTypes, !gameState.Cheats.ShowVehiclesFromOtherTrackTypes); + } + break; + case WIDX_DISABLE_TRAIN_LENGTH_LIMITS: + { + if (!gameState.Cheats.DisableTrainLengthLimit) + { + ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); + } + CheatsSet(CheatType::DisableTrainLengthLimit, !gameState.Cheats.DisableTrainLengthLimit); + } + break; + case WIDX_ENABLE_CHAIN_LIFT_ON_ALL_TRACK: + CheatsSet(CheatType::EnableChainLiftOnAllTrack, !gameState.Cheats.EnableChainLiftOnAllTrack); + break; + case WIDX_ENABLE_ARBITRARY_RIDE_TYPE_CHANGES: + { + if (!gameState.Cheats.AllowArbitraryRideTypeChanges) + { + ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); + } + CheatsSet(CheatType::AllowArbitraryRideTypeChanges, !gameState.Cheats.AllowArbitraryRideTypeChanges); + } + break; + case WIDX_DISABLE_RIDE_VALUE_AGING: + CheatsSet(CheatType::DisableRideValueAging, !gameState.Cheats.DisableRideValueAging); + break; + case WIDX_IGNORE_RESEARCH_STATUS: + CheatsSet(CheatType::IgnoreResearchStatus, !gameState.Cheats.IgnoreResearchStatus); + break; + case WIDX_ENABLE_ALL_DRAWABLE_TRACK_PIECES: + CheatsSet(CheatType::EnableAllDrawableTrackPieces, !gameState.Cheats.EnableAllDrawableTrackPieces); + break; + case WIDX_ALLOW_TRACK_PLACE_INVALID_HEIGHTS: + { + if (!gameState.Cheats.AllowTrackPlaceInvalidHeights) + { + ContextShowError(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE, {}); + } + CheatsSet(CheatType::AllowTrackPlaceInvalidHeights, !gameState.Cheats.AllowTrackPlaceInvalidHeights); + } + break; + } + } + + void OnResize() override + { + ResizeFrameWithPage(); + } + }; + + WindowBase* WindowCheatsOpen() { - window = WindowCreate(WindowClass::Cheats, ScreenCoordsXY(32, 32), WW, WH); + auto* window = WindowBringToFrontByClass(WindowClass::Cheats); + if (window == nullptr) + { + window = WindowCreate(WindowClass::Cheats, ScreenCoordsXY(32, 32), WW, WH); + } + return window; } - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/ClearScenery.cpp b/src/openrct2-ui/windows/ClearScenery.cpp index 0eff932f74..9909bda483 100644 --- a/src/openrct2-ui/windows/ClearScenery.cpp +++ b/src/openrct2-ui/windows/ClearScenery.cpp @@ -18,205 +18,208 @@ #include #include -using namespace OpenRCT2; - -enum WindowClearSceneryWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PREVIEW, - WIDX_DECREMENT, - WIDX_INCREMENT, - WIDX_SMALL_SCENERY, - WIDX_LARGE_SCENERY, - WIDX_FOOTPATH -}; -// clang-format on -static constexpr StringId WINDOW_TITLE = STR_CLEAR_SCENERY; -static constexpr int32_t WW = 98; -static constexpr int32_t WH = 94; - -static constexpr ScreenSize CLEAR_SCENERY_BUTTON = { 24, 24 }; - -static Widget window_clear_scenery_widgets[] = { - WINDOW_SHIM(WINDOW_TITLE, WW, WH), - MakeWidget( - { 27, 17 }, { 44, 32 }, WindowWidgetType::ImgBtn, WindowColour::Primary, SPR_LAND_TOOL_SIZE_0, STR_NONE), // preview box - MakeRemapWidget( - { 28, 18 }, { 16, 16 }, WindowWidgetType::TrnBtn, WindowColour::Secondary, SPR_LAND_TOOL_DECREASE, - STR_ADJUST_SMALLER_LAND_TIP), // decrement size - MakeRemapWidget( - { 54, 32 }, { 16, 16 }, WindowWidgetType::TrnBtn, WindowColour::Secondary, SPR_LAND_TOOL_INCREASE, - STR_ADJUST_LARGER_LAND_TIP), // increment size - MakeRemapWidget( - { 7, 53 }, CLEAR_SCENERY_BUTTON, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_BUTTON_TREES, - STR_CLEAR_SCENERY_REMOVE_SMALL_SCENERY_TIP), // small scenery - MakeRemapWidget( - { 37, 53 }, CLEAR_SCENERY_BUTTON, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_BUTTON_LARGE_SCENERY, - STR_CLEAR_SCENERY_REMOVE_LARGE_SCENERY_TIP), // large scenery - MakeRemapWidget( - { 67, 53 }, CLEAR_SCENERY_BUTTON, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_BUTTON_FOOTPATH, - STR_CLEAR_SCENERY_REMOVE_FOOTPATHS_TIP), // footpaths - kWidgetsEnd, -}; - -class CleanSceneryWindow final : public Window -{ -public: - void OnOpen() override + enum WindowClearSceneryWidgetIdx { - widgets = window_clear_scenery_widgets; - hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); - WindowInitScrollWidgets(*this); - WindowPushOthersBelow(*this); + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PREVIEW, + WIDX_DECREMENT, + WIDX_INCREMENT, + WIDX_SMALL_SCENERY, + WIDX_LARGE_SCENERY, + WIDX_FOOTPATH + }; + // clang-format on + static constexpr StringId WINDOW_TITLE = STR_CLEAR_SCENERY; + static constexpr int32_t WW = 98; + static constexpr int32_t WH = 94; - gLandToolSize = 2; - gClearSceneryCost = kMoney64Undefined; + static constexpr ScreenSize CLEAR_SCENERY_BUTTON = { 24, 24 }; - gClearSmallScenery = true; - gClearLargeScenery = false; - gClearFootpath = false; + static Widget window_clear_scenery_widgets[] = { + WINDOW_SHIM(WINDOW_TITLE, WW, WH), + MakeWidget( + { 27, 17 }, { 44, 32 }, WindowWidgetType::ImgBtn, WindowColour::Primary, SPR_LAND_TOOL_SIZE_0, + STR_NONE), // preview box + MakeRemapWidget( + { 28, 18 }, { 16, 16 }, WindowWidgetType::TrnBtn, WindowColour::Secondary, SPR_LAND_TOOL_DECREASE, + STR_ADJUST_SMALLER_LAND_TIP), // decrement size + MakeRemapWidget( + { 54, 32 }, { 16, 16 }, WindowWidgetType::TrnBtn, WindowColour::Secondary, SPR_LAND_TOOL_INCREASE, + STR_ADJUST_LARGER_LAND_TIP), // increment size + MakeRemapWidget( + { 7, 53 }, CLEAR_SCENERY_BUTTON, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_BUTTON_TREES, + STR_CLEAR_SCENERY_REMOVE_SMALL_SCENERY_TIP), // small scenery + MakeRemapWidget( + { 37, 53 }, CLEAR_SCENERY_BUTTON, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_BUTTON_LARGE_SCENERY, + STR_CLEAR_SCENERY_REMOVE_LARGE_SCENERY_TIP), // large scenery + MakeRemapWidget( + { 67, 53 }, CLEAR_SCENERY_BUTTON, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_BUTTON_FOOTPATH, + STR_CLEAR_SCENERY_REMOVE_FOOTPATHS_TIP), // footpaths + kWidgetsEnd, + }; - Invalidate(); - } - - void OnClose() override + class CleanSceneryWindow final : public Window { - if (ClearSceneryToolIsActive()) - ToolCancel(); - } - - void OnMouseUp(const WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_PREVIEW: - { - Formatter ft; - ft.Add(kLandToolMinimumSize); - ft.Add(kLandToolMaximumSize); - TextInputOpen(WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); - break; - } - case WIDX_SMALL_SCENERY: - gClearSmallScenery ^= 1; - Invalidate(); - break; - case WIDX_LARGE_SCENERY: - gClearLargeScenery ^= 1; - Invalidate(); - break; - case WIDX_FOOTPATH: - gClearFootpath ^= 1; - Invalidate(); - break; - } - } + widgets = window_clear_scenery_widgets; + hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); + WindowInitScrollWidgets(*this); + WindowPushOthersBelow(*this); - void OnMouseDown(const WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_DECREMENT: - // Decrement land tool size, if it stays within the limit - gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); + gLandToolSize = 2; + gClearSceneryCost = kMoney64Undefined; - // Invalidate the window - Invalidate(); - break; - case WIDX_INCREMENT: - // Increment land tool size, if it stays within the limit - gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); + gClearSmallScenery = true; + gClearLargeScenery = false; + gClearFootpath = false; - // Invalidate the window - Invalidate(); - break; - } - } - - void OnTextInput(const WidgetIndex widgetIndex, const std::string_view text) override - { - if (widgetIndex != WIDX_PREVIEW || text.empty()) - return; - - try - { - int32_t size = std::stol(std::string(text)); - size = std::clamp(size, kLandToolMinimumSize, kLandToolMaximumSize); - gLandToolSize = size; Invalidate(); } - catch (const std::logic_error&) + + void OnClose() override { - // std::stol can throw std::out_of_range or std::invalid_argument - } - } - - void OnUpdate() override - { - frame_no++; - // Close window if another tool is open - if (!ClearSceneryToolIsActive()) - Close(); - } - - void Invalidate() - { - // Set the preview image button to be pressed down - pressed_widgets = (1uLL << WIDX_PREVIEW) | (gClearSmallScenery ? (1uLL << WIDX_SMALL_SCENERY) : 0) - | (gClearLargeScenery ? (1uLL << WIDX_LARGE_SCENERY) : 0) | (gClearFootpath ? (1uLL << WIDX_FOOTPATH) : 0); - - // Update the preview image (for tool sizes up to 7) - window_clear_scenery_widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - // Draw number for tool sizes bigger than 7 - ScreenCoordsXY screenCoords = { windowPos.x + window_clear_scenery_widgets[WIDX_PREVIEW].midX(), - windowPos.y + window_clear_scenery_widgets[WIDX_PREVIEW].midY() }; - if (gLandToolSize > kLandToolMaximumSizeWithSprite) - { - auto ft = Formatter(); - ft.Add(gLandToolSize); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + if (ClearSceneryToolIsActive()) + ToolCancel(); } - // Draw cost amount - if (gClearSceneryCost != kMoney64Undefined && gClearSceneryCost != 0 - && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + void OnMouseUp(const WidgetIndex widgetIndex) override { - auto ft = Formatter(); - ft.Add(gClearSceneryCost); - screenCoords.x = window_clear_scenery_widgets[WIDX_PREVIEW].midX() + windowPos.x; - screenCoords.y = window_clear_scenery_widgets[WIDX_PREVIEW].bottom + windowPos.y + 5 + 27; - DrawTextBasic(dpi, screenCoords, STR_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_PREVIEW: + { + Formatter ft; + ft.Add(kLandToolMinimumSize); + ft.Add(kLandToolMaximumSize); + TextInputOpen(WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); + break; + } + case WIDX_SMALL_SCENERY: + gClearSmallScenery ^= 1; + Invalidate(); + break; + case WIDX_LARGE_SCENERY: + gClearLargeScenery ^= 1; + Invalidate(); + break; + case WIDX_FOOTPATH: + gClearFootpath ^= 1; + Invalidate(); + break; + } } - } - void OnResize() override + void OnMouseDown(const WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_DECREMENT: + // Decrement land tool size, if it stays within the limit + gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); + + // Invalidate the window + Invalidate(); + break; + case WIDX_INCREMENT: + // Increment land tool size, if it stays within the limit + gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); + + // Invalidate the window + Invalidate(); + break; + } + } + + void OnTextInput(const WidgetIndex widgetIndex, const std::string_view text) override + { + if (widgetIndex != WIDX_PREVIEW || text.empty()) + return; + + try + { + int32_t size = std::stol(std::string(text)); + size = std::clamp(size, kLandToolMinimumSize, kLandToolMaximumSize); + gLandToolSize = size; + Invalidate(); + } + catch (const std::logic_error&) + { + // std::stol can throw std::out_of_range or std::invalid_argument + } + } + + void OnUpdate() override + { + frame_no++; + // Close window if another tool is open + if (!ClearSceneryToolIsActive()) + Close(); + } + + void Invalidate() + { + // Set the preview image button to be pressed down + pressed_widgets = (1uLL << WIDX_PREVIEW) | (gClearSmallScenery ? (1uLL << WIDX_SMALL_SCENERY) : 0) + | (gClearLargeScenery ? (1uLL << WIDX_LARGE_SCENERY) : 0) | (gClearFootpath ? (1uLL << WIDX_FOOTPATH) : 0); + + // Update the preview image (for tool sizes up to 7) + window_clear_scenery_widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + // Draw number for tool sizes bigger than 7 + ScreenCoordsXY screenCoords = { windowPos.x + window_clear_scenery_widgets[WIDX_PREVIEW].midX(), + windowPos.y + window_clear_scenery_widgets[WIDX_PREVIEW].midY() }; + if (gLandToolSize > kLandToolMaximumSizeWithSprite) + { + auto ft = Formatter(); + ft.Add(gLandToolSize); + DrawTextBasic( + dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + } + + // Draw cost amount + if (gClearSceneryCost != kMoney64Undefined && gClearSceneryCost != 0 + && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + auto ft = Formatter(); + ft.Add(gClearSceneryCost); + screenCoords.x = window_clear_scenery_widgets[WIDX_PREVIEW].midX() + windowPos.x; + screenCoords.y = window_clear_scenery_widgets[WIDX_PREVIEW].bottom + windowPos.y + 5 + 27; + DrawTextBasic(dpi, screenCoords, STR_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + } + } + + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowClearSceneryOpen() { - ResizeFrame(); + auto* w = static_cast(WindowBringToFrontByClass(WindowClass::ClearScenery)); + + if (w != nullptr) + return w; + + w = WindowCreate(WindowClass::ClearScenery, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); + + if (w != nullptr) + return w; + + return nullptr; } -}; - -WindowBase* WindowClearSceneryOpen() -{ - auto* w = static_cast(WindowBringToFrontByClass(WindowClass::ClearScenery)); - - if (w != nullptr) - return w; - - w = WindowCreate(WindowClass::ClearScenery, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); - - if (w != nullptr) - return w; - - return nullptr; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/CustomCurrency.cpp b/src/openrct2-ui/windows/CustomCurrency.cpp index a7a03f73a1..6f58f25074 100644 --- a/src/openrct2-ui/windows/CustomCurrency.cpp +++ b/src/openrct2-ui/windows/CustomCurrency.cpp @@ -18,11 +18,13 @@ #include #include -static constexpr StringId WINDOW_TITLE = STR_CUSTOM_CURRENCY_WINDOW_TITLE; -static constexpr int32_t WH = 100; -static constexpr int32_t WW = 400; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_CUSTOM_CURRENCY_WINDOW_TITLE; + static constexpr int32_t WH = 100; + static constexpr int32_t WW = 400; -// clang-format off + // clang-format off enum WindowCustomCurrencyWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -44,188 +46,190 @@ static Widget window_custom_currency_widgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class CustomCurrencyWindow final : public Window -{ -public: - void OnOpen() override + class CustomCurrencyWindow final : public Window { - widgets = window_custom_currency_widgets; - hold_down_widgets = (1uLL << WIDX_RATE_UP) | (1uLL << WIDX_RATE_DOWN); - WindowInitScrollWidgets(*this); - colours[0] = COLOUR_LIGHT_BROWN; - colours[1] = COLOUR_LIGHT_BROWN; - colours[2] = COLOUR_LIGHT_BROWN; - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - auto* widget = &widgets[widgetIndex - 1]; - - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_RATE_UP: - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate += 1; - gConfigGeneral.CustomCurrencyRate = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate; - ConfigSaveDefault(); - WindowInvalidateAll(); - break; - case WIDX_RATE_DOWN: - if (CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate > 1) - { - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate -= 1; + widgets = window_custom_currency_widgets; + hold_down_widgets = (1uLL << WIDX_RATE_UP) | (1uLL << WIDX_RATE_DOWN); + WindowInitScrollWidgets(*this); + colours[0] = COLOUR_LIGHT_BROWN; + colours[1] = COLOUR_LIGHT_BROWN; + colours[2] = COLOUR_LIGHT_BROWN; + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + auto* widget = &widgets[widgetIndex - 1]; + + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_RATE_UP: + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate += 1; gConfigGeneral.CustomCurrencyRate = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate; ConfigSaveDefault(); WindowInvalidateAll(); - } - break; - case WIDX_AFFIX_DROPDOWN_BUTTON: - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_PREFIX; + break; + case WIDX_RATE_DOWN: + if (CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate > 1) + { + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate -= 1; + gConfigGeneral.CustomCurrencyRate = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate; + ConfigSaveDefault(); + WindowInvalidateAll(); + } + break; + case WIDX_AFFIX_DROPDOWN_BUTTON: + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_PREFIX; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Args = STR_SUFFIX; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Args = STR_SUFFIX; - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, 2, widget->width() - 3); + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, 2, widget->width() - 3); - if (CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode == CurrencyAffix::Prefix) - { - Dropdown::SetChecked(0, true); - } - else - { - Dropdown::SetChecked(1, true); - } + if (CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode == CurrencyAffix::Prefix) + { + Dropdown::SetChecked(0, true); + } + else + { + Dropdown::SetChecked(1, true); + } - break; - case WIDX_SYMBOL_TEXT: - WindowTextInputRawOpen( - this, WIDX_SYMBOL_TEXT, STR_CUSTOM_CURRENCY_SYMBOL_INPUT_TITLE, STR_CUSTOM_CURRENCY_SYMBOL_INPUT_DESC, {}, - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode, CURRENCY_SYMBOL_MAX_SIZE); - break; - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_RATE: - WindowTextInputOpen( - this, WIDX_RATE, STR_RATE_INPUT_TITLE, STR_RATE_INPUT_DESC, {}, STR_FORMAT_INTEGER, - static_cast(CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate), - CURRENCY_RATE_MAX_NUM_DIGITS); - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (dropdownIndex == -1) - return; - - if (widgetIndex == WIDX_AFFIX_DROPDOWN_BUTTON) - { - if (dropdownIndex == 0) - { - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_ascii = CurrencyAffix::Prefix; - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode = CurrencyAffix::Prefix; + break; + case WIDX_SYMBOL_TEXT: + WindowTextInputRawOpen( + this, WIDX_SYMBOL_TEXT, STR_CUSTOM_CURRENCY_SYMBOL_INPUT_TITLE, STR_CUSTOM_CURRENCY_SYMBOL_INPUT_DESC, + {}, CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode, CURRENCY_SYMBOL_MAX_SIZE); + break; } - else if (dropdownIndex == 1) - { - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_ascii = CurrencyAffix::Suffix; - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode = CurrencyAffix::Suffix; - } - - gConfigGeneral.CustomCurrencyAffix = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode; - ConfigSaveDefault(); - - WindowInvalidateAll(); } - } - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (text.empty()) - return; - - int32_t rate; - - switch (widgetIndex) + void OnMouseUp(WidgetIndex widgetIndex) override { - case WIDX_SYMBOL_TEXT: - SafeStrCpy( - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode, std::string(text).c_str(), - CURRENCY_SYMBOL_MAX_SIZE); + switch (widgetIndex) + { + case WIDX_RATE: + WindowTextInputOpen( + this, WIDX_RATE, STR_RATE_INPUT_TITLE, STR_RATE_INPUT_DESC, {}, STR_FORMAT_INTEGER, + static_cast(CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate), + CURRENCY_RATE_MAX_NUM_DIGITS); + break; + } + } - gConfigGeneral.CustomCurrencySymbol = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode; + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (dropdownIndex == -1) + return; + if (widgetIndex == WIDX_AFFIX_DROPDOWN_BUTTON) + { + if (dropdownIndex == 0) + { + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_ascii = CurrencyAffix::Prefix; + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode = CurrencyAffix::Prefix; + } + else if (dropdownIndex == 1) + { + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_ascii = CurrencyAffix::Suffix; + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode = CurrencyAffix::Suffix; + } + + gConfigGeneral.CustomCurrencyAffix = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode; ConfigSaveDefault(); - WindowInvalidateAll(); - break; - case WIDX_RATE: - const auto res = String::Parse(text); - if (res.has_value()) - { - rate = res.value(); - CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate = rate; - gConfigGeneral.CustomCurrencyRate = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate; + WindowInvalidateAll(); + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (text.empty()) + return; + + int32_t rate; + + switch (widgetIndex) + { + case WIDX_SYMBOL_TEXT: + SafeStrCpy( + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode, std::string(text).c_str(), + CURRENCY_SYMBOL_MAX_SIZE); + + gConfigGeneral.CustomCurrencySymbol = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode; + ConfigSaveDefault(); WindowInvalidateAll(); - } - break; + break; + + case WIDX_RATE: + const auto res = String::Parse(text); + if (res.has_value()) + { + rate = res.value(); + CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate = rate; + gConfigGeneral.CustomCurrencyRate = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate; + ConfigSaveDefault(); + WindowInvalidateAll(); + } + break; + } } - } - void OnDraw(DrawPixelInfo& dpi) override + void OnDraw(DrawPixelInfo& dpi) override + { + auto ft = Formatter::Common(); + ft.Add(10.00_GBP); + + DrawWidgets(dpi); + + auto screenCoords = windowPos + ScreenCoordsXY{ 10, 30 }; + + DrawTextBasic(dpi, screenCoords, STR_RATE, {}, { colours[1] }); + + int32_t baseExchange = CurrencyDescriptors[EnumValue(CurrencyType::Pounds)].rate; + ft = Formatter(); + ft.Add(baseExchange); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 200, 0 }, STR_CUSTOM_CURRENCY_EQUIVALENCY, ft, { colours[1] }); + + screenCoords.y += 20; + + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_SYMBOL_TEXT, {}, { colours[1] }); + + screenCoords = windowPos + + ScreenCoordsXY{ window_custom_currency_widgets[WIDX_SYMBOL_TEXT].left + 1, + window_custom_currency_widgets[WIDX_SYMBOL_TEXT].top }; + + GfxDrawString( + dpi, screenCoords, CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode, { colours[1] }); + + auto drawPos = windowPos + + ScreenCoordsXY{ window_custom_currency_widgets[WIDX_AFFIX_DROPDOWN].left + 1, + window_custom_currency_widgets[WIDX_AFFIX_DROPDOWN].top }; + StringId stringId = (CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode == CurrencyAffix::Prefix) + ? STR_PREFIX + : STR_SUFFIX; + DrawTextBasic(dpi, drawPos, stringId, {}, { colours[1] }); + } + + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* CustomCurrencyWindowOpen() { - auto ft = Formatter::Common(); - ft.Add(10.00_GBP); - - DrawWidgets(dpi); - - auto screenCoords = windowPos + ScreenCoordsXY{ 10, 30 }; - - DrawTextBasic(dpi, screenCoords, STR_RATE, {}, { colours[1] }); - - int32_t baseExchange = CurrencyDescriptors[EnumValue(CurrencyType::Pounds)].rate; - ft = Formatter(); - ft.Add(baseExchange); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 200, 0 }, STR_CUSTOM_CURRENCY_EQUIVALENCY, ft, { colours[1] }); - - screenCoords.y += 20; - - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_SYMBOL_TEXT, {}, { colours[1] }); - - screenCoords = windowPos - + ScreenCoordsXY{ window_custom_currency_widgets[WIDX_SYMBOL_TEXT].left + 1, - window_custom_currency_widgets[WIDX_SYMBOL_TEXT].top }; - - GfxDrawString(dpi, screenCoords, CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode, { colours[1] }); - - auto drawPos = windowPos - + ScreenCoordsXY{ window_custom_currency_widgets[WIDX_AFFIX_DROPDOWN].left + 1, - window_custom_currency_widgets[WIDX_AFFIX_DROPDOWN].top }; - StringId stringId = (CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode == CurrencyAffix::Prefix) - ? STR_PREFIX - : STR_SUFFIX; - DrawTextBasic(dpi, drawPos, stringId, {}, { colours[1] }); + return WindowFocusOrCreate(WindowClass::CustomCurrencyConfig, WW, WH, WF_CENTRE_SCREEN); } - - void OnResize() override - { - ResizeFrame(); - } -}; - -WindowBase* CustomCurrencyWindowOpen() -{ - return WindowFocusOrCreate(WindowClass::CustomCurrencyConfig, WW, WH, WF_CENTRE_SCREEN); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/DebugPaint.cpp b/src/openrct2-ui/windows/DebugPaint.cpp index d7b6434325..383df00ee1 100644 --- a/src/openrct2-ui/windows/DebugPaint.cpp +++ b/src/openrct2-ui/windows/DebugPaint.cpp @@ -19,7 +19,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowDebugPaintWidgetIdx { WIDX_BACKGROUND, @@ -42,112 +44,114 @@ static Widget window_debug_paint_widgets[] = { MakeWidget({8, 8 + 15 * 4}, { 185, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_DEBUG_PAINT_SHOW_DIRTY_VISUALS ), kWidgetsEnd, }; -// clang-format on + // clang-format on -class DebugPaintWindow final : public Window -{ -private: - int32_t ResizeLanguage = LANGUAGE_UNDEFINED; - -public: - void OnOpen() override + class DebugPaintWindow final : public Window { - widgets = window_debug_paint_widgets; + private: + int32_t ResizeLanguage = LANGUAGE_UNDEFINED; - InitScrollWidgets(); - WindowPushOthersBelow(*this); - - colours[0] = TRANSLUCENT(COLOUR_BLACK); - colours[1] = COLOUR_GREY; - - ResizeLanguage = LANGUAGE_UNDEFINED; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_TOGGLE_SHOW_WIDE_PATHS: - gPaintWidePathsAsGhost = !gPaintWidePathsAsGhost; - GfxInvalidateScreen(); - break; + widgets = window_debug_paint_widgets; - case WIDX_TOGGLE_SHOW_BLOCKED_TILES: - gPaintBlockedTiles = !gPaintBlockedTiles; - GfxInvalidateScreen(); - break; + InitScrollWidgets(); + WindowPushOthersBelow(*this); - case WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS: - gShowSupportSegmentHeights = !gShowSupportSegmentHeights; - GfxInvalidateScreen(); - break; + colours[0] = TRANSLUCENT(COLOUR_BLACK); + colours[1] = COLOUR_GREY; - case WIDX_TOGGLE_SHOW_BOUND_BOXES: - gPaintBoundingBoxes = !gPaintBoundingBoxes; - GfxInvalidateScreen(); - break; - - case WIDX_TOGGLE_SHOW_DIRTY_VISUALS: - gShowDirtyVisuals = !gShowDirtyVisuals; - GfxInvalidateScreen(); - break; + ResizeLanguage = LANGUAGE_UNDEFINED; } - } - void OnPrepareDraw() override - { - const auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); - const auto currentLanguage = ls.GetCurrentLanguage(); - if (ResizeLanguage != currentLanguage) + void OnMouseUp(WidgetIndex widgetIndex) override { - ResizeLanguage = currentLanguage; - Invalidate(); - - // Find the width of the longest string - int16_t newWidth = 0; - for (size_t widgetIndex = WIDX_TOGGLE_SHOW_WIDE_PATHS; widgetIndex <= WIDX_TOGGLE_SHOW_DIRTY_VISUALS; widgetIndex++) + switch (widgetIndex) { - const auto& stringIdx = widgets[widgetIndex].text; - auto string = ls.GetString(stringIdx); - Guard::ArgumentNotNull(string); - const auto strWidth = GfxGetStringWidth(string, FontStyle::Medium); - newWidth = std::max(strWidth, newWidth); + case WIDX_TOGGLE_SHOW_WIDE_PATHS: + gPaintWidePathsAsGhost = !gPaintWidePathsAsGhost; + GfxInvalidateScreen(); + break; + + case WIDX_TOGGLE_SHOW_BLOCKED_TILES: + gPaintBlockedTiles = !gPaintBlockedTiles; + GfxInvalidateScreen(); + break; + + case WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS: + gShowSupportSegmentHeights = !gShowSupportSegmentHeights; + GfxInvalidateScreen(); + break; + + case WIDX_TOGGLE_SHOW_BOUND_BOXES: + gPaintBoundingBoxes = !gPaintBoundingBoxes; + GfxInvalidateScreen(); + break; + + case WIDX_TOGGLE_SHOW_DIRTY_VISUALS: + gShowDirtyVisuals = !gShowDirtyVisuals; + GfxInvalidateScreen(); + break; + } + } + + void OnPrepareDraw() override + { + const auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); + const auto currentLanguage = ls.GetCurrentLanguage(); + if (ResizeLanguage != currentLanguage) + { + ResizeLanguage = currentLanguage; + Invalidate(); + + // Find the width of the longest string + int16_t newWidth = 0; + for (size_t widgetIndex = WIDX_TOGGLE_SHOW_WIDE_PATHS; widgetIndex <= WIDX_TOGGLE_SHOW_DIRTY_VISUALS; + widgetIndex++) + { + const auto& stringIdx = widgets[widgetIndex].text; + auto string = ls.GetString(stringIdx); + Guard::ArgumentNotNull(string); + const auto strWidth = GfxGetStringWidth(string, FontStyle::Medium); + newWidth = std::max(strWidth, newWidth); + } + + // Add padding for both sides (8) and the offset for the text after the checkbox (15) + newWidth += 8 * 2 + 15; + + width = newWidth; + max_width = newWidth; + min_width = newWidth; + widgets[WIDX_BACKGROUND].right = newWidth - 1; + widgets[WIDX_TOGGLE_SHOW_WIDE_PATHS].right = newWidth - 8; + widgets[WIDX_TOGGLE_SHOW_BLOCKED_TILES].right = newWidth - 8; + widgets[WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS].right = newWidth - 8; + widgets[WIDX_TOGGLE_SHOW_BOUND_BOXES].right = newWidth - 8; + widgets[WIDX_TOGGLE_SHOW_DIRTY_VISUALS].right = newWidth - 8; + + Invalidate(); } - // Add padding for both sides (8) and the offset for the text after the checkbox (15) - newWidth += 8 * 2 + 15; - - width = newWidth; - max_width = newWidth; - min_width = newWidth; - widgets[WIDX_BACKGROUND].right = newWidth - 1; - widgets[WIDX_TOGGLE_SHOW_WIDE_PATHS].right = newWidth - 8; - widgets[WIDX_TOGGLE_SHOW_BLOCKED_TILES].right = newWidth - 8; - widgets[WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS].right = newWidth - 8; - widgets[WIDX_TOGGLE_SHOW_BOUND_BOXES].right = newWidth - 8; - widgets[WIDX_TOGGLE_SHOW_DIRTY_VISUALS].right = newWidth - 8; - - Invalidate(); + WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_WIDE_PATHS, gPaintWidePathsAsGhost); + WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_BLOCKED_TILES, gPaintBlockedTiles); + WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS, gShowSupportSegmentHeights); + WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_BOUND_BOXES, gPaintBoundingBoxes); + WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_DIRTY_VISUALS, gShowDirtyVisuals); } - WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_WIDE_PATHS, gPaintWidePathsAsGhost); - WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_BLOCKED_TILES, gPaintBlockedTiles); - WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS, gShowSupportSegmentHeights); - WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_BOUND_BOXES, gPaintBoundingBoxes); - WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_DIRTY_VISUALS, gShowDirtyVisuals); - } + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + } + }; - void OnDraw(DrawPixelInfo& dpi) override + WindowBase* WindowDebugPaintOpen() { - DrawWidgets(dpi); + auto* window = WindowFocusOrCreate( + WindowClass::DebugPaint, { 16, ContextGetHeight() - 16 - 33 - WINDOW_HEIGHT }, WINDOW_WIDTH, WINDOW_HEIGHT, + WF_STICK_TO_FRONT | WF_TRANSPARENT); + + return window; } -}; - -WindowBase* WindowDebugPaintOpen() -{ - auto* window = WindowFocusOrCreate( - WindowClass::DebugPaint, { 16, ContextGetHeight() - 16 - 33 - WINDOW_HEIGHT }, WINDOW_WIDTH, WINDOW_HEIGHT, - WF_STICK_TO_FRONT | WF_TRANSPARENT); - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/DemolishRidePrompt.cpp b/src/openrct2-ui/windows/DemolishRidePrompt.cpp index 9e47e3fde7..8d8dc2d0d1 100644 --- a/src/openrct2-ui/windows/DemolishRidePrompt.cpp +++ b/src/openrct2-ui/windows/DemolishRidePrompt.cpp @@ -19,12 +19,12 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr int32_t WW = 200; + static constexpr int32_t WH = 100; -static constexpr int32_t WW = 200; -static constexpr int32_t WH = 100; - -// clang-format off + // clang-format off enum WindowRideDemolishWidgetIdx { WIDX_BACKGROUND, @@ -41,85 +41,87 @@ static Widget window_ride_demolish_widgets[] = MakeWidget({WW - 95, WH - 22}, {85, 14}, WindowWidgetType::Button, WindowColour::Primary, STR_SAVE_PROMPT_CANCEL), kWidgetsEnd, }; -// clang-format on + // clang-format on -class DemolishRidePromptWindow final : public Window -{ - money64 _demolishRideCost; - -public: - void SetRide(const Ride& currentRide) + class DemolishRidePromptWindow final : public Window { - rideId = currentRide.id; - _demolishRideCost = -RideGetRefundPrice(currentRide); - } + money64 _demolishRideCost; - void OnOpen() override - { - widgets = window_ride_demolish_widgets; - WindowInitScrollWidgets(*this); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void SetRide(const Ride& currentRide) { - case WIDX_DEMOLISH: + rideId = currentRide.id; + _demolishRideCost = -RideGetRefundPrice(currentRide); + } + + void OnOpen() override + { + widgets = window_ride_demolish_widgets; + WindowInitScrollWidgets(*this); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) { - auto gameAction = RideDemolishAction(rideId, RIDE_MODIFY_DEMOLISH); - GameActions::Execute(&gameAction); - break; + case WIDX_DEMOLISH: + { + auto gameAction = RideDemolishAction(rideId, RIDE_MODIFY_DEMOLISH); + GameActions::Execute(&gameAction); + break; + } + case WIDX_CANCEL: + case WIDX_CLOSE: + Close(); + break; } - case WIDX_CANCEL: - case WIDX_CLOSE: - Close(); - break; } - } - void OnDraw(DrawPixelInfo& dpi) override - { - WindowDrawWidgets(*this, dpi); - - auto currentRide = GetRide(rideId); - if (currentRide != nullptr) + void OnDraw(DrawPixelInfo& dpi) override { - auto stringId = (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) ? STR_DEMOLISH_RIDE_ID - : STR_DEMOLISH_RIDE_ID_MONEY; - auto ft = Formatter(); - currentRide->FormatNameTo(ft); - ft.Add(_demolishRideCost); + WindowDrawWidgets(*this, dpi); - ScreenCoordsXY stringCoords(windowPos.x + WW / 2, windowPos.y + (WH / 2) - 3); - DrawTextWrapped(dpi, stringCoords, WW - 4, stringId, ft, { TextAlignment::CENTRE }); + auto currentRide = GetRide(rideId); + if (currentRide != nullptr) + { + auto stringId = (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) ? STR_DEMOLISH_RIDE_ID + : STR_DEMOLISH_RIDE_ID_MONEY; + auto ft = Formatter(); + currentRide->FormatNameTo(ft); + ft.Add(_demolishRideCost); + + ScreenCoordsXY stringCoords(windowPos.x + WW / 2, windowPos.y + (WH / 2) - 3); + DrawTextWrapped(dpi, stringCoords, WW - 4, stringId, ft, { TextAlignment::CENTRE }); + } } - } - void OnResize() override + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowRideDemolishPromptOpen(const Ride& ride) { - ResizeFrame(); + WindowBase* w; + DemolishRidePromptWindow* newWindow; + + w = WindowFindByClass(WindowClass::DemolishRidePrompt); + if (w != nullptr) + { + auto windowPos = w->windowPos; + WindowClose(*w); + newWindow = WindowCreate( + WindowClass::DemolishRidePrompt, windowPos, WW, WH, WF_TRANSPARENT); + } + else + { + newWindow = WindowCreate( + WindowClass::DemolishRidePrompt, WW, WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); + } + + newWindow->SetRide(ride); + + return newWindow; } -}; - -WindowBase* WindowRideDemolishPromptOpen(const Ride& ride) -{ - WindowBase* w; - DemolishRidePromptWindow* newWindow; - - w = WindowFindByClass(WindowClass::DemolishRidePrompt); - if (w != nullptr) - { - auto windowPos = w->windowPos; - WindowClose(*w); - newWindow = WindowCreate(WindowClass::DemolishRidePrompt, windowPos, WW, WH, WF_TRANSPARENT); - } - else - { - newWindow = WindowCreate( - WindowClass::DemolishRidePrompt, WW, WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); - } - - newWindow->SetRide(ride); - - return newWindow; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Dropdown.cpp b/src/openrct2-ui/windows/Dropdown.cpp index 7cf3e40a78..1d796f5390 100644 --- a/src/openrct2-ui/windows/Dropdown.cpp +++ b/src/openrct2-ui/windows/Dropdown.cpp @@ -22,431 +22,381 @@ #include #include -using namespace OpenRCT2; - -// The maximum number of rows to list before items overflow into new columns -constexpr int32_t DROPDOWN_TEXT_MAX_ROWS = 32; - -constexpr int32_t DROPDOWN_ITEM_HEIGHT = 12; - -static constexpr std::array _appropriateImageDropdownItemsPerRow = { - 1, 1, 1, 1, 2, 2, 3, 3, 4, 3, // 10 - 5, 4, 4, 5, 5, 5, 4, 5, 6, 5, // 20 - 5, 7, 4, 5, 6, 5, 6, 6, 6, 6, // 30 - 6, 8, 8, 8, 9, 9, 9, 9, 9, 9, // 40 - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 50 - 9, 9, 9, 9, 9, 9, 9, // 56 -}; - -enum +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, -}; + // The maximum number of rows to list before items overflow into new columns + constexpr int32_t DROPDOWN_TEXT_MAX_ROWS = 32; -static Widget window_dropdown_widgets[] = { - MakeWidget({ 0, 0 }, { 1, 1 }, WindowWidgetType::ImgBtn, WindowColour::Primary), - kWidgetsEnd, -}; + constexpr int32_t DROPDOWN_ITEM_HEIGHT = 12; -int32_t gDropdownNumItems; -Dropdown::Item gDropdownItems[Dropdown::ItemsMaxSize]; -static ImageId _dropdownItemsImages[Dropdown::ItemsMaxSize]; -bool gDropdownIsColour; -int32_t gDropdownLastColourHover; -int32_t gDropdownHighlightedIndex; -int32_t gDropdownDefaultIndex; -static bool _dropdownPrepareUseImages; + static constexpr std::array _appropriateImageDropdownItemsPerRow = { + 1, 1, 1, 1, 2, 2, 3, 3, 4, 3, // 10 + 5, 4, 4, 5, 5, 5, 4, 5, 6, 5, // 20 + 5, 7, 4, 5, 6, 5, 6, 6, 6, 6, // 30 + 6, 8, 8, 8, 9, 9, 9, 9, 9, 9, // 40 + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 50 + 9, 9, 9, 9, 9, 9, 9, // 56 + }; -static void ResetDropdownFlags() -{ - for (size_t i = 0; i < std::size(gDropdownItems); i++) + enum { - gDropdownItems[i].Flags = 0; - } -} + WIDX_BACKGROUND, + }; -bool Dropdown::IsChecked(int32_t index) -{ - if (index < 0 || index >= static_cast(std::size(gDropdownItems))) + static Widget window_dropdown_widgets[] = { + MakeWidget({ 0, 0 }, { 1, 1 }, WindowWidgetType::ImgBtn, WindowColour::Primary), + kWidgetsEnd, + }; + + int32_t gDropdownNumItems; + Dropdown::Item gDropdownItems[Dropdown::ItemsMaxSize]; + static ImageId _dropdownItemsImages[Dropdown::ItemsMaxSize]; + bool gDropdownIsColour; + int32_t gDropdownLastColourHover; + int32_t gDropdownHighlightedIndex; + int32_t gDropdownDefaultIndex; + static bool _dropdownPrepareUseImages; + + static void ResetDropdownFlags() { - return false; - } - return gDropdownItems[index].IsChecked(); -} - -bool Dropdown::IsDisabled(int32_t index) -{ - if (index < 0 || index >= static_cast(std::size(gDropdownItems))) - { - return true; - } - return gDropdownItems[index].IsDisabled(); -} - -void Dropdown::SetChecked(int32_t index, bool value) -{ - if (index < 0 || index >= static_cast(std::size(gDropdownItems))) - { - return; - } - if (value) - gDropdownItems[index].Flags |= EnumValue(Dropdown::ItemFlag::IsChecked); - else - gDropdownItems[index].Flags &= ~EnumValue(Dropdown::ItemFlag::IsChecked); -} - -void Dropdown::SetDisabled(int32_t index, bool value) -{ - if (index < 0 || index >= static_cast(std::size(gDropdownItems))) - { - return; - } - if (value) - gDropdownItems[index].Flags |= EnumValue(Dropdown::ItemFlag::IsDisabled); - else - gDropdownItems[index].Flags &= ~EnumValue(Dropdown::ItemFlag::IsDisabled); -} - -void Dropdown::SetImage(int32_t index, ImageId image) -{ - if (index < 0 || index >= static_cast(std::size(_dropdownItemsImages))) - { - return; - } - _dropdownItemsImages[index] = image; - _dropdownPrepareUseImages = true; -} - -class DropdownWindow final : public Window -{ - bool UseImages; - int32_t NumColumns; - int32_t NumRows; - int32_t ItemWidth; - int32_t ItemHeight; - bool ListVertically; - -public: - void OnOpen() override - { - widgets = window_dropdown_widgets; - - // Input state - gDropdownHighlightedIndex = -1; - ResetDropdownFlags(); - gDropdownIsColour = false; - gDropdownDefaultIndex = -1; - InputSetState(InputState::DropdownActive); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - int32_t highlightedIndex = gDropdownHighlightedIndex; - for (int32_t i = 0; i < gDropdownNumItems; i++) + for (size_t i = 0; i < std::size(gDropdownItems); i++) { - ScreenCoordsXY cellCoords; - if (ListVertically) - cellCoords = { i / NumRows, i % NumRows }; - else - cellCoords = { i % NumColumns, i / NumColumns }; + gDropdownItems[i].Flags = 0; + } + } - ScreenCoordsXY screenCoords = windowPos - + ScreenCoordsXY{ 2 + (cellCoords.x * ItemWidth), 2 + (cellCoords.y * ItemHeight) }; + class DropdownWindow final : public Window + { + bool UseImages; + int32_t NumColumns; + int32_t NumRows; + int32_t ItemWidth; + int32_t ItemHeight; + bool ListVertically; - if (gDropdownItems[i].IsSeparator()) + public: + void OnOpen() override + { + widgets = window_dropdown_widgets; + + // Input state + gDropdownHighlightedIndex = -1; + ResetDropdownFlags(); + gDropdownIsColour = false; + gDropdownDefaultIndex = -1; + InputSetState(InputState::DropdownActive); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + int32_t highlightedIndex = gDropdownHighlightedIndex; + for (int32_t i = 0; i < gDropdownNumItems; i++) { - const ScreenCoordsXY leftTop = screenCoords + ScreenCoordsXY{ 0, (ItemHeight / 2) }; - const ScreenCoordsXY rightBottom = leftTop + ScreenCoordsXY{ ItemWidth - 1, 0 }; - const ScreenCoordsXY shadowOffset{ 0, 1 }; + ScreenCoordsXY cellCoords; + if (ListVertically) + cellCoords = { i / NumRows, i % NumRows }; + else + cellCoords = { i % NumColumns, i / NumColumns }; - if (colours[0] & COLOUR_FLAG_TRANSLUCENT) + ScreenCoordsXY screenCoords = windowPos + + ScreenCoordsXY{ 2 + (cellCoords.x * ItemWidth), 2 + (cellCoords.y * ItemHeight) }; + + if (gDropdownItems[i].IsSeparator()) { - TranslucentWindowPalette palette = TranslucentWindowPalettes[BASE_COLOUR(colours[0])]; - GfxFilterRect(dpi, { leftTop, rightBottom }, palette.highlight); - GfxFilterRect(dpi, { leftTop + shadowOffset, rightBottom + shadowOffset }, palette.shadow); + const ScreenCoordsXY leftTop = screenCoords + ScreenCoordsXY{ 0, (ItemHeight / 2) }; + const ScreenCoordsXY rightBottom = leftTop + ScreenCoordsXY{ ItemWidth - 1, 0 }; + const ScreenCoordsXY shadowOffset{ 0, 1 }; + + if (colours[0] & COLOUR_FLAG_TRANSLUCENT) + { + TranslucentWindowPalette palette = TranslucentWindowPalettes[BASE_COLOUR(colours[0])]; + GfxFilterRect(dpi, { leftTop, rightBottom }, palette.highlight); + GfxFilterRect(dpi, { leftTop + shadowOffset, rightBottom + shadowOffset }, palette.shadow); + } + else + { + GfxFillRect(dpi, { leftTop, rightBottom }, ColourMapA[colours[0]].mid_dark); + GfxFillRect( + dpi, { leftTop + shadowOffset, rightBottom + shadowOffset }, ColourMapA[colours[0]].lightest); + } } else { - GfxFillRect(dpi, { leftTop, rightBottom }, ColourMapA[colours[0]].mid_dark); - GfxFillRect(dpi, { leftTop + shadowOffset, rightBottom + shadowOffset }, ColourMapA[colours[0]].lightest); - } - } - else - { - if (i == highlightedIndex) - { - // Darken the cell's background slightly when highlighted - const ScreenCoordsXY rightBottom = screenCoords + ScreenCoordsXY{ ItemWidth - 1, ItemHeight - 1 }; - GfxFilterRect(dpi, { screenCoords, rightBottom }, FilterPaletteID::PaletteDarken3); - } - - StringId item = gDropdownItems[i].Format; - if (item == Dropdown::FormatLandPicker || item == Dropdown::FormatColourPicker) - { - // Image item - auto image = UseImages ? _dropdownItemsImages[i] - : ImageId::FromUInt32(static_cast(gDropdownItems[i].Args)); - if (item == Dropdown::FormatColourPicker && highlightedIndex == i) - image = image.WithIndexOffset(1); - GfxDrawSprite(dpi, image, screenCoords); - } - else - { - // Text item - if (i < Dropdown::ItemsMaxSize && Dropdown::IsChecked(i)) - item++; - - // Calculate colour - colour_t colour = NOT_TRANSLUCENT(colours[0]); if (i == highlightedIndex) - colour = COLOUR_WHITE; - if (i < Dropdown::ItemsMaxSize && Dropdown::IsDisabled(i)) - colour = NOT_TRANSLUCENT(colours[0]) | COLOUR_FLAG_INSET; + { + // Darken the cell's background slightly when highlighted + const ScreenCoordsXY rightBottom = screenCoords + ScreenCoordsXY{ ItemWidth - 1, ItemHeight - 1 }; + GfxFilterRect(dpi, { screenCoords, rightBottom }, FilterPaletteID::PaletteDarken3); + } - // Draw item string - Formatter ft(reinterpret_cast(&gDropdownItems[i].Args)); - DrawTextEllipsised(dpi, screenCoords, width - 5, item, ft, { colour }); + StringId item = gDropdownItems[i].Format; + if (item == Dropdown::FormatLandPicker || item == Dropdown::FormatColourPicker) + { + // Image item + auto image = UseImages ? _dropdownItemsImages[i] + : ImageId::FromUInt32(static_cast(gDropdownItems[i].Args)); + if (item == Dropdown::FormatColourPicker && highlightedIndex == i) + image = image.WithIndexOffset(1); + GfxDrawSprite(dpi, image, screenCoords); + } + else + { + // Text item + if (i < Dropdown::ItemsMaxSize && Dropdown::IsChecked(i)) + item++; + + // Calculate colour + colour_t colour = NOT_TRANSLUCENT(colours[0]); + if (i == highlightedIndex) + colour = COLOUR_WHITE; + if (i < Dropdown::ItemsMaxSize && Dropdown::IsDisabled(i)) + colour = NOT_TRANSLUCENT(colours[0]) | COLOUR_FLAG_INSET; + + // Draw item string + Formatter ft(reinterpret_cast(&gDropdownItems[i].Args)); + DrawTextEllipsised(dpi, screenCoords, width - 5, item, ft, { colour }); + } } } } - } - void SetTextItems( - const ScreenCoordsXY& screenPos, int32_t extraY, uint8_t colour, uint8_t customHeight, uint8_t txtFlags, - size_t numItems, int32_t itemWidth) + void SetTextItems( + const ScreenCoordsXY& screenPos, int32_t extraY, uint8_t colour, uint8_t customHeight, uint8_t txtFlags, + size_t numItems, int32_t itemWidth) + { + // Set and calculate num items, rows and columns + ItemWidth = itemWidth; + ItemHeight = (txtFlags & Dropdown::Flag::CustomHeight) ? customHeight : DROPDOWN_ITEM_HEIGHT; + gDropdownNumItems = static_cast(numItems); + // There must always be at least one column to prevent dividing by zero + if (gDropdownNumItems == 0) + { + NumColumns = 1; + NumRows = 1; + } + else + { + NumColumns = (gDropdownNumItems + DROPDOWN_TEXT_MAX_ROWS - 1) / DROPDOWN_TEXT_MAX_ROWS; + NumRows = (gDropdownNumItems + NumColumns - 1) / NumColumns; + } + + // Text dropdowns are listed horizontally + ListVertically = true; + + UpdateSizeAndPosition(screenPos, extraY); + + if (colour & COLOUR_FLAG_TRANSLUCENT) + flags |= WF_TRANSPARENT; + colours[0] = colour; + } + + void SetImageItems( + const ScreenCoordsXY& screenPos, int32_t extraY, uint8_t colour, int32_t numItems, int32_t itemWidth, + int32_t itemHeight, int32_t numColumns) + { + UseImages = _dropdownPrepareUseImages; + _dropdownPrepareUseImages = false; + + // Set and calculate num items, rows and columns + ItemWidth = itemWidth; + ItemHeight = itemHeight; + gDropdownNumItems = numItems; + // There must always be at least one column and row to prevent dividing by zero + if (gDropdownNumItems == 0) + { + NumColumns = 1; + NumRows = 1; + } + else + { + NumColumns = std::max(1, numColumns); + NumRows = gDropdownNumItems / NumColumns; + if (gDropdownNumItems % NumColumns != 0) + NumRows++; + } + + // image dropdowns are listed horizontally + ListVertically = false; + + UpdateSizeAndPosition(screenPos, extraY); + + if (colour & COLOUR_FLAG_TRANSLUCENT) + flags |= WF_TRANSPARENT; + colours[0] = colour; + } + + int32_t GetIndexFromPoint(const ScreenCoordsXY& loc) + { + int32_t top = loc.y - windowPos.y - 2; + if (top < 0) + return -1; + + int32_t left = loc.x - windowPos.x; + if (left >= width) + return -1; + left -= 2; + if (left < 0) + return -1; + + int32_t columnNum = left / ItemWidth; + if (columnNum >= NumColumns) + return -1; + + int32_t rowNum = top / ItemHeight; + if (rowNum >= NumRows) + return -1; + + int32_t dropdownIndex; + if (ListVertically) + dropdownIndex = columnNum * NumRows + rowNum; + else + dropdownIndex = rowNum * NumColumns + columnNum; + + if (dropdownIndex >= gDropdownNumItems) + return -1; + + return dropdownIndex; + } + + private: + void UpdateSizeAndPosition(const ScreenCoordsXY& screenPos, const int32_t extraY) + { + // Calculate position and size + const auto ddWidth = ItemWidth * NumColumns + 3; + const auto ddHeight = ItemHeight * NumRows + 3; + + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + auto boundedScreenPos = screenPos; + if (screenPos.x + ddWidth > screenWidth) + boundedScreenPos.x = std::max(0, screenWidth - ddWidth); + if (screenPos.y + ddHeight > screenHeight) + boundedScreenPos.y = std::max(0, screenHeight - ddHeight); + window_dropdown_widgets[WIDX_BACKGROUND].right = ddWidth; + window_dropdown_widgets[WIDX_BACKGROUND].bottom = ddHeight; + + Invalidate(); + width = ddWidth + 1; + height = ddHeight + 1; + windowPos = boundedScreenPos + ScreenCoordsXY{ 0, extraY }; + Invalidate(); + } + }; + + /** + * Shows a text dropdown menu. + * rct2: 0x006ECFB9 + * + * @param x (cx) + * @param y (dx) + * @param extray (di) + * @param flags (bh) + * @param num_items (bx) + * @param colour (al) + */ + void WindowDropdownShowText( + const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t flags, size_t num_items) { - // Set and calculate num items, rows and columns - ItemWidth = itemWidth; - ItemHeight = (txtFlags & Dropdown::Flag::CustomHeight) ? customHeight : DROPDOWN_ITEM_HEIGHT; - gDropdownNumItems = static_cast(numItems); - // There must always be at least one column to prevent dividing by zero - if (gDropdownNumItems == 0) + int32_t string_width, max_string_width; + char buffer[256]; + + // Calculate the longest string width + max_string_width = 0; + for (size_t i = 0; i < num_items; i++) { - NumColumns = 1; - NumRows = 1; - } - else - { - NumColumns = (gDropdownNumItems + DROPDOWN_TEXT_MAX_ROWS - 1) / DROPDOWN_TEXT_MAX_ROWS; - NumRows = (gDropdownNumItems + NumColumns - 1) / NumColumns; + FormatStringLegacy(buffer, 256, gDropdownItems[i].Format, static_cast(&gDropdownItems[i].Args)); + string_width = GfxGetStringWidth(buffer, FontStyle::Medium); + max_string_width = std::max(string_width, max_string_width); } - // Text dropdowns are listed horizontally - ListVertically = true; - - UpdateSizeAndPosition(screenPos, extraY); - - if (colour & COLOUR_FLAG_TRANSLUCENT) - flags |= WF_TRANSPARENT; - colours[0] = colour; + WindowDropdownShowTextCustomWidth(screenPos, extray, colour, 0, flags, num_items, max_string_width + 3); } - void SetImageItems( - const ScreenCoordsXY& screenPos, int32_t extraY, uint8_t colour, int32_t numItems, int32_t itemWidth, + /** + * Shows a text dropdown menu. + * rct2: 0x006ECFB9, although 0x006ECE50 is real version + * + * @param x (cx) + * @param y (dx) + * @param extray (di) + * @param flags (bh) + * @param num_items (bx) + * @param colour (al) + * @param custom_height (ah) requires flag set as well + */ + void WindowDropdownShowTextCustomWidth( + const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t custom_height, uint8_t flags, size_t num_items, + int32_t width) + { + InputSetFlag(static_cast(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP), false); + if (flags & Dropdown::Flag::StayOpen) + InputSetFlag(INPUT_FLAG_DROPDOWN_STAY_OPEN, true); + + WindowDropdownClose(); + + // Create the window (width/height position are set later) + auto* w = WindowCreate(WindowClass::Dropdown, width, custom_height, WF_STICK_TO_FRONT); + if (w != nullptr) + { + w->SetTextItems(screenPos, extray, colour, custom_height, flags, num_items, width); + } + } + + /** + * Shows an image dropdown menu. + * rct2: 0x006ECFB9 + * + * @param x (cx) + * @param y (dx) + * @param extray (di) + * @param flags (bh) + * @param numItems (bx) + * @param colour (al) + * @param itemWidth (bp) + * @param itemHeight (ah) + * @param numColumns (bl) + */ + void WindowDropdownShowImage( + int32_t x, int32_t y, int32_t extray, uint8_t colour, uint8_t flags, int32_t numItems, int32_t itemWidth, int32_t itemHeight, int32_t numColumns) { - UseImages = _dropdownPrepareUseImages; - _dropdownPrepareUseImages = false; + InputSetFlag(static_cast(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP), false); + if (flags & Dropdown::Flag::StayOpen) + InputSetFlag(INPUT_FLAG_DROPDOWN_STAY_OPEN, true); - // Set and calculate num items, rows and columns - ItemWidth = itemWidth; - ItemHeight = itemHeight; - gDropdownNumItems = numItems; - // There must always be at least one column and row to prevent dividing by zero - if (gDropdownNumItems == 0) + // Close existing dropdown + WindowDropdownClose(); + + // Create the window (width/height position are set later) + auto* w = WindowCreate(WindowClass::Dropdown, itemWidth, itemHeight, WF_STICK_TO_FRONT); + if (w != nullptr) { - NumColumns = 1; - NumRows = 1; + w->SetImageItems({ x, y }, extray, colour, numItems, itemWidth, itemHeight, numColumns); } - else + } + + void WindowDropdownClose() + { + WindowCloseByClass(WindowClass::Dropdown); + } + + /** + * New function based on 6e914e + * returns -1 if index is invalid + */ + int32_t DropdownIndexFromPoint(const ScreenCoordsXY& loc, WindowBase* w) + { + if (w->classification == WindowClass::Dropdown) { - NumColumns = std::max(1, numColumns); - NumRows = gDropdownNumItems / NumColumns; - if (gDropdownNumItems % NumColumns != 0) - NumRows++; + auto* ddWnd = static_cast(w); + return ddWnd->GetIndexFromPoint(loc); } - - // image dropdowns are listed horizontally - ListVertically = false; - - UpdateSizeAndPosition(screenPos, extraY); - - if (colour & COLOUR_FLAG_TRANSLUCENT) - flags |= WF_TRANSPARENT; - colours[0] = colour; + return -1; } - int32_t GetIndexFromPoint(const ScreenCoordsXY& loc) - { - int32_t top = loc.y - windowPos.y - 2; - if (top < 0) - return -1; - - int32_t left = loc.x - windowPos.x; - if (left >= width) - return -1; - left -= 2; - if (left < 0) - return -1; - - int32_t columnNum = left / ItemWidth; - if (columnNum >= NumColumns) - return -1; - - int32_t rowNum = top / ItemHeight; - if (rowNum >= NumRows) - return -1; - - int32_t dropdownIndex; - if (ListVertically) - dropdownIndex = columnNum * NumRows + rowNum; - else - dropdownIndex = rowNum * NumColumns + columnNum; - - if (dropdownIndex >= gDropdownNumItems) - return -1; - - return dropdownIndex; - } - -private: - void UpdateSizeAndPosition(const ScreenCoordsXY& screenPos, const int32_t extraY) - { - // Calculate position and size - const auto ddWidth = ItemWidth * NumColumns + 3; - const auto ddHeight = ItemHeight * NumRows + 3; - - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - auto boundedScreenPos = screenPos; - if (screenPos.x + ddWidth > screenWidth) - boundedScreenPos.x = std::max(0, screenWidth - ddWidth); - if (screenPos.y + ddHeight > screenHeight) - boundedScreenPos.y = std::max(0, screenHeight - ddHeight); - window_dropdown_widgets[WIDX_BACKGROUND].right = ddWidth; - window_dropdown_widgets[WIDX_BACKGROUND].bottom = ddHeight; - - Invalidate(); - width = ddWidth + 1; - height = ddHeight + 1; - windowPos = boundedScreenPos + ScreenCoordsXY{ 0, extraY }; - Invalidate(); - } -}; - -/** - * Shows a text dropdown menu. - * rct2: 0x006ECFB9 - * - * @param x (cx) - * @param y (dx) - * @param extray (di) - * @param flags (bh) - * @param num_items (bx) - * @param colour (al) - */ -void WindowDropdownShowText(const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t flags, size_t num_items) -{ - int32_t string_width, max_string_width; - char buffer[256]; - - // Calculate the longest string width - max_string_width = 0; - for (size_t i = 0; i < num_items; i++) - { - FormatStringLegacy(buffer, 256, gDropdownItems[i].Format, static_cast(&gDropdownItems[i].Args)); - string_width = GfxGetStringWidth(buffer, FontStyle::Medium); - max_string_width = std::max(string_width, max_string_width); - } - - WindowDropdownShowTextCustomWidth(screenPos, extray, colour, 0, flags, num_items, max_string_width + 3); -} - -/** - * Shows a text dropdown menu. - * rct2: 0x006ECFB9, although 0x006ECE50 is real version - * - * @param x (cx) - * @param y (dx) - * @param extray (di) - * @param flags (bh) - * @param num_items (bx) - * @param colour (al) - * @param custom_height (ah) requires flag set as well - */ -void WindowDropdownShowTextCustomWidth( - const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t custom_height, uint8_t flags, size_t num_items, - int32_t width) -{ - InputSetFlag(static_cast(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP), false); - if (flags & Dropdown::Flag::StayOpen) - InputSetFlag(INPUT_FLAG_DROPDOWN_STAY_OPEN, true); - - WindowDropdownClose(); - - // Create the window (width/height position are set later) - auto* w = WindowCreate(WindowClass::Dropdown, width, custom_height, WF_STICK_TO_FRONT); - if (w != nullptr) - { - w->SetTextItems(screenPos, extray, colour, custom_height, flags, num_items, width); - } -} - -/** - * Shows an image dropdown menu. - * rct2: 0x006ECFB9 - * - * @param x (cx) - * @param y (dx) - * @param extray (di) - * @param flags (bh) - * @param numItems (bx) - * @param colour (al) - * @param itemWidth (bp) - * @param itemHeight (ah) - * @param numColumns (bl) - */ -void WindowDropdownShowImage( - int32_t x, int32_t y, int32_t extray, uint8_t colour, uint8_t flags, int32_t numItems, int32_t itemWidth, - int32_t itemHeight, int32_t numColumns) -{ - InputSetFlag(static_cast(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP), false); - if (flags & Dropdown::Flag::StayOpen) - InputSetFlag(INPUT_FLAG_DROPDOWN_STAY_OPEN, true); - - // Close existing dropdown - WindowDropdownClose(); - - // Create the window (width/height position are set later) - auto* w = WindowCreate(WindowClass::Dropdown, itemWidth, itemHeight, WF_STICK_TO_FRONT); - if (w != nullptr) - { - w->SetImageItems({ x, y }, extray, colour, numItems, itemWidth, itemHeight, numColumns); - } -} - -void WindowDropdownClose() -{ - WindowCloseByClass(WindowClass::Dropdown); -} - -/** - * New function based on 6e914e - * returns -1 if index is invalid - */ -int32_t DropdownIndexFromPoint(const ScreenCoordsXY& loc, WindowBase* w) -{ - if (w->classification == WindowClass::Dropdown) - { - auto* ddWnd = static_cast(w); - return ddWnd->GetIndexFromPoint(loc); - } - return -1; -} - -// clang-format off + // clang-format off // colour_t ordered for use in color dropdown static constexpr colour_t kColoursDropdownOrder[] = { COLOUR_BLACK, @@ -512,56 +462,111 @@ static constexpr colour_t kColoursDropdownOrder[] = { COLOUR_INVISIBLE, COLOUR_VOID }; -// clang-format on + // clang-format on -colour_t ColourDropDownIndexToColour(uint8_t ddidx) -{ - return kColoursDropdownOrder[ddidx]; -} - -/** - * rct2: 0x006ED43D - */ -void WindowDropdownShowColour(WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour) -{ - int32_t defaultIndex = -1; - - auto numColours = (GetGameState().Cheats.AllowSpecialColourSchemes) ? static_cast(COLOUR_COUNT) - : COLOUR_NUM_NORMAL; - // Set items - for (uint64_t i = 0; i < numColours; i++) + colour_t ColourDropDownIndexToColour(uint8_t ddidx) { - auto orderedColour = ColourDropDownIndexToColour(i); - if (selectedColour == orderedColour) - defaultIndex = i; - - // Use special graphic for Invisible colour - auto imageId = (orderedColour == COLOUR_INVISIBLE) ? ImageId(SPR_G2_ICON_PALETTE_INVISIBLE, COLOUR_WHITE) - : ImageId(SPR_PALETTE_BTN, orderedColour); - - gDropdownItems[i].Format = Dropdown::FormatColourPicker; - gDropdownItems[i].Args = (i << 32) | imageId.ToUInt32(); + return kColoursDropdownOrder[ddidx]; } - // Show dropdown - WindowDropdownShowImage( - w->windowPos.x + widget->left, w->windowPos.y + widget->top, widget->height() + 1, dropdownColour, - Dropdown::Flag::StayOpen, numColours, 12, 12, - DropdownGetAppropriateImageDropdownItemsPerRow(static_cast(numColours))); + /** + * rct2: 0x006ED43D + */ + void WindowDropdownShowColour(WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour) + { + int32_t defaultIndex = -1; - gDropdownIsColour = true; - gDropdownLastColourHover = -1; - gDropdownDefaultIndex = defaultIndex; -} + auto numColours = (GetGameState().Cheats.AllowSpecialColourSchemes) ? static_cast(COLOUR_COUNT) + : COLOUR_NUM_NORMAL; + // Set items + for (uint64_t i = 0; i < numColours; i++) + { + auto orderedColour = ColourDropDownIndexToColour(i); + if (selectedColour == orderedColour) + defaultIndex = i; -uint32_t DropdownGetAppropriateImageDropdownItemsPerRow(uint32_t numItems) + // Use special graphic for Invisible colour + auto imageId = (orderedColour == COLOUR_INVISIBLE) ? ImageId(SPR_G2_ICON_PALETTE_INVISIBLE, COLOUR_WHITE) + : ImageId(SPR_PALETTE_BTN, orderedColour); + + gDropdownItems[i].Format = Dropdown::FormatColourPicker; + gDropdownItems[i].Args = (i << 32) | imageId.ToUInt32(); + } + + // Show dropdown + WindowDropdownShowImage( + w->windowPos.x + widget->left, w->windowPos.y + widget->top, widget->height() + 1, dropdownColour, + Dropdown::Flag::StayOpen, numColours, 12, 12, + DropdownGetAppropriateImageDropdownItemsPerRow(static_cast(numColours))); + + gDropdownIsColour = true; + gDropdownLastColourHover = -1; + gDropdownDefaultIndex = defaultIndex; + } + + uint32_t DropdownGetAppropriateImageDropdownItemsPerRow(uint32_t numItems) + { + // If above the table size return the last element + return _appropriateImageDropdownItemsPerRow[std::min( + numItems, static_cast(std::size(_appropriateImageDropdownItemsPerRow) - 1))]; + } + + bool WindowDropDownHasMultipleColumns(size_t numItems) + { + return numItems > DROPDOWN_TEXT_MAX_ROWS; + } +} // namespace OpenRCT2::Ui::Windows + +using namespace OpenRCT2::Ui::Windows; + +bool Dropdown::IsChecked(int32_t index) { - // If above the table size return the last element - return _appropriateImageDropdownItemsPerRow[std::min( - numItems, static_cast(std::size(_appropriateImageDropdownItemsPerRow) - 1))]; + if (index < 0 || index >= static_cast(std::size(gDropdownItems))) + { + return false; + } + return gDropdownItems[index].IsChecked(); } -bool WindowDropDownHasMultipleColumns(size_t numItems) +bool Dropdown::IsDisabled(int32_t index) { - return numItems > DROPDOWN_TEXT_MAX_ROWS; + if (index < 0 || index >= static_cast(std::size(gDropdownItems))) + { + return true; + } + return gDropdownItems[index].IsDisabled(); +} + +void Dropdown::SetChecked(int32_t index, bool value) +{ + if (index < 0 || index >= static_cast(std::size(gDropdownItems))) + { + return; + } + if (value) + gDropdownItems[index].Flags |= EnumValue(Dropdown::ItemFlag::IsChecked); + else + gDropdownItems[index].Flags &= ~EnumValue(Dropdown::ItemFlag::IsChecked); +} + +void Dropdown::SetDisabled(int32_t index, bool value) +{ + if (index < 0 || index >= static_cast(std::size(gDropdownItems))) + { + return; + } + if (value) + gDropdownItems[index].Flags |= EnumValue(Dropdown::ItemFlag::IsDisabled); + else + gDropdownItems[index].Flags &= ~EnumValue(Dropdown::ItemFlag::IsDisabled); +} + +void Dropdown::SetImage(int32_t index, ImageId image) +{ + if (index < 0 || index >= static_cast(std::size(_dropdownItemsImages))) + { + return; + } + _dropdownItemsImages[index] = image; + _dropdownPrepareUseImages = true; } diff --git a/src/openrct2-ui/windows/EditorBottomToolbar.cpp b/src/openrct2-ui/windows/EditorBottomToolbar.cpp index 99c30208f5..238ed308e8 100644 --- a/src/openrct2-ui/windows/EditorBottomToolbar.cpp +++ b/src/openrct2-ui/windows/EditorBottomToolbar.cpp @@ -28,9 +28,9 @@ #include #include -using namespace OpenRCT2; - -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WIDX_PREVIOUS_IMAGE, // 1 WIDX_PREVIOUS_STEP_BUTTON, // 2 @@ -45,363 +45,366 @@ static Widget _editorBottomToolbarWidgets[] = { MakeWidget({442, 2}, {196, 30}, WindowWidgetType::FlatBtn, WindowColour::Primary), kWidgetsEnd, }; -// clang-format on + // clang-format on -class EditorBottomToolbarWindow final : public Window -{ -private: - using FuncPtr = void (EditorBottomToolbarWindow::*)() const; - - static constexpr StringId _editorStepNames[] = { - STR_EDITOR_STEP_OBJECT_SELECTION, STR_EDITOR_STEP_LANDSCAPE_EDITOR, STR_EDITOR_STEP_INVENTIONS_LIST_SET_UP, - STR_EDITOR_STEP_OPTIONS_SELECTION, STR_EDITOR_STEP_OBJECTIVE_SELECTION, STR_EDITOR_STEP_SAVE_SCENARIO, - STR_EDITOR_STEP_ROLLERCOASTER_DESIGNER, STR_EDITOR_STEP_TRACK_DESIGNS_MANAGER, - }; - -public: - void OnOpen() override + class EditorBottomToolbarWindow final : public Window { - widgets = _editorBottomToolbarWidgets; + private: + using FuncPtr = void (EditorBottomToolbarWindow::*)() const; - InitScrollWidgets(); - SetAllSceneryItemsInvented(); - } + static constexpr StringId _editorStepNames[] = { + STR_EDITOR_STEP_OBJECT_SELECTION, STR_EDITOR_STEP_LANDSCAPE_EDITOR, + STR_EDITOR_STEP_INVENTIONS_LIST_SET_UP, STR_EDITOR_STEP_OPTIONS_SELECTION, + STR_EDITOR_STEP_OBJECTIVE_SELECTION, STR_EDITOR_STEP_SAVE_SCENARIO, + STR_EDITOR_STEP_ROLLERCOASTER_DESIGNER, STR_EDITOR_STEP_TRACK_DESIGNS_MANAGER, + }; - void OnPrepareDraw() override - { - ColourSchemeUpdateByClass( - this, - (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowClass::EditorScenarioBottomToolbar - : WindowClass::EditorTrackBottomToolbar); - - uint16_t screenWidth = ContextGetWidth(); - widgets[WIDX_NEXT_IMAGE].left = screenWidth - 200; - widgets[WIDX_NEXT_IMAGE].right = screenWidth - 1; - widgets[WIDX_NEXT_STEP_BUTTON].left = screenWidth - 198; - widgets[WIDX_NEXT_STEP_BUTTON].right = screenWidth - 3; - - widgets[WIDX_PREVIOUS_STEP_BUTTON].type = WindowWidgetType::FlatBtn; - widgets[WIDX_NEXT_STEP_BUTTON].type = WindowWidgetType::FlatBtn; - widgets[WIDX_PREVIOUS_IMAGE].type = WindowWidgetType::ImgBtn; - widgets[WIDX_NEXT_IMAGE].type = WindowWidgetType::ImgBtn; - - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + public: + void OnOpen() override { - HidePreviousStepButton(); - HideNextStepButton(); + widgets = _editorBottomToolbarWidgets; + + InitScrollWidgets(); + SetAllSceneryItemsInvented(); } - else - { - auto& gameState = GetGameState(); - if (gameState.EditorStep == EditorStep::ObjectSelection) + void OnPrepareDraw() override + { + ColourSchemeUpdateByClass( + this, + (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowClass::EditorScenarioBottomToolbar + : WindowClass::EditorTrackBottomToolbar); + + uint16_t screenWidth = ContextGetWidth(); + widgets[WIDX_NEXT_IMAGE].left = screenWidth - 200; + widgets[WIDX_NEXT_IMAGE].right = screenWidth - 1; + widgets[WIDX_NEXT_STEP_BUTTON].left = screenWidth - 198; + widgets[WIDX_NEXT_STEP_BUTTON].right = screenWidth - 3; + + widgets[WIDX_PREVIOUS_STEP_BUTTON].type = WindowWidgetType::FlatBtn; + widgets[WIDX_NEXT_STEP_BUTTON].type = WindowWidgetType::FlatBtn; + widgets[WIDX_PREVIOUS_IMAGE].type = WindowWidgetType::ImgBtn; + widgets[WIDX_NEXT_IMAGE].type = WindowWidgetType::ImgBtn; + + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { HidePreviousStepButton(); - } - else if (gameState.EditorStep == EditorStep::RollercoasterDesigner) - { HideNextStepButton(); } - else if (!(gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)) + else { - if (GetNumFreeEntities() != MAX_ENTITIES || GetGameState().ParkFlags & PARK_FLAGS_SPRITES_INITIALISED) + auto& gameState = GetGameState(); + + if (gameState.EditorStep == EditorStep::ObjectSelection) { HidePreviousStepButton(); } + else if (gameState.EditorStep == EditorStep::RollercoasterDesigner) + { + HideNextStepButton(); + } + else if (!(gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)) + { + if (GetNumFreeEntities() != MAX_ENTITIES || GetGameState().ParkFlags & PARK_FLAGS_SPRITES_INITIALISED) + { + HidePreviousStepButton(); + } + } } } - } - void OnDraw(DrawPixelInfo& dpi) override - { - auto drawPreviousButton = widgets[WIDX_PREVIOUS_STEP_BUTTON].type != WindowWidgetType::Empty; - auto drawNextButton = widgets[WIDX_NEXT_STEP_BUTTON].type != WindowWidgetType::Empty; - - if (drawPreviousButton) - DrawLeftButtonBack(dpi); - - if (drawNextButton) - DrawRightButtonBack(dpi); - - DrawWidgets(dpi); - - if (drawPreviousButton) - DrawLeftButton(dpi); - - if (drawNextButton) - DrawRightButton(dpi); - - DrawStepText(dpi); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - auto& gameState = GetGameState(); - if (widgetIndex == WIDX_PREVIOUS_STEP_BUTTON) + void OnDraw(DrawPixelInfo& dpi) override { - if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - || (GetNumFreeEntities() == MAX_ENTITIES && !(GetGameState().ParkFlags & PARK_FLAGS_SPRITES_INITIALISED))) + auto drawPreviousButton = widgets[WIDX_PREVIOUS_STEP_BUTTON].type != WindowWidgetType::Empty; + auto drawNextButton = widgets[WIDX_NEXT_STEP_BUTTON].type != WindowWidgetType::Empty; + + if (drawPreviousButton) + DrawLeftButtonBack(dpi); + + if (drawNextButton) + DrawRightButtonBack(dpi); + + DrawWidgets(dpi); + + if (drawPreviousButton) + DrawLeftButton(dpi); + + if (drawNextButton) + DrawRightButton(dpi); + + DrawStepText(dpi); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + auto& gameState = GetGameState(); + if (widgetIndex == WIDX_PREVIOUS_STEP_BUTTON) { - ((this)->*(_previousButtonMouseUp[EnumValue(gameState.EditorStep)]))(); + if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + || (GetNumFreeEntities() == MAX_ENTITIES && !(GetGameState().ParkFlags & PARK_FLAGS_SPRITES_INITIALISED))) + { + ((this)->*(_previousButtonMouseUp[EnumValue(gameState.EditorStep)]))(); + } + } + else if (widgetIndex == WIDX_NEXT_STEP_BUTTON) + { + ((this)->*(_nextButtonMouseUp[EnumValue(gameState.EditorStep)]))(); } } - else if (widgetIndex == WIDX_NEXT_STEP_BUTTON) + + private: + void JumpBackToObjectSelection() const { - ((this)->*(_nextButtonMouseUp[EnumValue(gameState.EditorStep)]))(); - } - } - -private: - void JumpBackToObjectSelection() const - { - WindowCloseAll(); - GetGameState().EditorStep = EditorStep::ObjectSelection; - GfxInvalidateScreen(); - } - - void JumpBackToLandscapeEditor() const - { - WindowCloseAll(); - SetAllSceneryItemsInvented(); - ScenerySetDefaultPlacementConfiguration(); - GetGameState().EditorStep = EditorStep::LandscapeEditor; - ContextOpenWindow(WindowClass::Map); - GfxInvalidateScreen(); - } - - void JumpBackToInventionListSetUp() const - { - WindowCloseAll(); - ContextOpenWindow(WindowClass::EditorInventionList); - GetGameState().EditorStep = EditorStep::InventionsListSetUp; - GfxInvalidateScreen(); - } - - void JumpBackToOptionsSelection() const - { - WindowCloseAll(); - ContextOpenWindow(WindowClass::EditorScenarioOptions); - GetGameState().EditorStep = EditorStep::OptionsSelection; - GfxInvalidateScreen(); - } - - bool CheckObjectSelection() const - { - WindowBase* w; - - auto [missingObjectType, errorString] = Editor::CheckObjectSelection(); - if (missingObjectType == ObjectType::None) - { - WindowCloseByClass(WindowClass::EditorObjectSelection); - return true; + WindowCloseAll(); + GetGameState().EditorStep = EditorStep::ObjectSelection; + GfxInvalidateScreen(); } - ContextShowError(STR_INVALID_SELECTION_OF_OBJECTS, errorString, {}); - w = WindowFindByClass(WindowClass::EditorObjectSelection); - if (w != nullptr) - { - // Click tab with missing object - w->OnMouseUp(WC_EDITOR_OBJECT_SELECTION__WIDX_TAB_1 + EnumValue(missingObjectType)); - } - return false; - } - - void JumpForwardFromObjectSelection() const - { - if (!CheckObjectSelection()) - return; - - FinishObjectSelection(); - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - { - ContextOpenWindow(WindowClass::ConstructRide); - } - else + void JumpBackToLandscapeEditor() const { + WindowCloseAll(); + SetAllSceneryItemsInvented(); + ScenerySetDefaultPlacementConfiguration(); + GetGameState().EditorStep = EditorStep::LandscapeEditor; ContextOpenWindow(WindowClass::Map); + GfxInvalidateScreen(); } - } - void JumpForwardToInventionListSetUp() const - { - auto [checksPassed, errorString] = Editor::CheckPark(); - if (checksPassed) + void JumpBackToInventionListSetUp() const { WindowCloseAll(); ContextOpenWindow(WindowClass::EditorInventionList); GetGameState().EditorStep = EditorStep::InventionsListSetUp; - } - else - { - ContextShowError(STR_CANT_ADVANCE_TO_NEXT_EDITOR_STAGE, errorString, {}); - } - - GfxInvalidateScreen(); - } - - void JumpForwardToOptionsSelection() const - { - WindowCloseAll(); - ContextOpenWindow(WindowClass::EditorScenarioOptions); - GetGameState().EditorStep = EditorStep::OptionsSelection; - GfxInvalidateScreen(); - } - - void JumpForwardToObjectiveSelection() const - { - WindowCloseAll(); - ContextOpenWindow(WindowClass::EditorObjectiveOptions); - GetGameState().EditorStep = EditorStep::ObjectiveSelection; - GfxInvalidateScreen(); - } - - void JumpForwardToSaveScenario() const - { - auto& gameState = GetGameState(); - const auto savePrepareResult = ScenarioPrepareForSave(gameState); - if (!savePrepareResult.Successful) - { - ContextShowError(STR_UNABLE_TO_SAVE_SCENARIO_FILE, savePrepareResult.Message, {}); GfxInvalidateScreen(); - return; } - WindowCloseAll(); - auto intent = Intent(WindowClass::Loadsave); - intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_SCENARIO); - intent.PutExtra(INTENT_EXTRA_PATH, gameState.ScenarioName); - ContextOpenIntent(&intent); - } - - void HidePreviousStepButton() - { - widgets[WIDX_PREVIOUS_STEP_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_PREVIOUS_IMAGE].type = WindowWidgetType::Empty; - } - - void HideNextStepButton() - { - widgets[WIDX_NEXT_STEP_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_NEXT_IMAGE].type = WindowWidgetType::Empty; - } - - void DrawLeftButtonBack(DrawPixelInfo& dpi) - { - auto previousWidget = widgets[WIDX_PREVIOUS_IMAGE]; - auto leftTop = windowPos + ScreenCoordsXY{ previousWidget.left, previousWidget.top }; - auto rightBottom = windowPos + ScreenCoordsXY{ previousWidget.right, previousWidget.bottom }; - GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); - } - - void DrawLeftButton(DrawPixelInfo& dpi) - { - const auto topLeft = windowPos - + ScreenCoordsXY{ widgets[WIDX_PREVIOUS_IMAGE].left + 1, widgets[WIDX_PREVIOUS_IMAGE].top + 1 }; - const auto bottomRight = windowPos - + ScreenCoordsXY{ widgets[WIDX_PREVIOUS_IMAGE].right - 1, widgets[WIDX_PREVIOUS_IMAGE].bottom - 1 }; - GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); - - GfxDrawSprite( - dpi, ImageId(SPR_PREVIOUS), - windowPos + ScreenCoordsXY{ widgets[WIDX_PREVIOUS_IMAGE].left + 6, widgets[WIDX_PREVIOUS_IMAGE].top + 6 }); - - colour_t textColour = NOT_TRANSLUCENT(colours[1]); - if (gHoverWidget.window_classification == WindowClass::BottomToolbar - && gHoverWidget.widget_index == WIDX_PREVIOUS_STEP_BUTTON) + void JumpBackToOptionsSelection() const { - textColour = COLOUR_WHITE; + WindowCloseAll(); + ContextOpenWindow(WindowClass::EditorScenarioOptions); + GetGameState().EditorStep = EditorStep::OptionsSelection; + GfxInvalidateScreen(); } - int16_t textX = (widgets[WIDX_PREVIOUS_IMAGE].left + 30 + widgets[WIDX_PREVIOUS_IMAGE].right) / 2 + windowPos.x; - int16_t textY = widgets[WIDX_PREVIOUS_IMAGE].top + 6 + windowPos.y; - - StringId stringId = _editorStepNames[EnumValue(GetGameState().EditorStep) - 1]; - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - stringId = STR_EDITOR_STEP_OBJECT_SELECTION; - - DrawTextBasic(dpi, { textX, textY }, STR_BACK_TO_PREVIOUS_STEP, {}, { textColour, TextAlignment::CENTRE }); - DrawTextBasic(dpi, { textX, textY + 10 }, stringId, {}, { textColour, TextAlignment::CENTRE }); - } - - void DrawRightButtonBack(DrawPixelInfo& dpi) - { - auto nextWidget = widgets[WIDX_NEXT_IMAGE]; - auto leftTop = windowPos + ScreenCoordsXY{ nextWidget.left, nextWidget.top }; - auto rightBottom = windowPos + ScreenCoordsXY{ nextWidget.right, nextWidget.bottom }; - GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); - } - - void DrawRightButton(DrawPixelInfo& dpi) - { - const auto topLeft = windowPos + ScreenCoordsXY{ widgets[WIDX_NEXT_IMAGE].left + 1, widgets[WIDX_NEXT_IMAGE].top + 1 }; - const auto bottomRight = windowPos - + ScreenCoordsXY{ widgets[WIDX_NEXT_IMAGE].right - 1, widgets[WIDX_NEXT_IMAGE].bottom - 1 }; - GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); - - GfxDrawSprite( - dpi, ImageId(SPR_NEXT), - windowPos + ScreenCoordsXY{ widgets[WIDX_NEXT_IMAGE].right - 29, widgets[WIDX_NEXT_IMAGE].top + 6 }); - - colour_t textColour = NOT_TRANSLUCENT(colours[1]); - - if (gHoverWidget.window_classification == WindowClass::BottomToolbar - && gHoverWidget.widget_index == WIDX_NEXT_STEP_BUTTON) + bool CheckObjectSelection() const { - textColour = COLOUR_WHITE; + WindowBase* w; + + auto [missingObjectType, errorString] = Editor::CheckObjectSelection(); + if (missingObjectType == ObjectType::None) + { + WindowCloseByClass(WindowClass::EditorObjectSelection); + return true; + } + + ContextShowError(STR_INVALID_SELECTION_OF_OBJECTS, errorString, {}); + w = WindowFindByClass(WindowClass::EditorObjectSelection); + if (w != nullptr) + { + // Click tab with missing object + w->OnMouseUp(WC_EDITOR_OBJECT_SELECTION__WIDX_TAB_1 + EnumValue(missingObjectType)); + } + return false; } - int16_t textX = (widgets[WIDX_NEXT_IMAGE].left + widgets[WIDX_NEXT_IMAGE].right - 30) / 2 + windowPos.x; - int16_t textY = widgets[WIDX_NEXT_IMAGE].top + 6 + windowPos.y; + void JumpForwardFromObjectSelection() const + { + if (!CheckObjectSelection()) + return; - StringId stringId = _editorStepNames[EnumValue(GetGameState().EditorStep) + 1]; - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - stringId = STR_EDITOR_STEP_ROLLERCOASTER_DESIGNER; + FinishObjectSelection(); + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + { + ContextOpenWindow(WindowClass::ConstructRide); + } + else + { + ContextOpenWindow(WindowClass::Map); + } + } - DrawTextBasic(dpi, { textX, textY }, STR_FORWARD_TO_NEXT_STEP, {}, { textColour, TextAlignment::CENTRE }); - DrawTextBasic(dpi, { textX, textY + 10 }, stringId, {}, { textColour, TextAlignment::CENTRE }); - } + void JumpForwardToInventionListSetUp() const + { + auto [checksPassed, errorString] = Editor::CheckPark(); + if (checksPassed) + { + WindowCloseAll(); + ContextOpenWindow(WindowClass::EditorInventionList); + GetGameState().EditorStep = EditorStep::InventionsListSetUp; + } + else + { + ContextShowError(STR_CANT_ADVANCE_TO_NEXT_EDITOR_STAGE, errorString, {}); + } - void DrawStepText(DrawPixelInfo& dpi) - { - int16_t stateX = (widgets[WIDX_PREVIOUS_IMAGE].right + widgets[WIDX_NEXT_IMAGE].left) / 2 + windowPos.x; - int16_t stateY = height - 0x0C + windowPos.y; - DrawTextBasic( - dpi, { stateX, stateY }, _editorStepNames[EnumValue(GetGameState().EditorStep)], {}, - { static_cast(NOT_TRANSLUCENT(colours[2]) | COLOUR_FLAG_OUTLINE), TextAlignment::CENTRE }); - } + GfxInvalidateScreen(); + } - static constexpr FuncPtr _previousButtonMouseUp[] = { - nullptr, - &EditorBottomToolbarWindow::JumpBackToObjectSelection, - &EditorBottomToolbarWindow::JumpBackToLandscapeEditor, - &EditorBottomToolbarWindow::JumpBackToInventionListSetUp, - &EditorBottomToolbarWindow::JumpBackToOptionsSelection, - nullptr, - &EditorBottomToolbarWindow::JumpBackToObjectSelection, - nullptr, + void JumpForwardToOptionsSelection() const + { + WindowCloseAll(); + ContextOpenWindow(WindowClass::EditorScenarioOptions); + GetGameState().EditorStep = EditorStep::OptionsSelection; + GfxInvalidateScreen(); + } + + void JumpForwardToObjectiveSelection() const + { + WindowCloseAll(); + ContextOpenWindow(WindowClass::EditorObjectiveOptions); + GetGameState().EditorStep = EditorStep::ObjectiveSelection; + GfxInvalidateScreen(); + } + + void JumpForwardToSaveScenario() const + { + auto& gameState = GetGameState(); + const auto savePrepareResult = ScenarioPrepareForSave(gameState); + if (!savePrepareResult.Successful) + { + ContextShowError(STR_UNABLE_TO_SAVE_SCENARIO_FILE, savePrepareResult.Message, {}); + GfxInvalidateScreen(); + return; + } + + WindowCloseAll(); + auto intent = Intent(WindowClass::Loadsave); + intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_SCENARIO); + intent.PutExtra(INTENT_EXTRA_PATH, gameState.ScenarioName); + ContextOpenIntent(&intent); + } + + void HidePreviousStepButton() + { + widgets[WIDX_PREVIOUS_STEP_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_PREVIOUS_IMAGE].type = WindowWidgetType::Empty; + } + + void HideNextStepButton() + { + widgets[WIDX_NEXT_STEP_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_NEXT_IMAGE].type = WindowWidgetType::Empty; + } + + void DrawLeftButtonBack(DrawPixelInfo& dpi) + { + auto previousWidget = widgets[WIDX_PREVIOUS_IMAGE]; + auto leftTop = windowPos + ScreenCoordsXY{ previousWidget.left, previousWidget.top }; + auto rightBottom = windowPos + ScreenCoordsXY{ previousWidget.right, previousWidget.bottom }; + GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); + } + + void DrawLeftButton(DrawPixelInfo& dpi) + { + const auto topLeft = windowPos + + ScreenCoordsXY{ widgets[WIDX_PREVIOUS_IMAGE].left + 1, widgets[WIDX_PREVIOUS_IMAGE].top + 1 }; + const auto bottomRight = windowPos + + ScreenCoordsXY{ widgets[WIDX_PREVIOUS_IMAGE].right - 1, widgets[WIDX_PREVIOUS_IMAGE].bottom - 1 }; + GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); + + GfxDrawSprite( + dpi, ImageId(SPR_PREVIOUS), + windowPos + ScreenCoordsXY{ widgets[WIDX_PREVIOUS_IMAGE].left + 6, widgets[WIDX_PREVIOUS_IMAGE].top + 6 }); + + colour_t textColour = NOT_TRANSLUCENT(colours[1]); + if (gHoverWidget.window_classification == WindowClass::BottomToolbar + && gHoverWidget.widget_index == WIDX_PREVIOUS_STEP_BUTTON) + { + textColour = COLOUR_WHITE; + } + + int16_t textX = (widgets[WIDX_PREVIOUS_IMAGE].left + 30 + widgets[WIDX_PREVIOUS_IMAGE].right) / 2 + windowPos.x; + int16_t textY = widgets[WIDX_PREVIOUS_IMAGE].top + 6 + windowPos.y; + + StringId stringId = _editorStepNames[EnumValue(GetGameState().EditorStep) - 1]; + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + stringId = STR_EDITOR_STEP_OBJECT_SELECTION; + + DrawTextBasic(dpi, { textX, textY }, STR_BACK_TO_PREVIOUS_STEP, {}, { textColour, TextAlignment::CENTRE }); + DrawTextBasic(dpi, { textX, textY + 10 }, stringId, {}, { textColour, TextAlignment::CENTRE }); + } + + void DrawRightButtonBack(DrawPixelInfo& dpi) + { + auto nextWidget = widgets[WIDX_NEXT_IMAGE]; + auto leftTop = windowPos + ScreenCoordsXY{ nextWidget.left, nextWidget.top }; + auto rightBottom = windowPos + ScreenCoordsXY{ nextWidget.right, nextWidget.bottom }; + GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); + } + + void DrawRightButton(DrawPixelInfo& dpi) + { + const auto topLeft = windowPos + + ScreenCoordsXY{ widgets[WIDX_NEXT_IMAGE].left + 1, widgets[WIDX_NEXT_IMAGE].top + 1 }; + const auto bottomRight = windowPos + + ScreenCoordsXY{ widgets[WIDX_NEXT_IMAGE].right - 1, widgets[WIDX_NEXT_IMAGE].bottom - 1 }; + GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); + + GfxDrawSprite( + dpi, ImageId(SPR_NEXT), + windowPos + ScreenCoordsXY{ widgets[WIDX_NEXT_IMAGE].right - 29, widgets[WIDX_NEXT_IMAGE].top + 6 }); + + colour_t textColour = NOT_TRANSLUCENT(colours[1]); + + if (gHoverWidget.window_classification == WindowClass::BottomToolbar + && gHoverWidget.widget_index == WIDX_NEXT_STEP_BUTTON) + { + textColour = COLOUR_WHITE; + } + + int16_t textX = (widgets[WIDX_NEXT_IMAGE].left + widgets[WIDX_NEXT_IMAGE].right - 30) / 2 + windowPos.x; + int16_t textY = widgets[WIDX_NEXT_IMAGE].top + 6 + windowPos.y; + + StringId stringId = _editorStepNames[EnumValue(GetGameState().EditorStep) + 1]; + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + stringId = STR_EDITOR_STEP_ROLLERCOASTER_DESIGNER; + + DrawTextBasic(dpi, { textX, textY }, STR_FORWARD_TO_NEXT_STEP, {}, { textColour, TextAlignment::CENTRE }); + DrawTextBasic(dpi, { textX, textY + 10 }, stringId, {}, { textColour, TextAlignment::CENTRE }); + } + + void DrawStepText(DrawPixelInfo& dpi) + { + int16_t stateX = (widgets[WIDX_PREVIOUS_IMAGE].right + widgets[WIDX_NEXT_IMAGE].left) / 2 + windowPos.x; + int16_t stateY = height - 0x0C + windowPos.y; + DrawTextBasic( + dpi, { stateX, stateY }, _editorStepNames[EnumValue(GetGameState().EditorStep)], {}, + { static_cast(NOT_TRANSLUCENT(colours[2]) | COLOUR_FLAG_OUTLINE), TextAlignment::CENTRE }); + } + + static constexpr FuncPtr _previousButtonMouseUp[] = { + nullptr, + &EditorBottomToolbarWindow::JumpBackToObjectSelection, + &EditorBottomToolbarWindow::JumpBackToLandscapeEditor, + &EditorBottomToolbarWindow::JumpBackToInventionListSetUp, + &EditorBottomToolbarWindow::JumpBackToOptionsSelection, + nullptr, + &EditorBottomToolbarWindow::JumpBackToObjectSelection, + nullptr, + }; + + static constexpr FuncPtr _nextButtonMouseUp[] = { + &EditorBottomToolbarWindow::JumpForwardFromObjectSelection, + &EditorBottomToolbarWindow::JumpForwardToInventionListSetUp, + &EditorBottomToolbarWindow::JumpForwardToOptionsSelection, + &EditorBottomToolbarWindow::JumpForwardToObjectiveSelection, + &EditorBottomToolbarWindow::JumpForwardToSaveScenario, + nullptr, + nullptr, + nullptr, + }; }; - static constexpr FuncPtr _nextButtonMouseUp[] = { - &EditorBottomToolbarWindow::JumpForwardFromObjectSelection, - &EditorBottomToolbarWindow::JumpForwardToInventionListSetUp, - &EditorBottomToolbarWindow::JumpForwardToOptionsSelection, - &EditorBottomToolbarWindow::JumpForwardToObjectiveSelection, - &EditorBottomToolbarWindow::JumpForwardToSaveScenario, - nullptr, - nullptr, - nullptr, - }; -}; + /** + * Creates the main editor top toolbar window. + * rct2: 0x0066F052 (part of 0x0066EF38) + */ + WindowBase* WindowEditorBottomToolbarOpen() + { + auto* window = WindowCreate( + WindowClass::BottomToolbar, ScreenCoordsXY(0, ContextGetHeight() - 32), ContextGetWidth(), 32, + WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); -/** - * Creates the main editor top toolbar window. - * rct2: 0x0066F052 (part of 0x0066EF38) - */ -WindowBase* WindowEditorBottomToolbarOpen() -{ - auto* window = WindowCreate( - WindowClass::BottomToolbar, ScreenCoordsXY(0, ContextGetHeight() - 32), ContextGetWidth(), 32, - WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); - - return window; -} + return window; + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/EditorInventionsList.cpp b/src/openrct2-ui/windows/EditorInventionsList.cpp index cce7f2ac2c..2b292e5efb 100644 --- a/src/openrct2-ui/windows/EditorInventionsList.cpp +++ b/src/openrct2-ui/windows/EditorInventionsList.cpp @@ -26,15 +26,15 @@ #include #include +namespace OpenRCT2::Ui::Windows +{ #pragma region Widgets -using namespace OpenRCT2; + static constexpr int32_t WW = 600; + static constexpr int32_t WH = 400; + static constexpr StringId WINDOW_TITLE = STR_INVENTION_LIST; -static constexpr int32_t WW = 600; -static constexpr int32_t WH = 400; -static constexpr StringId WINDOW_TITLE = STR_INVENTION_LIST; - -// clang-format off + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -66,636 +66,640 @@ static Widget _inventionListDragWidgets[] = { MakeWidget({0, 0}, {150, 14}, WindowWidgetType::ImgBtn, WindowColour::Primary), kWidgetsEnd, }; -// clang-format on + // clang-format on #pragma endregion -static void WindowEditorInventionsListDragOpen( - ResearchItem* researchItem, const ScreenCoordsXY& editorPos, int objectSelectionScrollWidth); -static const ResearchItem* WindowEditorInventionsListDragGetItem(); + static void WindowEditorInventionsListDragOpen( + ResearchItem* researchItem, const ScreenCoordsXY& editorPos, int objectSelectionScrollWidth); + static const ResearchItem* WindowEditorInventionsListDragGetItem(); -/** - * - * rct2: 0x0068596F - * Sets rides that are in use to be always researched - */ -static void ResearchRidesSetup() -{ - // Reset all objects to not required - for (auto objectType : TransientObjectTypes) + /** + * + * rct2: 0x0068596F + * Sets rides that are in use to be always researched + */ + static void ResearchRidesSetup() { - auto maxObjects = object_entry_group_counts[EnumValue(objectType)]; - for (int32_t i = 0; i < maxObjects; i++) + // Reset all objects to not required + for (auto objectType : TransientObjectTypes) { - Editor::ClearSelectedObject(objectType, i, ObjectSelectionFlags::AllFlags); + auto maxObjects = object_entry_group_counts[EnumValue(objectType)]; + for (int32_t i = 0; i < maxObjects; i++) + { + Editor::ClearSelectedObject(objectType, i, ObjectSelectionFlags::AllFlags); + } + } + + // Set research required for rides in use + for (const auto& ride : GetRideManager()) + { + Editor::SetSelectedObject( + ObjectType::Ride, ride.subtype, ObjectSelectionFlags::Selected | ObjectSelectionFlags::InUse); } } - // Set research required for rides in use - for (const auto& ride : GetRideManager()) + static void DrawResearchItem( + DrawPixelInfo& dpi, const ResearchItem& researchItem, const int16_t& width, const ScreenCoordsXY& screenCoords, + StringId format, TextPaint textPaint) { - Editor::SetSelectedObject(ObjectType::Ride, ride.subtype, ObjectSelectionFlags::Selected | ObjectSelectionFlags::InUse); + const StringId itemNameId = researchItem.GetName(); + int16_t columnSplitOffset = width / 2; + + if (researchItem.type == Research::EntryType::Ride + && !GetRideTypeDescriptor(researchItem.baseRideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + const StringId rideTypeName = GetRideNaming( + researchItem.baseRideType, *GetRideEntryByIndex(researchItem.entryIndex)) + .Name; + + // Draw group name + auto ft = Formatter(); + ft.Add(rideTypeName); + DrawTextEllipsised(dpi, screenCoords, columnSplitOffset - 11, format, ft, textPaint); + + // Draw vehicle name + ft = Formatter(); + ft.Add(itemNameId); + DrawTextEllipsised( + dpi, { screenCoords + ScreenCoordsXY{ columnSplitOffset, 0 } }, columnSplitOffset - 11, format, ft, textPaint); + } + else + { + // Scenery group, flat ride or shopdis + auto ft = Formatter(); + ft.Add(itemNameId); + DrawTextEllipsised(dpi, screenCoords, width, format, ft, textPaint); + } } -} - -static void DrawResearchItem( - DrawPixelInfo& dpi, const ResearchItem& researchItem, const int16_t& width, const ScreenCoordsXY& screenCoords, - StringId format, TextPaint textPaint) -{ - const StringId itemNameId = researchItem.GetName(); - int16_t columnSplitOffset = width / 2; - - if (researchItem.type == Research::EntryType::Ride - && !GetRideTypeDescriptor(researchItem.baseRideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) - { - const StringId rideTypeName = GetRideNaming(researchItem.baseRideType, *GetRideEntryByIndex(researchItem.entryIndex)) - .Name; - - // Draw group name - auto ft = Formatter(); - ft.Add(rideTypeName); - DrawTextEllipsised(dpi, screenCoords, columnSplitOffset - 11, format, ft, textPaint); - - // Draw vehicle name - ft = Formatter(); - ft.Add(itemNameId); - DrawTextEllipsised( - dpi, { screenCoords + ScreenCoordsXY{ columnSplitOffset, 0 } }, columnSplitOffset - 11, format, ft, textPaint); - } - else - { - // Scenery group, flat ride or shopdis - auto ft = Formatter(); - ft.Add(itemNameId); - DrawTextEllipsised(dpi, screenCoords, width, format, ft, textPaint); - } -} #pragma region Invention List Window -struct InventionListItem -{ - ResearchItem* research = nullptr; - bool isInvented = true; -}; - -class InventionListWindow final : public Window -{ - ResearchItem* _selectedResearchItem; - -public: - void OnOpen() override + struct InventionListItem { - ResearchRidesSetup(); + ResearchItem* research = nullptr; + bool isInvented = true; + }; - widgets = _inventionListWidgets; - InitScrollWidgets(); - selected_tab = 0; - _selectedResearchItem = nullptr; - - min_width = WW; - min_height = WH; - max_width = WW * 2; - max_height = WH * 2; - } - - void OnClose() override + class InventionListWindow final : public Window { - ResearchRemoveFlags(); + ResearchItem* _selectedResearchItem; - // When used in-game (as a cheat) - if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) + public: + void OnOpen() override { - gSilentResearch = true; - ResearchResetCurrentItem(); - gSilentResearch = false; + ResearchRidesSetup(); + + widgets = _inventionListWidgets; + InitScrollWidgets(); + selected_tab = 0; + _selectedResearchItem = nullptr; + + min_width = WW; + min_height = WH; + max_width = WW * 2; + max_height = WH * 2; } - } - void OnMouseUp(WidgetIndex widx) override - { - switch (widx) + void OnClose() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_RANDOM_SHUFFLE: - ResearchItemsShuffle(); - Invalidate(); - break; - case WIDX_MOVE_ITEMS_TO_TOP: - ResearchItemsMakeAllResearched(); - InitScrollWidgets(); - Invalidate(); - break; - case WIDX_MOVE_ITEMS_TO_BOTTOM: - ResearchItemsMakeAllUnresearched(); - InitScrollWidgets(); - Invalidate(); - break; + ResearchRemoveFlags(); + + // When used in-game (as a cheat) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) + { + gSilentResearch = true; + ResearchResetCurrentItem(); + gSilentResearch = false; + } } - } - void OnResize() override - { - if (width < min_width) + void OnMouseUp(WidgetIndex widx) override { + switch (widx) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_RANDOM_SHUFFLE: + ResearchItemsShuffle(); + Invalidate(); + break; + case WIDX_MOVE_ITEMS_TO_TOP: + ResearchItemsMakeAllResearched(); + InitScrollWidgets(); + Invalidate(); + break; + case WIDX_MOVE_ITEMS_TO_BOTTOM: + ResearchItemsMakeAllUnresearched(); + InitScrollWidgets(); + Invalidate(); + break; + } + } + + void OnResize() override + { + if (width < min_width) + { + Invalidate(); + width = min_width; + } + if (height < min_height) + { + Invalidate(); + height = min_height; + } + } + + void OnUpdate() override + { + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_1); + + if (WindowEditorInventionsListDragGetItem() != nullptr) + return; + Invalidate(); - width = min_width; } - if (height < min_height) + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override { - Invalidate(); - height = min_height; + const auto& gameState = GetGameState(); + ScreenSize size{}; + if (scrollIndex == 0) + { + size.height = static_cast(gameState.ResearchItemsInvented.size()) * SCROLLABLE_ROW_HEIGHT; + } + else + { + size.height = static_cast(gameState.ResearchItemsUninvented.size()) * SCROLLABLE_ROW_HEIGHT; + } + return size; } - } - void OnUpdate() override - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_1); - - if (WindowEditorInventionsListDragGetItem() != nullptr) - return; - - Invalidate(); - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - const auto& gameState = GetGameState(); - ScreenSize size{}; - if (scrollIndex == 0) + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - size.height = static_cast(gameState.ResearchItemsInvented.size()) * SCROLLABLE_ROW_HEIGHT; + auto* researchItem = GetItemFromScrollY(scrollIndex == 0, screenCoords.y); + if (researchItem != _selectedResearchItem) + { + _selectedResearchItem = researchItem; + Invalidate(); + + // Prevent always-researched items from being highlighted when hovered over + if (researchItem != nullptr && researchItem->IsAlwaysResearched()) + { + _selectedResearchItem = nullptr; + } + } } - else - { - size.height = static_cast(gameState.ResearchItemsUninvented.size()) * SCROLLABLE_ROW_HEIGHT; - } - return size; - } - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto* researchItem = GetItemFromScrollY(scrollIndex == 0, screenCoords.y); - if (researchItem != _selectedResearchItem) + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - _selectedResearchItem = researchItem; + auto* researchItem = GetItemFromScrollY(scrollIndex == 0, screenCoords.y); + if (researchItem == nullptr) + return; + + // Disallow picking up always-researched items + if (researchItem->IsAlwaysResearched()) + return; + Invalidate(); - // Prevent always-researched items from being highlighted when hovered over - if (researchItem != nullptr && researchItem->IsAlwaysResearched()) - { - _selectedResearchItem = nullptr; - } + WindowEditorInventionsListDragOpen(researchItem, windowPos, widgets[WIDX_PRE_RESEARCHED_SCROLL].right); } - } - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto* researchItem = GetItemFromScrollY(scrollIndex == 0, screenCoords.y); - if (researchItem == nullptr) - return; - - // Disallow picking up always-researched items - if (researchItem->IsAlwaysResearched()) - return; - - Invalidate(); - - WindowEditorInventionsListDragOpen(researchItem, windowPos, widgets[WIDX_PRE_RESEARCHED_SCROLL].right); - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - const auto& gameState = GetGameState(); - - // Draw background - uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; - GfxClear(&dpi, paletteIndex); - - int16_t boxWidth = widgets[WIDX_RESEARCH_ORDER_SCROLL].width(); - int32_t itemY = -SCROLLABLE_ROW_HEIGHT; - auto* dragItem = WindowEditorInventionsListDragGetItem(); - - const auto& researchList = scrollIndex == 0 ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; - for (const auto& researchItem : researchList) + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override { - itemY += SCROLLABLE_ROW_HEIGHT; - if (itemY + SCROLLABLE_ROW_HEIGHT < dpi.y || itemY >= dpi.y + dpi.height) - continue; + const auto& gameState = GetGameState(); - if (_selectedResearchItem == &researchItem) + // Draw background + uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; + GfxClear(&dpi, paletteIndex); + + int16_t boxWidth = widgets[WIDX_RESEARCH_ORDER_SCROLL].width(); + int32_t itemY = -SCROLLABLE_ROW_HEIGHT; + auto* dragItem = WindowEditorInventionsListDragGetItem(); + + const auto& researchList = scrollIndex == 0 ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; + for (const auto& researchItem : researchList) { - int32_t top, bottom; - if (dragItem == nullptr) + itemY += SCROLLABLE_ROW_HEIGHT; + if (itemY + SCROLLABLE_ROW_HEIGHT < dpi.y || itemY >= dpi.y + dpi.height) + continue; + + if (_selectedResearchItem == &researchItem) { - // Highlight - top = itemY; - bottom = itemY + SCROLLABLE_ROW_HEIGHT - 1; - } - else - { - // Drop horizontal rule - top = itemY - 1; - bottom = itemY; + int32_t top, bottom; + if (dragItem == nullptr) + { + // Highlight + top = itemY; + bottom = itemY + SCROLLABLE_ROW_HEIGHT - 1; + } + else + { + // Drop horizontal rule + top = itemY - 1; + bottom = itemY; + } + + GfxFilterRect(dpi, { 0, top, boxWidth, bottom }, FilterPaletteID::PaletteDarken1); } - GfxFilterRect(dpi, { 0, top, boxWidth, bottom }, FilterPaletteID::PaletteDarken1); - } + if (dragItem != nullptr && researchItem == *dragItem) + continue; - if (dragItem != nullptr && researchItem == *dragItem) - continue; + // TODO: this parameter by itself produces very light text. + // It needs a {BLACK} token in the string to work properly. + colour_t colour = COLOUR_BLACK; + FontStyle fontStyle = FontStyle::Medium; + auto darkness = TextDarkness::Regular; - // TODO: this parameter by itself produces very light text. - // It needs a {BLACK} token in the string to work properly. - colour_t colour = COLOUR_BLACK; - FontStyle fontStyle = FontStyle::Medium; - auto darkness = TextDarkness::Regular; - - if (researchItem.IsAlwaysResearched()) - { - if (_selectedResearchItem == &researchItem && dragItem == nullptr) - darkness = TextDarkness::ExtraDark; - else - darkness = TextDarkness::Dark; - colour = colours[1] | COLOUR_FLAG_INSET; - } - - DrawResearchItem(dpi, researchItem, boxWidth, { 1, itemY }, STR_BLACK_STRING, { colour, fontStyle, darkness }); - } - } - - CursorID OnCursor(WidgetIndex widx, const ScreenCoordsXY& screenCoords, CursorID fallback) override - { - bool isInvented = false; - - switch (widx) - { - case WIDX_PRE_RESEARCHED_SCROLL: - isInvented = true; - break; - case WIDX_RESEARCH_ORDER_SCROLL: - isInvented = false; - break; - default: - return fallback; - } - - // Use the open hand as cursor for items that can be picked up - auto* researchItem = GetItemFromScrollY(isInvented, screenCoords.y); - if (researchItem != nullptr && !researchItem->IsAlwaysResearched()) - { - return CursorID::HandOpen; - } - return fallback; - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - // Tab image - auto screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }; - GfxDrawSprite(dpi, ImageId(SPR_TAB_FINANCES_RESEARCH_0 + (frame_no / 2) % 8), screenPos); - - // Pre-researched items label - screenPos = windowPos - + ScreenCoordsXY{ widgets[WIDX_PRE_RESEARCHED_SCROLL].left, widgets[WIDX_PRE_RESEARCHED_SCROLL].top - 11 }; - DrawTextBasic(dpi, screenPos - ScreenCoordsXY{ 0, 1 }, STR_INVENTION_PREINVENTED_ITEMS); - - // Research order label - screenPos = windowPos - + ScreenCoordsXY{ widgets[WIDX_RESEARCH_ORDER_SCROLL].left, widgets[WIDX_RESEARCH_ORDER_SCROLL].top - 11 }; - DrawTextBasic(dpi, screenPos - ScreenCoordsXY{ 0, 1 }, STR_INVENTION_TO_BE_INVENTED_ITEMS); - - // Preview background - auto& bkWidget = widgets[WIDX_PREVIEW]; - GfxFillRect( - dpi, - { windowPos + ScreenCoordsXY{ bkWidget.left + 1, bkWidget.top + 1 }, - windowPos + ScreenCoordsXY{ bkWidget.right - 1, bkWidget.bottom - 1 } }, - ColourMapA[colours[1]].darkest); - - auto* researchItem = WindowEditorInventionsListDragGetItem(); - if (researchItem == nullptr || researchItem->IsNull()) - researchItem = _selectedResearchItem; - // If the research item is null or a list separator. - if (researchItem == nullptr || researchItem->IsNull()) - return; - - // Preview image - ObjectType objectEntryType = ObjectType::SceneryGroup; - if (researchItem->type == Research::EntryType::Ride) - objectEntryType = ObjectType::Ride; - - auto chunk = ObjectEntryGetChunk(objectEntryType, researchItem->entryIndex); - if (chunk == nullptr) - return; - - // Draw preview - const auto* object = ObjectEntryGetObject(objectEntryType, researchItem->entryIndex); - if (object != nullptr) - { - DrawPixelInfo clipDPI; - screenPos = windowPos + ScreenCoordsXY{ bkWidget.left + 1, bkWidget.top + 1 }; - const auto clipWidth = bkWidget.width() - 1; - const auto clipHeight = bkWidget.height() - 1; - if (ClipDrawPixelInfo(clipDPI, dpi, screenPos, clipWidth, clipHeight)) - { - object->DrawPreview(clipDPI, clipWidth, clipHeight); - } - } - - // Item name - screenPos = windowPos + ScreenCoordsXY{ bkWidget.midX() + 1, bkWidget.bottom + 3 }; - const auto itemWidth = width - widgets[WIDX_RESEARCH_ORDER_SCROLL].right - 6; - - StringId drawString = STR_WINDOW_COLOUR_2_STRINGID; - StringId stringId = researchItem->GetName(); - auto ft = Formatter(); - - if (researchItem->type == Research::EntryType::Ride - && !GetRideTypeDescriptor(researchItem->baseRideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) - { - drawString = STR_WINDOW_COLOUR_2_STRINGID_STRINGID; - StringId rideTypeName = GetRideNaming(researchItem->baseRideType, *GetRideEntryByIndex(researchItem->entryIndex)) - .Name; - ft.Add(rideTypeName); - ft.Add(stringId); - } - else - { - ft.Add(stringId); - } - - DrawTextEllipsised(dpi, screenPos, itemWidth, drawString, ft, { TextAlignment::CENTRE }); - screenPos.y += 15; - - // Item category - screenPos.x = windowPos.x + widgets[WIDX_RESEARCH_ORDER_SCROLL].right + 4; - ft = Formatter(); - ft.Add(researchItem->GetCategoryInventionString()); - DrawTextBasic(dpi, screenPos, STR_INVENTION_RESEARCH_GROUP, ft); - } - - void OnPrepareDraw() override - { - pressed_widgets |= 1uLL << WIDX_PREVIEW; - pressed_widgets |= 1uLL << WIDX_TAB_1; - - widgets[WIDX_CLOSE].type = gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR ? WindowWidgetType::Empty - : WindowWidgetType::CloseBox; - - ResizeFrameWithPage(); - - int16_t scrollListHeight = (height - 88) / 2; - - widgets[WIDX_PRE_RESEARCHED_SCROLL].bottom = 60 + scrollListHeight; - widgets[WIDX_PRE_RESEARCHED_SCROLL].right = width - 229; - - widgets[WIDX_RESEARCH_ORDER_SCROLL].top = widgets[WIDX_PRE_RESEARCHED_SCROLL].bottom + 15; - widgets[WIDX_RESEARCH_ORDER_SCROLL].bottom = widgets[WIDX_RESEARCH_ORDER_SCROLL].top + scrollListHeight; - widgets[WIDX_RESEARCH_ORDER_SCROLL].right = width - 229; - - widgets[WIDX_PREVIEW].left = width - 169; - widgets[WIDX_PREVIEW].right = width - 56; - - widgets[WIDX_MOVE_ITEMS_TO_TOP].top = height - 57; - widgets[WIDX_MOVE_ITEMS_TO_TOP].bottom = height - 44; - widgets[WIDX_MOVE_ITEMS_TO_TOP].left = width - 225; - widgets[WIDX_MOVE_ITEMS_TO_TOP].right = width - 6; - - widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].top = height - 42; - widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].bottom = height - 29; - widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].left = width - 225; - widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].right = width - 6; - - widgets[WIDX_RANDOM_SHUFFLE].top = height - 27; - widgets[WIDX_RANDOM_SHUFFLE].bottom = height - 14; - widgets[WIDX_RANDOM_SHUFFLE].left = width - 225; - widgets[WIDX_RANDOM_SHUFFLE].right = width - 6; - } - - // Get Research Item and Scroll Id (scroll id represents invented(0)/not invented(1)/failure(-1) - std::optional GetResearchItemAt(const ScreenCoordsXY& screenCoords) - { - if (windowPos.x <= screenCoords.x && windowPos.y < screenCoords.y && windowPos.x + width > screenCoords.x - && windowPos.y + height > screenCoords.y) - { - WidgetIndex widgetIndex = WindowFindWidgetFromPoint(*this, screenCoords); - auto& widget = widgets[widgetIndex]; - if (widgetIndex == WIDX_PRE_RESEARCHED_SCROLL || widgetIndex == WIDX_RESEARCH_ORDER_SCROLL) - { - gPressedWidget.widget_index = widgetIndex; - int32_t outScrollArea{}; - ScreenCoordsXY outScrollCoords{}; - int32_t outScrollId{}; - WidgetScrollGetPart(*this, &widget, screenCoords, outScrollCoords, &outScrollArea, &outScrollId); - if (outScrollArea == SCROLL_PART_VIEW) + if (researchItem.IsAlwaysResearched()) { - const auto isInvented = outScrollId == 0; - int32_t scrollY = outScrollCoords.y + 6; - return InventionListItem{ GetItemFromScrollYIncludeSeps(isInvented, scrollY), isInvented }; + if (_selectedResearchItem == &researchItem && dragItem == nullptr) + darkness = TextDarkness::ExtraDark; + else + darkness = TextDarkness::Dark; + colour = colours[1] | COLOUR_FLAG_INSET; + } + + DrawResearchItem(dpi, researchItem, boxWidth, { 1, itemY }, STR_BLACK_STRING, { colour, fontStyle, darkness }); + } + } + + CursorID OnCursor(WidgetIndex widx, const ScreenCoordsXY& screenCoords, CursorID fallback) override + { + bool isInvented = false; + + switch (widx) + { + case WIDX_PRE_RESEARCHED_SCROLL: + isInvented = true; + break; + case WIDX_RESEARCH_ORDER_SCROLL: + isInvented = false; + break; + default: + return fallback; + } + + // Use the open hand as cursor for items that can be picked up + auto* researchItem = GetItemFromScrollY(isInvented, screenCoords.y); + if (researchItem != nullptr && !researchItem->IsAlwaysResearched()) + { + return CursorID::HandOpen; + } + return fallback; + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + // Tab image + auto screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }; + GfxDrawSprite(dpi, ImageId(SPR_TAB_FINANCES_RESEARCH_0 + (frame_no / 2) % 8), screenPos); + + // Pre-researched items label + screenPos = windowPos + + ScreenCoordsXY{ widgets[WIDX_PRE_RESEARCHED_SCROLL].left, widgets[WIDX_PRE_RESEARCHED_SCROLL].top - 11 }; + DrawTextBasic(dpi, screenPos - ScreenCoordsXY{ 0, 1 }, STR_INVENTION_PREINVENTED_ITEMS); + + // Research order label + screenPos = windowPos + + ScreenCoordsXY{ widgets[WIDX_RESEARCH_ORDER_SCROLL].left, widgets[WIDX_RESEARCH_ORDER_SCROLL].top - 11 }; + DrawTextBasic(dpi, screenPos - ScreenCoordsXY{ 0, 1 }, STR_INVENTION_TO_BE_INVENTED_ITEMS); + + // Preview background + auto& bkWidget = widgets[WIDX_PREVIEW]; + GfxFillRect( + dpi, + { windowPos + ScreenCoordsXY{ bkWidget.left + 1, bkWidget.top + 1 }, + windowPos + ScreenCoordsXY{ bkWidget.right - 1, bkWidget.bottom - 1 } }, + ColourMapA[colours[1]].darkest); + + auto* researchItem = WindowEditorInventionsListDragGetItem(); + if (researchItem == nullptr || researchItem->IsNull()) + researchItem = _selectedResearchItem; + // If the research item is null or a list separator. + if (researchItem == nullptr || researchItem->IsNull()) + return; + + // Preview image + ObjectType objectEntryType = ObjectType::SceneryGroup; + if (researchItem->type == Research::EntryType::Ride) + objectEntryType = ObjectType::Ride; + + auto chunk = ObjectEntryGetChunk(objectEntryType, researchItem->entryIndex); + if (chunk == nullptr) + return; + + // Draw preview + const auto* object = ObjectEntryGetObject(objectEntryType, researchItem->entryIndex); + if (object != nullptr) + { + DrawPixelInfo clipDPI; + screenPos = windowPos + ScreenCoordsXY{ bkWidget.left + 1, bkWidget.top + 1 }; + const auto clipWidth = bkWidget.width() - 1; + const auto clipHeight = bkWidget.height() - 1; + if (ClipDrawPixelInfo(clipDPI, dpi, screenPos, clipWidth, clipHeight)) + { + object->DrawPreview(clipDPI, clipWidth, clipHeight); } } + + // Item name + screenPos = windowPos + ScreenCoordsXY{ bkWidget.midX() + 1, bkWidget.bottom + 3 }; + const auto itemWidth = width - widgets[WIDX_RESEARCH_ORDER_SCROLL].right - 6; + + StringId drawString = STR_WINDOW_COLOUR_2_STRINGID; + StringId stringId = researchItem->GetName(); + auto ft = Formatter(); + + if (researchItem->type == Research::EntryType::Ride + && !GetRideTypeDescriptor(researchItem->baseRideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + drawString = STR_WINDOW_COLOUR_2_STRINGID_STRINGID; + StringId rideTypeName = GetRideNaming( + researchItem->baseRideType, *GetRideEntryByIndex(researchItem->entryIndex)) + .Name; + ft.Add(rideTypeName); + ft.Add(stringId); + } + else + { + ft.Add(stringId); + } + + DrawTextEllipsised(dpi, screenPos, itemWidth, drawString, ft, { TextAlignment::CENTRE }); + screenPos.y += 15; + + // Item category + screenPos.x = windowPos.x + widgets[WIDX_RESEARCH_ORDER_SCROLL].right + 4; + ft = Formatter(); + ft.Add(researchItem->GetCategoryInventionString()); + DrawTextBasic(dpi, screenPos, STR_INVENTION_RESEARCH_GROUP, ft); } - return std::nullopt; - } - - bool IsResearchItemSelected(ResearchItem* item) const - { - return item == _selectedResearchItem; - } - - // hack to fix #17544: OnScrollMouseOver never gets called while dragging - void SetSelectedResearchItem(ResearchItem* item) - { - _selectedResearchItem = item; - } - - void MoveResearchItem(const ResearchItem& item, ResearchItem* beforeItem, bool isInvented) - { - auto& gameState = GetGameState(); - _selectedResearchItem = nullptr; - Invalidate(); - - uint32_t beforeItemRawValue = 0; - if (beforeItem != nullptr) - beforeItemRawValue = beforeItem->rawValue; - - if (item.rawValue == beforeItemRawValue) - return; - - ResearchRemove(item); - - auto& researchList = isInvented ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; - if (beforeItem != nullptr) + void OnPrepareDraw() override { - for (size_t i = 0; i < researchList.size(); i++) + pressed_widgets |= 1uLL << WIDX_PREVIEW; + pressed_widgets |= 1uLL << WIDX_TAB_1; + + widgets[WIDX_CLOSE].type = gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR ? WindowWidgetType::Empty + : WindowWidgetType::CloseBox; + + ResizeFrameWithPage(); + + int16_t scrollListHeight = (height - 88) / 2; + + widgets[WIDX_PRE_RESEARCHED_SCROLL].bottom = 60 + scrollListHeight; + widgets[WIDX_PRE_RESEARCHED_SCROLL].right = width - 229; + + widgets[WIDX_RESEARCH_ORDER_SCROLL].top = widgets[WIDX_PRE_RESEARCHED_SCROLL].bottom + 15; + widgets[WIDX_RESEARCH_ORDER_SCROLL].bottom = widgets[WIDX_RESEARCH_ORDER_SCROLL].top + scrollListHeight; + widgets[WIDX_RESEARCH_ORDER_SCROLL].right = width - 229; + + widgets[WIDX_PREVIEW].left = width - 169; + widgets[WIDX_PREVIEW].right = width - 56; + + widgets[WIDX_MOVE_ITEMS_TO_TOP].top = height - 57; + widgets[WIDX_MOVE_ITEMS_TO_TOP].bottom = height - 44; + widgets[WIDX_MOVE_ITEMS_TO_TOP].left = width - 225; + widgets[WIDX_MOVE_ITEMS_TO_TOP].right = width - 6; + + widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].top = height - 42; + widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].bottom = height - 29; + widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].left = width - 225; + widgets[WIDX_MOVE_ITEMS_TO_BOTTOM].right = width - 6; + + widgets[WIDX_RANDOM_SHUFFLE].top = height - 27; + widgets[WIDX_RANDOM_SHUFFLE].bottom = height - 14; + widgets[WIDX_RANDOM_SHUFFLE].left = width - 225; + widgets[WIDX_RANDOM_SHUFFLE].right = width - 6; + } + + // Get Research Item and Scroll Id (scroll id represents invented(0)/not invented(1)/failure(-1) + std::optional GetResearchItemAt(const ScreenCoordsXY& screenCoords) + { + if (windowPos.x <= screenCoords.x && windowPos.y < screenCoords.y && windowPos.x + width > screenCoords.x + && windowPos.y + height > screenCoords.y) { - if (researchList[i].rawValue == beforeItemRawValue) + WidgetIndex widgetIndex = WindowFindWidgetFromPoint(*this, screenCoords); + auto& widget = widgets[widgetIndex]; + if (widgetIndex == WIDX_PRE_RESEARCHED_SCROLL || widgetIndex == WIDX_RESEARCH_ORDER_SCROLL) { - researchList.insert((researchList.begin() + i), item); - return; + gPressedWidget.widget_index = widgetIndex; + int32_t outScrollArea{}; + ScreenCoordsXY outScrollCoords{}; + int32_t outScrollId{}; + WidgetScrollGetPart(*this, &widget, screenCoords, outScrollCoords, &outScrollArea, &outScrollId); + if (outScrollArea == SCROLL_PART_VIEW) + { + const auto isInvented = outScrollId == 0; + int32_t scrollY = outScrollCoords.y + 6; + return InventionListItem{ GetItemFromScrollYIncludeSeps(isInvented, scrollY), isInvented }; + } } } + + return std::nullopt; } - // Still not found? Append to end of list. - researchList.push_back(item); - } - -private: - ResearchItem* GetItemFromScrollY(bool isInvented, int32_t y) const - { - auto& gameState = GetGameState(); - auto& researchList = isInvented ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; - for (auto& researchItem : researchList) + bool IsResearchItemSelected(ResearchItem* item) const { - y -= SCROLLABLE_ROW_HEIGHT; - if (y < 0) - { - return &researchItem; - } + return item == _selectedResearchItem; } - return nullptr; - } - - ResearchItem* GetItemFromScrollYIncludeSeps(bool isInvented, int32_t y) const - { - auto& gameState = GetGameState(); - auto& researchList = isInvented ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; - for (auto& researchItem : researchList) + // hack to fix #17544: OnScrollMouseOver never gets called while dragging + void SetSelectedResearchItem(ResearchItem* item) { - y -= SCROLLABLE_ROW_HEIGHT; - if (y < 0) - { - return &researchItem; - } + _selectedResearchItem = item; } - return nullptr; - } -}; -/** - * - * rct2: 0x00684E04 - */ -WindowBase* WindowEditorInventionsListOpen() -{ - return WindowFocusOrCreate( - WindowClass::EditorInventionList, WW, WH, WF_NO_SCROLLING | WF_RESIZABLE | WF_CENTRE_SCREEN); -} + void MoveResearchItem(const ResearchItem& item, ResearchItem* beforeItem, bool isInvented) + { + auto& gameState = GetGameState(); + _selectedResearchItem = nullptr; + Invalidate(); + + uint32_t beforeItemRawValue = 0; + if (beforeItem != nullptr) + beforeItemRawValue = beforeItem->rawValue; + + if (item.rawValue == beforeItemRawValue) + return; + + ResearchRemove(item); + + auto& researchList = isInvented ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; + if (beforeItem != nullptr) + { + for (size_t i = 0; i < researchList.size(); i++) + { + if (researchList[i].rawValue == beforeItemRawValue) + { + researchList.insert((researchList.begin() + i), item); + return; + } + } + } + + // Still not found? Append to end of list. + researchList.push_back(item); + } + + private: + ResearchItem* GetItemFromScrollY(bool isInvented, int32_t y) const + { + auto& gameState = GetGameState(); + auto& researchList = isInvented ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; + for (auto& researchItem : researchList) + { + y -= SCROLLABLE_ROW_HEIGHT; + if (y < 0) + { + return &researchItem; + } + } + + return nullptr; + } + + ResearchItem* GetItemFromScrollYIncludeSeps(bool isInvented, int32_t y) const + { + auto& gameState = GetGameState(); + auto& researchList = isInvented ? gameState.ResearchItemsInvented : gameState.ResearchItemsUninvented; + for (auto& researchItem : researchList) + { + y -= SCROLLABLE_ROW_HEIGHT; + if (y < 0) + { + return &researchItem; + } + } + return nullptr; + } + }; + + /** + * + * rct2: 0x00684E04 + */ + WindowBase* WindowEditorInventionsListOpen() + { + return WindowFocusOrCreate( + WindowClass::EditorInventionList, WW, WH, WF_NO_SCROLLING | WF_RESIZABLE | WF_CENTRE_SCREEN); + } #pragma endregion #pragma region Drag item -class InventionDragWindow final : public Window -{ - ResearchItem _draggedItem; - -public: - void OnOpen() override + class InventionDragWindow final : public Window { - widgets = _inventionListDragWidgets; - colours[1] = COLOUR_WHITE; - } + ResearchItem _draggedItem; - CursorID OnCursor(const WidgetIndex widx, const ScreenCoordsXY& screenCoords, const CursorID defaultCursor) override - { - auto* inventionListWindow = static_cast(WindowFindByClass(WindowClass::EditorInventionList)); - if (inventionListWindow != nullptr) + public: + void OnOpen() override { - auto res = inventionListWindow->GetResearchItemAt(screenCoords); - auto* research = res.has_value() ? res->research : nullptr; - if (!inventionListWindow->IsResearchItemSelected(research)) + widgets = _inventionListDragWidgets; + colours[1] = COLOUR_WHITE; + } + + CursorID OnCursor(const WidgetIndex widx, const ScreenCoordsXY& screenCoords, const CursorID defaultCursor) override + { + auto* inventionListWindow = static_cast(WindowFindByClass(WindowClass::EditorInventionList)); + if (inventionListWindow != nullptr) { - inventionListWindow->SetSelectedResearchItem(research); - inventionListWindow->Invalidate(); + auto res = inventionListWindow->GetResearchItemAt(screenCoords); + auto* research = res.has_value() ? res->research : nullptr; + if (!inventionListWindow->IsResearchItemSelected(research)) + { + inventionListWindow->SetSelectedResearchItem(research); + inventionListWindow->Invalidate(); + } } + + return CursorID::HandClosed; } - return CursorID::HandClosed; - } - - void OnMoved(const ScreenCoordsXY& screenCoords) override - { - auto* inventionListWindow = static_cast(WindowFindByClass(WindowClass::EditorInventionList)); - if (inventionListWindow == nullptr) + void OnMoved(const ScreenCoordsXY& screenCoords) override { + auto* inventionListWindow = static_cast(WindowFindByClass(WindowClass::EditorInventionList)); + if (inventionListWindow == nullptr) + { + Close(); + return; + } + std::optional res; + // Skip always researched items, so that the dragged item gets placed underneath them + auto newScreenCoords = screenCoords; + do + { + res = inventionListWindow->GetResearchItemAt(newScreenCoords); + newScreenCoords.y += LIST_ROW_HEIGHT; + } while (res.has_value() && res->research != nullptr && res->research->IsAlwaysResearched()); + + if (res.has_value()) + { + inventionListWindow->MoveResearchItem(_draggedItem, res->research, res->isInvented); + } + + WindowInvalidateByClass(WindowClass::EditorInventionList); Close(); - return; - } - std::optional res; - // Skip always researched items, so that the dragged item gets placed underneath them - auto newScreenCoords = screenCoords; - do - { - res = inventionListWindow->GetResearchItemAt(newScreenCoords); - newScreenCoords.y += LIST_ROW_HEIGHT; - } while (res.has_value() && res->research != nullptr && res->research->IsAlwaysResearched()); - - if (res.has_value()) - { - inventionListWindow->MoveResearchItem(_draggedItem, res->research, res->isInvented); } - WindowInvalidateByClass(WindowClass::EditorInventionList); - Close(); - } + void OnDraw(DrawPixelInfo& dpi) override + { + auto screenCoords = windowPos + ScreenCoordsXY{ 0, 2 }; - void OnDraw(DrawPixelInfo& dpi) override + DrawResearchItem( + dpi, _draggedItem, width, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, + { COLOUR_BLACK | static_cast(COLOUR_FLAG_OUTLINE) }); + } + + void Init(ResearchItem& researchItem, const ScreenCoordsXY& editorPos, int objectSelectionScrollWidth) + { + _draggedItem = researchItem; + + widgets[0].right = objectSelectionScrollWidth; + Invalidate(); + windowPos = ScreenCoordsXY{ editorPos.x, gTooltipCursor.y - 7 }; + width = objectSelectionScrollWidth; + Invalidate(); + + InputWindowPositionBegin(*this, 0, gTooltipCursor); + } + + const ResearchItem& GetItem() const + { + return _draggedItem; + } + }; + + static void WindowEditorInventionsListDragOpen( + ResearchItem* researchItem, const ScreenCoordsXY& editorPos, int objectSelectionScrollWidth) { - auto screenCoords = windowPos + ScreenCoordsXY{ 0, 2 }; - - DrawResearchItem( - dpi, _draggedItem, width, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, - { COLOUR_BLACK | static_cast(COLOUR_FLAG_OUTLINE) }); + WindowCloseByClass(WindowClass::EditorInventionListDrag); + auto* wnd = WindowCreate( + WindowClass::EditorInventionListDrag, 10, 14, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_SNAPPING); + if (wnd != nullptr) + { + wnd->Init(*researchItem, editorPos, objectSelectionScrollWidth); + } } - void Init(ResearchItem& researchItem, const ScreenCoordsXY& editorPos, int objectSelectionScrollWidth) + static const ResearchItem* WindowEditorInventionsListDragGetItem() { - _draggedItem = researchItem; - - widgets[0].right = objectSelectionScrollWidth; - Invalidate(); - windowPos = ScreenCoordsXY{ editorPos.x, gTooltipCursor.y - 7 }; - width = objectSelectionScrollWidth; - Invalidate(); - - InputWindowPositionBegin(*this, 0, gTooltipCursor); + auto* wnd = static_cast(WindowFindByClass(WindowClass::EditorInventionListDrag)); + if (wnd == nullptr) + { + return nullptr; + } + return &wnd->GetItem(); } - const ResearchItem& GetItem() const - { - return _draggedItem; - } -}; - -static void WindowEditorInventionsListDragOpen( - ResearchItem* researchItem, const ScreenCoordsXY& editorPos, int objectSelectionScrollWidth) -{ - WindowCloseByClass(WindowClass::EditorInventionListDrag); - auto* wnd = WindowCreate( - WindowClass::EditorInventionListDrag, 10, 14, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_SNAPPING); - if (wnd != nullptr) - { - wnd->Init(*researchItem, editorPos, objectSelectionScrollWidth); - } -} - -static const ResearchItem* WindowEditorInventionsListDragGetItem() -{ - auto* wnd = static_cast(WindowFindByClass(WindowClass::EditorInventionListDrag)); - if (wnd == nullptr) - { - return nullptr; - } - return &wnd->GetItem(); -} - #pragma endregion +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/EditorObjectSelection.cpp b/src/openrct2-ui/windows/EditorObjectSelection.cpp index b94649d3d9..3aa91da3df 100644 --- a/src/openrct2-ui/windows/EditorObjectSelection.cpp +++ b/src/openrct2-ui/windows/EditorObjectSelection.cpp @@ -42,69 +42,69 @@ #include #include -using namespace OpenRCT2; - -enum +namespace OpenRCT2::Ui::Windows { - FILTER_RCT1 = (1 << 0), - FILTER_AA = (1 << 1), - FILTER_LL = (1 << 2), - FILTER_RCT2 = (1 << 3), - FILTER_WW = (1 << 4), - FILTER_TT = (1 << 5), - FILTER_OO = (1 << 6), - FILTER_CUSTOM = (1 << 7), + enum + { + FILTER_RCT1 = (1 << 0), + FILTER_AA = (1 << 1), + FILTER_LL = (1 << 2), + FILTER_RCT2 = (1 << 3), + FILTER_WW = (1 << 4), + FILTER_TT = (1 << 5), + FILTER_OO = (1 << 6), + FILTER_CUSTOM = (1 << 7), - FILTER_RIDE_TRANSPORT = (1 << 8), - FILTER_RIDE_GENTLE = (1 << 9), - FILTER_RIDE_COASTER = (1 << 10), - FILTER_RIDE_THRILL = (1 << 11), - FILTER_RIDE_WATER = (1 << 12), - FILTER_RIDE_STALL = (1 << 13), + FILTER_RIDE_TRANSPORT = (1 << 8), + FILTER_RIDE_GENTLE = (1 << 9), + FILTER_RIDE_COASTER = (1 << 10), + FILTER_RIDE_THRILL = (1 << 11), + FILTER_RIDE_WATER = (1 << 12), + FILTER_RIDE_STALL = (1 << 13), - FILTER_SELECTED = (1 << 14), - FILTER_NONSELECTED = (1 << 15), + FILTER_SELECTED = (1 << 14), + FILTER_NONSELECTED = (1 << 15), - FILTER_RIDES = FILTER_RIDE_TRANSPORT | FILTER_RIDE_GENTLE | FILTER_RIDE_COASTER | FILTER_RIDE_THRILL | FILTER_RIDE_WATER - | FILTER_RIDE_STALL, - FILTER_ALL = FILTER_RIDES | FILTER_RCT1 | FILTER_AA | FILTER_LL | FILTER_RCT2 | FILTER_WW | FILTER_TT | FILTER_OO - | FILTER_CUSTOM | FILTER_SELECTED | FILTER_NONSELECTED, -}; + FILTER_RIDES = FILTER_RIDE_TRANSPORT | FILTER_RIDE_GENTLE | FILTER_RIDE_COASTER | FILTER_RIDE_THRILL | FILTER_RIDE_WATER + | FILTER_RIDE_STALL, + FILTER_ALL = FILTER_RIDES | FILTER_RCT1 | FILTER_AA | FILTER_LL | FILTER_RCT2 | FILTER_WW | FILTER_TT | FILTER_OO + | FILTER_CUSTOM | FILTER_SELECTED | FILTER_NONSELECTED, + }; -enum -{ - RIDE_SORT_TYPE, - RIDE_SORT_RIDE -}; + enum + { + RIDE_SORT_TYPE, + RIDE_SORT_RIDE + }; -enum -{ - DDIX_FILTER_RCT1, - DDIX_FILTER_AA, - DDIX_FILTER_LL, - DDIX_FILTER_RCT2, - DDIX_FILTER_WW, - DDIX_FILTER_TT, - DDIX_FILTER_OO, - DDIX_FILTER_CUSTOM, - DDIX_FILTER_SEPARATOR, - DDIX_FILTER_SELECTED, - DDIX_FILTER_NONSELECTED, -}; + enum + { + DDIX_FILTER_RCT1, + DDIX_FILTER_AA, + DDIX_FILTER_LL, + DDIX_FILTER_RCT2, + DDIX_FILTER_WW, + DDIX_FILTER_TT, + DDIX_FILTER_OO, + DDIX_FILTER_CUSTOM, + DDIX_FILTER_SEPARATOR, + DDIX_FILTER_SELECTED, + DDIX_FILTER_NONSELECTED, + }; -struct ObjectListItem -{ - const ObjectRepositoryItem* repositoryItem; - std::unique_ptr filter; - uint8_t* flags; -}; + struct ObjectListItem + { + const ObjectRepositoryItem* repositoryItem; + std::unique_ptr filter; + uint8_t* flags; + }; -static constexpr uint8_t _numSourceGameItems = 8; + static constexpr uint8_t _numSourceGameItems = 8; -static uint32_t _filter_flags; -static uint16_t _filter_object_counts[EnumValue(ObjectType::Count)]; + static uint32_t _filter_flags; + static uint16_t _filter_object_counts[EnumValue(ObjectType::Count)]; -static char _filter_string[MAX_PATH]; + static char _filter_string[MAX_PATH]; #define _FILTER_ALL ((_filter_flags & FILTER_ALL) == FILTER_ALL) #define _FILTER_RCT1 (_filter_flags & FILTER_RCT1) @@ -118,18 +118,18 @@ static char _filter_string[MAX_PATH]; #define _FILTER_SELECTED (_filter_flags & FILTER_SELECTED) #define _FILTER_NONSELECTED (_filter_flags & FILTER_NONSELECTED) -static constexpr StringId WINDOW_TITLE = STR_OBJECT_SELECTION; -static constexpr int32_t WH = 400; -static constexpr int32_t WW = 755; + static constexpr StringId WINDOW_TITLE = STR_OBJECT_SELECTION; + static constexpr int32_t WH = 400; + static constexpr int32_t WW = 755; -struct ObjectPageDesc -{ - StringId Caption; - uint32_t Image; - bool IsAdvanced; -}; + struct ObjectPageDesc + { + StringId Caption; + uint32_t Image; + bool IsAdvanced; + }; -// clang-format off + // clang-format off // Order of which the object tabs are displayed. static constexpr ObjectPageDesc ObjectSelectionPages[] = { { STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS, SPR_TAB_RIDE_16, false }, @@ -149,52 +149,52 @@ static constexpr ObjectPageDesc ObjectSelectionPages[] = { { STR_OBJECT_SELECTION_TERRAIN_EDGES, SPR_G2_TERRAIN_EDGE_TAB, true }, { STR_OBJECT_SELECTION_WATER, SPR_TAB_WATER, false }, }; -// clang-format on + // clang-format on -// Order of which the contents of each tab is displayed. -ObjectType static TabOrder[] = { - ObjectType::Ride, ObjectType::Station, ObjectType::Music, - ObjectType::SceneryGroup, ObjectType::SmallScenery, ObjectType::LargeScenery, - ObjectType::Walls, ObjectType::FootpathSurface, ObjectType::FootpathRailings, - ObjectType::Paths, ObjectType::PathAdditions, ObjectType::Banners, - ObjectType::ParkEntrance, ObjectType::TerrainSurface, ObjectType::TerrainEdge, - ObjectType::Water, -}; + // Order of which the contents of each tab is displayed. + ObjectType static TabOrder[] = { + ObjectType::Ride, ObjectType::Station, ObjectType::Music, + ObjectType::SceneryGroup, ObjectType::SmallScenery, ObjectType::LargeScenery, + ObjectType::Walls, ObjectType::FootpathSurface, ObjectType::FootpathRailings, + ObjectType::Paths, ObjectType::PathAdditions, ObjectType::Banners, + ObjectType::ParkEntrance, ObjectType::TerrainSurface, ObjectType::TerrainEdge, + ObjectType::Water, + }; #pragma region Widgets -enum WINDOW_EDITOR_OBJECT_SELECTION_WIDGET_IDX -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_TAB_CONTENT_PANEL, - WIDX_ADVANCED, - WIDX_LIST, - WIDX_PREVIEW, - WIDX_INSTALL_TRACK, - WIDX_FILTER_DROPDOWN, - WIDX_FILTER_TEXT_BOX, - WIDX_FILTER_CLEAR_BUTTON, - WIDX_FILTER_RIDE_TAB_FRAME, - WIDX_FILTER_RIDE_TAB_ALL, - WIDX_FILTER_RIDE_TAB_TRANSPORT, - WIDX_FILTER_RIDE_TAB_GENTLE, - WIDX_FILTER_RIDE_TAB_COASTER, - WIDX_FILTER_RIDE_TAB_THRILL, - WIDX_FILTER_RIDE_TAB_WATER, - WIDX_FILTER_RIDE_TAB_STALL, - WIDX_LIST_SORT_TYPE, - WIDX_LIST_SORT_RIDE, - WIDX_RELOAD_OBJECT, - WIDX_TAB_1, -}; + enum WINDOW_EDITOR_OBJECT_SELECTION_WIDGET_IDX + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_TAB_CONTENT_PANEL, + WIDX_ADVANCED, + WIDX_LIST, + WIDX_PREVIEW, + WIDX_INSTALL_TRACK, + WIDX_FILTER_DROPDOWN, + WIDX_FILTER_TEXT_BOX, + WIDX_FILTER_CLEAR_BUTTON, + WIDX_FILTER_RIDE_TAB_FRAME, + WIDX_FILTER_RIDE_TAB_ALL, + WIDX_FILTER_RIDE_TAB_TRANSPORT, + WIDX_FILTER_RIDE_TAB_GENTLE, + WIDX_FILTER_RIDE_TAB_COASTER, + WIDX_FILTER_RIDE_TAB_THRILL, + WIDX_FILTER_RIDE_TAB_WATER, + WIDX_FILTER_RIDE_TAB_STALL, + WIDX_LIST_SORT_TYPE, + WIDX_LIST_SORT_RIDE, + WIDX_RELOAD_OBJECT, + WIDX_TAB_1, + }; -validate_global_widx(WC_EDITOR_OBJECT_SELECTION, WIDX_TAB_1); + validate_global_widx(WC_EDITOR_OBJECT_SELECTION, WIDX_TAB_1); -static bool _window_editor_object_selection_widgets_initialised; + static bool _window_editor_object_selection_widgets_initialised; -// clang-format off + // clang-format off static std::vector _window_editor_object_selection_widgets = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget({ 0, 43}, {WW, 357}, WindowWidgetType::Resize, WindowColour::Secondary ), @@ -221,1206 +221,1207 @@ static std::vector _window_editor_object_selection_widgets = { kWidgetsEnd, }; -// clang-format on + // clang-format on #pragma endregion -static constexpr int32_t window_editor_object_selection_animation_loops[] = { - 20, // All - 32, // Transport - 10, // Gentle - 72, // Coaster - 24, // Thrill - 28, // Water - 16, // Stall -}; -static constexpr int32_t window_editor_object_selection_animation_divisor[] = { - 4, // All - 8, // Transport - 2, // Gentle - 4, // Coaster - 4, // Thrill - 4, // Water - 2, // Stall -}; + static constexpr int32_t window_editor_object_selection_animation_loops[] = { + 20, // All + 32, // Transport + 10, // Gentle + 72, // Coaster + 24, // Thrill + 28, // Water + 16, // Stall + }; + static constexpr int32_t window_editor_object_selection_animation_divisor[] = { + 4, // All + 8, // Transport + 2, // Gentle + 4, // Coaster + 4, // Thrill + 4, // Water + 2, // Stall + }; -static StringId GetRideTypeStringId(const ObjectRepositoryItem* item); -static bool VisibleListSortRideType(const ObjectListItem& a, const ObjectListItem& b); -static bool VisibleListSortRideName(const ObjectListItem& a, const ObjectListItem& b); -void EditorLoadSelectedObjects(); + static StringId GetRideTypeStringId(const ObjectRepositoryItem* item); + static bool VisibleListSortRideType(const ObjectListItem& a, const ObjectListItem& b); + static bool VisibleListSortRideName(const ObjectListItem& a, const ObjectListItem& b); + void EditorLoadSelectedObjects(); -class EditorObjectSelectionWindow final : public Window -{ - using sortFunc_t = bool (*)(const ObjectListItem&, const ObjectListItem&); - -private: - std::vector _listItems; - int32_t _listSortType = RIDE_SORT_TYPE; - bool _listSortDescending = false; - std::unique_ptr _loadedObject; - -public: - /** - * - * rct2: 0x006AA64E - */ - void OnOpen() override + class EditorObjectSelectionWindow final : public Window { - InitWidgets(); + using sortFunc_t = bool (*)(const ObjectListItem&, const ObjectListItem&); - Sub6AB211(); - ResetSelectedObjectCountAndSize(); + private: + std::vector _listItems; + int32_t _listSortType = RIDE_SORT_TYPE; + bool _listSortDescending = false; + std::unique_ptr _loadedObject; - widgets[WIDX_FILTER_TEXT_BOX].string = _filter_string; - - _filter_flags = gConfigInterface.ObjectSelectionFilterFlags; - std::fill_n(_filter_string, sizeof(_filter_string), 0x00); - - WindowInitScrollWidgets(*this); - - selected_tab = 0; - selected_list_item = -1; - min_width = WW; - min_height = WH; - max_width = 1200; - max_height = 1000; - - _listSortType = RIDE_SORT_TYPE; - _listSortDescending = false; - - VisibleListRefresh(); - } - - /** - * - * rct2: 0x006AB199 - */ - void OnClose() override - { - UnloadUnselectedObjects(); - EditorLoadSelectedObjects(); - EditorObjectFlagsFree(); - - if (_loadedObject != nullptr) - _loadedObject->Unload(); - - if (gScreenFlags & SCREEN_FLAGS_EDITOR) + public: + /** + * + * rct2: 0x006AA64E + */ + void OnOpen() override { - ResearchPopulateListRandom(); - } - else - { - // Used for in-game object selection cheat - // This resets the ride selection list and resets research to 0 on current item - gSilentResearch = true; - ResearchResetCurrentItem(); - gSilentResearch = false; + InitWidgets(); + + Sub6AB211(); + ResetSelectedObjectCountAndSize(); + + widgets[WIDX_FILTER_TEXT_BOX].string = _filter_string; + + _filter_flags = gConfigInterface.ObjectSelectionFilterFlags; + std::fill_n(_filter_string, sizeof(_filter_string), 0x00); + + WindowInitScrollWidgets(*this); + + selected_tab = 0; + selected_list_item = -1; + min_width = WW; + min_height = WH; + max_width = 1200; + max_height = 1000; + + _listSortType = RIDE_SORT_TYPE; + _listSortDescending = false; + + VisibleListRefresh(); } - auto intent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES); - ContextBroadcastIntent(&intent); - - VisibleListDispose(); - - intent = Intent(INTENT_ACTION_REFRESH_SCENERY); - ContextBroadcastIntent(&intent); - } - - void OnUpdate() override - { - if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + /** + * + * rct2: 0x006AB199 + */ + void OnClose() override { - WindowUpdateTextboxCaret(); - WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX); - } - - for (WidgetIndex i = WIDX_FILTER_RIDE_TAB_TRANSPORT; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) - { - if (!IsWidgetPressed(i)) - continue; - - frame_no++; - if (frame_no >= window_editor_object_selection_animation_loops[i - WIDX_FILTER_RIDE_TAB_TRANSPORT]) - frame_no = 0; - - WidgetInvalidate(*this, i); - break; - } - } - - /** - * - * rct2: 0x006AAFAB - */ - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*this); - if (gScreenFlags & SCREEN_FLAGS_EDITOR) - { - FinishObjectSelection(); - } - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - GameNotifyMapChange(); - GameUnloadScripts(); - TitleLoad(); - } - break; - case WIDX_FILTER_RIDE_TAB_ALL: - _filter_flags |= FILTER_RIDES; - gConfigInterface.ObjectSelectionFilterFlags = _filter_flags; - ConfigSaveDefault(); - - FilterUpdateCounts(); - VisibleListRefresh(); - - selected_list_item = -1; - scrolls[0].v_top = 0; - Invalidate(); - break; - case WIDX_FILTER_RIDE_TAB_TRANSPORT: - case WIDX_FILTER_RIDE_TAB_GENTLE: - case WIDX_FILTER_RIDE_TAB_COASTER: - case WIDX_FILTER_RIDE_TAB_THRILL: - case WIDX_FILTER_RIDE_TAB_WATER: - case WIDX_FILTER_RIDE_TAB_STALL: - _filter_flags &= ~FILTER_RIDES; - _filter_flags |= (1 << (widgetIndex - WIDX_FILTER_RIDE_TAB_TRANSPORT + _numSourceGameItems)); - gConfigInterface.ObjectSelectionFilterFlags = _filter_flags; - ConfigSaveDefault(); - - FilterUpdateCounts(); - VisibleListRefresh(); - - selected_list_item = -1; - scrolls[0].v_top = 0; - frame_no = 0; - Invalidate(); - break; - - case WIDX_ADVANCED: - list_information_type ^= 1; - Invalidate(); - break; - - case WIDX_INSTALL_TRACK: - { - if (selected_list_item != -1) - { - selected_list_item = -1; - } - Invalidate(); - - auto intent = Intent(WindowClass::Loadsave); - intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK); - ContextOpenIntent(&intent); - break; - } - case WIDX_FILTER_TEXT_BOX: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _filter_string, sizeof(_filter_string)); - break; - case WIDX_FILTER_CLEAR_BUTTON: - std::fill_n(_filter_string, sizeof(_filter_string), 0x00); - FilterUpdateCounts(); - scrolls->v_top = 0; - VisibleListRefresh(); - Invalidate(); - break; - case WIDX_LIST_SORT_TYPE: - if (_listSortType == RIDE_SORT_TYPE) - { - _listSortDescending = !_listSortDescending; - } - else - { - _listSortType = RIDE_SORT_TYPE; - _listSortDescending = false; - } - VisibleListRefresh(); - break; - case WIDX_LIST_SORT_RIDE: - if (_listSortType == RIDE_SORT_RIDE) - { - _listSortDescending = !_listSortDescending; - } - else - { - _listSortType = RIDE_SORT_RIDE; - _listSortDescending = false; - } - VisibleListRefresh(); - break; - case WIDX_RELOAD_OBJECT: - if (_loadedObject != nullptr) - { - auto descriptor = _loadedObject->GetDescriptor(); - auto& objectManager = GetContext()->GetObjectManager(); - auto entryIndex = objectManager.GetLoadedObjectEntryIndex(descriptor); - if (entryIndex != OBJECT_ENTRY_INDEX_NULL) - { - objectManager.UnloadObjects({ descriptor }); - objectManager.LoadObject(descriptor, entryIndex); - } - } - break; - default: - if (widgetIndex >= WIDX_TAB_1 - && static_cast(widgetIndex) < WIDX_TAB_1 + std::size(ObjectSelectionPages)) - { - SetPage(widgetIndex - WIDX_TAB_1); - } - break; - } - } - - void OnResize() override - { - WindowSetResize(*this, WW, WH, 1200, 1000); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - int32_t numSelectionItems = 0; - - switch (widgetIndex) - { - case WIDX_FILTER_DROPDOWN: - - gDropdownItems[DDIX_FILTER_RCT1].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_AA].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_LL].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_RCT2].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_WW].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_TT].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_OO].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_CUSTOM].Format = STR_TOGGLE_OPTION; - - gDropdownItems[DDIX_FILTER_RCT1].Args = STR_SCENARIO_CATEGORY_RCT1; - gDropdownItems[DDIX_FILTER_AA].Args = STR_SCENARIO_CATEGORY_RCT1_AA; - gDropdownItems[DDIX_FILTER_LL].Args = STR_SCENARIO_CATEGORY_RCT1_LL; - gDropdownItems[DDIX_FILTER_RCT2].Args = STR_ROLLERCOASTER_TYCOON_2_DROPDOWN; - gDropdownItems[DDIX_FILTER_WW].Args = STR_OBJECT_FILTER_WW; - gDropdownItems[DDIX_FILTER_TT].Args = STR_OBJECT_FILTER_TT; - gDropdownItems[DDIX_FILTER_OO].Args = STR_OBJECT_FILTER_OPENRCT2_OFFICIAL; - gDropdownItems[DDIX_FILTER_CUSTOM].Args = STR_OBJECT_FILTER_CUSTOM; - - // Track manager cannot select multiple, so only show selection filters if not in track manager - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - numSelectionItems = 3; - gDropdownItems[DDIX_FILTER_SEPARATOR].Format = 0; - gDropdownItems[DDIX_FILTER_SELECTED].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_NONSELECTED].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIX_FILTER_SEPARATOR].Args = STR_NONE; - gDropdownItems[DDIX_FILTER_SELECTED].Args = STR_SELECTED_ONLY; - gDropdownItems[DDIX_FILTER_NONSELECTED].Args = STR_NON_SELECTED_ONLY; - } - - WindowDropdownShowText( - { windowPos.x + widgets[widgetIndex].left, windowPos.y + widgets[widgetIndex].top }, - widgets[widgetIndex].height() + 1, colours[widgets[widgetIndex].colour], Dropdown::Flag::StayOpen, - _numSourceGameItems + numSelectionItems); - - for (int32_t i = 0; i < _numSourceGameItems; i++) - { - if (_filter_flags & (1 << i)) - { - Dropdown::SetChecked(i, true); - } - } - - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - Dropdown::SetChecked(DDIX_FILTER_SELECTED, _FILTER_SELECTED != 0); - Dropdown::SetChecked(DDIX_FILTER_NONSELECTED, _FILTER_NONSELECTED != 0); - } - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (dropdownIndex == -1) - return; - - switch (widgetIndex) - { - case WIDX_FILTER_DROPDOWN: - if (dropdownIndex == DDIX_FILTER_SELECTED) - { - _filter_flags ^= FILTER_SELECTED; - _filter_flags &= ~FILTER_NONSELECTED; - } - else if (dropdownIndex == DDIX_FILTER_NONSELECTED) - { - _filter_flags ^= FILTER_NONSELECTED; - _filter_flags &= ~FILTER_SELECTED; - } - else - { - _filter_flags ^= (1 << dropdownIndex); - } - gConfigInterface.ObjectSelectionFilterFlags = _filter_flags; - ConfigSaveDefault(); - - FilterUpdateCounts(); - scrolls->v_top = 0; - - VisibleListRefresh(); - Invalidate(); - break; - } - } - - /** - * - * rct2: 0x006AB031 - */ - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - const auto newHeight = static_cast(_listItems.size() * SCROLLABLE_ROW_HEIGHT); - return { 0, newHeight }; - } - - /** - * - * rct2: 0x006AB0B6 - */ - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - // Used for in-game object selection cheat to prevent crashing the game - // when windows attempt to draw objects that don't exist any more - WindowCloseAllExceptClass(WindowClass::EditorObjectSelection); - - int32_t selected_object = GetObjectFromObjectSelection(GetSelectedObjectType(), screenCoords.y); - if (selected_object == -1) - return; - - ObjectListItem* listItem = &_listItems[selected_object]; - uint8_t object_selection_flags = *listItem->flags; - if (object_selection_flags & ObjectSelectionFlags::Flag6) - return; - - Invalidate(); - - const CursorState* state = ContextGetCursorState(); - Audio::Play(Audio::SoundId::Click1, 0, state->position.x); - - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - const auto objectSelectResult = WindowEditorObjectSelectionSelectObject( - 0, INPUT_FLAG_EDITOR_OBJECT_SELECT, listItem->repositoryItem); - if (!objectSelectResult.Successful) - return; + UnloadUnselectedObjects(); + EditorLoadSelectedObjects(); + EditorObjectFlagsFree(); if (_loadedObject != nullptr) - { _loadedObject->Unload(); - _loadedObject = nullptr; + + if (gScreenFlags & SCREEN_FLAGS_EDITOR) + { + ResearchPopulateListRandom(); + } + else + { + // Used for in-game object selection cheat + // This resets the ride selection list and resets research to 0 on current item + gSilentResearch = true; + ResearchResetCurrentItem(); + gSilentResearch = false; } - auto& objRepository = GetContext()->GetObjectRepository(); - _loadedObject = objRepository.LoadObject(listItem->repositoryItem); - auto& objManager = GetContext()->GetObjectManager(); - objManager.LoadObject(_loadedObject.get()->GetIdentifier()); + auto intent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES); + ContextBroadcastIntent(&intent); - // This function calls window_track_list_open - ManageTracks(); - Close(); - return; + VisibleListDispose(); + + intent = Intent(INTENT_ACTION_REFRESH_SCENERY); + ContextBroadcastIntent(&intent); } - uint32_t inputFlags = INPUT_FLAG_EDITOR_OBJECT_1 | INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP; - // If already selected - if (!(object_selection_flags & ObjectSelectionFlags::Selected)) - inputFlags |= INPUT_FLAG_EDITOR_OBJECT_SELECT; - - _gSceneryGroupPartialSelectError = std::nullopt; - const auto objectSelectResult = WindowEditorObjectSelectionSelectObject(0, inputFlags, listItem->repositoryItem); - if (!objectSelectResult.Successful) + void OnUpdate() override { - StringId error_title = (inputFlags & INPUT_FLAG_EDITOR_OBJECT_SELECT) ? STR_UNABLE_TO_SELECT_THIS_OBJECT - : STR_UNABLE_TO_DE_SELECT_THIS_OBJECT; + if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + { + WindowUpdateTextboxCaret(); + WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX); + } - ContextShowError(error_title, objectSelectResult.Message, {}); - return; + for (WidgetIndex i = WIDX_FILTER_RIDE_TAB_TRANSPORT; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) + { + if (!IsWidgetPressed(i)) + continue; + + frame_no++; + if (frame_no >= window_editor_object_selection_animation_loops[i - WIDX_FILTER_RIDE_TAB_TRANSPORT]) + frame_no = 0; + + WidgetInvalidate(*this, i); + break; + } } - if (_FILTER_SELECTED || _FILTER_NONSELECTED) + /** + * + * rct2: 0x006AAFAB + */ + void OnMouseUp(WidgetIndex widgetIndex) override { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*this); + if (gScreenFlags & SCREEN_FLAGS_EDITOR) + { + FinishObjectSelection(); + } + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + GameNotifyMapChange(); + GameUnloadScripts(); + TitleLoad(); + } + break; + case WIDX_FILTER_RIDE_TAB_ALL: + _filter_flags |= FILTER_RIDES; + gConfigInterface.ObjectSelectionFilterFlags = _filter_flags; + ConfigSaveDefault(); + + FilterUpdateCounts(); + VisibleListRefresh(); + + selected_list_item = -1; + scrolls[0].v_top = 0; + Invalidate(); + break; + case WIDX_FILTER_RIDE_TAB_TRANSPORT: + case WIDX_FILTER_RIDE_TAB_GENTLE: + case WIDX_FILTER_RIDE_TAB_COASTER: + case WIDX_FILTER_RIDE_TAB_THRILL: + case WIDX_FILTER_RIDE_TAB_WATER: + case WIDX_FILTER_RIDE_TAB_STALL: + _filter_flags &= ~FILTER_RIDES; + _filter_flags |= (1 << (widgetIndex - WIDX_FILTER_RIDE_TAB_TRANSPORT + _numSourceGameItems)); + gConfigInterface.ObjectSelectionFilterFlags = _filter_flags; + ConfigSaveDefault(); + + FilterUpdateCounts(); + VisibleListRefresh(); + + selected_list_item = -1; + scrolls[0].v_top = 0; + frame_no = 0; + Invalidate(); + break; + + case WIDX_ADVANCED: + list_information_type ^= 1; + Invalidate(); + break; + + case WIDX_INSTALL_TRACK: + { + if (selected_list_item != -1) + { + selected_list_item = -1; + } + Invalidate(); + + auto intent = Intent(WindowClass::Loadsave); + intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK); + ContextOpenIntent(&intent); + break; + } + case WIDX_FILTER_TEXT_BOX: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _filter_string, sizeof(_filter_string)); + break; + case WIDX_FILTER_CLEAR_BUTTON: + std::fill_n(_filter_string, sizeof(_filter_string), 0x00); + FilterUpdateCounts(); + scrolls->v_top = 0; + VisibleListRefresh(); + Invalidate(); + break; + case WIDX_LIST_SORT_TYPE: + if (_listSortType == RIDE_SORT_TYPE) + { + _listSortDescending = !_listSortDescending; + } + else + { + _listSortType = RIDE_SORT_TYPE; + _listSortDescending = false; + } + VisibleListRefresh(); + break; + case WIDX_LIST_SORT_RIDE: + if (_listSortType == RIDE_SORT_RIDE) + { + _listSortDescending = !_listSortDescending; + } + else + { + _listSortType = RIDE_SORT_RIDE; + _listSortDescending = false; + } + VisibleListRefresh(); + break; + case WIDX_RELOAD_OBJECT: + if (_loadedObject != nullptr) + { + auto descriptor = _loadedObject->GetDescriptor(); + auto& objectManager = GetContext()->GetObjectManager(); + auto entryIndex = objectManager.GetLoadedObjectEntryIndex(descriptor); + if (entryIndex != OBJECT_ENTRY_INDEX_NULL) + { + objectManager.UnloadObjects({ descriptor }); + objectManager.LoadObject(descriptor, entryIndex); + } + } + break; + default: + if (widgetIndex >= WIDX_TAB_1 + && static_cast(widgetIndex) < WIDX_TAB_1 + std::size(ObjectSelectionPages)) + { + SetPage(widgetIndex - WIDX_TAB_1); + } + break; + } + } + + void OnResize() override + { + WindowSetResize(*this, WW, WH, 1200, 1000); + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + int32_t numSelectionItems = 0; + + switch (widgetIndex) + { + case WIDX_FILTER_DROPDOWN: + + gDropdownItems[DDIX_FILTER_RCT1].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_AA].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_LL].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_RCT2].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_WW].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_TT].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_OO].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_CUSTOM].Format = STR_TOGGLE_OPTION; + + gDropdownItems[DDIX_FILTER_RCT1].Args = STR_SCENARIO_CATEGORY_RCT1; + gDropdownItems[DDIX_FILTER_AA].Args = STR_SCENARIO_CATEGORY_RCT1_AA; + gDropdownItems[DDIX_FILTER_LL].Args = STR_SCENARIO_CATEGORY_RCT1_LL; + gDropdownItems[DDIX_FILTER_RCT2].Args = STR_ROLLERCOASTER_TYCOON_2_DROPDOWN; + gDropdownItems[DDIX_FILTER_WW].Args = STR_OBJECT_FILTER_WW; + gDropdownItems[DDIX_FILTER_TT].Args = STR_OBJECT_FILTER_TT; + gDropdownItems[DDIX_FILTER_OO].Args = STR_OBJECT_FILTER_OPENRCT2_OFFICIAL; + gDropdownItems[DDIX_FILTER_CUSTOM].Args = STR_OBJECT_FILTER_CUSTOM; + + // Track manager cannot select multiple, so only show selection filters if not in track manager + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + { + numSelectionItems = 3; + gDropdownItems[DDIX_FILTER_SEPARATOR].Format = 0; + gDropdownItems[DDIX_FILTER_SELECTED].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_NONSELECTED].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIX_FILTER_SEPARATOR].Args = STR_NONE; + gDropdownItems[DDIX_FILTER_SELECTED].Args = STR_SELECTED_ONLY; + gDropdownItems[DDIX_FILTER_NONSELECTED].Args = STR_NON_SELECTED_ONLY; + } + + WindowDropdownShowText( + { windowPos.x + widgets[widgetIndex].left, windowPos.y + widgets[widgetIndex].top }, + widgets[widgetIndex].height() + 1, colours[widgets[widgetIndex].colour], Dropdown::Flag::StayOpen, + _numSourceGameItems + numSelectionItems); + + for (int32_t i = 0; i < _numSourceGameItems; i++) + { + if (_filter_flags & (1 << i)) + { + Dropdown::SetChecked(i, true); + } + } + + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + { + Dropdown::SetChecked(DDIX_FILTER_SELECTED, _FILTER_SELECTED != 0); + Dropdown::SetChecked(DDIX_FILTER_NONSELECTED, _FILTER_NONSELECTED != 0); + } + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (dropdownIndex == -1) + return; + + switch (widgetIndex) + { + case WIDX_FILTER_DROPDOWN: + if (dropdownIndex == DDIX_FILTER_SELECTED) + { + _filter_flags ^= FILTER_SELECTED; + _filter_flags &= ~FILTER_NONSELECTED; + } + else if (dropdownIndex == DDIX_FILTER_NONSELECTED) + { + _filter_flags ^= FILTER_NONSELECTED; + _filter_flags &= ~FILTER_SELECTED; + } + else + { + _filter_flags ^= (1 << dropdownIndex); + } + gConfigInterface.ObjectSelectionFilterFlags = _filter_flags; + ConfigSaveDefault(); + + FilterUpdateCounts(); + scrolls->v_top = 0; + + VisibleListRefresh(); + Invalidate(); + break; + } + } + + /** + * + * rct2: 0x006AB031 + */ + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + const auto newHeight = static_cast(_listItems.size() * SCROLLABLE_ROW_HEIGHT); + return { 0, newHeight }; + } + + /** + * + * rct2: 0x006AB0B6 + */ + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + // Used for in-game object selection cheat to prevent crashing the game + // when windows attempt to draw objects that don't exist any more + WindowCloseAllExceptClass(WindowClass::EditorObjectSelection); + + int32_t selected_object = GetObjectFromObjectSelection(GetSelectedObjectType(), screenCoords.y); + if (selected_object == -1) + return; + + ObjectListItem* listItem = &_listItems[selected_object]; + uint8_t object_selection_flags = *listItem->flags; + if (object_selection_flags & ObjectSelectionFlags::Flag6) + return; + + Invalidate(); + + const CursorState* state = ContextGetCursorState(); + Audio::Play(Audio::SoundId::Click1, 0, state->position.x); + + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + const auto objectSelectResult = WindowEditorObjectSelectionSelectObject( + 0, INPUT_FLAG_EDITOR_OBJECT_SELECT, listItem->repositoryItem); + if (!objectSelectResult.Successful) + return; + + if (_loadedObject != nullptr) + { + _loadedObject->Unload(); + _loadedObject = nullptr; + } + + auto& objRepository = GetContext()->GetObjectRepository(); + _loadedObject = objRepository.LoadObject(listItem->repositoryItem); + auto& objManager = GetContext()->GetObjectManager(); + objManager.LoadObject(_loadedObject.get()->GetIdentifier()); + + // This function calls window_track_list_open + ManageTracks(); + Close(); + return; + } + + uint32_t inputFlags = INPUT_FLAG_EDITOR_OBJECT_1 | INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP; + // If already selected + if (!(object_selection_flags & ObjectSelectionFlags::Selected)) + inputFlags |= INPUT_FLAG_EDITOR_OBJECT_SELECT; + + _gSceneryGroupPartialSelectError = std::nullopt; + const auto objectSelectResult = WindowEditorObjectSelectionSelectObject(0, inputFlags, listItem->repositoryItem); + if (!objectSelectResult.Successful) + { + StringId error_title = (inputFlags & INPUT_FLAG_EDITOR_OBJECT_SELECT) ? STR_UNABLE_TO_SELECT_THIS_OBJECT + : STR_UNABLE_TO_DE_SELECT_THIS_OBJECT; + + ContextShowError(error_title, objectSelectResult.Message, {}); + return; + } + + if (_FILTER_SELECTED || _FILTER_NONSELECTED) + { + FilterUpdateCounts(); + VisibleListRefresh(); + Invalidate(); + } + + if (_gSceneryGroupPartialSelectError.has_value()) + { + const auto errorMessage = _gSceneryGroupPartialSelectError.value(); + if (errorMessage == STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED) + { + ContextShowError( + STR_WARNING_TOO_MANY_OBJECTS_SELECTED, STR_NOT_ALL_OBJECTS_IN_THIS_SCENERY_GROUP_COULD_BE_SELECTED, {}); + } + else + { + ContextShowError( + errorMessage, STR_NOT_ALL_OBJECTS_IN_THIS_SCENERY_GROUP_COULD_BE_SELECTED, Formatter::Common()); + } + } + } + + /** + * + * rct2: 0x006AB079 + */ + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int32_t selectedObject = GetObjectFromObjectSelection(GetSelectedObjectType(), screenCoords.y); + if (selectedObject != -1) + { + ObjectListItem* listItem = &_listItems[selectedObject]; + uint8_t objectSelectionFlags = *listItem->flags; + if (objectSelectionFlags & ObjectSelectionFlags::Flag6) + { + selectedObject = -1; + } + } + if (selectedObject != selected_list_item) + { + selected_list_item = selectedObject; + + if (_loadedObject != nullptr) + { + _loadedObject->Unload(); + _loadedObject = nullptr; + } + + if (selectedObject != -1) + { + auto listItem = &_listItems[selectedObject]; + auto& objRepository = GetContext()->GetObjectRepository(); + _loadedObject = objRepository.LoadObject(listItem->repositoryItem); + if (_loadedObject != nullptr) + { + _loadedObject->Load(); + } + } + + Invalidate(); + } + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + // ScrollPaint + ScreenCoordsXY screenCoords; + bool ridePage = (GetSelectedObjectType() == ObjectType::Ride); + + uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; + GfxClear(&dpi, paletteIndex); + + screenCoords.y = 0; + for (size_t i = 0; i < _listItems.size(); i++) + { + const auto& listItem = _listItems[i]; + if (screenCoords.y + SCROLLABLE_ROW_HEIGHT >= dpi.y && screenCoords.y <= dpi.y + dpi.height) + { + // Draw checkbox + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && !(*listItem.flags & 0x20)) + GfxFillRectInset( + dpi, { { 2, screenCoords.y }, { 11, screenCoords.y + 10 } }, colours[1], INSET_RECT_F_E0); + + // Highlight background + auto highlighted = i == static_cast(selected_list_item) + && !(*listItem.flags & ObjectSelectionFlags::Flag6); + if (highlighted) + { + auto bottom = screenCoords.y + (SCROLLABLE_ROW_HEIGHT - 1); + GfxFilterRect(dpi, { 0, screenCoords.y, width, bottom }, FilterPaletteID::PaletteDarken1); + } + + // Draw checkmark + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && (*listItem.flags & ObjectSelectionFlags::Selected)) + { + screenCoords.x = 2; + auto darkness = highlighted ? TextDarkness::ExtraDark : TextDarkness::Dark; + colour_t colour2 = NOT_TRANSLUCENT(colours[1]); + if (*listItem.flags & (ObjectSelectionFlags::InUse | ObjectSelectionFlags::AlwaysRequired)) + colour2 |= COLOUR_FLAG_INSET; + + GfxDrawString( + dpi, screenCoords, static_cast(CheckBoxMarkString), + { static_cast(colour2), FontStyle::Medium, darkness }); + } + + screenCoords.x = gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? 0 : 15; + + auto bufferWithColour = strcpy(gCommonStringFormatBuffer, highlighted ? "{WINDOW_COLOUR_2}" : "{BLACK}"); + auto buffer = strchr(bufferWithColour, '\0'); + + colour_t colour = COLOUR_BLACK; + auto darkness = TextDarkness::Regular; + if (*listItem.flags & ObjectSelectionFlags::Flag6) + { + colour = colours[1] & 0x7F; + darkness = TextDarkness::Dark; + } + + int32_t width_limit = widgets[WIDX_LIST].width() - screenCoords.x; + + if (ridePage) + { + width_limit /= 2; + // Draw ride type + StringId rideTypeStringId = GetRideTypeStringId(listItem.repositoryItem); + SafeStrCpy(buffer, LanguageGetString(rideTypeStringId), 256 - (buffer - bufferWithColour)); + auto ft = Formatter(); + ft.Add(gCommonStringFormatBuffer); + DrawTextEllipsised( + dpi, screenCoords, width_limit - 15, STR_STRING, ft, { colour, FontStyle::Medium, darkness }); + screenCoords.x = widgets[WIDX_LIST_SORT_RIDE].left - widgets[WIDX_LIST].left; + } + + // Draw text + SafeStrCpy(buffer, listItem.repositoryItem->Name.c_str(), 256 - (buffer - bufferWithColour)); + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + while (*buffer != 0 && *buffer != 9) + buffer++; + + *buffer = 0; + } + auto ft = Formatter(); + ft.Add(gCommonStringFormatBuffer); + DrawTextEllipsised(dpi, screenCoords, width_limit, STR_STRING, ft, { colour, FontStyle::Medium, darkness }); + } + screenCoords.y += SCROLLABLE_ROW_HEIGHT; + } + } + + /** + * + * rct2: 0x006AB058 + */ + OpenRCT2String OnTooltip(const WidgetIndex widgetIndex, const StringId fallback) override + { + if (widgetIndex >= WIDX_TAB_1 && static_cast(widgetIndex) < WIDX_TAB_1 + std::size(ObjectSelectionPages)) + { + auto ft = Formatter(); + ft.Add(ObjectSelectionPages[(widgetIndex - WIDX_TAB_1)].Caption); + return { fallback, ft }; + } + return { fallback, {} }; + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (widgetIndex != WIDX_FILTER_TEXT_BOX) + return; + + std::string tempText = text.data(); + const char* c = tempText.c_str(); + + if (strcmp(_filter_string, c) == 0) + return; + + SafeStrCpy(_filter_string, c, sizeof(_filter_string)); + FilterUpdateCounts(); + + scrolls->v_top = 0; + VisibleListRefresh(); Invalidate(); } - if (_gSceneryGroupPartialSelectError.has_value()) + void OnPrepareDraw() override { - const auto errorMessage = _gSceneryGroupPartialSelectError.value(); - if (errorMessage == STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED) + // Resize widgets + ResizeFrameWithPage(); + widgets[WIDX_ADVANCED].left = width - 130; + widgets[WIDX_ADVANCED].right = width - 9; + widgets[WIDX_LIST].right = width - 309; + widgets[WIDX_LIST].bottom = height - 14; + widgets[WIDX_PREVIEW].left = width - 209; + widgets[WIDX_PREVIEW].right = width - 96; + widgets[WIDX_INSTALL_TRACK].left = width - 130; + widgets[WIDX_INSTALL_TRACK].right = width - 9; + widgets[WIDX_FILTER_DROPDOWN].left = width - 250; + widgets[WIDX_FILTER_DROPDOWN].right = width - 137; + widgets[WIDX_RELOAD_OBJECT].left = width - 9 - 24; + widgets[WIDX_RELOAD_OBJECT].right = width - 9; + + // Set pressed widgets + pressed_widgets |= 1uLL << WIDX_PREVIEW; + SetPressedTab(); + if (list_information_type & 1) + pressed_widgets |= (1uLL << WIDX_ADVANCED); + else + pressed_widgets &= ~(1uLL << WIDX_ADVANCED); + + // Set window title and buttons + auto ft = Formatter::Common(); + ft.Add(ObjectSelectionPages[selected_tab].Caption); + auto& titleWidget = widgets[WIDX_TITLE]; + auto& installTrackWidget = widgets[WIDX_INSTALL_TRACK]; + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { - ContextShowError( - STR_WARNING_TOO_MANY_OBJECTS_SELECTED, STR_NOT_ALL_OBJECTS_IN_THIS_SCENERY_GROUP_COULD_BE_SELECTED, {}); + titleWidget.text = STR_TRACK_DESIGNS_MANAGER_SELECT_RIDE_TYPE; + installTrackWidget.type = WindowWidgetType::Button; + } + else if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + { + titleWidget.text = STR_ROLLER_COASTER_DESIGNER_SELECT_RIDE_TYPES_VEHICLES; + installTrackWidget.type = WindowWidgetType::Empty; } else { - ContextShowError( - errorMessage, STR_NOT_ALL_OBJECTS_IN_THIS_SCENERY_GROUP_COULD_BE_SELECTED, Formatter::Common()); - } - } - } - - /** - * - * rct2: 0x006AB079 - */ - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - int32_t selectedObject = GetObjectFromObjectSelection(GetSelectedObjectType(), screenCoords.y); - if (selectedObject != -1) - { - ObjectListItem* listItem = &_listItems[selectedObject]; - uint8_t objectSelectionFlags = *listItem->flags; - if (objectSelectionFlags & ObjectSelectionFlags::Flag6) - { - selectedObject = -1; - } - } - if (selectedObject != selected_list_item) - { - selected_list_item = selectedObject; - - if (_loadedObject != nullptr) - { - _loadedObject->Unload(); - _loadedObject = nullptr; + titleWidget.text = STR_OBJECT_SELECTION; + installTrackWidget.type = WindowWidgetType::Empty; } - if (selectedObject != -1) + // Align tabs, hide advanced ones + bool advancedMode = (list_information_type & 1) != 0; + int32_t x = 3; + for (size_t i = 0; i < std::size(ObjectSelectionPages); i++) { - auto listItem = &_listItems[selectedObject]; - auto& objRepository = GetContext()->GetObjectRepository(); - _loadedObject = objRepository.LoadObject(listItem->repositoryItem); - if (_loadedObject != nullptr) + auto& widget = widgets[WIDX_TAB_1 + i]; + if ((!advancedMode && ObjectSelectionPages[i].IsAdvanced) + || ObjectSelectionPages[i].Image == static_cast(SPR_NONE)) { - _loadedObject->Load(); + widget.type = WindowWidgetType::Empty; + } + else + { + widget.type = WindowWidgetType::Tab; + widget.left = x; + widget.right = x + 30; + x += 31; } } - Invalidate(); - } - } + if (gConfigGeneral.DebuggingTools) + widgets[WIDX_RELOAD_OBJECT].type = WindowWidgetType::ImgBtn; + else + widgets[WIDX_RELOAD_OBJECT].type = WindowWidgetType::Empty; - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - // ScrollPaint - ScreenCoordsXY screenCoords; - bool ridePage = (GetSelectedObjectType() == ObjectType::Ride); - - uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; - GfxClear(&dpi, paletteIndex); - - screenCoords.y = 0; - for (size_t i = 0; i < _listItems.size(); i++) - { - const auto& listItem = _listItems[i]; - if (screenCoords.y + SCROLLABLE_ROW_HEIGHT >= dpi.y && screenCoords.y <= dpi.y + dpi.height) + if (gScreenFlags & (SCREEN_FLAGS_TRACK_MANAGER | SCREEN_FLAGS_TRACK_DESIGNER)) { - // Draw checkbox - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && !(*listItem.flags & 0x20)) - GfxFillRectInset(dpi, { { 2, screenCoords.y }, { 11, screenCoords.y + 10 } }, colours[1], INSET_RECT_F_E0); - - // Highlight background - auto highlighted = i == static_cast(selected_list_item) - && !(*listItem.flags & ObjectSelectionFlags::Flag6); - if (highlighted) + widgets[WIDX_ADVANCED].type = WindowWidgetType::Empty; + for (size_t i = 1; i < std::size(ObjectSelectionPages); i++) { - auto bottom = screenCoords.y + (SCROLLABLE_ROW_HEIGHT - 1); - GfxFilterRect(dpi, { 0, screenCoords.y, width, bottom }, FilterPaletteID::PaletteDarken1); + widgets[WIDX_TAB_1 + i].type = WindowWidgetType::Empty; } - - // Draw checkmark - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && (*listItem.flags & ObjectSelectionFlags::Selected)) - { - screenCoords.x = 2; - auto darkness = highlighted ? TextDarkness::ExtraDark : TextDarkness::Dark; - colour_t colour2 = NOT_TRANSLUCENT(colours[1]); - if (*listItem.flags & (ObjectSelectionFlags::InUse | ObjectSelectionFlags::AlwaysRequired)) - colour2 |= COLOUR_FLAG_INSET; - - GfxDrawString( - dpi, screenCoords, static_cast(CheckBoxMarkString), - { static_cast(colour2), FontStyle::Medium, darkness }); - } - - screenCoords.x = gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? 0 : 15; - - auto bufferWithColour = strcpy(gCommonStringFormatBuffer, highlighted ? "{WINDOW_COLOUR_2}" : "{BLACK}"); - auto buffer = strchr(bufferWithColour, '\0'); - - colour_t colour = COLOUR_BLACK; - auto darkness = TextDarkness::Regular; - if (*listItem.flags & ObjectSelectionFlags::Flag6) - { - colour = colours[1] & 0x7F; - darkness = TextDarkness::Dark; - } - - int32_t width_limit = widgets[WIDX_LIST].width() - screenCoords.x; - - if (ridePage) - { - width_limit /= 2; - // Draw ride type - StringId rideTypeStringId = GetRideTypeStringId(listItem.repositoryItem); - SafeStrCpy(buffer, LanguageGetString(rideTypeStringId), 256 - (buffer - bufferWithColour)); - auto ft = Formatter(); - ft.Add(gCommonStringFormatBuffer); - DrawTextEllipsised( - dpi, screenCoords, width_limit - 15, STR_STRING, ft, { colour, FontStyle::Medium, darkness }); - screenCoords.x = widgets[WIDX_LIST_SORT_RIDE].left - widgets[WIDX_LIST].left; - } - - // Draw text - SafeStrCpy(buffer, listItem.repositoryItem->Name.c_str(), 256 - (buffer - bufferWithColour)); - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - while (*buffer != 0 && *buffer != 9) - buffer++; - - *buffer = 0; - } - auto ft = Formatter(); - ft.Add(gCommonStringFormatBuffer); - DrawTextEllipsised(dpi, screenCoords, width_limit, STR_STRING, ft, { colour, FontStyle::Medium, darkness }); - } - screenCoords.y += SCROLLABLE_ROW_HEIGHT; - } - } - - /** - * - * rct2: 0x006AB058 - */ - OpenRCT2String OnTooltip(const WidgetIndex widgetIndex, const StringId fallback) override - { - if (widgetIndex >= WIDX_TAB_1 && static_cast(widgetIndex) < WIDX_TAB_1 + std::size(ObjectSelectionPages)) - { - auto ft = Formatter(); - ft.Add(ObjectSelectionPages[(widgetIndex - WIDX_TAB_1)].Caption); - return { fallback, ft }; - } - return { fallback, {} }; - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex != WIDX_FILTER_TEXT_BOX) - return; - - std::string tempText = text.data(); - const char* c = tempText.c_str(); - - if (strcmp(_filter_string, c) == 0) - return; - - SafeStrCpy(_filter_string, c, sizeof(_filter_string)); - - FilterUpdateCounts(); - - scrolls->v_top = 0; - - VisibleListRefresh(); - Invalidate(); - } - - void OnPrepareDraw() override - { - // Resize widgets - ResizeFrameWithPage(); - widgets[WIDX_ADVANCED].left = width - 130; - widgets[WIDX_ADVANCED].right = width - 9; - widgets[WIDX_LIST].right = width - 309; - widgets[WIDX_LIST].bottom = height - 14; - widgets[WIDX_PREVIEW].left = width - 209; - widgets[WIDX_PREVIEW].right = width - 96; - widgets[WIDX_INSTALL_TRACK].left = width - 130; - widgets[WIDX_INSTALL_TRACK].right = width - 9; - widgets[WIDX_FILTER_DROPDOWN].left = width - 250; - widgets[WIDX_FILTER_DROPDOWN].right = width - 137; - widgets[WIDX_RELOAD_OBJECT].left = width - 9 - 24; - widgets[WIDX_RELOAD_OBJECT].right = width - 9; - - // Set pressed widgets - pressed_widgets |= 1uLL << WIDX_PREVIEW; - SetPressedTab(); - if (list_information_type & 1) - pressed_widgets |= (1uLL << WIDX_ADVANCED); - else - pressed_widgets &= ~(1uLL << WIDX_ADVANCED); - - // Set window title and buttons - auto ft = Formatter::Common(); - ft.Add(ObjectSelectionPages[selected_tab].Caption); - auto& titleWidget = widgets[WIDX_TITLE]; - auto& installTrackWidget = widgets[WIDX_INSTALL_TRACK]; - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - titleWidget.text = STR_TRACK_DESIGNS_MANAGER_SELECT_RIDE_TYPE; - installTrackWidget.type = WindowWidgetType::Button; - } - else if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - { - titleWidget.text = STR_ROLLER_COASTER_DESIGNER_SELECT_RIDE_TYPES_VEHICLES; - installTrackWidget.type = WindowWidgetType::Empty; - } - else - { - titleWidget.text = STR_OBJECT_SELECTION; - installTrackWidget.type = WindowWidgetType::Empty; - } - - // Align tabs, hide advanced ones - bool advancedMode = (list_information_type & 1) != 0; - int32_t x = 3; - for (size_t i = 0; i < std::size(ObjectSelectionPages); i++) - { - auto& widget = widgets[WIDX_TAB_1 + i]; - if ((!advancedMode && ObjectSelectionPages[i].IsAdvanced) - || ObjectSelectionPages[i].Image == static_cast(SPR_NONE)) - { - widget.type = WindowWidgetType::Empty; + x = 150; } else { - widget.type = WindowWidgetType::Tab; - widget.left = x; - widget.right = x + 30; - x += 31; + widgets[WIDX_ADVANCED].type = WindowWidgetType::Button; + x = 300; } - } - if (gConfigGeneral.DebuggingTools) - widgets[WIDX_RELOAD_OBJECT].type = WindowWidgetType::ImgBtn; - else - widgets[WIDX_RELOAD_OBJECT].type = WindowWidgetType::Empty; + widgets[WIDX_FILTER_DROPDOWN].type = WindowWidgetType::Button; + widgets[WIDX_LIST].right = width - (WW - 587) - x; + widgets[WIDX_PREVIEW].left = width - (WW - 537) - (x / 2); + widgets[WIDX_PREVIEW].right = widgets[WIDX_PREVIEW].left + 113; + widgets[WIDX_FILTER_RIDE_TAB_FRAME].right = widgets[WIDX_LIST].right; - if (gScreenFlags & (SCREEN_FLAGS_TRACK_MANAGER | SCREEN_FLAGS_TRACK_DESIGNER)) - { - widgets[WIDX_ADVANCED].type = WindowWidgetType::Empty; - for (size_t i = 1; i < std::size(ObjectSelectionPages); i++) + bool ridePage = (GetSelectedObjectType() == ObjectType::Ride); + widgets[WIDX_LIST].top = (ridePage ? 118 : 60); + widgets[WIDX_FILTER_TEXT_BOX].right = widgets[WIDX_LIST].right - 77; + widgets[WIDX_FILTER_TEXT_BOX].top = (ridePage ? 79 : 45); + widgets[WIDX_FILTER_TEXT_BOX].bottom = (ridePage ? 92 : 58); + widgets[WIDX_FILTER_CLEAR_BUTTON].left = widgets[WIDX_LIST].right - 73; + widgets[WIDX_FILTER_CLEAR_BUTTON].right = widgets[WIDX_LIST].right; + widgets[WIDX_FILTER_CLEAR_BUTTON].top = (ridePage ? 79 : 45); + widgets[WIDX_FILTER_CLEAR_BUTTON].bottom = (ridePage ? 92 : 58); + + if (ridePage) { - widgets[WIDX_TAB_1 + i].type = WindowWidgetType::Empty; + for (int32_t i = WIDX_FILTER_RIDE_TAB_ALL; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) + pressed_widgets &= ~(1 << i); + + if ((_filter_flags & FILTER_RIDES) == FILTER_RIDES) + pressed_widgets |= (1uLL << WIDX_FILTER_RIDE_TAB_ALL); + else + { + for (int32_t i = 0; i < 6; i++) + { + if (_filter_flags & (1 << (_numSourceGameItems + i))) + pressed_widgets |= 1uLL << (WIDX_FILTER_RIDE_TAB_TRANSPORT + i); + } + } + + widgets[WIDX_FILTER_RIDE_TAB_FRAME].type = WindowWidgetType::ImgBtn; + for (int32_t i = WIDX_FILTER_RIDE_TAB_ALL; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) + widgets[i].type = WindowWidgetType::Tab; + + int32_t width_limit = (widgets[WIDX_LIST].width() - 15) / 2; + + widgets[WIDX_LIST_SORT_TYPE].type = WindowWidgetType::TableHeader; + widgets[WIDX_LIST_SORT_TYPE].top = widgets[WIDX_FILTER_TEXT_BOX].bottom + 3; + widgets[WIDX_LIST_SORT_TYPE].bottom = widgets[WIDX_LIST_SORT_TYPE].top + 13; + widgets[WIDX_LIST_SORT_TYPE].left = 4; + widgets[WIDX_LIST_SORT_TYPE].right = widgets[WIDX_LIST_SORT_TYPE].left + width_limit; + + widgets[WIDX_LIST_SORT_RIDE].type = WindowWidgetType::TableHeader; + widgets[WIDX_LIST_SORT_RIDE].top = widgets[WIDX_LIST_SORT_TYPE].top; + widgets[WIDX_LIST_SORT_RIDE].bottom = widgets[WIDX_LIST_SORT_TYPE].bottom; + widgets[WIDX_LIST_SORT_RIDE].left = widgets[WIDX_LIST_SORT_TYPE].right + 1; + widgets[WIDX_LIST_SORT_RIDE].right = widgets[WIDX_LIST].right; + + widgets[WIDX_LIST].top = widgets[WIDX_LIST_SORT_TYPE].bottom + 2; } - x = 150; - } - else - { - widgets[WIDX_ADVANCED].type = WindowWidgetType::Button; - x = 300; - } - - widgets[WIDX_FILTER_DROPDOWN].type = WindowWidgetType::Button; - widgets[WIDX_LIST].right = width - (WW - 587) - x; - widgets[WIDX_PREVIEW].left = width - (WW - 537) - (x / 2); - widgets[WIDX_PREVIEW].right = widgets[WIDX_PREVIEW].left + 113; - widgets[WIDX_FILTER_RIDE_TAB_FRAME].right = widgets[WIDX_LIST].right; - - bool ridePage = (GetSelectedObjectType() == ObjectType::Ride); - widgets[WIDX_LIST].top = (ridePage ? 118 : 60); - widgets[WIDX_FILTER_TEXT_BOX].right = widgets[WIDX_LIST].right - 77; - widgets[WIDX_FILTER_TEXT_BOX].top = (ridePage ? 79 : 45); - widgets[WIDX_FILTER_TEXT_BOX].bottom = (ridePage ? 92 : 58); - widgets[WIDX_FILTER_CLEAR_BUTTON].left = widgets[WIDX_LIST].right - 73; - widgets[WIDX_FILTER_CLEAR_BUTTON].right = widgets[WIDX_LIST].right; - widgets[WIDX_FILTER_CLEAR_BUTTON].top = (ridePage ? 79 : 45); - widgets[WIDX_FILTER_CLEAR_BUTTON].bottom = (ridePage ? 92 : 58); - - if (ridePage) - { - for (int32_t i = WIDX_FILTER_RIDE_TAB_ALL; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) - pressed_widgets &= ~(1 << i); - - if ((_filter_flags & FILTER_RIDES) == FILTER_RIDES) - pressed_widgets |= (1uLL << WIDX_FILTER_RIDE_TAB_ALL); else { - for (int32_t i = 0; i < 6; i++) + for (int32_t i = WIDX_FILTER_RIDE_TAB_FRAME; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) + widgets[i].type = WindowWidgetType::Empty; + + widgets[WIDX_LIST_SORT_TYPE].type = WindowWidgetType::Empty; + widgets[WIDX_LIST_SORT_RIDE].type = WindowWidgetType::Empty; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + int32_t _width; + + DrawWidgets(dpi); + + // Draw tabs + for (size_t i = 0; i < std::size(ObjectSelectionPages); i++) + { + const auto& widget = widgets[WIDX_TAB_1 + i]; + if (widget.type != WindowWidgetType::Empty) { - if (_filter_flags & (1 << (_numSourceGameItems + i))) - pressed_widgets |= 1uLL << (WIDX_FILTER_RIDE_TAB_TRANSPORT + i); + auto image = ImageId(ObjectSelectionPages[i].Image); + auto screenPos = windowPos + ScreenCoordsXY{ widget.left, widget.top }; + GfxDrawSprite(dpi, image, screenPos); } } - widgets[WIDX_FILTER_RIDE_TAB_FRAME].type = WindowWidgetType::ImgBtn; - for (int32_t i = WIDX_FILTER_RIDE_TAB_ALL; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) - widgets[i].type = WindowWidgetType::Tab; + const int32_t ride_tabs[] = { + SPR_TAB_RIDE_16, SPR_TAB_RIDES_TRANSPORT_0, SPR_TAB_RIDES_GENTLE_0, SPR_TAB_RIDES_ROLLER_COASTERS_0, + SPR_TAB_RIDES_THRILL_0, SPR_TAB_RIDES_WATER_0, SPR_TAB_RIDES_SHOP_0, SPR_TAB_FINANCES_RESEARCH_0, + }; + const int32_t ThrillRidesTabAnimationSequence[] = { + 5, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, + }; - int32_t width_limit = (widgets[WIDX_LIST].width() - 15) / 2; - - widgets[WIDX_LIST_SORT_TYPE].type = WindowWidgetType::TableHeader; - widgets[WIDX_LIST_SORT_TYPE].top = widgets[WIDX_FILTER_TEXT_BOX].bottom + 3; - widgets[WIDX_LIST_SORT_TYPE].bottom = widgets[WIDX_LIST_SORT_TYPE].top + 13; - widgets[WIDX_LIST_SORT_TYPE].left = 4; - widgets[WIDX_LIST_SORT_TYPE].right = widgets[WIDX_LIST_SORT_TYPE].left + width_limit; - - widgets[WIDX_LIST_SORT_RIDE].type = WindowWidgetType::TableHeader; - widgets[WIDX_LIST_SORT_RIDE].top = widgets[WIDX_LIST_SORT_TYPE].top; - widgets[WIDX_LIST_SORT_RIDE].bottom = widgets[WIDX_LIST_SORT_TYPE].bottom; - widgets[WIDX_LIST_SORT_RIDE].left = widgets[WIDX_LIST_SORT_TYPE].right + 1; - widgets[WIDX_LIST_SORT_RIDE].right = widgets[WIDX_LIST].right; - - widgets[WIDX_LIST].top = widgets[WIDX_LIST_SORT_TYPE].bottom + 2; - } - else - { - for (int32_t i = WIDX_FILTER_RIDE_TAB_FRAME; i <= WIDX_FILTER_RIDE_TAB_STALL; i++) - widgets[i].type = WindowWidgetType::Empty; - - widgets[WIDX_LIST_SORT_TYPE].type = WindowWidgetType::Empty; - widgets[WIDX_LIST_SORT_RIDE].type = WindowWidgetType::Empty; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - int32_t _width; - - DrawWidgets(dpi); - - // Draw tabs - for (size_t i = 0; i < std::size(ObjectSelectionPages); i++) - { - const auto& widget = widgets[WIDX_TAB_1 + i]; - if (widget.type != WindowWidgetType::Empty) + // Draw ride tabs + if (GetSelectedObjectType() == ObjectType::Ride) { - auto image = ImageId(ObjectSelectionPages[i].Image); - auto screenPos = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - GfxDrawSprite(dpi, image, screenPos); - } - } - - const int32_t ride_tabs[] = { - SPR_TAB_RIDE_16, SPR_TAB_RIDES_TRANSPORT_0, SPR_TAB_RIDES_GENTLE_0, SPR_TAB_RIDES_ROLLER_COASTERS_0, - SPR_TAB_RIDES_THRILL_0, SPR_TAB_RIDES_WATER_0, SPR_TAB_RIDES_SHOP_0, SPR_TAB_FINANCES_RESEARCH_0, - }; - const int32_t ThrillRidesTabAnimationSequence[] = { - 5, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, - }; - - // Draw ride tabs - if (GetSelectedObjectType() == ObjectType::Ride) - { - for (int32_t i = 0; i < 7; i++) - { - const auto& widget = widgets[WIDX_FILTER_RIDE_TAB_ALL + i]; - if (widget.type == WindowWidgetType::Empty) - continue; - - int32_t spriteIndex = ride_tabs[i]; - int32_t frame = 0; - if (i != 0 && IsWidgetPressed(WIDX_FILTER_RIDE_TAB_ALL + i)) + for (int32_t i = 0; i < 7; i++) { - frame = frame_no / window_editor_object_selection_animation_divisor[i - 1]; - } - spriteIndex += (i == 4 ? ThrillRidesTabAnimationSequence[frame] : frame); - - auto screenPos = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - GfxDrawSprite(dpi, ImageId(spriteIndex, colours[1]), screenPos); - } - } - - // Preview background - const auto& previewWidget = widgets[WIDX_PREVIEW]; - GfxFillRect( - dpi, - { windowPos + ScreenCoordsXY{ previewWidget.left + 1, previewWidget.top + 1 }, - windowPos + ScreenCoordsXY{ previewWidget.right - 1, previewWidget.bottom - 1 } }, - ColourMapA[colours[1]].darkest); - - // Draw number of selected items - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - auto screenPos = windowPos + ScreenCoordsXY{ 3, height - 13 }; - - int32_t numSelected = _numSelectedObjectsForType[EnumValue(GetSelectedObjectType())]; - int32_t totalSelectable = object_entry_group_counts[EnumValue(GetSelectedObjectType())]; - - auto ft = Formatter(); - ft.Add(numSelected); - ft.Add(totalSelectable); - DrawTextBasic(dpi, screenPos, STR_OBJECT_SELECTION_SELECTION_SIZE, ft); - } - - // Draw sort button text - const auto& listSortTypeWidget = widgets[WIDX_LIST_SORT_TYPE]; - if (listSortTypeWidget.type != WindowWidgetType::Empty) - { - auto ft = Formatter(); - auto stringId = _listSortType == RIDE_SORT_TYPE ? static_cast(_listSortDescending ? STR_DOWN : STR_UP) - : STR_NONE; - ft.Add(stringId); - auto screenPos = windowPos + ScreenCoordsXY{ listSortTypeWidget.left + 1, listSortTypeWidget.top + 1 }; - DrawTextEllipsised(dpi, screenPos, listSortTypeWidget.width(), STR_OBJECTS_SORT_TYPE, ft, { colours[1] }); - } - const auto& listSortRideWidget = widgets[WIDX_LIST_SORT_RIDE]; - if (listSortRideWidget.type != WindowWidgetType::Empty) - { - auto ft = Formatter(); - auto stringId = _listSortType == RIDE_SORT_RIDE ? static_cast(_listSortDescending ? STR_DOWN : STR_UP) - : STR_NONE; - ft.Add(stringId); - auto screenPos = windowPos + ScreenCoordsXY{ listSortRideWidget.left + 1, listSortRideWidget.top + 1 }; - DrawTextEllipsised(dpi, screenPos, listSortRideWidget.width(), STR_OBJECTS_SORT_RIDE, ft, { colours[1] }); - } - - if (selected_list_item == -1 || _loadedObject == nullptr) - return; - - ObjectListItem* listItem = &_listItems[selected_list_item]; - - // Draw preview - { - DrawPixelInfo clipDPI; - auto screenPos = windowPos + ScreenCoordsXY{ previewWidget.left + 1, previewWidget.top + 1 }; - _width = previewWidget.width() - 1; - int32_t _height = previewWidget.height() - 1; - if (ClipDrawPixelInfo(clipDPI, dpi, screenPos, _width, _height)) - { - _loadedObject->DrawPreview(clipDPI, _width, _height); - } - } - - // Draw name of object - { - auto screenPos = windowPos + ScreenCoordsXY{ previewWidget.midX() + 1, previewWidget.bottom + 3 }; - _width = this->width - widgets[WIDX_LIST].right - 6; - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(listItem->repositoryItem->Name.c_str()); - DrawTextEllipsised(dpi, screenPos, _width, STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::CENTRE }); - } - - DrawDescriptions(dpi); - DrawDebugData(dpi); - } - -private: - void InitWidgets() - { - auto& targetWidgets = _window_editor_object_selection_widgets; - if (!_window_editor_object_selection_widgets_initialised) - { - _window_editor_object_selection_widgets_initialised = true; - auto tabWidget = targetWidgets[targetWidgets.size() - 2]; - for (size_t i = 1; i < std::size(ObjectSelectionPages); i++) - { - targetWidgets.insert(targetWidgets.end() - 1, tabWidget); - } - } - - widgets = targetWidgets.data(); - } - - void SetPage(int32_t _page) - { - if (selected_tab == _page) - return; - - selected_tab = _page; - selected_list_item = -1; - scrolls[0].v_top = 0; - frame_no = 0; - - if (_page == EnumValue(ObjectType::Ride)) - { - _listSortType = RIDE_SORT_TYPE; - _listSortDescending = false; - } - else - { - _listSortType = RIDE_SORT_RIDE; - _listSortDescending = false; - } - - VisibleListRefresh(); - Invalidate(); - } - - void VisibleListRefresh() - { - int32_t numObjects = static_cast(ObjectRepositoryGetItemsCount()); - - VisibleListDispose(); - selected_list_item = -1; - - const ObjectRepositoryItem* items = ObjectRepositoryGetItems(); - for (int32_t i = 0; i < numObjects; i++) - { - uint8_t selectionFlags = _objectSelectionFlags[i]; - const ObjectRepositoryItem* item = &items[i]; - if (item->Type == GetSelectedObjectType() && !(selectionFlags & ObjectSelectionFlags::Flag6) && FilterSource(item) - && FilterString(*item) && FilterChunks(item) && FilterSelected(selectionFlags) - && FilterCompatibilityObject(*item, selectionFlags)) - { - auto filter = std::make_unique(); - filter->category[0] = 0; - filter->category[1] = 0; - filter->ride_type = 0; - - ObjectListItem currentListItem; - currentListItem.repositoryItem = item; - currentListItem.filter = std::move(filter); - currentListItem.flags = &_objectSelectionFlags[i]; - _listItems.push_back(std::move(currentListItem)); - } - } - - if (_listItems.empty()) - { - VisibleListDispose(); - } - else - { - sortFunc_t sortFunc = nullptr; - switch (_listSortType) - { - case RIDE_SORT_TYPE: - sortFunc = VisibleListSortRideType; - break; - case RIDE_SORT_RIDE: - sortFunc = VisibleListSortRideName; - break; - default: - LOG_WARNING("Wrong sort type %d, leaving list as-is.", _listSortType); - break; - } - if (sortFunc != nullptr) - { - std::sort(_listItems.begin(), _listItems.end(), sortFunc); - if (_listSortDescending) - { - std::reverse(_listItems.begin(), _listItems.end()); - } - } - } - Invalidate(); - } - - void VisibleListDispose() - { - _listItems.clear(); - _listItems.shrink_to_fit(); - } - - void DrawDescriptions(DrawPixelInfo& dpi) - { - const auto& widget = widgets[WIDX_PREVIEW]; - auto screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_LIST].right + 4, widget.bottom + 23 }; - auto _width2 = windowPos.x + this->width - screenPos.x - 4; - - if (_loadedObject->IsCompatibilityObject()) - { - screenPos.y += DrawTextWrapped( - dpi, screenPos, _width2, STR_OBJECT_SELECTION_COMPAT_OBJECT_DESCRIPTION, {}, - { COLOUR_BRIGHT_RED }) - + LIST_ROW_HEIGHT; - } - - auto description = ObjectGetDescription(_loadedObject.get()); - if (!description.empty()) - { - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(description.c_str()); - - screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_WINDOW_COLOUR_2_STRINGID, ft) + LIST_ROW_HEIGHT; - } - if (GetSelectedObjectType() == ObjectType::Ride) - { - auto* rideObject = reinterpret_cast(_loadedObject.get()); - const auto* rideEntry = reinterpret_cast(rideObject->GetLegacyData()); - if (rideEntry->shop_item[0] != ShopItem::None) - { - std::string sells = ""; - for (size_t i = 0; i < std::size(rideEntry->shop_item); i++) - { - if (rideEntry->shop_item[i] == ShopItem::None) + const auto& widget = widgets[WIDX_FILTER_RIDE_TAB_ALL + i]; + if (widget.type == WindowWidgetType::Empty) continue; - if (!sells.empty()) - sells += ", "; + int32_t spriteIndex = ride_tabs[i]; + int32_t frame = 0; + if (i != 0 && IsWidgetPressed(WIDX_FILTER_RIDE_TAB_ALL + i)) + { + frame = frame_no / window_editor_object_selection_animation_divisor[i - 1]; + } + spriteIndex += (i == 4 ? ThrillRidesTabAnimationSequence[frame] : frame); - sells += LanguageGetString(GetShopItemDescriptor(rideEntry->shop_item[i]).Naming.Plural); + auto screenPos = windowPos + ScreenCoordsXY{ widget.left, widget.top }; + GfxDrawSprite(dpi, ImageId(spriteIndex, colours[1]), screenPos); } - auto ft = Formatter(); - ft.Add(sells.c_str()); - screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_RIDE_OBJECT_SHOP_SELLS, ft) + 2; } - } - else if (GetSelectedObjectType() == ObjectType::SceneryGroup) - { - const auto* sceneryGroupObject = reinterpret_cast(_loadedObject.get()); - auto ft = Formatter(); - ft.Add(sceneryGroupObject->GetNumIncludedObjects()); - screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_INCLUDES_X_OBJECTS, ft) + 2; - } - else if (GetSelectedObjectType() == ObjectType::Music) - { - screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_MUSIC_OBJECT_TRACK_HEADER) + 2; - const auto* musicObject = reinterpret_cast(_loadedObject.get()); - for (size_t i = 0; i < musicObject->GetTrackCount(); i++) - { - const auto* track = musicObject->GetTrack(i); - if (track->Name.empty()) - continue; - auto stringId = track->Composer.empty() ? STR_MUSIC_OBJECT_TRACK_LIST_ITEM - : STR_MUSIC_OBJECT_TRACK_LIST_ITEM_WITH_COMPOSER; + // Preview background + const auto& previewWidget = widgets[WIDX_PREVIEW]; + GfxFillRect( + dpi, + { windowPos + ScreenCoordsXY{ previewWidget.left + 1, previewWidget.top + 1 }, + windowPos + ScreenCoordsXY{ previewWidget.right - 1, previewWidget.bottom - 1 } }, + ColourMapA[colours[1]].darkest); + + // Draw number of selected items + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + { + auto screenPos = windowPos + ScreenCoordsXY{ 3, height - 13 }; + + int32_t numSelected = _numSelectedObjectsForType[EnumValue(GetSelectedObjectType())]; + int32_t totalSelectable = object_entry_group_counts[EnumValue(GetSelectedObjectType())]; + auto ft = Formatter(); - ft.Add(track->Name.c_str()); - ft.Add(track->Composer.c_str()); - screenPos.y += DrawTextWrapped(dpi, screenPos + ScreenCoordsXY{ 10, 0 }, _width2, stringId, ft); + ft.Add(numSelected); + ft.Add(totalSelectable); + DrawTextBasic(dpi, screenPos, STR_OBJECT_SELECTION_SELECTION_SIZE, ft); } - } - } - void DrawDebugData(DrawPixelInfo& dpi) - { - ObjectListItem* listItem = &_listItems[selected_list_item]; - auto screenPos = windowPos + ScreenCoordsXY{ width - 5, height - (LIST_ROW_HEIGHT * 6) }; - - // Draw fallback image warning - if (_loadedObject && _loadedObject->UsesFallbackImages()) - { - DrawTextBasic(dpi, screenPos, STR_OBJECT_USES_FALLBACK_IMAGES, {}, { COLOUR_WHITE, TextAlignment::RIGHT }); - } - screenPos.y += LIST_ROW_HEIGHT; - - // Draw ride type. - if (GetSelectedObjectType() == ObjectType::Ride) - { - auto stringId = GetRideTypeStringId(listItem->repositoryItem); - DrawTextBasic(dpi, screenPos, stringId, {}, { COLOUR_WHITE, TextAlignment::RIGHT }); - } - - screenPos.y += LIST_ROW_HEIGHT; - - // Draw object source - auto stringId = ObjectManagerGetSourceGameString(listItem->repositoryItem->GetFirstSourceGame()); - DrawTextBasic(dpi, screenPos, stringId, {}, { COLOUR_WHITE, TextAlignment::RIGHT }); - screenPos.y += LIST_ROW_HEIGHT; - - // Draw object filename - { - auto path = Path::GetFileName(listItem->repositoryItem->Path); - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(path.c_str()); - DrawTextBasic( - dpi, { windowPos.x + this->width - 5, screenPos.y }, STR_WINDOW_COLOUR_2_STRINGID, ft, - { COLOUR_BLACK, TextAlignment::RIGHT }); - screenPos.y += LIST_ROW_HEIGHT; - } - - // Draw object author (will be blank space if no author in file or a non JSON object) - { - auto ft = Formatter(); - std::string authorsString; - for (size_t i = 0; i < listItem->repositoryItem->Authors.size(); i++) + // Draw sort button text + const auto& listSortTypeWidget = widgets[WIDX_LIST_SORT_TYPE]; + if (listSortTypeWidget.type != WindowWidgetType::Empty) { - if (i > 0) + auto ft = Formatter(); + auto stringId = _listSortType == RIDE_SORT_TYPE ? static_cast(_listSortDescending ? STR_DOWN : STR_UP) + : STR_NONE; + ft.Add(stringId); + auto screenPos = windowPos + ScreenCoordsXY{ listSortTypeWidget.left + 1, listSortTypeWidget.top + 1 }; + DrawTextEllipsised(dpi, screenPos, listSortTypeWidget.width(), STR_OBJECTS_SORT_TYPE, ft, { colours[1] }); + } + const auto& listSortRideWidget = widgets[WIDX_LIST_SORT_RIDE]; + if (listSortRideWidget.type != WindowWidgetType::Empty) + { + auto ft = Formatter(); + auto stringId = _listSortType == RIDE_SORT_RIDE ? static_cast(_listSortDescending ? STR_DOWN : STR_UP) + : STR_NONE; + ft.Add(stringId); + auto screenPos = windowPos + ScreenCoordsXY{ listSortRideWidget.left + 1, listSortRideWidget.top + 1 }; + DrawTextEllipsised(dpi, screenPos, listSortRideWidget.width(), STR_OBJECTS_SORT_RIDE, ft, { colours[1] }); + } + + if (selected_list_item == -1 || _loadedObject == nullptr) + return; + + ObjectListItem* listItem = &_listItems[selected_list_item]; + + // Draw preview + { + DrawPixelInfo clipDPI; + auto screenPos = windowPos + ScreenCoordsXY{ previewWidget.left + 1, previewWidget.top + 1 }; + _width = previewWidget.width() - 1; + int32_t _height = previewWidget.height() - 1; + if (ClipDrawPixelInfo(clipDPI, dpi, screenPos, _width, _height)) { - authorsString.append(", "); + _loadedObject->DrawPreview(clipDPI, _width, _height); } - authorsString.append(listItem->repositoryItem->Authors[i]); } - ft.Add(STR_STRING); - ft.Add(authorsString.c_str()); - DrawTextEllipsised( - dpi, { windowPos.x + width - 5, screenPos.y }, width - widgets[WIDX_LIST].right - 4, - STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::RIGHT }); - } - } - bool FilterSelected(uint8_t objectFlag) - { - // Track Manager has no concept of selection filtering, so always return true - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - return true; - } - if (_FILTER_SELECTED == _FILTER_NONSELECTED) - { - return true; - } - if (_FILTER_SELECTED && objectFlag & ObjectSelectionFlags::Selected) - { - return true; - } - if (_FILTER_NONSELECTED && !(objectFlag & ObjectSelectionFlags::Selected)) - { - return true; + // Draw name of object + { + auto screenPos = windowPos + ScreenCoordsXY{ previewWidget.midX() + 1, previewWidget.bottom + 3 }; + _width = this->width - widgets[WIDX_LIST].right - 6; + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(listItem->repositoryItem->Name.c_str()); + DrawTextEllipsised(dpi, screenPos, _width, STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::CENTRE }); + } + + DrawDescriptions(dpi); + DrawDebugData(dpi); } - return false; - } - - bool FilterCompatibilityObject(const ObjectRepositoryItem& item, uint8_t objectFlag) - { - // Only show compat objects if they are not selected already. - return !(item.Flags & ObjectItemFlags::IsCompatibilityObject) || (objectFlag & ObjectSelectionFlags::Selected); - } - - static bool IsFilterInName(const ObjectRepositoryItem& item, std::string_view filter) - { - return String::Contains(item.Name, filter, true); - } - - static bool IsFilterInRideType(const ObjectRepositoryItem& item, std::string_view filter) - { - if (item.Type == ObjectType::Ride) + private: + void InitWidgets() { - auto rideTypeName = LanguageGetString(GetRideTypeStringId(&item)); - if (String::Contains(rideTypeName, filter, true)) - return true; + auto& targetWidgets = _window_editor_object_selection_widgets; + if (!_window_editor_object_selection_widgets_initialised) + { + _window_editor_object_selection_widgets_initialised = true; + auto tabWidget = targetWidgets[targetWidgets.size() - 2]; + for (size_t i = 1; i < std::size(ObjectSelectionPages); i++) + { + targetWidgets.insert(targetWidgets.end() - 1, tabWidget); + } + } + + widgets = targetWidgets.data(); } - return false; - } - static bool IsFilterInFilename(const ObjectRepositoryItem& item, std::string_view filter) - { - return String::Contains(item.Path, filter, true); - } - - static bool IsFilterInAuthor(const ObjectRepositoryItem& item, std::string_view filter) - { - for (auto& author : item.Authors) + void SetPage(int32_t _page) { - if (String::Contains(author, filter, true)) + if (selected_tab == _page) + return; + + selected_tab = _page; + selected_list_item = -1; + scrolls[0].v_top = 0; + frame_no = 0; + + if (_page == EnumValue(ObjectType::Ride)) + { + _listSortType = RIDE_SORT_TYPE; + _listSortDescending = false; + } + else + { + _listSortType = RIDE_SORT_RIDE; + _listSortDescending = false; + } + + VisibleListRefresh(); + Invalidate(); + } + + void VisibleListRefresh() + { + int32_t numObjects = static_cast(ObjectRepositoryGetItemsCount()); + + VisibleListDispose(); + selected_list_item = -1; + + const ObjectRepositoryItem* items = ObjectRepositoryGetItems(); + for (int32_t i = 0; i < numObjects; i++) + { + uint8_t selectionFlags = _objectSelectionFlags[i]; + const ObjectRepositoryItem* item = &items[i]; + if (item->Type == GetSelectedObjectType() && !(selectionFlags & ObjectSelectionFlags::Flag6) + && FilterSource(item) && FilterString(*item) && FilterChunks(item) && FilterSelected(selectionFlags) + && FilterCompatibilityObject(*item, selectionFlags)) + { + auto filter = std::make_unique(); + filter->category[0] = 0; + filter->category[1] = 0; + filter->ride_type = 0; + + ObjectListItem currentListItem; + currentListItem.repositoryItem = item; + currentListItem.filter = std::move(filter); + currentListItem.flags = &_objectSelectionFlags[i]; + _listItems.push_back(std::move(currentListItem)); + } + } + + if (_listItems.empty()) + { + VisibleListDispose(); + } + else + { + sortFunc_t sortFunc = nullptr; + switch (_listSortType) + { + case RIDE_SORT_TYPE: + sortFunc = VisibleListSortRideType; + break; + case RIDE_SORT_RIDE: + sortFunc = VisibleListSortRideName; + break; + default: + LOG_WARNING("Wrong sort type %d, leaving list as-is.", _listSortType); + break; + } + if (sortFunc != nullptr) + { + std::sort(_listItems.begin(), _listItems.end(), sortFunc); + if (_listSortDescending) + { + std::reverse(_listItems.begin(), _listItems.end()); + } + } + } + Invalidate(); + } + + void VisibleListDispose() + { + _listItems.clear(); + _listItems.shrink_to_fit(); + } + + void DrawDescriptions(DrawPixelInfo& dpi) + { + const auto& widget = widgets[WIDX_PREVIEW]; + auto screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_LIST].right + 4, widget.bottom + 23 }; + auto _width2 = windowPos.x + this->width - screenPos.x - 4; + + if (_loadedObject->IsCompatibilityObject()) + { + screenPos.y += DrawTextWrapped( + dpi, screenPos, _width2, STR_OBJECT_SELECTION_COMPAT_OBJECT_DESCRIPTION, {}, + { COLOUR_BRIGHT_RED }) + + LIST_ROW_HEIGHT; + } + + auto description = ObjectGetDescription(_loadedObject.get()); + if (!description.empty()) + { + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(description.c_str()); + + screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_WINDOW_COLOUR_2_STRINGID, ft) + LIST_ROW_HEIGHT; + } + if (GetSelectedObjectType() == ObjectType::Ride) + { + auto* rideObject = reinterpret_cast(_loadedObject.get()); + const auto* rideEntry = reinterpret_cast(rideObject->GetLegacyData()); + if (rideEntry->shop_item[0] != ShopItem::None) + { + std::string sells = ""; + for (size_t i = 0; i < std::size(rideEntry->shop_item); i++) + { + if (rideEntry->shop_item[i] == ShopItem::None) + continue; + + if (!sells.empty()) + sells += ", "; + + sells += LanguageGetString(GetShopItemDescriptor(rideEntry->shop_item[i]).Naming.Plural); + } + auto ft = Formatter(); + ft.Add(sells.c_str()); + screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_RIDE_OBJECT_SHOP_SELLS, ft) + 2; + } + } + else if (GetSelectedObjectType() == ObjectType::SceneryGroup) + { + const auto* sceneryGroupObject = reinterpret_cast(_loadedObject.get()); + auto ft = Formatter(); + ft.Add(sceneryGroupObject->GetNumIncludedObjects()); + screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_INCLUDES_X_OBJECTS, ft) + 2; + } + else if (GetSelectedObjectType() == ObjectType::Music) + { + screenPos.y += DrawTextWrapped(dpi, screenPos, _width2, STR_MUSIC_OBJECT_TRACK_HEADER) + 2; + const auto* musicObject = reinterpret_cast(_loadedObject.get()); + for (size_t i = 0; i < musicObject->GetTrackCount(); i++) + { + const auto* track = musicObject->GetTrack(i); + if (track->Name.empty()) + continue; + + auto stringId = track->Composer.empty() ? STR_MUSIC_OBJECT_TRACK_LIST_ITEM + : STR_MUSIC_OBJECT_TRACK_LIST_ITEM_WITH_COMPOSER; + auto ft = Formatter(); + ft.Add(track->Name.c_str()); + ft.Add(track->Composer.c_str()); + screenPos.y += DrawTextWrapped(dpi, screenPos + ScreenCoordsXY{ 10, 0 }, _width2, stringId, ft); + } + } + } + + void DrawDebugData(DrawPixelInfo& dpi) + { + ObjectListItem* listItem = &_listItems[selected_list_item]; + auto screenPos = windowPos + ScreenCoordsXY{ width - 5, height - (LIST_ROW_HEIGHT * 6) }; + + // Draw fallback image warning + if (_loadedObject && _loadedObject->UsesFallbackImages()) + { + DrawTextBasic(dpi, screenPos, STR_OBJECT_USES_FALLBACK_IMAGES, {}, { COLOUR_WHITE, TextAlignment::RIGHT }); + } + screenPos.y += LIST_ROW_HEIGHT; + + // Draw ride type. + if (GetSelectedObjectType() == ObjectType::Ride) + { + auto stringId = GetRideTypeStringId(listItem->repositoryItem); + DrawTextBasic(dpi, screenPos, stringId, {}, { COLOUR_WHITE, TextAlignment::RIGHT }); + } + + screenPos.y += LIST_ROW_HEIGHT; + + // Draw object source + auto stringId = ObjectManagerGetSourceGameString(listItem->repositoryItem->GetFirstSourceGame()); + DrawTextBasic(dpi, screenPos, stringId, {}, { COLOUR_WHITE, TextAlignment::RIGHT }); + screenPos.y += LIST_ROW_HEIGHT; + + // Draw object filename + { + auto path = Path::GetFileName(listItem->repositoryItem->Path); + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(path.c_str()); + DrawTextBasic( + dpi, { windowPos.x + this->width - 5, screenPos.y }, STR_WINDOW_COLOUR_2_STRINGID, ft, + { COLOUR_BLACK, TextAlignment::RIGHT }); + screenPos.y += LIST_ROW_HEIGHT; + } + + // Draw object author (will be blank space if no author in file or a non JSON object) + { + auto ft = Formatter(); + std::string authorsString; + for (size_t i = 0; i < listItem->repositoryItem->Authors.size(); i++) + { + if (i > 0) + { + authorsString.append(", "); + } + authorsString.append(listItem->repositoryItem->Authors[i]); + } + ft.Add(STR_STRING); + ft.Add(authorsString.c_str()); + DrawTextEllipsised( + dpi, { windowPos.x + width - 5, screenPos.y }, width - widgets[WIDX_LIST].right - 4, + STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::RIGHT }); + } + } + + bool FilterSelected(uint8_t objectFlag) + { + // Track Manager has no concept of selection filtering, so always return true + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { return true; } + if (_FILTER_SELECTED == _FILTER_NONSELECTED) + { + return true; + } + if (_FILTER_SELECTED && objectFlag & ObjectSelectionFlags::Selected) + { + return true; + } + if (_FILTER_NONSELECTED && !(objectFlag & ObjectSelectionFlags::Selected)) + { + return true; + } + + return false; } - return false; - } - bool FilterString(const ObjectRepositoryItem& item) - { - // Nothing to search for - std::string_view filter = _filter_string; - if (filter.empty()) - return true; + bool FilterCompatibilityObject(const ObjectRepositoryItem& item, uint8_t objectFlag) + { + // Only show compat objects if they are not selected already. + return !(item.Flags & ObjectItemFlags::IsCompatibilityObject) || (objectFlag & ObjectSelectionFlags::Selected); + } - return IsFilterInName(item, filter) || IsFilterInRideType(item, filter) || IsFilterInFilename(item, filter) - || IsFilterInAuthor(item, filter); - } + static bool IsFilterInName(const ObjectRepositoryItem& item, std::string_view filter) + { + return String::Contains(item.Name, filter, true); + } - bool SourcesMatch(ObjectSourceGame source) - { - // clang-format off + static bool IsFilterInRideType(const ObjectRepositoryItem& item, std::string_view filter) + { + if (item.Type == ObjectType::Ride) + { + auto rideTypeName = LanguageGetString(GetRideTypeStringId(&item)); + if (String::Contains(rideTypeName, filter, true)) + return true; + } + return false; + } + + static bool IsFilterInFilename(const ObjectRepositoryItem& item, std::string_view filter) + { + return String::Contains(item.Path, filter, true); + } + + static bool IsFilterInAuthor(const ObjectRepositoryItem& item, std::string_view filter) + { + for (auto& author : item.Authors) + { + if (String::Contains(author, filter, true)) + { + return true; + } + } + return false; + } + + bool FilterString(const ObjectRepositoryItem& item) + { + // Nothing to search for + std::string_view filter = _filter_string; + if (filter.empty()) + return true; + + return IsFilterInName(item, filter) || IsFilterInRideType(item, filter) || IsFilterInFilename(item, filter) + || IsFilterInAuthor(item, filter); + } + + bool SourcesMatch(ObjectSourceGame source) + { + // clang-format off return (_FILTER_RCT1 && source == ObjectSourceGame::RCT1) || (_FILTER_AA && source == ObjectSourceGame::AddedAttractions) || (_FILTER_LL && source == ObjectSourceGame::LoopyLandscapes) || @@ -1436,226 +1437,227 @@ private: source != ObjectSourceGame::WackyWorlds && source != ObjectSourceGame::TimeTwister && source != ObjectSourceGame::OpenRCT2Official); - // clang-format on - } + // clang-format on + } - bool FilterSource(const ObjectRepositoryItem* item) - { - if (_FILTER_ALL) - return true; - - for (auto source : item->Sources) + bool FilterSource(const ObjectRepositoryItem* item) { - if (SourcesMatch(source)) + if (_FILTER_ALL) return true; - } - return false; - } - - bool FilterChunks(const ObjectRepositoryItem* item) - { - if (item->Type == ObjectType::Ride) - { - ride_type_t rideType = 0; - for (int32_t i = 0; i < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; i++) + for (auto source : item->Sources) { - if (item->RideInfo.RideType[i] != RIDE_TYPE_NULL) - { - rideType = item->RideInfo.RideType[i]; - break; - } + if (SourcesMatch(source)) + return true; } - return (_filter_flags & (1 << (GetRideTypeDescriptor(rideType).Category + _numSourceGameItems))) != 0; + + return false; } - return true; - } - void FilterUpdateCounts() - { - if (!_FILTER_ALL || _filter_string[0] != '\0') + bool FilterChunks(const ObjectRepositoryItem* item) { - const auto& selectionFlags = _objectSelectionFlags; - std::fill(std::begin(_filter_object_counts), std::end(_filter_object_counts), 0); - - size_t numObjects = ObjectRepositoryGetItemsCount(); - const ObjectRepositoryItem* items = ObjectRepositoryGetItems(); - for (size_t i = 0; i < numObjects; i++) + if (item->Type == ObjectType::Ride) { - const ObjectRepositoryItem* item = &items[i]; - if (FilterSource(item) && FilterString(*item) && FilterChunks(item) && FilterSelected(selectionFlags[i]) - && FilterCompatibilityObject(*item, selectionFlags[i])) + ride_type_t rideType = 0; + for (int32_t i = 0; i < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; i++) { - _filter_object_counts[EnumValue(item->Type)]++; + if (item->RideInfo.RideType[i] != RIDE_TYPE_NULL) + { + rideType = item->RideInfo.RideType[i]; + break; + } + } + return (_filter_flags & (1 << (GetRideTypeDescriptor(rideType).Category + _numSourceGameItems))) != 0; + } + return true; + } + + void FilterUpdateCounts() + { + if (!_FILTER_ALL || _filter_string[0] != '\0') + { + const auto& selectionFlags = _objectSelectionFlags; + std::fill(std::begin(_filter_object_counts), std::end(_filter_object_counts), 0); + + size_t numObjects = ObjectRepositoryGetItemsCount(); + const ObjectRepositoryItem* items = ObjectRepositoryGetItems(); + for (size_t i = 0; i < numObjects; i++) + { + const ObjectRepositoryItem* item = &items[i]; + if (FilterSource(item) && FilterString(*item) && FilterChunks(item) && FilterSelected(selectionFlags[i]) + && FilterCompatibilityObject(*item, selectionFlags[i])) + { + _filter_object_counts[EnumValue(item->Type)]++; + } } } } - } - std::string ObjectGetDescription(const Object* object) - { - switch (object->GetObjectType()) + std::string ObjectGetDescription(const Object* object) { - case ObjectType::Ride: + switch (object->GetObjectType()) { - const RideObject* rideObject = static_cast(object); - return rideObject->GetDescription(); + case ObjectType::Ride: + { + const RideObject* rideObject = static_cast(object); + return rideObject->GetDescription(); + } + default: + return ""; } - default: - return ""; } - } - ObjectType GetSelectedObjectType() - { - const bool inBounds = selected_tab >= 0 && selected_tab < static_cast(std::size(TabOrder)); - return inBounds ? TabOrder[selected_tab] : ObjectType::Ride; - } + ObjectType GetSelectedObjectType() + { + const bool inBounds = selected_tab >= 0 && selected_tab < static_cast(std::size(TabOrder)); + return inBounds ? TabOrder[selected_tab] : ObjectType::Ride; + } + + /** + * Takes the y coordinate of the clicked on scroll list + * and converts this into an object selection. + * Returns the position in the list. + * Object_selection_flags, installed_entry also populated + * + * rct2: 0x006AA703 + */ + int32_t GetObjectFromObjectSelection(ObjectType object_type, int32_t y) + { + int32_t listItemIndex = y / SCROLLABLE_ROW_HEIGHT; + if (listItemIndex < 0 || static_cast(listItemIndex) >= _listItems.size()) + return -1; + + return listItemIndex; + } + + void SetPressedTab() + { + for (size_t i = 0; i < std::size(ObjectSelectionPages); i++) + { + pressed_widgets &= ~(1ull << (WIDX_TAB_1 + i)); + } + pressed_widgets |= 1LL << (WIDX_TAB_1 + selected_tab); + } + + /** + * + * rct2: 0x006D33E2 + */ + void ManageTracks() + { + SetEveryRideTypeInvented(); + SetEveryRideEntryInvented(); + + GetGameState().EditorStep = EditorStep::DesignsManager; + + int32_t entry_index = 0; + for (; ObjectEntryGetChunk(ObjectType::Ride, entry_index) == nullptr; entry_index++) + ; + + const auto* rideEntry = GetRideEntryByIndex(entry_index); + auto rideType = rideEntry->GetFirstNonNullRideType(); + + auto intent = Intent(WindowClass::TrackDesignList); + intent.PutExtra(INTENT_EXTRA_RIDE_TYPE, rideType); + intent.PutExtra(INTENT_EXTRA_RIDE_ENTRY_INDEX, entry_index); + ContextOpenIntent(&intent); + } + }; /** - * Takes the y coordinate of the clicked on scroll list - * and converts this into an object selection. - * Returns the position in the list. - * Object_selection_flags, installed_entry also populated * - * rct2: 0x006AA703 + * rct2: 0x006AA64E */ - int32_t GetObjectFromObjectSelection(ObjectType object_type, int32_t y) + WindowBase* WindowEditorObjectSelectionOpen() { - int32_t listItemIndex = y / SCROLLABLE_ROW_HEIGHT; - if (listItemIndex < 0 || static_cast(listItemIndex) >= _listItems.size()) - return -1; - - return listItemIndex; + return WindowFocusOrCreate( + WindowClass::EditorObjectSelection, 755, 400, WF_10 | WF_RESIZABLE | WF_CENTRE_SCREEN); } - void SetPressedTab() + static bool VisibleListSortRideName(const ObjectListItem& a, const ObjectListItem& b) { - for (size_t i = 0; i < std::size(ObjectSelectionPages); i++) + auto nameA = a.repositoryItem->Name.c_str(); + auto nameB = b.repositoryItem->Name.c_str(); + return strcmp(nameA, nameB) < 0; + } + + static bool VisibleListSortRideType(const ObjectListItem& a, const ObjectListItem& b) + { + auto rideTypeA = LanguageGetString(GetRideTypeStringId(a.repositoryItem)); + auto rideTypeB = LanguageGetString(GetRideTypeStringId(b.repositoryItem)); + int32_t result = String::Compare(rideTypeA, rideTypeB); + return result != 0 ? result < 0 : VisibleListSortRideName(a, b); + } + + static StringId GetRideTypeStringId(const ObjectRepositoryItem* item) + { + StringId result = STR_NONE; + for (int32_t i = 0; i < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; i++) { - pressed_widgets &= ~(1ull << (WIDX_TAB_1 + i)); + auto rideType = item->RideInfo.RideType[i]; + if (rideType != RIDE_TYPE_NULL) + { + result = GetRideTypeDescriptor(rideType).Naming.Name; + break; + } } - pressed_widgets |= 1LL << (WIDX_TAB_1 + selected_tab); + return result; } /** * - * rct2: 0x006D33E2 + * rct2: 0x006ABBBE */ - void ManageTracks() + void EditorLoadSelectedObjects() { - SetEveryRideTypeInvented(); - SetEveryRideEntryInvented(); - - GetGameState().EditorStep = EditorStep::DesignsManager; - - int32_t entry_index = 0; - for (; ObjectEntryGetChunk(ObjectType::Ride, entry_index) == nullptr; entry_index++) - ; - - const auto* rideEntry = GetRideEntryByIndex(entry_index); - auto rideType = rideEntry->GetFirstNonNullRideType(); - - auto intent = Intent(WindowClass::TrackDesignList); - intent.PutExtra(INTENT_EXTRA_RIDE_TYPE, rideType); - intent.PutExtra(INTENT_EXTRA_RIDE_ENTRY_INDEX, entry_index); - ContextOpenIntent(&intent); - } -}; - -/** - * - * rct2: 0x006AA64E - */ -WindowBase* WindowEditorObjectSelectionOpen() -{ - return WindowFocusOrCreate( - WindowClass::EditorObjectSelection, 755, 400, WF_10 | WF_RESIZABLE | WF_CENTRE_SCREEN); -} - -static bool VisibleListSortRideName(const ObjectListItem& a, const ObjectListItem& b) -{ - auto nameA = a.repositoryItem->Name.c_str(); - auto nameB = b.repositoryItem->Name.c_str(); - return strcmp(nameA, nameB) < 0; -} - -static bool VisibleListSortRideType(const ObjectListItem& a, const ObjectListItem& b) -{ - auto rideTypeA = LanguageGetString(GetRideTypeStringId(a.repositoryItem)); - auto rideTypeB = LanguageGetString(GetRideTypeStringId(b.repositoryItem)); - int32_t result = String::Compare(rideTypeA, rideTypeB); - return result != 0 ? result < 0 : VisibleListSortRideName(a, b); -} - -static StringId GetRideTypeStringId(const ObjectRepositoryItem* item) -{ - StringId result = STR_NONE; - for (int32_t i = 0; i < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; i++) - { - auto rideType = item->RideInfo.RideType[i]; - if (rideType != RIDE_TYPE_NULL) + auto& objManager = GetContext()->GetObjectManager(); + int32_t numItems = static_cast(ObjectRepositoryGetItemsCount()); + const ObjectRepositoryItem* items = ObjectRepositoryGetItems(); + bool showFallbackWarning = false; + for (int32_t i = 0; i < numItems; i++) { - result = GetRideTypeDescriptor(rideType).Naming.Name; - break; - } - } - return result; -} - -/** - * - * rct2: 0x006ABBBE - */ -void EditorLoadSelectedObjects() -{ - auto& objManager = GetContext()->GetObjectManager(); - int32_t numItems = static_cast(ObjectRepositoryGetItemsCount()); - const ObjectRepositoryItem* items = ObjectRepositoryGetItems(); - bool showFallbackWarning = false; - for (int32_t i = 0; i < numItems; i++) - { - if (_objectSelectionFlags[i] & ObjectSelectionFlags::Selected) - { - const auto* item = &items[i]; - auto descriptor = ObjectEntryDescriptor(*item); - const auto* loadedObject = objManager.GetLoadedObject(descriptor); - if (loadedObject == nullptr) + if (_objectSelectionFlags[i] & ObjectSelectionFlags::Selected) { - loadedObject = objManager.LoadObject(descriptor); + const auto* item = &items[i]; + auto descriptor = ObjectEntryDescriptor(*item); + const auto* loadedObject = objManager.GetLoadedObject(descriptor); if (loadedObject == nullptr) { - LOG_ERROR("Failed to load entry %s", std::string(descriptor.GetName()).c_str()); - } - else if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) - { - // Defaults selected items to researched (if in-game) - auto objectType = loadedObject->GetObjectType(); - auto entryIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject); - if (objectType == ObjectType::Ride) + loadedObject = objManager.LoadObject(descriptor); + if (loadedObject == nullptr) { - const auto* rideEntry = GetRideEntryByIndex(entryIndex); - auto rideType = rideEntry->GetFirstNonNullRideType(); - ResearchCategory category = static_cast(GetRideTypeDescriptor(rideType).Category); - ResearchInsertRideEntry(rideType, entryIndex, category, true); + LOG_ERROR("Failed to load entry %s", std::string(descriptor.GetName()).c_str()); } - else if (objectType == ObjectType::SceneryGroup) + else if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) { - ResearchInsertSceneryGroupEntry(entryIndex, true); - } - if (loadedObject->UsesFallbackImages()) - { - showFallbackWarning = true; + // Defaults selected items to researched (if in-game) + auto objectType = loadedObject->GetObjectType(); + auto entryIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject); + if (objectType == ObjectType::Ride) + { + const auto* rideEntry = GetRideEntryByIndex(entryIndex); + auto rideType = rideEntry->GetFirstNonNullRideType(); + ResearchCategory category = static_cast(GetRideTypeDescriptor(rideType).Category); + ResearchInsertRideEntry(rideType, entryIndex, category, true); + } + else if (objectType == ObjectType::SceneryGroup) + { + ResearchInsertSceneryGroupEntry(entryIndex, true); + } + if (loadedObject->UsesFallbackImages()) + { + showFallbackWarning = true; + } } } } } + if (_numSelectedObjectsForType[EnumValue(ObjectType::Water)] == 0) + { + // Reloads the default cyan water palette if no palette was selected. + LoadPalette(); + } + if (showFallbackWarning) + ContextShowError(STR_OBJECT_SELECTION_FALLBACK_IMAGES_WARNING, STR_EMPTY, Formatter::Common()); } - if (_numSelectedObjectsForType[EnumValue(ObjectType::Water)] == 0) - { - // Reloads the default cyan water palette if no palette was selected. - LoadPalette(); - } - if (showFallbackWarning) - ContextShowError(STR_OBJECT_SELECTION_FALLBACK_IMAGES_WARNING, STR_EMPTY, Formatter::Common()); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/EditorObjectiveOptions.cpp b/src/openrct2-ui/windows/EditorObjectiveOptions.cpp index da18273156..48703f40f0 100644 --- a/src/openrct2-ui/windows/EditorObjectiveOptions.cpp +++ b/src/openrct2-ui/windows/EditorObjectiveOptions.cpp @@ -29,40 +29,40 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_OBJECTIVE_SELECTION; + static constexpr int32_t WH = 229; + static constexpr int32_t WW = 450; -static constexpr StringId WINDOW_TITLE = STR_OBJECTIVE_SELECTION; -static constexpr int32_t WH = 229; -static constexpr int32_t WW = 450; + static constexpr money64 ObjectiveCurrencyLoanAndValueMax = 2000000.00_GBP; + static constexpr money64 ObjectiveCurrencyLoanAndValueMin = 1000.00_GBP; + static constexpr money64 ObjectiveCurrencyLoanAndValueAdjustment = 1000.00_GBP; -static constexpr money64 ObjectiveCurrencyLoanAndValueMax = 2000000.00_GBP; -static constexpr money64 ObjectiveCurrencyLoanAndValueMin = 1000.00_GBP; -static constexpr money64 ObjectiveCurrencyLoanAndValueAdjustment = 1000.00_GBP; + static constexpr money64 ObjectiveCurrencyFoodMax = 2000000.00_GBP; + static constexpr money64 ObjectiveCurrencyFoodMin = 1000.00_GBP; + static constexpr money64 ObjectiveCurrencyFoodAdjustment = 100.00_GBP; -static constexpr money64 ObjectiveCurrencyFoodMax = 2000000.00_GBP; -static constexpr money64 ObjectiveCurrencyFoodMin = 1000.00_GBP; -static constexpr money64 ObjectiveCurrencyFoodAdjustment = 100.00_GBP; + static constexpr uint16_t ObjectiveLengthMax = 5000; + static constexpr uint16_t ObjectiveLengthMin = 1000; + static constexpr uint16_t ObjectiveLengthAdjustment = 100; -static constexpr uint16_t ObjectiveLengthMax = 5000; -static constexpr uint16_t ObjectiveLengthMin = 1000; -static constexpr uint16_t ObjectiveLengthAdjustment = 100; + static constexpr uint16_t ObjectiveExcitementMax = FIXED_2DP(9, 90); + static constexpr uint16_t ObjectiveExcitementMin = FIXED_2DP(4, 00); + static constexpr uint16_t ObjectiveExcitementAdjustment = FIXED_2DP(0, 10); -static constexpr uint16_t ObjectiveExcitementMax = FIXED_2DP(9, 90); -static constexpr uint16_t ObjectiveExcitementMin = FIXED_2DP(4, 00); -static constexpr uint16_t ObjectiveExcitementAdjustment = FIXED_2DP(0, 10); + // The number has to leave a bit of room for other entities like vehicles, litter and balloons. + static constexpr uint16_t ObjectiveGuestsMax = 50000; + static constexpr uint16_t ObjectiveGuestsMin = 250; + static constexpr uint16_t ObjectiveGuestsAdjustment = 50; -// The number has to leave a bit of room for other entities like vehicles, litter and balloons. -static constexpr uint16_t ObjectiveGuestsMax = 50000; -static constexpr uint16_t ObjectiveGuestsMin = 250; -static constexpr uint16_t ObjectiveGuestsAdjustment = 50; - -static constexpr uint8_t ObjectiveYearMax = 25; -static constexpr uint8_t ObjectiveYearMin = 1; -static constexpr uint8_t ObjectiveYearAdjustment = 1; + static constexpr uint8_t ObjectiveYearMax = 25; + static constexpr uint8_t ObjectiveYearMin = 1; + static constexpr uint8_t ObjectiveYearAdjustment = 1; #pragma region Widgets -// clang-format off + // clang-format off enum { WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN, WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES, @@ -152,1053 +152,1056 @@ static uint64_t window_editor_objective_options_page_hold_down_widgets[] = { 0, }; -// clang-format on + // clang-format on #pragma endregion -class EditorObjectiveOptionsWindow final : public Window -{ -private: - // Not shops or facilities - std::vector _rideableRides; - -public: - void OnOpen() override + class EditorObjectiveOptionsWindow final : public Window { - widgets = window_editor_objective_options_main_widgets; - pressed_widgets = 0; - hold_down_widgets = window_editor_objective_options_page_hold_down_widgets[WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN]; - InitScrollWidgets(); - selected_tab = WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN; - selected_list_item = -1; - UpdateDisabledWidgets(); - } + private: + // Not shops or facilities + std::vector _rideableRides; - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - return; - case WIDX_TAB_1: - case WIDX_TAB_2: - SetPage(widgetIndex - WIDX_TAB_1); - return; + widgets = window_editor_objective_options_main_widgets; + pressed_widgets = 0; + hold_down_widgets = window_editor_objective_options_page_hold_down_widgets + [WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN]; + InitScrollWidgets(); + selected_tab = WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN; + selected_list_item = -1; + UpdateDisabledWidgets(); } - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) + void OnMouseUp(WidgetIndex widgetIndex) override { - OnMouseUpMain(widgetIndex); - } - } + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + return; + case WIDX_TAB_1: + case WIDX_TAB_2: + SetPage(widgetIndex - WIDX_TAB_1); + return; + } - void OnResize() override - { - switch (page) - { - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: - OnResizeMain(); - break; - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: - OnResizeRides(); - break; - } - } - - void OnPrepareDraw() override - { - switch (page) - { - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: - OnPrepareDrawMain(); - break; - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: - OnPrepareDrawRides(); - break; - } - } - - void OnUpdate() override - { - switch (page) - { - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: - OnUpdateMain(); - break; - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: - OnUpdateRides(); - break; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - switch (page) - { - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: - OnDrawMain(dpi); - break; - case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: - OnDrawRides(dpi); - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) - { - OnMouseDownMain(widgetIndex); - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) - { - OnDropdownMain(widgetIndex, selectedIndex); - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) - { - OnTextInputMain(widgetIndex, text); - } - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) - { - OnScrollMouseDownRides(scrollIndex, screenCoords); - } - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) - { - OnScrollMouseOverRides(scrollIndex, screenCoords); - } - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) - { - OnScrollDrawRides(dpi, scrollIndex); - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) - { - return OnScrollGetSizeRides(scrollIndex); + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) + { + OnMouseUpMain(widgetIndex); + } } - return {}; - } - -private: - /** - * - * rct2: 0x00668496 - */ - void SetPage(int32_t newPage) - { - if (page == newPage) - return; - - page = newPage; - frame_no = 0; - _rideableRides.clear(); - selected_list_item = -1; - hold_down_widgets = window_editor_objective_options_page_hold_down_widgets[newPage]; - widgets = window_editor_objective_options_widgets[newPage]; - Invalidate(); - UpdateDisabledWidgets(); - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - Invalidate(); - } - - /** - * - * rct2: 0x00672609 - */ - void UpdateDisabledWidgets() - { - // Check if there are any rides (not shops or facilities) - const auto& rideManager = GetRideManager(); - if (std::any_of(rideManager.begin(), rideManager.end(), [](const Ride& rideToCheck) { return rideToCheck.IsRide(); })) + void OnResize() override { - disabled_widgets &= ~(1uLL << WIDX_TAB_2); + switch (page) + { + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: + OnResizeMain(); + break; + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: + OnResizeRides(); + break; + } } - else + + void OnPrepareDraw() override { - disabled_widgets |= (1uLL << WIDX_TAB_2); + switch (page) + { + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: + OnPrepareDrawMain(); + break; + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: + OnPrepareDrawRides(); + break; + } } - } - void SetPressedTab() - { - int32_t i; - for (i = WIDX_TAB_1; i <= WIDX_TAB_2; i++) - pressed_widgets &= ~(1 << i); - pressed_widgets |= 1LL << (WIDX_TAB_1 + page); - } - - void AnchorBorderWidgets() - { - ResizeFrameWithPage(); - } - - void DrawTabImages(DrawPixelInfo& dpi) - { - Widget* widget; - int32_t spriteIndex; - - // Tab 1 - widget = &widgets[WIDX_TAB_1]; - - spriteIndex = SPR_TAB_OBJECTIVE_0; - if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) - spriteIndex += (frame_no / 4) % 16; - - GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); - - // Tab 2 - if (!IsWidgetDisabled(WIDX_TAB_2)) + void OnUpdate() override + { + switch (page) + { + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: + OnUpdateMain(); + break; + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: + OnUpdateRides(); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + switch (page) + { + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN: + OnDrawMain(dpi); + break; + case WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES: + OnDrawRides(dpi); + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) + { + OnMouseDownMain(widgetIndex); + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) + { + OnDropdownMain(widgetIndex, selectedIndex); + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) + { + OnTextInputMain(widgetIndex, text); + } + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - widget = &widgets[WIDX_TAB_2]; - spriteIndex = SPR_TAB_RIDE_0; if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) + { + OnScrollMouseDownRides(scrollIndex, screenCoords); + } + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) + { + OnScrollMouseOverRides(scrollIndex, screenCoords); + } + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) + { + OnScrollDrawRides(dpi, scrollIndex); + } + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) + { + return OnScrollGetSizeRides(scrollIndex); + } + + return {}; + } + + private: + /** + * + * rct2: 0x00668496 + */ + void SetPage(int32_t newPage) + { + if (page == newPage) + return; + + page = newPage; + frame_no = 0; + _rideableRides.clear(); + selected_list_item = -1; + hold_down_widgets = window_editor_objective_options_page_hold_down_widgets[newPage]; + widgets = window_editor_objective_options_widgets[newPage]; + Invalidate(); + UpdateDisabledWidgets(); + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); + } + + /** + * + * rct2: 0x00672609 + */ + void UpdateDisabledWidgets() + { + // Check if there are any rides (not shops or facilities) + const auto& rideManager = GetRideManager(); + if (std::any_of( + rideManager.begin(), rideManager.end(), [](const Ride& rideToCheck) { return rideToCheck.IsRide(); })) + { + disabled_widgets &= ~(1uLL << WIDX_TAB_2); + } + else + { + disabled_widgets |= (1uLL << WIDX_TAB_2); + } + } + + void SetPressedTab() + { + int32_t i; + for (i = WIDX_TAB_1; i <= WIDX_TAB_2; i++) + pressed_widgets &= ~(1 << i); + pressed_widgets |= 1LL << (WIDX_TAB_1 + page); + } + + void AnchorBorderWidgets() + { + ResizeFrameWithPage(); + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + Widget* widget; + int32_t spriteIndex; + + // Tab 1 + widget = &widgets[WIDX_TAB_1]; + + spriteIndex = SPR_TAB_OBJECTIVE_0; + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_MAIN) spriteIndex += (frame_no / 4) % 16; GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); - } - } - /** - * - * rct2: 0x0067201D - */ - void SetObjective(int32_t objective) - { - auto& gameState = GetGameState(); - gameState.ScenarioObjective.Type = objective; - Invalidate(); - - // Set default objective arguments - switch (objective) - { - case OBJECTIVE_NONE: - case OBJECTIVE_HAVE_FUN: - case OBJECTIVE_BUILD_THE_BEST: - case OBJECTIVE_10_ROLLERCOASTERS: - break; - case OBJECTIVE_GUESTS_BY: - gameState.ScenarioObjective.Year = 3; - gameState.ScenarioObjective.NumGuests = 1500; - break; - case OBJECTIVE_PARK_VALUE_BY: - gameState.ScenarioObjective.Year = 3; - gameState.ScenarioObjective.Currency = 50000.00_GBP; - break; - case OBJECTIVE_GUESTS_AND_RATING: - gameState.ScenarioObjective.NumGuests = 2000; - break; - case OBJECTIVE_MONTHLY_RIDE_INCOME: - gameState.ScenarioObjective.Currency = 10000.00_GBP; - break; - case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: - gameState.ScenarioObjective.MinimumLength = 1200; - break; - case OBJECTIVE_FINISH_5_ROLLERCOASTERS: - gameState.ScenarioObjective.MinimumExcitement = FIXED_2DP(6, 70); - break; - case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: - gameState.ScenarioObjective.Currency = 50000.00_GBP; - break; - case OBJECTIVE_MONTHLY_FOOD_INCOME: - gameState.ScenarioObjective.Currency = 1000.00_GBP; - break; - } - } - - void ShowObjectiveDropdown() - { - const auto& gameState = GetGameState(); - int32_t numItems = 0, objectiveType; - Widget* dropdownWidget; - - dropdownWidget = &widgets[WIDX_OBJECTIVE]; - - for (auto i = 0; i < OBJECTIVE_COUNT; i++) - { - if (i == OBJECTIVE_NONE || i == OBJECTIVE_BUILD_THE_BEST) - continue; - - const bool objectiveAllowedByMoneyUsage = !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) - || !ObjectiveNeedsMoney(i); - // This objective can only work if the player can ask money for rides. - const bool objectiveAllowedByPaymentSettings = (i != OBJECTIVE_MONTHLY_RIDE_INCOME) || ParkRidePricesUnlocked(); - if (objectiveAllowedByMoneyUsage && objectiveAllowedByPaymentSettings) + // Tab 2 + if (!IsWidgetDisabled(WIDX_TAB_2)) { - gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numItems].Args = ObjectiveDropdownOptionNames[i]; - numItems++; + widget = &widgets[WIDX_TAB_2]; + spriteIndex = SPR_TAB_RIDE_0; + if (page == WINDOW_EDITOR_OBJECTIVE_OPTIONS_PAGE_RIDES) + spriteIndex += (frame_no / 4) % 16; + + GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); } } - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], - 0, Dropdown::Flag::StayOpen, numItems, dropdownWidget->width() - 3); - - objectiveType = gameState.ScenarioObjective.Type; - for (int32_t j = 0; j < numItems; j++) + /** + * + * rct2: 0x0067201D + */ + void SetObjective(int32_t objective) { - if (gDropdownItems[j].Args - STR_OBJECTIVE_DROPDOWN_NONE == objectiveType) + auto& gameState = GetGameState(); + gameState.ScenarioObjective.Type = objective; + Invalidate(); + + // Set default objective arguments + switch (objective) { - Dropdown::SetChecked(j, true); - break; + case OBJECTIVE_NONE: + case OBJECTIVE_HAVE_FUN: + case OBJECTIVE_BUILD_THE_BEST: + case OBJECTIVE_10_ROLLERCOASTERS: + break; + case OBJECTIVE_GUESTS_BY: + gameState.ScenarioObjective.Year = 3; + gameState.ScenarioObjective.NumGuests = 1500; + break; + case OBJECTIVE_PARK_VALUE_BY: + gameState.ScenarioObjective.Year = 3; + gameState.ScenarioObjective.Currency = 50000.00_GBP; + break; + case OBJECTIVE_GUESTS_AND_RATING: + gameState.ScenarioObjective.NumGuests = 2000; + break; + case OBJECTIVE_MONTHLY_RIDE_INCOME: + gameState.ScenarioObjective.Currency = 10000.00_GBP; + break; + case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: + gameState.ScenarioObjective.MinimumLength = 1200; + break; + case OBJECTIVE_FINISH_5_ROLLERCOASTERS: + gameState.ScenarioObjective.MinimumExcitement = FIXED_2DP(6, 70); + break; + case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: + gameState.ScenarioObjective.Currency = 50000.00_GBP; + break; + case OBJECTIVE_MONTHLY_FOOD_INCOME: + gameState.ScenarioObjective.Currency = 1000.00_GBP; + break; } } - } - void ShowCategoryDropdown() - { - int32_t i; - Widget* dropdownWidget; + void ShowObjectiveDropdown() + { + const auto& gameState = GetGameState(); + int32_t numItems = 0, objectiveType; + Widget* dropdownWidget; - dropdownWidget = &widgets[WIDX_CATEGORY]; + dropdownWidget = &widgets[WIDX_OBJECTIVE]; - for (i = SCENARIO_CATEGORY_BEGINNER; i <= SCENARIO_CATEGORY_OTHER; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = ScenarioCategoryStringIds[i]; - } - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], - 0, Dropdown::Flag::StayOpen, 5, dropdownWidget->width() - 3); - Dropdown::SetChecked(GetGameState().ScenarioCategory, true); - } + for (auto i = 0; i < OBJECTIVE_COUNT; i++) + { + if (i == OBJECTIVE_NONE || i == OBJECTIVE_BUILD_THE_BEST) + continue; - void Arg1Increase() - { - auto& gameState = GetGameState(); - switch (gameState.ScenarioObjective.Type) - { - case OBJECTIVE_PARK_VALUE_BY: - case OBJECTIVE_MONTHLY_RIDE_INCOME: - case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: - if (gameState.ScenarioObjective.Currency >= ObjectiveCurrencyLoanAndValueMax) + const bool objectiveAllowedByMoneyUsage = !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) + || !ObjectiveNeedsMoney(i); + // This objective can only work if the player can ask money for rides. + const bool objectiveAllowedByPaymentSettings = (i != OBJECTIVE_MONTHLY_RIDE_INCOME) || ParkRidePricesUnlocked(); + if (objectiveAllowedByMoneyUsage && objectiveAllowedByPaymentSettings) { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numItems].Args = ObjectiveDropdownOptionNames[i]; + numItems++; } - else - { - gameState.ScenarioObjective.Currency += ObjectiveCurrencyLoanAndValueAdjustment; - Invalidate(); - } - break; - case OBJECTIVE_MONTHLY_FOOD_INCOME: - if (gameState.ScenarioObjective.Currency >= ObjectiveCurrencyFoodMax) - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.Currency += ObjectiveCurrencyFoodAdjustment; - Invalidate(); - } - break; - case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: - if (gameState.ScenarioObjective.MinimumLength >= ObjectiveLengthMax) - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.MinimumLength += ObjectiveLengthAdjustment; - Invalidate(); - } - break; - case OBJECTIVE_FINISH_5_ROLLERCOASTERS: - if (gameState.ScenarioObjective.MinimumExcitement >= ObjectiveExcitementMax) - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.MinimumExcitement += ObjectiveExcitementAdjustment; - Invalidate(); - } - break; - default: - if (gameState.ScenarioObjective.NumGuests >= ObjectiveGuestsMax) - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.NumGuests += ObjectiveGuestsAdjustment; - Invalidate(); - } - break; - } - } + } - void Arg1Decrease() - { - auto& gameState = GetGameState(); - switch (gameState.ScenarioObjective.Type) - { - case OBJECTIVE_PARK_VALUE_BY: - case OBJECTIVE_MONTHLY_RIDE_INCOME: - case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: - if (gameState.ScenarioObjective.Currency <= ObjectiveCurrencyLoanAndValueMin) - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.Currency -= ObjectiveCurrencyLoanAndValueAdjustment; - Invalidate(); - } - break; - case OBJECTIVE_MONTHLY_FOOD_INCOME: - if (gameState.ScenarioObjective.Currency <= ObjectiveCurrencyFoodMin) - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.Currency -= ObjectiveCurrencyFoodAdjustment; - Invalidate(); - } - break; - case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: - if (gameState.ScenarioObjective.MinimumLength <= ObjectiveLengthMin) - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.MinimumLength -= ObjectiveLengthAdjustment; - Invalidate(); - } - break; - case OBJECTIVE_FINISH_5_ROLLERCOASTERS: - if (gameState.ScenarioObjective.MinimumExcitement <= ObjectiveExcitementMin) - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.MinimumExcitement -= ObjectiveExcitementAdjustment; - Invalidate(); - } - break; - default: - if (gameState.ScenarioObjective.NumGuests <= ObjectiveGuestsMin) - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - else - { - gameState.ScenarioObjective.NumGuests -= ObjectiveGuestsAdjustment; - Invalidate(); - } - break; - } - } + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, numItems, dropdownWidget->width() - 3); - void Arg2Increase() - { - auto& gameState = GetGameState(); - if (gameState.ScenarioObjective.Year >= ObjectiveYearMax) - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + objectiveType = gameState.ScenarioObjective.Type; + for (int32_t j = 0; j < numItems; j++) + { + if (gDropdownItems[j].Args - STR_OBJECTIVE_DROPDOWN_NONE == objectiveType) + { + Dropdown::SetChecked(j, true); + break; + } + } } - else - { - gameState.ScenarioObjective.Year += ObjectiveYearAdjustment; - Invalidate(); - } - } - void Arg2Decrease() - { - auto& gameState = GetGameState(); - if (gameState.ScenarioObjective.Year <= ObjectiveYearMin) + void ShowCategoryDropdown() { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + int32_t i; + Widget* dropdownWidget; + + dropdownWidget = &widgets[WIDX_CATEGORY]; + + for (i = SCENARIO_CATEGORY_BEGINNER; i <= SCENARIO_CATEGORY_OTHER; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = ScenarioCategoryStringIds[i]; + } + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 5, dropdownWidget->width() - 3); + Dropdown::SetChecked(GetGameState().ScenarioCategory, true); } - else + + void Arg1Increase() { - gameState.ScenarioObjective.Year -= ObjectiveYearAdjustment; - Invalidate(); + auto& gameState = GetGameState(); + switch (gameState.ScenarioObjective.Type) + { + case OBJECTIVE_PARK_VALUE_BY: + case OBJECTIVE_MONTHLY_RIDE_INCOME: + case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: + if (gameState.ScenarioObjective.Currency >= ObjectiveCurrencyLoanAndValueMax) + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.Currency += ObjectiveCurrencyLoanAndValueAdjustment; + Invalidate(); + } + break; + case OBJECTIVE_MONTHLY_FOOD_INCOME: + if (gameState.ScenarioObjective.Currency >= ObjectiveCurrencyFoodMax) + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.Currency += ObjectiveCurrencyFoodAdjustment; + Invalidate(); + } + break; + case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: + if (gameState.ScenarioObjective.MinimumLength >= ObjectiveLengthMax) + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.MinimumLength += ObjectiveLengthAdjustment; + Invalidate(); + } + break; + case OBJECTIVE_FINISH_5_ROLLERCOASTERS: + if (gameState.ScenarioObjective.MinimumExcitement >= ObjectiveExcitementMax) + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.MinimumExcitement += ObjectiveExcitementAdjustment; + Invalidate(); + } + break; + default: + if (gameState.ScenarioObjective.NumGuests >= ObjectiveGuestsMax) + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.NumGuests += ObjectiveGuestsAdjustment; + Invalidate(); + } + break; + } + } + + void Arg1Decrease() + { + auto& gameState = GetGameState(); + switch (gameState.ScenarioObjective.Type) + { + case OBJECTIVE_PARK_VALUE_BY: + case OBJECTIVE_MONTHLY_RIDE_INCOME: + case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: + if (gameState.ScenarioObjective.Currency <= ObjectiveCurrencyLoanAndValueMin) + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.Currency -= ObjectiveCurrencyLoanAndValueAdjustment; + Invalidate(); + } + break; + case OBJECTIVE_MONTHLY_FOOD_INCOME: + if (gameState.ScenarioObjective.Currency <= ObjectiveCurrencyFoodMin) + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.Currency -= ObjectiveCurrencyFoodAdjustment; + Invalidate(); + } + break; + case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: + if (gameState.ScenarioObjective.MinimumLength <= ObjectiveLengthMin) + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.MinimumLength -= ObjectiveLengthAdjustment; + Invalidate(); + } + break; + case OBJECTIVE_FINISH_5_ROLLERCOASTERS: + if (gameState.ScenarioObjective.MinimumExcitement <= ObjectiveExcitementMin) + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.MinimumExcitement -= ObjectiveExcitementAdjustment; + Invalidate(); + } + break; + default: + if (gameState.ScenarioObjective.NumGuests <= ObjectiveGuestsMin) + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.NumGuests -= ObjectiveGuestsAdjustment; + Invalidate(); + } + break; + } + } + + void Arg2Increase() + { + auto& gameState = GetGameState(); + if (gameState.ScenarioObjective.Year >= ObjectiveYearMax) + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.Year += ObjectiveYearAdjustment; + Invalidate(); + } + } + + void Arg2Decrease() + { + auto& gameState = GetGameState(); + if (gameState.ScenarioObjective.Year <= ObjectiveYearMin) + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + else + { + gameState.ScenarioObjective.Year -= ObjectiveYearAdjustment; + Invalidate(); + } } - } #pragma region Main - /** - * - * rct2: 0x006719CA - */ - void OnMouseUpMain(WidgetIndex widgetIndex) - { - const auto& gameState = GetGameState(); - switch (widgetIndex) + /** + * + * rct2: 0x006719CA + */ + void OnMouseUpMain(WidgetIndex widgetIndex) { - case WIDX_PARK_NAME: + const auto& gameState = GetGameState(); + switch (widgetIndex) { - auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - WindowTextInputRawOpen( - this, WIDX_PARK_NAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, {}, park.Name.c_str(), ParkNameMaxLength); - break; - } - case WIDX_SCENARIO_NAME: - WindowTextInputRawOpen( - this, WIDX_SCENARIO_NAME, STR_SCENARIO_NAME, STR_ENTER_SCENARIO_NAME, {}, gameState.ScenarioName.c_str(), - ScenarioNameMaxLength); - break; - case WIDX_DETAILS: - WindowTextInputRawOpen( - this, WIDX_DETAILS, STR_PARK_SCENARIO_DETAILS, STR_ENTER_SCENARIO_DESCRIPTION, {}, - gameState.ScenarioDetails.c_str(), ScenarioDetailsNameMaxLength); - break; - } - } - - /** - * - * rct2: 0x00672254 - */ - void OnResizeMain() - { - WindowSetResize(*this, 450, 229, 450, 229); - } - - /** - * - * rct2: 0x00671A0D - */ - void OnMouseDownMain(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_OBJECTIVE_DROPDOWN: - ShowObjectiveDropdown(); - break; - case WIDX_OBJECTIVE_ARG_1_INCREASE: - Arg1Increase(); - break; - case WIDX_OBJECTIVE_ARG_1_DECREASE: - Arg1Decrease(); - break; - case WIDX_OBJECTIVE_ARG_2_INCREASE: - Arg2Increase(); - break; - case WIDX_OBJECTIVE_ARG_2_DECREASE: - Arg2Decrease(); - break; - case WIDX_CATEGORY_DROPDOWN: - ShowCategoryDropdown(); - break; - } - } - - /** - * - * rct2: 0x00671A54 - */ - void OnDropdownMain(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - auto& gameState = GetGameState(); - uint8_t newObjectiveType; - - if (dropdownIndex == -1) - return; - - switch (widgetIndex) - { - case WIDX_OBJECTIVE_DROPDOWN: - // TODO: Don't rely on string ID order - newObjectiveType = static_cast(gDropdownItems[dropdownIndex].Args - STR_OBJECTIVE_DROPDOWN_NONE); - if (gameState.ScenarioObjective.Type != newObjectiveType) - SetObjective(newObjectiveType); - break; - case WIDX_CATEGORY_DROPDOWN: - if (gameState.ScenarioCategory != static_cast(dropdownIndex)) - { - gameState.ScenarioCategory = static_cast(dropdownIndex); - Invalidate(); - } - break; - } - } - - /** - * - * rct2: 0x006721E7 - */ - void OnUpdateMain() - { - uint8_t objectiveType; - - frame_no++; - OnPrepareDraw(); - InvalidateWidget(WIDX_TAB_1); - - objectiveType = GetGameState().ScenarioObjective.Type; - - // Check if objective is allowed by money and pay-per-ride settings. - const bool objectiveAllowedByMoneyUsage = !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) - || !ObjectiveNeedsMoney(objectiveType); - // This objective can only work if the player can ask money for rides. - const bool objectiveAllowedByPaymentSettings = (objectiveType != OBJECTIVE_MONTHLY_RIDE_INCOME) - || ParkRidePricesUnlocked(); - if (!objectiveAllowedByMoneyUsage || !objectiveAllowedByPaymentSettings) - { - // Reset objective - SetObjective(OBJECTIVE_GUESTS_AND_RATING); - } - } - - /** - * - * rct2: 0x00671A73 - */ - void OnTextInputMain(WidgetIndex widgetIndex, std::string_view text) - { - if (text.empty()) - return; - - auto& gameState = GetGameState(); - switch (widgetIndex) - { - case WIDX_PARK_NAME: - { - auto action = ParkSetNameAction(std::string(text)); - GameActions::Execute(&action); - - if (gameState.ScenarioName.empty()) + case WIDX_PARK_NAME: { auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - gameState.ScenarioName = park.Name; + WindowTextInputRawOpen( + this, WIDX_PARK_NAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, {}, park.Name.c_str(), ParkNameMaxLength); + break; } - break; + case WIDX_SCENARIO_NAME: + WindowTextInputRawOpen( + this, WIDX_SCENARIO_NAME, STR_SCENARIO_NAME, STR_ENTER_SCENARIO_NAME, {}, + gameState.ScenarioName.c_str(), ScenarioNameMaxLength); + break; + case WIDX_DETAILS: + WindowTextInputRawOpen( + this, WIDX_DETAILS, STR_PARK_SCENARIO_DETAILS, STR_ENTER_SCENARIO_DESCRIPTION, {}, + gameState.ScenarioDetails.c_str(), ScenarioDetailsNameMaxLength); + break; } - case WIDX_SCENARIO_NAME: - gameState.ScenarioName = text; - Invalidate(); - break; - case WIDX_DETAILS: - gameState.ScenarioDetails = text; - Invalidate(); - break; - } - } - - /** - * - * rct2: 0x0067161C - */ - void OnPrepareDrawMain() - { - auto& gameState = GetGameState(); - auto widgetsToSet = window_editor_objective_options_widgets[page]; - if (widgets != widgetsToSet) - { - widgets = widgetsToSet; - InitScrollWidgets(); } - SetPressedTab(); - - switch (gameState.ScenarioObjective.Type) + /** + * + * rct2: 0x00672254 + */ + void OnResizeMain() { - case OBJECTIVE_GUESTS_BY: - case OBJECTIVE_PARK_VALUE_BY: - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1].type = WindowWidgetType::Spinner; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_INCREASE].type = WindowWidgetType::Button; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_DECREASE].type = WindowWidgetType::Button; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2].type = WindowWidgetType::Spinner; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_INCREASE].type = WindowWidgetType::Button; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_DECREASE].type = WindowWidgetType::Button; - break; - case OBJECTIVE_GUESTS_AND_RATING: - case OBJECTIVE_MONTHLY_RIDE_INCOME: - case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: - case OBJECTIVE_FINISH_5_ROLLERCOASTERS: - case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: - case OBJECTIVE_MONTHLY_FOOD_INCOME: - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1].type = WindowWidgetType::Spinner; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_INCREASE].type = WindowWidgetType::Button; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_DECREASE].type = WindowWidgetType::Button; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2].type = WindowWidgetType::Empty; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_INCREASE].type = WindowWidgetType::Empty; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_DECREASE].type = WindowWidgetType::Empty; - break; - default: - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1].type = WindowWidgetType::Empty; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_INCREASE].type = WindowWidgetType::Empty; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_DECREASE].type = WindowWidgetType::Empty; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2].type = WindowWidgetType::Empty; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_INCREASE].type = WindowWidgetType::Empty; - window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_DECREASE].type = WindowWidgetType::Empty; - break; + WindowSetResize(*this, 450, 229, 450, 229); } - window_editor_objective_options_main_widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) - ? WindowWidgetType::Empty - : WindowWidgetType::CloseBox; - - AnchorBorderWidgets(); - } - - /** - * - * rct2: 0x0067161C - */ - void OnDrawMain(DrawPixelInfo& dpi) - { - const auto& gameState = GetGameState(); - int32_t widthToSet; - StringId stringId; - - DrawWidgets(dpi); - DrawTabImages(dpi); - - // Objective label - auto screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_OBJECTIVE].top }; - DrawTextBasic(dpi, screenCoords, STR_OBJECTIVE_WINDOW); - - // Objective value - screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_OBJECTIVE].left + 1, widgets[WIDX_OBJECTIVE].top }; - auto ft = Formatter(); - ft.Add(ObjectiveDropdownOptionNames[gameState.ScenarioObjective.Type]); - DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); - - if (widgets[WIDX_OBJECTIVE_ARG_1].type != WindowWidgetType::Empty) + /** + * + * rct2: 0x00671A0D + */ + void OnMouseDownMain(WidgetIndex widgetIndex) { - // Objective argument 1 label - screenCoords = windowPos + ScreenCoordsXY{ 28, widgets[WIDX_OBJECTIVE_ARG_1].top }; + switch (widgetIndex) + { + case WIDX_OBJECTIVE_DROPDOWN: + ShowObjectiveDropdown(); + break; + case WIDX_OBJECTIVE_ARG_1_INCREASE: + Arg1Increase(); + break; + case WIDX_OBJECTIVE_ARG_1_DECREASE: + Arg1Decrease(); + break; + case WIDX_OBJECTIVE_ARG_2_INCREASE: + Arg2Increase(); + break; + case WIDX_OBJECTIVE_ARG_2_DECREASE: + Arg2Decrease(); + break; + case WIDX_CATEGORY_DROPDOWN: + ShowCategoryDropdown(); + break; + } + } + + /** + * + * rct2: 0x00671A54 + */ + void OnDropdownMain(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + auto& gameState = GetGameState(); + uint8_t newObjectiveType; + + if (dropdownIndex == -1) + return; + + switch (widgetIndex) + { + case WIDX_OBJECTIVE_DROPDOWN: + // TODO: Don't rely on string ID order + newObjectiveType = static_cast(gDropdownItems[dropdownIndex].Args - STR_OBJECTIVE_DROPDOWN_NONE); + if (gameState.ScenarioObjective.Type != newObjectiveType) + SetObjective(newObjectiveType); + break; + case WIDX_CATEGORY_DROPDOWN: + if (gameState.ScenarioCategory != static_cast(dropdownIndex)) + { + gameState.ScenarioCategory = static_cast(dropdownIndex); + Invalidate(); + } + break; + } + } + + /** + * + * rct2: 0x006721E7 + */ + void OnUpdateMain() + { + uint8_t objectiveType; + + frame_no++; + OnPrepareDraw(); + InvalidateWidget(WIDX_TAB_1); + + objectiveType = GetGameState().ScenarioObjective.Type; + + // Check if objective is allowed by money and pay-per-ride settings. + const bool objectiveAllowedByMoneyUsage = !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) + || !ObjectiveNeedsMoney(objectiveType); + // This objective can only work if the player can ask money for rides. + const bool objectiveAllowedByPaymentSettings = (objectiveType != OBJECTIVE_MONTHLY_RIDE_INCOME) + || ParkRidePricesUnlocked(); + if (!objectiveAllowedByMoneyUsage || !objectiveAllowedByPaymentSettings) + { + // Reset objective + SetObjective(OBJECTIVE_GUESTS_AND_RATING); + } + } + + /** + * + * rct2: 0x00671A73 + */ + void OnTextInputMain(WidgetIndex widgetIndex, std::string_view text) + { + if (text.empty()) + return; + + auto& gameState = GetGameState(); + switch (widgetIndex) + { + case WIDX_PARK_NAME: + { + auto action = ParkSetNameAction(std::string(text)); + GameActions::Execute(&action); + + if (gameState.ScenarioName.empty()) + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + gameState.ScenarioName = park.Name; + } + break; + } + case WIDX_SCENARIO_NAME: + gameState.ScenarioName = text; + Invalidate(); + break; + case WIDX_DETAILS: + gameState.ScenarioDetails = text; + Invalidate(); + break; + } + } + + /** + * + * rct2: 0x0067161C + */ + void OnPrepareDrawMain() + { + auto& gameState = GetGameState(); + auto widgetsToSet = window_editor_objective_options_widgets[page]; + if (widgets != widgetsToSet) + { + widgets = widgetsToSet; + InitScrollWidgets(); + } + + SetPressedTab(); + switch (gameState.ScenarioObjective.Type) { case OBJECTIVE_GUESTS_BY: - case OBJECTIVE_GUESTS_AND_RATING: - stringId = STR_WINDOW_OBJECTIVE_GUEST_COUNT; - break; case OBJECTIVE_PARK_VALUE_BY: - case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: - stringId = STR_WINDOW_OBJECTIVE_PARK_VALUE; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1].type = WindowWidgetType::Spinner; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_INCREASE].type = WindowWidgetType::Button; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_DECREASE].type = WindowWidgetType::Button; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2].type = WindowWidgetType::Spinner; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_INCREASE].type = WindowWidgetType::Button; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_DECREASE].type = WindowWidgetType::Button; break; - case OBJECTIVE_MONTHLY_RIDE_INCOME: - stringId = STR_WINDOW_OBJECTIVE_MONTHLY_INCOME; - break; - case OBJECTIVE_MONTHLY_FOOD_INCOME: - stringId = STR_WINDOW_OBJECTIVE_MONTHLY_PROFIT; - break; - case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: - stringId = STR_WINDOW_OBJECTIVE_MINIMUM_LENGTH; - break; - default: - stringId = STR_WINDOW_OBJECTIVE_EXCITEMENT_RATING; - break; - } - DrawTextBasic(dpi, screenCoords, stringId); - - // Objective argument 1 value - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_OBJECTIVE_ARG_1].left + 1, widgets[WIDX_OBJECTIVE_ARG_1].top }; - ft = Formatter(); - switch (gameState.ScenarioObjective.Type) - { - case OBJECTIVE_GUESTS_BY: case OBJECTIVE_GUESTS_AND_RATING: - stringId = STR_WINDOW_COLOUR_2_COMMA16; - ft.Add(gameState.ScenarioObjective.NumGuests); - break; - case OBJECTIVE_PARK_VALUE_BY: - case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: case OBJECTIVE_MONTHLY_RIDE_INCOME: - case OBJECTIVE_MONTHLY_FOOD_INCOME: - stringId = STR_CURRENCY_FORMAT_LABEL; - ft.Add(gameState.ScenarioObjective.Currency); - break; case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: - stringId = STR_WINDOW_COLOUR_2_LENGTH; - ft.Add(gameState.ScenarioObjective.MinimumLength); - break; case OBJECTIVE_FINISH_5_ROLLERCOASTERS: - stringId = STR_WINDOW_COLOUR_2_COMMA2DP32; - ft.Add(gameState.ScenarioObjective.MinimumExcitement); + case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: + case OBJECTIVE_MONTHLY_FOOD_INCOME: + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1].type = WindowWidgetType::Spinner; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_INCREASE].type = WindowWidgetType::Button; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_DECREASE].type = WindowWidgetType::Button; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2].type = WindowWidgetType::Empty; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_INCREASE].type = WindowWidgetType::Empty; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_DECREASE].type = WindowWidgetType::Empty; break; default: - stringId = STR_WINDOW_COLOUR_2_COMMA2DP32; - ft.Add(gameState.ScenarioObjective.Currency); + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1].type = WindowWidgetType::Empty; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_INCREASE].type = WindowWidgetType::Empty; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_1_DECREASE].type = WindowWidgetType::Empty; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2].type = WindowWidgetType::Empty; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_INCREASE].type = WindowWidgetType::Empty; + window_editor_objective_options_main_widgets[WIDX_OBJECTIVE_ARG_2_DECREASE].type = WindowWidgetType::Empty; break; } - DrawTextBasic(dpi, screenCoords, stringId, ft, COLOUR_BLACK); + + window_editor_objective_options_main_widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + ? WindowWidgetType::Empty + : WindowWidgetType::CloseBox; + + AnchorBorderWidgets(); } - if (widgets[WIDX_OBJECTIVE_ARG_2].type != WindowWidgetType::Empty) + /** + * + * rct2: 0x0067161C + */ + void OnDrawMain(DrawPixelInfo& dpi) { - // Objective argument 2 label - screenCoords = windowPos + ScreenCoordsXY{ 28, widgets[WIDX_OBJECTIVE_ARG_2].top }; - DrawTextBasic(dpi, screenCoords, STR_WINDOW_OBJECTIVE_DATE); + const auto& gameState = GetGameState(); + int32_t widthToSet; + StringId stringId; - // Objective argument 2 value - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_OBJECTIVE_ARG_2].left + 1, widgets[WIDX_OBJECTIVE_ARG_2].top }; - ft = Formatter(); - ft.Add((gameState.ScenarioObjective.Year * MONTH_COUNT) - 1); - DrawTextBasic(dpi, screenCoords, STR_WINDOW_OBJECTIVE_VALUE_DATE, ft); - } + DrawWidgets(dpi); + DrawTabImages(dpi); - // Park name - screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_PARK_NAME].top }; - widthToSet = widgets[WIDX_PARK_NAME].left - 16; + // Objective label + auto screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_OBJECTIVE].top }; + DrawTextBasic(dpi, screenCoords, STR_OBJECTIVE_WINDOW); - { - auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - auto parkName = park.Name.c_str(); + // Objective value + screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_OBJECTIVE].left + 1, widgets[WIDX_OBJECTIVE].top }; + auto ft = Formatter(); + ft.Add(ObjectiveDropdownOptionNames[gameState.ScenarioObjective.Type]); + DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); + + if (widgets[WIDX_OBJECTIVE_ARG_1].type != WindowWidgetType::Empty) + { + // Objective argument 1 label + screenCoords = windowPos + ScreenCoordsXY{ 28, widgets[WIDX_OBJECTIVE_ARG_1].top }; + switch (gameState.ScenarioObjective.Type) + { + case OBJECTIVE_GUESTS_BY: + case OBJECTIVE_GUESTS_AND_RATING: + stringId = STR_WINDOW_OBJECTIVE_GUEST_COUNT; + break; + case OBJECTIVE_PARK_VALUE_BY: + case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: + stringId = STR_WINDOW_OBJECTIVE_PARK_VALUE; + break; + case OBJECTIVE_MONTHLY_RIDE_INCOME: + stringId = STR_WINDOW_OBJECTIVE_MONTHLY_INCOME; + break; + case OBJECTIVE_MONTHLY_FOOD_INCOME: + stringId = STR_WINDOW_OBJECTIVE_MONTHLY_PROFIT; + break; + case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: + stringId = STR_WINDOW_OBJECTIVE_MINIMUM_LENGTH; + break; + default: + stringId = STR_WINDOW_OBJECTIVE_EXCITEMENT_RATING; + break; + } + DrawTextBasic(dpi, screenCoords, stringId); + + // Objective argument 1 value + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_OBJECTIVE_ARG_1].left + 1, widgets[WIDX_OBJECTIVE_ARG_1].top }; + ft = Formatter(); + switch (gameState.ScenarioObjective.Type) + { + case OBJECTIVE_GUESTS_BY: + case OBJECTIVE_GUESTS_AND_RATING: + stringId = STR_WINDOW_COLOUR_2_COMMA16; + ft.Add(gameState.ScenarioObjective.NumGuests); + break; + case OBJECTIVE_PARK_VALUE_BY: + case OBJECTIVE_REPAY_LOAN_AND_PARK_VALUE: + case OBJECTIVE_MONTHLY_RIDE_INCOME: + case OBJECTIVE_MONTHLY_FOOD_INCOME: + stringId = STR_CURRENCY_FORMAT_LABEL; + ft.Add(gameState.ScenarioObjective.Currency); + break; + case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: + stringId = STR_WINDOW_COLOUR_2_LENGTH; + ft.Add(gameState.ScenarioObjective.MinimumLength); + break; + case OBJECTIVE_FINISH_5_ROLLERCOASTERS: + stringId = STR_WINDOW_COLOUR_2_COMMA2DP32; + ft.Add(gameState.ScenarioObjective.MinimumExcitement); + break; + default: + stringId = STR_WINDOW_COLOUR_2_COMMA2DP32; + ft.Add(gameState.ScenarioObjective.Currency); + break; + } + DrawTextBasic(dpi, screenCoords, stringId, ft, COLOUR_BLACK); + } + + if (widgets[WIDX_OBJECTIVE_ARG_2].type != WindowWidgetType::Empty) + { + // Objective argument 2 label + screenCoords = windowPos + ScreenCoordsXY{ 28, widgets[WIDX_OBJECTIVE_ARG_2].top }; + DrawTextBasic(dpi, screenCoords, STR_WINDOW_OBJECTIVE_DATE); + + // Objective argument 2 value + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_OBJECTIVE_ARG_2].left + 1, widgets[WIDX_OBJECTIVE_ARG_2].top }; + ft = Formatter(); + ft.Add((gameState.ScenarioObjective.Year * MONTH_COUNT) - 1); + DrawTextBasic(dpi, screenCoords, STR_WINDOW_OBJECTIVE_VALUE_DATE, ft); + } + + // Park name + screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_PARK_NAME].top }; + widthToSet = widgets[WIDX_PARK_NAME].left - 16; + + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(parkName); + DrawTextEllipsised(dpi, screenCoords, widthToSet, STR_WINDOW_PARK_NAME, ft); + } + + // Scenario name + screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_SCENARIO_NAME].top }; + widthToSet = widgets[WIDX_SCENARIO_NAME].left - 16; ft = Formatter(); ft.Add(STR_STRING); - ft.Add(parkName); - DrawTextEllipsised(dpi, screenCoords, widthToSet, STR_WINDOW_PARK_NAME, ft); + ft.Add(gameState.ScenarioName.c_str()); + DrawTextEllipsised(dpi, screenCoords, widthToSet, STR_WINDOW_SCENARIO_NAME, ft); + + // Scenario details label + screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_DETAILS].top }; + DrawTextBasic(dpi, screenCoords, STR_WINDOW_PARK_DETAILS); + + // Scenario details value + screenCoords = windowPos + ScreenCoordsXY{ 16, widgets[WIDX_DETAILS].top + 10 }; + widthToSet = widgets[WIDX_DETAILS].left - 4; + + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(gameState.ScenarioDetails.c_str()); + DrawTextWrapped(dpi, screenCoords, widthToSet, STR_BLACK_STRING, ft); + + // Scenario category label + screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_CATEGORY].top }; + DrawTextBasic(dpi, screenCoords, STR_WINDOW_SCENARIO_GROUP); + + // Scenario category value + screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_CATEGORY].left + 1, widgets[WIDX_CATEGORY].top }; + ft = Formatter(); + ft.Add(ScenarioCategoryStringIds[gameState.ScenarioCategory]); + DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); } - // Scenario name - screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_SCENARIO_NAME].top }; - widthToSet = widgets[WIDX_SCENARIO_NAME].left - 16; - - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(gameState.ScenarioName.c_str()); - DrawTextEllipsised(dpi, screenCoords, widthToSet, STR_WINDOW_SCENARIO_NAME, ft); - - // Scenario details label - screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_DETAILS].top }; - DrawTextBasic(dpi, screenCoords, STR_WINDOW_PARK_DETAILS); - - // Scenario details value - screenCoords = windowPos + ScreenCoordsXY{ 16, widgets[WIDX_DETAILS].top + 10 }; - widthToSet = widgets[WIDX_DETAILS].left - 4; - - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(gameState.ScenarioDetails.c_str()); - DrawTextWrapped(dpi, screenCoords, widthToSet, STR_BLACK_STRING, ft); - - // Scenario category label - screenCoords = windowPos + ScreenCoordsXY{ 8, widgets[WIDX_CATEGORY].top }; - DrawTextBasic(dpi, screenCoords, STR_WINDOW_SCENARIO_GROUP); - - // Scenario category value - screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_CATEGORY].left + 1, widgets[WIDX_CATEGORY].top }; - ft = Formatter(); - ft.Add(ScenarioCategoryStringIds[gameState.ScenarioCategory]); - DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); - } - #pragma endregion #pragma region Rides - /** - * - * rct2: 0x006725A8 - */ - void OnResizeRides() - { - WindowSetResize(*this, 380, 224, 380, 224); - } - - /** - * - * rct2: 0x00672544 - */ - void OnUpdateRides() - { - frame_no++; - OnPrepareDraw(); - OnResize(); - InvalidateWidget(WIDX_TAB_2); - - const auto oldSize = _rideableRides.size(); - _rideableRides.clear(); - for (auto& currentRide : GetRideManager()) + /** + * + * rct2: 0x006725A8 + */ + void OnResizeRides() { - if (currentRide.IsRide()) + WindowSetResize(*this, 380, 224, 380, 224); + } + + /** + * + * rct2: 0x00672544 + */ + void OnUpdateRides() + { + frame_no++; + OnPrepareDraw(); + OnResize(); + InvalidateWidget(WIDX_TAB_2); + + const auto oldSize = _rideableRides.size(); + _rideableRides.clear(); + for (auto& currentRide : GetRideManager()) { - _rideableRides.push_back(currentRide.id); + if (currentRide.IsRide()) + { + _rideableRides.push_back(currentRide.id); + } + } + + if (oldSize != _rideableRides.size()) + { + Invalidate(); } } - if (oldSize != _rideableRides.size()) + /** + * + * rct2: 0x006724BF + */ + ScreenSize OnScrollGetSizeRides(int32_t scrollIndex) { - Invalidate(); - } - } + ScreenSize newSize; + newSize.height = static_cast(_rideableRides.size()) * 10; - /** - * - * rct2: 0x006724BF - */ - ScreenSize OnScrollGetSizeRides(int32_t scrollIndex) - { - ScreenSize newSize; - newSize.height = static_cast(_rideableRides.size()) * 10; - - return newSize; - } - - /** - * - * rct2: 0x006724FC - */ - void OnScrollMouseDownRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) - { - auto i = screenCoords.y / 12; - if (i < 0 || i >= static_cast(_rideableRides.size())) - return; - - auto* currentRide = GetRide(_rideableRides[i]); - if (currentRide != nullptr) - { - currentRide->lifecycle_flags ^= RIDE_LIFECYCLE_INDESTRUCTIBLE; - } - Invalidate(); - } - - /** - * - * rct2: 0x006724CC - */ - void OnScrollMouseOverRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) - { - int32_t i; - - i = screenCoords.y / 12; - if (i < 0 || i >= static_cast(_rideableRides.size())) - return; - - if (selected_list_item != i) - { - selected_list_item = i; - Invalidate(); - } - } - - /** - * - * rct2: 0x006722B5 - */ - void OnPrepareDrawRides() - { - Widget* widgetsToSet = window_editor_objective_options_widgets[page]; - if (widgets != widgetsToSet) - { - widgets = widgetsToSet; - InitScrollWidgets(); + return newSize; } - SetPressedTab(); - - window_editor_objective_options_main_widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) - ? WindowWidgetType::Empty - : WindowWidgetType::CloseBox; - - AnchorBorderWidgets(); - } - - /** - * - * rct2: 0x00672340 - */ - void OnDrawRides(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PAGE_BACKGROUND].top + 3 }, STR_WINDOW_PRESERVATION_ORDER); - } - - /** - * - * rct2: 0x0067236F - */ - void OnScrollDrawRides(DrawPixelInfo& dpi, int32_t scrollIndex) - { - int32_t colour = ColourMapA[colours[1]].mid_light; - GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, colour); - - for (int32_t i = 0; i < static_cast(_rideableRides.size()); i++) + /** + * + * rct2: 0x006724FC + */ + void OnScrollMouseDownRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) { - int32_t y = i * 12; + auto i = screenCoords.y / 12; + if (i < 0 || i >= static_cast(_rideableRides.size())) + return; - if (y + 12 < dpi.y || y >= dpi.y + dpi.height) - continue; - - // Checkbox - GfxFillRectInset(dpi, { { 2, y }, { 11, y + 10 } }, colours[1], INSET_RECT_F_E0); - - // Highlighted - auto stringId = STR_BLACK_STRING; - if (i == selected_list_item) - { - stringId = STR_WINDOW_COLOUR_2_STRINGID; - GfxFilterRect(dpi, { 0, y, width, y + 11 }, FilterPaletteID::PaletteDarken1); - } - - // Checkbox mark auto* currentRide = GetRide(_rideableRides[i]); if (currentRide != nullptr) { - if (currentRide->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) - { - auto darkness = stringId == STR_WINDOW_COLOUR_2_STRINGID ? TextDarkness::ExtraDark : TextDarkness::Dark; - GfxDrawString( - dpi, { 2, y }, static_cast(CheckBoxMarkString), - { static_cast(colours[1] & 0x7F), FontStyle::Medium, darkness }); - } + currentRide->lifecycle_flags ^= RIDE_LIFECYCLE_INDESTRUCTIBLE; + } + Invalidate(); + } - // Ride name + /** + * + * rct2: 0x006724CC + */ + void OnScrollMouseOverRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) + { + int32_t i; - Formatter ft; - currentRide->FormatNameTo(ft); - DrawTextBasic(dpi, { 15, y }, stringId, ft); + i = screenCoords.y / 12; + if (i < 0 || i >= static_cast(_rideableRides.size())) + return; + + if (selected_list_item != i) + { + selected_list_item = i; + Invalidate(); + } + } + + /** + * + * rct2: 0x006722B5 + */ + void OnPrepareDrawRides() + { + Widget* widgetsToSet = window_editor_objective_options_widgets[page]; + if (widgets != widgetsToSet) + { + widgets = widgetsToSet; + InitScrollWidgets(); + } + + SetPressedTab(); + + window_editor_objective_options_main_widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + ? WindowWidgetType::Empty + : WindowWidgetType::CloseBox; + + AnchorBorderWidgets(); + } + + /** + * + * rct2: 0x00672340 + */ + void OnDrawRides(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PAGE_BACKGROUND].top + 3 }, STR_WINDOW_PRESERVATION_ORDER); + } + + /** + * + * rct2: 0x0067236F + */ + void OnScrollDrawRides(DrawPixelInfo& dpi, int32_t scrollIndex) + { + int32_t colour = ColourMapA[colours[1]].mid_light; + GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, colour); + + for (int32_t i = 0; i < static_cast(_rideableRides.size()); i++) + { + int32_t y = i * 12; + + if (y + 12 < dpi.y || y >= dpi.y + dpi.height) + continue; + + // Checkbox + GfxFillRectInset(dpi, { { 2, y }, { 11, y + 10 } }, colours[1], INSET_RECT_F_E0); + + // Highlighted + auto stringId = STR_BLACK_STRING; + if (i == selected_list_item) + { + stringId = STR_WINDOW_COLOUR_2_STRINGID; + GfxFilterRect(dpi, { 0, y, width, y + 11 }, FilterPaletteID::PaletteDarken1); + } + + // Checkbox mark + auto* currentRide = GetRide(_rideableRides[i]); + if (currentRide != nullptr) + { + if (currentRide->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) + { + auto darkness = stringId == STR_WINDOW_COLOUR_2_STRINGID ? TextDarkness::ExtraDark : TextDarkness::Dark; + GfxDrawString( + dpi, { 2, y }, static_cast(CheckBoxMarkString), + { static_cast(colours[1] & 0x7F), FontStyle::Medium, darkness }); + } + + // Ride name + + Formatter ft; + currentRide->FormatNameTo(ft); + DrawTextBasic(dpi, { 15, y }, stringId, ft); + } } } - } #pragma endregion -}; + }; + + /** + * + * rct2: 0x0067137D + */ + WindowBase* WindowEditorObjectiveOptionsOpen() + { + auto window = WindowBringToFrontByClass(WindowClass::EditorObjectiveOptions); + if (window != nullptr) + return window; + + window = WindowCreate( + WindowClass::EditorObjectiveOptions, 450, 225, WF_10 | WF_CENTRE_SCREEN); -/** - * - * rct2: 0x0067137D - */ -WindowBase* WindowEditorObjectiveOptionsOpen() -{ - auto window = WindowBringToFrontByClass(WindowClass::EditorObjectiveOptions); - if (window != nullptr) return window; - - window = WindowCreate( - WindowClass::EditorObjectiveOptions, 450, 225, WF_10 | WF_CENTRE_SCREEN); - - return window; -} + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/EditorScenarioOptions.cpp b/src/openrct2-ui/windows/EditorScenarioOptions.cpp index d923c7079d..dc4101f236 100644 --- a/src/openrct2-ui/windows/EditorScenarioOptions.cpp +++ b/src/openrct2-ui/windows/EditorScenarioOptions.cpp @@ -30,20 +30,20 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr int32_t WW_FINANCIAL = 280; + static constexpr int32_t WH_FINANCIAL = 149; -static constexpr int32_t WW_FINANCIAL = 280; -static constexpr int32_t WH_FINANCIAL = 149; + static constexpr int32_t WW_GUESTS = 380; + static constexpr int32_t WH_GUESTS = 149; -static constexpr int32_t WW_GUESTS = 380; -static constexpr int32_t WH_GUESTS = 149; - -static constexpr int32_t WW_PARK = 400; -static constexpr int32_t WH_PARK = 200; + static constexpr int32_t WW_PARK = 400; + static constexpr int32_t WH_PARK = 200; #pragma region Widgets -// clang-format off + // clang-format off enum { WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL, WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS, @@ -208,1080 +208,1082 @@ static uint32_t window_editor_scenario_options_page_hold_down_widgets[] = { (1uLL << WIDX_ENTRY_PRICE_INCREASE) | (1uLL << WIDX_ENTRY_PRICE_DECREASE), }; -// clang-format on + // clang-format on #pragma endregion -class EditorScenarioOptionsWindow final : public Window -{ -public: - void OnOpen() override + class EditorScenarioOptionsWindow final : public Window { - widgets = window_editor_scenario_options_widgets[WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL]; - hold_down_widgets = window_editor_scenario_options_page_hold_down_widgets - [WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL]; - WindowInitScrollWidgets(*this); - page = 0; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (page) + public: + void OnOpen() override { - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: - return FinancialMouseUp(widgetIndex); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: - return GuestsMouseUp(widgetIndex); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: - return ParkMouseUp(widgetIndex); + widgets = window_editor_scenario_options_widgets[WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL]; + hold_down_widgets = window_editor_scenario_options_page_hold_down_widgets + [WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL]; + WindowInitScrollWidgets(*this); + page = 0; } - } - void OnResize() override - { - switch (page) + void OnMouseUp(WidgetIndex widgetIndex) override { - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: - return FinancialResize(); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: - return GuestsResize(); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: - return ParkResize(); + switch (page) + { + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: + return FinancialMouseUp(widgetIndex); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: + return GuestsMouseUp(widgetIndex); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: + return ParkMouseUp(widgetIndex); + } } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (page) + void OnResize() override { - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: - return FinancialMouseDown(widgetIndex); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: - return GuestsMouseDown(widgetIndex); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: - return ParkMouseDown(widgetIndex); + switch (page) + { + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: + return FinancialResize(); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: + return GuestsResize(); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: + return ParkResize(); + } } - } - void OnUpdate() override - { - switch (page) + void OnMouseDown(WidgetIndex widgetIndex) override { - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: - return FinancialUpdate(); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: - return GuestsUpdate(); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: - return ParkUpdate(); + switch (page) + { + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: + return FinancialMouseDown(widgetIndex); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: + return GuestsMouseDown(widgetIndex); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: + return ParkMouseDown(widgetIndex); + } } - } - void OnPrepareDraw() override - { - switch (page) + void OnUpdate() override { - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: - return FinancialPrepareDraw(); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: - return GuestsPrepareDraw(); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: - return ParkPrepareDraw(); + switch (page) + { + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: + return FinancialUpdate(); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: + return GuestsUpdate(); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: + return ParkUpdate(); + } } - } - void OnDraw(DrawPixelInfo& dpi) override - { - switch (page) + void OnPrepareDraw() override { - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: - return FinancialDraw(dpi); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: - return GuestsDraw(dpi); - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: - return ParkDraw(dpi); + switch (page) + { + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: + return FinancialPrepareDraw(); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: + return GuestsPrepareDraw(); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: + return ParkPrepareDraw(); + } } - } - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - switch (page) + void OnDraw(DrawPixelInfo& dpi) override { - case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: - return ParkDropdown(widgetIndex, selectedIndex); + switch (page) + { + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL: + return FinancialDraw(dpi); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS: + return GuestsDraw(dpi); + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: + return ParkDraw(dpi); + } } - } -private: - void SetPressedTab() - { - int32_t i; - for (i = 0; i < WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_COUNT; i++) - SetWidgetPressed(WIDX_TAB_1 + i, false); - SetWidgetPressed(WIDX_TAB_1 + page, true); - } + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + switch (page) + { + case WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_PARK: + return ParkDropdown(widgetIndex, selectedIndex); + } + } - void AnchorBorderWidgets() - { - ResizeFrameWithPage(); - } + private: + void SetPressedTab() + { + int32_t i; + for (i = 0; i < WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_COUNT; i++) + SetWidgetPressed(WIDX_TAB_1 + i, false); + SetWidgetPressed(WIDX_TAB_1 + page, true); + } - void DrawTabImages(DrawPixelInfo& dpi) - { - Widget* widget; - int32_t spriteIndex; + void AnchorBorderWidgets() + { + ResizeFrameWithPage(); + } - // Tab 1 - widget = &widgets[WIDX_TAB_1]; - spriteIndex = SPR_TAB_FINANCES_SUMMARY_0; - if (page == WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL) - spriteIndex += (frame_no / 2) % 8; + void DrawTabImages(DrawPixelInfo& dpi) + { + Widget* widget; + int32_t spriteIndex; - GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); + // Tab 1 + widget = &widgets[WIDX_TAB_1]; + spriteIndex = SPR_TAB_FINANCES_SUMMARY_0; + if (page == WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_FINANCIAL) + spriteIndex += (frame_no / 2) % 8; - // Tab 2 - widget = &widgets[WIDX_TAB_2]; - spriteIndex = SPR_TAB_GUESTS_0; - if (page == WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS) - spriteIndex += (frame_no / 4) % 8; + GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); - GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); + // Tab 2 + widget = &widgets[WIDX_TAB_2]; + spriteIndex = SPR_TAB_GUESTS_0; + if (page == WINDOW_EDITOR_SCENARIO_OPTIONS_PAGE_GUESTS) + spriteIndex += (frame_no / 4) % 8; - // Tab 3 - widget = &widgets[WIDX_TAB_3]; - spriteIndex = SPR_TAB_PARK; - GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); - } + GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); - void SetPage(int32_t newPage) - { - if (page == newPage) - return; + // Tab 3 + widget = &widgets[WIDX_TAB_3]; + spriteIndex = SPR_TAB_PARK; + GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget->left, widget->top }); + } - page = newPage; - frame_no = 0; - hold_down_widgets = window_editor_scenario_options_page_hold_down_widgets[page]; - widgets = window_editor_scenario_options_widgets[page]; - Invalidate(); - OnResize(); - OnPrepareDraw(); - WindowInitScrollWidgets(*this); - Invalidate(); - } + void SetPage(int32_t newPage) + { + if (page == newPage) + return; + + page = newPage; + frame_no = 0; + hold_down_widgets = window_editor_scenario_options_page_hold_down_widgets[page]; + widgets = window_editor_scenario_options_widgets[page]; + Invalidate(); + OnResize(); + OnPrepareDraw(); + WindowInitScrollWidgets(*this); + Invalidate(); + } #pragma region Financial - void FinancialMouseUp(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) + void FinancialMouseUp(WidgetIndex widgetIndex) { - case WIDX_CLOSE: - WindowClose(*this); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - SetPage(widgetIndex - WIDX_TAB_1); - break; - case WIDX_NO_MONEY: + auto& gameState = GetGameState(); + switch (widgetIndex) { - auto newMoneySetting = (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) ? 0 : 1; - auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::NoMoney, newMoneySetting); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; - } - case WIDX_FORBID_MARKETING: - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::ForbidMarketingCampaigns, - gameState.ParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; - } - case WIDX_RCT1_INTEREST: - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::UseRCT1Interest, gameState.ParkFlags & PARK_FLAGS_RCT1_INTEREST ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; + case WIDX_CLOSE: + WindowClose(*this); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + SetPage(widgetIndex - WIDX_TAB_1); + break; + case WIDX_NO_MONEY: + { + auto newMoneySetting = (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) ? 0 : 1; + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::NoMoney, newMoneySetting); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_FORBID_MARKETING: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidMarketingCampaigns, + gameState.ParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_RCT1_INTEREST: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::UseRCT1Interest, gameState.ParkFlags & PARK_FLAGS_RCT1_INTEREST ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } } } - } - void FinancialResize() - { - WindowSetResize(*this, 280, 149, 280, 149); - } - - void ShowClimateDropdown() - { - int32_t i; - - const auto& dropdownWidget = widgets[WIDX_CLIMATE]; - - for (i = 0; i < static_cast(ClimateType::Count); i++) + void FinancialResize() { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = ClimateNames[i]; - } - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget.left, windowPos.y + dropdownWidget.top }, dropdownWidget.height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, static_cast(ClimateType::Count), dropdownWidget.width() - 3); - Dropdown::SetChecked(static_cast(GetGameState().Climate), true); - } - - void FinancialMouseDown(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) - { - case WIDX_INITIAL_CASH_INCREASE: - if (gameState.InitialCash < 1000000.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::InitialCash, gameState.InitialCash + 500.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_CASH, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_INITIAL_CASH_DECREASE: - if (gameState.InitialCash > 0.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::InitialCash, gameState.InitialCash - 500.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_CASH, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_INITIAL_LOAN_INCREASE: - if (gameState.BankLoan < 5000000.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::InitialLoan, gameState.BankLoan + 1000.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_INIT_LOAN, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_INITIAL_LOAN_DECREASE: - if (gameState.BankLoan > 0.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::InitialLoan, gameState.BankLoan - 1000.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_INIT_LOAN, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_MAXIMUM_LOAN_INCREASE: - if (gameState.MaxBankLoan < 5000000.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::MaximumLoanSize, gameState.MaxBankLoan + 1000.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_MAX_LOAN, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_MAXIMUM_LOAN_DECREASE: - if (gameState.MaxBankLoan > 0.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::MaximumLoanSize, gameState.MaxBankLoan - 1000.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_MAX_LOAN, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_INTEREST_RATE_INCREASE: - if (gameState.BankLoanInterestRate < MaxBankLoanInterestRate) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::AnnualInterestRate, gameState.BankLoanInterestRate + 1); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_INTEREST_RATE, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_INTEREST_RATE_DECREASE: - if (gameState.BankLoanInterestRate > 0) - { - auto interest = std::min(MaxBankLoanInterestRate, gameState.BankLoanInterestRate - 1); - auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::AnnualInterestRate, interest); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_INTEREST_RATE, STR_NONE, {}); - } - Invalidate(); - break; + WindowSetResize(*this, 280, 149, 280, 149); } - if (gScreenFlags == SCREEN_FLAGS_PLAYING) + void ShowClimateDropdown() { - WindowInvalidateByClass(WindowClass::Finances); - WindowInvalidateByClass(WindowClass::BottomToolbar); - } - } + int32_t i; - void FinancialUpdate() - { - frame_no++; - FinancialPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_1); - } + const auto& dropdownWidget = widgets[WIDX_CLIMATE]; - void FinancialPrepareDraw() - { - Widget* newWidgets = window_editor_scenario_options_widgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - WindowInitScrollWidgets(*this); - } - - SetPressedTab(); - - auto& gameState = GetGameState(); - if (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) - { - SetWidgetPressed(WIDX_NO_MONEY, true); - for (int32_t i = WIDX_INITIAL_CASH; i <= WIDX_RCT1_INTEREST; i++) - widgets[i].type = WindowWidgetType::Empty; - } - else - { - SetWidgetPressed(WIDX_NO_MONEY, false); - widgets[WIDX_INITIAL_CASH].type = WindowWidgetType::Spinner; - widgets[WIDX_INITIAL_CASH_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_INITIAL_CASH_DECREASE].type = WindowWidgetType::Button; - widgets[WIDX_INITIAL_LOAN].type = WindowWidgetType::Spinner; - widgets[WIDX_INITIAL_LOAN_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_INITIAL_LOAN_DECREASE].type = WindowWidgetType::Button; - widgets[WIDX_MAXIMUM_LOAN].type = WindowWidgetType::Spinner; - widgets[WIDX_MAXIMUM_LOAN_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_MAXIMUM_LOAN_DECREASE].type = WindowWidgetType::Button; - widgets[WIDX_FORBID_MARKETING].type = WindowWidgetType::Checkbox; - - if (gameState.ParkFlags & PARK_FLAGS_RCT1_INTEREST) + for (i = 0; i < static_cast(ClimateType::Count); i++) { - widgets[WIDX_INTEREST_RATE].type = WindowWidgetType::Empty; - widgets[WIDX_INTEREST_RATE_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_INTEREST_RATE_DECREASE].type = WindowWidgetType::Empty; - widgets[WIDX_RCT1_INTEREST].type = WindowWidgetType::Checkbox; - SetWidgetPressed(WIDX_RCT1_INTEREST, true); + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = ClimateNames[i]; + } + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget.left, windowPos.y + dropdownWidget.top }, dropdownWidget.height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, static_cast(ClimateType::Count), dropdownWidget.width() - 3); + Dropdown::SetChecked(static_cast(GetGameState().Climate), true); + } + + void FinancialMouseDown(WidgetIndex widgetIndex) + { + auto& gameState = GetGameState(); + switch (widgetIndex) + { + case WIDX_INITIAL_CASH_INCREASE: + if (gameState.InitialCash < 1000000.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialCash, gameState.InitialCash + 500.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_CASH, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_INITIAL_CASH_DECREASE: + if (gameState.InitialCash > 0.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialCash, gameState.InitialCash - 500.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_CASH, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_INITIAL_LOAN_INCREASE: + if (gameState.BankLoan < 5000000.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialLoan, gameState.BankLoan + 1000.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_INIT_LOAN, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_INITIAL_LOAN_DECREASE: + if (gameState.BankLoan > 0.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialLoan, gameState.BankLoan - 1000.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_INIT_LOAN, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_MAXIMUM_LOAN_INCREASE: + if (gameState.MaxBankLoan < 5000000.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::MaximumLoanSize, gameState.MaxBankLoan + 1000.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_MAX_LOAN, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_MAXIMUM_LOAN_DECREASE: + if (gameState.MaxBankLoan > 0.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::MaximumLoanSize, gameState.MaxBankLoan - 1000.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_MAX_LOAN, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_INTEREST_RATE_INCREASE: + if (gameState.BankLoanInterestRate < MaxBankLoanInterestRate) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::AnnualInterestRate, gameState.BankLoanInterestRate + 1); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_INTEREST_RATE, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_INTEREST_RATE_DECREASE: + if (gameState.BankLoanInterestRate > 0) + { + auto interest = std::min(MaxBankLoanInterestRate, gameState.BankLoanInterestRate - 1); + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::AnnualInterestRate, interest); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_INTEREST_RATE, STR_NONE, {}); + } + Invalidate(); + break; + } + + if (gScreenFlags == SCREEN_FLAGS_PLAYING) + { + WindowInvalidateByClass(WindowClass::Finances); + WindowInvalidateByClass(WindowClass::BottomToolbar); + } + } + + void FinancialUpdate() + { + frame_no++; + FinancialPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_1); + } + + void FinancialPrepareDraw() + { + Widget* newWidgets = window_editor_scenario_options_widgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + WindowInitScrollWidgets(*this); + } + + SetPressedTab(); + + auto& gameState = GetGameState(); + if (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) + { + SetWidgetPressed(WIDX_NO_MONEY, true); + for (int32_t i = WIDX_INITIAL_CASH; i <= WIDX_RCT1_INTEREST; i++) + widgets[i].type = WindowWidgetType::Empty; } else { - widgets[WIDX_INTEREST_RATE].type = WindowWidgetType::Spinner; - widgets[WIDX_INTEREST_RATE_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_INTEREST_RATE_DECREASE].type = WindowWidgetType::Button; - widgets[WIDX_RCT1_INTEREST].type = WindowWidgetType::Empty; + SetWidgetPressed(WIDX_NO_MONEY, false); + widgets[WIDX_INITIAL_CASH].type = WindowWidgetType::Spinner; + widgets[WIDX_INITIAL_CASH_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_INITIAL_CASH_DECREASE].type = WindowWidgetType::Button; + widgets[WIDX_INITIAL_LOAN].type = WindowWidgetType::Spinner; + widgets[WIDX_INITIAL_LOAN_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_INITIAL_LOAN_DECREASE].type = WindowWidgetType::Button; + widgets[WIDX_MAXIMUM_LOAN].type = WindowWidgetType::Spinner; + widgets[WIDX_MAXIMUM_LOAN_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_MAXIMUM_LOAN_DECREASE].type = WindowWidgetType::Button; + widgets[WIDX_FORBID_MARKETING].type = WindowWidgetType::Checkbox; + + if (gameState.ParkFlags & PARK_FLAGS_RCT1_INTEREST) + { + widgets[WIDX_INTEREST_RATE].type = WindowWidgetType::Empty; + widgets[WIDX_INTEREST_RATE_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_INTEREST_RATE_DECREASE].type = WindowWidgetType::Empty; + widgets[WIDX_RCT1_INTEREST].type = WindowWidgetType::Checkbox; + SetWidgetPressed(WIDX_RCT1_INTEREST, true); + } + else + { + widgets[WIDX_INTEREST_RATE].type = WindowWidgetType::Spinner; + widgets[WIDX_INTEREST_RATE_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_INTEREST_RATE_DECREASE].type = WindowWidgetType::Button; + widgets[WIDX_RCT1_INTEREST].type = WindowWidgetType::Empty; + } + } + + SetWidgetPressed(WIDX_FORBID_MARKETING, gameState.ParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN); + + widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowWidgetType::Empty + : WindowWidgetType::CloseBox; + + AnchorBorderWidgets(); + } + + void FinancialDraw(DrawPixelInfo& dpi) + { + ScreenCoordsXY screenCoords{}; + + WindowDrawWidgets(*this, dpi); + DrawTabImages(dpi); + + auto& gameState = GetGameState(); + + const auto& initialCashWidget = widgets[WIDX_INITIAL_CASH]; + if (initialCashWidget.type != WindowWidgetType::Empty) + { + screenCoords = windowPos + ScreenCoordsXY{ 8, initialCashWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_INIT_CASH_LABEL); + + screenCoords = windowPos + ScreenCoordsXY{ initialCashWidget.left + 1, initialCashWidget.top }; + auto ft = Formatter(); + ft.Add(GetGameState().InitialCash); + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); + } + + const auto& initialLoanWidget = widgets[WIDX_INITIAL_LOAN]; + if (initialLoanWidget.type != WindowWidgetType::Empty) + { + screenCoords = windowPos + ScreenCoordsXY{ 8, initialLoanWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_INIT_LOAN_LABEL); + + screenCoords = windowPos + ScreenCoordsXY{ initialLoanWidget.left + 1, initialLoanWidget.top }; + auto ft = Formatter(); + ft.Add(gameState.BankLoan); + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); + } + + const auto& maximumLoanWidget = widgets[WIDX_MAXIMUM_LOAN]; + if (maximumLoanWidget.type != WindowWidgetType::Empty) + { + screenCoords = windowPos + ScreenCoordsXY{ 8, maximumLoanWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_MAX_LOAN_LABEL); + + screenCoords = windowPos + ScreenCoordsXY{ maximumLoanWidget.left + 1, maximumLoanWidget.top }; + auto ft = Formatter(); + ft.Add(GetGameState().MaxBankLoan); + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); + } + + const auto& interestRateWidget = widgets[WIDX_INTEREST_RATE]; + if (interestRateWidget.type != WindowWidgetType::Empty) + { + screenCoords = windowPos + ScreenCoordsXY{ 8, interestRateWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_INTEREST_RATE_LABEL); + + screenCoords = windowPos + ScreenCoordsXY{ interestRateWidget.left + 1, interestRateWidget.top }; + + auto ft = Formatter(); + ft.Add( + std::clamp(static_cast(gameState.BankLoanInterestRate), INT16_MIN, INT16_MAX)); + DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); } } - SetWidgetPressed(WIDX_FORBID_MARKETING, gameState.ParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN); - - widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowWidgetType::Empty - : WindowWidgetType::CloseBox; - - AnchorBorderWidgets(); - } - - void FinancialDraw(DrawPixelInfo& dpi) - { - ScreenCoordsXY screenCoords{}; - - WindowDrawWidgets(*this, dpi); - DrawTabImages(dpi); - - auto& gameState = GetGameState(); - - const auto& initialCashWidget = widgets[WIDX_INITIAL_CASH]; - if (initialCashWidget.type != WindowWidgetType::Empty) - { - screenCoords = windowPos + ScreenCoordsXY{ 8, initialCashWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_INIT_CASH_LABEL); - - screenCoords = windowPos + ScreenCoordsXY{ initialCashWidget.left + 1, initialCashWidget.top }; - auto ft = Formatter(); - ft.Add(GetGameState().InitialCash); - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); - } - - const auto& initialLoanWidget = widgets[WIDX_INITIAL_LOAN]; - if (initialLoanWidget.type != WindowWidgetType::Empty) - { - screenCoords = windowPos + ScreenCoordsXY{ 8, initialLoanWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_INIT_LOAN_LABEL); - - screenCoords = windowPos + ScreenCoordsXY{ initialLoanWidget.left + 1, initialLoanWidget.top }; - auto ft = Formatter(); - ft.Add(gameState.BankLoan); - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); - } - - const auto& maximumLoanWidget = widgets[WIDX_MAXIMUM_LOAN]; - if (maximumLoanWidget.type != WindowWidgetType::Empty) - { - screenCoords = windowPos + ScreenCoordsXY{ 8, maximumLoanWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_MAX_LOAN_LABEL); - - screenCoords = windowPos + ScreenCoordsXY{ maximumLoanWidget.left + 1, maximumLoanWidget.top }; - auto ft = Formatter(); - ft.Add(GetGameState().MaxBankLoan); - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); - } - - const auto& interestRateWidget = widgets[WIDX_INTEREST_RATE]; - if (interestRateWidget.type != WindowWidgetType::Empty) - { - screenCoords = windowPos + ScreenCoordsXY{ 8, interestRateWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_INTEREST_RATE_LABEL); - - screenCoords = windowPos + ScreenCoordsXY{ interestRateWidget.left + 1, interestRateWidget.top }; - - auto ft = Formatter(); - ft.Add(std::clamp(static_cast(gameState.BankLoanInterestRate), INT16_MIN, INT16_MAX)); - DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); - } - } - #pragma endregion #pragma region Guests - void GuestsMouseUp(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) + void GuestsMouseUp(WidgetIndex widgetIndex) { - case WIDX_CLOSE: - WindowClose(*this); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - SetPage(widgetIndex - WIDX_TAB_1); - break; - case WIDX_GUEST_PREFER_LESS_INTENSE_RIDES: + auto& gameState = GetGameState(); + switch (widgetIndex) { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestsPreferLessIntenseRides, - gameState.ParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; - } - case WIDX_GUEST_PREFER_MORE_INTENSE_RIDES: - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestsPreferMoreIntenseRides, - gameState.ParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; + case WIDX_CLOSE: + WindowClose(*this); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + SetPage(widgetIndex - WIDX_TAB_1); + break; + case WIDX_GUEST_PREFER_LESS_INTENSE_RIDES: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestsPreferLessIntenseRides, + gameState.ParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_GUEST_PREFER_MORE_INTENSE_RIDES: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestsPreferMoreIntenseRides, + gameState.ParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } } } - } - void GuestsResize() - { - WindowSetResize(*this, 380, 149, 380, 149); - } - - void GuestsMouseDown(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - - switch (widgetIndex) + void GuestsResize() { - case WIDX_CASH_PER_GUEST_INCREASE: - if (gameState.GuestInitialCash < 1000.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::AverageCashPerGuest, gameState.GuestInitialCash + 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_CASH_PER_GUEST_DECREASE: - if (gameState.GuestInitialCash > 0.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::AverageCashPerGuest, gameState.GuestInitialCash - 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_GUEST_INITIAL_HAPPINESS_INCREASE: - if (gameState.GuestInitialHappiness < 250) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestInitialHappiness, gameState.GuestInitialHappiness + 4); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_GUEST_INITIAL_HAPPINESS_DECREASE: - if (gameState.GuestInitialHappiness > 40) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestInitialHappiness, gameState.GuestInitialHappiness - 4); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_GUEST_INITIAL_HUNGER_INCREASE: - if (gameState.GuestInitialHunger > 40) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestInitialHunger, gameState.GuestInitialHunger - 4); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_GUEST_INITIAL_HUNGER_DECREASE: - if (gameState.GuestInitialHunger < 250) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestInitialHunger, gameState.GuestInitialHunger + 4); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_GUEST_INITIAL_THIRST_INCREASE: - if (gameState.GuestInitialThirst > 40) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestInitialThirst, gameState.GuestInitialThirst - 4); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_GUEST_INITIAL_THIRST_DECREASE: - if (gameState.GuestInitialThirst < 250) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestInitialThirst, gameState.GuestInitialThirst + 4); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - } - } - - void GuestsUpdate() - { - frame_no++; - GuestsPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_2); - } - - void GuestsPrepareDraw() - { - Widget* newWidgets = window_editor_scenario_options_widgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - WindowInitScrollWidgets(*this); + WindowSetResize(*this, 380, 149, 380, 149); } - SetPressedTab(); + void GuestsMouseDown(WidgetIndex widgetIndex) + { + auto& gameState = GetGameState(); - auto& gameState = GetGameState(); - if (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) - { - widgets[WIDX_CASH_PER_GUEST].type = WindowWidgetType::Empty; - widgets[WIDX_CASH_PER_GUEST_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_CASH_PER_GUEST_DECREASE].type = WindowWidgetType::Empty; - } - else - { - widgets[WIDX_CASH_PER_GUEST].type = WindowWidgetType::Spinner; - widgets[WIDX_CASH_PER_GUEST_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_CASH_PER_GUEST_DECREASE].type = WindowWidgetType::Button; + switch (widgetIndex) + { + case WIDX_CASH_PER_GUEST_INCREASE: + if (gameState.GuestInitialCash < 1000.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::AverageCashPerGuest, gameState.GuestInitialCash + 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_CASH_PER_GUEST_DECREASE: + if (gameState.GuestInitialCash > 0.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::AverageCashPerGuest, gameState.GuestInitialCash - 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_GUEST_INITIAL_HAPPINESS_INCREASE: + if (gameState.GuestInitialHappiness < 250) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHappiness, gameState.GuestInitialHappiness + 4); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_GUEST_INITIAL_HAPPINESS_DECREASE: + if (gameState.GuestInitialHappiness > 40) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHappiness, gameState.GuestInitialHappiness - 4); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_GUEST_INITIAL_HUNGER_INCREASE: + if (gameState.GuestInitialHunger > 40) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHunger, gameState.GuestInitialHunger - 4); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_GUEST_INITIAL_HUNGER_DECREASE: + if (gameState.GuestInitialHunger < 250) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHunger, gameState.GuestInitialHunger + 4); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_GUEST_INITIAL_THIRST_INCREASE: + if (gameState.GuestInitialThirst > 40) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialThirst, gameState.GuestInitialThirst - 4); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_GUEST_INITIAL_THIRST_DECREASE: + if (gameState.GuestInitialThirst < 250) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialThirst, gameState.GuestInitialThirst + 4); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + } } - SetWidgetPressed(WIDX_GUEST_PREFER_LESS_INTENSE_RIDES, gameState.ParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES); - SetWidgetPressed(WIDX_GUEST_PREFER_MORE_INTENSE_RIDES, gameState.ParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES); - - widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowWidgetType::Empty - : WindowWidgetType::CloseBox; - - AnchorBorderWidgets(); - } - - void GuestsDraw(DrawPixelInfo& dpi) - { - ScreenCoordsXY screenCoords{}; - - WindowDrawWidgets(*this, dpi); - DrawTabImages(dpi); - auto& gameState = GetGameState(); - - const auto& cashPerGuestWidget = widgets[WIDX_CASH_PER_GUEST]; - if (cashPerGuestWidget.type != WindowWidgetType::Empty) + void GuestsUpdate() { - // Cash per guest label - screenCoords = windowPos + ScreenCoordsXY{ 8, cashPerGuestWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_CASH_PER_GUEST_LABEL); + frame_no++; + GuestsPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_2); + } - // Cash per guest value - screenCoords = windowPos + ScreenCoordsXY{ cashPerGuestWidget.left + 1, cashPerGuestWidget.top }; + void GuestsPrepareDraw() + { + Widget* newWidgets = window_editor_scenario_options_widgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + WindowInitScrollWidgets(*this); + } + + SetPressedTab(); + + auto& gameState = GetGameState(); + if (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) + { + widgets[WIDX_CASH_PER_GUEST].type = WindowWidgetType::Empty; + widgets[WIDX_CASH_PER_GUEST_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_CASH_PER_GUEST_DECREASE].type = WindowWidgetType::Empty; + } + else + { + widgets[WIDX_CASH_PER_GUEST].type = WindowWidgetType::Spinner; + widgets[WIDX_CASH_PER_GUEST_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_CASH_PER_GUEST_DECREASE].type = WindowWidgetType::Button; + } + + SetWidgetPressed(WIDX_GUEST_PREFER_LESS_INTENSE_RIDES, gameState.ParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES); + SetWidgetPressed(WIDX_GUEST_PREFER_MORE_INTENSE_RIDES, gameState.ParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES); + + widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowWidgetType::Empty + : WindowWidgetType::CloseBox; + + AnchorBorderWidgets(); + } + + void GuestsDraw(DrawPixelInfo& dpi) + { + ScreenCoordsXY screenCoords{}; + + WindowDrawWidgets(*this, dpi); + DrawTabImages(dpi); + auto& gameState = GetGameState(); + + const auto& cashPerGuestWidget = widgets[WIDX_CASH_PER_GUEST]; + if (cashPerGuestWidget.type != WindowWidgetType::Empty) + { + // Cash per guest label + screenCoords = windowPos + ScreenCoordsXY{ 8, cashPerGuestWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_CASH_PER_GUEST_LABEL); + + // Cash per guest value + screenCoords = windowPos + ScreenCoordsXY{ cashPerGuestWidget.left + 1, cashPerGuestWidget.top }; + auto ft = Formatter(); + ft.Add(gameState.GuestInitialCash); + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); + } + + // Guest initial happiness label + const auto& initialHappinessWidget = widgets[WIDX_GUEST_INITIAL_HAPPINESS]; + screenCoords = windowPos + ScreenCoordsXY{ 8, initialHappinessWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_GUEST_INIT_HAPPINESS); + + // Guest initial happiness value + screenCoords = windowPos + ScreenCoordsXY{ initialHappinessWidget.left + 1, initialHappinessWidget.top }; auto ft = Formatter(); - ft.Add(gameState.GuestInitialCash); - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); + ft.Add((gameState.GuestInitialHappiness * 100) / 255); + DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); + + // Guest initial hunger label + const auto& initialHungerWidget = widgets[WIDX_GUEST_INITIAL_HUNGER]; + screenCoords = windowPos + ScreenCoordsXY{ 8, initialHungerWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_GUEST_INIT_HUNGER); + + // Guest initial hunger value + screenCoords = windowPos + ScreenCoordsXY{ initialHungerWidget.left + 1, initialHungerWidget.top }; + ft = Formatter(); + ft.Add(((255 - gameState.GuestInitialHunger) * 100) / 255); + DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); + + // Guest initial thirst label + const auto& initialThirstWidget = widgets[WIDX_GUEST_INITIAL_THIRST]; + screenCoords = windowPos + ScreenCoordsXY{ 8, initialThirstWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_GUEST_INIT_THIRST); + + // Guest initial thirst value + screenCoords = windowPos + ScreenCoordsXY{ initialThirstWidget.left + 1, initialThirstWidget.top }; + ft = Formatter(); + ft.Add(((255 - gameState.GuestInitialThirst) * 100) / 255); + DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); } - // Guest initial happiness label - const auto& initialHappinessWidget = widgets[WIDX_GUEST_INITIAL_HAPPINESS]; - screenCoords = windowPos + ScreenCoordsXY{ 8, initialHappinessWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_GUEST_INIT_HAPPINESS); - - // Guest initial happiness value - screenCoords = windowPos + ScreenCoordsXY{ initialHappinessWidget.left + 1, initialHappinessWidget.top }; - auto ft = Formatter(); - ft.Add((gameState.GuestInitialHappiness * 100) / 255); - DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); - - // Guest initial hunger label - const auto& initialHungerWidget = widgets[WIDX_GUEST_INITIAL_HUNGER]; - screenCoords = windowPos + ScreenCoordsXY{ 8, initialHungerWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_GUEST_INIT_HUNGER); - - // Guest initial hunger value - screenCoords = windowPos + ScreenCoordsXY{ initialHungerWidget.left + 1, initialHungerWidget.top }; - ft = Formatter(); - ft.Add(((255 - gameState.GuestInitialHunger) * 100) / 255); - DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); - - // Guest initial thirst label - const auto& initialThirstWidget = widgets[WIDX_GUEST_INITIAL_THIRST]; - screenCoords = windowPos + ScreenCoordsXY{ 8, initialThirstWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_GUEST_INIT_THIRST); - - // Guest initial thirst value - screenCoords = windowPos + ScreenCoordsXY{ initialThirstWidget.left + 1, initialThirstWidget.top }; - ft = Formatter(); - ft.Add(((255 - gameState.GuestInitialThirst) * 100) / 255); - DrawTextBasic(dpi, screenCoords, STR_PERCENT_FORMAT_LABEL, ft); - } - #pragma endregion #pragma region Park - void ParkMouseUp(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) + void ParkMouseUp(WidgetIndex widgetIndex) { - case WIDX_CLOSE: - WindowClose(*this); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - SetPage(widgetIndex - WIDX_TAB_1); - break; - case WIDX_FORBID_TREE_REMOVAL: + auto& gameState = GetGameState(); + switch (widgetIndex) { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::ForbidTreeRemoval, gameState.ParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; - } - case WIDX_FORBID_LANDSCAPE_CHANGES: - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::ForbidLandscapeChanges, - gameState.ParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; - } - case WIDX_FORBID_HIGH_CONSTRUCTION: - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::ForbidHighConstruction, - gameState.ParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; - } - case WIDX_HARD_PARK_RATING: - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::ParkRatingHigherDifficultyLevel, - gameState.ParkFlags & PARK_FLAGS_DIFFICULT_PARK_RATING ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; - } - case WIDX_HARD_GUEST_GENERATION: - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::GuestGenerationHigherDifficultyLevel, - gameState.ParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION ? 0 : 1); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; + case WIDX_CLOSE: + WindowClose(*this); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + SetPage(widgetIndex - WIDX_TAB_1); + break; + case WIDX_FORBID_TREE_REMOVAL: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidTreeRemoval, gameState.ParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_FORBID_LANDSCAPE_CHANGES: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidLandscapeChanges, + gameState.ParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_FORBID_HIGH_CONSTRUCTION: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidHighConstruction, + gameState.ParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_HARD_PARK_RATING: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ParkRatingHigherDifficultyLevel, + gameState.ParkFlags & PARK_FLAGS_DIFFICULT_PARK_RATING ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_HARD_GUEST_GENERATION: + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestGenerationHigherDifficultyLevel, + gameState.ParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } } } - } - void ParkResize() - { - WindowSetResize(*this, 400, 200, 400, 200); - } - - void ParkMouseDown(WidgetIndex widgetIndex) - { - Widget* dropdownWidget; - Widget* widget = &widgets[widgetIndex]; - - auto& gameState = GetGameState(); - switch (widgetIndex) + void ParkResize() { - case WIDX_LAND_COST_INCREASE: - if (gameState.LandPrice < 200.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::CostToBuyLand, gameState.LandPrice + 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_LAND_COST_DECREASE: - if (gameState.LandPrice > 5.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::CostToBuyLand, gameState.LandPrice - 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_CONSTRUCTION_RIGHTS_COST_INCREASE: - if (gameState.ConstructionRightsPrice < 200.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::CostToBuyConstructionRights, gameState.ConstructionRightsPrice + 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_CONSTRUCTION_RIGHTS_COST_DECREASE: - if (gameState.ConstructionRightsPrice > 5.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::CostToBuyConstructionRights, gameState.ConstructionRightsPrice - 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_ENTRY_PRICE_INCREASE: - if (gameState.ParkEntranceFee < MAX_ENTRANCE_FEE) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::ParkChargeEntryFee, gameState.ParkEntranceFee + 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_ENTRY_PRICE_DECREASE: - if (gameState.ParkEntranceFee > 0.00_GBP) - { - auto scenarioSetSetting = ScenarioSetSettingAction( - ScenarioSetSetting::ParkChargeEntryFee, gameState.ParkEntranceFee - 1.00_GBP); - GameActions::Execute(&scenarioSetSetting); - } - else - { - ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); - } - Invalidate(); - break; - case WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN: - dropdownWidget = widget - 1; + WindowSetResize(*this, 400, 200, 400, 200); + } - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_FREE_PARK_ENTER; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Args = STR_PAY_PARK_ENTER; - gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[2].Args = STR_PAID_ENTRY_PAID_RIDES; + void ParkMouseDown(WidgetIndex widgetIndex) + { + Widget* dropdownWidget; + Widget* widget = &widgets[widgetIndex]; - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() - 1, - colours[1], 0, Dropdown::Flag::StayOpen, 3, dropdownWidget->width() - 3); + auto& gameState = GetGameState(); + switch (widgetIndex) + { + case WIDX_LAND_COST_INCREASE: + if (gameState.LandPrice < 200.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyLand, gameState.LandPrice + 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_LAND_COST_DECREASE: + if (gameState.LandPrice > 5.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyLand, gameState.LandPrice - 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_CONSTRUCTION_RIGHTS_COST_INCREASE: + if (gameState.ConstructionRightsPrice < 200.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyConstructionRights, gameState.ConstructionRightsPrice + 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_CONSTRUCTION_RIGHTS_COST_DECREASE: + if (gameState.ConstructionRightsPrice > 5.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyConstructionRights, gameState.ConstructionRightsPrice - 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_ENTRY_PRICE_INCREASE: + if (gameState.ParkEntranceFee < MAX_ENTRANCE_FEE) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ParkChargeEntryFee, gameState.ParkEntranceFee + 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_INCREASE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_ENTRY_PRICE_DECREASE: + if (gameState.ParkEntranceFee > 0.00_GBP) + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ParkChargeEntryFee, gameState.ParkEntranceFee - 1.00_GBP); + GameActions::Execute(&scenarioSetSetting); + } + else + { + ContextShowError(STR_CANT_REDUCE_FURTHER, STR_NONE, {}); + } + Invalidate(); + break; + case WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN: + dropdownWidget = widget - 1; + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_FREE_PARK_ENTER; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Args = STR_PAY_PARK_ENTER; + gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[2].Args = STR_PAID_ENTRY_PAID_RIDES; + + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() - 1, + colours[1], 0, Dropdown::Flag::StayOpen, 3, dropdownWidget->width() - 3); + + if (gameState.ParkFlags & PARK_FLAGS_UNLOCK_ALL_PRICES) + Dropdown::SetChecked(2, true); + else if (gameState.ParkFlags & PARK_FLAGS_PARK_FREE_ENTRY) + Dropdown::SetChecked(0, true); + else + Dropdown::SetChecked(1, true); + + break; + case WIDX_CLIMATE_DROPDOWN: + ShowClimateDropdown(); + break; + } + } + + void ParkDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + { + return; + } + + switch (widgetIndex) + { + case WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN: + { + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ParkChargeMethod, dropdownIndex); + GameActions::Execute(&scenarioSetSetting); + Invalidate(); + break; + } + case WIDX_CLIMATE_DROPDOWN: + if (static_cast(GetGameState().Climate) != static_cast(dropdownIndex)) + { + auto gameAction = ClimateSetAction(ClimateType{ static_cast(dropdownIndex) }); + GameActions::Execute(&gameAction); + } + break; + } + } + + void ParkUpdate() + { + frame_no++; + ParkPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_3); + } + + void ParkPrepareDraw() + { + Widget* newWidgets = window_editor_scenario_options_widgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + WindowInitScrollWidgets(*this); + } + + SetPressedTab(); + + auto& gameState = GetGameState(); + if (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) + { + for (int32_t i = WIDX_LAND_COST; i <= WIDX_ENTRY_PRICE_DECREASE; i++) + widgets[i].type = WindowWidgetType::Empty; + } + else + { + widgets[WIDX_LAND_COST].type = WindowWidgetType::Spinner; + widgets[WIDX_LAND_COST_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_LAND_COST_DECREASE].type = WindowWidgetType::Button; + widgets[WIDX_CONSTRUCTION_RIGHTS_COST].type = WindowWidgetType::Spinner; + widgets[WIDX_CONSTRUCTION_RIGHTS_COST_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_CONSTRUCTION_RIGHTS_COST_DECREASE].type = WindowWidgetType::Button; + widgets[WIDX_PAY_FOR_PARK_OR_RIDES].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN].type = WindowWidgetType::Button; + + if (!ParkEntranceFeeUnlocked()) + { + widgets[WIDX_ENTRY_PRICE].type = WindowWidgetType::Empty; + widgets[WIDX_ENTRY_PRICE_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_ENTRY_PRICE_DECREASE].type = WindowWidgetType::Empty; + } + else + { + widgets[WIDX_ENTRY_PRICE].type = WindowWidgetType::Spinner; + widgets[WIDX_ENTRY_PRICE_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_ENTRY_PRICE_DECREASE].type = WindowWidgetType::Button; + } + } + + SetWidgetPressed(WIDX_FORBID_TREE_REMOVAL, gameState.ParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL); + SetWidgetPressed(WIDX_FORBID_LANDSCAPE_CHANGES, gameState.ParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES); + SetWidgetPressed(WIDX_FORBID_HIGH_CONSTRUCTION, gameState.ParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION); + SetWidgetPressed(WIDX_HARD_PARK_RATING, gameState.ParkFlags & PARK_FLAGS_DIFFICULT_PARK_RATING); + SetWidgetPressed(WIDX_HARD_GUEST_GENERATION, gameState.ParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION); + + widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowWidgetType::Empty + : WindowWidgetType::CloseBox; + + AnchorBorderWidgets(); + } + + void ParkDraw(DrawPixelInfo& dpi) + { + ScreenCoordsXY screenCoords{}; + + WindowDrawWidgets(*this, dpi); + DrawTabImages(dpi); + + const auto& gameState = GetGameState(); + const auto& landCostWidget = widgets[WIDX_LAND_COST]; + if (landCostWidget.type != WindowWidgetType::Empty) + { + // Cost to buy land label + screenCoords = windowPos + ScreenCoordsXY{ 8, landCostWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_LAND_COST_LABEL); + + // Cost to buy land value + screenCoords = windowPos + ScreenCoordsXY{ landCostWidget.left + 1, landCostWidget.top }; + auto ft = Formatter(); + ft.Add(gameState.LandPrice); + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); + } + + const auto& constructionRightsCostWidget = widgets[WIDX_CONSTRUCTION_RIGHTS_COST]; + if (constructionRightsCostWidget.type != WindowWidgetType::Empty) + { + // Cost to buy construction rights label + screenCoords = windowPos + ScreenCoordsXY{ 8, constructionRightsCostWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_RIGHTS_COST_LABEL); + + // Cost to buy construction rights value + screenCoords = windowPos + + ScreenCoordsXY{ constructionRightsCostWidget.left + 1, constructionRightsCostWidget.top }; + auto ft = Formatter(); + ft.Add(gameState.ConstructionRightsPrice); + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); + } + + const auto& payForParkOrRidesWidget = widgets[WIDX_PAY_FOR_PARK_OR_RIDES]; + if (payForParkOrRidesWidget.type != WindowWidgetType::Empty) + { + // Pay for park or rides label + screenCoords = windowPos + ScreenCoordsXY{ payForParkOrRidesWidget.left + 1, payForParkOrRidesWidget.top }; + + auto ft = Formatter(); + // Pay for park and/or rides value if (gameState.ParkFlags & PARK_FLAGS_UNLOCK_ALL_PRICES) - Dropdown::SetChecked(2, true); + ft.Add(STR_PAID_ENTRY_PAID_RIDES); else if (gameState.ParkFlags & PARK_FLAGS_PARK_FREE_ENTRY) - Dropdown::SetChecked(0, true); + ft.Add(STR_FREE_PARK_ENTER); else - Dropdown::SetChecked(1, true); + ft.Add(STR_PAY_PARK_ENTER); - break; - case WIDX_CLIMATE_DROPDOWN: - ShowClimateDropdown(); - break; - } - } - - void ParkDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (dropdownIndex == -1) - { - return; - } - - switch (widgetIndex) - { - case WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN: - { - auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ParkChargeMethod, dropdownIndex); - GameActions::Execute(&scenarioSetSetting); - Invalidate(); - break; + DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); } - case WIDX_CLIMATE_DROPDOWN: - if (static_cast(GetGameState().Climate) != static_cast(dropdownIndex)) - { - auto gameAction = ClimateSetAction(ClimateType{ static_cast(dropdownIndex) }); - GameActions::Execute(&gameAction); - } - break; - } - } - void ParkUpdate() - { - frame_no++; - ParkPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_3); - } - - void ParkPrepareDraw() - { - Widget* newWidgets = window_editor_scenario_options_widgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - WindowInitScrollWidgets(*this); - } - - SetPressedTab(); - - auto& gameState = GetGameState(); - if (gameState.ParkFlags & PARK_FLAGS_NO_MONEY) - { - for (int32_t i = WIDX_LAND_COST; i <= WIDX_ENTRY_PRICE_DECREASE; i++) - widgets[i].type = WindowWidgetType::Empty; - } - else - { - widgets[WIDX_LAND_COST].type = WindowWidgetType::Spinner; - widgets[WIDX_LAND_COST_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_LAND_COST_DECREASE].type = WindowWidgetType::Button; - widgets[WIDX_CONSTRUCTION_RIGHTS_COST].type = WindowWidgetType::Spinner; - widgets[WIDX_CONSTRUCTION_RIGHTS_COST_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_CONSTRUCTION_RIGHTS_COST_DECREASE].type = WindowWidgetType::Button; - widgets[WIDX_PAY_FOR_PARK_OR_RIDES].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN].type = WindowWidgetType::Button; - - if (!ParkEntranceFeeUnlocked()) + const auto& entryPriceWidget = widgets[WIDX_ENTRY_PRICE]; + if (entryPriceWidget.type != WindowWidgetType::Empty) { - widgets[WIDX_ENTRY_PRICE].type = WindowWidgetType::Empty; - widgets[WIDX_ENTRY_PRICE_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_ENTRY_PRICE_DECREASE].type = WindowWidgetType::Empty; + // Entry price label + screenCoords = windowPos + ScreenCoordsXY{ payForParkOrRidesWidget.right + 8, entryPriceWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_ENTRY_PRICE_LABEL); + + // Entry price value + screenCoords = windowPos + ScreenCoordsXY{ entryPriceWidget.left + 1, entryPriceWidget.top }; + auto ft = Formatter(); + ft.Add(gameState.ParkEntranceFee); + DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); } - else - { - widgets[WIDX_ENTRY_PRICE].type = WindowWidgetType::Spinner; - widgets[WIDX_ENTRY_PRICE_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_ENTRY_PRICE_DECREASE].type = WindowWidgetType::Button; - } - } - SetWidgetPressed(WIDX_FORBID_TREE_REMOVAL, gameState.ParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL); - SetWidgetPressed(WIDX_FORBID_LANDSCAPE_CHANGES, gameState.ParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES); - SetWidgetPressed(WIDX_FORBID_HIGH_CONSTRUCTION, gameState.ParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION); - SetWidgetPressed(WIDX_HARD_PARK_RATING, gameState.ParkFlags & PARK_FLAGS_DIFFICULT_PARK_RATING); - SetWidgetPressed(WIDX_HARD_GUEST_GENERATION, gameState.ParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION); + // Climate label + const auto& climateWidget = widgets[WIDX_CLIMATE]; + screenCoords = windowPos + ScreenCoordsXY{ 8, climateWidget.top }; + DrawTextBasic(dpi, screenCoords, STR_CLIMATE_LABEL); - widgets[WIDX_CLOSE].type = (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) ? WindowWidgetType::Empty - : WindowWidgetType::CloseBox; - - AnchorBorderWidgets(); - } - - void ParkDraw(DrawPixelInfo& dpi) - { - ScreenCoordsXY screenCoords{}; - - WindowDrawWidgets(*this, dpi); - DrawTabImages(dpi); - - const auto& gameState = GetGameState(); - const auto& landCostWidget = widgets[WIDX_LAND_COST]; - if (landCostWidget.type != WindowWidgetType::Empty) - { - // Cost to buy land label - screenCoords = windowPos + ScreenCoordsXY{ 8, landCostWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_LAND_COST_LABEL); - - // Cost to buy land value - screenCoords = windowPos + ScreenCoordsXY{ landCostWidget.left + 1, landCostWidget.top }; + // Climate value + screenCoords = windowPos + ScreenCoordsXY{ climateWidget.left + 1, climateWidget.top }; auto ft = Formatter(); - ft.Add(gameState.LandPrice); - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); - } - - const auto& constructionRightsCostWidget = widgets[WIDX_CONSTRUCTION_RIGHTS_COST]; - if (constructionRightsCostWidget.type != WindowWidgetType::Empty) - { - // Cost to buy construction rights label - screenCoords = windowPos + ScreenCoordsXY{ 8, constructionRightsCostWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_RIGHTS_COST_LABEL); - - // Cost to buy construction rights value - screenCoords = windowPos - + ScreenCoordsXY{ constructionRightsCostWidget.left + 1, constructionRightsCostWidget.top }; - auto ft = Formatter(); - ft.Add(gameState.ConstructionRightsPrice); - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); - } - - const auto& payForParkOrRidesWidget = widgets[WIDX_PAY_FOR_PARK_OR_RIDES]; - if (payForParkOrRidesWidget.type != WindowWidgetType::Empty) - { - // Pay for park or rides label - screenCoords = windowPos + ScreenCoordsXY{ payForParkOrRidesWidget.left + 1, payForParkOrRidesWidget.top }; - - auto ft = Formatter(); - // Pay for park and/or rides value - if (gameState.ParkFlags & PARK_FLAGS_UNLOCK_ALL_PRICES) - ft.Add(STR_PAID_ENTRY_PAID_RIDES); - else if (gameState.ParkFlags & PARK_FLAGS_PARK_FREE_ENTRY) - ft.Add(STR_FREE_PARK_ENTER); - else - ft.Add(STR_PAY_PARK_ENTER); - + ft.Add(ClimateNames[EnumValue(gameState.Climate)]); DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); } - const auto& entryPriceWidget = widgets[WIDX_ENTRY_PRICE]; - if (entryPriceWidget.type != WindowWidgetType::Empty) - { - // Entry price label - screenCoords = windowPos + ScreenCoordsXY{ payForParkOrRidesWidget.right + 8, entryPriceWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_ENTRY_PRICE_LABEL); - - // Entry price value - screenCoords = windowPos + ScreenCoordsXY{ entryPriceWidget.left + 1, entryPriceWidget.top }; - auto ft = Formatter(); - ft.Add(gameState.ParkEntranceFee); - DrawTextBasic(dpi, screenCoords, STR_CURRENCY_FORMAT_LABEL, ft); - } - - // Climate label - const auto& climateWidget = widgets[WIDX_CLIMATE]; - screenCoords = windowPos + ScreenCoordsXY{ 8, climateWidget.top }; - DrawTextBasic(dpi, screenCoords, STR_CLIMATE_LABEL); - - // Climate value - screenCoords = windowPos + ScreenCoordsXY{ climateWidget.left + 1, climateWidget.top }; - auto ft = Formatter(); - ft.Add(ClimateNames[EnumValue(gameState.Climate)]); - DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); - } - #pragma endregion -}; + }; -WindowBase* WindowEditorScenarioOptionsOpen() -{ - return WindowFocusOrCreate(WindowClass::EditorScenarioOptions, 280, 148, WF_NO_SCROLLING); -} + WindowBase* WindowEditorScenarioOptionsOpen() + { + return WindowFocusOrCreate(WindowClass::EditorScenarioOptions, 280, 148, WF_NO_SCROLLING); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Error.cpp b/src/openrct2-ui/windows/Error.cpp index 1dffdd6a9b..0ad3005c4a 100644 --- a/src/openrct2-ui/windows/Error.cpp +++ b/src/openrct2-ui/windows/Error.cpp @@ -18,7 +18,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WIDX_BACKGROUND }; @@ -27,146 +29,147 @@ static Widget window_error_widgets[] = { MakeWidget({0, 0}, {200, 42}, WindowWidgetType::ImgBtn, WindowColour::Primary), kWidgetsEnd, }; -// clang-format on + // clang-format on -class ErrorWindow final : public Window -{ -private: - std::string _text; - uint16_t _numLines; - uint8_t _staleCount; - -public: - ErrorWindow(std::string text, uint16_t numLines) - : _text(std::move(text)) - , _numLines(numLines) + class ErrorWindow final : public Window { - } + private: + std::string _text; + uint16_t _numLines; + uint8_t _staleCount; - void OnOpen() override - { - window_error_widgets[WIDX_BACKGROUND].right = width; - window_error_widgets[WIDX_BACKGROUND].bottom = height; - - widgets = window_error_widgets; - _staleCount = 0; - if (!gDisableErrorWindowSound) + public: + ErrorWindow(std::string text, uint16_t numLines) + : _text(std::move(text)) + , _numLines(numLines) { - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Error, 0, windowPos.x + (width / 2)); } - } - void OnDraw(DrawPixelInfo& dpi) override - { - ScreenCoordsXY leftTop{ windowPos }; - ScreenCoordsXY rightBottom{ windowPos + ScreenCoordsXY{ width - 1, height - 1 } }; - ScreenCoordsXY leftBottom{ leftTop.x, rightBottom.y }; - ScreenCoordsXY rightTop{ rightBottom.x, leftTop.y }; - - GfxFilterRect( - dpi, ScreenRect{ leftTop + ScreenCoordsXY{ 1, 1 }, rightBottom - ScreenCoordsXY{ 1, 1 } }, - FilterPaletteID::Palette45); - GfxFilterRect(dpi, ScreenRect{ leftTop, rightBottom }, FilterPaletteID::PaletteGlassSaturatedRed); - - GfxFilterRect( - dpi, ScreenRect{ leftTop + ScreenCoordsXY{ 0, 2 }, leftBottom - ScreenCoordsXY{ 0, 2 } }, - FilterPaletteID::PaletteDarken3); - GfxFilterRect( - dpi, ScreenRect{ rightTop + ScreenCoordsXY{ 0, 2 }, rightBottom - ScreenCoordsXY{ 0, 2 } }, - FilterPaletteID::PaletteDarken3); - GfxFilterRect( - dpi, ScreenRect{ leftBottom + ScreenCoordsXY{ 2, 0 }, rightBottom - ScreenCoordsXY{ 2, 0 } }, - FilterPaletteID::PaletteDarken3); - GfxFilterRect( - dpi, ScreenRect{ leftTop + ScreenCoordsXY{ 2, 0 }, rightTop - ScreenCoordsXY{ 2, 0 } }, - FilterPaletteID::PaletteDarken3); - - GfxFilterRect( - dpi, ScreenRect{ rightTop + ScreenCoordsXY{ 1, 1 }, rightTop + ScreenCoordsXY{ 1, 1 } }, - FilterPaletteID::PaletteDarken3); - GfxFilterRect( - dpi, ScreenRect{ rightTop + ScreenCoordsXY{ -1, 1 }, rightTop + ScreenCoordsXY{ -1, 1 } }, - FilterPaletteID::PaletteDarken3); - GfxFilterRect( - dpi, ScreenRect{ leftBottom + ScreenCoordsXY{ 1, -1 }, leftBottom + ScreenCoordsXY{ 1, -1 } }, - FilterPaletteID::PaletteDarken3); - GfxFilterRect( - dpi, ScreenRect{ rightBottom - ScreenCoordsXY{ 1, 1 }, rightBottom - ScreenCoordsXY{ 1, 1 } }, - FilterPaletteID::PaletteDarken3); - - DrawStringCentredRaw( - dpi, { leftTop + ScreenCoordsXY{ (width + 1) / 2 - 1, 1 } }, _numLines, _text.data(), FontStyle::Medium); - } - - void OnPeriodicUpdate() override - { - // Close the window after 8 seconds of showing - _staleCount++; - if (_staleCount >= 8) + void OnOpen() override { - Close(); + window_error_widgets[WIDX_BACKGROUND].right = width; + window_error_widgets[WIDX_BACKGROUND].bottom = height; + + widgets = window_error_widgets; + _staleCount = 0; + if (!gDisableErrorWindowSound) + { + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Error, 0, windowPos.x + (width / 2)); + } } - } -}; -WindowBase* WindowErrorOpen(std::string_view title, std::string_view message) -{ - std::string buffer = "{BLACK}"; - buffer.append(title); + void OnDraw(DrawPixelInfo& dpi) override + { + ScreenCoordsXY leftTop{ windowPos }; + ScreenCoordsXY rightBottom{ windowPos + ScreenCoordsXY{ width - 1, height - 1 } }; + ScreenCoordsXY leftBottom{ leftTop.x, rightBottom.y }; + ScreenCoordsXY rightTop{ rightBottom.x, leftTop.y }; - // Format the message - if (!message.empty()) + GfxFilterRect( + dpi, ScreenRect{ leftTop + ScreenCoordsXY{ 1, 1 }, rightBottom - ScreenCoordsXY{ 1, 1 } }, + FilterPaletteID::Palette45); + GfxFilterRect(dpi, ScreenRect{ leftTop, rightBottom }, FilterPaletteID::PaletteGlassSaturatedRed); + + GfxFilterRect( + dpi, ScreenRect{ leftTop + ScreenCoordsXY{ 0, 2 }, leftBottom - ScreenCoordsXY{ 0, 2 } }, + FilterPaletteID::PaletteDarken3); + GfxFilterRect( + dpi, ScreenRect{ rightTop + ScreenCoordsXY{ 0, 2 }, rightBottom - ScreenCoordsXY{ 0, 2 } }, + FilterPaletteID::PaletteDarken3); + GfxFilterRect( + dpi, ScreenRect{ leftBottom + ScreenCoordsXY{ 2, 0 }, rightBottom - ScreenCoordsXY{ 2, 0 } }, + FilterPaletteID::PaletteDarken3); + GfxFilterRect( + dpi, ScreenRect{ leftTop + ScreenCoordsXY{ 2, 0 }, rightTop - ScreenCoordsXY{ 2, 0 } }, + FilterPaletteID::PaletteDarken3); + + GfxFilterRect( + dpi, ScreenRect{ rightTop + ScreenCoordsXY{ 1, 1 }, rightTop + ScreenCoordsXY{ 1, 1 } }, + FilterPaletteID::PaletteDarken3); + GfxFilterRect( + dpi, ScreenRect{ rightTop + ScreenCoordsXY{ -1, 1 }, rightTop + ScreenCoordsXY{ -1, 1 } }, + FilterPaletteID::PaletteDarken3); + GfxFilterRect( + dpi, ScreenRect{ leftBottom + ScreenCoordsXY{ 1, -1 }, leftBottom + ScreenCoordsXY{ 1, -1 } }, + FilterPaletteID::PaletteDarken3); + GfxFilterRect( + dpi, ScreenRect{ rightBottom - ScreenCoordsXY{ 1, 1 }, rightBottom - ScreenCoordsXY{ 1, 1 } }, + FilterPaletteID::PaletteDarken3); + + DrawStringCentredRaw( + dpi, { leftTop + ScreenCoordsXY{ (width + 1) / 2 - 1, 1 } }, _numLines, _text.data(), FontStyle::Medium); + } + + void OnPeriodicUpdate() override + { + // Close the window after 8 seconds of showing + _staleCount++; + if (_staleCount >= 8) + { + Close(); + } + } + }; + + WindowBase* WindowErrorOpen(std::string_view title, std::string_view message) { - buffer.push_back('\n'); - buffer.append(message); + std::string buffer = "{BLACK}"; + buffer.append(title); + + // Format the message + if (!message.empty()) + { + buffer.push_back('\n'); + buffer.append(message); + } + + LOG_VERBOSE("show error, %s", buffer.c_str() + 1); + + // Don't do unnecessary work in headless. Also saves checking if cursor state is null. + if (gOpenRCT2Headless) + { + return nullptr; + } + + // Check if there is any text to display + if (buffer.size() <= 1) + { + return nullptr; + } + + // Close any existing error windows if they exist. + WindowCloseByClass(WindowClass::Error); + + int32_t width = GfxGetStringWidthNewLined(buffer.data(), FontStyle::Medium); + width = std::clamp(width, 64, 196); + + int32_t numLines{}; + GfxWrapString(buffer, width + 1, FontStyle::Medium, &buffer, &numLines); + + width = width + 3; + int32_t height = (numLines + 1) * FontGetLineHeight(FontStyle::Medium) + 4; + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + const CursorState* state = ContextGetCursorState(); + ScreenCoordsXY windowPosition = state->position - ScreenCoordsXY(width / 2, -26); + windowPosition.x = std::clamp(windowPosition.x, 0, screenWidth); + windowPosition.y = std::max(22, windowPosition.y); + int32_t maxY = screenHeight - height; + if (windowPosition.y > maxY) + { + windowPosition.y = std::min(windowPosition.y - height - 40, maxY); + } + + auto errorWindow = std::make_unique(std::move(buffer), numLines); + return WindowCreate( + std::move(errorWindow), WindowClass::Error, windowPosition, width, height, + WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_RESIZABLE); } - LOG_VERBOSE("show error, %s", buffer.c_str() + 1); - - // Don't do unnecessary work in headless. Also saves checking if cursor state is null. - if (gOpenRCT2Headless) + WindowBase* WindowErrorOpen(StringId title, StringId message, const Formatter& args) { - return nullptr; + auto titlez = FormatStringID(title, args.Data()); + auto messagez = FormatStringID(message, args.Data()); + return WindowErrorOpen(titlez, messagez); } - - // Check if there is any text to display - if (buffer.size() <= 1) - { - return nullptr; - } - - // Close any existing error windows if they exist. - WindowCloseByClass(WindowClass::Error); - - int32_t width = GfxGetStringWidthNewLined(buffer.data(), FontStyle::Medium); - width = std::clamp(width, 64, 196); - - int32_t numLines{}; - GfxWrapString(buffer, width + 1, FontStyle::Medium, &buffer, &numLines); - - width = width + 3; - int32_t height = (numLines + 1) * FontGetLineHeight(FontStyle::Medium) + 4; - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - const CursorState* state = ContextGetCursorState(); - ScreenCoordsXY windowPosition = state->position - ScreenCoordsXY(width / 2, -26); - windowPosition.x = std::clamp(windowPosition.x, 0, screenWidth); - windowPosition.y = std::max(22, windowPosition.y); - int32_t maxY = screenHeight - height; - if (windowPosition.y > maxY) - { - windowPosition.y = std::min(windowPosition.y - height - 40, maxY); - } - - auto errorWindow = std::make_unique(std::move(buffer), numLines); - return WindowCreate( - std::move(errorWindow), WindowClass::Error, windowPosition, width, height, - WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_RESIZABLE); -} - -WindowBase* WindowErrorOpen(StringId title, StringId message, const Formatter& args) -{ - auto titlez = FormatStringID(title, args.Data()); - auto messagez = FormatStringID(message, args.Data()); - return WindowErrorOpen(titlez, messagez); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Finances.cpp b/src/openrct2-ui/windows/Finances.cpp index d8a93935dc..5a192397cb 100644 --- a/src/openrct2-ui/windows/Finances.cpp +++ b/src/openrct2-ui/windows/Finances.cpp @@ -28,79 +28,79 @@ #include #include -using namespace OpenRCT2; - -enum +namespace OpenRCT2::Ui::Windows { - WINDOW_FINANCES_PAGE_SUMMARY, - WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH, - WINDOW_FINANCES_PAGE_VALUE_GRAPH, - WINDOW_FINANCES_PAGE_PROFIT_GRAPH, - WINDOW_FINANCES_PAGE_MARKETING, - WINDOW_FINANCES_PAGE_RESEARCH, - WINDOW_FINANCES_PAGE_COUNT -}; + enum + { + WINDOW_FINANCES_PAGE_SUMMARY, + WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH, + WINDOW_FINANCES_PAGE_VALUE_GRAPH, + WINDOW_FINANCES_PAGE_PROFIT_GRAPH, + WINDOW_FINANCES_PAGE_MARKETING, + WINDOW_FINANCES_PAGE_RESEARCH, + WINDOW_FINANCES_PAGE_COUNT + }; -enum -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PAGE_BACKGROUND, - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_TAB_3, - WIDX_TAB_4, - WIDX_TAB_5, - WIDX_TAB_6, + enum + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PAGE_BACKGROUND, + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_TAB_3, + WIDX_TAB_4, + WIDX_TAB_5, + WIDX_TAB_6, - WIDX_TAB_CONTENT, + WIDX_TAB_CONTENT, - WIDX_SUMMARY_SCROLL = WIDX_TAB_CONTENT, - WIDX_LOAN, - WIDX_LOAN_INCREASE, - WIDX_LOAN_DECREASE, + WIDX_SUMMARY_SCROLL = WIDX_TAB_CONTENT, + WIDX_LOAN, + WIDX_LOAN_INCREASE, + WIDX_LOAN_DECREASE, - WIDX_ACTIVE_CAMPAIGNS_GROUP = WIDX_TAB_CONTENT, - WIDX_CAMPAIGNS_AVAILABLE_GROUP, - WIDX_CAMPAIGN_1, - WIDX_CAMPAIGN_2, - WIDX_CAMPAIGN_3, - WIDX_CAMPAIGN_4, - WIDX_CAMPAIGN_5, - WIDX_CAMPAIGN_6, + WIDX_ACTIVE_CAMPAIGNS_GROUP = WIDX_TAB_CONTENT, + WIDX_CAMPAIGNS_AVAILABLE_GROUP, + WIDX_CAMPAIGN_1, + WIDX_CAMPAIGN_2, + WIDX_CAMPAIGN_3, + WIDX_CAMPAIGN_4, + WIDX_CAMPAIGN_5, + WIDX_CAMPAIGN_6, - WIDX_RESEARCH_FUNDING_GROUP = WIDX_TAB_CONTENT, - WIDX_RESEARCH_FUNDING, - WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON, - WIDX_RESEARCH_PRIORITIES_GROUP, - WIDX_TRANSPORT_RIDES, - WIDX_GENTLE_RIDES, - WIDX_ROLLER_COASTERS, - WIDX_THRILL_RIDES, - WIDX_WATER_RIDES, - WIDX_SHOPS_AND_STALLS, - WIDX_SCENERY_AND_THEMING, -}; + WIDX_RESEARCH_FUNDING_GROUP = WIDX_TAB_CONTENT, + WIDX_RESEARCH_FUNDING, + WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON, + WIDX_RESEARCH_PRIORITIES_GROUP, + WIDX_TRANSPORT_RIDES, + WIDX_GENTLE_RIDES, + WIDX_ROLLER_COASTERS, + WIDX_THRILL_RIDES, + WIDX_WATER_RIDES, + WIDX_SHOPS_AND_STALLS, + WIDX_SCENERY_AND_THEMING, + }; #pragma region Measurements -static constexpr int32_t WH_SUMMARY = 309; -static constexpr int32_t WH_RESEARCH = 207; -static constexpr int32_t WH_OTHER_TABS = 257; -static constexpr int32_t WW_RESEARCH = 320; -static constexpr int32_t WW_OTHER_TABS = 530; -static constexpr int32_t RSH_SUMMARY = 266; -static constexpr int32_t RSH_RESEARCH = 164; -static constexpr int32_t RSH_OTHER_TABS = 214; -static constexpr int32_t RSW_RESEARCH = WW_RESEARCH; -static constexpr int32_t RSW_OTHER_TABS = WW_OTHER_TABS; + static constexpr int32_t WH_SUMMARY = 309; + static constexpr int32_t WH_RESEARCH = 207; + static constexpr int32_t WH_OTHER_TABS = 257; + static constexpr int32_t WW_RESEARCH = 320; + static constexpr int32_t WW_OTHER_TABS = 530; + static constexpr int32_t RSH_SUMMARY = 266; + static constexpr int32_t RSH_RESEARCH = 164; + static constexpr int32_t RSH_OTHER_TABS = 214; + static constexpr int32_t RSW_RESEARCH = WW_RESEARCH; + static constexpr int32_t RSW_OTHER_TABS = WW_OTHER_TABS; #pragma endregion #pragma region Widgets -// clang-format off + // clang-format off #define MAIN_FINANCES_WIDGETS(TITLE, RSW, RSH, WW, WH) \ WINDOW_SHIM(TITLE, WW, WH), \ MakeWidget({0, 43}, {RSW, RSH}, WindowWidgetType::Resize, WindowColour::Secondary), \ @@ -167,814 +167,819 @@ static Widget _windowFinancesResearchWidgets[] = MakeWidget({ 8, 186}, {WW_RESEARCH - 14, 12}, WindowWidgetType::Checkbox, WindowColour::Tertiary, STR_RESEARCH_NEW_SCENERY_AND_THEMING, STR_RESEARCH_NEW_SCENERY_AND_THEMING_TIP ), kWidgetsEnd, }; -// clang-format on + // clang-format on -static Widget* _windowFinancesPageWidgets[] = { - _windowFinancesSummaryWidgets, // WINDOW_FINANCES_PAGE_SUMMARY - _windowFinancesCashWidgets, // WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH - _windowFinancesParkValueWidgets, // WINDOW_FINANCES_PAGE_VALUE_GRAPH - _windowFinancesProfitWidgets, // WINDOW_FINANCES_PAGE_PROFIT_GRAPH - _windowFinancesMarketingWidgets, // WINDOW_FINANCES_PAGE_MARKETING - _windowFinancesResearchWidgets, // WINDOW_FINANCES_PAGE_RESEARCH -}; -static_assert(std::size(_windowFinancesPageWidgets) == WINDOW_FINANCES_PAGE_COUNT); + static Widget* _windowFinancesPageWidgets[] = { + _windowFinancesSummaryWidgets, // WINDOW_FINANCES_PAGE_SUMMARY + _windowFinancesCashWidgets, // WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH + _windowFinancesParkValueWidgets, // WINDOW_FINANCES_PAGE_VALUE_GRAPH + _windowFinancesProfitWidgets, // WINDOW_FINANCES_PAGE_PROFIT_GRAPH + _windowFinancesMarketingWidgets, // WINDOW_FINANCES_PAGE_MARKETING + _windowFinancesResearchWidgets, // WINDOW_FINANCES_PAGE_RESEARCH + }; + static_assert(std::size(_windowFinancesPageWidgets) == WINDOW_FINANCES_PAGE_COUNT); #pragma endregion -static constexpr StringId _windowFinancesSummaryRowLabels[EnumValue(ExpenditureType::Count)] = { - STR_FINANCES_SUMMARY_RIDE_CONSTRUCTION, - STR_FINANCES_SUMMARY_RIDE_RUNNING_COSTS, - STR_FINANCES_SUMMARY_LAND_PURCHASE, - STR_FINANCES_SUMMARY_LANDSCAPING, - STR_FINANCES_SUMMARY_PARK_ENTRANCE_TICKETS, - STR_FINANCES_SUMMARY_RIDE_TICKETS, - STR_FINANCES_SUMMARY_SHOP_SALES, - STR_FINANCES_SUMMARY_SHOP_STOCK, - STR_FINANCES_SUMMARY_FOOD_DRINK_SALES, - STR_FINANCES_SUMMARY_FOOD_DRINK_STOCK, - STR_FINANCES_SUMMARY_STAFF_WAGES, - STR_FINANCES_SUMMARY_MARKETING, - STR_FINANCES_SUMMARY_RESEARCH, - STR_FINANCES_SUMMARY_LOAN_INTEREST, -}; + static constexpr StringId _windowFinancesSummaryRowLabels[EnumValue(ExpenditureType::Count)] = { + STR_FINANCES_SUMMARY_RIDE_CONSTRUCTION, + STR_FINANCES_SUMMARY_RIDE_RUNNING_COSTS, + STR_FINANCES_SUMMARY_LAND_PURCHASE, + STR_FINANCES_SUMMARY_LANDSCAPING, + STR_FINANCES_SUMMARY_PARK_ENTRANCE_TICKETS, + STR_FINANCES_SUMMARY_RIDE_TICKETS, + STR_FINANCES_SUMMARY_SHOP_SALES, + STR_FINANCES_SUMMARY_SHOP_STOCK, + STR_FINANCES_SUMMARY_FOOD_DRINK_SALES, + STR_FINANCES_SUMMARY_FOOD_DRINK_STOCK, + STR_FINANCES_SUMMARY_STAFF_WAGES, + STR_FINANCES_SUMMARY_MARKETING, + STR_FINANCES_SUMMARY_RESEARCH, + STR_FINANCES_SUMMARY_LOAN_INTEREST, + }; -static constexpr int32_t _windowFinancesTabAnimationFrames[] = { - 8, // WINDOW_FINANCES_PAGE_SUMMARY - 16, // WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH - 16, // WINDOW_FINANCES_PAGE_VALUE_GRAPH - 16, // WINDOW_FINANCES_PAGE_PROFIT_GRAPH - 19, // WINDOW_FINANCES_PAGE_MARKETING - 8, // WINDOW_FINANCES_PAGE_RESEARCH -}; -static_assert(std::size(_windowFinancesTabAnimationFrames) == WINDOW_FINANCES_PAGE_COUNT); + static constexpr int32_t _windowFinancesTabAnimationFrames[] = { + 8, // WINDOW_FINANCES_PAGE_SUMMARY + 16, // WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH + 16, // WINDOW_FINANCES_PAGE_VALUE_GRAPH + 16, // WINDOW_FINANCES_PAGE_PROFIT_GRAPH + 19, // WINDOW_FINANCES_PAGE_MARKETING + 8, // WINDOW_FINANCES_PAGE_RESEARCH + }; + static_assert(std::size(_windowFinancesTabAnimationFrames) == WINDOW_FINANCES_PAGE_COUNT); -static constexpr int32_t EXPENDITURE_COLUMN_WIDTH = 80; + static constexpr int32_t EXPENDITURE_COLUMN_WIDTH = 80; -static constexpr uint32_t _windowFinancesPageHoldDownWidgets[] = { - (1uLL << WIDX_LOAN_INCREASE) | (1uLL << WIDX_LOAN_DECREASE), // WINDOW_FINANCES_PAGE_SUMMARY + static constexpr uint32_t _windowFinancesPageHoldDownWidgets[] = { + (1uLL << WIDX_LOAN_INCREASE) | (1uLL << WIDX_LOAN_DECREASE), // WINDOW_FINANCES_PAGE_SUMMARY - 0, // WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH - 0, // WINDOW_FINANCES_PAGE_VALUE_GRAPH - 0, // WINDOW_FINANCES_PAGE_PROFIT_GRAPH - 0, // WINDOW_FINANCES_PAGE_MARKETING - 0, // WINDOW_FINANCES_PAGE_RESEARCH -}; -static_assert(std::size(_windowFinancesPageHoldDownWidgets) == WINDOW_FINANCES_PAGE_COUNT); + 0, // WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH + 0, // WINDOW_FINANCES_PAGE_VALUE_GRAPH + 0, // WINDOW_FINANCES_PAGE_PROFIT_GRAPH + 0, // WINDOW_FINANCES_PAGE_MARKETING + 0, // WINDOW_FINANCES_PAGE_RESEARCH + }; + static_assert(std::size(_windowFinancesPageHoldDownWidgets) == WINDOW_FINANCES_PAGE_COUNT); #pragma endregion -class FinancesWindow final : public Window -{ -private: - uint32_t _lastPaintedMonth; - - void SetDisabledTabs() + class FinancesWindow final : public Window { - disabled_widgets = (GetGameState().ParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN) ? (1uLL << WIDX_TAB_5) : 0; - } + private: + uint32_t _lastPaintedMonth; -public: - void OnOpen() override - { - SetPage(WINDOW_FINANCES_PAGE_SUMMARY); - _lastPaintedMonth = std::numeric_limits::max(); - ResearchUpdateUncompletedTypes(); - } - - void OnUpdate() override - { - frame_no++; - InvalidateWidget(WIDX_TAB_1 + page); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (page) + void SetDisabledTabs() { - case WINDOW_FINANCES_PAGE_SUMMARY: - OnMouseDownSummary(widgetIndex); - break; - case WINDOW_FINANCES_PAGE_RESEARCH: - WindowResearchFundingMouseDown(this, widgetIndex, WIDX_RESEARCH_FUNDING); - break; - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - 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: - SetPage(widgetIndex - WIDX_TAB_1); - break; - default: - switch (page) - { - case WINDOW_FINANCES_PAGE_MARKETING: - OnMouseUpMarketing(widgetIndex); - break; - case WINDOW_FINANCES_PAGE_RESEARCH: - WindowResearchFundingMouseUp(widgetIndex, WIDX_RESEARCH_FUNDING); - } - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (page == WINDOW_FINANCES_PAGE_RESEARCH) - { - WindowResearchFundingDropdown(widgetIndex, selectedIndex, WIDX_RESEARCH_FUNDING); - } - } - - void OnPrepareDraw() override - { - auto* targetWidgets = _windowFinancesPageWidgets[page]; - - if (widgets != targetWidgets) - { - widgets = targetWidgets; - WindowInitScrollWidgets(*this); + disabled_widgets = (GetGameState().ParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN) ? (1uLL << WIDX_TAB_5) : 0; } - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_6); - - for (auto i = 0; i < WINDOW_FINANCES_PAGE_COUNT; i++) - SetWidgetPressed(WIDX_TAB_1 + i, false); - SetWidgetPressed(WIDX_TAB_1 + page, true); - switch (page) + public: + void OnOpen() override { - case WINDOW_FINANCES_PAGE_SUMMARY: - OnPrepareDrawSummary(); - break; - case WINDOW_FINANCES_PAGE_MARKETING: - OnPrepareDrawMarketing(); - break; - case WINDOW_FINANCES_PAGE_RESEARCH: - WindowResearchFundingPrepareDraw(this, WIDX_RESEARCH_FUNDING); - break; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - switch (page) - { - case WINDOW_FINANCES_PAGE_SUMMARY: - OnDrawSummary(dpi); - break; - case WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH: - OnDrawFinancialGraph(dpi); - break; - case WINDOW_FINANCES_PAGE_VALUE_GRAPH: - OnDrawParkValueGraph(dpi); - break; - case WINDOW_FINANCES_PAGE_PROFIT_GRAPH: - OnDrawProfitGraph(dpi); - break; - case WINDOW_FINANCES_PAGE_MARKETING: - OnDrawMarketing(dpi); - break; - case WINDOW_FINANCES_PAGE_RESEARCH: - WindowResearchFundingDraw(this, dpi); - break; - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - if (page == WINDOW_FINANCES_PAGE_SUMMARY) - { - return { EXPENDITURE_COLUMN_WIDTH * (SummaryMaxAvailableMonth() + 1), 0 }; + SetPage(WINDOW_FINANCES_PAGE_SUMMARY); + _lastPaintedMonth = std::numeric_limits::max(); + ResearchUpdateUncompletedTypes(); } - return {}; - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - if (page != WINDOW_FINANCES_PAGE_SUMMARY) - return; - - auto screenCoords = ScreenCoordsXY{ 0, TABLE_CELL_HEIGHT + 2 }; - - Widget self = widgets[WIDX_SUMMARY_SCROLL]; - int32_t row_width = std::max(scrolls[0].h_right, self.width()); - - // Expenditure / Income row labels - for (int32_t i = 0; i < static_cast(ExpenditureType::Count); i++) + void OnUpdate() override { - // Darken every even row - if (i % 2 == 0) - GfxFillRect( - dpi, - { screenCoords - ScreenCoordsXY{ 0, 1 }, - screenCoords + ScreenCoordsXY{ row_width, (TABLE_CELL_HEIGHT - 2) } }, - ColourMapA[colours[1]].lighter | 0x1000000); - - screenCoords.y += TABLE_CELL_HEIGHT; + frame_no++; + InvalidateWidget(WIDX_TAB_1 + page); } - auto& gameState = GetGameState(); - // Expenditure / Income values for each month - auto currentMonthYear = GetDate().GetMonthsElapsed(); - for (int32_t i = SummaryMaxAvailableMonth(); i >= 0; i--) + void OnMouseDown(WidgetIndex widgetIndex) override { - screenCoords.y = 0; - - uint16_t monthyear = currentMonthYear - i; - - // Month heading - auto ft = Formatter(); - ft.Add(STR_FINANCES_SUMMARY_MONTH_HEADING); - ft.Add(monthyear); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, 0 }, - monthyear == currentMonthYear ? STR_WINDOW_COLOUR_2_STRINGID : STR_BLACK_STRING, ft, - { TextUnderline::On, TextAlignment::RIGHT }); - screenCoords.y += 14; - - // Month expenditures - money64 profit = 0; - for (int32_t j = 0; j < static_cast(ExpenditureType::Count); j++) + switch (page) { - auto expenditure = gameState.ExpenditureTable[i][j]; - if (expenditure != 0) - { - profit += expenditure; - const StringId format = expenditure >= 0 ? STR_FINANCES_SUMMARY_INCOME_VALUE - : STR_FINANCES_SUMMARY_EXPENDITURE_VALUE; - ft = Formatter(); - ft.Add(expenditure); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, 0 }, format, ft, - { TextAlignment::RIGHT }); - } + case WINDOW_FINANCES_PAGE_SUMMARY: + OnMouseDownSummary(widgetIndex); + break; + case WINDOW_FINANCES_PAGE_RESEARCH: + WindowResearchFundingMouseDown(this, widgetIndex, WIDX_RESEARCH_FUNDING); + break; + } + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + 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: + SetPage(widgetIndex - WIDX_TAB_1); + break; + default: + switch (page) + { + case WINDOW_FINANCES_PAGE_MARKETING: + OnMouseUpMarketing(widgetIndex); + break; + case WINDOW_FINANCES_PAGE_RESEARCH: + WindowResearchFundingMouseUp(widgetIndex, WIDX_RESEARCH_FUNDING); + } + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + if (page == WINDOW_FINANCES_PAGE_RESEARCH) + { + WindowResearchFundingDropdown(widgetIndex, selectedIndex, WIDX_RESEARCH_FUNDING); + } + } + + void OnPrepareDraw() override + { + auto* targetWidgets = _windowFinancesPageWidgets[page]; + + if (widgets != targetWidgets) + { + widgets = targetWidgets; + WindowInitScrollWidgets(*this); + } + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_6); + + for (auto i = 0; i < WINDOW_FINANCES_PAGE_COUNT; i++) + SetWidgetPressed(WIDX_TAB_1 + i, false); + SetWidgetPressed(WIDX_TAB_1 + page, true); + switch (page) + { + case WINDOW_FINANCES_PAGE_SUMMARY: + OnPrepareDrawSummary(); + break; + case WINDOW_FINANCES_PAGE_MARKETING: + OnPrepareDrawMarketing(); + break; + case WINDOW_FINANCES_PAGE_RESEARCH: + WindowResearchFundingPrepareDraw(this, WIDX_RESEARCH_FUNDING); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + switch (page) + { + case WINDOW_FINANCES_PAGE_SUMMARY: + OnDrawSummary(dpi); + break; + case WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH: + OnDrawFinancialGraph(dpi); + break; + case WINDOW_FINANCES_PAGE_VALUE_GRAPH: + OnDrawParkValueGraph(dpi); + break; + case WINDOW_FINANCES_PAGE_PROFIT_GRAPH: + OnDrawProfitGraph(dpi); + break; + case WINDOW_FINANCES_PAGE_MARKETING: + OnDrawMarketing(dpi); + break; + case WINDOW_FINANCES_PAGE_RESEARCH: + WindowResearchFundingDraw(this, dpi); + break; + } + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + if (page == WINDOW_FINANCES_PAGE_SUMMARY) + { + return { EXPENDITURE_COLUMN_WIDTH * (SummaryMaxAvailableMonth() + 1), 0 }; + } + + return {}; + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + if (page != WINDOW_FINANCES_PAGE_SUMMARY) + return; + + auto screenCoords = ScreenCoordsXY{ 0, TABLE_CELL_HEIGHT + 2 }; + + Widget self = widgets[WIDX_SUMMARY_SCROLL]; + int32_t row_width = std::max(scrolls[0].h_right, self.width()); + + // Expenditure / Income row labels + for (int32_t i = 0; i < static_cast(ExpenditureType::Count); i++) + { + // Darken every even row + if (i % 2 == 0) + GfxFillRect( + dpi, + { screenCoords - ScreenCoordsXY{ 0, 1 }, + screenCoords + ScreenCoordsXY{ row_width, (TABLE_CELL_HEIGHT - 2) } }, + ColourMapA[colours[1]].lighter | 0x1000000); + screenCoords.y += TABLE_CELL_HEIGHT; } - screenCoords.y += 4; - // Month profit - const StringId format = profit >= 0 ? STR_FINANCES_SUMMARY_INCOME_VALUE : STR_FINANCES_SUMMARY_LOSS_VALUE; - ft = Formatter(); - ft.Add(profit); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, 0 }, format, ft, { TextAlignment::RIGHT }); + auto& gameState = GetGameState(); + // Expenditure / Income values for each month + auto currentMonthYear = GetDate().GetMonthsElapsed(); + for (int32_t i = SummaryMaxAvailableMonth(); i >= 0; i--) + { + screenCoords.y = 0; - GfxFillRect( - dpi, { screenCoords + ScreenCoordsXY{ 10, -2 }, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, -2 } }, - PALETTE_INDEX_10); + uint16_t monthyear = currentMonthYear - i; - screenCoords.x += EXPENDITURE_COLUMN_WIDTH; + // Month heading + auto ft = Formatter(); + ft.Add(STR_FINANCES_SUMMARY_MONTH_HEADING); + ft.Add(monthyear); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, 0 }, + monthyear == currentMonthYear ? STR_WINDOW_COLOUR_2_STRINGID : STR_BLACK_STRING, ft, + { TextUnderline::On, TextAlignment::RIGHT }); + screenCoords.y += 14; + + // Month expenditures + money64 profit = 0; + for (int32_t j = 0; j < static_cast(ExpenditureType::Count); j++) + { + auto expenditure = gameState.ExpenditureTable[i][j]; + if (expenditure != 0) + { + profit += expenditure; + const StringId format = expenditure >= 0 ? STR_FINANCES_SUMMARY_INCOME_VALUE + : STR_FINANCES_SUMMARY_EXPENDITURE_VALUE; + ft = Formatter(); + ft.Add(expenditure); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, 0 }, format, ft, + { TextAlignment::RIGHT }); + } + screenCoords.y += TABLE_CELL_HEIGHT; + } + screenCoords.y += 4; + + // Month profit + const StringId format = profit >= 0 ? STR_FINANCES_SUMMARY_INCOME_VALUE : STR_FINANCES_SUMMARY_LOSS_VALUE; + ft = Formatter(); + ft.Add(profit); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, 0 }, format, ft, { TextAlignment::RIGHT }); + + GfxFillRect( + dpi, + { screenCoords + ScreenCoordsXY{ 10, -2 }, screenCoords + ScreenCoordsXY{ EXPENDITURE_COLUMN_WIDTH, -2 } }, + PALETTE_INDEX_10); + + screenCoords.x += EXPENDITURE_COLUMN_WIDTH; + } + + _lastPaintedMonth = currentMonthYear; } - _lastPaintedMonth = currentMonthYear; - } - - void SetPage(int32_t p) - { - page = p; - frame_no = 0; - - hold_down_widgets = _windowFinancesPageHoldDownWidgets[p]; - pressed_widgets = 0; - widgets = _windowFinancesPageWidgets[p]; - SetDisabledTabs(); - Invalidate(); - - if (p == WINDOW_FINANCES_PAGE_RESEARCH) + void SetPage(int32_t p) { - width = WW_RESEARCH; - height = WH_RESEARCH; - } - else if (p == WINDOW_FINANCES_PAGE_SUMMARY) - { - width = WW_OTHER_TABS; - height = WH_SUMMARY; - } - else - { - width = WW_OTHER_TABS; - height = WH_OTHER_TABS; - } - OnResize(); - OnPrepareDraw(); + page = p; + frame_no = 0; - WindowInitScrollWidgets(*this); + hold_down_widgets = _windowFinancesPageHoldDownWidgets[p]; + pressed_widgets = 0; + widgets = _windowFinancesPageWidgets[p]; + SetDisabledTabs(); + Invalidate(); - // Scroll summary all the way to the right, initially. - if (p == WINDOW_FINANCES_PAGE_SUMMARY) - InitialiseScrollPosition(WIDX_SUMMARY_SCROLL, 0); + if (p == WINDOW_FINANCES_PAGE_RESEARCH) + { + width = WW_RESEARCH; + height = WH_RESEARCH; + } + else if (p == WINDOW_FINANCES_PAGE_SUMMARY) + { + width = WW_OTHER_TABS; + height = WH_SUMMARY; + } + else + { + width = WW_OTHER_TABS; + height = WH_OTHER_TABS; + } + OnResize(); + OnPrepareDraw(); - Invalidate(); - } + WindowInitScrollWidgets(*this); + + // Scroll summary all the way to the right, initially. + if (p == WINDOW_FINANCES_PAGE_SUMMARY) + InitialiseScrollPosition(WIDX_SUMMARY_SCROLL, 0); + + Invalidate(); + } #pragma region Summary Events - void OnMouseDownSummary(WidgetIndex widgetIndex) - { - auto& gameState = GetGameState(); - switch (widgetIndex) + void OnMouseDownSummary(WidgetIndex widgetIndex) { - case WIDX_LOAN_INCREASE: + auto& gameState = GetGameState(); + switch (widgetIndex) { - // If loan can be increased, do so. - // If not, action shows error message. - auto newLoan = gameState.BankLoan + 1000.00_GBP; - if (gameState.BankLoan < gameState.MaxBankLoan) + case WIDX_LOAN_INCREASE: { - newLoan = std::min(gameState.MaxBankLoan, newLoan); - } - auto gameAction = ParkSetLoanAction(newLoan); - GameActions::Execute(&gameAction); - break; - } - case WIDX_LOAN_DECREASE: - { - // If loan is positive, decrease it. - // If loan is negative, action shows error message. - // If loan is exactly 0, prevent error message. - if (gameState.BankLoan != 0) - { - auto newLoan = gameState.BankLoan - 1000.00_GBP; - if (gameState.BankLoan > 0) + // If loan can be increased, do so. + // If not, action shows error message. + auto newLoan = gameState.BankLoan + 1000.00_GBP; + if (gameState.BankLoan < gameState.MaxBankLoan) { - newLoan = std::max(static_cast(0LL), newLoan); + newLoan = std::min(gameState.MaxBankLoan, newLoan); } auto gameAction = ParkSetLoanAction(newLoan); GameActions::Execute(&gameAction); + break; + } + case WIDX_LOAN_DECREASE: + { + // If loan is positive, decrease it. + // If loan is negative, action shows error message. + // If loan is exactly 0, prevent error message. + if (gameState.BankLoan != 0) + { + auto newLoan = gameState.BankLoan - 1000.00_GBP; + if (gameState.BankLoan > 0) + { + newLoan = std::max(static_cast(0LL), newLoan); + } + auto gameAction = ParkSetLoanAction(newLoan); + GameActions::Execute(&gameAction); + } + break; } - break; } } - } - void OnPrepareDrawSummary() - { - // Setting loan widget's format arguments here. - // Nothing else should use the global formatter until - // drawing has completed. - auto ft = Formatter::Common(); - ft.Increment(6); - ft.Add(GetGameState().BankLoan); - - // Keep up with new months being added in the first two years. - if (GetDate().GetMonthsElapsed() != _lastPaintedMonth) - InitialiseScrollPosition(WIDX_SUMMARY_SCROLL, 0); - } - - void OnDrawSummary(DrawPixelInfo& dpi) - { - auto screenCoords = windowPos + ScreenCoordsXY{ 8, 51 }; - auto& gameState = GetGameState(); - - // Expenditure / Income heading - DrawTextBasic( - dpi, screenCoords, STR_FINANCES_SUMMARY_EXPENDITURE_INCOME, {}, - { COLOUR_BLACK, TextUnderline::On, TextAlignment::LEFT }); - screenCoords.y += 14; - - // Expenditure / Income row labels - for (int32_t i = 0; i < static_cast(ExpenditureType::Count); i++) + void OnPrepareDrawSummary() { - // Darken every even row - if (i % 2 == 0) - GfxFillRect( - dpi, - { screenCoords - ScreenCoordsXY{ 0, 1 }, screenCoords + ScreenCoordsXY{ 121, (TABLE_CELL_HEIGHT - 2) } }, - ColourMapA[colours[1]].lighter | 0x1000000); + // Setting loan widget's format arguments here. + // Nothing else should use the global formatter until + // drawing has completed. + auto ft = Formatter::Common(); + ft.Increment(6); + ft.Add(GetGameState().BankLoan); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 1 }, _windowFinancesSummaryRowLabels[i]); - screenCoords.y += TABLE_CELL_HEIGHT; + // Keep up with new months being added in the first two years. + if (GetDate().GetMonthsElapsed() != _lastPaintedMonth) + InitialiseScrollPosition(WIDX_SUMMARY_SCROLL, 0); } - // Horizontal rule below expenditure / income table - GfxFillRectInset( - dpi, { windowPos + ScreenCoordsXY{ 8, 272 }, windowPos + ScreenCoordsXY{ 8 + 513, 272 + 1 } }, colours[1], - INSET_RECT_FLAG_BORDER_INSET); - - // Loan and interest rate - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 8, 279 }, STR_FINANCES_SUMMARY_LOAN); - if (!(gameState.ParkFlags & PARK_FLAGS_RCT1_INTEREST)) + void OnDrawSummary(DrawPixelInfo& dpi) { - auto ft = Formatter(); - ft.Add(gameState.BankLoanInterestRate); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 167, 279 }, STR_FINANCES_SUMMARY_AT_X_PER_YEAR, ft); - } + auto screenCoords = windowPos + ScreenCoordsXY{ 8, 51 }; + auto& gameState = GetGameState(); - // Current cash - auto ft = Formatter(); - ft.Add(gameState.Cash); - StringId stringId = gameState.Cash >= 0 ? STR_CASH_LABEL : STR_CASH_NEGATIVE_LABEL; - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 8, 294 }, stringId, ft); - - // Objective related financial information - if (gameState.ScenarioObjective.Type == OBJECTIVE_MONTHLY_FOOD_INCOME) - { - auto lastMonthProfit = FinanceGetLastMonthShopProfit(); - ft = Formatter(); - ft.Add(lastMonthProfit); + // Expenditure / Income heading DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 280, 279 }, STR_LAST_MONTH_PROFIT_FROM_FOOD_DRINK_MERCHANDISE_SALES_LABEL, ft); - } - else - { - // Park value and company value - ft = Formatter(); - ft.Add(gameState.ParkValue); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 280, 279 }, STR_PARK_VALUE_LABEL, ft); - ft = Formatter(); - ft.Add(gameState.CompanyValue); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 280, 294 }, STR_COMPANY_VALUE_LABEL, ft); - } - } + dpi, screenCoords, STR_FINANCES_SUMMARY_EXPENDITURE_INCOME, {}, + { COLOUR_BLACK, TextUnderline::On, TextAlignment::LEFT }); + screenCoords.y += 14; - uint16_t SummaryMaxAvailableMonth() - { - return std::min(GetDate().GetMonthsElapsed(), EXPENDITURE_TABLE_MONTH_COUNT - 1); - } + // Expenditure / Income row labels + for (int32_t i = 0; i < static_cast(ExpenditureType::Count); i++) + { + // Darken every even row + if (i % 2 == 0) + GfxFillRect( + dpi, + { screenCoords - ScreenCoordsXY{ 0, 1 }, + screenCoords + ScreenCoordsXY{ 121, (TABLE_CELL_HEIGHT - 2) } }, + ColourMapA[colours[1]].lighter | 0x1000000); + + DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 1 }, _windowFinancesSummaryRowLabels[i]); + screenCoords.y += TABLE_CELL_HEIGHT; + } + + // Horizontal rule below expenditure / income table + GfxFillRectInset( + dpi, { windowPos + ScreenCoordsXY{ 8, 272 }, windowPos + ScreenCoordsXY{ 8 + 513, 272 + 1 } }, colours[1], + INSET_RECT_FLAG_BORDER_INSET); + + // Loan and interest rate + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 8, 279 }, STR_FINANCES_SUMMARY_LOAN); + if (!(gameState.ParkFlags & PARK_FLAGS_RCT1_INTEREST)) + { + auto ft = Formatter(); + ft.Add(gameState.BankLoanInterestRate); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 167, 279 }, STR_FINANCES_SUMMARY_AT_X_PER_YEAR, ft); + } + + // Current cash + auto ft = Formatter(); + ft.Add(gameState.Cash); + StringId stringId = gameState.Cash >= 0 ? STR_CASH_LABEL : STR_CASH_NEGATIVE_LABEL; + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 8, 294 }, stringId, ft); + + // Objective related financial information + if (gameState.ScenarioObjective.Type == OBJECTIVE_MONTHLY_FOOD_INCOME) + { + auto lastMonthProfit = FinanceGetLastMonthShopProfit(); + ft = Formatter(); + ft.Add(lastMonthProfit); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 280, 279 }, STR_LAST_MONTH_PROFIT_FROM_FOOD_DRINK_MERCHANDISE_SALES_LABEL, + ft); + } + else + { + // Park value and company value + ft = Formatter(); + ft.Add(gameState.ParkValue); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 280, 279 }, STR_PARK_VALUE_LABEL, ft); + ft = Formatter(); + ft.Add(gameState.CompanyValue); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 280, 294 }, STR_COMPANY_VALUE_LABEL, ft); + } + } + + uint16_t SummaryMaxAvailableMonth() + { + return std::min(GetDate().GetMonthsElapsed(), EXPENDITURE_TABLE_MONTH_COUNT - 1); + } #pragma endregion #pragma region Financial Graph Events - void OnDrawFinancialGraph(DrawPixelInfo& dpi) - { - Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; - auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; - auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - - const auto& gameState = GetGameState(); - - // Cash (less loan) - auto cashLessLoan = gameState.Cash - gameState.BankLoan; - auto ft = Formatter(); - ft.Add(cashLessLoan); - - DrawTextBasic( - dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, - cashLessLoan >= 0 ? STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_POSITIVE - : STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_NEGATIVE, - ft); - - // Graph - GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); - - // Calculate the Y axis scale (log2 of highest [+/-]balance) - int32_t yAxisScale = 0; - for (int32_t i = 0; i < 64; i++) + void OnDrawFinancialGraph(DrawPixelInfo& dpi) { - auto balance = gameState.CashHistory[i]; - if (balance == kMoney64Undefined) - continue; + Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; + auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; + auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - // Modifier balance then keep halving until less than 127 pixels - balance = std::abs(balance) >> yAxisScale; - while (balance > 127) - { - balance /= 2; - yAxisScale++; - } - } + const auto& gameState = GetGameState(); + + // Cash (less loan) + auto cashLessLoan = gameState.Cash - gameState.BankLoan; + auto ft = Formatter(); + ft.Add(cashLessLoan); - // Y axis labels - auto coords = graphTopLeft + ScreenCoordsXY{ 18, 14 }; - money64 axisBase; - for (axisBase = 12.00_GBP; axisBase >= -12.00_GBP; axisBase -= 6.00_GBP) - { - auto axisValue = axisBase << yAxisScale; - ft = Formatter(); - ft.Add(axisValue); DrawTextBasic( - dpi, coords + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { coords + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, coords.y + 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - coords.y += 39; - } + dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, + cashLessLoan >= 0 ? STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_POSITIVE + : STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_NEGATIVE, + ft); - // X axis labels and values - coords = graphTopLeft + ScreenCoordsXY{ 98, 17 }; - Graph::Draw(dpi, gameState.CashHistory, 64, coords, yAxisScale, 128); - } + // Graph + GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); + + // Calculate the Y axis scale (log2 of highest [+/-]balance) + int32_t yAxisScale = 0; + for (int32_t i = 0; i < 64; i++) + { + auto balance = gameState.CashHistory[i]; + if (balance == kMoney64Undefined) + continue; + + // Modifier balance then keep halving until less than 127 pixels + balance = std::abs(balance) >> yAxisScale; + while (balance > 127) + { + balance /= 2; + yAxisScale++; + } + } + + // Y axis labels + auto coords = graphTopLeft + ScreenCoordsXY{ 18, 14 }; + money64 axisBase; + for (axisBase = 12.00_GBP; axisBase >= -12.00_GBP; axisBase -= 6.00_GBP) + { + auto axisValue = axisBase << yAxisScale; + ft = Formatter(); + ft.Add(axisValue); + DrawTextBasic( + dpi, coords + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, + { FontStyle::Small, TextAlignment::RIGHT }); + GfxFillRectInset( + dpi, { coords + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, coords.y + 5 } }, colours[2], + INSET_RECT_FLAG_BORDER_INSET); + coords.y += 39; + } + + // X axis labels and values + coords = graphTopLeft + ScreenCoordsXY{ 98, 17 }; + Graph::Draw(dpi, gameState.CashHistory, 64, coords, yAxisScale, 128); + } #pragma endregion #pragma region Park Value Graph Events - void OnDrawParkValueGraph(DrawPixelInfo& dpi) - { - Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; - auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; - auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - - const auto& gameState = GetGameState(); - - // Park value - auto ft = Formatter(); - ft.Add(gameState.ParkValue); - DrawTextBasic(dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, STR_FINANCES_PARK_VALUE, ft); - - // Graph - GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); - - // Calculate the Y axis scale (log2 of highest [+/-]balance) - int32_t yAxisScale = 0; - for (int32_t i = 0; i < 64; i++) + void OnDrawParkValueGraph(DrawPixelInfo& dpi) { - auto balance = gameState.ParkValueHistory[i]; - if (balance == kMoney64Undefined) - continue; + Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; + auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; + auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - // Modifier balance then keep halving until less than 255 pixels - balance = std::abs(balance) >> yAxisScale; - while (balance > 255) + const auto& gameState = GetGameState(); + + // Park value + auto ft = Formatter(); + ft.Add(gameState.ParkValue); + DrawTextBasic(dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, STR_FINANCES_PARK_VALUE, ft); + + // Graph + GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); + + // Calculate the Y axis scale (log2 of highest [+/-]balance) + int32_t yAxisScale = 0; + for (int32_t i = 0; i < 64; i++) { - balance /= 2; - yAxisScale++; + auto balance = gameState.ParkValueHistory[i]; + if (balance == kMoney64Undefined) + continue; + + // Modifier balance then keep halving until less than 255 pixels + balance = std::abs(balance) >> yAxisScale; + while (balance > 255) + { + balance /= 2; + yAxisScale++; + } } - } - // Y axis labels - auto coords = graphTopLeft + ScreenCoordsXY{ 18, 14 }; - money64 axisBase; - for (axisBase = 24.00_GBP; axisBase >= 0.00_GBP; axisBase -= 6.00_GBP) - { - auto axisValue = axisBase << yAxisScale; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, coords + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { coords + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, coords.y + 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - coords.y += 39; - } + // Y axis labels + auto coords = graphTopLeft + ScreenCoordsXY{ 18, 14 }; + money64 axisBase; + for (axisBase = 24.00_GBP; axisBase >= 0.00_GBP; axisBase -= 6.00_GBP) + { + auto axisValue = axisBase << yAxisScale; + ft = Formatter(); + ft.Add(axisValue); + DrawTextBasic( + dpi, coords + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, + { FontStyle::Small, TextAlignment::RIGHT }); + GfxFillRectInset( + dpi, { coords + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, coords.y + 5 } }, colours[2], + INSET_RECT_FLAG_BORDER_INSET); + coords.y += 39; + } - // X axis labels and values - coords = graphTopLeft + ScreenCoordsXY{ 98, 17 }; - Graph::Draw(dpi, gameState.ParkValueHistory, 64, coords, yAxisScale, 0); - } + // X axis labels and values + coords = graphTopLeft + ScreenCoordsXY{ 98, 17 }; + Graph::Draw(dpi, gameState.ParkValueHistory, 64, coords, yAxisScale, 0); + } #pragma endregion #pragma region Profit Graph Events - void OnDrawProfitGraph(DrawPixelInfo& dpi) - { - auto& gameState = GetGameState(); - Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; - auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; - auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - - // Weekly profit - auto ft = Formatter(); - ft.Add(gameState.CurrentProfit); - DrawTextBasic( - dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, - gameState.CurrentProfit >= 0 ? STR_FINANCES_WEEKLY_PROFIT_POSITIVE : STR_FINANCES_WEEKLY_PROFIT_LOSS, ft); - - // Graph - GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); - - // Calculate the Y axis scale (log2 of highest [+/-]balance) - int32_t yAxisScale = 0; - for (int32_t i = 0; i < 64; i++) + void OnDrawProfitGraph(DrawPixelInfo& dpi) { - auto balance = gameState.WeeklyProfitHistory[i]; - if (balance == kMoney64Undefined) - continue; + auto& gameState = GetGameState(); + Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; + auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; + auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - // Modifier balance then keep halving until less than 127 pixels - balance = std::abs(balance) >> yAxisScale; - while (balance > 127) - { - balance /= 2; - yAxisScale++; - } - } - - // Y axis labels - auto screenPos = graphTopLeft + ScreenCoordsXY{ 18, 14 }; - money64 axisBase; - for (axisBase = 12.00_GBP; axisBase >= -12.00_GBP; axisBase -= 6.00_GBP) - { - money64 axisValue = axisBase << yAxisScale; - ft = Formatter(); - ft.Add(axisValue); + // Weekly profit + auto ft = Formatter(); + ft.Add(gameState.CurrentProfit); DrawTextBasic( - dpi, screenPos + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { screenPos + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, screenPos.y + 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - screenPos.y += 39; - } + dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, + gameState.CurrentProfit >= 0 ? STR_FINANCES_WEEKLY_PROFIT_POSITIVE : STR_FINANCES_WEEKLY_PROFIT_LOSS, ft); - // X axis labels and values - screenPos = graphTopLeft + ScreenCoordsXY{ 98, 17 }; - Graph::Draw(dpi, gameState.WeeklyProfitHistory, 64, screenPos, yAxisScale, 128); - } + // Graph + GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); + + // Calculate the Y axis scale (log2 of highest [+/-]balance) + int32_t yAxisScale = 0; + for (int32_t i = 0; i < 64; i++) + { + auto balance = gameState.WeeklyProfitHistory[i]; + if (balance == kMoney64Undefined) + continue; + + // Modifier balance then keep halving until less than 127 pixels + balance = std::abs(balance) >> yAxisScale; + while (balance > 127) + { + balance /= 2; + yAxisScale++; + } + } + + // Y axis labels + auto screenPos = graphTopLeft + ScreenCoordsXY{ 18, 14 }; + money64 axisBase; + for (axisBase = 12.00_GBP; axisBase >= -12.00_GBP; axisBase -= 6.00_GBP) + { + money64 axisValue = axisBase << yAxisScale; + ft = Formatter(); + ft.Add(axisValue); + DrawTextBasic( + dpi, screenPos + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, + { FontStyle::Small, TextAlignment::RIGHT }); + GfxFillRectInset( + dpi, { screenPos + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, screenPos.y + 5 } }, colours[2], + INSET_RECT_FLAG_BORDER_INSET); + screenPos.y += 39; + } + + // X axis labels and values + screenPos = graphTopLeft + ScreenCoordsXY{ 98, 17 }; + Graph::Draw(dpi, gameState.WeeklyProfitHistory, 64, screenPos, yAxisScale, 128); + } #pragma endregion #pragma region Marketing Events - void OnMouseUpMarketing(WidgetIndex widgetIndex) - { - if (widgetIndex >= WIDX_CAMPAIGN_1 && widgetIndex <= WIDX_CAMPAIGN_6) + void OnMouseUpMarketing(WidgetIndex widgetIndex) { - ContextOpenDetailWindow(WD_NEW_CAMPAIGN, widgetIndex - WIDX_CAMPAIGN_1); - } - } - - void OnPrepareDrawMarketing() - { - // Count number of active campaigns - int32_t numActiveCampaigns = static_cast(gMarketingCampaigns.size()); - int32_t y = std::max(1, numActiveCampaigns) * LIST_ROW_HEIGHT + 92; - - // Update group box positions - _windowFinancesMarketingWidgets[WIDX_ACTIVE_CAMPAIGNS_GROUP].bottom = y - 22; - _windowFinancesMarketingWidgets[WIDX_CAMPAIGNS_AVAILABLE_GROUP].top = y - 13; - - // Update new campaign button visibility - y += 3; - for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++) - { - auto campaignButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i]; - auto marketingCampaign = MarketingGetCampaign(i); - if (marketingCampaign == nullptr && MarketingIsCampaignTypeApplicable(i)) + if (widgetIndex >= WIDX_CAMPAIGN_1 && widgetIndex <= WIDX_CAMPAIGN_6) { - campaignButton->type = WindowWidgetType::Button; - campaignButton->top = y; - campaignButton->bottom = y + BUTTON_FACE_HEIGHT + 1; - y += BUTTON_FACE_HEIGHT + 2; - } - else - { - campaignButton->type = WindowWidgetType::Empty; + ContextOpenDetailWindow(WD_NEW_CAMPAIGN, widgetIndex - WIDX_CAMPAIGN_1); } } - } - void OnDrawMarketing(DrawPixelInfo& dpi) - { - auto screenCoords = windowPos + ScreenCoordsXY{ 8, 62 }; - int32_t noCampaignsActive = 1; - for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++) + void OnPrepareDrawMarketing() { - auto marketingCampaign = MarketingGetCampaign(i); - if (marketingCampaign == nullptr) - continue; + // Count number of active campaigns + int32_t numActiveCampaigns = static_cast(gMarketingCampaigns.size()); + int32_t y = std::max(1, numActiveCampaigns) * LIST_ROW_HEIGHT + 92; - noCampaignsActive = 0; - auto ft = Formatter(); + // Update group box positions + _windowFinancesMarketingWidgets[WIDX_ACTIVE_CAMPAIGNS_GROUP].bottom = y - 22; + _windowFinancesMarketingWidgets[WIDX_CAMPAIGNS_AVAILABLE_GROUP].top = y - 13; - // Set special parameters - switch (i) + // Update new campaign button visibility + y += 3; + for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++) { - case ADVERTISING_CAMPAIGN_RIDE_FREE: - case ADVERTISING_CAMPAIGN_RIDE: + auto campaignButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i]; + auto marketingCampaign = MarketingGetCampaign(i); + if (marketingCampaign == nullptr && MarketingIsCampaignTypeApplicable(i)) { - auto campaignRide = GetRide(marketingCampaign->RideId); - if (campaignRide != nullptr) - { - campaignRide->FormatNameTo(ft); - } - else - { - ft.Add(STR_NONE); - } - break; + campaignButton->type = WindowWidgetType::Button; + campaignButton->top = y; + campaignButton->bottom = y + BUTTON_FACE_HEIGHT + 1; + y += BUTTON_FACE_HEIGHT + 2; } - case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE: - ft.Add(GetShopItemDescriptor(marketingCampaign->ShopItemType).Naming.Plural); - break; - default: + else { - auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - auto parkName = park.Name.c_str(); - ft.Add(STR_STRING); - ft.Add(parkName); + campaignButton->type = WindowWidgetType::Empty; } } - // Advertisement - DrawTextEllipsised(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, 296, MarketingCampaignNames[i][1], ft); - - // Duration - uint16_t weeksRemaining = marketingCampaign->WeeksLeft; - ft = Formatter(); - ft.Add(weeksRemaining); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 304, 0 }, - weeksRemaining == 1 ? STR_1_WEEK_REMAINING : STR_X_WEEKS_REMAINING, ft); - - screenCoords.y += LIST_ROW_HEIGHT; } - if (noCampaignsActive) + void OnDrawMarketing(DrawPixelInfo& dpi) { - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, STR_MARKETING_CAMPAIGNS_NONE); - screenCoords.y += LIST_ROW_HEIGHT; - } - screenCoords.y += 34; - - // Draw campaign button text - for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++) - { - auto campaignButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i]; - if (campaignButton->type != WindowWidgetType::Empty) + auto screenCoords = windowPos + ScreenCoordsXY{ 8, 62 }; + int32_t noCampaignsActive = 1; + for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++) { - // Draw button text - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, MarketingCampaignNames[i][0]); + auto marketingCampaign = MarketingGetCampaign(i); + if (marketingCampaign == nullptr) + continue; + + noCampaignsActive = 0; auto ft = Formatter(); - ft.Add(AdvertisingCampaignPricePerWeek[i]); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ WH_SUMMARY, 0 }, STR_MARKETING_PER_WEEK, ft); - screenCoords.y += BUTTON_FACE_HEIGHT + 2; + // Set special parameters + switch (i) + { + case ADVERTISING_CAMPAIGN_RIDE_FREE: + case ADVERTISING_CAMPAIGN_RIDE: + { + auto campaignRide = GetRide(marketingCampaign->RideId); + if (campaignRide != nullptr) + { + campaignRide->FormatNameTo(ft); + } + else + { + ft.Add(STR_NONE); + } + break; + } + case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE: + ft.Add(GetShopItemDescriptor(marketingCampaign->ShopItemType).Naming.Plural); + break; + default: + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + ft.Add(STR_STRING); + ft.Add(parkName); + } + } + // Advertisement + DrawTextEllipsised(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, 296, MarketingCampaignNames[i][1], ft); + + // Duration + uint16_t weeksRemaining = marketingCampaign->WeeksLeft; + ft = Formatter(); + ft.Add(weeksRemaining); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 304, 0 }, + weeksRemaining == 1 ? STR_1_WEEK_REMAINING : STR_X_WEEKS_REMAINING, ft); + + screenCoords.y += LIST_ROW_HEIGHT; + } + + if (noCampaignsActive) + { + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, STR_MARKETING_CAMPAIGNS_NONE); + screenCoords.y += LIST_ROW_HEIGHT; + } + screenCoords.y += 34; + + // Draw campaign button text + for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++) + { + auto campaignButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i]; + if (campaignButton->type != WindowWidgetType::Empty) + { + // Draw button text + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, MarketingCampaignNames[i][0]); + auto ft = Formatter(); + ft.Add(AdvertisingCampaignPricePerWeek[i]); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ WH_SUMMARY, 0 }, STR_MARKETING_PER_WEEK, ft); + + screenCoords.y += BUTTON_FACE_HEIGHT + 2; + } } } - } #pragma endregion - void InitialiseScrollPosition(WidgetIndex widgetIndex, int32_t scrollId) - { - const auto& widget = this->widgets[widgetIndex]; - scrolls[scrollId].h_left = std::max(0, scrolls[scrollId].h_right - (widget.width() - 2)); - - WidgetScrollUpdateThumbs(*this, widgetIndex); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t tabPage, int32_t spriteIndex) - { - WidgetIndex widgetIndex = WIDX_TAB_1 + tabPage; - - if (!IsWidgetDisabled(widgetIndex)) + void InitialiseScrollPosition(WidgetIndex widgetIndex, int32_t scrollId) { - if (this->page == tabPage) - { - int32_t frame = frame_no / 2; - spriteIndex += (frame % _windowFinancesTabAnimationFrames[this->page]); - } + const auto& widget = this->widgets[widgetIndex]; + scrolls[scrollId].h_left = std::max(0, scrolls[scrollId].h_right - (widget.width() - 2)); - GfxDrawSprite( - dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + WidgetScrollUpdateThumbs(*this, widgetIndex); } - } - void DrawTabImages(DrawPixelInfo& dpi) + void DrawTabImage(DrawPixelInfo& dpi, int32_t tabPage, int32_t spriteIndex) + { + WidgetIndex widgetIndex = WIDX_TAB_1 + tabPage; + + if (!IsWidgetDisabled(widgetIndex)) + { + if (this->page == tabPage) + { + int32_t frame = frame_no / 2; + spriteIndex += (frame % _windowFinancesTabAnimationFrames[this->page]); + } + + GfxDrawSprite( + dpi, ImageId(spriteIndex), + windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + } + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + DrawTabImage(dpi, WINDOW_FINANCES_PAGE_SUMMARY, SPR_TAB_FINANCES_SUMMARY_0); + DrawTabImage(dpi, WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH, SPR_TAB_FINANCES_FINANCIAL_GRAPH_0); + DrawTabImage(dpi, WINDOW_FINANCES_PAGE_VALUE_GRAPH, SPR_TAB_FINANCES_VALUE_GRAPH_0); + DrawTabImage(dpi, WINDOW_FINANCES_PAGE_PROFIT_GRAPH, SPR_TAB_FINANCES_PROFIT_GRAPH_0); + DrawTabImage(dpi, WINDOW_FINANCES_PAGE_MARKETING, SPR_TAB_FINANCES_MARKETING_0); + DrawTabImage(dpi, WINDOW_FINANCES_PAGE_RESEARCH, SPR_TAB_FINANCES_RESEARCH_0); + } + + void OnResize() override + { + ResizeFrameWithPage(); + } + }; + + static FinancesWindow* FinancesWindowOpen(uint8_t page) { - DrawTabImage(dpi, WINDOW_FINANCES_PAGE_SUMMARY, SPR_TAB_FINANCES_SUMMARY_0); - DrawTabImage(dpi, WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH, SPR_TAB_FINANCES_FINANCIAL_GRAPH_0); - DrawTabImage(dpi, WINDOW_FINANCES_PAGE_VALUE_GRAPH, SPR_TAB_FINANCES_VALUE_GRAPH_0); - DrawTabImage(dpi, WINDOW_FINANCES_PAGE_PROFIT_GRAPH, SPR_TAB_FINANCES_PROFIT_GRAPH_0); - DrawTabImage(dpi, WINDOW_FINANCES_PAGE_MARKETING, SPR_TAB_FINANCES_MARKETING_0); - DrawTabImage(dpi, WINDOW_FINANCES_PAGE_RESEARCH, SPR_TAB_FINANCES_RESEARCH_0); + auto* window = WindowFocusOrCreate(WindowClass::Finances, WW_OTHER_TABS, WH_SUMMARY, WF_10); + + if (window != nullptr && page != WINDOW_FINANCES_PAGE_SUMMARY) + window->SetPage(page); + + return window; } - void OnResize() override + WindowBase* WindowFinancesOpen() { - ResizeFrameWithPage(); + return WindowFocusOrCreate(WindowClass::Finances, WW_OTHER_TABS, WH_SUMMARY, WF_10); } -}; -static FinancesWindow* FinancesWindowOpen(uint8_t page) -{ - auto* window = WindowFocusOrCreate(WindowClass::Finances, WW_OTHER_TABS, WH_SUMMARY, WF_10); + WindowBase* WindowFinancesResearchOpen() + { + return FinancesWindowOpen(WINDOW_FINANCES_PAGE_RESEARCH); + } - if (window != nullptr && page != WINDOW_FINANCES_PAGE_SUMMARY) - window->SetPage(page); - - return window; -} - -WindowBase* WindowFinancesOpen() -{ - return WindowFocusOrCreate(WindowClass::Finances, WW_OTHER_TABS, WH_SUMMARY, WF_10); -} - -WindowBase* WindowFinancesResearchOpen() -{ - return FinancesWindowOpen(WINDOW_FINANCES_PAGE_RESEARCH); -} - -WindowBase* WindowFinancesMarketingOpen() -{ - return FinancesWindowOpen(WINDOW_FINANCES_PAGE_MARKETING); -} + WindowBase* WindowFinancesMarketingOpen() + { + return FinancesWindowOpen(WINDOW_FINANCES_PAGE_MARKETING); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Footpath.cpp b/src/openrct2-ui/windows/Footpath.cpp index 94080b04ec..1f744ea857 100644 --- a/src/openrct2-ui/windows/Footpath.cpp +++ b/src/openrct2-ui/windows/Footpath.cpp @@ -34,10 +34,9 @@ #include #include -using namespace OpenRCT2; -using namespace OpenRCT2::Ui; - -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { PATH_CONSTRUCTION_MODE_LAND, @@ -143,695 +142,793 @@ static constexpr uint8_t ConstructionPreviewImages[][4] = { { 16, 17, 18, 19 }, // Upwards { 18, 19, 16, 17 }, // Downwards }; -// clang-format on + // clang-format on -class FootpathWindow final : public Window -{ -private: - const uint16_t ARROW_PULSE_DURATION = 200; + class FootpathWindow final : public Window + { + private: + const uint16_t ARROW_PULSE_DURATION = 200; - uint8_t _footpathConstructDirection; - uint8_t _footpathConstructValidDirections; - uint8_t _footpathConstructionMode; + uint8_t _footpathConstructDirection; + uint8_t _footpathConstructValidDirections; + uint8_t _footpathConstructionMode; - std::vector> _dropdownEntries; + std::vector> _dropdownEntries; - money64 _windowFootpathCost; - uint32_t _footpathConstructionNextArrowPulse = 0; - uint8_t _lastUpdatedCameraRotation = UINT8_MAX; - bool _footpathErrorOccured = false; + money64 _windowFootpathCost; + uint32_t _footpathConstructionNextArrowPulse = 0; + uint8_t _lastUpdatedCameraRotation = UINT8_MAX; + bool _footpathErrorOccured = false; -public: + public: #pragma region Window Override Events - void OnOpen() override - { - widgets = window_footpath_widgets; - - WindowInitScrollWidgets(*this); - WindowPushOthersRight(*this); - ShowGridlines(); - - ToolCancel(); - _footpathConstructionMode = PATH_CONSTRUCTION_MODE_LAND; - ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::PathDown); - InputSetFlag(INPUT_FLAG_6, true); - _footpathErrorOccured = false; - WindowFootpathSetEnabledAndPressedWidgets(); - } - - void OnClose() override - { - FootpathProvisionalUpdate(); - ViewportSetVisibility(ViewportVisibility::Default); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - WindowInvalidateByClass(WindowClass::TopToolbar); - HideGridlines(); - } - - void OnUpdate() override - { - WidgetInvalidate(*this, WIDX_CONSTRUCT); - WindowFootpathUpdateProvisionalPathForBridgeMode(); - - // #2502: The camera might have changed rotation, so we need to update which directional buttons are pressed - uint8_t currentRotation = GetCurrentRotation(); - if (_lastUpdatedCameraRotation != currentRotation) + void OnOpen() override { - _lastUpdatedCameraRotation = currentRotation; + widgets = window_footpath_widgets; + + WindowInitScrollWidgets(*this); + WindowPushOthersRight(*this); + ShowGridlines(); + + ToolCancel(); + _footpathConstructionMode = PATH_CONSTRUCTION_MODE_LAND; + ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::PathDown); + InputSetFlag(INPUT_FLAG_6, true); + _footpathErrorOccured = false; WindowFootpathSetEnabledAndPressedWidgets(); } - // Check tool - if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_LAND) + void OnClose() override { - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - { - Close(); - } - else if (gCurrentToolWidget.window_classification != WindowClass::Footpath) - { - Close(); - } - else if (gCurrentToolWidget.widget_index != WIDX_CONSTRUCT_ON_LAND) - { - Close(); - } - } - else if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL_TOOL) - { - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - { - Close(); - } - else if (gCurrentToolWidget.window_classification != WindowClass::Footpath) - { - Close(); - } - else if (gCurrentToolWidget.widget_index != WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL) - { - Close(); - } - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_FOOTPATH_TYPE: - WindowFootpathShowFootpathTypesDialog(&window_footpath_widgets[widgetIndex], false); - break; - case WIDX_QUEUELINE_TYPE: - WindowFootpathShowFootpathTypesDialog(&window_footpath_widgets[widgetIndex], true); - break; - case WIDX_RAILINGS_TYPE: - WindowFootpathShowRailingsTypesDialog(&window_footpath_widgets[widgetIndex]); - break; - case WIDX_DIRECTION_NW: - WindowFootpathMousedownDirection(0); - break; - case WIDX_DIRECTION_NE: - WindowFootpathMousedownDirection(1); - break; - case WIDX_DIRECTION_SW: - WindowFootpathMousedownDirection(2); - break; - case WIDX_DIRECTION_SE: - WindowFootpathMousedownDirection(3); - break; - case WIDX_SLOPEDOWN: - WindowFootpathMousedownSlope(6); - break; - case WIDX_LEVEL: - WindowFootpathMousedownSlope(0); - break; - case WIDX_SLOPEUP: - WindowFootpathMousedownSlope(2); - break; - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_CONSTRUCT: - WindowFootpathConstruct(); - break; - case WIDX_REMOVE: - WindowFootpathRemove(); - break; - case WIDX_CONSTRUCT_ON_LAND: - if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_LAND) - { - break; - } - - _windowFootpathCost = kMoney64Undefined; - ToolCancel(); - FootpathProvisionalUpdate(); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - _footpathConstructionMode = PATH_CONSTRUCTION_MODE_LAND; - ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::PathDown); - InputSetFlag(INPUT_FLAG_6, true); - _footpathErrorOccured = false; - WindowFootpathSetEnabledAndPressedWidgets(); - break; - case WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL: - if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL_TOOL) - { - break; - } - - _windowFootpathCost = kMoney64Undefined; - ToolCancel(); - FootpathProvisionalUpdate(); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - _footpathConstructionMode = PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL_TOOL; - ToolSet(*this, WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL, Tool::Crosshair); - InputSetFlag(INPUT_FLAG_6, true); - _footpathErrorOccured = false; - WindowFootpathSetEnabledAndPressedWidgets(); - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (selectedIndex < 0 || static_cast(selectedIndex) >= _dropdownEntries.size()) - return; - - auto entryIndex = _dropdownEntries[selectedIndex]; - if (widgetIndex == WIDX_FOOTPATH_TYPE) - { - gFootpathSelection.IsQueueSelected = false; - if (entryIndex.first == ObjectType::Paths) - { - gFootpathSelection.LegacyPath = entryIndex.second; - } - else - { - gFootpathSelection.LegacyPath = OBJECT_ENTRY_INDEX_NULL; - gFootpathSelection.NormalSurface = entryIndex.second; - } - } - else if (widgetIndex == WIDX_QUEUELINE_TYPE) - { - gFootpathSelection.IsQueueSelected = true; - if (entryIndex.first == ObjectType::Paths) - { - gFootpathSelection.LegacyPath = entryIndex.second; - } - else - { - gFootpathSelection.LegacyPath = OBJECT_ENTRY_INDEX_NULL; - gFootpathSelection.QueueSurface = entryIndex.second; - } - } - else if (widgetIndex == WIDX_RAILINGS_TYPE) - { - gFootpathSelection.Railings = entryIndex.second; - } - else - { - return; - } - - FootpathProvisionalUpdate(); - _windowFootpathCost = kMoney64Undefined; - Invalidate(); - } - - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) - { - WindowFootpathSetProvisionalPathAtPoint(screenCoords); - } - else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL) - { - WindowFootpathSetSelectionStartBridgeAtPoint(screenCoords); - } - } - - void OnToolUp(WidgetIndex widgetIndex, const ScreenCoordsXY&) override - { - if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) - { - _footpathErrorOccured = false; - } - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) - { - WindowFootpathPlacePathAtPoint(screenCoords); - } - else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL) - { - WindowFootpathStartBridgeAtPoint(screenCoords); - } - } - - void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) - { - WindowFootpathPlacePathAtPoint(screenCoords); - } - } - - void OnPrepareDraw() override - { - // Press / unpress footpath and queue type buttons - pressed_widgets &= ~(1uLL << WIDX_FOOTPATH_TYPE); - pressed_widgets &= ~(1uLL << WIDX_QUEUELINE_TYPE); - pressed_widgets |= gFootpathSelection.IsQueueSelected ? (1uLL << WIDX_QUEUELINE_TYPE) : (1uLL << WIDX_FOOTPATH_TYPE); - - // Enable / disable construct button - window_footpath_widgets[WIDX_CONSTRUCT].type = _footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL - ? WindowWidgetType::ImgBtn - : WindowWidgetType::Empty; - - if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) - { - window_footpath_widgets[WIDX_RAILINGS_TYPE].type = WindowWidgetType::FlatBtn; - } - else - { - window_footpath_widgets[WIDX_RAILINGS_TYPE].type = WindowWidgetType::Empty; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - ScreenCoordsXY screenCoords; - WindowDrawWidgets(*this, dpi); - WindowFootpathDrawDropdownButtons(dpi); - - if (!IsWidgetDisabled(WIDX_CONSTRUCT)) - { - // Get construction image - uint8_t direction = (_footpathConstructDirection + GetCurrentRotation()) % 4; - uint8_t slope = 0; - if (gFootpathConstructSlope == 2) - { - slope = TILE_ELEMENT_SLOPE_N_CORNER_UP; - } - else if (gFootpathConstructSlope == 6) - { - slope = TILE_ELEMENT_SLOPE_E_CORNER_UP; - } - - std::optional baseImage; - if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) - { - auto selectedPath = gFootpathSelection.GetSelectedSurface(); - const auto* pathType = GetPathSurfaceEntry(selectedPath); - if (pathType != nullptr) - { - baseImage = pathType->BaseImageId; - } - } - else - { - auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); - auto* pathObj = static_cast( - objManager.GetLoadedObject(ObjectType::Paths, gFootpathSelection.LegacyPath)); - if (pathObj != nullptr) - { - auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); - if (gFootpathSelection.IsQueueSelected) - baseImage = pathEntry->GetQueueImage(); - else - baseImage = pathEntry->image; - } - } - - if (baseImage) - { - auto image = *baseImage + ConstructionPreviewImages[slope][direction]; - - // Draw construction image - screenCoords = this->windowPos - + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), - window_footpath_widgets[WIDX_CONSTRUCT].bottom - 60 }; - GfxDrawSprite(dpi, ImageId(image), screenCoords); - } - - // Draw build this... label - screenCoords = this->windowPos - + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), - window_footpath_widgets[WIDX_CONSTRUCT].bottom - 23 }; - DrawTextBasic(dpi, screenCoords, STR_BUILD_THIS, {}, { TextAlignment::CENTRE }); - } - - // Draw cost - screenCoords = this->windowPos - + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), - window_footpath_widgets[WIDX_CONSTRUCT].bottom - 12 }; - if (_windowFootpathCost != kMoney64Undefined) - { - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - auto ft = Formatter(); - ft.Add(_windowFootpathCost); - DrawTextBasic(dpi, screenCoords, STR_COST_LABEL, ft, { TextAlignment::CENTRE }); - } - } - } - -#pragma endregion - -private: - /** - * - * rct2: 0x006A7760 - */ - void WindowFootpathUpdateProvisionalPathForBridgeMode() - { - if (_footpathConstructionMode != PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL) - { - return; - } - - // Recheck area for construction. Set by ride_construction window - if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_2) - { - FootpathProvisionalRemove(); - gProvisionalFootpath.Flags &= ~PROVISIONAL_PATH_FLAG_2; - } - - // Update provisional bridge mode path - if (!(gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_1)) - { - ObjectEntryIndex type; - ObjectEntryIndex railings = gFootpathSelection.Railings; - - CoordsXYZ footpathLoc; - int32_t slope; - FootpathGetNextPathInfo(&type, footpathLoc, &slope); - auto pathConstructFlags = FootpathCreateConstructFlags(type); - - _windowFootpathCost = FootpathProvisionalSet(type, railings, footpathLoc, slope, pathConstructFlags); - WidgetInvalidate(*this, WIDX_CONSTRUCT); - } - - auto curTime = Platform::GetTicks(); - - // Update little directional arrow on provisional bridge mode path - if (_footpathConstructionNextArrowPulse < curTime) - { - _footpathConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; - - gProvisionalFootpath.Flags ^= PROVISIONAL_PATH_FLAG_SHOW_ARROW; - CoordsXYZ footpathLoc; - int32_t slope; - FootpathGetNextPathInfo(nullptr, footpathLoc, &slope); - gMapSelectArrowPosition = footpathLoc; - gMapSelectArrowDirection = _footpathConstructDirection; - if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_SHOW_ARROW) - { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; - } - else - { - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - } - MapInvalidateTileFull(footpathLoc); - } - } - - void WindowFootpathDrawDropdownButtons(DrawPixelInfo& dpi) - { - if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) - { - // Set footpath and queue type button images - auto pathImage = static_cast(SPR_NONE); - auto queueImage = static_cast(SPR_NONE); - auto pathEntry = GetPathSurfaceEntry(gFootpathSelection.NormalSurface); - if (pathEntry != nullptr) - { - pathImage = pathEntry->PreviewImageId; - } - - pathEntry = GetPathSurfaceEntry(gFootpathSelection.QueueSurface); - if (pathEntry != nullptr) - { - queueImage = pathEntry->PreviewImageId; - } - - WindowFootpathDrawDropdownButton(dpi, WIDX_FOOTPATH_TYPE, pathImage); - WindowFootpathDrawDropdownButton(dpi, WIDX_QUEUELINE_TYPE, queueImage); - - // Set railing - auto railingsImage = static_cast(SPR_NONE); - auto railingsEntry = GetPathRailingsEntry(gFootpathSelection.Railings); - if (railingsEntry != nullptr) - { - railingsImage = railingsEntry->PreviewImageId; - } - WindowFootpathDrawDropdownButton(dpi, WIDX_RAILINGS_TYPE, railingsImage); - } - else - { - auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); - - // Set footpath and queue type button images - auto pathImage = static_cast(SPR_NONE); - auto queueImage = static_cast(SPR_NONE); - auto pathObj = static_cast( - objManager.GetLoadedObject(ObjectType::Paths, gFootpathSelection.LegacyPath)); - if (pathObj != nullptr) - { - auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); - pathImage = pathEntry->GetPreviewImage(); - queueImage = pathEntry->GetQueuePreviewImage(); - } - - WindowFootpathDrawDropdownButton(dpi, WIDX_FOOTPATH_TYPE, pathImage); - WindowFootpathDrawDropdownButton(dpi, WIDX_QUEUELINE_TYPE, queueImage); - } - } - - void WindowFootpathDrawDropdownButton(DrawPixelInfo& dpi, WidgetIndex widgetIndex, ImageIndex image) - { - const auto& widget = widgets[widgetIndex]; - GfxDrawSprite(dpi, ImageId(image), { windowPos.x + widget.left, windowPos.y + widget.top }); - } - - /** - * - * rct2: 0x006A7F88 - */ - void WindowFootpathShowFootpathTypesDialog(Widget* widget, bool showQueues) - { - auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); - - uint32_t numPathTypes = 0; - // If the game is in sandbox mode, also show paths that are normally restricted to the scenario editor - bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode); - - _dropdownEntries.clear(); - std::optional defaultIndex; - for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_SURFACE_OBJECTS; i++) - { - const auto* pathType = static_cast( - objManager.GetLoadedObject(ObjectType::FootpathSurface, i)); - if (pathType == nullptr) - { - continue; - } - if ((pathType->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) && !showEditorPaths) - { - continue; - } - // If regular paths can be used as queue, only hide the path if we’re _not_ looking at a queue, - // but the path surface is one. - if (GetGameState().Cheats.AllowRegularPathAsQueue && !showQueues - && ((pathType->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0)) - { - continue; - } - // If the cheat is disabled, hide queues from the regular path view and vice versa. - else if ( - !GetGameState().Cheats.AllowRegularPathAsQueue - && showQueues != ((pathType->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0)) - { - continue; - } - - if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL - && i == (showQueues ? gFootpathSelection.QueueSurface : gFootpathSelection.NormalSurface)) - { - defaultIndex = numPathTypes; - } - - gDropdownItems[numPathTypes].Format = STR_NONE; - Dropdown::SetImage(numPathTypes, ImageId(pathType->PreviewImageId)); - _dropdownEntries.push_back({ ObjectType::FootpathSurface, i }); - numPathTypes++; - } - - for (ObjectEntryIndex i = 0; i < MAX_PATH_OBJECTS; i++) - { - auto* pathObj = static_cast(objManager.GetLoadedObject(ObjectType::Paths, i)); - if (pathObj == nullptr) - { - continue; - } - - auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); - if ((pathEntry->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) && !showEditorPaths) - { - continue; - } - - if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL && gFootpathSelection.LegacyPath == i) - { - defaultIndex = numPathTypes; - } - - gDropdownItems[numPathTypes].Format = STR_NONE; - Dropdown::SetImage( - numPathTypes, ImageId(showQueues ? pathEntry->GetQueuePreviewImage() : pathEntry->GetPreviewImage())); - _dropdownEntries.push_back({ ObjectType::Paths, i }); - numPathTypes++; - } - - auto itemsPerRow = DropdownGetAppropriateImageDropdownItemsPerRow(numPathTypes); - WindowDropdownShowImage( - windowPos.x + widget->left, windowPos.y + widget->top, widget->height() + 1, colours[1], 0, numPathTypes, 47, 36, - itemsPerRow); - if (defaultIndex) - gDropdownDefaultIndex = static_cast(*defaultIndex); - } - - void WindowFootpathShowRailingsTypesDialog(Widget* widget) - { - uint32_t numRailingsTypes = 0; - // If the game is in sandbox mode, also show paths that are normally restricted to the scenario editor - - _dropdownEntries.clear(); - std::optional defaultIndex; - for (int32_t i = 0; i < MAX_FOOTPATH_RAILINGS_OBJECTS; i++) - { - const auto* railingsEntry = GetPathRailingsEntry(i); - if (railingsEntry == nullptr) - { - continue; - } - if (i == gFootpathSelection.Railings) - { - defaultIndex = numRailingsTypes; - } - - gDropdownItems[numRailingsTypes].Format = STR_NONE; - Dropdown::SetImage(numRailingsTypes, ImageId(railingsEntry->PreviewImageId)); - _dropdownEntries.push_back({ ObjectType::FootpathRailings, i }); - numRailingsTypes++; - } - - auto itemsPerRow = DropdownGetAppropriateImageDropdownItemsPerRow(numRailingsTypes); - WindowDropdownShowImage( - windowPos.x + widget->left, windowPos.y + widget->top, widget->height() + 1, colours[1], 0, numRailingsTypes, 47, - 36, itemsPerRow); - if (defaultIndex) - gDropdownDefaultIndex = static_cast(*defaultIndex); - } - - /** - * - * rct2: 0x006A8111 0x006A8135 0x006A815C 0x006A8183 - */ - void WindowFootpathMousedownDirection(int32_t direction) - { - FootpathProvisionalUpdate(); - _footpathConstructDirection = (direction - GetCurrentRotation()) & 3; - _windowFootpathCost = kMoney64Undefined; - WindowFootpathSetEnabledAndPressedWidgets(); - } - - /** - * - * rct2: 0x006A81AA 0x006A81C5 0x006A81E0 - */ - void WindowFootpathMousedownSlope(int32_t slope) - { - FootpathProvisionalUpdate(); - gFootpathConstructSlope = slope; - _windowFootpathCost = kMoney64Undefined; - WindowFootpathSetEnabledAndPressedWidgets(); - } - - /** - * - * rct2: 0x006A81FB - */ - void WindowFootpathSetProvisionalPathAtPoint(const ScreenCoordsXY& screenCoords) - { - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - - auto info = GetMapCoordinatesFromPos( - screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Footpath)); - - if (info.SpriteType == ViewportInteractionItem::None || info.Element == nullptr) - { - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; FootpathProvisionalUpdate(); + ViewportSetVisibility(ViewportVisibility::Default); + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + WindowInvalidateByClass(WindowClass::TopToolbar); + HideGridlines(); } - else + + void OnUpdate() override { - // Check for change - if ((gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_1) - && gProvisionalFootpath.Position == CoordsXYZ{ info.Loc, info.Element->GetBaseZ() }) + WidgetInvalidate(*this, WIDX_CONSTRUCT); + WindowFootpathUpdateProvisionalPathForBridgeMode(); + + // #2502: The camera might have changed rotation, so we need to update which directional buttons are pressed + uint8_t currentRotation = GetCurrentRotation(); + if (_lastUpdatedCameraRotation != currentRotation) + { + _lastUpdatedCameraRotation = currentRotation; + WindowFootpathSetEnabledAndPressedWidgets(); + } + + // Check tool + if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_LAND) + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + { + Close(); + } + else if (gCurrentToolWidget.window_classification != WindowClass::Footpath) + { + Close(); + } + else if (gCurrentToolWidget.widget_index != WIDX_CONSTRUCT_ON_LAND) + { + Close(); + } + } + else if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL_TOOL) + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + { + Close(); + } + else if (gCurrentToolWidget.window_classification != WindowClass::Footpath) + { + Close(); + } + else if (gCurrentToolWidget.widget_index != WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL) + { + Close(); + } + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_FOOTPATH_TYPE: + WindowFootpathShowFootpathTypesDialog(&window_footpath_widgets[widgetIndex], false); + break; + case WIDX_QUEUELINE_TYPE: + WindowFootpathShowFootpathTypesDialog(&window_footpath_widgets[widgetIndex], true); + break; + case WIDX_RAILINGS_TYPE: + WindowFootpathShowRailingsTypesDialog(&window_footpath_widgets[widgetIndex]); + break; + case WIDX_DIRECTION_NW: + WindowFootpathMousedownDirection(0); + break; + case WIDX_DIRECTION_NE: + WindowFootpathMousedownDirection(1); + break; + case WIDX_DIRECTION_SW: + WindowFootpathMousedownDirection(2); + break; + case WIDX_DIRECTION_SE: + WindowFootpathMousedownDirection(3); + break; + case WIDX_SLOPEDOWN: + WindowFootpathMousedownSlope(6); + break; + case WIDX_LEVEL: + WindowFootpathMousedownSlope(0); + break; + case WIDX_SLOPEUP: + WindowFootpathMousedownSlope(2); + break; + } + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_CONSTRUCT: + WindowFootpathConstruct(); + break; + case WIDX_REMOVE: + WindowFootpathRemove(); + break; + case WIDX_CONSTRUCT_ON_LAND: + if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_LAND) + { + break; + } + + _windowFootpathCost = kMoney64Undefined; + ToolCancel(); + FootpathProvisionalUpdate(); + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + _footpathConstructionMode = PATH_CONSTRUCTION_MODE_LAND; + ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::PathDown); + InputSetFlag(INPUT_FLAG_6, true); + _footpathErrorOccured = false; + WindowFootpathSetEnabledAndPressedWidgets(); + break; + case WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL: + if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL_TOOL) + { + break; + } + + _windowFootpathCost = kMoney64Undefined; + ToolCancel(); + FootpathProvisionalUpdate(); + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + _footpathConstructionMode = PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL_TOOL; + ToolSet(*this, WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL, Tool::Crosshair); + InputSetFlag(INPUT_FLAG_6, true); + _footpathErrorOccured = false; + WindowFootpathSetEnabledAndPressedWidgets(); + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + if (selectedIndex < 0 || static_cast(selectedIndex) >= _dropdownEntries.size()) + return; + + auto entryIndex = _dropdownEntries[selectedIndex]; + if (widgetIndex == WIDX_FOOTPATH_TYPE) + { + gFootpathSelection.IsQueueSelected = false; + if (entryIndex.first == ObjectType::Paths) + { + gFootpathSelection.LegacyPath = entryIndex.second; + } + else + { + gFootpathSelection.LegacyPath = OBJECT_ENTRY_INDEX_NULL; + gFootpathSelection.NormalSurface = entryIndex.second; + } + } + else if (widgetIndex == WIDX_QUEUELINE_TYPE) + { + gFootpathSelection.IsQueueSelected = true; + if (entryIndex.first == ObjectType::Paths) + { + gFootpathSelection.LegacyPath = entryIndex.second; + } + else + { + gFootpathSelection.LegacyPath = OBJECT_ENTRY_INDEX_NULL; + gFootpathSelection.QueueSurface = entryIndex.second; + } + } + else if (widgetIndex == WIDX_RAILINGS_TYPE) + { + gFootpathSelection.Railings = entryIndex.second; + } + else + { + return; + } + + FootpathProvisionalUpdate(); + _windowFootpathCost = kMoney64Undefined; + Invalidate(); + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) + { + WindowFootpathSetProvisionalPathAtPoint(screenCoords); + } + else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL) + { + WindowFootpathSetSelectionStartBridgeAtPoint(screenCoords); + } + } + + void OnToolUp(WidgetIndex widgetIndex, const ScreenCoordsXY&) override + { + if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) + { + _footpathErrorOccured = false; + } + } + + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) + { + WindowFootpathPlacePathAtPoint(screenCoords); + } + else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL) + { + WindowFootpathStartBridgeAtPoint(screenCoords); + } + } + + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (widgetIndex == WIDX_CONSTRUCT_ON_LAND) + { + WindowFootpathPlacePathAtPoint(screenCoords); + } + } + + void OnPrepareDraw() override + { + // Press / unpress footpath and queue type buttons + pressed_widgets &= ~(1uLL << WIDX_FOOTPATH_TYPE); + pressed_widgets &= ~(1uLL << WIDX_QUEUELINE_TYPE); + pressed_widgets |= gFootpathSelection.IsQueueSelected ? (1uLL << WIDX_QUEUELINE_TYPE) + : (1uLL << WIDX_FOOTPATH_TYPE); + + // Enable / disable construct button + window_footpath_widgets[WIDX_CONSTRUCT].type = _footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL + ? WindowWidgetType::ImgBtn + : WindowWidgetType::Empty; + + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) + { + window_footpath_widgets[WIDX_RAILINGS_TYPE].type = WindowWidgetType::FlatBtn; + } + else + { + window_footpath_widgets[WIDX_RAILINGS_TYPE].type = WindowWidgetType::Empty; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + ScreenCoordsXY screenCoords; + WindowDrawWidgets(*this, dpi); + WindowFootpathDrawDropdownButtons(dpi); + + if (!IsWidgetDisabled(WIDX_CONSTRUCT)) + { + // Get construction image + uint8_t direction = (_footpathConstructDirection + GetCurrentRotation()) % 4; + uint8_t slope = 0; + if (gFootpathConstructSlope == 2) + { + slope = TILE_ELEMENT_SLOPE_N_CORNER_UP; + } + else if (gFootpathConstructSlope == 6) + { + slope = TILE_ELEMENT_SLOPE_E_CORNER_UP; + } + + std::optional baseImage; + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) + { + auto selectedPath = gFootpathSelection.GetSelectedSurface(); + const auto* pathType = GetPathSurfaceEntry(selectedPath); + if (pathType != nullptr) + { + baseImage = pathType->BaseImageId; + } + } + else + { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto* pathObj = static_cast( + objManager.GetLoadedObject(ObjectType::Paths, gFootpathSelection.LegacyPath)); + if (pathObj != nullptr) + { + auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); + if (gFootpathSelection.IsQueueSelected) + baseImage = pathEntry->GetQueueImage(); + else + baseImage = pathEntry->image; + } + } + + if (baseImage) + { + auto image = *baseImage + ConstructionPreviewImages[slope][direction]; + + // Draw construction image + screenCoords = this->windowPos + + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), + window_footpath_widgets[WIDX_CONSTRUCT].bottom - 60 }; + GfxDrawSprite(dpi, ImageId(image), screenCoords); + } + + // Draw build this... label + screenCoords = this->windowPos + + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), + window_footpath_widgets[WIDX_CONSTRUCT].bottom - 23 }; + DrawTextBasic(dpi, screenCoords, STR_BUILD_THIS, {}, { TextAlignment::CENTRE }); + } + + // Draw cost + screenCoords = this->windowPos + + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), + window_footpath_widgets[WIDX_CONSTRUCT].bottom - 12 }; + if (_windowFootpathCost != kMoney64Undefined) + { + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + auto ft = Formatter(); + ft.Add(_windowFootpathCost); + DrawTextBasic(dpi, screenCoords, STR_COST_LABEL, ft, { TextAlignment::CENTRE }); + } + } + } + +#pragma endregion + + private: + /** + * + * rct2: 0x006A7760 + */ + void WindowFootpathUpdateProvisionalPathForBridgeMode() + { + if (_footpathConstructionMode != PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL) + { + return; + } + + // Recheck area for construction. Set by ride_construction window + if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_2) + { + FootpathProvisionalRemove(); + gProvisionalFootpath.Flags &= ~PROVISIONAL_PATH_FLAG_2; + } + + // Update provisional bridge mode path + if (!(gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_1)) + { + ObjectEntryIndex type; + ObjectEntryIndex railings = gFootpathSelection.Railings; + + CoordsXYZ footpathLoc; + int32_t slope; + FootpathGetNextPathInfo(&type, footpathLoc, &slope); + auto pathConstructFlags = FootpathCreateConstructFlags(type); + + _windowFootpathCost = FootpathProvisionalSet(type, railings, footpathLoc, slope, pathConstructFlags); + WidgetInvalidate(*this, WIDX_CONSTRUCT); + } + + auto curTime = Platform::GetTicks(); + + // Update little directional arrow on provisional bridge mode path + if (_footpathConstructionNextArrowPulse < curTime) + { + _footpathConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; + + gProvisionalFootpath.Flags ^= PROVISIONAL_PATH_FLAG_SHOW_ARROW; + CoordsXYZ footpathLoc; + int32_t slope; + FootpathGetNextPathInfo(nullptr, footpathLoc, &slope); + gMapSelectArrowPosition = footpathLoc; + gMapSelectArrowDirection = _footpathConstructDirection; + if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_SHOW_ARROW) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; + } + else + { + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + } + MapInvalidateTileFull(footpathLoc); + } + } + + void WindowFootpathDrawDropdownButtons(DrawPixelInfo& dpi) + { + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) + { + // Set footpath and queue type button images + auto pathImage = static_cast(SPR_NONE); + auto queueImage = static_cast(SPR_NONE); + auto pathEntry = GetPathSurfaceEntry(gFootpathSelection.NormalSurface); + if (pathEntry != nullptr) + { + pathImage = pathEntry->PreviewImageId; + } + + pathEntry = GetPathSurfaceEntry(gFootpathSelection.QueueSurface); + if (pathEntry != nullptr) + { + queueImage = pathEntry->PreviewImageId; + } + + WindowFootpathDrawDropdownButton(dpi, WIDX_FOOTPATH_TYPE, pathImage); + WindowFootpathDrawDropdownButton(dpi, WIDX_QUEUELINE_TYPE, queueImage); + + // Set railing + auto railingsImage = static_cast(SPR_NONE); + auto railingsEntry = GetPathRailingsEntry(gFootpathSelection.Railings); + if (railingsEntry != nullptr) + { + railingsImage = railingsEntry->PreviewImageId; + } + WindowFootpathDrawDropdownButton(dpi, WIDX_RAILINGS_TYPE, railingsImage); + } + else + { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + + // Set footpath and queue type button images + auto pathImage = static_cast(SPR_NONE); + auto queueImage = static_cast(SPR_NONE); + auto pathObj = static_cast( + objManager.GetLoadedObject(ObjectType::Paths, gFootpathSelection.LegacyPath)); + if (pathObj != nullptr) + { + auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); + pathImage = pathEntry->GetPreviewImage(); + queueImage = pathEntry->GetQueuePreviewImage(); + } + + WindowFootpathDrawDropdownButton(dpi, WIDX_FOOTPATH_TYPE, pathImage); + WindowFootpathDrawDropdownButton(dpi, WIDX_QUEUELINE_TYPE, queueImage); + } + } + + void WindowFootpathDrawDropdownButton(DrawPixelInfo& dpi, WidgetIndex widgetIndex, ImageIndex image) + { + const auto& widget = widgets[widgetIndex]; + GfxDrawSprite(dpi, ImageId(image), { windowPos.x + widget.left, windowPos.y + widget.top }); + } + + /** + * + * rct2: 0x006A7F88 + */ + void WindowFootpathShowFootpathTypesDialog(Widget* widget, bool showQueues) + { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + + uint32_t numPathTypes = 0; + // If the game is in sandbox mode, also show paths that are normally restricted to the scenario editor + bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode); + + _dropdownEntries.clear(); + std::optional defaultIndex; + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_SURFACE_OBJECTS; i++) + { + const auto* pathType = static_cast( + objManager.GetLoadedObject(ObjectType::FootpathSurface, i)); + if (pathType == nullptr) + { + continue; + } + if ((pathType->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) && !showEditorPaths) + { + continue; + } + // If regular paths can be used as queue, only hide the path if we’re _not_ looking at a queue, + // but the path surface is one. + if (GetGameState().Cheats.AllowRegularPathAsQueue && !showQueues + && ((pathType->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0)) + { + continue; + } + // If the cheat is disabled, hide queues from the regular path view and vice versa. + else if ( + !GetGameState().Cheats.AllowRegularPathAsQueue + && showQueues != ((pathType->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0)) + { + continue; + } + + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL + && i == (showQueues ? gFootpathSelection.QueueSurface : gFootpathSelection.NormalSurface)) + { + defaultIndex = numPathTypes; + } + + gDropdownItems[numPathTypes].Format = STR_NONE; + Dropdown::SetImage(numPathTypes, ImageId(pathType->PreviewImageId)); + _dropdownEntries.push_back({ ObjectType::FootpathSurface, i }); + numPathTypes++; + } + + for (ObjectEntryIndex i = 0; i < MAX_PATH_OBJECTS; i++) + { + auto* pathObj = static_cast(objManager.GetLoadedObject(ObjectType::Paths, i)); + if (pathObj == nullptr) + { + continue; + } + + auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); + if ((pathEntry->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) && !showEditorPaths) + { + continue; + } + + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL && gFootpathSelection.LegacyPath == i) + { + defaultIndex = numPathTypes; + } + + gDropdownItems[numPathTypes].Format = STR_NONE; + Dropdown::SetImage( + numPathTypes, ImageId(showQueues ? pathEntry->GetQueuePreviewImage() : pathEntry->GetPreviewImage())); + _dropdownEntries.push_back({ ObjectType::Paths, i }); + numPathTypes++; + } + + auto itemsPerRow = DropdownGetAppropriateImageDropdownItemsPerRow(numPathTypes); + WindowDropdownShowImage( + windowPos.x + widget->left, windowPos.y + widget->top, widget->height() + 1, colours[1], 0, numPathTypes, 47, + 36, itemsPerRow); + if (defaultIndex) + gDropdownDefaultIndex = static_cast(*defaultIndex); + } + + void WindowFootpathShowRailingsTypesDialog(Widget* widget) + { + uint32_t numRailingsTypes = 0; + // If the game is in sandbox mode, also show paths that are normally restricted to the scenario editor + + _dropdownEntries.clear(); + std::optional defaultIndex; + for (int32_t i = 0; i < MAX_FOOTPATH_RAILINGS_OBJECTS; i++) + { + const auto* railingsEntry = GetPathRailingsEntry(i); + if (railingsEntry == nullptr) + { + continue; + } + if (i == gFootpathSelection.Railings) + { + defaultIndex = numRailingsTypes; + } + + gDropdownItems[numRailingsTypes].Format = STR_NONE; + Dropdown::SetImage(numRailingsTypes, ImageId(railingsEntry->PreviewImageId)); + _dropdownEntries.push_back({ ObjectType::FootpathRailings, i }); + numRailingsTypes++; + } + + auto itemsPerRow = DropdownGetAppropriateImageDropdownItemsPerRow(numRailingsTypes); + WindowDropdownShowImage( + windowPos.x + widget->left, windowPos.y + widget->top, widget->height() + 1, colours[1], 0, numRailingsTypes, + 47, 36, itemsPerRow); + if (defaultIndex) + gDropdownDefaultIndex = static_cast(*defaultIndex); + } + + /** + * + * rct2: 0x006A8111 0x006A8135 0x006A815C 0x006A8183 + */ + void WindowFootpathMousedownDirection(int32_t direction) + { + FootpathProvisionalUpdate(); + _footpathConstructDirection = (direction - GetCurrentRotation()) & 3; + _windowFootpathCost = kMoney64Undefined; + WindowFootpathSetEnabledAndPressedWidgets(); + } + + /** + * + * rct2: 0x006A81AA 0x006A81C5 0x006A81E0 + */ + void WindowFootpathMousedownSlope(int32_t slope) + { + FootpathProvisionalUpdate(); + gFootpathConstructSlope = slope; + _windowFootpathCost = kMoney64Undefined; + WindowFootpathSetEnabledAndPressedWidgets(); + } + + /** + * + * rct2: 0x006A81FB + */ + void WindowFootpathSetProvisionalPathAtPoint(const ScreenCoordsXY& screenCoords) + { + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + + auto info = GetMapCoordinatesFromPos( + screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Footpath)); + + if (info.SpriteType == ViewportInteractionItem::None || info.Element == nullptr) + { + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + FootpathProvisionalUpdate(); + } + else + { + // Check for change + if ((gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_1) + && gProvisionalFootpath.Position == CoordsXYZ{ info.Loc, info.Element->GetBaseZ() }) + { + return; + } + + // Set map selection + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectPositionA = info.Loc; + gMapSelectPositionB = info.Loc; + + FootpathProvisionalUpdate(); + + // Set provisional path + int32_t slope = 0; + switch (info.SpriteType) + { + case ViewportInteractionItem::Terrain: + { + auto surfaceElement = info.Element->AsSurface(); + if (surfaceElement != nullptr) + { + slope = DefaultPathSlope[surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK]; + } + break; + } + case ViewportInteractionItem::Footpath: + { + auto pathElement = info.Element->AsPath(); + if (pathElement != nullptr) + { + slope = pathElement->GetSlopeDirection(); + if (pathElement->IsSloped()) + { + slope |= FOOTPATH_PROPERTIES_FLAG_IS_SLOPED; + } + } + break; + } + default: + break; + } + auto z = info.Element->GetBaseZ(); + if (slope & RAISE_FOOTPATH_FLAG) + { + slope &= ~RAISE_FOOTPATH_FLAG; + z += PATH_HEIGHT_STEP; + } + + auto pathType = gFootpathSelection.GetSelectedSurface(); + auto constructFlags = FootpathCreateConstructFlags(pathType); + _windowFootpathCost = FootpathProvisionalSet( + pathType, gFootpathSelection.Railings, { info.Loc, z }, slope, constructFlags); + WindowInvalidateByClass(WindowClass::Footpath); + } + } + + /** + * + * rct2: 0x006A8388 + */ + void WindowFootpathSetSelectionStartBridgeAtPoint(const ScreenCoordsXY& screenCoords) + { + int32_t direction; + TileElement* tileElement; + + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + + auto mapCoords = FootpathBridgeGetInfoFromPos(screenCoords, &direction, &tileElement); + if (mapCoords.IsNull()) { return; } - // Set map selection gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = info.Loc; - gMapSelectPositionB = info.Loc; + gMapSelectPositionA = mapCoords; + gMapSelectPositionB = mapCoords; + + int32_t z = tileElement->GetBaseZ(); + + if (tileElement->GetType() == TileElementType::Surface) + { + uint8_t slope = tileElement->AsSurface()->GetSlope(); + if (slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + { + z += PATH_HEIGHT_STEP; + } // Add 2 for a slope + if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + z += PATH_HEIGHT_STEP; // Add another 2 for a steep slope + } + + gMapSelectArrowPosition = CoordsXYZ{ mapCoords, z }; + gMapSelectArrowDirection = direction; + + MapInvalidateSelectionRect(); + } + + /** + * + * rct2: 0x006A82C5 + */ + void WindowFootpathPlacePathAtPoint(const ScreenCoordsXY& screenCoords) + { + if (_footpathErrorOccured) + { + return; + } FootpathProvisionalUpdate(); - // Set provisional path - int32_t slope = 0; + const auto info = GetMapCoordinatesFromPos( + screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Footpath)); + + if (info.SpriteType == ViewportInteractionItem::None) + { + return; + } + + // Set path + auto slope = 0; switch (info.SpriteType) { case ViewportInteractionItem::Terrain: - { - auto surfaceElement = info.Element->AsSurface(); - if (surfaceElement != nullptr) - { - slope = DefaultPathSlope[surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK]; - } + slope = DefaultPathSlope[info.Element->AsSurface()->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK]; break; - } case ViewportInteractionItem::Footpath: - { - auto pathElement = info.Element->AsPath(); - if (pathElement != nullptr) + slope = info.Element->AsPath()->GetSlopeDirection(); + if (info.Element->AsPath()->IsSloped()) { - slope = pathElement->GetSlopeDirection(); - if (pathElement->IsSloped()) - { - slope |= FOOTPATH_PROPERTIES_FLAG_IS_SLOPED; - } + slope |= FOOTPATH_PROPERTIES_FLAG_IS_SLOPED; } break; - } default: break; } @@ -842,680 +939,584 @@ private: z += PATH_HEIGHT_STEP; } - auto pathType = gFootpathSelection.GetSelectedSurface(); - auto constructFlags = FootpathCreateConstructFlags(pathType); - _windowFootpathCost = FootpathProvisionalSet( - pathType, gFootpathSelection.Railings, { info.Loc, z }, slope, constructFlags); - WindowInvalidateByClass(WindowClass::Footpath); - } - } + // Try and place path + auto selectedType = gFootpathSelection.GetSelectedSurface(); + PathConstructFlags constructFlags = FootpathCreateConstructFlags(selectedType); - /** - * - * rct2: 0x006A8388 - */ - void WindowFootpathSetSelectionStartBridgeAtPoint(const ScreenCoordsXY& screenCoords) - { - int32_t direction; - TileElement* tileElement; - - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - - auto mapCoords = FootpathBridgeGetInfoFromPos(screenCoords, &direction, &tileElement); - if (mapCoords.IsNull()) - { - return; - } - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = mapCoords; - gMapSelectPositionB = mapCoords; - - int32_t z = tileElement->GetBaseZ(); - - if (tileElement->GetType() == TileElementType::Surface) - { - uint8_t slope = tileElement->AsSurface()->GetSlope(); - if (slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) - { - z += PATH_HEIGHT_STEP; - } // Add 2 for a slope - if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - z += PATH_HEIGHT_STEP; // Add another 2 for a steep slope - } - - gMapSelectArrowPosition = CoordsXYZ{ mapCoords, z }; - gMapSelectArrowDirection = direction; - - MapInvalidateSelectionRect(); - } - - /** - * - * rct2: 0x006A82C5 - */ - void WindowFootpathPlacePathAtPoint(const ScreenCoordsXY& screenCoords) - { - if (_footpathErrorOccured) - { - return; - } - - FootpathProvisionalUpdate(); - - const auto info = GetMapCoordinatesFromPos( - screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Footpath)); - - if (info.SpriteType == ViewportInteractionItem::None) - { - return; - } - - // Set path - auto slope = 0; - switch (info.SpriteType) - { - case ViewportInteractionItem::Terrain: - slope = DefaultPathSlope[info.Element->AsSurface()->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK]; - break; - case ViewportInteractionItem::Footpath: - slope = info.Element->AsPath()->GetSlopeDirection(); - if (info.Element->AsPath()->IsSloped()) + auto footpathPlaceAction = FootpathPlaceAction( + { info.Loc, z }, slope, selectedType, gFootpathSelection.Railings, INVALID_DIRECTION, constructFlags); + footpathPlaceAction.SetCallback([this](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) { - slope |= FOOTPATH_PROPERTIES_FLAG_IS_SLOPED; - } - break; - default: - break; - } - auto z = info.Element->GetBaseZ(); - if (slope & RAISE_FOOTPATH_FLAG) - { - slope &= ~RAISE_FOOTPATH_FLAG; - z += PATH_HEIGHT_STEP; - } - - // Try and place path - auto selectedType = gFootpathSelection.GetSelectedSurface(); - PathConstructFlags constructFlags = FootpathCreateConstructFlags(selectedType); - - auto footpathPlaceAction = FootpathPlaceAction( - { info.Loc, z }, slope, selectedType, gFootpathSelection.Railings, INVALID_DIRECTION, constructFlags); - footpathPlaceAction.SetCallback([this](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) - { - // Don't play sound if it is no cost to prevent multiple sounds. TODO: make this work in no money scenarios - if (result->Cost != 0) - { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - } - } - else - { - _footpathErrorOccured = true; - } - }); - GameActions::Execute(&footpathPlaceAction); - } - - /** - * - * rct2: 0x006A840F - */ - void WindowFootpathStartBridgeAtPoint(const ScreenCoordsXY& screenCoords) - { - int32_t z, direction; - TileElement* tileElement; - - auto mapCoords = FootpathBridgeGetInfoFromPos(screenCoords, &direction, &tileElement); - if (mapCoords.IsNull()) - { - return; - } - - if (tileElement->GetType() == TileElementType::Surface) - { - // If we start the path on a slope, the arrow is slightly raised, so we - // expect the path to be slightly raised as well. - uint8_t slope = tileElement->AsSurface()->GetSlope(); - z = tileElement->GetBaseZ(); - if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - { - // Steep diagonal slope - z += 2 * PATH_HEIGHT_STEP; - } - else if (slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) - { - // Normal slope - z += PATH_HEIGHT_STEP; - } - } - else - { - z = tileElement->GetBaseZ(); - if (tileElement->GetType() == TileElementType::Path) - { - if (tileElement->AsPath()->IsSloped()) - { - if (direction == (tileElement->AsPath()->GetSlopeDirection())) + // Don't play sound if it is no cost to prevent multiple sounds. TODO: make this work in no money scenarios + if (result->Cost != 0) { - z += PATH_HEIGHT_STEP; + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); } } - } - } - - ToolCancel(); - gFootpathConstructFromPosition = { mapCoords, z }; - _footpathConstructDirection = direction; - gProvisionalFootpath.Flags = 0; - gFootpathConstructSlope = 0; - _footpathConstructionMode = PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL; - _footpathConstructValidDirections = INVALID_DIRECTION; - WindowFootpathSetEnabledAndPressedWidgets(); - } - - /** - * Construct a piece of footpath while in bridge building mode. - * rct2: 0x006A79B7 - */ - void WindowFootpathConstruct() - { - _windowFootpathCost = kMoney64Undefined; - FootpathProvisionalUpdate(); - - ObjectEntryIndex type; - int32_t slope; - CoordsXYZ footpathLoc; - FootpathGetNextPathInfo(&type, footpathLoc, &slope); - - PathConstructFlags constructFlags = FootpathCreateConstructFlags(type); - - auto footpathPlaceAction = FootpathPlaceAction( - footpathLoc, slope, type, gFootpathSelection.Railings, _footpathConstructDirection, constructFlags); - - footpathPlaceAction.SetCallback([footpathLoc](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) - { - Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - } - - auto* self = static_cast(WindowFindByClass(WindowClass::Footpath)); - if (self == nullptr) - { - return; - } - - if (result->Error == GameActions::Status::Ok) - { - if (gFootpathConstructSlope == 0) - { - self->_footpathConstructValidDirections = INVALID_DIRECTION; - } else { - self->_footpathConstructValidDirections = self->_footpathConstructDirection; + _footpathErrorOccured = true; } - - if (gFootpathGroundFlags & ELEMENT_IS_UNDERGROUND) - { - ViewportSetVisibility(ViewportVisibility::UndergroundViewOn); - } - - gFootpathConstructFromPosition = footpathLoc; - // If we have just built an upwards slope, the next path to construct is - // a bit higher. Note that the z returned by footpath_get_next_path_info - // already is lowered if we are building a downwards slope. - if (gFootpathConstructSlope == 2) - { - gFootpathConstructFromPosition.z += PATH_HEIGHT_STEP; - } - } - self->WindowFootpathSetEnabledAndPressedWidgets(); - }); - GameActions::Execute(&footpathPlaceAction); - } - - /** - * - * rct2: 0x006A78EF - */ - void FootpathRemoveTileElement(TileElement* tileElement) - { - auto z = tileElement->GetBaseZ(); - if (tileElement->AsPath()->IsSloped()) - { - uint8_t slopeDirection = tileElement->AsPath()->GetSlopeDirection(); - slopeDirection = DirectionReverse(slopeDirection); - if (slopeDirection == _footpathConstructDirection) - { - z += PATH_HEIGHT_STEP; - } + }); + GameActions::Execute(&footpathPlaceAction); } - // Find a connected edge - int32_t edge = DirectionReverse(_footpathConstructDirection); - if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) + /** + * + * rct2: 0x006A840F + */ + void WindowFootpathStartBridgeAtPoint(const ScreenCoordsXY& screenCoords) { - edge = (edge + 1) & 3; - if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) + int32_t z, direction; + TileElement* tileElement; + + auto mapCoords = FootpathBridgeGetInfoFromPos(screenCoords, &direction, &tileElement); + if (mapCoords.IsNull()) { - edge = (edge + 2) & 3; - if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) - { - edge = (edge - 1) & 3; - if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) - { - edge = DirectionReverse(edge); - } - } + return; } - } - gFootpathConstructFromPosition.z = tileElement->GetBaseZ(); - auto action = FootpathRemoveAction(gFootpathConstructFromPosition); - GameActions::Execute(&action); - - // Move selection - edge = DirectionReverse(edge); - gFootpathConstructFromPosition.x -= CoordsDirectionDelta[edge].x; - gFootpathConstructFromPosition.y -= CoordsDirectionDelta[edge].y; - gFootpathConstructFromPosition.z = z; - _footpathConstructDirection = edge; - _footpathConstructValidDirections = INVALID_DIRECTION; - } - - /** - * - * rct2: 0x006A7873 - */ - TileElement* FootpathGetTileElementToRemove() - { - TileElement* tileElement; - int32_t z, zLow; - - if (!MapIsLocationValid(gFootpathConstructFromPosition)) - { - return nullptr; - } - - z = std::min(255 * COORDS_Z_STEP, gFootpathConstructFromPosition.z); - zLow = z - PATH_HEIGHT_STEP; - - tileElement = MapGetFirstElementAt(gFootpathConstructFromPosition); - do - { - if (tileElement == nullptr) - break; - if (tileElement->GetType() == TileElementType::Path) + if (tileElement->GetType() == TileElementType::Surface) { - if (tileElement->GetBaseZ() == z) + // If we start the path on a slope, the arrow is slightly raised, so we + // expect the path to be slightly raised as well. + uint8_t slope = tileElement->AsSurface()->GetSlope(); + z = tileElement->GetBaseZ(); + if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) { - if (tileElement->AsPath()->IsSloped()) - { - if (DirectionReverse(tileElement->AsPath()->GetSlopeDirection()) != _footpathConstructDirection) - { - continue; - } - } - - return tileElement; + // Steep diagonal slope + z += 2 * PATH_HEIGHT_STEP; } - if (tileElement->GetBaseZ() == zLow) + else if (slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { - if (!tileElement->AsPath()->IsSloped()) - { - if ((tileElement->AsPath()->GetSlopeDirection()) == _footpathConstructDirection) - { - continue; - } - } - - return tileElement; + // Normal slope + z += PATH_HEIGHT_STEP; } } - } while (!(tileElement++)->IsLastForTile()); - - return nullptr; - } - - /** - * - * rct2: 0x006A7863 - */ - void WindowFootpathRemove() - { - TileElement* tileElement; - - _windowFootpathCost = kMoney64Undefined; - FootpathProvisionalUpdate(); - - tileElement = FootpathGetTileElementToRemove(); - if (tileElement != nullptr) - { - FootpathRemoveTileElement(tileElement); - } - - WindowFootpathSetEnabledAndPressedWidgets(); - } - - /** - * - * rct2: 0x006A855C - */ - void WindowFootpathSetEnabledAndPressedWidgets() - { - if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL) - { - MapInvalidateMapSelectionTiles(); - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags |= MAP_SELECT_FLAG_GREEN; - - int32_t direction = _footpathConstructDirection; - gMapSelectionTiles.clear(); - gMapSelectionTiles.push_back({ gFootpathConstructFromPosition.x + CoordsDirectionDelta[direction].x, - gFootpathConstructFromPosition.y + CoordsDirectionDelta[direction].y }); - MapInvalidateMapSelectionTiles(); - } - - uint64_t pressedWidgets = pressed_widgets - & ~((1LL << WIDX_DIRECTION_NW) | (1LL << WIDX_DIRECTION_NE) | (1LL << WIDX_DIRECTION_SW) - | (1LL << WIDX_DIRECTION_SE) | (1LL << WIDX_SLOPEDOWN) | (1LL << WIDX_LEVEL) | (1LL << WIDX_SLOPEUP)); - uint64_t disabledWidgets = 0; - int32_t currentRotation = GetCurrentRotation(); - if (_footpathConstructionMode >= PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL) - { - // Set pressed directional widget - int32_t direction = (_footpathConstructDirection + currentRotation) & 3; - pressedWidgets |= (1LL << (WIDX_DIRECTION_NW + direction)); - - // Set pressed slope widget - int32_t slope = gFootpathConstructSlope; - if (slope == TILE_ELEMENT_SLOPE_SE_SIDE_UP) - { - pressedWidgets |= (1uLL << WIDX_SLOPEDOWN); - } - else if (slope == TILE_ELEMENT_SLOPE_FLAT) - { - pressedWidgets |= (1uLL << WIDX_LEVEL); - } else { - pressedWidgets |= (1uLL << WIDX_SLOPEUP); + z = tileElement->GetBaseZ(); + if (tileElement->GetType() == TileElementType::Path) + { + if (tileElement->AsPath()->IsSloped()) + { + if (direction == (tileElement->AsPath()->GetSlopeDirection())) + { + z += PATH_HEIGHT_STEP; + } + } + } } - // Enable / disable directional widgets - direction = _footpathConstructValidDirections; - if (direction != INVALID_DIRECTION) - { - disabledWidgets |= (1uLL << WIDX_DIRECTION_NW) | (1uLL << WIDX_DIRECTION_NE) | (1uLL << WIDX_DIRECTION_SW) - | (1uLL << WIDX_DIRECTION_SE); - - direction = (direction + currentRotation) & 3; - disabledWidgets &= ~(1 << (WIDX_DIRECTION_NW + direction)); - } + ToolCancel(); + gFootpathConstructFromPosition = { mapCoords, z }; + _footpathConstructDirection = direction; + gProvisionalFootpath.Flags = 0; + gFootpathConstructSlope = 0; + _footpathConstructionMode = PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL; + _footpathConstructValidDirections = INVALID_DIRECTION; + WindowFootpathSetEnabledAndPressedWidgets(); } - else + + /** + * Construct a piece of footpath while in bridge building mode. + * rct2: 0x006A79B7 + */ + void WindowFootpathConstruct() { - // Disable all bridge mode widgets - disabledWidgets |= (1uLL << WIDX_DIRECTION_GROUP) | (1uLL << WIDX_DIRECTION_NW) | (1uLL << WIDX_DIRECTION_NE) - | (1uLL << WIDX_DIRECTION_SW) | (1uLL << WIDX_DIRECTION_SE) | (1uLL << WIDX_SLOPE_GROUP) - | (1uLL << WIDX_SLOPEDOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPEUP) | (1uLL << WIDX_CONSTRUCT) - | (1uLL << WIDX_REMOVE); + _windowFootpathCost = kMoney64Undefined; + FootpathProvisionalUpdate(); + + ObjectEntryIndex type; + int32_t slope; + CoordsXYZ footpathLoc; + FootpathGetNextPathInfo(&type, footpathLoc, &slope); + + PathConstructFlags constructFlags = FootpathCreateConstructFlags(type); + + auto footpathPlaceAction = FootpathPlaceAction( + footpathLoc, slope, type, gFootpathSelection.Railings, _footpathConstructDirection, constructFlags); + + footpathPlaceAction.SetCallback([footpathLoc](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) + { + Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + } + + auto* self = static_cast(WindowFindByClass(WindowClass::Footpath)); + if (self == nullptr) + { + return; + } + + if (result->Error == GameActions::Status::Ok) + { + if (gFootpathConstructSlope == 0) + { + self->_footpathConstructValidDirections = INVALID_DIRECTION; + } + else + { + self->_footpathConstructValidDirections = self->_footpathConstructDirection; + } + + if (gFootpathGroundFlags & ELEMENT_IS_UNDERGROUND) + { + ViewportSetVisibility(ViewportVisibility::UndergroundViewOn); + } + + gFootpathConstructFromPosition = footpathLoc; + // If we have just built an upwards slope, the next path to construct is + // a bit higher. Note that the z returned by footpath_get_next_path_info + // already is lowered if we are building a downwards slope. + if (gFootpathConstructSlope == 2) + { + gFootpathConstructFromPosition.z += PATH_HEIGHT_STEP; + } + } + self->WindowFootpathSetEnabledAndPressedWidgets(); + }); + GameActions::Execute(&footpathPlaceAction); } - pressed_widgets = pressedWidgets; - disabled_widgets = disabledWidgets; - Invalidate(); - } + /** + * + * rct2: 0x006A78EF + */ + void FootpathRemoveTileElement(TileElement* tileElement) + { + auto z = tileElement->GetBaseZ(); + if (tileElement->AsPath()->IsSloped()) + { + uint8_t slopeDirection = tileElement->AsPath()->GetSlopeDirection(); + slopeDirection = DirectionReverse(slopeDirection); + if (slopeDirection == _footpathConstructDirection) + { + z += PATH_HEIGHT_STEP; + } + } + + // Find a connected edge + int32_t edge = DirectionReverse(_footpathConstructDirection); + if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) + { + edge = (edge + 1) & 3; + if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) + { + edge = (edge + 2) & 3; + if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) + { + edge = (edge - 1) & 3; + if (!(tileElement->AsPath()->GetEdges() & (1 << edge))) + { + edge = DirectionReverse(edge); + } + } + } + } + + gFootpathConstructFromPosition.z = tileElement->GetBaseZ(); + auto action = FootpathRemoveAction(gFootpathConstructFromPosition); + GameActions::Execute(&action); + + // Move selection + edge = DirectionReverse(edge); + gFootpathConstructFromPosition.x -= CoordsDirectionDelta[edge].x; + gFootpathConstructFromPosition.y -= CoordsDirectionDelta[edge].y; + gFootpathConstructFromPosition.z = z; + _footpathConstructDirection = edge; + _footpathConstructValidDirections = INVALID_DIRECTION; + } + + /** + * + * rct2: 0x006A7873 + */ + TileElement* FootpathGetTileElementToRemove() + { + TileElement* tileElement; + int32_t z, zLow; + + if (!MapIsLocationValid(gFootpathConstructFromPosition)) + { + return nullptr; + } + + z = std::min(255 * COORDS_Z_STEP, gFootpathConstructFromPosition.z); + zLow = z - PATH_HEIGHT_STEP; + + tileElement = MapGetFirstElementAt(gFootpathConstructFromPosition); + do + { + if (tileElement == nullptr) + break; + if (tileElement->GetType() == TileElementType::Path) + { + if (tileElement->GetBaseZ() == z) + { + if (tileElement->AsPath()->IsSloped()) + { + if (DirectionReverse(tileElement->AsPath()->GetSlopeDirection()) != _footpathConstructDirection) + { + continue; + } + } + + return tileElement; + } + if (tileElement->GetBaseZ() == zLow) + { + if (!tileElement->AsPath()->IsSloped()) + { + if ((tileElement->AsPath()->GetSlopeDirection()) == _footpathConstructDirection) + { + continue; + } + } + + return tileElement; + } + } + } while (!(tileElement++)->IsLastForTile()); + + return nullptr; + } + + /** + * + * rct2: 0x006A7863 + */ + void WindowFootpathRemove() + { + TileElement* tileElement; + + _windowFootpathCost = kMoney64Undefined; + FootpathProvisionalUpdate(); + + tileElement = FootpathGetTileElementToRemove(); + if (tileElement != nullptr) + { + FootpathRemoveTileElement(tileElement); + } + + WindowFootpathSetEnabledAndPressedWidgets(); + } + + /** + * + * rct2: 0x006A855C + */ + void WindowFootpathSetEnabledAndPressedWidgets() + { + if (_footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL) + { + MapInvalidateMapSelectionTiles(); + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags |= MAP_SELECT_FLAG_GREEN; + + int32_t direction = _footpathConstructDirection; + gMapSelectionTiles.clear(); + gMapSelectionTiles.push_back({ gFootpathConstructFromPosition.x + CoordsDirectionDelta[direction].x, + gFootpathConstructFromPosition.y + CoordsDirectionDelta[direction].y }); + MapInvalidateMapSelectionTiles(); + } + + uint64_t pressedWidgets = pressed_widgets + & ~((1LL << WIDX_DIRECTION_NW) | (1LL << WIDX_DIRECTION_NE) | (1LL << WIDX_DIRECTION_SW) + | (1LL << WIDX_DIRECTION_SE) | (1LL << WIDX_SLOPEDOWN) | (1LL << WIDX_LEVEL) | (1LL << WIDX_SLOPEUP)); + uint64_t disabledWidgets = 0; + int32_t currentRotation = GetCurrentRotation(); + if (_footpathConstructionMode >= PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL) + { + // Set pressed directional widget + int32_t direction = (_footpathConstructDirection + currentRotation) & 3; + pressedWidgets |= (1LL << (WIDX_DIRECTION_NW + direction)); + + // Set pressed slope widget + int32_t slope = gFootpathConstructSlope; + if (slope == TILE_ELEMENT_SLOPE_SE_SIDE_UP) + { + pressedWidgets |= (1uLL << WIDX_SLOPEDOWN); + } + else if (slope == TILE_ELEMENT_SLOPE_FLAT) + { + pressedWidgets |= (1uLL << WIDX_LEVEL); + } + else + { + pressedWidgets |= (1uLL << WIDX_SLOPEUP); + } + + // Enable / disable directional widgets + direction = _footpathConstructValidDirections; + if (direction != INVALID_DIRECTION) + { + disabledWidgets |= (1uLL << WIDX_DIRECTION_NW) | (1uLL << WIDX_DIRECTION_NE) | (1uLL << WIDX_DIRECTION_SW) + | (1uLL << WIDX_DIRECTION_SE); + + direction = (direction + currentRotation) & 3; + disabledWidgets &= ~(1 << (WIDX_DIRECTION_NW + direction)); + } + } + else + { + // Disable all bridge mode widgets + disabledWidgets |= (1uLL << WIDX_DIRECTION_GROUP) | (1uLL << WIDX_DIRECTION_NW) | (1uLL << WIDX_DIRECTION_NE) + | (1uLL << WIDX_DIRECTION_SW) | (1uLL << WIDX_DIRECTION_SE) | (1uLL << WIDX_SLOPE_GROUP) + | (1uLL << WIDX_SLOPEDOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPEUP) | (1uLL << WIDX_CONSTRUCT) + | (1uLL << WIDX_REMOVE); + } + + pressed_widgets = pressedWidgets; + disabled_widgets = disabledWidgets; + Invalidate(); + } + + /** + * + * rct2: 0x006A7B20 + */ + void FootpathGetNextPathInfo(ObjectEntryIndex* type, CoordsXYZ& footpathLoc, int32_t* slope) + { + auto direction = _footpathConstructDirection; + footpathLoc.x = gFootpathConstructFromPosition.x + CoordsDirectionDelta[direction].x; + footpathLoc.y = gFootpathConstructFromPosition.y + CoordsDirectionDelta[direction].y; + footpathLoc.z = gFootpathConstructFromPosition.z; + if (type != nullptr) + { + *type = gFootpathSelection.GetSelectedSurface(); + } + *slope = TILE_ELEMENT_SLOPE_FLAT; + if (gFootpathConstructSlope != 0) + { + *slope = _footpathConstructDirection | TILE_ELEMENT_SLOPE_S_CORNER_UP; + if (gFootpathConstructSlope != 2) + { + footpathLoc.z -= PATH_HEIGHT_STEP; + *slope ^= TILE_ELEMENT_SLOPE_E_CORNER_UP; + } + } + } + + PathConstructFlags FootpathCreateConstructFlags(ObjectEntryIndex& type) + { + PathConstructFlags pathConstructFlags = 0; + if (gFootpathSelection.IsQueueSelected) + pathConstructFlags |= PathConstructFlag::IsQueue; + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL) + { + pathConstructFlags |= PathConstructFlag::IsLegacyPathObject; + type = gFootpathSelection.LegacyPath; + } + return pathConstructFlags; + } + +#pragma region Keyboard Shortcuts Events + public: + void KeyboardShortcutTurnLeft() + { + if (IsWidgetDisabled(WIDX_DIRECTION_NW) || IsWidgetDisabled(WIDX_DIRECTION_NE) + || IsWidgetDisabled(WIDX_DIRECTION_SW) || IsWidgetDisabled(WIDX_DIRECTION_SE) || _footpathConstructionMode != 2) + { + return; + } + int32_t currentRotation = GetCurrentRotation(); + int32_t turnedRotation = _footpathConstructDirection - currentRotation + (currentRotation % 2 == 1 ? 1 : -1); + WindowFootpathMousedownDirection(turnedRotation); + } + + void KeyboardShortcutTurnRight() + { + if (IsWidgetDisabled(WIDX_DIRECTION_NW) || IsWidgetDisabled(WIDX_DIRECTION_NE) + || IsWidgetDisabled(WIDX_DIRECTION_SW) || IsWidgetDisabled(WIDX_DIRECTION_SE) || _footpathConstructionMode != 2) + { + return; + } + int32_t currentRotation = GetCurrentRotation(); + int32_t turnedRotation = _footpathConstructDirection - currentRotation + (currentRotation % 2 == 1 ? -1 : 1); + WindowFootpathMousedownDirection(turnedRotation); + } + + void KeyboardShortcutShortcutSlopeDown() + { + if (IsWidgetDisabled(WIDX_SLOPEDOWN) || IsWidgetDisabled(WIDX_LEVEL) || IsWidgetDisabled(WIDX_SLOPEUP) + || widgets[WIDX_LEVEL].type == WindowWidgetType::Empty) + { + return; + } + + switch (gFootpathConstructSlope) + { + case 0: + OnMouseDown(WIDX_SLOPEDOWN); + break; + case 2: + OnMouseDown(WIDX_LEVEL); + break; + default: + case 6: + return; + } + } + + void KeyboardShortcutSlopeUp() + { + if (IsWidgetDisabled(WIDX_SLOPEDOWN) || IsWidgetDisabled(WIDX_LEVEL) || IsWidgetDisabled(WIDX_SLOPEUP) + || widgets[WIDX_LEVEL].type == WindowWidgetType::Empty) + { + return; + } + + switch (gFootpathConstructSlope) + { + case 6: + OnMouseDown(WIDX_LEVEL); + break; + case 0: + OnMouseDown(WIDX_SLOPEUP); + break; + default: + case 2: + return; + } + } + + void KeyboardShortcutSlopeLevel() + { + if (IsWidgetDisabled(WIDX_SLOPEDOWN) || IsWidgetDisabled(WIDX_LEVEL) || IsWidgetDisabled(WIDX_SLOPEUP) + || widgets[WIDX_LEVEL].type == WindowWidgetType::Empty || gFootpathConstructSlope == 0) + { + return; + } + + OnMouseDown(WIDX_LEVEL); + } + + void KeyboardShortcutDemolishCurrent() + { + if (IsWidgetDisabled(WIDX_REMOVE) || widgets[WIDX_REMOVE].type == WindowWidgetType::Empty + || (!GetGameState().Cheats.BuildInPauseMode && GameIsPaused())) + { + return; + } + + WindowFootpathRemove(); + } + + void KeyboardShortcutBuildCurrent() + { + if (IsWidgetDisabled(WIDX_CONSTRUCT) || widgets[WIDX_CONSTRUCT].type == WindowWidgetType::Empty) + { + return; + } + + OnMouseUp(WIDX_CONSTRUCT); + } + + void OnResize() override + { + ResizeFrame(); + } + +#pragma endregion + }; /** * - * rct2: 0x006A7B20 + * rct2: 0x006A7C43 */ - void FootpathGetNextPathInfo(ObjectEntryIndex* type, CoordsXYZ& footpathLoc, int32_t* slope) + WindowBase* WindowFootpathOpen() { - auto direction = _footpathConstructDirection; - footpathLoc.x = gFootpathConstructFromPosition.x + CoordsDirectionDelta[direction].x; - footpathLoc.y = gFootpathConstructFromPosition.y + CoordsDirectionDelta[direction].y; - footpathLoc.z = gFootpathConstructFromPosition.z; - if (type != nullptr) + if (!FootpathSelectDefault()) { - *type = gFootpathSelection.GetSelectedSurface(); + // No path objects to select from, don't open window + return nullptr; } - *slope = TILE_ELEMENT_SLOPE_FLAT; - if (gFootpathConstructSlope != 0) + + return WindowFocusOrCreate(WindowClass::Footpath, WW_WINDOW, WH_WINDOW, 0); + } + + void WindowFootpathResetSelectedPath() + { + gFootpathSelection = {}; + } + + void WindowFootpathKeyboardShortcutTurnLeft() + { + WindowBase* w = WindowFindByClass(WindowClass::Footpath); + if (w != nullptr) { - *slope = _footpathConstructDirection | TILE_ELEMENT_SLOPE_S_CORNER_UP; - if (gFootpathConstructSlope != 2) + auto* footpathWindow = static_cast(w); + if (footpathWindow != nullptr) { - footpathLoc.z -= PATH_HEIGHT_STEP; - *slope ^= TILE_ELEMENT_SLOPE_E_CORNER_UP; + footpathWindow->KeyboardShortcutTurnLeft(); } } } - PathConstructFlags FootpathCreateConstructFlags(ObjectEntryIndex& type) + void WindowFootpathKeyboardShortcutTurnRight() { - PathConstructFlags pathConstructFlags = 0; - if (gFootpathSelection.IsQueueSelected) - pathConstructFlags |= PathConstructFlag::IsQueue; - if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL) + WindowBase* w = WindowFindByClass(WindowClass::Footpath); + if (w != nullptr) { - pathConstructFlags |= PathConstructFlag::IsLegacyPathObject; - type = gFootpathSelection.LegacyPath; - } - return pathConstructFlags; - } - -#pragma region Keyboard Shortcuts Events -public: - void KeyboardShortcutTurnLeft() - { - if (IsWidgetDisabled(WIDX_DIRECTION_NW) || IsWidgetDisabled(WIDX_DIRECTION_NE) || IsWidgetDisabled(WIDX_DIRECTION_SW) - || IsWidgetDisabled(WIDX_DIRECTION_SE) || _footpathConstructionMode != 2) - { - return; - } - int32_t currentRotation = GetCurrentRotation(); - int32_t turnedRotation = _footpathConstructDirection - currentRotation + (currentRotation % 2 == 1 ? 1 : -1); - WindowFootpathMousedownDirection(turnedRotation); - } - - void KeyboardShortcutTurnRight() - { - if (IsWidgetDisabled(WIDX_DIRECTION_NW) || IsWidgetDisabled(WIDX_DIRECTION_NE) || IsWidgetDisabled(WIDX_DIRECTION_SW) - || IsWidgetDisabled(WIDX_DIRECTION_SE) || _footpathConstructionMode != 2) - { - return; - } - int32_t currentRotation = GetCurrentRotation(); - int32_t turnedRotation = _footpathConstructDirection - currentRotation + (currentRotation % 2 == 1 ? -1 : 1); - WindowFootpathMousedownDirection(turnedRotation); - } - - void KeyboardShortcutShortcutSlopeDown() - { - if (IsWidgetDisabled(WIDX_SLOPEDOWN) || IsWidgetDisabled(WIDX_LEVEL) || IsWidgetDisabled(WIDX_SLOPEUP) - || widgets[WIDX_LEVEL].type == WindowWidgetType::Empty) - { - return; - } - - switch (gFootpathConstructSlope) - { - case 0: - OnMouseDown(WIDX_SLOPEDOWN); - break; - case 2: - OnMouseDown(WIDX_LEVEL); - break; - default: - case 6: - return; + auto* footpathWindow = static_cast(w); + if (footpathWindow != nullptr) + { + footpathWindow->KeyboardShortcutTurnRight(); + } } } - void KeyboardShortcutSlopeUp() + void WindowFootpathKeyboardShortcutSlopeDown() { - if (IsWidgetDisabled(WIDX_SLOPEDOWN) || IsWidgetDisabled(WIDX_LEVEL) || IsWidgetDisabled(WIDX_SLOPEUP) - || widgets[WIDX_LEVEL].type == WindowWidgetType::Empty) + WindowBase* w = WindowFindByClass(WindowClass::Footpath); + if (w != nullptr) { - return; - } - - switch (gFootpathConstructSlope) - { - case 6: - OnMouseDown(WIDX_LEVEL); - break; - case 0: - OnMouseDown(WIDX_SLOPEUP); - break; - default: - case 2: - return; + auto* footpathWindow = static_cast(w); + if (footpathWindow != nullptr) + { + footpathWindow->KeyboardShortcutShortcutSlopeDown(); + } } } - void KeyboardShortcutSlopeLevel() + void WindowFootpathKeyboardShortcutSlopeUp() { - if (IsWidgetDisabled(WIDX_SLOPEDOWN) || IsWidgetDisabled(WIDX_LEVEL) || IsWidgetDisabled(WIDX_SLOPEUP) - || widgets[WIDX_LEVEL].type == WindowWidgetType::Empty || gFootpathConstructSlope == 0) + WindowBase* w = WindowFindByClass(WindowClass::Footpath); + if (w != nullptr) { - return; - } - - OnMouseDown(WIDX_LEVEL); - } - - void KeyboardShortcutDemolishCurrent() - { - if (IsWidgetDisabled(WIDX_REMOVE) || widgets[WIDX_REMOVE].type == WindowWidgetType::Empty - || (!GetGameState().Cheats.BuildInPauseMode && GameIsPaused())) - { - return; - } - - WindowFootpathRemove(); - } - - void KeyboardShortcutBuildCurrent() - { - if (IsWidgetDisabled(WIDX_CONSTRUCT) || widgets[WIDX_CONSTRUCT].type == WindowWidgetType::Empty) - { - return; - } - - OnMouseUp(WIDX_CONSTRUCT); - } - - void OnResize() override - { - ResizeFrame(); - } - -#pragma endregion -}; - -/** - * - * rct2: 0x006A7C43 - */ -WindowBase* WindowFootpathOpen() -{ - if (!FootpathSelectDefault()) - { - // No path objects to select from, don't open window - return nullptr; - } - - return WindowFocusOrCreate(WindowClass::Footpath, WW_WINDOW, WH_WINDOW, 0); -} - -void WindowFootpathResetSelectedPath() -{ - gFootpathSelection = {}; -} - -void WindowFootpathKeyboardShortcutTurnLeft() -{ - WindowBase* w = WindowFindByClass(WindowClass::Footpath); - if (w != nullptr) - { - auto* footpathWindow = static_cast(w); - if (footpathWindow != nullptr) - { - footpathWindow->KeyboardShortcutTurnLeft(); + auto* footpathWindow = static_cast(w); + if (footpathWindow != nullptr) + { + footpathWindow->KeyboardShortcutSlopeUp(); + } } } -} -void WindowFootpathKeyboardShortcutTurnRight() -{ - WindowBase* w = WindowFindByClass(WindowClass::Footpath); - if (w != nullptr) + void WindowFootpathKeyboardShortcutDemolishCurrent() { - auto* footpathWindow = static_cast(w); - if (footpathWindow != nullptr) + WindowBase* w = WindowFindByClass(WindowClass::Footpath); + if (w != nullptr) { - footpathWindow->KeyboardShortcutTurnRight(); + auto* footpathWindow = static_cast(w); + if (footpathWindow != nullptr) + { + footpathWindow->KeyboardShortcutDemolishCurrent(); + } } } -} -void WindowFootpathKeyboardShortcutSlopeDown() -{ - WindowBase* w = WindowFindByClass(WindowClass::Footpath); - if (w != nullptr) + void WindowFootpathKeyboardShortcutBuildCurrent() { - auto* footpathWindow = static_cast(w); - if (footpathWindow != nullptr) + WindowBase* w = WindowFindByClass(WindowClass::Footpath); + if (w != nullptr) { - footpathWindow->KeyboardShortcutShortcutSlopeDown(); + auto* footpathWindow = static_cast(w); + if (footpathWindow != nullptr) + { + footpathWindow->KeyboardShortcutBuildCurrent(); + } } } -} - -void WindowFootpathKeyboardShortcutSlopeUp() -{ - WindowBase* w = WindowFindByClass(WindowClass::Footpath); - if (w != nullptr) - { - auto* footpathWindow = static_cast(w); - if (footpathWindow != nullptr) - { - footpathWindow->KeyboardShortcutSlopeUp(); - } - } -} - -void WindowFootpathKeyboardShortcutDemolishCurrent() -{ - WindowBase* w = WindowFindByClass(WindowClass::Footpath); - if (w != nullptr) - { - auto* footpathWindow = static_cast(w); - if (footpathWindow != nullptr) - { - footpathWindow->KeyboardShortcutDemolishCurrent(); - } - } -} - -void WindowFootpathKeyboardShortcutBuildCurrent() -{ - WindowBase* w = WindowFindByClass(WindowClass::Footpath); - if (w != nullptr) - { - auto* footpathWindow = static_cast(w); - if (footpathWindow != nullptr) - { - footpathWindow->KeyboardShortcutBuildCurrent(); - } - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/GameBottomToolbar.cpp b/src/openrct2-ui/windows/GameBottomToolbar.cpp index 42453791d0..e78c49cfe3 100644 --- a/src/openrct2-ui/windows/GameBottomToolbar.cpp +++ b/src/openrct2-ui/windows/GameBottomToolbar.cpp @@ -29,9 +29,9 @@ #include #include -using namespace OpenRCT2; - -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowGameBottomToolbarWidgetIdx { WIDX_LEFT_OUTSET, @@ -68,630 +68,631 @@ static Widget window_game_bottom_toolbar_widgets[] = MakeWidget({500, 2}, {138, 12}, WindowWidgetType::FlatBtn, WindowColour::Primary ), // Date kWidgetsEnd, }; + // clang-format on -uint8_t gToolbarDirtyFlags; + uint8_t gToolbarDirtyFlags; -class GameBottomToolbar final : public Window -{ -private: - - void DrawLeftPanel(DrawPixelInfo &dpi) + class GameBottomToolbar final : public Window { - const auto topLeft = windowPos - + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].left + 1, - window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].top + 1 }; - const auto bottomRight = windowPos - + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].right - 1, - window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].bottom - 1 }; - // Draw green inset rectangle on panel - GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); - - // Figure out how much line height we have to work with. - uint32_t line_height = FontGetLineHeight(FontStyle::Medium); - - auto& gameState = GetGameState(); - - // Draw money - if (!(gameState.ParkFlags & PARK_FLAGS_NO_MONEY)) + private: + void DrawLeftPanel(DrawPixelInfo& dpi) { - Widget widget = window_game_bottom_toolbar_widgets[WIDX_MONEY]; - auto screenCoords = ScreenCoordsXY{ windowPos.x + widget.midX(), - windowPos.y + widget.midY() - (line_height == 10 ? 5 : 6) }; + const auto topLeft = windowPos + + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].left + 1, + window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].top + 1 }; + const auto bottomRight = windowPos + + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].right - 1, + window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET].bottom - 1 }; + // Draw green inset rectangle on panel + GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); + + // Figure out how much line height we have to work with. + uint32_t line_height = FontGetLineHeight(FontStyle::Medium); + + auto& gameState = GetGameState(); + + // Draw money + if (!(gameState.ParkFlags & PARK_FLAGS_NO_MONEY)) + { + Widget widget = window_game_bottom_toolbar_widgets[WIDX_MONEY]; + auto screenCoords = ScreenCoordsXY{ windowPos.x + widget.midX(), + windowPos.y + widget.midY() - (line_height == 10 ? 5 : 6) }; + + colour_t colour + = (gHoverWidget.window_classification == WindowClass::BottomToolbar + && gHoverWidget.widget_index == WIDX_MONEY + ? COLOUR_WHITE + : NOT_TRANSLUCENT(colours[0])); + StringId stringId = gameState.Cash < 0 ? STR_BOTTOM_TOOLBAR_CASH_NEGATIVE : STR_BOTTOM_TOOLBAR_CASH; + auto ft = Formatter(); + ft.Add(gameState.Cash); + DrawTextBasic(dpi, screenCoords, stringId, ft, { colour, TextAlignment::CENTRE }); + } + + static constexpr StringId _guestCountFormats[] = { + STR_BOTTOM_TOOLBAR_NUM_GUESTS_STABLE, + STR_BOTTOM_TOOLBAR_NUM_GUESTS_DECREASE, + STR_BOTTOM_TOOLBAR_NUM_GUESTS_INCREASE, + }; + + static constexpr StringId _guestCountFormatsSingular[] = { + STR_BOTTOM_TOOLBAR_NUM_GUESTS_STABLE_SINGULAR, + STR_BOTTOM_TOOLBAR_NUM_GUESTS_DECREASE_SINGULAR, + STR_BOTTOM_TOOLBAR_NUM_GUESTS_INCREASE_SINGULAR, + }; + + // Draw guests + { + Widget widget = window_game_bottom_toolbar_widgets[WIDX_GUESTS]; + auto screenCoords = ScreenCoordsXY{ windowPos.x + widget.midX(), windowPos.y + widget.midY() - 6 }; + + StringId stringId = gameState.NumGuestsInPark == 1 ? _guestCountFormatsSingular[gameState.GuestChangeModifier] + : _guestCountFormats[gameState.GuestChangeModifier]; + colour_t colour + = (gHoverWidget.window_classification == WindowClass::BottomToolbar + && gHoverWidget.widget_index == WIDX_GUESTS + ? COLOUR_WHITE + : NOT_TRANSLUCENT(colours[0])); + auto ft = Formatter(); + ft.Add(gameState.NumGuestsInPark); + DrawTextBasic(dpi, screenCoords, stringId, ft, { colour, TextAlignment::CENTRE }); + } + + // Draw park rating + { + Widget widget = window_game_bottom_toolbar_widgets[WIDX_PARK_RATING]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left + 11, widget.midY() - 5 }; + + DrawParkRating(dpi, colours[3], screenCoords, std::max(10, ((gameState.ParkRating / 4) * 263) / 256)); + } + } + + void DrawParkRating(DrawPixelInfo& dpi, int32_t colour, const ScreenCoordsXY& coords, uint8_t factor) + { + int16_t bar_width = (factor * 114) / 255; + GfxFillRectInset( + dpi, { coords + ScreenCoordsXY{ 1, 1 }, coords + ScreenCoordsXY{ 114, 9 } }, colours[1], INSET_RECT_F_30); + if (!(colour & kBarBlink) || GameIsPaused() || (gCurrentRealTimeTicks & 8)) + { + if (bar_width > 2) + { + GfxFillRectInset( + dpi, { coords + ScreenCoordsXY{ 2, 2 }, coords + ScreenCoordsXY{ bar_width - 1, 8 } }, colour, 0); + } + } + + // Draw thumbs on the sides + GfxDrawSprite(dpi, ImageId(SPR_RATING_LOW), coords - ScreenCoordsXY{ 14, 0 }); + GfxDrawSprite(dpi, ImageId(SPR_RATING_HIGH), coords + ScreenCoordsXY{ 114, 0 }); + } + + void DrawRightPanel(DrawPixelInfo& dpi) + { + const auto topLeft = windowPos + + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left + 1, + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].top + 1 }; + const auto bottomRight = windowPos + + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right - 1, + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].bottom - 1 }; + // Draw green inset rectangle on panel + GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); + + auto screenCoords = ScreenCoordsXY{ (window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left + + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right) + / 2 + + windowPos.x, + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].top + windowPos.y + 2 }; + + // Date + auto& date = GetDate(); + int32_t year = date.GetYear() + 1; + int32_t month = date.GetMonth(); + int32_t day = date.GetDay(); colour_t colour - = (gHoverWidget.window_classification == WindowClass::BottomToolbar && gHoverWidget.widget_index == WIDX_MONEY + = (gHoverWidget.window_classification == WindowClass::BottomToolbar && gHoverWidget.widget_index == WIDX_DATE ? COLOUR_WHITE : NOT_TRANSLUCENT(colours[0])); - StringId stringId = gameState.Cash < 0 ? STR_BOTTOM_TOOLBAR_CASH_NEGATIVE : STR_BOTTOM_TOOLBAR_CASH; + StringId stringId = DateFormatStringFormatIds[gConfigGeneral.DateFormat]; auto ft = Formatter(); - ft.Add(gameState.Cash); + ft.Add(DateDayNames[day]); + ft.Add(month); + ft.Add(year); DrawTextBasic(dpi, screenCoords, stringId, ft, { colour, TextAlignment::CENTRE }); - } - static constexpr StringId _guestCountFormats[] = { - STR_BOTTOM_TOOLBAR_NUM_GUESTS_STABLE, - STR_BOTTOM_TOOLBAR_NUM_GUESTS_DECREASE, - STR_BOTTOM_TOOLBAR_NUM_GUESTS_INCREASE, - }; + // Figure out how much line height we have to work with. + uint32_t line_height = FontGetLineHeight(FontStyle::Medium); - static constexpr StringId _guestCountFormatsSingular[] = { - STR_BOTTOM_TOOLBAR_NUM_GUESTS_STABLE_SINGULAR, - STR_BOTTOM_TOOLBAR_NUM_GUESTS_DECREASE_SINGULAR, - STR_BOTTOM_TOOLBAR_NUM_GUESTS_INCREASE_SINGULAR, - }; + // Temperature + screenCoords = { windowPos.x + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left + 15, + static_cast(screenCoords.y + line_height + 1) }; - // Draw guests - { - Widget widget = window_game_bottom_toolbar_widgets[WIDX_GUESTS]; - auto screenCoords = ScreenCoordsXY{ windowPos.x + widget.midX(), windowPos.y + widget.midY() - 6 }; - - StringId stringId = gameState.NumGuestsInPark == 1 ? _guestCountFormatsSingular[gameState.GuestChangeModifier] - : _guestCountFormats[gameState.GuestChangeModifier]; - colour_t colour - = (gHoverWidget.window_classification == WindowClass::BottomToolbar && gHoverWidget.widget_index == WIDX_GUESTS - ? COLOUR_WHITE - : NOT_TRANSLUCENT(colours[0])); - auto ft = Formatter(); - ft.Add(gameState.NumGuestsInPark); - DrawTextBasic(dpi, screenCoords, stringId, ft, { colour, TextAlignment::CENTRE }); - } - - // Draw park rating - { - Widget widget = window_game_bottom_toolbar_widgets[WIDX_PARK_RATING]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left + 11, widget.midY() - 5 }; - - DrawParkRating( - dpi, colours[3], screenCoords, std::max(10, ((gameState.ParkRating / 4) * 263) / 256)); - } - } - - void DrawParkRating(DrawPixelInfo &dpi, int32_t colour, const ScreenCoordsXY& coords, uint8_t factor) - { - int16_t bar_width = (factor * 114) / 255; - GfxFillRectInset( - dpi, { coords + ScreenCoordsXY{ 1, 1 }, coords + ScreenCoordsXY{ 114, 9 } }, colours[1], INSET_RECT_F_30); - if (!(colour & kBarBlink) || GameIsPaused() || (gCurrentRealTimeTicks & 8)) - { - if (bar_width > 2) + int32_t temperature = OpenRCT2::GetGameState().ClimateCurrent.Temperature; + StringId format = STR_CELSIUS_VALUE; + if (gConfigGeneral.TemperatureFormat == TemperatureUnit::Fahrenheit) { - GfxFillRectInset(dpi, { coords + ScreenCoordsXY{ 2, 2 }, coords + ScreenCoordsXY{ bar_width - 1, 8 } }, colour, 0); + temperature = ClimateCelsiusToFahrenheit(temperature); + format = STR_FAHRENHEIT_VALUE; + } + ft = Formatter(); + ft.Add(temperature); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 0, 6 }, format, ft); + screenCoords.x += 30; + + // Current weather + auto currentWeatherSpriteId = ClimateGetWeatherSpriteId(OpenRCT2::GetGameState().ClimateCurrent); + GfxDrawSprite(dpi, ImageId(currentWeatherSpriteId), screenCoords); + + // Next weather + auto nextWeatherSpriteId = ClimateGetWeatherSpriteId(GetGameState().ClimateNext); + if (currentWeatherSpriteId != nextWeatherSpriteId) + { + if (GetGameState().ClimateUpdateTimer < 960) + { + GfxDrawSprite(dpi, ImageId(SPR_NEXT_WEATHER), screenCoords + ScreenCoordsXY{ 27, 5 }); + GfxDrawSprite(dpi, ImageId(nextWeatherSpriteId), screenCoords + ScreenCoordsXY{ 40, 0 }); + } } } - // Draw thumbs on the sides - GfxDrawSprite(dpi, ImageId(SPR_RATING_LOW), coords - ScreenCoordsXY{ 14, 0 }); - GfxDrawSprite(dpi, ImageId(SPR_RATING_HIGH), coords + ScreenCoordsXY{ 114, 0 }); - } - - void DrawRightPanel(DrawPixelInfo &dpi) - { - const auto topLeft = windowPos - + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left + 1, - window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].top + 1 }; - const auto bottomRight = windowPos - + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right - 1, - window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].bottom - 1 }; - // Draw green inset rectangle on panel - GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_30); - - auto screenCoords = ScreenCoordsXY{ (window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left - + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right) - / 2 - + windowPos.x, - window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].top + windowPos.y + 2 }; - - // Date - auto& date = GetDate(); - int32_t year = date.GetYear() + 1; - int32_t month = date.GetMonth(); - int32_t day = date.GetDay(); - - colour_t colour - = (gHoverWidget.window_classification == WindowClass::BottomToolbar && gHoverWidget.widget_index == WIDX_DATE - ? COLOUR_WHITE - : NOT_TRANSLUCENT(colours[0])); - StringId stringId = DateFormatStringFormatIds[gConfigGeneral.DateFormat]; - auto ft = Formatter(); - ft.Add(DateDayNames[day]); - ft.Add(month); - ft.Add(year); - DrawTextBasic(dpi, screenCoords, stringId, ft, { colour, TextAlignment::CENTRE }); - - // Figure out how much line height we have to work with. - uint32_t line_height = FontGetLineHeight(FontStyle::Medium); - - // Temperature - screenCoords = { windowPos.x + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left + 15, - static_cast(screenCoords.y + line_height + 1) }; - - int32_t temperature = OpenRCT2::GetGameState().ClimateCurrent.Temperature; - StringId format = STR_CELSIUS_VALUE; - if (gConfigGeneral.TemperatureFormat == TemperatureUnit::Fahrenheit) + void DrawNewsItem(DrawPixelInfo& dpi) { - temperature = ClimateCelsiusToFahrenheit(temperature); - format = STR_FAHRENHEIT_VALUE; - } - ft = Formatter(); - ft.Add(temperature); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 0, 6 }, format, ft); - screenCoords.x += 30; + auto* middleOutsetWidget = &window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET]; + auto* newsItem = News::GetItem(0); - // Current weather - auto currentWeatherSpriteId = ClimateGetWeatherSpriteId(OpenRCT2::GetGameState().ClimateCurrent); - GfxDrawSprite(dpi, ImageId(currentWeatherSpriteId), screenCoords); + // Current news item + GfxFillRectInset( + dpi, - // Next weather - auto nextWeatherSpriteId = ClimateGetWeatherSpriteId(GetGameState().ClimateNext); - if (currentWeatherSpriteId != nextWeatherSpriteId) - { - if (GetGameState().ClimateUpdateTimer < 960) + { windowPos + ScreenCoordsXY{ middleOutsetWidget->left + 1, middleOutsetWidget->top + 1 }, + windowPos + ScreenCoordsXY{ middleOutsetWidget->right - 1, middleOutsetWidget->bottom - 1 } }, + colours[2], INSET_RECT_F_30); + + // Text + auto screenCoords = windowPos + ScreenCoordsXY{ middleOutsetWidget->midX(), middleOutsetWidget->top + 11 }; + int32_t itemWidth = middleOutsetWidget->width() - 62; + DrawNewsTicker( + dpi, screenCoords, itemWidth, COLOUR_BRIGHT_GREEN, STR_BOTTOM_TOOLBAR_NEWS_TEXT, newsItem->Text, + newsItem->Ticks); + + screenCoords = windowPos + + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].left, + window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].top }; + switch (newsItem->Type) { - GfxDrawSprite(dpi, ImageId(SPR_NEXT_WEATHER), screenCoords + ScreenCoordsXY{ 27, 5 }); - GfxDrawSprite(dpi, ImageId(nextWeatherSpriteId), screenCoords + ScreenCoordsXY{ 40, 0 }); - } - } - } - - void DrawNewsItem(DrawPixelInfo &dpi) - { - auto* middleOutsetWidget = &window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET]; - auto* newsItem = News::GetItem(0); - - // Current news item - GfxFillRectInset( - dpi, - - { windowPos + ScreenCoordsXY{ middleOutsetWidget->left + 1, middleOutsetWidget->top + 1 }, - windowPos + ScreenCoordsXY{ middleOutsetWidget->right - 1, middleOutsetWidget->bottom - 1 } }, - colours[2], INSET_RECT_F_30); - - // Text - auto screenCoords = windowPos + ScreenCoordsXY{ middleOutsetWidget->midX(), middleOutsetWidget->top + 11 }; - int32_t itemWidth = middleOutsetWidget->width() - 62; - DrawNewsTicker( - dpi, screenCoords, itemWidth, COLOUR_BRIGHT_GREEN, STR_BOTTOM_TOOLBAR_NEWS_TEXT, newsItem->Text, newsItem->Ticks); - - screenCoords = windowPos - + ScreenCoordsXY{ window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].left, - window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].top }; - switch (newsItem->Type) - { - case News::ItemType::Ride: - GfxDrawSprite(dpi, ImageId(SPR_RIDE), screenCoords); - break; - case News::ItemType::PeepOnRide: - case News::ItemType::Peep: - { - if (newsItem->HasButton()) + case News::ItemType::Ride: + GfxDrawSprite(dpi, ImageId(SPR_RIDE), screenCoords); break; - - DrawPixelInfo cliped_dpi; - if (!ClipDrawPixelInfo(cliped_dpi, dpi, screenCoords + ScreenCoordsXY{ 1, 1 }, 22, 22)) + case News::ItemType::PeepOnRide: + case News::ItemType::Peep: { - break; - } - - auto peep = TryGetEntity(EntityId::FromUnderlying(newsItem->Assoc)); - if (peep == nullptr) - return; - - auto clipCoords = ScreenCoordsXY{ 10, 19 }; - auto* staff = peep->As(); - if (staff != nullptr && staff->AssignedStaffType == StaffType::Entertainer) - { - clipCoords.y += 3; - } - - uint32_t image_id_base = GetPeepAnimation(peep->SpriteType).base_image; - image_id_base += frame_no & 0xFFFFFFFC; - image_id_base++; - - auto image_id = ImageId(image_id_base, peep->TshirtColour, peep->TrousersColour); - GfxDrawSprite(cliped_dpi, image_id, clipCoords); - - auto* guest = peep->As(); - if (guest != nullptr) - { - if (image_id_base >= 0x2A1D && image_id_base < 0x2A3D) - { - GfxDrawSprite(cliped_dpi, ImageId(image_id_base + 32, guest->BalloonColour), clipCoords); - } - else if (image_id_base >= 0x2BBD && image_id_base < 0x2BDD) - { - GfxDrawSprite(cliped_dpi, ImageId(image_id_base + 32, guest->UmbrellaColour), clipCoords); - } - else if (image_id_base >= 0x29DD && image_id_base < 0x29FD) - { - GfxDrawSprite(cliped_dpi, ImageId(image_id_base + 32, guest->HatColour), clipCoords); - } - } - break; - } - case News::ItemType::Money: - case News::ItemType::Campaign: - GfxDrawSprite(dpi, ImageId(SPR_FINANCE), screenCoords); - break; - case News::ItemType::Research: - GfxDrawSprite(dpi, ImageId(newsItem->Assoc < 0x10000 ? SPR_NEW_SCENERY : SPR_NEW_RIDE), screenCoords); - break; - case News::ItemType::Peeps: - GfxDrawSprite(dpi, ImageId(SPR_GUESTS), screenCoords); - break; - case News::ItemType::Award: - GfxDrawSprite(dpi, ImageId(SPR_AWARD), screenCoords); - break; - case News::ItemType::Graph: - GfxDrawSprite(dpi, ImageId(SPR_GRAPH), screenCoords); - break; - case News::ItemType::Null: - case News::ItemType::Blank: - case News::ItemType::Count: - break; - } - } - - void DrawMiddlePanel(DrawPixelInfo &dpi) - { - Widget* middleOutsetWidget = &window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET]; - - GfxFillRectInset( - dpi, - { windowPos + ScreenCoordsXY{ middleOutsetWidget->left + 1, middleOutsetWidget->top + 1 }, - windowPos + ScreenCoordsXY{ middleOutsetWidget->right - 1, middleOutsetWidget->bottom - 1 } }, - colours[1], INSET_RECT_F_30); - - // Figure out how much line height we have to work with. - uint32_t line_height = FontGetLineHeight(FontStyle::Medium); - - ScreenCoordsXY middleWidgetCoords( - windowPos.x + middleOutsetWidget->midX(), windowPos.y + middleOutsetWidget->top + line_height + 1); - int32_t panelWidth = middleOutsetWidget->width() - 62; - - // Check if there is a map tooltip to draw - StringId stringId; - auto ft = GetMapTooltip(); - std::memcpy(&stringId, ft.Data(), sizeof(StringId)); - if (stringId == STR_NONE) - { - DrawTextWrapped( - dpi, middleWidgetCoords, panelWidth, STR_TITLE_SEQUENCE_OPENRCT2, ft, { colours[0], TextAlignment::CENTRE }); - } - else - { - // Show tooltip in bottom toolbar - DrawTextWrapped(dpi, middleWidgetCoords, panelWidth, STR_STRINGID, ft, { colours[0], TextAlignment::CENTRE }); - } - } - - void InvalidateDirtyWidgets() - { - if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_MONEY) - { - gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_MONEY; - InvalidateWidget(WIDX_LEFT_INSET); - } - - if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_DATE) - { - gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_DATE; - InvalidateWidget(WIDX_RIGHT_INSET); - } - - if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_PEEP_COUNT) - { - gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_PEEP_COUNT; - InvalidateWidget(WIDX_LEFT_INSET); - } - - if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_CLIMATE) - { - gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_CLIMATE; - InvalidateWidget(WIDX_RIGHT_INSET); - } - - if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_PARK_RATING) - { - gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_PARK_RATING; - InvalidateWidget(WIDX_LEFT_INSET); - } - } - - -public: - GameBottomToolbar() - { - widgets = window_game_bottom_toolbar_widgets; - - frame_no = 0; - InitScrollWidgets(); - - // Reset the middle widget to not show by default. - // If it is required to be shown news_update will reshow it. - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].type = WindowWidgetType::Empty; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - - News::Item* newsItem; - - switch (widgetIndex) - { - case WIDX_LEFT_OUTSET: - case WIDX_MONEY: - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - ContextOpenWindow(WindowClass::Finances); - break; - case WIDX_GUESTS: - ContextOpenWindowView(WV_PARK_GUESTS); - break; - case WIDX_PARK_RATING: - ContextOpenWindowView(WV_PARK_RATING); - break; - case WIDX_MIDDLE_INSET: - if (News::IsQueueEmpty()) - { - ContextOpenWindow(WindowClass::RecentNews); - } - else - { - News::CloseCurrentItem(); - } - break; - case WIDX_NEWS_SUBJECT: - newsItem = News::GetItem(0); - News::OpenSubject(newsItem->Type, newsItem->Assoc); - break; - case WIDX_NEWS_LOCATE: - if (News::IsQueueEmpty()) - break; - - { - newsItem = News::GetItem(0); - - auto subjectLoc = News::GetSubjectLocation(newsItem->Type, newsItem->Assoc); - - if (!subjectLoc.has_value()) + if (newsItem->HasButton()) break; - WindowBase* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) - WindowScrollToLocation(*mainWindow, subjectLoc.value()); + DrawPixelInfo cliped_dpi; + if (!ClipDrawPixelInfo(cliped_dpi, dpi, screenCoords + ScreenCoordsXY{ 1, 1 }, 22, 22)) + { + break; + } + + auto peep = TryGetEntity(EntityId::FromUnderlying(newsItem->Assoc)); + if (peep == nullptr) + return; + + auto clipCoords = ScreenCoordsXY{ 10, 19 }; + auto* staff = peep->As(); + if (staff != nullptr && staff->AssignedStaffType == StaffType::Entertainer) + { + clipCoords.y += 3; + } + + uint32_t image_id_base = GetPeepAnimation(peep->SpriteType).base_image; + image_id_base += frame_no & 0xFFFFFFFC; + image_id_base++; + + auto image_id = ImageId(image_id_base, peep->TshirtColour, peep->TrousersColour); + GfxDrawSprite(cliped_dpi, image_id, clipCoords); + + auto* guest = peep->As(); + if (guest != nullptr) + { + if (image_id_base >= 0x2A1D && image_id_base < 0x2A3D) + { + GfxDrawSprite(cliped_dpi, ImageId(image_id_base + 32, guest->BalloonColour), clipCoords); + } + else if (image_id_base >= 0x2BBD && image_id_base < 0x2BDD) + { + GfxDrawSprite(cliped_dpi, ImageId(image_id_base + 32, guest->UmbrellaColour), clipCoords); + } + else if (image_id_base >= 0x29DD && image_id_base < 0x29FD) + { + GfxDrawSprite(cliped_dpi, ImageId(image_id_base + 32, guest->HatColour), clipCoords); + } + } + break; } - break; - case WIDX_RIGHT_OUTSET: - case WIDX_DATE: - ContextOpenWindow(WindowClass::RecentNews); - break; - } - } - - OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override - { - const auto& gameState = GetGameState(); - auto ft = Formatter(); - - switch (widgetIndex) - { - case WIDX_MONEY: - ft.Add(gameState.CurrentProfit); - ft.Add(gameState.ParkValue); - break; - case WIDX_PARK_RATING: - ft.Add(gameState.ParkRating); - break; - } - return { fallback, ft }; - } - - void OnPrepareDraw() override - { - // Figure out how much line height we have to work with. - uint32_t line_height = FontGetLineHeight(FontStyle::Medium); - - // Reset dimensions as appropriate -- in case we're switching languages. - height = line_height * 2 + 12; - windowPos.y = ContextGetHeight() - height; - - // Change height of widgets in accordance with line height. - widgets[WIDX_LEFT_OUTSET].bottom = widgets[WIDX_MIDDLE_OUTSET].bottom = widgets[WIDX_RIGHT_OUTSET].bottom - = line_height * 3 + 3; - widgets[WIDX_LEFT_INSET].bottom = widgets[WIDX_MIDDLE_INSET].bottom = widgets[WIDX_RIGHT_INSET].bottom - = line_height * 3 + 1; - - // Reposition left widgets in accordance with line height... depending on whether there is money in play. - if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) - { - widgets[WIDX_MONEY].type = WindowWidgetType::Empty; - widgets[WIDX_GUESTS].top = 1; - widgets[WIDX_GUESTS].bottom = line_height + 7; - widgets[WIDX_PARK_RATING].top = line_height + 8; - widgets[WIDX_PARK_RATING].bottom = height - 1; - } - else - { - widgets[WIDX_MONEY].type = WindowWidgetType::FlatBtn; - widgets[WIDX_MONEY].bottom = widgets[WIDX_MONEY].top + line_height; - widgets[WIDX_GUESTS].top = widgets[WIDX_MONEY].bottom + 1; - widgets[WIDX_GUESTS].bottom = widgets[WIDX_GUESTS].top + line_height; - widgets[WIDX_PARK_RATING].top = widgets[WIDX_GUESTS].bottom - 1; - widgets[WIDX_PARK_RATING].bottom = height - 1; + case News::ItemType::Money: + case News::ItemType::Campaign: + GfxDrawSprite(dpi, ImageId(SPR_FINANCE), screenCoords); + break; + case News::ItemType::Research: + GfxDrawSprite(dpi, ImageId(newsItem->Assoc < 0x10000 ? SPR_NEW_SCENERY : SPR_NEW_RIDE), screenCoords); + break; + case News::ItemType::Peeps: + GfxDrawSprite(dpi, ImageId(SPR_GUESTS), screenCoords); + break; + case News::ItemType::Award: + GfxDrawSprite(dpi, ImageId(SPR_AWARD), screenCoords); + break; + case News::ItemType::Graph: + GfxDrawSprite(dpi, ImageId(SPR_GRAPH), screenCoords); + break; + case News::ItemType::Null: + case News::ItemType::Blank: + case News::ItemType::Count: + break; + } } - // Reposition right widgets in accordance with line height, too. - widgets[WIDX_DATE].bottom = line_height + 1; - - // Anchor the middle and right panel to the right - int32_t x = ContextGetWidth(); - width = x; - x--; - window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right = x; - x -= 2; - window_game_bottom_toolbar_widgets[WIDX_RIGHT_INSET].right = x; - x -= 137; - window_game_bottom_toolbar_widgets[WIDX_RIGHT_INSET].left = x; - x -= 2; - window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left = x; - x--; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].right = x; - x -= 2; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].right = x; - x -= 3; - window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].right = x; - x -= 23; - window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].left = x; - window_game_bottom_toolbar_widgets[WIDX_DATE].left = window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left + 2; - window_game_bottom_toolbar_widgets[WIDX_DATE].right = window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right - 2; - - window_game_bottom_toolbar_widgets[WIDX_LEFT_INSET].type = WindowWidgetType::Empty; - window_game_bottom_toolbar_widgets[WIDX_RIGHT_INSET].type = WindowWidgetType::Empty; - - if (News::IsQueueEmpty()) + void DrawMiddlePanel(DrawPixelInfo& dpi) { - if (!(ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR)) + Widget* middleOutsetWidget = &window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET]; + + GfxFillRectInset( + dpi, + { windowPos + ScreenCoordsXY{ middleOutsetWidget->left + 1, middleOutsetWidget->top + 1 }, + windowPos + ScreenCoordsXY{ middleOutsetWidget->right - 1, middleOutsetWidget->bottom - 1 } }, + colours[1], INSET_RECT_F_30); + + // Figure out how much line height we have to work with. + uint32_t line_height = FontGetLineHeight(FontStyle::Medium); + + ScreenCoordsXY middleWidgetCoords( + windowPos.x + middleOutsetWidget->midX(), windowPos.y + middleOutsetWidget->top + line_height + 1); + int32_t panelWidth = middleOutsetWidget->width() - 62; + + // Check if there is a map tooltip to draw + StringId stringId; + auto ft = GetMapTooltip(); + std::memcpy(&stringId, ft.Data(), sizeof(StringId)); + if (stringId == STR_NONE) { - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].type = WindowWidgetType::Empty; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].type = WindowWidgetType::Empty; - window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::Empty; - window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].type = WindowWidgetType::Empty; + DrawTextWrapped( + dpi, middleWidgetCoords, panelWidth, STR_TITLE_SEQUENCE_OPENRCT2, ft, + { colours[0], TextAlignment::CENTRE }); } else { + // Show tooltip in bottom toolbar + DrawTextWrapped(dpi, middleWidgetCoords, panelWidth, STR_STRINGID, ft, { colours[0], TextAlignment::CENTRE }); + } + } + + void InvalidateDirtyWidgets() + { + if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_MONEY) + { + gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_MONEY; + InvalidateWidget(WIDX_LEFT_INSET); + } + + if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_DATE) + { + gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_DATE; + InvalidateWidget(WIDX_RIGHT_INSET); + } + + if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_PEEP_COUNT) + { + gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_PEEP_COUNT; + InvalidateWidget(WIDX_LEFT_INSET); + } + + if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_CLIMATE) + { + gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_CLIMATE; + InvalidateWidget(WIDX_RIGHT_INSET); + } + + if (gToolbarDirtyFlags & BTM_TB_DIRTY_FLAG_PARK_RATING) + { + gToolbarDirtyFlags &= ~BTM_TB_DIRTY_FLAG_PARK_RATING; + InvalidateWidget(WIDX_LEFT_INSET); + } + } + + public: + GameBottomToolbar() + { + widgets = window_game_bottom_toolbar_widgets; + + frame_no = 0; + InitScrollWidgets(); + + // Reset the middle widget to not show by default. + // If it is required to be shown news_update will reshow it. + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].type = WindowWidgetType::Empty; + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + News::Item* newsItem; + + switch (widgetIndex) + { + case WIDX_LEFT_OUTSET: + case WIDX_MONEY: + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + ContextOpenWindow(WindowClass::Finances); + break; + case WIDX_GUESTS: + ContextOpenWindowView(WV_PARK_GUESTS); + break; + case WIDX_PARK_RATING: + ContextOpenWindowView(WV_PARK_RATING); + break; + case WIDX_MIDDLE_INSET: + if (News::IsQueueEmpty()) + { + ContextOpenWindow(WindowClass::RecentNews); + } + else + { + News::CloseCurrentItem(); + } + break; + case WIDX_NEWS_SUBJECT: + newsItem = News::GetItem(0); + News::OpenSubject(newsItem->Type, newsItem->Assoc); + break; + case WIDX_NEWS_LOCATE: + if (News::IsQueueEmpty()) + break; + + { + newsItem = News::GetItem(0); + + auto subjectLoc = News::GetSubjectLocation(newsItem->Type, newsItem->Assoc); + + if (!subjectLoc.has_value()) + break; + + WindowBase* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + WindowScrollToLocation(*mainWindow, subjectLoc.value()); + } + break; + case WIDX_RIGHT_OUTSET: + case WIDX_DATE: + ContextOpenWindow(WindowClass::RecentNews); + break; + } + } + + OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override + { + const auto& gameState = GetGameState(); + auto ft = Formatter(); + + switch (widgetIndex) + { + case WIDX_MONEY: + ft.Add(gameState.CurrentProfit); + ft.Add(gameState.ParkValue); + break; + case WIDX_PARK_RATING: + ft.Add(gameState.ParkRating); + break; + } + return { fallback, ft }; + } + + void OnPrepareDraw() override + { + // Figure out how much line height we have to work with. + uint32_t line_height = FontGetLineHeight(FontStyle::Medium); + + // Reset dimensions as appropriate -- in case we're switching languages. + height = line_height * 2 + 12; + windowPos.y = ContextGetHeight() - height; + + // Change height of widgets in accordance with line height. + widgets[WIDX_LEFT_OUTSET].bottom = widgets[WIDX_MIDDLE_OUTSET].bottom = widgets[WIDX_RIGHT_OUTSET].bottom + = line_height * 3 + 3; + widgets[WIDX_LEFT_INSET].bottom = widgets[WIDX_MIDDLE_INSET].bottom = widgets[WIDX_RIGHT_INSET].bottom = line_height + * 3 + + 1; + + // Reposition left widgets in accordance with line height... depending on whether there is money in play. + if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) + { + widgets[WIDX_MONEY].type = WindowWidgetType::Empty; + widgets[WIDX_GUESTS].top = 1; + widgets[WIDX_GUESTS].bottom = line_height + 7; + widgets[WIDX_PARK_RATING].top = line_height + 8; + widgets[WIDX_PARK_RATING].bottom = height - 1; + } + else + { + widgets[WIDX_MONEY].type = WindowWidgetType::FlatBtn; + widgets[WIDX_MONEY].bottom = widgets[WIDX_MONEY].top + line_height; + widgets[WIDX_GUESTS].top = widgets[WIDX_MONEY].bottom + 1; + widgets[WIDX_GUESTS].bottom = widgets[WIDX_GUESTS].top + line_height; + widgets[WIDX_PARK_RATING].top = widgets[WIDX_GUESTS].bottom - 1; + widgets[WIDX_PARK_RATING].bottom = height - 1; + } + + // Reposition right widgets in accordance with line height, too. + widgets[WIDX_DATE].bottom = line_height + 1; + + // Anchor the middle and right panel to the right + int32_t x = ContextGetWidth(); + width = x; + x--; + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right = x; + x -= 2; + window_game_bottom_toolbar_widgets[WIDX_RIGHT_INSET].right = x; + x -= 137; + window_game_bottom_toolbar_widgets[WIDX_RIGHT_INSET].left = x; + x -= 2; + window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left = x; + x--; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].right = x; + x -= 2; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].right = x; + x -= 3; + window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].right = x; + x -= 23; + window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].left = x; + window_game_bottom_toolbar_widgets[WIDX_DATE].left = window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].left + 2; + window_game_bottom_toolbar_widgets[WIDX_DATE].right = window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].right + - 2; + + window_game_bottom_toolbar_widgets[WIDX_LEFT_INSET].type = WindowWidgetType::Empty; + window_game_bottom_toolbar_widgets[WIDX_RIGHT_INSET].type = WindowWidgetType::Empty; + + if (News::IsQueueEmpty()) + { + if (!(ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR)) + { + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].type = WindowWidgetType::Empty; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].type = WindowWidgetType::Empty; + window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::Empty; + window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].type = WindowWidgetType::Empty; + } + else + { + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].type = WindowWidgetType::ImgBtn; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].type = WindowWidgetType::FlatBtn; + window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::Empty; + window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].type = WindowWidgetType::Empty; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].colour = 0; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].colour = 0; + } + } + else + { + News::Item* newsItem = News::GetItem(0); window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].type = WindowWidgetType::ImgBtn; window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].type = WindowWidgetType::FlatBtn; - window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::Empty; - window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].type = WindowWidgetType::Empty; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].colour = 0; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].colour = 0; + window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::FlatBtn; + window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].type = WindowWidgetType::FlatBtn; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].colour = 2; + window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].colour = 2; + disabled_widgets &= ~(1uLL << WIDX_NEWS_SUBJECT); + disabled_widgets &= ~(1uLL << WIDX_NEWS_LOCATE); + + // Find out if the news item is no longer valid + auto subjectLoc = News::GetSubjectLocation(newsItem->Type, newsItem->Assoc); + + if (!subjectLoc.has_value()) + disabled_widgets |= (1uLL << WIDX_NEWS_LOCATE); + + if (!(newsItem->TypeHasSubject())) + { + disabled_widgets |= (1uLL << WIDX_NEWS_SUBJECT); + window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::Empty; + } + + if (newsItem->HasButton()) + { + disabled_widgets |= (1uLL << WIDX_NEWS_SUBJECT); + disabled_widgets |= (1uLL << WIDX_NEWS_LOCATE); + } } } - else + + void OnDraw(DrawPixelInfo& dpi) override { - News::Item* newsItem = News::GetItem(0); - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].type = WindowWidgetType::ImgBtn; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].type = WindowWidgetType::FlatBtn; - window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::FlatBtn; - window_game_bottom_toolbar_widgets[WIDX_NEWS_LOCATE].type = WindowWidgetType::FlatBtn; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET].colour = 2; - window_game_bottom_toolbar_widgets[WIDX_MIDDLE_INSET].colour = 2; - disabled_widgets &= ~(1uLL << WIDX_NEWS_SUBJECT); - disabled_widgets &= ~(1uLL << WIDX_NEWS_LOCATE); + auto leftWidget = window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET]; + auto rightWidget = window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET]; + auto middleWidget = window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET]; - // Find out if the news item is no longer valid - auto subjectLoc = News::GetSubjectLocation(newsItem->Type, newsItem->Assoc); - - if (!subjectLoc.has_value()) - disabled_widgets |= (1uLL << WIDX_NEWS_LOCATE); - - if (!(newsItem->TypeHasSubject())) - { - disabled_widgets |= (1uLL << WIDX_NEWS_SUBJECT); - window_game_bottom_toolbar_widgets[WIDX_NEWS_SUBJECT].type = WindowWidgetType::Empty; - } - - if (newsItem->HasButton()) - { - disabled_widgets |= (1uLL << WIDX_NEWS_SUBJECT); - disabled_widgets |= (1uLL << WIDX_NEWS_LOCATE); - } - } - } - - void OnDraw(DrawPixelInfo &dpi) override - { - auto leftWidget = window_game_bottom_toolbar_widgets[WIDX_LEFT_OUTSET]; - auto rightWidget = window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET]; - auto middleWidget = window_game_bottom_toolbar_widgets[WIDX_MIDDLE_OUTSET]; - - // Draw panel grey backgrounds - auto leftTop = windowPos + ScreenCoordsXY{ leftWidget.left, leftWidget.top }; - auto rightBottom = windowPos + ScreenCoordsXY{ leftWidget.right, leftWidget.bottom }; - GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); - - leftTop = windowPos + ScreenCoordsXY{ rightWidget.left, rightWidget.top }; - rightBottom = windowPos + ScreenCoordsXY{ rightWidget.right, rightWidget.bottom }; - GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); - - if (ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR) - { - // Draw grey background - leftTop = windowPos + ScreenCoordsXY{ middleWidget.left, middleWidget.top }; - rightBottom = windowPos + ScreenCoordsXY{ middleWidget.right, middleWidget.bottom }; + // Draw panel grey backgrounds + auto leftTop = windowPos + ScreenCoordsXY{ leftWidget.left, leftWidget.top }; + auto rightBottom = windowPos + ScreenCoordsXY{ leftWidget.right, leftWidget.bottom }; GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); + + leftTop = windowPos + ScreenCoordsXY{ rightWidget.left, rightWidget.top }; + rightBottom = windowPos + ScreenCoordsXY{ rightWidget.right, rightWidget.bottom }; + GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); + + if (ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR) + { + // Draw grey background + leftTop = windowPos + ScreenCoordsXY{ middleWidget.left, middleWidget.top }; + rightBottom = windowPos + ScreenCoordsXY{ middleWidget.right, middleWidget.bottom }; + GfxFilterRect(dpi, { leftTop, rightBottom }, FilterPaletteID::Palette51); + } + + DrawWidgets(dpi); + + DrawLeftPanel(dpi); + DrawRightPanel(dpi); + + if (!News::IsQueueEmpty()) + { + DrawNewsItem(dpi); + } + else if (ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR) + { + DrawMiddlePanel(dpi); + } } - DrawWidgets(dpi); - - DrawLeftPanel(dpi); - DrawRightPanel(dpi); - - if (!News::IsQueueEmpty()) + void OnUpdate() override { - DrawNewsItem(dpi); + frame_no++; + if (frame_no >= 24) + frame_no = 0; + + InvalidateDirtyWidgets(); } - else if (ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR) + + CursorID OnCursor(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords, CursorID cursorId) override { - DrawMiddlePanel(dpi); + switch (widgetIndex) + { + case WIDX_MONEY: + case WIDX_GUESTS: + case WIDX_PARK_RATING: + case WIDX_DATE: + gTooltipCloseTimeout = gCurrentRealTimeTicks + 2000; + break; + } + return cursorId; } - } - void OnUpdate() override - { - frame_no++; - if (frame_no >= 24) - frame_no = 0; - - InvalidateDirtyWidgets(); - } - - CursorID OnCursor(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords, CursorID cursorId) override - { - switch (widgetIndex) + void OnPeriodicUpdate() override { - case WIDX_MONEY: - case WIDX_GUESTS: - case WIDX_PARK_RATING: - case WIDX_DATE: - gTooltipCloseTimeout = gCurrentRealTimeTicks + 2000; - break; + InvalidateDirtyWidgets(); } - return cursorId; - } + }; - void OnPeriodicUpdate() override + /** + * Creates the main game bottom toolbar window. + */ + WindowBase* WindowGameBottomToolbarOpen() { - InvalidateDirtyWidgets(); + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + + // Figure out how much line height we have to work with. + uint32_t line_height = FontGetLineHeight(FontStyle::Medium); + uint32_t toolbar_height = line_height * 2 + 12; + + GameBottomToolbar* window = WindowCreate( + WindowClass::BottomToolbar, ScreenCoordsXY(0, screenHeight - toolbar_height), screenWidth, toolbar_height, + WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); + + return window; } -}; -/** - * Creates the main game bottom toolbar window. - */ -WindowBase* WindowGameBottomToolbarOpen() -{ - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - - // Figure out how much line height we have to work with. - uint32_t line_height = FontGetLineHeight(FontStyle::Medium); - uint32_t toolbar_height = line_height * 2 + 12; - - GameBottomToolbar* window = WindowCreate( - WindowClass::BottomToolbar, - ScreenCoordsXY(0, screenHeight - toolbar_height), - screenWidth, - toolbar_height, - WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); - - return window; -} - -void WindowGameBottomToolbarInvalidateNewsItem() -{ - if (gScreenFlags == SCREEN_FLAGS_PLAYING) + void WindowGameBottomToolbarInvalidateNewsItem() { - WidgetInvalidateByClass(WindowClass::BottomToolbar, WIDX_MIDDLE_OUTSET); + if (gScreenFlags == SCREEN_FLAGS_PLAYING) + { + WidgetInvalidateByClass(WindowClass::BottomToolbar, WIDX_MIDDLE_OUTSET); + } } -} - +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Guest.cpp b/src/openrct2-ui/windows/Guest.cpp index 35c4b58c63..8f2aaac172 100644 --- a/src/openrct2-ui/windows/Guest.cpp +++ b/src/openrct2-ui/windows/Guest.cpp @@ -36,52 +36,52 @@ #include #include -using namespace OpenRCT2; - -static constexpr StringId WINDOW_TITLE = STR_STRINGID; -static constexpr int32_t WH = 157; -static constexpr int32_t WW = 192; - -enum WindowGuestPage +namespace OpenRCT2::Ui::Windows { - WINDOW_GUEST_OVERVIEW, - WINDOW_GUEST_STATS, - WINDOW_GUEST_RIDES, - WINDOW_GUEST_FINANCE, - WINDOW_GUEST_THOUGHTS, - WINDOW_GUEST_INVENTORY, - WINDOW_GUEST_DEBUG, - WINDOW_GUEST_PAGE_COUNT, -}; + static constexpr StringId WINDOW_TITLE = STR_STRINGID; + static constexpr int32_t WH = 157; + static constexpr int32_t WW = 192; -enum WindowGuestWidgetIdx -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PAGE_BACKGROUND, - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_TAB_3, - WIDX_TAB_4, - WIDX_TAB_5, - WIDX_TAB_6, - WIDX_TAB_7, + enum WindowGuestPage + { + WINDOW_GUEST_OVERVIEW, + WINDOW_GUEST_STATS, + WINDOW_GUEST_RIDES, + WINDOW_GUEST_FINANCE, + WINDOW_GUEST_THOUGHTS, + WINDOW_GUEST_INVENTORY, + WINDOW_GUEST_DEBUG, + WINDOW_GUEST_PAGE_COUNT, + }; - WIDX_MARQUEE = 11, - WIDX_VIEWPORT, - WIDX_ACTION_LBL, - WIDX_PICKUP, - WIDX_RENAME, - WIDX_LOCATE, - WIDX_TRACK, + enum WindowGuestWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PAGE_BACKGROUND, + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_TAB_3, + WIDX_TAB_4, + WIDX_TAB_5, + WIDX_TAB_6, + WIDX_TAB_7, - WIDX_RIDE_SCROLL = 11 -}; + WIDX_MARQUEE = 11, + WIDX_VIEWPORT, + WIDX_ACTION_LBL, + WIDX_PICKUP, + WIDX_RENAME, + WIDX_LOCATE, + WIDX_TRACK, -validate_global_widx(WC_PEEP, WIDX_PICKUP); + WIDX_RIDE_SCROLL = 11 + }; -static constexpr int32_t TabWidth = 30; + validate_global_widx(WC_PEEP, WIDX_PICKUP); + + static constexpr int32_t TabWidth = 30; #define MAIN_GUEST_WIDGETS \ WINDOW_SHIM(WINDOW_TITLE, WW, WH), \ @@ -94,7 +94,7 @@ static constexpr int32_t TabWidth = 30; MakeTab({ 158, 17 }, STR_SHOW_GUEST_ITEMS_TIP), /* Tab 6 */ \ MakeTab({ 189, 17 }, STR_DEBUG_TIP) /* Tab 7 */ -// clang-format off + // clang-format off static Widget _guestWindowWidgetsOverview[] = { MAIN_GUEST_WIDGETS, MakeWidget({ 3, 45}, {164, 12}, WindowWidgetType::LabelCentred, WindowColour::Secondary ), // Label Thought marquee @@ -106,40 +106,40 @@ static Widget _guestWindowWidgetsOverview[] = { MakeWidget({167, 117}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_TRACK_PEEP), STR_TOGGLE_GUEST_TRACKING_TIP), // Track Button kWidgetsEnd, }; -// clang-format on + // clang-format on -static Widget _guestWindowWidgetsStats[] = { - MAIN_GUEST_WIDGETS, - kWidgetsEnd, -}; + static Widget _guestWindowWidgetsStats[] = { + MAIN_GUEST_WIDGETS, + kWidgetsEnd, + }; -static Widget _guestWindowWidgetsRides[] = { - MAIN_GUEST_WIDGETS, - MakeWidget({ 3, 57 }, { 186, 87 }, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_VERTICAL), - kWidgetsEnd, -}; + static Widget _guestWindowWidgetsRides[] = { + MAIN_GUEST_WIDGETS, + MakeWidget({ 3, 57 }, { 186, 87 }, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_VERTICAL), + kWidgetsEnd, + }; -static Widget _guestWindowWidgetsFinance[] = { - MAIN_GUEST_WIDGETS, - kWidgetsEnd, -}; + static Widget _guestWindowWidgetsFinance[] = { + MAIN_GUEST_WIDGETS, + kWidgetsEnd, + }; -static Widget _guestWindowWidgetsThoughts[] = { - MAIN_GUEST_WIDGETS, - kWidgetsEnd, -}; + static Widget _guestWindowWidgetsThoughts[] = { + MAIN_GUEST_WIDGETS, + kWidgetsEnd, + }; -static Widget _guestWindowWidgetsInventory[] = { - MAIN_GUEST_WIDGETS, - kWidgetsEnd, -}; + static Widget _guestWindowWidgetsInventory[] = { + MAIN_GUEST_WIDGETS, + kWidgetsEnd, + }; -static Widget _guestWindowWidgetsDebug[] = { - MAIN_GUEST_WIDGETS, - kWidgetsEnd, -}; + static Widget _guestWindowWidgetsDebug[] = { + MAIN_GUEST_WIDGETS, + kWidgetsEnd, + }; -// clang-format off + // clang-format off static constexpr std::array _guestWindowPageWidgets = { _guestWindowWidgetsOverview, _guestWindowWidgetsStats, @@ -150,1787 +150,1789 @@ static constexpr std::array _guestWindowPageWidgets = { _guestWindowWidgetsDebug, }; static_assert(_guestWindowPageWidgets.size() == WINDOW_GUEST_PAGE_COUNT); -// clang-format on + // clang-format on -static constexpr std::array _guestWindowPageSizes = { - std::array{ ScreenSize{ 192, 159 }, ScreenSize{ 500, 450 } }, // WINDOW_GUEST_OVERVIEW - std::array{ ScreenSize{ 192, 180 }, ScreenSize{ 192, 180 } }, // WINDOW_GUEST_STATS - std::array{ ScreenSize{ 192, 180 }, ScreenSize{ 500, 400 } }, // WINDOW_GUEST_RIDES - std::array{ ScreenSize{ 210, 148 }, ScreenSize{ 210, 148 } }, // WINDOW_GUEST_FINANCE - std::array{ ScreenSize{ 192, 159 }, ScreenSize{ 500, 450 } }, // WINDOW_GUEST_THOUGHTS - std::array{ ScreenSize{ 192, 159 }, ScreenSize{ 500, 450 } }, // WINDOW_GUEST_INVENTORY - std::array{ ScreenSize{ 192, 171 }, ScreenSize{ 192, 171 } }, // WINDOW_GUEST_DEBUG -}; -static_assert(_guestWindowPageSizes.size() == WINDOW_GUEST_PAGE_COUNT); + static constexpr std::array _guestWindowPageSizes = { + std::array{ ScreenSize{ 192, 159 }, ScreenSize{ 500, 450 } }, // WINDOW_GUEST_OVERVIEW + std::array{ ScreenSize{ 192, 180 }, ScreenSize{ 192, 180 } }, // WINDOW_GUEST_STATS + std::array{ ScreenSize{ 192, 180 }, ScreenSize{ 500, 400 } }, // WINDOW_GUEST_RIDES + std::array{ ScreenSize{ 210, 148 }, ScreenSize{ 210, 148 } }, // WINDOW_GUEST_FINANCE + std::array{ ScreenSize{ 192, 159 }, ScreenSize{ 500, 450 } }, // WINDOW_GUEST_THOUGHTS + std::array{ ScreenSize{ 192, 159 }, ScreenSize{ 500, 450 } }, // WINDOW_GUEST_INVENTORY + std::array{ ScreenSize{ 192, 171 }, ScreenSize{ 192, 171 } }, // WINDOW_GUEST_DEBUG + }; + static_assert(_guestWindowPageSizes.size() == WINDOW_GUEST_PAGE_COUNT); -class GuestWindow final : public Window -{ -private: - uint16_t _marqueePosition = 0; - uint16_t _beingWatchedTimer = 0; - uint16_t _guestAnimationFrame = 0; - int16_t _pickedPeepX = LOCATION_NULL; // entity->x gets set to 0x8000 on pickup, this is the old value - std::vector _riddenRides; - -public: - void OnOpen() override + class GuestWindow final : public Window { - widgets = _guestWindowWidgetsOverview; - page = WINDOW_GUEST_OVERVIEW; - frame_no = 0; - _marqueePosition = 0; - picked_peep_frame = 0; - min_width = width; - min_height = 157; - max_width = 500; - max_height = 450; - selected_list_item = -1; - } + private: + uint16_t _marqueePosition = 0; + uint16_t _beingWatchedTimer = 0; + uint16_t _guestAnimationFrame = 0; + int16_t _pickedPeepX = LOCATION_NULL; // entity->x gets set to 0x8000 on pickup, this is the old value + std::vector _riddenRides; - void Init(EntityId id) - { - number = id.ToUnderlying(); - page = -1; // Set Page to something invalid so that SetPage doesn't set audio on viewport - SetPage(WINDOW_GUEST_OVERVIEW); - } - - void OnClose() override - { - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + public: + void OnOpen() override { - if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) - ToolCancel(); - } - } - - void OnMouseUp(WidgetIndex widx) override - { - switch (widx) - { - 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: - SetPage(widx - WIDX_TAB_1); - return; + widgets = _guestWindowWidgetsOverview; + page = WINDOW_GUEST_OVERVIEW; + frame_no = 0; + _marqueePosition = 0; + picked_peep_frame = 0; + min_width = width; + min_height = 157; + max_width = 500; + max_height = 450; + selected_list_item = -1; } - switch (page) + void Init(EntityId id) { - case WINDOW_GUEST_OVERVIEW: - OnMouseUpOverview(widx); - break; + number = id.ToUnderlying(); + page = -1; // Set Page to something invalid so that SetPage doesn't set audio on viewport + SetPage(WINDOW_GUEST_OVERVIEW); } - } - void OnMouseDown(WidgetIndex widx) override - { - if (page == WINDOW_GUEST_OVERVIEW) - { - OnMouseDownOverview(widx); - } - } - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (page == WINDOW_GUEST_OVERVIEW) - { - OnDropdownOverview(widgetIndex, selectedIndex); - } - } - void OnResize() override - { - switch (page) - { - case WINDOW_GUEST_OVERVIEW: - OnResizeOverview(); - break; - case WINDOW_GUEST_STATS: - case WINDOW_GUEST_RIDES: - case WINDOW_GUEST_FINANCE: - case WINDOW_GUEST_THOUGHTS: - case WINDOW_GUEST_INVENTORY: - case WINDOW_GUEST_DEBUG: - OnResizeCommon(); - break; - } - } - void OnUpdate() override - { - switch (page) - { - case WINDOW_GUEST_OVERVIEW: - OnUpdateOverview(); - break; - case WINDOW_GUEST_STATS: - OnUpdateStats(); - break; - case WINDOW_GUEST_RIDES: - OnUpdateRides(); - break; - case WINDOW_GUEST_FINANCE: - OnUpdateFinance(); - break; - case WINDOW_GUEST_THOUGHTS: - OnUpdateThoughts(); - break; - case WINDOW_GUEST_INVENTORY: - OnUpdateInventory(); - break; - case WINDOW_GUEST_DEBUG: - OnUpdateDebug(); - break; - } - } - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (page == WINDOW_GUEST_OVERVIEW) - { - OnToolUpdateOverview(widgetIndex, screenCoords); - } - } - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (page == WINDOW_GUEST_OVERVIEW) - { - OnToolDownOverview(widgetIndex, screenCoords); - } - } - void OnToolAbort(WidgetIndex widgetIndex) override - { - if (page == WINDOW_GUEST_OVERVIEW) - { - OnToolAbortOverview(widgetIndex); - } - } - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (page == WINDOW_GUEST_OVERVIEW) - { - OnTextInputOverview(widgetIndex, text); - } - } - void OnViewportRotate() override - { - if (page == WINDOW_GUEST_OVERVIEW) - { - OnViewportRotateOverview(); - } - } - void OnDraw(DrawPixelInfo& dpi) override - { - switch (page) - { - case WINDOW_GUEST_OVERVIEW: - OnDrawOverview(dpi); - break; - case WINDOW_GUEST_STATS: - OnDrawStats(dpi); - break; - case WINDOW_GUEST_RIDES: - OnDrawRides(dpi); - break; - case WINDOW_GUEST_FINANCE: - OnDrawFinance(dpi); - break; - case WINDOW_GUEST_THOUGHTS: - OnDrawThoughts(dpi); - break; - case WINDOW_GUEST_INVENTORY: - OnDrawInventory(dpi); - break; - case WINDOW_GUEST_DEBUG: - OnDrawDebug(dpi); - break; - } - } - void OnPrepareDraw() override - { - OnPrepareDrawCommon(); - switch (page) - { - case WINDOW_GUEST_OVERVIEW: - OnPrepareDrawOverview(); - break; - case WINDOW_GUEST_RIDES: - OnPrepareDrawRides(); - break; - } - } - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - if (page == WINDOW_GUEST_RIDES) - { - return OnScrollGetSizeRides(scrollIndex); - } - return {}; - } - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (page == WINDOW_GUEST_RIDES) + void OnClose() override { - OnScrollMouseOverRides(scrollIndex, screenCoords); - } - } - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (page == WINDOW_GUEST_RIDES) - { - OnScrollMouseDownRides(scrollIndex, screenCoords); - } - } - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - if (page == WINDOW_GUEST_RIDES) - { - OnScrollDrawRides(scrollIndex, dpi); - } - } - -private: - Guest* GetGuest() - { - auto guest = GetEntity(EntityId::FromUnderlying(number)); - if (guest == nullptr) - { - Close(); - return nullptr; - } - return guest; - } - - void OnResizeCommon() - { - // Get page specific min and max size - int32_t minWidth = _guestWindowPageSizes[page][0].width; - int32_t minHeight = _guestWindowPageSizes[page][0].height; - int32_t maxWidth = _guestWindowPageSizes[page][1].width; - int32_t maxHeight = _guestWindowPageSizes[page][1].height; - - // Ensure min size is large enough for all tabs to fit - for (int32_t i = WIDX_TAB_1; i <= WIDX_TAB_7; i++) - { - if (!WidgetIsDisabled(*this, i)) + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) { - minWidth = std::max(minWidth, widgets[i].right + 3); + if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) + ToolCancel(); } } - maxWidth = std::max(minWidth, maxWidth); - WindowSetResize(*this, minWidth, minHeight, maxWidth, maxHeight); - } - - void OnPrepareDrawCommon() - { - if (_guestWindowPageWidgets[page] != widgets) + void OnMouseUp(WidgetIndex widx) override { + switch (widx) + { + 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: + SetPage(widx - WIDX_TAB_1); + return; + } + + switch (page) + { + case WINDOW_GUEST_OVERVIEW: + OnMouseUpOverview(widx); + break; + } + } + void OnMouseDown(WidgetIndex widx) override + { + if (page == WINDOW_GUEST_OVERVIEW) + { + OnMouseDownOverview(widx); + } + } + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + if (page == WINDOW_GUEST_OVERVIEW) + { + OnDropdownOverview(widgetIndex, selectedIndex); + } + } + void OnResize() override + { + switch (page) + { + case WINDOW_GUEST_OVERVIEW: + OnResizeOverview(); + break; + case WINDOW_GUEST_STATS: + case WINDOW_GUEST_RIDES: + case WINDOW_GUEST_FINANCE: + case WINDOW_GUEST_THOUGHTS: + case WINDOW_GUEST_INVENTORY: + case WINDOW_GUEST_DEBUG: + OnResizeCommon(); + break; + } + } + void OnUpdate() override + { + switch (page) + { + case WINDOW_GUEST_OVERVIEW: + OnUpdateOverview(); + break; + case WINDOW_GUEST_STATS: + OnUpdateStats(); + break; + case WINDOW_GUEST_RIDES: + OnUpdateRides(); + break; + case WINDOW_GUEST_FINANCE: + OnUpdateFinance(); + break; + case WINDOW_GUEST_THOUGHTS: + OnUpdateThoughts(); + break; + case WINDOW_GUEST_INVENTORY: + OnUpdateInventory(); + break; + case WINDOW_GUEST_DEBUG: + OnUpdateDebug(); + break; + } + } + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (page == WINDOW_GUEST_OVERVIEW) + { + OnToolUpdateOverview(widgetIndex, screenCoords); + } + } + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (page == WINDOW_GUEST_OVERVIEW) + { + OnToolDownOverview(widgetIndex, screenCoords); + } + } + void OnToolAbort(WidgetIndex widgetIndex) override + { + if (page == WINDOW_GUEST_OVERVIEW) + { + OnToolAbortOverview(widgetIndex); + } + } + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (page == WINDOW_GUEST_OVERVIEW) + { + OnTextInputOverview(widgetIndex, text); + } + } + void OnViewportRotate() override + { + if (page == WINDOW_GUEST_OVERVIEW) + { + OnViewportRotateOverview(); + } + } + void OnDraw(DrawPixelInfo& dpi) override + { + switch (page) + { + case WINDOW_GUEST_OVERVIEW: + OnDrawOverview(dpi); + break; + case WINDOW_GUEST_STATS: + OnDrawStats(dpi); + break; + case WINDOW_GUEST_RIDES: + OnDrawRides(dpi); + break; + case WINDOW_GUEST_FINANCE: + OnDrawFinance(dpi); + break; + case WINDOW_GUEST_THOUGHTS: + OnDrawThoughts(dpi); + break; + case WINDOW_GUEST_INVENTORY: + OnDrawInventory(dpi); + break; + case WINDOW_GUEST_DEBUG: + OnDrawDebug(dpi); + break; + } + } + void OnPrepareDraw() override + { + OnPrepareDrawCommon(); + switch (page) + { + case WINDOW_GUEST_OVERVIEW: + OnPrepareDrawOverview(); + break; + case WINDOW_GUEST_RIDES: + OnPrepareDrawRides(); + break; + } + } + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + if (page == WINDOW_GUEST_RIDES) + { + return OnScrollGetSizeRides(scrollIndex); + } + return {}; + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + if (page == WINDOW_GUEST_RIDES) + { + OnScrollMouseOverRides(scrollIndex, screenCoords); + } + } + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + if (page == WINDOW_GUEST_RIDES) + { + OnScrollMouseDownRides(scrollIndex, screenCoords); + } + } + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + if (page == WINDOW_GUEST_RIDES) + { + OnScrollDrawRides(scrollIndex, dpi); + } + } + + private: + Guest* GetGuest() + { + auto guest = GetEntity(EntityId::FromUnderlying(number)); + if (guest == nullptr) + { + Close(); + return nullptr; + } + return guest; + } + + void OnResizeCommon() + { + // Get page specific min and max size + int32_t minWidth = _guestWindowPageSizes[page][0].width; + int32_t minHeight = _guestWindowPageSizes[page][0].height; + int32_t maxWidth = _guestWindowPageSizes[page][1].width; + int32_t maxHeight = _guestWindowPageSizes[page][1].height; + + // Ensure min size is large enough for all tabs to fit + for (int32_t i = WIDX_TAB_1; i <= WIDX_TAB_7; i++) + { + if (!WidgetIsDisabled(*this, i)) + { + minWidth = std::max(minWidth, widgets[i].right + 3); + } + } + maxWidth = std::max(minWidth, maxWidth); + + WindowSetResize(*this, minWidth, minHeight, maxWidth, maxHeight); + } + + void OnPrepareDrawCommon() + { + if (_guestWindowPageWidgets[page] != widgets) + { + widgets = _guestWindowPageWidgets[page]; + InitScrollWidgets(); + } + + pressed_widgets |= 1uLL << (page + WIDX_TAB_1); + + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + auto ft = Formatter::Common(); + peep->FormatNameTo(ft); + + ResizeFrameWithPage(); + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + } + + void DisableWidgets() + { + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + uint64_t newDisabledWidgets = 0; + + if (peep->CanBePickedUp()) + { + if (WidgetIsDisabled(*this, WIDX_PICKUP)) + Invalidate(); + } + else + { + newDisabledWidgets = (1uLL << WIDX_PICKUP); + if (!WidgetIsDisabled(*this, WIDX_PICKUP)) + Invalidate(); + } + if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) + { + newDisabledWidgets |= (1uLL << WIDX_TAB_4); // Disable finance tab if no money + } + if (!gConfigGeneral.DebuggingTools) + { + newDisabledWidgets |= (1uLL << WIDX_TAB_7); // Disable debug tab when debug tools not turned on + } + disabled_widgets = newDisabledWidgets; + } + + void SetPage(int32_t newPage) + { + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + { + if (number == gCurrentToolWidget.window_number && classification == gCurrentToolWidget.window_classification) + ToolCancel(); + } + int32_t listen = 0; + if (newPage == WINDOW_GUEST_OVERVIEW && page == WINDOW_GUEST_OVERVIEW && viewport != nullptr) + { + if (!(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) + listen = 1; + } + + page = newPage; + frame_no = 0; + _riddenRides.clear(); + selected_list_item = -1; + + RemoveViewport(); + + hold_down_widgets = 0; + pressed_widgets = 0; widgets = _guestWindowPageWidgets[page]; + DisableWidgets(); + Invalidate(); + OnResize(); + OnPrepareDraw(); InitScrollWidgets(); + Invalidate(); + + if (listen && viewport != nullptr) + viewport->flags |= VIEWPORT_FLAG_SOUND_ON; } - pressed_widgets |= 1uLL << (page + WIDX_TAB_1); - - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - auto ft = Formatter::Common(); - peep->FormatNameTo(ft); - - ResizeFrameWithPage(); - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - } - - void DisableWidgets() - { - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - uint64_t newDisabledWidgets = 0; - - if (peep->CanBePickedUp()) - { - if (WidgetIsDisabled(*this, WIDX_PICKUP)) - Invalidate(); - } - else - { - newDisabledWidgets = (1uLL << WIDX_PICKUP); - if (!WidgetIsDisabled(*this, WIDX_PICKUP)) - Invalidate(); - } - if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) - { - newDisabledWidgets |= (1uLL << WIDX_TAB_4); // Disable finance tab if no money - } - if (!gConfigGeneral.DebuggingTools) - { - newDisabledWidgets |= (1uLL << WIDX_TAB_7); // Disable debug tab when debug tools not turned on - } - disabled_widgets = newDisabledWidgets; - } - - void SetPage(int32_t newPage) - { - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - { - if (number == gCurrentToolWidget.window_number && classification == gCurrentToolWidget.window_classification) - ToolCancel(); - } - int32_t listen = 0; - if (newPage == WINDOW_GUEST_OVERVIEW && page == WINDOW_GUEST_OVERVIEW && viewport != nullptr) - { - if (!(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) - listen = 1; - } - - page = newPage; - frame_no = 0; - _riddenRides.clear(); - selected_list_item = -1; - - RemoveViewport(); - - hold_down_widgets = 0; - pressed_widgets = 0; - widgets = _guestWindowPageWidgets[page]; - DisableWidgets(); - Invalidate(); - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - Invalidate(); - - if (listen && viewport != nullptr) - viewport->flags |= VIEWPORT_FLAG_SOUND_ON; - } - #pragma region Overview - void OverviewTabDraw(DrawPixelInfo& dpi) - { - if (WidgetIsDisabled(*this, WIDX_TAB_1)) - return; - - const auto& widget = widgets[WIDX_TAB_1]; - int32_t widgWidth = widget.width() - 1; - int32_t widgHeight = widget.height() - 1; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; - if (page == WINDOW_GUEST_OVERVIEW) - widgHeight++; - - DrawPixelInfo clipDpi; - if (!ClipDrawPixelInfo(clipDpi, dpi, screenCoords, widgWidth, widgHeight)) + void OverviewTabDraw(DrawPixelInfo& dpi) { - return; - } - - screenCoords = ScreenCoordsXY{ 14, 20 }; - - const auto* peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - int32_t animationFrame = GetPeepAnimation(peep->SpriteType).base_image + 1; - - int32_t animationFrameOffset = 0; - - if (page == WINDOW_GUEST_OVERVIEW) - { - animationFrameOffset = _guestAnimationFrame; - animationFrameOffset &= 0xFFFC; - } - animationFrame += animationFrameOffset; - - auto spriteId = ImageId(animationFrame, peep->TshirtColour, peep->TrousersColour); - GfxDrawSprite(clipDpi, spriteId, screenCoords); - - auto* guest = peep->As(); - if (guest != nullptr) - { - // If holding a balloon - if (animationFrame >= 0x2A1D && animationFrame < 0x2A3D) - { - GfxDrawSprite(clipDpi, ImageId(animationFrame + 32, guest->BalloonColour), screenCoords); - } - - // If holding umbrella - if (animationFrame >= 0x2BBD && animationFrame < 0x2BDD) - { - GfxDrawSprite(clipDpi, ImageId(animationFrame + 32, guest->UmbrellaColour), screenCoords); - } - - // If wearing hat - if (animationFrame >= 0x29DD && animationFrame < 0x29FD) - { - GfxDrawSprite(clipDpi, ImageId(animationFrame + 32, guest->HatColour), screenCoords); - } - } - } - - void OnResizeOverview() - { - DisableWidgets(); - OnPrepareDraw(); - - WidgetInvalidate(*this, WIDX_MARQUEE); - - OnResizeCommon(); - - if (viewport != nullptr) - { - auto reqViewportWidth = width - 30; - auto reqViewportHeight = height - 72; - if (viewport->width != reqViewportWidth || viewport->height != reqViewportHeight) - { - viewport->width = reqViewportWidth; - viewport->height = reqViewportHeight; - viewport->view_width = viewport->zoom.ApplyInversedTo(viewport->width); - viewport->view_height = viewport->zoom.ApplyInversedTo(viewport->height); - } - } - OnViewportRotate(); - } - - void OnMouseUpOverview(WidgetIndex widgetIndex) - { - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - switch (widgetIndex) - { - case WIDX_PICKUP: - { - if (!peep->CanBePickedUp()) - { - return; - } - _pickedPeepX = peep->x; - CoordsXYZ nullLoc{}; - nullLoc.SetNull(); - PeepPickupAction pickupAction{ PeepPickupType::Pickup, EntityId::FromUnderlying(number), nullLoc, - NetworkGetCurrentPlayerId() }; - pickupAction.SetCallback([peepnum = number](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) - return; - WindowBase* wind = WindowFindByNumber(WindowClass::Peep, peepnum); - if (wind != nullptr) - { - ToolSet(*wind, WC_PEEP__WIDX_PICKUP, Tool::Picker); - } - }); - GameActions::Execute(&pickupAction); - } - break; - case WIDX_RENAME: - { - auto peepName = peep->GetName(); - WindowTextInputRawOpen( - this, widgetIndex, STR_GUEST_RENAME_TITLE, STR_GUEST_RENAME_PROMPT, {}, peepName.c_str(), 32); - break; - } - case WIDX_TRACK: - { - uint32_t guestFlags = peep->PeepFlags ^ PEEP_FLAGS_TRACKING; - - auto guestSetFlagsAction = GuestSetFlagsAction(EntityId::FromUnderlying(number), guestFlags); - GameActions::Execute(&guestSetFlagsAction); - } - break; - } - } - - void OnMouseDownOverview(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_LOCATE: - ShowLocateDropdown(widgets[widgetIndex]); - break; - } - } - - void OnDropdownOverview(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_LOCATE: - { - if (dropdownIndex == 0) - { - ScrollToViewport(); - } - else if (dropdownIndex == 1) - { - GuestFollow(); - } - break; - } - } - } - - void ShowLocateDropdown(Widget& widget) - { - 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; - } - - void GuestFollow() - { - WindowBase* main = WindowGetMain(); - WindowFollowSprite(*main, EntityId::FromUnderlying(number)); - } - - void OnViewportRotateOverview() - { - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - ViewportUpdateSmartFollowGuest(this, *peep); - bool reCreateViewport = false; - uint16_t origViewportFlags{}; - if (viewport != nullptr) - { - if (focus.has_value()) + if (WidgetIsDisabled(*this, WIDX_TAB_1)) return; - origViewportFlags = viewport->flags; + const auto& widget = widgets[WIDX_TAB_1]; + int32_t widgWidth = widget.width() - 1; + int32_t widgHeight = widget.height() - 1; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; + if (page == WINDOW_GUEST_OVERVIEW) + widgHeight++; - reCreateViewport = true; - RemoveViewport(); + DrawPixelInfo clipDpi; + if (!ClipDrawPixelInfo(clipDpi, dpi, screenCoords, widgWidth, widgHeight)) + { + return; + } + + screenCoords = ScreenCoordsXY{ 14, 20 }; + + const auto* peep = GetGuest(); + if (peep == nullptr) + { + return; + } + + int32_t animationFrame = GetPeepAnimation(peep->SpriteType).base_image + 1; + + int32_t animationFrameOffset = 0; + + if (page == WINDOW_GUEST_OVERVIEW) + { + animationFrameOffset = _guestAnimationFrame; + animationFrameOffset &= 0xFFFC; + } + animationFrame += animationFrameOffset; + + auto spriteId = ImageId(animationFrame, peep->TshirtColour, peep->TrousersColour); + GfxDrawSprite(clipDpi, spriteId, screenCoords); + + auto* guest = peep->As(); + if (guest != nullptr) + { + // If holding a balloon + if (animationFrame >= 0x2A1D && animationFrame < 0x2A3D) + { + GfxDrawSprite(clipDpi, ImageId(animationFrame + 32, guest->BalloonColour), screenCoords); + } + + // If holding umbrella + if (animationFrame >= 0x2BBD && animationFrame < 0x2BDD) + { + GfxDrawSprite(clipDpi, ImageId(animationFrame + 32, guest->UmbrellaColour), screenCoords); + } + + // If wearing hat + if (animationFrame >= 0x29DD && animationFrame < 0x29FD) + { + GfxDrawSprite(clipDpi, ImageId(animationFrame + 32, guest->HatColour), screenCoords); + } + } } - OnPrepareDraw(); - - if (peep->State != PeepState::Picked && viewport == nullptr) + void OnResizeOverview() { - const auto& viewWidget = widgets[WIDX_VIEWPORT]; - auto screenPos = ScreenCoordsXY{ viewWidget.left + 1 + windowPos.x, viewWidget.top + 1 + windowPos.y }; - int32_t widgWidth = viewWidget.width() - 1; - int32_t widgHeight = viewWidget.height() - 1; + DisableWidgets(); + OnPrepareDraw(); - ViewportCreate(this, screenPos, widgWidth, widgHeight, focus.value()); - if (viewport != nullptr && reCreateViewport) + WidgetInvalidate(*this, WIDX_MARQUEE); + + OnResizeCommon(); + + if (viewport != nullptr) { - viewport->flags = origViewportFlags; + auto reqViewportWidth = width - 30; + auto reqViewportHeight = height - 72; + if (viewport->width != reqViewportWidth || viewport->height != reqViewportHeight) + { + viewport->width = reqViewportWidth; + viewport->height = reqViewportHeight; + viewport->view_width = viewport->zoom.ApplyInversedTo(viewport->width); + viewport->view_height = viewport->zoom.ApplyInversedTo(viewport->height); + } + } + OnViewportRotate(); + } + + void OnMouseUpOverview(WidgetIndex widgetIndex) + { + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + + switch (widgetIndex) + { + case WIDX_PICKUP: + { + if (!peep->CanBePickedUp()) + { + return; + } + _pickedPeepX = peep->x; + CoordsXYZ nullLoc{}; + nullLoc.SetNull(); + PeepPickupAction pickupAction{ PeepPickupType::Pickup, EntityId::FromUnderlying(number), nullLoc, + NetworkGetCurrentPlayerId() }; + pickupAction.SetCallback([peepnum = number](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; + WindowBase* wind = WindowFindByNumber(WindowClass::Peep, peepnum); + if (wind != nullptr) + { + ToolSet(*wind, WC_PEEP__WIDX_PICKUP, Tool::Picker); + } + }); + GameActions::Execute(&pickupAction); + } + break; + case WIDX_RENAME: + { + auto peepName = peep->GetName(); + WindowTextInputRawOpen( + this, widgetIndex, STR_GUEST_RENAME_TITLE, STR_GUEST_RENAME_PROMPT, {}, peepName.c_str(), 32); + break; + } + case WIDX_TRACK: + { + uint32_t guestFlags = peep->PeepFlags ^ PEEP_FLAGS_TRACKING; + + auto guestSetFlagsAction = GuestSetFlagsAction(EntityId::FromUnderlying(number), guestFlags); + GameActions::Execute(&guestSetFlagsAction); + } + break; + } + } + + void OnMouseDownOverview(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_LOCATE: + ShowLocateDropdown(widgets[widgetIndex]); + break; + } + } + + void OnDropdownOverview(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_LOCATE: + { + if (dropdownIndex == 0) + { + ScrollToViewport(); + } + else if (dropdownIndex == 1) + { + GuestFollow(); + } + break; + } + } + } + + void ShowLocateDropdown(Widget& widget) + { + 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; + } + + void GuestFollow() + { + WindowBase* main = WindowGetMain(); + WindowFollowSprite(*main, EntityId::FromUnderlying(number)); + } + + void OnViewportRotateOverview() + { + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + + ViewportUpdateSmartFollowGuest(this, *peep); + bool reCreateViewport = false; + uint16_t origViewportFlags{}; + if (viewport != nullptr) + { + if (focus.has_value()) + return; + + origViewportFlags = viewport->flags; + + reCreateViewport = true; + RemoveViewport(); + } + + OnPrepareDraw(); + + if (peep->State != PeepState::Picked && viewport == nullptr) + { + const auto& viewWidget = widgets[WIDX_VIEWPORT]; + auto screenPos = ScreenCoordsXY{ viewWidget.left + 1 + windowPos.x, viewWidget.top + 1 + windowPos.y }; + int32_t widgWidth = viewWidget.width() - 1; + int32_t widgHeight = viewWidget.height() - 1; + + ViewportCreate(this, screenPos, widgWidth, widgHeight, focus.value()); + if (viewport != nullptr && reCreateViewport) + { + viewport->flags = origViewportFlags; + } + flags |= WF_NO_SCROLLING; + Invalidate(); } - flags |= WF_NO_SCROLLING; Invalidate(); } - Invalidate(); - } - void OnDrawOverview(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - OverviewTabDraw(dpi); - StatsTabDraw(dpi); - RidesTabDraw(dpi); - FinanceTabDraw(dpi); - ThoughtsTabDraw(dpi); - InventoryTabDraw(dpi); - DebugTabDraw(dpi); - - // Draw the viewport no sound sprite - if (viewport != nullptr) + void OnDrawOverview(DrawPixelInfo& dpi) { - WindowDrawViewport(dpi, *this); - if (viewport->flags & VIEWPORT_FLAG_SOUND_ON) + DrawWidgets(dpi); + OverviewTabDraw(dpi); + StatsTabDraw(dpi); + RidesTabDraw(dpi); + FinanceTabDraw(dpi); + ThoughtsTabDraw(dpi); + InventoryTabDraw(dpi); + DebugTabDraw(dpi); + + // Draw the viewport no sound sprite + if (viewport != nullptr) { - GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this)); + WindowDrawViewport(dpi, *this); + if (viewport->flags & VIEWPORT_FLAG_SOUND_ON) + { + GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this)); + } } - } - // Draw the centred label - const auto* peep = GetGuest(); - if (peep == nullptr) - { - return; - } + // Draw the centred label + const auto* peep = GetGuest(); + if (peep == nullptr) + { + return; + } - const auto& actionLabelWidget = widgets[WIDX_ACTION_LBL]; - auto screenPos = windowPos + ScreenCoordsXY{ actionLabelWidget.midX(), actionLabelWidget.top - 1 }; + const auto& actionLabelWidget = widgets[WIDX_ACTION_LBL]; + auto screenPos = windowPos + ScreenCoordsXY{ actionLabelWidget.midX(), actionLabelWidget.top - 1 }; - { - auto ft = Formatter(); - peep->FormatActionTo(ft); - int32_t textWidth = actionLabelWidget.width(); - DrawTextEllipsised(dpi, screenPos, textWidth, STR_BLACK_STRING, ft, { TextAlignment::CENTRE }); - } + { + auto ft = Formatter(); + peep->FormatActionTo(ft); + int32_t textWidth = actionLabelWidget.width(); + DrawTextEllipsised(dpi, screenPos, textWidth, STR_BLACK_STRING, ft, { TextAlignment::CENTRE }); + } - // Draw the marquee thought - const auto& marqueeWidget = widgets[WIDX_MARQUEE]; - auto marqWidth = marqueeWidget.width() - 3; - int32_t left = marqueeWidget.left + 2 + windowPos.x; - int32_t top = marqueeWidget.top + windowPos.y; - int32_t marqHeight = marqueeWidget.height(); - DrawPixelInfo dpiMarquee; - if (!ClipDrawPixelInfo(dpiMarquee, dpi, { left, top }, marqWidth, marqHeight)) - { - return; - } + // Draw the marquee thought + const auto& marqueeWidget = widgets[WIDX_MARQUEE]; + auto marqWidth = marqueeWidget.width() - 3; + int32_t left = marqueeWidget.left + 2 + windowPos.x; + int32_t top = marqueeWidget.top + windowPos.y; + int32_t marqHeight = marqueeWidget.height(); + DrawPixelInfo dpiMarquee; + if (!ClipDrawPixelInfo(dpiMarquee, dpi, { left, top }, marqWidth, marqHeight)) + { + return; + } - int32_t i = 0; - for (; i < PEEP_MAX_THOUGHTS; ++i) - { - if (peep->Thoughts[i].type == PeepThoughtType::None) + int32_t i = 0; + for (; i < PEEP_MAX_THOUGHTS; ++i) + { + if (peep->Thoughts[i].type == PeepThoughtType::None) + { + _marqueePosition = 0; + return; + } + if (peep->Thoughts[i].freshness == 1) + { // If a fresh thought + break; + } + } + if (i == PEEP_MAX_THOUGHTS) { _marqueePosition = 0; return; } - if (peep->Thoughts[i].freshness == 1) - { // If a fresh thought - break; + + screenPos.x = marqueeWidget.width() - _marqueePosition; + { + auto ft = Formatter(); + PeepThoughtSetFormatArgs(&peep->Thoughts[i], ft); + DrawTextBasic(dpiMarquee, { screenPos.x, 0 }, STR_WINDOW_COLOUR_2_STRINGID, ft, { FontStyle::Small }); } } - if (i == PEEP_MAX_THOUGHTS) + + void OnPrepareDrawOverview() { - _marqueePosition = 0; - return; - } - - screenPos.x = marqueeWidget.width() - _marqueePosition; - { - auto ft = Formatter(); - PeepThoughtSetFormatArgs(&peep->Thoughts[i], ft); - DrawTextBasic(dpiMarquee, { screenPos.x, 0 }, STR_WINDOW_COLOUR_2_STRINGID, ft, { FontStyle::Small }); - } - } - - void OnPrepareDrawOverview() - { - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - pressed_widgets &= ~(1uLL << WIDX_TRACK); - if (peep->PeepFlags & PEEP_FLAGS_TRACKING) - { - pressed_widgets |= (1uLL << WIDX_TRACK); - } - - widgets[WIDX_VIEWPORT].right = width - 26; - widgets[WIDX_VIEWPORT].bottom = height - 14; - - widgets[WIDX_ACTION_LBL].top = height - 12; - widgets[WIDX_ACTION_LBL].bottom = height - 3; - widgets[WIDX_ACTION_LBL].right = width - 24; - - widgets[WIDX_MARQUEE].right = width - 24; - - widgets[WIDX_PICKUP].right = width - 2; - widgets[WIDX_RENAME].right = width - 2; - widgets[WIDX_LOCATE].right = width - 2; - widgets[WIDX_TRACK].right = width - 2; - - widgets[WIDX_PICKUP].left = width - 25; - widgets[WIDX_RENAME].left = width - 25; - widgets[WIDX_LOCATE].left = width - 25; - widgets[WIDX_TRACK].left = width - 25; - } - - void OnUpdateOverview() - { - _guestAnimationFrame++; - _guestAnimationFrame %= 24; - - // Update pickup animation, can only happen in this tab. - picked_peep_frame++; - picked_peep_frame %= 48; - - WidgetInvalidate(*this, WIDX_TAB_1); - WidgetInvalidate(*this, WIDX_TAB_2); - - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - if (peep->WindowInvalidateFlags & PEEP_INVALIDATE_PEEP_ACTION) - { - peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_ACTION; - WidgetInvalidate(*this, WIDX_ACTION_LBL); - } - - _marqueePosition += 2; - - _beingWatchedTimer++; - - // Disable peep watching thought for multiplayer as it's client specific - if (NetworkGetMode() == NETWORK_MODE_NONE) - { - // Create the "I have the strangest feeling I am being watched thought" - if (_beingWatchedTimer >= 3840) + const auto peep = GetGuest(); + if (peep == nullptr) { - if (!(_beingWatchedTimer & 0x3FF)) + return; + } + pressed_widgets &= ~(1uLL << WIDX_TRACK); + if (peep->PeepFlags & PEEP_FLAGS_TRACKING) + { + pressed_widgets |= (1uLL << WIDX_TRACK); + } + + widgets[WIDX_VIEWPORT].right = width - 26; + widgets[WIDX_VIEWPORT].bottom = height - 14; + + widgets[WIDX_ACTION_LBL].top = height - 12; + widgets[WIDX_ACTION_LBL].bottom = height - 3; + widgets[WIDX_ACTION_LBL].right = width - 24; + + widgets[WIDX_MARQUEE].right = width - 24; + + widgets[WIDX_PICKUP].right = width - 2; + widgets[WIDX_RENAME].right = width - 2; + widgets[WIDX_LOCATE].right = width - 2; + widgets[WIDX_TRACK].right = width - 2; + + widgets[WIDX_PICKUP].left = width - 25; + widgets[WIDX_RENAME].left = width - 25; + widgets[WIDX_LOCATE].left = width - 25; + widgets[WIDX_TRACK].left = width - 25; + } + + void OnUpdateOverview() + { + _guestAnimationFrame++; + _guestAnimationFrame %= 24; + + // Update pickup animation, can only happen in this tab. + picked_peep_frame++; + picked_peep_frame %= 48; + + WidgetInvalidate(*this, WIDX_TAB_1); + WidgetInvalidate(*this, WIDX_TAB_2); + + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + if (peep->WindowInvalidateFlags & PEEP_INVALIDATE_PEEP_ACTION) + { + peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_ACTION; + WidgetInvalidate(*this, WIDX_ACTION_LBL); + } + + _marqueePosition += 2; + + _beingWatchedTimer++; + + // Disable peep watching thought for multiplayer as it's client specific + if (NetworkGetMode() == NETWORK_MODE_NONE) + { + // Create the "I have the strangest feeling I am being watched thought" + if (_beingWatchedTimer >= 3840) { - int32_t random = UtilRand() & 0xFFFF; - if (random <= 0x2AAA) + if (!(_beingWatchedTimer & 0x3FF)) { - peep->InsertNewThought(PeepThoughtType::Watched); + int32_t random = UtilRand() & 0xFFFF; + if (random <= 0x2AAA) + { + peep->InsertNewThought(PeepThoughtType::Watched); + } } } } } - } - void OnTextInputOverview(WidgetIndex widgetIndex, std::string_view text) - { - if (widgetIndex != WIDX_RENAME) - return; - - if (text.empty()) - return; - std::string sText(text); - auto gameAction = GuestSetNameAction(EntityId::FromUnderlying(number), sText); - GameActions::Execute(&gameAction); - } - - void OnToolUpdateOverview(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) - { - if (widgetIndex != WIDX_PICKUP) - return; - - MapInvalidateSelectionRect(); - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - auto mapCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, nullptr); - if (!mapCoords.IsNull()) + void OnTextInputOverview(WidgetIndex widgetIndex, std::string_view text) { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = mapCoords; - gMapSelectPositionB = mapCoords; - MapInvalidateSelectionRect(); - } - - gPickupPeepImage = ImageId(); - - auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionItemAll); - if (info.SpriteType == ViewportInteractionItem::None) - return; - - gPickupPeepX = screenCoords.x - 1; - gPickupPeepY = screenCoords.y + 16; - - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - auto baseImageId = GetPeepAnimation(peep->SpriteType, PeepActionSpriteType::Ui).base_image; - baseImageId += picked_peep_frame >> 2; - gPickupPeepImage = ImageId(baseImageId, peep->TshirtColour, peep->TrousersColour); - } - - void OnToolDownOverview(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) - { - if (widgetIndex != WIDX_PICKUP) - return; - - TileElement* tileElement; - auto destCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement); - - if (destCoords.IsNull()) - return; - - PeepPickupAction pickupAction{ PeepPickupType::Place, - EntityId::FromUnderlying(number), - { destCoords, tileElement->GetBaseZ() }, - NetworkGetCurrentPlayerId() }; - pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) + if (widgetIndex != WIDX_RENAME) return; - ToolCancel(); + + if (text.empty()) + return; + std::string sText(text); + auto gameAction = GuestSetNameAction(EntityId::FromUnderlying(number), sText); + GameActions::Execute(&gameAction); + } + + void OnToolUpdateOverview(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex != WIDX_PICKUP) + return; + + MapInvalidateSelectionRect(); + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + + auto mapCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, nullptr); + if (!mapCoords.IsNull()) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectPositionA = mapCoords; + gMapSelectPositionB = mapCoords; + MapInvalidateSelectionRect(); + } + gPickupPeepImage = ImageId(); - }); - GameActions::Execute(&pickupAction); - } - void OnToolAbortOverview(WidgetIndex widgetIndex) - { - if (widgetIndex != WIDX_PICKUP) - return; + auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionItemAll); + if (info.SpriteType == ViewportInteractionItem::None) + return; - PeepPickupAction pickupAction{ - PeepPickupType::Cancel, EntityId::FromUnderlying(number), { _pickedPeepX, 0, 0 }, NetworkGetCurrentPlayerId() - }; - GameActions::Execute(&pickupAction); - } + gPickupPeepX = screenCoords.x - 1; + gPickupPeepY = screenCoords.y + 16; + + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + + auto baseImageId = GetPeepAnimation(peep->SpriteType, PeepActionSpriteType::Ui).base_image; + baseImageId += picked_peep_frame >> 2; + gPickupPeepImage = ImageId(baseImageId, peep->TshirtColour, peep->TrousersColour); + } + + void OnToolDownOverview(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex != WIDX_PICKUP) + return; + + TileElement* tileElement; + auto destCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement); + + if (destCoords.IsNull()) + return; + + PeepPickupAction pickupAction{ PeepPickupType::Place, + EntityId::FromUnderlying(number), + { destCoords, tileElement->GetBaseZ() }, + NetworkGetCurrentPlayerId() }; + pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; + ToolCancel(); + gPickupPeepImage = ImageId(); + }); + GameActions::Execute(&pickupAction); + } + + void OnToolAbortOverview(WidgetIndex widgetIndex) + { + if (widgetIndex != WIDX_PICKUP) + return; + + PeepPickupAction pickupAction{ + PeepPickupType::Cancel, EntityId::FromUnderlying(number), { _pickedPeepX, 0, 0 }, NetworkGetCurrentPlayerId() + }; + GameActions::Execute(&pickupAction); + } #pragma endregion #pragma region Stats - void StatsTabDraw(DrawPixelInfo& dpi) - { - if (WidgetIsDisabled(*this, WIDX_TAB_2)) - return; - - const auto& widget = widgets[WIDX_TAB_2]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - - const auto peep = GetGuest(); - if (peep == nullptr) + void StatsTabDraw(DrawPixelInfo& dpi) { - return; - } - int32_t imageId = GetPeepFaceSpriteLarge(peep); - if (page == WINDOW_GUEST_STATS) - { - // If currently viewing this tab animate tab - // if it is very sick or angry. - switch (imageId) - { - case SPR_PEEP_LARGE_FACE_VERY_VERY_SICK_0: - imageId += (frame_no / 4) & 0xF; - break; - case SPR_PEEP_LARGE_FACE_VERY_SICK_0: - imageId += (frame_no / 8) & 0x3; - break; - case SPR_PEEP_LARGE_FACE_ANGRY_0: - imageId += (frame_no / 8) & 0x3; - break; - } - } - GfxDrawSprite(dpi, ImageId(imageId), screenCoords); - } - - void OnUpdateStats() - { - frame_no++; - auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_STATS; - - Invalidate(); - } - - void StatsBarsDraw(int32_t value, const ScreenCoordsXY& origCoords, DrawPixelInfo& dpi, int32_t colour, bool blinkFlag) - { - auto coords = origCoords; - if (FontGetLineHeight(FontStyle::Medium) > 10) - { - coords.y += 1; - } - - GfxFillRectInset( - dpi, { coords + ScreenCoordsXY{ 61, 1 }, coords + ScreenCoordsXY{ 61 + 121, 9 } }, colours[1], INSET_RECT_F_30); - - if (!blinkFlag || GameIsPaused() || (gCurrentRealTimeTicks & 8) == 0) - { - value *= 118; - value >>= 8; - - if (value <= 2) + if (WidgetIsDisabled(*this, WIDX_TAB_2)) return; + const auto& widget = widgets[WIDX_TAB_2]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; + + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + int32_t imageId = GetPeepFaceSpriteLarge(peep); + if (page == WINDOW_GUEST_STATS) + { + // If currently viewing this tab animate tab + // if it is very sick or angry. + switch (imageId) + { + case SPR_PEEP_LARGE_FACE_VERY_VERY_SICK_0: + imageId += (frame_no / 4) & 0xF; + break; + case SPR_PEEP_LARGE_FACE_VERY_SICK_0: + imageId += (frame_no / 8) & 0x3; + break; + case SPR_PEEP_LARGE_FACE_ANGRY_0: + imageId += (frame_no / 8) & 0x3; + break; + } + } + GfxDrawSprite(dpi, ImageId(imageId), screenCoords); + } + + void OnUpdateStats() + { + frame_no++; + auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_STATS; + + Invalidate(); + } + + void StatsBarsDraw(int32_t value, const ScreenCoordsXY& origCoords, DrawPixelInfo& dpi, int32_t colour, bool blinkFlag) + { + auto coords = origCoords; + if (FontGetLineHeight(FontStyle::Medium) > 10) + { + coords.y += 1; + } + GfxFillRectInset( - dpi, { coords + ScreenCoordsXY{ 63, 2 }, coords + ScreenCoordsXY{ 63 + value - 1, 8 } }, colour, 0); - } - } + dpi, { coords + ScreenCoordsXY{ 61, 1 }, coords + ScreenCoordsXY{ 61 + 121, 9 } }, colours[1], INSET_RECT_F_30); - /** - * Takes a guest stat value (scaled to currMax) and adjusts it to be scaled out of 255. - * Then clamp the value to between newMin and 255. - */ - int32_t NormalizeGuestStatValue(int32_t value, int32_t currMax, int32_t newMin) const - { - int32_t newValue = (value * 255) / currMax; - return std::clamp(newValue, newMin, 255); - } - - void OnDrawStats(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - OverviewTabDraw(dpi); - StatsTabDraw(dpi); - RidesTabDraw(dpi); - FinanceTabDraw(dpi); - ThoughtsTabDraw(dpi); - InventoryTabDraw(dpi); - DebugTabDraw(dpi); - - // ebx - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - // Not sure why this is not stats widgets - // cx dx - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - - // Happiness - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_HAPPINESS_LABEL); - - int32_t happiness = NormalizeGuestStatValue(peep->Happiness, PEEP_MAX_HAPPINESS, 10); - int32_t barColour = COLOUR_BRIGHT_GREEN; - bool barBlink = happiness < 50; - StatsBarsDraw(happiness, screenCoords, dpi, barColour, barBlink); - - // Energy - screenCoords.y += LIST_ROW_HEIGHT; - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_ENERGY_LABEL); - - int32_t energy = NormalizeGuestStatValue(peep->Energy - PEEP_MIN_ENERGY, PEEP_MAX_ENERGY - PEEP_MIN_ENERGY, 10); - barColour = COLOUR_BRIGHT_GREEN; - barBlink = energy < 50; - StatsBarsDraw(energy, screenCoords, dpi, barColour, barBlink); - - // Hunger - screenCoords.y += LIST_ROW_HEIGHT; - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_HUNGER_LABEL); - - int32_t hunger = NormalizeGuestStatValue(peep->Hunger - 32, 158, 0); - hunger = 255 - hunger; // the bar should be longer when peep->Hunger is low - barColour = COLOUR_BRIGHT_RED; - barBlink = hunger > 170; - StatsBarsDraw(hunger, screenCoords, dpi, barColour, barBlink); - - // Thirst - screenCoords.y += LIST_ROW_HEIGHT; - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_THIRST_LABEL); - - int32_t thirst = NormalizeGuestStatValue(peep->Thirst - 32, 158, 0); - thirst = 255 - thirst; // the bar should be longer when peep->Thirst is low - barColour = COLOUR_BRIGHT_RED; - barBlink = thirst > 170; - StatsBarsDraw(thirst, screenCoords, dpi, barColour, barBlink); - - // Nausea - screenCoords.y += LIST_ROW_HEIGHT; - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_NAUSEA_LABEL); - - int32_t nausea = NormalizeGuestStatValue(peep->Nausea - 32, 223, 0); - barColour = COLOUR_BRIGHT_RED; - barBlink = nausea > 120; - StatsBarsDraw(nausea, screenCoords, dpi, barColour, barBlink); - - // Toilet - screenCoords.y += LIST_ROW_HEIGHT; - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_TOILET_LABEL); - - int32_t toilet = NormalizeGuestStatValue(peep->Toilet - 64, 178, 0); - barColour = COLOUR_BRIGHT_RED; - barBlink = toilet > 160; - StatsBarsDraw(toilet, screenCoords, dpi, barColour, barBlink); - - // Time in park - screenCoords.y += LIST_ROW_HEIGHT + 1; - int32_t guestEntryTime = peep->GetParkEntryTime(); - if (guestEntryTime != -1) - { - int32_t timeInPark = (GetGameState().CurrentTicks - guestEntryTime) >> 11; - auto ft = Formatter(); - ft.Add(timeInPark & 0xFFFF); - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_TIME_IN_PARK, ft); - } - - screenCoords.y += LIST_ROW_HEIGHT + 9; - GfxFillRectInset( - dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 179, -5 } }, colours[1], - INSET_RECT_FLAG_BORDER_INSET); - - // Preferred Ride - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_PREFERRED_RIDE); - screenCoords.y += LIST_ROW_HEIGHT; - - // Intensity - { - auto ft = Formatter(); - auto maxIntensity = peep->Intensity.GetMaximum(); - int32_t string_id = STR_GUEST_STAT_PREFERRED_INTESITY_BELOW; - if (peep->Intensity.GetMinimum() != 0) + if (!blinkFlag || GameIsPaused() || (gCurrentRealTimeTicks & 8) == 0) { - ft.Add(peep->Intensity.GetMinimum()); - ft.Add(maxIntensity); - string_id = STR_GUEST_STAT_PREFERRED_INTESITY_BETWEEN; - if (maxIntensity == 15) - string_id = STR_GUEST_STAT_PREFERRED_INTESITY_ABOVE; + value *= 118; + value >>= 8; + + if (value <= 2) + return; + + GfxFillRectInset( + dpi, { coords + ScreenCoordsXY{ 63, 2 }, coords + ScreenCoordsXY{ 63 + value - 1, 8 } }, colour, 0); } - else + } + + /** + * Takes a guest stat value (scaled to currMax) and adjusts it to be scaled out of 255. + * Then clamp the value to between newMin and 255. + */ + int32_t NormalizeGuestStatValue(int32_t value, int32_t currMax, int32_t newMin) const + { + int32_t newValue = (value * 255) / currMax; + return std::clamp(newValue, newMin, 255); + } + + void OnDrawStats(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + OverviewTabDraw(dpi); + StatsTabDraw(dpi); + RidesTabDraw(dpi); + FinanceTabDraw(dpi); + ThoughtsTabDraw(dpi); + InventoryTabDraw(dpi); + DebugTabDraw(dpi); + + // ebx + const auto peep = GetGuest(); + if (peep == nullptr) { - ft.Add(maxIntensity); + return; } - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, string_id, ft); - } + // Not sure why this is not stats widgets + // cx dx + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - // Nausea tolerance - { - static constexpr StringId _nauseaTolerances[] = { - STR_PEEP_STAT_NAUSEA_TOLERANCE_NONE, - STR_PEEP_STAT_NAUSEA_TOLERANCE_LOW, - STR_PEEP_STAT_NAUSEA_TOLERANCE_AVERAGE, - STR_PEEP_STAT_NAUSEA_TOLERANCE_HIGH, - }; + // Happiness + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_HAPPINESS_LABEL); + + int32_t happiness = NormalizeGuestStatValue(peep->Happiness, PEEP_MAX_HAPPINESS, 10); + int32_t barColour = COLOUR_BRIGHT_GREEN; + bool barBlink = happiness < 50; + StatsBarsDraw(happiness, screenCoords, dpi, barColour, barBlink); + + // Energy screenCoords.y += LIST_ROW_HEIGHT; - auto nausea_tolerance = EnumValue(peep->NauseaTolerance) & 0x3; - auto ft = Formatter(); - ft.Add(_nauseaTolerances[nausea_tolerance]); - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_NAUSEA_TOLERANCE, ft); + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_ENERGY_LABEL); + + int32_t energy = NormalizeGuestStatValue(peep->Energy - PEEP_MIN_ENERGY, PEEP_MAX_ENERGY - PEEP_MIN_ENERGY, 10); + barColour = COLOUR_BRIGHT_GREEN; + barBlink = energy < 50; + StatsBarsDraw(energy, screenCoords, dpi, barColour, barBlink); + + // Hunger + screenCoords.y += LIST_ROW_HEIGHT; + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_HUNGER_LABEL); + + int32_t hunger = NormalizeGuestStatValue(peep->Hunger - 32, 158, 0); + hunger = 255 - hunger; // the bar should be longer when peep->Hunger is low + barColour = COLOUR_BRIGHT_RED; + barBlink = hunger > 170; + StatsBarsDraw(hunger, screenCoords, dpi, barColour, barBlink); + + // Thirst + screenCoords.y += LIST_ROW_HEIGHT; + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_THIRST_LABEL); + + int32_t thirst = NormalizeGuestStatValue(peep->Thirst - 32, 158, 0); + thirst = 255 - thirst; // the bar should be longer when peep->Thirst is low + barColour = COLOUR_BRIGHT_RED; + barBlink = thirst > 170; + StatsBarsDraw(thirst, screenCoords, dpi, barColour, barBlink); + + // Nausea + screenCoords.y += LIST_ROW_HEIGHT; + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_NAUSEA_LABEL); + + int32_t nausea = NormalizeGuestStatValue(peep->Nausea - 32, 223, 0); + barColour = COLOUR_BRIGHT_RED; + barBlink = nausea > 120; + StatsBarsDraw(nausea, screenCoords, dpi, barColour, barBlink); + + // Toilet + screenCoords.y += LIST_ROW_HEIGHT; + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_TOILET_LABEL); + + int32_t toilet = NormalizeGuestStatValue(peep->Toilet - 64, 178, 0); + barColour = COLOUR_BRIGHT_RED; + barBlink = toilet > 160; + StatsBarsDraw(toilet, screenCoords, dpi, barColour, barBlink); + + // Time in park + screenCoords.y += LIST_ROW_HEIGHT + 1; + int32_t guestEntryTime = peep->GetParkEntryTime(); + if (guestEntryTime != -1) + { + int32_t timeInPark = (GetGameState().CurrentTicks - guestEntryTime) >> 11; + auto ft = Formatter(); + ft.Add(timeInPark & 0xFFFF); + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_TIME_IN_PARK, ft); + } + + screenCoords.y += LIST_ROW_HEIGHT + 9; + GfxFillRectInset( + dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 179, -5 } }, colours[1], + INSET_RECT_FLAG_BORDER_INSET); + + // Preferred Ride + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_PREFERRED_RIDE); + screenCoords.y += LIST_ROW_HEIGHT; + + // Intensity + { + auto ft = Formatter(); + auto maxIntensity = peep->Intensity.GetMaximum(); + int32_t string_id = STR_GUEST_STAT_PREFERRED_INTESITY_BELOW; + if (peep->Intensity.GetMinimum() != 0) + { + ft.Add(peep->Intensity.GetMinimum()); + ft.Add(maxIntensity); + string_id = STR_GUEST_STAT_PREFERRED_INTESITY_BETWEEN; + if (maxIntensity == 15) + string_id = STR_GUEST_STAT_PREFERRED_INTESITY_ABOVE; + } + else + { + ft.Add(maxIntensity); + } + + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 4, 0 }, string_id, ft); + } + + // Nausea tolerance + { + static constexpr StringId _nauseaTolerances[] = { + STR_PEEP_STAT_NAUSEA_TOLERANCE_NONE, + STR_PEEP_STAT_NAUSEA_TOLERANCE_LOW, + STR_PEEP_STAT_NAUSEA_TOLERANCE_AVERAGE, + STR_PEEP_STAT_NAUSEA_TOLERANCE_HIGH, + }; + screenCoords.y += LIST_ROW_HEIGHT; + auto nausea_tolerance = EnumValue(peep->NauseaTolerance) & 0x3; + auto ft = Formatter(); + ft.Add(_nauseaTolerances[nausea_tolerance]); + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_NAUSEA_TOLERANCE, ft); + } } - } #pragma endregion #pragma region Rides - void RidesTabDraw(DrawPixelInfo& dpi) - { - if (WidgetIsDisabled(*this, WIDX_TAB_3)) - return; - - const auto& widget = widgets[WIDX_TAB_3]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - - int32_t imageId = SPR_TAB_RIDE_0; - - if (page == WINDOW_GUEST_RIDES) + void RidesTabDraw(DrawPixelInfo& dpi) { - imageId += (frame_no / 4) & 0xF; - } + if (WidgetIsDisabled(*this, WIDX_TAB_3)) + return; - GfxDrawSprite(dpi, ImageId(imageId), screenCoords); - } + const auto& widget = widgets[WIDX_TAB_3]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - void OnUpdateRides() - { - frame_no++; + int32_t imageId = SPR_TAB_RIDE_0; - WidgetInvalidate(*this, WIDX_TAB_2); - WidgetInvalidate(*this, WIDX_TAB_3); - - const auto guest = GetGuest(); - if (guest == nullptr) - { - return; - } - - // Every 2048 ticks do a full window_invalidate - int32_t numTicks = GetGameState().CurrentTicks - guest->GetParkEntryTime(); - if (!(numTicks & 0x7FF)) - Invalidate(); - - const auto oldSize = _riddenRides.size(); - _riddenRides.clear(); - for (const auto& r : GetRideManager()) - { - if (r.IsRide() && guest->HasRidden(r)) + if (page == WINDOW_GUEST_RIDES) { - _riddenRides.push_back(r.id); + imageId += (frame_no / 4) & 0xF; + } + + GfxDrawSprite(dpi, ImageId(imageId), screenCoords); + } + + void OnUpdateRides() + { + frame_no++; + + WidgetInvalidate(*this, WIDX_TAB_2); + WidgetInvalidate(*this, WIDX_TAB_3); + + const auto guest = GetGuest(); + if (guest == nullptr) + { + return; + } + + // Every 2048 ticks do a full window_invalidate + int32_t numTicks = GetGameState().CurrentTicks - guest->GetParkEntryTime(); + if (!(numTicks & 0x7FF)) + Invalidate(); + + const auto oldSize = _riddenRides.size(); + _riddenRides.clear(); + for (const auto& r : GetRideManager()) + { + if (r.IsRide() && guest->HasRidden(r)) + { + _riddenRides.push_back(r.id); + } + } + + // If there are new items + if (oldSize != _riddenRides.size()) + { + Invalidate(); } } - // If there are new items - if (oldSize != _riddenRides.size()) + ScreenSize OnScrollGetSizeRides(int32_t scrollIndex) { - Invalidate(); - } - } + ScreenSize newSize; + newSize.height = static_cast(_riddenRides.size()) * 10; - ScreenSize OnScrollGetSizeRides(int32_t scrollIndex) - { - ScreenSize newSize; - newSize.height = static_cast(_riddenRides.size()) * 10; - - if (selected_list_item != -1) - { - selected_list_item = -1; - Invalidate(); - } - - int32_t visableHeight = newSize.height - widgets[WIDX_RIDE_SCROLL].bottom + widgets[WIDX_RIDE_SCROLL].top + 21; - - if (visableHeight < 0) - visableHeight = 0; - - if (visableHeight < scrolls[0].v_top) - { - scrolls[0].v_top = visableHeight; - Invalidate(); - } - return newSize; - } - - void OnScrollMouseDownRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) - { - auto index = screenCoords.y / 10; - if (index >= static_cast(_riddenRides.size())) - return; - - auto intent = Intent(WindowClass::Ride); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, _riddenRides[index]); - ContextOpenIntent(&intent); - } - - void OnScrollMouseOverRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) - { - auto index = screenCoords.y / 10; - if (index >= static_cast(_riddenRides.size())) - return; - - if (index == selected_list_item) - return; - selected_list_item = index; - - Invalidate(); - } - - void OnPrepareDrawRides() - { - widgets[WIDX_RIDE_SCROLL].right = width - 4; - widgets[WIDX_RIDE_SCROLL].bottom = height - 15; - } - - void OnDrawRides(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - OverviewTabDraw(dpi); - StatsTabDraw(dpi); - RidesTabDraw(dpi); - FinanceTabDraw(dpi); - ThoughtsTabDraw(dpi); - InventoryTabDraw(dpi); - DebugTabDraw(dpi); - - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - // cx dx - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 2, widgets[WIDX_PAGE_BACKGROUND].top + 2 }; - - DrawTextBasic(dpi, screenCoords, STR_GUEST_LABEL_RIDES_BEEN_ON); - - screenCoords.y = windowPos.y + widgets[WIDX_PAGE_BACKGROUND].bottom - 12; - - auto ft = Formatter(); - auto* r = GetRide(peep->FavouriteRide); - if (r != nullptr) - { - r->FormatNameTo(ft); - } - else - { - ft.Add(STR_PEEP_FAVOURITE_RIDE_NOT_AVAILABLE); - } - - DrawTextEllipsised(dpi, screenCoords, width - 14, STR_FAVOURITE_RIDE, ft); - } - - void OnScrollDrawRides(int32_t scrollIndex, DrawPixelInfo& dpi) - { - auto colour = ColourMapA[colours[1]].mid_light; - GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, colour); - - for (int32_t listIndex = 0; listIndex < static_cast(_riddenRides.size()); listIndex++) - { - int32_t y = listIndex * 10; - StringId stringId = STR_BLACK_STRING; - if (listIndex == selected_list_item) + if (selected_list_item != -1) { - GfxFilterRect(dpi, { 0, y, 800, y + 9 }, FilterPaletteID::PaletteDarken1); - stringId = STR_WINDOW_COLOUR_2_STRINGID; + selected_list_item = -1; + Invalidate(); } - auto* r = GetRide(_riddenRides[listIndex]); + int32_t visableHeight = newSize.height - widgets[WIDX_RIDE_SCROLL].bottom + widgets[WIDX_RIDE_SCROLL].top + 21; + + if (visableHeight < 0) + visableHeight = 0; + + if (visableHeight < scrolls[0].v_top) + { + scrolls[0].v_top = visableHeight; + Invalidate(); + } + return newSize; + } + + void OnScrollMouseDownRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) + { + auto index = screenCoords.y / 10; + if (index >= static_cast(_riddenRides.size())) + return; + + auto intent = Intent(WindowClass::Ride); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, _riddenRides[index]); + ContextOpenIntent(&intent); + } + + void OnScrollMouseOverRides(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) + { + auto index = screenCoords.y / 10; + if (index >= static_cast(_riddenRides.size())) + return; + + if (index == selected_list_item) + return; + selected_list_item = index; + + Invalidate(); + } + + void OnPrepareDrawRides() + { + widgets[WIDX_RIDE_SCROLL].right = width - 4; + widgets[WIDX_RIDE_SCROLL].bottom = height - 15; + } + + void OnDrawRides(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + OverviewTabDraw(dpi); + StatsTabDraw(dpi); + RidesTabDraw(dpi); + FinanceTabDraw(dpi); + ThoughtsTabDraw(dpi); + InventoryTabDraw(dpi); + DebugTabDraw(dpi); + + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + + // cx dx + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 2, widgets[WIDX_PAGE_BACKGROUND].top + 2 }; + + DrawTextBasic(dpi, screenCoords, STR_GUEST_LABEL_RIDES_BEEN_ON); + + screenCoords.y = windowPos.y + widgets[WIDX_PAGE_BACKGROUND].bottom - 12; + + auto ft = Formatter(); + auto* r = GetRide(peep->FavouriteRide); if (r != nullptr) { - auto ft = Formatter(); r->FormatNameTo(ft); - DrawTextBasic(dpi, { 0, y - 1 }, stringId, ft); + } + else + { + ft.Add(STR_PEEP_FAVOURITE_RIDE_NOT_AVAILABLE); + } + + DrawTextEllipsised(dpi, screenCoords, width - 14, STR_FAVOURITE_RIDE, ft); + } + + void OnScrollDrawRides(int32_t scrollIndex, DrawPixelInfo& dpi) + { + auto colour = ColourMapA[colours[1]].mid_light; + GfxFillRect(dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, colour); + + for (int32_t listIndex = 0; listIndex < static_cast(_riddenRides.size()); listIndex++) + { + int32_t y = listIndex * 10; + StringId stringId = STR_BLACK_STRING; + if (listIndex == selected_list_item) + { + GfxFilterRect(dpi, { 0, y, 800, y + 9 }, FilterPaletteID::PaletteDarken1); + stringId = STR_WINDOW_COLOUR_2_STRINGID; + } + + auto* r = GetRide(_riddenRides[listIndex]); + if (r != nullptr) + { + auto ft = Formatter(); + r->FormatNameTo(ft); + DrawTextBasic(dpi, { 0, y - 1 }, stringId, ft); + } } } - } #pragma endregion #pragma region Finance - void FinanceTabDraw(DrawPixelInfo& dpi) - { - if (WidgetIsDisabled(*this, WIDX_TAB_4)) - return; - - const auto& widget = widgets[WIDX_TAB_4]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - - int32_t imageId = SPR_TAB_FINANCES_SUMMARY_0; - - if (page == WINDOW_GUEST_FINANCE) + void FinanceTabDraw(DrawPixelInfo& dpi) { - imageId += (frame_no / 2) & 0x7; - } + if (WidgetIsDisabled(*this, WIDX_TAB_4)) + return; - GfxDrawSprite(dpi, ImageId(imageId), screenCoords); - } + const auto& widget = widgets[WIDX_TAB_4]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - void OnUpdateFinance() - { - frame_no++; + int32_t imageId = SPR_TAB_FINANCES_SUMMARY_0; - WidgetInvalidate(*this, WIDX_TAB_2); - WidgetInvalidate(*this, WIDX_TAB_4); - } - - void OnDrawFinance(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - OverviewTabDraw(dpi); - StatsTabDraw(dpi); - RidesTabDraw(dpi); - FinanceTabDraw(dpi); - ThoughtsTabDraw(dpi); - InventoryTabDraw(dpi); - DebugTabDraw(dpi); - - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - // cx dx - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - - // Cash in pocket - { - auto ft = Formatter(); - ft.Add(peep->CashInPocket); - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_CASH_IN_POCKET, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - // Cash spent - { - auto ft = Formatter(); - ft.Add(peep->CashSpent); - DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_CASH_SPENT, ft); - screenCoords.y += LIST_ROW_HEIGHT * 2; - } - - GfxFillRectInset( - dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 179, -5 } }, colours[1], - INSET_RECT_FLAG_BORDER_INSET); - - // Paid to enter - { - auto ft = Formatter(); - ft.Add(peep->PaidToEnter); - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_ENTRANCE_FEE, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - // Paid on rides - { - auto ft = Formatter(); - ft.Add(peep->PaidOnRides); - ft.Add(peep->GuestNumRides); - if (peep->GuestNumRides != 1) + if (page == WINDOW_GUEST_FINANCE) { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_RIDE_PLURAL, ft); + imageId += (frame_no / 2) & 0x7; } - else - { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_RIDE, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; - } - // Paid on food - { - auto ft = Formatter(); - ft.Add(peep->PaidOnFood); - ft.Add(peep->AmountOfFood); - if (peep->AmountOfFood != 1) - { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_FOOD_PLURAL, ft); - } - else - { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_FOOD, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; + + GfxDrawSprite(dpi, ImageId(imageId), screenCoords); } - // Paid on drinks + void OnUpdateFinance() { - auto ft = Formatter(); - ft.Add(peep->PaidOnDrink); - ft.Add(peep->AmountOfDrinks); - if (peep->AmountOfDrinks != 1) - { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_DRINK_PLURAL, ft); - } - else - { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_DRINK, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; + frame_no++; + + WidgetInvalidate(*this, WIDX_TAB_2); + WidgetInvalidate(*this, WIDX_TAB_4); } - // Paid on souvenirs + + void OnDrawFinance(DrawPixelInfo& dpi) { - auto ft = Formatter(); - ft.Add(peep->PaidOnSouvenirs); - ft.Add(peep->AmountOfSouvenirs); - if (peep->AmountOfSouvenirs != 1) + DrawWidgets(dpi); + OverviewTabDraw(dpi); + StatsTabDraw(dpi); + RidesTabDraw(dpi); + FinanceTabDraw(dpi); + ThoughtsTabDraw(dpi); + InventoryTabDraw(dpi); + DebugTabDraw(dpi); + + const auto peep = GetGuest(); + if (peep == nullptr) { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_SOUVENIR_PLURAL, ft); + return; } - else + + // cx dx + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + + // Cash in pocket { - DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_SOUVENIR, ft); + auto ft = Formatter(); + ft.Add(peep->CashInPocket); + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_CASH_IN_POCKET, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + // Cash spent + { + auto ft = Formatter(); + ft.Add(peep->CashSpent); + DrawTextBasic(dpi, screenCoords, STR_GUEST_STAT_CASH_SPENT, ft); + screenCoords.y += LIST_ROW_HEIGHT * 2; + } + + GfxFillRectInset( + dpi, { screenCoords - ScreenCoordsXY{ 0, 6 }, screenCoords + ScreenCoordsXY{ 179, -5 } }, colours[1], + INSET_RECT_FLAG_BORDER_INSET); + + // Paid to enter + { + auto ft = Formatter(); + ft.Add(peep->PaidToEnter); + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_ENTRANCE_FEE, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + // Paid on rides + { + auto ft = Formatter(); + ft.Add(peep->PaidOnRides); + ft.Add(peep->GuestNumRides); + if (peep->GuestNumRides != 1) + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_RIDE_PLURAL, ft); + } + else + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_RIDE, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + } + // Paid on food + { + auto ft = Formatter(); + ft.Add(peep->PaidOnFood); + ft.Add(peep->AmountOfFood); + if (peep->AmountOfFood != 1) + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_FOOD_PLURAL, ft); + } + else + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_FOOD, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + } + + // Paid on drinks + { + auto ft = Formatter(); + ft.Add(peep->PaidOnDrink); + ft.Add(peep->AmountOfDrinks); + if (peep->AmountOfDrinks != 1) + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_DRINK_PLURAL, ft); + } + else + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_DRINK, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + } + // Paid on souvenirs + { + auto ft = Formatter(); + ft.Add(peep->PaidOnSouvenirs); + ft.Add(peep->AmountOfSouvenirs); + if (peep->AmountOfSouvenirs != 1) + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_SOUVENIR_PLURAL, ft); + } + else + { + DrawTextBasic(dpi, screenCoords, STR_GUEST_EXPENSES_SOUVENIR, ft); + } } } - } #pragma endregion #pragma region Thoughts - void ThoughtsTabDraw(DrawPixelInfo& dpi) - { - if (WidgetIsDisabled(*this, WIDX_TAB_5)) - return; - - const auto& widget = widgets[WIDX_TAB_5]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - - int32_t imageId = SPR_TAB_THOUGHTS_0; - - if (page == WINDOW_GUEST_THOUGHTS) + void ThoughtsTabDraw(DrawPixelInfo& dpi) { - imageId += (frame_no / 2) & 0x7; - } - - GfxDrawSprite(dpi, ImageId(imageId), screenCoords); - } - - void OnUpdateThoughts() - { - frame_no++; - - WidgetInvalidate(*this, WIDX_TAB_2); - WidgetInvalidate(*this, WIDX_TAB_5); - - auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - if (peep->WindowInvalidateFlags & PEEP_INVALIDATE_PEEP_THOUGHTS) - { - peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_THOUGHTS; - Invalidate(); - } - } - - void OnDrawThoughts(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - OverviewTabDraw(dpi); - StatsTabDraw(dpi); - RidesTabDraw(dpi); - FinanceTabDraw(dpi); - ThoughtsTabDraw(dpi); - InventoryTabDraw(dpi); - DebugTabDraw(dpi); - - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - - // cx dx - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - - DrawTextBasic(dpi, screenCoords, STR_GUEST_RECENT_THOUGHTS_LABEL); - - screenCoords.y += 10; - for (const auto& thought : peep->Thoughts) - { - if (thought.type == PeepThoughtType::None) + if (WidgetIsDisabled(*this, WIDX_TAB_5)) return; - if (thought.freshness == 0) - continue; - int32_t widgWidth = widgets[WIDX_PAGE_BACKGROUND].right - widgets[WIDX_PAGE_BACKGROUND].left - 8; + const auto& widget = widgets[WIDX_TAB_5]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - auto ft = Formatter(); - PeepThoughtSetFormatArgs(&thought, ft); - screenCoords.y += DrawTextWrapped(dpi, screenCoords, widgWidth, STR_BLACK_STRING, ft, { FontStyle::Small }); + int32_t imageId = SPR_TAB_THOUGHTS_0; - // If this is the last visible line end drawing. - if (screenCoords.y > windowPos.y + widgets[WIDX_PAGE_BACKGROUND].bottom - 32) - return; + if (page == WINDOW_GUEST_THOUGHTS) + { + imageId += (frame_no / 2) & 0x7; + } + + GfxDrawSprite(dpi, ImageId(imageId), screenCoords); + } + + void OnUpdateThoughts() + { + frame_no++; + + WidgetInvalidate(*this, WIDX_TAB_2); + WidgetInvalidate(*this, WIDX_TAB_5); + + auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + if (peep->WindowInvalidateFlags & PEEP_INVALIDATE_PEEP_THOUGHTS) + { + peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_THOUGHTS; + Invalidate(); + } + } + + void OnDrawThoughts(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + OverviewTabDraw(dpi); + StatsTabDraw(dpi); + RidesTabDraw(dpi); + FinanceTabDraw(dpi); + ThoughtsTabDraw(dpi); + InventoryTabDraw(dpi); + DebugTabDraw(dpi); + + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + + // cx dx + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + + DrawTextBasic(dpi, screenCoords, STR_GUEST_RECENT_THOUGHTS_LABEL); + + screenCoords.y += 10; + for (const auto& thought : peep->Thoughts) + { + if (thought.type == PeepThoughtType::None) + return; + if (thought.freshness == 0) + continue; + + int32_t widgWidth = widgets[WIDX_PAGE_BACKGROUND].right - widgets[WIDX_PAGE_BACKGROUND].left - 8; + + auto ft = Formatter(); + PeepThoughtSetFormatArgs(&thought, ft); + screenCoords.y += DrawTextWrapped(dpi, screenCoords, widgWidth, STR_BLACK_STRING, ft, { FontStyle::Small }); + + // If this is the last visible line end drawing. + if (screenCoords.y > windowPos.y + widgets[WIDX_PAGE_BACKGROUND].bottom - 32) + return; + } } - } #pragma endregion #pragma region Inventory - void InventoryTabDraw(DrawPixelInfo& dpi) - { - if (WidgetIsDisabled(*this, WIDX_TAB_6)) - return; - - const auto& widget = widgets[WIDX_TAB_6]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - - GfxDrawSprite(dpi, ImageId(SPR_TAB_GUEST_INVENTORY), screenCoords); - } - - void OnUpdateInventory() - { - frame_no++; - - WidgetInvalidate(*this, WIDX_TAB_2); - WidgetInvalidate(*this, WIDX_TAB_6); - - auto peep = GetGuest(); - if (peep == nullptr) + void InventoryTabDraw(DrawPixelInfo& dpi) { - return; + if (WidgetIsDisabled(*this, WIDX_TAB_6)) + return; + + const auto& widget = widgets[WIDX_TAB_6]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; + + GfxDrawSprite(dpi, ImageId(SPR_TAB_GUEST_INVENTORY), screenCoords); } - if (peep->WindowInvalidateFlags & PEEP_INVALIDATE_PEEP_INVENTORY) + + void OnUpdateInventory() { - peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_INVENTORY; - Invalidate(); + frame_no++; + + WidgetInvalidate(*this, WIDX_TAB_2); + WidgetInvalidate(*this, WIDX_TAB_6); + + auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + if (peep->WindowInvalidateFlags & PEEP_INVALIDATE_PEEP_INVENTORY) + { + peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_PEEP_INVENTORY; + Invalidate(); + } } - } - std::pair InventoryFormatItem(Guest& guest, ShopItem item) const - { - auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - auto parkName = park.Name.c_str(); - - // Default arguments - auto ft = Formatter(); - ft.Add(GetShopItemDescriptor(item).Image); - ft.Add(GetShopItemDescriptor(item).Naming.Display); - ft.Add(STR_STRING); - ft.Add(parkName); - - // Special overrides - Ride* invRide{}; - switch (item) + std::pair InventoryFormatItem(Guest& guest, ShopItem item) const { - case ShopItem::Balloon: - ft.Rewind(); - ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.BalloonColour)).ToUInt32()); - break; - case ShopItem::Photo: - invRide = GetRide(guest.Photo1RideRef); - if (invRide != nullptr) - { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + + // Default arguments + auto ft = Formatter(); + ft.Add(GetShopItemDescriptor(item).Image); + ft.Add(GetShopItemDescriptor(item).Naming.Display); + ft.Add(STR_STRING); + ft.Add(parkName); + + // Special overrides + Ride* invRide{}; + switch (item) + { + case ShopItem::Balloon: ft.Rewind(); - ft.Increment(6); - invRide->FormatNameTo(ft); - } - - break; - case ShopItem::Umbrella: - ft.Rewind(); - ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.UmbrellaColour)).ToUInt32()); - break; - case ShopItem::Voucher: - switch (guest.VoucherType) - { - case VOUCHER_TYPE_PARK_ENTRY_FREE: + ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.BalloonColour)).ToUInt32()); + break; + case ShopItem::Photo: + invRide = GetRide(guest.Photo1RideRef); + if (invRide != nullptr) + { ft.Rewind(); ft.Increment(6); - ft.Add(STR_PEEP_INVENTORY_VOUCHER_PARK_ENTRY_FREE); - ft.Add(STR_STRING); - ft.Add(parkName); - break; - case VOUCHER_TYPE_RIDE_FREE: - invRide = GetRide(guest.VoucherRideId); - if (invRide != nullptr) - { + invRide->FormatNameTo(ft); + } + + break; + case ShopItem::Umbrella: + ft.Rewind(); + ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.UmbrellaColour)).ToUInt32()); + break; + case ShopItem::Voucher: + switch (guest.VoucherType) + { + case VOUCHER_TYPE_PARK_ENTRY_FREE: ft.Rewind(); ft.Increment(6); - ft.Add(STR_PEEP_INVENTORY_VOUCHER_RIDE_FREE); - invRide->FormatNameTo(ft); - } - break; - case VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE: + ft.Add(STR_PEEP_INVENTORY_VOUCHER_PARK_ENTRY_FREE); + ft.Add(STR_STRING); + ft.Add(parkName); + break; + case VOUCHER_TYPE_RIDE_FREE: + invRide = GetRide(guest.VoucherRideId); + if (invRide != nullptr) + { + ft.Rewind(); + ft.Increment(6); + ft.Add(STR_PEEP_INVENTORY_VOUCHER_RIDE_FREE); + invRide->FormatNameTo(ft); + } + break; + case VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE: + ft.Rewind(); + ft.Increment(6); + ft.Add(STR_PEEP_INVENTORY_VOUCHER_PARK_ENTRY_HALF_PRICE); + ft.Add(STR_STRING); + ft.Add(parkName); + break; + case VOUCHER_TYPE_FOOD_OR_DRINK_FREE: + ft.Rewind(); + ft.Increment(6); + ft.Add(STR_PEEP_INVENTORY_VOUCHER_FOOD_OR_DRINK_FREE); + ft.Add(GetShopItemDescriptor(guest.VoucherShopItem).Naming.Singular); + break; + } + break; + case ShopItem::Hat: + ft.Rewind(); + ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.HatColour)).ToUInt32()); + break; + case ShopItem::TShirt: + ft.Rewind(); + ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.TshirtColour)).ToUInt32()); + break; + case ShopItem::Photo2: + invRide = GetRide(guest.Photo2RideRef); + if (invRide != nullptr) + { ft.Rewind(); ft.Increment(6); - ft.Add(STR_PEEP_INVENTORY_VOUCHER_PARK_ENTRY_HALF_PRICE); - ft.Add(STR_STRING); - ft.Add(parkName); - break; - case VOUCHER_TYPE_FOOD_OR_DRINK_FREE: + invRide->FormatNameTo(ft); + } + break; + case ShopItem::Photo3: + invRide = GetRide(guest.Photo3RideRef); + if (invRide != nullptr) + { ft.Rewind(); ft.Increment(6); - ft.Add(STR_PEEP_INVENTORY_VOUCHER_FOOD_OR_DRINK_FREE); - ft.Add(GetShopItemDescriptor(guest.VoucherShopItem).Naming.Singular); - break; - } - break; - case ShopItem::Hat: - ft.Rewind(); - ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.HatColour)).ToUInt32()); - break; - case ShopItem::TShirt: - ft.Rewind(); - ft.Add(ImageId(GetShopItemDescriptor(item).Image, (guest.TshirtColour)).ToUInt32()); - break; - case ShopItem::Photo2: - invRide = GetRide(guest.Photo2RideRef); - if (invRide != nullptr) - { - ft.Rewind(); - ft.Increment(6); - invRide->FormatNameTo(ft); - } - break; - case ShopItem::Photo3: - invRide = GetRide(guest.Photo3RideRef); - if (invRide != nullptr) - { - ft.Rewind(); - ft.Increment(6); - invRide->FormatNameTo(ft); - } - break; - case ShopItem::Photo4: - invRide = GetRide(guest.Photo4RideRef); - if (invRide != nullptr) - { - ft.Rewind(); - ft.Increment(6); - invRide->FormatNameTo(ft); - } - break; - default: - // This switch handles only items visible in peep window - break; + invRide->FormatNameTo(ft); + } + break; + case ShopItem::Photo4: + invRide = GetRide(guest.Photo4RideRef); + if (invRide != nullptr) + { + ft.Rewind(); + ft.Increment(6); + invRide->FormatNameTo(ft); + } + break; + default: + // This switch handles only items visible in peep window + break; + } + + return std::make_pair(STR_GUEST_ITEM_FORMAT, ft); } - return std::make_pair(STR_GUEST_ITEM_FORMAT, ft); - } - - void OnDrawInventory(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - OverviewTabDraw(dpi); - StatsTabDraw(dpi); - RidesTabDraw(dpi); - FinanceTabDraw(dpi); - ThoughtsTabDraw(dpi); - InventoryTabDraw(dpi); - DebugTabDraw(dpi); - - const auto guest = GetGuest(); - if (guest == nullptr) + void OnDrawInventory(DrawPixelInfo& dpi) { - return; + DrawWidgets(dpi); + OverviewTabDraw(dpi); + StatsTabDraw(dpi); + RidesTabDraw(dpi); + FinanceTabDraw(dpi); + ThoughtsTabDraw(dpi); + InventoryTabDraw(dpi); + DebugTabDraw(dpi); + + const auto guest = GetGuest(); + if (guest == nullptr) + { + return; + } + + Widget* pageBackgroundWidget = &widgets[WIDX_PAGE_BACKGROUND]; + auto screenCoords = windowPos + ScreenCoordsXY{ pageBackgroundWidget->left + 4, pageBackgroundWidget->top + 2 }; + int32_t itemNameWidth = pageBackgroundWidget->width() - 8; + + int32_t maxY = windowPos.y + height - 22; + int32_t numItems = 0; + + DrawTextBasic(dpi, screenCoords, STR_CARRYING); + screenCoords.y += 10; + + for (ShopItem item = ShopItem::Balloon; item < ShopItem::Count; item++) + { + if (screenCoords.y >= maxY) + break; + if (!guest->HasItem(item)) + continue; + + auto [stringId, ft] = InventoryFormatItem(*guest, item); + screenCoords.y += DrawTextWrapped(dpi, screenCoords, itemNameWidth, stringId, ft); + numItems++; + } + + if (numItems == 0) + { + DrawTextBasic(dpi, screenCoords, STR_NOTHING); + } } - - Widget* pageBackgroundWidget = &widgets[WIDX_PAGE_BACKGROUND]; - auto screenCoords = windowPos + ScreenCoordsXY{ pageBackgroundWidget->left + 4, pageBackgroundWidget->top + 2 }; - int32_t itemNameWidth = pageBackgroundWidget->width() - 8; - - int32_t maxY = windowPos.y + height - 22; - int32_t numItems = 0; - - DrawTextBasic(dpi, screenCoords, STR_CARRYING); - screenCoords.y += 10; - - for (ShopItem item = ShopItem::Balloon; item < ShopItem::Count; item++) - { - if (screenCoords.y >= maxY) - break; - if (!guest->HasItem(item)) - continue; - - auto [stringId, ft] = InventoryFormatItem(*guest, item); - screenCoords.y += DrawTextWrapped(dpi, screenCoords, itemNameWidth, stringId, ft); - numItems++; - } - - if (numItems == 0) - { - DrawTextBasic(dpi, screenCoords, STR_NOTHING); - } - } #pragma endregion #pragma region Debug - void DebugTabDraw(DrawPixelInfo& dpi) - { - if (WidgetIsDisabled(*this, WIDX_TAB_7)) - return; - - const auto& widget = widgets[WIDX_TAB_7]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - - int32_t imageId = SPR_TAB_GEARS_0; - if (page == WINDOW_GUEST_DEBUG) + void DebugTabDraw(DrawPixelInfo& dpi) { - imageId += (frame_no / 2) & 0x3; - } + if (WidgetIsDisabled(*this, WIDX_TAB_7)) + return; - GfxDrawSprite(dpi, ImageId(imageId), screenCoords); - } + const auto& widget = widgets[WIDX_TAB_7]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - void OnUpdateDebug() - { - frame_no++; - Invalidate(); - } - - void OnDrawDebug(DrawPixelInfo& dpi) - { - char buffer[512]{}; - char buffer2[512]{}; - - DrawWidgets(dpi); - OverviewTabDraw(dpi); - StatsTabDraw(dpi); - RidesTabDraw(dpi); - FinanceTabDraw(dpi); - ThoughtsTabDraw(dpi); - InventoryTabDraw(dpi); - DebugTabDraw(dpi); - - const auto peep = GetGuest(); - if (peep == nullptr) - { - return; - } - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - { - auto ft = Formatter(); - ft.Add(peep->Id); - DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_SPRITE_INDEX, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; - { - auto ft = Formatter(); - ft.Add(peep->x); - ft.Add(peep->y); - ft.Add(peep->z); - DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_POSITION, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; - { - auto ft = Formatter(); - ft.Add(peep->NextLoc.x); - ft.Add(peep->NextLoc.y); - ft.Add(peep->NextLoc.z); - OpenRCT2::FormatStringLegacy(buffer, sizeof(buffer), STR_PEEP_DEBUG_NEXT, ft.Data()); - if (peep->GetNextIsSurface()) + int32_t imageId = SPR_TAB_GEARS_0; + if (page == WINDOW_GUEST_DEBUG) { - OpenRCT2::FormatStringLegacy(buffer2, sizeof(buffer2), STR_PEEP_DEBUG_NEXT_SURFACE, nullptr); - SafeStrCat(buffer, buffer2, sizeof(buffer)); + imageId += (frame_no / 2) & 0x3; } - if (peep->GetNextIsSloped()) - { - auto ft2 = Formatter(); - ft2.Add(peep->GetNextDirection()); - OpenRCT2::FormatStringLegacy(buffer2, sizeof(buffer2), STR_PEEP_DEBUG_NEXT_SLOPE, ft2.Data()); - SafeStrCat(buffer, buffer2, sizeof(buffer)); - } - GfxDrawString(dpi, screenCoords, buffer, {}); - } - screenCoords.y += LIST_ROW_HEIGHT; - { - auto ft = Formatter(); - ft.Add(peep->DestinationX); - ft.Add(peep->DestinationY); - ft.Add(peep->DestinationTolerance); - DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_DEST, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; - { - auto ft = Formatter(); - ft.Add(peep->PathfindGoal.x); - ft.Add(peep->PathfindGoal.y); - ft.Add(peep->PathfindGoal.z); - ft.Add(peep->PathfindGoal.direction); - DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_PATHFIND_GOAL, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; - DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_PATHFIND_HISTORY); - screenCoords.y += LIST_ROW_HEIGHT; - screenCoords.x += 10; - for (auto& point : peep->PathfindHistory) + GfxDrawSprite(dpi, ImageId(imageId), screenCoords); + } + + void OnUpdateDebug() { - auto ft = Formatter(); - ft.Add(point.x); - ft.Add(point.y); - ft.Add(point.z); - ft.Add(point.direction); - DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_PATHFIND_HISTORY_ITEM, ft); + frame_no++; + Invalidate(); + } + + void OnDrawDebug(DrawPixelInfo& dpi) + { + char buffer[512]{}; + char buffer2[512]{}; + + DrawWidgets(dpi); + OverviewTabDraw(dpi); + StatsTabDraw(dpi); + RidesTabDraw(dpi); + FinanceTabDraw(dpi); + ThoughtsTabDraw(dpi); + InventoryTabDraw(dpi); + DebugTabDraw(dpi); + + const auto peep = GetGuest(); + if (peep == nullptr) + { + return; + } + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + { + auto ft = Formatter(); + ft.Add(peep->Id); + DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_SPRITE_INDEX, ft); + } screenCoords.y += LIST_ROW_HEIGHT; + { + auto ft = Formatter(); + ft.Add(peep->x); + ft.Add(peep->y); + ft.Add(peep->z); + DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_POSITION, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + { + auto ft = Formatter(); + ft.Add(peep->NextLoc.x); + ft.Add(peep->NextLoc.y); + ft.Add(peep->NextLoc.z); + OpenRCT2::FormatStringLegacy(buffer, sizeof(buffer), STR_PEEP_DEBUG_NEXT, ft.Data()); + if (peep->GetNextIsSurface()) + { + OpenRCT2::FormatStringLegacy(buffer2, sizeof(buffer2), STR_PEEP_DEBUG_NEXT_SURFACE, nullptr); + SafeStrCat(buffer, buffer2, sizeof(buffer)); + } + if (peep->GetNextIsSloped()) + { + auto ft2 = Formatter(); + ft2.Add(peep->GetNextDirection()); + OpenRCT2::FormatStringLegacy(buffer2, sizeof(buffer2), STR_PEEP_DEBUG_NEXT_SLOPE, ft2.Data()); + SafeStrCat(buffer, buffer2, sizeof(buffer)); + } + GfxDrawString(dpi, screenCoords, buffer, {}); + } + screenCoords.y += LIST_ROW_HEIGHT; + { + auto ft = Formatter(); + ft.Add(peep->DestinationX); + ft.Add(peep->DestinationY); + ft.Add(peep->DestinationTolerance); + DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_DEST, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + { + auto ft = Formatter(); + ft.Add(peep->PathfindGoal.x); + ft.Add(peep->PathfindGoal.y); + ft.Add(peep->PathfindGoal.z); + ft.Add(peep->PathfindGoal.direction); + DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_PATHFIND_GOAL, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_PATHFIND_HISTORY); + screenCoords.y += LIST_ROW_HEIGHT; + + screenCoords.x += 10; + for (auto& point : peep->PathfindHistory) + { + auto ft = Formatter(); + ft.Add(point.x); + ft.Add(point.y); + ft.Add(point.z); + ft.Add(point.direction); + DrawTextBasic(dpi, screenCoords, STR_PEEP_DEBUG_PATHFIND_HISTORY_ITEM, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + screenCoords.x -= 10; } - screenCoords.x -= 10; - } #pragma endregion -}; + }; -/** - * - * rct2: 0x006989E9 - * - */ -WindowBase* WindowGuestOpen(Peep* peep) -{ - if (peep == nullptr) + /** + * + * rct2: 0x006989E9 + * + */ + WindowBase* WindowGuestOpen(Peep* peep) { - return nullptr; - } - if (peep->Is()) - { - return WindowStaffOpen(peep); - } - - auto* window = static_cast(WindowBringToFrontByNumber(WindowClass::Peep, peep->Id.ToUnderlying())); - if (window == nullptr) - { - int32_t windowWidth = 192; - if (gConfigGeneral.DebuggingTools) - windowWidth += TabWidth; - - window = WindowCreate(WindowClass::Peep, windowWidth, 157, WF_RESIZABLE); - if (window == nullptr) + if (peep == nullptr) { return nullptr; } + if (peep->Is()) + { + return WindowStaffOpen(peep); + } + + auto* window = static_cast(WindowBringToFrontByNumber(WindowClass::Peep, peep->Id.ToUnderlying())); + if (window == nullptr) + { + int32_t windowWidth = 192; + if (gConfigGeneral.DebuggingTools) + windowWidth += TabWidth; + + window = WindowCreate(WindowClass::Peep, windowWidth, 157, WF_RESIZABLE); + if (window == nullptr) + { + return nullptr; + } + } + + window->Init(peep->Id); + + return window; } - - window->Init(peep->Id); - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/GuestList.cpp b/src/openrct2-ui/windows/GuestList.cpp index 60e0515ef7..6ab7db86c1 100644 --- a/src/openrct2-ui/windows/GuestList.cpp +++ b/src/openrct2-ui/windows/GuestList.cpp @@ -29,31 +29,31 @@ #include #include -using namespace OpenRCT2; - -static constexpr StringId WINDOW_TITLE = STR_GUESTS; -static constexpr int32_t WH = 330; -static constexpr int32_t WW = 350; - -enum WindowGuestListWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_TAB_CONTENT_PANEL, - WIDX_PAGE_DROPDOWN, - WIDX_PAGE_DROPDOWN_BUTTON, - WIDX_INFO_TYPE_DROPDOWN, - WIDX_INFO_TYPE_DROPDOWN_BUTTON, - WIDX_MAP, - WIDX_FILTER_BY_NAME, - WIDX_TRACKING, - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_GUEST_LIST -}; + static constexpr StringId WINDOW_TITLE = STR_GUESTS; + static constexpr int32_t WH = 330; + static constexpr int32_t WW = 350; -// clang-format off + enum WindowGuestListWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_TAB_CONTENT_PANEL, + WIDX_PAGE_DROPDOWN, + WIDX_PAGE_DROPDOWN_BUTTON, + WIDX_INFO_TYPE_DROPDOWN, + WIDX_INFO_TYPE_DROPDOWN_BUTTON, + WIDX_MAP, + WIDX_FILTER_BY_NAME, + WIDX_TRACKING, + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_GUEST_LIST + }; + + // clang-format off static Widget window_guest_list_widgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget({ 0, 43}, {350, 287}, WindowWidgetType::Resize, WindowColour::Secondary ), // tab content panel @@ -69,927 +69,931 @@ static Widget window_guest_list_widgets[] = { MakeWidget({ 3, 72}, {344, 255}, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_BOTH ), // guest list kWidgetsEnd, }; -// clang-format on + // clang-format on -class GuestListWindow final : public Window -{ -private: - enum class TabId + class GuestListWindow final : public Window { - Individual, - Summarised - }; - - enum class GuestViewType - { - Actions, - Thoughts, - }; - - enum class GuestFilterType - { - Guests, - GuestsThinking, - GuestsThinkingAbout, - }; - - struct FilterArguments - { - uint8_t args[12]{}; - - StringId GetFirstStringId() + private: + enum class TabId { - StringId firstStrId{}; - std::memcpy(&firstStrId, args, sizeof(firstStrId)); - return firstStrId; - } + Individual, + Summarised + }; - bool operator==(const FilterArguments& other) const + enum class GuestViewType { - return std::memcmp(args, other.args, sizeof(args)) == 0; - } - bool operator!=(const FilterArguments& other) const + Actions, + Thoughts, + }; + + enum class GuestFilterType { - return !(*this == other); - } - }; + Guests, + GuestsThinking, + GuestsThinkingAbout, + }; - struct GuestGroup - { - size_t NumGuests{}; - FilterArguments Arguments; - uint8_t Faces[58]{}; - }; - - struct GuestItem - { - using CompareFunc = bool (*)(const GuestItem&, const GuestItem&); - - EntityId Id; - char Name[256]; - }; - - static constexpr uint8_t SUMMARISED_GUEST_ROW_HEIGHT = SCROLLABLE_ROW_HEIGHT + 11; - static constexpr auto GUESTS_PER_PAGE = 2000; - static constexpr const auto GUEST_PAGE_HEIGHT = GUESTS_PER_PAGE * SCROLLABLE_ROW_HEIGHT; - static constexpr size_t MaxGroups = 240; - - TabId _selectedTab{}; - GuestViewType _selectedView{}; - bool _trackingOnly{}; - std::optional _selectedFilter; - FilterArguments _filterArguments; - std::string _filterName; - - size_t _numPages{}; - size_t _selectedPage{}; - - GuestViewType _lastFindGroupsSelectedView{}; - uint32_t _lastFindGroupsTick{}; - uint32_t _lastFindGroupsWait{}; - std::vector _groups; - - std::vector _guestList; - std::optional _highlightedIndex; - - uint32_t _tabAnimationIndex{}; - -public: - void OnOpen() override - { - widgets = window_guest_list_widgets; - WindowInitScrollWidgets(*this); - - _selectedTab = TabId::Summarised; - _selectedView = GuestViewType::Thoughts; - _numPages = 1; - widgets[WIDX_TRACKING].type = WindowWidgetType::FlatBtn; - widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::FlatBtn; - widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; - min_width = 350; - min_height = 330; - max_width = 500; - max_height = 450; - RefreshList(); - } - - void SetFilter(GuestListFilterType type, int32_t index) - { - _selectedPage = 0; - _numPages = 1; - _trackingOnly = false; - _filterArguments = {}; - - Formatter ft(_filterArguments.args); - - switch (type) + struct FilterArguments { - case GuestListFilterType::GuestsOnRide: + uint8_t args[12]{}; + + StringId GetFirstStringId() { - auto guestRide = GetRide(RideId::FromUnderlying(index)); - if (guestRide != nullptr) - { - ft.Add( - guestRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IN_RIDE) ? STR_IN_RIDE : STR_ON_RIDE); - guestRide->FormatNameTo(ft); - - _selectedFilter = GuestFilterType::Guests; - _highlightedIndex = {}; - _selectedTab = TabId::Individual; - _selectedView = GuestViewType::Thoughts; - } - break; + StringId firstStrId{}; + std::memcpy(&firstStrId, args, sizeof(firstStrId)); + return firstStrId; } - case GuestListFilterType::GuestsInQueue: - { - auto guestRide = GetRide(RideId::FromUnderlying(index)); - if (guestRide != nullptr) - { - ft.Add(STR_QUEUING_FOR); - guestRide->FormatNameTo(ft); - _selectedFilter = GuestFilterType::Guests; - _highlightedIndex = {}; - _selectedTab = TabId::Individual; - _selectedView = GuestViewType::Thoughts; - } - break; - } - case GuestListFilterType::GuestsThinkingAboutRide: + bool operator==(const FilterArguments& other) const { - auto guestRide = GetRide(RideId::FromUnderlying(index)); - if (guestRide != nullptr) + return std::memcmp(args, other.args, sizeof(args)) == 0; + } + bool operator!=(const FilterArguments& other) const + { + return !(*this == other); + } + }; + + struct GuestGroup + { + size_t NumGuests{}; + FilterArguments Arguments; + uint8_t Faces[58]{}; + }; + + struct GuestItem + { + using CompareFunc = bool (*)(const GuestItem&, const GuestItem&); + + EntityId Id; + char Name[256]; + }; + + static constexpr uint8_t SUMMARISED_GUEST_ROW_HEIGHT = SCROLLABLE_ROW_HEIGHT + 11; + static constexpr auto GUESTS_PER_PAGE = 2000; + static constexpr const auto GUEST_PAGE_HEIGHT = GUESTS_PER_PAGE * SCROLLABLE_ROW_HEIGHT; + static constexpr size_t MaxGroups = 240; + + TabId _selectedTab{}; + GuestViewType _selectedView{}; + bool _trackingOnly{}; + std::optional _selectedFilter; + FilterArguments _filterArguments; + std::string _filterName; + + size_t _numPages{}; + size_t _selectedPage{}; + + GuestViewType _lastFindGroupsSelectedView{}; + uint32_t _lastFindGroupsTick{}; + uint32_t _lastFindGroupsWait{}; + std::vector _groups; + + std::vector _guestList; + std::optional _highlightedIndex; + + uint32_t _tabAnimationIndex{}; + + public: + void OnOpen() override + { + widgets = window_guest_list_widgets; + WindowInitScrollWidgets(*this); + + _selectedTab = TabId::Summarised; + _selectedView = GuestViewType::Thoughts; + _numPages = 1; + widgets[WIDX_TRACKING].type = WindowWidgetType::FlatBtn; + widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::FlatBtn; + widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; + min_width = 350; + min_height = 330; + max_width = 500; + max_height = 450; + RefreshList(); + } + + void SetFilter(GuestListFilterType type, int32_t index) + { + _selectedPage = 0; + _numPages = 1; + _trackingOnly = false; + _filterArguments = {}; + + Formatter ft(_filterArguments.args); + + switch (type) + { + case GuestListFilterType::GuestsOnRide: { - ft.Add(STR_NONE); - guestRide->FormatNameTo(ft); + auto guestRide = GetRide(RideId::FromUnderlying(index)); + if (guestRide != nullptr) + { + ft.Add( + guestRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IN_RIDE) ? STR_IN_RIDE : STR_ON_RIDE); + guestRide->FormatNameTo(ft); + + _selectedFilter = GuestFilterType::Guests; + _highlightedIndex = {}; + _selectedTab = TabId::Individual; + _selectedView = GuestViewType::Thoughts; + } + break; + } + case GuestListFilterType::GuestsInQueue: + { + auto guestRide = GetRide(RideId::FromUnderlying(index)); + if (guestRide != nullptr) + { + ft.Add(STR_QUEUING_FOR); + guestRide->FormatNameTo(ft); + + _selectedFilter = GuestFilterType::Guests; + _highlightedIndex = {}; + _selectedTab = TabId::Individual; + _selectedView = GuestViewType::Thoughts; + } + break; + } + case GuestListFilterType::GuestsThinkingAboutRide: + { + auto guestRide = GetRide(RideId::FromUnderlying(index)); + if (guestRide != nullptr) + { + ft.Add(STR_NONE); + guestRide->FormatNameTo(ft); + + _selectedFilter = GuestFilterType::GuestsThinking; + _highlightedIndex = {}; + _selectedTab = TabId::Individual; + _selectedView = GuestViewType::Thoughts; + } + break; + } + case GuestListFilterType::GuestsThinkingX: + { + ft.Add(PeepThoughts[index & 0xFF]); _selectedFilter = GuestFilterType::GuestsThinking; _highlightedIndex = {}; _selectedTab = TabId::Individual; _selectedView = GuestViewType::Thoughts; - } - break; - } - case GuestListFilterType::GuestsThinkingX: - { - ft.Add(PeepThoughts[index & 0xFF]); - - _selectedFilter = GuestFilterType::GuestsThinking; - _highlightedIndex = {}; - _selectedTab = TabId::Individual; - _selectedView = GuestViewType::Thoughts; - break; - } - } - - RefreshList(); - } - - void OnResize() override - { - min_width = 350; - min_height = 330; - if (width < min_width) - { - Invalidate(); - width = min_width; - } - if (height < min_height) - { - Invalidate(); - height = min_height; - } - } - - void OnUpdate() override - { - if (_lastFindGroupsWait != 0) - { - _lastFindGroupsWait--; - } - - // Current tab image animation - _tabAnimationIndex++; - if (_tabAnimationIndex >= (_selectedTab == TabId::Individual ? 24uL : 32uL)) - _tabAnimationIndex = 0; - InvalidateWidget(WIDX_TAB_1 + static_cast(_selectedTab)); - - gWindowMapFlashingFlags |= MapFlashingFlags::GuestListOpen; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_MAP: - ContextOpenWindow(WindowClass::Map); - break; - case WIDX_TRACKING: - _trackingOnly = !_trackingOnly; - SetWidgetPressed(WIDX_TRACKING, _trackingOnly); - Invalidate(); - scrolls[0].v_top = 0; - RefreshList(); - break; - case WIDX_FILTER_BY_NAME: - if (!_filterName.empty()) - { - // Unset the search filter. - _filterName.clear(); - SetWidgetPressed(WIDX_FILTER_BY_NAME, false); - RefreshList(); - } - else - { - WindowTextInputRawOpen( - this, WIDX_FILTER_BY_NAME, STR_GUESTS_FILTER_BY_NAME, STR_GUESTS_ENTER_NAME_TO_SEARCH, {}, - _filterName.c_str(), 32); - } - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_TAB_1: - case WIDX_TAB_2: - { - if (_selectedFilter && _selectedTab == static_cast(widgetIndex - WIDX_TAB_1)) break; - _selectedTab = static_cast(widgetIndex - WIDX_TAB_1); - _selectedPage = 0; - _numPages = 1; - widgets[WIDX_TRACKING].type = WindowWidgetType::Empty; - if (_selectedTab == TabId::Summarised) - { - widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::Empty; - SetWidgetPressed(WIDX_FILTER_BY_NAME, false); - _filterName.clear(); } - else if (_selectedTab == TabId::Individual) - { - widgets[WIDX_TRACKING].type = WindowWidgetType::FlatBtn; - widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::FlatBtn; - } - widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; - _tabAnimationIndex = 0; - _selectedFilter = {}; - Invalidate(); - scrolls[0].v_top = 0; - RefreshList(); - break; } - case WIDX_PAGE_DROPDOWN_BUTTON: - { - auto* widget = &widgets[widgetIndex - 1]; - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, _numPages, widget->width() - 3); - - for (size_t i = 0; i < _numPages; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - uint16_t* args = reinterpret_cast(&gDropdownItems[i].Args); - args[0] = STR_PAGE_X; - args[1] = static_cast(i + 1); - } - Dropdown::SetChecked(static_cast(_selectedPage), true); - break; - } - case WIDX_INFO_TYPE_DROPDOWN_BUTTON: - { - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = GetViewName(GuestViewType::Actions); - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Args = GetViewName(GuestViewType::Thoughts); - - auto* widget = &widgets[widgetIndex - 1]; - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, 2, widget->width() - 3); - - Dropdown::SetChecked(static_cast(_selectedView), true); - break; - } - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (dropdownIndex == -1) - { - return; - } - switch (widgetIndex) - { - case WIDX_PAGE_DROPDOWN_BUTTON: - _selectedPage = dropdownIndex; - Invalidate(); - break; - case WIDX_INFO_TYPE_DROPDOWN_BUTTON: - _selectedView = static_cast(dropdownIndex); - Invalidate(); - break; - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (!text.empty()) - { - _filterName = text; - SetWidgetPressed(WIDX_FILTER_BY_NAME, true); RefreshList(); } - } - void OnPrepareDraw() override - { - SetWidgetPressed(WIDX_TAB_1, false); - SetWidgetPressed(WIDX_TAB_2, false); - SetWidgetPressed(WIDX_TAB_1 + static_cast(_selectedTab), true); - - widgets[WIDX_INFO_TYPE_DROPDOWN].text = GetViewName(_selectedView); - widgets[WIDX_MAP].type = WindowWidgetType::Empty; - if (_selectedTab == TabId::Individual && _selectedFilter) - widgets[WIDX_MAP].type = WindowWidgetType::FlatBtn; - - ResizeFrameWithPage(); - widgets[WIDX_GUEST_LIST].right = width - 4; - widgets[WIDX_GUEST_LIST].bottom = height - 15; - widgets[WIDX_MAP].left = 273 - 350 + width; - widgets[WIDX_MAP].right = 296 - 350 + width; - widgets[WIDX_FILTER_BY_NAME].left = 297 - 350 + width; - widgets[WIDX_FILTER_BY_NAME].right = 320 - 350 + width; - widgets[WIDX_TRACKING].left = 321 - 350 + width; - widgets[WIDX_TRACKING].right = 344 - 350 + width; - - if (_numPages > 1) + void OnResize() override { - widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Button; - auto ft = Formatter::Common(); - ft.Increment(4); - ft.Add(_selectedPage + 1); - } - else - { - widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - // Filter description - StringId format; - auto screenCoords = windowPos + ScreenCoordsXY{ 6, widgets[WIDX_TAB_CONTENT_PANEL].top + 3 }; - if (_selectedTab == TabId::Individual) - { - if (_selectedFilter) + min_width = 350; + min_height = 330; + if (width < min_width) { - if (_filterArguments.GetFirstStringId() != STR_NONE) + Invalidate(); + width = min_width; + } + if (height < min_height) + { + Invalidate(); + height = min_height; + } + } + + void OnUpdate() override + { + if (_lastFindGroupsWait != 0) + { + _lastFindGroupsWait--; + } + + // Current tab image animation + _tabAnimationIndex++; + if (_tabAnimationIndex >= (_selectedTab == TabId::Individual ? 24uL : 32uL)) + _tabAnimationIndex = 0; + InvalidateWidget(WIDX_TAB_1 + static_cast(_selectedTab)); + + gWindowMapFlashingFlags |= MapFlashingFlags::GuestListOpen; + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_MAP: + ContextOpenWindow(WindowClass::Map); + break; + case WIDX_TRACKING: + _trackingOnly = !_trackingOnly; + SetWidgetPressed(WIDX_TRACKING, _trackingOnly); + Invalidate(); + scrolls[0].v_top = 0; + RefreshList(); + break; + case WIDX_FILTER_BY_NAME: + if (!_filterName.empty()) + { + // Unset the search filter. + _filterName.clear(); + SetWidgetPressed(WIDX_FILTER_BY_NAME, false); + RefreshList(); + } + else + { + WindowTextInputRawOpen( + this, WIDX_FILTER_BY_NAME, STR_GUESTS_FILTER_BY_NAME, STR_GUESTS_ENTER_NAME_TO_SEARCH, {}, + _filterName.c_str(), 32); + } + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_TAB_1: + case WIDX_TAB_2: { - format = GetFilterString(*_selectedFilter); + if (_selectedFilter && _selectedTab == static_cast(widgetIndex - WIDX_TAB_1)) + break; + _selectedTab = static_cast(widgetIndex - WIDX_TAB_1); + _selectedPage = 0; + _numPages = 1; + widgets[WIDX_TRACKING].type = WindowWidgetType::Empty; + if (_selectedTab == TabId::Summarised) + { + widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::Empty; + SetWidgetPressed(WIDX_FILTER_BY_NAME, false); + _filterName.clear(); + } + else if (_selectedTab == TabId::Individual) + { + widgets[WIDX_TRACKING].type = WindowWidgetType::FlatBtn; + widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::FlatBtn; + } + widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; + _tabAnimationIndex = 0; + _selectedFilter = {}; + Invalidate(); + scrolls[0].v_top = 0; + RefreshList(); + break; + } + case WIDX_PAGE_DROPDOWN_BUTTON: + { + auto* widget = &widgets[widgetIndex - 1]; + + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, _numPages, widget->width() - 3); + + for (size_t i = 0; i < _numPages; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + uint16_t* args = reinterpret_cast(&gDropdownItems[i].Args); + args[0] = STR_PAGE_X; + args[1] = static_cast(i + 1); + } + Dropdown::SetChecked(static_cast(_selectedPage), true); + break; + } + case WIDX_INFO_TYPE_DROPDOWN_BUTTON: + { + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = GetViewName(GuestViewType::Actions); + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Args = GetViewName(GuestViewType::Thoughts); + + auto* widget = &widgets[widgetIndex - 1]; + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, 2, widget->width() - 3); + + Dropdown::SetChecked(static_cast(_selectedView), true); + break; + } + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (dropdownIndex == -1) + { + return; + } + switch (widgetIndex) + { + case WIDX_PAGE_DROPDOWN_BUTTON: + _selectedPage = dropdownIndex; + Invalidate(); + break; + case WIDX_INFO_TYPE_DROPDOWN_BUTTON: + _selectedView = static_cast(dropdownIndex); + Invalidate(); + break; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (!text.empty()) + { + _filterName = text; + SetWidgetPressed(WIDX_FILTER_BY_NAME, true); + RefreshList(); + } + } + + void OnPrepareDraw() override + { + SetWidgetPressed(WIDX_TAB_1, false); + SetWidgetPressed(WIDX_TAB_2, false); + SetWidgetPressed(WIDX_TAB_1 + static_cast(_selectedTab), true); + + widgets[WIDX_INFO_TYPE_DROPDOWN].text = GetViewName(_selectedView); + widgets[WIDX_MAP].type = WindowWidgetType::Empty; + if (_selectedTab == TabId::Individual && _selectedFilter) + widgets[WIDX_MAP].type = WindowWidgetType::FlatBtn; + + ResizeFrameWithPage(); + widgets[WIDX_GUEST_LIST].right = width - 4; + widgets[WIDX_GUEST_LIST].bottom = height - 15; + widgets[WIDX_MAP].left = 273 - 350 + width; + widgets[WIDX_MAP].right = 296 - 350 + width; + widgets[WIDX_FILTER_BY_NAME].left = 297 - 350 + width; + widgets[WIDX_FILTER_BY_NAME].right = 320 - 350 + width; + widgets[WIDX_TRACKING].left = 321 - 350 + width; + widgets[WIDX_TRACKING].right = 344 - 350 + width; + + if (_numPages > 1) + { + widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Button; + auto ft = Formatter::Common(); + ft.Increment(4); + ft.Add(_selectedPage + 1); + } + else + { + widgets[WIDX_PAGE_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + // Filter description + StringId format; + auto screenCoords = windowPos + ScreenCoordsXY{ 6, widgets[WIDX_TAB_CONTENT_PANEL].top + 3 }; + if (_selectedTab == TabId::Individual) + { + if (_selectedFilter) + { + if (_filterArguments.GetFirstStringId() != STR_NONE) + { + format = GetFilterString(*_selectedFilter); + } + else + { + format = STR_GUESTS_FILTER_THINKING_ABOUT; + } } else { - format = STR_GUESTS_FILTER_THINKING_ABOUT; + format = STR_ALL_GUESTS; } } else { - format = STR_ALL_GUESTS; + format = STR_ALL_GUESTS_SUMMARISED; + } + + { + Formatter ft(_filterArguments.args); + DrawTextEllipsised(dpi, screenCoords, 310, format, ft); + } + + // Number of guests (list items) + if (_selectedTab == TabId::Individual) + { + screenCoords = windowPos + ScreenCoordsXY{ 4, widgets[WIDX_GUEST_LIST].bottom + 2 }; + auto ft = Formatter(); + ft.Add(static_cast(_guestList.size())); + DrawTextBasic( + dpi, screenCoords, (_guestList.size() == 1 ? STR_FORMAT_NUM_GUESTS_SINGULAR : STR_FORMAT_NUM_GUESTS_PLURAL), + ft); } } - else + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override { - format = STR_ALL_GUESTS_SUMMARISED; - } - - { - Formatter ft(_filterArguments.args); - DrawTextEllipsised(dpi, screenCoords, 310, format, ft); - } - - // Number of guests (list items) - if (_selectedTab == TabId::Individual) - { - screenCoords = windowPos + ScreenCoordsXY{ 4, widgets[WIDX_GUEST_LIST].bottom + 2 }; - auto ft = Formatter(); - ft.Add(static_cast(_guestList.size())); - DrawTextBasic( - dpi, screenCoords, (_guestList.size() == 1 ? STR_FORMAT_NUM_GUESTS_SINGULAR : STR_FORMAT_NUM_GUESTS_PLURAL), - ft); - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - int32_t y = 0; - switch (_selectedTab) - { - case TabId::Individual: - // Count the number of guests - y = static_cast(_guestList.size()) * SCROLLABLE_ROW_HEIGHT; - _numPages = (_guestList.size() + GUESTS_PER_PAGE - 1) / GUESTS_PER_PAGE; - if (_numPages == 0) - _selectedPage = 0; - else if (_selectedPage >= _numPages) - _selectedPage = _numPages - 1; - break; - case TabId::Summarised: - default: - // Find the groups - if (IsRefreshOfGroupsRequired()) - { - RefreshGroups(); - } - y = static_cast(_groups.size() * SUMMARISED_GUEST_ROW_HEIGHT); - break; - } - - y -= static_cast(GUEST_PAGE_HEIGHT * _selectedPage); - y = std::max(0, std::min(y, GUEST_PAGE_HEIGHT)); - - if (_highlightedIndex) - { - _highlightedIndex = {}; - Invalidate(); - } - - auto i = std::max(0, y - widgets[WIDX_GUEST_LIST].bottom + widgets[WIDX_GUEST_LIST].top + 21); - if (i < scrolls[0].v_top) - { - scrolls[0].v_top = i; - Invalidate(); - } - - return { 447, y }; - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto i = screenCoords.y / (_selectedTab == TabId::Individual ? SCROLLABLE_ROW_HEIGHT : SUMMARISED_GUEST_ROW_HEIGHT); - i += static_cast(_selectedPage * GUESTS_PER_PAGE); - if (static_cast(i) != _highlightedIndex) - { - _highlightedIndex = i; - Invalidate(); - } - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - switch (_selectedTab) - { - case TabId::Individual: + int32_t y = 0; + switch (_selectedTab) { - auto i = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - i += static_cast(_selectedPage * GUESTS_PER_PAGE); - for (const auto& guestItem : _guestList) - { - if (i == 0) + case TabId::Individual: + // Count the number of guests + y = static_cast(_guestList.size()) * SCROLLABLE_ROW_HEIGHT; + _numPages = (_guestList.size() + GUESTS_PER_PAGE - 1) / GUESTS_PER_PAGE; + if (_numPages == 0) + _selectedPage = 0; + else if (_selectedPage >= _numPages) + _selectedPage = _numPages - 1; + break; + case TabId::Summarised: + default: + // Find the groups + if (IsRefreshOfGroupsRequired()) { - auto guest = GetEntity(guestItem.Id); - if (guest != nullptr) - { - WindowGuestOpen(guest); - } - break; + RefreshGroups(); } - i--; - } - break; + y = static_cast(_groups.size() * SUMMARISED_GUEST_ROW_HEIGHT); + break; } - case TabId::Summarised: + + y -= static_cast(GUEST_PAGE_HEIGHT * _selectedPage); + y = std::max(0, std::min(y, GUEST_PAGE_HEIGHT)); + + if (_highlightedIndex) { - auto i = static_cast(screenCoords.y / SUMMARISED_GUEST_ROW_HEIGHT); - if (i < _groups.size()) - { - _filterArguments = _groups[i].Arguments; - _selectedFilter = _selectedView == GuestViewType::Actions ? GuestFilterType::Guests - : GuestFilterType::GuestsThinking; - _selectedTab = TabId::Individual; - widgets[WIDX_TRACKING].type = WindowWidgetType::FlatBtn; - Invalidate(); - widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::FlatBtn; - scrolls[0].v_top = 0; - RefreshList(); - } - break; + _highlightedIndex = {}; + Invalidate(); + } + + auto i = std::max(0, y - widgets[WIDX_GUEST_LIST].bottom + widgets[WIDX_GUEST_LIST].top + 21); + if (i < scrolls[0].v_top) + { + scrolls[0].v_top = i; + Invalidate(); + } + + return { 447, y }; + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + auto i = screenCoords.y / (_selectedTab == TabId::Individual ? SCROLLABLE_ROW_HEIGHT : SUMMARISED_GUEST_ROW_HEIGHT); + i += static_cast(_selectedPage * GUESTS_PER_PAGE); + if (static_cast(i) != _highlightedIndex) + { + _highlightedIndex = i; + Invalidate(); } } - } - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - GfxFillRect( - dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - switch (_selectedTab) + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - case TabId::Individual: - DrawScrollIndividual(dpi); - break; - case TabId::Summarised: - DrawScrollSummarised(dpi); - break; + switch (_selectedTab) + { + case TabId::Individual: + { + auto i = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + i += static_cast(_selectedPage * GUESTS_PER_PAGE); + for (const auto& guestItem : _guestList) + { + if (i == 0) + { + auto guest = GetEntity(guestItem.Id); + if (guest != nullptr) + { + WindowGuestOpen(guest); + } + break; + } + i--; + } + break; + } + case TabId::Summarised: + { + auto i = static_cast(screenCoords.y / SUMMARISED_GUEST_ROW_HEIGHT); + if (i < _groups.size()) + { + _filterArguments = _groups[i].Arguments; + _selectedFilter = _selectedView == GuestViewType::Actions ? GuestFilterType::Guests + : GuestFilterType::GuestsThinking; + _selectedTab = TabId::Individual; + widgets[WIDX_TRACKING].type = WindowWidgetType::FlatBtn; + Invalidate(); + widgets[WIDX_FILTER_BY_NAME].type = WindowWidgetType::FlatBtn; + scrolls[0].v_top = 0; + RefreshList(); + } + break; + } + } } - } - void RefreshList() - { - // Only the individual tab uses the GuestList so no point calculating it - if (_selectedTab != TabId::Individual) + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override { - RefreshGroups(); + GfxFillRect( + dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); + switch (_selectedTab) + { + case TabId::Individual: + DrawScrollIndividual(dpi); + break; + case TabId::Summarised: + DrawScrollSummarised(dpi); + break; + } } - else + + void RefreshList() { - _guestList.clear(); + // Only the individual tab uses the GuestList so no point calculating it + if (_selectedTab != TabId::Individual) + { + RefreshGroups(); + } + else + { + _guestList.clear(); + + for (auto peep : EntityList()) + { + EntitySetFlashing(peep, false); + if (peep->OutsideOfPark) + continue; + if (_selectedFilter) + { + if (!IsPeepInFilter(*peep)) + continue; + EntitySetFlashing(peep, true); + } + if (!GuestShouldBeVisible(*peep)) + continue; + + auto& item = _guestList.emplace_back(); + item.Id = peep->Id; + + Formatter ft; + peep->FormatNameTo(ft); + OpenRCT2::FormatStringLegacy(item.Name, sizeof(item.Name), STR_STRINGID, ft.Data()); + } + + std::sort(_guestList.begin(), _guestList.end(), GetGuestCompareFunc()); + } + } + + private: + void DrawTabImages(DrawPixelInfo& dpi) + { + // Tab 1 image + auto i = (_selectedTab == TabId::Individual ? _tabAnimationIndex & ~3 : 0); + i += GetPeepAnimation(PeepSpriteType::Normal).base_image + 1; + GfxDrawSprite( + dpi, ImageId(i, COLOUR_GREY, COLOUR_DARK_OLIVE_GREEN), + windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].midX(), widgets[WIDX_TAB_1].bottom - 6 }); + + // Tab 2 image + i = (_selectedTab == TabId::Summarised ? _tabAnimationIndex / 4 : 0); + GfxDrawSprite( + dpi, ImageId(SPR_TAB_GUESTS_0 + i), + windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); + } + + void DrawScrollIndividual(DrawPixelInfo& dpi) + { + size_t index = 0; + auto y = static_cast(_selectedPage) * -GUEST_PAGE_HEIGHT; + for (const auto& guestItem : _guestList) + { + // Check if y is beyond the scroll control + if (y + SCROLLABLE_ROW_HEIGHT + 1 >= -0x7FFF && y + SCROLLABLE_ROW_HEIGHT + 1 > dpi.y && y < 0x7FFF + && y < dpi.y + dpi.height) + { + // Highlight backcolour and text colour (format) + StringId format = STR_BLACK_STRING; + if (index == _highlightedIndex) + { + GfxFilterRect(dpi, { 0, y, 800, y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); + format = STR_WINDOW_COLOUR_2_STRINGID; + } + + // Guest name + auto peep = GetEntity(guestItem.Id); + if (peep == nullptr) + { + continue; + } + auto ft = Formatter(); + peep->FormatNameTo(ft); + DrawTextEllipsised(dpi, { 0, y }, 113, format, ft); + + switch (_selectedView) + { + case GuestViewType::Actions: + // Guest face + GfxDrawSprite(dpi, ImageId(GetPeepFaceSpriteSmall(peep)), { 118, y + 1 }); + + // Tracking icon + if (peep->PeepFlags & PEEP_FLAGS_TRACKING) + GfxDrawSprite(dpi, ImageId(STR_ENTER_SELECTION_SIZE), { 112, y + 1 }); + + // Action + ft = Formatter(); + peep->FormatActionTo(ft); + DrawTextEllipsised(dpi, { 133, y }, 314, format, ft); + break; + case GuestViewType::Thoughts: + // For each thought + for (const auto& thought : peep->Thoughts) + { + if (thought.type == PeepThoughtType::None) + break; + if (thought.freshness == 0) + continue; + if (thought.freshness > 5) + break; + + ft = Formatter(); + PeepThoughtSetFormatArgs(&thought, ft); + DrawTextEllipsised(dpi, { 118, y }, 329, format, ft, { FontStyle::Small }); + break; + } + break; + } + } + y += SCROLLABLE_ROW_HEIGHT; + index++; + } + } + + void DrawScrollSummarised(DrawPixelInfo& dpi) + { + size_t index = 0; + auto y = 0; + for (auto& group : _groups) + { + // Check if y is beyond the scroll control + if (y + SUMMARISED_GUEST_ROW_HEIGHT + 1 >= dpi.y) + { + // Check if y is beyond the scroll control + if (y >= dpi.y + dpi.height) + break; + + // Highlight backcolour and text colour (format) + StringId format = STR_BLACK_STRING; + if (index == _highlightedIndex) + { + GfxFilterRect(dpi, { 0, y, 800, y + SUMMARISED_GUEST_ROW_HEIGHT }, FilterPaletteID::PaletteDarken1); + format = STR_WINDOW_COLOUR_2_STRINGID; + } + + // Draw guest faces + for (uint32_t j = 0; j < std::size(group.Faces) && j < group.NumGuests; j++) + { + GfxDrawSprite( + dpi, ImageId(group.Faces[j] + SPR_PEEP_SMALL_FACE_VERY_VERY_UNHAPPY), + { static_cast(j) * 8, y + 12 }); + } + + // Draw action/thoughts + Formatter ft(group.Arguments.args); + // Draw small font if displaying guests + if (_selectedView == GuestViewType::Thoughts) + { + DrawTextEllipsised(dpi, { 0, y }, 414, format, ft, { FontStyle::Small }); + } + else + { + DrawTextEllipsised(dpi, { 0, y }, 414, format, ft); + } + + // Draw guest count + ft = Formatter(); + ft.Add(STR_GUESTS_COUNT_COMMA_SEP); + ft.Add(group.NumGuests); + DrawTextBasic(dpi, { 326, y }, format, ft, { TextAlignment::RIGHT }); + } + y += SUMMARISED_GUEST_ROW_HEIGHT; + index++; + } + } + + bool GuestShouldBeVisible(const Guest& peep) + { + if (_trackingOnly && !(peep.PeepFlags & PEEP_FLAGS_TRACKING)) + return false; + + if (!_filterName.empty()) + { + char name[256]{}; + + Formatter ft; + peep.FormatNameTo(ft); + OpenRCT2::FormatStringLegacy(name, sizeof(name), STR_STRINGID, ft.Data()); + if (!String::Contains(name, _filterName.c_str(), true)) + { + return false; + } + } + + return true; + } + + bool IsPeepInFilter(const Guest& peep) + { + auto guestViewType = _selectedFilter == GuestFilterType::Guests ? GuestViewType::Actions : GuestViewType::Thoughts; + auto peepArgs = GetArgumentsFromPeep(peep, guestViewType); + if (_filterArguments.GetFirstStringId() == STR_NONE && _selectedFilter == GuestFilterType::GuestsThinking) + { + Formatter(peepArgs.args).Add(STR_NONE); + } + return _filterArguments == peepArgs; + } + + bool IsRefreshOfGroupsRequired() + { + uint32_t tick256 = Floor2(GetGameState().CurrentTicks, 256); + if (_selectedView == _lastFindGroupsSelectedView) + { + if (_lastFindGroupsWait != 0 || _lastFindGroupsTick == tick256) + { + return false; + } + } + + return true; + } + + GuestGroup& FindOrAddGroup(FilterArguments&& arguments) + { + auto foundGroup = std::find_if(std::begin(_groups), std::end(_groups), [&arguments](GuestGroup& group) { + return group.Arguments == arguments; + }); + if (foundGroup != std::end(_groups)) + { + return *foundGroup; + } + auto& newGroup = _groups.emplace_back(); + newGroup.Arguments = arguments; + return newGroup; + } + + void RefreshGroups() + { + _lastFindGroupsTick = Floor2(GetGameState().CurrentTicks, 256); + _lastFindGroupsSelectedView = _selectedView; + _lastFindGroupsWait = 320; + _groups.clear(); for (auto peep : EntityList()) { - EntitySetFlashing(peep, false); if (peep->OutsideOfPark) continue; - if (_selectedFilter) + + auto& group = FindOrAddGroup(GetArgumentsFromPeep(*peep, _selectedView)); + if (group.NumGuests < std::size(group.Faces)) { - if (!IsPeepInFilter(*peep)) - continue; - EntitySetFlashing(peep, true); + group.Faces[group.NumGuests] = GetPeepFaceSpriteSmall(peep) - SPR_PEEP_SMALL_FACE_VERY_VERY_UNHAPPY; } - if (!GuestShouldBeVisible(*peep)) - continue; - - auto& item = _guestList.emplace_back(); - item.Id = peep->Id; - - Formatter ft; - peep->FormatNameTo(ft); - OpenRCT2::FormatStringLegacy(item.Name, sizeof(item.Name), STR_STRINGID, ft.Data()); + group.NumGuests++; } - std::sort(_guestList.begin(), _guestList.end(), GetGuestCompareFunc()); - } - } - -private: - void DrawTabImages(DrawPixelInfo& dpi) - { - // Tab 1 image - auto i = (_selectedTab == TabId::Individual ? _tabAnimationIndex & ~3 : 0); - i += GetPeepAnimation(PeepSpriteType::Normal).base_image + 1; - GfxDrawSprite( - dpi, ImageId(i, COLOUR_GREY, COLOUR_DARK_OLIVE_GREEN), - windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].midX(), widgets[WIDX_TAB_1].bottom - 6 }); - - // Tab 2 image - i = (_selectedTab == TabId::Summarised ? _tabAnimationIndex / 4 : 0); - GfxDrawSprite( - dpi, ImageId(SPR_TAB_GUESTS_0 + i), - windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); - } - - void DrawScrollIndividual(DrawPixelInfo& dpi) - { - size_t index = 0; - auto y = static_cast(_selectedPage) * -GUEST_PAGE_HEIGHT; - for (const auto& guestItem : _guestList) - { - // Check if y is beyond the scroll control - if (y + SCROLLABLE_ROW_HEIGHT + 1 >= -0x7FFF && y + SCROLLABLE_ROW_HEIGHT + 1 > dpi.y && y < 0x7FFF - && y < dpi.y + dpi.height) + // Remove empty group (basically guests with no thoughts) + auto foundGroup = std::find_if(std::begin(_groups), std::end(_groups), [](GuestGroup& group) { + return group.Arguments.GetFirstStringId() == STR_EMPTY; + }); + if (foundGroup != std::end(_groups)) { - // Highlight backcolour and text colour (format) - StringId format = STR_BLACK_STRING; - if (index == _highlightedIndex) - { - GfxFilterRect(dpi, { 0, y, 800, y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); - format = STR_WINDOW_COLOUR_2_STRINGID; - } - - // Guest name - auto peep = GetEntity(guestItem.Id); - if (peep == nullptr) - { - continue; - } - auto ft = Formatter(); - peep->FormatNameTo(ft); - DrawTextEllipsised(dpi, { 0, y }, 113, format, ft); - - switch (_selectedView) - { - case GuestViewType::Actions: - // Guest face - GfxDrawSprite(dpi, ImageId(GetPeepFaceSpriteSmall(peep)), { 118, y + 1 }); - - // Tracking icon - if (peep->PeepFlags & PEEP_FLAGS_TRACKING) - GfxDrawSprite(dpi, ImageId(STR_ENTER_SELECTION_SIZE), { 112, y + 1 }); - - // Action - ft = Formatter(); - peep->FormatActionTo(ft); - DrawTextEllipsised(dpi, { 133, y }, 314, format, ft); - break; - case GuestViewType::Thoughts: - // For each thought - for (const auto& thought : peep->Thoughts) - { - if (thought.type == PeepThoughtType::None) - break; - if (thought.freshness == 0) - continue; - if (thought.freshness > 5) - break; - - ft = Formatter(); - PeepThoughtSetFormatArgs(&thought, ft); - DrawTextEllipsised(dpi, { 118, y }, 329, format, ft, { FontStyle::Small }); - break; - } - break; - } + _groups.erase(foundGroup); } - y += SCROLLABLE_ROW_HEIGHT; - index++; - } - } - void DrawScrollSummarised(DrawPixelInfo& dpi) - { - size_t index = 0; - auto y = 0; - for (auto& group : _groups) - { - // Check if y is beyond the scroll control - if (y + SUMMARISED_GUEST_ROW_HEIGHT + 1 >= dpi.y) + // Sort groups by number of guests + std::sort(_groups.begin(), _groups.end(), [](const GuestGroup& a, const GuestGroup& b) { + return a.NumGuests > b.NumGuests; + }); + + // Remove up to MaxGroups + if (_groups.size() > MaxGroups) { - // Check if y is beyond the scroll control - if (y >= dpi.y + dpi.height) + _groups.resize(MaxGroups); + } + } + + /** + * Calculates a hash value (arguments) for comparing peep actions/thoughts + */ + static FilterArguments GetArgumentsFromPeep(const Guest& peep, GuestViewType type) + { + FilterArguments result; + Formatter ft(result.args); + switch (type) + { + case GuestViewType::Actions: + peep.FormatActionTo(ft); break; - - // Highlight backcolour and text colour (format) - StringId format = STR_BLACK_STRING; - if (index == _highlightedIndex) + case GuestViewType::Thoughts: { - GfxFilterRect(dpi, { 0, y, 800, y + SUMMARISED_GUEST_ROW_HEIGHT }, FilterPaletteID::PaletteDarken1); - format = STR_WINDOW_COLOUR_2_STRINGID; + const auto& thought = peep.Thoughts[0]; + if (thought.type != PeepThoughtType::None && thought.freshness <= 5) + { + PeepThoughtSetFormatArgs(&thought, ft); + } + break; } - - // Draw guest faces - for (uint32_t j = 0; j < std::size(group.Faces) && j < group.NumGuests; j++) - { - GfxDrawSprite( - dpi, ImageId(group.Faces[j] + SPR_PEEP_SMALL_FACE_VERY_VERY_UNHAPPY), - { static_cast(j) * 8, y + 12 }); - } - - // Draw action/thoughts - Formatter ft(group.Arguments.args); - // Draw small font if displaying guests - if (_selectedView == GuestViewType::Thoughts) - { - DrawTextEllipsised(dpi, { 0, y }, 414, format, ft, { FontStyle::Small }); - } - else - { - DrawTextEllipsised(dpi, { 0, y }, 414, format, ft); - } - - // Draw guest count - ft = Formatter(); - ft.Add(STR_GUESTS_COUNT_COMMA_SEP); - ft.Add(group.NumGuests); - DrawTextBasic(dpi, { 326, y }, format, ft, { TextAlignment::RIGHT }); } - y += SUMMARISED_GUEST_ROW_HEIGHT; - index++; + return result; } - } - bool GuestShouldBeVisible(const Guest& peep) - { - if (_trackingOnly && !(peep.PeepFlags & PEEP_FLAGS_TRACKING)) - return false; - - if (!_filterName.empty()) + static constexpr StringId GetViewName(GuestViewType type) { - char name[256]{}; - - Formatter ft; - peep.FormatNameTo(ft); - OpenRCT2::FormatStringLegacy(name, sizeof(name), STR_STRINGID, ft.Data()); - if (!String::Contains(name, _filterName.c_str(), true)) + switch (type) { - return false; + default: + case GuestViewType::Actions: + return STR_ACTIONS; + case GuestViewType::Thoughts: + return STR_THOUGHTS; } } - return true; - } - - bool IsPeepInFilter(const Guest& peep) - { - auto guestViewType = _selectedFilter == GuestFilterType::Guests ? GuestViewType::Actions : GuestViewType::Thoughts; - auto peepArgs = GetArgumentsFromPeep(peep, guestViewType); - if (_filterArguments.GetFirstStringId() == STR_NONE && _selectedFilter == GuestFilterType::GuestsThinking) + static constexpr StringId GetFilterString(GuestFilterType type) { - Formatter(peepArgs.args).Add(STR_NONE); - } - return _filterArguments == peepArgs; - } - - bool IsRefreshOfGroupsRequired() - { - uint32_t tick256 = Floor2(GetGameState().CurrentTicks, 256); - if (_selectedView == _lastFindGroupsSelectedView) - { - if (_lastFindGroupsWait != 0 || _lastFindGroupsTick == tick256) + switch (type) { - return false; + default: + case GuestFilterType::Guests: + return STR_GUESTS_FILTER; + case GuestFilterType::GuestsThinking: + return STR_GUESTS_FILTER_THINKING; + case GuestFilterType::GuestsThinkingAbout: + return STR_GUESTS_FILTER_THINKING_ABOUT; } } - return true; - } - - GuestGroup& FindOrAddGroup(FilterArguments&& arguments) - { - auto foundGroup = std::find_if( - std::begin(_groups), std::end(_groups), [&arguments](GuestGroup& group) { return group.Arguments == arguments; }); - if (foundGroup != std::end(_groups)) + template static bool CompareGuestItem(const GuestItem& a, const GuestItem& b) { - return *foundGroup; - } - auto& newGroup = _groups.emplace_back(); - newGroup.Arguments = arguments; - return newGroup; - } - - void RefreshGroups() - { - _lastFindGroupsTick = Floor2(GetGameState().CurrentTicks, 256); - _lastFindGroupsSelectedView = _selectedView; - _lastFindGroupsWait = 320; - _groups.clear(); - - for (auto peep : EntityList()) - { - if (peep->OutsideOfPark) - continue; - - auto& group = FindOrAddGroup(GetArgumentsFromPeep(*peep, _selectedView)); - if (group.NumGuests < std::size(group.Faces)) + const auto* peepA = GetEntity(a.Id); + const auto* peepB = GetEntity(b.Id); + if (peepA != nullptr && peepB != nullptr) { - group.Faces[group.NumGuests] = GetPeepFaceSpriteSmall(peep) - SPR_PEEP_SMALL_FACE_VERY_VERY_UNHAPPY; + // Compare types + if (peepA->Type != peepB->Type) + { + return static_cast(peepA->Type) < static_cast(peepB->Type); + } + + // Compare name + if constexpr (!TRealNames) + { + if (peepA->Name == nullptr && peepB->Name == nullptr) + { + // Simple ID comparison for when both peeps use a number or a generated name + return peepA->PeepId < peepB->PeepId; + } + } } - group.NumGuests++; + return StrLogicalCmp(a.Name, b.Name) < 0; } - // Remove empty group (basically guests with no thoughts) - auto foundGroup = std::find_if(std::begin(_groups), std::end(_groups), [](GuestGroup& group) { - return group.Arguments.GetFirstStringId() == STR_EMPTY; - }); - if (foundGroup != std::end(_groups)) + static GuestItem::CompareFunc GetGuestCompareFunc() { - _groups.erase(foundGroup); + return GetGameState().ParkFlags & PARK_FLAGS_SHOW_REAL_GUEST_NAMES ? CompareGuestItem + : CompareGuestItem; } + }; - // Sort groups by number of guests - std::sort( - _groups.begin(), _groups.end(), [](const GuestGroup& a, const GuestGroup& b) { return a.NumGuests > b.NumGuests; }); - - // Remove up to MaxGroups - if (_groups.size() > MaxGroups) + WindowBase* WindowGuestListOpen() + { + auto* window = WindowBringToFrontByClass(WindowClass::GuestList); + if (window == nullptr) { - _groups.resize(MaxGroups); + window = WindowCreate(WindowClass::GuestList, 350, 330, WF_10 | WF_RESIZABLE); } + return window; } /** - * Calculates a hash value (arguments) for comparing peep actions/thoughts + * @param index The number of the ride or index of the thought */ - static FilterArguments GetArgumentsFromPeep(const Guest& peep, GuestViewType type) + WindowBase* WindowGuestListOpenWithFilter(GuestListFilterType type, int32_t index) { - FilterArguments result; - Formatter ft(result.args); - switch (type) + auto* w = static_cast(WindowGuestListOpen()); + if (w != nullptr) { - case GuestViewType::Actions: - peep.FormatActionTo(ft); - break; - case GuestViewType::Thoughts: - { - const auto& thought = peep.Thoughts[0]; - if (thought.type != PeepThoughtType::None && thought.freshness <= 5) - { - PeepThoughtSetFormatArgs(&thought, ft); - } - break; - } + w->SetFilter(type, index); } - return result; + return w; } - static constexpr StringId GetViewName(GuestViewType type) + void WindowGuestListRefreshList() { - switch (type) + auto* w = WindowFindByClass(WindowClass::GuestList); + if (w != nullptr) { - default: - case GuestViewType::Actions: - return STR_ACTIONS; - case GuestViewType::Thoughts: - return STR_THOUGHTS; + static_cast(w)->RefreshList(); } } - - static constexpr StringId GetFilterString(GuestFilterType type) - { - switch (type) - { - default: - case GuestFilterType::Guests: - return STR_GUESTS_FILTER; - case GuestFilterType::GuestsThinking: - return STR_GUESTS_FILTER_THINKING; - case GuestFilterType::GuestsThinkingAbout: - return STR_GUESTS_FILTER_THINKING_ABOUT; - } - } - - template static bool CompareGuestItem(const GuestItem& a, const GuestItem& b) - { - const auto* peepA = GetEntity(a.Id); - const auto* peepB = GetEntity(b.Id); - if (peepA != nullptr && peepB != nullptr) - { - // Compare types - if (peepA->Type != peepB->Type) - { - return static_cast(peepA->Type) < static_cast(peepB->Type); - } - - // Compare name - if constexpr (!TRealNames) - { - if (peepA->Name == nullptr && peepB->Name == nullptr) - { - // Simple ID comparison for when both peeps use a number or a generated name - return peepA->PeepId < peepB->PeepId; - } - } - } - return StrLogicalCmp(a.Name, b.Name) < 0; - } - - static GuestItem::CompareFunc GetGuestCompareFunc() - { - return GetGameState().ParkFlags & PARK_FLAGS_SHOW_REAL_GUEST_NAMES ? CompareGuestItem : CompareGuestItem; - } -}; - -WindowBase* WindowGuestListOpen() -{ - auto* window = WindowBringToFrontByClass(WindowClass::GuestList); - if (window == nullptr) - { - window = WindowCreate(WindowClass::GuestList, 350, 330, WF_10 | WF_RESIZABLE); - } - return window; -} - -/** - * @param index The number of the ride or index of the thought - */ -WindowBase* WindowGuestListOpenWithFilter(GuestListFilterType type, int32_t index) -{ - auto* w = static_cast(WindowGuestListOpen()); - if (w != nullptr) - { - w->SetFilter(type, index); - } - return w; -} - -void WindowGuestListRefreshList() -{ - auto* w = WindowFindByClass(WindowClass::GuestList); - if (w != nullptr) - { - static_cast(w)->RefreshList(); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/InstallTrack.cpp b/src/openrct2-ui/windows/InstallTrack.cpp index 59447abcc7..3ee93e8253 100644 --- a/src/openrct2-ui/windows/InstallTrack.cpp +++ b/src/openrct2-ui/windows/InstallTrack.cpp @@ -28,7 +28,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -56,387 +58,388 @@ static Widget window_install_track_widgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class InstallTrackWindow final : public Window -{ -private: - std::unique_ptr _trackDesign; - std::string _trackPath; - std::string _trackName; - std::vector _trackDesignPreviewPixels; - -public: - void SetupTrack(const utf8* path, std::unique_ptr trackDesign) + class InstallTrackWindow final : public Window { - _trackDesign = std::move(trackDesign); - _trackPath = path; - _trackName = GetNameFromTrackPath(path); - _trackDesignPreviewPixels.resize(4 * TRACK_PREVIEW_IMAGE_SIZE); + private: + std::unique_ptr _trackDesign; + std::string _trackPath; + std::string _trackName; + std::vector _trackDesignPreviewPixels; - UpdatePreview(); - Invalidate(); - } - - void OnOpen() override - { - widgets = window_install_track_widgets; - - WindowInitScrollWidgets(*this); - WindowPushOthersRight(*this); - } - - void OnClose() override - { - _trackPath.clear(); - _trackName.clear(); - _trackDesignPreviewPixels.clear(); - _trackDesignPreviewPixels.shrink_to_fit(); - _trackDesign = nullptr; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void SetupTrack(const utf8* path, std::unique_ptr trackDesign) { - case WIDX_CLOSE: - case WIDX_CANCEL: - Close(); - break; - case WIDX_ROTATE: - _currentTrackPieceDirection++; - _currentTrackPieceDirection %= 4; - Invalidate(); - break; - case WIDX_TOGGLE_SCENERY: - gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle; - UpdatePreview(); - Invalidate(); - break; - case WIDX_INSTALL: - InstallTrackDesign(); - break; - } - } + _trackDesign = std::move(trackDesign); + _trackPath = path; + _trackName = GetNameFromTrackPath(path); + _trackDesignPreviewPixels.resize(4 * TRACK_PREVIEW_IMAGE_SIZE); - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex != WIDX_INSTALL || text.empty()) - { - return; + UpdatePreview(); + Invalidate(); } - _trackName = std::string(text); - - OnMouseUp(WIDX_INSTALL); - } - - void OnPrepareDraw() override - { - pressed_widgets |= 1uLL << WIDX_TRACK_PREVIEW; - if (!gTrackDesignSceneryToggle) + void OnOpen() override { - pressed_widgets |= (1uLL << WIDX_TOGGLE_SCENERY); + widgets = window_install_track_widgets; + + WindowInitScrollWidgets(*this); + WindowPushOthersRight(*this); } - else + + void OnClose() override { - pressed_widgets &= ~(1uLL << WIDX_TOGGLE_SCENERY); + _trackPath.clear(); + _trackName.clear(); + _trackDesignPreviewPixels.clear(); + _trackDesignPreviewPixels.shrink_to_fit(); + _trackDesign = nullptr; } - } - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - // Track preview - Widget* widget = &window_install_track_widgets[WIDX_TRACK_PREVIEW]; - auto screenPos = windowPos + ScreenCoordsXY{ widget->left + 1, widget->top + 1 }; - int32_t colour = ColourMapA[colours[0]].darkest; - GfxFillRect(dpi, { screenPos, screenPos + ScreenCoordsXY{ 369, 216 } }, colour); - - G1Element g1temp = {}; - g1temp.offset = _trackDesignPreviewPixels.data() + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE); - g1temp.width = 370; - g1temp.height = 217; - g1temp.flags = G1_FLAG_HAS_TRANSPARENCY; - GfxSetG1Element(SPR_TEMP, &g1temp); - DrawingEngineInvalidateImage(SPR_TEMP); - GfxDrawSprite(dpi, ImageId(SPR_TEMP), screenPos); - - screenPos = windowPos + ScreenCoordsXY{ widget->midX(), widget->bottom - 12 }; - - // Warnings - const TrackDesign* td6 = _trackDesign.get(); - if (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) + void OnMouseUp(WidgetIndex widgetIndex) override { + switch (widgetIndex) + { + case WIDX_CLOSE: + case WIDX_CANCEL: + Close(); + break; + case WIDX_ROTATE: + _currentTrackPieceDirection++; + _currentTrackPieceDirection %= 4; + Invalidate(); + break; + case WIDX_TOGGLE_SCENERY: + gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle; + UpdatePreview(); + Invalidate(); + break; + case WIDX_INSTALL: + InstallTrackDesign(); + break; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (widgetIndex != WIDX_INSTALL || text.empty()) + { + return; + } + + _trackName = std::string(text); + + OnMouseUp(WIDX_INSTALL); + } + + void OnPrepareDraw() override + { + pressed_widgets |= 1uLL << WIDX_TRACK_PREVIEW; if (!gTrackDesignSceneryToggle) { - // Scenery not available - DrawTextEllipsised( - dpi, screenPos, 308, STR_DESIGN_INCLUDES_SCENERY_WHICH_IS_UNAVAILABLE, {}, { TextAlignment::CENTRE }); - screenPos.y -= LIST_ROW_HEIGHT; - } - } - - // Information - screenPos = windowPos + ScreenCoordsXY{ widget->left + 1, widget->bottom + 4 }; - // 0x006D3CF1 -- 0x006d3d71 missing - - // Track design name & type - { - auto trackName = _trackName.c_str(); - auto ft = Formatter(); - ft.Add(trackName); - DrawTextBasic(dpi, screenPos - ScreenCoordsXY{ 1, 0 }, STR_TRACK_DESIGN_NAME, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - - // Friendly Track name - { - auto ft = Formatter(); - - const auto* objectEntry = ObjectManagerLoadObject(&td6->vehicle_object.Entry); - if (objectEntry != nullptr) - { - auto groupIndex = ObjectManagerGetLoadedObjectEntryIndex(objectEntry); - auto rideName = GetRideNaming(td6->type, *GetRideEntryByIndex(groupIndex)); - ft.Add(rideName.Name); + pressed_widgets |= (1uLL << WIDX_TOGGLE_SCENERY); } else { - // Fall back on the technical track name if the vehicle object cannot be loaded - ft.Add(GetRideTypeDescriptor(td6->type).Naming.Name); + pressed_widgets &= ~(1uLL << WIDX_TOGGLE_SCENERY); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + // Track preview + Widget* widget = &window_install_track_widgets[WIDX_TRACK_PREVIEW]; + auto screenPos = windowPos + ScreenCoordsXY{ widget->left + 1, widget->top + 1 }; + int32_t colour = ColourMapA[colours[0]].darkest; + GfxFillRect(dpi, { screenPos, screenPos + ScreenCoordsXY{ 369, 216 } }, colour); + + G1Element g1temp = {}; + g1temp.offset = _trackDesignPreviewPixels.data() + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE); + g1temp.width = 370; + g1temp.height = 217; + g1temp.flags = G1_FLAG_HAS_TRANSPARENCY; + GfxSetG1Element(SPR_TEMP, &g1temp); + DrawingEngineInvalidateImage(SPR_TEMP); + GfxDrawSprite(dpi, ImageId(SPR_TEMP), screenPos); + + screenPos = windowPos + ScreenCoordsXY{ widget->midX(), widget->bottom - 12 }; + + // Warnings + const TrackDesign* td6 = _trackDesign.get(); + if (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) + { + if (!gTrackDesignSceneryToggle) + { + // Scenery not available + DrawTextEllipsised( + dpi, screenPos, 308, STR_DESIGN_INCLUDES_SCENERY_WHICH_IS_UNAVAILABLE, {}, { TextAlignment::CENTRE }); + screenPos.y -= LIST_ROW_HEIGHT; + } } - DrawTextBasic(dpi, screenPos, STR_TRACK_DESIGN_TYPE, ft); - screenPos.y += LIST_ROW_HEIGHT + 4; - } + // Information + screenPos = windowPos + ScreenCoordsXY{ widget->left + 1, widget->bottom + 4 }; + // 0x006D3CF1 -- 0x006d3d71 missing - // Stats - { - fixed32_2dp rating = td6->excitement * 10; - auto ft = Formatter(); - ft.Add(rating); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_EXCITEMENT_RATING, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - { - fixed32_2dp rating = td6->intensity * 10; - auto ft = Formatter(); - ft.Add(rating); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_INTENSITY_RATING, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - { - fixed32_2dp rating = td6->nausea * 10; - auto ft = Formatter(); - ft.Add(rating); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_NAUSEA_RATING, ft); - screenPos.y += LIST_ROW_HEIGHT + 4; - } - - const auto& rtd = GetRideTypeDescriptor(td6->type); - if (!rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - if (td6->type == RIDE_TYPE_MINI_GOLF) + // Track design name & type { - // Holes - uint16_t holes = td6->holes & 0x1F; + auto trackName = _trackName.c_str(); auto ft = Formatter(); - ft.Add(holes); - DrawTextBasic(dpi, screenPos, STR_HOLES, ft); + ft.Add(trackName); + DrawTextBasic(dpi, screenPos - ScreenCoordsXY{ 1, 0 }, STR_TRACK_DESIGN_NAME, ft); screenPos.y += LIST_ROW_HEIGHT; } - else + + // Friendly Track name { - // Maximum speed + auto ft = Formatter(); + + const auto* objectEntry = ObjectManagerLoadObject(&td6->vehicle_object.Entry); + if (objectEntry != nullptr) { - uint16_t speed = ((td6->max_speed << 16) * 9) >> 18; + auto groupIndex = ObjectManagerGetLoadedObjectEntryIndex(objectEntry); + auto rideName = GetRideNaming(td6->type, *GetRideEntryByIndex(groupIndex)); + ft.Add(rideName.Name); + } + else + { + // Fall back on the technical track name if the vehicle object cannot be loaded + ft.Add(GetRideTypeDescriptor(td6->type).Naming.Name); + } + + DrawTextBasic(dpi, screenPos, STR_TRACK_DESIGN_TYPE, ft); + screenPos.y += LIST_ROW_HEIGHT + 4; + } + + // Stats + { + fixed32_2dp rating = td6->excitement * 10; + auto ft = Formatter(); + ft.Add(rating); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_EXCITEMENT_RATING, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + { + fixed32_2dp rating = td6->intensity * 10; + auto ft = Formatter(); + ft.Add(rating); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_INTENSITY_RATING, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + { + fixed32_2dp rating = td6->nausea * 10; + auto ft = Formatter(); + ft.Add(rating); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_NAUSEA_RATING, ft); + screenPos.y += LIST_ROW_HEIGHT + 4; + } + + const auto& rtd = GetRideTypeDescriptor(td6->type); + if (!rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + if (td6->type == RIDE_TYPE_MINI_GOLF) + { + // Holes + uint16_t holes = td6->holes & 0x1F; auto ft = Formatter(); - ft.Add(speed); - DrawTextBasic(dpi, screenPos, STR_MAX_SPEED, ft); + ft.Add(holes); + DrawTextBasic(dpi, screenPos, STR_HOLES, ft); screenPos.y += LIST_ROW_HEIGHT; } - // Average speed + else { - uint16_t speed = ((td6->average_speed << 16) * 9) >> 18; + // Maximum speed + { + uint16_t speed = ((td6->max_speed << 16) * 9) >> 18; + auto ft = Formatter(); + ft.Add(speed); + DrawTextBasic(dpi, screenPos, STR_MAX_SPEED, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + // Average speed + { + uint16_t speed = ((td6->average_speed << 16) * 9) >> 18; + auto ft = Formatter(); + ft.Add(speed); + DrawTextBasic(dpi, screenPos, STR_AVERAGE_SPEED, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + } + + // Ride length + auto ft = Formatter(); + ft.Add(STR_RIDE_LENGTH_ENTRY); + ft.Add(td6->ride_length); + DrawTextEllipsised(dpi, screenPos, 214, STR_TRACK_LIST_RIDE_LENGTH, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + + if (GetRideTypeDescriptor(td6->type).HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) + { + // Maximum positive vertical Gs + { + int32_t gForces = td6->max_positive_vertical_g * 32; auto ft = Formatter(); - ft.Add(speed); - DrawTextBasic(dpi, screenPos, STR_AVERAGE_SPEED, ft); + ft.Add(gForces); + DrawTextBasic(dpi, screenPos, STR_MAX_POSITIVE_VERTICAL_G, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + // Maximum negative vertical Gs + { + int32_t gForces = td6->max_negative_vertical_g * 32; + auto ft = Formatter(); + ft.Add(gForces); + DrawTextBasic(dpi, screenPos, STR_MAX_NEGATIVE_VERTICAL_G, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + // Maximum lateral Gs + { + int32_t gForces = td6->max_lateral_g * 32; + auto ft = Formatter(); + ft.Add(gForces); + DrawTextBasic(dpi, screenPos, STR_MAX_LATERAL_G, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + if (td6->total_air_time != 0) + { + // Total air time + int32_t airTime = td6->total_air_time * 25; + auto ft = Formatter(); + ft.Add(airTime); + DrawTextBasic(dpi, screenPos, STR_TOTAL_AIR_TIME, ft); screenPos.y += LIST_ROW_HEIGHT; } } - // Ride length - auto ft = Formatter(); - ft.Add(STR_RIDE_LENGTH_ENTRY); - ft.Add(td6->ride_length); - DrawTextEllipsised(dpi, screenPos, 214, STR_TRACK_LIST_RIDE_LENGTH, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - - if (GetRideTypeDescriptor(td6->type).HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) - { - // Maximum positive vertical Gs + if (GetRideTypeDescriptor(td6->type).HasFlag(RIDE_TYPE_FLAG_HAS_DROPS)) { - int32_t gForces = td6->max_positive_vertical_g * 32; + // Drops + uint16_t drops = td6->drops & 0x3F; auto ft = Formatter(); - ft.Add(gForces); - DrawTextBasic(dpi, screenPos, STR_MAX_POSITIVE_VERTICAL_G, ft); + ft.Add(drops); + DrawTextBasic(dpi, screenPos, STR_DROPS, ft); + screenPos.y += LIST_ROW_HEIGHT; + + // Drop height is multiplied by 0.75 + DrawTextBasic(dpi, screenPos, STR_HIGHEST_DROP_HEIGHT, ft); screenPos.y += LIST_ROW_HEIGHT; } - // Maximum negative vertical Gs + + if (td6->type != RIDE_TYPE_MINI_GOLF) { - int32_t gForces = td6->max_negative_vertical_g * 32; + uint16_t inversions = td6->inversions & 0x1F; + if (inversions != 0) + { + // Inversions + auto ft = Formatter(); + ft.Add(inversions); + DrawTextBasic(dpi, screenPos, STR_INVERSIONS, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + } + screenPos.y += 4; + + if (td6->space_required_x != 0xFF) + { + // Space required auto ft = Formatter(); - ft.Add(gForces); - DrawTextBasic(dpi, screenPos, STR_MAX_NEGATIVE_VERTICAL_G, ft); + ft.Add(td6->space_required_x); + ft.Add(td6->space_required_y); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_SPACE_REQUIRED, ft); screenPos.y += LIST_ROW_HEIGHT; } - // Maximum lateral Gs + + if (td6->cost != 0) { - int32_t gForces = td6->max_lateral_g * 32; auto ft = Formatter(); - ft.Add(gForces); - DrawTextBasic(dpi, screenPos, STR_MAX_LATERAL_G, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - if (td6->total_air_time != 0) - { - // Total air time - int32_t airTime = td6->total_air_time * 25; - auto ft = Formatter(); - ft.Add(airTime); - DrawTextBasic(dpi, screenPos, STR_TOTAL_AIR_TIME, ft); - screenPos.y += LIST_ROW_HEIGHT; + ft.Add(td6->cost); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_COST_AROUND, ft); } } - if (GetRideTypeDescriptor(td6->type).HasFlag(RIDE_TYPE_FLAG_HAS_DROPS)) + void OnResize() override { - // Drops - uint16_t drops = td6->drops & 0x3F; - auto ft = Formatter(); - ft.Add(drops); - DrawTextBasic(dpi, screenPos, STR_DROPS, ft); - screenPos.y += LIST_ROW_HEIGHT; - - // Drop height is multiplied by 0.75 - DrawTextBasic(dpi, screenPos, STR_HIGHEST_DROP_HEIGHT, ft); - screenPos.y += LIST_ROW_HEIGHT; + ResizeFrame(); } - if (td6->type != RIDE_TYPE_MINI_GOLF) + private: + void UpdatePreview() { - uint16_t inversions = td6->inversions & 0x1F; - if (inversions != 0) - { - // Inversions - auto ft = Formatter(); - ft.Add(inversions); - DrawTextBasic(dpi, screenPos, STR_INVERSIONS, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - } - screenPos.y += 4; - - if (td6->space_required_x != 0xFF) - { - // Space required - auto ft = Formatter(); - ft.Add(td6->space_required_x); - ft.Add(td6->space_required_y); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_SPACE_REQUIRED, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - - if (td6->cost != 0) - { - auto ft = Formatter(); - ft.Add(td6->cost); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_COST_AROUND, ft); - } - } - - void OnResize() override - { - ResizeFrame(); - } - -private: - void UpdatePreview() - { - TrackDesignDrawPreview(_trackDesign.get(), _trackDesignPreviewPixels.data()); - } - - void InstallTrackDesign() - { - auto env = OpenRCT2::GetContext()->GetPlatformEnvironment(); - auto destPath = env->GetDirectoryPath(OpenRCT2::DIRBASE::USER, OpenRCT2::DIRID::TRACK); - if (!Path::CreateDirectory(destPath)) - { - LOG_ERROR("Unable to create directory '%s'", destPath.c_str()); - ContextShowError(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE, {}); - return; - } - - destPath = Path::Combine(destPath, _trackName + u8".td6"); - - if (File::Exists(destPath)) - { - LOG_INFO("%s already exists, prompting user for a different track design name", destPath.c_str()); - ContextShowError(STR_UNABLE_TO_INSTALL_THIS_TRACK_DESIGN, STR_NONE, {}); - WindowTextInputRawOpen( - this, WIDX_INSTALL, STR_SELECT_NEW_NAME_FOR_TRACK_DESIGN, STR_AN_EXISTING_TRACK_DESIGN_ALREADY_HAS_THIS_NAME, - {}, _trackName.c_str(), 255); - } - else - { - if (TrackRepositoryInstall(_trackPath.c_str(), _trackName.c_str())) - { - Close(); - } - else + TrackDesignDrawPreview(_trackDesign.get(), _trackDesignPreviewPixels.data()); + } + + void InstallTrackDesign() + { + auto env = OpenRCT2::GetContext()->GetPlatformEnvironment(); + auto destPath = env->GetDirectoryPath(OpenRCT2::DIRBASE::USER, OpenRCT2::DIRID::TRACK); + if (!Path::CreateDirectory(destPath)) { + LOG_ERROR("Unable to create directory '%s'", destPath.c_str()); ContextShowError(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE, {}); + return; + } + + destPath = Path::Combine(destPath, _trackName + u8".td6"); + + if (File::Exists(destPath)) + { + LOG_INFO("%s already exists, prompting user for a different track design name", destPath.c_str()); + ContextShowError(STR_UNABLE_TO_INSTALL_THIS_TRACK_DESIGN, STR_NONE, {}); + WindowTextInputRawOpen( + this, WIDX_INSTALL, STR_SELECT_NEW_NAME_FOR_TRACK_DESIGN, + STR_AN_EXISTING_TRACK_DESIGN_ALREADY_HAS_THIS_NAME, {}, _trackName.c_str(), 255); + } + else + { + if (TrackRepositoryInstall(_trackPath.c_str(), _trackName.c_str())) + { + Close(); + } + else + { + ContextShowError(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE, {}); + } } } - } -}; + }; -WindowBase* WindowInstallTrackOpen(const utf8* path) -{ - auto trackDesign = TrackDesignImport(path); - if (trackDesign == nullptr) + WindowBase* WindowInstallTrackOpen(const utf8* path) { - ContextShowError(STR_UNABLE_TO_LOAD_FILE, STR_NONE, {}); - return nullptr; + auto trackDesign = TrackDesignImport(path); + if (trackDesign == nullptr) + { + ContextShowError(STR_UNABLE_TO_LOAD_FILE, STR_NONE, {}); + return nullptr; + } + + ObjectManagerUnloadAllObjects(); + if (trackDesign->type == RIDE_TYPE_NULL) + { + LOG_ERROR("Failed to load track (ride type null): %s", path); + return nullptr; + } + if (ObjectManagerLoadObject(&trackDesign->vehicle_object.Entry) == nullptr) + { + LOG_ERROR("Failed to load track (vehicle load fail): %s", path); + return nullptr; + } + + WindowCloseByClass(WindowClass::EditorObjectSelection); + WindowCloseConstructionWindows(); + + gTrackDesignSceneryToggle = false; + _currentTrackPieceDirection = 2; + + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + auto screenPos = ScreenCoordsXY{ screenWidth / 2 - 201, std::max(TOP_TOOLBAR_HEIGHT + 1, screenHeight / 2 - 200) }; + + auto* window = WindowFocusOrCreate(WindowClass::InstallTrack, screenPos, WW, WH, 0); + window->SetupTrack(path, std::move(trackDesign)); + + return window; } - - ObjectManagerUnloadAllObjects(); - if (trackDesign->type == RIDE_TYPE_NULL) - { - LOG_ERROR("Failed to load track (ride type null): %s", path); - return nullptr; - } - if (ObjectManagerLoadObject(&trackDesign->vehicle_object.Entry) == nullptr) - { - LOG_ERROR("Failed to load track (vehicle load fail): %s", path); - return nullptr; - } - - WindowCloseByClass(WindowClass::EditorObjectSelection); - WindowCloseConstructionWindows(); - - gTrackDesignSceneryToggle = false; - _currentTrackPieceDirection = 2; - - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - auto screenPos = ScreenCoordsXY{ screenWidth / 2 - 201, std::max(TOP_TOOLBAR_HEIGHT + 1, screenHeight / 2 - 200) }; - - auto* window = WindowFocusOrCreate(WindowClass::InstallTrack, screenPos, WW, WH, 0); - window->SetupTrack(path, std::move(trackDesign)); - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Land.cpp b/src/openrct2-ui/windows/Land.cpp index e88f254b17..f3c8cc3098 100644 --- a/src/openrct2-ui/windows/Land.cpp +++ b/src/openrct2-ui/windows/Land.cpp @@ -20,13 +20,13 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_LAND; + static constexpr int32_t WH = 160; + static constexpr int32_t WW = 98; -static constexpr StringId WINDOW_TITLE = STR_LAND; -static constexpr int32_t WH = 160; -static constexpr int32_t WW = 98; - -// clang-format off + // clang-format off enum WindowLandWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -51,302 +51,305 @@ static Widget window_land_widgets[] = { MakeWidget ({49, 106}, {47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CHANGE_VERTICAL_LAND_TIP), // wall texture kWidgetsEnd, }; -// clang-format on + // clang-format on -class LandWindow final : public Window -{ -private: - ObjectEntryIndex _selectedFloorTexture = 0; - ObjectEntryIndex _selectedWallTexture = 0; - - void InputSize() + class LandWindow final : public Window { - Formatter ft; - ft.Add(kLandToolMinimumSize); - ft.Add(kLandToolMaximumSize); - WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); - } + private: + ObjectEntryIndex _selectedFloorTexture = 0; + ObjectEntryIndex _selectedWallTexture = 0; -public: - void OnOpen() override - { - widgets = window_land_widgets; - hold_down_widgets = (1uLL << WIDX_DECREMENT) | (1uLL << WIDX_INCREMENT); - WindowInitScrollWidgets(*this); - WindowPushOthersBelow(*this); - - gLandToolSize = 1; - gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; - gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; - gLandMountainMode = false; - gLandPaintMode = false; - _selectedFloorTexture = LandTool::GetSurfaceStyleFromDropdownIndex(0); - _selectedWallTexture = LandTool::GetEdgeStyleFromDropdownIndex(0); - gLandToolRaiseCost = kMoney64Undefined; - gLandToolLowerCost = kMoney64Undefined; - } - - void OnClose() override - { - // If the tool wasn't changed, turn tool off - if (LandToolIsActive()) - ToolCancel(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void InputSize() { - case WIDX_CLOSE: + Formatter ft; + ft.Add(kLandToolMinimumSize); + ft.Add(kLandToolMaximumSize); + WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); + } + + public: + void OnOpen() override + { + widgets = window_land_widgets; + hold_down_widgets = (1uLL << WIDX_DECREMENT) | (1uLL << WIDX_INCREMENT); + WindowInitScrollWidgets(*this); + WindowPushOthersBelow(*this); + + gLandToolSize = 1; + gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; + gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; + gLandMountainMode = false; + gLandPaintMode = false; + _selectedFloorTexture = LandTool::GetSurfaceStyleFromDropdownIndex(0); + _selectedWallTexture = LandTool::GetEdgeStyleFromDropdownIndex(0); + gLandToolRaiseCost = kMoney64Undefined; + gLandToolLowerCost = kMoney64Undefined; + } + + void OnClose() override + { + // If the tool wasn't changed, turn tool off + if (LandToolIsActive()) + ToolCancel(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_MOUNTAINMODE: + gLandMountainMode ^= 1; + gLandPaintMode = 0; + Invalidate(); + break; + case WIDX_PAINTMODE: + gLandMountainMode = 0; + gLandPaintMode ^= 1; + Invalidate(); + break; + case WIDX_PREVIEW: + InputSize(); + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + Widget* widget = &widgets[widgetIndex]; + switch (widgetIndex) + { + case WIDX_FLOOR: + LandTool::ShowSurfaceStyleDropdown(this, widget, _selectedFloorTexture); + break; + case WIDX_WALL: + LandTool::ShowEdgeStyleDropdown(this, widget, _selectedWallTexture); + break; + case WIDX_PREVIEW: + InputSize(); + break; + case WIDX_DECREMENT: + // Decrement land tool size + gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); + + // Invalidate the window + Invalidate(); + break; + case WIDX_INCREMENT: + // Increment land tool size + gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); + + // Invalidate the window + Invalidate(); + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + int32_t type; + + switch (widgetIndex) + { + case WIDX_FLOOR: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; + + type = (dropdownIndex == -1) + ? _selectedFloorTexture + : LandTool::GetSurfaceStyleFromDropdownIndex(static_cast(dropdownIndex)); + + if (gLandToolTerrainSurface == type) + { + gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainSurface = type; + _selectedFloorTexture = type; + } + Invalidate(); + break; + case WIDX_WALL: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; + + type = (dropdownIndex == -1) ? _selectedWallTexture + : LandTool::GetEdgeStyleFromDropdownIndex(static_cast(dropdownIndex)); + + if (gLandToolTerrainEdge == type) + { + gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainEdge = type; + _selectedWallTexture = type; + } + Invalidate(); + break; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (widgetIndex != WIDX_PREVIEW) + return; + + char* end; + std::string textStr = std::string(text); + int32_t size = strtol(textStr.c_str(), &end, 10); + if (*end == '\0') + { + size = std::max(kLandToolMinimumSize, size); + size = std::min(kLandToolMaximumSize, size); + gLandToolSize = size; + + Invalidate(); + } + } + + void OnUpdate() override + { + if (!LandToolIsActive()) Close(); - break; - case WIDX_MOUNTAINMODE: - gLandMountainMode ^= 1; - gLandPaintMode = 0; - Invalidate(); - break; - case WIDX_PAINTMODE: - gLandMountainMode = 0; - gLandPaintMode ^= 1; - Invalidate(); - break; - case WIDX_PREVIEW: - InputSize(); - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - Widget* widget = &widgets[widgetIndex]; - switch (widgetIndex) - { - case WIDX_FLOOR: - LandTool::ShowSurfaceStyleDropdown(this, widget, _selectedFloorTexture); - break; - case WIDX_WALL: - LandTool::ShowEdgeStyleDropdown(this, widget, _selectedWallTexture); - break; - case WIDX_PREVIEW: - InputSize(); - break; - case WIDX_DECREMENT: - // Decrement land tool size - gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); - - // Invalidate the window - Invalidate(); - break; - case WIDX_INCREMENT: - // Increment land tool size - gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); - - // Invalidate the window - Invalidate(); - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - int32_t type; - - switch (widgetIndex) - { - case WIDX_FLOOR: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _selectedFloorTexture - : LandTool::GetSurfaceStyleFromDropdownIndex(static_cast(dropdownIndex)); - - if (gLandToolTerrainSurface == type) - { - gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainSurface = type; - _selectedFloorTexture = type; - } - Invalidate(); - break; - case WIDX_WALL: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _selectedWallTexture - : LandTool::GetEdgeStyleFromDropdownIndex(static_cast(dropdownIndex)); - - if (gLandToolTerrainEdge == type) - { - gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainEdge = type; - _selectedWallTexture = type; - } - Invalidate(); - break; - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex != WIDX_PREVIEW) - return; - - char* end; - std::string textStr = std::string(text); - int32_t size = strtol(textStr.c_str(), &end, 10); - if (*end == '\0') - { - size = std::max(kLandToolMinimumSize, size); - size = std::min(kLandToolMaximumSize, size); - gLandToolSize = size; - - Invalidate(); - } - } - - void OnUpdate() override - { - if (!LandToolIsActive()) - Close(); - } - - void OnPrepareDraw() override - { - pressed_widgets = 0; - SetWidgetPressed(WIDX_PREVIEW, true); - if (gLandToolTerrainSurface != OBJECT_ENTRY_INDEX_NULL) - SetWidgetPressed(WIDX_FLOOR, true); - if (gLandToolTerrainEdge != OBJECT_ENTRY_INDEX_NULL) - SetWidgetPressed(WIDX_WALL, true); - if (gLandMountainMode) - SetWidgetPressed(WIDX_MOUNTAINMODE, true); - if (gLandPaintMode) - SetWidgetPressed(WIDX_PAINTMODE, true); - - // Update the preview image (for tool sizes up to 7) - widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - ScreenCoordsXY screenCoords; - int32_t numTiles; - money64 price; - Widget* previewWidget = &widgets[WIDX_PREVIEW]; - - DrawWidgets(dpi); - DrawDropdownButtons(dpi); - - // Draw number for tool sizes bigger than 7 - if (gLandToolSize > kLandToolMaximumSizeWithSprite) - { - auto ft = Formatter(); - ft.Add(gLandToolSize); - screenCoords = { windowPos.x + previewWidget->midX(), windowPos.y + previewWidget->midY() }; - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); - } - else if (gLandMountainMode) - { - screenCoords = { windowPos.x + previewWidget->left, windowPos.y + previewWidget->top }; - auto sprite = ImageId(gLandToolSize % 2 == 0 ? SPR_G2_MOUNTAIN_TOOL_EVEN : SPR_G2_MOUNTAIN_TOOL_ODD); - GfxDrawSprite(dpi, sprite, screenCoords); - WidgetDraw(dpi, *this, WIDX_DECREMENT); - WidgetDraw(dpi, *this, WIDX_INCREMENT); } - screenCoords = { windowPos.x + previewWidget->midX(), windowPos.y + previewWidget->bottom + 5 }; - - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + void OnPrepareDraw() override { - // Draw raise cost amount - if (gLandToolRaiseCost != kMoney64Undefined && gLandToolRaiseCost != 0) - { - auto ft = Formatter(); - ft.Add(gLandToolRaiseCost); - DrawTextBasic(dpi, screenCoords, STR_RAISE_COST_AMOUNT, ft, { TextAlignment::CENTRE }); - } - screenCoords.y += 10; - - // Draw lower cost amount - if (gLandToolLowerCost != kMoney64Undefined && gLandToolLowerCost != 0) - { - auto ft = Formatter(); - ft.Add(gLandToolLowerCost); - DrawTextBasic(dpi, screenCoords, STR_LOWER_COST_AMOUNT, ft, { TextAlignment::CENTRE }); - } - screenCoords.y += 50; - - // Draw paint price - numTiles = gLandToolSize * gLandToolSize; - price = 0; + pressed_widgets = 0; + SetWidgetPressed(WIDX_PREVIEW, true); if (gLandToolTerrainSurface != OBJECT_ENTRY_INDEX_NULL) - { - auto& objManager = GetContext()->GetObjectManager(); - const auto surfaceObj = static_cast( - objManager.GetLoadedObject(ObjectType::TerrainSurface, gLandToolTerrainSurface)); - if (surfaceObj != nullptr) - { - price += numTiles * static_cast(surfaceObj->Price); - } - } - + SetWidgetPressed(WIDX_FLOOR, true); if (gLandToolTerrainEdge != OBJECT_ENTRY_INDEX_NULL) - price += numTiles * 100LL; + SetWidgetPressed(WIDX_WALL, true); + if (gLandMountainMode) + SetWidgetPressed(WIDX_MOUNTAINMODE, true); + if (gLandPaintMode) + SetWidgetPressed(WIDX_PAINTMODE, true); - if (price != 0) + // Update the preview image (for tool sizes up to 7) + widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + ScreenCoordsXY screenCoords; + int32_t numTiles; + money64 price; + Widget* previewWidget = &widgets[WIDX_PREVIEW]; + + DrawWidgets(dpi); + DrawDropdownButtons(dpi); + + // Draw number for tool sizes bigger than 7 + if (gLandToolSize > kLandToolMaximumSizeWithSprite) { auto ft = Formatter(); - ft.Add(price); - DrawTextBasic(dpi, screenCoords, STR_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + ft.Add(gLandToolSize); + screenCoords = { windowPos.x + previewWidget->midX(), windowPos.y + previewWidget->midY() }; + DrawTextBasic( + dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + } + else if (gLandMountainMode) + { + screenCoords = { windowPos.x + previewWidget->left, windowPos.y + previewWidget->top }; + auto sprite = ImageId(gLandToolSize % 2 == 0 ? SPR_G2_MOUNTAIN_TOOL_EVEN : SPR_G2_MOUNTAIN_TOOL_ODD); + GfxDrawSprite(dpi, sprite, screenCoords); + WidgetDraw(dpi, *this, WIDX_DECREMENT); + WidgetDraw(dpi, *this, WIDX_INCREMENT); + } + + screenCoords = { windowPos.x + previewWidget->midX(), windowPos.y + previewWidget->bottom + 5 }; + + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + // Draw raise cost amount + if (gLandToolRaiseCost != kMoney64Undefined && gLandToolRaiseCost != 0) + { + auto ft = Formatter(); + ft.Add(gLandToolRaiseCost); + DrawTextBasic(dpi, screenCoords, STR_RAISE_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + } + screenCoords.y += 10; + + // Draw lower cost amount + if (gLandToolLowerCost != kMoney64Undefined && gLandToolLowerCost != 0) + { + auto ft = Formatter(); + ft.Add(gLandToolLowerCost); + DrawTextBasic(dpi, screenCoords, STR_LOWER_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + } + screenCoords.y += 50; + + // Draw paint price + numTiles = gLandToolSize * gLandToolSize; + price = 0; + if (gLandToolTerrainSurface != OBJECT_ENTRY_INDEX_NULL) + { + auto& objManager = GetContext()->GetObjectManager(); + const auto surfaceObj = static_cast( + objManager.GetLoadedObject(ObjectType::TerrainSurface, gLandToolTerrainSurface)); + if (surfaceObj != nullptr) + { + price += numTiles * static_cast(surfaceObj->Price); + } + } + + if (gLandToolTerrainEdge != OBJECT_ENTRY_INDEX_NULL) + price += numTiles * 100LL; + + if (price != 0) + { + auto ft = Formatter(); + ft.Add(price); + DrawTextBasic(dpi, screenCoords, STR_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + } } } - } - void OnResize() override - { - ResizeFrame(); - } - -private: - void DrawDropdownButtons(DrawPixelInfo& dpi) - { - auto& objManager = GetContext()->GetObjectManager(); - const auto surfaceObj = static_cast( - objManager.GetLoadedObject(ObjectType::TerrainSurface, _selectedFloorTexture)); - ImageId surfaceImage; - if (surfaceObj != nullptr) + void OnResize() override { - surfaceImage = ImageId(surfaceObj->IconImageId); - if (surfaceObj->Colour != 255) - surfaceImage = surfaceImage.WithPrimary(surfaceObj->Colour); + ResizeFrame(); } - const auto edgeObj = static_cast( - objManager.GetLoadedObject(ObjectType::TerrainEdge, _selectedWallTexture)); - ImageId edgeImage; - if (edgeObj != nullptr) + private: + void DrawDropdownButtons(DrawPixelInfo& dpi) { - edgeImage = ImageId(edgeObj->IconImageId); + auto& objManager = GetContext()->GetObjectManager(); + const auto surfaceObj = static_cast( + objManager.GetLoadedObject(ObjectType::TerrainSurface, _selectedFloorTexture)); + ImageId surfaceImage; + if (surfaceObj != nullptr) + { + surfaceImage = ImageId(surfaceObj->IconImageId); + if (surfaceObj->Colour != 255) + surfaceImage = surfaceImage.WithPrimary(surfaceObj->Colour); + } + + const auto edgeObj = static_cast( + objManager.GetLoadedObject(ObjectType::TerrainEdge, _selectedWallTexture)); + ImageId edgeImage; + if (edgeObj != nullptr) + { + edgeImage = ImageId(edgeObj->IconImageId); + } + + DrawDropdownButton(dpi, WIDX_FLOOR, surfaceImage); + DrawDropdownButton(dpi, WIDX_WALL, edgeImage); } - DrawDropdownButton(dpi, WIDX_FLOOR, surfaceImage); - DrawDropdownButton(dpi, WIDX_WALL, edgeImage); - } + void DrawDropdownButton(DrawPixelInfo& dpi, WidgetIndex widgetIndex, ImageId image) + { + const auto& widget = widgets[widgetIndex]; + GfxDrawSprite(dpi, image, { windowPos.x + widget.left, windowPos.y + widget.top }); + } + }; - void DrawDropdownButton(DrawPixelInfo& dpi, WidgetIndex widgetIndex, ImageId image) + WindowBase* WindowLandOpen() { - const auto& widget = widgets[widgetIndex]; - GfxDrawSprite(dpi, image, { windowPos.x + widget.left, windowPos.y + widget.top }); + return WindowFocusOrCreate(WindowClass::Land, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); } -}; - -WindowBase* WindowLandOpen() -{ - return WindowFocusOrCreate(WindowClass::Land, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/LandRights.cpp b/src/openrct2-ui/windows/LandRights.cpp index 63bef5983a..a0415bb3ab 100644 --- a/src/openrct2-ui/windows/LandRights.cpp +++ b/src/openrct2-ui/windows/LandRights.cpp @@ -23,13 +23,13 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_LAND_RIGHTS; + static constexpr int32_t WH = 94; + static constexpr int32_t WW = 98; -static constexpr StringId WINDOW_TITLE = STR_LAND_RIGHTS; -static constexpr int32_t WH = 94; -static constexpr int32_t WW = 98; - -// clang-format off + // clang-format off enum WindowLandRightsWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -50,366 +50,369 @@ static Widget window_land_rights_widgets[] = { MakeRemapWidget({52, 53}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Tertiary, SPR_BUY_CONSTRUCTION_RIGHTS, STR_BUY_CONSTRUCTION_RIGHTS_TIP ), // construction rights kWidgetsEnd, }; -// clang-format on + // clang-format on -constexpr uint8_t LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS = 0; -constexpr uint8_t LAND_RIGHTS_MODE_BUY_LAND = 1; + constexpr uint8_t LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS = 0; + constexpr uint8_t LAND_RIGHTS_MODE_BUY_LAND = 1; -class LandRightsWindow final : public Window -{ -public: - void OnOpen() override + class LandRightsWindow final : public Window { - widgets = window_land_rights_widgets; - hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); - WindowInitScrollWidgets(*this); - WindowPushOthersBelow(*this); - _landRightsMode = LAND_RIGHTS_MODE_BUY_LAND; - pressed_widgets = (1uLL << WIDX_BUY_LAND_RIGHTS); - - gLandToolSize = 1; - - ShowGridlines(); - ToolSet(*this, WIDX_BUY_LAND_RIGHTS, Tool::UpArrow); - InputSetFlag(INPUT_FLAG_6, true); - - ShowLandRights(); - - if (gLandRemainingConstructionSales == 0) + public: + void OnOpen() override { - ShowConstructionRights(); - } - } + widgets = window_land_rights_widgets; + hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); + WindowInitScrollWidgets(*this); + WindowPushOthersBelow(*this); + _landRightsMode = LAND_RIGHTS_MODE_BUY_LAND; + pressed_widgets = (1uLL << WIDX_BUY_LAND_RIGHTS); - void OnClose() override - { - HideGridlines(); - if (gLandRemainingConstructionSales == 0) - { - HideConstructionRights(); + gLandToolSize = 1; + + ShowGridlines(); + ToolSet(*this, WIDX_BUY_LAND_RIGHTS, Tool::UpArrow); + InputSetFlag(INPUT_FLAG_6, true); + + ShowLandRights(); + + if (gLandRemainingConstructionSales == 0) + { + ShowConstructionRights(); + } } - // If the tool wasn't changed, turn tool off - if (LandRightsToolIsActive()) - ToolCancel(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnClose() override { - case WIDX_CLOSE: + HideGridlines(); + if (gLandRemainingConstructionSales == 0) + { + HideConstructionRights(); + } + + // If the tool wasn't changed, turn tool off + if (LandRightsToolIsActive()) + ToolCancel(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_PREVIEW: + InputSize(); + break; + case WIDX_BUY_LAND_RIGHTS: + if (_landRightsMode != LAND_RIGHTS_MODE_BUY_LAND) + { + ToolSet(*this, WIDX_BUY_LAND_RIGHTS, Tool::UpArrow); + _landRightsMode = LAND_RIGHTS_MODE_BUY_LAND; + ShowLandRights(); + Invalidate(); + } + break; + case WIDX_BUY_CONSTRUCTION_RIGHTS: + if (_landRightsMode != LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS) + { + ToolSet(*this, WIDX_BUY_CONSTRUCTION_RIGHTS, Tool::UpArrow); + _landRightsMode = LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS; + ShowConstructionRights(); + Invalidate(); + } + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_DECREMENT: + // Decrement land rights tool size + gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); + + // Invalidate the window + Invalidate(); + break; + case WIDX_INCREMENT: + // Decrement land rights tool size + gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); + + // Invalidate the window + Invalidate(); + break; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (text.empty()) + return; + + if (widgetIndex != WIDX_PREVIEW) + return; + + const auto res = String::Parse(text); + if (res.has_value()) + { + uint16_t size; + size = res.value(); + size = std::max(kLandToolMinimumSize, size); + size = std::min(kLandToolMaximumSize, size); + gLandToolSize = size; + Invalidate(); + } + } + + void OnUpdate() override + { + frame_no++; + // Close window if another tool is open + if (!LandRightsToolIsActive()) Close(); - break; - case WIDX_PREVIEW: - InputSize(); - break; - case WIDX_BUY_LAND_RIGHTS: - if (_landRightsMode != LAND_RIGHTS_MODE_BUY_LAND) + } + + void OnPrepareDraw() override + { + SetWidgetPressed(WIDX_PREVIEW, true); + if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) + { + SetWidgetPressed(WIDX_BUY_LAND_RIGHTS, true); + SetWidgetPressed(WIDX_BUY_CONSTRUCTION_RIGHTS, false); + } + else if (_landRightsMode == LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS) + { + SetWidgetPressed(WIDX_BUY_LAND_RIGHTS, false); + SetWidgetPressed(WIDX_BUY_CONSTRUCTION_RIGHTS, true); + } + + window_land_rights_widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); + + if (gLandRemainingOwnershipSales == 0) + { + SetWidgetDisabled(WIDX_BUY_LAND_RIGHTS, true); + window_land_rights_widgets[WIDX_BUY_LAND_RIGHTS].tooltip = STR_NO_LAND_RIGHTS_FOR_SALE_TIP; + } + else + { + SetWidgetDisabled(WIDX_BUY_LAND_RIGHTS, false); + window_land_rights_widgets[WIDX_BUY_LAND_RIGHTS].tooltip = STR_BUY_LAND_RIGHTS_TIP; + } + + if (gLandRemainingConstructionSales == 0) + { + SetWidgetDisabled(WIDX_BUY_CONSTRUCTION_RIGHTS, true); + window_land_rights_widgets[WIDX_BUY_CONSTRUCTION_RIGHTS].tooltip = STR_NO_CONSTRUCTION_RIGHTS_FOR_SALE_TIP; + } + else + { + SetWidgetDisabled(WIDX_BUY_CONSTRUCTION_RIGHTS, false); + window_land_rights_widgets[WIDX_BUY_CONSTRUCTION_RIGHTS].tooltip = STR_BUY_CONSTRUCTION_RIGHTS_TIP; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + auto screenCoords = ScreenCoordsXY{ windowPos.x + window_land_rights_widgets[WIDX_PREVIEW].midX(), + windowPos.y + window_land_rights_widgets[WIDX_PREVIEW].midY() }; + + DrawWidgets(dpi); + // Draw number for tool sizes bigger than 7 + if (gLandToolSize > kLandToolMaximumSizeWithSprite) + { + auto ft = Formatter(); + ft.Add(gLandToolSize); + DrawTextBasic( + dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + } + + // Draw cost amount + if (_landRightsCost != kMoney64Undefined && _landRightsCost != 0 + && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + auto ft = Formatter(); + ft.Add(_landRightsCost); + screenCoords = { window_land_rights_widgets[WIDX_PREVIEW].midX() + windowPos.x, + window_land_rights_widgets[WIDX_PREVIEW].bottom + windowPos.y + 32 }; + DrawTextBasic(dpi, screenCoords, STR_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + } + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + + auto info = GetMapCoordinatesFromPos( + screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water)); + if (info.SpriteType == ViewportInteractionItem::None) + { + if (_landRightsCost != kMoney64Undefined) { - ToolSet(*this, WIDX_BUY_LAND_RIGHTS, Tool::UpArrow); - _landRightsMode = LAND_RIGHTS_MODE_BUY_LAND; - ShowLandRights(); - Invalidate(); + _landRightsCost = kMoney64Undefined; + WindowInvalidateByClass(WindowClass::ClearScenery); } - break; - case WIDX_BUY_CONSTRUCTION_RIGHTS: - if (_landRightsMode != LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS) + return; + } + auto mapTile = info.Loc; + + uint8_t state_changed = 0; + + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + state_changed++; + } + + if (gMapSelectType != MAP_SELECT_TYPE_FULL_LAND_RIGHTS) + { + gMapSelectType = MAP_SELECT_TYPE_FULL_LAND_RIGHTS; + state_changed++; + } + + int16_t tool_size = gLandToolSize; + if (tool_size == 0) + tool_size = 1; + + int16_t tool_length = (tool_size - 1) * 32; + + // Move to tool bottom left + mapTile.x -= (tool_size - 1) * 16; + mapTile.y -= (tool_size - 1) * 16; + mapTile = mapTile.ToTileStart(); + + if (gMapSelectPositionA.x != mapTile.x) + { + gMapSelectPositionA.x = mapTile.x; + state_changed++; + } + + if (gMapSelectPositionA.y != mapTile.y) + { + gMapSelectPositionA.y = mapTile.y; + state_changed++; + } + + mapTile.x += tool_length; + mapTile.y += tool_length; + + if (gMapSelectPositionB.x != mapTile.x) + { + gMapSelectPositionB.x = mapTile.x; + state_changed++; + } + + if (gMapSelectPositionB.y != mapTile.y) + { + gMapSelectPositionB.y = mapTile.y; + state_changed++; + } + + MapInvalidateSelectionRect(); + if (!state_changed) + return; + + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) ? LandBuyRightSetting::BuyLand + : LandBuyRightSetting::BuyConstructionRights); + auto res = GameActions::Query(&landBuyRightsAction); + + _landRightsCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; + } + + void OnToolAbort(WidgetIndex widgetIndex) override + { + if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) + { + HideLandRights(); + } + else + { + HideConstructionRights(); + } + } + + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) + { + if (screenCoords.x != LOCATION_NULL) { - ToolSet(*this, WIDX_BUY_CONSTRUCTION_RIGHTS, Tool::UpArrow); - _landRightsMode = LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS; - ShowConstructionRights(); - Invalidate(); + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyLand); + GameActions::Execute(&landBuyRightsAction); } - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_DECREMENT: - // Decrement land rights tool size - gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); - - // Invalidate the window - Invalidate(); - break; - case WIDX_INCREMENT: - // Decrement land rights tool size - gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); - - // Invalidate the window - Invalidate(); - break; - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (text.empty()) - return; - - if (widgetIndex != WIDX_PREVIEW) - return; - - const auto res = String::Parse(text); - if (res.has_value()) - { - uint16_t size; - size = res.value(); - size = std::max(kLandToolMinimumSize, size); - size = std::min(kLandToolMaximumSize, size); - gLandToolSize = size; - Invalidate(); - } - } - - void OnUpdate() override - { - frame_no++; - // Close window if another tool is open - if (!LandRightsToolIsActive()) - Close(); - } - - void OnPrepareDraw() override - { - SetWidgetPressed(WIDX_PREVIEW, true); - if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) - { - SetWidgetPressed(WIDX_BUY_LAND_RIGHTS, true); - SetWidgetPressed(WIDX_BUY_CONSTRUCTION_RIGHTS, false); - } - else if (_landRightsMode == LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS) - { - SetWidgetPressed(WIDX_BUY_LAND_RIGHTS, false); - SetWidgetPressed(WIDX_BUY_CONSTRUCTION_RIGHTS, true); - } - - window_land_rights_widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); - - if (gLandRemainingOwnershipSales == 0) - { - SetWidgetDisabled(WIDX_BUY_LAND_RIGHTS, true); - window_land_rights_widgets[WIDX_BUY_LAND_RIGHTS].tooltip = STR_NO_LAND_RIGHTS_FOR_SALE_TIP; - } - else - { - SetWidgetDisabled(WIDX_BUY_LAND_RIGHTS, false); - window_land_rights_widgets[WIDX_BUY_LAND_RIGHTS].tooltip = STR_BUY_LAND_RIGHTS_TIP; - } - - if (gLandRemainingConstructionSales == 0) - { - SetWidgetDisabled(WIDX_BUY_CONSTRUCTION_RIGHTS, true); - window_land_rights_widgets[WIDX_BUY_CONSTRUCTION_RIGHTS].tooltip = STR_NO_CONSTRUCTION_RIGHTS_FOR_SALE_TIP; - } - else - { - SetWidgetDisabled(WIDX_BUY_CONSTRUCTION_RIGHTS, false); - window_land_rights_widgets[WIDX_BUY_CONSTRUCTION_RIGHTS].tooltip = STR_BUY_CONSTRUCTION_RIGHTS_TIP; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - auto screenCoords = ScreenCoordsXY{ windowPos.x + window_land_rights_widgets[WIDX_PREVIEW].midX(), - windowPos.y + window_land_rights_widgets[WIDX_PREVIEW].midY() }; - - DrawWidgets(dpi); - // Draw number for tool sizes bigger than 7 - if (gLandToolSize > kLandToolMaximumSizeWithSprite) - { - auto ft = Formatter(); - ft.Add(gLandToolSize); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); - } - - // Draw cost amount - if (_landRightsCost != kMoney64Undefined && _landRightsCost != 0 && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - auto ft = Formatter(); - ft.Add(_landRightsCost); - screenCoords = { window_land_rights_widgets[WIDX_PREVIEW].midX() + windowPos.x, - window_land_rights_widgets[WIDX_PREVIEW].bottom + windowPos.y + 32 }; - DrawTextBasic(dpi, screenCoords, STR_COST_AMOUNT, ft, { TextAlignment::CENTRE }); - } - } - - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - auto info = GetMapCoordinatesFromPos( - screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water)); - if (info.SpriteType == ViewportInteractionItem::None) - { - if (_landRightsCost != kMoney64Undefined) - { - _landRightsCost = kMoney64Undefined; - WindowInvalidateByClass(WindowClass::ClearScenery); } - return; - } - auto mapTile = info.Loc; - - uint8_t state_changed = 0; - - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - state_changed++; - } - - if (gMapSelectType != MAP_SELECT_TYPE_FULL_LAND_RIGHTS) - { - gMapSelectType = MAP_SELECT_TYPE_FULL_LAND_RIGHTS; - state_changed++; - } - - int16_t tool_size = gLandToolSize; - if (tool_size == 0) - tool_size = 1; - - int16_t tool_length = (tool_size - 1) * 32; - - // Move to tool bottom left - mapTile.x -= (tool_size - 1) * 16; - mapTile.y -= (tool_size - 1) * 16; - mapTile = mapTile.ToTileStart(); - - if (gMapSelectPositionA.x != mapTile.x) - { - gMapSelectPositionA.x = mapTile.x; - state_changed++; - } - - if (gMapSelectPositionA.y != mapTile.y) - { - gMapSelectPositionA.y = mapTile.y; - state_changed++; - } - - mapTile.x += tool_length; - mapTile.y += tool_length; - - if (gMapSelectPositionB.x != mapTile.x) - { - gMapSelectPositionB.x = mapTile.x; - state_changed++; - } - - if (gMapSelectPositionB.y != mapTile.y) - { - gMapSelectPositionB.y = mapTile.y; - state_changed++; - } - - MapInvalidateSelectionRect(); - if (!state_changed) - return; - - auto landBuyRightsAction = LandBuyRightsAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) ? LandBuyRightSetting::BuyLand - : LandBuyRightSetting::BuyConstructionRights); - auto res = GameActions::Query(&landBuyRightsAction); - - _landRightsCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; - } - - void OnToolAbort(WidgetIndex widgetIndex) override - { - if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) - { - HideLandRights(); - } - else - { - HideConstructionRights(); - } - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) - { - if (screenCoords.x != LOCATION_NULL) + else { - auto landBuyRightsAction = LandBuyRightsAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - LandBuyRightSetting::BuyLand); - GameActions::Execute(&landBuyRightsAction); + if (screenCoords.x != LOCATION_NULL) + { + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyConstructionRights); + GameActions::Execute(&landBuyRightsAction); + } } } - else + + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - if (screenCoords.x != LOCATION_NULL) + if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) { - auto landBuyRightsAction = LandBuyRightsAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - LandBuyRightSetting::BuyConstructionRights); - GameActions::Execute(&landBuyRightsAction); + if (screenCoords.x != LOCATION_NULL) + { + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyLand); + GameActions::Execute(&landBuyRightsAction); + } + } + else + { + if (screenCoords.x != LOCATION_NULL) + { + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyConstructionRights); + GameActions::Execute(&landBuyRightsAction); + } } } - } - void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) + void OnResize() override { - if (screenCoords.x != LOCATION_NULL) - { - auto landBuyRightsAction = LandBuyRightsAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - LandBuyRightSetting::BuyLand); - GameActions::Execute(&landBuyRightsAction); - } + ResizeFrame(); } - else + + private: + uint8_t _landRightsMode; + money64 _landRightsCost; + + void InputSize() { - if (screenCoords.x != LOCATION_NULL) - { - auto landBuyRightsAction = LandBuyRightsAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - LandBuyRightSetting::BuyConstructionRights); - GameActions::Execute(&landBuyRightsAction); - } + Formatter ft; + ft.Add(kLandToolMinimumSize); + ft.Add(kLandToolMaximumSize); + WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); } - } - void OnResize() override + bool LandRightsToolIsActive() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return false; + if (gCurrentToolWidget.window_classification != WindowClass::LandRights) + return false; + return true; + } + }; + + WindowBase* WindowLandRightsOpen() { - ResizeFrame(); + return WindowFocusOrCreate( + WindowClass::LandRights, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); } - -private: - uint8_t _landRightsMode; - money64 _landRightsCost; - - void InputSize() - { - Formatter ft; - ft.Add(kLandToolMinimumSize); - ft.Add(kLandToolMaximumSize); - WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); - } - - bool LandRightsToolIsActive() - { - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return false; - if (gCurrentToolWidget.window_classification != WindowClass::LandRights) - return false; - return true; - } -}; - -WindowBase* WindowLandRightsOpen() -{ - return WindowFocusOrCreate( - WindowClass::LandRights, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index 62c3b2a0ae..93000d8d75 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -39,33 +39,33 @@ #include #include -using namespace OpenRCT2; - +namespace OpenRCT2::Ui::Windows +{ #pragma region Widgets -static constexpr StringId WINDOW_TITLE = STR_NONE; -static constexpr int32_t WW = 350; -static constexpr int32_t WH = 400; + static constexpr StringId WINDOW_TITLE = STR_NONE; + static constexpr int32_t WW = 350; + static constexpr int32_t WH = 400; -static constexpr uint16_t DATE_TIME_GAP = 2; + static constexpr uint16_t DATE_TIME_GAP = 2; -enum -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_RESIZE, - WIDX_DEFAULT, - WIDX_UP, - WIDX_NEW_FOLDER, - WIDX_NEW_FILE, - WIDX_SORT_NAME, - WIDX_SORT_DATE, - WIDX_SCROLL, - WIDX_BROWSE, -}; + enum + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_RESIZE, + WIDX_DEFAULT, + WIDX_UP, + WIDX_NEW_FOLDER, + WIDX_NEW_FILE, + WIDX_SORT_NAME, + WIDX_SORT_DATE, + WIDX_SCROLL, + WIDX_BROWSE, + }; -// clang-format off + // clang-format off static Widget window_loadsave_widgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), @@ -80,1081 +80,1088 @@ static Widget window_loadsave_widgets[] = MakeWidget({ 4, WH - 24}, {197, 19}, WindowWidgetType::Button, WindowColour::Primary, STR_FILEBROWSER_USE_SYSTEM_WINDOW ), // WIDX_BROWSE kWidgetsEnd, }; -// clang-format on + // clang-format on #pragma endregion -static WindowBase* WindowOverwritePromptOpen(const std::string_view name, const std::string_view path); + static WindowBase* WindowOverwritePromptOpen(const std::string_view name, const std::string_view path); -enum -{ - TYPE_DIRECTORY, - TYPE_FILE, -}; - -struct LoadSaveListItem -{ - std::string name{}; - std::string path{}; - time_t date_modified{ 0 }; - std::string date_formatted{}; - std::string time_formatted{}; - uint8_t type{ 0 }; - bool loaded{ false }; -}; - -static std::function _loadSaveCallback; -static TrackDesign* _trackDesign; - -static std::vector _listItems; -static char _directory[MAX_PATH]; -static char _parentDirectory[MAX_PATH]; -static u8string _extensionPattern; -static u8string _defaultPath; -static int32_t _type; - -static bool ListItemSort(LoadSaveListItem& a, LoadSaveListItem& b) -{ - if (a.type != b.type) - return a.type - b.type < 0; - - switch (gConfigGeneral.LoadSaveSort) + enum { - case Sort::NameAscending: - return StrLogicalCmp(a.name.c_str(), b.name.c_str()) < 0; - case Sort::NameDescending: - return -StrLogicalCmp(a.name.c_str(), b.name.c_str()) < 0; - case Sort::DateDescending: - return -difftime(a.date_modified, b.date_modified) < 0; - case Sort::DateAscending: - return difftime(a.date_modified, b.date_modified) < 0; - } - return StrLogicalCmp(a.name.c_str(), b.name.c_str()) < 0; -} + TYPE_DIRECTORY, + TYPE_FILE, + }; -static void SetAndSaveConfigPath(u8string& config_str, u8string_view path) -{ - config_str = Path::GetDirectory(path); - ConfigSaveDefault(); -} - -static bool IsValidPath(const char* path) -{ - // HACK This is needed because tracks get passed through with td? - // I am sure this will change eventually to use the new FileScanner - // which handles multiple patterns - auto filename = Path::GetFileNameWithoutExtension(path); - - return Platform::IsFilenameValid(filename); -} - -static u8string GetLastDirectoryByType(int32_t type) -{ - switch (type & 0x0E) + struct LoadSaveListItem { - case LOADSAVETYPE_GAME: - return gConfigGeneral.LastSaveGameDirectory; + std::string name{}; + std::string path{}; + time_t date_modified{ 0 }; + std::string date_formatted{}; + std::string time_formatted{}; + uint8_t type{ 0 }; + bool loaded{ false }; + }; - case LOADSAVETYPE_LANDSCAPE: - return gConfigGeneral.LastSaveLandscapeDirectory; + static std::function _loadSaveCallback; + static TrackDesign* _trackDesign; - case LOADSAVETYPE_SCENARIO: - return gConfigGeneral.LastSaveScenarioDirectory; + static std::vector _listItems; + static char _directory[MAX_PATH]; + static char _parentDirectory[MAX_PATH]; + static u8string _extensionPattern; + static u8string _defaultPath; + static int32_t _type; - case LOADSAVETYPE_TRACK: - return gConfigGeneral.LastSaveTrackDirectory; - - default: - return u8string(); - } -} - -static u8string GetInitialDirectoryByType(const int32_t type) -{ - std::optional subdir = std::nullopt; - switch (type & 0x0E) + static bool ListItemSort(LoadSaveListItem& a, LoadSaveListItem& b) { - case LOADSAVETYPE_GAME: - subdir = OpenRCT2::DIRID::SAVE; - break; + if (a.type != b.type) + return a.type - b.type < 0; - case LOADSAVETYPE_LANDSCAPE: - subdir = OpenRCT2::DIRID::LANDSCAPE; - break; - - case LOADSAVETYPE_SCENARIO: - subdir = OpenRCT2::DIRID::SCENARIO; - break; - - case LOADSAVETYPE_TRACK: - subdir = OpenRCT2::DIRID::TRACK; - break; - - case LOADSAVETYPE_HEIGHTMAP: - subdir = OpenRCT2::DIRID::HEIGHTMAP; - break; - } - - auto env = OpenRCT2::GetContext()->GetPlatformEnvironment(); - if (subdir.has_value()) - return env->GetDirectoryPath(OpenRCT2::DIRBASE::USER, subdir.value()); - else - return env->GetDirectoryPath(OpenRCT2::DIRBASE::USER); -} - -static const char* GetFilterPatternByType(const int32_t type, const bool isSave) -{ - switch (type & 0x0E) - { - case LOADSAVETYPE_GAME: - return isSave ? "*.park" : "*.park;*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea"; - - case LOADSAVETYPE_LANDSCAPE: - return isSave ? "*.park" : "*.park;*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea"; - - case LOADSAVETYPE_SCENARIO: - return isSave ? "*.park" : "*.park;*.sc6;*.sc4"; - - case LOADSAVETYPE_TRACK: - return isSave ? "*.td6" : "*.td6;*.td4"; - - case LOADSAVETYPE_HEIGHTMAP: - return "*.bmp;*.png"; - - default: - Guard::Fail("Unsupported load/save directory type."); - } - - return ""; -} - -static u8string RemovePatternWildcard(u8string_view pattern) -{ - while (pattern.length() >= 1 && pattern.front() == '*') - { - pattern.remove_prefix(1); - } - return u8string{ pattern }; -} - -static u8string GetDir(const int32_t type) -{ - u8string result = GetLastDirectoryByType(type); - if (result.empty() || !Path::DirectoryExists(result)) - { - result = GetInitialDirectoryByType(type); - } - return result; -} - -static void InvokeCallback(int32_t result, const utf8* path) -{ - if (_loadSaveCallback != nullptr) - { - _loadSaveCallback(result, path); - } -} - -static void Select(const char* path) -{ - if (!IsValidPath(path)) - { - ContextShowError(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); - return; - } - - char pathBuffer[MAX_PATH]; - SafeStrCpy(pathBuffer, path, sizeof(pathBuffer)); - - auto& gameState = GetGameState(); - - switch (_type & 0x0F) - { - case (LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME): - SetAndSaveConfigPath(gConfigGeneral.LastSaveGameDirectory, pathBuffer); - if (OpenRCT2::GetContext()->LoadParkFromFile(pathBuffer)) - { - InvokeCallback(MODAL_RESULT_OK, pathBuffer); - WindowCloseByClass(WindowClass::Loadsave); - GfxInvalidateScreen(); - } - else - { - // Not the best message... - ContextShowError(STR_LOAD_GAME, STR_FAILED_TO_LOAD_FILE_CONTAINS_INVALID_DATA, {}); - InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); - } - break; - - case (LOADSAVETYPE_SAVE | LOADSAVETYPE_GAME): - SetAndSaveConfigPath(gConfigGeneral.LastSaveGameDirectory, pathBuffer); - if (ScenarioSave(gameState, pathBuffer, gConfigGeneral.SavePluginData ? 1 : 0)) - { - gScenarioSavePath = pathBuffer; - gCurrentLoadedPath = pathBuffer; - gIsAutosaveLoaded = false; - gFirstTimeSaving = false; - - WindowCloseByClass(WindowClass::Loadsave); - GfxInvalidateScreen(); - - InvokeCallback(MODAL_RESULT_OK, pathBuffer); - } - else - { - ContextShowError(STR_SAVE_GAME, STR_GAME_SAVE_FAILED, {}); - InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); - } - break; - - case (LOADSAVETYPE_LOAD | LOADSAVETYPE_LANDSCAPE): - SetAndSaveConfigPath(gConfigGeneral.LastSaveLandscapeDirectory, pathBuffer); - if (Editor::LoadLandscape(pathBuffer)) - { - gCurrentLoadedPath = pathBuffer; - GfxInvalidateScreen(); - InvokeCallback(MODAL_RESULT_OK, pathBuffer); - } - else - { - // Not the best message... - ContextShowError(STR_LOAD_LANDSCAPE, STR_FAILED_TO_LOAD_FILE_CONTAINS_INVALID_DATA, {}); - InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); - } - break; - - case (LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE): - SetAndSaveConfigPath(gConfigGeneral.LastSaveLandscapeDirectory, pathBuffer); - gScenarioFileName = std::string(String::ToStringView(pathBuffer, std::size(pathBuffer))); - if (ScenarioSave(gameState, pathBuffer, gConfigGeneral.SavePluginData ? 3 : 2)) - { - gCurrentLoadedPath = pathBuffer; - WindowCloseByClass(WindowClass::Loadsave); - GfxInvalidateScreen(); - InvokeCallback(MODAL_RESULT_OK, pathBuffer); - } - else - { - ContextShowError(STR_SAVE_LANDSCAPE, STR_LANDSCAPE_SAVE_FAILED, {}); - InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); - } - break; - - case (LOADSAVETYPE_SAVE | LOADSAVETYPE_SCENARIO): + switch (gConfigGeneral.LoadSaveSort) { - SetAndSaveConfigPath(gConfigGeneral.LastSaveScenarioDirectory, pathBuffer); - int32_t parkFlagsBackup = gameState.ParkFlags; - gameState.ParkFlags &= ~PARK_FLAGS_SPRITES_INITIALISED; - gameState.EditorStep = EditorStep::Invalid; - gScenarioFileName = std::string(String::ToStringView(pathBuffer, std::size(pathBuffer))); - int32_t success = ScenarioSave(gameState, pathBuffer, gConfigGeneral.SavePluginData ? 3 : 2); - gameState.ParkFlags = parkFlagsBackup; + case Sort::NameAscending: + return StrLogicalCmp(a.name.c_str(), b.name.c_str()) < 0; + case Sort::NameDescending: + return -StrLogicalCmp(a.name.c_str(), b.name.c_str()) < 0; + case Sort::DateDescending: + return -difftime(a.date_modified, b.date_modified) < 0; + case Sort::DateAscending: + return difftime(a.date_modified, b.date_modified) < 0; + } + return StrLogicalCmp(a.name.c_str(), b.name.c_str()) < 0; + } - if (success) - { - WindowCloseByClass(WindowClass::Loadsave); - InvokeCallback(MODAL_RESULT_OK, pathBuffer); - TitleLoad(); - } - else - { - ContextShowError(STR_FILE_DIALOG_TITLE_SAVE_SCENARIO, STR_SCENARIO_SAVE_FAILED, {}); - gameState.EditorStep = EditorStep::ObjectiveSelection; - InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); - } - break; + static void SetAndSaveConfigPath(u8string& config_str, u8string_view path) + { + config_str = Path::GetDirectory(path); + ConfigSaveDefault(); + } + + static bool IsValidPath(const char* path) + { + // HACK This is needed because tracks get passed through with td? + // I am sure this will change eventually to use the new FileScanner + // which handles multiple patterns + auto filename = Path::GetFileNameWithoutExtension(path); + + return Platform::IsFilenameValid(filename); + } + + static u8string GetLastDirectoryByType(int32_t type) + { + switch (type & 0x0E) + { + case LOADSAVETYPE_GAME: + return gConfigGeneral.LastSaveGameDirectory; + + case LOADSAVETYPE_LANDSCAPE: + return gConfigGeneral.LastSaveLandscapeDirectory; + + case LOADSAVETYPE_SCENARIO: + return gConfigGeneral.LastSaveScenarioDirectory; + + case LOADSAVETYPE_TRACK: + return gConfigGeneral.LastSaveTrackDirectory; + + default: + return u8string(); + } + } + + static u8string GetInitialDirectoryByType(const int32_t type) + { + std::optional subdir = std::nullopt; + switch (type & 0x0E) + { + case LOADSAVETYPE_GAME: + subdir = OpenRCT2::DIRID::SAVE; + break; + + case LOADSAVETYPE_LANDSCAPE: + subdir = OpenRCT2::DIRID::LANDSCAPE; + break; + + case LOADSAVETYPE_SCENARIO: + subdir = OpenRCT2::DIRID::SCENARIO; + break; + + case LOADSAVETYPE_TRACK: + subdir = OpenRCT2::DIRID::TRACK; + break; + + case LOADSAVETYPE_HEIGHTMAP: + subdir = OpenRCT2::DIRID::HEIGHTMAP; + break; } - case (LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK): - { - SetAndSaveConfigPath(gConfigGeneral.LastSaveTrackDirectory, pathBuffer); - auto intent = Intent(WindowClass::InstallTrack); - intent.PutExtra(INTENT_EXTRA_PATH, std::string{ pathBuffer }); - ContextOpenIntent(&intent); - WindowCloseByClass(WindowClass::Loadsave); - InvokeCallback(MODAL_RESULT_OK, pathBuffer); - break; - } - - case (LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK): - { - SetAndSaveConfigPath(gConfigGeneral.LastSaveTrackDirectory, pathBuffer); - - const auto withExtension = Path::WithExtension(pathBuffer, ".td6"); - String::Set(pathBuffer, sizeof(pathBuffer), withExtension.c_str()); - - RCT2::T6Exporter t6Export{ _trackDesign }; - - auto success = t6Export.SaveTrack(pathBuffer); - - if (success) - { - WindowCloseByClass(WindowClass::Loadsave); - WindowRideMeasurementsDesignCancel(); - InvokeCallback(MODAL_RESULT_OK, path); - } - else - { - ContextShowError(STR_FILE_DIALOG_TITLE_SAVE_TRACK, STR_TRACK_SAVE_FAILED, {}); - InvokeCallback(MODAL_RESULT_FAIL, path); - } - break; - } - - case (LOADSAVETYPE_LOAD | LOADSAVETYPE_HEIGHTMAP): - WindowCloseByClass(WindowClass::Loadsave); - InvokeCallback(MODAL_RESULT_OK, pathBuffer); - break; - } -} - -static u8string OpenSystemFileBrowser(bool isSave) -{ - OpenRCT2::Ui::FileDialogDesc desc = {}; - u8string extension; - StringId title = STR_NONE; - switch (_type & 0x0E) - { - case LOADSAVETYPE_GAME: - extension = u8".park"; - title = isSave ? STR_FILE_DIALOG_TITLE_SAVE_GAME : STR_FILE_DIALOG_TITLE_LOAD_GAME; - desc.Filters.emplace_back(LanguageGetString(STR_OPENRCT2_SAVED_GAME), GetFilterPatternByType(_type, isSave)); - break; - - case LOADSAVETYPE_LANDSCAPE: - extension = u8".park"; - title = isSave ? STR_FILE_DIALOG_TITLE_SAVE_LANDSCAPE : STR_FILE_DIALOG_TITLE_LOAD_LANDSCAPE; - desc.Filters.emplace_back(LanguageGetString(STR_OPENRCT2_LANDSCAPE_FILE), GetFilterPatternByType(_type, isSave)); - break; - - case LOADSAVETYPE_SCENARIO: - extension = u8".park"; - title = STR_FILE_DIALOG_TITLE_SAVE_SCENARIO; - desc.Filters.emplace_back(LanguageGetString(STR_OPENRCT2_SCENARIO_FILE), GetFilterPatternByType(_type, isSave)); - break; - - case LOADSAVETYPE_TRACK: - extension = u8".td6"; - title = isSave ? STR_FILE_DIALOG_TITLE_SAVE_TRACK : STR_FILE_DIALOG_TITLE_INSTALL_NEW_TRACK_DESIGN; - desc.Filters.emplace_back(LanguageGetString(STR_OPENRCT2_TRACK_DESIGN_FILE), GetFilterPatternByType(_type, isSave)); - break; - - case LOADSAVETYPE_HEIGHTMAP: - title = STR_FILE_DIALOG_TITLE_LOAD_HEIGHTMAP; - desc.Filters.emplace_back(LanguageGetString(STR_OPENRCT2_HEIGHTMAP_FILE), GetFilterPatternByType(_type, isSave)); - break; - } - - u8string path = _directory; - if (isSave) - { - // The file browser requires a file path instead of just a directory - if (!_defaultPath.empty()) - { - path = Path::Combine(path, _defaultPath); - } + auto env = OpenRCT2::GetContext()->GetPlatformEnvironment(); + if (subdir.has_value()) + return env->GetDirectoryPath(OpenRCT2::DIRBASE::USER, subdir.value()); else + return env->GetDirectoryPath(OpenRCT2::DIRBASE::USER); + } + + static const char* GetFilterPatternByType(const int32_t type, const bool isSave) + { + switch (type & 0x0E) { - auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - auto buffer = park.Name; - if (buffer.empty()) - { - // Use localised "Unnamed Park" if park name was empty. - buffer = FormatStringID(STR_UNNAMED_PARK, nullptr); - } - path = Path::Combine(path, buffer); + case LOADSAVETYPE_GAME: + return isSave ? "*.park" : "*.park;*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea"; + + case LOADSAVETYPE_LANDSCAPE: + return isSave ? "*.park" : "*.park;*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea"; + + case LOADSAVETYPE_SCENARIO: + return isSave ? "*.park" : "*.park;*.sc6;*.sc4"; + + case LOADSAVETYPE_TRACK: + return isSave ? "*.td6" : "*.td6;*.td4"; + + case LOADSAVETYPE_HEIGHTMAP: + return "*.bmp;*.png"; + + default: + Guard::Fail("Unsupported load/save directory type."); + } + + return ""; + } + + static u8string RemovePatternWildcard(u8string_view pattern) + { + while (pattern.length() >= 1 && pattern.front() == '*') + { + pattern.remove_prefix(1); + } + return u8string{ pattern }; + } + + static u8string GetDir(const int32_t type) + { + u8string result = GetLastDirectoryByType(type); + if (result.empty() || !Path::DirectoryExists(result)) + { + result = GetInitialDirectoryByType(type); + } + return result; + } + + static void InvokeCallback(int32_t result, const utf8* path) + { + if (_loadSaveCallback != nullptr) + { + _loadSaveCallback(result, path); } } - desc.InitialDirectory = _directory; - desc.Type = isSave ? OpenRCT2::Ui::FileDialogType::Save : OpenRCT2::Ui::FileDialogType::Open; - desc.DefaultFilename = isSave ? path : u8string(); - - desc.Filters.emplace_back(LanguageGetString(STR_ALL_FILES), "*"); - - desc.Title = LanguageGetString(title); - return ContextOpenCommonFileDialog(desc); -} - -class LoadSaveWindow final : public Window -{ -public: - LoadSaveWindow(int32_t loadSaveType) - : type(loadSaveType) + static void Select(const char* path) { - } - -private: - int32_t maxDateWidth{ 0 }; - int32_t maxTimeWidth{ 0 }; - int32_t type; - -public: - void PopulateList(int32_t includeNewItem, const u8string& directory, std::string_view extensionPattern) - { - const auto absoluteDirectory = Path::GetAbsolute(directory); - SafeStrCpy(_directory, absoluteDirectory.c_str(), std::size(_directory)); - // Note: This compares the pointers, not values - _extensionPattern = extensionPattern; - - _listItems.clear(); - - // Show "new" buttons when saving - window_loadsave_widgets[WIDX_NEW_FILE].type = includeNewItem ? WindowWidgetType::Button : WindowWidgetType::Empty; - window_loadsave_widgets[WIDX_NEW_FOLDER].type = includeNewItem ? WindowWidgetType::Button : WindowWidgetType::Empty; - - int32_t drives = Platform::GetDrives(); - if (directory.empty() && drives) - { - // List Windows drives - disabled_widgets |= (1uLL << WIDX_NEW_FILE) | (1uLL << WIDX_NEW_FOLDER) | (1uLL << WIDX_UP); - static constexpr auto NumDriveLetters = 26; - for (int32_t x = 0; x < NumDriveLetters; x++) - { - if (drives & (1 << x)) - { - // If the drive exists, list it - LoadSaveListItem newListItem; - newListItem.path = std::string(1, 'A' + x) + ":" PATH_SEPARATOR; - newListItem.name = newListItem.path; - newListItem.type = TYPE_DIRECTORY; - - _listItems.push_back(std::move(newListItem)); - } - } - } - else - { - // Remove the separator at the end of the path, if present - SafeStrCpy(_parentDirectory, absoluteDirectory.c_str(), std::size(_parentDirectory)); - if (_parentDirectory[strlen(_parentDirectory) - 1] == *PATH_SEPARATOR - || _parentDirectory[strlen(_parentDirectory) - 1] == '/') - _parentDirectory[strlen(_parentDirectory) - 1] = '\0'; - - // Remove everything past the now last separator - char* ch = strrchr(_parentDirectory, *PATH_SEPARATOR); - char* posix_ch = strrchr(_parentDirectory, '/'); - ch = ch < posix_ch ? posix_ch : ch; - if (ch != nullptr) - { - *(ch + 1) = '\0'; - } - else if (drives) - { - // If on Windows, clear the entire path to show the drives - _parentDirectory[0] = '\0'; - } - else - { - // Else, go to the root directory - snprintf(_parentDirectory, MAX_PATH, "%c", *PATH_SEPARATOR); - } - - // Disable the Up button if the current directory is the root directory - if (String::IsNullOrEmpty(_parentDirectory) && !drives) - disabled_widgets |= (1uLL << WIDX_UP); - else - disabled_widgets &= ~(1uLL << WIDX_UP); - - // Re-enable the "new" buttons if these were disabled - disabled_widgets &= ~(1uLL << WIDX_NEW_FILE); - disabled_widgets &= ~(1uLL << WIDX_NEW_FOLDER); - - // List all directories - auto subDirectories = Path::GetDirectories(absoluteDirectory); - for (const auto& sdName : subDirectories) - { - auto subDir = sdName + PATH_SEPARATOR; - - LoadSaveListItem newListItem; - newListItem.path = Path::Combine(absoluteDirectory, subDir); - newListItem.name = std::move(subDir); - newListItem.type = TYPE_DIRECTORY; - newListItem.loaded = false; - - _listItems.push_back(std::move(newListItem)); - } - - // List all files with the wanted extensions - bool showExtension = false; - for (const u8string& extToken : String::Split(extensionPattern, ";")) - { - const u8string filter = Path::Combine(directory, extToken); - auto scanner = Path::ScanDirectory(filter, false); - while (scanner->Next()) - { - LoadSaveListItem newListItem; - newListItem.path = scanner->GetPath(); - newListItem.type = TYPE_FILE; - newListItem.date_modified = Platform::FileGetModifiedTime(newListItem.path.c_str()); - - // Cache a human-readable version of the modified date. - newListItem.date_formatted = Platform::FormatShortDate(newListItem.date_modified); - newListItem.time_formatted = Platform::FormatTime(newListItem.date_modified); - - // Mark if file is the currently loaded game - newListItem.loaded = newListItem.path.compare(gCurrentLoadedPath) == 0; - - // Remove the extension (but only the first extension token) - if (!showExtension) - { - newListItem.name = Path::GetFileNameWithoutExtension(newListItem.path); - } - else - { - newListItem.name = Path::GetFileName(newListItem.path); - } - - _listItems.push_back(std::move(newListItem)); - } - - showExtension = true; // Show any extension after the first iteration - } - - SortList(); - } - - Invalidate(); - } - - void ComputeMaxDateWidth() - { - // Generate a time object for a relatively wide time: 2000-02-20 00:00:00 - std::tm tm; - tm.tm_sec = 0; - tm.tm_min = 0; - tm.tm_hour = 0; - tm.tm_mday = 20; - tm.tm_mon = 2; - tm.tm_year = 100; - tm.tm_wday = 5; - tm.tm_yday = 51; - tm.tm_isdst = -1; - - std::time_t long_time = mktime(&tm); - - // Check how this date is represented (e.g. 2000-02-20, or 00/02/20) - std::string date = Platform::FormatShortDate(long_time); - maxDateWidth = GfxGetStringWidth(date.c_str(), FontStyle::Medium) + DATE_TIME_GAP; - - // Some locales do not use leading zeros for months and days, so let's try October, too. - tm.tm_mon = 10; - tm.tm_yday = 294; - long_time = mktime(&tm); - - // Again, check how this date is represented (e.g. 2000-10-20, or 00/10/20) - date = Platform::FormatShortDate(long_time); - maxDateWidth = std::max(maxDateWidth, GfxGetStringWidth(date.c_str(), FontStyle::Medium) + DATE_TIME_GAP); - - // Time appears to be universally represented with two digits for minutes, so 12:00 or 00:00 should be representable. - std::string time = Platform::FormatTime(long_time); - maxTimeWidth = GfxGetStringWidth(time.c_str(), FontStyle::Medium) + DATE_TIME_GAP; - } - - void SortList() - { - std::sort(_listItems.begin(), _listItems.end(), ListItemSort); - } - -#pragma region Events -public: - void OnOpen() override - { - widgets = window_loadsave_widgets; - - const auto uiContext = OpenRCT2::GetContext()->GetUiContext(); - if (!uiContext->HasFilePicker()) - { - disabled_widgets |= (1uLL << WIDX_BROWSE); - window_loadsave_widgets[WIDX_BROWSE].type = WindowWidgetType::Empty; - } - - // TODO: Split LOADSAVETYPE_* into two proper enum classes (one for load/save, the other for the type) - const bool isSave = (type & 0x01) == LOADSAVETYPE_SAVE; - const auto path = GetDir(type); - - const char* pattern = GetFilterPatternByType(type, isSave); - PopulateList(isSave, path, pattern); - no_list_items = static_cast(_listItems.size()); - selected_list_item = -1; - - InitScrollWidgets(); - ComputeMaxDateWidth(); - min_width = WW; - min_height = WH / 2; - max_width = WW * 2; - max_height = WH * 2; - } - - void OnClose() override - { - _listItems.clear(); - WindowCloseByClass(WindowClass::LoadsaveOverwritePrompt); - } - - void OnResize() override - { - if (width < min_width) - { - Invalidate(); - width = min_width; - } - if (height < min_height) - { - Invalidate(); - height = min_height; - } - } - - void OnPrepareDraw() override - { - ResizeFrameWithPage(); - - Widget* date_widget = &window_loadsave_widgets[WIDX_SORT_DATE]; - date_widget->right = width - 5; - date_widget->left = date_widget->right - (maxDateWidth + maxTimeWidth + (4 * DATE_TIME_GAP) + (kScrollBarWidth + 1)); - - window_loadsave_widgets[WIDX_SORT_NAME].left = 4; - window_loadsave_widgets[WIDX_SORT_NAME].right = window_loadsave_widgets[WIDX_SORT_DATE].left - 1; - - window_loadsave_widgets[WIDX_SCROLL].right = width - 5; - window_loadsave_widgets[WIDX_SCROLL].bottom = height - 30; - - window_loadsave_widgets[WIDX_BROWSE].top = height - 24; - window_loadsave_widgets[WIDX_BROWSE].bottom = height - 6; - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - const auto shortPath = ShortenPath(_directory, width - 8, FontStyle::Medium); - - // Format text - std::string buffer; - buffer.assign("{BLACK}"); - buffer += shortPath; - - // Draw path text - const auto normalisedPath = Platform::StrDecompToPrecomp(buffer.data()); - const auto* normalisedPathC = normalisedPath.c_str(); - auto ft = Formatter(); - ft.Add(normalisedPathC); - DrawTextEllipsised(dpi, windowPos + ScreenCoordsXY{ 4, 20 }, width - 8, STR_STRING, ft); - - // Name button text - StringId id = STR_NONE; - if (gConfigGeneral.LoadSaveSort == Sort::NameAscending) - id = STR_UP; - else if (gConfigGeneral.LoadSaveSort == Sort::NameDescending) - id = STR_DOWN; - - // Draw name button indicator. - Widget sort_name_widget = window_loadsave_widgets[WIDX_SORT_NAME]; - ft = Formatter(); - ft.Add(id); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ sort_name_widget.left + 11, sort_name_widget.top + 1 }, STR_NAME, ft, - { COLOUR_GREY }); - - // Date button text - if (gConfigGeneral.LoadSaveSort == Sort::DateAscending) - id = STR_UP; - else if (gConfigGeneral.LoadSaveSort == Sort::DateDescending) - id = STR_DOWN; - else - id = STR_NONE; - - Widget sort_date_widget = window_loadsave_widgets[WIDX_SORT_DATE]; - ft = Formatter(); - ft.Add(id); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ sort_date_widget.left + 5, sort_date_widget.top + 1 }, STR_DATE, ft, - { COLOUR_GREY }); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - bool isSave = (_type & 0x01) == LOADSAVETYPE_SAVE; - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - - case WIDX_UP: - PopulateList(isSave, _parentDirectory, _extensionPattern); - InitScrollWidgets(); - no_list_items = static_cast(_listItems.size()); - break; - - case WIDX_NEW_FILE: - WindowTextInputOpen( - this, WIDX_NEW_FILE, STR_NONE, STR_FILEBROWSER_FILE_NAME_PROMPT, {}, STR_STRING, - reinterpret_cast(_defaultPath.c_str()), 64); - break; - - case WIDX_NEW_FOLDER: - WindowTextInputRawOpen(this, WIDX_NEW_FOLDER, STR_NONE, STR_FILEBROWSER_FOLDER_NAME_PROMPT, {}, "", 64); - break; - - case WIDX_BROWSE: - { - u8string path = OpenSystemFileBrowser(isSave); - if (!path.empty()) - { - Select(path.c_str()); - } - else - { - // If user cancels file dialog, refresh list - PopulateList(isSave, _directory, _extensionPattern); - InitScrollWidgets(); - no_list_items = static_cast(_listItems.size()); - } - } - break; - - case WIDX_SORT_NAME: - if (gConfigGeneral.LoadSaveSort == Sort::NameAscending) - { - gConfigGeneral.LoadSaveSort = Sort::NameDescending; - } - else - { - gConfigGeneral.LoadSaveSort = Sort::NameAscending; - } - ConfigSaveDefault(); - SortList(); - Invalidate(); - break; - - case WIDX_SORT_DATE: - if (gConfigGeneral.LoadSaveSort == Sort::DateDescending) - { - gConfigGeneral.LoadSaveSort = Sort::DateAscending; - } - else - { - gConfigGeneral.LoadSaveSort = Sort::DateDescending; - } - ConfigSaveDefault(); - SortList(); - Invalidate(); - break; - - case WIDX_DEFAULT: - PopulateList(isSave, GetInitialDirectoryByType(_type).c_str(), _extensionPattern); - InitScrollWidgets(); - no_list_items = static_cast(_listItems.size()); - break; - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (text.empty()) - return; - - if (!Platform::IsFilenameValid(text)) + if (!IsValidPath(path)) { ContextShowError(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); return; } - switch (widgetIndex) + char pathBuffer[MAX_PATH]; + SafeStrCpy(pathBuffer, path, sizeof(pathBuffer)); + + auto& gameState = GetGameState(); + + switch (_type & 0x0F) { - case WIDX_NEW_FOLDER: - { - const u8string path = Path::Combine(_directory, text); - if (!Path::CreateDirectory(path)) + case (LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME): + SetAndSaveConfigPath(gConfigGeneral.LastSaveGameDirectory, pathBuffer); + if (OpenRCT2::GetContext()->LoadParkFromFile(pathBuffer)) { - ContextShowError(STR_UNABLE_TO_CREATE_FOLDER, STR_NONE, {}); - return; + InvokeCallback(MODAL_RESULT_OK, pathBuffer); + WindowCloseByClass(WindowClass::Loadsave); + GfxInvalidateScreen(); } + else + { + // Not the best message... + ContextShowError(STR_LOAD_GAME, STR_FAILED_TO_LOAD_FILE_CONTAINS_INVALID_DATA, {}); + InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); + } + break; + + case (LOADSAVETYPE_SAVE | LOADSAVETYPE_GAME): + SetAndSaveConfigPath(gConfigGeneral.LastSaveGameDirectory, pathBuffer); + if (ScenarioSave(gameState, pathBuffer, gConfigGeneral.SavePluginData ? 1 : 0)) + { + gScenarioSavePath = pathBuffer; + gCurrentLoadedPath = pathBuffer; + gIsAutosaveLoaded = false; + gFirstTimeSaving = false; + + WindowCloseByClass(WindowClass::Loadsave); + GfxInvalidateScreen(); + + InvokeCallback(MODAL_RESULT_OK, pathBuffer); + } + else + { + ContextShowError(STR_SAVE_GAME, STR_GAME_SAVE_FAILED, {}); + InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); + } + break; + + case (LOADSAVETYPE_LOAD | LOADSAVETYPE_LANDSCAPE): + SetAndSaveConfigPath(gConfigGeneral.LastSaveLandscapeDirectory, pathBuffer); + if (Editor::LoadLandscape(pathBuffer)) + { + gCurrentLoadedPath = pathBuffer; + GfxInvalidateScreen(); + InvokeCallback(MODAL_RESULT_OK, pathBuffer); + } + else + { + // Not the best message... + ContextShowError(STR_LOAD_LANDSCAPE, STR_FAILED_TO_LOAD_FILE_CONTAINS_INVALID_DATA, {}); + InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); + } + break; + + case (LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE): + SetAndSaveConfigPath(gConfigGeneral.LastSaveLandscapeDirectory, pathBuffer); + gScenarioFileName = std::string(String::ToStringView(pathBuffer, std::size(pathBuffer))); + if (ScenarioSave(gameState, pathBuffer, gConfigGeneral.SavePluginData ? 3 : 2)) + { + gCurrentLoadedPath = pathBuffer; + WindowCloseByClass(WindowClass::Loadsave); + GfxInvalidateScreen(); + InvokeCallback(MODAL_RESULT_OK, pathBuffer); + } + else + { + ContextShowError(STR_SAVE_LANDSCAPE, STR_LANDSCAPE_SAVE_FAILED, {}); + InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); + } + break; + + case (LOADSAVETYPE_SAVE | LOADSAVETYPE_SCENARIO): + { + SetAndSaveConfigPath(gConfigGeneral.LastSaveScenarioDirectory, pathBuffer); + int32_t parkFlagsBackup = gameState.ParkFlags; + gameState.ParkFlags &= ~PARK_FLAGS_SPRITES_INITIALISED; + gameState.EditorStep = EditorStep::Invalid; + gScenarioFileName = std::string(String::ToStringView(pathBuffer, std::size(pathBuffer))); + int32_t success = ScenarioSave(gameState, pathBuffer, gConfigGeneral.SavePluginData ? 3 : 2); + gameState.ParkFlags = parkFlagsBackup; + + if (success) + { + WindowCloseByClass(WindowClass::Loadsave); + InvokeCallback(MODAL_RESULT_OK, pathBuffer); + TitleLoad(); + } + else + { + ContextShowError(STR_FILE_DIALOG_TITLE_SAVE_SCENARIO, STR_SCENARIO_SAVE_FAILED, {}); + gameState.EditorStep = EditorStep::ObjectiveSelection; + InvokeCallback(MODAL_RESULT_FAIL, pathBuffer); + } + break; + } + + case (LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK): + { + SetAndSaveConfigPath(gConfigGeneral.LastSaveTrackDirectory, pathBuffer); + auto intent = Intent(WindowClass::InstallTrack); + intent.PutExtra(INTENT_EXTRA_PATH, std::string{ pathBuffer }); + ContextOpenIntent(&intent); + WindowCloseByClass(WindowClass::Loadsave); + InvokeCallback(MODAL_RESULT_OK, pathBuffer); + break; + } + + case (LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK): + { + SetAndSaveConfigPath(gConfigGeneral.LastSaveTrackDirectory, pathBuffer); + + const auto withExtension = Path::WithExtension(pathBuffer, ".td6"); + String::Set(pathBuffer, sizeof(pathBuffer), withExtension.c_str()); + + RCT2::T6Exporter t6Export{ _trackDesign }; + + auto success = t6Export.SaveTrack(pathBuffer); + + if (success) + { + WindowCloseByClass(WindowClass::Loadsave); + WindowRideMeasurementsDesignCancel(); + InvokeCallback(MODAL_RESULT_OK, path); + } + else + { + ContextShowError(STR_FILE_DIALOG_TITLE_SAVE_TRACK, STR_TRACK_SAVE_FAILED, {}); + InvokeCallback(MODAL_RESULT_FAIL, path); + } + break; + } + + case (LOADSAVETYPE_LOAD | LOADSAVETYPE_HEIGHTMAP): + WindowCloseByClass(WindowClass::Loadsave); + InvokeCallback(MODAL_RESULT_OK, pathBuffer); + break; + } + } + + static u8string OpenSystemFileBrowser(bool isSave) + { + OpenRCT2::Ui::FileDialogDesc desc = {}; + u8string extension; + StringId title = STR_NONE; + switch (_type & 0x0E) + { + case LOADSAVETYPE_GAME: + extension = u8".park"; + title = isSave ? STR_FILE_DIALOG_TITLE_SAVE_GAME : STR_FILE_DIALOG_TITLE_LOAD_GAME; + desc.Filters.emplace_back(LanguageGetString(STR_OPENRCT2_SAVED_GAME), GetFilterPatternByType(_type, isSave)); + break; + + case LOADSAVETYPE_LANDSCAPE: + extension = u8".park"; + title = isSave ? STR_FILE_DIALOG_TITLE_SAVE_LANDSCAPE : STR_FILE_DIALOG_TITLE_LOAD_LANDSCAPE; + desc.Filters.emplace_back( + LanguageGetString(STR_OPENRCT2_LANDSCAPE_FILE), GetFilterPatternByType(_type, isSave)); + break; + + case LOADSAVETYPE_SCENARIO: + extension = u8".park"; + title = STR_FILE_DIALOG_TITLE_SAVE_SCENARIO; + desc.Filters.emplace_back(LanguageGetString(STR_OPENRCT2_SCENARIO_FILE), GetFilterPatternByType(_type, isSave)); + break; + + case LOADSAVETYPE_TRACK: + extension = u8".td6"; + title = isSave ? STR_FILE_DIALOG_TITLE_SAVE_TRACK : STR_FILE_DIALOG_TITLE_INSTALL_NEW_TRACK_DESIGN; + desc.Filters.emplace_back( + LanguageGetString(STR_OPENRCT2_TRACK_DESIGN_FILE), GetFilterPatternByType(_type, isSave)); + break; + + case LOADSAVETYPE_HEIGHTMAP: + title = STR_FILE_DIALOG_TITLE_LOAD_HEIGHTMAP; + desc.Filters.emplace_back( + LanguageGetString(STR_OPENRCT2_HEIGHTMAP_FILE), GetFilterPatternByType(_type, isSave)); + break; + } + + u8string path = _directory; + if (isSave) + { + // The file browser requires a file path instead of just a directory + if (!_defaultPath.empty()) + { + path = Path::Combine(path, _defaultPath); + } + else + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto buffer = park.Name; + if (buffer.empty()) + { + // Use localised "Unnamed Park" if park name was empty. + buffer = FormatStringID(STR_UNNAMED_PARK, nullptr); + } + path = Path::Combine(path, buffer); + } + } + + desc.InitialDirectory = _directory; + desc.Type = isSave ? OpenRCT2::Ui::FileDialogType::Save : OpenRCT2::Ui::FileDialogType::Open; + desc.DefaultFilename = isSave ? path : u8string(); + + desc.Filters.emplace_back(LanguageGetString(STR_ALL_FILES), "*"); + + desc.Title = LanguageGetString(title); + return ContextOpenCommonFileDialog(desc); + } + + class LoadSaveWindow final : public Window + { + public: + LoadSaveWindow(int32_t loadSaveType) + : type(loadSaveType) + { + } + + private: + int32_t maxDateWidth{ 0 }; + int32_t maxTimeWidth{ 0 }; + int32_t type; + + public: + void PopulateList(int32_t includeNewItem, const u8string& directory, std::string_view extensionPattern) + { + const auto absoluteDirectory = Path::GetAbsolute(directory); + SafeStrCpy(_directory, absoluteDirectory.c_str(), std::size(_directory)); + // Note: This compares the pointers, not values + _extensionPattern = extensionPattern; + + _listItems.clear(); + + // Show "new" buttons when saving + window_loadsave_widgets[WIDX_NEW_FILE].type = includeNewItem ? WindowWidgetType::Button : WindowWidgetType::Empty; + window_loadsave_widgets[WIDX_NEW_FOLDER].type = includeNewItem ? WindowWidgetType::Button : WindowWidgetType::Empty; + + int32_t drives = Platform::GetDrives(); + if (directory.empty() && drives) + { + // List Windows drives + disabled_widgets |= (1uLL << WIDX_NEW_FILE) | (1uLL << WIDX_NEW_FOLDER) | (1uLL << WIDX_UP); + static constexpr auto NumDriveLetters = 26; + for (int32_t x = 0; x < NumDriveLetters; x++) + { + if (drives & (1 << x)) + { + // If the drive exists, list it + LoadSaveListItem newListItem; + newListItem.path = std::string(1, 'A' + x) + ":" PATH_SEPARATOR; + newListItem.name = newListItem.path; + newListItem.type = TYPE_DIRECTORY; + + _listItems.push_back(std::move(newListItem)); + } + } + } + else + { + // Remove the separator at the end of the path, if present + SafeStrCpy(_parentDirectory, absoluteDirectory.c_str(), std::size(_parentDirectory)); + if (_parentDirectory[strlen(_parentDirectory) - 1] == *PATH_SEPARATOR + || _parentDirectory[strlen(_parentDirectory) - 1] == '/') + _parentDirectory[strlen(_parentDirectory) - 1] = '\0'; + + // Remove everything past the now last separator + char* ch = strrchr(_parentDirectory, *PATH_SEPARATOR); + char* posix_ch = strrchr(_parentDirectory, '/'); + ch = ch < posix_ch ? posix_ch : ch; + if (ch != nullptr) + { + *(ch + 1) = '\0'; + } + else if (drives) + { + // If on Windows, clear the entire path to show the drives + _parentDirectory[0] = '\0'; + } + else + { + // Else, go to the root directory + snprintf(_parentDirectory, MAX_PATH, "%c", *PATH_SEPARATOR); + } + + // Disable the Up button if the current directory is the root directory + if (String::IsNullOrEmpty(_parentDirectory) && !drives) + disabled_widgets |= (1uLL << WIDX_UP); + else + disabled_widgets &= ~(1uLL << WIDX_UP); + + // Re-enable the "new" buttons if these were disabled + disabled_widgets &= ~(1uLL << WIDX_NEW_FILE); + disabled_widgets &= ~(1uLL << WIDX_NEW_FOLDER); + + // List all directories + auto subDirectories = Path::GetDirectories(absoluteDirectory); + for (const auto& sdName : subDirectories) + { + auto subDir = sdName + PATH_SEPARATOR; + + LoadSaveListItem newListItem; + newListItem.path = Path::Combine(absoluteDirectory, subDir); + newListItem.name = std::move(subDir); + newListItem.type = TYPE_DIRECTORY; + newListItem.loaded = false; + + _listItems.push_back(std::move(newListItem)); + } + + // List all files with the wanted extensions + bool showExtension = false; + for (const u8string& extToken : String::Split(extensionPattern, ";")) + { + const u8string filter = Path::Combine(directory, extToken); + auto scanner = Path::ScanDirectory(filter, false); + while (scanner->Next()) + { + LoadSaveListItem newListItem; + newListItem.path = scanner->GetPath(); + newListItem.type = TYPE_FILE; + newListItem.date_modified = Platform::FileGetModifiedTime(newListItem.path.c_str()); + + // Cache a human-readable version of the modified date. + newListItem.date_formatted = Platform::FormatShortDate(newListItem.date_modified); + newListItem.time_formatted = Platform::FormatTime(newListItem.date_modified); + + // Mark if file is the currently loaded game + newListItem.loaded = newListItem.path.compare(gCurrentLoadedPath) == 0; + + // Remove the extension (but only the first extension token) + if (!showExtension) + { + newListItem.name = Path::GetFileNameWithoutExtension(newListItem.path); + } + else + { + newListItem.name = Path::GetFileName(newListItem.path); + } + + _listItems.push_back(std::move(newListItem)); + } + + showExtension = true; // Show any extension after the first iteration + } + + SortList(); + } + + Invalidate(); + } + + void ComputeMaxDateWidth() + { + // Generate a time object for a relatively wide time: 2000-02-20 00:00:00 + std::tm tm; + tm.tm_sec = 0; + tm.tm_min = 0; + tm.tm_hour = 0; + tm.tm_mday = 20; + tm.tm_mon = 2; + tm.tm_year = 100; + tm.tm_wday = 5; + tm.tm_yday = 51; + tm.tm_isdst = -1; + + std::time_t long_time = mktime(&tm); + + // Check how this date is represented (e.g. 2000-02-20, or 00/02/20) + std::string date = Platform::FormatShortDate(long_time); + maxDateWidth = GfxGetStringWidth(date.c_str(), FontStyle::Medium) + DATE_TIME_GAP; + + // Some locales do not use leading zeros for months and days, so let's try October, too. + tm.tm_mon = 10; + tm.tm_yday = 294; + long_time = mktime(&tm); + + // Again, check how this date is represented (e.g. 2000-10-20, or 00/10/20) + date = Platform::FormatShortDate(long_time); + maxDateWidth = std::max(maxDateWidth, GfxGetStringWidth(date.c_str(), FontStyle::Medium) + DATE_TIME_GAP); + + // Time appears to be universally represented with two digits for minutes, so 12:00 or 00:00 should be + // representable. + std::string time = Platform::FormatTime(long_time); + maxTimeWidth = GfxGetStringWidth(time.c_str(), FontStyle::Medium) + DATE_TIME_GAP; + } + + void SortList() + { + std::sort(_listItems.begin(), _listItems.end(), ListItemSort); + } + +#pragma region Events + public: + void OnOpen() override + { + widgets = window_loadsave_widgets; + + const auto uiContext = OpenRCT2::GetContext()->GetUiContext(); + if (!uiContext->HasFilePicker()) + { + disabled_widgets |= (1uLL << WIDX_BROWSE); + window_loadsave_widgets[WIDX_BROWSE].type = WindowWidgetType::Empty; + } + + // TODO: Split LOADSAVETYPE_* into two proper enum classes (one for load/save, the other for the type) + const bool isSave = (type & 0x01) == LOADSAVETYPE_SAVE; + const auto path = GetDir(type); + + const char* pattern = GetFilterPatternByType(type, isSave); + PopulateList(isSave, path, pattern); + no_list_items = static_cast(_listItems.size()); + selected_list_item = -1; + + InitScrollWidgets(); + ComputeMaxDateWidth(); + min_width = WW; + min_height = WH / 2; + max_width = WW * 2; + max_height = WH * 2; + } + + void OnClose() override + { + _listItems.clear(); + WindowCloseByClass(WindowClass::LoadsaveOverwritePrompt); + } + + void OnResize() override + { + if (width < min_width) + { + Invalidate(); + width = min_width; + } + if (height < min_height) + { + Invalidate(); + height = min_height; + } + } + + void OnPrepareDraw() override + { + ResizeFrameWithPage(); + + Widget* date_widget = &window_loadsave_widgets[WIDX_SORT_DATE]; + date_widget->right = width - 5; + date_widget->left = date_widget->right + - (maxDateWidth + maxTimeWidth + (4 * DATE_TIME_GAP) + (kScrollBarWidth + 1)); + + window_loadsave_widgets[WIDX_SORT_NAME].left = 4; + window_loadsave_widgets[WIDX_SORT_NAME].right = window_loadsave_widgets[WIDX_SORT_DATE].left - 1; + + window_loadsave_widgets[WIDX_SCROLL].right = width - 5; + window_loadsave_widgets[WIDX_SCROLL].bottom = height - 30; + + window_loadsave_widgets[WIDX_BROWSE].top = height - 24; + window_loadsave_widgets[WIDX_BROWSE].bottom = height - 6; + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + const auto shortPath = ShortenPath(_directory, width - 8, FontStyle::Medium); + + // Format text + std::string buffer; + buffer.assign("{BLACK}"); + buffer += shortPath; + + // Draw path text + const auto normalisedPath = Platform::StrDecompToPrecomp(buffer.data()); + const auto* normalisedPathC = normalisedPath.c_str(); + auto ft = Formatter(); + ft.Add(normalisedPathC); + DrawTextEllipsised(dpi, windowPos + ScreenCoordsXY{ 4, 20 }, width - 8, STR_STRING, ft); + + // Name button text + StringId id = STR_NONE; + if (gConfigGeneral.LoadSaveSort == Sort::NameAscending) + id = STR_UP; + else if (gConfigGeneral.LoadSaveSort == Sort::NameDescending) + id = STR_DOWN; + + // Draw name button indicator. + Widget sort_name_widget = window_loadsave_widgets[WIDX_SORT_NAME]; + ft = Formatter(); + ft.Add(id); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ sort_name_widget.left + 11, sort_name_widget.top + 1 }, STR_NAME, ft, + { COLOUR_GREY }); + + // Date button text + if (gConfigGeneral.LoadSaveSort == Sort::DateAscending) + id = STR_UP; + else if (gConfigGeneral.LoadSaveSort == Sort::DateDescending) + id = STR_DOWN; + else + id = STR_NONE; + + Widget sort_date_widget = window_loadsave_widgets[WIDX_SORT_DATE]; + ft = Formatter(); + ft.Add(id); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ sort_date_widget.left + 5, sort_date_widget.top + 1 }, STR_DATE, ft, + { COLOUR_GREY }); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + bool isSave = (_type & 0x01) == LOADSAVETYPE_SAVE; + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + + case WIDX_UP: + PopulateList(isSave, _parentDirectory, _extensionPattern); + InitScrollWidgets(); + no_list_items = static_cast(_listItems.size()); + break; + + case WIDX_NEW_FILE: + WindowTextInputOpen( + this, WIDX_NEW_FILE, STR_NONE, STR_FILEBROWSER_FILE_NAME_PROMPT, {}, STR_STRING, + reinterpret_cast(_defaultPath.c_str()), 64); + break; + + case WIDX_NEW_FOLDER: + WindowTextInputRawOpen(this, WIDX_NEW_FOLDER, STR_NONE, STR_FILEBROWSER_FOLDER_NAME_PROMPT, {}, "", 64); + break; + + case WIDX_BROWSE: + { + u8string path = OpenSystemFileBrowser(isSave); + if (!path.empty()) + { + Select(path.c_str()); + } + else + { + // If user cancels file dialog, refresh list + PopulateList(isSave, _directory, _extensionPattern); + InitScrollWidgets(); + no_list_items = static_cast(_listItems.size()); + } + } + break; + + case WIDX_SORT_NAME: + if (gConfigGeneral.LoadSaveSort == Sort::NameAscending) + { + gConfigGeneral.LoadSaveSort = Sort::NameDescending; + } + else + { + gConfigGeneral.LoadSaveSort = Sort::NameAscending; + } + ConfigSaveDefault(); + SortList(); + Invalidate(); + break; + + case WIDX_SORT_DATE: + if (gConfigGeneral.LoadSaveSort == Sort::DateDescending) + { + gConfigGeneral.LoadSaveSort = Sort::DateAscending; + } + else + { + gConfigGeneral.LoadSaveSort = Sort::DateDescending; + } + ConfigSaveDefault(); + SortList(); + Invalidate(); + break; + + case WIDX_DEFAULT: + PopulateList(isSave, GetInitialDirectoryByType(_type).c_str(), _extensionPattern); + InitScrollWidgets(); + no_list_items = static_cast(_listItems.size()); + break; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (text.empty()) + return; + + if (!Platform::IsFilenameValid(text)) + { + ContextShowError(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); + return; + } + + switch (widgetIndex) + { + case WIDX_NEW_FOLDER: + { + const u8string path = Path::Combine(_directory, text); + if (!Path::CreateDirectory(path)) + { + ContextShowError(STR_UNABLE_TO_CREATE_FOLDER, STR_NONE, {}); + return; + } + + no_list_items = 0; + selected_list_item = -1; + + PopulateList((_type & 1) == LOADSAVETYPE_SAVE, path, _extensionPattern); + InitScrollWidgets(); + + no_list_items = static_cast(_listItems.size()); + Invalidate(); + break; + } + + case WIDX_NEW_FILE: + { + const u8string path = Path::WithExtension( + Path::Combine(_directory, text), RemovePatternWildcard(_extensionPattern)); + + if (File::Exists(path)) + WindowOverwritePromptOpen(text, path); + else + Select(path.c_str()); + break; + } + } + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + return { 0, no_list_items * SCROLLABLE_ROW_HEIGHT }; + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int32_t selectedItem; + + selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (selectedItem >= no_list_items) + return; + + selected_list_item = selectedItem; + + Invalidate(); + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int32_t selectedItem; + + selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (selectedItem >= no_list_items) + return; + + if (_listItems[selectedItem].type == TYPE_DIRECTORY) + { + // The selected item is a folder + int32_t includeNewItem; no_list_items = 0; selected_list_item = -1; + includeNewItem = (_type & 1) == LOADSAVETYPE_SAVE; - PopulateList((_type & 1) == LOADSAVETYPE_SAVE, path, _extensionPattern); + char directory[MAX_PATH]; + SafeStrCpy(directory, _listItems[selectedItem].path.c_str(), sizeof(directory)); + + PopulateList(includeNewItem, directory, _extensionPattern); InitScrollWidgets(); no_list_items = static_cast(_listItems.size()); - Invalidate(); - break; } - - case WIDX_NEW_FILE: - { - const u8string path = Path::WithExtension( - Path::Combine(_directory, text), RemovePatternWildcard(_extensionPattern)); - - if (File::Exists(path)) - WindowOverwritePromptOpen(text, path); - else - Select(path.c_str()); - break; - } - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - return { 0, no_list_items * SCROLLABLE_ROW_HEIGHT }; - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - int32_t selectedItem; - - selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (selectedItem >= no_list_items) - return; - - selected_list_item = selectedItem; - - Invalidate(); - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - int32_t selectedItem; - - selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (selectedItem >= no_list_items) - return; - - if (_listItems[selectedItem].type == TYPE_DIRECTORY) - { - // The selected item is a folder - int32_t includeNewItem; - - no_list_items = 0; - selected_list_item = -1; - includeNewItem = (_type & 1) == LOADSAVETYPE_SAVE; - - char directory[MAX_PATH]; - SafeStrCpy(directory, _listItems[selectedItem].path.c_str(), sizeof(directory)); - - PopulateList(includeNewItem, directory, _extensionPattern); - InitScrollWidgets(); - - no_list_items = static_cast(_listItems.size()); - } - else - { - // TYPE_FILE - // Load or overwrite - if ((_type & 0x01) == LOADSAVETYPE_SAVE) - WindowOverwritePromptOpen(_listItems[selectedItem].name, _listItems[selectedItem].path); else - Select(_listItems[selectedItem].path.c_str()); + { + // TYPE_FILE + // Load or overwrite + if ((_type & 0x01) == LOADSAVETYPE_SAVE) + WindowOverwritePromptOpen(_listItems[selectedItem].name, _listItems[selectedItem].path); + else + Select(_listItems[selectedItem].path.c_str()); + } } - } - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - GfxFillRect( - dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - const int32_t listWidth = widgets[WIDX_SCROLL].width(); - const int32_t dateAnchor = widgets[WIDX_SORT_DATE].left + maxDateWidth + DATE_TIME_GAP; - - for (int32_t i = 0; i < no_list_items; i++) + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override { - int32_t y = i * SCROLLABLE_ROW_HEIGHT; - if (y > dpi.y + dpi.height) + GfxFillRect( + dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); + const int32_t listWidth = widgets[WIDX_SCROLL].width(); + const int32_t dateAnchor = widgets[WIDX_SORT_DATE].left + maxDateWidth + DATE_TIME_GAP; + + for (int32_t i = 0; i < no_list_items; i++) + { + int32_t y = i * SCROLLABLE_ROW_HEIGHT; + if (y > dpi.y + dpi.height) + break; + + if (y + SCROLLABLE_ROW_HEIGHT < dpi.y) + continue; + + StringId stringId = STR_BLACK_STRING; + + // If hovering over item, change the color and fill the backdrop. + if (i == selected_list_item) + { + stringId = STR_WINDOW_COLOUR_2_STRINGID; + GfxFilterRect(dpi, { 0, y, listWidth, y + SCROLLABLE_ROW_HEIGHT }, FilterPaletteID::PaletteDarken1); + } + // display a marker next to the currently loaded game file + if (_listItems[i].loaded) + { + auto ft = Formatter(); + ft.Add(STR_RIGHTGUILLEMET); + DrawTextBasic(dpi, { 0, y }, stringId, ft); + } + + // Print filename + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(_listItems[i].name.c_str()); + int32_t max_file_width = widgets[WIDX_SORT_NAME].width() - 10; + DrawTextEllipsised(dpi, { 10, y }, max_file_width, stringId, ft); + + // Print formatted modified date, if this is a file + if (_listItems[i].type == TYPE_FILE) + { + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(_listItems[i].date_formatted.c_str()); + DrawTextEllipsised( + dpi, { dateAnchor - DATE_TIME_GAP, y }, maxDateWidth, stringId, ft, { TextAlignment::RIGHT }); + + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(_listItems[i].time_formatted.c_str()); + DrawTextEllipsised(dpi, { dateAnchor + DATE_TIME_GAP, y }, maxTimeWidth, stringId, ft); + } + } + } +#pragma endregion + }; + + WindowBase* WindowLoadsaveOpen( + int32_t type, std::string_view defaultPath, std::function callback, + TrackDesign* trackDesign) + { + _loadSaveCallback = callback; + _trackDesign = trackDesign; + _type = type; + _defaultPath = defaultPath; + + bool isSave = (type & 0x01) == LOADSAVETYPE_SAVE; + + // Bypass the lot? + auto hasFilePicker = OpenRCT2::GetContext()->GetUiContext()->HasFilePicker(); + if (gConfigGeneral.UseNativeBrowseDialog && hasFilePicker) + { + const u8string path = OpenSystemFileBrowser(isSave); + if (!path.empty()) + { + Select(path.c_str()); + } + return nullptr; + } + + const u8string path = GetDir(type); + + auto* w = static_cast(WindowBringToFrontByClass(WindowClass::Loadsave)); + if (w == nullptr) + { + w = WindowCreate( + WindowClass::Loadsave, WW, WH, WF_STICK_TO_FRONT | WF_RESIZABLE | WF_AUTO_POSITION | WF_CENTRE_SCREEN, type); + } + + switch (type & 0x0E) + { + case LOADSAVETYPE_GAME: + w->widgets[WIDX_TITLE].text = isSave ? STR_FILE_DIALOG_TITLE_SAVE_GAME : STR_FILE_DIALOG_TITLE_LOAD_GAME; break; - if (y + SCROLLABLE_ROW_HEIGHT < dpi.y) - continue; + case LOADSAVETYPE_LANDSCAPE: + w->widgets[WIDX_TITLE].text = isSave ? STR_FILE_DIALOG_TITLE_SAVE_LANDSCAPE + : STR_FILE_DIALOG_TITLE_LOAD_LANDSCAPE; + break; - StringId stringId = STR_BLACK_STRING; + case LOADSAVETYPE_SCENARIO: + w->widgets[WIDX_TITLE].text = STR_FILE_DIALOG_TITLE_SAVE_SCENARIO; + break; - // If hovering over item, change the color and fill the backdrop. - if (i == selected_list_item) - { - stringId = STR_WINDOW_COLOUR_2_STRINGID; - GfxFilterRect(dpi, { 0, y, listWidth, y + SCROLLABLE_ROW_HEIGHT }, FilterPaletteID::PaletteDarken1); - } - // display a marker next to the currently loaded game file - if (_listItems[i].loaded) - { - auto ft = Formatter(); - ft.Add(STR_RIGHTGUILLEMET); - DrawTextBasic(dpi, { 0, y }, stringId, ft); - } + case LOADSAVETYPE_TRACK: + w->widgets[WIDX_TITLE].text = isSave ? STR_FILE_DIALOG_TITLE_SAVE_TRACK + : STR_FILE_DIALOG_TITLE_INSTALL_NEW_TRACK_DESIGN; + break; - // Print filename - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(_listItems[i].name.c_str()); - int32_t max_file_width = widgets[WIDX_SORT_NAME].width() - 10; - DrawTextEllipsised(dpi, { 10, y }, max_file_width, stringId, ft); + case LOADSAVETYPE_HEIGHTMAP: + Guard::Assert(!isSave, "Cannot save images through loadsave window"); + w->widgets[WIDX_TITLE].text = STR_FILE_DIALOG_TITLE_LOAD_HEIGHTMAP; + break; - // Print formatted modified date, if this is a file - if (_listItems[i].type == TYPE_FILE) - { - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(_listItems[i].date_formatted.c_str()); - DrawTextEllipsised( - dpi, { dateAnchor - DATE_TIME_GAP, y }, maxDateWidth, stringId, ft, { TextAlignment::RIGHT }); - - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(_listItems[i].time_formatted.c_str()); - DrawTextEllipsised(dpi, { dateAnchor + DATE_TIME_GAP, y }, maxTimeWidth, stringId, ft); - } + default: + Guard::Fail("Unsupported load/save type: %d", type & 0x0F); + break; } + + return w; } -#pragma endregion -}; - -WindowBase* WindowLoadsaveOpen( - int32_t type, std::string_view defaultPath, std::function callback, - TrackDesign* trackDesign) -{ - _loadSaveCallback = callback; - _trackDesign = trackDesign; - _type = type; - _defaultPath = defaultPath; - - bool isSave = (type & 0x01) == LOADSAVETYPE_SAVE; - - // Bypass the lot? - auto hasFilePicker = OpenRCT2::GetContext()->GetUiContext()->HasFilePicker(); - if (gConfigGeneral.UseNativeBrowseDialog && hasFilePicker) - { - const u8string path = OpenSystemFileBrowser(isSave); - if (!path.empty()) - { - Select(path.c_str()); - } - return nullptr; - } - - const u8string path = GetDir(type); - - auto* w = static_cast(WindowBringToFrontByClass(WindowClass::Loadsave)); - if (w == nullptr) - { - w = WindowCreate( - WindowClass::Loadsave, WW, WH, WF_STICK_TO_FRONT | WF_RESIZABLE | WF_AUTO_POSITION | WF_CENTRE_SCREEN, type); - } - - switch (type & 0x0E) - { - case LOADSAVETYPE_GAME: - w->widgets[WIDX_TITLE].text = isSave ? STR_FILE_DIALOG_TITLE_SAVE_GAME : STR_FILE_DIALOG_TITLE_LOAD_GAME; - break; - - case LOADSAVETYPE_LANDSCAPE: - w->widgets[WIDX_TITLE].text = isSave ? STR_FILE_DIALOG_TITLE_SAVE_LANDSCAPE : STR_FILE_DIALOG_TITLE_LOAD_LANDSCAPE; - break; - - case LOADSAVETYPE_SCENARIO: - w->widgets[WIDX_TITLE].text = STR_FILE_DIALOG_TITLE_SAVE_SCENARIO; - break; - - case LOADSAVETYPE_TRACK: - w->widgets[WIDX_TITLE].text = isSave ? STR_FILE_DIALOG_TITLE_SAVE_TRACK - : STR_FILE_DIALOG_TITLE_INSTALL_NEW_TRACK_DESIGN; - break; - - case LOADSAVETYPE_HEIGHTMAP: - Guard::Assert(!isSave, "Cannot save images through loadsave window"); - w->widgets[WIDX_TITLE].text = STR_FILE_DIALOG_TITLE_LOAD_HEIGHTMAP; - break; - - default: - Guard::Fail("Unsupported load/save type: %d", type & 0x0F); - break; - } - - return w; -} #pragma region Overwrite prompt -constexpr int32_t OVERWRITE_WW = 200; -constexpr int32_t OVERWRITE_WH = 100; + constexpr int32_t OVERWRITE_WW = 200; + constexpr int32_t OVERWRITE_WH = 100; -enum -{ - WIDX_OVERWRITE_BACKGROUND, - WIDX_OVERWRITE_TITLE, - WIDX_OVERWRITE_CLOSE, - WIDX_OVERWRITE_OVERWRITE, - WIDX_OVERWRITE_CANCEL -}; - -static Widget window_overwrite_prompt_widgets[] = { - WINDOW_SHIM_WHITE(STR_FILEBROWSER_OVERWRITE_TITLE, OVERWRITE_WW, OVERWRITE_WH), - { WindowWidgetType::Button, 0, 10, 94, OVERWRITE_WH - 20, OVERWRITE_WH - 9, STR_FILEBROWSER_OVERWRITE_TITLE, STR_NONE }, - { WindowWidgetType::Button, 0, OVERWRITE_WW - 95, OVERWRITE_WW - 11, OVERWRITE_WH - 20, OVERWRITE_WH - 9, - STR_SAVE_PROMPT_CANCEL, STR_NONE }, - kWidgetsEnd, -}; - -class OverwritePromptWindow final : public Window -{ - std::string _name; - std::string _path; - -public: - OverwritePromptWindow(const std::string_view name, const std::string_view path) - : _name(name) - , _path(path) + enum { - } + WIDX_OVERWRITE_BACKGROUND, + WIDX_OVERWRITE_TITLE, + WIDX_OVERWRITE_CLOSE, + WIDX_OVERWRITE_OVERWRITE, + WIDX_OVERWRITE_CANCEL + }; - void OnOpen() override - { - widgets = window_overwrite_prompt_widgets; - colours[0] = TRANSLUCENT(COLOUR_BORDEAUX_RED); - } + static Widget window_overwrite_prompt_widgets[] = { + WINDOW_SHIM_WHITE(STR_FILEBROWSER_OVERWRITE_TITLE, OVERWRITE_WW, OVERWRITE_WH), + { WindowWidgetType::Button, 0, 10, 94, OVERWRITE_WH - 20, OVERWRITE_WH - 9, STR_FILEBROWSER_OVERWRITE_TITLE, STR_NONE }, + { WindowWidgetType::Button, 0, OVERWRITE_WW - 95, OVERWRITE_WW - 11, OVERWRITE_WH - 20, OVERWRITE_WH - 9, + STR_SAVE_PROMPT_CANCEL, STR_NONE }, + kWidgetsEnd, + }; - void OnMouseUp(WidgetIndex widgetIndex) override + class OverwritePromptWindow final : public Window { - switch (widgetIndex) + std::string _name; + std::string _path; + + public: + OverwritePromptWindow(const std::string_view name, const std::string_view path) + : _name(name) + , _path(path) { - case WIDX_OVERWRITE_OVERWRITE: - { - Select(_path.c_str()); - - // As the LoadSaveWindow::Select function can change the order of the - // windows we can't use WindowClose(w). - WindowCloseByClass(WindowClass::LoadsaveOverwritePrompt); - break; - } - - case WIDX_OVERWRITE_CANCEL: - case WIDX_OVERWRITE_CLOSE: - Close(); - break; } - } - void OnDraw(DrawPixelInfo& dpi) override + void OnOpen() override + { + widgets = window_overwrite_prompt_widgets; + colours[0] = TRANSLUCENT(COLOUR_BORDEAUX_RED); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_OVERWRITE_OVERWRITE: + { + Select(_path.c_str()); + + // As the LoadSaveWindow::Select function can change the order of the + // windows we can't use WindowClose(w). + WindowCloseByClass(WindowClass::LoadsaveOverwritePrompt); + break; + } + + case WIDX_OVERWRITE_CANCEL: + case WIDX_OVERWRITE_CLOSE: + Close(); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(_name.c_str()); + + ScreenCoordsXY stringCoords(windowPos.x + width / 2, windowPos.y + (height / 2) - 3); + DrawTextWrapped(dpi, stringCoords, width - 4, STR_FILEBROWSER_OVERWRITE_PROMPT, ft, { TextAlignment::CENTRE }); + } + }; + + static WindowBase* WindowOverwritePromptOpen(const std::string_view name, const std::string_view path) { - DrawWidgets(dpi); + WindowCloseByClass(WindowClass::LoadsaveOverwritePrompt); - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(_name.c_str()); - - ScreenCoordsXY stringCoords(windowPos.x + width / 2, windowPos.y + (height / 2) - 3); - DrawTextWrapped(dpi, stringCoords, width - 4, STR_FILEBROWSER_OVERWRITE_PROMPT, ft, { TextAlignment::CENTRE }); + return WindowCreate( + WindowClass::LoadsaveOverwritePrompt, OVERWRITE_WW, OVERWRITE_WH, + WF_TRANSPARENT | WF_STICK_TO_FRONT | WF_CENTRE_SCREEN, name, path); } -}; - -static WindowBase* WindowOverwritePromptOpen(const std::string_view name, const std::string_view path) -{ - WindowCloseByClass(WindowClass::LoadsaveOverwritePrompt); - - return WindowCreate( - WindowClass::LoadsaveOverwritePrompt, OVERWRITE_WW, OVERWRITE_WH, WF_TRANSPARENT | WF_STICK_TO_FRONT | WF_CENTRE_SCREEN, - name, path); -} #pragma endregion +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Main.cpp b/src/openrct2-ui/windows/Main.cpp index b61bb7b880..29fd68f364 100644 --- a/src/openrct2-ui/windows/Main.cpp +++ b/src/openrct2-ui/windows/Main.cpp @@ -15,63 +15,67 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off static Widget _mainWidgets[] = { MakeWidget({0, 0}, {0, 0}, WindowWidgetType::Viewport, WindowColour::Primary, STR_VIEWPORT), kWidgetsEnd, }; -// clang-format on + // clang-format on -class MainWindow final : public Window -{ -public: - void OnOpen() override + class MainWindow final : public Window { - _mainWidgets[0].right = width; - _mainWidgets[0].bottom = height; - widgets = _mainWidgets; - - ViewportCreate(this, windowPos, width, height, Focus(CoordsXYZ(0x0FFF, 0x0FFF, 0))); - if (viewport != nullptr) + public: + void OnOpen() override { - SetViewportFlags(); - viewport->rotation = 0; + _mainWidgets[0].right = width; + _mainWidgets[0].bottom = height; + widgets = _mainWidgets; + + ViewportCreate(this, windowPos, width, height, Focus(CoordsXYZ(0x0FFF, 0x0FFF, 0))); + if (viewport != nullptr) + { + SetViewportFlags(); + viewport->rotation = 0; + } + gShowGridLinesRefCount = 0; + gShowLandRightsRefCount = 0; + gShowConstructionRightsRefCount = 0; + WindowFootpathResetSelectedPath(); } - gShowGridLinesRefCount = 0; - gShowLandRightsRefCount = 0; - gShowConstructionRightsRefCount = 0; - WindowFootpathResetSelectedPath(); - } - void OnDraw(DrawPixelInfo& dpi) override + void OnDraw(DrawPixelInfo& dpi) override + { + ViewportRender(dpi, viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } }); + } + + private: + void SetViewportFlags() + { + viewport->flags |= VIEWPORT_FLAG_SOUND_ON; + if (gConfigGeneral.InvisibleRides) + viewport->flags |= VIEWPORT_FLAG_INVISIBLE_RIDES; + if (gConfigGeneral.InvisibleVehicles) + viewport->flags |= VIEWPORT_FLAG_INVISIBLE_VEHICLES; + if (gConfigGeneral.InvisibleTrees) + viewport->flags |= VIEWPORT_FLAG_INVISIBLE_VEGETATION; + if (gConfigGeneral.InvisibleScenery) + viewport->flags |= VIEWPORT_FLAG_INVISIBLE_SCENERY; + if (gConfigGeneral.InvisiblePaths) + viewport->flags |= VIEWPORT_FLAG_INVISIBLE_PATHS; + if (gConfigGeneral.InvisibleSupports) + viewport->flags |= VIEWPORT_FLAG_INVISIBLE_SUPPORTS; + } + }; + + /** + * Creates the main window that holds the main viewport. + * rct2: 0x0066B3E8 + */ + WindowBase* WindowMainOpen() { - ViewportRender(dpi, viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } }); + return WindowCreate( + WindowClass::MainWindow, { 0, 0 }, ContextGetWidth(), ContextGetHeight(), WF_STICK_TO_BACK); } - -private: - void SetViewportFlags() - { - viewport->flags |= VIEWPORT_FLAG_SOUND_ON; - if (gConfigGeneral.InvisibleRides) - viewport->flags |= VIEWPORT_FLAG_INVISIBLE_RIDES; - if (gConfigGeneral.InvisibleVehicles) - viewport->flags |= VIEWPORT_FLAG_INVISIBLE_VEHICLES; - if (gConfigGeneral.InvisibleTrees) - viewport->flags |= VIEWPORT_FLAG_INVISIBLE_VEGETATION; - if (gConfigGeneral.InvisibleScenery) - viewport->flags |= VIEWPORT_FLAG_INVISIBLE_SCENERY; - if (gConfigGeneral.InvisiblePaths) - viewport->flags |= VIEWPORT_FLAG_INVISIBLE_PATHS; - if (gConfigGeneral.InvisibleSupports) - viewport->flags |= VIEWPORT_FLAG_INVISIBLE_SUPPORTS; - } -}; - -/** - * Creates the main window that holds the main viewport. - * rct2: 0x0066B3E8 - */ -WindowBase* WindowMainOpen() -{ - return WindowCreate(WindowClass::MainWindow, { 0, 0 }, ContextGetWidth(), ContextGetHeight(), WF_STICK_TO_BACK); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Map.cpp b/src/openrct2-ui/windows/Map.cpp index 296fdc8a07..3c07bf2e23 100644 --- a/src/openrct2-ui/windows/Map.cpp +++ b/src/openrct2-ui/windows/Map.cpp @@ -40,70 +40,70 @@ #include #include -using namespace OpenRCT2; - -static constexpr uint16_t MapColour2(uint8_t colourA, uint8_t colourB) +namespace OpenRCT2::Ui::Windows { - return (colourA << 8) | colourB; -} -static constexpr uint16_t MapColour(uint8_t colour) -{ - return MapColour2(colour, colour); -} -static constexpr uint16_t MapColourUnowned(uint16_t colour) -{ - return MapColour2((colour & 0xFF00) >> 8, PALETTE_INDEX_10); -} + static constexpr uint16_t MapColour2(uint8_t colourA, uint8_t colourB) + { + return (colourA << 8) | colourB; + } + static constexpr uint16_t MapColour(uint8_t colour) + { + return MapColour2(colour, colour); + } + static constexpr uint16_t MapColourUnowned(uint16_t colour) + { + return MapColour2((colour & 0xFF00) >> 8, PALETTE_INDEX_10); + } -constexpr int32_t MAP_WINDOW_MAP_SIZE = MAXIMUM_MAP_SIZE_TECHNICAL * 2; + constexpr int32_t MAP_WINDOW_MAP_SIZE = MAXIMUM_MAP_SIZE_TECHNICAL * 2; -static constexpr StringId WINDOW_TITLE = STR_MAP_LABEL; -static constexpr int32_t WH = 259; -static constexpr int32_t WW = 245; + static constexpr StringId WINDOW_TITLE = STR_MAP_LABEL; + static constexpr int32_t WH = 259; + static constexpr int32_t WW = 245; -// Some functions manipulate coordinates on the map. These are the coordinates of the pixels in the -// minimap. In order to distinguish those from actual coordinates, we use a separate name. -using MapCoordsXY = TileCoordsXY; + // Some functions manipulate coordinates on the map. These are the coordinates of the pixels in the + // minimap. In order to distinguish those from actual coordinates, we use a separate name. + using MapCoordsXY = TileCoordsXY; -enum -{ - PAGE_PEEPS, - PAGE_RIDES -}; + enum + { + PAGE_PEEPS, + PAGE_RIDES + }; -enum WindowMapWidgetIdx -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_RESIZE, - WIDX_PEOPLE_TAB, - WIDX_RIDES_TAB, - WIDX_MAP, - WIDX_MAP_SIZE_SPINNER_Y, - WIDX_MAP_SIZE_SPINNER_Y_UP, - WIDX_MAP_SIZE_SPINNER_Y_DOWN, - WIDX_MAP_SIZE_LINK, - WIDX_MAP_SIZE_SPINNER_X, - WIDX_MAP_SIZE_SPINNER_X_UP, - WIDX_MAP_SIZE_SPINNER_X_DOWN, - WIDX_SET_LAND_RIGHTS, - WIDX_BUILD_PARK_ENTRANCE, - WIDX_PEOPLE_STARTING_POSITION, - WIDX_LAND_TOOL, - WIDX_LAND_TOOL_SMALLER, - WIDX_LAND_TOOL_LARGER, - WIDX_LAND_OWNED_CHECKBOX, - WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX, - WIDX_LAND_SALE_CHECKBOX, - WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX, - WIDX_ROTATE_90, - WIDX_MAP_GENERATOR -}; + enum WindowMapWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_RESIZE, + WIDX_PEOPLE_TAB, + WIDX_RIDES_TAB, + WIDX_MAP, + WIDX_MAP_SIZE_SPINNER_Y, + WIDX_MAP_SIZE_SPINNER_Y_UP, + WIDX_MAP_SIZE_SPINNER_Y_DOWN, + WIDX_MAP_SIZE_LINK, + WIDX_MAP_SIZE_SPINNER_X, + WIDX_MAP_SIZE_SPINNER_X_UP, + WIDX_MAP_SIZE_SPINNER_X_DOWN, + WIDX_SET_LAND_RIGHTS, + WIDX_BUILD_PARK_ENTRANCE, + WIDX_PEOPLE_STARTING_POSITION, + WIDX_LAND_TOOL, + WIDX_LAND_TOOL_SMALLER, + WIDX_LAND_TOOL_LARGER, + WIDX_LAND_OWNED_CHECKBOX, + WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX, + WIDX_LAND_SALE_CHECKBOX, + WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX, + WIDX_ROTATE_90, + WIDX_MAP_GENERATOR + }; -validate_global_widx(WC_MAP, WIDX_ROTATE_90); + validate_global_widx(WC_MAP, WIDX_ROTATE_90); -// clang-format off + // clang-format off static Widget window_map_widgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget ({ 0, 43}, {245, 215}, WindowWidgetType::Resize, WindowColour::Secondary ), @@ -136,1387 +136,1392 @@ static constexpr ScreenCoordsXY MiniMapOffsets[] = { { MAXIMUM_MAP_SIZE_TECHNICAL - 8, 2 * MAXIMUM_MAP_SIZE_TECHNICAL }, { 0 - 8, MAXIMUM_MAP_SIZE_TECHNICAL }, }; -// clang-format on + // clang-format on -static constexpr StringId MapLabels[] = { - STR_MAP_RIDE, STR_MAP_FOOD_STALL, STR_MAP_DRINK_STALL, STR_MAP_SOUVENIR_STALL, - STR_MAP_INFO_KIOSK, STR_MAP_FIRST_AID, STR_MAP_CASH_MACHINE, STR_MAP_TOILET, -}; + static constexpr StringId MapLabels[] = { + STR_MAP_RIDE, STR_MAP_FOOD_STALL, STR_MAP_DRINK_STALL, STR_MAP_SOUVENIR_STALL, + STR_MAP_INFO_KIOSK, STR_MAP_FIRST_AID, STR_MAP_CASH_MACHINE, STR_MAP_TOILET, + }; -static constexpr uint16_t RideKeyColours[] = { - MapColour(PALETTE_INDEX_61), // COLOUR_KEY_RIDE - MapColour(PALETTE_INDEX_42), // COLOUR_KEY_FOOD - MapColour(PALETTE_INDEX_20), // COLOUR_KEY_DRINK - MapColour(PALETTE_INDEX_209), // COLOUR_KEY_SOUVENIR - MapColour(PALETTE_INDEX_136), // COLOUR_KEY_KIOSK - MapColour(PALETTE_INDEX_102), // COLOUR_KEY_FIRST_AID - MapColour(PALETTE_INDEX_55), // COLOUR_KEY_CASH_MACHINE - MapColour(PALETTE_INDEX_161), // COLOUR_KEY_TOILETS -}; + static constexpr uint16_t RideKeyColours[] = { + MapColour(PALETTE_INDEX_61), // COLOUR_KEY_RIDE + MapColour(PALETTE_INDEX_42), // COLOUR_KEY_FOOD + MapColour(PALETTE_INDEX_20), // COLOUR_KEY_DRINK + MapColour(PALETTE_INDEX_209), // COLOUR_KEY_SOUVENIR + MapColour(PALETTE_INDEX_136), // COLOUR_KEY_KIOSK + MapColour(PALETTE_INDEX_102), // COLOUR_KEY_FIRST_AID + MapColour(PALETTE_INDEX_55), // COLOUR_KEY_CASH_MACHINE + MapColour(PALETTE_INDEX_161), // COLOUR_KEY_TOILETS + }; -static constexpr uint8_t DefaultPeepMapColour = PALETTE_INDEX_20; -static constexpr uint8_t GuestMapColour = PALETTE_INDEX_172; -static constexpr uint8_t GuestMapColourAlternate = PALETTE_INDEX_21; -static constexpr uint8_t StaffMapColour = PALETTE_INDEX_138; -static constexpr uint8_t StaffMapColourAlternate = PALETTE_INDEX_10; + static constexpr uint8_t DefaultPeepMapColour = PALETTE_INDEX_20; + static constexpr uint8_t GuestMapColour = PALETTE_INDEX_172; + static constexpr uint8_t GuestMapColourAlternate = PALETTE_INDEX_21; + static constexpr uint8_t StaffMapColour = PALETTE_INDEX_138; + static constexpr uint8_t StaffMapColourAlternate = PALETTE_INDEX_10; -static constexpr uint16_t WaterColour = MapColour(PALETTE_INDEX_195); + static constexpr uint16_t WaterColour = MapColour(PALETTE_INDEX_195); -static constexpr uint16_t ElementTypeMaskColour[] = { - 0xFFFF, // TILE_ELEMENT_TYPE_SURFACE - 0x0000, // TILE_ELEMENT_TYPE_PATH - 0x00FF, // TILE_ELEMENT_TYPE_TRACK - 0xFF00, // TILE_ELEMENT_TYPE_SMALL_SCENERY - 0x0000, // TILE_ELEMENT_TYPE_ENTRANCE - 0xFFFF, // TILE_ELEMENT_TYPE_WALL - 0x0000, // TILE_ELEMENT_TYPE_LARGE_SCENERY - 0xFFFF, // TILE_ELEMENT_TYPE_BANNER -}; + static constexpr uint16_t ElementTypeMaskColour[] = { + 0xFFFF, // TILE_ELEMENT_TYPE_SURFACE + 0x0000, // TILE_ELEMENT_TYPE_PATH + 0x00FF, // TILE_ELEMENT_TYPE_TRACK + 0xFF00, // TILE_ELEMENT_TYPE_SMALL_SCENERY + 0x0000, // TILE_ELEMENT_TYPE_ENTRANCE + 0xFFFF, // TILE_ELEMENT_TYPE_WALL + 0x0000, // TILE_ELEMENT_TYPE_LARGE_SCENERY + 0xFFFF, // TILE_ELEMENT_TYPE_BANNER + }; -static constexpr uint16_t ElementTypeAddColour[] = { - MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_SURFACE - MapColour(PALETTE_INDEX_17), // TILE_ELEMENT_TYPE_PATH - MapColour2(PALETTE_INDEX_183, PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_TRACK - MapColour2(PALETTE_INDEX_0, PALETTE_INDEX_99), // TILE_ELEMENT_TYPE_SMALL_SCENERY - MapColour(PALETTE_INDEX_186), // TILE_ELEMENT_TYPE_ENTRANCE - MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_WALL - MapColour(PALETTE_INDEX_99), // TILE_ELEMENT_TYPE_LARGE_SCENERY - MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_BANNER -}; + static constexpr uint16_t ElementTypeAddColour[] = { + MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_SURFACE + MapColour(PALETTE_INDEX_17), // TILE_ELEMENT_TYPE_PATH + MapColour2(PALETTE_INDEX_183, PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_TRACK + MapColour2(PALETTE_INDEX_0, PALETTE_INDEX_99), // TILE_ELEMENT_TYPE_SMALL_SCENERY + MapColour(PALETTE_INDEX_186), // TILE_ELEMENT_TYPE_ENTRANCE + MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_WALL + MapColour(PALETTE_INDEX_99), // TILE_ELEMENT_TYPE_LARGE_SCENERY + MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_BANNER + }; -class MapWindow final : public Window -{ - uint8_t _rotation; - uint8_t _activeTool; - uint32_t _currentLine; - uint16_t _landRightsToolSize; - int32_t _firstColumnWidth; - std::vector _mapImageData; - bool _mapWidthAndHeightLinked{ true }; - bool _recalculateScrollbars = false; - enum class ResizeDirection + class MapWindow final : public Window { - Both, - X, - Y, - } _resizeDirection{ ResizeDirection::Both }; - -public: - MapWindow() - { - _mapImageData.resize(MAP_WINDOW_MAP_SIZE * MAP_WINDOW_MAP_SIZE); - } - - void OnOpen() override - { - widgets = window_map_widgets; - - hold_down_widgets = (1uLL << WIDX_MAP_SIZE_SPINNER_Y_UP) | (1uLL << WIDX_MAP_SIZE_SPINNER_Y_DOWN) - | (1uLL << WIDX_MAP_SIZE_SPINNER_X_UP) | (1uLL << WIDX_MAP_SIZE_SPINNER_X_DOWN) | (1uLL << WIDX_LAND_TOOL_LARGER) - | (1uLL << WIDX_LAND_TOOL_SMALLER); - - flags |= WF_RESIZABLE; - min_width = WW; - max_width = 800; - min_height = WH; - max_height = 560; - - ResizeMap(); - InitScrollWidgets(); - CalculateTextLayout(); - - _rotation = GetCurrentRotation(); - - InitMap(); - gWindowSceneryRotation = 0; - CentreMapOnViewPoint(); - FootpathSelectDefault(); - - auto& gameState = GetGameState(); - _mapWidthAndHeightLinked = gameState.MapSize.x == gameState.MapSize.y; - - // Reset land rights tool size - _landRightsToolSize = 1; - } - - void OnClose() override - { - _mapImageData.clear(); - _mapImageData.shrink_to_fit(); - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == classification - && gCurrentToolWidget.window_number == number) + uint8_t _rotation; + uint8_t _activeTool; + uint32_t _currentLine; + uint16_t _landRightsToolSize; + int32_t _firstColumnWidth; + std::vector _mapImageData; + bool _mapWidthAndHeightLinked{ true }; + bool _recalculateScrollbars = false; + enum class ResizeDirection { - ToolCancel(); + Both, + X, + Y, + } _resizeDirection{ ResizeDirection::Both }; + + public: + MapWindow() + { + _mapImageData.resize(MAP_WINDOW_MAP_SIZE * MAP_WINDOW_MAP_SIZE); } - } - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_SET_LAND_RIGHTS: - Invalidate(); - if (ToolSet(*this, widgetIndex, Tool::UpArrow)) + widgets = window_map_widgets; + + hold_down_widgets = (1uLL << WIDX_MAP_SIZE_SPINNER_Y_UP) | (1uLL << WIDX_MAP_SIZE_SPINNER_Y_DOWN) + | (1uLL << WIDX_MAP_SIZE_SPINNER_X_UP) | (1uLL << WIDX_MAP_SIZE_SPINNER_X_DOWN) + | (1uLL << WIDX_LAND_TOOL_LARGER) | (1uLL << WIDX_LAND_TOOL_SMALLER); + + flags |= WF_RESIZABLE; + min_width = WW; + max_width = 800; + min_height = WH; + max_height = 560; + + ResizeMap(); + InitScrollWidgets(); + CalculateTextLayout(); + + _rotation = GetCurrentRotation(); + + InitMap(); + gWindowSceneryRotation = 0; + CentreMapOnViewPoint(); + FootpathSelectDefault(); + + auto& gameState = GetGameState(); + _mapWidthAndHeightLinked = gameState.MapSize.x == gameState.MapSize.y; + + // Reset land rights tool size + _landRightsToolSize = 1; + } + + void OnClose() override + { + _mapImageData.clear(); + _mapImageData.shrink_to_fit(); + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == classification + && gCurrentToolWidget.window_number == number) + { + ToolCancel(); + } + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); break; - _activeTool = 2; - // Prevent mountain tool size. - _landRightsToolSize = std::max(kLandToolMinimumSize, _landRightsToolSize); - ShowGridlines(); - ShowLandRights(); - ShowConstructionRights(); - break; - case WIDX_LAND_OWNED_CHECKBOX: - _activeTool ^= 2; - - if (_activeTool & 2) - _activeTool &= 0xF2; - - Invalidate(); - break; - case WIDX_LAND_SALE_CHECKBOX: - _activeTool ^= 8; - - if (_activeTool & 8) - _activeTool &= 0xF8; - - Invalidate(); - break; - case WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX: - _activeTool ^= 1; - - if (_activeTool & 1) - _activeTool &= 0xF1; - - Invalidate(); - break; - case WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX: - _activeTool ^= 4; - - if (_activeTool & 4) - _activeTool &= 0xF4; - - Invalidate(); - break; - case WIDX_BUILD_PARK_ENTRANCE: - Invalidate(); - if (ToolSet(*this, widgetIndex, Tool::UpArrow)) + case WIDX_SET_LAND_RIGHTS: + Invalidate(); + if (ToolSet(*this, widgetIndex, Tool::UpArrow)) + break; + _activeTool = 2; + // Prevent mountain tool size. + _landRightsToolSize = std::max(kLandToolMinimumSize, _landRightsToolSize); + ShowGridlines(); + ShowLandRights(); + ShowConstructionRights(); break; + case WIDX_LAND_OWNED_CHECKBOX: + _activeTool ^= 2; - gParkEntranceGhostExists = false; - InputSetFlag(INPUT_FLAG_6, true); + if (_activeTool & 2) + _activeTool &= 0xF2; - ShowGridlines(); - ShowLandRights(); - ShowConstructionRights(); - break; - case WIDX_ROTATE_90: - gWindowSceneryRotation = (gWindowSceneryRotation + 1) & 3; - break; - case WIDX_PEOPLE_STARTING_POSITION: - if (ToolSet(*this, widgetIndex, Tool::UpArrow)) + Invalidate(); break; + case WIDX_LAND_SALE_CHECKBOX: + _activeTool ^= 8; - ShowGridlines(); - ShowLandRights(); - ShowConstructionRights(); - break; - case WIDX_LAND_TOOL: - InputLandSize(); - break; - case WIDX_MAP_SIZE_SPINNER_Y: - case WIDX_MAP_SIZE_SPINNER_X: - InputMapSize(widgetIndex); - break; - case WIDX_MAP_SIZE_LINK: - _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; - break; - case WIDX_MAP_GENERATOR: - ContextOpenWindow(WindowClass::Mapgen); - break; - default: - if (widgetIndex >= WIDX_PEOPLE_TAB && widgetIndex <= WIDX_RIDES_TAB) - { - widgetIndex -= WIDX_PEOPLE_TAB; - if (widgetIndex == selected_tab) + if (_activeTool & 8) + _activeTool &= 0xF8; + + Invalidate(); + break; + case WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX: + _activeTool ^= 1; + + if (_activeTool & 1) + _activeTool &= 0xF1; + + Invalidate(); + break; + case WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX: + _activeTool ^= 4; + + if (_activeTool & 4) + _activeTool &= 0xF4; + + Invalidate(); + break; + case WIDX_BUILD_PARK_ENTRANCE: + Invalidate(); + if (ToolSet(*this, widgetIndex, Tool::UpArrow)) break; - selected_tab = widgetIndex; - list_information_type = 0; - _recalculateScrollbars = true; - } - } - } + gParkEntranceGhostExists = false; + InputSetFlag(INPUT_FLAG_6, true); - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_MAP_SIZE_SPINNER_Y_UP: - _resizeDirection = ResizeDirection::Y; - IncreaseMapSize(); - break; - case WIDX_MAP_SIZE_SPINNER_Y_DOWN: - _resizeDirection = ResizeDirection::Y; - DecreaseMapSize(); - break; - case WIDX_MAP_SIZE_SPINNER_X_UP: - _resizeDirection = ResizeDirection::X; - IncreaseMapSize(); - break; - case WIDX_MAP_SIZE_SPINNER_X_DOWN: - _resizeDirection = ResizeDirection::X; - DecreaseMapSize(); - break; - case WIDX_LAND_TOOL_SMALLER: - // Decrement land rights tool size - _landRightsToolSize = std::max(kLandToolMinimumSize, _landRightsToolSize - 1); + ShowGridlines(); + ShowLandRights(); + ShowConstructionRights(); + break; + case WIDX_ROTATE_90: + gWindowSceneryRotation = (gWindowSceneryRotation + 1) & 3; + break; + case WIDX_PEOPLE_STARTING_POSITION: + if (ToolSet(*this, widgetIndex, Tool::UpArrow)) + break; - Invalidate(); - break; - case WIDX_LAND_TOOL_LARGER: - // Increment land rights tool size - _landRightsToolSize = std::min(kLandToolMaximumSize, _landRightsToolSize + 1); + ShowGridlines(); + ShowLandRights(); + ShowConstructionRights(); + break; + case WIDX_LAND_TOOL: + InputLandSize(); + break; + case WIDX_MAP_SIZE_SPINNER_Y: + case WIDX_MAP_SIZE_SPINNER_X: + InputMapSize(widgetIndex); + break; + case WIDX_MAP_SIZE_LINK: + _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; + break; + case WIDX_MAP_GENERATOR: + ContextOpenWindow(WindowClass::Mapgen); + break; + default: + if (widgetIndex >= WIDX_PEOPLE_TAB && widgetIndex <= WIDX_RIDES_TAB) + { + widgetIndex -= WIDX_PEOPLE_TAB; + if (widgetIndex == selected_tab) + break; - Invalidate(); - break; - } - } - - void OnUpdate() override - { - if (GetCurrentRotation() != _rotation) - { - _rotation = GetCurrentRotation(); - InitMap(); - CentreMapOnViewPoint(); + selected_tab = widgetIndex; + list_information_type = 0; + _recalculateScrollbars = true; + } + } } - for (int32_t i = 0; i < 16; i++) - SetMapPixels(); - - Invalidate(); - - // Update tab animations - list_information_type++; - switch (selected_tab) + void OnMouseDown(WidgetIndex widgetIndex) override { - case PAGE_PEEPS: - if (list_information_type >= 32) - { - list_information_type = 0; - } - break; - case PAGE_RIDES: - if (list_information_type >= 64) - { - list_information_type = 0; - } - break; - } - } - - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_SET_LAND_RIGHTS: - SetLandRightsToolUpdate(screenCoords); - break; - case WIDX_BUILD_PARK_ENTRANCE: - PlaceParkEntranceToolUpdate(screenCoords); - break; - case WIDX_PEOPLE_STARTING_POSITION: - SetPeepSpawnToolUpdate(screenCoords); - break; - } - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_BUILD_PARK_ENTRANCE: - PlaceParkEntranceToolDown(screenCoords); - break; - case WIDX_PEOPLE_STARTING_POSITION: - SetPeepSpawnToolDown(screenCoords); - break; - } - } - - void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_SET_LAND_RIGHTS: - if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) - { - auto landSetRightsAction = LandSetRightsAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4); - GameActions::Execute(&landSetRightsAction); - } - break; - } - } - - void OnToolAbort(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_SET_LAND_RIGHTS: - Invalidate(); - HideGridlines(); - HideLandRights(); - HideConstructionRights(); - break; - case WIDX_BUILD_PARK_ENTRANCE: - ParkEntranceRemoveGhost(); - Invalidate(); - HideGridlines(); - HideLandRights(); - HideConstructionRights(); - break; - case WIDX_PEOPLE_STARTING_POSITION: - Invalidate(); - HideGridlines(); - HideLandRights(); - HideConstructionRights(); - break; - } - } - - void SetLandRightsToolUpdate(const ScreenCoordsXY& screenCoords) - { - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - auto info = GetMapCoordinatesFromPos( - screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water)); - if (info.SpriteType == ViewportInteractionItem::None) - return; - - auto mapCoords = info.Loc; - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectType = MAP_SELECT_TYPE_FULL_LAND_RIGHTS; - - int32_t landRightsToolSize = _landRightsToolSize; - if (landRightsToolSize == 0) - landRightsToolSize = 1; - - int32_t size = (landRightsToolSize * 32) - 32; - int32_t radius = (landRightsToolSize * 16) - 16; - mapCoords.x -= radius; - mapCoords.y -= radius; - mapCoords = mapCoords.ToTileStart(); - gMapSelectPositionA = mapCoords; - gMapSelectPositionB.x = mapCoords.x + size; - gMapSelectPositionB.y = mapCoords.y + size; - MapInvalidateSelectionRect(); - } - - CoordsXYZD PlaceParkEntranceGetMapPosition(const ScreenCoordsXY& screenCoords) - { - CoordsXYZD parkEntranceMapPosition{ 0, 0, 0, INVALID_DIRECTION }; - const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); - parkEntranceMapPosition = { mapCoords.x, mapCoords.y, 0, INVALID_DIRECTION }; - if (parkEntranceMapPosition.IsNull()) - return parkEntranceMapPosition; - - auto surfaceElement = MapGetSurfaceElementAt(mapCoords); - if (surfaceElement == nullptr) - { - parkEntranceMapPosition.SetNull(); - return parkEntranceMapPosition; - } - - parkEntranceMapPosition.z = surfaceElement->GetWaterHeight(); - if (parkEntranceMapPosition.z == 0) - { - parkEntranceMapPosition.z = surfaceElement->GetBaseZ(); - if ((surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0) + switch (widgetIndex) { - parkEntranceMapPosition.z += 16; - if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + case WIDX_MAP_SIZE_SPINNER_Y_UP: + _resizeDirection = ResizeDirection::Y; + IncreaseMapSize(); + break; + case WIDX_MAP_SIZE_SPINNER_Y_DOWN: + _resizeDirection = ResizeDirection::Y; + DecreaseMapSize(); + break; + case WIDX_MAP_SIZE_SPINNER_X_UP: + _resizeDirection = ResizeDirection::X; + IncreaseMapSize(); + break; + case WIDX_MAP_SIZE_SPINNER_X_DOWN: + _resizeDirection = ResizeDirection::X; + DecreaseMapSize(); + break; + case WIDX_LAND_TOOL_SMALLER: + // Decrement land rights tool size + _landRightsToolSize = std::max(kLandToolMinimumSize, _landRightsToolSize - 1); + + Invalidate(); + break; + case WIDX_LAND_TOOL_LARGER: + // Increment land rights tool size + _landRightsToolSize = std::min(kLandToolMaximumSize, _landRightsToolSize + 1); + + Invalidate(); + break; + } + } + + void OnUpdate() override + { + if (GetCurrentRotation() != _rotation) + { + _rotation = GetCurrentRotation(); + InitMap(); + CentreMapOnViewPoint(); + } + + for (int32_t i = 0; i < 16; i++) + SetMapPixels(); + + Invalidate(); + + // Update tab animations + list_information_type++; + switch (selected_tab) + { + case PAGE_PEEPS: + if (list_information_type >= 32) + { + list_information_type = 0; + } + break; + case PAGE_RIDES: + if (list_information_type >= 64) + { + list_information_type = 0; + } + break; + } + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (widgetIndex) + { + case WIDX_SET_LAND_RIGHTS: + SetLandRightsToolUpdate(screenCoords); + break; + case WIDX_BUILD_PARK_ENTRANCE: + PlaceParkEntranceToolUpdate(screenCoords); + break; + case WIDX_PEOPLE_STARTING_POSITION: + SetPeepSpawnToolUpdate(screenCoords); + break; + } + } + + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (widgetIndex) + { + case WIDX_BUILD_PARK_ENTRANCE: + PlaceParkEntranceToolDown(screenCoords); + break; + case WIDX_PEOPLE_STARTING_POSITION: + SetPeepSpawnToolDown(screenCoords); + break; + } + } + + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (widgetIndex) + { + case WIDX_SET_LAND_RIGHTS: + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) + { + auto landSetRightsAction = LandSetRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4); + GameActions::Execute(&landSetRightsAction); + } + break; + } + } + + void OnToolAbort(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_SET_LAND_RIGHTS: + Invalidate(); + HideGridlines(); + HideLandRights(); + HideConstructionRights(); + break; + case WIDX_BUILD_PARK_ENTRANCE: + ParkEntranceRemoveGhost(); + Invalidate(); + HideGridlines(); + HideLandRights(); + HideConstructionRights(); + break; + case WIDX_PEOPLE_STARTING_POSITION: + Invalidate(); + HideGridlines(); + HideLandRights(); + HideConstructionRights(); + break; + } + } + + void SetLandRightsToolUpdate(const ScreenCoordsXY& screenCoords) + { + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + + auto info = GetMapCoordinatesFromPos( + screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water)); + if (info.SpriteType == ViewportInteractionItem::None) + return; + + auto mapCoords = info.Loc; + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectType = MAP_SELECT_TYPE_FULL_LAND_RIGHTS; + + int32_t landRightsToolSize = _landRightsToolSize; + if (landRightsToolSize == 0) + landRightsToolSize = 1; + + int32_t size = (landRightsToolSize * 32) - 32; + int32_t radius = (landRightsToolSize * 16) - 16; + mapCoords.x -= radius; + mapCoords.y -= radius; + mapCoords = mapCoords.ToTileStart(); + gMapSelectPositionA = mapCoords; + gMapSelectPositionB.x = mapCoords.x + size; + gMapSelectPositionB.y = mapCoords.y + size; + MapInvalidateSelectionRect(); + } + + CoordsXYZD PlaceParkEntranceGetMapPosition(const ScreenCoordsXY& screenCoords) + { + CoordsXYZD parkEntranceMapPosition{ 0, 0, 0, INVALID_DIRECTION }; + const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); + parkEntranceMapPosition = { mapCoords.x, mapCoords.y, 0, INVALID_DIRECTION }; + if (parkEntranceMapPosition.IsNull()) + return parkEntranceMapPosition; + + auto surfaceElement = MapGetSurfaceElementAt(mapCoords); + if (surfaceElement == nullptr) + { + parkEntranceMapPosition.SetNull(); + return parkEntranceMapPosition; + } + + parkEntranceMapPosition.z = surfaceElement->GetWaterHeight(); + if (parkEntranceMapPosition.z == 0) + { + parkEntranceMapPosition.z = surfaceElement->GetBaseZ(); + if ((surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0) { parkEntranceMapPosition.z += 16; + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + { + parkEntranceMapPosition.z += 16; + } + } + } + parkEntranceMapPosition.direction = (gWindowSceneryRotation - GetCurrentRotation()) & 3; + return parkEntranceMapPosition; + } + + void PlaceParkEntranceToolUpdate(const ScreenCoordsXY& screenCoords) + { + MapInvalidateSelectionRect(); + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords); + if (parkEntrancePosition.IsNull()) + { + ParkEntranceRemoveGhost(); + return; + } + + int32_t sideDirection = (parkEntrancePosition.direction + 1) & 3; + gMapSelectionTiles.clear(); + gMapSelectionTiles.push_back({ parkEntrancePosition.x, parkEntrancePosition.y }); + gMapSelectionTiles.push_back({ parkEntrancePosition.x + CoordsDirectionDelta[sideDirection].x, + parkEntrancePosition.y + CoordsDirectionDelta[sideDirection].y }); + gMapSelectionTiles.push_back({ parkEntrancePosition.x - CoordsDirectionDelta[sideDirection].x, + parkEntrancePosition.y - CoordsDirectionDelta[sideDirection].y }); + + gMapSelectArrowPosition = parkEntrancePosition; + gMapSelectArrowDirection = parkEntrancePosition.direction; + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT | MAP_SELECT_FLAG_ENABLE_ARROW; + MapInvalidateMapSelectionTiles(); + if (gParkEntranceGhostExists && parkEntrancePosition == gParkEntranceGhostPosition) + { + return; + } + + ParkEntranceRemoveGhost(); + + auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId); + gameAction.SetFlags(GAME_COMMAND_FLAG_GHOST); + + auto result = GameActions::Execute(&gameAction); + if (result.Error == GameActions::Status::Ok) + { + gParkEntranceGhostPosition = parkEntrancePosition; + gParkEntranceGhostExists = true; + } + } + + void PlaceParkEntranceToolDown(const ScreenCoordsXY& screenCoords) + { + ParkEntranceRemoveGhost(); + + CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords); + if (!parkEntrancePosition.IsNull()) + { + auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId); + auto result = GameActions::Execute(&gameAction); + if (result.Error == GameActions::Status::Ok) + { + Audio::Play3D(Audio::SoundId::PlaceItem, result.Position); } } } - parkEntranceMapPosition.direction = (gWindowSceneryRotation - GetCurrentRotation()) & 3; - return parkEntranceMapPosition; - } - void PlaceParkEntranceToolUpdate(const ScreenCoordsXY& screenCoords) - { - MapInvalidateSelectionRect(); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords); - if (parkEntrancePosition.IsNull()) + void SetPeepSpawnToolUpdate(const ScreenCoordsXY& screenCoords) { - ParkEntranceRemoveGhost(); - return; + int32_t direction; + TileElement* tileElement; + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + auto mapCoords = FootpathBridgeGetInfoFromPos(screenCoords, &direction, &tileElement); + if (mapCoords.IsNull()) + return; + + int32_t mapZ = tileElement->GetBaseZ(); + if (tileElement->GetType() == TileElementType::Surface) + { + if ((tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0) + mapZ += 16; + if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + mapZ += 16; + } + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectPositionA = mapCoords; + gMapSelectPositionB = mapCoords; + gMapSelectArrowPosition = CoordsXYZ{ mapCoords, mapZ }; + gMapSelectArrowDirection = DirectionReverse(direction); + MapInvalidateSelectionRect(); } - int32_t sideDirection = (parkEntrancePosition.direction + 1) & 3; - gMapSelectionTiles.clear(); - gMapSelectionTiles.push_back({ parkEntrancePosition.x, parkEntrancePosition.y }); - gMapSelectionTiles.push_back({ parkEntrancePosition.x + CoordsDirectionDelta[sideDirection].x, - parkEntrancePosition.y + CoordsDirectionDelta[sideDirection].y }); - gMapSelectionTiles.push_back({ parkEntrancePosition.x - CoordsDirectionDelta[sideDirection].x, - parkEntrancePosition.y - CoordsDirectionDelta[sideDirection].y }); - - gMapSelectArrowPosition = parkEntrancePosition; - gMapSelectArrowDirection = parkEntrancePosition.direction; - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT | MAP_SELECT_FLAG_ENABLE_ARROW; - MapInvalidateMapSelectionTiles(); - if (gParkEntranceGhostExists && parkEntrancePosition == gParkEntranceGhostPosition) + void SetPeepSpawnToolDown(const ScreenCoordsXY& screenCoords) { - return; - } + // Verify footpath exists at location, and retrieve coordinates + TileElement* tileElement; + int32_t direction; + auto mapCoords = FootpathGetCoordinatesFromPos(screenCoords, &direction, &tileElement); + if (mapCoords.IsNull()) + return; - ParkEntranceRemoveGhost(); + int32_t mapZ = tileElement->GetBaseZ(); - auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId); - gameAction.SetFlags(GAME_COMMAND_FLAG_GHOST); - - auto result = GameActions::Execute(&gameAction); - if (result.Error == GameActions::Status::Ok) - { - gParkEntranceGhostPosition = parkEntrancePosition; - gParkEntranceGhostExists = true; - } - } - - void PlaceParkEntranceToolDown(const ScreenCoordsXY& screenCoords) - { - ParkEntranceRemoveGhost(); - - CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords); - if (!parkEntrancePosition.IsNull()) - { - auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId); + auto gameAction = PeepSpawnPlaceAction({ mapCoords, mapZ, static_cast(direction) }); auto result = GameActions::Execute(&gameAction); if (result.Error == GameActions::Status::Ok) { Audio::Play3D(Audio::SoundId::PlaceItem, result.Position); } } - } - void SetPeepSpawnToolUpdate(const ScreenCoordsXY& screenCoords) - { - int32_t direction; - TileElement* tileElement; - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - auto mapCoords = FootpathBridgeGetInfoFromPos(screenCoords, &direction, &tileElement); - if (mapCoords.IsNull()) - return; - - int32_t mapZ = tileElement->GetBaseZ(); - if (tileElement->GetType() == TileElementType::Surface) + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { - if ((tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0) - mapZ += 16; - if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - mapZ += 16; - } + if (text.empty()) + return; - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = mapCoords; - gMapSelectPositionB = mapCoords; - gMapSelectArrowPosition = CoordsXYZ{ mapCoords, mapZ }; - gMapSelectArrowDirection = DirectionReverse(direction); - MapInvalidateSelectionRect(); - } - - void SetPeepSpawnToolDown(const ScreenCoordsXY& screenCoords) - { - // Verify footpath exists at location, and retrieve coordinates - TileElement* tileElement; - int32_t direction; - auto mapCoords = FootpathGetCoordinatesFromPos(screenCoords, &direction, &tileElement); - if (mapCoords.IsNull()) - return; - - int32_t mapZ = tileElement->GetBaseZ(); - - auto gameAction = PeepSpawnPlaceAction({ mapCoords, mapZ, static_cast(direction) }); - auto result = GameActions::Execute(&gameAction); - if (result.Error == GameActions::Status::Ok) - { - Audio::Play3D(Audio::SoundId::PlaceItem, result.Position); - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (text.empty()) - return; - - switch (widgetIndex) - { - case WIDX_LAND_TOOL: + switch (widgetIndex) { - char* end; - std::string textStr = std::string(text); - int32_t size = strtol(textStr.c_str(), &end, 10); - if (*end == '\0') + case WIDX_LAND_TOOL: { - size = std::clamp(size, kLandToolMinimumSize, kLandToolMaximumSize); - _landRightsToolSize = size; - Invalidate(); + char* end; + std::string textStr = std::string(text); + int32_t size = strtol(textStr.c_str(), &end, 10); + if (*end == '\0') + { + size = std::clamp(size, kLandToolMinimumSize, kLandToolMaximumSize); + _landRightsToolSize = size; + Invalidate(); + } + break; } - break; - } - case WIDX_MAP_SIZE_SPINNER_Y: - case WIDX_MAP_SIZE_SPINNER_X: - { - char* end; - std::string textStr = std::string(text); - int32_t size = strtol(textStr.c_str(), &end, 10); - if (*end == '\0') + case WIDX_MAP_SIZE_SPINNER_Y: + case WIDX_MAP_SIZE_SPINNER_X: { - // The practical size is 2 lower than the technical size - size += 2; - size = std::clamp(size, MINIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL); + char* end; + std::string textStr = std::string(text); + int32_t size = strtol(textStr.c_str(), &end, 10); + if (*end == '\0') + { + // The practical size is 2 lower than the technical size + size += 2; + size = std::clamp(size, MINIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL); - TileCoordsXY newMapSize = GetGameState().MapSize; - if (_resizeDirection != ResizeDirection::X) - newMapSize.y = size; - if (_resizeDirection != ResizeDirection::Y) - newMapSize.x = size; + TileCoordsXY newMapSize = GetGameState().MapSize; + if (_resizeDirection != ResizeDirection::X) + newMapSize.y = size; + if (_resizeDirection != ResizeDirection::Y) + newMapSize.x = size; - auto mapChangeSizeAction = MapChangeSizeAction(newMapSize); - GameActions::Execute(&mapChangeSizeAction); - Invalidate(); + auto mapChangeSizeAction = MapChangeSizeAction(newMapSize); + GameActions::Execute(&mapChangeSizeAction); + Invalidate(); + } + break; } - break; } } - } - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - return ScreenSize(MAP_WINDOW_MAP_SIZE, MAP_WINDOW_MAP_SIZE); - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - CoordsXY c = ScreenToMap(screenCoords); - auto mapCoords = CoordsXY{ std::clamp(c.x, 0, MAXIMUM_MAP_SIZE_BIG - 1), std::clamp(c.y, 0, MAXIMUM_MAP_SIZE_BIG - 1) }; - auto mapZ = TileElementHeight(mapCoords); - - WindowBase* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) + ScreenSize OnScrollGetSize(int32_t scrollIndex) override { - WindowScrollToLocation(*mainWindow, { mapCoords, mapZ }); + return ScreenSize(MAP_WINDOW_MAP_SIZE, MAP_WINDOW_MAP_SIZE); } - if (LandToolIsActive()) + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - // Set land terrain - int32_t landToolSize = std::max(1, gLandToolSize); - int32_t size = (landToolSize * 32) - 32; - int32_t radius = (landToolSize * 16) - 16; + CoordsXY c = ScreenToMap(screenCoords); + auto mapCoords = CoordsXY{ std::clamp(c.x, 0, MAXIMUM_MAP_SIZE_BIG - 1), + std::clamp(c.y, 0, MAXIMUM_MAP_SIZE_BIG - 1) }; + auto mapZ = TileElementHeight(mapCoords); - mapCoords = (mapCoords - CoordsXY{ radius, radius }).ToTileStart(); - MapInvalidateSelectionRect(); - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = mapCoords; - gMapSelectPositionB = mapCoords + CoordsXY{ size, size }; - MapInvalidateSelectionRect(); - - auto surfaceSetStyleAction = SurfaceSetStyleAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - gLandToolTerrainSurface, gLandToolTerrainEdge); - GameActions::Execute(&surfaceSetStyleAction); - } - else if (WidgetIsActiveTool(*this, WIDX_SET_LAND_RIGHTS)) - { - // Set land rights - int32_t landRightsToolSize = std::max(1, _landRightsToolSize); - int32_t size = (landRightsToolSize * 32) - 32; - int32_t radius = (landRightsToolSize * 16) - 16; - mapCoords = (mapCoords - CoordsXY{ radius, radius }).ToTileStart(); - - MapInvalidateSelectionRect(); - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectType = MAP_SELECT_TYPE_FULL_LAND_RIGHTS; - gMapSelectPositionA = mapCoords; - gMapSelectPositionB = mapCoords + CoordsXY{ size, size }; - MapInvalidateSelectionRect(); - - auto landSetRightsAction = LandSetRightsAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4); - GameActions::Execute(&landSetRightsAction); - } - } - - void OnScrollMouseDrag(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - OnScrollMouseDown(scrollIndex, screenCoords); - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - GfxClear(&dpi, PALETTE_INDEX_10); - - G1Element g1temp = {}; - g1temp.offset = _mapImageData.data(); - g1temp.width = MAP_WINDOW_MAP_SIZE; - g1temp.height = MAP_WINDOW_MAP_SIZE; - g1temp.x_offset = -8; - g1temp.y_offset = -8; - GfxSetG1Element(SPR_TEMP, &g1temp); - DrawingEngineInvalidateImage(SPR_TEMP); - GfxDrawSprite(dpi, ImageId(SPR_TEMP), { 0, 0 }); - - if (selected_tab == PAGE_PEEPS) - { - PaintPeepOverlay(dpi); - } - else - { - PaintTrainOverlay(dpi); - } - PaintHudRectangle(dpi); - } - - void OnPrepareDraw() override - { - // Set the pressed widgets - pressed_widgets = 0; - SetWidgetPressed(WIDX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); - pressed_widgets |= (1uLL << (WIDX_PEOPLE_TAB + selected_tab)); - pressed_widgets |= (1uLL << WIDX_LAND_TOOL); - - if (_activeTool & (1 << 3)) - pressed_widgets |= (1uLL << WIDX_LAND_SALE_CHECKBOX); - - if (_activeTool & (1 << 2)) - pressed_widgets |= (1uLL << WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX); - - if (_activeTool & (1 << 1)) - pressed_widgets |= (1uLL << WIDX_LAND_OWNED_CHECKBOX); - - if (_activeTool & (1 << 0)) - pressed_widgets |= (1uLL << WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX); - - // Set disabled widgets - auto& gameState = GetGameState(); - SetWidgetDisabled(WIDX_MAP_SIZE_LINK, gameState.MapSize.x != gameState.MapSize.y); - - // Resize widgets to window size - ResizeFrameWithPage(); - ResizeMap(); - - widgets[WIDX_MAP_SIZE_SPINNER_Y].top = height - 15; - widgets[WIDX_MAP_SIZE_SPINNER_Y].bottom = height - 4; - widgets[WIDX_MAP_SIZE_SPINNER_Y_UP].top = height - 14; - widgets[WIDX_MAP_SIZE_SPINNER_Y_UP].bottom = height - 5; - widgets[WIDX_MAP_SIZE_SPINNER_Y_DOWN].top = height - 14; - widgets[WIDX_MAP_SIZE_SPINNER_Y_DOWN].bottom = height - 5; - widgets[WIDX_MAP_SIZE_LINK].top = height - 15; - widgets[WIDX_MAP_SIZE_LINK].bottom = height - 4; - widgets[WIDX_MAP_SIZE_SPINNER_X].top = height - 15; - widgets[WIDX_MAP_SIZE_SPINNER_X].bottom = height - 4; - widgets[WIDX_MAP_SIZE_SPINNER_X_UP].top = height - 14; - widgets[WIDX_MAP_SIZE_SPINNER_X_UP].bottom = height - 5; - widgets[WIDX_MAP_SIZE_SPINNER_X_DOWN].top = height - 14; - widgets[WIDX_MAP_SIZE_SPINNER_X_DOWN].bottom = height - 5; - - widgets[WIDX_SET_LAND_RIGHTS].top = height - 70; - widgets[WIDX_SET_LAND_RIGHTS].bottom = height - 70 + 23; - widgets[WIDX_BUILD_PARK_ENTRANCE].top = height - 46; - widgets[WIDX_BUILD_PARK_ENTRANCE].bottom = height - 46 + 23; - widgets[WIDX_ROTATE_90].top = height - 46; - widgets[WIDX_ROTATE_90].bottom = height - 46 + 23; - widgets[WIDX_PEOPLE_STARTING_POSITION].top = height - 46; - widgets[WIDX_PEOPLE_STARTING_POSITION].bottom = height - 46 + 23; - - widgets[WIDX_LAND_TOOL].top = height - 42; - widgets[WIDX_LAND_TOOL].bottom = height - 42 + 30; - widgets[WIDX_LAND_TOOL_SMALLER].top = height - 41; - widgets[WIDX_LAND_TOOL_SMALLER].bottom = height - 41 + 15; - widgets[WIDX_LAND_TOOL_LARGER].top = height - 27; - widgets[WIDX_LAND_TOOL_LARGER].bottom = height - 27 + 15; - - widgets[WIDX_MAP_GENERATOR].top = height - 69; - widgets[WIDX_MAP_GENERATOR].bottom = height - 69 + 13; - - // Land tool mode (4 checkboxes) - int checkboxY = height - 55; - for (int32_t i = WIDX_LAND_OWNED_CHECKBOX; i <= WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX; i++) - { - widgets[i].top = checkboxY; - checkboxY += 11; - widgets[i].bottom = checkboxY; - checkboxY += 2; - } - - // Disable all scenario editor related widgets - for (int32_t i = WIDX_MAP_SIZE_SPINNER_Y; i <= WIDX_MAP_GENERATOR; i++) - { - widgets[i].type = WindowWidgetType::Empty; - } - - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode) - { - // scenario editor: build park entrance selected, show rotate button - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::Map - && gCurrentToolWidget.widget_index == WIDX_BUILD_PARK_ENTRANCE) + WindowBase* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) { - widgets[WIDX_ROTATE_90].type = WindowWidgetType::FlatBtn; + WindowScrollToLocation(*mainWindow, { mapCoords, mapZ }); } - // Always show set land rights button - widgets[WIDX_SET_LAND_RIGHTS].type = WindowWidgetType::FlatBtn; - - // If any tool is active - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::Map) + if (LandToolIsActive()) { - // if not in set land rights mode: show the default scenario editor buttons - if (gCurrentToolWidget.widget_index != WIDX_SET_LAND_RIGHTS) - { - ShowDefaultScenarioEditorButtons(); - } - else - { // if in set land rights mode: show land tool buttons + modes - widgets[WIDX_LAND_TOOL].type = WindowWidgetType::ImgBtn; - widgets[WIDX_LAND_TOOL_SMALLER].type = WindowWidgetType::TrnBtn; - widgets[WIDX_LAND_TOOL_LARGER].type = WindowWidgetType::TrnBtn; + // Set land terrain + int32_t landToolSize = std::max(1, gLandToolSize); + int32_t size = (landToolSize * 32) - 32; + int32_t radius = (landToolSize * 16) - 16; - for (int32_t i = WIDX_LAND_OWNED_CHECKBOX; i <= WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX; i++) - widgets[i].type = WindowWidgetType::Checkbox; + mapCoords = (mapCoords - CoordsXY{ radius, radius }).ToTileStart(); + MapInvalidateSelectionRect(); + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectPositionA = mapCoords; + gMapSelectPositionB = mapCoords + CoordsXY{ size, size }; + MapInvalidateSelectionRect(); - widgets[WIDX_LAND_TOOL].image = ImageId(LandTool::SizeToSpriteIndex(_landRightsToolSize)); - } + auto surfaceSetStyleAction = SurfaceSetStyleAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + gLandToolTerrainSurface, gLandToolTerrainEdge); + GameActions::Execute(&surfaceSetStyleAction); + } + else if (WidgetIsActiveTool(*this, WIDX_SET_LAND_RIGHTS)) + { + // Set land rights + int32_t landRightsToolSize = std::max(1, _landRightsToolSize); + int32_t size = (landRightsToolSize * 32) - 32; + int32_t radius = (landRightsToolSize * 16) - 16; + mapCoords = (mapCoords - CoordsXY{ radius, radius }).ToTileStart(); + + MapInvalidateSelectionRect(); + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectType = MAP_SELECT_TYPE_FULL_LAND_RIGHTS; + gMapSelectPositionA = mapCoords; + gMapSelectPositionB = mapCoords + CoordsXY{ size, size }; + MapInvalidateSelectionRect(); + + auto landSetRightsAction = LandSetRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4); + GameActions::Execute(&landSetRightsAction); + } + } + + void OnScrollMouseDrag(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + OnScrollMouseDown(scrollIndex, screenCoords); + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + GfxClear(&dpi, PALETTE_INDEX_10); + + G1Element g1temp = {}; + g1temp.offset = _mapImageData.data(); + g1temp.width = MAP_WINDOW_MAP_SIZE; + g1temp.height = MAP_WINDOW_MAP_SIZE; + g1temp.x_offset = -8; + g1temp.y_offset = -8; + GfxSetG1Element(SPR_TEMP, &g1temp); + DrawingEngineInvalidateImage(SPR_TEMP); + GfxDrawSprite(dpi, ImageId(SPR_TEMP), { 0, 0 }); + + if (selected_tab == PAGE_PEEPS) + { + PaintPeepOverlay(dpi); } else { - // if no tool is active: show the default scenario editor buttons - ShowDefaultScenarioEditorButtons(); + PaintTrainOverlay(dpi); + } + PaintHudRectangle(dpi); + } + + void OnPrepareDraw() override + { + // Set the pressed widgets + pressed_widgets = 0; + SetWidgetPressed(WIDX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); + pressed_widgets |= (1uLL << (WIDX_PEOPLE_TAB + selected_tab)); + pressed_widgets |= (1uLL << WIDX_LAND_TOOL); + + if (_activeTool & (1 << 3)) + pressed_widgets |= (1uLL << WIDX_LAND_SALE_CHECKBOX); + + if (_activeTool & (1 << 2)) + pressed_widgets |= (1uLL << WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX); + + if (_activeTool & (1 << 1)) + pressed_widgets |= (1uLL << WIDX_LAND_OWNED_CHECKBOX); + + if (_activeTool & (1 << 0)) + pressed_widgets |= (1uLL << WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX); + + // Set disabled widgets + auto& gameState = GetGameState(); + SetWidgetDisabled(WIDX_MAP_SIZE_LINK, gameState.MapSize.x != gameState.MapSize.y); + + // Resize widgets to window size + ResizeFrameWithPage(); + ResizeMap(); + + widgets[WIDX_MAP_SIZE_SPINNER_Y].top = height - 15; + widgets[WIDX_MAP_SIZE_SPINNER_Y].bottom = height - 4; + widgets[WIDX_MAP_SIZE_SPINNER_Y_UP].top = height - 14; + widgets[WIDX_MAP_SIZE_SPINNER_Y_UP].bottom = height - 5; + widgets[WIDX_MAP_SIZE_SPINNER_Y_DOWN].top = height - 14; + widgets[WIDX_MAP_SIZE_SPINNER_Y_DOWN].bottom = height - 5; + widgets[WIDX_MAP_SIZE_LINK].top = height - 15; + widgets[WIDX_MAP_SIZE_LINK].bottom = height - 4; + widgets[WIDX_MAP_SIZE_SPINNER_X].top = height - 15; + widgets[WIDX_MAP_SIZE_SPINNER_X].bottom = height - 4; + widgets[WIDX_MAP_SIZE_SPINNER_X_UP].top = height - 14; + widgets[WIDX_MAP_SIZE_SPINNER_X_UP].bottom = height - 5; + widgets[WIDX_MAP_SIZE_SPINNER_X_DOWN].top = height - 14; + widgets[WIDX_MAP_SIZE_SPINNER_X_DOWN].bottom = height - 5; + + widgets[WIDX_SET_LAND_RIGHTS].top = height - 70; + widgets[WIDX_SET_LAND_RIGHTS].bottom = height - 70 + 23; + widgets[WIDX_BUILD_PARK_ENTRANCE].top = height - 46; + widgets[WIDX_BUILD_PARK_ENTRANCE].bottom = height - 46 + 23; + widgets[WIDX_ROTATE_90].top = height - 46; + widgets[WIDX_ROTATE_90].bottom = height - 46 + 23; + widgets[WIDX_PEOPLE_STARTING_POSITION].top = height - 46; + widgets[WIDX_PEOPLE_STARTING_POSITION].bottom = height - 46 + 23; + + widgets[WIDX_LAND_TOOL].top = height - 42; + widgets[WIDX_LAND_TOOL].bottom = height - 42 + 30; + widgets[WIDX_LAND_TOOL_SMALLER].top = height - 41; + widgets[WIDX_LAND_TOOL_SMALLER].bottom = height - 41 + 15; + widgets[WIDX_LAND_TOOL_LARGER].top = height - 27; + widgets[WIDX_LAND_TOOL_LARGER].bottom = height - 27 + 15; + + widgets[WIDX_MAP_GENERATOR].top = height - 69; + widgets[WIDX_MAP_GENERATOR].bottom = height - 69 + 13; + + // Land tool mode (4 checkboxes) + int checkboxY = height - 55; + for (int32_t i = WIDX_LAND_OWNED_CHECKBOX; i <= WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX; i++) + { + widgets[i].top = checkboxY; + checkboxY += 11; + widgets[i].bottom = checkboxY; + checkboxY += 2; + } + + // Disable all scenario editor related widgets + for (int32_t i = WIDX_MAP_SIZE_SPINNER_Y; i <= WIDX_MAP_GENERATOR; i++) + { + widgets[i].type = WindowWidgetType::Empty; + } + + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode) + { + // scenario editor: build park entrance selected, show rotate button + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::Map + && gCurrentToolWidget.widget_index == WIDX_BUILD_PARK_ENTRANCE) + { + widgets[WIDX_ROTATE_90].type = WindowWidgetType::FlatBtn; + } + + // Always show set land rights button + widgets[WIDX_SET_LAND_RIGHTS].type = WindowWidgetType::FlatBtn; + + // If any tool is active + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::Map) + { + // if not in set land rights mode: show the default scenario editor buttons + if (gCurrentToolWidget.widget_index != WIDX_SET_LAND_RIGHTS) + { + ShowDefaultScenarioEditorButtons(); + } + else + { // if in set land rights mode: show land tool buttons + modes + widgets[WIDX_LAND_TOOL].type = WindowWidgetType::ImgBtn; + widgets[WIDX_LAND_TOOL_SMALLER].type = WindowWidgetType::TrnBtn; + widgets[WIDX_LAND_TOOL_LARGER].type = WindowWidgetType::TrnBtn; + + for (int32_t i = WIDX_LAND_OWNED_CHECKBOX; i <= WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX; i++) + widgets[i].type = WindowWidgetType::Checkbox; + + widgets[WIDX_LAND_TOOL].image = ImageId(LandTool::SizeToSpriteIndex(_landRightsToolSize)); + } + } + else + { + // if no tool is active: show the default scenario editor buttons + ShowDefaultScenarioEditorButtons(); + } + } + if (_recalculateScrollbars) + { + WidgetScrollUpdateThumbs(*this, WIDX_MAP); + _recalculateScrollbars = false; } } - if (_recalculateScrollbars) + + void OnDraw(DrawPixelInfo& dpi) override { - WidgetScrollUpdateThumbs(*this, WIDX_MAP); - _recalculateScrollbars = false; - } - } + DrawWidgets(dpi); + DrawTabImages(dpi); - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); + auto screenCoords = windowPos + + ScreenCoordsXY{ window_map_widgets[WIDX_LAND_TOOL].midX(), window_map_widgets[WIDX_LAND_TOOL].midY() }; - auto screenCoords = windowPos - + ScreenCoordsXY{ window_map_widgets[WIDX_LAND_TOOL].midX(), window_map_widgets[WIDX_LAND_TOOL].midY() }; - - // Draw land tool size - if (WidgetIsActiveTool(*this, WIDX_SET_LAND_RIGHTS) && _landRightsToolSize > kLandToolMaximumSizeWithSprite) - { - auto ft = Formatter(); - ft.Add(_landRightsToolSize); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); - } - screenCoords.y = windowPos.y + window_map_widgets[WIDX_LAND_TOOL].bottom + 5; - - // People starting position (scenario editor only) - if (widgets[WIDX_PEOPLE_STARTING_POSITION].type != WindowWidgetType::Empty) - { - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PEOPLE_STARTING_POSITION].left + 12, - widgets[WIDX_PEOPLE_STARTING_POSITION].top + 18 }; - GfxDrawSprite(dpi, ImageId(SPR_6410, COLOUR_BRIGHT_RED, COLOUR_LIGHT_BROWN), screenCoords); - } - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !GetGameState().Cheats.SandboxMode) - { - // Render the map legend - if (selected_tab == PAGE_RIDES) + // Draw land tool size + if (WidgetIsActiveTool(*this, WIDX_SET_LAND_RIGHTS) && _landRightsToolSize > kLandToolMaximumSizeWithSprite) { - screenCoords = windowPos + ScreenCoordsXY{ 4, widgets[WIDX_MAP].bottom + 2 }; + auto ft = Formatter(); + ft.Add(_landRightsToolSize); + DrawTextBasic( + dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + } + screenCoords.y = windowPos.y + window_map_widgets[WIDX_LAND_TOOL].bottom + 5; - static_assert(std::size(RideKeyColours) == std::size(MapLabels)); + // People starting position (scenario editor only) + if (widgets[WIDX_PEOPLE_STARTING_POSITION].type != WindowWidgetType::Empty) + { + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PEOPLE_STARTING_POSITION].left + 12, + widgets[WIDX_PEOPLE_STARTING_POSITION].top + 18 }; + GfxDrawSprite(dpi, ImageId(SPR_6410, COLOUR_BRIGHT_RED, COLOUR_LIGHT_BROWN), screenCoords); + } - for (uint32_t i = 0; i < std::size(RideKeyColours); i++) + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !GetGameState().Cheats.SandboxMode) + { + // Render the map legend + if (selected_tab == PAGE_RIDES) { - GfxFillRect( - dpi, { screenCoords + ScreenCoordsXY{ 0, 2 }, screenCoords + ScreenCoordsXY{ 6, 8 } }, - RideKeyColours[i]); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ LIST_ROW_HEIGHT, 0 }, MapLabels[i], {}); - screenCoords.y += LIST_ROW_HEIGHT; - if (i == 3) + screenCoords = windowPos + ScreenCoordsXY{ 4, widgets[WIDX_MAP].bottom + 2 }; + + static_assert(std::size(RideKeyColours) == std::size(MapLabels)); + + for (uint32_t i = 0; i < std::size(RideKeyColours); i++) { - screenCoords += { _firstColumnWidth, -(LIST_ROW_HEIGHT * 4) }; + GfxFillRect( + dpi, { screenCoords + ScreenCoordsXY{ 0, 2 }, screenCoords + ScreenCoordsXY{ 6, 8 } }, + RideKeyColours[i]); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ LIST_ROW_HEIGHT, 0 }, MapLabels[i], {}); + screenCoords.y += LIST_ROW_HEIGHT; + if (i == 3) + { + screenCoords += { _firstColumnWidth, -(LIST_ROW_HEIGHT * 4) }; + } } } } - } - else if (!WidgetIsActiveTool(*this, WIDX_SET_LAND_RIGHTS)) - { - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_MAP_SIZE_SPINNER_Y].top + 1 }, STR_MAP_SIZE, {}, - { colours[1] }); - } - } - - void OnLanguageChange() override - { - CalculateTextLayout(); - } - - void ResetMap() - { - InitMap(); - CentreMapOnViewPoint(); - } - -private: - void InitMap() - { - std::fill(_mapImageData.begin(), _mapImageData.end(), PALETTE_INDEX_10); - _currentLine = 0; - } - - void CentreMapOnViewPoint() - { - WindowBase* mainWindow = WindowGetMain(); - int16_t ax, bx, cx, dx; - int16_t bp, di; - - if (mainWindow == nullptr || mainWindow->viewport == nullptr) - return; - - auto offset = MiniMapOffsets[GetCurrentRotation()]; - - // calculate centre view point of viewport and transform it to minimap coordinates - - cx = ((mainWindow->viewport->view_width >> 1) + mainWindow->viewport->viewPos.x) >> 5; - dx = ((mainWindow->viewport->view_height >> 1) + mainWindow->viewport->viewPos.y) >> 4; - cx += offset.x; - dx += offset.y; - - // calculate width and height of minimap - - ax = widgets[WIDX_MAP].width() - 11; - bx = widgets[WIDX_MAP].height() - 11; - bp = ax; - di = bx; - - ax >>= 1; - bx >>= 1; - cx = std::max(cx - ax, 0); - dx = std::max(dx - bx, 0); - - bp = scrolls[0].h_right - bp; - di = scrolls[0].v_bottom - di; - - if (bp < 0 && (bp - cx) < 0) - cx = 0; - - if (di < 0 && (di - dx) < 0) - dx = 0; - - scrolls[0].h_left = cx; - scrolls[0].v_top = dx; - WidgetScrollUpdateThumbs(*this, WIDX_MAP); - } - - void IncreaseMapSize() - { - auto newMapSize = GetGameState().MapSize; - if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::Y) - newMapSize.y++; - if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::X) - newMapSize.x++; - - auto increaseMapSizeAction = MapChangeSizeAction(newMapSize); - GameActions::Execute(&increaseMapSizeAction); - } - - void DecreaseMapSize() - { - auto newMapSize = GetGameState().MapSize; - if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::Y) - newMapSize.y--; - if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::X) - newMapSize.x--; - - auto decreaseMapSizeAction = MapChangeSizeAction(newMapSize); - GameActions::Execute(&decreaseMapSizeAction); - } - - void SetMapPixels() - { - int32_t x = 0, y = 0, dx = 0, dy = 0; - - int32_t pos = (_currentLine * (MAP_WINDOW_MAP_SIZE - 1)) + MAXIMUM_MAP_SIZE_TECHNICAL - 1; - auto destinationPosition = ScreenCoordsXY{ pos % MAP_WINDOW_MAP_SIZE, pos / MAP_WINDOW_MAP_SIZE }; - auto destination = _mapImageData.data() + (destinationPosition.y * MAP_WINDOW_MAP_SIZE) + destinationPosition.x; - switch (GetCurrentRotation()) - { - case 0: - x = _currentLine * COORDS_XY_STEP; - y = 0; - dx = 0; - dy = COORDS_XY_STEP; - break; - case 1: - x = MAXIMUM_TILE_START_XY; - y = _currentLine * COORDS_XY_STEP; - dx = -COORDS_XY_STEP; - dy = 0; - break; - case 2: - x = MAXIMUM_MAP_SIZE_BIG - ((_currentLine + 1) * COORDS_XY_STEP); - y = MAXIMUM_TILE_START_XY; - dx = 0; - dy = -COORDS_XY_STEP; - break; - case 3: - x = 0; - y = MAXIMUM_MAP_SIZE_BIG - ((_currentLine + 1) * COORDS_XY_STEP); - dx = COORDS_XY_STEP; - dy = 0; - break; - } - - for (int32_t i = 0; i < MAXIMUM_MAP_SIZE_TECHNICAL; i++) - { - if (!MapIsEdge({ x, y })) + else if (!WidgetIsActiveTool(*this, WIDX_SET_LAND_RIGHTS)) { - uint16_t colour = 0; - switch (selected_tab) - { - case PAGE_PEEPS: - colour = GetPixelColourPeep({ x, y }); - break; - case PAGE_RIDES: - colour = GetPixelColourRide({ x, y }); - break; - } - destination[0] = (colour >> 8) & 0xFF; - destination[1] = colour; + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_MAP_SIZE_SPINNER_Y].top + 1 }, STR_MAP_SIZE, {}, + { colours[1] }); } - x += dx; - y += dy; - - destinationPosition.x++; - destinationPosition.y++; - destination = _mapImageData.data() + (destinationPosition.y * MAP_WINDOW_MAP_SIZE) + destinationPosition.x; } - _currentLine++; - if (_currentLine >= MAXIMUM_MAP_SIZE_TECHNICAL) + + void OnLanguageChange() override + { + CalculateTextLayout(); + } + + void ResetMap() + { + InitMap(); + CentreMapOnViewPoint(); + } + + private: + void InitMap() + { + std::fill(_mapImageData.begin(), _mapImageData.end(), PALETTE_INDEX_10); _currentLine = 0; - } - - uint16_t GetPixelColourPeep(const CoordsXY& c) - { - auto* surfaceElement = MapGetSurfaceElementAt(c); - if (surfaceElement == nullptr) - return 0; - - uint16_t colour = MapColour(PALETTE_INDEX_0); - const auto* surfaceObject = surfaceElement->GetSurfaceObject(); - if (surfaceObject != nullptr) - colour = MapColour2(surfaceObject->MapColours[0], surfaceObject->MapColours[1]); - - if (surfaceElement->GetWaterHeight() > 0) - colour = WaterColour; - - if (!(surfaceElement->GetOwnership() & OWNERSHIP_OWNED)) - colour = MapColourUnowned(colour); - - const int32_t maxSupportedTileElementType = static_cast(std::size(ElementTypeAddColour)); - auto tileElement = reinterpret_cast(surfaceElement); - while (!(tileElement++)->IsLastForTile()) - { - if (tileElement->IsGhost()) - { - colour = MapColour(PALETTE_INDEX_21); - break; - } - - auto tileElementType = tileElement->GetType(); - if (EnumValue(tileElementType) >= maxSupportedTileElementType) - { - tileElementType = TileElementType::Surface; - } - colour &= ElementTypeMaskColour[EnumValue(tileElementType)]; - colour |= ElementTypeAddColour[EnumValue(tileElementType)]; } - return colour; - } - - uint16_t GetPixelColourRide(const CoordsXY& c) - { - uint16_t colourA = 0; // highlight colour - uint16_t colourB = MapColour(PALETTE_INDEX_13); // surface colour (dark grey) - - // as an improvement we could use first_element to show underground stuff? - TileElement* tileElement = reinterpret_cast(MapGetSurfaceElementAt(c)); - do + void CentreMapOnViewPoint() { - if (tileElement == nullptr) - break; + WindowBase* mainWindow = WindowGetMain(); + int16_t ax, bx, cx, dx; + int16_t bp, di; - if (tileElement->IsGhost()) + if (mainWindow == nullptr || mainWindow->viewport == nullptr) + return; + + auto offset = MiniMapOffsets[GetCurrentRotation()]; + + // calculate centre view point of viewport and transform it to minimap coordinates + + cx = ((mainWindow->viewport->view_width >> 1) + mainWindow->viewport->viewPos.x) >> 5; + dx = ((mainWindow->viewport->view_height >> 1) + mainWindow->viewport->viewPos.y) >> 4; + cx += offset.x; + dx += offset.y; + + // calculate width and height of minimap + + ax = widgets[WIDX_MAP].width() - 11; + bx = widgets[WIDX_MAP].height() - 11; + bp = ax; + di = bx; + + ax >>= 1; + bx >>= 1; + cx = std::max(cx - ax, 0); + dx = std::max(dx - bx, 0); + + bp = scrolls[0].h_right - bp; + di = scrolls[0].v_bottom - di; + + if (bp < 0 && (bp - cx) < 0) + cx = 0; + + if (di < 0 && (di - dx) < 0) + dx = 0; + + scrolls[0].h_left = cx; + scrolls[0].v_top = dx; + WidgetScrollUpdateThumbs(*this, WIDX_MAP); + } + + void IncreaseMapSize() + { + auto newMapSize = GetGameState().MapSize; + if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::Y) + newMapSize.y++; + if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::X) + newMapSize.x++; + + auto increaseMapSizeAction = MapChangeSizeAction(newMapSize); + GameActions::Execute(&increaseMapSizeAction); + } + + void DecreaseMapSize() + { + auto newMapSize = GetGameState().MapSize; + if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::Y) + newMapSize.y--; + if (IsWidgetPressed(WIDX_MAP_SIZE_LINK) || _resizeDirection == ResizeDirection::X) + newMapSize.x--; + + auto decreaseMapSizeAction = MapChangeSizeAction(newMapSize); + GameActions::Execute(&decreaseMapSizeAction); + } + + void SetMapPixels() + { + int32_t x = 0, y = 0, dx = 0, dy = 0; + + int32_t pos = (_currentLine * (MAP_WINDOW_MAP_SIZE - 1)) + MAXIMUM_MAP_SIZE_TECHNICAL - 1; + auto destinationPosition = ScreenCoordsXY{ pos % MAP_WINDOW_MAP_SIZE, pos / MAP_WINDOW_MAP_SIZE }; + auto destination = _mapImageData.data() + (destinationPosition.y * MAP_WINDOW_MAP_SIZE) + destinationPosition.x; + switch (GetCurrentRotation()) { - colourA = MapColour(PALETTE_INDEX_21); - break; + case 0: + x = _currentLine * COORDS_XY_STEP; + y = 0; + dx = 0; + dy = COORDS_XY_STEP; + break; + case 1: + x = MAXIMUM_TILE_START_XY; + y = _currentLine * COORDS_XY_STEP; + dx = -COORDS_XY_STEP; + dy = 0; + break; + case 2: + x = MAXIMUM_MAP_SIZE_BIG - ((_currentLine + 1) * COORDS_XY_STEP); + y = MAXIMUM_TILE_START_XY; + dx = 0; + dy = -COORDS_XY_STEP; + break; + case 3: + x = 0; + y = MAXIMUM_MAP_SIZE_BIG - ((_currentLine + 1) * COORDS_XY_STEP); + dx = COORDS_XY_STEP; + dy = 0; + break; } - switch (tileElement->GetType()) + for (int32_t i = 0; i < MAXIMUM_MAP_SIZE_TECHNICAL; i++) { - case TileElementType::Surface: - if (tileElement->AsSurface()->GetWaterHeight() > 0) - // Why is this a different water colour as above (195)? - colourB = MapColour(PALETTE_INDEX_194); - if (!(tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED)) - colourB = MapColourUnowned(colourB); - break; - case TileElementType::Path: - colourA = MapColour(PALETTE_INDEX_14); // lighter grey - break; - case TileElementType::Entrance: + if (!MapIsEdge({ x, y })) { - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + uint16_t colour = 0; + switch (selected_tab) + { + case PAGE_PEEPS: + colour = GetPixelColourPeep({ x, y }); + break; + case PAGE_RIDES: + colour = GetPixelColourRide({ x, y }); + break; + } + destination[0] = (colour >> 8) & 0xFF; + destination[1] = colour; + } + x += dx; + y += dy; + + destinationPosition.x++; + destinationPosition.y++; + destination = _mapImageData.data() + (destinationPosition.y * MAP_WINDOW_MAP_SIZE) + destinationPosition.x; + } + _currentLine++; + if (_currentLine >= MAXIMUM_MAP_SIZE_TECHNICAL) + _currentLine = 0; + } + + uint16_t GetPixelColourPeep(const CoordsXY& c) + { + auto* surfaceElement = MapGetSurfaceElementAt(c); + if (surfaceElement == nullptr) + return 0; + + uint16_t colour = MapColour(PALETTE_INDEX_0); + const auto* surfaceObject = surfaceElement->GetSurfaceObject(); + if (surfaceObject != nullptr) + colour = MapColour2(surfaceObject->MapColours[0], surfaceObject->MapColours[1]); + + if (surfaceElement->GetWaterHeight() > 0) + colour = WaterColour; + + if (!(surfaceElement->GetOwnership() & OWNERSHIP_OWNED)) + colour = MapColourUnowned(colour); + + const int32_t maxSupportedTileElementType = static_cast(std::size(ElementTypeAddColour)); + auto tileElement = reinterpret_cast(surfaceElement); + while (!(tileElement++)->IsLastForTile()) + { + if (tileElement->IsGhost()) + { + colour = MapColour(PALETTE_INDEX_21); + break; + } + + auto tileElementType = tileElement->GetType(); + if (EnumValue(tileElementType) >= maxSupportedTileElementType) + { + tileElementType = TileElementType::Surface; + } + colour &= ElementTypeMaskColour[EnumValue(tileElementType)]; + colour |= ElementTypeAddColour[EnumValue(tileElementType)]; + } + + return colour; + } + + uint16_t GetPixelColourRide(const CoordsXY& c) + { + uint16_t colourA = 0; // highlight colour + uint16_t colourB = MapColour(PALETTE_INDEX_13); // surface colour (dark grey) + + // as an improvement we could use first_element to show underground stuff? + TileElement* tileElement = reinterpret_cast(MapGetSurfaceElementAt(c)); + do + { + if (tileElement == nullptr) + break; + + if (tileElement->IsGhost()) + { + colourA = MapColour(PALETTE_INDEX_21); + break; + } + + switch (tileElement->GetType()) + { + case TileElementType::Surface: + if (tileElement->AsSurface()->GetWaterHeight() > 0) + // Why is this a different water colour as above (195)? + colourB = MapColour(PALETTE_INDEX_194); + if (!(tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED)) + colourB = MapColourUnowned(colourB); break; - Ride* targetRide = GetRide(tileElement->AsEntrance()->GetRideIndex()); - if (targetRide != nullptr) + case TileElementType::Path: + colourA = MapColour(PALETTE_INDEX_14); // lighter grey + break; + case TileElementType::Entrance: { - const auto& colourKey = targetRide->GetRideTypeDescriptor().ColourKey; - colourA = RideKeyColours[EnumValue(colourKey)]; + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + break; + Ride* targetRide = GetRide(tileElement->AsEntrance()->GetRideIndex()); + if (targetRide != nullptr) + { + const auto& colourKey = targetRide->GetRideTypeDescriptor().ColourKey; + colourA = RideKeyColours[EnumValue(colourKey)]; + } + break; } - break; + case TileElementType::Track: + { + Ride* targetRide = GetRide(tileElement->AsTrack()->GetRideIndex()); + if (targetRide != nullptr) + { + const auto& colourKey = targetRide->GetRideTypeDescriptor().ColourKey; + colourA = RideKeyColours[EnumValue(colourKey)]; + } + + break; + } + default: + break; } - case TileElementType::Track: + } while (!(tileElement++)->IsLastForTile()); + + if (colourA != 0) + return colourA; + + return colourB; + } + + void PaintPeepOverlay(DrawPixelInfo& dpi) + { + auto flashColour = GetGuestFlashColour(); + for (auto guest : EntityList()) + { + DrawMapPeepPixel(guest, flashColour, dpi); + } + flashColour = GetStaffFlashColour(); + for (auto staff : EntityList()) + { + DrawMapPeepPixel(staff, flashColour, dpi); + } + } + + void DrawMapPeepPixel(Peep* peep, const uint8_t flashColour, DrawPixelInfo& dpi) + { + if (peep->x == LOCATION_NULL) + return; + + MapCoordsXY c = TransformToMapCoords({ peep->x, peep->y }); + auto leftTop = ScreenCoordsXY{ c.x, c.y }; + auto rightBottom = leftTop; + uint8_t colour = DefaultPeepMapColour; + if (EntityGetFlashing(peep)) + { + colour = flashColour; + // If flashing then map peep pixel size is increased (by moving left top downwards) + if (flashColour != DefaultPeepMapColour) { - Ride* targetRide = GetRide(tileElement->AsTrack()->GetRideIndex()); - if (targetRide != nullptr) - { - const auto& colourKey = targetRide->GetRideTypeDescriptor().ColourKey; - colourA = RideKeyColours[EnumValue(colourKey)]; - } - - break; + leftTop.x--; } - default: + } + + GfxFillRect(dpi, { leftTop, rightBottom }, colour); + } + + static uint8_t GetGuestFlashColour() + { + uint8_t colour = DefaultPeepMapColour; + if ((gWindowMapFlashingFlags & MapFlashingFlags::FlashGuests) != 0) + { + colour = GuestMapColour; + if ((gWindowMapFlashingFlags & MapFlashingFlags::SwitchColour) == 0) + colour = GuestMapColourAlternate; + } + return colour; + } + + static uint8_t GetStaffFlashColour() + { + uint8_t colour = DefaultPeepMapColour; + if ((gWindowMapFlashingFlags & MapFlashingFlags::FlashStaff) != 0) + { + colour = StaffMapColour; + if ((gWindowMapFlashingFlags & MapFlashingFlags::SwitchColour) == 0) + colour = StaffMapColourAlternate; + } + return colour; + } + + void PaintTrainOverlay(DrawPixelInfo& dpi) + { + for (auto train : TrainManager::View()) + { + for (Vehicle* vehicle = train; vehicle != nullptr; vehicle = GetEntity(vehicle->next_vehicle_on_train)) + { + if (vehicle->x == LOCATION_NULL) + continue; + + MapCoordsXY c = TransformToMapCoords({ vehicle->x, vehicle->y }); + + GfxFillRect(dpi, { { c.x, c.y }, { c.x, c.y } }, PALETTE_INDEX_171); + } + } + } + + /** + * The call to GfxFillRect was originally wrapped in Sub68DABD which made sure that arguments were ordered correctly, + * but it doesn't look like it's ever necessary here so the call was removed. + */ + void PaintHudRectangle(DrawPixelInfo& dpi) + { + WindowBase* mainWindow = WindowGetMain(); + if (mainWindow == nullptr) + return; + + Viewport* mainViewport = mainWindow->viewport; + if (mainViewport == nullptr) + return; + + auto offset = MiniMapOffsets[GetCurrentRotation()]; + auto leftTop = ScreenCoordsXY{ (mainViewport->viewPos.x >> 5) + offset.x, + (mainViewport->viewPos.y >> 4) + offset.y }; + auto rightBottom = ScreenCoordsXY{ ((mainViewport->viewPos.x + mainViewport->view_width) >> 5) + offset.x, + ((mainViewport->viewPos.y + mainViewport->view_height) >> 4) + offset.y }; + auto rightTop = ScreenCoordsXY{ rightBottom.x, leftTop.y }; + auto leftBottom = ScreenCoordsXY{ leftTop.x, rightBottom.y }; + + // top horizontal lines + GfxFillRect(dpi, { leftTop, leftTop + ScreenCoordsXY{ 3, 0 } }, PALETTE_INDEX_56); + GfxFillRect(dpi, { rightTop - ScreenCoordsXY{ 3, 0 }, rightTop }, PALETTE_INDEX_56); + + // left vertical lines + GfxFillRect(dpi, { leftTop, leftTop + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_56); + GfxFillRect(dpi, { leftBottom - ScreenCoordsXY{ 0, 3 }, leftBottom }, PALETTE_INDEX_56); + + // bottom horizontal lines + GfxFillRect(dpi, { leftBottom, leftBottom + ScreenCoordsXY{ 3, 0 } }, PALETTE_INDEX_56); + GfxFillRect(dpi, { rightBottom - ScreenCoordsXY{ 3, 0 }, rightBottom }, PALETTE_INDEX_56); + + // right vertical lines + GfxFillRect(dpi, { rightTop, rightTop + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_56); + GfxFillRect(dpi, { rightBottom - ScreenCoordsXY{ 0, 3 }, rightBottom }, PALETTE_INDEX_56); + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + // Guest tab image (animated) + uint32_t guestTabImage = SPR_TAB_GUESTS_0; + if (selected_tab == PAGE_PEEPS) + guestTabImage += list_information_type / 4; + + GfxDrawSprite( + dpi, ImageId(guestTabImage), + windowPos + ScreenCoordsXY{ widgets[WIDX_PEOPLE_TAB].left, widgets[WIDX_PEOPLE_TAB].top }); + + // Ride/stall tab image (animated) + uint32_t rideTabImage = SPR_TAB_RIDE_0; + if (selected_tab == PAGE_RIDES) + rideTabImage += list_information_type / 4; + + GfxDrawSprite( + dpi, ImageId(rideTabImage), + windowPos + ScreenCoordsXY{ widgets[WIDX_RIDES_TAB].left, widgets[WIDX_RIDES_TAB].top }); + } + + void ShowDefaultScenarioEditorButtons() + { + widgets[WIDX_BUILD_PARK_ENTRANCE].type = WindowWidgetType::FlatBtn; + widgets[WIDX_PEOPLE_STARTING_POSITION].type = WindowWidgetType::FlatBtn; + widgets[WIDX_MAP_SIZE_SPINNER_Y].type = WindowWidgetType::Spinner; + widgets[WIDX_MAP_SIZE_SPINNER_Y_UP].type = WindowWidgetType::Button; + widgets[WIDX_MAP_SIZE_SPINNER_Y_DOWN].type = WindowWidgetType::Button; + widgets[WIDX_MAP_SIZE_LINK].type = WindowWidgetType::FlatBtn; + widgets[WIDX_MAP_SIZE_SPINNER_X].type = WindowWidgetType::Spinner; + widgets[WIDX_MAP_SIZE_SPINNER_X_UP].type = WindowWidgetType::Button; + widgets[WIDX_MAP_SIZE_SPINNER_X_DOWN].type = WindowWidgetType::Button; + + // Only show this in the scenario editor, even when in sandbox mode. + if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + widgets[WIDX_MAP_GENERATOR].type = WindowWidgetType::Button; + + // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use + auto& gameState = GetGameState(); + auto ft = Formatter::Common(); + ft.Add(gameState.MapSize.y - 2); + ft.Add(gameState.MapSize.x - 2); + } + + void InputLandSize() + { + Formatter ft; + ft.Add(kLandToolMinimumSize); + ft.Add(kLandToolMaximumSize); + TextInputOpen(WIDX_LAND_TOOL, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); + } + + void InputMapSize(WidgetIndex callingWidget) + { + if (IsWidgetPressed(WIDX_MAP_SIZE_LINK)) + _resizeDirection = ResizeDirection::Both; + else + _resizeDirection = (callingWidget == WIDX_MAP_SIZE_SPINNER_Y) ? ResizeDirection::Y : ResizeDirection::X; + + Formatter ft; + ft.Add(MINIMUM_MAP_SIZE_PRACTICAL); + ft.Add(MAXIMUM_MAP_SIZE_PRACTICAL); + TextInputOpen(callingWidget, STR_MAP_SIZE_2, STR_ENTER_MAP_SIZE, ft, STR_NONE, STR_NONE, 4); + } + + CoordsXY ScreenToMap(ScreenCoordsXY screenCoords) + { + screenCoords.x = ((screenCoords.x + 8) - MAXIMUM_MAP_SIZE_TECHNICAL) / 2; + screenCoords.y = ((screenCoords.y + 8)) / 2; + auto location = TileCoordsXY(screenCoords.y - screenCoords.x, screenCoords.x + screenCoords.y).ToCoordsXY(); + + switch (GetCurrentRotation()) + { + case 0: + return location; + case 1: + return { MAXIMUM_MAP_SIZE_BIG - 1 - location.y, location.x }; + case 2: + return { MAXIMUM_MAP_SIZE_BIG - 1 - location.x, MAXIMUM_MAP_SIZE_BIG - 1 - location.y }; + case 3: + return { location.y, MAXIMUM_MAP_SIZE_BIG - 1 - location.x }; + } + + return { 0, 0 }; // unreachable + } + + MapCoordsXY TransformToMapCoords(CoordsXY c) + { + int32_t x = c.x, y = c.y; + + switch (GetCurrentRotation()) + { + case 3: + std::swap(x, y); + x = MAXIMUM_MAP_SIZE_BIG - 1 - x; + break; + case 2: + x = MAXIMUM_MAP_SIZE_BIG - 1 - x; + y = MAXIMUM_MAP_SIZE_BIG - 1 - y; + break; + case 1: + std::swap(x, y); + y = MAXIMUM_MAP_SIZE_BIG - 1 - y; + break; + case 0: break; } - } while (!(tileElement++)->IsLastForTile()); + x /= 32; + y /= 32; - if (colourA != 0) - return colourA; - - return colourB; - } - - void PaintPeepOverlay(DrawPixelInfo& dpi) - { - auto flashColour = GetGuestFlashColour(); - for (auto guest : EntityList()) - { - DrawMapPeepPixel(guest, flashColour, dpi); + return { -x + y + MAXIMUM_MAP_SIZE_TECHNICAL - 8, x + y - 8 }; } - flashColour = GetStaffFlashColour(); - for (auto staff : EntityList()) + + void ResizeMap() { - DrawMapPeepPixel(staff, flashColour, dpi); + widgets[WIDX_MAP].right = width - 4; + + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode) + widgets[WIDX_MAP].bottom = height - 1 - 72; + else if (selected_tab == PAGE_RIDES) + widgets[WIDX_MAP].bottom = height - 1 - (4 * LIST_ROW_HEIGHT + 4); + else + widgets[WIDX_MAP].bottom = height - 1 - 14; } - } - void DrawMapPeepPixel(Peep* peep, const uint8_t flashColour, DrawPixelInfo& dpi) - { - if (peep->x == LOCATION_NULL) - return; - - MapCoordsXY c = TransformToMapCoords({ peep->x, peep->y }); - auto leftTop = ScreenCoordsXY{ c.x, c.y }; - auto rightBottom = leftTop; - uint8_t colour = DefaultPeepMapColour; - if (EntityGetFlashing(peep)) + void CalculateTextLayout() { - colour = flashColour; - // If flashing then map peep pixel size is increased (by moving left top downwards) - if (flashColour != DefaultPeepMapColour) + int32_t textOffset = 4 + LIST_ROW_HEIGHT; + _firstColumnWidth = 118; + for (uint32_t i = 0; i < 4; i++) { - leftTop.x--; + const auto* labelStr = LanguageGetString(MapLabels[i]); + _firstColumnWidth = std::max(textOffset + GfxGetStringWidth(labelStr, FontStyle::Medium), _firstColumnWidth); } - } - GfxFillRect(dpi, { leftTop, rightBottom }, colour); - } - - static uint8_t GetGuestFlashColour() - { - uint8_t colour = DefaultPeepMapColour; - if ((gWindowMapFlashingFlags & MapFlashingFlags::FlashGuests) != 0) - { - colour = GuestMapColour; - if ((gWindowMapFlashingFlags & MapFlashingFlags::SwitchColour) == 0) - colour = GuestMapColourAlternate; - } - return colour; - } - - static uint8_t GetStaffFlashColour() - { - uint8_t colour = DefaultPeepMapColour; - if ((gWindowMapFlashingFlags & MapFlashingFlags::FlashStaff) != 0) - { - colour = StaffMapColour; - if ((gWindowMapFlashingFlags & MapFlashingFlags::SwitchColour) == 0) - colour = StaffMapColourAlternate; - } - return colour; - } - - void PaintTrainOverlay(DrawPixelInfo& dpi) - { - for (auto train : TrainManager::View()) - { - for (Vehicle* vehicle = train; vehicle != nullptr; vehicle = GetEntity(vehicle->next_vehicle_on_train)) + textOffset += _firstColumnWidth + 4; + min_width = WW; + for (uint32_t i = 4; i < std::size(MapLabels); i++) { - if (vehicle->x == LOCATION_NULL) - continue; - - MapCoordsXY c = TransformToMapCoords({ vehicle->x, vehicle->y }); - - GfxFillRect(dpi, { { c.x, c.y }, { c.x, c.y } }, PALETTE_INDEX_171); + const auto* labelStr = LanguageGetString(MapLabels[i]); + min_width = std::max( + static_cast(textOffset + GfxGetStringWidth(labelStr, FontStyle::Medium)), min_width); } + width = std::max(min_width, width); + _recalculateScrollbars = true; + } + }; + + WindowBase* WindowMapOpen() + { + try + { + WindowBase* w = WindowFocusOrCreate(WindowClass::Map, 245, 259, WF_10); + w->selected_tab = 0; + w->list_information_type = 0; + return w; + } + catch (const std::bad_alloc&) + { + return nullptr; } } - /** - * The call to GfxFillRect was originally wrapped in Sub68DABD which made sure that arguments were ordered correctly, - * but it doesn't look like it's ever necessary here so the call was removed. - */ - void PaintHudRectangle(DrawPixelInfo& dpi) + void WindowMapReset() { - WindowBase* mainWindow = WindowGetMain(); - if (mainWindow == nullptr) + WindowBase* w; + + // Check if window is even opened + w = WindowBringToFrontByClass(WindowClass::Map); + if (w == nullptr) + { return; - - Viewport* mainViewport = mainWindow->viewport; - if (mainViewport == nullptr) - return; - - auto offset = MiniMapOffsets[GetCurrentRotation()]; - auto leftTop = ScreenCoordsXY{ (mainViewport->viewPos.x >> 5) + offset.x, (mainViewport->viewPos.y >> 4) + offset.y }; - auto rightBottom = ScreenCoordsXY{ ((mainViewport->viewPos.x + mainViewport->view_width) >> 5) + offset.x, - ((mainViewport->viewPos.y + mainViewport->view_height) >> 4) + offset.y }; - auto rightTop = ScreenCoordsXY{ rightBottom.x, leftTop.y }; - auto leftBottom = ScreenCoordsXY{ leftTop.x, rightBottom.y }; - - // top horizontal lines - GfxFillRect(dpi, { leftTop, leftTop + ScreenCoordsXY{ 3, 0 } }, PALETTE_INDEX_56); - GfxFillRect(dpi, { rightTop - ScreenCoordsXY{ 3, 0 }, rightTop }, PALETTE_INDEX_56); - - // left vertical lines - GfxFillRect(dpi, { leftTop, leftTop + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_56); - GfxFillRect(dpi, { leftBottom - ScreenCoordsXY{ 0, 3 }, leftBottom }, PALETTE_INDEX_56); - - // bottom horizontal lines - GfxFillRect(dpi, { leftBottom, leftBottom + ScreenCoordsXY{ 3, 0 } }, PALETTE_INDEX_56); - GfxFillRect(dpi, { rightBottom - ScreenCoordsXY{ 3, 0 }, rightBottom }, PALETTE_INDEX_56); - - // right vertical lines - GfxFillRect(dpi, { rightTop, rightTop + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_56); - GfxFillRect(dpi, { rightBottom - ScreenCoordsXY{ 0, 3 }, rightBottom }, PALETTE_INDEX_56); - } - - void DrawTabImages(DrawPixelInfo& dpi) - { - // Guest tab image (animated) - uint32_t guestTabImage = SPR_TAB_GUESTS_0; - if (selected_tab == PAGE_PEEPS) - guestTabImage += list_information_type / 4; - - GfxDrawSprite( - dpi, ImageId(guestTabImage), - windowPos + ScreenCoordsXY{ widgets[WIDX_PEOPLE_TAB].left, widgets[WIDX_PEOPLE_TAB].top }); - - // Ride/stall tab image (animated) - uint32_t rideTabImage = SPR_TAB_RIDE_0; - if (selected_tab == PAGE_RIDES) - rideTabImage += list_information_type / 4; - - GfxDrawSprite( - dpi, ImageId(rideTabImage), - windowPos + ScreenCoordsXY{ widgets[WIDX_RIDES_TAB].left, widgets[WIDX_RIDES_TAB].top }); - } - - void ShowDefaultScenarioEditorButtons() - { - widgets[WIDX_BUILD_PARK_ENTRANCE].type = WindowWidgetType::FlatBtn; - widgets[WIDX_PEOPLE_STARTING_POSITION].type = WindowWidgetType::FlatBtn; - widgets[WIDX_MAP_SIZE_SPINNER_Y].type = WindowWidgetType::Spinner; - widgets[WIDX_MAP_SIZE_SPINNER_Y_UP].type = WindowWidgetType::Button; - widgets[WIDX_MAP_SIZE_SPINNER_Y_DOWN].type = WindowWidgetType::Button; - widgets[WIDX_MAP_SIZE_LINK].type = WindowWidgetType::FlatBtn; - widgets[WIDX_MAP_SIZE_SPINNER_X].type = WindowWidgetType::Spinner; - widgets[WIDX_MAP_SIZE_SPINNER_X_UP].type = WindowWidgetType::Button; - widgets[WIDX_MAP_SIZE_SPINNER_X_DOWN].type = WindowWidgetType::Button; - - // Only show this in the scenario editor, even when in sandbox mode. - if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) - widgets[WIDX_MAP_GENERATOR].type = WindowWidgetType::Button; - - // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use - auto& gameState = GetGameState(); - auto ft = Formatter::Common(); - ft.Add(gameState.MapSize.y - 2); - ft.Add(gameState.MapSize.x - 2); - } - - void InputLandSize() - { - Formatter ft; - ft.Add(kLandToolMinimumSize); - ft.Add(kLandToolMaximumSize); - TextInputOpen(WIDX_LAND_TOOL, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); - } - - void InputMapSize(WidgetIndex callingWidget) - { - if (IsWidgetPressed(WIDX_MAP_SIZE_LINK)) - _resizeDirection = ResizeDirection::Both; - else - _resizeDirection = (callingWidget == WIDX_MAP_SIZE_SPINNER_Y) ? ResizeDirection::Y : ResizeDirection::X; - - Formatter ft; - ft.Add(MINIMUM_MAP_SIZE_PRACTICAL); - ft.Add(MAXIMUM_MAP_SIZE_PRACTICAL); - TextInputOpen(callingWidget, STR_MAP_SIZE_2, STR_ENTER_MAP_SIZE, ft, STR_NONE, STR_NONE, 4); - } - - CoordsXY ScreenToMap(ScreenCoordsXY screenCoords) - { - screenCoords.x = ((screenCoords.x + 8) - MAXIMUM_MAP_SIZE_TECHNICAL) / 2; - screenCoords.y = ((screenCoords.y + 8)) / 2; - auto location = TileCoordsXY(screenCoords.y - screenCoords.x, screenCoords.x + screenCoords.y).ToCoordsXY(); - - switch (GetCurrentRotation()) - { - case 0: - return location; - case 1: - return { MAXIMUM_MAP_SIZE_BIG - 1 - location.y, location.x }; - case 2: - return { MAXIMUM_MAP_SIZE_BIG - 1 - location.x, MAXIMUM_MAP_SIZE_BIG - 1 - location.y }; - case 3: - return { location.y, MAXIMUM_MAP_SIZE_BIG - 1 - location.x }; } - return { 0, 0 }; // unreachable + auto* mapWindow = static_cast(w); + mapWindow->ResetMap(); } - - MapCoordsXY TransformToMapCoords(CoordsXY c) - { - int32_t x = c.x, y = c.y; - - switch (GetCurrentRotation()) - { - case 3: - std::swap(x, y); - x = MAXIMUM_MAP_SIZE_BIG - 1 - x; - break; - case 2: - x = MAXIMUM_MAP_SIZE_BIG - 1 - x; - y = MAXIMUM_MAP_SIZE_BIG - 1 - y; - break; - case 1: - std::swap(x, y); - y = MAXIMUM_MAP_SIZE_BIG - 1 - y; - break; - case 0: - break; - } - x /= 32; - y /= 32; - - return { -x + y + MAXIMUM_MAP_SIZE_TECHNICAL - 8, x + y - 8 }; - } - - void ResizeMap() - { - widgets[WIDX_MAP].right = width - 4; - - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode) - widgets[WIDX_MAP].bottom = height - 1 - 72; - else if (selected_tab == PAGE_RIDES) - widgets[WIDX_MAP].bottom = height - 1 - (4 * LIST_ROW_HEIGHT + 4); - else - widgets[WIDX_MAP].bottom = height - 1 - 14; - } - - void CalculateTextLayout() - { - int32_t textOffset = 4 + LIST_ROW_HEIGHT; - _firstColumnWidth = 118; - for (uint32_t i = 0; i < 4; i++) - { - const auto* labelStr = LanguageGetString(MapLabels[i]); - _firstColumnWidth = std::max(textOffset + GfxGetStringWidth(labelStr, FontStyle::Medium), _firstColumnWidth); - } - - textOffset += _firstColumnWidth + 4; - min_width = WW; - for (uint32_t i = 4; i < std::size(MapLabels); i++) - { - const auto* labelStr = LanguageGetString(MapLabels[i]); - min_width = std::max(static_cast(textOffset + GfxGetStringWidth(labelStr, FontStyle::Medium)), min_width); - } - width = std::max(min_width, width); - _recalculateScrollbars = true; - } -}; - -WindowBase* WindowMapOpen() -{ - try - { - WindowBase* w = WindowFocusOrCreate(WindowClass::Map, 245, 259, WF_10); - w->selected_tab = 0; - w->list_information_type = 0; - return w; - } - catch (const std::bad_alloc&) - { - return nullptr; - } -} - -void WindowMapReset() -{ - WindowBase* w; - - // Check if window is even opened - w = WindowBringToFrontByClass(WindowClass::Map); - if (w == nullptr) - { - return; - } - - auto* mapWindow = static_cast(w); - mapWindow->ResetMap(); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/MapGen.cpp b/src/openrct2-ui/windows/MapGen.cpp index f8bae95d8e..0c99fc1f98 100644 --- a/src/openrct2-ui/windows/MapGen.cpp +++ b/src/openrct2-ui/windows/MapGen.cpp @@ -25,105 +25,105 @@ #include #include -using namespace OpenRCT2; - -enum +namespace OpenRCT2::Ui::Windows { - WINDOW_MAPGEN_PAGE_BASE, - WINDOW_MAPGEN_PAGE_RANDOM, - WINDOW_MAPGEN_PAGE_SIMPLEX, - WINDOW_MAPGEN_PAGE_HEIGHTMAP, - WINDOW_MAPGEN_PAGE_COUNT -}; + enum + { + WINDOW_MAPGEN_PAGE_BASE, + WINDOW_MAPGEN_PAGE_RANDOM, + WINDOW_MAPGEN_PAGE_SIMPLEX, + WINDOW_MAPGEN_PAGE_HEIGHTMAP, + WINDOW_MAPGEN_PAGE_COUNT + }; -enum -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PAGE_BACKGROUND, - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_TAB_3, - WIDX_TAB_4, + enum + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PAGE_BACKGROUND, + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_TAB_3, + WIDX_TAB_4, - TAB_BEGIN, + TAB_BEGIN, - WIDX_MAP_GENERATE = TAB_BEGIN, - WIDX_MAP_SIZE_Y, - WIDX_MAP_SIZE_Y_UP, - WIDX_MAP_SIZE_Y_DOWN, - WIDX_MAP_SIZE_LINK, - WIDX_MAP_SIZE_X, - WIDX_MAP_SIZE_X_UP, - WIDX_MAP_SIZE_X_DOWN, - WIDX_BASE_HEIGHT, - WIDX_BASE_HEIGHT_UP, - WIDX_BASE_HEIGHT_DOWN, - WIDX_WATER_LEVEL, - WIDX_WATER_LEVEL_UP, - WIDX_WATER_LEVEL_DOWN, - WIDX_FLOOR_TEXTURE, - WIDX_WALL_TEXTURE, + WIDX_MAP_GENERATE = TAB_BEGIN, + WIDX_MAP_SIZE_Y, + WIDX_MAP_SIZE_Y_UP, + WIDX_MAP_SIZE_Y_DOWN, + WIDX_MAP_SIZE_LINK, + WIDX_MAP_SIZE_X, + WIDX_MAP_SIZE_X_UP, + WIDX_MAP_SIZE_X_DOWN, + WIDX_BASE_HEIGHT, + WIDX_BASE_HEIGHT_UP, + WIDX_BASE_HEIGHT_DOWN, + WIDX_WATER_LEVEL, + WIDX_WATER_LEVEL_UP, + WIDX_WATER_LEVEL_DOWN, + WIDX_FLOOR_TEXTURE, + WIDX_WALL_TEXTURE, - WIDX_RANDOM_GENERATE = TAB_BEGIN, - WIDX_RANDOM_TERRAIN, - WIDX_RANDOM_PLACE_TREES, + WIDX_RANDOM_GENERATE = TAB_BEGIN, + WIDX_RANDOM_TERRAIN, + WIDX_RANDOM_PLACE_TREES, - WIDX_SIMPLEX_GENERATE = TAB_BEGIN, - WIDX_SIMPLEX_LABEL, - WIDX_SIMPLEX_LOW, - WIDX_SIMPLEX_LOW_UP, - WIDX_SIMPLEX_LOW_DOWN, - WIDX_SIMPLEX_HIGH, - WIDX_SIMPLEX_HIGH_UP, - WIDX_SIMPLEX_HIGH_DOWN, - WIDX_SIMPLEX_BASE_FREQ, - WIDX_SIMPLEX_BASE_FREQ_UP, - WIDX_SIMPLEX_BASE_FREQ_DOWN, - WIDX_SIMPLEX_OCTAVES, - WIDX_SIMPLEX_OCTAVES_UP, - WIDX_SIMPLEX_OCTAVES_DOWN, - WIDX_SIMPLEX_MAP_SIZE_Y, - WIDX_SIMPLEX_MAP_SIZE_Y_UP, - WIDX_SIMPLEX_MAP_SIZE_Y_DOWN, - WIDX_SIMPLEX_MAP_SIZE_LINK, - WIDX_SIMPLEX_MAP_SIZE_X, - WIDX_SIMPLEX_MAP_SIZE_X_UP, - WIDX_SIMPLEX_MAP_SIZE_X_DOWN, - WIDX_SIMPLEX_WATER_LEVEL, - WIDX_SIMPLEX_WATER_LEVEL_UP, - WIDX_SIMPLEX_WATER_LEVEL_DOWN, - WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX, - WIDX_SIMPLEX_FLOOR_TEXTURE, - WIDX_SIMPLEX_WALL_TEXTURE, - WIDX_SIMPLEX_PLACE_TREES_CHECKBOX, + WIDX_SIMPLEX_GENERATE = TAB_BEGIN, + WIDX_SIMPLEX_LABEL, + WIDX_SIMPLEX_LOW, + WIDX_SIMPLEX_LOW_UP, + WIDX_SIMPLEX_LOW_DOWN, + WIDX_SIMPLEX_HIGH, + WIDX_SIMPLEX_HIGH_UP, + WIDX_SIMPLEX_HIGH_DOWN, + WIDX_SIMPLEX_BASE_FREQ, + WIDX_SIMPLEX_BASE_FREQ_UP, + WIDX_SIMPLEX_BASE_FREQ_DOWN, + WIDX_SIMPLEX_OCTAVES, + WIDX_SIMPLEX_OCTAVES_UP, + WIDX_SIMPLEX_OCTAVES_DOWN, + WIDX_SIMPLEX_MAP_SIZE_Y, + WIDX_SIMPLEX_MAP_SIZE_Y_UP, + WIDX_SIMPLEX_MAP_SIZE_Y_DOWN, + WIDX_SIMPLEX_MAP_SIZE_LINK, + WIDX_SIMPLEX_MAP_SIZE_X, + WIDX_SIMPLEX_MAP_SIZE_X_UP, + WIDX_SIMPLEX_MAP_SIZE_X_DOWN, + WIDX_SIMPLEX_WATER_LEVEL, + WIDX_SIMPLEX_WATER_LEVEL_UP, + WIDX_SIMPLEX_WATER_LEVEL_DOWN, + WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX, + WIDX_SIMPLEX_FLOOR_TEXTURE, + WIDX_SIMPLEX_WALL_TEXTURE, + WIDX_SIMPLEX_PLACE_TREES_CHECKBOX, - WIDX_HEIGHTMAP_SELECT = TAB_BEGIN, - WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, - WIDX_HEIGHTMAP_STRENGTH, - WIDX_HEIGHTMAP_STRENGTH_UP, - WIDX_HEIGHTMAP_STRENGTH_DOWN, - WIDX_HEIGHTMAP_NORMALIZE, - WIDX_HEIGHTMAP_SMOOTH_TILES, - WIDX_HEIGHTMAP_LOW, - WIDX_HEIGHTMAP_LOW_UP, - WIDX_HEIGHTMAP_LOW_DOWN, - WIDX_HEIGHTMAP_HIGH, - WIDX_HEIGHTMAP_HIGH_UP, - WIDX_HEIGHTMAP_HIGH_DOWN, - WIDX_HEIGHTMAP_WATER_LEVEL, - WIDX_HEIGHTMAP_WATER_LEVEL_UP, - WIDX_HEIGHTMAP_WATER_LEVEL_DOWN, -}; + WIDX_HEIGHTMAP_SELECT = TAB_BEGIN, + WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, + WIDX_HEIGHTMAP_STRENGTH, + WIDX_HEIGHTMAP_STRENGTH_UP, + WIDX_HEIGHTMAP_STRENGTH_DOWN, + WIDX_HEIGHTMAP_NORMALIZE, + WIDX_HEIGHTMAP_SMOOTH_TILES, + WIDX_HEIGHTMAP_LOW, + WIDX_HEIGHTMAP_LOW_UP, + WIDX_HEIGHTMAP_LOW_DOWN, + WIDX_HEIGHTMAP_HIGH, + WIDX_HEIGHTMAP_HIGH_UP, + WIDX_HEIGHTMAP_HIGH_DOWN, + WIDX_HEIGHTMAP_WATER_LEVEL, + WIDX_HEIGHTMAP_WATER_LEVEL_UP, + WIDX_HEIGHTMAP_WATER_LEVEL_DOWN, + }; #pragma region Widgets -static constexpr StringId WINDOW_TITLE = STR_MAPGEN_WINDOW_TITLE; -static constexpr int32_t WW = 250; -static constexpr int32_t WH = 273; + static constexpr StringId WINDOW_TITLE = STR_MAPGEN_WINDOW_TITLE; + static constexpr int32_t WW = 250; + static constexpr int32_t WH = 273; -// clang-format off + // clang-format off #define SHARED_WIDGETS \ WINDOW_SHIM(WINDOW_TITLE, WW, WH), /* WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE */ \ MakeWidget({ 0, 43}, {WW, 229}, WindowWidgetType::Resize, WindowColour::Secondary), /* WIDX_PAGE_BACKGROUND */ \ @@ -184,20 +184,20 @@ static Widget HeightmapWidgets[] = { MakeSpinnerWidgets({104, 160}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_WATER_LEVEL{,_UP,_DOWN} kWidgetsEnd, }; -// clang-format on + // clang-format on -static Widget* PageWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { - MapWidgets, - RandomWidgets, - SimplexWidgets, - HeightmapWidgets, -}; + static Widget* PageWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { + MapWidgets, + RandomWidgets, + SimplexWidgets, + HeightmapWidgets, + }; #pragma endregion #pragma region Widget flags -// clang-format off + // clang-format off static uint64_t PageDisabledWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { 0, @@ -265,1126 +265,1139 @@ static uint64_t PressedWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { 0, (1uLL << WIDX_HEIGHTMAP_SMOOTH_TILES) }; -// clang-format on + // clang-format on #pragma endregion -static constexpr int32_t TabAnimationDivisor[WINDOW_MAPGEN_PAGE_COUNT] = { - 1, - 1, - 1, - 1, -}; -static constexpr int32_t TabAnimationFrames[WINDOW_MAPGEN_PAGE_COUNT] = { - 1, - 1, - 1, - 1, -}; -static constexpr int32_t TabAnimationLoops[WINDOW_MAPGEN_PAGE_COUNT] = { - 16, - 16, - 16, - 0, -}; -// clang-format on + static constexpr int32_t TabAnimationDivisor[WINDOW_MAPGEN_PAGE_COUNT] = { + 1, + 1, + 1, + 1, + }; + static constexpr int32_t TabAnimationFrames[WINDOW_MAPGEN_PAGE_COUNT] = { + 1, + 1, + 1, + 1, + }; + static constexpr int32_t TabAnimationLoops[WINDOW_MAPGEN_PAGE_COUNT] = { + 16, + 16, + 16, + 0, + }; + // clang-format on -constexpr int32_t BASESIZE_MIN = 0; -constexpr int32_t BASESIZE_MAX = 60; -constexpr int32_t WATERLEVEL_MIN = 0; -constexpr int32_t WATERLEVEL_MAX = 54; -constexpr int32_t MAX_SMOOTH_ITERATIONS = 20; + constexpr int32_t BASESIZE_MIN = 0; + constexpr int32_t BASESIZE_MAX = 60; + constexpr int32_t WATERLEVEL_MIN = 0; + constexpr int32_t WATERLEVEL_MAX = 54; + constexpr int32_t MAX_SMOOTH_ITERATIONS = 20; -enum class ResizeDirection -{ - Both, - X, - Y, -}; - -static void HeightmapLoadsaveCallback(int32_t result, const utf8* path); - -class MapGenWindow final : public Window -{ -private: - TileCoordsXY _mapSize{ 150, 150 }; - ResizeDirection _resizeDirection{ ResizeDirection::Both }; - bool _mapWidthAndHeightLinked{ true }; - int32_t _baseHeight = 12; - int32_t _waterLevel = 6; - int32_t _floorTexture = 0; - int32_t _wallTexture = 0; - bool _randomTerrain = true; - int32_t _placeTrees = 1; - - int32_t _simplex_low = 6; - int32_t _simplex_high = 10; - int32_t _simplex_base_freq = 60; - int32_t _simplex_octaves = 4; - - bool _heightmapLoaded = false; - bool _heightmapSmoothMap = false; - int32_t _heightmapSmoothStrength = 1; - bool _heightmapNormalize = false; - bool _heightmapSmoothTiles = true; - int32_t _heightmapLow = 2; - int32_t _heightmapHigh = 70; - - void SetPage(int32_t newPage) + enum class ResizeDirection { - page = newPage; - frame_no = 0; - RemoveViewport(); + Both, + X, + Y, + }; - hold_down_widgets = HoldDownWidgets[newPage]; - widgets = PageWidgets[newPage]; - disabled_widgets = PageDisabledWidgets[newPage]; - pressed_widgets = PressedWidgets[newPage]; + static void HeightmapLoadsaveCallback(int32_t result, const utf8* path); - // Enable heightmap widgets if one is loaded - if (newPage == WINDOW_MAPGEN_PAGE_HEIGHTMAP && _heightmapLoaded) + class MapGenWindow final : public Window + { + private: + TileCoordsXY _mapSize{ 150, 150 }; + ResizeDirection _resizeDirection{ ResizeDirection::Both }; + bool _mapWidthAndHeightLinked{ true }; + int32_t _baseHeight = 12; + int32_t _waterLevel = 6; + int32_t _floorTexture = 0; + int32_t _wallTexture = 0; + bool _randomTerrain = true; + int32_t _placeTrees = 1; + + int32_t _simplex_low = 6; + int32_t _simplex_high = 10; + int32_t _simplex_base_freq = 60; + int32_t _simplex_octaves = 4; + + bool _heightmapLoaded = false; + bool _heightmapSmoothMap = false; + int32_t _heightmapSmoothStrength = 1; + bool _heightmapNormalize = false; + bool _heightmapSmoothTiles = true; + int32_t _heightmapLow = 2; + int32_t _heightmapHigh = 70; + + void SetPage(int32_t newPage) { - SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_NORMALIZE, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_TILES, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH_UP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH_DOWN, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_LOW, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_LOW_UP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_LOW_DOWN, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL_UP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL_DOWN, true); - } + page = newPage; + frame_no = 0; + RemoveViewport(); - InitScrollWidgets(); - Invalidate(); - } + hold_down_widgets = HoldDownWidgets[newPage]; + widgets = PageWidgets[newPage]; + disabled_widgets = PageDisabledWidgets[newPage]; + pressed_widgets = PressedWidgets[newPage]; - void SetPressedTab() - { - int32_t i; - for (i = 0; i < WINDOW_MAPGEN_PAGE_COUNT; i++) - pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); - pressed_widgets |= 1LL << (WIDX_TAB_1 + page); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t newPage, int32_t spriteIndex) - { - WidgetIndex widgetIndex = WIDX_TAB_1 + newPage; - - if (!WidgetIsDisabled(*this, widgetIndex)) - { - if (page == newPage) + // Enable heightmap widgets if one is loaded + if (newPage == WINDOW_MAPGEN_PAGE_HEIGHTMAP && _heightmapLoaded) { - int32_t frame = frame_no / TabAnimationDivisor[page]; - spriteIndex += (frame % TabAnimationFrames[page]); + SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _heightmapSmoothMap); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapSmoothMap); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapSmoothMap); + SetWidgetEnabled(WIDX_HEIGHTMAP_NORMALIZE, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_TILES, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH_UP, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH_DOWN, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_LOW, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_LOW_UP, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_LOW_DOWN, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL_UP, true); + SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL_DOWN, true); } - GfxDrawSprite( - dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + InitScrollWidgets(); + Invalidate(); } - } - void DrawTabImages(DrawPixelInfo& dpi) - { - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_BASE, SPR_G2_TAB_LAND); - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_RANDOM, SPR_G2_TAB_TREE); - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_SIMPLEX, SPR_G2_TAB_PENCIL); - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_HEIGHTMAP, SPR_TAB_GRAPH_0); - } - - void ChangeMapSize(int32_t sizeOffset) - { - if (_mapWidthAndHeightLinked) - _resizeDirection = ResizeDirection::Both; - - if (_resizeDirection != ResizeDirection::X) - _mapSize.y = std::clamp(_mapSize.y + sizeOffset, MINIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL); - if (_resizeDirection != ResizeDirection::Y) - _mapSize.x = std::clamp(_mapSize.x + sizeOffset, MINIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL); - } - - void InputMapSize(WidgetIndex callingWidget, int32_t currentValue) - { - Formatter ft; - ft.Add(MINIMUM_MAP_SIZE_PRACTICAL); - ft.Add(MAXIMUM_MAP_SIZE_PRACTICAL); - - // Practical map size is 2 lower than the technical map size - currentValue -= 2; - WindowTextInputOpen(this, callingWidget, STR_MAP_SIZE_2, STR_ENTER_MAP_SIZE, ft, STR_FORMAT_INTEGER, currentValue, 4); - } - - void SharedMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void SetPressedTab() { - case WIDX_CLOSE: - Close(); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - SetPage(widgetIndex - WIDX_TAB_1); - break; + int32_t i; + for (i = 0; i < WINDOW_MAPGEN_PAGE_COUNT; i++) + pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); + pressed_widgets |= 1LL << (WIDX_TAB_1 + page); + } + + void DrawTabImage(DrawPixelInfo& dpi, int32_t newPage, int32_t spriteIndex) + { + WidgetIndex widgetIndex = WIDX_TAB_1 + newPage; + + if (!WidgetIsDisabled(*this, widgetIndex)) + { + if (page == newPage) + { + int32_t frame = frame_no / TabAnimationDivisor[page]; + spriteIndex += (frame % TabAnimationFrames[page]); + } + + GfxDrawSprite( + dpi, ImageId(spriteIndex), + windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + } + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_BASE, SPR_G2_TAB_LAND); + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_RANDOM, SPR_G2_TAB_TREE); + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_SIMPLEX, SPR_G2_TAB_PENCIL); + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_HEIGHTMAP, SPR_TAB_GRAPH_0); + } + + void ChangeMapSize(int32_t sizeOffset) + { + if (_mapWidthAndHeightLinked) + _resizeDirection = ResizeDirection::Both; + + if (_resizeDirection != ResizeDirection::X) + _mapSize.y = std::clamp(_mapSize.y + sizeOffset, MINIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL); + if (_resizeDirection != ResizeDirection::Y) + _mapSize.x = std::clamp(_mapSize.x + sizeOffset, MINIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL); + } + + void InputMapSize(WidgetIndex callingWidget, int32_t currentValue) + { + Formatter ft; + ft.Add(MINIMUM_MAP_SIZE_PRACTICAL); + ft.Add(MAXIMUM_MAP_SIZE_PRACTICAL); + + // Practical map size is 2 lower than the technical map size + currentValue -= 2; + WindowTextInputOpen( + this, callingWidget, STR_MAP_SIZE_2, STR_ENTER_MAP_SIZE, ft, STR_FORMAT_INTEGER, currentValue, 4); + } + + void SharedMouseUp(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + SetPage(widgetIndex - WIDX_TAB_1); + break; + } } - } #pragma region Base page - void BaseMouseUp(WidgetIndex widgetIndex) - { - SharedMouseUp(widgetIndex); - - MapGenSettings mapgenSettings; - Formatter ft; - switch (widgetIndex) + void BaseMouseUp(WidgetIndex widgetIndex) { - case WIDX_MAP_GENERATE: - mapgenSettings.mapSize = _mapSize; - mapgenSettings.height = _baseHeight + 2; - mapgenSettings.water_level = _waterLevel + 2; - mapgenSettings.floor = _floorTexture; - mapgenSettings.wall = _wallTexture; + SharedMouseUp(widgetIndex); - MapGenGenerateBlank(&mapgenSettings); - GfxInvalidateScreen(); - break; - case WIDX_MAP_SIZE_Y: - _resizeDirection = ResizeDirection::Y; - InputMapSize(WIDX_MAP_SIZE_Y, _mapSize.y); - break; - case WIDX_MAP_SIZE_X: - _resizeDirection = ResizeDirection::X; - InputMapSize(WIDX_MAP_SIZE_X, _mapSize.x); - break; - case WIDX_MAP_SIZE_LINK: - _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; - break; - case WIDX_BASE_HEIGHT: - ft.Add((BASESIZE_MIN - 12) / 2); - ft.Add((BASESIZE_MAX - 12) / 2); - WindowTextInputOpen( - this, WIDX_BASE_HEIGHT, STR_BASE_HEIGHT, STR_ENTER_BASE_HEIGHT, ft, STR_FORMAT_INTEGER, - (_baseHeight - 12) / 2, 3); - break; - case WIDX_WATER_LEVEL: - ft.Add((WATERLEVEL_MIN - 12) / 2); - ft.Add((WATERLEVEL_MAX - 12) / 2); - WindowTextInputOpen( - this, WIDX_WATER_LEVEL, STR_WATER_LEVEL, STR_ENTER_WATER_LEVEL, ft, STR_FORMAT_INTEGER, - (_waterLevel - 12) / 2, 3); - break; - } - } - - void BaseMouseDown(WidgetIndex widgetIndex, Widget* widget) - { - switch (widgetIndex) - { - case WIDX_MAP_SIZE_Y_UP: - _resizeDirection = ResizeDirection::Y; - ChangeMapSize(+1); - Invalidate(); - break; - case WIDX_MAP_SIZE_Y_DOWN: - _resizeDirection = ResizeDirection::Y; - ChangeMapSize(-1); - Invalidate(); - break; - case WIDX_MAP_SIZE_X_UP: - _resizeDirection = ResizeDirection::X; - ChangeMapSize(+1); - Invalidate(); - break; - case WIDX_MAP_SIZE_X_DOWN: - _resizeDirection = ResizeDirection::X; - ChangeMapSize(-1); - Invalidate(); - break; - case WIDX_BASE_HEIGHT_UP: - _baseHeight = std::min(_baseHeight + 2, BASESIZE_MAX); - Invalidate(); - break; - case WIDX_BASE_HEIGHT_DOWN: - _baseHeight = std::max(_baseHeight - 2, BASESIZE_MIN); - Invalidate(); - break; - case WIDX_WATER_LEVEL_UP: - _waterLevel = std::min(_waterLevel + 2, WATERLEVEL_MAX); - Invalidate(); - break; - case WIDX_WATER_LEVEL_DOWN: - _waterLevel = std::max(_waterLevel - 2, WATERLEVEL_MIN); - Invalidate(); - break; - case WIDX_FLOOR_TEXTURE: - LandTool::ShowSurfaceStyleDropdown(this, widget, _floorTexture); - break; - case WIDX_WALL_TEXTURE: - LandTool::ShowEdgeStyleDropdown(this, widget, _wallTexture); - break; - } - } - - void BaseDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - int32_t type; - - switch (widgetIndex) - { - case WIDX_FLOOR_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _floorTexture : dropdownIndex; - - if (gLandToolTerrainSurface == type) - { - gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainSurface = type; - _floorTexture = type; - } - Invalidate(); - break; - case WIDX_WALL_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _wallTexture : dropdownIndex; - - if (gLandToolTerrainEdge == type) - { - gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainEdge = type; - _wallTexture = type; - } - Invalidate(); - break; - } - } - - void BaseUpdate() - { - // Tab animation - if (++frame_no >= TabAnimationLoops[page]) - frame_no = 0; - InvalidateWidget(WIDX_TAB_1); - } - - void TextInput(WidgetIndex widgetIndex, std::string_view text) - { - int32_t value; - char* end; - - const auto strText = u8string(text); - value = strtol(strText.c_str(), &end, 10); - - if (*end != '\0') - { - return; - } - - switch (widgetIndex) - { - case WIDX_MAP_SIZE_Y: - case WIDX_MAP_SIZE_X: - case WIDX_SIMPLEX_MAP_SIZE_Y: - case WIDX_SIMPLEX_MAP_SIZE_X: - // The practical size is 2 lower than the technical size - value += 2; - if (_resizeDirection == ResizeDirection::Y || _mapWidthAndHeightLinked) - _mapSize.y = value; - if (_resizeDirection == ResizeDirection::X || _mapWidthAndHeightLinked) - _mapSize.x = value; - break; - case WIDX_BASE_HEIGHT: - _baseHeight = std::clamp((value * 2) + 12, BASESIZE_MIN, BASESIZE_MAX); - break; - case WIDX_WATER_LEVEL: - _waterLevel = std::clamp((value * 2) + 12, WATERLEVEL_MIN, WATERLEVEL_MAX); - break; - } - - Invalidate(); - } - - void BasePrepareDraw() - { - if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_BASE]) - { - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_BASE]; - InitScrollWidgets(); - } - - // Only allow linking the map size when X and Y are the same - SetWidgetPressed(WIDX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); - SetWidgetDisabled(WIDX_MAP_SIZE_LINK, _mapSize.x != _mapSize.y); - - SetPressedTab(); - - // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use - auto ft = Formatter::Common(); - ft.Add(_mapSize.y - 2); - ft.Add(_mapSize.x - 2); - } - - void DrawDropdownButton(DrawPixelInfo& dpi, WidgetIndex widgetIndex, ImageId image) - { - const auto& widget = widgets[widgetIndex]; - ScreenCoordsXY pos = { windowPos.x + widget.left, windowPos.y + widget.top }; - if (IsWidgetDisabled(widgetIndex)) - { - // Draw greyed out (light border bottom right shadow) - auto colour = colours[widget.colour]; - colour = ColourMapA[NOT_TRANSLUCENT(colour)].lighter; - GfxDrawSpriteSolid(&dpi, image, pos + ScreenCoordsXY{ 1, 1 }, colour); - - // Draw greyed out (dark) - colour = colours[widget.colour]; - colour = ColourMapA[NOT_TRANSLUCENT(colour)].mid_light; - GfxDrawSpriteSolid(&dpi, image, pos, colour); - } - else - { - GfxDrawSprite(dpi, image, pos); - } - } - - void DrawDropdownButtons(DrawPixelInfo& dpi, WidgetIndex floorWidgetIndex, WidgetIndex edgeWidgetIndex) - { - auto& objManager = GetContext()->GetObjectManager(); - const auto surfaceObj = static_cast( - objManager.GetLoadedObject(ObjectType::TerrainSurface, _floorTexture)); - ImageId surfaceImage; - if (surfaceObj != nullptr) - { - surfaceImage = ImageId(surfaceObj->IconImageId); - if (surfaceObj->Colour != 255) + MapGenSettings mapgenSettings; + Formatter ft; + switch (widgetIndex) { - surfaceImage = surfaceImage.WithPrimary(surfaceObj->Colour); + case WIDX_MAP_GENERATE: + mapgenSettings.mapSize = _mapSize; + mapgenSettings.height = _baseHeight + 2; + mapgenSettings.water_level = _waterLevel + 2; + mapgenSettings.floor = _floorTexture; + mapgenSettings.wall = _wallTexture; + + MapGenGenerateBlank(&mapgenSettings); + GfxInvalidateScreen(); + break; + case WIDX_MAP_SIZE_Y: + _resizeDirection = ResizeDirection::Y; + InputMapSize(WIDX_MAP_SIZE_Y, _mapSize.y); + break; + case WIDX_MAP_SIZE_X: + _resizeDirection = ResizeDirection::X; + InputMapSize(WIDX_MAP_SIZE_X, _mapSize.x); + break; + case WIDX_MAP_SIZE_LINK: + _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; + break; + case WIDX_BASE_HEIGHT: + ft.Add((BASESIZE_MIN - 12) / 2); + ft.Add((BASESIZE_MAX - 12) / 2); + WindowTextInputOpen( + this, WIDX_BASE_HEIGHT, STR_BASE_HEIGHT, STR_ENTER_BASE_HEIGHT, ft, STR_FORMAT_INTEGER, + (_baseHeight - 12) / 2, 3); + break; + case WIDX_WATER_LEVEL: + ft.Add((WATERLEVEL_MIN - 12) / 2); + ft.Add((WATERLEVEL_MAX - 12) / 2); + WindowTextInputOpen( + this, WIDX_WATER_LEVEL, STR_WATER_LEVEL, STR_ENTER_WATER_LEVEL, ft, STR_FORMAT_INTEGER, + (_waterLevel - 12) / 2, 3); + break; } } - ImageId edgeImage; - const auto edgeObj = static_cast(objManager.GetLoadedObject(ObjectType::TerrainEdge, _wallTexture)); - if (edgeObj != nullptr) + void BaseMouseDown(WidgetIndex widgetIndex, Widget* widget) { - edgeImage = ImageId(edgeObj->IconImageId); + switch (widgetIndex) + { + case WIDX_MAP_SIZE_Y_UP: + _resizeDirection = ResizeDirection::Y; + ChangeMapSize(+1); + Invalidate(); + break; + case WIDX_MAP_SIZE_Y_DOWN: + _resizeDirection = ResizeDirection::Y; + ChangeMapSize(-1); + Invalidate(); + break; + case WIDX_MAP_SIZE_X_UP: + _resizeDirection = ResizeDirection::X; + ChangeMapSize(+1); + Invalidate(); + break; + case WIDX_MAP_SIZE_X_DOWN: + _resizeDirection = ResizeDirection::X; + ChangeMapSize(-1); + Invalidate(); + break; + case WIDX_BASE_HEIGHT_UP: + _baseHeight = std::min(_baseHeight + 2, BASESIZE_MAX); + Invalidate(); + break; + case WIDX_BASE_HEIGHT_DOWN: + _baseHeight = std::max(_baseHeight - 2, BASESIZE_MIN); + Invalidate(); + break; + case WIDX_WATER_LEVEL_UP: + _waterLevel = std::min(_waterLevel + 2, WATERLEVEL_MAX); + Invalidate(); + break; + case WIDX_WATER_LEVEL_DOWN: + _waterLevel = std::max(_waterLevel - 2, WATERLEVEL_MIN); + Invalidate(); + break; + case WIDX_FLOOR_TEXTURE: + LandTool::ShowSurfaceStyleDropdown(this, widget, _floorTexture); + break; + case WIDX_WALL_TEXTURE: + LandTool::ShowEdgeStyleDropdown(this, widget, _wallTexture); + break; + } } - DrawDropdownButton(dpi, floorWidgetIndex, surfaceImage); - DrawDropdownButton(dpi, edgeWidgetIndex, edgeImage); - } + void BaseDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + int32_t type; - void BaseDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - DrawDropdownButtons(dpi, WIDX_FLOOR_TEXTURE, WIDX_WALL_TEXTURE); + switch (widgetIndex) + { + case WIDX_FLOOR_TEXTURE: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; - const auto textColour = colours[1]; + type = (dropdownIndex == -1) ? _floorTexture : dropdownIndex; - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_BASE_HEIGHT].top + 1 }, STR_BASE_HEIGHT_LABEL, {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_FLOOR_TEXTURE].top + 1 }, STR_TERRAIN_LABEL, {}, { textColour }); + if (gLandToolTerrainSurface == type) + { + gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainSurface = type; + _floorTexture = type; + } + Invalidate(); + break; + case WIDX_WALL_TEXTURE: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; - auto ft = Formatter(); - ft.Add((_baseHeight - 12) / 2); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_BASE_HEIGHT].left + 1, widgets[WIDX_BASE_HEIGHT].top + 1 }, - STR_COMMA16, ft, { colours[1] }); + type = (dropdownIndex == -1) ? _wallTexture : dropdownIndex; - ft = Formatter(); - ft.Add((_waterLevel - 12) / 2); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_WATER_LEVEL].left + 1, widgets[WIDX_WATER_LEVEL].top + 1 }, - STR_COMMA16, ft, { colours[1] }); - } + if (gLandToolTerrainEdge == type) + { + gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainEdge = type; + _wallTexture = type; + } + Invalidate(); + break; + } + } + + void BaseUpdate() + { + // Tab animation + if (++frame_no >= TabAnimationLoops[page]) + frame_no = 0; + InvalidateWidget(WIDX_TAB_1); + } + + void TextInput(WidgetIndex widgetIndex, std::string_view text) + { + int32_t value; + char* end; + + const auto strText = u8string(text); + value = strtol(strText.c_str(), &end, 10); + + if (*end != '\0') + { + return; + } + + switch (widgetIndex) + { + case WIDX_MAP_SIZE_Y: + case WIDX_MAP_SIZE_X: + case WIDX_SIMPLEX_MAP_SIZE_Y: + case WIDX_SIMPLEX_MAP_SIZE_X: + // The practical size is 2 lower than the technical size + value += 2; + if (_resizeDirection == ResizeDirection::Y || _mapWidthAndHeightLinked) + _mapSize.y = value; + if (_resizeDirection == ResizeDirection::X || _mapWidthAndHeightLinked) + _mapSize.x = value; + break; + case WIDX_BASE_HEIGHT: + _baseHeight = std::clamp((value * 2) + 12, BASESIZE_MIN, BASESIZE_MAX); + break; + case WIDX_WATER_LEVEL: + _waterLevel = std::clamp((value * 2) + 12, WATERLEVEL_MIN, WATERLEVEL_MAX); + break; + } + + Invalidate(); + } + + void BasePrepareDraw() + { + if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_BASE]) + { + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_BASE]; + InitScrollWidgets(); + } + + // Only allow linking the map size when X and Y are the same + SetWidgetPressed(WIDX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); + SetWidgetDisabled(WIDX_MAP_SIZE_LINK, _mapSize.x != _mapSize.y); + + SetPressedTab(); + + // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use + auto ft = Formatter::Common(); + ft.Add(_mapSize.y - 2); + ft.Add(_mapSize.x - 2); + } + + void DrawDropdownButton(DrawPixelInfo& dpi, WidgetIndex widgetIndex, ImageId image) + { + const auto& widget = widgets[widgetIndex]; + ScreenCoordsXY pos = { windowPos.x + widget.left, windowPos.y + widget.top }; + if (IsWidgetDisabled(widgetIndex)) + { + // Draw greyed out (light border bottom right shadow) + auto colour = colours[widget.colour]; + colour = ColourMapA[NOT_TRANSLUCENT(colour)].lighter; + GfxDrawSpriteSolid(&dpi, image, pos + ScreenCoordsXY{ 1, 1 }, colour); + + // Draw greyed out (dark) + colour = colours[widget.colour]; + colour = ColourMapA[NOT_TRANSLUCENT(colour)].mid_light; + GfxDrawSpriteSolid(&dpi, image, pos, colour); + } + else + { + GfxDrawSprite(dpi, image, pos); + } + } + + void DrawDropdownButtons(DrawPixelInfo& dpi, WidgetIndex floorWidgetIndex, WidgetIndex edgeWidgetIndex) + { + auto& objManager = GetContext()->GetObjectManager(); + const auto surfaceObj = static_cast( + objManager.GetLoadedObject(ObjectType::TerrainSurface, _floorTexture)); + ImageId surfaceImage; + if (surfaceObj != nullptr) + { + surfaceImage = ImageId(surfaceObj->IconImageId); + if (surfaceObj->Colour != 255) + { + surfaceImage = surfaceImage.WithPrimary(surfaceObj->Colour); + } + } + + ImageId edgeImage; + const auto edgeObj = static_cast( + objManager.GetLoadedObject(ObjectType::TerrainEdge, _wallTexture)); + if (edgeObj != nullptr) + { + edgeImage = ImageId(edgeObj->IconImageId); + } + + DrawDropdownButton(dpi, floorWidgetIndex, surfaceImage); + DrawDropdownButton(dpi, edgeWidgetIndex, edgeImage); + } + + void BaseDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + DrawDropdownButtons(dpi, WIDX_FLOOR_TEXTURE, WIDX_WALL_TEXTURE); + + const auto textColour = colours[1]; + + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_BASE_HEIGHT].top + 1 }, STR_BASE_HEIGHT_LABEL, {}, + { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, + { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_FLOOR_TEXTURE].top + 1 }, STR_TERRAIN_LABEL, {}, + { textColour }); + + auto ft = Formatter(); + ft.Add((_baseHeight - 12) / 2); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_BASE_HEIGHT].left + 1, widgets[WIDX_BASE_HEIGHT].top + 1 }, + STR_COMMA16, ft, { colours[1] }); + + ft = Formatter(); + ft.Add((_waterLevel - 12) / 2); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_WATER_LEVEL].left + 1, widgets[WIDX_WATER_LEVEL].top + 1 }, + STR_COMMA16, ft, { colours[1] }); + } #pragma endregion #pragma region Random page - void RandomMouseUp(WidgetIndex widgetIndex) - { - SharedMouseUp(widgetIndex); - - MapGenSettings mapgenSettings; - - switch (widgetIndex) + void RandomMouseUp(WidgetIndex widgetIndex) { - case WIDX_RANDOM_GENERATE: - mapgenSettings.mapSize = _mapSize; - mapgenSettings.height = _baseHeight + 2; - mapgenSettings.water_level = _waterLevel + 2; - mapgenSettings.floor = _randomTerrain ? -1 : _floorTexture; - mapgenSettings.wall = _randomTerrain ? -1 : _wallTexture; - mapgenSettings.trees = _placeTrees; + SharedMouseUp(widgetIndex); - mapgenSettings.simplex_low = UtilRand() % 4; - mapgenSettings.simplex_high = 12 + (UtilRand() % (32 - 12)); - mapgenSettings.simplex_base_freq = 1.75f; - mapgenSettings.simplex_octaves = 6; + MapGenSettings mapgenSettings; - MapGenGenerate(&mapgenSettings); - GfxInvalidateScreen(); - break; - case WIDX_RANDOM_TERRAIN: - _randomTerrain = !_randomTerrain; - break; - case WIDX_RANDOM_PLACE_TREES: - _placeTrees ^= 1; - break; - } - } + switch (widgetIndex) + { + case WIDX_RANDOM_GENERATE: + mapgenSettings.mapSize = _mapSize; + mapgenSettings.height = _baseHeight + 2; + mapgenSettings.water_level = _waterLevel + 2; + mapgenSettings.floor = _randomTerrain ? -1 : _floorTexture; + mapgenSettings.wall = _randomTerrain ? -1 : _wallTexture; + mapgenSettings.trees = _placeTrees; - void RandomUpdate() - { - // Tab animation - if (++frame_no >= TabAnimationLoops[page]) - frame_no = 0; - InvalidateWidget(WIDX_TAB_2); - } + mapgenSettings.simplex_low = UtilRand() % 4; + mapgenSettings.simplex_high = 12 + (UtilRand() % (32 - 12)); + mapgenSettings.simplex_base_freq = 1.75f; + mapgenSettings.simplex_octaves = 6; - void RandomPrepareDraw() - { - if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_RANDOM]) - { - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_RANDOM]; - InitScrollWidgets(); + MapGenGenerate(&mapgenSettings); + GfxInvalidateScreen(); + break; + case WIDX_RANDOM_TERRAIN: + _randomTerrain = !_randomTerrain; + break; + case WIDX_RANDOM_PLACE_TREES: + _placeTrees ^= 1; + break; + } } - pressed_widgets = 0; - if (_randomTerrain) - pressed_widgets |= 1uLL << WIDX_RANDOM_TERRAIN; - if (_placeTrees) - pressed_widgets |= 1uLL << WIDX_RANDOM_PLACE_TREES; + void RandomUpdate() + { + // Tab animation + if (++frame_no >= TabAnimationLoops[page]) + frame_no = 0; + InvalidateWidget(WIDX_TAB_2); + } - SetPressedTab(); - } + void RandomPrepareDraw() + { + if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_RANDOM]) + { + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_RANDOM]; + InitScrollWidgets(); + } - void RandomDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - } + pressed_widgets = 0; + if (_randomTerrain) + pressed_widgets |= 1uLL << WIDX_RANDOM_TERRAIN; + if (_placeTrees) + pressed_widgets |= 1uLL << WIDX_RANDOM_PLACE_TREES; + + SetPressedTab(); + } + + void RandomDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + } #pragma endregion #pragma region Simplex page - void SimplexMouseUp(WidgetIndex widgetIndex) - { - SharedMouseUp(widgetIndex); - - MapGenSettings mapgenSettings; - - switch (widgetIndex) + void SimplexMouseUp(WidgetIndex widgetIndex) { - case WIDX_SIMPLEX_MAP_SIZE_Y: - _resizeDirection = ResizeDirection::Y; - InputMapSize(WIDX_SIMPLEX_MAP_SIZE_Y, _mapSize.y); - break; - case WIDX_SIMPLEX_MAP_SIZE_X: - _resizeDirection = ResizeDirection::X; - InputMapSize(WIDX_SIMPLEX_MAP_SIZE_X, _mapSize.x); - break; - case WIDX_SIMPLEX_MAP_SIZE_LINK: - _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; - break; - case WIDX_SIMPLEX_GENERATE: - mapgenSettings.mapSize = _mapSize; + SharedMouseUp(widgetIndex); - mapgenSettings.height = _baseHeight; - mapgenSettings.water_level = _waterLevel + kMinimumWaterHeight; - mapgenSettings.floor = _randomTerrain ? -1 : _floorTexture; - mapgenSettings.wall = _randomTerrain ? -1 : _wallTexture; - mapgenSettings.trees = _placeTrees; + MapGenSettings mapgenSettings; - mapgenSettings.simplex_low = _simplex_low; - mapgenSettings.simplex_high = _simplex_high; - mapgenSettings.simplex_base_freq = (static_cast(_simplex_base_freq)) / 100.00f; - mapgenSettings.simplex_octaves = _simplex_octaves; + switch (widgetIndex) + { + case WIDX_SIMPLEX_MAP_SIZE_Y: + _resizeDirection = ResizeDirection::Y; + InputMapSize(WIDX_SIMPLEX_MAP_SIZE_Y, _mapSize.y); + break; + case WIDX_SIMPLEX_MAP_SIZE_X: + _resizeDirection = ResizeDirection::X; + InputMapSize(WIDX_SIMPLEX_MAP_SIZE_X, _mapSize.x); + break; + case WIDX_SIMPLEX_MAP_SIZE_LINK: + _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; + break; + case WIDX_SIMPLEX_GENERATE: + mapgenSettings.mapSize = _mapSize; - MapGenGenerate(&mapgenSettings); - GfxInvalidateScreen(); - break; - } - } + mapgenSettings.height = _baseHeight; + mapgenSettings.water_level = _waterLevel + kMinimumWaterHeight; + mapgenSettings.floor = _randomTerrain ? -1 : _floorTexture; + mapgenSettings.wall = _randomTerrain ? -1 : _wallTexture; + mapgenSettings.trees = _placeTrees; - void SimplexMouseDown(WidgetIndex widgetIndex, Widget* widget) - { - switch (widgetIndex) - { - case WIDX_SIMPLEX_LOW_UP: - _simplex_low = std::min(_simplex_low + 1, 24); - Invalidate(); - break; - case WIDX_SIMPLEX_LOW_DOWN: - _simplex_low = std::max(_simplex_low - 1, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_HIGH_UP: - _simplex_high = std::min(_simplex_high + 1, 36); - Invalidate(); - break; - case WIDX_SIMPLEX_HIGH_DOWN: - _simplex_high = std::max(_simplex_high - 1, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_BASE_FREQ_UP: - _simplex_base_freq = std::min(_simplex_base_freq + 5, 1000); - Invalidate(); - break; - case WIDX_SIMPLEX_BASE_FREQ_DOWN: - _simplex_base_freq = std::max(_simplex_base_freq - 5, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_OCTAVES_UP: - _simplex_octaves = std::min(_simplex_octaves + 1, 10); - Invalidate(); - break; - case WIDX_SIMPLEX_OCTAVES_DOWN: - _simplex_octaves = std::max(_simplex_octaves - 1, 1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_Y_UP: - _resizeDirection = ResizeDirection::Y; - ChangeMapSize(+1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_Y_DOWN: - _resizeDirection = ResizeDirection::Y; - ChangeMapSize(-1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_X_UP: - _resizeDirection = ResizeDirection::X; - ChangeMapSize(+1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_X_DOWN: - _resizeDirection = ResizeDirection::X; - ChangeMapSize(-1); - Invalidate(); - break; - case WIDX_SIMPLEX_WATER_LEVEL_UP: - _waterLevel = std::min(_waterLevel + kMinimumWaterHeight, kMinimumWaterHeight + kMaximumWaterHeight); - Invalidate(); - break; - case WIDX_SIMPLEX_WATER_LEVEL_DOWN: - _waterLevel = std::max(_waterLevel - kMinimumWaterHeight, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX: - _randomTerrain = !_randomTerrain; - Invalidate(); - break; - case WIDX_SIMPLEX_FLOOR_TEXTURE: - LandTool::ShowSurfaceStyleDropdown(this, widget, _floorTexture); - break; - case WIDX_SIMPLEX_WALL_TEXTURE: - LandTool::ShowEdgeStyleDropdown(this, widget, _wallTexture); - break; - case WIDX_SIMPLEX_PLACE_TREES_CHECKBOX: - _placeTrees ^= 1; - Invalidate(); - break; - } - } + mapgenSettings.simplex_low = _simplex_low; + mapgenSettings.simplex_high = _simplex_high; + mapgenSettings.simplex_base_freq = (static_cast(_simplex_base_freq)) / 100.00f; + mapgenSettings.simplex_octaves = _simplex_octaves; - void SimplexDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - int32_t type; - - switch (widgetIndex) - { - case WIDX_SIMPLEX_FLOOR_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _floorTexture : dropdownIndex; - - if (gLandToolTerrainSurface == type) - { - gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainSurface = type; - _floorTexture = type; - } - Invalidate(); - break; - case WIDX_SIMPLEX_WALL_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _wallTexture : dropdownIndex; - - if (gLandToolTerrainEdge == type) - { - gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainEdge = type; - _wallTexture = type; - } - Invalidate(); - break; - } - } - - void SimplexUpdate() - { - // Tab animation - if (++frame_no >= TabAnimationLoops[page]) - frame_no = 0; - InvalidateWidget(WIDX_TAB_3); - } - - void SimplexPrepareDraw() - { - if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_SIMPLEX]) - { - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_SIMPLEX]; - InitScrollWidgets(); + MapGenGenerate(&mapgenSettings); + GfxInvalidateScreen(); + break; + } } - // Only allow linking the map size when X and Y are the same - SetWidgetPressed(WIDX_SIMPLEX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); - SetWidgetDisabled(WIDX_SIMPLEX_MAP_SIZE_LINK, _mapSize.x != _mapSize.y); - - SetCheckboxValue(WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX, _randomTerrain != 0); - SetCheckboxValue(WIDX_SIMPLEX_PLACE_TREES_CHECKBOX, _placeTrees != 0); - - // Only allow floor and wall texture options if random terrain is disabled - if (!_randomTerrain) + void SimplexMouseDown(WidgetIndex widgetIndex, Widget* widget) { - SetWidgetEnabled(WIDX_SIMPLEX_FLOOR_TEXTURE, true); - SetWidgetEnabled(WIDX_SIMPLEX_WALL_TEXTURE, true); - } - else - { - SetWidgetEnabled(WIDX_SIMPLEX_FLOOR_TEXTURE, false); - SetWidgetEnabled(WIDX_SIMPLEX_WALL_TEXTURE, false); + switch (widgetIndex) + { + case WIDX_SIMPLEX_LOW_UP: + _simplex_low = std::min(_simplex_low + 1, 24); + Invalidate(); + break; + case WIDX_SIMPLEX_LOW_DOWN: + _simplex_low = std::max(_simplex_low - 1, 0); + Invalidate(); + break; + case WIDX_SIMPLEX_HIGH_UP: + _simplex_high = std::min(_simplex_high + 1, 36); + Invalidate(); + break; + case WIDX_SIMPLEX_HIGH_DOWN: + _simplex_high = std::max(_simplex_high - 1, 0); + Invalidate(); + break; + case WIDX_SIMPLEX_BASE_FREQ_UP: + _simplex_base_freq = std::min(_simplex_base_freq + 5, 1000); + Invalidate(); + break; + case WIDX_SIMPLEX_BASE_FREQ_DOWN: + _simplex_base_freq = std::max(_simplex_base_freq - 5, 0); + Invalidate(); + break; + case WIDX_SIMPLEX_OCTAVES_UP: + _simplex_octaves = std::min(_simplex_octaves + 1, 10); + Invalidate(); + break; + case WIDX_SIMPLEX_OCTAVES_DOWN: + _simplex_octaves = std::max(_simplex_octaves - 1, 1); + Invalidate(); + break; + case WIDX_SIMPLEX_MAP_SIZE_Y_UP: + _resizeDirection = ResizeDirection::Y; + ChangeMapSize(+1); + Invalidate(); + break; + case WIDX_SIMPLEX_MAP_SIZE_Y_DOWN: + _resizeDirection = ResizeDirection::Y; + ChangeMapSize(-1); + Invalidate(); + break; + case WIDX_SIMPLEX_MAP_SIZE_X_UP: + _resizeDirection = ResizeDirection::X; + ChangeMapSize(+1); + Invalidate(); + break; + case WIDX_SIMPLEX_MAP_SIZE_X_DOWN: + _resizeDirection = ResizeDirection::X; + ChangeMapSize(-1); + Invalidate(); + break; + case WIDX_SIMPLEX_WATER_LEVEL_UP: + _waterLevel = std::min(_waterLevel + kMinimumWaterHeight, kMinimumWaterHeight + kMaximumWaterHeight); + Invalidate(); + break; + case WIDX_SIMPLEX_WATER_LEVEL_DOWN: + _waterLevel = std::max(_waterLevel - kMinimumWaterHeight, 0); + Invalidate(); + break; + case WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX: + _randomTerrain = !_randomTerrain; + Invalidate(); + break; + case WIDX_SIMPLEX_FLOOR_TEXTURE: + LandTool::ShowSurfaceStyleDropdown(this, widget, _floorTexture); + break; + case WIDX_SIMPLEX_WALL_TEXTURE: + LandTool::ShowEdgeStyleDropdown(this, widget, _wallTexture); + break; + case WIDX_SIMPLEX_PLACE_TREES_CHECKBOX: + _placeTrees ^= 1; + Invalidate(); + break; + } } - SetPressedTab(); + void SimplexDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + int32_t type; - // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use - auto ft = Formatter::Common(); - ft.Add(_mapSize.y - 2); - ft.Add(_mapSize.x - 2); - } + switch (widgetIndex) + { + case WIDX_SIMPLEX_FLOOR_TEXTURE: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; - void SimplexDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - DrawDropdownButtons(dpi, WIDX_SIMPLEX_FLOOR_TEXTURE, WIDX_SIMPLEX_WALL_TEXTURE); + type = (dropdownIndex == -1) ? _floorTexture : dropdownIndex; - const uint8_t textColour = colours[1]; + if (gLandToolTerrainSurface == type) + { + gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainSurface = type; + _floorTexture = type; + } + Invalidate(); + break; + case WIDX_SIMPLEX_WALL_TEXTURE: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_LOW].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_LOW_, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_HIGH].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_HIGH, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, - STR_MAPGEN_SIMPLEX_NOISE_BASE_FREQUENCY, {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_OCTAVES, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, - { textColour }); + type = (dropdownIndex == -1) ? _wallTexture : dropdownIndex; - auto ft = Formatter(); - ft.Add(_simplex_low); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_LOW].left + 1, widgets[WIDX_SIMPLEX_LOW].top + 1 }, - STR_COMMA16, ft, { textColour }); - ft = Formatter(); - ft.Add(_simplex_high); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_HIGH].left + 1, widgets[WIDX_SIMPLEX_HIGH].top + 1 }, - STR_COMMA16, ft, { textColour }); - ft = Formatter(); - ft.Add(_simplex_base_freq); - DrawTextBasic( - dpi, - windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_BASE_FREQ].left + 1, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, - STR_WINDOW_COLOUR_2_COMMA2DP32, ft, { textColour }); - ft = Formatter(); - ft.Add(_simplex_octaves); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_OCTAVES].left + 1, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, - STR_COMMA16, ft, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX].top + 1 }, STR_TERRAIN_LABEL, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_PLACE_TREES_CHECKBOX].top + 1 }, - STR_MAPGEN_OPTION_PLACE_TREES, {}, { textColour }); + if (gLandToolTerrainEdge == type) + { + gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainEdge = type; + _wallTexture = type; + } + Invalidate(); + break; + } + } - ft = Formatter(); - ft.Add((_waterLevel - 12) / 2); - DrawTextBasic( - dpi, - windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_WATER_LEVEL].left + 1, widgets[WIDX_SIMPLEX_WATER_LEVEL].top + 1 }, - STR_COMMA16, ft, { textColour }); - } + void SimplexUpdate() + { + // Tab animation + if (++frame_no >= TabAnimationLoops[page]) + frame_no = 0; + InvalidateWidget(WIDX_TAB_3); + } + + void SimplexPrepareDraw() + { + if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_SIMPLEX]) + { + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_SIMPLEX]; + InitScrollWidgets(); + } + + // Only allow linking the map size when X and Y are the same + SetWidgetPressed(WIDX_SIMPLEX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); + SetWidgetDisabled(WIDX_SIMPLEX_MAP_SIZE_LINK, _mapSize.x != _mapSize.y); + + SetCheckboxValue(WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX, _randomTerrain != 0); + SetCheckboxValue(WIDX_SIMPLEX_PLACE_TREES_CHECKBOX, _placeTrees != 0); + + // Only allow floor and wall texture options if random terrain is disabled + if (!_randomTerrain) + { + SetWidgetEnabled(WIDX_SIMPLEX_FLOOR_TEXTURE, true); + SetWidgetEnabled(WIDX_SIMPLEX_WALL_TEXTURE, true); + } + else + { + SetWidgetEnabled(WIDX_SIMPLEX_FLOOR_TEXTURE, false); + SetWidgetEnabled(WIDX_SIMPLEX_WALL_TEXTURE, false); + } + + SetPressedTab(); + + // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use + auto ft = Formatter::Common(); + ft.Add(_mapSize.y - 2); + ft.Add(_mapSize.x - 2); + } + + void SimplexDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + DrawDropdownButtons(dpi, WIDX_SIMPLEX_FLOOR_TEXTURE, WIDX_SIMPLEX_WALL_TEXTURE); + + const uint8_t textColour = colours[1]; + + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_LOW].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_LOW_, {}, + { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_HIGH].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_HIGH, {}, + { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, + STR_MAPGEN_SIMPLEX_NOISE_BASE_FREQUENCY, {}, { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_OCTAVES, + {}, { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, + { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, + { textColour }); + + auto ft = Formatter(); + ft.Add(_simplex_low); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_LOW].left + 1, widgets[WIDX_SIMPLEX_LOW].top + 1 }, + STR_COMMA16, ft, { textColour }); + ft = Formatter(); + ft.Add(_simplex_high); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_HIGH].left + 1, widgets[WIDX_SIMPLEX_HIGH].top + 1 }, + STR_COMMA16, ft, { textColour }); + ft = Formatter(); + ft.Add(_simplex_base_freq); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_BASE_FREQ].left + 1, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, + STR_WINDOW_COLOUR_2_COMMA2DP32, ft, { textColour }); + ft = Formatter(); + ft.Add(_simplex_octaves); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_OCTAVES].left + 1, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, + STR_COMMA16, ft, { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX].top + 1 }, STR_TERRAIN_LABEL, + {}, { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_PLACE_TREES_CHECKBOX].top + 1 }, + STR_MAPGEN_OPTION_PLACE_TREES, {}, { textColour }); + + ft = Formatter(); + ft.Add((_waterLevel - 12) / 2); + DrawTextBasic( + dpi, + windowPos + + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_WATER_LEVEL].left + 1, widgets[WIDX_SIMPLEX_WATER_LEVEL].top + 1 }, + STR_COMMA16, ft, { textColour }); + } #pragma endregion #pragma region Heightmap page - void HeightmapMouseDown(WidgetIndex widgetIndex, Widget* widget) - { - switch (widgetIndex) + void HeightmapMouseDown(WidgetIndex widgetIndex, Widget* widget) { - case WIDX_HEIGHTMAP_STRENGTH_UP: - _heightmapSmoothStrength = std::min(_heightmapSmoothStrength + 1, MAX_SMOOTH_ITERATIONS); - InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); - break; - case WIDX_HEIGHTMAP_STRENGTH_DOWN: - _heightmapSmoothStrength = std::max(_heightmapSmoothStrength - 1, 1); - InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); - break; - case WIDX_HEIGHTMAP_LOW_UP: - _heightmapLow = std::min(_heightmapLow + 1, 142 - 1); - _heightmapHigh = std::max(_heightmapHigh, _heightmapLow + 1); - InvalidateWidget(WIDX_HEIGHTMAP_LOW); - break; - case WIDX_HEIGHTMAP_LOW_DOWN: - _heightmapLow = std::max(_heightmapLow - 1, 2); - InvalidateWidget(WIDX_HEIGHTMAP_LOW); - break; - case WIDX_HEIGHTMAP_HIGH_UP: - _heightmapHigh = std::min(_heightmapHigh + 1, 142); - InvalidateWidget(WIDX_HEIGHTMAP_HIGH); - break; - case WIDX_HEIGHTMAP_HIGH_DOWN: - _heightmapHigh = std::max(_heightmapHigh - 1, 2 + 1); - _heightmapLow = std::min(_heightmapLow, _heightmapHigh - 1); - InvalidateWidget(WIDX_HEIGHTMAP_HIGH); - break; - case WIDX_HEIGHTMAP_WATER_LEVEL_UP: - _waterLevel = std::min(_waterLevel + kMinimumWaterHeight, kMinimumWaterHeight + kMaximumWaterHeight); - InvalidateWidget(WIDX_HEIGHTMAP_WATER_LEVEL); - break; - case WIDX_HEIGHTMAP_WATER_LEVEL_DOWN: - _waterLevel = std::max(_waterLevel - kMinimumWaterHeight, 0); - InvalidateWidget(WIDX_HEIGHTMAP_WATER_LEVEL); - break; - } - } - - void HeightmapGenerateMap() - { - MapGenSettings mapgenSettings; - mapgenSettings.water_level = _waterLevel; - mapgenSettings.smooth = _heightmapSmoothTiles; - mapgenSettings.smooth_height_map = _heightmapSmoothMap; - mapgenSettings.smooth_strength = _heightmapSmoothStrength; - mapgenSettings.normalize_height = _heightmapNormalize; - mapgenSettings.simplex_low = _heightmapLow; - mapgenSettings.simplex_high = _heightmapHigh; - MapGenGenerateFromHeightmap(&mapgenSettings); - GfxInvalidateScreen(); - } - - void HeightmapMouseUp(WidgetIndex widgetIndex) - { - SharedMouseUp(widgetIndex); - - switch (widgetIndex) - { - case WIDX_CLOSE: - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - return; // Only widgets that change a setting need to regenerate the map - - // Page widgets - case WIDX_HEIGHTMAP_SELECT: + switch (widgetIndex) { - auto intent = Intent(WindowClass::Loadsave); - intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_HEIGHTMAP); - intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(HeightmapLoadsaveCallback)); - ContextOpenIntent(&intent); - return; + case WIDX_HEIGHTMAP_STRENGTH_UP: + _heightmapSmoothStrength = std::min(_heightmapSmoothStrength + 1, MAX_SMOOTH_ITERATIONS); + InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); + break; + case WIDX_HEIGHTMAP_STRENGTH_DOWN: + _heightmapSmoothStrength = std::max(_heightmapSmoothStrength - 1, 1); + InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); + break; + case WIDX_HEIGHTMAP_LOW_UP: + _heightmapLow = std::min(_heightmapLow + 1, 142 - 1); + _heightmapHigh = std::max(_heightmapHigh, _heightmapLow + 1); + InvalidateWidget(WIDX_HEIGHTMAP_LOW); + break; + case WIDX_HEIGHTMAP_LOW_DOWN: + _heightmapLow = std::max(_heightmapLow - 1, 2); + InvalidateWidget(WIDX_HEIGHTMAP_LOW); + break; + case WIDX_HEIGHTMAP_HIGH_UP: + _heightmapHigh = std::min(_heightmapHigh + 1, 142); + InvalidateWidget(WIDX_HEIGHTMAP_HIGH); + break; + case WIDX_HEIGHTMAP_HIGH_DOWN: + _heightmapHigh = std::max(_heightmapHigh - 1, 2 + 1); + _heightmapLow = std::min(_heightmapLow, _heightmapHigh - 1); + InvalidateWidget(WIDX_HEIGHTMAP_HIGH); + break; + case WIDX_HEIGHTMAP_WATER_LEVEL_UP: + _waterLevel = std::min(_waterLevel + kMinimumWaterHeight, kMinimumWaterHeight + kMaximumWaterHeight); + InvalidateWidget(WIDX_HEIGHTMAP_WATER_LEVEL); + break; + case WIDX_HEIGHTMAP_WATER_LEVEL_DOWN: + _waterLevel = std::max(_waterLevel - kMinimumWaterHeight, 0); + InvalidateWidget(WIDX_HEIGHTMAP_WATER_LEVEL); + break; } - case WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP: - _heightmapSmoothMap = !_heightmapSmoothMap; - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapSmoothMap); - InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP); - InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); - break; - case WIDX_HEIGHTMAP_NORMALIZE: - _heightmapNormalize = !_heightmapNormalize; - SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _heightmapNormalize); - InvalidateWidget(WIDX_HEIGHTMAP_NORMALIZE); - break; - case WIDX_HEIGHTMAP_SMOOTH_TILES: - _heightmapSmoothTiles = !_heightmapSmoothTiles; - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILES, _heightmapSmoothTiles); - InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_TILES); - break; } - // Always regenerate the map after one of the page widgets has been changed - HeightmapGenerateMap(); - } - - void HeightmapPrepareDraw() - { - if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_HEIGHTMAP]) + void HeightmapGenerateMap() { - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_HEIGHTMAP]; - InitScrollWidgets(); + MapGenSettings mapgenSettings; + mapgenSettings.water_level = _waterLevel; + mapgenSettings.smooth = _heightmapSmoothTiles; + mapgenSettings.smooth_height_map = _heightmapSmoothMap; + mapgenSettings.smooth_strength = _heightmapSmoothStrength; + mapgenSettings.normalize_height = _heightmapNormalize; + mapgenSettings.simplex_low = _heightmapLow; + mapgenSettings.simplex_high = _heightmapHigh; + MapGenGenerateFromHeightmap(&mapgenSettings); + GfxInvalidateScreen(); } - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapSmoothMap); - SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _heightmapNormalize); - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILES, _heightmapSmoothTiles); + void HeightmapMouseUp(WidgetIndex widgetIndex) + { + SharedMouseUp(widgetIndex); - SetPressedTab(); - } + switch (widgetIndex) + { + case WIDX_CLOSE: + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + case WIDX_TAB_4: + return; // Only widgets that change a setting need to regenerate the map - void HeightmapDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); + // Page widgets + case WIDX_HEIGHTMAP_SELECT: + { + auto intent = Intent(WindowClass::Loadsave); + intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_HEIGHTMAP); + intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(HeightmapLoadsaveCallback)); + ContextOpenIntent(&intent); + return; + } + case WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP: + _heightmapSmoothMap = !_heightmapSmoothMap; + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapSmoothMap); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _heightmapSmoothMap); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapSmoothMap); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapSmoothMap); + InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP); + InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); + break; + case WIDX_HEIGHTMAP_NORMALIZE: + _heightmapNormalize = !_heightmapNormalize; + SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _heightmapNormalize); + InvalidateWidget(WIDX_HEIGHTMAP_NORMALIZE); + break; + case WIDX_HEIGHTMAP_SMOOTH_TILES: + _heightmapSmoothTiles = !_heightmapSmoothTiles; + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILES, _heightmapSmoothTiles); + InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_TILES); + break; + } - const colour_t enabledColour = colours[1]; - const colour_t disabledColour = enabledColour | COLOUR_FLAG_INSET; + // Always regenerate the map after one of the page widgets has been changed + HeightmapGenerateMap(); + } - // Smooth strength label and value - const colour_t strengthColour = _heightmapSmoothMap ? enabledColour : disabledColour; - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, STR_MAPGEN_SMOOTH_STRENGTH, {}, - { strengthColour }); + void HeightmapPrepareDraw() + { + if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_HEIGHTMAP]) + { + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_HEIGHTMAP]; + InitScrollWidgets(); + } - auto ft = Formatter(); - ft.Add(_heightmapSmoothStrength); - DrawTextBasic( - dpi, - windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_STRENGTH].left + 1, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, - STR_COMMA16, ft, { strengthColour }); + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapSmoothMap); + SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _heightmapNormalize); + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILES, _heightmapSmoothTiles); - // Low label and value - const colour_t labelColour = _heightmapLoaded ? enabledColour : disabledColour; - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_LOW_, {}, - { labelColour }); + SetPressedTab(); + } - ft = Formatter(); - ft.Add(_heightmapLow); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_LOW].left + 1, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, - STR_COMMA16, ft, { labelColour }); + void HeightmapDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); - // High label and value - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_HIGH, {}, - { labelColour }); + const colour_t enabledColour = colours[1]; + const colour_t disabledColour = enabledColour | COLOUR_FLAG_INSET; - ft = Formatter(); - ft.Add(_heightmapHigh); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_HIGH].left + 1, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, - STR_COMMA16, ft, { labelColour }); + // Smooth strength label and value + const colour_t strengthColour = _heightmapSmoothMap ? enabledColour : disabledColour; + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, STR_MAPGEN_SMOOTH_STRENGTH, {}, + { strengthColour }); - // Water level label and value - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, - { labelColour }); + auto ft = Formatter(); + ft.Add(_heightmapSmoothStrength); + DrawTextBasic( + dpi, + windowPos + + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_STRENGTH].left + 1, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, + STR_COMMA16, ft, { strengthColour }); - ft = Formatter(); - ft.Add(_waterLevel); - DrawTextBasic( - dpi, - windowPos - + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_WATER_LEVEL].left + 1, widgets[WIDX_HEIGHTMAP_WATER_LEVEL].top + 1 }, - STR_COMMA16, ft, { labelColour }); - } + // Low label and value + const colour_t labelColour = _heightmapLoaded ? enabledColour : disabledColour; + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_LOW_, {}, + { labelColour }); + + ft = Formatter(); + ft.Add(_heightmapLow); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_LOW].left + 1, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, + STR_COMMA16, ft, { labelColour }); + + // High label and value + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_HIGH, {}, + { labelColour }); + + ft = Formatter(); + ft.Add(_heightmapHigh); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_HIGH].left + 1, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, + STR_COMMA16, ft, { labelColour }); + + // Water level label and value + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, + { labelColour }); + + ft = Formatter(); + ft.Add(_waterLevel); + DrawTextBasic( + dpi, + windowPos + + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_WATER_LEVEL].left + 1, + widgets[WIDX_HEIGHTMAP_WATER_LEVEL].top + 1 }, + STR_COMMA16, ft, { labelColour }); + } #pragma endregion -public: - void OnOpen() override - { - number = 0; - frame_no = 0; - - page = WINDOW_MAPGEN_PAGE_BASE; - Invalidate(); - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_BASE]; - hold_down_widgets = HoldDownWidgets[WINDOW_MAPGEN_PAGE_BASE]; - pressed_widgets = PressedWidgets[WINDOW_MAPGEN_PAGE_BASE]; - disabled_widgets = PageDisabledWidgets[WINDOW_MAPGEN_PAGE_BASE]; - InitScrollWidgets(); - - _heightmapLoaded = false; - } - - void OnClose() override - { - MapGenUnloadHeightmap(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (page) + public: + void OnOpen() override { - case WINDOW_MAPGEN_PAGE_BASE: - return BaseMouseUp(widgetIndex); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomMouseUp(widgetIndex); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexMouseUp(widgetIndex); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapMouseUp(widgetIndex); + number = 0; + frame_no = 0; + + page = WINDOW_MAPGEN_PAGE_BASE; + Invalidate(); + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_BASE]; + hold_down_widgets = HoldDownWidgets[WINDOW_MAPGEN_PAGE_BASE]; + pressed_widgets = PressedWidgets[WINDOW_MAPGEN_PAGE_BASE]; + disabled_widgets = PageDisabledWidgets[WINDOW_MAPGEN_PAGE_BASE]; + InitScrollWidgets(); + + _heightmapLoaded = false; } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (page) + void OnClose() override { - case WINDOW_MAPGEN_PAGE_BASE: - return BaseMouseDown(widgetIndex, &widgets[widgetIndex]); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexMouseDown(widgetIndex, &widgets[widgetIndex]); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapMouseDown(widgetIndex, &widgets[widgetIndex]); + MapGenUnloadHeightmap(); } - } - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - switch (page) + void OnMouseUp(WidgetIndex widgetIndex) override { - case WINDOW_MAPGEN_PAGE_BASE: - return BaseDropdown(widgetIndex, selectedIndex); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexDropdown(widgetIndex, selectedIndex); - } - } - - void OnUpdate() override - { - switch (page) - { - case WINDOW_MAPGEN_PAGE_BASE: - return BaseUpdate(); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomUpdate(); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexUpdate(); - } - } - - void OnPrepareDraw() override - { - switch (page) - { - case WINDOW_MAPGEN_PAGE_BASE: - return BasePrepareDraw(); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomPrepareDraw(); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexPrepareDraw(); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapPrepareDraw(); - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - switch (page) - { - case WINDOW_MAPGEN_PAGE_BASE: - return BaseDraw(dpi); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomDraw(dpi); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexDraw(dpi); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapDraw(dpi); - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - switch (page) - { - case WINDOW_MAPGEN_PAGE_BASE: - return TextInput(widgetIndex, text); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return TextInput(widgetIndex, text); - } - } - - void AfterLoadingHeightMap(int32_t result, const utf8* path) - { - if (result == MODAL_RESULT_OK) - { - if (!MapGenLoadHeightmap(path)) + switch (page) { - // TODO: Display error popup - return; + case WINDOW_MAPGEN_PAGE_BASE: + return BaseMouseUp(widgetIndex); + case WINDOW_MAPGEN_PAGE_RANDOM: + return RandomMouseUp(widgetIndex); + case WINDOW_MAPGEN_PAGE_SIMPLEX: + return SimplexMouseUp(widgetIndex); + case WINDOW_MAPGEN_PAGE_HEIGHTMAP: + return HeightmapMouseUp(widgetIndex); } - - // The window needs to be open while using the map - _heightmapLoaded = true; - SetPage(WINDOW_MAPGEN_PAGE_HEIGHTMAP); - - HeightmapGenerateMap(); } - } - void OnResize() override + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (page) + { + case WINDOW_MAPGEN_PAGE_BASE: + return BaseMouseDown(widgetIndex, &widgets[widgetIndex]); + case WINDOW_MAPGEN_PAGE_SIMPLEX: + return SimplexMouseDown(widgetIndex, &widgets[widgetIndex]); + case WINDOW_MAPGEN_PAGE_HEIGHTMAP: + return HeightmapMouseDown(widgetIndex, &widgets[widgetIndex]); + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + switch (page) + { + case WINDOW_MAPGEN_PAGE_BASE: + return BaseDropdown(widgetIndex, selectedIndex); + case WINDOW_MAPGEN_PAGE_SIMPLEX: + return SimplexDropdown(widgetIndex, selectedIndex); + } + } + + void OnUpdate() override + { + switch (page) + { + case WINDOW_MAPGEN_PAGE_BASE: + return BaseUpdate(); + case WINDOW_MAPGEN_PAGE_RANDOM: + return RandomUpdate(); + case WINDOW_MAPGEN_PAGE_SIMPLEX: + return SimplexUpdate(); + } + } + + void OnPrepareDraw() override + { + switch (page) + { + case WINDOW_MAPGEN_PAGE_BASE: + return BasePrepareDraw(); + case WINDOW_MAPGEN_PAGE_RANDOM: + return RandomPrepareDraw(); + case WINDOW_MAPGEN_PAGE_SIMPLEX: + return SimplexPrepareDraw(); + case WINDOW_MAPGEN_PAGE_HEIGHTMAP: + return HeightmapPrepareDraw(); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + switch (page) + { + case WINDOW_MAPGEN_PAGE_BASE: + return BaseDraw(dpi); + case WINDOW_MAPGEN_PAGE_RANDOM: + return RandomDraw(dpi); + case WINDOW_MAPGEN_PAGE_SIMPLEX: + return SimplexDraw(dpi); + case WINDOW_MAPGEN_PAGE_HEIGHTMAP: + return HeightmapDraw(dpi); + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + switch (page) + { + case WINDOW_MAPGEN_PAGE_BASE: + return TextInput(widgetIndex, text); + case WINDOW_MAPGEN_PAGE_SIMPLEX: + return TextInput(widgetIndex, text); + } + } + + void AfterLoadingHeightMap(int32_t result, const utf8* path) + { + if (result == MODAL_RESULT_OK) + { + if (!MapGenLoadHeightmap(path)) + { + // TODO: Display error popup + return; + } + + // The window needs to be open while using the map + _heightmapLoaded = true; + SetPage(WINDOW_MAPGEN_PAGE_HEIGHTMAP); + + HeightmapGenerateMap(); + } + } + + void OnResize() override + { + ResizeFrameWithPage(); + } + }; + + WindowBase* WindowMapgenOpen() { - ResizeFrameWithPage(); + return WindowFocusOrCreate(WindowClass::Mapgen, WW, WH, WF_10 | WF_AUTO_POSITION | WF_CENTRE_SCREEN); } -}; -WindowBase* WindowMapgenOpen() -{ - return WindowFocusOrCreate(WindowClass::Mapgen, WW, WH, WF_10 | WF_AUTO_POSITION | WF_CENTRE_SCREEN); -} - -static void HeightmapLoadsaveCallback(int32_t result, const utf8* path) -{ - auto* w = static_cast(WindowMapgenOpen()); - w->AfterLoadingHeightMap(result, path); -} + static void HeightmapLoadsaveCallback(int32_t result, const utf8* path) + { + auto* w = static_cast(WindowMapgenOpen()); + w->AfterLoadingHeightMap(result, path); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/MapTooltip.cpp b/src/openrct2-ui/windows/MapTooltip.cpp index 0361c3e6b3..331df333f8 100644 --- a/src/openrct2-ui/windows/MapTooltip.cpp +++ b/src/openrct2-ui/windows/MapTooltip.cpp @@ -17,7 +17,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off static Widget window_map_tooltip_widgets[] = { MakeWidget({0, 0}, {200, 30}, WindowWidgetType::ImgBtn, WindowColour::Primary), kWidgetsEnd, @@ -26,105 +28,106 @@ static Widget window_map_tooltip_widgets[] = { // clang-format on #define MAP_TOOLTIP_ARGS -static ScreenCoordsXY _lastCursor; -static int32_t _cursorHoldDuration; + static ScreenCoordsXY _lastCursor; + static int32_t _cursorHoldDuration; -static void WindowMapTooltipOpen(); + static void WindowMapTooltipOpen(); -static Formatter _mapTooltipArgs; + static Formatter _mapTooltipArgs; -class MapTooltip final : public Window -{ -public: - void OnOpen() override + class MapTooltip final : public Window { - widgets = window_map_tooltip_widgets; - } - - void OnUpdate() override - { - Invalidate(); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - StringId stringId; - std::memcpy(&stringId, _mapTooltipArgs.Data(), sizeof(StringId)); - if (stringId == STR_NONE) + public: + void OnOpen() override { + widgets = window_map_tooltip_widgets; + } + + void OnUpdate() override + { + Invalidate(); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + StringId stringId; + std::memcpy(&stringId, _mapTooltipArgs.Data(), sizeof(StringId)); + if (stringId == STR_NONE) + { + return; + } + + auto stringCoords = windowPos + ScreenCoordsXY{ width / 2, height / 2 }; + DrawTextWrapped(dpi, stringCoords, width, STR_MAP_TOOLTIP_STRINGID, _mapTooltipArgs, { TextAlignment::CENTRE }); + } + }; + + void SetMapTooltip(Formatter& ft) + { + _mapTooltipArgs = ft; + } + + const Formatter& GetMapTooltip() + { + return _mapTooltipArgs; + } + + void WindowMapTooltipUpdateVisibility() + { + if (ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR) + { + // The map tooltip is drawn by the bottom toolbar + WindowInvalidateByClass(WindowClass::BottomToolbar); return; } - auto stringCoords = windowPos + ScreenCoordsXY{ width / 2, height / 2 }; - DrawTextWrapped(dpi, stringCoords, width, STR_MAP_TOOLTIP_STRINGID, _mapTooltipArgs, { TextAlignment::CENTRE }); + const CursorState* state = ContextGetCursorState(); + auto cursor = state->position; + auto cursorChange = cursor - _lastCursor; + + // Check for cursor movement + _cursorHoldDuration++; + if (abs(cursorChange.x) > 5 || abs(cursorChange.y) > 5 || (InputTestFlag(INPUT_FLAG_5)) + || InputGetState() == InputState::ViewportRight) + _cursorHoldDuration = 0; + + _lastCursor = cursor; + + // Show or hide tooltip + StringId stringId; + std::memcpy(&stringId, _mapTooltipArgs.Data(), sizeof(StringId)); + + if (_cursorHoldDuration < 25 || stringId == STR_NONE + || InputTestPlaceObjectModifier( + static_cast(PLACE_OBJECT_MODIFIER_COPY_Z | PLACE_OBJECT_MODIFIER_SHIFT_Z)) + || WindowFindByClass(WindowClass::Error) != nullptr) + { + WindowCloseByClass(WindowClass::MapTooltip); + } + else + { + WindowMapTooltipOpen(); + } } -}; -void SetMapTooltip(Formatter& ft) -{ - _mapTooltipArgs = ft; -} - -const Formatter& GetMapTooltip() -{ - return _mapTooltipArgs; -} - -void WindowMapTooltipUpdateVisibility() -{ - if (ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR) + static void WindowMapTooltipOpen() { - // The map tooltip is drawn by the bottom toolbar - WindowInvalidateByClass(WindowClass::BottomToolbar); - return; + constexpr int32_t width = 200; + constexpr int32_t height = 44; + const CursorState* state = ContextGetCursorState(); + auto pos = state->position + ScreenCoordsXY{ -width / 2, 15 }; + + if (auto w = WindowFindByClass(WindowClass::MapTooltip)) + { + w->Invalidate(); + w->windowPos = pos; + w->width = width; + w->height = height; + } + else + { + w = WindowCreate( + WindowClass::MapTooltip, pos, width, height, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); + } } - - const CursorState* state = ContextGetCursorState(); - auto cursor = state->position; - auto cursorChange = cursor - _lastCursor; - - // Check for cursor movement - _cursorHoldDuration++; - if (abs(cursorChange.x) > 5 || abs(cursorChange.y) > 5 || (InputTestFlag(INPUT_FLAG_5)) - || InputGetState() == InputState::ViewportRight) - _cursorHoldDuration = 0; - - _lastCursor = cursor; - - // Show or hide tooltip - StringId stringId; - std::memcpy(&stringId, _mapTooltipArgs.Data(), sizeof(StringId)); - - if (_cursorHoldDuration < 25 || stringId == STR_NONE - || InputTestPlaceObjectModifier( - static_cast(PLACE_OBJECT_MODIFIER_COPY_Z | PLACE_OBJECT_MODIFIER_SHIFT_Z)) - || WindowFindByClass(WindowClass::Error) != nullptr) - { - WindowCloseByClass(WindowClass::MapTooltip); - } - else - { - WindowMapTooltipOpen(); - } -} - -static void WindowMapTooltipOpen() -{ - constexpr int32_t width = 200; - constexpr int32_t height = 44; - const CursorState* state = ContextGetCursorState(); - auto pos = state->position + ScreenCoordsXY{ -width / 2, 15 }; - - if (auto w = WindowFindByClass(WindowClass::MapTooltip)) - { - w->Invalidate(); - w->windowPos = pos; - w->width = width; - w->height = height; - } - else - { - w = WindowCreate( - WindowClass::MapTooltip, pos, width, height, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/MazeConstruction.cpp b/src/openrct2-ui/windows/MazeConstruction.cpp index 48ba5c9832..34cde8c77c 100644 --- a/src/openrct2-ui/windows/MazeConstruction.cpp +++ b/src/openrct2-ui/windows/MazeConstruction.cpp @@ -26,13 +26,15 @@ #include #include +namespace OpenRCT2::Ui::Windows +{ #pragma region Widgets -static constexpr StringId WINDOW_TITLE = STR_RIDE_CONSTRUCTION_WINDOW_TITLE; -static constexpr int32_t WH = 200; -static constexpr int32_t WW = 166; + static constexpr StringId WINDOW_TITLE = STR_RIDE_CONSTRUCTION_WINDOW_TITLE; + static constexpr int32_t WH = 200; + static constexpr int32_t WW = 166; -// clang-format off + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -91,389 +93,391 @@ static Widget window_maze_construction_widgets[] = { }; #pragma endregion -// clang-format on + // clang-format on -class MazeConstructionWindow final : public Window -{ -public: - void OnOpen() override + class MazeConstructionWindow final : public Window { - widgets = window_maze_construction_widgets; - WindowInitScrollWidgets(*this); - rideId = _currentRideIndex; - ShowGridlines(); - } - - void OnClose() override - { - RideConstructionInvalidateCurrentTrack(); - ViewportSetVisibility(ViewportVisibility::Default); - - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - - // In order to cancel the yellow arrow correctly the - // selection tool should be cancelled. - ToolCancel(); - - HideGridlines(); - - auto currentRide = GetRide(_currentRideIndex); - if (currentRide != nullptr) + public: + void OnOpen() override { - if (currentRide->overall_view.IsNull()) - { - auto gameAction = RideDemolishAction(currentRide->id, RIDE_MODIFY_DEMOLISH); - gameAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); - GameActions::Execute(&gameAction); - } - else - { - auto intent = Intent(WindowClass::Ride); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, currentRide->id.ToUnderlying()); - ContextOpenIntent(&intent); - } - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_MAZE_ENTRANCE: - case WIDX_MAZE_EXIT: - WindowMazeConstructionEntranceMouseup(widgetIndex); - break; - case WIDX_MAZE_DIRECTION_NW: - case WIDX_MAZE_DIRECTION_NE: - case WIDX_MAZE_DIRECTION_SE: - case WIDX_MAZE_DIRECTION_SW: - WindowMazeConstructionConstruct(((widgetIndex - WIDX_MAZE_DIRECTION_NW) - GetCurrentRotation()) & 3); - break; - } - } - - void OnResize() override - { - ResizeFrameWithPage(); - uint64_t disabledWidgets = 0; - if (_rideConstructionState == RideConstructionState::Place) - { - disabledWidgets - |= ((1uLL << WIDX_MAZE_BUILD_MODE) | (1uLL << WIDX_MAZE_MOVE_MODE) | (1uLL << WIDX_MAZE_FILL_MODE) - | (1uLL << WIDX_MAZE_DIRECTION_NW) | (1uLL << WIDX_MAZE_DIRECTION_NE) | (1uLL << WIDX_MAZE_DIRECTION_SW) - | (1uLL << WIDX_MAZE_DIRECTION_SE)); - } - else if (_rideConstructionState == RideConstructionState::EntranceExit) - { - disabledWidgets = (1uLL << WIDX_MAZE_DIRECTION_NW) | (1uLL << WIDX_MAZE_DIRECTION_NE) - | (1uLL << WIDX_MAZE_DIRECTION_SW) | (1uLL << WIDX_MAZE_DIRECTION_SE); + widgets = window_maze_construction_widgets; + WindowInitScrollWidgets(*this); + rideId = _currentRideIndex; + ShowGridlines(); } - // Set and invalidate the changed widgets - uint64_t currentDisabledWidgets = disabled_widgets; - if (currentDisabledWidgets == disabledWidgets) - return; - - for (WidgetIndex i = 0; i < 64; i++) + void OnClose() override { - if ((disabledWidgets & (1uLL << i)) != (currentDisabledWidgets & (1uLL << i))) - { - WidgetInvalidate(*this, i); - } - } - disabled_widgets = disabledWidgets; - } + RideConstructionInvalidateCurrentTrack(); + ViewportSetVisibility(ViewportVisibility::Default); - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_MAZE_BUILD_MODE: - WindowMazeConstructionBuildModeMousedown(RideConstructionState::MazeBuild); - break; - case WIDX_MAZE_MOVE_MODE: - WindowMazeConstructionBuildModeMousedown(RideConstructionState::MazeMove); - break; - case WIDX_MAZE_FILL_MODE: - WindowMazeConstructionBuildModeMousedown(RideConstructionState::MazeFill); - break; - } - } + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - void OnUpdate() override - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr || currentRide->status != RideStatus::Closed) - { - Close(); - return; - } - - switch (_rideConstructionState) - { - case RideConstructionState::Place: - if (!WidgetIsActiveTool(*this, WIDX_MAZE_DIRECTION_GROUPBOX)) - { - Close(); - return; - } - break; - case RideConstructionState::EntranceExit: - if (!WidgetIsActiveTool(*this, WIDX_MAZE_ENTRANCE) && !WidgetIsActiveTool(*this, WIDX_MAZE_EXIT)) - { - _rideConstructionState = gRideEntranceExitPlacePreviousRideConstructionState; - WindowMazeConstructionUpdatePressedWidgets(); - } - break; - default: - break; - } - - switch (_rideConstructionState) - { - case RideConstructionState::Front: - case RideConstructionState::Back: - case RideConstructionState::Selected: - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - && gCurrentToolWidget.window_classification == WindowClass::RideConstruction) - { - ToolCancel(); - } - break; - default: - break; - } - UpdateGhostTrackAndArrow(); - } - - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_MAZE_DIRECTION_GROUPBOX: - RideConstructionToolupdateConstruct(screenCoords); - break; - case WIDX_MAZE_ENTRANCE: - case WIDX_MAZE_EXIT: - RideConstructionToolupdateEntranceExit(screenCoords); - break; - } - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_MAZE_DIRECTION_GROUPBOX: - RideConstructionTooldownConstruct(screenCoords); - break; - case WIDX_MAZE_ENTRANCE: - case WIDX_MAZE_EXIT: - WindowMazeConstructionEntranceTooldown(screenCoords); - break; - } - } - - void OnPrepareDraw() override - { - auto currentRide = GetRide(_currentRideIndex); - auto ft = Formatter::Common(); - if (currentRide != nullptr) - { - ft.Increment(4); - currentRide->FormatNameTo(ft); - } - else - { - ft.Increment(4); - ft.Add(STR_NONE); - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - } - -private: - void WindowMazeConstructionEntranceMouseup(WidgetIndex widgetIndex) - { - if (ToolSet(*this, widgetIndex, Tool::Crosshair)) - return; - - gRideEntranceExitPlaceType = widgetIndex == WIDX_MAZE_ENTRANCE ? ENTRANCE_TYPE_RIDE_ENTRANCE : ENTRANCE_TYPE_RIDE_EXIT; - gRideEntranceExitPlaceRideIndex = rideId; - gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0); - InputSetFlag(INPUT_FLAG_6, true); - - RideConstructionInvalidateCurrentTrack(); - - if (_rideConstructionState != RideConstructionState::EntranceExit) - { - gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState; - } - _rideConstructionState = RideConstructionState::EntranceExit; - - WindowMazeConstructionUpdatePressedWidgets(); - } - - void WindowMazeConstructionBuildModeMousedown(RideConstructionState rideConstructionState) - { - if (_rideConstructionState == RideConstructionState::EntranceExit) - { + // In order to cancel the yellow arrow correctly the + // selection tool should be cancelled. ToolCancel(); + + HideGridlines(); + + auto currentRide = GetRide(_currentRideIndex); + if (currentRide != nullptr) + { + if (currentRide->overall_view.IsNull()) + { + auto gameAction = RideDemolishAction(currentRide->id, RIDE_MODIFY_DEMOLISH); + gameAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); + GameActions::Execute(&gameAction); + } + else + { + auto intent = Intent(WindowClass::Ride); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, currentRide->id.ToUnderlying()); + ContextOpenIntent(&intent); + } + } } - _rideConstructionState = rideConstructionState; - WindowMazeConstructionUpdatePressedWidgets(); - } - void WindowMazeConstructionEntranceTooldown(const ScreenCoordsXY& screenCoords) - { - RideConstructionInvalidateCurrentTrack(); + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_MAZE_ENTRANCE: + case WIDX_MAZE_EXIT: + WindowMazeConstructionEntranceMouseup(widgetIndex); + break; + case WIDX_MAZE_DIRECTION_NW: + case WIDX_MAZE_DIRECTION_NE: + case WIDX_MAZE_DIRECTION_SE: + case WIDX_MAZE_DIRECTION_SW: + WindowMazeConstructionConstruct(((widgetIndex - WIDX_MAZE_DIRECTION_NW) - GetCurrentRotation()) & 3); + break; + } + } - MapInvalidateSelectionRect(); + void OnResize() override + { + ResizeFrameWithPage(); + uint64_t disabledWidgets = 0; + if (_rideConstructionState == RideConstructionState::Place) + { + disabledWidgets + |= ((1uLL << WIDX_MAZE_BUILD_MODE) | (1uLL << WIDX_MAZE_MOVE_MODE) | (1uLL << WIDX_MAZE_FILL_MODE) + | (1uLL << WIDX_MAZE_DIRECTION_NW) | (1uLL << WIDX_MAZE_DIRECTION_NE) | (1uLL << WIDX_MAZE_DIRECTION_SW) + | (1uLL << WIDX_MAZE_DIRECTION_SE)); + } + else if (_rideConstructionState == RideConstructionState::EntranceExit) + { + disabledWidgets = (1uLL << WIDX_MAZE_DIRECTION_NW) | (1uLL << WIDX_MAZE_DIRECTION_NE) + | (1uLL << WIDX_MAZE_DIRECTION_SW) | (1uLL << WIDX_MAZE_DIRECTION_SE); + } - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - - CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords); - if (entranceOrExitCoords.IsNull()) - return; - - if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION) - return; - - RideId rideIndex = gRideEntranceExitPlaceRideIndex; - - auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction( - entranceOrExitCoords, DirectionReverse(entranceOrExitCoords.direction), rideIndex, - gRideEntranceExitPlaceStationIndex, gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT); - - rideEntranceExitPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) + // Set and invalidate the changed widgets + uint64_t currentDisabledWidgets = disabled_widgets; + if (currentDisabledWidgets == disabledWidgets) return; - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + for (WidgetIndex i = 0; i < 64; i++) + { + if ((disabledWidgets & (1uLL << i)) != (currentDisabledWidgets & (1uLL << i))) + { + WidgetInvalidate(*this, i); + } + } + disabled_widgets = disabledWidgets; + } - auto currentRide = GetRide(rideIndex); - if (currentRide != nullptr && RideAreAllPossibleEntrancesAndExitsBuilt(*currentRide).Successful) + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_MAZE_BUILD_MODE: + WindowMazeConstructionBuildModeMousedown(RideConstructionState::MazeBuild); + break; + case WIDX_MAZE_MOVE_MODE: + WindowMazeConstructionBuildModeMousedown(RideConstructionState::MazeMove); + break; + case WIDX_MAZE_FILL_MODE: + WindowMazeConstructionBuildModeMousedown(RideConstructionState::MazeFill); + break; + } + } + + void OnUpdate() override + { + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr || currentRide->status != RideStatus::Closed) + { + Close(); + return; + } + + switch (_rideConstructionState) + { + case RideConstructionState::Place: + if (!WidgetIsActiveTool(*this, WIDX_MAZE_DIRECTION_GROUPBOX)) + { + Close(); + return; + } + break; + case RideConstructionState::EntranceExit: + if (!WidgetIsActiveTool(*this, WIDX_MAZE_ENTRANCE) && !WidgetIsActiveTool(*this, WIDX_MAZE_EXIT)) + { + _rideConstructionState = gRideEntranceExitPlacePreviousRideConstructionState; + WindowMazeConstructionUpdatePressedWidgets(); + } + break; + default: + break; + } + + switch (_rideConstructionState) + { + case RideConstructionState::Front: + case RideConstructionState::Back: + case RideConstructionState::Selected: + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + && gCurrentToolWidget.window_classification == WindowClass::RideConstruction) + { + ToolCancel(); + } + break; + default: + break; + } + UpdateGhostTrackAndArrow(); + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (widgetIndex) + { + case WIDX_MAZE_DIRECTION_GROUPBOX: + RideConstructionToolupdateConstruct(screenCoords); + break; + case WIDX_MAZE_ENTRANCE: + case WIDX_MAZE_EXIT: + RideConstructionToolupdateEntranceExit(screenCoords); + break; + } + } + + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (widgetIndex) + { + case WIDX_MAZE_DIRECTION_GROUPBOX: + RideConstructionTooldownConstruct(screenCoords); + break; + case WIDX_MAZE_ENTRANCE: + case WIDX_MAZE_EXIT: + WindowMazeConstructionEntranceTooldown(screenCoords); + break; + } + } + + void OnPrepareDraw() override + { + auto currentRide = GetRide(_currentRideIndex); + auto ft = Formatter::Common(); + if (currentRide != nullptr) + { + ft.Increment(4); + currentRide->FormatNameTo(ft); + } + else + { + ft.Increment(4); + ft.Add(STR_NONE); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + } + + private: + void WindowMazeConstructionEntranceMouseup(WidgetIndex widgetIndex) + { + if (ToolSet(*this, widgetIndex, Tool::Crosshair)) + return; + + gRideEntranceExitPlaceType = widgetIndex == WIDX_MAZE_ENTRANCE ? ENTRANCE_TYPE_RIDE_ENTRANCE + : ENTRANCE_TYPE_RIDE_EXIT; + gRideEntranceExitPlaceRideIndex = rideId; + gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0); + InputSetFlag(INPUT_FLAG_6, true); + + RideConstructionInvalidateCurrentTrack(); + + if (_rideConstructionState != RideConstructionState::EntranceExit) + { + gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState; + } + _rideConstructionState = RideConstructionState::EntranceExit; + + WindowMazeConstructionUpdatePressedWidgets(); + } + + void WindowMazeConstructionBuildModeMousedown(RideConstructionState rideConstructionState) + { + if (_rideConstructionState == RideConstructionState::EntranceExit) { ToolCancel(); - if (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) - WindowCloseByClass(WindowClass::RideConstruction); } - else - { - gRideEntranceExitPlaceType = gRideEntranceExitPlaceType ^ 1; - WindowInvalidateByClass(WindowClass::RideConstruction); - gCurrentToolWidget.widget_index = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE) - ? WIDX_MAZE_ENTRANCE - : WIDX_MAZE_EXIT; + _rideConstructionState = rideConstructionState; + WindowMazeConstructionUpdatePressedWidgets(); + } - WindowMazeConstructionUpdatePressedWidgets(); + void WindowMazeConstructionEntranceTooldown(const ScreenCoordsXY& screenCoords) + { + RideConstructionInvalidateCurrentTrack(); + + MapInvalidateSelectionRect(); + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + + CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords); + if (entranceOrExitCoords.IsNull()) + return; + + if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION) + return; + + RideId rideIndex = gRideEntranceExitPlaceRideIndex; + + auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction( + entranceOrExitCoords, DirectionReverse(entranceOrExitCoords.direction), rideIndex, + gRideEntranceExitPlaceStationIndex, gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT); + + rideEntranceExitPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; + + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + + auto currentRide = GetRide(rideIndex); + if (currentRide != nullptr && RideAreAllPossibleEntrancesAndExitsBuilt(*currentRide).Successful) + { + ToolCancel(); + if (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) + WindowCloseByClass(WindowClass::RideConstruction); + } + else + { + gRideEntranceExitPlaceType = gRideEntranceExitPlaceType ^ 1; + WindowInvalidateByClass(WindowClass::RideConstruction); + gCurrentToolWidget.widget_index = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE) + ? WIDX_MAZE_ENTRANCE + : WIDX_MAZE_EXIT; + + WindowMazeConstructionUpdatePressedWidgets(); + } + }); + auto res = GameActions::Execute(&rideEntranceExitPlaceAction); + } + + void WindowMazeConstructionConstruct(int32_t direction) + { + int32_t x, y, z, actionFlags = 0, mode; + + _currentTrackSelectionFlags = 0; + _rideConstructionNextArrowPulse = 0; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + RideConstructionInvalidateCurrentTrack(); + + x = _currentTrackBegin.x + (CoordsDirectionDelta[direction].x / 2); + y = _currentTrackBegin.y + (CoordsDirectionDelta[direction].y / 2); + z = _currentTrackBegin.z; + switch (_rideConstructionState) + { + case RideConstructionState::MazeBuild: + mode = GC_SET_MAZE_TRACK_BUILD; + break; + case RideConstructionState::MazeMove: + mode = GC_SET_MAZE_TRACK_MOVE; + actionFlags = GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED; + break; + default: + case RideConstructionState::MazeFill: + mode = GC_SET_MAZE_TRACK_FILL; + break; } - }); - auto res = GameActions::Execute(&rideEntranceExitPlaceAction); + + const auto loc = CoordsXYZD{ x, y, z, static_cast(direction) }; + auto action = MazeSetTrackAction(loc, false, _currentRideIndex, mode); + action.SetFlags(actionFlags); + const auto res = GameActions::Execute(&action); + if (res.Error != GameActions::Status::Ok) + { + return; + } + + _currentTrackBegin.x = x; + _currentTrackBegin.y = y; + if (_rideConstructionState != RideConstructionState::MazeMove) + { + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, { x, y, z }); + } + } + }; + + WindowBase* WindowMazeConstructionOpen() + { + return WindowFocusOrCreate( + WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE); } - void WindowMazeConstructionConstruct(int32_t direction) + void WindowMazeConstructionUpdatePressedWidgets() { - int32_t x, y, z, actionFlags = 0, mode; + WindowBase* w; - _currentTrackSelectionFlags = 0; - _rideConstructionNextArrowPulse = 0; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - RideConstructionInvalidateCurrentTrack(); + w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr) + return; + + uint64_t pressedWidgets = w->pressed_widgets; + + // Unpress all the mode buttons + pressedWidgets &= ~EnumToFlag(WIDX_MAZE_BUILD_MODE); + pressedWidgets &= ~EnumToFlag(WIDX_MAZE_MOVE_MODE); + pressedWidgets &= ~EnumToFlag(WIDX_MAZE_FILL_MODE); + pressedWidgets &= ~EnumToFlag(WIDX_MAZE_ENTRANCE); + pressedWidgets &= ~EnumToFlag(WIDX_MAZE_EXIT); - x = _currentTrackBegin.x + (CoordsDirectionDelta[direction].x / 2); - y = _currentTrackBegin.y + (CoordsDirectionDelta[direction].y / 2); - z = _currentTrackBegin.z; switch (_rideConstructionState) { + case RideConstructionState::EntranceExit: + if (gCurrentToolWidget.widget_index == WIDX_MAZE_ENTRANCE) + { + pressedWidgets |= EnumToFlag(WIDX_MAZE_ENTRANCE); + } + else + { + pressedWidgets |= EnumToFlag(WIDX_MAZE_EXIT); + } + break; case RideConstructionState::MazeBuild: - mode = GC_SET_MAZE_TRACK_BUILD; + pressedWidgets |= EnumToFlag(WIDX_MAZE_BUILD_MODE); break; case RideConstructionState::MazeMove: - mode = GC_SET_MAZE_TRACK_MOVE; - actionFlags = GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED; + pressedWidgets |= EnumToFlag(WIDX_MAZE_MOVE_MODE); + break; + case RideConstructionState::MazeFill: + pressedWidgets |= EnumToFlag(WIDX_MAZE_FILL_MODE); break; default: - case RideConstructionState::MazeFill: - mode = GC_SET_MAZE_TRACK_FILL; break; } - const auto loc = CoordsXYZD{ x, y, z, static_cast(direction) }; - auto action = MazeSetTrackAction(loc, false, _currentRideIndex, mode); - action.SetFlags(actionFlags); - const auto res = GameActions::Execute(&action); - if (res.Error != GameActions::Status::Ok) - { - return; - } - - _currentTrackBegin.x = x; - _currentTrackBegin.y = y; - if (_rideConstructionState != RideConstructionState::MazeMove) - { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, { x, y, z }); - } + w->pressed_widgets = pressedWidgets; + w->Invalidate(); } -}; - -WindowBase* WindowMazeConstructionOpen() -{ - return WindowFocusOrCreate( - WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE); -} - -void WindowMazeConstructionUpdatePressedWidgets() -{ - WindowBase* w; - - w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr) - return; - - uint64_t pressedWidgets = w->pressed_widgets; - - // Unpress all the mode buttons - pressedWidgets &= ~EnumToFlag(WIDX_MAZE_BUILD_MODE); - pressedWidgets &= ~EnumToFlag(WIDX_MAZE_MOVE_MODE); - pressedWidgets &= ~EnumToFlag(WIDX_MAZE_FILL_MODE); - pressedWidgets &= ~EnumToFlag(WIDX_MAZE_ENTRANCE); - pressedWidgets &= ~EnumToFlag(WIDX_MAZE_EXIT); - - switch (_rideConstructionState) - { - case RideConstructionState::EntranceExit: - if (gCurrentToolWidget.widget_index == WIDX_MAZE_ENTRANCE) - { - pressedWidgets |= EnumToFlag(WIDX_MAZE_ENTRANCE); - } - else - { - pressedWidgets |= EnumToFlag(WIDX_MAZE_EXIT); - } - break; - case RideConstructionState::MazeBuild: - pressedWidgets |= EnumToFlag(WIDX_MAZE_BUILD_MODE); - break; - case RideConstructionState::MazeMove: - pressedWidgets |= EnumToFlag(WIDX_MAZE_MOVE_MODE); - break; - case RideConstructionState::MazeFill: - pressedWidgets |= EnumToFlag(WIDX_MAZE_FILL_MODE); - break; - default: - break; - } - - w->pressed_widgets = pressedWidgets; - w->Invalidate(); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Multiplayer.cpp b/src/openrct2-ui/windows/Multiplayer.cpp index 7be9caed98..08eccaece4 100644 --- a/src/openrct2-ui/windows/Multiplayer.cpp +++ b/src/openrct2-ui/windows/Multiplayer.cpp @@ -20,7 +20,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WINDOW_MULTIPLAYER_PAGE_INFORMATION, WINDOW_MULTIPLAYER_PAGE_PLAYERS, @@ -118,831 +120,834 @@ static constexpr StringId WindowMultiplayerPageTitles[] = { STR_MULTIPLAYER_OPTIONS_TITLE, }; -// clang-format on + // clang-format on -static constexpr int32_t window_multiplayer_animation_divisor[] = { - 4, - 4, - 2, - 2, -}; -static constexpr int32_t window_multiplayer_animation_frames[] = { - 8, - 8, - 7, - 4, -}; + static constexpr int32_t window_multiplayer_animation_divisor[] = { + 4, + 4, + 2, + 2, + }; + static constexpr int32_t window_multiplayer_animation_frames[] = { + 8, + 8, + 7, + 4, + }; -static bool IsServerPlayerInvisible() -{ - return NetworkIsServerPlayerInvisible() && !gConfigGeneral.DebuggingTools; -} - -class MultiplayerWindow final : public Window -{ -private: - std::optional _windowInformationSize; - uint8_t _selectedGroup{ 0 }; - -private: - void ResetPressedWidgets(); - - void InformationPaint(DrawPixelInfo& dpi); - void PlayersPaint(DrawPixelInfo& dpi); - void GroupsPaint(DrawPixelInfo& dpi); - - void DrawTabImage(DrawPixelInfo& dpi, int32_t page_number, int32_t spriteIndex); - void DrawTabImages(DrawPixelInfo& dpi); - - void PlayersScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const; - void GroupsScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const; - - void ShowGroupDropdown(WidgetIndex widgetIndex); - ScreenCoordsXY InformationGetSize(); - -public: - void OnOpen() override; - - void SetPage(int32_t page_number); - - void OnMouseUp(WidgetIndex widgetIndex) override; - void OnResize() override; - void OnUpdate() override; - void OnPrepareDraw() override; - void OnDraw(DrawPixelInfo& dpi) override; - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override; - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override; - void OnMouseDown(WidgetIndex widgetIndex) override; - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override; - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override; - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override; - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override; -}; - -WindowBase* WindowMultiplayerOpen() -{ - // Check if window is already open - WindowBase* window = WindowBringToFrontByClass(WindowClass::Multiplayer); - if (window == nullptr) + static bool IsServerPlayerInvisible() { - window = WindowCreate(WindowClass::Multiplayer, 320, 144, WF_10 | WF_RESIZABLE | WF_AUTO_POSITION); + return NetworkIsServerPlayerInvisible() && !gConfigGeneral.DebuggingTools; } - return window; -} - -void MultiplayerWindow::OnOpen() -{ - SetPage(WINDOW_MULTIPLAYER_PAGE_INFORMATION); -} - -void MultiplayerWindow::SetPage(int32_t page_number) -{ - _windowInformationSize.reset(); - - page = page_number; - frame_no = 0; - no_list_items = 0; - selected_list_item = -1; - - hold_down_widgets = 0; - pressed_widgets = 0; - widgets = window_multiplayer_page_widgets[page]; - widgets[WIDX_TITLE].text = WindowMultiplayerPageTitles[page]; - - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - Invalidate(); -} - -void MultiplayerWindow::OnMouseUp(WidgetIndex widgetIndex) -{ - switch (widgetIndex) + class MultiplayerWindow final : public Window { - case WIDX_CLOSE: - Close(); - break; - case WIDX_TAB1: - case WIDX_TAB2: - case WIDX_TAB3: - case WIDX_TAB4: - if (page != widgetIndex - WIDX_TAB1) - { - SetPage(widgetIndex - WIDX_TAB1); - } - break; - } - switch (page) + private: + std::optional _windowInformationSize; + uint8_t _selectedGroup{ 0 }; + + private: + void ResetPressedWidgets(); + + void InformationPaint(DrawPixelInfo& dpi); + void PlayersPaint(DrawPixelInfo& dpi); + void GroupsPaint(DrawPixelInfo& dpi); + + void DrawTabImage(DrawPixelInfo& dpi, int32_t page_number, int32_t spriteIndex); + void DrawTabImages(DrawPixelInfo& dpi); + + void PlayersScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const; + void GroupsScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const; + + void ShowGroupDropdown(WidgetIndex widgetIndex); + ScreenCoordsXY InformationGetSize(); + + public: + void OnOpen() override; + + void SetPage(int32_t page_number); + + void OnMouseUp(WidgetIndex widgetIndex) override; + void OnResize() override; + void OnUpdate() override; + void OnPrepareDraw() override; + void OnDraw(DrawPixelInfo& dpi) override; + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override; + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override; + void OnMouseDown(WidgetIndex widgetIndex) override; + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override; + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override; + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override; + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override; + }; + + WindowBase* WindowMultiplayerOpen() { - case WINDOW_MULTIPLAYER_PAGE_GROUPS: + // Check if window is already open + WindowBase* window = WindowBringToFrontByClass(WindowClass::Multiplayer); + if (window == nullptr) { - switch (widgetIndex) - { - case WIDX_ADD_GROUP: + window = WindowCreate( + WindowClass::Multiplayer, 320, 144, WF_10 | WF_RESIZABLE | WF_AUTO_POSITION); + } + + return window; + } + + void MultiplayerWindow::OnOpen() + { + SetPage(WINDOW_MULTIPLAYER_PAGE_INFORMATION); + } + + void MultiplayerWindow::SetPage(int32_t page_number) + { + _windowInformationSize.reset(); + + page = page_number; + frame_no = 0; + no_list_items = 0; + selected_list_item = -1; + + hold_down_widgets = 0; + pressed_widgets = 0; + widgets = window_multiplayer_page_widgets[page]; + widgets[WIDX_TITLE].text = WindowMultiplayerPageTitles[page]; + + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); + } + + void MultiplayerWindow::OnMouseUp(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB1: + case WIDX_TAB2: + case WIDX_TAB3: + case WIDX_TAB4: + if (page != widgetIndex - WIDX_TAB1) { - auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::AddGroup); - GameActions::Execute(&networkModifyGroup); - break; + SetPage(widgetIndex - WIDX_TAB1); } - case WIDX_REMOVE_GROUP: + break; + } + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + switch (widgetIndex) { - auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, _selectedGroup); - GameActions::Execute(&networkModifyGroup); - break; + case WIDX_ADD_GROUP: + { + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::AddGroup); + GameActions::Execute(&networkModifyGroup); + break; + } + case WIDX_REMOVE_GROUP: + { + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, _selectedGroup); + GameActions::Execute(&networkModifyGroup); + break; + } + case WIDX_RENAME_GROUP: + { + int32_t groupIndex = NetworkGetGroupIndex(_selectedGroup); + const utf8* groupName = NetworkGetGroupName(groupIndex); + WindowTextInputRawOpen( + this, widgetIndex, STR_GROUP_NAME, STR_ENTER_NEW_NAME_FOR_THIS_GROUP, {}, groupName, 32); + break; + } } - case WIDX_RENAME_GROUP: + break; + } + case WINDOW_MULTIPLAYER_PAGE_OPTIONS: + { + switch (widgetIndex) { - int32_t groupIndex = NetworkGetGroupIndex(_selectedGroup); - const utf8* groupName = NetworkGetGroupName(groupIndex); - WindowTextInputRawOpen( - this, widgetIndex, STR_GROUP_NAME, STR_ENTER_NEW_NAME_FOR_THIS_GROUP, {}, groupName, 32); - break; + case WIDX_LOG_CHAT_CHECKBOX: + gConfigNetwork.LogChat = !gConfigNetwork.LogChat; + ConfigSaveDefault(); + break; + case WIDX_LOG_SERVER_ACTIONS_CHECKBOX: + gConfigNetwork.LogServerActions = !gConfigNetwork.LogServerActions; + ConfigSaveDefault(); + break; + case WIDX_KNOWN_KEYS_ONLY_CHECKBOX: + gConfigNetwork.KnownKeysOnly = !gConfigNetwork.KnownKeysOnly; + ConfigSaveDefault(); + break; } + break; } - break; } - case WINDOW_MULTIPLAYER_PAGE_OPTIONS: + } + + ScreenCoordsXY MultiplayerWindow::InformationGetSize() + { + assert(!_windowInformationSize.has_value()); + + int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + + // Base dimensions. + const int32_t baseWidth = 450; + int32_t baseHeight = 55; + + // Server name is displayed word-wrapped, so figure out how high it will be. { - switch (widgetIndex) + int32_t numLines; + GfxWrapString(NetworkGetServerName(), baseWidth, FontStyle::Medium, nullptr, &numLines); + baseHeight += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); + } + + // Likewise, for the optional server description -- which can be a little longer. + const auto& descString = NetworkGetServerDescription(); + if (!descString.empty()) + { + int32_t numLines; + GfxWrapString(descString, baseWidth, FontStyle::Medium, nullptr, &numLines); + baseHeight += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); + } + + // Finally, account for provider info, if present. + { + const auto& providerName = NetworkGetServerProviderName(); + if (!providerName.empty()) + baseHeight += LIST_ROW_HEIGHT; + + const auto& providerEmail = NetworkGetServerProviderEmail(); + if (!providerEmail.empty()) + baseHeight += LIST_ROW_HEIGHT; + + const auto& providerWebsite = NetworkGetServerProviderWebsite(); + if (!providerWebsite.empty()) + baseHeight += LIST_ROW_HEIGHT; + } + + // TODO: Are these casts still neccessary? + _windowInformationSize = { static_cast(baseWidth), static_cast(baseHeight) }; + return _windowInformationSize.value(); + } + + void MultiplayerWindow::OnResize() + { + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_INFORMATION: { - case WIDX_LOG_CHAT_CHECKBOX: - gConfigNetwork.LogChat = !gConfigNetwork.LogChat; - ConfigSaveDefault(); - break; - case WIDX_LOG_SERVER_ACTIONS_CHECKBOX: - gConfigNetwork.LogServerActions = !gConfigNetwork.LogServerActions; - ConfigSaveDefault(); - break; - case WIDX_KNOWN_KEYS_ONLY_CHECKBOX: - gConfigNetwork.KnownKeysOnly = !gConfigNetwork.KnownKeysOnly; - ConfigSaveDefault(); - break; + auto size = _windowInformationSize ? _windowInformationSize.value() : InformationGetSize(); + WindowSetResize(*this, size.x, size.y, size.x, size.y); + break; } - break; - } - } -} - -ScreenCoordsXY MultiplayerWindow::InformationGetSize() -{ - assert(!_windowInformationSize.has_value()); - - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - - // Base dimensions. - const int32_t baseWidth = 450; - int32_t baseHeight = 55; - - // Server name is displayed word-wrapped, so figure out how high it will be. - { - int32_t numLines; - GfxWrapString(NetworkGetServerName(), baseWidth, FontStyle::Medium, nullptr, &numLines); - baseHeight += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); - } - - // Likewise, for the optional server description -- which can be a little longer. - const auto& descString = NetworkGetServerDescription(); - if (!descString.empty()) - { - int32_t numLines; - GfxWrapString(descString, baseWidth, FontStyle::Medium, nullptr, &numLines); - baseHeight += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); - } - - // Finally, account for provider info, if present. - { - const auto& providerName = NetworkGetServerProviderName(); - if (!providerName.empty()) - baseHeight += LIST_ROW_HEIGHT; - - const auto& providerEmail = NetworkGetServerProviderEmail(); - if (!providerEmail.empty()) - baseHeight += LIST_ROW_HEIGHT; - - const auto& providerWebsite = NetworkGetServerProviderWebsite(); - if (!providerWebsite.empty()) - baseHeight += LIST_ROW_HEIGHT; - } - - // TODO: Are these casts still neccessary? - _windowInformationSize = { static_cast(baseWidth), static_cast(baseHeight) }; - return _windowInformationSize.value(); -} - -void MultiplayerWindow::OnResize() -{ - switch (page) - { - case WINDOW_MULTIPLAYER_PAGE_INFORMATION: - { - auto size = _windowInformationSize ? _windowInformationSize.value() : InformationGetSize(); - WindowSetResize(*this, size.x, size.y, size.x, size.y); - break; - } - case WINDOW_MULTIPLAYER_PAGE_PLAYERS: - { - WindowSetResize(*this, 420, 124, 500, 450); - - no_list_items = (IsServerPlayerInvisible() ? NetworkGetNumVisiblePlayers() : NetworkGetNumPlayers()); - - widgets[WIDX_HEADER_PING].right = width - 5; - - selected_list_item = -1; - Invalidate(); - break; - } - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - WindowSetResize(*this, 320, 200, 320, 500); - - no_list_items = NetworkGetNumActions(); - - selected_list_item = -1; - Invalidate(); - break; - } - case WINDOW_MULTIPLAYER_PAGE_OPTIONS: - { - WindowSetResize(*this, 300, 100, 300, 100); - break; - } - } -} - -void MultiplayerWindow::OnUpdate() -{ - frame_no++; - InvalidateWidget(WIDX_TAB1 + page); -} - -void MultiplayerWindow::ResetPressedWidgets() -{ - for (int32_t i = WIDX_TAB1; i <= WIDX_TAB4; i++) - { - SetWidgetPressed(i, false); - } -} - -void MultiplayerWindow::OnPrepareDraw() -{ - ResetPressedWidgets(); - SetWidgetPressed(WIDX_TAB1 + page, true); - ResizeFrameWithPage(); - switch (page) - { - case WINDOW_MULTIPLAYER_PAGE_INFORMATION: - { - WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); - break; - } - case WINDOW_MULTIPLAYER_PAGE_PLAYERS: - { - window_multiplayer_players_widgets[WIDX_LIST].right = width - 4; - window_multiplayer_players_widgets[WIDX_LIST].bottom = height - 0x0F; - WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); - break; - } - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].right = width - 4; - window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].bottom = height - 0x0F; - WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); - - // select other group if one is removed - while (NetworkGetGroupIndex(_selectedGroup) == -1 && _selectedGroup > 0) + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: { - _selectedGroup--; - } - break; - } - case WINDOW_MULTIPLAYER_PAGE_OPTIONS: - { - WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); + WindowSetResize(*this, 420, 124, 500, 450); - if (NetworkGetMode() == NETWORK_MODE_CLIENT) - { - widgets[WIDX_KNOWN_KEYS_ONLY_CHECKBOX].type = WindowWidgetType::Empty; - } + no_list_items = (IsServerPlayerInvisible() ? NetworkGetNumVisiblePlayers() : NetworkGetNumPlayers()); - SetCheckboxValue(WIDX_LOG_CHAT_CHECKBOX, gConfigNetwork.LogChat); - SetCheckboxValue(WIDX_LOG_SERVER_ACTIONS_CHECKBOX, gConfigNetwork.LogServerActions); - SetCheckboxValue(WIDX_KNOWN_KEYS_ONLY_CHECKBOX, gConfigNetwork.KnownKeysOnly); - break; - } - } -} + widgets[WIDX_HEADER_PING].right = width - 5; -void MultiplayerWindow::OnDraw(DrawPixelInfo& dpi) -{ - DrawWidgets(dpi); - DrawTabImages(dpi); - switch (page) - { - case WINDOW_MULTIPLAYER_PAGE_INFORMATION: - { - InformationPaint(dpi); - break; - } - case WINDOW_MULTIPLAYER_PAGE_PLAYERS: - { - PlayersPaint(dpi); - break; - } - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - GroupsPaint(dpi); - break; - } - } -} - -void MultiplayerWindow::OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) -{ - switch (page) - { - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - if (selectedIndex == -1) - { - return; - } - - switch (widgetIndex) - { - case WIDX_DEFAULT_GROUP_DROPDOWN: - { - auto networkModifyGroup = NetworkModifyGroupAction( - ModifyGroupType::SetDefault, NetworkGetGroupID(selectedIndex)); - GameActions::Execute(&networkModifyGroup); - break; - } - case WIDX_SELECTED_GROUP_DROPDOWN: - { - _selectedGroup = NetworkGetGroupID(selectedIndex); - break; - } - } - Invalidate(); - break; - } - } -} - -void MultiplayerWindow::OnTextInput(WidgetIndex widgetIndex, std::string_view text) -{ - switch (page) - { - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - if (widgetIndex != WIDX_RENAME_GROUP) - return; - - if (text.empty()) - return; - - auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::SetName, _selectedGroup, std::string(text)); - GameActions::Execute(&networkModifyGroup); - break; - } - } -} - -void MultiplayerWindow::ShowGroupDropdown(WidgetIndex widgetIndex) -{ - auto widget = &widgets[widgetIndex]; - Widget* dropdownWidget = widget - 1; - auto numItems = NetworkGetNumGroups(); - - WindowDropdownShowTextCustomWidth( - windowPos + ScreenCoordsXY{ dropdownWidget->left, dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], 0, 0, - numItems, widget->right - dropdownWidget->left); - - for (auto i = 0; i < NetworkGetNumGroups(); i++) - { - gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; - gDropdownItems[i].Args = reinterpret_cast(NetworkGetGroupName(i)); - } - if (widget == &window_multiplayer_groups_widgets[WIDX_DEFAULT_GROUP_DROPDOWN]) - { - Dropdown::SetChecked(NetworkGetGroupIndex(NetworkGetDefaultGroup()), true); - } - else if (widget == &window_multiplayer_groups_widgets[WIDX_SELECTED_GROUP_DROPDOWN]) - { - Dropdown::SetChecked(NetworkGetGroupIndex(_selectedGroup), true); - } -} - -void MultiplayerWindow::OnMouseDown(WidgetIndex widgetIndex) -{ - switch (page) - { - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - switch (widgetIndex) - { - case WIDX_DEFAULT_GROUP_DROPDOWN: - case WIDX_SELECTED_GROUP_DROPDOWN: - ShowGroupDropdown(widgetIndex); - break; - } - break; - } - } -} - -ScreenSize MultiplayerWindow::OnScrollGetSize(int32_t scrollIndex) -{ - ScreenSize screenSize{}; - switch (page) - { - case WINDOW_MULTIPLAYER_PAGE_PLAYERS: - { - if (selected_list_item != -1) - { selected_list_item = -1; Invalidate(); + break; } - - screenSize = { 0, NetworkGetNumPlayers() * SCROLLABLE_ROW_HEIGHT }; - int32_t i = screenSize.height - window_multiplayer_players_widgets[WIDX_LIST].bottom - + window_multiplayer_players_widgets[WIDX_LIST].top + 21; - if (i < 0) - i = 0; - if (i < scrolls[0].v_top) + case WINDOW_MULTIPLAYER_PAGE_GROUPS: { - scrolls[0].v_top = i; - Invalidate(); - } - break; - } + WindowSetResize(*this, 320, 200, 320, 500); + + no_list_items = NetworkGetNumActions(); - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - if (selected_list_item != -1) - { selected_list_item = -1; Invalidate(); + break; } - - screenSize = { 0, NetworkGetNumActions() * SCROLLABLE_ROW_HEIGHT }; - int32_t i = screenSize.height - window_multiplayer_groups_widgets[WIDX_LIST].bottom - + window_multiplayer_groups_widgets[WIDX_LIST].top + 21; - if (i < 0) - i = 0; - if (i < scrolls[0].v_top) + case WINDOW_MULTIPLAYER_PAGE_OPTIONS: { - scrolls[0].v_top = i; - Invalidate(); + WindowSetResize(*this, 300, 100, 300, 100); + break; } - break; } } - return screenSize; -} -void MultiplayerWindow::OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - switch (page) + void MultiplayerWindow::OnUpdate() { - case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + frame_no++; + InvalidateWidget(WIDX_TAB1 + page); + } + + void MultiplayerWindow::ResetPressedWidgets() + { + for (int32_t i = WIDX_TAB1; i <= WIDX_TAB4; i++) { - int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index >= no_list_items) - return; - - selected_list_item = index; - Invalidate(); - - int32_t player = (IsServerPlayerInvisible() ? index + 1 : index); - WindowPlayerOpen(NetworkGetPlayerID(player)); - break; - } - - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - { - int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index >= no_list_items) - return; - - selected_list_item = index; - Invalidate(); - - auto networkModifyGroup = NetworkModifyGroupAction( - ModifyGroupType::SetPermissions, _selectedGroup, "", index, PermissionState::Toggle); - GameActions::Execute(&networkModifyGroup); - break; + SetWidgetPressed(i, false); } } -} -void MultiplayerWindow::OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - switch (page) + void MultiplayerWindow::OnPrepareDraw() { - case WINDOW_MULTIPLAYER_PAGE_PLAYERS: - case WINDOW_MULTIPLAYER_PAGE_GROUPS: + ResetPressedWidgets(); + SetWidgetPressed(WIDX_TAB1 + page, true); + ResizeFrameWithPage(); + switch (page) { - int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index >= no_list_items) - return; + case WINDOW_MULTIPLAYER_PAGE_INFORMATION: + { + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); + break; + } + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + window_multiplayer_players_widgets[WIDX_LIST].right = width - 4; + window_multiplayer_players_widgets[WIDX_LIST].bottom = height - 0x0F; + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); + break; + } + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].right = width - 4; + window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].bottom = height - 0x0F; + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); - selected_list_item = index; - Invalidate(); - break; + // select other group if one is removed + while (NetworkGetGroupIndex(_selectedGroup) == -1 && _selectedGroup > 0) + { + _selectedGroup--; + } + break; + } + case WINDOW_MULTIPLAYER_PAGE_OPTIONS: + { + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); + + if (NetworkGetMode() == NETWORK_MODE_CLIENT) + { + widgets[WIDX_KNOWN_KEYS_ONLY_CHECKBOX].type = WindowWidgetType::Empty; + } + + SetCheckboxValue(WIDX_LOG_CHAT_CHECKBOX, gConfigNetwork.LogChat); + SetCheckboxValue(WIDX_LOG_SERVER_ACTIONS_CHECKBOX, gConfigNetwork.LogServerActions); + SetCheckboxValue(WIDX_KNOWN_KEYS_ONLY_CHECKBOX, gConfigNetwork.KnownKeysOnly); + break; + } } } -} -void MultiplayerWindow::OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) -{ - switch (page) + void MultiplayerWindow::OnDraw(DrawPixelInfo& dpi) { - case WINDOW_MULTIPLAYER_PAGE_PLAYERS: - PlayersScrollPaint(scrollIndex, dpi); - break; - - case WINDOW_MULTIPLAYER_PAGE_GROUPS: - GroupsScrollPaint(scrollIndex, dpi); - break; - } -} - -void MultiplayerWindow::InformationPaint(DrawPixelInfo& dpi) -{ - DrawPixelInfo clippedDPI; - if (ClipDrawPixelInfo(clippedDPI, dpi, windowPos, width, height)) - { - auto screenCoords = ScreenCoordsXY{ 3, 50 }; - int32_t newWidth = width - 6; - - const auto& name = NetworkGetServerName(); + DrawWidgets(dpi); + DrawTabImages(dpi); + switch (page) { - auto ft = Formatter(); - ft.Add(name.c_str()); - screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, newWidth, STR_STRING, ft, { colours[1] }); - screenCoords.y += LIST_ROW_HEIGHT / 2; - } - - const auto& description = NetworkGetServerDescription(); - if (!description.empty()) - { - auto ft = Formatter(); - ft.Add(description.c_str()); - screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, newWidth, STR_STRING, ft, { colours[1] }); - screenCoords.y += LIST_ROW_HEIGHT / 2; - } - - const auto& providerName = NetworkGetServerProviderName(); - if (!providerName.empty()) - { - auto ft = Formatter(); - ft.Add(providerName.c_str()); - DrawTextBasic(clippedDPI, screenCoords, STR_PROVIDER_NAME, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - const auto& providerEmail = NetworkGetServerProviderEmail(); - if (!providerEmail.empty()) - { - auto ft = Formatter(); - ft.Add(providerEmail.c_str()); - DrawTextBasic(clippedDPI, screenCoords, STR_PROVIDER_EMAIL, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - const auto& providerWebsite = NetworkGetServerProviderWebsite(); - if (!providerWebsite.empty()) - { - auto ft = Formatter(); - ft.Add(providerWebsite.c_str()); - DrawTextBasic(clippedDPI, screenCoords, STR_PROVIDER_WEBSITE, ft); + case WINDOW_MULTIPLAYER_PAGE_INFORMATION: + { + InformationPaint(dpi); + break; + } + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + PlayersPaint(dpi); + break; + } + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + GroupsPaint(dpi); + break; + } } } -} -void MultiplayerWindow::PlayersPaint(DrawPixelInfo& dpi) -{ - // Number of players - StringId stringId = no_list_items == 1 ? STR_MULTIPLAYER_PLAYER_COUNT : STR_MULTIPLAYER_PLAYER_COUNT_PLURAL; - auto screenCoords = windowPos + ScreenCoordsXY{ 4, widgets[WIDX_LIST].bottom + 2 }; - auto ft = Formatter(); - ft.Add(no_list_items); - DrawTextBasic(dpi, screenCoords, stringId, ft, { colours[2] }); -} - -void MultiplayerWindow::PlayersScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const -{ - ScreenCoordsXY screenCoords; - screenCoords.y = 0; - - const int32_t firstPlayerInList = (IsServerPlayerInvisible() ? 1 : 0); - int32_t listPosition = 0; - - for (int32_t player = firstPlayerInList; player < NetworkGetNumPlayers(); player++) + void MultiplayerWindow::OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) { - if (screenCoords.y > dpi.y + dpi.height) + switch (page) { - break; + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + if (selectedIndex == -1) + { + return; + } + + switch (widgetIndex) + { + case WIDX_DEFAULT_GROUP_DROPDOWN: + { + auto networkModifyGroup = NetworkModifyGroupAction( + ModifyGroupType::SetDefault, NetworkGetGroupID(selectedIndex)); + GameActions::Execute(&networkModifyGroup); + break; + } + case WIDX_SELECTED_GROUP_DROPDOWN: + { + _selectedGroup = NetworkGetGroupID(selectedIndex); + break; + } + } + Invalidate(); + break; + } + } + } + + void MultiplayerWindow::OnTextInput(WidgetIndex widgetIndex, std::string_view text) + { + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + if (widgetIndex != WIDX_RENAME_GROUP) + return; + + if (text.empty()) + return; + + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::SetName, _selectedGroup, std::string(text)); + GameActions::Execute(&networkModifyGroup); + break; + } + } + } + + void MultiplayerWindow::ShowGroupDropdown(WidgetIndex widgetIndex) + { + auto widget = &widgets[widgetIndex]; + Widget* dropdownWidget = widget - 1; + auto numItems = NetworkGetNumGroups(); + + WindowDropdownShowTextCustomWidth( + windowPos + ScreenCoordsXY{ dropdownWidget->left, dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], + 0, 0, numItems, widget->right - dropdownWidget->left); + + for (auto i = 0; i < NetworkGetNumGroups(); i++) + { + gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; + gDropdownItems[i].Args = reinterpret_cast(NetworkGetGroupName(i)); + } + if (widget == &window_multiplayer_groups_widgets[WIDX_DEFAULT_GROUP_DROPDOWN]) + { + Dropdown::SetChecked(NetworkGetGroupIndex(NetworkGetDefaultGroup()), true); + } + else if (widget == &window_multiplayer_groups_widgets[WIDX_SELECTED_GROUP_DROPDOWN]) + { + Dropdown::SetChecked(NetworkGetGroupIndex(_selectedGroup), true); + } + } + + void MultiplayerWindow::OnMouseDown(WidgetIndex widgetIndex) + { + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + switch (widgetIndex) + { + case WIDX_DEFAULT_GROUP_DROPDOWN: + case WIDX_SELECTED_GROUP_DROPDOWN: + ShowGroupDropdown(widgetIndex); + break; + } + break; + } + } + } + + ScreenSize MultiplayerWindow::OnScrollGetSize(int32_t scrollIndex) + { + ScreenSize screenSize{}; + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + if (selected_list_item != -1) + { + selected_list_item = -1; + Invalidate(); + } + + screenSize = { 0, NetworkGetNumPlayers() * SCROLLABLE_ROW_HEIGHT }; + int32_t i = screenSize.height - window_multiplayer_players_widgets[WIDX_LIST].bottom + + window_multiplayer_players_widgets[WIDX_LIST].top + 21; + if (i < 0) + i = 0; + if (i < scrolls[0].v_top) + { + scrolls[0].v_top = i; + Invalidate(); + } + break; + } + + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + if (selected_list_item != -1) + { + selected_list_item = -1; + Invalidate(); + } + + screenSize = { 0, NetworkGetNumActions() * SCROLLABLE_ROW_HEIGHT }; + int32_t i = screenSize.height - window_multiplayer_groups_widgets[WIDX_LIST].bottom + + window_multiplayer_groups_widgets[WIDX_LIST].top + 21; + if (i < 0) + i = 0; + if (i < scrolls[0].v_top) + { + scrolls[0].v_top = i; + Invalidate(); + } + break; + } + } + return screenSize; + } + + void MultiplayerWindow::OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) + { + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index >= no_list_items) + return; + + selected_list_item = index; + Invalidate(); + + int32_t player = (IsServerPlayerInvisible() ? index + 1 : index); + WindowPlayerOpen(NetworkGetPlayerID(player)); + break; + } + + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index >= no_list_items) + return; + + selected_list_item = index; + Invalidate(); + + auto networkModifyGroup = NetworkModifyGroupAction( + ModifyGroupType::SetPermissions, _selectedGroup, "", index, PermissionState::Toggle); + GameActions::Execute(&networkModifyGroup); + break; + } + } + } + + void MultiplayerWindow::OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) + { + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index >= no_list_items) + return; + + selected_list_item = index; + Invalidate(); + break; + } + } + } + + void MultiplayerWindow::OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) + { + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + PlayersScrollPaint(scrollIndex, dpi); + break; + + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + GroupsScrollPaint(scrollIndex, dpi); + break; + } + } + + void MultiplayerWindow::InformationPaint(DrawPixelInfo& dpi) + { + DrawPixelInfo clippedDPI; + if (ClipDrawPixelInfo(clippedDPI, dpi, windowPos, width, height)) + { + auto screenCoords = ScreenCoordsXY{ 3, 50 }; + int32_t newWidth = width - 6; + + const auto& name = NetworkGetServerName(); + { + auto ft = Formatter(); + ft.Add(name.c_str()); + screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, newWidth, STR_STRING, ft, { colours[1] }); + screenCoords.y += LIST_ROW_HEIGHT / 2; + } + + const auto& description = NetworkGetServerDescription(); + if (!description.empty()) + { + auto ft = Formatter(); + ft.Add(description.c_str()); + screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, newWidth, STR_STRING, ft, { colours[1] }); + screenCoords.y += LIST_ROW_HEIGHT / 2; + } + + const auto& providerName = NetworkGetServerProviderName(); + if (!providerName.empty()) + { + auto ft = Formatter(); + ft.Add(providerName.c_str()); + DrawTextBasic(clippedDPI, screenCoords, STR_PROVIDER_NAME, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + const auto& providerEmail = NetworkGetServerProviderEmail(); + if (!providerEmail.empty()) + { + auto ft = Formatter(); + ft.Add(providerEmail.c_str()); + DrawTextBasic(clippedDPI, screenCoords, STR_PROVIDER_EMAIL, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } + + const auto& providerWebsite = NetworkGetServerProviderWebsite(); + if (!providerWebsite.empty()) + { + auto ft = Formatter(); + ft.Add(providerWebsite.c_str()); + DrawTextBasic(clippedDPI, screenCoords, STR_PROVIDER_WEBSITE, ft); + } + } + } + + void MultiplayerWindow::PlayersPaint(DrawPixelInfo& dpi) + { + // Number of players + StringId stringId = no_list_items == 1 ? STR_MULTIPLAYER_PLAYER_COUNT : STR_MULTIPLAYER_PLAYER_COUNT_PLURAL; + auto screenCoords = windowPos + ScreenCoordsXY{ 4, widgets[WIDX_LIST].bottom + 2 }; + auto ft = Formatter(); + ft.Add(no_list_items); + DrawTextBasic(dpi, screenCoords, stringId, ft, { colours[2] }); + } + + void MultiplayerWindow::PlayersScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const + { + ScreenCoordsXY screenCoords; + screenCoords.y = 0; + + const int32_t firstPlayerInList = (IsServerPlayerInvisible() ? 1 : 0); + int32_t listPosition = 0; + + for (int32_t player = firstPlayerInList; player < NetworkGetNumPlayers(); player++) + { + if (screenCoords.y > dpi.y + dpi.height) + { + break; + } + + if (screenCoords.y + SCROLLABLE_ROW_HEIGHT + 1 >= dpi.y) + { + thread_local std::string _buffer; + _buffer.reserve(512); + _buffer.clear(); + + // Draw player name + colour_t colour = COLOUR_BLACK; + if (listPosition == selected_list_item) + { + GfxFilterRect( + dpi, { 0, screenCoords.y, 800, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 }, + FilterPaletteID::PaletteDarken1); + _buffer += NetworkGetPlayerName(player); + colour = colours[2]; + } + else + { + if (NetworkGetPlayerFlags(player) & NETWORK_PLAYER_FLAG_ISSERVER) + { + _buffer += "{BABYBLUE}"; + } + else + { + _buffer += "{BLACK}"; + } + _buffer += NetworkGetPlayerName(player); + } + screenCoords.x = 0; + GfxClipString(_buffer.data(), 230, FontStyle::Medium); + GfxDrawString(dpi, screenCoords, _buffer.c_str(), { colour }); + + // Draw group name + _buffer.resize(0); + int32_t group = NetworkGetGroupIndex(NetworkGetPlayerGroup(player)); + if (group != -1) + { + _buffer += "{BLACK}"; + screenCoords.x = 173; + _buffer += NetworkGetGroupName(group); + GfxClipString(_buffer.data(), 80, FontStyle::Medium); + GfxDrawString(dpi, screenCoords, _buffer.c_str(), { colour }); + } + + // Draw last action + int32_t action = NetworkGetPlayerLastAction(player, 2000); + auto ft = Formatter(); + if (action != -999) + { + ft.Add(NetworkGetActionNameStringID(action)); + } + else + { + ft.Add(STR_ACTION_NA); + } + DrawTextEllipsised(dpi, { 256, screenCoords.y }, 100, STR_BLACK_STRING, ft); + + // Draw ping + _buffer.resize(0); + int32_t ping = NetworkGetPlayerPing(player); + if (ping <= 100) + { + _buffer += "{GREEN}"; + } + else if (ping <= 250) + { + _buffer += "{YELLOW}"; + } + else + { + _buffer += "{RED}"; + } + + char pingBuffer[64]{}; + snprintf(pingBuffer, sizeof(pingBuffer), "%d ms", ping); + _buffer += pingBuffer; + + screenCoords.x = 356; + GfxDrawString(dpi, screenCoords, _buffer.c_str(), { colour }); + } + screenCoords.y += SCROLLABLE_ROW_HEIGHT; + listPosition++; + } + } + + void MultiplayerWindow::GroupsPaint(DrawPixelInfo& dpi) + { + thread_local std::string _buffer; + + Widget* widget = &window_multiplayer_groups_widgets[WIDX_DEFAULT_GROUP]; + int32_t group = NetworkGetGroupIndex(NetworkGetDefaultGroup()); + if (group != -1) + { + _buffer.assign("{WINDOW_COLOUR_2}"); + _buffer += NetworkGetGroupName(group); + + auto ft = Formatter(); + ft.Add(_buffer.c_str()); + DrawTextEllipsised( + dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, + { TextAlignment::CENTRE }); } - if (screenCoords.y + SCROLLABLE_ROW_HEIGHT + 1 >= dpi.y) - { - thread_local std::string _buffer; - _buffer.reserve(512); - _buffer.clear(); + auto screenPos = windowPos + + ScreenCoordsXY{ window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].left + 4, + window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].top + 4 }; - // Draw player name - colour_t colour = COLOUR_BLACK; - if (listPosition == selected_list_item) + DrawTextBasic(dpi, screenPos, STR_DEFAULT_GROUP, {}, { colours[2] }); + + screenPos.y += 20; + + GfxFillRectInset( + dpi, { screenPos - ScreenCoordsXY{ 0, 6 }, screenPos + ScreenCoordsXY{ 310, -5 } }, colours[1], + INSET_RECT_FLAG_BORDER_INSET); + + widget = &window_multiplayer_groups_widgets[WIDX_SELECTED_GROUP]; + group = NetworkGetGroupIndex(_selectedGroup); + if (group != -1) + { + _buffer.assign("{WINDOW_COLOUR_2}"); + _buffer += NetworkGetGroupName(group); + auto ft = Formatter(); + ft.Add(_buffer.c_str()); + DrawTextEllipsised( + dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, + { TextAlignment::CENTRE }); + } + } + + void MultiplayerWindow::GroupsScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const + { + auto screenCoords = ScreenCoordsXY{ 0, 0 }; + + auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; + GfxFillRect( + dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); + + for (int32_t i = 0; i < NetworkGetNumActions(); i++) + { + if (i == selected_list_item) { GfxFilterRect( dpi, { 0, screenCoords.y, 800, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); - _buffer += NetworkGetPlayerName(player); - colour = colours[2]; } - else + if (screenCoords.y > dpi.y + dpi.height) { - if (NetworkGetPlayerFlags(player) & NETWORK_PLAYER_FLAG_ISSERVER) + break; + } + + if (screenCoords.y + SCROLLABLE_ROW_HEIGHT + 1 >= dpi.y) + { + int32_t groupindex = NetworkGetGroupIndex(_selectedGroup); + if (groupindex != -1) { - _buffer += "{BABYBLUE}"; + if (NetworkCanPerformAction(groupindex, static_cast(i))) + { + screenCoords.x = 0; + GfxDrawString(dpi, screenCoords, u8"{WINDOW_COLOUR_2}✓", {}); + } } - else + + // Draw action name + auto ft = Formatter(); + ft.Add(NetworkGetActionNameStringID(i)); + DrawTextBasic(dpi, { 10, screenCoords.y }, STR_WINDOW_COLOUR_2_STRINGID, ft); + } + screenCoords.y += SCROLLABLE_ROW_HEIGHT; + } + } + + void MultiplayerWindow::DrawTabImage(DrawPixelInfo& dpi, int32_t page_number, int32_t spriteIndex) + { + WidgetIndex widgetIndex = WIDX_TAB1 + page_number; + + if (!IsWidgetDisabled(widgetIndex)) + { + if (page == page_number) + { + int32_t numFrames = window_multiplayer_animation_frames[page]; + if (numFrames > 1) { - _buffer += "{BLACK}"; - } - _buffer += NetworkGetPlayerName(player); - } - screenCoords.x = 0; - GfxClipString(_buffer.data(), 230, FontStyle::Medium); - GfxDrawString(dpi, screenCoords, _buffer.c_str(), { colour }); - - // Draw group name - _buffer.resize(0); - int32_t group = NetworkGetGroupIndex(NetworkGetPlayerGroup(player)); - if (group != -1) - { - _buffer += "{BLACK}"; - screenCoords.x = 173; - _buffer += NetworkGetGroupName(group); - GfxClipString(_buffer.data(), 80, FontStyle::Medium); - GfxDrawString(dpi, screenCoords, _buffer.c_str(), { colour }); - } - - // Draw last action - int32_t action = NetworkGetPlayerLastAction(player, 2000); - auto ft = Formatter(); - if (action != -999) - { - ft.Add(NetworkGetActionNameStringID(action)); - } - else - { - ft.Add(STR_ACTION_NA); - } - DrawTextEllipsised(dpi, { 256, screenCoords.y }, 100, STR_BLACK_STRING, ft); - - // Draw ping - _buffer.resize(0); - int32_t ping = NetworkGetPlayerPing(player); - if (ping <= 100) - { - _buffer += "{GREEN}"; - } - else if (ping <= 250) - { - _buffer += "{YELLOW}"; - } - else - { - _buffer += "{RED}"; - } - - char pingBuffer[64]{}; - snprintf(pingBuffer, sizeof(pingBuffer), "%d ms", ping); - _buffer += pingBuffer; - - screenCoords.x = 356; - GfxDrawString(dpi, screenCoords, _buffer.c_str(), { colour }); - } - screenCoords.y += SCROLLABLE_ROW_HEIGHT; - listPosition++; - } -} - -void MultiplayerWindow::GroupsPaint(DrawPixelInfo& dpi) -{ - thread_local std::string _buffer; - - Widget* widget = &window_multiplayer_groups_widgets[WIDX_DEFAULT_GROUP]; - int32_t group = NetworkGetGroupIndex(NetworkGetDefaultGroup()); - if (group != -1) - { - _buffer.assign("{WINDOW_COLOUR_2}"); - _buffer += NetworkGetGroupName(group); - - auto ft = Formatter(); - ft.Add(_buffer.c_str()); - DrawTextEllipsised( - dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, - { TextAlignment::CENTRE }); - } - - auto screenPos = windowPos - + ScreenCoordsXY{ window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].left + 4, - window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].top + 4 }; - - DrawTextBasic(dpi, screenPos, STR_DEFAULT_GROUP, {}, { colours[2] }); - - screenPos.y += 20; - - GfxFillRectInset( - dpi, { screenPos - ScreenCoordsXY{ 0, 6 }, screenPos + ScreenCoordsXY{ 310, -5 } }, colours[1], - INSET_RECT_FLAG_BORDER_INSET); - - widget = &window_multiplayer_groups_widgets[WIDX_SELECTED_GROUP]; - group = NetworkGetGroupIndex(_selectedGroup); - if (group != -1) - { - _buffer.assign("{WINDOW_COLOUR_2}"); - _buffer += NetworkGetGroupName(group); - auto ft = Formatter(); - ft.Add(_buffer.c_str()); - DrawTextEllipsised( - dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, - { TextAlignment::CENTRE }); - } -} - -void MultiplayerWindow::GroupsScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const -{ - auto screenCoords = ScreenCoordsXY{ 0, 0 }; - - auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; - GfxFillRect( - dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - - for (int32_t i = 0; i < NetworkGetNumActions(); i++) - { - if (i == selected_list_item) - { - GfxFilterRect( - dpi, { 0, screenCoords.y, 800, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); - } - if (screenCoords.y > dpi.y + dpi.height) - { - break; - } - - if (screenCoords.y + SCROLLABLE_ROW_HEIGHT + 1 >= dpi.y) - { - int32_t groupindex = NetworkGetGroupIndex(_selectedGroup); - if (groupindex != -1) - { - if (NetworkCanPerformAction(groupindex, static_cast(i))) - { - screenCoords.x = 0; - GfxDrawString(dpi, screenCoords, u8"{WINDOW_COLOUR_2}✓", {}); + int32_t frame = frame_no / window_multiplayer_animation_divisor[page]; + spriteIndex += (frame % numFrames); } } - // Draw action name - auto ft = Formatter(); - ft.Add(NetworkGetActionNameStringID(i)); - DrawTextBasic(dpi, { 10, screenCoords.y }, STR_WINDOW_COLOUR_2_STRINGID, ft); + GfxDrawSprite( + dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); } - screenCoords.y += SCROLLABLE_ROW_HEIGHT; } -} -void MultiplayerWindow::DrawTabImage(DrawPixelInfo& dpi, int32_t page_number, int32_t spriteIndex) -{ - WidgetIndex widgetIndex = WIDX_TAB1 + page_number; - - if (!IsWidgetDisabled(widgetIndex)) + void MultiplayerWindow::DrawTabImages(DrawPixelInfo& dpi) { - if (page == page_number) - { - int32_t numFrames = window_multiplayer_animation_frames[page]; - if (numFrames > 1) - { - int32_t frame = frame_no / window_multiplayer_animation_divisor[page]; - spriteIndex += (frame % numFrames); - } - } - - GfxDrawSprite( - dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_INFORMATION, SPR_TAB_KIOSKS_AND_FACILITIES_0); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_PLAYERS, SPR_TAB_GUESTS_0); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_GROUPS, SPR_TAB_STAFF_OPTIONS_0); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_OPTIONS, SPR_TAB_GEARS_0); } -} - -void MultiplayerWindow::DrawTabImages(DrawPixelInfo& dpi) -{ - DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_INFORMATION, SPR_TAB_KIOSKS_AND_FACILITIES_0); - DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_PLAYERS, SPR_TAB_GUESTS_0); - DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_GROUPS, SPR_TAB_STAFF_OPTIONS_0); - DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_OPTIONS, SPR_TAB_GEARS_0); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/NetworkStatus.cpp b/src/openrct2-ui/windows/NetworkStatus.cpp index 29dc11758b..6dafc0c5ad 100644 --- a/src/openrct2-ui/windows/NetworkStatus.cpp +++ b/src/openrct2-ui/windows/NetworkStatus.cpp @@ -14,7 +14,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowNetworkStatusWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -29,133 +31,134 @@ static Widget window_network_status_widgets[] = { kWidgetsEnd, }; -// clang-format on -class NetworkStatusWindow final : public Window -{ -public: - void OnOpen() override + // clang-format on + class NetworkStatusWindow final : public Window { - widgets = window_network_status_widgets; - WindowInitScrollWidgets(*this); - - frame_no = 0; - min_width = 320; - min_height = 90; - max_width = min_width; - max_height = min_height; - - page = 0; - list_information_type = 0; - } - - void OnClose() override - { - if (_onClose != nullptr) + public: + void OnOpen() override { - _onClose(); - } - } + widgets = window_network_status_widgets; + WindowInitScrollWidgets(*this); - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + frame_no = 0; + min_width = 320; + min_height = 90; + max_width = min_width; + max_height = min_height; + + page = 0; + list_information_type = 0; + } + + void OnClose() override { - case WIDX_CLOSE: - Close(); - break; + if (_onClose != nullptr) + { + _onClose(); + } } - } - void OnUpdate() override - { - InvalidateWidget(WIDX_BACKGROUND); - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - _password.clear(); - switch (widgetIndex) + void OnMouseUp(WidgetIndex widgetIndex) override { - case WIDX_PASSWORD: - _password = text; - break; + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + } } - if (text.empty()) + + void OnUpdate() override { - NetworkShutdownClient(); + InvalidateWidget(WIDX_BACKGROUND); } - else + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { - NetworkSendPassword(_password); + _password.clear(); + switch (widgetIndex) + { + case WIDX_PASSWORD: + _password = text; + break; + } + if (text.empty()) + { + NetworkShutdownClient(); + } + else + { + NetworkSendPassword(_password); + } } - } - void OnPrepareDraw() override + void OnPrepareDraw() override + { + ResizeFrame(); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + WindowDrawWidgets(*this, dpi); + thread_local std::string _buffer; + _buffer.assign("{BLACK}"); + _buffer += _windowNetworkStatusText; + GfxClipString(_buffer.data(), widgets[WIDX_BACKGROUND].right - 50, FontStyle::Medium); + ScreenCoordsXY screenCoords(windowPos.x + (width / 2), windowPos.y + (height / 2)); + screenCoords.x -= GfxGetStringWidth(_buffer, FontStyle::Medium) / 2; + GfxDrawString(dpi, screenCoords, _buffer.c_str()); + } + + void SetCloseCallBack(close_callback onClose) + { + _onClose = onClose; + } + + void SetWindowNetworkStatusText(const std::string& text) + { + _windowNetworkStatusText = text; + } + + void SetPassword(char* password) + { + _password = password; + } + + private: + close_callback _onClose = nullptr; + std::string _windowNetworkStatusText; + std::string _password; + }; + + WindowBase* WindowNetworkStatusOpen(const std::string& text, close_callback onClose) { - ResizeFrame(); + auto window = WindowFocusOrCreate( + WindowClass::NetworkStatus, 420, 90, WF_10 | WF_TRANSPARENT | WF_CENTRE_SCREEN); + window->SetCloseCallBack(onClose); + window->SetWindowNetworkStatusText(text); + return window; } - void OnDraw(DrawPixelInfo& dpi) override + // force close + void WindowNetworkStatusClose() { - WindowDrawWidgets(*this, dpi); - thread_local std::string _buffer; - _buffer.assign("{BLACK}"); - _buffer += _windowNetworkStatusText; - GfxClipString(_buffer.data(), widgets[WIDX_BACKGROUND].right - 50, FontStyle::Medium); - ScreenCoordsXY screenCoords(windowPos.x + (width / 2), windowPos.y + (height / 2)); - screenCoords.x -= GfxGetStringWidth(_buffer, FontStyle::Medium) / 2; - GfxDrawString(dpi, screenCoords, _buffer.c_str()); + auto window = WindowFindByClass(WindowClass::NetworkStatus); + if (window == nullptr) + { + return; + } + auto networkWindow = static_cast(window); + networkWindow->SetCloseCallBack(nullptr); + networkWindow->Close(); } - void SetCloseCallBack(close_callback onClose) + WindowBase* WindowNetworkStatusOpenPassword() { - _onClose = onClose; + auto window = WindowFocusOrCreate( + WindowClass::NetworkStatus, 420, 90, WF_10 | WF_TRANSPARENT | WF_CENTRE_SCREEN); + char password[33]{}; + WindowTextInputRawOpen(window, WIDX_PASSWORD, STR_PASSWORD_REQUIRED, STR_PASSWORD_REQUIRED_DESC, {}, password, 32); + window->SetPassword(password); + return window; } - - void SetWindowNetworkStatusText(const std::string& text) - { - _windowNetworkStatusText = text; - } - - void SetPassword(char* password) - { - _password = password; - } - -private: - close_callback _onClose = nullptr; - std::string _windowNetworkStatusText; - std::string _password; -}; - -WindowBase* WindowNetworkStatusOpen(const std::string& text, close_callback onClose) -{ - auto window = WindowFocusOrCreate( - WindowClass::NetworkStatus, 420, 90, WF_10 | WF_TRANSPARENT | WF_CENTRE_SCREEN); - window->SetCloseCallBack(onClose); - window->SetWindowNetworkStatusText(text); - return window; -} - -// force close -void WindowNetworkStatusClose() -{ - auto window = WindowFindByClass(WindowClass::NetworkStatus); - if (window == nullptr) - { - return; - } - auto networkWindow = static_cast(window); - networkWindow->SetCloseCallBack(nullptr); - networkWindow->Close(); -} - -WindowBase* WindowNetworkStatusOpenPassword() -{ - auto window = WindowFocusOrCreate( - WindowClass::NetworkStatus, 420, 90, WF_10 | WF_TRANSPARENT | WF_CENTRE_SCREEN); - char password[33]{}; - WindowTextInputRawOpen(window, WIDX_PASSWORD, STR_PASSWORD_REQUIRED, STR_PASSWORD_REQUIRED_DESC, {}, password, 32); - window->SetPassword(password); - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/NewCampaign.cpp b/src/openrct2-ui/windows/NewCampaign.cpp index bdb372bfd6..218c7cd41f 100644 --- a/src/openrct2-ui/windows/NewCampaign.cpp +++ b/src/openrct2-ui/windows/NewCampaign.cpp @@ -21,15 +21,15 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_NONE; + static constexpr int32_t WH = 109; + static constexpr int32_t WW = 350; -static constexpr StringId WINDOW_TITLE = STR_NONE; -static constexpr int32_t WH = 109; -static constexpr int32_t WW = 350; + constexpr uint16_t SELECTED_ITEM_UNDEFINED = 0xFFFF; -constexpr uint16_t SELECTED_ITEM_UNDEFINED = 0xFFFF; - -// clang-format off + // clang-format off enum WindowNewCampaignWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -54,159 +54,189 @@ static Widget window_new_campaign_widgets[] = { MakeWidget ({ 14, 89}, {322, 14}, WindowWidgetType::Button, WindowColour::Primary, STR_MARKETING_START_THIS_MARKETING_CAMPAIGN), // start button kWidgetsEnd, }; -// clang-format on + // clang-format on -class NewCampaignWindow final : public Window -{ -private: - std::vector RideList; - std::vector ShopItems; - struct CampaignVariables + class NewCampaignWindow final : public Window { - int16_t campaign_type; - int16_t no_weeks; // 0x482 - union + private: + std::vector RideList; + std::vector ShopItems; + struct CampaignVariables { - ::RideId RideId; - ObjectEntryIndex ShopItemId; - }; - } Campaign; - - static bool RideValueCompare(const RideId& a, const RideId& b) - { - auto valueA = 0; - auto rideA = GetRide(a); - if (rideA != nullptr) - valueA = rideA->value; - - auto valueB = 0; - auto rideB = GetRide(b); - if (rideB != nullptr) - valueB = rideB->value; - - return (valueB - valueA) < 0; - } - - static int32_t RideNameCompare(const RideId& a, const RideId& b) - { - std::string rideAName = ""; - auto rideA = GetRide(a); - if (rideA != nullptr) - rideAName = rideA->GetName(); - - std::string rideBName = ""; - auto rideB = GetRide(b); - if (rideB != nullptr) - rideBName = rideB->GetName(); - - return StrLogicalCmp(rideAName.c_str(), rideBName.c_str()) < 0; - } - - /** - * - * rct2: 0x0069E320 - */ - void GetShopItems() - { - BitSet items = {}; - for (auto& curRide : GetRideManager()) - { - auto rideEntry = curRide.GetRideEntry(); - if (rideEntry != nullptr) + int16_t campaign_type; + int16_t no_weeks; // 0x482 + union { - for (const auto itemType : rideEntry->shop_item) + ::RideId RideId; + ObjectEntryIndex ShopItemId; + }; + } Campaign; + + static bool RideValueCompare(const RideId& a, const RideId& b) + { + auto valueA = 0; + auto rideA = GetRide(a); + if (rideA != nullptr) + valueA = rideA->value; + + auto valueB = 0; + auto rideB = GetRide(b); + if (rideB != nullptr) + valueB = rideB->value; + + return (valueB - valueA) < 0; + } + + static int32_t RideNameCompare(const RideId& a, const RideId& b) + { + std::string rideAName = ""; + auto rideA = GetRide(a); + if (rideA != nullptr) + rideAName = rideA->GetName(); + + std::string rideBName = ""; + auto rideB = GetRide(b); + if (rideB != nullptr) + rideBName = rideB->GetName(); + + return StrLogicalCmp(rideAName.c_str(), rideBName.c_str()) < 0; + } + + /** + * + * rct2: 0x0069E320 + */ + void GetShopItems() + { + BitSet items = {}; + for (auto& curRide : GetRideManager()) + { + auto rideEntry = curRide.GetRideEntry(); + if (rideEntry != nullptr) { - if (itemType != ShopItem::None && GetShopItemDescriptor(itemType).IsFoodOrDrink()) + for (const auto itemType : rideEntry->shop_item) { - items[EnumValue(itemType)] = true; + if (itemType != ShopItem::None && GetShopItemDescriptor(itemType).IsFoodOrDrink()) + { + items[EnumValue(itemType)] = true; + } } } } - } - ShopItems.clear(); - for (auto i = 0; i < EnumValue(ShopItem::Count); i++) - { - if (items[i]) + ShopItems.clear(); + for (auto i = 0; i < EnumValue(ShopItem::Count); i++) { - ShopItems.push_back(ShopItem(i)); - } - } - } - -public: - void RefreshRides() - { - // Get all applicable rides - RideList.clear(); - for (const auto& curRide : GetRideManager()) - { - if (curRide.status == RideStatus::Open) - { - if (!curRide.GetRideTypeDescriptor().HasFlag( - RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY | RIDE_TYPE_FLAG_SELLS_FOOD | RIDE_TYPE_FLAG_SELLS_DRINKS - | RIDE_TYPE_FLAG_IS_TOILET)) + if (items[i]) { - RideList.push_back(curRide.id); + ShopItems.push_back(ShopItem(i)); } } } - if (RideList.size() > Dropdown::ItemsMaxSize) + public: + void RefreshRides() { - std::sort(RideList.begin(), RideList.end(), RideValueCompare); - RideList.resize(Dropdown::ItemsMaxSize); + // Get all applicable rides + RideList.clear(); + for (const auto& curRide : GetRideManager()) + { + if (curRide.status == RideStatus::Open) + { + if (!curRide.GetRideTypeDescriptor().HasFlag( + RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY | RIDE_TYPE_FLAG_SELLS_FOOD | RIDE_TYPE_FLAG_SELLS_DRINKS + | RIDE_TYPE_FLAG_IS_TOILET)) + { + RideList.push_back(curRide.id); + } + } + } + + if (RideList.size() > Dropdown::ItemsMaxSize) + { + std::sort(RideList.begin(), RideList.end(), RideValueCompare); + RideList.resize(Dropdown::ItemsMaxSize); + } + + // Sort rides by name + std::sort(RideList.begin(), RideList.end(), RideNameCompare); } - // Sort rides by name - std::sort(RideList.begin(), RideList.end(), RideNameCompare); - } - - void OnOpen() override - { - widgets = window_new_campaign_widgets; - hold_down_widgets = (1uLL << WIDX_WEEKS_INCREASE_BUTTON) | (1uLL << WIDX_WEEKS_DECREASE_BUTTON); - WindowInitScrollWidgets(*this); - } - - void SetCampaign(int16_t campaignType) - { - widgets[WIDX_TITLE].text = MarketingCampaignNames[campaignType][0]; - - // Campaign type - Campaign.campaign_type = campaignType; - - // Number of weeks - Campaign.no_weeks = 2; - - // Currently selected ride - Campaign.RideId = RideId::GetNull(); - - RefreshRides(); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - Widget* widget = &widgets[widgetIndex]; - Widget* dropdownWidget; - - switch (widgetIndex) + void OnOpen() override { - case WIDX_RIDE_DROPDOWN_BUTTON: - dropdownWidget = widget - 1; + widgets = window_new_campaign_widgets; + hold_down_widgets = (1uLL << WIDX_WEEKS_INCREASE_BUTTON) | (1uLL << WIDX_WEEKS_DECREASE_BUTTON); + WindowInitScrollWidgets(*this); + } - if (Campaign.campaign_type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) - { - GetShopItems(); - if (!ShopItems.empty()) + void SetCampaign(int16_t campaignType) + { + widgets[WIDX_TITLE].text = MarketingCampaignNames[campaignType][0]; + + // Campaign type + Campaign.campaign_type = campaignType; + + // Number of weeks + Campaign.no_weeks = 2; + + // Currently selected ride + Campaign.RideId = RideId::GetNull(); + + RefreshRides(); + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + Widget* widget = &widgets[widgetIndex]; + Widget* dropdownWidget; + + switch (widgetIndex) + { + case WIDX_RIDE_DROPDOWN_BUTTON: + dropdownWidget = widget - 1; + + if (Campaign.campaign_type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) + { + GetShopItems(); + if (!ShopItems.empty()) + { + int32_t numItems = 0; + int32_t maxSize = std::min(Dropdown::ItemsMaxSize, static_cast(ShopItems.size())); + for (int32_t i = 0; i < maxSize; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = GetShopItemDescriptor(ShopItems[i]).Naming.Plural; + numItems++; + } + + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, colours[1], 0, Dropdown::Flag::StayOpen, numItems, + dropdownWidget->width() - 3); + } + } + else { int32_t numItems = 0; - int32_t maxSize = std::min(Dropdown::ItemsMaxSize, static_cast(ShopItems.size())); - for (int32_t i = 0; i < maxSize; i++) + for (auto rideIndex : RideList) { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = GetShopItemDescriptor(ShopItems[i]).Naming.Plural; - numItems++; + auto curRide = GetRide(rideIndex); + if (curRide != nullptr) + { + // HACK until dropdown items have longer argument buffers + gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; + Formatter ft(reinterpret_cast(&gDropdownItems[numItems].Args)); + if (curRide->custom_name.empty()) + { + curRide->FormatNameTo(ft); + } + else + { + gDropdownItems[numItems].Format = STR_OPTIONS_DROPDOWN_ITEM; + ft.Add(curRide->custom_name.c_str()); + } + numItems++; + } } WindowDropdownShowTextCustomWidth( @@ -214,207 +244,179 @@ public: dropdownWidget->height() + 1, colours[1], 0, Dropdown::Flag::StayOpen, numItems, dropdownWidget->width() - 3); } - } - else - { - int32_t numItems = 0; - for (auto rideIndex : RideList) - { - auto curRide = GetRide(rideIndex); - if (curRide != nullptr) - { - // HACK until dropdown items have longer argument buffers - gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; - Formatter ft(reinterpret_cast(&gDropdownItems[numItems].Args)); - if (curRide->custom_name.empty()) - { - curRide->FormatNameTo(ft); - } - else - { - gDropdownItems[numItems].Format = STR_OPTIONS_DROPDOWN_ITEM; - ft.Add(curRide->custom_name.c_str()); - } - numItems++; - } - } - - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, numItems, dropdownWidget->width() - 3); - } - break; - // In RCT2, the maximum was 6 weeks - case WIDX_WEEKS_INCREASE_BUTTON: - Campaign.no_weeks = std::min(Campaign.no_weeks + 1, 12); - Invalidate(); - break; - case WIDX_WEEKS_DECREASE_BUTTON: - Campaign.no_weeks = std::max(Campaign.no_weeks - 1, 2); - Invalidate(); - break; - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_START_BUTTON: - { - auto gameAction = ParkMarketingAction( - Campaign.campaign_type, Campaign.RideId.ToUnderlying(), Campaign.no_weeks); - gameAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) - { - WindowCloseByClass(WindowClass::NewCampaign); - } - }); - GameActions::Execute(&gameAction); - break; + break; + // In RCT2, the maximum was 6 weeks + case WIDX_WEEKS_INCREASE_BUTTON: + Campaign.no_weeks = std::min(Campaign.no_weeks + 1, 12); + Invalidate(); + break; + case WIDX_WEEKS_DECREASE_BUTTON: + Campaign.no_weeks = std::max(Campaign.no_weeks - 1, 2); + Invalidate(); + break; } } - } - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (widgetIndex != WIDX_RIDE_DROPDOWN_BUTTON) - return; - - if (dropdownIndex < 0) - return; - - if (Campaign.campaign_type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) + void OnMouseUp(WidgetIndex widgetIndex) override { - if (static_cast(dropdownIndex) >= ShopItems.size()) - return; - - Campaign.ShopItemId = EnumValue(ShopItems[dropdownIndex]); - } - else - { - if (static_cast(dropdownIndex) >= RideList.size()) - return; - - Campaign.RideId = RideList[dropdownIndex]; - } - - Invalidate(); - } - - void OnPrepareDraw() override - { - widgets[WIDX_RIDE_LABEL].type = WindowWidgetType::Empty; - widgets[WIDX_RIDE_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_RIDE_DROPDOWN].text = STR_MARKETING_NOT_SELECTED; - switch (Campaign.campaign_type) - { - case ADVERTISING_CAMPAIGN_RIDE_FREE: - case ADVERTISING_CAMPAIGN_RIDE: - widgets[WIDX_RIDE_LABEL].type = WindowWidgetType::Label; - widgets[WIDX_RIDE_DROPDOWN].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WindowWidgetType::Button; - widgets[WIDX_RIDE_LABEL].text = STR_MARKETING_RIDE; - if (Campaign.RideId != RideId::GetNull()) + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_START_BUTTON: { - auto curRide = GetRide(Campaign.RideId); - if (curRide != nullptr) + auto gameAction = ParkMarketingAction( + Campaign.campaign_type, Campaign.RideId.ToUnderlying(), Campaign.no_weeks); + gameAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) + { + WindowCloseByClass(WindowClass::NewCampaign); + } + }); + GameActions::Execute(&gameAction); + break; + } + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (widgetIndex != WIDX_RIDE_DROPDOWN_BUTTON) + return; + + if (dropdownIndex < 0) + return; + + if (Campaign.campaign_type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) + { + if (static_cast(dropdownIndex) >= ShopItems.size()) + return; + + Campaign.ShopItemId = EnumValue(ShopItems[dropdownIndex]); + } + else + { + if (static_cast(dropdownIndex) >= RideList.size()) + return; + + Campaign.RideId = RideList[dropdownIndex]; + } + + Invalidate(); + } + + void OnPrepareDraw() override + { + widgets[WIDX_RIDE_LABEL].type = WindowWidgetType::Empty; + widgets[WIDX_RIDE_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_RIDE_DROPDOWN].text = STR_MARKETING_NOT_SELECTED; + switch (Campaign.campaign_type) + { + case ADVERTISING_CAMPAIGN_RIDE_FREE: + case ADVERTISING_CAMPAIGN_RIDE: + widgets[WIDX_RIDE_LABEL].type = WindowWidgetType::Label; + widgets[WIDX_RIDE_DROPDOWN].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WindowWidgetType::Button; + widgets[WIDX_RIDE_LABEL].text = STR_MARKETING_RIDE; + if (Campaign.RideId != RideId::GetNull()) { - widgets[WIDX_RIDE_DROPDOWN].text = STR_STRINGID; + auto curRide = GetRide(Campaign.RideId); + if (curRide != nullptr) + { + widgets[WIDX_RIDE_DROPDOWN].text = STR_STRINGID; - auto ft = Formatter::Common(); - curRide->FormatNameTo(ft); + auto ft = Formatter::Common(); + curRide->FormatNameTo(ft); + } } - } - break; - case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE: - widgets[WIDX_RIDE_LABEL].type = WindowWidgetType::Label; - widgets[WIDX_RIDE_DROPDOWN].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WindowWidgetType::Button; - widgets[WIDX_RIDE_LABEL].text = STR_MARKETING_ITEM; - if (Campaign.ShopItemId != SELECTED_ITEM_UNDEFINED) - { - widgets[WIDX_RIDE_DROPDOWN].text = GetShopItemDescriptor(ShopItem(Campaign.ShopItemId)).Naming.Plural; - } - break; + break; + case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE: + widgets[WIDX_RIDE_LABEL].type = WindowWidgetType::Label; + widgets[WIDX_RIDE_DROPDOWN].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WindowWidgetType::Button; + widgets[WIDX_RIDE_LABEL].text = STR_MARKETING_ITEM; + if (Campaign.ShopItemId != SELECTED_ITEM_UNDEFINED) + { + widgets[WIDX_RIDE_DROPDOWN].text = GetShopItemDescriptor(ShopItem(Campaign.ShopItemId)).Naming.Plural; + } + break; + } + + // Set current number of weeks spinner (moved to paint due to required parameter) + widgets[WIDX_WEEKS_SPINNER].text = STR_NONE; + + // Enable / disable start button based on ride dropdown + WidgetSetDisabled(*this, WIDX_START_BUTTON, false); + if (widgets[WIDX_RIDE_DROPDOWN].type == WindowWidgetType::DropdownMenu && Campaign.RideId == RideId::GetNull()) + WidgetSetDisabled(*this, WIDX_START_BUTTON, true); } - // Set current number of weeks spinner (moved to paint due to required parameter) - widgets[WIDX_WEEKS_SPINNER].text = STR_NONE; + void OnDraw(DrawPixelInfo& dpi) override + { + ScreenCoordsXY screenCoords{}; - // Enable / disable start button based on ride dropdown - WidgetSetDisabled(*this, WIDX_START_BUTTON, false); - if (widgets[WIDX_RIDE_DROPDOWN].type == WindowWidgetType::DropdownMenu && Campaign.RideId == RideId::GetNull()) - WidgetSetDisabled(*this, WIDX_START_BUTTON, true); - } + DrawWidgets(dpi); - void OnDraw(DrawPixelInfo& dpi) override + // Number of weeks + Widget* spinnerWidget = &widgets[WIDX_WEEKS_SPINNER]; + auto ft = Formatter(); + ft.Add(Campaign.no_weeks); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ spinnerWidget->left + 1, spinnerWidget->top }, + Campaign.no_weeks == 1 ? STR_MARKETING_1_WEEK : STR_X_WEEKS, ft, { colours[0] }); + + screenCoords = windowPos + ScreenCoordsXY{ 14, 60 }; + + // Price per week + ft = Formatter(); + ft.Add(AdvertisingCampaignPricePerWeek[Campaign.campaign_type]); + DrawTextBasic(dpi, screenCoords, STR_MARKETING_COST_PER_WEEK, ft); + screenCoords.y += 13; + + // Total price + ft = Formatter(); + ft.Add(AdvertisingCampaignPricePerWeek[Campaign.campaign_type] * Campaign.no_weeks); + DrawTextBasic(dpi, screenCoords, STR_MARKETING_TOTAL_COST, ft); + } + + void OnResize() override + { + ResizeFrame(); + } + + int16_t GetCampaignType() const + { + return Campaign.campaign_type; + } + }; + + WindowBase* WindowNewCampaignOpen(int16_t campaignType) { - ScreenCoordsXY screenCoords{}; + auto w = static_cast(WindowBringToFrontByClass(WindowClass::NewCampaign)); + if (w != nullptr) + { + if (w->GetCampaignType() == campaignType) + return w; - DrawWidgets(dpi); + WindowClose(*w); + } - // Number of weeks - Widget* spinnerWidget = &widgets[WIDX_WEEKS_SPINNER]; - auto ft = Formatter(); - ft.Add(Campaign.no_weeks); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ spinnerWidget->left + 1, spinnerWidget->top }, - Campaign.no_weeks == 1 ? STR_MARKETING_1_WEEK : STR_X_WEEKS, ft, { colours[0] }); - - screenCoords = windowPos + ScreenCoordsXY{ 14, 60 }; - - // Price per week - ft = Formatter(); - ft.Add(AdvertisingCampaignPricePerWeek[Campaign.campaign_type]); - DrawTextBasic(dpi, screenCoords, STR_MARKETING_COST_PER_WEEK, ft); - screenCoords.y += 13; - - // Total price - ft = Formatter(); - ft.Add(AdvertisingCampaignPricePerWeek[Campaign.campaign_type] * Campaign.no_weeks); - DrawTextBasic(dpi, screenCoords, STR_MARKETING_TOTAL_COST, ft); + w = WindowCreate(WindowClass::NewCampaign, WW, WH, 0); + if (w != nullptr) + { + w->SetCampaign(campaignType); + } + return w; } - void OnResize() override + void WindowCampaignRefreshRides() { - ResizeFrame(); + auto w = static_cast(WindowFindByClass(WindowClass::NewCampaign)); + if (w != nullptr) + { + w->RefreshRides(); + } } - - int16_t GetCampaignType() const - { - return Campaign.campaign_type; - } -}; - -WindowBase* WindowNewCampaignOpen(int16_t campaignType) -{ - auto w = static_cast(WindowBringToFrontByClass(WindowClass::NewCampaign)); - if (w != nullptr) - { - if (w->GetCampaignType() == campaignType) - return w; - - WindowClose(*w); - } - - w = WindowCreate(WindowClass::NewCampaign, WW, WH, 0); - if (w != nullptr) - { - w->SetCampaign(campaignType); - } - return w; -} - -void WindowCampaignRefreshRides() -{ - auto w = static_cast(WindowFindByClass(WindowClass::NewCampaign)); - if (w != nullptr) - { - w->RefreshRides(); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/NewRide.cpp b/src/openrct2-ui/windows/NewRide.cpp index 65099ed86c..d20b49db76 100644 --- a/src/openrct2-ui/windows/NewRide.cpp +++ b/src/openrct2-ui/windows/NewRide.cpp @@ -39,172 +39,172 @@ #include #include -using namespace OpenRCT2; using namespace OpenRCT2::TrackMetaData; - -static constexpr StringId WindowTitle = STR_NONE; -static constexpr int32_t WindowHeight = 382; -static constexpr int32_t WindowWidth = 601; -static constexpr int32_t RideListItemsMax = 384; -static constexpr int32_t RideTabCount = 6; -static constexpr int32_t GroupByTrackTypeWidth = 172; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WindowTitle = STR_NONE; + static constexpr int32_t WindowHeight = 382; + static constexpr int32_t WindowWidth = 601; + static constexpr int32_t RideListItemsMax = 384; + static constexpr int32_t RideTabCount = 6; + static constexpr int32_t GroupByTrackTypeWidth = 172; #pragma region Ride type view order -/** - * The order of ride types shown in the new ride window so that the order stays consistent across games and rides of the same - * type are kept together. - */ -static constexpr ride_type_t RideTypeViewOrder[] = { - // Transport rides - RIDE_TYPE_MINIATURE_RAILWAY, - RIDE_TYPE_MONORAIL, - RIDE_TYPE_SUSPENDED_MONORAIL, - RIDE_TYPE_CHAIRLIFT, - RIDE_TYPE_LIFT, + /** + * The order of ride types shown in the new ride window so that the order stays consistent across games and rides of the + * same type are kept together. + */ + static constexpr ride_type_t RideTypeViewOrder[] = { + // Transport rides + RIDE_TYPE_MINIATURE_RAILWAY, + RIDE_TYPE_MONORAIL, + RIDE_TYPE_SUSPENDED_MONORAIL, + RIDE_TYPE_CHAIRLIFT, + RIDE_TYPE_LIFT, - // Roller Coasters - RIDE_TYPE_SIDE_FRICTION_ROLLER_COASTER, - RIDE_TYPE_VIRGINIA_REEL, - RIDE_TYPE_REVERSER_ROLLER_COASTER, - RIDE_TYPE_CLASSIC_WOODEN_ROLLER_COASTER, - RIDE_TYPE_WOODEN_ROLLER_COASTER, - RIDE_TYPE_WOODEN_WILD_MOUSE, - RIDE_TYPE_STEEL_WILD_MOUSE, - RIDE_TYPE_SPINNING_WILD_MOUSE, - RIDE_TYPE_INVERTED_HAIRPIN_COASTER, - RIDE_TYPE_JUNIOR_ROLLER_COASTER, - RIDE_TYPE_CLASSIC_MINI_ROLLER_COASTER, - RIDE_TYPE_MINI_ROLLER_COASTER, - RIDE_TYPE_SPIRAL_ROLLER_COASTER, - RIDE_TYPE_MINE_TRAIN_COASTER, - RIDE_TYPE_LOOPING_ROLLER_COASTER, - RIDE_TYPE_STAND_UP_ROLLER_COASTER, - RIDE_TYPE_CORKSCREW_ROLLER_COASTER, - RIDE_TYPE_HYPERCOASTER, - RIDE_TYPE_LIM_LAUNCHED_ROLLER_COASTER, - RIDE_TYPE_TWISTER_ROLLER_COASTER, - RIDE_TYPE_HYPER_TWISTER, - RIDE_TYPE_GIGA_COASTER, - RIDE_TYPE_SUSPENDED_SWINGING_COASTER, - RIDE_TYPE_COMPACT_INVERTED_COASTER, - RIDE_TYPE_INVERTED_ROLLER_COASTER, - RIDE_TYPE_INVERTED_IMPULSE_COASTER, - RIDE_TYPE_MINI_SUSPENDED_COASTER, - RIDE_TYPE_STEEPLECHASE, - RIDE_TYPE_BOBSLEIGH_COASTER, - RIDE_TYPE_MINE_RIDE, - RIDE_TYPE_HEARTLINE_TWISTER_COASTER, - RIDE_TYPE_LAY_DOWN_ROLLER_COASTER, - RIDE_TYPE_FLYING_ROLLER_COASTER, - RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER, - RIDE_TYPE_REVERSE_FREEFALL_COASTER, - RIDE_TYPE_VERTICAL_DROP_ROLLER_COASTER, - RIDE_TYPE_AIR_POWERED_VERTICAL_COASTER, - RIDE_TYPE_HYBRID_COASTER, - RIDE_TYPE_SINGLE_RAIL_ROLLER_COASTER, - RIDE_TYPE_ALPINE_COASTER, + // Roller Coasters + RIDE_TYPE_SIDE_FRICTION_ROLLER_COASTER, + RIDE_TYPE_VIRGINIA_REEL, + RIDE_TYPE_REVERSER_ROLLER_COASTER, + RIDE_TYPE_CLASSIC_WOODEN_ROLLER_COASTER, + RIDE_TYPE_WOODEN_ROLLER_COASTER, + RIDE_TYPE_WOODEN_WILD_MOUSE, + RIDE_TYPE_STEEL_WILD_MOUSE, + RIDE_TYPE_SPINNING_WILD_MOUSE, + RIDE_TYPE_INVERTED_HAIRPIN_COASTER, + RIDE_TYPE_JUNIOR_ROLLER_COASTER, + RIDE_TYPE_CLASSIC_MINI_ROLLER_COASTER, + RIDE_TYPE_MINI_ROLLER_COASTER, + RIDE_TYPE_SPIRAL_ROLLER_COASTER, + RIDE_TYPE_MINE_TRAIN_COASTER, + RIDE_TYPE_LOOPING_ROLLER_COASTER, + RIDE_TYPE_STAND_UP_ROLLER_COASTER, + RIDE_TYPE_CORKSCREW_ROLLER_COASTER, + RIDE_TYPE_HYPERCOASTER, + RIDE_TYPE_LIM_LAUNCHED_ROLLER_COASTER, + RIDE_TYPE_TWISTER_ROLLER_COASTER, + RIDE_TYPE_HYPER_TWISTER, + RIDE_TYPE_GIGA_COASTER, + RIDE_TYPE_SUSPENDED_SWINGING_COASTER, + RIDE_TYPE_COMPACT_INVERTED_COASTER, + RIDE_TYPE_INVERTED_ROLLER_COASTER, + RIDE_TYPE_INVERTED_IMPULSE_COASTER, + RIDE_TYPE_MINI_SUSPENDED_COASTER, + RIDE_TYPE_STEEPLECHASE, + RIDE_TYPE_BOBSLEIGH_COASTER, + RIDE_TYPE_MINE_RIDE, + RIDE_TYPE_HEARTLINE_TWISTER_COASTER, + RIDE_TYPE_LAY_DOWN_ROLLER_COASTER, + RIDE_TYPE_FLYING_ROLLER_COASTER, + RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER, + RIDE_TYPE_REVERSE_FREEFALL_COASTER, + RIDE_TYPE_VERTICAL_DROP_ROLLER_COASTER, + RIDE_TYPE_AIR_POWERED_VERTICAL_COASTER, + RIDE_TYPE_HYBRID_COASTER, + RIDE_TYPE_SINGLE_RAIL_ROLLER_COASTER, + RIDE_TYPE_ALPINE_COASTER, - // Gentle rides - RIDE_TYPE_MONORAIL_CYCLES, - RIDE_TYPE_CROOKED_HOUSE, - RIDE_TYPE_HAUNTED_HOUSE, - RIDE_TYPE_FERRIS_WHEEL, - RIDE_TYPE_MAZE, - RIDE_TYPE_MERRY_GO_ROUND, - RIDE_TYPE_MINI_GOLF, - RIDE_TYPE_OBSERVATION_TOWER, - RIDE_TYPE_CAR_RIDE, - RIDE_TYPE_MONSTER_TRUCKS, - RIDE_TYPE_MINI_HELICOPTERS, - RIDE_TYPE_SPIRAL_SLIDE, - RIDE_TYPE_DODGEMS, - RIDE_TYPE_SPACE_RINGS, - RIDE_TYPE_CIRCUS, - RIDE_TYPE_GHOST_TRAIN, - RIDE_TYPE_FLYING_SAUCERS, + // Gentle rides + RIDE_TYPE_MONORAIL_CYCLES, + RIDE_TYPE_CROOKED_HOUSE, + RIDE_TYPE_HAUNTED_HOUSE, + RIDE_TYPE_FERRIS_WHEEL, + RIDE_TYPE_MAZE, + RIDE_TYPE_MERRY_GO_ROUND, + RIDE_TYPE_MINI_GOLF, + RIDE_TYPE_OBSERVATION_TOWER, + RIDE_TYPE_CAR_RIDE, + RIDE_TYPE_MONSTER_TRUCKS, + RIDE_TYPE_MINI_HELICOPTERS, + RIDE_TYPE_SPIRAL_SLIDE, + RIDE_TYPE_DODGEMS, + RIDE_TYPE_SPACE_RINGS, + RIDE_TYPE_CIRCUS, + RIDE_TYPE_GHOST_TRAIN, + RIDE_TYPE_FLYING_SAUCERS, - // Thrill rides - RIDE_TYPE_TWIST, - RIDE_TYPE_MAGIC_CARPET, - RIDE_TYPE_LAUNCHED_FREEFALL, - RIDE_TYPE_SWINGING_SHIP, - RIDE_TYPE_GO_KARTS, - RIDE_TYPE_SWINGING_INVERTER_SHIP, - RIDE_TYPE_MOTION_SIMULATOR, - RIDE_TYPE_3D_CINEMA, - RIDE_TYPE_TOP_SPIN, - RIDE_TYPE_ROTO_DROP, - RIDE_TYPE_ENTERPRISE, + // Thrill rides + RIDE_TYPE_TWIST, + RIDE_TYPE_MAGIC_CARPET, + RIDE_TYPE_LAUNCHED_FREEFALL, + RIDE_TYPE_SWINGING_SHIP, + RIDE_TYPE_GO_KARTS, + RIDE_TYPE_SWINGING_INVERTER_SHIP, + RIDE_TYPE_MOTION_SIMULATOR, + RIDE_TYPE_3D_CINEMA, + RIDE_TYPE_TOP_SPIN, + RIDE_TYPE_ROTO_DROP, + RIDE_TYPE_ENTERPRISE, - // Water rides - RIDE_TYPE_DINGHY_SLIDE, - RIDE_TYPE_LOG_FLUME, - RIDE_TYPE_RIVER_RAPIDS, - RIDE_TYPE_SPLASH_BOATS, - RIDE_TYPE_SUBMARINE_RIDE, - RIDE_TYPE_BOAT_HIRE, - RIDE_TYPE_RIVER_RAFTS, - RIDE_TYPE_WATER_COASTER, + // Water rides + RIDE_TYPE_DINGHY_SLIDE, + RIDE_TYPE_LOG_FLUME, + RIDE_TYPE_RIVER_RAPIDS, + RIDE_TYPE_SPLASH_BOATS, + RIDE_TYPE_SUBMARINE_RIDE, + RIDE_TYPE_BOAT_HIRE, + RIDE_TYPE_RIVER_RAFTS, + RIDE_TYPE_WATER_COASTER, - // Shops / stalls - RIDE_TYPE_FOOD_STALL, - RIDE_TYPE_1D, - RIDE_TYPE_DRINK_STALL, - RIDE_TYPE_1F, - RIDE_TYPE_SHOP, - RIDE_TYPE_22, - RIDE_TYPE_INFORMATION_KIOSK, - RIDE_TYPE_FIRST_AID, - RIDE_TYPE_CASH_MACHINE, - RIDE_TYPE_TOILETS, -}; + // Shops / stalls + RIDE_TYPE_FOOD_STALL, + RIDE_TYPE_1D, + RIDE_TYPE_DRINK_STALL, + RIDE_TYPE_1F, + RIDE_TYPE_SHOP, + RIDE_TYPE_22, + RIDE_TYPE_INFORMATION_KIOSK, + RIDE_TYPE_FIRST_AID, + RIDE_TYPE_CASH_MACHINE, + RIDE_TYPE_TOILETS, + }; #pragma endregion -enum NewRideTabId -{ - TRANSPORT_TAB, - GENTLE_TAB, - ROLLER_COASTER_TAB, - THRILL_TAB, - WATER_TAB, - SHOP_TAB, - RESEARCH_TAB, - TAB_COUNT, -}; + enum NewRideTabId + { + TRANSPORT_TAB, + GENTLE_TAB, + ROLLER_COASTER_TAB, + THRILL_TAB, + WATER_TAB, + SHOP_TAB, + RESEARCH_TAB, + TAB_COUNT, + }; #pragma region Widgets -enum -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PAGE_BACKGROUND, - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_TAB_3, - WIDX_TAB_4, - WIDX_TAB_5, - WIDX_TAB_6, - WIDX_TAB_7, - WIDX_RIDE_LIST, + enum + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PAGE_BACKGROUND, + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_TAB_3, + WIDX_TAB_4, + WIDX_TAB_5, + WIDX_TAB_6, + WIDX_TAB_7, + WIDX_RIDE_LIST, - WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP, - WIDX_LAST_DEVELOPMENT_GROUP, - WIDX_LAST_DEVELOPMENT_BUTTON, - WIDX_RESEARCH_FUNDING_BUTTON, + WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP, + WIDX_LAST_DEVELOPMENT_GROUP, + WIDX_LAST_DEVELOPMENT_BUTTON, + WIDX_RESEARCH_FUNDING_BUTTON, - WIDX_FILTER_TEXT_BOX, - WIDX_FILTER_CLEAR_BUTTON, - WIDX_GROUP_BY_TRACK_TYPE, -}; + WIDX_FILTER_TEXT_BOX, + WIDX_FILTER_CLEAR_BUTTON, + WIDX_GROUP_BY_TRACK_TYPE, + }; -static constexpr ScreenCoordsXY GroupByTrackTypeOrigin{ WindowWidth - 8 - GroupByTrackTypeWidth, 47 }; -static constexpr ScreenSize GroupTrackTypeSize{ GroupByTrackTypeWidth, 14 }; + static constexpr ScreenCoordsXY GroupByTrackTypeOrigin{ WindowWidth - 8 - GroupByTrackTypeWidth, 47 }; + static constexpr ScreenSize GroupTrackTypeSize{ GroupByTrackTypeWidth, 14 }; -// clang-format off + // clang-format off static Widget window_new_ride_widgets[] = { WINDOW_SHIM(WindowTitle, WindowWidth, WindowHeight), MakeWidget({ 0, 43}, {601, 339}, WindowWidgetType::Resize, WindowColour::Secondary ), @@ -225,861 +225,863 @@ static Widget window_new_ride_widgets[] = { MakeWidget(GroupByTrackTypeOrigin, GroupTrackTypeSize, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_GROUP_BY_TRACK_TYPE, STR_GROUP_BY_TRACK_TYPE_TIP ), kWidgetsEnd, }; -// clang-format on + // clang-format on #pragma endregion -static constexpr StringId RideTitles[] = { - STR_NEW_TRANSPORT_RIDES, // TRANSPORT_TAB - STR_NEW_GENTLE_RIDES, // GENTLE_TAB - STR_NEW_ROLLER_COASTERS, // ROLLER_COASTER_TAB - STR_NEW_THRILL_RIDES, // THRILL_TAB - STR_NEW_WATER_RIDES, // WATER_TAB - STR_NEW_SHOPS_STALLS, // SHOP_TAB - STR_RESEARCH_AND_DEVELOPMENT, // RESEARCH_TAB -}; -static_assert(std::size(RideTitles) == TAB_COUNT); + static constexpr StringId RideTitles[] = { + STR_NEW_TRANSPORT_RIDES, // TRANSPORT_TAB + STR_NEW_GENTLE_RIDES, // GENTLE_TAB + STR_NEW_ROLLER_COASTERS, // ROLLER_COASTER_TAB + STR_NEW_THRILL_RIDES, // THRILL_TAB + STR_NEW_WATER_RIDES, // WATER_TAB + STR_NEW_SHOPS_STALLS, // SHOP_TAB + STR_RESEARCH_AND_DEVELOPMENT, // RESEARCH_TAB + }; + static_assert(std::size(RideTitles) == TAB_COUNT); -static constexpr int32_t TabAnimationLoops[] = { - 20, // TRANSPORT_TAB - 32, // GENTLE_TAB - 10, // ROLLER_COASTER_TAB - 72, // THRILL_TAB - 24, // WATER_TAB - 28, // SHOP_TAB - 16, // RESEARCH_TAB -}; -static_assert(std::size(TabAnimationLoops) == TAB_COUNT); + static constexpr int32_t TabAnimationLoops[] = { + 20, // TRANSPORT_TAB + 32, // GENTLE_TAB + 10, // ROLLER_COASTER_TAB + 72, // THRILL_TAB + 24, // WATER_TAB + 28, // SHOP_TAB + 16, // RESEARCH_TAB + }; + static_assert(std::size(TabAnimationLoops) == TAB_COUNT); -static constexpr int32_t TabAnimationDivisor[] = { - 4, // TRANSPORT_TAB - 8, // GENTLE_TAB - 2, // ROLLER_COASTER_TAB - 4, // THRILL_TAB - 4, // WATER_TAB - 4, // SHOP_TAB - 2, // RESEARCH_TAB -}; -static_assert(std::size(TabAnimationDivisor) == TAB_COUNT); + static constexpr int32_t TabAnimationDivisor[] = { + 4, // TRANSPORT_TAB + 8, // GENTLE_TAB + 2, // ROLLER_COASTER_TAB + 4, // THRILL_TAB + 4, // WATER_TAB + 4, // SHOP_TAB + 2, // RESEARCH_TAB + }; + static_assert(std::size(TabAnimationDivisor) == TAB_COUNT); -static constexpr int32_t ThrillRidesTabAnimationSequence[] = { - 5, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, -}; + static constexpr int32_t ThrillRidesTabAnimationSequence[] = { + 5, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, + }; -class NewRideWindow final : public Window -{ -private: - static NewRideTabId _currentTab; - static uint_fast16_t _windowNewRideTabScroll[RideTabCount]; - u8string _filter; - u8string _vehicleAvailability{}; - RideSelection _lastTrackDesignCountRideType{}; - int32_t _lastTrackDesignCount{}; - RideSelection _windowNewRideListItems[RideListItemsMax]{}; - struct NewRideVariables + class NewRideWindow final : public Window { - RideSelection SelectedRide; - RideSelection HighlightedRide; - uint16_t SelectedRideCountdown; - } _newRideVars{}; - -public: - static void SetOpeningPage(NewRideTabId tab) - { - _currentTab = tab; - } - - static void ResetTabScrolls() - { - std::fill_n(NewRideWindow::_windowNewRideTabScroll, RideTabCount, 0); - } - - void OnOpen() override - { - widgets = window_new_ride_widgets; - PopulateRideList(); - InitScrollWidgets(); - _filter.clear(); - - frame_no = 0; - _newRideVars.SelectedRide = { RIDE_TYPE_NULL, OBJECT_ENTRY_INDEX_NULL }; - _lastTrackDesignCountRideType.Type = RIDE_TYPE_NULL; - _lastTrackDesignCountRideType.EntryIndex = OBJECT_ENTRY_INDEX_NULL; - - width = 1; - RefreshWidgetSizing(); - - if (_currentTab < RESEARCH_TAB) + private: + static NewRideTabId _currentTab; + static uint_fast16_t _windowNewRideTabScroll[RideTabCount]; + u8string _filter; + u8string _vehicleAvailability{}; + RideSelection _lastTrackDesignCountRideType{}; + int32_t _lastTrackDesignCount{}; + RideSelection _windowNewRideListItems[RideListItemsMax]{}; + struct NewRideVariables { - SetPage(_currentTab); - RestoreScrollPositionForCurrentTab(); - } - } + RideSelection SelectedRide; + RideSelection HighlightedRide; + uint16_t SelectedRideCountdown; + } _newRideVars{}; + + public: + static void SetOpeningPage(NewRideTabId tab) + { + _currentTab = tab; + } + + static void ResetTabScrolls() + { + std::fill_n(NewRideWindow::_windowNewRideTabScroll, RideTabCount, 0); + } + + void OnOpen() override + { + widgets = window_new_ride_widgets; + PopulateRideList(); + InitScrollWidgets(); + _filter.clear(); - void OnUpdate() override - { - frame_no++; - if (frame_no >= TabAnimationLoops[_currentTab]) frame_no = 0; + _newRideVars.SelectedRide = { RIDE_TYPE_NULL, OBJECT_ENTRY_INDEX_NULL }; + _lastTrackDesignCountRideType.Type = RIDE_TYPE_NULL; + _lastTrackDesignCountRideType.EntryIndex = OBJECT_ENTRY_INDEX_NULL; - WidgetInvalidate(*this, WIDX_TAB_1 + static_cast(_currentTab)); + width = 1; + RefreshWidgetSizing(); - if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) - { - WindowUpdateTextboxCaret(); - WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX); - } - - if (_newRideVars.SelectedRide.Type != RIDE_TYPE_NULL && _newRideVars.SelectedRideCountdown-- == 0) - { - RideSelect(); - } - - PopulateRideList(); - - if (_currentTab < RESEARCH_TAB) - { - _windowNewRideTabScroll[_currentTab] = scrolls[0].v_top; - - // Remove highlight when mouse leaves rides list - if (!WidgetIsHighlighted(*this, WIDX_RIDE_LIST)) + if (_currentTab < RESEARCH_TAB) { - _newRideVars.HighlightedRide = { RIDE_TYPE_NULL, OBJECT_ENTRY_INDEX_NULL }; - WidgetInvalidate(*this, WIDX_RIDE_LIST); - } - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_LAST_DEVELOPMENT_BUTTON: - WindowResearchDevelopmentMouseUp(widgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - break; - case WIDX_RESEARCH_FUNDING_BUTTON: - ContextOpenWindowView(WV_FINANCES_RESEARCH); - break; - case WIDX_GROUP_BY_TRACK_TYPE: - gConfigInterface.ListRideVehiclesSeparately = !gConfigInterface.ListRideVehiclesSeparately; - ConfigSaveDefault(); SetPage(_currentTab); - break; - case WIDX_FILTER_TEXT_BOX: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _filter.data(), TEXT_INPUT_SIZE); - break; - case WIDX_FILTER_CLEAR_BUTTON: - _filter.clear(); - scrolls->v_top = 0; - Invalidate(); - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - if (widgetIndex >= WIDX_TAB_1 && widgetIndex <= WIDX_TAB_7) - { - SetPage(widgetIndex - WIDX_TAB_1); - } - } - - void OnPrepareDraw() override - { - SetPressedTab(); - - if (!gConfigInterface.ListRideVehiclesSeparately) - pressed_widgets |= (1LL << WIDX_GROUP_BY_TRACK_TYPE); - else - pressed_widgets &= ~(1LL << WIDX_GROUP_BY_TRACK_TYPE); - - widgets[WIDX_TITLE].text = RideTitles[_currentTab]; - widgets[WIDX_TAB_7].type = WindowWidgetType::Tab; - widgets[WIDX_FILTER_TEXT_BOX].string = _filter.data(); - - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - widgets[WIDX_TAB_7].type = WindowWidgetType::Empty; - - if (_currentTab == RESEARCH_TAB) - { - WindowResearchDevelopmentPrepareDraw(this, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - } - - const auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); - auto string = ls.GetString(STR_GROUP_BY_TRACK_TYPE); - auto strWidth = GfxGetStringWidth(string, FontStyle::Medium); - auto localizedGroupByTrackTypeWidth = strWidth + 14; - widgets[WIDX_GROUP_BY_TRACK_TYPE].left = width - 8 - localizedGroupByTrackTypeWidth; - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - if (_currentTab != RESEARCH_TAB) - { - RideSelection item = _newRideVars.HighlightedRide; - if (item.Type != RIDE_TYPE_NULL || item.EntryIndex != OBJECT_ENTRY_INDEX_NULL) - DrawRideInformation(dpi, item, windowPos + ScreenCoordsXY{ 3, height - 64 }, width - 6); - } - else - { - WindowResearchDevelopmentDraw(this, dpi, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - RideSelection* listItem = _windowNewRideListItems; - - int32_t count = 0; - while (listItem->Type != RIDE_TYPE_NULL || listItem->EntryIndex != OBJECT_ENTRY_INDEX_NULL) - { - count++; - listItem++; - } - return { widgets[WIDX_RIDE_LIST].width(), ((count + 4) / 5) * 116 }; - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - RideSelection item = ScrollGetRideListItemAt(screenCoords); - if (_newRideVars.HighlightedRide == item) - { - return; - } - - _newRideVars.HighlightedRide = item; - Invalidate(); - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - RideSelection item = ScrollGetRideListItemAt(screenCoords); - if (item.Type == RIDE_TYPE_NULL && item.EntryIndex == OBJECT_ENTRY_INDEX_NULL) - { - return; - } - - _newRideVars.SelectedRide = item; - - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); - _newRideVars.SelectedRideCountdown = 8; - Invalidate(); - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - if (_currentTab == RESEARCH_TAB) - { - return; - } - - GfxClear(&dpi, ColourMapA[colours[1]].mid_light); - - ScreenCoordsXY coords{ 1, 1 }; - RideSelection* listItem = _windowNewRideListItems; - while (listItem->Type != RIDE_TYPE_NULL || listItem->EntryIndex != OBJECT_ENTRY_INDEX_NULL) - { - // Draw flat button rectangle - int32_t buttonFlags = 0; - if (_newRideVars.SelectedRide == *listItem) - buttonFlags |= INSET_RECT_FLAG_BORDER_INSET; - if (_newRideVars.HighlightedRide == *listItem || buttonFlags != 0) - GfxFillRectInset( - dpi, { coords, coords + ScreenCoordsXY{ 115, 115 } }, colours[1], - INSET_RECT_FLAG_FILL_MID_LIGHT | buttonFlags); - - // Draw ride image with feathered border - auto mask = ImageId(SPR_NEW_RIDE_MASK); - auto rideImage = ImageId(GetRideImage(*listItem)); - GfxDrawSpriteRawMasked(&dpi, coords + ScreenCoordsXY{ 2, 2 }, mask, rideImage); - - // Next position - coords.x += 116; - if (coords.x >= 116 * 5 + 1) - { - coords.x = 1; - coords.y += 116; - } - - // Next item - listItem++; - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex != WIDX_FILTER_TEXT_BOX) - return; - - if (text == _filter) - return; - - _filter.assign(text); - - scrolls->v_top = 0; - Invalidate(); - } - - void SetPage(int tab) - { - if (tab >= TRANSPORT_TAB && tab < TAB_COUNT) - { - SetPage(static_cast(tab)); - } - } - - void SetPage(NewRideTabId tab) - { - _currentTab = tab; - frame_no = 0; - _newRideVars.HighlightedRide = { RIDE_TYPE_NULL, OBJECT_ENTRY_INDEX_NULL }; - _newRideVars.SelectedRideCountdown = std::numeric_limits::max(); - PopulateRideList(); - RefreshWidgetSizing(); - Invalidate(); - - if (tab < RESEARCH_TAB) - { - RestoreScrollPositionForCurrentTab(); - } - } - -private: - void RideSelect() - { - RideSelection item = _newRideVars.SelectedRide; - if (item.Type == RIDE_TYPE_NULL) - { - return; - } - - Close(); - WindowCloseConstructionWindows(); - - auto count = GetNumTrackDesigns(item); - if (count > 0) - { - auto intent = Intent(WindowClass::TrackDesignList); - intent.PutExtra(INTENT_EXTRA_RIDE_TYPE, item.Type); - intent.PutExtra(INTENT_EXTRA_RIDE_ENTRY_INDEX, item.EntryIndex); - ContextOpenIntent(&intent); - return; - } - - RideConstructNew(item); - } - - int32_t GetNumTrackDesigns(RideSelection item) - { - // Cache the result - if (item.Type == _lastTrackDesignCountRideType.Type && item.EntryIndex == _lastTrackDesignCountRideType.EntryIndex) - { - return _lastTrackDesignCount; - } - - std::string entryName; - - if (item.Type < 0x80) - { - if (GetRideTypeDescriptor(item.Type).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) - { - entryName = GetRideEntryName(item.EntryIndex); + RestoreScrollPositionForCurrentTab(); } } - auto repo = OpenRCT2::GetContext()->GetTrackDesignRepository(); - _lastTrackDesignCount = static_cast(repo->GetCountForObjectEntry(item.Type, entryName)); - _lastTrackDesignCountRideType = item; - return _lastTrackDesignCount; - } - - void UpdateVehicleAvailability(ObjectEntryIndex rideType) - { - _vehicleAvailability.clear(); - if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + void OnUpdate() override { - return; - } + frame_no++; + if (frame_no >= TabAnimationLoops[_currentTab]) + frame_no = 0; - auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); - auto& rideEntries = objManager.GetAllRideEntries(rideType); - auto isFirst = true; - for (auto rideEntryIndex : rideEntries) - { - auto currentRideEntry = GetRideEntryByIndex(rideEntryIndex); + WidgetInvalidate(*this, WIDX_TAB_1 + static_cast(_currentTab)); - // Skip if vehicle type is not invented yet - if (!RideEntryIsInvented(rideEntryIndex) && !GetGameState().Cheats.IgnoreResearchStatus) + if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) { - continue; + WindowUpdateTextboxCaret(); + WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX); } - // Append comma if not the first iteration - if (!isFirst) + if (_newRideVars.SelectedRide.Type != RIDE_TYPE_NULL && _newRideVars.SelectedRideCountdown-- == 0) { - _vehicleAvailability += u8", "; + RideSelect(); } - // Append vehicle name - auto vehicleName = LanguageGetString(currentRideEntry->naming.Name); - _vehicleAvailability += vehicleName; + PopulateRideList(); - isFirst = false; - } - } - - ImageIndex GetRideImage(RideSelection rideSelection) - { - auto& objMgr = OpenRCT2::GetContext()->GetObjectManager(); - auto obj = static_cast(objMgr.GetLoadedObject(ObjectType::Ride, rideSelection.EntryIndex)); - return obj == nullptr ? ImageIndexUndefined : obj->GetPreviewImage(rideSelection.Type); - } - - void PopulateRideList() - { - uint8_t currentCategory = _currentTab; - RideSelection* nextListItem = _windowNewRideListItems; - RideSelection* listEnd = _windowNewRideListItems + RideListItemsMax; - - // For each ride type in the view order list - for (int32_t i = 0; i < static_cast(std::size(RideTypeViewOrder)); i++) - { - auto rideType = RideTypeViewOrder[i]; - if (rideType == RIDE_TYPE_NULL) - continue; - - if (GetRideTypeDescriptor(rideType).Category != currentCategory) - continue; - - nextListItem = IterateOverRideType(rideType, nextListItem, listEnd); - } - - nextListItem->Type = RIDE_TYPE_NULL; - nextListItem->EntryIndex = OBJECT_ENTRY_INDEX_NULL; - } - - RideSelection* IterateOverRideType(ride_type_t rideType, RideSelection* nextListItem, RideSelection* listEnd) - { - bool buttonForRideTypeCreated = false; - bool allowDrawingOverLastButton = false; - - uint8_t highestVehiclePriority = 0; - - // For each ride entry for this ride type - auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); - auto& rideEntries = objManager.GetAllRideEntries(rideType); - for (auto rideEntryIndex : rideEntries) - { - // Skip if vehicle type is not invented yet - if (!RideEntryIsInvented(rideEntryIndex) && !GetGameState().Cheats.IgnoreResearchStatus) - continue; - - // Ride entries - const auto* rideEntry = GetRideEntryByIndex(rideEntryIndex); - - // Skip if the vehicle isn't the preferred vehicle for this generic track type - if (!gConfigInterface.ListRideVehiclesSeparately - && !GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY) - && highestVehiclePriority > rideEntry->BuildMenuPriority) + if (_currentTab < RESEARCH_TAB) { - continue; - } + _windowNewRideTabScroll[_currentTab] = scrolls[0].v_top; - if (!IsFiltered(*rideEntry)) - { - continue; - } - - highestVehiclePriority = rideEntry->BuildMenuPriority; - - // Determines how and where to draw a button for this ride type/vehicle. - if (gConfigInterface.ListRideVehiclesSeparately - || GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) - { - // Separate, draw apart - allowDrawingOverLastButton = false; - - if (nextListItem >= listEnd) - continue; - - nextListItem->Type = rideType; - nextListItem->EntryIndex = rideEntryIndex; - nextListItem++; - } - else if (!buttonForRideTypeCreated) - { - // Non-separate, draw-apart - buttonForRideTypeCreated = true; - allowDrawingOverLastButton = true; - - if (nextListItem >= listEnd) - continue; - - nextListItem->Type = rideType; - nextListItem->EntryIndex = rideEntryIndex; - nextListItem++; - } - else if (allowDrawingOverLastButton) - { - // Non-separate, draw over previous - if (rideType == rideEntry->ride_type[0]) + // Remove highlight when mouse leaves rides list + if (!WidgetIsHighlighted(*this, WIDX_RIDE_LIST)) { - nextListItem--; - nextListItem->Type = rideType; - nextListItem->EntryIndex = rideEntryIndex; - nextListItem++; + _newRideVars.HighlightedRide = { RIDE_TYPE_NULL, OBJECT_ENTRY_INDEX_NULL }; + WidgetInvalidate(*this, WIDX_RIDE_LIST); } } } - return nextListItem; - } - - bool IsFiltered(const RideObjectEntry& rideEntry) - { - if (_filter.empty()) - return true; - - return IsFilterInRideType(rideEntry) || IsFilterInRideName(rideEntry) || IsFilterInIdentifier(rideEntry) - || IsFilterInAuthors(rideEntry) || IsFilterInFilename(rideEntry); - } - - bool IsFilterInRideType(const RideObjectEntry& rideEntry) - { - auto rideTypeName = GetRideNaming(rideEntry.ride_type[0], rideEntry).Name; - return String::Contains(u8string_view(LanguageGetString(rideTypeName)), _filter, true); - } - - bool IsFilterInRideName(const RideObjectEntry& rideEntry) - { - auto rideName = rideEntry.naming.Name; - return String::Contains(u8string_view(LanguageGetString(rideName)), _filter, true); - } - - bool IsFilterInAuthors(const RideObjectEntry& rideEntry) - { - auto rideObject = static_cast(rideEntry.obj); - auto authors = rideObject->GetAuthors(); - - for (auto author : authors) - if (String::Contains(author, _filter, true)) - return true; - - return false; - } - - bool IsFilterInIdentifier(const RideObjectEntry& rideEntry) - { - auto rideObject = static_cast(rideEntry.obj); - auto objectName = rideObject->GetObjectEntry().GetName(); - - return String::Contains(objectName, _filter, true); - } - - bool IsFilterInFilename(const RideObjectEntry& rideEntry) - { - auto rideObject = static_cast(rideEntry.obj); - auto repoItem = ObjectRepositoryFindObjectByEntry(&(rideObject->GetObjectEntry())); - - return String::Contains(repoItem->Path, _filter, true); - } - - void SetPressedTab() - { - int32_t i{}; - for (i = 0; i < TAB_COUNT; i++) + void OnMouseUp(WidgetIndex widgetIndex) override { - pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); - } - pressed_widgets |= 1LL << (WIDX_TAB_1 + static_cast(_currentTab)); - } - - void RefreshWidgetSizing() - { - int32_t newWidth{}, newHeight{}; - - if (_currentTab < SHOP_TAB) - { - disabled_widgets &= ~(1 << WIDX_GROUP_BY_TRACK_TYPE); - } - else - { - disabled_widgets |= 1LL << WIDX_GROUP_BY_TRACK_TYPE; - } - - // Show or hide unrelated widgets - - if (_currentTab < RESEARCH_TAB) - { - widgets[WIDX_GROUP_BY_TRACK_TYPE].type = WindowWidgetType::Checkbox; - } - else - { - widgets[WIDX_GROUP_BY_TRACK_TYPE].type = WindowWidgetType::Empty; - } - - if (_currentTab != RESEARCH_TAB) - { - widgets[WIDX_RIDE_LIST].type = WindowWidgetType::Scroll; - widgets[WIDX_FILTER_TEXT_BOX].type = WindowWidgetType::TextBox; - widgets[WIDX_FILTER_CLEAR_BUTTON].type = WindowWidgetType::Button; - widgets[WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP].type = WindowWidgetType::Empty; - widgets[WIDX_LAST_DEVELOPMENT_GROUP].type = WindowWidgetType::Empty; - widgets[WIDX_LAST_DEVELOPMENT_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_RESEARCH_FUNDING_BUTTON].type = WindowWidgetType::Empty; - - newWidth = WindowWidth; - newHeight = WindowHeight; - } - else - { - widgets[WIDX_RIDE_LIST].type = WindowWidgetType::Empty; - widgets[WIDX_FILTER_TEXT_BOX].type = WindowWidgetType::Empty; - widgets[WIDX_FILTER_CLEAR_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP].type = WindowWidgetType::Groupbox; - widgets[WIDX_LAST_DEVELOPMENT_GROUP].type = WindowWidgetType::Groupbox; - widgets[WIDX_LAST_DEVELOPMENT_BUTTON].type = WindowWidgetType::FlatBtn; - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - widgets[WIDX_RESEARCH_FUNDING_BUTTON].type = WindowWidgetType::FlatBtn; - - newWidth = 300; - newHeight = 196; - } - - // Handle new window size - if (width != newWidth || height != newHeight) - { - Invalidate(); - - // Resize widgets to new window size - width = newWidth; - height = newHeight; - ResizeFrameWithPage(); - widgets[WIDX_GROUP_BY_TRACK_TYPE].left = newWidth - 8 - GroupByTrackTypeWidth; - widgets[WIDX_GROUP_BY_TRACK_TYPE].right = newWidth - 8; - - Invalidate(); - } - - InitScrollWidgets(); - } - - RideSelection ScrollGetRideListItemAt(const ScreenCoordsXY& screenCoords) - { - RideSelection result; - result.Type = RIDE_TYPE_NULL; - result.EntryIndex = OBJECT_ENTRY_INDEX_NULL; - - if (screenCoords.x <= 0 || screenCoords.y <= 0) - return result; - - int32_t column = screenCoords.x / 116; - int32_t row = screenCoords.y / 116; - if (column >= 5) - return result; - - int32_t index = column + (row * 5); - - RideSelection* listItem = _windowNewRideListItems; - while (listItem->Type != RIDE_TYPE_NULL || listItem->EntryIndex != OBJECT_ENTRY_INDEX_NULL) - { - if (index-- == 0) + switch (widgetIndex) { - return *listItem; + case WIDX_CLOSE: + Close(); + break; + case WIDX_LAST_DEVELOPMENT_BUTTON: + WindowResearchDevelopmentMouseUp(widgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); + break; + case WIDX_RESEARCH_FUNDING_BUTTON: + ContextOpenWindowView(WV_FINANCES_RESEARCH); + break; + case WIDX_GROUP_BY_TRACK_TYPE: + gConfigInterface.ListRideVehiclesSeparately = !gConfigInterface.ListRideVehiclesSeparately; + ConfigSaveDefault(); + SetPage(_currentTab); + break; + case WIDX_FILTER_TEXT_BOX: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _filter.data(), TEXT_INPUT_SIZE); + break; + case WIDX_FILTER_CLEAR_BUTTON: + _filter.clear(); + scrolls->v_top = 0; + Invalidate(); + break; } - listItem++; } - return result; - } - - void RestoreScrollPositionForCurrentTab() - { - assert(static_cast(_currentTab) < std::size(_windowNewRideTabScroll)); - auto& currentTabScroll = _windowNewRideTabScroll[_currentTab]; - - // Get maximum scroll height - ScreenSize scrollSize = OnScrollGetSize(0); - const Widget& listWidget = widgets[WIDX_RIDE_LIST]; - const int32_t listWidgetHeight = listWidget.bottom - listWidget.top - 1; - - // Ensure the current tab scroll is within range - currentTabScroll = std::min(currentTabScroll, std::max(0, scrollSize.height - listWidgetHeight)); - - scrolls[0].v_top = currentTabScroll; - WidgetScrollUpdateThumbs(*this, WIDX_RIDE_LIST); - } - - void DrawRideInformation(DrawPixelInfo& dpi, RideSelection item, const ScreenCoordsXY& screenPos, int32_t textWidth) - { - const auto* rideEntry = GetRideEntryByIndex(item.EntryIndex); - RideNaming rideNaming = GetRideNaming(item.Type, *rideEntry); - auto ft = Formatter(); - - UpdateVehicleAvailability(item.Type); - - // Ride name and description - ft.Add(rideNaming.Name); - ft.Add(rideNaming.Description); - DrawTextWrapped(dpi, screenPos, textWidth, STR_NEW_RIDE_NAME_AND_DESCRIPTION, ft); - - if (!_vehicleAvailability.empty()) + void OnMouseDown(WidgetIndex widgetIndex) override { - if (gConfigInterface.ListRideVehiclesSeparately) + if (widgetIndex >= WIDX_TAB_1 && widgetIndex <= WIDX_TAB_7) { - ft = Formatter(); - ft.Add(rideEntry->naming.Name); - DrawTextEllipsised(dpi, screenPos + ScreenCoordsXY{ 0, 39 }, WindowWidth - 2, STR_NEW_RIDE_VEHICLE_NAME, ft); + SetPage(widgetIndex - WIDX_TAB_1); + } + } + + void OnPrepareDraw() override + { + SetPressedTab(); + + if (!gConfigInterface.ListRideVehiclesSeparately) + pressed_widgets |= (1LL << WIDX_GROUP_BY_TRACK_TYPE); + else + pressed_widgets &= ~(1LL << WIDX_GROUP_BY_TRACK_TYPE); + + widgets[WIDX_TITLE].text = RideTitles[_currentTab]; + widgets[WIDX_TAB_7].type = WindowWidgetType::Tab; + widgets[WIDX_FILTER_TEXT_BOX].string = _filter.data(); + + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + widgets[WIDX_TAB_7].type = WindowWidgetType::Empty; + + if (_currentTab == RESEARCH_TAB) + { + WindowResearchDevelopmentPrepareDraw(this, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); + } + + const auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); + auto string = ls.GetString(STR_GROUP_BY_TRACK_TYPE); + auto strWidth = GfxGetStringWidth(string, FontStyle::Medium); + auto localizedGroupByTrackTypeWidth = strWidth + 14; + widgets[WIDX_GROUP_BY_TRACK_TYPE].left = width - 8 - localizedGroupByTrackTypeWidth; + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + if (_currentTab != RESEARCH_TAB) + { + RideSelection item = _newRideVars.HighlightedRide; + if (item.Type != RIDE_TYPE_NULL || item.EntryIndex != OBJECT_ENTRY_INDEX_NULL) + DrawRideInformation(dpi, item, windowPos + ScreenCoordsXY{ 3, height - 64 }, width - 6); } else { - ft = Formatter(); - ft.Add(_vehicleAvailability.c_str()); - DrawTextEllipsised(dpi, screenPos + ScreenCoordsXY{ 0, 39 }, WindowWidth - 2, STR_AVAILABLE_VEHICLES, ft); + WindowResearchDevelopmentDraw(this, dpi, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); } } - auto count = GetNumTrackDesigns(item); - auto designCountStringId = GetDesignsAvailableStringId(count); - ft = Formatter(); - ft.Add(count); - DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ 0, 51 }, designCountStringId, ft); - - // Price - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + ScreenSize OnScrollGetSize(int32_t scrollIndex) override { - // Get price of ride - int32_t startPieceId = GetRideTypeDescriptor(item.Type).StartTrackPiece; - money64 price = GetRideTypeDescriptor(item.Type).BuildCosts.TrackPrice; - const auto& ted = GetTrackElementDescriptor(startPieceId); - price *= ted.PriceModifier; - price = (price >> 16) * GetRideTypeDescriptor(item.Type).BuildCosts.PriceEstimateMultiplier; + RideSelection* listItem = _windowNewRideListItems; - // - StringId stringId = STR_NEW_RIDE_COST; - if (GetRideTypeDescriptor(item.Type).HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) - stringId = STR_NEW_RIDE_COST_FROM; + int32_t count = 0; + while (listItem->Type != RIDE_TYPE_NULL || listItem->EntryIndex != OBJECT_ENTRY_INDEX_NULL) + { + count++; + listItem++; + } + return { widgets[WIDX_RIDE_LIST].width(), ((count + 4) / 5) * 116 }; + } + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + RideSelection item = ScrollGetRideListItemAt(screenCoords); + if (_newRideVars.HighlightedRide == item) + { + return; + } + + _newRideVars.HighlightedRide = item; + Invalidate(); + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + RideSelection item = ScrollGetRideListItemAt(screenCoords); + if (item.Type == RIDE_TYPE_NULL && item.EntryIndex == OBJECT_ENTRY_INDEX_NULL) + { + return; + } + + _newRideVars.SelectedRide = item; + + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); + _newRideVars.SelectedRideCountdown = 8; + Invalidate(); + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + if (_currentTab == RESEARCH_TAB) + { + return; + } + + GfxClear(&dpi, ColourMapA[colours[1]].mid_light); + + ScreenCoordsXY coords{ 1, 1 }; + RideSelection* listItem = _windowNewRideListItems; + while (listItem->Type != RIDE_TYPE_NULL || listItem->EntryIndex != OBJECT_ENTRY_INDEX_NULL) + { + // Draw flat button rectangle + int32_t buttonFlags = 0; + if (_newRideVars.SelectedRide == *listItem) + buttonFlags |= INSET_RECT_FLAG_BORDER_INSET; + if (_newRideVars.HighlightedRide == *listItem || buttonFlags != 0) + GfxFillRectInset( + dpi, { coords, coords + ScreenCoordsXY{ 115, 115 } }, colours[1], + INSET_RECT_FLAG_FILL_MID_LIGHT | buttonFlags); + + // Draw ride image with feathered border + auto mask = ImageId(SPR_NEW_RIDE_MASK); + auto rideImage = ImageId(GetRideImage(*listItem)); + GfxDrawSpriteRawMasked(&dpi, coords + ScreenCoordsXY{ 2, 2 }, mask, rideImage); + + // Next position + coords.x += 116; + if (coords.x >= 116 * 5 + 1) + { + coords.x = 1; + coords.y += 116; + } + + // Next item + listItem++; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (widgetIndex != WIDX_FILTER_TEXT_BOX) + return; + + if (text == _filter) + return; + + _filter.assign(text); + + scrolls->v_top = 0; + Invalidate(); + } + + void SetPage(int tab) + { + if (tab >= TRANSPORT_TAB && tab < TAB_COUNT) + { + SetPage(static_cast(tab)); + } + } + + void SetPage(NewRideTabId tab) + { + _currentTab = tab; + frame_no = 0; + _newRideVars.HighlightedRide = { RIDE_TYPE_NULL, OBJECT_ENTRY_INDEX_NULL }; + _newRideVars.SelectedRideCountdown = std::numeric_limits::max(); + PopulateRideList(); + RefreshWidgetSizing(); + Invalidate(); + + if (tab < RESEARCH_TAB) + { + RestoreScrollPositionForCurrentTab(); + } + } + + private: + void RideSelect() + { + RideSelection item = _newRideVars.SelectedRide; + if (item.Type == RIDE_TYPE_NULL) + { + return; + } + + Close(); + WindowCloseConstructionWindows(); + + auto count = GetNumTrackDesigns(item); + if (count > 0) + { + auto intent = Intent(WindowClass::TrackDesignList); + intent.PutExtra(INTENT_EXTRA_RIDE_TYPE, item.Type); + intent.PutExtra(INTENT_EXTRA_RIDE_ENTRY_INDEX, item.EntryIndex); + ContextOpenIntent(&intent); + return; + } + + RideConstructNew(item); + } + + int32_t GetNumTrackDesigns(RideSelection item) + { + // Cache the result + if (item.Type == _lastTrackDesignCountRideType.Type && item.EntryIndex == _lastTrackDesignCountRideType.EntryIndex) + { + return _lastTrackDesignCount; + } + + std::string entryName; + + if (item.Type < 0x80) + { + if (GetRideTypeDescriptor(item.Type).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + entryName = GetRideEntryName(item.EntryIndex); + } + } + + auto repo = OpenRCT2::GetContext()->GetTrackDesignRepository(); + _lastTrackDesignCount = static_cast(repo->GetCountForObjectEntry(item.Type, entryName)); + _lastTrackDesignCountRideType = item; + return _lastTrackDesignCount; + } + + void UpdateVehicleAvailability(ObjectEntryIndex rideType) + { + _vehicleAvailability.clear(); + if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + return; + } + + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto& rideEntries = objManager.GetAllRideEntries(rideType); + auto isFirst = true; + for (auto rideEntryIndex : rideEntries) + { + auto currentRideEntry = GetRideEntryByIndex(rideEntryIndex); + + // Skip if vehicle type is not invented yet + if (!RideEntryIsInvented(rideEntryIndex) && !GetGameState().Cheats.IgnoreResearchStatus) + { + continue; + } + + // Append comma if not the first iteration + if (!isFirst) + { + _vehicleAvailability += u8", "; + } + + // Append vehicle name + auto vehicleName = LanguageGetString(currentRideEntry->naming.Name); + _vehicleAvailability += vehicleName; + + isFirst = false; + } + } + + ImageIndex GetRideImage(RideSelection rideSelection) + { + auto& objMgr = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = static_cast(objMgr.GetLoadedObject(ObjectType::Ride, rideSelection.EntryIndex)); + return obj == nullptr ? ImageIndexUndefined : obj->GetPreviewImage(rideSelection.Type); + } + + void PopulateRideList() + { + uint8_t currentCategory = _currentTab; + RideSelection* nextListItem = _windowNewRideListItems; + RideSelection* listEnd = _windowNewRideListItems + RideListItemsMax; + + // For each ride type in the view order list + for (int32_t i = 0; i < static_cast(std::size(RideTypeViewOrder)); i++) + { + auto rideType = RideTypeViewOrder[i]; + if (rideType == RIDE_TYPE_NULL) + continue; + + if (GetRideTypeDescriptor(rideType).Category != currentCategory) + continue; + + nextListItem = IterateOverRideType(rideType, nextListItem, listEnd); + } + + nextListItem->Type = RIDE_TYPE_NULL; + nextListItem->EntryIndex = OBJECT_ENTRY_INDEX_NULL; + } + + RideSelection* IterateOverRideType(ride_type_t rideType, RideSelection* nextListItem, RideSelection* listEnd) + { + bool buttonForRideTypeCreated = false; + bool allowDrawingOverLastButton = false; + + uint8_t highestVehiclePriority = 0; + + // For each ride entry for this ride type + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto& rideEntries = objManager.GetAllRideEntries(rideType); + for (auto rideEntryIndex : rideEntries) + { + // Skip if vehicle type is not invented yet + if (!RideEntryIsInvented(rideEntryIndex) && !GetGameState().Cheats.IgnoreResearchStatus) + continue; + + // Ride entries + const auto* rideEntry = GetRideEntryByIndex(rideEntryIndex); + + // Skip if the vehicle isn't the preferred vehicle for this generic track type + if (!gConfigInterface.ListRideVehiclesSeparately + && !GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY) + && highestVehiclePriority > rideEntry->BuildMenuPriority) + { + continue; + } + + if (!IsFiltered(*rideEntry)) + { + continue; + } + + highestVehiclePriority = rideEntry->BuildMenuPriority; + + // Determines how and where to draw a button for this ride type/vehicle. + if (gConfigInterface.ListRideVehiclesSeparately + || GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + // Separate, draw apart + allowDrawingOverLastButton = false; + + if (nextListItem >= listEnd) + continue; + + nextListItem->Type = rideType; + nextListItem->EntryIndex = rideEntryIndex; + nextListItem++; + } + else if (!buttonForRideTypeCreated) + { + // Non-separate, draw-apart + buttonForRideTypeCreated = true; + allowDrawingOverLastButton = true; + + if (nextListItem >= listEnd) + continue; + + nextListItem->Type = rideType; + nextListItem->EntryIndex = rideEntryIndex; + nextListItem++; + } + else if (allowDrawingOverLastButton) + { + // Non-separate, draw over previous + if (rideType == rideEntry->ride_type[0]) + { + nextListItem--; + nextListItem->Type = rideType; + nextListItem->EntryIndex = rideEntryIndex; + nextListItem++; + } + } + } + + return nextListItem; + } + + bool IsFiltered(const RideObjectEntry& rideEntry) + { + if (_filter.empty()) + return true; + + return IsFilterInRideType(rideEntry) || IsFilterInRideName(rideEntry) || IsFilterInIdentifier(rideEntry) + || IsFilterInAuthors(rideEntry) || IsFilterInFilename(rideEntry); + } + + bool IsFilterInRideType(const RideObjectEntry& rideEntry) + { + auto rideTypeName = GetRideNaming(rideEntry.ride_type[0], rideEntry).Name; + return String::Contains(u8string_view(LanguageGetString(rideTypeName)), _filter, true); + } + + bool IsFilterInRideName(const RideObjectEntry& rideEntry) + { + auto rideName = rideEntry.naming.Name; + return String::Contains(u8string_view(LanguageGetString(rideName)), _filter, true); + } + + bool IsFilterInAuthors(const RideObjectEntry& rideEntry) + { + auto rideObject = static_cast(rideEntry.obj); + auto authors = rideObject->GetAuthors(); + + for (auto author : authors) + if (String::Contains(author, _filter, true)) + return true; + + return false; + } + + bool IsFilterInIdentifier(const RideObjectEntry& rideEntry) + { + auto rideObject = static_cast(rideEntry.obj); + auto objectName = rideObject->GetObjectEntry().GetName(); + + return String::Contains(objectName, _filter, true); + } + + bool IsFilterInFilename(const RideObjectEntry& rideEntry) + { + auto rideObject = static_cast(rideEntry.obj); + auto repoItem = ObjectRepositoryFindObjectByEntry(&(rideObject->GetObjectEntry())); + + return String::Contains(repoItem->Path, _filter, true); + } + + void SetPressedTab() + { + int32_t i{}; + for (i = 0; i < TAB_COUNT; i++) + { + pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); + } + pressed_widgets |= 1LL << (WIDX_TAB_1 + static_cast(_currentTab)); + } + + void RefreshWidgetSizing() + { + int32_t newWidth{}, newHeight{}; + + if (_currentTab < SHOP_TAB) + { + disabled_widgets &= ~(1 << WIDX_GROUP_BY_TRACK_TYPE); + } + else + { + disabled_widgets |= 1LL << WIDX_GROUP_BY_TRACK_TYPE; + } + + // Show or hide unrelated widgets + + if (_currentTab < RESEARCH_TAB) + { + widgets[WIDX_GROUP_BY_TRACK_TYPE].type = WindowWidgetType::Checkbox; + } + else + { + widgets[WIDX_GROUP_BY_TRACK_TYPE].type = WindowWidgetType::Empty; + } + + if (_currentTab != RESEARCH_TAB) + { + widgets[WIDX_RIDE_LIST].type = WindowWidgetType::Scroll; + widgets[WIDX_FILTER_TEXT_BOX].type = WindowWidgetType::TextBox; + widgets[WIDX_FILTER_CLEAR_BUTTON].type = WindowWidgetType::Button; + widgets[WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP].type = WindowWidgetType::Empty; + widgets[WIDX_LAST_DEVELOPMENT_GROUP].type = WindowWidgetType::Empty; + widgets[WIDX_LAST_DEVELOPMENT_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_RESEARCH_FUNDING_BUTTON].type = WindowWidgetType::Empty; + + newWidth = WindowWidth; + newHeight = WindowHeight; + } + else + { + widgets[WIDX_RIDE_LIST].type = WindowWidgetType::Empty; + widgets[WIDX_FILTER_TEXT_BOX].type = WindowWidgetType::Empty; + widgets[WIDX_FILTER_CLEAR_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP].type = WindowWidgetType::Groupbox; + widgets[WIDX_LAST_DEVELOPMENT_GROUP].type = WindowWidgetType::Groupbox; + widgets[WIDX_LAST_DEVELOPMENT_BUTTON].type = WindowWidgetType::FlatBtn; + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + widgets[WIDX_RESEARCH_FUNDING_BUTTON].type = WindowWidgetType::FlatBtn; + + newWidth = 300; + newHeight = 196; + } + + // Handle new window size + if (width != newWidth || height != newHeight) + { + Invalidate(); + + // Resize widgets to new window size + width = newWidth; + height = newHeight; + ResizeFrameWithPage(); + widgets[WIDX_GROUP_BY_TRACK_TYPE].left = newWidth - 8 - GroupByTrackTypeWidth; + widgets[WIDX_GROUP_BY_TRACK_TYPE].right = newWidth - 8; + + Invalidate(); + } + + InitScrollWidgets(); + } + + RideSelection ScrollGetRideListItemAt(const ScreenCoordsXY& screenCoords) + { + RideSelection result; + result.Type = RIDE_TYPE_NULL; + result.EntryIndex = OBJECT_ENTRY_INDEX_NULL; + + if (screenCoords.x <= 0 || screenCoords.y <= 0) + return result; + + int32_t column = screenCoords.x / 116; + int32_t row = screenCoords.y / 116; + if (column >= 5) + return result; + + int32_t index = column + (row * 5); + + RideSelection* listItem = _windowNewRideListItems; + while (listItem->Type != RIDE_TYPE_NULL || listItem->EntryIndex != OBJECT_ENTRY_INDEX_NULL) + { + if (index-- == 0) + { + return *listItem; + } + listItem++; + } + + return result; + } + + void RestoreScrollPositionForCurrentTab() + { + assert(static_cast(_currentTab) < std::size(_windowNewRideTabScroll)); + auto& currentTabScroll = _windowNewRideTabScroll[_currentTab]; + + // Get maximum scroll height + ScreenSize scrollSize = OnScrollGetSize(0); + const Widget& listWidget = widgets[WIDX_RIDE_LIST]; + const int32_t listWidgetHeight = listWidget.bottom - listWidget.top - 1; + + // Ensure the current tab scroll is within range + currentTabScroll = std::min(currentTabScroll, std::max(0, scrollSize.height - listWidgetHeight)); + + scrolls[0].v_top = currentTabScroll; + WidgetScrollUpdateThumbs(*this, WIDX_RIDE_LIST); + } + + void DrawRideInformation(DrawPixelInfo& dpi, RideSelection item, const ScreenCoordsXY& screenPos, int32_t textWidth) + { + const auto* rideEntry = GetRideEntryByIndex(item.EntryIndex); + RideNaming rideNaming = GetRideNaming(item.Type, *rideEntry); + auto ft = Formatter(); + + UpdateVehicleAvailability(item.Type); + + // Ride name and description + ft.Add(rideNaming.Name); + ft.Add(rideNaming.Description); + DrawTextWrapped(dpi, screenPos, textWidth, STR_NEW_RIDE_NAME_AND_DESCRIPTION, ft); + + if (!_vehicleAvailability.empty()) + { + if (gConfigInterface.ListRideVehiclesSeparately) + { + ft = Formatter(); + ft.Add(rideEntry->naming.Name); + DrawTextEllipsised( + dpi, screenPos + ScreenCoordsXY{ 0, 39 }, WindowWidth - 2, STR_NEW_RIDE_VEHICLE_NAME, ft); + } + else + { + ft = Formatter(); + ft.Add(_vehicleAvailability.c_str()); + DrawTextEllipsised(dpi, screenPos + ScreenCoordsXY{ 0, 39 }, WindowWidth - 2, STR_AVAILABLE_VEHICLES, ft); + } + } + + auto count = GetNumTrackDesigns(item); + auto designCountStringId = GetDesignsAvailableStringId(count); ft = Formatter(); - ft.Add(price); - DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ textWidth, 51 }, stringId, ft, { TextAlignment::RIGHT }); + ft.Add(count); + DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ 0, 51 }, designCountStringId, ft); + + // Price + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + // Get price of ride + int32_t startPieceId = GetRideTypeDescriptor(item.Type).StartTrackPiece; + money64 price = GetRideTypeDescriptor(item.Type).BuildCosts.TrackPrice; + const auto& ted = GetTrackElementDescriptor(startPieceId); + price *= ted.PriceModifier; + price = (price >> 16) * GetRideTypeDescriptor(item.Type).BuildCosts.PriceEstimateMultiplier; + + // + StringId stringId = STR_NEW_RIDE_COST; + if (GetRideTypeDescriptor(item.Type).HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) + stringId = STR_NEW_RIDE_COST_FROM; + + ft = Formatter(); + ft.Add(price); + DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ textWidth, 51 }, stringId, ft, { TextAlignment::RIGHT }); + } } - } - void DrawTabImage(DrawPixelInfo& dpi, NewRideTabId tab, int32_t spriteIndex) - { - WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(tab); - - if (widgets[widgetIndex].type != WindowWidgetType::Empty && !WidgetIsDisabled(*this, widgetIndex)) + void DrawTabImage(DrawPixelInfo& dpi, NewRideTabId tab, int32_t spriteIndex) { - int32_t frame = 0; - if (_currentTab == tab) - frame = frame_no / TabAnimationDivisor[_currentTab]; + WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(tab); - spriteIndex += tab == THRILL_TAB ? ThrillRidesTabAnimationSequence[frame] : frame; + if (widgets[widgetIndex].type != WindowWidgetType::Empty && !WidgetIsDisabled(*this, widgetIndex)) + { + int32_t frame = 0; + if (_currentTab == tab) + frame = frame_no / TabAnimationDivisor[_currentTab]; - GfxDrawSprite( - dpi, ImageId(spriteIndex, colours[1]), - windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + spriteIndex += tab == THRILL_TAB ? ThrillRidesTabAnimationSequence[frame] : frame; + + GfxDrawSprite( + dpi, ImageId(spriteIndex, colours[1]), + windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + } } - } - void DrawTabImages(DrawPixelInfo& dpi) - { - DrawTabImage(dpi, TRANSPORT_TAB, SPR_TAB_RIDES_TRANSPORT_0); - DrawTabImage(dpi, GENTLE_TAB, SPR_TAB_RIDES_GENTLE_0); - DrawTabImage(dpi, ROLLER_COASTER_TAB, SPR_TAB_RIDES_ROLLER_COASTERS_0); - DrawTabImage(dpi, THRILL_TAB, SPR_TAB_RIDES_THRILL_0); - DrawTabImage(dpi, WATER_TAB, SPR_TAB_RIDES_WATER_0); - DrawTabImage(dpi, SHOP_TAB, SPR_TAB_RIDES_SHOP_0); - DrawTabImage(dpi, RESEARCH_TAB, SPR_TAB_FINANCES_RESEARCH_0); - } - - StringId GetDesignsAvailableStringId(int32_t count) - { - switch (count) + void DrawTabImages(DrawPixelInfo& dpi) { - case 0: - return STR_CUSTOM_DESIGNED_LAYOUT; - case 1: - return STR_1_DESIGN_AVAILABLE; - default: - return STR_X_DESIGNS_AVAILABLE; + DrawTabImage(dpi, TRANSPORT_TAB, SPR_TAB_RIDES_TRANSPORT_0); + DrawTabImage(dpi, GENTLE_TAB, SPR_TAB_RIDES_GENTLE_0); + DrawTabImage(dpi, ROLLER_COASTER_TAB, SPR_TAB_RIDES_ROLLER_COASTERS_0); + DrawTabImage(dpi, THRILL_TAB, SPR_TAB_RIDES_THRILL_0); + DrawTabImage(dpi, WATER_TAB, SPR_TAB_RIDES_WATER_0); + DrawTabImage(dpi, SHOP_TAB, SPR_TAB_RIDES_SHOP_0); + DrawTabImage(dpi, RESEARCH_TAB, SPR_TAB_FINANCES_RESEARCH_0); } - } -}; -NewRideTabId NewRideWindow::_currentTab{}; -uint_fast16_t NewRideWindow::_windowNewRideTabScroll[RideTabCount]{}; + StringId GetDesignsAvailableStringId(int32_t count) + { + switch (count) + { + case 0: + return STR_CUSTOM_DESIGNED_LAYOUT; + case 1: + return STR_1_DESIGN_AVAILABLE; + default: + return STR_X_DESIGNS_AVAILABLE; + } + } + }; -/** - * - * rct2: 0x006ACA58 - */ -void WindowNewRideInitVars() -{ - // If we are in the track designer, default to the Roller Coaster tab - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + NewRideTabId NewRideWindow::_currentTab{}; + uint_fast16_t NewRideWindow::_windowNewRideTabScroll[RideTabCount]{}; + + /** + * + * rct2: 0x006ACA58 + */ + void WindowNewRideInitVars() { - NewRideWindow::SetOpeningPage(ROLLER_COASTER_TAB); + // If we are in the track designer, default to the Roller Coaster tab + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + { + NewRideWindow::SetOpeningPage(ROLLER_COASTER_TAB); + } + else + { + NewRideWindow::SetOpeningPage(TRANSPORT_TAB); + } + NewRideWindow::ResetTabScrolls(); } - else - { - NewRideWindow::SetOpeningPage(TRANSPORT_TAB); - } - NewRideWindow::ResetTabScrolls(); -} -/** - * - * rct2: 0x006B3CFF - */ -WindowBase* WindowNewRideOpen() -{ - WindowBase* window; - - window = WindowBringToFrontByClass(WindowClass::ConstructRide); - if (window) + /** + * + * rct2: 0x006B3CFF + */ + WindowBase* WindowNewRideOpen() { + WindowBase* window; + + window = WindowBringToFrontByClass(WindowClass::ConstructRide); + if (window) + { + return window; + } + + WindowCloseByClass(WindowClass::TrackDesignList); + WindowCloseByClass(WindowClass::TrackDesignPlace); + + window = WindowCreate(WindowClass::ConstructRide, WindowWidth, WindowHeight, WF_10 | WF_AUTO_POSITION); return window; } - WindowCloseByClass(WindowClass::TrackDesignList); - WindowCloseByClass(WindowClass::TrackDesignPlace); - - window = WindowCreate(WindowClass::ConstructRide, WindowWidth, WindowHeight, WF_10 | WF_AUTO_POSITION); - return window; -} - -WindowBase* WindowNewRideOpenResearch() -{ - auto w = static_cast(WindowNewRideOpen()); - w->SetPage(RESEARCH_TAB); - return w; -} - -/** - * - * rct2: 0x006B3EBA - */ -void WindowNewRideFocus(RideSelection rideItem) -{ - auto w = static_cast(WindowFindByClass(WindowClass::ConstructRide)); - if (!w) + WindowBase* WindowNewRideOpenResearch() { - return; + auto w = static_cast(WindowNewRideOpen()); + w->SetPage(RESEARCH_TAB); + return w; } - const auto* rideEntry = GetRideEntryByIndex(rideItem.EntryIndex); - if (rideEntry == nullptr) - return; + /** + * + * rct2: 0x006B3EBA + */ + void WindowNewRideFocus(RideSelection rideItem) + { + auto w = static_cast(WindowFindByClass(WindowClass::ConstructRide)); + if (!w) + { + return; + } - auto rideTypeIndex = rideEntry->GetFirstNonNullRideType(); - w->SetPage(GetRideTypeDescriptor(rideTypeIndex).Category); -} + const auto* rideEntry = GetRideEntryByIndex(rideItem.EntryIndex); + if (rideEntry == nullptr) + return; + + auto rideTypeIndex = rideEntry->GetFirstNonNullRideType(); + w->SetPage(GetRideTypeDescriptor(rideTypeIndex).Category); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/News.cpp b/src/openrct2-ui/windows/News.cpp index 13431bd0e6..c23bbf25d3 100644 --- a/src/openrct2-ui/windows/News.cpp +++ b/src/openrct2-ui/windows/News.cpp @@ -22,13 +22,13 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_RECENT_MESSAGES; + static constexpr int32_t WH = 300; + static constexpr int32_t WW = 400; -static constexpr StringId WINDOW_TITLE = STR_RECENT_MESSAGES; -static constexpr int32_t WH = 300; -static constexpr int32_t WW = 400; - -// clang-format off + // clang-format off enum WindowNewsWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -45,278 +45,280 @@ static Widget window_news_widgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class NewsWindow final : public Window -{ -private: - int32_t _pressedNewsItemIndex{}, _pressedButtonIndex{}, _suspendUpdateTicks{}; - static int32_t CalculateItemHeight() + class NewsWindow final : public Window { - return 4 * FontGetLineHeight(FontStyle::Small) + 2; - } - -public: - void OnOpen() override - { - widgets = window_news_widgets; - WindowInitScrollWidgets(*this); - _pressedNewsItemIndex = -1; - - Widget* widget = &widgets[WIDX_SCROLL]; - ScreenSize scrollSize = OnScrollGetSize(0); - scrolls[0].v_top = std::max(0, scrollSize.height - (widget->height() - 1)); - WidgetScrollUpdateThumbs(*this, WIDX_SCROLL); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + private: + int32_t _pressedNewsItemIndex{}, _pressedButtonIndex{}, _suspendUpdateTicks{}; + static int32_t CalculateItemHeight() { - case WIDX_CLOSE: - Close(); - break; - case WIDX_SETTINGS: - ContextOpenWindow(WindowClass::NotificationOptions); - break; - } - } - - void OnUpdate() override - { - if (_pressedNewsItemIndex == -1 || --_suspendUpdateTicks != 0) - { - return; + return 4 * FontGetLineHeight(FontStyle::Small) + 2; } - Invalidate(); - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click2, 0, windowPos.x + (width / 2)); - - size_t j = _pressedNewsItemIndex; - _pressedNewsItemIndex = -1; - auto& gameState = GetGameState(); - - if (j >= gameState.NewsItems.GetArchived().size()) + public: + void OnOpen() override { - return; + widgets = window_news_widgets; + WindowInitScrollWidgets(*this); + _pressedNewsItemIndex = -1; + + Widget* widget = &widgets[WIDX_SCROLL]; + ScreenSize scrollSize = OnScrollGetSize(0); + scrolls[0].v_top = std::max(0, scrollSize.height - (widget->height() - 1)); + WidgetScrollUpdateThumbs(*this, WIDX_SCROLL); } - const auto& newsItem = gameState.NewsItems.GetArchived()[j]; - if (newsItem.HasButton()) + void OnMouseUp(WidgetIndex widgetIndex) override { - return; - } - - if (_pressedButtonIndex == 1) - { - News::OpenSubject(newsItem.Type, newsItem.Assoc); - } - else if (_pressedButtonIndex > 1) - { - static WindowBase* _mainWindow; - auto subjectLoc = News::GetSubjectLocation(newsItem.Type, newsItem.Assoc); - if (subjectLoc.has_value() && (_mainWindow = WindowGetMain()) != nullptr) + switch (widgetIndex) { - WindowScrollToLocation(*_mainWindow, subjectLoc.value()); + case WIDX_CLOSE: + Close(); + break; + case WIDX_SETTINGS: + ContextOpenWindow(WindowClass::NotificationOptions); + break; } } - } - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - int32_t scrollHeight = static_cast(GetGameState().NewsItems.GetArchived().size()) * CalculateItemHeight(); - return { WW, scrollHeight }; - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - int32_t itemHeight = CalculateItemHeight(); - int32_t i = 0; - int32_t buttonIndex = 0; - auto mutableScreenCoords = screenCoords; - for (const auto& newsItem : GetGameState().NewsItems.GetArchived()) + void OnUpdate() override { - if (mutableScreenCoords.y < itemHeight) + if (_pressedNewsItemIndex == -1 || --_suspendUpdateTicks != 0) { - if (newsItem.HasButton() || mutableScreenCoords.y < 14 || mutableScreenCoords.y >= 38 - || mutableScreenCoords.x < 328) - { - buttonIndex = 0; - break; - } - if (mutableScreenCoords.x < 351 && newsItem.TypeHasSubject()) - { - buttonIndex = 1; - break; - } - if (mutableScreenCoords.x < 376 && newsItem.TypeHasLocation()) - { - buttonIndex = 2; - break; - } + return; } - mutableScreenCoords.y -= itemHeight; - i++; - } - if (buttonIndex != 0) - { - _pressedNewsItemIndex = i; - _pressedButtonIndex = buttonIndex; - _suspendUpdateTicks = 4; Invalidate(); - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); - } - } + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click2, 0, windowPos.x + (width / 2)); - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - } + size_t j = _pressedNewsItemIndex; + _pressedNewsItemIndex = -1; + auto& gameState = GetGameState(); - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - int32_t lineHeight = FontGetLineHeight(FontStyle::Small); - int32_t itemHeight = CalculateItemHeight(); - int32_t y = 0; - int32_t i = 0; - - for (const auto& newsItem : GetGameState().NewsItems.GetArchived()) - { - if (y >= dpi.y + dpi.height) - break; - if (y + itemHeight < dpi.y) + if (j >= gameState.NewsItems.GetArchived().size()) { + return; + } + + const auto& newsItem = gameState.NewsItems.GetArchived()[j]; + if (newsItem.HasButton()) + { + return; + } + + if (_pressedButtonIndex == 1) + { + News::OpenSubject(newsItem.Type, newsItem.Assoc); + } + else if (_pressedButtonIndex > 1) + { + static WindowBase* _mainWindow; + auto subjectLoc = News::GetSubjectLocation(newsItem.Type, newsItem.Assoc); + if (subjectLoc.has_value() && (_mainWindow = WindowGetMain()) != nullptr) + { + WindowScrollToLocation(*_mainWindow, subjectLoc.value()); + } + } + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + int32_t scrollHeight = static_cast(GetGameState().NewsItems.GetArchived().size()) * CalculateItemHeight(); + return { WW, scrollHeight }; + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int32_t itemHeight = CalculateItemHeight(); + int32_t i = 0; + int32_t buttonIndex = 0; + auto mutableScreenCoords = screenCoords; + for (const auto& newsItem : GetGameState().NewsItems.GetArchived()) + { + if (mutableScreenCoords.y < itemHeight) + { + if (newsItem.HasButton() || mutableScreenCoords.y < 14 || mutableScreenCoords.y >= 38 + || mutableScreenCoords.x < 328) + { + buttonIndex = 0; + break; + } + if (mutableScreenCoords.x < 351 && newsItem.TypeHasSubject()) + { + buttonIndex = 1; + break; + } + if (mutableScreenCoords.x < 376 && newsItem.TypeHasLocation()) + { + buttonIndex = 2; + break; + } + } + mutableScreenCoords.y -= itemHeight; + i++; + } + + if (buttonIndex != 0) + { + _pressedNewsItemIndex = i; + _pressedButtonIndex = buttonIndex; + _suspendUpdateTicks = 4; + Invalidate(); + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + int32_t lineHeight = FontGetLineHeight(FontStyle::Small); + int32_t itemHeight = CalculateItemHeight(); + int32_t y = 0; + int32_t i = 0; + + for (const auto& newsItem : GetGameState().NewsItems.GetArchived()) + { + if (y >= dpi.y + dpi.height) + break; + if (y + itemHeight < dpi.y) + { + y += itemHeight; + i++; + continue; + } + + // Background + GfxFillRectInset( + dpi, { -1, y, 383, y + itemHeight - 1 }, colours[1], + (INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_GREY)); + + // Date text + { + auto ft = Formatter(); + ft.Add(DateDayNames[newsItem.Day - 1]); + ft.Add(DateGameMonthNames[DateGetMonth(newsItem.MonthYear)]); + DrawTextBasic(dpi, { 2, y }, STR_NEWS_DATE_FORMAT, ft, { COLOUR_WHITE, FontStyle::Small }); + } + // Item text + { + auto ft = Formatter(); + ft.Add(newsItem.Text.c_str()); + DrawTextWrapped(dpi, { 2, y + lineHeight }, 325, STR_BOTTOM_TOOLBAR_NEWS_TEXT, ft, { FontStyle::Small }); + } + // Subject button + if ((newsItem.TypeHasSubject()) && !(newsItem.HasButton())) + { + auto screenCoords = ScreenCoordsXY{ 328, y + lineHeight + 4 }; + + int32_t press = 0; + if (_pressedNewsItemIndex != -1) + { + News::IsValidIndex(_pressedNewsItemIndex + News::ItemHistoryStart); + if (i == _pressedNewsItemIndex && _pressedButtonIndex == 1) + { + press = INSET_RECT_FLAG_BORDER_INSET; + } + } + GfxFillRectInset(dpi, { screenCoords, screenCoords + ScreenCoordsXY{ 23, 23 } }, colours[2], press); + + switch (newsItem.Type) + { + case News::ItemType::Ride: + GfxDrawSprite(dpi, ImageId(SPR_RIDE), screenCoords); + break; + case News::ItemType::Peep: + case News::ItemType::PeepOnRide: + { + DrawPixelInfo cliped_dpi; + if (!ClipDrawPixelInfo(cliped_dpi, dpi, screenCoords + ScreenCoordsXY{ 1, 1 }, 22, 22)) + { + break; + } + + auto peep = TryGetEntity(EntityId::FromUnderlying(newsItem.Assoc)); + if (peep == nullptr) + { + break; + } + + auto clipCoords = ScreenCoordsXY{ 10, 19 }; + + // If normal peep set sprite to normal (no food) + // If staff set sprite to staff sprite + auto spriteType = PeepSpriteType::Normal; + auto* staff = peep->As(); + if (staff != nullptr) + { + spriteType = staff->SpriteType; + if (staff->AssignedStaffType == StaffType::Entertainer) + { + clipCoords.y += 3; + } + } + + ImageIndex imageId = GetPeepAnimation(spriteType).base_image + 1; + auto image = ImageId(imageId, peep->TshirtColour, peep->TrousersColour); + GfxDrawSprite(cliped_dpi, image, clipCoords); + break; + } + case News::ItemType::Money: + case News::ItemType::Campaign: + GfxDrawSprite(dpi, ImageId(SPR_FINANCE), screenCoords); + break; + case News::ItemType::Research: + GfxDrawSprite( + dpi, ImageId(newsItem.Assoc < 0x10000 ? SPR_NEW_SCENERY : SPR_NEW_RIDE), screenCoords); + break; + case News::ItemType::Peeps: + GfxDrawSprite(dpi, ImageId(SPR_GUESTS), screenCoords); + break; + case News::ItemType::Award: + GfxDrawSprite(dpi, ImageId(SPR_AWARD), screenCoords); + break; + case News::ItemType::Graph: + GfxDrawSprite(dpi, ImageId(SPR_GRAPH), screenCoords); + break; + case News::ItemType::Null: + case News::ItemType::Blank: + case News::ItemType::Count: + break; + } + } + + // Location button + if ((newsItem.TypeHasLocation()) && !(newsItem.HasButton())) + { + auto screenCoords = ScreenCoordsXY{ 352, y + lineHeight + 4 }; + + int32_t press = 0; + if (_pressedNewsItemIndex != -1) + { + News::IsValidIndex(_pressedNewsItemIndex + News::ItemHistoryStart); + if (i == _pressedNewsItemIndex && _pressedButtonIndex == 2) + press = 0x20; + } + GfxFillRectInset(dpi, { screenCoords, screenCoords + ScreenCoordsXY{ 23, 23 } }, colours[2], press); + GfxDrawSprite(dpi, ImageId(SPR_LOCATE), screenCoords); + } + y += itemHeight; i++; - continue; } - - // Background - GfxFillRectInset( - dpi, { -1, y, 383, y + itemHeight - 1 }, colours[1], - (INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_GREY)); - - // Date text - { - auto ft = Formatter(); - ft.Add(DateDayNames[newsItem.Day - 1]); - ft.Add(DateGameMonthNames[DateGetMonth(newsItem.MonthYear)]); - DrawTextBasic(dpi, { 2, y }, STR_NEWS_DATE_FORMAT, ft, { COLOUR_WHITE, FontStyle::Small }); - } - // Item text - { - auto ft = Formatter(); - ft.Add(newsItem.Text.c_str()); - DrawTextWrapped(dpi, { 2, y + lineHeight }, 325, STR_BOTTOM_TOOLBAR_NEWS_TEXT, ft, { FontStyle::Small }); - } - // Subject button - if ((newsItem.TypeHasSubject()) && !(newsItem.HasButton())) - { - auto screenCoords = ScreenCoordsXY{ 328, y + lineHeight + 4 }; - - int32_t press = 0; - if (_pressedNewsItemIndex != -1) - { - News::IsValidIndex(_pressedNewsItemIndex + News::ItemHistoryStart); - if (i == _pressedNewsItemIndex && _pressedButtonIndex == 1) - { - press = INSET_RECT_FLAG_BORDER_INSET; - } - } - GfxFillRectInset(dpi, { screenCoords, screenCoords + ScreenCoordsXY{ 23, 23 } }, colours[2], press); - - switch (newsItem.Type) - { - case News::ItemType::Ride: - GfxDrawSprite(dpi, ImageId(SPR_RIDE), screenCoords); - break; - case News::ItemType::Peep: - case News::ItemType::PeepOnRide: - { - DrawPixelInfo cliped_dpi; - if (!ClipDrawPixelInfo(cliped_dpi, dpi, screenCoords + ScreenCoordsXY{ 1, 1 }, 22, 22)) - { - break; - } - - auto peep = TryGetEntity(EntityId::FromUnderlying(newsItem.Assoc)); - if (peep == nullptr) - { - break; - } - - auto clipCoords = ScreenCoordsXY{ 10, 19 }; - - // If normal peep set sprite to normal (no food) - // If staff set sprite to staff sprite - auto spriteType = PeepSpriteType::Normal; - auto* staff = peep->As(); - if (staff != nullptr) - { - spriteType = staff->SpriteType; - if (staff->AssignedStaffType == StaffType::Entertainer) - { - clipCoords.y += 3; - } - } - - ImageIndex imageId = GetPeepAnimation(spriteType).base_image + 1; - auto image = ImageId(imageId, peep->TshirtColour, peep->TrousersColour); - GfxDrawSprite(cliped_dpi, image, clipCoords); - break; - } - case News::ItemType::Money: - case News::ItemType::Campaign: - GfxDrawSprite(dpi, ImageId(SPR_FINANCE), screenCoords); - break; - case News::ItemType::Research: - GfxDrawSprite(dpi, ImageId(newsItem.Assoc < 0x10000 ? SPR_NEW_SCENERY : SPR_NEW_RIDE), screenCoords); - break; - case News::ItemType::Peeps: - GfxDrawSprite(dpi, ImageId(SPR_GUESTS), screenCoords); - break; - case News::ItemType::Award: - GfxDrawSprite(dpi, ImageId(SPR_AWARD), screenCoords); - break; - case News::ItemType::Graph: - GfxDrawSprite(dpi, ImageId(SPR_GRAPH), screenCoords); - break; - case News::ItemType::Null: - case News::ItemType::Blank: - case News::ItemType::Count: - break; - } - } - - // Location button - if ((newsItem.TypeHasLocation()) && !(newsItem.HasButton())) - { - auto screenCoords = ScreenCoordsXY{ 352, y + lineHeight + 4 }; - - int32_t press = 0; - if (_pressedNewsItemIndex != -1) - { - News::IsValidIndex(_pressedNewsItemIndex + News::ItemHistoryStart); - if (i == _pressedNewsItemIndex && _pressedButtonIndex == 2) - press = 0x20; - } - GfxFillRectInset(dpi, { screenCoords, screenCoords + ScreenCoordsXY{ 23, 23 } }, colours[2], press); - GfxDrawSprite(dpi, ImageId(SPR_LOCATE), screenCoords); - } - - y += itemHeight; - i++; } - } - void OnResize() override + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowNewsOpen() { - ResizeFrame(); + return WindowFocusOrCreate(WindowClass::RecentNews, WW, WH, 0); } -}; - -WindowBase* WindowNewsOpen() -{ - return WindowFocusOrCreate(WindowClass::RecentNews, WW, WH, 0); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/NewsOptions.cpp b/src/openrct2-ui/windows/NewsOptions.cpp index 1a2a140869..69c387405a 100644 --- a/src/openrct2-ui/windows/NewsOptions.cpp +++ b/src/openrct2-ui/windows/NewsOptions.cpp @@ -14,11 +14,13 @@ #include #include -static constexpr StringId WINDOW_TITLE = STR_NOTIFICATION_SETTINGS; -static constexpr int32_t WH = 300; -static constexpr int32_t WW = 400; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_NOTIFICATION_SETTINGS; + static constexpr int32_t WH = 300; + static constexpr int32_t WW = 400; -// clang-format off + // clang-format off enum { NOTIFICATION_CATEGORY_PARK, @@ -85,198 +87,199 @@ static Widget WindowNewsOptionsWidgets[] = { MakeWidget({ 0, 0}, {343, 14}, WindowWidgetType::Checkbox, WindowColour::Tertiary ), kWidgetsEnd, }; -// clang-format on + // clang-format on -class NewsOptionsWindow final : public Window -{ -public: - void OnOpen() override + class NewsOptionsWindow final : public Window { - widgets = WindowNewsOptionsWidgets; - InitScrollWidgets(); - colours[0] = COLOUR_GREY; - colours[1] = COLOUR_LIGHT_BLUE; - colours[2] = COLOUR_LIGHT_BLUE; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_TAB_PARK: - case WIDX_TAB_RIDE: - case WIDX_TAB_GUEST: - SetPage(widgetIndex - WIDX_FIRST_TAB); - break; - default: + widgets = WindowNewsOptionsWidgets; + InitScrollWidgets(); + colours[0] = COLOUR_GREY; + colours[1] = COLOUR_LIGHT_BLUE; + colours[2] = COLOUR_LIGHT_BLUE; + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) { - int32_t checkBoxIndex = widgetIndex - WIDX_CHECKBOX_0; - if (checkBoxIndex >= 0) + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB_PARK: + case WIDX_TAB_RIDE: + case WIDX_TAB_GUEST: + SetPage(widgetIndex - WIDX_FIRST_TAB); + break; + default: { - int32_t matchIndex = 0; - for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) + int32_t checkBoxIndex = widgetIndex - WIDX_CHECKBOX_0; + if (checkBoxIndex >= 0) { - const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; - if (ndef->category != page) - continue; - - if (matchIndex == checkBoxIndex) + int32_t matchIndex = 0; + for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) { - // Toggle value - bool* configValue = GetNotificationValuePtr(ndef); - *configValue = !(*configValue); + const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; + if (ndef->category != page) + continue; - ConfigSaveDefault(); + if (matchIndex == checkBoxIndex) + { + // Toggle value + bool* configValue = GetNotificationValuePtr(ndef); + *configValue = !(*configValue); - InvalidateWidget(widgetIndex); - break; + ConfigSaveDefault(); + + InvalidateWidget(widgetIndex); + break; + } + matchIndex++; } - matchIndex++; + } + break; + } + } + } + + void OnUpdate() override + { + // Tab animation + frame_no++; + InvalidateWidget(WIDX_FIRST_TAB + page); + } + + void OnPrepareDraw() override + { + SetPressedTab(); + + // Set checkboxes + const auto& baseCheckBox = widgets[WIDX_CHECKBOX_0]; + int32_t y = baseCheckBox.top; + + int32_t checkboxWidgetIndex = WIDX_CHECKBOX_0; + Widget* checkboxWidget = &widgets[checkboxWidgetIndex]; + for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) + { + const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; + if (ndef->category != page) + continue; + + checkboxWidget->type = WindowWidgetType::Checkbox; + checkboxWidget->left = baseCheckBox.left; + checkboxWidget->right = baseCheckBox.right; + checkboxWidget->top = y; + checkboxWidget->bottom = checkboxWidget->top + LIST_ROW_HEIGHT + 3; + checkboxWidget->text = ndef->caption; + + const bool* configValue = GetNotificationValuePtr(ndef); + SetCheckboxValue(checkboxWidgetIndex, *configValue); + + checkboxWidgetIndex++; + checkboxWidget++; + y += LIST_ROW_HEIGHT + 3; + } + + // Remove unused checkboxes + while (checkboxWidget->type != WindowWidgetType::Last) + { + checkboxWidget->type = WindowWidgetType::Empty; + checkboxWidgetIndex++; + checkboxWidget++; + } + + // Resize window to fit checkboxes exactly + y += 3; + + if (height != y) + { + Invalidate(); + height = y; + widgets[WIDX_BACKGROUND].bottom = y - 1; + widgets[WIDX_TAB_CONTENT_PANEL].bottom = y - 1; + Invalidate(); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(dpi); + } + + private: + void SetPage(int32_t p) + { + if (page != p) + { + page = p; + frame_no = 0; + Invalidate(); + } + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + DrawTabImage(dpi, NOTIFICATION_CATEGORY_PARK, SPR_TAB_PARK); + DrawTabImage(dpi, NOTIFICATION_CATEGORY_RIDE, SPR_TAB_RIDE_0); + DrawTabImage(dpi, NOTIFICATION_CATEGORY_GUEST, SPR_TAB_GUESTS_0); + } + + void DrawTabImage(DrawPixelInfo& dpi, int32_t p, int32_t spriteIndex) + { + WidgetIndex widgetIndex = WIDX_FIRST_TAB + p; + + if (!WidgetIsDisabled(*this, widgetIndex)) + { + if (page == p) + { + int32_t numFrames = TabAnimationFrames[page]; + if (numFrames > 1) + { + int32_t frame = frame_no / TabAnimationDivisor[page]; + spriteIndex += (frame % numFrames); } } - break; + + const auto& widget = widgets[widgetIndex]; + GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget.left, widget.top }); } } - } - void OnUpdate() override - { - // Tab animation - frame_no++; - InvalidateWidget(WIDX_FIRST_TAB + page); - } - - void OnPrepareDraw() override - { - SetPressedTab(); - - // Set checkboxes - const auto& baseCheckBox = widgets[WIDX_CHECKBOX_0]; - int32_t y = baseCheckBox.top; - - int32_t checkboxWidgetIndex = WIDX_CHECKBOX_0; - Widget* checkboxWidget = &widgets[checkboxWidgetIndex]; - for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) + void SetPressedTab() { - const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; - if (ndef->category != page) - continue; - - checkboxWidget->type = WindowWidgetType::Checkbox; - checkboxWidget->left = baseCheckBox.left; - checkboxWidget->right = baseCheckBox.right; - checkboxWidget->top = y; - checkboxWidget->bottom = checkboxWidget->top + LIST_ROW_HEIGHT + 3; - checkboxWidget->text = ndef->caption; - - const bool* configValue = GetNotificationValuePtr(ndef); - SetCheckboxValue(checkboxWidgetIndex, *configValue); - - checkboxWidgetIndex++; - checkboxWidget++; - y += LIST_ROW_HEIGHT + 3; + for (int32_t i = 0; i < NOTIFICATION_CATEGORY_COUNT; i++) + pressed_widgets &= ~(1 << (WIDX_FIRST_TAB + i)); + pressed_widgets |= 1LL << (WIDX_FIRST_TAB + page); } - // Remove unused checkboxes - while (checkboxWidget->type != WindowWidgetType::Last) + bool* GetNotificationValuePtr(const NotificationDef* ndef) { - checkboxWidget->type = WindowWidgetType::Empty; - checkboxWidgetIndex++; - checkboxWidget++; + bool* configValue = reinterpret_cast(reinterpret_cast(&gConfigNotifications) + ndef->config_offset); + return configValue; } - // Resize window to fit checkboxes exactly - y += 3; - - if (height != y) + void OnResize() override { - Invalidate(); - height = y; - widgets[WIDX_BACKGROUND].bottom = y - 1; - widgets[WIDX_TAB_CONTENT_PANEL].bottom = y - 1; - Invalidate(); + ResizeFrameWithPage(); } - } - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - } - -private: - void SetPage(int32_t p) - { - if (page != p) - { - page = p; - frame_no = 0; - Invalidate(); - } - } - - void DrawTabImages(DrawPixelInfo& dpi) - { - DrawTabImage(dpi, NOTIFICATION_CATEGORY_PARK, SPR_TAB_PARK); - DrawTabImage(dpi, NOTIFICATION_CATEGORY_RIDE, SPR_TAB_RIDE_0); - DrawTabImage(dpi, NOTIFICATION_CATEGORY_GUEST, SPR_TAB_GUESTS_0); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t p, int32_t spriteIndex) - { - WidgetIndex widgetIndex = WIDX_FIRST_TAB + p; - - if (!WidgetIsDisabled(*this, widgetIndex)) - { - if (page == p) - { - int32_t numFrames = TabAnimationFrames[page]; - if (numFrames > 1) - { - int32_t frame = frame_no / TabAnimationDivisor[page]; - spriteIndex += (frame % numFrames); - } - } - - const auto& widget = widgets[widgetIndex]; - GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget.left, widget.top }); - } - } - - void SetPressedTab() - { - for (int32_t i = 0; i < NOTIFICATION_CATEGORY_COUNT; i++) - pressed_widgets &= ~(1 << (WIDX_FIRST_TAB + i)); - pressed_widgets |= 1LL << (WIDX_FIRST_TAB + page); - } - - bool* GetNotificationValuePtr(const NotificationDef* ndef) - { - bool* configValue = reinterpret_cast(reinterpret_cast(&gConfigNotifications) + ndef->config_offset); - return configValue; - } - - void OnResize() override - { - ResizeFrameWithPage(); - } - - static constexpr int32_t TabAnimationDivisor[3] = { - 1, // Park - 4, // Ride - 4, // Guest + static constexpr int32_t TabAnimationDivisor[3] = { + 1, // Park + 4, // Ride + 4, // Guest + }; + static constexpr int32_t TabAnimationFrames[3] = { + 1, // Park + 16, // Ride + 8, // Guest + }; }; - static constexpr int32_t TabAnimationFrames[3] = { - 1, // Park - 16, // Ride - 8, // Guest - }; -}; -WindowBase* WindowNewsOptionsOpen() -{ - return WindowFocusOrCreate(WindowClass::NotificationOptions, WW, WH, WF_CENTRE_SCREEN); -} + WindowBase* WindowNewsOptionsOpen() + { + return WindowFocusOrCreate(WindowClass::NotificationOptions, WW, WH, WF_CENTRE_SCREEN); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 88ca2b2a72..c5100c7277 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -27,232 +27,233 @@ #include #include #include - +namespace OpenRCT2::Ui::Windows +{ #ifndef DISABLE_HTTP -class ObjectDownloader -{ -private: - static constexpr auto OPENRCT2_API_LEGACY_OBJECT_URL = "https://api.openrct2.io/objects/legacy/"; - - struct DownloadStatusInfo + class ObjectDownloader { - std::string Name; - std::string Source; - size_t Count{}; - size_t Total{}; + private: + static constexpr auto OPENRCT2_API_LEGACY_OBJECT_URL = "https://api.openrct2.io/objects/legacy/"; - bool operator==(const DownloadStatusInfo& rhs) const + struct DownloadStatusInfo { - return Name == rhs.Name && Source == rhs.Source && Count == rhs.Count && Total == rhs.Total; + std::string Name; + std::string Source; + size_t Count{}; + size_t Total{}; + + bool operator==(const DownloadStatusInfo& rhs) const + { + return Name == rhs.Name && Source == rhs.Source && Count == rhs.Count && Total == rhs.Total; + } + bool operator!=(const DownloadStatusInfo& rhs) const + { + return !(*this == rhs); + } + }; + + std::vector _entries; + std::vector _downloadedEntries; + size_t _currentDownloadIndex{}; + std::mutex _downloadedEntriesMutex; + std::mutex _queueMutex; + bool _nextDownloadQueued{}; + + DownloadStatusInfo _lastDownloadStatusInfo; + DownloadStatusInfo _downloadStatusInfo; + std::mutex _downloadStatusInfoMutex; + std::string _lastDownloadSource; + + // TODO static due to INTENT_EXTRA_CALLBACK not allowing a std::function + inline static bool _downloadingObjects; + + public: + void Begin(const std::vector& entries) + { + _lastDownloadStatusInfo = {}; + _downloadStatusInfo = {}; + _lastDownloadSource = {}; + _entries = entries; + _currentDownloadIndex = 0; + _downloadingObjects = true; + QueueNextDownload(); } - bool operator!=(const DownloadStatusInfo& rhs) const + + bool IsDownloading() const { - return !(*this == rhs); + return _downloadingObjects; + } + + std::vector GetDownloadedEntries() + { + std::lock_guard guard(_downloadedEntriesMutex); + return _downloadedEntries; + } + + void Update() + { + std::lock_guard guard(_queueMutex); + if (_nextDownloadQueued) + { + _nextDownloadQueued = false; + NextDownload(); + } + UpdateStatusBox(); + } + + private: + void UpdateStatusBox() + { + std::lock_guard guard(_downloadStatusInfoMutex); + if (_lastDownloadStatusInfo != _downloadStatusInfo) + { + _lastDownloadStatusInfo = _downloadStatusInfo; + + if (_downloadStatusInfo == DownloadStatusInfo()) + { + ContextForceCloseWindowByClass(WindowClass::NetworkStatus); + } + else + { + char str_downloading_objects[256]{}; + Formatter ft; + if (_downloadStatusInfo.Source.empty()) + { + ft.Add(static_cast(_downloadStatusInfo.Count)); + ft.Add(static_cast(_downloadStatusInfo.Total)); + ft.Add(_downloadStatusInfo.Name.c_str()); + OpenRCT2::FormatStringLegacy( + str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS, ft.Data()); + } + else + { + ft.Add(_downloadStatusInfo.Name.c_str()); + ft.Add(_downloadStatusInfo.Source.c_str()); + ft.Add(static_cast(_downloadStatusInfo.Count)); + ft.Add(static_cast(_downloadStatusInfo.Total)); + OpenRCT2::FormatStringLegacy( + str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS_FROM, ft.Data()); + } + + auto intent = Intent(WindowClass::NetworkStatus); + intent.PutExtra(INTENT_EXTRA_MESSAGE, std::string(str_downloading_objects)); + intent.PutExtra(INTENT_EXTRA_CALLBACK, []() -> void { _downloadingObjects = false; }); + ContextOpenIntent(&intent); + } + } + } + + void UpdateProgress(const DownloadStatusInfo& info) + { + std::lock_guard guard(_downloadStatusInfoMutex); + _downloadStatusInfo = info; + } + + void QueueNextDownload() + { + std::lock_guard guard(_queueMutex); + _nextDownloadQueued = true; + } + + void DownloadObject(const ObjectEntryDescriptor& entry, const std::string& name, const std::string& url) + { + try + { + Console::WriteLine("Downloading %s", url.c_str()); + Http::Request req; + req.method = Http::Method::GET; + req.url = url; + Http::DoAsync(req, [this, entry, name](Http::Response response) { + if (response.status == Http::Status::Ok) + { + // Check that download operation hasn't been cancelled + if (_downloadingObjects) + { + auto data = reinterpret_cast(response.body.data()); + auto dataLen = response.body.size(); + + auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); + objRepo.AddObjectFromFile(ObjectGeneration::DAT, name, data, dataLen); + + std::lock_guard guard(_downloadedEntriesMutex); + _downloadedEntries.push_back(entry); + } + } + else + { + Console::Error::WriteLine(" Failed to download %s", name.c_str()); + } + QueueNextDownload(); + }); + } + catch (const std::exception&) + { + Console::Error::WriteLine(" Failed to download %s", name.c_str()); + QueueNextDownload(); + } + } + + void NextDownload() + { + if (!_downloadingObjects || _currentDownloadIndex >= _entries.size()) + { + // Finished... + _downloadingObjects = false; + UpdateProgress({}); + return; + } + + auto& entry = _entries[_currentDownloadIndex]; + auto name = String::Trim(std::string(entry.GetName())); + LOG_VERBOSE("Downloading object: [%s]:", name.c_str()); + _currentDownloadIndex++; + UpdateProgress({ name, _lastDownloadSource, _currentDownloadIndex, _entries.size() }); + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = OPENRCT2_API_LEGACY_OBJECT_URL + name; + Http::DoAsync(req, [this, entry, name](Http::Response response) { + if (response.status == Http::Status::Ok) + { + auto jresponse = Json::FromString(response.body); + if (jresponse.is_object()) + { + auto objName = Json::GetString(jresponse["name"]); + auto source = Json::GetString(jresponse["source"]); + auto downloadLink = Json::GetString(jresponse["download"]); + if (!downloadLink.empty()) + { + _lastDownloadSource = source; + UpdateProgress({ name, source, _currentDownloadIndex, _entries.size() }); + DownloadObject(entry, objName, downloadLink); + } + } + } + else if (response.status == Http::Status::NotFound) + { + Console::Error::WriteLine(" %s not found", name.c_str()); + QueueNextDownload(); + } + else + { + Console::Error::WriteLine( + " %s query failed (status %d)", name.c_str(), static_cast(response.status)); + QueueNextDownload(); + } + }); + } + catch (const std::exception&) + { + Console::Error::WriteLine(" Failed to query %s", name.c_str()); + } } }; - std::vector _entries; - std::vector _downloadedEntries; - size_t _currentDownloadIndex{}; - std::mutex _downloadedEntriesMutex; - std::mutex _queueMutex; - bool _nextDownloadQueued{}; - - DownloadStatusInfo _lastDownloadStatusInfo; - DownloadStatusInfo _downloadStatusInfo; - std::mutex _downloadStatusInfoMutex; - std::string _lastDownloadSource; - - // TODO static due to INTENT_EXTRA_CALLBACK not allowing a std::function - inline static bool _downloadingObjects; - -public: - void Begin(const std::vector& entries) - { - _lastDownloadStatusInfo = {}; - _downloadStatusInfo = {}; - _lastDownloadSource = {}; - _entries = entries; - _currentDownloadIndex = 0; - _downloadingObjects = true; - QueueNextDownload(); - } - - bool IsDownloading() const - { - return _downloadingObjects; - } - - std::vector GetDownloadedEntries() - { - std::lock_guard guard(_downloadedEntriesMutex); - return _downloadedEntries; - } - - void Update() - { - std::lock_guard guard(_queueMutex); - if (_nextDownloadQueued) - { - _nextDownloadQueued = false; - NextDownload(); - } - UpdateStatusBox(); - } - -private: - void UpdateStatusBox() - { - std::lock_guard guard(_downloadStatusInfoMutex); - if (_lastDownloadStatusInfo != _downloadStatusInfo) - { - _lastDownloadStatusInfo = _downloadStatusInfo; - - if (_downloadStatusInfo == DownloadStatusInfo()) - { - ContextForceCloseWindowByClass(WindowClass::NetworkStatus); - } - else - { - char str_downloading_objects[256]{}; - Formatter ft; - if (_downloadStatusInfo.Source.empty()) - { - ft.Add(static_cast(_downloadStatusInfo.Count)); - ft.Add(static_cast(_downloadStatusInfo.Total)); - ft.Add(_downloadStatusInfo.Name.c_str()); - OpenRCT2::FormatStringLegacy( - str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS, ft.Data()); - } - else - { - ft.Add(_downloadStatusInfo.Name.c_str()); - ft.Add(_downloadStatusInfo.Source.c_str()); - ft.Add(static_cast(_downloadStatusInfo.Count)); - ft.Add(static_cast(_downloadStatusInfo.Total)); - OpenRCT2::FormatStringLegacy( - str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS_FROM, ft.Data()); - } - - auto intent = Intent(WindowClass::NetworkStatus); - intent.PutExtra(INTENT_EXTRA_MESSAGE, std::string(str_downloading_objects)); - intent.PutExtra(INTENT_EXTRA_CALLBACK, []() -> void { _downloadingObjects = false; }); - ContextOpenIntent(&intent); - } - } - } - - void UpdateProgress(const DownloadStatusInfo& info) - { - std::lock_guard guard(_downloadStatusInfoMutex); - _downloadStatusInfo = info; - } - - void QueueNextDownload() - { - std::lock_guard guard(_queueMutex); - _nextDownloadQueued = true; - } - - void DownloadObject(const ObjectEntryDescriptor& entry, const std::string& name, const std::string& url) - { - try - { - Console::WriteLine("Downloading %s", url.c_str()); - Http::Request req; - req.method = Http::Method::GET; - req.url = url; - Http::DoAsync(req, [this, entry, name](Http::Response response) { - if (response.status == Http::Status::Ok) - { - // Check that download operation hasn't been cancelled - if (_downloadingObjects) - { - auto data = reinterpret_cast(response.body.data()); - auto dataLen = response.body.size(); - - auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); - objRepo.AddObjectFromFile(ObjectGeneration::DAT, name, data, dataLen); - - std::lock_guard guard(_downloadedEntriesMutex); - _downloadedEntries.push_back(entry); - } - } - else - { - Console::Error::WriteLine(" Failed to download %s", name.c_str()); - } - QueueNextDownload(); - }); - } - catch (const std::exception&) - { - Console::Error::WriteLine(" Failed to download %s", name.c_str()); - QueueNextDownload(); - } - } - - void NextDownload() - { - if (!_downloadingObjects || _currentDownloadIndex >= _entries.size()) - { - // Finished... - _downloadingObjects = false; - UpdateProgress({}); - return; - } - - auto& entry = _entries[_currentDownloadIndex]; - auto name = String::Trim(std::string(entry.GetName())); - LOG_VERBOSE("Downloading object: [%s]:", name.c_str()); - _currentDownloadIndex++; - UpdateProgress({ name, _lastDownloadSource, _currentDownloadIndex, _entries.size() }); - try - { - Http::Request req; - req.method = Http::Method::GET; - req.url = OPENRCT2_API_LEGACY_OBJECT_URL + name; - Http::DoAsync(req, [this, entry, name](Http::Response response) { - if (response.status == Http::Status::Ok) - { - auto jresponse = Json::FromString(response.body); - if (jresponse.is_object()) - { - auto objName = Json::GetString(jresponse["name"]); - auto source = Json::GetString(jresponse["source"]); - auto downloadLink = Json::GetString(jresponse["download"]); - if (!downloadLink.empty()) - { - _lastDownloadSource = source; - UpdateProgress({ name, source, _currentDownloadIndex, _entries.size() }); - DownloadObject(entry, objName, downloadLink); - } - } - } - else if (response.status == Http::Status::NotFound) - { - Console::Error::WriteLine(" %s not found", name.c_str()); - QueueNextDownload(); - } - else - { - Console::Error::WriteLine( - " %s query failed (status %d)", name.c_str(), static_cast(response.status)); - QueueNextDownload(); - } - }); - } - catch (const std::exception&) - { - Console::Error::WriteLine(" Failed to query %s", name.c_str()); - } - } -}; - #endif -// clang-format off + // clang-format off enum WindowObjectLoadErrorWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -287,297 +288,299 @@ static Widget window_object_load_error_widgets[] = { #endif kWidgetsEnd, }; -// clang-format on - -/** - * Returns an StringId that represents an RCTObjectEntry's type. - * - * Could possibly be moved out of the window file if other - * uses exist and a suitable location is found. - */ -static constexpr StringId GetStringFromObjectType(const ObjectType type) -{ - switch (type) - { - case ObjectType::Ride: - return STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS; - case ObjectType::SmallScenery: - return STR_OBJECT_SELECTION_SMALL_SCENERY; - case ObjectType::LargeScenery: - return STR_OBJECT_SELECTION_LARGE_SCENERY; - case ObjectType::Walls: - return STR_OBJECT_SELECTION_WALLS_FENCES; - case ObjectType::Banners: - return STR_OBJECT_SELECTION_PATH_SIGNS; - case ObjectType::Paths: - return STR_OBJECT_SELECTION_FOOTPATHS; - case ObjectType::PathAdditions: - return STR_OBJECT_SELECTION_PATH_EXTRAS; - case ObjectType::SceneryGroup: - return STR_OBJECT_SELECTION_SCENERY_GROUPS; - case ObjectType::ParkEntrance: - return STR_OBJECT_SELECTION_PARK_ENTRANCE; - case ObjectType::Water: - return STR_OBJECT_SELECTION_WATER; - default: - return STR_UNKNOWN_OBJECT_TYPE; - } -} - -class ObjectLoadErrorWindow final : public WindowBase -{ -private: - std::vector _invalidEntries; - int32_t _highlightedIndex = -1; - std::string _filePath; -#ifndef DISABLE_HTTP - ObjectDownloader _objDownloader; - bool _updatedListAfterDownload{}; - - void DownloadAllObjects() - { - if (!_objDownloader.IsDownloading()) - { - _updatedListAfterDownload = false; - _objDownloader.Begin(_invalidEntries); - } - } - - void UpdateObjectList() - { - const auto entries = _objDownloader.GetDownloadedEntries(); - for (auto& de : entries) - { - _invalidEntries.erase( - std::remove_if( - _invalidEntries.begin(), _invalidEntries.end(), - [de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }), - _invalidEntries.end()); - } - no_list_items = static_cast(_invalidEntries.size()); - } -#endif + // clang-format on /** - * Returns a newline-separated string listing all object names. - * Used for placing all names on the clipboard. + * Returns an StringId that represents an RCTObjectEntry's type. + * + * Could possibly be moved out of the window file if other + * uses exist and a suitable location is found. */ - void CopyObjectNamesToClipboard() + static constexpr StringId GetStringFromObjectType(const ObjectType type) { - std::stringstream stream; - for (uint16_t i = 0; i < no_list_items; i++) + switch (type) { - const auto& entry = _invalidEntries[i]; - stream << entry.GetName(); - stream << PLATFORM_NEWLINE; + case ObjectType::Ride: + return STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS; + case ObjectType::SmallScenery: + return STR_OBJECT_SELECTION_SMALL_SCENERY; + case ObjectType::LargeScenery: + return STR_OBJECT_SELECTION_LARGE_SCENERY; + case ObjectType::Walls: + return STR_OBJECT_SELECTION_WALLS_FENCES; + case ObjectType::Banners: + return STR_OBJECT_SELECTION_PATH_SIGNS; + case ObjectType::Paths: + return STR_OBJECT_SELECTION_FOOTPATHS; + case ObjectType::PathAdditions: + return STR_OBJECT_SELECTION_PATH_EXTRAS; + case ObjectType::SceneryGroup: + return STR_OBJECT_SELECTION_SCENERY_GROUPS; + case ObjectType::ParkEntrance: + return STR_OBJECT_SELECTION_PARK_ENTRANCE; + case ObjectType::Water: + return STR_OBJECT_SELECTION_WATER; + default: + return STR_UNKNOWN_OBJECT_TYPE; } - - const auto clip = stream.str(); - OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str()); } - void SelectObjectFromList(const int32_t index) + class ObjectLoadErrorWindow final : public WindowBase { - if (index < 0 || index > no_list_items) - { - selected_list_item = -1; - } - else - { - selected_list_item = index; - } - WidgetInvalidate(*this, WIDX_SCROLL); - } - -public: - void OnOpen() override - { - widgets = window_object_load_error_widgets; - - WindowInitScrollWidgets(*this); - colours[0] = COLOUR_LIGHT_BLUE; - colours[1] = COLOUR_LIGHT_BLUE; - colours[2] = COLOUR_LIGHT_BLUE; - } - - void OnClose() override - { - _invalidEntries.clear(); - _invalidEntries.shrink_to_fit(); - } - - void OnMouseUp(const WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*this); - return; - case WIDX_COPY_CURRENT: - if (selected_list_item > -1 && selected_list_item < no_list_items) - { - const auto name = std::string(_invalidEntries[selected_list_item].GetName()); - OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str()); - } - break; - case WIDX_COPY_ALL: - CopyObjectNamesToClipboard(); - break; + private: + std::vector _invalidEntries; + int32_t _highlightedIndex = -1; + std::string _filePath; #ifndef DISABLE_HTTP - case WIDX_DOWNLOAD_ALL: - DownloadAllObjects(); - break; -#endif - } - } + ObjectDownloader _objDownloader; + bool _updatedListAfterDownload{}; - void OnUpdate() override - { - frame_no++; - - // Check if the mouse is hovering over the list - if (!WidgetIsHighlighted(*this, WIDX_SCROLL)) + void DownloadAllObjects() { - _highlightedIndex = -1; + if (!_objDownloader.IsDownloading()) + { + _updatedListAfterDownload = false; + _objDownloader.Begin(_invalidEntries); + } + } + + void UpdateObjectList() + { + const auto entries = _objDownloader.GetDownloadedEntries(); + for (auto& de : entries) + { + _invalidEntries.erase( + std::remove_if( + _invalidEntries.begin(), _invalidEntries.end(), + [de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }), + _invalidEntries.end()); + } + no_list_items = static_cast(_invalidEntries.size()); + } +#endif + + /** + * Returns a newline-separated string listing all object names. + * Used for placing all names on the clipboard. + */ + void CopyObjectNamesToClipboard() + { + std::stringstream stream; + for (uint16_t i = 0; i < no_list_items; i++) + { + const auto& entry = _invalidEntries[i]; + stream << entry.GetName(); + stream << PLATFORM_NEWLINE; + } + + const auto clip = stream.str(); + OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str()); + } + + void SelectObjectFromList(const int32_t index) + { + if (index < 0 || index > no_list_items) + { + selected_list_item = -1; + } + else + { + selected_list_item = index; + } WidgetInvalidate(*this, WIDX_SCROLL); } -#ifndef DISABLE_HTTP - _objDownloader.Update(); - - // Remove downloaded objects from our invalid entry list - if (_objDownloader.IsDownloading()) + public: + void OnOpen() override { - // Don't do this too often as it isn't particularly efficient - if (frame_no % 64 == 0) + widgets = window_object_load_error_widgets; + + WindowInitScrollWidgets(*this); + colours[0] = COLOUR_LIGHT_BLUE; + colours[1] = COLOUR_LIGHT_BLUE; + colours[2] = COLOUR_LIGHT_BLUE; + } + + void OnClose() override + { + _invalidEntries.clear(); + _invalidEntries.shrink_to_fit(); + } + + void OnMouseUp(const WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*this); + return; + case WIDX_COPY_CURRENT: + if (selected_list_item > -1 && selected_list_item < no_list_items) + { + const auto name = std::string(_invalidEntries[selected_list_item].GetName()); + OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str()); + } + break; + case WIDX_COPY_ALL: + CopyObjectNamesToClipboard(); + break; +#ifndef DISABLE_HTTP + case WIDX_DOWNLOAD_ALL: + DownloadAllObjects(); + break; +#endif + } + } + + void OnUpdate() override + { + frame_no++; + + // Check if the mouse is hovering over the list + if (!WidgetIsHighlighted(*this, WIDX_SCROLL)) + { + _highlightedIndex = -1; + WidgetInvalidate(*this, WIDX_SCROLL); + } + +#ifndef DISABLE_HTTP + _objDownloader.Update(); + + // Remove downloaded objects from our invalid entry list + if (_objDownloader.IsDownloading()) + { + // Don't do this too often as it isn't particularly efficient + if (frame_no % 64 == 0) + { + UpdateObjectList(); + } + } + else if (!_updatedListAfterDownload) { UpdateObjectList(); + _updatedListAfterDownload = true; } - } - else if (!_updatedListAfterDownload) - { - UpdateObjectList(); - _updatedListAfterDownload = true; - } #endif - } - - ScreenSize OnScrollGetSize(const int32_t scrollIndex) override - { - return ScreenSize(0, no_list_items * SCROLLABLE_ROW_HEIGHT); - } - - void OnScrollMouseDown(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - SelectObjectFromList(selectedItem); - } - - void OnScrollMouseOver(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - // Highlight item that the cursor is over, or remove highlighting if none - const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (selectedItem < 0 || selectedItem >= no_list_items) - _highlightedIndex = -1; - else - _highlightedIndex = selectedItem; - - WidgetInvalidate(*this, WIDX_SCROLL); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - WindowDrawWidgets(*this, dpi); - - // Draw explanatory message - auto ft = Formatter(); - ft.Add(STR_OBJECT_ERROR_WINDOW_EXPLANATION); - DrawTextWrapped(dpi, windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft); - - // Draw file name - ft = Formatter(); - ft.Add(STR_OBJECT_ERROR_WINDOW_FILE); - ft.Add(_filePath.c_str()); - DrawTextEllipsised(dpi, { windowPos.x + 5, windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft); - } - - void OnScrollDraw(const int32_t scrollIndex, DrawPixelInfo& dpi) override - { - auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; - GfxFillRect( - dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - const int32_t listWidth = widgets[WIDX_SCROLL].width(); - - for (int32_t i = 0; i < no_list_items; i++) - { - ScreenCoordsXY screenCoords; - screenCoords.y = i * SCROLLABLE_ROW_HEIGHT; - if (screenCoords.y > dpi.y + dpi.height) - break; - - if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi.y) - continue; - - const auto screenRect = ScreenRect{ { 0, screenCoords.y }, - { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; - // If hovering over item, change the color and fill the backdrop. - if (i == selected_list_item) - GfxFillRect(dpi, screenRect, ColourMapA[colours[1]].darker); - else if (i == _highlightedIndex) - GfxFillRect(dpi, screenRect, ColourMapA[colours[1]].mid_dark); - else if ((i & 1) != 0) // odd / even check - GfxFillRect(dpi, screenRect, ColourMapA[colours[1]].light); - - // Draw the actual object entry's name... - screenCoords.x = NAME_COL_LEFT - 3; - - const auto& entry = _invalidEntries[i]; - - auto name = entry.GetName(); - char buffer[256]; - String::Set(buffer, sizeof(buffer), name.data(), name.size()); - GfxDrawString(dpi, screenCoords, buffer, { COLOUR_DARK_GREEN }); - - if (entry.Generation == ObjectGeneration::DAT) - { - // ... source game ... - const auto sourceStringId = ObjectManagerGetSourceGameString(entry.Entry.GetSourceGame()); - DrawTextBasic(dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN }); - } - - // ... and type - const auto type = GetStringFromObjectType(entry.GetType()); - DrawTextBasic(dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN }); } - } - void OnResize() override + ScreenSize OnScrollGetSize(const int32_t scrollIndex) override + { + return ScreenSize(0, no_list_items * SCROLLABLE_ROW_HEIGHT); + } + + void OnScrollMouseDown(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + SelectObjectFromList(selectedItem); + } + + void OnScrollMouseOver(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + // Highlight item that the cursor is over, or remove highlighting if none + const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (selectedItem < 0 || selectedItem >= no_list_items) + _highlightedIndex = -1; + else + _highlightedIndex = selectedItem; + + WidgetInvalidate(*this, WIDX_SCROLL); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + WindowDrawWidgets(*this, dpi); + + // Draw explanatory message + auto ft = Formatter(); + ft.Add(STR_OBJECT_ERROR_WINDOW_EXPLANATION); + DrawTextWrapped(dpi, windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft); + + // Draw file name + ft = Formatter(); + ft.Add(STR_OBJECT_ERROR_WINDOW_FILE); + ft.Add(_filePath.c_str()); + DrawTextEllipsised(dpi, { windowPos.x + 5, windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft); + } + + void OnScrollDraw(const int32_t scrollIndex, DrawPixelInfo& dpi) override + { + auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; + GfxFillRect( + dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, + ColourMapA[colours[1]].mid_light); + const int32_t listWidth = widgets[WIDX_SCROLL].width(); + + for (int32_t i = 0; i < no_list_items; i++) + { + ScreenCoordsXY screenCoords; + screenCoords.y = i * SCROLLABLE_ROW_HEIGHT; + if (screenCoords.y > dpi.y + dpi.height) + break; + + if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi.y) + continue; + + const auto screenRect = ScreenRect{ { 0, screenCoords.y }, + { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; + // If hovering over item, change the color and fill the backdrop. + if (i == selected_list_item) + GfxFillRect(dpi, screenRect, ColourMapA[colours[1]].darker); + else if (i == _highlightedIndex) + GfxFillRect(dpi, screenRect, ColourMapA[colours[1]].mid_dark); + else if ((i & 1) != 0) // odd / even check + GfxFillRect(dpi, screenRect, ColourMapA[colours[1]].light); + + // Draw the actual object entry's name... + screenCoords.x = NAME_COL_LEFT - 3; + + const auto& entry = _invalidEntries[i]; + + auto name = entry.GetName(); + char buffer[256]; + String::Set(buffer, sizeof(buffer), name.data(), name.size()); + GfxDrawString(dpi, screenCoords, buffer, { COLOUR_DARK_GREEN }); + + if (entry.Generation == ObjectGeneration::DAT) + { + // ... source game ... + const auto sourceStringId = ObjectManagerGetSourceGameString(entry.Entry.GetSourceGame()); + DrawTextBasic(dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN }); + } + + // ... and type + const auto type = GetStringFromObjectType(entry.GetType()); + DrawTextBasic(dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN }); + } + } + + void OnResize() override + { + ResizeFrame(); + } + + void Initialise(utf8* path, const size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects) + { + _invalidEntries = std::vector(missingObjects, missingObjects + numMissingObjects); + + // Refresh list items and path + no_list_items = static_cast(numMissingObjects); + _filePath = path; + + Invalidate(); + } + }; + + WindowBase* WindowObjectLoadErrorOpen(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects) { - ResizeFrame(); + // Check if window is already open + auto* window = WindowBringToFrontByClass(WindowClass::ObjectLoadError); + if (window == nullptr) + { + window = WindowCreate(WindowClass::ObjectLoadError, WW, WH, 0); + } + + static_cast(window)->Initialise(path, numMissingObjects, missingObjects); + + return window; } - - void Initialise(utf8* path, const size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects) - { - _invalidEntries = std::vector(missingObjects, missingObjects + numMissingObjects); - - // Refresh list items and path - no_list_items = static_cast(numMissingObjects); - _filePath = path; - - Invalidate(); - } -}; - -WindowBase* WindowObjectLoadErrorOpen(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects) -{ - // Check if window is already open - auto* window = WindowBringToFrontByClass(WindowClass::ObjectLoadError); - if (window == nullptr) - { - window = WindowCreate(WindowClass::ObjectLoadError, WW, WH, 0); - } - - static_cast(window)->Initialise(path, numMissingObjects, missingObjects); - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Options.cpp b/src/openrct2-ui/windows/Options.cpp index 3d12d0ec13..9d2a0dab49 100644 --- a/src/openrct2-ui/windows/Options.cpp +++ b/src/openrct2-ui/windows/Options.cpp @@ -46,10 +46,10 @@ #include #include -using namespace OpenRCT2; using namespace OpenRCT2::Audio; - -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowOptionsPage { WINDOW_OPTIONS_PAGE_DISPLAY, WINDOW_OPTIONS_PAGE_RENDERING, @@ -411,1791 +411,1798 @@ static Widget *window_options_page_widgets[] = { }; #pragma endregion -// clang-format on + // clang-format on -class OptionsWindow final : public Window -{ -public: - void OnOpen() override + class OptionsWindow final : public Window { - widgets = window_options_display_widgets; - page = WINDOW_OPTIONS_PAGE_DISPLAY; - frame_no = 0; - InitScrollWidgets(); - } + public: + void OnOpen() override + { + widgets = window_options_display_widgets; + page = WINDOW_OPTIONS_PAGE_DISPLAY; + frame_no = 0; + InitScrollWidgets(); + } - void OnMouseUp(WidgetIndex widgetIndex) override - { - if (widgetIndex < WIDX_PAGE_START) - CommonMouseUp(widgetIndex); - else + void OnMouseUp(WidgetIndex widgetIndex) override + { + if (widgetIndex < WIDX_PAGE_START) + CommonMouseUp(widgetIndex); + else + { + switch (page) + { + case WINDOW_OPTIONS_PAGE_DISPLAY: + DisplayMouseUp(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_RENDERING: + RenderingMouseUp(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_AUDIO: + AudioMouseUp(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: + ControlsMouseUp(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_MISC: + MiscMouseUp(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_ADVANCED: + AdvancedMouseUp(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_CULTURE: + default: + break; + } + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override { switch (page) { case WINDOW_OPTIONS_PAGE_DISPLAY: - DisplayMouseUp(widgetIndex); + DisplayMouseDown(widgetIndex); break; case WINDOW_OPTIONS_PAGE_RENDERING: - RenderingMouseUp(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_AUDIO: - AudioMouseUp(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: - ControlsMouseUp(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_MISC: - MiscMouseUp(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_ADVANCED: - AdvancedMouseUp(widgetIndex); + RenderingMouseDown(widgetIndex); break; case WINDOW_OPTIONS_PAGE_CULTURE: + CultureMouseDown(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_AUDIO: + AudioMouseDown(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: + ControlsMouseDown(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_MISC: + MiscMouseDown(widgetIndex); + break; + case WINDOW_OPTIONS_PAGE_ADVANCED: + AdvancedMouseDown(widgetIndex); + break; default: break; } } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (page) + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override { - case WINDOW_OPTIONS_PAGE_DISPLAY: - DisplayMouseDown(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_RENDERING: - RenderingMouseDown(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_CULTURE: - CultureMouseDown(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_AUDIO: - AudioMouseDown(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: - ControlsMouseDown(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_MISC: - MiscMouseDown(widgetIndex); - break; - case WINDOW_OPTIONS_PAGE_ADVANCED: - AdvancedMouseDown(widgetIndex); - break; - default: - break; - } - } + if (dropdownIndex == -1) + return; - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (dropdownIndex == -1) - return; - - switch (page) - { - case WINDOW_OPTIONS_PAGE_DISPLAY: - DisplayDropdown(widgetIndex, dropdownIndex); - break; - case WINDOW_OPTIONS_PAGE_RENDERING: - RenderingDropdown(widgetIndex, dropdownIndex); - break; - case WINDOW_OPTIONS_PAGE_CULTURE: - CultureDropdown(widgetIndex, dropdownIndex); - break; - case WINDOW_OPTIONS_PAGE_AUDIO: - AudioDropdown(widgetIndex, dropdownIndex); - break; - case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: - ControlsDropdown(widgetIndex, dropdownIndex); - break; - case WINDOW_OPTIONS_PAGE_MISC: - MiscDropdown(widgetIndex, dropdownIndex); - break; - case WINDOW_OPTIONS_PAGE_ADVANCED: - AdvancedDropdown(widgetIndex, dropdownIndex); - break; - default: - break; - } - } - - void OnPrepareDraw() override - { - CommonPrepareDrawBefore(); - - switch (page) - { - case WINDOW_OPTIONS_PAGE_DISPLAY: - DisplayPrepareDraw(); - break; - case WINDOW_OPTIONS_PAGE_RENDERING: - RenderingPrepareDraw(); - break; - case WINDOW_OPTIONS_PAGE_CULTURE: - CulturePrepareDraw(); - break; - case WINDOW_OPTIONS_PAGE_AUDIO: - AudioPrepareDraw(); - break; - case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: - ControlsPrepareDraw(); - break; - case WINDOW_OPTIONS_PAGE_MISC: - MiscPrepareDraw(); - break; - case WINDOW_OPTIONS_PAGE_ADVANCED: - AdvancedPrepareDraw(); - break; - default: - break; + switch (page) + { + case WINDOW_OPTIONS_PAGE_DISPLAY: + DisplayDropdown(widgetIndex, dropdownIndex); + break; + case WINDOW_OPTIONS_PAGE_RENDERING: + RenderingDropdown(widgetIndex, dropdownIndex); + break; + case WINDOW_OPTIONS_PAGE_CULTURE: + CultureDropdown(widgetIndex, dropdownIndex); + break; + case WINDOW_OPTIONS_PAGE_AUDIO: + AudioDropdown(widgetIndex, dropdownIndex); + break; + case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: + ControlsDropdown(widgetIndex, dropdownIndex); + break; + case WINDOW_OPTIONS_PAGE_MISC: + MiscDropdown(widgetIndex, dropdownIndex); + break; + case WINDOW_OPTIONS_PAGE_ADVANCED: + AdvancedDropdown(widgetIndex, dropdownIndex); + break; + default: + break; + } } - CommonPrepareDrawAfter(); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - switch (page) + void OnPrepareDraw() override { - case WINDOW_OPTIONS_PAGE_DISPLAY: - DisplayDraw(dpi); - break; - case WINDOW_OPTIONS_PAGE_ADVANCED: - AdvancedDraw(dpi); - break; - default: - break; + CommonPrepareDrawBefore(); + + switch (page) + { + case WINDOW_OPTIONS_PAGE_DISPLAY: + DisplayPrepareDraw(); + break; + case WINDOW_OPTIONS_PAGE_RENDERING: + RenderingPrepareDraw(); + break; + case WINDOW_OPTIONS_PAGE_CULTURE: + CulturePrepareDraw(); + break; + case WINDOW_OPTIONS_PAGE_AUDIO: + AudioPrepareDraw(); + break; + case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: + ControlsPrepareDraw(); + break; + case WINDOW_OPTIONS_PAGE_MISC: + MiscPrepareDraw(); + break; + case WINDOW_OPTIONS_PAGE_ADVANCED: + AdvancedPrepareDraw(); + break; + default: + break; + } + + CommonPrepareDrawAfter(); } - } - void OnUpdate() override - { - CommonUpdate(); - - switch (page) + void OnDraw(DrawPixelInfo& dpi) override { - case WINDOW_OPTIONS_PAGE_AUDIO: - AudioUpdate(); - break; - case WINDOW_OPTIONS_PAGE_DISPLAY: - case WINDOW_OPTIONS_PAGE_RENDERING: - case WINDOW_OPTIONS_PAGE_CULTURE: - case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: - case WINDOW_OPTIONS_PAGE_MISC: - case WINDOW_OPTIONS_PAGE_ADVANCED: - default: - break; - } - } + DrawWidgets(dpi); + DrawTabImages(dpi); - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - switch (page) + switch (page) + { + case WINDOW_OPTIONS_PAGE_DISPLAY: + DisplayDraw(dpi); + break; + case WINDOW_OPTIONS_PAGE_ADVANCED: + AdvancedDraw(dpi); + break; + default: + break; + } + } + + void OnUpdate() override { - case WINDOW_OPTIONS_PAGE_AUDIO: - return AudioScrollGetSize(scrollIndex); - case WINDOW_OPTIONS_PAGE_DISPLAY: - case WINDOW_OPTIONS_PAGE_RENDERING: - case WINDOW_OPTIONS_PAGE_CULTURE: - case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: - case WINDOW_OPTIONS_PAGE_MISC: - case WINDOW_OPTIONS_PAGE_ADVANCED: - default: - return { WW, WH }; + CommonUpdate(); + + switch (page) + { + case WINDOW_OPTIONS_PAGE_AUDIO: + AudioUpdate(); + break; + case WINDOW_OPTIONS_PAGE_DISPLAY: + case WINDOW_OPTIONS_PAGE_RENDERING: + case WINDOW_OPTIONS_PAGE_CULTURE: + case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: + case WINDOW_OPTIONS_PAGE_MISC: + case WINDOW_OPTIONS_PAGE_ADVANCED: + default: + break; + } } - } - OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override - { - if (page == WINDOW_OPTIONS_PAGE_ADVANCED) - return AdvancedTooltip(widgetIndex, fallback); + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + switch (page) + { + case WINDOW_OPTIONS_PAGE_AUDIO: + return AudioScrollGetSize(scrollIndex); + case WINDOW_OPTIONS_PAGE_DISPLAY: + case WINDOW_OPTIONS_PAGE_RENDERING: + case WINDOW_OPTIONS_PAGE_CULTURE: + case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE: + case WINDOW_OPTIONS_PAGE_MISC: + case WINDOW_OPTIONS_PAGE_ADVANCED: + default: + return { WW, WH }; + } + } - return WindowBase::OnTooltip(widgetIndex, fallback); - } + OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override + { + if (page == WINDOW_OPTIONS_PAGE_ADVANCED) + return AdvancedTooltip(widgetIndex, fallback); -private: + return WindowBase::OnTooltip(widgetIndex, fallback); + } + + private: #pragma region Common events - void CommonMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void CommonMouseUp(WidgetIndex widgetIndex) { - case WIDX_CLOSE: - WindowClose(*this); - break; - case WIDX_TAB_DISPLAY: - case WIDX_TAB_RENDERING: - case WIDX_TAB_CULTURE: - case WIDX_TAB_AUDIO: - case WIDX_TAB_CONTROLS_AND_INTERFACE: - case WIDX_TAB_MISC: - case WIDX_TAB_ADVANCED: - SetPage(widgetIndex - WIDX_FIRST_TAB); - break; + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*this); + break; + case WIDX_TAB_DISPLAY: + case WIDX_TAB_RENDERING: + case WIDX_TAB_CULTURE: + case WIDX_TAB_AUDIO: + case WIDX_TAB_CONTROLS_AND_INTERFACE: + case WIDX_TAB_MISC: + case WIDX_TAB_ADVANCED: + SetPage(widgetIndex - WIDX_FIRST_TAB); + break; + } } - } - void CommonPrepareDrawBefore() - { - if (window_options_page_widgets[page] != widgets) + void CommonPrepareDrawBefore() { - widgets = window_options_page_widgets[page]; - InitScrollWidgets(); - } - SetPressedTab(); + if (window_options_page_widgets[page] != widgets) + { + widgets = window_options_page_widgets[page]; + InitScrollWidgets(); + } + SetPressedTab(); - disabled_widgets = 0; - auto hasFilePicker = OpenRCT2::GetContext()->GetUiContext()->HasFilePicker(); - const bool controlsTabSelected = (WIDX_FIRST_TAB + page) == WIDX_TAB_CONTROLS_AND_INTERFACE; - if (!hasFilePicker && controlsTabSelected) + disabled_widgets = 0; + auto hasFilePicker = OpenRCT2::GetContext()->GetUiContext()->HasFilePicker(); + const bool controlsTabSelected = (WIDX_FIRST_TAB + page) == WIDX_TAB_CONTROLS_AND_INTERFACE; + if (!hasFilePicker && controlsTabSelected) + { + disabled_widgets |= (1uLL << WIDX_ALWAYS_NATIVE_LOADSAVE); + widgets[WIDX_ALWAYS_NATIVE_LOADSAVE].type = WindowWidgetType::Empty; + } + } + + void CommonPrepareDrawAfter() { - disabled_widgets |= (1uLL << WIDX_ALWAYS_NATIVE_LOADSAVE); - widgets[WIDX_ALWAYS_NATIVE_LOADSAVE].type = WindowWidgetType::Empty; + // Automatically adjust window height to fit widgets + int32_t y = 0; + for (auto widget = &widgets[WIDX_PAGE_START]; widget->type != WindowWidgetType::Last; widget++) + { + y = std::max(y, widget->bottom); + } + height = y + 6; + ResizeFrameWithPage(); } - } - void CommonPrepareDrawAfter() - { - // Automatically adjust window height to fit widgets - int32_t y = 0; - for (auto widget = &widgets[WIDX_PAGE_START]; widget->type != WindowWidgetType::Last; widget++) + void OnResize() override { - y = std::max(y, widget->bottom); + ResizeFrameWithPage(); } - height = y + 6; - ResizeFrameWithPage(); - } - void OnResize() override - { - ResizeFrameWithPage(); - } - - void CommonUpdate() - { - // Tab animation - frame_no++; - InvalidateWidget(WIDX_FIRST_TAB + page); - } + void CommonUpdate() + { + // Tab animation + frame_no++; + InvalidateWidget(WIDX_FIRST_TAB + page); + } #pragma endregion #pragma region Display tab events - void DisplayMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void DisplayMouseUp(WidgetIndex widgetIndex) { - case WIDX_UNCAP_FPS_CHECKBOX: - gConfigGeneral.UncapFPS ^= 1; - DrawingEngineSetVSync(gConfigGeneral.UseVSync); - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_USE_VSYNC_CHECKBOX: - gConfigGeneral.UseVSync ^= 1; - DrawingEngineSetVSync(gConfigGeneral.UseVSync); - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_SHOW_FPS_CHECKBOX: - gConfigGeneral.ShowFPS ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_MULTITHREADING_CHECKBOX: - gConfigGeneral.MultiThreading ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_MINIMIZE_FOCUS_LOSS: - gConfigGeneral.MinimizeFullscreenFocusLoss ^= 1; - RefreshVideo(false); - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_STEAM_OVERLAY_PAUSE: - gConfigGeneral.SteamOverlayPause ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_DISABLE_SCREENSAVER_LOCK: - gConfigGeneral.DisableScreensaver ^= 1; - ApplyScreenSaverLockSetting(); - ConfigSaveDefault(); - Invalidate(); - break; - } - } - - void DisplayMouseDown(WidgetIndex widgetIndex) - { - Widget* widget = &widgets[widgetIndex - 1]; - - switch (widgetIndex) - { - case WIDX_RESOLUTION_DROPDOWN: + switch (widgetIndex) { - const auto& resolutions = OpenRCT2::GetContext()->GetUiContext()->GetFullscreenResolutions(); - - int32_t selectedResolution = -1; - for (size_t i = 0; i < resolutions.size(); i++) - { - const Resolution& resolution = resolutions[i]; - - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - - uint16_t* args = reinterpret_cast(&gDropdownItems[i].Args); - args[0] = STR_RESOLUTION_X_BY_Y; - args[1] = resolution.Width; - args[2] = resolution.Height; - - if (resolution.Width == gConfigGeneral.FullscreenWidth - && resolution.Height == gConfigGeneral.FullscreenHeight) - { - selectedResolution = static_cast(i); - } - } - - ShowDropdown(widget, static_cast(resolutions.size())); - - if (selectedResolution != -1 && selectedResolution < 32) - { - Dropdown::SetChecked(selectedResolution, true); - } - } - - break; - case WIDX_FULLSCREEN_DROPDOWN: - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_OPTIONS_DISPLAY_WINDOWED; - gDropdownItems[1].Args = STR_OPTIONS_DISPLAY_FULLSCREEN; - gDropdownItems[2].Args = STR_OPTIONS_DISPLAY_FULLSCREEN_BORDERLESS; - - ShowDropdown(widget, 3); - - Dropdown::SetChecked(gConfigGeneral.FullscreenMode, true); - break; - case WIDX_DRAWING_ENGINE_DROPDOWN: - { - int32_t numItems = 3; -#ifdef DISABLE_OPENGL - numItems = 2; -#endif - - for (int32_t i = 0; i < numItems; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = DrawingEngineStringIds[i]; - } - ShowDropdown(widget, numItems); - Dropdown::SetChecked(EnumValue(gConfigGeneral.DrawingEngine), true); - break; - } - case WIDX_SCALE_UP: - gConfigGeneral.WindowScale += 0.25f; - ConfigSaveDefault(); - GfxInvalidateScreen(); - ContextTriggerResize(); - ContextUpdateCursorScale(); - break; - case WIDX_SCALE_DOWN: - gConfigGeneral.WindowScale -= 0.25f; - gConfigGeneral.WindowScale = std::max(0.5f, gConfigGeneral.WindowScale); - ConfigSaveDefault(); - GfxInvalidateScreen(); - ContextTriggerResize(); - ContextUpdateCursorScale(); - break; - } - } - - void DisplayDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_RESOLUTION_DROPDOWN: - { - const auto& resolutions = OpenRCT2::GetContext()->GetUiContext()->GetFullscreenResolutions(); - - const Resolution& resolution = resolutions[dropdownIndex]; - if (resolution.Width != gConfigGeneral.FullscreenWidth || resolution.Height != gConfigGeneral.FullscreenHeight) - { - gConfigGeneral.FullscreenWidth = resolution.Width; - gConfigGeneral.FullscreenHeight = resolution.Height; - - if (gConfigGeneral.FullscreenMode == static_cast(OpenRCT2::Ui::FULLSCREEN_MODE::FULLSCREEN)) - ContextSetFullscreenMode(static_cast(OpenRCT2::Ui::FULLSCREEN_MODE::FULLSCREEN)); - - ConfigSaveDefault(); - GfxInvalidateScreen(); - } - } - break; - case WIDX_FULLSCREEN_DROPDOWN: - if (dropdownIndex != gConfigGeneral.FullscreenMode) - { - ContextSetFullscreenMode(dropdownIndex); - - gConfigGeneral.FullscreenMode = static_cast(dropdownIndex); - ConfigSaveDefault(); - GfxInvalidateScreen(); - } - break; - case WIDX_DRAWING_ENGINE_DROPDOWN: - if (dropdownIndex != EnumValue(gConfigGeneral.DrawingEngine)) - { - DrawingEngine srcEngine = drawing_engine_get_type(); - DrawingEngine dstEngine = static_cast(dropdownIndex); - - gConfigGeneral.DrawingEngine = dstEngine; - bool recreate_window = DrawingEngineRequiresNewWindow(srcEngine, dstEngine); - RefreshVideo(recreate_window); + case WIDX_UNCAP_FPS_CHECKBOX: + gConfigGeneral.UncapFPS ^= 1; + DrawingEngineSetVSync(gConfigGeneral.UseVSync); ConfigSaveDefault(); Invalidate(); - } - break; - } - } - - void DisplayPrepareDraw() - { - // Resolution dropdown caption. - auto ft = Formatter::Common(); - ft.Increment(16); - ft.Add(static_cast(gConfigGeneral.FullscreenWidth)); - ft.Add(static_cast(gConfigGeneral.FullscreenHeight)); - - // Disable resolution dropdown on "Windowed" and "Fullscreen (desktop)" - if (gConfigGeneral.FullscreenMode != static_cast(OpenRCT2::Ui::FULLSCREEN_MODE::FULLSCREEN)) - { - disabled_widgets |= (1uLL << WIDX_RESOLUTION_DROPDOWN); - disabled_widgets |= (1uLL << WIDX_RESOLUTION); - disabled_widgets |= (1uLL << WIDX_RESOLUTION_LABEL); - } - else - { - disabled_widgets &= ~(1uLL << WIDX_RESOLUTION_DROPDOWN); - disabled_widgets &= ~(1uLL << WIDX_RESOLUTION); - disabled_widgets &= ~(1uLL << WIDX_RESOLUTION_LABEL); - } - - // Disable Steam Overlay checkbox when using software or OpenGL rendering. - if (gConfigGeneral.DrawingEngine == DrawingEngine::Software || gConfigGeneral.DrawingEngine == DrawingEngine::OpenGL) - { - disabled_widgets |= (1uLL << WIDX_STEAM_OVERLAY_PAUSE); - } - else - { - disabled_widgets &= ~(1uLL << WIDX_STEAM_OVERLAY_PAUSE); - } - - // Disable changing VSync for Software engine, as we can't control its use of VSync - if (gConfigGeneral.DrawingEngine == DrawingEngine::Software) - { - disabled_widgets |= (1uLL << WIDX_USE_VSYNC_CHECKBOX); - } - else - { - disabled_widgets &= ~(1uLL << WIDX_USE_VSYNC_CHECKBOX); - } - - SetCheckboxValue(WIDX_UNCAP_FPS_CHECKBOX, gConfigGeneral.UncapFPS); - SetCheckboxValue(WIDX_USE_VSYNC_CHECKBOX, gConfigGeneral.UseVSync); - SetCheckboxValue(WIDX_SHOW_FPS_CHECKBOX, gConfigGeneral.ShowFPS); - SetCheckboxValue(WIDX_MULTITHREADING_CHECKBOX, gConfigGeneral.MultiThreading); - SetCheckboxValue(WIDX_MINIMIZE_FOCUS_LOSS, gConfigGeneral.MinimizeFullscreenFocusLoss); - SetCheckboxValue(WIDX_STEAM_OVERLAY_PAUSE, gConfigGeneral.SteamOverlayPause); - SetCheckboxValue(WIDX_DISABLE_SCREENSAVER_LOCK, gConfigGeneral.DisableScreensaver); - - // Dropdown captions for straightforward strings. - widgets[WIDX_FULLSCREEN].text = FullscreenModeNames[gConfigGeneral.FullscreenMode]; - widgets[WIDX_DRAWING_ENGINE].text = DrawingEngineStringIds[EnumValue(gConfigGeneral.DrawingEngine)]; - } - - void DisplayDraw(DrawPixelInfo& dpi) - { - auto ft = Formatter(); - ft.Add(static_cast(gConfigGeneral.WindowScale * 100)); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SCALE].left + 1, widgets[WIDX_SCALE].top + 1 }, - STR_WINDOW_COLOUR_2_COMMA2DP32, ft, { colours[1] }); - } -#pragma endregion - -#pragma region Rendering tab events - void RenderingMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_TILE_SMOOTHING_CHECKBOX: - gConfigGeneral.LandscapeSmoothing ^= 1; - ConfigSaveDefault(); - GfxInvalidateScreen(); - break; - case WIDX_GRIDLINES_CHECKBOX: - { - gConfigGeneral.AlwaysShowGridlines ^= 1; - ConfigSaveDefault(); - GfxInvalidateScreen(); - WindowBase* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) - { - if (gConfigGeneral.AlwaysShowGridlines) - mainWindow->viewport->flags |= VIEWPORT_FLAG_GRIDLINES; - else - mainWindow->viewport->flags &= ~VIEWPORT_FLAG_GRIDLINES; - } - break; + break; + case WIDX_USE_VSYNC_CHECKBOX: + gConfigGeneral.UseVSync ^= 1; + DrawingEngineSetVSync(gConfigGeneral.UseVSync); + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_SHOW_FPS_CHECKBOX: + gConfigGeneral.ShowFPS ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_MULTITHREADING_CHECKBOX: + gConfigGeneral.MultiThreading ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_MINIMIZE_FOCUS_LOSS: + gConfigGeneral.MinimizeFullscreenFocusLoss ^= 1; + RefreshVideo(false); + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_STEAM_OVERLAY_PAUSE: + gConfigGeneral.SteamOverlayPause ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_DISABLE_SCREENSAVER_LOCK: + gConfigGeneral.DisableScreensaver ^= 1; + ApplyScreenSaverLockSetting(); + ConfigSaveDefault(); + Invalidate(); + break; } - case WIDX_DAY_NIGHT_CHECKBOX: - gConfigGeneral.DayNightCycle ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_ENABLE_LIGHT_FX_CHECKBOX: - gConfigGeneral.EnableLightFx ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX: - gConfigGeneral.EnableLightFxForVehicles ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_UPPER_CASE_BANNERS_CHECKBOX: - gConfigGeneral.UpperCaseBanners ^= 1; - ConfigSaveDefault(); - Invalidate(); - ScrollingTextInvalidate(); - break; - case WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX: - gConfigGeneral.DisableLightningEffect ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_RENDER_WEATHER_EFFECTS_CHECKBOX: - gConfigGeneral.RenderWeatherEffects ^= 1; - gConfigGeneral.RenderWeatherGloom = gConfigGeneral.RenderWeatherEffects; - ConfigSaveDefault(); - Invalidate(); - GfxInvalidateScreen(); - break; - case WIDX_SHOW_GUEST_PURCHASES_CHECKBOX: - gConfigGeneral.ShowGuestPurchases ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX: - gConfigGeneral.TransparentScreenshot ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - } - } - - void RenderingMouseDown(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_VIRTUAL_FLOOR_DROPDOWN: - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_VIRTUAL_FLOOR_STYLE_DISABLED; - gDropdownItems[1].Args = STR_VIRTUAL_FLOOR_STYLE_TRANSPARENT; - gDropdownItems[2].Args = STR_VIRTUAL_FLOOR_STYLE_GLASSY; - - Widget* widget = &widgets[widgetIndex - 1]; - ShowDropdown(widget, 3); - - Dropdown::SetChecked(static_cast(gConfigGeneral.VirtualFloorStyle), true); - break; - } - } - - void RenderingDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_VIRTUAL_FLOOR_DROPDOWN: - gConfigGeneral.VirtualFloorStyle = static_cast(dropdownIndex); - ConfigSaveDefault(); - break; - } - } - - void RenderingPrepareDraw() - { - SetCheckboxValue(WIDX_TILE_SMOOTHING_CHECKBOX, gConfigGeneral.LandscapeSmoothing); - SetCheckboxValue(WIDX_GRIDLINES_CHECKBOX, gConfigGeneral.AlwaysShowGridlines); - SetCheckboxValue(WIDX_DAY_NIGHT_CHECKBOX, gConfigGeneral.DayNightCycle); - SetCheckboxValue(WIDX_SHOW_GUEST_PURCHASES_CHECKBOX, gConfigGeneral.ShowGuestPurchases); - SetCheckboxValue(WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX, gConfigGeneral.TransparentScreenshot); - SetCheckboxValue(WIDX_UPPER_CASE_BANNERS_CHECKBOX, gConfigGeneral.UpperCaseBanners); - - static constexpr StringId _virtualFloorStyleStrings[] = { - STR_VIRTUAL_FLOOR_STYLE_DISABLED, - STR_VIRTUAL_FLOOR_STYLE_TRANSPARENT, - STR_VIRTUAL_FLOOR_STYLE_GLASSY, - }; - - widgets[WIDX_VIRTUAL_FLOOR].text = _virtualFloorStyleStrings[EnumValue(gConfigGeneral.VirtualFloorStyle)]; - - SetCheckboxValue(WIDX_ENABLE_LIGHT_FX_CHECKBOX, gConfigGeneral.EnableLightFx); - if (gConfigGeneral.DayNightCycle && gConfigGeneral.DrawingEngine == DrawingEngine::SoftwareWithHardwareDisplay) - { - disabled_widgets &= ~(1uLL << WIDX_ENABLE_LIGHT_FX_CHECKBOX); - } - else - { - disabled_widgets |= (1uLL << WIDX_ENABLE_LIGHT_FX_CHECKBOX); - gConfigGeneral.EnableLightFx = false; } - SetCheckboxValue(WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX, gConfigGeneral.EnableLightFxForVehicles); - if (gConfigGeneral.DayNightCycle && gConfigGeneral.DrawingEngine == DrawingEngine::SoftwareWithHardwareDisplay - && gConfigGeneral.EnableLightFx) + void DisplayMouseDown(WidgetIndex widgetIndex) { - disabled_widgets &= ~(1uLL << WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX); - } - else - { - disabled_widgets |= (1uLL << WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX); - gConfigGeneral.EnableLightFxForVehicles = false; - } + Widget* widget = &widgets[widgetIndex - 1]; - WidgetSetCheckboxValue( - *this, WIDX_RENDER_WEATHER_EFFECTS_CHECKBOX, - gConfigGeneral.RenderWeatherEffects || gConfigGeneral.RenderWeatherGloom); - SetCheckboxValue(WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX, gConfigGeneral.DisableLightningEffect); - if (!gConfigGeneral.RenderWeatherEffects && !gConfigGeneral.RenderWeatherGloom) - { - SetCheckboxValue(WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX, true); - disabled_widgets |= (1uLL << WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX); - } - else - { - disabled_widgets &= ~(1uLL << WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX); - } - } - -#pragma endregion - -#pragma region Culture tab events - void CultureMouseDown(WidgetIndex widgetIndex) - { - Widget* widget = &widgets[widgetIndex - 1]; - - switch (widgetIndex) - { - case WIDX_HEIGHT_LABELS_DROPDOWN: - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_HEIGHT_IN_UNITS; - gDropdownItems[1].Args = STR_REAL_VALUES; - - ShowDropdown(widget, 2); - - Dropdown::SetChecked(gConfigGeneral.ShowHeightAsUnits ? 0 : 1, true); - break; - case WIDX_CURRENCY_DROPDOWN: + switch (widgetIndex) { - // All the currencies plus the separator - constexpr auto numItems = EnumValue(CurrencyType::Count) + 1; - - // All the currencies except custom currency - size_t numOrdinaryCurrencies = EnumValue(CurrencyType::Count) - 1; - - for (size_t i = 0; i < numOrdinaryCurrencies; i++) + case WIDX_RESOLUTION_DROPDOWN: { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = CurrencyDescriptors[i].stringId; + const auto& resolutions = OpenRCT2::GetContext()->GetUiContext()->GetFullscreenResolutions(); + + int32_t selectedResolution = -1; + for (size_t i = 0; i < resolutions.size(); i++) + { + const Resolution& resolution = resolutions[i]; + + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + + uint16_t* args = reinterpret_cast(&gDropdownItems[i].Args); + args[0] = STR_RESOLUTION_X_BY_Y; + args[1] = resolution.Width; + args[2] = resolution.Height; + + if (resolution.Width == gConfigGeneral.FullscreenWidth + && resolution.Height == gConfigGeneral.FullscreenHeight) + { + selectedResolution = static_cast(i); + } + } + + ShowDropdown(widget, static_cast(resolutions.size())); + + if (selectedResolution != -1 && selectedResolution < 32) + { + Dropdown::SetChecked(selectedResolution, true); + } } - gDropdownItems[numOrdinaryCurrencies].Format = Dropdown::SeparatorString; + break; + case WIDX_FULLSCREEN_DROPDOWN: + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_OPTIONS_DISPLAY_WINDOWED; + gDropdownItems[1].Args = STR_OPTIONS_DISPLAY_FULLSCREEN; + gDropdownItems[2].Args = STR_OPTIONS_DISPLAY_FULLSCREEN_BORDERLESS; - gDropdownItems[numOrdinaryCurrencies + 1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numOrdinaryCurrencies + 1].Args = CurrencyDescriptors[EnumValue(CurrencyType::Custom)].stringId; + ShowDropdown(widget, 3); - ShowDropdown(widget, numItems); - - if (gConfigGeneral.CurrencyFormat == CurrencyType::Custom) + Dropdown::SetChecked(gConfigGeneral.FullscreenMode, true); + break; + case WIDX_DRAWING_ENGINE_DROPDOWN: { - Dropdown::SetChecked(EnumValue(gConfigGeneral.CurrencyFormat) + 1, true); - } - else - { - Dropdown::SetChecked(EnumValue(gConfigGeneral.CurrencyFormat), true); - } - break; - } - case WIDX_DISTANCE_DROPDOWN: - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_IMPERIAL; - gDropdownItems[1].Args = STR_METRIC; - gDropdownItems[2].Args = STR_SI; + int32_t numItems = 3; +#ifdef DISABLE_OPENGL + numItems = 2; +#endif - ShowDropdown(widget, 3); - - Dropdown::SetChecked(static_cast(gConfigGeneral.MeasurementFormat), true); - break; - case WIDX_TEMPERATURE_DROPDOWN: - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_CELSIUS; - gDropdownItems[1].Args = STR_FAHRENHEIT; - - ShowDropdown(widget, 2); - - Dropdown::SetChecked(static_cast(gConfigGeneral.TemperatureFormat), true); - break; - case WIDX_LANGUAGE_DROPDOWN: - for (size_t i = 1; i < LANGUAGE_COUNT; i++) - { - gDropdownItems[i - 1].Format = STR_OPTIONS_DROPDOWN_ITEM; - gDropdownItems[i - 1].Args = reinterpret_cast(LanguagesDescriptors[i].native_name); + for (int32_t i = 0; i < numItems; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = DrawingEngineStringIds[i]; + } + ShowDropdown(widget, numItems); + Dropdown::SetChecked(EnumValue(gConfigGeneral.DrawingEngine), true); + break; } - ShowDropdown(widget, LANGUAGE_COUNT - 1); - Dropdown::SetChecked(LocalisationService_GetCurrentLanguage() - 1, true); - break; - case WIDX_DATE_FORMAT_DROPDOWN: - for (size_t i = 0; i < 4; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = DateFormatStringIDs[i]; - } - ShowDropdown(widget, 4); - Dropdown::SetChecked(gConfigGeneral.DateFormat, true); - break; - } - } - - void CultureDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_HEIGHT_LABELS_DROPDOWN: - // reset flag and set it to 1 if height as units is selected - gConfigGeneral.ShowHeightAsUnits = 0; - - if (dropdownIndex == 0) - { - gConfigGeneral.ShowHeightAsUnits = 1; - } - ConfigSaveDefault(); - UpdateHeightMarkers(); - break; - case WIDX_CURRENCY_DROPDOWN: - if (dropdownIndex == EnumValue(CurrencyType::Custom) + 1) - { // Add 1 because the separator occupies a position - gConfigGeneral.CurrencyFormat = static_cast(dropdownIndex - 1); - ContextOpenWindow(WindowClass::CustomCurrencyConfig); - } - else - { - gConfigGeneral.CurrencyFormat = static_cast(dropdownIndex); - } - ConfigSaveDefault(); - GfxInvalidateScreen(); - break; - case WIDX_DISTANCE_DROPDOWN: - gConfigGeneral.MeasurementFormat = static_cast(dropdownIndex); - ConfigSaveDefault(); - UpdateHeightMarkers(); - break; - case WIDX_TEMPERATURE_DROPDOWN: - if (dropdownIndex != static_cast(gConfigGeneral.TemperatureFormat)) - { - gConfigGeneral.TemperatureFormat = static_cast(dropdownIndex); + case WIDX_SCALE_UP: + gConfigGeneral.WindowScale += 0.25f; ConfigSaveDefault(); GfxInvalidateScreen(); - } - break; - case WIDX_LANGUAGE_DROPDOWN: + ContextTriggerResize(); + ContextUpdateCursorScale(); + break; + case WIDX_SCALE_DOWN: + gConfigGeneral.WindowScale -= 0.25f; + gConfigGeneral.WindowScale = std::max(0.5f, gConfigGeneral.WindowScale); + ConfigSaveDefault(); + GfxInvalidateScreen(); + ContextTriggerResize(); + ContextUpdateCursorScale(); + break; + } + } + + void DisplayDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) { - auto fallbackLanguage = LocalisationService_GetCurrentLanguage(); - if (dropdownIndex != LocalisationService_GetCurrentLanguage() - 1) + case WIDX_RESOLUTION_DROPDOWN: { - if (!LanguageOpen(dropdownIndex + 1)) + const auto& resolutions = OpenRCT2::GetContext()->GetUiContext()->GetFullscreenResolutions(); + + const Resolution& resolution = resolutions[dropdownIndex]; + if (resolution.Width != gConfigGeneral.FullscreenWidth + || resolution.Height != gConfigGeneral.FullscreenHeight) { - // Failed to open language file, try to recover by falling - // back to previously used language - if (LanguageOpen(fallbackLanguage)) - { - // It worked, so we can say it with error message in-game - ContextShowError(STR_LANGUAGE_LOAD_FAILED, STR_NONE, {}); - } - // report error to console regardless - LOG_ERROR("Failed to open language file."); - } - else - { - gConfigGeneral.Language = dropdownIndex + 1; + gConfigGeneral.FullscreenWidth = resolution.Width; + gConfigGeneral.FullscreenHeight = resolution.Height; + + if (gConfigGeneral.FullscreenMode == static_cast(OpenRCT2::Ui::FULLSCREEN_MODE::FULLSCREEN)) + ContextSetFullscreenMode(static_cast(OpenRCT2::Ui::FULLSCREEN_MODE::FULLSCREEN)); + ConfigSaveDefault(); GfxInvalidateScreen(); } } + break; + case WIDX_FULLSCREEN_DROPDOWN: + if (dropdownIndex != gConfigGeneral.FullscreenMode) + { + ContextSetFullscreenMode(dropdownIndex); + + gConfigGeneral.FullscreenMode = static_cast(dropdownIndex); + ConfigSaveDefault(); + GfxInvalidateScreen(); + } + break; + case WIDX_DRAWING_ENGINE_DROPDOWN: + if (dropdownIndex != EnumValue(gConfigGeneral.DrawingEngine)) + { + DrawingEngine srcEngine = drawing_engine_get_type(); + DrawingEngine dstEngine = static_cast(dropdownIndex); + + gConfigGeneral.DrawingEngine = dstEngine; + bool recreate_window = DrawingEngineRequiresNewWindow(srcEngine, dstEngine); + RefreshVideo(recreate_window); + ConfigSaveDefault(); + Invalidate(); + } + break; } - break; - case WIDX_DATE_FORMAT_DROPDOWN: - if (dropdownIndex != gConfigGeneral.DateFormat) - { - gConfigGeneral.DateFormat = static_cast(dropdownIndex); + } + + void DisplayPrepareDraw() + { + // Resolution dropdown caption. + auto ft = Formatter::Common(); + ft.Increment(16); + ft.Add(static_cast(gConfigGeneral.FullscreenWidth)); + ft.Add(static_cast(gConfigGeneral.FullscreenHeight)); + + // Disable resolution dropdown on "Windowed" and "Fullscreen (desktop)" + if (gConfigGeneral.FullscreenMode != static_cast(OpenRCT2::Ui::FULLSCREEN_MODE::FULLSCREEN)) + { + disabled_widgets |= (1uLL << WIDX_RESOLUTION_DROPDOWN); + disabled_widgets |= (1uLL << WIDX_RESOLUTION); + disabled_widgets |= (1uLL << WIDX_RESOLUTION_LABEL); + } + else + { + disabled_widgets &= ~(1uLL << WIDX_RESOLUTION_DROPDOWN); + disabled_widgets &= ~(1uLL << WIDX_RESOLUTION); + disabled_widgets &= ~(1uLL << WIDX_RESOLUTION_LABEL); + } + + // Disable Steam Overlay checkbox when using software or OpenGL rendering. + if (gConfigGeneral.DrawingEngine == DrawingEngine::Software + || gConfigGeneral.DrawingEngine == DrawingEngine::OpenGL) + { + disabled_widgets |= (1uLL << WIDX_STEAM_OVERLAY_PAUSE); + } + else + { + disabled_widgets &= ~(1uLL << WIDX_STEAM_OVERLAY_PAUSE); + } + + // Disable changing VSync for Software engine, as we can't control its use of VSync + if (gConfigGeneral.DrawingEngine == DrawingEngine::Software) + { + disabled_widgets |= (1uLL << WIDX_USE_VSYNC_CHECKBOX); + } + else + { + disabled_widgets &= ~(1uLL << WIDX_USE_VSYNC_CHECKBOX); + } + + SetCheckboxValue(WIDX_UNCAP_FPS_CHECKBOX, gConfigGeneral.UncapFPS); + SetCheckboxValue(WIDX_USE_VSYNC_CHECKBOX, gConfigGeneral.UseVSync); + SetCheckboxValue(WIDX_SHOW_FPS_CHECKBOX, gConfigGeneral.ShowFPS); + SetCheckboxValue(WIDX_MULTITHREADING_CHECKBOX, gConfigGeneral.MultiThreading); + SetCheckboxValue(WIDX_MINIMIZE_FOCUS_LOSS, gConfigGeneral.MinimizeFullscreenFocusLoss); + SetCheckboxValue(WIDX_STEAM_OVERLAY_PAUSE, gConfigGeneral.SteamOverlayPause); + SetCheckboxValue(WIDX_DISABLE_SCREENSAVER_LOCK, gConfigGeneral.DisableScreensaver); + + // Dropdown captions for straightforward strings. + widgets[WIDX_FULLSCREEN].text = FullscreenModeNames[gConfigGeneral.FullscreenMode]; + widgets[WIDX_DRAWING_ENGINE].text = DrawingEngineStringIds[EnumValue(gConfigGeneral.DrawingEngine)]; + } + + void DisplayDraw(DrawPixelInfo& dpi) + { + auto ft = Formatter(); + ft.Add(static_cast(gConfigGeneral.WindowScale * 100)); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SCALE].left + 1, widgets[WIDX_SCALE].top + 1 }, + STR_WINDOW_COLOUR_2_COMMA2DP32, ft, { colours[1] }); + } +#pragma endregion + +#pragma region Rendering tab events + void RenderingMouseUp(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_TILE_SMOOTHING_CHECKBOX: + gConfigGeneral.LandscapeSmoothing ^= 1; ConfigSaveDefault(); GfxInvalidateScreen(); + break; + case WIDX_GRIDLINES_CHECKBOX: + { + gConfigGeneral.AlwaysShowGridlines ^= 1; + ConfigSaveDefault(); + GfxInvalidateScreen(); + WindowBase* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + { + if (gConfigGeneral.AlwaysShowGridlines) + mainWindow->viewport->flags |= VIEWPORT_FLAG_GRIDLINES; + else + mainWindow->viewport->flags &= ~VIEWPORT_FLAG_GRIDLINES; + } + break; } - break; - } - } - - void CulturePrepareDraw() - { - // Language - auto ft = Formatter::Common(); - ft.Add(LanguagesDescriptors[LocalisationService_GetCurrentLanguage()].native_name); - - // Currency: pounds, dollars, etc. (10 total) - widgets[WIDX_CURRENCY].text = CurrencyDescriptors[EnumValue(gConfigGeneral.CurrencyFormat)].stringId; - - // Distance: metric / imperial / si - { - StringId stringId = STR_NONE; - switch (gConfigGeneral.MeasurementFormat) - { - case MeasurementFormat::Imperial: - stringId = STR_IMPERIAL; + case WIDX_DAY_NIGHT_CHECKBOX: + gConfigGeneral.DayNightCycle ^= 1; + ConfigSaveDefault(); + Invalidate(); break; - case MeasurementFormat::Metric: - stringId = STR_METRIC; + case WIDX_ENABLE_LIGHT_FX_CHECKBOX: + gConfigGeneral.EnableLightFx ^= 1; + ConfigSaveDefault(); + Invalidate(); break; - case MeasurementFormat::SI: - stringId = STR_SI; + case WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX: + gConfigGeneral.EnableLightFxForVehicles ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_UPPER_CASE_BANNERS_CHECKBOX: + gConfigGeneral.UpperCaseBanners ^= 1; + ConfigSaveDefault(); + Invalidate(); + ScrollingTextInvalidate(); + break; + case WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX: + gConfigGeneral.DisableLightningEffect ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_RENDER_WEATHER_EFFECTS_CHECKBOX: + gConfigGeneral.RenderWeatherEffects ^= 1; + gConfigGeneral.RenderWeatherGloom = gConfigGeneral.RenderWeatherEffects; + ConfigSaveDefault(); + Invalidate(); + GfxInvalidateScreen(); + break; + case WIDX_SHOW_GUEST_PURCHASES_CHECKBOX: + gConfigGeneral.ShowGuestPurchases ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX: + gConfigGeneral.TransparentScreenshot ^= 1; + ConfigSaveDefault(); + Invalidate(); break; } - widgets[WIDX_DISTANCE].text = stringId; } - // Date format - widgets[WIDX_DATE_FORMAT].text = DateFormatStringIDs[gConfigGeneral.DateFormat]; + void RenderingMouseDown(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_VIRTUAL_FLOOR_DROPDOWN: + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_VIRTUAL_FLOOR_STYLE_DISABLED; + gDropdownItems[1].Args = STR_VIRTUAL_FLOOR_STYLE_TRANSPARENT; + gDropdownItems[2].Args = STR_VIRTUAL_FLOOR_STYLE_GLASSY; - // Temperature: celsius/fahrenheit - widgets[WIDX_TEMPERATURE].text = gConfigGeneral.TemperatureFormat == TemperatureUnit::Fahrenheit ? STR_FAHRENHEIT - : STR_CELSIUS; + Widget* widget = &widgets[widgetIndex - 1]; + ShowDropdown(widget, 3); - // Height: units/real values - widgets[WIDX_HEIGHT_LABELS].text = gConfigGeneral.ShowHeightAsUnits ? STR_HEIGHT_IN_UNITS : STR_REAL_VALUES; - } + Dropdown::SetChecked(static_cast(gConfigGeneral.VirtualFloorStyle), true); + break; + } + } + + void RenderingDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_VIRTUAL_FLOOR_DROPDOWN: + gConfigGeneral.VirtualFloorStyle = static_cast(dropdownIndex); + ConfigSaveDefault(); + break; + } + } + + void RenderingPrepareDraw() + { + SetCheckboxValue(WIDX_TILE_SMOOTHING_CHECKBOX, gConfigGeneral.LandscapeSmoothing); + SetCheckboxValue(WIDX_GRIDLINES_CHECKBOX, gConfigGeneral.AlwaysShowGridlines); + SetCheckboxValue(WIDX_DAY_NIGHT_CHECKBOX, gConfigGeneral.DayNightCycle); + SetCheckboxValue(WIDX_SHOW_GUEST_PURCHASES_CHECKBOX, gConfigGeneral.ShowGuestPurchases); + SetCheckboxValue(WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX, gConfigGeneral.TransparentScreenshot); + SetCheckboxValue(WIDX_UPPER_CASE_BANNERS_CHECKBOX, gConfigGeneral.UpperCaseBanners); + + static constexpr StringId _virtualFloorStyleStrings[] = { + STR_VIRTUAL_FLOOR_STYLE_DISABLED, + STR_VIRTUAL_FLOOR_STYLE_TRANSPARENT, + STR_VIRTUAL_FLOOR_STYLE_GLASSY, + }; + + widgets[WIDX_VIRTUAL_FLOOR].text = _virtualFloorStyleStrings[EnumValue(gConfigGeneral.VirtualFloorStyle)]; + + SetCheckboxValue(WIDX_ENABLE_LIGHT_FX_CHECKBOX, gConfigGeneral.EnableLightFx); + if (gConfigGeneral.DayNightCycle && gConfigGeneral.DrawingEngine == DrawingEngine::SoftwareWithHardwareDisplay) + { + disabled_widgets &= ~(1uLL << WIDX_ENABLE_LIGHT_FX_CHECKBOX); + } + else + { + disabled_widgets |= (1uLL << WIDX_ENABLE_LIGHT_FX_CHECKBOX); + gConfigGeneral.EnableLightFx = false; + } + + SetCheckboxValue(WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX, gConfigGeneral.EnableLightFxForVehicles); + if (gConfigGeneral.DayNightCycle && gConfigGeneral.DrawingEngine == DrawingEngine::SoftwareWithHardwareDisplay + && gConfigGeneral.EnableLightFx) + { + disabled_widgets &= ~(1uLL << WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX); + } + else + { + disabled_widgets |= (1uLL << WIDX_ENABLE_LIGHT_FX_FOR_VEHICLES_CHECKBOX); + gConfigGeneral.EnableLightFxForVehicles = false; + } + + WidgetSetCheckboxValue( + *this, WIDX_RENDER_WEATHER_EFFECTS_CHECKBOX, + gConfigGeneral.RenderWeatherEffects || gConfigGeneral.RenderWeatherGloom); + SetCheckboxValue(WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX, gConfigGeneral.DisableLightningEffect); + if (!gConfigGeneral.RenderWeatherEffects && !gConfigGeneral.RenderWeatherGloom) + { + SetCheckboxValue(WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX, true); + disabled_widgets |= (1uLL << WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX); + } + else + { + disabled_widgets &= ~(1uLL << WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX); + } + } + +#pragma endregion + +#pragma region Culture tab events + void CultureMouseDown(WidgetIndex widgetIndex) + { + Widget* widget = &widgets[widgetIndex - 1]; + + switch (widgetIndex) + { + case WIDX_HEIGHT_LABELS_DROPDOWN: + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_HEIGHT_IN_UNITS; + gDropdownItems[1].Args = STR_REAL_VALUES; + + ShowDropdown(widget, 2); + + Dropdown::SetChecked(gConfigGeneral.ShowHeightAsUnits ? 0 : 1, true); + break; + case WIDX_CURRENCY_DROPDOWN: + { + // All the currencies plus the separator + constexpr auto numItems = EnumValue(CurrencyType::Count) + 1; + + // All the currencies except custom currency + size_t numOrdinaryCurrencies = EnumValue(CurrencyType::Count) - 1; + + for (size_t i = 0; i < numOrdinaryCurrencies; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = CurrencyDescriptors[i].stringId; + } + + gDropdownItems[numOrdinaryCurrencies].Format = Dropdown::SeparatorString; + + gDropdownItems[numOrdinaryCurrencies + 1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numOrdinaryCurrencies + 1].Args = CurrencyDescriptors[EnumValue(CurrencyType::Custom)] + .stringId; + + ShowDropdown(widget, numItems); + + if (gConfigGeneral.CurrencyFormat == CurrencyType::Custom) + { + Dropdown::SetChecked(EnumValue(gConfigGeneral.CurrencyFormat) + 1, true); + } + else + { + Dropdown::SetChecked(EnumValue(gConfigGeneral.CurrencyFormat), true); + } + break; + } + case WIDX_DISTANCE_DROPDOWN: + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_IMPERIAL; + gDropdownItems[1].Args = STR_METRIC; + gDropdownItems[2].Args = STR_SI; + + ShowDropdown(widget, 3); + + Dropdown::SetChecked(static_cast(gConfigGeneral.MeasurementFormat), true); + break; + case WIDX_TEMPERATURE_DROPDOWN: + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_CELSIUS; + gDropdownItems[1].Args = STR_FAHRENHEIT; + + ShowDropdown(widget, 2); + + Dropdown::SetChecked(static_cast(gConfigGeneral.TemperatureFormat), true); + break; + case WIDX_LANGUAGE_DROPDOWN: + for (size_t i = 1; i < LANGUAGE_COUNT; i++) + { + gDropdownItems[i - 1].Format = STR_OPTIONS_DROPDOWN_ITEM; + gDropdownItems[i - 1].Args = reinterpret_cast(LanguagesDescriptors[i].native_name); + } + ShowDropdown(widget, LANGUAGE_COUNT - 1); + Dropdown::SetChecked(LocalisationService_GetCurrentLanguage() - 1, true); + break; + case WIDX_DATE_FORMAT_DROPDOWN: + for (size_t i = 0; i < 4; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = DateFormatStringIDs[i]; + } + ShowDropdown(widget, 4); + Dropdown::SetChecked(gConfigGeneral.DateFormat, true); + break; + } + } + + void CultureDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_HEIGHT_LABELS_DROPDOWN: + // reset flag and set it to 1 if height as units is selected + gConfigGeneral.ShowHeightAsUnits = 0; + + if (dropdownIndex == 0) + { + gConfigGeneral.ShowHeightAsUnits = 1; + } + ConfigSaveDefault(); + UpdateHeightMarkers(); + break; + case WIDX_CURRENCY_DROPDOWN: + if (dropdownIndex == EnumValue(CurrencyType::Custom) + 1) + { // Add 1 because the separator occupies a position + gConfigGeneral.CurrencyFormat = static_cast(dropdownIndex - 1); + ContextOpenWindow(WindowClass::CustomCurrencyConfig); + } + else + { + gConfigGeneral.CurrencyFormat = static_cast(dropdownIndex); + } + ConfigSaveDefault(); + GfxInvalidateScreen(); + break; + case WIDX_DISTANCE_DROPDOWN: + gConfigGeneral.MeasurementFormat = static_cast(dropdownIndex); + ConfigSaveDefault(); + UpdateHeightMarkers(); + break; + case WIDX_TEMPERATURE_DROPDOWN: + if (dropdownIndex != static_cast(gConfigGeneral.TemperatureFormat)) + { + gConfigGeneral.TemperatureFormat = static_cast(dropdownIndex); + ConfigSaveDefault(); + GfxInvalidateScreen(); + } + break; + case WIDX_LANGUAGE_DROPDOWN: + { + auto fallbackLanguage = LocalisationService_GetCurrentLanguage(); + if (dropdownIndex != LocalisationService_GetCurrentLanguage() - 1) + { + if (!LanguageOpen(dropdownIndex + 1)) + { + // Failed to open language file, try to recover by falling + // back to previously used language + if (LanguageOpen(fallbackLanguage)) + { + // It worked, so we can say it with error message in-game + ContextShowError(STR_LANGUAGE_LOAD_FAILED, STR_NONE, {}); + } + // report error to console regardless + LOG_ERROR("Failed to open language file."); + } + else + { + gConfigGeneral.Language = dropdownIndex + 1; + ConfigSaveDefault(); + GfxInvalidateScreen(); + } + } + } + break; + case WIDX_DATE_FORMAT_DROPDOWN: + if (dropdownIndex != gConfigGeneral.DateFormat) + { + gConfigGeneral.DateFormat = static_cast(dropdownIndex); + ConfigSaveDefault(); + GfxInvalidateScreen(); + } + break; + } + } + + void CulturePrepareDraw() + { + // Language + auto ft = Formatter::Common(); + ft.Add(LanguagesDescriptors[LocalisationService_GetCurrentLanguage()].native_name); + + // Currency: pounds, dollars, etc. (10 total) + widgets[WIDX_CURRENCY].text = CurrencyDescriptors[EnumValue(gConfigGeneral.CurrencyFormat)].stringId; + + // Distance: metric / imperial / si + { + StringId stringId = STR_NONE; + switch (gConfigGeneral.MeasurementFormat) + { + case MeasurementFormat::Imperial: + stringId = STR_IMPERIAL; + break; + case MeasurementFormat::Metric: + stringId = STR_METRIC; + break; + case MeasurementFormat::SI: + stringId = STR_SI; + break; + } + widgets[WIDX_DISTANCE].text = stringId; + } + + // Date format + widgets[WIDX_DATE_FORMAT].text = DateFormatStringIDs[gConfigGeneral.DateFormat]; + + // Temperature: celsius/fahrenheit + widgets[WIDX_TEMPERATURE].text = gConfigGeneral.TemperatureFormat == TemperatureUnit::Fahrenheit ? STR_FAHRENHEIT + : STR_CELSIUS; + + // Height: units/real values + widgets[WIDX_HEIGHT_LABELS].text = gConfigGeneral.ShowHeightAsUnits ? STR_HEIGHT_IN_UNITS : STR_REAL_VALUES; + } #pragma endregion #pragma region Audio tab events - void AudioMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void AudioMouseUp(WidgetIndex widgetIndex) { - case WIDX_SOUND_CHECKBOX: - gConfigSound.SoundEnabled = !gConfigSound.SoundEnabled; - ConfigSaveDefault(); - Invalidate(); - break; - - case WIDX_MASTER_SOUND_CHECKBOX: - gConfigSound.MasterSoundEnabled = !gConfigSound.MasterSoundEnabled; - if (!gConfigSound.MasterSoundEnabled) - OpenRCT2::Audio::Pause(); - else - OpenRCT2::Audio::Resume(); - WindowInvalidateByClass(WindowClass::TopToolbar); - ConfigSaveDefault(); - Invalidate(); - break; - - case WIDX_MUSIC_CHECKBOX: - gConfigSound.RideMusicEnabled = !gConfigSound.RideMusicEnabled; - if (!gConfigSound.RideMusicEnabled) - { - OpenRCT2::RideAudio::StopAllChannels(); - } - ConfigSaveDefault(); - Invalidate(); - break; - - case WIDX_AUDIO_FOCUS_CHECKBOX: - gConfigSound.audio_focus = !gConfigSound.audio_focus; - ConfigSaveDefault(); - Invalidate(); - break; - } - } - - void AudioMouseDown(WidgetIndex widgetIndex) - { - Widget* widget = &widgets[widgetIndex - 1]; - - switch (widgetIndex) - { - case WIDX_SOUND_DROPDOWN: - OpenRCT2::Audio::PopulateDevices(); - - // populate the list with the sound devices - for (int32_t i = 0; i < OpenRCT2::Audio::GetDeviceCount(); i++) - { - gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; - gDropdownItems[i].Args = reinterpret_cast(OpenRCT2::Audio::GetDeviceName(i).c_str()); - } - - ShowDropdown(widget, OpenRCT2::Audio::GetDeviceCount()); - - Dropdown::SetChecked(OpenRCT2::Audio::GetCurrentDeviceIndex(), true); - break; - case WIDX_TITLE_MUSIC_DROPDOWN: + switch (widgetIndex) { - const bool rct1MusicThemeIsAvailable = IsRCT1TitleMusicAvailable(); - int32_t numItems{}; - int32_t checkedIndex{}; - for (auto theme : TitleThemeOptions) - { - if (theme.Kind == TitleMusicKind::RCT1 && !rct1MusicThemeIsAvailable) - continue; + case WIDX_SOUND_CHECKBOX: + gConfigSound.SoundEnabled = !gConfigSound.SoundEnabled; + ConfigSaveDefault(); + Invalidate(); + break; - if (gConfigSound.TitleMusic == theme.Kind) - checkedIndex = numItems; - - gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numItems++].Args = theme.Name; - } - ShowDropdown(widget, numItems); - Dropdown::SetChecked(checkedIndex, true); - break; - } - } - } - - void AudioDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_SOUND_DROPDOWN: - OpenRCT2::Audio::InitRideSounds(dropdownIndex); - if (dropdownIndex < OpenRCT2::Audio::GetDeviceCount()) - { - auto audioContext = GetContext()->GetAudioContext(); - if (dropdownIndex == 0) - { - audioContext->SetOutputDevice(""); - gConfigSound.Device = ""; - } + case WIDX_MASTER_SOUND_CHECKBOX: + gConfigSound.MasterSoundEnabled = !gConfigSound.MasterSoundEnabled; + if (!gConfigSound.MasterSoundEnabled) + OpenRCT2::Audio::Pause(); else + OpenRCT2::Audio::Resume(); + WindowInvalidateByClass(WindowClass::TopToolbar); + ConfigSaveDefault(); + Invalidate(); + break; + + case WIDX_MUSIC_CHECKBOX: + gConfigSound.RideMusicEnabled = !gConfigSound.RideMusicEnabled; + if (!gConfigSound.RideMusicEnabled) { - const auto& deviceName = GetDeviceName(dropdownIndex); - audioContext->SetOutputDevice(deviceName); - gConfigSound.Device = deviceName; + OpenRCT2::RideAudio::StopAllChannels(); } ConfigSaveDefault(); - OpenRCT2::Audio::PlayTitleMusic(); - } - Invalidate(); - break; - case WIDX_TITLE_MUSIC_DROPDOWN: - { - // HACK: When RCT1 is not available, it's not in the dropdown, so indices higher than it should be incremented - const bool rct1MusicThemeIsAvailable = IsRCT1TitleMusicAvailable(); - for (size_t i = 0; i < std::size(TitleThemeOptions) && static_cast(i) <= dropdownIndex; i++) - { - if (TitleThemeOptions[i].Kind == TitleMusicKind::RCT1 && !rct1MusicThemeIsAvailable) - dropdownIndex++; - } + Invalidate(); + break; - gConfigSound.TitleMusic = TitleThemeOptions[dropdownIndex].Kind; + case WIDX_AUDIO_FOCUS_CHECKBOX: + gConfigSound.audio_focus = !gConfigSound.audio_focus; + ConfigSaveDefault(); + Invalidate(); + break; + } + } + + void AudioMouseDown(WidgetIndex widgetIndex) + { + Widget* widget = &widgets[widgetIndex - 1]; + + switch (widgetIndex) + { + case WIDX_SOUND_DROPDOWN: + OpenRCT2::Audio::PopulateDevices(); + + // populate the list with the sound devices + for (int32_t i = 0; i < OpenRCT2::Audio::GetDeviceCount(); i++) + { + gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; + gDropdownItems[i].Args = reinterpret_cast(OpenRCT2::Audio::GetDeviceName(i).c_str()); + } + + ShowDropdown(widget, OpenRCT2::Audio::GetDeviceCount()); + + Dropdown::SetChecked(OpenRCT2::Audio::GetCurrentDeviceIndex(), true); + break; + case WIDX_TITLE_MUSIC_DROPDOWN: + { + const bool rct1MusicThemeIsAvailable = IsRCT1TitleMusicAvailable(); + int32_t numItems{}; + int32_t checkedIndex{}; + for (auto theme : TitleThemeOptions) + { + if (theme.Kind == TitleMusicKind::RCT1 && !rct1MusicThemeIsAvailable) + continue; + + if (gConfigSound.TitleMusic == theme.Kind) + checkedIndex = numItems; + + gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numItems++].Args = theme.Name; + } + ShowDropdown(widget, numItems); + Dropdown::SetChecked(checkedIndex, true); + break; + } + } + } + + void AudioDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_SOUND_DROPDOWN: + OpenRCT2::Audio::InitRideSounds(dropdownIndex); + if (dropdownIndex < OpenRCT2::Audio::GetDeviceCount()) + { + auto audioContext = GetContext()->GetAudioContext(); + if (dropdownIndex == 0) + { + audioContext->SetOutputDevice(""); + gConfigSound.Device = ""; + } + else + { + const auto& deviceName = GetDeviceName(dropdownIndex); + audioContext->SetOutputDevice(deviceName); + gConfigSound.Device = deviceName; + } + ConfigSaveDefault(); + OpenRCT2::Audio::PlayTitleMusic(); + } + Invalidate(); + break; + case WIDX_TITLE_MUSIC_DROPDOWN: + { + // HACK: When RCT1 is not available, it's not in the dropdown, so indices higher than it should be + // incremented + const bool rct1MusicThemeIsAvailable = IsRCT1TitleMusicAvailable(); + for (size_t i = 0; i < std::size(TitleThemeOptions) && static_cast(i) <= dropdownIndex; i++) + { + if (TitleThemeOptions[i].Kind == TitleMusicKind::RCT1 && !rct1MusicThemeIsAvailable) + dropdownIndex++; + } + + gConfigSound.TitleMusic = TitleThemeOptions[dropdownIndex].Kind; + ConfigSaveDefault(); + Invalidate(); + + OpenRCT2::Audio::StopTitleMusic(); + if (gConfigSound.TitleMusic != TitleMusicKind::None) + { + OpenRCT2::Audio::PlayTitleMusic(); + } + break; + } + } + } + + void AudioUpdate() + { + const auto& masterVolumeWidget = widgets[WIDX_MASTER_VOLUME]; + const auto& masterVolumeScroll = scrolls[0]; + uint8_t masterVolume = GetScrollPercentage(masterVolumeWidget, masterVolumeScroll); + if (masterVolume != gConfigSound.MasterVolume) + { + gConfigSound.MasterVolume = masterVolume; ConfigSaveDefault(); - Invalidate(); + InvalidateWidget(WIDX_MASTER_VOLUME); + } - OpenRCT2::Audio::StopTitleMusic(); - if (gConfigSound.TitleMusic != TitleMusicKind::None) - { - OpenRCT2::Audio::PlayTitleMusic(); - } - break; + const auto& soundVolumeWidget = widgets[WIDX_MASTER_VOLUME]; + const auto& soundVolumeScroll = scrolls[1]; + uint8_t soundVolume = GetScrollPercentage(soundVolumeWidget, soundVolumeScroll); + if (soundVolume != gConfigSound.SoundVolume) + { + gConfigSound.SoundVolume = soundVolume; + ConfigSaveDefault(); + InvalidateWidget(WIDX_SOUND_VOLUME); + } + + const auto& musicVolumeWidget = widgets[WIDX_MASTER_VOLUME]; + const auto& musicVolumeScroll = scrolls[2]; + uint8_t rideMusicVolume = GetScrollPercentage(musicVolumeWidget, musicVolumeScroll); + if (rideMusicVolume != gConfigSound.AudioFocus) + { + gConfigSound.AudioFocus = rideMusicVolume; + ConfigSaveDefault(); + InvalidateWidget(WIDX_MUSIC_VOLUME); } } - } - void AudioUpdate() - { - const auto& masterVolumeWidget = widgets[WIDX_MASTER_VOLUME]; - const auto& masterVolumeScroll = scrolls[0]; - uint8_t masterVolume = GetScrollPercentage(masterVolumeWidget, masterVolumeScroll); - if (masterVolume != gConfigSound.MasterVolume) + ScreenSize AudioScrollGetSize(int32_t scrollIndex) { - gConfigSound.MasterVolume = masterVolume; - ConfigSaveDefault(); - InvalidateWidget(WIDX_MASTER_VOLUME); + return { 500, 0 }; } - const auto& soundVolumeWidget = widgets[WIDX_MASTER_VOLUME]; - const auto& soundVolumeScroll = scrolls[1]; - uint8_t soundVolume = GetScrollPercentage(soundVolumeWidget, soundVolumeScroll); - if (soundVolume != gConfigSound.SoundVolume) + StringId GetTitleMusicName() const { - gConfigSound.SoundVolume = soundVolume; - ConfigSaveDefault(); - InvalidateWidget(WIDX_SOUND_VOLUME); + auto theme = std::find_if(std::begin(TitleThemeOptions), std::end(TitleThemeOptions), [](auto&& option) { + return gConfigSound.TitleMusic == option.Kind; + }); + if (theme != std::end(TitleThemeOptions)) + return theme->Name; + return STR_OPENRCT2_DROPDOWN; } - const auto& musicVolumeWidget = widgets[WIDX_MASTER_VOLUME]; - const auto& musicVolumeScroll = scrolls[2]; - uint8_t rideMusicVolume = GetScrollPercentage(musicVolumeWidget, musicVolumeScroll); - if (rideMusicVolume != gConfigSound.AudioFocus) + void AudioPrepareDraw() { - gConfigSound.AudioFocus = rideMusicVolume; - ConfigSaveDefault(); - InvalidateWidget(WIDX_MUSIC_VOLUME); - } - } - - ScreenSize AudioScrollGetSize(int32_t scrollIndex) - { - return { 500, 0 }; - } - - StringId GetTitleMusicName() const - { - auto theme = std::find_if(std::begin(TitleThemeOptions), std::end(TitleThemeOptions), [](auto&& option) { - return gConfigSound.TitleMusic == option.Kind; - }); - if (theme != std::end(TitleThemeOptions)) - return theme->Name; - return STR_OPENRCT2_DROPDOWN; - } - - void AudioPrepareDraw() - { - // Sound device - StringId audioDeviceStringId = STR_OPTIONS_SOUND_VALUE_DEFAULT; - const char* audioDeviceName = nullptr; - const int32_t currentDeviceIndex = OpenRCT2::Audio::GetCurrentDeviceIndex(); - if (currentDeviceIndex == -1 || OpenRCT2::Audio::GetDeviceCount() == 0) - { - audioDeviceStringId = STR_SOUND_NONE; - } - else - { - audioDeviceStringId = STR_STRING; + // Sound device + StringId audioDeviceStringId = STR_OPTIONS_SOUND_VALUE_DEFAULT; + const char* audioDeviceName = nullptr; + const int32_t currentDeviceIndex = OpenRCT2::Audio::GetCurrentDeviceIndex(); + if (currentDeviceIndex == -1 || OpenRCT2::Audio::GetDeviceCount() == 0) + { + audioDeviceStringId = STR_SOUND_NONE; + } + else + { + audioDeviceStringId = STR_STRING; #ifndef __linux__ - if (currentDeviceIndex == 0) - { - audioDeviceStringId = STR_OPTIONS_SOUND_VALUE_DEFAULT; - } + if (currentDeviceIndex == 0) + { + audioDeviceStringId = STR_OPTIONS_SOUND_VALUE_DEFAULT; + } #endif // __linux__ - if (audioDeviceStringId == STR_STRING) + if (audioDeviceStringId == STR_STRING) + { + audioDeviceName = OpenRCT2::Audio::GetDeviceName(currentDeviceIndex).c_str(); + } + } + + widgets[WIDX_SOUND].text = audioDeviceStringId; + auto ft = Formatter::Common(); + ft.Add(audioDeviceName); + + widgets[WIDX_TITLE_MUSIC].text = GetTitleMusicName(); + + SetCheckboxValue(WIDX_SOUND_CHECKBOX, gConfigSound.SoundEnabled); + SetCheckboxValue(WIDX_MASTER_SOUND_CHECKBOX, gConfigSound.MasterSoundEnabled); + SetCheckboxValue(WIDX_MUSIC_CHECKBOX, gConfigSound.RideMusicEnabled); + SetCheckboxValue(WIDX_AUDIO_FOCUS_CHECKBOX, gConfigSound.audio_focus); + WidgetSetEnabled(*this, WIDX_SOUND_CHECKBOX, gConfigSound.MasterSoundEnabled); + WidgetSetEnabled(*this, WIDX_MUSIC_CHECKBOX, gConfigSound.MasterSoundEnabled); + + // Initialize only on first frame, otherwise the scrollbars won't be able to be modified + if (frame_no == 0) { - audioDeviceName = OpenRCT2::Audio::GetDeviceName(currentDeviceIndex).c_str(); + InitializeScrollPosition(WIDX_MASTER_VOLUME, 0, gConfigSound.MasterVolume); + InitializeScrollPosition(WIDX_SOUND_VOLUME, 1, gConfigSound.SoundVolume); + InitializeScrollPosition(WIDX_MUSIC_VOLUME, 2, gConfigSound.AudioFocus); } } - widgets[WIDX_SOUND].text = audioDeviceStringId; - auto ft = Formatter::Common(); - ft.Add(audioDeviceName); - - widgets[WIDX_TITLE_MUSIC].text = GetTitleMusicName(); - - SetCheckboxValue(WIDX_SOUND_CHECKBOX, gConfigSound.SoundEnabled); - SetCheckboxValue(WIDX_MASTER_SOUND_CHECKBOX, gConfigSound.MasterSoundEnabled); - SetCheckboxValue(WIDX_MUSIC_CHECKBOX, gConfigSound.RideMusicEnabled); - SetCheckboxValue(WIDX_AUDIO_FOCUS_CHECKBOX, gConfigSound.audio_focus); - WidgetSetEnabled(*this, WIDX_SOUND_CHECKBOX, gConfigSound.MasterSoundEnabled); - WidgetSetEnabled(*this, WIDX_MUSIC_CHECKBOX, gConfigSound.MasterSoundEnabled); - - // Initialize only on first frame, otherwise the scrollbars won't be able to be modified - if (frame_no == 0) - { - InitializeScrollPosition(WIDX_MASTER_VOLUME, 0, gConfigSound.MasterVolume); - InitializeScrollPosition(WIDX_SOUND_VOLUME, 1, gConfigSound.SoundVolume); - InitializeScrollPosition(WIDX_MUSIC_VOLUME, 2, gConfigSound.AudioFocus); - } - } - #pragma endregion #pragma region Controls tab events - void ControlsMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void ControlsMouseUp(WidgetIndex widgetIndex) { - case WIDX_HOTKEY_DROPDOWN: - ContextOpenWindow(WindowClass::KeyboardShortcutList); - break; - case WIDX_SCREEN_EDGE_SCROLLING: - gConfigGeneral.EdgeScrolling ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_TRAP_CURSOR: - gConfigGeneral.TrapCursor ^= 1; - ConfigSaveDefault(); - ContextSetCursorTrap(gConfigGeneral.TrapCursor); - Invalidate(); - break; - case WIDX_ZOOM_TO_CURSOR: - gConfigGeneral.ZoomToCursor ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_TOOLBAR_SHOW_FINANCES: - gConfigInterface.ToolbarShowFinances ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateByClass(WindowClass::TopToolbar); - break; - case WIDX_TOOLBAR_SHOW_RESEARCH: - gConfigInterface.ToolbarShowResearch ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateByClass(WindowClass::TopToolbar); - break; - case WIDX_TOOLBAR_SHOW_CHEATS: - gConfigInterface.ToolbarShowCheats ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateByClass(WindowClass::TopToolbar); - break; - case WIDX_TOOLBAR_SHOW_NEWS: - gConfigInterface.ToolbarShowNews ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateByClass(WindowClass::TopToolbar); - break; - case WIDX_TOOLBAR_SHOW_MUTE: - gConfigInterface.ToolbarShowMute ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateByClass(WindowClass::TopToolbar); - break; - case WIDX_TOOLBAR_SHOW_CHAT: - gConfigInterface.ToolbarShowChat ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateByClass(WindowClass::TopToolbar); - break; - case WIDX_TOOLBAR_SHOW_ZOOM: - gConfigInterface.ToolbarShowZoom ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateByClass(WindowClass::TopToolbar); - break; - case WIDX_WINDOW_BUTTONS_ON_THE_LEFT: - gConfigInterface.WindowButtonsOnTheLeft ^= 1; - ConfigSaveDefault(); - Invalidate(); - WindowInvalidateAll(); - break; - case WIDX_INVERT_DRAG: - gConfigGeneral.InvertViewportDrag ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_THEMES_BUTTON: - ContextOpenWindow(WindowClass::Themes); - Invalidate(); - break; + switch (widgetIndex) + { + case WIDX_HOTKEY_DROPDOWN: + ContextOpenWindow(WindowClass::KeyboardShortcutList); + break; + case WIDX_SCREEN_EDGE_SCROLLING: + gConfigGeneral.EdgeScrolling ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_TRAP_CURSOR: + gConfigGeneral.TrapCursor ^= 1; + ConfigSaveDefault(); + ContextSetCursorTrap(gConfigGeneral.TrapCursor); + Invalidate(); + break; + case WIDX_ZOOM_TO_CURSOR: + gConfigGeneral.ZoomToCursor ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_TOOLBAR_SHOW_FINANCES: + gConfigInterface.ToolbarShowFinances ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateByClass(WindowClass::TopToolbar); + break; + case WIDX_TOOLBAR_SHOW_RESEARCH: + gConfigInterface.ToolbarShowResearch ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateByClass(WindowClass::TopToolbar); + break; + case WIDX_TOOLBAR_SHOW_CHEATS: + gConfigInterface.ToolbarShowCheats ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateByClass(WindowClass::TopToolbar); + break; + case WIDX_TOOLBAR_SHOW_NEWS: + gConfigInterface.ToolbarShowNews ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateByClass(WindowClass::TopToolbar); + break; + case WIDX_TOOLBAR_SHOW_MUTE: + gConfigInterface.ToolbarShowMute ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateByClass(WindowClass::TopToolbar); + break; + case WIDX_TOOLBAR_SHOW_CHAT: + gConfigInterface.ToolbarShowChat ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateByClass(WindowClass::TopToolbar); + break; + case WIDX_TOOLBAR_SHOW_ZOOM: + gConfigInterface.ToolbarShowZoom ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateByClass(WindowClass::TopToolbar); + break; + case WIDX_WINDOW_BUTTONS_ON_THE_LEFT: + gConfigInterface.WindowButtonsOnTheLeft ^= 1; + ConfigSaveDefault(); + Invalidate(); + WindowInvalidateAll(); + break; + case WIDX_INVERT_DRAG: + gConfigGeneral.InvertViewportDrag ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_THEMES_BUTTON: + ContextOpenWindow(WindowClass::Themes); + Invalidate(); + break; + } } - } - void ControlsMouseDown(WidgetIndex widgetIndex) - { - Widget* widget = &widgets[widgetIndex - 1]; - - switch (widgetIndex) + void ControlsMouseDown(WidgetIndex widgetIndex) { - case WIDX_THEMES_DROPDOWN: - uint32_t numItems = static_cast(ThemeManagerGetNumAvailableThemes()); + Widget* widget = &widgets[widgetIndex - 1]; - for (size_t i = 0; i < numItems; i++) - { - gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; - gDropdownItems[i].Args = reinterpret_cast(ThemeManagerGetAvailableThemeName(i)); - } + switch (widgetIndex) + { + case WIDX_THEMES_DROPDOWN: + uint32_t numItems = static_cast(ThemeManagerGetNumAvailableThemes()); - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, numItems, widget->width() - 3); + for (size_t i = 0; i < numItems; i++) + { + gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; + gDropdownItems[i].Args = reinterpret_cast(ThemeManagerGetAvailableThemeName(i)); + } - Dropdown::SetChecked(static_cast(ThemeManagerGetAvailableThemeIndex()), true); - InvalidateWidget(WIDX_THEMES_DROPDOWN); - break; + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, numItems, widget->width() - 3); + + Dropdown::SetChecked(static_cast(ThemeManagerGetAvailableThemeIndex()), true); + InvalidateWidget(WIDX_THEMES_DROPDOWN); + break; + } } - } - void ControlsDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) + void ControlsDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { - case WIDX_THEMES_DROPDOWN: - if (dropdownIndex != -1) - { - ThemeManagerSetActiveAvailableTheme(dropdownIndex); - } - ConfigSaveDefault(); - break; + switch (widgetIndex) + { + case WIDX_THEMES_DROPDOWN: + if (dropdownIndex != -1) + { + ThemeManagerSetActiveAvailableTheme(dropdownIndex); + } + ConfigSaveDefault(); + break; + } } - } - void ControlsPrepareDraw() - { - SetCheckboxValue(WIDX_SCREEN_EDGE_SCROLLING, gConfigGeneral.EdgeScrolling); - SetCheckboxValue(WIDX_TRAP_CURSOR, gConfigGeneral.TrapCursor); - SetCheckboxValue(WIDX_INVERT_DRAG, gConfigGeneral.InvertViewportDrag); - SetCheckboxValue(WIDX_ZOOM_TO_CURSOR, gConfigGeneral.ZoomToCursor); - SetCheckboxValue(WIDX_TOOLBAR_SHOW_FINANCES, gConfigInterface.ToolbarShowFinances); - SetCheckboxValue(WIDX_TOOLBAR_SHOW_RESEARCH, gConfigInterface.ToolbarShowResearch); - SetCheckboxValue(WIDX_TOOLBAR_SHOW_CHEATS, gConfigInterface.ToolbarShowCheats); - SetCheckboxValue(WIDX_TOOLBAR_SHOW_NEWS, gConfigInterface.ToolbarShowNews); - SetCheckboxValue(WIDX_TOOLBAR_SHOW_MUTE, gConfigInterface.ToolbarShowMute); - SetCheckboxValue(WIDX_TOOLBAR_SHOW_CHAT, gConfigInterface.ToolbarShowChat); - SetCheckboxValue(WIDX_TOOLBAR_SHOW_ZOOM, gConfigInterface.ToolbarShowZoom); - SetCheckboxValue(WIDX_WINDOW_BUTTONS_ON_THE_LEFT, gConfigInterface.WindowButtonsOnTheLeft); + void ControlsPrepareDraw() + { + SetCheckboxValue(WIDX_SCREEN_EDGE_SCROLLING, gConfigGeneral.EdgeScrolling); + SetCheckboxValue(WIDX_TRAP_CURSOR, gConfigGeneral.TrapCursor); + SetCheckboxValue(WIDX_INVERT_DRAG, gConfigGeneral.InvertViewportDrag); + SetCheckboxValue(WIDX_ZOOM_TO_CURSOR, gConfigGeneral.ZoomToCursor); + SetCheckboxValue(WIDX_TOOLBAR_SHOW_FINANCES, gConfigInterface.ToolbarShowFinances); + SetCheckboxValue(WIDX_TOOLBAR_SHOW_RESEARCH, gConfigInterface.ToolbarShowResearch); + SetCheckboxValue(WIDX_TOOLBAR_SHOW_CHEATS, gConfigInterface.ToolbarShowCheats); + SetCheckboxValue(WIDX_TOOLBAR_SHOW_NEWS, gConfigInterface.ToolbarShowNews); + SetCheckboxValue(WIDX_TOOLBAR_SHOW_MUTE, gConfigInterface.ToolbarShowMute); + SetCheckboxValue(WIDX_TOOLBAR_SHOW_CHAT, gConfigInterface.ToolbarShowChat); + SetCheckboxValue(WIDX_TOOLBAR_SHOW_ZOOM, gConfigInterface.ToolbarShowZoom); + SetCheckboxValue(WIDX_WINDOW_BUTTONS_ON_THE_LEFT, gConfigInterface.WindowButtonsOnTheLeft); - size_t activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex(); - const utf8* activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex); - auto ft = Formatter::Common(); - ft.Add(activeThemeName); - } + size_t activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex(); + const utf8* activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex); + auto ft = Formatter::Common(); + ft.Add(activeThemeName); + } #pragma endregion #pragma region Misc tab events - void MiscMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void MiscMouseUp(WidgetIndex widgetIndex) { - case WIDX_REAL_NAME_CHECKBOX: - gConfigGeneral.ShowRealNamesOfGuests ^= 1; - ConfigSaveDefault(); - Invalidate(); - PeepUpdateNames(gConfigGeneral.ShowRealNamesOfGuests); - break; - case WIDX_AUTO_STAFF_PLACEMENT: - gConfigGeneral.AutoStaffPlacement ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_SCENARIO_UNLOCKING: - gConfigGeneral.ScenarioUnlockingEnabled ^= 1; - ConfigSaveDefault(); - WindowCloseByClass(WindowClass::ScenarioSelect); - break; - case WIDX_AUTO_OPEN_SHOPS: - gConfigGeneral.AutoOpenShops = !gConfigGeneral.AutoOpenShops; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_ALLOW_EARLY_COMPLETION: - gConfigGeneral.AllowEarlyCompletion ^= 1; - // Only the server can control this setting and needs to send the - // current value of allow_early_completion to all clients - if (NetworkGetMode() == NETWORK_MODE_SERVER) - { - auto setAllowEarlyCompletionAction = ScenarioSetSettingAction( - ScenarioSetSetting::AllowEarlyCompletion, gConfigGeneral.AllowEarlyCompletion); - GameActions::Execute(&setAllowEarlyCompletionAction); - } - ConfigSaveDefault(); - Invalidate(); - break; - } - } - - void MiscMouseDown(WidgetIndex widgetIndex) - { - Widget* widget = &widgets[widgetIndex - 1]; - - switch (widgetIndex) - { - case WIDX_TITLE_SEQUENCE_DROPDOWN: + switch (widgetIndex) { - uint32_t numItems = static_cast(TitleSequenceManagerGetCount()); - for (size_t i = 0; i < numItems; i++) - { - gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; - gDropdownItems[i].Args = reinterpret_cast(TitleSequenceManagerGetName(i)); - } - - gDropdownItems[numItems].Format = 0; - numItems++; - gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numItems].Args = STR_TITLE_SEQUENCE_RANDOM; - numItems++; - - WindowDropdownShowText( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], - Dropdown::Flag::StayOpen, numItems); - - auto selectedIndex = gConfigInterface.RandomTitleSequence ? numItems - 1 - : static_cast(TitleGetCurrentSequence()); - Dropdown::SetChecked(selectedIndex, true); - break; - } - case WIDX_SCENARIO_GROUPING_DROPDOWN: - { - uint32_t numItems = 2; - - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_OPTIONS_SCENARIO_DIFFICULTY; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Args = STR_OPTIONS_SCENARIO_ORIGIN; - - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, numItems, widget->width() - 3); - - Dropdown::SetChecked(gConfigGeneral.ScenarioSelectMode, true); - break; - } - case WIDX_DEFAULT_INSPECTION_INTERVAL_DROPDOWN: - for (size_t i = 0; i < 7; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = RideInspectionIntervalNames[i]; - } - - ShowDropdown(widget, 7); - Dropdown::SetChecked(gConfigGeneral.DefaultInspectionInterval, true); - break; - } - } - - void MiscDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_TITLE_SEQUENCE_DROPDOWN: - { - auto numItems = static_cast(TitleSequenceManagerGetCount()); - if (dropdownIndex < numItems && dropdownIndex != static_cast(TitleGetCurrentSequence())) - { - gConfigInterface.RandomTitleSequence = false; - TitleSequenceChangePreset(static_cast(dropdownIndex)); + case WIDX_REAL_NAME_CHECKBOX: + gConfigGeneral.ShowRealNamesOfGuests ^= 1; ConfigSaveDefault(); Invalidate(); - } - else if (dropdownIndex == numItems + 1) - { - gConfigInterface.RandomTitleSequence = true; + PeepUpdateNames(gConfigGeneral.ShowRealNamesOfGuests); + break; + case WIDX_AUTO_STAFF_PLACEMENT: + gConfigGeneral.AutoStaffPlacement ^= 1; ConfigSaveDefault(); Invalidate(); - } - break; - } - case WIDX_DEFAULT_INSPECTION_INTERVAL_DROPDOWN: - if (dropdownIndex != gConfigGeneral.DefaultInspectionInterval) - { - gConfigGeneral.DefaultInspectionInterval = static_cast(dropdownIndex); + break; + case WIDX_SCENARIO_UNLOCKING: + gConfigGeneral.ScenarioUnlockingEnabled ^= 1; ConfigSaveDefault(); - Invalidate(); - } - break; - case WIDX_SCENARIO_GROUPING_DROPDOWN: - if (dropdownIndex != gConfigGeneral.ScenarioSelectMode) - { - gConfigGeneral.ScenarioSelectMode = dropdownIndex; - gConfigInterface.ScenarioselectLastTab = 0; - ConfigSaveDefault(); - Invalidate(); WindowCloseByClass(WindowClass::ScenarioSelect); - } - break; - } - } - - void MiscPrepareDraw() - { - auto ft = Formatter::Common(); - if (gConfigInterface.RandomTitleSequence) - { - ft.Add(STR_TITLE_SEQUENCE_RANDOM); - } - else - { - auto name = TitleSequenceManagerGetName(TitleGetConfigSequence()); - ft.Add(STR_STRING); - ft.Add(name); - } - - // The real name setting of clients is fixed to that of the server - // and the server cannot change the setting during gameplay to prevent desyncs - if (NetworkGetMode() != NETWORK_MODE_NONE) - { - disabled_widgets |= (1uLL << WIDX_REAL_NAME_CHECKBOX); - widgets[WIDX_REAL_NAME_CHECKBOX].tooltip = STR_OPTION_DISABLED_DURING_NETWORK_PLAY; - // Disable the use of the allow_early_completion option during network play on clients. - // This is to prevent confusion on clients because changing this setting during network play wouldn't change - // the way scenarios are completed during this network-session - if (NetworkGetMode() == NETWORK_MODE_CLIENT) - { - disabled_widgets |= (1uLL << WIDX_ALLOW_EARLY_COMPLETION); - widgets[WIDX_ALLOW_EARLY_COMPLETION].tooltip = STR_OPTION_DISABLED_DURING_NETWORK_PLAY; + break; + case WIDX_AUTO_OPEN_SHOPS: + gConfigGeneral.AutoOpenShops = !gConfigGeneral.AutoOpenShops; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_ALLOW_EARLY_COMPLETION: + gConfigGeneral.AllowEarlyCompletion ^= 1; + // Only the server can control this setting and needs to send the + // current value of allow_early_completion to all clients + if (NetworkGetMode() == NETWORK_MODE_SERVER) + { + auto setAllowEarlyCompletionAction = ScenarioSetSettingAction( + ScenarioSetSetting::AllowEarlyCompletion, gConfigGeneral.AllowEarlyCompletion); + GameActions::Execute(&setAllowEarlyCompletionAction); + } + ConfigSaveDefault(); + Invalidate(); + break; } } - SetCheckboxValue(WIDX_REAL_NAME_CHECKBOX, gConfigGeneral.ShowRealNamesOfGuests); - SetCheckboxValue(WIDX_AUTO_STAFF_PLACEMENT, gConfigGeneral.AutoStaffPlacement); - SetCheckboxValue(WIDX_AUTO_OPEN_SHOPS, gConfigGeneral.AutoOpenShops); - SetCheckboxValue(WIDX_ALLOW_EARLY_COMPLETION, gConfigGeneral.AllowEarlyCompletion); - - if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_DIFFICULTY) - widgets[WIDX_SCENARIO_GROUPING].text = STR_OPTIONS_SCENARIO_DIFFICULTY; - else - widgets[WIDX_SCENARIO_GROUPING].text = STR_OPTIONS_SCENARIO_ORIGIN; - - SetCheckboxValue(WIDX_SCENARIO_UNLOCKING, gConfigGeneral.ScenarioUnlockingEnabled); - - if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) + void MiscMouseDown(WidgetIndex widgetIndex) { - disabled_widgets &= ~(1uLL << WIDX_SCENARIO_UNLOCKING); - } - else - { - disabled_widgets |= (1uLL << WIDX_SCENARIO_UNLOCKING); + Widget* widget = &widgets[widgetIndex - 1]; + + switch (widgetIndex) + { + case WIDX_TITLE_SEQUENCE_DROPDOWN: + { + uint32_t numItems = static_cast(TitleSequenceManagerGetCount()); + for (size_t i = 0; i < numItems; i++) + { + gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; + gDropdownItems[i].Args = reinterpret_cast(TitleSequenceManagerGetName(i)); + } + + gDropdownItems[numItems].Format = 0; + numItems++; + gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numItems].Args = STR_TITLE_SEQUENCE_RANDOM; + numItems++; + + WindowDropdownShowText( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], + Dropdown::Flag::StayOpen, numItems); + + auto selectedIndex = gConfigInterface.RandomTitleSequence ? numItems - 1 + : static_cast(TitleGetCurrentSequence()); + Dropdown::SetChecked(selectedIndex, true); + break; + } + case WIDX_SCENARIO_GROUPING_DROPDOWN: + { + uint32_t numItems = 2; + + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_OPTIONS_SCENARIO_DIFFICULTY; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Args = STR_OPTIONS_SCENARIO_ORIGIN; + + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, numItems, widget->width() - 3); + + Dropdown::SetChecked(gConfigGeneral.ScenarioSelectMode, true); + break; + } + case WIDX_DEFAULT_INSPECTION_INTERVAL_DROPDOWN: + for (size_t i = 0; i < 7; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = RideInspectionIntervalNames[i]; + } + + ShowDropdown(widget, 7); + Dropdown::SetChecked(gConfigGeneral.DefaultInspectionInterval, true); + break; + } } - widgets[WIDX_DEFAULT_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[gConfigGeneral.DefaultInspectionInterval]; - } + void MiscDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_TITLE_SEQUENCE_DROPDOWN: + { + auto numItems = static_cast(TitleSequenceManagerGetCount()); + if (dropdownIndex < numItems && dropdownIndex != static_cast(TitleGetCurrentSequence())) + { + gConfigInterface.RandomTitleSequence = false; + TitleSequenceChangePreset(static_cast(dropdownIndex)); + ConfigSaveDefault(); + Invalidate(); + } + else if (dropdownIndex == numItems + 1) + { + gConfigInterface.RandomTitleSequence = true; + ConfigSaveDefault(); + Invalidate(); + } + break; + } + case WIDX_DEFAULT_INSPECTION_INTERVAL_DROPDOWN: + if (dropdownIndex != gConfigGeneral.DefaultInspectionInterval) + { + gConfigGeneral.DefaultInspectionInterval = static_cast(dropdownIndex); + ConfigSaveDefault(); + Invalidate(); + } + break; + case WIDX_SCENARIO_GROUPING_DROPDOWN: + if (dropdownIndex != gConfigGeneral.ScenarioSelectMode) + { + gConfigGeneral.ScenarioSelectMode = dropdownIndex; + gConfigInterface.ScenarioselectLastTab = 0; + ConfigSaveDefault(); + Invalidate(); + WindowCloseByClass(WindowClass::ScenarioSelect); + } + break; + } + } + + void MiscPrepareDraw() + { + auto ft = Formatter::Common(); + if (gConfigInterface.RandomTitleSequence) + { + ft.Add(STR_TITLE_SEQUENCE_RANDOM); + } + else + { + auto name = TitleSequenceManagerGetName(TitleGetConfigSequence()); + ft.Add(STR_STRING); + ft.Add(name); + } + + // The real name setting of clients is fixed to that of the server + // and the server cannot change the setting during gameplay to prevent desyncs + if (NetworkGetMode() != NETWORK_MODE_NONE) + { + disabled_widgets |= (1uLL << WIDX_REAL_NAME_CHECKBOX); + widgets[WIDX_REAL_NAME_CHECKBOX].tooltip = STR_OPTION_DISABLED_DURING_NETWORK_PLAY; + // Disable the use of the allow_early_completion option during network play on clients. + // This is to prevent confusion on clients because changing this setting during network play wouldn't change + // the way scenarios are completed during this network-session + if (NetworkGetMode() == NETWORK_MODE_CLIENT) + { + disabled_widgets |= (1uLL << WIDX_ALLOW_EARLY_COMPLETION); + widgets[WIDX_ALLOW_EARLY_COMPLETION].tooltip = STR_OPTION_DISABLED_DURING_NETWORK_PLAY; + } + } + + SetCheckboxValue(WIDX_REAL_NAME_CHECKBOX, gConfigGeneral.ShowRealNamesOfGuests); + SetCheckboxValue(WIDX_AUTO_STAFF_PLACEMENT, gConfigGeneral.AutoStaffPlacement); + SetCheckboxValue(WIDX_AUTO_OPEN_SHOPS, gConfigGeneral.AutoOpenShops); + SetCheckboxValue(WIDX_ALLOW_EARLY_COMPLETION, gConfigGeneral.AllowEarlyCompletion); + + if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_DIFFICULTY) + widgets[WIDX_SCENARIO_GROUPING].text = STR_OPTIONS_SCENARIO_DIFFICULTY; + else + widgets[WIDX_SCENARIO_GROUPING].text = STR_OPTIONS_SCENARIO_ORIGIN; + + SetCheckboxValue(WIDX_SCENARIO_UNLOCKING, gConfigGeneral.ScenarioUnlockingEnabled); + + if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) + { + disabled_widgets &= ~(1uLL << WIDX_SCENARIO_UNLOCKING); + } + else + { + disabled_widgets |= (1uLL << WIDX_SCENARIO_UNLOCKING); + } + + widgets[WIDX_DEFAULT_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[gConfigGeneral + .DefaultInspectionInterval]; + } #pragma endregion #pragma region Advanced tab events - void AdvancedMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void AdvancedMouseUp(WidgetIndex widgetIndex) { - case WIDX_DEBUGGING_TOOLS: - gConfigGeneral.DebuggingTools ^= 1; - ConfigSaveDefault(); - GfxInvalidateScreen(); - break; - case WIDX_SAVE_PLUGIN_DATA_CHECKBOX: - gConfigGeneral.SavePluginData ^= 1; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_STAY_CONNECTED_AFTER_DESYNC: - gConfigNetwork.StayConnected = !gConfigNetwork.StayConnected; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_ALWAYS_NATIVE_LOADSAVE: - gConfigGeneral.UseNativeBrowseDialog = !gConfigGeneral.UseNativeBrowseDialog; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_PATH_TO_RCT1_BUTTON: + switch (widgetIndex) { - auto rct1path = OpenRCT2::GetContext()->GetUiContext()->ShowDirectoryDialog( - LanguageGetString(STR_PATH_TO_RCT1_BROWSER)); - if (!rct1path.empty()) + case WIDX_DEBUGGING_TOOLS: + gConfigGeneral.DebuggingTools ^= 1; + ConfigSaveDefault(); + GfxInvalidateScreen(); + break; + case WIDX_SAVE_PLUGIN_DATA_CHECKBOX: + gConfigGeneral.SavePluginData ^= 1; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_STAY_CONNECTED_AFTER_DESYNC: + gConfigNetwork.StayConnected = !gConfigNetwork.StayConnected; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_ALWAYS_NATIVE_LOADSAVE: + gConfigGeneral.UseNativeBrowseDialog = !gConfigGeneral.UseNativeBrowseDialog; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_PATH_TO_RCT1_BUTTON: { - // Check if this directory actually contains RCT1 - if (Csg1datPresentAtLocation(rct1path)) + auto rct1path = OpenRCT2::GetContext()->GetUiContext()->ShowDirectoryDialog( + LanguageGetString(STR_PATH_TO_RCT1_BROWSER)); + if (!rct1path.empty()) { - if (Csg1idatPresentAtLocation(rct1path)) + // Check if this directory actually contains RCT1 + if (Csg1datPresentAtLocation(rct1path)) { - if (CsgAtLocationIsUsable(rct1path)) + if (Csg1idatPresentAtLocation(rct1path)) { - gConfigGeneral.RCT1Path = std::move(rct1path); - gConfigInterface.ScenarioselectLastTab = 0; - ConfigSaveDefault(); - ContextShowError(STR_RESTART_REQUIRED, STR_NONE, {}); + if (CsgAtLocationIsUsable(rct1path)) + { + gConfigGeneral.RCT1Path = std::move(rct1path); + gConfigInterface.ScenarioselectLastTab = 0; + ConfigSaveDefault(); + ContextShowError(STR_RESTART_REQUIRED, STR_NONE, {}); + } + else + { + ContextShowError(STR_PATH_TO_RCT1_IS_WRONG_VERSION, STR_NONE, {}); + } } else { - ContextShowError(STR_PATH_TO_RCT1_IS_WRONG_VERSION, STR_NONE, {}); + ContextShowError(STR_PATH_TO_RCT1_DOES_NOT_CONTAIN_CSG1I_DAT, STR_NONE, {}); } } else { - ContextShowError(STR_PATH_TO_RCT1_DOES_NOT_CONTAIN_CSG1I_DAT, STR_NONE, {}); + ContextShowError(STR_PATH_TO_RCT1_WRONG_ERROR, STR_NONE, {}); } } - else + Invalidate(); + break; + } + case WIDX_PATH_TO_RCT1_CLEAR: + if (!gConfigGeneral.RCT1Path.empty()) { - ContextShowError(STR_PATH_TO_RCT1_WRONG_ERROR, STR_NONE, {}); + gConfigGeneral.RCT1Path.clear(); + ConfigSaveDefault(); } - } - Invalidate(); - break; + Invalidate(); + break; + case WIDX_ASSET_PACKS: + ContextOpenWindow(WindowClass::AssetPacks); + break; } - case WIDX_PATH_TO_RCT1_CLEAR: - if (!gConfigGeneral.RCT1Path.empty()) - { - gConfigGeneral.RCT1Path.clear(); - ConfigSaveDefault(); - } - Invalidate(); - break; - case WIDX_ASSET_PACKS: - ContextOpenWindow(WindowClass::AssetPacks); - break; } - } - void AdvancedMouseDown(WidgetIndex widgetIndex) - { - Widget* widget = &widgets[widgetIndex - 1]; - - switch (widgetIndex) + void AdvancedMouseDown(WidgetIndex widgetIndex) { - case WIDX_AUTOSAVE_FREQUENCY_DROPDOWN: - for (size_t i = AUTOSAVE_EVERY_MINUTE; i <= AUTOSAVE_NEVER; i++) - { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = AutosaveNames[i]; - } + Widget* widget = &widgets[widgetIndex - 1]; - ShowDropdown(widget, AUTOSAVE_NEVER + 1); - Dropdown::SetChecked(gConfigGeneral.AutosaveFrequency, true); - break; - case WIDX_AUTOSAVE_AMOUNT_UP: - gConfigGeneral.AutosaveAmount += 1; - ConfigSaveDefault(); - InvalidateWidget(WIDX_AUTOSAVE_FREQUENCY); - InvalidateWidget(WIDX_AUTOSAVE_FREQUENCY_DROPDOWN); - InvalidateWidget(WIDX_AUTOSAVE_AMOUNT); - break; - case WIDX_AUTOSAVE_AMOUNT_DOWN: - if (gConfigGeneral.AutosaveAmount > 1) - { - gConfigGeneral.AutosaveAmount -= 1; + switch (widgetIndex) + { + case WIDX_AUTOSAVE_FREQUENCY_DROPDOWN: + for (size_t i = AUTOSAVE_EVERY_MINUTE; i <= AUTOSAVE_NEVER; i++) + { + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = AutosaveNames[i]; + } + + ShowDropdown(widget, AUTOSAVE_NEVER + 1); + Dropdown::SetChecked(gConfigGeneral.AutosaveFrequency, true); + break; + case WIDX_AUTOSAVE_AMOUNT_UP: + gConfigGeneral.AutosaveAmount += 1; ConfigSaveDefault(); InvalidateWidget(WIDX_AUTOSAVE_FREQUENCY); InvalidateWidget(WIDX_AUTOSAVE_FREQUENCY_DROPDOWN); InvalidateWidget(WIDX_AUTOSAVE_AMOUNT); - } - } - } - - void AdvancedDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_AUTOSAVE_FREQUENCY_DROPDOWN: - if (dropdownIndex != gConfigGeneral.AutosaveFrequency) - { - gConfigGeneral.AutosaveFrequency = static_cast(dropdownIndex); - ConfigSaveDefault(); - Invalidate(); - } - break; - } - } - - void AdvancedPrepareDraw() - { - SetCheckboxValue(WIDX_DEBUGGING_TOOLS, gConfigGeneral.DebuggingTools); - SetCheckboxValue(WIDX_SAVE_PLUGIN_DATA_CHECKBOX, gConfigGeneral.SavePluginData); - SetCheckboxValue(WIDX_STAY_CONNECTED_AFTER_DESYNC, gConfigNetwork.StayConnected); - SetCheckboxValue(WIDX_ALWAYS_NATIVE_LOADSAVE, gConfigGeneral.UseNativeBrowseDialog); - widgets[WIDX_AUTOSAVE_FREQUENCY].text = AutosaveNames[gConfigGeneral.AutosaveFrequency]; - } - - void AdvancedDraw(DrawPixelInfo& dpi) - { - auto ft = Formatter(); - ft.Add(static_cast(gConfigGeneral.AutosaveAmount)); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_AUTOSAVE_AMOUNT].left + 1, widgets[WIDX_AUTOSAVE_AMOUNT].top + 1 }, - STR_WINDOW_COLOUR_2_COMMA16, ft, { colours[1] }); - - const auto normalisedPath = Platform::StrDecompToPrecomp(gConfigGeneral.RCT1Path); - ft = Formatter(); - ft.Add(normalisedPath.c_str()); - - Widget pathWidget = widgets[WIDX_PATH_TO_RCT1_BUTTON]; - - // Apply vertical alignment if appropriate. - int32_t widgetHeight = pathWidget.bottom - pathWidget.top; - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - uint32_t padding = widgetHeight > lineHeight ? (widgetHeight - lineHeight) / 2 : 0; - ScreenCoordsXY screenCoords = { windowPos.x + pathWidget.left + 1, - windowPos.y + pathWidget.top + static_cast(padding) }; - DrawTextEllipsised(dpi, screenCoords, 277, STR_STRING, ft, { colours[1] }); - } - - OpenRCT2String AdvancedTooltip(WidgetIndex widgetIndex, StringId fallback) - { - if (widgetIndex == WIDX_PATH_TO_RCT1_BUTTON) - { - if (gConfigGeneral.RCT1Path.empty()) - { - // No tooltip if the path is empty - return { STR_NONE, {} }; + break; + case WIDX_AUTOSAVE_AMOUNT_DOWN: + if (gConfigGeneral.AutosaveAmount > 1) + { + gConfigGeneral.AutosaveAmount -= 1; + ConfigSaveDefault(); + InvalidateWidget(WIDX_AUTOSAVE_FREQUENCY); + InvalidateWidget(WIDX_AUTOSAVE_FREQUENCY_DROPDOWN); + InvalidateWidget(WIDX_AUTOSAVE_AMOUNT); + } } - - auto ft = Formatter(); - ft.Add(gConfigGeneral.RCT1Path.c_str()); - return { fallback, ft }; } - return { fallback, {} }; - } + + void AdvancedDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_AUTOSAVE_FREQUENCY_DROPDOWN: + if (dropdownIndex != gConfigGeneral.AutosaveFrequency) + { + gConfigGeneral.AutosaveFrequency = static_cast(dropdownIndex); + ConfigSaveDefault(); + Invalidate(); + } + break; + } + } + + void AdvancedPrepareDraw() + { + SetCheckboxValue(WIDX_DEBUGGING_TOOLS, gConfigGeneral.DebuggingTools); + SetCheckboxValue(WIDX_SAVE_PLUGIN_DATA_CHECKBOX, gConfigGeneral.SavePluginData); + SetCheckboxValue(WIDX_STAY_CONNECTED_AFTER_DESYNC, gConfigNetwork.StayConnected); + SetCheckboxValue(WIDX_ALWAYS_NATIVE_LOADSAVE, gConfigGeneral.UseNativeBrowseDialog); + widgets[WIDX_AUTOSAVE_FREQUENCY].text = AutosaveNames[gConfigGeneral.AutosaveFrequency]; + } + + void AdvancedDraw(DrawPixelInfo& dpi) + { + auto ft = Formatter(); + ft.Add(static_cast(gConfigGeneral.AutosaveAmount)); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_AUTOSAVE_AMOUNT].left + 1, widgets[WIDX_AUTOSAVE_AMOUNT].top + 1 }, + STR_WINDOW_COLOUR_2_COMMA16, ft, { colours[1] }); + + const auto normalisedPath = Platform::StrDecompToPrecomp(gConfigGeneral.RCT1Path); + ft = Formatter(); + ft.Add(normalisedPath.c_str()); + + Widget pathWidget = widgets[WIDX_PATH_TO_RCT1_BUTTON]; + + // Apply vertical alignment if appropriate. + int32_t widgetHeight = pathWidget.bottom - pathWidget.top; + int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + uint32_t padding = widgetHeight > lineHeight ? (widgetHeight - lineHeight) / 2 : 0; + ScreenCoordsXY screenCoords = { windowPos.x + pathWidget.left + 1, + windowPos.y + pathWidget.top + static_cast(padding) }; + DrawTextEllipsised(dpi, screenCoords, 277, STR_STRING, ft, { colours[1] }); + } + + OpenRCT2String AdvancedTooltip(WidgetIndex widgetIndex, StringId fallback) + { + if (widgetIndex == WIDX_PATH_TO_RCT1_BUTTON) + { + if (gConfigGeneral.RCT1Path.empty()) + { + // No tooltip if the path is empty + return { STR_NONE, {} }; + } + + auto ft = Formatter(); + ft.Add(gConfigGeneral.RCT1Path.c_str()); + return { fallback, ft }; + } + return { fallback, {} }; + } #pragma endregion - void SetPage(int32_t p) - { - page = p; - frame_no = 0; - pressed_widgets = 0; - widgets = window_options_page_widgets[page]; - - Invalidate(); - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - Invalidate(); - } - - void SetPressedTab() - { - for (int32_t i = 0; i < WINDOW_OPTIONS_PAGE_COUNT; i++) - pressed_widgets &= ~(1 << (WIDX_FIRST_TAB + i)); - pressed_widgets |= 1LL << (WIDX_FIRST_TAB + page); - } - - void ShowDropdown(Widget* widget, int32_t num_items) - { - // helper function, all dropdown boxes have similar properties - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, num_items, widget->width() - 3); - } - - void DrawTabImages(DrawPixelInfo& dpi) - { - DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_DISPLAY, SPR_TAB_PAINT_0); - DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_RENDERING, SPR_G2_TAB_TREE); - DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_CULTURE, SPR_TAB_TIMER_0); - DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_AUDIO, SPR_TAB_MUSIC_0); - DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE, SPR_TAB_GEARS_0); - DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_MISC, SPR_TAB_RIDE_0); - DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_ADVANCED, SPR_TAB_WRENCH_0); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t p, int32_t spriteIndex) - { - WidgetIndex widgetIndex = WIDX_FIRST_TAB + p; - Widget* widget = &widgets[widgetIndex]; - - auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; - - if (!WidgetIsDisabled(*this, widgetIndex)) + void SetPage(int32_t p) { - if (page == p) + page = p; + frame_no = 0; + pressed_widgets = 0; + widgets = window_options_page_widgets[page]; + + Invalidate(); + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); + } + + void SetPressedTab() + { + for (int32_t i = 0; i < WINDOW_OPTIONS_PAGE_COUNT; i++) + pressed_widgets &= ~(1 << (WIDX_FIRST_TAB + i)); + pressed_widgets |= 1LL << (WIDX_FIRST_TAB + page); + } + + void ShowDropdown(Widget* widget, int32_t num_items) + { + // helper function, all dropdown boxes have similar properties + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, num_items, widget->width() - 3); + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_DISPLAY, SPR_TAB_PAINT_0); + DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_RENDERING, SPR_G2_TAB_TREE); + DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_CULTURE, SPR_TAB_TIMER_0); + DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_AUDIO, SPR_TAB_MUSIC_0); + DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE, SPR_TAB_GEARS_0); + DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_MISC, SPR_TAB_RIDE_0); + DrawTabImage(dpi, WINDOW_OPTIONS_PAGE_ADVANCED, SPR_TAB_WRENCH_0); + } + + void DrawTabImage(DrawPixelInfo& dpi, int32_t p, int32_t spriteIndex) + { + WidgetIndex widgetIndex = WIDX_FIRST_TAB + p; + Widget* widget = &widgets[widgetIndex]; + + auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; + + if (!WidgetIsDisabled(*this, widgetIndex)) { - int32_t frame = frame_no / TabAnimationDivisor[page]; - spriteIndex += (frame % TabAnimationFrames[page]); + if (page == p) + { + int32_t frame = frame_no / TabAnimationDivisor[page]; + spriteIndex += (frame % TabAnimationFrames[page]); + } + + // Draw normal, enabled sprite. + GfxDrawSprite(dpi, ImageId(spriteIndex), screenCoords); } + else + { + // Get the window background colour + uint8_t window_colour = NOT_TRANSLUCENT(colours[widget->colour]); - // Draw normal, enabled sprite. - GfxDrawSprite(dpi, ImageId(spriteIndex), screenCoords); + // Draw greyed out (light border bottom right shadow) + GfxDrawSpriteSolid( + &dpi, ImageId(spriteIndex), screenCoords + ScreenCoordsXY{ 1, 1 }, ColourMapA[window_colour].lighter); + + // Draw greyed out (dark) + GfxDrawSpriteSolid(&dpi, ImageId(spriteIndex), screenCoords, ColourMapA[window_colour].mid_light); + } } - else + + void UpdateHeightMarkers() { - // Get the window background colour - uint8_t window_colour = NOT_TRANSLUCENT(colours[widget->colour]); - - // Draw greyed out (light border bottom right shadow) - GfxDrawSpriteSolid( - &dpi, ImageId(spriteIndex), screenCoords + ScreenCoordsXY{ 1, 1 }, ColourMapA[window_colour].lighter); - - // Draw greyed out (dark) - GfxDrawSpriteSolid(&dpi, ImageId(spriteIndex), screenCoords, ColourMapA[window_colour].mid_light); + ConfigSaveDefault(); + GfxInvalidateScreen(); } - } - void UpdateHeightMarkers() - { - ConfigSaveDefault(); - GfxInvalidateScreen(); - } + uint8_t GetScrollPercentage(const Widget& widget, const ScrollBar& scroll) + { + uint8_t w = widget.width() - 1; + return static_cast(scroll.h_left) / (scroll.h_right - w) * 100; + } - uint8_t GetScrollPercentage(const Widget& widget, const ScrollBar& scroll) - { - uint8_t w = widget.width() - 1; - return static_cast(scroll.h_left) / (scroll.h_right - w) * 100; - } + void InitializeScrollPosition(WidgetIndex widgetIndex, int32_t scrollId, uint8_t volume) + { + const auto& widget = widgets[widgetIndex]; + auto& scroll = scrolls[scrollId]; - void InitializeScrollPosition(WidgetIndex widgetIndex, int32_t scrollId, uint8_t volume) - { - const auto& widget = widgets[widgetIndex]; - auto& scroll = scrolls[scrollId]; + int32_t widgetSize = scroll.h_right - (widget.width() - 1); + scroll.h_left = ceil(volume / 100.0f * widgetSize); - int32_t widgetSize = scroll.h_right - (widget.width() - 1); - scroll.h_left = ceil(volume / 100.0f * widgetSize); + WidgetScrollUpdateThumbs(*this, widgetIndex); + } - WidgetScrollUpdateThumbs(*this, widgetIndex); - } + static bool IsRCT1TitleMusicAvailable() + { + auto env = GetContext()->GetPlatformEnvironment(); + auto rct1path = env->GetDirectoryPath(DIRBASE::RCT1); + return !rct1path.empty(); + } - static bool IsRCT1TitleMusicAvailable() - { - auto env = GetContext()->GetPlatformEnvironment(); - auto rct1path = env->GetDirectoryPath(DIRBASE::RCT1); - return !rct1path.empty(); - } + static constexpr StringId AutosaveNames[] = { + STR_SAVE_EVERY_MINUTE, STR_SAVE_EVERY_5MINUTES, STR_SAVE_EVERY_15MINUTES, + STR_SAVE_EVERY_30MINUTES, STR_SAVE_EVERY_HOUR, STR_SAVE_NEVER, + }; - static constexpr StringId AutosaveNames[] = { - STR_SAVE_EVERY_MINUTE, STR_SAVE_EVERY_5MINUTES, STR_SAVE_EVERY_15MINUTES, - STR_SAVE_EVERY_30MINUTES, STR_SAVE_EVERY_HOUR, STR_SAVE_NEVER, + static constexpr struct + { + TitleMusicKind Kind; + StringId Name; + } TitleThemeOptions[] = { + { TitleMusicKind::None, STR_OPTIONS_MUSIC_VALUE_NONE }, + { TitleMusicKind::OpenRCT2, STR_OPENRCT2_DROPDOWN }, + { TitleMusicKind::RCT1, STR_ROLLERCOASTER_TYCOON_1_DROPDOWN }, + { TitleMusicKind::RCT2, STR_ROLLERCOASTER_TYCOON_2_DROPDOWN }, + { TitleMusicKind::Random, STR_OPTIONS_MUSIC_VALUE_RANDOM }, + }; + + static constexpr StringId FullscreenModeNames[] = { + STR_OPTIONS_DISPLAY_WINDOWED, + STR_OPTIONS_DISPLAY_FULLSCREEN, + STR_OPTIONS_DISPLAY_FULLSCREEN_BORDERLESS, + }; + + static constexpr int32_t TabAnimationDivisor[] = { + 4, // WINDOW_OPTIONS_PAGE_DISPLAY, + 1, // WINDOW_OPTIONS_PAGE_RENDERING, + 8, // WINDOW_OPTIONS_PAGE_CULTURE, + 2, // WINDOW_OPTIONS_PAGE_AUDIO, + 2, // WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE, + 4, // WINDOW_OPTIONS_PAGE_MISC, + 2, // WINDOW_OPTIONS_PAGE_ADVANCED, + }; + + static constexpr int32_t TabAnimationFrames[] = { + 8, // WINDOW_OPTIONS_PAGE_DISPLAY, + 1, // WINDOW_OPTIONS_PAGE_RENDERING, + 8, // WINDOW_OPTIONS_PAGE_CULTURE, + 16, // WINDOW_OPTIONS_PAGE_AUDIO, + 4, // WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE, + 16, // WINDOW_OPTIONS_PAGE_MISC, + 16, // WINDOW_OPTIONS_PAGE_ADVANCED, + }; }; - static constexpr struct + /** + * + * rct2: 0x006BAC5B + */ + WindowBase* WindowOptionsOpen() { - TitleMusicKind Kind; - StringId Name; - } TitleThemeOptions[] = { - { TitleMusicKind::None, STR_OPTIONS_MUSIC_VALUE_NONE }, - { TitleMusicKind::OpenRCT2, STR_OPENRCT2_DROPDOWN }, - { TitleMusicKind::RCT1, STR_ROLLERCOASTER_TYCOON_1_DROPDOWN }, - { TitleMusicKind::RCT2, STR_ROLLERCOASTER_TYCOON_2_DROPDOWN }, - { TitleMusicKind::Random, STR_OPTIONS_MUSIC_VALUE_RANDOM }, - }; - - static constexpr StringId FullscreenModeNames[] = { - STR_OPTIONS_DISPLAY_WINDOWED, - STR_OPTIONS_DISPLAY_FULLSCREEN, - STR_OPTIONS_DISPLAY_FULLSCREEN_BORDERLESS, - }; - - static constexpr int32_t TabAnimationDivisor[] = { - 4, // WINDOW_OPTIONS_PAGE_DISPLAY, - 1, // WINDOW_OPTIONS_PAGE_RENDERING, - 8, // WINDOW_OPTIONS_PAGE_CULTURE, - 2, // WINDOW_OPTIONS_PAGE_AUDIO, - 2, // WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE, - 4, // WINDOW_OPTIONS_PAGE_MISC, - 2, // WINDOW_OPTIONS_PAGE_ADVANCED, - }; - - static constexpr int32_t TabAnimationFrames[] = { - 8, // WINDOW_OPTIONS_PAGE_DISPLAY, - 1, // WINDOW_OPTIONS_PAGE_RENDERING, - 8, // WINDOW_OPTIONS_PAGE_CULTURE, - 16, // WINDOW_OPTIONS_PAGE_AUDIO, - 4, // WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE, - 16, // WINDOW_OPTIONS_PAGE_MISC, - 16, // WINDOW_OPTIONS_PAGE_ADVANCED, - }; -}; - -/** - * - * rct2: 0x006BAC5B - */ -WindowBase* WindowOptionsOpen() -{ - return WindowFocusOrCreate(WindowClass::Options, WW, WH, WF_CENTRE_SCREEN); -} + return WindowFocusOrCreate(WindowClass::Options, WW, WH, WF_CENTRE_SCREEN); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Park.cpp b/src/openrct2-ui/windows/Park.cpp index c5eaf575d7..9b2b894dee 100644 --- a/src/openrct2-ui/windows/Park.cpp +++ b/src/openrct2-ui/windows/Park.cpp @@ -35,12 +35,12 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_STRINGID; + static constexpr int32_t WH = 224; -static constexpr StringId WINDOW_TITLE = STR_STRINGID; -static constexpr int32_t WH = 224; - -// clang-format off + // clang-format off enum WindowParkPage { WINDOW_PARK_PAGE_ENTRANCE, WINDOW_PARK_PAGE_RATING, @@ -188,328 +188,218 @@ static constexpr WindowParkAward _parkAwards[] = { { STR_AWARD_MOST_CONFUSING_LAYOUT, SPR_AWARD_MOST_CONFUSING_LAYOUT }, { STR_AWARD_BEST_GENTLE_RIDES, SPR_AWARD_BEST_GENTLE_RIDES }, }; -// clang-format on + // clang-format on -class ParkWindow final : public Window -{ - int32_t _numberOfStaff = -1; - int32_t _numberOfRides = -1; - uint8_t _peepAnimationFrame = 0; - -public: - void OnOpen() override + class ParkWindow final : public Window { - number = 0; - frame_no = 0; - _numberOfRides = -1; - _numberOfStaff = -1; - _peepAnimationFrame = 0; - SetPage(0); - } + int32_t _numberOfStaff = -1; + int32_t _numberOfRides = -1; + uint8_t _peepAnimationFrame = 0; - void OnClose() override - { - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && classification == gCurrentToolWidget.window_classification - && number == gCurrentToolWidget.window_number) + public: + void OnOpen() override { - ToolCancel(); + number = 0; + frame_no = 0; + _numberOfRides = -1; + _numberOfStaff = -1; + _peepAnimationFrame = 0; + SetPage(0); } - } - void OnMouseUp(WidgetIndex idx) override - { - switch (idx) + void OnClose() override { - 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: - SetPage(idx - WIDX_TAB_1); - return; - } - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnMouseUpEntrance(idx); - break; - case WINDOW_PARK_PAGE_OBJECTIVE: - OnMouseUpObjective(idx); - break; - } - } - - void OnResize() override - { - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnResizeEntrance(); - break; - case WINDOW_PARK_PAGE_RATING: - OnResizeRating(); - break; - case WINDOW_PARK_PAGE_GUESTS: - OnResizeGuests(); - break; - case WINDOW_PARK_PAGE_PRICE: - OnResizePrice(); - break; - case WINDOW_PARK_PAGE_STATS: - OnResizeStats(); - break; - case WINDOW_PARK_PAGE_OBJECTIVE: - OnResizeObjective(); - break; - case WINDOW_PARK_PAGE_AWARDS: - OnResizeAwards(); - break; - } - } - - void OnMouseDown(WidgetIndex idx) override - { - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnMouseDownEntrance(idx); - break; - case WINDOW_PARK_PAGE_PRICE: - OnMouseDownPrice(idx); - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnDropdownEntrance(widgetIndex, selectedIndex); - break; - } - } - - void OnUpdate() override - { - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnUpdateEntrance(); - break; - case WINDOW_PARK_PAGE_RATING: - OnUpdateRating(); - break; - case WINDOW_PARK_PAGE_GUESTS: - OnUpdateGuests(); - break; - case WINDOW_PARK_PAGE_PRICE: - OnUpdatePrice(); - break; - case WINDOW_PARK_PAGE_STATS: - OnUpdateStats(); - break; - case WINDOW_PARK_PAGE_OBJECTIVE: - OnUpdateObjective(); - break; - case WINDOW_PARK_PAGE_AWARDS: - OnUpdateAwards(); - break; - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnTextInputEntrance(widgetIndex, text); - break; - case WINDOW_PARK_PAGE_OBJECTIVE: - OnTextInputObjective(widgetIndex, text); - break; - } - } - - void OnPrepareDraw() override - { - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnPrepareDrawEntrance(); - break; - case WINDOW_PARK_PAGE_RATING: - OnPrepareDrawRating(); - break; - case WINDOW_PARK_PAGE_GUESTS: - OnPrepareDrawGuests(); - break; - case WINDOW_PARK_PAGE_PRICE: - OnPrepareDrawPrice(); - break; - case WINDOW_PARK_PAGE_STATS: - OnPrepareDrawStats(); - break; - case WINDOW_PARK_PAGE_OBJECTIVE: - OnPrepareDrawObjective(); - break; - case WINDOW_PARK_PAGE_AWARDS: - OnPrepareDrawAwards(); - break; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - switch (page) - { - case WINDOW_PARK_PAGE_ENTRANCE: - OnDrawEntrance(dpi); - break; - case WINDOW_PARK_PAGE_RATING: - OnDrawRating(dpi); - break; - case WINDOW_PARK_PAGE_GUESTS: - OnDrawGuests(dpi); - break; - case WINDOW_PARK_PAGE_PRICE: - OnDrawPrice(dpi); - break; - case WINDOW_PARK_PAGE_STATS: - OnDrawStats(dpi); - break; - case WINDOW_PARK_PAGE_OBJECTIVE: - OnDrawObjective(dpi); - break; - case WINDOW_PARK_PAGE_AWARDS: - OnDrawAwards(dpi); - break; - } - } - -private: - void SetDisabledTabs() - { - // Disable price tab if money is disabled - disabled_widgets = (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) ? (1uLL << WIDX_TAB_4) : 0; - } - - void PrepareWindowTitleText() - { - auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - auto parkName = park.Name.c_str(); - - auto ft = Formatter::Common(); - ft.Add(STR_STRING); - ft.Add(parkName); - } - -#pragma region Entrance page - void OnMouseUpEntrance(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_BUY_LAND_RIGHTS: - ContextOpenWindow(WindowClass::LandRights); - break; - case WIDX_LOCATE: - ScrollToViewport(); - break; - case WIDX_RENAME: + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && classification == gCurrentToolWidget.window_classification + && number == gCurrentToolWidget.window_number) { - auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); - WindowTextInputRawOpen( - this, WIDX_RENAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, {}, park.Name.c_str(), USER_STRING_MAX_LENGTH); - break; - } - case WIDX_CLOSE_LIGHT: - ParkSetOpen(false); - break; - case WIDX_OPEN_LIGHT: - ParkSetOpen(true); - break; - } - } - - void OnResizeEntrance() - { - flags |= WF_RESIZABLE; - WindowSetResize(*this, 230, 174 + 9, 230 * 3, (274 + 9) * 3); - InitViewport(); - } - - void OnMouseDownEntrance(WidgetIndex widgetIndex) - { - if (widgetIndex == WIDX_OPEN_OR_CLOSE) - { - auto& widget = widgets[widgetIndex]; - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_CLOSE_PARK; - gDropdownItems[1].Args = STR_OPEN_PARK; - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1], 0, 2); - - if (ParkIsOpen()) - { - gDropdownDefaultIndex = 0; - Dropdown::SetChecked(1, true); - } - else - { - gDropdownDefaultIndex = 1; - Dropdown::SetChecked(0, true); + ToolCancel(); } } - } - void OnDropdownEntrance(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (widgetIndex == WIDX_OPEN_OR_CLOSE) + void OnMouseUp(WidgetIndex idx) override { - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - if (dropdownIndex != 0) + switch (idx) { - ParkSetOpen(true); + 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: + SetPage(idx - WIDX_TAB_1); + return; } - else + switch (page) { - ParkSetOpen(false); + case WINDOW_PARK_PAGE_ENTRANCE: + OnMouseUpEntrance(idx); + break; + case WINDOW_PARK_PAGE_OBJECTIVE: + OnMouseUpObjective(idx); + break; } } - } - void OnUpdateEntrance() - { - frame_no++; - WidgetInvalidate(*this, WIDX_TAB_1); - } - - void OnTextInputEntrance(WidgetIndex widgetIndex, std::string_view text) - { - if (widgetIndex == WIDX_RENAME && !text.empty()) + void OnResize() override { - auto action = ParkSetNameAction(std::string(text)); - GameActions::Execute(&action); + switch (page) + { + case WINDOW_PARK_PAGE_ENTRANCE: + OnResizeEntrance(); + break; + case WINDOW_PARK_PAGE_RATING: + OnResizeRating(); + break; + case WINDOW_PARK_PAGE_GUESTS: + OnResizeGuests(); + break; + case WINDOW_PARK_PAGE_PRICE: + OnResizePrice(); + break; + case WINDOW_PARK_PAGE_STATS: + OnResizeStats(); + break; + case WINDOW_PARK_PAGE_OBJECTIVE: + OnResizeObjective(); + break; + case WINDOW_PARK_PAGE_AWARDS: + OnResizeAwards(); + break; + } } - } - void OnPrepareDrawEntrance() - { - const auto& gameState = GetGameState(); - widgets = _pagedWidgets[page]; - InitScrollWidgets(); + void OnMouseDown(WidgetIndex idx) override + { + switch (page) + { + case WINDOW_PARK_PAGE_ENTRANCE: + OnMouseDownEntrance(idx); + break; + case WINDOW_PARK_PAGE_PRICE: + OnMouseDownPrice(idx); + break; + } + } - SetPressedTab(); + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + switch (page) + { + case WINDOW_PARK_PAGE_ENTRANCE: + OnDropdownEntrance(widgetIndex, selectedIndex); + break; + } + } - // Set open / close park button state + void OnUpdate() override + { + switch (page) + { + case WINDOW_PARK_PAGE_ENTRANCE: + OnUpdateEntrance(); + break; + case WINDOW_PARK_PAGE_RATING: + OnUpdateRating(); + break; + case WINDOW_PARK_PAGE_GUESTS: + OnUpdateGuests(); + break; + case WINDOW_PARK_PAGE_PRICE: + OnUpdatePrice(); + break; + case WINDOW_PARK_PAGE_STATS: + OnUpdateStats(); + break; + case WINDOW_PARK_PAGE_OBJECTIVE: + OnUpdateObjective(); + break; + case WINDOW_PARK_PAGE_AWARDS: + OnUpdateAwards(); + break; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + switch (page) + { + case WINDOW_PARK_PAGE_ENTRANCE: + OnTextInputEntrance(widgetIndex, text); + break; + case WINDOW_PARK_PAGE_OBJECTIVE: + OnTextInputObjective(widgetIndex, text); + break; + } + } + + void OnPrepareDraw() override + { + switch (page) + { + case WINDOW_PARK_PAGE_ENTRANCE: + OnPrepareDrawEntrance(); + break; + case WINDOW_PARK_PAGE_RATING: + OnPrepareDrawRating(); + break; + case WINDOW_PARK_PAGE_GUESTS: + OnPrepareDrawGuests(); + break; + case WINDOW_PARK_PAGE_PRICE: + OnPrepareDrawPrice(); + break; + case WINDOW_PARK_PAGE_STATS: + OnPrepareDrawStats(); + break; + case WINDOW_PARK_PAGE_OBJECTIVE: + OnPrepareDrawObjective(); + break; + case WINDOW_PARK_PAGE_AWARDS: + OnPrepareDrawAwards(); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + switch (page) + { + case WINDOW_PARK_PAGE_ENTRANCE: + OnDrawEntrance(dpi); + break; + case WINDOW_PARK_PAGE_RATING: + OnDrawRating(dpi); + break; + case WINDOW_PARK_PAGE_GUESTS: + OnDrawGuests(dpi); + break; + case WINDOW_PARK_PAGE_PRICE: + OnDrawPrice(dpi); + break; + case WINDOW_PARK_PAGE_STATS: + OnDrawStats(dpi); + break; + case WINDOW_PARK_PAGE_OBJECTIVE: + OnDrawObjective(dpi); + break; + case WINDOW_PARK_PAGE_AWARDS: + OnDrawAwards(dpi); + break; + } + } + + private: + void SetDisabledTabs() + { + // Disable price tab if money is disabled + disabled_widgets = (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) ? (1uLL << WIDX_TAB_4) : 0; + } + + void PrepareWindowTitleText() { auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); auto parkName = park.Name.c_str(); @@ -518,845 +408,960 @@ private: ft.Add(STR_STRING); ft.Add(parkName); } - widgets[WIDX_OPEN_OR_CLOSE].image = ImageId(ParkIsOpen() ? SPR_OPEN : SPR_CLOSED); - const auto closeLightImage = SPR_G2_RCT1_CLOSE_BUTTON_0 + !ParkIsOpen() * 2 + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT); - widgets[WIDX_CLOSE_LIGHT].image = ImageId(closeLightImage); - const auto openLightImage = SPR_G2_RCT1_OPEN_BUTTON_0 + ParkIsOpen() * 2 + WidgetIsPressed(*this, WIDX_OPEN_LIGHT); - widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); - // Only allow closing of park for guest / rating objective - if (gameState.ScenarioObjective.Type == OBJECTIVE_GUESTS_AND_RATING) - disabled_widgets |= (1uLL << WIDX_OPEN_OR_CLOSE) | (1uLL << WIDX_CLOSE_LIGHT) | (1uLL << WIDX_OPEN_LIGHT); - else - disabled_widgets &= ~((1uLL << WIDX_OPEN_OR_CLOSE) | (1uLL << WIDX_CLOSE_LIGHT) | (1uLL << WIDX_OPEN_LIGHT)); - - // Only allow purchase of land when there is money - if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) - widgets[WIDX_BUY_LAND_RIGHTS].type = WindowWidgetType::Empty; - else - widgets[WIDX_BUY_LAND_RIGHTS].type = WindowWidgetType::FlatBtn; - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - AnchorBorderWidgets(); - - // Anchor entrance page specific widgets - widgets[WIDX_VIEWPORT].right = width - 26; - widgets[WIDX_VIEWPORT].bottom = height - 14; - widgets[WIDX_STATUS].right = width - 26; - widgets[WIDX_STATUS].top = height - 13; - widgets[WIDX_STATUS].bottom = height - 3; - - auto y = 0; - if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_PARK) +#pragma region Entrance page + void OnMouseUpEntrance(WidgetIndex widgetIndex) { - widgets[WIDX_OPEN_OR_CLOSE].type = WindowWidgetType::Empty; - if (gameState.ScenarioObjective.Type == OBJECTIVE_GUESTS_AND_RATING) + switch (widgetIndex) { - widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::FlatBtn; - widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::FlatBtn; + case WIDX_BUY_LAND_RIGHTS: + ContextOpenWindow(WindowClass::LandRights); + break; + case WIDX_LOCATE: + ScrollToViewport(); + break; + case WIDX_RENAME: + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + WindowTextInputRawOpen( + this, WIDX_RENAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, {}, park.Name.c_str(), USER_STRING_MAX_LENGTH); + break; + } + case WIDX_CLOSE_LIGHT: + ParkSetOpen(false); + break; + case WIDX_OPEN_LIGHT: + ParkSetOpen(true); + break; + } + } + + void OnResizeEntrance() + { + flags |= WF_RESIZABLE; + WindowSetResize(*this, 230, 174 + 9, 230 * 3, (274 + 9) * 3); + InitViewport(); + } + + void OnMouseDownEntrance(WidgetIndex widgetIndex) + { + if (widgetIndex == WIDX_OPEN_OR_CLOSE) + { + auto& widget = widgets[widgetIndex]; + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_CLOSE_PARK; + gDropdownItems[1].Args = STR_OPEN_PARK; + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1], 0, 2); + + if (ParkIsOpen()) + { + gDropdownDefaultIndex = 0; + Dropdown::SetChecked(1, true); + } + else + { + gDropdownDefaultIndex = 1; + Dropdown::SetChecked(0, true); + } + } + } + + void OnDropdownEntrance(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (widgetIndex == WIDX_OPEN_OR_CLOSE) + { + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; + + if (dropdownIndex != 0) + { + ParkSetOpen(true); + } + else + { + ParkSetOpen(false); + } + } + } + + void OnUpdateEntrance() + { + frame_no++; + WidgetInvalidate(*this, WIDX_TAB_1); + } + + void OnTextInputEntrance(WidgetIndex widgetIndex, std::string_view text) + { + if (widgetIndex == WIDX_RENAME && !text.empty()) + { + auto action = ParkSetNameAction(std::string(text)); + GameActions::Execute(&action); + } + } + + void OnPrepareDrawEntrance() + { + const auto& gameState = GetGameState(); + widgets = _pagedWidgets[page]; + InitScrollWidgets(); + + SetPressedTab(); + + // Set open / close park button state + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + + auto ft = Formatter::Common(); + ft.Add(STR_STRING); + ft.Add(parkName); + } + widgets[WIDX_OPEN_OR_CLOSE].image = ImageId(ParkIsOpen() ? SPR_OPEN : SPR_CLOSED); + const auto closeLightImage = SPR_G2_RCT1_CLOSE_BUTTON_0 + !ParkIsOpen() * 2 + + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT); + widgets[WIDX_CLOSE_LIGHT].image = ImageId(closeLightImage); + const auto openLightImage = SPR_G2_RCT1_OPEN_BUTTON_0 + ParkIsOpen() * 2 + WidgetIsPressed(*this, WIDX_OPEN_LIGHT); + widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); + + // Only allow closing of park for guest / rating objective + if (gameState.ScenarioObjective.Type == OBJECTIVE_GUESTS_AND_RATING) + disabled_widgets |= (1uLL << WIDX_OPEN_OR_CLOSE) | (1uLL << WIDX_CLOSE_LIGHT) | (1uLL << WIDX_OPEN_LIGHT); + else + disabled_widgets &= ~((1uLL << WIDX_OPEN_OR_CLOSE) | (1uLL << WIDX_CLOSE_LIGHT) | (1uLL << WIDX_OPEN_LIGHT)); + + // Only allow purchase of land when there is money + if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) + widgets[WIDX_BUY_LAND_RIGHTS].type = WindowWidgetType::Empty; + else + widgets[WIDX_BUY_LAND_RIGHTS].type = WindowWidgetType::FlatBtn; + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + AnchorBorderWidgets(); + + // Anchor entrance page specific widgets + widgets[WIDX_VIEWPORT].right = width - 26; + widgets[WIDX_VIEWPORT].bottom = height - 14; + widgets[WIDX_STATUS].right = width - 26; + widgets[WIDX_STATUS].top = height - 13; + widgets[WIDX_STATUS].bottom = height - 3; + + auto y = 0; + if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_PARK) + { + widgets[WIDX_OPEN_OR_CLOSE].type = WindowWidgetType::Empty; + if (gameState.ScenarioObjective.Type == OBJECTIVE_GUESTS_AND_RATING) + { + widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::FlatBtn; + widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::FlatBtn; + } + else + { + widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; + widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; + } + y = widgets[WIDX_OPEN_LIGHT].bottom + 5; } else { - widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; - widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; + widgets[WIDX_OPEN_OR_CLOSE].type = WindowWidgetType::FlatBtn; + widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; + y = 49; + } + + for (int32_t i = WIDX_CLOSE_LIGHT; i <= WIDX_OPEN_LIGHT; i++) + { + widgets[i].left = width - 20; + widgets[i].right = width - 7; + } + for (int32_t i = WIDX_OPEN_OR_CLOSE; i <= WIDX_RENAME; i++) + { + if (widgets[i].type == WindowWidgetType::Empty) + continue; + + widgets[i].left = width - 25; + widgets[i].right = width - 2; + widgets[i].top = y; + widgets[i].bottom = y + 23; + y += 24; } - y = widgets[WIDX_OPEN_LIGHT].bottom + 5; - } - else - { - widgets[WIDX_OPEN_OR_CLOSE].type = WindowWidgetType::FlatBtn; - widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; - y = 49; } - for (int32_t i = WIDX_CLOSE_LIGHT; i <= WIDX_OPEN_LIGHT; i++) + void OnDrawEntrance(DrawPixelInfo& dpi) { - widgets[i].left = width - 20; - widgets[i].right = width - 7; - } - for (int32_t i = WIDX_OPEN_OR_CLOSE; i <= WIDX_RENAME; i++) - { - if (widgets[i].type == WindowWidgetType::Empty) - continue; + DrawWidgets(dpi); + DrawTabImages(dpi); - widgets[i].left = width - 25; - widgets[i].right = width - 2; - widgets[i].top = y; - widgets[i].bottom = y + 23; - y += 24; - } - } + // Draw viewport + if (viewport != nullptr) + { + WindowDrawViewport(dpi, *this); + if (viewport->flags & VIEWPORT_FLAG_SOUND_ON) + GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this)); + } - void OnDrawEntrance(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); + // Draw park closed / open label + auto ft = Formatter(); + ft.Add(ParkIsOpen() ? STR_PARK_OPEN : STR_PARK_CLOSED); - // Draw viewport - if (viewport != nullptr) - { - WindowDrawViewport(dpi, *this); - if (viewport->flags & VIEWPORT_FLAG_SOUND_ON) - GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this)); + auto* labelWidget = &widgets[WIDX_STATUS]; + DrawTextEllipsised( + dpi, windowPos + ScreenCoordsXY{ labelWidget->midX(), labelWidget->top }, labelWidget->width(), + STR_BLACK_STRING, ft, { TextAlignment::CENTRE }); } - // Draw park closed / open label - auto ft = Formatter(); - ft.Add(ParkIsOpen() ? STR_PARK_OPEN : STR_PARK_CLOSED); - - auto* labelWidget = &widgets[WIDX_STATUS]; - DrawTextEllipsised( - dpi, windowPos + ScreenCoordsXY{ labelWidget->midX(), labelWidget->top }, labelWidget->width(), STR_BLACK_STRING, - ft, { TextAlignment::CENTRE }); - } - - void InitViewport() - { - if (page != WINDOW_PARK_PAGE_ENTRANCE) - return; - - const auto& gameState = GetGameState(); - - std::optional newFocus = std::nullopt; - if (!gameState.ParkEntrances.empty()) + void InitViewport() { - const auto& entrance = gameState.ParkEntrances[0]; - newFocus = Focus(CoordsXYZ{ entrance.x + 16, entrance.y + 16, entrance.z + 32 }); - } + if (page != WINDOW_PARK_PAGE_ENTRANCE) + return; - int32_t viewportFlags{}; - if (viewport == nullptr) - { - viewportFlags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE; - } - else - { - viewportFlags = viewport->flags; - RemoveViewport(); - } + const auto& gameState = GetGameState(); - // Call invalidate event - OnPrepareDraw(); + std::optional newFocus = std::nullopt; + if (!gameState.ParkEntrances.empty()) + { + const auto& entrance = gameState.ParkEntrances[0]; + newFocus = Focus(CoordsXYZ{ entrance.x + 16, entrance.y + 16, entrance.z + 32 }); + } - focus = newFocus; - - if (focus.has_value()) - { - // Create viewport + int32_t viewportFlags{}; if (viewport == nullptr) { - Widget* viewportWidget = &widgets[WIDX_VIEWPORT]; - ViewportCreate( - this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }, - viewportWidget->width() - 1, viewportWidget->height() - 1, focus.value()); - flags |= WF_NO_SCROLLING; - Invalidate(); + viewportFlags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE; + } + else + { + viewportFlags = viewport->flags; + RemoveViewport(); } - } - if (viewport != nullptr) - viewport->flags = viewportFlags; - Invalidate(); - } + // Call invalidate event + OnPrepareDraw(); + + focus = newFocus; + + if (focus.has_value()) + { + // Create viewport + if (viewport == nullptr) + { + Widget* viewportWidget = &widgets[WIDX_VIEWPORT]; + ViewportCreate( + this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }, + viewportWidget->width() - 1, viewportWidget->height() - 1, focus.value()); + flags |= WF_NO_SCROLLING; + Invalidate(); + } + } + + if (viewport != nullptr) + viewport->flags = viewportFlags; + Invalidate(); + } #pragma endregion #pragma region Rating page - void OnResizeRating() - { - WindowSetResize(*this, 255, 182, 255, 182); - } - - void OnUpdateRating() - { - frame_no++; - WidgetInvalidate(*this, WIDX_TAB_2); - } - - void OnPrepareDrawRating() - { - auto* ratingWidgets = _pagedWidgets[page]; - if (ratingWidgets != widgets) + void OnResizeRating() { - widgets = ratingWidgets; - InitScrollWidgets(); + WindowSetResize(*this, 255, 182, 255, 182); } - SetPressedTab(); - PrepareWindowTitleText(); - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - AnchorBorderWidgets(); - } - - void OnDrawRating(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto screenPos = windowPos; - Widget* widget = &widgets[WIDX_PAGE_BACKGROUND]; - - // Current value - auto ft = Formatter(); - ft.Add(GetGameState().ParkRating); - DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_PARK_RATING_LABEL, ft); - - // Graph border - GfxFillRectInset( - dpi, - { screenPos + ScreenCoordsXY{ widget->left + 4, widget->top + 15 }, - screenPos + ScreenCoordsXY{ widget->right - 4, widget->bottom - 4 } }, - colours[1], INSET_RECT_F_30); - - // Y axis labels - screenPos = screenPos + ScreenCoordsXY{ widget->left + 27, widget->top + 23 }; - for (int i = 5; i >= 0; i--) + void OnUpdateRating() { - uint32_t axisValue = i * 200; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, screenPos + ScreenCoordsXY{ 10, 0 }, STR_GRAPH_AXIS_LABEL, ft, { FontStyle::Small, TextAlignment::RIGHT }); + frame_no++; + WidgetInvalidate(*this, WIDX_TAB_2); + } + + void OnPrepareDrawRating() + { + auto* ratingWidgets = _pagedWidgets[page]; + if (ratingWidgets != widgets) + { + widgets = ratingWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + PrepareWindowTitleText(); + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + AnchorBorderWidgets(); + } + + void OnDrawRating(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto screenPos = windowPos; + Widget* widget = &widgets[WIDX_PAGE_BACKGROUND]; + + // Current value + auto ft = Formatter(); + ft.Add(GetGameState().ParkRating); + DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_PARK_RATING_LABEL, ft); + + // Graph border GfxFillRectInset( - dpi, { screenPos + ScreenCoordsXY{ 15, 5 }, screenPos + ScreenCoordsXY{ width - 32, 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - screenPos.y += 20; + dpi, + { screenPos + ScreenCoordsXY{ widget->left + 4, widget->top + 15 }, + screenPos + ScreenCoordsXY{ widget->right - 4, widget->bottom - 4 } }, + colours[1], INSET_RECT_F_30); + + // Y axis labels + screenPos = screenPos + ScreenCoordsXY{ widget->left + 27, widget->top + 23 }; + for (int i = 5; i >= 0; i--) + { + uint32_t axisValue = i * 200; + ft = Formatter(); + ft.Add(axisValue); + DrawTextBasic( + dpi, screenPos + ScreenCoordsXY{ 10, 0 }, STR_GRAPH_AXIS_LABEL, ft, + { FontStyle::Small, TextAlignment::RIGHT }); + GfxFillRectInset( + dpi, { screenPos + ScreenCoordsXY{ 15, 5 }, screenPos + ScreenCoordsXY{ width - 32, 5 } }, colours[2], + INSET_RECT_FLAG_BORDER_INSET); + screenPos.y += 20; + } + + // Graph + screenPos = windowPos + ScreenCoordsXY{ widget->left + 47, widget->top + 26 }; + + Graph::Draw(dpi, GetGameState().ParkRatingHistory, 32, screenPos); } - // Graph - screenPos = windowPos + ScreenCoordsXY{ widget->left + 47, widget->top + 26 }; - - Graph::Draw(dpi, GetGameState().ParkRatingHistory, 32, screenPos); - } - #pragma endregion #pragma region Guests page - void OnResizeGuests() - { - WindowSetResize(*this, 255, 182, 255, 182); - } - - void OnUpdateGuests() - { - frame_no++; - _peepAnimationFrame = (_peepAnimationFrame + 1) % 24; - WidgetInvalidate(*this, WIDX_TAB_3); - } - - void OnPrepareDrawGuests() - { - auto* guestsWidgets = _pagedWidgets[page]; - if (widgets != guestsWidgets) + void OnResizeGuests() { - widgets = guestsWidgets; - InitScrollWidgets(); + WindowSetResize(*this, 255, 182, 255, 182); } - SetPressedTab(); - PrepareWindowTitleText(); - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - AnchorBorderWidgets(); - } - - void OnDrawGuests(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto screenPos = windowPos; - Widget* widget = &widgets[WIDX_PAGE_BACKGROUND]; - - const auto& gameState = OpenRCT2::GetGameState(); - - // Current value - auto ft = Formatter(); - ft.Add(gameState.NumGuestsInPark); - DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_GUESTS_IN_PARK_LABEL, ft); - - // Graph border - GfxFillRectInset( - dpi, - { screenPos + ScreenCoordsXY{ widget->left + 4, widget->top + 15 }, - screenPos + ScreenCoordsXY{ widget->right - 4, widget->bottom - 4 } }, - colours[1], INSET_RECT_F_30); - - // Y axis labels - screenPos = screenPos + ScreenCoordsXY{ widget->left + 27, widget->top + 23 }; - for (int i = 5; i >= 0; i--) + void OnUpdateGuests() { - uint32_t axisValue = i * 1000; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, screenPos + ScreenCoordsXY{ 10, 0 }, STR_GRAPH_AXIS_LABEL, ft, { FontStyle::Small, TextAlignment::RIGHT }); + frame_no++; + _peepAnimationFrame = (_peepAnimationFrame + 1) % 24; + WidgetInvalidate(*this, WIDX_TAB_3); + } + + void OnPrepareDrawGuests() + { + auto* guestsWidgets = _pagedWidgets[page]; + if (widgets != guestsWidgets) + { + widgets = guestsWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + PrepareWindowTitleText(); + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + AnchorBorderWidgets(); + } + + void OnDrawGuests(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto screenPos = windowPos; + Widget* widget = &widgets[WIDX_PAGE_BACKGROUND]; + + const auto& gameState = OpenRCT2::GetGameState(); + + // Current value + auto ft = Formatter(); + ft.Add(gameState.NumGuestsInPark); + DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_GUESTS_IN_PARK_LABEL, ft); + + // Graph border GfxFillRectInset( - dpi, { screenPos + ScreenCoordsXY{ 15, 5 }, screenPos + ScreenCoordsXY{ width - 32, 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - screenPos.y += 20; - } + dpi, + { screenPos + ScreenCoordsXY{ widget->left + 4, widget->top + 15 }, + screenPos + ScreenCoordsXY{ widget->right - 4, widget->bottom - 4 } }, + colours[1], INSET_RECT_F_30); - // Graph - screenPos = windowPos + ScreenCoordsXY{ widget->left + 47, widget->top + 26 }; + // Y axis labels + screenPos = screenPos + ScreenCoordsXY{ widget->left + 27, widget->top + 23 }; + for (int i = 5; i >= 0; i--) + { + uint32_t axisValue = i * 1000; + ft = Formatter(); + ft.Add(axisValue); + DrawTextBasic( + dpi, screenPos + ScreenCoordsXY{ 10, 0 }, STR_GRAPH_AXIS_LABEL, ft, + { FontStyle::Small, TextAlignment::RIGHT }); + GfxFillRectInset( + dpi, { screenPos + ScreenCoordsXY{ 15, 5 }, screenPos + ScreenCoordsXY{ width - 32, 5 } }, colours[2], + INSET_RECT_FLAG_BORDER_INSET); + screenPos.y += 20; + } - uint8_t cappedHistory[32]; - for (size_t i = 0; i < std::size(cappedHistory); i++) - { - auto value = gameState.GuestsInParkHistory[i]; - if (value != std::numeric_limits::max()) + // Graph + screenPos = windowPos + ScreenCoordsXY{ widget->left + 47, widget->top + 26 }; + + uint8_t cappedHistory[32]; + for (size_t i = 0; i < std::size(cappedHistory); i++) { - cappedHistory[i] = static_cast(std::min(value, 5000) / 20); - } - else - { - cappedHistory[i] = std::numeric_limits::max(); + auto value = gameState.GuestsInParkHistory[i]; + if (value != std::numeric_limits::max()) + { + cappedHistory[i] = static_cast(std::min(value, 5000) / 20); + } + else + { + cappedHistory[i] = std::numeric_limits::max(); + } } + Graph::Draw(dpi, cappedHistory, static_cast(std::size(cappedHistory)), screenPos); } - Graph::Draw(dpi, cappedHistory, static_cast(std::size(cappedHistory)), screenPos); - } #pragma endregion #pragma region Price page - void OnResizePrice() - { - WindowSetResize(*this, 230, 124, 230, 124); - } - - void OnMouseDownPrice(WidgetIndex widgetIndex) - { - const auto& gameState = GetGameState(); - switch (widgetIndex) + void OnResizePrice() { - case WIDX_INCREASE_PRICE: + WindowSetResize(*this, 230, 124, 230, 124); + } + + void OnMouseDownPrice(WidgetIndex widgetIndex) + { + const auto& gameState = GetGameState(); + switch (widgetIndex) { - const auto newFee = std::min(MAX_ENTRANCE_FEE, gameState.ParkEntranceFee + 1.00_GBP); - auto gameAction = ParkSetEntranceFeeAction(newFee); - GameActions::Execute(&gameAction); - break; - } - case WIDX_DECREASE_PRICE: - { - const auto newFee = std::max(0.00_GBP, gameState.ParkEntranceFee - 1.00_GBP); - auto gameAction = ParkSetEntranceFeeAction(newFee); - GameActions::Execute(&gameAction); - break; + case WIDX_INCREASE_PRICE: + { + const auto newFee = std::min(MAX_ENTRANCE_FEE, gameState.ParkEntranceFee + 1.00_GBP); + auto gameAction = ParkSetEntranceFeeAction(newFee); + GameActions::Execute(&gameAction); + break; + } + case WIDX_DECREASE_PRICE: + { + const auto newFee = std::max(0.00_GBP, gameState.ParkEntranceFee - 1.00_GBP); + auto gameAction = ParkSetEntranceFeeAction(newFee); + GameActions::Execute(&gameAction); + break; + } } } - } - void OnUpdatePrice() - { - frame_no++; - WidgetInvalidate(*this, WIDX_TAB_4); - } - - void OnPrepareDrawPrice() - { - auto* priceWidgets = _pagedWidgets[page]; - if (widgets != priceWidgets) + void OnUpdatePrice() { - widgets = priceWidgets; - InitScrollWidgets(); + frame_no++; + WidgetInvalidate(*this, WIDX_TAB_4); } - SetPressedTab(); - PrepareWindowTitleText(); - - // Show a tooltip if the park is pay per ride. - widgets[WIDX_PRICE_LABEL].tooltip = STR_NONE; - widgets[WIDX_PRICE].tooltip = STR_NONE; - - if (!ParkEntranceFeeUnlocked()) + void OnPrepareDrawPrice() { - widgets[WIDX_PRICE_LABEL].tooltip = STR_ADMISSION_PRICE_PAY_PER_RIDE_TIP; - widgets[WIDX_PRICE].tooltip = STR_ADMISSION_PRICE_PAY_PER_RIDE_TIP; + auto* priceWidgets = _pagedWidgets[page]; + if (widgets != priceWidgets) + { + widgets = priceWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + PrepareWindowTitleText(); + + // Show a tooltip if the park is pay per ride. + widgets[WIDX_PRICE_LABEL].tooltip = STR_NONE; + widgets[WIDX_PRICE].tooltip = STR_NONE; + + if (!ParkEntranceFeeUnlocked()) + { + widgets[WIDX_PRICE_LABEL].tooltip = STR_ADMISSION_PRICE_PAY_PER_RIDE_TIP; + widgets[WIDX_PRICE].tooltip = STR_ADMISSION_PRICE_PAY_PER_RIDE_TIP; + } + + // If the entry price is locked at free, disable the widget, unless the unlock_all_prices cheat is active. + if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) || !ParkEntranceFeeUnlocked()) + { + widgets[WIDX_PRICE].type = WindowWidgetType::LabelCentred; + widgets[WIDX_INCREASE_PRICE].type = WindowWidgetType::Empty; + widgets[WIDX_DECREASE_PRICE].type = WindowWidgetType::Empty; + } + else + { + widgets[WIDX_PRICE].type = WindowWidgetType::Spinner; + widgets[WIDX_INCREASE_PRICE].type = WindowWidgetType::Button; + widgets[WIDX_DECREASE_PRICE].type = WindowWidgetType::Button; + } + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + AnchorBorderWidgets(); } - // If the entry price is locked at free, disable the widget, unless the unlock_all_prices cheat is active. - if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) || !ParkEntranceFeeUnlocked()) + void OnDrawPrice(DrawPixelInfo& dpi) { - widgets[WIDX_PRICE].type = WindowWidgetType::LabelCentred; - widgets[WIDX_INCREASE_PRICE].type = WindowWidgetType::Empty; - widgets[WIDX_DECREASE_PRICE].type = WindowWidgetType::Empty; + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 30 }; + auto ft = Formatter(); + ft.Add(GetGameState().TotalIncomeFromAdmissions); + DrawTextBasic(dpi, screenCoords, STR_INCOME_FROM_ADMISSIONS, ft); + + money64 parkEntranceFee = ParkGetEntranceFee(); + auto stringId = parkEntranceFee == 0 ? STR_FREE : STR_BOTTOM_TOOLBAR_CASH; + screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_PRICE].left + 1, widgets[WIDX_PRICE].top + 1 }; + ft = Formatter(); + ft.Add(parkEntranceFee); + DrawTextBasic(dpi, screenCoords, stringId, ft, { colours[1] }); } - else - { - widgets[WIDX_PRICE].type = WindowWidgetType::Spinner; - widgets[WIDX_INCREASE_PRICE].type = WindowWidgetType::Button; - widgets[WIDX_DECREASE_PRICE].type = WindowWidgetType::Button; - } - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - AnchorBorderWidgets(); - } - - void OnDrawPrice(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 30 }; - auto ft = Formatter(); - ft.Add(GetGameState().TotalIncomeFromAdmissions); - DrawTextBasic(dpi, screenCoords, STR_INCOME_FROM_ADMISSIONS, ft); - - money64 parkEntranceFee = ParkGetEntranceFee(); - auto stringId = parkEntranceFee == 0 ? STR_FREE : STR_BOTTOM_TOOLBAR_CASH; - screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_PRICE].left + 1, widgets[WIDX_PRICE].top + 1 }; - ft = Formatter(); - ft.Add(parkEntranceFee); - DrawTextBasic(dpi, screenCoords, stringId, ft, { colours[1] }); - } #pragma endregion #pragma region Stats page - void OnResizeStats() - { - WindowSetResize(*this, 230, 119, 230, 119); - } - - void OnUpdateStats() - { - frame_no++; - WidgetInvalidate(*this, WIDX_TAB_5); - - // Invalidate ride count if changed - const auto rideCount = RideGetCount(); - if (_numberOfRides != rideCount) + void OnResizeStats() { - _numberOfRides = rideCount; - WidgetInvalidate(*this, WIDX_PAGE_BACKGROUND); + WindowSetResize(*this, 230, 119, 230, 119); } - // Invalidate number of staff if changed - const auto staffCount = PeepGetStaffCount(); - if (_numberOfStaff != staffCount) + void OnUpdateStats() { - _numberOfStaff = staffCount; - WidgetInvalidate(*this, WIDX_PAGE_BACKGROUND); - } - } + frame_no++; + WidgetInvalidate(*this, WIDX_TAB_5); - void OnPrepareDrawStats() - { - auto* statsWidgets = _pagedWidgets[page]; - if (widgets != statsWidgets) - { - widgets = statsWidgets; - InitScrollWidgets(); + // Invalidate ride count if changed + const auto rideCount = RideGetCount(); + if (_numberOfRides != rideCount) + { + _numberOfRides = rideCount; + WidgetInvalidate(*this, WIDX_PAGE_BACKGROUND); + } + + // Invalidate number of staff if changed + const auto staffCount = PeepGetStaffCount(); + if (_numberOfStaff != staffCount) + { + _numberOfStaff = staffCount; + WidgetInvalidate(*this, WIDX_PAGE_BACKGROUND); + } } - SetPressedTab(); - PrepareWindowTitleText(); - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - AnchorBorderWidgets(); - } - - void OnDrawStats(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - - auto& gameState = GetGameState(); - // Draw park size - auto parkSize = gameState.ParkSize * 10; - auto stringIndex = STR_PARK_SIZE_METRIC_LABEL; - if (gConfigGeneral.MeasurementFormat == MeasurementFormat::Imperial) + void OnPrepareDrawStats() { - stringIndex = STR_PARK_SIZE_IMPERIAL_LABEL; - parkSize = SquaredMetresToSquaredFeet(parkSize); + auto* statsWidgets = _pagedWidgets[page]; + if (widgets != statsWidgets) + { + widgets = statsWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + PrepareWindowTitleText(); + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + AnchorBorderWidgets(); } - auto ft = Formatter(); - ft.Add(parkSize); - DrawTextBasic(dpi, screenCoords, stringIndex, ft); - screenCoords.y += LIST_ROW_HEIGHT; - // Draw number of rides / attractions - if (_numberOfRides != -1) + void OnDrawStats(DrawPixelInfo& dpi) { + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + + auto& gameState = GetGameState(); + // Draw park size + auto parkSize = gameState.ParkSize * 10; + auto stringIndex = STR_PARK_SIZE_METRIC_LABEL; + if (gConfigGeneral.MeasurementFormat == MeasurementFormat::Imperial) + { + stringIndex = STR_PARK_SIZE_IMPERIAL_LABEL; + parkSize = SquaredMetresToSquaredFeet(parkSize); + } + auto ft = Formatter(); + ft.Add(parkSize); + DrawTextBasic(dpi, screenCoords, stringIndex, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + // Draw number of rides / attractions + if (_numberOfRides != -1) + { + ft = Formatter(); + ft.Add(_numberOfRides); + DrawTextBasic(dpi, screenCoords, STR_NUMBER_OF_RIDES_LABEL, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + + // Draw number of staff + if (_numberOfStaff != -1) + { + ft = Formatter(); + ft.Add(_numberOfStaff); + DrawTextBasic(dpi, screenCoords, STR_STAFF_LABEL, ft); + } + screenCoords.y += LIST_ROW_HEIGHT; + + // Draw number of guests in park ft = Formatter(); - ft.Add(_numberOfRides); - DrawTextBasic(dpi, screenCoords, STR_NUMBER_OF_RIDES_LABEL, ft); - } - screenCoords.y += LIST_ROW_HEIGHT; + ft.Add(gameState.NumGuestsInPark); + DrawTextBasic(dpi, screenCoords, STR_GUESTS_IN_PARK_LABEL, ft); + screenCoords.y += LIST_ROW_HEIGHT; - // Draw number of staff - if (_numberOfStaff != -1) - { ft = Formatter(); - ft.Add(_numberOfStaff); - DrawTextBasic(dpi, screenCoords, STR_STAFF_LABEL, ft); + ft.Add(gameState.TotalAdmissions); + DrawTextBasic(dpi, screenCoords, STR_TOTAL_ADMISSIONS, ft); } - screenCoords.y += LIST_ROW_HEIGHT; - - // Draw number of guests in park - ft = Formatter(); - ft.Add(gameState.NumGuestsInPark); - DrawTextBasic(dpi, screenCoords, STR_GUESTS_IN_PARK_LABEL, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(gameState.TotalAdmissions); - DrawTextBasic(dpi, screenCoords, STR_TOTAL_ADMISSIONS, ft); - } #pragma endregion #pragma region Objective page - void OnMouseUpObjective(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void OnMouseUpObjective(WidgetIndex widgetIndex) { - case WIDX_ENTER_NAME: - WindowTextInputOpen( - this, WIDX_ENTER_NAME, STR_ENTER_NAME, STR_PLEASE_ENTER_YOUR_NAME_FOR_THE_SCENARIO_CHART, {}, 0, 0, - ParkNameMaxLength); - break; + switch (widgetIndex) + { + case WIDX_ENTER_NAME: + WindowTextInputOpen( + this, WIDX_ENTER_NAME, STR_ENTER_NAME, STR_PLEASE_ENTER_YOUR_NAME_FOR_THE_SCENARIO_CHART, {}, 0, 0, + ParkNameMaxLength); + break; + } } - } - void OnResizeObjective() - { + void OnResizeObjective() + { #ifndef NO_TTF - if (gCurrentTTFFontSet != nullptr) - WindowSetResize(*this, 230, 270, 230, 270); - else + if (gCurrentTTFFontSet != nullptr) + WindowSetResize(*this, 230, 270, 230, 270); + else #endif - WindowSetResize(*this, 230, 226, 230, 226); - } - - void OnUpdateObjective() - { - frame_no++; - WidgetInvalidate(*this, WIDX_TAB_6); - } - - void OnTextInputObjective(WidgetIndex widgetIndex, std::string_view text) - { - if (widgetIndex == WIDX_ENTER_NAME && !text.empty()) - { - std::string strText(text); - ScenarioSuccessSubmitName(GetGameState(), strText.c_str()); - Invalidate(); + WindowSetResize(*this, 230, 226, 230, 226); } - } - void OnPrepareDrawObjective() - { - SetPressedTab(); - PrepareWindowTitleText(); - - // Show name input button on scenario completion. - if (GetGameState().ParkFlags & PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT) + void OnUpdateObjective() { - widgets[WIDX_ENTER_NAME].type = WindowWidgetType::Button; - widgets[WIDX_ENTER_NAME].top = height - 19; - widgets[WIDX_ENTER_NAME].bottom = height - 6; + frame_no++; + WidgetInvalidate(*this, WIDX_TAB_6); } - else - widgets[WIDX_ENTER_NAME].type = WindowWidgetType::Empty; - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - AnchorBorderWidgets(); - } - - void OnDrawObjective(DrawPixelInfo& dpi) - { - auto& gameState = GetGameState(); - DrawWidgets(dpi); - DrawTabImages(dpi); - - // Scenario description - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 7 }; - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(gameState.ScenarioDetails.c_str()); - screenCoords.y += DrawTextWrapped(dpi, screenCoords, 222, STR_BLACK_STRING, ft); - screenCoords.y += 5; - - // Your objective: - DrawTextBasic(dpi, screenCoords, STR_OBJECTIVE_LABEL); - screenCoords.y += LIST_ROW_HEIGHT; - - // Objective - ft = Formatter(); - if (gameState.ScenarioObjective.Type == OBJECTIVE_BUILD_THE_BEST) + void OnTextInputObjective(WidgetIndex widgetIndex, std::string_view text) { - StringId rideTypeString = STR_NONE; - auto rideTypeId = gameState.ScenarioObjective.RideId; - if (rideTypeId != RIDE_TYPE_NULL && rideTypeId < RIDE_TYPE_COUNT) + if (widgetIndex == WIDX_ENTER_NAME && !text.empty()) { - rideTypeString = GetRideTypeDescriptor(rideTypeId).Naming.Name; + std::string strText(text); + ScenarioSuccessSubmitName(GetGameState(), strText.c_str()); + Invalidate(); } - ft.Add(rideTypeString); - } - else - { - ft.Add(gameState.ScenarioObjective.NumGuests); - ft.Add(DateGetTotalMonths(MONTH_OCTOBER, gameState.ScenarioObjective.Year)); - if (gameState.ScenarioObjective.Type == OBJECTIVE_FINISH_5_ROLLERCOASTERS) - ft.Add(gameState.ScenarioObjective.MinimumExcitement); - else - ft.Add(gameState.ScenarioObjective.Currency); } - screenCoords.y += DrawTextWrapped(dpi, screenCoords, 221, ObjectiveNames[gameState.ScenarioObjective.Type], ft); - screenCoords.y += 5; - - // Objective outcome - if (gameState.ScenarioCompletedCompanyValue != kMoney64Undefined) + void OnPrepareDrawObjective() { - if (gameState.ScenarioCompletedCompanyValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE) + SetPressedTab(); + PrepareWindowTitleText(); + + // Show name input button on scenario completion. + if (GetGameState().ParkFlags & PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT) { - // Objective failed - DrawTextWrapped(dpi, screenCoords, 222, STR_OBJECTIVE_FAILED); + widgets[WIDX_ENTER_NAME].type = WindowWidgetType::Button; + widgets[WIDX_ENTER_NAME].top = height - 19; + widgets[WIDX_ENTER_NAME].bottom = height - 6; + } + else + widgets[WIDX_ENTER_NAME].type = WindowWidgetType::Empty; + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + AnchorBorderWidgets(); + } + + void OnDrawObjective(DrawPixelInfo& dpi) + { + auto& gameState = GetGameState(); + DrawWidgets(dpi); + DrawTabImages(dpi); + + // Scenario description + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 7 }; + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(gameState.ScenarioDetails.c_str()); + screenCoords.y += DrawTextWrapped(dpi, screenCoords, 222, STR_BLACK_STRING, ft); + screenCoords.y += 5; + + // Your objective: + DrawTextBasic(dpi, screenCoords, STR_OBJECTIVE_LABEL); + screenCoords.y += LIST_ROW_HEIGHT; + + // Objective + ft = Formatter(); + if (gameState.ScenarioObjective.Type == OBJECTIVE_BUILD_THE_BEST) + { + StringId rideTypeString = STR_NONE; + auto rideTypeId = gameState.ScenarioObjective.RideId; + if (rideTypeId != RIDE_TYPE_NULL && rideTypeId < RIDE_TYPE_COUNT) + { + rideTypeString = GetRideTypeDescriptor(rideTypeId).Naming.Name; + } + ft.Add(rideTypeString); } else { - // Objective completed - ft = Formatter(); - ft.Add(gameState.ScenarioCompletedCompanyValue); - DrawTextWrapped(dpi, screenCoords, 222, STR_OBJECTIVE_ACHIEVED, ft); + ft.Add(gameState.ScenarioObjective.NumGuests); + ft.Add(DateGetTotalMonths(MONTH_OCTOBER, gameState.ScenarioObjective.Year)); + if (gameState.ScenarioObjective.Type == OBJECTIVE_FINISH_5_ROLLERCOASTERS) + ft.Add(gameState.ScenarioObjective.MinimumExcitement); + else + ft.Add(gameState.ScenarioObjective.Currency); + } + + screenCoords.y += DrawTextWrapped(dpi, screenCoords, 221, ObjectiveNames[gameState.ScenarioObjective.Type], ft); + screenCoords.y += 5; + + // Objective outcome + if (gameState.ScenarioCompletedCompanyValue != kMoney64Undefined) + { + if (gameState.ScenarioCompletedCompanyValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE) + { + // Objective failed + DrawTextWrapped(dpi, screenCoords, 222, STR_OBJECTIVE_FAILED); + } + else + { + // Objective completed + ft = Formatter(); + ft.Add(gameState.ScenarioCompletedCompanyValue); + DrawTextWrapped(dpi, screenCoords, 222, STR_OBJECTIVE_ACHIEVED, ft); + } } } - } #pragma endregion #pragma region Awards page - void OnResizeAwards() - { - WindowSetResize(*this, 230, 182, 230, 182); - } - - void OnUpdateAwards() - { - frame_no++; - WidgetInvalidate(*this, WIDX_TAB_7); - } - - void OnPrepareDrawAwards() - { - auto* awardsWidgets = _pagedWidgets[page]; - if (widgets != awardsWidgets) + void OnResizeAwards() { - widgets = awardsWidgets; - InitScrollWidgets(); + WindowSetResize(*this, 230, 182, 230, 182); } - SetPressedTab(); - PrepareWindowTitleText(); - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); - AnchorBorderWidgets(); - } - - void OnDrawAwards(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - - for (const auto& award : GetAwards()) + void OnUpdateAwards() { - GfxDrawSprite(dpi, ImageId(_parkAwards[EnumValue(award.Type)].sprite), screenCoords); - DrawTextWrapped(dpi, screenCoords + ScreenCoordsXY{ 34, 6 }, 180, _parkAwards[EnumValue(award.Type)].text); - - screenCoords.y += 32; + frame_no++; + WidgetInvalidate(*this, WIDX_TAB_7); } - if (GetAwards().empty()) - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 6, 6 }, STR_NO_RECENT_AWARDS); - } + void OnPrepareDrawAwards() + { + auto* awardsWidgets = _pagedWidgets[page]; + if (widgets != awardsWidgets) + { + widgets = awardsWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + PrepareWindowTitleText(); + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); + AnchorBorderWidgets(); + } + + void OnDrawAwards(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + + for (const auto& award : GetAwards()) + { + GfxDrawSprite(dpi, ImageId(_parkAwards[EnumValue(award.Type)].sprite), screenCoords); + DrawTextWrapped(dpi, screenCoords + ScreenCoordsXY{ 34, 6 }, 180, _parkAwards[EnumValue(award.Type)].text); + + screenCoords.y += 32; + } + + if (GetAwards().empty()) + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 6, 6 }, STR_NO_RECENT_AWARDS); + } #pragma endregion #pragma region Common - void SetPage(int32_t newPage) + void SetPage(int32_t newPage) + { + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) + ToolCancel(); + + // Set listen only to viewport + bool listen = false; + if (newPage == WINDOW_PARK_PAGE_ENTRANCE && viewport != nullptr && !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) + listen = true; + + page = newPage; + frame_no = 0; + _peepAnimationFrame = 0; + RemoveViewport(); + + hold_down_widgets = _pagedHoldDownWidgets[newPage]; + widgets = _pagedWidgets[newPage]; + SetDisabledTabs(); + Invalidate(); + + OnResize(); + OnPrepareDraw(); + OnUpdate(); + if (listen && viewport != nullptr) + viewport->flags |= VIEWPORT_FLAG_SOUND_ON; + } + + void AnchorBorderWidgets() + { + ResizeFrameWithPage(); + } + + void SetPressedTab() + { + for (int32_t i = WIDX_TAB_1; i <= WIDX_TAB_7; i++) + pressed_widgets &= ~(1 << i); + pressed_widgets |= 1LL << (WIDX_TAB_1 + page); + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + // Entrance tab + if (!WidgetIsDisabled(*this, WIDX_TAB_1)) + { + GfxDrawSprite( + dpi, ImageId(SPR_TAB_PARK_ENTRANCE), + windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }); + } + + // Rating tab + if (!WidgetIsDisabled(*this, WIDX_TAB_2)) + { + ImageId spriteIdx(SPR_TAB_GRAPH_0); + if (page == WINDOW_PARK_PAGE_RATING) + spriteIdx = spriteIdx.WithIndexOffset((frame_no / 8) % 8); + GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); + GfxDrawSprite( + dpi, ImageId(SPR_RATING_HIGH), + windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left + 7, widgets[WIDX_TAB_2].top + 1 }); + GfxDrawSprite( + dpi, ImageId(SPR_RATING_LOW), + windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left + 16, widgets[WIDX_TAB_2].top + 12 }); + } + + // Guests tab + if (!WidgetIsDisabled(*this, WIDX_TAB_3)) + { + ImageId spriteIdx(SPR_TAB_GRAPH_0); + if (page == WINDOW_PARK_PAGE_GUESTS) + spriteIdx = spriteIdx.WithIndexOffset((frame_no / 8) % 8); + GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].left, widgets[WIDX_TAB_3].top }); + + ImageId peepImage(GetPeepAnimation(PeepSpriteType::Normal).base_image + 1, COLOUR_BRIGHT_RED, COLOUR_TEAL); + if (page == WINDOW_PARK_PAGE_GUESTS) + peepImage = peepImage.WithIndexOffset(_peepAnimationFrame & 0xFFFFFFFC); + + GfxDrawSprite( + dpi, peepImage, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].midX(), widgets[WIDX_TAB_3].bottom - 9 }); + } + + // Price tab + if (!WidgetIsDisabled(*this, WIDX_TAB_4)) + { + ImageId spriteIdx(SPR_TAB_ADMISSION_0); + if (page == WINDOW_PARK_PAGE_PRICE) + spriteIdx = spriteIdx.WithIndexOffset((frame_no / 2) % 8); + GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_4].left, widgets[WIDX_TAB_4].top }); + } + + // Statistics tab + if (!WidgetIsDisabled(*this, WIDX_TAB_5)) + { + ImageId spriteIdx(SPR_TAB_STATS_0); + if (page == WINDOW_PARK_PAGE_STATS) + spriteIdx = spriteIdx.WithIndexOffset((frame_no / 4) % 7); + GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_5].left, widgets[WIDX_TAB_5].top }); + } + + // Objective tab + if (!WidgetIsDisabled(*this, WIDX_TAB_6)) + { + ImageId spriteIdx(SPR_TAB_OBJECTIVE_0); + if (page == WINDOW_PARK_PAGE_OBJECTIVE) + spriteIdx = spriteIdx.WithIndexOffset((frame_no / 4) % 16); + GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_6].left, widgets[WIDX_TAB_6].top }); + } + + // Awards tab + if (!WidgetIsDisabled(*this, WIDX_TAB_7)) + { + GfxDrawSprite( + dpi, ImageId(SPR_TAB_AWARDS), + windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_7].left, widgets[WIDX_TAB_7].top }); + } + } + }; + + static ParkWindow* ParkWindowOpen(uint8_t page) { - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) - ToolCancel(); - - // Set listen only to viewport - bool listen = false; - if (newPage == WINDOW_PARK_PAGE_ENTRANCE && viewport != nullptr && !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) - listen = true; - - page = newPage; - frame_no = 0; - _peepAnimationFrame = 0; - RemoveViewport(); - - hold_down_widgets = _pagedHoldDownWidgets[newPage]; - widgets = _pagedWidgets[newPage]; - SetDisabledTabs(); - Invalidate(); - - OnResize(); - OnPrepareDraw(); - OnUpdate(); - if (listen && viewport != nullptr) - viewport->flags |= VIEWPORT_FLAG_SOUND_ON; + auto* wnd = WindowFocusOrCreate(WindowClass::ParkInformation, 230, 174 + 9, WF_10); + if (wnd != nullptr && page != WINDOW_PARK_PAGE_ENTRANCE) + { + wnd->OnMouseUp(WIDX_TAB_1 + page); + } + return wnd; } - void AnchorBorderWidgets() + /** + * + * rct2: 0x00667C48 + */ + WindowBase* WindowParkEntranceOpen() { - ResizeFrameWithPage(); + return ParkWindowOpen(WINDOW_PARK_PAGE_ENTRANCE); } - void SetPressedTab() + /** + * + * rct2: 0x00667CA4 + */ + WindowBase* WindowParkRatingOpen() { - for (int32_t i = WIDX_TAB_1; i <= WIDX_TAB_7; i++) - pressed_widgets &= ~(1 << i); - pressed_widgets |= 1LL << (WIDX_TAB_1 + page); + return ParkWindowOpen(WINDOW_PARK_PAGE_RATING); } - void DrawTabImages(DrawPixelInfo& dpi) + /** + * + * rct2: 0x00667D35 + */ + WindowBase* WindowParkGuestsOpen() { - // Entrance tab - if (!WidgetIsDisabled(*this, WIDX_TAB_1)) - { - GfxDrawSprite( - dpi, ImageId(SPR_TAB_PARK_ENTRANCE), - windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }); - } - - // Rating tab - if (!WidgetIsDisabled(*this, WIDX_TAB_2)) - { - ImageId spriteIdx(SPR_TAB_GRAPH_0); - if (page == WINDOW_PARK_PAGE_RATING) - spriteIdx = spriteIdx.WithIndexOffset((frame_no / 8) % 8); - GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); - GfxDrawSprite( - dpi, ImageId(SPR_RATING_HIGH), - windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left + 7, widgets[WIDX_TAB_2].top + 1 }); - GfxDrawSprite( - dpi, ImageId(SPR_RATING_LOW), - windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left + 16, widgets[WIDX_TAB_2].top + 12 }); - } - - // Guests tab - if (!WidgetIsDisabled(*this, WIDX_TAB_3)) - { - ImageId spriteIdx(SPR_TAB_GRAPH_0); - if (page == WINDOW_PARK_PAGE_GUESTS) - spriteIdx = spriteIdx.WithIndexOffset((frame_no / 8) % 8); - GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].left, widgets[WIDX_TAB_3].top }); - - ImageId peepImage(GetPeepAnimation(PeepSpriteType::Normal).base_image + 1, COLOUR_BRIGHT_RED, COLOUR_TEAL); - if (page == WINDOW_PARK_PAGE_GUESTS) - peepImage = peepImage.WithIndexOffset(_peepAnimationFrame & 0xFFFFFFFC); - - GfxDrawSprite( - dpi, peepImage, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].midX(), widgets[WIDX_TAB_3].bottom - 9 }); - } - - // Price tab - if (!WidgetIsDisabled(*this, WIDX_TAB_4)) - { - ImageId spriteIdx(SPR_TAB_ADMISSION_0); - if (page == WINDOW_PARK_PAGE_PRICE) - spriteIdx = spriteIdx.WithIndexOffset((frame_no / 2) % 8); - GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_4].left, widgets[WIDX_TAB_4].top }); - } - - // Statistics tab - if (!WidgetIsDisabled(*this, WIDX_TAB_5)) - { - ImageId spriteIdx(SPR_TAB_STATS_0); - if (page == WINDOW_PARK_PAGE_STATS) - spriteIdx = spriteIdx.WithIndexOffset((frame_no / 4) % 7); - GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_5].left, widgets[WIDX_TAB_5].top }); - } - - // Objective tab - if (!WidgetIsDisabled(*this, WIDX_TAB_6)) - { - ImageId spriteIdx(SPR_TAB_OBJECTIVE_0); - if (page == WINDOW_PARK_PAGE_OBJECTIVE) - spriteIdx = spriteIdx.WithIndexOffset((frame_no / 4) % 16); - GfxDrawSprite(dpi, spriteIdx, windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_6].left, widgets[WIDX_TAB_6].top }); - } - - // Awards tab - if (!WidgetIsDisabled(*this, WIDX_TAB_7)) - { - GfxDrawSprite( - dpi, ImageId(SPR_TAB_AWARDS), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_7].left, widgets[WIDX_TAB_7].top }); - } + return ParkWindowOpen(WINDOW_PARK_PAGE_GUESTS); } -}; -static ParkWindow* ParkWindowOpen(uint8_t page) -{ - auto* wnd = WindowFocusOrCreate(WindowClass::ParkInformation, 230, 174 + 9, WF_10); - if (wnd != nullptr && page != WINDOW_PARK_PAGE_ENTRANCE) + /** + * + * rct2: 0x00667E57 + */ + WindowBase* WindowParkObjectiveOpen() { - wnd->OnMouseUp(WIDX_TAB_1 + page); + auto* wnd = ParkWindowOpen(WINDOW_PARK_PAGE_OBJECTIVE); + if (wnd != nullptr) + { + wnd->Invalidate(); + wnd->windowPos.x = ContextGetWidth() / 2 - 115; + wnd->windowPos.y = ContextGetHeight() / 2 - 87; + wnd->Invalidate(); + } + return wnd; } - return wnd; -} -/** - * - * rct2: 0x00667C48 - */ -WindowBase* WindowParkEntranceOpen() -{ - return ParkWindowOpen(WINDOW_PARK_PAGE_ENTRANCE); -} - -/** - * - * rct2: 0x00667CA4 - */ -WindowBase* WindowParkRatingOpen() -{ - return ParkWindowOpen(WINDOW_PARK_PAGE_RATING); -} - -/** - * - * rct2: 0x00667D35 - */ -WindowBase* WindowParkGuestsOpen() -{ - return ParkWindowOpen(WINDOW_PARK_PAGE_GUESTS); -} - -/** - * - * rct2: 0x00667E57 - */ -WindowBase* WindowParkObjectiveOpen() -{ - auto* wnd = ParkWindowOpen(WINDOW_PARK_PAGE_OBJECTIVE); - if (wnd != nullptr) + /** + * + * rct2: 0x00667DC6 + */ + WindowBase* WindowParkAwardsOpen() { - wnd->Invalidate(); - wnd->windowPos.x = ContextGetWidth() / 2 - 115; - wnd->windowPos.y = ContextGetHeight() / 2 - 87; - wnd->Invalidate(); + return ParkWindowOpen(WINDOW_PARK_PAGE_AWARDS); } - return wnd; -} - -/** - * - * rct2: 0x00667DC6 - */ -WindowBase* WindowParkAwardsOpen() -{ - return ParkWindowOpen(WINDOW_PARK_PAGE_AWARDS); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/PatrolArea.cpp b/src/openrct2-ui/windows/PatrolArea.cpp index 3a096aa865..388d74f4c4 100644 --- a/src/openrct2-ui/windows/PatrolArea.cpp +++ b/src/openrct2-ui/windows/PatrolArea.cpp @@ -25,21 +25,23 @@ #include #include -static constexpr StringId WINDOW_TITLE = STR_SET_PATROL_AREA; -static constexpr int32_t WH = 54; -static constexpr int32_t WW = 104; - -enum WindowPatrolAreaWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PREVIEW, - WIDX_DECREMENT, - WIDX_INCREMENT, -}; + static constexpr StringId WINDOW_TITLE = STR_SET_PATROL_AREA; + static constexpr int32_t WH = 54; + static constexpr int32_t WW = 104; -// clang-format off + enum WindowPatrolAreaWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PREVIEW, + WIDX_DECREMENT, + WIDX_INCREMENT, + }; + + // clang-format off static Widget PatrolAreaWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget ({27, 17}, {44, 32}, WindowWidgetType::ImgBtn, WindowColour::Primary , ImageId(SPR_LAND_TOOL_SIZE_0) ), // preview box @@ -47,262 +49,264 @@ static Widget PatrolAreaWidgets[] = { MakeRemapWidget({54, 32}, {16, 16}, WindowWidgetType::TrnBtn, WindowColour::Tertiary, SPR_LAND_TOOL_INCREASE, STR_ADJUST_LARGER_PATROL_AREA_TIP ), // increment size kWidgetsEnd, }; -// clang-format on + // clang-format on -class PatrolAreaWindow final : public Window -{ -public: - void OnOpen() override + class PatrolAreaWindow final : public Window { - widgets = PatrolAreaWidgets; - hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); - WindowInitScrollWidgets(*this); - WindowPushOthersBelow(*this); - gLandToolSize = 4; - } - - void OnClose() override - { - // If the tool wasn't changed, turn tool off - if (PatrolAreaToolIsActive()) - ToolCancel(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: + widgets = PatrolAreaWidgets; + hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); + WindowInitScrollWidgets(*this); + WindowPushOthersBelow(*this); + gLandToolSize = 4; + } + + void OnClose() override + { + // If the tool wasn't changed, turn tool off + if (PatrolAreaToolIsActive()) + ToolCancel(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_PREVIEW: + InputSize(); + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_DECREMENT: + gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); + Invalidate(); + break; + case WIDX_INCREMENT: + gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); + Invalidate(); + break; + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (text.empty()) + return; + + if (widgetIndex != WIDX_PREVIEW) + return; + + const auto res = String::Parse(text); + if (res.has_value()) + { + int32_t size; + size = res.value(); + size = std::max(kLandToolMinimumSize, size); + size = std::min(kLandToolMaximumSize, size); + gLandToolSize = size; + Invalidate(); + } + } + + void OnUpdate() override + { + // Close window if another tool is open or staff window gets closed + if (!PatrolAreaToolIsActive() || !IsStaffWindowOpen()) + { Close(); - break; - case WIDX_PREVIEW: - InputSize(); - break; + } } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnPrepareDraw() override { - case WIDX_DECREMENT: - gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); - Invalidate(); - break; - case WIDX_INCREMENT: - gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); - Invalidate(); - break; + SetWidgetPressed(WIDX_PREVIEW, true); + PatrolAreaWidgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); } - } - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (text.empty()) - return; - - if (widgetIndex != WIDX_PREVIEW) - return; - - const auto res = String::Parse(text); - if (res.has_value()) + void OnDraw(DrawPixelInfo& dpi) override { - int32_t size; - size = res.value(); - size = std::max(kLandToolMinimumSize, size); - size = std::min(kLandToolMaximumSize, size); - gLandToolSize = size; - Invalidate(); - } - } + DrawWidgets(dpi); - void OnUpdate() override - { - // Close window if another tool is open or staff window gets closed - if (!PatrolAreaToolIsActive() || !IsStaffWindowOpen()) + // Draw number for tool sizes bigger than 7 + if (gLandToolSize > kLandToolMaximumSizeWithSprite) + { + auto screenCoords = ScreenCoordsXY{ windowPos.x + PatrolAreaWidgets[WIDX_PREVIEW].midX(), + windowPos.y + PatrolAreaWidgets[WIDX_PREVIEW].midY() }; + auto ft = Formatter(); + ft.Add(gLandToolSize); + DrawTextBasic( + dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + } + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - Close(); + auto mapTile = GetBestCoordsFromPos(screenCoords); + if (!mapTile) + return; + + auto stateChanged = false; + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + stateChanged = true; + + if (gMapSelectType != MAP_SELECT_TYPE_FULL) + stateChanged = true; + + auto toolSize = std::max(1, gLandToolSize); + auto toolLength = (toolSize - 1) * 32; + + // Move to tool bottom left + mapTile->x -= (toolSize - 1) * 16; + mapTile->y -= (toolSize - 1) * 16; + mapTile = mapTile->ToTileStart(); + auto posA = *mapTile; + mapTile->x += toolLength; + mapTile->y += toolLength; + auto posB = *mapTile; + if (gMapSelectPositionA != posA || gMapSelectPositionB != posB) + stateChanged = true; + + if (stateChanged) + { + // Invalidate previous area + MapInvalidateSelectionRect(); + + // Update and invalidate new area + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectPositionA = posA; + gMapSelectPositionB = posB; + MapInvalidateSelectionRect(); + } } - } - void OnPrepareDraw() override - { - SetWidgetPressed(WIDX_PREVIEW, true); - PatrolAreaWidgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - // Draw number for tool sizes bigger than 7 - if (gLandToolSize > kLandToolMaximumSizeWithSprite) + void OnToolAbort(WidgetIndex widgetIndex) override { - auto screenCoords = ScreenCoordsXY{ windowPos.x + PatrolAreaWidgets[WIDX_PREVIEW].midX(), - windowPos.y + PatrolAreaWidgets[WIDX_PREVIEW].midY() }; - auto ft = Formatter(); - ft.Add(gLandToolSize); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + HideGridlines(); + ClearPatrolAreaToRender(); + GfxInvalidateScreen(); } - } - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - auto mapTile = GetBestCoordsFromPos(screenCoords); - if (!mapTile) - return; - - auto stateChanged = false; - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - stateChanged = true; - - if (gMapSelectType != MAP_SELECT_TYPE_FULL) - stateChanged = true; - - auto toolSize = std::max(1, gLandToolSize); - auto toolLength = (toolSize - 1) * 32; - - // Move to tool bottom left - mapTile->x -= (toolSize - 1) * 16; - mapTile->y -= (toolSize - 1) * 16; - mapTile = mapTile->ToTileStart(); - auto posA = *mapTile; - mapTile->x += toolLength; - mapTile->y += toolLength; - auto posB = *mapTile; - if (gMapSelectPositionA != posA || gMapSelectPositionB != posB) - stateChanged = true; - - if (stateChanged) + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - // Invalidate previous area - MapInvalidateSelectionRect(); + auto mapTile = GetBestCoordsFromPos(screenCoords); + if (mapTile) + { + auto staff = GetEntity(_staffId); + if (staff != nullptr) + { + _mode = staff->IsPatrolAreaSet(*mapTile) ? StaffSetPatrolAreaMode::Unset : StaffSetPatrolAreaMode::Set; + } + } - // Update and invalidate new area - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = posA; - gMapSelectPositionB = posB; - MapInvalidateSelectionRect(); + OnToolDrag(widgetIndex, screenCoords); } - } - void OnToolAbort(WidgetIndex widgetIndex) override - { - HideGridlines(); - ClearPatrolAreaToRender(); - GfxInvalidateScreen(); - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - auto mapTile = GetBestCoordsFromPos(screenCoords); - if (mapTile) + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { auto staff = GetEntity(_staffId); if (staff != nullptr) { - _mode = staff->IsPatrolAreaSet(*mapTile) ? StaffSetPatrolAreaMode::Unset : StaffSetPatrolAreaMode::Set; + MapRange range(gMapSelectPositionA, gMapSelectPositionB); + auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(_staffId, range, _mode); + GameActions::Execute(&staffSetPatrolAreaAction); } } - OnToolDrag(widgetIndex, screenCoords); - } - - void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - auto staff = GetEntity(_staffId); - if (staff != nullptr) + EntityId GetStaffId() const { - MapRange range(gMapSelectPositionA, gMapSelectPositionB); - auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(_staffId, range, _mode); - GameActions::Execute(&staffSetPatrolAreaAction); + return _staffId; } - } - EntityId GetStaffId() const - { - return _staffId; - } - - void SetStaffId(EntityId staffId) - { - _staffId = staffId; - EnableTool(); - } - -private: - EntityId _staffId; - StaffSetPatrolAreaMode _mode; - - void EnableTool() - { - if (PatrolAreaToolIsActive()) + void SetStaffId(EntityId staffId) { - SetPatrolAreaToRender(_staffId); - GfxInvalidateScreen(); + _staffId = staffId; + EnableTool(); } - else + + private: + EntityId _staffId; + StaffSetPatrolAreaMode _mode; + + void EnableTool() { - if (!ToolSet(*this, 0, Tool::WalkDown)) + if (PatrolAreaToolIsActive()) { - ShowGridlines(); - InputSetFlag(INPUT_FLAG_6, true); SetPatrolAreaToRender(_staffId); GfxInvalidateScreen(); } + else + { + if (!ToolSet(*this, 0, Tool::WalkDown)) + { + ShowGridlines(); + InputSetFlag(INPUT_FLAG_6, true); + SetPatrolAreaToRender(_staffId); + GfxInvalidateScreen(); + } + } } - } - void InputSize() + void InputSize() + { + Formatter ft; + ft.Add(kLandToolMinimumSize); + ft.Add(kLandToolMaximumSize); + WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); + } + + bool PatrolAreaToolIsActive() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return false; + if (gCurrentToolWidget.window_classification != WindowClass::PatrolArea) + return false; + return true; + } + + bool IsStaffWindowOpen() + { + // If staff window for this patrol area was closed, tool is no longer active + auto staffWindow = WindowFindByNumber(WindowClass::Peep, _staffId); + return staffWindow != nullptr; + } + + std::optional GetBestCoordsFromPos(const ScreenCoordsXY& pos) + { + auto coords = FootpathGetCoordinatesFromPos(pos, nullptr, nullptr); + return coords.IsNull() ? std::nullopt : std::make_optional(coords); + } + + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowPatrolAreaOpen(EntityId staffId) { - Formatter ft; - ft.Add(kLandToolMinimumSize); - ft.Add(kLandToolMaximumSize); - WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); + auto w = WindowFocusOrCreate( + WindowClass::PatrolArea, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); + if (w != nullptr) + { + w->SetStaffId(staffId); + } + return w; } - bool PatrolAreaToolIsActive() + EntityId WindowPatrolAreaGetCurrentStaffId() { - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return false; - if (gCurrentToolWidget.window_classification != WindowClass::PatrolArea) - return false; - return true; + auto current = reinterpret_cast(WindowFindByClass(WindowClass::PatrolArea)); + return current != nullptr ? current->GetStaffId() : EntityId::GetNull(); } - - bool IsStaffWindowOpen() - { - // If staff window for this patrol area was closed, tool is no longer active - auto staffWindow = WindowFindByNumber(WindowClass::Peep, _staffId); - return staffWindow != nullptr; - } - - std::optional GetBestCoordsFromPos(const ScreenCoordsXY& pos) - { - auto coords = FootpathGetCoordinatesFromPos(pos, nullptr, nullptr); - return coords.IsNull() ? std::nullopt : std::make_optional(coords); - } - - void OnResize() override - { - ResizeFrame(); - } -}; - -WindowBase* WindowPatrolAreaOpen(EntityId staffId) -{ - auto w = WindowFocusOrCreate( - WindowClass::PatrolArea, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); - if (w != nullptr) - { - w->SetStaffId(staffId); - } - return w; -} - -EntityId WindowPatrolAreaGetCurrentStaffId() -{ - auto current = reinterpret_cast(WindowFindByClass(WindowClass::PatrolArea)); - return current != nullptr ? current->GetStaffId() : EntityId::GetNull(); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Player.cpp b/src/openrct2-ui/windows/Player.cpp index a9d2f50957..3a76fc9eac 100644 --- a/src/openrct2-ui/windows/Player.cpp +++ b/src/openrct2-ui/windows/Player.cpp @@ -21,7 +21,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowPlayerPage { WINDOW_PLAYER_PAGE_OVERVIEW, WINDOW_PLAYER_PAGE_STATISTICS, @@ -74,579 +76,580 @@ static Widget *window_player_page_widgets[] = { #pragma endregion -// clang-format on + // clang-format on -class PlayerWindow final : public Window -{ - int16_t _previousRotation = -1; - bool _drawViewport = true; - -public: - void Init(const uint8_t id) + class PlayerWindow final : public Window { - number = id; - InitScrollWidgets(); - SetPage(WINDOW_PLAYER_PAGE_OVERVIEW); - } + int16_t _previousRotation = -1; + bool _drawViewport = true; + + public: + void Init(const uint8_t id) + { + number = id; + InitScrollWidgets(); + SetPage(WINDOW_PLAYER_PAGE_OVERVIEW); + } #pragma region Events - void OnOpen() override - { - page = 0; - frame_no = 0; - list_information_type = 0; - min_width = 210; - min_height = 134; - max_width = 500; - max_height = 450; - - Invalidate(); - - widgets = window_player_page_widgets[WINDOW_PLAYER_PAGE_OVERVIEW]; - hold_down_widgets = 0; - pressed_widgets = 0; - } - - void OnResize() override - { - switch (page) + void OnOpen() override { - case WINDOW_PLAYER_PAGE_OVERVIEW: - OnResizeOverview(); - break; + page = 0; + frame_no = 0; + list_information_type = 0; + min_width = 210; + min_height = 134; + max_width = 500; + max_height = 450; - case WINDOW_PLAYER_PAGE_STATISTICS: - OnResizeStatistics(); - break; - } - } + Invalidate(); - void OnUpdate() override - { - switch (page) - { - case WINDOW_PLAYER_PAGE_OVERVIEW: - OnUpdateOverview(); - break; - - case WINDOW_PLAYER_PAGE_STATISTICS: - OnUpdateStatistics(); - break; - } - } - - void OnPrepareDraw() override - { - switch (page) - { - case WINDOW_PLAYER_PAGE_OVERVIEW: - OnPrepareDrawOverview(); - break; - - case WINDOW_PLAYER_PAGE_STATISTICS: - OnPrepareDrawStatistics(); - break; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - switch (page) - { - case WINDOW_PLAYER_PAGE_OVERVIEW: - OnDrawOverview(dpi); - break; - - case WINDOW_PLAYER_PAGE_STATISTICS: - OnDrawStatistics(dpi); - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (page) - { - case WINDOW_PLAYER_PAGE_OVERVIEW: - OnMouseDownOverview(widgetIndex); - break; - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - return; - case WIDX_TAB_1: - case WIDX_TAB_2: - SetPage(widgetIndex - WIDX_TAB_1); - return; + widgets = window_player_page_widgets[WINDOW_PLAYER_PAGE_OVERVIEW]; + hold_down_widgets = 0; + pressed_widgets = 0; } - switch (page) + void OnResize() override { - case WINDOW_PLAYER_PAGE_OVERVIEW: - OnMouseUpOverview(widgetIndex); - break; - } - } + switch (page) + { + case WINDOW_PLAYER_PAGE_OVERVIEW: + OnResizeOverview(); + break; - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - switch (page) - { - case WINDOW_PLAYER_PAGE_OVERVIEW: - OnDropdownOverview(widgetIndex, selectedIndex); - break; + case WINDOW_PLAYER_PAGE_STATISTICS: + OnResizeStatistics(); + break; + } + } + + void OnUpdate() override + { + switch (page) + { + case WINDOW_PLAYER_PAGE_OVERVIEW: + OnUpdateOverview(); + break; + + case WINDOW_PLAYER_PAGE_STATISTICS: + OnUpdateStatistics(); + break; + } + } + + void OnPrepareDraw() override + { + switch (page) + { + case WINDOW_PLAYER_PAGE_OVERVIEW: + OnPrepareDrawOverview(); + break; + + case WINDOW_PLAYER_PAGE_STATISTICS: + OnPrepareDrawStatistics(); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + switch (page) + { + case WINDOW_PLAYER_PAGE_OVERVIEW: + OnDrawOverview(dpi); + break; + + case WINDOW_PLAYER_PAGE_STATISTICS: + OnDrawStatistics(dpi); + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (page) + { + case WINDOW_PLAYER_PAGE_OVERVIEW: + OnMouseDownOverview(widgetIndex); + break; + } + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + return; + case WIDX_TAB_1: + case WIDX_TAB_2: + SetPage(widgetIndex - WIDX_TAB_1); + return; + } + + switch (page) + { + case WINDOW_PLAYER_PAGE_OVERVIEW: + OnMouseUpOverview(widgetIndex); + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + switch (page) + { + case WINDOW_PLAYER_PAGE_OVERVIEW: + OnDropdownOverview(widgetIndex, selectedIndex); + break; + } } - } #pragma endregion -private: - void SetPage(int32_t newPage) - { - int32_t originalPage = page; - - page = newPage; - frame_no = 0; - - hold_down_widgets = 0; - pressed_widgets = 0; - widgets = window_player_page_widgets[newPage]; - Invalidate(); - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - Invalidate(); - - if (page == WINDOW_PLAYER_PAGE_OVERVIEW) + private: + void SetPage(int32_t newPage) { - if (viewport == nullptr) + int32_t originalPage = page; + + page = newPage; + frame_no = 0; + + hold_down_widgets = 0; + pressed_widgets = 0; + widgets = window_player_page_widgets[newPage]; + Invalidate(); + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); + + if (page == WINDOW_PLAYER_PAGE_OVERVIEW) { - const auto viewportFocus = Focus(TileCoordsXYZ(128, 128, 0).ToCoordsXYZ()); - ViewportCreate(this, windowPos, width, height, viewportFocus); - flags |= WF_NO_SCROLLING; - OnPrepareDraw(); - UpdateViewport(false); - } - else if (originalPage != page) - { - OnPrepareDraw(); - UpdateViewport(false); - } - } - else - { - RemoveViewport(); - } - } - - void DrawTabImages(DrawPixelInfo& dpi) - { - Widget* widget; - - // Tab 1 - if (!IsWidgetDisabled(WIDX_TAB_1)) - { - widget = &this->widgets[WIDX_TAB_1]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; - GfxDrawSprite(dpi, ImageId(SPR_PEEP_LARGE_FACE_NORMAL), screenCoords); - } - - // Tab 2 - if (!IsWidgetDisabled(WIDX_TAB_2)) - { - widget = &this->widgets[WIDX_TAB_2]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; - int32_t imageId = SPR_TAB_FINANCES_SUMMARY_0; - - if (page == WINDOW_PLAYER_PAGE_STATISTICS) - { - imageId += (frame_no / 2) & 7; - } - - GfxDrawSprite(dpi, ImageId(imageId), screenCoords); - } - } - - void UpdateViewport(bool scroll) - { - int32_t playerIndex = NetworkGetPlayerIndex(static_cast(number)); - if (playerIndex == -1) - { - return; - } - - if (viewport != nullptr) - { - auto coord = NetworkGetPlayerLastActionCoord(playerIndex); - if (coord.x != 0 || coord.y != 0 || coord.z != 0) - { - auto centreLoc = centre_2d_coordinates(coord, viewport); - if (!centreLoc.has_value()) + if (viewport == nullptr) { - return; + const auto viewportFocus = Focus(TileCoordsXYZ(128, 128, 0).ToCoordsXYZ()); + ViewportCreate(this, windowPos, width, height, viewportFocus); + flags |= WF_NO_SCROLLING; + OnPrepareDraw(); + UpdateViewport(false); } - // Don't scroll if the view was originally undefined - if (!_drawViewport) + else if (originalPage != page) { - scroll = false; + OnPrepareDraw(); + UpdateViewport(false); } - - if (!scroll || savedViewPos != centreLoc.value()) - { - flags |= WF_SCROLLING_TO_LOCATION; - savedViewPos = centreLoc.value(); - if (!scroll) - { - viewport->viewPos = centreLoc.value(); - } - InvalidateWidget(WIDX_VIEWPORT); - } - - // Draw the viewport - _drawViewport = true; } else { - // Don't draw the viewport - _drawViewport = false; + RemoveViewport(); } } - } - void UpdateTitle() - { - auto ft = Formatter::Common(); - int32_t player = NetworkGetPlayerIndex(static_cast(number)); - if (player != -1) + void DrawTabImages(DrawPixelInfo& dpi) { - ft.Add(NetworkGetPlayerName(player)); // set title caption to player name - } - else - { - ft.Add(""); - } - } + Widget* widget; -#pragma region Overview - - void OnResizeOverview() - { - WindowSetResize(*this, 240, 170, 500, 300); - } - - void OnUpdateOverview() - { - frame_no++; - InvalidateWidget(WIDX_TAB_1 + page); - - if (NetworkGetPlayerIndex(static_cast(number)) == -1) - { - Close(); - return; - } - - // Update viewport - bool scroll = true; - - // Use this spare window field for rotation check - if (_previousRotation != GetCurrentRotation()) - { - _previousRotation = GetCurrentRotation(); - scroll = false; - } - UpdateViewport(scroll); - } - - void OnPrepareDrawOverview() - { - int32_t playerIndex = NetworkGetPlayerIndex(static_cast(number)); - if (playerIndex == -1) - { - return; - } - - if (window_player_page_widgets[page] != widgets) - { - widgets = window_player_page_widgets[page]; - InitScrollWidgets(); - } - - pressed_widgets &= ~(WIDX_TAB_1); - pressed_widgets &= ~(WIDX_TAB_2); - pressed_widgets |= 1uLL << (page + WIDX_TAB_1); - - UpdateTitle(); - - ResizeFrameWithPage(); - widgets[WIDX_LOCATE].right = width - 2; - widgets[WIDX_LOCATE].left = width - 25; - widgets[WIDX_KICK].right = width - 2; - widgets[WIDX_KICK].left = width - 25; - widgets[WIDX_VIEWPORT].right = width - 26; - widgets[WIDX_VIEWPORT].bottom = height - 14; - - int32_t groupDropdownWidth = widgets[WIDX_GROUP].width(); - widgets[WIDX_GROUP].left = (width - groupDropdownWidth) / 2; - widgets[WIDX_GROUP].right = widgets[WIDX_GROUP].left + groupDropdownWidth; - widgets[WIDX_GROUP_DROPDOWN].left = widgets[WIDX_GROUP].right - 10; - widgets[WIDX_GROUP_DROPDOWN].right = widgets[WIDX_GROUP].right; - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_2); - - if (viewport != nullptr) - { - Widget* viewportWidget = &window_player_overview_widgets[WIDX_VIEWPORT]; - - viewport->pos = windowPos + ScreenCoordsXY{ viewportWidget->left, viewportWidget->top }; - viewport->width = viewportWidget->width(); - viewport->height = viewportWidget->height(); - viewport->view_width = viewport->zoom.ApplyTo(viewport->width); - viewport->view_height = viewport->zoom.ApplyTo(viewport->height); - } - - // Only enable kick button for other players - const bool canKick = NetworkCanPerformAction(NetworkGetCurrentPlayerGroupIndex(), NetworkPermission::KickPlayer); - const bool isServer = NetworkGetPlayerFlags(playerIndex) & NETWORK_PLAYER_FLAG_ISSERVER; - const bool isOwnWindow = (NetworkGetCurrentPlayerId() == number); - WidgetSetEnabled(*this, WIDX_KICK, canKick && !isOwnWindow && !isServer); - } - - void OnDrawOverview(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - int32_t player = NetworkGetPlayerIndex(static_cast(number)); - if (player == -1) - { - return; - } - - // Draw current group - int32_t groupindex = NetworkGetGroupIndex(NetworkGetPlayerGroup(player)); - if (groupindex != -1) - { - Widget* widget = &window_player_overview_widgets[WIDX_GROUP]; - - thread_local std::string _buffer; - _buffer.assign("{WINDOW_COLOUR_2}"); - _buffer += NetworkGetGroupName(groupindex); - auto ft = Formatter(); - ft.Add(_buffer.c_str()); - - DrawTextEllipsised( - dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, - { TextAlignment::CENTRE }); - } - - // Draw ping - auto screenCoords = windowPos + ScreenCoordsXY{ 90, 24 }; - - auto ft = Formatter(); - ft.Add(STR_PING); - DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); - char ping[64]; - snprintf(ping, 64, "%d ms", NetworkGetPlayerPing(player)); - GfxDrawString(dpi, screenCoords + ScreenCoordsXY(30, 0), ping, { colours[2] }); - - // Draw last action - screenCoords = windowPos + ScreenCoordsXY{ width / 2, height - 13 }; - int32_t updatedWidth = this->width - 8; - int32_t lastaction = NetworkGetPlayerLastAction(player, 0); - ft = Formatter(); - if (lastaction != -999) - { - ft.Add(NetworkGetActionNameStringID(lastaction)); - } - else - { - ft.Add(STR_ACTION_NA); - } - DrawTextEllipsised(dpi, screenCoords, updatedWidth, STR_LAST_ACTION_RAN, ft, { TextAlignment::CENTRE }); - - if (viewport != nullptr && _drawViewport) - { - WindowDrawViewport(dpi, *this); - } - } - - void OnMouseDownOverview(WidgetIndex widgetIndex) - { - auto* widget = &widgets[widgetIndex]; - switch (widgetIndex) - { - case WIDX_GROUP_DROPDOWN: - ShowGroupDropdownOverview(widget); - break; - } - } - - void OnMouseUpOverview(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_LOCATE: + // Tab 1 + if (!IsWidgetDisabled(WIDX_TAB_1)) { - WindowBase* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) + widget = &this->widgets[WIDX_TAB_1]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; + GfxDrawSprite(dpi, ImageId(SPR_PEEP_LARGE_FACE_NORMAL), screenCoords); + } + + // Tab 2 + if (!IsWidgetDisabled(WIDX_TAB_2)) + { + widget = &this->widgets[WIDX_TAB_2]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; + int32_t imageId = SPR_TAB_FINANCES_SUMMARY_0; + + if (page == WINDOW_PLAYER_PAGE_STATISTICS) { - int32_t player = NetworkGetPlayerIndex(static_cast(number)); - if (player == -1) + imageId += (frame_no / 2) & 7; + } + + GfxDrawSprite(dpi, ImageId(imageId), screenCoords); + } + } + + void UpdateViewport(bool scroll) + { + int32_t playerIndex = NetworkGetPlayerIndex(static_cast(number)); + if (playerIndex == -1) + { + return; + } + + if (viewport != nullptr) + { + auto coord = NetworkGetPlayerLastActionCoord(playerIndex); + if (coord.x != 0 || coord.y != 0 || coord.z != 0) + { + auto centreLoc = centre_2d_coordinates(coord, viewport); + if (!centreLoc.has_value()) { return; } - auto coord = NetworkGetPlayerLastActionCoord(player); - if (coord.x || coord.y || coord.z) + // Don't scroll if the view was originally undefined + if (!_drawViewport) { - WindowScrollToLocation(*mainWindow, coord); + scroll = false; } + + if (!scroll || savedViewPos != centreLoc.value()) + { + flags |= WF_SCROLLING_TO_LOCATION; + savedViewPos = centreLoc.value(); + if (!scroll) + { + viewport->viewPos = centreLoc.value(); + } + InvalidateWidget(WIDX_VIEWPORT); + } + + // Draw the viewport + _drawViewport = true; + } + else + { + // Don't draw the viewport + _drawViewport = false; } } - break; - case WIDX_KICK: + } + + void UpdateTitle() + { + auto ft = Formatter::Common(); + int32_t player = NetworkGetPlayerIndex(static_cast(number)); + if (player != -1) { - auto kickPlayerAction = PlayerKickAction(number); - GameActions::Execute(&kickPlayerAction); + ft.Add(NetworkGetPlayerName(player)); // set title caption to player name } - break; - } - } - - void OnDropdownOverview(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - const auto playerId = static_cast(number); - const auto playerIdx = NetworkGetPlayerIndex(playerId); - if (playerIdx == -1) - { - return; - } - if (dropdownIndex == -1) - { - return; - } - const auto groupId = NetworkGetGroupID(dropdownIndex); - const auto windowHandle = std::make_pair(classification, number); - auto playerSetGroupAction = PlayerSetGroupAction(playerId, groupId); - playerSetGroupAction.SetCallback([windowHandle](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) + else { - WindowInvalidateByNumber(windowHandle.first, windowHandle.second); + ft.Add(""); } - }); - GameActions::Execute(&playerSetGroupAction); - } - - void ShowGroupDropdownOverview(Widget* widget) - { - Widget* dropdownWidget; - int32_t numItems, i; - int32_t player = NetworkGetPlayerIndex(static_cast(number)); - if (player == -1) - { - return; } - dropdownWidget = widget - 1; +#pragma region Overview - numItems = NetworkGetNumGroups(); - - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], - 0, 0, numItems, widget->right - dropdownWidget->left); - - for (i = 0; i < NetworkGetNumGroups(); i++) + void OnResizeOverview() { - gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; - gDropdownItems[i].Args = reinterpret_cast(NetworkGetGroupName(i)); + WindowSetResize(*this, 240, 170, 500, 300); } - Dropdown::SetChecked(NetworkGetGroupIndex(NetworkGetPlayerGroup(player)), true); - } + void OnUpdateOverview() + { + frame_no++; + InvalidateWidget(WIDX_TAB_1 + page); + + if (NetworkGetPlayerIndex(static_cast(number)) == -1) + { + Close(); + return; + } + + // Update viewport + bool scroll = true; + + // Use this spare window field for rotation check + if (_previousRotation != GetCurrentRotation()) + { + _previousRotation = GetCurrentRotation(); + scroll = false; + } + UpdateViewport(scroll); + } + + void OnPrepareDrawOverview() + { + int32_t playerIndex = NetworkGetPlayerIndex(static_cast(number)); + if (playerIndex == -1) + { + return; + } + + if (window_player_page_widgets[page] != widgets) + { + widgets = window_player_page_widgets[page]; + InitScrollWidgets(); + } + + pressed_widgets &= ~(WIDX_TAB_1); + pressed_widgets &= ~(WIDX_TAB_2); + pressed_widgets |= 1uLL << (page + WIDX_TAB_1); + + UpdateTitle(); + + ResizeFrameWithPage(); + widgets[WIDX_LOCATE].right = width - 2; + widgets[WIDX_LOCATE].left = width - 25; + widgets[WIDX_KICK].right = width - 2; + widgets[WIDX_KICK].left = width - 25; + widgets[WIDX_VIEWPORT].right = width - 26; + widgets[WIDX_VIEWPORT].bottom = height - 14; + + int32_t groupDropdownWidth = widgets[WIDX_GROUP].width(); + widgets[WIDX_GROUP].left = (width - groupDropdownWidth) / 2; + widgets[WIDX_GROUP].right = widgets[WIDX_GROUP].left + groupDropdownWidth; + widgets[WIDX_GROUP_DROPDOWN].left = widgets[WIDX_GROUP].right - 10; + widgets[WIDX_GROUP_DROPDOWN].right = widgets[WIDX_GROUP].right; + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_2); + + if (viewport != nullptr) + { + Widget* viewportWidget = &window_player_overview_widgets[WIDX_VIEWPORT]; + + viewport->pos = windowPos + ScreenCoordsXY{ viewportWidget->left, viewportWidget->top }; + viewport->width = viewportWidget->width(); + viewport->height = viewportWidget->height(); + viewport->view_width = viewport->zoom.ApplyTo(viewport->width); + viewport->view_height = viewport->zoom.ApplyTo(viewport->height); + } + + // Only enable kick button for other players + const bool canKick = NetworkCanPerformAction(NetworkGetCurrentPlayerGroupIndex(), NetworkPermission::KickPlayer); + const bool isServer = NetworkGetPlayerFlags(playerIndex) & NETWORK_PLAYER_FLAG_ISSERVER; + const bool isOwnWindow = (NetworkGetCurrentPlayerId() == number); + WidgetSetEnabled(*this, WIDX_KICK, canKick && !isOwnWindow && !isServer); + } + + void OnDrawOverview(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + int32_t player = NetworkGetPlayerIndex(static_cast(number)); + if (player == -1) + { + return; + } + + // Draw current group + int32_t groupindex = NetworkGetGroupIndex(NetworkGetPlayerGroup(player)); + if (groupindex != -1) + { + Widget* widget = &window_player_overview_widgets[WIDX_GROUP]; + + thread_local std::string _buffer; + _buffer.assign("{WINDOW_COLOUR_2}"); + _buffer += NetworkGetGroupName(groupindex); + auto ft = Formatter(); + ft.Add(_buffer.c_str()); + + DrawTextEllipsised( + dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, + { TextAlignment::CENTRE }); + } + + // Draw ping + auto screenCoords = windowPos + ScreenCoordsXY{ 90, 24 }; + + auto ft = Formatter(); + ft.Add(STR_PING); + DrawTextBasic(dpi, screenCoords, STR_WINDOW_COLOUR_2_STRINGID, ft); + char ping[64]; + snprintf(ping, 64, "%d ms", NetworkGetPlayerPing(player)); + GfxDrawString(dpi, screenCoords + ScreenCoordsXY(30, 0), ping, { colours[2] }); + + // Draw last action + screenCoords = windowPos + ScreenCoordsXY{ width / 2, height - 13 }; + int32_t updatedWidth = this->width - 8; + int32_t lastaction = NetworkGetPlayerLastAction(player, 0); + ft = Formatter(); + if (lastaction != -999) + { + ft.Add(NetworkGetActionNameStringID(lastaction)); + } + else + { + ft.Add(STR_ACTION_NA); + } + DrawTextEllipsised(dpi, screenCoords, updatedWidth, STR_LAST_ACTION_RAN, ft, { TextAlignment::CENTRE }); + + if (viewport != nullptr && _drawViewport) + { + WindowDrawViewport(dpi, *this); + } + } + + void OnMouseDownOverview(WidgetIndex widgetIndex) + { + auto* widget = &widgets[widgetIndex]; + switch (widgetIndex) + { + case WIDX_GROUP_DROPDOWN: + ShowGroupDropdownOverview(widget); + break; + } + } + + void OnMouseUpOverview(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_LOCATE: + { + WindowBase* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + { + int32_t player = NetworkGetPlayerIndex(static_cast(number)); + if (player == -1) + { + return; + } + auto coord = NetworkGetPlayerLastActionCoord(player); + if (coord.x || coord.y || coord.z) + { + WindowScrollToLocation(*mainWindow, coord); + } + } + } + break; + case WIDX_KICK: + { + auto kickPlayerAction = PlayerKickAction(number); + GameActions::Execute(&kickPlayerAction); + } + break; + } + } + + void OnDropdownOverview(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + const auto playerId = static_cast(number); + const auto playerIdx = NetworkGetPlayerIndex(playerId); + if (playerIdx == -1) + { + return; + } + if (dropdownIndex == -1) + { + return; + } + const auto groupId = NetworkGetGroupID(dropdownIndex); + const auto windowHandle = std::make_pair(classification, number); + auto playerSetGroupAction = PlayerSetGroupAction(playerId, groupId); + playerSetGroupAction.SetCallback([windowHandle](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) + { + WindowInvalidateByNumber(windowHandle.first, windowHandle.second); + } + }); + GameActions::Execute(&playerSetGroupAction); + } + + void ShowGroupDropdownOverview(Widget* widget) + { + Widget* dropdownWidget; + int32_t numItems, i; + int32_t player = NetworkGetPlayerIndex(static_cast(number)); + if (player == -1) + { + return; + } + + dropdownWidget = widget - 1; + + numItems = NetworkGetNumGroups(); + + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, 0, numItems, widget->right - dropdownWidget->left); + + for (i = 0; i < NetworkGetNumGroups(); i++) + { + gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; + gDropdownItems[i].Args = reinterpret_cast(NetworkGetGroupName(i)); + } + + Dropdown::SetChecked(NetworkGetGroupIndex(NetworkGetPlayerGroup(player)), true); + } #pragma endregion #pragma region Statistics - void OnResizeStatistics() - { - WindowSetResize(*this, 210, 80, 210, 80); - } - - void OnUpdateStatistics() - { - frame_no++; - InvalidateWidget(WIDX_TAB_1 + page); - - if (NetworkGetPlayerIndex(static_cast(number)) == -1) + void OnResizeStatistics() { - Close(); - } - } - - void OnPrepareDrawStatistics() - { - if (window_player_page_widgets[page] != widgets) - { - widgets = window_player_page_widgets[page]; - InitScrollWidgets(); + WindowSetResize(*this, 210, 80, 210, 80); } - pressed_widgets &= ~(WIDX_TAB_1); - pressed_widgets &= ~(WIDX_TAB_2); - pressed_widgets |= 1uLL << (page + WIDX_TAB_1); - - UpdateTitle(); - - ResizeFrameWithPage(); - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_2); - } - - void OnDrawStatistics(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - int32_t player = NetworkGetPlayerIndex(static_cast(number)); - if (player == -1) + void OnUpdateStatistics() { - return; + frame_no++; + InvalidateWidget(WIDX_TAB_1 + page); + + if (NetworkGetPlayerIndex(static_cast(number)) == -1) + { + Close(); + } } - auto screenCoords = windowPos - + ScreenCoordsXY{ window_player_overview_widgets[WIDX_PAGE_BACKGROUND].left + 4, - window_player_overview_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + void OnPrepareDrawStatistics() + { + if (window_player_page_widgets[page] != widgets) + { + widgets = window_player_page_widgets[page]; + InitScrollWidgets(); + } - auto ft = Formatter(); - ft.Add(NetworkGetPlayerCommandsRan(player)); - DrawTextBasic(dpi, screenCoords, STR_COMMANDS_RAN, ft); + pressed_widgets &= ~(WIDX_TAB_1); + pressed_widgets &= ~(WIDX_TAB_2); + pressed_widgets |= 1uLL << (page + WIDX_TAB_1); - screenCoords.y += LIST_ROW_HEIGHT; + UpdateTitle(); - ft = Formatter(); - ft.Add(NetworkGetPlayerMoneySpent(player)); - DrawTextBasic(dpi, screenCoords, STR_MONEY_SPENT, ft); - } + ResizeFrameWithPage(); + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_2); + } + + void OnDrawStatistics(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + int32_t player = NetworkGetPlayerIndex(static_cast(number)); + if (player == -1) + { + return; + } + + auto screenCoords = windowPos + + ScreenCoordsXY{ window_player_overview_widgets[WIDX_PAGE_BACKGROUND].left + 4, + window_player_overview_widgets[WIDX_PAGE_BACKGROUND].top + 4 }; + + auto ft = Formatter(); + ft.Add(NetworkGetPlayerCommandsRan(player)); + DrawTextBasic(dpi, screenCoords, STR_COMMANDS_RAN, ft); + + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(NetworkGetPlayerMoneySpent(player)); + DrawTextBasic(dpi, screenCoords, STR_MONEY_SPENT, ft); + } #pragma endregion -}; + }; -WindowBase* WindowPlayerOpen(uint8_t id) -{ - auto* window = static_cast(WindowBringToFrontByNumber(WindowClass::Player, id)); - if (window == nullptr) + WindowBase* WindowPlayerOpen(uint8_t id) { - window = WindowCreate(WindowClass::Player, 240, 170, WF_RESIZABLE); + auto* window = static_cast(WindowBringToFrontByNumber(WindowClass::Player, id)); + if (window == nullptr) + { + window = WindowCreate(WindowClass::Player, 240, 170, WF_RESIZABLE); + } + + window->Init(id); + + return window; } - - window->Init(id); - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/RefurbishRidePrompt.cpp b/src/openrct2-ui/windows/RefurbishRidePrompt.cpp index a98dca1d61..a25a3edb70 100644 --- a/src/openrct2-ui/windows/RefurbishRidePrompt.cpp +++ b/src/openrct2-ui/windows/RefurbishRidePrompt.cpp @@ -18,12 +18,12 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr int32_t WW = 200; + static constexpr int32_t WH = 100; -static constexpr int32_t WW = 200; -static constexpr int32_t WH = 100; - -// clang-format off + // clang-format off enum WindowRideRefurbishWidgetIdx { WIDX_BACKGROUND, @@ -40,85 +40,87 @@ static Widget window_ride_refurbish_widgets[] = MakeWidget({ WW - 95, WH - 22 }, { 85, 14 }, WindowWidgetType::Button, WindowColour::Primary, STR_SAVE_PROMPT_CANCEL), kWidgetsEnd, }; -// clang-format on + // clang-format on -class RefurbishRidePromptWindow final : public Window -{ - money64 _demolishRideCost; - -public: - void SetRide(const Ride& currentRide) + class RefurbishRidePromptWindow final : public Window { - rideId = currentRide.id; - _demolishRideCost = -RideGetRefundPrice(currentRide); - } + money64 _demolishRideCost; - void OnOpen() override - { - widgets = window_ride_refurbish_widgets; - WindowInitScrollWidgets(*this); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void SetRide(const Ride& currentRide) { - case WIDX_REFURBISH: + rideId = currentRide.id; + _demolishRideCost = -RideGetRefundPrice(currentRide); + } + + void OnOpen() override + { + widgets = window_ride_refurbish_widgets; + WindowInitScrollWidgets(*this); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) { - auto gameAction = RideDemolishAction(rideId, RIDE_MODIFY_RENEW); - GameActions::Execute(&gameAction); - break; + case WIDX_REFURBISH: + { + auto gameAction = RideDemolishAction(rideId, RIDE_MODIFY_RENEW); + GameActions::Execute(&gameAction); + break; + } + case WIDX_CANCEL: + case WIDX_CLOSE: + Close(); + break; } - case WIDX_CANCEL: - case WIDX_CLOSE: - Close(); - break; } - } - void OnDraw(DrawPixelInfo& dpi) override - { - WindowDrawWidgets(*this, dpi); - - auto currentRide = GetRide(rideId); - if (currentRide != nullptr) + void OnDraw(DrawPixelInfo& dpi) override { - auto stringId = (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) ? STR_REFURBISH_RIDE_ID_NO_MONEY - : STR_REFURBISH_RIDE_ID_MONEY; - auto ft = Formatter(); - currentRide->FormatNameTo(ft); - ft.Add(_demolishRideCost / 2); + WindowDrawWidgets(*this, dpi); - ScreenCoordsXY stringCoords(windowPos.x + WW / 2, windowPos.y + (WH / 2) - 3); - DrawTextWrapped(dpi, stringCoords, WW - 4, stringId, ft, { TextAlignment::CENTRE }); + auto currentRide = GetRide(rideId); + if (currentRide != nullptr) + { + auto stringId = (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) ? STR_REFURBISH_RIDE_ID_NO_MONEY + : STR_REFURBISH_RIDE_ID_MONEY; + auto ft = Formatter(); + currentRide->FormatNameTo(ft); + ft.Add(_demolishRideCost / 2); + + ScreenCoordsXY stringCoords(windowPos.x + WW / 2, windowPos.y + (WH / 2) - 3); + DrawTextWrapped(dpi, stringCoords, WW - 4, stringId, ft, { TextAlignment::CENTRE }); + } } - } - void OnResize() override + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowRideRefurbishPromptOpen(const Ride& ride) { - ResizeFrame(); + WindowBase* w; + RefurbishRidePromptWindow* newWindow; + + w = WindowFindByClass(WindowClass::DemolishRidePrompt); + if (w != nullptr) + { + auto windowPos = w->windowPos; + WindowClose(*w); + newWindow = WindowCreate( + WindowClass::DemolishRidePrompt, windowPos, WW, WH, WF_TRANSPARENT); + } + else + { + newWindow = WindowCreate( + WindowClass::DemolishRidePrompt, WW, WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); + } + + newWindow->SetRide(ride); + + return newWindow; } -}; - -WindowBase* WindowRideRefurbishPromptOpen(const Ride& ride) -{ - WindowBase* w; - RefurbishRidePromptWindow* newWindow; - - w = WindowFindByClass(WindowClass::DemolishRidePrompt); - if (w != nullptr) - { - auto windowPos = w->windowPos; - WindowClose(*w); - newWindow = WindowCreate(WindowClass::DemolishRidePrompt, windowPos, WW, WH, WF_TRANSPARENT); - } - else - { - newWindow = WindowCreate( - WindowClass::DemolishRidePrompt, WW, WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); - } - - newWindow->SetRide(ride); - - return newWindow; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Research.cpp b/src/openrct2-ui/windows/Research.cpp index 7ae443680a..02a254bb8d 100644 --- a/src/openrct2-ui/windows/Research.cpp +++ b/src/openrct2-ui/windows/Research.cpp @@ -24,14 +24,14 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr int32_t WH_DEVELOPMENT = 196; + static constexpr int32_t WW_DEVELOPMENT = 300; + static constexpr int32_t WH_FUNDING = 207; + static constexpr int32_t WW_FUNDING = 320; -static constexpr int32_t WH_DEVELOPMENT = 196; -static constexpr int32_t WW_DEVELOPMENT = 300; -static constexpr int32_t WH_FUNDING = 207; -static constexpr int32_t WW_FUNDING = 320; - -// clang-format off + // clang-format off enum { WINDOW_RESEARCH_PAGE_DEVELOPMENT, WINDOW_RESEARCH_PAGE_FUNDING, @@ -102,502 +102,504 @@ static Widget *window_research_page_widgets[] = { #pragma endregion -// clang-format on + // clang-format on -const int32_t window_research_tab_animation_loops[] = { - 16, - 16, -}; + const int32_t window_research_tab_animation_loops[] = { + 16, + 16, + }; -static constexpr StringId ResearchStageNames[] = { - STR_RESEARCH_STAGE_INITIAL_RESEARCH, - STR_RESEARCH_STAGE_DESIGNING, - STR_RESEARCH_STAGE_COMPLETING_DESIGN, - STR_RESEARCH_STAGE_UNKNOWN, -}; + static constexpr StringId ResearchStageNames[] = { + STR_RESEARCH_STAGE_INITIAL_RESEARCH, + STR_RESEARCH_STAGE_DESIGNING, + STR_RESEARCH_STAGE_COMPLETING_DESIGN, + STR_RESEARCH_STAGE_UNKNOWN, + }; -class ResearchWindow final : public Window -{ -public: - void OnOpen() override + class ResearchWindow final : public Window { - widgets = window_research_page_widgets[WINDOW_RESEARCH_PAGE_DEVELOPMENT]; - width = WW_DEVELOPMENT; - height = WH_DEVELOPMENT; - ResearchUpdateUncompletedTypes(); - } - - void SetPage(int32_t newPageIndex) - { - page = newPageIndex; - frame_no = 0; - RemoveViewport(); - - hold_down_widgets = 0; - widgets = window_research_page_widgets[newPageIndex]; - disabled_widgets = 0; - pressed_widgets = 0; - - Invalidate(); - if (newPageIndex == WINDOW_RESEARCH_PAGE_DEVELOPMENT) + public: + void OnOpen() override { + widgets = window_research_page_widgets[WINDOW_RESEARCH_PAGE_DEVELOPMENT]; width = WW_DEVELOPMENT; height = WH_DEVELOPMENT; + ResearchUpdateUncompletedTypes(); } - else + + void SetPage(int32_t newPageIndex) { - width = WW_FUNDING; - height = WH_FUNDING; - } - OnResize(); - - InitScrollWidgets(); - Invalidate(); - } - -private: - void OnUpdate() override - { - // Tab animation - if (++frame_no >= window_research_tab_animation_loops[page]) + page = newPageIndex; frame_no = 0; + RemoveViewport(); - switch (page) - { - case WINDOW_RESEARCH_PAGE_DEVELOPMENT: + hold_down_widgets = 0; + widgets = window_research_page_widgets[newPageIndex]; + disabled_widgets = 0; + pressed_widgets = 0; + + Invalidate(); + if (newPageIndex == WINDOW_RESEARCH_PAGE_DEVELOPMENT) { - InvalidateWidget(WIDX_TAB_1); - break; + width = WW_DEVELOPMENT; + height = WH_DEVELOPMENT; } - case WINDOW_RESEARCH_PAGE_FUNDING: + else { - InvalidateWidget(WIDX_TAB_2); - break; + width = WW_FUNDING; + height = WH_FUNDING; } - } - } + OnResize(); - void OnMouseDown(WidgetIndex widgetIndex) override - { - if (page == WINDOW_RESEARCH_PAGE_FUNDING) - { - WindowResearchFundingMouseDown(this, widgetIndex, WIDX_RESEARCH_FUNDING); - } - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - // Switch tab or close - switch (widgetIndex) - { - case WIDX_CLOSE: - { - Close(); - break; - } - case WIDX_TAB_1: - case WIDX_TAB_2: - { - SetPage(widgetIndex - WIDX_TAB_1); - break; - } - } - - // Process mouse up for specific tab - switch (page) - { - case WINDOW_RESEARCH_PAGE_DEVELOPMENT: - { - WindowResearchDevelopmentMouseUp(widgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - break; - } - case WINDOW_RESEARCH_PAGE_FUNDING: - { - WindowResearchFundingMouseUp(widgetIndex, WIDX_RESEARCH_FUNDING); - break; - } - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (page == WINDOW_RESEARCH_PAGE_FUNDING) - { - WindowResearchFundingDropdown(widgetIndex, selectedIndex, WIDX_RESEARCH_FUNDING); - } - } - - void OnPrepareDraw() override - { - auto* targetWidgets = window_research_page_widgets[page]; - - if (widgets != targetWidgets) - { - widgets = targetWidgets; InitScrollWidgets(); + Invalidate(); } - for (auto i = 0; i < WINDOW_RESEARCH_PAGE_COUNT; i++) + private: + void OnUpdate() override { - SetWidgetPressed(WIDX_TAB_1 + i, false); + // Tab animation + if (++frame_no >= window_research_tab_animation_loops[page]) + frame_no = 0; + + switch (page) + { + case WINDOW_RESEARCH_PAGE_DEVELOPMENT: + { + InvalidateWidget(WIDX_TAB_1); + break; + } + case WINDOW_RESEARCH_PAGE_FUNDING: + { + InvalidateWidget(WIDX_TAB_2); + break; + } + } } - SetWidgetPressed(WIDX_TAB_1 + page, true); - - switch (page) + void OnMouseDown(WidgetIndex widgetIndex) override { - case WINDOW_RESEARCH_PAGE_DEVELOPMENT: + if (page == WINDOW_RESEARCH_PAGE_FUNDING) { - WindowResearchDevelopmentPrepareDraw(this, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - break; - } - case WINDOW_RESEARCH_PAGE_FUNDING: - { - WindowResearchFundingPrepareDraw(this, WIDX_RESEARCH_FUNDING); - break; + WindowResearchFundingMouseDown(this, widgetIndex, WIDX_RESEARCH_FUNDING); } } - } - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - switch (page) + void OnMouseUp(WidgetIndex widgetIndex) override { - case WINDOW_RESEARCH_PAGE_DEVELOPMENT: + // Switch tab or close + switch (widgetIndex) { - WindowResearchDevelopmentDraw(this, dpi, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - break; + case WIDX_CLOSE: + { + Close(); + break; + } + case WIDX_TAB_1: + case WIDX_TAB_2: + { + SetPage(widgetIndex - WIDX_TAB_1); + break; + } } - case WINDOW_RESEARCH_PAGE_FUNDING: + + // Process mouse up for specific tab + switch (page) { - WindowResearchFundingDraw(this, dpi); - break; + case WINDOW_RESEARCH_PAGE_DEVELOPMENT: + { + WindowResearchDevelopmentMouseUp(widgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); + break; + } + case WINDOW_RESEARCH_PAGE_FUNDING: + { + WindowResearchFundingMouseUp(widgetIndex, WIDX_RESEARCH_FUNDING); + break; + } } } - } - void OnResize() override - { - ResizeFrameWithPage(); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t tabPage, int32_t spriteIndex) - { - WidgetIndex widgetIndex = WIDX_TAB_1 + tabPage; - - if (!IsWidgetDisabled(widgetIndex)) + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override { - if (page == tabPage) + if (page == WINDOW_RESEARCH_PAGE_FUNDING) { - int32_t frame = frame_no / 2; - if (tabPage == WINDOW_RESEARCH_PAGE_DEVELOPMENT) - frame %= 8; - spriteIndex += frame; + WindowResearchFundingDropdown(widgetIndex, selectedIndex, WIDX_RESEARCH_FUNDING); + } + } + + void OnPrepareDraw() override + { + auto* targetWidgets = window_research_page_widgets[page]; + + if (widgets != targetWidgets) + { + widgets = targetWidgets; + InitScrollWidgets(); } - GfxDrawSprite( - dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + for (auto i = 0; i < WINDOW_RESEARCH_PAGE_COUNT; i++) + { + SetWidgetPressed(WIDX_TAB_1 + i, false); + } + + SetWidgetPressed(WIDX_TAB_1 + page, true); + + switch (page) + { + case WINDOW_RESEARCH_PAGE_DEVELOPMENT: + { + WindowResearchDevelopmentPrepareDraw(this, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); + break; + } + case WINDOW_RESEARCH_PAGE_FUNDING: + { + WindowResearchFundingPrepareDraw(this, WIDX_RESEARCH_FUNDING); + break; + } + } } - } - void DrawTabImages(DrawPixelInfo& dpi) + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + switch (page) + { + case WINDOW_RESEARCH_PAGE_DEVELOPMENT: + { + WindowResearchDevelopmentDraw(this, dpi, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); + break; + } + case WINDOW_RESEARCH_PAGE_FUNDING: + { + WindowResearchFundingDraw(this, dpi); + break; + } + } + } + + void OnResize() override + { + ResizeFrameWithPage(); + } + + void DrawTabImage(DrawPixelInfo& dpi, int32_t tabPage, int32_t spriteIndex) + { + WidgetIndex widgetIndex = WIDX_TAB_1 + tabPage; + + if (!IsWidgetDisabled(widgetIndex)) + { + if (page == tabPage) + { + int32_t frame = frame_no / 2; + if (tabPage == WINDOW_RESEARCH_PAGE_DEVELOPMENT) + frame %= 8; + spriteIndex += frame; + } + + GfxDrawSprite( + dpi, ImageId(spriteIndex), + windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); + } + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + DrawTabImage(dpi, WINDOW_RESEARCH_PAGE_DEVELOPMENT, SPR_TAB_FINANCES_RESEARCH_0); + DrawTabImage(dpi, WINDOW_RESEARCH_PAGE_FUNDING, SPR_TAB_FINANCES_SUMMARY_0); + } + }; + + WindowBase* WindowResearchOpen() { - DrawTabImage(dpi, WINDOW_RESEARCH_PAGE_DEVELOPMENT, SPR_TAB_FINANCES_RESEARCH_0); - DrawTabImage(dpi, WINDOW_RESEARCH_PAGE_FUNDING, SPR_TAB_FINANCES_SUMMARY_0); + ResearchWindow* window = WindowFocusOrCreate(WindowClass::Research, WW_FUNDING, WH_FUNDING, WF_10); + window->SetPage(WINDOW_RESEARCH_PAGE_DEVELOPMENT); + return window; } -}; -WindowBase* WindowResearchOpen() -{ - ResearchWindow* window = WindowFocusOrCreate(WindowClass::Research, WW_FUNDING, WH_FUNDING, WF_10); - window->SetPage(WINDOW_RESEARCH_PAGE_DEVELOPMENT); - return window; -} - -static WidgetIndex GetWidgetIndexOffset(WidgetIndex baseWidgetIndex, WidgetIndex currentPageWidgetIndex) -{ - // Other windows that reuse the logic here will have different values for the widget enums, but they otherwise align to - // those in this class. Therefore, they can be referenced relative to the widget index for the page in this class, using - // the difference between them as an offset. - return baseWidgetIndex - currentPageWidgetIndex; -} + static WidgetIndex GetWidgetIndexOffset(WidgetIndex baseWidgetIndex, WidgetIndex currentPageWidgetIndex) + { + // Other windows that reuse the logic here will have different values for the widget enums, but they otherwise align to + // those in this class. Therefore, they can be referenced relative to the widget index for the page in this class, using + // the difference between them as an offset. + return baseWidgetIndex - currentPageWidgetIndex; + } #pragma region Development page -void WindowResearchDevelopmentMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex) -{ - const auto& gameState = GetGameState(); - auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - - if (widgetIndex == (WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset)) + void WindowResearchDevelopmentMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex) { - News::OpenSubject(News::ItemType::Research, gameState.ResearchLastItem->rawValue); - } -} + const auto& gameState = GetGameState(); + auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); -void WindowResearchDevelopmentPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex) -{ - const auto& gameState = GetGameState(); - // Offset the widget index to allow reuse from other windows - auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - w->widgets[WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset].type = WindowWidgetType::Empty; - - // Display button to link to the last development, if there is one - if (gameState.ResearchLastItem.has_value()) - { - auto type = gameState.ResearchLastItem->type; - w->widgets[WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset].type = WindowWidgetType::FlatBtn; - const auto image = type == Research::EntryType::Ride ? SPR_NEW_RIDE : SPR_NEW_SCENERY; - w->widgets[WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset].image = ImageId(image); - } -} - -void WindowResearchDevelopmentDraw(WindowBase* w, DrawPixelInfo& dpi, WidgetIndex baseWidgetIndex) -{ - const auto& gameState = GetGameState(); - - auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); - auto screenCoords = w->windowPos - + ScreenCoordsXY{ 10, w->widgets[WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP + widgetOffset].top + 12 }; - - if (gameState.ResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL) - { - // Research type - auto ft = Formatter(); - ft.Add(STR_RESEARCH_UNKNOWN); - DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_TYPE_LABEL, ft); - screenCoords.y += 25; - - // Progress - ft = Formatter(); - ft.Add(STR_RESEARCH_COMPLETED_AL); - DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_PROGRESS_LABEL, ft); - screenCoords.y += 15; - - // Expected - ft = Formatter(); - ft.Add(STR_RESEARCH_STAGE_UNKNOWN); - DrawTextBasic(dpi, screenCoords, STR_RESEARCH_EXPECTED_LABEL, ft); - } - else - { - // Research type - auto ft = Formatter(); - StringId label = STR_RESEARCH_TYPE_LABEL; - if (gameState.ResearchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH) + if (widgetIndex == (WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset)) { + News::OpenSubject(News::ItemType::Research, gameState.ResearchLastItem->rawValue); + } + } + + void WindowResearchDevelopmentPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex) + { + const auto& gameState = GetGameState(); + // Offset the widget index to allow reuse from other windows + auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); + w->widgets[WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset].type = WindowWidgetType::Empty; + + // Display button to link to the last development, if there is one + if (gameState.ResearchLastItem.has_value()) + { + auto type = gameState.ResearchLastItem->type; + w->widgets[WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset].type = WindowWidgetType::FlatBtn; + const auto image = type == Research::EntryType::Ride ? SPR_NEW_RIDE : SPR_NEW_SCENERY; + w->widgets[WIDX_LAST_DEVELOPMENT_BUTTON + widgetOffset].image = ImageId(image); + } + } + + void WindowResearchDevelopmentDraw(WindowBase* w, DrawPixelInfo& dpi, WidgetIndex baseWidgetIndex) + { + const auto& gameState = GetGameState(); + + auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP); + auto screenCoords = w->windowPos + + ScreenCoordsXY{ 10, w->widgets[WIDX_CURRENTLY_IN_DEVELOPMENT_GROUP + widgetOffset].top + 12 }; + + if (gameState.ResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL) + { + // Research type + auto ft = Formatter(); ft.Add(STR_RESEARCH_UNKNOWN); - } - else if (gameState.ResearchProgressStage == RESEARCH_STAGE_DESIGNING) - { - ft.Add(gameState.ResearchNextItem->GetCategoryName()); - } - else if (gameState.ResearchNextItem->type == Research::EntryType::Ride) - { - const auto& rtd = GetRideTypeDescriptor(gameState.ResearchNextItem->baseRideType); - if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) - { - ft.Add(gameState.ResearchNextItem->GetName()); - } - else if (gameState.ResearchNextItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE) - { - ft.Add(rtd.Naming.Name); - } - else - { - ft.Add(gameState.ResearchNextItem->GetName()); - ft.Add(rtd.Naming.Name); - label = STR_RESEARCH_TYPE_LABEL_VEHICLE; - } - } - else - { - ft.Add(gameState.ResearchNextItem->GetName()); - } - DrawTextWrapped(dpi, screenCoords, 296, label, ft); - screenCoords.y += 25; + DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_TYPE_LABEL, ft); + screenCoords.y += 25; - // Progress - ft = Formatter(); - ft.Add(ResearchStageNames[gameState.ResearchProgressStage]); - DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_PROGRESS_LABEL, ft); - screenCoords.y += 15; + // Progress + ft = Formatter(); + ft.Add(STR_RESEARCH_COMPLETED_AL); + DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_PROGRESS_LABEL, ft); + screenCoords.y += 15; - // Expected - ft = Formatter(); - if (gameState.ResearchProgressStage != RESEARCH_STAGE_INITIAL_RESEARCH && gameState.ResearchExpectedDay != 255) - { - // TODO: Should probably use game date format setting - ft.Add(STR_RESEARCH_EXPECTED_FORMAT); - ft.Add(DateDayNames[gameState.ResearchExpectedDay]); - ft.Add(DateGameMonthNames[gameState.ResearchExpectedMonth]); - } - else - { + // Expected + ft = Formatter(); ft.Add(STR_RESEARCH_STAGE_UNKNOWN); - } - DrawTextBasic(dpi, screenCoords, STR_RESEARCH_EXPECTED_LABEL, ft); - } - - // Last development - screenCoords = w->windowPos + ScreenCoordsXY{ 10, w->widgets[WIDX_LAST_DEVELOPMENT_GROUP + widgetOffset].top + 12 }; - - if (gameState.ResearchLastItem.has_value()) - { - StringId lastDevelopmentFormat = STR_EMPTY; - auto ft = Formatter(); - if (gameState.ResearchLastItem->type == Research::EntryType::Scenery) - { - lastDevelopmentFormat = STR_RESEARCH_SCENERY_LABEL; - ft.Add(gameState.ResearchLastItem->GetName()); + DrawTextBasic(dpi, screenCoords, STR_RESEARCH_EXPECTED_LABEL, ft); } else { - lastDevelopmentFormat = STR_RESEARCH_RIDE_LABEL; - const auto& rtd = GetRideTypeDescriptor(gameState.ResearchLastItem->baseRideType); - if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + // Research type + auto ft = Formatter(); + StringId label = STR_RESEARCH_TYPE_LABEL; + if (gameState.ResearchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH) { - ft.Add(gameState.ResearchLastItem->GetName()); + ft.Add(STR_RESEARCH_UNKNOWN); } - else if (gameState.ResearchLastItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE) + else if (gameState.ResearchProgressStage == RESEARCH_STAGE_DESIGNING) { - ft.Add(rtd.Naming.Name); + ft.Add(gameState.ResearchNextItem->GetCategoryName()); + } + else if (gameState.ResearchNextItem->type == Research::EntryType::Ride) + { + const auto& rtd = GetRideTypeDescriptor(gameState.ResearchNextItem->baseRideType); + if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + ft.Add(gameState.ResearchNextItem->GetName()); + } + else if (gameState.ResearchNextItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE) + { + ft.Add(rtd.Naming.Name); + } + else + { + ft.Add(gameState.ResearchNextItem->GetName()); + ft.Add(rtd.Naming.Name); + label = STR_RESEARCH_TYPE_LABEL_VEHICLE; + } } else { - ft.Add(gameState.ResearchLastItem->GetName()); - ft.Add(rtd.Naming.Name); - lastDevelopmentFormat = STR_RESEARCH_VEHICLE_LABEL; + ft.Add(gameState.ResearchNextItem->GetName()); } + DrawTextWrapped(dpi, screenCoords, 296, label, ft); + screenCoords.y += 25; + + // Progress + ft = Formatter(); + ft.Add(ResearchStageNames[gameState.ResearchProgressStage]); + DrawTextWrapped(dpi, screenCoords, 296, STR_RESEARCH_PROGRESS_LABEL, ft); + screenCoords.y += 15; + + // Expected + ft = Formatter(); + if (gameState.ResearchProgressStage != RESEARCH_STAGE_INITIAL_RESEARCH && gameState.ResearchExpectedDay != 255) + { + // TODO: Should probably use game date format setting + ft.Add(STR_RESEARCH_EXPECTED_FORMAT); + ft.Add(DateDayNames[gameState.ResearchExpectedDay]); + ft.Add(DateGameMonthNames[gameState.ResearchExpectedMonth]); + } + else + { + ft.Add(STR_RESEARCH_STAGE_UNKNOWN); + } + DrawTextBasic(dpi, screenCoords, STR_RESEARCH_EXPECTED_LABEL, ft); } - DrawTextWrapped(dpi, screenCoords, 266, lastDevelopmentFormat, ft); + // Last development + screenCoords = w->windowPos + ScreenCoordsXY{ 10, w->widgets[WIDX_LAST_DEVELOPMENT_GROUP + widgetOffset].top + 12 }; + + if (gameState.ResearchLastItem.has_value()) + { + StringId lastDevelopmentFormat = STR_EMPTY; + auto ft = Formatter(); + if (gameState.ResearchLastItem->type == Research::EntryType::Scenery) + { + lastDevelopmentFormat = STR_RESEARCH_SCENERY_LABEL; + ft.Add(gameState.ResearchLastItem->GetName()); + } + else + { + lastDevelopmentFormat = STR_RESEARCH_RIDE_LABEL; + const auto& rtd = GetRideTypeDescriptor(gameState.ResearchLastItem->baseRideType); + if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + ft.Add(gameState.ResearchLastItem->GetName()); + } + else if (gameState.ResearchLastItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE) + { + ft.Add(rtd.Naming.Name); + } + else + { + ft.Add(gameState.ResearchLastItem->GetName()); + ft.Add(rtd.Naming.Name); + lastDevelopmentFormat = STR_RESEARCH_VEHICLE_LABEL; + } + } + + DrawTextWrapped(dpi, screenCoords, 266, lastDevelopmentFormat, ft); + } } -} #pragma endregion #pragma region Funding page -void WindowResearchFundingMouseDown(WindowBase* w, WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex) -{ - const auto& gameState = GetGameState(); - auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); - - if (widgetIndex != (WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset)) - return; - - Widget* dropdownWidget = &w->widgets[widgetIndex - 1]; - - for (std::size_t i = 0; i < std::size(ResearchFundingLevelNames); i++) + void WindowResearchFundingMouseDown(WindowBase* w, WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex) { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = ResearchFundingLevelNames[i]; - } - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, Dropdown::Flag::StayOpen, 4, dropdownWidget->width() - 3); + const auto& gameState = GetGameState(); + auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); - int32_t currentResearchLevel = gameState.ResearchFundingLevel; - Dropdown::SetChecked(currentResearchLevel, true); -} + if (widgetIndex != (WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset)) + return; -void WindowResearchFundingMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex) -{ - const auto& gameState = GetGameState(); - auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); + Widget* dropdownWidget = &w->widgets[widgetIndex - 1]; - switch (widgetIndex - widgetOffset) - { - case WIDX_TRANSPORT_RIDES: - case WIDX_GENTLE_RIDES: - case WIDX_ROLLER_COASTERS: - case WIDX_THRILL_RIDES: - case WIDX_WATER_RIDES: - case WIDX_SHOPS_AND_STALLS: - case WIDX_SCENERY_AND_THEMING: + for (std::size_t i = 0; i < std::size(ResearchFundingLevelNames); i++) { - auto activeResearchTypes = gameState.ResearchPriorities; - activeResearchTypes ^= 1uLL << (widgetIndex - (WIDX_TRANSPORT_RIDES + widgetOffset)); - auto gameAction = ParkSetResearchFundingAction(activeResearchTypes, gameState.ResearchFundingLevel); - GameActions::Execute(&gameAction); - break; + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = ResearchFundingLevelNames[i]; + } + WindowDropdownShowTextCustomWidth( + { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + w->colours[1], 0, Dropdown::Flag::StayOpen, 4, dropdownWidget->width() - 3); + + int32_t currentResearchLevel = gameState.ResearchFundingLevel; + Dropdown::SetChecked(currentResearchLevel, true); + } + + void WindowResearchFundingMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex) + { + const auto& gameState = GetGameState(); + auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); + + switch (widgetIndex - widgetOffset) + { + case WIDX_TRANSPORT_RIDES: + case WIDX_GENTLE_RIDES: + case WIDX_ROLLER_COASTERS: + case WIDX_THRILL_RIDES: + case WIDX_WATER_RIDES: + case WIDX_SHOPS_AND_STALLS: + case WIDX_SCENERY_AND_THEMING: + { + auto activeResearchTypes = gameState.ResearchPriorities; + activeResearchTypes ^= 1uLL << (widgetIndex - (WIDX_TRANSPORT_RIDES + widgetOffset)); + auto gameAction = ParkSetResearchFundingAction(activeResearchTypes, gameState.ResearchFundingLevel); + GameActions::Execute(&gameAction); + break; + } } } -} -void WindowResearchFundingDropdown(WidgetIndex widgetIndex, int32_t selectedIndex, WidgetIndex baseWidgetIndex) -{ - const auto& gameState = GetGameState(); - auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); - - if (widgetIndex != (WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset) || selectedIndex == -1) - return; - - auto gameAction = ParkSetResearchFundingAction(gameState.ResearchPriorities, selectedIndex); - GameActions::Execute(&gameAction); -} - -void WindowResearchFundingPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex) -{ - const auto& gameState = GetGameState(); - auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); - - if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) || gameState.ResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL) + void WindowResearchFundingDropdown(WidgetIndex widgetIndex, int32_t selectedIndex, WidgetIndex baseWidgetIndex) { - w->widgets[WIDX_RESEARCH_FUNDING + widgetOffset].type = WindowWidgetType::Empty; - w->widgets[WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset].type = WindowWidgetType::Empty; - } - else - { - w->widgets[WIDX_RESEARCH_FUNDING + widgetOffset].type = WindowWidgetType::DropdownMenu; - w->widgets[WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset].type = WindowWidgetType::Button; + const auto& gameState = GetGameState(); + auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); + + if (widgetIndex != (WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset) || selectedIndex == -1) + return; + + auto gameAction = ParkSetResearchFundingAction(gameState.ResearchPriorities, selectedIndex); + GameActions::Execute(&gameAction); } - // Current funding - int32_t currentResearchLevel = gameState.ResearchFundingLevel; - w->widgets[WIDX_RESEARCH_FUNDING + widgetOffset].text = ResearchFundingLevelNames[currentResearchLevel]; - - // Checkboxes - uint8_t activeResearchTypes = gameState.ResearchPriorities; - int32_t uncompletedResearchTypes = gameState.ResearchUncompletedCategories; - for (int32_t i = 0; i < 7; i++) + void WindowResearchFundingPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex) { - int32_t mask = 1 << i; - int32_t widgetMask = 1uLL << (i + WIDX_TRANSPORT_RIDES + widgetOffset); + const auto& gameState = GetGameState(); + auto widgetOffset = GetWidgetIndexOffset(baseWidgetIndex, WIDX_RESEARCH_FUNDING); - // Set checkbox disabled if research type is complete - if (uncompletedResearchTypes & mask) + if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) || gameState.ResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL) { - w->disabled_widgets &= ~widgetMask; - - // Set checkbox ticked if research type is active - if (activeResearchTypes & mask) - w->pressed_widgets |= widgetMask; - else - w->pressed_widgets &= ~widgetMask; + w->widgets[WIDX_RESEARCH_FUNDING + widgetOffset].type = WindowWidgetType::Empty; + w->widgets[WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset].type = WindowWidgetType::Empty; } else { - w->disabled_widgets |= widgetMask; - w->pressed_widgets &= ~widgetMask; + w->widgets[WIDX_RESEARCH_FUNDING + widgetOffset].type = WindowWidgetType::DropdownMenu; + w->widgets[WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON + widgetOffset].type = WindowWidgetType::Button; + } + + // Current funding + int32_t currentResearchLevel = gameState.ResearchFundingLevel; + w->widgets[WIDX_RESEARCH_FUNDING + widgetOffset].text = ResearchFundingLevelNames[currentResearchLevel]; + + // Checkboxes + uint8_t activeResearchTypes = gameState.ResearchPriorities; + int32_t uncompletedResearchTypes = gameState.ResearchUncompletedCategories; + for (int32_t i = 0; i < 7; i++) + { + int32_t mask = 1 << i; + int32_t widgetMask = 1uLL << (i + WIDX_TRANSPORT_RIDES + widgetOffset); + + // Set checkbox disabled if research type is complete + if (uncompletedResearchTypes & mask) + { + w->disabled_widgets &= ~widgetMask; + + // Set checkbox ticked if research type is active + if (activeResearchTypes & mask) + w->pressed_widgets |= widgetMask; + else + w->pressed_widgets &= ~widgetMask; + } + else + { + w->disabled_widgets |= widgetMask; + w->pressed_widgets &= ~widgetMask; + } } } -} -void WindowResearchFundingDraw(WindowBase* w, DrawPixelInfo& dpi) -{ - if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) - return; + void WindowResearchFundingDraw(WindowBase* w, DrawPixelInfo& dpi) + { + if (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) + return; - const auto& gameState = GetGameState(); - int32_t currentResearchLevel = gameState.ResearchFundingLevel; - auto ft = Formatter(); - ft.Add(research_cost_table[currentResearchLevel]); - DrawTextBasic(dpi, w->windowPos + ScreenCoordsXY{ 10, 77 }, STR_RESEARCH_COST_PER_MONTH, ft); -} + const auto& gameState = GetGameState(); + int32_t currentResearchLevel = gameState.ResearchFundingLevel; + auto ft = Formatter(); + ft.Add(research_cost_table[currentResearchLevel]); + DrawTextBasic(dpi, w->windowPos + ScreenCoordsXY{ 10, 77 }, STR_RESEARCH_COST_PER_MONTH, ft); + } #pragma endregion +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index e55227c195..2d09cbdff7 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -67,31 +67,31 @@ #include #include -using namespace OpenRCT2; using namespace OpenRCT2::TrackMetaData; - -static constexpr StringId WINDOW_TITLE = STR_RIDE_WINDOW_TITLE; -static constexpr int32_t WH = 207; -static constexpr int32_t WW = 316; - -enum +namespace OpenRCT2::Ui::Windows { - WINDOW_RIDE_PAGE_MAIN, - WINDOW_RIDE_PAGE_VEHICLE, - WINDOW_RIDE_PAGE_OPERATING, - WINDOW_RIDE_PAGE_MAINTENANCE, - WINDOW_RIDE_PAGE_COLOUR, - WINDOW_RIDE_PAGE_MUSIC, - WINDOW_RIDE_PAGE_MEASUREMENTS, - WINDOW_RIDE_PAGE_GRAPHS, - WINDOW_RIDE_PAGE_INCOME, - WINDOW_RIDE_PAGE_CUSTOMER, - WINDOW_RIDE_PAGE_COUNT -}; + static constexpr StringId WINDOW_TITLE = STR_RIDE_WINDOW_TITLE; + static constexpr int32_t WH = 207; + static constexpr int32_t WW = 316; + + enum + { + WINDOW_RIDE_PAGE_MAIN, + WINDOW_RIDE_PAGE_VEHICLE, + WINDOW_RIDE_PAGE_OPERATING, + WINDOW_RIDE_PAGE_MAINTENANCE, + WINDOW_RIDE_PAGE_COLOUR, + WINDOW_RIDE_PAGE_MUSIC, + WINDOW_RIDE_PAGE_MEASUREMENTS, + WINDOW_RIDE_PAGE_GRAPHS, + WINDOW_RIDE_PAGE_INCOME, + WINDOW_RIDE_PAGE_CUSTOMER, + WINDOW_RIDE_PAGE_COUNT + }; #pragma region Widgets -// clang-format off + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -427,56 +427,56 @@ static constexpr std::array PageHoldDownWidgets = { 0uLL, }; static_assert(std::size(PageHoldDownWidgets) == WINDOW_RIDE_PAGE_COUNT); -// clang-format on + // clang-format on #pragma endregion -static void CancelScenerySelection(); + static void CancelScenerySelection(); -static bool _collectTrackDesignScenery = false; -static int32_t _lastSceneryX = 0; -static int32_t _lastSceneryY = 0; -static std::unique_ptr _trackDesign; + static bool _collectTrackDesignScenery = false; + static int32_t _lastSceneryX = 0; + static int32_t _lastSceneryY = 0; + static std::unique_ptr _trackDesign; -// Cached overall view for each ride -// (Re)calculated when the ride window is opened -struct RideOverallView -{ - CoordsXYZ loc; - ZoomLevel zoom; -}; + // Cached overall view for each ride + // (Re)calculated when the ride window is opened + struct RideOverallView + { + CoordsXYZ loc; + ZoomLevel zoom; + }; -static std::vector _rideOverallViewsCache = {}; + static std::vector _rideOverallViewsCache = {}; -static constexpr std::array PageTabAnimationDivisor = { - 0, // WINDOW_RIDE_PAGE_MAIN - 0, // WINDOW_RIDE_PAGE_VEHICLE - 2, // WINDOW_RIDE_PAGE_OPERATING - 2, // WINDOW_RIDE_PAGE_MAINTENANCE - 4, // WINDOW_RIDE_PAGE_COLOUR - 2, // WINDOW_RIDE_PAGE_MUSIC - 8, // WINDOW_RIDE_PAGE_MEASUREMENTS - 8, // WINDOW_RIDE_PAGE_GRAPHS - 2, // WINDOW_RIDE_PAGE_INCOME - 0, // WINDOW_RIDE_PAGE_CUSTOMER -}; -static_assert(std::size(PageTabAnimationDivisor) == WINDOW_RIDE_PAGE_COUNT); + static constexpr std::array PageTabAnimationDivisor = { + 0, // WINDOW_RIDE_PAGE_MAIN + 0, // WINDOW_RIDE_PAGE_VEHICLE + 2, // WINDOW_RIDE_PAGE_OPERATING + 2, // WINDOW_RIDE_PAGE_MAINTENANCE + 4, // WINDOW_RIDE_PAGE_COLOUR + 2, // WINDOW_RIDE_PAGE_MUSIC + 8, // WINDOW_RIDE_PAGE_MEASUREMENTS + 8, // WINDOW_RIDE_PAGE_GRAPHS + 2, // WINDOW_RIDE_PAGE_INCOME + 0, // WINDOW_RIDE_PAGE_CUSTOMER + }; + static_assert(std::size(PageTabAnimationDivisor) == WINDOW_RIDE_PAGE_COUNT); -static constexpr std::array PageTabAnimationNumFrames = { - 0, // WINDOW_RIDE_PAGE_MAIN - 0, // WINDOW_RIDE_PAGE_VEHICLE - 4, // WINDOW_RIDE_PAGE_OPERATING - 16, // WINDOW_RIDE_PAGE_MAINTENANCE - 8, // WINDOW_RIDE_PAGE_COLOUR - 16, // WINDOW_RIDE_PAGE_MUSIC - 8, // WINDOW_RIDE_PAGE_MEASUREMENTS - 8, // WINDOW_RIDE_PAGE_GRAPHS - 8, // WINDOW_RIDE_PAGE_INCOME - 0, // WINDOW_RIDE_PAGE_CUSTOMER -}; -static_assert(std::size(PageTabAnimationNumFrames) == WINDOW_RIDE_PAGE_COUNT); + static constexpr std::array PageTabAnimationNumFrames = { + 0, // WINDOW_RIDE_PAGE_MAIN + 0, // WINDOW_RIDE_PAGE_VEHICLE + 4, // WINDOW_RIDE_PAGE_OPERATING + 16, // WINDOW_RIDE_PAGE_MAINTENANCE + 8, // WINDOW_RIDE_PAGE_COLOUR + 16, // WINDOW_RIDE_PAGE_MUSIC + 8, // WINDOW_RIDE_PAGE_MEASUREMENTS + 8, // WINDOW_RIDE_PAGE_GRAPHS + 8, // WINDOW_RIDE_PAGE_INCOME + 0, // WINDOW_RIDE_PAGE_CUSTOMER + }; + static_assert(std::size(PageTabAnimationNumFrames) == WINDOW_RIDE_PAGE_COUNT); -// clang-format off + // clang-format off static constexpr std::array RatingNames = { static_cast(STR_RATING_LOW), static_cast(STR_RATING_MEDIUM), @@ -486,689 +486,2300 @@ static constexpr std::array RatingNames = { static_cast(STR_RATING_ULTRA_EXTREME), }; static_assert(std::size(RatingNames) == 6); -// clang-format on + // clang-format on -static constexpr std::array RideBreakdownReasonNames = { - static_cast(STR_RIDE_BREAKDOWN_SAFETY_CUT_OUT), // BREAKDOWN_SAFETY_CUT_OUT - static_cast(STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_CLOSED), // BREAKDOWN_RESTRAINTS_STUCK_CLOSED - static_cast(STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_OPEN), // BREAKDOWN_RESTRAINTS_STUCK_OPEN - static_cast(STR_RIDE_BREAKDOWN_DOORS_STUCK_CLOSED), // BREAKDOWN_DOORS_STUCK_CLOSED - static_cast(STR_RIDE_BREAKDOWN_DOORS_STUCK_OPEN), // BREAKDOWN_DOORS_STUCK_OPEN - static_cast(STR_RIDE_BREAKDOWN_VEHICLE_MALFUNCTION), // BREAKDOWN_VEHICLE_MALFUNCTION - static_cast(STR_RIDE_BREAKDOWN_BRAKES_FAILURE), // BREAKDOWN_BRAKES_FAILURE - static_cast(STR_RIDE_BREAKDOWN_CONTROL_FAILURE), // BREAKDOWN_CONTROL_FAILURE -}; -static_assert(std::size(RideBreakdownReasonNames) == BREAKDOWN_COUNT); + static constexpr std::array RideBreakdownReasonNames = { + static_cast(STR_RIDE_BREAKDOWN_SAFETY_CUT_OUT), // BREAKDOWN_SAFETY_CUT_OUT + static_cast(STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_CLOSED), // BREAKDOWN_RESTRAINTS_STUCK_CLOSED + static_cast(STR_RIDE_BREAKDOWN_RESTRAINTS_STUCK_OPEN), // BREAKDOWN_RESTRAINTS_STUCK_OPEN + static_cast(STR_RIDE_BREAKDOWN_DOORS_STUCK_CLOSED), // BREAKDOWN_DOORS_STUCK_CLOSED + static_cast(STR_RIDE_BREAKDOWN_DOORS_STUCK_OPEN), // BREAKDOWN_DOORS_STUCK_OPEN + static_cast(STR_RIDE_BREAKDOWN_VEHICLE_MALFUNCTION), // BREAKDOWN_VEHICLE_MALFUNCTION + static_cast(STR_RIDE_BREAKDOWN_BRAKES_FAILURE), // BREAKDOWN_BRAKES_FAILURE + static_cast(STR_RIDE_BREAKDOWN_CONTROL_FAILURE), // BREAKDOWN_CONTROL_FAILURE + }; + static_assert(std::size(RideBreakdownReasonNames) == BREAKDOWN_COUNT); -// Used in other places as well -const StringId ColourSchemeNames[4] = { - STR_MAIN_COLOUR_SCHEME, // RIDE_COLOUR_SCHEME_MAIN - STR_ALTERNATIVE_COLOUR_SCHEME_1, // RIDE_COLOUR_SCHEME_ADDITIONAL_1 - STR_ALTERNATIVE_COLOUR_SCHEME_2, // RIDE_COLOUR_SCHEME_ADDITIONAL_2 - STR_ALTERNATIVE_COLOUR_SCHEME_3, // RIDE_COLOUR_SCHEME_ADDITIONAL_3 -}; -static_assert(std::size(ColourSchemeNames) == RIDE_COLOUR_SCHEME_COUNT); + // Used in other places as well + const StringId ColourSchemeNames[4] = { + STR_MAIN_COLOUR_SCHEME, // RIDE_COLOUR_SCHEME_MAIN + STR_ALTERNATIVE_COLOUR_SCHEME_1, // RIDE_COLOUR_SCHEME_ADDITIONAL_1 + STR_ALTERNATIVE_COLOUR_SCHEME_2, // RIDE_COLOUR_SCHEME_ADDITIONAL_2 + STR_ALTERNATIVE_COLOUR_SCHEME_3, // RIDE_COLOUR_SCHEME_ADDITIONAL_3 + }; + static_assert(std::size(ColourSchemeNames) == RIDE_COLOUR_SCHEME_COUNT); -static constexpr std::array VehicleLoadNames = { - static_cast(STR_QUARTER_LOAD), // WAIT_FOR_LOAD_QUARTER - static_cast(STR_HALF_LOAD), // WAIT_FOR_LOAD_HALF - static_cast(STR_THREE_QUARTER_LOAD), // WAIT_FOR_LOAD_THREE_QUARTER - static_cast(STR_FULL_LOAD), // WAIT_FOR_LOAD_FULL - static_cast(STR_ANY_LOAD), // WAIT_FOR_LOAD_ANY -}; -static_assert(std::size(VehicleLoadNames) == WAIT_FOR_LOAD_COUNT); + static constexpr std::array VehicleLoadNames = { + static_cast(STR_QUARTER_LOAD), // WAIT_FOR_LOAD_QUARTER + static_cast(STR_HALF_LOAD), // WAIT_FOR_LOAD_HALF + static_cast(STR_THREE_QUARTER_LOAD), // WAIT_FOR_LOAD_THREE_QUARTER + static_cast(STR_FULL_LOAD), // WAIT_FOR_LOAD_FULL + static_cast(STR_ANY_LOAD), // WAIT_FOR_LOAD_ANY + }; + static_assert(std::size(VehicleLoadNames) == WAIT_FOR_LOAD_COUNT); -static constexpr std::array VehicleColourSchemeNames = { - static_cast(STR_ALL_VEHICLES_IN_SAME_COLOURS), // RIDE_COLOUR_SCHEME_MODE_ALL_SAME, - static_cast(STR_DIFFERENT_COLOURS_PER), // RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_TRAIN, - static_cast(STR_DIFFERENT_COLOURS_PER_VEHICLE), // RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR, -}; -static_assert(std::size(VehicleColourSchemeNames) == RIDE_COLOUR_SCHEME_MODE_COUNT); + static constexpr std::array VehicleColourSchemeNames = { + static_cast(STR_ALL_VEHICLES_IN_SAME_COLOURS), // RIDE_COLOUR_SCHEME_MODE_ALL_SAME, + static_cast(STR_DIFFERENT_COLOURS_PER), // RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_TRAIN, + static_cast(STR_DIFFERENT_COLOURS_PER_VEHICLE), // RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR, + }; + static_assert(std::size(VehicleColourSchemeNames) == RIDE_COLOUR_SCHEME_MODE_COUNT); -static constexpr std::array VehicleStatusNames = { - static_cast(STR_MOVING_TO_END_OF), // Vehicle::Status::MovingToEndOfStation - static_cast(STR_WAITING_FOR_PASSENGERS_AT), // Vehicle::Status::WaitingForPassengers - static_cast(STR_WAITING_TO_DEPART), // Vehicle::Status::WaitingToDepart - static_cast(STR_DEPARTING), // Vehicle::Status::Departing - static_cast(STR_TRAVELLING_AT_0), // Vehicle::Status::Travelling - static_cast(STR_ARRIVING_AT), // Vehicle::Status::Arriving - static_cast(STR_UNLOADING_PASSENGERS_AT), // Vehicle::Status::UnloadingPassengers - static_cast(STR_TRAVELLING_AT_1), // Vehicle::Status::TravellingBoat - static_cast(STR_CRASHING), // Vehicle::Status::Crashing - static_cast(STR_CRASHED_0), // Vehicle::Status::Crashed - static_cast(STR_TRAVELLING_AT_2), // Vehicle::Status::TravellingDodgems - static_cast(STR_SWINGING), // Vehicle::Status::Swinging - static_cast(STR_ROTATING_0), // Vehicle::Status::Rotating - static_cast(STR_ROTATING_1), // Vehicle::Status::FerrisWheelRotating - static_cast(STR_OPERATING_0), // Vehicle::Status::SimulatorOperating - static_cast(STR_SHOWING_FILM), // Vehicle::Status::ShowingFilm - static_cast(STR_ROTATING_2), // Vehicle::Status::SpaceRingsOperating - static_cast(STR_OPERATING_1), // Vehicle::Status::TopSpinOperating - static_cast(STR_OPERATING_2), // Vehicle::Status::HauntedHouseOperating - static_cast(STR_DOING_CIRCUS_SHOW), // Vehicle::Status::DoingCircusShow - static_cast(STR_OPERATING_3), // Vehicle::Status::CrookedHouseOperating - static_cast(STR_WAITING_FOR_CABLE_LIFT), // Vehicle::Status::WaitingForCableLift - static_cast(STR_TRAVELLING_AT_3), // Vehicle::Status::TravellingCableLift - static_cast(STR_STOPPING_0), // Vehicle::Status::Stopping - static_cast(STR_WAITING_FOR_PASSENGERS), // Vehicle::Status::WaitingForPassengers17 - static_cast(STR_WAITING_TO_START), // Vehicle::Status::WaitingToStart - static_cast(STR_STARTING), // Vehicle::Status::Starting - static_cast(STR_OPERATING), // Vehicle::Status::Operating1A - static_cast(STR_STOPPING_1), // Vehicle::Status::Stopping1B - static_cast(STR_UNLOADING_PASSENGERS), // Vehicle::Status::UnloadingPassengers1C - static_cast(STR_STOPPED_BY_BLOCK_BRAKES), // Vehicle::Status::StoppedByBlockBrakes -}; -static_assert(std::size(VehicleStatusNames) == 31); + static constexpr std::array VehicleStatusNames = { + static_cast(STR_MOVING_TO_END_OF), // Vehicle::Status::MovingToEndOfStation + static_cast(STR_WAITING_FOR_PASSENGERS_AT), // Vehicle::Status::WaitingForPassengers + static_cast(STR_WAITING_TO_DEPART), // Vehicle::Status::WaitingToDepart + static_cast(STR_DEPARTING), // Vehicle::Status::Departing + static_cast(STR_TRAVELLING_AT_0), // Vehicle::Status::Travelling + static_cast(STR_ARRIVING_AT), // Vehicle::Status::Arriving + static_cast(STR_UNLOADING_PASSENGERS_AT), // Vehicle::Status::UnloadingPassengers + static_cast(STR_TRAVELLING_AT_1), // Vehicle::Status::TravellingBoat + static_cast(STR_CRASHING), // Vehicle::Status::Crashing + static_cast(STR_CRASHED_0), // Vehicle::Status::Crashed + static_cast(STR_TRAVELLING_AT_2), // Vehicle::Status::TravellingDodgems + static_cast(STR_SWINGING), // Vehicle::Status::Swinging + static_cast(STR_ROTATING_0), // Vehicle::Status::Rotating + static_cast(STR_ROTATING_1), // Vehicle::Status::FerrisWheelRotating + static_cast(STR_OPERATING_0), // Vehicle::Status::SimulatorOperating + static_cast(STR_SHOWING_FILM), // Vehicle::Status::ShowingFilm + static_cast(STR_ROTATING_2), // Vehicle::Status::SpaceRingsOperating + static_cast(STR_OPERATING_1), // Vehicle::Status::TopSpinOperating + static_cast(STR_OPERATING_2), // Vehicle::Status::HauntedHouseOperating + static_cast(STR_DOING_CIRCUS_SHOW), // Vehicle::Status::DoingCircusShow + static_cast(STR_OPERATING_3), // Vehicle::Status::CrookedHouseOperating + static_cast(STR_WAITING_FOR_CABLE_LIFT), // Vehicle::Status::WaitingForCableLift + static_cast(STR_TRAVELLING_AT_3), // Vehicle::Status::TravellingCableLift + static_cast(STR_STOPPING_0), // Vehicle::Status::Stopping + static_cast(STR_WAITING_FOR_PASSENGERS), // Vehicle::Status::WaitingForPassengers17 + static_cast(STR_WAITING_TO_START), // Vehicle::Status::WaitingToStart + static_cast(STR_STARTING), // Vehicle::Status::Starting + static_cast(STR_OPERATING), // Vehicle::Status::Operating1A + static_cast(STR_STOPPING_1), // Vehicle::Status::Stopping1B + static_cast(STR_UNLOADING_PASSENGERS), // Vehicle::Status::UnloadingPassengers1C + static_cast(STR_STOPPED_BY_BLOCK_BRAKES), // Vehicle::Status::StoppedByBlockBrakes + }; + static_assert(std::size(VehicleStatusNames) == 31); -static constexpr std::array SingleSessionVehicleStatusNames = { - static_cast(STR_STOPPING_0), // Vehicle::Status::MovingToEndOfStation - static_cast(STR_WAITING_FOR_PASSENGERS), // Vehicle::Status::WaitingForPassengers - static_cast(STR_WAITING_TO_START), // Vehicle::Status::WaitingToDepart - static_cast(STR_STARTING), // Vehicle::Status::Departing - static_cast(STR_OPERATING), // Vehicle::Status::Travelling - static_cast(STR_STOPPING_1), // Vehicle::Status::Arriving - static_cast(STR_UNLOADING_PASSENGERS), // Vehicle::Status::UnloadingPassengers -}; -static_assert(std::size(SingleSessionVehicleStatusNames) == 7); + static constexpr std::array SingleSessionVehicleStatusNames = { + static_cast(STR_STOPPING_0), // Vehicle::Status::MovingToEndOfStation + static_cast(STR_WAITING_FOR_PASSENGERS), // Vehicle::Status::WaitingForPassengers + static_cast(STR_WAITING_TO_START), // Vehicle::Status::WaitingToDepart + static_cast(STR_STARTING), // Vehicle::Status::Departing + static_cast(STR_OPERATING), // Vehicle::Status::Travelling + static_cast(STR_STOPPING_1), // Vehicle::Status::Arriving + static_cast(STR_UNLOADING_PASSENGERS), // Vehicle::Status::UnloadingPassengers + }; + static_assert(std::size(SingleSessionVehicleStatusNames) == 7); -struct WindowRideMazeDesignOption -{ - StringId text; - uint32_t sprite; -}; - -static constexpr std::array MazeOptions = { - WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_BRICK_WALLS, SPR_RIDE_DESIGN_PREVIEW_MAZE_BRICK_WALLS }, - WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_HEDGES, SPR_RIDE_DESIGN_PREVIEW_MAZE_HEDGES }, - WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_ICE_BLOCKS, SPR_RIDE_DESIGN_PREVIEW_MAZE_ICE_BLOCKS }, - WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_WOODEN_FENCES, SPR_RIDE_DESIGN_PREVIEW_MAZE_WOODEN_FENCES }, -}; -static_assert(std::size(MazeOptions) == 4); - -struct GraphsYAxis -{ - uint8_t interval; - int8_t unit; - int8_t unit_interval; - StringId label; -}; - -static constexpr const std::array GraphsYAxisDetails = { - GraphsYAxis{ 11, 0, 10, STR_RIDE_STATS_VELOCITY_FORMAT }, // GRAPH_VELOCITY - GraphsYAxis{ 10, 0, 15, STR_RIDE_STATS_ALTITUDE_FORMAT }, // GRAPH_ALTITUDE - GraphsYAxis{ 13, -3, 1, STR_RIDE_STATS_G_FORCE_FORMAT }, // GRAPH_VERTICAL - GraphsYAxis{ 13, -4, 1, STR_RIDE_STATS_G_FORCE_FORMAT }, // GRAPH_LATERAL -}; -static_assert(std::size(GraphsYAxisDetails) == 4); - -static constexpr auto RIDE_G_FORCES_RED_NEG_VERTICAL = -FIXED_2DP(2, 50); -static constexpr auto RIDE_G_FORCES_RED_LATERAL = FIXED_2DP(2, 80); - -// Used for sorting the ride type cheat dropdown. -struct RideTypeLabel -{ - ride_type_t RideTypeId; - StringId LabelId; - u8string_view LabelString; -}; - -// Used for sorting the vehicle type dropdown. -struct VehicleTypeLabel -{ - ObjectEntryIndex SubTypeId; - StringId LabelId; - u8string_view LabelString; -}; - -// Used for sorting the entrance type dropdown. -struct EntranceTypeLabel -{ - ObjectEntryIndex EntranceTypeId; - StringId LabelId; - u8string_view LabelString; -}; - -class RideWindow final : public Window -{ - int16_t _viewIndex; - std::vector _rideDropdownData; - int32_t _rideDropdownDataLanguage = LANGUAGE_UNDEFINED; - int32_t _vehicleDropdownDataLanguage = LANGUAGE_UNDEFINED; - int32_t _entranceDropdownDataLanguage = LANGUAGE_UNDEFINED; - const RideObjectEntry* _vehicleDropdownRideType = nullptr; - bool _vehicleDropdownExpanded = false; - std::vector _vehicleDropdownData; - int16_t _vehicleIndex = 0; - uint16_t _rideColour = 0; - std::vector _entranceDropdownData; - bool _autoScrollGraph = true; - -public: - RideWindow(const Ride& ride) + struct WindowRideMazeDesignOption { - rideId = ride.id; - } + StringId text; + uint32_t sprite; + }; - virtual void OnOpen() override + static constexpr std::array MazeOptions = { + WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_BRICK_WALLS, SPR_RIDE_DESIGN_PREVIEW_MAZE_BRICK_WALLS }, + WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_HEDGES, SPR_RIDE_DESIGN_PREVIEW_MAZE_HEDGES }, + WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_ICE_BLOCKS, SPR_RIDE_DESIGN_PREVIEW_MAZE_ICE_BLOCKS }, + WindowRideMazeDesignOption{ STR_RIDE_DESIGN_MAZE_WOODEN_FENCES, SPR_RIDE_DESIGN_PREVIEW_MAZE_WOODEN_FENCES }, + }; + static_assert(std::size(MazeOptions) == 4); + + struct GraphsYAxis { - widgets = PageWidgets[WINDOW_RIDE_PAGE_MAIN]; - hold_down_widgets = PageHoldDownWidgets[WINDOW_RIDE_PAGE_MAIN]; + uint8_t interval; + int8_t unit; + int8_t unit_interval; + StringId label; + }; - page = WINDOW_RIDE_PAGE_MAIN; - frame_no = 0; - list_information_type = 0; - picked_peep_frame = 0; - DisableTabs(); - min_width = 316; - min_height = 180; - max_width = 500; - max_height = 450; + static constexpr const std::array GraphsYAxisDetails = { + GraphsYAxis{ 11, 0, 10, STR_RIDE_STATS_VELOCITY_FORMAT }, // GRAPH_VELOCITY + GraphsYAxis{ 10, 0, 15, STR_RIDE_STATS_ALTITUDE_FORMAT }, // GRAPH_ALTITUDE + GraphsYAxis{ 13, -3, 1, STR_RIDE_STATS_G_FORCE_FORMAT }, // GRAPH_VERTICAL + GraphsYAxis{ 13, -4, 1, STR_RIDE_STATS_G_FORCE_FORMAT }, // GRAPH_LATERAL + }; + static_assert(std::size(GraphsYAxisDetails) == 4); - auto ride = GetRide(rideId); - if (ride == nullptr) + static constexpr auto RIDE_G_FORCES_RED_NEG_VERTICAL = -FIXED_2DP(2, 50); + static constexpr auto RIDE_G_FORCES_RED_LATERAL = FIXED_2DP(2, 80); + + // Used for sorting the ride type cheat dropdown. + struct RideTypeLabel + { + ride_type_t RideTypeId; + StringId LabelId; + u8string_view LabelString; + }; + + // Used for sorting the vehicle type dropdown. + struct VehicleTypeLabel + { + ObjectEntryIndex SubTypeId; + StringId LabelId; + u8string_view LabelString; + }; + + // Used for sorting the entrance type dropdown. + struct EntranceTypeLabel + { + ObjectEntryIndex EntranceTypeId; + StringId LabelId; + u8string_view LabelString; + }; + + class RideWindow final : public Window + { + int16_t _viewIndex; + std::vector _rideDropdownData; + int32_t _rideDropdownDataLanguage = LANGUAGE_UNDEFINED; + int32_t _vehicleDropdownDataLanguage = LANGUAGE_UNDEFINED; + int32_t _entranceDropdownDataLanguage = LANGUAGE_UNDEFINED; + const RideObjectEntry* _vehicleDropdownRideType = nullptr; + bool _vehicleDropdownExpanded = false; + std::vector _vehicleDropdownData; + int16_t _vehicleIndex = 0; + uint16_t _rideColour = 0; + std::vector _entranceDropdownData; + bool _autoScrollGraph = true; + + public: + RideWindow(const Ride& ride) { - Close(); - return; - } - UpdateOverallView(*ride); - - 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: - GraphsOnScrollSelect(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; - } - } - - void SetPage(int32_t newPage) - { - 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); - } + rideId = ride.id; } - // Set listen only to viewport - bool listen = false; - if (newPage == WINDOW_RIDE_PAGE_MAIN && page == WINDOW_RIDE_PAGE_MAIN && viewport != nullptr - && !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) - listen = true; - - 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 = PageHoldDownWidgets[page]; - pressed_widgets = 0; - widgets = PageWidgets[page]; - DisableTabs(); - Invalidate(); - - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - Invalidate(); - - if (listen && viewport != nullptr) - viewport->flags |= VIEWPORT_FLAG_SOUND_ON; - } - - void SetViewIndex(int16_t newIndex) - { - _viewIndex = newIndex; - OnViewportRotate(); - } - int16_t GetViewIndex() const - { - return _viewIndex; - } - - void ResetVehicleIndex() - { - _vehicleIndex = 0; - } - -private: - void DrawTabImage(DrawPixelInfo& dpi, int32_t tab, int32_t spriteIndex) - { - WidgetIndex widgetIndex = WIDX_TAB_1 + tab; - - if (!WidgetIsDisabled(*this, widgetIndex)) + virtual void OnOpen() override { - if (page == tab) - { - int32_t frame = frame_no / PageTabAnimationDivisor[page]; - spriteIndex += (frame % PageTabAnimationNumFrames[page]); - } + widgets = PageWidgets[WINDOW_RIDE_PAGE_MAIN]; + hold_down_widgets = PageHoldDownWidgets[WINDOW_RIDE_PAGE_MAIN]; - const auto& widget = widgets[widgetIndex]; - GfxDrawSprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget.left, widget.top }); - } - } + page = WINDOW_RIDE_PAGE_MAIN; + frame_no = 0; + list_information_type = 0; + picked_peep_frame = 0; + DisableTabs(); + min_width = 316; + min_height = 180; + max_width = 500; + max_height = 450; - void DrawTabMain(DrawPixelInfo& dpi) - { - WidgetIndex widgetIndex = WIDX_TAB_1 + static_cast(WINDOW_RIDE_PAGE_MAIN); - if (!WidgetIsDisabled(*this, widgetIndex)) - { auto ride = GetRide(rideId); - if (ride != nullptr) + if (ride == nullptr) { - int32_t spriteIndex = 0; - switch (ride->GetClassification()) + Close(); + return; + } + UpdateOverallView(*ride); + + 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: + GraphsOnScrollSelect(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; + } + } + + void SetPage(int32_t newPage) + { + 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) { - 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; + 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 + bool listen = false; + if (newPage == WINDOW_RIDE_PAGE_MAIN && page == WINDOW_RIDE_PAGE_MAIN && viewport != nullptr + && !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) + listen = true; + + 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 = PageHoldDownWidgets[page]; + pressed_widgets = 0; + widgets = PageWidgets[page]; + DisableTabs(); + Invalidate(); + + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); + + if (listen && viewport != nullptr) + viewport->flags |= VIEWPORT_FLAG_SOUND_ON; + } + + void SetViewIndex(int16_t newIndex) + { + _viewIndex = newIndex; + OnViewportRotate(); + } + int16_t GetViewIndex() const + { + return _viewIndex; + } + + void ResetVehicleIndex() + { + _vehicleIndex = 0; + } + + private: + void DrawTabImage(DrawPixelInfo& dpi, int32_t tab, int32_t spriteIndex) + { + WidgetIndex widgetIndex = WIDX_TAB_1 + tab; + + if (!WidgetIsDisabled(*this, widgetIndex)) + { + if (page == tab) + { + int32_t frame = frame_no / PageTabAnimationDivisor[page]; + spriteIndex += (frame % PageTabAnimationNumFrames[page]); } 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)) + void DrawTabMain(DrawPixelInfo& dpi) { - auto screenCoords = ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; - int32_t clipWidth = widget.right - screenCoords.x; - int32_t clipHeight = widget.bottom - 3 - screenCoords.y; - if (page == WINDOW_RIDE_PAGE_VEHICLE) - clipHeight += 4; - - screenCoords += windowPos; - - DrawPixelInfo clipDPI; - if (!ClipDrawPixelInfo(clipDPI, dpi, screenCoords, clipWidth, clipHeight)) + 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 clipWidth = widget.right - screenCoords.x; + int32_t clipHeight = widget.bottom - 3 - screenCoords.y; + if (page == WINDOW_RIDE_PAGE_VEHICLE) + clipHeight += 4; + + screenCoords += windowPos; + + DrawPixelInfo clipDPI; + if (!ClipDrawPixelInfo(clipDPI, dpi, screenCoords, clipWidth, clipHeight)) + { + 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 disabledTabs = 0; + auto ride = GetRide(rideId); + if (ride == nullptr) return; + + const auto& rtd = ride->GetRideTypeDescriptor(); + + if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) + disabledTabs |= (1uLL << WIDX_TAB_8); // 0x800 + + if (ride->type == RIDE_TYPE_MINI_GOLF) + disabledTabs |= (1uLL << WIDX_TAB_2 | 1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4); // 0xE0 + + if (rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) + disabledTabs |= (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)) + { + disabledTabs |= (1uLL << WIDX_TAB_5); // 0x100 } - screenCoords = ScreenCoordsXY{ widget.width() / 2, widget.height() - 12 }; + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) + disabledTabs |= (1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_7); // 0x4C0 + + if (!rtd.HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC)) + { + disabledTabs |= (1uLL << WIDX_TAB_6); // 0x200 + } + + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_CASH_MACHINE) || rtd.HasFlag(RIDE_TYPE_FLAG_IS_FIRST_AID) + || (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) != 0) + disabledTabs |= (1uLL << WIDX_TAB_9); // 0x1000 + + if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) != 0) + disabledTabs |= (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) + { + disabledTabs |= 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) + { + disabledTabs |= (1uLL << WIDX_TAB_5); + } + + disabled_widgets = disabledTabs; + } + + 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 >= _rideOverallViewsCache.size()) + { + _rideOverallViewsCache.resize(rideIndex + 1); + } + + auto& view = _rideOverallViewsCache[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 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 = _viewIndex - 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 = _viewIndex - 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) + { + _viewIndex = 0; + } + if (number < _rideOverallViewsCache.size()) + { + const auto& view = _rideOverallViewsCache[number]; + newFocus = Focus(view.loc, view.zoom); + } + } + + uint16_t newViewportFlags = 0; + if (viewport != nullptr) + { + if (focus == newFocus) + { + return; + } + newViewportFlags = viewport->flags; + RemoveViewport(); + } + else if (gConfigGeneral.AlwaysShowGridlines) + { + newViewportFlags |= 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& viewWidget = widgets[WIDX_VIEWPORT]; + + auto screenPos = windowPos + ScreenCoordsXY{ viewWidget.left + 1, viewWidget.top + 1 }; + int32_t viewWidth = viewWidget.width() - 1; + int32_t viewHeight = viewWidget.height() - 1; + + ViewportCreate(this, screenPos, viewWidth, viewHeight, focus.value()); + + flags |= WF_NO_SCROLLING; + Invalidate(); + } + if (viewport != nullptr) + { + viewport->flags = newViewportFlags; + 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 MainOnMouseUp(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 (GetGameState().Cheats.AllowArbitraryRideTypeChanges) + { + 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(_viewIndex, 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() + { + 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, u8string_view{ ls.GetString(name) } }); + } + + std::sort(_rideDropdownData.begin(), _rideDropdownData.end(), [](auto& a, auto& b) { + return a.LabelString.compare(b.LabelString) < 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].LabelId; + } + + 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].RideTypeId == 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) || _viewIndex == 0 + || _viewIndex > 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 (_viewIndex > 0) + { + if (_viewIndex <= ride->NumTrains) + { + Vehicle* vehicle = GetEntity(ride->vehicles[_viewIndex - 1]); + if (vehicle != nullptr) + { + auto headVehicleSpriteIndex = vehicle->Id; + WindowBase* w_main = WindowGetMain(); + WindowFollowSprite(*w_main, headVehicleSpriteIndex); + } + } + } + } + } + } + + void PopulateVehicleTypeDropdown(const Ride& ride, bool forceRefresh = false) + { + auto& objManager = GetContext()->GetObjectManager(); + const auto* rideEntry = ride.GetRideEntry(); + + bool selectionShouldBeExpanded; + int32_t rideTypeIterator, rideTypeIteratorMax; + + const auto& rtd = ride.GetRideTypeDescriptor(); + if (GetGameState().Cheats.ShowVehiclesFromOtherTrackTypes + && !( + 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) && !GetGameState().Cheats.IgnoreResearchStatus) + continue; + + auto name = currentRideEntry->naming.Name; + _vehicleDropdownData.push_back({ rideEntryIndex, name, u8string_view{ ls.GetString(name) } }); + } + } + + std::sort(_vehicleDropdownData.begin(), _vehicleDropdownData.end(), [](auto& a, auto& b) { + return a.LabelString.compare(b.LabelString) < 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].LabelId; + } + + Widget* dropdownWidget = widget - 1; + 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, ddWidth); + + // 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].SubTypeId == ride->subtype) + { + pos = i; + break; + } + } + + gDropdownHighlightedIndex = pos; + gDropdownDefaultIndex = pos; + Dropdown::SetChecked(pos, true); + } + + void PopulateEntranceStyleDropdown() + { + auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); + if (_entranceDropdownDataLanguage == ls.GetCurrentLanguage()) + return; + + _entranceDropdownData.clear(); + + auto& objManager = GetContext()->GetObjectManager(); + + for (ObjectEntryIndex i = 0; i < MAX_STATION_OBJECTS; i++) + { + auto stationObj = static_cast(objManager.GetLoadedObject(ObjectType::Station, i)); + if (stationObj != nullptr) + { + auto name = stationObj->NameStringId; + _entranceDropdownData.push_back({ i, name, u8string_view{ ls.GetString(name) } }); + } + } + + std::sort(_entranceDropdownData.begin(), _entranceDropdownData.end(), [](auto& a, auto& b) { + return a.LabelString.compare(b.LabelString) < 0; + }); + + _entranceDropdownDataLanguage = ls.GetCurrentLanguage(); + } + + void ShowEntranceStyleDropdown() + { + auto dropdownWidget = &widgets[WIDX_ENTRANCE_STYLE_DROPDOWN] - 1; + auto ride = GetRide(rideId); + + PopulateEntranceStyleDropdown(); + + for (size_t i = 0; i < _entranceDropdownData.size(); i++) + { + gDropdownItems[i].Args = _entranceDropdownData[i].LabelId; + gDropdownItems[i].Format = _entranceDropdownData[i].EntranceTypeId == ride->entrance_style + ? STR_DROPDOWN_MENU_LABEL_SELECTED + : STR_DROPDOWN_MENU_LABEL; + } + + WindowDropdownShowTextCustomWidth( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, _entranceDropdownData.size(), + widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].right - dropdownWidget->left); + } + + void MainOnMouseDown(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_VIEW_DROPDOWN: + ShowViewDropdown(&widgets[widgetIndex]); + break; + case WIDX_OPEN: + ShowOpenDropdown(&widgets[widgetIndex]); + break; + case WIDX_RIDE_TYPE_DROPDOWN: + ShowRideTypeDropdown(&widgets[widgetIndex]); + break; + case WIDX_LOCATE: + ShowLocateDropdown(&widgets[widgetIndex]); + break; + } + } + + void MainOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_VIEW_DROPDOWN: + if (dropdownIndex == -1) + { + dropdownIndex = _viewIndex + 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; + } + } + } + + _viewIndex = 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].RideTypeId; + 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 (_viewIndex == 0) + return; + + if (_viewIndex <= ride->NumTrains) + { + Vehicle* vehicle = GetEntity(ride->vehicles[_viewIndex - 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 MainOnTextInput(WidgetIndex widgetIndex, std::string_view text) + { + if (widgetIndex != WIDX_RENAME || text.empty()) + return; + + auto ride = GetRide(rideId); + if (ride != nullptr) + { + auto strText = std::string(text); + auto gameAction = RideSetNameAction(ride->id, strText); + GameActions::Execute(&gameAction); + } + } + + void MainViewportRotate() + { + InitViewport(); + } + + void MainOnPrepareDraw() + { + int32_t i, widgetHeight; + + auto* newWidgets = PageWidgets[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) + && !GetGameState().Cheats.MakeAllDestructible) + disabled_widgets |= (1uLL << WIDX_DEMOLISH); + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + uint32_t spriteIds[] = { + SPR_CLOSED, + SPR_OPEN, + SPR_TESTING, + SPR_G2_SIMULATE, + }; + widgets[WIDX_OPEN].image = ImageId(spriteIds[EnumValue(ride->status)]); + +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2 + + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT); + widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0 + (ride->status == RideStatus::Simulating) * 2 + + WidgetIsPressed(*w, WIDX_SIMULATE_LIGHT); + 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); + 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); + 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); + widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); + + AnchorBorderWidgets(); + + const int32_t offset = GetGameState().Cheats.AllowArbitraryRideTypeChanges ? 15 : 0; + // Anchor main page specific widgets + widgets[WIDX_VIEWPORT].right = width - 26; + widgets[WIDX_VIEWPORT].bottom = height - (14 + offset); + widgets[WIDX_STATUS].right = width - 26; + widgets[WIDX_STATUS].top = height - (13 + offset); + widgets[WIDX_STATUS].bottom = height - (3 + offset); + widgets[WIDX_VIEW].right = width - 60; + widgets[WIDX_VIEW_DROPDOWN].right = width - 61; + widgets[WIDX_VIEW_DROPDOWN].left = width - 71; + widgets[WIDX_RIDE_TYPE].right = width - 26; + widgets[WIDX_RIDE_TYPE].top = height - 17; + widgets[WIDX_RIDE_TYPE].bottom = height - 4; + widgets[WIDX_RIDE_TYPE_DROPDOWN].left = width - 37; + widgets[WIDX_RIDE_TYPE_DROPDOWN].right = width - 27; + widgets[WIDX_RIDE_TYPE_DROPDOWN].top = height - 16; + widgets[WIDX_RIDE_TYPE_DROPDOWN].bottom = height - 5; + + if (!GetGameState().Cheats.AllowArbitraryRideTypeChanges) + { + widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::Empty; + widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Empty; + } + else + { + widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_RIDE_TYPE].text = ride->GetRideTypeDescriptor().Naming.Name; + widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Button; + } + + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + + if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) + { + widgets[WIDX_OPEN].type = WindowWidgetType::Empty; + widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; + widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + if (ride->SupportsStatus(RideStatus::Simulating)) + widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::ImgBtn; +#endif + widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RideStatus::Testing) ? WindowWidgetType::ImgBtn + : WindowWidgetType::Empty; + widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; + + widgetHeight = 62; + if (widgets[WIDX_SIMULATE_LIGHT].type != WindowWidgetType::Empty) + { + widgets[WIDX_SIMULATE_LIGHT].top = widgetHeight; + widgets[WIDX_SIMULATE_LIGHT].bottom = widgetHeight + 13; + widgetHeight += 14; + } + if (widgets[WIDX_TEST_LIGHT].type != WindowWidgetType::Empty) + { + widgets[WIDX_TEST_LIGHT].top = widgetHeight; + widgets[WIDX_TEST_LIGHT].bottom = widgetHeight + 13; + widgetHeight += 14; + } + widgets[WIDX_OPEN_LIGHT].top = widgetHeight; + widgets[WIDX_OPEN_LIGHT].bottom = widgetHeight + 13; + widgetHeight += 14 - 24 + RCT1_LIGHT_OFFSET; + } + else + { + widgets[WIDX_OPEN].type = WindowWidgetType::FlatBtn; + widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_TEST_LIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; + widgetHeight = 46; + } + for (i = WIDX_CLOSE_LIGHT; i <= WIDX_OPEN_LIGHT; i++) + { + widgets[i].left = width - 20; + widgets[i].right = width - 7; + } + for (i = WIDX_OPEN; i <= WIDX_DEMOLISH; i++, widgetHeight += 24) + { + widgets[i].left = width - 25; + widgets[i].right = width - 2; + widgets[i].top = widgetHeight; + 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[_viewIndex - 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 + || trackType == TrackElemType::DiagBlockBrakes) + { + 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[EnumValue(vehicle->status)]; + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SINGLE_SESSION) + && vehicle->status <= Vehicle::Status::UnloadingPassengers) + { + stringId = SingleSessionVehicleStatusNames[EnumValue(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 (_viewIndex == 0) + return GetStatusOverallView(ft); + if (ride != nullptr && _viewIndex <= ride->NumTrains) + return GetStatusVehicle(ft); + if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + return GetStatusOverallView(ft); + return GetStatusStation(ft); + } + + void MainOnDraw(DrawPixelInfo& dpi) + { + 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 (_viewIndex != 0) + { + if (_viewIndex > ride->NumTrains) + { + ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station).number); + ft.Add(_viewIndex - ride->NumTrains); + } + else + { + ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number); + ft.Add(_viewIndex); + } + } + else + { + ft.Add(STR_OVERALL_VIEW); + } + + auto* widget = &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 = &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 VehicleOnMouseUp(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 VehicleOnMouseDown(WidgetIndex widgetIndex) + { + 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 VehicleOnDropdown(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].SubTypeId; + ride->SetRideEntry(newRideType); + } + } + break; + } + } + + void VehicleUpdate() + { + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_2); + } + + OpenRCT2String VehicleTooltip(const WidgetIndex widgetIndex, StringId fallback) + { + auto ride = GetRide(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 = PageWidgets[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 + widgets[WIDX_VEHICLE_TYPE].text = rideEntry->naming.Name; + + // Trains + if (rideEntry->cars_per_flat_ride > 1 || GetGameState().Cheats.DisableTrainLengthLimit) + { + widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Spinner; + widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Button; + } + else + { + widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Empty; + } + + // Cars per train + if (rideEntry->zero_cars + 1 < rideEntry->max_cars_in_train || GetGameState().Cheats.DisableTrainLengthLimit) + { + widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Spinner; + widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Button; + } + else + { + widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Empty; + } + + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_ALLOW_REVERSED_TRAINS) + || (GetGameState().Cheats.DisableTrainLengthLimit + && !ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))) + { + 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 + { + 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) + { + widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_1_CAR_PER_TRAIN; + } + else + { + widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_X_CARS_PER_TRAIN; + } + } + + void VehicleOnDraw(DrawPixelInfo& dpi) + { + WindowDrawWidgets(*this, dpi); + DrawTabImages(dpi); auto ride = GetRide(rideId); if (ride == nullptr) @@ -1178,2684 +2789,945 @@ private: 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; - } + auto screenCoords = windowPos + ScreenCoordsXY{ 8, 64 }; - // 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 disabledTabs = 0; - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - const auto& rtd = ride->GetRideTypeDescriptor(); - - if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) - disabledTabs |= (1uLL << WIDX_TAB_8); // 0x800 - - if (ride->type == RIDE_TYPE_MINI_GOLF) - disabledTabs |= (1uLL << WIDX_TAB_2 | 1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4); // 0xE0 - - if (rtd.HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) - disabledTabs |= (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)) - { - disabledTabs |= (1uLL << WIDX_TAB_5); // 0x100 - } - - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) - disabledTabs |= (1uLL << WIDX_TAB_3 | 1uLL << WIDX_TAB_4 | 1uLL << WIDX_TAB_7); // 0x4C0 - - if (!rtd.HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC)) - { - disabledTabs |= (1uLL << WIDX_TAB_6); // 0x200 - } - - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_CASH_MACHINE) || rtd.HasFlag(RIDE_TYPE_FLAG_IS_FIRST_AID) - || (GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) != 0) - disabledTabs |= (1uLL << WIDX_TAB_9); // 0x1000 - - if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) != 0) - disabledTabs |= (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) - { - disabledTabs |= 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) - { - disabledTabs |= (1uLL << WIDX_TAB_5); - } - - disabled_widgets = disabledTabs; - } - - 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 >= _rideOverallViewsCache.size()) - { - _rideOverallViewsCache.resize(rideIndex + 1); - } - - auto& view = _rideOverallViewsCache[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 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 = _viewIndex - 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 = _viewIndex - 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) - { - _viewIndex = 0; - } - if (number < _rideOverallViewsCache.size()) - { - const auto& view = _rideOverallViewsCache[number]; - newFocus = Focus(view.loc, view.zoom); - } - } - - uint16_t newViewportFlags = 0; - if (viewport != nullptr) - { - if (focus == newFocus) - { - return; - } - newViewportFlags = viewport->flags; - RemoveViewport(); - } - else if (gConfigGeneral.AlwaysShowGridlines) - { - newViewportFlags |= 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& viewWidget = widgets[WIDX_VIEWPORT]; - - auto screenPos = windowPos + ScreenCoordsXY{ viewWidget.left + 1, viewWidget.top + 1 }; - int32_t viewWidth = viewWidget.width() - 1; - int32_t viewHeight = viewWidget.height() - 1; - - ViewportCreate(this, screenPos, viewWidth, viewHeight, focus.value()); - - flags |= WF_NO_SCROLLING; - Invalidate(); - } - if (viewport != nullptr) - { - viewport->flags = newViewportFlags; - 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 MainOnMouseUp(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 (GetGameState().Cheats.AllowArbitraryRideTypeChanges) - { - 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(_viewIndex, 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() - { - 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, u8string_view{ ls.GetString(name) } }); - } - - std::sort(_rideDropdownData.begin(), _rideDropdownData.end(), [](auto& a, auto& b) { - return a.LabelString.compare(b.LabelString) < 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].LabelId; - } - - 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].RideTypeId == 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) || _viewIndex == 0 || _viewIndex > 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 (_viewIndex > 0) - { - if (_viewIndex <= ride->NumTrains) - { - Vehicle* vehicle = GetEntity(ride->vehicles[_viewIndex - 1]); - if (vehicle != nullptr) - { - auto headVehicleSpriteIndex = vehicle->Id; - WindowBase* w_main = WindowGetMain(); - WindowFollowSprite(*w_main, headVehicleSpriteIndex); - } - } - } - } - } - } - - void PopulateVehicleTypeDropdown(const Ride& ride, bool forceRefresh = false) - { - auto& objManager = GetContext()->GetObjectManager(); - const auto* rideEntry = ride.GetRideEntry(); - - bool selectionShouldBeExpanded; - int32_t rideTypeIterator, rideTypeIteratorMax; - - const auto& rtd = ride.GetRideTypeDescriptor(); - if (GetGameState().Cheats.ShowVehiclesFromOtherTrackTypes - && !( - 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) && !GetGameState().Cheats.IgnoreResearchStatus) - continue; - - auto name = currentRideEntry->naming.Name; - _vehicleDropdownData.push_back({ rideEntryIndex, name, u8string_view{ ls.GetString(name) } }); - } - } - - std::sort(_vehicleDropdownData.begin(), _vehicleDropdownData.end(), [](auto& a, auto& b) { - return a.LabelString.compare(b.LabelString) < 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].LabelId; - } - - Widget* dropdownWidget = widget - 1; - 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, ddWidth); - - // 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].SubTypeId == ride->subtype) - { - pos = i; - break; - } - } - - gDropdownHighlightedIndex = pos; - gDropdownDefaultIndex = pos; - Dropdown::SetChecked(pos, true); - } - - void PopulateEntranceStyleDropdown() - { - auto& ls = OpenRCT2::GetContext()->GetLocalisationService(); - if (_entranceDropdownDataLanguage == ls.GetCurrentLanguage()) - return; - - _entranceDropdownData.clear(); - - auto& objManager = GetContext()->GetObjectManager(); - - for (ObjectEntryIndex i = 0; i < MAX_STATION_OBJECTS; i++) - { - auto stationObj = static_cast(objManager.GetLoadedObject(ObjectType::Station, i)); - if (stationObj != nullptr) - { - auto name = stationObj->NameStringId; - _entranceDropdownData.push_back({ i, name, u8string_view{ ls.GetString(name) } }); - } - } - - std::sort(_entranceDropdownData.begin(), _entranceDropdownData.end(), [](auto& a, auto& b) { - return a.LabelString.compare(b.LabelString) < 0; - }); - - _entranceDropdownDataLanguage = ls.GetCurrentLanguage(); - } - - void ShowEntranceStyleDropdown() - { - auto dropdownWidget = &widgets[WIDX_ENTRANCE_STYLE_DROPDOWN] - 1; - auto ride = GetRide(rideId); - - PopulateEntranceStyleDropdown(); - - for (size_t i = 0; i < _entranceDropdownData.size(); i++) - { - gDropdownItems[i].Args = _entranceDropdownData[i].LabelId; - gDropdownItems[i].Format = _entranceDropdownData[i].EntranceTypeId == ride->entrance_style - ? STR_DROPDOWN_MENU_LABEL_SELECTED - : STR_DROPDOWN_MENU_LABEL; - } - - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], - 0, Dropdown::Flag::StayOpen, _entranceDropdownData.size(), - widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].right - dropdownWidget->left); - } - - void MainOnMouseDown(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_VIEW_DROPDOWN: - ShowViewDropdown(&widgets[widgetIndex]); - break; - case WIDX_OPEN: - ShowOpenDropdown(&widgets[widgetIndex]); - break; - case WIDX_RIDE_TYPE_DROPDOWN: - ShowRideTypeDropdown(&widgets[widgetIndex]); - break; - case WIDX_LOCATE: - ShowLocateDropdown(&widgets[widgetIndex]); - break; - } - } - - void MainOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) - { - case WIDX_VIEW_DROPDOWN: - if (dropdownIndex == -1) - { - dropdownIndex = _viewIndex + 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; - } - } - } - - _viewIndex = 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].RideTypeId; - 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 (_viewIndex == 0) - return; - - if (_viewIndex <= ride->NumTrains) - { - Vehicle* vehicle = GetEntity(ride->vehicles[_viewIndex - 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 MainOnTextInput(WidgetIndex widgetIndex, std::string_view text) - { - if (widgetIndex != WIDX_RENAME || text.empty()) - return; - - auto ride = GetRide(rideId); - if (ride != nullptr) - { - auto strText = std::string(text); - auto gameAction = RideSetNameAction(ride->id, strText); - GameActions::Execute(&gameAction); - } - } - - void MainViewportRotate() - { - InitViewport(); - } - - void MainOnPrepareDraw() - { - int32_t i, widgetHeight; - - auto* newWidgets = PageWidgets[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) - && !GetGameState().Cheats.MakeAllDestructible) - disabled_widgets |= (1uLL << WIDX_DEMOLISH); - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - uint32_t spriteIds[] = { - SPR_CLOSED, - SPR_OPEN, - SPR_TESTING, - SPR_G2_SIMULATE, - }; - widgets[WIDX_OPEN].image = ImageId(spriteIds[EnumValue(ride->status)]); - -#ifdef __SIMULATE_IN_RIDE_WINDOW__ - widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RideStatus::Closed) * 2 - + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT); - widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0 + (ride->status == RideStatus::Simulating) * 2 - + WidgetIsPressed(*w, WIDX_SIMULATE_LIGHT); - 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); - 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); - 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); - widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); - - AnchorBorderWidgets(); - - const int32_t offset = GetGameState().Cheats.AllowArbitraryRideTypeChanges ? 15 : 0; - // Anchor main page specific widgets - widgets[WIDX_VIEWPORT].right = width - 26; - widgets[WIDX_VIEWPORT].bottom = height - (14 + offset); - widgets[WIDX_STATUS].right = width - 26; - widgets[WIDX_STATUS].top = height - (13 + offset); - widgets[WIDX_STATUS].bottom = height - (3 + offset); - widgets[WIDX_VIEW].right = width - 60; - widgets[WIDX_VIEW_DROPDOWN].right = width - 61; - widgets[WIDX_VIEW_DROPDOWN].left = width - 71; - widgets[WIDX_RIDE_TYPE].right = width - 26; - widgets[WIDX_RIDE_TYPE].top = height - 17; - widgets[WIDX_RIDE_TYPE].bottom = height - 4; - widgets[WIDX_RIDE_TYPE_DROPDOWN].left = width - 37; - widgets[WIDX_RIDE_TYPE_DROPDOWN].right = width - 27; - widgets[WIDX_RIDE_TYPE_DROPDOWN].top = height - 16; - widgets[WIDX_RIDE_TYPE_DROPDOWN].bottom = height - 5; - - if (!GetGameState().Cheats.AllowArbitraryRideTypeChanges) - { - widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::Empty; - widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Empty; - } - else - { - widgets[WIDX_RIDE_TYPE].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_RIDE_TYPE].text = ride->GetRideTypeDescriptor().Naming.Name; - widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WindowWidgetType::Button; - } - - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - - if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) - { - widgets[WIDX_OPEN].type = WindowWidgetType::Empty; - widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; - widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; -#ifdef __SIMULATE_IN_RIDE_WINDOW__ - if (ride->SupportsStatus(RideStatus::Simulating)) - widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::ImgBtn; -#endif - widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RideStatus::Testing) ? WindowWidgetType::ImgBtn - : WindowWidgetType::Empty; - widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; - - widgetHeight = 62; - if (widgets[WIDX_SIMULATE_LIGHT].type != WindowWidgetType::Empty) - { - widgets[WIDX_SIMULATE_LIGHT].top = widgetHeight; - widgets[WIDX_SIMULATE_LIGHT].bottom = widgetHeight + 13; - widgetHeight += 14; - } - if (widgets[WIDX_TEST_LIGHT].type != WindowWidgetType::Empty) - { - widgets[WIDX_TEST_LIGHT].top = widgetHeight; - widgets[WIDX_TEST_LIGHT].bottom = widgetHeight + 13; - widgetHeight += 14; - } - widgets[WIDX_OPEN_LIGHT].top = widgetHeight; - widgets[WIDX_OPEN_LIGHT].bottom = widgetHeight + 13; - widgetHeight += 14 - 24 + RCT1_LIGHT_OFFSET; - } - else - { - widgets[WIDX_OPEN].type = WindowWidgetType::FlatBtn; - widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_SIMULATE_LIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_TEST_LIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; - widgetHeight = 46; - } - for (i = WIDX_CLOSE_LIGHT; i <= WIDX_OPEN_LIGHT; i++) - { - widgets[i].left = width - 20; - widgets[i].right = width - 7; - } - for (i = WIDX_OPEN; i <= WIDX_DEMOLISH; i++, widgetHeight += 24) - { - widgets[i].left = width - 25; - widgets[i].right = width - 2; - widgets[i].top = widgetHeight; - 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[_viewIndex - 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 - || trackType == TrackElemType::DiagBlockBrakes) - { - 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[EnumValue(vehicle->status)]; - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SINGLE_SESSION) - && vehicle->status <= Vehicle::Status::UnloadingPassengers) - { - stringId = SingleSessionVehicleStatusNames[EnumValue(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 (_viewIndex == 0) - return GetStatusOverallView(ft); - if (ride != nullptr && _viewIndex <= ride->NumTrains) - return GetStatusVehicle(ft); - if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) - return GetStatusOverallView(ft); - return GetStatusStation(ft); - } - - void MainOnDraw(DrawPixelInfo& dpi) - { - 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 (_viewIndex != 0) - { - if (_viewIndex > ride->NumTrains) - { - ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.station).number); - ft.Add(_viewIndex - ride->NumTrains); - } - else - { - ft.Add(GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle).number); - ft.Add(_viewIndex); - } - } - else - { - ft.Add(STR_OVERALL_VIEW); - } - - auto* widget = &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 = &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 VehicleOnMouseUp(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 VehicleOnMouseDown(WidgetIndex widgetIndex) - { - 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 VehicleOnDropdown(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].SubTypeId; - ride->SetRideEntry(newRideType); - } - } - break; - } - } - - void VehicleUpdate() - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_2); - } - - OpenRCT2String VehicleTooltip(const WidgetIndex widgetIndex, StringId fallback) - { - auto ride = GetRide(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 = PageWidgets[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 - widgets[WIDX_VEHICLE_TYPE].text = rideEntry->naming.Name; - - // Trains - if (rideEntry->cars_per_flat_ride > 1 || GetGameState().Cheats.DisableTrainLengthLimit) - { - widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Spinner; - widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Button; - } - else - { - widgets[WIDX_VEHICLE_TRAINS].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_TRAINS_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_TRAINS_DECREASE].type = WindowWidgetType::Empty; - } - - // Cars per train - if (rideEntry->zero_cars + 1 < rideEntry->max_cars_in_train || GetGameState().Cheats.DisableTrainLengthLimit) - { - widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Spinner; - widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Button; - } - else - { - widgets[WIDX_VEHICLE_CARS_PER_TRAIN].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE].type = WindowWidgetType::Empty; - } - - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_ALLOW_REVERSED_TRAINS) - || (GetGameState().Cheats.DisableTrainLengthLimit - && !ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))) - { - 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 - { - 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) - { - widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_1_CAR_PER_TRAIN; - } - else - { - widgets[WIDX_VEHICLE_CARS_PER_TRAIN].text = STR_X_CARS_PER_TRAIN; - } - } - - void VehicleOnDraw(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; + // 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(abs(rideEntry->excitement_multiplier)); - StringId stringId = rideEntry->excitement_multiplier > 0 ? STR_EXCITEMENT_FACTOR : STR_EXCITEMENT_FACTOR_NEGATIVE; - DrawTextBasic(dpi, screenCoords, stringId, ft); - } + ft.Add(rideEntry->capacity); + DrawTextBasic(dpi, screenCoords, STR_CAPACITY, ft); - // Intensity Factor - if (rideEntry->intensity_multiplier != 0) - { - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - if (lineHeight != 10) - screenCoords.x += 150; - else + // Excitement Factor + if (rideEntry->excitement_multiplier != 0) + { 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 VehicleOnScrollDraw(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 = &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; + 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); } - if (ride->type == RIDE_TYPE_REVERSER_ROLLER_COASTER) + // Intensity Factor + if (rideEntry->intensity_multiplier != 0) { - VehicleDrawInfo tmp = *(nextSpriteToDraw - 1); - *(nextSpriteToDraw - 1) = *(nextSpriteToDraw - 2); - *(nextSpriteToDraw - 2) = tmp; + 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; } - VehicleDrawInfo* current = nextSpriteToDraw; - while (--current >= trainCarImages) - GfxDrawSprite(dpi, current->imageId, { current->x, current->y }); + // Nausea Factor + if (rideEntry->nausea_multiplier != 0) + { + screenCoords.y += LIST_ROW_HEIGHT; - startX += 36; + 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 VehicleOnScrollDraw(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 = &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 - void ModeTweakIncrease() - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; - uint8_t maxValue = operatingSettings.MaxValue; - uint8_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 0 : operatingSettings.MinValue; - - if (GetGameState().Cheats.UnlockOperatingLimits) + void ModeTweakIncrease() { - maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; - } + auto ride = GetRide(rideId); + if (ride == nullptr) + return; - uint8_t increment = ride->mode == RideMode::Dodgems ? 10 : 1; + const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; + uint8_t maxValue = operatingSettings.MaxValue; + uint8_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 0 : operatingSettings.MinValue; - SetOperatingSetting( - rideId, RideSetSetting::Operation, std::clamp(ride->operation_option + increment, minValue, maxValue)); - } - - void ModeTweakDecrease() - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; - uint8_t maxValue = operatingSettings.MaxValue; - uint8_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 0 : operatingSettings.MinValue; - if (GetGameState().Cheats.UnlockOperatingLimits) - { - maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; - } - - uint8_t decrement = ride->mode == RideMode::Dodgems ? 10 : 1; - - SetOperatingSetting( - rideId, RideSetSetting::Operation, std::clamp(ride->operation_option - decrement, minValue, maxValue)); - } - - void ModeDropdown(Widget* widget) - { - Widget* dropdownWidget; - - dropdownWidget = widget - 1; - auto ride = GetRide(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)) + if (GetGameState().Cheats.UnlockOperatingLimits) { - gDropdownItems[numAvailableModes].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numAvailableModes].Args = RideModeNames[i]; + maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; + } - if (ride->mode == static_cast(i)) - checkedIndex = numAvailableModes; + uint8_t increment = ride->mode == RideMode::Dodgems ? 10 : 1; - numAvailableModes++; + SetOperatingSetting( + rideId, RideSetSetting::Operation, std::clamp(ride->operation_option + increment, minValue, maxValue)); + } + + void ModeTweakDecrease() + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; + uint8_t maxValue = operatingSettings.MaxValue; + uint8_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 0 : operatingSettings.MinValue; + if (GetGameState().Cheats.UnlockOperatingLimits) + { + maxValue = OpenRCT2::Limits::CheatsMaxOperatingLimit; + } + + uint8_t decrement = ride->mode == RideMode::Dodgems ? 10 : 1; + + SetOperatingSetting( + rideId, RideSetSetting::Operation, std::clamp(ride->operation_option - decrement, minValue, maxValue)); + } + + void ModeDropdown(Widget* widget) + { + Widget* dropdownWidget; + + dropdownWidget = widget - 1; + auto ride = GetRide(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( + { 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) + { + Dropdown::SetChecked(checkedIndex, true); } } - WindowDropdownShowTextCustomWidth( - { 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) + void LoadDropdown(Widget* widget) { - Dropdown::SetChecked(checkedIndex, true); - } - } - - void LoadDropdown(Widget* widget) - { - auto ride = GetRide(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( - { 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); - } - - void OperatingOnMouseUp(WidgetIndex widgetIndex) - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); + auto ride = GetRide(rideId); + if (ride == nullptr) 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_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; - } - } - void OperatingResize() - { - WindowSetResize(*this, 316, 186, 316, 186); - } - - void OperatingOnMouseDown(WidgetIndex widgetIndex) - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - uint8_t upperBound, lowerBound; - switch (widgetIndex) - { - case WIDX_MODE_TWEAK: - OperatingTweakTextInput(*ride); - break; - case WIDX_MODE_TWEAK_INCREASE: - ModeTweakIncrease(); - break; - case WIDX_MODE_TWEAK_DECREASE: - ModeTweakDecrease(); - break; - case WIDX_LIFT_HILL_SPEED_INCREASE: - upperBound = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : ride->GetRideTypeDescriptor().LiftData.maximum_speed; - lowerBound = GetGameState().Cheats.UnlockOperatingLimits ? 0 - : ride->GetRideTypeDescriptor().LiftData.minimum_speed; - SetOperatingSetting( - rideId, RideSetSetting::LiftHillSpeed, - std::clamp(ride->lift_hill_speed + 1, lowerBound, upperBound)); - break; - case WIDX_LIFT_HILL_SPEED_DECREASE: - upperBound = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : ride->GetRideTypeDescriptor().LiftData.maximum_speed; - lowerBound = GetGameState().Cheats.UnlockOperatingLimits ? 0 - : ride->GetRideTypeDescriptor().LiftData.minimum_speed; - SetOperatingSetting( - rideId, RideSetSetting::LiftHillSpeed, - std::clamp(ride->lift_hill_speed - 1, lowerBound, upperBound)); - break; - case WIDX_MINIMUM_LENGTH: - OperatingLengthWindow(WIDX_MINIMUM_LENGTH); - break; - case WIDX_MAXIMUM_LENGTH: - OperatingLengthWindow(WIDX_MAXIMUM_LENGTH); - break; - case WIDX_MINIMUM_LENGTH_INCREASE: - upperBound = OpenRCT2::Limits::MaxWaitingTime; - lowerBound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MinWaitingTime, - std::clamp(ride->min_waiting_time + 1, lowerBound, upperBound)); - break; - case WIDX_MINIMUM_LENGTH_DECREASE: - upperBound = OpenRCT2::Limits::MaxWaitingTime; - lowerBound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MinWaitingTime, - std::clamp(ride->min_waiting_time - 1, lowerBound, upperBound)); - break; - case WIDX_MAXIMUM_LENGTH_INCREASE: - upperBound = OpenRCT2::Limits::MaxWaitingTime; - lowerBound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MaxWaitingTime, - std::clamp(ride->max_waiting_time + 1, lowerBound, upperBound)); - break; - case WIDX_MAXIMUM_LENGTH_DECREASE: - upperBound = OpenRCT2::Limits::MaxWaitingTime; - lowerBound = 0; - SetOperatingSetting( - rideId, RideSetSetting::MaxWaitingTime, - std::clamp(ride->max_waiting_time - 1, lowerBound, upperBound)); - break; - case WIDX_MODE_DROPDOWN: - ModeDropdown(&widgets[widgetIndex]); - break; - case WIDX_LOAD_DROPDOWN: - LoadDropdown(&widgets[widgetIndex]); - break; - case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE: - upperBound = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : OpenRCT2::Limits::MaxCircuitsPerRide; - lowerBound = 1; - SetOperatingSetting( - rideId, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits + 1, lowerBound, upperBound)); - break; - case WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE: - upperBound = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : OpenRCT2::Limits::MaxCircuitsPerRide; - lowerBound = 1; - SetOperatingSetting( - rideId, RideSetSetting::NumCircuits, std::clamp(ride->num_circuits - 1, lowerBound, upperBound)); - break; - } - } - - void OperatingLengthWindow(WidgetIndex widgetIndex) - { - auto ride = GetRide(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(this, 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 = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : operatingSettings.MaxValue; - int16_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 0 : operatingSettings.MinValue; - - const auto& title = 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(this, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4); - } - - void OperatingOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (dropdownIndex == -1) - return; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - switch (widgetIndex) - { - case WIDX_MODE_DROPDOWN: + auto dropdownWidget = widget - 1; + for (auto i = 0; i < 5; i++) { - RideMode rideMode = RideMode::NullMode; - auto availableModes = ride->GetAvailableModes(); - auto modeInDropdownIndex = -1; - for (RideMode rideModeIndex = RideMode::Normal; rideModeIndex < RideMode::Count; rideModeIndex++) + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[i].Args = VehicleLoadNames[i]; + } + WindowDropdownShowTextCustomWidth( + { 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); + } + + void OperatingOnMouseUp(WidgetIndex widgetIndex) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + 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_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; + } + } + + void OperatingResize() + { + WindowSetResize(*this, 316, 186, 316, 186); + } + + void OperatingOnMouseDown(WidgetIndex widgetIndex) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + uint8_t upperBound, lowerBound; + switch (widgetIndex) + { + case WIDX_MODE_TWEAK: + OperatingTweakTextInput(*ride); + break; + case WIDX_MODE_TWEAK_INCREASE: + ModeTweakIncrease(); + break; + case WIDX_MODE_TWEAK_DECREASE: + ModeTweakDecrease(); + break; + case WIDX_LIFT_HILL_SPEED_INCREASE: + upperBound = GetGameState().Cheats.UnlockOperatingLimits + ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : ride->GetRideTypeDescriptor().LiftData.maximum_speed; + lowerBound = GetGameState().Cheats.UnlockOperatingLimits + ? 0 + : ride->GetRideTypeDescriptor().LiftData.minimum_speed; + SetOperatingSetting( + rideId, RideSetSetting::LiftHillSpeed, + std::clamp(ride->lift_hill_speed + 1, lowerBound, upperBound)); + break; + case WIDX_LIFT_HILL_SPEED_DECREASE: + upperBound = GetGameState().Cheats.UnlockOperatingLimits + ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : ride->GetRideTypeDescriptor().LiftData.maximum_speed; + lowerBound = GetGameState().Cheats.UnlockOperatingLimits + ? 0 + : ride->GetRideTypeDescriptor().LiftData.minimum_speed; + SetOperatingSetting( + rideId, RideSetSetting::LiftHillSpeed, + std::clamp(ride->lift_hill_speed - 1, lowerBound, upperBound)); + break; + case WIDX_MINIMUM_LENGTH: + OperatingLengthWindow(WIDX_MINIMUM_LENGTH); + break; + case WIDX_MAXIMUM_LENGTH: + OperatingLengthWindow(WIDX_MAXIMUM_LENGTH); + break; + case WIDX_MINIMUM_LENGTH_INCREASE: + upperBound = OpenRCT2::Limits::MaxWaitingTime; + lowerBound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MinWaitingTime, + std::clamp(ride->min_waiting_time + 1, lowerBound, upperBound)); + break; + case WIDX_MINIMUM_LENGTH_DECREASE: + upperBound = OpenRCT2::Limits::MaxWaitingTime; + lowerBound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MinWaitingTime, + std::clamp(ride->min_waiting_time - 1, lowerBound, upperBound)); + break; + case WIDX_MAXIMUM_LENGTH_INCREASE: + upperBound = OpenRCT2::Limits::MaxWaitingTime; + lowerBound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MaxWaitingTime, + std::clamp(ride->max_waiting_time + 1, lowerBound, upperBound)); + break; + case WIDX_MAXIMUM_LENGTH_DECREASE: + upperBound = OpenRCT2::Limits::MaxWaitingTime; + lowerBound = 0; + SetOperatingSetting( + rideId, RideSetSetting::MaxWaitingTime, + std::clamp(ride->max_waiting_time - 1, lowerBound, upperBound)); + break; + case WIDX_MODE_DROPDOWN: + ModeDropdown(&widgets[widgetIndex]); + break; + case WIDX_LOAD_DROPDOWN: + LoadDropdown(&widgets[widgetIndex]); + break; + case WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE: + upperBound = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : OpenRCT2::Limits::MaxCircuitsPerRide; + lowerBound = 1; + SetOperatingSetting( + rideId, RideSetSetting::NumCircuits, + std::clamp(ride->num_circuits + 1, lowerBound, upperBound)); + break; + case WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE: + upperBound = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : OpenRCT2::Limits::MaxCircuitsPerRide; + lowerBound = 1; + SetOperatingSetting( + rideId, RideSetSetting::NumCircuits, + std::clamp(ride->num_circuits - 1, lowerBound, upperBound)); + break; + } + } + + void OperatingLengthWindow(WidgetIndex widgetIndex) + { + auto ride = GetRide(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(this, 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 = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : operatingSettings.MaxValue; + int16_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 0 : operatingSettings.MinValue; + + const auto& title = 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(this, WIDX_MODE_TWEAK, title, STR_ENTER_VALUE, ft, buffer, 4); + } + + void OperatingOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + return; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + switch (widgetIndex) + { + case WIDX_MODE_DROPDOWN: { - if (availableModes & EnumToFlag(rideModeIndex)) + RideMode rideMode = RideMode::NullMode; + auto availableModes = ride->GetAvailableModes(); + auto modeInDropdownIndex = -1; + for (RideMode rideModeIndex = RideMode::Normal; rideModeIndex < RideMode::Count; rideModeIndex++) { - modeInDropdownIndex++; - if (modeInDropdownIndex == dropdownIndex) + if (availableModes & EnumToFlag(rideModeIndex)) { - rideMode = rideModeIndex; - break; + modeInDropdownIndex++; + if (modeInDropdownIndex == dropdownIndex) + { + rideMode = rideModeIndex; + break; + } } } + if (rideMode != RideMode::NullMode) + SetOperatingSetting(rideId, RideSetSetting::Mode, static_cast(rideMode)); + 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; } - case WIDX_LOAD_DROPDOWN: - SetOperatingSetting( - rideId, RideSetSetting::Departure, (ride->depart_flags & ~RIDE_DEPART_WAIT_FOR_LOAD_MASK) | dropdownIndex); - break; } - } - void OperatingUpdate() - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_3); - - auto ride = GetRide(rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) + void OperatingUpdate() { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING; - Invalidate(); + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_3); + + auto ride = GetRide(rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) + { + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING; + Invalidate(); + } } - } - void OperatingOnTextInput(WidgetIndex widgetIndex, std::string_view text) - { - if (text.empty()) - return; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - if (widgetIndex == WIDX_MODE_TWEAK) + void OperatingOnTextInput(WidgetIndex widgetIndex, std::string_view text) { - const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; - uint32_t maxValue = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit - : operatingSettings.MaxValue; - uint32_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 0 : operatingSettings.MinValue; + if (text.empty()) + return; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + if (widgetIndex == WIDX_MODE_TWEAK) + { + const auto& operatingSettings = ride->GetRideTypeDescriptor().OperatingSettings; + uint32_t maxValue = GetGameState().Cheats.UnlockOperatingLimits ? OpenRCT2::Limits::CheatsMaxOperatingLimit + : operatingSettings.MaxValue; + uint32_t minValue = GetGameState().Cheats.UnlockOperatingLimits ? 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 + } + } + } + + void OperatingOnPrepareDraw() + { + StringId format, caption, tooltip; + + auto* newWidgets = PageWidgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + // Widget setup + 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))) + { + widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Label; + widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Spinner; + widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Button; + ft.Rewind(); + ft.Increment(20); + ft.Add(ride->lift_hill_speed); + } + else + { + widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Empty; + widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Empty; + widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Empty; + } + + // Number of circuits + if (ride->CanHaveMultipleCircuits()) + { + widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Label; + widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Spinner; + widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Button; + ft.Rewind(); + ft.Increment(22); + ft.Add(ride->num_circuits); + } + else + { + widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Empty; + widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Empty; + widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Empty; + 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()) + { + widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Checkbox; + widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].tooltip = STR_LEAVE_IF_ANOTHER_VEHICLE_ARRIVES_TIP; + 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 + { + 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)) + { + widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Checkbox; + widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].text = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS; + widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].tooltip = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP; + } + else + { + widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Empty; + } + + // Mode + widgets[WIDX_MODE].text = RideModeNames[EnumValue(ride->mode)]; + + // Waiting + widgets[WIDX_LOAD].text = VehicleLoadNames[(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK)]; + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS)) + { + widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Checkbox; + widgets[WIDX_LOAD].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Button; + + widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; + widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Spinner; + widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button; + + widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; + widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Spinner; + widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; + 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) + pressed_widgets |= (1uLL << WIDX_LOAD_CHECKBOX); + } + else + { + widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Empty; + widgets[WIDX_LOAD].type = WindowWidgetType::Empty; + widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Empty; + + widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; + widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Empty; + widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; + + widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; + widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Empty; + widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; + } + + if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES) + pressed_widgets |= (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); + if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) + pressed_widgets |= (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX); + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) + pressed_widgets |= (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX); + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH) + pressed_widgets |= (1uLL << WIDX_MAXIMUM_LENGTH_CHECKBOX); + + // Mode specific functionality 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 - } - } - } - - void OperatingOnPrepareDraw() - { - StringId format, caption, tooltip; - - auto* newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); - } - - SetPressedTab(); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - // Widget setup - 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))) - { - widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Label; - widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Spinner; - widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Button; ft.Rewind(); - ft.Increment(20); - ft.Add(ride->lift_hill_speed); + 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) + { + widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Label; + widgets[WIDX_MODE_TWEAK_LABEL].text = caption; + widgets[WIDX_MODE_TWEAK_LABEL].tooltip = tooltip; + widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Spinner; + widgets[WIDX_MODE_TWEAK].text = format; + widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Button; + pressed_widgets &= ~(1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); + } + else + { + widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Empty; + widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Empty; + widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Empty; + } + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - else + + void OperatingOnDraw(DrawPixelInfo& dpi) { - widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WindowWidgetType::Empty; - widgets[WIDX_LIFT_HILL_SPEED].type = WindowWidgetType::Empty; - widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_LIFT_HILL_SPEED_DECREASE].type = WindowWidgetType::Empty; + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + // Horizontal rule between mode settings and depart settings + GfxFillRectInset( + dpi, + { windowPos + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 }, + windowPos + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } }, + 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, windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 }, + STR_BLOCK_SECTIONS, ft, COLOUR_BLACK); + } } - // Number of circuits - if (ride->CanHaveMultipleCircuits()) - { - widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Label; - widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Spinner; - widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_DECREASE].type = WindowWidgetType::Button; - ft.Rewind(); - ft.Increment(22); - ft.Add(ride->num_circuits); - } - else - { - widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WindowWidgetType::Empty; - widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WindowWidgetType::Empty; - widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_INCREASE].type = WindowWidgetType::Empty; - 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()) - { - widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].type = WindowWidgetType::Checkbox; - widgets[WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX].tooltip = STR_LEAVE_IF_ANOTHER_VEHICLE_ARRIVES_TIP; - 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 - { - 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)) - { - widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Checkbox; - widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].text = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS; - widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].tooltip = STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP; - } - else - { - widgets[WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX].type = WindowWidgetType::Empty; - } - - // Mode - widgets[WIDX_MODE].text = RideModeNames[EnumValue(ride->mode)]; - - // Waiting - widgets[WIDX_LOAD].text = VehicleLoadNames[(ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK)]; - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS)) - { - widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Checkbox; - widgets[WIDX_LOAD].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Button; - - widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; - widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Spinner; - widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Button; - - widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Checkbox; - widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Spinner; - widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Button; - 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) - pressed_widgets |= (1uLL << WIDX_LOAD_CHECKBOX); - } - else - { - widgets[WIDX_LOAD_CHECKBOX].type = WindowWidgetType::Empty; - widgets[WIDX_LOAD].type = WindowWidgetType::Empty; - widgets[WIDX_LOAD_DROPDOWN].type = WindowWidgetType::Empty; - - widgets[WIDX_MINIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; - widgets[WIDX_MINIMUM_LENGTH].type = WindowWidgetType::Empty; - widgets[WIDX_MINIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_MINIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; - - widgets[WIDX_MAXIMUM_LENGTH_CHECKBOX].type = WindowWidgetType::Empty; - widgets[WIDX_MAXIMUM_LENGTH].type = WindowWidgetType::Empty; - widgets[WIDX_MAXIMUM_LENGTH_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_MAXIMUM_LENGTH_DECREASE].type = WindowWidgetType::Empty; - } - - if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES) - pressed_widgets |= (1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); - if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) - pressed_widgets |= (1uLL << WIDX_SYNCHRONISE_WITH_ADJACENT_STATIONS_CHECKBOX); - if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) - pressed_widgets |= (1uLL << WIDX_MINIMUM_LENGTH_CHECKBOX); - if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH) - 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) - { - widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Label; - widgets[WIDX_MODE_TWEAK_LABEL].text = caption; - widgets[WIDX_MODE_TWEAK_LABEL].tooltip = tooltip; - widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Spinner; - widgets[WIDX_MODE_TWEAK].text = format; - widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Button; - pressed_widgets &= ~(1uLL << WIDX_LEAVE_WHEN_ANOTHER_ARRIVES_CHECKBOX); - } - else - { - widgets[WIDX_MODE_TWEAK_LABEL].type = WindowWidgetType::Empty; - widgets[WIDX_MODE_TWEAK].type = WindowWidgetType::Empty; - widgets[WIDX_MODE_TWEAK_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_MODE_TWEAK_DECREASE].type = WindowWidgetType::Empty; - } - - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - } - - void OperatingOnDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - // Horizontal rule between mode settings and depart settings - GfxFillRectInset( - dpi, - { windowPos + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, 103 }, - windowPos + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].right - 5, 104 } }, - 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, windowPos + ScreenCoordsXY{ 21, ride->mode == RideMode::PoweredLaunchBlockSectioned ? 89 : 61 }, - STR_BLOCK_SECTIONS, ft, COLOUR_BLACK); - } - } - #pragma endregion #pragma region Maintenance - void LocateMechanic() - { - auto ride = GetRide(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 + void LocateMechanic() { - auto intent = Intent(WindowClass::Peep); - intent.PutExtra(INTENT_EXTRA_PEEP, mechanic); - ContextOpenIntent(&intent); - } - } - - void MaintenanceDrawBar(DrawPixelInfo& dpi, const ScreenCoordsXY& coords, int32_t value, int32_t colour) const - { - GfxFillRectInset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, colours[1], INSET_RECT_F_30); - if (colour & kBarBlink) - { - colour &= ~kBarBlink; - if (GameIsNotPaused() && (gCurrentRealTimeTicks & 8)) + auto ride = GetRide(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); + } } - value = ((186 * ((value * 2) & 0xFF)) >> 8) & 0xFF; - if (value > 2) + void MaintenanceDrawBar(DrawPixelInfo& dpi, const ScreenCoordsXY& coords, int32_t value, int32_t colour) const { - GfxFillRectInset(dpi, { coords + ScreenCoordsXY{ 2, 1 }, coords + ScreenCoordsXY{ value + 1, 7 } }, colour, 0); + GfxFillRectInset(dpi, { coords, coords + ScreenCoordsXY{ 149, 8 } }, colours[1], INSET_RECT_F_30); + if (colour & kBarBlink) + { + colour &= ~kBarBlink; + 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); + } } - } - void MaintenanceOnMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void MaintenanceOnMouseUp(WidgetIndex 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_LOCATE_MECHANIC: - LocateMechanic(); - break; - case WIDX_REFURBISH_RIDE: - ContextOpenDetailWindow(WD_REFURBISH_RIDE, number); - break; - } - } - - void MaintenanceResize() - { - WindowSetResize(*this, 316, 135, 316, 135); - } - - void MaintenanceOnMouseDown(WidgetIndex widgetIndex) - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - Widget* dropdownWidget = &widgets[widgetIndex]; - int32_t j, numItems; - - 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( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, 7, widgets[widgetIndex].right - dropdownWidget->left); - - Dropdown::SetChecked(ride->inspection_interval, true); - break; - - case WIDX_FORCE_BREAKDOWN: - numItems = 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[numItems].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numItems].Args = RideBreakdownReasonNames[i]; - numItems++; - } - } - if (numItems == 1) - { - ContextShowError(STR_DEBUG_NO_BREAKDOWNS_AVAILABLE, STR_NONE, {}); - } - else - { - WindowDropdownShowText( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], Dropdown::Flag::StayOpen, numItems); - - numItems = 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(numItems, true); - break; - } - gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numItems].Args = RideBreakdownReasonNames[i]; - numItems++; - } - } - } - - if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING) == 0) - { - Dropdown::SetDisabled(0, true); - } - } - break; - } - } - - void MaintenanceOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (dropdownIndex == -1) - return; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - switch (widgetIndex) - { - case WIDX_INSPECTION_INTERVAL_DROPDOWN: - SetOperatingSetting(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, number); + 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; - } - 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; + case WIDX_LOCATE_MECHANIC: + LocateMechanic(); + break; + case WIDX_REFURBISH_RIDE: + ContextOpenDetailWindow(WD_REFURBISH_RIDE, number); + break; + } + } + + void MaintenanceResize() + { + WindowSetResize(*this, 316, 135, 316, 135); + } + + void MaintenanceOnMouseDown(WidgetIndex widgetIndex) + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + Widget* dropdownWidget = &widgets[widgetIndex]; + int32_t j, numItems; + + 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( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 7, widgets[widgetIndex].right - dropdownWidget->left); + + Dropdown::SetChecked(ride->inspection_interval, true); + break; + + case WIDX_FORCE_BREAKDOWN: + numItems = 1; for (j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++) { if (rideEntry->ride_type[j] != RIDE_TYPE_NULL) break; } - int32_t i; - int32_t numItems = 1; - for (i = 0; i < BREAKDOWN_COUNT; i++) + 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)) @@ -3865,1441 +3737,2807 @@ private: if (ride->NumTrains != 1) continue; } - if (numItems == dropdownIndex) - break; + gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numItems].Args = RideBreakdownReasonNames[i]; numItems++; } } - RidePrepareBreakdown(*ride, i); - } - break; - } - } - - void MaintenanceUpdate() - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_4); - - auto ride = GetRide(rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE) - { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE; - Invalidate(); - } - } - - void MaintenanceOnPrepareDraw() - { - auto newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); - } - - SetPressedTab(); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval]; - - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - - if (gConfigGeneral.DebuggingTools && NetworkGetMode() == NETWORK_MODE_NONE) - { - widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::FlatBtn; - } - else - { - widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::Empty; - } - - if (ride->GetRideTypeDescriptor().AvailableBreakdowns == 0 - || !(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) - { - disabled_widgets |= (1uLL << WIDX_REFURBISH_RIDE); - widgets[WIDX_REFURBISH_RIDE].tooltip = STR_CANT_REFURBISH_NOT_NEEDED; - } - else - { - disabled_widgets &= ~(1uLL << WIDX_REFURBISH_RIDE); - widgets[WIDX_REFURBISH_RIDE].tooltip = STR_REFURBISH_RIDE_TIP; - } - } - - void MaintenanceOnDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - // Locate mechanic button image - Widget* widget = &widgets[WIDX_LOCATE_MECHANIC]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; - auto image = ImageId(SPR_MECHANIC, COLOUR_BLACK, GetGameState().StaffMechanicColour); - GfxDrawSprite(dpi, image, screenCoords); - - // Inspection label - widget = &widgets[WIDX_INSPECTION_INTERVAL]; - screenCoords = windowPos + ScreenCoordsXY{ 4, widget->top + 1 }; - DrawTextBasic(dpi, screenCoords, STR_INSPECTION); - - // Reliability - widget = &widgets[WIDX_PAGE_BACKGROUND]; - 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( - 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(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 (numItems == 1) { - if (peep->IsMechanic()) + ContextShowError(STR_DEBUG_NO_BREAKDOWNS_AVAILABLE, STR_NONE, {}); + } + else + { + WindowDropdownShowText( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, + dropdownWidget->height() + 1, colours[1], Dropdown::Flag::StayOpen, numItems); + + numItems = 1; + int32_t breakdownReason = ride->breakdown_reason_pending; + if (breakdownReason != BREAKDOWN_NONE && (ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING)) { - stringId = STR_CALLING_MECHANIC; - break; + 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(numItems, true); + break; + } + gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numItems].Args = RideBreakdownReasonNames[i]; + numItems++; + } + } + } + + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BREAKDOWN_PENDING) == 0) + { + Dropdown::SetDisabled(0, true); } } break; - } - case RIDE_MECHANIC_STATUS_HEADING: - stringId = STR_MEHCANIC_IS_HEADING_FOR_THE_RIDE; + } + } + + void MaintenanceOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + return; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + switch (widgetIndex) + { + case WIDX_INSPECTION_INTERVAL_DROPDOWN: + SetOperatingSetting(rideId, RideSetSetting::InspectionInterval, dropdownIndex); 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; + + 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, 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 numItems = 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 (numItems == dropdownIndex) + break; + numItems++; + } + } + RidePrepareBreakdown(*ride, i); + } break; } + } - if (stringId != STR_EMPTY) + void MaintenanceUpdate() + { + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_4); + + auto ride = GetRide(rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE) { - if (stringId == STR_CALLING_MECHANIC || stringId == STR_NO_MECHANICS_ARE_HIRED_MESSAGE) + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE; + Invalidate(); + } + } + + void MaintenanceOnPrepareDraw() + { + auto newWidgets = PageWidgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval]; + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + + if (gConfigGeneral.DebuggingTools && NetworkGetMode() == NETWORK_MODE_NONE) + { + widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::FlatBtn; + } + else + { + widgets[WIDX_FORCE_BREAKDOWN].type = WindowWidgetType::Empty; + } + + if (ride->GetRideTypeDescriptor().AvailableBreakdowns == 0 + || !(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) + { + disabled_widgets |= (1uLL << WIDX_REFURBISH_RIDE); + widgets[WIDX_REFURBISH_RIDE].tooltip = STR_CANT_REFURBISH_NOT_NEEDED; + } + else + { + disabled_widgets &= ~(1uLL << WIDX_REFURBISH_RIDE); + widgets[WIDX_REFURBISH_RIDE].tooltip = STR_REFURBISH_RIDE_TIP; + } + } + + void MaintenanceOnDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + // Locate mechanic button image + Widget* widget = &widgets[WIDX_LOCATE_MECHANIC]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; + auto image = ImageId(SPR_MECHANIC, COLOUR_BLACK, GetGameState().StaffMechanicColour); + GfxDrawSprite(dpi, image, screenCoords); + + // Inspection label + widget = &widgets[WIDX_INSPECTION_INTERVAL]; + screenCoords = windowPos + ScreenCoordsXY{ 4, widget->top + 1 }; + DrawTextBasic(dpi, screenCoords, STR_INSPECTION); + + // Reliability + widget = &widgets[WIDX_PAGE_BACKGROUND]; + 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( + 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(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) { - DrawTextWrapped(dpi, screenCoords, 280, stringId, {}, { TextAlignment::LEFT }); - } - else - { - auto staff = GetEntity(ride->mechanic); - if (staff != nullptr && staff->IsMechanic()) + case RIDE_MECHANIC_STATUS_CALLING: { - ft = Formatter(); - staff->FormatNameTo(ft); - DrawTextWrapped(dpi, screenCoords, 280, stringId, ft, { TextAlignment::LEFT }); + 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)) + int32_t HasTrackColour(const Ride& ride, int32_t trackColour) { - auto stationObj = ride.GetStationObject(); - if (stationObj != nullptr) + // Get station flags (shops don't have them) + auto stationObjFlags = 0; + if (!ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) { - stationObjFlags = stationObj->Flags; + 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; } } - switch (trackColour) + void SetTrackColourScheme(const ScreenCoordsXY& screenPos) { - 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; - } - } + auto newColourScheme = static_cast(_rideColour); + auto info = GetMapCoordinatesFromPos(screenPos, EnumsToFlags(ViewportInteractionItem::Ride)); - void SetTrackColourScheme(const ScreenCoordsXY& screenPos) - { - auto newColourScheme = static_cast(_rideColour); - 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() != 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); - } - - void ColourClose() - { - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return; - - if (gCurrentToolWidget.window_classification != classification) - return; - - if (gCurrentToolWidget.window_number != number) - return; - - ToolCancel(); - } - - void ColourOnMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); + if (info.SpriteType != ViewportInteractionItem::Ride) 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_PAINT_INDIVIDUAL_AREA: - ToolSet(*this, WIDX_PAINT_INDIVIDUAL_AREA, Tool::PaintDown); - break; - case WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX: - auto ride = GetRide(rideId); - if (ride != nullptr) + if (info.Element->GetType() != TileElementType::Track) + return; + if (info.Element->AsTrack()->GetRideIndex() != 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); + } + + void ColourClose() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return; + + if (gCurrentToolWidget.window_classification != classification) + return; + + if (gCurrentToolWidget.window_number != number) + return; + + ToolCancel(); + } + + void ColourOnMouseUp(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_PAINT_INDIVIDUAL_AREA: + ToolSet(*this, WIDX_PAINT_INDIVIDUAL_AREA, Tool::PaintDown); + break; + case WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX: + auto ride = GetRide(rideId); + if (ride != nullptr) + { + const bool currentlyEnabled = ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS); + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::SellingItemColourIsRandom, currentlyEnabled ? 0 : 1, 0); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + } + } + + void ColourResize() + { + WindowSetResize(*this, 316, 207, 316, 207); + } + + void ColourOnMouseDown(WidgetIndex widgetIndex) + { + VehicleColour vehicleColour; + int32_t i, numItems; + StringId stringId; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto colourSchemeIndex = _rideColour; + auto dropdownWidget = &widgets[widgetIndex] - 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( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 4, widgets[widgetIndex].right - dropdownWidget->left); + + Dropdown::SetChecked(colourSchemeIndex, true); + break; + case WIDX_TRACK_MAIN_COLOUR: + WindowDropdownShowColour( + this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].main); + break; + case WIDX_TRACK_ADDITIONAL_COLOUR: + WindowDropdownShowColour( + this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].additional); + break; + case WIDX_TRACK_SUPPORT_COLOUR: + WindowDropdownShowColour( + this, &widgets[widgetIndex], 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( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + colours[1], 0, Dropdown::Flag::StayOpen, 4, widgets[widgetIndex].right - dropdownWidget->left); + + Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true); + break; + case WIDX_ENTRANCE_STYLE_DROPDOWN: + ShowEntranceStyleDropdown(); + 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( + { 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, + widgets[widgetIndex].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( + { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, + 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, &widgets[widgetIndex], colours[1], vehicleColour.Body); + break; + case WIDX_VEHICLE_TRIM_COLOUR: + vehicleColour = RideGetVehicleColour(*ride, _vehicleIndex); + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], vehicleColour.Trim); + break; + case WIDX_VEHICLE_TERNARY_COLOUR: + vehicleColour = RideGetVehicleColour(*ride, _vehicleIndex); + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], vehicleColour.Tertiary); + break; + } + } + + void ColourOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (dropdownIndex == -1) + return; + + switch (widgetIndex) + { + case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: + _rideColour = static_cast(dropdownIndex); + Invalidate(); + break; + case WIDX_TRACK_MAIN_COLOUR: { - const bool currentlyEnabled = ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS); auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::SellingItemColourIsRandom, currentlyEnabled ? 0 : 1, 0); + rideId, RideSetAppearanceType::TrackColourMain, ColourDropDownIndexToColour(dropdownIndex), + _rideColour); GameActions::Execute(&rideSetAppearanceAction); } break; - } - } - - void ColourResize() - { - WindowSetResize(*this, 316, 207, 316, 207); - } - - void ColourOnMouseDown(WidgetIndex widgetIndex) - { - VehicleColour vehicleColour; - int32_t i, numItems; - StringId stringId; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - auto colourSchemeIndex = _rideColour; - auto dropdownWidget = &widgets[widgetIndex] - 1; - - switch (widgetIndex) - { - case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: - for (i = 0; i < OpenRCT2::Limits::NumColourSchemes; i++) + case WIDX_TRACK_ADDITIONAL_COLOUR: { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = ColourSchemeNames[i]; + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::TrackColourAdditional, ColourDropDownIndexToColour(dropdownIndex), + _rideColour); + GameActions::Execute(&rideSetAppearanceAction); } - - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, 4, widgets[widgetIndex].right - dropdownWidget->left); - - Dropdown::SetChecked(colourSchemeIndex, true); break; - case WIDX_TRACK_MAIN_COLOUR: - WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].main); - break; - case WIDX_TRACK_ADDITIONAL_COLOUR: - WindowDropdownShowColour( - this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].additional); - break; - case WIDX_TRACK_SUPPORT_COLOUR: - WindowDropdownShowColour( - this, &widgets[widgetIndex], colours[1], ride->track_colour[colourSchemeIndex].supports); - break; - case WIDX_MAZE_STYLE_DROPDOWN: - for (i = 0; i < 4; i++) + case WIDX_TRACK_SUPPORT_COLOUR: { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = MazeOptions[i].text; + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::TrackColourSupports, ColourDropDownIndexToColour(dropdownIndex), + _rideColour); + GameActions::Execute(&rideSetAppearanceAction); } - - WindowDropdownShowTextCustomWidth( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - colours[1], 0, Dropdown::Flag::StayOpen, 4, widgets[widgetIndex].right - dropdownWidget->left); - - Dropdown::SetChecked(ride->track_colour[colourSchemeIndex].supports, true); break; - case WIDX_ENTRANCE_STYLE_DROPDOWN: - ShowEntranceStyleDropdown(); - break; - case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: - for (i = 0; i < 3; i++) + case WIDX_MAZE_STYLE_DROPDOWN: { - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = (GetRideComponentName(ride->GetRideTypeDescriptor().NameConvention.vehicle) - .singular - << 16) - | VehicleColourSchemeNames[i]; + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::MazeStyle, dropdownIndex, _rideColour); + GameActions::Execute(&rideSetAppearanceAction); } - - 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, - widgets[widgetIndex].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( - { windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - 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, &widgets[widgetIndex], colours[1], vehicleColour.Body); - break; - case WIDX_VEHICLE_TRIM_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, _vehicleIndex); - WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], vehicleColour.Trim); - break; - case WIDX_VEHICLE_TERNARY_COLOUR: - vehicleColour = RideGetVehicleColour(*ride, _vehicleIndex); - WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], vehicleColour.Tertiary); - break; - } - } - - void ColourOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (dropdownIndex == -1) - return; - - switch (widgetIndex) - { - case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: - _rideColour = static_cast(dropdownIndex); - Invalidate(); - break; - case WIDX_TRACK_MAIN_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::TrackColourMain, ColourDropDownIndexToColour(dropdownIndex), _rideColour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_TRACK_ADDITIONAL_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::TrackColourAdditional, ColourDropDownIndexToColour(dropdownIndex), - _rideColour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_TRACK_SUPPORT_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::TrackColourSupports, ColourDropDownIndexToColour(dropdownIndex), - _rideColour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_MAZE_STYLE_DROPDOWN: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::MazeStyle, dropdownIndex, _rideColour); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_ENTRANCE_STYLE_DROPDOWN: - { - if (static_cast(dropdownIndex) >= _entranceDropdownData.size()) + case WIDX_ENTRANCE_STYLE_DROPDOWN: { + if (static_cast(dropdownIndex) >= _entranceDropdownData.size()) + { + break; + } + auto objIndex = _entranceDropdownData[dropdownIndex].EntranceTypeId; + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::EntranceStyle, objIndex, 0); + rideSetAppearanceAction.SetCallback([objIndex](const GameAction*, const GameActions::Result* res) { + if (res->Error != GameActions::Status::Ok) + return; + GetGameState().LastEntranceStyle = objIndex; + }); + GameActions::Execute(&rideSetAppearanceAction); break; } - auto objIndex = _entranceDropdownData[dropdownIndex].EntranceTypeId; - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::EntranceStyle, objIndex, 0); - rideSetAppearanceAction.SetCallback([objIndex](const GameAction*, const GameActions::Result* res) { - if (res->Error != GameActions::Status::Ok) - return; - GetGameState().LastEntranceStyle = objIndex; - }); - GameActions::Execute(&rideSetAppearanceAction); + case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourScheme, dropdownIndex, 0); + GameActions::Execute(&rideSetAppearanceAction); + _vehicleIndex = 0; + } + break; + case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: + _vehicleIndex = dropdownIndex; + Invalidate(); + break; + case WIDX_VEHICLE_BODY_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourBody, ColourDropDownIndexToColour(dropdownIndex), + _vehicleIndex); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_VEHICLE_TRIM_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourTrim, ColourDropDownIndexToColour(dropdownIndex), + _vehicleIndex); + GameActions::Execute(&rideSetAppearanceAction); + } + break; + case WIDX_VEHICLE_TERNARY_COLOUR: + { + auto rideSetAppearanceAction = RideSetAppearanceAction( + rideId, RideSetAppearanceType::VehicleColourTernary, ColourDropDownIndexToColour(dropdownIndex), + _vehicleIndex); + GameActions::Execute(&rideSetAppearanceAction); + } break; } - case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: + } + + void ColourUpdate() + { + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_5); + WidgetInvalidate(*this, WIDX_VEHICLE_PREVIEW); + } + + void ColourOnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) + SetTrackColourScheme(screenCoords); + } + + void ColourOnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) + SetTrackColourScheme(screenCoords); + } + + void ColourOnPrepareDraw() + { + TrackColour trackColour; + VehicleColour vehicleColour; + + auto newWidgets = PageWidgets[page]; + if (widgets != newWidgets) { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourScheme, dropdownIndex, 0); - GameActions::Execute(&rideSetAppearanceAction); - _vehicleIndex = 0; + widgets = newWidgets; + InitScrollWidgets(); } - break; - case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: - _vehicleIndex = dropdownIndex; - Invalidate(); - break; - case WIDX_VEHICLE_BODY_COLOUR: + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; + auto ft = Formatter::Common(); + ft.Increment(16); + ride->FormatNameTo(ft); + + // Track colours + int32_t colourScheme = _rideColour; + trackColour = ride->track_colour[colourScheme]; + + // Maze style + const auto& rtd = ride->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourBody, ColourDropDownIndexToColour(dropdownIndex), - _vehicleIndex); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_VEHICLE_TRIM_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourTrim, ColourDropDownIndexToColour(dropdownIndex), - _vehicleIndex); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - case WIDX_VEHICLE_TERNARY_COLOUR: - { - auto rideSetAppearanceAction = RideSetAppearanceAction( - rideId, RideSetAppearanceType::VehicleColourTernary, ColourDropDownIndexToColour(dropdownIndex), - _vehicleIndex); - GameActions::Execute(&rideSetAppearanceAction); - } - break; - } - } - - void ColourUpdate() - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_5); - WidgetInvalidate(*this, WIDX_VEHICLE_PREVIEW); - } - - void ColourOnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) - { - if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) - SetTrackColourScheme(screenCoords); - } - - void ColourOnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) - { - if (widgetIndex == WIDX_PAINT_INDIVIDUAL_AREA) - SetTrackColourScheme(screenCoords); - } - - void ColourOnPrepareDraw() - { - TrackColour trackColour; - VehicleColour vehicleColour; - - auto newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); - } - - SetPressedTab(); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; - auto ft = Formatter::Common(); - ft.Increment(16); - ride->FormatNameTo(ft); - - // Track colours - int32_t colourScheme = _rideColour; - trackColour = ride->track_colour[colourScheme]; - - // Maze style - const auto& rtd = ride->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Button; - widgets[WIDX_MAZE_STYLE].text = MazeOptions[trackColour.supports].text; - } - else - { - widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::Empty; - widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Empty; - } - - // Track, multiple colour schemes - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR)) - { - widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; - widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::FlatBtn; - } - else - { - widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::Empty; - widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::Empty; - } - - // Track main colour - if (HasTrackColour(*ride, 0)) - { - widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::ColourBtn; - widgets[WIDX_TRACK_MAIN_COLOUR].image = GetColourButtonImage(trackColour.main); - } - else - { - widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::Empty; - } - - // Track additional colour - if (HasTrackColour(*ride, 1)) - { - widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::ColourBtn; - widgets[WIDX_TRACK_ADDITIONAL_COLOUR].image = GetColourButtonImage(trackColour.additional); - } - else - { - widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::Empty; - } - - // Selling item random colour checkbox - if (ride->HasRecolourableShopItems()) - { - widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Checkbox; - if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS)) - { - pressed_widgets |= (1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); + widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Button; + widgets[WIDX_MAZE_STYLE].text = MazeOptions[trackColour.supports].text; } else { - pressed_widgets &= ~(1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); - } - } - else - { - widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Empty; - } - - // Track supports colour - if (HasTrackColour(*ride, 2) && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::ColourBtn; - widgets[WIDX_TRACK_SUPPORT_COLOUR].image = GetColourButtonImage(trackColour.supports); - } - else - { - 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)) - widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Spinner; - else - widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Empty; - - // Entrance style - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT)) - { - widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Spinner; - widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Button; - - auto stringId = STR_NONE; - auto stationObj = ride->GetStationObject(); - if (stationObj != nullptr) - { - stringId = stationObj->NameStringId; - } - widgets[WIDX_ENTRANCE_STYLE].text = stringId; - } - else - { - widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Empty; - widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::Empty; - 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) - _vehicleIndex = 0; - - vehicleColour = RideGetVehicleColour(*ride, _vehicleIndex); - - widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Scroll; - widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::ColourBtn; - 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; - } + widgets[WIDX_MAZE_STYLE].type = WindowWidgetType::Empty; + widgets[WIDX_MAZE_STYLE_DROPDOWN].type = WindowWidgetType::Empty; } - // Additional colours - if (allowChangingTrimColour) + // Track, multiple colour schemes + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_MULTIPLE_TRACK_COLOUR)) { - widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::ColourBtn; - widgets[WIDX_VEHICLE_TRIM_COLOUR].image = GetColourButtonImage(vehicleColour.Trim); - if (allowChangingTernaryColour) + widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; + widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::FlatBtn; + } + else + { + widgets[WIDX_TRACK_COLOUR_SCHEME].type = WindowWidgetType::Empty; + widgets[WIDX_TRACK_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_PAINT_INDIVIDUAL_AREA].type = WindowWidgetType::Empty; + } + + // Track main colour + if (HasTrackColour(*ride, 0)) + { + widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::ColourBtn; + widgets[WIDX_TRACK_MAIN_COLOUR].image = GetColourButtonImage(trackColour.main); + } + else + { + widgets[WIDX_TRACK_MAIN_COLOUR].type = WindowWidgetType::Empty; + } + + // Track additional colour + if (HasTrackColour(*ride, 1)) + { + widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::ColourBtn; + widgets[WIDX_TRACK_ADDITIONAL_COLOUR].image = GetColourButtonImage(trackColour.additional); + } + else + { + widgets[WIDX_TRACK_ADDITIONAL_COLOUR].type = WindowWidgetType::Empty; + } + + // Selling item random colour checkbox + if (ride->HasRecolourableShopItems()) + { + widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Checkbox; + if (ride->HasLifecycleFlag(RIDE_LIFECYCLE_RANDOM_SHOP_COLOURS)) { - widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::ColourBtn; - widgets[WIDX_VEHICLE_TERNARY_COLOUR].image = GetColourButtonImage(vehicleColour.Tertiary); + pressed_widgets |= (1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); } else { - widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; + pressed_widgets &= ~(1uLL << WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX); } } else { + widgets[WIDX_SELL_ITEM_RANDOM_COLOUR_CHECKBOX].type = WindowWidgetType::Empty; + } + + // Track supports colour + if (HasTrackColour(*ride, 2) && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + widgets[WIDX_TRACK_SUPPORT_COLOUR].type = WindowWidgetType::ColourBtn; + widgets[WIDX_TRACK_SUPPORT_COLOUR].image = GetColourButtonImage(trackColour.supports); + } + else + { + 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)) + widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Spinner; + else + widgets[WIDX_TRACK_PREVIEW].type = WindowWidgetType::Empty; + + // Entrance style + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT)) + { + widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Spinner; + widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_ENTRANCE_STYLE_DROPDOWN].type = WindowWidgetType::Button; + + auto stringId = STR_NONE; + auto stationObj = ride->GetStationObject(); + if (stationObj != nullptr) + { + stringId = stationObj->NameStringId; + } + widgets[WIDX_ENTRANCE_STYLE].text = stringId; + } + else + { + widgets[WIDX_ENTRANCE_PREVIEW].type = WindowWidgetType::Empty; + widgets[WIDX_ENTRANCE_STYLE].type = WindowWidgetType::Empty; + 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) + _vehicleIndex = 0; + + vehicleColour = RideGetVehicleColour(*ride, _vehicleIndex); + + widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Scroll; + widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::ColourBtn; + 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) + { + widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::ColourBtn; + widgets[WIDX_VEHICLE_TRIM_COLOUR].image = GetColourButtonImage(vehicleColour.Trim); + if (allowChangingTernaryColour) + { + widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::ColourBtn; + widgets[WIDX_VEHICLE_TERNARY_COLOUR].image = GetColourButtonImage(vehicleColour.Tertiary); + } + else + { + widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; + } + } + else + { + widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty; + 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) + { + widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; + } + else + { + widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; + 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(_vehicleIndex + 1); + + // Vehicle index + if (vehicleColourSchemeType != 0) + { + widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Button; + widgets[WIDX_VEHICLE_COLOUR_INDEX].text = vehicleColourSchemeType == 1 ? STR_RIDE_COLOUR_TRAIN_VALUE + : STR_RIDE_COLOUR_VEHICLE_VALUE; + } + else + { + widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; + } + } + else + { + widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::Empty; widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty; 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) - { - widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Button; - } - else - { - widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; - 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(_vehicleIndex + 1); + ft.Increment(14); + ft.Add(ColourSchemeNames[colourScheme]); - // Vehicle index - if (vehicleColourSchemeType != 0) - { - widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Button; - widgets[WIDX_VEHICLE_COLOUR_INDEX].text = vehicleColourSchemeType == 1 ? STR_RIDE_COLOUR_TRAIN_VALUE - : STR_RIDE_COLOUR_VEHICLE_VALUE; - } - else - { - widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; - } - } - else - { - widgets[WIDX_VEHICLE_PREVIEW].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_COLOUR_SCHEME].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_COLOUR_INDEX].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_BODY_COLOUR].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_TRIM_COLOUR].type = WindowWidgetType::Empty; - widgets[WIDX_VEHICLE_TERNARY_COLOUR].type = WindowWidgetType::Empty; + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - ft.Rewind(); - ft.Increment(14); - ft.Add(ColourSchemeNames[colourScheme]); - - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - } - - void ColourOnDraw(DrawPixelInfo& dpi) - { - // TODO: This should use lists and identified sprites - DrawPixelInfo clippedDpi; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - DrawWidgets(dpi); - DrawTabImages(dpi); - - // Track / shop item preview - const auto& trackPreviewWidget = widgets[WIDX_TRACK_PREVIEW]; - if (trackPreviewWidget.type != WindowWidgetType::Empty) - GfxFillRect( - dpi, - { { windowPos + ScreenCoordsXY{ trackPreviewWidget.left + 1, trackPreviewWidget.top + 1 } }, - { windowPos + ScreenCoordsXY{ trackPreviewWidget.right - 1, trackPreviewWidget.bottom - 1 } } }, - PALETTE_INDEX_12); - - auto trackColour = ride->track_colour[_rideColour]; - - // - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr || rideEntry->shop_item[0] == ShopItem::None) + void ColourOnDraw(DrawPixelInfo& dpi) { - auto screenCoords = windowPos + ScreenCoordsXY{ trackPreviewWidget.left, trackPreviewWidget.top }; + // TODO: This should use lists and identified sprites + DrawPixelInfo clippedDpi; - // Track - const auto& rtd = ride->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + DrawWidgets(dpi); + DrawTabImages(dpi); + + // Track / shop item preview + const auto& trackPreviewWidget = widgets[WIDX_TRACK_PREVIEW]; + if (trackPreviewWidget.type != WindowWidgetType::Empty) + GfxFillRect( + dpi, + { { windowPos + ScreenCoordsXY{ trackPreviewWidget.left + 1, trackPreviewWidget.top + 1 } }, + { windowPos + ScreenCoordsXY{ trackPreviewWidget.right - 1, trackPreviewWidget.bottom - 1 } } }, + PALETTE_INDEX_12); + + auto trackColour = ride->track_colour[_rideColour]; + + // + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr || rideEntry->shop_item[0] == ShopItem::None) { - GfxDrawSprite(dpi, ImageId(MazeOptions[trackColour.supports].sprite), screenCoords); - } - else - { - auto typeDescriptor = ride->GetRideTypeDescriptor(); - int32_t spriteIndex = typeDescriptor.ColourPreview.Track; - if (spriteIndex != 0) + auto screenCoords = windowPos + ScreenCoordsXY{ trackPreviewWidget.left, trackPreviewWidget.top }; + + // Track + const auto& rtd = ride->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) { - GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.main, trackColour.additional), screenCoords); + GfxDrawSprite(dpi, ImageId(MazeOptions[trackColour.supports].sprite), screenCoords); } - - // Supports - spriteIndex = typeDescriptor.ColourPreview.Supports; - if (spriteIndex != 0) + else { - GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.supports), screenCoords); - } - } - } - else - { - auto screenCoords = 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 ((GetGameState().CurrentTicks % 64) == 0) - { - spriteColour++; - if (spriteColour >= COLOUR_NUM_NORMAL) + auto typeDescriptor = ride->GetRideTypeDescriptor(); + int32_t spriteIndex = typeDescriptor.ColourPreview.Track; + if (spriteIndex != 0) { - spriteColour = COLOUR_BLACK; + GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.main, trackColour.additional), screenCoords); } - } - 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 = widgets[WIDX_ENTRANCE_PREVIEW]; - if (entrancePreviewWidget.type != WindowWidgetType::Empty) - { - if (ClipDrawPixelInfo( - clippedDpi, dpi, - 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) + // Supports + spriteIndex = typeDescriptor.ColourPreview.Supports; + if (spriteIndex != 0) { - auto glassImageId = ImageId(stationObj->BaseImageId + 20).WithTransparency(trackColour.main); - GfxDrawSprite(clippedDpi, glassImageId, { 34, 20 }); + GfxDrawSprite(dpi, ImageId(spriteIndex, trackColour.supports), screenCoords); } } } + else + { + auto screenCoords = windowPos + + ScreenCoordsXY{ (trackPreviewWidget.left + trackPreviewWidget.right) / 2 - 8, + (trackPreviewWidget.bottom + trackPreviewWidget.top) / 2 - 6 }; - DrawTextEllipsised(dpi, { windowPos.x + 3, windowPos.y + 103 }, 97, STR_STATION_STYLE, {}); + 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 ((GetGameState().CurrentTicks % 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 = widgets[WIDX_ENTRANCE_PREVIEW]; + if (entrancePreviewWidget.type != WindowWidgetType::Empty) + { + if (ClipDrawPixelInfo( + clippedDpi, dpi, + 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, { windowPos.x + 3, windowPos.y + 103 }, 97, STR_STATION_STYLE, {}); + } } - } - void ColourOnScrollDraw(DrawPixelInfo& dpi, int32_t scrollIndex) const - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return; + void ColourOnScrollDraw(DrawPixelInfo& dpi, int32_t scrollIndex) const + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; - auto vehiclePreviewWidget = &widgets[WIDX_VEHICLE_PREVIEW]; - auto vehicleColour = RideGetVehicleColour(*ride, _vehicleIndex); + auto vehiclePreviewWidget = &widgets[WIDX_VEHICLE_PREVIEW]; + 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); + // 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 screenCoords = ScreenCoordsXY{ vehiclePreviewWidget->width() / 2, vehiclePreviewWidget->height() - 15 }; - // ? - auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR ? _vehicleIndex - : rideEntry->TabCar; + // ? + auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_MODE_DIFFERENT_PER_CAR + ? _vehicleIndex + : rideEntry->TabCar; - const auto& carEntry = rideEntry->Cars[RideEntryGetVehicleAtPosition( - ride->subtype, ride->num_cars_per_train, trainCarIndex)]; + const auto& carEntry = rideEntry->Cars[RideEntryGetVehicleAtPosition( + ride->subtype, ride->num_cars_per_train, trainCarIndex)]; - screenCoords.y += carEntry.tab_height; + screenCoords.y += carEntry.tab_height; - // Draw the coloured spinning vehicle - // 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; - auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary); - GfxDrawSprite(dpi, imageId, screenCoords); - } + // Draw the coloured spinning vehicle + // 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; + auto imageId = ImageId(imageIndex, vehicleColour.Body, vehicleColour.Trim, vehicleColour.Tertiary); + GfxDrawSprite(dpi, imageId, screenCoords); + } #pragma endregion #pragma region Music - std::vector window_ride_current_music_style_order; + std::vector window_ride_current_music_style_order; - void ToggleMusic() - { - auto ride = GetRide(rideId); - if (ride != nullptr) + void ToggleMusic() { - int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; - SetOperatingSetting(rideId, RideSetSetting::Music, activateMusic); - } - } - - void MusicOnMouseUp(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_PLAY_MUSIC: - ToggleMusic(); - break; - } - } - - void MusicResize() - { - flags |= WF_RESIZABLE; - WindowSetResize(*this, 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 - */ - void MusicOnMouseDown(WidgetIndex widgetIndex) - { - if (widgetIndex != WIDX_MUSIC_DROPDOWN) - return; - - auto dropdownWidget = &widgets[widgetIndex] - 1; - auto ride = GetRide(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) + auto ride = GetRide(rideId); + if (ride != 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)) + int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; + SetOperatingSetting(rideId, RideSetSetting::Music, activateMusic); + } + } + + void MusicOnMouseUp(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_PLAY_MUSIC: + ToggleMusic(); + break; + } + } + + void MusicResize() + { + flags |= WF_RESIZABLE; + WindowSetResize(*this, 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 + */ + void MusicOnMouseDown(WidgetIndex widgetIndex) + { + if (widgetIndex != WIDX_MUSIC_DROPDOWN) + return; + + auto dropdownWidget = &widgets[widgetIndex] - 1; + auto ride = GetRide(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) { - auto numTracks = musicObj->GetTrackCount(); - if (numTracks != 0) + // 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 track0 = musicObj->GetTrack(0); - if (!track0->Asset.IsAvailable()) + auto numTracks = musicObj->GetTrackCount(); + if (numTracks != 0) + { + auto track0 = musicObj->GetTrack(0); + if (!track0->Asset.IsAvailable()) + { + continue; + } + } + else { continue; } } - else + + if (GetGameState().Cheats.UnlockOperatingLimits || musicObj->SupportsRideType(ride->type)) { - continue; + musicOrder.push_back(i); } } + } - if (GetGameState().Cheats.UnlockOperatingLimits || musicObj->SupportsRideType(ride->type)) + // 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( + { 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++) + { + if (musicOrder[i] == ride->music) { - musicOrder.push_back(i); + Dropdown::SetChecked(static_cast(i), true); } } } - // 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++) + void MusicOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { - auto musicObj = static_cast(objManager.GetLoadedObject(ObjectType::Music, musicOrder[i])); - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[i].Args = musicObj->NameStringId; - } - - WindowDropdownShowTextCustomWidth( - { 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++) - { - if (musicOrder[i] == ride->music) + if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0 + && static_cast(dropdownIndex) < window_ride_current_music_style_order.size()) { - Dropdown::SetChecked(static_cast(i), true); + auto musicStyle = window_ride_current_music_style_order[dropdownIndex]; + SetOperatingSetting(rideId, RideSetSetting::MusicType, musicStyle); } } - } - void MusicOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0 - && static_cast(dropdownIndex) < window_ride_current_music_style_order.size()) + void MusicUpdate() { - auto musicStyle = window_ride_current_music_style_order[dropdownIndex]; - SetOperatingSetting(rideId, RideSetSetting::MusicType, musicStyle); - } - } - - void MusicUpdate() - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_6); - } - - void MusicOnPrepareDraw() - { - auto newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_6); } - SetPressedTab(); - - auto ride = GetRide(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) + void MusicOnPrepareDraw() { - musicName = musicObj->NameStringId; - } - widgets[WIDX_MUSIC].text = musicName; + auto newWidgets = PageWidgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } - // Set music activated - auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0; - if (isMusicActivated) - { - pressed_widgets |= (1uLL << WIDX_PLAY_MUSIC); - disabled_widgets &= ~(1uLL << WIDX_MUSIC); - disabled_widgets &= ~(1uLL << WIDX_MUSIC_DROPDOWN); - } - else - { - pressed_widgets &= ~(1uLL << WIDX_PLAY_MUSIC); - disabled_widgets |= (1uLL << WIDX_MUSIC); - disabled_widgets |= (1uLL << WIDX_MUSIC_DROPDOWN); + SetPressedTab(); + + auto ride = GetRide(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; + } + widgets[WIDX_MUSIC].text = musicName; + + // Set music activated + auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0; + if (isMusicActivated) + { + pressed_widgets |= (1uLL << WIDX_PLAY_MUSIC); + disabled_widgets &= ~(1uLL << WIDX_MUSIC); + disabled_widgets &= ~(1uLL << WIDX_MUSIC_DROPDOWN); + } + else + { + pressed_widgets &= ~(1uLL << WIDX_PLAY_MUSIC); + disabled_widgets |= (1uLL << WIDX_MUSIC); + disabled_widgets |= (1uLL << WIDX_MUSIC_DROPDOWN); + } + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); } - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - } - - void MusicOnDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - } + void MusicOnDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + } #pragma endregion #pragma region Measurements - static constexpr StringId GetRatingName(ride_rating rating) - { - int32_t index = std::clamp(rating >> 8, 0, static_cast(std::size(RatingNames)) - 1); - return RatingNames[index]; - } - - void SetupScenerySelection() - { - if (gTrackDesignSaveMode) + static constexpr StringId GetRatingName(ride_rating rating) { - CancelScenerySelection(); + int32_t index = std::clamp(rating >> 8, 0, static_cast(std::size(RatingNames)) - 1); + return RatingNames[index]; } - while (ToolSet(*this, WIDX_BACKGROUND, Tool::Crosshair)) - ; - - gTrackDesignSaveRideIndex = rideId; - - TrackDesignSaveInit(); - gGamePaused |= GAME_PAUSED_SAVING_TRACK; - gTrackDesignSaveMode = true; - - OpenRCT2::Audio::StopAll(); - - WindowBase* w_main = WindowGetMain(); - if (w_main != nullptr) + void SetupScenerySelection() { - w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); - } - - GfxInvalidateScreen(); - } - - void MeasurementsDesignReset() - { - TrackDesignSaveResetScenery(); - } - - void MeasurementsDesignSelectNearbyScenery() - { - TrackDesignSaveSelectNearbyScenery(gTrackDesignSaveRideIndex); - } - - void MeasurementsDesignCancel() - { - if (gTrackDesignSaveMode) - { - CancelScenerySelection(); - } - } - - static void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) - { - if (result == MODAL_RESULT_OK) - { - TrackRepositoryScan(); - } - GfxInvalidateScreen(); - }; - - void MeasurementsDesignSave() - { - TrackDesignState tds{}; - - Ride* ride = GetRide(rideId); - _trackDesign = ride->SaveToTrackDesign(tds); - if (!_trackDesign) - { - return; - } - - if (gTrackDesignSaveMode) - { - auto errMessage = _trackDesign->CreateTrackDesignScenery(tds); - if (!errMessage.Successful) + if (gTrackDesignSaveMode) { - ContextShowError(STR_CANT_SAVE_TRACK_DESIGN, errMessage.Message, {}); - return; + CancelScenerySelection(); } - if (errMessage.HasMessage()) + + while (ToolSet(*this, WIDX_BACKGROUND, Tool::Crosshair)) + ; + + gTrackDesignSaveRideIndex = rideId; + + TrackDesignSaveInit(); + gGamePaused |= GAME_PAUSED_SAVING_TRACK; + gTrackDesignSaveMode = true; + + OpenRCT2::Audio::StopAll(); + + WindowBase* w_main = WindowGetMain(); + if (w_main != nullptr) { - ContextShowError(errMessage.Message, STR_EMPTY, {}); + w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); + } + + GfxInvalidateScreen(); + } + + void MeasurementsDesignReset() + { + TrackDesignSaveResetScenery(); + } + + void MeasurementsDesignSelectNearbyScenery() + { + TrackDesignSaveSelectNearbyScenery(gTrackDesignSaveRideIndex); + } + + void MeasurementsDesignCancel() + { + if (gTrackDesignSaveMode) + { + CancelScenerySelection(); } } - 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); - } - - void MeasurementsClose() - { - MeasurementsDesignCancel(); - } - - void MeasurementsOnMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + static void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) { - 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_SELECT_NEARBY_SCENERY: - MeasurementsDesignSelectNearbyScenery(); - break; - case WIDX_RESET_SELECTION: - MeasurementsDesignReset(); - break; - case WIDX_SAVE_DESIGN: - MeasurementsDesignSave(); - break; - case WIDX_CANCEL_DESIGN: - MeasurementsDesignCancel(); - break; - } - } - - void MeasurementsResize() - { - WindowSetResize(*this, 316, 234, 316, 234); - } - - void MeasurementsOnMouseDown(WidgetIndex widgetIndex) - { - if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) - return; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - gDropdownItems[0].Format = STR_SAVE_TRACK_DESIGN_ITEM; - 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); - 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; - } - } - - void MeasurementsOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) - return; - - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - if (dropdownIndex == 0) - { - MeasurementsDesignSave(); - } - else - SetupScenerySelection(); - } - - void MeasurementsUpdate() - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_7); - } - - void MeasurementsOnToolDown(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 interactionFlags = EnumsToFlags( - ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, - ViewportInteractionItem::LargeScenery); - auto info = GetMapCoordinatesFromPos(screenCoords, interactionFlags); - 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 MeasurementsOnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) - { - if (screenCoords.x == _lastSceneryX && screenCoords.y == _lastSceneryY) - return; - _lastSceneryX = screenCoords.x; - _lastSceneryY = screenCoords.y; - - auto interactionFlags = EnumsToFlags( - ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, - ViewportInteractionItem::LargeScenery); - auto info = GetMapCoordinatesFromPos(screenCoords, interactionFlags); - 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; - } - } - - void MeasurementsOnToolAbort(WidgetIndex widgetIndex) - { - MeasurementsDesignCancel(); - } - - void MeasurementsOnPrepareDraw() - { - auto newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); - } - - SetPressedTab(); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE; - widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::Empty; - if (gTrackDesignSaveMode && gTrackDesignSaveRideIndex == rideId) - { - widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Button; - widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Button; - widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Button; - widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Button; - } - else - { - widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Empty; - widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Empty; - widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Empty; - widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Empty; - - widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::FlatBtn; - disabled_widgets |= (1uLL << WIDX_SAVE_TRACK_DESIGN); - if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) + if (result == MODAL_RESULT_OK) { - if (ride->excitement != RIDE_RATING_UNDEFINED) + TrackRepositoryScan(); + } + GfxInvalidateScreen(); + }; + + void MeasurementsDesignSave() + { + TrackDesignState tds{}; + + Ride* ride = GetRide(rideId); + _trackDesign = ride->SaveToTrackDesign(tds); + if (!_trackDesign) + { + return; + } + + if (gTrackDesignSaveMode) + { + auto errMessage = _trackDesign->CreateTrackDesignScenery(tds); + if (!errMessage.Successful) { - disabled_widgets &= ~(1uLL << WIDX_SAVE_TRACK_DESIGN); - widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN; + 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); + } + + void MeasurementsClose() + { + MeasurementsDesignCancel(); + } + + void MeasurementsOnMouseUp(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_SELECT_NEARBY_SCENERY: + MeasurementsDesignSelectNearbyScenery(); + break; + case WIDX_RESET_SELECTION: + MeasurementsDesignReset(); + break; + case WIDX_SAVE_DESIGN: + MeasurementsDesignSave(); + break; + case WIDX_CANCEL_DESIGN: + MeasurementsDesignCancel(); + break; + } + } + + void MeasurementsResize() + { + WindowSetResize(*this, 316, 234, 316, 234); + } + + void MeasurementsOnMouseDown(WidgetIndex widgetIndex) + { + if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) + return; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + gDropdownItems[0].Format = STR_SAVE_TRACK_DESIGN_ITEM; + 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); + 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; + } + } + + void MeasurementsOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) + return; + + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; + + if (dropdownIndex == 0) + { + MeasurementsDesignSave(); + } + else + SetupScenerySelection(); + } + + void MeasurementsUpdate() + { + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_7); + } + + void MeasurementsOnToolDown(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 interactionFlags = EnumsToFlags( + ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, + ViewportInteractionItem::LargeScenery); + auto info = GetMapCoordinatesFromPos(screenCoords, interactionFlags); + 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 MeasurementsOnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (screenCoords.x == _lastSceneryX && screenCoords.y == _lastSceneryY) + return; + _lastSceneryX = screenCoords.x; + _lastSceneryY = screenCoords.y; + + auto interactionFlags = EnumsToFlags( + ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, + ViewportInteractionItem::LargeScenery); + auto info = GetMapCoordinatesFromPos(screenCoords, interactionFlags); + 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; + } + } + + void MeasurementsOnToolAbort(WidgetIndex widgetIndex) + { + MeasurementsDesignCancel(); + } + + void MeasurementsOnPrepareDraw() + { + auto newWidgets = PageWidgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE; + widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::Empty; + if (gTrackDesignSaveMode && gTrackDesignSaveRideIndex == rideId) + { + widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Button; + widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Button; + widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Button; + widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Button; + } + else + { + widgets[WIDX_SELECT_NEARBY_SCENERY].type = WindowWidgetType::Empty; + widgets[WIDX_RESET_SELECTION].type = WindowWidgetType::Empty; + widgets[WIDX_SAVE_DESIGN].type = WindowWidgetType::Empty; + widgets[WIDX_CANCEL_DESIGN].type = WindowWidgetType::Empty; + + widgets[WIDX_SAVE_TRACK_DESIGN].type = WindowWidgetType::FlatBtn; + disabled_widgets |= (1uLL << WIDX_SAVE_TRACK_DESIGN); + if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) + { + if (ride->excitement != RIDE_RATING_UNDEFINED) + { + disabled_widgets &= ~(1uLL << WIDX_SAVE_TRACK_DESIGN); + widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN; + } + } + } + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + } + + void MeasurementsOnDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + if (widgets[WIDX_SAVE_DESIGN].type == WindowWidgetType::Button) + { + Widget* widget = &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 }); + + widgetCoords.x = windowPos.x + 4; + widgetCoords.y = windowPos.y + widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; + GfxFillRectInset( + dpi, { widgetCoords, { windowPos.x + 312, widgetCoords.y + 1 } }, colours[1], INSET_RECT_FLAG_BORDER_INSET); + } + else + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, 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 } }, 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); } } } - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - } +#pragma endregion - void MeasurementsOnDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); +#pragma region Graphs - if (widgets[WIDX_SAVE_DESIGN].type == WindowWidgetType::Button) + enum { - Widget* widget = &widgets[WIDX_PAGE_BACKGROUND]; + GRAPH_VELOCITY, + GRAPH_ALTITUDE, + GRAPH_VERTICAL, + GRAPH_LATERAL + }; - 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 }); - - widgetCoords.x = windowPos.x + 4; - widgetCoords.y = windowPos.y + widgets[WIDX_SELECT_NEARBY_SCENERY].bottom + 17; - GfxFillRectInset( - dpi, { widgetCoords, { windowPos.x + 312, widgetCoords.y + 1 } }, colours[1], INSET_RECT_FLAG_BORDER_INSET); + void SetGraph(int32_t type) + { + if (list_information_type == type) + { + _autoScrollGraph = !_autoScrollGraph; + } + else + { + list_information_type = type; + } + Invalidate(); } - else + + void GraphsOnMouseUp(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 GraphsResize() + { + WindowSetResize(*this, 316, 182, 500, 450); + } + + void GraphsOnMouseDown(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_GRAPH_VELOCITY: + SetGraph(GRAPH_VELOCITY); + break; + case WIDX_GRAPH_ALTITUDE: + SetGraph(GRAPH_ALTITUDE); + break; + case WIDX_GRAPH_VERTICAL: + SetGraph(GRAPH_VERTICAL); + break; + case WIDX_GRAPH_LATERAL: + SetGraph(GRAPH_LATERAL); + break; + } + } + + void GraphsUpdate() + { + Widget* widget; + int32_t x; + + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_8); + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_GRAPH); + + widget = &widgets[WIDX_GRAPH]; + x = scrolls[0].h_left; + if (_autoScrollGraph) + { + auto ride = GetRide(rideId); + if (ride != nullptr) + { + RideMeasurement* measurement{}; + std::tie(measurement, std::ignore) = ride->GetMeasurement(); + x = measurement == nullptr ? 0 : measurement->current_item - ((widget->width() / 4) * 3); + } + } + + scrolls[0].h_left = std::clamp(x, 0, scrolls[0].h_right - (widget->width() - 2)); + WidgetScrollUpdateThumbs(*this, WIDX_GRAPH); + } + + ScreenSize GraphsScrollGetSize(int32_t scrollIndex) + { + OnPrepareDraw(); + + ScreenSize size{}; + // Set minimum size + size.width = widgets[WIDX_GRAPH].width() - 2; + + // Get measurement size + auto ride = GetRide(rideId); + if (ride != nullptr) + { + RideMeasurement* measurement{}; + std::tie(measurement, std::ignore) = ride->GetMeasurement(); + if (measurement != nullptr) + { + size.width = std::max(size.width, measurement->num_items); + } + } + return size; + } + + void GraphsOnScrollSelect(int32_t scrollIndex, int32_t scrollAreaType) + { + _autoScrollGraph = false; + } + + OpenRCT2String GraphsTooltip(const WidgetIndex widgetIndex, const StringId fallback) + { + if (widgetIndex == WIDX_GRAPH) + { + auto ride = GetRide(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, {} }; + } + + void GraphsOnPrepareDraw() + { + auto newWidgets = PageWidgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + // Set pressed graph button type + 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)); + + // Hide graph buttons that are not applicable + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) + { + widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Button; + widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Button; + } + else + { + widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Empty; + widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Empty; + } + + // Anchor graph widget + auto x = width - 4; + auto y = height - BUTTON_FACE_HEIGHT - 8; + + widgets[WIDX_GRAPH].right = x; + widgets[WIDX_GRAPH].bottom = y; + y += 3; + widgets[WIDX_GRAPH_VELOCITY].top = y; + widgets[WIDX_GRAPH_ALTITUDE].top = y; + widgets[WIDX_GRAPH_VERTICAL].top = y; + widgets[WIDX_GRAPH_LATERAL].top = y; + y += BUTTON_FACE_HEIGHT + 1; + widgets[WIDX_GRAPH_VELOCITY].bottom = y; + widgets[WIDX_GRAPH_ALTITUDE].bottom = y; + widgets[WIDX_GRAPH_VERTICAL].bottom = y; + widgets[WIDX_GRAPH_LATERAL].bottom = y; + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + } + + void GraphsOnDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + } + + void GraphsOnScrollDraw(DrawPixelInfo& dpi, int32_t scrollIndex) + { + GfxClear(&dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker); + + auto widget = &widgets[WIDX_GRAPH]; + auto ride = GetRide(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 txtWidth = widget->width() - 2; + DrawTextWrapped(dpi, stringCoords, txtWidth, 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 = list_information_type; + int16_t yUnit = GraphsYAxisDetails[listType].unit; + StringId stringID = GraphsYAxisDetails[listType].label; + int16_t yUnitInterval = GraphsYAxisDetails[listType].unit_interval; + int16_t yInterval = GraphsYAxisDetails[listType].interval; + + // Scale modifier + if (listType == GRAPH_ALTITUDE) + { + yUnit -= kMapBaseZ * 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, { 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 graphWidth = 0; graphWidth < dpi.width; graphWidth++, 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 void UpdateSamePriceThroughoutFlags(ShopItem shop_item) + { + const auto& gameState = GetGameState(); + const auto existingFlags = gameState.SamePriceThroughoutPark; + + auto newFlags = existingFlags; + if (GetShopItemDescriptor(shop_item).IsPhoto()) + { + if (existingFlags & EnumToFlag(shop_item)) + newFlags &= ~EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); + else + newFlags |= EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); + } + else + { + newFlags ^= EnumToFlag(shop_item); + } + + auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); + GameActions::Execute(&parkSetParameter); + } + + void IncomeTogglePrimaryPrice() + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + ShopItem shopItem; + const auto& rtd = ride->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET)) + { + shopItem = ShopItem::Admission; + } + else + { + auto rideEntry = GetRideEntryByIndex(ride->subtype); + if (rideEntry != nullptr) + { + shopItem = rideEntry->shop_item[0]; + if (shopItem == ShopItem::None) + return; + } + else + { + return; + } + } + + UpdateSamePriceThroughoutFlags(shopItem); + + auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[0], true); + GameActions::Execute(&rideSetPriceAction); + } + + void IncomeToggleSecondaryPrice() + { + 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(rideId, price, true); + GameActions::Execute(&rideSetPriceAction); + } + + void IncomeIncreasePrimaryPrice() + { + if (!IncomeCanModifyPrimaryPrice()) + return; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto price = ride->price[0]; + if (price < 20.00_GBP) + price++; + + IncomeSetPrimaryPrice(price); + } + + void IncomeDecreasePrimaryPrice() + { + if (!IncomeCanModifyPrimaryPrice()) + return; + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto price = ride->price[0]; + if (price > 0.00_GBP) + price--; + + IncomeSetPrimaryPrice(price); + } + + money64 IncomeGetSecondaryPrice() + { + auto ride = GetRide(rideId); + if (ride == nullptr) + return 0; + + return ride->price[1]; + } + + void IncomeSetSecondaryPrice(money64 price) + { + auto rideSetPriceAction = RideSetPriceAction(rideId, price, false); + GameActions::Execute(&rideSetPriceAction); + } + + bool IncomeCanModifyPrimaryPrice() + { + auto ride = GetRide(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); + } + + void IncomeIncreaseSecondaryPrice() + { + auto price = IncomeGetSecondaryPrice(); + + if (price < 20.00_GBP) + price++; + + IncomeSetSecondaryPrice(price); + } + + void IncomeDecreaseSecondaryPrice() + { + auto price = IncomeGetSecondaryPrice(); + + if (price > 0.00_GBP) + price--; + + IncomeSetSecondaryPrice(price); + } + + void IncomeOnMouseUp(WidgetIndex widgetIndex) + { + utf8 _moneyInputText[MONEY_STRING_MAXLENGTH] = {}; + + 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_PRIMARY_PRICE: + { + if (!IncomeCanModifyPrimaryPrice()) + return; + + auto ride = GetRide(rideId); + if (ride != nullptr) + { + MoneyToString(ride->price[0], _moneyInputText, MONEY_STRING_MAXLENGTH, true); + WindowTextInputRawOpen( + 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(); + break; + case WIDX_SECONDARY_PRICE: + { + auto price64 = IncomeGetSecondaryPrice(); + + MoneyToString(price64, _moneyInputText, MONEY_STRING_MAXLENGTH, true); + WindowTextInputRawOpen( + 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(); + break; + } + } + + void IncomeResize() + { + WindowSetResize(*this, 316, 194, 316, 194); + } + + void IncomeOnMouseDown(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_PRIMARY_PRICE_INCREASE: + IncomeIncreasePrimaryPrice(); + break; + case WIDX_PRIMARY_PRICE_DECREASE: + IncomeDecreasePrimaryPrice(); + break; + case WIDX_SECONDARY_PRICE_INCREASE: + IncomeIncreaseSecondaryPrice(); + break; + case WIDX_SECONDARY_PRICE_DECREASE: + IncomeDecreaseSecondaryPrice(); + break; + } + } + + void IncomeUpdate() + { + frame_no++; + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_9); + + auto ride = GetRide(rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) + { + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME; + Invalidate(); + } + } + + void IncomeOnTextInput(WidgetIndex widgetIndex, std::string_view text) + { + if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text.empty()) + return; + + std::string strText{ text }; + money64 price = StringToMoney(strText.c_str()); + if (price == kMoney64Undefined) + { + return; + } + + price = std::clamp(price, 0.00_GBP, 20.00_GBP); + + if (widgetIndex == WIDX_PRIMARY_PRICE) + { + IncomeSetPrimaryPrice(price); + } + else + { + IncomeSetSecondaryPrice(price); + } + } + + void IncomeOnPrepareDraw() + { + auto newWidgets = PageWidgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + 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 + pressed_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); + disabled_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE); + + widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_NONE; + 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)) + { + disabled_widgets |= (1uLL << WIDX_PRIMARY_PRICE); + widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; + widgets[WIDX_PRIMARY_PRICE].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; + } + + widgets[WIDX_PRIMARY_PRICE_LABEL].text = STR_RIDE_INCOME_ADMISSION_PRICE; + widgets[WIDX_SECONDARY_PRICE_LABEL].text = STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO; + widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; + + widgets[WIDX_PRIMARY_PRICE].text = STR_BOTTOM_TOOLBAR_CASH; + auto ridePrimaryPrice = RideGetPrice(*ride); + ft.Rewind(); + ft.Add(ridePrimaryPrice); + if (ridePrimaryPrice == 0) + 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)) + { + widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; + + if (ShopItemHasCommonPrice(primaryItem)) + pressed_widgets |= (1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); + + 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) + { + widgets[WIDX_SECONDARY_PRICE_LABEL].text = GetShopItemDescriptor(secondaryItem).Naming.PriceLabel; + } + } + + if (secondaryItem == ShopItem::None) + { + // Hide secondary item widgets + widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Empty; + widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Empty; + widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Empty; + widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Empty; + widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; + } + else + { + // Set same price throughout park checkbox + pressed_widgets &= ~(1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); + if (ShopItemHasCommonPrice(secondaryItem)) + pressed_widgets |= (1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); + + // Show widgets + widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Label; + widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Spinner; + widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Button; + widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Button; + widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; + + // Set secondary item price + 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) + widgets[WIDX_SECONDARY_PRICE].text = STR_FREE; + } + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + } + + void IncomeOnDraw(DrawPixelInfo& dpi) + { + StringId stringId; + money64 profit; + ShopItem primaryItem, secondaryItem; + + DrawWidgets(dpi); + DrawTabImages(dpi); + + auto ride = GetRide(rideId); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, 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 != kMoney64Undefined) + { + 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 == kMoney64Undefined ? 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 != kMoney64Undefined) + { + 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 + + void CustomerOnMouseUp(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_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, 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, 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, number); + ContextOpenIntent(&intent); + break; + } + } + } + + void CustomerResize() + { + flags |= WF_RESIZABLE; + WindowSetResize(*this, 316, 163, 316, 163); + } + + void CustomerUpdate() + { + picked_peep_frame++; + if (picked_peep_frame >= 24) + picked_peep_frame = 0; + + OnPrepareDraw(); + WidgetInvalidate(*this, WIDX_TAB_10); + + auto ride = GetRide(rideId); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER) + { + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER; + Invalidate(); + } + } + + void CustomerOnPrepareDraw() + { + auto newWidgets = PageWidgets[page]; + if (widgets != newWidgets) + { + widgets = newWidgets; + InitScrollWidgets(); + } + + SetPressedTab(); + + auto ride = GetRide(rideId); + if (ride != nullptr) + { + auto ft = Formatter::Common(); + ride->FormatNameTo(ft); + + widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WindowWidgetType::FlatBtn; + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) + { + widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::Empty; + widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::Empty; + } + else + { + widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::FlatBtn; + widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::FlatBtn; + } + + AnchorBorderWidgets(); + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); + } + } + + void CustomerOnDraw(DrawPixelInfo& dpi) + { + ShopItem shopItem; + int16_t popularity, satisfaction, queueTime; + StringId stringId; + + DrawWidgets(dpi); + DrawTabImages(dpi); + auto ride = GetRide(rideId); if (ride == nullptr) return; @@ -5307,1485 +6545,181 @@ private: auto screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, widgets[WIDX_PAGE_BACKGROUND].top + 4 }; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) + // Customers currently on ride + if (ride->IsRide()) { - // 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); + ft.Add(ride->num_riders); + DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_ON_RIDE, ft); screenCoords.y += LIST_ROW_HEIGHT; + } - // Intensity - ratingName = GetRatingName(ride->intensity); - ft = Formatter(); - ft.Add(ride->intensity); - ft.Add(ratingName); + // Customers per hour + auto ft = Formatter(); + ft.Add(RideCustomersPerHour(*ride)); + DrawTextBasic(dpi, screenCoords, STR_CUSTOMERS_PER_HOUR, ft); + screenCoords.y += LIST_ROW_HEIGHT; - 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 } }, 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; - } - } - } + // Popularity + popularity = ride->popularity; + if (popularity == 255) + { + stringId = STR_POPULARITY_UNKNOWN; } else { - DrawTextBasic(dpi, screenCoords, STR_NO_TEST_RESULTS_YET); + 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 - -#pragma region Graphs - - enum - { - GRAPH_VELOCITY, - GRAPH_ALTITUDE, - GRAPH_VERTICAL, - GRAPH_LATERAL }; - void SetGraph(int32_t type) + /** + * + * rct2: 0x006AEAB4 + */ + static RideWindow* WindowRideOpen(const Ride& ride) { - if (list_information_type == type) - { - _autoScrollGraph = !_autoScrollGraph; - } - else - { - list_information_type = type; - } - Invalidate(); + return WindowCreate(WindowClass::Ride, 316, 207, WF_10 | WF_RESIZABLE, ride); } - void GraphsOnMouseUp(WidgetIndex widgetIndex) + /** + * + * rct2: 0x006ACC28 + */ + WindowBase* WindowRideMainOpen(const Ride& ride) { - switch (widgetIndex) + if (ride.type >= RIDE_TYPE_COUNT) { - 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; + return nullptr; } - } - void GraphsResize() - { - WindowSetResize(*this, 316, 182, 500, 450); - } - - void GraphsOnMouseDown(WidgetIndex widgetIndex) - { - switch (widgetIndex) + RideWindow* w = static_cast(WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying())); + if (w == nullptr) { - case WIDX_GRAPH_VELOCITY: - SetGraph(GRAPH_VELOCITY); - break; - case WIDX_GRAPH_ALTITUDE: - SetGraph(GRAPH_ALTITUDE); - break; - case WIDX_GRAPH_VERTICAL: - SetGraph(GRAPH_VERTICAL); - break; - case WIDX_GRAPH_LATERAL: - SetGraph(GRAPH_LATERAL); - break; + w = WindowRideOpen(ride); + w->SetViewIndex(0); } - } - - void GraphsUpdate() - { - Widget* widget; - int32_t x; - - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_8); - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_GRAPH); - - widget = &widgets[WIDX_GRAPH]; - x = scrolls[0].h_left; - if (_autoScrollGraph) + else if (w->GetViewIndex() >= (1 + ride.NumTrains + ride.num_stations)) { - auto ride = GetRide(rideId); - if (ride != nullptr) + w->SetViewIndex(0); + } + + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + { + if (w->classification == gCurrentToolWidget.window_classification && w->number == gCurrentToolWidget.window_number) { - RideMeasurement* measurement{}; - std::tie(measurement, std::ignore) = ride->GetMeasurement(); - x = measurement == nullptr ? 0 : measurement->current_item - ((widget->width() / 4) * 3); + ToolCancel(); } } - scrolls[0].h_left = std::clamp(x, 0, scrolls[0].h_right - (widget->width() - 2)); - WidgetScrollUpdateThumbs(*this, WIDX_GRAPH); + if (w->page != WINDOW_RIDE_PAGE_MAIN) + { + w->SetPage(WINDOW_RIDE_PAGE_MAIN); + } + + w->OnViewportRotate(); + return w; } - ScreenSize GraphsScrollGetSize(int32_t scrollIndex) + /** + * + * rct2: 0x006ACCCE + */ + static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationIndex) { - OnPrepareDraw(); + if (ride.type >= RIDE_TYPE_COUNT) + return nullptr; - ScreenSize size{}; - // Set minimum size - size.width = widgets[WIDX_GRAPH].width() - 2; + if (ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) + return WindowRideMainOpen(ride); - // Get measurement size - auto ride = GetRide(rideId); - if (ride != nullptr) + auto* w = static_cast(WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying())); + if (w == nullptr) { - RideMeasurement* measurement{}; - std::tie(measurement, std::ignore) = ride->GetMeasurement(); - if (measurement != nullptr) - { - size.width = std::max(size.width, measurement->num_items); - } + w = WindowRideOpen(ride); } - return size; - } - - void GraphsOnScrollSelect(int32_t scrollIndex, int32_t scrollAreaType) - { - _autoScrollGraph = false; - } - - OpenRCT2String GraphsTooltip(const WidgetIndex widgetIndex, const StringId fallback) - { - if (widgetIndex == WIDX_GRAPH) - { - auto ride = GetRide(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, {} }; - } - - void GraphsOnPrepareDraw() - { - auto newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); - } - - SetPressedTab(); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - // Set pressed graph button type - 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)); - - // Hide graph buttons that are not applicable - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) - { - widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Button; - widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Button; - } - else - { - widgets[WIDX_GRAPH_VERTICAL].type = WindowWidgetType::Empty; - widgets[WIDX_GRAPH_LATERAL].type = WindowWidgetType::Empty; - } - - // Anchor graph widget - auto x = width - 4; - auto y = height - BUTTON_FACE_HEIGHT - 8; - - widgets[WIDX_GRAPH].right = x; - widgets[WIDX_GRAPH].bottom = y; - y += 3; - widgets[WIDX_GRAPH_VELOCITY].top = y; - widgets[WIDX_GRAPH_ALTITUDE].top = y; - widgets[WIDX_GRAPH_VERTICAL].top = y; - widgets[WIDX_GRAPH_LATERAL].top = y; - y += BUTTON_FACE_HEIGHT + 1; - widgets[WIDX_GRAPH_VELOCITY].bottom = y; - widgets[WIDX_GRAPH_ALTITUDE].bottom = y; - widgets[WIDX_GRAPH_VERTICAL].bottom = y; - widgets[WIDX_GRAPH_LATERAL].bottom = y; - - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - } - - void GraphsOnDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - } - - void GraphsOnScrollDraw(DrawPixelInfo& dpi, int32_t scrollIndex) - { - GfxClear(&dpi, ColourMapA[COLOUR_SATURATED_GREEN].darker); - - auto widget = &widgets[WIDX_GRAPH]; - auto ride = GetRide(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 txtWidth = widget->width() - 2; - DrawTextWrapped(dpi, stringCoords, txtWidth, 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 = list_information_type; - int16_t yUnit = GraphsYAxisDetails[listType].unit; - StringId stringID = GraphsYAxisDetails[listType].label; - int16_t yUnitInterval = GraphsYAxisDetails[listType].unit_interval; - int16_t yInterval = GraphsYAxisDetails[listType].interval; - - // Scale modifier - if (listType == GRAPH_ALTITUDE) - { - yUnit -= kMapBaseZ * 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, { 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 graphWidth = 0; graphWidth < dpi.width; graphWidth++, 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 void UpdateSamePriceThroughoutFlags(ShopItem shop_item) - { - const auto& gameState = GetGameState(); - const auto existingFlags = gameState.SamePriceThroughoutPark; - - auto newFlags = existingFlags; - if (GetShopItemDescriptor(shop_item).IsPhoto()) - { - if (existingFlags & EnumToFlag(shop_item)) - newFlags &= ~EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); - else - newFlags |= EnumsToFlags(ShopItem::Photo, ShopItem::Photo2, ShopItem::Photo3, ShopItem::Photo4); - } - else - { - newFlags ^= EnumToFlag(shop_item); - } - - auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); - GameActions::Execute(&parkSetParameter); - } - - void IncomeTogglePrimaryPrice() - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - ShopItem shopItem; - const auto& rtd = ride->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_TOILET)) - { - shopItem = ShopItem::Admission; - } - else - { - auto rideEntry = GetRideEntryByIndex(ride->subtype); - if (rideEntry != nullptr) - { - shopItem = rideEntry->shop_item[0]; - if (shopItem == ShopItem::None) - return; - } - else - { - return; - } - } - - UpdateSamePriceThroughoutFlags(shopItem); - - auto rideSetPriceAction = RideSetPriceAction(rideId, ride->price[0], true); - GameActions::Execute(&rideSetPriceAction); - } - - void IncomeToggleSecondaryPrice() - { - 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(rideId, price, true); - GameActions::Execute(&rideSetPriceAction); - } - - void IncomeIncreasePrimaryPrice() - { - if (!IncomeCanModifyPrimaryPrice()) - return; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto price = ride->price[0]; - if (price < 20.00_GBP) - price++; - - IncomeSetPrimaryPrice(price); - } - - void IncomeDecreasePrimaryPrice() - { - if (!IncomeCanModifyPrimaryPrice()) - return; - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto price = ride->price[0]; - if (price > 0.00_GBP) - price--; - - IncomeSetPrimaryPrice(price); - } - - money64 IncomeGetSecondaryPrice() - { - auto ride = GetRide(rideId); - if (ride == nullptr) - return 0; - - return ride->price[1]; - } - - void IncomeSetSecondaryPrice(money64 price) - { - auto rideSetPriceAction = RideSetPriceAction(rideId, price, false); - GameActions::Execute(&rideSetPriceAction); - } - - bool IncomeCanModifyPrimaryPrice() - { - auto ride = GetRide(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); - } - - void IncomeIncreaseSecondaryPrice() - { - auto price = IncomeGetSecondaryPrice(); - - if (price < 20.00_GBP) - price++; - - IncomeSetSecondaryPrice(price); - } - - void IncomeDecreaseSecondaryPrice() - { - auto price = IncomeGetSecondaryPrice(); - - if (price > 0.00_GBP) - price--; - - IncomeSetSecondaryPrice(price); - } - - void IncomeOnMouseUp(WidgetIndex widgetIndex) - { - utf8 _moneyInputText[MONEY_STRING_MAXLENGTH] = {}; - - 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_PRIMARY_PRICE: - { - if (!IncomeCanModifyPrimaryPrice()) - return; - - auto ride = GetRide(rideId); - if (ride != nullptr) - { - MoneyToString(ride->price[0], _moneyInputText, MONEY_STRING_MAXLENGTH, true); - WindowTextInputRawOpen( - 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(); - break; - case WIDX_SECONDARY_PRICE: - { - auto price64 = IncomeGetSecondaryPrice(); - - MoneyToString(price64, _moneyInputText, MONEY_STRING_MAXLENGTH, true); - WindowTextInputRawOpen( - 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(); - break; - } - } - - void IncomeResize() - { - WindowSetResize(*this, 316, 194, 316, 194); - } - - void IncomeOnMouseDown(WidgetIndex widgetIndex) - { - switch (widgetIndex) - { - case WIDX_PRIMARY_PRICE_INCREASE: - IncomeIncreasePrimaryPrice(); - break; - case WIDX_PRIMARY_PRICE_DECREASE: - IncomeDecreasePrimaryPrice(); - break; - case WIDX_SECONDARY_PRICE_INCREASE: - IncomeIncreaseSecondaryPrice(); - break; - case WIDX_SECONDARY_PRICE_DECREASE: - IncomeDecreaseSecondaryPrice(); - break; - } - } - - void IncomeUpdate() - { - frame_no++; - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_9); - - auto ride = GetRide(rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) - { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME; - Invalidate(); - } - } - - void IncomeOnTextInput(WidgetIndex widgetIndex, std::string_view text) - { - if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text.empty()) - return; - - std::string strText{ text }; - money64 price = StringToMoney(strText.c_str()); - if (price == kMoney64Undefined) - { - return; - } - - price = std::clamp(price, 0.00_GBP, 20.00_GBP); - - if (widgetIndex == WIDX_PRIMARY_PRICE) - { - IncomeSetPrimaryPrice(price); - } - else - { - IncomeSetSecondaryPrice(price); - } - } - - void IncomeOnPrepareDraw() - { - auto newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); - } - - SetPressedTab(); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - 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 - pressed_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); - disabled_widgets &= ~(1uLL << WIDX_PRIMARY_PRICE); - - widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_NONE; - 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)) - { - disabled_widgets |= (1uLL << WIDX_PRIMARY_PRICE); - widgets[WIDX_PRIMARY_PRICE_LABEL].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; - widgets[WIDX_PRIMARY_PRICE].tooltip = STR_RIDE_INCOME_ADMISSION_PAY_FOR_ENTRY_TIP; - } - - widgets[WIDX_PRIMARY_PRICE_LABEL].text = STR_RIDE_INCOME_ADMISSION_PRICE; - widgets[WIDX_SECONDARY_PRICE_LABEL].text = STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO; - widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; - - widgets[WIDX_PRIMARY_PRICE].text = STR_BOTTOM_TOOLBAR_CASH; - auto ridePrimaryPrice = RideGetPrice(*ride); - ft.Rewind(); - ft.Add(ridePrimaryPrice); - if (ridePrimaryPrice == 0) - 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)) - { - widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; - - if (ShopItemHasCommonPrice(primaryItem)) - pressed_widgets |= (1uLL << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); - - 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) - { - widgets[WIDX_SECONDARY_PRICE_LABEL].text = GetShopItemDescriptor(secondaryItem).Naming.PriceLabel; - } - } - - if (secondaryItem == ShopItem::None) - { - // Hide secondary item widgets - widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Empty; - widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Empty; - widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Empty; - widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Empty; - widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Empty; - } - else - { - // Set same price throughout park checkbox - pressed_widgets &= ~(1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); - if (ShopItemHasCommonPrice(secondaryItem)) - pressed_widgets |= (1uLL << WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK); - - // Show widgets - widgets[WIDX_SECONDARY_PRICE_LABEL].type = WindowWidgetType::Label; - widgets[WIDX_SECONDARY_PRICE].type = WindowWidgetType::Spinner; - widgets[WIDX_SECONDARY_PRICE_INCREASE].type = WindowWidgetType::Button; - widgets[WIDX_SECONDARY_PRICE_DECREASE].type = WindowWidgetType::Button; - widgets[WIDX_SECONDARY_PRICE_SAME_THROUGHOUT_PARK].type = WindowWidgetType::Checkbox; - - // Set secondary item price - 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) - widgets[WIDX_SECONDARY_PRICE].text = STR_FREE; - } - - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - } - - void IncomeOnDraw(DrawPixelInfo& dpi) - { - StringId stringId; - money64 profit; - ShopItem primaryItem, secondaryItem; - - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, 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 != kMoney64Undefined) - { - 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 == kMoney64Undefined ? 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 != kMoney64Undefined) - { - 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 - - void CustomerOnMouseUp(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_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, 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, 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, number); - ContextOpenIntent(&intent); - break; - } - } - } - - void CustomerResize() - { - flags |= WF_RESIZABLE; - WindowSetResize(*this, 316, 163, 316, 163); - } - - void CustomerUpdate() - { - picked_peep_frame++; - if (picked_peep_frame >= 24) - picked_peep_frame = 0; - - OnPrepareDraw(); - WidgetInvalidate(*this, WIDX_TAB_10); - - auto ride = GetRide(rideId); - if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER) - { - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER; - Invalidate(); - } - } - - void CustomerOnPrepareDraw() - { - auto newWidgets = PageWidgets[page]; - if (widgets != newWidgets) - { - widgets = newWidgets; - InitScrollWidgets(); - } - - SetPressedTab(); - - auto ride = GetRide(rideId); - if (ride != nullptr) - { - auto ft = Formatter::Common(); - ride->FormatNameTo(ft); - - widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WindowWidgetType::FlatBtn; - if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) - { - widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::Empty; - widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::Empty; - } - else - { - widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WindowWidgetType::FlatBtn; - widgets[WIDX_SHOW_GUESTS_QUEUING].type = WindowWidgetType::FlatBtn; - } - - AnchorBorderWidgets(); - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_10); - } - } - - void CustomerOnDraw(DrawPixelInfo& dpi) - { - ShopItem shopItem; - int16_t popularity, satisfaction, queueTime; - StringId stringId; - - DrawWidgets(dpi); - DrawTabImages(dpi); - - auto ride = GetRide(rideId); - if (ride == nullptr) - return; - - auto screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_PAGE_BACKGROUND].left + 4, 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 RideWindow* WindowRideOpen(const Ride& ride) -{ - return WindowCreate(WindowClass::Ride, 316, 207, WF_10 | WF_RESIZABLE, ride); -} - -/** - * - * rct2: 0x006ACC28 - */ -WindowBase* WindowRideMainOpen(const Ride& ride) -{ - if (ride.type >= RIDE_TYPE_COUNT) - { - return nullptr; - } - - RideWindow* w = static_cast(WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying())); - if (w == nullptr) - { - w = WindowRideOpen(ride); - w->SetViewIndex(0); - } - else if (w->GetViewIndex() >= (1 + ride.NumTrains + ride.num_stations)) - { - w->SetViewIndex(0); - } - - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - { - if (w->classification == gCurrentToolWidget.window_classification && w->number == gCurrentToolWidget.window_number) - { - ToolCancel(); - } - } - - if (w->page != WINDOW_RIDE_PAGE_MAIN) - { - w->SetPage(WINDOW_RIDE_PAGE_MAIN); - } - - w->OnViewportRotate(); - return w; -} - -/** - * - * rct2: 0x006ACCCE - */ -static WindowBase* WindowRideOpenStation(const Ride& ride, StationIndex stationIndex) -{ - if (ride.type >= RIDE_TYPE_COUNT) - return nullptr; - - if (ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_NO_VEHICLES)) - return WindowRideMainOpen(ride); - - auto* w = static_cast(WindowBringToFrontByNumber(WindowClass::Ride, ride.id.ToUnderlying())); - if (w == nullptr) - { - w = WindowRideOpen(ride); - } - - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification - && gCurrentToolWidget.window_number == w->number) - { - ToolCancel(); - } - - // View - for (int32_t i = stationIndex.ToUnderlying(); i >= 0; i--) - { - if (ride.GetStations()[i].Start.IsNull()) - { - stationIndex = StationIndex::FromUnderlying(stationIndex.ToUnderlying() - 1); - } - } - - w->SetViewIndex(1 + ride.NumTrains + stationIndex.ToUnderlying()); - - return w; -} - -WindowBase* WindowRideOpenTrack(TileElement* tileElement) -{ - assert(tileElement != nullptr); - auto rideIndex = tileElement->GetRideIndex(); - if (!rideIndex.IsNull()) - { - auto ride = GetRide(rideIndex); - if (ride != nullptr) - { - const auto type = tileElement->GetType(); - if (type == TileElementType::Entrance) - { - // Open ride window in station view - auto entranceElement = tileElement->AsEntrance(); - auto stationIndex = entranceElement->GetStationIndex(); - return WindowRideOpenStation(*ride, stationIndex); - } - else if (type == TileElementType::Track) - { - // Open ride window in station view - auto trackElement = tileElement->AsTrack(); - auto trackType = trackElement->GetTrackType(); - const auto& ted = GetTrackElementDescriptor(trackType); - if (ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN) - { - auto stationIndex = trackElement->GetStationIndex(); - return WindowRideOpenStation(*ride, stationIndex); - } - } - - // Open ride window in overview mode - return WindowRideMainOpen(*ride); - } - } - return nullptr; -} - -/** - * - * rct2: 0x006ACAC2 - */ -WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) -{ - if (vehicle == nullptr) - return nullptr; - - Vehicle* headVehicle = vehicle->TrainHead(); - if (headVehicle == nullptr) - return nullptr; - - EntityId headVehicleSpriteIndex = headVehicle->Id; - auto ride = headVehicle->GetRide(); - if (ride == nullptr) - return nullptr; - - // Get view index - int16_t view = 1; - for (int32_t i = 0; i <= OpenRCT2::Limits::MaxTrainsPerRide; i++) - { - if (ride->vehicles[i] == headVehicleSpriteIndex) - break; - - view++; - } - - auto* w = static_cast(WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying())); - if (w != nullptr) - { - w->Invalidate(); if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification && gCurrentToolWidget.window_number == w->number) @@ -6793,96 +6727,187 @@ WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) ToolCancel(); } - int32_t openedPeepWindow = 0; - if (w->GetViewIndex() == view) + // View + for (int32_t i = stationIndex.ToUnderlying(); i >= 0; i--) { - int32_t numPeepsLeft = vehicle->num_peeps; - for (int32_t i = 0; i < 32 && numPeepsLeft > 0; i++) + if (ride.GetStations()[i].Start.IsNull()) { - Peep* peep = GetEntity(vehicle->peep[i]); - if (peep == nullptr) - continue; - - numPeepsLeft--; - WindowBase* w2 = WindowFindByNumber(WindowClass::Peep, vehicle->peep[i]); - if (w2 == nullptr) - { - auto intent = Intent(WindowClass::Peep); - intent.PutExtra(INTENT_EXTRA_PEEP, peep); - ContextOpenIntent(&intent); - openedPeepWindow = 1; - - break; - } + stationIndex = StationIndex::FromUnderlying(stationIndex.ToUnderlying() - 1); } } - w = static_cast( - openedPeepWindow ? WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying()) - : WindowBringToFrontByNumber(WindowClass::Ride, ride->id.ToUnderlying())); + w->SetViewIndex(1 + ride.NumTrains + stationIndex.ToUnderlying()); + + return w; } - if (w == nullptr) + WindowBase* WindowRideOpenTrack(TileElement* tileElement) { - w = WindowRideOpen(*ride); - } - - 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(); -} - -void WindowRidePaintResetVehicle(RideId rideIndex) -{ - auto w = static_cast(WindowFindByNumber(WindowClass::Ride, rideIndex.ToUnderlying())); - if (w != nullptr) - { - if (w->page == 4) // WINDOW_RIDE_PAGE_COLOUR + assert(tileElement != nullptr); + auto rideIndex = tileElement->GetRideIndex(); + if (!rideIndex.IsNull()) { - w->ResetVehicleIndex(); + auto ride = GetRide(rideIndex); + if (ride != nullptr) + { + const auto type = tileElement->GetType(); + if (type == TileElementType::Entrance) + { + // Open ride window in station view + auto entranceElement = tileElement->AsEntrance(); + auto stationIndex = entranceElement->GetStationIndex(); + return WindowRideOpenStation(*ride, stationIndex); + } + else if (type == TileElementType::Track) + { + // Open ride window in station view + auto trackElement = tileElement->AsTrack(); + auto trackType = trackElement->GetTrackType(); + const auto& ted = GetTrackElementDescriptor(trackType); + if (ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN) + { + auto stationIndex = trackElement->GetStationIndex(); + return WindowRideOpenStation(*ride, stationIndex); + } + } + + // Open ride window in overview mode + return WindowRideMainOpen(*ride); + } } + return nullptr; + } + + /** + * + * rct2: 0x006ACAC2 + */ + WindowBase* WindowRideOpenVehicle(Vehicle* vehicle) + { + if (vehicle == nullptr) + return nullptr; + + Vehicle* headVehicle = vehicle->TrainHead(); + if (headVehicle == nullptr) + return nullptr; + + EntityId headVehicleSpriteIndex = headVehicle->Id; + auto ride = headVehicle->GetRide(); + if (ride == nullptr) + return nullptr; + + // Get view index + int16_t view = 1; + for (int32_t i = 0; i <= OpenRCT2::Limits::MaxTrainsPerRide; i++) + { + if (ride->vehicles[i] == headVehicleSpriteIndex) + break; + + view++; + } + + auto* w = static_cast(WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying())); + if (w != nullptr) + { + w->Invalidate(); + + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification + && gCurrentToolWidget.window_number == w->number) + { + ToolCancel(); + } + + int32_t openedPeepWindow = 0; + if (w->GetViewIndex() == view) + { + int32_t numPeepsLeft = vehicle->num_peeps; + for (int32_t i = 0; i < 32 && numPeepsLeft > 0; i++) + { + Peep* peep = GetEntity(vehicle->peep[i]); + if (peep == nullptr) + continue; + + numPeepsLeft--; + WindowBase* w2 = WindowFindByNumber(WindowClass::Peep, vehicle->peep[i]); + if (w2 == nullptr) + { + auto intent = Intent(WindowClass::Peep); + intent.PutExtra(INTENT_EXTRA_PEEP, peep); + ContextOpenIntent(&intent); + openedPeepWindow = 1; + + break; + } + } + } + + w = static_cast( + openedPeepWindow ? WindowFindByNumber(WindowClass::Ride, ride->id.ToUnderlying()) + : WindowBringToFrontByNumber(WindowClass::Ride, ride->id.ToUnderlying())); + } + + if (w == nullptr) + { + w = WindowRideOpen(*ride); + } + + 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) + void WindowRidePaintResetVehicle(RideId rideIndex) { - main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); + auto w = static_cast(WindowFindByNumber(WindowClass::Ride, rideIndex.ToUnderlying())); + if (w != nullptr) + { + if (w->page == 4) // WINDOW_RIDE_PAGE_COLOUR + { + w->ResetVehicleIndex(); + } + w->Invalidate(); + } } - GfxInvalidateScreen(); - ToolCancel(); -} - -void WindowRideMeasurementsDesignCancel() -{ - if (gTrackDesignSaveMode) + static void CancelScenerySelection() { - 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(); + } + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/RideConstruction.cpp b/src/openrct2-ui/windows/RideConstruction.cpp index 8fd3259692..82c99a013c 100644 --- a/src/openrct2-ui/windows/RideConstruction.cpp +++ b/src/openrct2-ui/windows/RideConstruction.cpp @@ -47,73 +47,73 @@ #include #include -using namespace OpenRCT2; using namespace OpenRCT2::TrackMetaData; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_RIDE_CONSTRUCTION_WINDOW_TITLE; + static constexpr int32_t WH = 394; + static constexpr int32_t WW = 210; -static constexpr StringId WINDOW_TITLE = STR_RIDE_CONSTRUCTION_WINDOW_TITLE; -static constexpr int32_t WH = 394; -static constexpr int32_t WW = 210; - -static constexpr uint16_t ARROW_PULSE_DURATION = 200; -// Width of the group boxes, e.g. “Banking” -static constexpr int32_t GW = WW - 6; + static constexpr uint16_t ARROW_PULSE_DURATION = 200; + // Width of the group boxes, e.g. “Banking” + static constexpr int32_t GW = WW - 6; #pragma region Widgets -enum -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_DIRECTION_GROUPBOX, - WIDX_SLOPE_GROUPBOX, - WIDX_BANKING_GROUPBOX, - WIDX_LEFT_CURVE_VERY_SMALL, - WIDX_LEFT_CURVE_SMALL, - WIDX_LEFT_CURVE, - WIDX_LEFT_CURVE_LARGE, - WIDX_STRAIGHT, - WIDX_RIGHT_CURVE_LARGE, - WIDX_RIGHT_CURVE, - WIDX_RIGHT_CURVE_SMALL, - WIDX_RIGHT_CURVE_VERY_SMALL, - WIDX_SPECIAL_TRACK_DROPDOWN, - WIDX_SLOPE_DOWN_STEEP, - WIDX_SLOPE_DOWN, - WIDX_LEVEL, - WIDX_SLOPE_UP, - WIDX_SLOPE_UP_STEEP, - WIDX_CHAIN_LIFT, - WIDX_BANK_LEFT, - WIDX_BANK_STRAIGHT, - WIDX_BANK_RIGHT, - WIDX_CONSTRUCT, - WIDX_DEMOLISH, - WIDX_PREVIOUS_SECTION, - WIDX_NEXT_SECTION, - WIDX_ENTRANCE_EXIT_GROUPBOX, - WIDX_ENTRANCE, - WIDX_EXIT, - WIDX_ROTATE, - WIDX_U_TRACK, - WIDX_O_TRACK, - WIDX_SEAT_ROTATION_GROUPBOX, - WIDX_SEAT_ROTATION_ANGLE_SPINNER, - WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP, - WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN, - WIDX_SIMULATE, - WIDX_SPEED_GROUPBOX = WIDX_BANKING_GROUPBOX, - WIDX_SPEED_SETTING_SPINNER = WIDX_BANK_LEFT, - WIDX_SPEED_SETTING_SPINNER_UP = WIDX_BANK_STRAIGHT, - WIDX_SPEED_SETTING_SPINNER_DOWN = WIDX_BANK_RIGHT, -}; + enum + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_DIRECTION_GROUPBOX, + WIDX_SLOPE_GROUPBOX, + WIDX_BANKING_GROUPBOX, + WIDX_LEFT_CURVE_VERY_SMALL, + WIDX_LEFT_CURVE_SMALL, + WIDX_LEFT_CURVE, + WIDX_LEFT_CURVE_LARGE, + WIDX_STRAIGHT, + WIDX_RIGHT_CURVE_LARGE, + WIDX_RIGHT_CURVE, + WIDX_RIGHT_CURVE_SMALL, + WIDX_RIGHT_CURVE_VERY_SMALL, + WIDX_SPECIAL_TRACK_DROPDOWN, + WIDX_SLOPE_DOWN_STEEP, + WIDX_SLOPE_DOWN, + WIDX_LEVEL, + WIDX_SLOPE_UP, + WIDX_SLOPE_UP_STEEP, + WIDX_CHAIN_LIFT, + WIDX_BANK_LEFT, + WIDX_BANK_STRAIGHT, + WIDX_BANK_RIGHT, + WIDX_CONSTRUCT, + WIDX_DEMOLISH, + WIDX_PREVIOUS_SECTION, + WIDX_NEXT_SECTION, + WIDX_ENTRANCE_EXIT_GROUPBOX, + WIDX_ENTRANCE, + WIDX_EXIT, + WIDX_ROTATE, + WIDX_U_TRACK, + WIDX_O_TRACK, + WIDX_SEAT_ROTATION_GROUPBOX, + WIDX_SEAT_ROTATION_ANGLE_SPINNER, + WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP, + WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN, + WIDX_SIMULATE, + WIDX_SPEED_GROUPBOX = WIDX_BANKING_GROUPBOX, + WIDX_SPEED_SETTING_SPINNER = WIDX_BANK_LEFT, + WIDX_SPEED_SETTING_SPINNER_UP = WIDX_BANK_STRAIGHT, + WIDX_SPEED_SETTING_SPINNER_DOWN = WIDX_BANK_RIGHT, + }; -validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_CONSTRUCT); -validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_ENTRANCE); -validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_EXIT); -validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_ROTATE); + validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_CONSTRUCT); + validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_ENTRANCE); + validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_EXIT); + validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_ROTATE); -// clang-format off + // clang-format off static Widget _rideConstructionWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget ({ 3, 17}, { GW, 57}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_RIDE_CONSTRUCTION_DIRECTION ), @@ -153,205 +153,216 @@ static Widget _rideConstructionWidgets[] = { MakeWidget ({161, 338}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_SIMULATE), STR_SIMULATE_RIDE_TIP ), kWidgetsEnd, }; -// clang-format on + // clang-format on #pragma endregion -static bool _trackPlaceCtrlState; -static int32_t _trackPlaceCtrlZ; -static bool _trackPlaceShiftState; -static ScreenCoordsXY _trackPlaceShiftStart; -static int32_t _trackPlaceShiftZ; -static int32_t _trackPlaceZ; -static money64 _trackPlaceCost; -static StringId _trackPlaceErrorMessage; -static bool _autoRotatingShop; -static bool _gotoStartPlacementMode = false; + static bool _trackPlaceCtrlState; + static int32_t _trackPlaceCtrlZ; + static bool _trackPlaceShiftState; + static ScreenCoordsXY _trackPlaceShiftStart; + static int32_t _trackPlaceShiftZ; + static int32_t _trackPlaceZ; + static money64 _trackPlaceCost; + static StringId _trackPlaceErrorMessage; + static bool _autoRotatingShop; + static bool _gotoStartPlacementMode = false; -static constexpr StringId RideConstructionSeatAngleRotationStrings[] = { - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_180, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_135, - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_90, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_45, - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_0, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_45, - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_90, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_135, - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_180, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_225, - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_270, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_315, - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_360, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_405, - STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_450, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_495, -}; + static constexpr StringId RideConstructionSeatAngleRotationStrings[] = { + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_180, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_135, + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_90, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_NEG_45, + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_0, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_45, + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_90, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_135, + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_180, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_225, + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_270, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_315, + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_360, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_405, + STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_450, STR_RIDE_CONSTRUCTION_SEAT_ROTATION_ANGLE_495, + }; -static void WindowRideConstructionMouseUpDemolishNextPiece(const CoordsXYZD& piecePos, int32_t type); + static void WindowRideConstructionMouseUpDemolishNextPiece(const CoordsXYZD& piecePos, int32_t type); -static int32_t RideGetAlternativeType(const Ride& ride) -{ - return (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE) ? ride.GetRideTypeDescriptor().AlternateType - : ride.type; -} - -/* move to ride.c */ -static void CloseRideWindowForConstruction(RideId rideId) -{ - WindowBase* w = WindowFindByNumber(WindowClass::Ride, rideId.ToUnderlying()); - if (w != nullptr && w->page == 1) - WindowClose(*w); -} - -static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActions::Result* result); -static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActions::Result* result); -static void CloseConstructWindowOnCompletion(const Ride& ride); - -class RideConstructionWindow final : public Window -{ -private: - uint8_t _currentlyShowingBrakeOrBoosterSpeed{}; - SpecialElementsDropdownState _specialElementDropdownState; - bool _autoOpeningShop{}; - -public: - void OnOpen() override + static int32_t RideGetAlternativeType(const Ride& ride) { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; - } - - widgets = _rideConstructionWidgets; - number = _currentRideIndex.ToUnderlying(); - - InitScrollWidgets(); - - WindowPushOthersRight(*this); - ShowGridlines(); - - _currentTrackPrice = kMoney64Undefined; - _currentBrakeSpeed2 = 8; - _currentSeatRotationAngle = 4; - - _currentTrackCurve = currentRide->GetRideTypeDescriptor().StartTrackPiece | RideConstructionSpecialPieceSelected; - _currentTrackPitchEnd = TrackPitch::None; - _currentTrackRollEnd = TrackRoll::None; - _currentTrackLiftHill = 0; - _currentTrackAlternative = RIDE_TYPE_NO_ALTERNATIVES; - - if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_START_CONSTRUCTION_INVERTED)) - _currentTrackAlternative |= RIDE_TYPE_ALTERNATIVE_TRACK_TYPE; - - _previousTrackRollEnd = TrackRoll::None; - _previousTrackPitchEnd = TrackPitch::None; - - _currentTrackPieceDirection = 0; - _rideConstructionState = RideConstructionState::Place; - _currentTrackSelectionFlags = 0; - _autoOpeningShop = false; - _autoRotatingShop = true; - _trackPlaceCtrlState = false; - _trackPlaceShiftState = false; + return (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE) ? ride.GetRideTypeDescriptor().AlternateType + : ride.type; } - void OnClose() override + /* move to ride.c */ + static void CloseRideWindowForConstruction(RideId rideId) { - RideConstructionInvalidateCurrentTrack(); - ViewportSetVisibility(ViewportVisibility::Default); + WindowBase* w = WindowFindByNumber(WindowClass::Ride, rideId.ToUnderlying()); + if (w != nullptr && w->page == 1) + WindowClose(*w); + } - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActions::Result* result); + static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActions::Result* result); + static void CloseConstructWindowOnCompletion(const Ride& ride); - // In order to cancel the yellow arrow correctly the - // selection tool should be cancelled. Don't do a tool cancel if - // another window has already taken control of tool. - if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) - ToolCancel(); + class RideConstructionWindow final : public Window + { + private: + uint8_t _currentlyShowingBrakeOrBoosterSpeed{}; + SpecialElementsDropdownState _specialElementDropdownState; + bool _autoOpeningShop{}; - HideGridlines(); - - // If we demolish a currentRide all windows will be closed including the construction window, - // the currentRide at this point is already gone. - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) + public: + void OnOpen() override { - return; + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) + { + return; + } + + widgets = _rideConstructionWidgets; + number = _currentRideIndex.ToUnderlying(); + + InitScrollWidgets(); + + WindowPushOthersRight(*this); + ShowGridlines(); + + _currentTrackPrice = kMoney64Undefined; + _currentBrakeSpeed2 = 8; + _currentSeatRotationAngle = 4; + + _currentTrackCurve = currentRide->GetRideTypeDescriptor().StartTrackPiece | RideConstructionSpecialPieceSelected; + _currentTrackPitchEnd = TrackPitch::None; + _currentTrackRollEnd = TrackRoll::None; + _currentTrackLiftHill = 0; + _currentTrackAlternative = RIDE_TYPE_NO_ALTERNATIVES; + + if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_START_CONSTRUCTION_INVERTED)) + _currentTrackAlternative |= RIDE_TYPE_ALTERNATIVE_TRACK_TYPE; + + _previousTrackRollEnd = TrackRoll::None; + _previousTrackPitchEnd = TrackPitch::None; + + _currentTrackPieceDirection = 0; + _rideConstructionState = RideConstructionState::Place; + _currentTrackSelectionFlags = 0; + _autoOpeningShop = false; + _autoRotatingShop = true; + _trackPlaceCtrlState = false; + _trackPlaceShiftState = false; } - if (RideTryGetOriginElement(*currentRide, nullptr)) + void OnClose() override { - // Auto open shops if required. - if (currentRide->mode == RideMode::ShopStall && gConfigGeneral.AutoOpenShops) + RideConstructionInvalidateCurrentTrack(); + ViewportSetVisibility(ViewportVisibility::Default); + + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + + // In order to cancel the yellow arrow correctly the + // selection tool should be cancelled. Don't do a tool cancel if + // another window has already taken control of tool. + if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) + ToolCancel(); + + HideGridlines(); + + // If we demolish a currentRide all windows will be closed including the construction window, + // the currentRide at this point is already gone. + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) { - // HACK: Until we find a good a way to defer the game command for opening the shop, stop this - // from getting stuck in an infinite loop as opening the currentRide will try to close this window - if (!_autoOpeningShop) + return; + } + + if (RideTryGetOriginElement(*currentRide, nullptr)) + { + // Auto open shops if required. + if (currentRide->mode == RideMode::ShopStall && gConfigGeneral.AutoOpenShops) { - _autoOpeningShop = true; - auto gameAction = RideSetStatusAction(currentRide->id, RideStatus::Open); - GameActions::Execute(&gameAction); - _autoOpeningShop = false; + // HACK: Until we find a good a way to defer the game command for opening the shop, stop this + // from getting stuck in an infinite loop as opening the currentRide will try to close this window + if (!_autoOpeningShop) + { + _autoOpeningShop = true; + auto gameAction = RideSetStatusAction(currentRide->id, RideStatus::Open); + GameActions::Execute(&gameAction); + _autoOpeningShop = false; + } + } + + currentRide->SetToDefaultInspectionInterval(); + auto intent = Intent(WindowClass::Ride); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, currentRide->id.ToUnderlying()); + ContextOpenIntent(&intent); + } + else + { + auto gameAction = RideDemolishAction(currentRide->id, RIDE_MODIFY_DEMOLISH); + gameAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); + GameActions::Execute(&gameAction); + } + } + + void OnResize() override + { + ResizeFrame(); + WindowRideConstructionUpdateEnabledTrackPieces(); + + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) + { + return; + } + + int32_t rideType = RideGetAlternativeType(*currentRide); + + uint64_t disabledWidgets = 0; + + if (_currentTrackCurve & RideConstructionSpecialPieceSelected) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_BANKING_GROUPBOX) + | (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL) + | (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT) + | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT); + } + + // Disable large curves if the start or end of the track is sloped and large sloped curves are not available + if ((_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None)) + { + if (!IsTrackEnabled(TRACK_SLOPE_CURVE_LARGE) + || !(_previousTrackPitchEnd == TrackPitch::Up25 || _previousTrackPitchEnd == TrackPitch::Down25) + || !(_currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Down25)) + { + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); } } - - currentRide->SetToDefaultInspectionInterval(); - auto intent = Intent(WindowClass::Ride); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, currentRide->id.ToUnderlying()); - ContextOpenIntent(&intent); - } - else - { - auto gameAction = RideDemolishAction(currentRide->id, RIDE_MODIFY_DEMOLISH); - gameAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); - GameActions::Execute(&gameAction); - } - } - - void OnResize() override - { - ResizeFrame(); - WindowRideConstructionUpdateEnabledTrackPieces(); - - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; - } - - int32_t rideType = RideGetAlternativeType(*currentRide); - - uint64_t disabledWidgets = 0; - - if (_currentTrackCurve & RideConstructionSpecialPieceSelected) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_SLOPE_DOWN_STEEP) - | (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP) - | (1uLL << WIDX_CHAIN_LIFT) | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) - | (1uLL << WIDX_BANK_RIGHT); - } - - // Disable large curves if the start or end of the track is sloped and large sloped curves are not available - if ((_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None)) - { - if (!IsTrackEnabled(TRACK_SLOPE_CURVE_LARGE) - || !(_previousTrackPitchEnd == TrackPitch::Up25 || _previousTrackPitchEnd == TrackPitch::Down25) - || !(_currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Down25)) + if (IsTrackEnabled(TRACK_SLOPE_CURVE) && IsTrackEnabled(TRACK_CURVE_VERY_SMALL)) { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); - } - } - if (IsTrackEnabled(TRACK_SLOPE_CURVE) && IsTrackEnabled(TRACK_CURVE_VERY_SMALL)) - { - // Disable small curves if the start or end of the track is sloped. - if (_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None) - { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) - | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); - } - } - if (!IsTrackEnabled(TRACK_SLOPE_CURVE)) - { - if (IsTrackEnabled(TRACK_CURVE_VERTICAL)) - { - // Disable all curves only on vertical track - if (_previousTrackPitchEnd != TrackPitch::Up90 || _currentTrackPitchEnd != TrackPitch::Up90) + // Disable small curves if the start or end of the track is sloped. + if (_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None) { - if (_previousTrackPitchEnd != TrackPitch::Down90 || _currentTrackPitchEnd != TrackPitch::Down90) + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); + } + } + if (!IsTrackEnabled(TRACK_SLOPE_CURVE)) + { + if (IsTrackEnabled(TRACK_CURVE_VERTICAL)) + { + // Disable all curves only on vertical track + if (_previousTrackPitchEnd != TrackPitch::Up90 || _currentTrackPitchEnd != TrackPitch::Up90) + { + if (_previousTrackPitchEnd != TrackPitch::Down90 || _currentTrackPitchEnd != TrackPitch::Down90) + { + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) + | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) + | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); + } + } + } + else + { + // Disable all curves on sloped track + if (_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None) { disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) @@ -359,2915 +370,2962 @@ public: } } } - else + if (!IsTrackEnabled(TRACK_FLAT_ROLL_BANKING)) { - // Disable all curves on sloped track - if (_previousTrackPitchEnd != TrackPitch::None || _currentTrackPitchEnd != TrackPitch::None) - { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) - | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) - | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); - } + // Disable banking + disabledWidgets |= (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) + | (1uLL << WIDX_BANK_RIGHT); } - } - if (!IsTrackEnabled(TRACK_FLAT_ROLL_BANKING)) - { - // Disable banking - disabledWidgets |= (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) - | (1uLL << WIDX_BANK_RIGHT); - } - // Disable banking if the start track is steep and the end of the track becomes flat. - if ((_previousTrackPitchEnd == TrackPitch::Down60 || _previousTrackPitchEnd == TrackPitch::Up60) - && _currentTrackPitchEnd == TrackPitch::None) - { - disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); - } - if (!IsTrackEnabled(TRACK_SLOPE) && !IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN) && !IsTrackEnabled(TRACK_SLOPE_STEEP_UP)) - { - if (!currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) - { - // Disable all slopes - disabledWidgets |= (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN) - | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP); - } - } - // If ride type does not have access to diagonal sloped turns, disallow simultaneous use of banked and sloped diagonals - if (!IsTrackEnabled(TRACK_SLOPE_CURVE_LARGE) && TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection)) - { - if (_currentTrackPitchEnd != TrackPitch::None) + // Disable banking if the start track is steep and the end of the track becomes flat. + if ((_previousTrackPitchEnd == TrackPitch::Down60 || _previousTrackPitchEnd == TrackPitch::Up60) + && _currentTrackPitchEnd == TrackPitch::None) { disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); } - else if (_currentTrackRollEnd != TrackRoll::None) + if (!IsTrackEnabled(TRACK_SLOPE) && !IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN) + && !IsTrackEnabled(TRACK_SLOPE_STEEP_UP)) { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); + if (!currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) + { + // Disable all slopes + disabledWidgets |= (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_SLOPE_DOWN_STEEP) + | (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP) + | (1uLL << WIDX_SLOPE_UP_STEEP); + } } - } - if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT) - && !GetGameState().Cheats.EnableAllDrawableTrackPieces) - { - // Disable lift hill toggle and banking if current track piece is uphill - if (_previousTrackPitchEnd == TrackPitch::Up25 || _previousTrackPitchEnd == TrackPitch::Up60 - || _currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Up60) - disabledWidgets |= 1uLL << WIDX_CHAIN_LIFT | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); - // Disable upward slope if current track piece is not flat - if ((_previousTrackPitchEnd != TrackPitch::None || _previousTrackRollEnd != TrackRoll::None) - && !(_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED)) - disabledWidgets |= (1uLL << WIDX_SLOPE_UP); - } - if (_rideConstructionState == RideConstructionState::State0) - { - disabledWidgets |= (1uLL << WIDX_CONSTRUCT) | (1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_PREVIOUS_SECTION) - | (1uLL << WIDX_NEXT_SECTION); - } - switch (_currentTrackCurve) - { - case EnumValue(TrackCurve::LeftVerySmall): - case EnumValue(TrackCurve::LeftSmall): - case EnumValue(TrackCurve::Left): - case EnumValue(TrackCurve::LeftLarge): - disabledWidgets |= (1uLL << WIDX_BANK_RIGHT); - if (_previousTrackRollEnd == TrackRoll::None) + // If ride type does not have access to diagonal sloped turns, disallow simultaneous use of banked and sloped + // diagonals + if (!IsTrackEnabled(TRACK_SLOPE_CURVE_LARGE) && TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection)) + { + if (_currentTrackPitchEnd != TrackPitch::None) { - disabledWidgets |= (1uLL << WIDX_BANK_LEFT); + disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); } - else + else if (_currentTrackRollEnd != TrackRoll::None) { - disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT); + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); } - break; - case EnumValue(TrackCurve::RightLarge): - case EnumValue(TrackCurve::Right): - case EnumValue(TrackCurve::RightSmall): - case EnumValue(TrackCurve::RightVerySmall): - disabledWidgets |= (1uLL << WIDX_BANK_LEFT); - if (_previousTrackRollEnd == TrackRoll::None) - { + } + if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT) + && !GetGameState().Cheats.EnableAllDrawableTrackPieces) + { + // Disable lift hill toggle and banking if current track piece is uphill + if (_previousTrackPitchEnd == TrackPitch::Up25 || _previousTrackPitchEnd == TrackPitch::Up60 + || _currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Up60) + disabledWidgets |= 1uLL << WIDX_CHAIN_LIFT | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); + // Disable upward slope if current track piece is not flat + if ((_previousTrackPitchEnd != TrackPitch::None || _previousTrackRollEnd != TrackRoll::None) + && !(_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED)) + disabledWidgets |= (1uLL << WIDX_SLOPE_UP); + } + if (_rideConstructionState == RideConstructionState::State0) + { + disabledWidgets |= (1uLL << WIDX_CONSTRUCT) | (1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_PREVIOUS_SECTION) + | (1uLL << WIDX_NEXT_SECTION); + } + switch (_currentTrackCurve) + { + case EnumValue(TrackCurve::LeftVerySmall): + case EnumValue(TrackCurve::LeftSmall): + case EnumValue(TrackCurve::Left): + case EnumValue(TrackCurve::LeftLarge): disabledWidgets |= (1uLL << WIDX_BANK_RIGHT); - } - else + if (_previousTrackRollEnd == TrackRoll::None) + { + disabledWidgets |= (1uLL << WIDX_BANK_LEFT); + } + else + { + disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT); + } + break; + case EnumValue(TrackCurve::RightLarge): + case EnumValue(TrackCurve::Right): + case EnumValue(TrackCurve::RightSmall): + case EnumValue(TrackCurve::RightVerySmall): + disabledWidgets |= (1uLL << WIDX_BANK_LEFT); + if (_previousTrackRollEnd == TrackRoll::None) + { + disabledWidgets |= (1uLL << WIDX_BANK_RIGHT); + } + else + { + disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT); + } + break; + } + if (!IsTrackEnabled(TRACK_SLOPE_ROLL_BANKING)) + { + if (_currentTrackRollEnd != TrackRoll::None) { - disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT); + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); } - break; - } - if (!IsTrackEnabled(TRACK_SLOPE_ROLL_BANKING)) - { - if (_currentTrackRollEnd != TrackRoll::None) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); } - } - if (_previousTrackPitchEnd == _currentTrackPitchEnd) - { - switch (_currentTrackPitchEnd) + if (_previousTrackPitchEnd == _currentTrackPitchEnd) { - case TrackPitch::Up60: - case TrackPitch::Down60: - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE) - | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); - if (!IsTrackEnabled(TRACK_SLOPE_CURVE_STEEP)) - { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_SMALL); - } - break; - case TrackPitch::Up90: - case TrackPitch::Down90: - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE) - | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); - if (!IsTrackEnabled(TRACK_CURVE_VERTICAL)) - { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_SMALL); - } - break; - default: - break; + switch (_currentTrackPitchEnd) + { + case TrackPitch::Up60: + case TrackPitch::Down60: + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); + if (!IsTrackEnabled(TRACK_SLOPE_CURVE_STEEP)) + { + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_SMALL); + } + break; + case TrackPitch::Up90: + case TrackPitch::Down90: + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); + if (!IsTrackEnabled(TRACK_CURVE_VERTICAL)) + { + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_SMALL); + } + break; + default: + break; + } + } + else + { + // Disable all curves + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) + | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) + | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); } - } - else - { - // Disable all curves - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) - | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) - | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); - } - switch (_previousTrackPitchEnd) - { - case TrackPitch::None: - if (_currentTrackCurve != EnumValue(TrackCurve::None) - || (IsTrackEnabled(TRACK_SLOPE_STEEP_LONG) && TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection))) + switch (_previousTrackPitchEnd) + { + case TrackPitch::None: + if (_currentTrackCurve != EnumValue(TrackCurve::None) + || (IsTrackEnabled(TRACK_SLOPE_STEEP_LONG) + && TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection))) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP); + } + break; + case TrackPitch::Down25: + disabledWidgets |= (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP); + break; + case TrackPitch::Down60: + disabledWidgets |= (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP); + if (!IsTrackEnabled(TRACK_SLOPE_LONG) + && !( + IsTrackEnabled(TRACK_SLOPE_STEEP_LONG) + && !TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection))) + { + disabledWidgets |= (1uLL << WIDX_LEVEL); + } + break; + case TrackPitch::Up25: + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN); + break; + case TrackPitch::Up60: + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN); + if (!IsTrackEnabled(TRACK_SLOPE_LONG) + && !( + IsTrackEnabled(TRACK_SLOPE_STEEP_LONG) + && !TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection))) + { + disabledWidgets |= (1uLL << WIDX_LEVEL); + } + break; + case TrackPitch::Down90: + case TrackPitch::Up90: + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP); + break; + } + if (_previousTrackPitchEnd == TrackPitch::None) + { + if (!IsTrackEnabled(TRACK_SLOPE_LONG) && !IsTrackEnabled(TRACK_SLOPE_STEEP_LONG)) { disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP); } - break; - case TrackPitch::Down25: - disabledWidgets |= (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP); - break; - case TrackPitch::Down60: - disabledWidgets |= (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP); - if (!IsTrackEnabled(TRACK_SLOPE_LONG) - && !(IsTrackEnabled(TRACK_SLOPE_STEEP_LONG) && !TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection))) + } + if (IsTrackEnabled(TRACK_SLOPE_VERTICAL)) + { + if (_previousTrackPitchEnd == TrackPitch::Up60 && _currentTrackPieceDirection < 4) { - disabledWidgets |= (1uLL << WIDX_LEVEL); + disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); } - break; - case TrackPitch::Up25: - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN); - break; - case TrackPitch::Up60: - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN); - if (!IsTrackEnabled(TRACK_SLOPE_LONG) - && !(IsTrackEnabled(TRACK_SLOPE_STEEP_LONG) && !TrackPieceDirectionIsDiagonal(_currentTrackPieceDirection))) + if (_previousTrackPitchEnd == TrackPitch::Up90) { - disabledWidgets |= (1uLL << WIDX_LEVEL); + disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); } - break; - case TrackPitch::Down90: - case TrackPitch::Up90: - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP); - break; - } - if (_previousTrackPitchEnd == TrackPitch::None) - { - if (!IsTrackEnabled(TRACK_SLOPE_LONG) && !IsTrackEnabled(TRACK_SLOPE_STEEP_LONG)) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP); - } - } - if (IsTrackEnabled(TRACK_SLOPE_VERTICAL)) - { - if (_previousTrackPitchEnd == TrackPitch::Up60 && _currentTrackPieceDirection < 4) - { - disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); - } - if (_previousTrackPitchEnd == TrackPitch::Up90) - { - disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); - } - if (_previousTrackPitchEnd == TrackPitch::Down60 && _currentTrackPieceDirection < 4) - { - disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); - } - } - if (_previousTrackRollEnd == TrackRoll::Left) - { - disabledWidgets |= (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_LARGE) - | (1uLL << WIDX_BANK_RIGHT); - } - if (_previousTrackRollEnd == TrackRoll::Right) - { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE) - | (1uLL << WIDX_BANK_LEFT); - } - if (_currentTrackRollEnd != _previousTrackRollEnd) - { - disabledWidgets |= (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_LARGE) - | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE); - } - if (_currentTrackPitchEnd != TrackPitch::None) - { - if (IsTrackEnabled(TRACK_SLOPE_ROLL_BANKING)) - { - if (_previousTrackPitchEnd == TrackPitch::None) + if (_previousTrackPitchEnd == TrackPitch::Down60 && _currentTrackPieceDirection < 4) { - if (_currentTrackPitchEnd != TrackPitch::Up25 && _currentTrackPitchEnd != TrackPitch::Down25) - { - disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); - } + disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); } - else + } + if (_previousTrackRollEnd == TrackRoll::Left) + { + disabledWidgets |= (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE_LARGE) | (1uLL << WIDX_BANK_RIGHT); + } + if (_previousTrackRollEnd == TrackRoll::Right) + { + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE) + | (1uLL << WIDX_BANK_LEFT); + } + if (_currentTrackRollEnd != _previousTrackRollEnd) + { + disabledWidgets |= (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE_LARGE) | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) + | (1uLL << WIDX_LEFT_CURVE_LARGE); + } + if (_currentTrackPitchEnd != TrackPitch::None) + { + if (IsTrackEnabled(TRACK_SLOPE_ROLL_BANKING)) { - if (_currentTrackPitchEnd != _previousTrackPitchEnd) - { - disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); - } - else + if (_previousTrackPitchEnd == TrackPitch::None) { if (_currentTrackPitchEnd != TrackPitch::Up25 && _currentTrackPitchEnd != TrackPitch::Down25) { disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); } } - } - } - else - { - disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); - } - } - if (_currentTrackRollEnd != TrackRoll::None || _previousTrackRollEnd != TrackRoll::None) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT); - } - if (_currentTrackCurve != EnumValue(TrackCurve::None)) - { - if (!IsTrackEnabled(TRACK_LIFT_HILL_CURVE)) - { - disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); - } - if (_currentTrackPitchEnd == TrackPitch::None) - { - disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); - } - if (_currentTrackPitchEnd == TrackPitch::Up60) - { - disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); - } - if (_currentTrackPitchEnd == TrackPitch::Down60) - { - disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); - } - } - if (_currentTrackPitchEnd == TrackPitch::Up90 || _previousTrackPitchEnd == TrackPitch::Up90) - { - disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); - } - if (!IsTrackEnabled(TRACK_LIFT_HILL_STEEP)) - { - if (_previousTrackPitchEnd == TrackPitch::Up60 || _currentTrackPitchEnd == TrackPitch::Up60) - { - disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); - } - } - if (_previousTrackRollEnd == TrackRoll::UpsideDown) - { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE) - | (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) - | (1uLL << WIDX_RIGHT_CURVE_LARGE); - } - if (_currentTrackCurve != EnumValue(TrackCurve::None)) - { - if (_currentTrackPitchEnd == TrackPitch::None) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); - } - if (_currentTrackPitchEnd == _previousTrackPitchEnd) - { - if (_currentTrackPitchEnd == TrackPitch::Up25) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP); - if (_currentTrackCurve == EnumValue(TrackCurve::Left) || _currentTrackCurve == EnumValue(TrackCurve::Right) - || _rideConstructionState != RideConstructionState::Back || !IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) + else { - disabledWidgets |= (1uLL << WIDX_LEVEL); + if (_currentTrackPitchEnd != _previousTrackPitchEnd) + { + disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); + } + else + { + if (_currentTrackPitchEnd != TrackPitch::Up25 && _currentTrackPitchEnd != TrackPitch::Down25) + { + disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); + } + } } } - if (_currentTrackPitchEnd == TrackPitch::Down25) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP); - if (_currentTrackCurve == EnumValue(TrackCurve::Left) || _currentTrackCurve == EnumValue(TrackCurve::Right) - || _rideConstructionState != RideConstructionState::Front || !IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) - { - disabledWidgets |= (1uLL << WIDX_LEVEL); - } - } - } - else if (IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP); - if (_currentTrackRollEnd == TrackRoll::Left) - { - disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT); - disabledWidgets &= ~(1uLL << WIDX_BANK_LEFT); - } - if (_currentTrackRollEnd == TrackRoll::Right) - { - disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT); - disabledWidgets &= ~(1uLL << WIDX_BANK_RIGHT); - } - if (_currentTrackRollEnd == TrackRoll::None) + else { disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); - disabledWidgets &= ~(1uLL << WIDX_BANK_STRAIGHT); + } + } + if (_currentTrackRollEnd != TrackRoll::None || _previousTrackRollEnd != TrackRoll::None) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT); + } + if (_currentTrackCurve != EnumValue(TrackCurve::None)) + { + if (!IsTrackEnabled(TRACK_LIFT_HILL_CURVE)) + { + disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); } if (_currentTrackPitchEnd == TrackPitch::None) { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); - disabledWidgets &= ~(1uLL << WIDX_LEVEL); + disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); } - if (_currentTrackPitchEnd == TrackPitch::Up25) + if (_currentTrackPitchEnd == TrackPitch::Up60) { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL); - disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP); + disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); } - if (_currentTrackPitchEnd == TrackPitch::Down25) + if (_currentTrackPitchEnd == TrackPitch::Down60) { - disabledWidgets |= (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP); - disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN); - } - if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)) - { - disabledWidgets &= ~(1uLL << WIDX_LEFT_CURVE_SMALL); - } - if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall)) - { - disabledWidgets &= ~(1uLL << WIDX_RIGHT_CURVE_SMALL); + disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); } } - } - if (_currentTrackCurve != EnumValue(TrackCurve::None) && _currentTrackPitchEnd == TrackPitch::Up60) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_UP); - } - if (_currentTrackCurve != EnumValue(TrackCurve::None) && _currentTrackPitchEnd == TrackPitch::Down60) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN); - } - if ((_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) && !GetGameState().Cheats.EnableChainLiftOnAllTrack) - { - if (_currentTrackPitchEnd != TrackPitch::None && !IsTrackEnabled(TRACK_LIFT_HILL_CURVE)) + if (_currentTrackPitchEnd == TrackPitch::Up90 || _previousTrackPitchEnd == TrackPitch::Up90) { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE) - | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); + disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); } if (!IsTrackEnabled(TRACK_LIFT_HILL_STEEP)) { - if (widgets[WIDX_SLOPE_UP_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP) + if (_previousTrackPitchEnd == TrackPitch::Up60 || _currentTrackPitchEnd == TrackPitch::Up60) + { + disabledWidgets |= (1uLL << WIDX_CHAIN_LIFT); + } + } + if (_previousTrackRollEnd == TrackRoll::UpsideDown) + { + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_LEFT_CURVE_LARGE) + | (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE_LARGE); + } + if (_currentTrackCurve != EnumValue(TrackCurve::None)) + { + if (_currentTrackPitchEnd == TrackPitch::None) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); + } + if (_currentTrackPitchEnd == _previousTrackPitchEnd) + { + if (_currentTrackPitchEnd == TrackPitch::Up25) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP); + if (_currentTrackCurve == EnumValue(TrackCurve::Left) + || _currentTrackCurve == EnumValue(TrackCurve::Right) + || _rideConstructionState != RideConstructionState::Back + || !IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) + { + disabledWidgets |= (1uLL << WIDX_LEVEL); + } + } + if (_currentTrackPitchEnd == TrackPitch::Down25) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP); + if (_currentTrackCurve == EnumValue(TrackCurve::Left) + || _currentTrackCurve == EnumValue(TrackCurve::Right) + || _rideConstructionState != RideConstructionState::Front + || !IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) + { + disabledWidgets |= (1uLL << WIDX_LEVEL); + } + } + } + else if (IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_UP_STEEP); + if (_currentTrackRollEnd == TrackRoll::Left) + { + disabledWidgets |= (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT); + disabledWidgets &= ~(1uLL << WIDX_BANK_LEFT); + } + if (_currentTrackRollEnd == TrackRoll::Right) + { + disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT); + disabledWidgets &= ~(1uLL << WIDX_BANK_RIGHT); + } + if (_currentTrackRollEnd == TrackRoll::None) + { + disabledWidgets |= (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_RIGHT); + disabledWidgets &= ~(1uLL << WIDX_BANK_STRAIGHT); + } + if (_currentTrackPitchEnd == TrackPitch::None) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_SLOPE_UP); + disabledWidgets &= ~(1uLL << WIDX_LEVEL); + } + if (_currentTrackPitchEnd == TrackPitch::Up25) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN) | (1uLL << WIDX_LEVEL); + disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP); + } + if (_currentTrackPitchEnd == TrackPitch::Down25) + { + disabledWidgets |= (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP); + disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN); + } + if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)) + { + disabledWidgets &= ~(1uLL << WIDX_LEFT_CURVE_SMALL); + } + if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall)) + { + disabledWidgets &= ~(1uLL << WIDX_RIGHT_CURVE_SMALL); + } + } + } + if (_currentTrackCurve != EnumValue(TrackCurve::None) && _currentTrackPitchEnd == TrackPitch::Up60) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_UP); + } + if (_currentTrackCurve != EnumValue(TrackCurve::None) && _currentTrackPitchEnd == TrackPitch::Down60) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN); + } + if ((_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) && !GetGameState().Cheats.EnableChainLiftOnAllTrack) + { + if (_currentTrackPitchEnd != TrackPitch::None && !IsTrackEnabled(TRACK_LIFT_HILL_CURVE)) + { + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE) + | (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE_LARGE); + } + if (!IsTrackEnabled(TRACK_LIFT_HILL_STEEP)) + { + if (widgets[WIDX_SLOPE_UP_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP); + } + } + } + if (_previousTrackPitchEnd == TrackPitch::Up60 && _currentTrackCurve != EnumValue(TrackCurve::None)) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_LEVEL); + } + if (_previousTrackPitchEnd == TrackPitch::Down60 && _currentTrackCurve != EnumValue(TrackCurve::None)) + { + disabledWidgets |= (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP_STEEP); + } + if (_currentTrackPitchEnd == TrackPitch::Up90 || _previousTrackPitchEnd == TrackPitch::Up90) + { + if (_currentTrackCurve != EnumValue(TrackCurve::None)) { disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP); } + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); + if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) + { + disabledWidgets |= (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) + | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE); + } } - } - if (_previousTrackPitchEnd == TrackPitch::Up60 && _currentTrackCurve != EnumValue(TrackCurve::None)) - { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_LEVEL); - } - if (_previousTrackPitchEnd == TrackPitch::Down60 && _currentTrackCurve != EnumValue(TrackCurve::None)) - { - disabledWidgets |= (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP_STEEP); - } - if (_currentTrackPitchEnd == TrackPitch::Up90 || _previousTrackPitchEnd == TrackPitch::Up90) - { - if (_currentTrackCurve != EnumValue(TrackCurve::None)) + else if (_currentTrackPitchEnd == TrackPitch::Down90 || _previousTrackPitchEnd == TrackPitch::Down90) { - disabledWidgets |= (1uLL << WIDX_SLOPE_UP_STEEP); + if (_currentTrackCurve != EnumValue(TrackCurve::None)) + { + disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP); + } + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); + if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) + { + disabledWidgets |= (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) + | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE); + } } - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); - if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) + // If the previous track is flat and the next track is flat, attempt to show buttons for helixes + if (_currentTrackPitchEnd == TrackPitch::None && _currentTrackPitchEnd == _previousTrackPitchEnd) { - disabledWidgets |= (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) - | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE); + // If the bank is none, attempt to show unbanked quarter helixes + if (_currentTrackRollEnd == TrackRoll::None + && (_currentTrackCurve == EnumValue(TrackCurve::Left) + || _currentTrackCurve == EnumValue(TrackCurve::Right))) + { + if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER)) + disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); + if (IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) + disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); + } + // If the track is banked left or right and curvature is standard size (2.5 tile radius), attempt to show + // buttons for half or quarter helixes + else if ( + (_currentTrackRollEnd == TrackRoll::Left || _currentTrackRollEnd == TrackRoll::Right) + && (_currentTrackCurve == EnumValue(TrackCurve::Left) + || _currentTrackCurve == EnumValue(TrackCurve::Right))) + { + if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER)) + disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); + if (IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER)) + disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); + } + // If the track is banked left or right and curvature is small size (1.5 tile radius), attempt to show buttons + // for half helixes + else if ( + (_currentTrackRollEnd == TrackRoll::Left || _currentTrackRollEnd == TrackRoll::Right) + && (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) + || _currentTrackCurve == EnumValue(TrackCurve::RightSmall))) + { + if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF)) + disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); + if (IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) + disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); + } } - } - else if (_currentTrackPitchEnd == TrackPitch::Down90 || _previousTrackPitchEnd == TrackPitch::Down90) - { - if (_currentTrackCurve != EnumValue(TrackCurve::None)) + if (IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) { - disabledWidgets |= (1uLL << WIDX_SLOPE_DOWN_STEEP); + if (_rideConstructionState == RideConstructionState::Front) + { + if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) + || _currentTrackCurve == EnumValue(TrackCurve::RightSmall)) + { + if (_currentTrackPitchEnd == TrackPitch::None && _previousTrackRollEnd != TrackRoll::None + && (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT) + || GetGameState().Cheats.EnableAllDrawableTrackPieces)) + { + disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP); + } + } + } + else if (_rideConstructionState == RideConstructionState::Back) + { + if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) + || _currentTrackCurve == EnumValue(TrackCurve::RightSmall)) + { + if (_currentTrackPitchEnd == TrackPitch::None && _previousTrackRollEnd != TrackRoll::None) + { + disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN); + } + } + } } - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); - if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) + if (_currentTrackPieceDirection >= 4) { - disabledWidgets |= (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) - | (1uLL << WIDX_LEFT_CURVE_SMALL) | (1uLL << WIDX_LEFT_CURVE); + disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) + | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) + | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); } - } - // If the previous track is flat and the next track is flat, attempt to show buttons for helixes - if (_currentTrackPitchEnd == TrackPitch::None && _currentTrackPitchEnd == _previousTrackPitchEnd) - { - // If the bank is none, attempt to show unbanked quarter helixes - if (_currentTrackRollEnd == TrackRoll::None - && (_currentTrackCurve == EnumValue(TrackCurve::Left) || _currentTrackCurve == EnumValue(TrackCurve::Right))) - { - if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER)) - disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); - if (IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) - disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); - } - // If the track is banked left or right and curvature is standard size (2.5 tile radius), attempt to show buttons - // for half or quarter helixes - else if ( - (_currentTrackRollEnd == TrackRoll::Left || _currentTrackRollEnd == TrackRoll::Right) - && (_currentTrackCurve == EnumValue(TrackCurve::Left) || _currentTrackCurve == EnumValue(TrackCurve::Right))) - { - if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER)) - disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); - if (IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER)) - disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); - } - // If the track is banked left or right and curvature is small size (1.5 tile radius), attempt to show buttons for - // half helixes - else if ( - (_currentTrackRollEnd == TrackRoll::Left || _currentTrackRollEnd == TrackRoll::Right) - && (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) - || _currentTrackCurve == EnumValue(TrackCurve::RightSmall))) - { - if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF)) - disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN_STEEP); - if (IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) - disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP_STEEP); - } - } - if (IsTrackEnabled(TRACK_SLOPE_CURVE_BANKED)) - { if (_rideConstructionState == RideConstructionState::Front) { - if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) - || _currentTrackCurve == EnumValue(TrackCurve::RightSmall)) + disabledWidgets |= (1uLL << WIDX_NEXT_SECTION); + if (WindowRideConstructionUpdateState(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { - if (_currentTrackPitchEnd == TrackPitch::None && _previousTrackRollEnd != TrackRoll::None - && (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT) - || GetGameState().Cheats.EnableAllDrawableTrackPieces)) - { - disabledWidgets &= ~(1uLL << WIDX_SLOPE_UP); - } + disabledWidgets |= (1uLL << WIDX_CONSTRUCT); } } else if (_rideConstructionState == RideConstructionState::Back) { - if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) - || _currentTrackCurve == EnumValue(TrackCurve::RightSmall)) + disabledWidgets |= (1uLL << WIDX_PREVIOUS_SECTION); + if (WindowRideConstructionUpdateState(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { - if (_currentTrackPitchEnd == TrackPitch::None && _previousTrackRollEnd != TrackRoll::None) - { - disabledWidgets &= ~(1uLL << WIDX_SLOPE_DOWN); - } + disabledWidgets |= (1uLL << WIDX_CONSTRUCT); } } - } - if (_currentTrackPieceDirection >= 4) - { - disabledWidgets |= (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) - | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_RIGHT_CURVE) | (1uLL << WIDX_RIGHT_CURVE_SMALL) - | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL); - } - if (_rideConstructionState == RideConstructionState::Front) - { - disabledWidgets |= (1uLL << WIDX_NEXT_SECTION); - if (WindowRideConstructionUpdateState(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) + if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_TRACK_ELEMENTS_HAVE_TWO_VARIETIES)) { - disabledWidgets |= (1uLL << WIDX_CONSTRUCT); + disabledWidgets &= ~(1uLL << WIDX_BANKING_GROUPBOX); } - } - else if (_rideConstructionState == RideConstructionState::Back) - { - disabledWidgets |= (1uLL << WIDX_PREVIOUS_SECTION); - if (WindowRideConstructionUpdateState(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) + if (_rideConstructionState == RideConstructionState::EntranceExit + || _rideConstructionState == RideConstructionState::Selected) { - disabledWidgets |= (1uLL << WIDX_CONSTRUCT); + disabledWidgets |= (1uLL << WIDX_DIRECTION_GROUPBOX) | (1uLL << WIDX_SLOPE_GROUPBOX) + | (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) + | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) + | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL) + | (1uLL << WIDX_SPECIAL_TRACK_DROPDOWN) | (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN) + | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT) + | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT) + | (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); } - } - if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_TRACK_ELEMENTS_HAVE_TWO_VARIETIES)) - { - disabledWidgets &= ~(1uLL << WIDX_BANKING_GROUPBOX); - } - if (_rideConstructionState == RideConstructionState::EntranceExit - || _rideConstructionState == RideConstructionState::Selected) - { - disabledWidgets |= (1uLL << WIDX_DIRECTION_GROUPBOX) | (1uLL << WIDX_SLOPE_GROUPBOX) - | (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_LEFT_CURVE_VERY_SMALL) | (1uLL << WIDX_LEFT_CURVE_SMALL) - | (1uLL << WIDX_LEFT_CURVE) | (1uLL << WIDX_STRAIGHT) | (1uLL << WIDX_RIGHT_CURVE) - | (1uLL << WIDX_RIGHT_CURVE_SMALL) | (1uLL << WIDX_RIGHT_CURVE_VERY_SMALL) - | (1uLL << WIDX_SPECIAL_TRACK_DROPDOWN) | (1uLL << WIDX_SLOPE_DOWN_STEEP) | (1uLL << WIDX_SLOPE_DOWN) - | (1uLL << WIDX_LEVEL) | (1uLL << WIDX_SLOPE_UP) | (1uLL << WIDX_SLOPE_UP_STEEP) | (1uLL << WIDX_CHAIN_LIFT) - | (1uLL << WIDX_BANK_LEFT) | (1uLL << WIDX_BANK_STRAIGHT) | (1uLL << WIDX_BANK_RIGHT) - | (1uLL << WIDX_LEFT_CURVE_LARGE) | (1uLL << WIDX_RIGHT_CURVE_LARGE); - } - if (_currentlyShowingBrakeOrBoosterSpeed) - { - disabledWidgets &= ~(1uLL << WIDX_BANKING_GROUPBOX); - disabledWidgets &= ~(1uLL << WIDX_BANK_LEFT); - disabledWidgets &= ~(1uLL << WIDX_BANK_STRAIGHT); - disabledWidgets &= ~(1uLL << WIDX_BANK_RIGHT); - } - - // If chain lift cheat is enabled then show the chain lift widget no matter what - if (GetGameState().Cheats.EnableChainLiftOnAllTrack) - { - disabledWidgets &= ~(1uLL << WIDX_CHAIN_LIFT); - } - - // Set and invalidate the changed widgets - uint64_t currentDisabledWidgets = disabled_widgets; - if (currentDisabledWidgets == disabledWidgets) - return; - - for (WidgetIndex i = 0; i < 64; i++) - { - if ((disabledWidgets & (1uLL << i)) != (currentDisabledWidgets & (1uLL << i))) + if (_currentlyShowingBrakeOrBoosterSpeed) { - WidgetInvalidate(*this, i); + disabledWidgets &= ~(1uLL << WIDX_BANKING_GROUPBOX); + disabledWidgets &= ~(1uLL << WIDX_BANK_LEFT); + disabledWidgets &= ~(1uLL << WIDX_BANK_STRAIGHT); + disabledWidgets &= ~(1uLL << WIDX_BANK_RIGHT); } - } - disabled_widgets = disabledWidgets; - } - void OnUpdate() override - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; + // If chain lift cheat is enabled then show the chain lift widget no matter what + if (GetGameState().Cheats.EnableChainLiftOnAllTrack) + { + disabledWidgets &= ~(1uLL << WIDX_CHAIN_LIFT); + } + + // Set and invalidate the changed widgets + uint64_t currentDisabledWidgets = disabled_widgets; + if (currentDisabledWidgets == disabledWidgets) + return; + + for (WidgetIndex i = 0; i < 64; i++) + { + if ((disabledWidgets & (1uLL << i)) != (currentDisabledWidgets & (1uLL << i))) + { + WidgetInvalidate(*this, i); + } + } + disabled_widgets = disabledWidgets; } - // Close construction window if currentRide is not closed, - // editing currentRide while open will cause many issues until properly handled - if (currentRide->status != RideStatus::Closed && currentRide->status != RideStatus::Simulating) + void OnUpdate() override { - Close(); - return; - } + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) + { + return; + } - switch (_currentTrackCurve) - { - case TrackElemType::SpinningTunnel | RideConstructionSpecialPieceSelected: - case TrackElemType::Whirlpool | RideConstructionSpecialPieceSelected: - case TrackElemType::Rapids | RideConstructionSpecialPieceSelected: - case TrackElemType::Waterfall | RideConstructionSpecialPieceSelected: - WidgetInvalidate(*this, WIDX_CONSTRUCT); - break; - } - - if (_rideConstructionState == RideConstructionState::Place) - { - if (!WidgetIsActiveTool(*this, WIDX_CONSTRUCT)) + // Close construction window if currentRide is not closed, + // editing currentRide while open will cause many issues until properly handled + if (currentRide->status != RideStatus::Closed && currentRide->status != RideStatus::Simulating) { Close(); return; } + + switch (_currentTrackCurve) + { + case TrackElemType::SpinningTunnel | RideConstructionSpecialPieceSelected: + case TrackElemType::Whirlpool | RideConstructionSpecialPieceSelected: + case TrackElemType::Rapids | RideConstructionSpecialPieceSelected: + case TrackElemType::Waterfall | RideConstructionSpecialPieceSelected: + WidgetInvalidate(*this, WIDX_CONSTRUCT); + break; + } + + if (_rideConstructionState == RideConstructionState::Place) + { + if (!WidgetIsActiveTool(*this, WIDX_CONSTRUCT)) + { + Close(); + return; + } + } + + if (_rideConstructionState == RideConstructionState::EntranceExit) + { + if (!WidgetIsActiveTool(*this, WIDX_ENTRANCE) && !WidgetIsActiveTool(*this, WIDX_EXIT)) + { + _rideConstructionState = gRideEntranceExitPlacePreviousRideConstructionState; + WindowRideConstructionUpdateActiveElements(); + } + } + + switch (_rideConstructionState) + { + case RideConstructionState::Front: + case RideConstructionState::Back: + case RideConstructionState::Selected: + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + && gCurrentToolWidget.window_classification == WindowClass::RideConstruction) + { + ToolCancel(); + } + break; + default: + break; + } + + UpdateGhostTrackAndArrow(); } - if (_rideConstructionState == RideConstructionState::EntranceExit) + void OnMouseUp(WidgetIndex widgetIndex) override { - if (!WidgetIsActiveTool(*this, WIDX_ENTRANCE) && !WidgetIsActiveTool(*this, WIDX_EXIT)) + WindowRideConstructionUpdateEnabledTrackPieces(); + switch (widgetIndex) { - _rideConstructionState = gRideEntranceExitPlacePreviousRideConstructionState; - WindowRideConstructionUpdateActiveElements(); + case WIDX_CLOSE: + Close(); + break; + case WIDX_NEXT_SECTION: + RideSelectNextSection(); + break; + case WIDX_PREVIOUS_SECTION: + RideSelectPreviousSection(); + break; + case WIDX_CONSTRUCT: + Construct(); + // Force any footpath construction to recheck the area. + gProvisionalFootpath.Flags |= PROVISIONAL_PATH_FLAG_2; + break; + case WIDX_DEMOLISH: + MouseUpDemolish(); + break; + case WIDX_ROTATE: + Rotate(); + break; + case WIDX_ENTRANCE: + EntranceClick(); + break; + case WIDX_EXIT: + ExitClick(); + break; + case WIDX_SIMULATE: + { + auto currentRide = GetRide(_currentRideIndex); + if (currentRide != nullptr) + { + auto status = currentRide->status == RideStatus::Simulating ? RideStatus::Closed + : RideStatus::Simulating; + auto gameAction = RideSetStatusAction(currentRide->id, status); + GameActions::Execute(&gameAction); + } + break; + } } } - switch (_rideConstructionState) + void OnMouseDown(WidgetIndex widgetIndex) override { - case RideConstructionState::Front: - case RideConstructionState::Back: - case RideConstructionState::Selected: - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - && gCurrentToolWidget.window_classification == WindowClass::RideConstruction) - { - ToolCancel(); - } - break; - default: - break; - } - - UpdateGhostTrackAndArrow(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - WindowRideConstructionUpdateEnabledTrackPieces(); - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_NEXT_SECTION: - RideSelectNextSection(); - break; - case WIDX_PREVIOUS_SECTION: - RideSelectPreviousSection(); - break; - case WIDX_CONSTRUCT: - Construct(); - // Force any footpath construction to recheck the area. - gProvisionalFootpath.Flags |= PROVISIONAL_PATH_FLAG_2; - break; - case WIDX_DEMOLISH: - MouseUpDemolish(); - break; - case WIDX_ROTATE: - Rotate(); - break; - case WIDX_ENTRANCE: - EntranceClick(); - break; - case WIDX_EXIT: - ExitClick(); - break; - case WIDX_SIMULATE: + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide != nullptr) - { - auto status = currentRide->status == RideStatus::Simulating ? RideStatus::Closed : RideStatus::Simulating; - auto gameAction = RideSetStatusAction(currentRide->id, status); - GameActions::Execute(&gameAction); - } - break; + return; } - } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; - } - - WindowRideConstructionUpdateEnabledTrackPieces(); - switch (widgetIndex) - { - case WIDX_LEFT_CURVE: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::Left); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_RIGHT_CURVE: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::Right); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_LEFT_CURVE_SMALL: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::LeftSmall); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_RIGHT_CURVE_SMALL: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::RightSmall); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_LEFT_CURVE_VERY_SMALL: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::LeftVerySmall); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_RIGHT_CURVE_VERY_SMALL: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::RightVerySmall); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_LEFT_CURVE_LARGE: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::LeftLarge); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_RIGHT_CURVE_LARGE: - RideConstructionInvalidateCurrentTrack(); - _currentTrackCurve = EnumValue(TrackCurve::RightLarge); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_STRAIGHT: - RideConstructionInvalidateCurrentTrack(); - if (_currentTrackCurve != EnumValue(TrackCurve::None)) - _currentTrackRollEnd = TrackRoll::None; - _currentTrackCurve = EnumValue(TrackCurve::None); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_SLOPE_DOWN_STEEP: - RideConstructionInvalidateCurrentTrack(); - if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) - { - if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) + WindowRideConstructionUpdateEnabledTrackPieces(); + switch (widgetIndex) + { + case WIDX_LEFT_CURVE: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::Left); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_RIGHT_CURVE: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::Right); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_LEFT_CURVE_SMALL: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::LeftSmall); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_RIGHT_CURVE_SMALL: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::RightSmall); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_LEFT_CURVE_VERY_SMALL: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::LeftVerySmall); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_RIGHT_CURVE_VERY_SMALL: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::RightVerySmall); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_LEFT_CURVE_LARGE: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::LeftLarge); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_RIGHT_CURVE_LARGE: + RideConstructionInvalidateCurrentTrack(); + _currentTrackCurve = EnumValue(TrackCurve::RightLarge); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_STRAIGHT: + RideConstructionInvalidateCurrentTrack(); + if (_currentTrackCurve != EnumValue(TrackCurve::None)) + _currentTrackRollEnd = TrackRoll::None; + _currentTrackCurve = EnumValue(TrackCurve::None); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_SLOPE_DOWN_STEEP: + RideConstructionInvalidateCurrentTrack(); + if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) { - _currentTrackCurve = TrackElemType::LeftHalfBankedHelixDownLarge | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) - { - _currentTrackCurve = TrackElemType::RightHalfBankedHelixDownLarge - | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) && _currentTrackRollEnd == TrackRoll::Left) - { - _currentTrackCurve = TrackElemType::LeftHalfBankedHelixDownSmall | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall) && _currentTrackRollEnd == TrackRoll::Right) - { - _currentTrackCurve = TrackElemType::RightHalfBankedHelixDownSmall - | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - } - if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER)) - { - if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) - { - _currentTrackCurve = TrackElemType::LeftQuarterBankedHelixLargeDown - | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) - { - _currentTrackCurve = TrackElemType::RightQuarterBankedHelixLargeDown - | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - } - if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) - { - if (_currentTrackRollEnd == TrackRoll::None) - { - if (_currentTrackCurve == EnumValue(TrackCurve::Left)) + if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) { - _currentTrackCurve = TrackElemType::LeftQuarterHelixLargeDown + _currentTrackCurve = TrackElemType::LeftHalfBankedHelixDownLarge | RideConstructionSpecialPieceSelected; _currentTrackPrice = kMoney64Undefined; WindowRideConstructionUpdateActiveElements(); break; } - if (_currentTrackCurve == EnumValue(TrackCurve::Right)) + if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) { - _currentTrackCurve = TrackElemType::RightQuarterHelixLargeDown + _currentTrackCurve = TrackElemType::RightHalfBankedHelixDownLarge + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) && _currentTrackRollEnd == TrackRoll::Left) + { + _currentTrackCurve = TrackElemType::LeftHalfBankedHelixDownSmall + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall) && _currentTrackRollEnd == TrackRoll::Right) + { + _currentTrackCurve = TrackElemType::RightHalfBankedHelixDownSmall | RideConstructionSpecialPieceSelected; _currentTrackPrice = kMoney64Undefined; WindowRideConstructionUpdateActiveElements(); break; } } - } - if (widgets[WIDX_SLOPE_DOWN_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP) - { - UpdateLiftHillSelected(TrackPitch::Down60); - } - else - { - UpdateLiftHillSelected(TrackPitch::Up90); - } - break; - case WIDX_SLOPE_DOWN: - RideConstructionInvalidateCurrentTrack(); - if (_rideConstructionState == RideConstructionState::Back && _currentTrackRollEnd != TrackRoll::None) - { - _currentTrackRollEnd = TrackRoll::None; - } - UpdateLiftHillSelected(TrackPitch::Down25); - break; - case WIDX_LEVEL: - RideConstructionInvalidateCurrentTrack(); - if (_rideConstructionState == RideConstructionState::Front && _previousTrackPitchEnd == TrackPitch::Down25) - { - if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)) + if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER)) { - _currentTrackRollEnd = TrackRoll::Left; - } - else if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall)) - { - _currentTrackRollEnd = TrackRoll::Right; - } - } - else if (_rideConstructionState == RideConstructionState::Back && _previousTrackPitchEnd == TrackPitch::Up25) - { - if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)) - { - _currentTrackRollEnd = TrackRoll::Left; - } - else if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall)) - { - _currentTrackRollEnd = TrackRoll::Right; - } - } - UpdateLiftHillSelected(TrackPitch::None); - break; - case WIDX_SLOPE_UP: - RideConstructionInvalidateCurrentTrack(); - if (_rideConstructionState == RideConstructionState::Front && _currentTrackRollEnd != TrackRoll::None) - { - _currentTrackRollEnd = TrackRoll::None; - } - if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) - { - if (_rideConstructionState == RideConstructionState::Front - && _currentTrackCurve == EnumValue(TrackCurve::None)) - { - _currentTrackCurve = TrackElemType::ReverseFreefallSlope | RideConstructionSpecialPieceSelected; - WindowRideConstructionUpdateActiveElements(); - } - } - else - { - UpdateLiftHillSelected(TrackPitch::Up25); - } - break; - case WIDX_SLOPE_UP_STEEP: - RideConstructionInvalidateCurrentTrack(); - if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) - { - if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) - { - _currentTrackCurve = TrackElemType::LeftHalfBankedHelixUpLarge | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) - { - _currentTrackCurve = TrackElemType::RightHalfBankedHelixUpLarge | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) && _currentTrackRollEnd == TrackRoll::Left) - { - _currentTrackCurve = TrackElemType::LeftHalfBankedHelixUpSmall | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall) && _currentTrackRollEnd == TrackRoll::Right) - { - _currentTrackCurve = TrackElemType::RightHalfBankedHelixUpSmall | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - } - if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER)) - { - if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) - { - _currentTrackCurve = TrackElemType::LeftQuarterBankedHelixLargeUp - | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) - { - _currentTrackCurve = TrackElemType::RightQuarterBankedHelixLargeUp - | RideConstructionSpecialPieceSelected; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - } - } - if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) - { - if (_currentTrackRollEnd == TrackRoll::None) - { - if (_currentTrackCurve == EnumValue(TrackCurve::Left)) + if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) { - _currentTrackCurve = TrackElemType::LeftQuarterHelixLargeUp | RideConstructionSpecialPieceSelected; + _currentTrackCurve = TrackElemType::LeftQuarterBankedHelixLargeDown + | RideConstructionSpecialPieceSelected; _currentTrackPrice = kMoney64Undefined; WindowRideConstructionUpdateActiveElements(); break; } - if (_currentTrackCurve == EnumValue(TrackCurve::Right)) + if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) { - _currentTrackCurve = TrackElemType::RightQuarterHelixLargeUp | RideConstructionSpecialPieceSelected; + _currentTrackCurve = TrackElemType::RightQuarterBankedHelixLargeDown + | RideConstructionSpecialPieceSelected; _currentTrackPrice = kMoney64Undefined; WindowRideConstructionUpdateActiveElements(); break; } } - } - if (widgets[WIDX_SLOPE_UP_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP) - { - UpdateLiftHillSelected(TrackPitch::Up60); - } - else - { - UpdateLiftHillSelected(TrackPitch::Down90); - } - break; - case WIDX_CHAIN_LIFT: - RideConstructionInvalidateCurrentTrack(); - _currentTrackLiftHill ^= CONSTRUCTION_LIFT_HILL_SELECTED; - if ((_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) - && !GetGameState().Cheats.EnableChainLiftOnAllTrack) + if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) + { + if (_currentTrackRollEnd == TrackRoll::None) + { + if (_currentTrackCurve == EnumValue(TrackCurve::Left)) + { + _currentTrackCurve = TrackElemType::LeftQuarterHelixLargeDown + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::Right)) + { + _currentTrackCurve = TrackElemType::RightQuarterHelixLargeDown + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + } + } + if (widgets[WIDX_SLOPE_DOWN_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP) + { + UpdateLiftHillSelected(TrackPitch::Down60); + } + else + { + UpdateLiftHillSelected(TrackPitch::Up90); + } + break; + case WIDX_SLOPE_DOWN: + RideConstructionInvalidateCurrentTrack(); + if (_rideConstructionState == RideConstructionState::Back && _currentTrackRollEnd != TrackRoll::None) + { + _currentTrackRollEnd = TrackRoll::None; + } + UpdateLiftHillSelected(TrackPitch::Down25); + break; + case WIDX_LEVEL: + RideConstructionInvalidateCurrentTrack(); + if (_rideConstructionState == RideConstructionState::Front && _previousTrackPitchEnd == TrackPitch::Down25) + { + if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)) + { + _currentTrackRollEnd = TrackRoll::Left; + } + else if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall)) + { + _currentTrackRollEnd = TrackRoll::Right; + } + } + else if ( + _rideConstructionState == RideConstructionState::Back && _previousTrackPitchEnd == TrackPitch::Up25) + { + if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall)) + { + _currentTrackRollEnd = TrackRoll::Left; + } + else if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall)) + { + _currentTrackRollEnd = TrackRoll::Right; + } + } + UpdateLiftHillSelected(TrackPitch::None); + break; + case WIDX_SLOPE_UP: + RideConstructionInvalidateCurrentTrack(); + if (_rideConstructionState == RideConstructionState::Front && _currentTrackRollEnd != TrackRoll::None) + { + _currentTrackRollEnd = TrackRoll::None; + } + if (currentRide->GetRideTypeDescriptor().SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) + { + if (_rideConstructionState == RideConstructionState::Front + && _currentTrackCurve == EnumValue(TrackCurve::None)) + { + _currentTrackCurve = TrackElemType::ReverseFreefallSlope | RideConstructionSpecialPieceSelected; + WindowRideConstructionUpdateActiveElements(); + } + } + else + { + UpdateLiftHillSelected(TrackPitch::Up25); + } + break; + case WIDX_SLOPE_UP_STEEP: + RideConstructionInvalidateCurrentTrack(); + if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) + { + if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) + { + _currentTrackCurve = TrackElemType::LeftHalfBankedHelixUpLarge + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) + { + _currentTrackCurve = TrackElemType::RightHalfBankedHelixUpLarge + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::LeftSmall) && _currentTrackRollEnd == TrackRoll::Left) + { + _currentTrackCurve = TrackElemType::LeftHalfBankedHelixUpSmall + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::RightSmall) && _currentTrackRollEnd == TrackRoll::Right) + { + _currentTrackCurve = TrackElemType::RightHalfBankedHelixUpSmall + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + } + if (IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER)) + { + if (_currentTrackCurve == EnumValue(TrackCurve::Left) && _currentTrackRollEnd == TrackRoll::Left) + { + _currentTrackCurve = TrackElemType::LeftQuarterBankedHelixLargeUp + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::Right) && _currentTrackRollEnd == TrackRoll::Right) + { + _currentTrackCurve = TrackElemType::RightQuarterBankedHelixLargeUp + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + } + if (IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) + { + if (_currentTrackRollEnd == TrackRoll::None) + { + if (_currentTrackCurve == EnumValue(TrackCurve::Left)) + { + _currentTrackCurve = TrackElemType::LeftQuarterHelixLargeUp + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + if (_currentTrackCurve == EnumValue(TrackCurve::Right)) + { + _currentTrackCurve = TrackElemType::RightQuarterHelixLargeUp + | RideConstructionSpecialPieceSelected; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + } + } + } + if (widgets[WIDX_SLOPE_UP_STEEP].tooltip == STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP) + { + UpdateLiftHillSelected(TrackPitch::Up60); + } + else + { + UpdateLiftHillSelected(TrackPitch::Down90); + } + break; + case WIDX_CHAIN_LIFT: + RideConstructionInvalidateCurrentTrack(); + _currentTrackLiftHill ^= CONSTRUCTION_LIFT_HILL_SELECTED; + if ((_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) + && !GetGameState().Cheats.EnableChainLiftOnAllTrack) + _currentTrackAlternative &= ~RIDE_TYPE_ALTERNATIVE_TRACK_PIECES; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_BANK_LEFT: + RideConstructionInvalidateCurrentTrack(); + if (!_currentlyShowingBrakeOrBoosterSpeed) + { + _currentTrackRollEnd = TrackRoll::Left; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + } + break; + case WIDX_BANK_STRAIGHT: + RideConstructionInvalidateCurrentTrack(); + if (!_currentlyShowingBrakeOrBoosterSpeed) + { + _currentTrackRollEnd = TrackRoll::None; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + } + else + { + uint8_t* brakesSpeedPtr = &_currentBrakeSpeed2; + uint8_t maxBrakesSpeed = 30; + uint8_t brakesSpeed = *brakesSpeedPtr + 2; + if (brakesSpeed <= maxBrakesSpeed) + { + if (_rideConstructionState == RideConstructionState::Selected) + { + SetBrakeSpeed(brakesSpeed); + } + else + { + *brakesSpeedPtr = brakesSpeed; + WindowRideConstructionUpdateActiveElements(); + } + } + } + break; + case WIDX_BANK_RIGHT: + RideConstructionInvalidateCurrentTrack(); + if (!_currentlyShowingBrakeOrBoosterSpeed) + { + _currentTrackRollEnd = TrackRoll::Right; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + } + else + { + uint8_t* brakesSpeedPtr = &_currentBrakeSpeed2; + uint8_t brakesSpeed = *brakesSpeedPtr - 2; + if (brakesSpeed >= 2) + { + if (_rideConstructionState == RideConstructionState::Selected) + { + SetBrakeSpeed(brakesSpeed); + } + else + { + *brakesSpeedPtr = brakesSpeed; + WindowRideConstructionUpdateActiveElements(); + } + } + } + break; + case WIDX_SPECIAL_TRACK_DROPDOWN: + ShowSpecialTrackDropdown(&widgets[widgetIndex]); + break; + case WIDX_U_TRACK: + RideConstructionInvalidateCurrentTrack(); _currentTrackAlternative &= ~RIDE_TYPE_ALTERNATIVE_TRACK_PIECES; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_BANK_LEFT: - RideConstructionInvalidateCurrentTrack(); - if (!_currentlyShowingBrakeOrBoosterSpeed) - { - _currentTrackRollEnd = TrackRoll::Left; _currentTrackPrice = kMoney64Undefined; WindowRideConstructionUpdateActiveElements(); - } - break; - case WIDX_BANK_STRAIGHT: - RideConstructionInvalidateCurrentTrack(); - if (!_currentlyShowingBrakeOrBoosterSpeed) - { + break; + case WIDX_O_TRACK: + RideConstructionInvalidateCurrentTrack(); + _currentTrackAlternative |= RIDE_TYPE_ALTERNATIVE_TRACK_PIECES; + if (!GetGameState().Cheats.EnableChainLiftOnAllTrack) + _currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED; + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + break; + case WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP: + if (_currentSeatRotationAngle < 15) + { + if (_rideConstructionState == RideConstructionState::Selected) + { + RideSelectedTrackSetSeatRotation(_currentSeatRotationAngle + 1); + } + else + { + _currentSeatRotationAngle++; + WindowRideConstructionUpdateActiveElements(); + } + } + break; + case WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN: + if (_currentSeatRotationAngle > 0) + { + if (_rideConstructionState == RideConstructionState::Selected) + { + RideSelectedTrackSetSeatRotation(_currentSeatRotationAngle - 1); + } + else + { + _currentSeatRotationAngle--; + WindowRideConstructionUpdateActiveElements(); + } + } + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + if (widgetIndex != WIDX_SPECIAL_TRACK_DROPDOWN) + return; + if (selectedIndex == -1) + return; + + RideConstructionInvalidateCurrentTrack(); + _currentTrackPrice = kMoney64Undefined; + track_type_t trackPiece = _specialElementDropdownState.Elements[selectedIndex].TrackType; + switch (trackPiece) + { + case TrackElemType::EndStation: + case TrackElemType::SBendLeft: + case TrackElemType::SBendRight: + _currentTrackPitchEnd = TrackPitch::None; + break; + case TrackElemType::LeftVerticalLoop: + case TrackElemType::RightVerticalLoop: _currentTrackRollEnd = TrackRoll::None; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - } - else - { - uint8_t* brakesSpeedPtr = &_currentBrakeSpeed2; - uint8_t maxBrakesSpeed = 30; - uint8_t brakesSpeed = *brakesSpeedPtr + 2; - if (brakesSpeed <= maxBrakesSpeed) - { - if (_rideConstructionState == RideConstructionState::Selected) - { - SetBrakeSpeed(brakesSpeed); - } - else - { - *brakesSpeedPtr = brakesSpeed; - WindowRideConstructionUpdateActiveElements(); - } - } - } - break; - case WIDX_BANK_RIGHT: - RideConstructionInvalidateCurrentTrack(); - if (!_currentlyShowingBrakeOrBoosterSpeed) - { - _currentTrackRollEnd = TrackRoll::Right; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - } - else - { - uint8_t* brakesSpeedPtr = &_currentBrakeSpeed2; - uint8_t brakesSpeed = *brakesSpeedPtr - 2; - if (brakesSpeed >= 2) - { - if (_rideConstructionState == RideConstructionState::Selected) - { - SetBrakeSpeed(brakesSpeed); - } - else - { - *brakesSpeedPtr = brakesSpeed; - WindowRideConstructionUpdateActiveElements(); - } - } - } - break; - case WIDX_SPECIAL_TRACK_DROPDOWN: - ShowSpecialTrackDropdown(&widgets[widgetIndex]); - break; - case WIDX_U_TRACK: - RideConstructionInvalidateCurrentTrack(); - _currentTrackAlternative &= ~RIDE_TYPE_ALTERNATIVE_TRACK_PIECES; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_O_TRACK: - RideConstructionInvalidateCurrentTrack(); - _currentTrackAlternative |= RIDE_TYPE_ALTERNATIVE_TRACK_PIECES; - if (!GetGameState().Cheats.EnableChainLiftOnAllTrack) _currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED; - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - break; - case WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP: - if (_currentSeatRotationAngle < 15) - { - if (_rideConstructionState == RideConstructionState::Selected) - { - RideSelectedTrackSetSeatRotation(_currentSeatRotationAngle + 1); - } - else - { - _currentSeatRotationAngle++; - WindowRideConstructionUpdateActiveElements(); - } - } - break; - case WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN: - if (_currentSeatRotationAngle > 0) - { - if (_rideConstructionState == RideConstructionState::Selected) - { - RideSelectedTrackSetSeatRotation(_currentSeatRotationAngle - 1); - } - else - { - _currentSeatRotationAngle--; - WindowRideConstructionUpdateActiveElements(); - } - } - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (widgetIndex != WIDX_SPECIAL_TRACK_DROPDOWN) - return; - if (selectedIndex == -1) - return; - - RideConstructionInvalidateCurrentTrack(); - _currentTrackPrice = kMoney64Undefined; - track_type_t trackPiece = _specialElementDropdownState.Elements[selectedIndex].TrackType; - switch (trackPiece) - { - case TrackElemType::EndStation: - case TrackElemType::SBendLeft: - case TrackElemType::SBendRight: - _currentTrackPitchEnd = TrackPitch::None; - break; - case TrackElemType::LeftVerticalLoop: - case TrackElemType::RightVerticalLoop: - _currentTrackRollEnd = TrackRoll::None; - _currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED; - break; - case TrackElemType::BlockBrakes: - case TrackElemType::DiagBlockBrakes: - _currentBrakeSpeed2 = kRCT2DefaultBlockBrakeSpeed; - } - _currentTrackCurve = trackPiece | RideConstructionSpecialPieceSelected; - WindowRideConstructionUpdateActiveElements(); - } - - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_CONSTRUCT: - RideConstructionToolupdateConstruct(screenCoords); - break; - case WIDX_ENTRANCE: - case WIDX_EXIT: - RideConstructionToolupdateEntranceExit(screenCoords); - break; - } - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_CONSTRUCT: - RideConstructionTooldownConstruct(screenCoords); - break; - case WIDX_ENTRANCE: - case WIDX_EXIT: - ToolDownEntranceExit(screenCoords); - break; - } - } - - void OnPrepareDraw() override - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; + break; + case TrackElemType::BlockBrakes: + case TrackElemType::DiagBlockBrakes: + _currentBrakeSpeed2 = kRCT2DefaultBlockBrakeSpeed; + } + _currentTrackCurve = trackPiece | RideConstructionSpecialPieceSelected; + WindowRideConstructionUpdateActiveElements(); } - StringId stringId = STR_RIDE_CONSTRUCTION_SPECIAL; - if (_currentTrackCurve & RideConstructionSpecialPieceSelected) + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - const auto& rtd = currentRide->GetRideTypeDescriptor(); - const auto& ted = GetTrackElementDescriptor(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); - stringId = ted.Description; - if (stringId == STR_RAPIDS && rtd.Category != RIDE_CATEGORY_WATER) + switch (widgetIndex) { - stringId = STR_LOG_BUMPS; + case WIDX_CONSTRUCT: + RideConstructionToolupdateConstruct(screenCoords); + break; + case WIDX_ENTRANCE: + case WIDX_EXIT: + RideConstructionToolupdateEntranceExit(screenCoords); + break; } } - auto ft = Formatter::Common(); - ft.Add(stringId); - if (_currentlyShowingBrakeOrBoosterSpeed) + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - uint16_t brakeSpeed2 = ((_currentBrakeSpeed2 * 9) >> 2) & 0xFFFF; - if (TrackTypeIsBooster(_selectedTrackType) - || TrackTypeIsBooster(_currentTrackCurve & ~RideConstructionSpecialPieceSelected)) + switch (widgetIndex) { - brakeSpeed2 = GetBoosterSpeed(currentRide->type, brakeSpeed2); + case WIDX_CONSTRUCT: + RideConstructionTooldownConstruct(screenCoords); + break; + case WIDX_ENTRANCE: + case WIDX_EXIT: + ToolDownEntranceExit(screenCoords); + break; } - ft.Add(brakeSpeed2); } - widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].text = RideConstructionSeatAngleRotationStrings[_currentSeatRotationAngle]; - - // Simulate button - auto& simulateWidget = widgets[WIDX_SIMULATE]; - simulateWidget.type = WindowWidgetType::Empty; - if (currentRide->SupportsStatus(RideStatus::Simulating)) + void OnPrepareDraw() override { - simulateWidget.type = WindowWidgetType::FlatBtn; - if (currentRide->status == RideStatus::Simulating) + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) { - pressed_widgets |= (1uLL << WIDX_SIMULATE); + return; + } + + StringId stringId = STR_RIDE_CONSTRUCTION_SPECIAL; + if (_currentTrackCurve & RideConstructionSpecialPieceSelected) + { + const auto& rtd = currentRide->GetRideTypeDescriptor(); + const auto& ted = GetTrackElementDescriptor(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); + stringId = ted.Description; + if (stringId == STR_RAPIDS && rtd.Category != RIDE_CATEGORY_WATER) + { + stringId = STR_LOG_BUMPS; + } + } + auto ft = Formatter::Common(); + ft.Add(stringId); + + if (_currentlyShowingBrakeOrBoosterSpeed) + { + uint16_t brakeSpeed2 = ((_currentBrakeSpeed2 * 9) >> 2) & 0xFFFF; + if (TrackTypeIsBooster(_selectedTrackType) + || TrackTypeIsBooster(_currentTrackCurve & ~RideConstructionSpecialPieceSelected)) + { + brakeSpeed2 = GetBoosterSpeed(currentRide->type, brakeSpeed2); + } + ft.Add(brakeSpeed2); + } + + widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].text = RideConstructionSeatAngleRotationStrings + [_currentSeatRotationAngle]; + + // Simulate button + auto& simulateWidget = widgets[WIDX_SIMULATE]; + simulateWidget.type = WindowWidgetType::Empty; + if (currentRide->SupportsStatus(RideStatus::Simulating)) + { + simulateWidget.type = WindowWidgetType::FlatBtn; + if (currentRide->status == RideStatus::Simulating) + { + pressed_widgets |= (1uLL << WIDX_SIMULATE); + } + else + { + pressed_widgets &= ~(1uLL << WIDX_SIMULATE); + } + } + + // Set window title arguments + ft = Formatter::Common(); + ft.Increment(4); + currentRide->FormatNameTo(ft); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawPixelInfo clipdpi; + Widget* widget; + int32_t widgetWidth, widgetHeight; + + DrawWidgets(dpi); + + widget = &widgets[WIDX_CONSTRUCT]; + if (widget->type == WindowWidgetType::Empty) + return; + + RideId rideIndex; + int32_t trackType, trackDirection, liftHillAndInvertedState; + if (WindowRideConstructionUpdateState( + &trackType, &trackDirection, &rideIndex, &liftHillAndInvertedState, nullptr, nullptr)) + return; + + // Draw track piece + auto screenCoords = ScreenCoordsXY{ windowPos.x + widget->left + 1, windowPos.y + widget->top + 1 }; + widgetWidth = widget->width() - 1; + widgetHeight = widget->height() - 1; + if (ClipDrawPixelInfo(clipdpi, dpi, screenCoords, widgetWidth, widgetHeight)) + { + DrawTrackPiece( + clipdpi, rideIndex, trackType, trackDirection, liftHillAndInvertedState, widgetWidth, widgetHeight); + } + + // Draw cost + screenCoords = { windowPos.x + widget->midX(), windowPos.y + widget->bottom - 23 }; + if (_rideConstructionState != RideConstructionState::Place) + DrawTextBasic(dpi, screenCoords, STR_BUILD_THIS, {}, { TextAlignment::CENTRE }); + + screenCoords.y += 11; + if (_currentTrackPrice != kMoney64Undefined && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + auto ft = Formatter(); + ft.Add(_currentTrackPrice); + DrawTextBasic(dpi, screenCoords, STR_COST_LABEL, ft, { TextAlignment::CENTRE }); + } + } + + void UpdateWidgets() + { + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) + { + return; + } + int32_t rideType = RideGetAlternativeType(*currentRide); + + hold_down_widgets = 0; + if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY) || !currentRide->HasStation()) + { + widgets[WIDX_ENTRANCE_EXIT_GROUPBOX].type = WindowWidgetType::Empty; + widgets[WIDX_ENTRANCE].type = WindowWidgetType::Empty; + widgets[WIDX_EXIT].type = WindowWidgetType::Empty; } else { - pressed_widgets &= ~(1uLL << WIDX_SIMULATE); + widgets[WIDX_ENTRANCE_EXIT_GROUPBOX].type = WindowWidgetType::Groupbox; + widgets[WIDX_ENTRANCE].type = WindowWidgetType::Button; + widgets[WIDX_EXIT].type = WindowWidgetType::Button; } - } - // Set window title arguments - ft = Formatter::Common(); - ft.Increment(4); - currentRide->FormatNameTo(ft); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawPixelInfo clipdpi; - Widget* widget; - int32_t widgetWidth, widgetHeight; - - DrawWidgets(dpi); - - widget = &widgets[WIDX_CONSTRUCT]; - if (widget->type == WindowWidgetType::Empty) - return; - - RideId rideIndex; - int32_t trackType, trackDirection, liftHillAndInvertedState; - if (WindowRideConstructionUpdateState( - &trackType, &trackDirection, &rideIndex, &liftHillAndInvertedState, nullptr, nullptr)) - return; - - // Draw track piece - auto screenCoords = ScreenCoordsXY{ windowPos.x + widget->left + 1, windowPos.y + widget->top + 1 }; - widgetWidth = widget->width() - 1; - widgetHeight = widget->height() - 1; - if (ClipDrawPixelInfo(clipdpi, dpi, screenCoords, widgetWidth, widgetHeight)) - { - DrawTrackPiece(clipdpi, rideIndex, trackType, trackDirection, liftHillAndInvertedState, widgetWidth, widgetHeight); - } - - // Draw cost - screenCoords = { windowPos.x + widget->midX(), windowPos.y + widget->bottom - 23 }; - if (_rideConstructionState != RideConstructionState::Place) - DrawTextBasic(dpi, screenCoords, STR_BUILD_THIS, {}, { TextAlignment::CENTRE }); - - screenCoords.y += 11; - if (_currentTrackPrice != kMoney64Undefined && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - auto ft = Formatter(); - ft.Add(_currentTrackPrice); - DrawTextBasic(dpi, screenCoords, STR_COST_LABEL, ft, { TextAlignment::CENTRE }); - } - } - - void UpdateWidgets() - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; - } - int32_t rideType = RideGetAlternativeType(*currentRide); - - hold_down_widgets = 0; - if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY) || !currentRide->HasStation()) - { - widgets[WIDX_ENTRANCE_EXIT_GROUPBOX].type = WindowWidgetType::Empty; - widgets[WIDX_ENTRANCE].type = WindowWidgetType::Empty; - widgets[WIDX_EXIT].type = WindowWidgetType::Empty; - } - else - { - widgets[WIDX_ENTRANCE_EXIT_GROUPBOX].type = WindowWidgetType::Groupbox; - widgets[WIDX_ENTRANCE].type = WindowWidgetType::Button; - widgets[WIDX_EXIT].type = WindowWidgetType::Button; - } - - if (_specialElementDropdownState.HasActiveElements) - { - widgets[WIDX_SPECIAL_TRACK_DROPDOWN].type = WindowWidgetType::Button; - } - else - { - widgets[WIDX_SPECIAL_TRACK_DROPDOWN].type = WindowWidgetType::Empty; - } - - if (IsTrackEnabled(TRACK_STRAIGHT)) - { - widgets[WIDX_STRAIGHT].type = WindowWidgetType::FlatBtn; - } - else - { - widgets[WIDX_STRAIGHT].type = WindowWidgetType::Empty; - } - - if (IsTrackEnabled(TRACK_CURVE_LARGE)) - { - widgets[WIDX_LEFT_CURVE_LARGE].type = WindowWidgetType::FlatBtn; - widgets[WIDX_RIGHT_CURVE_LARGE].type = WindowWidgetType::FlatBtn; - } - else - { - widgets[WIDX_LEFT_CURVE_LARGE].type = WindowWidgetType::Empty; - widgets[WIDX_RIGHT_CURVE_LARGE].type = WindowWidgetType::Empty; - } - - widgets[WIDX_LEFT_CURVE].type = WindowWidgetType::Empty; - widgets[WIDX_RIGHT_CURVE].type = WindowWidgetType::Empty; - widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::Empty; - widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::Empty; - widgets[WIDX_LEFT_CURVE_VERY_SMALL].type = WindowWidgetType::Empty; - widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type = WindowWidgetType::Empty; - if (IsTrackEnabled(TRACK_CURVE_VERTICAL)) - { - widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; - widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; - } - if (IsTrackEnabled(TRACK_CURVE)) - { - widgets[WIDX_LEFT_CURVE].type = WindowWidgetType::FlatBtn; - widgets[WIDX_RIGHT_CURVE].type = WindowWidgetType::FlatBtn; - } - if (IsTrackEnabled(TRACK_CURVE_SMALL)) - { - widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; - widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; - } - if (IsTrackEnabled(TRACK_CURVE_VERY_SMALL)) - { - widgets[WIDX_LEFT_CURVE_VERY_SMALL].type = WindowWidgetType::FlatBtn; - widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type = WindowWidgetType::FlatBtn; - } - - widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::Empty; - widgets[WIDX_SLOPE_DOWN].type = WindowWidgetType::Empty; - widgets[WIDX_LEVEL].type = WindowWidgetType::Empty; - widgets[WIDX_SLOPE_UP].type = WindowWidgetType::Empty; - widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::Empty; - widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN_STEEP); - widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP; - widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP_STEEP); - widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP; - if (GetRideTypeDescriptor(rideType).SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) - { - widgets[WIDX_LEVEL].type = WindowWidgetType::FlatBtn; - widgets[WIDX_SLOPE_UP].type = WindowWidgetType::FlatBtn; - } - if (IsTrackEnabled(TRACK_SLOPE) || IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN) || IsTrackEnabled(TRACK_SLOPE_STEEP_UP)) - { - widgets[WIDX_LEVEL].type = WindowWidgetType::FlatBtn; - } - if (IsTrackEnabled(TRACK_SLOPE)) - { - widgets[WIDX_SLOPE_DOWN].type = WindowWidgetType::FlatBtn; - widgets[WIDX_SLOPE_UP].type = WindowWidgetType::FlatBtn; - } - if ((IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) - && _currentTrackRollEnd != TrackRoll::None && _currentTrackPitchEnd == TrackPitch::None) - { - if (_currentTrackCurve >= EnumValue(TrackCurve::Left) && _currentTrackCurve <= EnumValue(TrackCurve::RightSmall)) + if (_specialElementDropdownState.HasActiveElements) + { + widgets[WIDX_SPECIAL_TRACK_DROPDOWN].type = WindowWidgetType::Button; + } + else + { + widgets[WIDX_SPECIAL_TRACK_DROPDOWN].type = WindowWidgetType::Empty; + } + + if (IsTrackEnabled(TRACK_STRAIGHT)) + { + widgets[WIDX_STRAIGHT].type = WindowWidgetType::FlatBtn; + } + else + { + widgets[WIDX_STRAIGHT].type = WindowWidgetType::Empty; + } + + if (IsTrackEnabled(TRACK_CURVE_LARGE)) + { + widgets[WIDX_LEFT_CURVE_LARGE].type = WindowWidgetType::FlatBtn; + widgets[WIDX_RIGHT_CURVE_LARGE].type = WindowWidgetType::FlatBtn; + } + else + { + widgets[WIDX_LEFT_CURVE_LARGE].type = WindowWidgetType::Empty; + widgets[WIDX_RIGHT_CURVE_LARGE].type = WindowWidgetType::Empty; + } + + widgets[WIDX_LEFT_CURVE].type = WindowWidgetType::Empty; + widgets[WIDX_RIGHT_CURVE].type = WindowWidgetType::Empty; + widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::Empty; + widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::Empty; + widgets[WIDX_LEFT_CURVE_VERY_SMALL].type = WindowWidgetType::Empty; + widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type = WindowWidgetType::Empty; + if (IsTrackEnabled(TRACK_CURVE_VERTICAL)) + { + widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; + widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; + } + if (IsTrackEnabled(TRACK_CURVE)) + { + widgets[WIDX_LEFT_CURVE].type = WindowWidgetType::FlatBtn; + widgets[WIDX_RIGHT_CURVE].type = WindowWidgetType::FlatBtn; + } + if (IsTrackEnabled(TRACK_CURVE_SMALL)) + { + widgets[WIDX_LEFT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; + widgets[WIDX_RIGHT_CURVE_SMALL].type = WindowWidgetType::FlatBtn; + } + if (IsTrackEnabled(TRACK_CURVE_VERY_SMALL)) + { + widgets[WIDX_LEFT_CURVE_VERY_SMALL].type = WindowWidgetType::FlatBtn; + widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type = WindowWidgetType::FlatBtn; + } + + widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::Empty; + widgets[WIDX_SLOPE_DOWN].type = WindowWidgetType::Empty; + widgets[WIDX_LEVEL].type = WindowWidgetType::Empty; + widgets[WIDX_SLOPE_UP].type = WindowWidgetType::Empty; + widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::Empty; + widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN_STEEP); + widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP; + widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP_STEEP); + widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP; + if (GetRideTypeDescriptor(rideType).SupportsTrackPiece(TRACK_REVERSE_FREEFALL)) + { + widgets[WIDX_LEVEL].type = WindowWidgetType::FlatBtn; + widgets[WIDX_SLOPE_UP].type = WindowWidgetType::FlatBtn; + } + if (IsTrackEnabled(TRACK_SLOPE) || IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN) || IsTrackEnabled(TRACK_SLOPE_STEEP_UP)) + { + widgets[WIDX_LEVEL].type = WindowWidgetType::FlatBtn; + } + if (IsTrackEnabled(TRACK_SLOPE)) + { + widgets[WIDX_SLOPE_DOWN].type = WindowWidgetType::FlatBtn; + widgets[WIDX_SLOPE_UP].type = WindowWidgetType::FlatBtn; + } + if ((IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) + && _currentTrackRollEnd != TrackRoll::None && _currentTrackPitchEnd == TrackPitch::None) + { + if (_currentTrackCurve >= EnumValue(TrackCurve::Left) + && _currentTrackCurve <= EnumValue(TrackCurve::RightSmall)) + { + // Enable helix + widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::FlatBtn; + if (rideType != RIDE_TYPE_SPLASH_BOATS && rideType != RIDE_TYPE_RIVER_RAFTS) + widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::FlatBtn; + } + } + + if (IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN)) { - // Enable helix widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::FlatBtn; - if (rideType != RIDE_TYPE_SPLASH_BOATS && rideType != RIDE_TYPE_RIVER_RAFTS) - widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::FlatBtn; } - } - - if (IsTrackEnabled(TRACK_SLOPE_STEEP_DOWN)) - { - widgets[WIDX_SLOPE_DOWN_STEEP].type = WindowWidgetType::FlatBtn; - } - if (IsTrackEnabled(TRACK_SLOPE_STEEP_UP)) - { - widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::FlatBtn; - } - - if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT) - && (_currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Up60) - && !GetGameState().Cheats.EnableAllDrawableTrackPieces) - { - _currentTrackLiftHill |= CONSTRUCTION_LIFT_HILL_SELECTED; - } - - if ((IsTrackEnabled(TRACK_LIFT_HILL) && (_currentTrackCurve & RideConstructionSpecialPieceSelected) == 0) - || (GetGameState().Cheats.EnableChainLiftOnAllTrack - && currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK))) - { - widgets[WIDX_CHAIN_LIFT].type = WindowWidgetType::FlatBtn; - } - else - { - widgets[WIDX_CHAIN_LIFT].type = WindowWidgetType::Empty; - } - - int32_t x = 45; - for (int32_t i = WIDX_SLOPE_DOWN_STEEP; i <= WIDX_SLOPE_UP_STEEP; i++) - { - widgets[i].left = x; - widgets[i].right = x + 23; - x += 24; - } - - widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP_STEEP); - widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP; - widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN_STEEP); - widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP; - if (IsTrackEnabled(TRACK_SLOPE_VERTICAL)) - { - if (_previousTrackPitchEnd == TrackPitch::Up60 || _previousTrackPitchEnd == TrackPitch::Up90) + if (IsTrackEnabled(TRACK_SLOPE_STEEP_UP)) { - int32_t originalSlopeUpSteepLeft = widgets[WIDX_SLOPE_UP_STEEP].left; - int32_t originalSlopeUpSteepRight = widgets[WIDX_SLOPE_UP_STEEP].right; - for (int32_t i = WIDX_SLOPE_UP_STEEP; i > WIDX_SLOPE_DOWN_STEEP; i--) + widgets[WIDX_SLOPE_UP_STEEP].type = WindowWidgetType::FlatBtn; + } + + if (currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_UP_INCLINE_REQUIRES_LIFT) + && (_currentTrackPitchEnd == TrackPitch::Up25 || _currentTrackPitchEnd == TrackPitch::Up60) + && !GetGameState().Cheats.EnableAllDrawableTrackPieces) + { + _currentTrackLiftHill |= CONSTRUCTION_LIFT_HILL_SELECTED; + } + + if ((IsTrackEnabled(TRACK_LIFT_HILL) && (_currentTrackCurve & RideConstructionSpecialPieceSelected) == 0) + || (GetGameState().Cheats.EnableChainLiftOnAllTrack + && currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK))) + { + widgets[WIDX_CHAIN_LIFT].type = WindowWidgetType::FlatBtn; + } + else + { + widgets[WIDX_CHAIN_LIFT].type = WindowWidgetType::Empty; + } + + int32_t x = 45; + for (int32_t i = WIDX_SLOPE_DOWN_STEEP; i <= WIDX_SLOPE_UP_STEEP; i++) + { + widgets[i].left = x; + widgets[i].right = x + 23; + x += 24; + } + + widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_UP_STEEP); + widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_UP_TIP; + widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_SLOPE_DOWN_STEEP); + widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_STEEP_SLOPE_DOWN_TIP; + if (IsTrackEnabled(TRACK_SLOPE_VERTICAL)) + { + if (_previousTrackPitchEnd == TrackPitch::Up60 || _previousTrackPitchEnd == TrackPitch::Up90) { - widgets[i].left = widgets[i - 1].left; - widgets[i].right = widgets[i - 1].right; - } - widgets[WIDX_SLOPE_DOWN_STEEP].left = originalSlopeUpSteepLeft; - widgets[WIDX_SLOPE_DOWN_STEEP].right = originalSlopeUpSteepRight; - widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_VERTICAL_RISE); - widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_VERTICAL_RISE_TIP; - } - else if (_previousTrackPitchEnd == TrackPitch::Down60 || _previousTrackPitchEnd == TrackPitch::Down90) - { - int32_t originalSlopeDownSteepLeft = widgets[WIDX_SLOPE_DOWN_STEEP].left; - int32_t originalSlopeDownSteepRight = widgets[WIDX_SLOPE_DOWN_STEEP].right; - for (int32_t i = WIDX_SLOPE_DOWN_STEEP; i < WIDX_SLOPE_UP_STEEP; i++) - { - widgets[i].left = widgets[i + 1].left; - widgets[i].right = widgets[i + 1].right; - } - widgets[WIDX_SLOPE_UP_STEEP].left = originalSlopeDownSteepLeft; - widgets[WIDX_SLOPE_UP_STEEP].right = originalSlopeDownSteepRight; - widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_VERTICAL_DROP); - widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_VERTICAL_DROP_TIP; - } - } - - if ((IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) - && _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd == TrackRoll::None - && (_currentTrackCurve == EnumValue(TrackCurve::Left) || _currentTrackCurve == EnumValue(TrackCurve::Right))) - { - widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_DOWN); - widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_DOWN_TIP; - widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_UP); - widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_UP_TIP; - - int32_t tmp = widgets[WIDX_SLOPE_DOWN_STEEP].left; - widgets[WIDX_SLOPE_DOWN_STEEP].left = widgets[WIDX_SLOPE_DOWN].left; - widgets[WIDX_SLOPE_DOWN].left = tmp; - - tmp = widgets[WIDX_SLOPE_DOWN_STEEP].right; - widgets[WIDX_SLOPE_DOWN_STEEP].right = widgets[WIDX_SLOPE_DOWN].right; - widgets[WIDX_SLOPE_DOWN].right = tmp; - - tmp = widgets[WIDX_SLOPE_UP_STEEP].left; - widgets[WIDX_SLOPE_UP_STEEP].left = widgets[WIDX_SLOPE_UP].left; - widgets[WIDX_SLOPE_UP].left = tmp; - - tmp = widgets[WIDX_SLOPE_UP_STEEP].right; - widgets[WIDX_SLOPE_UP_STEEP].right = widgets[WIDX_SLOPE_UP].right; - widgets[WIDX_SLOPE_UP].right = tmp; - } - - if ((IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER) - || IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) - && (_currentTrackCurve >= EnumValue(TrackCurve::Left) && _currentTrackCurve <= EnumValue(TrackCurve::RightSmall)) - && _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd != TrackRoll::None) - { - widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_DOWN); - widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_DOWN_TIP; - widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_UP); - widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_UP_TIP; - - int32_t tmp = widgets[WIDX_SLOPE_DOWN_STEEP].left; - widgets[WIDX_SLOPE_DOWN_STEEP].left = widgets[WIDX_SLOPE_DOWN].left; - widgets[WIDX_SLOPE_DOWN].left = tmp; - - tmp = widgets[WIDX_SLOPE_DOWN_STEEP].right; - widgets[WIDX_SLOPE_DOWN_STEEP].right = widgets[WIDX_SLOPE_DOWN].right; - widgets[WIDX_SLOPE_DOWN].right = tmp; - - tmp = widgets[WIDX_SLOPE_UP_STEEP].left; - widgets[WIDX_SLOPE_UP_STEEP].left = widgets[WIDX_SLOPE_UP].left; - widgets[WIDX_SLOPE_UP].left = tmp; - - tmp = widgets[WIDX_SLOPE_UP_STEEP].right; - widgets[WIDX_SLOPE_UP_STEEP].right = widgets[WIDX_SLOPE_UP].right; - widgets[WIDX_SLOPE_UP].right = tmp; - } - - widgets[WIDX_BANKING_GROUPBOX].image = ImageId(STR_RIDE_CONSTRUCTION_ROLL_BANKING); - widgets[WIDX_BANK_LEFT].image = ImageId(SPR_RIDE_CONSTRUCTION_LEFT_BANK); - widgets[WIDX_BANK_LEFT].tooltip = STR_RIDE_CONSTRUCTION_ROLL_FOR_LEFT_CURVE_TIP; - widgets[WIDX_BANK_LEFT].left = 69; - widgets[WIDX_BANK_LEFT].right = 92; - widgets[WIDX_BANK_LEFT].top = 132; - widgets[WIDX_BANK_LEFT].bottom = 155; - widgets[WIDX_BANK_STRAIGHT].image = ImageId(SPR_RIDE_CONSTRUCTION_NO_BANK); - widgets[WIDX_BANK_STRAIGHT].tooltip = STR_RIDE_CONSTRUCTION_NO_ROLL_TIP; - widgets[WIDX_BANK_STRAIGHT].left = 93; - widgets[WIDX_BANK_STRAIGHT].right = 116; - widgets[WIDX_BANK_STRAIGHT].top = 132; - widgets[WIDX_BANK_STRAIGHT].bottom = 155; - widgets[WIDX_BANK_RIGHT].image = ImageId(SPR_RIDE_CONSTRUCTION_RIGHT_BANK); - widgets[WIDX_BANK_RIGHT].tooltip = STR_RIDE_CONSTRUCTION_ROLL_FOR_RIGHT_CURVE_TIP; - widgets[WIDX_BANK_RIGHT].left = 117; - widgets[WIDX_BANK_RIGHT].right = 140; - widgets[WIDX_BANK_RIGHT].top = 132; - widgets[WIDX_BANK_RIGHT].bottom = 155; - widgets[WIDX_BANK_LEFT].type = WindowWidgetType::Empty; - widgets[WIDX_BANK_STRAIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_BANK_RIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_U_TRACK].type = WindowWidgetType::Empty; - widgets[WIDX_O_TRACK].type = WindowWidgetType::Empty; - - bool trackHasSpeedSetting = TrackTypeHasSpeedSetting(_selectedTrackType) - || TrackTypeHasSpeedSetting(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); - bool boosterTrackSelected = TrackTypeIsBooster(_selectedTrackType) - || TrackTypeIsBooster(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); - - // Only necessary because TD6 writes speed and seat rotation to the same bits. Remove for new track design format. - bool trackHasSpeedAndSeatRotation = _selectedTrackType == TrackElemType::BlockBrakes - || _currentTrackCurve == (RideConstructionSpecialPieceSelected | TrackElemType::BlockBrakes) - || _selectedTrackType > TrackElemType::HighestAlias - || _currentTrackCurve > (RideConstructionSpecialPieceSelected | TrackElemType::HighestAlias); - - const auto& rtd = GetRideTypeDescriptor(rideType); - bool rideHasSeatRotation = rtd.HasFlag(RIDE_TYPE_FLAG_HAS_SEAT_ROTATION); - - if (!trackHasSpeedSetting) - { - if (IsTrackEnabled(TRACK_FLAT_ROLL_BANKING)) - { - widgets[WIDX_BANK_LEFT].type = WindowWidgetType::FlatBtn; - widgets[WIDX_BANK_STRAIGHT].type = WindowWidgetType::FlatBtn; - widgets[WIDX_BANK_RIGHT].type = WindowWidgetType::FlatBtn; - } - if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_TRACK_ELEMENTS_HAVE_TWO_VARIETIES)) - { - if (rideType == RIDE_TYPE_WATER_COASTER) - { - widgets[WIDX_U_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_RC_TRACK); - widgets[WIDX_O_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_WATER_CHANNEL); - widgets[WIDX_U_TRACK].tooltip = STR_RIDE_CONSTRUCTION_STANDARD_RC_TRACK_TIP; - widgets[WIDX_O_TRACK].tooltip = STR_RIDE_CONSTRUCTION_WATER_CHANNEL_TIP; - if ((_currentTrackCurve < EnumValue(TrackCurve::LeftSmall) - || _currentTrackCurve == (RideConstructionSpecialPieceSelected | TrackElemType::SBendLeft) - || _currentTrackCurve == (RideConstructionSpecialPieceSelected | TrackElemType::SBendRight)) - && _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd == TrackRoll::None) + int32_t originalSlopeUpSteepLeft = widgets[WIDX_SLOPE_UP_STEEP].left; + int32_t originalSlopeUpSteepRight = widgets[WIDX_SLOPE_UP_STEEP].right; + for (int32_t i = WIDX_SLOPE_UP_STEEP; i > WIDX_SLOPE_DOWN_STEEP; i--) { + widgets[i].left = widgets[i - 1].left; + widgets[i].right = widgets[i - 1].right; + } + widgets[WIDX_SLOPE_DOWN_STEEP].left = originalSlopeUpSteepLeft; + widgets[WIDX_SLOPE_DOWN_STEEP].right = originalSlopeUpSteepRight; + widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_VERTICAL_RISE); + widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_VERTICAL_RISE_TIP; + } + else if (_previousTrackPitchEnd == TrackPitch::Down60 || _previousTrackPitchEnd == TrackPitch::Down90) + { + int32_t originalSlopeDownSteepLeft = widgets[WIDX_SLOPE_DOWN_STEEP].left; + int32_t originalSlopeDownSteepRight = widgets[WIDX_SLOPE_DOWN_STEEP].right; + for (int32_t i = WIDX_SLOPE_DOWN_STEEP; i < WIDX_SLOPE_UP_STEEP; i++) + { + widgets[i].left = widgets[i + 1].left; + widgets[i].right = widgets[i + 1].right; + } + widgets[WIDX_SLOPE_UP_STEEP].left = originalSlopeDownSteepLeft; + widgets[WIDX_SLOPE_UP_STEEP].right = originalSlopeDownSteepRight; + widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_VERTICAL_DROP); + widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_VERTICAL_DROP_TIP; + } + } + + if ((IsTrackEnabled(TRACK_HELIX_DOWN_UNBANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_UNBANKED_QUARTER)) + && _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd == TrackRoll::None + && (_currentTrackCurve == EnumValue(TrackCurve::Left) || _currentTrackCurve == EnumValue(TrackCurve::Right))) + { + widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_DOWN); + widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_DOWN_TIP; + widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_UP); + widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_UP_TIP; + + int32_t tmp = widgets[WIDX_SLOPE_DOWN_STEEP].left; + widgets[WIDX_SLOPE_DOWN_STEEP].left = widgets[WIDX_SLOPE_DOWN].left; + widgets[WIDX_SLOPE_DOWN].left = tmp; + + tmp = widgets[WIDX_SLOPE_DOWN_STEEP].right; + widgets[WIDX_SLOPE_DOWN_STEEP].right = widgets[WIDX_SLOPE_DOWN].right; + widgets[WIDX_SLOPE_DOWN].right = tmp; + + tmp = widgets[WIDX_SLOPE_UP_STEEP].left; + widgets[WIDX_SLOPE_UP_STEEP].left = widgets[WIDX_SLOPE_UP].left; + widgets[WIDX_SLOPE_UP].left = tmp; + + tmp = widgets[WIDX_SLOPE_UP_STEEP].right; + widgets[WIDX_SLOPE_UP_STEEP].right = widgets[WIDX_SLOPE_UP].right; + widgets[WIDX_SLOPE_UP].right = tmp; + } + + if ((IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_QUARTER) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_QUARTER) + || IsTrackEnabled(TRACK_HELIX_DOWN_BANKED_HALF) || IsTrackEnabled(TRACK_HELIX_UP_BANKED_HALF)) + && (_currentTrackCurve >= EnumValue(TrackCurve::Left) + && _currentTrackCurve <= EnumValue(TrackCurve::RightSmall)) + && _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd != TrackRoll::None) + { + widgets[WIDX_SLOPE_DOWN_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_DOWN); + widgets[WIDX_SLOPE_DOWN_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_DOWN_TIP; + widgets[WIDX_SLOPE_UP_STEEP].image = ImageId(SPR_RIDE_CONSTRUCTION_HELIX_UP); + widgets[WIDX_SLOPE_UP_STEEP].tooltip = STR_RIDE_CONSTRUCTION_HELIX_UP_TIP; + + int32_t tmp = widgets[WIDX_SLOPE_DOWN_STEEP].left; + widgets[WIDX_SLOPE_DOWN_STEEP].left = widgets[WIDX_SLOPE_DOWN].left; + widgets[WIDX_SLOPE_DOWN].left = tmp; + + tmp = widgets[WIDX_SLOPE_DOWN_STEEP].right; + widgets[WIDX_SLOPE_DOWN_STEEP].right = widgets[WIDX_SLOPE_DOWN].right; + widgets[WIDX_SLOPE_DOWN].right = tmp; + + tmp = widgets[WIDX_SLOPE_UP_STEEP].left; + widgets[WIDX_SLOPE_UP_STEEP].left = widgets[WIDX_SLOPE_UP].left; + widgets[WIDX_SLOPE_UP].left = tmp; + + tmp = widgets[WIDX_SLOPE_UP_STEEP].right; + widgets[WIDX_SLOPE_UP_STEEP].right = widgets[WIDX_SLOPE_UP].right; + widgets[WIDX_SLOPE_UP].right = tmp; + } + + widgets[WIDX_BANKING_GROUPBOX].image = ImageId(STR_RIDE_CONSTRUCTION_ROLL_BANKING); + widgets[WIDX_BANK_LEFT].image = ImageId(SPR_RIDE_CONSTRUCTION_LEFT_BANK); + widgets[WIDX_BANK_LEFT].tooltip = STR_RIDE_CONSTRUCTION_ROLL_FOR_LEFT_CURVE_TIP; + widgets[WIDX_BANK_LEFT].left = 69; + widgets[WIDX_BANK_LEFT].right = 92; + widgets[WIDX_BANK_LEFT].top = 132; + widgets[WIDX_BANK_LEFT].bottom = 155; + widgets[WIDX_BANK_STRAIGHT].image = ImageId(SPR_RIDE_CONSTRUCTION_NO_BANK); + widgets[WIDX_BANK_STRAIGHT].tooltip = STR_RIDE_CONSTRUCTION_NO_ROLL_TIP; + widgets[WIDX_BANK_STRAIGHT].left = 93; + widgets[WIDX_BANK_STRAIGHT].right = 116; + widgets[WIDX_BANK_STRAIGHT].top = 132; + widgets[WIDX_BANK_STRAIGHT].bottom = 155; + widgets[WIDX_BANK_RIGHT].image = ImageId(SPR_RIDE_CONSTRUCTION_RIGHT_BANK); + widgets[WIDX_BANK_RIGHT].tooltip = STR_RIDE_CONSTRUCTION_ROLL_FOR_RIGHT_CURVE_TIP; + widgets[WIDX_BANK_RIGHT].left = 117; + widgets[WIDX_BANK_RIGHT].right = 140; + widgets[WIDX_BANK_RIGHT].top = 132; + widgets[WIDX_BANK_RIGHT].bottom = 155; + widgets[WIDX_BANK_LEFT].type = WindowWidgetType::Empty; + widgets[WIDX_BANK_STRAIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_BANK_RIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_U_TRACK].type = WindowWidgetType::Empty; + widgets[WIDX_O_TRACK].type = WindowWidgetType::Empty; + + bool trackHasSpeedSetting = TrackTypeHasSpeedSetting(_selectedTrackType) + || TrackTypeHasSpeedSetting(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); + bool boosterTrackSelected = TrackTypeIsBooster(_selectedTrackType) + || TrackTypeIsBooster(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); + + // Only necessary because TD6 writes speed and seat rotation to the same bits. Remove for new track design format. + bool trackHasSpeedAndSeatRotation = _selectedTrackType == TrackElemType::BlockBrakes + || _currentTrackCurve == (RideConstructionSpecialPieceSelected | TrackElemType::BlockBrakes) + || _selectedTrackType > TrackElemType::HighestAlias + || _currentTrackCurve > (RideConstructionSpecialPieceSelected | TrackElemType::HighestAlias); + + const auto& rtd = GetRideTypeDescriptor(rideType); + bool rideHasSeatRotation = rtd.HasFlag(RIDE_TYPE_FLAG_HAS_SEAT_ROTATION); + + if (!trackHasSpeedSetting) + { + if (IsTrackEnabled(TRACK_FLAT_ROLL_BANKING)) + { + widgets[WIDX_BANK_LEFT].type = WindowWidgetType::FlatBtn; + widgets[WIDX_BANK_STRAIGHT].type = WindowWidgetType::FlatBtn; + widgets[WIDX_BANK_RIGHT].type = WindowWidgetType::FlatBtn; + } + if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_TRACK_ELEMENTS_HAVE_TWO_VARIETIES)) + { + if (rideType == RIDE_TYPE_WATER_COASTER) + { + widgets[WIDX_U_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_RC_TRACK); + widgets[WIDX_O_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_WATER_CHANNEL); + widgets[WIDX_U_TRACK].tooltip = STR_RIDE_CONSTRUCTION_STANDARD_RC_TRACK_TIP; + widgets[WIDX_O_TRACK].tooltip = STR_RIDE_CONSTRUCTION_WATER_CHANNEL_TIP; + if ((_currentTrackCurve < EnumValue(TrackCurve::LeftSmall) + || _currentTrackCurve == (RideConstructionSpecialPieceSelected | TrackElemType::SBendLeft) + || _currentTrackCurve == (RideConstructionSpecialPieceSelected | TrackElemType::SBendRight)) + && _currentTrackPitchEnd == TrackPitch::None && _currentTrackRollEnd == TrackRoll::None) + { + widgets[WIDX_BANKING_GROUPBOX].text = STR_RIDE_CONSTRUCTION_TRACK_STYLE; + widgets[WIDX_U_TRACK].type = WindowWidgetType::FlatBtn; + widgets[WIDX_O_TRACK].type = WindowWidgetType::FlatBtn; + } + } + else + { + widgets[WIDX_U_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_U_SHAPED_TRACK); + widgets[WIDX_O_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_O_SHAPED_TRACK); + widgets[WIDX_U_TRACK].tooltip = STR_RIDE_CONSTRUCTION_U_SHAPED_OPEN_TRACK_TIP; + widgets[WIDX_O_TRACK].tooltip = STR_RIDE_CONSTRUCTION_O_SHAPED_ENCLOSED_TRACK_TIP; widgets[WIDX_BANKING_GROUPBOX].text = STR_RIDE_CONSTRUCTION_TRACK_STYLE; widgets[WIDX_U_TRACK].type = WindowWidgetType::FlatBtn; widgets[WIDX_O_TRACK].type = WindowWidgetType::FlatBtn; } } - else - { - widgets[WIDX_U_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_U_SHAPED_TRACK); - widgets[WIDX_O_TRACK].image = ImageId(SPR_RIDE_CONSTRUCTION_O_SHAPED_TRACK); - widgets[WIDX_U_TRACK].tooltip = STR_RIDE_CONSTRUCTION_U_SHAPED_OPEN_TRACK_TIP; - widgets[WIDX_O_TRACK].tooltip = STR_RIDE_CONSTRUCTION_O_SHAPED_ENCLOSED_TRACK_TIP; - widgets[WIDX_BANKING_GROUPBOX].text = STR_RIDE_CONSTRUCTION_TRACK_STYLE; - widgets[WIDX_U_TRACK].type = WindowWidgetType::FlatBtn; - widgets[WIDX_O_TRACK].type = WindowWidgetType::FlatBtn; - } - } - } - else - { - if (!boosterTrackSelected) - { - widgets[WIDX_SPEED_GROUPBOX].text = STR_RIDE_CONSTRUCTION_BRAKE_SPEED; - widgets[WIDX_SPEED_SETTING_SPINNER].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP; - widgets[WIDX_SPEED_SETTING_SPINNER_UP].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP; - widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP; } else { - widgets[WIDX_SPEED_GROUPBOX].text = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED; - widgets[WIDX_SPEED_SETTING_SPINNER].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP; - widgets[WIDX_SPEED_SETTING_SPINNER_UP].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP; - widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP; - } - - _currentlyShowingBrakeOrBoosterSpeed = true; - widgets[WIDX_SPEED_SETTING_SPINNER].text = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_VELOCITY; - - widgets[WIDX_SPEED_SETTING_SPINNER].type = WindowWidgetType::Spinner; - widgets[WIDX_SPEED_SETTING_SPINNER_UP].type = WindowWidgetType::Button; - widgets[WIDX_SPEED_SETTING_SPINNER_UP].text = STR_NUMERIC_UP; - widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].type = WindowWidgetType::Button; - widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].text = STR_NUMERIC_DOWN; - - ResizeSpinner(WIDX_SPEED_SETTING_SPINNER, { 12, 138 }, { 85, SPINNER_HEIGHT }); - - hold_down_widgets |= (1uLL << WIDX_SPEED_SETTING_SPINNER_UP) | (1uLL << WIDX_SPEED_SETTING_SPINNER_DOWN); - } - - static constexpr int16_t bankingGroupboxRightNoSeatRotation = GW; - static constexpr int16_t bankingGroupboxRightWithSeatRotation = 114; - - widgets[WIDX_BANKING_GROUPBOX].right = bankingGroupboxRightNoSeatRotation; - widgets[WIDX_SEAT_ROTATION_GROUPBOX].type = WindowWidgetType::Empty; - widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].type = WindowWidgetType::Empty; - widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP].type = WindowWidgetType::Empty; - widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN].type = WindowWidgetType::Empty; - - // Simplify this condition to "rideHasSeatRotation" for new track design format - if ((rideHasSeatRotation && !trackHasSpeedSetting) - || (rideHasSeatRotation && trackHasSpeedSetting && trackHasSpeedAndSeatRotation)) - { - widgets[WIDX_SEAT_ROTATION_GROUPBOX].type = WindowWidgetType::Groupbox; - widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].type = WindowWidgetType::Spinner; - widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP].type = WindowWidgetType::Button; - widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN].type = WindowWidgetType::Button; - widgets[WIDX_BANKING_GROUPBOX].right = bankingGroupboxRightWithSeatRotation; - - // squishes the track speed spinner slightly to make room for the seat rotation widgets - if (trackHasSpeedSetting) - { - widgets[WIDX_SPEED_SETTING_SPINNER].left -= 4; - widgets[WIDX_SPEED_SETTING_SPINNER].right -= 8; - widgets[WIDX_SPEED_SETTING_SPINNER_UP].right -= 8; - widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].right -= 8; - widgets[WIDX_SPEED_SETTING_SPINNER_UP].left -= 8; - widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].left -= 8; - } - // moves banking buttons to the left to make room for the seat rotation widgets - else if (IsTrackEnabled(TRACK_FLAT_ROLL_BANKING)) - { - for (int32_t i = WIDX_BANK_LEFT; i <= WIDX_BANK_RIGHT; i++) + if (!boosterTrackSelected) { - widgets[i].left -= 36; - widgets[i].right -= 36; - } - } - } - - uint64_t pressedWidgets = pressed_widgets - & ((1uLL << WIDX_BACKGROUND) | (1uLL << WIDX_TITLE) | (1uLL << WIDX_CLOSE) | (1uLL << WIDX_DIRECTION_GROUPBOX) - | (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_CONSTRUCT) - | (1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_PREVIOUS_SECTION) | (1uLL << WIDX_NEXT_SECTION) - | (1uLL << WIDX_ENTRANCE_EXIT_GROUPBOX) | (1uLL << WIDX_ENTRANCE) | (1uLL << WIDX_EXIT)); - - widgets[WIDX_CONSTRUCT].type = WindowWidgetType::Empty; - widgets[WIDX_DEMOLISH].type = WindowWidgetType::FlatBtn; - widgets[WIDX_ROTATE].type = WindowWidgetType::Empty; - if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_CANNOT_HAVE_GAPS)) - { - widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; - widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; - } - else - { - widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::FlatBtn; - widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::FlatBtn; - } - - switch (_rideConstructionState) - { - case RideConstructionState::Front: - widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn; - widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; - break; - case RideConstructionState::Back: - widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn; - widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; - break; - case RideConstructionState::Place: - widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn; - widgets[WIDX_DEMOLISH].type = WindowWidgetType::Empty; - widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; - widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; - widgets[WIDX_ROTATE].type = WindowWidgetType::FlatBtn; - break; - case RideConstructionState::EntranceExit: - widgets[WIDX_DEMOLISH].type = WindowWidgetType::Empty; - widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; - widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; - break; - default: - pressed_widgets = pressedWidgets; - Invalidate(); - return; - } - - WidgetIndex widgetIndex; - switch (_currentTrackCurve) - { - case EnumValue(TrackCurve::None): - widgetIndex = WIDX_STRAIGHT; - break; - case EnumValue(TrackCurve::Left): - widgetIndex = WIDX_LEFT_CURVE; - break; - case EnumValue(TrackCurve::Right): - widgetIndex = WIDX_RIGHT_CURVE; - break; - case EnumValue(TrackCurve::LeftSmall): - widgetIndex = WIDX_LEFT_CURVE_SMALL; - break; - case EnumValue(TrackCurve::RightSmall): - widgetIndex = WIDX_RIGHT_CURVE_SMALL; - break; - case EnumValue(TrackCurve::LeftVerySmall): - widgetIndex = WIDX_LEFT_CURVE_VERY_SMALL; - break; - case EnumValue(TrackCurve::RightVerySmall): - widgetIndex = WIDX_RIGHT_CURVE_VERY_SMALL; - break; - case EnumValue(TrackCurve::LeftLarge): - widgetIndex = WIDX_LEFT_CURVE_LARGE; - break; - case EnumValue(TrackCurve::RightLarge): - widgetIndex = WIDX_RIGHT_CURVE_LARGE; - break; - default: - widgetIndex = WIDX_SPECIAL_TRACK_DROPDOWN; - break; - } - pressedWidgets |= (1uLL << widgetIndex); - - switch (_currentTrackPitchEnd) - { - case TrackPitch::Down60: - case TrackPitch::Up90: - widgetIndex = WIDX_SLOPE_DOWN_STEEP; - break; - case TrackPitch::Down25: - widgetIndex = WIDX_SLOPE_DOWN; - break; - case TrackPitch::Up25: - widgetIndex = WIDX_SLOPE_UP; - break; - case TrackPitch::Up60: - case TrackPitch::Down90: - widgetIndex = WIDX_SLOPE_UP_STEEP; - break; - default: - widgetIndex = WIDX_LEVEL; - break; - } - pressedWidgets |= (1uLL << widgetIndex); - - if (!_currentlyShowingBrakeOrBoosterSpeed) - { - if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_TRACK_ELEMENTS_HAVE_TWO_VARIETIES)) - { - if (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_PIECES) - { - pressed_widgets |= (1uLL << WIDX_O_TRACK); + widgets[WIDX_SPEED_GROUPBOX].text = STR_RIDE_CONSTRUCTION_BRAKE_SPEED; + widgets[WIDX_SPEED_SETTING_SPINNER].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP; + widgets[WIDX_SPEED_SETTING_SPINNER_UP].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP; + widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].tooltip = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_LIMIT_TIP; } else { - pressed_widgets |= (1uLL << WIDX_U_TRACK); + widgets[WIDX_SPEED_GROUPBOX].text = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED; + widgets[WIDX_SPEED_SETTING_SPINNER].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP; + widgets[WIDX_SPEED_SETTING_SPINNER_UP].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP; + widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].tooltip = STR_RIDE_CONSTRUCTION_BOOSTER_SPEED_LIMIT_TIP; + } + + _currentlyShowingBrakeOrBoosterSpeed = true; + widgets[WIDX_SPEED_SETTING_SPINNER].text = STR_RIDE_CONSTRUCTION_BRAKE_SPEED_VELOCITY; + + widgets[WIDX_SPEED_SETTING_SPINNER].type = WindowWidgetType::Spinner; + widgets[WIDX_SPEED_SETTING_SPINNER_UP].type = WindowWidgetType::Button; + widgets[WIDX_SPEED_SETTING_SPINNER_UP].text = STR_NUMERIC_UP; + widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].type = WindowWidgetType::Button; + widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].text = STR_NUMERIC_DOWN; + + ResizeSpinner(WIDX_SPEED_SETTING_SPINNER, { 12, 138 }, { 85, SPINNER_HEIGHT }); + + hold_down_widgets |= (1uLL << WIDX_SPEED_SETTING_SPINNER_UP) | (1uLL << WIDX_SPEED_SETTING_SPINNER_DOWN); + } + + static constexpr int16_t bankingGroupboxRightNoSeatRotation = GW; + static constexpr int16_t bankingGroupboxRightWithSeatRotation = 114; + + widgets[WIDX_BANKING_GROUPBOX].right = bankingGroupboxRightNoSeatRotation; + widgets[WIDX_SEAT_ROTATION_GROUPBOX].type = WindowWidgetType::Empty; + widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].type = WindowWidgetType::Empty; + widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP].type = WindowWidgetType::Empty; + widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN].type = WindowWidgetType::Empty; + + // Simplify this condition to "rideHasSeatRotation" for new track design format + if ((rideHasSeatRotation && !trackHasSpeedSetting) + || (rideHasSeatRotation && trackHasSpeedSetting && trackHasSpeedAndSeatRotation)) + { + widgets[WIDX_SEAT_ROTATION_GROUPBOX].type = WindowWidgetType::Groupbox; + widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].type = WindowWidgetType::Spinner; + widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP].type = WindowWidgetType::Button; + widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN].type = WindowWidgetType::Button; + widgets[WIDX_BANKING_GROUPBOX].right = bankingGroupboxRightWithSeatRotation; + + // squishes the track speed spinner slightly to make room for the seat rotation widgets + if (trackHasSpeedSetting) + { + widgets[WIDX_SPEED_SETTING_SPINNER].left -= 4; + widgets[WIDX_SPEED_SETTING_SPINNER].right -= 8; + widgets[WIDX_SPEED_SETTING_SPINNER_UP].right -= 8; + widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].right -= 8; + widgets[WIDX_SPEED_SETTING_SPINNER_UP].left -= 8; + widgets[WIDX_SPEED_SETTING_SPINNER_DOWN].left -= 8; + } + // moves banking buttons to the left to make room for the seat rotation widgets + else if (IsTrackEnabled(TRACK_FLAT_ROLL_BANKING)) + { + for (int32_t i = WIDX_BANK_LEFT; i <= WIDX_BANK_RIGHT; i++) + { + widgets[i].left -= 36; + widgets[i].right -= 36; + } } } - switch (_currentTrackRollEnd) + + uint64_t pressedWidgets = pressed_widgets + & ((1uLL << WIDX_BACKGROUND) | (1uLL << WIDX_TITLE) | (1uLL << WIDX_CLOSE) | (1uLL << WIDX_DIRECTION_GROUPBOX) + | (1uLL << WIDX_SLOPE_GROUPBOX) | (1uLL << WIDX_BANKING_GROUPBOX) | (1uLL << WIDX_CONSTRUCT) + | (1uLL << WIDX_DEMOLISH) | (1uLL << WIDX_PREVIOUS_SECTION) | (1uLL << WIDX_NEXT_SECTION) + | (1uLL << WIDX_ENTRANCE_EXIT_GROUPBOX) | (1uLL << WIDX_ENTRANCE) | (1uLL << WIDX_EXIT)); + + widgets[WIDX_CONSTRUCT].type = WindowWidgetType::Empty; + widgets[WIDX_DEMOLISH].type = WindowWidgetType::FlatBtn; + widgets[WIDX_ROTATE].type = WindowWidgetType::Empty; + if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_CANNOT_HAVE_GAPS)) { - case TrackRoll::Left: - widgetIndex = WIDX_BANK_LEFT; + widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; + widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; + } + else + { + widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::FlatBtn; + widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::FlatBtn; + } + + switch (_rideConstructionState) + { + case RideConstructionState::Front: + widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn; + widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; break; - case TrackRoll::None: - widgetIndex = WIDX_BANK_STRAIGHT; + case RideConstructionState::Back: + widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn; + widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; + break; + case RideConstructionState::Place: + widgets[WIDX_CONSTRUCT].type = WindowWidgetType::ImgBtn; + widgets[WIDX_DEMOLISH].type = WindowWidgetType::Empty; + widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; + widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; + widgets[WIDX_ROTATE].type = WindowWidgetType::FlatBtn; + break; + case RideConstructionState::EntranceExit: + widgets[WIDX_DEMOLISH].type = WindowWidgetType::Empty; + widgets[WIDX_NEXT_SECTION].type = WindowWidgetType::Empty; + widgets[WIDX_PREVIOUS_SECTION].type = WindowWidgetType::Empty; break; default: - widgetIndex = WIDX_BANK_RIGHT; + pressed_widgets = pressedWidgets; + Invalidate(); + return; + } + + WidgetIndex widgetIndex; + switch (_currentTrackCurve) + { + case EnumValue(TrackCurve::None): + widgetIndex = WIDX_STRAIGHT; + break; + case EnumValue(TrackCurve::Left): + widgetIndex = WIDX_LEFT_CURVE; + break; + case EnumValue(TrackCurve::Right): + widgetIndex = WIDX_RIGHT_CURVE; + break; + case EnumValue(TrackCurve::LeftSmall): + widgetIndex = WIDX_LEFT_CURVE_SMALL; + break; + case EnumValue(TrackCurve::RightSmall): + widgetIndex = WIDX_RIGHT_CURVE_SMALL; + break; + case EnumValue(TrackCurve::LeftVerySmall): + widgetIndex = WIDX_LEFT_CURVE_VERY_SMALL; + break; + case EnumValue(TrackCurve::RightVerySmall): + widgetIndex = WIDX_RIGHT_CURVE_VERY_SMALL; + break; + case EnumValue(TrackCurve::LeftLarge): + widgetIndex = WIDX_LEFT_CURVE_LARGE; + break; + case EnumValue(TrackCurve::RightLarge): + widgetIndex = WIDX_RIGHT_CURVE_LARGE; + break; + default: + widgetIndex = WIDX_SPECIAL_TRACK_DROPDOWN; break; } pressedWidgets |= (1uLL << widgetIndex); - } - if (_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) - pressedWidgets |= (1uLL << WIDX_CHAIN_LIFT); + switch (_currentTrackPitchEnd) + { + case TrackPitch::Down60: + case TrackPitch::Up90: + widgetIndex = WIDX_SLOPE_DOWN_STEEP; + break; + case TrackPitch::Down25: + widgetIndex = WIDX_SLOPE_DOWN; + break; + case TrackPitch::Up25: + widgetIndex = WIDX_SLOPE_UP; + break; + case TrackPitch::Up60: + case TrackPitch::Down90: + widgetIndex = WIDX_SLOPE_UP_STEEP; + break; + default: + widgetIndex = WIDX_LEVEL; + break; + } + pressedWidgets |= (1uLL << widgetIndex); - pressed_widgets = pressedWidgets; - Invalidate(); - } - - void UpdatePossibleRideConfigurations() - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; - } - _specialElementDropdownState = BuildSpecialElementsList( - *currentRide, _currentTrackPieceDirection, _previousTrackPitchEnd, _previousTrackRollEnd, _rideConstructionState); - _currentlyShowingBrakeOrBoosterSpeed = false; - } - - void UpdateMapSelection() - { - int32_t trackType, trackDirection; - CoordsXYZ trackPos{}; - - MapInvalidateMapSelectionTiles(); - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags |= MAP_SELECT_FLAG_GREEN; - - switch (_rideConstructionState) - { - case RideConstructionState::State0: - trackDirection = _currentTrackPieceDirection; - trackType = 0; - trackPos = _currentTrackBegin; - break; - case RideConstructionState::Selected: - trackDirection = _currentTrackPieceDirection; - trackType = _currentTrackPieceType; - trackPos = _currentTrackBegin; - break; - case RideConstructionState::EntranceExit: - gMapSelectionTiles.clear(); - return; - default: - if (WindowRideConstructionUpdateState(&trackType, &trackDirection, nullptr, nullptr, &trackPos, nullptr)) + if (!_currentlyShowingBrakeOrBoosterSpeed) + { + if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_TRACK_ELEMENTS_HAVE_TWO_VARIETIES)) { + if (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_PIECES) + { + pressed_widgets |= (1uLL << WIDX_O_TRACK); + } + else + { + pressed_widgets |= (1uLL << WIDX_U_TRACK); + } + } + switch (_currentTrackRollEnd) + { + case TrackRoll::Left: + widgetIndex = WIDX_BANK_LEFT; + break; + case TrackRoll::None: + widgetIndex = WIDX_BANK_STRAIGHT; + break; + default: + widgetIndex = WIDX_BANK_RIGHT; + break; + } + pressedWidgets |= (1uLL << widgetIndex); + } + + if (_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) + pressedWidgets |= (1uLL << WIDX_CHAIN_LIFT); + + pressed_widgets = pressedWidgets; + Invalidate(); + } + + void UpdatePossibleRideConfigurations() + { + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) + { + return; + } + _specialElementDropdownState = BuildSpecialElementsList( + *currentRide, _currentTrackPieceDirection, _previousTrackPitchEnd, _previousTrackRollEnd, + _rideConstructionState); + _currentlyShowingBrakeOrBoosterSpeed = false; + } + + void UpdateMapSelection() + { + int32_t trackType, trackDirection; + CoordsXYZ trackPos{}; + + MapInvalidateMapSelectionTiles(); + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags |= MAP_SELECT_FLAG_GREEN; + + switch (_rideConstructionState) + { + case RideConstructionState::State0: trackDirection = _currentTrackPieceDirection; trackType = 0; trackPos = _currentTrackBegin; - } - break; + break; + case RideConstructionState::Selected: + trackDirection = _currentTrackPieceDirection; + trackType = _currentTrackPieceType; + trackPos = _currentTrackBegin; + break; + case RideConstructionState::EntranceExit: + gMapSelectionTiles.clear(); + return; + default: + if (WindowRideConstructionUpdateState(&trackType, &trackDirection, nullptr, nullptr, &trackPos, nullptr)) + { + trackDirection = _currentTrackPieceDirection; + trackType = 0; + trackPos = _currentTrackBegin; + } + break; + } + + if (GetRide(_currentRideIndex)) + { + SelectMapTiles(trackType, trackDirection, trackPos); + MapInvalidateMapSelectionTiles(); + } } - if (GetRide(_currentRideIndex)) + void SelectMapTiles(int32_t trackType, int32_t trackDirection, const CoordsXY& tileCoords) { - SelectMapTiles(trackType, trackDirection, trackPos); - MapInvalidateMapSelectionTiles(); - } - } + // If the scenery tool is active, we do not display our tiles as it + // will conflict with larger scenery objects selecting tiles + if (SceneryToolIsActive()) + { + return; + } - void SelectMapTiles(int32_t trackType, int32_t trackDirection, const CoordsXY& tileCoords) - { - // If the scenery tool is active, we do not display our tiles as it - // will conflict with larger scenery objects selecting tiles - if (SceneryToolIsActive()) - { - return; + const PreviewTrack* trackBlock; + + const auto& ted = GetTrackElementDescriptor(trackType); + trackBlock = ted.Block; + trackDirection &= 3; + gMapSelectionTiles.clear(); + while (trackBlock->index != 255) + { + CoordsXY offsets = { trackBlock->x, trackBlock->y }; + CoordsXY currentTileCoords = tileCoords + offsets.Rotate(trackDirection); + + gMapSelectionTiles.push_back(currentTileCoords); + trackBlock++; + } } - const PreviewTrack* trackBlock; - - const auto& ted = GetTrackElementDescriptor(trackType); - trackBlock = ted.Block; - trackDirection &= 3; - gMapSelectionTiles.clear(); - while (trackBlock->index != 255) + private: + void Construct() { - CoordsXY offsets = { trackBlock->x, trackBlock->y }; - CoordsXY currentTileCoords = tileCoords + offsets.Rotate(trackDirection); + RideId rideIndex; + int32_t trackType, trackDirection, liftHillAndAlternativeState, properties; + CoordsXYZ trackPos{}; - gMapSelectionTiles.push_back(currentTileCoords); - trackBlock++; - } - } - -private: - void Construct() - { - RideId rideIndex; - int32_t trackType, trackDirection, liftHillAndAlternativeState, properties; - CoordsXYZ trackPos{}; - - _currentTrackPrice = kMoney64Undefined; - _trackPlaceCost = kMoney64Undefined; - _trackPlaceErrorMessage = STR_NONE; - RideConstructionInvalidateCurrentTrack(); - if (WindowRideConstructionUpdateState( - &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, &properties)) - { - WindowRideConstructionUpdateActiveElements(); - return; - } - - auto currentRide = GetRide(_currentRideIndex); - if (currentRide == nullptr) - { - return; - } - - auto trackPlaceAction = TrackPlaceAction( - rideIndex, trackType, currentRide->type, { trackPos, static_cast(trackDirection) }, properties & 0xFF, - (properties >> 8) & 0x0F, (properties >> 12) & 0x0F, liftHillAndAlternativeState, false); - if (_rideConstructionState == RideConstructionState::Back) - { - trackPlaceAction.SetCallback(RideConstructPlacedBackwardGameActionCallback); - } - else if (_rideConstructionState == RideConstructionState::Front) - { - trackPlaceAction.SetCallback(RideConstructPlacedForwardGameActionCallback); - } - auto res = GameActions::Execute(&trackPlaceAction); - // Used by some functions - if (res.Error != GameActions::Status::Ok) - { + _currentTrackPrice = kMoney64Undefined; _trackPlaceCost = kMoney64Undefined; - _trackPlaceErrorMessage = std::get(res.ErrorMessage); - } - else - { - _trackPlaceCost = res.Cost; _trackPlaceErrorMessage = STR_NONE; - } - - if (res.Error != GameActions::Status::Ok) - { - return; - } - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, trackPos); - - if (NetworkGetMode() != NETWORK_MODE_NONE) - { - _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK_PLACE_ACTION_QUEUED; - } - - const auto resultData = res.GetData(); - if (resultData.GroundFlags & ELEMENT_IS_UNDERGROUND) - { - ViewportSetVisibility(ViewportVisibility::UndergroundViewOn); - } - - const bool helixSelected = (_currentTrackCurve & RideConstructionSpecialPieceSelected) - && TrackTypeIsHelix(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); - if (helixSelected || (_currentTrackPitchEnd != TrackPitch::None)) - { - ViewportSetVisibility(ViewportVisibility::TrackHeights); - } - } - - void MouseUpDemolish() - { - int32_t direction; - TileElement* tileElement; - CoordsXYE inputElement, outputElement; - TrackBeginEnd trackBeginEnd; - - _currentTrackPrice = kMoney64Undefined; - RideConstructionInvalidateCurrentTrack(); - - // Select the track element that is to be deleted - _rideConstructionState2 = RideConstructionState::Selected; - if (_rideConstructionState == RideConstructionState::Front) - { - if (!RideSelectBackwardsFromFront()) + RideConstructionInvalidateCurrentTrack(); + if (WindowRideConstructionUpdateState( + &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, &properties)) { WindowRideConstructionUpdateActiveElements(); return; } - _rideConstructionState2 = RideConstructionState::Front; - } - else if (_rideConstructionState == RideConstructionState::Back) - { - if (!RideSelectForwardsFromBack()) + + auto currentRide = GetRide(_currentRideIndex); + if (currentRide == nullptr) { - WindowRideConstructionUpdateActiveElements(); return; } - _rideConstructionState2 = RideConstructionState::Back; + + auto trackPlaceAction = TrackPlaceAction( + rideIndex, trackType, currentRide->type, { trackPos, static_cast(trackDirection) }, properties & 0xFF, + (properties >> 8) & 0x0F, (properties >> 12) & 0x0F, liftHillAndAlternativeState, false); + if (_rideConstructionState == RideConstructionState::Back) + { + trackPlaceAction.SetCallback(RideConstructPlacedBackwardGameActionCallback); + } + else if (_rideConstructionState == RideConstructionState::Front) + { + trackPlaceAction.SetCallback(RideConstructPlacedForwardGameActionCallback); + } + auto res = GameActions::Execute(&trackPlaceAction); + // Used by some functions + if (res.Error != GameActions::Status::Ok) + { + _trackPlaceCost = kMoney64Undefined; + _trackPlaceErrorMessage = std::get(res.ErrorMessage); + } + else + { + _trackPlaceCost = res.Cost; + _trackPlaceErrorMessage = STR_NONE; + } + + if (res.Error != GameActions::Status::Ok) + { + return; + } + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, trackPos); + + if (NetworkGetMode() != NETWORK_MODE_NONE) + { + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK_PLACE_ACTION_QUEUED; + } + + const auto resultData = res.GetData(); + if (resultData.GroundFlags & ELEMENT_IS_UNDERGROUND) + { + ViewportSetVisibility(ViewportVisibility::UndergroundViewOn); + } + + const bool helixSelected = (_currentTrackCurve & RideConstructionSpecialPieceSelected) + && TrackTypeIsHelix(_currentTrackCurve & ~RideConstructionSpecialPieceSelected); + if (helixSelected || (_currentTrackPitchEnd != TrackPitch::None)) + { + ViewportSetVisibility(ViewportVisibility::TrackHeights); + } } - // Invalidate the selected track element or make sure it's at origin??? - direction = _currentTrackPieceDirection; - // The direction is reset by ride_initialise_construction_window(), but we need it to remove flat rides properly. - Direction currentDirection = _currentTrackPieceDirection; - track_type_t type = _currentTrackPieceType; - auto newCoords = GetTrackElementOriginAndApplyChanges( - { _currentTrackBegin, static_cast(direction & 3) }, type, 0, &tileElement, 0); - if (!newCoords.has_value()) + void MouseUpDemolish() { - WindowRideConstructionUpdateActiveElements(); - return; - } + int32_t direction; + TileElement* tileElement; + CoordsXYE inputElement, outputElement; + TrackBeginEnd trackBeginEnd; - // Get the previous track element to go to after the selected track element is deleted - inputElement.x = newCoords->x; - inputElement.y = newCoords->y; - inputElement.element = tileElement; - if (TrackBlockGetPrevious({ *newCoords, tileElement }, &trackBeginEnd)) - { - *newCoords = { trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z }; - direction = trackBeginEnd.begin_direction; - type = trackBeginEnd.begin_element->AsTrack()->GetTrackType(); - _gotoStartPlacementMode = false; - } - else if (TrackBlockGetNext(&inputElement, &outputElement, &newCoords->z, &direction)) - { - newCoords->x = outputElement.x; - newCoords->y = outputElement.y; - direction = outputElement.element->GetDirection(); - type = outputElement.element->AsTrack()->GetTrackType(); - _gotoStartPlacementMode = false; - } - else - { + _currentTrackPrice = kMoney64Undefined; + RideConstructionInvalidateCurrentTrack(); + + // Select the track element that is to be deleted + _rideConstructionState2 = RideConstructionState::Selected; + if (_rideConstructionState == RideConstructionState::Front) + { + if (!RideSelectBackwardsFromFront()) + { + WindowRideConstructionUpdateActiveElements(); + return; + } + _rideConstructionState2 = RideConstructionState::Front; + } + else if (_rideConstructionState == RideConstructionState::Back) + { + if (!RideSelectForwardsFromBack()) + { + WindowRideConstructionUpdateActiveElements(); + return; + } + _rideConstructionState2 = RideConstructionState::Back; + } + + // Invalidate the selected track element or make sure it's at origin??? direction = _currentTrackPieceDirection; - type = _currentTrackPieceType; - newCoords = GetTrackElementOriginAndApplyChanges( + // The direction is reset by ride_initialise_construction_window(), but we need it to remove flat rides properly. + Direction currentDirection = _currentTrackPieceDirection; + track_type_t type = _currentTrackPieceType; + auto newCoords = GetTrackElementOriginAndApplyChanges( { _currentTrackBegin, static_cast(direction & 3) }, type, 0, &tileElement, 0); - if (!newCoords.has_value()) { WindowRideConstructionUpdateActiveElements(); return; } - const auto& ted = GetTrackElementDescriptor(tileElement->AsTrack()->GetTrackType()); - const PreviewTrack* trackBlock = ted.Block; - newCoords->z = (tileElement->GetBaseZ()) - trackBlock->z; - _gotoStartPlacementMode = true; - - // When flat rides are deleted, the window should be reset so the currentRide can be placed again. - auto currentRide = GetRide(_currentRideIndex); - const auto& rtd = currentRide->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE) && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) + // Get the previous track element to go to after the selected track element is deleted + inputElement.x = newCoords->x; + inputElement.y = newCoords->y; + inputElement.element = tileElement; + if (TrackBlockGetPrevious({ *newCoords, tileElement }, &trackBeginEnd)) { - RideInitialiseConstructionWindow(*currentRide); + *newCoords = { trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z }; + direction = trackBeginEnd.begin_direction; + type = trackBeginEnd.begin_element->AsTrack()->GetTrackType(); + _gotoStartPlacementMode = false; } - } - - auto trackRemoveAction = TrackRemoveAction( - _currentTrackPieceType, 0, { _currentTrackBegin.x, _currentTrackBegin.y, _currentTrackBegin.z, currentDirection }); - - trackRemoveAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) + else if (TrackBlockGetNext(&inputElement, &outputElement, &newCoords->z, &direction)) { - WindowRideConstructionUpdateActiveElements(); + newCoords->x = outputElement.x; + newCoords->y = outputElement.y; + direction = outputElement.element->GetDirection(); + type = outputElement.element->AsTrack()->GetTrackType(); + _gotoStartPlacementMode = false; } else { + direction = _currentTrackPieceDirection; + type = _currentTrackPieceType; + newCoords = GetTrackElementOriginAndApplyChanges( + { _currentTrackBegin, static_cast(direction & 3) }, type, 0, &tileElement, 0); + + if (!newCoords.has_value()) + { + WindowRideConstructionUpdateActiveElements(); + return; + } + + const auto& ted = GetTrackElementDescriptor(tileElement->AsTrack()->GetTrackType()); + const PreviewTrack* trackBlock = ted.Block; + newCoords->z = (tileElement->GetBaseZ()) - trackBlock->z; + _gotoStartPlacementMode = true; + + // When flat rides are deleted, the window should be reset so the currentRide can be placed again. auto currentRide = GetRide(_currentRideIndex); - if (currentRide != nullptr) + const auto& rtd = currentRide->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE) && !rtd.HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) { - WindowRideConstructionMouseUpDemolishNextPiece({ *newCoords, static_cast(direction) }, type); + RideInitialiseConstructionWindow(*currentRide); } } - }); - GameActions::Execute(&trackRemoveAction); - } + auto trackRemoveAction = TrackRemoveAction( + _currentTrackPieceType, 0, + { _currentTrackBegin.x, _currentTrackBegin.y, _currentTrackBegin.z, currentDirection }); - void Rotate() - { - _autoRotatingShop = false; - _currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3; - RideConstructionInvalidateCurrentTrack(); - _currentTrackPrice = kMoney64Undefined; - WindowRideConstructionUpdateActiveElements(); - } - - void EntranceClick() - { - if (ToolSet(*this, WIDX_ENTRANCE, Tool::Crosshair)) - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide != nullptr && !RideTryGetOriginElement(*currentRide, nullptr)) - { - RideInitialiseConstructionWindow(*currentRide); - } - } - else - { - gRideEntranceExitPlaceType = ENTRANCE_TYPE_RIDE_ENTRANCE; - gRideEntranceExitPlaceRideIndex = _currentRideIndex; - gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0); - InputSetFlag(INPUT_FLAG_6, true); - RideConstructionInvalidateCurrentTrack(); - if (_rideConstructionState != RideConstructionState::EntranceExit) - { - gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState; - _rideConstructionState = RideConstructionState::EntranceExit; - } - WindowRideConstructionUpdateActiveElements(); - } - } - - void ExitClick() - { - if (ToolSet(*this, WIDX_EXIT, Tool::Crosshair)) - { - auto currentRide = GetRide(_currentRideIndex); - if (!RideTryGetOriginElement(*currentRide, nullptr)) - { - RideInitialiseConstructionWindow(*currentRide); - } - } - else - { - gRideEntranceExitPlaceType = ENTRANCE_TYPE_RIDE_EXIT; - gRideEntranceExitPlaceRideIndex = _currentRideIndex; - gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0); - InputSetFlag(INPUT_FLAG_6, true); - RideConstructionInvalidateCurrentTrack(); - if (_rideConstructionState != RideConstructionState::EntranceExit) - { - gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState; - _rideConstructionState = RideConstructionState::EntranceExit; - } - WindowRideConstructionUpdateActiveElements(); - } - } - - void UpdateLiftHillSelected(TrackPitch slope) - { - _currentTrackPitchEnd = slope; - _currentTrackPrice = kMoney64Undefined; - if (_rideConstructionState == RideConstructionState::Front && !GetGameState().Cheats.EnableChainLiftOnAllTrack) - { - switch (slope) - { - case TrackPitch::None: - case TrackPitch::Up25: - case TrackPitch::Up60: - break; - default: - _currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED; - break; - } - } - WindowRideConstructionUpdateActiveElements(); - } - - void SetBrakeSpeed(int32_t brakesSpeed) - { - TileElement* tileElement; - - if (GetTrackElementOriginAndApplyChanges( - { _currentTrackBegin, static_cast(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, 0, - &tileElement, 0) - != std::nullopt) - { - auto trackSetBrakeSpeed = TrackSetBrakeSpeedAction( - _currentTrackBegin, tileElement->AsTrack()->GetTrackType(), brakesSpeed); - trackSetBrakeSpeed.SetCallback( - [](const GameAction* ga, const GameActions::Result* result) { WindowRideConstructionUpdateActiveElements(); }); - GameActions::Execute(&trackSetBrakeSpeed); - return; - } - WindowRideConstructionUpdateActiveElements(); - } - - void ShowSpecialTrackDropdown(Widget* widget) - { - int32_t defaultIndex = -1; - for (size_t i = 0; i < _specialElementDropdownState.Elements.size(); i++) - { - track_type_t trackPiece = _specialElementDropdownState.Elements[i].TrackType; - - const auto& ted = GetTrackElementDescriptor(trackPiece); - StringId trackPieceStringId = ted.Description; - if (trackPieceStringId == STR_RAPIDS) - { - auto currentRide = GetRide(_currentRideIndex); - if (currentRide != nullptr) + trackRemoveAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) { - const auto& rtd = currentRide->GetRideTypeDescriptor(); - if (rtd.Category != RIDE_CATEGORY_WATER) - trackPieceStringId = STR_LOG_BUMPS; - } - } - gDropdownItems[i].Format = trackPieceStringId; - if ((trackPiece | RideConstructionSpecialPieceSelected) == _currentTrackCurve) - { - defaultIndex = static_cast(i); - } - } - - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, 0, - _specialElementDropdownState.Elements.size(), widget->width()); - - for (size_t i = 0; i < _specialElementDropdownState.Elements.size(); i++) - { - Dropdown::SetDisabled(static_cast(i), _specialElementDropdownState.Elements[i].Disabled); - } - gDropdownDefaultIndex = defaultIndex; - } - - void RideSelectedTrackSetSeatRotation(int32_t seatRotation) - { - GetTrackElementOriginAndApplyChanges( - { _currentTrackBegin, static_cast(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, - seatRotation, nullptr, TRACK_ELEMENT_SET_SEAT_ROTATION); - WindowRideConstructionUpdateActiveElements(); - } - - void ToolDownEntranceExit(const ScreenCoordsXY& screenCoords) - { - RideConstructionInvalidateCurrentTrack(); - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - - CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords); - if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION) - return; - - auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction( - entranceOrExitCoords, DirectionReverse(gRideEntranceExitPlaceDirection), gRideEntranceExitPlaceRideIndex, - gRideEntranceExitPlaceStationIndex, gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT); - - rideEntranceExitPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) - return; - - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - - auto currentRide = GetRide(gRideEntranceExitPlaceRideIndex); - if (currentRide != nullptr && RideAreAllPossibleEntrancesAndExitsBuilt(*currentRide).Successful) - { - ToolCancel(); - if (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) - { - WindowCloseByClass(WindowClass::RideConstruction); - } - } - else - { - gRideEntranceExitPlaceType = gRideEntranceExitPlaceType ^ 1; - WindowInvalidateByClass(WindowClass::RideConstruction); - gCurrentToolWidget.widget_index = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE) - ? WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE - : WC_RIDE_CONSTRUCTION__WIDX_EXIT; - } - }); - auto res = GameActions::Execute(&rideEntranceExitPlaceAction); - } - - void DrawTrackPiece( - DrawPixelInfo& dpi, RideId rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndInvertedState, - int32_t widgetWidth, int32_t widgetHeight) - { - auto currentRide = GetRide(rideIndex); - if (currentRide == nullptr) - { - return; - } - - const auto& ted = GetTrackElementDescriptor(trackType); - const auto* trackBlock = ted.Block; - while ((trackBlock + 1)->index != 0xFF) - trackBlock++; - - CoordsXYZ mapCoords{ trackBlock->x, trackBlock->y, trackBlock->z }; - if (trackBlock->flags & RCT_PREVIEW_TRACK_FLAG_1) - { - mapCoords.x = 0; - mapCoords.y = 0; - } - - auto rotatedMapCoords = mapCoords.Rotate(trackDirection); - // this is actually case 0, but the other cases all jump to it - mapCoords.x = 4112 + (rotatedMapCoords.x / 2); - mapCoords.y = 4112 + (rotatedMapCoords.y / 2); - mapCoords.z = 1024 + mapCoords.z; - - auto previewZOffset = ted.Definition.PreviewZOffset; - mapCoords.z -= previewZOffset; - - const ScreenCoordsXY rotatedScreenCoords = Translate3DTo2DWithZ(GetCurrentRotation(), mapCoords); - - dpi.x += rotatedScreenCoords.x - widgetWidth / 2; - dpi.y += rotatedScreenCoords.y - widgetHeight / 2 - 16; - - DrawTrackPieceHelper(dpi, rideIndex, trackType, trackDirection, liftHillAndInvertedState, { 4096, 4096 }, 1024); - } - - void DrawTrackPieceHelper( - DrawPixelInfo& dpi, RideId rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndInvertedState, - const CoordsXY& originCoords, int32_t originZ) - { - TileElement tempSideTrackTileElement{ 0x80, 0x8F, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - TileElement tempTrackTileElement{}; - TileElement* backupTileElementArrays[5]{}; - PaintSession* session = PaintSessionAlloc(dpi, 0, GetCurrentRotation()); - trackDirection &= 3; - - auto currentRide = GetRide(rideIndex); - if (currentRide == nullptr) - { - return; - } - - auto& gameState = OpenRCT2::GetGameState(); - auto preserveMapSize = gameState.MapSize; - - gameState.MapSize = { MAXIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL }; - - // Setup non changing parts of the temporary track tile element - tempTrackTileElement.SetType(TileElementType::Track); - tempTrackTileElement.SetDirection(trackDirection); - tempTrackTileElement.AsTrack()->SetHasChain((liftHillAndInvertedState & CONSTRUCTION_LIFT_HILL_SELECTED) != 0); - tempTrackTileElement.SetLastForTile(true); - tempTrackTileElement.AsTrack()->SetTrackType(trackType); - tempTrackTileElement.AsTrack()->SetRideType(currentRide->type); - tempTrackTileElement.AsTrack()->SetHasCableLift(false); - tempTrackTileElement.AsTrack()->SetInverted((liftHillAndInvertedState & CONSTRUCTION_INVERTED_TRACK_SELECTED) != 0); - tempTrackTileElement.AsTrack()->SetColourScheme(RIDE_COLOUR_SCHEME_MAIN); - // Skipping seat rotation, should not be necessary for a temporary piece. - tempTrackTileElement.AsTrack()->SetRideIndex(rideIndex); - - const auto& ted = GetTrackElementDescriptor(trackType); - const auto* trackBlock = ted.Block; - const auto* rideEntry = currentRide->GetRideEntry(); - auto clearanceHeight = (rideEntry != nullptr) ? rideEntry->Clearance - : currentRide->GetRideTypeDescriptor().Heights.ClearanceHeight; - - while (trackBlock->index != 255) - { - auto quarterTile = trackBlock->var_08.Rotate(trackDirection); - CoordsXY offsets = { trackBlock->x, trackBlock->y }; - CoordsXY coords = originCoords + offsets.Rotate(trackDirection); - - int32_t baseZ = originZ + trackBlock->z; - int32_t clearanceZ = trackBlock->ClearanceZ + clearanceHeight + baseZ + (4 * COORDS_Z_STEP); - - auto centreTileCoords = TileCoordsXY{ coords }; - auto eastTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_EAST]; - auto westTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_WEST]; - auto northTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_NORTH]; - auto southTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_SOUTH]; - - // Replace map elements with temporary ones containing track - backupTileElementArrays[0] = MapGetFirstElementAt(centreTileCoords); - backupTileElementArrays[1] = MapGetFirstElementAt(eastTileCoords); - backupTileElementArrays[2] = MapGetFirstElementAt(westTileCoords); - backupTileElementArrays[3] = MapGetFirstElementAt(northTileCoords); - backupTileElementArrays[4] = MapGetFirstElementAt(southTileCoords); - MapSetTileElement(centreTileCoords, &tempTrackTileElement); - MapSetTileElement(eastTileCoords, &tempSideTrackTileElement); - MapSetTileElement(westTileCoords, &tempSideTrackTileElement); - MapSetTileElement(northTileCoords, &tempSideTrackTileElement); - MapSetTileElement(southTileCoords, &tempSideTrackTileElement); - - // Set the temporary track element - tempTrackTileElement.SetOccupiedQuadrants(quarterTile.GetBaseQuarterOccupied()); - tempTrackTileElement.SetBaseZ(baseZ); - tempTrackTileElement.SetClearanceZ(clearanceZ); - tempTrackTileElement.AsTrack()->SetSequenceIndex(trackBlock->index); - - // Draw this map tile - TileElementPaintSetup(*session, coords, true); - - // Restore map elements - MapSetTileElement(centreTileCoords, backupTileElementArrays[0]); - MapSetTileElement(eastTileCoords, backupTileElementArrays[1]); - MapSetTileElement(westTileCoords, backupTileElementArrays[2]); - MapSetTileElement(northTileCoords, backupTileElementArrays[3]); - MapSetTileElement(southTileCoords, backupTileElementArrays[4]); - - trackBlock++; - } - - gameState.MapSize = preserveMapSize; - - PaintSessionArrange(*session); - PaintDrawStructs(*session); - PaintSessionFree(session); - } -}; - -static void WindowRideConstructionUpdateDisabledPieces(ObjectEntryIndex rideType) -{ - RideTrackGroup disabledPieces{}; - const auto& rtd = GetRideTypeDescriptor(rideType); - if (rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) - { - // Set all pieces as “disabled”. When looping over the ride entries, - // pieces will be re-enabled as soon as a single entry supports it. - disabledPieces.flip(); - - auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); - auto& rideEntries = objManager.GetAllRideEntries(rideType); - // If there are no vehicles for this ride type, enable all the track pieces. - if (rideEntries.size() == 0) - { - disabledPieces.reset(); - } - - for (auto rideEntryIndex : rideEntries) - { - const auto* currentRideEntry = GetRideEntryByIndex(rideEntryIndex); - if (currentRideEntry == nullptr) - continue; - - // Non-default vehicle visuals do not use this system, so we have to assume it supports all the track pieces. - if (currentRideEntry->Cars[0].PaintStyle != VEHICLE_VISUAL_DEFAULT || rideType == RIDE_TYPE_CHAIRLIFT - || (currentRideEntry->Cars[0].flags & CAR_ENTRY_FLAG_SLIDE_SWING)) - { - disabledPieces.reset(); - break; - } - - // Any pieces that this ride entry supports must be taken out of the array. - auto supportedPieces = RideEntryGetSupportedTrackPieces(*currentRideEntry); - disabledPieces &= supportedPieces.flip(); - } - } - - UpdateDisabledRidePieces(disabledPieces); -} - -/** - * - * rct2: 0x006CB481 - */ -WindowBase* WindowRideConstructionOpen() -{ - RideId rideIndex = _currentRideIndex; - CloseRideWindowForConstruction(rideIndex); - - auto currentRide = GetRide(rideIndex); - if (currentRide == nullptr) - { - return nullptr; - } - - WindowRideConstructionUpdateDisabledPieces(currentRide->type); - - const auto& rtd = currentRide->GetRideTypeDescriptor(); - switch (rtd.ConstructionWindowContext) - { - case RideConstructionWindowContext::Maze: - return ContextOpenWindowView(WV_MAZE_CONSTRUCTION); - case RideConstructionWindowContext::Default: - return WindowCreate( - WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE); - } - return WindowCreate(WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE); -} - -static void CloseConstructWindowOnCompletion(const Ride& ride) -{ - if (_rideConstructionState == RideConstructionState::State0) - { - auto w = WindowFindByClass(WindowClass::RideConstruction); - if (w != nullptr) - { - if (RideAreAllPossibleEntrancesAndExitsBuilt(ride).Successful) - { - WindowClose(*w); - } - else - { - w->OnMouseUp(WIDX_ENTRANCE); - } - } - } -} - -static void WindowRideConstructionDoEntranceExitCheck() -{ - auto w = WindowFindByClass(WindowClass::RideConstruction); - auto ride = GetRide(_currentRideIndex); - if (w == nullptr || ride == nullptr) - { - return; - } - - if (_rideConstructionState == RideConstructionState::State0) - { - w = WindowFindByClass(WindowClass::RideConstruction); - if (w != nullptr) - { - if (!RideAreAllPossibleEntrancesAndExitsBuilt(*ride).Successful) - { - w->OnMouseUp(WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE); - } - } - } -} - -static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActions::Result* result) -{ - if (result->Error != GameActions::Status::Ok) - { - WindowRideConstructionUpdateActiveElements(); - return; - } - auto ride = GetRide(_currentRideIndex); - if (ride != nullptr) - { - int32_t trackDirection = _currentTrackPieceDirection; - auto trackPos = _currentTrackBegin; - if (!(trackDirection & 4)) - { - trackPos -= CoordsDirectionDelta[trackDirection]; - } - - CoordsXYE next_track; - if (TrackBlockGetNextFromZero(trackPos, *ride, trackDirection, &next_track, &trackPos.z, &trackDirection, false)) - { - _currentTrackBegin.x = next_track.x; - _currentTrackBegin.y = next_track.y; - _currentTrackBegin.z = trackPos.z; - _currentTrackPieceDirection = next_track.element->GetDirection(); - _currentTrackPieceType = next_track.element->AsTrack()->GetTrackType(); - _currentTrackSelectionFlags = 0; - _rideConstructionState = RideConstructionState::Selected; - _rideConstructionNextArrowPulse = 0; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - RideSelectNextSection(); - } - else - { - _rideConstructionState = RideConstructionState::State0; - } - - WindowRideConstructionDoEntranceExitCheck(); - WindowRideConstructionUpdateActiveElements(); - } - - WindowCloseByClass(WindowClass::Error); - if (ride != nullptr) - CloseConstructWindowOnCompletion(*ride); -} - -static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActions::Result* result) -{ - if (result->Error != GameActions::Status::Ok) - { - WindowRideConstructionUpdateActiveElements(); - return; - } - auto ride = GetRide(_currentRideIndex); - if (ride != nullptr) - { - auto trackDirection = DirectionReverse(_currentTrackPieceDirection); - auto trackPos = _currentTrackBegin; - if (!(trackDirection & 4)) - { - trackPos += CoordsDirectionDelta[trackDirection]; - } - - TrackBeginEnd trackBeginEnd; - if (TrackBlockGetPreviousFromZero(trackPos, *ride, trackDirection, &trackBeginEnd)) - { - _currentTrackBegin.x = trackBeginEnd.begin_x; - _currentTrackBegin.y = trackBeginEnd.begin_y; - _currentTrackBegin.z = trackBeginEnd.begin_z; - _currentTrackPieceDirection = trackBeginEnd.begin_direction; - _currentTrackPieceType = trackBeginEnd.begin_element->AsTrack()->GetTrackType(); - _currentTrackSelectionFlags = 0; - _rideConstructionState = RideConstructionState::Selected; - _rideConstructionNextArrowPulse = 0; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - RideSelectPreviousSection(); - } - else - { - _rideConstructionState = RideConstructionState::State0; - } - - WindowRideConstructionUpdateActiveElements(); - } - - WindowCloseByClass(WindowClass::Error); - if (ride != nullptr) - CloseConstructWindowOnCompletion(*ride); -} - -/** - * - * rct2: 0x006CC538 - */ -static std::optional RideGetPlacePositionFromScreenPosition(ScreenCoordsXY screenCoords) -{ - CoordsXY mapCoords; - - if (!_trackPlaceCtrlState) - { - if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_COPY_Z) - { - auto info = GetMapCoordinatesFromPos(screenCoords, 0xFCCA); - if (info.SpriteType != ViewportInteractionItem::None) - { - _trackPlaceCtrlZ = info.Element->GetBaseZ(); - _trackPlaceCtrlState = true; - } - } - } - else - { - if (!(gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_COPY_Z)) - { - _trackPlaceCtrlState = false; - } - } - - if (!_trackPlaceShiftState) - { - if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z) - { - _trackPlaceShiftState = true; - _trackPlaceShiftStart = screenCoords; - _trackPlaceShiftZ = 0; - } - } - else - { - if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z) - { - uint16_t maxHeight = ZoomLevel::max().ApplyTo(std::numeric_limits::max() - 32); - - _trackPlaceShiftZ = _trackPlaceShiftStart.y - screenCoords.y + 4; - // Scale delta by zoom to match mouse position. - auto* mainWnd = WindowGetMain(); - if (mainWnd != nullptr && mainWnd->viewport != nullptr) - { - _trackPlaceShiftZ = mainWnd->viewport->zoom.ApplyTo(_trackPlaceShiftZ); - } - _trackPlaceShiftZ = Floor2(_trackPlaceShiftZ, 8); - - // Clamp to maximum possible value of BaseHeight can offer. - _trackPlaceShiftZ = std::min(_trackPlaceShiftZ, maxHeight); - - screenCoords = _trackPlaceShiftStart; - } - else - { - _trackPlaceShiftState = false; - } - } - - if (!_trackPlaceCtrlState) - { - mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); - if (mapCoords.IsNull()) - return std::nullopt; - - _trackPlaceZ = 0; - if (_trackPlaceShiftState) - { - auto surfaceElement = MapGetSurfaceElementAt(mapCoords); - if (surfaceElement == nullptr) - return std::nullopt; - auto mapZ = Floor2(surfaceElement->GetBaseZ(), 16); - mapZ += _trackPlaceShiftZ; - mapZ = std::max(mapZ, 16); - _trackPlaceZ = mapZ; - } - } - else - { - auto mapZ = _trackPlaceCtrlZ; - auto mapXYCoords = ScreenGetMapXYWithZ(screenCoords, mapZ); - if (mapXYCoords.has_value()) - { - mapCoords = mapXYCoords.value(); - } - else - { - return std::nullopt; - } - - if (_trackPlaceShiftState != 0) - { - mapZ += _trackPlaceShiftZ; - } - _trackPlaceZ = std::max(mapZ, 16); - } - - if (mapCoords.x == LOCATION_NULL) - return std::nullopt; - - return mapCoords.ToTileStart(); -} - -/** - * - * rct2: 0x006C84CE - */ -void WindowRideConstructionUpdateActiveElementsImpl() -{ - WindowRideConstructionUpdateEnabledTrackPieces(); - if (auto currentRide = GetRide(_currentRideIndex); - !currentRide || currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - return; - } - - auto window = static_cast(WindowFindByClass(WindowClass::RideConstruction)); - if (!window) - { - return; - } - - window->UpdateMapSelection(); - - _selectedTrackType = TrackElemType::None; - if (_rideConstructionState == RideConstructionState::Selected) - { - TileElement* tileElement; - if (GetTrackElementOriginAndApplyChanges( - { _currentTrackBegin, static_cast(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, 0, - &tileElement, 0) - != std::nullopt) - { - _selectedTrackType = tileElement->AsTrack()->GetTrackType(); - if (TrackTypeHasSpeedSetting(tileElement->AsTrack()->GetTrackType())) - _currentBrakeSpeed2 = tileElement->AsTrack()->GetBrakeBoosterSpeed(); - _currentSeatRotationAngle = tileElement->AsTrack()->GetSeatRotation(); - } - } - - window->UpdatePossibleRideConfigurations(); - window->UpdateWidgets(); -} - -/** - * - * rct2: 0x006C6A77 - */ -void WindowRideConstructionUpdateEnabledTrackPieces() -{ - auto ride = GetRide(_currentRideIndex); - if (ride == nullptr) - return; - - auto rideEntry = ride->GetRideEntry(); - if (rideEntry == nullptr) - return; - - int32_t rideType = RideGetAlternativeType(*ride); - UpdateEnabledRidePieces(rideType); -} - -/** - * - * rct2: 0x006C94D8 - */ -void UpdateGhostTrackAndArrow() -{ - RideId rideIndex; - int32_t direction, type, liftHillAndAlternativeState; - CoordsXYZ trackPos{}; - - if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK_PLACE_ACTION_QUEUED) - { - return; - } - - // Recheck if area is fine for new track. - // Set by footpath placement - if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_RECHECK) - { - RideConstructionInvalidateCurrentTrack(); - _currentTrackSelectionFlags &= ~TRACK_SELECTION_FLAG_RECHECK; - } - - switch (_rideConstructionState) - { - case RideConstructionState::Front: - case RideConstructionState::Back: - { - // place ghost piece - if (!(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK)) - { - if (WindowRideConstructionUpdateState( - &type, &direction, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr)) - { - RideConstructionRemoveGhosts(); + WindowRideConstructionUpdateActiveElements(); } else { - _currentTrackPrice = PlaceProvisionalTrackPiece( - rideIndex, type, direction, liftHillAndAlternativeState, trackPos); - WindowRideConstructionUpdateActiveElements(); + auto currentRide = GetRide(_currentRideIndex); + if (currentRide != nullptr) + { + WindowRideConstructionMouseUpDemolishNextPiece({ *newCoords, static_cast(direction) }, type); + } + } + }); + + GameActions::Execute(&trackRemoveAction); + } + + void Rotate() + { + _autoRotatingShop = false; + _currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3; + RideConstructionInvalidateCurrentTrack(); + _currentTrackPrice = kMoney64Undefined; + WindowRideConstructionUpdateActiveElements(); + } + + void EntranceClick() + { + if (ToolSet(*this, WIDX_ENTRANCE, Tool::Crosshair)) + { + auto currentRide = GetRide(_currentRideIndex); + if (currentRide != nullptr && !RideTryGetOriginElement(*currentRide, nullptr)) + { + RideInitialiseConstructionWindow(*currentRide); } } - // update flashing arrow - auto curTime = Platform::GetTicks(); - if (_rideConstructionNextArrowPulse >= curTime) - break; - _rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; - - _currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW; - trackPos = _currentTrackBegin; - direction = _currentTrackPieceDirection; - type = _currentTrackPieceType; - // diagonal pieces trigger this - if (direction >= 4) - direction += 4; - if (_rideConstructionState == RideConstructionState::Back) - direction = DirectionReverse(direction); - gMapSelectArrowPosition = trackPos; - gMapSelectArrowDirection = direction; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW) - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; - MapInvalidateTileFull(trackPos); - break; - } - case RideConstructionState::Selected: - { - auto curTime = Platform::GetTicks(); - if (_rideConstructionNextArrowPulse >= curTime) - break; - _rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; - - _currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW; - direction = _currentTrackPieceDirection & 3; - type = _currentTrackPieceType; - uint16_t flags = _currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW ? TRACK_ELEMENT_SET_HIGHLIGHT_TRUE - : TRACK_ELEMENT_SET_HIGHLIGHT_FALSE; - auto newCoords = GetTrackElementOriginAndApplyChanges( - { _currentTrackBegin, static_cast(direction) }, type, 0, nullptr, flags); - if (!newCoords.has_value()) + else + { + gRideEntranceExitPlaceType = ENTRANCE_TYPE_RIDE_ENTRANCE; + gRideEntranceExitPlaceRideIndex = _currentRideIndex; + gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0); + InputSetFlag(INPUT_FLAG_6, true); + RideConstructionInvalidateCurrentTrack(); + if (_rideConstructionState != RideConstructionState::EntranceExit) + { + gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState; + _rideConstructionState = RideConstructionState::EntranceExit; + } + WindowRideConstructionUpdateActiveElements(); + } + } + + void ExitClick() + { + if (ToolSet(*this, WIDX_EXIT, Tool::Crosshair)) + { + auto currentRide = GetRide(_currentRideIndex); + if (!RideTryGetOriginElement(*currentRide, nullptr)) + { + RideInitialiseConstructionWindow(*currentRide); + } + } + else + { + gRideEntranceExitPlaceType = ENTRANCE_TYPE_RIDE_EXIT; + gRideEntranceExitPlaceRideIndex = _currentRideIndex; + gRideEntranceExitPlaceStationIndex = StationIndex::FromUnderlying(0); + InputSetFlag(INPUT_FLAG_6, true); + RideConstructionInvalidateCurrentTrack(); + if (_rideConstructionState != RideConstructionState::EntranceExit) + { + gRideEntranceExitPlacePreviousRideConstructionState = _rideConstructionState; + _rideConstructionState = RideConstructionState::EntranceExit; + } + WindowRideConstructionUpdateActiveElements(); + } + } + + void UpdateLiftHillSelected(TrackPitch slope) + { + _currentTrackPitchEnd = slope; + _currentTrackPrice = kMoney64Undefined; + if (_rideConstructionState == RideConstructionState::Front && !GetGameState().Cheats.EnableChainLiftOnAllTrack) + { + switch (slope) + { + case TrackPitch::None: + case TrackPitch::Up25: + case TrackPitch::Up60: + break; + default: + _currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED; + break; + } + } + WindowRideConstructionUpdateActiveElements(); + } + + void SetBrakeSpeed(int32_t brakesSpeed) + { + TileElement* tileElement; + + if (GetTrackElementOriginAndApplyChanges( + { _currentTrackBegin, static_cast(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, 0, + &tileElement, 0) + != std::nullopt) + { + auto trackSetBrakeSpeed = TrackSetBrakeSpeedAction( + _currentTrackBegin, tileElement->AsTrack()->GetTrackType(), brakesSpeed); + trackSetBrakeSpeed.SetCallback([](const GameAction* ga, const GameActions::Result* result) { + WindowRideConstructionUpdateActiveElements(); + }); + GameActions::Execute(&trackSetBrakeSpeed); + return; + } + WindowRideConstructionUpdateActiveElements(); + } + + void ShowSpecialTrackDropdown(Widget* widget) + { + int32_t defaultIndex = -1; + for (size_t i = 0; i < _specialElementDropdownState.Elements.size(); i++) + { + track_type_t trackPiece = _specialElementDropdownState.Elements[i].TrackType; + + const auto& ted = GetTrackElementDescriptor(trackPiece); + StringId trackPieceStringId = ted.Description; + if (trackPieceStringId == STR_RAPIDS) + { + auto currentRide = GetRide(_currentRideIndex); + if (currentRide != nullptr) + { + const auto& rtd = currentRide->GetRideTypeDescriptor(); + if (rtd.Category != RIDE_CATEGORY_WATER) + trackPieceStringId = STR_LOG_BUMPS; + } + } + gDropdownItems[i].Format = trackPieceStringId; + if ((trackPiece | RideConstructionSpecialPieceSelected) == _currentTrackCurve) + { + defaultIndex = static_cast(i); + } + } + + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, 0, + _specialElementDropdownState.Elements.size(), widget->width()); + + for (size_t i = 0; i < _specialElementDropdownState.Elements.size(); i++) + { + Dropdown::SetDisabled(static_cast(i), _specialElementDropdownState.Elements[i].Disabled); + } + gDropdownDefaultIndex = defaultIndex; + } + + void RideSelectedTrackSetSeatRotation(int32_t seatRotation) + { + GetTrackElementOriginAndApplyChanges( + { _currentTrackBegin, static_cast(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, + seatRotation, nullptr, TRACK_ELEMENT_SET_SEAT_ROTATION); + WindowRideConstructionUpdateActiveElements(); + } + + void ToolDownEntranceExit(const ScreenCoordsXY& screenCoords) + { + RideConstructionInvalidateCurrentTrack(); + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + + CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords); + if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION) + return; + + auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction( + entranceOrExitCoords, DirectionReverse(gRideEntranceExitPlaceDirection), gRideEntranceExitPlaceRideIndex, + gRideEntranceExitPlaceStationIndex, gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT); + + rideEntranceExitPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; + + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + + auto currentRide = GetRide(gRideEntranceExitPlaceRideIndex); + if (currentRide != nullptr && RideAreAllPossibleEntrancesAndExitsBuilt(*currentRide).Successful) + { + ToolCancel(); + if (!currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) + { + WindowCloseByClass(WindowClass::RideConstruction); + } + } + else + { + gRideEntranceExitPlaceType = gRideEntranceExitPlaceType ^ 1; + WindowInvalidateByClass(WindowClass::RideConstruction); + gCurrentToolWidget.widget_index = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE) + ? WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE + : WC_RIDE_CONSTRUCTION__WIDX_EXIT; + } + }); + auto res = GameActions::Execute(&rideEntranceExitPlaceAction); + } + + void DrawTrackPiece( + DrawPixelInfo& dpi, RideId rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndInvertedState, + int32_t widgetWidth, int32_t widgetHeight) + { + auto currentRide = GetRide(rideIndex); + if (currentRide == nullptr) + { + return; + } + + const auto& ted = GetTrackElementDescriptor(trackType); + const auto* trackBlock = ted.Block; + while ((trackBlock + 1)->index != 0xFF) + trackBlock++; + + CoordsXYZ mapCoords{ trackBlock->x, trackBlock->y, trackBlock->z }; + if (trackBlock->flags & RCT_PREVIEW_TRACK_FLAG_1) + { + mapCoords.x = 0; + mapCoords.y = 0; + } + + auto rotatedMapCoords = mapCoords.Rotate(trackDirection); + // this is actually case 0, but the other cases all jump to it + mapCoords.x = 4112 + (rotatedMapCoords.x / 2); + mapCoords.y = 4112 + (rotatedMapCoords.y / 2); + mapCoords.z = 1024 + mapCoords.z; + + auto previewZOffset = ted.Definition.PreviewZOffset; + mapCoords.z -= previewZOffset; + + const ScreenCoordsXY rotatedScreenCoords = Translate3DTo2DWithZ(GetCurrentRotation(), mapCoords); + + dpi.x += rotatedScreenCoords.x - widgetWidth / 2; + dpi.y += rotatedScreenCoords.y - widgetHeight / 2 - 16; + + DrawTrackPieceHelper(dpi, rideIndex, trackType, trackDirection, liftHillAndInvertedState, { 4096, 4096 }, 1024); + } + + void DrawTrackPieceHelper( + DrawPixelInfo& dpi, RideId rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndInvertedState, + const CoordsXY& originCoords, int32_t originZ) + { + TileElement tempSideTrackTileElement{ 0x80, 0x8F, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + TileElement tempTrackTileElement{}; + TileElement* backupTileElementArrays[5]{}; + PaintSession* session = PaintSessionAlloc(dpi, 0, GetCurrentRotation()); + trackDirection &= 3; + + auto currentRide = GetRide(rideIndex); + if (currentRide == nullptr) + { + return; + } + + auto& gameState = OpenRCT2::GetGameState(); + auto preserveMapSize = gameState.MapSize; + + gameState.MapSize = { MAXIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL }; + + // Setup non changing parts of the temporary track tile element + tempTrackTileElement.SetType(TileElementType::Track); + tempTrackTileElement.SetDirection(trackDirection); + tempTrackTileElement.AsTrack()->SetHasChain((liftHillAndInvertedState & CONSTRUCTION_LIFT_HILL_SELECTED) != 0); + tempTrackTileElement.SetLastForTile(true); + tempTrackTileElement.AsTrack()->SetTrackType(trackType); + tempTrackTileElement.AsTrack()->SetRideType(currentRide->type); + tempTrackTileElement.AsTrack()->SetHasCableLift(false); + tempTrackTileElement.AsTrack()->SetInverted((liftHillAndInvertedState & CONSTRUCTION_INVERTED_TRACK_SELECTED) != 0); + tempTrackTileElement.AsTrack()->SetColourScheme(RIDE_COLOUR_SCHEME_MAIN); + // Skipping seat rotation, should not be necessary for a temporary piece. + tempTrackTileElement.AsTrack()->SetRideIndex(rideIndex); + + const auto& ted = GetTrackElementDescriptor(trackType); + const auto* trackBlock = ted.Block; + const auto* rideEntry = currentRide->GetRideEntry(); + auto clearanceHeight = (rideEntry != nullptr) ? rideEntry->Clearance + : currentRide->GetRideTypeDescriptor().Heights.ClearanceHeight; + + while (trackBlock->index != 255) + { + auto quarterTile = trackBlock->var_08.Rotate(trackDirection); + CoordsXY offsets = { trackBlock->x, trackBlock->y }; + CoordsXY coords = originCoords + offsets.Rotate(trackDirection); + + int32_t baseZ = originZ + trackBlock->z; + int32_t clearanceZ = trackBlock->ClearanceZ + clearanceHeight + baseZ + (4 * COORDS_Z_STEP); + + auto centreTileCoords = TileCoordsXY{ coords }; + auto eastTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_EAST]; + auto westTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_WEST]; + auto northTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_NORTH]; + auto southTileCoords = centreTileCoords + TileDirectionDelta[TILE_ELEMENT_DIRECTION_SOUTH]; + + // Replace map elements with temporary ones containing track + backupTileElementArrays[0] = MapGetFirstElementAt(centreTileCoords); + backupTileElementArrays[1] = MapGetFirstElementAt(eastTileCoords); + backupTileElementArrays[2] = MapGetFirstElementAt(westTileCoords); + backupTileElementArrays[3] = MapGetFirstElementAt(northTileCoords); + backupTileElementArrays[4] = MapGetFirstElementAt(southTileCoords); + MapSetTileElement(centreTileCoords, &tempTrackTileElement); + MapSetTileElement(eastTileCoords, &tempSideTrackTileElement); + MapSetTileElement(westTileCoords, &tempSideTrackTileElement); + MapSetTileElement(northTileCoords, &tempSideTrackTileElement); + MapSetTileElement(southTileCoords, &tempSideTrackTileElement); + + // Set the temporary track element + tempTrackTileElement.SetOccupiedQuadrants(quarterTile.GetBaseQuarterOccupied()); + tempTrackTileElement.SetBaseZ(baseZ); + tempTrackTileElement.SetClearanceZ(clearanceZ); + tempTrackTileElement.AsTrack()->SetSequenceIndex(trackBlock->index); + + // Draw this map tile + TileElementPaintSetup(*session, coords, true); + + // Restore map elements + MapSetTileElement(centreTileCoords, backupTileElementArrays[0]); + MapSetTileElement(eastTileCoords, backupTileElementArrays[1]); + MapSetTileElement(westTileCoords, backupTileElementArrays[2]); + MapSetTileElement(northTileCoords, backupTileElementArrays[3]); + MapSetTileElement(southTileCoords, backupTileElementArrays[4]); + + trackBlock++; + } + + gameState.MapSize = preserveMapSize; + + PaintSessionArrange(*session); + PaintDrawStructs(*session); + PaintSessionFree(session); + } + }; + + static void WindowRideConstructionUpdateDisabledPieces(ObjectEntryIndex rideType) + { + RideTrackGroup disabledPieces{}; + const auto& rtd = GetRideTypeDescriptor(rideType); + if (rtd.HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) + { + // Set all pieces as “disabled”. When looping over the ride entries, + // pieces will be re-enabled as soon as a single entry supports it. + disabledPieces.flip(); + + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto& rideEntries = objManager.GetAllRideEntries(rideType); + // If there are no vehicles for this ride type, enable all the track pieces. + if (rideEntries.size() == 0) + { + disabledPieces.reset(); + } + + for (auto rideEntryIndex : rideEntries) + { + const auto* currentRideEntry = GetRideEntryByIndex(rideEntryIndex); + if (currentRideEntry == nullptr) + continue; + + // Non-default vehicle visuals do not use this system, so we have to assume it supports all the track pieces. + if (currentRideEntry->Cars[0].PaintStyle != VEHICLE_VISUAL_DEFAULT || rideType == RIDE_TYPE_CHAIRLIFT + || (currentRideEntry->Cars[0].flags & CAR_ENTRY_FLAG_SLIDE_SWING)) + { + disabledPieces.reset(); + break; + } + + // Any pieces that this ride entry supports must be taken out of the array. + auto supportedPieces = RideEntryGetSupportedTrackPieces(*currentRideEntry); + disabledPieces &= supportedPieces.flip(); + } + } + + UpdateDisabledRidePieces(disabledPieces); + } + + /** + * + * rct2: 0x006CB481 + */ + WindowBase* WindowRideConstructionOpen() + { + RideId rideIndex = _currentRideIndex; + CloseRideWindowForConstruction(rideIndex); + + auto currentRide = GetRide(rideIndex); + if (currentRide == nullptr) + { + return nullptr; + } + + WindowRideConstructionUpdateDisabledPieces(currentRide->type); + + const auto& rtd = currentRide->GetRideTypeDescriptor(); + switch (rtd.ConstructionWindowContext) + { + case RideConstructionWindowContext::Maze: + return ContextOpenWindowView(WV_MAZE_CONSTRUCTION); + case RideConstructionWindowContext::Default: + return WindowCreate( + WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE); + } + return WindowCreate( + WindowClass::RideConstruction, ScreenCoordsXY(0, 29), WW, WH, WF_NO_AUTO_CLOSE); + } + + static void CloseConstructWindowOnCompletion(const Ride& ride) + { + if (_rideConstructionState == RideConstructionState::State0) + { + auto w = WindowFindByClass(WindowClass::RideConstruction); + if (w != nullptr) + { + if (RideAreAllPossibleEntrancesAndExitsBuilt(ride).Successful) + { + WindowClose(*w); + } + else + { + w->OnMouseUp(WIDX_ENTRANCE); + } + } + } + } + + static void WindowRideConstructionDoEntranceExitCheck() + { + auto w = WindowFindByClass(WindowClass::RideConstruction); + auto ride = GetRide(_currentRideIndex); + if (w == nullptr || ride == nullptr) + { + return; + } + + if (_rideConstructionState == RideConstructionState::State0) + { + w = WindowFindByClass(WindowClass::RideConstruction); + if (w != nullptr) + { + if (!RideAreAllPossibleEntrancesAndExitsBuilt(*ride).Successful) + { + w->OnMouseUp(WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE); + } + } + } + } + + static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActions::Result* result) + { + if (result->Error != GameActions::Status::Ok) + { + WindowRideConstructionUpdateActiveElements(); + return; + } + auto ride = GetRide(_currentRideIndex); + if (ride != nullptr) + { + int32_t trackDirection = _currentTrackPieceDirection; + auto trackPos = _currentTrackBegin; + if (!(trackDirection & 4)) + { + trackPos -= CoordsDirectionDelta[trackDirection]; + } + + CoordsXYE next_track; + if (TrackBlockGetNextFromZero(trackPos, *ride, trackDirection, &next_track, &trackPos.z, &trackDirection, false)) + { + _currentTrackBegin.x = next_track.x; + _currentTrackBegin.y = next_track.y; + _currentTrackBegin.z = trackPos.z; + _currentTrackPieceDirection = next_track.element->GetDirection(); + _currentTrackPieceType = next_track.element->AsTrack()->GetTrackType(); + _currentTrackSelectionFlags = 0; + _rideConstructionState = RideConstructionState::Selected; + _rideConstructionNextArrowPulse = 0; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + RideSelectNextSection(); + } + else { - RideConstructionRemoveGhosts(); _rideConstructionState = RideConstructionState::State0; } - break; - } - case RideConstructionState::MazeBuild: - case RideConstructionState::MazeMove: - case RideConstructionState::MazeFill: - { - auto curTime = Platform::GetTicks(); - if (_rideConstructionNextArrowPulse >= curTime) - break; - _rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; - _currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW; - trackPos = CoordsXYZ{ _currentTrackBegin.x & 0xFFE0, _currentTrackBegin.y & 0xFFE0, _currentTrackBegin.z + 15 }; - gMapSelectArrowPosition = trackPos; - gMapSelectArrowDirection = 4; - if (((_currentTrackBegin.x & 0x1F) | (_currentTrackBegin.y & 0x1F)) != 0) + WindowRideConstructionDoEntranceExitCheck(); + WindowRideConstructionUpdateActiveElements(); + } + + WindowCloseByClass(WindowClass::Error); + if (ride != nullptr) + CloseConstructWindowOnCompletion(*ride); + } + + static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActions::Result* result) + { + if (result->Error != GameActions::Status::Ok) + { + WindowRideConstructionUpdateActiveElements(); + return; + } + auto ride = GetRide(_currentRideIndex); + if (ride != nullptr) + { + auto trackDirection = DirectionReverse(_currentTrackPieceDirection); + auto trackPos = _currentTrackBegin; + if (!(trackDirection & 4)) { - gMapSelectArrowDirection = 6; - if (((_currentTrackBegin.x & 0x1F) & (_currentTrackBegin.y & 0x1F)) == 0) + trackPos += CoordsDirectionDelta[trackDirection]; + } + + TrackBeginEnd trackBeginEnd; + if (TrackBlockGetPreviousFromZero(trackPos, *ride, trackDirection, &trackBeginEnd)) + { + _currentTrackBegin.x = trackBeginEnd.begin_x; + _currentTrackBegin.y = trackBeginEnd.begin_y; + _currentTrackBegin.z = trackBeginEnd.begin_z; + _currentTrackPieceDirection = trackBeginEnd.begin_direction; + _currentTrackPieceType = trackBeginEnd.begin_element->AsTrack()->GetTrackType(); + _currentTrackSelectionFlags = 0; + _rideConstructionState = RideConstructionState::Selected; + _rideConstructionNextArrowPulse = 0; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + RideSelectPreviousSection(); + } + else + { + _rideConstructionState = RideConstructionState::State0; + } + + WindowRideConstructionUpdateActiveElements(); + } + + WindowCloseByClass(WindowClass::Error); + if (ride != nullptr) + CloseConstructWindowOnCompletion(*ride); + } + + /** + * + * rct2: 0x006CC538 + */ + static std::optional RideGetPlacePositionFromScreenPosition(ScreenCoordsXY screenCoords) + { + CoordsXY mapCoords; + + if (!_trackPlaceCtrlState) + { + if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_COPY_Z) + { + auto info = GetMapCoordinatesFromPos(screenCoords, 0xFCCA); + if (info.SpriteType != ViewportInteractionItem::None) { - gMapSelectArrowDirection = 5; - if ((_currentTrackBegin.y & 0x1F) == 0) - gMapSelectArrowDirection = 7; + _trackPlaceCtrlZ = info.Element->GetBaseZ(); + _trackPlaceCtrlState = true; } } - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW) - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; - MapInvalidateTileFull(trackPos); - break; } - default: - break; - } -} + else + { + if (!(gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_COPY_Z)) + { + _trackPlaceCtrlState = false; + } + } -/** - * - * rct2: 0x006CC6A8 - */ -void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords) -{ - int32_t z; - const PreviewTrack* trackBlock; + if (!_trackPlaceShiftState) + { + if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z) + { + _trackPlaceShiftState = true; + _trackPlaceShiftStart = screenCoords; + _trackPlaceShiftZ = 0; + } + } + else + { + if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z) + { + uint16_t maxHeight = ZoomLevel::max().ApplyTo( + std::numeric_limits::max() - 32); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - auto mapCoords = RideGetPlacePositionFromScreenPosition(screenCoords); - if (!mapCoords) - { - RideConstructionInvalidateCurrentTrack(); - MapInvalidateMapSelectionTiles(); - return; + _trackPlaceShiftZ = _trackPlaceShiftStart.y - screenCoords.y + 4; + // Scale delta by zoom to match mouse position. + auto* mainWnd = WindowGetMain(); + if (mainWnd != nullptr && mainWnd->viewport != nullptr) + { + _trackPlaceShiftZ = mainWnd->viewport->zoom.ApplyTo(_trackPlaceShiftZ); + } + _trackPlaceShiftZ = Floor2(_trackPlaceShiftZ, 8); + + // Clamp to maximum possible value of BaseHeight can offer. + _trackPlaceShiftZ = std::min(_trackPlaceShiftZ, maxHeight); + + screenCoords = _trackPlaceShiftStart; + } + else + { + _trackPlaceShiftState = false; + } + } + + if (!_trackPlaceCtrlState) + { + mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); + if (mapCoords.IsNull()) + return std::nullopt; + + _trackPlaceZ = 0; + if (_trackPlaceShiftState) + { + auto surfaceElement = MapGetSurfaceElementAt(mapCoords); + if (surfaceElement == nullptr) + return std::nullopt; + auto mapZ = Floor2(surfaceElement->GetBaseZ(), 16); + mapZ += _trackPlaceShiftZ; + mapZ = std::max(mapZ, 16); + _trackPlaceZ = mapZ; + } + } + else + { + auto mapZ = _trackPlaceCtrlZ; + auto mapXYCoords = ScreenGetMapXYWithZ(screenCoords, mapZ); + if (mapXYCoords.has_value()) + { + mapCoords = mapXYCoords.value(); + } + else + { + return std::nullopt; + } + + if (_trackPlaceShiftState != 0) + { + mapZ += _trackPlaceShiftZ; + } + _trackPlaceZ = std::max(mapZ, 16); + } + + if (mapCoords.x == LOCATION_NULL) + return std::nullopt; + + return mapCoords.ToTileStart(); } - z = _trackPlaceZ; - if (z == 0) - z = MapGetHighestZ(*mapCoords); - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; - gMapSelectArrowPosition = CoordsXYZ{ *mapCoords, z }; - gMapSelectArrowDirection = _currentTrackPieceDirection; - gMapSelectionTiles.clear(); - gMapSelectionTiles.push_back(*mapCoords); - - RideId rideIndex; - int32_t trackType, trackDirection, liftHillAndAlternativeState; - if (WindowRideConstructionUpdateState( - &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, nullptr, nullptr)) + /** + * + * rct2: 0x006C84CE + */ + void WindowRideConstructionUpdateActiveElementsImpl() { - RideConstructionInvalidateCurrentTrack(); - MapInvalidateMapSelectionTiles(); - return; - } - _currentTrackPieceType = trackType; - auto ride = GetRide(_currentRideIndex); - if (!ride) - { - return; - } + WindowRideConstructionUpdateEnabledTrackPieces(); + if (auto currentRide = GetRide(_currentRideIndex); + !currentRide || currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + return; + } - const auto& rtd = ride->GetRideTypeDescriptor(); - if (!rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { auto window = static_cast(WindowFindByClass(WindowClass::RideConstruction)); if (!window) { return; } - // Re-using this other code, very slight difference from original - // - Original code checks for MSB mask instead of 255 on trackPart->var_00 - // - Original code checks this first as its already set origin tile, probably just a micro optimisation - window->SelectMapTiles(trackType, trackDirection, *mapCoords); - } - gMapSelectArrowPosition.z = z; - if (_trackPlaceZ == 0) - { - // Raise z above all slopes and water - if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) + window->UpdateMapSelection(); + + _selectedTrackType = TrackElemType::None; + if (_rideConstructionState == RideConstructionState::Selected) { - int32_t highestZ = 0; - for (const auto& selectedTile : gMapSelectionTiles) + TileElement* tileElement; + if (GetTrackElementOriginAndApplyChanges( + { _currentTrackBegin, static_cast(_currentTrackPieceDirection & 3) }, _currentTrackPieceType, 0, + &tileElement, 0) + != std::nullopt) { - if (MapIsLocationValid(selectedTile)) - { - z = MapGetHighestZ(selectedTile); - if (z > highestZ) - highestZ = z; - } + _selectedTrackType = tileElement->AsTrack()->GetTrackType(); + if (TrackTypeHasSpeedSetting(tileElement->AsTrack()->GetTrackType())) + _currentBrakeSpeed2 = tileElement->AsTrack()->GetBrakeBoosterSpeed(); + _currentSeatRotationAngle = tileElement->AsTrack()->GetSeatRotation(); } } - // Loc6CC8BF: - // z = MapGetHighestZ(x >> 5, y >> 5); - } - // Loc6CC91B: - const auto& ted = GetTrackElementDescriptor(trackType); - trackBlock = ted.Block; - int32_t bx = 0; - do - { - bx = std::min(bx, trackBlock->z); - trackBlock++; - } while (trackBlock->index != 255); - z -= bx; - gMapSelectArrowPosition.z = z; - _currentTrackBegin.x = mapCoords->x; - _currentTrackBegin.y = mapCoords->y; - _currentTrackBegin.z = z; - if ((_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK) && _currentTrackBegin == _previousTrackPiece) + window->UpdatePossibleRideConfigurations(); + window->UpdateWidgets(); + } + + /** + * + * rct2: 0x006C6A77 + */ + void WindowRideConstructionUpdateEnabledTrackPieces() { + auto ride = GetRide(_currentRideIndex); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + int32_t rideType = RideGetAlternativeType(*ride); + UpdateEnabledRidePieces(rideType); + } + + /** + * + * rct2: 0x006C94D8 + */ + void UpdateGhostTrackAndArrow() + { + RideId rideIndex; + int32_t direction, type, liftHillAndAlternativeState; + CoordsXYZ trackPos{}; + + if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK_PLACE_ACTION_QUEUED) + { + return; + } + + // Recheck if area is fine for new track. + // Set by footpath placement + if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_RECHECK) + { + RideConstructionInvalidateCurrentTrack(); + _currentTrackSelectionFlags &= ~TRACK_SELECTION_FLAG_RECHECK; + } + + switch (_rideConstructionState) + { + case RideConstructionState::Front: + case RideConstructionState::Back: + { + // place ghost piece + if (!(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK)) + { + if (WindowRideConstructionUpdateState( + &type, &direction, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr)) + { + RideConstructionRemoveGhosts(); + } + else + { + _currentTrackPrice = PlaceProvisionalTrackPiece( + rideIndex, type, direction, liftHillAndAlternativeState, trackPos); + WindowRideConstructionUpdateActiveElements(); + } + } + // update flashing arrow + auto curTime = Platform::GetTicks(); + if (_rideConstructionNextArrowPulse >= curTime) + break; + _rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; + + _currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW; + trackPos = _currentTrackBegin; + direction = _currentTrackPieceDirection; + type = _currentTrackPieceType; + // diagonal pieces trigger this + if (direction >= 4) + direction += 4; + if (_rideConstructionState == RideConstructionState::Back) + direction = DirectionReverse(direction); + gMapSelectArrowPosition = trackPos; + gMapSelectArrowDirection = direction; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW) + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; + MapInvalidateTileFull(trackPos); + break; + } + case RideConstructionState::Selected: + { + auto curTime = Platform::GetTicks(); + if (_rideConstructionNextArrowPulse >= curTime) + break; + _rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; + + _currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW; + direction = _currentTrackPieceDirection & 3; + type = _currentTrackPieceType; + uint16_t flags = _currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW ? TRACK_ELEMENT_SET_HIGHLIGHT_TRUE + : TRACK_ELEMENT_SET_HIGHLIGHT_FALSE; + auto newCoords = GetTrackElementOriginAndApplyChanges( + { _currentTrackBegin, static_cast(direction) }, type, 0, nullptr, flags); + if (!newCoords.has_value()) + { + RideConstructionRemoveGhosts(); + _rideConstructionState = RideConstructionState::State0; + } + break; + } + case RideConstructionState::MazeBuild: + case RideConstructionState::MazeMove: + case RideConstructionState::MazeFill: + { + auto curTime = Platform::GetTicks(); + if (_rideConstructionNextArrowPulse >= curTime) + break; + _rideConstructionNextArrowPulse = curTime + ARROW_PULSE_DURATION; + + _currentTrackSelectionFlags ^= TRACK_SELECTION_FLAG_ARROW; + trackPos = CoordsXYZ{ _currentTrackBegin.x & 0xFFE0, _currentTrackBegin.y & 0xFFE0, _currentTrackBegin.z + 15 }; + gMapSelectArrowPosition = trackPos; + gMapSelectArrowDirection = 4; + if (((_currentTrackBegin.x & 0x1F) | (_currentTrackBegin.y & 0x1F)) != 0) + { + gMapSelectArrowDirection = 6; + if (((_currentTrackBegin.x & 0x1F) & (_currentTrackBegin.y & 0x1F)) == 0) + { + gMapSelectArrowDirection = 5; + if ((_currentTrackBegin.y & 0x1F) == 0) + gMapSelectArrowDirection = 7; + } + } + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ARROW) + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; + MapInvalidateTileFull(trackPos); + break; + } + default: + break; + } + } + + /** + * + * rct2: 0x006CC6A8 + */ + void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords) + { + int32_t z; + const PreviewTrack* trackBlock; + MapInvalidateMapSelectionTiles(); - return; - } + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + auto mapCoords = RideGetPlacePositionFromScreenPosition(screenCoords); + if (!mapCoords) + { + RideConstructionInvalidateCurrentTrack(); + MapInvalidateMapSelectionTiles(); + return; + } - _previousTrackPiece = _currentTrackBegin; - // search for appropriate z value for ghost, up to max ride height - int numAttempts = (z <= MAX_TRACK_HEIGHT ? ((MAX_TRACK_HEIGHT - z) / COORDS_Z_STEP + 1) : 2); + z = _trackPlaceZ; + if (z == 0) + z = MapGetHighestZ(*mapCoords); + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; + gMapSelectArrowPosition = CoordsXYZ{ *mapCoords, z }; + gMapSelectArrowDirection = _currentTrackPieceDirection; + gMapSelectionTiles.clear(); + gMapSelectionTiles.push_back(*mapCoords); + + RideId rideIndex; + int32_t trackType, trackDirection, liftHillAndAlternativeState; + if (WindowRideConstructionUpdateState( + &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, nullptr, nullptr)) + { + RideConstructionInvalidateCurrentTrack(); + MapInvalidateMapSelectionTiles(); + return; + } + _currentTrackPieceType = trackType; + auto ride = GetRide(_currentRideIndex); + if (!ride) + { + return; + } + + const auto& rtd = ride->GetRideTypeDescriptor(); + if (!rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + auto window = static_cast(WindowFindByClass(WindowClass::RideConstruction)); + if (!window) + { + return; + } + // Re-using this other code, very slight difference from original + // - Original code checks for MSB mask instead of 255 on trackPart->var_00 + // - Original code checks this first as its already set origin tile, probably just a micro optimisation + window->SelectMapTiles(trackType, trackDirection, *mapCoords); + } + + gMapSelectArrowPosition.z = z; + if (_trackPlaceZ == 0) + { + // Raise z above all slopes and water + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) + { + int32_t highestZ = 0; + for (const auto& selectedTile : gMapSelectionTiles) + { + if (MapIsLocationValid(selectedTile)) + { + z = MapGetHighestZ(selectedTile); + if (z > highestZ) + highestZ = z; + } + } + } + // Loc6CC8BF: + // z = MapGetHighestZ(x >> 5, y >> 5); + } + // Loc6CC91B: + const auto& ted = GetTrackElementDescriptor(trackType); + trackBlock = ted.Block; + int32_t bx = 0; + do + { + bx = std::min(bx, trackBlock->z); + trackBlock++; + } while (trackBlock->index != 255); + z -= bx; + + gMapSelectArrowPosition.z = z; + _currentTrackBegin.x = mapCoords->x; + _currentTrackBegin.y = mapCoords->y; + _currentTrackBegin.z = z; + if ((_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK) && _currentTrackBegin == _previousTrackPiece) + { + MapInvalidateMapSelectionTiles(); + return; + } + + _previousTrackPiece = _currentTrackBegin; + // search for appropriate z value for ghost, up to max ride height + int numAttempts = (z <= MAX_TRACK_HEIGHT ? ((MAX_TRACK_HEIGHT - z) / COORDS_Z_STEP + 1) : 2); + + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + for (int zAttempts = 0; zAttempts < numAttempts; ++zAttempts) + { + CoordsXYZ trackPos{}; + WindowRideConstructionUpdateState( + &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr); + _currentTrackPrice = PlaceProvisionalTrackPiece( + rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos); + if (_currentTrackPrice != kMoney64Undefined) + break; + + _currentTrackBegin.z -= 8; + if (_currentTrackBegin.z < 0) + break; + + _currentTrackBegin.z += 16; + } + + auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); + ContextBroadcastIntent(&intent); + MapInvalidateMapSelectionTiles(); + return; + } - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { for (int zAttempts = 0; zAttempts < numAttempts; ++zAttempts) { CoordsXYZ trackPos{}; @@ -3275,6 +3333,8 @@ void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords) &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr); _currentTrackPrice = PlaceProvisionalTrackPiece( rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos); + mapCoords = trackPos; + z = trackPos.z; if (_currentTrackPrice != kMoney64Undefined) break; @@ -3285,1287 +3345,1274 @@ void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords) _currentTrackBegin.z += 16; } - auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); - ContextBroadcastIntent(&intent); - MapInvalidateMapSelectionTiles(); - return; - } - - for (int zAttempts = 0; zAttempts < numAttempts; ++zAttempts) - { - CoordsXYZ trackPos{}; - WindowRideConstructionUpdateState( - &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr); - _currentTrackPrice = PlaceProvisionalTrackPiece( - rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos); - mapCoords = trackPos; - z = trackPos.z; - if (_currentTrackPrice != kMoney64Undefined) - break; - - _currentTrackBegin.z -= 8; - if (_currentTrackBegin.z < 0) - break; - - _currentTrackBegin.z += 16; - } - - if (_autoRotatingShop && _rideConstructionState == RideConstructionState::Place - && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) - { - PathElement* pathsByDir[NumOrthogonalDirections]; - - bool keepOrientation = false; - for (int8_t i = 0; i < NumOrthogonalDirections; i++) + if (_autoRotatingShop && _rideConstructionState == RideConstructionState::Place + && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_IS_SHOP_OR_FACILITY)) { - const auto testLoc = CoordsXYZ{ *mapCoords + CoordsDirectionDelta[i], z }; - if (!MapIsLocationOwned(testLoc)) + PathElement* pathsByDir[NumOrthogonalDirections]; + + bool keepOrientation = false; + for (int8_t i = 0; i < NumOrthogonalDirections; i++) { - pathsByDir[i] = nullptr; - continue; - } + const auto testLoc = CoordsXYZ{ *mapCoords + CoordsDirectionDelta[i], z }; + if (!MapIsLocationOwned(testLoc)) + { + pathsByDir[i] = nullptr; + continue; + } - pathsByDir[i] = MapGetFootpathElement(testLoc); + pathsByDir[i] = MapGetFootpathElement(testLoc); - if (pathsByDir[i] != nullptr && pathsByDir[i]->IsSloped() && pathsByDir[i]->GetSlopeDirection() != i) - { - pathsByDir[i] = nullptr; - } - - // Sloped path on the level below - if (pathsByDir[i] == nullptr) - { - pathsByDir[i] = MapGetFootpathElement({ *mapCoords + CoordsDirectionDelta[i], z - PATH_HEIGHT_STEP }); - - if (pathsByDir[i] != nullptr - && (!pathsByDir[i]->IsSloped() || pathsByDir[i]->GetSlopeDirection() != DirectionReverse(i))) + if (pathsByDir[i] != nullptr && pathsByDir[i]->IsSloped() && pathsByDir[i]->GetSlopeDirection() != i) { pathsByDir[i] = nullptr; } - } - if (pathsByDir[i] != nullptr && pathsByDir[i]->IsQueue()) - { - pathsByDir[i] = nullptr; - } - - if (pathsByDir[i] != nullptr && i == _currentTrackPieceDirection) - { - keepOrientation = true; - break; - } - } - - if (!keepOrientation) - { - for (int8_t i = 0; i < NumOrthogonalDirections; i++) - { - if (pathsByDir[i] != nullptr) + // Sloped path on the level below + if (pathsByDir[i] == nullptr) { - _currentTrackPieceDirection = i; + pathsByDir[i] = MapGetFootpathElement({ *mapCoords + CoordsDirectionDelta[i], z - PATH_HEIGHT_STEP }); - CoordsXYZ trackPos{}; - WindowRideConstructionUpdateState( - &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr); - PlaceProvisionalTrackPiece(rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos); - gMapSelectArrowDirection = _currentTrackPieceDirection; + if (pathsByDir[i] != nullptr + && (!pathsByDir[i]->IsSloped() || pathsByDir[i]->GetSlopeDirection() != DirectionReverse(i))) + { + pathsByDir[i] = nullptr; + } + } + + if (pathsByDir[i] != nullptr && pathsByDir[i]->IsQueue()) + { + pathsByDir[i] = nullptr; + } + + if (pathsByDir[i] != nullptr && i == _currentTrackPieceDirection) + { + keepOrientation = true; break; } } + + if (!keepOrientation) + { + for (int8_t i = 0; i < NumOrthogonalDirections; i++) + { + if (pathsByDir[i] != nullptr) + { + _currentTrackPieceDirection = i; + + CoordsXYZ trackPos{}; + WindowRideConstructionUpdateState( + &trackType, &trackDirection, &rideIndex, &liftHillAndAlternativeState, &trackPos, nullptr); + PlaceProvisionalTrackPiece(rideIndex, trackType, trackDirection, liftHillAndAlternativeState, trackPos); + gMapSelectArrowDirection = _currentTrackPieceDirection; + break; + } + } + } } - } - WindowRideConstructionUpdateActiveElements(); - MapInvalidateMapSelectionTiles(); -} - -/** - * - * rct2: 0x006CD354 - */ -void RideConstructionToolupdateEntranceExit(const ScreenCoordsXY& screenCoords) -{ - MapInvalidateSelectionRect(); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords); - if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION) - { - RideConstructionInvalidateCurrentTrack(); - return; - } - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = entranceOrExitCoords; - gMapSelectPositionB = entranceOrExitCoords; - gMapSelectArrowPosition = entranceOrExitCoords; - gMapSelectArrowDirection = DirectionReverse(entranceOrExitCoords.direction); - MapInvalidateSelectionRect(); - - entranceOrExitCoords.direction = DirectionReverse(gRideEntranceExitPlaceDirection); - StationIndex stationNum = gRideEntranceExitPlaceStationIndex; - if (!(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT) - || entranceOrExitCoords != gRideEntranceExitGhostPosition || stationNum != gRideEntranceExitGhostStationIndex) - { - auto ride = GetRide(_currentRideIndex); - if (ride != nullptr) - { - _currentTrackPrice = RideEntranceExitPlaceGhost( - *ride, entranceOrExitCoords, entranceOrExitCoords.direction, gRideEntranceExitPlaceType, stationNum); - } WindowRideConstructionUpdateActiveElements(); + MapInvalidateMapSelectionTiles(); } -} -/** - * - * rct2: 0x006CCA73 - */ -void RideConstructionTooldownConstruct(const ScreenCoordsXY& screenCoords) -{ - const CursorState* state = ContextGetCursorState(); - - WindowBase* w; - - MapInvalidateMapSelectionTiles(); - RideConstructionInvalidateCurrentTrack(); - - CoordsXYZ mapCoords{}; - int32_t trackType, z, highestZ; - - if (WindowRideConstructionUpdateState(&trackType, nullptr, nullptr, nullptr, nullptr, nullptr)) - return; - - z = mapCoords.z; - _currentTrackPieceType = trackType; - - // Raise z above all slopes and water - highestZ = 0; - if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) + /** + * + * rct2: 0x006CD354 + */ + void RideConstructionToolupdateEntranceExit(const ScreenCoordsXY& screenCoords) { - for (const auto& selectedTile : gMapSelectionTiles) + MapInvalidateSelectionRect(); + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + CoordsXYZD entranceOrExitCoords = RideGetEntranceOrExitPositionFromScreenPosition(screenCoords); + if (gRideEntranceExitPlaceDirection == INVALID_DIRECTION) { - if (!MapIsLocationValid(selectedTile)) - continue; + RideConstructionInvalidateCurrentTrack(); + return; + } + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectPositionA = entranceOrExitCoords; + gMapSelectPositionB = entranceOrExitCoords; + gMapSelectArrowPosition = entranceOrExitCoords; + gMapSelectArrowDirection = DirectionReverse(entranceOrExitCoords.direction); + MapInvalidateSelectionRect(); - z = MapGetHighestZ(selectedTile); - if (z > highestZ) - highestZ = z; + entranceOrExitCoords.direction = DirectionReverse(gRideEntranceExitPlaceDirection); + StationIndex stationNum = gRideEntranceExitPlaceStationIndex; + if (!(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT) + || entranceOrExitCoords != gRideEntranceExitGhostPosition || stationNum != gRideEntranceExitGhostStationIndex) + { + auto ride = GetRide(_currentRideIndex); + if (ride != nullptr) + { + _currentTrackPrice = RideEntranceExitPlaceGhost( + *ride, entranceOrExitCoords, entranceOrExitCoords.direction, gRideEntranceExitPlaceType, stationNum); + } + WindowRideConstructionUpdateActiveElements(); } } - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - auto ridePlacePosition = RideGetPlacePositionFromScreenPosition(screenCoords); - if (!ridePlacePosition) - return; - - mapCoords = { *ridePlacePosition, z }; - - z = _trackPlaceZ; - if (z == 0) - z = MapGetHighestZ(mapCoords); - - ToolCancel(); - - auto ride = GetRide(_currentRideIndex); - if (ride == nullptr) - return; - - if (_trackPlaceZ == 0) + /** + * + * rct2: 0x006CCA73 + */ + void RideConstructionTooldownConstruct(const ScreenCoordsXY& screenCoords) { - const auto& ted = GetTrackElementDescriptor(_currentTrackPieceType); - const PreviewTrack* trackBlock = ted.Block; - int32_t bx = 0; - do - { - bx = std::min(bx, trackBlock->z); - trackBlock++; - } while (trackBlock->index != 255); - z -= bx; + const CursorState* state = ContextGetCursorState(); - // FIX not sure exactly why it starts trial and error place from a lower Z, but it causes issues with disable clearance - if (!GetGameState().Cheats.DisableClearanceChecks && z > kMinimumLandZ) + WindowBase* w; + + MapInvalidateMapSelectionTiles(); + RideConstructionInvalidateCurrentTrack(); + + CoordsXYZ mapCoords{}; + int32_t trackType, z, highestZ; + + if (WindowRideConstructionUpdateState(&trackType, nullptr, nullptr, nullptr, nullptr, nullptr)) + return; + + z = mapCoords.z; + _currentTrackPieceType = trackType; + + // Raise z above all slopes and water + highestZ = 0; + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) { - z -= LAND_HEIGHT_STEP; + for (const auto& selectedTile : gMapSelectionTiles) + { + if (!MapIsLocationValid(selectedTile)) + continue; + + z = MapGetHighestZ(selectedTile); + if (z > highestZ) + highestZ = z; + } } - } - else - { + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + auto ridePlacePosition = RideGetPlacePositionFromScreenPosition(screenCoords); + if (!ridePlacePosition) + return; + + mapCoords = { *ridePlacePosition, z }; + z = _trackPlaceZ; - } + if (z == 0) + z = MapGetHighestZ(mapCoords); - // search for z value to build at, up to max ride height - int numAttempts = (z <= MAX_TRACK_HEIGHT ? ((MAX_TRACK_HEIGHT - z) / COORDS_Z_STEP + 1) : 2); + ToolCancel(); + + auto ride = GetRide(_currentRideIndex); + if (ride == nullptr) + return; + + if (_trackPlaceZ == 0) + { + const auto& ted = GetTrackElementDescriptor(_currentTrackPieceType); + const PreviewTrack* trackBlock = ted.Block; + int32_t bx = 0; + do + { + bx = std::min(bx, trackBlock->z); + trackBlock++; + } while (trackBlock->index != 255); + z -= bx; + + // FIX not sure exactly why it starts trial and error place from a lower Z, but it causes issues with disable + // clearance + if (!GetGameState().Cheats.DisableClearanceChecks && z > kMinimumLandZ) + { + z -= LAND_HEIGHT_STEP; + } + } + else + { + z = _trackPlaceZ; + } + + // search for z value to build at, up to max ride height + int numAttempts = (z <= MAX_TRACK_HEIGHT ? ((MAX_TRACK_HEIGHT - z) / COORDS_Z_STEP + 1) : 2); + + const auto& rtd = ride->GetRideTypeDescriptor(); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + for (int32_t zAttempts = 0; zAttempts < numAttempts; ++zAttempts) + { + _rideConstructionState = RideConstructionState::MazeBuild; + _currentTrackBegin.x = mapCoords.x; + _currentTrackBegin.y = mapCoords.y; + _currentTrackBegin.z = z; + _currentTrackSelectionFlags = 0; + auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); + ContextBroadcastIntent(&intent); + w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr) + break; + + gDisableErrorWindowSound = true; + + auto gameAction = MazeSetTrackAction( + CoordsXYZD{ _currentTrackBegin, 0 }, true, _currentRideIndex, GC_SET_MAZE_TRACK_BUILD); + auto mazeSetTrackResult = GameActions::Execute(&gameAction); + if (mazeSetTrackResult.Error == GameActions::Status::Ok) + { + _trackPlaceCost = mazeSetTrackResult.Cost; + _trackPlaceErrorMessage = STR_NONE; + } + else + { + _trackPlaceCost = kMoney64Undefined; + _trackPlaceErrorMessage = std::get(mazeSetTrackResult.ErrorMessage); + } + + gDisableErrorWindowSound = false; + + if (mazeSetTrackResult.Error != GameActions::Status::Ok) + { + _rideConstructionState = RideConstructionState::Place; + StringId errorText = std::get(mazeSetTrackResult.ErrorMessage); + z -= 8; + if (errorText == STR_NOT_ENOUGH_CASH_REQUIRES || errorText == STR_CAN_ONLY_BUILD_THIS_UNDERWATER + || errorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER || errorText == STR_RIDE_CANT_BUILD_THIS_UNDERWATER + || errorText == STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND || errorText == STR_TOO_HIGH_FOR_SUPPORTS + || zAttempts == (numAttempts - 1) || z < 0) + { + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Error, 0, state->position.x); + w = WindowFindByClass(WindowClass::RideConstruction); + if (w != nullptr) + { + ToolSet(*w, WIDX_CONSTRUCT, Tool::Crosshair); + InputSetFlag(INPUT_FLAG_6, true); + _trackPlaceCtrlState = false; + _trackPlaceShiftState = false; + } + auto intent2 = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); + ContextBroadcastIntent(&intent2); + break; + } + z += 16; + } + else + { + WindowCloseByClass(WindowClass::Error); + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, _currentTrackBegin); + break; + } + } + return; + } - const auto& rtd = ride->GetRideTypeDescriptor(); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { for (int32_t zAttempts = 0; zAttempts < numAttempts; ++zAttempts) { - _rideConstructionState = RideConstructionState::MazeBuild; + _rideConstructionState = RideConstructionState::Front; _currentTrackBegin.x = mapCoords.x; _currentTrackBegin.y = mapCoords.y; _currentTrackBegin.z = z; _currentTrackSelectionFlags = 0; - auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); - ContextBroadcastIntent(&intent); + WindowRideConstructionUpdateActiveElements(); w = WindowFindByClass(WindowClass::RideConstruction); if (w == nullptr) break; gDisableErrorWindowSound = true; - - auto gameAction = MazeSetTrackAction( - CoordsXYZD{ _currentTrackBegin, 0 }, true, _currentRideIndex, GC_SET_MAZE_TRACK_BUILD); - auto mazeSetTrackResult = GameActions::Execute(&gameAction); - if (mazeSetTrackResult.Error == GameActions::Status::Ok) - { - _trackPlaceCost = mazeSetTrackResult.Cost; - _trackPlaceErrorMessage = STR_NONE; - } - else - { - _trackPlaceCost = kMoney64Undefined; - _trackPlaceErrorMessage = std::get(mazeSetTrackResult.ErrorMessage); - } - + w->OnMouseUp(WIDX_CONSTRUCT); gDisableErrorWindowSound = false; - if (mazeSetTrackResult.Error != GameActions::Status::Ok) + if (_trackPlaceCost == kMoney64Undefined) { - _rideConstructionState = RideConstructionState::Place; - StringId errorText = std::get(mazeSetTrackResult.ErrorMessage); + StringId errorText = _trackPlaceErrorMessage; z -= 8; if (errorText == STR_NOT_ENOUGH_CASH_REQUIRES || errorText == STR_CAN_ONLY_BUILD_THIS_UNDERWATER - || errorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER || errorText == STR_RIDE_CANT_BUILD_THIS_UNDERWATER - || errorText == STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND || errorText == STR_TOO_HIGH_FOR_SUPPORTS + || errorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER || errorText == STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND + || errorText == STR_TOO_HIGH_FOR_SUPPORTS || errorText == STR_TOO_HIGH + || errorText == STR_LOCAL_AUTHORITY_WONT_ALLOW_CONSTRUCTION_ABOVE_TREE_HEIGHT || zAttempts == (numAttempts - 1) || z < 0) { + int32_t saveTrackDirection = _currentTrackPieceDirection; + auto saveCurrentTrackCurve = _currentTrackCurve; + auto savePreviousTrackPitchEnd = _previousTrackPitchEnd; + auto saveCurrentTrackPitchEnd = _currentTrackPitchEnd; + auto savePreviousTrackRollEnd = _previousTrackRollEnd; + auto saveCurrentTrackRollEnd = _currentTrackRollEnd; + int32_t saveCurrentTrackAlternative = _currentTrackAlternative; + int32_t saveCurrentTrackLiftHill = _currentTrackLiftHill; + + RideInitialiseConstructionWindow(*ride); + + _currentTrackPieceDirection = saveTrackDirection; + _currentTrackCurve = saveCurrentTrackCurve; + _previousTrackPitchEnd = savePreviousTrackPitchEnd; + _currentTrackPitchEnd = saveCurrentTrackPitchEnd; + _previousTrackRollEnd = savePreviousTrackRollEnd; + _currentTrackRollEnd = saveCurrentTrackRollEnd; + _currentTrackAlternative = saveCurrentTrackAlternative; + _currentTrackLiftHill = saveCurrentTrackLiftHill; + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Error, 0, state->position.x); - w = WindowFindByClass(WindowClass::RideConstruction); - if (w != nullptr) - { - ToolSet(*w, WIDX_CONSTRUCT, Tool::Crosshair); - InputSetFlag(INPUT_FLAG_6, true); - _trackPlaceCtrlState = false; - _trackPlaceShiftState = false; - } - auto intent2 = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); - ContextBroadcastIntent(&intent2); break; } + z += 16; } else { - WindowCloseByClass(WindowClass::Error); - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, _currentTrackBegin); break; } } - return; } - for (int32_t zAttempts = 0; zAttempts < numAttempts; ++zAttempts) + void WindowRideConstructionKeyboardShortcutTurnLeft() { - _rideConstructionState = RideConstructionState::Front; - _currentTrackBegin.x = mapCoords.x; - _currentTrackBegin.y = mapCoords.y; - _currentTrackBegin.z = z; - _currentTrackSelectionFlags = 0; - WindowRideConstructionUpdateActiveElements(); - w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr) - break; - - gDisableErrorWindowSound = true; - w->OnMouseUp(WIDX_CONSTRUCT); - gDisableErrorWindowSound = false; - - if (_trackPlaceCost == kMoney64Undefined) + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) { - StringId errorText = _trackPlaceErrorMessage; - z -= 8; - if (errorText == STR_NOT_ENOUGH_CASH_REQUIRES || errorText == STR_CAN_ONLY_BUILD_THIS_UNDERWATER - || errorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER || errorText == STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND - || errorText == STR_TOO_HIGH_FOR_SUPPORTS || errorText == STR_TOO_HIGH - || errorText == STR_LOCAL_AUTHORITY_WONT_ALLOW_CONSTRUCTION_ABOVE_TREE_HEIGHT || zAttempts == (numAttempts - 1) - || z < 0) - { - int32_t saveTrackDirection = _currentTrackPieceDirection; - auto saveCurrentTrackCurve = _currentTrackCurve; - auto savePreviousTrackPitchEnd = _previousTrackPitchEnd; - auto saveCurrentTrackPitchEnd = _currentTrackPitchEnd; - auto savePreviousTrackRollEnd = _previousTrackRollEnd; - auto saveCurrentTrackRollEnd = _currentTrackRollEnd; - int32_t saveCurrentTrackAlternative = _currentTrackAlternative; - int32_t saveCurrentTrackLiftHill = _currentTrackLiftHill; + return; + } - RideInitialiseConstructionWindow(*ride); - - _currentTrackPieceDirection = saveTrackDirection; - _currentTrackCurve = saveCurrentTrackCurve; - _previousTrackPitchEnd = savePreviousTrackPitchEnd; - _currentTrackPitchEnd = saveCurrentTrackPitchEnd; - _previousTrackRollEnd = savePreviousTrackRollEnd; - _currentTrackRollEnd = saveCurrentTrackRollEnd; - _currentTrackAlternative = saveCurrentTrackAlternative; - _currentTrackLiftHill = saveCurrentTrackLiftHill; - - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Error, 0, state->position.x); + switch (_currentTrackCurve) + { + case EnumValue(TrackCurve::LeftSmall): + if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } break; - } - - z += 16; + case EnumValue(TrackCurve::Left): + if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::LeftLarge): + if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::None): + if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::RightLarge): + if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::Right): + if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::RightSmall): + if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::RightVerySmall): + if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) + && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + default: + return; } - else + } + + void WindowRideConstructionKeyboardShortcutTurnRight() + { + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) { - break; + return; + } + + switch (_currentTrackCurve) + { + case EnumValue(TrackCurve::RightSmall): + if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + break; + case EnumValue(TrackCurve::Right): + if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::RightLarge): + if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::None): + if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::LeftLarge): + if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::Left): + if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::LeftSmall): + if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + case EnumValue(TrackCurve::LeftVerySmall): + if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) + && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); + } + else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) + && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); + } + else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) + && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); + } + else if ( + !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) + && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); + } + else + { + return; + } + break; + default: + return; } } -} -void WindowRideConstructionKeyboardShortcutTurnLeft() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) + void WindowRideConstructionKeyboardShortcutUseTrackDefault() { - return; - } - - switch (_currentTrackCurve) - { - case EnumValue(TrackCurve::LeftSmall): - if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - break; - case EnumValue(TrackCurve::Left): - if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::LeftLarge): - if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::None): - if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::RightLarge): - if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::Right): - if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::RightSmall): - if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::RightVerySmall): - if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_VERY_SMALL) - && w->widgets[WIDX_LEFT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - default: + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) + { return; - } -} + } -void WindowRideConstructionKeyboardShortcutTurnRight() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) - { - return; + if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_STRAIGHT); + } + + if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEVEL); + } + + if (!WidgetIsDisabled(*w, WIDX_CHAIN_LIFT) && w->widgets[WIDX_CHAIN_LIFT].type != WindowWidgetType::Empty + && _currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) + { + w->OnMouseDown(WIDX_CHAIN_LIFT); + } + + if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_BANK_STRAIGHT); + } } - switch (_currentTrackCurve) + void WindowRideConstructionKeyboardShortcutSlopeDown() { - case EnumValue(TrackCurve::RightSmall): - if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - break; - case EnumValue(TrackCurve::Right): - if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::RightLarge): - if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::None): - if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::LeftLarge): - if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::Left): - if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::LeftSmall): - if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - case EnumValue(TrackCurve::LeftVerySmall): - if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE_SMALL) - && w->widgets[WIDX_LEFT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_SMALL); - } - else if (!WidgetIsDisabled(*w, WIDX_LEFT_CURVE) && w->widgets[WIDX_LEFT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_LEFT_CURVE_LARGE) - && w->widgets[WIDX_LEFT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEFT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_STRAIGHT); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_LARGE) - && w->widgets[WIDX_RIGHT_CURVE_LARGE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_LARGE); - } - else if (!WidgetIsDisabled(*w, WIDX_RIGHT_CURVE) && w->widgets[WIDX_RIGHT_CURVE].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_SMALL); - } - else if ( - !WidgetIsDisabled(*w, WIDX_RIGHT_CURVE_VERY_SMALL) - && w->widgets[WIDX_RIGHT_CURVE_VERY_SMALL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_RIGHT_CURVE_VERY_SMALL); - } - else - { - return; - } - break; - default: + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) + { return; - } -} + } -void WindowRideConstructionKeyboardShortcutUseTrackDefault() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) - { - return; + switch (_currentTrackPitchEnd) + { + case TrackPitch::Down60: + if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) + && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP + && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP_STEEP); + } + break; + case TrackPitch::Down25: + if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); + } + break; + case TrackPitch::None: + if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN); + } + else if ( + IsTrackEnabled(TRACK_SLOPE_VERTICAL) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE) + { + return; + } + else if ( + !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); + } + else + { + return; + } + break; + case TrackPitch::Up25: + if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEVEL); + } + else if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN); + } + else if ( + !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); + } + else + { + return; + } + break; + case TrackPitch::Up60: + if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP); + } + else if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEVEL); + } + else if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN); + } + else if ( + IsTrackEnabled(TRACK_SLOPE_VERTICAL) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE) + { + return; + } + else if ( + !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); + } + else + { + return; + } + break; + case TrackPitch::Up90: + if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE + && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP_STEEP); + } + break; + default: + return; + } } - if (!WidgetIsDisabled(*w, WIDX_STRAIGHT) && w->widgets[WIDX_STRAIGHT].type != WindowWidgetType::Empty) + void WindowRideConstructionKeyboardShortcutSlopeUp() { - w->OnMouseDown(WIDX_STRAIGHT); + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) + { + return; + } + + switch (_currentTrackPitchEnd) + { + case TrackPitch::Up60: + if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) + && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE + && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); + } + break; + case TrackPitch::Up25: + if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) + && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP_STEEP); + } + break; + case TrackPitch::None: + if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP); + } + else if ( + IsTrackEnabled(TRACK_SLOPE_VERTICAL) + && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP) + { + return; + } + else if ( + !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) + && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP_STEEP); + } + else + { + return; + } + break; + case TrackPitch::Down25: + if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEVEL); + } + else if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP); + } + else if ( + !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) + && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP_STEEP); + } + else + { + return; + } + break; + case TrackPitch::Down60: + if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN); + } + else if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_LEVEL); + } + else if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP); + } + else if ( + IsTrackEnabled(TRACK_SLOPE_VERTICAL) + && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP) + { + return; + } + else if ( + !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) + && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_UP_STEEP); + } + else + { + return; + } + break; + case TrackPitch::Down90: + if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) + && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP + && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); + } + break; + default: + return; + } } - if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) + void WindowRideConstructionKeyboardShortcutChainLiftToggle() { - w->OnMouseDown(WIDX_LEVEL); - } + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_CHAIN_LIFT) + || w->widgets[WIDX_CHAIN_LIFT].type == WindowWidgetType::Empty) + { + return; + } - if (!WidgetIsDisabled(*w, WIDX_CHAIN_LIFT) && w->widgets[WIDX_CHAIN_LIFT].type != WindowWidgetType::Empty - && _currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED) - { w->OnMouseDown(WIDX_CHAIN_LIFT); } - if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty) + void WindowRideConstructionKeyboardShortcutBankLeft() { - w->OnMouseDown(WIDX_BANK_STRAIGHT); - } -} - -void WindowRideConstructionKeyboardShortcutSlopeDown() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) - { - return; - } - - switch (_currentTrackPitchEnd) - { - case TrackPitch::Down60: - if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) - && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP - && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP_STEEP); - } - break; - case TrackPitch::Down25: - if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); - } - break; - case TrackPitch::None: - if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN); - } - else if ( - IsTrackEnabled(TRACK_SLOPE_VERTICAL) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE) - { - return; - } - else if ( - !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); - } - else - { - return; - } - break; - case TrackPitch::Up25: - if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEVEL); - } - else if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN); - } - else if ( - !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); - } - else - { - return; - } - break; - case TrackPitch::Up60: - if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP); - } - else if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEVEL); - } - else if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN); - } - else if ( - IsTrackEnabled(TRACK_SLOPE_VERTICAL) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE) - { - return; - } - else if ( - !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); - } - else - { - return; - } - break; - case TrackPitch::Up90: - if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE - && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP_STEEP); - } - break; - default: - return; - } -} - -void WindowRideConstructionKeyboardShortcutSlopeUp() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_STRAIGHT) || w->widgets[WIDX_STRAIGHT].type == WindowWidgetType::Empty) - { - return; - } - - switch (_currentTrackPitchEnd) - { - case TrackPitch::Up60: - if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) - && w->widgets[WIDX_SLOPE_DOWN_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_RISE - && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); - } - break; - case TrackPitch::Up25: - if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP_STEEP); - } - break; - case TrackPitch::None: - if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP); - } - else if ( - IsTrackEnabled(TRACK_SLOPE_VERTICAL) - && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP) - { - return; - } - else if ( - !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP_STEEP); - } - else - { - return; - } - break; - case TrackPitch::Down25: - if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEVEL); - } - else if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP); - } - else if ( - !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP_STEEP); - } - else - { - return; - } - break; - case TrackPitch::Down60: - if (!WidgetIsDisabled(*w, WIDX_SLOPE_DOWN) && w->widgets[WIDX_SLOPE_DOWN].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN); - } - else if (!WidgetIsDisabled(*w, WIDX_LEVEL) && w->widgets[WIDX_LEVEL].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_LEVEL); - } - else if (!WidgetIsDisabled(*w, WIDX_SLOPE_UP) && w->widgets[WIDX_SLOPE_UP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP); - } - else if ( - IsTrackEnabled(TRACK_SLOPE_VERTICAL) - && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP) - { - return; - } - else if ( - !WidgetIsDisabled(*w, WIDX_SLOPE_UP_STEEP) && w->widgets[WIDX_SLOPE_UP_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_UP_STEEP); - } - else - { - return; - } - break; - case TrackPitch::Down90: - if (IsTrackEnabled(TRACK_SLOPE_VERTICAL) && !WidgetIsDisabled(*w, WIDX_SLOPE_DOWN_STEEP) - && w->widgets[WIDX_SLOPE_UP_STEEP].image.GetIndex() == SPR_RIDE_CONSTRUCTION_VERTICAL_DROP - && w->widgets[WIDX_SLOPE_DOWN_STEEP].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_SLOPE_DOWN_STEEP); - } - break; - default: - return; - } -} - -void WindowRideConstructionKeyboardShortcutChainLiftToggle() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_CHAIN_LIFT) || w->widgets[WIDX_CHAIN_LIFT].type == WindowWidgetType::Empty) - { - return; - } - - w->OnMouseDown(WIDX_CHAIN_LIFT); -} - -void WindowRideConstructionKeyboardShortcutBankLeft() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) - || w->widgets[WIDX_BANK_STRAIGHT].type == WindowWidgetType::Empty) - { - return; - } - - switch (_currentTrackRollEnd) - { - case TrackRoll::None: - if (!WidgetIsDisabled(*w, WIDX_BANK_LEFT) && w->widgets[WIDX_BANK_LEFT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_BANK_LEFT); - } - break; - case TrackRoll::Right: - if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_BANK_STRAIGHT); - } - else if (!WidgetIsDisabled(*w, WIDX_BANK_LEFT) && w->widgets[WIDX_BANK_LEFT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_BANK_LEFT); - } - else - { - return; - } - break; - default: - return; - } -} - -void WindowRideConstructionKeyboardShortcutBankRight() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) - || w->widgets[WIDX_BANK_STRAIGHT].type == WindowWidgetType::Empty) - { - return; - } - - switch (_currentTrackRollEnd) - { - case TrackRoll::None: - if (!WidgetIsDisabled(*w, WIDX_BANK_RIGHT) && w->widgets[WIDX_BANK_RIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_BANK_RIGHT); - } - break; - case TrackRoll::Left: - if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_BANK_STRAIGHT); - } - else if (!WidgetIsDisabled(*w, WIDX_BANK_RIGHT) && w->widgets[WIDX_BANK_RIGHT].type != WindowWidgetType::Empty) - { - w->OnMouseDown(WIDX_BANK_RIGHT); - } - else - { - return; - } - break; - default: - return; - } -} - -void WindowRideConstructionKeyboardShortcutPreviousTrack() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_PREVIOUS_SECTION) - || w->widgets[WIDX_PREVIOUS_SECTION].type == WindowWidgetType::Empty) - { - return; - } - - w->OnMouseUp(WIDX_PREVIOUS_SECTION); -} - -void WindowRideConstructionKeyboardShortcutNextTrack() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_NEXT_SECTION) - || w->widgets[WIDX_NEXT_SECTION].type == WindowWidgetType::Empty) - { - return; - } - - w->OnMouseUp(WIDX_NEXT_SECTION); -} - -void WindowRideConstructionKeyboardShortcutBuildCurrent() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_CONSTRUCT) || w->widgets[WIDX_CONSTRUCT].type == WindowWidgetType::Empty) - { - return; - } - - w->OnMouseUp(WIDX_CONSTRUCT); -} - -void WindowRideConstructionKeyboardShortcutDemolishCurrent() -{ - WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); - if (w == nullptr || WidgetIsDisabled(*w, WIDX_DEMOLISH) || w->widgets[WIDX_DEMOLISH].type == WindowWidgetType::Empty) - { - return; - } - - w->OnMouseUp(WIDX_DEMOLISH); -} - -static void WindowRideConstructionMouseUpDemolishNextPiece(const CoordsXYZD& piecePos, int32_t type) -{ - if (_gotoStartPlacementMode) - { - _currentTrackBegin.z = Floor2(piecePos.z, COORDS_Z_STEP); - _rideConstructionState = RideConstructionState::Front; - _currentTrackSelectionFlags = 0; - _currentTrackPieceDirection = piecePos.direction & 3; - auto savedCurrentTrackCurve = _currentTrackCurve; - auto savedPreviousTrackPitchEnd = _previousTrackPitchEnd; - auto savedCurrentTrackPitchEnd = _currentTrackPitchEnd; - auto savedPreviousTrackRollEnd = _previousTrackRollEnd; - auto savedCurrentTrackRollEnd = _currentTrackRollEnd; - int32_t savedCurrentTrackAlternative = _currentTrackAlternative; - int32_t savedCurrentTrackLiftHill = _currentTrackLiftHill; - RideConstructionSetDefaultNextPiece(); - WindowRideConstructionUpdateActiveElements(); - auto ride = GetRide(_currentRideIndex); - if (!RideTryGetOriginElement(*ride, nullptr)) + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) + || w->widgets[WIDX_BANK_STRAIGHT].type == WindowWidgetType::Empty) { - RideInitialiseConstructionWindow(*ride); + return; + } + + switch (_currentTrackRollEnd) + { + case TrackRoll::None: + if (!WidgetIsDisabled(*w, WIDX_BANK_LEFT) && w->widgets[WIDX_BANK_LEFT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_BANK_LEFT); + } + break; + case TrackRoll::Right: + if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_BANK_STRAIGHT); + } + else if (!WidgetIsDisabled(*w, WIDX_BANK_LEFT) && w->widgets[WIDX_BANK_LEFT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_BANK_LEFT); + } + else + { + return; + } + break; + default: + return; + } + } + + void WindowRideConstructionKeyboardShortcutBankRight() + { + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) + || w->widgets[WIDX_BANK_STRAIGHT].type == WindowWidgetType::Empty) + { + return; + } + + switch (_currentTrackRollEnd) + { + case TrackRoll::None: + if (!WidgetIsDisabled(*w, WIDX_BANK_RIGHT) && w->widgets[WIDX_BANK_RIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_BANK_RIGHT); + } + break; + case TrackRoll::Left: + if (!WidgetIsDisabled(*w, WIDX_BANK_STRAIGHT) && w->widgets[WIDX_BANK_STRAIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_BANK_STRAIGHT); + } + else if (!WidgetIsDisabled(*w, WIDX_BANK_RIGHT) && w->widgets[WIDX_BANK_RIGHT].type != WindowWidgetType::Empty) + { + w->OnMouseDown(WIDX_BANK_RIGHT); + } + else + { + return; + } + break; + default: + return; + } + } + + void WindowRideConstructionKeyboardShortcutPreviousTrack() + { + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_PREVIOUS_SECTION) + || w->widgets[WIDX_PREVIOUS_SECTION].type == WindowWidgetType::Empty) + { + return; + } + + w->OnMouseUp(WIDX_PREVIOUS_SECTION); + } + + void WindowRideConstructionKeyboardShortcutNextTrack() + { + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_NEXT_SECTION) + || w->widgets[WIDX_NEXT_SECTION].type == WindowWidgetType::Empty) + { + return; + } + + w->OnMouseUp(WIDX_NEXT_SECTION); + } + + void WindowRideConstructionKeyboardShortcutBuildCurrent() + { + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_CONSTRUCT) || w->widgets[WIDX_CONSTRUCT].type == WindowWidgetType::Empty) + { + return; + } + + w->OnMouseUp(WIDX_CONSTRUCT); + } + + void WindowRideConstructionKeyboardShortcutDemolishCurrent() + { + WindowBase* w = WindowFindByClass(WindowClass::RideConstruction); + if (w == nullptr || WidgetIsDisabled(*w, WIDX_DEMOLISH) || w->widgets[WIDX_DEMOLISH].type == WindowWidgetType::Empty) + { + return; + } + + w->OnMouseUp(WIDX_DEMOLISH); + } + + static void WindowRideConstructionMouseUpDemolishNextPiece(const CoordsXYZD& piecePos, int32_t type) + { + if (_gotoStartPlacementMode) + { + _currentTrackBegin.z = Floor2(piecePos.z, COORDS_Z_STEP); + _rideConstructionState = RideConstructionState::Front; + _currentTrackSelectionFlags = 0; _currentTrackPieceDirection = piecePos.direction & 3; - if (!(savedCurrentTrackCurve & RideConstructionSpecialPieceSelected)) + auto savedCurrentTrackCurve = _currentTrackCurve; + auto savedPreviousTrackPitchEnd = _previousTrackPitchEnd; + auto savedCurrentTrackPitchEnd = _currentTrackPitchEnd; + auto savedPreviousTrackRollEnd = _previousTrackRollEnd; + auto savedCurrentTrackRollEnd = _currentTrackRollEnd; + int32_t savedCurrentTrackAlternative = _currentTrackAlternative; + int32_t savedCurrentTrackLiftHill = _currentTrackLiftHill; + RideConstructionSetDefaultNextPiece(); + WindowRideConstructionUpdateActiveElements(); + auto ride = GetRide(_currentRideIndex); + if (!RideTryGetOriginElement(*ride, nullptr)) { - _currentTrackCurve = savedCurrentTrackCurve; - _previousTrackPitchEnd = savedPreviousTrackPitchEnd; - _currentTrackPitchEnd = savedCurrentTrackPitchEnd; - _previousTrackRollEnd = savedPreviousTrackRollEnd; - _currentTrackRollEnd = savedCurrentTrackRollEnd; - _currentTrackAlternative = savedCurrentTrackAlternative; - _currentTrackLiftHill = savedCurrentTrackLiftHill; - WindowRideConstructionUpdateActiveElements(); + RideInitialiseConstructionWindow(*ride); + _currentTrackPieceDirection = piecePos.direction & 3; + if (!(savedCurrentTrackCurve & RideConstructionSpecialPieceSelected)) + { + _currentTrackCurve = savedCurrentTrackCurve; + _previousTrackPitchEnd = savedPreviousTrackPitchEnd; + _currentTrackPitchEnd = savedCurrentTrackPitchEnd; + _previousTrackRollEnd = savedPreviousTrackRollEnd; + _currentTrackRollEnd = savedCurrentTrackRollEnd; + _currentTrackAlternative = savedCurrentTrackAlternative; + _currentTrackLiftHill = savedCurrentTrackLiftHill; + WindowRideConstructionUpdateActiveElements(); + } } } + else + { + if (_rideConstructionState2 == RideConstructionState::Selected + || _rideConstructionState2 == RideConstructionState::Front) + { + if (type == TrackElemType::MiddleStation || type == TrackElemType::BeginStation) + { + type = TrackElemType::EndStation; + } + } + if (_rideConstructionState2 == RideConstructionState::Back) + { + if (type == TrackElemType::MiddleStation) + { + type = TrackElemType::BeginStation; + } + } + if (NetworkGetMode() == NETWORK_MODE_CLIENT) + { + // rideConstructionState needs to be set again to the proper value, this only affects the client + _rideConstructionState = RideConstructionState::Selected; + } + _currentTrackBegin = piecePos; + _currentTrackPieceDirection = piecePos.direction; + _currentTrackPieceType = type; + _currentTrackSelectionFlags = 0; + if (_rideConstructionState2 == RideConstructionState::Front) + { + RideSelectNextSection(); + } + else if (_rideConstructionState2 == RideConstructionState::Back) + { + RideSelectPreviousSection(); + } + WindowRideConstructionUpdateActiveElements(); + } } - else - { - if (_rideConstructionState2 == RideConstructionState::Selected - || _rideConstructionState2 == RideConstructionState::Front) - { - if (type == TrackElemType::MiddleStation || type == TrackElemType::BeginStation) - { - type = TrackElemType::EndStation; - } - } - if (_rideConstructionState2 == RideConstructionState::Back) - { - if (type == TrackElemType::MiddleStation) - { - type = TrackElemType::BeginStation; - } - } - if (NetworkGetMode() == NETWORK_MODE_CLIENT) - { - // rideConstructionState needs to be set again to the proper value, this only affects the client - _rideConstructionState = RideConstructionState::Selected; - } - _currentTrackBegin = piecePos; - _currentTrackPieceDirection = piecePos.direction; - _currentTrackPieceType = type; - _currentTrackSelectionFlags = 0; - if (_rideConstructionState2 == RideConstructionState::Front) - { - RideSelectNextSection(); - } - else if (_rideConstructionState2 == RideConstructionState::Back) - { - RideSelectPreviousSection(); - } - WindowRideConstructionUpdateActiveElements(); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/RideList.cpp b/src/openrct2-ui/windows/RideList.cpp index 2d7692e76d..3a4426c6f0 100644 --- a/src/openrct2-ui/windows/RideList.cpp +++ b/src/openrct2-ui/windows/RideList.cpp @@ -29,40 +29,40 @@ #include #include -using namespace OpenRCT2; - -static constexpr StringId WINDOW_TITLE = STR_NONE; -static constexpr int32_t WH = 240; -static constexpr int32_t WW = 340; - -enum +namespace OpenRCT2::Ui::Windows { - PAGE_RIDES, - PAGE_SHOPS_AND_STALLS, - PAGE_KIOSKS_AND_FACILITIES, - PAGE_COUNT -}; + static constexpr StringId WINDOW_TITLE = STR_NONE; + static constexpr int32_t WH = 240; + static constexpr int32_t WW = 340; -enum WindowRideListWidgetIdx -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PAGE_BACKGROUND, - WIDX_OPEN_CLOSE_ALL, - WIDX_CURRENT_INFORMATION_TYPE, - WIDX_INFORMATION_TYPE_DROPDOWN, - WIDX_SORT, - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_TAB_3, - WIDX_LIST, - WIDX_CLOSE_LIGHT, - WIDX_OPEN_LIGHT, - WIDX_QUICK_DEMOLISH, -}; + enum + { + PAGE_RIDES, + PAGE_SHOPS_AND_STALLS, + PAGE_KIOSKS_AND_FACILITIES, + PAGE_COUNT + }; -// clang-format off + enum WindowRideListWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PAGE_BACKGROUND, + WIDX_OPEN_CLOSE_ALL, + WIDX_CURRENT_INFORMATION_TYPE, + WIDX_INFORMATION_TYPE_DROPDOWN, + WIDX_SORT, + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_TAB_3, + WIDX_LIST, + WIDX_CLOSE_LIGHT, + WIDX_OPEN_LIGHT, + WIDX_QUICK_DEMOLISH, + }; + + // clang-format off static Widget _rideListWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget({ 0, 43}, {340, 197}, WindowWidgetType::Resize, WindowColour::Secondary ), // tab page background @@ -79,894 +79,901 @@ static Widget _rideListWidgets[] = { MakeWidget({315, 90}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_DEMOLISH), STR_QUICK_DEMOLISH_RIDE ), kWidgetsEnd, }; -// clang-format on + // clang-format on -enum -{ - INFORMATION_TYPE_STATUS, - INFORMATION_TYPE_POPULARITY, - INFORMATION_TYPE_SATISFACTION, - INFORMATION_TYPE_PROFIT, - INFORMATION_TYPE_TOTAL_CUSTOMERS, - INFORMATION_TYPE_TOTAL_PROFIT, - INFORMATION_TYPE_CUSTOMERS, - INFORMATION_TYPE_AGE, - INFORMATION_TYPE_INCOME, - INFORMATION_TYPE_RUNNING_COST, - INFORMATION_TYPE_QUEUE_LENGTH, - INFORMATION_TYPE_QUEUE_TIME, - INFORMATION_TYPE_RELIABILITY, - INFORMATION_TYPE_DOWN_TIME, - INFORMATION_TYPE_GUESTS_FAVOURITE, - INFORMATION_TYPE_EXCITEMENT, - INFORMATION_TYPE_INTENSITY, - INFORMATION_TYPE_NAUSEA, - DROPDOWN_LIST_COUNT, -}; - -static constexpr StringId ride_info_type_string_mapping[DROPDOWN_LIST_COUNT] = { - STR_STATUS, - STR_POPULARITY, - STR_SATISFACTION, - STR_PROFIT, - STR_RIDE_LIST_TOTAL_CUSTOMERS, - STR_RIDE_LIST_TOTAL_PROFIT, - STR_RIDE_LIST_CUSTOMERS_PER_HOUR, - STR_RIDE_LIST_AGE, - STR_RIDE_LIST_INCOME, - STR_RIDE_LIST_RUNNING_COST, - STR_QUEUE_LENGTH, - STR_QUEUE_TIME, - STR_RELIABILITY, - STR_DOWN_TIME, - STR_GUESTS_FAVOURITE, - STR_RIDE_LIST_EXCITEMENT, - STR_RIDE_LIST_INTENSITY, - STR_RIDE_LIST_NAUSEA, -}; - -static constexpr StringId ride_list_statusbar_count_strings[PAGE_COUNT] = { - STR_NUMBER_RIDES, - STR_NUMBER_SHOPS_AND_STALLS, - STR_NUMBER_TOILETS_AND_INFORMATION_KIOSKS, -}; - -static constexpr bool ride_info_type_money_mapping[DROPDOWN_LIST_COUNT] = { - false, // Status - false, // Popularity - false, // Satisfaction - true, // Profit - false, // Total customers - true, // Total profit - false, // Customers - false, // Age - true, // Income - true, // Running_cost - false, // Queue length - false, // Queue time - false, // Reliability - false, // Down time - false, // Guests favourite - false, // Excitement - false, // Intensity - false, // Nausea -}; - -static constexpr StringId page_names[] = { - STR_RIDES, - STR_SHOPS_AND_STALLS, - STR_TOILETS_AND_INFORMATION_KIOSKS, -}; - -class RideListWindow final : public Window -{ -private: - bool _quickDemolishMode = false; - int32_t _windowRideListInformationType = INFORMATION_TYPE_STATUS; - - struct RideListEntry + enum { - RideId Id; - u8string Name; + INFORMATION_TYPE_STATUS, + INFORMATION_TYPE_POPULARITY, + INFORMATION_TYPE_SATISFACTION, + INFORMATION_TYPE_PROFIT, + INFORMATION_TYPE_TOTAL_CUSTOMERS, + INFORMATION_TYPE_TOTAL_PROFIT, + INFORMATION_TYPE_CUSTOMERS, + INFORMATION_TYPE_AGE, + INFORMATION_TYPE_INCOME, + INFORMATION_TYPE_RUNNING_COST, + INFORMATION_TYPE_QUEUE_LENGTH, + INFORMATION_TYPE_QUEUE_TIME, + INFORMATION_TYPE_RELIABILITY, + INFORMATION_TYPE_DOWN_TIME, + INFORMATION_TYPE_GUESTS_FAVOURITE, + INFORMATION_TYPE_EXCITEMENT, + INFORMATION_TYPE_INTENSITY, + INFORMATION_TYPE_NAUSEA, + DROPDOWN_LIST_COUNT, }; - std::vector _rideList; -public: - void OnOpen() override + static constexpr StringId ride_info_type_string_mapping[DROPDOWN_LIST_COUNT] = { + STR_STATUS, + STR_POPULARITY, + STR_SATISFACTION, + STR_PROFIT, + STR_RIDE_LIST_TOTAL_CUSTOMERS, + STR_RIDE_LIST_TOTAL_PROFIT, + STR_RIDE_LIST_CUSTOMERS_PER_HOUR, + STR_RIDE_LIST_AGE, + STR_RIDE_LIST_INCOME, + STR_RIDE_LIST_RUNNING_COST, + STR_QUEUE_LENGTH, + STR_QUEUE_TIME, + STR_RELIABILITY, + STR_DOWN_TIME, + STR_GUESTS_FAVOURITE, + STR_RIDE_LIST_EXCITEMENT, + STR_RIDE_LIST_INTENSITY, + STR_RIDE_LIST_NAUSEA, + }; + + static constexpr StringId ride_list_statusbar_count_strings[PAGE_COUNT] = { + STR_NUMBER_RIDES, + STR_NUMBER_SHOPS_AND_STALLS, + STR_NUMBER_TOILETS_AND_INFORMATION_KIOSKS, + }; + + static constexpr bool ride_info_type_money_mapping[DROPDOWN_LIST_COUNT] = { + false, // Status + false, // Popularity + false, // Satisfaction + true, // Profit + false, // Total customers + true, // Total profit + false, // Customers + false, // Age + true, // Income + true, // Running_cost + false, // Queue length + false, // Queue time + false, // Reliability + false, // Down time + false, // Guests favourite + false, // Excitement + false, // Intensity + false, // Nausea + }; + + static constexpr StringId page_names[] = { + STR_RIDES, + STR_SHOPS_AND_STALLS, + STR_TOILETS_AND_INFORMATION_KIOSKS, + }; + + class RideListWindow final : public Window { - widgets = _rideListWidgets; - WindowInitScrollWidgets(*this); - page = PAGE_RIDES; - selected_list_item = -1; - frame_no = 0; - min_width = 340; - min_height = 240; - max_width = 400; - max_height = 700; - RefreshList(); + private: + bool _quickDemolishMode = false; + int32_t _windowRideListInformationType = INFORMATION_TYPE_STATUS; - list_information_type = 0; - - _windowRideListInformationType = INFORMATION_TYPE_STATUS; - _quickDemolishMode = false; - } - - /** - * - * rct2: 0x006B38A7 - */ - void OnResize() override - { - if (width < min_width) + struct RideListEntry { - Invalidate(); - width = min_width; - } - if (height < min_height) + RideId Id; + u8string Name; + }; + std::vector _rideList; + + public: + void OnOpen() override { - Invalidate(); - height = min_height; + widgets = _rideListWidgets; + WindowInitScrollWidgets(*this); + page = PAGE_RIDES; + selected_list_item = -1; + frame_no = 0; + min_width = 340; + min_height = 240; + max_width = 400; + max_height = 700; + RefreshList(); + + list_information_type = 0; + + _windowRideListInformationType = INFORMATION_TYPE_STATUS; + _quickDemolishMode = false; } - widgets[WIDX_SORT].left = width - 60; - widgets[WIDX_SORT].right = width - 60 + 54; + /** + * + * rct2: 0x006B38A7 + */ + void OnResize() override + { + if (width < min_width) + { + Invalidate(); + width = min_width; + } + if (height < min_height) + { + Invalidate(); + height = min_height; + } - ResizeDropdown(WIDX_CURRENT_INFORMATION_TYPE, { 150, 46 }, { width - 216, DROPDOWN_HEIGHT }); + widgets[WIDX_SORT].left = width - 60; + widgets[WIDX_SORT].right = width - 60 + 54; - // Refreshing the list can be a very intensive operation - // owing to its use of ride_has_any_track_elements(). - // This makes sure it's only refreshed every 64 ticks. - if (!(gCurrentRealTimeTicks & 0x3f)) + ResizeDropdown(WIDX_CURRENT_INFORMATION_TYPE, { 150, 46 }, { width - 216, DROPDOWN_HEIGHT }); + + // Refreshing the list can be a very intensive operation + // owing to its use of ride_has_any_track_elements(). + // This makes sure it's only refreshed every 64 ticks. + if (!(gCurrentRealTimeTicks & 0x3f)) + { + RefreshList(); + } + } + + /** + * + * rct2: 0x006B3511 + */ + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*this); + break; + case WIDX_SORT: + list_information_type = _windowRideListInformationType; + selected_list_item = -1; + RefreshList(); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + if (page != widgetIndex - WIDX_TAB_1) + { + page = widgetIndex - WIDX_TAB_1; + frame_no = 0; + selected_list_item = -1; + if (page != PAGE_RIDES && _windowRideListInformationType > INFORMATION_TYPE_RUNNING_COST) + { + _windowRideListInformationType = INFORMATION_TYPE_STATUS; + } + RefreshList(); + } + break; + case WIDX_CLOSE_LIGHT: + CloseAllRides(); + break; + case WIDX_OPEN_LIGHT: + OpenAllRides(); + break; + case WIDX_QUICK_DEMOLISH: + if (NetworkGetMode() != NETWORK_MODE_CLIENT) + { + _quickDemolishMode = !_quickDemolishMode; + } + else + { + _quickDemolishMode = false; + } + Invalidate(); + break; + } + } + + /** + * + * rct2: 0x006B3532 + */ + void OnMouseDown(WidgetIndex widgetIndex) override + { + if (widgetIndex == WIDX_OPEN_CLOSE_ALL) + { + const auto& widget = widgets[widgetIndex]; + gDropdownItems[0].Format = STR_CLOSE_ALL; + gDropdownItems[1].Format = STR_OPEN_ALL; + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height(), colours[1], 0, 2); + } + else if (widgetIndex == WIDX_INFORMATION_TYPE_DROPDOWN) + { + const auto& widget = widgets[widgetIndex - 1]; + + int32_t lastType; + if (page == PAGE_RIDES) + lastType = INFORMATION_TYPE_NAUSEA; + else + lastType = INFORMATION_TYPE_RUNNING_COST; + + int32_t numItems = 0; + int32_t selectedIndex = -1; + for (int32_t type = INFORMATION_TYPE_STATUS; type <= lastType; type++) + { + if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + if (ride_info_type_money_mapping[type]) + { + continue; + } + } + + if (type == _windowRideListInformationType) + { + selectedIndex = numItems; + } + + gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[numItems].Args = ride_info_type_string_mapping[type]; + numItems++; + } + + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height(), colours[1], 0, + Dropdown::Flag::StayOpen, numItems, widget.width() - 3); + if (selectedIndex != -1) + { + Dropdown::SetChecked(selectedIndex, true); + } + } + } + + /** + * + * rct2: 0x006B3547 + */ + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (widgetIndex == WIDX_OPEN_CLOSE_ALL) + { + if (dropdownIndex == 0) + { + CloseAllRides(); + } + else if (dropdownIndex == 1) + { + OpenAllRides(); + } + + Invalidate(); + } + else if (widgetIndex == WIDX_INFORMATION_TYPE_DROPDOWN) + { + if (dropdownIndex == -1) + return; + + int32_t informationType = INFORMATION_TYPE_STATUS; + uint32_t arg = static_cast(gDropdownItems[dropdownIndex].Args); + for (size_t i = 0; i < std::size(ride_info_type_string_mapping); i++) + { + if (arg == ride_info_type_string_mapping[i]) + { + informationType = static_cast(i); + } + } + + _windowRideListInformationType = informationType; + Invalidate(); + } + } + + /** + * + * rct2: 0x006B386B + */ + void OnUpdate() override + { + frame_no = (frame_no + 1) % 64; + WidgetInvalidate(*this, WIDX_TAB_1 + page); + if (_windowRideListInformationType != INFORMATION_TYPE_STATUS) + Invalidate(); + } + + /** + * + * rct2: 0x006B35A1 + */ + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + const auto newHeight = static_cast(_rideList.size() * SCROLLABLE_ROW_HEIGHT); + if (selected_list_item != -1) + { + selected_list_item = -1; + Invalidate(); + } + + auto top = newHeight - widgets[WIDX_LIST].bottom + widgets[WIDX_LIST].top + 21; + if (top < 0) + top = 0; + if (top < scrolls[0].v_top) + { + scrolls[0].v_top = top; + Invalidate(); + } + + return { 0, newHeight }; + } + + /** + * + * rct2: 0x006B361F + */ + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + const auto index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index < 0 || static_cast(index) >= _rideList.size()) + return; + + // Open ride window + const auto selectedRideId = _rideList[index].Id; + if (_quickDemolishMode && NetworkGetMode() != NETWORK_MODE_CLIENT) + { + auto gameAction = RideDemolishAction(selectedRideId, RIDE_MODIFY_DEMOLISH); + GameActions::Execute(&gameAction); + RefreshList(); + } + else + { + auto intent = Intent(WindowClass::Ride); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, selectedRideId.ToUnderlying()); + ContextOpenIntent(&intent); + } + } + + /** + * + * rct2: 0x006B35EF + */ + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + const auto index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index < 0 || static_cast(index) >= _rideList.size()) + return; + + selected_list_item = index; + Invalidate(); + } + + /** + * + * rct2: 0x006B3182 + */ + void OnPrepareDraw() override + { + widgets[WIDX_CURRENT_INFORMATION_TYPE].text = ride_info_type_string_mapping[_windowRideListInformationType]; + + // Set correct active tab + for (int32_t i = 0; i < 3; i++) + pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); + pressed_widgets |= 1LL << (WIDX_TAB_1 + page); + + widgets[WIDX_TITLE].text = page_names[page]; + + if (_quickDemolishMode) + pressed_widgets |= (1uLL << WIDX_QUICK_DEMOLISH); + else + pressed_widgets &= ~(1uLL << WIDX_QUICK_DEMOLISH); + + ResizeFrameWithPage(); + + widgets[WIDX_LIST].right = width - 26; + widgets[WIDX_LIST].bottom = height - 15; + widgets[WIDX_OPEN_CLOSE_ALL].right = width - 2; + widgets[WIDX_OPEN_CLOSE_ALL].left = width - 25; + widgets[WIDX_CLOSE_LIGHT].right = width - 7; + widgets[WIDX_CLOSE_LIGHT].left = width - 20; + widgets[WIDX_OPEN_LIGHT].right = width - 7; + widgets[WIDX_OPEN_LIGHT].left = width - 20; + widgets[WIDX_QUICK_DEMOLISH].right = width - 2; + widgets[WIDX_QUICK_DEMOLISH].left = width - 25; + + if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) + { + widgets[WIDX_OPEN_CLOSE_ALL].type = WindowWidgetType::Empty; + widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; + widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; + + const auto& rideManager = GetRideManager(); + auto allClosed = true; + auto allOpen = false; + if (_rideList.size() > 0 && std::size(rideManager) != 0) + { + auto c = static_cast(page); + allClosed = std::none_of(rideManager.begin(), rideManager.end(), [c](const Ride& rideRef) { + return rideRef.GetClassification() == c && rideRef.status == RideStatus::Open; + }); + allOpen = std::none_of(rideManager.begin(), rideManager.end(), [c](const Ride& rideRef) { + return rideRef.GetClassification() == c && rideRef.status != RideStatus::Open; + }); + } + + const auto closeLightImage( + SPR_G2_RCT1_CLOSE_BUTTON_0 + (allClosed ? 1 : 0) * 2 + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT)); + widgets[WIDX_CLOSE_LIGHT].image = ImageId(closeLightImage); + const auto openLightImage = SPR_G2_RCT1_OPEN_BUTTON_0 + (allOpen ? 1 : 0) * 2 + + WidgetIsPressed(*this, WIDX_OPEN_LIGHT); + widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); + widgets[WIDX_QUICK_DEMOLISH].top = widgets[WIDX_OPEN_LIGHT].bottom + 3; + } + else + { + widgets[WIDX_OPEN_CLOSE_ALL].type = WindowWidgetType::FlatBtn; + widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; + widgets[WIDX_QUICK_DEMOLISH].top = widgets[WIDX_OPEN_CLOSE_ALL].bottom + 3; + } + widgets[WIDX_QUICK_DEMOLISH].bottom = widgets[WIDX_QUICK_DEMOLISH].top + 23; + widgets[WIDX_QUICK_DEMOLISH].type = NetworkGetMode() != NETWORK_MODE_CLIENT ? WindowWidgetType::FlatBtn + : WindowWidgetType::Empty; + } + + /** + * + * rct2: 0x006B3235 + */ + void OnDraw(DrawPixelInfo& dpi) override + { + WindowDrawWidgets(*this, dpi); + DrawTabImages(dpi); + + // Draw number of attractions on bottom + auto ft = Formatter(); + ft.Add(static_cast(_rideList.size())); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_LIST].bottom + 2 }, ride_list_statusbar_count_strings[page], + ft); + } + + /** + * + * rct2: 0x006B3240 + */ + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; + GfxFillRect( + dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width, dpi.height } }, ColourMapA[colours[1]].mid_light); + + auto y = 0; + for (size_t i = 0; i < _rideList.size(); i++) + { + StringId format = (_quickDemolishMode ? STR_RED_STRINGID : STR_BLACK_STRING); + if (i == static_cast(selected_list_item)) + { + // Background highlight + GfxFilterRect(dpi, { 0, y, 800, y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); + format = (_quickDemolishMode ? STR_LIGHTPINK_STRINGID : STR_WINDOW_COLOUR_2_STRINGID); + } + + // Get ride + const auto* ridePtr = GetRide(_rideList[i].Id); + if (ridePtr == nullptr) + continue; + + // Ride name + auto ft = Formatter(); + ridePtr->FormatNameTo(ft); + DrawTextEllipsised(dpi, { 0, y - 1 }, 159, format, ft); + + // Ride information + ft = Formatter(); + ft.Increment(2); + auto formatSecondaryEnabled = true; + StringId formatSecondary = 0; + switch (_windowRideListInformationType) + { + case INFORMATION_TYPE_STATUS: + formatSecondaryEnabled = false; + ft.Rewind(); + ridePtr->FormatStatusTo(ft); + + // Make test red and bold if broken down or crashed + if ((ridePtr->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + || (ridePtr->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) + { + format = STR_RED_OUTLINED_STRING; + } + break; + case INFORMATION_TYPE_POPULARITY: + formatSecondary = STR_POPULARITY_UNKNOWN_LABEL; + if (ridePtr->popularity != 255) + { + formatSecondary = STR_POPULARITY_LABEL; + ft.Add(ridePtr->popularity * 4); + } + break; + case INFORMATION_TYPE_SATISFACTION: + formatSecondary = STR_SATISFACTION_UNKNOWN_LABEL; + if (ridePtr->satisfaction != 255) + { + formatSecondary = STR_SATISFACTION_LABEL; + ft.Add(ridePtr->satisfaction * 5); + } + break; + case INFORMATION_TYPE_PROFIT: + formatSecondary = 0; + if (ridePtr->profit != kMoney64Undefined) + { + formatSecondary = STR_PROFIT_LABEL; + ft.Add(ridePtr->profit); + } + break; + case INFORMATION_TYPE_TOTAL_CUSTOMERS: + formatSecondary = STR_RIDE_LIST_TOTAL_CUSTOMERS_LABEL; + ft.Add(ridePtr->total_customers); + break; + case INFORMATION_TYPE_TOTAL_PROFIT: + formatSecondary = 0; + if (ridePtr->total_profit != kMoney64Undefined) + { + formatSecondary = STR_RIDE_LIST_TOTAL_PROFIT_LABEL; + ft.Add(ridePtr->total_profit); + } + break; + case INFORMATION_TYPE_CUSTOMERS: + formatSecondary = STR_RIDE_LIST_CUSTOMERS_PER_HOUR_LABEL; + ft.Add(RideCustomersPerHour(*ridePtr)); + break; + case INFORMATION_TYPE_AGE: + { + const auto age = DateGetYear(ridePtr->GetAge()); + switch (age) + { + case 0: + formatSecondary = STR_RIDE_LIST_BUILT_THIS_YEAR_LABEL; + break; + case 1: + formatSecondary = STR_RIDE_LIST_BUILT_LAST_YEAR_LABEL; + break; + default: + formatSecondary = STR_RIDE_LIST_BUILT_X_YEARS_AGO_LABEL; + break; + } + ft.Add(age); + break; + } + case INFORMATION_TYPE_INCOME: + formatSecondary = 0; + if (ridePtr->income_per_hour != kMoney64Undefined) + { + formatSecondary = STR_RIDE_LIST_INCOME_LABEL; + ft.Add(ridePtr->income_per_hour); + } + break; + case INFORMATION_TYPE_RUNNING_COST: + formatSecondary = STR_RIDE_LIST_RUNNING_COST_UNKNOWN; + if (ridePtr->upkeep_cost != kMoney64Undefined) + { + formatSecondary = STR_RIDE_LIST_RUNNING_COST_LABEL; + ft.Add(ridePtr->upkeep_cost * 16); + } + break; + case INFORMATION_TYPE_QUEUE_LENGTH: + { + const auto queueLength = ridePtr->GetTotalQueueLength(); + ft.Add(queueLength); + + if (queueLength == 1) + { + formatSecondary = STR_QUEUE_ONE_PERSON; + } + else if (queueLength > 1) + { + formatSecondary = STR_QUEUE_PEOPLE; + } + else + { + formatSecondary = STR_QUEUE_EMPTY; + } + break; + } + case INFORMATION_TYPE_QUEUE_TIME: + { + const auto maxQueueTime = ridePtr->GetMaxQueueTime(); + ft.Add(maxQueueTime); + + if (maxQueueTime > 1) + { + formatSecondary = STR_QUEUE_TIME_PLURAL_LABEL; + } + else + { + formatSecondary = STR_QUEUE_TIME_LABEL; + } + break; + } + case INFORMATION_TYPE_RELIABILITY: + ft.Add(ridePtr->reliability_percentage); + formatSecondary = STR_RELIABILITY_LABEL; + break; + case INFORMATION_TYPE_DOWN_TIME: + ft.Add(ridePtr->downtime); + formatSecondary = STR_DOWN_TIME_LABEL; + break; + case INFORMATION_TYPE_GUESTS_FAVOURITE: + formatSecondary = 0; + if (ridePtr->IsRide()) + { + ft.Add(ridePtr->guests_favourite); + formatSecondary = ridePtr->guests_favourite == 1 ? STR_GUESTS_FAVOURITE_LABEL + : STR_GUESTS_FAVOURITE_PLURAL_LABEL; + } + break; + case INFORMATION_TYPE_EXCITEMENT: + formatSecondary = STR_RATING_UKNOWN_LABEL; + if (RideHasRatings(*ridePtr)) + { + formatSecondary = STR_EXCITEMENT_LABEL; + ft.Add(ridePtr->excitement); + } + break; + case INFORMATION_TYPE_INTENSITY: + formatSecondary = STR_RATING_UKNOWN_LABEL; + if (RideHasRatings(*ridePtr)) + { + formatSecondary = STR_INTENSITY_LABEL; + ft.Add(ridePtr->intensity); + } + break; + case INFORMATION_TYPE_NAUSEA: + formatSecondary = STR_RATING_UKNOWN_LABEL; + if (RideHasRatings(*ridePtr)) + { + formatSecondary = STR_NAUSEA_LABEL; + ft.Add(ridePtr->nausea); + } + break; + } + + if (formatSecondaryEnabled) + { + ft.Rewind(); + ft.Add(formatSecondary); + } + DrawTextEllipsised(dpi, { 160, y - 1 }, 157, format, ft); + y += SCROLLABLE_ROW_HEIGHT; + } + } + + void RefreshListWrapper() { RefreshList(); } - } - /** - * - * rct2: 0x006B3511 - */ - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + private: + /** + * + * rct2: 0x006B38EA + */ + void DrawTabImages(DrawPixelInfo& dpi) { - case WIDX_CLOSE: - WindowClose(*this); - break; - case WIDX_SORT: - list_information_type = _windowRideListInformationType; - selected_list_item = -1; - RefreshList(); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - if (page != widgetIndex - WIDX_TAB_1) - { - page = widgetIndex - WIDX_TAB_1; - frame_no = 0; - selected_list_item = -1; - if (page != PAGE_RIDES && _windowRideListInformationType > INFORMATION_TYPE_RUNNING_COST) - { - _windowRideListInformationType = INFORMATION_TYPE_STATUS; - } - RefreshList(); - } - break; - case WIDX_CLOSE_LIGHT: - CloseAllRides(); - break; - case WIDX_OPEN_LIGHT: - OpenAllRides(); - break; - case WIDX_QUICK_DEMOLISH: - if (NetworkGetMode() != NETWORK_MODE_CLIENT) - { - _quickDemolishMode = !_quickDemolishMode; - } - else - { - _quickDemolishMode = false; - } - Invalidate(); - break; - } - } + int32_t sprite_idx; - /** - * - * rct2: 0x006B3532 - */ - void OnMouseDown(WidgetIndex widgetIndex) override - { - if (widgetIndex == WIDX_OPEN_CLOSE_ALL) - { - const auto& widget = widgets[widgetIndex]; - gDropdownItems[0].Format = STR_CLOSE_ALL; - gDropdownItems[1].Format = STR_OPEN_ALL; - WindowDropdownShowText({ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height(), colours[1], 0, 2); - } - else if (widgetIndex == WIDX_INFORMATION_TYPE_DROPDOWN) - { - const auto& widget = widgets[widgetIndex - 1]; - - int32_t lastType; + // Rides tab + sprite_idx = SPR_TAB_RIDE_0; if (page == PAGE_RIDES) - lastType = INFORMATION_TYPE_NAUSEA; - else - lastType = INFORMATION_TYPE_RUNNING_COST; + sprite_idx += frame_no / 4; + GfxDrawSprite( + dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }); - int32_t numItems = 0; - int32_t selectedIndex = -1; - for (int32_t type = INFORMATION_TYPE_STATUS; type <= lastType; type++) - { - if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + // Shops and stalls tab + sprite_idx = SPR_TAB_SHOPS_AND_STALLS_0; + if (page == PAGE_SHOPS_AND_STALLS) + sprite_idx += frame_no / 4; + GfxDrawSprite( + dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); + + // Information kiosks and facilities tab + sprite_idx = SPR_TAB_KIOSKS_AND_FACILITIES_0; + if (page == PAGE_KIOSKS_AND_FACILITIES) + sprite_idx += (frame_no / 4) % 8; + GfxDrawSprite( + dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].left, widgets[WIDX_TAB_3].top }); + } + + /** + * Used in RefreshList() to handle the sorting of the list. + * Uses a lambda function (predicate) as exit criteria for the algorithm. + */ + template void SortListByPredicate(const TSortPred& pred) + { + std::sort(_rideList.begin(), _rideList.end(), [&pred](const auto& lhs, const auto& rhs) { + const Ride* rideLhs = GetRide(lhs.Id); + const Ride* rideRhs = GetRide(rhs.Id); + if (rideLhs == nullptr || rideRhs == nullptr) { - if (ride_info_type_money_mapping[type]) - { - continue; - } + return false; + } + return !pred(*rideLhs, *rideRhs); + }); + } + + void SortListByName() + { + std::sort(_rideList.begin(), _rideList.end(), [](const auto& lhs, const auto& rhs) { + return !(0 <= StrLogicalCmp(lhs.Name.c_str(), rhs.Name.c_str())); + }); + } + + /** + * + * rct2: 0x006B39A8 + */ + void RefreshList() + { + _rideList.clear(); + for (auto& rideRef : GetRideManager()) + { + if (rideRef.GetClassification() != static_cast(page) + || (rideRef.status == RideStatus::Closed && !RideHasAnyTrackElements(rideRef))) + { + continue; } - if (type == _windowRideListInformationType) + if (rideRef.window_invalidate_flags & RIDE_INVALIDATE_RIDE_LIST) { - selectedIndex = numItems; + rideRef.window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_LIST; } - gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[numItems].Args = ride_info_type_string_mapping[type]; - numItems++; + RideListEntry entry{}; + entry.Id = rideRef.id; + entry.Name = rideRef.GetName(); + + _rideList.push_back(std::move(entry)); } - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height(), colours[1], 0, - Dropdown::Flag::StayOpen, numItems, widget.width() - 3); - if (selectedIndex != -1) + switch (list_information_type) { - Dropdown::SetChecked(selectedIndex, true); - } - } - } - - /** - * - * rct2: 0x006B3547 - */ - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (widgetIndex == WIDX_OPEN_CLOSE_ALL) - { - if (dropdownIndex == 0) - { - CloseAllRides(); - } - else if (dropdownIndex == 1) - { - OpenAllRides(); + case INFORMATION_TYPE_STATUS: + SortListByName(); + break; + case INFORMATION_TYPE_POPULARITY: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.popularity * 4 <= otherRide.popularity * 4; + }); + break; + case INFORMATION_TYPE_SATISFACTION: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.satisfaction * 5 <= otherRide.satisfaction * 5; + }); + break; + case INFORMATION_TYPE_PROFIT: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.profit <= otherRide.profit; + }); + break; + case INFORMATION_TYPE_TOTAL_CUSTOMERS: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.total_customers <= otherRide.total_customers; + }); + break; + case INFORMATION_TYPE_TOTAL_PROFIT: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.total_profit <= otherRide.total_profit; + }); + break; + case INFORMATION_TYPE_CUSTOMERS: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return RideCustomersPerHour(thisRide) <= RideCustomersPerHour(otherRide); + }); + break; + case INFORMATION_TYPE_AGE: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.build_date <= otherRide.build_date; + }); + break; + case INFORMATION_TYPE_INCOME: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.income_per_hour <= otherRide.income_per_hour; + }); + break; + case INFORMATION_TYPE_RUNNING_COST: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.upkeep_cost <= otherRide.upkeep_cost; + }); + break; + case INFORMATION_TYPE_QUEUE_LENGTH: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.GetTotalQueueLength() <= otherRide.GetTotalQueueLength(); + }); + break; + case INFORMATION_TYPE_QUEUE_TIME: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.GetMaxQueueTime() <= otherRide.GetMaxQueueTime(); + }); + break; + case INFORMATION_TYPE_RELIABILITY: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.reliability_percentage <= otherRide.reliability_percentage; + }); + break; + case INFORMATION_TYPE_DOWN_TIME: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.downtime <= otherRide.downtime; + }); + break; + case INFORMATION_TYPE_GUESTS_FAVOURITE: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.guests_favourite <= otherRide.guests_favourite; + }); + break; + case INFORMATION_TYPE_EXCITEMENT: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.excitement <= otherRide.excitement; + }); + break; + case INFORMATION_TYPE_INTENSITY: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.intensity <= otherRide.intensity; + }); + break; + case INFORMATION_TYPE_NAUSEA: + SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { + return thisRide.nausea <= otherRide.nausea; + }); + break; } - Invalidate(); - } - else if (widgetIndex == WIDX_INFORMATION_TYPE_DROPDOWN) - { - if (dropdownIndex == -1) - return; - - int32_t informationType = INFORMATION_TYPE_STATUS; - uint32_t arg = static_cast(gDropdownItems[dropdownIndex].Args); - for (size_t i = 0; i < std::size(ride_info_type_string_mapping); i++) - { - if (arg == ride_info_type_string_mapping[i]) - { - informationType = static_cast(i); - } - } - - _windowRideListInformationType = informationType; - Invalidate(); - } - } - - /** - * - * rct2: 0x006B386B - */ - void OnUpdate() override - { - frame_no = (frame_no + 1) % 64; - WidgetInvalidate(*this, WIDX_TAB_1 + page); - if (_windowRideListInformationType != INFORMATION_TYPE_STATUS) - Invalidate(); - } - - /** - * - * rct2: 0x006B35A1 - */ - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - const auto newHeight = static_cast(_rideList.size() * SCROLLABLE_ROW_HEIGHT); - if (selected_list_item != -1) - { selected_list_item = -1; Invalidate(); } - auto top = newHeight - widgets[WIDX_LIST].bottom + widgets[WIDX_LIST].top + 21; - if (top < 0) - top = 0; - if (top < scrolls[0].v_top) + // window_ride_list_close_all + void CloseAllRides() { - scrolls[0].v_top = top; - Invalidate(); - } - - return { 0, newHeight }; - } - - /** - * - * rct2: 0x006B361F - */ - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - const auto index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index < 0 || static_cast(index) >= _rideList.size()) - return; - - // Open ride window - const auto selectedRideId = _rideList[index].Id; - if (_quickDemolishMode && NetworkGetMode() != NETWORK_MODE_CLIENT) - { - auto gameAction = RideDemolishAction(selectedRideId, RIDE_MODIFY_DEMOLISH); - GameActions::Execute(&gameAction); - RefreshList(); - } - else - { - auto intent = Intent(WindowClass::Ride); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, selectedRideId.ToUnderlying()); - ContextOpenIntent(&intent); - } - } - - /** - * - * rct2: 0x006B35EF - */ - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - const auto index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index < 0 || static_cast(index) >= _rideList.size()) - return; - - selected_list_item = index; - Invalidate(); - } - - /** - * - * rct2: 0x006B3182 - */ - void OnPrepareDraw() override - { - widgets[WIDX_CURRENT_INFORMATION_TYPE].text = ride_info_type_string_mapping[_windowRideListInformationType]; - - // Set correct active tab - for (int32_t i = 0; i < 3; i++) - pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); - pressed_widgets |= 1LL << (WIDX_TAB_1 + page); - - widgets[WIDX_TITLE].text = page_names[page]; - - if (_quickDemolishMode) - pressed_widgets |= (1uLL << WIDX_QUICK_DEMOLISH); - else - pressed_widgets &= ~(1uLL << WIDX_QUICK_DEMOLISH); - - ResizeFrameWithPage(); - - widgets[WIDX_LIST].right = width - 26; - widgets[WIDX_LIST].bottom = height - 15; - widgets[WIDX_OPEN_CLOSE_ALL].right = width - 2; - widgets[WIDX_OPEN_CLOSE_ALL].left = width - 25; - widgets[WIDX_CLOSE_LIGHT].right = width - 7; - widgets[WIDX_CLOSE_LIGHT].left = width - 20; - widgets[WIDX_OPEN_LIGHT].right = width - 7; - widgets[WIDX_OPEN_LIGHT].left = width - 20; - widgets[WIDX_QUICK_DEMOLISH].right = width - 2; - widgets[WIDX_QUICK_DEMOLISH].left = width - 25; - - if (ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE) - { - widgets[WIDX_OPEN_CLOSE_ALL].type = WindowWidgetType::Empty; - widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::ImgBtn; - widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::ImgBtn; - - const auto& rideManager = GetRideManager(); - auto allClosed = true; - auto allOpen = false; - if (_rideList.size() > 0 && std::size(rideManager) != 0) + for (auto& rideRef : GetRideManager()) { - auto c = static_cast(page); - allClosed = std::none_of(rideManager.begin(), rideManager.end(), [c](const Ride& rideRef) { - return rideRef.GetClassification() == c && rideRef.status == RideStatus::Open; - }); - allOpen = std::none_of(rideManager.begin(), rideManager.end(), [c](const Ride& rideRef) { - return rideRef.GetClassification() == c && rideRef.status != RideStatus::Open; - }); - } - - const auto closeLightImage( - SPR_G2_RCT1_CLOSE_BUTTON_0 + (allClosed ? 1 : 0) * 2 + WidgetIsPressed(*this, WIDX_CLOSE_LIGHT)); - widgets[WIDX_CLOSE_LIGHT].image = ImageId(closeLightImage); - const auto openLightImage = SPR_G2_RCT1_OPEN_BUTTON_0 + (allOpen ? 1 : 0) * 2 - + WidgetIsPressed(*this, WIDX_OPEN_LIGHT); - widgets[WIDX_OPEN_LIGHT].image = ImageId(openLightImage); - widgets[WIDX_QUICK_DEMOLISH].top = widgets[WIDX_OPEN_LIGHT].bottom + 3; - } - else - { - widgets[WIDX_OPEN_CLOSE_ALL].type = WindowWidgetType::FlatBtn; - widgets[WIDX_CLOSE_LIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_OPEN_LIGHT].type = WindowWidgetType::Empty; - widgets[WIDX_QUICK_DEMOLISH].top = widgets[WIDX_OPEN_CLOSE_ALL].bottom + 3; - } - widgets[WIDX_QUICK_DEMOLISH].bottom = widgets[WIDX_QUICK_DEMOLISH].top + 23; - widgets[WIDX_QUICK_DEMOLISH].type = NetworkGetMode() != NETWORK_MODE_CLIENT ? WindowWidgetType::FlatBtn - : WindowWidgetType::Empty; - } - - /** - * - * rct2: 0x006B3235 - */ - void OnDraw(DrawPixelInfo& dpi) override - { - WindowDrawWidgets(*this, dpi); - DrawTabImages(dpi); - - // Draw number of attractions on bottom - auto ft = Formatter(); - ft.Add(static_cast(_rideList.size())); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_LIST].bottom + 2 }, ride_list_statusbar_count_strings[page], ft); - } - - /** - * - * rct2: 0x006B3240 - */ - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; - GfxFillRect(dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width, dpi.height } }, ColourMapA[colours[1]].mid_light); - - auto y = 0; - for (size_t i = 0; i < _rideList.size(); i++) - { - StringId format = (_quickDemolishMode ? STR_RED_STRINGID : STR_BLACK_STRING); - if (i == static_cast(selected_list_item)) - { - // Background highlight - GfxFilterRect(dpi, { 0, y, 800, y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); - format = (_quickDemolishMode ? STR_LIGHTPINK_STRINGID : STR_WINDOW_COLOUR_2_STRINGID); - } - - // Get ride - const auto* ridePtr = GetRide(_rideList[i].Id); - if (ridePtr == nullptr) - continue; - - // Ride name - auto ft = Formatter(); - ridePtr->FormatNameTo(ft); - DrawTextEllipsised(dpi, { 0, y - 1 }, 159, format, ft); - - // Ride information - ft = Formatter(); - ft.Increment(2); - auto formatSecondaryEnabled = true; - StringId formatSecondary = 0; - switch (_windowRideListInformationType) - { - case INFORMATION_TYPE_STATUS: - formatSecondaryEnabled = false; - ft.Rewind(); - ridePtr->FormatStatusTo(ft); - - // Make test red and bold if broken down or crashed - if ((ridePtr->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) - || (ridePtr->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) - { - format = STR_RED_OUTLINED_STRING; - } - break; - case INFORMATION_TYPE_POPULARITY: - formatSecondary = STR_POPULARITY_UNKNOWN_LABEL; - if (ridePtr->popularity != 255) - { - formatSecondary = STR_POPULARITY_LABEL; - ft.Add(ridePtr->popularity * 4); - } - break; - case INFORMATION_TYPE_SATISFACTION: - formatSecondary = STR_SATISFACTION_UNKNOWN_LABEL; - if (ridePtr->satisfaction != 255) - { - formatSecondary = STR_SATISFACTION_LABEL; - ft.Add(ridePtr->satisfaction * 5); - } - break; - case INFORMATION_TYPE_PROFIT: - formatSecondary = 0; - if (ridePtr->profit != kMoney64Undefined) - { - formatSecondary = STR_PROFIT_LABEL; - ft.Add(ridePtr->profit); - } - break; - case INFORMATION_TYPE_TOTAL_CUSTOMERS: - formatSecondary = STR_RIDE_LIST_TOTAL_CUSTOMERS_LABEL; - ft.Add(ridePtr->total_customers); - break; - case INFORMATION_TYPE_TOTAL_PROFIT: - formatSecondary = 0; - if (ridePtr->total_profit != kMoney64Undefined) - { - formatSecondary = STR_RIDE_LIST_TOTAL_PROFIT_LABEL; - ft.Add(ridePtr->total_profit); - } - break; - case INFORMATION_TYPE_CUSTOMERS: - formatSecondary = STR_RIDE_LIST_CUSTOMERS_PER_HOUR_LABEL; - ft.Add(RideCustomersPerHour(*ridePtr)); - break; - case INFORMATION_TYPE_AGE: + if (rideRef.status != RideStatus::Closed + && rideRef.GetClassification() == static_cast(page)) { - const auto age = DateGetYear(ridePtr->GetAge()); - switch (age) - { - case 0: - formatSecondary = STR_RIDE_LIST_BUILT_THIS_YEAR_LABEL; - break; - case 1: - formatSecondary = STR_RIDE_LIST_BUILT_LAST_YEAR_LABEL; - break; - default: - formatSecondary = STR_RIDE_LIST_BUILT_X_YEARS_AGO_LABEL; - break; - } - ft.Add(age); - break; + auto gameAction = RideSetStatusAction(rideRef.id, RideStatus::Closed); + GameActions::Execute(&gameAction); } - case INFORMATION_TYPE_INCOME: - formatSecondary = 0; - if (ridePtr->income_per_hour != kMoney64Undefined) - { - formatSecondary = STR_RIDE_LIST_INCOME_LABEL; - ft.Add(ridePtr->income_per_hour); - } - break; - case INFORMATION_TYPE_RUNNING_COST: - formatSecondary = STR_RIDE_LIST_RUNNING_COST_UNKNOWN; - if (ridePtr->upkeep_cost != kMoney64Undefined) - { - formatSecondary = STR_RIDE_LIST_RUNNING_COST_LABEL; - ft.Add(ridePtr->upkeep_cost * 16); - } - break; - case INFORMATION_TYPE_QUEUE_LENGTH: - { - const auto queueLength = ridePtr->GetTotalQueueLength(); - ft.Add(queueLength); - - if (queueLength == 1) - { - formatSecondary = STR_QUEUE_ONE_PERSON; - } - else if (queueLength > 1) - { - formatSecondary = STR_QUEUE_PEOPLE; - } - else - { - formatSecondary = STR_QUEUE_EMPTY; - } - break; - } - case INFORMATION_TYPE_QUEUE_TIME: - { - const auto maxQueueTime = ridePtr->GetMaxQueueTime(); - ft.Add(maxQueueTime); - - if (maxQueueTime > 1) - { - formatSecondary = STR_QUEUE_TIME_PLURAL_LABEL; - } - else - { - formatSecondary = STR_QUEUE_TIME_LABEL; - } - break; - } - case INFORMATION_TYPE_RELIABILITY: - ft.Add(ridePtr->reliability_percentage); - formatSecondary = STR_RELIABILITY_LABEL; - break; - case INFORMATION_TYPE_DOWN_TIME: - ft.Add(ridePtr->downtime); - formatSecondary = STR_DOWN_TIME_LABEL; - break; - case INFORMATION_TYPE_GUESTS_FAVOURITE: - formatSecondary = 0; - if (ridePtr->IsRide()) - { - ft.Add(ridePtr->guests_favourite); - formatSecondary = ridePtr->guests_favourite == 1 ? STR_GUESTS_FAVOURITE_LABEL - : STR_GUESTS_FAVOURITE_PLURAL_LABEL; - } - break; - case INFORMATION_TYPE_EXCITEMENT: - formatSecondary = STR_RATING_UKNOWN_LABEL; - if (RideHasRatings(*ridePtr)) - { - formatSecondary = STR_EXCITEMENT_LABEL; - ft.Add(ridePtr->excitement); - } - break; - case INFORMATION_TYPE_INTENSITY: - formatSecondary = STR_RATING_UKNOWN_LABEL; - if (RideHasRatings(*ridePtr)) - { - formatSecondary = STR_INTENSITY_LABEL; - ft.Add(ridePtr->intensity); - } - break; - case INFORMATION_TYPE_NAUSEA: - formatSecondary = STR_RATING_UKNOWN_LABEL; - if (RideHasRatings(*ridePtr)) - { - formatSecondary = STR_NAUSEA_LABEL; - ft.Add(ridePtr->nausea); - } - break; } - - if (formatSecondaryEnabled) - { - ft.Rewind(); - ft.Add(formatSecondary); - } - DrawTextEllipsised(dpi, { 160, y - 1 }, 157, format, ft); - y += SCROLLABLE_ROW_HEIGHT; } - } - void RefreshListWrapper() - { - RefreshList(); - } - -private: - /** - * - * rct2: 0x006B38EA - */ - void DrawTabImages(DrawPixelInfo& dpi) - { - int32_t sprite_idx; - - // Rides tab - sprite_idx = SPR_TAB_RIDE_0; - if (page == PAGE_RIDES) - sprite_idx += frame_no / 4; - GfxDrawSprite( - dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_1].left, widgets[WIDX_TAB_1].top }); - - // Shops and stalls tab - sprite_idx = SPR_TAB_SHOPS_AND_STALLS_0; - if (page == PAGE_SHOPS_AND_STALLS) - sprite_idx += frame_no / 4; - GfxDrawSprite( - dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_2].left, widgets[WIDX_TAB_2].top }); - - // Information kiosks and facilities tab - sprite_idx = SPR_TAB_KIOSKS_AND_FACILITIES_0; - if (page == PAGE_KIOSKS_AND_FACILITIES) - sprite_idx += (frame_no / 4) % 8; - GfxDrawSprite( - dpi, ImageId(sprite_idx), windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_3].left, widgets[WIDX_TAB_3].top }); - } - - /** - * Used in RefreshList() to handle the sorting of the list. - * Uses a lambda function (predicate) as exit criteria for the algorithm. - */ - template void SortListByPredicate(const TSortPred& pred) - { - std::sort(_rideList.begin(), _rideList.end(), [&pred](const auto& lhs, const auto& rhs) { - const Ride* rideLhs = GetRide(lhs.Id); - const Ride* rideRhs = GetRide(rhs.Id); - if (rideLhs == nullptr || rideRhs == nullptr) + // window_ride_list_open_all + void OpenAllRides() + { + for (auto& rideRef : GetRideManager()) { - return false; + if (rideRef.status != RideStatus::Open && rideRef.GetClassification() == static_cast(page)) + { + auto gameAction = RideSetStatusAction(rideRef.id, RideStatus::Open); + GameActions::Execute(&gameAction); + } } - return !pred(*rideLhs, *rideRhs); - }); - } - - void SortListByName() - { - std::sort(_rideList.begin(), _rideList.end(), [](const auto& lhs, const auto& rhs) { - return !(0 <= StrLogicalCmp(lhs.Name.c_str(), rhs.Name.c_str())); - }); - } + } + }; /** * - * rct2: 0x006B39A8 + * rct2: 0x006B30BC */ - void RefreshList() + WindowBase* WindowRideListOpen() { - _rideList.clear(); - for (auto& rideRef : GetRideManager()) + // Check if window is already open + auto* window = WindowBringToFrontByClass(WindowClass::RideList); + if (window == nullptr) { - if (rideRef.GetClassification() != static_cast(page) - || (rideRef.status == RideStatus::Closed && !RideHasAnyTrackElements(rideRef))) - { - continue; - } - - if (rideRef.window_invalidate_flags & RIDE_INVALIDATE_RIDE_LIST) - { - rideRef.window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_LIST; - } - - RideListEntry entry{}; - entry.Id = rideRef.id; - entry.Name = rideRef.GetName(); - - _rideList.push_back(std::move(entry)); + window = WindowCreate(WindowClass::RideList, ScreenCoordsXY(32, 32), WW, WH, WF_10 | WF_RESIZABLE); } - - switch (list_information_type) - { - case INFORMATION_TYPE_STATUS: - SortListByName(); - break; - case INFORMATION_TYPE_POPULARITY: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.popularity * 4 <= otherRide.popularity * 4; - }); - break; - case INFORMATION_TYPE_SATISFACTION: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.satisfaction * 5 <= otherRide.satisfaction * 5; - }); - break; - case INFORMATION_TYPE_PROFIT: - SortListByPredicate( - [](const Ride& thisRide, const Ride& otherRide) -> bool { return thisRide.profit <= otherRide.profit; }); - break; - case INFORMATION_TYPE_TOTAL_CUSTOMERS: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.total_customers <= otherRide.total_customers; - }); - break; - case INFORMATION_TYPE_TOTAL_PROFIT: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.total_profit <= otherRide.total_profit; - }); - break; - case INFORMATION_TYPE_CUSTOMERS: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return RideCustomersPerHour(thisRide) <= RideCustomersPerHour(otherRide); - }); - break; - case INFORMATION_TYPE_AGE: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.build_date <= otherRide.build_date; - }); - break; - case INFORMATION_TYPE_INCOME: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.income_per_hour <= otherRide.income_per_hour; - }); - break; - case INFORMATION_TYPE_RUNNING_COST: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.upkeep_cost <= otherRide.upkeep_cost; - }); - break; - case INFORMATION_TYPE_QUEUE_LENGTH: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.GetTotalQueueLength() <= otherRide.GetTotalQueueLength(); - }); - break; - case INFORMATION_TYPE_QUEUE_TIME: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.GetMaxQueueTime() <= otherRide.GetMaxQueueTime(); - }); - break; - case INFORMATION_TYPE_RELIABILITY: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.reliability_percentage <= otherRide.reliability_percentage; - }); - break; - case INFORMATION_TYPE_DOWN_TIME: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.downtime <= otherRide.downtime; - }); - break; - case INFORMATION_TYPE_GUESTS_FAVOURITE: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.guests_favourite <= otherRide.guests_favourite; - }); - break; - case INFORMATION_TYPE_EXCITEMENT: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.excitement <= otherRide.excitement; - }); - break; - case INFORMATION_TYPE_INTENSITY: - SortListByPredicate([](const Ride& thisRide, const Ride& otherRide) -> bool { - return thisRide.intensity <= otherRide.intensity; - }); - break; - case INFORMATION_TYPE_NAUSEA: - SortListByPredicate( - [](const Ride& thisRide, const Ride& otherRide) -> bool { return thisRide.nausea <= otherRide.nausea; }); - break; - } - - selected_list_item = -1; - Invalidate(); + return window; } - // window_ride_list_close_all - void CloseAllRides() + void WindowRideListRefreshList(WindowBase* w) { - for (auto& rideRef : GetRideManager()) - { - if (rideRef.status != RideStatus::Closed && rideRef.GetClassification() == static_cast(page)) - { - auto gameAction = RideSetStatusAction(rideRef.id, RideStatus::Closed); - GameActions::Execute(&gameAction); - } - } + dynamic_cast(w)->RefreshListWrapper(); } - - // window_ride_list_open_all - void OpenAllRides() - { - for (auto& rideRef : GetRideManager()) - { - if (rideRef.status != RideStatus::Open && rideRef.GetClassification() == static_cast(page)) - { - auto gameAction = RideSetStatusAction(rideRef.id, RideStatus::Open); - GameActions::Execute(&gameAction); - } - } - } -}; - -/** - * - * rct2: 0x006B30BC - */ -WindowBase* WindowRideListOpen() -{ - // Check if window is already open - auto* window = WindowBringToFrontByClass(WindowClass::RideList); - if (window == nullptr) - { - window = WindowCreate(WindowClass::RideList, ScreenCoordsXY(32, 32), WW, WH, WF_10 | WF_RESIZABLE); - } - return window; -} - -void WindowRideListRefreshList(WindowBase* w) -{ - dynamic_cast(w)->RefreshListWrapper(); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/SavePrompt.cpp b/src/openrct2-ui/windows/SavePrompt.cpp index ffd9039d66..6171ca20c7 100644 --- a/src/openrct2-ui/windows/SavePrompt.cpp +++ b/src/openrct2-ui/windows/SavePrompt.cpp @@ -21,14 +21,14 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr int32_t WH_SAVE = 54; + static constexpr int32_t WW_SAVE = 260; + static constexpr int32_t WH_QUIT = 38; + static constexpr int32_t WW_QUIT = 177; -static constexpr int32_t WH_SAVE = 54; -static constexpr int32_t WW_SAVE = 260; -static constexpr int32_t WH_QUIT = 38; -static constexpr int32_t WW_QUIT = 177; - -// clang-format off + // clang-format off enum WindowSavePromptWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -69,184 +69,185 @@ static constexpr StringId window_save_prompt_labels[][2] = { { STR_QUIT_GAME_2_PROMPT_TITLE, STR_SAVE_BEFORE_QUITTING_2 }, { STR_NEW_GAME, STR_SAVE_BEFORE_QUITTING }, }; -// clang-format on + // clang-format on -static void WindowSavePromptCallback(int32_t result, const utf8* path) -{ - if (result == MODAL_RESULT_OK) + static void WindowSavePromptCallback(int32_t result, const utf8* path) { - GameLoadOrQuitNoSavePrompt(); - } -} - -class SavePromptWindow final : public Window -{ -private: - PromptMode _promptMode; - -public: - SavePromptWindow(PromptMode promptMode) - : _promptMode(promptMode) - { - } - - void OnOpen() override - { - bool canSave = !(gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)); - - widgets = canSave ? _savePromptWidgets : _quitPromptWidgets; - - InitScrollWidgets(); - - // Pause the game if not network play. - if (NetworkGetMode() == NETWORK_MODE_NONE) + if (result == MODAL_RESULT_OK) + { + GameLoadOrQuitNoSavePrompt(); + } + } + + class SavePromptWindow final : public Window + { + private: + PromptMode _promptMode; + + public: + SavePromptWindow(PromptMode promptMode) + : _promptMode(promptMode) { - gGamePaused |= GAME_PAUSED_MODAL; - Audio::StopAll(); } - WindowInvalidateByClass(WindowClass::TopToolbar); - - if (canSave) + void OnOpen() override { - StringId stringId = window_save_prompt_labels[EnumValue(_promptMode)][0]; - if (stringId == STR_LOAD_GAME_PROMPT_TITLE && gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + bool canSave = !(gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)); + + widgets = canSave ? _savePromptWidgets : _quitPromptWidgets; + + InitScrollWidgets(); + + // Pause the game if not network play. + if (NetworkGetMode() == NETWORK_MODE_NONE) { - stringId = STR_LOAD_LANDSCAPE_PROMPT_TITLE; + gGamePaused |= GAME_PAUSED_MODAL; + Audio::StopAll(); } - else if (stringId == STR_QUIT_GAME_PROMPT_TITLE && gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + + WindowInvalidateByClass(WindowClass::TopToolbar); + + if (canSave) { - stringId = STR_QUIT_SCENARIO_EDITOR; + StringId stringId = window_save_prompt_labels[EnumValue(_promptMode)][0]; + if (stringId == STR_LOAD_GAME_PROMPT_TITLE && gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + { + stringId = STR_LOAD_LANDSCAPE_PROMPT_TITLE; + } + else if (stringId == STR_QUIT_GAME_PROMPT_TITLE && gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + { + stringId = STR_QUIT_SCENARIO_EDITOR; + } + widgets[WIDX_TITLE].text = stringId; + widgets[WIDX_LABEL].text = window_save_prompt_labels[EnumValue(_promptMode)][1]; } - widgets[WIDX_TITLE].text = stringId; - widgets[WIDX_LABEL].text = window_save_prompt_labels[EnumValue(_promptMode)][1]; - } - } - - void OnClose() override - { - // Unpause the game - if (NetworkGetMode() == NETWORK_MODE_NONE) - { - gGamePaused &= ~GAME_PAUSED_MODAL; - Audio::Resume(); } - WindowInvalidateByClass(WindowClass::TopToolbar); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - if (gScreenFlags & (SCREEN_FLAGS_TITLE_DEMO | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + void OnClose() override { + // Unpause the game + if (NetworkGetMode() == NETWORK_MODE_NONE) + { + gGamePaused &= ~GAME_PAUSED_MODAL; + Audio::Resume(); + } + + WindowInvalidateByClass(WindowClass::TopToolbar); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + if (gScreenFlags & (SCREEN_FLAGS_TITLE_DEMO | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + { + switch (widgetIndex) + { + case WQIDX_OK: + GameLoadOrQuitNoSavePrompt(); + break; + case WQIDX_CLOSE: + case WQIDX_CANCEL: + Close(); + break; + } + return; + } + switch (widgetIndex) { - case WQIDX_OK: - GameLoadOrQuitNoSavePrompt(); - break; - case WQIDX_CLOSE: - case WQIDX_CANCEL: + case WIDX_SAVE: + { + std::unique_ptr intent; + + if (gScreenFlags & (SCREEN_FLAGS_EDITOR)) + { + intent = std::make_unique(WindowClass::Loadsave); + intent->PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE); + intent->PutExtra(INTENT_EXTRA_PATH, GetGameState().ScenarioName); + } + else + { + intent = CreateSaveGameAsIntent(); + } Close(); + intent->PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(WindowSavePromptCallback)); + ContextOpenIntent(intent.get()); break; + } + case WIDX_DONT_SAVE: + GameLoadOrQuitNoSavePrompt(); + return; + case WIDX_CLOSE: + case WIDX_CANCEL: + Close(); + return; } - return; } - switch (widgetIndex) + void OnDraw(DrawPixelInfo& dpi) override { - case WIDX_SAVE: - { - std::unique_ptr intent; - - if (gScreenFlags & (SCREEN_FLAGS_EDITOR)) - { - intent = std::make_unique(WindowClass::Loadsave); - intent->PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE); - intent->PutExtra(INTENT_EXTRA_PATH, GetGameState().ScenarioName); - } - else - { - intent = CreateSaveGameAsIntent(); - } - Close(); - intent->PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(WindowSavePromptCallback)); - ContextOpenIntent(intent.get()); - break; - } - case WIDX_DONT_SAVE: - GameLoadOrQuitNoSavePrompt(); - return; - case WIDX_CLOSE: - case WIDX_CANCEL: - Close(); - return; + DrawWidgets(dpi); } - } - void OnDraw(DrawPixelInfo& dpi) override + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowSavePromptOpen() { - DrawWidgets(dpi); - } + PromptMode prompt_mode = gSavePromptMode; + if (prompt_mode == PromptMode::Quit) + { + prompt_mode = PromptMode::SaveBeforeQuit; + } - void OnResize() override - { - ResizeFrame(); - } -}; - -WindowBase* WindowSavePromptOpen() -{ - PromptMode prompt_mode = gSavePromptMode; - if (prompt_mode == PromptMode::Quit) - { - prompt_mode = PromptMode::SaveBeforeQuit; - } - - // do not show save prompt if we're in the title demo and click on load game - if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) - { - GameLoadOrQuitNoSavePrompt(); - return nullptr; - } - - if (!gConfigGeneral.ConfirmationPrompt) - { - /* game_load_or_quit_no_save_prompt() will exec requested task and close this window - * immediately again. - * TODO restructure these functions when we're sure game_load_or_quit_no_save_prompt() - * and game_load_or_quit() are not called by the original binary anymore. - */ - - if (gScreenAge < 3840 && NetworkGetMode() == NETWORK_MODE_NONE) + // do not show save prompt if we're in the title demo and click on load game + if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) { GameLoadOrQuitNoSavePrompt(); return nullptr; } - } - // Check if window is already open - WindowBase* window = WindowBringToFrontByClass(WindowClass::SavePrompt); - if (window != nullptr) - { - WindowClose(*window); - } + if (!gConfigGeneral.ConfirmationPrompt) + { + /* game_load_or_quit_no_save_prompt() will exec requested task and close this window + * immediately again. + * TODO restructure these functions when we're sure game_load_or_quit_no_save_prompt() + * and game_load_or_quit() are not called by the original binary anymore. + */ - if (EnumValue(prompt_mode) >= std::size(window_save_prompt_labels)) - { - LOG_WARNING("Invalid save prompt mode %u", prompt_mode); - return nullptr; - } + if (gScreenAge < 3840 && NetworkGetMode() == NETWORK_MODE_NONE) + { + GameLoadOrQuitNoSavePrompt(); + return nullptr; + } + } - int32_t width = WW_SAVE; - int32_t height = WH_SAVE; - if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) - { - width = WW_QUIT; - height = WH_QUIT; - } + // Check if window is already open + WindowBase* window = WindowBringToFrontByClass(WindowClass::SavePrompt); + if (window != nullptr) + { + WindowClose(*window); + } - auto savePromptWindow = std::make_unique(prompt_mode); - return WindowCreate( - std::move(savePromptWindow), WindowClass::SavePrompt, {}, width, height, - WF_TRANSPARENT | WF_STICK_TO_FRONT | WF_CENTRE_SCREEN | WF_AUTO_POSITION); -} + if (EnumValue(prompt_mode) >= std::size(window_save_prompt_labels)) + { + LOG_WARNING("Invalid save prompt mode %u", prompt_mode); + return nullptr; + } + + int32_t width = WW_SAVE; + int32_t height = WH_SAVE; + if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + { + width = WW_QUIT; + height = WH_QUIT; + } + + auto savePromptWindow = std::make_unique(prompt_mode); + return WindowCreate( + std::move(savePromptWindow), WindowClass::SavePrompt, {}, width, height, + WF_TRANSPARENT | WF_STICK_TO_FRONT | WF_CENTRE_SCREEN | WF_AUTO_POSITION); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/ScenarioSelect.cpp b/src/openrct2-ui/windows/ScenarioSelect.cpp index 6a13c831e3..795a87fda5 100644 --- a/src/openrct2-ui/windows/ScenarioSelect.cpp +++ b/src/openrct2-ui/windows/ScenarioSelect.cpp @@ -28,68 +28,70 @@ #include #include -static constexpr StringId WINDOW_TITLE = STR_SELECT_SCENARIO; -static constexpr int32_t WW = 734; -static constexpr int32_t WH = 384; -static constexpr int32_t SidebarWidth = 180; -static constexpr int32_t TabWidth = 92; -static constexpr int32_t TabHeight = 34; -static constexpr int32_t TrueFontSize = 24; -static constexpr int32_t WidgetsStart = 17; -static constexpr int32_t TabsStart = WidgetsStart; -static constexpr int32_t InitialNumUnlockedScenarios = 5; -constexpr uint8_t NumTabs = 10; - -enum class ListItemType : uint8_t +namespace OpenRCT2::Ui::Windows { - Heading, - Scenario, -}; + static constexpr StringId WINDOW_TITLE = STR_SELECT_SCENARIO; + static constexpr int32_t WW = 734; + static constexpr int32_t WH = 384; + static constexpr int32_t SidebarWidth = 180; + static constexpr int32_t TabWidth = 92; + static constexpr int32_t TabHeight = 34; + static constexpr int32_t TrueFontSize = 24; + static constexpr int32_t WidgetsStart = 17; + static constexpr int32_t TabsStart = WidgetsStart; + static constexpr int32_t InitialNumUnlockedScenarios = 5; + constexpr uint8_t NumTabs = 10; -struct ScenarioListItem -{ - ListItemType type; - union + enum class ListItemType : uint8_t { - struct - { - StringId string_id; - } heading; - struct - { - const ScenarioIndexEntry* scenario; - bool is_locked; - } scenario; + Heading, + Scenario, }; -}; -enum -{ - WIDX_BACKGROUND, - WIDX_TITLEBAR, - WIDX_CLOSE, - WIDX_TABCONTENT, - WIDX_TAB1, - WIDX_TAB2, - WIDX_TAB3, - WIDX_TAB4, - WIDX_TAB5, - WIDX_TAB6, - WIDX_TAB7, - WIDX_TAB8, - WIDX_TAB9, - WIDX_TAB10, - WIDX_SCENARIOLIST -}; + struct ScenarioListItem + { + ListItemType type; + union + { + struct + { + StringId string_id; + } heading; + struct + { + const ScenarioIndexEntry* scenario; + bool is_locked; + } scenario; + }; + }; -static constexpr StringId kScenarioOriginStringIds[] = { - STR_SCENARIO_CATEGORY_RCT1, STR_SCENARIO_CATEGORY_RCT1_AA, STR_SCENARIO_CATEGORY_RCT1_LL, - STR_SCENARIO_CATEGORY_RCT2, STR_SCENARIO_CATEGORY_RCT2_WW, STR_SCENARIO_CATEGORY_RCT2_TT, - STR_SCENARIO_CATEGORY_UCES, STR_SCENARIO_CATEGORY_REAL_PARKS, STR_SCENARIO_CATEGORY_EXTRAS_PARKS, - STR_SCENARIO_CATEGORY_OTHER_PARKS, -}; + enum + { + WIDX_BACKGROUND, + WIDX_TITLEBAR, + WIDX_CLOSE, + WIDX_TABCONTENT, + WIDX_TAB1, + WIDX_TAB2, + WIDX_TAB3, + WIDX_TAB4, + WIDX_TAB5, + WIDX_TAB6, + WIDX_TAB7, + WIDX_TAB8, + WIDX_TAB9, + WIDX_TAB10, + WIDX_SCENARIOLIST + }; -// clang-format off + static constexpr StringId kScenarioOriginStringIds[] = { + STR_SCENARIO_CATEGORY_RCT1, STR_SCENARIO_CATEGORY_RCT1_AA, STR_SCENARIO_CATEGORY_RCT1_LL, + STR_SCENARIO_CATEGORY_RCT2, STR_SCENARIO_CATEGORY_RCT2_WW, STR_SCENARIO_CATEGORY_RCT2_TT, + STR_SCENARIO_CATEGORY_UCES, STR_SCENARIO_CATEGORY_REAL_PARKS, STR_SCENARIO_CATEGORY_EXTRAS_PARKS, + STR_SCENARIO_CATEGORY_OTHER_PARKS, + }; + + // clang-format off static Widget _scenarioSelectWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget({ TabWidth + 1, WidgetsStart }, { WW, 284 }, WindowWidgetType::Resize, WindowColour::Secondary), // tab content panel @@ -106,674 +108,677 @@ static Widget _scenarioSelectWidgets[] = { MakeWidget({ TabWidth + 3, WidgetsStart + 1 }, { WW - SidebarWidth, 362 }, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_VERTICAL), // level list kWidgetsEnd, }; -// clang-format on + // clang-format on -class ScenarioSelectWindow final : public Window -{ -private: - bool _showLockedInformation = false; - std::function _callback; - std::vector _listItems; - const ScenarioIndexEntry* _highlightedScenario = nullptr; - -public: - ScenarioSelectWindow(std::function callback) - : _callback(callback) + class ScenarioSelectWindow final : public Window { - } + private: + bool _showLockedInformation = false; + std::function _callback; + std::vector _listItems; + const ScenarioIndexEntry* _highlightedScenario = nullptr; - void OnOpen() override - { - // Load scenario list - ScenarioRepositoryScan(); - - widgets = _scenarioSelectWidgets; - _highlightedScenario = nullptr; - InitTabs(); - InitialiseListItems(); - InitScrollWidgets(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - if (widgetIndex == WIDX_CLOSE) + public: + ScenarioSelectWindow(std::function callback) + : _callback(callback) { - Close(); } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - if (widgetIndex >= WIDX_TAB1 && widgetIndex <= WIDX_TAB10) + void OnOpen() override { - selected_tab = widgetIndex - 4; + // Load scenario list + ScenarioRepositoryScan(); + + widgets = _scenarioSelectWidgets; _highlightedScenario = nullptr; - gConfigInterface.ScenarioselectLastTab = selected_tab; - ConfigSaveDefault(); + InitTabs(); InitialiseListItems(); - Invalidate(); - OnResize(); - OnPrepareDraw(); InitScrollWidgets(); - Invalidate(); } - } - void OnDraw(DrawPixelInfo& dpi) override - { - int32_t format; - const ScenarioIndexEntry* scenario; - - DrawWidgets(dpi); - - format = ScenarioSelectUseSmallFont() ? STR_SMALL_WINDOW_COLOUR_2_STRINGID : STR_WINDOW_COLOUR_2_STRINGID; - FontStyle fontStyle = ScenarioSelectUseSmallFont() ? FontStyle::Small : FontStyle::Medium; - - // Text for each tab - for (uint32_t i = 0; i < std::size(kScenarioOriginStringIds); i++) + void OnMouseUp(WidgetIndex widgetIndex) override { - const Widget& widget = widgets[WIDX_TAB1 + i]; - if (widget.type == WindowWidgetType::Empty) - continue; - - auto ft = Formatter(); - if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) + if (widgetIndex == WIDX_CLOSE) { - ft.Add(kScenarioOriginStringIds[i]); - } - else - { // old-style - ft.Add(ScenarioCategoryStringIds[i]); - } - - auto stringCoords = windowPos + ScreenCoordsXY{ widget.midX(), widget.midY() - 3 }; - DrawTextWrapped(dpi, stringCoords, 87, format, ft, { COLOUR_AQUAMARINE, fontStyle, TextAlignment::CENTRE }); - } - - // Return if no scenario highlighted - scenario = _highlightedScenario; - if (scenario == nullptr) - { - if (_showLockedInformation) - { - // Show locked information - auto screenPos = windowPos - + ScreenCoordsXY{ widgets[WIDX_SCENARIOLIST].right + 4, widgets[WIDX_TABCONTENT].top + 5 }; - DrawTextEllipsised( - dpi, screenPos + ScreenCoordsXY{ 85, 0 }, 170, STR_SCENARIO_LOCKED, {}, { TextAlignment::CENTRE }); - - DrawTextWrapped(dpi, screenPos + ScreenCoordsXY{ 0, 15 }, 170, STR_SCENARIO_LOCKED_DESC); - } - else - { - // Show general information about how to start. - auto screenPos = windowPos - + ScreenCoordsXY{ widgets[WIDX_SCENARIOLIST].right + 4, widgets[WIDX_TABCONTENT].top + 5 }; - - DrawTextWrapped(dpi, screenPos + ScreenCoordsXY{ 0, 15 }, 170, STR_SCENARIO_HOVER_HINT); - } - return; - } - - // Scenario path - if (gConfigGeneral.DebuggingTools) - { - const auto shortPath = ShortenPath(scenario->Path, width - 6 - TabWidth, FontStyle::Medium); - - auto ft = Formatter(); - ft.Add(shortPath.c_str()); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ TabWidth + 3, height - 3 - 11 }, STR_STRING, ft, { colours[1] }); - } - - // Scenario name - auto screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_SCENARIOLIST].right + 4, widgets[WIDX_TABCONTENT].top + 5 }; - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(scenario->Name); - DrawTextEllipsised( - dpi, screenPos + ScreenCoordsXY{ 85, 0 }, 170, STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::CENTRE }); - screenPos.y += 15; - - // Scenario details - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(scenario->Details); - screenPos.y += DrawTextWrapped(dpi, screenPos, 170, STR_BLACK_STRING, ft) + 5; - - // Scenario objective - ft = Formatter(); - ft.Add(ObjectiveNames[scenario->ObjectiveType]); - if (scenario->ObjectiveType == OBJECTIVE_BUILD_THE_BEST) - { - StringId rideTypeString = STR_NONE; - auto rideTypeId = scenario->ObjectiveArg3; - if (rideTypeId != RIDE_TYPE_NULL && rideTypeId < RIDE_TYPE_COUNT) - { - rideTypeString = GetRideTypeDescriptor(rideTypeId).Naming.Name; - } - ft.Add(rideTypeString); - } - else - { - ft.Add(scenario->ObjectiveArg3); - ft.Add(DateGetTotalMonths(MONTH_OCTOBER, scenario->ObjectiveArg1)); - if (scenario->ObjectiveType == OBJECTIVE_FINISH_5_ROLLERCOASTERS) - ft.Add(scenario->ObjectiveArg2); - else - ft.Add(scenario->ObjectiveArg2); - } - screenPos.y += DrawTextWrapped(dpi, screenPos, 170, STR_OBJECTIVE, ft) + 5; - - // Scenario score - if (scenario->Highscore != nullptr) - { - // TODO: Should probably be translatable - u8string completedByName = "???"; - if (!scenario->Highscore->name.empty()) - { - completedByName = scenario->Highscore->name; - } - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(completedByName.c_str()); - ft.Add(scenario->Highscore->company_value); - screenPos.y += DrawTextWrapped(dpi, screenPos, 170, STR_COMPLETED_BY_WITH_COMPANY_VALUE, ft); - } - } - - void OnPrepareDraw() override - { - pressed_widgets &= ~( - (1uLL << WIDX_CLOSE) | (1uLL << WIDX_TAB1) | (1uLL << WIDX_TAB2) | (1uLL << WIDX_TAB3) | (1uLL << WIDX_TAB4) - | (1uLL << WIDX_TAB5) | (1uLL << WIDX_TAB6) | (1uLL << WIDX_TAB7) | (1uLL << WIDX_TAB8) | (1uLL << WIDX_TAB9) - | (1uLL << WIDX_TAB10)); - - pressed_widgets |= 1LL << (selected_tab + WIDX_TAB1); - - ResizeFrameWithPage(); - const int32_t bottomMargin = gConfigGeneral.DebuggingTools ? 17 : 5; - widgets[WIDX_SCENARIOLIST].right = width - 179; - widgets[WIDX_SCENARIOLIST].bottom = height - bottomMargin; - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - const int32_t scenarioItemHeight = GetScenarioListItemSize(); - - int32_t y = 0; - for (const auto& listItem : _listItems) - { - switch (listItem.type) - { - case ListItemType::Heading: - y += 18; - break; - case ListItemType::Scenario: - y += scenarioItemHeight; - break; + Close(); } } - return { WW, y }; - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - const int32_t scenarioItemHeight = GetScenarioListItemSize(); - - bool originalShowLockedInformation = _showLockedInformation; - _showLockedInformation = false; - const ScenarioIndexEntry* selected = nullptr; - auto mutableScreenCoords = screenCoords; - for (const auto& listItem : _listItems) + void OnMouseDown(WidgetIndex widgetIndex) override { - switch (listItem.type) + if (widgetIndex >= WIDX_TAB1 && widgetIndex <= WIDX_TAB10) { - case ListItemType::Heading: - mutableScreenCoords.y -= 18; - break; - case ListItemType::Scenario: - mutableScreenCoords.y -= scenarioItemHeight; - if (mutableScreenCoords.y < 0) - { - if (listItem.scenario.is_locked) - { - _showLockedInformation = true; - } - else - { - selected = listItem.scenario.scenario; - } - } - break; - } - if (mutableScreenCoords.y < 0) - { - break; + selected_tab = widgetIndex - 4; + _highlightedScenario = nullptr; + gConfigInterface.ScenarioselectLastTab = selected_tab; + ConfigSaveDefault(); + InitialiseListItems(); + Invalidate(); + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + Invalidate(); } } - if (_highlightedScenario != selected) + void OnDraw(DrawPixelInfo& dpi) override { - _highlightedScenario = selected; - Invalidate(); - } - else if (_showLockedInformation != originalShowLockedInformation) - { - Invalidate(); - } - } + int32_t format; + const ScenarioIndexEntry* scenario; - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - const int32_t scenarioItemHeight = GetScenarioListItemSize(); + DrawWidgets(dpi); - auto mutableScreenCoords = screenCoords; - for (const auto& listItem : _listItems) - { - switch (listItem.type) + format = ScenarioSelectUseSmallFont() ? STR_SMALL_WINDOW_COLOUR_2_STRINGID : STR_WINDOW_COLOUR_2_STRINGID; + FontStyle fontStyle = ScenarioSelectUseSmallFont() ? FontStyle::Small : FontStyle::Medium; + + // Text for each tab + for (uint32_t i = 0; i < std::size(kScenarioOriginStringIds); i++) { - case ListItemType::Heading: - mutableScreenCoords.y -= 18; - break; - case ListItemType::Scenario: - mutableScreenCoords.y -= scenarioItemHeight; - if (mutableScreenCoords.y < 0 && !listItem.scenario.is_locked) - { - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); - gFirstTimeSaving = true; - // Callback will likely close this window! So should always return after it. - _callback(listItem.scenario.scenario->Path); - return; - } - break; - } - if (mutableScreenCoords.y < 0) - { - break; - } - } - } + const Widget& widget = widgets[WIDX_TAB1 + i]; + if (widget.type == WindowWidgetType::Empty) + continue; - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; - GfxClear(&dpi, paletteIndex); - - StringId highlighted_format = ScenarioSelectUseSmallFont() ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; - StringId unhighlighted_format = ScenarioSelectUseSmallFont() ? STR_WHITE_STRING : STR_BLACK_STRING; - - const auto& listWidget = widgets[WIDX_SCENARIOLIST]; - int32_t listWidth = listWidget.width() - 12; - - const int32_t scenarioItemHeight = GetScenarioListItemSize(); - - // Scenario title - int32_t scenarioTitleHeight = FontGetLineHeight(FontStyle::Medium); - - int32_t y = 0; - for (const auto& listItem : _listItems) - { - if (y > dpi.y + dpi.height) - { - continue; - } - - switch (listItem.type) - { - case ListItemType::Heading: + auto ft = Formatter(); + if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) { - const int32_t horizontalRuleMargin = 4; - DrawCategoryHeading( - dpi, horizontalRuleMargin, listWidth - horizontalRuleMargin, y + 2, listItem.heading.string_id); - y += 18; - break; + ft.Add(kScenarioOriginStringIds[i]); } - case ListItemType::Scenario: - { - // Draw hover highlight - const ScenarioIndexEntry* scenario = listItem.scenario.scenario; - bool isHighlighted = _highlightedScenario == scenario; - if (isHighlighted) - { - GfxFilterRect(dpi, { 0, y, width, y + scenarioItemHeight - 1 }, FilterPaletteID::PaletteDarken1); - } - - bool isCompleted = scenario->Highscore != nullptr; - bool isDisabled = listItem.scenario.is_locked; - - // Draw scenario name - char buffer[64]; - SafeStrCpy(buffer, scenario->Name, sizeof(buffer)); - StringId format = isDisabled ? static_cast(STR_STRINGID) - : (isHighlighted ? highlighted_format : unhighlighted_format); - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(buffer); - colour_t colour = isDisabled ? colours[1] | COLOUR_FLAG_INSET : COLOUR_BLACK; - auto darkness = isDisabled ? TextDarkness::Dark : TextDarkness::Regular; - const auto scrollCentre = widgets[WIDX_SCENARIOLIST].width() / 2; - - DrawTextBasic( - dpi, { scrollCentre, y + 1 }, format, ft, - { colour, FontStyle::Medium, TextAlignment::CENTRE, darkness }); - - // Check if scenario is completed - if (isCompleted) - { - // Draw completion tick - GfxDrawSprite(dpi, ImageId(SPR_MENU_CHECKMARK), { widgets[WIDX_SCENARIOLIST].width() - 45, y + 1 }); - - // Draw completion score - u8string completedByName = "???"; - if (!scenario->Highscore->name.empty()) - { - completedByName = scenario->Highscore->name; - } - ft = Formatter(); - ft.Add(STR_COMPLETED_BY); - ft.Add(STR_STRING); - ft.Add(completedByName.c_str()); - DrawTextBasic( - dpi, { scrollCentre, y + scenarioTitleHeight + 1 }, format, ft, - { FontStyle::Small, TextAlignment::CENTRE }); - } - - y += scenarioItemHeight; - break; + else + { // old-style + ft.Add(ScenarioCategoryStringIds[i]); } - } - } - } -private: - void DrawCategoryHeading(DrawPixelInfo& dpi, int32_t left, int32_t right, int32_t y, StringId stringId) const - { - colour_t baseColour = colours[1]; - colour_t lightColour = ColourMapA[baseColour].lighter; - colour_t darkColour = ColourMapA[baseColour].mid_dark; - - // Draw string - int32_t centreX = (left + right) / 2; - DrawTextBasic(dpi, { centreX, y }, stringId, {}, { baseColour, TextAlignment::CENTRE }); - - // Get string dimensions - utf8 buffer[CommonTextBufferSize]; - auto bufferPtr = buffer; - OpenRCT2::FormatStringLegacy(bufferPtr, sizeof(buffer), stringId, nullptr); - int32_t categoryStringHalfWidth = (GfxGetStringWidth(bufferPtr, FontStyle::Medium) / 2) + 4; - int32_t strLeft = centreX - categoryStringHalfWidth; - int32_t strRight = centreX + categoryStringHalfWidth; - - // Draw light horizontal rule - int32_t lineY = y + 4; - auto lightLineLeftTop1 = ScreenCoordsXY{ left, lineY }; - auto lightLineRightBottom1 = ScreenCoordsXY{ strLeft, lineY }; - GfxDrawLine(dpi, { lightLineLeftTop1, lightLineRightBottom1 }, lightColour); - - auto lightLineLeftTop2 = ScreenCoordsXY{ strRight, lineY }; - auto lightLineRightBottom2 = ScreenCoordsXY{ right, lineY }; - GfxDrawLine(dpi, { lightLineLeftTop2, lightLineRightBottom2 }, lightColour); - - // Draw dark horizontal rule - lineY++; - auto darkLineLeftTop1 = ScreenCoordsXY{ left, lineY }; - auto darkLineRightBottom1 = ScreenCoordsXY{ strLeft, lineY }; - GfxDrawLine(dpi, { darkLineLeftTop1, darkLineRightBottom1 }, darkColour); - - auto darkLineLeftTop2 = ScreenCoordsXY{ strRight, lineY }; - auto darkLineRightBottom2 = ScreenCoordsXY{ right, lineY }; - GfxDrawLine(dpi, { darkLineLeftTop2, darkLineRightBottom2 }, darkColour); - } - - void InitialiseListItems() - { - size_t numScenarios = ScenarioRepositoryGetCount(); - _listItems.clear(); - - // Mega park unlock - const uint32_t rct1RequiredCompletedScenarios = (1 << SC_MEGA_PARK) - 1; - uint32_t rct1CompletedScenarios = 0; - std::optional megaParkListItemIndex = std::nullopt; - - int32_t numUnlocks = InitialNumUnlockedScenarios; - uint8_t currentHeading = UINT8_MAX; - for (size_t i = 0; i < numScenarios; i++) - { - const ScenarioIndexEntry* scenario = ScenarioRepositoryGetByIndex(i); - - if (!IsScenarioVisible(*scenario)) - continue; - - // Category heading - StringId headingStringId = STR_NONE; - if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) - { - if (selected_tab != static_cast(ScenarioSource::Real) && currentHeading != scenario->Category) - { - currentHeading = scenario->Category; - headingStringId = ScenarioCategoryStringIds[currentHeading]; - } - } - else - { - if (selected_tab <= SCENARIO_CATEGORY_EXPERT) - { - if (currentHeading != static_cast(scenario->SourceGame)) - { - currentHeading = static_cast(scenario->SourceGame); - headingStringId = kScenarioOriginStringIds[currentHeading]; - } - } - else if (selected_tab == SCENARIO_CATEGORY_OTHER) - { - int32_t category = scenario->Category; - if (category <= SCENARIO_CATEGORY_REAL) - { - category = SCENARIO_CATEGORY_OTHER; - } - if (currentHeading != category) - { - currentHeading = category; - headingStringId = ScenarioCategoryStringIds[category]; - } - } + auto stringCoords = windowPos + ScreenCoordsXY{ widget.midX(), widget.midY() - 3 }; + DrawTextWrapped(dpi, stringCoords, 87, format, ft, { COLOUR_AQUAMARINE, fontStyle, TextAlignment::CENTRE }); } - if (headingStringId != STR_NONE) + // Return if no scenario highlighted + scenario = _highlightedScenario; + if (scenario == nullptr) { - ScenarioListItem headerItem; - headerItem.type = ListItemType::Heading; - headerItem.heading.string_id = headingStringId; - _listItems.push_back(std::move(headerItem)); - } - - // Scenario - ScenarioListItem scenarioItem; - scenarioItem.type = ListItemType::Scenario; - scenarioItem.scenario.scenario = scenario; - if (IsLockingEnabled()) - { - scenarioItem.scenario.is_locked = numUnlocks <= 0; - if (scenario->Highscore == nullptr) + if (_showLockedInformation) { - numUnlocks--; + // Show locked information + auto screenPos = windowPos + + ScreenCoordsXY{ widgets[WIDX_SCENARIOLIST].right + 4, widgets[WIDX_TABCONTENT].top + 5 }; + DrawTextEllipsised( + dpi, screenPos + ScreenCoordsXY{ 85, 0 }, 170, STR_SCENARIO_LOCKED, {}, { TextAlignment::CENTRE }); + + DrawTextWrapped(dpi, screenPos + ScreenCoordsXY{ 0, 15 }, 170, STR_SCENARIO_LOCKED_DESC); } else { - // Mark RCT1 scenario as completed - if (scenario->ScenarioId < SC_MEGA_PARK) - { - rct1CompletedScenarios |= 1 << scenario->ScenarioId; - } - } + // Show general information about how to start. + auto screenPos = windowPos + + ScreenCoordsXY{ widgets[WIDX_SCENARIOLIST].right + 4, widgets[WIDX_TABCONTENT].top + 5 }; - // If scenario is Mega Park, keep a reference to it - if (scenario->ScenarioId == SC_MEGA_PARK) - { - megaParkListItemIndex = _listItems.size() - 1; + DrawTextWrapped(dpi, screenPos + ScreenCoordsXY{ 0, 15 }, 170, STR_SCENARIO_HOVER_HINT); } + return; + } + + // Scenario path + if (gConfigGeneral.DebuggingTools) + { + const auto shortPath = ShortenPath(scenario->Path, width - 6 - TabWidth, FontStyle::Medium); + + auto ft = Formatter(); + ft.Add(shortPath.c_str()); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ TabWidth + 3, height - 3 - 11 }, STR_STRING, ft, { colours[1] }); + } + + // Scenario name + auto screenPos = windowPos + + ScreenCoordsXY{ widgets[WIDX_SCENARIOLIST].right + 4, widgets[WIDX_TABCONTENT].top + 5 }; + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(scenario->Name); + DrawTextEllipsised( + dpi, screenPos + ScreenCoordsXY{ 85, 0 }, 170, STR_WINDOW_COLOUR_2_STRINGID, ft, { TextAlignment::CENTRE }); + screenPos.y += 15; + + // Scenario details + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(scenario->Details); + screenPos.y += DrawTextWrapped(dpi, screenPos, 170, STR_BLACK_STRING, ft) + 5; + + // Scenario objective + ft = Formatter(); + ft.Add(ObjectiveNames[scenario->ObjectiveType]); + if (scenario->ObjectiveType == OBJECTIVE_BUILD_THE_BEST) + { + StringId rideTypeString = STR_NONE; + auto rideTypeId = scenario->ObjectiveArg3; + if (rideTypeId != RIDE_TYPE_NULL && rideTypeId < RIDE_TYPE_COUNT) + { + rideTypeString = GetRideTypeDescriptor(rideTypeId).Naming.Name; + } + ft.Add(rideTypeString); } else { - scenarioItem.scenario.is_locked = false; + ft.Add(scenario->ObjectiveArg3); + ft.Add(DateGetTotalMonths(MONTH_OCTOBER, scenario->ObjectiveArg1)); + if (scenario->ObjectiveType == OBJECTIVE_FINISH_5_ROLLERCOASTERS) + ft.Add(scenario->ObjectiveArg2); + else + ft.Add(scenario->ObjectiveArg2); + } + screenPos.y += DrawTextWrapped(dpi, screenPos, 170, STR_OBJECTIVE, ft) + 5; + + // Scenario score + if (scenario->Highscore != nullptr) + { + // TODO: Should probably be translatable + u8string completedByName = "???"; + if (!scenario->Highscore->name.empty()) + { + completedByName = scenario->Highscore->name; + } + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(completedByName.c_str()); + ft.Add(scenario->Highscore->company_value); + screenPos.y += DrawTextWrapped(dpi, screenPos, 170, STR_COMPLETED_BY_WITH_COMPANY_VALUE, ft); } - _listItems.push_back(std::move(scenarioItem)); } - // Mega park handling - if (megaParkListItemIndex.has_value()) + void OnPrepareDraw() override { - bool megaParkLocked = (rct1CompletedScenarios & rct1RequiredCompletedScenarios) != rct1RequiredCompletedScenarios; - _listItems[megaParkListItemIndex.value()].scenario.is_locked = megaParkLocked; - if (megaParkLocked && gConfigGeneral.ScenarioHideMegaPark) - { - // Remove mega park - _listItems.pop_back(); + pressed_widgets &= ~( + (1uLL << WIDX_CLOSE) | (1uLL << WIDX_TAB1) | (1uLL << WIDX_TAB2) | (1uLL << WIDX_TAB3) | (1uLL << WIDX_TAB4) + | (1uLL << WIDX_TAB5) | (1uLL << WIDX_TAB6) | (1uLL << WIDX_TAB7) | (1uLL << WIDX_TAB8) | (1uLL << WIDX_TAB9) + | (1uLL << WIDX_TAB10)); - // Remove empty headings - for (auto it = _listItems.begin(); it != _listItems.end();) + pressed_widgets |= 1LL << (selected_tab + WIDX_TAB1); + + ResizeFrameWithPage(); + const int32_t bottomMargin = gConfigGeneral.DebuggingTools ? 17 : 5; + widgets[WIDX_SCENARIOLIST].right = width - 179; + widgets[WIDX_SCENARIOLIST].bottom = height - bottomMargin; + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + const int32_t scenarioItemHeight = GetScenarioListItemSize(); + + int32_t y = 0; + for (const auto& listItem : _listItems) + { + switch (listItem.type) { - const auto& listItem = *it; - if (listItem.type == ListItemType::Heading) - { - auto nextIt = std::next(it); - if (nextIt == _listItems.end() || nextIt->type == ListItemType::Heading) + case ListItemType::Heading: + y += 18; + break; + case ListItemType::Scenario: + y += scenarioItemHeight; + break; + } + } + + return { WW, y }; + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + const int32_t scenarioItemHeight = GetScenarioListItemSize(); + + bool originalShowLockedInformation = _showLockedInformation; + _showLockedInformation = false; + const ScenarioIndexEntry* selected = nullptr; + auto mutableScreenCoords = screenCoords; + for (const auto& listItem : _listItems) + { + switch (listItem.type) + { + case ListItemType::Heading: + mutableScreenCoords.y -= 18; + break; + case ListItemType::Scenario: + mutableScreenCoords.y -= scenarioItemHeight; + if (mutableScreenCoords.y < 0) { - it = _listItems.erase(it); - continue; + if (listItem.scenario.is_locked) + { + _showLockedInformation = true; + } + else + { + selected = listItem.scenario.scenario; + } + } + break; + } + if (mutableScreenCoords.y < 0) + { + break; + } + } + + if (_highlightedScenario != selected) + { + _highlightedScenario = selected; + Invalidate(); + } + else if (_showLockedInformation != originalShowLockedInformation) + { + Invalidate(); + } + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + const int32_t scenarioItemHeight = GetScenarioListItemSize(); + + auto mutableScreenCoords = screenCoords; + for (const auto& listItem : _listItems) + { + switch (listItem.type) + { + case ListItemType::Heading: + mutableScreenCoords.y -= 18; + break; + case ListItemType::Scenario: + mutableScreenCoords.y -= scenarioItemHeight; + if (mutableScreenCoords.y < 0 && !listItem.scenario.is_locked) + { + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); + gFirstTimeSaving = true; + // Callback will likely close this window! So should always return after it. + _callback(listItem.scenario.scenario->Path); + return; + } + break; + } + if (mutableScreenCoords.y < 0) + { + break; + } + } + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; + GfxClear(&dpi, paletteIndex); + + StringId highlighted_format = ScenarioSelectUseSmallFont() ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; + StringId unhighlighted_format = ScenarioSelectUseSmallFont() ? STR_WHITE_STRING : STR_BLACK_STRING; + + const auto& listWidget = widgets[WIDX_SCENARIOLIST]; + int32_t listWidth = listWidget.width() - 12; + + const int32_t scenarioItemHeight = GetScenarioListItemSize(); + + // Scenario title + int32_t scenarioTitleHeight = FontGetLineHeight(FontStyle::Medium); + + int32_t y = 0; + for (const auto& listItem : _listItems) + { + if (y > dpi.y + dpi.height) + { + continue; + } + + switch (listItem.type) + { + case ListItemType::Heading: + { + const int32_t horizontalRuleMargin = 4; + DrawCategoryHeading( + dpi, horizontalRuleMargin, listWidth - horizontalRuleMargin, y + 2, listItem.heading.string_id); + y += 18; + break; + } + case ListItemType::Scenario: + { + // Draw hover highlight + const ScenarioIndexEntry* scenario = listItem.scenario.scenario; + bool isHighlighted = _highlightedScenario == scenario; + if (isHighlighted) + { + GfxFilterRect(dpi, { 0, y, width, y + scenarioItemHeight - 1 }, FilterPaletteID::PaletteDarken1); + } + + bool isCompleted = scenario->Highscore != nullptr; + bool isDisabled = listItem.scenario.is_locked; + + // Draw scenario name + char buffer[64]; + SafeStrCpy(buffer, scenario->Name, sizeof(buffer)); + StringId format = isDisabled ? static_cast(STR_STRINGID) + : (isHighlighted ? highlighted_format : unhighlighted_format); + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(buffer); + colour_t colour = isDisabled ? colours[1] | COLOUR_FLAG_INSET : COLOUR_BLACK; + auto darkness = isDisabled ? TextDarkness::Dark : TextDarkness::Regular; + const auto scrollCentre = widgets[WIDX_SCENARIOLIST].width() / 2; + + DrawTextBasic( + dpi, { scrollCentre, y + 1 }, format, ft, + { colour, FontStyle::Medium, TextAlignment::CENTRE, darkness }); + + // Check if scenario is completed + if (isCompleted) + { + // Draw completion tick + GfxDrawSprite(dpi, ImageId(SPR_MENU_CHECKMARK), { widgets[WIDX_SCENARIOLIST].width() - 45, y + 1 }); + + // Draw completion score + u8string completedByName = "???"; + if (!scenario->Highscore->name.empty()) + { + completedByName = scenario->Highscore->name; + } + ft = Formatter(); + ft.Add(STR_COMPLETED_BY); + ft.Add(STR_STRING); + ft.Add(completedByName.c_str()); + DrawTextBasic( + dpi, { scrollCentre, y + scenarioTitleHeight + 1 }, format, ft, + { FontStyle::Small, TextAlignment::CENTRE }); + } + + y += scenarioItemHeight; + break; + } + } + } + } + + private: + void DrawCategoryHeading(DrawPixelInfo& dpi, int32_t left, int32_t right, int32_t y, StringId stringId) const + { + colour_t baseColour = colours[1]; + colour_t lightColour = ColourMapA[baseColour].lighter; + colour_t darkColour = ColourMapA[baseColour].mid_dark; + + // Draw string + int32_t centreX = (left + right) / 2; + DrawTextBasic(dpi, { centreX, y }, stringId, {}, { baseColour, TextAlignment::CENTRE }); + + // Get string dimensions + utf8 buffer[CommonTextBufferSize]; + auto bufferPtr = buffer; + OpenRCT2::FormatStringLegacy(bufferPtr, sizeof(buffer), stringId, nullptr); + int32_t categoryStringHalfWidth = (GfxGetStringWidth(bufferPtr, FontStyle::Medium) / 2) + 4; + int32_t strLeft = centreX - categoryStringHalfWidth; + int32_t strRight = centreX + categoryStringHalfWidth; + + // Draw light horizontal rule + int32_t lineY = y + 4; + auto lightLineLeftTop1 = ScreenCoordsXY{ left, lineY }; + auto lightLineRightBottom1 = ScreenCoordsXY{ strLeft, lineY }; + GfxDrawLine(dpi, { lightLineLeftTop1, lightLineRightBottom1 }, lightColour); + + auto lightLineLeftTop2 = ScreenCoordsXY{ strRight, lineY }; + auto lightLineRightBottom2 = ScreenCoordsXY{ right, lineY }; + GfxDrawLine(dpi, { lightLineLeftTop2, lightLineRightBottom2 }, lightColour); + + // Draw dark horizontal rule + lineY++; + auto darkLineLeftTop1 = ScreenCoordsXY{ left, lineY }; + auto darkLineRightBottom1 = ScreenCoordsXY{ strLeft, lineY }; + GfxDrawLine(dpi, { darkLineLeftTop1, darkLineRightBottom1 }, darkColour); + + auto darkLineLeftTop2 = ScreenCoordsXY{ strRight, lineY }; + auto darkLineRightBottom2 = ScreenCoordsXY{ right, lineY }; + GfxDrawLine(dpi, { darkLineLeftTop2, darkLineRightBottom2 }, darkColour); + } + + void InitialiseListItems() + { + size_t numScenarios = ScenarioRepositoryGetCount(); + _listItems.clear(); + + // Mega park unlock + const uint32_t rct1RequiredCompletedScenarios = (1 << SC_MEGA_PARK) - 1; + uint32_t rct1CompletedScenarios = 0; + std::optional megaParkListItemIndex = std::nullopt; + + int32_t numUnlocks = InitialNumUnlockedScenarios; + uint8_t currentHeading = UINT8_MAX; + for (size_t i = 0; i < numScenarios; i++) + { + const ScenarioIndexEntry* scenario = ScenarioRepositoryGetByIndex(i); + + if (!IsScenarioVisible(*scenario)) + continue; + + // Category heading + StringId headingStringId = STR_NONE; + if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) + { + if (selected_tab != static_cast(ScenarioSource::Real) && currentHeading != scenario->Category) + { + currentHeading = scenario->Category; + headingStringId = ScenarioCategoryStringIds[currentHeading]; + } + } + else + { + if (selected_tab <= SCENARIO_CATEGORY_EXPERT) + { + if (currentHeading != static_cast(scenario->SourceGame)) + { + currentHeading = static_cast(scenario->SourceGame); + headingStringId = kScenarioOriginStringIds[currentHeading]; } } - ++it; + else if (selected_tab == SCENARIO_CATEGORY_OTHER) + { + int32_t category = scenario->Category; + if (category <= SCENARIO_CATEGORY_REAL) + { + category = SCENARIO_CATEGORY_OTHER; + } + if (currentHeading != category) + { + currentHeading = category; + headingStringId = ScenarioCategoryStringIds[category]; + } + } + } + + if (headingStringId != STR_NONE) + { + ScenarioListItem headerItem; + headerItem.type = ListItemType::Heading; + headerItem.heading.string_id = headingStringId; + _listItems.push_back(std::move(headerItem)); + } + + // Scenario + ScenarioListItem scenarioItem; + scenarioItem.type = ListItemType::Scenario; + scenarioItem.scenario.scenario = scenario; + if (IsLockingEnabled()) + { + scenarioItem.scenario.is_locked = numUnlocks <= 0; + if (scenario->Highscore == nullptr) + { + numUnlocks--; + } + else + { + // Mark RCT1 scenario as completed + if (scenario->ScenarioId < SC_MEGA_PARK) + { + rct1CompletedScenarios |= 1 << scenario->ScenarioId; + } + } + + // If scenario is Mega Park, keep a reference to it + if (scenario->ScenarioId == SC_MEGA_PARK) + { + megaParkListItemIndex = _listItems.size() - 1; + } + } + else + { + scenarioItem.scenario.is_locked = false; + } + _listItems.push_back(std::move(scenarioItem)); + } + + // Mega park handling + if (megaParkListItemIndex.has_value()) + { + bool megaParkLocked = (rct1CompletedScenarios & rct1RequiredCompletedScenarios) + != rct1RequiredCompletedScenarios; + _listItems[megaParkListItemIndex.value()].scenario.is_locked = megaParkLocked; + if (megaParkLocked && gConfigGeneral.ScenarioHideMegaPark) + { + // Remove mega park + _listItems.pop_back(); + + // Remove empty headings + for (auto it = _listItems.begin(); it != _listItems.end();) + { + const auto& listItem = *it; + if (listItem.type == ListItemType::Heading) + { + auto nextIt = std::next(it); + if (nextIt == _listItems.end() || nextIt->type == ListItemType::Heading) + { + it = _listItems.erase(it); + continue; + } + } + ++it; + } } } } - } - bool IsScenarioVisible(const ScenarioIndexEntry& scenario) const - { - if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) + bool IsScenarioVisible(const ScenarioIndexEntry& scenario) const { - if (static_cast(scenario.SourceGame) != selected_tab) - { - return false; - } - } - else - { - int32_t category = scenario.Category; - if (category > SCENARIO_CATEGORY_OTHER) - { - category = SCENARIO_CATEGORY_OTHER; - } - if (category != selected_tab) - { - return false; - } - } - return true; - } - - bool IsLockingEnabled() const - { - if (gConfigGeneral.ScenarioSelectMode != SCENARIO_SELECT_MODE_ORIGIN) - return false; - if (!gConfigGeneral.ScenarioUnlockingEnabled) - return false; - if (selected_tab >= 6) - return false; - - return true; - } - - void InitTabs() - { - int32_t showPages = 0; - size_t numScenarios = ScenarioRepositoryGetCount(); - for (size_t i = 0; i < numScenarios; i++) - { - const ScenarioIndexEntry* scenario = ScenarioRepositoryGetByIndex(i); if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) { - showPages |= 1 << static_cast(scenario->SourceGame); + if (static_cast(scenario.SourceGame) != selected_tab) + { + return false; + } } else { - int32_t category = scenario->Category; + int32_t category = scenario.Category; if (category > SCENARIO_CATEGORY_OTHER) { category = SCENARIO_CATEGORY_OTHER; } - showPages |= 1 << category; + if (category != selected_tab) + { + return false; + } } + return true; } - if (showPages & (1 << gConfigInterface.ScenarioselectLastTab)) + bool IsLockingEnabled() const { - selected_tab = gConfigInterface.ScenarioselectLastTab; + if (gConfigGeneral.ScenarioSelectMode != SCENARIO_SELECT_MODE_ORIGIN) + return false; + if (!gConfigGeneral.ScenarioUnlockingEnabled) + return false; + if (selected_tab >= 6) + return false; + + return true; } - else + + void InitTabs() { - int32_t firstPage = UtilBitScanForward(showPages); - if (firstPage != -1) + int32_t showPages = 0; + size_t numScenarios = ScenarioRepositoryGetCount(); + for (size_t i = 0; i < numScenarios; i++) { - selected_tab = firstPage; + const ScenarioIndexEntry* scenario = ScenarioRepositoryGetByIndex(i); + if (gConfigGeneral.ScenarioSelectMode == SCENARIO_SELECT_MODE_ORIGIN) + { + showPages |= 1 << static_cast(scenario->SourceGame); + } + else + { + int32_t category = scenario->Category; + if (category > SCENARIO_CATEGORY_OTHER) + { + category = SCENARIO_CATEGORY_OTHER; + } + showPages |= 1 << category; + } } - } - int32_t y = TabsStart; - for (int32_t i = 0; i < NumTabs; i++) - { - auto& widget = widgets[i + WIDX_TAB1]; - if (!(showPages & (1 << i))) + if (showPages & (1 << gConfigInterface.ScenarioselectLastTab)) { - widget.type = WindowWidgetType::Empty; - continue; + selected_tab = gConfigInterface.ScenarioselectLastTab; + } + else + { + int32_t firstPage = UtilBitScanForward(showPages); + if (firstPage != -1) + { + selected_tab = firstPage; + } } - widget.type = WindowWidgetType::Tab; - widget.top = y; - widget.bottom = y + (TabHeight - 1); - y += TabHeight; + int32_t y = TabsStart; + for (int32_t i = 0; i < NumTabs; i++) + { + auto& widget = widgets[i + WIDX_TAB1]; + if (!(showPages & (1 << i))) + { + widget.type = WindowWidgetType::Empty; + continue; + } + + widget.type = WindowWidgetType::Tab; + widget.top = y; + widget.bottom = y + (TabHeight - 1); + y += TabHeight; + } } + + static bool ScenarioSelectUseSmallFont() + { + return ThemeGetFlags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT; + } + + static int32_t GetScenarioListItemSize() + { + if (!LocalisationService_UseTrueTypeFont()) + return TrueFontSize; + + // Scenario title + int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + + // 'Completed by' line + lineHeight += FontGetLineHeight(FontStyle::Small); + + return lineHeight; + } + }; + + WindowBase* WindowScenarioselectOpen(scenarioselect_callback callback) + { + return WindowScenarioselectOpen([callback](std::string_view scenario) { callback(std::string(scenario).c_str()); }); } - static bool ScenarioSelectUseSmallFont() + WindowBase* WindowScenarioselectOpen(std::function callback) { - return ThemeGetFlags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT; - } + auto* window = static_cast(WindowBringToFrontByClass(WindowClass::ScenarioSelect)); + if (window != nullptr) + { + return window; + } - static int32_t GetScenarioListItemSize() - { - if (!LocalisationService_UseTrueTypeFont()) - return TrueFontSize; - - // Scenario title - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - - // 'Completed by' line - lineHeight += FontGetLineHeight(FontStyle::Small); - - return lineHeight; - } -}; - -WindowBase* WindowScenarioselectOpen(scenarioselect_callback callback) -{ - return WindowScenarioselectOpen([callback](std::string_view scenario) { callback(std::string(scenario).c_str()); }); -} - -WindowBase* WindowScenarioselectOpen(std::function callback) -{ - auto* window = static_cast(WindowBringToFrontByClass(WindowClass::ScenarioSelect)); - if (window != nullptr) - { + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + ScreenCoordsXY screenPos = { (screenWidth - WW) / 2, std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - WH) / 2) }; + window = WindowCreate(WindowClass::ScenarioSelect, screenPos, WW, WH, 0, callback); return window; } - - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - ScreenCoordsXY screenPos = { (screenWidth - WW) / 2, std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - WH) / 2) }; - window = WindowCreate(WindowClass::ScenarioSelect, screenPos, WW, WH, 0, callback); - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Scenery.cpp b/src/openrct2-ui/windows/Scenery.cpp index 75250faffc..5b8f444d42 100644 --- a/src/openrct2-ui/windows/Scenery.cpp +++ b/src/openrct2-ui/windows/Scenery.cpp @@ -38,48 +38,48 @@ #include #include -using namespace OpenRCT2; - -static constexpr StringId WINDOW_TITLE = STR_NONE; -constexpr int32_t WINDOW_SCENERY_MIN_WIDTH = 634; -constexpr int32_t WINDOW_SCENERY_MIN_HEIGHT = 195; -constexpr int32_t SCENERY_BUTTON_WIDTH = 66; -constexpr int32_t SCENERY_BUTTON_HEIGHT = 80; -constexpr int32_t InitTabPosX = 3; -constexpr int32_t InitTabPosY = 17; -constexpr int32_t TabWidth = 31; -constexpr int32_t TabHeight = 28; -constexpr int32_t ReservedTabCount = 2; -constexpr int32_t MaxTabs = 257; // 255 selected tabs + misc + all -constexpr int32_t MaxTabsPerRow = 20; - -constexpr uint8_t SceneryContentScrollIndex = 0; - -enum WindowSceneryListWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_SCENERY_BACKGROUND, - WIDX_SCENERY_TITLE, - WIDX_SCENERY_CLOSE, - WIDX_SCENERY_TAB_CONTENT_PANEL, - WIDX_SCENERY_LIST, - WIDX_SCENERY_ROTATE_OBJECTS_BUTTON, - WIDX_SCENERY_REPAINT_SCENERY_BUTTON, - WIDX_SCENERY_PRIMARY_COLOUR_BUTTON, - WIDX_SCENERY_SECONDARY_COLOUR_BUTTON, - WIDX_SCENERY_TERTIARY_COLOUR_BUTTON, - WIDX_SCENERY_EYEDROPPER_BUTTON, - WIDX_SCENERY_BUILD_CLUSTER_BUTTON, - WIDX_FILTER_TEXT_BOX, - WIDX_FILTER_CLEAR_BUTTON, - WIDX_RESTRICT_SCENERY, - WIDX_SCENERY_TAB_1, -}; + static constexpr StringId WINDOW_TITLE = STR_NONE; + constexpr int32_t WINDOW_SCENERY_MIN_WIDTH = 634; + constexpr int32_t WINDOW_SCENERY_MIN_HEIGHT = 195; + constexpr int32_t SCENERY_BUTTON_WIDTH = 66; + constexpr int32_t SCENERY_BUTTON_HEIGHT = 80; + constexpr int32_t InitTabPosX = 3; + constexpr int32_t InitTabPosY = 17; + constexpr int32_t TabWidth = 31; + constexpr int32_t TabHeight = 28; + constexpr int32_t ReservedTabCount = 2; + constexpr int32_t MaxTabs = 257; // 255 selected tabs + misc + all + constexpr int32_t MaxTabsPerRow = 20; -validate_global_widx(WC_SCENERY, WIDX_SCENERY_TAB_1); -validate_global_widx(WC_SCENERY, WIDX_SCENERY_ROTATE_OBJECTS_BUTTON); -validate_global_widx(WC_SCENERY, WIDX_SCENERY_EYEDROPPER_BUTTON); + constexpr uint8_t SceneryContentScrollIndex = 0; -// clang-format off + enum WindowSceneryListWidgetIdx + { + WIDX_SCENERY_BACKGROUND, + WIDX_SCENERY_TITLE, + WIDX_SCENERY_CLOSE, + WIDX_SCENERY_TAB_CONTENT_PANEL, + WIDX_SCENERY_LIST, + WIDX_SCENERY_ROTATE_OBJECTS_BUTTON, + WIDX_SCENERY_REPAINT_SCENERY_BUTTON, + WIDX_SCENERY_PRIMARY_COLOUR_BUTTON, + WIDX_SCENERY_SECONDARY_COLOUR_BUTTON, + WIDX_SCENERY_TERTIARY_COLOUR_BUTTON, + WIDX_SCENERY_EYEDROPPER_BUTTON, + WIDX_SCENERY_BUILD_CLUSTER_BUTTON, + WIDX_FILTER_TEXT_BOX, + WIDX_FILTER_CLEAR_BUTTON, + WIDX_RESTRICT_SCENERY, + WIDX_SCENERY_TAB_1, + }; + + validate_global_widx(WC_SCENERY, WIDX_SCENERY_TAB_1); + validate_global_widx(WC_SCENERY, WIDX_SCENERY_ROTATE_OBJECTS_BUTTON); + validate_global_widx(WC_SCENERY, WIDX_SCENERY_EYEDROPPER_BUTTON); + + // clang-format off static Widget WindowSceneryBaseWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WINDOW_SCENERY_MIN_WIDTH, WINDOW_SCENERY_MIN_HEIGHT), MakeWidget ({ 0, 43}, {634, 99}, WindowWidgetType::Resize, WindowColour::Secondary ), // 8 0x009DE2C8 @@ -96,1657 +96,1670 @@ static Widget WindowSceneryBaseWidgets[] = { MakeWidget ({539, 46}, { 70, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_RESTRICT_SCENERY, STR_RESTRICT_SCENERY_TIP ), kWidgetsEnd, }; -// clang-format on + // clang-format on -// Persistent between window instances -static size_t _activeTabIndex; -static std::vector _tabSelections; + // Persistent between window instances + static size_t _activeTabIndex; + static std::vector _tabSelections; -uint8_t gWindowSceneryPaintEnabled; -uint8_t gWindowSceneryRotation; -colour_t gWindowSceneryPrimaryColour; -colour_t gWindowScenerySecondaryColour; -colour_t gWindowSceneryTertiaryColour; -bool gWindowSceneryEyedropperEnabled; + uint8_t gWindowSceneryPaintEnabled; + uint8_t gWindowSceneryRotation; + colour_t gWindowSceneryPrimaryColour; + colour_t gWindowScenerySecondaryColour; + colour_t gWindowSceneryTertiaryColour; + bool gWindowSceneryEyedropperEnabled; -class SceneryWindow final : public Window -{ -private: - enum SceneryTabType + class SceneryWindow final : public Window { - SCENERY_TAB_TYPE_GROUP, - SCENERY_TAB_TYPE_MISC, - SCENERY_TAB_TYPE_ALL, - }; - - struct SceneryItem - { - int32_t allRows; - int32_t selected_item; - ScenerySelection scenerySelection; - }; - - struct SceneryTabInfo - { - SceneryTabType Type = SCENERY_TAB_TYPE_GROUP; - ObjectEntryIndex SceneryGroupIndex = OBJECT_ENTRY_INDEX_NULL; - std::deque Entries{}; - u8string Filter = ""; - - bool IsMisc() const + private: + enum SceneryTabType { - return Type == SCENERY_TAB_TYPE_MISC; - } + SCENERY_TAB_TYPE_GROUP, + SCENERY_TAB_TYPE_MISC, + SCENERY_TAB_TYPE_ALL, + }; - bool IsAll() const + struct SceneryItem { - return Type == SCENERY_TAB_TYPE_ALL; - } + int32_t allRows; + int32_t selected_item; + ScenerySelection scenerySelection; + }; - bool IsSceneryGroup() const + struct SceneryTabInfo { - return Type == SCENERY_TAB_TYPE_GROUP; - } + SceneryTabType Type = SCENERY_TAB_TYPE_GROUP; + ObjectEntryIndex SceneryGroupIndex = OBJECT_ENTRY_INDEX_NULL; + std::deque Entries{}; + u8string Filter = ""; - bool Contains(const ScenerySelection& entry) const - { - return std::find(std::begin(Entries), std::end(Entries), entry) != std::end(Entries); - } - - void AddEntryToBack(const ScenerySelection& entry) - { - if (!Contains(entry)) + bool IsMisc() const { - Entries.push_back(entry); + return Type == SCENERY_TAB_TYPE_MISC; + } + + bool IsAll() const + { + return Type == SCENERY_TAB_TYPE_ALL; + } + + bool IsSceneryGroup() const + { + return Type == SCENERY_TAB_TYPE_GROUP; + } + + bool Contains(const ScenerySelection& entry) const + { + return std::find(std::begin(Entries), std::end(Entries), entry) != std::end(Entries); + } + + void AddEntryToBack(const ScenerySelection& entry) + { + if (!Contains(entry)) + { + Entries.push_back(entry); + } + } + + void AddEntryToFront(const ScenerySelection& entry) + { + if (!Contains(entry)) + { + Entries.push_front(entry); + } + } + + const SceneryGroupEntry* GetSceneryGroupEntry() const + { + return OpenRCT2::ObjectManager::GetObjectEntry(SceneryGroupIndex); + } + }; + + std::vector _tabEntries; + std::vector _widgets; + int32_t _requiredWidth; + int32_t _actualMinHeight; + ScenerySelection _selectedScenery; + int16_t _hoverCounter; + SceneryTabInfo _filteredSceneryTab; + + public: + void OnOpen() override + { + Init(); + + InitScrollWidgets(); + ContentUpdateScroll(); + ShowGridlines(); + gWindowSceneryRotation = 3; + gSceneryCtrlPressed = false; + gSceneryShiftPressed = false; + _selectedScenery = {}; + _hoverCounter = 0; + gSceneryGhostType = 0; + gSceneryPlaceCost = kMoney64Undefined; + gSceneryPlaceRotation = 0; + gWindowSceneryPaintEnabled = 0; // repaint coloured scenery tool state + gWindowSceneryEyedropperEnabled = false; + + width = GetRequiredWidth(); + min_width = width; + max_width = width; + height = _actualMinHeight; + min_height = height; + max_height = height; + if (_activeTabIndex > _tabSelections.size()) + { + _activeTabIndex = 0; + } + + WindowMovePosition(*this, { ContextGetWidth() - GetRequiredWidth(), 0x1D }); + WindowPushOthersBelow(*this); + } + + void OnClose() override + { + SceneryRemoveGhostToolPlacement(); + HideGridlines(); + ViewportSetVisibility(ViewportVisibility::Default); + + if (gWindowSceneryScatterEnabled) + WindowCloseByClass(WindowClass::SceneryScatter); + + if (SceneryToolIsActive()) + ToolCancel(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_SCENERY_CLOSE: + Close(); + break; + case WIDX_SCENERY_ROTATE_OBJECTS_BUTTON: + gWindowSceneryRotation++; + gWindowSceneryRotation = gWindowSceneryRotation % 4; + SceneryRemoveGhostToolPlacement(); + Invalidate(); + break; + case WIDX_SCENERY_REPAINT_SCENERY_BUTTON: + gWindowSceneryPaintEnabled ^= 1; + gWindowSceneryEyedropperEnabled = false; + if (gWindowSceneryScatterEnabled) + WindowCloseByClass(WindowClass::SceneryScatter); + Invalidate(); + break; + case WIDX_SCENERY_EYEDROPPER_BUTTON: + gWindowSceneryPaintEnabled = 0; + gWindowSceneryEyedropperEnabled = !gWindowSceneryEyedropperEnabled; + if (gWindowSceneryScatterEnabled) + WindowCloseByClass(WindowClass::SceneryScatter); + SceneryRemoveGhostToolPlacement(); + Invalidate(); + break; + case WIDX_SCENERY_BUILD_CLUSTER_BUTTON: + gWindowSceneryPaintEnabled = 0; + gWindowSceneryEyedropperEnabled = false; + if (gWindowSceneryScatterEnabled) + WindowCloseByClass(WindowClass::SceneryScatter); + else if ( + NetworkGetMode() != NETWORK_MODE_CLIENT + || NetworkCanPerformCommand(NetworkGetCurrentPlayerGroupIndex(), -2)) + { + WindowSceneryScatterOpen(); + } + else + { + ContextShowError(STR_CANT_DO_THIS, STR_PERMISSION_DENIED, {}); + } + Invalidate(); + break; + case WIDX_FILTER_TEXT_BOX: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _filteredSceneryTab.Filter.data(), TEXT_INPUT_SIZE); + break; + case WIDX_FILTER_CLEAR_BUTTON: + _tabEntries[_activeTabIndex].Filter.clear(); + ContentUpdateScroll(); + scrolls->v_top = 0; + Invalidate(); + break; + case WIDX_RESTRICT_SCENERY: + { + const auto tabIndex = _activeTabIndex; + const auto tabSelectedScenery = GetSelectedScenery(tabIndex); + if (!tabSelectedScenery.IsUndefined()) + { + const auto newStatus = !IsSceneryItemRestricted(tabSelectedScenery); + const auto objectType = GetObjectTypeFromSceneryType(tabSelectedScenery.SceneryType); + auto action = ScenerySetRestrictedAction(objectType, tabSelectedScenery.EntryIndex, newStatus); + GameActions::Execute(&action); + } + break; + } } } - void AddEntryToFront(const ScenerySelection& entry) + void OnResize() override { - if (!Contains(entry)) + if (width < min_width) { - Entries.push_front(entry); + Invalidate(); + width = min_width; + Invalidate(); } - } - const SceneryGroupEntry* GetSceneryGroupEntry() const - { - return OpenRCT2::ObjectManager::GetObjectEntry(SceneryGroupIndex); - } - }; - - std::vector _tabEntries; - std::vector _widgets; - int32_t _requiredWidth; - int32_t _actualMinHeight; - ScenerySelection _selectedScenery; - int16_t _hoverCounter; - SceneryTabInfo _filteredSceneryTab; - -public: - void OnOpen() override - { - Init(); - - InitScrollWidgets(); - ContentUpdateScroll(); - ShowGridlines(); - gWindowSceneryRotation = 3; - gSceneryCtrlPressed = false; - gSceneryShiftPressed = false; - _selectedScenery = {}; - _hoverCounter = 0; - gSceneryGhostType = 0; - gSceneryPlaceCost = kMoney64Undefined; - gSceneryPlaceRotation = 0; - gWindowSceneryPaintEnabled = 0; // repaint coloured scenery tool state - gWindowSceneryEyedropperEnabled = false; - - width = GetRequiredWidth(); - min_width = width; - max_width = width; - height = _actualMinHeight; - min_height = height; - max_height = height; - if (_activeTabIndex > _tabSelections.size()) - { - _activeTabIndex = 0; - } - - WindowMovePosition(*this, { ContextGetWidth() - GetRequiredWidth(), 0x1D }); - WindowPushOthersBelow(*this); - } - - void OnClose() override - { - SceneryRemoveGhostToolPlacement(); - HideGridlines(); - ViewportSetVisibility(ViewportVisibility::Default); - - if (gWindowSceneryScatterEnabled) - WindowCloseByClass(WindowClass::SceneryScatter); - - if (SceneryToolIsActive()) - ToolCancel(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_SCENERY_CLOSE: - Close(); - break; - case WIDX_SCENERY_ROTATE_OBJECTS_BUTTON: - gWindowSceneryRotation++; - gWindowSceneryRotation = gWindowSceneryRotation % 4; - SceneryRemoveGhostToolPlacement(); + if (width > max_width) + { Invalidate(); - break; - case WIDX_SCENERY_REPAINT_SCENERY_BUTTON: - gWindowSceneryPaintEnabled ^= 1; - gWindowSceneryEyedropperEnabled = false; - if (gWindowSceneryScatterEnabled) - WindowCloseByClass(WindowClass::SceneryScatter); + width = max_width; Invalidate(); - break; - case WIDX_SCENERY_EYEDROPPER_BUTTON: - gWindowSceneryPaintEnabled = 0; - gWindowSceneryEyedropperEnabled = !gWindowSceneryEyedropperEnabled; - if (gWindowSceneryScatterEnabled) - WindowCloseByClass(WindowClass::SceneryScatter); - SceneryRemoveGhostToolPlacement(); + } + + if (height < min_height) + { Invalidate(); - break; - case WIDX_SCENERY_BUILD_CLUSTER_BUTTON: - gWindowSceneryPaintEnabled = 0; - gWindowSceneryEyedropperEnabled = false; - if (gWindowSceneryScatterEnabled) - WindowCloseByClass(WindowClass::SceneryScatter); - else if ( - NetworkGetMode() != NETWORK_MODE_CLIENT - || NetworkCanPerformCommand(NetworkGetCurrentPlayerGroupIndex(), -2)) - { - WindowSceneryScatterOpen(); - } - else - { - ContextShowError(STR_CANT_DO_THIS, STR_PERMISSION_DENIED, {}); - } + height = min_height; Invalidate(); - break; - case WIDX_FILTER_TEXT_BOX: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _filteredSceneryTab.Filter.data(), TEXT_INPUT_SIZE); - break; - case WIDX_FILTER_CLEAR_BUTTON: - _tabEntries[_activeTabIndex].Filter.clear(); + // HACK: For some reason invalidate has not been called + OnPrepareDraw(); ContentUpdateScroll(); - scrolls->v_top = 0; + } + + if (height > max_height) + { Invalidate(); - break; - case WIDX_RESTRICT_SCENERY: + height = max_height; + Invalidate(); + // HACK: For some reason invalidate has not been called + OnPrepareDraw(); + ContentUpdateScroll(); + } + + ResizeFrameWithPage(); + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_SCENERY_PRIMARY_COLOUR_BUTTON: + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], gWindowSceneryPrimaryColour); + break; + case WIDX_SCENERY_SECONDARY_COLOUR_BUTTON: + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], gWindowScenerySecondaryColour); + break; + case WIDX_SCENERY_TERTIARY_COLOUR_BUTTON: + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], gWindowSceneryTertiaryColour); + break; + } + + if (widgetIndex >= WIDX_SCENERY_TAB_1) + { + _activeTabIndex = widgetIndex - WIDX_SCENERY_TAB_1; + Invalidate(); + gSceneryPlaceCost = kMoney64Undefined; + + ContentUpdateScroll(); + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (dropdownIndex == -1) + return; + + if (widgetIndex == WIDX_SCENERY_PRIMARY_COLOUR_BUTTON) + { + gWindowSceneryPrimaryColour = ColourDropDownIndexToColour(dropdownIndex); + } + else if (widgetIndex == WIDX_SCENERY_SECONDARY_COLOUR_BUTTON) + { + gWindowScenerySecondaryColour = ColourDropDownIndexToColour(dropdownIndex); + } + else if (widgetIndex == WIDX_SCENERY_TERTIARY_COLOUR_BUTTON) + { + gWindowSceneryTertiaryColour = ColourDropDownIndexToColour(dropdownIndex); + } + + Invalidate(); + } + + void OnPeriodicUpdate() override + { + if (!_selectedScenery.IsUndefined()) + { + // Find out what scenery the cursor is over + const CursorState* state = ContextGetCursorState(); + WidgetIndex widgetIndex = WindowFindWidgetFromPoint(*this, state->position); + if (widgetIndex == WIDX_SCENERY_LIST) + { + ScreenCoordsXY scrollPos = {}; + int32_t scrollArea = 0; + int32_t scrollId = 0; + WidgetScrollGetPart(*this, &widgets[WIDX_SCENERY_LIST], state->position, scrollPos, &scrollArea, &scrollId); + if (scrollArea == SCROLL_PART_VIEW) + { + const ScenerySelection scenery = GetSceneryIdByCursorPos(scrollPos); + if (scenery == _selectedScenery) + { + return; + } + } + } + + // Cursor was not over the currently hover selected scenery so reset hover selection. + // This will happen when the mouse leaves the scroll window and is required so that the cost and description + // switch to the tool scenery selection. + _selectedScenery = {}; + } + } + + void OnUpdate() override + { + const CursorState* state = ContextGetCursorState(); + WindowBase* other = WindowFindFromPoint(state->position); + if (other == this) + { + ScreenCoordsXY window = state->position - ScreenCoordsXY{ windowPos.x - 26, windowPos.y }; + + if (window.y < 44 || window.x <= width) + { + WidgetIndex widgetIndex = WindowFindWidgetFromPoint(*this, state->position); + if (widgetIndex >= WIDX_SCENERY_TAB_CONTENT_PANEL) + { + _hoverCounter++; + if (_hoverCounter < 8) + { + if (InputGetState() != InputState::ScrollLeft) + { + min_height = _actualMinHeight; + max_height = _actualMinHeight; + } + } + else + { + const auto& listWidget = widgets[WIDX_SCENERY_LIST]; + const auto nonListHeight = height - listWidget.height() + 12; + + const auto numRows = static_cast(CountRows()); + const auto maxContentHeight = numRows * SCENERY_BUTTON_HEIGHT; + const auto maxWindowHeight = maxContentHeight + nonListHeight; + const auto windowHeight = std::clamp(maxWindowHeight, _actualMinHeight, 473); + + min_height = windowHeight; + max_height = windowHeight; + } + } + } + } + else + { + _hoverCounter = 0; + if (InputGetState() != InputState::ScrollLeft) + { + min_height = _actualMinHeight; + max_height = _actualMinHeight; + } + } + + if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + { + WindowUpdateTextboxCaret(); + WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX); + } + + Invalidate(); + + if (!SceneryToolIsActive()) + { + Close(); + return; + } + + if (gWindowSceneryEyedropperEnabled) + { + gCurrentToolId = Tool::Crosshair; + } + else if (gWindowSceneryPaintEnabled == 1) + { + gCurrentToolId = Tool::PaintDown; + } + else { const auto tabIndex = _activeTabIndex; const auto tabSelectedScenery = GetSelectedScenery(tabIndex); if (!tabSelectedScenery.IsUndefined()) { - const auto newStatus = !IsSceneryItemRestricted(tabSelectedScenery); - const auto objectType = GetObjectTypeFromSceneryType(tabSelectedScenery.SceneryType); - auto action = ScenerySetRestrictedAction(objectType, tabSelectedScenery.EntryIndex, newStatus); - GameActions::Execute(&action); - } - break; - } - } - } - - void OnResize() override - { - if (width < min_width) - { - Invalidate(); - width = min_width; - Invalidate(); - } - - if (width > max_width) - { - Invalidate(); - width = max_width; - Invalidate(); - } - - if (height < min_height) - { - Invalidate(); - height = min_height; - Invalidate(); - // HACK: For some reason invalidate has not been called - OnPrepareDraw(); - ContentUpdateScroll(); - } - - if (height > max_height) - { - Invalidate(); - height = max_height; - Invalidate(); - // HACK: For some reason invalidate has not been called - OnPrepareDraw(); - ContentUpdateScroll(); - } - - ResizeFrameWithPage(); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_SCENERY_PRIMARY_COLOUR_BUTTON: - WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], gWindowSceneryPrimaryColour); - break; - case WIDX_SCENERY_SECONDARY_COLOUR_BUTTON: - WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], gWindowScenerySecondaryColour); - break; - case WIDX_SCENERY_TERTIARY_COLOUR_BUTTON: - WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], gWindowSceneryTertiaryColour); - break; - } - - if (widgetIndex >= WIDX_SCENERY_TAB_1) - { - _activeTabIndex = widgetIndex - WIDX_SCENERY_TAB_1; - Invalidate(); - gSceneryPlaceCost = kMoney64Undefined; - - ContentUpdateScroll(); - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (dropdownIndex == -1) - return; - - if (widgetIndex == WIDX_SCENERY_PRIMARY_COLOUR_BUTTON) - { - gWindowSceneryPrimaryColour = ColourDropDownIndexToColour(dropdownIndex); - } - else if (widgetIndex == WIDX_SCENERY_SECONDARY_COLOUR_BUTTON) - { - gWindowScenerySecondaryColour = ColourDropDownIndexToColour(dropdownIndex); - } - else if (widgetIndex == WIDX_SCENERY_TERTIARY_COLOUR_BUTTON) - { - gWindowSceneryTertiaryColour = ColourDropDownIndexToColour(dropdownIndex); - } - - Invalidate(); - } - - void OnPeriodicUpdate() override - { - if (!_selectedScenery.IsUndefined()) - { - // Find out what scenery the cursor is over - const CursorState* state = ContextGetCursorState(); - WidgetIndex widgetIndex = WindowFindWidgetFromPoint(*this, state->position); - if (widgetIndex == WIDX_SCENERY_LIST) - { - ScreenCoordsXY scrollPos = {}; - int32_t scrollArea = 0; - int32_t scrollId = 0; - WidgetScrollGetPart(*this, &widgets[WIDX_SCENERY_LIST], state->position, scrollPos, &scrollArea, &scrollId); - if (scrollArea == SCROLL_PART_VIEW) - { - const ScenerySelection scenery = GetSceneryIdByCursorPos(scrollPos); - if (scenery == _selectedScenery) + if (tabSelectedScenery.SceneryType == SCENERY_TYPE_BANNER) { - return; + gCurrentToolId = Tool::EntranceDown; } - } - } - - // Cursor was not over the currently hover selected scenery so reset hover selection. - // This will happen when the mouse leaves the scroll window and is required so that the cost and description switch - // to the tool scenery selection. - _selectedScenery = {}; - } - } - - void OnUpdate() override - { - const CursorState* state = ContextGetCursorState(); - WindowBase* other = WindowFindFromPoint(state->position); - if (other == this) - { - ScreenCoordsXY window = state->position - ScreenCoordsXY{ windowPos.x - 26, windowPos.y }; - - if (window.y < 44 || window.x <= width) - { - WidgetIndex widgetIndex = WindowFindWidgetFromPoint(*this, state->position); - if (widgetIndex >= WIDX_SCENERY_TAB_CONTENT_PANEL) - { - _hoverCounter++; - if (_hoverCounter < 8) + else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_LARGE) { - if (InputGetState() != InputState::ScrollLeft) - { - min_height = _actualMinHeight; - max_height = _actualMinHeight; - } + gCurrentToolId = static_cast( + OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); + } + else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL) + { + gCurrentToolId = static_cast( + OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); + } + else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_PATH_ITEM) + { + gCurrentToolId = static_cast( + OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); } else - { - const auto& listWidget = widgets[WIDX_SCENERY_LIST]; - const auto nonListHeight = height - listWidget.height() + 12; - - const auto numRows = static_cast(CountRows()); - const auto maxContentHeight = numRows * SCENERY_BUTTON_HEIGHT; - const auto maxWindowHeight = maxContentHeight + nonListHeight; - const auto windowHeight = std::clamp(maxWindowHeight, _actualMinHeight, 473); - - min_height = windowHeight; - max_height = windowHeight; + { // small scenery + gCurrentToolId = static_cast( + OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); } } + else + { + gCurrentToolId = Tool::Arrow; + } } } - else + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { - _hoverCounter = 0; - if (InputGetState() != InputState::ScrollLeft) + if (widgetIndex != WIDX_FILTER_TEXT_BOX) + return; + + if (text == _tabEntries[_activeTabIndex].Filter) + return; + + _tabEntries[_activeTabIndex].Filter.assign(text); + ContentUpdateScroll(); + + scrolls->v_top = 0; + Invalidate(); + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + if (scrollIndex == SceneryContentScrollIndex) { - min_height = _actualMinHeight; - max_height = _actualMinHeight; + return ContentScrollGetSize(); + } + return {}; + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + if (scrollIndex == SceneryContentScrollIndex) + { + ContentScrollMouseDown(screenCoords); } } - if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - WindowUpdateTextboxCaret(); - WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX); + if (scrollIndex == SceneryContentScrollIndex) + { + ContentScrollMouseOver(screenCoords); + } } - Invalidate(); - - if (!SceneryToolIsActive()) + OpenRCT2String OnTooltip(const WidgetIndex widgetIndex, const StringId fallback) override { - Close(); - return; + if (widgetIndex >= WIDX_SCENERY_TAB_1) + { + const auto tabIndex = static_cast(widgetIndex - WIDX_SCENERY_TAB_1); + if (_tabEntries.size() > tabIndex) + { + const auto& tabInfo = _tabEntries[tabIndex]; + if (tabInfo.IsMisc()) + { + auto ft = Formatter(); + ft.Add(STR_MISCELLANEOUS); + return { fallback, ft }; + } + + if (tabInfo.IsAll()) + { + auto ft = Formatter(); + ft.Add(STR_ALL_SCENERY); + return { fallback, ft }; + } + + const auto* sceneryEntry = tabInfo.GetSceneryGroupEntry(); + if (sceneryEntry != nullptr) + { + auto ft = Formatter(); + ft.Add(sceneryEntry->name); + return { fallback, ft }; + } + } + } + return { STR_NONE, Formatter() }; } - if (gWindowSceneryEyedropperEnabled) - { - gCurrentToolId = Tool::Crosshair; - } - else if (gWindowSceneryPaintEnabled == 1) - { - gCurrentToolId = Tool::PaintDown; - } - else + void OnPrepareDraw() override { + // Set the window title + StringId titleStringId = STR_MISCELLANEOUS; const auto tabIndex = _activeTabIndex; + if (tabIndex < _tabEntries.size()) + { + const auto& tabInfo = _tabEntries[tabIndex]; + if (tabInfo.IsAll()) + { + titleStringId = STR_ALL_SCENERY; + } + else + { + const auto* sgEntry = tabInfo.GetSceneryGroupEntry(); + if (sgEntry != nullptr) + { + titleStringId = sgEntry->name; + } + } + } + widgets[WIDX_SCENERY_TITLE].text = titleStringId; + widgets[WIDX_FILTER_TEXT_BOX].string = _filteredSceneryTab.Filter.data(); + + pressed_widgets = 0; + pressed_widgets |= 1uLL << (tabIndex + WIDX_SCENERY_TAB_1); + if (gWindowSceneryPaintEnabled == 1) + pressed_widgets |= (1uLL << WIDX_SCENERY_REPAINT_SCENERY_BUTTON); + if (gWindowSceneryEyedropperEnabled) + pressed_widgets |= (1uLL << WIDX_SCENERY_EYEDROPPER_BUTTON); + if (gWindowSceneryScatterEnabled) + pressed_widgets |= (1uLL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); + + widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_RESTRICT_SCENERY].type = WindowWidgetType::Empty; + + if (!(gWindowSceneryPaintEnabled & 1)) + { + widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::FlatBtn; + } + const auto tabSelectedScenery = GetSelectedScenery(tabIndex); if (!tabSelectedScenery.IsUndefined()) { - if (tabSelectedScenery.SceneryType == SCENERY_TYPE_BANNER) + if (tabSelectedScenery.SceneryType == SCENERY_TYPE_SMALL) { - gCurrentToolId = Tool::EntranceDown; + if (!(gWindowSceneryPaintEnabled & 1)) + { + widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::FlatBtn; + } + + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + tabSelectedScenery.EntryIndex); + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) + { + widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn; + } } - else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_LARGE) - { - gCurrentToolId = static_cast( - OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); - } - else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL) - { - gCurrentToolId = static_cast( - OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); - } - else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_PATH_ITEM) - { - gCurrentToolId = static_cast( - OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); - } - else - { // small scenery - gCurrentToolId = static_cast( - OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex)->tool_id); - } - } - else - { - gCurrentToolId = Tool::Arrow; - } - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex != WIDX_FILTER_TEXT_BOX) - return; - - if (text == _tabEntries[_activeTabIndex].Filter) - return; - - _tabEntries[_activeTabIndex].Filter.assign(text); - ContentUpdateScroll(); - - scrolls->v_top = 0; - Invalidate(); - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - if (scrollIndex == SceneryContentScrollIndex) - { - return ContentScrollGetSize(); - } - return {}; - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (scrollIndex == SceneryContentScrollIndex) - { - ContentScrollMouseDown(screenCoords); - } - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (scrollIndex == SceneryContentScrollIndex) - { - ContentScrollMouseOver(screenCoords); - } - } - - OpenRCT2String OnTooltip(const WidgetIndex widgetIndex, const StringId fallback) override - { - if (widgetIndex >= WIDX_SCENERY_TAB_1) - { - const auto tabIndex = static_cast(widgetIndex - WIDX_SCENERY_TAB_1); - if (_tabEntries.size() > tabIndex) - { - const auto& tabInfo = _tabEntries[tabIndex]; - if (tabInfo.IsMisc()) - { - auto ft = Formatter(); - ft.Add(STR_MISCELLANEOUS); - return { fallback, ft }; - } - - if (tabInfo.IsAll()) - { - auto ft = Formatter(); - ft.Add(STR_ALL_SCENERY); - return { fallback, ft }; - } - - const auto* sceneryEntry = tabInfo.GetSceneryGroupEntry(); - if (sceneryEntry != nullptr) - { - auto ft = Formatter(); - ft.Add(sceneryEntry->name); - return { fallback, ft }; - } - } - } - return { STR_NONE, Formatter() }; - } - - void OnPrepareDraw() override - { - // Set the window title - StringId titleStringId = STR_MISCELLANEOUS; - const auto tabIndex = _activeTabIndex; - if (tabIndex < _tabEntries.size()) - { - const auto& tabInfo = _tabEntries[tabIndex]; - if (tabInfo.IsAll()) - { - titleStringId = STR_ALL_SCENERY; - } - else - { - const auto* sgEntry = tabInfo.GetSceneryGroupEntry(); - if (sgEntry != nullptr) - { - titleStringId = sgEntry->name; - } - } - } - widgets[WIDX_SCENERY_TITLE].text = titleStringId; - widgets[WIDX_FILTER_TEXT_BOX].string = _filteredSceneryTab.Filter.data(); - - pressed_widgets = 0; - pressed_widgets |= 1uLL << (tabIndex + WIDX_SCENERY_TAB_1); - if (gWindowSceneryPaintEnabled == 1) - pressed_widgets |= (1uLL << WIDX_SCENERY_REPAINT_SCENERY_BUTTON); - if (gWindowSceneryEyedropperEnabled) - pressed_widgets |= (1uLL << WIDX_SCENERY_EYEDROPPER_BUTTON); - if (gWindowSceneryScatterEnabled) - pressed_widgets |= (1uLL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); - - widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_RESTRICT_SCENERY].type = WindowWidgetType::Empty; - - if (!(gWindowSceneryPaintEnabled & 1)) - { - widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::FlatBtn; - } - - const auto tabSelectedScenery = GetSelectedScenery(tabIndex); - if (!tabSelectedScenery.IsUndefined()) - { - if (tabSelectedScenery.SceneryType == SCENERY_TYPE_SMALL) - { - if (!(gWindowSceneryPaintEnabled & 1)) - { - widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::FlatBtn; - } - - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex); - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) + else if (tabSelectedScenery.SceneryType >= SCENERY_TYPE_LARGE) { widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn; } - } - else if (tabSelectedScenery.SceneryType >= SCENERY_TYPE_LARGE) - { - widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn; - } - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode) - { - widgets[WIDX_RESTRICT_SCENERY].type = WindowWidgetType::Button; - if (IsSceneryItemRestricted(tabSelectedScenery)) - pressed_widgets |= (1uLL << WIDX_RESTRICT_SCENERY); - else - pressed_widgets &= ~(1uLL << WIDX_RESTRICT_SCENERY); - } - } - - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].image = GetColourButtonImage(gWindowSceneryPrimaryColour); - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].image = GetColourButtonImage(gWindowScenerySecondaryColour); - widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].image = GetColourButtonImage(gWindowSceneryTertiaryColour); - - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; - - if (gWindowSceneryPaintEnabled & 1) - { // repaint coloured scenery tool is on - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; - } - else if (!tabSelectedScenery.IsUndefined()) - { - if (tabSelectedScenery.SceneryType == SCENERY_TYPE_BANNER) - { - auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex); - if (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode) { - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + widgets[WIDX_RESTRICT_SCENERY].type = WindowWidgetType::Button; + if (IsSceneryItemRestricted(tabSelectedScenery)) + pressed_widgets |= (1uLL << WIDX_RESTRICT_SCENERY); + else + pressed_widgets &= ~(1uLL << WIDX_RESTRICT_SCENERY); } } - else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_LARGE) - { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex); - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR - && !(sceneryEntry->flags & LARGE_SCENERY_FLAG_HIDE_PRIMARY_REMAP_BUTTON)) - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR - && !(sceneryEntry->flags & LARGE_SCENERY_FLAG_HIDE_SECONDARY_REMAP_BUTTON)) - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) - widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].image = GetColourButtonImage(gWindowSceneryPrimaryColour); + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].image = GetColourButtonImage(gWindowScenerySecondaryColour); + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].image = GetColourButtonImage(gWindowSceneryTertiaryColour); + + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + + if (gWindowSceneryPaintEnabled & 1) + { // repaint coloured scenery tool is on + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; } - else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL) + else if (!tabSelectedScenery.IsUndefined()) { - auto* wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex); - if (wallEntry->flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS)) + if (tabSelectedScenery.SceneryType == SCENERY_TYPE_BANNER) { - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - - if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) + auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry( + tabSelectedScenery.EntryIndex); + if (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) { - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + } + } + else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_LARGE) + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + tabSelectedScenery.EntryIndex); - if (wallEntry->flags2 & WALL_SCENERY_2_NO_SELECT_PRIMARY_COLOUR) - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; - if (wallEntry->flags & WALL_SCENERY_HAS_TERTIARY_COLOUR) + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR + && !(sceneryEntry->flags & LARGE_SCENERY_FLAG_HIDE_PRIMARY_REMAP_BUTTON)) + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR + && !(sceneryEntry->flags & LARGE_SCENERY_FLAG_HIDE_SECONDARY_REMAP_BUTTON)) + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + } + else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL) + { + auto* wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex); + if (wallEntry->flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS)) + { + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + + if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) + { + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + + if (wallEntry->flags2 & WALL_SCENERY_2_NO_SELECT_PRIMARY_COLOUR) + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + if (wallEntry->flags & WALL_SCENERY_HAS_TERTIARY_COLOUR) + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + } + } + } + else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_SMALL) + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + tabSelectedScenery.EntryIndex); + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | SMALL_SCENERY_FLAG_HAS_GLASS)) + { + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR)) widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; } } } - else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_SMALL) + + auto windowWidth = width; + if (_tabEntries.size() > 0) { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(tabSelectedScenery.EntryIndex); + const auto lastTabIndex = GetMaxTabCountInARow() == MaxTabsPerRow ? MaxTabsPerRow - 1 : _tabEntries.size() - 1; + const auto lastTabWidget = &widgets[WIDX_SCENERY_TAB_1 + lastTabIndex]; + windowWidth = std::max(windowWidth, lastTabWidget->right + 3); - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | SMALL_SCENERY_FLAG_HAS_GLASS)) + if (GetSceneryTabInfoForMisc() != nullptr) { - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + auto miscTabWidget = &widgets[WIDX_SCENERY_TAB_1 + _tabEntries.size() - 2]; + miscTabWidget->left = windowWidth - 2 * TabWidth - 6; + miscTabWidget->right = windowWidth - TabWidth - 7; + miscTabWidget->top = InitTabPosY; + miscTabWidget->bottom = InitTabPosY + TabHeight; + } - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR)) - widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + if (_tabEntries.back().IsAll()) + { + auto allTabWidget = &widgets[WIDX_SCENERY_TAB_1 + _tabEntries.size() - 1]; + allTabWidget->left = windowWidth - TabWidth - 6; + allTabWidget->right = windowWidth - 7; + allTabWidget->top = InitTabPosY; + allTabWidget->bottom = InitTabPosY + TabHeight; } } + + ResizeFrameWithPage(); + widgets[WIDX_SCENERY_LIST].right = windowWidth - 26; + widgets[WIDX_SCENERY_LIST].bottom = height - 24; + + widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].left = windowWidth - 25; + widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].left = windowWidth - 25; + widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].left = windowWidth - 25; + widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].left = windowWidth - 25; + widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].right = windowWidth - 2; + widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].right = windowWidth - 2; + widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].right = windowWidth - 2; + widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].right = windowWidth - 2; + + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].left = windowWidth - 19; + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].left = windowWidth - 19; + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].left = windowWidth - 19; + widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].right = windowWidth - 8; + widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].right = windowWidth - 8; + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].right = windowWidth - 8; } - auto windowWidth = width; - if (_tabEntries.size() > 0) + void OnDraw(DrawPixelInfo& dpi) override { - const auto lastTabIndex = GetMaxTabCountInARow() == MaxTabsPerRow ? MaxTabsPerRow - 1 : _tabEntries.size() - 1; - const auto lastTabWidget = &widgets[WIDX_SCENERY_TAB_1 + lastTabIndex]; - windowWidth = std::max(windowWidth, lastTabWidget->right + 3); + DrawWidgets(dpi); + DrawTabs(dpi, windowPos); - if (GetSceneryTabInfoForMisc() != nullptr) - { - auto miscTabWidget = &widgets[WIDX_SCENERY_TAB_1 + _tabEntries.size() - 2]; - miscTabWidget->left = windowWidth - 2 * TabWidth - 6; - miscTabWidget->right = windowWidth - TabWidth - 7; - miscTabWidget->top = InitTabPosY; - miscTabWidget->bottom = InitTabPosY + TabHeight; - } - - if (_tabEntries.back().IsAll()) - { - auto allTabWidget = &widgets[WIDX_SCENERY_TAB_1 + _tabEntries.size() - 1]; - allTabWidget->left = windowWidth - TabWidth - 6; - allTabWidget->right = windowWidth - 7; - allTabWidget->top = InitTabPosY; - allTabWidget->bottom = InitTabPosY + TabHeight; - } - } - - ResizeFrameWithPage(); - widgets[WIDX_SCENERY_LIST].right = windowWidth - 26; - widgets[WIDX_SCENERY_LIST].bottom = height - 24; - - widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].left = windowWidth - 25; - widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].left = windowWidth - 25; - widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].left = windowWidth - 25; - widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].left = windowWidth - 25; - widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].right = windowWidth - 2; - widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].right = windowWidth - 2; - widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].right = windowWidth - 2; - widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].right = windowWidth - 2; - - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].left = windowWidth - 19; - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].left = windowWidth - 19; - widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].left = windowWidth - 19; - widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].right = windowWidth - 8; - widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].right = windowWidth - 8; - widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].right = windowWidth - 8; - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabs(dpi, windowPos); - - auto selectedSceneryEntry = _selectedScenery; - if (selectedSceneryEntry.IsUndefined()) - { - if (gWindowSceneryPaintEnabled & 1) // repaint coloured scenery tool is on - return; - if (gWindowSceneryEyedropperEnabled) - return; - - selectedSceneryEntry = GetSelectedScenery(_activeTabIndex); + auto selectedSceneryEntry = _selectedScenery; if (selectedSceneryEntry.IsUndefined()) - return; - } - - auto [name, price] = GetNameAndPrice(selectedSceneryEntry); - if (price != kMoney64Undefined && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - auto ft = Formatter(); - ft.Add(price); - - // -14 - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ width - 0x1A, height - 13 }, STR_COST_LABEL, ft, { TextAlignment::RIGHT }); - } - - auto ft = Formatter(); - ft.Add(name); - DrawTextEllipsised(dpi, { windowPos.x + 3, windowPos.y + height - 23 }, width - 19, STR_BLACK_STRING, ft); - - auto sceneryObjectType = GetObjectTypeFromSceneryType(selectedSceneryEntry.SceneryType); - auto& objManager = GetContext()->GetObjectManager(); - auto sceneryObject = objManager.GetLoadedObject(sceneryObjectType, selectedSceneryEntry.EntryIndex); - if (sceneryObject != nullptr && sceneryObject->GetAuthors().size() > 0) - { - std::string authorsString; - const auto& authors = sceneryObject->GetAuthors(); - for (size_t i = 0; i < authors.size(); ++i) { - if (i > 0) - { - authorsString.append(", "); - } - authorsString.append(authors[i]); + if (gWindowSceneryPaintEnabled & 1) // repaint coloured scenery tool is on + return; + if (gWindowSceneryEyedropperEnabled) + return; + + selectedSceneryEntry = GetSelectedScenery(_activeTabIndex); + if (selectedSceneryEntry.IsUndefined()) + return; } - ft = Formatter(); - ft.Add(authorsString.c_str()); - DrawTextEllipsised( - dpi, windowPos + ScreenCoordsXY{ 3, height - 13 }, width - 19, - (sceneryObject->GetAuthors().size() == 1 ? STR_SCENERY_AUTHOR : STR_SCENERY_AUTHOR_PLURAL), ft); - } - } - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - if (scrollIndex == SceneryContentScrollIndex) + auto [name, price] = GetNameAndPrice(selectedSceneryEntry); + if (price != kMoney64Undefined && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + auto ft = Formatter(); + ft.Add(price); + + // -14 + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ width - 0x1A, height - 13 }, STR_COST_LABEL, ft, { TextAlignment::RIGHT }); + } + + auto ft = Formatter(); + ft.Add(name); + DrawTextEllipsised(dpi, { windowPos.x + 3, windowPos.y + height - 23 }, width - 19, STR_BLACK_STRING, ft); + + auto sceneryObjectType = GetObjectTypeFromSceneryType(selectedSceneryEntry.SceneryType); + auto& objManager = GetContext()->GetObjectManager(); + auto sceneryObject = objManager.GetLoadedObject(sceneryObjectType, selectedSceneryEntry.EntryIndex); + if (sceneryObject != nullptr && sceneryObject->GetAuthors().size() > 0) + { + std::string authorsString; + const auto& authors = sceneryObject->GetAuthors(); + for (size_t i = 0; i < authors.size(); ++i) + { + if (i > 0) + { + authorsString.append(", "); + } + authorsString.append(authors[i]); + } + ft = Formatter(); + ft.Add(authorsString.c_str()); + DrawTextEllipsised( + dpi, windowPos + ScreenCoordsXY{ 3, height - 13 }, width - 19, + (sceneryObject->GetAuthors().size() == 1 ? STR_SCENERY_AUTHOR : STR_SCENERY_AUTHOR_PLURAL), ft); + } + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override { - ContentScrollDraw(dpi); + if (scrollIndex == SceneryContentScrollIndex) + { + ContentScrollDraw(dpi); + } } + + void SetSelectedItem( + const ScenerySelection& scenery, const std::optional primary, const std::optional secondary, + const std::optional tertiary, const std::optional rotation) + { + auto tabIndex = FindTabWithScenery(scenery); + if (!tabIndex.has_value()) + { + return; + } + + _activeTabIndex = tabIndex.value(); + SetSelectedScenery(tabIndex.value(), scenery); + if (primary.has_value()) + { + gWindowSceneryPrimaryColour = primary.value(); + } + if (secondary.has_value()) + { + gWindowScenerySecondaryColour = secondary.value(); + } + if (tertiary.has_value()) + { + gWindowSceneryTertiaryColour = tertiary.value(); + } + if (rotation.has_value()) + { + gWindowSceneryRotation = rotation.value(); + } + gWindowSceneryEyedropperEnabled = false; + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, ContextGetWidth() / 2); + _hoverCounter = -16; + gSceneryPlaceCost = kMoney64Undefined; + Invalidate(); + } + + void SetSelectedTab(const ObjectEntryIndex sceneryGroupIndex) + { + const auto* tabInfo = GetSceneryTabInfoForGroup(sceneryGroupIndex); + if (tabInfo == nullptr) + { + tabInfo = &_tabEntries.back(); + } + const auto tabId = std::distance(&*_tabEntries.cbegin(), tabInfo); + + OnMouseDown(WIDX_SCENERY_TAB_1 + tabId); + } + + const ScenerySelection GetTabSelection() + { + return GetSelectedScenery(_activeTabIndex); + } + + void Init() + { + _tabEntries.clear(); + + for (ObjectEntryIndex scenerySetIndex = 0; scenerySetIndex < MaxTabs - ReservedTabCount; scenerySetIndex++) + { + const auto* sceneryGroupEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySetIndex); + if (sceneryGroupEntry != nullptr && SceneryGroupIsInvented(scenerySetIndex)) + { + SceneryTabInfo tabInfo; + tabInfo.SceneryGroupIndex = scenerySetIndex; + for (const auto& sceneryEntry : sceneryGroupEntry->SceneryEntries) + { + if (IsSceneryAvailableToBuild(sceneryEntry)) + { + tabInfo.AddEntryToBack(sceneryEntry); + } + } + if (tabInfo.Entries.size() > 0) + { + _tabEntries.push_back(std::move(tabInfo)); + } + } + } + + // Sort scenery group tabs before adding other tabs + SortTabs(); + + // Add misc and all tab + _tabEntries.emplace_back(SceneryWindow::SceneryTabInfo{ SCENERY_TAB_TYPE_MISC }); + _tabEntries.emplace_back(SceneryWindow::SceneryTabInfo{ SCENERY_TAB_TYPE_ALL }); + + // small scenery + for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_SMALL_SCENERY_OBJECTS; sceneryId++) + { + const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); + if (sceneryEntry != nullptr) + { + InitSceneryEntry({ SCENERY_TYPE_SMALL, sceneryId }, sceneryEntry->scenery_tab_id); + } + } + + // large scenery + for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_LARGE_SCENERY_OBJECTS; sceneryId++) + { + const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); + if (sceneryEntry != nullptr) + { + InitSceneryEntry({ SCENERY_TYPE_LARGE, sceneryId }, sceneryEntry->scenery_tab_id); + } + } + + // walls + for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_WALL_SCENERY_OBJECTS; sceneryId++) + { + const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); + if (sceneryEntry != nullptr) + { + InitSceneryEntry({ SCENERY_TYPE_WALL, sceneryId }, sceneryEntry->scenery_tab_id); + } + } + + // banners + for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_BANNER_OBJECTS; sceneryId++) + { + const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); + if (sceneryEntry != nullptr) + { + InitSceneryEntry({ SCENERY_TYPE_BANNER, sceneryId }, sceneryEntry->scenery_tab_id); + } + } + + for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_PATH_ADDITION_OBJECTS; sceneryId++) + { + const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); + if (sceneryEntry != nullptr) + { + InitSceneryEntry({ SCENERY_TYPE_PATH_ITEM, sceneryId }, sceneryEntry->scenery_tab_id); + } + } + + // Remove empty tabs + _tabEntries.erase( + std::remove_if( + _tabEntries.begin(), _tabEntries.end(), + [](const SceneryTabInfo& tabInfo) { return tabInfo.Entries.empty(); }), + _tabEntries.end()); + + // Set required width + _requiredWidth = std::min(static_cast(_tabEntries.size()), MaxTabsPerRow) * TabWidth + 5; + + PrepareWidgets(); + WindowInvalidateByClass(WindowClass::Scenery); + } + + int32_t GetRequiredWidth() const + { + return std::max(_requiredWidth, WINDOW_SCENERY_MIN_WIDTH); + } + + private: + int32_t GetNumColumns() const + { + const auto& listWidget = widgets[WIDX_SCENERY_LIST]; + const auto contentWidth = listWidget.width() - kScrollBarWidth; + return contentWidth / SCENERY_BUTTON_WIDTH; + } + + template T CountRows(T items) const + { + const auto rows = items / GetNumColumns(); + return rows; + } + + size_t CountRows() const + { + const auto tabIndex = _activeTabIndex; + if (tabIndex >= _tabEntries.size()) + { + return 0; + } + + const auto totalItems = _filteredSceneryTab.Entries.size(); + const auto numColumns = GetNumColumns(); + const auto rows = CountRows(totalItems + numColumns - 1); + return rows; + } + + int32_t constexpr ContentRowsHeight(const size_t rows) const + { + return static_cast(rows * SCENERY_BUTTON_HEIGHT); + } + + void ContentUpdateScroll() + { + const auto tabIndex = _activeTabIndex; + if (tabIndex >= _tabEntries.size()) + { + return; + } + + SetFilteredScenery(tabIndex); + + const int32_t listHeight = height - 14 - widgets[WIDX_SCENERY_LIST].top - 1; + + const auto sceneryItem = ContentCountRowsWithSelectedItem(tabIndex); + scrolls[SceneryContentScrollIndex].v_bottom = ContentRowsHeight(sceneryItem.allRows) + 1; + + const int32_t maxTop = std::max(0, scrolls[SceneryContentScrollIndex].v_bottom - listHeight); + auto rowSelected = CountRows(sceneryItem.selected_item); + if (sceneryItem.scenerySelection.IsUndefined()) + { + auto& oldScenery = GetSelectedScenery(tabIndex); + if (!oldScenery.IsUndefined()) + { + // Make sure last selected scenery for tab is always present + _filteredSceneryTab.AddEntryToFront(oldScenery); + } + else + { + // No last selection was made, use blank ScenerySelection + SetSelectedScenery(tabIndex, ScenerySelection()); + rowSelected = 0; + if (!_filteredSceneryTab.Entries.empty()) + { + // Select first item in current filtered tab if available + const auto& scenery = _filteredSceneryTab.Entries.front(); + if (!scenery.IsUndefined()) + { + SetSelectedScenery(tabIndex, scenery); + } + } + } + } + + scrolls[SceneryContentScrollIndex].v_top = ContentRowsHeight(rowSelected); + scrolls[SceneryContentScrollIndex].v_top = std::min(maxTop, scrolls[SceneryContentScrollIndex].v_top); + + WidgetScrollUpdateThumbs(*this, WIDX_SCENERY_LIST); + } + + SceneryItem ContentCountRowsWithSelectedItem(const size_t tabIndex) + { + SceneryItem sceneryItem = { 0, 0, ScenerySelection() }; + const auto scenerySelection = GetSelectedScenery(tabIndex); + for (size_t i = 0; i < _filteredSceneryTab.Entries.size(); i++) + { + const auto& currentEntry = _filteredSceneryTab.Entries[i]; + if (currentEntry == scenerySelection) + { + sceneryItem.selected_item = static_cast(i); + sceneryItem.scenerySelection = scenerySelection; + } + } + sceneryItem.allRows = static_cast(CountRows(_filteredSceneryTab.Entries.size() + 8)); + return sceneryItem; + } + + const ScenerySelection GetSelectedScenery(const size_t tabIndex) + { + if (_tabSelections.size() > tabIndex) + { + return _tabSelections[tabIndex]; + } + return {}; + } + + void SetSelectedScenery(const size_t tabIndex, const ScenerySelection& value) + { + if (_tabSelections.size() <= tabIndex) + { + _tabSelections.resize(tabIndex + 1); + } + _tabSelections[tabIndex] = value; + } + + SceneryTabInfo* GetSceneryTabInfoForMisc() + { + if (_tabEntries.size() >= 2) + { + if (_tabEntries[_tabEntries.size() - 2].IsMisc()) + return &_tabEntries[_tabEntries.size() - 2]; + } + + return nullptr; + } + + SceneryTabInfo* GetSceneryTabInfoForAll() + { + if (!_tabEntries.empty()) + { + if (_tabEntries.back().IsAll()) + return &_tabEntries.back(); + } + + return nullptr; + } + + SceneryTabInfo* GetSceneryTabInfoForGroup(const ObjectEntryIndex sceneryGroupIndex) + { + for (auto& tabEntry : _tabEntries) + { + if (tabEntry.SceneryGroupIndex == sceneryGroupIndex) + return &tabEntry; + } + + return nullptr; + } + + std::optional FindTabWithScenery(const ScenerySelection& scenery) + { + for (size_t i = 0; i < _tabEntries.size(); i++) + { + const auto& tabInfo = _tabEntries[i]; + if (tabInfo.IsAll()) + { + // The scenery will be always added here so exclude this one. + continue; + } + if (tabInfo.Contains(scenery)) + { + return i; + } + } + return std::nullopt; + } + + void InitSceneryEntry(const ScenerySelection& selection, const ObjectEntryIndex sceneryGroupIndex) + { + Guard::ArgumentInRange(selection.EntryIndex, 0, OBJECT_ENTRY_INDEX_NULL); + + if (IsSceneryAvailableToBuild(selection)) + { + // Add scenery to all tab + auto* allTabInfo = GetSceneryTabInfoForAll(); + if (allTabInfo != nullptr) + { + allTabInfo->AddEntryToBack(selection); + } + + // Add scenery to primary group (usually trees or path additions) + if (sceneryGroupIndex != OBJECT_ENTRY_INDEX_NULL) + { + auto* tabInfo = GetSceneryTabInfoForGroup(sceneryGroupIndex); + if (tabInfo != nullptr) + { + tabInfo->AddEntryToBack(selection); + return; + } + } + + // If scenery has no tab, add it to misc + const auto tabIndex = FindTabWithScenery(selection); + if (!tabIndex.has_value()) + { + auto* tabInfo = GetSceneryTabInfoForMisc(); + if (tabInfo != nullptr) + { + tabInfo->AddEntryToBack(selection); + } + } + } + } + + void SetFilteredScenery(const size_t tabIndex) + { + auto currentTab = _tabEntries[tabIndex]; + + _filteredSceneryTab.Entries.clear(); + _filteredSceneryTab.Filter = currentTab.Filter; + + for (auto selection : currentTab.Entries) + { + if (MatchFilter(selection)) + _filteredSceneryTab.AddEntryToBack(selection); + } + } + + bool MatchFilter(const ScenerySelection& selection) + { + if (_filteredSceneryTab.Filter.empty()) + return true; + + auto& objManager = GetContext()->GetObjectManager(); + auto sceneryObjectType = GetObjectTypeFromSceneryType(selection.SceneryType); + auto sceneryObject = objManager.GetLoadedObject(sceneryObjectType, selection.EntryIndex); + + return IsFilterInName(*sceneryObject) || IsFilterInAuthors(*sceneryObject) || IsFilterInIdentifier(*sceneryObject) + || IsFilterInFilename(*sceneryObject); + } + + bool IsFilterInName(const Object& object) + { + return String::Contains(object.GetName(), _filteredSceneryTab.Filter, true); + } + + bool IsFilterInAuthors(const Object& object) + { + for (auto author : object.GetAuthors()) + if (String::Contains(author, _filteredSceneryTab.Filter, true)) + return true; + + return false; + } + + bool IsFilterInIdentifier(const Object& object) + { + return String::Contains(object.GetIdentifier(), _filteredSceneryTab.Filter, true); + } + + bool IsFilterInFilename(const Object& object) + { + auto repoItem = ObjectRepositoryFindObjectByEntry(&(object.GetObjectEntry())); + return String::Contains(repoItem->Path, _filteredSceneryTab.Filter, true); + } + + void SortTabs() + { + std::sort(_tabEntries.begin(), _tabEntries.end(), [](const SceneryTabInfo& a, const SceneryTabInfo& b) { + if (a.SceneryGroupIndex == b.SceneryGroupIndex) + return false; + + if (a.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) + return false; + if (b.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) + return true; + + const auto* entryA = a.GetSceneryGroupEntry(); + const auto* entryB = b.GetSceneryGroupEntry(); + return entryA->priority < entryB->priority; + }); + } + + int32_t GetTabRowCount() + { + return std::max((static_cast(_tabEntries.size()) + MaxTabsPerRow - 1) / MaxTabsPerRow, 0); + } + + int32_t GetMaxTabCountInARow() + { + int32_t tabEntries = static_cast(_tabEntries.size()); + return std::min(tabEntries, MaxTabsPerRow); + } + + void PrepareWidgets() + { + // Add the base widgets + _widgets.clear(); + for (const auto& widget : WindowSceneryBaseWidgets) + { + _widgets.push_back(widget); + } + + // Remove WWT_LAST + auto lastWidget = _widgets.back(); + _widgets.pop_back(); + + // Add tabs + _actualMinHeight = WINDOW_SCENERY_MIN_HEIGHT; + int32_t xInit = InitTabPosX; + int32_t tabsInThisRow = 0; + + auto hasMisc = GetSceneryTabInfoForMisc() != nullptr; + auto maxTabsInThisRow = MaxTabsPerRow - 1 - (hasMisc ? 1 : 0); + + ScreenCoordsXY pos = { xInit, InitTabPosY }; + for (const auto& tabInfo : _tabEntries) + { + auto widget = MakeTab(pos, STR_STRING_DEFINED_TOOLTIP); + pos.x += TabWidth; + + if (tabInfo.IsMisc()) + { + widget.image = ImageId(SPR_TAB_QUESTION, FilterPaletteID::PaletteNull); + } + else if (tabInfo.IsAll()) + { + widget.image = ImageId(SPR_TAB, FilterPaletteID::PaletteNull); + } + else if (tabInfo.IsSceneryGroup()) + { + // Default tab image + widget.image = ImageId(SPR_TAB_QUESTION, FilterPaletteID::PaletteNull); + + // Scenery Group image + auto scgEntry = tabInfo.GetSceneryGroupEntry(); + if (scgEntry != nullptr) + { + widget.image = ImageId(scgEntry->image, colours[1]); + } + } + + _widgets.push_back(widget); + + tabsInThisRow++; + if (tabsInThisRow >= maxTabsInThisRow) + { + pos.x = xInit; + pos.y += TabHeight; + tabsInThisRow = 0; + _actualMinHeight += TabHeight; + maxTabsInThisRow = MaxTabsPerRow; + } + } + + _widgets.push_back(lastWidget); + + // Shift base widgets based on number of tab rows + int32_t shiftAmount = (GetTabRowCount() - 1) * TabHeight; + if (shiftAmount > 0) + { + _widgets[WIDX_SCENERY_LIST].top += shiftAmount; + _widgets[WIDX_SCENERY_LIST].bottom += shiftAmount; + _widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].top += shiftAmount; + _widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].bottom += shiftAmount; + _widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].top += shiftAmount; + _widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].bottom += shiftAmount; + _widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].top += shiftAmount; + _widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].bottom += shiftAmount; + _widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].top += shiftAmount; + _widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].bottom += shiftAmount; + _widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].top += shiftAmount; + _widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].bottom += shiftAmount; + _widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].top += shiftAmount; + _widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].bottom += shiftAmount; + _widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].top += shiftAmount; + _widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].bottom += shiftAmount; + _widgets[WIDX_FILTER_TEXT_BOX].top += shiftAmount; + _widgets[WIDX_FILTER_TEXT_BOX].bottom += shiftAmount; + _widgets[WIDX_FILTER_CLEAR_BUTTON].top += shiftAmount; + _widgets[WIDX_FILTER_CLEAR_BUTTON].bottom += shiftAmount; + } + + widgets = _widgets.data(); + } + + ScenerySelection GetSceneryIdByCursorPos(const ScreenCoordsXY& screenCoords) const + { + ScenerySelection scenery{}; + + const auto numColumns = GetNumColumns(); + const auto colIndex = screenCoords.x / SCENERY_BUTTON_WIDTH; + const auto rowIndex = screenCoords.y / SCENERY_BUTTON_HEIGHT; + if (colIndex >= 0 && colIndex < numColumns && rowIndex >= 0) + { + const auto tabSceneryIndex = static_cast((rowIndex * numColumns) + colIndex); + if (tabSceneryIndex < _filteredSceneryTab.Entries.size()) + { + return _filteredSceneryTab.Entries[tabSceneryIndex]; + } + } + return scenery; + } + + ScreenSize ContentScrollGetSize() const + { + auto rows = CountRows(); + return { 0, ContentRowsHeight(rows) }; + } + + void ContentScrollMouseDown(const ScreenCoordsXY& screenCoords) + { + const auto scenery = GetSceneryIdByCursorPos(screenCoords); + if (scenery.IsUndefined()) + return; + + auto lastScenery = GetSelectedScenery(_activeTabIndex); + if (lastScenery != scenery && !MatchFilter(lastScenery)) + { + _filteredSceneryTab.Entries.pop_front(); + } + SetSelectedScenery(_activeTabIndex, scenery); + + gWindowSceneryPaintEnabled &= 0xFE; + gWindowSceneryEyedropperEnabled = false; + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); + _hoverCounter = -16; + gSceneryPlaceCost = kMoney64Undefined; + Invalidate(); + } + + void ContentScrollMouseOver(const ScreenCoordsXY& screenCoords) + { + ScenerySelection scenery = GetSceneryIdByCursorPos(screenCoords); + if (!scenery.IsUndefined()) + { + _selectedScenery = scenery; + Invalidate(); + } + } + + std::pair GetNameAndPrice(ScenerySelection selectedScenery) + { + StringId name = STR_UNKNOWN_OBJECT_TYPE; + money64 price = kMoney64Undefined; + if (selectedScenery.IsUndefined() && gSceneryPlaceCost != kMoney64Undefined) + { + price = gSceneryPlaceCost; + } + else + { + switch (selectedScenery.SceneryType) + { + case SCENERY_TYPE_SMALL: + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_PATH_ITEM: + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_WALL: + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_LARGE: + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_BANNER: + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( + selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + } + } + return { name, price }; + } + + void DrawTabs(DrawPixelInfo& dpi, const ScreenCoordsXY& offset) + { + for (size_t tabIndex = 0; tabIndex < _tabEntries.size(); tabIndex++) + { + auto widgetIndex = static_cast(WIDX_SCENERY_TAB_1 + tabIndex); + auto widgetCoordsXY = ScreenCoordsXY(widgets[widgetIndex].left, widgets[widgetIndex].top); + + if (_tabEntries[tabIndex].IsAll()) + { + auto imageId = ImageId(SPR_G2_INFINITY, FilterPaletteID::PaletteNull); + GfxDrawSprite(dpi, imageId, offset + widgetCoordsXY + ScreenCoordsXY(2, 6)); + } + } + } + + void DrawSceneryItem(DrawPixelInfo& dpi, ScenerySelection scenerySelection) + { + if (scenerySelection.SceneryType == SCENERY_TYPE_BANNER) + { + auto bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); + auto imageId = ImageId(bannerEntry->image + gWindowSceneryRotation * 2, gWindowSceneryPrimaryColour); + GfxDrawSprite(dpi, imageId, { 33, 40 }); + GfxDrawSprite(dpi, imageId.WithIndexOffset(1), { 33, 40 }); + } + else if (scenerySelection.SceneryType == SCENERY_TYPE_LARGE) + { + auto sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); + auto imageId = ImageId(sceneryEntry->image + gWindowSceneryRotation); + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) + imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) + imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); + GfxDrawSprite(dpi, imageId, { 33, 0 }); + } + else if (scenerySelection.SceneryType == SCENERY_TYPE_WALL) + { + auto wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); + auto imageId = ImageId(wallEntry->image); + auto spriteTop = (wallEntry->height * 2) + 0x32; + if (wallEntry->flags & WALL_SCENERY_HAS_GLASS) + { + imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); + if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) + { + imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); + } + GfxDrawSprite(dpi, imageId, { 47, spriteTop }); + + auto glassImageId = ImageId(wallEntry->image + 6).WithTransparency(gWindowSceneryPrimaryColour); + GfxDrawSprite(dpi, glassImageId, { 47, spriteTop }); + } + else + { + imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); + if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) + { + imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); + if (wallEntry->flags & WALL_SCENERY_HAS_TERTIARY_COLOUR) + { + imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); + } + } + GfxDrawSprite(dpi, imageId, { 47, spriteTop }); + + if (wallEntry->flags & WALL_SCENERY_IS_DOOR) + { + GfxDrawSprite(dpi, imageId.WithIndexOffset(1), { 47, spriteTop }); + } + } + } + else if (scenerySelection.SceneryType == SCENERY_TYPE_PATH_ITEM) + { + auto* pathAdditionEntry = OpenRCT2::ObjectManager::GetObjectEntry( + scenerySelection.EntryIndex); + auto imageId = ImageId(pathAdditionEntry->image); + GfxDrawSprite(dpi, imageId, { 11, 16 }); + } + else + { + auto sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); + auto imageId = ImageId(sceneryEntry->image + gWindowSceneryRotation); + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) + { + imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) + { + imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); + } + } + if (sceneryEntry->flags & SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + { + imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); + } + + auto spriteTop = (sceneryEntry->height / 4) + 43; + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) + && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) + { + spriteTop -= 12; + } + + GfxDrawSprite(dpi, imageId, { 32, spriteTop }); + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_GLASS)) + { + imageId = ImageId(sceneryEntry->image + 4 + gWindowSceneryRotation) + .WithTransparency(gWindowSceneryPrimaryColour); + GfxDrawSprite(dpi, imageId, { 32, spriteTop }); + } + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED_FG)) + { + imageId = ImageId(sceneryEntry->image + 4 + gWindowSceneryRotation); + GfxDrawSprite(dpi, imageId, { 32, spriteTop }); + } + } + } + + void ContentScrollDraw(DrawPixelInfo& dpi) + { + GfxClear(&dpi, ColourMapA[colours[1]].mid_light); + + auto numColumns = GetNumColumns(); + auto tabIndex = _activeTabIndex; + if (tabIndex >= _tabEntries.size()) + { + return; + } + + ScreenCoordsXY topLeft{ 0, 0 }; + + for (size_t sceneryTabItemIndex = 0; sceneryTabItemIndex < _filteredSceneryTab.Entries.size(); + sceneryTabItemIndex++) + { + const auto& currentSceneryGlobal = _filteredSceneryTab.Entries[sceneryTabItemIndex]; + const auto tabSelectedScenery = GetSelectedScenery(tabIndex); + if (gWindowSceneryPaintEnabled == 1 || gWindowSceneryEyedropperEnabled) + { + if (_selectedScenery == currentSceneryGlobal) + { + GfxFillRectInset( + dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, + colours[1], INSET_RECT_FLAG_FILL_MID_LIGHT); + } + } + else + { + if (tabSelectedScenery == currentSceneryGlobal) + { + GfxFillRectInset( + dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, + colours[1], (INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_MID_LIGHT)); + } + else if (_selectedScenery == currentSceneryGlobal) + { + GfxFillRectInset( + dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, + colours[1], INSET_RECT_FLAG_FILL_MID_LIGHT); + } + } + + DrawPixelInfo clipdpi; + if (ClipDrawPixelInfo( + clipdpi, dpi, topLeft + ScreenCoordsXY{ 1, 1 }, SCENERY_BUTTON_WIDTH - 2, SCENERY_BUTTON_HEIGHT - 2)) + { + DrawSceneryItem(clipdpi, currentSceneryGlobal); + } + + topLeft.x += SCENERY_BUTTON_WIDTH; + if (topLeft.x >= numColumns * SCENERY_BUTTON_WIDTH) + { + topLeft.y += SCENERY_BUTTON_HEIGHT; + topLeft.x = 0; + } + } + } + }; + + WindowBase* WindowSceneryOpen() + { + auto* w = static_cast(WindowBringToFrontByClass(WindowClass::Scenery)); + if (w == nullptr) + { + w = WindowCreate(WindowClass::Scenery); + } + return w; } - void SetSelectedItem( + void WindowScenerySetSelectedItem( const ScenerySelection& scenery, const std::optional primary, const std::optional secondary, const std::optional tertiary, const std::optional rotation) { - auto tabIndex = FindTabWithScenery(scenery); - if (!tabIndex.has_value()) + auto* w = static_cast(WindowBringToFrontByClass(WindowClass::Scenery)); + if (w != nullptr) { - return; - } - - _activeTabIndex = tabIndex.value(); - SetSelectedScenery(tabIndex.value(), scenery); - if (primary.has_value()) - { - gWindowSceneryPrimaryColour = primary.value(); - } - if (secondary.has_value()) - { - gWindowScenerySecondaryColour = secondary.value(); - } - if (tertiary.has_value()) - { - gWindowSceneryTertiaryColour = tertiary.value(); - } - if (rotation.has_value()) - { - gWindowSceneryRotation = rotation.value(); - } - gWindowSceneryEyedropperEnabled = false; - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, ContextGetWidth() / 2); - _hoverCounter = -16; - gSceneryPlaceCost = kMoney64Undefined; - Invalidate(); - } - - void SetSelectedTab(const ObjectEntryIndex sceneryGroupIndex) - { - const auto* tabInfo = GetSceneryTabInfoForGroup(sceneryGroupIndex); - if (tabInfo == nullptr) - { - tabInfo = &_tabEntries.back(); - } - const auto tabId = std::distance(&*_tabEntries.cbegin(), tabInfo); - - OnMouseDown(WIDX_SCENERY_TAB_1 + tabId); - } - - const ScenerySelection GetTabSelection() - { - return GetSelectedScenery(_activeTabIndex); - } - - void Init() - { - _tabEntries.clear(); - - for (ObjectEntryIndex scenerySetIndex = 0; scenerySetIndex < MaxTabs - ReservedTabCount; scenerySetIndex++) - { - const auto* sceneryGroupEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySetIndex); - if (sceneryGroupEntry != nullptr && SceneryGroupIsInvented(scenerySetIndex)) - { - SceneryTabInfo tabInfo; - tabInfo.SceneryGroupIndex = scenerySetIndex; - for (const auto& sceneryEntry : sceneryGroupEntry->SceneryEntries) - { - if (IsSceneryAvailableToBuild(sceneryEntry)) - { - tabInfo.AddEntryToBack(sceneryEntry); - } - } - if (tabInfo.Entries.size() > 0) - { - _tabEntries.push_back(std::move(tabInfo)); - } - } - } - - // Sort scenery group tabs before adding other tabs - SortTabs(); - - // Add misc and all tab - _tabEntries.emplace_back(SceneryWindow::SceneryTabInfo{ SCENERY_TAB_TYPE_MISC }); - _tabEntries.emplace_back(SceneryWindow::SceneryTabInfo{ SCENERY_TAB_TYPE_ALL }); - - // small scenery - for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_SMALL_SCENERY_OBJECTS; sceneryId++) - { - const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); - if (sceneryEntry != nullptr) - { - InitSceneryEntry({ SCENERY_TYPE_SMALL, sceneryId }, sceneryEntry->scenery_tab_id); - } - } - - // large scenery - for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_LARGE_SCENERY_OBJECTS; sceneryId++) - { - const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); - if (sceneryEntry != nullptr) - { - InitSceneryEntry({ SCENERY_TYPE_LARGE, sceneryId }, sceneryEntry->scenery_tab_id); - } - } - - // walls - for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_WALL_SCENERY_OBJECTS; sceneryId++) - { - const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); - if (sceneryEntry != nullptr) - { - InitSceneryEntry({ SCENERY_TYPE_WALL, sceneryId }, sceneryEntry->scenery_tab_id); - } - } - - // banners - for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_BANNER_OBJECTS; sceneryId++) - { - const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); - if (sceneryEntry != nullptr) - { - InitSceneryEntry({ SCENERY_TYPE_BANNER, sceneryId }, sceneryEntry->scenery_tab_id); - } - } - - for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_PATH_ADDITION_OBJECTS; sceneryId++) - { - const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryId); - if (sceneryEntry != nullptr) - { - InitSceneryEntry({ SCENERY_TYPE_PATH_ITEM, sceneryId }, sceneryEntry->scenery_tab_id); - } - } - - // Remove empty tabs - _tabEntries.erase( - std::remove_if( - _tabEntries.begin(), _tabEntries.end(), [](const SceneryTabInfo& tabInfo) { return tabInfo.Entries.empty(); }), - _tabEntries.end()); - - // Set required width - _requiredWidth = std::min(static_cast(_tabEntries.size()), MaxTabsPerRow) * TabWidth + 5; - - PrepareWidgets(); - WindowInvalidateByClass(WindowClass::Scenery); - } - - int32_t GetRequiredWidth() const - { - return std::max(_requiredWidth, WINDOW_SCENERY_MIN_WIDTH); - } - -private: - int32_t GetNumColumns() const - { - const auto& listWidget = widgets[WIDX_SCENERY_LIST]; - const auto contentWidth = listWidget.width() - kScrollBarWidth; - return contentWidth / SCENERY_BUTTON_WIDTH; - } - - template T CountRows(T items) const - { - const auto rows = items / GetNumColumns(); - return rows; - } - - size_t CountRows() const - { - const auto tabIndex = _activeTabIndex; - if (tabIndex >= _tabEntries.size()) - { - return 0; - } - - const auto totalItems = _filteredSceneryTab.Entries.size(); - const auto numColumns = GetNumColumns(); - const auto rows = CountRows(totalItems + numColumns - 1); - return rows; - } - - int32_t constexpr ContentRowsHeight(const size_t rows) const - { - return static_cast(rows * SCENERY_BUTTON_HEIGHT); - } - - void ContentUpdateScroll() - { - const auto tabIndex = _activeTabIndex; - if (tabIndex >= _tabEntries.size()) - { - return; - } - - SetFilteredScenery(tabIndex); - - const int32_t listHeight = height - 14 - widgets[WIDX_SCENERY_LIST].top - 1; - - const auto sceneryItem = ContentCountRowsWithSelectedItem(tabIndex); - scrolls[SceneryContentScrollIndex].v_bottom = ContentRowsHeight(sceneryItem.allRows) + 1; - - const int32_t maxTop = std::max(0, scrolls[SceneryContentScrollIndex].v_bottom - listHeight); - auto rowSelected = CountRows(sceneryItem.selected_item); - if (sceneryItem.scenerySelection.IsUndefined()) - { - auto& oldScenery = GetSelectedScenery(tabIndex); - if (!oldScenery.IsUndefined()) - { - // Make sure last selected scenery for tab is always present - _filteredSceneryTab.AddEntryToFront(oldScenery); - } - else - { - // No last selection was made, use blank ScenerySelection - SetSelectedScenery(tabIndex, ScenerySelection()); - rowSelected = 0; - if (!_filteredSceneryTab.Entries.empty()) - { - // Select first item in current filtered tab if available - const auto& scenery = _filteredSceneryTab.Entries.front(); - if (!scenery.IsUndefined()) - { - SetSelectedScenery(tabIndex, scenery); - } - } - } - } - - scrolls[SceneryContentScrollIndex].v_top = ContentRowsHeight(rowSelected); - scrolls[SceneryContentScrollIndex].v_top = std::min(maxTop, scrolls[SceneryContentScrollIndex].v_top); - - WidgetScrollUpdateThumbs(*this, WIDX_SCENERY_LIST); - } - - SceneryItem ContentCountRowsWithSelectedItem(const size_t tabIndex) - { - SceneryItem sceneryItem = { 0, 0, ScenerySelection() }; - const auto scenerySelection = GetSelectedScenery(tabIndex); - for (size_t i = 0; i < _filteredSceneryTab.Entries.size(); i++) - { - const auto& currentEntry = _filteredSceneryTab.Entries[i]; - if (currentEntry == scenerySelection) - { - sceneryItem.selected_item = static_cast(i); - sceneryItem.scenerySelection = scenerySelection; - } - } - sceneryItem.allRows = static_cast(CountRows(_filteredSceneryTab.Entries.size() + 8)); - return sceneryItem; - } - - const ScenerySelection GetSelectedScenery(const size_t tabIndex) - { - if (_tabSelections.size() > tabIndex) - { - return _tabSelections[tabIndex]; - } - return {}; - } - - void SetSelectedScenery(const size_t tabIndex, const ScenerySelection& value) - { - if (_tabSelections.size() <= tabIndex) - { - _tabSelections.resize(tabIndex + 1); - } - _tabSelections[tabIndex] = value; - } - - SceneryTabInfo* GetSceneryTabInfoForMisc() - { - if (_tabEntries.size() >= 2) - { - if (_tabEntries[_tabEntries.size() - 2].IsMisc()) - return &_tabEntries[_tabEntries.size() - 2]; - } - - return nullptr; - } - - SceneryTabInfo* GetSceneryTabInfoForAll() - { - if (!_tabEntries.empty()) - { - if (_tabEntries.back().IsAll()) - return &_tabEntries.back(); - } - - return nullptr; - } - - SceneryTabInfo* GetSceneryTabInfoForGroup(const ObjectEntryIndex sceneryGroupIndex) - { - for (auto& tabEntry : _tabEntries) - { - if (tabEntry.SceneryGroupIndex == sceneryGroupIndex) - return &tabEntry; - } - - return nullptr; - } - - std::optional FindTabWithScenery(const ScenerySelection& scenery) - { - for (size_t i = 0; i < _tabEntries.size(); i++) - { - const auto& tabInfo = _tabEntries[i]; - if (tabInfo.IsAll()) - { - // The scenery will be always added here so exclude this one. - continue; - } - if (tabInfo.Contains(scenery)) - { - return i; - } - } - return std::nullopt; - } - - void InitSceneryEntry(const ScenerySelection& selection, const ObjectEntryIndex sceneryGroupIndex) - { - Guard::ArgumentInRange(selection.EntryIndex, 0, OBJECT_ENTRY_INDEX_NULL); - - if (IsSceneryAvailableToBuild(selection)) - { - // Add scenery to all tab - auto* allTabInfo = GetSceneryTabInfoForAll(); - if (allTabInfo != nullptr) - { - allTabInfo->AddEntryToBack(selection); - } - - // Add scenery to primary group (usually trees or path additions) - if (sceneryGroupIndex != OBJECT_ENTRY_INDEX_NULL) - { - auto* tabInfo = GetSceneryTabInfoForGroup(sceneryGroupIndex); - if (tabInfo != nullptr) - { - tabInfo->AddEntryToBack(selection); - return; - } - } - - // If scenery has no tab, add it to misc - const auto tabIndex = FindTabWithScenery(selection); - if (!tabIndex.has_value()) - { - auto* tabInfo = GetSceneryTabInfoForMisc(); - if (tabInfo != nullptr) - { - tabInfo->AddEntryToBack(selection); - } - } + w->SetSelectedItem(scenery, primary, secondary, tertiary, rotation); } } - void SetFilteredScenery(const size_t tabIndex) + void WindowScenerySetSelectedTab(const ObjectEntryIndex sceneryGroupIndex) { - auto currentTab = _tabEntries[tabIndex]; - - _filteredSceneryTab.Entries.clear(); - _filteredSceneryTab.Filter = currentTab.Filter; - - for (auto selection : currentTab.Entries) + // Should this bring to front? + auto* w = static_cast(WindowFindByClass(WindowClass::Scenery)); + if (w != nullptr) { - if (MatchFilter(selection)) - _filteredSceneryTab.AddEntryToBack(selection); + return w->SetSelectedTab(sceneryGroupIndex); } } - bool MatchFilter(const ScenerySelection& selection) + // Used after removing objects, in order to avoid crashes. + void WindowSceneryResetSelectedSceneryItems() { - if (_filteredSceneryTab.Filter.empty()) - return true; - - auto& objManager = GetContext()->GetObjectManager(); - auto sceneryObjectType = GetObjectTypeFromSceneryType(selection.SceneryType); - auto sceneryObject = objManager.GetLoadedObject(sceneryObjectType, selection.EntryIndex); - - return IsFilterInName(*sceneryObject) || IsFilterInAuthors(*sceneryObject) || IsFilterInIdentifier(*sceneryObject) - || IsFilterInFilename(*sceneryObject); + _tabSelections.clear(); + _activeTabIndex = 0; } - bool IsFilterInName(const Object& object) + void WindowScenerySetDefaultPlacementConfiguration() { - return String::Contains(object.GetName(), _filteredSceneryTab.Filter, true); + gWindowSceneryRotation = 3; + gWindowSceneryPrimaryColour = COLOUR_BORDEAUX_RED; + gWindowScenerySecondaryColour = COLOUR_YELLOW; + gWindowSceneryTertiaryColour = COLOUR_DARK_BROWN; + + WindowSceneryResetSelectedSceneryItems(); } - bool IsFilterInAuthors(const Object& object) + const ScenerySelection WindowSceneryGetTabSelection() { - for (auto author : object.GetAuthors()) - if (String::Contains(author, _filteredSceneryTab.Filter, true)) - return true; - - return false; - } - - bool IsFilterInIdentifier(const Object& object) - { - return String::Contains(object.GetIdentifier(), _filteredSceneryTab.Filter, true); - } - - bool IsFilterInFilename(const Object& object) - { - auto repoItem = ObjectRepositoryFindObjectByEntry(&(object.GetObjectEntry())); - return String::Contains(repoItem->Path, _filteredSceneryTab.Filter, true); - } - - void SortTabs() - { - std::sort(_tabEntries.begin(), _tabEntries.end(), [](const SceneryTabInfo& a, const SceneryTabInfo& b) { - if (a.SceneryGroupIndex == b.SceneryGroupIndex) - return false; - - if (a.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) - return false; - if (b.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) - return true; - - const auto* entryA = a.GetSceneryGroupEntry(); - const auto* entryB = b.GetSceneryGroupEntry(); - return entryA->priority < entryB->priority; - }); - } - - int32_t GetTabRowCount() - { - return std::max((static_cast(_tabEntries.size()) + MaxTabsPerRow - 1) / MaxTabsPerRow, 0); - } - - int32_t GetMaxTabCountInARow() - { - int32_t tabEntries = static_cast(_tabEntries.size()); - return std::min(tabEntries, MaxTabsPerRow); - } - - void PrepareWidgets() - { - // Add the base widgets - _widgets.clear(); - for (const auto& widget : WindowSceneryBaseWidgets) + auto* w = static_cast(WindowFindByClass(WindowClass::Scenery)); + if (w != nullptr) { - _widgets.push_back(widget); - } - - // Remove WWT_LAST - auto lastWidget = _widgets.back(); - _widgets.pop_back(); - - // Add tabs - _actualMinHeight = WINDOW_SCENERY_MIN_HEIGHT; - int32_t xInit = InitTabPosX; - int32_t tabsInThisRow = 0; - - auto hasMisc = GetSceneryTabInfoForMisc() != nullptr; - auto maxTabsInThisRow = MaxTabsPerRow - 1 - (hasMisc ? 1 : 0); - - ScreenCoordsXY pos = { xInit, InitTabPosY }; - for (const auto& tabInfo : _tabEntries) - { - auto widget = MakeTab(pos, STR_STRING_DEFINED_TOOLTIP); - pos.x += TabWidth; - - if (tabInfo.IsMisc()) - { - widget.image = ImageId(SPR_TAB_QUESTION, FilterPaletteID::PaletteNull); - } - else if (tabInfo.IsAll()) - { - widget.image = ImageId(SPR_TAB, FilterPaletteID::PaletteNull); - } - else if (tabInfo.IsSceneryGroup()) - { - // Default tab image - widget.image = ImageId(SPR_TAB_QUESTION, FilterPaletteID::PaletteNull); - - // Scenery Group image - auto scgEntry = tabInfo.GetSceneryGroupEntry(); - if (scgEntry != nullptr) - { - widget.image = ImageId(scgEntry->image, colours[1]); - } - } - - _widgets.push_back(widget); - - tabsInThisRow++; - if (tabsInThisRow >= maxTabsInThisRow) - { - pos.x = xInit; - pos.y += TabHeight; - tabsInThisRow = 0; - _actualMinHeight += TabHeight; - maxTabsInThisRow = MaxTabsPerRow; - } - } - - _widgets.push_back(lastWidget); - - // Shift base widgets based on number of tab rows - int32_t shiftAmount = (GetTabRowCount() - 1) * TabHeight; - if (shiftAmount > 0) - { - _widgets[WIDX_SCENERY_LIST].top += shiftAmount; - _widgets[WIDX_SCENERY_LIST].bottom += shiftAmount; - _widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].top += shiftAmount; - _widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].bottom += shiftAmount; - _widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].top += shiftAmount; - _widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].bottom += shiftAmount; - _widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].top += shiftAmount; - _widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].bottom += shiftAmount; - _widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].top += shiftAmount; - _widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].bottom += shiftAmount; - _widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].top += shiftAmount; - _widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].bottom += shiftAmount; - _widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].top += shiftAmount; - _widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].bottom += shiftAmount; - _widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].top += shiftAmount; - _widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].bottom += shiftAmount; - _widgets[WIDX_FILTER_TEXT_BOX].top += shiftAmount; - _widgets[WIDX_FILTER_TEXT_BOX].bottom += shiftAmount; - _widgets[WIDX_FILTER_CLEAR_BUTTON].top += shiftAmount; - _widgets[WIDX_FILTER_CLEAR_BUTTON].bottom += shiftAmount; - } - - widgets = _widgets.data(); - } - - ScenerySelection GetSceneryIdByCursorPos(const ScreenCoordsXY& screenCoords) const - { - ScenerySelection scenery{}; - - const auto numColumns = GetNumColumns(); - const auto colIndex = screenCoords.x / SCENERY_BUTTON_WIDTH; - const auto rowIndex = screenCoords.y / SCENERY_BUTTON_HEIGHT; - if (colIndex >= 0 && colIndex < numColumns && rowIndex >= 0) - { - const auto tabSceneryIndex = static_cast((rowIndex * numColumns) + colIndex); - if (tabSceneryIndex < _filteredSceneryTab.Entries.size()) - { - return _filteredSceneryTab.Entries[tabSceneryIndex]; - } - } - return scenery; - } - - ScreenSize ContentScrollGetSize() const - { - auto rows = CountRows(); - return { 0, ContentRowsHeight(rows) }; - } - - void ContentScrollMouseDown(const ScreenCoordsXY& screenCoords) - { - const auto scenery = GetSceneryIdByCursorPos(screenCoords); - if (scenery.IsUndefined()) - return; - - auto lastScenery = GetSelectedScenery(_activeTabIndex); - if (lastScenery != scenery && !MatchFilter(lastScenery)) - { - _filteredSceneryTab.Entries.pop_front(); - } - SetSelectedScenery(_activeTabIndex, scenery); - - gWindowSceneryPaintEnabled &= 0xFE; - gWindowSceneryEyedropperEnabled = false; - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); - _hoverCounter = -16; - gSceneryPlaceCost = kMoney64Undefined; - Invalidate(); - } - - void ContentScrollMouseOver(const ScreenCoordsXY& screenCoords) - { - ScenerySelection scenery = GetSceneryIdByCursorPos(screenCoords); - if (!scenery.IsUndefined()) - { - _selectedScenery = scenery; - Invalidate(); - } - } - - std::pair GetNameAndPrice(ScenerySelection selectedScenery) - { - StringId name = STR_UNKNOWN_OBJECT_TYPE; - money64 price = kMoney64Undefined; - if (selectedScenery.IsUndefined() && gSceneryPlaceCost != kMoney64Undefined) - { - price = gSceneryPlaceCost; + return w->GetTabSelection(); } else { - switch (selectedScenery.SceneryType) - { - case SCENERY_TYPE_SMALL: - { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selectedScenery.EntryIndex); - if (sceneryEntry != nullptr) - { - price = sceneryEntry->price; - name = sceneryEntry->name; - } - break; - } - case SCENERY_TYPE_PATH_ITEM: - { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selectedScenery.EntryIndex); - if (sceneryEntry != nullptr) - { - price = sceneryEntry->price; - name = sceneryEntry->name; - } - break; - } - case SCENERY_TYPE_WALL: - { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selectedScenery.EntryIndex); - if (sceneryEntry != nullptr) - { - price = sceneryEntry->price; - name = sceneryEntry->name; - } - break; - } - case SCENERY_TYPE_LARGE: - { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selectedScenery.EntryIndex); - if (sceneryEntry != nullptr) - { - price = sceneryEntry->price; - name = sceneryEntry->name; - } - break; - } - case SCENERY_TYPE_BANNER: - { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry( - selectedScenery.EntryIndex); - if (sceneryEntry != nullptr) - { - price = sceneryEntry->price; - name = sceneryEntry->name; - } - break; - } - } + return {}; } - return { name, price }; } - void DrawTabs(DrawPixelInfo& dpi, const ScreenCoordsXY& offset) + void WindowSceneryInit() { - for (size_t tabIndex = 0; tabIndex < _tabEntries.size(); tabIndex++) + auto* w = static_cast(WindowFindByClass(WindowClass::Scenery)); + if (w != nullptr) { - auto widgetIndex = static_cast(WIDX_SCENERY_TAB_1 + tabIndex); - auto widgetCoordsXY = ScreenCoordsXY(widgets[widgetIndex].left, widgets[widgetIndex].top); - - if (_tabEntries[tabIndex].IsAll()) - { - auto imageId = ImageId(SPR_G2_INFINITY, FilterPaletteID::PaletteNull); - GfxDrawSprite(dpi, imageId, offset + widgetCoordsXY + ScreenCoordsXY(2, 6)); - } + w->Init(); } } - - void DrawSceneryItem(DrawPixelInfo& dpi, ScenerySelection scenerySelection) - { - if (scenerySelection.SceneryType == SCENERY_TYPE_BANNER) - { - auto bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); - auto imageId = ImageId(bannerEntry->image + gWindowSceneryRotation * 2, gWindowSceneryPrimaryColour); - GfxDrawSprite(dpi, imageId, { 33, 40 }); - GfxDrawSprite(dpi, imageId.WithIndexOffset(1), { 33, 40 }); - } - else if (scenerySelection.SceneryType == SCENERY_TYPE_LARGE) - { - auto sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); - auto imageId = ImageId(sceneryEntry->image + gWindowSceneryRotation); - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) - imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) - imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) - imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); - GfxDrawSprite(dpi, imageId, { 33, 0 }); - } - else if (scenerySelection.SceneryType == SCENERY_TYPE_WALL) - { - auto wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); - auto imageId = ImageId(wallEntry->image); - auto spriteTop = (wallEntry->height * 2) + 0x32; - if (wallEntry->flags & WALL_SCENERY_HAS_GLASS) - { - imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); - if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) - { - imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); - } - GfxDrawSprite(dpi, imageId, { 47, spriteTop }); - - auto glassImageId = ImageId(wallEntry->image + 6).WithTransparency(gWindowSceneryPrimaryColour); - GfxDrawSprite(dpi, glassImageId, { 47, spriteTop }); - } - else - { - imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); - if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) - { - imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); - if (wallEntry->flags & WALL_SCENERY_HAS_TERTIARY_COLOUR) - { - imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); - } - } - GfxDrawSprite(dpi, imageId, { 47, spriteTop }); - - if (wallEntry->flags & WALL_SCENERY_IS_DOOR) - { - GfxDrawSprite(dpi, imageId.WithIndexOffset(1), { 47, spriteTop }); - } - } - } - else if (scenerySelection.SceneryType == SCENERY_TYPE_PATH_ITEM) - { - auto* pathAdditionEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); - auto imageId = ImageId(pathAdditionEntry->image); - GfxDrawSprite(dpi, imageId, { 11, 16 }); - } - else - { - auto sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(scenerySelection.EntryIndex); - auto imageId = ImageId(sceneryEntry->image + gWindowSceneryRotation); - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) - { - imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) - { - imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); - } - } - if (sceneryEntry->flags & SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR) - { - imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); - } - - auto spriteTop = (sceneryEntry->height / 4) + 43; - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) - { - spriteTop -= 12; - } - - GfxDrawSprite(dpi, imageId, { 32, spriteTop }); - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_GLASS)) - { - imageId = ImageId(sceneryEntry->image + 4 + gWindowSceneryRotation) - .WithTransparency(gWindowSceneryPrimaryColour); - GfxDrawSprite(dpi, imageId, { 32, spriteTop }); - } - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED_FG)) - { - imageId = ImageId(sceneryEntry->image + 4 + gWindowSceneryRotation); - GfxDrawSprite(dpi, imageId, { 32, spriteTop }); - } - } - } - - void ContentScrollDraw(DrawPixelInfo& dpi) - { - GfxClear(&dpi, ColourMapA[colours[1]].mid_light); - - auto numColumns = GetNumColumns(); - auto tabIndex = _activeTabIndex; - if (tabIndex >= _tabEntries.size()) - { - return; - } - - ScreenCoordsXY topLeft{ 0, 0 }; - - for (size_t sceneryTabItemIndex = 0; sceneryTabItemIndex < _filteredSceneryTab.Entries.size(); sceneryTabItemIndex++) - { - const auto& currentSceneryGlobal = _filteredSceneryTab.Entries[sceneryTabItemIndex]; - const auto tabSelectedScenery = GetSelectedScenery(tabIndex); - if (gWindowSceneryPaintEnabled == 1 || gWindowSceneryEyedropperEnabled) - { - if (_selectedScenery == currentSceneryGlobal) - { - GfxFillRectInset( - dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, - colours[1], INSET_RECT_FLAG_FILL_MID_LIGHT); - } - } - else - { - if (tabSelectedScenery == currentSceneryGlobal) - { - GfxFillRectInset( - dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, - colours[1], (INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_MID_LIGHT)); - } - else if (_selectedScenery == currentSceneryGlobal) - { - GfxFillRectInset( - dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, - colours[1], INSET_RECT_FLAG_FILL_MID_LIGHT); - } - } - - DrawPixelInfo clipdpi; - if (ClipDrawPixelInfo( - clipdpi, dpi, topLeft + ScreenCoordsXY{ 1, 1 }, SCENERY_BUTTON_WIDTH - 2, SCENERY_BUTTON_HEIGHT - 2)) - { - DrawSceneryItem(clipdpi, currentSceneryGlobal); - } - - topLeft.x += SCENERY_BUTTON_WIDTH; - if (topLeft.x >= numColumns * SCENERY_BUTTON_WIDTH) - { - topLeft.y += SCENERY_BUTTON_HEIGHT; - topLeft.x = 0; - } - } - } -}; - -WindowBase* WindowSceneryOpen() -{ - auto* w = static_cast(WindowBringToFrontByClass(WindowClass::Scenery)); - if (w == nullptr) - { - w = WindowCreate(WindowClass::Scenery); - } - return w; -} - -void WindowScenerySetSelectedItem( - const ScenerySelection& scenery, const std::optional primary, const std::optional secondary, - const std::optional tertiary, const std::optional rotation) -{ - auto* w = static_cast(WindowBringToFrontByClass(WindowClass::Scenery)); - if (w != nullptr) - { - w->SetSelectedItem(scenery, primary, secondary, tertiary, rotation); - } -} - -void WindowScenerySetSelectedTab(const ObjectEntryIndex sceneryGroupIndex) -{ - // Should this bring to front? - auto* w = static_cast(WindowFindByClass(WindowClass::Scenery)); - if (w != nullptr) - { - return w->SetSelectedTab(sceneryGroupIndex); - } -} - -// Used after removing objects, in order to avoid crashes. -void WindowSceneryResetSelectedSceneryItems() -{ - _tabSelections.clear(); - _activeTabIndex = 0; -} - -void WindowScenerySetDefaultPlacementConfiguration() -{ - gWindowSceneryRotation = 3; - gWindowSceneryPrimaryColour = COLOUR_BORDEAUX_RED; - gWindowScenerySecondaryColour = COLOUR_YELLOW; - gWindowSceneryTertiaryColour = COLOUR_DARK_BROWN; - - WindowSceneryResetSelectedSceneryItems(); -} - -const ScenerySelection WindowSceneryGetTabSelection() -{ - auto* w = static_cast(WindowFindByClass(WindowClass::Scenery)); - if (w != nullptr) - { - return w->GetTabSelection(); - } - else - { - return {}; - } -} - -void WindowSceneryInit() -{ - auto* w = static_cast(WindowFindByClass(WindowClass::Scenery)); - if (w != nullptr) - { - w->Init(); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/SceneryScatter.cpp b/src/openrct2-ui/windows/SceneryScatter.cpp index d067c39b3e..cf1014d16b 100644 --- a/src/openrct2-ui/windows/SceneryScatter.cpp +++ b/src/openrct2-ui/windows/SceneryScatter.cpp @@ -15,26 +15,27 @@ #include #include #include - -enum WindowSceneryScatterWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PREVIEW, - WIDX_DECREMENT, - WIDX_INCREMENT, - WIDX_DENSITY, - WIDX_DENSITY_LOW, - WIDX_DENSITY_MEDIUM, - WIDX_DENSITY_HIGH -}; + enum WindowSceneryScatterWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PREVIEW, + WIDX_DECREMENT, + WIDX_INCREMENT, + WIDX_DENSITY, + WIDX_DENSITY_LOW, + WIDX_DENSITY_MEDIUM, + WIDX_DENSITY_HIGH + }; -bool gWindowSceneryScatterEnabled = false; -uint16_t gWindowSceneryScatterSize; -ScatterToolDensity gWindowSceneryScatterDensity; + bool gWindowSceneryScatterEnabled = false; + uint16_t gWindowSceneryScatterSize; + ScatterToolDensity gWindowSceneryScatterDensity; -// clang-format off + // clang-format off static Widget _sceneryScatterWidgets[] = { MakeWidget ({ 0, 0}, {86, 100}, WindowWidgetType::Frame, WindowColour::Secondary ), // panel / background MakeWidget ({ 1, 1}, {84, 14}, WindowWidgetType::Caption, WindowColour::Primary , STR_SCENERY_SCATTER, STR_WINDOW_TITLE_TIP ), // title bar @@ -50,161 +51,165 @@ static Widget _sceneryScatterWidgets[] = { MakeRemapWidget({55, 68}, {24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_SCENERY_SCATTER_HIGH, STR_SCATTER_TOOL_DENSITY_HIGH ), // high amount kWidgetsEnd, }; -// clang-format on + // clang-format on -class SceneryScatterWindow final : public WindowBase -{ -public: - void OnOpen() override + class SceneryScatterWindow final : public WindowBase { - widgets = _sceneryScatterWidgets; - hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); - WindowInitScrollWidgets(*this); - WindowPushOthersBelow(*this); - - gWindowSceneryScatterEnabled = true; - gWindowSceneryScatterSize = 16; - gWindowSceneryScatterDensity = ScatterToolDensity::MediumDensity; - } - - void OnClose() override - { - gWindowSceneryScatterEnabled = false; - } - - void InputSize(const WidgetIndex widgetIndex) - { - uint8_t maxLength = 0; - Formatter ft; - - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_PREVIEW: - ft.Add(kLandToolMinimumSize); - ft.Add(kLandToolMaximumSize); - maxLength = 3; - break; + widgets = _sceneryScatterWidgets; + hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); + WindowInitScrollWidgets(*this); + WindowPushOthersBelow(*this); + + gWindowSceneryScatterEnabled = true; + gWindowSceneryScatterSize = 16; + gWindowSceneryScatterDensity = ScatterToolDensity::MediumDensity; } - WindowTextInputOpen(this, widgetIndex, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, maxLength); - } - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnClose() override { - case WIDX_CLOSE: - WindowClose(*this); - break; - - case WIDX_PREVIEW: - InputSize(widgetIndex); - break; - - case WIDX_DENSITY_LOW: - gWindowSceneryScatterDensity = ScatterToolDensity::LowDensity; - break; - - case WIDX_DENSITY_MEDIUM: - gWindowSceneryScatterDensity = ScatterToolDensity::MediumDensity; - break; - - case WIDX_DENSITY_HIGH: - gWindowSceneryScatterDensity = ScatterToolDensity::HighDensity; - break; + gWindowSceneryScatterEnabled = false; } - } - void OnMouseDown(const WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void InputSize(const WidgetIndex widgetIndex) { - case WIDX_DECREMENT: - // Decrement land tool size, if it stays within the limit - gWindowSceneryScatterSize = std::max(kLandToolMinimumSize, gWindowSceneryScatterSize - 1); - Invalidate(); - break; + uint8_t maxLength = 0; + Formatter ft; - case WIDX_INCREMENT: - // Increment land tool size, if it stays within the limit - gWindowSceneryScatterSize = std::min(kLandToolMaximumSize, gWindowSceneryScatterSize + 1); - Invalidate(); - break; - } - } - - void OnTextInput(const WidgetIndex widgetIndex, const std::string_view text) override - { - if (widgetIndex != WIDX_PREVIEW || text.empty()) - return; - - const auto res = String::Parse(text); - - if (res.has_value()) - { switch (widgetIndex) { case WIDX_PREVIEW: - gWindowSceneryScatterSize = std::clamp(res.value(), kLandToolMinimumSize, kLandToolMaximumSize); + ft.Add(kLandToolMinimumSize); + ft.Add(kLandToolMaximumSize); + maxLength = 3; break; } - Invalidate(); + WindowTextInputOpen( + this, widgetIndex, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, maxLength); } - } - void OnPrepareDraw() override - { - // Set the preview image button to be pressed down - pressed_widgets = (1uLL << WIDX_PREVIEW); - - // Set density buttons' pressed state. - switch (gWindowSceneryScatterDensity) + void OnMouseUp(WidgetIndex widgetIndex) override { - case ScatterToolDensity::LowDensity: - pressed_widgets |= (1uLL << WIDX_DENSITY_LOW); - break; + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowClose(*this); + break; - case ScatterToolDensity::MediumDensity: - pressed_widgets |= (1uLL << WIDX_DENSITY_MEDIUM); - break; + case WIDX_PREVIEW: + InputSize(widgetIndex); + break; - case ScatterToolDensity::HighDensity: - pressed_widgets |= (1uLL << WIDX_DENSITY_HIGH); - break; + case WIDX_DENSITY_LOW: + gWindowSceneryScatterDensity = ScatterToolDensity::LowDensity; + break; + + case WIDX_DENSITY_MEDIUM: + gWindowSceneryScatterDensity = ScatterToolDensity::MediumDensity; + break; + + case WIDX_DENSITY_HIGH: + gWindowSceneryScatterDensity = ScatterToolDensity::HighDensity; + break; + } } - // Update the preview image (for tool sizes up to 7) - widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gWindowSceneryScatterSize)); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - WindowDrawWidgets(*this, dpi); - - // Draw area as a number for tool sizes bigger than 7 - if (gWindowSceneryScatterSize > kLandToolMaximumSizeWithSprite) + void OnMouseDown(const WidgetIndex widgetIndex) override { - const auto& preview = widgets[WIDX_PREVIEW]; - const auto screenCoords = ScreenCoordsXY{ windowPos.x + preview.midX(), windowPos.y + preview.midY() }; - auto ft = Formatter(); - ft.Add(gWindowSceneryScatterSize); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + switch (widgetIndex) + { + case WIDX_DECREMENT: + // Decrement land tool size, if it stays within the limit + gWindowSceneryScatterSize = std::max(kLandToolMinimumSize, gWindowSceneryScatterSize - 1); + Invalidate(); + break; + + case WIDX_INCREMENT: + // Increment land tool size, if it stays within the limit + gWindowSceneryScatterSize = std::min(kLandToolMaximumSize, gWindowSceneryScatterSize + 1); + Invalidate(); + break; + } } - } - void OnResize() override + void OnTextInput(const WidgetIndex widgetIndex, const std::string_view text) override + { + if (widgetIndex != WIDX_PREVIEW || text.empty()) + return; + + const auto res = String::Parse(text); + + if (res.has_value()) + { + switch (widgetIndex) + { + case WIDX_PREVIEW: + gWindowSceneryScatterSize = std::clamp( + res.value(), kLandToolMinimumSize, kLandToolMaximumSize); + break; + } + Invalidate(); + } + } + + void OnPrepareDraw() override + { + // Set the preview image button to be pressed down + pressed_widgets = (1uLL << WIDX_PREVIEW); + + // Set density buttons' pressed state. + switch (gWindowSceneryScatterDensity) + { + case ScatterToolDensity::LowDensity: + pressed_widgets |= (1uLL << WIDX_DENSITY_LOW); + break; + + case ScatterToolDensity::MediumDensity: + pressed_widgets |= (1uLL << WIDX_DENSITY_MEDIUM); + break; + + case ScatterToolDensity::HighDensity: + pressed_widgets |= (1uLL << WIDX_DENSITY_HIGH); + break; + } + + // Update the preview image (for tool sizes up to 7) + widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gWindowSceneryScatterSize)); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + WindowDrawWidgets(*this, dpi); + + // Draw area as a number for tool sizes bigger than 7 + if (gWindowSceneryScatterSize > kLandToolMaximumSizeWithSprite) + { + const auto& preview = widgets[WIDX_PREVIEW]; + const auto screenCoords = ScreenCoordsXY{ windowPos.x + preview.midX(), windowPos.y + preview.midY() }; + auto ft = Formatter(); + ft.Add(gWindowSceneryScatterSize); + DrawTextBasic( + dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + } + } + + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowSceneryScatterOpen() { - ResizeFrame(); - } -}; + // Check if window is already open + auto* window = WindowFindByClass(WindowClass::SceneryScatter); + if (window == nullptr) + { + window = WindowCreate(WindowClass::SceneryScatter, 86, 100, 0); + } -WindowBase* WindowSceneryScatterOpen() -{ - // Check if window is already open - auto* window = WindowFindByClass(WindowClass::SceneryScatter); - if (window == nullptr) - { - window = WindowCreate(WindowClass::SceneryScatter, 86, 100, 0); + return window; } - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/ServerList.cpp b/src/openrct2-ui/windows/ServerList.cpp index be342dcb81..3f520921e9 100644 --- a/src/openrct2-ui/windows/ServerList.cpp +++ b/src/openrct2-ui/windows/ServerList.cpp @@ -27,39 +27,41 @@ # include # include +namespace OpenRCT2::Ui::Windows +{ # define WWIDTH_MIN 500 # define WHEIGHT_MIN 300 # define WWIDTH_MAX 1200 # define WHEIGHT_MAX 800 # define ITEM_HEIGHT (3 + 9 + 3) -constexpr size_t MaxPlayerNameLength = 32; + constexpr size_t MaxPlayerNameLength = 32; -enum -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_PLAYER_NAME_INPUT, - WIDX_LIST, - WIDX_FETCH_SERVERS, - WIDX_ADD_SERVER, - WIDX_START_SERVER -}; + enum + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PLAYER_NAME_INPUT, + WIDX_LIST, + WIDX_FETCH_SERVERS, + WIDX_ADD_SERVER, + WIDX_START_SERVER + }; -enum -{ - WIDX_LIST_REMOVE, - WIDX_LIST_SPECTATE -}; + enum + { + WIDX_LIST_REMOVE, + WIDX_LIST_SPECTATE + }; -enum -{ - DDIDX_JOIN, - DDIDX_FAVOURITE -}; + enum + { + DDIDX_JOIN, + DDIDX_FAVOURITE + }; -// clang-format off + // clang-format off static Widget _serverListWidgets[] = { MakeWidget({ 0, 0}, {341, 91}, WindowWidgetType::Frame, WindowColour::Primary ), // panel / background MakeWidget({ 1, 1}, {338, 14}, WindowWidgetType::Caption, WindowColour::Primary, STR_SERVER_LIST, STR_WINDOW_TITLE_TIP), // title bar @@ -71,513 +73,516 @@ static Widget _serverListWidgets[] = { MakeWidget({218, 53}, {101, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_START_SERVER ), // start server button kWidgetsEnd, }; -// clang-format on + // clang-format on -void JoinServer(std::string address); + void JoinServer(std::string address); -class ServerListWindow final : public Window -{ -private: - u8string _playerName; - ServerList _serverList; - std::future, StringId>> _fetchFuture; - uint32_t _numPlayersOnline = 0; - StringId _statusText = STR_SERVER_LIST_CONNECTING; + class ServerListWindow final : public Window + { + private: + u8string _playerName; + ServerList _serverList; + std::future, StringId>> _fetchFuture; + uint32_t _numPlayersOnline = 0; + StringId _statusText = STR_SERVER_LIST_CONNECTING; - bool _showNetworkVersionTooltip = false; - std::string _version; + bool _showNetworkVersionTooltip = false; + std::string _version; -public: + public: # pragma region Window Override Events - void OnOpen() override - { - _playerName = gConfigNetwork.PlayerName; - widgets = _serverListWidgets; - _serverListWidgets[WIDX_PLAYER_NAME_INPUT].string = const_cast(_playerName.c_str()); - InitScrollWidgets(); - no_list_items = 0; - selected_list_item = -1; - frame_no = 0; - min_width = 320; - min_height = 90; - max_width = min_width; - max_height = min_height; - - page = 0; - list_information_type = 0; - - WindowSetResize(*this, WWIDTH_MIN, WHEIGHT_MIN, WWIDTH_MAX, WHEIGHT_MAX); - - no_list_items = static_cast(_serverList.GetCount()); - - ServerListFetchServersBegin(); - } - - void OnClose() override - { - _serverList = {}; - _fetchFuture = {}; - ConfigSaveDefault(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_PLAYER_NAME_INPUT: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _playerName.c_str(), MaxPlayerNameLength); - break; - case WIDX_LIST: + _playerName = gConfigNetwork.PlayerName; + widgets = _serverListWidgets; + _serverListWidgets[WIDX_PLAYER_NAME_INPUT].string = const_cast(_playerName.c_str()); + InitScrollWidgets(); + no_list_items = 0; + selected_list_item = -1; + frame_no = 0; + min_width = 320; + min_height = 90; + max_width = min_width; + max_height = min_height; + + page = 0; + list_information_type = 0; + + WindowSetResize(*this, WWIDTH_MIN, WHEIGHT_MIN, WWIDTH_MAX, WHEIGHT_MAX); + + no_list_items = static_cast(_serverList.GetCount()); + + ServerListFetchServersBegin(); + } + + void OnClose() override + { + _serverList = {}; + _fetchFuture = {}; + ConfigSaveDefault(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) { - int32_t serverIndex = selected_list_item; - if (serverIndex >= 0 && serverIndex < static_cast(_serverList.GetCount())) + case WIDX_CLOSE: + Close(); + break; + case WIDX_PLAYER_NAME_INPUT: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _playerName.c_str(), MaxPlayerNameLength); + break; + case WIDX_LIST: { - const auto& server = _serverList.GetServer(serverIndex); - if (server.IsVersionValid()) + int32_t serverIndex = selected_list_item; + if (serverIndex >= 0 && serverIndex < static_cast(_serverList.GetCount())) { - JoinServer(server.Address); - } - else - { - Formatter ft; - ft.Add(server.Version.c_str()); - ContextShowError(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION, ft); - } - } - break; - } - case WIDX_FETCH_SERVERS: - ServerListFetchServersBegin(); - break; - case WIDX_ADD_SERVER: - TextInputOpen(widgetIndex, STR_ADD_SERVER, STR_ENTER_HOSTNAME_OR_IP_ADDRESS, {}, STR_NONE, 0, 128); - break; - case WIDX_START_SERVER: - ContextOpenWindow(WindowClass::ServerStart); - break; - } - } - - void OnResize() override - { - WindowSetResize(*this, WWIDTH_MIN, WHEIGHT_MIN, WWIDTH_MAX, WHEIGHT_MAX); - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (selectedIndex == -1) - { - return; - } - auto serverIndex = selected_list_item; - if (serverIndex >= 0 && serverIndex < static_cast(_serverList.GetCount())) - { - auto& server = _serverList.GetServer(serverIndex); - switch (selectedIndex) - { - case DDIDX_JOIN: - if (server.IsVersionValid()) - { - JoinServer(server.Address); - } - else - { - Formatter ft; - ft.Add(server.Version.c_str()); - ContextShowError(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION, ft); + const auto& server = _serverList.GetServer(serverIndex); + if (server.IsVersionValid()) + { + JoinServer(server.Address); + } + else + { + Formatter ft; + ft.Add(server.Version.c_str()); + ContextShowError(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION, ft); + } } break; - case DDIDX_FAVOURITE: - { - server.Favourite = !server.Favourite; - _serverList.WriteFavourites(); } - break; + case WIDX_FETCH_SERVERS: + ServerListFetchServersBegin(); + break; + case WIDX_ADD_SERVER: + TextInputOpen(widgetIndex, STR_ADD_SERVER, STR_ENTER_HOSTNAME_OR_IP_ADDRESS, {}, STR_NONE, 0, 128); + break; + case WIDX_START_SERVER: + ContextOpenWindow(WindowClass::ServerStart); + break; } } - } - void OnUpdate() override - { - if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + void OnResize() override { - WindowUpdateTextboxCaret(); - InvalidateWidget(WIDX_PLAYER_NAME_INPUT); + WindowSetResize(*this, WWIDTH_MIN, WHEIGHT_MIN, WWIDTH_MAX, WHEIGHT_MAX); } - ServerListFetchServersCheck(); - } - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - return { 0, no_list_items * ITEM_HEIGHT }; - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - int32_t serverIndex = selected_list_item; - if (serverIndex >= 0 && serverIndex < static_cast(_serverList.GetCount())) + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override { - const auto& server = _serverList.GetServer(serverIndex); - - const auto& listWidget = widgets[WIDX_LIST]; - - gDropdownItems[0].Format = STR_JOIN_GAME; - if (server.Favourite) + if (selectedIndex == -1) { - gDropdownItems[1].Format = STR_REMOVE_FROM_FAVOURITES; + return; } - else + auto serverIndex = selected_list_item; + if (serverIndex >= 0 && serverIndex < static_cast(_serverList.GetCount())) { - gDropdownItems[1].Format = STR_ADD_TO_FAVOURITES; + auto& server = _serverList.GetServer(serverIndex); + switch (selectedIndex) + { + case DDIDX_JOIN: + if (server.IsVersionValid()) + { + JoinServer(server.Address); + } + else + { + Formatter ft; + ft.Add(server.Version.c_str()); + ContextShowError(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION, ft); + } + break; + case DDIDX_FAVOURITE: + { + server.Favourite = !server.Favourite; + _serverList.WriteFavourites(); + } + break; + } } - auto dropdownPos = ScreenCoordsXY{ windowPos.x + listWidget.left + screenCoords.x + 2 - scrolls[0].h_left, - windowPos.y + listWidget.top + screenCoords.y + 2 - scrolls[0].v_top }; - WindowDropdownShowText(dropdownPos, 0, COLOUR_GREY, 0, 2); - } - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto& listWidget = widgets[WIDX_LIST]; - - int32_t itemIndex = screenCoords.y / ITEM_HEIGHT; - bool showNetworkVersionTooltip = false; - if (itemIndex < 0 || itemIndex >= no_list_items) - { - itemIndex = -1; - } - else - { - const int32_t iconX = listWidget.width() - kScrollBarWidth - 7 - 10; - showNetworkVersionTooltip = screenCoords.x > iconX; } - if (selected_list_item != itemIndex || _showNetworkVersionTooltip != showNetworkVersionTooltip) + void OnUpdate() override { - selected_list_item = itemIndex; - _showNetworkVersionTooltip = showNetworkVersionTooltip; - - listWidget.tooltip = showNetworkVersionTooltip ? static_cast(STR_NETWORK_VERSION_TIP) : STR_NONE; - WindowTooltipClose(); - - Invalidate(); - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (text.empty()) - return; - - auto temp = u8string{ text }; - - switch (widgetIndex) - { - case WIDX_PLAYER_NAME_INPUT: - if (_playerName == text) - return; - - _playerName = temp; - gConfigNetwork.PlayerName = _playerName; - widgets[WIDX_PLAYER_NAME_INPUT].string = const_cast(_playerName.c_str()); - + if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + { + WindowUpdateTextboxCaret(); InvalidateWidget(WIDX_PLAYER_NAME_INPUT); - break; + } + ServerListFetchServersCheck(); + } - case WIDX_ADD_SERVER: + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + return { 0, no_list_items * ITEM_HEIGHT }; + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int32_t serverIndex = selected_list_item; + if (serverIndex >= 0 && serverIndex < static_cast(_serverList.GetCount())) { - ServerListEntry entry; - entry.Address = text; - entry.Name = text; - entry.Favourite = true; - _serverList.Add(entry); - _serverList.WriteFavourites(); - Invalidate(); - break; + const auto& server = _serverList.GetServer(serverIndex); + + const auto& listWidget = widgets[WIDX_LIST]; + + gDropdownItems[0].Format = STR_JOIN_GAME; + if (server.Favourite) + { + gDropdownItems[1].Format = STR_REMOVE_FROM_FAVOURITES; + } + else + { + gDropdownItems[1].Format = STR_ADD_TO_FAVOURITES; + } + auto dropdownPos = ScreenCoordsXY{ windowPos.x + listWidget.left + screenCoords.x + 2 - scrolls[0].h_left, + windowPos.y + listWidget.top + screenCoords.y + 2 - scrolls[0].v_top }; + WindowDropdownShowText(dropdownPos, 0, COLOUR_GREY, 0, 2); } } - } - OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override - { - auto ft = Formatter(); - ft.Add(_version.c_str()); - return { fallback, ft }; - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PLAYER_NAME_INPUT].top }, STR_PLAYER_NAME, {}, { COLOUR_WHITE }); - - // Draw version number - std::string version = NetworkGetVersion(); - auto ft = Formatter(); - ft.Add(version.c_str()); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 324, widgets[WIDX_START_SERVER].top + 1 }, STR_NETWORK_VERSION, ft, - { COLOUR_WHITE }); - - ft = Formatter(); - ft.Add(_numPlayersOnline); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 8, height - 15 }, _statusText, ft, { COLOUR_WHITE }); - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; - GfxClear(&dpi, paletteIndex); - - auto& listWidget = widgets[WIDX_LIST]; - int32_t listWidgetWidth = listWidget.width(); - - ScreenCoordsXY screenCoords; - screenCoords.y = 0; - for (int32_t i = 0; i < no_list_items; i++) + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - if (screenCoords.y >= dpi.y + dpi.height) - continue; + auto& listWidget = widgets[WIDX_LIST]; - const auto& serverDetails = _serverList.GetServer(i); - bool highlighted = i == selected_list_item; - - // Draw hover highlight - if (highlighted) + int32_t itemIndex = screenCoords.y / ITEM_HEIGHT; + bool showNetworkVersionTooltip = false; + if (itemIndex < 0 || itemIndex >= no_list_items) { - GfxFilterRect( - dpi, { 0, screenCoords.y, listWidgetWidth, screenCoords.y + ITEM_HEIGHT }, FilterPaletteID::PaletteDarken1); - _version = serverDetails.Version; - } - - colour_t colour = colours[1]; - if (serverDetails.Favourite) - { - colour = COLOUR_YELLOW; - } - else if (serverDetails.Local) - { - colour = COLOUR_MOSS_GREEN; - } - - screenCoords.x = 3; - - // Before we draw the server info, we need to know how much room we'll need for player info. - char players[32] = { 0 }; - if (serverDetails.MaxPlayers > 0) - { - snprintf(players, sizeof(players), "%d/%d", serverDetails.Players, serverDetails.MaxPlayers); - } - const int16_t numPlayersStringWidth = GfxGetStringWidth(players, FontStyle::Medium); - - // How much space we have for the server info depends on the size of everything rendered after. - const int16_t spaceAvailableForInfo = listWidgetWidth - numPlayersStringWidth - kScrollBarWidth - 35; - - // Are we showing the server's name or description? - const char* serverInfoToShow = serverDetails.Name.c_str(); - if (highlighted && !serverDetails.Description.empty()) - { - serverInfoToShow = serverDetails.Description.c_str(); - } - - // Finally, draw the server information. - auto ft = Formatter(); - ft.Add(serverInfoToShow); - DrawTextEllipsised(dpi, screenCoords + ScreenCoordsXY{ 0, 3 }, spaceAvailableForInfo, STR_STRING, ft, { colour }); - - int32_t right = listWidgetWidth - 7 - kScrollBarWidth; - - // Draw compatibility icon - right -= 10; - int32_t compatibilitySpriteId; - if (serverDetails.Version.empty()) - { - // Server not online... - compatibilitySpriteId = SPR_G2_RCT1_CLOSE_BUTTON_0; + itemIndex = -1; } else { - // Server online... check version - bool correctVersion = serverDetails.Version == NetworkGetVersion(); - compatibilitySpriteId = correctVersion ? SPR_G2_RCT1_OPEN_BUTTON_2 : SPR_G2_RCT1_CLOSE_BUTTON_2; + const int32_t iconX = listWidget.width() - kScrollBarWidth - 7 - 10; + showNetworkVersionTooltip = screenCoords.x > iconX; } - GfxDrawSprite(dpi, ImageId(compatibilitySpriteId), { right, screenCoords.y + 1 }); - right -= 4; - // Draw lock icon - right -= 8; - if (serverDetails.RequiresPassword) + if (selected_list_item != itemIndex || _showNetworkVersionTooltip != showNetworkVersionTooltip) { - GfxDrawSprite(dpi, ImageId(SPR_G2_LOCKED), { right, screenCoords.y + 4 }); + selected_list_item = itemIndex; + _showNetworkVersionTooltip = showNetworkVersionTooltip; + + listWidget.tooltip = showNetworkVersionTooltip ? static_cast(STR_NETWORK_VERSION_TIP) : STR_NONE; + WindowTooltipClose(); + + Invalidate(); + } + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (text.empty()) + return; + + auto temp = u8string{ text }; + + switch (widgetIndex) + { + case WIDX_PLAYER_NAME_INPUT: + if (_playerName == text) + return; + + _playerName = temp; + gConfigNetwork.PlayerName = _playerName; + widgets[WIDX_PLAYER_NAME_INPUT].string = const_cast(_playerName.c_str()); + + InvalidateWidget(WIDX_PLAYER_NAME_INPUT); + break; + + case WIDX_ADD_SERVER: + { + ServerListEntry entry; + entry.Address = text; + entry.Name = text; + entry.Favourite = true; + _serverList.Add(entry); + _serverList.WriteFavourites(); + Invalidate(); + break; + } + } + } + + OpenRCT2String OnTooltip(WidgetIndex widgetIndex, StringId fallback) override + { + auto ft = Formatter(); + ft.Add(_version.c_str()); + return { fallback, ft }; + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PLAYER_NAME_INPUT].top }, STR_PLAYER_NAME, {}, + { COLOUR_WHITE }); + + // Draw version number + std::string version = NetworkGetVersion(); + auto ft = Formatter(); + ft.Add(version.c_str()); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 324, widgets[WIDX_START_SERVER].top + 1 }, STR_NETWORK_VERSION, ft, + { COLOUR_WHITE }); + + ft = Formatter(); + ft.Add(_numPlayersOnline); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 8, height - 15 }, _statusText, ft, { COLOUR_WHITE }); + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + uint8_t paletteIndex = ColourMapA[colours[1]].mid_light; + GfxClear(&dpi, paletteIndex); + + auto& listWidget = widgets[WIDX_LIST]; + int32_t listWidgetWidth = listWidget.width(); + + ScreenCoordsXY screenCoords; + screenCoords.y = 0; + for (int32_t i = 0; i < no_list_items; i++) + { + if (screenCoords.y >= dpi.y + dpi.height) + continue; + + const auto& serverDetails = _serverList.GetServer(i); + bool highlighted = i == selected_list_item; + + // Draw hover highlight + if (highlighted) + { + GfxFilterRect( + dpi, { 0, screenCoords.y, listWidgetWidth, screenCoords.y + ITEM_HEIGHT }, + FilterPaletteID::PaletteDarken1); + _version = serverDetails.Version; + } + + colour_t colour = colours[1]; + if (serverDetails.Favourite) + { + colour = COLOUR_YELLOW; + } + else if (serverDetails.Local) + { + colour = COLOUR_MOSS_GREEN; + } + + screenCoords.x = 3; + + // Before we draw the server info, we need to know how much room we'll need for player info. + char players[32] = { 0 }; + if (serverDetails.MaxPlayers > 0) + { + snprintf(players, sizeof(players), "%d/%d", serverDetails.Players, serverDetails.MaxPlayers); + } + const int16_t numPlayersStringWidth = GfxGetStringWidth(players, FontStyle::Medium); + + // How much space we have for the server info depends on the size of everything rendered after. + const int16_t spaceAvailableForInfo = listWidgetWidth - numPlayersStringWidth - kScrollBarWidth - 35; + + // Are we showing the server's name or description? + const char* serverInfoToShow = serverDetails.Name.c_str(); + if (highlighted && !serverDetails.Description.empty()) + { + serverInfoToShow = serverDetails.Description.c_str(); + } + + // Finally, draw the server information. + auto ft = Formatter(); + ft.Add(serverInfoToShow); + DrawTextEllipsised( + dpi, screenCoords + ScreenCoordsXY{ 0, 3 }, spaceAvailableForInfo, STR_STRING, ft, { colour }); + + int32_t right = listWidgetWidth - 7 - kScrollBarWidth; + + // Draw compatibility icon + right -= 10; + int32_t compatibilitySpriteId; + if (serverDetails.Version.empty()) + { + // Server not online... + compatibilitySpriteId = SPR_G2_RCT1_CLOSE_BUTTON_0; + } + else + { + // Server online... check version + bool correctVersion = serverDetails.Version == NetworkGetVersion(); + compatibilitySpriteId = correctVersion ? SPR_G2_RCT1_OPEN_BUTTON_2 : SPR_G2_RCT1_CLOSE_BUTTON_2; + } + GfxDrawSprite(dpi, ImageId(compatibilitySpriteId), { right, screenCoords.y + 1 }); + right -= 4; + + // Draw lock icon + right -= 8; + if (serverDetails.RequiresPassword) + { + GfxDrawSprite(dpi, ImageId(SPR_G2_LOCKED), { right, screenCoords.y + 4 }); + } + right -= 6; + + // Draw number of players + screenCoords.x = right - numPlayersStringWidth; + GfxDrawString(dpi, screenCoords + ScreenCoordsXY{ 0, 3 }, players, { colours[1] }); + + screenCoords.y += ITEM_HEIGHT; } - right -= 6; - - // Draw number of players - screenCoords.x = right - numPlayersStringWidth; - GfxDrawString(dpi, screenCoords + ScreenCoordsXY{ 0, 3 }, players, { colours[1] }); - - screenCoords.y += ITEM_HEIGHT; } - } # pragma endregion -private: - void ServerListFetchServersBegin() - { - if (_fetchFuture.valid()) + private: + void ServerListFetchServersBegin() { - // A fetch is already in progress - return; - } - - _serverList.Clear(); - _serverList.ReadAndAddFavourites(); - _statusText = STR_SERVER_LIST_CONNECTING; - - _fetchFuture = std::async(std::launch::async, [this] { - // Spin off background fetches - auto lanF = _serverList.FetchLocalServerListAsync(); - auto wanF = _serverList.FetchOnlineServerListAsync(); - - // Merge or deal with errors - std::vector allEntries; - try - { - auto entries = lanF.get(); - allEntries.reserve(entries.size()); - allEntries.insert(allEntries.end(), entries.begin(), entries.end()); - } - // TODO: Stop catching all exceptions - catch (...) + if (_fetchFuture.valid()) { + // A fetch is already in progress + return; } - auto status = STR_NONE; - try - { - auto entries = wanF.get(); - allEntries.reserve(allEntries.capacity() + entries.size()); - allEntries.insert(allEntries.end(), entries.begin(), entries.end()); - } - catch (const MasterServerException& e) - { - status = e.StatusText; - } - // TODO: Stop catching all exceptions - catch (...) - { - status = STR_SERVER_LIST_NO_CONNECTION; - } - return std::make_tuple(allEntries, status); - }); - } + _serverList.Clear(); + _serverList.ReadAndAddFavourites(); + _statusText = STR_SERVER_LIST_CONNECTING; - void ServerListFetchServersCheck() - { - if (_fetchFuture.valid()) - { - auto status = _fetchFuture.wait_for(std::chrono::seconds::zero()); - if (status == std::future_status::ready) - { + _fetchFuture = std::async(std::launch::async, [this] { + // Spin off background fetches + auto lanF = _serverList.FetchLocalServerListAsync(); + auto wanF = _serverList.FetchOnlineServerListAsync(); + + // Merge or deal with errors + std::vector allEntries; try { - auto [entries, statusText] = _fetchFuture.get(); - _serverList.AddOrUpdateRange(entries); - _serverList.WriteFavourites(); // Update favourites in case favourited server info changes - _numPlayersOnline = _serverList.GetTotalPlayerCount(); - _statusText = STR_X_PLAYERS_ONLINE; - if (statusText != STR_NONE) - { - _statusText = statusText; - } + auto entries = lanF.get(); + allEntries.reserve(entries.size()); + allEntries.insert(allEntries.end(), entries.begin(), entries.end()); + } + // TODO: Stop catching all exceptions + catch (...) + { + } + + auto status = STR_NONE; + try + { + auto entries = wanF.get(); + allEntries.reserve(allEntries.capacity() + entries.size()); + allEntries.insert(allEntries.end(), entries.begin(), entries.end()); } catch (const MasterServerException& e) { - _statusText = e.StatusText; + status = e.StatusText; } - catch (const std::exception& e) + // TODO: Stop catching all exceptions + catch (...) { - _statusText = STR_SERVER_LIST_NO_CONNECTION; - LOG_WARNING("Unable to connect to master server: %s", e.what()); + status = STR_SERVER_LIST_NO_CONNECTION; } - _fetchFuture = {}; - Invalidate(); - } + return std::make_tuple(allEntries, status); + }); } - } - void OnPrepareDraw() override - { - ResizeFrame(); - - int32_t margin = 6; - int32_t buttonHeight = 13; - int32_t buttonTop = height - margin - buttonHeight - 13; - int32_t buttonBottom = buttonTop + buttonHeight; - int32_t listBottom = buttonTop - margin; - - widgets[WIDX_PLAYER_NAME_INPUT].right = width - 6; - widgets[WIDX_LIST].left = 6; - widgets[WIDX_LIST].right = width - 6; - widgets[WIDX_LIST].bottom = listBottom; - widgets[WIDX_FETCH_SERVERS].top = buttonTop; - widgets[WIDX_FETCH_SERVERS].bottom = buttonBottom; - widgets[WIDX_ADD_SERVER].top = buttonTop; - widgets[WIDX_ADD_SERVER].bottom = buttonBottom; - widgets[WIDX_START_SERVER].top = buttonTop; - widgets[WIDX_START_SERVER].bottom = buttonBottom; - - no_list_items = static_cast(_serverList.GetCount()); - } -}; - -WindowBase* WindowServerListOpen() -{ - // Check if window is already open - auto* window = WindowBringToFrontByClass(WindowClass::ServerList); - if (window != nullptr) - return window; - - window = WindowCreate( - WindowClass::ServerList, WWIDTH_MIN, WHEIGHT_MIN, WF_10 | WF_RESIZABLE | WF_CENTRE_SCREEN); - - return window; -} - -void JoinServer(std::string address) -{ - int32_t port = NETWORK_DEFAULT_PORT; - auto endBracketIndex = address.find(']'); - auto colonIndex = address.find_last_of(':'); - if (colonIndex != std::string::npos) - { - if (auto dotIndex = address.find('.'); endBracketIndex != std::string::npos || dotIndex != std::string::npos) + void ServerListFetchServersCheck() { - auto ret = std::sscanf(&address[colonIndex + 1], "%d", &port); - assert(ret); - if (ret > 0) + if (_fetchFuture.valid()) { - address = address.substr(0, colonIndex); + auto status = _fetchFuture.wait_for(std::chrono::seconds::zero()); + if (status == std::future_status::ready) + { + try + { + auto [entries, statusText] = _fetchFuture.get(); + _serverList.AddOrUpdateRange(entries); + _serverList.WriteFavourites(); // Update favourites in case favourited server info changes + _numPlayersOnline = _serverList.GetTotalPlayerCount(); + _statusText = STR_X_PLAYERS_ONLINE; + if (statusText != STR_NONE) + { + _statusText = statusText; + } + } + catch (const MasterServerException& e) + { + _statusText = e.StatusText; + } + catch (const std::exception& e) + { + _statusText = STR_SERVER_LIST_NO_CONNECTION; + LOG_WARNING("Unable to connect to master server: %s", e.what()); + } + _fetchFuture = {}; + Invalidate(); + } } } - } - if (auto beginBracketIndex = address.find('['); - beginBracketIndex != std::string::npos && endBracketIndex != std::string::npos) + void OnPrepareDraw() override + { + ResizeFrame(); + + int32_t margin = 6; + int32_t buttonHeight = 13; + int32_t buttonTop = height - margin - buttonHeight - 13; + int32_t buttonBottom = buttonTop + buttonHeight; + int32_t listBottom = buttonTop - margin; + + widgets[WIDX_PLAYER_NAME_INPUT].right = width - 6; + widgets[WIDX_LIST].left = 6; + widgets[WIDX_LIST].right = width - 6; + widgets[WIDX_LIST].bottom = listBottom; + widgets[WIDX_FETCH_SERVERS].top = buttonTop; + widgets[WIDX_FETCH_SERVERS].bottom = buttonBottom; + widgets[WIDX_ADD_SERVER].top = buttonTop; + widgets[WIDX_ADD_SERVER].bottom = buttonBottom; + widgets[WIDX_START_SERVER].top = buttonTop; + widgets[WIDX_START_SERVER].bottom = buttonBottom; + + no_list_items = static_cast(_serverList.GetCount()); + } + }; + + WindowBase* WindowServerListOpen() { - address = address.substr(beginBracketIndex + 1, endBracketIndex - beginBracketIndex - 1); + // Check if window is already open + auto* window = WindowBringToFrontByClass(WindowClass::ServerList); + if (window != nullptr) + return window; + + window = WindowCreate( + WindowClass::ServerList, WWIDTH_MIN, WHEIGHT_MIN, WF_10 | WF_RESIZABLE | WF_CENTRE_SCREEN); + + return window; } - if (!NetworkBeginClient(address, port)) + void JoinServer(std::string address) { - ContextShowError(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_NONE, {}); - } -} + int32_t port = NETWORK_DEFAULT_PORT; + auto endBracketIndex = address.find(']'); + auto colonIndex = address.find_last_of(':'); + if (colonIndex != std::string::npos) + { + if (auto dotIndex = address.find('.'); endBracketIndex != std::string::npos || dotIndex != std::string::npos) + { + auto ret = std::sscanf(&address[colonIndex + 1], "%d", &port); + assert(ret); + if (ret > 0) + { + address = address.substr(0, colonIndex); + } + } + } + if (auto beginBracketIndex = address.find('['); + beginBracketIndex != std::string::npos && endBracketIndex != std::string::npos) + { + address = address.substr(beginBracketIndex + 1, endBracketIndex - beginBracketIndex - 1); + } + + if (!NetworkBeginClient(address, port)) + { + ContextShowError(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_NONE, {}); + } + } +} // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/windows/ServerStart.cpp b/src/openrct2-ui/windows/ServerStart.cpp index c8bddae320..619bff5db9 100644 --- a/src/openrct2-ui/windows/ServerStart.cpp +++ b/src/openrct2-ui/windows/ServerStart.cpp @@ -22,9 +22,9 @@ # include # include -using namespace OpenRCT2; - -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -60,226 +60,231 @@ static Widget _windowServerStartWidgets[] = { MakeWidget({ 112, WH - 6 - 13 }, { 101, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_LOAD_GAME), // None kWidgetsEnd, }; -// clang-format on -class ServerStartWindow final : public Window -{ -public: - void OnOpen() override + // clang-format on + class ServerStartWindow final : public Window { - widgets = _windowServerStartWidgets; - widgets[WIDX_PORT_INPUT].string = _port; - widgets[WIDX_NAME_INPUT].string = _name; - widgets[WIDX_DESCRIPTION_INPUT].string = _description; - widgets[WIDX_GREETING_INPUT].string = _greeting; - widgets[WIDX_PASSWORD_INPUT].string = _password; - InitScrollWidgets(); - frame_no = 0; - min_width = width; - min_height = height; - max_width = min_width; - max_height = min_height; - - page = 0; - list_information_type = 0; - - snprintf(_port, 7, "%u", gConfigNetwork.DefaultPort); - SafeStrCpy(_name, gConfigNetwork.ServerName.c_str(), sizeof(_name)); - SafeStrCpy(_description, gConfigNetwork.ServerDescription.c_str(), sizeof(_description)); - SafeStrCpy(_greeting, gConfigNetwork.ServerGreeting.c_str(), sizeof(_greeting)); - } - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_PORT_INPUT: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _port, 6); - break; - case WIDX_NAME_INPUT: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _name, 64); - break; - case WIDX_DESCRIPTION_INPUT: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _description, MAX_SERVER_DESCRIPTION_LENGTH); - break; - case WIDX_GREETING_INPUT: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _greeting, CHAT_INPUT_SIZE); - break; - case WIDX_PASSWORD_INPUT: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _password, 32); - break; - case WIDX_MAXPLAYERS_INCREASE: - if (gConfigNetwork.Maxplayers < 255) - { - gConfigNetwork.Maxplayers++; - } - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_MAXPLAYERS_DECREASE: - if (gConfigNetwork.Maxplayers > 1) - { - gConfigNetwork.Maxplayers--; - } - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_ADVERTISE_CHECKBOX: - gConfigNetwork.Advertise = !gConfigNetwork.Advertise; - ConfigSaveDefault(); - Invalidate(); - break; - case WIDX_START_SERVER: - NetworkSetPassword(_password); - WindowScenarioselectOpen(ScenarioSelectCallback); - break; - case WIDX_LOAD_SERVER: - NetworkSetPassword(_password); - auto intent = Intent(WindowClass::Loadsave); - intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME); - intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(LoadSaveCallback)); - ContextOpenIntent(&intent); - break; + widgets = _windowServerStartWidgets; + widgets[WIDX_PORT_INPUT].string = _port; + widgets[WIDX_NAME_INPUT].string = _name; + widgets[WIDX_DESCRIPTION_INPUT].string = _description; + widgets[WIDX_GREETING_INPUT].string = _greeting; + widgets[WIDX_PASSWORD_INPUT].string = _password; + InitScrollWidgets(); + frame_no = 0; + min_width = width; + min_height = height; + max_width = min_width; + max_height = min_height; + + page = 0; + list_information_type = 0; + + snprintf(_port, 7, "%u", gConfigNetwork.DefaultPort); + SafeStrCpy(_name, gConfigNetwork.ServerName.c_str(), sizeof(_name)); + SafeStrCpy(_description, gConfigNetwork.ServerDescription.c_str(), sizeof(_description)); + SafeStrCpy(_greeting, gConfigNetwork.ServerGreeting.c_str(), sizeof(_greeting)); } - } - void OnPrepareDraw() override - { - ColourSchemeUpdateByClass(this, WindowClass::ServerList); - - WidgetSetCheckboxValue(*this, WIDX_ADVERTISE_CHECKBOX, gConfigNetwork.Advertise); - auto ft = Formatter::Common(); - ft.Increment(18); - ft.Add(gConfigNetwork.Maxplayers); - } - void OnUpdate() override - { - if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + void OnMouseUp(WidgetIndex widgetIndex) override { - WindowUpdateTextboxCaret(); - WidgetInvalidate(*this, WIDX_NAME_INPUT); - WidgetInvalidate(*this, WIDX_DESCRIPTION_INPUT); - WidgetInvalidate(*this, WIDX_GREETING_INPUT); - WidgetInvalidate(*this, WIDX_PASSWORD_INPUT); + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_PORT_INPUT: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _port, 6); + break; + case WIDX_NAME_INPUT: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _name, 64); + break; + case WIDX_DESCRIPTION_INPUT: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _description, MAX_SERVER_DESCRIPTION_LENGTH); + break; + case WIDX_GREETING_INPUT: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _greeting, CHAT_INPUT_SIZE); + break; + case WIDX_PASSWORD_INPUT: + WindowStartTextbox(*this, widgetIndex, STR_STRING, _password, 32); + break; + case WIDX_MAXPLAYERS_INCREASE: + if (gConfigNetwork.Maxplayers < 255) + { + gConfigNetwork.Maxplayers++; + } + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_MAXPLAYERS_DECREASE: + if (gConfigNetwork.Maxplayers > 1) + { + gConfigNetwork.Maxplayers--; + } + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_ADVERTISE_CHECKBOX: + gConfigNetwork.Advertise = !gConfigNetwork.Advertise; + ConfigSaveDefault(); + Invalidate(); + break; + case WIDX_START_SERVER: + NetworkSetPassword(_password); + WindowScenarioselectOpen(ScenarioSelectCallback); + break; + case WIDX_LOAD_SERVER: + NetworkSetPassword(_password); + auto intent = Intent(WindowClass::Loadsave); + intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME); + intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(LoadSaveCallback)); + ContextOpenIntent(&intent); + break; + } } - } - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - std::string temp = static_cast(text); - int tempPort = 0; - - switch (widgetIndex) + void OnPrepareDraw() override { - case WIDX_PORT_INPUT: - if (strcmp(_port, temp.c_str()) == 0) - return; - - SafeStrCpy(_port, temp.c_str(), sizeof(_port)); - - // Don't allow negative/zero for port number - tempPort = atoi(_port); - if (tempPort > 0) - { - gConfigNetwork.DefaultPort = tempPort; - ConfigSaveDefault(); - } - - WidgetInvalidate(*this, WIDX_PORT_INPUT); - break; - case WIDX_NAME_INPUT: - if (strcmp(_name, temp.c_str()) == 0) - return; - - SafeStrCpy(_name, temp.c_str(), sizeof(_name)); - - // Don't allow empty server names - if (_name[0] != '\0') - { - gConfigNetwork.ServerName = _name; - ConfigSaveDefault(); - } + ColourSchemeUpdateByClass(this, WindowClass::ServerList); + WidgetSetCheckboxValue(*this, WIDX_ADVERTISE_CHECKBOX, gConfigNetwork.Advertise); + auto ft = Formatter::Common(); + ft.Increment(18); + ft.Add(gConfigNetwork.Maxplayers); + } + void OnUpdate() override + { + if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + { + WindowUpdateTextboxCaret(); WidgetInvalidate(*this, WIDX_NAME_INPUT); - break; - case WIDX_DESCRIPTION_INPUT: - if (strcmp(_description, temp.c_str()) == 0) - return; - - SafeStrCpy(_description, temp.c_str(), sizeof(_description)); - gConfigNetwork.ServerDescription = _description; - ConfigSaveDefault(); - WidgetInvalidate(*this, WIDX_DESCRIPTION_INPUT); - break; - case WIDX_GREETING_INPUT: - if (strcmp(_greeting, temp.c_str()) == 0) - return; - - SafeStrCpy(_greeting, temp.c_str(), sizeof(_greeting)); - gConfigNetwork.ServerGreeting = _greeting; - ConfigSaveDefault(); - WidgetInvalidate(*this, WIDX_GREETING_INPUT); - break; - case WIDX_PASSWORD_INPUT: - if (strcmp(_password, temp.c_str()) == 0) - return; - - SafeStrCpy(_password, temp.c_str(), sizeof(_password)); - WidgetInvalidate(*this, WIDX_PASSWORD_INPUT); - break; + } } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PORT_INPUT].top }, STR_PORT, {}, { colours[1] }); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_NAME_INPUT].top }, STR_SERVER_NAME, {}, { colours[1] }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_DESCRIPTION_INPUT].top }, STR_SERVER_DESCRIPTION, {}, - { colours[1] }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_GREETING_INPUT].top }, STR_SERVER_GREETING, {}, { colours[1] }); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PASSWORD_INPUT].top }, STR_PASSWORD, {}, { colours[1] }); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_MAXPLAYERS].top }, STR_MAX_PLAYERS, {}, { colours[1] }); - } - - void OnResize() override - { - ResizeFrame(); - } - -private: - char _port[7]; - char _name[65]; - char _description[MAX_SERVER_DESCRIPTION_LENGTH]; - char _greeting[CHAT_INPUT_SIZE]; - char _password[33]; - static void ScenarioSelectCallback(const utf8* path) - { - GameNotifyMapChange(); - if (GetContext()->LoadParkFromFile(path, false, true)) + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { - NetworkBeginServer(gConfigNetwork.DefaultPort, gConfigNetwork.ListenAddress); - } - } + std::string temp = static_cast(text); + int tempPort = 0; - static void LoadSaveCallback(int32_t result, const utf8* path) - { - if (result == MODAL_RESULT_OK) + switch (widgetIndex) + { + case WIDX_PORT_INPUT: + if (strcmp(_port, temp.c_str()) == 0) + return; + + SafeStrCpy(_port, temp.c_str(), sizeof(_port)); + + // Don't allow negative/zero for port number + tempPort = atoi(_port); + if (tempPort > 0) + { + gConfigNetwork.DefaultPort = tempPort; + ConfigSaveDefault(); + } + + WidgetInvalidate(*this, WIDX_PORT_INPUT); + break; + case WIDX_NAME_INPUT: + if (strcmp(_name, temp.c_str()) == 0) + return; + + SafeStrCpy(_name, temp.c_str(), sizeof(_name)); + + // Don't allow empty server names + if (_name[0] != '\0') + { + gConfigNetwork.ServerName = _name; + ConfigSaveDefault(); + } + + WidgetInvalidate(*this, WIDX_NAME_INPUT); + break; + case WIDX_DESCRIPTION_INPUT: + if (strcmp(_description, temp.c_str()) == 0) + return; + + SafeStrCpy(_description, temp.c_str(), sizeof(_description)); + gConfigNetwork.ServerDescription = _description; + ConfigSaveDefault(); + + WidgetInvalidate(*this, WIDX_DESCRIPTION_INPUT); + break; + case WIDX_GREETING_INPUT: + if (strcmp(_greeting, temp.c_str()) == 0) + return; + + SafeStrCpy(_greeting, temp.c_str(), sizeof(_greeting)); + gConfigNetwork.ServerGreeting = _greeting; + ConfigSaveDefault(); + + WidgetInvalidate(*this, WIDX_GREETING_INPUT); + break; + case WIDX_PASSWORD_INPUT: + if (strcmp(_password, temp.c_str()) == 0) + return; + + SafeStrCpy(_password, temp.c_str(), sizeof(_password)); + + WidgetInvalidate(*this, WIDX_PASSWORD_INPUT); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PORT_INPUT].top }, STR_PORT, {}, { colours[1] }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_NAME_INPUT].top }, STR_SERVER_NAME, {}, { colours[1] }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_DESCRIPTION_INPUT].top }, STR_SERVER_DESCRIPTION, {}, + { colours[1] }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_GREETING_INPUT].top }, STR_SERVER_GREETING, {}, + { colours[1] }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_PASSWORD_INPUT].top }, STR_PASSWORD, {}, { colours[1] }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_MAXPLAYERS].top }, STR_MAX_PLAYERS, {}, { colours[1] }); + } + + void OnResize() override + { + ResizeFrame(); + } + + private: + char _port[7]; + char _name[65]; + char _description[MAX_SERVER_DESCRIPTION_LENGTH]; + char _greeting[CHAT_INPUT_SIZE]; + char _password[33]; + static void ScenarioSelectCallback(const utf8* path) { GameNotifyMapChange(); - GetContext()->LoadParkFromFile(path); - NetworkBeginServer(gConfigNetwork.DefaultPort, gConfigNetwork.ListenAddress); + if (GetContext()->LoadParkFromFile(path, false, true)) + { + NetworkBeginServer(gConfigNetwork.DefaultPort, gConfigNetwork.ListenAddress); + } } - } -}; -WindowBase* WindowServerStartOpen() -{ - return WindowFocusOrCreate(WindowClass::ServerStart, WW, WH, WF_CENTRE_SCREEN); -} + static void LoadSaveCallback(int32_t result, const utf8* path) + { + if (result == MODAL_RESULT_OK) + { + GameNotifyMapChange(); + GetContext()->LoadParkFromFile(path); + NetworkBeginServer(gConfigNetwork.DefaultPort, gConfigNetwork.ListenAddress); + } + } + }; + + WindowBase* WindowServerStartOpen() + { + return WindowFocusOrCreate(WindowClass::ServerStart, WW, WH, WF_CENTRE_SCREEN); + } +} // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/windows/ShortcutKeys.cpp b/src/openrct2-ui/windows/ShortcutKeys.cpp index 9d9360c094..99a3a8b781 100644 --- a/src/openrct2-ui/windows/ShortcutKeys.cpp +++ b/src/openrct2-ui/windows/ShortcutKeys.cpp @@ -16,30 +16,29 @@ #include #include -using namespace OpenRCT2; -using namespace OpenRCT2::Ui; - -WindowBase* ResetShortcutKeysPromptOpen(); - -static constexpr StringId WINDOW_TITLE = STR_SHORTCUTS_TITLE; -static constexpr int32_t WW = 420; -static constexpr int32_t WH = 280; - -static constexpr int32_t WW_SC_MAX = 1200; -static constexpr int32_t WH_SC_MAX = 800; - -enum WindowShortcutWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_TAB_CONTENT_PANEL, - WIDX_SCROLL, - WIDX_RESET, - WIDX_TAB_0, -}; + WindowBase* ResetShortcutKeysPromptOpen(); -// clang-format off + static constexpr StringId WINDOW_TITLE = STR_SHORTCUTS_TITLE; + static constexpr int32_t WW = 420; + static constexpr int32_t WH = 280; + + static constexpr int32_t WW_SC_MAX = 1200; + static constexpr int32_t WH_SC_MAX = 800; + + enum WindowShortcutWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_TAB_CONTENT_PANEL, + WIDX_SCROLL, + WIDX_RESET, + WIDX_TAB_0, + }; + + // clang-format off static Widget _shortcutWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget({0, 43}, {350, 287}, WindowWidgetType::Resize, WindowColour::Secondary), @@ -47,578 +46,581 @@ static Widget _shortcutWidgets[] = { MakeWidget({4, WH-15}, {150, 12}, WindowWidgetType::Button, WindowColour::Primary, STR_SHORTCUT_ACTION_RESET, STR_SHORTCUT_ACTION_RESET_TIP), kWidgetsEnd, }; -// clang-format on + // clang-format on -static constexpr StringId CHANGE_WINDOW_TITLE = STR_SHORTCUT_CHANGE_TITLE; -static constexpr int32_t CHANGE_WW = 250; -static constexpr int32_t CHANGE_WH = 80; + static constexpr StringId CHANGE_WINDOW_TITLE = STR_SHORTCUT_CHANGE_TITLE; + static constexpr int32_t CHANGE_WW = 250; + static constexpr int32_t CHANGE_WH = 80; -enum -{ - WIDX_REMOVE = 3 -}; + enum + { + WIDX_REMOVE = 3 + }; -// clang-format off + // clang-format off static Widget window_shortcut_change_widgets[] = { WINDOW_SHIM(CHANGE_WINDOW_TITLE, CHANGE_WW, CHANGE_WH), MakeWidget({ 75, 56 }, { 100, 14 }, WindowWidgetType::Button, WindowColour::Primary, STR_SHORTCUT_REMOVE, STR_SHORTCUT_REMOVE_TIP), kWidgetsEnd, }; -// clang-format on + // clang-format on -class ChangeShortcutWindow final : public Window -{ -private: - std::string _shortcutId; - StringId _shortcutLocalisedName{}; - std::string _shortcutCustomName; - -public: - static ChangeShortcutWindow* Open(std::string_view shortcutId) + class ChangeShortcutWindow final : public Window { - auto& shortcutManager = GetShortcutManager(); - auto registeredShortcut = shortcutManager.GetShortcut(shortcutId); - if (registeredShortcut != nullptr) + private: + std::string _shortcutId; + StringId _shortcutLocalisedName{}; + std::string _shortcutCustomName; + + public: + static ChangeShortcutWindow* Open(std::string_view shortcutId) { - WindowCloseByClass(WindowClass::ChangeKeyboardShortcut); - auto w = WindowCreate( - WindowClass::ChangeKeyboardShortcut, CHANGE_WW, CHANGE_WH, WF_CENTRE_SCREEN); - if (w != nullptr) + auto& shortcutManager = GetShortcutManager(); + auto registeredShortcut = shortcutManager.GetShortcut(shortcutId); + if (registeredShortcut != nullptr) { - w->_shortcutId = shortcutId; - w->_shortcutLocalisedName = registeredShortcut->LocalisedName; - w->_shortcutCustomName = registeredShortcut->CustomName; - shortcutManager.SetPendingShortcutChange(registeredShortcut->Id); - return w; - } - } - return nullptr; - } - - void OnOpen() override - { - widgets = window_shortcut_change_widgets; - WindowInitScrollWidgets(*this); - } - - void OnClose() override - { - auto& shortcutManager = GetShortcutManager(); - shortcutManager.SetPendingShortcutChange({}); - NotifyShortcutKeysWindow(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_REMOVE: - Remove(); - break; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - ScreenCoordsXY stringCoords(windowPos.x + 125, windowPos.y + 30); - - auto ft = Formatter(); - if (_shortcutCustomName.empty()) - { - ft.Add(_shortcutLocalisedName); - } - else - { - ft.Add(STR_STRING); - ft.Add(_shortcutCustomName.c_str()); - } - DrawTextWrapped(dpi, stringCoords, 242, STR_SHORTCUT_CHANGE_PROMPT, ft, { TextAlignment::CENTRE }); - } - -private: - void NotifyShortcutKeysWindow(); - - void Remove() - { - auto& shortcutManager = GetShortcutManager(); - auto* shortcut = shortcutManager.GetShortcut(_shortcutId); - if (shortcut != nullptr) - { - shortcut->Current.clear(); - shortcutManager.SaveUserBindings(); - } - Close(); - } -}; - -class ShortcutKeysWindow final : public Window -{ -private: - struct ShortcutStringPair - { - std::string ShortcutId; - ::StringId StringId = STR_NONE; - std::string CustomString; - std::string Binding; - }; - - struct ShortcutTabDesc - { - std::string_view IdGroup; - uint32_t ImageId; - uint32_t ImageDivisor; - uint32_t ImageNumFrames; - }; - - std::vector _tabs; - std::vector _widgets; - std::vector _list; - int_fast16_t _highlightedItem; - size_t _currentTabIndex{}; - uint32_t _tabAnimationIndex{}; - -public: - void OnOpen() override - { - InitialiseTabs(); - InitialiseWidgets(); - InitialiseList(); - - min_width = WW; - min_height = WH; - max_width = WW_SC_MAX; - max_height = WH_SC_MAX; - } - - void OnClose() override - { - WindowCloseByClass(WindowClass::ResetShortcutKeysPrompt); - } - - void OnResize() override - { - WindowSetResize(*this, min_width, min_height, max_width, max_height); - } - - void OnUpdate() override - { - // Remove highlight when the mouse is not hovering over the list - if (_highlightedItem != -1 && !WidgetIsHighlighted(*this, WIDX_SCROLL)) - { - _highlightedItem = -1; - InvalidateWidget(WIDX_SCROLL); - } - - _tabAnimationIndex++; - InvalidateWidget(static_cast(WIDX_TAB_0 + _currentTabIndex)); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_RESET: - ResetShortcutKeysPromptOpen(); - break; - default: - { - auto tabIndex = static_cast(widgetIndex - WIDX_TAB_0); - if (tabIndex < _tabs.size()) + WindowCloseByClass(WindowClass::ChangeKeyboardShortcut); + auto w = WindowCreate( + WindowClass::ChangeKeyboardShortcut, CHANGE_WW, CHANGE_WH, WF_CENTRE_SCREEN); + if (w != nullptr) { - SetTab(tabIndex); + w->_shortcutId = shortcutId; + w->_shortcutLocalisedName = registeredShortcut->LocalisedName; + w->_shortcutCustomName = registeredShortcut->CustomName; + shortcutManager.SetPendingShortcutChange(registeredShortcut->Id); + return w; } } + return nullptr; } - } - void OnPrepareDraw() override - { - ResizeFrameWithPage(); - widgets[WIDX_SCROLL].right = width - 5; - widgets[WIDX_SCROLL].bottom = height - 19; - widgets[WIDX_RESET].top = height - 16; - widgets[WIDX_RESET].bottom = height - 5; - WindowAlignTabs(this, WIDX_TAB_0, static_cast(WIDX_TAB_0 + _tabs.size())); - - // Set selected tab - for (size_t i = 0; i < _tabs.size(); i++) + void OnOpen() override { - SetWidgetPressed(static_cast(WIDX_TAB_0 + i), false); + widgets = window_shortcut_change_widgets; + WindowInitScrollWidgets(*this); } - SetWidgetPressed(static_cast(WIDX_TAB_0 + _currentTabIndex), true); - } - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - auto h = static_cast(_list.size() * SCROLLABLE_ROW_HEIGHT); - auto bottom = std::max(0, h - widgets[WIDX_SCROLL].bottom + widgets[WIDX_SCROLL].top + 21); - if (bottom < scrolls[0].v_top) + void OnClose() override { - scrolls[0].v_top = bottom; - Invalidate(); + auto& shortcutManager = GetShortcutManager(); + shortcutManager.SetPendingShortcutChange({}); + NotifyShortcutKeysWindow(); } - return { 0, h }; - } - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto index = static_cast((screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT); - if (static_cast(index) < _list.size()) + void OnMouseUp(WidgetIndex widgetIndex) override { - _highlightedItem = index; - Invalidate(); - } - else - { - _highlightedItem = -1; - } - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto selectedItem = static_cast((screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT); - if (selectedItem < _list.size()) - { - // Is this a separator? - if (!_list[selectedItem].ShortcutId.empty()) + switch (widgetIndex) { - auto& shortcut = _list[selectedItem]; - ChangeShortcutWindow::Open(shortcut.ShortcutId); + case WIDX_CLOSE: + Close(); + break; + case WIDX_REMOVE: + Remove(); + break; } } - } - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; - GfxFillRect( - dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - - // TODO: the line below is a workaround for what is presumably a bug with dpi->width - // see https://github.com/OpenRCT2/OpenRCT2/issues/11238 for details - const auto scrollWidth = width - kScrollBarWidth - 10; - - for (size_t i = 0; i < _list.size(); ++i) + void OnDraw(DrawPixelInfo& dpi) override { - auto y = static_cast(1 + i * SCROLLABLE_ROW_HEIGHT); - if (y > dpi.y + dpi.height) - { - break; - } + DrawWidgets(dpi); - if (y + SCROLLABLE_ROW_HEIGHT < dpi.y) - { - continue; - } + ScreenCoordsXY stringCoords(windowPos.x + 125, windowPos.y + 30); - // Is this a separator? - if (_list[i].ShortcutId.empty()) + auto ft = Formatter(); + if (_shortcutCustomName.empty()) { - DrawSeparator(dpi, y, scrollWidth); + ft.Add(_shortcutLocalisedName); } else { - auto isHighlighted = _highlightedItem == static_cast(i); - DrawItem(dpi, y, scrollWidth, _list[i], isHighlighted); + ft.Add(STR_STRING); + ft.Add(_shortcutCustomName.c_str()); } + DrawTextWrapped(dpi, stringCoords, 242, STR_SHORTCUT_CHANGE_PROMPT, ft, { TextAlignment::CENTRE }); } - } - void RefreshBindings() - { - InitialiseList(); - } + private: + void NotifyShortcutKeysWindow(); - void ResetAllOnActiveTab() - { - auto& shortcutManager = GetShortcutManager(); - for (const auto& item : _list) + void Remove() { - auto shortcut = shortcutManager.GetShortcut(item.ShortcutId); + auto& shortcutManager = GetShortcutManager(); + auto* shortcut = shortcutManager.GetShortcut(_shortcutId); if (shortcut != nullptr) { - shortcut->Current = shortcut->Default; + shortcut->Current.clear(); + shortcutManager.SaveUserBindings(); } + Close(); } - shortcutManager.SaveUserBindings(); - RefreshBindings(); - } + }; -private: - bool IsInCurrentTab(const RegisteredShortcut& shortcut) + class ShortcutKeysWindow final : public Window { - auto groupFilter = _tabs[_currentTabIndex].IdGroup; - auto group = shortcut.GetTopLevelGroup(); - if (groupFilter.empty()) + private: + struct ShortcutStringPair { - // Check it doesn't belong in any other tab - for (const auto& tab : _tabs) + std::string ShortcutId; + ::StringId StringId = STR_NONE; + std::string CustomString; + std::string Binding; + }; + + struct ShortcutTabDesc + { + std::string_view IdGroup; + uint32_t ImageId; + uint32_t ImageDivisor; + uint32_t ImageNumFrames; + }; + + std::vector _tabs; + std::vector _widgets; + std::vector _list; + int_fast16_t _highlightedItem; + size_t _currentTabIndex{}; + uint32_t _tabAnimationIndex{}; + + public: + void OnOpen() override + { + InitialiseTabs(); + InitialiseWidgets(); + InitialiseList(); + + min_width = WW; + min_height = WH; + max_width = WW_SC_MAX; + max_height = WH_SC_MAX; + } + + void OnClose() override + { + WindowCloseByClass(WindowClass::ResetShortcutKeysPrompt); + } + + void OnResize() override + { + WindowSetResize(*this, min_width, min_height, max_width, max_height); + } + + void OnUpdate() override + { + // Remove highlight when the mouse is not hovering over the list + if (_highlightedItem != -1 && !WidgetIsHighlighted(*this, WIDX_SCROLL)) { - if (!tab.IdGroup.empty()) + _highlightedItem = -1; + InvalidateWidget(WIDX_SCROLL); + } + + _tabAnimationIndex++; + InvalidateWidget(static_cast(WIDX_TAB_0 + _currentTabIndex)); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_RESET: + ResetShortcutKeysPromptOpen(); + break; + default: { - if (tab.IdGroup == group) + auto tabIndex = static_cast(widgetIndex - WIDX_TAB_0); + if (tabIndex < _tabs.size()) { - return false; + SetTab(tabIndex); } } } - return true; } - return group == groupFilter; - } - - void InitialiseList() - { - // Get shortcuts and sort by group - auto shortcuts = GetShortcutsForCurrentTab(); - std::stable_sort(shortcuts.begin(), shortcuts.end(), [](const RegisteredShortcut* a, const RegisteredShortcut* b) { - return a->OrderIndex < b->OrderIndex; - }); - - // Create list items with a separator between each group - _list.clear(); - std::string group; - for (const auto* shortcut : shortcuts) + void OnPrepareDraw() override { - if (group.empty()) + ResizeFrameWithPage(); + widgets[WIDX_SCROLL].right = width - 5; + widgets[WIDX_SCROLL].bottom = height - 19; + widgets[WIDX_RESET].top = height - 16; + widgets[WIDX_RESET].bottom = height - 5; + WindowAlignTabs(this, WIDX_TAB_0, static_cast(WIDX_TAB_0 + _tabs.size())); + + // Set selected tab + for (size_t i = 0; i < _tabs.size(); i++) { - group = shortcut->GetGroup(); + SetWidgetPressed(static_cast(WIDX_TAB_0 + i), false); + } + SetWidgetPressed(static_cast(WIDX_TAB_0 + _currentTabIndex), true); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(dpi); + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + auto h = static_cast(_list.size() * SCROLLABLE_ROW_HEIGHT); + auto bottom = std::max(0, h - widgets[WIDX_SCROLL].bottom + widgets[WIDX_SCROLL].top + 21); + if (bottom < scrolls[0].v_top) + { + scrolls[0].v_top = bottom; + Invalidate(); + } + return { 0, h }; + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + auto index = static_cast((screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT); + if (static_cast(index) < _list.size()) + { + _highlightedItem = index; + Invalidate(); } else { - auto groupName = shortcut->GetGroup(); - if (group != groupName) + _highlightedItem = -1; + } + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + auto selectedItem = static_cast((screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT); + if (selectedItem < _list.size()) + { + // Is this a separator? + if (!_list[selectedItem].ShortcutId.empty()) { - // Add separator - group = groupName; - _list.emplace_back(); + auto& shortcut = _list[selectedItem]; + ChangeShortcutWindow::Open(shortcut.ShortcutId); } } - - ShortcutStringPair ssp; - ssp.ShortcutId = shortcut->Id; - ssp.StringId = shortcut->LocalisedName; - ssp.CustomString = shortcut->CustomName; - ssp.Binding = shortcut->GetDisplayString(); - _list.push_back(std::move(ssp)); } - Invalidate(); - } - - std::vector GetShortcutsForCurrentTab() - { - std::vector result; - auto& shortcutManager = GetShortcutManager(); - for (const auto& shortcut : shortcutManager.Shortcuts) + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override { - if (IsInCurrentTab(shortcut.second)) + auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; + GfxFillRect( + dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, + ColourMapA[colours[1]].mid_light); + + // TODO: the line below is a workaround for what is presumably a bug with dpi->width + // see https://github.com/OpenRCT2/OpenRCT2/issues/11238 for details + const auto scrollWidth = width - kScrollBarWidth - 10; + + for (size_t i = 0; i < _list.size(); ++i) { - result.push_back(&shortcut.second); + auto y = static_cast(1 + i * SCROLLABLE_ROW_HEIGHT); + if (y > dpi.y + dpi.height) + { + break; + } + + if (y + SCROLLABLE_ROW_HEIGHT < dpi.y) + { + continue; + } + + // Is this a separator? + if (_list[i].ShortcutId.empty()) + { + DrawSeparator(dpi, y, scrollWidth); + } + else + { + auto isHighlighted = _highlightedItem == static_cast(i); + DrawItem(dpi, y, scrollWidth, _list[i], isHighlighted); + } } } - return result; - } - void InitialiseTabs() - { - _tabs.clear(); - _tabs.push_back({ "interface", SPR_TAB_GEARS_0, 2, 4 }); - _tabs.push_back({ "view", SPR_G2_VIEW, 0, 0 }); - _tabs.push_back({ "window", SPR_TAB_PARK_ENTRANCE, 0, 0 }); - _tabs.push_back({ {}, SPR_TAB_WRENCH_0, 2, 16 }); - } - - void InitialiseWidgets() - { - _widgets.clear(); - _widgets.insert(_widgets.begin(), std::begin(_shortcutWidgets), std::end(_shortcutWidgets) - 1); - - int32_t x = 3; - for (size_t i = 0; i < _tabs.size(); i++) + void RefreshBindings() { - auto tab = MakeTab({ x, 17 }, STR_NONE); - _widgets.push_back(tab); - x += 31; - } - - _widgets.push_back(kWidgetsEnd); - widgets = _widgets.data(); - - WindowInitScrollWidgets(*this); - } - - void SetTab(size_t index) - { - if (_currentTabIndex != index) - { - _currentTabIndex = index; - _tabAnimationIndex = 0; InitialiseList(); } - } - void DrawTabImages(DrawPixelInfo& dpi) const - { - for (size_t i = 0; i < _tabs.size(); i++) + void ResetAllOnActiveTab() { - DrawTabImage(dpi, i); - } - } - - void DrawTabImage(DrawPixelInfo& dpi, size_t tabIndex) const - { - const auto& tabDesc = _tabs[tabIndex]; - auto widgetIndex = static_cast(WIDX_TAB_0 + tabIndex); - if (!IsWidgetDisabled(widgetIndex)) - { - auto imageId = tabDesc.ImageId; - if (imageId != 0) + auto& shortcutManager = GetShortcutManager(); + for (const auto& item : _list) { - if (tabIndex == _currentTabIndex && tabDesc.ImageDivisor != 0 && tabDesc.ImageNumFrames != 0) + auto shortcut = shortcutManager.GetShortcut(item.ShortcutId); + if (shortcut != nullptr) { - auto frame = _tabAnimationIndex / tabDesc.ImageDivisor; - imageId += frame % tabDesc.ImageNumFrames; + shortcut->Current = shortcut->Default; + } + } + shortcutManager.SaveUserBindings(); + RefreshBindings(); + } + + private: + bool IsInCurrentTab(const RegisteredShortcut& shortcut) + { + auto groupFilter = _tabs[_currentTabIndex].IdGroup; + auto group = shortcut.GetTopLevelGroup(); + if (groupFilter.empty()) + { + // Check it doesn't belong in any other tab + for (const auto& tab : _tabs) + { + if (!tab.IdGroup.empty()) + { + if (tab.IdGroup == group) + { + return false; + } + } + } + return true; + } + + return group == groupFilter; + } + + void InitialiseList() + { + // Get shortcuts and sort by group + auto shortcuts = GetShortcutsForCurrentTab(); + std::stable_sort(shortcuts.begin(), shortcuts.end(), [](const RegisteredShortcut* a, const RegisteredShortcut* b) { + return a->OrderIndex < b->OrderIndex; + }); + + // Create list items with a separator between each group + _list.clear(); + std::string group; + for (const auto* shortcut : shortcuts) + { + if (group.empty()) + { + group = shortcut->GetGroup(); + } + else + { + auto groupName = shortcut->GetGroup(); + if (group != groupName) + { + // Add separator + group = groupName; + _list.emplace_back(); + } } - const auto& widget = widgets[widgetIndex]; - GfxDrawSprite(dpi, ImageId(imageId), windowPos + ScreenCoordsXY{ widget.left, widget.top }); + ShortcutStringPair ssp; + ssp.ShortcutId = shortcut->Id; + ssp.StringId = shortcut->LocalisedName; + ssp.CustomString = shortcut->CustomName; + ssp.Binding = shortcut->GetDisplayString(); + _list.push_back(std::move(ssp)); + } + + Invalidate(); + } + + std::vector GetShortcutsForCurrentTab() + { + std::vector result; + auto& shortcutManager = GetShortcutManager(); + for (const auto& shortcut : shortcutManager.Shortcuts) + { + if (IsInCurrentTab(shortcut.second)) + { + result.push_back(&shortcut.second); + } + } + return result; + } + + void InitialiseTabs() + { + _tabs.clear(); + _tabs.push_back({ "interface", SPR_TAB_GEARS_0, 2, 4 }); + _tabs.push_back({ "view", SPR_G2_VIEW, 0, 0 }); + _tabs.push_back({ "window", SPR_TAB_PARK_ENTRANCE, 0, 0 }); + _tabs.push_back({ {}, SPR_TAB_WRENCH_0, 2, 16 }); + } + + void InitialiseWidgets() + { + _widgets.clear(); + _widgets.insert(_widgets.begin(), std::begin(_shortcutWidgets), std::end(_shortcutWidgets) - 1); + + int32_t x = 3; + for (size_t i = 0; i < _tabs.size(); i++) + { + auto tab = MakeTab({ x, 17 }, STR_NONE); + _widgets.push_back(tab); + x += 31; + } + + _widgets.push_back(kWidgetsEnd); + widgets = _widgets.data(); + + WindowInitScrollWidgets(*this); + } + + void SetTab(size_t index) + { + if (_currentTabIndex != index) + { + _currentTabIndex = index; + _tabAnimationIndex = 0; + InitialiseList(); } } - } - void DrawSeparator(DrawPixelInfo& dpi, int32_t y, int32_t scrollWidth) - { - const int32_t top = y + (SCROLLABLE_ROW_HEIGHT / 2) - 1; - GfxFillRect(dpi, { { 0, top }, { scrollWidth, top } }, ColourMapA[colours[0]].mid_dark); - GfxFillRect(dpi, { { 0, top + 1 }, { scrollWidth, top + 1 } }, ColourMapA[colours[0]].lightest); - } - - void DrawItem(DrawPixelInfo& dpi, int32_t y, int32_t scrollWidth, const ShortcutStringPair& shortcut, bool isHighlighted) - { - auto format = STR_BLACK_STRING; - if (isHighlighted) + void DrawTabImages(DrawPixelInfo& dpi) const { - format = STR_WINDOW_COLOUR_2_STRINGID; - GfxFilterRect(dpi, { 0, y - 1, scrollWidth, y + (SCROLLABLE_ROW_HEIGHT - 2) }, FilterPaletteID::PaletteDarken1); + for (size_t i = 0; i < _tabs.size(); i++) + { + DrawTabImage(dpi, i); + } } - auto bindingOffset = (scrollWidth * 2) / 3; - auto ft = Formatter(); - ft.Add(STR_SHORTCUT_ENTRY_FORMAT); - if (shortcut.CustomString.empty()) + void DrawTabImage(DrawPixelInfo& dpi, size_t tabIndex) const { - ft.Add(shortcut.StringId); - } - else - { - ft.Add(STR_STRING); - ft.Add(shortcut.CustomString.c_str()); - } - DrawTextEllipsised(dpi, { 0, y - 1 }, bindingOffset, format, ft); + const auto& tabDesc = _tabs[tabIndex]; + auto widgetIndex = static_cast(WIDX_TAB_0 + tabIndex); + if (!IsWidgetDisabled(widgetIndex)) + { + auto imageId = tabDesc.ImageId; + if (imageId != 0) + { + if (tabIndex == _currentTabIndex && tabDesc.ImageDivisor != 0 && tabDesc.ImageNumFrames != 0) + { + auto frame = _tabAnimationIndex / tabDesc.ImageDivisor; + imageId += frame % tabDesc.ImageNumFrames; + } - if (!shortcut.Binding.empty()) - { - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(shortcut.Binding.c_str()); - DrawTextEllipsised(dpi, { bindingOffset, y - 1 }, 150, format, ft); + const auto& widget = widgets[widgetIndex]; + GfxDrawSprite(dpi, ImageId(imageId), windowPos + ScreenCoordsXY{ widget.left, widget.top }); + } + } } - } -}; -void ChangeShortcutWindow::NotifyShortcutKeysWindow() -{ - auto w = WindowFindByClass(WindowClass::KeyboardShortcutList); - if (w != nullptr) + void DrawSeparator(DrawPixelInfo& dpi, int32_t y, int32_t scrollWidth) + { + const int32_t top = y + (SCROLLABLE_ROW_HEIGHT / 2) - 1; + GfxFillRect(dpi, { { 0, top }, { scrollWidth, top } }, ColourMapA[colours[0]].mid_dark); + GfxFillRect(dpi, { { 0, top + 1 }, { scrollWidth, top + 1 } }, ColourMapA[colours[0]].lightest); + } + + void DrawItem( + DrawPixelInfo& dpi, int32_t y, int32_t scrollWidth, const ShortcutStringPair& shortcut, bool isHighlighted) + { + auto format = STR_BLACK_STRING; + if (isHighlighted) + { + format = STR_WINDOW_COLOUR_2_STRINGID; + GfxFilterRect(dpi, { 0, y - 1, scrollWidth, y + (SCROLLABLE_ROW_HEIGHT - 2) }, FilterPaletteID::PaletteDarken1); + } + + auto bindingOffset = (scrollWidth * 2) / 3; + auto ft = Formatter(); + ft.Add(STR_SHORTCUT_ENTRY_FORMAT); + if (shortcut.CustomString.empty()) + { + ft.Add(shortcut.StringId); + } + else + { + ft.Add(STR_STRING); + ft.Add(shortcut.CustomString.c_str()); + } + DrawTextEllipsised(dpi, { 0, y - 1 }, bindingOffset, format, ft); + + if (!shortcut.Binding.empty()) + { + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(shortcut.Binding.c_str()); + DrawTextEllipsised(dpi, { bindingOffset, y - 1 }, 150, format, ft); + } + } + }; + + void ChangeShortcutWindow::NotifyShortcutKeysWindow() { - static_cast(w)->RefreshBindings(); + auto w = WindowFindByClass(WindowClass::KeyboardShortcutList); + if (w != nullptr) + { + static_cast(w)->RefreshBindings(); + } } -} -WindowBase* WindowShortcutKeysOpen() -{ - auto w = WindowBringToFrontByClass(WindowClass::KeyboardShortcutList); - if (w == nullptr) + WindowBase* WindowShortcutKeysOpen() { - w = WindowCreate(WindowClass::KeyboardShortcutList, WW, WH, WF_RESIZABLE); + auto w = WindowBringToFrontByClass(WindowClass::KeyboardShortcutList); + if (w == nullptr) + { + w = WindowCreate(WindowClass::KeyboardShortcutList, WW, WH, WF_RESIZABLE); + } + return w; } - return w; -} #pragma region Reset prompt -static constexpr int32_t RESET_PROMPT_WW = 200; -static constexpr int32_t RESET_PROMPT_WH = 80; + static constexpr int32_t RESET_PROMPT_WW = 200; + static constexpr int32_t RESET_PROMPT_WH = 80; -enum -{ - WIDX_RESET_PROMPT_BACKGROUND, - WIDX_RESET_PROMPT_TITLE, - WIDX_RESET_PROMPT_CLOSE, - WIDX_RESET_PROMPT_LABEL, - WIDX_RESET_PROMPT_RESET, - WIDX_RESET_PROMPT_CANCEL -}; - -static Widget WindowResetShortcutKeysPromptWidgets[] = { - WINDOW_SHIM_WHITE(STR_SHORTCUT_ACTION_RESET, RESET_PROMPT_WW, RESET_PROMPT_WH), - MakeWidget( - { 2, 30 }, { RESET_PROMPT_WW - 4, 12 }, WindowWidgetType::LabelCentred, WindowColour::Primary, - STR_RESET_SHORTCUT_KEYS_PROMPT), - MakeWidget({ 8, RESET_PROMPT_WH - 22 }, { 85, 14 }, WindowWidgetType::Button, WindowColour::Primary, STR_RESET), - MakeWidget( - { RESET_PROMPT_WW - 95, RESET_PROMPT_WH - 22 }, { 85, 14 }, WindowWidgetType::Button, WindowColour::Primary, - STR_SAVE_PROMPT_CANCEL), - kWidgetsEnd, -}; - -class ResetShortcutKeysPrompt final : public Window -{ - void OnOpen() override + enum { - widgets = WindowResetShortcutKeysPromptWidgets; - } + WIDX_RESET_PROMPT_BACKGROUND, + WIDX_RESET_PROMPT_TITLE, + WIDX_RESET_PROMPT_CLOSE, + WIDX_RESET_PROMPT_LABEL, + WIDX_RESET_PROMPT_RESET, + WIDX_RESET_PROMPT_CANCEL + }; - void OnMouseUp(WidgetIndex widgetIndex) override + static Widget WindowResetShortcutKeysPromptWidgets[] = { + WINDOW_SHIM_WHITE(STR_SHORTCUT_ACTION_RESET, RESET_PROMPT_WW, RESET_PROMPT_WH), + MakeWidget( + { 2, 30 }, { RESET_PROMPT_WW - 4, 12 }, WindowWidgetType::LabelCentred, WindowColour::Primary, + STR_RESET_SHORTCUT_KEYS_PROMPT), + MakeWidget({ 8, RESET_PROMPT_WH - 22 }, { 85, 14 }, WindowWidgetType::Button, WindowColour::Primary, STR_RESET), + MakeWidget( + { RESET_PROMPT_WW - 95, RESET_PROMPT_WH - 22 }, { 85, 14 }, WindowWidgetType::Button, WindowColour::Primary, + STR_SAVE_PROMPT_CANCEL), + kWidgetsEnd, + }; + + class ResetShortcutKeysPrompt final : public Window { - switch (widgetIndex) + void OnOpen() override { - case WIDX_RESET_PROMPT_RESET: - { - auto w = WindowFindByClass(WindowClass::KeyboardShortcutList); - if (w != nullptr) - { - static_cast(w)->ResetAllOnActiveTab(); - } - Close(); - break; - } - case WIDX_RESET_PROMPT_CANCEL: - case WIDX_RESET_PROMPT_CLOSE: - Close(); - break; + widgets = WindowResetShortcutKeysPromptWidgets; } - } -}; -WindowBase* ResetShortcutKeysPromptOpen() -{ - return WindowFocusOrCreate( - WindowClass::ResetShortcutKeysPrompt, RESET_PROMPT_WW, RESET_PROMPT_WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); -} + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_RESET_PROMPT_RESET: + { + auto w = WindowFindByClass(WindowClass::KeyboardShortcutList); + if (w != nullptr) + { + static_cast(w)->ResetAllOnActiveTab(); + } + Close(); + break; + } + case WIDX_RESET_PROMPT_CANCEL: + case WIDX_RESET_PROMPT_CLOSE: + Close(); + break; + } + } + }; + + WindowBase* ResetShortcutKeysPromptOpen() + { + return WindowFocusOrCreate( + WindowClass::ResetShortcutKeysPrompt, RESET_PROMPT_WW, RESET_PROMPT_WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); + } #pragma endregion +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Sign.cpp b/src/openrct2-ui/windows/Sign.cpp index 107205ce71..0584cea2e2 100644 --- a/src/openrct2-ui/windows/Sign.cpp +++ b/src/openrct2-ui/windows/Sign.cpp @@ -26,12 +26,13 @@ #include #include #include +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_SIGN; + static constexpr int32_t WW = 113; + static constexpr int32_t WH = 96; -static constexpr StringId WINDOW_TITLE = STR_SIGN; -static constexpr int32_t WW = 113; -static constexpr int32_t WH = 96; - -// clang-format off + // clang-format off enum WindowSignWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -54,319 +55,321 @@ static Widget _signWidgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class SignWindow final : public Window -{ -private: - bool _isSmall = false; - ObjectEntryIndex _sceneryEntry = OBJECT_ENTRY_INDEX_NULL; - colour_t _mainColour = {}; - colour_t _textColour = {}; - - BannerIndex GetBannerIndex() const + class SignWindow final : public Window { - return BannerIndex::FromUnderlying(number); - } + private: + bool _isSmall = false; + ObjectEntryIndex _sceneryEntry = OBJECT_ENTRY_INDEX_NULL; + colour_t _mainColour = {}; + colour_t _textColour = {}; - void ShowTextInput() - { - auto* banner = GetBanner(GetBannerIndex()); - if (banner != nullptr) + BannerIndex GetBannerIndex() const { - auto bannerText = banner->GetText(); - WindowTextInputRawOpen(this, WIDX_SIGN_TEXT, STR_SIGN_TEXT_TITLE, STR_SIGN_TEXT_PROMPT, {}, bannerText.c_str(), 32); - } - } - -public: - void OnOpen() override - { - widgets = _signWidgets; - WindowInitScrollWidgets(*this); - } - - /* - * Initializes the window and sets it's number and if it's small - * @return true if successfull - */ - bool Initialize(rct_windownumber windowNumber, const bool isSmall) - { - number = windowNumber; - _isSmall = isSmall; - auto* banner = GetBanner(GetBannerIndex()); - if (banner == nullptr) - { - return false; + return BannerIndex::FromUnderlying(number); } - auto signViewPosition = banner->position.ToCoordsXY().ToTileCentre(); - auto* tileElement = BannerGetTileElement(GetBannerIndex()); - if (tileElement == nullptr) - return false; - - int32_t viewZ = tileElement->GetBaseZ(); - frame_no = viewZ; - - if (_isSmall) + void ShowTextInput() { - auto* wallElement = tileElement->AsWall(); - if (wallElement == nullptr) + auto* banner = GetBanner(GetBannerIndex()); + if (banner != nullptr) + { + auto bannerText = banner->GetText(); + WindowTextInputRawOpen( + this, WIDX_SIGN_TEXT, STR_SIGN_TEXT_TITLE, STR_SIGN_TEXT_PROMPT, {}, bannerText.c_str(), 32); + } + } + + public: + void OnOpen() override + { + widgets = _signWidgets; + WindowInitScrollWidgets(*this); + } + + /* + * Initializes the window and sets it's number and if it's small + * @return true if successfull + */ + bool Initialize(rct_windownumber windowNumber, const bool isSmall) + { + number = windowNumber; + _isSmall = isSmall; + auto* banner = GetBanner(GetBannerIndex()); + if (banner == nullptr) { return false; } - _mainColour = wallElement->GetPrimaryColour(); - _textColour = wallElement->GetSecondaryColour(); - _sceneryEntry = wallElement->GetEntryIndex(); - } - else - { - auto* sceneryElement = tileElement->AsLargeScenery(); - if (sceneryElement == nullptr) - { + + auto signViewPosition = banner->position.ToCoordsXY().ToTileCentre(); + auto* tileElement = BannerGetTileElement(GetBannerIndex()); + if (tileElement == nullptr) return false; - } - _mainColour = sceneryElement->GetPrimaryColour(); - _textColour = sceneryElement->GetSecondaryColour(); - _sceneryEntry = sceneryElement->GetEntryIndex(); - } - // Create viewport - Widget& viewportWidget = widgets[WIDX_VIEWPORT]; - ViewportCreate( - this, windowPos + ScreenCoordsXY{ viewportWidget.left + 1, viewportWidget.top + 1 }, viewportWidget.width() - 1, - viewportWidget.height() - 1, Focus(CoordsXYZ{ signViewPosition, viewZ })); + int32_t viewZ = tileElement->GetBaseZ(); + frame_no = viewZ; - viewport->flags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE; - Invalidate(); - - return true; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - auto* banner = GetBanner(GetBannerIndex()); - if (banner == nullptr) - { - Close(); - return; - } - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_SIGN_DEMOLISH: + if (_isSmall) { - auto* tileElement = BannerGetTileElement(GetBannerIndex()); - if (tileElement == nullptr) + auto* wallElement = tileElement->AsWall(); + if (wallElement == nullptr) { - Close(); - return; + return false; } - auto bannerCoords = banner->position.ToCoordsXY(); - - if (_isSmall) + _mainColour = wallElement->GetPrimaryColour(); + _textColour = wallElement->GetSecondaryColour(); + _sceneryEntry = wallElement->GetEntryIndex(); + } + else + { + auto* sceneryElement = tileElement->AsLargeScenery(); + if (sceneryElement == nullptr) { - CoordsXYZD wallLocation = { bannerCoords, tileElement->GetBaseZ(), tileElement->GetDirection() }; - auto wallRemoveAction = WallRemoveAction(wallLocation); - GameActions::Execute(&wallRemoveAction); + return false; } - else - { - auto sceneryRemoveAction = LargeSceneryRemoveAction( - { bannerCoords, tileElement->GetBaseZ(), tileElement->GetDirection() }, - tileElement->AsLargeScenery()->GetSequenceIndex()); - GameActions::Execute(&sceneryRemoveAction); - } - break; + _mainColour = sceneryElement->GetPrimaryColour(); + _textColour = sceneryElement->GetSecondaryColour(); + _sceneryEntry = sceneryElement->GetEntryIndex(); } - case WIDX_SIGN_TEXT: - ShowTextInput(); - break; - } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - Widget* widget = &widgets[widgetIndex]; - switch (widgetIndex) - { - case WIDX_MAIN_COLOUR: - WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), static_cast(_mainColour)); - break; - case WIDX_TEXT_COLOUR: - WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), static_cast(_textColour)); - break; - } - } + // Create viewport + Widget& viewportWidget = widgets[WIDX_VIEWPORT]; + ViewportCreate( + this, windowPos + ScreenCoordsXY{ viewportWidget.left + 1, viewportWidget.top + 1 }, viewportWidget.width() - 1, + viewportWidget.height() - 1, Focus(CoordsXYZ{ signViewPosition, viewZ })); - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - switch (widgetIndex) - { - case WIDX_MAIN_COLOUR: - { - if (dropdownIndex == -1) - return; - _mainColour = ColourDropDownIndexToColour(dropdownIndex); - auto signSetStyleAction = SignSetStyleAction(GetBannerIndex(), _mainColour, _textColour, !_isSmall); - GameActions::Execute(&signSetStyleAction); - break; - } - case WIDX_TEXT_COLOUR: - { - if (dropdownIndex == -1) - return; - _textColour = ColourDropDownIndexToColour(dropdownIndex); - auto signSetStyleAction = SignSetStyleAction(GetBannerIndex(), _mainColour, _textColour, !_isSmall); - GameActions::Execute(&signSetStyleAction); - break; - } - default: - return; - } - - Invalidate(); - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex == WIDX_SIGN_TEXT && !text.empty()) - { - auto signSetNameAction = SignSetNameAction(GetBannerIndex(), std::string(text)); - GameActions::Execute(&signSetNameAction); - } - } - - void OnPrepareDraw() override - { - Widget* main_colour_btn = &widgets[WIDX_MAIN_COLOUR]; - Widget* text_colour_btn = &widgets[WIDX_TEXT_COLOUR]; - - if (_isSmall) - { - auto* wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(_sceneryEntry); - - main_colour_btn->type = WindowWidgetType::Empty; - text_colour_btn->type = WindowWidgetType::Empty; - if (wallEntry == nullptr) - { - return; - } - if (wallEntry->flags & WALL_SCENERY_HAS_PRIMARY_COLOUR) - { - main_colour_btn->type = WindowWidgetType::ColourBtn; - } - if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) - { - text_colour_btn->type = WindowWidgetType::ColourBtn; - } - } - else - { - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(_sceneryEntry); - - main_colour_btn->type = WindowWidgetType::Empty; - text_colour_btn->type = WindowWidgetType::Empty; - if (sceneryEntry == nullptr) - { - return; - } - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) - { - main_colour_btn->type = WindowWidgetType::ColourBtn; - } - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) - { - text_colour_btn->type = WindowWidgetType::ColourBtn; - } - } - - main_colour_btn->image = GetColourButtonImage(_mainColour); - text_colour_btn->image = GetColourButtonImage(_textColour); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - if (viewport != nullptr) - { - WindowDrawViewport(dpi, *this); - } - } - - void OnViewportRotate() override - { - RemoveViewport(); - - auto banner = GetBanner(GetBannerIndex()); - if (banner == nullptr) - { - return; - } - - auto signViewPos = CoordsXYZ{ banner->position.ToCoordsXY().ToTileCentre(), frame_no }; - - // Create viewport - Widget* viewportWidget = &widgets[WIDX_VIEWPORT]; - ViewportCreate( - this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }, viewportWidget->width() - 1, - viewportWidget->height() - 1, Focus(CoordsXYZ{ signViewPos })); - if (viewport != nullptr) viewport->flags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE; - Invalidate(); - } + Invalidate(); - void OnResize() override + return true; + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + auto* banner = GetBanner(GetBannerIndex()); + if (banner == nullptr) + { + Close(); + return; + } + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_SIGN_DEMOLISH: + { + auto* tileElement = BannerGetTileElement(GetBannerIndex()); + if (tileElement == nullptr) + { + Close(); + return; + } + auto bannerCoords = banner->position.ToCoordsXY(); + + if (_isSmall) + { + CoordsXYZD wallLocation = { bannerCoords, tileElement->GetBaseZ(), tileElement->GetDirection() }; + auto wallRemoveAction = WallRemoveAction(wallLocation); + GameActions::Execute(&wallRemoveAction); + } + else + { + auto sceneryRemoveAction = LargeSceneryRemoveAction( + { bannerCoords, tileElement->GetBaseZ(), tileElement->GetDirection() }, + tileElement->AsLargeScenery()->GetSequenceIndex()); + GameActions::Execute(&sceneryRemoveAction); + } + break; + } + case WIDX_SIGN_TEXT: + ShowTextInput(); + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + Widget* widget = &widgets[widgetIndex]; + switch (widgetIndex) + { + case WIDX_MAIN_COLOUR: + WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), static_cast(_mainColour)); + break; + case WIDX_TEXT_COLOUR: + WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), static_cast(_textColour)); + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + switch (widgetIndex) + { + case WIDX_MAIN_COLOUR: + { + if (dropdownIndex == -1) + return; + _mainColour = ColourDropDownIndexToColour(dropdownIndex); + auto signSetStyleAction = SignSetStyleAction(GetBannerIndex(), _mainColour, _textColour, !_isSmall); + GameActions::Execute(&signSetStyleAction); + break; + } + case WIDX_TEXT_COLOUR: + { + if (dropdownIndex == -1) + return; + _textColour = ColourDropDownIndexToColour(dropdownIndex); + auto signSetStyleAction = SignSetStyleAction(GetBannerIndex(), _mainColour, _textColour, !_isSmall); + GameActions::Execute(&signSetStyleAction); + break; + } + default: + return; + } + + Invalidate(); + } + + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + if (widgetIndex == WIDX_SIGN_TEXT && !text.empty()) + { + auto signSetNameAction = SignSetNameAction(GetBannerIndex(), std::string(text)); + GameActions::Execute(&signSetNameAction); + } + } + + void OnPrepareDraw() override + { + Widget* main_colour_btn = &widgets[WIDX_MAIN_COLOUR]; + Widget* text_colour_btn = &widgets[WIDX_TEXT_COLOUR]; + + if (_isSmall) + { + auto* wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(_sceneryEntry); + + main_colour_btn->type = WindowWidgetType::Empty; + text_colour_btn->type = WindowWidgetType::Empty; + if (wallEntry == nullptr) + { + return; + } + if (wallEntry->flags & WALL_SCENERY_HAS_PRIMARY_COLOUR) + { + main_colour_btn->type = WindowWidgetType::ColourBtn; + } + if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) + { + text_colour_btn->type = WindowWidgetType::ColourBtn; + } + } + else + { + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(_sceneryEntry); + + main_colour_btn->type = WindowWidgetType::Empty; + text_colour_btn->type = WindowWidgetType::Empty; + if (sceneryEntry == nullptr) + { + return; + } + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) + { + main_colour_btn->type = WindowWidgetType::ColourBtn; + } + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) + { + text_colour_btn->type = WindowWidgetType::ColourBtn; + } + } + + main_colour_btn->image = GetColourButtonImage(_mainColour); + text_colour_btn->image = GetColourButtonImage(_textColour); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + if (viewport != nullptr) + { + WindowDrawViewport(dpi, *this); + } + } + + void OnViewportRotate() override + { + RemoveViewport(); + + auto banner = GetBanner(GetBannerIndex()); + if (banner == nullptr) + { + return; + } + + auto signViewPos = CoordsXYZ{ banner->position.ToCoordsXY().ToTileCentre(), frame_no }; + + // Create viewport + Widget* viewportWidget = &widgets[WIDX_VIEWPORT]; + ViewportCreate( + this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }, + viewportWidget->width() - 1, viewportWidget->height() - 1, Focus(CoordsXYZ{ signViewPos })); + if (viewport != nullptr) + viewport->flags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE; + Invalidate(); + } + + void OnResize() override + { + ResizeFrame(); + } + }; + + /** + * + * rct2: 0x006BA305 + */ + WindowBase* WindowSignOpen(rct_windownumber number) { - ResizeFrame(); + auto* w = static_cast(WindowBringToFrontByNumber(WindowClass::Banner, number)); + + if (w != nullptr) + return w; + + w = WindowCreate(WindowClass::Banner, WW, WH, 0); + + if (w == nullptr) + return nullptr; + + bool result = w->Initialize(number, false); + if (result != true) + return nullptr; + + return w; } -}; -/** - * - * rct2: 0x006BA305 - */ -WindowBase* WindowSignOpen(rct_windownumber number) -{ - auto* w = static_cast(WindowBringToFrontByNumber(WindowClass::Banner, number)); + /** + * + * rct2: 0x6E5F52 + */ + WindowBase* WindowSignSmallOpen(rct_windownumber number) + { + auto* w = static_cast(WindowBringToFrontByNumber(WindowClass::Banner, number)); + + if (w != nullptr) + return w; + + w = WindowCreate(WindowClass::Banner, WW, WH, 0); + + if (w == nullptr) + return nullptr; + + bool result = w->Initialize(number, true); + if (result != true) + return nullptr; - if (w != nullptr) return w; - - w = WindowCreate(WindowClass::Banner, WW, WH, 0); - - if (w == nullptr) - return nullptr; - - bool result = w->Initialize(number, false); - if (result != true) - return nullptr; - - return w; -} - -/** - * - * rct2: 0x6E5F52 - */ -WindowBase* WindowSignSmallOpen(rct_windownumber number) -{ - auto* w = static_cast(WindowBringToFrontByNumber(WindowClass::Banner, number)); - - if (w != nullptr) - return w; - - w = WindowCreate(WindowClass::Banner, WW, WH, 0); - - if (w == nullptr) - return nullptr; - - bool result = w->Initialize(number, true); - if (result != true) - return nullptr; - - return w; -} + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Staff.cpp b/src/openrct2-ui/windows/Staff.cpp index ba50504a93..e98a3d52b0 100644 --- a/src/openrct2-ui/windows/Staff.cpp +++ b/src/openrct2-ui/windows/Staff.cpp @@ -35,51 +35,51 @@ #include #include -using namespace OpenRCT2; - -static constexpr StringId WINDOW_TITLE = STR_STRINGID; - -static constexpr int32_t WW = 190; -static constexpr int32_t WH = 180; - -enum WindowStaffPage +namespace OpenRCT2::Ui::Windows { - WINDOW_STAFF_OVERVIEW, - WINDOW_STAFF_OPTIONS, - WINDOW_STAFF_STATISTICS, - WINDOW_STAFF_PAGE_COUNT, -}; + static constexpr StringId WINDOW_TITLE = STR_STRINGID; -enum WindowStaffWidgetIdx -{ - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_RESIZE, - WIDX_TAB_1, - WIDX_TAB_2, - WIDX_TAB_3, + static constexpr int32_t WW = 190; + static constexpr int32_t WH = 180; - WIDX_VIEWPORT = 7, - WIDX_BTM_LABEL, - WIDX_PICKUP, - WIDX_PATROL, - WIDX_RENAME, - WIDX_LOCATE, - WIDX_FIRE, + enum WindowStaffPage + { + WINDOW_STAFF_OVERVIEW, + WINDOW_STAFF_OPTIONS, + WINDOW_STAFF_STATISTICS, + WINDOW_STAFF_PAGE_COUNT, + }; - WIDX_CHECKBOX_1 = 7, - WIDX_CHECKBOX_2, - WIDX_CHECKBOX_3, - WIDX_CHECKBOX_4, - WIDX_COSTUME_BOX, - WIDX_COSTUME_BTN, -}; + enum WindowStaffWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_RESIZE, + WIDX_TAB_1, + WIDX_TAB_2, + WIDX_TAB_3, -validate_global_widx(WC_PEEP, WIDX_PATROL); -validate_global_widx(WC_STAFF, WIDX_PICKUP); + WIDX_VIEWPORT = 7, + WIDX_BTM_LABEL, + WIDX_PICKUP, + WIDX_PATROL, + WIDX_RENAME, + WIDX_LOCATE, + WIDX_FIRE, -// clang-format off + WIDX_CHECKBOX_1 = 7, + WIDX_CHECKBOX_2, + WIDX_CHECKBOX_3, + WIDX_CHECKBOX_4, + WIDX_COSTUME_BOX, + WIDX_COSTUME_BTN, + }; + + validate_global_widx(WC_PEEP, WIDX_PATROL); + validate_global_widx(WC_STAFF, WIDX_PICKUP); + + // clang-format off #define MAIN_STAFF_WIDGETS \ WINDOW_SHIM(WINDOW_TITLE, WW, WH), \ MakeWidget({ 0, 43}, {190, 137}, WindowWidgetType::Resize, WindowColour::Secondary), /* Resize */ \ @@ -110,1150 +110,1152 @@ static Widget _staffOptionsWidgets[] = { MakeWidget ({WW - 17, 51}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_COSTUME_TIP), // Costume Dropdown Button kWidgetsEnd, }; -// clang-format on + // clang-format on -// 0x9AF9F4 -static Widget _staffStatsWidgets[] = { - MAIN_STAFF_WIDGETS, - kWidgetsEnd, -}; + // 0x9AF9F4 + static Widget _staffStatsWidgets[] = { + MAIN_STAFF_WIDGETS, + kWidgetsEnd, + }; -static Widget* window_staff_page_widgets[] = { - _staffOverviewWidgets, - _staffOptionsWidgets, - _staffStatsWidgets, -}; + static Widget* window_staff_page_widgets[] = { + _staffOverviewWidgets, + _staffOptionsWidgets, + _staffStatsWidgets, + }; -class StaffWindow final : public Window -{ -private: - EntertainerCostume _availableCostumes[EnumValue(EntertainerCostume::Count)]{}; - uint16_t _tabAnimationOffset = 0; - int32_t _pickedPeepOldX = LOCATION_NULL; - -public: - void Initialise(EntityId entityId) + class StaffWindow final : public Window { - number = entityId.ToUnderlying(); - } + private: + EntertainerCostume _availableCostumes[EnumValue(EntertainerCostume::Count)]{}; + uint16_t _tabAnimationOffset = 0; + int32_t _pickedPeepOldX = LOCATION_NULL; - void OnOpen() override - { - SetPage(WINDOW_STAFF_OVERVIEW); - } + public: + void Initialise(EntityId entityId) + { + number = entityId.ToUnderlying(); + } - void OnClose() override - { - CancelTools(); - } + void OnOpen() override + { + SetPage(WINDOW_STAFF_OVERVIEW); + } - void OnMouseUp(WidgetIndex widgetIndex) override - { - if (widgetIndex <= WIDX_TAB_3) - CommonMouseUp(widgetIndex); - else + void OnClose() override + { + CancelTools(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + if (widgetIndex <= WIDX_TAB_3) + CommonMouseUp(widgetIndex); + else + { + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewMouseUp(widgetIndex); + break; + case WINDOW_STAFF_OPTIONS: + OptionsMouseUp(widgetIndex); + } + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override { switch (page) { case WINDOW_STAFF_OVERVIEW: - OverviewMouseUp(widgetIndex); + OverviewOnMouseDown(widgetIndex); break; case WINDOW_STAFF_OPTIONS: - OptionsMouseUp(widgetIndex); + OptionsOnMouseDown(widgetIndex); + break; } } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (page) + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override { - case WINDOW_STAFF_OVERVIEW: - OverviewOnMouseDown(widgetIndex); - break; - case WINDOW_STAFF_OPTIONS: - OptionsOnMouseDown(widgetIndex); - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - switch (page) - { - case WINDOW_STAFF_OVERVIEW: - OverviewOnDropdown(widgetIndex, dropdownIndex); - break; - case WINDOW_STAFF_OPTIONS: - OptionsOnDropdown(widgetIndex, dropdownIndex); - break; - } - } - - void OnPrepareDraw() override - { - CommonPrepareDrawBefore(); - - switch (page) - { - case WINDOW_STAFF_OVERVIEW: - OverviewPrepareDraw(); - break; - case WINDOW_STAFF_OPTIONS: - OptionsPrepareDraw(); - break; + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewOnDropdown(widgetIndex, dropdownIndex); + break; + case WINDOW_STAFF_OPTIONS: + OptionsOnDropdown(widgetIndex, dropdownIndex); + break; + } } - CommonPrepareDrawAfter(); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - switch (page) + void OnPrepareDraw() override { - case WINDOW_STAFF_OVERVIEW: - OverviewDraw(dpi); - break; - case WINDOW_STAFF_STATISTICS: - StatsDraw(dpi); - break; - } - } + CommonPrepareDrawBefore(); - void OnResize() override - { - switch (page) + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewPrepareDraw(); + break; + case WINDOW_STAFF_OPTIONS: + OptionsPrepareDraw(); + break; + } + + CommonPrepareDrawAfter(); + } + + void OnDraw(DrawPixelInfo& dpi) override { - case WINDOW_STAFF_OVERVIEW: - OverviewResize(); - break; - case WINDOW_STAFF_OPTIONS: - OptionsResize(); - break; - case WINDOW_STAFF_STATISTICS: - StatsResize(); - break; - } - } + DrawWidgets(dpi); + DrawTabImages(dpi); - void OnUpdate() override - { - switch (page) + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewDraw(dpi); + break; + case WINDOW_STAFF_STATISTICS: + StatsDraw(dpi); + break; + } + } + + void OnResize() override { - case WINDOW_STAFF_OVERVIEW: - OverviewUpdate(); - break; - case WINDOW_STAFF_OPTIONS: - OptionsUpdate(); - break; - case WINDOW_STAFF_STATISTICS: - StatsUpdate(); - break; + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewResize(); + break; + case WINDOW_STAFF_OPTIONS: + OptionsResize(); + break; + case WINDOW_STAFF_STATISTICS: + StatsResize(); + break; + } } - } - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (page) + void OnUpdate() override { - case WINDOW_STAFF_OVERVIEW: - OverviewToolUpdate(widgetIndex, screenCoords); - break; + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewUpdate(); + break; + case WINDOW_STAFF_OPTIONS: + OptionsUpdate(); + break; + case WINDOW_STAFF_STATISTICS: + StatsUpdate(); + break; + } } - } - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (page) + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - case WINDOW_STAFF_OVERVIEW: - OverviewToolDown(widgetIndex, screenCoords); - break; + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewToolUpdate(widgetIndex, screenCoords); + break; + } } - } - void OnToolAbort(WidgetIndex widgetIndex) override - { - switch (page) + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - case WINDOW_STAFF_OVERVIEW: - OverviewToolAbort(widgetIndex); - break; + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewToolDown(widgetIndex, screenCoords); + break; + } } - } - void OnViewportRotate() override - { - switch (page) + void OnToolAbort(WidgetIndex widgetIndex) override { - case WINDOW_STAFF_OVERVIEW: - OverviewViewportRotate(); - break; + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewToolAbort(widgetIndex); + break; + } } - } - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - switch (page) + void OnViewportRotate() override { - case WINDOW_STAFF_OVERVIEW: - OverviewTextInput(widgetIndex, text); - break; + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewViewportRotate(); + break; + } } - } -private: + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + switch (page) + { + case WINDOW_STAFF_OVERVIEW: + OverviewTextInput(widgetIndex, text); + break; + } + } + + private: #pragma region Common events - void CommonMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void CommonMouseUp(WidgetIndex widgetIndex) { - case WIDX_CLOSE: - Close(); - break; - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - SetPage(widgetIndex - WIDX_TAB_1); - break; - } - } - - void CommonPrepareDrawBefore() - { - ColourSchemeUpdateByClass(this, static_cast(WindowClass::Staff)); - - if (window_staff_page_widgets[page] != widgets) - { - widgets = window_staff_page_widgets[page]; - InitScrollWidgets(); - } - SetPressedTab(); - DisableWidgets(); - - auto staff = GetStaff(); - if (staff == nullptr) - { - return; + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB_1: + case WIDX_TAB_2: + case WIDX_TAB_3: + SetPage(widgetIndex - WIDX_TAB_1); + break; + } } - auto ft = Formatter::Common(); - staff->FormatNameTo(ft); + void CommonPrepareDrawBefore() + { + ColourSchemeUpdateByClass(this, static_cast(WindowClass::Staff)); - ResizeFrameWithPage(); - } + if (window_staff_page_widgets[page] != widgets) + { + widgets = window_staff_page_widgets[page]; + InitScrollWidgets(); + } + SetPressedTab(); + DisableWidgets(); - void CommonPrepareDrawAfter() - { - WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_3); - } + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + auto ft = Formatter::Common(); + staff->FormatNameTo(ft); + + ResizeFrameWithPage(); + } + + void CommonPrepareDrawAfter() + { + WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_3); + } #pragma endregion #pragma region Overview tab events - void OverviewMouseUp(WidgetIndex widgetIndex) - { - auto staff = GetStaff(); - if (staff == nullptr) + void OverviewMouseUp(WidgetIndex widgetIndex) { - return; - } - - switch (widgetIndex) - { - case WIDX_PICKUP: + auto staff = GetStaff(); + if (staff == nullptr) { - _pickedPeepOldX = staff->x; - CoordsXYZ nullLoc{}; - nullLoc.SetNull(); - PeepPickupAction pickupAction{ PeepPickupType::Pickup, EntityId::FromUnderlying(number), nullLoc, - NetworkGetCurrentPlayerId() }; - pickupAction.SetCallback([peepnum = number](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) - return; - WindowBase* wind = WindowFindByNumber(WindowClass::Peep, peepnum); - if (wind != nullptr) - { - ToolSet(*wind, WC_STAFF__WIDX_PICKUP, Tool::Picker); - } - }); - GameActions::Execute(&pickupAction); + return; } - break; - case WIDX_FIRE: + + switch (widgetIndex) { - auto intent = Intent(WindowClass::FirePrompt); - intent.PutExtra(INTENT_EXTRA_PEEP, staff); - ContextOpenIntent(&intent); - break; - } - case WIDX_RENAME: - { - auto peepName = staff->GetName(); - WindowTextInputRawOpen( - this, widgetIndex, STR_STAFF_TITLE_STAFF_MEMBER_NAME, STR_STAFF_PROMPT_ENTER_NAME, {}, peepName.c_str(), - 32); - break; - } - } - } - - void OverviewOnMouseDown(WidgetIndex widgetIndex) - { - Widget* widget = &widgets[widgetIndex]; - - switch (widgetIndex) - { - case WIDX_LOCATE: - ShowLocateDropdown(widget); - break; - case WIDX_PATROL: - { - // Dropdown names - gDropdownItems[0].Format = STR_SET_PATROL_AREA; - gDropdownItems[1].Format = STR_CLEAR_PATROL_AREA; - - auto ddPos = ScreenCoordsXY{ widget->left + windowPos.x, widget->top + windowPos.y }; - int32_t extraHeight = widget->height() + 1; - WindowDropdownShowText(ddPos, extraHeight, colours[1], 0, 2); - gDropdownDefaultIndex = 0; - - auto staff = GetStaff(); - if (staff == nullptr) + case WIDX_PICKUP: { - return; + _pickedPeepOldX = staff->x; + CoordsXYZ nullLoc{}; + nullLoc.SetNull(); + PeepPickupAction pickupAction{ PeepPickupType::Pickup, EntityId::FromUnderlying(number), nullLoc, + NetworkGetCurrentPlayerId() }; + pickupAction.SetCallback([peepnum = number](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; + WindowBase* wind = WindowFindByNumber(WindowClass::Peep, peepnum); + if (wind != nullptr) + { + ToolSet(*wind, WC_STAFF__WIDX_PICKUP, Tool::Picker); + } + }); + GameActions::Execute(&pickupAction); } - - // Disable clear patrol area if no area is set. - if (!staff->HasPatrolArea()) + break; + case WIDX_FIRE: { - Dropdown::SetDisabled(1, true); + auto intent = Intent(WindowClass::FirePrompt); + intent.PutExtra(INTENT_EXTRA_PEEP, staff); + ContextOpenIntent(&intent); + break; + } + case WIDX_RENAME: + { + auto peepName = staff->GetName(); + WindowTextInputRawOpen( + this, widgetIndex, STR_STAFF_TITLE_STAFF_MEMBER_NAME, STR_STAFF_PROMPT_ENTER_NAME, {}, peepName.c_str(), + 32); + break; } } } - } - void OverviewOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - switch (widgetIndex) + void OverviewOnMouseDown(WidgetIndex widgetIndex) { - case WIDX_LOCATE: + Widget* widget = &widgets[widgetIndex]; + + switch (widgetIndex) { - if (dropdownIndex == 0) - { - ScrollToViewport(); - } - else if (dropdownIndex == 1) - { - FollowPeep(); - } - break; - } - case WIDX_PATROL: - { - // Clear patrol - if (dropdownIndex == 1) + case WIDX_LOCATE: + ShowLocateDropdown(widget); + break; + case WIDX_PATROL: { + // Dropdown names + gDropdownItems[0].Format = STR_SET_PATROL_AREA; + gDropdownItems[1].Format = STR_CLEAR_PATROL_AREA; + + auto ddPos = ScreenCoordsXY{ widget->left + windowPos.x, widget->top + windowPos.y }; + int32_t extraHeight = widget->height() + 1; + WindowDropdownShowText(ddPos, extraHeight, colours[1], 0, 2); + gDropdownDefaultIndex = 0; + auto staff = GetStaff(); if (staff == nullptr) { return; } - WindowCloseByClass(WindowClass::PatrolArea); - - auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(staff->Id, {}, StaffSetPatrolAreaMode::ClearAll); - GameActions::Execute(&staffSetPatrolAreaAction); - } - else - { - auto staffId = EntityId::FromUnderlying(number); - if (WindowPatrolAreaGetCurrentStaffId() == staffId) + // Disable clear patrol area if no area is set. + if (!staff->HasPatrolArea()) { + Dropdown::SetDisabled(1, true); + } + } + } + } + + void OverviewOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + switch (widgetIndex) + { + case WIDX_LOCATE: + { + if (dropdownIndex == 0) + { + ScrollToViewport(); + } + else if (dropdownIndex == 1) + { + FollowPeep(); + } + break; + } + case WIDX_PATROL: + { + // Clear patrol + if (dropdownIndex == 1) + { + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + WindowCloseByClass(WindowClass::PatrolArea); + + auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction( + staff->Id, {}, StaffSetPatrolAreaMode::ClearAll); + GameActions::Execute(&staffSetPatrolAreaAction); } else { - WindowPatrolAreaOpen(staffId); + auto staffId = EntityId::FromUnderlying(number); + if (WindowPatrolAreaGetCurrentStaffId() == staffId) + { + WindowCloseByClass(WindowClass::PatrolArea); + } + else + { + WindowPatrolAreaOpen(staffId); + } } + break; } - break; } } - } - void OverviewPrepareDraw() - { - auto staff = GetStaff(); - if (staff == nullptr) + void OverviewPrepareDraw() { - return; - } - - widgets[WIDX_VIEWPORT].right = width - 26; - widgets[WIDX_VIEWPORT].bottom = height - 14; - - widgets[WIDX_BTM_LABEL].right = width - 26; - widgets[WIDX_BTM_LABEL].top = height - 13; - widgets[WIDX_BTM_LABEL].bottom = height - 3; - - widgets[WIDX_PICKUP].left = width - 25; - widgets[WIDX_PICKUP].right = width - 2; - - SetWidgetPressed(WIDX_PATROL, WindowPatrolAreaGetCurrentStaffId() == staff->Id); - - widgets[WIDX_PATROL].left = width - 25; - widgets[WIDX_PATROL].right = width - 2; - - widgets[WIDX_RENAME].left = width - 25; - widgets[WIDX_RENAME].right = width - 2; - - widgets[WIDX_LOCATE].left = width - 25; - widgets[WIDX_LOCATE].right = width - 2; - - widgets[WIDX_FIRE].left = width - 25; - widgets[WIDX_FIRE].right = width - 2; - } - - void OverviewDraw(DrawPixelInfo& dpi) - { - // Draw the viewport no sound sprite - if (viewport != nullptr) - { - WindowDrawViewport(dpi, *this); - - if (viewport->flags & VIEWPORT_FLAG_SOUND_ON) + auto staff = GetStaff(); + if (staff == nullptr) { - GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this)); - } - } - - // Draw the centred label - auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - auto ft = Formatter(); - staff->FormatActionTo(ft); - const auto& widget = widgets[WIDX_BTM_LABEL]; - auto screenPos = windowPos + ScreenCoordsXY{ widget.midX(), widget.top }; - int32_t widgetWidth = widget.width(); - DrawTextEllipsised(dpi, screenPos, widgetWidth, STR_BLACK_STRING, ft, { TextAlignment::CENTRE }); - } - - void DrawOverviewTabImage(DrawPixelInfo& dpi) - { - if (IsWidgetDisabled(WIDX_TAB_1)) - return; - - const auto& widget = widgets[WIDX_TAB_1]; - int32_t widgetWidth = widget.width() - 1; - int32_t widgetHeight = widget.height() - 1; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; - if (page == WINDOW_STAFF_OVERVIEW) - widgetHeight++; - - DrawPixelInfo clip_dpi; - if (!ClipDrawPixelInfo(clip_dpi, dpi, screenCoords, widgetWidth, widgetHeight)) - { - return; - } - - screenCoords = ScreenCoordsXY{ 14, 20 }; - - auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - - if (staff->Is() && staff->AssignedStaffType == StaffType::Entertainer) - screenCoords.y++; - - int32_t imageIndex = GetPeepAnimation(staff->SpriteType).base_image + 1; - - int32_t offset = 0; - - if (page == WINDOW_STAFF_OVERVIEW) - { - offset = _tabAnimationOffset; - offset = Floor2(offset, 4); - } - imageIndex += offset; - - GfxDrawSprite(clip_dpi, ImageId(imageIndex, staff->TshirtColour, staff->TrousersColour), screenCoords); - } - - void OverviewResize() - { - min_width = WW; - max_width = 500; - min_height = WH; - max_height = 450; - - if (width < min_width) - { - width = min_width; - Invalidate(); - } - if (width > max_width) - { - Invalidate(); - width = max_width; - } - if (height < min_height) - { - height = min_height; - Invalidate(); - } - if (height > max_height) - { - Invalidate(); - height = max_height; - } - - if (viewport != nullptr) - { - int32_t newWidth = width - 30; - int32_t newHeight = height - 62; - - // Update the viewport size - if (viewport->width != newWidth || viewport->height != newHeight) - { - viewport->width = newWidth; - viewport->height = newHeight; - viewport->view_width = viewport->zoom.ApplyTo(newWidth); - viewport->view_height = viewport->zoom.ApplyTo(newHeight); - } - } - - ViewportInit(); - } - - void OverviewUpdate() - { - _tabAnimationOffset++; - _tabAnimationOffset %= 24; - - // Update pickup animation, can only happen in this tab. - picked_peep_frame++; - picked_peep_frame %= 48; - - InvalidateWidget(WIDX_TAB_1); - } - - void OverviewToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) - { - if (widgetIndex != WIDX_PICKUP) - return; - - MapInvalidateSelectionRect(); - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - auto mapCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, nullptr); - if (!mapCoords.IsNull()) - { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectType = MAP_SELECT_TYPE_FULL; - gMapSelectPositionA = mapCoords; - gMapSelectPositionB = mapCoords; - MapInvalidateSelectionRect(); - } - - gPickupPeepImage = ImageId(); - - auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionItemAll); - if (info.SpriteType == ViewportInteractionItem::None) - return; - - gPickupPeepX = screenCoords.x - 1; - gPickupPeepY = screenCoords.y + 16; - - auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - - auto baseImageId = GetPeepAnimation(staff->SpriteType, PeepActionSpriteType::Ui).base_image; - baseImageId += picked_peep_frame >> 2; - gPickupPeepImage = ImageId(baseImageId, staff->TshirtColour, staff->TrousersColour); - } - - void OverviewToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) - { - if (widgetIndex != WIDX_PICKUP) - return; - - const auto staffEntityId = EntityId::FromUnderlying(number); - TileElement* tileElement; - auto destCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement); - - if (destCoords.IsNull()) - return; - - PeepPickupAction pickupAction{ - PeepPickupType::Place, staffEntityId, { destCoords, tileElement->GetBaseZ() }, NetworkGetCurrentPlayerId() - }; - pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) return; - ToolCancel(); + } + + widgets[WIDX_VIEWPORT].right = width - 26; + widgets[WIDX_VIEWPORT].bottom = height - 14; + + widgets[WIDX_BTM_LABEL].right = width - 26; + widgets[WIDX_BTM_LABEL].top = height - 13; + widgets[WIDX_BTM_LABEL].bottom = height - 3; + + widgets[WIDX_PICKUP].left = width - 25; + widgets[WIDX_PICKUP].right = width - 2; + + SetWidgetPressed(WIDX_PATROL, WindowPatrolAreaGetCurrentStaffId() == staff->Id); + + widgets[WIDX_PATROL].left = width - 25; + widgets[WIDX_PATROL].right = width - 2; + + widgets[WIDX_RENAME].left = width - 25; + widgets[WIDX_RENAME].right = width - 2; + + widgets[WIDX_LOCATE].left = width - 25; + widgets[WIDX_LOCATE].right = width - 2; + + widgets[WIDX_FIRE].left = width - 25; + widgets[WIDX_FIRE].right = width - 2; + } + + void OverviewDraw(DrawPixelInfo& dpi) + { + // Draw the viewport no sound sprite + if (viewport != nullptr) + { + WindowDrawViewport(dpi, *this); + + if (viewport->flags & VIEWPORT_FLAG_SOUND_ON) + { + GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this)); + } + } + + // Draw the centred label + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + auto ft = Formatter(); + staff->FormatActionTo(ft); + const auto& widget = widgets[WIDX_BTM_LABEL]; + auto screenPos = windowPos + ScreenCoordsXY{ widget.midX(), widget.top }; + int32_t widgetWidth = widget.width(); + DrawTextEllipsised(dpi, screenPos, widgetWidth, STR_BLACK_STRING, ft, { TextAlignment::CENTRE }); + } + + void DrawOverviewTabImage(DrawPixelInfo& dpi) + { + if (IsWidgetDisabled(WIDX_TAB_1)) + return; + + const auto& widget = widgets[WIDX_TAB_1]; + int32_t widgetWidth = widget.width() - 1; + int32_t widgetHeight = widget.height() - 1; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 }; + if (page == WINDOW_STAFF_OVERVIEW) + widgetHeight++; + + DrawPixelInfo clip_dpi; + if (!ClipDrawPixelInfo(clip_dpi, dpi, screenCoords, widgetWidth, widgetHeight)) + { + return; + } + + screenCoords = ScreenCoordsXY{ 14, 20 }; + + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + if (staff->Is() && staff->AssignedStaffType == StaffType::Entertainer) + screenCoords.y++; + + int32_t imageIndex = GetPeepAnimation(staff->SpriteType).base_image + 1; + + int32_t offset = 0; + + if (page == WINDOW_STAFF_OVERVIEW) + { + offset = _tabAnimationOffset; + offset = Floor2(offset, 4); + } + imageIndex += offset; + + GfxDrawSprite(clip_dpi, ImageId(imageIndex, staff->TshirtColour, staff->TrousersColour), screenCoords); + } + + void OverviewResize() + { + min_width = WW; + max_width = 500; + min_height = WH; + max_height = 450; + + if (width < min_width) + { + width = min_width; + Invalidate(); + } + if (width > max_width) + { + Invalidate(); + width = max_width; + } + if (height < min_height) + { + height = min_height; + Invalidate(); + } + if (height > max_height) + { + Invalidate(); + height = max_height; + } + + if (viewport != nullptr) + { + int32_t newWidth = width - 30; + int32_t newHeight = height - 62; + + // Update the viewport size + if (viewport->width != newWidth || viewport->height != newHeight) + { + viewport->width = newWidth; + viewport->height = newHeight; + viewport->view_width = viewport->zoom.ApplyTo(newWidth); + viewport->view_height = viewport->zoom.ApplyTo(newHeight); + } + } + + ViewportInit(); + } + + void OverviewUpdate() + { + _tabAnimationOffset++; + _tabAnimationOffset %= 24; + + // Update pickup animation, can only happen in this tab. + picked_peep_frame++; + picked_peep_frame %= 48; + + InvalidateWidget(WIDX_TAB_1); + } + + void OverviewToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex != WIDX_PICKUP) + return; + + MapInvalidateSelectionRect(); + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + + auto mapCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, nullptr); + if (!mapCoords.IsNull()) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectPositionA = mapCoords; + gMapSelectPositionB = mapCoords; + MapInvalidateSelectionRect(); + } + gPickupPeepImage = ImageId(); - }); - GameActions::Execute(&pickupAction); - } - void OverviewToolAbort(WidgetIndex widgetIndex) - { - if (widgetIndex != WIDX_PICKUP) - return; + auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionItemAll); + if (info.SpriteType == ViewportInteractionItem::None) + return; - PeepPickupAction pickupAction{ - PeepPickupType::Cancel, EntityId::FromUnderlying(number), { _pickedPeepOldX, 0, 0 }, NetworkGetCurrentPlayerId() - }; - GameActions::Execute(&pickupAction); - } + gPickupPeepX = screenCoords.x - 1; + gPickupPeepY = screenCoords.y + 16; - void OverviewViewportRotate() - { - ViewportInit(); - } + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } - void OverviewTextInput(WidgetIndex widgetIndex, std::string_view text) - { - if (widgetIndex != WIDX_RENAME) - return; + auto baseImageId = GetPeepAnimation(staff->SpriteType, PeepActionSpriteType::Ui).base_image; + baseImageId += picked_peep_frame >> 2; + gPickupPeepImage = ImageId(baseImageId, staff->TshirtColour, staff->TrousersColour); + } - if (text.empty()) - return; + void OverviewToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetIndex != WIDX_PICKUP) + return; - auto gameAction = StaffSetNameAction(EntityId::FromUnderlying(number), std::string{ text }); - GameActions::Execute(&gameAction); - } + const auto staffEntityId = EntityId::FromUnderlying(number); + TileElement* tileElement; + auto destCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement); + + if (destCoords.IsNull()) + return; + + PeepPickupAction pickupAction{ + PeepPickupType::Place, staffEntityId, { destCoords, tileElement->GetBaseZ() }, NetworkGetCurrentPlayerId() + }; + pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; + ToolCancel(); + gPickupPeepImage = ImageId(); + }); + GameActions::Execute(&pickupAction); + } + + void OverviewToolAbort(WidgetIndex widgetIndex) + { + if (widgetIndex != WIDX_PICKUP) + return; + + PeepPickupAction pickupAction{ + PeepPickupType::Cancel, EntityId::FromUnderlying(number), { _pickedPeepOldX, 0, 0 }, NetworkGetCurrentPlayerId() + }; + GameActions::Execute(&pickupAction); + } + + void OverviewViewportRotate() + { + ViewportInit(); + } + + void OverviewTextInput(WidgetIndex widgetIndex, std::string_view text) + { + if (widgetIndex != WIDX_RENAME) + return; + + if (text.empty()) + return; + + auto gameAction = StaffSetNameAction(EntityId::FromUnderlying(number), std::string{ text }); + GameActions::Execute(&gameAction); + } #pragma endregion #pragma region Options tab events - void OptionsMouseUp(WidgetIndex widgetIndex) - { - switch (widgetIndex) + void OptionsMouseUp(WidgetIndex widgetIndex) { - case WIDX_CHECKBOX_1: - case WIDX_CHECKBOX_2: - case WIDX_CHECKBOX_3: - case WIDX_CHECKBOX_4: - SetOrder(widgetIndex - WIDX_CHECKBOX_1); - break; - } - } - - void OptionsOnMouseDown(WidgetIndex widgetIndex) - { - if (widgetIndex != WIDX_COSTUME_BTN) - { - return; - } - - auto* ddWidget = &widgets[WIDX_COSTUME_BOX]; - - auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - - int32_t checkedIndex = -1; - // This will be moved below where Items Checked is when all - // of dropdown related functions are finished. This prevents - // the dropdown from not working on first click. - int32_t numCostumes = StaffGetAvailableEntertainerCostumeList(_availableCostumes); - for (int32_t i = 0; i < numCostumes; i++) - { - EntertainerCostume costume = _availableCostumes[i]; - if (staff->SpriteType == EntertainerCostumeToSprite(costume)) + switch (widgetIndex) { - checkedIndex = i; + case WIDX_CHECKBOX_1: + case WIDX_CHECKBOX_2: + case WIDX_CHECKBOX_3: + case WIDX_CHECKBOX_4: + SetOrder(widgetIndex - WIDX_CHECKBOX_1); + break; } - gDropdownItems[i].Args = StaffCostumeNames[EnumValue(costume)]; - gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; } - auto ddPos = ScreenCoordsXY{ ddWidget->left + windowPos.x, ddWidget->top + windowPos.y }; - int32_t ddHeight = ddWidget->height() + 1; - int32_t ddWidth = ddWidget->width() - 3; - WindowDropdownShowTextCustomWidth(ddPos, ddHeight, colours[1], 0, Dropdown::Flag::StayOpen, numCostumes, ddWidth); - - // See above note. - if (checkedIndex != -1) + void OptionsOnMouseDown(WidgetIndex widgetIndex) { - Dropdown::SetChecked(checkedIndex, true); - } - } + if (widgetIndex != WIDX_COSTUME_BTN) + { + return; + } - void OptionsOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - if (widgetIndex != WIDX_COSTUME_BTN) + auto* ddWidget = &widgets[WIDX_COSTUME_BOX]; + + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + int32_t checkedIndex = -1; + // This will be moved below where Items Checked is when all + // of dropdown related functions are finished. This prevents + // the dropdown from not working on first click. + int32_t numCostumes = StaffGetAvailableEntertainerCostumeList(_availableCostumes); + for (int32_t i = 0; i < numCostumes; i++) + { + EntertainerCostume costume = _availableCostumes[i]; + if (staff->SpriteType == EntertainerCostumeToSprite(costume)) + { + checkedIndex = i; + } + gDropdownItems[i].Args = StaffCostumeNames[EnumValue(costume)]; + gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL; + } + + auto ddPos = ScreenCoordsXY{ ddWidget->left + windowPos.x, ddWidget->top + windowPos.y }; + int32_t ddHeight = ddWidget->height() + 1; + int32_t ddWidth = ddWidget->width() - 3; + WindowDropdownShowTextCustomWidth(ddPos, ddHeight, colours[1], 0, Dropdown::Flag::StayOpen, numCostumes, ddWidth); + + // See above note. + if (checkedIndex != -1) + { + Dropdown::SetChecked(checkedIndex, true); + } + } + + void OptionsOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { - return; + if (widgetIndex != WIDX_COSTUME_BTN) + { + return; + } + + if (dropdownIndex == -1) + return; + + EntertainerCostume costume = _availableCostumes[dropdownIndex]; + auto staffSetCostumeAction = StaffSetCostumeAction(EntityId::FromUnderlying(number), costume); + GameActions::Execute(&staffSetCostumeAction); } - if (dropdownIndex == -1) - return; - - EntertainerCostume costume = _availableCostumes[dropdownIndex]; - auto staffSetCostumeAction = StaffSetCostumeAction(EntityId::FromUnderlying(number), costume); - GameActions::Execute(&staffSetCostumeAction); - } - - void OptionsPrepareDraw() - { - auto staff = GetStaff(); - if (staff == nullptr) + void OptionsPrepareDraw() { - return; + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + switch (staff->AssignedStaffType) + { + case StaffType::Entertainer: + widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Empty; + widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Empty; + widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty; + widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty; + widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Button; + widgets[WIDX_COSTUME_BOX].text = StaffCostumeNames[EnumValue(staff->SpriteType) - 4]; + break; + case StaffType::Handyman: + widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox; + widgets[WIDX_CHECKBOX_1].text = STR_STAFF_OPTION_SWEEP_FOOTPATHS; + widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox; + widgets[WIDX_CHECKBOX_2].text = STR_STAFF_OPTION_WATER_GARDENS; + widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Checkbox; + widgets[WIDX_CHECKBOX_3].text = STR_STAFF_OPTION_EMPTY_LITTER; + widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Checkbox; + widgets[WIDX_CHECKBOX_4].text = STR_STAFF_OPTION_MOW_GRASS; + widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty; + widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty; + OptionsSetCheckboxValues(); + break; + case StaffType::Mechanic: + widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox; + widgets[WIDX_CHECKBOX_1].text = STR_INSPECT_RIDES; + widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox; + widgets[WIDX_CHECKBOX_2].text = STR_FIX_RIDES; + widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty; + widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty; + widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty; + widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty; + widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty; + OptionsSetCheckboxValues(); + break; + case StaffType::Security: + // Security guards don't have an options screen. + break; + case StaffType::Count: + break; + } } - switch (staff->AssignedStaffType) + void OptionsSetCheckboxValues() { - case StaffType::Entertainer: - widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Empty; - widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Empty; - widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty; - widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty; - widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Button; - widgets[WIDX_COSTUME_BOX].text = StaffCostumeNames[EnumValue(staff->SpriteType) - 4]; - break; - case StaffType::Handyman: - widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox; - widgets[WIDX_CHECKBOX_1].text = STR_STAFF_OPTION_SWEEP_FOOTPATHS; - widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox; - widgets[WIDX_CHECKBOX_2].text = STR_STAFF_OPTION_WATER_GARDENS; - widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Checkbox; - widgets[WIDX_CHECKBOX_3].text = STR_STAFF_OPTION_EMPTY_LITTER; - widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Checkbox; - widgets[WIDX_CHECKBOX_4].text = STR_STAFF_OPTION_MOW_GRASS; - widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty; - widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty; - OptionsSetCheckboxValues(); - break; - case StaffType::Mechanic: - widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox; - widgets[WIDX_CHECKBOX_1].text = STR_INSPECT_RIDES; - widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox; - widgets[WIDX_CHECKBOX_2].text = STR_FIX_RIDES; - widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty; - widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty; - widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty; - widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty; - widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty; - OptionsSetCheckboxValues(); - break; - case StaffType::Security: - // Security guards don't have an options screen. - break; - case StaffType::Count: - break; + SetCheckboxValue(WIDX_CHECKBOX_1, false); + SetCheckboxValue(WIDX_CHECKBOX_2, false); + SetCheckboxValue(WIDX_CHECKBOX_3, false); + SetCheckboxValue(WIDX_CHECKBOX_4, false); + + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + auto staffOrders = staff->StaffOrders; + + for (auto index = UtilBitScanForward(staffOrders); index != -1; index = UtilBitScanForward(staffOrders)) + { + staffOrders &= ~(1 << index); + SetCheckboxValue(WIDX_CHECKBOX_1 + index, true); + } } - } - void OptionsSetCheckboxValues() - { - SetCheckboxValue(WIDX_CHECKBOX_1, false); - SetCheckboxValue(WIDX_CHECKBOX_2, false); - SetCheckboxValue(WIDX_CHECKBOX_3, false); - SetCheckboxValue(WIDX_CHECKBOX_4, false); - - auto staff = GetStaff(); - if (staff == nullptr) + void OptionsResize() { - return; + min_width = 190; + max_width = 190; + min_height = 126; + max_height = 126; + + if (width < min_width) + { + width = min_width; + Invalidate(); + } + if (width > max_width) + { + Invalidate(); + width = max_width; + } + if (height < min_height) + { + height = min_height; + Invalidate(); + } + if (height > max_height) + { + Invalidate(); + height = max_height; + } } - auto staffOrders = staff->StaffOrders; - - for (auto index = UtilBitScanForward(staffOrders); index != -1; index = UtilBitScanForward(staffOrders)) + void OptionsUpdate() { - staffOrders &= ~(1 << index); - SetCheckboxValue(WIDX_CHECKBOX_1 + index, true); + frame_no++; + InvalidateWidget(WIDX_TAB_2); } - } - - void OptionsResize() - { - min_width = 190; - max_width = 190; - min_height = 126; - max_height = 126; - - if (width < min_width) - { - width = min_width; - Invalidate(); - } - if (width > max_width) - { - Invalidate(); - width = max_width; - } - if (height < min_height) - { - height = min_height; - Invalidate(); - } - if (height > max_height) - { - Invalidate(); - height = max_height; - } - } - - void OptionsUpdate() - { - frame_no++; - InvalidateWidget(WIDX_TAB_2); - } #pragma endregion #pragma region Statistics tab events - void StatsDraw(DrawPixelInfo& dpi) - { - auto staff = GetStaff(); - if (staff == nullptr) + void StatsDraw(DrawPixelInfo& dpi) { - return; - } - - auto screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_RESIZE].left + 4, widgets[WIDX_RESIZE].top + 4 }; - - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - auto ft = Formatter(); - ft.Add(GetStaffWage(staff->AssignedStaffType)); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_WAGES, ft); - screenCoords.y += LIST_ROW_HEIGHT; - } - - auto ft = Formatter(); - ft.Add(staff->GetHireDate()); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_EMPLOYED_FOR, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - switch (staff->AssignedStaffType) - { - case StaffType::Handyman: - ft = Formatter(); - ft.Add(staff->StaffLawnsMown); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LAWNS_MOWN, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(staff->StaffGardensWatered); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_GARDENS_WATERED, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(staff->StaffLitterSwept); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LITTER_SWEPT, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(staff->StaffBinsEmptied); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_BINS_EMPTIED, ft); - break; - case StaffType::Mechanic: - ft = Formatter(); - ft.Add(staff->StaffRidesInspected); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_INSPECTED, ft); - screenCoords.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(staff->StaffRidesFixed); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_FIXED, ft); - break; - case StaffType::Security: - ft = Formatter(); - ft.Add(staff->StaffVandalsStopped); - DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_VANDALS_STOPPED, ft); - break; - case StaffType::Entertainer: - case StaffType::Count: - break; - } - } - - void StatsResize() - { - min_width = 190; - max_width = 190; - min_height = 126; - max_height = 126; - - if (width < min_width) - { - width = min_width; - Invalidate(); - } - if (width > max_width) - { - Invalidate(); - width = max_width; - } - if (height < min_height) - { - height = min_height; - Invalidate(); - } - if (height > max_height) - { - Invalidate(); - height = max_height; - } - } - - void StatsUpdate() - { - frame_no++; - InvalidateWidget(WIDX_TAB_3); - - auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - - if (staff->WindowInvalidateFlags & PEEP_INVALIDATE_STAFF_STATS) - { - staff->WindowInvalidateFlags &= ~PEEP_INVALIDATE_STAFF_STATS; - Invalidate(); - } - } -#pragma endregion - void DisableWidgets() - { - const auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - - for (WidgetIndex widgetIndex = WIDX_TAB_1; widgets[widgetIndex].type != kWidgetsEnd.type; widgetIndex++) - { - SetWidgetDisabled(widgetIndex, false); - } - - if (staff->AssignedStaffType == StaffType::Security) - { - SetWidgetDisabled(WIDX_TAB_2, true); - } - - if (page == WINDOW_STAFF_OVERVIEW) - { - if (staff->CanBePickedUp()) + auto staff = GetStaff(); + if (staff == nullptr) { - SetWidgetDisabled(WIDX_PICKUP, false); - } - else - { - SetWidgetDisabled(WIDX_PICKUP, true); - } - - SetWidgetDisabled(WIDX_FIRE, staff->State == PeepState::Fixing || staff->State == PeepState::Inspecting); - } - } - - void CancelTools() - { - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) - { - if (number == gCurrentToolWidget.window_number && classification == gCurrentToolWidget.window_classification) - ToolCancel(); - } - } - - void SetPage(int32_t pageNum) - { - CancelTools(); - - int32_t listen = 0; - if (page == WINDOW_STAFF_OVERVIEW && viewport != nullptr) - { - if (!(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) - listen = 1; - } - - page = pageNum; - frame_no = 0; - pressed_widgets = 0; - hold_down_widgets = 0; - widgets = window_staff_page_widgets[page]; - - RemoveViewport(); - - Invalidate(); - OnResize(); - OnPrepareDraw(); - InitScrollWidgets(); - ViewportInit(); - Invalidate(); - - if (listen && viewport != nullptr) - viewport->flags |= VIEWPORT_FLAG_SOUND_ON; - } - - void SetPressedTab() - { - for (int32_t i = 0; i < WINDOW_STAFF_PAGE_COUNT; i++) - SetWidgetPressed((WIDX_TAB_1 + i), false); - SetWidgetPressed(WIDX_TAB_1 + page, true); - } - - void SetOrder(int32_t orderId) - { - auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - - uint8_t newOrders = staff->StaffOrders ^ (1 << orderId); - auto staffSetOrdersAction = StaffSetOrdersAction(EntityId::FromUnderlying(number), newOrders); - GameActions::Execute(&staffSetOrdersAction); - } - - void ViewportInit() - { - if (page != WINDOW_STAFF_OVERVIEW) - return; - - auto staff = GetStaff(); - if (staff == nullptr) - { - return; - } - - std::optional tempFocus; - if (staff->State != PeepState::Picked) - { - tempFocus = Focus(staff->Id); - } - - uint16_t viewport_flags; - - if (viewport != nullptr) - { - if (tempFocus == focus) return; + } - viewport_flags = viewport->flags; - RemoveViewport(); - } - else - { - viewport_flags = 0; - if (gConfigGeneral.AlwaysShowGridlines) - viewport_flags |= VIEWPORT_FLAG_GRIDLINES; - } + auto screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_RESIZE].left + 4, widgets[WIDX_RESIZE].top + 4 }; - OnPrepareDraw(); - - focus = tempFocus; - - if (staff->State != PeepState::Picked) - { - if (viewport == nullptr) + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) { - const auto& viewWidget = widgets[WIDX_VIEWPORT]; + auto ft = Formatter(); + ft.Add(GetStaffWage(staff->AssignedStaffType)); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_WAGES, ft); + screenCoords.y += LIST_ROW_HEIGHT; + } - auto screenPos = ScreenCoordsXY{ viewWidget.left + 1 + windowPos.x, viewWidget.top + 1 + windowPos.y }; - int32_t viewportWidth = viewWidget.width() - 1; - int32_t viewportHeight = viewWidget.height() - 1; + auto ft = Formatter(); + ft.Add(staff->GetHireDate()); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_EMPLOYED_FOR, ft); + screenCoords.y += LIST_ROW_HEIGHT; - ViewportCreate(this, screenPos, viewportWidth, viewportHeight, focus.value()); - flags |= WF_NO_SCROLLING; + switch (staff->AssignedStaffType) + { + case StaffType::Handyman: + ft = Formatter(); + ft.Add(staff->StaffLawnsMown); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LAWNS_MOWN, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(staff->StaffGardensWatered); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_GARDENS_WATERED, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(staff->StaffLitterSwept); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LITTER_SWEPT, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(staff->StaffBinsEmptied); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_BINS_EMPTIED, ft); + break; + case StaffType::Mechanic: + ft = Formatter(); + ft.Add(staff->StaffRidesInspected); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_INSPECTED, ft); + screenCoords.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(staff->StaffRidesFixed); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_FIXED, ft); + break; + case StaffType::Security: + ft = Formatter(); + ft.Add(staff->StaffVandalsStopped); + DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_VANDALS_STOPPED, ft); + break; + case StaffType::Entertainer: + case StaffType::Count: + break; + } + } + + void StatsResize() + { + min_width = 190; + max_width = 190; + min_height = 126; + max_height = 126; + + if (width < min_width) + { + width = min_width; + Invalidate(); + } + if (width > max_width) + { + Invalidate(); + width = max_width; + } + if (height < min_height) + { + height = min_height; + Invalidate(); + } + if (height > max_height) + { + Invalidate(); + height = max_height; + } + } + + void StatsUpdate() + { + frame_no++; + InvalidateWidget(WIDX_TAB_3); + + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + if (staff->WindowInvalidateFlags & PEEP_INVALIDATE_STAFF_STATS) + { + staff->WindowInvalidateFlags &= ~PEEP_INVALIDATE_STAFF_STATS; Invalidate(); } } - - if (viewport != nullptr) - viewport->flags = viewport_flags; - Invalidate(); - } - - void ShowLocateDropdown(Widget* widget) - { - 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; - } - - void FollowPeep() - { - WindowBase* main = WindowGetMain(); - WindowFollowSprite(*main, EntityId::FromUnderlying(number)); - } - - void DrawTabImages(DrawPixelInfo& dpi) - { - DrawOverviewTabImage(dpi); - DrawTabImage(dpi, WINDOW_STAFF_OPTIONS, SPR_TAB_STAFF_OPTIONS_0); - DrawTabImage(dpi, WINDOW_STAFF_STATISTICS, SPR_TAB_STATS_0); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t p, int32_t baseImageId) - { - WidgetIndex widgetIndex = WIDX_TAB_1 + p; - Widget* widget = &widgets[widgetIndex]; - - auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; - - if (!IsWidgetDisabled(widgetIndex)) +#pragma endregion + void DisableWidgets() { - if (page == p) + const auto staff = GetStaff(); + if (staff == nullptr) { - int32_t frame = frame_no / TabAnimationDivisor[page - 1]; - baseImageId += (frame % TabAnimationFrames); + return; } - // Draw normal, enabled sprite. - GfxDrawSprite(dpi, ImageId(baseImageId), screenCoords); + for (WidgetIndex widgetIndex = WIDX_TAB_1; widgets[widgetIndex].type != kWidgetsEnd.type; widgetIndex++) + { + SetWidgetDisabled(widgetIndex, false); + } + + if (staff->AssignedStaffType == StaffType::Security) + { + SetWidgetDisabled(WIDX_TAB_2, true); + } + + if (page == WINDOW_STAFF_OVERVIEW) + { + if (staff->CanBePickedUp()) + { + SetWidgetDisabled(WIDX_PICKUP, false); + } + else + { + SetWidgetDisabled(WIDX_PICKUP, true); + } + + SetWidgetDisabled(WIDX_FIRE, staff->State == PeepState::Fixing || staff->State == PeepState::Inspecting); + } } - } - Staff* GetStaff() - { - return GetEntity(EntityId::FromUnderlying(number)); - } + void CancelTools() + { + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + { + if (number == gCurrentToolWidget.window_number && classification == gCurrentToolWidget.window_classification) + ToolCancel(); + } + } - static constexpr int32_t TabAnimationDivisor[] = { - 2, // WINDOW_STAFF_OPTIONS, - 4, // WINDOW_STAFF_STATISTICS, + void SetPage(int32_t pageNum) + { + CancelTools(); + + int32_t listen = 0; + if (page == WINDOW_STAFF_OVERVIEW && viewport != nullptr) + { + if (!(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) + listen = 1; + } + + page = pageNum; + frame_no = 0; + pressed_widgets = 0; + hold_down_widgets = 0; + widgets = window_staff_page_widgets[page]; + + RemoveViewport(); + + Invalidate(); + OnResize(); + OnPrepareDraw(); + InitScrollWidgets(); + ViewportInit(); + Invalidate(); + + if (listen && viewport != nullptr) + viewport->flags |= VIEWPORT_FLAG_SOUND_ON; + } + + void SetPressedTab() + { + for (int32_t i = 0; i < WINDOW_STAFF_PAGE_COUNT; i++) + SetWidgetPressed((WIDX_TAB_1 + i), false); + SetWidgetPressed(WIDX_TAB_1 + page, true); + } + + void SetOrder(int32_t orderId) + { + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + uint8_t newOrders = staff->StaffOrders ^ (1 << orderId); + auto staffSetOrdersAction = StaffSetOrdersAction(EntityId::FromUnderlying(number), newOrders); + GameActions::Execute(&staffSetOrdersAction); + } + + void ViewportInit() + { + if (page != WINDOW_STAFF_OVERVIEW) + return; + + auto staff = GetStaff(); + if (staff == nullptr) + { + return; + } + + std::optional tempFocus; + if (staff->State != PeepState::Picked) + { + tempFocus = Focus(staff->Id); + } + + uint16_t viewport_flags; + + if (viewport != nullptr) + { + if (tempFocus == focus) + return; + + viewport_flags = viewport->flags; + RemoveViewport(); + } + else + { + viewport_flags = 0; + if (gConfigGeneral.AlwaysShowGridlines) + viewport_flags |= VIEWPORT_FLAG_GRIDLINES; + } + + OnPrepareDraw(); + + focus = tempFocus; + + if (staff->State != PeepState::Picked) + { + if (viewport == nullptr) + { + const auto& viewWidget = widgets[WIDX_VIEWPORT]; + + auto screenPos = ScreenCoordsXY{ viewWidget.left + 1 + windowPos.x, viewWidget.top + 1 + windowPos.y }; + int32_t viewportWidth = viewWidget.width() - 1; + int32_t viewportHeight = viewWidget.height() - 1; + + ViewportCreate(this, screenPos, viewportWidth, viewportHeight, focus.value()); + flags |= WF_NO_SCROLLING; + Invalidate(); + } + } + + if (viewport != nullptr) + viewport->flags = viewport_flags; + Invalidate(); + } + + void ShowLocateDropdown(Widget* widget) + { + 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; + } + + void FollowPeep() + { + WindowBase* main = WindowGetMain(); + WindowFollowSprite(*main, EntityId::FromUnderlying(number)); + } + + void DrawTabImages(DrawPixelInfo& dpi) + { + DrawOverviewTabImage(dpi); + DrawTabImage(dpi, WINDOW_STAFF_OPTIONS, SPR_TAB_STAFF_OPTIONS_0); + DrawTabImage(dpi, WINDOW_STAFF_STATISTICS, SPR_TAB_STATS_0); + } + + void DrawTabImage(DrawPixelInfo& dpi, int32_t p, int32_t baseImageId) + { + WidgetIndex widgetIndex = WIDX_TAB_1 + p; + Widget* widget = &widgets[widgetIndex]; + + auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top }; + + if (!IsWidgetDisabled(widgetIndex)) + { + if (page == p) + { + int32_t frame = frame_no / TabAnimationDivisor[page - 1]; + baseImageId += (frame % TabAnimationFrames); + } + + // Draw normal, enabled sprite. + GfxDrawSprite(dpi, ImageId(baseImageId), screenCoords); + } + } + + Staff* GetStaff() + { + return GetEntity(EntityId::FromUnderlying(number)); + } + + static constexpr int32_t TabAnimationDivisor[] = { + 2, // WINDOW_STAFF_OPTIONS, + 4, // WINDOW_STAFF_STATISTICS, + }; + + static constexpr int32_t TabAnimationFrames = 7; }; - static constexpr int32_t TabAnimationFrames = 7; -}; + WindowBase* WindowStaffOpen(Peep* peep) + { + auto w = static_cast(WindowBringToFrontByNumber(WindowClass::Peep, peep->Id.ToUnderlying())); -WindowBase* WindowStaffOpen(Peep* peep) -{ - auto w = static_cast(WindowBringToFrontByNumber(WindowClass::Peep, peep->Id.ToUnderlying())); + if (w != nullptr) + return w; + + w = WindowCreate(WindowClass::Peep, WW, WH, WF_10 | WF_RESIZABLE); + + if (w == nullptr) + return nullptr; + + if (w != nullptr) + w->Initialise(peep->Id); - if (w != nullptr) return w; - - w = WindowCreate(WindowClass::Peep, WW, WH, WF_10 | WF_RESIZABLE); - - if (w == nullptr) - return nullptr; - - if (w != nullptr) - w->Initialise(peep->Id); - - return w; -} + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/StaffFirePrompt.cpp b/src/openrct2-ui/windows/StaffFirePrompt.cpp index 7a5529026d..637c9f7b71 100644 --- a/src/openrct2-ui/windows/StaffFirePrompt.cpp +++ b/src/openrct2-ui/windows/StaffFirePrompt.cpp @@ -18,11 +18,13 @@ #include #include -static constexpr StringId WINDOW_TITLE = STR_SACK_STAFF; -static constexpr int32_t WW = 200; -static constexpr int32_t WH = 100; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_SACK_STAFF; + static constexpr int32_t WW = 200; + static constexpr int32_t WH = 100; -// clang-format off + // clang-format off enum WindowStaffFireWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -39,62 +41,63 @@ static Widget _staffFireWidgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class StaffFirePromptWindow final : public Window -{ -public: - void SetWindowNumber(rct_windownumber windownumber) + class StaffFirePromptWindow final : public Window { - number = windownumber; - } - - void OnOpen() override - { - widgets = _staffFireWidgets; - WindowInitScrollWidgets(*this); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void SetWindowNumber(rct_windownumber windownumber) { - case WIDX_YES: - { - auto staffFireAction = StaffFireAction(EntityId::FromUnderlying(number)); - GameActions::Execute(&staffFireAction); - break; - } - case WIDX_CLOSE: - case WIDX_CANCEL: - Close(); - break; + number = windownumber; } - } - void OnDraw(DrawPixelInfo& dpi) override + void OnOpen() override + { + widgets = _staffFireWidgets; + WindowInitScrollWidgets(*this); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_YES: + { + auto staffFireAction = StaffFireAction(EntityId::FromUnderlying(number)); + GameActions::Execute(&staffFireAction); + break; + } + case WIDX_CLOSE: + case WIDX_CANCEL: + Close(); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + Peep* peep = GetEntity(EntityId::FromUnderlying(number)); + auto ft = Formatter(); + peep->FormatNameTo(ft); + + ScreenCoordsXY textCoords(windowPos + ScreenCoordsXY{ WW / 2, (WH / 2) - 3 }); + DrawTextWrapped(dpi, textCoords, WW - 4, STR_FIRE_STAFF_ID, ft, { TextAlignment::CENTRE }); + } + + void OnResize() override + { + ResizeFrame(); + } + }; + + WindowBase* WindowStaffFirePromptOpen(Peep* peep) { - DrawWidgets(dpi); - - Peep* peep = GetEntity(EntityId::FromUnderlying(number)); - auto ft = Formatter(); - peep->FormatNameTo(ft); - - ScreenCoordsXY textCoords(windowPos + ScreenCoordsXY{ WW / 2, (WH / 2) - 3 }); - DrawTextWrapped(dpi, textCoords, WW - 4, STR_FIRE_STAFF_ID, ft, { TextAlignment::CENTRE }); + // Check if the confirm window already exists + auto* window = WindowFocusOrCreate( + WindowClass::FirePrompt, WW, WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); + window->SetWindowNumber(peep->Id.ToUnderlying()); + return window; } - - void OnResize() override - { - ResizeFrame(); - } -}; - -WindowBase* WindowStaffFirePromptOpen(Peep* peep) -{ - // Check if the confirm window already exists - auto* window = WindowFocusOrCreate( - WindowClass::FirePrompt, WW, WH, WF_CENTRE_SCREEN | WF_TRANSPARENT); - window->SetWindowNumber(peep->Id.ToUnderlying()); - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/StaffList.cpp b/src/openrct2-ui/windows/StaffList.cpp index 7003384afc..0ddf7019c4 100644 --- a/src/openrct2-ui/windows/StaffList.cpp +++ b/src/openrct2-ui/windows/StaffList.cpp @@ -36,41 +36,41 @@ #include #include -using namespace OpenRCT2; - -enum +namespace OpenRCT2::Ui::Windows { - WINDOW_STAFF_LIST_TAB_HANDYMEN, - WINDOW_STAFF_LIST_TAB_MECHANICS, - WINDOW_STAFF_LIST_TAB_SECURITY, - WINDOW_STAFF_LIST_TAB_ENTERTAINERS -}; + enum + { + WINDOW_STAFF_LIST_TAB_HANDYMEN, + WINDOW_STAFF_LIST_TAB_MECHANICS, + WINDOW_STAFF_LIST_TAB_SECURITY, + WINDOW_STAFF_LIST_TAB_ENTERTAINERS + }; -enum WindowStaffListWidgetIdx -{ - WIDX_STAFF_LIST_BACKGROUND, - WIDX_STAFF_LIST_TITLE, - WIDX_STAFF_LIST_CLOSE, - WIDX_STAFF_LIST_TAB_CONTENT_PANEL, - WIDX_STAFF_LIST_HANDYMEN_TAB, - WIDX_STAFF_LIST_MECHANICS_TAB, - WIDX_STAFF_LIST_SECURITY_TAB, - WIDX_STAFF_LIST_ENTERTAINERS_TAB, - WIDX_STAFF_LIST_LIST, - WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER, - WIDX_STAFF_LIST_HIRE_BUTTON, - WIDX_STAFF_LIST_QUICK_FIRE, - WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON, - WIDX_STAFF_LIST_MAP, -}; + enum WindowStaffListWidgetIdx + { + WIDX_STAFF_LIST_BACKGROUND, + WIDX_STAFF_LIST_TITLE, + WIDX_STAFF_LIST_CLOSE, + WIDX_STAFF_LIST_TAB_CONTENT_PANEL, + WIDX_STAFF_LIST_HANDYMEN_TAB, + WIDX_STAFF_LIST_MECHANICS_TAB, + WIDX_STAFF_LIST_SECURITY_TAB, + WIDX_STAFF_LIST_ENTERTAINERS_TAB, + WIDX_STAFF_LIST_LIST, + WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER, + WIDX_STAFF_LIST_HIRE_BUTTON, + WIDX_STAFF_LIST_QUICK_FIRE, + WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON, + WIDX_STAFF_LIST_MAP, + }; -static constexpr StringId WINDOW_TITLE = STR_STAFF; -static constexpr int32_t WW = 320; -static constexpr int32_t WH = 270; -constexpr int32_t MAX_WW = 500; -constexpr int32_t MAX_WH = 450; + static constexpr StringId WINDOW_TITLE = STR_STAFF; + static constexpr int32_t WW = 320; + static constexpr int32_t WH = 270; + constexpr int32_t MAX_WW = 500; + constexpr int32_t MAX_WH = 450; -// clang-format off + // clang-format off static Widget _staffListWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget({ 0, 43}, { WW, WH - 43}, WindowWidgetType::Resize, WindowColour::Secondary ), // tab content panel @@ -86,677 +86,681 @@ static Widget _staffListWidgets[] = { MakeWidget({291, 46}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_MAP), STR_SHOW_STAFF_ON_MAP_TIP ), // show staff on map button kWidgetsEnd, }; -// clang-format on + // clang-format on -class StaffListWindow final : public Window -{ -private: - struct StaffNamingConvention + class StaffListWindow final : public Window { - StringId Plural; - StringId Singular; - StringId ActionHire; - }; - - struct StaffEntry - { - EntityId Id; - u8string Name; - }; - - std::vector _staffList; - bool _quickFireMode{}; - std::optional _highlightedIndex{}; - int32_t _selectedTab{}; - uint32_t _tabAnimationIndex{}; - -public: - void OnOpen() override - { - widgets = _staffListWidgets; - WindowInitScrollWidgets(*this); - - widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].type = WindowWidgetType::Empty; - min_width = WW; - min_height = WH; - max_width = MAX_WW; - max_height = MAX_WH; - - RefreshList(); - } - - void OnClose() override - { - CancelTools(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + private: + struct StaffNamingConvention { - case WIDX_STAFF_LIST_CLOSE: - Close(); - break; - case WIDX_STAFF_LIST_HIRE_BUTTON: + StringId Plural; + StringId Singular; + StringId ActionHire; + }; + + struct StaffEntry + { + EntityId Id; + u8string Name; + }; + + std::vector _staffList; + bool _quickFireMode{}; + std::optional _highlightedIndex{}; + int32_t _selectedTab{}; + uint32_t _tabAnimationIndex{}; + + public: + void OnOpen() override + { + widgets = _staffListWidgets; + WindowInitScrollWidgets(*this); + + widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].type = WindowWidgetType::Empty; + min_width = WW; + min_height = WH; + max_width = MAX_WW; + max_height = MAX_WH; + + RefreshList(); + } + + void OnClose() override + { + CancelTools(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) { - auto staffType = GetSelectedStaffType(); - auto costume = EntertainerCostume::Count; - if (staffType == StaffType::Entertainer) + case WIDX_STAFF_LIST_CLOSE: + Close(); + break; + case WIDX_STAFF_LIST_HIRE_BUTTON: { - costume = GetRandomEntertainerCostume(); - } - HireNewMember(staffType, costume); - break; - } - case WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON: - if (!ToolSet(*this, WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON, Tool::Crosshair)) - { - ShowGridlines(); - SetPatrolAreaToRender(GetSelectedStaffType()); - GfxInvalidateScreen(); - } - break; - case WIDX_STAFF_LIST_MAP: - ContextOpenWindow(WindowClass::Map); - break; - case WIDX_STAFF_LIST_QUICK_FIRE: - _quickFireMode = !_quickFireMode; - Invalidate(); - break; - } - } - - void OnResize() override - { - min_width = WW; - min_height = WH; - if (width < min_width) - { - width = min_width; - Invalidate(); - } - if (height < min_height) - { - height = min_height; - Invalidate(); - } - ResizeFrameWithPage(); - } - - void OnUpdate() override - { - _tabAnimationIndex++; - if (_tabAnimationIndex >= 24) - { - _tabAnimationIndex = 0; - } - else - { - InvalidateWidget(WIDX_STAFF_LIST_HANDYMEN_TAB + _selectedTab); - - // Enable highlighting of these staff members in map window - if (WindowFindByClass(WindowClass::Map) != nullptr) - { - gWindowMapFlashingFlags |= MapFlashingFlags::StaffListOpen; - for (auto peep : EntityList()) - { - EntitySetFlashing(peep, false); - if (peep->AssignedStaffType == GetSelectedStaffType()) + auto staffType = GetSelectedStaffType(); + auto costume = EntertainerCostume::Count; + if (staffType == StaffType::Entertainer) { - EntitySetFlashing(peep, true); + costume = GetRandomEntertainerCostume(); } + HireNewMember(staffType, costume); + break; } - } - } - - // Note this may be slow if number of staff increases a large amount. - // See GuestList for fix (more intents) if required. - RefreshList(); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_STAFF_LIST_HANDYMEN_TAB: - case WIDX_STAFF_LIST_MECHANICS_TAB: - case WIDX_STAFF_LIST_SECURITY_TAB: - case WIDX_STAFF_LIST_ENTERTAINERS_TAB: - { - auto newSelectedTab = widgetIndex - WIDX_STAFF_LIST_HANDYMEN_TAB; - if (_selectedTab != newSelectedTab) - { - _selectedTab = static_cast(newSelectedTab); + case WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON: + if (!ToolSet(*this, WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON, Tool::Crosshair)) + { + ShowGridlines(); + SetPatrolAreaToRender(GetSelectedStaffType()); + GfxInvalidateScreen(); + } + break; + case WIDX_STAFF_LIST_MAP: + ContextOpenWindow(WindowClass::Map); + break; + case WIDX_STAFF_LIST_QUICK_FIRE: + _quickFireMode = !_quickFireMode; Invalidate(); - scrolls[0].v_top = 0; - CancelTools(); - } - break; + break; } - case WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER: - WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], StaffGetColour(GetSelectedStaffType())); - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (dropdownIndex == -1) - { - return; - } - if (widgetIndex == WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER) - { - auto action = StaffSetColourAction(GetSelectedStaffType(), ColourDropDownIndexToColour(dropdownIndex)); - GameActions::Execute(&action); - } - } - - void OnPrepareDraw() override - { - // Set selected tab - SetWidgetPressed(WIDX_STAFF_LIST_HANDYMEN_TAB, false); - SetWidgetPressed(WIDX_STAFF_LIST_MECHANICS_TAB, false); - SetWidgetPressed(WIDX_STAFF_LIST_SECURITY_TAB, false); - SetWidgetPressed(WIDX_STAFF_LIST_ENTERTAINERS_TAB, false); - SetWidgetPressed(_selectedTab + WIDX_STAFF_LIST_HANDYMEN_TAB, true); - - widgets[WIDX_STAFF_LIST_HIRE_BUTTON].text = GetStaffNamingConvention(GetSelectedStaffType()).ActionHire; - widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].type = WindowWidgetType::Empty; - - if (GetSelectedStaffType() != StaffType::Entertainer) - { - widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].type = WindowWidgetType::ColourBtn; - widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].image = GetColourButtonImage(StaffGetColour(GetSelectedStaffType())); - } - SetWidgetPressed(WIDX_STAFF_LIST_QUICK_FIRE, _quickFireMode); - - ResizeFrameWithPage(); - widgets[WIDX_STAFF_LIST_LIST].right = width - 4; - widgets[WIDX_STAFF_LIST_LIST].bottom = height - 15; - widgets[WIDX_STAFF_LIST_QUICK_FIRE].left = width - 77; - widgets[WIDX_STAFF_LIST_QUICK_FIRE].right = width - 54; - widgets[WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON].left = width - 53; - widgets[WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON].right = width - 30; - widgets[WIDX_STAFF_LIST_MAP].left = width - 29; - widgets[WIDX_STAFF_LIST_MAP].right = width - 6; - widgets[WIDX_STAFF_LIST_HIRE_BUTTON].left = width - 155; - widgets[WIDX_STAFF_LIST_HIRE_BUTTON].right = width - 11; - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - DrawTabImages(dpi); - - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - auto ft = Formatter(); - ft.Add(GetStaffWage(GetSelectedStaffType())); - DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ width - 155, 32 }, STR_COST_PER_MONTH, ft); } - if (GetSelectedStaffType() != StaffType::Entertainer) + void OnResize() override { - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].top + 1 }, - STR_UNIFORM_COLOUR); - } - - auto namingConvention = GetStaffNamingConvention(GetSelectedStaffType()); - auto staffTypeStringId = _staffList.size() == 1 ? namingConvention.Singular : namingConvention.Plural; - - auto ft = Formatter(); - ft.Add(_staffList.size()); - ft.Add(staffTypeStringId); - - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_STAFF_LIST_LIST].bottom + 2 }, STR_STAFF_LIST_COUNTER, ft); - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - if (_highlightedIndex) - { - _highlightedIndex = {}; - Invalidate(); - } - - auto scrollHeight = static_cast(_staffList.size()) * SCROLLABLE_ROW_HEIGHT; - auto i = scrollHeight - widgets[WIDX_STAFF_LIST_LIST].bottom + widgets[WIDX_STAFF_LIST_LIST].top + 21; - if (i < 0) - i = 0; - if (i < scrolls[0].v_top) - { - scrolls[0].v_top = i; - Invalidate(); - } - - auto scrollWidth = widgets[WIDX_STAFF_LIST_LIST].width() - 15; - return { scrollWidth, scrollHeight }; - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - auto i = static_cast(screenCoords.y / SCROLLABLE_ROW_HEIGHT); - if (i != _highlightedIndex) - { - _highlightedIndex = static_cast(i); - Invalidate(); - } - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - int32_t i = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - for (const auto& entry : _staffList) - { - if (i == 0) + min_width = WW; + min_height = WH; + if (width < min_width) { - if (_quickFireMode) + width = min_width; + Invalidate(); + } + if (height < min_height) + { + height = min_height; + Invalidate(); + } + ResizeFrameWithPage(); + } + + void OnUpdate() override + { + _tabAnimationIndex++; + if (_tabAnimationIndex >= 24) + { + _tabAnimationIndex = 0; + } + else + { + InvalidateWidget(WIDX_STAFF_LIST_HANDYMEN_TAB + _selectedTab); + + // Enable highlighting of these staff members in map window + if (WindowFindByClass(WindowClass::Map) != nullptr) { - auto staffFireAction = StaffFireAction(entry.Id); - GameActions::Execute(&staffFireAction); - } - else - { - auto peep = GetEntity(entry.Id); - if (peep != nullptr) + gWindowMapFlashingFlags |= MapFlashingFlags::StaffListOpen; + for (auto peep : EntityList()) { - auto intent = Intent(WindowClass::Peep); - intent.PutExtra(INTENT_EXTRA_PEEP, peep); - ContextOpenIntent(&intent); - } - } - break; - } - - i--; - } - } - - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; - GfxFillRect( - dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - - // How much space do we have for the name and action columns? (Discount scroll area and icons.) - const int32_t nonIconSpace = widgets[WIDX_STAFF_LIST_LIST].width() - 15 - 68; - const int32_t nameColumnSize = nonIconSpace * 0.42; - const int32_t actionColumnSize = nonIconSpace * 0.58; - const int32_t actionOffset = widgets[WIDX_STAFF_LIST_LIST].right - actionColumnSize - 15; - - auto y = 0; - size_t i = 0; - for (const auto& entry : _staffList) - { - if (y > dpi.y + dpi.height) - { - break; - } - - if (y + 11 >= dpi.y) - { - const auto* peep = GetEntity(entry.Id); - if (peep == nullptr) - { - continue; - } - int32_t format = (_quickFireMode ? STR_RED_STRINGID : STR_BLACK_STRING); - - if (i == _highlightedIndex) - { - GfxFilterRect(dpi, { 0, y, 800, y + (SCROLLABLE_ROW_HEIGHT - 1) }, FilterPaletteID::PaletteDarken1); - format = (_quickFireMode ? STR_LIGHTPINK_STRINGID : STR_WINDOW_COLOUR_2_STRINGID); - } - - auto ft = Formatter(); - peep->FormatNameTo(ft); - DrawTextEllipsised(dpi, { 0, y }, nameColumnSize, format, ft); - - ft = Formatter(); - peep->FormatActionTo(ft); - DrawTextEllipsised(dpi, { actionOffset, y }, actionColumnSize, format, ft); - - // True if a patrol path is set for the worker - if (peep->HasPatrolArea()) - { - GfxDrawSprite(dpi, ImageId(SPR_STAFF_PATROL_PATH), { nameColumnSize + 5, y }); - } - - auto staffOrderIcon_x = nameColumnSize + 20; - if (peep->AssignedStaffType != StaffType::Entertainer) - { - auto staffOrders = peep->StaffOrders; - auto staffOrderSprite = GetStaffOrderBaseSprite(GetSelectedStaffType()); - - while (staffOrders != 0) - { - if (staffOrders & 1) + EntitySetFlashing(peep, false); + if (peep->AssignedStaffType == GetSelectedStaffType()) { - GfxDrawSprite(dpi, ImageId(staffOrderSprite), { staffOrderIcon_x, y }); + EntitySetFlashing(peep, true); } - staffOrders = staffOrders >> 1; - staffOrderIcon_x += 9; - // TODO: Remove sprite ID addition - staffOrderSprite++; } } + } + + // Note this may be slow if number of staff increases a large amount. + // See GuestList for fix (more intents) if required. + RefreshList(); + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_STAFF_LIST_HANDYMEN_TAB: + case WIDX_STAFF_LIST_MECHANICS_TAB: + case WIDX_STAFF_LIST_SECURITY_TAB: + case WIDX_STAFF_LIST_ENTERTAINERS_TAB: + { + auto newSelectedTab = widgetIndex - WIDX_STAFF_LIST_HANDYMEN_TAB; + if (_selectedTab != newSelectedTab) + { + _selectedTab = static_cast(newSelectedTab); + Invalidate(); + scrolls[0].v_top = 0; + CancelTools(); + } + break; + } + case WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER: + WindowDropdownShowColour(this, &widgets[widgetIndex], colours[1], StaffGetColour(GetSelectedStaffType())); + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (dropdownIndex == -1) + { + return; + } + if (widgetIndex == WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER) + { + auto action = StaffSetColourAction(GetSelectedStaffType(), ColourDropDownIndexToColour(dropdownIndex)); + GameActions::Execute(&action); + } + } + + void OnPrepareDraw() override + { + // Set selected tab + SetWidgetPressed(WIDX_STAFF_LIST_HANDYMEN_TAB, false); + SetWidgetPressed(WIDX_STAFF_LIST_MECHANICS_TAB, false); + SetWidgetPressed(WIDX_STAFF_LIST_SECURITY_TAB, false); + SetWidgetPressed(WIDX_STAFF_LIST_ENTERTAINERS_TAB, false); + SetWidgetPressed(_selectedTab + WIDX_STAFF_LIST_HANDYMEN_TAB, true); + + widgets[WIDX_STAFF_LIST_HIRE_BUTTON].text = GetStaffNamingConvention(GetSelectedStaffType()).ActionHire; + widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].type = WindowWidgetType::Empty; + + if (GetSelectedStaffType() != StaffType::Entertainer) + { + widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].type = WindowWidgetType::ColourBtn; + widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].image = GetColourButtonImage( + StaffGetColour(GetSelectedStaffType())); + } + SetWidgetPressed(WIDX_STAFF_LIST_QUICK_FIRE, _quickFireMode); + + ResizeFrameWithPage(); + widgets[WIDX_STAFF_LIST_LIST].right = width - 4; + widgets[WIDX_STAFF_LIST_LIST].bottom = height - 15; + widgets[WIDX_STAFF_LIST_QUICK_FIRE].left = width - 77; + widgets[WIDX_STAFF_LIST_QUICK_FIRE].right = width - 54; + widgets[WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON].left = width - 53; + widgets[WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON].right = width - 30; + widgets[WIDX_STAFF_LIST_MAP].left = width - 29; + widgets[WIDX_STAFF_LIST_MAP].right = width - 6; + widgets[WIDX_STAFF_LIST_HIRE_BUTTON].left = width - 155; + widgets[WIDX_STAFF_LIST_HIRE_BUTTON].right = width - 11; + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + auto ft = Formatter(); + ft.Add(GetStaffWage(GetSelectedStaffType())); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ width - 155, 32 }, STR_COST_PER_MONTH, ft); + } + + if (GetSelectedStaffType() != StaffType::Entertainer) + { + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 6, widgets[WIDX_STAFF_LIST_UNIFORM_COLOUR_PICKER].top + 1 }, + STR_UNIFORM_COLOUR); + } + + auto namingConvention = GetStaffNamingConvention(GetSelectedStaffType()); + auto staffTypeStringId = _staffList.size() == 1 ? namingConvention.Singular : namingConvention.Plural; + + auto ft = Formatter(); + ft.Add(_staffList.size()); + ft.Add(staffTypeStringId); + + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_STAFF_LIST_LIST].bottom + 2 }, STR_STAFF_LIST_COUNTER, ft); + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + if (_highlightedIndex) + { + _highlightedIndex = {}; + Invalidate(); + } + + auto scrollHeight = static_cast(_staffList.size()) * SCROLLABLE_ROW_HEIGHT; + auto i = scrollHeight - widgets[WIDX_STAFF_LIST_LIST].bottom + widgets[WIDX_STAFF_LIST_LIST].top + 21; + if (i < 0) + i = 0; + if (i < scrolls[0].v_top) + { + scrolls[0].v_top = i; + Invalidate(); + } + + auto scrollWidth = widgets[WIDX_STAFF_LIST_LIST].width() - 15; + return { scrollWidth, scrollHeight }; + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + auto i = static_cast(screenCoords.y / SCROLLABLE_ROW_HEIGHT); + if (i != _highlightedIndex) + { + _highlightedIndex = static_cast(i); + Invalidate(); + } + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int32_t i = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + for (const auto& entry : _staffList) + { + if (i == 0) + { + if (_quickFireMode) + { + auto staffFireAction = StaffFireAction(entry.Id); + GameActions::Execute(&staffFireAction); + } + else + { + auto peep = GetEntity(entry.Id); + if (peep != nullptr) + { + auto intent = Intent(WindowClass::Peep); + intent.PutExtra(INTENT_EXTRA_PEEP, peep); + ContextOpenIntent(&intent); + } + } + break; + } + + i--; + } + } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; + GfxFillRect( + dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, + ColourMapA[colours[1]].mid_light); + + // How much space do we have for the name and action columns? (Discount scroll area and icons.) + const int32_t nonIconSpace = widgets[WIDX_STAFF_LIST_LIST].width() - 15 - 68; + const int32_t nameColumnSize = nonIconSpace * 0.42; + const int32_t actionColumnSize = nonIconSpace * 0.58; + const int32_t actionOffset = widgets[WIDX_STAFF_LIST_LIST].right - actionColumnSize - 15; + + auto y = 0; + size_t i = 0; + for (const auto& entry : _staffList) + { + if (y > dpi.y + dpi.height) + { + break; + } + + if (y + 11 >= dpi.y) + { + const auto* peep = GetEntity(entry.Id); + if (peep == nullptr) + { + continue; + } + int32_t format = (_quickFireMode ? STR_RED_STRINGID : STR_BLACK_STRING); + + if (i == _highlightedIndex) + { + GfxFilterRect(dpi, { 0, y, 800, y + (SCROLLABLE_ROW_HEIGHT - 1) }, FilterPaletteID::PaletteDarken1); + format = (_quickFireMode ? STR_LIGHTPINK_STRINGID : STR_WINDOW_COLOUR_2_STRINGID); + } + + auto ft = Formatter(); + peep->FormatNameTo(ft); + DrawTextEllipsised(dpi, { 0, y }, nameColumnSize, format, ft); + + ft = Formatter(); + peep->FormatActionTo(ft); + DrawTextEllipsised(dpi, { actionOffset, y }, actionColumnSize, format, ft); + + // True if a patrol path is set for the worker + if (peep->HasPatrolArea()) + { + GfxDrawSprite(dpi, ImageId(SPR_STAFF_PATROL_PATH), { nameColumnSize + 5, y }); + } + + auto staffOrderIcon_x = nameColumnSize + 20; + if (peep->AssignedStaffType != StaffType::Entertainer) + { + auto staffOrders = peep->StaffOrders; + auto staffOrderSprite = GetStaffOrderBaseSprite(GetSelectedStaffType()); + + while (staffOrders != 0) + { + if (staffOrders & 1) + { + GfxDrawSprite(dpi, ImageId(staffOrderSprite), { staffOrderIcon_x, y }); + } + staffOrders = staffOrders >> 1; + staffOrderIcon_x += 9; + // TODO: Remove sprite ID addition + staffOrderSprite++; + } + } + else + { + GfxDrawSprite(dpi, ImageId(GetEntertainerCostumeSprite(peep->SpriteType)), { staffOrderIcon_x, y }); + } + } + + y += SCROLLABLE_ROW_HEIGHT; + i++; + } + } + + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (widgetIndex == WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON) + { + auto closestStaffMember = GetClosestStaffMemberTo(screenCoords); + if (closestStaffMember != nullptr) + { + ToolCancel(); + auto* staffWindow = WindowStaffOpen(closestStaffMember); + staffWindow->OnDropdown(WC_PEEP__WIDX_PATROL, 0); + } else { - GfxDrawSprite(dpi, ImageId(GetEntertainerCostumeSprite(peep->SpriteType)), { staffOrderIcon_x, y }); + auto ft = Formatter(); + ft.Add(GetStaffNamingConvention(GetSelectedStaffType()).Plural); + ContextShowError(STR_NO_THING_IN_PARK_YET, STR_NONE, ft); + } + } + } + + void OnToolAbort(WidgetIndex widgetIndex) override + { + if (widgetIndex == WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON) + { + HideGridlines(); + ToolCancel(); + ClearPatrolAreaToRender(); + GfxInvalidateScreen(); + } + } + + void RefreshList() + { + _staffList.clear(); + + for (auto* peep : EntityList()) + { + EntitySetFlashing(peep, false); + if (peep->AssignedStaffType == GetSelectedStaffType()) + { + EntitySetFlashing(peep, true); + + StaffEntry entry; + entry.Id = peep->Id; + entry.Name = peep->GetName(); + + _staffList.push_back(std::move(entry)); } } - y += SCROLLABLE_ROW_HEIGHT; - i++; + std::sort(_staffList.begin(), _staffList.end(), [](const auto& a, const auto& b) { + return StrLogicalCmp(a.Name.c_str(), b.Name.c_str()) < 0; + }); } - } - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (widgetIndex == WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON) + private: + /** + * Hires a new staff member of the given type. + */ + void HireNewMember(StaffType staffType, EntertainerCostume entertainerType) { - auto closestStaffMember = GetClosestStaffMemberTo(screenCoords); - if (closestStaffMember != nullptr) + bool autoPosition = gConfigGeneral.AutoStaffPlacement; + if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z) { - ToolCancel(); - auto* staffWindow = WindowStaffOpen(closestStaffMember); - staffWindow->OnDropdown(WC_PEEP__WIDX_PATROL, 0); + autoPosition = autoPosition ^ 1; } - else + + uint32_t staffOrders = 0; + + if (staffType == StaffType::Handyman) { - auto ft = Formatter(); - ft.Add(GetStaffNamingConvention(GetSelectedStaffType()).Plural); - ContextShowError(STR_NO_THING_IN_PARK_YET, STR_NONE, ft); + staffOrders = STAFF_ORDERS_SWEEPING | STAFF_ORDERS_WATER_FLOWERS | STAFF_ORDERS_EMPTY_BINS; + if (gConfigGeneral.HandymenMowByDefault) + { + staffOrders |= STAFF_ORDERS_MOWING; + } } - } - } - - void OnToolAbort(WidgetIndex widgetIndex) override - { - if (widgetIndex == WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON) - { - HideGridlines(); - ToolCancel(); - ClearPatrolAreaToRender(); - GfxInvalidateScreen(); - } - } - - void RefreshList() - { - _staffList.clear(); - - for (auto* peep : EntityList()) - { - EntitySetFlashing(peep, false); - if (peep->AssignedStaffType == GetSelectedStaffType()) + else if (staffType == StaffType::Mechanic) { - EntitySetFlashing(peep, true); - - StaffEntry entry; - entry.Id = peep->Id; - entry.Name = peep->GetName(); - - _staffList.push_back(std::move(entry)); + staffOrders = STAFF_ORDERS_INSPECT_RIDES | STAFF_ORDERS_FIX_RIDES; } - } - std::sort(_staffList.begin(), _staffList.end(), [](const auto& a, const auto& b) { - return StrLogicalCmp(a.Name.c_str(), b.Name.c_str()) < 0; - }); - } + auto hireStaffAction = StaffHireNewAction(autoPosition, staffType, entertainerType, staffOrders); + hireStaffAction.SetCallback([=](const GameAction*, const GameActions::Result* res) -> void { + if (res->Error != GameActions::Status::Ok) + return; -private: - /** - * Hires a new staff member of the given type. - */ - void HireNewMember(StaffType staffType, EntertainerCostume entertainerType) - { - bool autoPosition = gConfigGeneral.AutoStaffPlacement; - if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z) - { - autoPosition = autoPosition ^ 1; - } + auto actionResult = res->GetData(); + auto* staff = GetEntity(actionResult.StaffEntityId); + if (staff == nullptr) + return; - uint32_t staffOrders = 0; + // If autoposition of staff is disabled, pickup peep and then open the staff window + if (staff->State == PeepState::Picked) + { + CoordsXYZ nullLoc{}; + nullLoc.SetNull(); - if (staffType == StaffType::Handyman) - { - staffOrders = STAFF_ORDERS_SWEEPING | STAFF_ORDERS_WATER_FLOWERS | STAFF_ORDERS_EMPTY_BINS; - if (gConfigGeneral.HandymenMowByDefault) - { - staffOrders |= STAFF_ORDERS_MOWING; - } - } - else if (staffType == StaffType::Mechanic) - { - staffOrders = STAFF_ORDERS_INSPECT_RIDES | STAFF_ORDERS_FIX_RIDES; - } + PeepPickupAction pickupAction{ PeepPickupType::Pickup, staff->Id, nullLoc, NetworkGetCurrentPlayerId() }; + pickupAction.SetCallback([staffId = staff->Id](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; - auto hireStaffAction = StaffHireNewAction(autoPosition, staffType, entertainerType, staffOrders); - hireStaffAction.SetCallback([=](const GameAction*, const GameActions::Result* res) -> void { - if (res->Error != GameActions::Status::Ok) - return; - - auto actionResult = res->GetData(); - auto* staff = GetEntity(actionResult.StaffEntityId); - if (staff == nullptr) - return; - - // If autoposition of staff is disabled, pickup peep and then open the staff window - if (staff->State == PeepState::Picked) - { - CoordsXYZ nullLoc{}; - nullLoc.SetNull(); - - PeepPickupAction pickupAction{ PeepPickupType::Pickup, staff->Id, nullLoc, NetworkGetCurrentPlayerId() }; - pickupAction.SetCallback([staffId = staff->Id](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) - return; - - auto* staff2 = GetEntity(staffId); + auto* staff2 = GetEntity(staffId); + auto intent = Intent(WindowClass::Peep); + intent.PutExtra(INTENT_EXTRA_PEEP, staff2); + auto* wind = ContextOpenIntent(&intent); + if (wind != nullptr) + { + ToolSet(*wind, WC_STAFF__WIDX_PICKUP, Tool::Picker); + } + }); + GameActions::Execute(&pickupAction); + } + else + { + // Open window for new staff. auto intent = Intent(WindowClass::Peep); - intent.PutExtra(INTENT_EXTRA_PEEP, staff2); - auto* wind = ContextOpenIntent(&intent); - if (wind != nullptr) - { - ToolSet(*wind, WC_STAFF__WIDX_PICKUP, Tool::Picker); - } - }); - GameActions::Execute(&pickupAction); - } - else - { - // Open window for new staff. - auto intent = Intent(WindowClass::Peep); - intent.PutExtra(INTENT_EXTRA_PEEP, staff); - ContextOpenIntent(&intent); - } - }); + intent.PutExtra(INTENT_EXTRA_PEEP, staff); + ContextOpenIntent(&intent); + } + }); - GameActions::Execute(&hireStaffAction); - } + GameActions::Execute(&hireStaffAction); + } - StaffType GetSelectedStaffType() const - { - return static_cast(_selectedTab); - } - - void DrawTabImages(DrawPixelInfo& dpi) const - { - const auto& gameState = GetGameState(); - DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_HANDYMEN, PeepSpriteType::Handyman, gameState.StaffHandymanColour); - DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_MECHANICS, PeepSpriteType::Mechanic, gameState.StaffMechanicColour); - DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_SECURITY, PeepSpriteType::Security, gameState.StaffSecurityColour); - DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_ENTERTAINERS, PeepSpriteType::EntertainerElephant); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t tabIndex, PeepSpriteType type, colour_t colour) const - { - auto widgetIndex = WIDX_STAFF_LIST_HANDYMEN_TAB + tabIndex; - const auto& widget = widgets[widgetIndex]; - auto imageId = (_selectedTab == tabIndex ? (_tabAnimationIndex & ~3) : 0); - imageId += GetPeepAnimation(type).base_image + 1; - GfxDrawSprite( - dpi, ImageId(imageId, colour), windowPos + ScreenCoordsXY{ (widget.left + widget.right) / 2, widget.bottom - 6 }); - } - - void DrawTabImage(DrawPixelInfo& dpi, int32_t tabIndex, PeepSpriteType type) const - { - auto widgetIndex = WIDX_STAFF_LIST_HANDYMEN_TAB + tabIndex; - const auto& widget = widgets[widgetIndex]; - DrawPixelInfo clippedDpi; - if (ClipDrawPixelInfo( - clippedDpi, dpi, windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 }, widget.right - widget.left - 1, - widget.bottom - widget.top - 1)) + StaffType GetSelectedStaffType() const { - auto imageId = (_selectedTab == 3 ? (_tabAnimationIndex & ~3) : 0); + return static_cast(_selectedTab); + } + + void DrawTabImages(DrawPixelInfo& dpi) const + { + const auto& gameState = GetGameState(); + DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_HANDYMEN, PeepSpriteType::Handyman, gameState.StaffHandymanColour); + DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_MECHANICS, PeepSpriteType::Mechanic, gameState.StaffMechanicColour); + DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_SECURITY, PeepSpriteType::Security, gameState.StaffSecurityColour); + DrawTabImage(dpi, WINDOW_STAFF_LIST_TAB_ENTERTAINERS, PeepSpriteType::EntertainerElephant); + } + + void DrawTabImage(DrawPixelInfo& dpi, int32_t tabIndex, PeepSpriteType type, colour_t colour) const + { + auto widgetIndex = WIDX_STAFF_LIST_HANDYMEN_TAB + tabIndex; + const auto& widget = widgets[widgetIndex]; + auto imageId = (_selectedTab == tabIndex ? (_tabAnimationIndex & ~3) : 0); imageId += GetPeepAnimation(type).base_image + 1; - GfxDrawSprite(clippedDpi, ImageId(imageId), { 15, 23 }); + GfxDrawSprite( + dpi, ImageId(imageId, colour), + windowPos + ScreenCoordsXY{ (widget.left + widget.right) / 2, widget.bottom - 6 }); } - } - void CancelTools() - { - if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) + void DrawTabImage(DrawPixelInfo& dpi, int32_t tabIndex, PeepSpriteType type) const { - if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) + auto widgetIndex = WIDX_STAFF_LIST_HANDYMEN_TAB + tabIndex; + const auto& widget = widgets[widgetIndex]; + DrawPixelInfo clippedDpi; + if (ClipDrawPixelInfo( + clippedDpi, dpi, windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 }, + widget.right - widget.left - 1, widget.bottom - widget.top - 1)) { - ToolCancel(); + auto imageId = (_selectedTab == 3 ? (_tabAnimationIndex & ~3) : 0); + imageId += GetPeepAnimation(type).base_image + 1; + GfxDrawSprite(clippedDpi, ImageId(imageId), { 15, 23 }); } } - } - Peep* GetClosestStaffMemberTo(const ScreenCoordsXY& screenCoords) - { - int32_t direction{}; - TileElement* tileElement{}; - auto footpathCoords = FootpathGetCoordinatesFromPos(screenCoords, &direction, &tileElement); - if (footpathCoords.IsNull()) - return nullptr; - - auto isPatrolAreaSet = IsPatrolAreaSetForStaffType(GetSelectedStaffType(), footpathCoords); - - Peep* closestPeep = nullptr; - auto closestPeepDistance = std::numeric_limits::max(); - for (auto peep : EntityList()) + void CancelTools() { - if (peep->AssignedStaffType != GetSelectedStaffType()) - continue; - - if (isPatrolAreaSet) + if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) { - if (!peep->HasPatrolArea()) + if (classification == gCurrentToolWidget.window_classification && number == gCurrentToolWidget.window_number) + { + ToolCancel(); + } + } + } + + Peep* GetClosestStaffMemberTo(const ScreenCoordsXY& screenCoords) + { + int32_t direction{}; + TileElement* tileElement{}; + auto footpathCoords = FootpathGetCoordinatesFromPos(screenCoords, &direction, &tileElement); + if (footpathCoords.IsNull()) + return nullptr; + + auto isPatrolAreaSet = IsPatrolAreaSetForStaffType(GetSelectedStaffType(), footpathCoords); + + Peep* closestPeep = nullptr; + auto closestPeepDistance = std::numeric_limits::max(); + for (auto peep : EntityList()) + { + if (peep->AssignedStaffType != GetSelectedStaffType()) + continue; + + if (isPatrolAreaSet) + { + if (!peep->HasPatrolArea()) + { + continue; + } + if (!peep->IsLocationInPatrol(footpathCoords)) + { + continue; + } + } + + if (peep->x == LOCATION_NULL) { continue; } - if (!peep->IsLocationInPatrol(footpathCoords)) + + auto distance = std::abs(footpathCoords.x - peep->x) + std::abs(footpathCoords.y - peep->y); + if (distance < closestPeepDistance) { - continue; + closestPeepDistance = distance; + closestPeep = peep; } } + return closestPeep; + } - if (peep->x == LOCATION_NULL) + static EntertainerCostume GetRandomEntertainerCostume() + { + auto result = EntertainerCostume::Panda; + EntertainerCostume costumeList[EnumValue(EntertainerCostume::Count)]; + int32_t numCostumes = StaffGetAvailableEntertainerCostumeList(costumeList); + if (numCostumes > 0) { - continue; + int32_t index = UtilRand() % numCostumes; + result = costumeList[index]; } + return result; + } - auto distance = std::abs(footpathCoords.x - peep->x) + std::abs(footpathCoords.y - peep->y); - if (distance < closestPeepDistance) + static constexpr StaffNamingConvention GetStaffNamingConvention(StaffType type) + { + switch (type) { - closestPeepDistance = distance; - closestPeep = peep; + default: + case StaffType::Handyman: + return { STR_HANDYMAN_PLURAL, STR_HANDYMAN_SINGULAR, STR_HIRE_HANDYMAN }; + case StaffType::Mechanic: + return { STR_MECHANIC_PLURAL, STR_MECHANIC_SINGULAR, STR_HIRE_MECHANIC }; + case StaffType::Security: + return { STR_SECURITY_GUARD_PLURAL, STR_SECURITY_GUARD_SINGULAR, STR_HIRE_SECURITY_GUARD }; + case StaffType::Entertainer: + return { STR_ENTERTAINER_PLURAL, STR_ENTERTAINER_SINGULAR, STR_HIRE_ENTERTAINER }; } } - return closestPeep; - } - static EntertainerCostume GetRandomEntertainerCostume() - { - auto result = EntertainerCostume::Panda; - EntertainerCostume costumeList[EnumValue(EntertainerCostume::Count)]; - int32_t numCostumes = StaffGetAvailableEntertainerCostumeList(costumeList); - if (numCostumes > 0) + static uint32_t GetStaffOrderBaseSprite(StaffType type) { - int32_t index = UtilRand() % numCostumes; - result = costumeList[index]; + switch (type) + { + case StaffType::Handyman: + return SPR_STAFF_ORDERS_SWEEPING; + case StaffType::Mechanic: + return SPR_STAFF_ORDERS_INSPECT_RIDES; + default: + return 0; + } } - return result; + + static uint32_t GetEntertainerCostumeSprite(PeepSpriteType type) + { + switch (type) + { + default: + case PeepSpriteType::EntertainerPanda: + return SPR_STAFF_COSTUME_PANDA; + case PeepSpriteType::EntertainerTiger: + return SPR_STAFF_COSTUME_TIGER; + case PeepSpriteType::EntertainerElephant: + return SPR_STAFF_COSTUME_ELEPHANT; + case PeepSpriteType::EntertainerRoman: + return SPR_STAFF_COSTUME_ROMAN; + case PeepSpriteType::EntertainerGorilla: + return SPR_STAFF_COSTUME_GORILLA; + case PeepSpriteType::EntertainerSnowman: + return SPR_STAFF_COSTUME_SNOWMAN; + case PeepSpriteType::EntertainerKnight: + return SPR_STAFF_COSTUME_KNIGHT; + case PeepSpriteType::EntertainerAstronaut: + return SPR_STAFF_COSTUME_ASTRONAUT; + case PeepSpriteType::EntertainerBandit: + return SPR_STAFF_COSTUME_BANDIT; + case PeepSpriteType::EntertainerSheriff: + return SPR_STAFF_COSTUME_SHERIFF; + case PeepSpriteType::EntertainerPirate: + return SPR_STAFF_COSTUME_PIRATE; + } + } + }; + + WindowBase* WindowStaffListOpen() + { + return WindowFocusOrCreate(WindowClass::StaffList, WW, WH, WF_10 | WF_RESIZABLE); } - static constexpr StaffNamingConvention GetStaffNamingConvention(StaffType type) + void WindowStaffListRefresh() { - switch (type) + auto* window = WindowFindByClass(WindowClass::StaffList); + if (window != nullptr) { - default: - case StaffType::Handyman: - return { STR_HANDYMAN_PLURAL, STR_HANDYMAN_SINGULAR, STR_HIRE_HANDYMAN }; - case StaffType::Mechanic: - return { STR_MECHANIC_PLURAL, STR_MECHANIC_SINGULAR, STR_HIRE_MECHANIC }; - case StaffType::Security: - return { STR_SECURITY_GUARD_PLURAL, STR_SECURITY_GUARD_SINGULAR, STR_HIRE_SECURITY_GUARD }; - case StaffType::Entertainer: - return { STR_ENTERTAINER_PLURAL, STR_ENTERTAINER_SINGULAR, STR_HIRE_ENTERTAINER }; + static_cast(window)->RefreshList(); } } - - static uint32_t GetStaffOrderBaseSprite(StaffType type) - { - switch (type) - { - case StaffType::Handyman: - return SPR_STAFF_ORDERS_SWEEPING; - case StaffType::Mechanic: - return SPR_STAFF_ORDERS_INSPECT_RIDES; - default: - return 0; - } - } - - static uint32_t GetEntertainerCostumeSprite(PeepSpriteType type) - { - switch (type) - { - default: - case PeepSpriteType::EntertainerPanda: - return SPR_STAFF_COSTUME_PANDA; - case PeepSpriteType::EntertainerTiger: - return SPR_STAFF_COSTUME_TIGER; - case PeepSpriteType::EntertainerElephant: - return SPR_STAFF_COSTUME_ELEPHANT; - case PeepSpriteType::EntertainerRoman: - return SPR_STAFF_COSTUME_ROMAN; - case PeepSpriteType::EntertainerGorilla: - return SPR_STAFF_COSTUME_GORILLA; - case PeepSpriteType::EntertainerSnowman: - return SPR_STAFF_COSTUME_SNOWMAN; - case PeepSpriteType::EntertainerKnight: - return SPR_STAFF_COSTUME_KNIGHT; - case PeepSpriteType::EntertainerAstronaut: - return SPR_STAFF_COSTUME_ASTRONAUT; - case PeepSpriteType::EntertainerBandit: - return SPR_STAFF_COSTUME_BANDIT; - case PeepSpriteType::EntertainerSheriff: - return SPR_STAFF_COSTUME_SHERIFF; - case PeepSpriteType::EntertainerPirate: - return SPR_STAFF_COSTUME_PIRATE; - } - } -}; - -WindowBase* WindowStaffListOpen() -{ - return WindowFocusOrCreate(WindowClass::StaffList, WW, WH, WF_10 | WF_RESIZABLE); -} - -void WindowStaffListRefresh() -{ - auto* window = WindowFindByClass(WindowClass::StaffList); - if (window != nullptr) - { - static_cast(window)->RefreshList(); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index dda0e23ce7..0b1fb7a13b 100644 --- a/src/openrct2-ui/windows/TextInput.cpp +++ b/src/openrct2-ui/windows/TextInput.cpp @@ -21,407 +21,411 @@ #include #include -static constexpr int32_t WW = 250; -static constexpr int32_t WH = 90; - -enum WindowTextInputWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_CANCEL, - WIDX_OKAY -}; + static constexpr int32_t WW = 250; + static constexpr int32_t WH = 90; -static Widget _textInputWidgets[] = { - WINDOW_SHIM(STR_NONE, WW, WH), - MakeWidget({ 170, 68 }, { 71, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_CANCEL), - MakeWidget({ 10, 68 }, { 71, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_OK), - kWidgetsEnd, -}; - -class TextInputWindow final : public Window -{ -private: - WidgetIdentifier _parentWidget{}; - - std::string _title; - StringId _titleStringId = STR_NONE; - - std::string _description; - StringId _descriptionStringId = STR_NONE; - Formatter _descriptionArgs; - - std::function _callback; - std::function _cancelCallback; - - int32_t _cursorBlink{}; - size_t _maxInputLength{}; - u8string _buffer; - -public: - void OnOpen() override + enum WindowTextInputWidgetIdx { - widgets = _textInputWidgets; - WindowInitScrollWidgets(*this); - SetParentWindow(nullptr, 0); - } + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_CANCEL, + WIDX_OKAY + }; - void SetParentWindow(WindowBase* parentWindow, WidgetIndex widgetIndex) + static Widget _textInputWidgets[] = { + WINDOW_SHIM(STR_NONE, WW, WH), + MakeWidget({ 170, 68 }, { 71, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_CANCEL), + MakeWidget({ 10, 68 }, { 71, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_OK), + kWidgetsEnd, + }; + + class TextInputWindow final : public Window { - // Save calling window details so that the information can be passed back to the correct window & widget - if (parentWindow == nullptr) + private: + WidgetIdentifier _parentWidget{}; + + std::string _title; + StringId _titleStringId = STR_NONE; + + std::string _description; + StringId _descriptionStringId = STR_NONE; + Formatter _descriptionArgs; + + std::function _callback; + std::function _cancelCallback; + + int32_t _cursorBlink{}; + size_t _maxInputLength{}; + u8string _buffer; + + public: + void OnOpen() override { - _parentWidget.window.classification = WindowClass::Null; - _parentWidget.window.number = 0; - _parentWidget.widget_index = 0; - - colours[0] = COLOUR_GREY; - colours[1] = COLOUR_GREY; - colours[2] = COLOUR_GREY; + widgets = _textInputWidgets; + WindowInitScrollWidgets(*this); + SetParentWindow(nullptr, 0); } - else + + void SetParentWindow(WindowBase* parentWindow, WidgetIndex widgetIndex) { - _parentWidget.window.classification = parentWindow->classification; - _parentWidget.window.number = parentWindow->number; - _parentWidget.widget_index = widgetIndex; - - colours[0] = parentWindow->colours[1]; - colours[1] = parentWindow->colours[1]; - colours[2] = parentWindow->colours[1]; - } - } - - void SetTitle(StringId title, StringId description, const Formatter& decriptionArgs) - { - _titleStringId = title; - _descriptionStringId = description; - _descriptionArgs = decriptionArgs; - } - - void SetTitle(std::string_view title, std::string_view description) - { - _titleStringId = STR_NONE; - _title = title; - _descriptionStringId = STR_NONE; - _description = description; - } - - void SetText(std::string_view text, size_t maxLength) - { - text = String::UTF8TruncateCodePoints(text, maxLength); - _buffer = u8string{ text }; - _maxInputLength = maxLength; - gTextInput = ContextStartTextInput(_buffer, maxLength); - } - - void SetCallback(std::function callback, std::function cancelCallback) - { - _callback = callback; - _cancelCallback = cancelCallback; - } - - void OnClose() override - { - // Make sure that we take it out of the text input - // mode otherwise problems may occur. - ContextStopTextInput(); - } - - void OnUpdate() override - { - if (HasParentWindow()) - { - // If the calling window is closed then close the text input window - auto parentWindow = GetParentWindow(); + // Save calling window details so that the information can be passed back to the correct window & widget if (parentWindow == nullptr) { - WindowClose(*this); - return; + _parentWidget.window.classification = WindowClass::Null; + _parentWidget.window.number = 0; + _parentWidget.widget_index = 0; + + colours[0] = COLOUR_GREY; + colours[1] = COLOUR_GREY; + colours[2] = COLOUR_GREY; + } + else + { + _parentWidget.window.classification = parentWindow->classification; + _parentWidget.window.number = parentWindow->number; + _parentWidget.widget_index = widgetIndex; + + colours[0] = parentWindow->colours[1]; + colours[1] = parentWindow->colours[1]; + colours[2] = parentWindow->colours[1]; } } - // Used to blink the cursor. - _cursorBlink++; - if (_cursorBlink > 30) - _cursorBlink = 0; - - Invalidate(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void SetTitle(StringId title, StringId description, const Formatter& decriptionArgs) { - case WIDX_CANCEL: - case WIDX_CLOSE: - ContextStopTextInput(); - ExecuteCallback(false); - WindowClose(*this); - break; - case WIDX_OKAY: - ContextStopTextInput(); - ExecuteCallback(true); - WindowClose(*this); - } - } - - void OnPrepareDraw() override - { - // Change window size if required. - int32_t newHeight = CalculateWindowHeight(_buffer.data()); - if (newHeight != height) - { - WindowSetResize(*this, WW, newHeight, WW, newHeight); + _titleStringId = title; + _descriptionStringId = description; + _descriptionArgs = decriptionArgs; } - widgets[WIDX_OKAY].top = newHeight - 22; - widgets[WIDX_OKAY].bottom = newHeight - 9; - widgets[WIDX_CANCEL].top = newHeight - 22; - widgets[WIDX_CANCEL].bottom = newHeight - 9; - widgets[WIDX_BACKGROUND].bottom = newHeight - 1; - - // Set window title argument - if (_titleStringId == STR_NONE) + void SetTitle(std::string_view title, std::string_view description) { - auto ft = Formatter::Common(); - ft.Add(_title.c_str()); - widgets[WIDX_TITLE].text = STR_STRING; - } - else - { - widgets[WIDX_TITLE].text = _titleStringId; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - - ScreenCoordsXY screenCoords; - screenCoords.y = windowPos.y + 25; - - int32_t no_lines = 0; - - if (_descriptionStringId == STR_NONE) - { - auto ft = Formatter(); - ft.Add(_description.c_str()); - DrawTextWrapped( - dpi, { windowPos.x + WW / 2, screenCoords.y }, WW, STR_STRING, ft, { colours[1], TextAlignment::CENTRE }); - } - else - { - DrawTextWrapped( - dpi, { windowPos.x + WW / 2, screenCoords.y }, WW, _descriptionStringId, _descriptionArgs, - { colours[1], TextAlignment::CENTRE }); + _titleStringId = STR_NONE; + _title = title; + _descriptionStringId = STR_NONE; + _description = description; } - screenCoords.y += 25; - - // String length needs to add 12 either side of box - // +13 for cursor when max length. - u8string wrappedString; - GfxWrapString( - u8string_view{ _buffer.data(), _buffer.size() }, WW - (24 + 13), FontStyle::Medium, &wrappedString, &no_lines); - - GfxFillRectInset( - dpi, { { windowPos.x + 10, screenCoords.y }, { windowPos.x + WW - 10, screenCoords.y + 10 * (no_lines + 1) + 3 } }, - colours[1], INSET_RECT_F_60); - - screenCoords.y += 1; - - const utf8* wrapPointer = wrappedString.data(); - size_t char_count = 0; - uint8_t cur_drawn = 0; - - int32_t cursorX = 0; - int32_t cursorY = 0; - for (int32_t line = 0; line <= no_lines; line++) + void SetText(std::string_view text, size_t maxLength) { - screenCoords.x = windowPos.x + 12; - DrawText(dpi, screenCoords, { colours[1], FontStyle::Medium, TextAlignment::LEFT }, wrapPointer, true); + text = String::UTF8TruncateCodePoints(text, maxLength); + _buffer = u8string{ text }; + _maxInputLength = maxLength; + gTextInput = ContextStartTextInput(_buffer, maxLength); + } - size_t string_length = GetStringSize(wrapPointer) - 1; + void SetCallback(std::function callback, std::function cancelCallback) + { + _callback = callback; + _cancelCallback = cancelCallback; + } - if (!cur_drawn && (gTextInput->SelectionStart <= char_count + string_length)) + void OnClose() override + { + // Make sure that we take it out of the text input + // mode otherwise problems may occur. + ContextStopTextInput(); + } + + void OnUpdate() override + { + if (HasParentWindow()) { - // Make a view of the string for measuring the width. - cursorX = windowPos.x + 13 - + GfxGetStringWidthNoFormatting( - u8string_view{ wrapPointer, gTextInput->SelectionStart - char_count }, FontStyle::Medium); - cursorY = screenCoords.y; - - int32_t textWidth = 6; - if (gTextInput->SelectionStart < strlen(_buffer.data())) + // If the calling window is closed then close the text input window + auto parentWindow = GetParentWindow(); + if (parentWindow == nullptr) { - // Make a 1 utf8-character wide string for measuring the width - // of the currently selected character. - utf8 tmp[5] = {}; // This is easier than setting temp_string[0..5] - uint32_t codepoint = UTF8GetNext(_buffer.data() + gTextInput->SelectionStart, nullptr); - UTF8WriteCodepoint(tmp, codepoint); - textWidth = std::max(GfxGetStringWidthNoFormatting(tmp, FontStyle::Medium) - 2, 4); - } - - if (_cursorBlink > 15) - { - uint8_t colour = ColourMapA[colours[1]].mid_light; - // TODO: palette index addition - GfxFillRect( - dpi, { { cursorX, screenCoords.y + 9 }, { cursorX + textWidth, screenCoords.y + 9 } }, colour + 5); - } - - cur_drawn++; - } - - wrapPointer += string_length + 1; - - if (_buffer[char_count + string_length] == ' ') - char_count++; - char_count += string_length; - - screenCoords.y += 10; - } - - if (!cur_drawn) - { - cursorX = dpi.lastStringPos.x; - cursorY = screenCoords.y - 10; - } - - // IME composition - if (!String::IsNullOrEmpty(gTextInput->ImeBuffer)) - { - IMEComposition(cursorX, cursorY); - } - } - - void OnReturnPressed() - { - ContextStopTextInput(); - ExecuteCallback(true); - WindowClose(*this); - } - - static int32_t CalculateWindowHeight(std::string_view text) - { - // String length needs to add 12 either side of box +13 for cursor when max length. - int32_t numLines{}; - GfxWrapString(text, WW - (24 + 13), FontStyle::Medium, nullptr, &numLines); - return numLines * 10 + WH; - } - - void OnResize() override - { - ResizeFrame(); - } - -private: - static void IMEComposition(int32_t cursorX, int32_t cursorY) - { - SDL_Rect rect = { cursorX, cursorY, 100, 100 }; - SDL_SetTextInputRect(&rect); - } - - void ExecuteCallback(bool hasValue) - { - if (HasParentWindow()) - { - if (hasValue) - { - auto w = GetParentWindow(); - if (w != nullptr) - { - w->OnTextInput(_parentWidget.widget_index, _buffer); + WindowClose(*this); + return; } } + + // Used to blink the cursor. + _cursorBlink++; + if (_cursorBlink > 30) + _cursorBlink = 0; + + Invalidate(); } - else + + void OnMouseUp(WidgetIndex widgetIndex) override { - if (hasValue) + switch (widgetIndex) { - if (_callback) + case WIDX_CANCEL: + case WIDX_CLOSE: + ContextStopTextInput(); + ExecuteCallback(false); + WindowClose(*this); + break; + case WIDX_OKAY: + ContextStopTextInput(); + ExecuteCallback(true); + WindowClose(*this); + } + } + + void OnPrepareDraw() override + { + // Change window size if required. + int32_t newHeight = CalculateWindowHeight(_buffer.data()); + if (newHeight != height) + { + WindowSetResize(*this, WW, newHeight, WW, newHeight); + } + + widgets[WIDX_OKAY].top = newHeight - 22; + widgets[WIDX_OKAY].bottom = newHeight - 9; + widgets[WIDX_CANCEL].top = newHeight - 22; + widgets[WIDX_CANCEL].bottom = newHeight - 9; + widgets[WIDX_BACKGROUND].bottom = newHeight - 1; + + // Set window title argument + if (_titleStringId == STR_NONE) + { + auto ft = Formatter::Common(); + ft.Add(_title.c_str()); + widgets[WIDX_TITLE].text = STR_STRING; + } + else + { + widgets[WIDX_TITLE].text = _titleStringId; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + ScreenCoordsXY screenCoords; + screenCoords.y = windowPos.y + 25; + + int32_t no_lines = 0; + + if (_descriptionStringId == STR_NONE) + { + auto ft = Formatter(); + ft.Add(_description.c_str()); + DrawTextWrapped( + dpi, { windowPos.x + WW / 2, screenCoords.y }, WW, STR_STRING, ft, { colours[1], TextAlignment::CENTRE }); + } + else + { + DrawTextWrapped( + dpi, { windowPos.x + WW / 2, screenCoords.y }, WW, _descriptionStringId, _descriptionArgs, + { colours[1], TextAlignment::CENTRE }); + } + + screenCoords.y += 25; + + // String length needs to add 12 either side of box + // +13 for cursor when max length. + u8string wrappedString; + GfxWrapString( + u8string_view{ _buffer.data(), _buffer.size() }, WW - (24 + 13), FontStyle::Medium, &wrappedString, &no_lines); + + GfxFillRectInset( + dpi, + { { windowPos.x + 10, screenCoords.y }, { windowPos.x + WW - 10, screenCoords.y + 10 * (no_lines + 1) + 3 } }, + colours[1], INSET_RECT_F_60); + + screenCoords.y += 1; + + const utf8* wrapPointer = wrappedString.data(); + size_t char_count = 0; + uint8_t cur_drawn = 0; + + int32_t cursorX = 0; + int32_t cursorY = 0; + for (int32_t line = 0; line <= no_lines; line++) + { + screenCoords.x = windowPos.x + 12; + DrawText(dpi, screenCoords, { colours[1], FontStyle::Medium, TextAlignment::LEFT }, wrapPointer, true); + + size_t string_length = GetStringSize(wrapPointer) - 1; + + if (!cur_drawn && (gTextInput->SelectionStart <= char_count + string_length)) { - _callback(_buffer.data()); + // Make a view of the string for measuring the width. + cursorX = windowPos.x + 13 + + GfxGetStringWidthNoFormatting( + u8string_view{ wrapPointer, gTextInput->SelectionStart - char_count }, FontStyle::Medium); + cursorY = screenCoords.y; + + int32_t textWidth = 6; + if (gTextInput->SelectionStart < strlen(_buffer.data())) + { + // Make a 1 utf8-character wide string for measuring the width + // of the currently selected character. + utf8 tmp[5] = {}; // This is easier than setting temp_string[0..5] + uint32_t codepoint = UTF8GetNext(_buffer.data() + gTextInput->SelectionStart, nullptr); + UTF8WriteCodepoint(tmp, codepoint); + textWidth = std::max(GfxGetStringWidthNoFormatting(tmp, FontStyle::Medium) - 2, 4); + } + + if (_cursorBlink > 15) + { + uint8_t colour = ColourMapA[colours[1]].mid_light; + // TODO: palette index addition + GfxFillRect( + dpi, { { cursorX, screenCoords.y + 9 }, { cursorX + textWidth, screenCoords.y + 9 } }, colour + 5); + } + + cur_drawn++; + } + + wrapPointer += string_length + 1; + + if (_buffer[char_count + string_length] == ' ') + char_count++; + char_count += string_length; + + screenCoords.y += 10; + } + + if (!cur_drawn) + { + cursorX = dpi.lastStringPos.x; + cursorY = screenCoords.y - 10; + } + + // IME composition + if (!String::IsNullOrEmpty(gTextInput->ImeBuffer)) + { + IMEComposition(cursorX, cursorY); + } + } + + void OnReturnPressed() + { + ContextStopTextInput(); + ExecuteCallback(true); + WindowClose(*this); + } + + static int32_t CalculateWindowHeight(std::string_view text) + { + // String length needs to add 12 either side of box +13 for cursor when max length. + int32_t numLines{}; + GfxWrapString(text, WW - (24 + 13), FontStyle::Medium, nullptr, &numLines); + return numLines * 10 + WH; + } + + void OnResize() override + { + ResizeFrame(); + } + + private: + static void IMEComposition(int32_t cursorX, int32_t cursorY) + { + SDL_Rect rect = { cursorX, cursorY, 100, 100 }; + SDL_SetTextInputRect(&rect); + } + + void ExecuteCallback(bool hasValue) + { + if (HasParentWindow()) + { + if (hasValue) + { + auto w = GetParentWindow(); + if (w != nullptr) + { + w->OnTextInput(_parentWidget.widget_index, _buffer); + } } } else { - if (_cancelCallback) + if (hasValue) { - _cancelCallback(); + if (_callback) + { + _callback(_buffer.data()); + } + } + else + { + if (_cancelCallback) + { + _cancelCallback(); + } } } } - } - bool HasParentWindow() const - { - return _parentWidget.window.classification != WindowClass::Null; - } - - WindowBase* GetParentWindow() const - { - return HasParentWindow() ? WindowFindByNumber(_parentWidget.window.classification, _parentWidget.window.number) - : nullptr; - } -}; - -void WindowTextInputRawOpen( - WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, - const_utf8string existing_text, int32_t maxLength) -{ - WindowCloseByClass(WindowClass::Textinput); - - auto height = TextInputWindow::CalculateWindowHeight(existing_text); - auto w = WindowCreate(WindowClass::Textinput, WW, height, WF_CENTRE_SCREEN | WF_STICK_TO_FRONT); - if (w != nullptr) - { - w->SetParentWindow(call_w, call_widget); - w->SetTitle(title, description, descriptionArgs); - w->SetText(existing_text, maxLength); - } -} - -void WindowTextInputOpen( - std::string_view title, std::string_view description, std::string_view initialValue, size_t maxLength, - std::function callback, std::function cancelCallback) -{ - auto height = TextInputWindow::CalculateWindowHeight(initialValue); - auto w = WindowCreate(WindowClass::Textinput, WW, height, WF_CENTRE_SCREEN | WF_STICK_TO_FRONT); - if (w != nullptr) - { - w->SetTitle(title, description); - w->SetText(initialValue, maxLength); - w->SetCallback(callback, cancelCallback); - } -} - -void WindowTextInputOpen( - WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, - StringId existing_text, uintptr_t existing_args, int32_t maxLength) -{ - auto existingText = FormatStringID(existing_text, &existing_args); - WindowTextInputRawOpen(call_w, call_widget, title, description, descriptionArgs, existingText.c_str(), maxLength); -} - -void WindowTextInputKey(WindowBase* w, uint32_t keycode) -{ - const auto wndNumber = w->number; - const auto wndClass = w->classification; - - // If the return button is pressed stop text input - if (keycode == SDLK_RETURN || keycode == SDLK_KP_ENTER) - { - if (w->classification == WindowClass::Textinput) + bool HasParentWindow() const { - auto textInputWindow = static_cast(w); - textInputWindow->OnReturnPressed(); + return _parentWidget.window.classification != WindowClass::Null; + } + + WindowBase* GetParentWindow() const + { + return HasParentWindow() ? WindowFindByNumber(_parentWidget.window.classification, _parentWidget.window.number) + : nullptr; + } + }; + + void WindowTextInputRawOpen( + WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, + const_utf8string existing_text, int32_t maxLength) + { + WindowCloseByClass(WindowClass::Textinput); + + auto height = TextInputWindow::CalculateWindowHeight(existing_text); + auto w = WindowCreate(WindowClass::Textinput, WW, height, WF_CENTRE_SCREEN | WF_STICK_TO_FRONT); + if (w != nullptr) + { + w->SetParentWindow(call_w, call_widget); + w->SetTitle(title, description, descriptionArgs); + w->SetText(existing_text, maxLength); } } - // The window can be potentially closed within a callback, we need to check if its still alive. - w = WindowFindByNumber(wndClass, wndNumber); - if (w != nullptr) - w->Invalidate(); -} + void WindowTextInputOpen( + std::string_view title, std::string_view description, std::string_view initialValue, size_t maxLength, + std::function callback, std::function cancelCallback) + { + auto height = TextInputWindow::CalculateWindowHeight(initialValue); + auto w = WindowCreate(WindowClass::Textinput, WW, height, WF_CENTRE_SCREEN | WF_STICK_TO_FRONT); + if (w != nullptr) + { + w->SetTitle(title, description); + w->SetText(initialValue, maxLength); + w->SetCallback(callback, cancelCallback); + } + } + + void WindowTextInputOpen( + WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, + StringId existing_text, uintptr_t existing_args, int32_t maxLength) + { + auto existingText = FormatStringID(existing_text, &existing_args); + WindowTextInputRawOpen(call_w, call_widget, title, description, descriptionArgs, existingText.c_str(), maxLength); + } + + void WindowTextInputKey(WindowBase* w, uint32_t keycode) + { + const auto wndNumber = w->number; + const auto wndClass = w->classification; + + // If the return button is pressed stop text input + if (keycode == SDLK_RETURN || keycode == SDLK_KP_ENTER) + { + if (w->classification == WindowClass::Textinput) + { + auto textInputWindow = static_cast(w); + textInputWindow->OnReturnPressed(); + } + } + + // The window can be potentially closed within a callback, we need to check if its still alive. + w = WindowFindByNumber(wndClass, wndNumber); + if (w != nullptr) + w->Invalidate(); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Themes.cpp b/src/openrct2-ui/windows/Themes.cpp index 32d727632d..9356636964 100644 --- a/src/openrct2-ui/windows/Themes.cpp +++ b/src/openrct2-ui/windows/Themes.cpp @@ -22,55 +22,57 @@ #include #include -enum +namespace OpenRCT2::Ui::Windows { - WINDOW_THEMES_TAB_SETTINGS, - WINDOW_THEMES_TAB_MAIN_UI, - WINDOW_THEMES_TAB_PARK, - WINDOW_THEMES_TAB_TOOLS, - WINDOW_THEMES_TAB_RIDES_PEEPS, - WINDOW_THEMES_TAB_EDITORS, - WINDOW_THEMES_TAB_MISC, - WINDOW_THEMES_TAB_PROMPTS, - WINDOW_THEMES_TAB_FEATURES, - WINDOW_THEMES_TAB_COUNT -}; + enum + { + WINDOW_THEMES_TAB_SETTINGS, + WINDOW_THEMES_TAB_MAIN_UI, + WINDOW_THEMES_TAB_PARK, + WINDOW_THEMES_TAB_TOOLS, + WINDOW_THEMES_TAB_RIDES_PEEPS, + WINDOW_THEMES_TAB_EDITORS, + WINDOW_THEMES_TAB_MISC, + WINDOW_THEMES_TAB_PROMPTS, + WINDOW_THEMES_TAB_FEATURES, + WINDOW_THEMES_TAB_COUNT + }; -enum WindowThemesWidgetIdx -{ - WIDX_THEMES_BACKGROUND, - WIDX_THEMES_TITLE, - WIDX_THEMES_CLOSE, - WIDX_THEMES_TAB_CONTENT_PANEL, - WIDX_THEMES_SETTINGS_TAB, - WIDX_THEMES_MAIN_UI_TAB, - WIDX_THEMES_PARK_TAB, - WIDX_THEMES_TOOLS_TAB, - WIDX_THEMES_RIDE_PEEPS_TAB, - WIDX_THEMES_EDITORS_TAB, - WIDX_THEMES_MISC_TAB, - WIDX_THEMES_PROMPTS_TAB, - WIDX_THEMES_FEATURES_TAB, - WIDX_THEMES_HEADER_WINDOW, - WIDX_THEMES_HEADER_PALETTE, - WIDX_THEMES_PRESETS, - WIDX_THEMES_PRESETS_DROPDOWN, - WIDX_THEMES_DUPLICATE_BUTTON, - WIDX_THEMES_DELETE_BUTTON, - WIDX_THEMES_RENAME_BUTTON, - WIDX_THEMES_COLOURBTN_MASK, - WIDX_THEMES_LIST, - WIDX_THEMES_RCT1_RIDE_LIGHTS, - WIDX_THEMES_RCT1_PARK_LIGHTS, - WIDX_THEMES_RCT1_SCENARIO_FONT, - WIDX_THEMES_RCT1_BOTTOM_TOOLBAR -}; + enum WindowThemesWidgetIdx + { + WIDX_THEMES_BACKGROUND, + WIDX_THEMES_TITLE, + WIDX_THEMES_CLOSE, + WIDX_THEMES_TAB_CONTENT_PANEL, + WIDX_THEMES_SETTINGS_TAB, + WIDX_THEMES_MAIN_UI_TAB, + WIDX_THEMES_PARK_TAB, + WIDX_THEMES_TOOLS_TAB, + WIDX_THEMES_RIDE_PEEPS_TAB, + WIDX_THEMES_EDITORS_TAB, + WIDX_THEMES_MISC_TAB, + WIDX_THEMES_PROMPTS_TAB, + WIDX_THEMES_FEATURES_TAB, + WIDX_THEMES_HEADER_WINDOW, + WIDX_THEMES_HEADER_PALETTE, + WIDX_THEMES_PRESETS, + WIDX_THEMES_PRESETS_DROPDOWN, + WIDX_THEMES_DUPLICATE_BUTTON, + WIDX_THEMES_DELETE_BUTTON, + WIDX_THEMES_RENAME_BUTTON, + WIDX_THEMES_COLOURBTN_MASK, + WIDX_THEMES_LIST, + WIDX_THEMES_RCT1_RIDE_LIGHTS, + WIDX_THEMES_RCT1_PARK_LIGHTS, + WIDX_THEMES_RCT1_SCENARIO_FONT, + WIDX_THEMES_RCT1_BOTTOM_TOOLBAR + }; -static constexpr StringId WINDOW_TITLE = STR_THEMES_TITLE; -static constexpr int32_t WW = 320; -static constexpr int32_t WH = 107; + static constexpr StringId WINDOW_TITLE = STR_THEMES_TITLE; + static constexpr int32_t WW = 320; + static constexpr int32_t WH = 107; -// clang-format off + // clang-format off static Widget _themesWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), MakeWidget({ 0, 43}, {320, 64}, WindowWidgetType::Resize, WindowColour::Secondary ), // tab content panel @@ -219,665 +221,669 @@ static WindowClass window_themes_tab_7_classes[] = { WindowClass::LoadsaveOverwritePrompt, WindowClass::NetworkStatus, }; -// clang-format on + // clang-format on -static WindowClass* window_themes_tab_classes[] = { - nullptr, - window_themes_tab_1_classes, - window_themes_tab_2_classes, - window_themes_tab_3_classes, - window_themes_tab_4_classes, - window_themes_tab_5_classes, - window_themes_tab_6_classes, - window_themes_tab_7_classes, -}; + static WindowClass* window_themes_tab_classes[] = { + nullptr, + window_themes_tab_1_classes, + window_themes_tab_2_classes, + window_themes_tab_3_classes, + window_themes_tab_4_classes, + window_themes_tab_5_classes, + window_themes_tab_6_classes, + window_themes_tab_7_classes, + }; #pragma endregion -class ThemesWindow final : public Window -{ -private: - uint8_t _selected_tab = 0; - int16_t _colour_index_1 = -1; - int8_t _colour_index_2 = -1; - const uint8_t _row_height = 32; - const uint8_t _button_offset_x = 220; - const uint8_t _button_offset_y = 3; - const uint8_t _check_offset_y = 3 + 12 + 2; + class ThemesWindow final : public Window + { + private: + uint8_t _selected_tab = 0; + int16_t _colour_index_1 = -1; + int8_t _colour_index_2 = -1; + const uint8_t _row_height = 32; + const uint8_t _button_offset_x = 220; + const uint8_t _button_offset_y = 3; + const uint8_t _check_offset_y = 3 + 12 + 2; -public: + public: #pragma region Window Override Events - void OnOpen() override - { - widgets = _themesWidgets; - - WindowThemesInitVars(); - - WindowInitScrollWidgets(*this); - list_information_type = 0; - _colour_index_1 = -1; - _colour_index_2 = -1; - min_width = 320; - min_height = 107; - max_width = 320; - max_height = 107; - } - - void OnResize() override - { - if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS) + void OnOpen() override { + widgets = _themesWidgets; + + WindowThemesInitVars(); + + WindowInitScrollWidgets(*this); + list_information_type = 0; + _colour_index_1 = -1; + _colour_index_2 = -1; min_width = 320; min_height = 107; max_width = 320; max_height = 107; - - if (width < min_width) - { - width = min_width; - GfxInvalidateScreen(); - } - if (height < min_height) - { - height = min_height; - GfxInvalidateScreen(); - } - if (width > max_width) - { - width = max_width; - GfxInvalidateScreen(); - } - if (height > max_height) - { - height = max_height; - GfxInvalidateScreen(); - } - } - else if (_selected_tab == WINDOW_THEMES_TAB_FEATURES) - { - min_width = 320; - min_height = 122; - max_width = 320; - max_height = 122; - - if (width < min_width) - { - width = min_width; - GfxInvalidateScreen(); - } - if (height < min_height) - { - height = min_height; - GfxInvalidateScreen(); - } - if (width > max_width) - { - width = max_width; - GfxInvalidateScreen(); - } - if (height > max_height) - { - height = max_height; - GfxInvalidateScreen(); - } - } - else - { - min_width = 320; - min_height = 270; - max_width = 320; - max_height = 450; - - if (width < min_width) - { - width = min_width; - Invalidate(); - } - if (height < min_height) - { - height = min_height; - Invalidate(); - } - if (width > max_width) - { - width = max_width; - Invalidate(); - } - if (height > max_height) - { - height = max_height; - Invalidate(); - } } - ResizeFrameWithPage(); - } - - void OnUpdate() override - { - frame_no++; - if (frame_no >= window_themes_tab_animation_loops[_selected_tab]) - frame_no = 0; - - WidgetInvalidate(*this, WIDX_THEMES_SETTINGS_TAB + _selected_tab); - } - - void OnPrepareDraw() override - { - int32_t pressedWidgets = pressed_widgets - & ~((1LL << WIDX_THEMES_SETTINGS_TAB) | (1LL << WIDX_THEMES_MAIN_UI_TAB) | (1LL << WIDX_THEMES_PARK_TAB) - | (1LL << WIDX_THEMES_TOOLS_TAB) | (1LL << WIDX_THEMES_RIDE_PEEPS_TAB) | (1LL << WIDX_THEMES_EDITORS_TAB) - | (1LL << WIDX_THEMES_MISC_TAB) | (1LL << WIDX_THEMES_PROMPTS_TAB) | (1LL << WIDX_THEMES_FEATURES_TAB)); - WidgetIndex widgetIndex = _selected_tab + WIDX_THEMES_SETTINGS_TAB; - - pressed_widgets = pressedWidgets | (1 << widgetIndex); - - if (WindowFindByClass(WindowClass::Dropdown) == nullptr) + void OnResize() override { - _colour_index_1 = -1; - _colour_index_2 = -1; + if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS) + { + min_width = 320; + min_height = 107; + max_width = 320; + max_height = 107; + + if (width < min_width) + { + width = min_width; + GfxInvalidateScreen(); + } + if (height < min_height) + { + height = min_height; + GfxInvalidateScreen(); + } + if (width > max_width) + { + width = max_width; + GfxInvalidateScreen(); + } + if (height > max_height) + { + height = max_height; + GfxInvalidateScreen(); + } + } + else if (_selected_tab == WINDOW_THEMES_TAB_FEATURES) + { + min_width = 320; + min_height = 122; + max_width = 320; + max_height = 122; + + if (width < min_width) + { + width = min_width; + GfxInvalidateScreen(); + } + if (height < min_height) + { + height = min_height; + GfxInvalidateScreen(); + } + if (width > max_width) + { + width = max_width; + GfxInvalidateScreen(); + } + if (height > max_height) + { + height = max_height; + GfxInvalidateScreen(); + } + } + else + { + min_width = 320; + min_height = 270; + max_width = 320; + max_height = 450; + + if (width < min_width) + { + width = min_width; + Invalidate(); + } + if (height < min_height) + { + height = min_height; + Invalidate(); + } + if (width > max_width) + { + width = max_width; + Invalidate(); + } + if (height > max_height) + { + height = max_height; + Invalidate(); + } + } + + ResizeFrameWithPage(); } - ResizeFrameWithPage(); - widgets[WIDX_THEMES_LIST].right = width - 4; - widgets[WIDX_THEMES_LIST].bottom = height - 0x0F; - - if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS) + void OnUpdate() override { - widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Button; - widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Button; - widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Button; - widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::DropdownMenu; - widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Button; - widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty; - } - else if (_selected_tab == WINDOW_THEMES_TAB_FEATURES) - { - widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Checkbox; - widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Checkbox; - widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Checkbox; - widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Checkbox; - widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty; - - WidgetSetCheckboxValue(*this, WIDX_THEMES_RCT1_RIDE_LIGHTS, ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE); - WidgetSetCheckboxValue(*this, WIDX_THEMES_RCT1_PARK_LIGHTS, ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_PARK); - WidgetSetCheckboxValue( - *this, WIDX_THEMES_RCT1_SCENARIO_FONT, ThemeGetFlags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT); - WidgetSetCheckboxValue( - *this, WIDX_THEMES_RCT1_BOTTOM_TOOLBAR, ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR); - } - else - { - widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::TableHeader; - widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::TableHeader; - widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Scroll; - widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Empty; - widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty; - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - // Widgets - WindowDrawWidgets(*this, dpi); - WindowThemesDrawTabImages(dpi); - - if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS) - { - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_THEMES_PRESETS].top + 1 }, STR_THEMES_LABEL_CURRENT_THEME, {}, - { colours[1] }); - - size_t activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex(); - const utf8* activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex); - auto ft = Formatter(); - ft.Add(activeThemeName); - - auto screenPos = windowPos - + ScreenCoordsXY{ widgets[WIDX_THEMES_PRESETS].left + 1, widgets[WIDX_THEMES_PRESETS].top }; - auto newWidth = windowPos.x + widgets[WIDX_THEMES_PRESETS_DROPDOWN].left - widgets[WIDX_THEMES_PRESETS].left - 4; - - DrawTextEllipsised(dpi, screenPos, newWidth, STR_STRING, ft, { colours[1] }); - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - int16_t newSelectedTab; - int32_t num_items; - auto widget = &widgets[widgetIndex]; - - switch (widgetIndex) - { - case WIDX_THEMES_SETTINGS_TAB: - case WIDX_THEMES_MAIN_UI_TAB: - case WIDX_THEMES_PARK_TAB: - case WIDX_THEMES_TOOLS_TAB: - case WIDX_THEMES_RIDE_PEEPS_TAB: - case WIDX_THEMES_EDITORS_TAB: - case WIDX_THEMES_MISC_TAB: - case WIDX_THEMES_PROMPTS_TAB: - case WIDX_THEMES_FEATURES_TAB: - newSelectedTab = widgetIndex - WIDX_THEMES_SETTINGS_TAB; - if (_selected_tab == newSelectedTab) - break; - _selected_tab = static_cast(newSelectedTab); - scrolls[0].v_top = 0; + frame_no++; + if (frame_no >= window_themes_tab_animation_loops[_selected_tab]) frame_no = 0; - OnResize(); - Invalidate(); - break; - case WIDX_THEMES_PRESETS_DROPDOWN: - ThemeManagerLoadAvailableThemes(); - num_items = static_cast(ThemeManagerGetNumAvailableThemes()); - widget--; - for (int32_t i = 0; i < num_items; i++) - { - gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; - gDropdownItems[i].Args = reinterpret_cast(ThemeManagerGetAvailableThemeName(i)); - } - - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, num_items, widget->width() - 3); - - Dropdown::SetChecked(static_cast(ThemeManagerGetAvailableThemeIndex()), true); - break; - case WIDX_THEMES_RCT1_RIDE_LIGHTS: - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) - { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); - } - else - { - ThemeSetFlags(ThemeGetFlags() ^ UITHEME_FLAG_USE_LIGHTS_RIDE); - ThemeSave(); - WindowInvalidateAll(); - } - break; - case WIDX_THEMES_RCT1_PARK_LIGHTS: - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) - { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); - } - else - { - ThemeSetFlags(ThemeGetFlags() ^ static_cast(UITHEME_FLAG_USE_LIGHTS_PARK)); - ThemeSave(); - WindowInvalidateAll(); - } - break; - case WIDX_THEMES_RCT1_SCENARIO_FONT: - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) - { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); - } - else - { - ThemeSetFlags(ThemeGetFlags() ^ static_cast(UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT)); - ThemeSave(); - WindowInvalidateAll(); - } - break; - case WIDX_THEMES_RCT1_BOTTOM_TOOLBAR: - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) - { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); - } - else - { - ThemeSetFlags(ThemeGetFlags() ^ static_cast(UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR)); - ThemeSave(); - WindowInvalidateAll(); - } + WidgetInvalidate(*this, WIDX_THEMES_SETTINGS_TAB + _selected_tab); } - } - void OnMouseUp(WidgetIndex widgetIndex) override - { - size_t activeAvailableThemeIndex; - const utf8* activeThemeName; - - switch (widgetIndex) + void OnPrepareDraw() override { - case WIDX_THEMES_CLOSE: - Close(); - break; - case WIDX_THEMES_DUPLICATE_BUTTON:; - activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex(); - activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex); - WindowTextInputOpen( - this, widgetIndex, STR_THEMES_ACTION_DUPLICATE, STR_THEMES_PROMPT_ENTER_THEME_NAME, {}, STR_STRING, - reinterpret_cast(activeThemeName), 64); - break; - case WIDX_THEMES_DELETE_BUTTON: - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) - { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); - } - else - { - ThemeDelete(); - } - break; - case WIDX_THEMES_RENAME_BUTTON: - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) - { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); - } - else - { + int32_t pressedWidgets = pressed_widgets + & ~((1LL << WIDX_THEMES_SETTINGS_TAB) | (1LL << WIDX_THEMES_MAIN_UI_TAB) | (1LL << WIDX_THEMES_PARK_TAB) + | (1LL << WIDX_THEMES_TOOLS_TAB) | (1LL << WIDX_THEMES_RIDE_PEEPS_TAB) | (1LL << WIDX_THEMES_EDITORS_TAB) + | (1LL << WIDX_THEMES_MISC_TAB) | (1LL << WIDX_THEMES_PROMPTS_TAB) | (1LL << WIDX_THEMES_FEATURES_TAB)); + WidgetIndex widgetIndex = _selected_tab + WIDX_THEMES_SETTINGS_TAB; + + pressed_widgets = pressedWidgets | (1 << widgetIndex); + + if (WindowFindByClass(WindowClass::Dropdown) == nullptr) + { + _colour_index_1 = -1; + _colour_index_2 = -1; + } + + ResizeFrameWithPage(); + widgets[WIDX_THEMES_LIST].right = width - 4; + widgets[WIDX_THEMES_LIST].bottom = height - 0x0F; + + if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS) + { + widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Button; + widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Button; + widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Button; + widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::DropdownMenu; + widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Button; + widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty; + } + else if (_selected_tab == WINDOW_THEMES_TAB_FEATURES) + { + widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Checkbox; + widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Checkbox; + widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Checkbox; + widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Checkbox; + widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty; + + WidgetSetCheckboxValue(*this, WIDX_THEMES_RCT1_RIDE_LIGHTS, ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE); + WidgetSetCheckboxValue(*this, WIDX_THEMES_RCT1_PARK_LIGHTS, ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_PARK); + WidgetSetCheckboxValue( + *this, WIDX_THEMES_RCT1_SCENARIO_FONT, ThemeGetFlags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT); + WidgetSetCheckboxValue( + *this, WIDX_THEMES_RCT1_BOTTOM_TOOLBAR, ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR); + } + else + { + widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::TableHeader; + widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::TableHeader; + widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Scroll; + widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Empty; + widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + // Widgets + WindowDrawWidgets(*this, dpi); + WindowThemesDrawTabImages(dpi); + + if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS) + { + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_THEMES_PRESETS].top + 1 }, STR_THEMES_LABEL_CURRENT_THEME, + {}, { colours[1] }); + + size_t activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex(); + const utf8* activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex); + auto ft = Formatter(); + ft.Add(activeThemeName); + + auto screenPos = windowPos + + ScreenCoordsXY{ widgets[WIDX_THEMES_PRESETS].left + 1, widgets[WIDX_THEMES_PRESETS].top }; + auto newWidth = windowPos.x + widgets[WIDX_THEMES_PRESETS_DROPDOWN].left - widgets[WIDX_THEMES_PRESETS].left + - 4; + + DrawTextEllipsised(dpi, screenPos, newWidth, STR_STRING, ft, { colours[1] }); + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + int16_t newSelectedTab; + int32_t num_items; + auto widget = &widgets[widgetIndex]; + + switch (widgetIndex) + { + case WIDX_THEMES_SETTINGS_TAB: + case WIDX_THEMES_MAIN_UI_TAB: + case WIDX_THEMES_PARK_TAB: + case WIDX_THEMES_TOOLS_TAB: + case WIDX_THEMES_RIDE_PEEPS_TAB: + case WIDX_THEMES_EDITORS_TAB: + case WIDX_THEMES_MISC_TAB: + case WIDX_THEMES_PROMPTS_TAB: + case WIDX_THEMES_FEATURES_TAB: + newSelectedTab = widgetIndex - WIDX_THEMES_SETTINGS_TAB; + if (_selected_tab == newSelectedTab) + break; + _selected_tab = static_cast(newSelectedTab); + scrolls[0].v_top = 0; + frame_no = 0; + OnResize(); + Invalidate(); + break; + case WIDX_THEMES_PRESETS_DROPDOWN: + ThemeManagerLoadAvailableThemes(); + num_items = static_cast(ThemeManagerGetNumAvailableThemes()); + + widget--; + for (int32_t i = 0; i < num_items; i++) + { + gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; + gDropdownItems[i].Args = reinterpret_cast(ThemeManagerGetAvailableThemeName(i)); + } + + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, num_items, widget->width() - 3); + + Dropdown::SetChecked(static_cast(ThemeManagerGetAvailableThemeIndex()), true); + break; + case WIDX_THEMES_RCT1_RIDE_LIGHTS: + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + { + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); + } + else + { + ThemeSetFlags(ThemeGetFlags() ^ UITHEME_FLAG_USE_LIGHTS_RIDE); + ThemeSave(); + WindowInvalidateAll(); + } + break; + case WIDX_THEMES_RCT1_PARK_LIGHTS: + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + { + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); + } + else + { + ThemeSetFlags(ThemeGetFlags() ^ static_cast(UITHEME_FLAG_USE_LIGHTS_PARK)); + ThemeSave(); + WindowInvalidateAll(); + } + break; + case WIDX_THEMES_RCT1_SCENARIO_FONT: + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + { + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); + } + else + { + ThemeSetFlags( + ThemeGetFlags() ^ static_cast(UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT)); + ThemeSave(); + WindowInvalidateAll(); + } + break; + case WIDX_THEMES_RCT1_BOTTOM_TOOLBAR: + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + { + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); + } + else + { + ThemeSetFlags(ThemeGetFlags() ^ static_cast(UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR)); + ThemeSave(); + WindowInvalidateAll(); + } + } + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + size_t activeAvailableThemeIndex; + const utf8* activeThemeName; + + switch (widgetIndex) + { + case WIDX_THEMES_CLOSE: + Close(); + break; + case WIDX_THEMES_DUPLICATE_BUTTON:; activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex(); activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex); WindowTextInputOpen( - this, widgetIndex, STR_TRACK_MANAGE_RENAME, STR_THEMES_PROMPT_ENTER_THEME_NAME, {}, STR_STRING, + this, widgetIndex, STR_THEMES_ACTION_DUPLICATE, STR_THEMES_PROMPT_ENTER_THEME_NAME, {}, STR_STRING, reinterpret_cast(activeThemeName), 64); - } - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - switch (widgetIndex) - { - case WIDX_THEMES_LIST: - if (selectedIndex != -1) - { - const auto newColour = ThemeOverrideExtendedColour(ColourDropDownIndexToColour(selectedIndex)); - WindowClass wc = GetWindowClassTabIndex(_colour_index_1); - uint8_t colour = ThemeGetColour(wc, _colour_index_2); - colour = (colour & COLOUR_FLAG_TRANSLUCENT) | newColour; - ThemeSetColour(wc, _colour_index_2, colour); - ColourSchemeUpdateAll(); - WindowInvalidateAll(); - _colour_index_1 = -1; - _colour_index_2 = -1; - } - break; - case WIDX_THEMES_PRESETS_DROPDOWN: - if (selectedIndex != -1) - { - ThemeManagerSetActiveAvailableTheme(selectedIndex); - } - break; - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - if (text.empty()) - return; - - switch (widgetIndex) - { - case WIDX_THEMES_DUPLICATE_BUTTON: - case WIDX_THEMES_RENAME_BUTTON: - if (Platform::IsFilenameValid(text)) - { - if (ThemeGetIndexForName(std::string(text).c_str()) == SIZE_MAX) + break; + case WIDX_THEMES_DELETE_BUTTON: + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) { - if (widgetIndex == WIDX_THEMES_DUPLICATE_BUTTON) - { - ThemeDuplicate(std::string(text).c_str()); - } - else - { - ThemeRename(std::string(text).c_str()); - } - Invalidate(); + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); } else { - ContextShowError(STR_THEMES_ERR_NAME_ALREADY_EXISTS, STR_NONE, {}); + ThemeDelete(); } - } - else - { - ContextShowError(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); - } - break; - } - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS || _selected_tab == WINDOW_THEMES_TAB_FEATURES) - return {}; - - int32_t scrollHeight = GetColourSchemeTabCount() * _row_height; - int32_t i = scrollHeight - widgets[WIDX_THEMES_LIST].bottom + widgets[WIDX_THEMES_LIST].top + 21; - if (i < 0) - i = 0; - if (i < scrolls[0].v_top) - { - scrolls[0].v_top = i; - Invalidate(); + break; + case WIDX_THEMES_RENAME_BUTTON: + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + { + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {}); + } + else + { + activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex(); + activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex); + WindowTextInputOpen( + this, widgetIndex, STR_TRACK_MANAGE_RENAME, STR_THEMES_PROMPT_ENTER_THEME_NAME, {}, STR_STRING, + reinterpret_cast(activeThemeName), 64); + } + break; + } } - return { 420, scrollHeight }; - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (screenCoords.y / _row_height < GetColourSchemeTabCount()) + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override { - int32_t y2 = screenCoords.y % _row_height; - _colour_index_1 = screenCoords.y / _row_height; - _colour_index_2 = ((screenCoords.x - _button_offset_x) / 12); - - WindowClass wc = GetWindowClassTabIndex(_colour_index_1); - int32_t numColours = ThemeDescGetNumColours(wc); - if (_colour_index_2 < numColours) + switch (widgetIndex) { - if (screenCoords.x >= _button_offset_x && screenCoords.x < _button_offset_x + 12 * 6 && y2 >= _button_offset_y - && y2 < _button_offset_y + 11) - { - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + case WIDX_THEMES_LIST: + if (selectedIndex != -1) { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_THEMES_DESC_CANT_CHANGE_THIS_THEME, {}); - } - else - { - widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::ColourBtn; - widgets[WIDX_THEMES_COLOURBTN_MASK].left = _button_offset_x + _colour_index_2 * 12 - + widgets[WIDX_THEMES_LIST].left; - widgets[WIDX_THEMES_COLOURBTN_MASK].top = _colour_index_1 * _row_height + _button_offset_y - - scrolls[0].v_top + widgets[WIDX_THEMES_LIST].top; - widgets[WIDX_THEMES_COLOURBTN_MASK].right = widgets[WIDX_THEMES_COLOURBTN_MASK].left + 12; - widgets[WIDX_THEMES_COLOURBTN_MASK].bottom = widgets[WIDX_THEMES_COLOURBTN_MASK].top + 12; - + const auto newColour = ThemeOverrideExtendedColour(ColourDropDownIndexToColour(selectedIndex)); + WindowClass wc = GetWindowClassTabIndex(_colour_index_1); uint8_t colour = ThemeGetColour(wc, _colour_index_2); - WindowDropdownShowColour(this, &(widgets[WIDX_THEMES_COLOURBTN_MASK]), colours[1], colour); - WidgetInvalidate(*this, WIDX_THEMES_LIST); - } - } - else if ( - screenCoords.x >= _button_offset_x && screenCoords.x < _button_offset_x + 12 * 6 - 1 - && y2 >= _check_offset_y && y2 < _check_offset_y + 11) - { - if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) - { - ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_THEMES_DESC_CANT_CHANGE_THIS_THEME, {}); - } - else - { - uint8_t colour = ThemeGetColour(wc, _colour_index_2); - if (colour & COLOUR_FLAG_TRANSLUCENT) - { - colour &= ~COLOUR_FLAG_TRANSLUCENT; - } - else - { - colour |= COLOUR_FLAG_TRANSLUCENT; - } + colour = (colour & COLOUR_FLAG_TRANSLUCENT) | newColour; ThemeSetColour(wc, _colour_index_2, colour); ColourSchemeUpdateAll(); WindowInvalidateAll(); + _colour_index_1 = -1; + _colour_index_2 = -1; } - } + break; + case WIDX_THEMES_PRESETS_DROPDOWN: + if (selectedIndex != -1) + { + ThemeManagerSetActiveAvailableTheme(selectedIndex); + } + break; } } - } - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - ScreenCoordsXY screenCoords; - - if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS || _selected_tab == WINDOW_THEMES_TAB_FEATURES) - return; - - if ((colours[1] & 0x80) == 0) - // GfxFillRect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, - // ColourMapA[colours[1]].mid_light); - GfxClear(&dpi, ColourMapA[colours[1]].mid_light); - screenCoords.y = 0; - for (int32_t i = 0; i < GetColourSchemeTabCount(); i++) + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { - if (screenCoords.y > dpi.y + dpi.height) - { - break; - } - if (screenCoords.y + _row_height >= dpi.y) - { - if (i + 1 < GetColourSchemeTabCount()) - { - int32_t colour = colours[1]; + if (text.empty()) + return; - auto leftTop = ScreenCoordsXY{ 0, screenCoords.y + _row_height - 2 }; - auto rightBottom = ScreenCoordsXY{ widgets[WIDX_THEMES_LIST].right, screenCoords.y + _row_height - 2 }; - auto yPixelOffset = ScreenCoordsXY{ 0, 1 }; - - if (colour & COLOUR_FLAG_TRANSLUCENT) + switch (widgetIndex) + { + case WIDX_THEMES_DUPLICATE_BUTTON: + case WIDX_THEMES_RENAME_BUTTON: + if (Platform::IsFilenameValid(text)) { - TranslucentWindowPalette windowPalette = TranslucentWindowPalettes[BASE_COLOUR(colour)]; - - GfxFilterRect(dpi, { leftTop, rightBottom }, windowPalette.highlight); - GfxFilterRect(dpi, { leftTop + yPixelOffset, rightBottom + yPixelOffset }, windowPalette.shadow); + if (ThemeGetIndexForName(std::string(text).c_str()) == SIZE_MAX) + { + if (widgetIndex == WIDX_THEMES_DUPLICATE_BUTTON) + { + ThemeDuplicate(std::string(text).c_str()); + } + else + { + ThemeRename(std::string(text).c_str()); + } + Invalidate(); + } + else + { + ContextShowError(STR_THEMES_ERR_NAME_ALREADY_EXISTS, STR_NONE, {}); + } } else { - colour = ColourMapA[colours[1]].mid_dark; - GfxFillRect(dpi, { leftTop, rightBottom }, colour); - - colour = ColourMapA[colours[1]].lightest; - GfxFillRect(dpi, { leftTop + yPixelOffset, rightBottom + yPixelOffset }, colour); + ContextShowError(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); } - } + break; + } + } - WindowClass wc = GetWindowClassTabIndex(i); + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS || _selected_tab == WINDOW_THEMES_TAB_FEATURES) + return {}; + + int32_t scrollHeight = GetColourSchemeTabCount() * _row_height; + int32_t i = scrollHeight - widgets[WIDX_THEMES_LIST].bottom + widgets[WIDX_THEMES_LIST].top + 21; + if (i < 0) + i = 0; + if (i < scrolls[0].v_top) + { + scrolls[0].v_top = i; + Invalidate(); + } + + return { 420, scrollHeight }; + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + if (screenCoords.y / _row_height < GetColourSchemeTabCount()) + { + int32_t y2 = screenCoords.y % _row_height; + _colour_index_1 = screenCoords.y / _row_height; + _colour_index_2 = ((screenCoords.x - _button_offset_x) / 12); + + WindowClass wc = GetWindowClassTabIndex(_colour_index_1); int32_t numColours = ThemeDescGetNumColours(wc); - for (uint8_t j = 0; j < numColours; j++) + if (_colour_index_2 < numColours) { - DrawTextBasic(dpi, { 2, screenCoords.y + 4 }, ThemeDescGetName(wc), {}, { colours[1] }); - - uint8_t colour = ThemeGetColour(wc, j); - const bool isPressed = (i == _colour_index_1 && j == _colour_index_2); - auto image = ImageId( - isPressed ? SPR_PALETTE_BTN_PRESSED : SPR_PALETTE_BTN, colour & ~COLOUR_FLAG_TRANSLUCENT); - GfxDrawSprite(dpi, image, { _button_offset_x + 12 * j, screenCoords.y + _button_offset_y }); - - ScreenCoordsXY topLeft{ _button_offset_x + 12 * j, screenCoords.y + _check_offset_y }; - ScreenCoordsXY bottomRight{ _button_offset_x + 12 * j + 9, screenCoords.y + _check_offset_y + 10 }; - GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_E0); - if (colour & COLOUR_FLAG_TRANSLUCENT) + if (screenCoords.x >= _button_offset_x && screenCoords.x < _button_offset_x + 12 * 6 + && y2 >= _button_offset_y && y2 < _button_offset_y + 11) { - GfxDrawString( - dpi, topLeft, static_cast(CheckBoxMarkString), - { static_cast(colours[1] & 0x7F), FontStyle::Medium, TextDarkness::Dark }); + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + { + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_THEMES_DESC_CANT_CHANGE_THIS_THEME, {}); + } + else + { + widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::ColourBtn; + widgets[WIDX_THEMES_COLOURBTN_MASK].left = _button_offset_x + _colour_index_2 * 12 + + widgets[WIDX_THEMES_LIST].left; + widgets[WIDX_THEMES_COLOURBTN_MASK].top = _colour_index_1 * _row_height + _button_offset_y + - scrolls[0].v_top + widgets[WIDX_THEMES_LIST].top; + widgets[WIDX_THEMES_COLOURBTN_MASK].right = widgets[WIDX_THEMES_COLOURBTN_MASK].left + 12; + widgets[WIDX_THEMES_COLOURBTN_MASK].bottom = widgets[WIDX_THEMES_COLOURBTN_MASK].top + 12; + + uint8_t colour = ThemeGetColour(wc, _colour_index_2); + WindowDropdownShowColour(this, &(widgets[WIDX_THEMES_COLOURBTN_MASK]), colours[1], colour); + WidgetInvalidate(*this, WIDX_THEMES_LIST); + } + } + else if ( + screenCoords.x >= _button_offset_x && screenCoords.x < _button_offset_x + 12 * 6 - 1 + && y2 >= _check_offset_y && y2 < _check_offset_y + 11) + { + if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED) + { + ContextShowError(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_THEMES_DESC_CANT_CHANGE_THIS_THEME, {}); + } + else + { + uint8_t colour = ThemeGetColour(wc, _colour_index_2); + if (colour & COLOUR_FLAG_TRANSLUCENT) + { + colour &= ~COLOUR_FLAG_TRANSLUCENT; + } + else + { + colour |= COLOUR_FLAG_TRANSLUCENT; + } + ThemeSetColour(wc, _colour_index_2, colour); + ColourSchemeUpdateAll(); + WindowInvalidateAll(); + } } } } - - screenCoords.y += _row_height; } - } + + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override + { + ScreenCoordsXY screenCoords; + + if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS || _selected_tab == WINDOW_THEMES_TAB_FEATURES) + return; + + if ((colours[1] & 0x80) == 0) + // GfxFillRect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, + // ColourMapA[colours[1]].mid_light); + GfxClear(&dpi, ColourMapA[colours[1]].mid_light); + screenCoords.y = 0; + for (int32_t i = 0; i < GetColourSchemeTabCount(); i++) + { + if (screenCoords.y > dpi.y + dpi.height) + { + break; + } + if (screenCoords.y + _row_height >= dpi.y) + { + if (i + 1 < GetColourSchemeTabCount()) + { + int32_t colour = colours[1]; + + auto leftTop = ScreenCoordsXY{ 0, screenCoords.y + _row_height - 2 }; + auto rightBottom = ScreenCoordsXY{ widgets[WIDX_THEMES_LIST].right, screenCoords.y + _row_height - 2 }; + auto yPixelOffset = ScreenCoordsXY{ 0, 1 }; + + if (colour & COLOUR_FLAG_TRANSLUCENT) + { + TranslucentWindowPalette windowPalette = TranslucentWindowPalettes[BASE_COLOUR(colour)]; + + GfxFilterRect(dpi, { leftTop, rightBottom }, windowPalette.highlight); + GfxFilterRect(dpi, { leftTop + yPixelOffset, rightBottom + yPixelOffset }, windowPalette.shadow); + } + else + { + colour = ColourMapA[colours[1]].mid_dark; + GfxFillRect(dpi, { leftTop, rightBottom }, colour); + + colour = ColourMapA[colours[1]].lightest; + GfxFillRect(dpi, { leftTop + yPixelOffset, rightBottom + yPixelOffset }, colour); + } + } + + WindowClass wc = GetWindowClassTabIndex(i); + int32_t numColours = ThemeDescGetNumColours(wc); + for (uint8_t j = 0; j < numColours; j++) + { + DrawTextBasic(dpi, { 2, screenCoords.y + 4 }, ThemeDescGetName(wc), {}, { colours[1] }); + + uint8_t colour = ThemeGetColour(wc, j); + const bool isPressed = (i == _colour_index_1 && j == _colour_index_2); + auto image = ImageId( + isPressed ? SPR_PALETTE_BTN_PRESSED : SPR_PALETTE_BTN, colour & ~COLOUR_FLAG_TRANSLUCENT); + GfxDrawSprite(dpi, image, { _button_offset_x + 12 * j, screenCoords.y + _button_offset_y }); + + ScreenCoordsXY topLeft{ _button_offset_x + 12 * j, screenCoords.y + _check_offset_y }; + ScreenCoordsXY bottomRight{ _button_offset_x + 12 * j + 9, screenCoords.y + _check_offset_y + 10 }; + GfxFillRectInset(dpi, { topLeft, bottomRight }, colours[1], INSET_RECT_F_E0); + if (colour & COLOUR_FLAG_TRANSLUCENT) + { + GfxDrawString( + dpi, topLeft, static_cast(CheckBoxMarkString), + { static_cast(colours[1] & 0x7F), FontStyle::Medium, TextDarkness::Dark }); + } + } + } + + screenCoords.y += _row_height; + } + } #pragma endregion - void WindowThemesInitVars() - { - _selected_tab = WINDOW_THEMES_TAB_SETTINGS; - } - - WindowClass GetWindowClassTabIndex(int32_t index) - { - WindowClass* classes = window_themes_tab_classes[_selected_tab]; - return classes[index]; - } - - int32_t GetColourSchemeTabCount() - { - switch (_selected_tab) + void WindowThemesInitVars() { - case 1: - return sizeof(window_themes_tab_1_classes); - case 2: - return sizeof(window_themes_tab_2_classes); - case 3: - return sizeof(window_themes_tab_3_classes); - case 4: - return sizeof(window_themes_tab_4_classes); - case 5: - return sizeof(window_themes_tab_5_classes); - case 6: - return sizeof(window_themes_tab_6_classes); - case 7: - return sizeof(window_themes_tab_7_classes); + _selected_tab = WINDOW_THEMES_TAB_SETTINGS; } - return 0; - } - void WindowThemesDrawTabImages(DrawPixelInfo& dpi) - { - for (int32_t i = 0; i < WINDOW_THEMES_TAB_COUNT; i++) + WindowClass GetWindowClassTabIndex(int32_t index) { - int32_t sprite_idx = window_themes_tab_sprites[i]; - if (_selected_tab == i) - sprite_idx += frame_no / window_themes_tab_animation_divisor[_selected_tab]; - GfxDrawSprite( - dpi, ImageId(sprite_idx), - windowPos - + ScreenCoordsXY{ widgets[WIDX_THEMES_SETTINGS_TAB + i].left, widgets[WIDX_THEMES_SETTINGS_TAB + i].top }); + WindowClass* classes = window_themes_tab_classes[_selected_tab]; + return classes[index]; } - } -}; -WindowBase* WindowThemesOpen() -{ - WindowBase* window; + int32_t GetColourSchemeTabCount() + { + switch (_selected_tab) + { + case 1: + return sizeof(window_themes_tab_1_classes); + case 2: + return sizeof(window_themes_tab_2_classes); + case 3: + return sizeof(window_themes_tab_3_classes); + case 4: + return sizeof(window_themes_tab_4_classes); + case 5: + return sizeof(window_themes_tab_5_classes); + case 6: + return sizeof(window_themes_tab_6_classes); + case 7: + return sizeof(window_themes_tab_7_classes); + } + return 0; + } + + void WindowThemesDrawTabImages(DrawPixelInfo& dpi) + { + for (int32_t i = 0; i < WINDOW_THEMES_TAB_COUNT; i++) + { + int32_t sprite_idx = window_themes_tab_sprites[i]; + if (_selected_tab == i) + sprite_idx += frame_no / window_themes_tab_animation_divisor[_selected_tab]; + GfxDrawSprite( + dpi, ImageId(sprite_idx), + windowPos + + ScreenCoordsXY{ widgets[WIDX_THEMES_SETTINGS_TAB + i].left, + widgets[WIDX_THEMES_SETTINGS_TAB + i].top }); + } + } + }; + + WindowBase* WindowThemesOpen() + { + WindowBase* window; + + // Check if window is already open + window = WindowBringToFrontByClass(WindowClass::Themes); + if (window != nullptr) + return window; + + window = WindowCreate(WindowClass::Themes, 320, 107, WF_10 | WF_RESIZABLE); - // Check if window is already open - window = WindowBringToFrontByClass(WindowClass::Themes); - if (window != nullptr) return window; - - window = WindowCreate(WindowClass::Themes, 320, 107, WF_10 | WF_RESIZABLE); - - return window; -} + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TileInspector.cpp b/src/openrct2-ui/windows/TileInspector.cpp index 25192c5958..56831298ba 100644 --- a/src/openrct2-ui/windows/TileInspector.cpp +++ b/src/openrct2-ui/windows/TileInspector.cpp @@ -41,204 +41,206 @@ #include #include -static constexpr StringId EntranceTypeStringIds[] = { - STR_TILE_INSPECTOR_ENTRANCE_TYPE_RIDE_ENTRANCE, - STR_TILE_INSPECTOR_ENTRANCE_TYPE_RIDE_EXIT, - STR_TILE_INSPECTOR_ENTRANCE_TYPE_PARK_ENTRANC, -}; - -static constexpr StringId ParkEntrancePartStringIds[] = { - STR_TILE_INSPECTOR_ENTRANCE_MIDDLE, - STR_TILE_INSPECTOR_ENTRANCE_LEFT, - STR_TILE_INSPECTOR_ENTRANCE_RIGHT, -}; - -static constexpr StringId WallSlopeStringIds[] = { - STR_TILE_INSPECTOR_WALL_FLAT, - STR_TILE_INSPECTOR_WALL_SLOPED_LEFT, - STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT, - STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME, -}; - -enum WindowTileInspectorWidgetIdx +namespace OpenRCT2::Ui::Windows { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_LIST, - WIDX_SPINNER_X, - WIDX_SPINNER_X_INCREASE, - WIDX_SPINNER_X_DECREASE, - WIDX_SPINNER_Y, - WIDX_SPINNER_Y_INCREASE, - WIDX_SPINNER_Y_DECREASE, - WIDX_BUTTON_REMOVE, - WIDX_BUTTON_MOVE_UP, - WIDX_BUTTON_MOVE_DOWN, - WIDX_BUTTON_ROTATE, - WIDX_BUTTON_SORT, - WIDX_BUTTON_PASTE, - WIDX_BUTTON_COPY, - WIDX_COLUMN_INVISIBLE, - WIDX_COLUMN_TYPE, - WIDX_COLUMN_BASEHEIGHT, - WIDX_COLUMN_CLEARANCEHEIGHT, - WIDX_COLUMN_DIRECTION, - WIDX_COLUMN_GHOSTFLAG, - WIDX_COLUMN_LASTFLAG, - WIDX_GROUPBOX_DETAILS, - WIDX_GROUPBOX_PROPERTIES, + static constexpr StringId EntranceTypeStringIds[] = { + STR_TILE_INSPECTOR_ENTRANCE_TYPE_RIDE_ENTRANCE, + STR_TILE_INSPECTOR_ENTRANCE_TYPE_RIDE_EXIT, + STR_TILE_INSPECTOR_ENTRANCE_TYPE_PARK_ENTRANC, + }; - PAGE_WIDGETS, + static constexpr StringId ParkEntrancePartStringIds[] = { + STR_TILE_INSPECTOR_ENTRANCE_MIDDLE, + STR_TILE_INSPECTOR_ENTRANCE_LEFT, + STR_TILE_INSPECTOR_ENTRANCE_RIGHT, + }; - // Surface - WIDX_SURFACE_SPINNER_HEIGHT = PAGE_WIDGETS, - WIDX_SURFACE_SPINNER_HEIGHT_INCREASE, - WIDX_SURFACE_SPINNER_HEIGHT_DECREASE, - WIDX_SURFACE_BUTTON_REMOVE_FENCES, - WIDX_SURFACE_BUTTON_RESTORE_FENCES, - WIDX_SURFACE_CHECK_CORNER_N, - WIDX_SURFACE_CHECK_CORNER_E, - WIDX_SURFACE_CHECK_CORNER_S, - WIDX_SURFACE_CHECK_CORNER_W, - WIDX_SURFACE_CHECK_DIAGONAL, + static constexpr StringId WallSlopeStringIds[] = { + STR_TILE_INSPECTOR_WALL_FLAT, + STR_TILE_INSPECTOR_WALL_SLOPED_LEFT, + STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT, + STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME, + }; - // Path - WIDX_PATH_SPINNER_HEIGHT = PAGE_WIDGETS, - WIDX_PATH_SPINNER_HEIGHT_INCREASE, - WIDX_PATH_SPINNER_HEIGHT_DECREASE, - WIDX_PATH_CHECK_BROKEN, - WIDX_PATH_CHECK_SLOPED, - WIDX_PATH_CHECK_JUNCTION_RAILINGS, - WIDX_PATH_CHECK_EDGE_NE, // Note: This is NOT named after the world orientation, but after the way - WIDX_PATH_CHECK_EDGE_E, // it looks in the window (top corner is north). Their order is important, - WIDX_PATH_CHECK_EDGE_SE, // as this is the same order paths use for their corners / edges. - WIDX_PATH_CHECK_EDGE_S, // N - WIDX_PATH_CHECK_EDGE_SW, // NW-------NE - WIDX_PATH_CHECK_EDGE_W, // W ------------- E - WIDX_PATH_CHECK_EDGE_NW, // SW-------SE - WIDX_PATH_CHECK_EDGE_N, // S + enum WindowTileInspectorWidgetIdx + { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_LIST, + WIDX_SPINNER_X, + WIDX_SPINNER_X_INCREASE, + WIDX_SPINNER_X_DECREASE, + WIDX_SPINNER_Y, + WIDX_SPINNER_Y_INCREASE, + WIDX_SPINNER_Y_DECREASE, + WIDX_BUTTON_REMOVE, + WIDX_BUTTON_MOVE_UP, + WIDX_BUTTON_MOVE_DOWN, + WIDX_BUTTON_ROTATE, + WIDX_BUTTON_SORT, + WIDX_BUTTON_PASTE, + WIDX_BUTTON_COPY, + WIDX_COLUMN_INVISIBLE, + WIDX_COLUMN_TYPE, + WIDX_COLUMN_BASEHEIGHT, + WIDX_COLUMN_CLEARANCEHEIGHT, + WIDX_COLUMN_DIRECTION, + WIDX_COLUMN_GHOSTFLAG, + WIDX_COLUMN_LASTFLAG, + WIDX_GROUPBOX_DETAILS, + WIDX_GROUPBOX_PROPERTIES, - // Track - WIDX_TRACK_CHECK_APPLY_TO_ALL = PAGE_WIDGETS, - WIDX_TRACK_SPINNER_HEIGHT, - WIDX_TRACK_SPINNER_HEIGHT_INCREASE, - WIDX_TRACK_SPINNER_HEIGHT_DECREASE, - WIDX_TRACK_CHECK_CHAIN_LIFT, - WIDX_TRACK_CHECK_BRAKE_CLOSED, - WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, + PAGE_WIDGETS, - // Scenery - WIDX_SCENERY_SPINNER_HEIGHT = PAGE_WIDGETS, - WIDX_SCENERY_SPINNER_HEIGHT_INCREASE, - WIDX_SCENERY_SPINNER_HEIGHT_DECREASE, - WIDX_SCENERY_CHECK_QUARTER_N, - WIDX_SCENERY_CHECK_QUARTER_E, - WIDX_SCENERY_CHECK_QUARTER_S, - WIDX_SCENERY_CHECK_QUARTER_W, - WIDX_SCENERY_CHECK_COLLISION_N, - WIDX_SCENERY_CHECK_COLLISION_E, - WIDX_SCENERY_CHECK_COLLISION_S, - WIDX_SCENERY_CHECK_COLLISION_W, + // Surface + WIDX_SURFACE_SPINNER_HEIGHT = PAGE_WIDGETS, + WIDX_SURFACE_SPINNER_HEIGHT_INCREASE, + WIDX_SURFACE_SPINNER_HEIGHT_DECREASE, + WIDX_SURFACE_BUTTON_REMOVE_FENCES, + WIDX_SURFACE_BUTTON_RESTORE_FENCES, + WIDX_SURFACE_CHECK_CORNER_N, + WIDX_SURFACE_CHECK_CORNER_E, + WIDX_SURFACE_CHECK_CORNER_S, + WIDX_SURFACE_CHECK_CORNER_W, + WIDX_SURFACE_CHECK_DIAGONAL, - // Entrance - WIDX_ENTRANCE_SPINNER_HEIGHT = PAGE_WIDGETS, - WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE, - WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE, - WIDX_ENTRANCE_BUTTON_MAKE_USABLE, + // Path + WIDX_PATH_SPINNER_HEIGHT = PAGE_WIDGETS, + WIDX_PATH_SPINNER_HEIGHT_INCREASE, + WIDX_PATH_SPINNER_HEIGHT_DECREASE, + WIDX_PATH_CHECK_BROKEN, + WIDX_PATH_CHECK_SLOPED, + WIDX_PATH_CHECK_JUNCTION_RAILINGS, + WIDX_PATH_CHECK_EDGE_NE, // Note: This is NOT named after the world orientation, but after the way + WIDX_PATH_CHECK_EDGE_E, // it looks in the window (top corner is north). Their order is important, + WIDX_PATH_CHECK_EDGE_SE, // as this is the same order paths use for their corners / edges. + WIDX_PATH_CHECK_EDGE_S, // N + WIDX_PATH_CHECK_EDGE_SW, // NW-------NE + WIDX_PATH_CHECK_EDGE_W, // W ------------- E + WIDX_PATH_CHECK_EDGE_NW, // SW-------SE + WIDX_PATH_CHECK_EDGE_N, // S - // Wall - WIDX_WALL_SPINNER_HEIGHT = PAGE_WIDGETS, - WIDX_WALL_SPINNER_HEIGHT_INCREASE, - WIDX_WALL_SPINNER_HEIGHT_DECREASE, - WIDX_WALL_DROPDOWN_SLOPE, - WIDX_WALL_DROPDOWN_SLOPE_BUTTON, - WIDX_WALL_SPINNER_ANIMATION_FRAME, - WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE, - WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE, + // Track + WIDX_TRACK_CHECK_APPLY_TO_ALL = PAGE_WIDGETS, + WIDX_TRACK_SPINNER_HEIGHT, + WIDX_TRACK_SPINNER_HEIGHT_INCREASE, + WIDX_TRACK_SPINNER_HEIGHT_DECREASE, + WIDX_TRACK_CHECK_CHAIN_LIFT, + WIDX_TRACK_CHECK_BRAKE_CLOSED, + WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, - // Large - WIDX_LARGE_SCENERY_SPINNER_HEIGHT = PAGE_WIDGETS, - WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE, - WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE, + // Scenery + WIDX_SCENERY_SPINNER_HEIGHT = PAGE_WIDGETS, + WIDX_SCENERY_SPINNER_HEIGHT_INCREASE, + WIDX_SCENERY_SPINNER_HEIGHT_DECREASE, + WIDX_SCENERY_CHECK_QUARTER_N, + WIDX_SCENERY_CHECK_QUARTER_E, + WIDX_SCENERY_CHECK_QUARTER_S, + WIDX_SCENERY_CHECK_QUARTER_W, + WIDX_SCENERY_CHECK_COLLISION_N, + WIDX_SCENERY_CHECK_COLLISION_E, + WIDX_SCENERY_CHECK_COLLISION_S, + WIDX_SCENERY_CHECK_COLLISION_W, - // Banner - WIDX_BANNER_SPINNER_HEIGHT = PAGE_WIDGETS, - WIDX_BANNER_SPINNER_HEIGHT_INCREASE, - WIDX_BANNER_SPINNER_HEIGHT_DECREASE, - WIDX_BANNER_CHECK_BLOCK_NE, - WIDX_BANNER_CHECK_BLOCK_SE, - WIDX_BANNER_CHECK_BLOCK_SW, - WIDX_BANNER_CHECK_BLOCK_NW, -}; + // Entrance + WIDX_ENTRANCE_SPINNER_HEIGHT = PAGE_WIDGETS, + WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE, + WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE, + WIDX_ENTRANCE_BUTTON_MAKE_USABLE, -static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_ROTATE == WIDX_BUTTON_ROTATE); -static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_COPY == WIDX_BUTTON_COPY); -static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_PASTE == WIDX_BUTTON_PASTE); -static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_REMOVE == WIDX_BUTTON_REMOVE); -static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_MOVE_UP == WIDX_BUTTON_MOVE_UP); -static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_MOVE_DOWN == WIDX_BUTTON_MOVE_DOWN); -static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_X_INCREASE == WIDX_SPINNER_X_INCREASE); -static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_X_DECREASE == WIDX_SPINNER_X_DECREASE); -static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_Y_INCREASE == WIDX_SPINNER_Y_INCREASE); -static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_Y_DECREASE == WIDX_SPINNER_Y_DECREASE); + // Wall + WIDX_WALL_SPINNER_HEIGHT = PAGE_WIDGETS, + WIDX_WALL_SPINNER_HEIGHT_INCREASE, + WIDX_WALL_SPINNER_HEIGHT_DECREASE, + WIDX_WALL_DROPDOWN_SLOPE, + WIDX_WALL_DROPDOWN_SLOPE_BUTTON, + WIDX_WALL_SPINNER_ANIMATION_FRAME, + WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE, + WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE, + + // Large + WIDX_LARGE_SCENERY_SPINNER_HEIGHT = PAGE_WIDGETS, + WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE, + WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE, + + // Banner + WIDX_BANNER_SPINNER_HEIGHT = PAGE_WIDGETS, + WIDX_BANNER_SPINNER_HEIGHT_INCREASE, + WIDX_BANNER_SPINNER_HEIGHT_DECREASE, + WIDX_BANNER_CHECK_BLOCK_NE, + WIDX_BANNER_CHECK_BLOCK_SE, + WIDX_BANNER_CHECK_BLOCK_SW, + WIDX_BANNER_CHECK_BLOCK_NW, + }; + + static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_ROTATE == WIDX_BUTTON_ROTATE); + static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_COPY == WIDX_BUTTON_COPY); + static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_PASTE == WIDX_BUTTON_PASTE); + static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_REMOVE == WIDX_BUTTON_REMOVE); + static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_MOVE_UP == WIDX_BUTTON_MOVE_UP); + static_assert(WC_TILE_INSPECTOR__WIDX_BUTTON_MOVE_DOWN == WIDX_BUTTON_MOVE_DOWN); + static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_X_INCREASE == WIDX_SPINNER_X_INCREASE); + static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_X_DECREASE == WIDX_SPINNER_X_DECREASE); + static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_Y_INCREASE == WIDX_SPINNER_Y_INCREASE); + static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_Y_DECREASE == WIDX_SPINNER_Y_DECREASE); #pragma region MEASUREMENTS -static constexpr StringId WINDOW_TITLE = STR_TILE_INSPECTOR_TITLE; -// Window sizes -static constexpr int32_t WW = 400; -static constexpr int32_t WH = 170; + static constexpr StringId WINDOW_TITLE = STR_TILE_INSPECTOR_TITLE; + // Window sizes + static constexpr int32_t WW = 400; + static constexpr int32_t WH = 170; -constexpr int32_t MIN_WW = WW; -constexpr int32_t MAX_WW = WW; -constexpr int32_t MIN_WH = 130; -constexpr int32_t MAX_WH = 800; + constexpr int32_t MIN_WW = WW; + constexpr int32_t MAX_WW = WW; + constexpr int32_t MIN_WH = 130; + constexpr int32_t MAX_WH = 800; -// Button space for top buttons -constexpr auto ToolbarButtonAnchor = ScreenCoordsXY{ WW - 27, 17 }; -constexpr auto ToolbarButtonSize = ScreenSize{ 24, 24 }; -constexpr auto ToolbarButtonHalfSize = ScreenSize{ 24, 12 }; -constexpr auto ToolbarButtonOffsetX = ScreenSize{ -24, 0 }; + // Button space for top buttons + constexpr auto ToolbarButtonAnchor = ScreenCoordsXY{ WW - 27, 17 }; + constexpr auto ToolbarButtonSize = ScreenSize{ 24, 24 }; + constexpr auto ToolbarButtonHalfSize = ScreenSize{ 24, 12 }; + constexpr auto ToolbarButtonOffsetX = ScreenSize{ -24, 0 }; -// List's column offsets -constexpr auto InvisibleFlagColumnXY = ScreenCoordsXY{ 3, 42 }; -constexpr auto InvisibleFlagColumnSize = ScreenSize{ 20, 14 }; -constexpr auto TypeColumnXY = InvisibleFlagColumnXY + ScreenSize{ InvisibleFlagColumnSize.width, 0 }; -constexpr auto TypeColumnSize = ScreenSize{ 252, 14 }; -constexpr auto BaseHeightColumnXY = TypeColumnXY + ScreenSize{ TypeColumnSize.width, 0 }; -constexpr auto BaseHeightColumnSize = ScreenSize{ 30, 14 }; -constexpr auto ClearanceHeightColumnXY = BaseHeightColumnXY + ScreenCoordsXY{ BaseHeightColumnSize.width, 0 }; -constexpr auto ClearanceHeightColumnSize = ScreenSize{ 30, 14 }; -constexpr auto DirectionColumnXY = ClearanceHeightColumnXY + ScreenCoordsXY{ ClearanceHeightColumnSize.width, 0 }; -constexpr auto DirectionColumnSize = ScreenSize{ 15, 14 }; -constexpr auto GhostFlagColumnXY = DirectionColumnXY + ScreenCoordsXY{ DirectionColumnSize.width, 0 }; -constexpr auto GhostFlagColumnSize = ScreenSize{ 15, 14 }; -constexpr auto LastFlagColumnXY = GhostFlagColumnXY + ScreenCoordsXY{ GhostFlagColumnSize.width, 0 }; -constexpr auto LastFlagColumnSize = ScreenSize{ 32, 14 }; + // List's column offsets + constexpr auto InvisibleFlagColumnXY = ScreenCoordsXY{ 3, 42 }; + constexpr auto InvisibleFlagColumnSize = ScreenSize{ 20, 14 }; + constexpr auto TypeColumnXY = InvisibleFlagColumnXY + ScreenSize{ InvisibleFlagColumnSize.width, 0 }; + constexpr auto TypeColumnSize = ScreenSize{ 252, 14 }; + constexpr auto BaseHeightColumnXY = TypeColumnXY + ScreenSize{ TypeColumnSize.width, 0 }; + constexpr auto BaseHeightColumnSize = ScreenSize{ 30, 14 }; + constexpr auto ClearanceHeightColumnXY = BaseHeightColumnXY + ScreenCoordsXY{ BaseHeightColumnSize.width, 0 }; + constexpr auto ClearanceHeightColumnSize = ScreenSize{ 30, 14 }; + constexpr auto DirectionColumnXY = ClearanceHeightColumnXY + ScreenCoordsXY{ ClearanceHeightColumnSize.width, 0 }; + constexpr auto DirectionColumnSize = ScreenSize{ 15, 14 }; + constexpr auto GhostFlagColumnXY = DirectionColumnXY + ScreenCoordsXY{ DirectionColumnSize.width, 0 }; + constexpr auto GhostFlagColumnSize = ScreenSize{ 15, 14 }; + constexpr auto LastFlagColumnXY = GhostFlagColumnXY + ScreenCoordsXY{ GhostFlagColumnSize.width, 0 }; + constexpr auto LastFlagColumnSize = ScreenSize{ 32, 14 }; -constexpr int32_t PADDING_BOTTOM = 15; -constexpr int32_t GROUPBOX_PADDING = 6; -constexpr int32_t HORIZONTAL_GROUPBOX_PADDING = 5; -constexpr int32_t VERTICAL_GROUPBOX_PADDING = 4; -constexpr auto PropertyButtonSize = ScreenSize{ 130, 18 }; -constexpr auto PropertyFullWidth = ScreenSize{ 370, 18 }; + constexpr int32_t PADDING_BOTTOM = 15; + constexpr int32_t GROUPBOX_PADDING = 6; + constexpr int32_t HORIZONTAL_GROUPBOX_PADDING = 5; + constexpr int32_t VERTICAL_GROUPBOX_PADDING = 4; + constexpr auto PropertyButtonSize = ScreenSize{ 130, 18 }; + constexpr auto PropertyFullWidth = ScreenSize{ 370, 18 }; #pragma endregion -constexpr ScreenCoordsXY PropertyRowCol(ScreenCoordsXY anchor, int32_t row, int32_t column) -{ - return anchor - + ScreenCoordsXY{ column * (PropertyButtonSize.width + HORIZONTAL_GROUPBOX_PADDING), - row * (PropertyButtonSize.height + VERTICAL_GROUPBOX_PADDING) }; -} + constexpr ScreenCoordsXY PropertyRowCol(ScreenCoordsXY anchor, int32_t row, int32_t column) + { + return anchor + + ScreenCoordsXY{ column * (PropertyButtonSize.width + HORIZONTAL_GROUPBOX_PADDING), + row * (PropertyButtonSize.height + VERTICAL_GROUPBOX_PADDING) }; + } -constexpr ScreenCoordsXY CheckboxGroupOffset( - ScreenCoordsXY anchorPoint, int16_t horizontalMultiplier, int16_t verticalMultiplier) -{ - return anchorPoint + ScreenCoordsXY{ 14 * horizontalMultiplier, 7 * verticalMultiplier }; -} + constexpr ScreenCoordsXY CheckboxGroupOffset( + ScreenCoordsXY anchorPoint, int16_t horizontalMultiplier, int16_t verticalMultiplier) + { + return anchorPoint + ScreenCoordsXY{ 14 * horizontalMultiplier, 7 * verticalMultiplier }; + } -// clang-format off + // clang-format off // Macros for easily obtaining the top and bottom of a widget inside a properties group box #define GBBT(GROUPTOP, row) ((GROUPTOP) + 14 + row * (PropertyButtonSize.height + VERTICAL_GROUPBOX_PADDING)) #define GBBB(GROUPTOP, row) (GBBT((GROUPTOP), row) + PropertyButtonSize.height) @@ -404,46 +406,46 @@ static Widget *PageWidgets[] = { LargeSceneryWidgets, BannerWidgets, }; -// clang-format on + // clang-format on -struct TileInspectorGroupboxSettings -{ - // Offsets from the bottom of the window - int16_t details_top_offset, details_bottom_offset; - int16_t properties_top_offset, properties_bottom_offset; - // String to be displayed in the details groupbox - StringId string_id; -}; + struct TileInspectorGroupboxSettings + { + // Offsets from the bottom of the window + int16_t details_top_offset, details_bottom_offset; + int16_t properties_top_offset, properties_bottom_offset; + // String to be displayed in the details groupbox + StringId string_id; + }; -static constexpr TileInspectorGroupboxSettings MakeGroupboxSettings( - int16_t detailsHeight, int16_t propertiesHeight, StringId stringId) -{ - TileInspectorGroupboxSettings settings{}; - decltype(settings.properties_bottom_offset) offsetSum = 0; - settings.properties_bottom_offset = (offsetSum += PADDING_BOTTOM); - settings.properties_top_offset = (offsetSum += propertiesHeight); - settings.details_bottom_offset = (offsetSum += GROUPBOX_PADDING); - settings.details_top_offset = (offsetSum += detailsHeight); - settings.string_id = stringId; - return settings; -} + static constexpr TileInspectorGroupboxSettings MakeGroupboxSettings( + int16_t detailsHeight, int16_t propertiesHeight, StringId stringId) + { + TileInspectorGroupboxSettings settings{}; + decltype(settings.properties_bottom_offset) offsetSum = 0; + settings.properties_bottom_offset = (offsetSum += PADDING_BOTTOM); + settings.properties_top_offset = (offsetSum += propertiesHeight); + settings.details_bottom_offset = (offsetSum += GROUPBOX_PADDING); + settings.details_top_offset = (offsetSum += detailsHeight); + settings.string_id = stringId; + return settings; + } -static constexpr TileInspectorGroupboxSettings PageGroupBoxSettings[] = { - MakeGroupboxSettings(SurfaceDetailsHeight, SurfacePropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_SURFACE_INFO), - MakeGroupboxSettings(PathDetailsHeight, PathPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_PATH_INFO), - MakeGroupboxSettings(TrackDetailsHeight, TrackPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_TRACK_INFO), - MakeGroupboxSettings(SceneryDetailsHeight, SceneryPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_SCENERY_INFO), - MakeGroupboxSettings(EntranceDetailsHeight, EntrancePropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_ENTRANCE_INFO), - MakeGroupboxSettings(WallDetailsHeight, WallPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_WALL_INFO), - MakeGroupboxSettings(LargeSceneryDetailsHeight, LargeSceneryPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO), - MakeGroupboxSettings(BannerDetailsHeight, BannerPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO), -}; + static constexpr TileInspectorGroupboxSettings PageGroupBoxSettings[] = { + MakeGroupboxSettings(SurfaceDetailsHeight, SurfacePropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_SURFACE_INFO), + MakeGroupboxSettings(PathDetailsHeight, PathPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_PATH_INFO), + MakeGroupboxSettings(TrackDetailsHeight, TrackPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_TRACK_INFO), + MakeGroupboxSettings(SceneryDetailsHeight, SceneryPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_SCENERY_INFO), + MakeGroupboxSettings(EntranceDetailsHeight, EntrancePropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_ENTRANCE_INFO), + MakeGroupboxSettings(WallDetailsHeight, WallPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_WALL_INFO), + MakeGroupboxSettings(LargeSceneryDetailsHeight, LargeSceneryPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO), + MakeGroupboxSettings(BannerDetailsHeight, BannerPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO), + }; -static constexpr int32_t ViewportInteractionFlags = EnumsToFlags( - ViewportInteractionItem::Terrain, ViewportInteractionItem::Ride, ViewportInteractionItem::Scenery, - ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition, ViewportInteractionItem::ParkEntrance, - ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery, ViewportInteractionItem::Banner); -// clang-format off + static constexpr int32_t ViewportInteractionFlags = EnumsToFlags( + ViewportInteractionItem::Terrain, ViewportInteractionItem::Ride, ViewportInteractionItem::Scenery, + ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition, ViewportInteractionItem::ParkEntrance, + ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery, ViewportInteractionItem::Banner); + // clang-format off static uint64_t PageHoldDownWidgets[] = { (1uLL << WIDX_SPINNER_X_INCREASE) | (1uLL << WIDX_SPINNER_X_DECREASE) | (1uLL << WIDX_SPINNER_Y_INCREASE) | (1uLL << WIDX_SPINNER_Y_DECREASE), @@ -468,1919 +470,1945 @@ static uint64_t PageDisabledWidgets[] = { (1uLL << WIDX_BUTTON_ROTATE), 0, }; -// clang-format on + // clang-format on -class TileInspector final : public Window -{ -private: - int16_t _highlightedIndex = -1; - bool _tileSelected = false; - int32_t _toolMouseX = 0; - int32_t _toolMouseY = 0; - bool _toolCtrlDown = false; - CoordsXY _toolMap = {}; - bool _applyToAll = false; - bool _elementCopied = false; - TileElement _copiedElement; - Banner _copiedBanner; - -public: - void OnOpen() override + class TileInspector final : public Window { - min_width = MIN_WW; - min_height = MIN_WH; - max_width = MAX_WW; - max_height = MAX_WH; + private: + int16_t _highlightedIndex = -1; + bool _tileSelected = false; + int32_t _toolMouseX = 0; + int32_t _toolMouseY = 0; + bool _toolCtrlDown = false; + CoordsXY _toolMap = {}; + bool _applyToAll = false; + bool _elementCopied = false; + TileElement _copiedElement; + Banner _copiedBanner; - windowTileInspectorSelectedIndex = -1; - SetPage(TileInspectorPage::Default); - WindowInitScrollWidgets(*this); - _tileSelected = false; - - ToolSet(*this, WIDX_BACKGROUND, Tool::Crosshair); - } - - void OnUpdate() override - { - // Check if the mouse is hovering over the list - if (!WidgetIsHighlighted(*this, WIDX_LIST)) + public: + void OnOpen() override { - if (_highlightedIndex != -1) - InvalidateWidget(WIDX_LIST); - _highlightedIndex = -1; + min_width = MIN_WW; + min_height = MIN_WH; + max_width = MAX_WW; + max_height = MAX_WH; + + windowTileInspectorSelectedIndex = -1; + SetPage(TileInspectorPage::Default); + WindowInitScrollWidgets(*this); + _tileSelected = false; + + ToolSet(*this, WIDX_BACKGROUND, Tool::Crosshair); } - if (gCurrentToolWidget.window_classification != WindowClass::TileInspector) - Close(); - } - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnUpdate() override { - case WIDX_CLOSE: + // Check if the mouse is hovering over the list + if (!WidgetIsHighlighted(*this, WIDX_LIST)) + { + if (_highlightedIndex != -1) + InvalidateWidget(WIDX_LIST); + _highlightedIndex = -1; + } + if (gCurrentToolWidget.window_classification != WindowClass::TileInspector) Close(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + return; + + case WIDX_BUTTON_REMOVE: + { + RemoveElement(windowTileInspectorSelectedIndex); + break; + } + + case WIDX_BUTTON_ROTATE: + RotateElement(windowTileInspectorSelectedIndex); + break; + + case WIDX_BUTTON_SORT: + SortElements(); + break; + + case WIDX_BUTTON_COPY: + CopyElement(); + break; + + case WIDX_BUTTON_PASTE: + PasteElement(); + break; + + case WIDX_BUTTON_MOVE_UP: + SwapElements(windowTileInspectorSelectedIndex, windowTileInspectorSelectedIndex + 1); + break; + + case WIDX_BUTTON_MOVE_DOWN: + SwapElements(windowTileInspectorSelectedIndex - 1, windowTileInspectorSelectedIndex); + break; + } + + // Only element-specific widgets from now on + if (tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) return; - case WIDX_BUTTON_REMOVE: - { - RemoveElement(windowTileInspectorSelectedIndex); - break; - } - - case WIDX_BUTTON_ROTATE: - RotateElement(windowTileInspectorSelectedIndex); - break; - - case WIDX_BUTTON_SORT: - SortElements(); - break; - - case WIDX_BUTTON_COPY: - CopyElement(); - break; - - case WIDX_BUTTON_PASTE: - PasteElement(); - break; - - case WIDX_BUTTON_MOVE_UP: - SwapElements(windowTileInspectorSelectedIndex, windowTileInspectorSelectedIndex + 1); - break; - - case WIDX_BUTTON_MOVE_DOWN: - SwapElements(windowTileInspectorSelectedIndex - 1, windowTileInspectorSelectedIndex); - break; - } - - // Only element-specific widgets from now on - if (tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) - return; - - const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); - - if (tileElement == nullptr) - return; - - // Page widgets - switch (tileElement->GetType()) - { - case TileElementType::Surface: - switch (widgetIndex) - { - case WIDX_SURFACE_BUTTON_REMOVE_FENCES: - SurfaceShowParkFences(false); - break; - - case WIDX_SURFACE_BUTTON_RESTORE_FENCES: - SurfaceShowParkFences(true); - break; - - case WIDX_SURFACE_CHECK_CORNER_N: - case WIDX_SURFACE_CHECK_CORNER_E: - case WIDX_SURFACE_CHECK_CORNER_S: - case WIDX_SURFACE_CHECK_CORNER_W: - SurfaceToggleCorner(((widgetIndex - WIDX_SURFACE_CHECK_CORNER_N) + 2 - GetCurrentRotation()) & 3); - break; - - case WIDX_SURFACE_CHECK_DIAGONAL: - SurfaceToggleDiagonal(); - break; - } // switch widgetindex - break; - case TileElementType::Path: - switch (widgetIndex) - { - case WIDX_PATH_CHECK_SLOPED: - PathSetSloped(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsSloped()); - break; - case WIDX_PATH_CHECK_JUNCTION_RAILINGS: - PathSetJunctionRailings( - windowTileInspectorSelectedIndex, !tileElement->AsPath()->HasJunctionRailings()); - break; - - case WIDX_PATH_CHECK_BROKEN: - PathSetBroken(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsBroken()); - break; - - case WIDX_PATH_CHECK_EDGE_E: - case WIDX_PATH_CHECK_EDGE_S: - case WIDX_PATH_CHECK_EDGE_W: - case WIDX_PATH_CHECK_EDGE_N: - { - // 0 = east/right, 1 = south/bottom, 2 = west/left, 3 = north/top - const int32_t eswn = (widgetIndex - WIDX_PATH_CHECK_EDGE_E) / 2; - // Transform to world orientation - const int32_t index = (eswn - GetCurrentRotation()) & 3; - PathToggleEdge( - windowTileInspectorSelectedIndex, - index + 4); // The corners are stored in the 4 most significant bits, hence the + 4 - break; - } - - case WIDX_PATH_CHECK_EDGE_NE: - case WIDX_PATH_CHECK_EDGE_SE: - case WIDX_PATH_CHECK_EDGE_SW: - case WIDX_PATH_CHECK_EDGE_NW: - { - // 0 = NE, 1 = SE, 2 = SW, 3 = NW - const int32_t neseswnw = (widgetIndex - WIDX_PATH_CHECK_EDGE_NE) / 2; - // Transform to world orientation - const int32_t index = (neseswnw - GetCurrentRotation()) & 3; - PathToggleEdge(windowTileInspectorSelectedIndex, index); - break; - } - } // switch widget index - break; - - case TileElementType::Track: - switch (widgetIndex) - { - case WIDX_TRACK_CHECK_APPLY_TO_ALL: - _applyToAll ^= 1; - InvalidateWidget(widgetIndex); - break; - - case WIDX_TRACK_CHECK_CHAIN_LIFT: - { - bool entireTrackBlock = IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL); - bool newLift = !tileElement->AsTrack()->HasChain(); - TrackBlockSetLift(windowTileInspectorSelectedIndex, entireTrackBlock, newLift); - break; - } - - case WIDX_TRACK_CHECK_BRAKE_CLOSED: - TrackSetBrakeClosed(windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsBrakeClosed()); - break; - - case WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE: - TrackSetIndestructible(windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsIndestructible()); - break; - } // switch widget index - break; - - case TileElementType::SmallScenery: - switch (widgetIndex) - { - case WIDX_SCENERY_CHECK_QUARTER_N: - case WIDX_SCENERY_CHECK_QUARTER_E: - case WIDX_SCENERY_CHECK_QUARTER_S: - case WIDX_SCENERY_CHECK_QUARTER_W: - QuarterTileSet(windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_QUARTER_N); - break; - - case WIDX_SCENERY_CHECK_COLLISION_N: - case WIDX_SCENERY_CHECK_COLLISION_E: - case WIDX_SCENERY_CHECK_COLLISION_S: - case WIDX_SCENERY_CHECK_COLLISION_W: - ToggleQuadrantCollosion(windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_COLLISION_N); - break; - } // switch widget index - break; - - case TileElementType::Entrance: - switch (widgetIndex) - { - case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: - EntranceMakeUsable(windowTileInspectorSelectedIndex); - break; - } // switch widget index - break; - - case TileElementType::Banner: - switch (widgetIndex) - { - case WIDX_BANNER_CHECK_BLOCK_NE: - case WIDX_BANNER_CHECK_BLOCK_SE: - case WIDX_BANNER_CHECK_BLOCK_SW: - case WIDX_BANNER_CHECK_BLOCK_NW: - BannerToggleBlock(windowTileInspectorSelectedIndex, widgetIndex - WIDX_BANNER_CHECK_BLOCK_NE); - break; - } // switch widget index - break; - - case TileElementType::LargeScenery: - case TileElementType::Wall: - default: - break; - } - } - - void OnClose() override - { - ToolCancel(); - TileElement* const elem = OpenRCT2::TileInspector::GetSelectedElement(); - if (elem != nullptr) - { - MapInvalidateElement(_toolMap, elem); - } - windowTileInspectorSelectedIndex = -1; - } - - void OnResize() override - { - if (width < min_width) - { - Invalidate(); - width = min_width; - } - if (height < min_height) - { - Invalidate(); - height = min_height; - } - ResizeFrame(); - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_SPINNER_X_INCREASE: - windowTileInspectorTile.x = std::min(windowTileInspectorTile.x + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); - _toolMap.x = std::min(_toolMap.x + 32, MAXIMUM_TILE_START_XY); - LoadTile(nullptr); - break; - - case WIDX_SPINNER_X_DECREASE: - windowTileInspectorTile.x = std::max(windowTileInspectorTile.x - 1, 0); - _toolMap.x = std::max(_toolMap.x - 32, 0); - LoadTile(nullptr); - break; - - case WIDX_SPINNER_Y_INCREASE: - windowTileInspectorTile.y = std::min(windowTileInspectorTile.y + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); - _toolMap.y = std::min(_toolMap.y + 32, MAXIMUM_TILE_START_XY); - LoadTile(nullptr); - break; - - case WIDX_SPINNER_Y_DECREASE: - windowTileInspectorTile.y = std::max(windowTileInspectorTile.y - 1, 0); - _toolMap.y = std::max(_toolMap.y - 32, 0); - LoadTile(nullptr); - break; - } // switch widget index - - // Only element-specific widgets from now on - if (tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) - return; - - const TileElement* tileElement = OpenRCT2::TileInspector::GetSelectedElement(); - if (tileElement == nullptr) - return; - - switch (tileElement->GetType()) - { - case TileElementType::Surface: - switch (widgetIndex) - { - case WIDX_SURFACE_SPINNER_HEIGHT_INCREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_SURFACE_SPINNER_HEIGHT_DECREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Path: - switch (widgetIndex) - { - case WIDX_PATH_SPINNER_HEIGHT_INCREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_PATH_SPINNER_HEIGHT_DECREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Track: - switch (widgetIndex) - { - case WIDX_TRACK_SPINNER_HEIGHT_INCREASE: - if (IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL)) - TrackBlockHeightOffset(windowTileInspectorSelectedIndex, 1); - else - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_TRACK_SPINNER_HEIGHT_DECREASE: - if (IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL)) - TrackBlockHeightOffset(windowTileInspectorSelectedIndex, -1); - else - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::SmallScenery: - switch (widgetIndex) - { - case WIDX_SCENERY_SPINNER_HEIGHT_INCREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_SCENERY_SPINNER_HEIGHT_DECREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Entrance: - switch (widgetIndex) - { - case WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - - case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: - EntranceMakeUsable(windowTileInspectorSelectedIndex); - break; - } // switch widget index - break; - - case TileElementType::Wall: - switch (widgetIndex) - { - case WIDX_WALL_SPINNER_HEIGHT_INCREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_WALL_SPINNER_HEIGHT_DECREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - - case WIDX_WALL_DROPDOWN_SLOPE_BUTTON: - { - Widget* widget = &widgets[widgetIndex]; - // Use dropdown instead of dropdown button - widget--; - // Fill dropdown list - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_TILE_INSPECTOR_WALL_FLAT; - gDropdownItems[1].Args = STR_TILE_INSPECTOR_WALL_SLOPED_LEFT; - gDropdownItems[2].Args = STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT; - WindowDropdownShowTextCustomWidth( - { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, - Dropdown::Flag::StayOpen, 3, widget->width() - 3); - - // Set current value as checked - Dropdown::SetChecked(tileElement->AsWall()->GetSlope(), true); - break; - } - - case WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE: - WallAnimationFrameOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE: - WallAnimationFrameOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::LargeScenery: - switch (widgetIndex) - { - case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Banner: - switch (widgetIndex) - { - case WIDX_BANNER_SPINNER_HEIGHT_INCREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - - case WIDX_BANNER_SPINNER_HEIGHT_DECREASE: - BaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - default: - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override - { - if (dropdownIndex == -1) - return; - // Get selected element - const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); - if (tileInspectorPage == TileInspectorPage::Wall) - { - Guard::Assert(tileElement->GetType() == TileElementType::Wall, "Element is not a wall"); - if (widgetIndex == WIDX_WALL_DROPDOWN_SLOPE_BUTTON) - WallSetSlope(windowTileInspectorSelectedIndex, dropdownIndex); - } - } - - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - MapInvalidateSelectionRect(); - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - - CoordsXY mapCoords; - TileElement* clickedElement = nullptr; - bool mouseOnViewport = false; - if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z)) - { - auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionFlags); - clickedElement = info.Element; - mapCoords = info.Loc; - } - // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor - if (clickedElement == nullptr) - { - auto mouseCoords = ScreenPosToMapPos(screenCoords, nullptr); - if (mouseCoords.has_value()) - { - mouseOnViewport = true; - mapCoords = mouseCoords.value(); - } - } - if (mouseOnViewport) - gMapSelectPositionA = gMapSelectPositionB = mapCoords; - else if (_tileSelected) - gMapSelectPositionA = gMapSelectPositionB = _toolMap; - else - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - gMapSelectType = MAP_SELECT_TYPE_FULL; - MapInvalidateSelectionRect(); - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - UpdateSelectedTile(screenCoords); - } - - void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - UpdateSelectedTile(screenCoords); - } - - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - return ScreenSize(WW - 30, windowTileInspectorElementCount * SCROLLABLE_ROW_HEIGHT); - } - - void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - // There is nothing to interact with when no tile is selected - if (!_tileSelected) - return; - - // Because the list items are displayed in reverse order, subtract the calculated index from the amount of elements - const int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; - const ScreenRect checkboxColumnRect{ { 2, 0 }, { 15, screenCoords.y } }; - if (index >= 0 && checkboxColumnRect.Contains(screenCoords)) - { // Checkbox was clicked - ToggleInvisibility(index); - } - else - { - SelectElementFromList(index); - } - } - - void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; - if (index < 0 || index >= windowTileInspectorElementCount) - _highlightedIndex = -1; - else - _highlightedIndex = index; - InvalidateWidget(WIDX_LIST); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - ScreenCoordsXY screenCoords(windowPos.x, windowPos.y); - - // Draw coordinates - GfxDrawString(dpi, screenCoords + ScreenCoordsXY(5, 24), "X:", { colours[1] }); - GfxDrawString(dpi, screenCoords + ScreenCoordsXY(74, 24), "Y:", { colours[1] }); - if (_tileSelected) - { - auto tileCoords = TileCoordsXY{ _toolMap }; - auto ft = Formatter(); - ft.Add(tileCoords.x); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 43, 24 }, STR_FORMAT_INTEGER, ft, { colours[1], TextAlignment::RIGHT }); - ft = Formatter(); - ft.Add(tileCoords.y); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 113, 24 }, STR_FORMAT_INTEGER, ft, { colours[1], TextAlignment::RIGHT }); - } - else - { - GfxDrawString(dpi, screenCoords + ScreenCoordsXY(43 - 7, 24), "-", { colours[1] }); - GfxDrawString(dpi, screenCoords + ScreenCoordsXY(113 - 7, 24), "-", { colours[1] }); - } - - if (windowTileInspectorSelectedIndex != -1) - { - // X and Y of first element in detail box - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_GROUPBOX_DETAILS].top + 14 }; - - // Get map element const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); + + if (tileElement == nullptr) + return; + + // Page widgets + switch (tileElement->GetType()) + { + case TileElementType::Surface: + switch (widgetIndex) + { + case WIDX_SURFACE_BUTTON_REMOVE_FENCES: + SurfaceShowParkFences(false); + break; + + case WIDX_SURFACE_BUTTON_RESTORE_FENCES: + SurfaceShowParkFences(true); + break; + + case WIDX_SURFACE_CHECK_CORNER_N: + case WIDX_SURFACE_CHECK_CORNER_E: + case WIDX_SURFACE_CHECK_CORNER_S: + case WIDX_SURFACE_CHECK_CORNER_W: + SurfaceToggleCorner(((widgetIndex - WIDX_SURFACE_CHECK_CORNER_N) + 2 - GetCurrentRotation()) & 3); + break; + + case WIDX_SURFACE_CHECK_DIAGONAL: + SurfaceToggleDiagonal(); + break; + } // switch widgetindex + break; + case TileElementType::Path: + switch (widgetIndex) + { + case WIDX_PATH_CHECK_SLOPED: + PathSetSloped(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsSloped()); + break; + case WIDX_PATH_CHECK_JUNCTION_RAILINGS: + PathSetJunctionRailings( + windowTileInspectorSelectedIndex, !tileElement->AsPath()->HasJunctionRailings()); + break; + + case WIDX_PATH_CHECK_BROKEN: + PathSetBroken(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsBroken()); + break; + + case WIDX_PATH_CHECK_EDGE_E: + case WIDX_PATH_CHECK_EDGE_S: + case WIDX_PATH_CHECK_EDGE_W: + case WIDX_PATH_CHECK_EDGE_N: + { + // 0 = east/right, 1 = south/bottom, 2 = west/left, 3 = north/top + const int32_t eswn = (widgetIndex - WIDX_PATH_CHECK_EDGE_E) / 2; + // Transform to world orientation + const int32_t index = (eswn - GetCurrentRotation()) & 3; + PathToggleEdge( + windowTileInspectorSelectedIndex, + index + 4); // The corners are stored in the 4 most significant bits, hence the + 4 + break; + } + + case WIDX_PATH_CHECK_EDGE_NE: + case WIDX_PATH_CHECK_EDGE_SE: + case WIDX_PATH_CHECK_EDGE_SW: + case WIDX_PATH_CHECK_EDGE_NW: + { + // 0 = NE, 1 = SE, 2 = SW, 3 = NW + const int32_t neseswnw = (widgetIndex - WIDX_PATH_CHECK_EDGE_NE) / 2; + // Transform to world orientation + const int32_t index = (neseswnw - GetCurrentRotation()) & 3; + PathToggleEdge(windowTileInspectorSelectedIndex, index); + break; + } + } // switch widget index + break; + + case TileElementType::Track: + switch (widgetIndex) + { + case WIDX_TRACK_CHECK_APPLY_TO_ALL: + _applyToAll ^= 1; + InvalidateWidget(widgetIndex); + break; + + case WIDX_TRACK_CHECK_CHAIN_LIFT: + { + bool entireTrackBlock = IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL); + bool newLift = !tileElement->AsTrack()->HasChain(); + TrackBlockSetLift(windowTileInspectorSelectedIndex, entireTrackBlock, newLift); + break; + } + + case WIDX_TRACK_CHECK_BRAKE_CLOSED: + TrackSetBrakeClosed(windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsBrakeClosed()); + break; + + case WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE: + TrackSetIndestructible( + windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsIndestructible()); + break; + } // switch widget index + break; + + case TileElementType::SmallScenery: + switch (widgetIndex) + { + case WIDX_SCENERY_CHECK_QUARTER_N: + case WIDX_SCENERY_CHECK_QUARTER_E: + case WIDX_SCENERY_CHECK_QUARTER_S: + case WIDX_SCENERY_CHECK_QUARTER_W: + QuarterTileSet(windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_QUARTER_N); + break; + + case WIDX_SCENERY_CHECK_COLLISION_N: + case WIDX_SCENERY_CHECK_COLLISION_E: + case WIDX_SCENERY_CHECK_COLLISION_S: + case WIDX_SCENERY_CHECK_COLLISION_W: + ToggleQuadrantCollosion( + windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_COLLISION_N); + break; + } // switch widget index + break; + + case TileElementType::Entrance: + switch (widgetIndex) + { + case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: + EntranceMakeUsable(windowTileInspectorSelectedIndex); + break; + } // switch widget index + break; + + case TileElementType::Banner: + switch (widgetIndex) + { + case WIDX_BANNER_CHECK_BLOCK_NE: + case WIDX_BANNER_CHECK_BLOCK_SE: + case WIDX_BANNER_CHECK_BLOCK_SW: + case WIDX_BANNER_CHECK_BLOCK_NW: + BannerToggleBlock(windowTileInspectorSelectedIndex, widgetIndex - WIDX_BANNER_CHECK_BLOCK_NE); + break; + } // switch widget index + break; + + case TileElementType::LargeScenery: + case TileElementType::Wall: + default: + break; + } + } + + void OnClose() override + { + ToolCancel(); + TileElement* const elem = OpenRCT2::TileInspector::GetSelectedElement(); + if (elem != nullptr) + { + MapInvalidateElement(_toolMap, elem); + } + windowTileInspectorSelectedIndex = -1; + } + + void OnResize() override + { + if (width < min_width) + { + Invalidate(); + width = min_width; + } + if (height < min_height) + { + Invalidate(); + height = min_height; + } + ResizeFrame(); + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_SPINNER_X_INCREASE: + windowTileInspectorTile.x = std::min( + windowTileInspectorTile.x + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); + _toolMap.x = std::min(_toolMap.x + 32, MAXIMUM_TILE_START_XY); + LoadTile(nullptr); + break; + + case WIDX_SPINNER_X_DECREASE: + windowTileInspectorTile.x = std::max(windowTileInspectorTile.x - 1, 0); + _toolMap.x = std::max(_toolMap.x - 32, 0); + LoadTile(nullptr); + break; + + case WIDX_SPINNER_Y_INCREASE: + windowTileInspectorTile.y = std::min( + windowTileInspectorTile.y + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); + _toolMap.y = std::min(_toolMap.y + 32, MAXIMUM_TILE_START_XY); + LoadTile(nullptr); + break; + + case WIDX_SPINNER_Y_DECREASE: + windowTileInspectorTile.y = std::max(windowTileInspectorTile.y - 1, 0); + _toolMap.y = std::max(_toolMap.y - 32, 0); + LoadTile(nullptr); + break; + } // switch widget index + + // Only element-specific widgets from now on + if (tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) + return; + + const TileElement* tileElement = OpenRCT2::TileInspector::GetSelectedElement(); if (tileElement == nullptr) return; switch (tileElement->GetType()) { case TileElementType::Surface: - { - // Details - // Terrain texture name - StringId terrainNameId = STR_EMPTY; - auto surfaceStyle = tileElement->AsSurface()->GetSurfaceObject(); - if (surfaceStyle != nullptr) - terrainNameId = surfaceStyle->NameStringId; - auto ft = Formatter(); - ft.Add(terrainNameId); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_TERAIN, ft, { colours[1] }); + switch (widgetIndex) + { + case WIDX_SURFACE_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Edge texture name - StringId terrainEdgeNameId = STR_EMPTY; - auto edgeStyle = tileElement->AsSurface()->GetEdgeObject(); - if (edgeStyle != nullptr) - terrainEdgeNameId = edgeStyle->NameStringId; - ft = Formatter(); - ft.Add(terrainEdgeNameId); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SURFACE_EDGE, ft, { colours[1] }); - - // Land ownership - StringId landOwnership; - if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) - landOwnership = STR_LAND_OWNED; - else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_AVAILABLE) - landOwnership = STR_LAND_SALE; - else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) - landOwnership = STR_CONSTRUCTION_RIGHTS_OWNED; - else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) - landOwnership = STR_CONSTRUCTION_RIGHTS_SALE; - else - landOwnership = STR_TILE_INSPECTOR_LAND_NOT_OWNED_AND_NOT_AVAILABLE; - - ft = Formatter(); - ft.Add(landOwnership); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SURFACE_OWNERSHIP, ft, { colours[1] }); - - // Water level - ft = Formatter(); - ft.Add(tileElement->AsSurface()->GetWaterHeight()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_SURFACE_WATER_LEVEL, ft, - { colours[1] }); - - // Properties - // Raise / lower label - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_SURFACE_SPINNER_HEIGHT].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_SURFACE_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); - - // Raised corners - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_SURFACE_CHECK_CORNER_E].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_CORNERS, {}, { colours[1] }); + case WIDX_SURFACE_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } + case TileElementType::Path: - { - // Details - auto pathEl = tileElement->AsPath(); - auto footpathObj = pathEl->GetLegacyPathEntry(); - if (footpathObj == nullptr) + switch (widgetIndex) { - // Surface name - auto surfaceObj = pathEl->GetSurfaceEntry(); - if (surfaceObj != nullptr) - { - auto ft = Formatter(); - ft.Add(surfaceObj->NameStringId); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_FOOTPATH_SURFACE_NAME, ft, { COLOUR_WHITE }); - } + case WIDX_PATH_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Railings name - auto railingsObj = pathEl->GetRailingsEntry(); - if (railingsObj != nullptr) - { - auto ft = Formatter(); - ft.Add(railingsObj->NameStringId); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_FOOTPATH_RAILINGS_NAME, ft, - { COLOUR_WHITE }); - } - } - else - { - // Legacy path name - auto footpathEntry = reinterpret_cast(footpathObj->GetLegacyData()); - auto ft = Formatter(); - ft.Add(footpathEntry->string_idx); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_NAME, ft, { COLOUR_WHITE }); - } - - // Path addition - if (tileElement->AsPath()->HasAddition()) - { - const auto pathAdditionEntry = tileElement->AsPath()->GetAdditionEntry(); - StringId additionNameId = pathAdditionEntry != nullptr ? pathAdditionEntry->name - : static_cast(STR_UNKNOWN_OBJECT_TYPE); - auto ft = Formatter(); - ft.Add(additionNameId); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS, ft, - { COLOUR_WHITE }); - } - else - { - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, {}, - { COLOUR_WHITE }); - } - - // Properties - // Raise / lower label - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_PATH_SPINNER_HEIGHT].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_PATH_SPINNER_HEIGHT].left + 3; - auto ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); - - // Path connections - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_PATH_CHECK_EDGE_W].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_CONNECTED_EDGES, {}, { colours[1] }); + case WIDX_PATH_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } case TileElementType::Track: - { - auto trackElement = tileElement->AsTrack(); - RideId id = trackElement->GetRideIndex(); - auto rideTile = GetRide(id); - - // Ride ID - auto ft = Formatter(); - ft.Add(id); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_TRACK_RIDE_ID, ft, { colours[1] }); - - // Ride name - if (rideTile != nullptr) + switch (widgetIndex) { - ft = Formatter(); - rideTile->FormatNameTo(ft); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_TRACK_RIDE_NAME, ft, - { colours[1] }); - } + case WIDX_TRACK_SPINNER_HEIGHT_INCREASE: + if (IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL)) + TrackBlockHeightOffset(windowTileInspectorSelectedIndex, 1); + else + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Ride type. Individual pieces may be of a different ride type from the ride it belongs to. - const auto& rtd = GetRideTypeDescriptor(trackElement->GetRideType()); - ft = Formatter(); - ft.Add(rtd.Naming.Name); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_TRACK_RIDE_TYPE, ft, { colours[1] }); - - // Track - ft = Formatter(); - ft.Add(trackElement->GetTrackType()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_TRACK_PIECE_ID, ft, { colours[1] }); - - ft = Formatter(); - ft.Add(trackElement->GetSequenceIndex()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 44 }, STR_TILE_INSPECTOR_TRACK_SEQUENCE, ft, { colours[1] }); - if (trackElement->IsStation()) - { - auto stationIndex = trackElement->GetStationIndex(); - ft = Formatter(); - ft.Add(STR_COMMA16); - ft.Add(stationIndex.ToUnderlying()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { colours[1] }); - } - else - { - const char* stationNone = "-"; - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(stationNone); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { colours[1] }); - } - - ft = Formatter(); - ft.Add(ColourSchemeNames[trackElement->GetColourScheme()]); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 66 }, STR_TILE_INSPECTOR_COLOUR_SCHEME, ft, { colours[1] }); - - // Properties - // Raise / lower label - screenCoords.y = windowPos.y + widgets[WIDX_TRACK_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_TRACK_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + case WIDX_TRACK_SPINNER_HEIGHT_DECREASE: + if (IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL)) + TrackBlockHeightOffset(windowTileInspectorSelectedIndex, -1); + else + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } case TileElementType::SmallScenery: - { - // Details - // Age - auto ft = Formatter(); - ft.Add(tileElement->AsSmallScenery()->GetAge()); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_AGE, ft, { colours[1] }); - - // Quadrant value - const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); - if (sceneryEntry != nullptr && !(sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))) + switch (widgetIndex) { - int16_t quadrant = tileElement->AsSmallScenery()->GetSceneryQuadrant(); - static constexpr StringId _quadrantStringIdx[] = { - STR_TILE_INSPECTOR_SCENERY_QUADRANT_SW, - STR_TILE_INSPECTOR_SCENERY_QUADRANT_NW, - STR_TILE_INSPECTOR_SCENERY_QUADRANT_NE, - STR_TILE_INSPECTOR_SCENERY_QUADRANT_SE, - }; - ft = Formatter(); - ft.Add(_quadrantStringIdx[quadrant]); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SCENERY_QUADRANT, ft, - { colours[1] }); - } + case WIDX_SCENERY_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Scenery ID - ft = Formatter(); - ft.Add(tileElement->AsSmallScenery()->GetEntryIndex()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SCENERY_ENTRY_IDX, ft, { colours[1] }); - - // Properties - // Raise / Lower - screenCoords.y = windowPos.y + widgets[WIDX_SCENERY_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_SCENERY_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); - - // Quarter tile - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_SCENERY_CHECK_QUARTER_E].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_QUADRANT_LABEL, {}, { colours[1] }); - - // Collision - screenCoords.y = windowPos.y + widgets[WIDX_SCENERY_CHECK_COLLISION_E].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_COLLISSION, {}, { colours[1] }); + case WIDX_SCENERY_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } case TileElementType::Entrance: - { - // Details - // Entrance type - auto ft = Formatter(); - ft.Add(EntranceTypeStringIds[tileElement->AsEntrance()->GetEntranceType()]); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRANCE_TYPE, ft, { colours[1] }); + switch (widgetIndex) + { + case WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + case WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + + case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: + EntranceMakeUsable(windowTileInspectorSelectedIndex); + break; + } // switch widget index + break; + + case TileElementType::Wall: + switch (widgetIndex) { - // TODO: Make this work with Left/Right park entrance parts - ft = Formatter(); - ft.Add(ParkEntranceGetIndex({ _toolMap, tileElement->GetBaseZ() })); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft, - { colours[1] }); - } - else - { - ft = Formatter(); - ft.Add(tileElement->AsEntrance()->GetStationIndex().ToUnderlying()); - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + case WIDX_WALL_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_WALL_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + + case WIDX_WALL_DROPDOWN_SLOPE_BUTTON: { - // Ride entrance ID + Widget* widget = &widgets[widgetIndex]; + // Use dropdown instead of dropdown button + widget--; + // Fill dropdown list + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_TILE_INSPECTOR_WALL_FLAT; + gDropdownItems[1].Args = STR_TILE_INSPECTOR_WALL_SLOPED_LEFT; + gDropdownItems[2].Args = STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT; + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, 3, widget->width() - 3); + + // Set current value as checked + Dropdown::SetChecked(tileElement->AsWall()->GetSlope(), true); + break; + } + + case WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE: + WallAnimationFrameOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE: + WallAnimationFrameOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index + break; + + case TileElementType::LargeScenery: + switch (widgetIndex) + { + case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index + break; + + case TileElementType::Banner: + switch (widgetIndex) + { + case WIDX_BANNER_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_BANNER_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index + break; + + default: + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (dropdownIndex == -1) + return; + // Get selected element + const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); + if (tileInspectorPage == TileInspectorPage::Wall) + { + Guard::Assert(tileElement->GetType() == TileElementType::Wall, "Element is not a wall"); + if (widgetIndex == WIDX_WALL_DROPDOWN_SLOPE_BUTTON) + WallSetSlope(windowTileInspectorSelectedIndex, dropdownIndex); + } + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + MapInvalidateSelectionRect(); + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + + CoordsXY mapCoords; + TileElement* clickedElement = nullptr; + bool mouseOnViewport = false; + if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z)) + { + auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionFlags); + clickedElement = info.Element; + mapCoords = info.Loc; + } + // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor + if (clickedElement == nullptr) + { + auto mouseCoords = ScreenPosToMapPos(screenCoords, nullptr); + if (mouseCoords.has_value()) + { + mouseOnViewport = true; + mapCoords = mouseCoords.value(); + } + } + if (mouseOnViewport) + gMapSelectPositionA = gMapSelectPositionB = mapCoords; + else if (_tileSelected) + gMapSelectPositionA = gMapSelectPositionB = _toolMap; + else + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + + gMapSelectType = MAP_SELECT_TYPE_FULL; + MapInvalidateSelectionRect(); + } + + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + UpdateSelectedTile(screenCoords); + } + + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + UpdateSelectedTile(screenCoords); + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + return ScreenSize(WW - 30, windowTileInspectorElementCount * SCROLLABLE_ROW_HEIGHT); + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + // There is nothing to interact with when no tile is selected + if (!_tileSelected) + return; + + // Because the list items are displayed in reverse order, subtract the calculated index from the amount of elements + const int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; + const ScreenRect checkboxColumnRect{ { 2, 0 }, { 15, screenCoords.y } }; + if (index >= 0 && checkboxColumnRect.Contains(screenCoords)) + { // Checkbox was clicked + ToggleInvisibility(index); + } + else + { + SelectElementFromList(index); + } + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; + if (index < 0 || index >= windowTileInspectorElementCount) + _highlightedIndex = -1; + else + _highlightedIndex = index; + InvalidateWidget(WIDX_LIST); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + ScreenCoordsXY screenCoords(windowPos.x, windowPos.y); + + // Draw coordinates + GfxDrawString(dpi, screenCoords + ScreenCoordsXY(5, 24), "X:", { colours[1] }); + GfxDrawString(dpi, screenCoords + ScreenCoordsXY(74, 24), "Y:", { colours[1] }); + if (_tileSelected) + { + auto tileCoords = TileCoordsXY{ _toolMap }; + auto ft = Formatter(); + ft.Add(tileCoords.x); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 43, 24 }, STR_FORMAT_INTEGER, ft, { colours[1], TextAlignment::RIGHT }); + ft = Formatter(); + ft.Add(tileCoords.y); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 113, 24 }, STR_FORMAT_INTEGER, ft, + { colours[1], TextAlignment::RIGHT }); + } + else + { + GfxDrawString(dpi, screenCoords + ScreenCoordsXY(43 - 7, 24), "-", { colours[1] }); + GfxDrawString(dpi, screenCoords + ScreenCoordsXY(113 - 7, 24), "-", { colours[1] }); + } + + if (windowTileInspectorSelectedIndex != -1) + { + // X and Y of first element in detail box + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_GROUPBOX_DETAILS].top + 14 }; + + // Get map element + const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); + if (tileElement == nullptr) + return; + + switch (tileElement->GetType()) + { + case TileElementType::Surface: + { + // Details + // Terrain texture name + StringId terrainNameId = STR_EMPTY; + auto surfaceStyle = tileElement->AsSurface()->GetSurfaceObject(); + if (surfaceStyle != nullptr) + terrainNameId = surfaceStyle->NameStringId; + auto ft = Formatter(); + ft.Add(terrainNameId); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_TERAIN, ft, { colours[1] }); + + // Edge texture name + StringId terrainEdgeNameId = STR_EMPTY; + auto edgeStyle = tileElement->AsSurface()->GetEdgeObject(); + if (edgeStyle != nullptr) + terrainEdgeNameId = edgeStyle->NameStringId; + ft = Formatter(); + ft.Add(terrainEdgeNameId); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SURFACE_EDGE, ft, { colours[1] }); + + // Land ownership + StringId landOwnership; + if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) + landOwnership = STR_LAND_OWNED; + else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_AVAILABLE) + landOwnership = STR_LAND_SALE; + else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) + landOwnership = STR_CONSTRUCTION_RIGHTS_OWNED; + else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) + landOwnership = STR_CONSTRUCTION_RIGHTS_SALE; + else + landOwnership = STR_TILE_INSPECTOR_LAND_NOT_OWNED_AND_NOT_AVAILABLE; + + ft = Formatter(); + ft.Add(landOwnership); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SURFACE_OWNERSHIP, ft, + { colours[1] }); + + // Water level + ft = Formatter(); + ft.Add(tileElement->AsSurface()->GetWaterHeight()); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_SURFACE_WATER_LEVEL, ft, + { colours[1] }); + + // Properties + // Raise / lower label + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, + widgets[WIDX_SURFACE_SPINNER_HEIGHT].top }; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_SURFACE_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Raised corners + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, + widgets[WIDX_SURFACE_CHECK_CORNER_E].top }; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_CORNERS, {}, { colours[1] }); + break; + } + case TileElementType::Path: + { + // Details + auto pathEl = tileElement->AsPath(); + auto footpathObj = pathEl->GetLegacyPathEntry(); + if (footpathObj == nullptr) + { + // Surface name + auto surfaceObj = pathEl->GetSurfaceEntry(); + if (surfaceObj != nullptr) + { + auto ft = Formatter(); + ft.Add(surfaceObj->NameStringId); + DrawTextBasic( + dpi, screenCoords, STR_TILE_INSPECTOR_FOOTPATH_SURFACE_NAME, ft, { COLOUR_WHITE }); + } + + // Railings name + auto railingsObj = pathEl->GetRailingsEntry(); + if (railingsObj != nullptr) + { + auto ft = Formatter(); + ft.Add(railingsObj->NameStringId); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_FOOTPATH_RAILINGS_NAME, ft, + { COLOUR_WHITE }); + } + } + else + { + // Legacy path name + auto footpathEntry = reinterpret_cast(footpathObj->GetLegacyData()); + auto ft = Formatter(); + ft.Add(footpathEntry->string_idx); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_NAME, ft, { COLOUR_WHITE }); + } + + // Path addition + if (tileElement->AsPath()->HasAddition()) + { + const auto pathAdditionEntry = tileElement->AsPath()->GetAdditionEntry(); + StringId additionNameId = pathAdditionEntry != nullptr + ? pathAdditionEntry->name + : static_cast(STR_UNKNOWN_OBJECT_TYPE); + auto ft = Formatter(); + ft.Add(additionNameId); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS, ft, + { COLOUR_WHITE }); + } + else + { + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, {}, + { COLOUR_WHITE }); + } + + // Properties + // Raise / lower label + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_PATH_SPINNER_HEIGHT].top }; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_PATH_SPINNER_HEIGHT].left + 3; + auto ft = Formatter(); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Path connections + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_PATH_CHECK_EDGE_W].top }; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_CONNECTED_EDGES, {}, { colours[1] }); + break; + } + + case TileElementType::Track: + { + auto trackElement = tileElement->AsTrack(); + RideId id = trackElement->GetRideIndex(); + auto rideTile = GetRide(id); + + // Ride ID + auto ft = Formatter(); + ft.Add(id); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_TRACK_RIDE_ID, ft, { colours[1] }); + + // Ride name + if (rideTile != nullptr) + { + ft = Formatter(); + rideTile->FormatNameTo(ft); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_TRACK_RIDE_NAME, ft, + { colours[1] }); + } + + // Ride type. Individual pieces may be of a different ride type from the ride it belongs to. + const auto& rtd = GetRideTypeDescriptor(trackElement->GetRideType()); + ft = Formatter(); + ft.Add(rtd.Naming.Name); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_TRACK_RIDE_TYPE, ft, + { colours[1] }); + + // Track + ft = Formatter(); + ft.Add(trackElement->GetTrackType()); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_TRACK_PIECE_ID, ft, { colours[1] }); + + ft = Formatter(); + ft.Add(trackElement->GetSequenceIndex()); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 44 }, STR_TILE_INSPECTOR_TRACK_SEQUENCE, ft, { colours[1] }); + if (trackElement->IsStation()) + { + auto stationIndex = trackElement->GetStationIndex(); + ft = Formatter(); + ft.Add(STR_COMMA16); + ft.Add(stationIndex.ToUnderlying()); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, + { colours[1] }); + } + else + { + const char* stationNone = "-"; + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(stationNone); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, + { colours[1] }); + } + + ft = Formatter(); + ft.Add(ColourSchemeNames[trackElement->GetColourScheme()]); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 66 }, STR_TILE_INSPECTOR_COLOUR_SCHEME, ft, { colours[1] }); + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_TRACK_SPINNER_HEIGHT].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_TRACK_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + break; + } + + case TileElementType::SmallScenery: + { + // Details + // Age + auto ft = Formatter(); + ft.Add(tileElement->AsSmallScenery()->GetAge()); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_AGE, ft, { colours[1] }); + + // Quadrant value + const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); + if (sceneryEntry != nullptr && !(sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))) + { + int16_t quadrant = tileElement->AsSmallScenery()->GetSceneryQuadrant(); + static constexpr StringId _quadrantStringIdx[] = { + STR_TILE_INSPECTOR_SCENERY_QUADRANT_SW, + STR_TILE_INSPECTOR_SCENERY_QUADRANT_NW, + STR_TILE_INSPECTOR_SCENERY_QUADRANT_NE, + STR_TILE_INSPECTOR_SCENERY_QUADRANT_SE, + }; + ft = Formatter(); + ft.Add(_quadrantStringIdx[quadrant]); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SCENERY_QUADRANT, ft, + { colours[1] }); + } + + // Scenery ID + ft = Formatter(); + ft.Add(tileElement->AsSmallScenery()->GetEntryIndex()); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SCENERY_ENTRY_IDX, ft, + { colours[1] }); + + // Properties + // Raise / Lower + screenCoords.y = windowPos.y + widgets[WIDX_SCENERY_SPINNER_HEIGHT].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_SCENERY_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Quarter tile + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, + widgets[WIDX_SCENERY_CHECK_QUARTER_E].top }; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_QUADRANT_LABEL, {}, { colours[1] }); + + // Collision + screenCoords.y = windowPos.y + widgets[WIDX_SCENERY_CHECK_COLLISION_E].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_COLLISSION, {}, { colours[1] }); + break; + } + + case TileElementType::Entrance: + { + // Details + // Entrance type + auto ft = Formatter(); + ft.Add(EntranceTypeStringIds[tileElement->AsEntrance()->GetEntranceType()]); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRANCE_TYPE, ft, { colours[1] }); + + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + { + // TODO: Make this work with Left/Right park entrance parts + ft = Formatter(); + ft.Add(ParkEntranceGetIndex({ _toolMap, tileElement->GetBaseZ() })); DrawTextBasic( dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft, { colours[1] }); } else { - // Ride exit ID + ft = Formatter(); + ft.Add(tileElement->AsEntrance()->GetStationIndex().ToUnderlying()); + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + { + // Ride entrance ID + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft, + { colours[1] }); + } + else + { + // Ride exit ID + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_EXIT_ID, ft, + { colours[1] }); + } + } + + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + { + // Entrance part + ft = Formatter(); + ft.Add(ParkEntrancePartStringIds[tileElement->AsEntrance()->GetSequenceIndex()]); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_EXIT_ID, ft, + dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_PART, ft, + { colours[1] }); + } + else + { + // Ride ID + ft = Formatter(); + ft.Add(tileElement->AsEntrance()->GetRideIndex()); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_RIDE_ID, ft, + { colours[1] }); + // Station index + auto stationIndex = tileElement->AsEntrance()->GetStationIndex(); + ft = Formatter(); + ft.Add(STR_COMMA16); + ft.Add(stationIndex.ToUnderlying()); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { colours[1] }); } - } - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) - { - // Entrance part + // Properties + // Raise / Lower + screenCoords.y = windowPos.y + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].left + 3; ft = Formatter(); - ft.Add(ParkEntrancePartStringIds[tileElement->AsEntrance()->GetSequenceIndex()]); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_PART, ft, { colours[1] }); - } - else - { - // Ride ID - ft = Formatter(); - ft.Add(tileElement->AsEntrance()->GetRideIndex()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_RIDE_ID, ft, - { colours[1] }); - // Station index - auto stationIndex = tileElement->AsEntrance()->GetStationIndex(); - ft = Formatter(); - ft.Add(STR_COMMA16); - ft.Add(stationIndex.ToUnderlying()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { colours[1] }); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + break; } - // Properties - // Raise / Lower - screenCoords.y = windowPos.y + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); - break; - } - - case TileElementType::Wall: - { - // Details - // Type - auto ft = Formatter(); - ft.Add(tileElement->AsWall()->GetEntryIndex()); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_TYPE, ft, { colours[1] }); - - // Banner info - auto banner = tileElement->AsWall()->GetBanner(); - if (banner != nullptr) + case TileElementType::Wall: { - ft = Formatter(); - banner->FormatTextTo(ft); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, - { colours[1] }); - } - else - { - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, - { colours[1] }); - } + // Details + // Type + auto ft = Formatter(); + ft.Add(tileElement->AsWall()->GetEntryIndex()); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_TYPE, ft, { colours[1] }); - // Properties - // Raise / lower label - screenCoords.y = windowPos.y + widgets[WIDX_WALL_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_WALL_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); - - // Slope label - screenCoords = windowPos - + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_WALL_DROPDOWN_SLOPE].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_SLOPE, {}, { colours[1] }); - - // Animation frame label - screenCoords.y = windowPos.y + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME, {}, { colours[1] }); - - // Current animation frame - colour_t colour = colours[1]; - if (IsWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME)) - { - colour = colours[0] | COLOUR_FLAG_INSET; - } - screenCoords.x = windowPos.x + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].left + 3; - ft = Formatter(); - ft.Add(tileElement->AsWall()->GetAnimationFrame()); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colour }); - break; - } - - case TileElementType::LargeScenery: - { - // Details - // Type - auto sceneryElement = tileElement->AsLargeScenery(); - ObjectEntryIndex largeSceneryType = sceneryElement->GetEntryIndex(); - auto ft = Formatter(); - ft.Add(largeSceneryType); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_LARGE_SCENERY_TYPE, ft, { colours[1] }); - - // Part ID - ft = Formatter(); - ft.Add(sceneryElement->GetSequenceIndex()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_LARGE_SCENERY_PIECE_ID, ft, - { colours[1] }); - - // Banner info - auto* largeSceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(largeSceneryType); - if (largeSceneryEntry != nullptr && largeSceneryEntry->scrolling_mode != SCROLLING_MODE_NONE) - { - auto banner = sceneryElement->GetBanner(); + // Banner info + auto banner = tileElement->AsWall()->GetBanner(); if (banner != nullptr) { ft = Formatter(); banner->FormatTextTo(ft); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, { colours[1] }); } + else + { + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, + { colours[1] }); + } + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_WALL_SPINNER_HEIGHT].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_WALL_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Slope label + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_WALL_DROPDOWN_SLOPE].top }; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_SLOPE, {}, { colours[1] }); + + // Animation frame label + screenCoords.y = windowPos.y + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME, {}, { colours[1] }); + + // Current animation frame + colour_t colour = colours[1]; + if (IsWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME)) + { + colour = colours[0] | COLOUR_FLAG_INSET; + } + screenCoords.x = windowPos.x + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].left + 3; + ft = Formatter(); + ft.Add(tileElement->AsWall()->GetAnimationFrame()); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colour }); + break; } - else + + case TileElementType::LargeScenery: { + // Details + // Type + auto sceneryElement = tileElement->AsLargeScenery(); + ObjectEntryIndex largeSceneryType = sceneryElement->GetEntryIndex(); + auto ft = Formatter(); + ft.Add(largeSceneryType); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_LARGE_SCENERY_TYPE, ft, { colours[1] }); + + // Part ID + ft = Formatter(); + ft.Add(sceneryElement->GetSequenceIndex()); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, + dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_LARGE_SCENERY_PIECE_ID, ft, { colours[1] }); + + // Banner info + auto* largeSceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(largeSceneryType); + if (largeSceneryEntry != nullptr && largeSceneryEntry->scrolling_mode != SCROLLING_MODE_NONE) + { + auto banner = sceneryElement->GetBanner(); + if (banner != nullptr) + { + ft = Formatter(); + banner->FormatTextTo(ft); + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, + { colours[1] }); + } + } + else + { + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, + { colours[1] }); + } + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + break; } - // Properties - // Raise / lower label - screenCoords.y = windowPos.y + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); - break; - } - - case TileElementType::Banner: - { - // Details - // Banner info - auto banner = tileElement->AsBanner()->GetBanner(); - if (banner != nullptr) + case TileElementType::Banner: { - Formatter ft; - banner->FormatTextTo(ft); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, { colours[1] }); + // Details + // Banner info + auto banner = tileElement->AsBanner()->GetBanner(); + if (banner != nullptr) + { + Formatter ft; + banner->FormatTextTo(ft); + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, { colours[1] }); + } + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_BANNER_SPINNER_HEIGHT].top; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_BANNER_SPINNER_HEIGHT].left + 3; + auto ft = Formatter(); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Blocked paths + screenCoords.y += 28; + screenCoords.x = windowPos.x + widgets[WIDX_GROUPBOX_DETAILS].left + 7; + DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, {}, { colours[1] }); + break; } - // Properties - // Raise / lower label - screenCoords.y = windowPos.y + widgets[WIDX_BANNER_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); - - // Current base height - screenCoords.x = windowPos.x + widgets[WIDX_BANNER_SPINNER_HEIGHT].left + 3; - auto ft = Formatter(); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); - - // Blocked paths - screenCoords.y += 28; - screenCoords.x = windowPos.x + widgets[WIDX_GROUPBOX_DETAILS].left + 7; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, {}, { colours[1] }); - break; + default: + break; } - - default: - break; } } - } - void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override - { - const int32_t listWidth = widgets[WIDX_LIST].width(); - GfxFillRect( - dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - - // Show usage hint when nothing is selected - if (!_tileSelected) + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override { - auto& listWidget = widgets[WIDX_LIST]; - auto centrePos = ScreenCoordsXY{ listWidget.width() / 2, - (listWidget.height() - FontGetLineHeight(FontStyle::Medium)) / 2 }; - auto ft = Formatter{}; - auto textPaint = TextPaint{ colours[1], TextAlignment::CENTRE }; - DrawTextWrapped(dpi, centrePos, listWidth, STR_TILE_INSPECTOR_SELECT_TILE_HINT, ft, textPaint); - return; - } + const int32_t listWidth = widgets[WIDX_LIST].width(); + GfxFillRect( + dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - ScreenCoordsXY screenCoords{}; - screenCoords.y = SCROLLABLE_ROW_HEIGHT * (windowTileInspectorElementCount - 1); - int32_t i = 0; - char buffer[256]; - - const TileElement* tileElement = MapGetFirstElementAt(_toolMap); - - do - { - if (tileElement == nullptr) - break; - - const bool selectedRow = i == windowTileInspectorSelectedIndex; - const bool hoveredRow = i == _highlightedIndex; - const char* typeName = ""; - - // Draw row background colour - auto fillRectangle = ScreenRect{ { 0, screenCoords.y }, { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; - if (selectedRow) - GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark); - else if (hoveredRow) - GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark | 0x1000000); - // Zebra stripes - else if (((windowTileInspectorElementCount - i) & 1) == 0) - GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].light | 0x1000000); - - const StringId stringFormat = (selectedRow || hoveredRow) ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; - auto checkboxFormatter = Formatter(); - checkboxFormatter.Add(STR_STRING); - checkboxFormatter.Add(CheckBoxMarkString); - - // Draw checkbox and check if visible - GfxFillRectInset(dpi, { { 2, screenCoords.y }, { 15, screenCoords.y + 11 } }, colours[1], INSET_RECT_F_E0); - if (!tileElement->IsInvisible()) + // Show usage hint when nothing is selected + if (!_tileSelected) { - auto eyeFormatter = Formatter(); - eyeFormatter.Add(STR_STRING); - eyeFormatter.Add(EyeString); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 2, 1 }, stringFormat, eyeFormatter); + auto& listWidget = widgets[WIDX_LIST]; + auto centrePos = ScreenCoordsXY{ listWidget.width() / 2, + (listWidget.height() - FontGetLineHeight(FontStyle::Medium)) / 2 }; + auto ft = Formatter{}; + auto textPaint = TextPaint{ colours[1], TextAlignment::CENTRE }; + DrawTextWrapped(dpi, centrePos, listWidth, STR_TILE_INSPECTOR_SELECT_TILE_HINT, ft, textPaint); + return; } - const auto type = tileElement->GetType(); - switch (type) + ScreenCoordsXY screenCoords{}; + screenCoords.y = SCROLLABLE_ROW_HEIGHT * (windowTileInspectorElementCount - 1); + int32_t i = 0; + char buffer[256]; + + const TileElement* tileElement = MapGetFirstElementAt(_toolMap); + + do { - case TileElementType::Surface: - typeName = LanguageGetString(STR_TILE_INSPECTOR_SURFACE); + if (tileElement == nullptr) break; - case TileElementType::Path: - typeName = tileElement->AsPath()->IsQueue() ? LanguageGetString(STR_QUEUE_LINE_MAP_TIP) - : LanguageGetString(STR_FOOTPATH_MAP_TIP); - break; + const bool selectedRow = i == windowTileInspectorSelectedIndex; + const bool hoveredRow = i == _highlightedIndex; + const char* typeName = ""; - case TileElementType::Track: - typeName = LanguageGetString(STR_RIDE_COMPONENT_TRACK_CAPITALISED); - break; + // Draw row background colour + auto fillRectangle = ScreenRect{ { 0, screenCoords.y }, + { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; + if (selectedRow) + GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark); + else if (hoveredRow) + GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark | 0x1000000); + // Zebra stripes + else if (((windowTileInspectorElementCount - i) & 1) == 0) + GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].light | 0x1000000); - case TileElementType::SmallScenery: + const StringId stringFormat = (selectedRow || hoveredRow) ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; + auto checkboxFormatter = Formatter(); + checkboxFormatter.Add(STR_STRING); + checkboxFormatter.Add(CheckBoxMarkString); + + // Draw checkbox and check if visible + GfxFillRectInset(dpi, { { 2, screenCoords.y }, { 15, screenCoords.y + 11 } }, colours[1], INSET_RECT_F_E0); + if (!tileElement->IsInvisible()) { - const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); - snprintf( - buffer, sizeof(buffer), "%s (%s)", LanguageGetString(STR_OBJECT_SELECTION_SMALL_SCENERY), - sceneryEntry != nullptr ? LanguageGetString(sceneryEntry->name) : ""); - typeName = buffer; - break; + auto eyeFormatter = Formatter(); + eyeFormatter.Add(STR_STRING); + eyeFormatter.Add(EyeString); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 2, 1 }, stringFormat, eyeFormatter); } - case TileElementType::Entrance: - typeName = LanguageGetString(STR_RIDE_CONSTRUCTION_ENTRANCE); - break; - - case TileElementType::Wall: + const auto type = tileElement->GetType(); + switch (type) { - const auto* entry = tileElement->AsWall()->GetEntry(); - snprintf( - buffer, sizeof(buffer), "%s (%s)", LanguageGetString(STR_TILE_INSPECTOR_WALL), - entry != nullptr ? LanguageGetString(entry->name) : ""); - typeName = buffer; - break; + case TileElementType::Surface: + typeName = LanguageGetString(STR_TILE_INSPECTOR_SURFACE); + break; + + case TileElementType::Path: + typeName = tileElement->AsPath()->IsQueue() ? LanguageGetString(STR_QUEUE_LINE_MAP_TIP) + : LanguageGetString(STR_FOOTPATH_MAP_TIP); + break; + + case TileElementType::Track: + typeName = LanguageGetString(STR_RIDE_COMPONENT_TRACK_CAPITALISED); + break; + + case TileElementType::SmallScenery: + { + const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); + snprintf( + buffer, sizeof(buffer), "%s (%s)", LanguageGetString(STR_OBJECT_SELECTION_SMALL_SCENERY), + sceneryEntry != nullptr ? LanguageGetString(sceneryEntry->name) : ""); + typeName = buffer; + break; + } + + case TileElementType::Entrance: + typeName = LanguageGetString(STR_RIDE_CONSTRUCTION_ENTRANCE); + break; + + case TileElementType::Wall: + { + const auto* entry = tileElement->AsWall()->GetEntry(); + snprintf( + buffer, sizeof(buffer), "%s (%s)", LanguageGetString(STR_TILE_INSPECTOR_WALL), + entry != nullptr ? LanguageGetString(entry->name) : ""); + typeName = buffer; + break; + } + + case TileElementType::LargeScenery: + typeName = LanguageGetString(STR_OBJECT_SELECTION_LARGE_SCENERY); + break; + + case TileElementType::Banner: + snprintf( + buffer, sizeof(buffer), "%s (%u)", LanguageGetString(STR_BANNER_WINDOW_TITLE), + tileElement->AsBanner()->GetIndex().ToUnderlying()); + typeName = buffer; + break; + + default: + snprintf( + buffer, sizeof(buffer), "%s (%d)", LanguageGetString(STR_UNKNOWN_OBJECT_TYPE), EnumValue(type)); + typeName = buffer; } - case TileElementType::LargeScenery: - typeName = LanguageGetString(STR_OBJECT_SELECTION_LARGE_SCENERY); - break; + const int32_t clearanceHeight = tileElement->ClearanceHeight; + const bool ghost = tileElement->IsGhost(); + const bool last = tileElement->IsLastForTile(); - case TileElementType::Banner: - snprintf( - buffer, sizeof(buffer), "%s (%u)", LanguageGetString(STR_BANNER_WINDOW_TITLE), - tileElement->AsBanner()->GetIndex().ToUnderlying()); - typeName = buffer; - break; + // Element name + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(typeName); + DrawTextEllipsised( + dpi, screenCoords + ScreenCoordsXY{ TypeColumnXY.x, 0 }, TypeColumnSize.width, stringFormat, ft); - default: - snprintf(buffer, sizeof(buffer), "%s (%d)", LanguageGetString(STR_UNKNOWN_OBJECT_TYPE), EnumValue(type)); - typeName = buffer; + // Base height + ft = Formatter(); + ft.Add(STR_FORMAT_INTEGER); + ft.Add(tileElement->BaseHeight); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ BaseHeightColumnXY.x, 0 }, stringFormat, ft); + + // Clearance height + ft = Formatter(); + ft.Add(STR_FORMAT_INTEGER); + ft.Add(clearanceHeight); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ ClearanceHeightColumnXY.x, 0 }, stringFormat, ft); + + // Direction + ft = Formatter(); + ft.Add(STR_FORMAT_INTEGER); + ft.Add(tileElement->GetDirection()); + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ DirectionColumnXY.x, 0 }, stringFormat, ft); + + // Checkmarks for ghost and last for tile + if (ghost) + DrawTextBasic( + dpi, screenCoords + ScreenCoordsXY{ GhostFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); + if (last) + DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ LastFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); + + screenCoords.y -= SCROLLABLE_ROW_HEIGHT; + i++; + } while (!(tileElement++)->IsLastForTile()); + } + + void ClearClipboard() + { + _elementCopied = false; + } + + void ToggleInvisibility() + { + if (windowTileInspectorSelectedIndex >= 0 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount) + { + ToggleInvisibility(windowTileInspectorSelectedIndex); } - - const int32_t clearanceHeight = tileElement->ClearanceHeight; - const bool ghost = tileElement->IsGhost(); - const bool last = tileElement->IsLastForTile(); - - // Element name - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(typeName); - DrawTextEllipsised(dpi, screenCoords + ScreenCoordsXY{ TypeColumnXY.x, 0 }, TypeColumnSize.width, stringFormat, ft); - - // Base height - ft = Formatter(); - ft.Add(STR_FORMAT_INTEGER); - ft.Add(tileElement->BaseHeight); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ BaseHeightColumnXY.x, 0 }, stringFormat, ft); - - // Clearance height - ft = Formatter(); - ft.Add(STR_FORMAT_INTEGER); - ft.Add(clearanceHeight); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ ClearanceHeightColumnXY.x, 0 }, stringFormat, ft); - - // Direction - ft = Formatter(); - ft.Add(STR_FORMAT_INTEGER); - ft.Add(tileElement->GetDirection()); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ DirectionColumnXY.x, 0 }, stringFormat, ft); - - // Checkmarks for ghost and last for tile - if (ghost) - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ GhostFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); - if (last) - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ LastFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); - - screenCoords.y -= SCROLLABLE_ROW_HEIGHT; - i++; - } while (!(tileElement++)->IsLastForTile()); - } - - void ClearClipboard() - { - _elementCopied = false; - } - - void ToggleInvisibility() - { - if (windowTileInspectorSelectedIndex >= 0 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount) - { - ToggleInvisibility(windowTileInspectorSelectedIndex); - } - } - -private: - void SetPage(const TileInspectorPage p) - { - Invalidate(); - // subtract current page height, then add new page height - if (tileInspectorPage != TileInspectorPage::Default) - { - auto index = EnumValue(tileInspectorPage) - 1; - height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - min_height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - } - if (p != TileInspectorPage::Default) - { - auto index = EnumValue(p) - 1; - height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - min_height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - } - tileInspectorPage = p; - auto pageIndex = EnumValue(p); - widgets = PageWidgets[pageIndex]; - hold_down_widgets = PageHoldDownWidgets[pageIndex]; - disabled_widgets = PageDisabledWidgets[pageIndex]; - pressed_widgets = 0; - Invalidate(); - } - - void UpdateSelectedTile(const ScreenCoordsXY& screenCoords) - { - const bool ctrlIsHeldDown = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z); - // Mouse hasn't moved - if (screenCoords.x == _toolMouseX && screenCoords.y == _toolMouseY && _toolCtrlDown == ctrlIsHeldDown) - return; - - _toolMouseX = screenCoords.x; - _toolMouseY = screenCoords.y; - _toolCtrlDown = ctrlIsHeldDown; - - CoordsXY mapCoords{}; - TileElement* clickedElement = nullptr; - if (ctrlIsHeldDown) - { - auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionFlags); - clickedElement = info.Element; - mapCoords = info.Loc; } - // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor - if (clickedElement == nullptr) + private: + void SetPage(const TileInspectorPage p) { - auto mouseCoords = ScreenPosToMapPos(screenCoords, nullptr); - if (!mouseCoords.has_value()) + Invalidate(); + // subtract current page height, then add new page height + if (tileInspectorPage != TileInspectorPage::Default) + { + auto index = EnumValue(tileInspectorPage) - 1; + height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + min_height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + } + if (p != TileInspectorPage::Default) + { + auto index = EnumValue(p) - 1; + height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + min_height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + } + tileInspectorPage = p; + auto pageIndex = EnumValue(p); + widgets = PageWidgets[pageIndex]; + hold_down_widgets = PageHoldDownWidgets[pageIndex]; + disabled_widgets = PageDisabledWidgets[pageIndex]; + pressed_widgets = 0; + Invalidate(); + } + + void UpdateSelectedTile(const ScreenCoordsXY& screenCoords) + { + const bool ctrlIsHeldDown = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z); + // Mouse hasn't moved + if (screenCoords.x == _toolMouseX && screenCoords.y == _toolMouseY && _toolCtrlDown == ctrlIsHeldDown) return; - mapCoords = mouseCoords.value(); - // Tile is already selected - if (_tileSelected && mapCoords.x == _toolMap.x && mapCoords.y == _toolMap.y) - return; + _toolMouseX = screenCoords.x; + _toolMouseY = screenCoords.y; + _toolCtrlDown = ctrlIsHeldDown; + + CoordsXY mapCoords{}; + TileElement* clickedElement = nullptr; + if (ctrlIsHeldDown) + { + auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionFlags); + clickedElement = info.Element; + mapCoords = info.Loc; + } + + // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor + if (clickedElement == nullptr) + { + auto mouseCoords = ScreenPosToMapPos(screenCoords, nullptr); + if (!mouseCoords.has_value()) + return; + + mapCoords = mouseCoords.value(); + // Tile is already selected + if (_tileSelected && mapCoords.x == _toolMap.x && mapCoords.y == _toolMap.y) + return; + } + + // Invalidate the previous selection + if (auto* elem = OpenRCT2::TileInspector::GetSelectedElement(); elem != nullptr) + { + MapInvalidateElement(windowTileInspectorTile.ToCoordsXY(), elem); + } + + _tileSelected = true; + _toolMap = mapCoords; + windowTileInspectorTile = TileCoordsXY(mapCoords); + LoadTile(clickedElement); } - // Invalidate the previous selection - if (auto* elem = OpenRCT2::TileInspector::GetSelectedElement(); elem != nullptr) + void SelectElementFromList(int32_t index) { - MapInvalidateElement(windowTileInspectorTile.ToCoordsXY(), elem); + if (index < 0 || index >= windowTileInspectorElementCount) + { + windowTileInspectorSelectedIndex = -1; + } + else + { + windowTileInspectorSelectedIndex = index; + } + Invalidate(); } - _tileSelected = true; - _toolMap = mapCoords; - windowTileInspectorTile = TileCoordsXY(mapCoords); - LoadTile(clickedElement); - } - - void SelectElementFromList(int32_t index) - { - if (index < 0 || index >= windowTileInspectorElementCount) + void LoadTile(TileElement* elementToSelect) { windowTileInspectorSelectedIndex = -1; + scrolls[0].v_top = 0; + + TileElement* element = MapGetFirstElementAt(_toolMap); + int16_t numItems = 0; + do + { + if (element == nullptr) + break; + if (element == elementToSelect) + windowTileInspectorSelectedIndex = numItems; + + numItems++; + } while (!(element++)->IsLastForTile()); + windowTileInspectorElementCount = numItems; + Invalidate(); } - else + + void RemoveElement(int32_t elementIndex) { - windowTileInspectorSelectedIndex = index; + Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyRemove, elementIndex); + GameActions::Execute(&modifyTile); } - Invalidate(); - } - void LoadTile(TileElement* elementToSelect) - { - windowTileInspectorSelectedIndex = -1; - scrolls[0].v_top = 0; - - TileElement* element = MapGetFirstElementAt(_toolMap); - int16_t numItems = 0; - do + void RotateElement(int32_t elementIndex) { - if (element == nullptr) - break; - if (element == elementToSelect) - windowTileInspectorSelectedIndex = numItems; - - numItems++; - } while (!(element++)->IsLastForTile()); - windowTileInspectorElementCount = numItems; - Invalidate(); - } - - void RemoveElement(int32_t elementIndex) - { - Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyRemove, elementIndex); - GameActions::Execute(&modifyTile); - } - - void RotateElement(int32_t elementIndex) - { - Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyRotate, elementIndex); - GameActions::Execute(&modifyTile); - } - - // Swap element with its parent - void SwapElements(int16_t first, int16_t second) - { - bool firstInRange = first >= 0 && first < windowTileInspectorElementCount; - bool secondInRange = second >= 0 && second < windowTileInspectorElementCount; - // This might happen if two people are modifying the same tile. - if (!firstInRange || !secondInRange) - return; - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnySwap, first, second); - GameActions::Execute(&modifyTile); - } - - void SortElements() - { - Guard::Assert(_tileSelected, "No tile selected"); - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnySort); - GameActions::Execute(&modifyTile); - } - - void CopyElement() - { - const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); - Guard::Assert(tileElement != nullptr, "Invalid tile element"); - // Copy value, in case the element gets moved - _copiedElement = *tileElement; - _copiedBanner = {}; - auto bannerIndex = _copiedElement.GetBannerIndex(); - if (bannerIndex != BannerIndex::GetNull()) - { - auto banner = GetBanner(bannerIndex); - if (banner != nullptr) - _copiedBanner = *banner; + Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyRotate, elementIndex); + GameActions::Execute(&modifyTile); } - _elementCopied = true; - Invalidate(); - } - void PasteElement() - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyPaste, 0, 0, _copiedElement, _copiedBanner); - GameActions::Execute(&modifyTile); - } - - void BaseHeightOffset(int16_t elementIndex, int8_t heightOffset) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyBaseHeightOffset, elementIndex, heightOffset); - GameActions::Execute(&modifyTile); - } - - void SurfaceShowParkFences(bool showFences) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceShowParkFences, showFences); - GameActions::Execute(&modifyTile); - } - - void SurfaceToggleCorner(int32_t cornerIndex) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceToggleCorner, cornerIndex); - GameActions::Execute(&modifyTile); - } - - void SurfaceToggleDiagonal() - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceToggleDiagonal); - GameActions::Execute(&modifyTile); - } - - void PathSetSloped(int32_t elementIndex, bool sloped) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathSetSlope, elementIndex, sloped); - GameActions::Execute(&modifyTile); - } - - void PathSetJunctionRailings(int32_t elementIndex, bool hasJunctionRailings) - { - auto modifyTile = TileModifyAction( - _toolMap, TileModifyType::PathSetJunctionRailings, elementIndex, hasJunctionRailings); - GameActions::Execute(&modifyTile); - } - - void PathSetBroken(int32_t elementIndex, bool broken) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathSetBroken, elementIndex, broken); - GameActions::Execute(&modifyTile); - } - - void PathToggleEdge(int32_t elementIndex, int32_t cornerIndex) - { - Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - Guard::Assert(cornerIndex >= 0 && cornerIndex < 8, "cornerIndex out of range"); - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathToggleEdge, elementIndex, cornerIndex); - GameActions::Execute(&modifyTile); - } - - void EntranceMakeUsable(int32_t elementIndex) - { - Guard::ArgumentInRange(elementIndex, 0, windowTileInspectorElementCount - 1); - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::EntranceMakeUsable, elementIndex); - GameActions::Execute(&modifyTile); - } - - void WallSetSlope(int32_t elementIndex, int32_t slopeValue) - { - // Make sure only the correct bits are set - Guard::Assert((slopeValue & 3) == slopeValue, "slopeValue doesn't match its mask"); - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::WallSetSlope, elementIndex, slopeValue); - GameActions::Execute(&modifyTile); - } - - void WallAnimationFrameOffset(int16_t elementIndex, int8_t animationFrameOffset) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::WallSetAnimationFrame, elementIndex, animationFrameOffset); - GameActions::Execute(&modifyTile); - } - - void TrackBlockHeightOffset(int32_t elementIndex, int8_t heightOffset) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackBaseHeightOffset, elementIndex, heightOffset); - GameActions::Execute(&modifyTile); - } - - void TrackBlockSetLift(int32_t elementIndex, bool entireTrackBlock, bool chain) - { - auto modifyTile = TileModifyAction( - _toolMap, entireTrackBlock ? TileModifyType::TrackSetChainBlock : TileModifyType::TrackSetChain, elementIndex, - chain); - GameActions::Execute(&modifyTile); - } - - void TrackSetBrakeClosed(int32_t elementIndex, bool isClosed) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackSetBrake, elementIndex, isClosed); - GameActions::Execute(&modifyTile); - } - - void TrackSetIndestructible(int32_t elementIndex, bool isIndestructible) - { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackSetIndestructible, elementIndex, isIndestructible); - GameActions::Execute(&modifyTile); - } - - void QuarterTileSet(int32_t elementIndex, const int32_t quarterIndex) - { - // quarterIndex is widget index relative to WIDX_SCENERY_CHECK_QUARTER_N, so a value from 0-3 - Guard::Assert(quarterIndex >= 0 && quarterIndex < 4, "quarterIndex out of range"); - auto modifyTile = TileModifyAction( - _toolMap, TileModifyType::ScenerySetQuarterLocation, elementIndex, (quarterIndex - GetCurrentRotation()) & 3); - GameActions::Execute(&modifyTile); - } - - // ToggleQuadrantCollision? - void ToggleQuadrantCollosion(int32_t elementIndex, const int32_t quadrantIndex) - { - auto modifyTile = TileModifyAction( - _toolMap, TileModifyType::ScenerySetQuarterCollision, elementIndex, (quadrantIndex + 2 - GetCurrentRotation()) & 3); - GameActions::Execute(&modifyTile); - } - - void BannerToggleBlock(int32_t elementIndex, int32_t edgeIndex) - { - Guard::Assert(edgeIndex >= 0 && edgeIndex < 4, "edgeIndex out of range"); - // Make edgeIndex abstract - edgeIndex = (edgeIndex - GetCurrentRotation()) & 3; - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::BannerToggleBlockingEdge, elementIndex, edgeIndex); - GameActions::Execute(&modifyTile); - } - - void ToggleInvisibility(int32_t elementIndex) - { - Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyToggleInvisilibity, elementIndex); - GameActions::Execute(&modifyTile); - } - - void OnPrepareDraw() override - { - const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); - - // Set the correct page automatically - TileInspectorPage p = TileInspectorPage::Default; - if (tileElement != nullptr) + // Swap element with its parent + void SwapElements(int16_t first, int16_t second) { + bool firstInRange = first >= 0 && first < windowTileInspectorElementCount; + bool secondInRange = second >= 0 && second < windowTileInspectorElementCount; + // This might happen if two people are modifying the same tile. + if (!firstInRange || !secondInRange) + return; + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnySwap, first, second); + GameActions::Execute(&modifyTile); + } + + void SortElements() + { + Guard::Assert(_tileSelected, "No tile selected"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnySort); + GameActions::Execute(&modifyTile); + } + + void CopyElement() + { + const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); + Guard::Assert(tileElement != nullptr, "Invalid tile element"); + // Copy value, in case the element gets moved + _copiedElement = *tileElement; + _copiedBanner = {}; + auto bannerIndex = _copiedElement.GetBannerIndex(); + if (bannerIndex != BannerIndex::GetNull()) + { + auto banner = GetBanner(bannerIndex); + if (banner != nullptr) + _copiedBanner = *banner; + } + _elementCopied = true; + Invalidate(); + } + + void PasteElement() + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyPaste, 0, 0, _copiedElement, _copiedBanner); + GameActions::Execute(&modifyTile); + } + + void BaseHeightOffset(int16_t elementIndex, int8_t heightOffset) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyBaseHeightOffset, elementIndex, heightOffset); + GameActions::Execute(&modifyTile); + } + + void SurfaceShowParkFences(bool showFences) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceShowParkFences, showFences); + GameActions::Execute(&modifyTile); + } + + void SurfaceToggleCorner(int32_t cornerIndex) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceToggleCorner, cornerIndex); + GameActions::Execute(&modifyTile); + } + + void SurfaceToggleDiagonal() + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceToggleDiagonal); + GameActions::Execute(&modifyTile); + } + + void PathSetSloped(int32_t elementIndex, bool sloped) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathSetSlope, elementIndex, sloped); + GameActions::Execute(&modifyTile); + } + + void PathSetJunctionRailings(int32_t elementIndex, bool hasJunctionRailings) + { + auto modifyTile = TileModifyAction( + _toolMap, TileModifyType::PathSetJunctionRailings, elementIndex, hasJunctionRailings); + GameActions::Execute(&modifyTile); + } + + void PathSetBroken(int32_t elementIndex, bool broken) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathSetBroken, elementIndex, broken); + GameActions::Execute(&modifyTile); + } + + void PathToggleEdge(int32_t elementIndex, int32_t cornerIndex) + { + Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + Guard::Assert(cornerIndex >= 0 && cornerIndex < 8, "cornerIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathToggleEdge, elementIndex, cornerIndex); + GameActions::Execute(&modifyTile); + } + + void EntranceMakeUsable(int32_t elementIndex) + { + Guard::ArgumentInRange(elementIndex, 0, windowTileInspectorElementCount - 1); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::EntranceMakeUsable, elementIndex); + GameActions::Execute(&modifyTile); + } + + void WallSetSlope(int32_t elementIndex, int32_t slopeValue) + { + // Make sure only the correct bits are set + Guard::Assert((slopeValue & 3) == slopeValue, "slopeValue doesn't match its mask"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::WallSetSlope, elementIndex, slopeValue); + GameActions::Execute(&modifyTile); + } + + void WallAnimationFrameOffset(int16_t elementIndex, int8_t animationFrameOffset) + { + auto modifyTile = TileModifyAction( + _toolMap, TileModifyType::WallSetAnimationFrame, elementIndex, animationFrameOffset); + GameActions::Execute(&modifyTile); + } + + void TrackBlockHeightOffset(int32_t elementIndex, int8_t heightOffset) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackBaseHeightOffset, elementIndex, heightOffset); + GameActions::Execute(&modifyTile); + } + + void TrackBlockSetLift(int32_t elementIndex, bool entireTrackBlock, bool chain) + { + auto modifyTile = TileModifyAction( + _toolMap, entireTrackBlock ? TileModifyType::TrackSetChainBlock : TileModifyType::TrackSetChain, elementIndex, + chain); + GameActions::Execute(&modifyTile); + } + + void TrackSetBrakeClosed(int32_t elementIndex, bool isClosed) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackSetBrake, elementIndex, isClosed); + GameActions::Execute(&modifyTile); + } + + void TrackSetIndestructible(int32_t elementIndex, bool isIndestructible) + { + auto modifyTile = TileModifyAction( + _toolMap, TileModifyType::TrackSetIndestructible, elementIndex, isIndestructible); + GameActions::Execute(&modifyTile); + } + + void QuarterTileSet(int32_t elementIndex, const int32_t quarterIndex) + { + // quarterIndex is widget index relative to WIDX_SCENERY_CHECK_QUARTER_N, so a value from 0-3 + Guard::Assert(quarterIndex >= 0 && quarterIndex < 4, "quarterIndex out of range"); + auto modifyTile = TileModifyAction( + _toolMap, TileModifyType::ScenerySetQuarterLocation, elementIndex, (quarterIndex - GetCurrentRotation()) & 3); + GameActions::Execute(&modifyTile); + } + + // ToggleQuadrantCollision? + void ToggleQuadrantCollosion(int32_t elementIndex, const int32_t quadrantIndex) + { + auto modifyTile = TileModifyAction( + _toolMap, TileModifyType::ScenerySetQuarterCollision, elementIndex, + (quadrantIndex + 2 - GetCurrentRotation()) & 3); + GameActions::Execute(&modifyTile); + } + + void BannerToggleBlock(int32_t elementIndex, int32_t edgeIndex) + { + Guard::Assert(edgeIndex >= 0 && edgeIndex < 4, "edgeIndex out of range"); + // Make edgeIndex abstract + edgeIndex = (edgeIndex - GetCurrentRotation()) & 3; + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::BannerToggleBlockingEdge, elementIndex, edgeIndex); + GameActions::Execute(&modifyTile); + } + + void ToggleInvisibility(int32_t elementIndex) + { + Guard::Assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyToggleInvisilibity, elementIndex); + GameActions::Execute(&modifyTile); + } + + void OnPrepareDraw() override + { + const TileElement* const tileElement = OpenRCT2::TileInspector::GetSelectedElement(); + + // Set the correct page automatically + TileInspectorPage p = TileInspectorPage::Default; + if (tileElement != nullptr) + { + switch (tileElement->GetType()) + { + case TileElementType::Surface: + p = TileInspectorPage::Surface; + break; + + case TileElementType::Path: + p = TileInspectorPage::Path; + break; + + case TileElementType::Track: + p = TileInspectorPage::Track; + break; + + case TileElementType::SmallScenery: + p = TileInspectorPage::Scenery; + break; + + case TileElementType::Entrance: + p = TileInspectorPage::Entrance; + break; + + case TileElementType::Wall: + p = TileInspectorPage::Wall; + break; + + case TileElementType::LargeScenery: + p = TileInspectorPage::LargeScenery; + break; + + case TileElementType::Banner: + p = TileInspectorPage::Banner; + break; + } + } + + if (tileInspectorPage != p) + { + SetPage(p); + Invalidate(); + } + // X and Y spinners + SetWidgetDisabledAndInvalidate( + WIDX_SPINNER_X_INCREASE, !(_tileSelected && ((_toolMap.x / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); + SetWidgetDisabledAndInvalidate(WIDX_SPINNER_X_DECREASE, !(_tileSelected && ((_toolMap.x / 32) > 0))); + SetWidgetDisabledAndInvalidate( + WIDX_SPINNER_Y_INCREASE, !(_tileSelected && ((_toolMap.y / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); + SetWidgetDisabledAndInvalidate(WIDX_SPINNER_Y_DECREASE, !(_tileSelected && ((_toolMap.y / 32) > 0))); + + // Sort buttons + SetWidgetDisabledAndInvalidate(WIDX_BUTTON_SORT, !(_tileSelected && windowTileInspectorElementCount > 1)); + + // Move Up button + SetWidgetDisabledAndInvalidate( + WIDX_BUTTON_MOVE_UP, + !(windowTileInspectorSelectedIndex != -1 + && windowTileInspectorSelectedIndex < windowTileInspectorElementCount - 1)); + + // Move Down button + SetWidgetDisabledAndInvalidate(WIDX_BUTTON_MOVE_DOWN, !(windowTileInspectorSelectedIndex > 0)); + + // Copy button + SetWidgetDisabledAndInvalidate(WIDX_BUTTON_COPY, !(windowTileInspectorSelectedIndex >= 0)); + + // Paste button + SetWidgetDisabledAndInvalidate(WIDX_BUTTON_PASTE, !(_tileSelected && _elementCopied)); + + widgets[WIDX_BACKGROUND].bottom = height - 1; + + if (tileInspectorPage == TileInspectorPage::Default) + { + widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Empty; + widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Empty; + widgets[WIDX_LIST].bottom = height - PADDING_BOTTOM; + } + else + { + widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Groupbox; + widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Groupbox; + auto pageIndex = EnumValue(tileInspectorPage) - 1; + widgets[WIDX_GROUPBOX_DETAILS].text = PageGroupBoxSettings[pageIndex].string_id; + widgets[WIDX_GROUPBOX_DETAILS].top = height - PageGroupBoxSettings[pageIndex].details_top_offset; + widgets[WIDX_GROUPBOX_DETAILS].bottom = height - PageGroupBoxSettings[pageIndex].details_bottom_offset; + widgets[WIDX_GROUPBOX_PROPERTIES].top = height - PageGroupBoxSettings[pageIndex].properties_top_offset; + widgets[WIDX_GROUPBOX_PROPERTIES].bottom = height - PageGroupBoxSettings[pageIndex].properties_bottom_offset; + widgets[WIDX_LIST].bottom = widgets[WIDX_GROUPBOX_DETAILS].top - GROUPBOX_PADDING; + } + + // The default page doesn't need further invalidation + if (tileInspectorPage == TileInspectorPage::Default) + return; + + // Using a switch, because I don't think giving each page their own callbacks is + // needed here, as only the mouseup and invalidate functions are different. + const int32_t propertiesAnchor = widgets[WIDX_GROUPBOX_PROPERTIES].top; + switch (tileElement->GetType()) { case TileElementType::Surface: - p = TileInspectorPage::Surface; + widgets[WIDX_SURFACE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_SURFACE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_SURFACE_CHECK_CORNER_N].top = GBBT(propertiesAnchor, 2) + 7 * 0; + widgets[WIDX_SURFACE_CHECK_CORNER_N].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_N].top + 13; + widgets[WIDX_SURFACE_CHECK_CORNER_E].top = GBBT(propertiesAnchor, 2) + 7 * 1; + widgets[WIDX_SURFACE_CHECK_CORNER_E].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_E].top + 13; + widgets[WIDX_SURFACE_CHECK_CORNER_S].top = GBBT(propertiesAnchor, 2) + 7 * 2; + widgets[WIDX_SURFACE_CHECK_CORNER_S].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_S].top + 13; + widgets[WIDX_SURFACE_CHECK_CORNER_W].top = GBBT(propertiesAnchor, 2) + 7 * 1; + widgets[WIDX_SURFACE_CHECK_CORNER_W].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_W].top + 13; + widgets[WIDX_SURFACE_CHECK_DIAGONAL].top = GBBT(propertiesAnchor, 3) + 7 * 1; + widgets[WIDX_SURFACE_CHECK_DIAGONAL].bottom = widgets[WIDX_SURFACE_CHECK_DIAGONAL].top + 13; + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_N, + tileElement->AsSurface()->GetSlope() & (1 << ((2 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_E, + tileElement->AsSurface()->GetSlope() & (1 << ((3 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_S, + tileElement->AsSurface()->GetSlope() & (1 << ((0 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_W, + tileElement->AsSurface()->GetSlope() & (1 << ((1 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_DIAGONAL, tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT); break; case TileElementType::Path: - p = TileInspectorPage::Path; + widgets[WIDX_PATH_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_PATH_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_PATH_CHECK_BROKEN].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_PATH_CHECK_BROKEN].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_PATH_CHECK_SLOPED].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_PATH_CHECK_SLOPED].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_PATH_CHECK_JUNCTION_RAILINGS].top = GBBT(propertiesAnchor, 3); + widgets[WIDX_PATH_CHECK_JUNCTION_RAILINGS].bottom = GBBB(propertiesAnchor, 3); + widgets[WIDX_PATH_CHECK_EDGE_N].top = GBBT(propertiesAnchor, 4) + 7 * 0; + widgets[WIDX_PATH_CHECK_EDGE_N].bottom = widgets[WIDX_PATH_CHECK_EDGE_N].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_NE].top = GBBT(propertiesAnchor, 4) + 7 * 1; + widgets[WIDX_PATH_CHECK_EDGE_NE].bottom = widgets[WIDX_PATH_CHECK_EDGE_NE].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_E].top = GBBT(propertiesAnchor, 4) + 7 * 2; + widgets[WIDX_PATH_CHECK_EDGE_E].bottom = widgets[WIDX_PATH_CHECK_EDGE_E].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_SE].top = GBBT(propertiesAnchor, 4) + 7 * 3; + widgets[WIDX_PATH_CHECK_EDGE_SE].bottom = widgets[WIDX_PATH_CHECK_EDGE_SE].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_S].top = GBBT(propertiesAnchor, 4) + 7 * 4; + widgets[WIDX_PATH_CHECK_EDGE_S].bottom = widgets[WIDX_PATH_CHECK_EDGE_S].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_SW].top = GBBT(propertiesAnchor, 4) + 7 * 3; + widgets[WIDX_PATH_CHECK_EDGE_SW].bottom = widgets[WIDX_PATH_CHECK_EDGE_SW].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_W].top = GBBT(propertiesAnchor, 4) + 7 * 2; + widgets[WIDX_PATH_CHECK_EDGE_W].bottom = widgets[WIDX_PATH_CHECK_EDGE_W].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_NW].top = GBBT(propertiesAnchor, 4) + 7 * 1; + widgets[WIDX_PATH_CHECK_EDGE_NW].bottom = widgets[WIDX_PATH_CHECK_EDGE_NW].top + 13; + SetCheckboxValue(WIDX_PATH_CHECK_SLOPED, tileElement->AsPath()->IsSloped()); + SetCheckboxValue(WIDX_PATH_CHECK_JUNCTION_RAILINGS, tileElement->AsPath()->HasJunctionRailings()); + SetCheckboxValue(WIDX_PATH_CHECK_BROKEN, tileElement->AsPath()->IsBroken()); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_NE, tileElement->AsPath()->GetEdges() & (1 << ((0 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_SE, tileElement->AsPath()->GetEdges() & (1 << ((1 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_SW, tileElement->AsPath()->GetEdges() & (1 << ((2 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_NW, tileElement->AsPath()->GetEdges() & (1 << ((3 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_E, tileElement->AsPath()->GetCorners() & (1 << ((0 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_S, tileElement->AsPath()->GetCorners() & (1 << ((1 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_W, tileElement->AsPath()->GetCorners() & (1 << ((2 - GetCurrentRotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_N, tileElement->AsPath()->GetCorners() & (1 << ((3 - GetCurrentRotation()) & 3))); break; case TileElementType::Track: - p = TileInspectorPage::Track; + widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].top = GBBT(propertiesAnchor, 0); + widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].bottom = GBBB(propertiesAnchor, 0); + widgets[WIDX_TRACK_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 1) + 3; + widgets[WIDX_TRACK_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 1) - 3; + widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 1) + 4; + widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 1) - 4; + widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 1) + 4; + widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 1) - 4; + widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_TRACK_CHECK_BRAKE_CLOSED].top = GBBT(propertiesAnchor, 3); + widgets[WIDX_TRACK_CHECK_BRAKE_CLOSED].bottom = GBBB(propertiesAnchor, 3); + widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].top = GBBT(propertiesAnchor, 4); + widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].bottom = GBBB(propertiesAnchor, 4); + SetCheckboxValue(WIDX_TRACK_CHECK_APPLY_TO_ALL, _applyToAll); + SetCheckboxValue(WIDX_TRACK_CHECK_CHAIN_LIFT, tileElement->AsTrack()->HasChain()); + SetCheckboxValue(WIDX_TRACK_CHECK_BRAKE_CLOSED, tileElement->AsTrack()->IsBrakeClosed()); + widgets[WIDX_TRACK_CHECK_BRAKE_CLOSED].content = tileElement->AsTrack()->IsBlockStart() + ? STR_TILE_INSPECTOR_TRACK_BLOCK_BRAKE + : STR_TILE_INSPECTOR_TRACK_BRAKE_CLOSED; + SetCheckboxValue(WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, tileElement->AsTrack()->IsIndestructible()); break; case TileElementType::SmallScenery: - p = TileInspectorPage::Scenery; + { + // Raise / Lower + widgets[WIDX_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + + // Quadrant checkboxes + widgets[WIDX_SCENERY_CHECK_QUARTER_N].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 0; + widgets[WIDX_SCENERY_CHECK_QUARTER_N].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_N].top + 13; + widgets[WIDX_SCENERY_CHECK_QUARTER_E].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_QUARTER_E].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_E].top + 13; + widgets[WIDX_SCENERY_CHECK_QUARTER_S].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 2; + widgets[WIDX_SCENERY_CHECK_QUARTER_S].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_S].top + 13; + widgets[WIDX_SCENERY_CHECK_QUARTER_W].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_QUARTER_W].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_W].top + 13; + // This gets the relative rotation, by subtracting the camera's rotation, and wrapping it between 0-3 + // inclusive + bool N = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((0 - GetCurrentRotation()) & 3); + bool E = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((1 - GetCurrentRotation()) & 3); + bool S = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((2 - GetCurrentRotation()) & 3); + bool W = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((3 - GetCurrentRotation()) & 3); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_N, N); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_E, E); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_S, S); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_W, W); + + // Collision checkboxes + widgets[WIDX_SCENERY_CHECK_COLLISION_N].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 0; + widgets[WIDX_SCENERY_CHECK_COLLISION_N].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_N].top + 13; + widgets[WIDX_SCENERY_CHECK_COLLISION_E].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_COLLISION_E].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_E].top + 13; + widgets[WIDX_SCENERY_CHECK_COLLISION_S].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 2; + widgets[WIDX_SCENERY_CHECK_COLLISION_S].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_S].top + 13; + widgets[WIDX_SCENERY_CHECK_COLLISION_W].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_COLLISION_W].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_W].top + 13; + auto occupiedQuadrants = tileElement->GetOccupiedQuadrants(); + N = (occupiedQuadrants & (1 << ((2 - GetCurrentRotation()) & 3))) != 0; + E = (occupiedQuadrants & (1 << ((3 - GetCurrentRotation()) & 3))) != 0; + S = (occupiedQuadrants & (1 << ((0 - GetCurrentRotation()) & 3))) != 0; + W = (occupiedQuadrants & (1 << ((1 - GetCurrentRotation()) & 3))) != 0; + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_N, N); + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_E, E); + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_S, S); + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_W, W); break; + } case TileElementType::Entrance: - p = TileInspectorPage::Entrance; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].bottom = GBBB(propertiesAnchor, 1); + SetWidgetDisabled( + WIDX_ENTRANCE_BUTTON_MAKE_USABLE, + !(tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)); break; case TileElementType::Wall: - p = TileInspectorPage::Wall; + { + bool canBeSloped = false; + bool hasAnimation = false; + const auto wallEntry = tileElement->AsWall()->GetEntry(); + if (wallEntry != nullptr) + { + canBeSloped = !(wallEntry->flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE); + hasAnimation = wallEntry->flags & WALL_SCENERY_IS_DOOR; + } + + widgets[WIDX_WALL_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_WALL_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_WALL_DROPDOWN_SLOPE].top = GBBT(propertiesAnchor, 1) + 3; + widgets[WIDX_WALL_DROPDOWN_SLOPE].bottom = GBBB(propertiesAnchor, 1) - 3; + widgets[WIDX_WALL_DROPDOWN_SLOPE].text = WallSlopeStringIds[tileElement->AsWall()->GetSlope()]; + widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].top = GBBT(propertiesAnchor, 1) + 4; + widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].bottom = GBBB(propertiesAnchor, 1) - 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top = GBBT(propertiesAnchor, 2) + 3; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].bottom = GBBB(propertiesAnchor, 2) - 3; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].top = GBBT(propertiesAnchor, 2) + 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].bottom = GBBB(propertiesAnchor, 2) - 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].top = GBBT(propertiesAnchor, 2) + 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].bottom = GBBB(propertiesAnchor, 2) - 4; + + // Wall slope dropdown + SetWidgetDisabled(WIDX_WALL_DROPDOWN_SLOPE, !canBeSloped); + InvalidateWidget(WIDX_WALL_DROPDOWN_SLOPE); + SetWidgetDisabled(WIDX_WALL_DROPDOWN_SLOPE_BUTTON, !canBeSloped); + InvalidateWidget(WIDX_WALL_DROPDOWN_SLOPE_BUTTON); + // Wall animation frame spinner + SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME, !hasAnimation); + SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE, !hasAnimation); + SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE, !hasAnimation); break; + } case TileElementType::LargeScenery: - p = TileInspectorPage::LargeScenery; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; break; case TileElementType::Banner: - p = TileInspectorPage::Banner; + widgets[WIDX_BANNER_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_BANNER_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_BANNER_CHECK_BLOCK_NE].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_BANNER_CHECK_BLOCK_NE].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_BANNER_CHECK_BLOCK_SE].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_SE].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_SW].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_SW].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_NW].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_BANNER_CHECK_BLOCK_NW].bottom = GBBB(propertiesAnchor, 1); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_NE, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((0 - GetCurrentRotation()) & 3)))); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_SE, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((1 - GetCurrentRotation()) & 3)))); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_SW, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((2 - GetCurrentRotation()) & 3)))); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_NW, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((3 - GetCurrentRotation()) & 3)))); break; + + default: + break; // Nothing. } } + }; - if (tileInspectorPage != p) - { - SetPage(p); - Invalidate(); - } - // X and Y spinners - SetWidgetDisabledAndInvalidate( - WIDX_SPINNER_X_INCREASE, !(_tileSelected && ((_toolMap.x / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); - SetWidgetDisabledAndInvalidate(WIDX_SPINNER_X_DECREASE, !(_tileSelected && ((_toolMap.x / 32) > 0))); - SetWidgetDisabledAndInvalidate( - WIDX_SPINNER_Y_INCREASE, !(_tileSelected && ((_toolMap.y / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); - SetWidgetDisabledAndInvalidate(WIDX_SPINNER_Y_DECREASE, !(_tileSelected && ((_toolMap.y / 32) > 0))); - - // Sort buttons - SetWidgetDisabledAndInvalidate(WIDX_BUTTON_SORT, !(_tileSelected && windowTileInspectorElementCount > 1)); - - // Move Up button - SetWidgetDisabledAndInvalidate( - WIDX_BUTTON_MOVE_UP, - !(windowTileInspectorSelectedIndex != -1 - && windowTileInspectorSelectedIndex < windowTileInspectorElementCount - 1)); - - // Move Down button - SetWidgetDisabledAndInvalidate(WIDX_BUTTON_MOVE_DOWN, !(windowTileInspectorSelectedIndex > 0)); - - // Copy button - SetWidgetDisabledAndInvalidate(WIDX_BUTTON_COPY, !(windowTileInspectorSelectedIndex >= 0)); - - // Paste button - SetWidgetDisabledAndInvalidate(WIDX_BUTTON_PASTE, !(_tileSelected && _elementCopied)); - - widgets[WIDX_BACKGROUND].bottom = height - 1; - - if (tileInspectorPage == TileInspectorPage::Default) - { - widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Empty; - widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Empty; - widgets[WIDX_LIST].bottom = height - PADDING_BOTTOM; - } - else - { - widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Groupbox; - widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Groupbox; - auto pageIndex = EnumValue(tileInspectorPage) - 1; - widgets[WIDX_GROUPBOX_DETAILS].text = PageGroupBoxSettings[pageIndex].string_id; - widgets[WIDX_GROUPBOX_DETAILS].top = height - PageGroupBoxSettings[pageIndex].details_top_offset; - widgets[WIDX_GROUPBOX_DETAILS].bottom = height - PageGroupBoxSettings[pageIndex].details_bottom_offset; - widgets[WIDX_GROUPBOX_PROPERTIES].top = height - PageGroupBoxSettings[pageIndex].properties_top_offset; - widgets[WIDX_GROUPBOX_PROPERTIES].bottom = height - PageGroupBoxSettings[pageIndex].properties_bottom_offset; - widgets[WIDX_LIST].bottom = widgets[WIDX_GROUPBOX_DETAILS].top - GROUPBOX_PADDING; - } - - // The default page doesn't need further invalidation - if (tileInspectorPage == TileInspectorPage::Default) - return; - - // Using a switch, because I don't think giving each page their own callbacks is - // needed here, as only the mouseup and invalidate functions are different. - const int32_t propertiesAnchor = widgets[WIDX_GROUPBOX_PROPERTIES].top; - - switch (tileElement->GetType()) - { - case TileElementType::Surface: - widgets[WIDX_SURFACE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - widgets[WIDX_SURFACE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].top = GBBT(propertiesAnchor, 1); - widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].bottom = GBBB(propertiesAnchor, 1); - widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].top = GBBT(propertiesAnchor, 1); - widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].bottom = GBBB(propertiesAnchor, 1); - widgets[WIDX_SURFACE_CHECK_CORNER_N].top = GBBT(propertiesAnchor, 2) + 7 * 0; - widgets[WIDX_SURFACE_CHECK_CORNER_N].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_N].top + 13; - widgets[WIDX_SURFACE_CHECK_CORNER_E].top = GBBT(propertiesAnchor, 2) + 7 * 1; - widgets[WIDX_SURFACE_CHECK_CORNER_E].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_E].top + 13; - widgets[WIDX_SURFACE_CHECK_CORNER_S].top = GBBT(propertiesAnchor, 2) + 7 * 2; - widgets[WIDX_SURFACE_CHECK_CORNER_S].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_S].top + 13; - widgets[WIDX_SURFACE_CHECK_CORNER_W].top = GBBT(propertiesAnchor, 2) + 7 * 1; - widgets[WIDX_SURFACE_CHECK_CORNER_W].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_W].top + 13; - widgets[WIDX_SURFACE_CHECK_DIAGONAL].top = GBBT(propertiesAnchor, 3) + 7 * 1; - widgets[WIDX_SURFACE_CHECK_DIAGONAL].bottom = widgets[WIDX_SURFACE_CHECK_DIAGONAL].top + 13; - SetCheckboxValue( - WIDX_SURFACE_CHECK_CORNER_N, - tileElement->AsSurface()->GetSlope() & (1 << ((2 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_SURFACE_CHECK_CORNER_E, - tileElement->AsSurface()->GetSlope() & (1 << ((3 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_SURFACE_CHECK_CORNER_S, - tileElement->AsSurface()->GetSlope() & (1 << ((0 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_SURFACE_CHECK_CORNER_W, - tileElement->AsSurface()->GetSlope() & (1 << ((1 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_SURFACE_CHECK_DIAGONAL, tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT); - break; - - case TileElementType::Path: - widgets[WIDX_PATH_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - widgets[WIDX_PATH_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_PATH_CHECK_BROKEN].top = GBBT(propertiesAnchor, 1); - widgets[WIDX_PATH_CHECK_BROKEN].bottom = GBBB(propertiesAnchor, 1); - widgets[WIDX_PATH_CHECK_SLOPED].top = GBBT(propertiesAnchor, 2); - widgets[WIDX_PATH_CHECK_SLOPED].bottom = GBBB(propertiesAnchor, 2); - widgets[WIDX_PATH_CHECK_JUNCTION_RAILINGS].top = GBBT(propertiesAnchor, 3); - widgets[WIDX_PATH_CHECK_JUNCTION_RAILINGS].bottom = GBBB(propertiesAnchor, 3); - widgets[WIDX_PATH_CHECK_EDGE_N].top = GBBT(propertiesAnchor, 4) + 7 * 0; - widgets[WIDX_PATH_CHECK_EDGE_N].bottom = widgets[WIDX_PATH_CHECK_EDGE_N].top + 13; - widgets[WIDX_PATH_CHECK_EDGE_NE].top = GBBT(propertiesAnchor, 4) + 7 * 1; - widgets[WIDX_PATH_CHECK_EDGE_NE].bottom = widgets[WIDX_PATH_CHECK_EDGE_NE].top + 13; - widgets[WIDX_PATH_CHECK_EDGE_E].top = GBBT(propertiesAnchor, 4) + 7 * 2; - widgets[WIDX_PATH_CHECK_EDGE_E].bottom = widgets[WIDX_PATH_CHECK_EDGE_E].top + 13; - widgets[WIDX_PATH_CHECK_EDGE_SE].top = GBBT(propertiesAnchor, 4) + 7 * 3; - widgets[WIDX_PATH_CHECK_EDGE_SE].bottom = widgets[WIDX_PATH_CHECK_EDGE_SE].top + 13; - widgets[WIDX_PATH_CHECK_EDGE_S].top = GBBT(propertiesAnchor, 4) + 7 * 4; - widgets[WIDX_PATH_CHECK_EDGE_S].bottom = widgets[WIDX_PATH_CHECK_EDGE_S].top + 13; - widgets[WIDX_PATH_CHECK_EDGE_SW].top = GBBT(propertiesAnchor, 4) + 7 * 3; - widgets[WIDX_PATH_CHECK_EDGE_SW].bottom = widgets[WIDX_PATH_CHECK_EDGE_SW].top + 13; - widgets[WIDX_PATH_CHECK_EDGE_W].top = GBBT(propertiesAnchor, 4) + 7 * 2; - widgets[WIDX_PATH_CHECK_EDGE_W].bottom = widgets[WIDX_PATH_CHECK_EDGE_W].top + 13; - widgets[WIDX_PATH_CHECK_EDGE_NW].top = GBBT(propertiesAnchor, 4) + 7 * 1; - widgets[WIDX_PATH_CHECK_EDGE_NW].bottom = widgets[WIDX_PATH_CHECK_EDGE_NW].top + 13; - SetCheckboxValue(WIDX_PATH_CHECK_SLOPED, tileElement->AsPath()->IsSloped()); - SetCheckboxValue(WIDX_PATH_CHECK_JUNCTION_RAILINGS, tileElement->AsPath()->HasJunctionRailings()); - SetCheckboxValue(WIDX_PATH_CHECK_BROKEN, tileElement->AsPath()->IsBroken()); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_NE, tileElement->AsPath()->GetEdges() & (1 << ((0 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_SE, tileElement->AsPath()->GetEdges() & (1 << ((1 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_SW, tileElement->AsPath()->GetEdges() & (1 << ((2 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_NW, tileElement->AsPath()->GetEdges() & (1 << ((3 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_E, tileElement->AsPath()->GetCorners() & (1 << ((0 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_S, tileElement->AsPath()->GetCorners() & (1 << ((1 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_W, tileElement->AsPath()->GetCorners() & (1 << ((2 - GetCurrentRotation()) & 3))); - SetCheckboxValue( - WIDX_PATH_CHECK_EDGE_N, tileElement->AsPath()->GetCorners() & (1 << ((3 - GetCurrentRotation()) & 3))); - break; - - case TileElementType::Track: - widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].top = GBBT(propertiesAnchor, 0); - widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].bottom = GBBB(propertiesAnchor, 0); - widgets[WIDX_TRACK_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 1) + 3; - widgets[WIDX_TRACK_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 1) - 3; - widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 1) + 4; - widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 1) - 4; - widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 1) + 4; - widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 1) - 4; - widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].top = GBBT(propertiesAnchor, 2); - widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].bottom = GBBB(propertiesAnchor, 2); - widgets[WIDX_TRACK_CHECK_BRAKE_CLOSED].top = GBBT(propertiesAnchor, 3); - widgets[WIDX_TRACK_CHECK_BRAKE_CLOSED].bottom = GBBB(propertiesAnchor, 3); - widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].top = GBBT(propertiesAnchor, 4); - widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].bottom = GBBB(propertiesAnchor, 4); - SetCheckboxValue(WIDX_TRACK_CHECK_APPLY_TO_ALL, _applyToAll); - SetCheckboxValue(WIDX_TRACK_CHECK_CHAIN_LIFT, tileElement->AsTrack()->HasChain()); - SetCheckboxValue(WIDX_TRACK_CHECK_BRAKE_CLOSED, tileElement->AsTrack()->IsBrakeClosed()); - widgets[WIDX_TRACK_CHECK_BRAKE_CLOSED].content = tileElement->AsTrack()->IsBlockStart() - ? STR_TILE_INSPECTOR_TRACK_BLOCK_BRAKE - : STR_TILE_INSPECTOR_TRACK_BRAKE_CLOSED; - SetCheckboxValue(WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, tileElement->AsTrack()->IsIndestructible()); - break; - - case TileElementType::SmallScenery: - { - // Raise / Lower - widgets[WIDX_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - widgets[WIDX_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - - // Quadrant checkboxes - widgets[WIDX_SCENERY_CHECK_QUARTER_N].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 0; - widgets[WIDX_SCENERY_CHECK_QUARTER_N].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_N].top + 13; - widgets[WIDX_SCENERY_CHECK_QUARTER_E].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; - widgets[WIDX_SCENERY_CHECK_QUARTER_E].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_E].top + 13; - widgets[WIDX_SCENERY_CHECK_QUARTER_S].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 2; - widgets[WIDX_SCENERY_CHECK_QUARTER_S].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_S].top + 13; - widgets[WIDX_SCENERY_CHECK_QUARTER_W].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; - widgets[WIDX_SCENERY_CHECK_QUARTER_W].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_W].top + 13; - // This gets the relative rotation, by subtracting the camera's rotation, and wrapping it between 0-3 inclusive - bool N = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((0 - GetCurrentRotation()) & 3); - bool E = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((1 - GetCurrentRotation()) & 3); - bool S = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((2 - GetCurrentRotation()) & 3); - bool W = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((3 - GetCurrentRotation()) & 3); - SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_N, N); - SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_E, E); - SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_S, S); - SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_W, W); - - // Collision checkboxes - widgets[WIDX_SCENERY_CHECK_COLLISION_N].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 0; - widgets[WIDX_SCENERY_CHECK_COLLISION_N].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_N].top + 13; - widgets[WIDX_SCENERY_CHECK_COLLISION_E].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; - widgets[WIDX_SCENERY_CHECK_COLLISION_E].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_E].top + 13; - widgets[WIDX_SCENERY_CHECK_COLLISION_S].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 2; - widgets[WIDX_SCENERY_CHECK_COLLISION_S].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_S].top + 13; - widgets[WIDX_SCENERY_CHECK_COLLISION_W].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; - widgets[WIDX_SCENERY_CHECK_COLLISION_W].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_W].top + 13; - auto occupiedQuadrants = tileElement->GetOccupiedQuadrants(); - N = (occupiedQuadrants & (1 << ((2 - GetCurrentRotation()) & 3))) != 0; - E = (occupiedQuadrants & (1 << ((3 - GetCurrentRotation()) & 3))) != 0; - S = (occupiedQuadrants & (1 << ((0 - GetCurrentRotation()) & 3))) != 0; - W = (occupiedQuadrants & (1 << ((1 - GetCurrentRotation()) & 3))) != 0; - SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_N, N); - SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_E, E); - SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_S, S); - SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_W, W); - break; - } - - case TileElementType::Entrance: - widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].top = GBBT(propertiesAnchor, 1); - widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].bottom = GBBB(propertiesAnchor, 1); - SetWidgetDisabled( - WIDX_ENTRANCE_BUTTON_MAKE_USABLE, - !(tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)); - break; - - case TileElementType::Wall: - { - bool canBeSloped = false; - bool hasAnimation = false; - const auto wallEntry = tileElement->AsWall()->GetEntry(); - if (wallEntry != nullptr) - { - canBeSloped = !(wallEntry->flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE); - hasAnimation = wallEntry->flags & WALL_SCENERY_IS_DOOR; - } - - widgets[WIDX_WALL_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - widgets[WIDX_WALL_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_WALL_DROPDOWN_SLOPE].top = GBBT(propertiesAnchor, 1) + 3; - widgets[WIDX_WALL_DROPDOWN_SLOPE].bottom = GBBB(propertiesAnchor, 1) - 3; - widgets[WIDX_WALL_DROPDOWN_SLOPE].text = WallSlopeStringIds[tileElement->AsWall()->GetSlope()]; - widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].top = GBBT(propertiesAnchor, 1) + 4; - widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].bottom = GBBB(propertiesAnchor, 1) - 4; - widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top = GBBT(propertiesAnchor, 2) + 3; - widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].bottom = GBBB(propertiesAnchor, 2) - 3; - widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].top = GBBT(propertiesAnchor, 2) + 4; - widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].bottom = GBBB(propertiesAnchor, 2) - 4; - widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].top = GBBT(propertiesAnchor, 2) + 4; - widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].bottom = GBBB(propertiesAnchor, 2) - 4; - - // Wall slope dropdown - SetWidgetDisabled(WIDX_WALL_DROPDOWN_SLOPE, !canBeSloped); - InvalidateWidget(WIDX_WALL_DROPDOWN_SLOPE); - SetWidgetDisabled(WIDX_WALL_DROPDOWN_SLOPE_BUTTON, !canBeSloped); - InvalidateWidget(WIDX_WALL_DROPDOWN_SLOPE_BUTTON); - // Wall animation frame spinner - SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME, !hasAnimation); - SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE, !hasAnimation); - SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE, !hasAnimation); - break; - } - - case TileElementType::LargeScenery: - widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - break; - - case TileElementType::Banner: - widgets[WIDX_BANNER_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - widgets[WIDX_BANNER_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - widgets[WIDX_BANNER_CHECK_BLOCK_NE].top = GBBT(propertiesAnchor, 1); - widgets[WIDX_BANNER_CHECK_BLOCK_NE].bottom = GBBB(propertiesAnchor, 1); - widgets[WIDX_BANNER_CHECK_BLOCK_SE].top = GBBT(propertiesAnchor, 2); - widgets[WIDX_BANNER_CHECK_BLOCK_SE].bottom = GBBB(propertiesAnchor, 2); - widgets[WIDX_BANNER_CHECK_BLOCK_SW].top = GBBT(propertiesAnchor, 2); - widgets[WIDX_BANNER_CHECK_BLOCK_SW].bottom = GBBB(propertiesAnchor, 2); - widgets[WIDX_BANNER_CHECK_BLOCK_NW].top = GBBT(propertiesAnchor, 1); - widgets[WIDX_BANNER_CHECK_BLOCK_NW].bottom = GBBB(propertiesAnchor, 1); - SetCheckboxValue( - WIDX_BANNER_CHECK_BLOCK_NE, - (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((0 - GetCurrentRotation()) & 3)))); - SetCheckboxValue( - WIDX_BANNER_CHECK_BLOCK_SE, - (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((1 - GetCurrentRotation()) & 3)))); - SetCheckboxValue( - WIDX_BANNER_CHECK_BLOCK_SW, - (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((2 - GetCurrentRotation()) & 3)))); - SetCheckboxValue( - WIDX_BANNER_CHECK_BLOCK_NW, - (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((3 - GetCurrentRotation()) & 3)))); - break; - - default: - break; // Nothing. - } + WindowBase* WindowTileInspectorOpen() + { + WindowBase* window = WindowBringToFrontByClass(WindowClass::TileInspector); + if (window == nullptr) + window = WindowCreate(WindowClass::TileInspector, ScreenCoordsXY(0, 29), WW, WH, WF_RESIZABLE); + return window; } -}; -WindowBase* WindowTileInspectorOpen() -{ - WindowBase* window = WindowBringToFrontByClass(WindowClass::TileInspector); - if (window == nullptr) - window = WindowCreate(WindowClass::TileInspector, ScreenCoordsXY(0, 29), WW, WH, WF_RESIZABLE); - return window; -} + void WindowTileInspectorClearClipboard() + { + auto* window = WindowFindByClass(WindowClass::TileInspector); + if (window != nullptr) + static_cast(window)->ClearClipboard(); + } -void WindowTileInspectorClearClipboard() -{ - auto* window = WindowFindByClass(WindowClass::TileInspector); - if (window != nullptr) - static_cast(window)->ClearClipboard(); -} - -void WindowTileInspectorKeyboardShortcutToggleInvisibility() -{ - auto* window = WindowFindByClass(WindowClass::TileInspector); - if (window != nullptr) - static_cast(window)->ToggleInvisibility(); -} + void WindowTileInspectorKeyboardShortcutToggleInvisibility() + { + auto* window = WindowFindByClass(WindowClass::TileInspector); + if (window != nullptr) + static_cast(window)->ToggleInvisibility(); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TitleExit.cpp b/src/openrct2-ui/windows/TitleExit.cpp index 17870cdf4a..2afad31f02 100644 --- a/src/openrct2-ui/windows/TitleExit.cpp +++ b/src/openrct2-ui/windows/TitleExit.cpp @@ -15,7 +15,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowTitleExitWidgetIdx { WIDX_EXIT, }; @@ -24,43 +26,44 @@ static Widget _titleExitWidgets[] = { MakeWidget({0, 0}, {40, 64}, WindowWidgetType::ImgBtn, WindowColour::Tertiary, ImageId(SPR_MENU_EXIT), STR_EXIT), kWidgetsEnd, }; -// clang-format on + // clang-format on -class TitleExitWindow final : public Window -{ - void OnOpen() override + class TitleExitWindow final : public Window { - widgets = _titleExitWidgets; - InitScrollWidgets(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - if (gIntroState != IntroState::None) - return; - - switch (widgetIndex) + void OnOpen() override { - case WIDX_EXIT: - ContextQuit(); - // game_do_command(0, 1, 0, 0, 5, 3, 0); - break; - }; - } + widgets = _titleExitWidgets; + InitScrollWidgets(); + } - void OnDraw(DrawPixelInfo& dpi) override + void OnMouseUp(WidgetIndex widgetIndex) override + { + if (gIntroState != IntroState::None) + return; + + switch (widgetIndex) + { + case WIDX_EXIT: + ContextQuit(); + // game_do_command(0, 1, 0, 0, 5, 3, 0); + break; + }; + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + } + }; + + /** + * Creates the window containing the exit button on the title screen. + * rct2: 0x0066B624 (part of 0x0066B3E8) + */ + WindowBase* WindowTitleExitOpen() { - DrawWidgets(dpi); + return WindowCreate( + WindowClass::TitleExit, ScreenCoordsXY(ContextGetWidth() - 40, ContextGetHeight() - 64), 40, 64, + WF_STICK_TO_BACK | WF_TRANSPARENT); } -}; - -/** - * Creates the window containing the exit button on the title screen. - * rct2: 0x0066B624 (part of 0x0066B3E8) - */ -WindowBase* WindowTitleExitOpen() -{ - return WindowCreate( - WindowClass::TitleExit, ScreenCoordsXY(ContextGetWidth() - 40, ContextGetHeight() - 64), 40, 64, - WF_STICK_TO_BACK | WF_TRANSPARENT); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TitleLogo.cpp b/src/openrct2-ui/windows/TitleLogo.cpp index d70897312d..8b68d437ca 100644 --- a/src/openrct2-ui/windows/TitleLogo.cpp +++ b/src/openrct2-ui/windows/TitleLogo.cpp @@ -14,64 +14,67 @@ #include #include -static constexpr int32_t WW = 232; -static constexpr int32_t WH = 136; - -enum +namespace OpenRCT2::Ui::Windows { - WIDX_LOGO -}; + static constexpr int32_t WW = 232; + static constexpr int32_t WH = 136; -static Widget _titleLogoWidgets[] = { - MakeWidget({ 0, 0 }, { WW + 1, WH + 1 }, WindowWidgetType::ImgBtn, WindowColour::Primary), - kWidgetsEnd, -}; - -class TitleLogoWindow final : public Window -{ -public: - /** - * Creates the window containing the logo and the expansion packs on the title screen. - * rct2: 0x0066B679 (part of 0x0066B3E8) - */ - void OnOpen() override + enum { - widgets = _titleLogoWidgets; - WindowInitScrollWidgets(*this); - colours[0] = TRANSLUCENT(COLOUR_GREY); - colours[1] = TRANSLUCENT(COLOUR_GREY); - colours[2] = TRANSLUCENT(COLOUR_GREY); - } + WIDX_LOGO + }; - void OnMouseUp(WidgetIndex widgetIndex) override + static Widget _titleLogoWidgets[] = { + MakeWidget({ 0, 0 }, { WW + 1, WH + 1 }, WindowWidgetType::ImgBtn, WindowColour::Primary), + kWidgetsEnd, + }; + + class TitleLogoWindow final : public Window { - switch (widgetIndex) + public: + /** + * Creates the window containing the logo and the expansion packs on the title screen. + * rct2: 0x0066B679 (part of 0x0066B3E8) + */ + void OnOpen() override { - case WIDX_LOGO: - WindowAboutOpen(); - break; + widgets = _titleLogoWidgets; + WindowInitScrollWidgets(*this); + colours[0] = TRANSLUCENT(COLOUR_GREY); + colours[1] = TRANSLUCENT(COLOUR_GREY); + colours[2] = TRANSLUCENT(COLOUR_GREY); } - } - /** - * - * rct2: 0x0066B872 - */ - void OnDraw(DrawPixelInfo& dpi) override - { - auto screenCoords = windowPos + ScreenCoordsXY{ 2, 2 }; - GfxDrawSprite(dpi, ImageId(SPR_G2_LOGO), screenCoords); - GfxDrawSprite(dpi, ImageId(SPR_G2_TITLE), screenCoords + ScreenCoordsXY{ 104, 18 }); - } -}; + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_LOGO: + WindowAboutOpen(); + break; + } + } -WindowBase* WindowTitleLogoOpen() -{ - auto* window = WindowBringToFrontByClass(WindowClass::TitleLogo); - if (window == nullptr) + /** + * + * rct2: 0x0066B872 + */ + void OnDraw(DrawPixelInfo& dpi) override + { + auto screenCoords = windowPos + ScreenCoordsXY{ 2, 2 }; + GfxDrawSprite(dpi, ImageId(SPR_G2_LOGO), screenCoords); + GfxDrawSprite(dpi, ImageId(SPR_G2_TITLE), screenCoords + ScreenCoordsXY{ 104, 18 }); + } + }; + + WindowBase* WindowTitleLogoOpen() { - window = WindowCreate( - WindowClass::TitleLogo, ScreenCoordsXY(0, 0), WW, WH, WF_STICK_TO_BACK | WF_TRANSPARENT); + auto* window = WindowBringToFrontByClass(WindowClass::TitleLogo); + if (window == nullptr) + { + window = WindowCreate( + WindowClass::TitleLogo, ScreenCoordsXY(0, 0), WW, WH, WF_STICK_TO_BACK | WF_TRANSPARENT); + } + return window; } - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TitleMenu.cpp b/src/openrct2-ui/windows/TitleMenu.cpp index eadabe6888..06fec7320f 100644 --- a/src/openrct2-ui/windows/TitleMenu.cpp +++ b/src/openrct2-ui/windows/TitleMenu.cpp @@ -23,7 +23,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WIDX_START_NEW_GAME, WIDX_CONTINUE_SAVED_GAME, @@ -53,238 +55,239 @@ static Widget _titleMenuWidgets[] = { MakeWidget({0, 0}, UpdateButtonDims, WindowWidgetType::Empty, WindowColour::Secondary, STR_UPDATE_AVAILABLE), kWidgetsEnd, }; -// clang-format on + // clang-format on -static void WindowTitleMenuScenarioselectCallback(const utf8* path) -{ - GameNotifyMapChange(); - OpenRCT2::GetContext()->LoadParkFromFile(path, false, true); - GameLoadScripts(); - GameNotifyMapChanged(); -} - -static void InvokeCustomToolboxMenuItem(size_t index) -{ -#ifdef ENABLE_SCRIPTING - const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; - size_t i = 0; - for (const auto& item : customMenuItems) + static void WindowTitleMenuScenarioselectCallback(const utf8* path) { - if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Toolbox) - { - if (i == index) - { - item.Invoke(); - break; - } - i++; - } + GameNotifyMapChange(); + OpenRCT2::GetContext()->LoadParkFromFile(path, false, true); + GameLoadScripts(); + GameNotifyMapChanged(); } -#endif -} -class TitleMenuWindow final : public Window -{ -private: - ScreenRect _filterRect; - -public: - void OnOpen() override + static void InvokeCustomToolboxMenuItem(size_t index) { - widgets = _titleMenuWidgets; +#ifdef ENABLE_SCRIPTING + const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; + size_t i = 0; + for (const auto& item : customMenuItems) + { + if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Toolbox) + { + if (i == index) + { + item.Invoke(); + break; + } + i++; + } + } +#endif + } + + class TitleMenuWindow final : public Window + { + private: + ScreenRect _filterRect; + + public: + void OnOpen() override + { + widgets = _titleMenuWidgets; #ifdef DISABLE_NETWORK - widgets[WIDX_MULTIPLAYER].type = WindowWidgetType::Empty; + widgets[WIDX_MULTIPLAYER].type = WindowWidgetType::Empty; #endif - int32_t x = 0; - for (Widget* widget = widgets; widget != &widgets[WIDX_NEW_VERSION]; widget++) - { - if (widget->type != WindowWidgetType::Empty) + int32_t x = 0; + for (Widget* widget = widgets; widget != &widgets[WIDX_NEW_VERSION]; widget++) { - widget->left = x; - widget->right = x + MenuButtonDims.width - 1; + if (widget->type != WindowWidgetType::Empty) + { + widget->left = x; + widget->right = x + MenuButtonDims.width - 1; - x += MenuButtonDims.width; + x += MenuButtonDims.width; + } + } + width = x; + widgets[WIDX_NEW_VERSION].right = width; + windowPos.x = (ContextGetWidth() - width) / 2; + colours[1] = TRANSLUCENT(COLOUR_LIGHT_ORANGE); + + InitScrollWidgets(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + WindowBase* windowToOpen = nullptr; + + switch (widgetIndex) + { + case WIDX_START_NEW_GAME: + windowToOpen = WindowFindByClass(WindowClass::ScenarioSelect); + if (windowToOpen != nullptr) + { + WindowBringToFront(*windowToOpen); + } + else + { + WindowCloseByClass(WindowClass::Loadsave); + WindowCloseByClass(WindowClass::ServerList); + WindowScenarioselectOpen(WindowTitleMenuScenarioselectCallback); + } + break; + case WIDX_CONTINUE_SAVED_GAME: + windowToOpen = WindowFindByClass(WindowClass::Loadsave); + if (windowToOpen != nullptr) + { + WindowBringToFront(*windowToOpen); + } + else + { + WindowCloseByClass(WindowClass::ScenarioSelect); + WindowCloseByClass(WindowClass::ServerList); + auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::OpenSavePrompt); + GameActions::Execute(&loadOrQuitAction); + } + break; + case WIDX_MULTIPLAYER: + windowToOpen = WindowFindByClass(WindowClass::ServerList); + if (windowToOpen != nullptr) + { + WindowBringToFront(*windowToOpen); + } + else + { + WindowCloseByClass(WindowClass::ScenarioSelect); + WindowCloseByClass(WindowClass::Loadsave); + ContextOpenWindow(WindowClass::ServerList); + } + break; + case WIDX_NEW_VERSION: + ContextOpenWindowView(WV_NEW_VERSION_INFO); + break; } } - width = x; - widgets[WIDX_NEW_VERSION].right = width; - windowPos.x = (ContextGetWidth() - width) / 2; - colours[1] = TRANSLUCENT(COLOUR_LIGHT_ORANGE); - InitScrollWidgets(); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - WindowBase* windowToOpen = nullptr; - - switch (widgetIndex) + void OnMouseDown(WidgetIndex widgetIndex) override { - case WIDX_START_NEW_GAME: - windowToOpen = WindowFindByClass(WindowClass::ScenarioSelect); - if (windowToOpen != nullptr) - { - WindowBringToFront(*windowToOpen); - } - else - { - WindowCloseByClass(WindowClass::Loadsave); - WindowCloseByClass(WindowClass::ServerList); - WindowScenarioselectOpen(WindowTitleMenuScenarioselectCallback); - } - break; - case WIDX_CONTINUE_SAVED_GAME: - windowToOpen = WindowFindByClass(WindowClass::Loadsave); - if (windowToOpen != nullptr) - { - WindowBringToFront(*windowToOpen); - } - else - { - WindowCloseByClass(WindowClass::ScenarioSelect); - WindowCloseByClass(WindowClass::ServerList); - auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::OpenSavePrompt); - GameActions::Execute(&loadOrQuitAction); - } - break; - case WIDX_MULTIPLAYER: - windowToOpen = WindowFindByClass(WindowClass::ServerList); - if (windowToOpen != nullptr) - { - WindowBringToFront(*windowToOpen); - } - else - { - WindowCloseByClass(WindowClass::ScenarioSelect); - WindowCloseByClass(WindowClass::Loadsave); - ContextOpenWindow(WindowClass::ServerList); - } - break; - case WIDX_NEW_VERSION: - ContextOpenWindowView(WV_NEW_VERSION_INFO); - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - if (widgetIndex == WIDX_GAME_TOOLS) - { - int32_t i = 0; - gDropdownItems[i++].Format = STR_SCENARIO_EDITOR; - gDropdownItems[i++].Format = STR_CONVERT_SAVED_GAME_TO_SCENARIO; - gDropdownItems[i++].Format = STR_ROLLER_COASTER_DESIGNER; - gDropdownItems[i++].Format = STR_TRACK_DESIGNS_MANAGER; - gDropdownItems[i++].Format = STR_OPEN_USER_CONTENT_FOLDER; + if (widgetIndex == WIDX_GAME_TOOLS) + { + int32_t i = 0; + gDropdownItems[i++].Format = STR_SCENARIO_EDITOR; + gDropdownItems[i++].Format = STR_CONVERT_SAVED_GAME_TO_SCENARIO; + gDropdownItems[i++].Format = STR_ROLLER_COASTER_DESIGNER; + gDropdownItems[i++].Format = STR_TRACK_DESIGNS_MANAGER; + gDropdownItems[i++].Format = STR_OPEN_USER_CONTENT_FOLDER; #ifdef ENABLE_SCRIPTING - auto hasCustomItems = false; - const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; - if (!customMenuItems.empty()) - { - for (const auto& item : customMenuItems) + auto hasCustomItems = false; + const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; + if (!customMenuItems.empty()) { - if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Toolbox) + for (const auto& item : customMenuItems) { - // Add seperator - if (!hasCustomItems) + if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Toolbox) { - hasCustomItems = true; - gDropdownItems[i++].Format = STR_EMPTY; - } + // Add seperator + if (!hasCustomItems) + { + hasCustomItems = true; + gDropdownItems[i++].Format = STR_EMPTY; + } - gDropdownItems[i].Format = STR_STRING; - auto sz = item.Text.c_str(); - std::memcpy(&gDropdownItems[i].Args, &sz, sizeof(const char*)); - i++; + gDropdownItems[i].Format = STR_STRING; + auto sz = item.Text.c_str(); + std::memcpy(&gDropdownItems[i].Args, &sz, sizeof(const char*)); + i++; + } } } - } #endif - Widget* widget = &widgets[widgetIndex]; - int32_t yOffset = 0; - if (i > 5) - { - yOffset = -(widget->height() + 5 + (i * 12)); - } - - WindowDropdownShowText( - windowPos + ScreenCoordsXY{ widget->left, widget->top + yOffset }, widget->height() + 1, - TRANSLUCENT(colours[0]), Dropdown::Flag::StayOpen, i); - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (selectedIndex == -1) - { - return; - } - if (widgetIndex == WIDX_GAME_TOOLS) - { - switch (selectedIndex) - { - case DDIDX_SCENARIO_EDITOR: - Editor::Load(); - break; - case DDIDX_CONVERT_SAVED_GAME: - Editor::ConvertSaveToScenario(); - break; - case DDIDX_TRACK_DESIGNER: - Editor::LoadTrackDesigner(); - break; - case DDIDX_TRACK_MANAGER: - Editor::LoadTrackManager(); - break; - case DDIDX_OPEN_CONTENT_FOLDER: + Widget* widget = &widgets[widgetIndex]; + int32_t yOffset = 0; + if (i > 5) { - auto context = OpenRCT2::GetContext(); - auto env = context->GetPlatformEnvironment(); - auto uiContext = context->GetUiContext(); - uiContext->OpenFolder(env->GetDirectoryPath(OpenRCT2::DIRBASE::USER)); - break; + yOffset = -(widget->height() + 5 + (i * 12)); } - default: - InvokeCustomToolboxMenuItem(selectedIndex - DDIDX_CUSTOM_BEGIN); - break; + + WindowDropdownShowText( + windowPos + ScreenCoordsXY{ widget->left, widget->top + yOffset }, widget->height() + 1, + TRANSLUCENT(colours[0]), Dropdown::Flag::StayOpen, i); } } - } - CursorID OnCursor(WidgetIndex, const ScreenCoordsXY&, CursorID cursorId) override - { - gTooltipCloseTimeout = gCurrentRealTimeTicks + 2000; - return cursorId; - } - - void OnPrepareDraw() override - { - _filterRect = { windowPos + ScreenCoordsXY{ 0, UpdateButtonDims.height }, - windowPos + ScreenCoordsXY{ width - 1, MenuButtonDims.height + UpdateButtonDims.height - 1 } }; - if (OpenRCT2::GetContext()->HasNewVersionInfo()) + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override { - widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button; - _filterRect.Point1.y = windowPos.y; + if (selectedIndex == -1) + { + return; + } + if (widgetIndex == WIDX_GAME_TOOLS) + { + switch (selectedIndex) + { + case DDIDX_SCENARIO_EDITOR: + Editor::Load(); + break; + case DDIDX_CONVERT_SAVED_GAME: + Editor::ConvertSaveToScenario(); + break; + case DDIDX_TRACK_DESIGNER: + Editor::LoadTrackDesigner(); + break; + case DDIDX_TRACK_MANAGER: + Editor::LoadTrackManager(); + break; + case DDIDX_OPEN_CONTENT_FOLDER: + { + auto context = OpenRCT2::GetContext(); + auto env = context->GetPlatformEnvironment(); + auto uiContext = context->GetUiContext(); + uiContext->OpenFolder(env->GetDirectoryPath(OpenRCT2::DIRBASE::USER)); + break; + } + default: + InvokeCustomToolboxMenuItem(selectedIndex - DDIDX_CUSTOM_BEGIN); + break; + } + } } - } - void OnDraw(DrawPixelInfo& dpi) override + CursorID OnCursor(WidgetIndex, const ScreenCoordsXY&, CursorID cursorId) override + { + gTooltipCloseTimeout = gCurrentRealTimeTicks + 2000; + return cursorId; + } + + void OnPrepareDraw() override + { + _filterRect = { windowPos + ScreenCoordsXY{ 0, UpdateButtonDims.height }, + windowPos + ScreenCoordsXY{ width - 1, MenuButtonDims.height + UpdateButtonDims.height - 1 } }; + if (OpenRCT2::GetContext()->HasNewVersionInfo()) + { + widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button; + _filterRect.Point1.y = windowPos.y; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + GfxFilterRect(dpi, _filterRect, FilterPaletteID::Palette51); + DrawWidgets(dpi); + } + }; + + /** + * Creates the window containing the menu buttons on the title screen. + */ + WindowBase* WindowTitleMenuOpen() { - GfxFilterRect(dpi, _filterRect, FilterPaletteID::Palette51); - DrawWidgets(dpi); + const uint16_t windowHeight = MenuButtonDims.height + UpdateButtonDims.height; + return WindowCreate( + WindowClass::TitleMenu, ScreenCoordsXY(0, ContextGetHeight() - 182), 0, windowHeight, + WF_STICK_TO_BACK | WF_TRANSPARENT | WF_NO_BACKGROUND); } -}; - -/** - * Creates the window containing the menu buttons on the title screen. - */ -WindowBase* WindowTitleMenuOpen() -{ - const uint16_t windowHeight = MenuButtonDims.height + UpdateButtonDims.height; - return WindowCreate( - WindowClass::TitleMenu, ScreenCoordsXY(0, ContextGetHeight() - 182), 0, windowHeight, - WF_STICK_TO_BACK | WF_TRANSPARENT | WF_NO_BACKGROUND); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TitleOptions.cpp b/src/openrct2-ui/windows/TitleOptions.cpp index fee3b088db..639b960a17 100644 --- a/src/openrct2-ui/windows/TitleOptions.cpp +++ b/src/openrct2-ui/windows/TitleOptions.cpp @@ -14,7 +14,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowTitleOptionsWidgetIdx { WIDX_OPTIONS, }; @@ -23,44 +25,46 @@ static Widget _windowTitleOptionsWidgets[] = { MakeWidget({0, 0}, {80, 15}, WindowWidgetType::Button, WindowColour::Tertiary, STR_OPTIONS, STR_OPTIONS_TIP), kWidgetsEnd, }; -// clang-format on + // clang-format on -class TitleOptionsWindow final : public Window -{ -public: - void OnOpen() override + class TitleOptionsWindow final : public Window { - widgets = _windowTitleOptionsWidgets; - WindowInitScrollWidgets(*this); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_OPTIONS: - ContextOpenWindow(WindowClass::Options); - break; + widgets = _windowTitleOptionsWidgets; + WindowInitScrollWidgets(*this); } - } - void OnDraw(DrawPixelInfo& dpi) override + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_OPTIONS: + ContextOpenWindow(WindowClass::Options); + break; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + } + }; + + /** + * Creates the window containing the options button on the title screen. + */ + WindowBase* WindowTitleOptionsOpen() { - DrawWidgets(dpi); - } -}; + auto* window = WindowBringToFrontByClass(WindowClass::TitleOptions); + if (window == nullptr) + { + window = WindowCreate( + WindowClass::TitleOptions, ScreenCoordsXY(ContextGetWidth() - 80, 0), 80, 15, + WF_STICK_TO_BACK | WF_TRANSPARENT); + } -/** - * Creates the window containing the options button on the title screen. - */ -WindowBase* WindowTitleOptionsOpen() -{ - auto* window = WindowBringToFrontByClass(WindowClass::TitleOptions); - if (window == nullptr) - { - window = WindowCreate( - WindowClass::TitleOptions, ScreenCoordsXY(ContextGetWidth() - 80, 0), 80, 15, WF_STICK_TO_BACK | WF_TRANSPARENT); + return window; } - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Tooltip.cpp b/src/openrct2-ui/windows/Tooltip.cpp index 2517e1046a..049d0d0c13 100644 --- a/src/openrct2-ui/windows/Tooltip.cpp +++ b/src/openrct2-ui/windows/Tooltip.cpp @@ -18,7 +18,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum { WIDX_BACKGROUND }; @@ -28,181 +30,183 @@ static Widget _tooltipWidgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class TooltipWindow final : public Window -{ -private: - u8string _tooltipText; - int16_t _tooltipNumLines = 1; - -public: - TooltipWindow(const OpenRCT2String& message, ScreenCoordsXY screenCoords) + class TooltipWindow final : public Window { - int32_t textWidth = FormatTextForTooltip(message); - int32_t textHeight = ((_tooltipNumLines + 1) * FontGetLineHeight(FontStyle::Small)); + private: + u8string _tooltipText; + int16_t _tooltipNumLines = 1; - width = textWidth + 5; - height = textHeight + 4; + public: + TooltipWindow(const OpenRCT2String& message, ScreenCoordsXY screenCoords) + { + int32_t textWidth = FormatTextForTooltip(message); + int32_t textHeight = ((_tooltipNumLines + 1) * FontGetLineHeight(FontStyle::Small)); - widgets = _tooltipWidgets; - widgets[WIDX_BACKGROUND].right = width; - widgets[WIDX_BACKGROUND].bottom = height; + width = textWidth + 5; + height = textHeight + 4; - UpdatePosition(screenCoords); + widgets = _tooltipWidgets; + widgets[WIDX_BACKGROUND].right = width; + widgets[WIDX_BACKGROUND].bottom = height; + + UpdatePosition(screenCoords); + } + + void UpdatePosition(ScreenCoordsXY screenCoords) + { + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + screenCoords.x = std::clamp(screenCoords.x - (width / 2), 0, screenWidth - width); + + // TODO The cursor size will be relative to the window DPI. + // The amount to offset the y should be adjusted. + + const int32_t cursorHeight = 20; + const int32_t cursorMargin = 4; + const int32_t cursorOffset = cursorHeight + cursorMargin; + + const int32_t maxY = screenHeight - height; + + if (screenCoords.y + cursorOffset > maxY) + { + // Display the tooltip above the cursor if there is not enough space below. + screenCoords.y -= (height + cursorMargin); + } + else + { + // Display the tooltip under the cursor if there is enough space below. + screenCoords.y += cursorOffset; + } + + screenCoords.y = std::clamp(screenCoords.y, cursorOffset, maxY); + + if (windowPos != screenCoords) + { + WindowSetPosition(*this, screenCoords); + } + } + + void OnOpen() override + { + ResetTooltipNotShown(); + } + + void OnUpdate() override + { + UpdatePosition(gTooltipCursor); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + int32_t left = windowPos.x; + int32_t top = windowPos.y; + int32_t right = windowPos.x + width - 1; + int32_t bottom = windowPos.y + height - 1; + + // Background + GfxFilterRect(dpi, { { left + 1, top + 1 }, { right - 1, bottom - 1 } }, FilterPaletteID::Palette45); + GfxFilterRect(dpi, { { left + 1, top + 1 }, { right - 1, bottom - 1 } }, FilterPaletteID::PaletteGlassLightOrange); + + // Sides + GfxFilterRect(dpi, { { left + 0, top + 2 }, { left + 0, bottom - 2 } }, FilterPaletteID::PaletteDarken3); + GfxFilterRect(dpi, { { right + 0, top + 2 }, { right + 0, bottom - 2 } }, FilterPaletteID::PaletteDarken3); + GfxFilterRect(dpi, { { left + 2, bottom + 0 }, { right - 2, bottom + 0 } }, FilterPaletteID::PaletteDarken3); + GfxFilterRect(dpi, { { left + 2, top + 0 }, { right - 2, top + 0 } }, FilterPaletteID::PaletteDarken3); + + // Corners + GfxFilterPixel(dpi, { left + 1, top + 1 }, FilterPaletteID::PaletteDarken3); + GfxFilterPixel(dpi, { right - 1, top + 1 }, FilterPaletteID::PaletteDarken3); + GfxFilterPixel(dpi, { left + 1, bottom - 1 }, FilterPaletteID::PaletteDarken3); + GfxFilterPixel(dpi, { right - 1, bottom - 1 }, FilterPaletteID::PaletteDarken3); + + // Text + left = windowPos.x + ((width + 1) / 2) - 1; + top = windowPos.y + 1; + DrawStringCentredRaw(dpi, { left, top }, _tooltipNumLines, _tooltipText.data(), FontStyle::Small); + } + + private: + // Returns the width of the new tooltip text + int32_t FormatTextForTooltip(const OpenRCT2String& message) + { + const u8string tempString = ::FormatStringID(message.str, message.args.Data()); + + OpenRCT2String formattedMessage{ STR_STRING_TOOLTIP, Formatter() }; + formattedMessage.args.Add(tempString.c_str()); + const u8string tooltipTextUnwrapped = ::FormatStringID(formattedMessage.str, formattedMessage.args.Data()); + + auto textWidth = GfxGetStringWidthNewLined(tooltipTextUnwrapped, FontStyle::Small); + textWidth = std::min(textWidth, 196); + + int32_t numLines; + textWidth = GfxWrapString(tooltipTextUnwrapped, textWidth + 1, FontStyle::Small, &_tooltipText, &numLines); + _tooltipNumLines = numLines; + return textWidth; + } + }; + + void WindowTooltipReset(const ScreenCoordsXY& screenCoords) + { + gTooltipCursor = screenCoords; + gTooltipCloseTimeout = 0; + gTooltipWidget.window_classification = WindowClass::Null; + InputSetState(InputState::Normal); + InputSetFlag(INPUT_FLAG_4, false); } - void UpdatePosition(ScreenCoordsXY screenCoords) + void WindowTooltipShow(const OpenRCT2String& message, ScreenCoordsXY screenCoords) { - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - screenCoords.x = std::clamp(screenCoords.x - (width / 2), 0, screenWidth - width); + auto tooltipWindow = std::make_unique(message, screenCoords); + auto windowPos = tooltipWindow->windowPos; + auto width = tooltipWindow->width; + auto height = tooltipWindow->height; + WindowCreate( + std::move(tooltipWindow), WindowClass::Tooltip, windowPos, width, height, WF_TRANSPARENT | WF_STICK_TO_FRONT); + } - // TODO The cursor size will be relative to the window DPI. - // The amount to offset the y should be adjusted. + void WindowTooltipOpen(WindowBase* widgetWindow, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) + { + if (widgetWindow == nullptr || widgetIndex == -1) + return; - const int32_t cursorHeight = 20; - const int32_t cursorMargin = 4; - const int32_t cursorOffset = cursorHeight + cursorMargin; + auto widget = &widgetWindow->widgets[widgetIndex]; + widgetWindow->OnPrepareDraw(); - const int32_t maxY = screenHeight - height; - - if (screenCoords.y + cursorOffset > maxY) + OpenRCT2String result; + if (widget->flags & WIDGET_FLAGS::TOOLTIP_IS_STRING) { - // Display the tooltip above the cursor if there is not enough space below. - screenCoords.y -= (height + cursorMargin); + auto tooltipString = widget->sztooltip; + if (*tooltipString == 0) + return; + + result.str = STR_STRING_TOOLTIP; + result.args = Formatter(); + result.args.Add(tooltipString); + + gTooltipWidget.window_classification = widgetWindow->classification; + gTooltipWidget.window_number = widgetWindow->number; + gTooltipWidget.widget_index = widgetIndex; } else { - // Display the tooltip under the cursor if there is enough space below. - screenCoords.y += cursorOffset; + auto stringId = widget->tooltip; + gTooltipWidget.window_classification = widgetWindow->classification; + gTooltipWidget.window_number = widgetWindow->number; + gTooltipWidget.widget_index = widgetIndex; + result = widgetWindow->OnTooltip(widgetIndex, stringId); + if (result.str == STR_NONE) + return; } - screenCoords.y = std::clamp(screenCoords.y, cursorOffset, maxY); - - if (windowPos != screenCoords) - { - WindowSetPosition(*this, screenCoords); - } + WindowTooltipShow(result, screenCoords); } - void OnOpen() override + void WindowTooltipClose() { - ResetTooltipNotShown(); + WindowCloseByClass(WindowClass::Tooltip); + gTooltipCloseTimeout = 0; + gTooltipWidget.window_classification = WindowClass::Null; } - - void OnUpdate() override - { - UpdatePosition(gTooltipCursor); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - int32_t left = windowPos.x; - int32_t top = windowPos.y; - int32_t right = windowPos.x + width - 1; - int32_t bottom = windowPos.y + height - 1; - - // Background - GfxFilterRect(dpi, { { left + 1, top + 1 }, { right - 1, bottom - 1 } }, FilterPaletteID::Palette45); - GfxFilterRect(dpi, { { left + 1, top + 1 }, { right - 1, bottom - 1 } }, FilterPaletteID::PaletteGlassLightOrange); - - // Sides - GfxFilterRect(dpi, { { left + 0, top + 2 }, { left + 0, bottom - 2 } }, FilterPaletteID::PaletteDarken3); - GfxFilterRect(dpi, { { right + 0, top + 2 }, { right + 0, bottom - 2 } }, FilterPaletteID::PaletteDarken3); - GfxFilterRect(dpi, { { left + 2, bottom + 0 }, { right - 2, bottom + 0 } }, FilterPaletteID::PaletteDarken3); - GfxFilterRect(dpi, { { left + 2, top + 0 }, { right - 2, top + 0 } }, FilterPaletteID::PaletteDarken3); - - // Corners - GfxFilterPixel(dpi, { left + 1, top + 1 }, FilterPaletteID::PaletteDarken3); - GfxFilterPixel(dpi, { right - 1, top + 1 }, FilterPaletteID::PaletteDarken3); - GfxFilterPixel(dpi, { left + 1, bottom - 1 }, FilterPaletteID::PaletteDarken3); - GfxFilterPixel(dpi, { right - 1, bottom - 1 }, FilterPaletteID::PaletteDarken3); - - // Text - left = windowPos.x + ((width + 1) / 2) - 1; - top = windowPos.y + 1; - DrawStringCentredRaw(dpi, { left, top }, _tooltipNumLines, _tooltipText.data(), FontStyle::Small); - } - -private: - // Returns the width of the new tooltip text - int32_t FormatTextForTooltip(const OpenRCT2String& message) - { - const u8string tempString = FormatStringID(message.str, message.args.Data()); - - OpenRCT2String formattedMessage{ STR_STRING_TOOLTIP, Formatter() }; - formattedMessage.args.Add(tempString.c_str()); - const u8string tooltipTextUnwrapped = FormatStringID(formattedMessage.str, formattedMessage.args.Data()); - - auto textWidth = GfxGetStringWidthNewLined(tooltipTextUnwrapped, FontStyle::Small); - textWidth = std::min(textWidth, 196); - - int32_t numLines; - textWidth = GfxWrapString(tooltipTextUnwrapped, textWidth + 1, FontStyle::Small, &_tooltipText, &numLines); - _tooltipNumLines = numLines; - return textWidth; - } -}; - -void WindowTooltipReset(const ScreenCoordsXY& screenCoords) -{ - gTooltipCursor = screenCoords; - gTooltipCloseTimeout = 0; - gTooltipWidget.window_classification = WindowClass::Null; - InputSetState(InputState::Normal); - InputSetFlag(INPUT_FLAG_4, false); -} - -void WindowTooltipShow(const OpenRCT2String& message, ScreenCoordsXY screenCoords) -{ - auto tooltipWindow = std::make_unique(message, screenCoords); - auto windowPos = tooltipWindow->windowPos; - auto width = tooltipWindow->width; - auto height = tooltipWindow->height; - WindowCreate(std::move(tooltipWindow), WindowClass::Tooltip, windowPos, width, height, WF_TRANSPARENT | WF_STICK_TO_FRONT); -} - -void WindowTooltipOpen(WindowBase* widgetWindow, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - if (widgetWindow == nullptr || widgetIndex == -1) - return; - - auto widget = &widgetWindow->widgets[widgetIndex]; - widgetWindow->OnPrepareDraw(); - - OpenRCT2String result; - if (widget->flags & WIDGET_FLAGS::TOOLTIP_IS_STRING) - { - auto tooltipString = widget->sztooltip; - if (*tooltipString == 0) - return; - - result.str = STR_STRING_TOOLTIP; - result.args = Formatter(); - result.args.Add(tooltipString); - - gTooltipWidget.window_classification = widgetWindow->classification; - gTooltipWidget.window_number = widgetWindow->number; - gTooltipWidget.widget_index = widgetIndex; - } - else - { - auto stringId = widget->tooltip; - gTooltipWidget.window_classification = widgetWindow->classification; - gTooltipWidget.window_number = widgetWindow->number; - gTooltipWidget.widget_index = widgetIndex; - result = widgetWindow->OnTooltip(widgetIndex, stringId); - if (result.str == STR_NONE) - return; - } - - WindowTooltipShow(result, screenCoords); -} - -void WindowTooltipClose() -{ - WindowCloseByClass(WindowClass::Tooltip); - gTooltipCloseTimeout = 0; - gTooltipWidget.window_classification = WindowClass::Null; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index ec93fe72da..b349881355 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -73,143 +73,142 @@ #include #include -using namespace OpenRCT2; -using namespace OpenRCT2::Ui; - -enum +namespace OpenRCT2::Ui::Windows { - WIDX_PAUSE, - WIDX_FILE_MENU, - WIDX_MUTE, - WIDX_ZOOM_OUT, - WIDX_ZOOM_IN, - WIDX_ROTATE, - WIDX_VIEW_MENU, - WIDX_MAP, + enum + { + WIDX_PAUSE, + WIDX_FILE_MENU, + WIDX_MUTE, + WIDX_ZOOM_OUT, + WIDX_ZOOM_IN, + WIDX_ROTATE, + WIDX_VIEW_MENU, + WIDX_MAP, - WIDX_LAND, - WIDX_WATER, - WIDX_SCENERY, - WIDX_PATH, - WIDX_CONSTRUCT_RIDE, - WIDX_RIDES, - WIDX_PARK, - WIDX_STAFF, - WIDX_GUESTS, - WIDX_CLEAR_SCENERY, + WIDX_LAND, + WIDX_WATER, + WIDX_SCENERY, + WIDX_PATH, + WIDX_CONSTRUCT_RIDE, + WIDX_RIDES, + WIDX_PARK, + WIDX_STAFF, + WIDX_GUESTS, + WIDX_CLEAR_SCENERY, - WIDX_FASTFORWARD, - WIDX_CHEATS, - WIDX_DEBUG, - WIDX_FINANCES, - WIDX_RESEARCH, - WIDX_NEWS, - WIDX_NETWORK, - WIDX_CHAT, + WIDX_FASTFORWARD, + WIDX_CHEATS, + WIDX_DEBUG, + WIDX_FINANCES, + WIDX_RESEARCH, + WIDX_NEWS, + WIDX_NETWORK, + WIDX_CHAT, - WIDX_SEPARATOR, -}; + WIDX_SEPARATOR, + }; -validate_global_widx(WC_TOP_TOOLBAR, WIDX_PAUSE); -validate_global_widx(WC_TOP_TOOLBAR, WIDX_LAND); -validate_global_widx(WC_TOP_TOOLBAR, WIDX_WATER); -validate_global_widx(WC_TOP_TOOLBAR, WIDX_SCENERY); -validate_global_widx(WC_TOP_TOOLBAR, WIDX_PATH); + validate_global_widx(WC_TOP_TOOLBAR, WIDX_PAUSE); + validate_global_widx(WC_TOP_TOOLBAR, WIDX_LAND); + validate_global_widx(WC_TOP_TOOLBAR, WIDX_WATER); + validate_global_widx(WC_TOP_TOOLBAR, WIDX_SCENERY); + validate_global_widx(WC_TOP_TOOLBAR, WIDX_PATH); -enum FileMenuDdidx -{ - DDIDX_NEW_GAME = 0, - DDIDX_LOAD_GAME = 1, - DDIDX_SAVE_GAME = 2, - DDIDX_SAVE_GAME_AS = 3, - // separator - DDIDX_ABOUT = 5, - DDIDX_OPTIONS = 6, - DDIDX_SCREENSHOT = 7, - DDIDX_GIANT_SCREENSHOT = 8, - // separator - DDIDX_FILE_BUG_ON_GITHUB = 10, - DDIDX_UPDATE_AVAILABLE = 11, - // separator - DDIDX_QUIT_TO_MENU = 13, - DDIDX_EXIT_OPENRCT2 = 14, -}; + enum FileMenuDdidx + { + DDIDX_NEW_GAME = 0, + DDIDX_LOAD_GAME = 1, + DDIDX_SAVE_GAME = 2, + DDIDX_SAVE_GAME_AS = 3, + // separator + DDIDX_ABOUT = 5, + DDIDX_OPTIONS = 6, + DDIDX_SCREENSHOT = 7, + DDIDX_GIANT_SCREENSHOT = 8, + // separator + DDIDX_FILE_BUG_ON_GITHUB = 10, + DDIDX_UPDATE_AVAILABLE = 11, + // separator + DDIDX_QUIT_TO_MENU = 13, + DDIDX_EXIT_OPENRCT2 = 14, + }; -enum TopToolbarViewMenuDdidx -{ - DDIDX_UNDERGROUND_INSIDE = 0, - DDIDX_TRANSPARENT_WATER = 1, - DDIDX_HIDE_BASE = 2, - DDIDX_HIDE_VERTICAL = 3, - // separator - DDIDX_HIDE_RIDES = 5, - DDIDX_HIDE_VEHICLES = 6, - DDIDX_HIDE_VEGETATION = 7, - DDIDX_HIDE_SCENERY = 8, - DDIDX_HIDE_PATHS = 9, - DDIDX_HIDE_SUPPORTS = 10, - DDIDX_HIDE_GUESTS = 11, - DDIDX_HIDE_STAFF = 12, - // separator - DDIDX_LAND_HEIGHTS = 14, - DDIDX_TRACK_HEIGHTS = 15, - DDIDX_PATH_HEIGHTS = 16, - // separator - DDIDX_VIEW_CLIPPING = 18, - DDIDX_HIGHLIGHT_PATH_ISSUES = 19, - // separator - DDIDX_TRANSPARENCY = 21, + enum TopToolbarViewMenuDdidx + { + DDIDX_UNDERGROUND_INSIDE = 0, + DDIDX_TRANSPARENT_WATER = 1, + DDIDX_HIDE_BASE = 2, + DDIDX_HIDE_VERTICAL = 3, + // separator + DDIDX_HIDE_RIDES = 5, + DDIDX_HIDE_VEHICLES = 6, + DDIDX_HIDE_VEGETATION = 7, + DDIDX_HIDE_SCENERY = 8, + DDIDX_HIDE_PATHS = 9, + DDIDX_HIDE_SUPPORTS = 10, + DDIDX_HIDE_GUESTS = 11, + DDIDX_HIDE_STAFF = 12, + // separator + DDIDX_LAND_HEIGHTS = 14, + DDIDX_TRACK_HEIGHTS = 15, + DDIDX_PATH_HEIGHTS = 16, + // separator + DDIDX_VIEW_CLIPPING = 18, + DDIDX_HIGHLIGHT_PATH_ISSUES = 19, + // separator + DDIDX_TRANSPARENCY = 21, - TOP_TOOLBAR_VIEW_MENU_COUNT, -}; + TOP_TOOLBAR_VIEW_MENU_COUNT, + }; -enum TopToolbarDebugDdidx -{ - DDIDX_CONSOLE = 0, - DDIDX_DEBUG_PAINT = 1, + enum TopToolbarDebugDdidx + { + DDIDX_CONSOLE = 0, + DDIDX_DEBUG_PAINT = 1, - TOP_TOOLBAR_DEBUG_COUNT, -}; + TOP_TOOLBAR_DEBUG_COUNT, + }; -enum TopToolbarNetworkDdidx -{ - DDIDX_MULTIPLAYER = 0, - DDIDX_MULTIPLAYER_RECONNECT = 1, + enum TopToolbarNetworkDdidx + { + DDIDX_MULTIPLAYER = 0, + DDIDX_MULTIPLAYER_RECONNECT = 1, - TOP_TOOLBAR_NETWORK_COUNT, -}; + TOP_TOOLBAR_NETWORK_COUNT, + }; -enum -{ - DDIDX_CHEATS, - DDIDX_TILE_INSPECTOR = 1, - DDIDX_OBJECT_SELECTION = 2, - DDIDX_INVENTIONS_LIST = 3, - DDIDX_SCENARIO_OPTIONS = 4, - DDIDX_OBJECTIVE_OPTIONS = 5, - // 6 is a separator - DDIDX_ENABLE_SANDBOX_MODE = 7, - DDIDX_DISABLE_CLEARANCE_CHECKS = 8, - DDIDX_DISABLE_SUPPORT_LIMITS = 9, + enum + { + DDIDX_CHEATS, + DDIDX_TILE_INSPECTOR = 1, + DDIDX_OBJECT_SELECTION = 2, + DDIDX_INVENTIONS_LIST = 3, + DDIDX_SCENARIO_OPTIONS = 4, + DDIDX_OBJECTIVE_OPTIONS = 5, + // 6 is a separator + DDIDX_ENABLE_SANDBOX_MODE = 7, + DDIDX_DISABLE_CLEARANCE_CHECKS = 8, + DDIDX_DISABLE_SUPPORT_LIMITS = 9, - TOP_TOOLBAR_CHEATS_COUNT, -}; + TOP_TOOLBAR_CHEATS_COUNT, + }; -enum -{ - DDIDX_SHOW_MAP, - DDIDX_OPEN_VIEWPORT, -}; + enum + { + DDIDX_SHOW_MAP, + DDIDX_OPEN_VIEWPORT, + }; -enum -{ - DDIDX_ROTATE_CLOCKWISE, - DDIDX_ROTATE_ANTI_CLOCKWISE, -}; + enum + { + DDIDX_ROTATE_CLOCKWISE, + DDIDX_ROTATE_ANTI_CLOCKWISE, + }; #pragma region Toolbar_widget_ordering -// clang-format off + // clang-format off // from left to right static constexpr int32_t left_aligned_widgets_order[] = { WIDX_PAUSE, @@ -282,250 +281,327 @@ static Widget _topToolbarWidgets[] = { MakeWidget ({ 0, 0}, {10, 1}, WindowWidgetType::Empty, WindowColour::Primary ), // Artificial widget separator kWidgetsEnd, }; -// clang-format on + // clang-format on -static void ScenarioSelectCallback(const utf8* path); + static void ScenarioSelectCallback(const utf8* path); -class TopToolbar final : public Window -{ -private: - bool _landToolBlocked{ false }; - uint8_t _unkF64F0E{ 0 }; - int16_t _unkF64F0A{ 0 }; - - void InitViewMenu(Widget& widget); - - void ViewMenuDropdown(int16_t dropdownIndex); - - void InitMapMenu(Widget& widget); - - void MapMenuDropdown(int16_t dropdownIndex); - - void InitFastforwardMenu(Widget& widget); - - void FastforwardMenuDropdown(int16_t dropdownIndex); - - void InitRotateMenu(Widget& widget); - - void RotateMenuDropdown(int16_t dropdownIndex); - - void InitCheatsMenu(Widget& widget); - - void CheatsMenuDropdown(int16_t dropdownIndex); - - void InitDebugMenu(Widget& widget); - - void DebugMenuDropdown(int16_t dropdownIndex); - - void InitNetworkMenu(Widget& widget); - - void NetworkMenuDropdown(int16_t dropdownIndex); - - /** - * - * rct2: 0x0066CCE7 - */ - void ToggleFootpathWindow() + class TopToolbar final : public Window { - if (WindowFindByClass(WindowClass::Footpath) == nullptr) - { - ContextOpenWindow(WindowClass::Footpath); - } - else - { - ToolCancel(); - WindowCloseByClass(WindowClass::Footpath); - } - } + private: + bool _landToolBlocked{ false }; + uint8_t _unkF64F0E{ 0 }; + int16_t _unkF64F0A{ 0 }; - /** - * - * rct2: 0x0066CD54 - */ - void ToggleLandWindow(WidgetIndex widgetIndex) - { - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar - && gCurrentToolWidget.widget_index == WIDX_LAND) - { - ToolCancel(); - } - else - { - _landToolBlocked = false; - ShowGridlines(); - ToolSet(*this, widgetIndex, Tool::DigDown); - InputSetFlag(INPUT_FLAG_6, true); - ContextOpenWindow(WindowClass::Land); - } - } + void InitViewMenu(Widget& widget); - /** - * - * rct2: 0x0066CD0C - */ - void ToggleClearSceneryWindow(WidgetIndex widgetIndex) - { - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar - && gCurrentToolWidget.widget_index == WIDX_CLEAR_SCENERY)) + void ViewMenuDropdown(int16_t dropdownIndex); + + void InitMapMenu(Widget& widget); + + void MapMenuDropdown(int16_t dropdownIndex); + + void InitFastforwardMenu(Widget& widget); + + void FastforwardMenuDropdown(int16_t dropdownIndex); + + void InitRotateMenu(Widget& widget); + + void RotateMenuDropdown(int16_t dropdownIndex); + + void InitCheatsMenu(Widget& widget); + + void CheatsMenuDropdown(int16_t dropdownIndex); + + void InitDebugMenu(Widget& widget); + + void DebugMenuDropdown(int16_t dropdownIndex); + + void InitNetworkMenu(Widget& widget); + + void NetworkMenuDropdown(int16_t dropdownIndex); + + /** + * + * rct2: 0x0066CCE7 + */ + void ToggleFootpathWindow() { - ToolCancel(); - } - else - { - ShowGridlines(); - ToolSet(*this, widgetIndex, Tool::Crosshair); - InputSetFlag(INPUT_FLAG_6, true); - ContextOpenWindow(WindowClass::ClearScenery); - } - } - - /** - * - * rct2: 0x0066CD9C - */ - void ToggleWaterWindow(WidgetIndex widgetIndex) - { - if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar - && gCurrentToolWidget.widget_index == WIDX_WATER) - { - ToolCancel(); - } - else - { - _landToolBlocked = false; - ShowGridlines(); - ToolSet(*this, widgetIndex, Tool::WaterDown); - InputSetFlag(INPUT_FLAG_6, true); - ContextOpenWindow(WindowClass::Water); - } - } - - /** - * - * rct2: 0x0068E213 - */ - void ToolUpdateSceneryClear(const ScreenCoordsXY& screenPos) - { - if (!ToolUpdateLandPaint(screenPos)) - return; - - auto action = GetClearAction(); - auto result = GameActions::Query(&action); - auto cost = (result.Error == GameActions::Status::Ok ? result.Cost : kMoney64Undefined); - if (gClearSceneryCost != cost) - { - gClearSceneryCost = cost; - WindowInvalidateByClass(WindowClass::ClearScenery); - } - } - - int8_t ToolUpdateLandPaint(const ScreenCoordsXY& screenPos) - { - uint8_t state_changed = 0; - - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - auto mapTile = ScreenGetMapXY(screenPos, nullptr); - - if (!mapTile.has_value()) - { - if (gClearSceneryCost != kMoney64Undefined) + if (WindowFindByClass(WindowClass::Footpath) == nullptr) { - gClearSceneryCost = kMoney64Undefined; + ContextOpenWindow(WindowClass::Footpath); + } + else + { + ToolCancel(); + WindowCloseByClass(WindowClass::Footpath); + } + } + + /** + * + * rct2: 0x0066CD54 + */ + void ToggleLandWindow(WidgetIndex widgetIndex) + { + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar + && gCurrentToolWidget.widget_index == WIDX_LAND) + { + ToolCancel(); + } + else + { + _landToolBlocked = false; + ShowGridlines(); + ToolSet(*this, widgetIndex, Tool::DigDown); + InputSetFlag(INPUT_FLAG_6, true); + ContextOpenWindow(WindowClass::Land); + } + } + + /** + * + * rct2: 0x0066CD0C + */ + void ToggleClearSceneryWindow(WidgetIndex widgetIndex) + { + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar + && gCurrentToolWidget.widget_index == WIDX_CLEAR_SCENERY)) + { + ToolCancel(); + } + else + { + ShowGridlines(); + ToolSet(*this, widgetIndex, Tool::Crosshair); + InputSetFlag(INPUT_FLAG_6, true); + ContextOpenWindow(WindowClass::ClearScenery); + } + } + + /** + * + * rct2: 0x0066CD9C + */ + void ToggleWaterWindow(WidgetIndex widgetIndex) + { + if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar + && gCurrentToolWidget.widget_index == WIDX_WATER) + { + ToolCancel(); + } + else + { + _landToolBlocked = false; + ShowGridlines(); + ToolSet(*this, widgetIndex, Tool::WaterDown); + InputSetFlag(INPUT_FLAG_6, true); + ContextOpenWindow(WindowClass::Water); + } + } + + /** + * + * rct2: 0x0068E213 + */ + void ToolUpdateSceneryClear(const ScreenCoordsXY& screenPos) + { + if (!ToolUpdateLandPaint(screenPos)) + return; + + auto action = GetClearAction(); + auto result = GameActions::Query(&action); + auto cost = (result.Error == GameActions::Status::Ok ? result.Cost : kMoney64Undefined); + if (gClearSceneryCost != cost) + { + gClearSceneryCost = cost; WindowInvalidateByClass(WindowClass::ClearScenery); } + } + + int8_t ToolUpdateLandPaint(const ScreenCoordsXY& screenPos) + { + uint8_t state_changed = 0; + + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + + auto mapTile = ScreenGetMapXY(screenPos, nullptr); + + if (!mapTile.has_value()) + { + if (gClearSceneryCost != kMoney64Undefined) + { + gClearSceneryCost = kMoney64Undefined; + WindowInvalidateByClass(WindowClass::ClearScenery); + } + return state_changed; + } + + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + state_changed++; + } + + if (gMapSelectType != MAP_SELECT_TYPE_FULL) + { + gMapSelectType = MAP_SELECT_TYPE_FULL; + state_changed++; + } + + int16_t tool_size = std::max(1, gLandToolSize); + int16_t tool_length = (tool_size - 1) * 32; + + // Move to tool bottom left + mapTile->x -= (tool_size - 1) * 16; + mapTile->y -= (tool_size - 1) * 16; + mapTile = mapTile->ToTileStart(); + + if (gMapSelectPositionA.x != mapTile->x) + { + gMapSelectPositionA.x = mapTile->x; + state_changed++; + } + + if (gMapSelectPositionA.y != mapTile->y) + { + gMapSelectPositionA.y = mapTile->y; + state_changed++; + } + + mapTile->x += tool_length; + mapTile->y += tool_length; + + if (gMapSelectPositionB.x != mapTile->x) + { + gMapSelectPositionB.x = mapTile->x; + state_changed++; + } + + if (gMapSelectPositionB.y != mapTile->y) + { + gMapSelectPositionB.y = mapTile->y; + state_changed++; + } + + MapInvalidateSelectionRect(); return state_changed; } - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + /** + * + * rct2: 0x00664280 + */ + void ToolUpdateLand(const ScreenCoordsXY& screenPos) { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - state_changed++; - } + const bool mapCtrlPressed = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z); - if (gMapSelectType != MAP_SELECT_TYPE_FULL) - { - gMapSelectType = MAP_SELECT_TYPE_FULL; - state_changed++; - } + MapInvalidateSelectionRect(); - int16_t tool_size = std::max(1, gLandToolSize); - int16_t tool_length = (tool_size - 1) * 32; - - // Move to tool bottom left - mapTile->x -= (tool_size - 1) * 16; - mapTile->y -= (tool_size - 1) * 16; - mapTile = mapTile->ToTileStart(); - - if (gMapSelectPositionA.x != mapTile->x) - { - gMapSelectPositionA.x = mapTile->x; - state_changed++; - } - - if (gMapSelectPositionA.y != mapTile->y) - { - gMapSelectPositionA.y = mapTile->y; - state_changed++; - } - - mapTile->x += tool_length; - mapTile->y += tool_length; - - if (gMapSelectPositionB.x != mapTile->x) - { - gMapSelectPositionB.x = mapTile->x; - state_changed++; - } - - if (gMapSelectPositionB.y != mapTile->y) - { - gMapSelectPositionB.y = mapTile->y; - state_changed++; - } - - MapInvalidateSelectionRect(); - return state_changed; - } - - /** - * - * rct2: 0x00664280 - */ - void ToolUpdateLand(const ScreenCoordsXY& screenPos) - { - const bool mapCtrlPressed = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z); - - MapInvalidateSelectionRect(); - - if (gCurrentToolId == Tool::UpDownArrow) - { - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - return; - - money64 lower_cost = SelectionLowerLand(0); - money64 raise_cost = SelectionRaiseLand(0); - - if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) + if (gCurrentToolId == Tool::UpDownArrow) { - gLandToolRaiseCost = raise_cost; - gLandToolLowerCost = lower_cost; - WindowInvalidateByClass(WindowClass::Land); + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + return; + + money64 lower_cost = SelectionLowerLand(0); + money64 raise_cost = SelectionRaiseLand(0); + + if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) + { + gLandToolRaiseCost = raise_cost; + gLandToolLowerCost = lower_cost; + WindowInvalidateByClass(WindowClass::Land); + } + return; } - return; - } - int16_t tool_size = gLandToolSize; - std::optional mapTile; - uint8_t side{}; + int16_t tool_size = gLandToolSize; + std::optional mapTile; + uint8_t side{}; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - if (tool_size == 1) - { - int32_t selectionType; - // Get selection type and map coordinates from mouse x,y position - ScreenPosToMapPos(screenPos, &selectionType); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + if (tool_size == 1) + { + int32_t selectionType; + // Get selection type and map coordinates from mouse x,y position + ScreenPosToMapPos(screenPos, &selectionType); + mapTile = ScreenGetMapXYSide(screenPos, &side); + + if (!mapTile.has_value()) + { + money64 lower_cost = kMoney64Undefined; + money64 raise_cost = kMoney64Undefined; + + if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) + { + gLandToolRaiseCost = raise_cost; + gLandToolLowerCost = lower_cost; + WindowInvalidateByClass(WindowClass::Land); + } + return; + } + + uint8_t state_changed = 0; + + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + state_changed++; + } + + if (gMapSelectType != selectionType) + { + gMapSelectType = selectionType; + state_changed++; + } + + if ((gMapSelectType != MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF)) && mapCtrlPressed) + { + gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF); + state_changed++; + } + + if (gMapSelectPositionA.x != mapTile->x) + { + gMapSelectPositionA.x = mapTile->x; + state_changed++; + } + + if (gMapSelectPositionA.y != mapTile->y) + { + gMapSelectPositionA.y = mapTile->y; + state_changed++; + } + + if (gMapSelectPositionB.x != mapTile->x) + { + gMapSelectPositionB.x = mapTile->x; + state_changed++; + } + + if (gMapSelectPositionB.y != mapTile->y) + { + gMapSelectPositionB.y = mapTile->y; + state_changed++; + } + + MapInvalidateSelectionRect(); + if (!state_changed) + return; + + money64 lower_cost = SelectionLowerLand(0); + money64 raise_cost = SelectionRaiseLand(0); + + if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) + { + gLandToolRaiseCost = raise_cost; + gLandToolLowerCost = lower_cost; + WindowInvalidateByClass(WindowClass::Land); + } + return; + } + + // Get map coordinates and the side of the tile that is being hovered over mapTile = ScreenGetMapXYSide(screenPos, &side); if (!mapTile.has_value()) @@ -550,9 +626,9 @@ private: state_changed++; } - if (gMapSelectType != selectionType) + if (gMapSelectType != MAP_SELECT_TYPE_FULL) { - gMapSelectType = selectionType; + gMapSelectType = MAP_SELECT_TYPE_FULL; state_changed++; } @@ -562,6 +638,34 @@ private: state_changed++; } + if (tool_size == 0) + tool_size = 1; + + int16_t tool_length = (tool_size - 1) * 32; + + // Decide on shape of the brush for bigger selection size + switch (gMapSelectType) + { + case MAP_SELECT_TYPE_EDGE_0: + case MAP_SELECT_TYPE_EDGE_2: + // Line + mapTile->y -= (tool_size - 1) * 16; + mapTile->y = mapTile->ToTileStart().y; + break; + case MAP_SELECT_TYPE_EDGE_1: + case MAP_SELECT_TYPE_EDGE_3: + // Line + mapTile->x -= (tool_size - 1) * 16; + mapTile->x = mapTile->ToTileStart().x; + break; + default: + // Move to tool bottom left + mapTile->x -= (tool_size - 1) * 16; + mapTile->y -= (tool_size - 1) * 16; + mapTile = mapTile->ToTileStart(); + break; + } + if (gMapSelectPositionA.x != mapTile->x) { gMapSelectPositionA.x = mapTile->x; @@ -574,6 +678,27 @@ private: state_changed++; } + // Go to other side + switch (gMapSelectType) + { + case MAP_SELECT_TYPE_EDGE_0: + case MAP_SELECT_TYPE_EDGE_2: + // Line + mapTile->y += tool_length; + gMapSelectType = MAP_SELECT_TYPE_FULL; + break; + case MAP_SELECT_TYPE_EDGE_1: + case MAP_SELECT_TYPE_EDGE_3: + // Line + mapTile->x += tool_length; + gMapSelectType = MAP_SELECT_TYPE_FULL; + break; + default: + mapTile->x += tool_length; + mapTile->y += tool_length; + break; + } + if (gMapSelectPositionB.x != mapTile->x) { gMapSelectPositionB.x = mapTile->x; @@ -599,145 +724,111 @@ private: gLandToolLowerCost = lower_cost; WindowInvalidateByClass(WindowClass::Land); } - return; } - // Get map coordinates and the side of the tile that is being hovered over - mapTile = ScreenGetMapXYSide(screenPos, &side); - - if (!mapTile.has_value()) + /** + * + * rct2: 0x006E6BDC + */ + void ToolUpdateWater(const ScreenCoordsXY& screenPos) { - money64 lower_cost = kMoney64Undefined; - money64 raise_cost = kMoney64Undefined; + MapInvalidateSelectionRect(); - if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) + if (gCurrentToolId == Tool::UpDownArrow) { - gLandToolRaiseCost = raise_cost; - gLandToolLowerCost = lower_cost; - WindowInvalidateByClass(WindowClass::Land); + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + return; + + auto waterLowerAction = WaterLowerAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + auto waterRaiseAction = WaterRaiseAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + + auto res = GameActions::Query(&waterLowerAction); + money64 lowerCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; + + res = GameActions::Query(&waterRaiseAction); + money64 raiseCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; + + if (gWaterToolRaiseCost != raiseCost || gWaterToolLowerCost != lowerCost) + { + gWaterToolRaiseCost = raiseCost; + gWaterToolLowerCost = lowerCost; + WindowInvalidateByClass(WindowClass::Water); + } + return; } - return; - } - uint8_t state_changed = 0; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - state_changed++; - } + auto info = GetMapCoordinatesFromPos( + screenPos, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water)); - if (gMapSelectType != MAP_SELECT_TYPE_FULL) - { - gMapSelectType = MAP_SELECT_TYPE_FULL; - state_changed++; - } + if (info.SpriteType == ViewportInteractionItem::None) + { + if (gWaterToolRaiseCost != kMoney64Undefined || gWaterToolLowerCost != kMoney64Undefined) + { + gWaterToolRaiseCost = kMoney64Undefined; + gWaterToolLowerCost = kMoney64Undefined; + WindowInvalidateByClass(WindowClass::Water); + } + return; + } - if ((gMapSelectType != MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF)) && mapCtrlPressed) - { - gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF); - state_changed++; - } + auto mapTile = info.Loc.ToTileCentre(); - if (tool_size == 0) - tool_size = 1; + uint8_t state_changed = 0; - int16_t tool_length = (tool_size - 1) * 32; - - // Decide on shape of the brush for bigger selection size - switch (gMapSelectType) - { - case MAP_SELECT_TYPE_EDGE_0: - case MAP_SELECT_TYPE_EDGE_2: - // Line - mapTile->y -= (tool_size - 1) * 16; - mapTile->y = mapTile->ToTileStart().y; - break; - case MAP_SELECT_TYPE_EDGE_1: - case MAP_SELECT_TYPE_EDGE_3: - // Line - mapTile->x -= (tool_size - 1) * 16; - mapTile->x = mapTile->ToTileStart().x; - break; - default: - // Move to tool bottom left - mapTile->x -= (tool_size - 1) * 16; - mapTile->y -= (tool_size - 1) * 16; - mapTile = mapTile->ToTileStart(); - break; - } - - if (gMapSelectPositionA.x != mapTile->x) - { - gMapSelectPositionA.x = mapTile->x; - state_changed++; - } - - if (gMapSelectPositionA.y != mapTile->y) - { - gMapSelectPositionA.y = mapTile->y; - state_changed++; - } - - // Go to other side - switch (gMapSelectType) - { - case MAP_SELECT_TYPE_EDGE_0: - case MAP_SELECT_TYPE_EDGE_2: - // Line - mapTile->y += tool_length; - gMapSelectType = MAP_SELECT_TYPE_FULL; - break; - case MAP_SELECT_TYPE_EDGE_1: - case MAP_SELECT_TYPE_EDGE_3: - // Line - mapTile->x += tool_length; - gMapSelectType = MAP_SELECT_TYPE_FULL; - break; - default: - mapTile->x += tool_length; - mapTile->y += tool_length; - break; - } - - if (gMapSelectPositionB.x != mapTile->x) - { - gMapSelectPositionB.x = mapTile->x; - state_changed++; - } - - if (gMapSelectPositionB.y != mapTile->y) - { - gMapSelectPositionB.y = mapTile->y; - state_changed++; - } - - MapInvalidateSelectionRect(); - if (!state_changed) - return; - - money64 lower_cost = SelectionLowerLand(0); - money64 raise_cost = SelectionRaiseLand(0); - - if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) - { - gLandToolRaiseCost = raise_cost; - gLandToolLowerCost = lower_cost; - WindowInvalidateByClass(WindowClass::Land); - } - } - - /** - * - * rct2: 0x006E6BDC - */ - void ToolUpdateWater(const ScreenCoordsXY& screenPos) - { - MapInvalidateSelectionRect(); - - if (gCurrentToolId == Tool::UpDownArrow) - { if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + state_changed++; + } + + if (gMapSelectType != MAP_SELECT_TYPE_FULL_WATER) + { + gMapSelectType = MAP_SELECT_TYPE_FULL_WATER; + state_changed++; + } + + int16_t tool_size = std::max(1, gLandToolSize); + int16_t tool_length = (tool_size - 1) * 32; + + // Move to tool bottom left + mapTile.x -= (tool_size - 1) * 16; + mapTile.y -= (tool_size - 1) * 16; + mapTile.x &= 0xFFE0; + mapTile.y &= 0xFFE0; + + if (gMapSelectPositionA.x != mapTile.x) + { + gMapSelectPositionA.x = mapTile.x; + state_changed++; + } + + if (gMapSelectPositionA.y != mapTile.y) + { + gMapSelectPositionA.y = mapTile.y; + state_changed++; + } + + mapTile.x += tool_length; + mapTile.y += tool_length; + + if (gMapSelectPositionB.x != mapTile.x) + { + gMapSelectPositionB.x = mapTile.x; + state_changed++; + } + + if (gMapSelectPositionB.y != mapTile.y) + { + gMapSelectPositionB.y = mapTile.y; + state_changed++; + } + + MapInvalidateSelectionRect(); + if (!state_changed) return; auto waterLowerAction = WaterLowerAction( @@ -757,976 +848,1105 @@ private: gWaterToolLowerCost = lowerCost; WindowInvalidateByClass(WindowClass::Water); } - return; } - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - auto info = GetMapCoordinatesFromPos( - screenPos, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water)); - - if (info.SpriteType == ViewportInteractionItem::None) + /** + * + * rct2: 0x006E287B + */ + void ToolUpdateScenery(const ScreenCoordsXY& screenPos) { - if (gWaterToolRaiseCost != kMoney64Undefined || gWaterToolLowerCost != kMoney64Undefined) + MapInvalidateSelectionRect(); + MapInvalidateMapSelectionTiles(); + + if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) { - gWaterToolRaiseCost = kMoney64Undefined; - gWaterToolLowerCost = kMoney64Undefined; - WindowInvalidateByClass(WindowClass::Water); + VirtualFloorInvalidate(); } - return; - } - auto mapTile = info.Loc.ToTileCentre(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - uint8_t state_changed = 0; + if (gWindowSceneryPaintEnabled) + return; + if (gWindowSceneryEyedropperEnabled) + return; - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - state_changed++; - } - - if (gMapSelectType != MAP_SELECT_TYPE_FULL_WATER) - { - gMapSelectType = MAP_SELECT_TYPE_FULL_WATER; - state_changed++; - } - - int16_t tool_size = std::max(1, gLandToolSize); - int16_t tool_length = (tool_size - 1) * 32; - - // Move to tool bottom left - mapTile.x -= (tool_size - 1) * 16; - mapTile.y -= (tool_size - 1) * 16; - mapTile.x &= 0xFFE0; - mapTile.y &= 0xFFE0; - - if (gMapSelectPositionA.x != mapTile.x) - { - gMapSelectPositionA.x = mapTile.x; - state_changed++; - } - - if (gMapSelectPositionA.y != mapTile.y) - { - gMapSelectPositionA.y = mapTile.y; - state_changed++; - } - - mapTile.x += tool_length; - mapTile.y += tool_length; - - if (gMapSelectPositionB.x != mapTile.x) - { - gMapSelectPositionB.x = mapTile.x; - state_changed++; - } - - if (gMapSelectPositionB.y != mapTile.y) - { - gMapSelectPositionB.y = mapTile.y; - state_changed++; - } - - MapInvalidateSelectionRect(); - if (!state_changed) - return; - - auto waterLowerAction = WaterLowerAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); - auto waterRaiseAction = WaterRaiseAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); - - auto res = GameActions::Query(&waterLowerAction); - money64 lowerCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; - - res = GameActions::Query(&waterRaiseAction); - money64 raiseCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; - - if (gWaterToolRaiseCost != raiseCost || gWaterToolLowerCost != lowerCost) - { - gWaterToolRaiseCost = raiseCost; - gWaterToolLowerCost = lowerCost; - WindowInvalidateByClass(WindowClass::Water); - } - } - - /** - * - * rct2: 0x006E287B - */ - void ToolUpdateScenery(const ScreenCoordsXY& screenPos) - { - MapInvalidateSelectionRect(); - MapInvalidateMapSelectionTiles(); - - if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) - { - VirtualFloorInvalidate(); - } - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - - if (gWindowSceneryPaintEnabled) - return; - if (gWindowSceneryEyedropperEnabled) - return; - - const auto selection = WindowSceneryGetTabSelection(); - if (selection.IsUndefined()) - { - SceneryRemoveGhostToolPlacement(); - return; - } - - money64 cost = 0; - - switch (selection.SceneryType) - { - case SCENERY_TYPE_SMALL: + const auto selection = WindowSceneryGetTabSelection(); + if (selection.IsUndefined()) { - CoordsXY mapTile = {}; - uint8_t quadrant; - Direction rotation; + SceneryRemoveGhostToolPlacement(); + return; + } - Sub6E1F34SmallScenery(screenPos, selection.EntryIndex, mapTile, &quadrant, &rotation); + money64 cost = 0; - if (mapTile.IsNull()) + switch (selection.SceneryType) + { + case SCENERY_TYPE_SMALL: { - SceneryRemoveGhostToolPlacement(); - return; - } + CoordsXY mapTile = {}; + uint8_t quadrant; + Direction rotation; - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - if (gWindowSceneryScatterEnabled) - { - uint16_t cluster_size = (gWindowSceneryScatterSize - 1) * COORDS_XY_STEP; - gMapSelectPositionA.x = mapTile.x - cluster_size / 2; - gMapSelectPositionA.y = mapTile.y - cluster_size / 2; - gMapSelectPositionB.x = mapTile.x + cluster_size / 2; - gMapSelectPositionB.y = mapTile.y + cluster_size / 2; - if (gWindowSceneryScatterSize % 2 == 0) + Sub6E1F34SmallScenery(screenPos, selection.EntryIndex, mapTile, &quadrant, &rotation); + + if (mapTile.IsNull()) { - gMapSelectPositionB.x += COORDS_XY_STEP; - gMapSelectPositionB.y += COORDS_XY_STEP; + SceneryRemoveGhostToolPlacement(); + return; } + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + if (gWindowSceneryScatterEnabled) + { + uint16_t cluster_size = (gWindowSceneryScatterSize - 1) * COORDS_XY_STEP; + gMapSelectPositionA.x = mapTile.x - cluster_size / 2; + gMapSelectPositionA.y = mapTile.y - cluster_size / 2; + gMapSelectPositionB.x = mapTile.x + cluster_size / 2; + gMapSelectPositionB.y = mapTile.y + cluster_size / 2; + if (gWindowSceneryScatterSize % 2 == 0) + { + gMapSelectPositionB.x += COORDS_XY_STEP; + gMapSelectPositionB.y += COORDS_XY_STEP; + } + } + else + { + gMapSelectPositionA.x = mapTile.x; + gMapSelectPositionA.y = mapTile.y; + gMapSelectPositionB.x = mapTile.x; + gMapSelectPositionB.y = mapTile.y; + } + + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selection.EntryIndex); + + gMapSelectType = MAP_SELECT_TYPE_FULL; + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && !gWindowSceneryScatterEnabled) + { + gMapSelectType = MAP_SELECT_TYPE_QUARTER_0 + (quadrant ^ 2); + } + + MapInvalidateSelectionRect(); + + // If no change in ghost placement + if ((gSceneryGhostType & SCENERY_GHOST_FLAG_0) && mapTile == gSceneryGhostPosition && quadrant == _unkF64F0E + && gSceneryPlaceZ == _unkF64F0A && gSceneryPlaceObject.SceneryType == SCENERY_TYPE_SMALL + && gSceneryPlaceObject.EntryIndex == selection.EntryIndex) + { + return; + } + + SceneryRemoveGhostToolPlacement(); + + _unkF64F0E = quadrant; + _unkF64F0A = gSceneryPlaceZ; + + uint8_t attemptsLeft = 1; + if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) + { + attemptsLeft = 20; + } + + for (; attemptsLeft != 0; attemptsLeft--) + { + cost = TryPlaceGhostSmallScenery( + { mapTile, gSceneryPlaceZ, rotation }, quadrant, selection.EntryIndex, gWindowSceneryPrimaryColour, + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + if (cost != kMoney64Undefined) + break; + gSceneryPlaceZ += 8; + } + + gSceneryPlaceCost = cost; + break; } - else + case SCENERY_TYPE_PATH_ITEM: { + CoordsXY mapTile = {}; + int32_t z; + + Sub6E1F34PathItem(screenPos, selection.EntryIndex, mapTile, &z); + + if (mapTile.IsNull()) + { + SceneryRemoveGhostToolPlacement(); + return; + } + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; gMapSelectPositionA.x = mapTile.x; gMapSelectPositionA.y = mapTile.y; gMapSelectPositionB.x = mapTile.x; gMapSelectPositionB.y = mapTile.y; - } + gMapSelectType = MAP_SELECT_TYPE_FULL; - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selection.EntryIndex); + MapInvalidateSelectionRect(); - gMapSelectType = MAP_SELECT_TYPE_FULL; - if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && !gWindowSceneryScatterEnabled) - { - gMapSelectType = MAP_SELECT_TYPE_QUARTER_0 + (quadrant ^ 2); - } - - MapInvalidateSelectionRect(); - - // If no change in ghost placement - if ((gSceneryGhostType & SCENERY_GHOST_FLAG_0) && mapTile == gSceneryGhostPosition && quadrant == _unkF64F0E - && gSceneryPlaceZ == _unkF64F0A && gSceneryPlaceObject.SceneryType == SCENERY_TYPE_SMALL - && gSceneryPlaceObject.EntryIndex == selection.EntryIndex) - { - return; - } - - SceneryRemoveGhostToolPlacement(); - - _unkF64F0E = quadrant; - _unkF64F0A = gSceneryPlaceZ; - - uint8_t attemptsLeft = 1; - if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) - { - attemptsLeft = 20; - } - - for (; attemptsLeft != 0; attemptsLeft--) - { - cost = TryPlaceGhostSmallScenery( - { mapTile, gSceneryPlaceZ, rotation }, quadrant, selection.EntryIndex, gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - if (cost != kMoney64Undefined) - break; - gSceneryPlaceZ += 8; - } - - gSceneryPlaceCost = cost; - break; - } - case SCENERY_TYPE_PATH_ITEM: - { - CoordsXY mapTile = {}; - int32_t z; - - Sub6E1F34PathItem(screenPos, selection.EntryIndex, mapTile, &z); - - if (mapTile.IsNull()) - { - SceneryRemoveGhostToolPlacement(); - return; - } - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectPositionA.x = mapTile.x; - gMapSelectPositionA.y = mapTile.y; - gMapSelectPositionB.x = mapTile.x; - gMapSelectPositionB.y = mapTile.y; - gMapSelectType = MAP_SELECT_TYPE_FULL; - - MapInvalidateSelectionRect(); - - // If no change in ghost placement - if ((gSceneryGhostType & SCENERY_GHOST_FLAG_1) && mapTile == gSceneryGhostPosition - && z == gSceneryGhostPosition.z) - { - return; - } - - SceneryRemoveGhostToolPlacement(); - - cost = TryPlaceGhostPathAddition({ mapTile, z }, selection.EntryIndex); - - gSceneryPlaceCost = cost; - break; - } - case SCENERY_TYPE_WALL: - { - CoordsXY mapTile = {}; - uint8_t edge; - - Sub6E1F34Wall(screenPos, selection.EntryIndex, mapTile, &edge); - - if (mapTile.IsNull()) - { - SceneryRemoveGhostToolPlacement(); - return; - } - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectPositionA.x = mapTile.x; - gMapSelectPositionA.y = mapTile.y; - gMapSelectPositionB.x = mapTile.x; - gMapSelectPositionB.y = mapTile.y; - gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + edge; - - MapInvalidateSelectionRect(); - - // If no change in ghost placement - if ((gSceneryGhostType & SCENERY_GHOST_FLAG_2) && mapTile == gSceneryGhostPosition - && edge == gSceneryGhostWallRotation && gSceneryPlaceZ == _unkF64F0A) - { - return; - } - - SceneryRemoveGhostToolPlacement(); - - gSceneryGhostWallRotation = edge; - _unkF64F0A = gSceneryPlaceZ; - - uint8_t attemptsLeft = 1; - if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) - { - attemptsLeft = 20; - } - - cost = 0; - for (; attemptsLeft != 0; attemptsLeft--) - { - cost = TryPlaceGhostWall( - { mapTile, gSceneryPlaceZ }, edge, selection.EntryIndex, gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - if (cost != kMoney64Undefined) - break; - gSceneryPlaceZ += 8; - } - - gSceneryPlaceCost = cost; - break; - } - case SCENERY_TYPE_LARGE: - { - CoordsXY mapTile = {}; - Direction direction; - - Sub6E1F34LargeScenery(screenPos, selection.EntryIndex, mapTile, &direction); - - if (mapTile.IsNull()) - { - SceneryRemoveGhostToolPlacement(); - return; - } - - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selection.EntryIndex); - gMapSelectionTiles.clear(); - - for (auto* tile = sceneryEntry->tiles; tile->x_offset != static_cast(static_cast(0xFFFF)); - tile++) - { - CoordsXY tileLocation = { tile->x_offset, tile->y_offset }; - auto rotatedTileCoords = tileLocation.Rotate(direction); - - rotatedTileCoords.x += mapTile.x; - rotatedTileCoords.y += mapTile.y; - - gMapSelectionTiles.push_back(rotatedTileCoords); - } - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - MapInvalidateMapSelectionTiles(); - - // If no change in ghost placement - if ((gSceneryGhostType & SCENERY_GHOST_FLAG_3) && mapTile == gSceneryGhostPosition - && gSceneryPlaceZ == _unkF64F0A && gSceneryPlaceObject.SceneryType == SCENERY_TYPE_LARGE - && gSceneryPlaceObject.EntryIndex == selection.EntryIndex) - { - return; - } - - SceneryRemoveGhostToolPlacement(); - - gSceneryPlaceObject.SceneryType = SCENERY_TYPE_LARGE; - gSceneryPlaceObject.EntryIndex = selection.EntryIndex; - _unkF64F0A = gSceneryPlaceZ; - - uint8_t attemptsLeft = 1; - if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) - { - attemptsLeft = 20; - } - - cost = 0; - for (; attemptsLeft != 0; attemptsLeft--) - { - cost = TryPlaceGhostLargeScenery( - { mapTile, gSceneryPlaceZ, direction }, selection.EntryIndex, gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - if (cost != kMoney64Undefined) - break; - gSceneryPlaceZ += COORDS_Z_STEP; - } - - gSceneryPlaceCost = cost; - break; - } - case SCENERY_TYPE_BANNER: - { - CoordsXY mapTile = {}; - Direction direction; - int32_t z; - - Sub6E1F34Banner(screenPos, selection.EntryIndex, mapTile, &z, &direction); - - if (mapTile.IsNull()) - { - SceneryRemoveGhostToolPlacement(); - return; - } - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectPositionA.x = mapTile.x; - gMapSelectPositionA.y = mapTile.y; - gMapSelectPositionB.x = mapTile.x; - gMapSelectPositionB.y = mapTile.y; - gMapSelectType = MAP_SELECT_TYPE_FULL; - - MapInvalidateSelectionRect(); - - // If no change in ghost placement - if ((gSceneryGhostType & SCENERY_GHOST_FLAG_4) && mapTile == gSceneryGhostPosition - && z == gSceneryGhostPosition.z && direction == gSceneryPlaceRotation) - { - return; - } - - SceneryRemoveGhostToolPlacement(); - - cost = TryPlaceGhostBanner({ mapTile, z, direction }, selection.EntryIndex); - - gSceneryPlaceCost = cost; - break; - } - } - } - - /** - * - * rct2: 0x006644DD - */ - money64 SelectionRaiseLand(uint8_t flag) - { - int32_t centreX = (gMapSelectPositionA.x + gMapSelectPositionB.x) / 2; - int32_t centreY = (gMapSelectPositionA.y + gMapSelectPositionB.y) / 2; - centreX += 16; - centreY += 16; - - if (gLandMountainMode) - { - auto landSmoothAction = LandSmoothAction( - { centreX, centreY }, - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType, - false); - auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction) - : GameActions::Query(&landSmoothAction); - return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; - } - - auto landRaiseAction = LandRaiseAction( - { centreX, centreY }, - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType); - auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landRaiseAction) - : GameActions::Query(&landRaiseAction); - - return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; - } - - /** - * - * rct2: 0x006645B3 - */ - money64 SelectionLowerLand(uint8_t flag) - { - int32_t centreX = (gMapSelectPositionA.x + gMapSelectPositionB.x) / 2; - int32_t centreY = (gMapSelectPositionA.y + gMapSelectPositionB.y) / 2; - centreX += 16; - centreY += 16; - - if (gLandMountainMode) - { - auto landSmoothAction = LandSmoothAction( - { centreX, centreY }, - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType, - true); - auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction) - : GameActions::Query(&landSmoothAction); - return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; - } - - auto landLowerAction = LandLowerAction( - { centreX, centreY }, - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType); - auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landLowerAction) - : GameActions::Query(&landLowerAction); - - return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; - } - - /** - * part of window_top_toolbar_tool_drag(0x0066CB4E) - * rct2: 0x00664454 - */ - void LandToolDrag(const ScreenCoordsXY& screenPos) - { - auto* window = WindowFindFromPoint(screenPos); - if (window == nullptr) - return; - WidgetIndex widget_index = WindowFindWidgetFromPoint(*window, screenPos); - if (widget_index == -1) - return; - const auto& widget = window->widgets[widget_index]; - if (widget.type != WindowWidgetType::Viewport) - return; - const auto* selectedViewport = window->viewport; - if (selectedViewport == nullptr) - return; - - int16_t tile_height = selectedViewport->zoom.ApplyInversedTo(-16); - - int32_t y_diff = screenPos.y - gInputDragLast.y; - - if (y_diff <= tile_height) - { - gInputDragLast.y += tile_height; - - SelectionRaiseLand(GAME_COMMAND_FLAG_APPLY); - - gLandToolRaiseCost = kMoney64Undefined; - gLandToolLowerCost = kMoney64Undefined; - } - else if (y_diff >= -tile_height) - { - gInputDragLast.y -= tile_height; - - SelectionLowerLand(GAME_COMMAND_FLAG_APPLY); - - gLandToolRaiseCost = kMoney64Undefined; - gLandToolLowerCost = kMoney64Undefined; - } - } - - /** - * part of window_top_toolbar_tool_drag(0x0066CB4E) - * rct2: 0x006E6D4B - */ - void WaterToolDrag(const ScreenCoordsXY& screenPos) - { - auto* window = WindowFindFromPoint(screenPos); - if (window == nullptr || window->viewport == nullptr) - return; - - int16_t dx = window->viewport->zoom.ApplyInversedTo(-16); - - auto offsetPos = screenPos - ScreenCoordsXY{ 0, gInputDragLast.y }; - - if (offsetPos.y <= dx) - { - gInputDragLast.y += dx; - - auto waterRaiseAction = WaterRaiseAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); - GameActions::Execute(&waterRaiseAction); - - gWaterToolRaiseCost = kMoney64Undefined; - gWaterToolLowerCost = kMoney64Undefined; - - return; - } - - dx = -dx; - - if (offsetPos.y >= dx) - { - gInputDragLast.y += dx; - - auto waterLowerAction = WaterLowerAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); - GameActions::Execute(&waterLowerAction); - gWaterToolRaiseCost = kMoney64Undefined; - gWaterToolLowerCost = kMoney64Undefined; - - return; - } - } - - /** - * - * rct2: 0x006E24F6 - * On failure returns kMoney64Undefined - * On success places ghost scenery and returns cost to place proper - */ - money64 TryPlaceGhostSmallScenery( - CoordsXYZD loc, uint8_t quadrant, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, - colour_t tertiaryColour) - { - SceneryRemoveGhostToolPlacement(); - - // 6e252b - auto smallSceneryPlaceAction = SmallSceneryPlaceAction( - loc, quadrant, entryIndex, primaryColour, secondaryColour, tertiaryColour); - smallSceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); - auto res = GameActions::Execute(&smallSceneryPlaceAction); - if (res.Error != GameActions::Status::Ok) - return kMoney64Undefined; - - const auto placementData = res.GetData(); - - gSceneryPlaceRotation = loc.direction; - gSceneryPlaceObject.SceneryType = SCENERY_TYPE_SMALL; - gSceneryPlaceObject.EntryIndex = entryIndex; - - gSceneryGhostPosition = { loc, placementData.BaseHeight }; - gSceneryQuadrant = placementData.SceneryQuadrant; - if (placementData.GroundFlags & ELEMENT_IS_UNDERGROUND) - { - // Set underground on - ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOn); - } - else - { - // Set underground off - ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOff); - } - - gSceneryGhostType |= SCENERY_GHOST_FLAG_0; - return res.Cost; - } - - money64 TryPlaceGhostPathAddition(CoordsXYZ loc, ObjectEntryIndex entryIndex) - { - SceneryRemoveGhostToolPlacement(); - - // 6e265b - auto footpathAdditionPlaceAction = FootpathAdditionPlaceAction(loc, entryIndex); - footpathAdditionPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); - footpathAdditionPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) - { - return; - } - gSceneryGhostPosition = loc; - gSceneryGhostType |= SCENERY_GHOST_FLAG_1; - }); - auto res = GameActions::Execute(&footpathAdditionPlaceAction); - if (res.Error != GameActions::Status::Ok) - return kMoney64Undefined; - - return res.Cost; - } - - money64 TryPlaceGhostWall( - CoordsXYZ loc, uint8_t edge, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, - colour_t tertiaryColour) - { - SceneryRemoveGhostToolPlacement(); - - // 6e26b0 - auto wallPlaceAction = WallPlaceAction(entryIndex, loc, edge, primaryColour, secondaryColour, tertiaryColour); - wallPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); - wallPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) - return; - - const auto placementData = result->GetData(); - gSceneryGhostPosition = { loc, placementData.BaseHeight }; - gSceneryGhostWallRotation = edge; - - gSceneryGhostType |= SCENERY_GHOST_FLAG_2; - }); - - auto res = GameActions::Execute(&wallPlaceAction); - if (res.Error != GameActions::Status::Ok) - return kMoney64Undefined; - - return res.Cost; - } - - money64 TryPlaceGhostLargeScenery( - CoordsXYZD loc, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, colour_t tertiaryColour) - { - SceneryRemoveGhostToolPlacement(); - - // 6e25a7 - auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, entryIndex, primaryColour, secondaryColour, tertiaryColour); - sceneryPlaceAction.SetFlags( - GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); - auto res = GameActions::Execute(&sceneryPlaceAction); - if (res.Error != GameActions::Status::Ok) - return kMoney64Undefined; - - const auto placementData = res.GetData(); - - gSceneryPlaceRotation = loc.direction; - - gSceneryGhostPosition = { loc, placementData.firstTileHeight }; - if (placementData.GroundFlags & ELEMENT_IS_UNDERGROUND) - { - // Set underground on - ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOn); - } - else - { - // Set underground off - ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOff); - } - - gSceneryGhostType |= SCENERY_GHOST_FLAG_3; - return res.Cost; - } - - money64 TryPlaceGhostBanner(CoordsXYZD loc, ObjectEntryIndex entryIndex) - { - SceneryRemoveGhostToolPlacement(); - - // 6e2612 - auto primaryColour = gWindowSceneryPrimaryColour; - auto bannerPlaceAction = BannerPlaceAction(loc, entryIndex, primaryColour); - bannerPlaceAction.SetFlags( - GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); - auto res = GameActions::Execute(&bannerPlaceAction); - if (res.Error != GameActions::Status::Ok) - return kMoney64Undefined; - - gSceneryGhostPosition = loc; - gSceneryGhostPosition.z += PATH_HEIGHT_STEP; - gSceneryPlaceRotation = loc.direction; - gSceneryGhostType |= SCENERY_GHOST_FLAG_4; - return res.Cost; - } - - /** - * - * rct2: 0x006E3158 - */ - void RepaintSceneryToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex) - { - auto flag = EnumsToFlags( - ViewportInteractionItem::Scenery, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery, - ViewportInteractionItem::Banner); - auto info = GetMapCoordinatesFromPos(screenCoords, flag); - switch (info.SpriteType) - { - case ViewportInteractionItem::Scenery: - { - auto* sceneryEntry = info.Element->AsSmallScenery()->GetEntry(); - - // If can't repaint - if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | SMALL_SCENERY_FLAG_HAS_GLASS)) - return; - - uint8_t quadrant = info.Element->AsSmallScenery()->GetSceneryQuadrant(); - auto repaintScenery = SmallScenerySetColourAction( - { info.Loc, info.Element->GetBaseZ() }, quadrant, info.Element->AsSmallScenery()->GetEntryIndex(), - gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - GameActions::Execute(&repaintScenery); - break; - } - case ViewportInteractionItem::Wall: - { - auto* scenery_entry = info.Element->AsWall()->GetEntry(); - - // If can't repaint - if (!(scenery_entry->flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS))) - return; - - auto repaintScenery = WallSetColourAction( - { info.Loc, info.Element->GetBaseZ(), info.Element->GetDirection() }, gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - GameActions::Execute(&repaintScenery); - break; - } - case ViewportInteractionItem::LargeScenery: - { - auto* sceneryEntry = info.Element->AsLargeScenery()->GetEntry(); - - // If can't repaint - if (!(sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) - return; - - auto repaintScenery = LargeScenerySetColourAction( - { info.Loc, info.Element->GetBaseZ(), info.Element->GetDirection() }, - info.Element->AsLargeScenery()->GetSequenceIndex(), gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - GameActions::Execute(&repaintScenery); - break; - } - case ViewportInteractionItem::Banner: - { - auto banner = info.Element->AsBanner()->GetBanner(); - if (banner != nullptr) - { - auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry(banner->type); - if (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) + // If no change in ghost placement + if ((gSceneryGhostType & SCENERY_GHOST_FLAG_1) && mapTile == gSceneryGhostPosition + && z == gSceneryGhostPosition.z) { - auto repaintScenery = BannerSetColourAction( - { info.Loc, info.Element->GetBaseZ(), info.Element->AsBanner()->GetPosition() }, - gWindowSceneryPrimaryColour); - - GameActions::Execute(&repaintScenery); + return; } - } - break; - } - default: - return; - } - } - void SceneryEyedropperToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex) - { - auto flag = EnumsToFlags( - ViewportInteractionItem::Scenery, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery, - ViewportInteractionItem::Banner, ViewportInteractionItem::PathAddition); - auto info = GetMapCoordinatesFromPos(screenCoords, flag); - switch (info.SpriteType) + SceneryRemoveGhostToolPlacement(); + + cost = TryPlaceGhostPathAddition({ mapTile, z }, selection.EntryIndex); + + gSceneryPlaceCost = cost; + break; + } + case SCENERY_TYPE_WALL: + { + CoordsXY mapTile = {}; + uint8_t edge; + + Sub6E1F34Wall(screenPos, selection.EntryIndex, mapTile, &edge); + + if (mapTile.IsNull()) + { + SceneryRemoveGhostToolPlacement(); + return; + } + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectPositionA.x = mapTile.x; + gMapSelectPositionA.y = mapTile.y; + gMapSelectPositionB.x = mapTile.x; + gMapSelectPositionB.y = mapTile.y; + gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + edge; + + MapInvalidateSelectionRect(); + + // If no change in ghost placement + if ((gSceneryGhostType & SCENERY_GHOST_FLAG_2) && mapTile == gSceneryGhostPosition + && edge == gSceneryGhostWallRotation && gSceneryPlaceZ == _unkF64F0A) + { + return; + } + + SceneryRemoveGhostToolPlacement(); + + gSceneryGhostWallRotation = edge; + _unkF64F0A = gSceneryPlaceZ; + + uint8_t attemptsLeft = 1; + if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) + { + attemptsLeft = 20; + } + + cost = 0; + for (; attemptsLeft != 0; attemptsLeft--) + { + cost = TryPlaceGhostWall( + { mapTile, gSceneryPlaceZ }, edge, selection.EntryIndex, gWindowSceneryPrimaryColour, + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + if (cost != kMoney64Undefined) + break; + gSceneryPlaceZ += 8; + } + + gSceneryPlaceCost = cost; + break; + } + case SCENERY_TYPE_LARGE: + { + CoordsXY mapTile = {}; + Direction direction; + + Sub6E1F34LargeScenery(screenPos, selection.EntryIndex, mapTile, &direction); + + if (mapTile.IsNull()) + { + SceneryRemoveGhostToolPlacement(); + return; + } + + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selection.EntryIndex); + gMapSelectionTiles.clear(); + + for (auto* tile = sceneryEntry->tiles; + tile->x_offset != static_cast(static_cast(0xFFFF)); tile++) + { + CoordsXY tileLocation = { tile->x_offset, tile->y_offset }; + auto rotatedTileCoords = tileLocation.Rotate(direction); + + rotatedTileCoords.x += mapTile.x; + rotatedTileCoords.y += mapTile.y; + + gMapSelectionTiles.push_back(rotatedTileCoords); + } + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + MapInvalidateMapSelectionTiles(); + + // If no change in ghost placement + if ((gSceneryGhostType & SCENERY_GHOST_FLAG_3) && mapTile == gSceneryGhostPosition + && gSceneryPlaceZ == _unkF64F0A && gSceneryPlaceObject.SceneryType == SCENERY_TYPE_LARGE + && gSceneryPlaceObject.EntryIndex == selection.EntryIndex) + { + return; + } + + SceneryRemoveGhostToolPlacement(); + + gSceneryPlaceObject.SceneryType = SCENERY_TYPE_LARGE; + gSceneryPlaceObject.EntryIndex = selection.EntryIndex; + _unkF64F0A = gSceneryPlaceZ; + + uint8_t attemptsLeft = 1; + if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) + { + attemptsLeft = 20; + } + + cost = 0; + for (; attemptsLeft != 0; attemptsLeft--) + { + cost = TryPlaceGhostLargeScenery( + { mapTile, gSceneryPlaceZ, direction }, selection.EntryIndex, gWindowSceneryPrimaryColour, + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + if (cost != kMoney64Undefined) + break; + gSceneryPlaceZ += COORDS_Z_STEP; + } + + gSceneryPlaceCost = cost; + break; + } + case SCENERY_TYPE_BANNER: + { + CoordsXY mapTile = {}; + Direction direction; + int32_t z; + + Sub6E1F34Banner(screenPos, selection.EntryIndex, mapTile, &z, &direction); + + if (mapTile.IsNull()) + { + SceneryRemoveGhostToolPlacement(); + return; + } + + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectPositionA.x = mapTile.x; + gMapSelectPositionA.y = mapTile.y; + gMapSelectPositionB.x = mapTile.x; + gMapSelectPositionB.y = mapTile.y; + gMapSelectType = MAP_SELECT_TYPE_FULL; + + MapInvalidateSelectionRect(); + + // If no change in ghost placement + if ((gSceneryGhostType & SCENERY_GHOST_FLAG_4) && mapTile == gSceneryGhostPosition + && z == gSceneryGhostPosition.z && direction == gSceneryPlaceRotation) + { + return; + } + + SceneryRemoveGhostToolPlacement(); + + cost = TryPlaceGhostBanner({ mapTile, z, direction }, selection.EntryIndex); + + gSceneryPlaceCost = cost; + break; + } + } + } + + /** + * + * rct2: 0x006644DD + */ + money64 SelectionRaiseLand(uint8_t flag) { - case ViewportInteractionItem::Scenery: + int32_t centreX = (gMapSelectPositionA.x + gMapSelectPositionB.x) / 2; + int32_t centreY = (gMapSelectPositionA.y + gMapSelectPositionB.y) / 2; + centreX += 16; + centreY += 16; + + if (gLandMountainMode) { - SmallSceneryElement* sceneryElement = info.Element->AsSmallScenery(); - auto entryIndex = sceneryElement->GetEntryIndex(); - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); - if (sceneryEntry != nullptr) - { - WindowScenerySetSelectedItem( - { SCENERY_TYPE_SMALL, entryIndex }, sceneryElement->GetPrimaryColour(), - sceneryElement->GetSecondaryColour(), sceneryElement->GetTertiaryColour(), - sceneryElement->GetDirectionWithOffset(GetCurrentRotation())); - } - break; + auto landSmoothAction = LandSmoothAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + gMapSelectType, false); + auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction) + : GameActions::Query(&landSmoothAction); + return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; } - case ViewportInteractionItem::Wall: + + auto landRaiseAction = LandRaiseAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType); + auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landRaiseAction) + : GameActions::Query(&landRaiseAction); + + return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; + } + + /** + * + * rct2: 0x006645B3 + */ + money64 SelectionLowerLand(uint8_t flag) + { + int32_t centreX = (gMapSelectPositionA.x + gMapSelectPositionB.x) / 2; + int32_t centreY = (gMapSelectPositionA.y + gMapSelectPositionB.y) / 2; + centreX += 16; + centreY += 16; + + if (gLandMountainMode) { - auto entryIndex = info.Element->AsWall()->GetEntryIndex(); - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); - if (sceneryEntry != nullptr) - { - WindowScenerySetSelectedItem( - { SCENERY_TYPE_WALL, entryIndex }, info.Element->AsWall()->GetPrimaryColour(), - info.Element->AsWall()->GetSecondaryColour(), info.Element->AsWall()->GetTertiaryColour(), - std::nullopt); - } - break; + auto landSmoothAction = LandSmoothAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + gMapSelectType, true); + auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction) + : GameActions::Query(&landSmoothAction); + return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; } - case ViewportInteractionItem::LargeScenery: + + auto landLowerAction = LandLowerAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType); + auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landLowerAction) + : GameActions::Query(&landLowerAction); + + return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; + } + + /** + * part of window_top_toolbar_tool_drag(0x0066CB4E) + * rct2: 0x00664454 + */ + void LandToolDrag(const ScreenCoordsXY& screenPos) + { + auto* window = WindowFindFromPoint(screenPos); + if (window == nullptr) + return; + WidgetIndex widget_index = WindowFindWidgetFromPoint(*window, screenPos); + if (widget_index == -1) + return; + const auto& widget = window->widgets[widget_index]; + if (widget.type != WindowWidgetType::Viewport) + return; + const auto* selectedViewport = window->viewport; + if (selectedViewport == nullptr) + return; + + int16_t tile_height = selectedViewport->zoom.ApplyInversedTo(-16); + + int32_t y_diff = screenPos.y - gInputDragLast.y; + + if (y_diff <= tile_height) { - auto entryIndex = info.Element->AsLargeScenery()->GetEntryIndex(); - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); - if (sceneryEntry != nullptr) - { - WindowScenerySetSelectedItem( - { SCENERY_TYPE_LARGE, entryIndex }, info.Element->AsLargeScenery()->GetPrimaryColour(), - info.Element->AsLargeScenery()->GetSecondaryColour(), std::nullopt, - (GetCurrentRotation() + info.Element->GetDirection()) & 3); - } - break; + gInputDragLast.y += tile_height; + + SelectionRaiseLand(GAME_COMMAND_FLAG_APPLY); + + gLandToolRaiseCost = kMoney64Undefined; + gLandToolLowerCost = kMoney64Undefined; } - case ViewportInteractionItem::Banner: + else if (y_diff >= -tile_height) { - auto banner = info.Element->AsBanner()->GetBanner(); - if (banner != nullptr) + gInputDragLast.y -= tile_height; + + SelectionLowerLand(GAME_COMMAND_FLAG_APPLY); + + gLandToolRaiseCost = kMoney64Undefined; + gLandToolLowerCost = kMoney64Undefined; + } + } + + /** + * part of window_top_toolbar_tool_drag(0x0066CB4E) + * rct2: 0x006E6D4B + */ + void WaterToolDrag(const ScreenCoordsXY& screenPos) + { + auto* window = WindowFindFromPoint(screenPos); + if (window == nullptr || window->viewport == nullptr) + return; + + int16_t dx = window->viewport->zoom.ApplyInversedTo(-16); + + auto offsetPos = screenPos - ScreenCoordsXY{ 0, gInputDragLast.y }; + + if (offsetPos.y <= dx) + { + gInputDragLast.y += dx; + + auto waterRaiseAction = WaterRaiseAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + GameActions::Execute(&waterRaiseAction); + + gWaterToolRaiseCost = kMoney64Undefined; + gWaterToolLowerCost = kMoney64Undefined; + + return; + } + + dx = -dx; + + if (offsetPos.y >= dx) + { + gInputDragLast.y += dx; + + auto waterLowerAction = WaterLowerAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + GameActions::Execute(&waterLowerAction); + gWaterToolRaiseCost = kMoney64Undefined; + gWaterToolLowerCost = kMoney64Undefined; + + return; + } + } + + /** + * + * rct2: 0x006E24F6 + * On failure returns kMoney64Undefined + * On success places ghost scenery and returns cost to place proper + */ + money64 TryPlaceGhostSmallScenery( + CoordsXYZD loc, uint8_t quadrant, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, + colour_t tertiaryColour) + { + SceneryRemoveGhostToolPlacement(); + + // 6e252b + auto smallSceneryPlaceAction = SmallSceneryPlaceAction( + loc, quadrant, entryIndex, primaryColour, secondaryColour, tertiaryColour); + smallSceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); + auto res = GameActions::Execute(&smallSceneryPlaceAction); + if (res.Error != GameActions::Status::Ok) + return kMoney64Undefined; + + const auto placementData = res.GetData(); + + gSceneryPlaceRotation = loc.direction; + gSceneryPlaceObject.SceneryType = SCENERY_TYPE_SMALL; + gSceneryPlaceObject.EntryIndex = entryIndex; + + gSceneryGhostPosition = { loc, placementData.BaseHeight }; + gSceneryQuadrant = placementData.SceneryQuadrant; + if (placementData.GroundFlags & ELEMENT_IS_UNDERGROUND) + { + // Set underground on + ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOn); + } + else + { + // Set underground off + ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOff); + } + + gSceneryGhostType |= SCENERY_GHOST_FLAG_0; + return res.Cost; + } + + money64 TryPlaceGhostPathAddition(CoordsXYZ loc, ObjectEntryIndex entryIndex) + { + SceneryRemoveGhostToolPlacement(); + + // 6e265b + auto footpathAdditionPlaceAction = FootpathAdditionPlaceAction(loc, entryIndex); + footpathAdditionPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); + footpathAdditionPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) { - auto sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(banner->type); + return; + } + gSceneryGhostPosition = loc; + gSceneryGhostType |= SCENERY_GHOST_FLAG_1; + }); + auto res = GameActions::Execute(&footpathAdditionPlaceAction); + if (res.Error != GameActions::Status::Ok) + return kMoney64Undefined; + + return res.Cost; + } + + money64 TryPlaceGhostWall( + CoordsXYZ loc, uint8_t edge, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, + colour_t tertiaryColour) + { + SceneryRemoveGhostToolPlacement(); + + // 6e26b0 + auto wallPlaceAction = WallPlaceAction(entryIndex, loc, edge, primaryColour, secondaryColour, tertiaryColour); + wallPlaceAction.SetFlags( + GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); + wallPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + return; + + const auto placementData = result->GetData(); + gSceneryGhostPosition = { loc, placementData.BaseHeight }; + gSceneryGhostWallRotation = edge; + + gSceneryGhostType |= SCENERY_GHOST_FLAG_2; + }); + + auto res = GameActions::Execute(&wallPlaceAction); + if (res.Error != GameActions::Status::Ok) + return kMoney64Undefined; + + return res.Cost; + } + + money64 TryPlaceGhostLargeScenery( + CoordsXYZD loc, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, + colour_t tertiaryColour) + { + SceneryRemoveGhostToolPlacement(); + + // 6e25a7 + auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, entryIndex, primaryColour, secondaryColour, tertiaryColour); + sceneryPlaceAction.SetFlags( + GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); + auto res = GameActions::Execute(&sceneryPlaceAction); + if (res.Error != GameActions::Status::Ok) + return kMoney64Undefined; + + const auto placementData = res.GetData(); + + gSceneryPlaceRotation = loc.direction; + + gSceneryGhostPosition = { loc, placementData.firstTileHeight }; + if (placementData.GroundFlags & ELEMENT_IS_UNDERGROUND) + { + // Set underground on + ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOn); + } + else + { + // Set underground off + ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOff); + } + + gSceneryGhostType |= SCENERY_GHOST_FLAG_3; + return res.Cost; + } + + money64 TryPlaceGhostBanner(CoordsXYZD loc, ObjectEntryIndex entryIndex) + { + SceneryRemoveGhostToolPlacement(); + + // 6e2612 + auto primaryColour = gWindowSceneryPrimaryColour; + auto bannerPlaceAction = BannerPlaceAction(loc, entryIndex, primaryColour); + bannerPlaceAction.SetFlags( + GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); + auto res = GameActions::Execute(&bannerPlaceAction); + if (res.Error != GameActions::Status::Ok) + return kMoney64Undefined; + + gSceneryGhostPosition = loc; + gSceneryGhostPosition.z += PATH_HEIGHT_STEP; + gSceneryPlaceRotation = loc.direction; + gSceneryGhostType |= SCENERY_GHOST_FLAG_4; + return res.Cost; + } + + /** + * + * rct2: 0x006E3158 + */ + void RepaintSceneryToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex) + { + auto flag = EnumsToFlags( + ViewportInteractionItem::Scenery, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery, + ViewportInteractionItem::Banner); + auto info = GetMapCoordinatesFromPos(screenCoords, flag); + switch (info.SpriteType) + { + case ViewportInteractionItem::Scenery: + { + auto* sceneryEntry = info.Element->AsSmallScenery()->GetEntry(); + + // If can't repaint + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | SMALL_SCENERY_FLAG_HAS_GLASS)) + return; + + uint8_t quadrant = info.Element->AsSmallScenery()->GetSceneryQuadrant(); + auto repaintScenery = SmallScenerySetColourAction( + { info.Loc, info.Element->GetBaseZ() }, quadrant, info.Element->AsSmallScenery()->GetEntryIndex(), + gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + GameActions::Execute(&repaintScenery); + break; + } + case ViewportInteractionItem::Wall: + { + auto* scenery_entry = info.Element->AsWall()->GetEntry(); + + // If can't repaint + if (!(scenery_entry->flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS))) + return; + + auto repaintScenery = WallSetColourAction( + { info.Loc, info.Element->GetBaseZ(), info.Element->GetDirection() }, gWindowSceneryPrimaryColour, + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + GameActions::Execute(&repaintScenery); + break; + } + case ViewportInteractionItem::LargeScenery: + { + auto* sceneryEntry = info.Element->AsLargeScenery()->GetEntry(); + + // If can't repaint + if (!(sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) + return; + + auto repaintScenery = LargeScenerySetColourAction( + { info.Loc, info.Element->GetBaseZ(), info.Element->GetDirection() }, + info.Element->AsLargeScenery()->GetSequenceIndex(), gWindowSceneryPrimaryColour, + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + GameActions::Execute(&repaintScenery); + break; + } + case ViewportInteractionItem::Banner: + { + auto banner = info.Element->AsBanner()->GetBanner(); + if (banner != nullptr) + { + auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry(banner->type); + if (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) + { + auto repaintScenery = BannerSetColourAction( + { info.Loc, info.Element->GetBaseZ(), info.Element->AsBanner()->GetPosition() }, + gWindowSceneryPrimaryColour); + + GameActions::Execute(&repaintScenery); + } + } + break; + } + default: + return; + } + } + + void SceneryEyedropperToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex) + { + auto flag = EnumsToFlags( + ViewportInteractionItem::Scenery, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery, + ViewportInteractionItem::Banner, ViewportInteractionItem::PathAddition); + auto info = GetMapCoordinatesFromPos(screenCoords, flag); + switch (info.SpriteType) + { + case ViewportInteractionItem::Scenery: + { + SmallSceneryElement* sceneryElement = info.Element->AsSmallScenery(); + auto entryIndex = sceneryElement->GetEntryIndex(); + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); if (sceneryEntry != nullptr) { WindowScenerySetSelectedItem( - { SCENERY_TYPE_BANNER, banner->type }, std::nullopt, std::nullopt, std::nullopt, std::nullopt); + { SCENERY_TYPE_SMALL, entryIndex }, sceneryElement->GetPrimaryColour(), + sceneryElement->GetSecondaryColour(), sceneryElement->GetTertiaryColour(), + sceneryElement->GetDirectionWithOffset(GetCurrentRotation())); } + break; } - break; - } - case ViewportInteractionItem::PathAddition: - { - auto entryIndex = info.Element->AsPath()->GetAdditionEntryIndex(); - auto* pathAdditionEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); - if (pathAdditionEntry != nullptr) + case ViewportInteractionItem::Wall: { - WindowScenerySetSelectedItem( - { SCENERY_TYPE_PATH_ITEM, entryIndex }, std::nullopt, std::nullopt, std::nullopt, std::nullopt); - } - break; - } - default: - break; - } - } - - void Sub6E1F34UpdateScreenCoordsAndButtonsPressed(bool canRaiseItem, ScreenCoordsXY& screenPos) - { - if (!canRaiseItem && !GetGameState().Cheats.DisableSupportLimits) - { - gSceneryCtrlPressed = false; - gSceneryShiftPressed = false; - } - else - { - if (!gSceneryCtrlPressed) - { - if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z)) - { - // CTRL pressed - constexpr auto flag = EnumsToFlags( - ViewportInteractionItem::Terrain, ViewportInteractionItem::Ride, ViewportInteractionItem::Scenery, - ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, - ViewportInteractionItem::LargeScenery); - auto info = GetMapCoordinatesFromPos(screenPos, flag); - - if (info.SpriteType != ViewportInteractionItem::None) + auto entryIndex = info.Element->AsWall()->GetEntryIndex(); + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); + if (sceneryEntry != nullptr) { - gSceneryCtrlPressed = true; - gSceneryCtrlPressZ = info.Element->GetBaseZ(); + WindowScenerySetSelectedItem( + { SCENERY_TYPE_WALL, entryIndex }, info.Element->AsWall()->GetPrimaryColour(), + info.Element->AsWall()->GetSecondaryColour(), info.Element->AsWall()->GetTertiaryColour(), + std::nullopt); } + break; } + case ViewportInteractionItem::LargeScenery: + { + auto entryIndex = info.Element->AsLargeScenery()->GetEntryIndex(); + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); + if (sceneryEntry != nullptr) + { + WindowScenerySetSelectedItem( + { SCENERY_TYPE_LARGE, entryIndex }, info.Element->AsLargeScenery()->GetPrimaryColour(), + info.Element->AsLargeScenery()->GetSecondaryColour(), std::nullopt, + (GetCurrentRotation() + info.Element->GetDirection()) & 3); + } + break; + } + case ViewportInteractionItem::Banner: + { + auto banner = info.Element->AsBanner()->GetBanner(); + if (banner != nullptr) + { + auto sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(banner->type); + if (sceneryEntry != nullptr) + { + WindowScenerySetSelectedItem( + { SCENERY_TYPE_BANNER, banner->type }, std::nullopt, std::nullopt, std::nullopt, std::nullopt); + } + } + break; + } + case ViewportInteractionItem::PathAddition: + { + auto entryIndex = info.Element->AsPath()->GetAdditionEntryIndex(); + auto* pathAdditionEntry = OpenRCT2::ObjectManager::GetObjectEntry(entryIndex); + if (pathAdditionEntry != nullptr) + { + WindowScenerySetSelectedItem( + { SCENERY_TYPE_PATH_ITEM, entryIndex }, std::nullopt, std::nullopt, std::nullopt, std::nullopt); + } + break; + } + default: + break; + } + } + + void Sub6E1F34UpdateScreenCoordsAndButtonsPressed(bool canRaiseItem, ScreenCoordsXY& screenPos) + { + if (!canRaiseItem && !GetGameState().Cheats.DisableSupportLimits) + { + gSceneryCtrlPressed = false; + gSceneryShiftPressed = false; } else { - if (!(InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z))) + if (!gSceneryCtrlPressed) { - // CTRL not pressed - gSceneryCtrlPressed = false; - } - } - - if (!gSceneryShiftPressed) - { - if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_SHIFT_Z)) - { - // SHIFT pressed - gSceneryShiftPressed = true; - gSceneryShiftPressX = screenPos.x; - gSceneryShiftPressY = screenPos.y; - gSceneryShiftPressZOffset = 0; - } - } - else - { - if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_SHIFT_Z)) - { - // SHIFT pressed - gSceneryShiftPressZOffset = (gSceneryShiftPressY - screenPos.y + 4); - // Scale delta by zoom to match mouse position. - auto* mainWnd = WindowGetMain(); - if (mainWnd != nullptr && mainWnd->viewport != nullptr) + if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z)) { - gSceneryShiftPressZOffset = mainWnd->viewport->zoom.ApplyTo(gSceneryShiftPressZOffset); - } - gSceneryShiftPressZOffset = Floor2(gSceneryShiftPressZOffset, 8); + // CTRL pressed + constexpr auto flag = EnumsToFlags( + ViewportInteractionItem::Terrain, ViewportInteractionItem::Ride, ViewportInteractionItem::Scenery, + ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall, + ViewportInteractionItem::LargeScenery); + auto info = GetMapCoordinatesFromPos(screenPos, flag); - screenPos.x = gSceneryShiftPressX; - screenPos.y = gSceneryShiftPressY; + if (info.SpriteType != ViewportInteractionItem::None) + { + gSceneryCtrlPressed = true; + gSceneryCtrlPressZ = info.Element->GetBaseZ(); + } + } } else { - // SHIFT not pressed - gSceneryShiftPressed = false; + if (!(InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z))) + { + // CTRL not pressed + gSceneryCtrlPressed = false; + } + } + + if (!gSceneryShiftPressed) + { + if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_SHIFT_Z)) + { + // SHIFT pressed + gSceneryShiftPressed = true; + gSceneryShiftPressX = screenPos.x; + gSceneryShiftPressY = screenPos.y; + gSceneryShiftPressZOffset = 0; + } + } + else + { + if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_SHIFT_Z)) + { + // SHIFT pressed + gSceneryShiftPressZOffset = (gSceneryShiftPressY - screenPos.y + 4); + // Scale delta by zoom to match mouse position. + auto* mainWnd = WindowGetMain(); + if (mainWnd != nullptr && mainWnd->viewport != nullptr) + { + gSceneryShiftPressZOffset = mainWnd->viewport->zoom.ApplyTo(gSceneryShiftPressZOffset); + } + gSceneryShiftPressZOffset = Floor2(gSceneryShiftPressZOffset, 8); + + screenPos.x = gSceneryShiftPressX; + screenPos.y = gSceneryShiftPressY; + } + else + { + // SHIFT not pressed + gSceneryShiftPressed = false; + } } } } - } - void Sub6E1F34SmallScenery( - const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, uint8_t* outQuadrant, - Direction* outRotation) - { - auto* w = WindowFindByClass(WindowClass::Scenery); - - if (w == nullptr) + void Sub6E1F34SmallScenery( + const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, uint8_t* outQuadrant, + Direction* outRotation) { - gridPos.SetNull(); - return; - } + auto* w = WindowFindByClass(WindowClass::Scenery); - auto screenPos = sourceScreenPos; - uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo( - std::numeric_limits::max() - 32); - bool can_raise_item = false; + if (w == nullptr) + { + gridPos.SetNull(); + return; + } - const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryIndex); - if (sceneryEntry == nullptr) - { - gridPos.SetNull(); - return; - } + auto screenPos = sourceScreenPos; + uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo( + std::numeric_limits::max() - 32); + bool can_raise_item = false; - maxPossibleHeight -= sceneryEntry->height; - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_STACKABLE)) - { - can_raise_item = true; - } + const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryIndex); + if (sceneryEntry == nullptr) + { + gridPos.SetNull(); + return; + } - Sub6E1F34UpdateScreenCoordsAndButtonsPressed(can_raise_item, screenPos); + maxPossibleHeight -= sceneryEntry->height; + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_STACKABLE)) + { + can_raise_item = true; + } - // Small scenery - if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) - { - uint8_t quadrant = 0; + Sub6E1F34UpdateScreenCoordsAndButtonsPressed(can_raise_item, screenPos); + + // Small scenery + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) + { + uint8_t quadrant = 0; + + // If CTRL not pressed + if (!gSceneryCtrlPressed) + { + auto gridCoords = ScreenGetMapXYQuadrant(screenPos, &quadrant); + if (!gridCoords.has_value()) + { + gridPos.SetNull(); + return; + } + gridPos = gridCoords.value(); + + gSceneryPlaceZ = 0; + + // If SHIFT pressed + if (gSceneryShiftPressed) + { + auto* surfaceElement = MapGetSurfaceElementAt(gridPos); + + if (surfaceElement == nullptr) + { + gridPos.SetNull(); + return; + } + + int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0; + z += gSceneryShiftPressZOffset; + + z = std::clamp(z, 16, maxPossibleHeight); + + gSceneryPlaceZ = z; + } + } + else + { + int16_t z = gSceneryCtrlPressZ; + + auto mapCoords = ScreenGetMapXYQuadrantWithZ(screenPos, z, &quadrant); + if (!mapCoords.has_value()) + { + gridPos.SetNull(); + return; + } + gridPos = mapCoords.value(); + + // If SHIFT pressed + if (gSceneryShiftPressed) + { + z += gSceneryShiftPressZOffset; + } + + z = std::clamp(z, 16, maxPossibleHeight); + + gSceneryPlaceZ = z; + } + + if (gridPos.IsNull()) + return; + + uint8_t rotation = gWindowSceneryRotation; + + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) + { + rotation = UtilRand() & 0xFF; + } + + rotation -= GetCurrentRotation(); + rotation &= 0x3; + + if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) + { + VirtualFloorSetHeight(gSceneryPlaceZ); + } + + *outQuadrant = quadrant ^ 2; + *outRotation = rotation; + + return; + } // If CTRL not pressed if (!gSceneryCtrlPressed) { - auto gridCoords = ScreenGetMapXYQuadrant(screenPos, &quadrant); + constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water); + + auto info = GetMapCoordinatesFromPos(screenPos, flag); + gridPos = info.Loc; + + if (info.SpriteType == ViewportInteractionItem::None) + { + gridPos.SetNull(); + return; + } + + // If CTRL and SHIFT not pressed + gSceneryPlaceZ = 0; + + // If SHIFT pressed + if (gSceneryShiftPressed) + { + auto surfaceElement = MapGetSurfaceElementAt(gridPos); + + if (surfaceElement == nullptr) + { + gridPos.SetNull(); + return; + } + + int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0; + z += gSceneryShiftPressZOffset; + + z = std::clamp(z, 16, maxPossibleHeight); + + gSceneryPlaceZ = z; + } + } + else + { + int16_t z = gSceneryCtrlPressZ; + auto coords = ScreenGetMapXYWithZ(screenPos, z); + if (coords.has_value()) + { + gridPos = *coords; + } + else + { + gridPos.SetNull(); + } + // If SHIFT pressed + if (gSceneryShiftPressed) + { + z += gSceneryShiftPressZOffset; + } + + z = std::clamp(z, 16, maxPossibleHeight); + + gSceneryPlaceZ = z; + } + + if (gridPos.IsNull()) + return; + + gridPos = gridPos.ToTileStart(); + uint8_t rotation = gWindowSceneryRotation; + + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) + { + rotation = UtilRand() & 0xFF; + } + + rotation -= GetCurrentRotation(); + rotation &= 0x3; + + if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) + { + VirtualFloorSetHeight(gSceneryPlaceZ); + } + + *outQuadrant = 0; + *outRotation = rotation; + } + + void Sub6E1F34PathItem( + const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, int32_t* outZ) + { + auto* w = WindowFindByClass(WindowClass::Scenery); + + if (w == nullptr) + { + gridPos.SetNull(); + return; + } + + auto screenPos = sourceScreenPos; + Sub6E1F34UpdateScreenCoordsAndButtonsPressed(false, screenPos); + + // Path bits + constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition); + auto info = GetMapCoordinatesFromPos(screenPos, flag); + gridPos = info.Loc; + + if (info.SpriteType == ViewportInteractionItem::None) + { + gridPos.SetNull(); + return; + } + + if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) + { + VirtualFloorSetHeight(gSceneryPlaceZ); + } + + *outZ = info.Element->GetBaseZ(); + } + + void Sub6E1F34Wall( + const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, uint8_t* outEdges) + { + auto* w = WindowFindByClass(WindowClass::Scenery); + + if (w == nullptr) + { + gridPos.SetNull(); + return; + } + + auto screenPos = sourceScreenPos; + uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo( + std::numeric_limits::max() - 32); + + auto* wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryIndex); + if (wallEntry != nullptr) + { + maxPossibleHeight -= wallEntry->height; + } + + Sub6E1F34UpdateScreenCoordsAndButtonsPressed(true, screenPos); + + // Walls + uint8_t edge; + // If CTRL not pressed + if (!gSceneryCtrlPressed) + { + auto gridCoords = ScreenGetMapXYSide(screenPos, &edge); if (!gridCoords.has_value()) { gridPos.SetNull(); @@ -1758,8 +1978,7 @@ private: else { int16_t z = gSceneryCtrlPressZ; - - auto mapCoords = ScreenGetMapXYQuadrantWithZ(screenPos, z, &quadrant); + auto mapCoords = ScreenGetMapXYSideWithZ(screenPos, z, &edge); if (!mapCoords.has_value()) { gridPos.SetNull(); @@ -1781,13 +2000,103 @@ private: if (gridPos.IsNull()) return; - uint8_t rotation = gWindowSceneryRotation; - - if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) + if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) { - rotation = UtilRand() & 0xFF; + VirtualFloorSetHeight(gSceneryPlaceZ); } + *outEdges = edge; + } + + void Sub6E1F34LargeScenery( + const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, Direction* outDirection) + { + auto* w = WindowFindByClass(WindowClass::Scenery); + + if (w == nullptr) + { + gridPos.SetNull(); + return; + } + + auto screenPos = sourceScreenPos; + uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo( + std::numeric_limits::max() - 32); + + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryIndex); + if (sceneryEntry) + { + int16_t maxClearZ = 0; + for (int32_t i = 0; sceneryEntry->tiles[i].x_offset != -1; ++i) + { + maxClearZ = std::max(maxClearZ, sceneryEntry->tiles[i].z_clearance); + } + maxPossibleHeight = std::max(0, maxPossibleHeight - maxClearZ); + } + + Sub6E1F34UpdateScreenCoordsAndButtonsPressed(true, screenPos); + + // Large scenery + // If CTRL not pressed + if (!gSceneryCtrlPressed) + { + const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenPos); + gridPos = mapCoords; + + if (gridPos.IsNull()) + return; + + gSceneryPlaceZ = 0; + + // If SHIFT pressed + if (gSceneryShiftPressed) + { + auto* surfaceElement = MapGetSurfaceElementAt(gridPos); + + if (surfaceElement == nullptr) + { + gridPos.SetNull(); + return; + } + + int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0; + z += gSceneryShiftPressZOffset; + + z = std::clamp(z, 16, maxPossibleHeight); + + gSceneryPlaceZ = z; + } + } + else + { + int16_t z = gSceneryCtrlPressZ; + auto coords = ScreenGetMapXYWithZ(screenPos, z); + if (coords.has_value()) + { + gridPos = *coords; + } + else + { + gridPos.SetNull(); + } + + // If SHIFT pressed + if (gSceneryShiftPressed) + { + z += gSceneryShiftPressZOffset; + } + + z = std::clamp(z, 16, maxPossibleHeight); + + gSceneryPlaceZ = z; + } + + if (gridPos.IsNull()) + return; + + gridPos = gridPos.ToTileStart(); + + Direction rotation = gWindowSceneryRotation; rotation -= GetCurrentRotation(); rotation &= 0x3; @@ -1796,17 +2105,26 @@ private: VirtualFloorSetHeight(gSceneryPlaceZ); } - *outQuadrant = quadrant ^ 2; - *outRotation = rotation; - - return; + *outDirection = rotation; } - // If CTRL not pressed - if (!gSceneryCtrlPressed) + void Sub6E1F34Banner( + const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, int32_t* outZ, + Direction* outDirection) { - constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water); + auto* w = WindowFindByClass(WindowClass::Scenery); + if (w == nullptr) + { + gridPos.SetNull(); + return; + } + + auto screenPos = sourceScreenPos; + Sub6E1F34UpdateScreenCoordsAndButtonsPressed(false, screenPos); + + // Banner + constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition); auto info = GetMapCoordinatesFromPos(screenPos, flag); gridPos = info.Loc; @@ -1816,1066 +2134,694 @@ private: return; } - // If CTRL and SHIFT not pressed - gSceneryPlaceZ = 0; + uint8_t rotation = gWindowSceneryRotation; + rotation -= GetCurrentRotation(); + rotation &= 0x3; - // If SHIFT pressed - if (gSceneryShiftPressed) + auto z = info.Element->GetBaseZ(); + + if (info.Element->AsPath()->IsSloped()) { - auto surfaceElement = MapGetSurfaceElementAt(gridPos); - - if (surfaceElement == nullptr) + if (rotation != DirectionReverse(info.Element->AsPath()->GetSlopeDirection())) { - gridPos.SetNull(); - return; + z += (2 * COORDS_Z_STEP); } - - int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0; - z += gSceneryShiftPressZOffset; - - z = std::clamp(z, 16, maxPossibleHeight); - - gSceneryPlaceZ = z; - } - } - else - { - int16_t z = gSceneryCtrlPressZ; - auto coords = ScreenGetMapXYWithZ(screenPos, z); - if (coords.has_value()) - { - gridPos = *coords; - } - else - { - gridPos.SetNull(); - } - // If SHIFT pressed - if (gSceneryShiftPressed) - { - z += gSceneryShiftPressZOffset; } - z = std::clamp(z, 16, maxPossibleHeight); - - gSceneryPlaceZ = z; - } - - if (gridPos.IsNull()) - return; - - gridPos = gridPos.ToTileStart(); - uint8_t rotation = gWindowSceneryRotation; - - if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) - { - rotation = UtilRand() & 0xFF; - } - - rotation -= GetCurrentRotation(); - rotation &= 0x3; - - if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) - { - VirtualFloorSetHeight(gSceneryPlaceZ); - } - - *outQuadrant = 0; - *outRotation = rotation; - } - - void Sub6E1F34PathItem( - const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, int32_t* outZ) - { - auto* w = WindowFindByClass(WindowClass::Scenery); - - if (w == nullptr) - { - gridPos.SetNull(); - return; - } - - auto screenPos = sourceScreenPos; - Sub6E1F34UpdateScreenCoordsAndButtonsPressed(false, screenPos); - - // Path bits - constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition); - auto info = GetMapCoordinatesFromPos(screenPos, flag); - gridPos = info.Loc; - - if (info.SpriteType == ViewportInteractionItem::None) - { - gridPos.SetNull(); - return; - } - - if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) - { - VirtualFloorSetHeight(gSceneryPlaceZ); - } - - *outZ = info.Element->GetBaseZ(); - } - - void Sub6E1F34Wall( - const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, uint8_t* outEdges) - { - auto* w = WindowFindByClass(WindowClass::Scenery); - - if (w == nullptr) - { - gridPos.SetNull(); - return; - } - - auto screenPos = sourceScreenPos; - uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo( - std::numeric_limits::max() - 32); - - auto* wallEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryIndex); - if (wallEntry != nullptr) - { - maxPossibleHeight -= wallEntry->height; - } - - Sub6E1F34UpdateScreenCoordsAndButtonsPressed(true, screenPos); - - // Walls - uint8_t edge; - // If CTRL not pressed - if (!gSceneryCtrlPressed) - { - auto gridCoords = ScreenGetMapXYSide(screenPos, &edge); - if (!gridCoords.has_value()) + if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) { - gridPos.SetNull(); + VirtualFloorSetHeight(gSceneryPlaceZ); + } + + *outDirection = rotation; + *outZ = z; + } + + /** + * + * rct2: 0x006E2CC6 + */ + void SceneryToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex) + { + SceneryRemoveGhostToolPlacement(); + if (gWindowSceneryPaintEnabled & 1) + { + RepaintSceneryToolDown(screenCoords, widgetIndex); return; } - gridPos = gridCoords.value(); - gSceneryPlaceZ = 0; - - // If SHIFT pressed - if (gSceneryShiftPressed) + if (gWindowSceneryEyedropperEnabled) { - auto* surfaceElement = MapGetSurfaceElementAt(gridPos); - - if (surfaceElement == nullptr) - { - gridPos.SetNull(); - return; - } - - int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0; - z += gSceneryShiftPressZOffset; - - z = std::clamp(z, 16, maxPossibleHeight); - - gSceneryPlaceZ = z; - } - } - else - { - int16_t z = gSceneryCtrlPressZ; - auto mapCoords = ScreenGetMapXYSideWithZ(screenPos, z, &edge); - if (!mapCoords.has_value()) - { - gridPos.SetNull(); + SceneryEyedropperToolDown(screenCoords, widgetIndex); return; } - gridPos = mapCoords.value(); - // If SHIFT pressed - if (gSceneryShiftPressed) + auto selectedTab = WindowSceneryGetTabSelection(); + uint8_t sceneryType = selectedTab.SceneryType; + uint16_t selectedScenery = selectedTab.EntryIndex; + CoordsXY gridPos; + + switch (sceneryType) { - z += gSceneryShiftPressZOffset; - } - - z = std::clamp(z, 16, maxPossibleHeight); - - gSceneryPlaceZ = z; - } - - if (gridPos.IsNull()) - return; - - if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) - { - VirtualFloorSetHeight(gSceneryPlaceZ); - } - - *outEdges = edge; - } - - void Sub6E1F34LargeScenery( - const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, Direction* outDirection) - { - auto* w = WindowFindByClass(WindowClass::Scenery); - - if (w == nullptr) - { - gridPos.SetNull(); - return; - } - - auto screenPos = sourceScreenPos; - uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo( - std::numeric_limits::max() - 32); - - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(sceneryIndex); - if (sceneryEntry) - { - int16_t maxClearZ = 0; - for (int32_t i = 0; sceneryEntry->tiles[i].x_offset != -1; ++i) - { - maxClearZ = std::max(maxClearZ, sceneryEntry->tiles[i].z_clearance); - } - maxPossibleHeight = std::max(0, maxPossibleHeight - maxClearZ); - } - - Sub6E1F34UpdateScreenCoordsAndButtonsPressed(true, screenPos); - - // Large scenery - // If CTRL not pressed - if (!gSceneryCtrlPressed) - { - const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenPos); - gridPos = mapCoords; - - if (gridPos.IsNull()) - return; - - gSceneryPlaceZ = 0; - - // If SHIFT pressed - if (gSceneryShiftPressed) - { - auto* surfaceElement = MapGetSurfaceElementAt(gridPos); - - if (surfaceElement == nullptr) + case SCENERY_TYPE_SMALL: { - gridPos.SetNull(); - return; - } + uint8_t quadrant; + Direction rotation; + Sub6E1F34SmallScenery(screenCoords, selectedScenery, gridPos, &quadrant, &rotation); + if (gridPos.IsNull()) + return; - int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0; - z += gSceneryShiftPressZOffset; - - z = std::clamp(z, 16, maxPossibleHeight); - - gSceneryPlaceZ = z; - } - } - else - { - int16_t z = gSceneryCtrlPressZ; - auto coords = ScreenGetMapXYWithZ(screenPos, z); - if (coords.has_value()) - { - gridPos = *coords; - } - else - { - gridPos.SetNull(); - } - - // If SHIFT pressed - if (gSceneryShiftPressed) - { - z += gSceneryShiftPressZOffset; - } - - z = std::clamp(z, 16, maxPossibleHeight); - - gSceneryPlaceZ = z; - } - - if (gridPos.IsNull()) - return; - - gridPos = gridPos.ToTileStart(); - - Direction rotation = gWindowSceneryRotation; - rotation -= GetCurrentRotation(); - rotation &= 0x3; - - if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) - { - VirtualFloorSetHeight(gSceneryPlaceZ); - } - - *outDirection = rotation; - } - - void Sub6E1F34Banner( - const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, int32_t* outZ, - Direction* outDirection) - { - auto* w = WindowFindByClass(WindowClass::Scenery); - - if (w == nullptr) - { - gridPos.SetNull(); - return; - } - - auto screenPos = sourceScreenPos; - Sub6E1F34UpdateScreenCoordsAndButtonsPressed(false, screenPos); - - // Banner - constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition); - auto info = GetMapCoordinatesFromPos(screenPos, flag); - gridPos = info.Loc; - - if (info.SpriteType == ViewportInteractionItem::None) - { - gridPos.SetNull(); - return; - } - - uint8_t rotation = gWindowSceneryRotation; - rotation -= GetCurrentRotation(); - rotation &= 0x3; - - auto z = info.Element->GetBaseZ(); - - if (info.Element->AsPath()->IsSloped()) - { - if (rotation != DirectionReverse(info.Element->AsPath()->GetSlopeDirection())) - { - z += (2 * COORDS_Z_STEP); - } - } - - if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off) - { - VirtualFloorSetHeight(gSceneryPlaceZ); - } - - *outDirection = rotation; - *outZ = z; - } - - /** - * - * rct2: 0x006E2CC6 - */ - void SceneryToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex) - { - SceneryRemoveGhostToolPlacement(); - if (gWindowSceneryPaintEnabled & 1) - { - RepaintSceneryToolDown(screenCoords, widgetIndex); - return; - } - - if (gWindowSceneryEyedropperEnabled) - { - SceneryEyedropperToolDown(screenCoords, widgetIndex); - return; - } - - auto selectedTab = WindowSceneryGetTabSelection(); - uint8_t sceneryType = selectedTab.SceneryType; - uint16_t selectedScenery = selectedTab.EntryIndex; - CoordsXY gridPos; - - switch (sceneryType) - { - case SCENERY_TYPE_SMALL: - { - uint8_t quadrant; - Direction rotation; - Sub6E1F34SmallScenery(screenCoords, selectedScenery, gridPos, &quadrant, &rotation); - if (gridPos.IsNull()) - return; - - int32_t quantity = 1; - bool isCluster = gWindowSceneryScatterEnabled - && (NetworkGetMode() != NETWORK_MODE_CLIENT - || NetworkCanPerformCommand(NetworkGetCurrentPlayerGroupIndex(), -2)); - - if (isCluster) - { - switch (gWindowSceneryScatterDensity) - { - case ScatterToolDensity::LowDensity: - quantity = gWindowSceneryScatterSize; - break; - - case ScatterToolDensity::MediumDensity: - quantity = gWindowSceneryScatterSize * 2; - break; - - case ScatterToolDensity::HighDensity: - quantity = gWindowSceneryScatterSize * 3; - break; - } - } - - bool forceError = true; - for (int32_t q = 0; q < quantity; q++) - { - int32_t zCoordinate = gSceneryPlaceZ; - auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selectedScenery); - - int16_t cur_grid_x = gridPos.x; - int16_t cur_grid_y = gridPos.y; + int32_t quantity = 1; + bool isCluster = gWindowSceneryScatterEnabled + && (NetworkGetMode() != NETWORK_MODE_CLIENT + || NetworkCanPerformCommand(NetworkGetCurrentPlayerGroupIndex(), -2)); if (isCluster) { - if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) + switch (gWindowSceneryScatterDensity) { - quadrant = UtilRand() & 3; - } + case ScatterToolDensity::LowDensity: + quantity = gWindowSceneryScatterSize; + break; - int16_t grid_x_offset = (UtilRand() % gWindowSceneryScatterSize) - (gWindowSceneryScatterSize / 2); - int16_t grid_y_offset = (UtilRand() % gWindowSceneryScatterSize) - (gWindowSceneryScatterSize / 2); - if (gWindowSceneryScatterSize % 2 == 0) - { - grid_x_offset += 1; - grid_y_offset += 1; - } - cur_grid_x += grid_x_offset * COORDS_XY_STEP; - cur_grid_y += grid_y_offset * COORDS_XY_STEP; + case ScatterToolDensity::MediumDensity: + quantity = gWindowSceneryScatterSize * 2; + break; - if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) - { - gSceneryPlaceRotation = (gSceneryPlaceRotation + 1) & 3; + case ScatterToolDensity::HighDensity: + quantity = gWindowSceneryScatterSize * 3; + break; } } + bool forceError = true; + for (int32_t q = 0; q < quantity; q++) + { + int32_t zCoordinate = gSceneryPlaceZ; + auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry(selectedScenery); + + int16_t cur_grid_x = gridPos.x; + int16_t cur_grid_y = gridPos.y; + + if (isCluster) + { + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) + { + quadrant = UtilRand() & 3; + } + + int16_t grid_x_offset = (UtilRand() % gWindowSceneryScatterSize) - (gWindowSceneryScatterSize / 2); + int16_t grid_y_offset = (UtilRand() % gWindowSceneryScatterSize) - (gWindowSceneryScatterSize / 2); + if (gWindowSceneryScatterSize % 2 == 0) + { + grid_x_offset += 1; + grid_y_offset += 1; + } + cur_grid_x += grid_x_offset * COORDS_XY_STEP; + cur_grid_y += grid_y_offset * COORDS_XY_STEP; + + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) + { + gSceneryPlaceRotation = (gSceneryPlaceRotation + 1) & 3; + } + } + + uint8_t zAttemptRange = 1; + if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) + { + zAttemptRange = 20; + } + + auto success = GameActions::Status::Unknown; + // Try find a valid z coordinate + for (; zAttemptRange != 0; zAttemptRange--) + { + auto smallSceneryPlaceAction = SmallSceneryPlaceAction( + { cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery, + gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + auto res = GameActions::Query(&smallSceneryPlaceAction); + success = res.Error; + if (res.Error == GameActions::Status::Ok) + { + break; + } + + if (res.Error == GameActions::Status::InsufficientFunds) + { + break; + } + if (zAttemptRange != 1) + { + gSceneryPlaceZ += 8; + } + } + + // Actually place + if (success == GameActions::Status::Ok || ((q + 1 == quantity) && forceError)) + { + auto smallSceneryPlaceAction = SmallSceneryPlaceAction( + { cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery, + gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + smallSceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) + { + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + } + }); + auto res = GameActions::Execute(&smallSceneryPlaceAction); + if (res.Error == GameActions::Status::Ok) + { + forceError = false; + } + + if (res.Error == GameActions::Status::InsufficientFunds) + { + break; + } + } + gSceneryPlaceZ = zCoordinate; + } + break; + } + case SCENERY_TYPE_PATH_ITEM: + { + int32_t z; + Sub6E1F34PathItem(screenCoords, selectedScenery, gridPos, &z); + if (gridPos.IsNull()) + return; + + auto footpathAdditionPlaceAction = FootpathAdditionPlaceAction({ gridPos, z }, selectedScenery); + + footpathAdditionPlaceAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { + if (result->Error != GameActions::Status::Ok) + { + return; + } + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + }); + auto res = GameActions::Execute(&footpathAdditionPlaceAction); + break; + } + case SCENERY_TYPE_WALL: + { + uint8_t edges; + Sub6E1F34Wall(screenCoords, selectedScenery, gridPos, &edges); + if (gridPos.IsNull()) + return; + uint8_t zAttemptRange = 1; if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) { zAttemptRange = 20; } - auto success = GameActions::Status::Unknown; - // Try find a valid z coordinate for (; zAttemptRange != 0; zAttemptRange--) { - auto smallSceneryPlaceAction = SmallSceneryPlaceAction( - { cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery, - gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - auto res = GameActions::Query(&smallSceneryPlaceAction); - success = res.Error; + auto wallPlaceAction = WallPlaceAction( + selectedScenery, { gridPos, gSceneryPlaceZ }, edges, gWindowSceneryPrimaryColour, + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + auto res = GameActions::Query(&wallPlaceAction); if (res.Error == GameActions::Status::Ok) { break; } - if (res.Error == GameActions::Status::InsufficientFunds) + if (const auto* message = std::get_if(&res.ErrorMessage)) { - break; + if (*message == STR_NOT_ENOUGH_CASH_REQUIRES || *message == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + { + break; + } } + if (zAttemptRange != 1) { gSceneryPlaceZ += 8; } } - // Actually place - if (success == GameActions::Status::Ok || ((q + 1 == quantity) && forceError)) - { - auto smallSceneryPlaceAction = SmallSceneryPlaceAction( - { cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery, - gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - smallSceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) - { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - } - }); - auto res = GameActions::Execute(&smallSceneryPlaceAction); - if (res.Error == GameActions::Status::Ok) - { - forceError = false; - } - - if (res.Error == GameActions::Status::InsufficientFunds) - { - break; - } - } - gSceneryPlaceZ = zCoordinate; - } - break; - } - case SCENERY_TYPE_PATH_ITEM: - { - int32_t z; - Sub6E1F34PathItem(screenCoords, selectedScenery, gridPos, &z); - if (gridPos.IsNull()) - return; - - auto footpathAdditionPlaceAction = FootpathAdditionPlaceAction({ gridPos, z }, selectedScenery); - - footpathAdditionPlaceAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { - if (result->Error != GameActions::Status::Ok) - { - return; - } - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - }); - auto res = GameActions::Execute(&footpathAdditionPlaceAction); - break; - } - case SCENERY_TYPE_WALL: - { - uint8_t edges; - Sub6E1F34Wall(screenCoords, selectedScenery, gridPos, &edges); - if (gridPos.IsNull()) - return; - - uint8_t zAttemptRange = 1; - if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) - { - zAttemptRange = 20; - } - - for (; zAttemptRange != 0; zAttemptRange--) - { auto wallPlaceAction = WallPlaceAction( selectedScenery, { gridPos, gSceneryPlaceZ }, edges, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - auto res = GameActions::Query(&wallPlaceAction); - if (res.Error == GameActions::Status::Ok) + wallPlaceAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) + { + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + } + }); + auto res = GameActions::Execute(&wallPlaceAction); + break; + } + case SCENERY_TYPE_LARGE: + { + Direction direction; + Sub6E1F34LargeScenery(screenCoords, selectedScenery, gridPos, &direction); + if (gridPos.IsNull()) + return; + + uint8_t zAttemptRange = 1; + if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) { - break; + zAttemptRange = 20; } - if (const auto* message = std::get_if(&res.ErrorMessage)) + for (; zAttemptRange != 0; zAttemptRange--) { - if (*message == STR_NOT_ENOUGH_CASH_REQUIRES || *message == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + CoordsXYZD loc = { gridPos, gSceneryPlaceZ, direction }; + + auto sceneryPlaceAction = LargeSceneryPlaceAction( + loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, + gWindowSceneryTertiaryColour); + + auto res = GameActions::Query(&sceneryPlaceAction); + if (res.Error == GameActions::Status::Ok) { break; } + + if (const auto* message = std::get_if(&res.ErrorMessage)) + { + if (*message == STR_NOT_ENOUGH_CASH_REQUIRES || *message == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + { + break; + } + } + + if (zAttemptRange != 1) + { + gSceneryPlaceZ += 8; + } } - if (zAttemptRange != 1) - { - gSceneryPlaceZ += 8; - } - } - - auto wallPlaceAction = WallPlaceAction( - selectedScenery, { gridPos, gSceneryPlaceZ }, edges, gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - wallPlaceAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) - { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - } - }); - auto res = GameActions::Execute(&wallPlaceAction); - break; - } - case SCENERY_TYPE_LARGE: - { - Direction direction; - Sub6E1F34LargeScenery(screenCoords, selectedScenery, gridPos, &direction); - if (gridPos.IsNull()) - return; - - uint8_t zAttemptRange = 1; - if (gSceneryPlaceZ != 0 && gSceneryShiftPressed) - { - zAttemptRange = 20; - } - - for (; zAttemptRange != 0; zAttemptRange--) - { CoordsXYZD loc = { gridPos, gSceneryPlaceZ, direction }; auto sceneryPlaceAction = LargeSceneryPlaceAction( loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); - - auto res = GameActions::Query(&sceneryPlaceAction); - if (res.Error == GameActions::Status::Ok) - { - break; - } - - if (const auto* message = std::get_if(&res.ErrorMessage)) - { - if (*message == STR_NOT_ENOUGH_CASH_REQUIRES || *message == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + sceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) { - break; - } - } - - if (zAttemptRange != 1) - { - gSceneryPlaceZ += 8; - } - } - - CoordsXYZD loc = { gridPos, gSceneryPlaceZ, direction }; - - auto sceneryPlaceAction = LargeSceneryPlaceAction( - loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, - gWindowSceneryTertiaryColour); - sceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) - { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - } - else - { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Error, { loc.x, loc.y, gSceneryPlaceZ }); - } - }); - auto res = GameActions::Execute(&sceneryPlaceAction); - break; - } - case SCENERY_TYPE_BANNER: - { - int32_t z; - Direction direction; - Sub6E1F34Banner(screenCoords, selectedScenery, gridPos, &z, &direction); - if (gridPos.IsNull()) - return; - - CoordsXYZD loc{ gridPos, z, direction }; - auto primaryColour = gWindowSceneryPrimaryColour; - auto bannerPlaceAction = BannerPlaceAction(loc, selectedScenery, primaryColour); - bannerPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) - { - auto data = result->GetData(); - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); - ContextOpenDetailWindow(WD_BANNER, data.bannerId.ToUnderlying()); - } - }); - GameActions::Execute(&bannerPlaceAction); - break; - } - } - } - - ClearAction GetClearAction() - { - auto range = MapRange(gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y); - - ClearableItems itemsToClear = 0; - - if (gClearSmallScenery) - itemsToClear |= CLEARABLE_ITEMS::SCENERY_SMALL; - if (gClearLargeScenery) - itemsToClear |= CLEARABLE_ITEMS::SCENERY_LARGE; - if (gClearFootpath) - itemsToClear |= CLEARABLE_ITEMS::SCENERY_FOOTPATH; - - return ClearAction(range, itemsToClear); - } - -public: - void OnMouseUp(WidgetIndex widgetIndex) override - { - WindowBase* mainWindow; - - switch (widgetIndex) - { - case WIDX_PAUSE: - if (NetworkGetMode() != NETWORK_MODE_CLIENT) - { - auto pauseToggleAction = PauseToggleAction(); - GameActions::Execute(&pauseToggleAction); - } - break; - case WIDX_ZOOM_OUT: - if ((mainWindow = WindowGetMain()) != nullptr) - WindowZoomOut(*mainWindow, false); - break; - case WIDX_ZOOM_IN: - if ((mainWindow = WindowGetMain()) != nullptr) - WindowZoomIn(*mainWindow, false); - break; - case WIDX_CLEAR_SCENERY: - ToggleClearSceneryWindow(WIDX_CLEAR_SCENERY); - break; - case WIDX_LAND: - ToggleLandWindow(WIDX_LAND); - break; - case WIDX_WATER: - ToggleWaterWindow(WIDX_WATER); - break; - case WIDX_SCENERY: - if (!ToolSet(*this, WIDX_SCENERY, Tool::Arrow)) - { - InputSetFlag(INPUT_FLAG_6, true); - ContextOpenWindow(WindowClass::Scenery); - } - break; - case WIDX_PATH: - ToggleFootpathWindow(); - break; - case WIDX_CONSTRUCT_RIDE: - ContextOpenWindow(WindowClass::ConstructRide); - break; - case WIDX_RIDES: - ContextOpenWindow(WindowClass::RideList); - break; - case WIDX_PARK: - ContextOpenWindow(WindowClass::ParkInformation); - break; - case WIDX_STAFF: - ContextOpenWindow(WindowClass::StaffList); - break; - case WIDX_GUESTS: - ContextOpenWindow(WindowClass::GuestList); - break; - case WIDX_FINANCES: - ContextOpenWindow(WindowClass::Finances); - break; - case WIDX_RESEARCH: - ContextOpenWindow(WindowClass::Research); - break; - case WIDX_NEWS: - ContextOpenWindow(WindowClass::RecentNews); - break; - case WIDX_MUTE: - OpenRCT2::Audio::ToggleAllSounds(); - break; - case WIDX_CHAT: - if (ChatAvailable()) - { - ChatToggle(); - } - else - { - ContextShowError(STR_CHAT_UNAVAILABLE, STR_NONE, {}); - } - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - int32_t numItems = 0; - Widget& widget = widgets[widgetIndex]; - - switch (widgetIndex) - { - case WIDX_FILE_MENU: - if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) - { - gDropdownItems[numItems++].Format = STR_ABOUT; - gDropdownItems[numItems++].Format = STR_OPTIONS; - gDropdownItems[numItems++].Format = STR_SCREENSHOT; - gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT; - gDropdownItems[numItems++].Format = STR_EMPTY; - gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB; - - if (OpenRCT2::GetContext()->HasNewVersionInfo()) - gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE; - - gDropdownItems[numItems++].Format = STR_EMPTY; - - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - gDropdownItems[numItems++].Format = STR_QUIT_ROLLERCOASTER_DESIGNER; - else - gDropdownItems[numItems++].Format = STR_QUIT_TRACK_DESIGNS_MANAGER; - - gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2; - } - else if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) - { - gDropdownItems[numItems++].Format = STR_LOAD_LANDSCAPE; - gDropdownItems[numItems++].Format = STR_SAVE_LANDSCAPE; - gDropdownItems[numItems++].Format = STR_EMPTY; - gDropdownItems[numItems++].Format = STR_ABOUT; - gDropdownItems[numItems++].Format = STR_OPTIONS; - gDropdownItems[numItems++].Format = STR_SCREENSHOT; - gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT; - gDropdownItems[numItems++].Format = STR_EMPTY; - gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB; - - if (OpenRCT2::GetContext()->HasNewVersionInfo()) - gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE; - - gDropdownItems[numItems++].Format = STR_EMPTY; - gDropdownItems[numItems++].Format = STR_QUIT_SCENARIO_EDITOR; - gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2; - } - else - { - gDropdownItems[numItems++].Format = STR_NEW_GAME; - gDropdownItems[numItems++].Format = STR_LOAD_GAME; - gDropdownItems[numItems++].Format = STR_SAVE_GAME; - gDropdownItems[numItems++].Format = STR_SAVE_GAME_AS; - gDropdownItems[numItems++].Format = STR_EMPTY; - gDropdownItems[numItems++].Format = STR_ABOUT; - gDropdownItems[numItems++].Format = STR_OPTIONS; - gDropdownItems[numItems++].Format = STR_SCREENSHOT; - gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT; - gDropdownItems[numItems++].Format = STR_EMPTY; - gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB; - - if (OpenRCT2::GetContext()->HasNewVersionInfo()) - gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE; - - gDropdownItems[numItems++].Format = STR_EMPTY; - gDropdownItems[numItems++].Format = STR_QUIT_TO_MENU; - gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2; - } - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, - Dropdown::Flag::StayOpen, numItems); - break; - case WIDX_CHEATS: - InitCheatsMenu(widget); - break; - case WIDX_VIEW_MENU: - InitViewMenu(widget); - break; - case WIDX_MAP: - InitMapMenu(widget); - break; - case WIDX_FASTFORWARD: - InitFastforwardMenu(widget); - break; - case WIDX_ROTATE: - InitRotateMenu(widget); - break; - case WIDX_DEBUG: - InitDebugMenu(widget); - break; - case WIDX_NETWORK: - InitNetworkMenu(widget); - break; - } - } - - void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override - { - if (selectedIndex == -1) - { - return; - } - switch (widgetIndex) - { - case WIDX_FILE_MENU: - - // New game is only available in the normal game. Skip one position to avoid incorrect mappings in the menus of - // the other modes. - if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR)) - selectedIndex += 1; - - // Quicksave is only available in the normal game. Skip one position to avoid incorrect mappings in the menus of - // the other modes. - if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR) && selectedIndex > DDIDX_LOAD_GAME) - selectedIndex += 1; - - // Track designer and track designs manager start with About, not Load/save - if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) - selectedIndex += DDIDX_ABOUT; - - // The "Update available" menu item is only available when there is one - if (selectedIndex >= DDIDX_UPDATE_AVAILABLE && !OpenRCT2::GetContext()->HasNewVersionInfo()) - selectedIndex += 1; - - switch (selectedIndex) - { - case DDIDX_NEW_GAME: - { - auto intent = Intent(WindowClass::ScenarioSelect); - intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(ScenarioSelectCallback)); - ContextOpenIntent(&intent); - break; - } - case DDIDX_LOAD_GAME: - { - auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::OpenSavePrompt); - GameActions::Execute(&loadOrQuitAction); - break; - } - case DDIDX_SAVE_GAME: - ToolCancel(); - SaveGame(); - break; - case DDIDX_SAVE_GAME_AS: - if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) - { - auto intent = Intent(WindowClass::Loadsave); - intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE); - intent.PutExtra(INTENT_EXTRA_PATH, GetGameState().ScenarioName); - ContextOpenIntent(&intent); + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); } else { - ToolCancel(); - SaveGameAs(); + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Error, { loc.x, loc.y, gSceneryPlaceZ }); } - break; - case DDIDX_ABOUT: - ContextOpenWindow(WindowClass::About); - break; - case DDIDX_OPTIONS: - ContextOpenWindow(WindowClass::Options); - break; - case DDIDX_SCREENSHOT: - gScreenshotCountdown = 10; - break; - case DDIDX_GIANT_SCREENSHOT: - ScreenshotGiant(); - break; - case DDIDX_FILE_BUG_ON_GITHUB: + }); + auto res = GameActions::Execute(&sceneryPlaceAction); + break; + } + case SCENERY_TYPE_BANNER: + { + int32_t z; + Direction direction; + Sub6E1F34Banner(screenCoords, selectedScenery, gridPos, &z, &direction); + if (gridPos.IsNull()) + return; + + CoordsXYZD loc{ gridPos, z, direction }; + auto primaryColour = gWindowSceneryPrimaryColour; + auto bannerPlaceAction = BannerPlaceAction(loc, selectedScenery, primaryColour); + bannerPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) + { + auto data = result->GetData(); + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position); + ContextOpenDetailWindow(WD_BANNER, data.bannerId.ToUnderlying()); + } + }); + GameActions::Execute(&bannerPlaceAction); + break; + } + } + } + + ClearAction GetClearAction() + { + auto range = MapRange(gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y); + + ClearableItems itemsToClear = 0; + + if (gClearSmallScenery) + itemsToClear |= CLEARABLE_ITEMS::SCENERY_SMALL; + if (gClearLargeScenery) + itemsToClear |= CLEARABLE_ITEMS::SCENERY_LARGE; + if (gClearFootpath) + itemsToClear |= CLEARABLE_ITEMS::SCENERY_FOOTPATH; + + return ClearAction(range, itemsToClear); + } + + public: + void OnMouseUp(WidgetIndex widgetIndex) override + { + WindowBase* mainWindow; + + switch (widgetIndex) + { + case WIDX_PAUSE: + if (NetworkGetMode() != NETWORK_MODE_CLIENT) { - std::string url = "https://github.com/OpenRCT2/OpenRCT2/issues/new?" - "assignees=&labels=bug&template=bug_report.yaml"; - // Automatically fill the "OpenRCT2 build" input - auto versionStr = String::URLEncode(gVersionInfoFull); - url.append("&f299dd2a20432827d99b648f73eb4649b23f8ec98d158d6f82b81e43196ee36b=" + versionStr); - OpenRCT2::GetContext()->GetUiContext()->OpenURL(url); + auto pauseToggleAction = PauseToggleAction(); + GameActions::Execute(&pauseToggleAction); } break; - case DDIDX_UPDATE_AVAILABLE: - ContextOpenWindowView(WV_NEW_VERSION_INFO); - break; - case DDIDX_QUIT_TO_MENU: + case WIDX_ZOOM_OUT: + if ((mainWindow = WindowGetMain()) != nullptr) + WindowZoomOut(*mainWindow, false); + break; + case WIDX_ZOOM_IN: + if ((mainWindow = WindowGetMain()) != nullptr) + WindowZoomIn(*mainWindow, false); + break; + case WIDX_CLEAR_SCENERY: + ToggleClearSceneryWindow(WIDX_CLEAR_SCENERY); + break; + case WIDX_LAND: + ToggleLandWindow(WIDX_LAND); + break; + case WIDX_WATER: + ToggleWaterWindow(WIDX_WATER); + break; + case WIDX_SCENERY: + if (!ToolSet(*this, WIDX_SCENERY, Tool::Arrow)) { - WindowCloseByClass(WindowClass::ManageTrackDesign); - WindowCloseByClass(WindowClass::TrackDeletePrompt); - auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::OpenSavePrompt, PromptMode::SaveBeforeQuit); - GameActions::Execute(&loadOrQuitAction); - break; + InputSetFlag(INPUT_FLAG_6, true); + ContextOpenWindow(WindowClass::Scenery); } - case DDIDX_EXIT_OPENRCT2: - ContextQuit(); + break; + case WIDX_PATH: + ToggleFootpathWindow(); + break; + case WIDX_CONSTRUCT_RIDE: + ContextOpenWindow(WindowClass::ConstructRide); + break; + case WIDX_RIDES: + ContextOpenWindow(WindowClass::RideList); + break; + case WIDX_PARK: + ContextOpenWindow(WindowClass::ParkInformation); + break; + case WIDX_STAFF: + ContextOpenWindow(WindowClass::StaffList); + break; + case WIDX_GUESTS: + ContextOpenWindow(WindowClass::GuestList); + break; + case WIDX_FINANCES: + ContextOpenWindow(WindowClass::Finances); + break; + case WIDX_RESEARCH: + ContextOpenWindow(WindowClass::Research); + break; + case WIDX_NEWS: + ContextOpenWindow(WindowClass::RecentNews); + break; + case WIDX_MUTE: + OpenRCT2::Audio::ToggleAllSounds(); + break; + case WIDX_CHAT: + if (ChatAvailable()) + { + ChatToggle(); + } + else + { + ContextShowError(STR_CHAT_UNAVAILABLE, STR_NONE, {}); + } + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + int32_t numItems = 0; + Widget& widget = widgets[widgetIndex]; + + switch (widgetIndex) + { + case WIDX_FILE_MENU: + if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + { + gDropdownItems[numItems++].Format = STR_ABOUT; + gDropdownItems[numItems++].Format = STR_OPTIONS; + gDropdownItems[numItems++].Format = STR_SCREENSHOT; + gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT; + gDropdownItems[numItems++].Format = STR_EMPTY; + gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB; + + if (OpenRCT2::GetContext()->HasNewVersionInfo()) + gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE; + + gDropdownItems[numItems++].Format = STR_EMPTY; + + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + gDropdownItems[numItems++].Format = STR_QUIT_ROLLERCOASTER_DESIGNER; + else + gDropdownItems[numItems++].Format = STR_QUIT_TRACK_DESIGNS_MANAGER; + + gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2; + } + else if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + { + gDropdownItems[numItems++].Format = STR_LOAD_LANDSCAPE; + gDropdownItems[numItems++].Format = STR_SAVE_LANDSCAPE; + gDropdownItems[numItems++].Format = STR_EMPTY; + gDropdownItems[numItems++].Format = STR_ABOUT; + gDropdownItems[numItems++].Format = STR_OPTIONS; + gDropdownItems[numItems++].Format = STR_SCREENSHOT; + gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT; + gDropdownItems[numItems++].Format = STR_EMPTY; + gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB; + + if (OpenRCT2::GetContext()->HasNewVersionInfo()) + gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE; + + gDropdownItems[numItems++].Format = STR_EMPTY; + gDropdownItems[numItems++].Format = STR_QUIT_SCENARIO_EDITOR; + gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2; + } + else + { + gDropdownItems[numItems++].Format = STR_NEW_GAME; + gDropdownItems[numItems++].Format = STR_LOAD_GAME; + gDropdownItems[numItems++].Format = STR_SAVE_GAME; + gDropdownItems[numItems++].Format = STR_SAVE_GAME_AS; + gDropdownItems[numItems++].Format = STR_EMPTY; + gDropdownItems[numItems++].Format = STR_ABOUT; + gDropdownItems[numItems++].Format = STR_OPTIONS; + gDropdownItems[numItems++].Format = STR_SCREENSHOT; + gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT; + gDropdownItems[numItems++].Format = STR_EMPTY; + gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB; + + if (OpenRCT2::GetContext()->HasNewVersionInfo()) + gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE; + + gDropdownItems[numItems++].Format = STR_EMPTY; + gDropdownItems[numItems++].Format = STR_QUIT_TO_MENU; + gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2; + } + + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, + Dropdown::Flag::StayOpen, numItems); + break; + case WIDX_CHEATS: + InitCheatsMenu(widget); + break; + case WIDX_VIEW_MENU: + InitViewMenu(widget); + break; + case WIDX_MAP: + InitMapMenu(widget); + break; + case WIDX_FASTFORWARD: + InitFastforwardMenu(widget); + break; + case WIDX_ROTATE: + InitRotateMenu(widget); + break; + case WIDX_DEBUG: + InitDebugMenu(widget); + break; + case WIDX_NETWORK: + InitNetworkMenu(widget); + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override + { + if (selectedIndex == -1) + { + return; + } + switch (widgetIndex) + { + case WIDX_FILE_MENU: + + // New game is only available in the normal game. Skip one position to avoid incorrect mappings in the menus + // of the other modes. + if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR)) + selectedIndex += 1; + + // Quicksave is only available in the normal game. Skip one position to avoid incorrect mappings in the + // menus of the other modes. + if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR) && selectedIndex > DDIDX_LOAD_GAME) + selectedIndex += 1; + + // Track designer and track designs manager start with About, not Load/save + if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + selectedIndex += DDIDX_ABOUT; + + // The "Update available" menu item is only available when there is one + if (selectedIndex >= DDIDX_UPDATE_AVAILABLE && !OpenRCT2::GetContext()->HasNewVersionInfo()) + selectedIndex += 1; + + switch (selectedIndex) + { + case DDIDX_NEW_GAME: + { + auto intent = Intent(WindowClass::ScenarioSelect); + intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(ScenarioSelectCallback)); + ContextOpenIntent(&intent); + break; + } + case DDIDX_LOAD_GAME: + { + auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::OpenSavePrompt); + GameActions::Execute(&loadOrQuitAction); + break; + } + case DDIDX_SAVE_GAME: + ToolCancel(); + SaveGame(); + break; + case DDIDX_SAVE_GAME_AS: + if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + { + auto intent = Intent(WindowClass::Loadsave); + intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE); + intent.PutExtra(INTENT_EXTRA_PATH, GetGameState().ScenarioName); + ContextOpenIntent(&intent); + } + else + { + ToolCancel(); + SaveGameAs(); + } + break; + case DDIDX_ABOUT: + ContextOpenWindow(WindowClass::About); + break; + case DDIDX_OPTIONS: + ContextOpenWindow(WindowClass::Options); + break; + case DDIDX_SCREENSHOT: + gScreenshotCountdown = 10; + break; + case DDIDX_GIANT_SCREENSHOT: + ScreenshotGiant(); + break; + case DDIDX_FILE_BUG_ON_GITHUB: + { + std::string url = "https://github.com/OpenRCT2/OpenRCT2/issues/new?" + "assignees=&labels=bug&template=bug_report.yaml"; + // Automatically fill the "OpenRCT2 build" input + auto versionStr = String::URLEncode(gVersionInfoFull); + url.append("&f299dd2a20432827d99b648f73eb4649b23f8ec98d158d6f82b81e43196ee36b=" + versionStr); + OpenRCT2::GetContext()->GetUiContext()->OpenURL(url); + } break; - } - break; - case WIDX_CHEATS: - CheatsMenuDropdown(selectedIndex); - break; - case WIDX_VIEW_MENU: - ViewMenuDropdown(selectedIndex); - break; - case WIDX_MAP: - MapMenuDropdown(selectedIndex); - break; - case WIDX_FASTFORWARD: - FastforwardMenuDropdown(selectedIndex); - break; - case WIDX_ROTATE: - RotateMenuDropdown(selectedIndex); - break; - case WIDX_DEBUG: - DebugMenuDropdown(selectedIndex); - break; - case WIDX_NETWORK: - NetworkMenuDropdown(selectedIndex); - break; + case DDIDX_UPDATE_AVAILABLE: + ContextOpenWindowView(WV_NEW_VERSION_INFO); + break; + case DDIDX_QUIT_TO_MENU: + { + WindowCloseByClass(WindowClass::ManageTrackDesign); + WindowCloseByClass(WindowClass::TrackDeletePrompt); + auto loadOrQuitAction = LoadOrQuitAction( + LoadOrQuitModes::OpenSavePrompt, PromptMode::SaveBeforeQuit); + GameActions::Execute(&loadOrQuitAction); + break; + } + case DDIDX_EXIT_OPENRCT2: + ContextQuit(); + break; + } + break; + case WIDX_CHEATS: + CheatsMenuDropdown(selectedIndex); + break; + case WIDX_VIEW_MENU: + ViewMenuDropdown(selectedIndex); + break; + case WIDX_MAP: + MapMenuDropdown(selectedIndex); + break; + case WIDX_FASTFORWARD: + FastforwardMenuDropdown(selectedIndex); + break; + case WIDX_ROTATE: + RotateMenuDropdown(selectedIndex); + break; + case WIDX_DEBUG: + DebugMenuDropdown(selectedIndex); + break; + case WIDX_NETWORK: + NetworkMenuDropdown(selectedIndex); + break; + } } - } - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - case WIDX_CLEAR_SCENERY: - ToolUpdateSceneryClear(screenCoords); - break; - case WIDX_LAND: - if (gLandPaintMode) - ToolUpdateLandPaint(screenCoords); - else - ToolUpdateLand(screenCoords); - break; - case WIDX_WATER: - ToolUpdateWater(screenCoords); - break; - case WIDX_SCENERY: - ToolUpdateScenery(screenCoords); - break; + switch (widgetIndex) + { + case WIDX_CLEAR_SCENERY: + ToolUpdateSceneryClear(screenCoords); + break; + case WIDX_LAND: + if (gLandPaintMode) + ToolUpdateLandPaint(screenCoords); + else + ToolUpdateLand(screenCoords); + break; + case WIDX_WATER: + ToolUpdateWater(screenCoords); + break; + case WIDX_SCENERY: + ToolUpdateScenery(screenCoords); + break; #ifdef ENABLE_SCRIPTING - default: - auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; - if (customTool) - { - customTool->OnUpdate(screenCoords); - } - break; + default: + auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; + if (customTool) + { + customTool->OnUpdate(screenCoords); + } + break; #endif + } } - } - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - case WIDX_CLEAR_SCENERY: - if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) - { - auto action = GetClearAction(); - GameActions::Execute(&action); - gCurrentToolId = Tool::Crosshair; - } - break; - case WIDX_LAND: - if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) - { - auto surfaceSetStyleAction = SurfaceSetStyleAction( - { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, - gLandToolTerrainSurface, gLandToolTerrainEdge); - - GameActions::Execute(&surfaceSetStyleAction); - - gCurrentToolId = Tool::UpDownArrow; - } - else - { - _landToolBlocked = true; - } - break; - case WIDX_WATER: - if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) - { - gCurrentToolId = Tool::UpDownArrow; - } - else - { - _landToolBlocked = true; - } - break; - case WIDX_SCENERY: - SceneryToolDown(screenCoords, widgetIndex); - break; -#ifdef ENABLE_SCRIPTING - default: - auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; - if (customTool) - { - customTool->OnDown(screenCoords); - } - break; -#endif - } - } - - void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - switch (widgetIndex) - { - case WIDX_CLEAR_SCENERY: - if (WindowFindByClass(WindowClass::Error) == nullptr && (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - { - auto action = GetClearAction(); - GameActions::Execute(&action); - gCurrentToolId = Tool::Crosshair; - } - break; - case WIDX_LAND: - // Custom setting to only change land style instead of raising or lowering land - if (gLandPaintMode) - { + switch (widgetIndex) + { + case WIDX_CLEAR_SCENERY: + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) + { + auto action = GetClearAction(); + GameActions::Execute(&action); + gCurrentToolId = Tool::Crosshair; + } + break; + case WIDX_LAND: if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) { auto surfaceSetStyleAction = SurfaceSetStyleAction( @@ -2884,981 +2830,1039 @@ public: GameActions::Execute(&surfaceSetStyleAction); - // The tool is set to 12 here instead of 3 so that the dragging cursor is not the elevation change - // cursor + gCurrentToolId = Tool::UpDownArrow; + } + else + { + _landToolBlocked = true; + } + break; + case WIDX_WATER: + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) + { + gCurrentToolId = Tool::UpDownArrow; + } + else + { + _landToolBlocked = true; + } + break; + case WIDX_SCENERY: + SceneryToolDown(screenCoords, widgetIndex); + break; +#ifdef ENABLE_SCRIPTING + default: + auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; + if (customTool) + { + customTool->OnDown(screenCoords); + } + break; +#endif + } + } + + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + switch (widgetIndex) + { + case WIDX_CLEAR_SCENERY: + if (WindowFindByClass(WindowClass::Error) == nullptr && (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { + auto action = GetClearAction(); + GameActions::Execute(&action); gCurrentToolId = Tool::Crosshair; } - } - else - { + break; + case WIDX_LAND: + // Custom setting to only change land style instead of raising or lowering land + if (gLandPaintMode) + { + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) + { + auto surfaceSetStyleAction = SurfaceSetStyleAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + gLandToolTerrainSurface, gLandToolTerrainEdge); + + GameActions::Execute(&surfaceSetStyleAction); + + // The tool is set to 12 here instead of 3 so that the dragging cursor is not the elevation change + // cursor + gCurrentToolId = Tool::Crosshair; + } + } + else + { + if (!_landToolBlocked) + { + LandToolDrag(screenCoords); + } + } + break; + case WIDX_WATER: if (!_landToolBlocked) { - LandToolDrag(screenCoords); + WaterToolDrag(screenCoords); } - } - break; - case WIDX_WATER: - if (!_landToolBlocked) - { - WaterToolDrag(screenCoords); - } - break; - case WIDX_SCENERY: - if (gWindowSceneryPaintEnabled & 1) - SceneryToolDown(screenCoords, widgetIndex); - if (gWindowSceneryEyedropperEnabled) - SceneryToolDown(screenCoords, widgetIndex); - break; + break; + case WIDX_SCENERY: + if (gWindowSceneryPaintEnabled & 1) + SceneryToolDown(screenCoords, widgetIndex); + if (gWindowSceneryEyedropperEnabled) + SceneryToolDown(screenCoords, widgetIndex); + break; #ifdef ENABLE_SCRIPTING - default: - auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; - if (customTool) + default: + auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; + if (customTool) + { + customTool->OnDrag(screenCoords); + } + break; +#endif + } + } + + void OnToolUp(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + _landToolBlocked = false; + switch (widgetIndex) + { + case WIDX_LAND: + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gCurrentToolId = Tool::DigDown; + break; + case WIDX_WATER: + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gCurrentToolId = Tool::WaterDown; + break; + case WIDX_CLEAR_SCENERY: + MapInvalidateSelectionRect(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gCurrentToolId = Tool::Crosshair; + break; +#ifdef ENABLE_SCRIPTING + default: + auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; + if (customTool) + { + customTool->OnUp(screenCoords); + } + break; +#endif + } + } + + void OnToolAbort(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_LAND: + case WIDX_WATER: + case WIDX_CLEAR_SCENERY: + HideGridlines(); + break; +#ifdef ENABLE_SCRIPTING + default: + auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; + if (customTool) + { + customTool->OnAbort(); + customTool = {}; + } + break; +#endif + } + } + + void OnPrepareDraw() override + { + int32_t x, widgetIndex, widgetWidth, firstAlignment; + Widget* widget; + + // Enable / disable buttons + widgets[WIDX_PAUSE].type = WindowWidgetType::TrnBtn; + widgets[WIDX_FILE_MENU].type = WindowWidgetType::TrnBtn; + widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::TrnBtn; + widgets[WIDX_ZOOM_IN].type = WindowWidgetType::TrnBtn; + widgets[WIDX_ROTATE].type = WindowWidgetType::TrnBtn; + widgets[WIDX_VIEW_MENU].type = WindowWidgetType::TrnBtn; + widgets[WIDX_MAP].type = WindowWidgetType::TrnBtn; + widgets[WIDX_MUTE].type = WindowWidgetType::TrnBtn; + widgets[WIDX_CHAT].type = WindowWidgetType::TrnBtn; + widgets[WIDX_LAND].type = WindowWidgetType::TrnBtn; + widgets[WIDX_WATER].type = WindowWidgetType::TrnBtn; + widgets[WIDX_SCENERY].type = WindowWidgetType::TrnBtn; + widgets[WIDX_PATH].type = WindowWidgetType::TrnBtn; + widgets[WIDX_CONSTRUCT_RIDE].type = WindowWidgetType::TrnBtn; + widgets[WIDX_RIDES].type = WindowWidgetType::TrnBtn; + widgets[WIDX_PARK].type = WindowWidgetType::TrnBtn; + widgets[WIDX_STAFF].type = WindowWidgetType::TrnBtn; + widgets[WIDX_GUESTS].type = WindowWidgetType::TrnBtn; + widgets[WIDX_CLEAR_SCENERY].type = WindowWidgetType::TrnBtn; + widgets[WIDX_FINANCES].type = WindowWidgetType::TrnBtn; + widgets[WIDX_RESEARCH].type = WindowWidgetType::TrnBtn; + widgets[WIDX_FASTFORWARD].type = WindowWidgetType::TrnBtn; + widgets[WIDX_CHEATS].type = WindowWidgetType::TrnBtn; + widgets[WIDX_DEBUG].type = gConfigGeneral.DebuggingTools ? WindowWidgetType::TrnBtn : WindowWidgetType::Empty; + widgets[WIDX_NEWS].type = WindowWidgetType::TrnBtn; + widgets[WIDX_NETWORK].type = WindowWidgetType::TrnBtn; + + if (!gConfigInterface.ToolbarShowMute) + widgets[WIDX_MUTE].type = WindowWidgetType::Empty; + + if (!gConfigInterface.ToolbarShowChat) + widgets[WIDX_CHAT].type = WindowWidgetType::Empty; + + if (!gConfigInterface.ToolbarShowResearch) + widgets[WIDX_RESEARCH].type = WindowWidgetType::Empty; + + if (!gConfigInterface.ToolbarShowCheats) + widgets[WIDX_CHEATS].type = WindowWidgetType::Empty; + + if (!gConfigInterface.ToolbarShowNews) + widgets[WIDX_NEWS].type = WindowWidgetType::Empty; + + if (!gConfigInterface.ToolbarShowZoom) + { + widgets[WIDX_ZOOM_IN].type = WindowWidgetType::Empty; + widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::Empty; + } + + if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR || gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + widgets[WIDX_PAUSE].type = WindowWidgetType::Empty; + } + + if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) || !gConfigInterface.ToolbarShowFinances) + widgets[WIDX_FINANCES].type = WindowWidgetType::Empty; + + if (gScreenFlags & SCREEN_FLAGS_EDITOR) + { + widgets[WIDX_PARK].type = WindowWidgetType::Empty; + widgets[WIDX_STAFF].type = WindowWidgetType::Empty; + widgets[WIDX_GUESTS].type = WindowWidgetType::Empty; + widgets[WIDX_FINANCES].type = WindowWidgetType::Empty; + widgets[WIDX_RESEARCH].type = WindowWidgetType::Empty; + widgets[WIDX_NEWS].type = WindowWidgetType::Empty; + widgets[WIDX_NETWORK].type = WindowWidgetType::Empty; + + auto& gameState = GetGameState(); + if (gameState.EditorStep != EditorStep::LandscapeEditor) { - customTool->OnDrag(screenCoords); + widgets[WIDX_LAND].type = WindowWidgetType::Empty; + widgets[WIDX_WATER].type = WindowWidgetType::Empty; } - break; + + if (gameState.EditorStep != EditorStep::RollercoasterDesigner) + { + widgets[WIDX_RIDES].type = WindowWidgetType::Empty; + widgets[WIDX_CONSTRUCT_RIDE].type = WindowWidgetType::Empty; + widgets[WIDX_FASTFORWARD].type = WindowWidgetType::Empty; + } + + if (gameState.EditorStep != EditorStep::LandscapeEditor + && gameState.EditorStep != EditorStep::RollercoasterDesigner) + { + widgets[WIDX_MAP].type = WindowWidgetType::Empty; + widgets[WIDX_SCENERY].type = WindowWidgetType::Empty; + widgets[WIDX_PATH].type = WindowWidgetType::Empty; + widgets[WIDX_CLEAR_SCENERY].type = WindowWidgetType::Empty; + + widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::Empty; + widgets[WIDX_ZOOM_IN].type = WindowWidgetType::Empty; + widgets[WIDX_ROTATE].type = WindowWidgetType::Empty; + widgets[WIDX_VIEW_MENU].type = WindowWidgetType::Empty; + } + } + + switch (NetworkGetMode()) + { + case NETWORK_MODE_NONE: + widgets[WIDX_NETWORK].type = WindowWidgetType::Empty; + widgets[WIDX_CHAT].type = WindowWidgetType::Empty; + break; + case NETWORK_MODE_CLIENT: + widgets[WIDX_PAUSE].type = WindowWidgetType::Empty; + [[fallthrough]]; + case NETWORK_MODE_SERVER: + widgets[WIDX_FASTFORWARD].type = WindowWidgetType::Empty; + break; + } + + // Align left hand side toolbar buttons + firstAlignment = 1; + x = 0; + for (size_t i = 0; i < std::size(left_aligned_widgets_order); ++i) + { + widgetIndex = left_aligned_widgets_order[i]; + widget = &widgets[widgetIndex]; + if (widget->type == WindowWidgetType::Empty && widgetIndex != WIDX_SEPARATOR) + continue; + + if (firstAlignment && widgetIndex == WIDX_SEPARATOR) + continue; + + widgetWidth = widget->width(); + widget->left = x; + x += widgetWidth; + widget->right = x; + x += 1; + firstAlignment = 0; + } + + // Align right hand side toolbar buttons if necessary + int32_t screenWidth = ContextGetWidth(); + firstAlignment = 1; + x = std::max(640, screenWidth); + for (size_t i = 0; i < std::size(right_aligned_widgets_order); ++i) + { + widgetIndex = right_aligned_widgets_order[i]; + widget = &widgets[widgetIndex]; + if (widget->type == WindowWidgetType::Empty && widgetIndex != WIDX_SEPARATOR) + continue; + + if (firstAlignment && widgetIndex == WIDX_SEPARATOR) + continue; + + widgetWidth = widget->width(); + x -= 1; + widget->right = x; + x -= widgetWidth; + widget->left = x; + firstAlignment = 0; + } + + // Footpath button pressed down + if (WindowFindByClass(WindowClass::Footpath) == nullptr) + pressed_widgets &= ~(1uLL << WIDX_PATH); + else + pressed_widgets |= (1uLL << WIDX_PATH); + + if (gGamePaused & GAME_PAUSED_NORMAL) + pressed_widgets |= (1uLL << WIDX_PAUSE); + else + pressed_widgets &= ~(1uLL << WIDX_PAUSE); + + if (!OpenRCT2::Audio::gGameSoundsOff) + widgets[WIDX_MUTE].image = ImageId(SPR_G2_TOOLBAR_MUTE, FilterPaletteID::PaletteNull); + else + widgets[WIDX_MUTE].image = ImageId(SPR_G2_TOOLBAR_UNMUTE, FilterPaletteID::PaletteNull); + + // Set map button to the right image. + if (widgets[WIDX_MAP].type != WindowWidgetType::Empty) + { + static constexpr uint32_t _imageIdByRotation[] = { + SPR_G2_MAP_NORTH, + SPR_G2_MAP_WEST, + SPR_G2_MAP_SOUTH, + SPR_G2_MAP_EAST, + }; + + uint32_t mapImageId = _imageIdByRotation[GetCurrentRotation()]; + widgets[WIDX_MAP].image = ImageId(mapImageId, FilterPaletteID::PaletteNull); + } + + // Zoomed out/in disable. Not sure where this code is in the original. + const auto* mainWindow = WindowGetMain(); + if (mainWindow == nullptr || mainWindow->viewport == nullptr) + { + LOG_ERROR("mainWindow or mainWindow->viewport is null!"); + return; + } + + if (mainWindow->viewport->zoom == ZoomLevel::min()) + { + disabled_widgets |= (1uLL << WIDX_ZOOM_IN); + } + else if (mainWindow->viewport->zoom >= ZoomLevel::max()) + { + disabled_widgets |= (1uLL << WIDX_ZOOM_OUT); + } + else + { + disabled_widgets &= ~((1uLL << WIDX_ZOOM_IN) | (1uLL << WIDX_ZOOM_OUT)); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + const auto& gameState = GetGameState(); + int32_t imgId; + + WindowDrawWidgets(*this, dpi); + + ScreenCoordsXY screenPos{}; + // Draw staff button image (setting masks to the staff colours) + if (widgets[WIDX_STAFF].type != WindowWidgetType::Empty) + { + screenPos = { windowPos.x + widgets[WIDX_STAFF].left, windowPos.y + widgets[WIDX_STAFF].top }; + imgId = SPR_TOOLBAR_STAFF; + if (WidgetIsPressed(*this, WIDX_STAFF)) + imgId++; + GfxDrawSprite(dpi, ImageId(imgId, gameState.StaffHandymanColour, gameState.StaffMechanicColour), screenPos); + } + + // Draw fast forward button + if (widgets[WIDX_FASTFORWARD].type != WindowWidgetType::Empty) + { + screenPos = { windowPos.x + widgets[WIDX_FASTFORWARD].left + 0, + windowPos.y + widgets[WIDX_FASTFORWARD].top + 0 }; + if (WidgetIsPressed(*this, WIDX_FASTFORWARD)) + screenPos.y++; + GfxDrawSprite(dpi, ImageId(SPR_G2_FASTFORWARD), screenPos + ScreenCoordsXY{ 6, 3 }); + + for (int32_t i = 0; i < gGameSpeed && gGameSpeed <= 4; i++) + { + GfxDrawSprite(dpi, ImageId(SPR_G2_SPEED_ARROW), screenPos + ScreenCoordsXY{ 5 + i * 5, 15 }); + } + for (int32_t i = 0; i < 3 && i < gGameSpeed - 4 && gGameSpeed >= 5; i++) + { + GfxDrawSprite(dpi, ImageId(SPR_G2_HYPER_ARROW), screenPos + ScreenCoordsXY{ 5 + i * 6, 15 }); + } + } + + // Draw cheats button + if (widgets[WIDX_CHEATS].type != WindowWidgetType::Empty) + { + screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_CHEATS].left - 1, widgets[WIDX_CHEATS].top - 1 }; + if (WidgetIsPressed(*this, WIDX_CHEATS)) + screenPos.y++; + GfxDrawSprite(dpi, ImageId(SPR_G2_SANDBOX), screenPos); + + // Draw an overlay if clearance checks are disabled + if (GetGameState().Cheats.DisableClearanceChecks) + { + auto colour = static_cast(EnumValue(COLOUR_DARK_ORANGE) | EnumValue(COLOUR_FLAG_OUTLINE)); + DrawTextBasic( + dpi, screenPos + ScreenCoordsXY{ 26, 2 }, STR_OVERLAY_CLEARANCE_CHECKS_DISABLED, {}, + { colour, TextAlignment::RIGHT }); + } + } + + // Draw chat button + if (widgets[WIDX_CHAT].type != WindowWidgetType::Empty) + { + screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_CHAT].left, widgets[WIDX_CHAT].top - 2 }; + if (WidgetIsPressed(*this, WIDX_CHAT)) + screenPos.y++; + GfxDrawSprite(dpi, ImageId(SPR_G2_CHAT), screenPos); + } + + // Draw debug button + if (widgets[WIDX_DEBUG].type != WindowWidgetType::Empty) + { + screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_DEBUG].left, widgets[WIDX_DEBUG].top - 1 }; + if (WidgetIsPressed(*this, WIDX_DEBUG)) + screenPos.y++; + GfxDrawSprite(dpi, ImageId(SPR_TAB_GEARS_0), screenPos); + } + + // Draw research button + if (widgets[WIDX_RESEARCH].type != WindowWidgetType::Empty) + { + screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_RESEARCH].left - 1, widgets[WIDX_RESEARCH].top }; + if (WidgetIsPressed(*this, WIDX_RESEARCH)) + screenPos.y++; + GfxDrawSprite(dpi, ImageId(SPR_TAB_FINANCES_RESEARCH_0), screenPos); + } + + // Draw finances button + if (widgets[WIDX_FINANCES].type != WindowWidgetType::Empty) + { + screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_FINANCES].left + 3, widgets[WIDX_FINANCES].top + 1 }; + if (WidgetIsPressed(*this, WIDX_FINANCES)) + screenPos.y++; + GfxDrawSprite(dpi, ImageId(SPR_FINANCE), screenPos); + } + + // Draw news button + if (widgets[WIDX_NEWS].type != WindowWidgetType::Empty) + { + screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_NEWS].left + 3, widgets[WIDX_NEWS].top + 0 }; + if (WidgetIsPressed(*this, WIDX_NEWS)) + screenPos.y++; + GfxDrawSprite(dpi, ImageId(SPR_G2_TAB_NEWS), screenPos); + } + + // Draw network button + if (widgets[WIDX_NETWORK].type != WindowWidgetType::Empty) + { + screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_NETWORK].left + 3, widgets[WIDX_NETWORK].top + 0 }; + if (WidgetIsPressed(*this, WIDX_NETWORK)) + screenPos.y++; + + // Draw (de)sync icon. + imgId = (NetworkIsDesynchronised() ? SPR_G2_MULTIPLAYER_DESYNC : SPR_G2_MULTIPLAYER_SYNC); + GfxDrawSprite(dpi, ImageId(imgId), screenPos + ScreenCoordsXY{ 3, 11 }); + + // Draw number of players. + auto ft = Formatter(); + ft.Add(NetworkGetNumVisiblePlayers()); + auto colour = static_cast(EnumValue(COLOUR_WHITE) | EnumValue(COLOUR_FLAG_OUTLINE)); + DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ 23, 1 }, STR_COMMA16, ft, { colour, TextAlignment::RIGHT }); + } + } + }; + + static void ScenarioSelectCallback(const utf8* path) + { + WindowCloseByClass(WindowClass::EditorObjectSelection); + GameNotifyMapChange(); + GetContext()->LoadParkFromFile(path, false, true); + GameLoadScripts(); + GameNotifyMapChanged(); + } + + /** + * Creates the main game top toolbar window. + * rct2: 0x0066B485 (part of 0x0066B3E8) + */ + WindowBase* WindowTopToolbarOpen() + { + TopToolbar* window = WindowCreate( + WindowClass::TopToolbar, ScreenCoordsXY(0, 0), ContextGetWidth(), TOP_TOOLBAR_HEIGHT + 1, + WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); + + window->widgets = _topToolbarWidgets; + + WindowInitScrollWidgets(*window); + + return window; + } + + /** + * + * rct2: 0x0066D104 + */ + bool LandToolIsActive() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return false; + if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar) + return false; + if (gCurrentToolWidget.widget_index != WIDX_LAND) + return false; + return true; + } + + /** + * + * rct2: 0x0066D125 + */ + bool ClearSceneryToolIsActive() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return false; + if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar) + return false; + if (gCurrentToolWidget.widget_index != WIDX_CLEAR_SCENERY) + return false; + return true; + } + + /** + * + * rct2: 0x0066D125 + */ + bool WaterToolIsActive() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return false; + if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar) + return false; + if (gCurrentToolWidget.widget_index != WIDX_WATER) + return false; + return true; + } + + void TopToolbar::InitViewMenu(Widget& widget) + { + using namespace Dropdown; + constexpr ItemExt items[] = { + ToggleOption(DDIDX_UNDERGROUND_INSIDE, STR_UNDERGROUND_VIEW), + ToggleOption(DDIDX_TRANSPARENT_WATER, STR_VIEWPORT_TRANSPARENT_WATER), + ToggleOption(DDIDX_HIDE_BASE, STR_REMOVE_BASE_LAND), + ToggleOption(DDIDX_HIDE_VERTICAL, STR_REMOVE_VERTICAL_FACES), + Separator(), + ToggleOption(DDIDX_HIDE_RIDES, STR_SEE_THROUGH_RIDES), + ToggleOption(DDIDX_HIDE_VEHICLES, STR_SEE_THROUGH_VEHICLES), + ToggleOption(DDIDX_HIDE_VEGETATION, STR_SEE_THROUGH_VEGETATION), + ToggleOption(DDIDX_HIDE_SCENERY, STR_SEE_THROUGH_SCENERY), + ToggleOption(DDIDX_HIDE_PATHS, STR_SEE_THROUGH_PATHS), + ToggleOption(DDIDX_HIDE_SUPPORTS, STR_SEE_THROUGH_SUPPORTS), + ToggleOption(DDIDX_HIDE_GUESTS, STR_SEE_THROUGH_GUESTS), + ToggleOption(DDIDX_HIDE_STAFF, STR_SEE_THROUGH_STAFF), + Separator(), + ToggleOption(DDIDX_LAND_HEIGHTS, STR_HEIGHT_MARKS_ON_LAND), + ToggleOption(DDIDX_TRACK_HEIGHTS, STR_HEIGHT_MARKS_ON_RIDE_TRACKS), + ToggleOption(DDIDX_PATH_HEIGHTS, STR_HEIGHT_MARKS_ON_PATHS), + Separator(), + ToggleOption(DDIDX_VIEW_CLIPPING, STR_VIEW_CLIPPING_MENU), + ToggleOption(DDIDX_HIGHLIGHT_PATH_ISSUES, STR_HIGHLIGHT_PATH_ISSUES_MENU), + Separator(), + ToggleOption(DDIDX_TRANSPARENCY, STR_TRANSPARENCY_OPTIONS), + }; + + static_assert(ItemIDsMatchIndices(items)); + + SetItems(items); + + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, + TOP_TOOLBAR_VIEW_MENU_COUNT); + + // Set checkmarks + auto* mainViewport = WindowGetMain()->viewport; + if (mainViewport->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE) + Dropdown::SetChecked(DDIDX_UNDERGROUND_INSIDE, true); + if (gConfigGeneral.TransparentWater) + Dropdown::SetChecked(DDIDX_TRANSPARENT_WATER, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_BASE) + Dropdown::SetChecked(DDIDX_HIDE_BASE, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VERTICAL) + Dropdown::SetChecked(DDIDX_HIDE_VERTICAL, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_RIDES) + Dropdown::SetChecked(DDIDX_HIDE_RIDES, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VEHICLES) + Dropdown::SetChecked(DDIDX_HIDE_VEHICLES, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VEGETATION) + Dropdown::SetChecked(DDIDX_HIDE_VEGETATION, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_SCENERY) + Dropdown::SetChecked(DDIDX_HIDE_SCENERY, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_PATHS) + Dropdown::SetChecked(DDIDX_HIDE_PATHS, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_SUPPORTS) + Dropdown::SetChecked(DDIDX_HIDE_SUPPORTS, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_GUESTS) + Dropdown::SetChecked(DDIDX_HIDE_GUESTS, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIDE_STAFF) + Dropdown::SetChecked(DDIDX_HIDE_STAFF, true); + if (mainViewport->flags & VIEWPORT_FLAG_LAND_HEIGHTS) + Dropdown::SetChecked(DDIDX_LAND_HEIGHTS, true); + if (mainViewport->flags & VIEWPORT_FLAG_TRACK_HEIGHTS) + Dropdown::SetChecked(DDIDX_TRACK_HEIGHTS, true); + if (mainViewport->flags & VIEWPORT_FLAG_PATH_HEIGHTS) + Dropdown::SetChecked(DDIDX_PATH_HEIGHTS, true); + if (mainViewport->flags & VIEWPORT_FLAG_CLIP_VIEW) + Dropdown::SetChecked(DDIDX_VIEW_CLIPPING, true); + if (mainViewport->flags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES) + Dropdown::SetChecked(DDIDX_HIGHLIGHT_PATH_ISSUES, true); + + gDropdownDefaultIndex = DDIDX_UNDERGROUND_INSIDE; + + // Opaque water relies on RCT1 sprites. + if (!IsCsgLoaded()) + { + Dropdown::SetDisabled(DDIDX_TRANSPARENT_WATER, true); + } + } + + void TopToolbar::ViewMenuDropdown(int16_t dropdownIndex) + { + auto* w = WindowGetMain(); + if (w != nullptr) + { + switch (dropdownIndex) + { + case DDIDX_UNDERGROUND_INSIDE: + w->viewport->flags ^= VIEWPORT_FLAG_UNDERGROUND_INSIDE; + break; + case DDIDX_TRANSPARENT_WATER: + gConfigGeneral.TransparentWater ^= 1; + ConfigSaveDefault(); + break; + case DDIDX_HIDE_BASE: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_BASE; + break; + case DDIDX_HIDE_VERTICAL: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VERTICAL; + break; + case DDIDX_HIDE_RIDES: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_RIDES; + break; + case DDIDX_HIDE_VEHICLES: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VEHICLES; + break; + case DDIDX_HIDE_VEGETATION: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VEGETATION; + break; + case DDIDX_HIDE_SCENERY: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_SCENERY; + break; + case DDIDX_HIDE_PATHS: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_PATHS; + break; + case DDIDX_HIDE_SUPPORTS: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_SUPPORTS; + break; + case DDIDX_HIDE_GUESTS: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_GUESTS; + break; + case DDIDX_HIDE_STAFF: + w->viewport->flags ^= VIEWPORT_FLAG_HIDE_STAFF; + break; + case DDIDX_LAND_HEIGHTS: + w->viewport->flags ^= VIEWPORT_FLAG_LAND_HEIGHTS; + break; + case DDIDX_TRACK_HEIGHTS: + w->viewport->flags ^= VIEWPORT_FLAG_TRACK_HEIGHTS; + break; + case DDIDX_PATH_HEIGHTS: + w->viewport->flags ^= VIEWPORT_FLAG_PATH_HEIGHTS; + break; + case DDIDX_VIEW_CLIPPING: + if (WindowFindByClass(WindowClass::ViewClipping) == nullptr) + { + ContextOpenWindow(WindowClass::ViewClipping); + } + else + { + // If window is already open, toggle the view clipping on/off + w->viewport->flags ^= VIEWPORT_FLAG_CLIP_VIEW; + } + break; + case DDIDX_HIGHLIGHT_PATH_ISSUES: + w->viewport->flags ^= VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES; + break; + case DDIDX_TRANSPARENCY: + ContextOpenWindow(WindowClass::Transparency); + break; + default: + return; + } + w->Invalidate(); + } + } + + void TopToolbar::InitMapMenu(Widget& widget) + { + auto i = 0; + gDropdownItems[i++].Format = STR_SHORTCUT_SHOW_MAP; + gDropdownItems[i++].Format = STR_EXTRA_VIEWPORT; + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor) + { + gDropdownItems[i++].Format = STR_MAPGEN_WINDOW_TITLE; + } + +#ifdef ENABLE_SCRIPTING + const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; + if (!customMenuItems.empty()) + { + gDropdownItems[i++].Format = STR_EMPTY; + for (const auto& item : customMenuItems) + { + if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Standard) + { + gDropdownItems[i].Format = STR_STRING; + auto sz = item.Text.c_str(); + std::memcpy(&gDropdownItems[i].Args, &sz, sizeof(const char*)); + i++; + } + } + } +#endif + + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, i); + gDropdownDefaultIndex = DDIDX_SHOW_MAP; + } + + void TopToolbar::MapMenuDropdown(int16_t dropdownIndex) + { + int32_t customStartIndex = 3; + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor) + { + customStartIndex++; + } + + if (dropdownIndex < customStartIndex) + { + switch (dropdownIndex) + { + case 0: + ContextOpenWindow(WindowClass::Map); + break; + case 1: + ContextOpenWindow(WindowClass::Viewport); + break; + case 2: + ContextOpenWindow(WindowClass::Mapgen); + break; + } + } + else + { +#ifdef ENABLE_SCRIPTING + const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; + auto customIndex = static_cast(dropdownIndex - customStartIndex); + size_t i = 0; + for (const auto& item : customMenuItems) + { + if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Standard) + { + if (i == customIndex) + { + item.Invoke(); + break; + } + i++; + } + } #endif } } - void OnToolUp(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + void TopToolbar::InitFastforwardMenu(Widget& widget) { - _landToolBlocked = false; - switch (widgetIndex) + int32_t num_items = 4; + gDropdownItems[0].Format = STR_TOGGLE_OPTION; + gDropdownItems[1].Format = STR_TOGGLE_OPTION; + gDropdownItems[2].Format = STR_TOGGLE_OPTION; + gDropdownItems[3].Format = STR_TOGGLE_OPTION; + if (gConfigGeneral.DebuggingTools) { - case WIDX_LAND: - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gCurrentToolId = Tool::DigDown; - break; - case WIDX_WATER: - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gCurrentToolId = Tool::WaterDown; - break; - case WIDX_CLEAR_SCENERY: - MapInvalidateSelectionRect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gCurrentToolId = Tool::Crosshair; - break; -#ifdef ENABLE_SCRIPTING - default: - auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; - if (customTool) - { - customTool->OnUp(screenCoords); - } - break; -#endif + gDropdownItems[4].Format = STR_EMPTY; + gDropdownItems[5].Format = STR_TOGGLE_OPTION; + gDropdownItems[5].Args = STR_SPEED_HYPER; + num_items = 6; + } + + gDropdownItems[0].Args = STR_SPEED_NORMAL; + gDropdownItems[1].Args = STR_SPEED_QUICK; + gDropdownItems[2].Args = STR_SPEED_FAST; + gDropdownItems[3].Args = STR_SPEED_TURBO; + + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0, num_items); + + // Set checkmarks + if (gGameSpeed <= 4) + { + Dropdown::SetChecked(gGameSpeed - 1, true); + } + if (gGameSpeed == 8) + { + Dropdown::SetChecked(5, true); + } + + if (gConfigGeneral.DebuggingTools) + { + gDropdownDefaultIndex = (gGameSpeed == 8 ? 0 : gGameSpeed); + } + else + { + gDropdownDefaultIndex = (gGameSpeed >= 4 ? 0 : gGameSpeed); + } + if (gDropdownDefaultIndex == 4) + { + gDropdownDefaultIndex = 5; } } - void OnToolAbort(WidgetIndex widgetIndex) override + void TopToolbar::FastforwardMenuDropdown(int16_t dropdownIndex) { - switch (widgetIndex) + auto* w = WindowGetMain(); + if (w != nullptr) { - case WIDX_LAND: - case WIDX_WATER: - case WIDX_CLEAR_SCENERY: - HideGridlines(); - break; -#ifdef ENABLE_SCRIPTING - default: - auto& customTool = OpenRCT2::Scripting::ActiveCustomTool; - if (customTool) - { - customTool->OnAbort(); - customTool = {}; - } - break; -#endif + if (dropdownIndex >= 0 && dropdownIndex <= 5) + { + auto newSpeed = dropdownIndex + 1; + if (newSpeed >= 5) + newSpeed = 8; + + auto setSpeedAction = GameSetSpeedAction(newSpeed); + GameActions::Execute(&setSpeedAction); + } } } - void OnPrepareDraw() override + void TopToolbar::InitRotateMenu(Widget& widget) { - int32_t x, widgetIndex, widgetWidth, firstAlignment; - Widget* widget; + gDropdownItems[0].Format = STR_ROTATE_CLOCKWISE; + gDropdownItems[1].Format = STR_ROTATE_ANTI_CLOCKWISE; - // Enable / disable buttons - widgets[WIDX_PAUSE].type = WindowWidgetType::TrnBtn; - widgets[WIDX_FILE_MENU].type = WindowWidgetType::TrnBtn; - widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::TrnBtn; - widgets[WIDX_ZOOM_IN].type = WindowWidgetType::TrnBtn; - widgets[WIDX_ROTATE].type = WindowWidgetType::TrnBtn; - widgets[WIDX_VIEW_MENU].type = WindowWidgetType::TrnBtn; - widgets[WIDX_MAP].type = WindowWidgetType::TrnBtn; - widgets[WIDX_MUTE].type = WindowWidgetType::TrnBtn; - widgets[WIDX_CHAT].type = WindowWidgetType::TrnBtn; - widgets[WIDX_LAND].type = WindowWidgetType::TrnBtn; - widgets[WIDX_WATER].type = WindowWidgetType::TrnBtn; - widgets[WIDX_SCENERY].type = WindowWidgetType::TrnBtn; - widgets[WIDX_PATH].type = WindowWidgetType::TrnBtn; - widgets[WIDX_CONSTRUCT_RIDE].type = WindowWidgetType::TrnBtn; - widgets[WIDX_RIDES].type = WindowWidgetType::TrnBtn; - widgets[WIDX_PARK].type = WindowWidgetType::TrnBtn; - widgets[WIDX_STAFF].type = WindowWidgetType::TrnBtn; - widgets[WIDX_GUESTS].type = WindowWidgetType::TrnBtn; - widgets[WIDX_CLEAR_SCENERY].type = WindowWidgetType::TrnBtn; - widgets[WIDX_FINANCES].type = WindowWidgetType::TrnBtn; - widgets[WIDX_RESEARCH].type = WindowWidgetType::TrnBtn; - widgets[WIDX_FASTFORWARD].type = WindowWidgetType::TrnBtn; - widgets[WIDX_CHEATS].type = WindowWidgetType::TrnBtn; - widgets[WIDX_DEBUG].type = gConfigGeneral.DebuggingTools ? WindowWidgetType::TrnBtn : WindowWidgetType::Empty; - widgets[WIDX_NEWS].type = WindowWidgetType::TrnBtn; - widgets[WIDX_NETWORK].type = WindowWidgetType::TrnBtn; + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, 2); - if (!gConfigInterface.ToolbarShowMute) - widgets[WIDX_MUTE].type = WindowWidgetType::Empty; + gDropdownDefaultIndex = DDIDX_ROTATE_CLOCKWISE; + } - if (!gConfigInterface.ToolbarShowChat) - widgets[WIDX_CHAT].type = WindowWidgetType::Empty; - - if (!gConfigInterface.ToolbarShowResearch) - widgets[WIDX_RESEARCH].type = WindowWidgetType::Empty; - - if (!gConfigInterface.ToolbarShowCheats) - widgets[WIDX_CHEATS].type = WindowWidgetType::Empty; - - if (!gConfigInterface.ToolbarShowNews) - widgets[WIDX_NEWS].type = WindowWidgetType::Empty; - - if (!gConfigInterface.ToolbarShowZoom) + void TopToolbar::RotateMenuDropdown(int16_t dropdownIndex) + { + if (dropdownIndex == 0) { - widgets[WIDX_ZOOM_IN].type = WindowWidgetType::Empty; - widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::Empty; + ViewportRotateAll(1); } - - if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR || gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + else if (dropdownIndex == 1) { - widgets[WIDX_PAUSE].type = WindowWidgetType::Empty; + ViewportRotateAll(-1); } + } - if ((GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY) || !gConfigInterface.ToolbarShowFinances) - widgets[WIDX_FINANCES].type = WindowWidgetType::Empty; + void TopToolbar::InitCheatsMenu(Widget& widget) + { + using namespace Dropdown; + + constexpr ItemExt items[] = { + ToggleOption(DDIDX_CHEATS, STR_CHEAT_TITLE), + ToggleOption(DDIDX_TILE_INSPECTOR, STR_DEBUG_DROPDOWN_TILE_INSPECTOR), + ToggleOption(DDIDX_OBJECT_SELECTION, STR_DEBUG_DROPDOWN_OBJECT_SELECTION), + ToggleOption(DDIDX_INVENTIONS_LIST, STR_DEBUG_DROPDOWN_INVENTIONS_LIST), + ToggleOption(DDIDX_SCENARIO_OPTIONS, STR_DEBUG_DROPDOWN_SCENARIO_OPTIONS), + ToggleOption(DDIDX_OBJECTIVE_OPTIONS, STR_CHEATS_MENU_OBJECTIVE_OPTIONS), + Separator(), + ToggleOption(DDIDX_ENABLE_SANDBOX_MODE, STR_ENABLE_SANDBOX_MODE), + ToggleOption(DDIDX_DISABLE_CLEARANCE_CHECKS, STR_DISABLE_CLEARANCE_CHECKS), + ToggleOption(DDIDX_DISABLE_SUPPORT_LIMITS, STR_DISABLE_SUPPORT_LIMITS), + }; + static_assert(ItemIDsMatchIndices(items)); + + SetItems(items); + + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0, + TOP_TOOLBAR_CHEATS_COUNT); + + // Disable items that are not yet available in multiplayer + if (NetworkGetMode() != NETWORK_MODE_NONE) + { + Dropdown::SetDisabled(DDIDX_OBJECT_SELECTION, true); + Dropdown::SetDisabled(DDIDX_INVENTIONS_LIST, true); + Dropdown::SetDisabled(DDIDX_OBJECTIVE_OPTIONS, true); + } if (gScreenFlags & SCREEN_FLAGS_EDITOR) { - widgets[WIDX_PARK].type = WindowWidgetType::Empty; - widgets[WIDX_STAFF].type = WindowWidgetType::Empty; - widgets[WIDX_GUESTS].type = WindowWidgetType::Empty; - widgets[WIDX_FINANCES].type = WindowWidgetType::Empty; - widgets[WIDX_RESEARCH].type = WindowWidgetType::Empty; - widgets[WIDX_NEWS].type = WindowWidgetType::Empty; - widgets[WIDX_NETWORK].type = WindowWidgetType::Empty; - - auto& gameState = GetGameState(); - if (gameState.EditorStep != EditorStep::LandscapeEditor) - { - widgets[WIDX_LAND].type = WindowWidgetType::Empty; - widgets[WIDX_WATER].type = WindowWidgetType::Empty; - } - - if (gameState.EditorStep != EditorStep::RollercoasterDesigner) - { - widgets[WIDX_RIDES].type = WindowWidgetType::Empty; - widgets[WIDX_CONSTRUCT_RIDE].type = WindowWidgetType::Empty; - widgets[WIDX_FASTFORWARD].type = WindowWidgetType::Empty; - } - - if (gameState.EditorStep != EditorStep::LandscapeEditor - && gameState.EditorStep != EditorStep::RollercoasterDesigner) - { - widgets[WIDX_MAP].type = WindowWidgetType::Empty; - widgets[WIDX_SCENERY].type = WindowWidgetType::Empty; - widgets[WIDX_PATH].type = WindowWidgetType::Empty; - widgets[WIDX_CLEAR_SCENERY].type = WindowWidgetType::Empty; - - widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::Empty; - widgets[WIDX_ZOOM_IN].type = WindowWidgetType::Empty; - widgets[WIDX_ROTATE].type = WindowWidgetType::Empty; - widgets[WIDX_VIEW_MENU].type = WindowWidgetType::Empty; - } + Dropdown::SetDisabled(DDIDX_OBJECT_SELECTION, true); + Dropdown::SetDisabled(DDIDX_INVENTIONS_LIST, true); + Dropdown::SetDisabled(DDIDX_SCENARIO_OPTIONS, true); + Dropdown::SetDisabled(DDIDX_OBJECTIVE_OPTIONS, true); + Dropdown::SetDisabled(DDIDX_ENABLE_SANDBOX_MODE, true); } - switch (NetworkGetMode()) + if (GetGameState().Cheats.SandboxMode) { - case NETWORK_MODE_NONE: - widgets[WIDX_NETWORK].type = WindowWidgetType::Empty; - widgets[WIDX_CHAT].type = WindowWidgetType::Empty; - break; - case NETWORK_MODE_CLIENT: - widgets[WIDX_PAUSE].type = WindowWidgetType::Empty; - [[fallthrough]]; - case NETWORK_MODE_SERVER: - widgets[WIDX_FASTFORWARD].type = WindowWidgetType::Empty; - break; + Dropdown::SetChecked(DDIDX_ENABLE_SANDBOX_MODE, true); } - - // Align left hand side toolbar buttons - firstAlignment = 1; - x = 0; - for (size_t i = 0; i < std::size(left_aligned_widgets_order); ++i) + if (GetGameState().Cheats.DisableClearanceChecks) { - widgetIndex = left_aligned_widgets_order[i]; - widget = &widgets[widgetIndex]; - if (widget->type == WindowWidgetType::Empty && widgetIndex != WIDX_SEPARATOR) - continue; - - if (firstAlignment && widgetIndex == WIDX_SEPARATOR) - continue; - - widgetWidth = widget->width(); - widget->left = x; - x += widgetWidth; - widget->right = x; - x += 1; - firstAlignment = 0; + Dropdown::SetChecked(DDIDX_DISABLE_CLEARANCE_CHECKS, true); } - - // Align right hand side toolbar buttons if necessary - int32_t screenWidth = ContextGetWidth(); - firstAlignment = 1; - x = std::max(640, screenWidth); - for (size_t i = 0; i < std::size(right_aligned_widgets_order); ++i) + if (GetGameState().Cheats.DisableSupportLimits) { - widgetIndex = right_aligned_widgets_order[i]; - widget = &widgets[widgetIndex]; - if (widget->type == WindowWidgetType::Empty && widgetIndex != WIDX_SEPARATOR) - continue; - - if (firstAlignment && widgetIndex == WIDX_SEPARATOR) - continue; - - widgetWidth = widget->width(); - x -= 1; - widget->right = x; - x -= widgetWidth; - widget->left = x; - firstAlignment = 0; + Dropdown::SetChecked(DDIDX_DISABLE_SUPPORT_LIMITS, true); } - // Footpath button pressed down - if (WindowFindByClass(WindowClass::Footpath) == nullptr) - pressed_widgets &= ~(1uLL << WIDX_PATH); - else - pressed_widgets |= (1uLL << WIDX_PATH); - - if (gGamePaused & GAME_PAUSED_NORMAL) - pressed_widgets |= (1uLL << WIDX_PAUSE); - else - pressed_widgets &= ~(1uLL << WIDX_PAUSE); - - if (!OpenRCT2::Audio::gGameSoundsOff) - widgets[WIDX_MUTE].image = ImageId(SPR_G2_TOOLBAR_MUTE, FilterPaletteID::PaletteNull); - else - widgets[WIDX_MUTE].image = ImageId(SPR_G2_TOOLBAR_UNMUTE, FilterPaletteID::PaletteNull); - - // Set map button to the right image. - if (widgets[WIDX_MAP].type != WindowWidgetType::Empty) - { - static constexpr uint32_t _imageIdByRotation[] = { - SPR_G2_MAP_NORTH, - SPR_G2_MAP_WEST, - SPR_G2_MAP_SOUTH, - SPR_G2_MAP_EAST, - }; - - uint32_t mapImageId = _imageIdByRotation[GetCurrentRotation()]; - widgets[WIDX_MAP].image = ImageId(mapImageId, FilterPaletteID::PaletteNull); - } - - // Zoomed out/in disable. Not sure where this code is in the original. - const auto* mainWindow = WindowGetMain(); - if (mainWindow == nullptr || mainWindow->viewport == nullptr) - { - LOG_ERROR("mainWindow or mainWindow->viewport is null!"); - return; - } - - if (mainWindow->viewport->zoom == ZoomLevel::min()) - { - disabled_widgets |= (1uLL << WIDX_ZOOM_IN); - } - else if (mainWindow->viewport->zoom >= ZoomLevel::max()) - { - disabled_widgets |= (1uLL << WIDX_ZOOM_OUT); - } - else - { - disabled_widgets &= ~((1uLL << WIDX_ZOOM_IN) | (1uLL << WIDX_ZOOM_OUT)); - } + gDropdownDefaultIndex = DDIDX_CHEATS; } - void OnDraw(DrawPixelInfo& dpi) override - { - const auto& gameState = GetGameState(); - int32_t imgId; - - WindowDrawWidgets(*this, dpi); - - ScreenCoordsXY screenPos{}; - // Draw staff button image (setting masks to the staff colours) - if (widgets[WIDX_STAFF].type != WindowWidgetType::Empty) - { - screenPos = { windowPos.x + widgets[WIDX_STAFF].left, windowPos.y + widgets[WIDX_STAFF].top }; - imgId = SPR_TOOLBAR_STAFF; - if (WidgetIsPressed(*this, WIDX_STAFF)) - imgId++; - GfxDrawSprite(dpi, ImageId(imgId, gameState.StaffHandymanColour, gameState.StaffMechanicColour), screenPos); - } - - // Draw fast forward button - if (widgets[WIDX_FASTFORWARD].type != WindowWidgetType::Empty) - { - screenPos = { windowPos.x + widgets[WIDX_FASTFORWARD].left + 0, windowPos.y + widgets[WIDX_FASTFORWARD].top + 0 }; - if (WidgetIsPressed(*this, WIDX_FASTFORWARD)) - screenPos.y++; - GfxDrawSprite(dpi, ImageId(SPR_G2_FASTFORWARD), screenPos + ScreenCoordsXY{ 6, 3 }); - - for (int32_t i = 0; i < gGameSpeed && gGameSpeed <= 4; i++) - { - GfxDrawSprite(dpi, ImageId(SPR_G2_SPEED_ARROW), screenPos + ScreenCoordsXY{ 5 + i * 5, 15 }); - } - for (int32_t i = 0; i < 3 && i < gGameSpeed - 4 && gGameSpeed >= 5; i++) - { - GfxDrawSprite(dpi, ImageId(SPR_G2_HYPER_ARROW), screenPos + ScreenCoordsXY{ 5 + i * 6, 15 }); - } - } - - // Draw cheats button - if (widgets[WIDX_CHEATS].type != WindowWidgetType::Empty) - { - screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_CHEATS].left - 1, widgets[WIDX_CHEATS].top - 1 }; - if (WidgetIsPressed(*this, WIDX_CHEATS)) - screenPos.y++; - GfxDrawSprite(dpi, ImageId(SPR_G2_SANDBOX), screenPos); - - // Draw an overlay if clearance checks are disabled - if (GetGameState().Cheats.DisableClearanceChecks) - { - auto colour = static_cast(EnumValue(COLOUR_DARK_ORANGE) | EnumValue(COLOUR_FLAG_OUTLINE)); - DrawTextBasic( - dpi, screenPos + ScreenCoordsXY{ 26, 2 }, STR_OVERLAY_CLEARANCE_CHECKS_DISABLED, {}, - { colour, TextAlignment::RIGHT }); - } - } - - // Draw chat button - if (widgets[WIDX_CHAT].type != WindowWidgetType::Empty) - { - screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_CHAT].left, widgets[WIDX_CHAT].top - 2 }; - if (WidgetIsPressed(*this, WIDX_CHAT)) - screenPos.y++; - GfxDrawSprite(dpi, ImageId(SPR_G2_CHAT), screenPos); - } - - // Draw debug button - if (widgets[WIDX_DEBUG].type != WindowWidgetType::Empty) - { - screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_DEBUG].left, widgets[WIDX_DEBUG].top - 1 }; - if (WidgetIsPressed(*this, WIDX_DEBUG)) - screenPos.y++; - GfxDrawSprite(dpi, ImageId(SPR_TAB_GEARS_0), screenPos); - } - - // Draw research button - if (widgets[WIDX_RESEARCH].type != WindowWidgetType::Empty) - { - screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_RESEARCH].left - 1, widgets[WIDX_RESEARCH].top }; - if (WidgetIsPressed(*this, WIDX_RESEARCH)) - screenPos.y++; - GfxDrawSprite(dpi, ImageId(SPR_TAB_FINANCES_RESEARCH_0), screenPos); - } - - // Draw finances button - if (widgets[WIDX_FINANCES].type != WindowWidgetType::Empty) - { - screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_FINANCES].left + 3, widgets[WIDX_FINANCES].top + 1 }; - if (WidgetIsPressed(*this, WIDX_FINANCES)) - screenPos.y++; - GfxDrawSprite(dpi, ImageId(SPR_FINANCE), screenPos); - } - - // Draw news button - if (widgets[WIDX_NEWS].type != WindowWidgetType::Empty) - { - screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_NEWS].left + 3, widgets[WIDX_NEWS].top + 0 }; - if (WidgetIsPressed(*this, WIDX_NEWS)) - screenPos.y++; - GfxDrawSprite(dpi, ImageId(SPR_G2_TAB_NEWS), screenPos); - } - - // Draw network button - if (widgets[WIDX_NETWORK].type != WindowWidgetType::Empty) - { - screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_NETWORK].left + 3, widgets[WIDX_NETWORK].top + 0 }; - if (WidgetIsPressed(*this, WIDX_NETWORK)) - screenPos.y++; - - // Draw (de)sync icon. - imgId = (NetworkIsDesynchronised() ? SPR_G2_MULTIPLAYER_DESYNC : SPR_G2_MULTIPLAYER_SYNC); - GfxDrawSprite(dpi, ImageId(imgId), screenPos + ScreenCoordsXY{ 3, 11 }); - - // Draw number of players. - auto ft = Formatter(); - ft.Add(NetworkGetNumVisiblePlayers()); - auto colour = static_cast(EnumValue(COLOUR_WHITE) | EnumValue(COLOUR_FLAG_OUTLINE)); - DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ 23, 1 }, STR_COMMA16, ft, { colour, TextAlignment::RIGHT }); - } - } -}; - -static void ScenarioSelectCallback(const utf8* path) -{ - WindowCloseByClass(WindowClass::EditorObjectSelection); - GameNotifyMapChange(); - GetContext()->LoadParkFromFile(path, false, true); - GameLoadScripts(); - GameNotifyMapChanged(); -} - -/** - * Creates the main game top toolbar window. - * rct2: 0x0066B485 (part of 0x0066B3E8) - */ -WindowBase* WindowTopToolbarOpen() -{ - TopToolbar* window = WindowCreate( - WindowClass::TopToolbar, ScreenCoordsXY(0, 0), ContextGetWidth(), TOP_TOOLBAR_HEIGHT + 1, - WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); - - window->widgets = _topToolbarWidgets; - - WindowInitScrollWidgets(*window); - - return window; -} - -/** - * - * rct2: 0x0066D104 - */ -bool LandToolIsActive() -{ - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return false; - if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar) - return false; - if (gCurrentToolWidget.widget_index != WIDX_LAND) - return false; - return true; -} - -/** - * - * rct2: 0x0066D125 - */ -bool ClearSceneryToolIsActive() -{ - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return false; - if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar) - return false; - if (gCurrentToolWidget.widget_index != WIDX_CLEAR_SCENERY) - return false; - return true; -} - -/** - * - * rct2: 0x0066D125 - */ -bool WaterToolIsActive() -{ - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return false; - if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar) - return false; - if (gCurrentToolWidget.widget_index != WIDX_WATER) - return false; - return true; -} - -void TopToolbar::InitViewMenu(Widget& widget) -{ - using namespace Dropdown; - constexpr ItemExt items[] = { - ToggleOption(DDIDX_UNDERGROUND_INSIDE, STR_UNDERGROUND_VIEW), - ToggleOption(DDIDX_TRANSPARENT_WATER, STR_VIEWPORT_TRANSPARENT_WATER), - ToggleOption(DDIDX_HIDE_BASE, STR_REMOVE_BASE_LAND), - ToggleOption(DDIDX_HIDE_VERTICAL, STR_REMOVE_VERTICAL_FACES), - Separator(), - ToggleOption(DDIDX_HIDE_RIDES, STR_SEE_THROUGH_RIDES), - ToggleOption(DDIDX_HIDE_VEHICLES, STR_SEE_THROUGH_VEHICLES), - ToggleOption(DDIDX_HIDE_VEGETATION, STR_SEE_THROUGH_VEGETATION), - ToggleOption(DDIDX_HIDE_SCENERY, STR_SEE_THROUGH_SCENERY), - ToggleOption(DDIDX_HIDE_PATHS, STR_SEE_THROUGH_PATHS), - ToggleOption(DDIDX_HIDE_SUPPORTS, STR_SEE_THROUGH_SUPPORTS), - ToggleOption(DDIDX_HIDE_GUESTS, STR_SEE_THROUGH_GUESTS), - ToggleOption(DDIDX_HIDE_STAFF, STR_SEE_THROUGH_STAFF), - Separator(), - ToggleOption(DDIDX_LAND_HEIGHTS, STR_HEIGHT_MARKS_ON_LAND), - ToggleOption(DDIDX_TRACK_HEIGHTS, STR_HEIGHT_MARKS_ON_RIDE_TRACKS), - ToggleOption(DDIDX_PATH_HEIGHTS, STR_HEIGHT_MARKS_ON_PATHS), - Separator(), - ToggleOption(DDIDX_VIEW_CLIPPING, STR_VIEW_CLIPPING_MENU), - ToggleOption(DDIDX_HIGHLIGHT_PATH_ISSUES, STR_HIGHLIGHT_PATH_ISSUES_MENU), - Separator(), - ToggleOption(DDIDX_TRANSPARENCY, STR_TRANSPARENCY_OPTIONS), - }; - - static_assert(ItemIDsMatchIndices(items)); - - SetItems(items); - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, - TOP_TOOLBAR_VIEW_MENU_COUNT); - - // Set checkmarks - auto* mainViewport = WindowGetMain()->viewport; - if (mainViewport->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE) - Dropdown::SetChecked(DDIDX_UNDERGROUND_INSIDE, true); - if (gConfigGeneral.TransparentWater) - Dropdown::SetChecked(DDIDX_TRANSPARENT_WATER, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_BASE) - Dropdown::SetChecked(DDIDX_HIDE_BASE, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VERTICAL) - Dropdown::SetChecked(DDIDX_HIDE_VERTICAL, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_RIDES) - Dropdown::SetChecked(DDIDX_HIDE_RIDES, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VEHICLES) - Dropdown::SetChecked(DDIDX_HIDE_VEHICLES, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VEGETATION) - Dropdown::SetChecked(DDIDX_HIDE_VEGETATION, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_SCENERY) - Dropdown::SetChecked(DDIDX_HIDE_SCENERY, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_PATHS) - Dropdown::SetChecked(DDIDX_HIDE_PATHS, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_SUPPORTS) - Dropdown::SetChecked(DDIDX_HIDE_SUPPORTS, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_GUESTS) - Dropdown::SetChecked(DDIDX_HIDE_GUESTS, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIDE_STAFF) - Dropdown::SetChecked(DDIDX_HIDE_STAFF, true); - if (mainViewport->flags & VIEWPORT_FLAG_LAND_HEIGHTS) - Dropdown::SetChecked(DDIDX_LAND_HEIGHTS, true); - if (mainViewport->flags & VIEWPORT_FLAG_TRACK_HEIGHTS) - Dropdown::SetChecked(DDIDX_TRACK_HEIGHTS, true); - if (mainViewport->flags & VIEWPORT_FLAG_PATH_HEIGHTS) - Dropdown::SetChecked(DDIDX_PATH_HEIGHTS, true); - if (mainViewport->flags & VIEWPORT_FLAG_CLIP_VIEW) - Dropdown::SetChecked(DDIDX_VIEW_CLIPPING, true); - if (mainViewport->flags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES) - Dropdown::SetChecked(DDIDX_HIGHLIGHT_PATH_ISSUES, true); - - gDropdownDefaultIndex = DDIDX_UNDERGROUND_INSIDE; - - // Opaque water relies on RCT1 sprites. - if (!IsCsgLoaded()) - { - Dropdown::SetDisabled(DDIDX_TRANSPARENT_WATER, true); - } -} - -void TopToolbar::ViewMenuDropdown(int16_t dropdownIndex) -{ - auto* w = WindowGetMain(); - if (w != nullptr) + void TopToolbar::CheatsMenuDropdown(int16_t dropdownIndex) { switch (dropdownIndex) { - case DDIDX_UNDERGROUND_INSIDE: - w->viewport->flags ^= VIEWPORT_FLAG_UNDERGROUND_INSIDE; + case DDIDX_CHEATS: + ContextOpenWindow(WindowClass::Cheats); break; - case DDIDX_TRANSPARENT_WATER: - gConfigGeneral.TransparentWater ^= 1; - ConfigSaveDefault(); + case DDIDX_TILE_INSPECTOR: + ContextOpenWindow(WindowClass::TileInspector); break; - case DDIDX_HIDE_BASE: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_BASE; + case DDIDX_OBJECT_SELECTION: + WindowCloseAll(); + ContextOpenWindow(WindowClass::EditorObjectSelection); break; - case DDIDX_HIDE_VERTICAL: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VERTICAL; + case DDIDX_INVENTIONS_LIST: + ContextOpenWindow(WindowClass::EditorInventionList); break; - case DDIDX_HIDE_RIDES: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_RIDES; + case DDIDX_SCENARIO_OPTIONS: + ContextOpenWindow(WindowClass::EditorScenarioOptions); break; - case DDIDX_HIDE_VEHICLES: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VEHICLES; + case DDIDX_OBJECTIVE_OPTIONS: + ContextOpenWindow(WindowClass::EditorObjectiveOptions); break; - case DDIDX_HIDE_VEGETATION: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VEGETATION; + case DDIDX_ENABLE_SANDBOX_MODE: + CheatsSet(CheatType::SandboxMode, !GetGameState().Cheats.SandboxMode); break; - case DDIDX_HIDE_SCENERY: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_SCENERY; + case DDIDX_DISABLE_CLEARANCE_CHECKS: + CheatsSet(CheatType::DisableClearanceChecks, !GetGameState().Cheats.DisableClearanceChecks); break; - case DDIDX_HIDE_PATHS: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_PATHS; + case DDIDX_DISABLE_SUPPORT_LIMITS: + CheatsSet(CheatType::DisableSupportLimits, !GetGameState().Cheats.DisableSupportLimits); break; - case DDIDX_HIDE_SUPPORTS: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_SUPPORTS; - break; - case DDIDX_HIDE_GUESTS: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_GUESTS; - break; - case DDIDX_HIDE_STAFF: - w->viewport->flags ^= VIEWPORT_FLAG_HIDE_STAFF; - break; - case DDIDX_LAND_HEIGHTS: - w->viewport->flags ^= VIEWPORT_FLAG_LAND_HEIGHTS; - break; - case DDIDX_TRACK_HEIGHTS: - w->viewport->flags ^= VIEWPORT_FLAG_TRACK_HEIGHTS; - break; - case DDIDX_PATH_HEIGHTS: - w->viewport->flags ^= VIEWPORT_FLAG_PATH_HEIGHTS; - break; - case DDIDX_VIEW_CLIPPING: - if (WindowFindByClass(WindowClass::ViewClipping) == nullptr) - { - ContextOpenWindow(WindowClass::ViewClipping); - } - else - { - // If window is already open, toggle the view clipping on/off - w->viewport->flags ^= VIEWPORT_FLAG_CLIP_VIEW; - } - break; - case DDIDX_HIGHLIGHT_PATH_ISSUES: - w->viewport->flags ^= VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES; - break; - case DDIDX_TRANSPARENCY: - ContextOpenWindow(WindowClass::Transparency); - break; - default: - return; } - w->Invalidate(); - } -} - -void TopToolbar::InitMapMenu(Widget& widget) -{ - auto i = 0; - gDropdownItems[i++].Format = STR_SHORTCUT_SHOW_MAP; - gDropdownItems[i++].Format = STR_EXTRA_VIEWPORT; - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor) - { - gDropdownItems[i++].Format = STR_MAPGEN_WINDOW_TITLE; } -#ifdef ENABLE_SCRIPTING - const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; - if (!customMenuItems.empty()) + void TopToolbar::InitDebugMenu(Widget& widget) { - gDropdownItems[i++].Format = STR_EMPTY; - for (const auto& item : customMenuItems) + gDropdownItems[DDIDX_CONSOLE].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIDX_CONSOLE].Args = STR_DEBUG_DROPDOWN_CONSOLE; + gDropdownItems[DDIDX_DEBUG_PAINT].Format = STR_TOGGLE_OPTION; + gDropdownItems[DDIDX_DEBUG_PAINT].Args = STR_DEBUG_DROPDOWN_DEBUG_PAINT; + + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, + Dropdown::Flag::StayOpen, TOP_TOOLBAR_DEBUG_COUNT); + + Dropdown::SetChecked(DDIDX_DEBUG_PAINT, WindowFindByClass(WindowClass::DebugPaint) != nullptr); + } + + void TopToolbar::DebugMenuDropdown(int16_t dropdownIndex) + { + auto* w = WindowGetMain(); + if (w != nullptr) { - if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Standard) + switch (dropdownIndex) { - gDropdownItems[i].Format = STR_STRING; - auto sz = item.Text.c_str(); - std::memcpy(&gDropdownItems[i].Args, &sz, sizeof(const char*)); - i++; - } - } - } -#endif - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, i); - gDropdownDefaultIndex = DDIDX_SHOW_MAP; -} - -void TopToolbar::MapMenuDropdown(int16_t dropdownIndex) -{ - int32_t customStartIndex = 3; - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor) - { - customStartIndex++; - } - - if (dropdownIndex < customStartIndex) - { - switch (dropdownIndex) - { - case 0: - ContextOpenWindow(WindowClass::Map); - break; - case 1: - ContextOpenWindow(WindowClass::Viewport); - break; - case 2: - ContextOpenWindow(WindowClass::Mapgen); - break; - } - } - else - { -#ifdef ENABLE_SCRIPTING - const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems; - auto customIndex = static_cast(dropdownIndex - customStartIndex); - size_t i = 0; - for (const auto& item : customMenuItems) - { - if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Standard) - { - if (i == customIndex) + case DDIDX_CONSOLE: { - item.Invoke(); + auto& console = GetInGameConsole(); + console.Open(); break; } - i++; + case DDIDX_DEBUG_PAINT: + if (WindowFindByClass(WindowClass::DebugPaint) == nullptr) + { + ContextOpenWindow(WindowClass::DebugPaint); + } + else + { + WindowCloseByClass(WindowClass::DebugPaint); + } + break; } } -#endif - } -} - -void TopToolbar::InitFastforwardMenu(Widget& widget) -{ - int32_t num_items = 4; - gDropdownItems[0].Format = STR_TOGGLE_OPTION; - gDropdownItems[1].Format = STR_TOGGLE_OPTION; - gDropdownItems[2].Format = STR_TOGGLE_OPTION; - gDropdownItems[3].Format = STR_TOGGLE_OPTION; - if (gConfigGeneral.DebuggingTools) - { - gDropdownItems[4].Format = STR_EMPTY; - gDropdownItems[5].Format = STR_TOGGLE_OPTION; - gDropdownItems[5].Args = STR_SPEED_HYPER; - num_items = 6; } - gDropdownItems[0].Args = STR_SPEED_NORMAL; - gDropdownItems[1].Args = STR_SPEED_QUICK; - gDropdownItems[2].Args = STR_SPEED_FAST; - gDropdownItems[3].Args = STR_SPEED_TURBO; - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0, num_items); - - // Set checkmarks - if (gGameSpeed <= 4) + void TopToolbar::InitNetworkMenu(Widget& widget) { - Dropdown::SetChecked(gGameSpeed - 1, true); - } - if (gGameSpeed == 8) - { - Dropdown::SetChecked(5, true); + gDropdownItems[DDIDX_MULTIPLAYER].Format = STR_MULTIPLAYER; + gDropdownItems[DDIDX_MULTIPLAYER_RECONNECT].Format = STR_MULTIPLAYER_RECONNECT; + + WindowDropdownShowText( + { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0, + TOP_TOOLBAR_NETWORK_COUNT); + + Dropdown::SetDisabled(DDIDX_MULTIPLAYER_RECONNECT, !NetworkIsDesynchronised()); + + gDropdownDefaultIndex = DDIDX_MULTIPLAYER; } - if (gConfigGeneral.DebuggingTools) + void TopToolbar::NetworkMenuDropdown(int16_t dropdownIndex) { - gDropdownDefaultIndex = (gGameSpeed == 8 ? 0 : gGameSpeed); - } - else - { - gDropdownDefaultIndex = (gGameSpeed >= 4 ? 0 : gGameSpeed); - } - if (gDropdownDefaultIndex == 4) - { - gDropdownDefaultIndex = 5; - } -} - -void TopToolbar::FastforwardMenuDropdown(int16_t dropdownIndex) -{ - auto* w = WindowGetMain(); - if (w != nullptr) - { - if (dropdownIndex >= 0 && dropdownIndex <= 5) + auto* w = WindowGetMain(); + if (w != nullptr) { - auto newSpeed = dropdownIndex + 1; - if (newSpeed >= 5) - newSpeed = 8; - - auto setSpeedAction = GameSetSpeedAction(newSpeed); - GameActions::Execute(&setSpeedAction); - } - } -} - -void TopToolbar::InitRotateMenu(Widget& widget) -{ - gDropdownItems[0].Format = STR_ROTATE_CLOCKWISE; - gDropdownItems[1].Format = STR_ROTATE_ANTI_CLOCKWISE; - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, 2); - - gDropdownDefaultIndex = DDIDX_ROTATE_CLOCKWISE; -} - -void TopToolbar::RotateMenuDropdown(int16_t dropdownIndex) -{ - if (dropdownIndex == 0) - { - ViewportRotateAll(1); - } - else if (dropdownIndex == 1) - { - ViewportRotateAll(-1); - } -} - -void TopToolbar::InitCheatsMenu(Widget& widget) -{ - using namespace Dropdown; - - constexpr ItemExt items[] = { - ToggleOption(DDIDX_CHEATS, STR_CHEAT_TITLE), - ToggleOption(DDIDX_TILE_INSPECTOR, STR_DEBUG_DROPDOWN_TILE_INSPECTOR), - ToggleOption(DDIDX_OBJECT_SELECTION, STR_DEBUG_DROPDOWN_OBJECT_SELECTION), - ToggleOption(DDIDX_INVENTIONS_LIST, STR_DEBUG_DROPDOWN_INVENTIONS_LIST), - ToggleOption(DDIDX_SCENARIO_OPTIONS, STR_DEBUG_DROPDOWN_SCENARIO_OPTIONS), - ToggleOption(DDIDX_OBJECTIVE_OPTIONS, STR_CHEATS_MENU_OBJECTIVE_OPTIONS), - Separator(), - ToggleOption(DDIDX_ENABLE_SANDBOX_MODE, STR_ENABLE_SANDBOX_MODE), - ToggleOption(DDIDX_DISABLE_CLEARANCE_CHECKS, STR_DISABLE_CLEARANCE_CHECKS), - ToggleOption(DDIDX_DISABLE_SUPPORT_LIMITS, STR_DISABLE_SUPPORT_LIMITS), - }; - static_assert(ItemIDsMatchIndices(items)); - - SetItems(items); - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0, - TOP_TOOLBAR_CHEATS_COUNT); - - // Disable items that are not yet available in multiplayer - if (NetworkGetMode() != NETWORK_MODE_NONE) - { - Dropdown::SetDisabled(DDIDX_OBJECT_SELECTION, true); - Dropdown::SetDisabled(DDIDX_INVENTIONS_LIST, true); - Dropdown::SetDisabled(DDIDX_OBJECTIVE_OPTIONS, true); - } - - if (gScreenFlags & SCREEN_FLAGS_EDITOR) - { - Dropdown::SetDisabled(DDIDX_OBJECT_SELECTION, true); - Dropdown::SetDisabled(DDIDX_INVENTIONS_LIST, true); - Dropdown::SetDisabled(DDIDX_SCENARIO_OPTIONS, true); - Dropdown::SetDisabled(DDIDX_OBJECTIVE_OPTIONS, true); - Dropdown::SetDisabled(DDIDX_ENABLE_SANDBOX_MODE, true); - } - - if (GetGameState().Cheats.SandboxMode) - { - Dropdown::SetChecked(DDIDX_ENABLE_SANDBOX_MODE, true); - } - if (GetGameState().Cheats.DisableClearanceChecks) - { - Dropdown::SetChecked(DDIDX_DISABLE_CLEARANCE_CHECKS, true); - } - if (GetGameState().Cheats.DisableSupportLimits) - { - Dropdown::SetChecked(DDIDX_DISABLE_SUPPORT_LIMITS, true); - } - - gDropdownDefaultIndex = DDIDX_CHEATS; -} - -void TopToolbar::CheatsMenuDropdown(int16_t dropdownIndex) -{ - switch (dropdownIndex) - { - case DDIDX_CHEATS: - ContextOpenWindow(WindowClass::Cheats); - break; - case DDIDX_TILE_INSPECTOR: - ContextOpenWindow(WindowClass::TileInspector); - break; - case DDIDX_OBJECT_SELECTION: - WindowCloseAll(); - ContextOpenWindow(WindowClass::EditorObjectSelection); - break; - case DDIDX_INVENTIONS_LIST: - ContextOpenWindow(WindowClass::EditorInventionList); - break; - case DDIDX_SCENARIO_OPTIONS: - ContextOpenWindow(WindowClass::EditorScenarioOptions); - break; - case DDIDX_OBJECTIVE_OPTIONS: - ContextOpenWindow(WindowClass::EditorObjectiveOptions); - break; - case DDIDX_ENABLE_SANDBOX_MODE: - CheatsSet(CheatType::SandboxMode, !GetGameState().Cheats.SandboxMode); - break; - case DDIDX_DISABLE_CLEARANCE_CHECKS: - CheatsSet(CheatType::DisableClearanceChecks, !GetGameState().Cheats.DisableClearanceChecks); - break; - case DDIDX_DISABLE_SUPPORT_LIMITS: - CheatsSet(CheatType::DisableSupportLimits, !GetGameState().Cheats.DisableSupportLimits); - break; - } -} - -void TopToolbar::InitDebugMenu(Widget& widget) -{ - gDropdownItems[DDIDX_CONSOLE].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIDX_CONSOLE].Args = STR_DEBUG_DROPDOWN_CONSOLE; - gDropdownItems[DDIDX_DEBUG_PAINT].Format = STR_TOGGLE_OPTION; - gDropdownItems[DDIDX_DEBUG_PAINT].Args = STR_DEBUG_DROPDOWN_DEBUG_PAINT; - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, - Dropdown::Flag::StayOpen, TOP_TOOLBAR_DEBUG_COUNT); - - Dropdown::SetChecked(DDIDX_DEBUG_PAINT, WindowFindByClass(WindowClass::DebugPaint) != nullptr); -} - -void TopToolbar::DebugMenuDropdown(int16_t dropdownIndex) -{ - auto* w = WindowGetMain(); - if (w != nullptr) - { - switch (dropdownIndex) - { - case DDIDX_CONSOLE: + switch (dropdownIndex) { - auto& console = GetInGameConsole(); - console.Open(); - break; + case DDIDX_MULTIPLAYER: + ContextOpenWindow(WindowClass::Multiplayer); + break; + case DDIDX_MULTIPLAYER_RECONNECT: + NetworkReconnect(); + break; } - case DDIDX_DEBUG_PAINT: - if (WindowFindByClass(WindowClass::DebugPaint) == nullptr) - { - ContextOpenWindow(WindowClass::DebugPaint); - } - else - { - WindowCloseByClass(WindowClass::DebugPaint); - } - break; } } -} - -void TopToolbar::InitNetworkMenu(Widget& widget) -{ - gDropdownItems[DDIDX_MULTIPLAYER].Format = STR_MULTIPLAYER; - gDropdownItems[DDIDX_MULTIPLAYER_RECONNECT].Format = STR_MULTIPLAYER_RECONNECT; - - WindowDropdownShowText( - { windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0, - TOP_TOOLBAR_NETWORK_COUNT); - - Dropdown::SetDisabled(DDIDX_MULTIPLAYER_RECONNECT, !NetworkIsDesynchronised()); - - gDropdownDefaultIndex = DDIDX_MULTIPLAYER; -} - -void TopToolbar::NetworkMenuDropdown(int16_t dropdownIndex) -{ - auto* w = WindowGetMain(); - if (w != nullptr) - { - switch (dropdownIndex) - { - case DDIDX_MULTIPLAYER: - ContextOpenWindow(WindowClass::Multiplayer); - break; - case DDIDX_MULTIPLAYER_RECONNECT: - NetworkReconnect(); - break; - } - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TrackDesignManage.cpp b/src/openrct2-ui/windows/TrackDesignManage.cpp index d6ddb2561b..57e1650753 100644 --- a/src/openrct2-ui/windows/TrackDesignManage.cpp +++ b/src/openrct2-ui/windows/TrackDesignManage.cpp @@ -17,16 +17,18 @@ #include #include -static constexpr StringId WINDOW_TITLE = STR_STRING; -static constexpr int32_t WH = 44; -static constexpr int32_t WW = 250; -static constexpr int32_t WH_DELETE_PROMPT = 74; -static constexpr int32_t WW_DELETE_PROMPT = 250; -static constexpr int32_t TrackDesignNameMaxLength = 127; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_STRING; + static constexpr int32_t WH = 44; + static constexpr int32_t WW = 250; + static constexpr int32_t WH_DELETE_PROMPT = 74; + static constexpr int32_t WW_DELETE_PROMPT = 250; + static constexpr int32_t TrackDesignNameMaxLength = 127; #pragma region Widgets -// clang-format off + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -51,16 +53,19 @@ static Widget _trackDeletePromptWidgets[] = { MakeWidget({130, 54}, {110, 12}, WindowWidgetType::Button, WindowColour::Primary, STR_CANCEL ), kWidgetsEnd, }; - +// clang-format on #pragma endregion -class TrackDesignManageWindow final : public Window -{ + class TrackDesignManageWindow final : public Window + { private: - TrackDesignFileRef* _trackDesignFileReference{nullptr}; + TrackDesignFileRef* _trackDesignFileReference{ nullptr }; public: - TrackDesignManageWindow(TrackDesignFileRef* tdFileRef) : _trackDesignFileReference(tdFileRef){} + TrackDesignManageWindow(TrackDesignFileRef* tdFileRef) + : _trackDesignFileReference(tdFileRef) + { + } void OnOpen() override; void OnClose() override; @@ -68,170 +73,174 @@ class TrackDesignManageWindow final : public Window void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override; void OnDraw(DrawPixelInfo& dpi) override; - void OnResize() override - { - ResizeFrame(); - } -}; + void OnResize() override + { + ResizeFrame(); + } + }; -class TrackDeletePromptWindow final : public Window -{ + class TrackDeletePromptWindow final : public Window + { private: - TrackDesignFileRef* _trackDesignFileReference{nullptr}; + TrackDesignFileRef* _trackDesignFileReference{ nullptr }; public: - TrackDeletePromptWindow(TrackDesignFileRef* tdFileRef) : _trackDesignFileReference(tdFileRef){} + TrackDeletePromptWindow(TrackDesignFileRef* tdFileRef) + : _trackDesignFileReference(tdFileRef) + { + } void OnOpen() override; void OnMouseUp(WidgetIndex widgetIndex) override; void OnDraw(DrawPixelInfo& dpi) override; - void OnResize() override + void OnResize() override + { + ResizeFrame(); + } + }; + + static void WindowTrackDeletePromptOpen(TrackDesignFileRef* tdFileRef); + + /** + * + * rct2: 0x006D348F + */ + WindowBase* WindowTrackManageOpen(TrackDesignFileRef* tdFileRef) { - ResizeFrame(); + WindowCloseByClass(WindowClass::ManageTrackDesign); + auto trackDesignManageWindow = std::make_unique(tdFileRef); + auto* window = WindowCreate( + std::move(trackDesignManageWindow), WindowClass::ManageTrackDesign, {}, WW, WH, + WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_CENTRE_SCREEN | WF_AUTO_POSITION); + + return window; } -}; -static void WindowTrackDeletePromptOpen(TrackDesignFileRef* tdFileRef); - -/** - * - * rct2: 0x006D348F - */ -WindowBase* WindowTrackManageOpen(TrackDesignFileRef* tdFileRef) -{ - WindowCloseByClass(WindowClass::ManageTrackDesign); - auto trackDesignManageWindow = std::make_unique(tdFileRef); - auto* window = WindowCreate( - std::move(trackDesignManageWindow), WindowClass::ManageTrackDesign, {}, WW, WH, - WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_CENTRE_SCREEN | WF_AUTO_POSITION); - - return window; -} - -void TrackDesignManageWindow::OnOpen() -{ - widgets = _trackManageWidgets; - WindowInitScrollWidgets(*this); - - WindowTrackDesignListSetBeingUpdated(true); -} - -void TrackDesignManageWindow::OnClose() -{ - WindowTrackDesignListSetBeingUpdated(false); -} - -void TrackDesignManageWindow::OnMouseUp(WidgetIndex widgetIndex) -{ - switch (widgetIndex) + void TrackDesignManageWindow::OnOpen() { - case WIDX_CLOSE: + widgets = _trackManageWidgets; + WindowInitScrollWidgets(*this); + + WindowTrackDesignListSetBeingUpdated(true); + } + + void TrackDesignManageWindow::OnClose() + { + WindowTrackDesignListSetBeingUpdated(false); + } + + void TrackDesignManageWindow::OnMouseUp(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_CLOSE: + WindowCloseByClass(WindowClass::TrackDeletePrompt); + Close(); + break; + case WIDX_RENAME: + WindowTextInputRawOpen( + this, widgetIndex, STR_TRACK_DESIGN_RENAME_TITLE, STR_TRACK_DESIGN_RENAME_DESC, {}, + _trackDesignFileReference->name.c_str(), TrackDesignNameMaxLength); + break; + case WIDX_DELETE: + WindowTrackDeletePromptOpen(_trackDesignFileReference); + break; + } + } + + void TrackDesignManageWindow::OnTextInput(WidgetIndex widgetIndex, std::string_view text) + { + if (widgetIndex != WIDX_RENAME) + { + return; + } + else if (text.empty()) + { + ContextShowError(STR_CANT_RENAME_TRACK_DESIGN, STR_NONE, {}); + return; + } + else if (!Platform::IsFilenameValid(text)) + { + ContextShowError(STR_CANT_RENAME_TRACK_DESIGN, STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS, {}); + return; + } + + if (TrackRepositoryRename(_trackDesignFileReference->path, std::string(text))) + { WindowCloseByClass(WindowClass::TrackDeletePrompt); Close(); - break; - case WIDX_RENAME: - WindowTextInputRawOpen( - this, widgetIndex, STR_TRACK_DESIGN_RENAME_TITLE, STR_TRACK_DESIGN_RENAME_DESC, {}, - _trackDesignFileReference->name.c_str(), TrackDesignNameMaxLength); - break; - case WIDX_DELETE: - WindowTrackDeletePromptOpen(_trackDesignFileReference); - break; - } -} - -void TrackDesignManageWindow::OnTextInput(WidgetIndex widgetIndex, std::string_view text) -{ - if (widgetIndex != WIDX_RENAME) - { - return; - } - else if (text.empty()) - { - ContextShowError(STR_CANT_RENAME_TRACK_DESIGN, STR_NONE, {}); - return; - } - else if (!Platform::IsFilenameValid(text)) - { - ContextShowError(STR_CANT_RENAME_TRACK_DESIGN, STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS, {}); - return; + WindowTrackDesignListReloadTracks(); + } + else + { + ContextShowError(STR_CANT_RENAME_TRACK_DESIGN, STR_ANOTHER_FILE_EXISTS_WITH_NAME_OR_FILE_IS_WRITE_PROTECTED, {}); + } } - if (TrackRepositoryRename(_trackDesignFileReference->path, std::string(text))) + void TrackDesignManageWindow::OnDraw(DrawPixelInfo& dpi) + { + Formatter::Common().Add(_trackDesignFileReference->name.c_str()); + DrawWidgets(dpi); + } + + /** + * + * rct2: 0x006D35CD + */ + static void WindowTrackDeletePromptOpen(TrackDesignFileRef* tdFileRef) { WindowCloseByClass(WindowClass::TrackDeletePrompt); - Close(); - WindowTrackDesignListReloadTracks(); + + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + auto trackDeletePromptWindow = std::make_unique(tdFileRef); + WindowCreate( + std::move(trackDeletePromptWindow), WindowClass::TrackDeletePrompt, + ScreenCoordsXY( + std::max(TOP_TOOLBAR_HEIGHT + 1, (screenWidth - WW_DELETE_PROMPT) / 2), (screenHeight - WH_DELETE_PROMPT) / 2), + WW_DELETE_PROMPT, WH_DELETE_PROMPT, WF_STICK_TO_FRONT | WF_TRANSPARENT); } - else + + void TrackDeletePromptWindow::OnOpen() { - ContextShowError(STR_CANT_RENAME_TRACK_DESIGN, STR_ANOTHER_FILE_EXISTS_WITH_NAME_OR_FILE_IS_WRITE_PROTECTED, {}); + widgets = _trackDeletePromptWidgets; + WindowInitScrollWidgets(*this); } -} -void TrackDesignManageWindow::OnDraw(DrawPixelInfo& dpi) -{ - Formatter::Common().Add(_trackDesignFileReference->name.c_str()); - DrawWidgets(dpi); -} - -/** - * - * rct2: 0x006D35CD - */ -static void WindowTrackDeletePromptOpen(TrackDesignFileRef* tdFileRef) -{ - WindowCloseByClass(WindowClass::TrackDeletePrompt); - - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - auto trackDeletePromptWindow = std::make_unique(tdFileRef); - WindowCreate( - std::move(trackDeletePromptWindow), WindowClass::TrackDeletePrompt, ScreenCoordsXY( - std::max(TOP_TOOLBAR_HEIGHT + 1, (screenWidth - WW_DELETE_PROMPT) / 2), (screenHeight - WH_DELETE_PROMPT) / 2), WW_DELETE_PROMPT, WH_DELETE_PROMPT, - WF_STICK_TO_FRONT | WF_TRANSPARENT); -} - -void TrackDeletePromptWindow::OnOpen() -{ - widgets = _trackDeletePromptWidgets; - WindowInitScrollWidgets(*this); -} - -void TrackDeletePromptWindow::OnMouseUp(WidgetIndex widgetIndex) -{ - switch (widgetIndex) + void TrackDeletePromptWindow::OnMouseUp(WidgetIndex widgetIndex) { - case WIDX_CLOSE: - case WIDX_PROMPT_CANCEL: - Close(); - break; - case WIDX_PROMPT_DELETE: - // tdPath has to be saved before window is closed, as that would blank it out. - auto tdPath = _trackDesignFileReference->path; - Close(); - if (TrackRepositoryDelete(tdPath)) - { - WindowCloseByClass(WindowClass::ManageTrackDesign); - WindowTrackDesignListReloadTracks(); - } - else - { - ContextShowError(STR_CANT_DELETE_TRACK_DESIGN, STR_FILE_IS_WRITE_PROTECTED_OR_LOCKED, {}); - } - break; + switch (widgetIndex) + { + case WIDX_CLOSE: + case WIDX_PROMPT_CANCEL: + Close(); + break; + case WIDX_PROMPT_DELETE: + // tdPath has to be saved before window is closed, as that would blank it out. + auto tdPath = _trackDesignFileReference->path; + Close(); + if (TrackRepositoryDelete(tdPath)) + { + WindowCloseByClass(WindowClass::ManageTrackDesign); + WindowTrackDesignListReloadTracks(); + } + else + { + ContextShowError(STR_CANT_DELETE_TRACK_DESIGN, STR_FILE_IS_WRITE_PROTECTED_OR_LOCKED, {}); + } + break; + } } -} -void TrackDeletePromptWindow::OnDraw(DrawPixelInfo& dpi) -{ - DrawWidgets(dpi); - - auto ft = Formatter(); - ft.Add(_trackDesignFileReference->name.c_str()); - DrawTextWrapped( - dpi, { windowPos.x + (WW_DELETE_PROMPT / 2), windowPos.y + ((WH_DELETE_PROMPT / 2) - 9) }, (WW_DELETE_PROMPT - 4), - STR_ARE_YOU_SURE_YOU_WANT_TO_PERMANENTLY_DELETE_TRACK, ft, { TextAlignment::CENTRE }); -} + void TrackDeletePromptWindow::OnDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + auto ft = Formatter(); + ft.Add(_trackDesignFileReference->name.c_str()); + DrawTextWrapped( + dpi, { windowPos.x + (WW_DELETE_PROMPT / 2), windowPos.y + ((WH_DELETE_PROMPT / 2) - 9) }, (WW_DELETE_PROMPT - 4), + STR_ARE_YOU_SURE_YOU_WANT_TO_PERMANENTLY_DELETE_TRACK, ft, { TextAlignment::CENTRE }); + } +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TrackDesignPlace.cpp b/src/openrct2-ui/windows/TrackDesignPlace.cpp index 446b274cfd..75156eb9ca 100644 --- a/src/openrct2-ui/windows/TrackDesignPlace.cpp +++ b/src/openrct2-ui/windows/TrackDesignPlace.cpp @@ -34,22 +34,23 @@ #include #include -using namespace OpenRCT2; using namespace OpenRCT2::TrackMetaData; -static constexpr StringId WINDOW_TITLE = STR_STRING; -static constexpr int32_t WH = 124; -static constexpr int32_t WW = 200; -constexpr int16_t TRACK_MINI_PREVIEW_WIDTH = 168; -constexpr int16_t TRACK_MINI_PREVIEW_HEIGHT = 78; -constexpr uint16_t TRACK_MINI_PREVIEW_SIZE = TRACK_MINI_PREVIEW_WIDTH * TRACK_MINI_PREVIEW_HEIGHT; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_STRING; + static constexpr int32_t WH = 124; + static constexpr int32_t WW = 200; + constexpr int16_t TRACK_MINI_PREVIEW_WIDTH = 168; + constexpr int16_t TRACK_MINI_PREVIEW_HEIGHT = 78; + constexpr uint16_t TRACK_MINI_PREVIEW_SIZE = TRACK_MINI_PREVIEW_WIDTH * TRACK_MINI_PREVIEW_HEIGHT; -static constexpr uint8_t _PaletteIndexColourEntrance = PALETTE_INDEX_20; // White -static constexpr uint8_t _PaletteIndexColourExit = PALETTE_INDEX_10; // Black -static constexpr uint8_t _PaletteIndexColourTrack = PALETTE_INDEX_248; // Grey (dark) -static constexpr uint8_t _PaletteIndexColourStation = PALETTE_INDEX_252; // Grey (light) + static constexpr uint8_t _PaletteIndexColourEntrance = PALETTE_INDEX_20; // White + static constexpr uint8_t _PaletteIndexColourExit = PALETTE_INDEX_10; // Black + static constexpr uint8_t _PaletteIndexColourTrack = PALETTE_INDEX_248; // Grey (dark) + static constexpr uint8_t _PaletteIndexColourStation = PALETTE_INDEX_252; // Grey (light) -// clang-format off + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -71,580 +72,583 @@ static Widget _trackPlaceWidgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -class TrackDesignPlaceWindow final : public Window -{ -public: - void OnOpen() override + class TrackDesignPlaceWindow final : public Window { - widgets = _trackPlaceWidgets; - WindowInitScrollWidgets(*this); - ToolSet(*this, WIDX_PRICE, Tool::Crosshair); - InputSetFlag(INPUT_FLAG_6, true); - WindowPushOthersRight(*this); - ShowGridlines(); - _miniPreview.resize(TRACK_MINI_PREVIEW_SIZE); - _placementCost = kMoney64Undefined; - _placementLoc.SetNull(); - _currentTrackPieceDirection = (2 - GetCurrentRotation()) & 3; - } - - void OnClose() override - { - ClearProvisional(); - ViewportSetVisibility(ViewportVisibility::Default); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - HideGridlines(); - _miniPreview.clear(); - _miniPreview.shrink_to_fit(); - _trackDesign = nullptr; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - case WIDX_ROTATE: + widgets = _trackPlaceWidgets; + WindowInitScrollWidgets(*this); + ToolSet(*this, WIDX_PRICE, Tool::Crosshair); + InputSetFlag(INPUT_FLAG_6, true); + WindowPushOthersRight(*this); + ShowGridlines(); + _miniPreview.resize(TRACK_MINI_PREVIEW_SIZE); + _placementCost = kMoney64Undefined; + _placementLoc.SetNull(); + _currentTrackPieceDirection = (2 - GetCurrentRotation()) & 3; + } + + void OnClose() override + { + ClearProvisional(); + ViewportSetVisibility(ViewportVisibility::Default); + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + HideGridlines(); + _miniPreview.clear(); + _miniPreview.shrink_to_fit(); + _trackDesign = nullptr; + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_ROTATE: + ClearProvisional(); + _currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3; + Invalidate(); + _placementLoc.SetNull(); + DrawMiniPreview(_trackDesign.get()); + break; + case WIDX_MIRROR: + TrackDesignMirror(_trackDesign.get()); + _currentTrackPieceDirection = (0 - _currentTrackPieceDirection) & 3; + Invalidate(); + _placementLoc.SetNull(); + DrawMiniPreview(_trackDesign.get()); + break; + case WIDX_SELECT_DIFFERENT_DESIGN: + Close(); + + auto intent = Intent(WindowClass::TrackDesignList); + intent.PutExtra(INTENT_EXTRA_RIDE_TYPE, _window_track_list_item.Type); + intent.PutExtra(INTENT_EXTRA_RIDE_ENTRY_INDEX, _window_track_list_item.EntryIndex); + ContextOpenIntent(&intent); + break; + } + } + + void OnUpdate() override + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + if (gCurrentToolWidget.window_classification != WindowClass::TrackDesignPlace) + Close(); + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + TrackDesignState tds{}; + int16_t mapZ; + + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + + // Get the tool map position + CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); + if (mapCoords.IsNull()) + { ClearProvisional(); - _currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3; - Invalidate(); - _placementLoc.SetNull(); - DrawMiniPreview(_trackDesign.get()); - break; - case WIDX_MIRROR: - TrackDesignMirror(_trackDesign.get()); - _currentTrackPieceDirection = (0 - _currentTrackPieceDirection) & 3; - Invalidate(); - _placementLoc.SetNull(); - DrawMiniPreview(_trackDesign.get()); - break; - case WIDX_SELECT_DIFFERENT_DESIGN: - Close(); + return; + } - auto intent = Intent(WindowClass::TrackDesignList); - intent.PutExtra(INTENT_EXTRA_RIDE_TYPE, _window_track_list_item.Type); - intent.PutExtra(INTENT_EXTRA_RIDE_ENTRY_INDEX, _window_track_list_item.EntryIndex); - ContextOpenIntent(&intent); - break; + // Check if tool map position has changed since last update + if (mapCoords == _placementLoc) + { + TrackDesignPreviewDrawOutlines( + tds, _trackDesign.get(), RideGetTemporaryForPreview(), { mapCoords, 0, _currentTrackPieceDirection }); + return; + } + + money64 cost = kMoney64Undefined; + + // Get base Z position + mapZ = GetBaseZ(mapCoords); + CoordsXYZD trackLoc = { mapCoords, mapZ, _currentTrackPieceDirection }; + + if (GameIsNotPaused() || GetGameState().Cheats.BuildInPauseMode) + { + ClearProvisional(); + auto res = FindValidTrackDesignPlaceHeight(trackLoc, GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); + + if (res.Error == GameActions::Status::Ok) + { + // Valid location found. Place the ghost at the location. + auto tdAction = TrackDesignAction(trackLoc, *_trackDesign); + tdAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); + tdAction.SetCallback([&](const GameAction*, const GameActions::Result* result) { + if (result->Error == GameActions::Status::Ok) + { + _placementGhostRideId = result->GetData(); + _placementGhostLoc = trackLoc; + _hasPlacementGhost = true; + } + }); + res = GameActions::Execute(&tdAction); + cost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; + } + } + + _placementLoc = trackLoc; + if (cost != _placementCost) + { + _placementCost = cost; + WidgetInvalidate(*this, WIDX_PRICE); + } + + TrackDesignPreviewDrawOutlines(tds, _trackDesign.get(), RideGetTemporaryForPreview(), trackLoc); } - } - void OnUpdate() override - { - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - if (gCurrentToolWidget.window_classification != WindowClass::TrackDesignPlace) - Close(); - } - - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - TrackDesignState tds{}; - int16_t mapZ; - - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - - // Get the tool map position - CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); - if (mapCoords.IsNull()) + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { ClearProvisional(); - return; - } + MapInvalidateMapSelectionTiles(); + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - // Check if tool map position has changed since last update - if (mapCoords == _placementLoc) - { - TrackDesignPreviewDrawOutlines( - tds, _trackDesign.get(), RideGetTemporaryForPreview(), { mapCoords, 0, _currentTrackPieceDirection }); - return; - } + const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); + if (mapCoords.IsNull()) + return; - money64 cost = kMoney64Undefined; - - // Get base Z position - mapZ = GetBaseZ(mapCoords); - CoordsXYZD trackLoc = { mapCoords, mapZ, _currentTrackPieceDirection }; - - if (GameIsNotPaused() || GetGameState().Cheats.BuildInPauseMode) - { - ClearProvisional(); - auto res = FindValidTrackDesignPlaceHeight(trackLoc, GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); + // Try increasing Z until a feasible placement is found + int16_t mapZ = GetBaseZ(mapCoords); + CoordsXYZ trackLoc = { mapCoords, mapZ }; + auto res = FindValidTrackDesignPlaceHeight(trackLoc, 0); if (res.Error == GameActions::Status::Ok) { - // Valid location found. Place the ghost at the location. - auto tdAction = TrackDesignAction(trackLoc, *_trackDesign); - tdAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); + auto tdAction = TrackDesignAction({ trackLoc, _currentTrackPieceDirection }, *_trackDesign); tdAction.SetCallback([&](const GameAction*, const GameActions::Result* result) { if (result->Error == GameActions::Status::Ok) { - _placementGhostRideId = result->GetData(); - _placementGhostLoc = trackLoc; - _hasPlacementGhost = true; + rideId = result->GetData(); + auto getRide = GetRide(rideId); + if (getRide != nullptr) + { + WindowCloseByClass(WindowClass::Error); + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, trackLoc); + + _currentRideIndex = rideId; + if (TrackDesignAreEntranceAndExitPlaced()) + { + auto intent = Intent(WindowClass::Ride); + intent.PutExtra(INTENT_EXTRA_RIDE_ID, rideId.ToUnderlying()); + ContextOpenIntent(&intent); + auto wnd = WindowFindByClass(WindowClass::TrackDesignPlace); + WindowClose(*wnd); + } + else + { + RideInitialiseConstructionWindow(*getRide); + auto wnd = WindowFindByClass(WindowClass::RideConstruction); + wnd->OnMouseUp(WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE); + } + } + } + else + { + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Error, result->Position); } }); - res = GameActions::Execute(&tdAction); - cost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined; + GameActions::Execute(&tdAction); + return; + } + + // Unable to build track + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Error, trackLoc); + + auto windowManager = GetContext()->GetUiContext()->GetWindowManager(); + windowManager->ShowError(res.GetErrorTitle(), res.GetErrorMessage()); + } + + void OnToolAbort(WidgetIndex widgetIndex) override + { + ClearProvisional(); + } + + void OnViewportRotate() override + { + DrawMiniPreview(_trackDesign.get()); + } + + void OnPrepareDraw() override + { + DrawMiniPreview(_trackDesign.get()); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + auto ft = Formatter::Common(); + ft.Add(_trackDesign->name.c_str()); + WindowDrawWidgets(*this, dpi); + + // Draw mini tile preview + DrawPixelInfo clippedDpi; + if (ClipDrawPixelInfo(clippedDpi, dpi, this->windowPos + ScreenCoordsXY{ 4, 18 }, 168, 78)) + { + G1Element g1temp = {}; + g1temp.offset = _miniPreview.data(); + g1temp.width = TRACK_MINI_PREVIEW_WIDTH; + g1temp.height = TRACK_MINI_PREVIEW_HEIGHT; + GfxSetG1Element(SPR_TEMP, &g1temp); + DrawingEngineInvalidateImage(SPR_TEMP); + GfxDrawSprite(clippedDpi, ImageId(SPR_TEMP, NOT_TRANSLUCENT(this->colours[0])), { 0, 0 }); + } + + // Price + if (_placementCost != kMoney64Undefined && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + ft = Formatter(); + ft.Add(_placementCost); + DrawTextBasic(dpi, this->windowPos + ScreenCoordsXY{ 88, 94 }, STR_COST_LABEL, ft, { TextAlignment::CENTRE }); } } - _placementLoc = trackLoc; - if (cost != _placementCost) + void OnResize() override { - _placementCost = cost; - WidgetInvalidate(*this, WIDX_PRICE); + ResizeFrame(); } - TrackDesignPreviewDrawOutlines(tds, _trackDesign.get(), RideGetTemporaryForPreview(), trackLoc); - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - ClearProvisional(); - MapInvalidateMapSelectionTiles(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - - const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords); - if (mapCoords.IsNull()) - return; - - // Try increasing Z until a feasible placement is found - int16_t mapZ = GetBaseZ(mapCoords); - CoordsXYZ trackLoc = { mapCoords, mapZ }; - - auto res = FindValidTrackDesignPlaceHeight(trackLoc, 0); - if (res.Error == GameActions::Status::Ok) + void ClearProvisionalTemporarily() { - auto tdAction = TrackDesignAction({ trackLoc, _currentTrackPieceDirection }, *_trackDesign); - tdAction.SetCallback([&](const GameAction*, const GameActions::Result* result) { - if (result->Error == GameActions::Status::Ok) + if (_hasPlacementGhost) + { + auto provRide = GetRide(_placementGhostRideId); + if (provRide != nullptr) { - rideId = result->GetData(); - auto getRide = GetRide(rideId); - if (getRide != nullptr) - { - WindowCloseByClass(WindowClass::Error); - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, trackLoc); + TrackDesignPreviewRemoveGhosts(_trackDesign.get(), *provRide, _placementGhostLoc); + } + } + } - _currentRideIndex = rideId; - if (TrackDesignAreEntranceAndExitPlaced()) - { - auto intent = Intent(WindowClass::Ride); - intent.PutExtra(INTENT_EXTRA_RIDE_ID, rideId.ToUnderlying()); - ContextOpenIntent(&intent); - auto wnd = WindowFindByClass(WindowClass::TrackDesignPlace); - WindowClose(*wnd); - } - else - { - RideInitialiseConstructionWindow(*getRide); - auto wnd = WindowFindByClass(WindowClass::RideConstruction); - wnd->OnMouseUp(WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE); - } - } + void RestoreProvisional() + { + if (_hasPlacementGhost) + { + auto tdAction = TrackDesignAction({ _placementGhostLoc }, *_trackDesign); + tdAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); + auto res = GameActions::Execute(&tdAction); + if (res.Error != GameActions::Status::Ok) + { + _hasPlacementGhost = false; + } + } + } + + void Init(std::unique_ptr&& trackDesign) + { + _trackDesign = std::move(trackDesign); + } + + void DrawMiniPreview(TrackDesign* td6) + { + ClearMiniPreview(); + + // First pass is used to determine the width and height of the image so it can centre it + CoordsXY min = { 0, 0 }; + CoordsXY max = { 0, 0 }; + for (int32_t pass = 0; pass < 2; pass++) + { + CoordsXY origin = { 0, 0 }; + if (pass == 1) + { + origin.x -= ((max.x + min.x) >> 6) * COORDS_XY_STEP; + origin.y -= ((max.y + min.y) >> 6) * COORDS_XY_STEP; + } + + const auto& rtd = GetRideTypeDescriptor(td6->type); + if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + DrawMiniPreviewMaze(td6, pass, origin, min, max); } else { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Error, result->Position); + DrawMiniPreviewTrack(td6, pass, origin, min, max); } - }); - GameActions::Execute(&tdAction); - return; - } - - // Unable to build track - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Error, trackLoc); - - auto windowManager = GetContext()->GetUiContext()->GetWindowManager(); - windowManager->ShowError(res.GetErrorTitle(), res.GetErrorMessage()); - } - - void OnToolAbort(WidgetIndex widgetIndex) override - { - ClearProvisional(); - } - - void OnViewportRotate() override - { - DrawMiniPreview(_trackDesign.get()); - } - - void OnPrepareDraw() override - { - DrawMiniPreview(_trackDesign.get()); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - auto ft = Formatter::Common(); - ft.Add(_trackDesign->name.c_str()); - WindowDrawWidgets(*this, dpi); - - // Draw mini tile preview - DrawPixelInfo clippedDpi; - if (ClipDrawPixelInfo(clippedDpi, dpi, this->windowPos + ScreenCoordsXY{ 4, 18 }, 168, 78)) - { - G1Element g1temp = {}; - g1temp.offset = _miniPreview.data(); - g1temp.width = TRACK_MINI_PREVIEW_WIDTH; - g1temp.height = TRACK_MINI_PREVIEW_HEIGHT; - GfxSetG1Element(SPR_TEMP, &g1temp); - DrawingEngineInvalidateImage(SPR_TEMP); - GfxDrawSprite(clippedDpi, ImageId(SPR_TEMP, NOT_TRANSLUCENT(this->colours[0])), { 0, 0 }); - } - - // Price - if (_placementCost != kMoney64Undefined && !(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - ft = Formatter(); - ft.Add(_placementCost); - DrawTextBasic(dpi, this->windowPos + ScreenCoordsXY{ 88, 94 }, STR_COST_LABEL, ft, { TextAlignment::CENTRE }); - } - } - - void OnResize() override - { - ResizeFrame(); - } - - void ClearProvisionalTemporarily() - { - if (_hasPlacementGhost) - { - auto provRide = GetRide(_placementGhostRideId); - if (provRide != nullptr) - { - TrackDesignPreviewRemoveGhosts(_trackDesign.get(), *provRide, _placementGhostLoc); } } - } - void RestoreProvisional() - { - if (_hasPlacementGhost) + void ClearMiniPreview() { - auto tdAction = TrackDesignAction({ _placementGhostLoc }, *_trackDesign); - tdAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); - auto res = GameActions::Execute(&tdAction); - if (res.Error != GameActions::Status::Ok) - { - _hasPlacementGhost = false; - } - } - } - - void Init(std::unique_ptr&& trackDesign) - { - _trackDesign = std::move(trackDesign); - } - - void DrawMiniPreview(TrackDesign* td6) - { - ClearMiniPreview(); - - // First pass is used to determine the width and height of the image so it can centre it - CoordsXY min = { 0, 0 }; - CoordsXY max = { 0, 0 }; - for (int32_t pass = 0; pass < 2; pass++) - { - CoordsXY origin = { 0, 0 }; - if (pass == 1) - { - origin.x -= ((max.x + min.x) >> 6) * COORDS_XY_STEP; - origin.y -= ((max.y + min.y) >> 6) * COORDS_XY_STEP; - } - - const auto& rtd = GetRideTypeDescriptor(td6->type); - if (rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) - { - DrawMiniPreviewMaze(td6, pass, origin, min, max); - } - else - { - DrawMiniPreviewTrack(td6, pass, origin, min, max); - } - } - } - - void ClearMiniPreview() - { - // Fill with transparent colour. - std::fill(_miniPreview.begin(), _miniPreview.end(), PALETTE_INDEX_0); - } - -private: - std::unique_ptr _trackDesign; - - CoordsXY _placementLoc; - RideId _placementGhostRideId; - bool _hasPlacementGhost; - money64 _placementCost; - CoordsXYZD _placementGhostLoc; - - std::vector _miniPreview; - - void ClearProvisional() - { - if (_hasPlacementGhost) - { - auto newRide = GetRide(_placementGhostRideId); - if (newRide != nullptr) - { - TrackDesignPreviewRemoveGhosts(_trackDesign.get(), *newRide, _placementGhostLoc); - _hasPlacementGhost = false; - } - } - } - - int32_t GetBaseZ(const CoordsXY& loc) - { - auto surfaceElement = MapGetSurfaceElementAt(loc); - if (surfaceElement == nullptr) - return 0; - - auto z = surfaceElement->GetBaseZ(); - - // Increase Z above slope - if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) - { - z += 16; - - // Increase Z above double slope - if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - z += 16; + // Fill with transparent colour. + std::fill(_miniPreview.begin(), _miniPreview.end(), PALETTE_INDEX_0); } - // Increase Z above water - if (surfaceElement->GetWaterHeight() > 0) - z = std::max(z, surfaceElement->GetWaterHeight()); + private: + std::unique_ptr _trackDesign; - return z - + TrackDesignGetZPlacement( - _trackDesign.get(), RideGetTemporaryForPreview(), { loc, z, _currentTrackPieceDirection }); - } + CoordsXY _placementLoc; + RideId _placementGhostRideId; + bool _hasPlacementGhost; + money64 _placementCost; + CoordsXYZD _placementGhostLoc; - void DrawMiniPreviewEntrances( - const TrackDesign& td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max, Direction rotation) - { - for (const auto& entrance : td6.entrance_elements) + std::vector _miniPreview; + + void ClearProvisional() { - auto rotatedAndOffsetEntrance = origin + entrance.Location.ToCoordsXY().Rotate(rotation); - - if (pass == 0) + if (_hasPlacementGhost) { - min.x = std::min(min.x, rotatedAndOffsetEntrance.x); - max.x = std::max(max.x, rotatedAndOffsetEntrance.x); - min.y = std::min(min.y, rotatedAndOffsetEntrance.y); - max.y = std::max(max.y, rotatedAndOffsetEntrance.y); - } - else - { - auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedAndOffsetEntrance); - if (DrawMiniPreviewIsPixelInBounds(pixelPosition)) + auto newRide = GetRide(_placementGhostRideId); + if (newRide != nullptr) { - uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition); - uint8_t colour = entrance.IsExit ? _PaletteIndexColourExit : _PaletteIndexColourEntrance; - for (int32_t i = 0; i < 4; i++) - { - pixel[338 + i] = colour; // x + 2, y + 2 - pixel[168 + i] = colour; // y + 1 - pixel[2 + i] = colour; // x + 2 - pixel[172 + i] = colour; // x + 4, y + 1 - } + TrackDesignPreviewRemoveGhosts(_trackDesign.get(), *newRide, _placementGhostLoc); + _hasPlacementGhost = false; } } } - } - void DrawMiniPreviewTrack(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max) - { - const uint8_t rotation = (_currentTrackPieceDirection + GetCurrentRotation()) & 3; - - CoordsXY curTrackStart = origin; - uint8_t curTrackRotation = rotation; - for (const auto& trackElement : td6->track_elements) + int32_t GetBaseZ(const CoordsXY& loc) { - // Follow a single track piece shape - const auto& ted = GetTrackElementDescriptor(trackElement.Type); - const PreviewTrack* trackBlock = ted.Block; - while (trackBlock->index != 255) + auto surfaceElement = MapGetSurfaceElementAt(loc); + if (surfaceElement == nullptr) + return 0; + + auto z = surfaceElement->GetBaseZ(); + + // Increase Z above slope + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { - auto rotatedAndOffsetTrackBlock = curTrackStart - + CoordsXY{ trackBlock->x, trackBlock->y }.Rotate(curTrackRotation); + z += 16; + + // Increase Z above double slope + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + z += 16; + } + + // Increase Z above water + if (surfaceElement->GetWaterHeight() > 0) + z = std::max(z, surfaceElement->GetWaterHeight()); + + return z + + TrackDesignGetZPlacement( + _trackDesign.get(), RideGetTemporaryForPreview(), { loc, z, _currentTrackPieceDirection }); + } + + void DrawMiniPreviewEntrances( + const TrackDesign& td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max, Direction rotation) + { + for (const auto& entrance : td6.entrance_elements) + { + auto rotatedAndOffsetEntrance = origin + entrance.Location.ToCoordsXY().Rotate(rotation); if (pass == 0) { - min.x = std::min(min.x, rotatedAndOffsetTrackBlock.x); - max.x = std::max(max.x, rotatedAndOffsetTrackBlock.x); - min.y = std::min(min.y, rotatedAndOffsetTrackBlock.y); - max.y = std::max(max.y, rotatedAndOffsetTrackBlock.y); + min.x = std::min(min.x, rotatedAndOffsetEntrance.x); + max.x = std::max(max.x, rotatedAndOffsetEntrance.x); + min.y = std::min(min.y, rotatedAndOffsetEntrance.y); + max.y = std::max(max.y, rotatedAndOffsetEntrance.y); } else { - auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedAndOffsetTrackBlock); + auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedAndOffsetEntrance); + if (DrawMiniPreviewIsPixelInBounds(pixelPosition)) + { + uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition); + uint8_t colour = entrance.IsExit ? _PaletteIndexColourExit : _PaletteIndexColourEntrance; + for (int32_t i = 0; i < 4; i++) + { + pixel[338 + i] = colour; // x + 2, y + 2 + pixel[168 + i] = colour; // y + 1 + pixel[2 + i] = colour; // x + 2 + pixel[172 + i] = colour; // x + 4, y + 1 + } + } + } + } + } + + void DrawMiniPreviewTrack(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max) + { + const uint8_t rotation = (_currentTrackPieceDirection + GetCurrentRotation()) & 3; + + CoordsXY curTrackStart = origin; + uint8_t curTrackRotation = rotation; + for (const auto& trackElement : td6->track_elements) + { + // Follow a single track piece shape + const auto& ted = GetTrackElementDescriptor(trackElement.Type); + const PreviewTrack* trackBlock = ted.Block; + while (trackBlock->index != 255) + { + auto rotatedAndOffsetTrackBlock = curTrackStart + + CoordsXY{ trackBlock->x, trackBlock->y }.Rotate(curTrackRotation); + + if (pass == 0) + { + min.x = std::min(min.x, rotatedAndOffsetTrackBlock.x); + max.x = std::max(max.x, rotatedAndOffsetTrackBlock.x); + min.y = std::min(min.y, rotatedAndOffsetTrackBlock.y); + max.y = std::max(max.y, rotatedAndOffsetTrackBlock.y); + } + else + { + auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedAndOffsetTrackBlock); + if (DrawMiniPreviewIsPixelInBounds(pixelPosition)) + { + uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition); + + auto bits = trackBlock->var_08.Rotate(curTrackRotation & 3).GetBaseQuarterOccupied(); + + // Station track is a lighter colour + uint8_t colour = (ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN) + ? _PaletteIndexColourStation + : _PaletteIndexColourTrack; + + for (int32_t i = 0; i < 4; i++) + { + if (bits & 1) + pixel[338 + i] = colour; // x + 2, y + 2 + if (bits & 2) + pixel[168 + i] = colour; // y + 1 + if (bits & 4) + pixel[2 + i] = colour; // x + 2 + if (bits & 8) + pixel[172 + i] = colour; // x + 4, y + 1 + } + } + } + trackBlock++; + } + + // Change rotation and next position based on track curvature + curTrackRotation &= 3; + + const TrackCoordinates* track_coordinate = &ted.Coordinates; + + curTrackStart += CoordsXY{ track_coordinate->x, track_coordinate->y }.Rotate(curTrackRotation); + curTrackRotation += track_coordinate->rotation_end - track_coordinate->rotation_begin; + curTrackRotation &= 3; + if (track_coordinate->rotation_end & 4) + { + curTrackRotation |= 4; + } + if (!(curTrackRotation & 4)) + { + curTrackStart += CoordsDirectionDelta[curTrackRotation]; + } + } + + DrawMiniPreviewEntrances(*td6, pass, origin, min, max, rotation); + } + + void DrawMiniPreviewMaze(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max) + { + uint8_t rotation = (_currentTrackPieceDirection + GetCurrentRotation()) & 3; + for (const auto& mazeElement : td6->maze_elements) + { + auto rotatedMazeCoords = origin + TileCoordsXY{ mazeElement.x, mazeElement.y }.ToCoordsXY().Rotate(rotation); + + if (pass == 0) + { + min.x = std::min(min.x, rotatedMazeCoords.x); + max.x = std::max(max.x, rotatedMazeCoords.x); + min.y = std::min(min.y, rotatedMazeCoords.y); + max.y = std::max(max.y, rotatedMazeCoords.y); + } + else + { + auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedMazeCoords); if (DrawMiniPreviewIsPixelInBounds(pixelPosition)) { uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition); - auto bits = trackBlock->var_08.Rotate(curTrackRotation & 3).GetBaseQuarterOccupied(); - - // Station track is a lighter colour - uint8_t colour = (ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN) ? _PaletteIndexColourStation - : _PaletteIndexColourTrack; - + uint8_t colour = _PaletteIndexColourTrack; for (int32_t i = 0; i < 4; i++) { - if (bits & 1) - pixel[338 + i] = colour; // x + 2, y + 2 - if (bits & 2) - pixel[168 + i] = colour; // y + 1 - if (bits & 4) - pixel[2 + i] = colour; // x + 2 - if (bits & 8) - pixel[172 + i] = colour; // x + 4, y + 1 + pixel[338 + i] = colour; // x + 2, y + 2 + pixel[168 + i] = colour; // y + 1 + pixel[2 + i] = colour; // x + 2 + pixel[172 + i] = colour; // x + 4, y + 1 } } } - trackBlock++; } - // Change rotation and next position based on track curvature - curTrackRotation &= 3; - - const TrackCoordinates* track_coordinate = &ted.Coordinates; - - curTrackStart += CoordsXY{ track_coordinate->x, track_coordinate->y }.Rotate(curTrackRotation); - curTrackRotation += track_coordinate->rotation_end - track_coordinate->rotation_begin; - curTrackRotation &= 3; - if (track_coordinate->rotation_end & 4) - { - curTrackRotation |= 4; - } - if (!(curTrackRotation & 4)) - { - curTrackStart += CoordsDirectionDelta[curTrackRotation]; - } + DrawMiniPreviewEntrances(*td6, pass, origin, min, max, rotation); } - DrawMiniPreviewEntrances(*td6, pass, origin, min, max, rotation); - } - - void DrawMiniPreviewMaze(TrackDesign* td6, int32_t pass, const CoordsXY& origin, CoordsXY& min, CoordsXY& max) - { - uint8_t rotation = (_currentTrackPieceDirection + GetCurrentRotation()) & 3; - for (const auto& mazeElement : td6->maze_elements) + ScreenCoordsXY DrawMiniPreviewGetPixelPosition(const CoordsXY& location) { - auto rotatedMazeCoords = origin + TileCoordsXY{ mazeElement.x, mazeElement.y }.ToCoordsXY().Rotate(rotation); + auto tilePos = TileCoordsXY(location); + return { (80 + (tilePos.y - tilePos.x) * 4), (38 + (tilePos.y + tilePos.x) * 2) }; + } - if (pass == 0) + bool DrawMiniPreviewIsPixelInBounds(const ScreenCoordsXY& pixel) + { + return pixel.x >= 0 && pixel.y >= 0 && pixel.x <= 160 && pixel.y <= 75; + } + + uint8_t* DrawMiniPreviewGetPixelPtr(const ScreenCoordsXY& pixel) + { + return &_miniPreview[pixel.y * TRACK_MINI_PREVIEW_WIDTH + pixel.x]; + } + + GameActions::Result FindValidTrackDesignPlaceHeight(CoordsXYZ& loc, uint32_t newFlags) + { + GameActions::Result res; + for (int32_t i = 0; i < 7; i++, loc.z += 8) { - min.x = std::min(min.x, rotatedMazeCoords.x); - max.x = std::max(max.x, rotatedMazeCoords.x); - min.y = std::min(min.y, rotatedMazeCoords.y); - max.y = std::max(max.y, rotatedMazeCoords.y); - } - else - { - auto pixelPosition = DrawMiniPreviewGetPixelPosition(rotatedMazeCoords); - if (DrawMiniPreviewIsPixelInBounds(pixelPosition)) + auto tdAction = TrackDesignAction( + CoordsXYZD{ loc.x, loc.y, loc.z, _currentTrackPieceDirection }, *_trackDesign); + tdAction.SetFlags(newFlags); + res = GameActions::Query(&tdAction); + + // If successful don't keep trying. + // If failure due to no money then increasing height only makes problem worse + if (res.Error == GameActions::Status::Ok || res.Error == GameActions::Status::InsufficientFunds) { - uint8_t* pixel = DrawMiniPreviewGetPixelPtr(pixelPosition); - - uint8_t colour = _PaletteIndexColourTrack; - for (int32_t i = 0; i < 4; i++) - { - pixel[338 + i] = colour; // x + 2, y + 2 - pixel[168 + i] = colour; // y + 1 - pixel[2 + i] = colour; // x + 2 - pixel[172 + i] = colour; // x + 4, y + 1 - } + return res; } } + return res; } + }; - DrawMiniPreviewEntrances(*td6, pass, origin, min, max, rotation); - } - - ScreenCoordsXY DrawMiniPreviewGetPixelPosition(const CoordsXY& location) + WindowBase* WindowTrackPlaceOpen(const TrackDesignFileRef* tdFileRef) { - auto tilePos = TileCoordsXY(location); - return { (80 + (tilePos.y - tilePos.x) * 4), (38 + (tilePos.y + tilePos.x) * 2) }; - } + std::unique_ptr openTrackDesign = TrackDesignImport(tdFileRef->path.c_str()); - bool DrawMiniPreviewIsPixelInBounds(const ScreenCoordsXY& pixel) - { - return pixel.x >= 0 && pixel.y >= 0 && pixel.x <= 160 && pixel.y <= 75; - } - - uint8_t* DrawMiniPreviewGetPixelPtr(const ScreenCoordsXY& pixel) - { - return &_miniPreview[pixel.y * TRACK_MINI_PREVIEW_WIDTH + pixel.x]; - } - - GameActions::Result FindValidTrackDesignPlaceHeight(CoordsXYZ& loc, uint32_t newFlags) - { - GameActions::Result res; - for (int32_t i = 0; i < 7; i++, loc.z += 8) + if (openTrackDesign == nullptr) { - auto tdAction = TrackDesignAction(CoordsXYZD{ loc.x, loc.y, loc.z, _currentTrackPieceDirection }, *_trackDesign); - tdAction.SetFlags(newFlags); - res = GameActions::Query(&tdAction); - - // If successful don't keep trying. - // If failure due to no money then increasing height only makes problem worse - if (res.Error == GameActions::Status::Ok || res.Error == GameActions::Status::InsufficientFunds) - { - return res; - } + return nullptr; } - return res; + + WindowCloseConstructionWindows(); + + auto* window = WindowFocusOrCreate(WindowClass::TrackDesignPlace, WW, WH, 0); + if (window != nullptr) + { + window->Init(std::move(openTrackDesign)); + } + return window; } -}; -WindowBase* WindowTrackPlaceOpen(const TrackDesignFileRef* tdFileRef) -{ - std::unique_ptr openTrackDesign = TrackDesignImport(tdFileRef->path.c_str()); - - if (openTrackDesign == nullptr) + void TrackPlaceClearProvisionalTemporarily() { - return nullptr; + auto* trackPlaceWnd = static_cast(WindowFindByClass(WindowClass::TrackDesignPlace)); + if (trackPlaceWnd != nullptr) + { + trackPlaceWnd->ClearProvisionalTemporarily(); + } } - WindowCloseConstructionWindows(); - - auto* window = WindowFocusOrCreate(WindowClass::TrackDesignPlace, WW, WH, 0); - if (window != nullptr) + void TrackPlaceRestoreProvisional() { - window->Init(std::move(openTrackDesign)); + auto* trackPlaceWnd = static_cast(WindowFindByClass(WindowClass::TrackDesignPlace)); + if (trackPlaceWnd != nullptr) + { + trackPlaceWnd->RestoreProvisional(); + } } - return window; -} - -void TrackPlaceClearProvisionalTemporarily() -{ - auto* trackPlaceWnd = static_cast(WindowFindByClass(WindowClass::TrackDesignPlace)); - if (trackPlaceWnd != nullptr) - { - trackPlaceWnd->ClearProvisionalTemporarily(); - } -} - -void TrackPlaceRestoreProvisional() -{ - auto* trackPlaceWnd = static_cast(WindowFindByClass(WindowClass::TrackDesignPlace)); - if (trackPlaceWnd != nullptr) - { - trackPlaceWnd->RestoreProvisional(); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/TrackList.cpp b/src/openrct2-ui/windows/TrackList.cpp index 5274e79342..edc4b91b8e 100644 --- a/src/openrct2-ui/windows/TrackList.cpp +++ b/src/openrct2-ui/windows/TrackList.cpp @@ -28,14 +28,16 @@ #include #include -static constexpr StringId WINDOW_TITLE = STR_SELECT_DESIGN; -static constexpr int32_t WH = 441; -static constexpr int32_t WW = 600; -static constexpr int32_t DEBUG_PATH_HEIGHT = 12; -static constexpr int32_t ROTATE_AND_SCENERY_BUTTON_SIZE = 24; -static constexpr int32_t WINDOW_PADDING = 5; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_SELECT_DESIGN; + static constexpr int32_t WH = 441; + static constexpr int32_t WW = 600; + static constexpr int32_t DEBUG_PATH_HEIGHT = 12; + static constexpr int32_t ROTATE_AND_SCENERY_BUTTON_SIZE = 24; + static constexpr int32_t WINDOW_PADDING = 5; -// clang-format off + // clang-format off enum { WIDX_BACKGROUND, WIDX_TITLE, @@ -63,658 +65,634 @@ static Widget _trackListWidgets[] = { kWidgetsEnd, }; -// clang-format on + // clang-format on -constexpr uint16_t TRACK_DESIGN_INDEX_UNLOADED = UINT16_MAX; + constexpr uint16_t TRACK_DESIGN_INDEX_UNLOADED = UINT16_MAX; -RideSelection _window_track_list_item; + RideSelection _window_track_list_item; -class TrackListWindow final : public Window -{ -private: - std::vector _trackDesigns; - utf8 _filterString[USER_STRING_MAX_LENGTH]{}; - std::vector _filteredTrackIds; - uint16_t _loadedTrackDesignIndex; - std::unique_ptr _loadedTrackDesign; - std::vector _trackDesignPreviewPixels; - bool _selectedItemIsBeingUpdated; - bool _reloadTrackDesigns; - - void FilterList() + class TrackListWindow final : public Window { - _filteredTrackIds.clear(); + private: + std::vector _trackDesigns; + utf8 _filterString[USER_STRING_MAX_LENGTH]{}; + std::vector _filteredTrackIds; + uint16_t _loadedTrackDesignIndex; + std::unique_ptr _loadedTrackDesign; + std::vector _trackDesignPreviewPixels; + bool _selectedItemIsBeingUpdated; + bool _reloadTrackDesigns; - // Nothing to filter, so fill the list with all indices - if (String::LengthOf(_filterString) == 0) + void FilterList() { - for (uint16_t i = 0; i < _trackDesigns.size(); i++) - _filteredTrackIds.push_back(i); + _filteredTrackIds.clear(); - return; - } - - // Convert filter to uppercase - const auto filterStringUpper = String::ToUpper(_filterString); - - // Fill the set with indices for tracks that match the filter - for (uint16_t i = 0; i < _trackDesigns.size(); i++) - { - const auto trackNameUpper = String::ToUpper(_trackDesigns[i].name); - if (trackNameUpper.find(filterStringUpper) != std::string::npos) + // Nothing to filter, so fill the list with all indices + if (String::LengthOf(_filterString) == 0) { - _filteredTrackIds.push_back(i); - } - } + for (uint16_t i = 0; i < _trackDesigns.size(); i++) + _filteredTrackIds.push_back(i); - // Ensure that the selected item is still in the list. - if (static_cast(selected_list_item) >= _filteredTrackIds.size()) - { - selected_list_item = 0; - } - } - - void SelectFromList(int32_t listIndex) - { - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, this->windowPos.x + (this->width / 2)); - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - if (listIndex == 0) - { - Close(); - RideConstructNew(_window_track_list_item); return; } - listIndex--; - } - // Displays a message if the ride can't load, fix #4080 - if (_loadedTrackDesign == nullptr) - { - ContextShowError(STR_CANT_BUILD_THIS_HERE, STR_TRACK_LOAD_FAILED_ERROR, {}); - return; - } + // Convert filter to uppercase + const auto filterStringUpper = String::ToUpper(_filterString); - if (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) - { - gTrackDesignSceneryToggle = true; - } - - uint16_t trackDesignIndex = _filteredTrackIds[listIndex]; - TrackDesignFileRef* tdRef = &_trackDesigns[trackDesignIndex]; - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - auto intent = Intent(WindowClass::ManageTrackDesign); - intent.PutExtra(INTENT_EXTRA_TRACK_DESIGN, tdRef); - ContextOpenIntent(&intent); - } - else - { - if (_loadedTrackDesignIndex != TRACK_DESIGN_INDEX_UNLOADED - && (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE)) + // Fill the set with indices for tracks that match the filter + for (uint16_t i = 0; i < _trackDesigns.size(); i++) { - ContextShowError(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, STR_NONE, {}); - } - - auto intent = Intent(WindowClass::TrackDesignPlace); - intent.PutExtra(INTENT_EXTRA_TRACK_DESIGN, tdRef); - ContextOpenIntent(&intent); - } - } - - int32_t GetListItemFromPosition(const ScreenCoordsXY& screenCoords) - { - size_t maxItems = _filteredTrackIds.size(); - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - // Extra item: custom design - maxItems++; - } - - int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index < 0 || static_cast(index) >= maxItems) - { - index = -1; - } - return index; - } - - void LoadDesignsList(RideSelection item) - { - auto repo = OpenRCT2::GetContext()->GetTrackDesignRepository(); - std::string entryName; - if (item.Type < 0x80) - { - if (GetRideTypeDescriptor(item.Type).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) - { - entryName = GetRideEntryName(item.EntryIndex); - } - } - _trackDesigns = repo->GetItemsForObjectEntry(item.Type, entryName); - - FilterList(); - } - - bool LoadDesignPreview(const u8string& path) - { - _loadedTrackDesign = TrackDesignImport(path.c_str()); - if (_loadedTrackDesign != nullptr) - { - TrackDesignDrawPreview(_loadedTrackDesign.get(), _trackDesignPreviewPixels.data()); - return true; - } - return false; - } - -public: - TrackListWindow(const RideSelection item) - { - _window_track_list_item = item; - } - - void OnOpen() override - { - LoadDesignsList(_window_track_list_item); - - String::Set(_filterString, sizeof(_filterString), ""); - _trackListWidgets[WIDX_FILTER_STRING].string = _filterString; - widgets = _trackListWidgets; - - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - widgets[WIDX_BACK].type = WindowWidgetType::Empty; - } - else - { - widgets[WIDX_BACK].type = WindowWidgetType::TableHeader; - } - - WindowInitScrollWidgets(*this); - _selectedItemIsBeingUpdated = false; - _reloadTrackDesigns = false; - // Start with first track highlighted - selected_list_item = 0; - if (_trackDesigns.size() != 0 && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - selected_list_item = 1; - } - gTrackDesignSceneryToggle = false; - WindowPushOthersRight(*this); - _currentTrackPieceDirection = 2; - _trackDesignPreviewPixels.resize(4 * TRACK_PREVIEW_IMAGE_SIZE); - - _loadedTrackDesign = nullptr; - _loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED; - } - - void OnClose() override - { - // Dispose track design and preview - _loadedTrackDesign = nullptr; - _trackDesignPreviewPixels.clear(); - _trackDesignPreviewPixels.shrink_to_fit(); - - // Dispose track list - _trackDesigns.clear(); - - // If gScreenAge is zero, we're already in the process - // of loading the track manager, so we shouldn't try - // to do it again. Otherwise, this window will get - // another close signal from the track manager load function, - // try to load the track manager again, and an infinite loop will result. - if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && gScreenAge != 0) - { - WindowCloseByNumber(WindowClass::ManageTrackDesign, number); - WindowCloseByNumber(WindowClass::TrackDeletePrompt, number); - Editor::LoadTrackManager(); - } - } - - void OnMouseUp(const WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: - Close(); - break; - case WIDX_ROTATE: - _currentTrackPieceDirection++; - _currentTrackPieceDirection %= 4; - Invalidate(); - break; - case WIDX_TOGGLE_SCENERY: - gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle; - _loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED; - Invalidate(); - break; - case WIDX_BACK: - Close(); - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + const auto trackNameUpper = String::ToUpper(_trackDesigns[i].name); + if (trackNameUpper.find(filterStringUpper) != std::string::npos) { - ContextOpenWindow(WindowClass::ConstructRide); - } - break; - case WIDX_FILTER_STRING: - WindowStartTextbox(*this, widgetIndex, STR_STRING, _filterString, sizeof(_filterString)); // TODO check this out - break; - case WIDX_FILTER_CLEAR: - // Keep the highlighted item selected - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - if (selected_list_item != -1 && _filteredTrackIds.size() > static_cast(selected_list_item)) - selected_list_item = _filteredTrackIds[selected_list_item]; - else - selected_list_item = -1; - } - else - { - if (selected_list_item != 0) - selected_list_item = _filteredTrackIds[selected_list_item - 1] + 1; + _filteredTrackIds.push_back(i); } + } - String::Set(_filterString, sizeof(_filterString), ""); - FilterList(); - Invalidate(); - break; - } - } - - ScreenSize OnScrollGetSize(const int32_t scrollIndex) override - { - size_t numItems = _filteredTrackIds.size(); - if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - // Extra item: custom design - numItems++; - } - int32_t scrollHeight = static_cast(numItems * SCROLLABLE_ROW_HEIGHT); - - return { width, scrollHeight }; - } - - void OnScrollMouseDown(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (!_selectedItemIsBeingUpdated) - { - int32_t i = GetListItemFromPosition(screenCoords); - if (i != -1) + // Ensure that the selected item is still in the list. + if (static_cast(selected_list_item) >= _filteredTrackIds.size()) { - SelectFromList(i); + selected_list_item = 0; } } - } - void OnScrollMouseOver(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override - { - if (!_selectedItemIsBeingUpdated) + void SelectFromList(int32_t listIndex) { - int32_t i = GetListItemFromPosition(screenCoords); - if (i != -1 && selected_list_item != i) + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, this->windowPos.x + (this->width / 2)); + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) { - selected_list_item = i; - Invalidate(); + if (listIndex == 0) + { + Close(); + RideConstructNew(_window_track_list_item); + return; + } + listIndex--; } - } - } - void OnTextInput(const WidgetIndex widgetIndex, std::string_view text) override - { - if (widgetIndex != WIDX_FILTER_STRING) - return; - - if (String::Equals(_filterString, std::string(text).c_str())) - return; - - String::Set(_filterString, sizeof(_filterString), std::string(text).c_str()); - - FilterList(); - - scrolls->v_top = 0; - - Invalidate(); - } - - void OnPrepareDraw() override - { - StringId stringId = STR_NONE; - const auto* entry = GetRideEntryByIndex(_window_track_list_item.EntryIndex); - - if (entry != nullptr) - { - RideNaming rideName = GetRideNaming(_window_track_list_item.Type, *entry); - stringId = rideName.Name; - } - - Formatter::Common().Add(stringId); - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - widgets[WIDX_TITLE].text = STR_TRACK_DESIGNS; - widgets[WIDX_TRACK_LIST].tooltip = STR_CLICK_ON_DESIGN_TO_RENAME_OR_DELETE_IT; - } - else - { - widgets[WIDX_TITLE].text = STR_SELECT_DESIGN; - widgets[WIDX_TRACK_LIST].tooltip = STR_CLICK_ON_DESIGN_TO_BUILD_IT_TIP; - } - - if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) || selected_list_item != 0) - { - pressed_widgets |= 1uLL << WIDX_TRACK_PREVIEW; - disabled_widgets &= ~(1uLL << WIDX_TRACK_PREVIEW); - widgets[WIDX_ROTATE].type = WindowWidgetType::FlatBtn; - widgets[WIDX_TOGGLE_SCENERY].type = WindowWidgetType::FlatBtn; - if (gTrackDesignSceneryToggle) + // Displays a message if the ride can't load, fix #4080 + if (_loadedTrackDesign == nullptr) { - pressed_widgets &= ~(1uLL << WIDX_TOGGLE_SCENERY); + ContextShowError(STR_CANT_BUILD_THIS_HERE, STR_TRACK_LOAD_FAILED_ERROR, {}); + return; + } + + if (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) + { + gTrackDesignSceneryToggle = true; + } + + uint16_t trackDesignIndex = _filteredTrackIds[listIndex]; + TrackDesignFileRef* tdRef = &_trackDesigns[trackDesignIndex]; + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + auto intent = Intent(WindowClass::ManageTrackDesign); + intent.PutExtra(INTENT_EXTRA_TRACK_DESIGN, tdRef); + ContextOpenIntent(&intent); } else { - pressed_widgets |= (1uLL << WIDX_TOGGLE_SCENERY); + if (_loadedTrackDesignIndex != TRACK_DESIGN_INDEX_UNLOADED + && (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE)) + { + ContextShowError(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, STR_NONE, {}); + } + + auto intent = Intent(WindowClass::TrackDesignPlace); + intent.PutExtra(INTENT_EXTRA_TRACK_DESIGN, tdRef); + ContextOpenIntent(&intent); } } - else + + int32_t GetListItemFromPosition(const ScreenCoordsXY& screenCoords) { - pressed_widgets &= ~(1uLL << WIDX_TRACK_PREVIEW); - disabled_widgets |= (1uLL << WIDX_TRACK_PREVIEW); - widgets[WIDX_ROTATE].type = WindowWidgetType::Empty; - widgets[WIDX_TOGGLE_SCENERY].type = WindowWidgetType::Empty; + size_t maxItems = _filteredTrackIds.size(); + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + { + // Extra item: custom design + maxItems++; + } + + int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index < 0 || static_cast(index) >= maxItems) + { + index = -1; + } + return index; } - // When debugging tools are on, shift everything up a bit to make room for displaying the path. - const int32_t bottomMargin = gConfigGeneral.DebuggingTools ? (WINDOW_PADDING + DEBUG_PATH_HEIGHT) : WINDOW_PADDING; - widgets[WIDX_TRACK_LIST].bottom = height - bottomMargin; - widgets[WIDX_ROTATE].bottom = height - bottomMargin; - widgets[WIDX_ROTATE].top = widgets[WIDX_ROTATE].bottom - ROTATE_AND_SCENERY_BUTTON_SIZE; - widgets[WIDX_TOGGLE_SCENERY].bottom = widgets[WIDX_ROTATE].top; - widgets[WIDX_TOGGLE_SCENERY].top = widgets[WIDX_TOGGLE_SCENERY].bottom - ROTATE_AND_SCENERY_BUTTON_SIZE; - } - - void OnUpdate() override - { - if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) + void LoadDesignsList(RideSelection item) { - WindowUpdateTextboxCaret(); - WidgetInvalidate(*this, WIDX_FILTER_STRING); // TODO Check this + auto repo = OpenRCT2::GetContext()->GetTrackDesignRepository(); + std::string entryName; + if (item.Type < 0x80) + { + if (GetRideTypeDescriptor(item.Type).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + entryName = GetRideEntryName(item.EntryIndex); + } + } + _trackDesigns = repo->GetItemsForObjectEntry(item.Type, entryName); + + FilterList(); } - if (_reloadTrackDesigns) + bool LoadDesignPreview(const u8string& path) + { + _loadedTrackDesign = TrackDesignImport(path.c_str()); + if (_loadedTrackDesign != nullptr) + { + TrackDesignDrawPreview(_loadedTrackDesign.get(), _trackDesignPreviewPixels.data()); + return true; + } + return false; + } + + public: + TrackListWindow(const RideSelection item) + { + _window_track_list_item = item; + } + + void OnOpen() override { LoadDesignsList(_window_track_list_item); - selected_list_item = 0; - Invalidate(); - _reloadTrackDesigns = false; - } - } - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); + String::Set(_filterString, sizeof(_filterString), ""); + _trackListWidgets[WIDX_FILTER_STRING].string = _filterString; + widgets = _trackListWidgets; - int32_t listItemIndex = selected_list_item; - if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) == 0) - { - // Because the first item in the list is "Build a custom design", lower the index by one - listItemIndex--; - } - - if (_filteredTrackIds.empty() || listItemIndex == -1) - return; - - int32_t trackIndex = _filteredTrackIds[listItemIndex]; - - // Track preview - auto& tdWidget = widgets[WIDX_TRACK_PREVIEW]; - int32_t colour = ColourMapA[colours[0]].darkest; - u8string path = _trackDesigns[trackIndex].path; - - // Show track file path (in debug mode) - if (gConfigGeneral.DebuggingTools) - { - const auto shortPath = ShortenPath(path, width, FontStyle::Medium); - auto ft = Formatter(); - ft.Add(shortPath.c_str()); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 0, height - DEBUG_PATH_HEIGHT - 3 }, STR_STRING, ft, - { colours[1] }); // TODO Check dpi - } - - auto screenPos = windowPos + ScreenCoordsXY{ tdWidget.left + 1, tdWidget.top + 1 }; - GfxFillRect(dpi, { screenPos, screenPos + ScreenCoordsXY{ 369, 216 } }, colour); // TODO Check dpi - - if (_loadedTrackDesignIndex != trackIndex) - { - if (LoadDesignPreview(path)) + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { - _loadedTrackDesignIndex = trackIndex; + widgets[WIDX_BACK].type = WindowWidgetType::Empty; } else { - _loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED; + widgets[WIDX_BACK].type = WindowWidgetType::TableHeader; + } + + WindowInitScrollWidgets(*this); + _selectedItemIsBeingUpdated = false; + _reloadTrackDesigns = false; + // Start with first track highlighted + selected_list_item = 0; + if (_trackDesigns.size() != 0 && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + { + selected_list_item = 1; + } + gTrackDesignSceneryToggle = false; + WindowPushOthersRight(*this); + _currentTrackPieceDirection = 2; + _trackDesignPreviewPixels.resize(4 * TRACK_PREVIEW_IMAGE_SIZE); + + _loadedTrackDesign = nullptr; + _loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED; + } + + void OnClose() override + { + // Dispose track design and preview + _loadedTrackDesign = nullptr; + _trackDesignPreviewPixels.clear(); + _trackDesignPreviewPixels.shrink_to_fit(); + + // Dispose track list + _trackDesigns.clear(); + + // If gScreenAge is zero, we're already in the process + // of loading the track manager, so we shouldn't try + // to do it again. Otherwise, this window will get + // another close signal from the track manager load function, + // try to load the track manager again, and an infinite loop will result. + if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && gScreenAge != 0) + { + WindowCloseByNumber(WindowClass::ManageTrackDesign, number); + WindowCloseByNumber(WindowClass::TrackDeletePrompt, number); + Editor::LoadTrackManager(); } } - if (!_loadedTrackDesign) + void OnMouseUp(const WidgetIndex widgetIndex) override { - return; - } - - auto trackPreview = screenPos; - screenPos = windowPos + ScreenCoordsXY{ tdWidget.midX(), tdWidget.midY() }; - - G1Element g1temp = {}; - g1temp.offset = _trackDesignPreviewPixels.data() + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE); - g1temp.width = 370; - g1temp.height = 217; - g1temp.flags = G1_FLAG_HAS_TRANSPARENCY; - GfxSetG1Element(SPR_TEMP, &g1temp); - DrawingEngineInvalidateImage(SPR_TEMP); - GfxDrawSprite(dpi, ImageId(SPR_TEMP), trackPreview); - - screenPos.y = windowPos.y + tdWidget.bottom - 12; - - // Warnings - if ((_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE) - && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) - { - // Vehicle design not available - DrawTextEllipsised(dpi, screenPos, 368, STR_VEHICLE_DESIGN_UNAVAILABLE, {}, { TextAlignment::CENTRE }); - screenPos.y -= SCROLLABLE_ROW_HEIGHT; - } - - if (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) - { - if (!gTrackDesignSceneryToggle) + switch (widgetIndex) { - // Scenery not available - DrawTextEllipsised( - dpi, screenPos, 368, STR_DESIGN_INCLUDES_SCENERY_WHICH_IS_UNAVAILABLE, {}, { TextAlignment::CENTRE }); - screenPos.y -= SCROLLABLE_ROW_HEIGHT; + case WIDX_CLOSE: + Close(); + break; + case WIDX_ROTATE: + _currentTrackPieceDirection++; + _currentTrackPieceDirection %= 4; + Invalidate(); + break; + case WIDX_TOGGLE_SCENERY: + gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle; + _loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED; + Invalidate(); + break; + case WIDX_BACK: + Close(); + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + { + ContextOpenWindow(WindowClass::ConstructRide); + } + break; + case WIDX_FILTER_STRING: + WindowStartTextbox( + *this, widgetIndex, STR_STRING, _filterString, sizeof(_filterString)); // TODO check this out + break; + case WIDX_FILTER_CLEAR: + // Keep the highlighted item selected + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + if (selected_list_item != -1 && _filteredTrackIds.size() > static_cast(selected_list_item)) + selected_list_item = _filteredTrackIds[selected_list_item]; + else + selected_list_item = -1; + } + else + { + if (selected_list_item != 0) + selected_list_item = _filteredTrackIds[selected_list_item - 1] + 1; + } + + String::Set(_filterString, sizeof(_filterString), ""); + FilterList(); + Invalidate(); + break; } } - // Track design name - auto ft = Formatter(); - ft.Add(_trackDesigns[trackIndex].name.c_str()); - DrawTextEllipsised(dpi, screenPos, 368, STR_TRACK_PREVIEW_NAME_FORMAT, ft, { TextAlignment::CENTRE }); - - // Information - screenPos = windowPos + ScreenCoordsXY{ tdWidget.left + 1, tdWidget.bottom + 2 }; - - // Stats - ft = Formatter(); - ft.Add(_loadedTrackDesign->excitement * 10); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_EXCITEMENT_RATING, ft); - screenPos.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(_loadedTrackDesign->intensity * 10); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_INTENSITY_RATING, ft); - screenPos.y += LIST_ROW_HEIGHT; - - ft = Formatter(); - ft.Add(_loadedTrackDesign->nausea * 10); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_NAUSEA_RATING, ft); - screenPos.y += LIST_ROW_HEIGHT + 4; - - // Information for tracked rides. - if (GetRideTypeDescriptor(_loadedTrackDesign->type).HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) + ScreenSize OnScrollGetSize(const int32_t scrollIndex) override { - const auto& rtd = GetRideTypeDescriptor(_loadedTrackDesign->type); - if (!rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + size_t numItems = _filteredTrackIds.size(); + if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) { - if (_loadedTrackDesign->type == RIDE_TYPE_MINI_GOLF) + // Extra item: custom design + numItems++; + } + int32_t scrollHeight = static_cast(numItems * SCROLLABLE_ROW_HEIGHT); + + return { width, scrollHeight }; + } + + void OnScrollMouseDown(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + if (!_selectedItemIsBeingUpdated) + { + int32_t i = GetListItemFromPosition(screenCoords); + if (i != -1) { - // Holes - ft = Formatter(); - ft.Add(_loadedTrackDesign->holes & 0x1F); - DrawTextBasic(dpi, screenPos, STR_HOLES, ft); - screenPos.y += LIST_ROW_HEIGHT; + SelectFromList(i); + } + } + } + + void OnScrollMouseOver(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + if (!_selectedItemIsBeingUpdated) + { + int32_t i = GetListItemFromPosition(screenCoords); + if (i != -1 && selected_list_item != i) + { + selected_list_item = i; + Invalidate(); + } + } + } + + void OnTextInput(const WidgetIndex widgetIndex, std::string_view text) override + { + if (widgetIndex != WIDX_FILTER_STRING) + return; + + if (String::Equals(_filterString, std::string(text).c_str())) + return; + + String::Set(_filterString, sizeof(_filterString), std::string(text).c_str()); + + FilterList(); + + scrolls->v_top = 0; + + Invalidate(); + } + + void OnPrepareDraw() override + { + StringId stringId = STR_NONE; + const auto* entry = GetRideEntryByIndex(_window_track_list_item.EntryIndex); + + if (entry != nullptr) + { + RideNaming rideName = GetRideNaming(_window_track_list_item.Type, *entry); + stringId = rideName.Name; + } + + Formatter::Common().Add(stringId); + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + widgets[WIDX_TITLE].text = STR_TRACK_DESIGNS; + widgets[WIDX_TRACK_LIST].tooltip = STR_CLICK_ON_DESIGN_TO_RENAME_OR_DELETE_IT; + } + else + { + widgets[WIDX_TITLE].text = STR_SELECT_DESIGN; + widgets[WIDX_TRACK_LIST].tooltip = STR_CLICK_ON_DESIGN_TO_BUILD_IT_TIP; + } + + if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) || selected_list_item != 0) + { + pressed_widgets |= 1uLL << WIDX_TRACK_PREVIEW; + disabled_widgets &= ~(1uLL << WIDX_TRACK_PREVIEW); + widgets[WIDX_ROTATE].type = WindowWidgetType::FlatBtn; + widgets[WIDX_TOGGLE_SCENERY].type = WindowWidgetType::FlatBtn; + if (gTrackDesignSceneryToggle) + { + pressed_widgets &= ~(1uLL << WIDX_TOGGLE_SCENERY); } else { - // Maximum speed - ft = Formatter(); - ft.Add(((_loadedTrackDesign->max_speed << 16) * 9) >> 18); - DrawTextBasic(dpi, screenPos, STR_MAX_SPEED, ft); - screenPos.y += LIST_ROW_HEIGHT; - - // Average speed - ft = Formatter(); - ft.Add(((_loadedTrackDesign->average_speed << 16) * 9) >> 18); - DrawTextBasic(dpi, screenPos, STR_AVERAGE_SPEED, ft); - screenPos.y += LIST_ROW_HEIGHT; + pressed_widgets |= (1uLL << WIDX_TOGGLE_SCENERY); } - - // Ride length - ft = Formatter(); - ft.Add(STR_RIDE_LENGTH_ENTRY); - ft.Add(_loadedTrackDesign->ride_length); - DrawTextEllipsised(dpi, screenPos, 214, STR_TRACK_LIST_RIDE_LENGTH, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - - if (GetRideTypeDescriptor(_loadedTrackDesign->type).HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) - { - // Maximum positive vertical Gs - ft = Formatter(); - ft.Add(_loadedTrackDesign->max_positive_vertical_g * 32); - DrawTextBasic(dpi, screenPos, STR_MAX_POSITIVE_VERTICAL_G, ft); - screenPos.y += LIST_ROW_HEIGHT; - - // Maximum negative vertical Gs - ft = Formatter(); - ft.Add(_loadedTrackDesign->max_negative_vertical_g * 32); - DrawTextBasic(dpi, screenPos, STR_MAX_NEGATIVE_VERTICAL_G, ft); - screenPos.y += LIST_ROW_HEIGHT; - - // Maximum lateral Gs - ft = Formatter(); - ft.Add(_loadedTrackDesign->max_lateral_g * 32); - DrawTextBasic(dpi, screenPos, STR_MAX_LATERAL_G, ft); - screenPos.y += LIST_ROW_HEIGHT; - - if (_loadedTrackDesign->total_air_time != 0) - { - // Total air time - ft = Formatter(); - ft.Add(_loadedTrackDesign->total_air_time * 25); - DrawTextBasic(dpi, screenPos, STR_TOTAL_AIR_TIME, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - } - - if (GetRideTypeDescriptor(_loadedTrackDesign->type).HasFlag(RIDE_TYPE_FLAG_HAS_DROPS)) - { - // Drops - ft = Formatter(); - ft.Add(_loadedTrackDesign->drops & 0x3F); - DrawTextBasic(dpi, screenPos, STR_DROPS, ft); - screenPos.y += LIST_ROW_HEIGHT; - - // Drop height is multiplied by 0.75 - ft = Formatter(); - ft.Add((_loadedTrackDesign->highest_drop_height * 3) / 4); - DrawTextBasic(dpi, screenPos, STR_HIGHEST_DROP_HEIGHT, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - - if (_loadedTrackDesign->type != RIDE_TYPE_MINI_GOLF) - { - uint16_t inversions = _loadedTrackDesign->inversions & 0x1F; - if (inversions != 0) - { - ft = Formatter(); - ft.Add(inversions); - // Inversions - DrawTextBasic(dpi, screenPos, STR_INVERSIONS, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - } - screenPos.y += 4; - } - - if (_loadedTrackDesign->space_required_x != 0xFF) - { - // Space required - ft = Formatter(); - ft.Add(_loadedTrackDesign->space_required_x); - ft.Add(_loadedTrackDesign->space_required_y); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_SPACE_REQUIRED, ft); - screenPos.y += LIST_ROW_HEIGHT; - } - - if (_loadedTrackDesign->cost != 0) - { - ft = Formatter(); - ft.Add(_loadedTrackDesign->cost); - DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_COST_AROUND, ft); - } - } - - void OnScrollDraw(const int32_t scrollIndex, DrawPixelInfo& dpi) override - { - uint8_t paletteIndex = ColourMapA[colours[0]].mid_light; - GfxClear(&dpi, paletteIndex); - - auto screenCoords = ScreenCoordsXY{ 0, 0 }; - size_t listIndex = 0; - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - if (_trackDesigns.empty()) - { - // No track designs - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 1 }, STR_NO_TRACK_DESIGNS_OF_THIS_TYPE); - return; - } - } - else - { - // Build custom track item - StringId stringId; - if (listIndex == static_cast(selected_list_item)) - { - // Highlight - GfxFilterRect( - dpi, { screenCoords, { width, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }, - FilterPaletteID::PaletteDarken1); - stringId = STR_WINDOW_COLOUR_2_STRINGID; } else { - stringId = STR_BLACK_STRING; + pressed_widgets &= ~(1uLL << WIDX_TRACK_PREVIEW); + disabled_widgets |= (1uLL << WIDX_TRACK_PREVIEW); + widgets[WIDX_ROTATE].type = WindowWidgetType::Empty; + widgets[WIDX_TOGGLE_SCENERY].type = WindowWidgetType::Empty; } - auto ft = Formatter(); - ft.Add(STR_BUILD_CUSTOM_DESIGN); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 1 }, stringId, ft); - screenCoords.y += SCROLLABLE_ROW_HEIGHT; - listIndex++; + // When debugging tools are on, shift everything up a bit to make room for displaying the path. + const int32_t bottomMargin = gConfigGeneral.DebuggingTools ? (WINDOW_PADDING + DEBUG_PATH_HEIGHT) : WINDOW_PADDING; + widgets[WIDX_TRACK_LIST].bottom = height - bottomMargin; + widgets[WIDX_ROTATE].bottom = height - bottomMargin; + widgets[WIDX_ROTATE].top = widgets[WIDX_ROTATE].bottom - ROTATE_AND_SCENERY_BUTTON_SIZE; + widgets[WIDX_TOGGLE_SCENERY].bottom = widgets[WIDX_ROTATE].top; + widgets[WIDX_TOGGLE_SCENERY].top = widgets[WIDX_TOGGLE_SCENERY].bottom - ROTATE_AND_SCENERY_BUTTON_SIZE; } - for (auto i : _filteredTrackIds) + void OnUpdate() override { - if (screenCoords.y + SCROLLABLE_ROW_HEIGHT >= dpi.y && screenCoords.y < dpi.y + dpi.height) + if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number) { + WindowUpdateTextboxCaret(); + WidgetInvalidate(*this, WIDX_FILTER_STRING); // TODO Check this + } + + if (_reloadTrackDesigns) + { + LoadDesignsList(_window_track_list_item); + selected_list_item = 0; + Invalidate(); + _reloadTrackDesigns = false; + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + + int32_t listItemIndex = selected_list_item; + if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) == 0) + { + // Because the first item in the list is "Build a custom design", lower the index by one + listItemIndex--; + } + + if (_filteredTrackIds.empty() || listItemIndex == -1) + return; + + int32_t trackIndex = _filteredTrackIds[listItemIndex]; + + // Track preview + auto& tdWidget = widgets[WIDX_TRACK_PREVIEW]; + int32_t colour = ColourMapA[colours[0]].darkest; + u8string path = _trackDesigns[trackIndex].path; + + // Show track file path (in debug mode) + if (gConfigGeneral.DebuggingTools) + { + const auto shortPath = ShortenPath(path, width, FontStyle::Medium); + auto ft = Formatter(); + ft.Add(shortPath.c_str()); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 0, height - DEBUG_PATH_HEIGHT - 3 }, STR_STRING, ft, + { colours[1] }); // TODO Check dpi + } + + auto screenPos = windowPos + ScreenCoordsXY{ tdWidget.left + 1, tdWidget.top + 1 }; + GfxFillRect(dpi, { screenPos, screenPos + ScreenCoordsXY{ 369, 216 } }, colour); // TODO Check dpi + + if (_loadedTrackDesignIndex != trackIndex) + { + if (LoadDesignPreview(path)) + { + _loadedTrackDesignIndex = trackIndex; + } + else + { + _loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED; + } + } + + if (!_loadedTrackDesign) + { + return; + } + + auto trackPreview = screenPos; + screenPos = windowPos + ScreenCoordsXY{ tdWidget.midX(), tdWidget.midY() }; + + G1Element g1temp = {}; + g1temp.offset = _trackDesignPreviewPixels.data() + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE); + g1temp.width = 370; + g1temp.height = 217; + g1temp.flags = G1_FLAG_HAS_TRANSPARENCY; + GfxSetG1Element(SPR_TEMP, &g1temp); + DrawingEngineInvalidateImage(SPR_TEMP); + GfxDrawSprite(dpi, ImageId(SPR_TEMP), trackPreview); + + screenPos.y = windowPos.y + tdWidget.bottom - 12; + + // Warnings + if ((_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE) + && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + { + // Vehicle design not available + DrawTextEllipsised(dpi, screenPos, 368, STR_VEHICLE_DESIGN_UNAVAILABLE, {}, { TextAlignment::CENTRE }); + screenPos.y -= SCROLLABLE_ROW_HEIGHT; + } + + if (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) + { + if (!gTrackDesignSceneryToggle) + { + // Scenery not available + DrawTextEllipsised( + dpi, screenPos, 368, STR_DESIGN_INCLUDES_SCENERY_WHICH_IS_UNAVAILABLE, {}, { TextAlignment::CENTRE }); + screenPos.y -= SCROLLABLE_ROW_HEIGHT; + } + } + + // Track design name + auto ft = Formatter(); + ft.Add(_trackDesigns[trackIndex].name.c_str()); + DrawTextEllipsised(dpi, screenPos, 368, STR_TRACK_PREVIEW_NAME_FORMAT, ft, { TextAlignment::CENTRE }); + + // Information + screenPos = windowPos + ScreenCoordsXY{ tdWidget.left + 1, tdWidget.bottom + 2 }; + + // Stats + ft = Formatter(); + ft.Add(_loadedTrackDesign->excitement * 10); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_EXCITEMENT_RATING, ft); + screenPos.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(_loadedTrackDesign->intensity * 10); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_INTENSITY_RATING, ft); + screenPos.y += LIST_ROW_HEIGHT; + + ft = Formatter(); + ft.Add(_loadedTrackDesign->nausea * 10); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_NAUSEA_RATING, ft); + screenPos.y += LIST_ROW_HEIGHT + 4; + + // Information for tracked rides. + if (GetRideTypeDescriptor(_loadedTrackDesign->type).HasFlag(RIDE_TYPE_FLAG_HAS_TRACK)) + { + const auto& rtd = GetRideTypeDescriptor(_loadedTrackDesign->type); + if (!rtd.HasFlag(RIDE_TYPE_FLAG_IS_MAZE)) + { + if (_loadedTrackDesign->type == RIDE_TYPE_MINI_GOLF) + { + // Holes + ft = Formatter(); + ft.Add(_loadedTrackDesign->holes & 0x1F); + DrawTextBasic(dpi, screenPos, STR_HOLES, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + else + { + // Maximum speed + ft = Formatter(); + ft.Add(((_loadedTrackDesign->max_speed << 16) * 9) >> 18); + DrawTextBasic(dpi, screenPos, STR_MAX_SPEED, ft); + screenPos.y += LIST_ROW_HEIGHT; + + // Average speed + ft = Formatter(); + ft.Add(((_loadedTrackDesign->average_speed << 16) * 9) >> 18); + DrawTextBasic(dpi, screenPos, STR_AVERAGE_SPEED, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + + // Ride length + ft = Formatter(); + ft.Add(STR_RIDE_LENGTH_ENTRY); + ft.Add(_loadedTrackDesign->ride_length); + DrawTextEllipsised(dpi, screenPos, 214, STR_TRACK_LIST_RIDE_LENGTH, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + + if (GetRideTypeDescriptor(_loadedTrackDesign->type).HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES)) + { + // Maximum positive vertical Gs + ft = Formatter(); + ft.Add(_loadedTrackDesign->max_positive_vertical_g * 32); + DrawTextBasic(dpi, screenPos, STR_MAX_POSITIVE_VERTICAL_G, ft); + screenPos.y += LIST_ROW_HEIGHT; + + // Maximum negative vertical Gs + ft = Formatter(); + ft.Add(_loadedTrackDesign->max_negative_vertical_g * 32); + DrawTextBasic(dpi, screenPos, STR_MAX_NEGATIVE_VERTICAL_G, ft); + screenPos.y += LIST_ROW_HEIGHT; + + // Maximum lateral Gs + ft = Formatter(); + ft.Add(_loadedTrackDesign->max_lateral_g * 32); + DrawTextBasic(dpi, screenPos, STR_MAX_LATERAL_G, ft); + screenPos.y += LIST_ROW_HEIGHT; + + if (_loadedTrackDesign->total_air_time != 0) + { + // Total air time + ft = Formatter(); + ft.Add(_loadedTrackDesign->total_air_time * 25); + DrawTextBasic(dpi, screenPos, STR_TOTAL_AIR_TIME, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + } + + if (GetRideTypeDescriptor(_loadedTrackDesign->type).HasFlag(RIDE_TYPE_FLAG_HAS_DROPS)) + { + // Drops + ft = Formatter(); + ft.Add(_loadedTrackDesign->drops & 0x3F); + DrawTextBasic(dpi, screenPos, STR_DROPS, ft); + screenPos.y += LIST_ROW_HEIGHT; + + // Drop height is multiplied by 0.75 + ft = Formatter(); + ft.Add((_loadedTrackDesign->highest_drop_height * 3) / 4); + DrawTextBasic(dpi, screenPos, STR_HIGHEST_DROP_HEIGHT, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + + if (_loadedTrackDesign->type != RIDE_TYPE_MINI_GOLF) + { + uint16_t inversions = _loadedTrackDesign->inversions & 0x1F; + if (inversions != 0) + { + ft = Formatter(); + ft.Add(inversions); + // Inversions + DrawTextBasic(dpi, screenPos, STR_INVERSIONS, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + } + screenPos.y += 4; + } + + if (_loadedTrackDesign->space_required_x != 0xFF) + { + // Space required + ft = Formatter(); + ft.Add(_loadedTrackDesign->space_required_x); + ft.Add(_loadedTrackDesign->space_required_y); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_SPACE_REQUIRED, ft); + screenPos.y += LIST_ROW_HEIGHT; + } + + if (_loadedTrackDesign->cost != 0) + { + ft = Formatter(); + ft.Add(_loadedTrackDesign->cost); + DrawTextBasic(dpi, screenPos, STR_TRACK_LIST_COST_AROUND, ft); + } + } + + void OnScrollDraw(const int32_t scrollIndex, DrawPixelInfo& dpi) override + { + uint8_t paletteIndex = ColourMapA[colours[0]].mid_light; + GfxClear(&dpi, paletteIndex); + + auto screenCoords = ScreenCoordsXY{ 0, 0 }; + size_t listIndex = 0; + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + if (_trackDesigns.empty()) + { + // No track designs + DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 1 }, STR_NO_TRACK_DESIGNS_OF_THIS_TYPE); + return; + } + } + else + { + // Build custom track item StringId stringId; if (listIndex == static_cast(selected_list_item)) { @@ -729,65 +707,91 @@ public: stringId = STR_BLACK_STRING; } - // Draw track name auto ft = Formatter(); - ft.Add(STR_TRACK_LIST_NAME_FORMAT); - ft.Add(_trackDesigns[i].name.c_str()); + ft.Add(STR_BUILD_CUSTOM_DESIGN); DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 1 }, stringId, ft); + screenCoords.y += SCROLLABLE_ROW_HEIGHT; + listIndex++; } - screenCoords.y += SCROLLABLE_ROW_HEIGHT; - listIndex++; + for (auto i : _filteredTrackIds) + { + if (screenCoords.y + SCROLLABLE_ROW_HEIGHT >= dpi.y && screenCoords.y < dpi.y + dpi.height) + { + StringId stringId; + if (listIndex == static_cast(selected_list_item)) + { + // Highlight + GfxFilterRect( + dpi, { screenCoords, { width, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }, + FilterPaletteID::PaletteDarken1); + stringId = STR_WINDOW_COLOUR_2_STRINGID; + } + else + { + stringId = STR_BLACK_STRING; + } + + // Draw track name + auto ft = Formatter(); + ft.Add(STR_TRACK_LIST_NAME_FORMAT); + ft.Add(_trackDesigns[i].name.c_str()); + DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 1 }, stringId, ft); + } + + screenCoords.y += SCROLLABLE_ROW_HEIGHT; + listIndex++; + } + } + + void OnResize() override + { + ResizeFrame(); + } + + void SetIsBeingUpdated(const bool beingUpdated) + { + _selectedItemIsBeingUpdated = beingUpdated; + } + + void ReloadTrackDesigns() + { + _reloadTrackDesigns = true; + } + }; + + WindowBase* WindowTrackListOpen(const RideSelection item) + { + WindowCloseConstructionWindows(); + ScreenCoordsXY screenPos{}; + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) + { + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + screenPos = { screenWidth / 2 - 300, std::max(TOP_TOOLBAR_HEIGHT + 1, screenHeight / 2 - 200) }; + } + else + { + screenPos = { 0, TOP_TOOLBAR_HEIGHT + 2 }; + } + return WindowCreate(WindowClass::TrackDesignList, WW, WH, 0, item); + } + + void WindowTrackDesignListReloadTracks() + { + auto* trackListWindow = static_cast(WindowFindByClass(WindowClass::TrackDesignList)); + if (trackListWindow != nullptr) + { + trackListWindow->ReloadTrackDesigns(); } } - void OnResize() override + void WindowTrackDesignListSetBeingUpdated(const bool beingUpdated) { - ResizeFrame(); + auto* trackListWindow = static_cast(WindowFindByClass(WindowClass::TrackDesignList)); + if (trackListWindow != nullptr) + { + trackListWindow->SetIsBeingUpdated(beingUpdated); + } } - - void SetIsBeingUpdated(const bool beingUpdated) - { - _selectedItemIsBeingUpdated = beingUpdated; - } - - void ReloadTrackDesigns() - { - _reloadTrackDesigns = true; - } -}; - -WindowBase* WindowTrackListOpen(const RideSelection item) -{ - WindowCloseConstructionWindows(); - ScreenCoordsXY screenPos{}; - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) - { - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - screenPos = { screenWidth / 2 - 300, std::max(TOP_TOOLBAR_HEIGHT + 1, screenHeight / 2 - 200) }; - } - else - { - screenPos = { 0, TOP_TOOLBAR_HEIGHT + 2 }; - } - return WindowCreate(WindowClass::TrackDesignList, WW, WH, 0, item); -} - -void WindowTrackDesignListReloadTracks() -{ - auto* trackListWindow = static_cast(WindowFindByClass(WindowClass::TrackDesignList)); - if (trackListWindow != nullptr) - { - trackListWindow->ReloadTrackDesigns(); - } -} - -void WindowTrackDesignListSetBeingUpdated(const bool beingUpdated) -{ - auto* trackListWindow = static_cast(WindowFindByClass(WindowClass::TrackDesignList)); - if (trackListWindow != nullptr) - { - trackListWindow->SetIsBeingUpdated(beingUpdated); - } -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Transparency.cpp b/src/openrct2-ui/windows/Transparency.cpp index 76e158d4f6..823e53ddb3 100644 --- a/src/openrct2-ui/windows/Transparency.cpp +++ b/src/openrct2-ui/windows/Transparency.cpp @@ -30,9 +30,9 @@ #include #include -using namespace OpenRCT2; - -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WINDOW_TRANSPARENCY_WIDGET_IDX { WIDX_BACKGROUND, @@ -87,176 +87,177 @@ static Widget _transparancyWidgets[] = { kWidgetsEnd }, }; -// clang-format on + // clang-format on -class TransparencyWindow final : public Window -{ -private: -public: - void OnOpen() override + class TransparencyWindow final : public Window { - widgets = _transparancyWidgets; - WindowPushOthersBelow(*this); - - auto* w = WindowGetMain(); - if (w != nullptr) - windowPos.x = ((w->width / 2) - (width / 2)); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + private: + public: + void OnOpen() override { - case WIDX_CLOSE: - Close(); - break; - default: - ToggleViewportFlag(widgetIndex); - break; + widgets = _transparancyWidgets; + WindowPushOthersBelow(*this); + + auto* w = WindowGetMain(); + if (w != nullptr) + windowPos.x = ((w->width / 2) - (width / 2)); } - } - void OnPrepareDraw() override - { - uint32_t wflags = 0; - WindowBase* w = WindowGetMain(); + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + default: + ToggleViewportFlag(widgetIndex); + break; + } + } - pressed_widgets = 0; - disabled_widgets = 0; + void OnPrepareDraw() override + { + uint32_t wflags = 0; + WindowBase* w = WindowGetMain(); + + pressed_widgets = 0; + disabled_widgets = 0; + + if (w != nullptr) + wflags = w->viewport->flags; + + SetWidgetPressed(WIDX_HIDE_VEGETATION, (wflags & VIEWPORT_FLAG_HIDE_VEGETATION)); + SetWidgetPressed(WIDX_HIDE_SCENERY, (wflags & VIEWPORT_FLAG_HIDE_SCENERY)); + SetWidgetPressed(WIDX_HIDE_PATHS, (wflags & VIEWPORT_FLAG_HIDE_PATHS)); + SetWidgetPressed(WIDX_HIDE_RIDES, (wflags & VIEWPORT_FLAG_HIDE_RIDES)); + SetWidgetPressed(WIDX_HIDE_VEHICLES, (wflags & VIEWPORT_FLAG_HIDE_VEHICLES)); + SetWidgetPressed(WIDX_HIDE_SUPPORTS, (wflags & VIEWPORT_FLAG_HIDE_SUPPORTS)); + SetWidgetPressed(WIDX_HIDE_GUESTS, (wflags & VIEWPORT_FLAG_HIDE_GUESTS)); + SetWidgetPressed(WIDX_HIDE_STAFF, (wflags & VIEWPORT_FLAG_HIDE_STAFF)); + SetWidgetPressed(WIDX_INVISIBLE_VEGETATION, (wflags & VIEWPORT_FLAG_INVISIBLE_VEGETATION)); + SetWidgetPressed(WIDX_INVISIBLE_SCENERY, (wflags & VIEWPORT_FLAG_INVISIBLE_SCENERY)); + SetWidgetPressed(WIDX_INVISIBLE_PATHS, (wflags & VIEWPORT_FLAG_INVISIBLE_PATHS)); + SetWidgetPressed(WIDX_INVISIBLE_RIDES, (wflags & VIEWPORT_FLAG_INVISIBLE_RIDES)); + SetWidgetPressed(WIDX_INVISIBLE_VEHICLES, (wflags & VIEWPORT_FLAG_INVISIBLE_VEHICLES)); + SetWidgetPressed(WIDX_INVISIBLE_SUPPORTS, (wflags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS)); + + for (WidgetIndex i = WIDX_INVISIBLE_VEGETATION; i <= WIDX_INVISIBLE_SUPPORTS; i++) + { + widgets[i].image = ImageId(IsWidgetPressed(i) ? SPR_G2_BUTTON_HIDE_FULL : SPR_G2_BUTTON_HIDE_PARTIAL); + } + } + + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); + // Locate mechanic button image + const auto& widget = widgets[WIDX_HIDE_STAFF]; + auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; + auto image = ImageId(SPR_MECHANIC, COLOUR_BLACK, GetGameState().StaffMechanicColour); + GfxDrawSprite(dpi, image, screenCoords); + } + + private: + uint32_t ToggleTransparency(uint32_t wflags, uint32_t transparencyFlag, uint32_t seeThroughFlag) + { + wflags ^= transparencyFlag; + if (wflags & transparencyFlag) + { + wflags |= seeThroughFlag; + } + SaveInConfig(wflags); + return wflags; + } + + void ToggleViewportFlag(WidgetIndex widgetIndex) + { + uint32_t wflags = 0; + WindowBase* w = WindowGetMain(); + + if (w == nullptr) + return; - if (w != nullptr) wflags = w->viewport->flags; - SetWidgetPressed(WIDX_HIDE_VEGETATION, (wflags & VIEWPORT_FLAG_HIDE_VEGETATION)); - SetWidgetPressed(WIDX_HIDE_SCENERY, (wflags & VIEWPORT_FLAG_HIDE_SCENERY)); - SetWidgetPressed(WIDX_HIDE_PATHS, (wflags & VIEWPORT_FLAG_HIDE_PATHS)); - SetWidgetPressed(WIDX_HIDE_RIDES, (wflags & VIEWPORT_FLAG_HIDE_RIDES)); - SetWidgetPressed(WIDX_HIDE_VEHICLES, (wflags & VIEWPORT_FLAG_HIDE_VEHICLES)); - SetWidgetPressed(WIDX_HIDE_SUPPORTS, (wflags & VIEWPORT_FLAG_HIDE_SUPPORTS)); - SetWidgetPressed(WIDX_HIDE_GUESTS, (wflags & VIEWPORT_FLAG_HIDE_GUESTS)); - SetWidgetPressed(WIDX_HIDE_STAFF, (wflags & VIEWPORT_FLAG_HIDE_STAFF)); - SetWidgetPressed(WIDX_INVISIBLE_VEGETATION, (wflags & VIEWPORT_FLAG_INVISIBLE_VEGETATION)); - SetWidgetPressed(WIDX_INVISIBLE_SCENERY, (wflags & VIEWPORT_FLAG_INVISIBLE_SCENERY)); - SetWidgetPressed(WIDX_INVISIBLE_PATHS, (wflags & VIEWPORT_FLAG_INVISIBLE_PATHS)); - SetWidgetPressed(WIDX_INVISIBLE_RIDES, (wflags & VIEWPORT_FLAG_INVISIBLE_RIDES)); - SetWidgetPressed(WIDX_INVISIBLE_VEHICLES, (wflags & VIEWPORT_FLAG_INVISIBLE_VEHICLES)); - SetWidgetPressed(WIDX_INVISIBLE_SUPPORTS, (wflags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS)); + switch (widgetIndex) + { + case WIDX_HIDE_RIDES: + wflags ^= VIEWPORT_FLAG_HIDE_RIDES; + break; + case WIDX_HIDE_VEHICLES: + wflags ^= VIEWPORT_FLAG_HIDE_VEHICLES; + break; + case WIDX_HIDE_SCENERY: + wflags ^= VIEWPORT_FLAG_HIDE_SCENERY; + break; + case WIDX_HIDE_VEGETATION: + wflags ^= VIEWPORT_FLAG_HIDE_VEGETATION; + break; + case WIDX_HIDE_PATHS: + wflags ^= VIEWPORT_FLAG_HIDE_PATHS; + break; + case WIDX_HIDE_SUPPORTS: + wflags ^= VIEWPORT_FLAG_HIDE_SUPPORTS; + break; + case WIDX_INVISIBLE_RIDES: + wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_RIDES, VIEWPORT_FLAG_HIDE_RIDES); + break; + case WIDX_INVISIBLE_VEHICLES: + wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_VEHICLES, VIEWPORT_FLAG_HIDE_VEHICLES); + break; + case WIDX_INVISIBLE_SCENERY: + wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_SCENERY, VIEWPORT_FLAG_HIDE_SCENERY); + break; + case WIDX_INVISIBLE_VEGETATION: + wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_VEGETATION, VIEWPORT_FLAG_HIDE_VEGETATION); + break; + case WIDX_INVISIBLE_PATHS: + wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_PATHS, VIEWPORT_FLAG_HIDE_PATHS); + break; + case WIDX_INVISIBLE_SUPPORTS: + wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_SUPPORTS, VIEWPORT_FLAG_HIDE_SUPPORTS); + break; + case WIDX_HIDE_GUESTS: + wflags ^= VIEWPORT_FLAG_HIDE_GUESTS; + break; + case WIDX_HIDE_STAFF: + wflags ^= VIEWPORT_FLAG_HIDE_STAFF; + break; + default: + return; + } - for (WidgetIndex i = WIDX_INVISIBLE_VEGETATION; i <= WIDX_INVISIBLE_SUPPORTS; i++) - { - widgets[i].image = ImageId(IsWidgetPressed(i) ? SPR_G2_BUTTON_HIDE_FULL : SPR_G2_BUTTON_HIDE_PARTIAL); - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); - // Locate mechanic button image - const auto& widget = widgets[WIDX_HIDE_STAFF]; - auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.top }; - auto image = ImageId(SPR_MECHANIC, COLOUR_BLACK, GetGameState().StaffMechanicColour); - GfxDrawSprite(dpi, image, screenCoords); - } - -private: - uint32_t ToggleTransparency(uint32_t wflags, uint32_t transparencyFlag, uint32_t seeThroughFlag) - { - wflags ^= transparencyFlag; - if (wflags & transparencyFlag) - { - wflags |= seeThroughFlag; - } - SaveInConfig(wflags); - return wflags; - } - - void ToggleViewportFlag(WidgetIndex widgetIndex) - { - uint32_t wflags = 0; - WindowBase* w = WindowGetMain(); - - if (w == nullptr) - return; - - wflags = w->viewport->flags; - - switch (widgetIndex) - { - case WIDX_HIDE_RIDES: - wflags ^= VIEWPORT_FLAG_HIDE_RIDES; - break; - case WIDX_HIDE_VEHICLES: - wflags ^= VIEWPORT_FLAG_HIDE_VEHICLES; - break; - case WIDX_HIDE_SCENERY: - wflags ^= VIEWPORT_FLAG_HIDE_SCENERY; - break; - case WIDX_HIDE_VEGETATION: - wflags ^= VIEWPORT_FLAG_HIDE_VEGETATION; - break; - case WIDX_HIDE_PATHS: - wflags ^= VIEWPORT_FLAG_HIDE_PATHS; - break; - case WIDX_HIDE_SUPPORTS: - wflags ^= VIEWPORT_FLAG_HIDE_SUPPORTS; - break; - case WIDX_INVISIBLE_RIDES: - wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_RIDES, VIEWPORT_FLAG_HIDE_RIDES); - break; - case WIDX_INVISIBLE_VEHICLES: - wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_VEHICLES, VIEWPORT_FLAG_HIDE_VEHICLES); - break; - case WIDX_INVISIBLE_SCENERY: - wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_SCENERY, VIEWPORT_FLAG_HIDE_SCENERY); - break; - case WIDX_INVISIBLE_VEGETATION: - wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_VEGETATION, VIEWPORT_FLAG_HIDE_VEGETATION); - break; - case WIDX_INVISIBLE_PATHS: - wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_PATHS, VIEWPORT_FLAG_HIDE_PATHS); - break; - case WIDX_INVISIBLE_SUPPORTS: - wflags = ToggleTransparency(wflags, VIEWPORT_FLAG_INVISIBLE_SUPPORTS, VIEWPORT_FLAG_HIDE_SUPPORTS); - break; - case WIDX_HIDE_GUESTS: - wflags ^= VIEWPORT_FLAG_HIDE_GUESTS; - break; - case WIDX_HIDE_STAFF: - wflags ^= VIEWPORT_FLAG_HIDE_STAFF; - break; - default: + if (w->viewport->flags == wflags) return; + + w->viewport->flags = wflags; + w->Invalidate(); } - if (w->viewport->flags == wflags) - return; + void SaveInConfig(uint32_t wflags) + { + gConfigGeneral.InvisibleRides = wflags & VIEWPORT_FLAG_INVISIBLE_RIDES; + gConfigGeneral.InvisibleVehicles = wflags & VIEWPORT_FLAG_INVISIBLE_VEHICLES; + gConfigGeneral.InvisibleScenery = wflags & VIEWPORT_FLAG_INVISIBLE_SCENERY; + gConfigGeneral.InvisibleTrees = wflags & VIEWPORT_FLAG_INVISIBLE_VEGETATION; + gConfigGeneral.InvisiblePaths = wflags & VIEWPORT_FLAG_INVISIBLE_PATHS; + gConfigGeneral.InvisibleSupports = wflags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS; + ConfigSaveDefault(); + } - w->viewport->flags = wflags; - w->Invalidate(); - } + void OnResize() override + { + ResizeFrame(); + } + }; - void SaveInConfig(uint32_t wflags) + WindowBase* WindowTransparencyOpen() { - gConfigGeneral.InvisibleRides = wflags & VIEWPORT_FLAG_INVISIBLE_RIDES; - gConfigGeneral.InvisibleVehicles = wflags & VIEWPORT_FLAG_INVISIBLE_VEHICLES; - gConfigGeneral.InvisibleScenery = wflags & VIEWPORT_FLAG_INVISIBLE_SCENERY; - gConfigGeneral.InvisibleTrees = wflags & VIEWPORT_FLAG_INVISIBLE_VEGETATION; - gConfigGeneral.InvisiblePaths = wflags & VIEWPORT_FLAG_INVISIBLE_PATHS; - gConfigGeneral.InvisibleSupports = wflags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS; - ConfigSaveDefault(); + auto* window = WindowBringToFrontByClass(WindowClass::Transparency); + if (window == nullptr) + window = WindowCreate(WindowClass::Transparency, ScreenCoordsXY(32, 32), WW, WH); + + return window; } - - void OnResize() override - { - ResizeFrame(); - } -}; - -WindowBase* WindowTransparencyOpen() -{ - auto* window = WindowBringToFrontByClass(WindowClass::Transparency); - if (window == nullptr) - window = WindowCreate(WindowClass::Transparency, ScreenCoordsXY(32, 32), WW, WH); - - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/ViewClipping.cpp b/src/openrct2-ui/windows/ViewClipping.cpp index c3a7e8513a..cf4815ba61 100644 --- a/src/openrct2-ui/windows/ViewClipping.cpp +++ b/src/openrct2-ui/windows/ViewClipping.cpp @@ -21,7 +21,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowViewClippingWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -63,350 +65,351 @@ static Widget _viewClippingWidgets[] = { #pragma endregion -// clang-format on -class ViewClippingWindow final : public Window -{ -private: - CoordsXY _selectionStart; - CoordsXY _previousClipSelectionA; - CoordsXY _previousClipSelectionB; - bool _toolActive{ false }; - bool _dragging{ false }; - static inline DisplayType _clipHeightDisplayType; - -public: - void OnCloseButton() + // clang-format on + class ViewClippingWindow final : public Window { - OnClose(); - } + private: + CoordsXY _selectionStart; + CoordsXY _previousClipSelectionA; + CoordsXY _previousClipSelectionB; + bool _toolActive{ false }; + bool _dragging{ false }; + static inline DisplayType _clipHeightDisplayType; - void OnMouseUp(WidgetIndex widgetIndex) override - { - // mouseup appears to be used for buttons, checkboxes - switch (widgetIndex) + public: + void OnCloseButton() { - case WIDX_CLOSE: - WindowClose(*this); - break; - case WIDX_CLIP_CHECKBOX_ENABLE: + OnClose(); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + // mouseup appears to be used for buttons, checkboxes + switch (widgetIndex) { - // Toggle height clipping. + case WIDX_CLOSE: + WindowClose(*this); + break; + case WIDX_CLIP_CHECKBOX_ENABLE: + { + // Toggle height clipping. + WindowBase* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + { + mainWindow->viewport->flags ^= VIEWPORT_FLAG_CLIP_VIEW; + mainWindow->Invalidate(); + } + this->Invalidate(); + break; + } + case WIDX_CLIP_HEIGHT_VALUE: + // Toggle display of the cut height value in RAW vs UNITS + if (_clipHeightDisplayType == DisplayType::DisplayRaw) + { + _clipHeightDisplayType = DisplayType::DisplayUnits; + } + else + { + _clipHeightDisplayType = DisplayType::DisplayRaw; + } + this->Invalidate(); + break; + case WIDX_CLIP_SELECTOR: + // Activate the selection tool + ToolSet(*this, WIDX_BACKGROUND, Tool::Crosshair); + _toolActive = true; + _dragging = false; + + // Reset clip selection to show all tiles + _previousClipSelectionA = gClipSelectionA; + _previousClipSelectionB = gClipSelectionB; + gClipSelectionA = { 0, 0 }; + gClipSelectionB = { MAXIMUM_MAP_SIZE_BIG - 1, MAXIMUM_MAP_SIZE_BIG - 1 }; + GfxInvalidateScreen(); + break; + case WIDX_CLIP_CLEAR: + if (IsActive()) + { + _toolActive = false; + ToolCancel(); + } + gClipSelectionA = { 0, 0 }; + gClipSelectionB = { MAXIMUM_MAP_SIZE_BIG - 1, MAXIMUM_MAP_SIZE_BIG - 1 }; + GfxInvalidateScreen(); + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + WindowBase* mainWindow; + + switch (widgetIndex) + { + case WIDX_CLIP_HEIGHT_INCREASE: + if (gClipHeight < 255) + SetClipHeight(gClipHeight + 1); + mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + mainWindow->Invalidate(); + break; + case WIDX_CLIP_HEIGHT_DECREASE: + if (gClipHeight > 0) + SetClipHeight(gClipHeight - 1); + mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + mainWindow->Invalidate(); + break; + } + } + + void OnUpdate() override + { + const auto& widget = widgets[WIDX_CLIP_HEIGHT_SLIDER]; + const ScrollBar* const scroll = &this->scrolls[0]; + const int16_t scroll_width = widget.width() - 1; + const uint8_t clip_height = static_cast( + (static_cast(scroll->h_left) / (scroll->h_right - scroll_width)) * 255); + if (clip_height != gClipHeight) + { + gClipHeight = clip_height; + + // Update the main window accordingly. WindowBase* mainWindow = WindowGetMain(); if (mainWindow != nullptr) { - mainWindow->viewport->flags ^= VIEWPORT_FLAG_CLIP_VIEW; mainWindow->Invalidate(); } - this->Invalidate(); - break; } - case WIDX_CLIP_HEIGHT_VALUE: - // Toggle display of the cut height value in RAW vs UNITS - if (_clipHeightDisplayType == DisplayType::DisplayRaw) - { - _clipHeightDisplayType = DisplayType::DisplayUnits; - } - else - { - _clipHeightDisplayType = DisplayType::DisplayRaw; - } - this->Invalidate(); - break; - case WIDX_CLIP_SELECTOR: - // Activate the selection tool - ToolSet(*this, WIDX_BACKGROUND, Tool::Crosshair); - _toolActive = true; - _dragging = false; - // Reset clip selection to show all tiles - _previousClipSelectionA = gClipSelectionA; - _previousClipSelectionB = gClipSelectionB; - gClipSelectionA = { 0, 0 }; - gClipSelectionB = { MAXIMUM_MAP_SIZE_BIG - 1, MAXIMUM_MAP_SIZE_BIG - 1 }; - GfxInvalidateScreen(); - break; - case WIDX_CLIP_CLEAR: - if (IsActive()) - { - _toolActive = false; - ToolCancel(); - } - gClipSelectionA = { 0, 0 }; - gClipSelectionB = { MAXIMUM_MAP_SIZE_BIG - 1, MAXIMUM_MAP_SIZE_BIG - 1 }; - GfxInvalidateScreen(); - break; + // Restore previous selection if the tool has been interrupted + if (_toolActive && !IsActive()) + { + _toolActive = false; + gClipSelectionA = _previousClipSelectionA; + gClipSelectionB = _previousClipSelectionB; + } + + WidgetInvalidate(*this, WIDX_CLIP_HEIGHT_SLIDER); } - } - void OnMouseDown(WidgetIndex widgetIndex) override - { - WindowBase* mainWindow; - - switch (widgetIndex) + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - case WIDX_CLIP_HEIGHT_INCREASE: - if (gClipHeight < 255) - SetClipHeight(gClipHeight + 1); - mainWindow = WindowGetMain(); - if (mainWindow != nullptr) - mainWindow->Invalidate(); - break; - case WIDX_CLIP_HEIGHT_DECREASE: - if (gClipHeight > 0) - SetClipHeight(gClipHeight - 1); - mainWindow = WindowGetMain(); - if (mainWindow != nullptr) - mainWindow->Invalidate(); - break; + if (_dragging) + { + return; + } + + int32_t direction; + auto mapCoords = ScreenPosToMapPos(screenCoords, &direction); + if (mapCoords.has_value()) + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + MapInvalidateTileFull(gMapSelectPositionA); + gMapSelectPositionA = gMapSelectPositionB = mapCoords.value(); + MapInvalidateTileFull(mapCoords.value()); + gMapSelectType = MAP_SELECT_TYPE_FULL; + } } - } - void OnUpdate() override - { - const auto& widget = widgets[WIDX_CLIP_HEIGHT_SLIDER]; - const ScrollBar* const scroll = &this->scrolls[0]; - const int16_t scroll_width = widget.width() - 1; - const uint8_t clip_height = static_cast( - (static_cast(scroll->h_left) / (scroll->h_right - scroll_width)) * 255); - if (clip_height != gClipHeight) + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override { - gClipHeight = clip_height; + int32_t direction; + auto mapCoords = ScreenPosToMapPos(screenCoords, &direction); + if (mapCoords.has_value()) + { + _dragging = true; + _selectionStart = mapCoords.value(); + } + } + + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + if (!_dragging) + { + return; + } + + int32_t direction; + auto mapCoords = ScreenPosToMapPos(screenCoords, &direction); + if (mapCoords) + { + MapInvalidateSelectionRect(); + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + gMapSelectPositionA.x = std::min(_selectionStart.x, mapCoords->x); + gMapSelectPositionB.x = std::max(_selectionStart.x, mapCoords->x); + gMapSelectPositionA.y = std::min(_selectionStart.y, mapCoords->y); + gMapSelectPositionB.y = std::max(_selectionStart.y, mapCoords->y); + gMapSelectType = MAP_SELECT_TYPE_FULL; + MapInvalidateSelectionRect(); + } + } + + void OnToolUp(WidgetIndex, const ScreenCoordsXY&) override + { + gClipSelectionA = gMapSelectPositionA; + gClipSelectionB = gMapSelectPositionB; + _toolActive = false; + ToolCancel(); + GfxInvalidateScreen(); + } + + void OnPrepareDraw() override + { + WidgetScrollUpdateThumbs(*this, WIDX_CLIP_HEIGHT_SLIDER); - // Update the main window accordingly. WindowBase* mainWindow = WindowGetMain(); if (mainWindow != nullptr) { - mainWindow->Invalidate(); + WidgetSetCheckboxValue(*this, WIDX_CLIP_CHECKBOX_ENABLE, mainWindow->viewport->flags & VIEWPORT_FLAG_CLIP_VIEW); + } + + if (IsActive()) + { + this->pressed_widgets |= 1uLL << WIDX_CLIP_SELECTOR; + } + else + { + this->pressed_widgets &= ~(1uLL << WIDX_CLIP_SELECTOR); } } - // Restore previous selection if the tool has been interrupted - if (_toolActive && !IsActive()) + void OnDraw(DrawPixelInfo& dpi) override { - _toolActive = false; - gClipSelectionA = _previousClipSelectionA; - gClipSelectionB = _previousClipSelectionB; - } + WindowDrawWidgets(*this, dpi); - WidgetInvalidate(*this, WIDX_CLIP_HEIGHT_SLIDER); - } + // Clip height value + auto screenCoords = this->windowPos + ScreenCoordsXY{ 8, this->widgets[WIDX_CLIP_HEIGHT_VALUE].top }; + DrawTextBasic(dpi, screenCoords, STR_VIEW_CLIPPING_HEIGHT_VALUE, {}, { this->colours[0] }); - void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (_dragging) - { - return; - } + screenCoords = this->windowPos + + ScreenCoordsXY{ this->widgets[WIDX_CLIP_HEIGHT_VALUE].left + 1, this->widgets[WIDX_CLIP_HEIGHT_VALUE].top }; - int32_t direction; - auto mapCoords = ScreenPosToMapPos(screenCoords, &direction); - if (mapCoords.has_value()) - { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - MapInvalidateTileFull(gMapSelectPositionA); - gMapSelectPositionA = gMapSelectPositionB = mapCoords.value(); - MapInvalidateTileFull(mapCoords.value()); - gMapSelectType = MAP_SELECT_TYPE_FULL; - } - } - - void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - int32_t direction; - auto mapCoords = ScreenPosToMapPos(screenCoords, &direction); - if (mapCoords.has_value()) - { - _dragging = true; - _selectionStart = mapCoords.value(); - } - } - - void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override - { - if (!_dragging) - { - return; - } - - int32_t direction; - auto mapCoords = ScreenPosToMapPos(screenCoords, &direction); - if (mapCoords) - { - MapInvalidateSelectionRect(); - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - gMapSelectPositionA.x = std::min(_selectionStart.x, mapCoords->x); - gMapSelectPositionB.x = std::max(_selectionStart.x, mapCoords->x); - gMapSelectPositionA.y = std::min(_selectionStart.y, mapCoords->y); - gMapSelectPositionB.y = std::max(_selectionStart.y, mapCoords->y); - gMapSelectType = MAP_SELECT_TYPE_FULL; - MapInvalidateSelectionRect(); - } - } - - void OnToolUp(WidgetIndex, const ScreenCoordsXY&) override - { - gClipSelectionA = gMapSelectPositionA; - gClipSelectionB = gMapSelectPositionB; - _toolActive = false; - ToolCancel(); - GfxInvalidateScreen(); - } - - void OnPrepareDraw() override - { - WidgetScrollUpdateThumbs(*this, WIDX_CLIP_HEIGHT_SLIDER); - - WindowBase* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) - { - WidgetSetCheckboxValue(*this, WIDX_CLIP_CHECKBOX_ENABLE, mainWindow->viewport->flags & VIEWPORT_FLAG_CLIP_VIEW); - } - - if (IsActive()) - { - this->pressed_widgets |= 1uLL << WIDX_CLIP_SELECTOR; - } - else - { - this->pressed_widgets &= ~(1uLL << WIDX_CLIP_SELECTOR); - } - } - - void OnDraw(DrawPixelInfo& dpi) override - { - WindowDrawWidgets(*this, dpi); - - // Clip height value - auto screenCoords = this->windowPos + ScreenCoordsXY{ 8, this->widgets[WIDX_CLIP_HEIGHT_VALUE].top }; - DrawTextBasic(dpi, screenCoords, STR_VIEW_CLIPPING_HEIGHT_VALUE, {}, { this->colours[0] }); - - screenCoords = this->windowPos - + ScreenCoordsXY{ this->widgets[WIDX_CLIP_HEIGHT_VALUE].left + 1, this->widgets[WIDX_CLIP_HEIGHT_VALUE].top }; - - switch (_clipHeightDisplayType) - { - case DisplayType::DisplayRaw: - default: + switch (_clipHeightDisplayType) { - auto ft = Formatter(); - ft.Add(static_cast(gClipHeight)); - - // Printing the raw value. - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { this->colours[0] }); - break; - } - case DisplayType::DisplayUnits: - { - // Print the value in the configured height label type: - if (gConfigGeneral.ShowHeightAsUnits) + case DisplayType::DisplayRaw: + default: { - // Height label is Units. auto ft = Formatter(); - ft.Add(static_cast(FIXED_1DP(gClipHeight, 0) / 2 - FIXED_1DP(7, 0))); - DrawTextBasic( - dpi, screenCoords, STR_UNIT1DP_NO_SUFFIX, ft, - { this->colours[0] }); // Printing the value in Height Units. + ft.Add(static_cast(gClipHeight)); + + // Printing the raw value. + DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { this->colours[0] }); + break; } - else + case DisplayType::DisplayUnits: { - // Height label is Real Values. - // Print the value in the configured measurement units. - switch (gConfigGeneral.MeasurementFormat) + // Print the value in the configured height label type: + if (gConfigGeneral.ShowHeightAsUnits) { - case MeasurementFormat::Metric: - case MeasurementFormat::SI: + // Height label is Units. + auto ft = Formatter(); + ft.Add(static_cast(FIXED_1DP(gClipHeight, 0) / 2 - FIXED_1DP(7, 0))); + DrawTextBasic( + dpi, screenCoords, STR_UNIT1DP_NO_SUFFIX, ft, + { this->colours[0] }); // Printing the value in Height Units. + } + else + { + // Height label is Real Values. + // Print the value in the configured measurement units. + switch (gConfigGeneral.MeasurementFormat) { - auto ft = Formatter(); - ft.Add( - static_cast(FIXED_2DP(gClipHeight, 0) / 2 * 1.5f - FIXED_2DP(10, 50))); - DrawTextBasic(dpi, screenCoords, STR_UNIT2DP_SUFFIX_METRES, ft, { this->colours[0] }); - break; - } - case MeasurementFormat::Imperial: - { - auto ft = Formatter(); - ft.Add( - static_cast(FIXED_1DP(gClipHeight, 0) / 2.0f * 5 - FIXED_1DP(35, 0))); - DrawTextBasic(dpi, screenCoords, STR_UNIT1DP_SUFFIX_FEET, ft, { this->colours[0] }); - break; + case MeasurementFormat::Metric: + case MeasurementFormat::SI: + { + auto ft = Formatter(); + ft.Add( + static_cast(FIXED_2DP(gClipHeight, 0) / 2 * 1.5f - FIXED_2DP(10, 50))); + DrawTextBasic(dpi, screenCoords, STR_UNIT2DP_SUFFIX_METRES, ft, { this->colours[0] }); + break; + } + case MeasurementFormat::Imperial: + { + auto ft = Formatter(); + ft.Add( + static_cast(FIXED_1DP(gClipHeight, 0) / 2.0f * 5 - FIXED_1DP(35, 0))); + DrawTextBasic(dpi, screenCoords, STR_UNIT1DP_SUFFIX_FEET, ft, { this->colours[0] }); + break; + } } } } } } - } - ScreenSize OnScrollGetSize(int32_t scrollIndex) override - { - return { 1000, 0 }; - } - - void OnOpen() override - { - this->widgets = _viewClippingWidgets; - this->hold_down_widgets = (1uLL << WIDX_CLIP_HEIGHT_INCREASE) | (1uL << WIDX_CLIP_HEIGHT_DECREASE); - WindowInitScrollWidgets(*this); - - _clipHeightDisplayType = DisplayType::DisplayUnits; - - // Initialise the clip height slider from the current clip height value. - this->SetClipHeight(gClipHeight); - - WindowPushOthersBelow(*this); - - // Get the main viewport to set the view clipping flag. - WindowBase* mainWindow = WindowGetMain(); - - // Turn on view clipping when the window is opened. - if (mainWindow != nullptr) + ScreenSize OnScrollGetSize(int32_t scrollIndex) override { - mainWindow->viewport->flags |= VIEWPORT_FLAG_CLIP_VIEW; - mainWindow->Invalidate(); + return { 1000, 0 }; } - } - void OnResize() override - { - ResizeFrame(); - } - -private: - void OnClose() override - { - // Turn off view clipping when the window is closed. - WindowBase* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) + void OnOpen() override { - mainWindow->viewport->flags &= ~VIEWPORT_FLAG_CLIP_VIEW; - mainWindow->Invalidate(); + this->widgets = _viewClippingWidgets; + this->hold_down_widgets = (1uLL << WIDX_CLIP_HEIGHT_INCREASE) | (1uL << WIDX_CLIP_HEIGHT_DECREASE); + WindowInitScrollWidgets(*this); + + _clipHeightDisplayType = DisplayType::DisplayUnits; + + // Initialise the clip height slider from the current clip height value. + this->SetClipHeight(gClipHeight); + + WindowPushOthersBelow(*this); + + // Get the main viewport to set the view clipping flag. + WindowBase* mainWindow = WindowGetMain(); + + // Turn on view clipping when the window is opened. + if (mainWindow != nullptr) + { + mainWindow->viewport->flags |= VIEWPORT_FLAG_CLIP_VIEW; + mainWindow->Invalidate(); + } } - } - void SetClipHeight(const uint8_t clipHeight) - { - gClipHeight = clipHeight; - const auto& widget = widgets[WIDX_CLIP_HEIGHT_SLIDER]; - const float clip_height_ratio = static_cast(gClipHeight) / 255; - this->scrolls[0].h_left = static_cast( - std::ceil(clip_height_ratio * (this->scrolls[0].h_right - (widget.width() - 1)))); - } + void OnResize() override + { + ResizeFrame(); + } - bool IsActive() - { - if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) - return false; - if (gCurrentToolWidget.window_classification != WindowClass::ViewClipping) - return false; - return _toolActive; - } -}; + private: + void OnClose() override + { + // Turn off view clipping when the window is closed. + WindowBase* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + { + mainWindow->viewport->flags &= ~VIEWPORT_FLAG_CLIP_VIEW; + mainWindow->Invalidate(); + } + } -WindowBase* WindowViewClippingOpen() -{ - auto* window = WindowBringToFrontByClass(WindowClass::ViewClipping); - if (window == nullptr) + void SetClipHeight(const uint8_t clipHeight) + { + gClipHeight = clipHeight; + const auto& widget = widgets[WIDX_CLIP_HEIGHT_SLIDER]; + const float clip_height_ratio = static_cast(gClipHeight) / 255; + this->scrolls[0].h_left = static_cast( + std::ceil(clip_height_ratio * (this->scrolls[0].h_right - (widget.width() - 1)))); + } + + bool IsActive() + { + if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))) + return false; + if (gCurrentToolWidget.window_classification != WindowClass::ViewClipping) + return false; + return _toolActive; + } + }; + + WindowBase* WindowViewClippingOpen() { - window = WindowCreate(WindowClass::ViewClipping, ScreenCoordsXY(32, 32), WW, WH); + auto* window = WindowBringToFrontByClass(WindowClass::ViewClipping); + if (window == nullptr) + { + window = WindowCreate(WindowClass::ViewClipping, ScreenCoordsXY(32, 32), WW, WH); + } + return window; } - return window; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Viewport.cpp b/src/openrct2-ui/windows/Viewport.cpp index 9ddcaa4072..22091cec83 100644 --- a/src/openrct2-ui/windows/Viewport.cpp +++ b/src/openrct2-ui/windows/Viewport.cpp @@ -17,7 +17,9 @@ #include #include -// clang-format off +namespace OpenRCT2::Ui::Windows +{ + // clang-format off enum WindowViewportWidgetIdx { WIDX_BACKGROUND, @@ -53,187 +55,188 @@ static Widget _viewportWidgets[] = kWidgetsEnd, }; -// clang-format on + // clang-format on -class ViewportWindow final : public Window -{ -private: - void GetFreeViewportNumber() + class ViewportWindow final : public Window { - number = 1; - WindowVisitEach([&](WindowBase* w) { - if (w != nullptr && w != this && w->classification == WindowClass::Viewport) + private: + void GetFreeViewportNumber() + { + number = 1; + WindowVisitEach([&](WindowBase* w) { + if (w != nullptr && w != this && w->classification == WindowClass::Viewport) + { + if (w->number >= number) + number = w->number + 1; + } + }); + } + + public: + void OnOpen() override + { + GetFreeViewportNumber(); + + widgets = _viewportWidgets; + + // Create viewport + ViewportCreate(this, windowPos, width, height, Focus(TileCoordsXYZ(128, 128, 0).ToCoordsXYZ())); + if (viewport == nullptr) { - if (w->number >= number) - number = w->number + 1; - } - }); - } - -public: - void OnOpen() override - { - GetFreeViewportNumber(); - - widgets = _viewportWidgets; - - // Create viewport - ViewportCreate(this, windowPos, width, height, Focus(TileCoordsXYZ(128, 128, 0).ToCoordsXYZ())); - if (viewport == nullptr) - { - Close(); - WindowErrorOpen("Unexpected Error", "Failed to create viewport window."); - return; - } - - auto* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) - { - Viewport* mainViewport = mainWindow->viewport; - int32_t x = mainViewport->viewPos.x + (mainViewport->view_width / 2); - int32_t y = mainViewport->viewPos.y + (mainViewport->view_height / 2); - savedViewPos = { x - (viewport->view_width / 2), y - (viewport->view_height / 2) }; - } - - viewport->flags |= VIEWPORT_FLAG_SOUND_ON | VIEWPORT_FLAG_INDEPEDENT_ROTATION; - - min_width = WW; - min_height = WH; - max_width = WW; - max_height = WH; - } - - void OnUpdate() override - { - auto* mainWindow = WindowGetMain(); - if (mainWindow == nullptr) - return; - - if (viewport != nullptr && viewport->flags != mainWindow->viewport->flags) - { - viewport->flags = mainWindow->viewport->flags | VIEWPORT_FLAG_INDEPEDENT_ROTATION; - Invalidate(); - } - - // Not sure how to invalidate part of the viewport that has changed, this will have to do for now - // widget_invalidate(*this, WIDX_VIEWPORT); - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_CLOSE: Close(); - break; - case WIDX_ZOOM_IN: - { - if (viewport != nullptr && viewport->zoom > ZoomLevel::min()) - { - viewport->zoom--; - Invalidate(); - } - break; + WindowErrorOpen("Unexpected Error", "Failed to create viewport window."); + return; } - case WIDX_ZOOM_OUT: + + auto* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) { - if (viewport != nullptr && viewport->zoom < ZoomLevel::max()) - { - viewport->zoom++; - Invalidate(); - } - break; + Viewport* mainViewport = mainWindow->viewport; + int32_t x = mainViewport->viewPos.x + (mainViewport->view_width / 2); + int32_t y = mainViewport->viewPos.y + (mainViewport->view_height / 2); + savedViewPos = { x - (viewport->view_width / 2), y - (viewport->view_height / 2) }; } - case WIDX_LOCATE: + + viewport->flags |= VIEWPORT_FLAG_SOUND_ON | VIEWPORT_FLAG_INDEPEDENT_ROTATION; + + min_width = WW; + min_height = WH; + max_width = WW; + max_height = WH; + } + + void OnUpdate() override + { + auto* mainWindow = WindowGetMain(); + if (mainWindow == nullptr) + return; + + if (viewport != nullptr && viewport->flags != mainWindow->viewport->flags) { - auto* mainWindow = WindowGetMain(); - if (mainWindow != nullptr) - { - auto info = GetMapCoordinatesFromPos( - { windowPos.x + (width / 2), windowPos.y + (height / 2) }, ViewportInteractionItemAll); - WindowScrollToLocation(*mainWindow, { info.Loc, TileElementHeight(info.Loc) }); - } - break; - } - case WIDX_ROTATE: - { - ViewportRotateSingle(this, 1); + viewport->flags = mainWindow->viewport->flags | VIEWPORT_FLAG_INDEPEDENT_ROTATION; Invalidate(); - break; + } + + // Not sure how to invalidate part of the viewport that has changed, this will have to do for now + // widget_invalidate(*this, WIDX_VIEWPORT); + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_ZOOM_IN: + { + if (viewport != nullptr && viewport->zoom > ZoomLevel::min()) + { + viewport->zoom--; + Invalidate(); + } + break; + } + case WIDX_ZOOM_OUT: + { + if (viewport != nullptr && viewport->zoom < ZoomLevel::max()) + { + viewport->zoom++; + Invalidate(); + } + break; + } + case WIDX_LOCATE: + { + auto* mainWindow = WindowGetMain(); + if (mainWindow != nullptr) + { + auto info = GetMapCoordinatesFromPos( + { windowPos.x + (width / 2), windowPos.y + (height / 2) }, ViewportInteractionItemAll); + WindowScrollToLocation(*mainWindow, { info.Loc, TileElementHeight(info.Loc) }); + } + break; + } + case WIDX_ROTATE: + { + ViewportRotateSingle(this, 1); + Invalidate(); + break; + } } } - } - void OnDraw(DrawPixelInfo& dpi) override - { - DrawWidgets(dpi); + void OnDraw(DrawPixelInfo& dpi) override + { + DrawWidgets(dpi); - // Draw viewport - if (viewport != nullptr) - WindowDrawViewport(dpi, *this); - } + // Draw viewport + if (viewport != nullptr) + WindowDrawViewport(dpi, *this); + } - void OnResize() override + void OnResize() override + { + int32_t screenWidth = ContextGetWidth(); + int32_t screenHeight = ContextGetHeight(); + + max_width = (screenWidth * 4) / 5; + max_height = (screenHeight * 4) / 5; + + min_width = WW; + min_height = WH; + + WindowSetResize(*this, min_width, min_height, max_width, max_height); + } + + void OnPrepareDraw() override + { + Widget* viewportWidget = &widgets[WIDX_VIEWPORT]; + + ResizeFrameWithPage(); + widgets[WIDX_ZOOM_IN].left = width - 27; + widgets[WIDX_ZOOM_IN].right = width - 2; + widgets[WIDX_ZOOM_OUT].left = width - 27; + widgets[WIDX_ZOOM_OUT].right = width - 2; + widgets[WIDX_LOCATE].left = width - 27; + widgets[WIDX_LOCATE].right = width - 2; + widgets[WIDX_ROTATE].left = width - 27; + widgets[WIDX_ROTATE].right = width - 2; + widgets[WIDX_VIEWPORT].right = widgets[WIDX_ZOOM_IN].left - 1; + widgets[WIDX_VIEWPORT].bottom = widgets[WIDX_BACKGROUND].bottom - 3; + + // Set title + Formatter::Common().Add(number); + + // Set disabled widgets + disabled_widgets = 0; + if (viewport != nullptr && viewport->zoom == ZoomLevel::min()) + disabled_widgets |= 1uLL << WIDX_ZOOM_IN; + if (viewport != nullptr && viewport->zoom >= ZoomLevel::max()) + disabled_widgets |= 1uLL << WIDX_ZOOM_OUT; + + if (viewport != nullptr) + { + viewport->pos = windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }; + viewport->width = widgets[WIDX_VIEWPORT].width() - 1; + viewport->height = widgets[WIDX_VIEWPORT].height() - 1; + viewport->view_width = viewport->zoom.ApplyTo(viewport->width); + viewport->view_height = viewport->zoom.ApplyTo(viewport->height); + } + } + }; + + WindowBase* WindowViewportOpen() { int32_t screenWidth = ContextGetWidth(); int32_t screenHeight = ContextGetHeight(); + int32_t width = (screenWidth / 2); + int32_t height = (screenHeight / 2); - max_width = (screenWidth * 4) / 5; - max_height = (screenHeight * 4) / 5; + auto* w = WindowCreate(WindowClass::Viewport, std::max(WW, width), std::max(WH, height), WF_RESIZABLE); - min_width = WW; - min_height = WH; - - WindowSetResize(*this, min_width, min_height, max_width, max_height); + if (w != nullptr) + return w; + return nullptr; } - - void OnPrepareDraw() override - { - Widget* viewportWidget = &widgets[WIDX_VIEWPORT]; - - ResizeFrameWithPage(); - widgets[WIDX_ZOOM_IN].left = width - 27; - widgets[WIDX_ZOOM_IN].right = width - 2; - widgets[WIDX_ZOOM_OUT].left = width - 27; - widgets[WIDX_ZOOM_OUT].right = width - 2; - widgets[WIDX_LOCATE].left = width - 27; - widgets[WIDX_LOCATE].right = width - 2; - widgets[WIDX_ROTATE].left = width - 27; - widgets[WIDX_ROTATE].right = width - 2; - widgets[WIDX_VIEWPORT].right = widgets[WIDX_ZOOM_IN].left - 1; - widgets[WIDX_VIEWPORT].bottom = widgets[WIDX_BACKGROUND].bottom - 3; - - // Set title - Formatter::Common().Add(number); - - // Set disabled widgets - disabled_widgets = 0; - if (viewport != nullptr && viewport->zoom == ZoomLevel::min()) - disabled_widgets |= 1uLL << WIDX_ZOOM_IN; - if (viewport != nullptr && viewport->zoom >= ZoomLevel::max()) - disabled_widgets |= 1uLL << WIDX_ZOOM_OUT; - - if (viewport != nullptr) - { - viewport->pos = windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 }; - viewport->width = widgets[WIDX_VIEWPORT].width() - 1; - viewport->height = widgets[WIDX_VIEWPORT].height() - 1; - viewport->view_width = viewport->zoom.ApplyTo(viewport->width); - viewport->view_height = viewport->zoom.ApplyTo(viewport->height); - } - } -}; - -WindowBase* WindowViewportOpen() -{ - int32_t screenWidth = ContextGetWidth(); - int32_t screenHeight = ContextGetHeight(); - int32_t width = (screenWidth / 2); - int32_t height = (screenHeight / 2); - - auto* w = WindowCreate(WindowClass::Viewport, std::max(WW, width), std::max(WH, height), WF_RESIZABLE); - - if (w != nullptr) - return w; - return nullptr; -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Water.cpp b/src/openrct2-ui/windows/Water.cpp index 28355ed396..fe82ebe890 100644 --- a/src/openrct2-ui/windows/Water.cpp +++ b/src/openrct2-ui/windows/Water.cpp @@ -18,13 +18,13 @@ #include #include -using namespace OpenRCT2; +namespace OpenRCT2::Ui::Windows +{ + static constexpr StringId WINDOW_TITLE = STR_WATER; + static constexpr int32_t WH = 77; + static constexpr int32_t WW = 76; -static constexpr StringId WINDOW_TITLE = STR_WATER; -static constexpr int32_t WH = 77; -static constexpr int32_t WW = 76; - -// clang-format off + // clang-format off enum WindowWaterWidgetIdx { WIDX_BACKGROUND, WIDX_TITLE, @@ -41,158 +41,160 @@ static Widget _waterWidgets[] = { MakeRemapWidget({43, 32}, {16, 16}, WindowWidgetType::TrnBtn, WindowColour::Tertiary, SPR_LAND_TOOL_INCREASE, STR_ADJUST_LARGER_WATER_TIP), // increment size kWidgetsEnd, }; -// clang-format on + // clang-format on -class WaterWindow final : public Window -{ -public: - void OnOpen() override + class WaterWindow final : public Window { - widgets = _waterWidgets; - hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); - WindowInitScrollWidgets(*this); - WindowPushOthersBelow(*this); - - gLandToolSize = 1; - gWaterToolRaiseCost = kMoney64Undefined; - gWaterToolLowerCost = kMoney64Undefined; - } - - void OnClose() override - { - // If the tool wasn't changed, turn tool off - if (WaterToolIsActive()) + public: + void OnOpen() override { - ToolCancel(); + widgets = _waterWidgets; + hold_down_widgets = (1uLL << WIDX_INCREMENT) | (1uLL << WIDX_DECREMENT); + WindowInitScrollWidgets(*this); + WindowPushOthersBelow(*this); + + gLandToolSize = 1; + gWaterToolRaiseCost = kMoney64Undefined; + gWaterToolLowerCost = kMoney64Undefined; } - } - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) + void OnClose() override { - case WIDX_CLOSE: + // If the tool wasn't changed, turn tool off + if (WaterToolIsActive()) + { + ToolCancel(); + } + } + + void OnMouseUp(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_PREVIEW: + InputSize(); + break; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_DECREMENT: + // Decrement land tool size + gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); + + // Invalidate the window + Invalidate(); + break; + case WIDX_INCREMENT: + // Increment land tool size + gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); + + // Invalidate the window + Invalidate(); + break; + } + } + + void OnUpdate() override + { + // Close window if another tool is open + if (!WaterToolIsActive()) + { Close(); - break; - case WIDX_PREVIEW: - InputSize(); - break; - } - } - - void OnMouseDown(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - case WIDX_DECREMENT: - // Decrement land tool size - gLandToolSize = std::max(kLandToolMinimumSize, gLandToolSize - 1); - - // Invalidate the window - Invalidate(); - break; - case WIDX_INCREMENT: - // Increment land tool size - gLandToolSize = std::min(kLandToolMaximumSize, gLandToolSize + 1); - - // Invalidate the window - Invalidate(); - break; - } - } - - void OnUpdate() override - { - // Close window if another tool is open - if (!WaterToolIsActive()) - { - Close(); - } - } - - void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override - { - int32_t size; - char* end; - - if (widgetIndex != WIDX_PREVIEW) - { - return; - } - - std::string textStr = std::string(text); - size = strtol(textStr.c_str(), &end, 10); - if (*end == '\0') - { - size = std::max(kLandToolMinimumSize, size); - size = std::min(kLandToolMaximumSize, size); - gLandToolSize = size; - - Invalidate(); - } - } - - void OnPrepareDraw() override - { - // Set the preview image button to be pressed down - SetWidgetPressed(WIDX_PREVIEW, true); - - // Update the preview image - widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); - } - - void OnDraw(DrawPixelInfo& dpi) override - { - auto screenCoords = ScreenCoordsXY{ windowPos.x + widgets[WIDX_PREVIEW].midX(), - windowPos.y + widgets[WIDX_PREVIEW].midY() }; - - DrawWidgets(dpi); - // Draw number for tool sizes bigger than 7 - if (gLandToolSize > kLandToolMaximumSizeWithSprite) - { - auto ft = Formatter(); - ft.Add(gLandToolSize); - DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); - } - - if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) - { - // Draw raise cost amount - screenCoords = { widgets[WIDX_PREVIEW].midX() + windowPos.x, widgets[WIDX_PREVIEW].bottom + windowPos.y + 5 }; - if (gWaterToolRaiseCost != kMoney64Undefined && gWaterToolRaiseCost != 0) - { - auto ft = Formatter(); - ft.Add(gWaterToolRaiseCost); - DrawTextBasic(dpi, screenCoords, STR_RAISE_COST_AMOUNT, ft, { TextAlignment::CENTRE }); - } - screenCoords.y += 10; - - // Draw lower cost amount - if (gWaterToolLowerCost != kMoney64Undefined && gWaterToolLowerCost != 0) - { - auto ft = Formatter(); - ft.Add(gWaterToolLowerCost); - DrawTextBasic(dpi, screenCoords, STR_LOWER_COST_AMOUNT, ft, { TextAlignment::CENTRE }); } } - } - void OnResize() override + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override + { + int32_t size; + char* end; + + if (widgetIndex != WIDX_PREVIEW) + { + return; + } + + std::string textStr = std::string(text); + size = strtol(textStr.c_str(), &end, 10); + if (*end == '\0') + { + size = std::max(kLandToolMinimumSize, size); + size = std::min(kLandToolMaximumSize, size); + gLandToolSize = size; + + Invalidate(); + } + } + + void OnPrepareDraw() override + { + // Set the preview image button to be pressed down + SetWidgetPressed(WIDX_PREVIEW, true); + + // Update the preview image + widgets[WIDX_PREVIEW].image = ImageId(LandTool::SizeToSpriteIndex(gLandToolSize)); + } + + void OnDraw(DrawPixelInfo& dpi) override + { + auto screenCoords = ScreenCoordsXY{ windowPos.x + widgets[WIDX_PREVIEW].midX(), + windowPos.y + widgets[WIDX_PREVIEW].midY() }; + + DrawWidgets(dpi); + // Draw number for tool sizes bigger than 7 + if (gLandToolSize > kLandToolMaximumSizeWithSprite) + { + auto ft = Formatter(); + ft.Add(gLandToolSize); + DrawTextBasic( + dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE }); + } + + if (!(GetGameState().ParkFlags & PARK_FLAGS_NO_MONEY)) + { + // Draw raise cost amount + screenCoords = { widgets[WIDX_PREVIEW].midX() + windowPos.x, widgets[WIDX_PREVIEW].bottom + windowPos.y + 5 }; + if (gWaterToolRaiseCost != kMoney64Undefined && gWaterToolRaiseCost != 0) + { + auto ft = Formatter(); + ft.Add(gWaterToolRaiseCost); + DrawTextBasic(dpi, screenCoords, STR_RAISE_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + } + screenCoords.y += 10; + + // Draw lower cost amount + if (gWaterToolLowerCost != kMoney64Undefined && gWaterToolLowerCost != 0) + { + auto ft = Formatter(); + ft.Add(gWaterToolLowerCost); + DrawTextBasic(dpi, screenCoords, STR_LOWER_COST_AMOUNT, ft, { TextAlignment::CENTRE }); + } + } + } + + void OnResize() override + { + ResizeFrame(); + } + + private: + void InputSize() + { + Formatter ft; + ft.Add(kLandToolMinimumSize); + ft.Add(kLandToolMaximumSize); + WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); + } + }; + + WindowBase* WindowWaterOpen() { - ResizeFrame(); + return WindowFocusOrCreate(WindowClass::Water, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); } - -private: - void InputSize() - { - Formatter ft; - ft.Add(kLandToolMinimumSize); - ft.Add(kLandToolMaximumSize); - WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3); - } -}; - -WindowBase* WindowWaterOpen() -{ - return WindowFocusOrCreate(WindowClass::Water, ScreenCoordsXY(ContextGetWidth() - WW, 29), WW, WH, 0); -} +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 87e217fd03..5c484af922 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -16,205 +16,208 @@ #include #include -using loadsave_callback = void (*)(int32_t result, const utf8* path); -using scenarioselect_callback = void (*)(const utf8* path); struct Peep; struct TileElement; struct Vehicle; +struct TrackDesign; enum class GuestListFilterType : int32_t; enum class ScatterToolDensity : uint8_t; +using loadsave_callback = void (*)(int32_t result, const utf8* path); +using scenarioselect_callback = void (*)(const utf8* path); -extern bool gWindowSceneryScatterEnabled; -extern uint16_t gWindowSceneryScatterSize; -extern ScatterToolDensity gWindowSceneryScatterDensity; -extern uint8_t gWindowSceneryPaintEnabled; -extern uint8_t gWindowSceneryRotation; -extern colour_t gWindowSceneryPrimaryColour; -extern colour_t gWindowScenerySecondaryColour; -extern colour_t gWindowSceneryTertiaryColour; -extern bool gWindowSceneryEyedropperEnabled; +namespace OpenRCT2::Ui::Windows +{ + extern bool gWindowSceneryScatterEnabled; + extern uint16_t gWindowSceneryScatterSize; + extern ScatterToolDensity gWindowSceneryScatterDensity; + extern uint8_t gWindowSceneryPaintEnabled; + extern uint8_t gWindowSceneryRotation; + extern colour_t gWindowSceneryPrimaryColour; + extern colour_t gWindowScenerySecondaryColour; + extern colour_t gWindowSceneryTertiaryColour; + extern bool gWindowSceneryEyedropperEnabled; -WindowBase* WindowAboutOpen(); -void WindowCampaignRefreshRides(); -WindowBase* WindowChangelogOpen(int personality); -WindowBase* WindowCheatsOpen(); -WindowBase* WindowClearSceneryOpen(); -WindowBase* CustomCurrencyWindowOpen(); -WindowBase* WindowDebugPaintOpen(); -WindowBase* WindowEditorInventionsListOpen(); -WindowBase* WindowEditorMainOpen(); -WindowBase* WindowEditorObjectiveOptionsOpen(); -WindowBase* WindowEditorScenarioOptionsOpen(); -WindowBase* WindowFootpathOpen(); -void WindowFootpathResetSelectedPath(); -WindowBase* WindowGuestOpen(Peep* peep); -WindowBase* WindowLandOpen(); -WindowBase* WindowLandRightsOpen(); -WindowBase* WindowMainOpen(); -WindowBase* WindowMapgenOpen(); -WindowBase* WindowMultiplayerOpen(); -WindowBase* WindowNewsOpen(); -WindowBase* WindowNewsOptionsOpen(); -WindowBase* WindowOptionsOpen(); -WindowBase* WindowSavePromptOpen(); + WindowBase* WindowAboutOpen(); + void WindowCampaignRefreshRides(); + WindowBase* WindowChangelogOpen(int personality); + WindowBase* WindowCheatsOpen(); + WindowBase* WindowClearSceneryOpen(); + WindowBase* CustomCurrencyWindowOpen(); + WindowBase* WindowDebugPaintOpen(); + WindowBase* WindowEditorInventionsListOpen(); + WindowBase* WindowEditorMainOpen(); + WindowBase* WindowEditorObjectiveOptionsOpen(); + WindowBase* WindowEditorScenarioOptionsOpen(); + WindowBase* WindowFootpathOpen(); + void WindowFootpathResetSelectedPath(); + WindowBase* WindowGuestOpen(Peep* peep); + WindowBase* WindowLandOpen(); + WindowBase* WindowLandRightsOpen(); + WindowBase* WindowMainOpen(); + WindowBase* WindowMapgenOpen(); + WindowBase* WindowMultiplayerOpen(); + WindowBase* WindowNewsOpen(); + WindowBase* WindowNewsOptionsOpen(); + WindowBase* WindowOptionsOpen(); + WindowBase* WindowSavePromptOpen(); #ifndef DISABLE_NETWORK -WindowBase* WindowServerListOpen(); -WindowBase* WindowServerStartOpen(); + WindowBase* WindowServerListOpen(); + WindowBase* WindowServerStartOpen(); #endif -WindowBase* WindowShortcutKeysOpen(); -WindowBase* WindowStaffListOpen(); -WindowBase* WindowStaffOpen(Peep* peep); -void WindowStaffListRefresh(); -WindowBase* WindowThemesOpen(); -WindowBase* WindowTitleExitOpen(); -WindowBase* WindowTitleLogoOpen(); -WindowBase* WindowTitleMenuOpen(); -WindowBase* WindowTitleOptionsOpen(); -WindowBase* WindowViewportOpen(); -WindowBase* WindowWaterOpen(); -WindowBase* WindowViewClippingOpen(); -WindowBase* WindowTransparencyOpen(); -WindowBase* WindowAssetPacksOpen(); + WindowBase* WindowShortcutKeysOpen(); + WindowBase* WindowStaffListOpen(); + WindowBase* WindowStaffOpen(Peep* peep); + void WindowStaffListRefresh(); + WindowBase* WindowThemesOpen(); + WindowBase* WindowTitleExitOpen(); + WindowBase* WindowTitleLogoOpen(); + WindowBase* WindowTitleMenuOpen(); + WindowBase* WindowTitleOptionsOpen(); + WindowBase* WindowViewportOpen(); + WindowBase* WindowWaterOpen(); + WindowBase* WindowViewClippingOpen(); + WindowBase* WindowTransparencyOpen(); + WindowBase* WindowAssetPacksOpen(); -// WC_FINANCES -WindowBase* WindowFinancesOpen(); -WindowBase* WindowFinancesResearchOpen(); -WindowBase* WindowFinancesMarketingOpen(); + // WC_FINANCES + WindowBase* WindowFinancesOpen(); + WindowBase* WindowFinancesResearchOpen(); + WindowBase* WindowFinancesMarketingOpen(); -// WC_PARK_INFORMATION -WindowBase* WindowParkAwardsOpen(); -WindowBase* WindowParkEntranceOpen(); -WindowBase* WindowParkGuestsOpen(); -WindowBase* WindowParkObjectiveOpen(); -WindowBase* WindowParkRatingOpen(); + // WC_PARK_INFORMATION + WindowBase* WindowParkAwardsOpen(); + WindowBase* WindowParkEntranceOpen(); + WindowBase* WindowParkGuestsOpen(); + WindowBase* WindowParkObjectiveOpen(); + WindowBase* WindowParkRatingOpen(); -WindowBase* WindowBannerOpen(rct_windownumber number); -WindowBase* WindowRideDemolishPromptOpen(const Ride& ride); -WindowBase* WindowRideRefurbishPromptOpen(const Ride& ride); -WindowBase* WindowSignOpen(rct_windownumber number); -WindowBase* WindowSignSmallOpen(rct_windownumber number); -WindowBase* WindowPlayerOpen(uint8_t id); -WindowBase* WindowNewCampaignOpen(int16_t campaignType); + WindowBase* WindowBannerOpen(rct_windownumber number); + WindowBase* WindowRideDemolishPromptOpen(const Ride& ride); + WindowBase* WindowRideRefurbishPromptOpen(const Ride& ride); + WindowBase* WindowSignOpen(rct_windownumber number); + WindowBase* WindowSignSmallOpen(rct_windownumber number); + WindowBase* WindowPlayerOpen(uint8_t id); + WindowBase* WindowNewCampaignOpen(int16_t campaignType); -WindowBase* WindowInstallTrackOpen(const utf8* path); -void WindowGuestListRefreshList(); -WindowBase* WindowGuestListOpen(); -WindowBase* WindowGuestListOpenWithFilter(GuestListFilterType type, int32_t index); -WindowBase* WindowStaffFirePromptOpen(Peep* peep); -WindowBase* WindowScenarioselectOpen(scenarioselect_callback callback); -WindowBase* WindowScenarioselectOpen(std::function callback); + WindowBase* WindowInstallTrackOpen(const utf8* path); + void WindowGuestListRefreshList(); + WindowBase* WindowGuestListOpen(); + WindowBase* WindowGuestListOpenWithFilter(GuestListFilterType type, int32_t index); + WindowBase* WindowStaffFirePromptOpen(Peep* peep); + WindowBase* WindowScenarioselectOpen(scenarioselect_callback callback); + WindowBase* WindowScenarioselectOpen(std::function callback); -WindowBase* WindowErrorOpen(StringId title, StringId message, const class Formatter& formatter); -WindowBase* WindowErrorOpen(std::string_view title, std::string_view message); -struct TrackDesign; -WindowBase* WindowLoadsaveOpen( - int32_t type, std::string_view defaultPath, std::function callback, - TrackDesign* trackDesign); -WindowBase* WindowTrackPlaceOpen(const struct TrackDesignFileRef* tdFileRef); -WindowBase* WindowTrackManageOpen(struct TrackDesignFileRef* tdFileRef); + WindowBase* WindowErrorOpen(StringId title, StringId message, const class Formatter& formatter); + WindowBase* WindowErrorOpen(std::string_view title, std::string_view message); -void TrackPlaceClearProvisionalTemporarily(); -void TrackPlaceRestoreProvisional(); + WindowBase* WindowLoadsaveOpen( + int32_t type, std::string_view defaultPath, std::function callback, + TrackDesign* trackDesign); + WindowBase* WindowTrackPlaceOpen(const struct TrackDesignFileRef* tdFileRef); + WindowBase* WindowTrackManageOpen(struct TrackDesignFileRef* tdFileRef); -WindowBase* WindowMapOpen(); -void WindowMapReset(); + void TrackPlaceClearProvisionalTemporarily(); + void TrackPlaceRestoreProvisional(); -WindowBase* WindowResearchOpen(); -void WindowResearchDevelopmentMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex); -void WindowResearchDevelopmentPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex); -void WindowResearchDevelopmentDraw(WindowBase* w, DrawPixelInfo& dpi, WidgetIndex baseWidgetIndex); -void WindowResearchFundingMouseDown(WindowBase* w, WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex); -void WindowResearchFundingMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex); -void WindowResearchFundingDropdown(WidgetIndex widgetIndex, int32_t selectedIndex, WidgetIndex baseWidgetIndex); -void WindowResearchFundingPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex); -void WindowResearchFundingDraw(WindowBase* w, DrawPixelInfo& dpi); + WindowBase* WindowMapOpen(); + void WindowMapReset(); -WindowBase* WindowNewRideOpen(); -WindowBase* WindowNewRideOpenResearch(); -void WindowNewRideInitVars(); -void WindowNewRideFocus(RideSelection rideItem); + WindowBase* WindowResearchOpen(); + void WindowResearchDevelopmentMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex); + void WindowResearchDevelopmentPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex); + void WindowResearchDevelopmentDraw(WindowBase* w, DrawPixelInfo& dpi, WidgetIndex baseWidgetIndex); + void WindowResearchFundingMouseDown(WindowBase* w, WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex); + void WindowResearchFundingMouseUp(WidgetIndex widgetIndex, WidgetIndex baseWidgetIndex); + void WindowResearchFundingDropdown(WidgetIndex widgetIndex, int32_t selectedIndex, WidgetIndex baseWidgetIndex); + void WindowResearchFundingPrepareDraw(WindowBase* w, WidgetIndex baseWidgetIndex); + void WindowResearchFundingDraw(WindowBase* w, DrawPixelInfo& dpi); -WindowBase* WindowRideListOpen(); -void WindowRideListRefreshList(WindowBase* w); + WindowBase* WindowNewRideOpen(); + WindowBase* WindowNewRideOpenResearch(); + void WindowNewRideInitVars(); + void WindowNewRideFocus(RideSelection rideItem); -WindowBase* WindowRideMainOpen(const Ride& ride); -WindowBase* WindowRideOpenTrack(TileElement* tileElement); -WindowBase* WindowRideOpenVehicle(Vehicle* vehicle); -void WindowRideInvalidateVehicle(const Vehicle& vehicle); -void WindowRidePaintResetVehicle(RideId rideIndex); -void WindowRideMeasurementsDesignCancel(); + WindowBase* WindowRideListOpen(); + void WindowRideListRefreshList(WindowBase* w); -// rct2: 0x00F635EE -extern RideSelection _window_track_list_item; -WindowBase* WindowTrackListOpen(RideSelection item); -void WindowTrackDesignListReloadTracks(); -void WindowTrackDesignListSetBeingUpdated(bool beingUpdated); + WindowBase* WindowRideMainOpen(const Ride& ride); + WindowBase* WindowRideOpenTrack(TileElement* tileElement); + WindowBase* WindowRideOpenVehicle(Vehicle* vehicle); + void WindowRideInvalidateVehicle(const Vehicle& vehicle); + void WindowRidePaintResetVehicle(RideId rideIndex); + void WindowRideMeasurementsDesignCancel(); -void SetMapTooltip(Formatter& ft); -const Formatter& GetMapTooltip(); -void WindowMapTooltipUpdateVisibility(); + // rct2: 0x00F635EE + extern RideSelection _window_track_list_item; + WindowBase* WindowTrackListOpen(RideSelection item); + void WindowTrackDesignListReloadTracks(); + void WindowTrackDesignListSetBeingUpdated(bool beingUpdated); -WindowBase* WindowMazeConstructionOpen(); -void WindowMazeConstructionUpdatePressedWidgets(); + void SetMapTooltip(Formatter& ft); + const Formatter& GetMapTooltip(); + void WindowMapTooltipUpdateVisibility(); -WindowBase* WindowNetworkStatusOpen(const std::string& text, close_callback onClose); -WindowBase* WindowNetworkStatusOpenPassword(); -void WindowNetworkStatusClose(); + WindowBase* WindowMazeConstructionOpen(); + void WindowMazeConstructionUpdatePressedWidgets(); -void WindowTextInputKey(WindowBase* w, uint32_t keycode); -void WindowTextInputOpen( - WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, - StringId existing_text, uintptr_t existing_args, int32_t maxLength); -void WindowTextInputRawOpen( - WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, - const_utf8string existing_text, int32_t maxLength); + WindowBase* WindowNetworkStatusOpen(const std::string& text, close_callback onClose); + WindowBase* WindowNetworkStatusOpenPassword(); + void WindowNetworkStatusClose(); -void WindowTextInputOpen( - std::string_view title, std::string_view description, std::string_view initialValue, size_t maxLength, - std::function okCallback, std::function cancelCallback); + void WindowTextInputKey(WindowBase* w, uint32_t keycode); + void WindowTextInputOpen( + WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, + StringId existing_text, uintptr_t existing_args, int32_t maxLength); + void WindowTextInputRawOpen( + WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs, + const_utf8string existing_text, int32_t maxLength); -WindowBase* WindowObjectLoadErrorOpen(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects); + void WindowTextInputOpen( + std::string_view title, std::string_view description, std::string_view initialValue, size_t maxLength, + std::function okCallback, std::function cancelCallback); -WindowBase* WindowRideConstructionOpen(); -void WindowRideConstructionUpdateActiveElementsImpl(); -void WindowRideConstructionUpdateEnabledTrackPieces(); + WindowBase* WindowObjectLoadErrorOpen(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects); -WindowBase* WindowTopToolbarOpen(); -bool LandToolIsActive(); -bool ClearSceneryToolIsActive(); -bool WaterToolIsActive(); + WindowBase* WindowRideConstructionOpen(); + void WindowRideConstructionUpdateActiveElementsImpl(); + void WindowRideConstructionUpdateEnabledTrackPieces(); -WindowBase* WindowSceneryOpen(); -void WindowScenerySetSelectedItem( - const ScenerySelection& sceneryconst, std::optional primary, const std::optional secondary, - const std::optional tertiary, const std::optional rotation); -void WindowScenerySetSelectedTab(const ObjectEntryIndex sceneryGroupIndex); -void WindowScenerySetDefaultPlacementConfiguration(); -void WindowSceneryInit(); -void WindowSceneryResetSelectedSceneryItems(); -const ScenerySelection WindowSceneryGetTabSelection(); + WindowBase* WindowTopToolbarOpen(); + bool LandToolIsActive(); + bool ClearSceneryToolIsActive(); + bool WaterToolIsActive(); -extern uint8_t gToolbarDirtyFlags; -WindowBase* WindowGameBottomToolbarOpen(); -void WindowGameBottomToolbarInvalidateNewsItem(); + WindowBase* WindowSceneryOpen(); + void WindowScenerySetSelectedItem( + const ScenerySelection& sceneryconst, std::optional primary, const std::optional secondary, + const std::optional tertiary, const std::optional rotation); + void WindowScenerySetSelectedTab(const ObjectEntryIndex sceneryGroupIndex); + void WindowScenerySetDefaultPlacementConfiguration(); + void WindowSceneryInit(); + void WindowSceneryResetSelectedSceneryItems(); + const ScenerySelection WindowSceneryGetTabSelection(); -WindowBase* WindowEditorBottomToolbarOpen(); + extern uint8_t gToolbarDirtyFlags; + WindowBase* WindowGameBottomToolbarOpen(); + void WindowGameBottomToolbarInvalidateNewsItem(); -WindowBase* WindowTileInspectorOpen(); -void WindowTileInspectorClearClipboard(); + WindowBase* WindowEditorBottomToolbarOpen(); -WindowBase* WindowEditorObjectSelectionOpen(); + WindowBase* WindowTileInspectorOpen(); + void WindowTileInspectorClearClipboard(); -void WindowTooltipReset(const ScreenCoordsXY& screenCoords); -void WindowTooltipShow(const OpenRCT2String& message, ScreenCoordsXY screenCoords); -void WindowTooltipOpen(WindowBase* widgetWindow, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -void WindowTooltipClose(); + WindowBase* WindowEditorObjectSelectionOpen(); -WindowBase* WindowSceneryScatterOpen(); -WindowBase* WindowPatrolAreaOpen(EntityId staffId); -EntityId WindowPatrolAreaGetCurrentStaffId(); + void WindowTooltipReset(const ScreenCoordsXY& screenCoords); + void WindowTooltipShow(const OpenRCT2String& message, ScreenCoordsXY screenCoords); + void WindowTooltipOpen(WindowBase* widgetWindow, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); + void WindowTooltipClose(); -// clang-format off + WindowBase* WindowSceneryScatterOpen(); + WindowBase* WindowPatrolAreaOpen(EntityId staffId); + EntityId WindowPatrolAreaGetCurrentStaffId(); + + // clang-format off #define WINDOW_SHIM_RAW(TITLE, WIDTH, HEIGHT, CLOSE_STR) \ { WindowWidgetType::Frame, 0, 0, WIDTH - 1, 0, HEIGHT - 1, 0xFFFFFFFF, STR_NONE }, \ { WindowWidgetType::Caption, 0, 1, WIDTH - 2, 1, 14, TITLE, STR_WINDOW_TITLE_TIP }, \ @@ -223,4 +226,5 @@ EntityId WindowPatrolAreaGetCurrentStaffId(); #define WINDOW_SHIM(TITLE, WIDTH, HEIGHT) WINDOW_SHIM_RAW(TITLE, WIDTH, HEIGHT, STR_CLOSE_X) #define WINDOW_SHIM_WHITE(TITLE, WIDTH, HEIGHT) WINDOW_SHIM_RAW(TITLE, WIDTH, HEIGHT, STR_CLOSE_X_WHITE) -// clang-format on + // clang-format on +} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index f671194564..800a20d703 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -611,9 +611,6 @@ Viewport* WindowGetViewport(WindowBase* window); void WindowRelocateWindows(int32_t width, int32_t height); void WindowResizeGui(int32_t width, int32_t height); void WindowResizeGuiScenarioEditor(int32_t width, int32_t height); -void RideConstructionToolupdateEntranceExit(const ScreenCoordsXY& screenCoords); -void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords); -void RideConstructionTooldownConstruct(const ScreenCoordsXY& screenCoords); void InvalidateAllWindowsAfterInput(); void TextinputCancel(); @@ -635,28 +632,6 @@ Viewport* WindowGetPreviousViewport(Viewport* current); void WindowResetVisibilities(); void WindowInitAll(); -void WindowRideConstructionKeyboardShortcutTurnLeft(); -void WindowRideConstructionKeyboardShortcutTurnRight(); -void WindowRideConstructionKeyboardShortcutUseTrackDefault(); -void WindowRideConstructionKeyboardShortcutSlopeDown(); -void WindowRideConstructionKeyboardShortcutSlopeUp(); -void WindowRideConstructionKeyboardShortcutChainLiftToggle(); -void WindowRideConstructionKeyboardShortcutBankLeft(); -void WindowRideConstructionKeyboardShortcutBankRight(); -void WindowRideConstructionKeyboardShortcutPreviousTrack(); -void WindowRideConstructionKeyboardShortcutNextTrack(); -void WindowRideConstructionKeyboardShortcutBuildCurrent(); -void WindowRideConstructionKeyboardShortcutDemolishCurrent(); - -void WindowFootpathKeyboardShortcutTurnLeft(); -void WindowFootpathKeyboardShortcutTurnRight(); -void WindowFootpathKeyboardShortcutSlopeDown(); -void WindowFootpathKeyboardShortcutSlopeUp(); -void WindowFootpathKeyboardShortcutBuildCurrent(); -void WindowFootpathKeyboardShortcutDemolishCurrent(); - -void WindowTileInspectorKeyboardShortcutToggleInvisibility(); - void WindowFollowSprite(WindowBase& w, EntityId spriteIndex); void WindowUnfollowSprite(WindowBase& w); diff --git a/src/openrct2/ride/Ride.h b/src/openrct2/ride/Ride.h index dc1795a7fa..902fce5e98 100644 --- a/src/openrct2/ride/Ride.h +++ b/src/openrct2/ride/Ride.h @@ -1012,8 +1012,6 @@ void RideDelete(RideId id); const RideObjectEntry* GetRideEntryByIndex(ObjectEntryIndex index); std::string_view GetRideEntryName(ObjectEntryIndex index); -extern const StringId ColourSchemeNames[4]; - int32_t RideGetCount(); void RideInitAll(); void ResetAllRideBuildDates(); @@ -1084,8 +1082,6 @@ enum class RideSetSetting : uint8_t; money64 SetOperatingSetting(RideId rideId, RideSetSetting setting, uint8_t value); money64 SetOperatingSettingNested(RideId rideId, RideSetSetting setting, uint8_t value, uint8_t flags); -void UpdateGhostTrackAndArrow(); - uint32_t RideCustomersPerHour(const Ride& ride); uint32_t RideCustomersInLast5Minutes(const Ride& ride);