diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index cc980aca1f..d063323be1 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3826,3 +3826,6 @@ STR_6778 :Preview scenarios using: STR_6779 :Select the kind of preview image to use in the scenario selection screen. STR_6780 :Mini maps STR_6781 :Screenshots +STR_6782 :Park notifications +STR_6783 :Ride notifications +STR_6784 :Guest notifications diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 56bf83750e..b464c405f5 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,6 +1,7 @@ 0.4.24 (in development) ------------------------------------------------------------------------ - Feature: [#24411] Vanilla scenarios now also have previews in the scenario selection window. +- Improved: [#24026] Notification settings have been made into a tab of the Recent Messages window. - Change: [#24559] Scenario options are now disabled rather than hidden when disabling money makes them non-applicable. - Change: [objects#383] Disable all base colours on non-remappable WWTT vehicles, change black to light_blue. - Change: [objects#384] Remove erroneously enabled WWTT third remaps. diff --git a/src/openrct2-ui/UiStringIds.h b/src/openrct2-ui/UiStringIds.h index fe0afb546b..ea54f8313c 100644 --- a/src/openrct2-ui/UiStringIds.h +++ b/src/openrct2-ui/UiStringIds.h @@ -1042,9 +1042,9 @@ namespace OpenRCT2 // Window: News STR_NEWS_DATE_FORMAT = 2235, - STR_RECENT_MESSAGES = 2234, - - // Window: NewsOptions + STR_NEWS_GROUP_GUEST = 6784, + STR_NEWS_GROUP_PARK = 6782, + STR_NEWS_GROUP_RIDE = 6783, STR_NOTIFICATION_GUEST_BOUGHT_ITEM = 5604, STR_NOTIFICATION_GUEST_DIED = 5606, STR_NOTIFICATION_GUEST_LEFT_PARK = 5600, @@ -1064,6 +1064,7 @@ namespace OpenRCT2 STR_NOTIFICATION_RIDE_VEHICLE_STALLED = 6366, STR_NOTIFICATION_RIDE_WARNINGS = 5596, STR_NOTIFICATION_SETTINGS = 5589, + STR_RECENT_MESSAGES = 2234, // Window: ObjectLoadError STR_COPY_ALL = 6130, diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 49dc6820a9..ea404331f5 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -112,8 +112,6 @@ public: return ResearchOpen(); case WindowClass::RideList: return RideListOpen(); - case WindowClass::NotificationOptions: - return NewsOptionsOpen(); case WindowClass::Options: return OptionsOpen(); case WindowClass::SavePrompt: diff --git a/src/openrct2-ui/interface/Theme.cpp b/src/openrct2-ui/interface/Theme.cpp index f35a8ad7a9..49e5249715 100644 --- a/src/openrct2-ui/interface/Theme.cpp +++ b/src/openrct2-ui/interface/Theme.cpp @@ -153,7 +153,7 @@ static constexpr WindowThemeDesc WindowThemeDescriptors[] = { WindowClass::Finances, "WC_FINANCES", STR_THEMES_WINDOW_FINANCES, COLOURS_3(opaque(COLOUR_GREY), opaque(COLOUR_DARK_YELLOW), opaque(COLOUR_DARK_YELLOW) ) }, { WindowClass::TitleMenu, "WC_TITLE_MENU", STR_THEMES_WINDOW_TITLE_MENU_BUTTONS, COLOURS_3(translucent(COLOUR_DARK_GREEN), translucent(COLOUR_DARK_GREEN), translucent(COLOUR_DARK_GREEN) ) }, { WindowClass::TitleExit, "WC_TITLE_EXIT", STR_THEMES_WINDOW_TITLE_MENU_EXIT, COLOURS_3(translucent(COLOUR_DARK_GREEN), translucent(COLOUR_DARK_GREEN), translucent(COLOUR_DARK_GREEN) ) }, - { WindowClass::RecentNews, "WC_RECENT_NEWS", STR_THEMES_WINDOW_RECENT_NEWS, COLOURS_3(opaque(COLOUR_GREY), opaque(COLOUR_GREY), opaque(COLOUR_BLACK) ) }, + { WindowClass::RecentNews, "WC_RECENT_NEWS", STR_THEMES_WINDOW_RECENT_NEWS, COLOURS_3(opaque(COLOUR_DARK_GREEN), opaque(COLOUR_GREY), opaque(COLOUR_BLACK) ) }, { WindowClass::ScenarioSelect, "WC_SCENARIO_SELECT", STR_THEMES_WINDOW_TITLE_MENU_SCENARIO_SELECTION, COLOURS_3(opaque(COLOUR_GREY), opaque(COLOUR_BORDEAUX_RED), opaque(COLOUR_BORDEAUX_RED) ) }, { WindowClass::TrackDesignList, "WC_TRACK_DESIGN_LIST", STR_THEMES_WINDOW_TRACK_DESIGN_LIST, COLOURS_3(opaque(COLOUR_BORDEAUX_RED), opaque(COLOUR_BORDEAUX_RED), opaque(COLOUR_BORDEAUX_RED) ) }, { WindowClass::TrackDesignPlace, "WC_TRACK_DESIGN_PLACE", STR_THEMES_WINDOW_TRACK_DESIGN_PLACE, COLOURS_3(opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN) ) }, @@ -224,6 +224,7 @@ static constexpr UIThemeWindowEntry PredefinedThemeRCT1_Entries[] = { WindowClass::AssetPacks, COLOURS_RCT1(opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK)) }, { WindowClass::KeyboardShortcutList, COLOURS_RCT1(opaque(COLOUR_GREY), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK)) }, { WindowClass::ChangeKeyboardShortcut, COLOURS_RCT1(opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK)) }, + { WindowClass::RecentNews, COLOURS_RCT1(opaque(COLOUR_DARK_BROWN), opaque(COLOUR_GREY), opaque(COLOUR_GREY), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK)) }, { WindowClass::TrackDesignList, COLOURS_RCT1(opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK)) }, { WindowClass::Map, COLOURS_RCT1(opaque(COLOUR_DARK_BROWN), opaque(COLOUR_GREY), opaque(COLOUR_GREY), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK)) }, { WindowClass::About, COLOURS_RCT1(opaque(COLOUR_GREY), opaque(COLOUR_DARK_BROWN), opaque(COLOUR_WHITE), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK), opaque(COLOUR_BLACK)) }, diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj index d424299fbb..e450099bc4 100644 --- a/src/openrct2-ui/libopenrct2ui.vcxproj +++ b/src/openrct2-ui/libopenrct2ui.vcxproj @@ -197,7 +197,6 @@ - diff --git a/src/openrct2-ui/windows/News.cpp b/src/openrct2-ui/windows/News.cpp index 967a733e72..77c60a5acb 100644 --- a/src/openrct2-ui/windows/News.cpp +++ b/src/openrct2-ui/windows/News.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ namespace OpenRCT2::Ui::Windows { - static constexpr StringId WINDOW_TITLE = STR_RECENT_MESSAGES; static constexpr int32_t WH = 300; static constexpr int32_t WW = 400; @@ -35,38 +35,280 @@ namespace OpenRCT2::Ui::Windows WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE, - WIDX_SETTINGS, - WIDX_SCROLL + WIDX_TAB_BACKGROUND, + WIDX_TAB_NEWS, + WIDX_TAB_OPTIONS, + WIDX_TAB_CONTENT, + + WIDX_SCROLL = WIDX_TAB_CONTENT, + + WIDX_CHECKBOX_0 = WIDX_TAB_CONTENT, }; // clang-format off - static constexpr auto window_news_widgets = makeWidgets( - makeWindowShim(WINDOW_TITLE, WW, WH), - makeWidget({372, 18}, { 24, 24}, WidgetType::flatBtn, WindowColour::primary, ImageId(SPR_TAB_GEARS_0)), // settings - makeWidget({ 4, 44}, {392, 252}, WidgetType::scroll, WindowColour::primary, SCROLL_VERTICAL) // scroll + enum NewsWindowTab : uint8_t + { + newsTab, + optionsTab, + }; + + static constexpr auto makeNewsWidgets = [](StringId title) { + return makeWidgets( + makeWindowShim(title, WW, WH), + makeWidget({ 0, 43 }, { WW, 257 }, WidgetType::resize, WindowColour::secondary), + makeTab({ 3, 17 }, STR_RECENT_MESSAGES), + makeTab({ 34, 17 }, STR_NOTIFICATION_SETTINGS) + ); + }; + + static constexpr auto kNewsTabWidgets = makeWidgets( + makeNewsWidgets(STR_RECENT_MESSAGES), + makeWidget({ 4, 44 }, { 392, 252 }, WidgetType::scroll, WindowColour::secondary, SCROLL_VERTICAL) ); + + static constexpr auto kOptionsTabWidgets = makeWidgets( + makeNewsWidgets(STR_NOTIFICATION_SETTINGS), + makeWidget({ 10, 49 }, { 380, 14 }, WidgetType::checkbox, WindowColour::secondary) + ); + // clang-format on + + struct NewsOption + { + StringId group; + StringId caption; + size_t configOffset; + }; + + // clang-format off + static constexpr NewsOption kNewsItemOptionDefinitions[] = { + { STR_NEWS_GROUP_PARK, STR_NOTIFICATION_PARK_AWARD, offsetof(Config::Notification, ParkAward) }, + { STR_NEWS_GROUP_PARK, STR_NOTIFICATION_PARK_MARKETING_CAMPAIGN_FINISHED, offsetof(Config::Notification, ParkMarketingCampaignFinished) }, + { STR_NEWS_GROUP_PARK, STR_NOTIFICATION_PARK_WARNINGS, offsetof(Config::Notification, ParkWarnings) }, + { STR_NEWS_GROUP_PARK, STR_NOTIFICATION_PARK_RATING_WARNINGS, offsetof(Config::Notification, ParkRatingWarnings) }, + { STR_NEWS_GROUP_RIDE, STR_NOTIFICATION_RIDE_BROKEN_DOWN, offsetof(Config::Notification, RideBrokenDown) }, + { STR_NEWS_GROUP_RIDE, STR_NOTIFICATION_RIDE_CRASHED, offsetof(Config::Notification, RideCrashed) }, + { STR_NEWS_GROUP_RIDE, STR_NOTIFICATION_RIDE_CASUALTIES, offsetof(Config::Notification, RideCasualties) }, + { STR_NEWS_GROUP_RIDE, STR_NOTIFICATION_RIDE_WARNINGS, offsetof(Config::Notification, RideWarnings) }, + { STR_NEWS_GROUP_RIDE, STR_NOTIFICATION_RIDE_RESEARCHED, offsetof(Config::Notification, RideResearched) }, + { STR_NEWS_GROUP_RIDE, STR_NOTIFICATION_RIDE_VEHICLE_STALLED, offsetof(Config::Notification, RideStalledVehicles) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_WARNINGS, offsetof(Config::Notification, GuestWarnings) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_LEFT_PARK, offsetof(Config::Notification, GuestLeftPark) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_QUEUING_FOR_RIDE, offsetof(Config::Notification, GuestQueuingForRide) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_ON_RIDE, offsetof(Config::Notification, GuestOnRide) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_LEFT_RIDE, offsetof(Config::Notification, GuestLeftRide) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_BOUGHT_ITEM, offsetof(Config::Notification, GuestBoughtItem) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_USED_FACILITY, offsetof(Config::Notification, GuestUsedFacility) }, + { STR_NEWS_GROUP_GUEST, STR_NOTIFICATION_GUEST_DIED, offsetof(Config::Notification, GuestDied) }, + }; // clang-format on class NewsWindow final : public Window { private: int32_t _pressedNewsItemIndex{}, _pressedButtonIndex{}, _suspendUpdateTicks{}; - static int32_t CalculateItemHeight() + WidgetIndex _baseCheckboxIndex; + + static int32_t CalculateNewsItemHeight() { return 4 * FontGetLineHeight(FontStyle::Small) + 2; } + void InitNewsWidgets() + { + Invalidate(); + page = newsTab; + height = WH; + SetWidgets(kNewsTabWidgets); + + WindowInitScrollWidgets(*this); + _pressedNewsItemIndex = -1; + + auto& widget = widgets[WIDX_SCROLL]; + ScreenSize scrollSize = OnScrollGetSize(0); + scrolls[0].contentOffsetY = std::max(0, scrollSize.height - (widget.height() - 1)); + WidgetScrollUpdateThumbs(*this, WIDX_SCROLL); + } + + void InitOptionsWidgets() + { + Invalidate(); + page = optionsTab; + + widgets.clear(); + widgets.insert(widgets.end(), kOptionsTabWidgets.begin(), kOptionsTabWidgets.end()); + + // Collect widgets to insert at the end + std::vector groupWidgetsToInsert{}; + std::vector checkboxWidgetsToInsert{}; + + // Collect existing checkbox template, then remove it from the window + const auto& baseCheckBox = kOptionsTabWidgets[WIDX_CHECKBOX_0]; + int16_t y = baseCheckBox.top; + widgets.pop_back(); + + StringId lastGroup = kStringIdNone; + uint16_t numGroupElements = 0; + + for (const auto& def : kNewsItemOptionDefinitions) + { + // Create group elements as needed + if (def.group != lastGroup) + { + // Fit previous group to its contents + if (!groupWidgetsToInsert.empty()) + { + auto& prevGroup = groupWidgetsToInsert.back(); + prevGroup.bottom += numGroupElements * (kListRowHeight + 5) + 2; + numGroupElements = 0; + y += 7; + } + + Widget groupWidget = { + .type = WidgetType::groupbox, + .colour = colours[1].colour, + .left = static_cast(baseCheckBox.left - 5), + .right = static_cast(baseCheckBox.right + 5), + .top = y, + .bottom = static_cast(y + kListRowHeight), + .text = def.group, + }; + + groupWidgetsToInsert.emplace_back(groupWidget); + lastGroup = def.group; + y += groupWidget.height(); + } + + // Create checkbox widgets + Widget checkboxWidget = { + .type = WidgetType::checkbox, + .colour = colours[1].colour, + .left = baseCheckBox.left, + .right = baseCheckBox.right, + .top = y, + .bottom = static_cast(y + kListRowHeight + 3), + .text = def.caption, + }; + + checkboxWidgetsToInsert.emplace_back(checkboxWidget); + numGroupElements++; + y += kListRowHeight + 5; + } + + // Fit final group to its contents + if (!groupWidgetsToInsert.empty()) + { + auto& prevGroup = groupWidgetsToInsert.back(); + prevGroup.bottom += numGroupElements * (kListRowHeight + 5) + 2; + numGroupElements = 0; + } + + for (auto& widget : groupWidgetsToInsert) + { + widgets.emplace_back(widget); + } + + _baseCheckboxIndex = static_cast(WIDX_CHECKBOX_0 + groupWidgetsToInsert.size()); + + for (auto& widget : checkboxWidgetsToInsert) + { + widgets.emplace_back(widget); + } + + // Fit panel to all widget elements, plus padding + y += 7; + + if (height != y) + { + Invalidate(); + height = y; + widgets[WIDX_BACKGROUND].bottom = y - 1; + widgets[WIDX_TAB_BACKGROUND].bottom = y - 1; + Invalidate(); + } + + // We're not using SetWidgets, so invoke ResizeFrame manually + ResizeFrame(); + } + + bool& GetNotificationValueRef(const NewsOption& def) + { + bool& configValue = *reinterpret_cast( + reinterpret_cast(&Config::Get().notifications) + def.configOffset); + return configValue; + } + + void SetPage(NewsWindowTab newPage) + { + if (page == newPage && !widgets.empty()) + return; + + switch (newPage) + { + case newsTab: + InitNewsWidgets(); + break; + + case optionsTab: + InitOptionsWidgets(); + break; + } + + SetWidgetPressed(WIDX_TAB_NEWS, page == newsTab); + SetWidgetPressed(WIDX_TAB_OPTIONS, page == optionsTab); + } + + void DrawTabImages(RenderTarget& rt) + { + if (!IsWidgetDisabled(WIDX_TAB_NEWS)) + { + auto imageId = ImageId(SPR_G2_TAB_NEWS); + auto& widget = widgets[WIDX_TAB_NEWS]; + GfxDrawSprite(rt, imageId, windowPos + ScreenCoordsXY{ widget.left + 3, widget.top }); + } + + if (!IsWidgetDisabled(WIDX_TAB_OPTIONS)) + { + auto imageId = ImageId(SPR_TAB_GEARS_0); + if (page == optionsTab) + imageId = imageId.WithIndexOffset((frame_no / 2) % 4); + + auto& widget = widgets[WIDX_TAB_OPTIONS]; + GfxDrawSprite(rt, imageId, windowPos + ScreenCoordsXY{ widget.left, widget.top }); + } + } + public: void OnOpen() override { - SetWidgets(window_news_widgets); - WindowInitScrollWidgets(*this); - _pressedNewsItemIndex = -1; + SetPage(newsTab); + } - Widget* widget = &widgets[WIDX_SCROLL]; - ScreenSize scrollSize = OnScrollGetSize(0); - scrolls[0].contentOffsetY = std::max(0, scrollSize.height - (widget->height() - 1)); - WidgetScrollUpdateThumbs(*this, WIDX_SCROLL); + void OnPrepareDraw() override + { + if (page != optionsTab) + return; + + uint16_t checkboxWidgetIndex = _baseCheckboxIndex; + for (const auto& def : kNewsItemOptionDefinitions) + { + auto& widget = widgets[checkboxWidgetIndex]; + if (widget.type == WidgetType::groupbox) + { + // Skip group box! + checkboxWidgetIndex++; + continue; + } + + const bool& configValue = GetNotificationValueRef(def); + SetCheckboxValue(checkboxWidgetIndex, configValue); + checkboxWidgetIndex++; + } + } + + void OnDraw(RenderTarget& rt) override + { + DrawWidgets(rt); + DrawTabImages(rt); } void OnMouseUp(WidgetIndex widgetIndex) override @@ -76,21 +318,46 @@ namespace OpenRCT2::Ui::Windows case WIDX_CLOSE: Close(); break; - case WIDX_SETTINGS: - ContextOpenWindow(WindowClass::NotificationOptions); + case WIDX_TAB_NEWS: + SetPage(newsTab); break; + case WIDX_TAB_OPTIONS: + SetPage(optionsTab); + break; + default: + { + if (page != optionsTab) + break; + + int32_t checkBoxIndex = widgetIndex - _baseCheckboxIndex; + if (checkBoxIndex < 0) + break; + + const auto& def = kNewsItemOptionDefinitions[checkBoxIndex]; + bool& configValue = GetNotificationValueRef(def); + configValue = !configValue; + Config::Save(); + + InvalidateWidget(widgetIndex); + break; + } } } void OnUpdate() override { + frame_no++; + + if (page != newsTab) + return; + if (_pressedNewsItemIndex == -1 || --_suspendUpdateTicks != 0) { return; } Invalidate(); - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click2, 0, windowPos.x + (width / 2)); + Audio::Play(Audio::SoundId::Click2, 0, windowPos.x + (width / 2)); size_t j = _pressedNewsItemIndex; _pressedNewsItemIndex = -1; @@ -124,13 +391,14 @@ namespace OpenRCT2::Ui::Windows ScreenSize OnScrollGetSize(int32_t scrollIndex) override { - int32_t scrollHeight = static_cast(getGameState().newsItems.GetArchived().size()) * CalculateItemHeight(); + int32_t scrollHeight = static_cast(getGameState().newsItems.GetArchived().size()) + * CalculateNewsItemHeight(); return { WW, scrollHeight }; } void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override { - int32_t itemHeight = CalculateItemHeight(); + int32_t itemHeight = CalculateNewsItemHeight(); int32_t i = 0; int32_t buttonIndex = 0; auto mutableScreenCoords = screenCoords; @@ -165,19 +433,14 @@ namespace OpenRCT2::Ui::Windows _pressedButtonIndex = buttonIndex; _suspendUpdateTicks = 4; Invalidate(); - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); + Audio::Play(Audio::SoundId::Click1, 0, windowPos.x + (width / 2)); } } - void OnDraw(RenderTarget& rt) override - { - DrawWidgets(rt); - } - void OnScrollDraw(int32_t scrollIndex, RenderTarget& rt) override { int32_t lineHeight = FontGetLineHeight(FontStyle::Small); - int32_t itemHeight = CalculateItemHeight(); + int32_t itemHeight = CalculateNewsItemHeight(); int32_t y = 0; int32_t i = 0; diff --git a/src/openrct2-ui/windows/NewsOptions.cpp b/src/openrct2-ui/windows/NewsOptions.cpp deleted file mode 100644 index 98f0e6df38..0000000000 --- a/src/openrct2-ui/windows/NewsOptions.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2014-2025 OpenRCT2 developers - * - * For a complete list of all authors, please refer to contributors.md - * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 - * - * OpenRCT2 is licensed under the GNU General Public License version 3. - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -namespace OpenRCT2::Ui::Windows -{ - static constexpr StringId WINDOW_TITLE = STR_NOTIFICATION_SETTINGS; - static constexpr int32_t WH = 300; - static constexpr int32_t WW = 400; - - enum - { - NOTIFICATION_CATEGORY_PARK, - NOTIFICATION_CATEGORY_RIDE, - NOTIFICATION_CATEGORY_GUEST, - NOTIFICATION_CATEGORY_COUNT - }; - - struct NotificationDef - { - uint8_t category; - StringId caption; - size_t config_offset; - }; - - // clang-format off - static constexpr NotificationDef NewsItemOptionDefinitions[] = { - { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_AWARD, offsetof(Config::Notification, ParkAward) }, - { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_MARKETING_CAMPAIGN_FINISHED, offsetof(Config::Notification, ParkMarketingCampaignFinished) }, - { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_WARNINGS, offsetof(Config::Notification, ParkWarnings) }, - { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_RATING_WARNINGS, offsetof(Config::Notification, ParkRatingWarnings) }, - { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_BROKEN_DOWN, offsetof(Config::Notification, RideBrokenDown) }, - { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_CRASHED, offsetof(Config::Notification, RideCrashed) }, - { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_CASUALTIES, offsetof(Config::Notification, RideCasualties) }, - { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_WARNINGS, offsetof(Config::Notification, RideWarnings) }, - { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_RESEARCHED, offsetof(Config::Notification, RideResearched) }, - { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_VEHICLE_STALLED, offsetof(Config::Notification, RideStalledVehicles) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_WARNINGS, offsetof(Config::Notification, GuestWarnings) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_LEFT_PARK, offsetof(Config::Notification, GuestLeftPark) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_QUEUING_FOR_RIDE, offsetof(Config::Notification, GuestQueuingForRide) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_ON_RIDE, offsetof(Config::Notification, GuestOnRide) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_LEFT_RIDE, offsetof(Config::Notification, GuestLeftRide) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_BOUGHT_ITEM, offsetof(Config::Notification, GuestBoughtItem) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_USED_FACILITY, offsetof(Config::Notification, GuestUsedFacility) }, - { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_DIED, offsetof(Config::Notification, GuestDied) }, - }; - // clang-format on - - enum WindowNewsOptionsWidgetIdx - { - WIDX_BACKGROUND, - WIDX_TITLE, - WIDX_CLOSE, - WIDX_TAB_CONTENT_PANEL, - WIDX_FIRST_TAB, - WIDX_TAB_PARK = WIDX_FIRST_TAB, - WIDX_TAB_RIDE, - WIDX_TAB_GUEST, - WIDX_CHECKBOX_0 - }; - - // clang-format off - static constexpr auto WindowNewsOptionsWidgets = makeWidgets( - makeWindowShim(WINDOW_TITLE, WW, WH), - makeWidget({ 0, 43}, {400, 257}, WidgetType::resize, WindowColour::secondary), // Tab content panel - makeTab ({ 3, 17} ), // Park tab - makeTab ({34, 17} ), // Ride tab - makeTab ({65, 17} ), // Guest tab - makeWidget({ 7, 49}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ), - makeWidget({ 0, 0}, {343, 14}, WidgetType::checkbox, WindowColour::tertiary ) - ); - // clang-format on - - class NewsOptionsWindow final : public Window - { - public: - void OnOpen() override - { - SetWidgets(WindowNewsOptionsWidgets); - InitScrollWidgets(); - colours[0] = COLOUR_GREY; - colours[1] = COLOUR_LIGHT_BLUE; - colours[2] = COLOUR_LIGHT_BLUE; - } - - void OnMouseUp(WidgetIndex widgetIndex) override - { - switch (widgetIndex) - { - 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 checkBoxIndex = widgetIndex - WIDX_CHECKBOX_0; - if (checkBoxIndex >= 0) - { - int32_t matchIndex = 0; - for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) - { - const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; - if (ndef->category != page) - continue; - - if (matchIndex == checkBoxIndex) - { - // Toggle value - bool* configValue = GetNotificationValuePtr(ndef); - *configValue = !(*configValue); - - Config::Save(); - - InvalidateWidget(widgetIndex); - break; - } - 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; - - uint16_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 = WidgetType::checkbox; - checkboxWidget->left = baseCheckBox.left; - checkboxWidget->right = baseCheckBox.right; - checkboxWidget->top = y; - checkboxWidget->bottom = checkboxWidget->top + kListRowHeight + 3; - checkboxWidget->text = ndef->caption; - - const bool* configValue = GetNotificationValuePtr(ndef); - SetCheckboxValue(checkboxWidgetIndex, *configValue); - - checkboxWidgetIndex++; - checkboxWidget++; - y += kListRowHeight + 3; - } - - // Remove unused checkboxes - while (checkboxWidgetIndex < widgets.size()) - { - checkboxWidget->type = WidgetType::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(RenderTarget& rt) override - { - DrawWidgets(rt); - DrawTabImages(rt); - } - - private: - void SetPage(int32_t p) - { - if (page != p) - { - page = p; - frame_no = 0; - Invalidate(); - } - } - - void DrawTabImages(RenderTarget& rt) - { - DrawTabImage(rt, NOTIFICATION_CATEGORY_PARK, SPR_TAB_PARK); - DrawTabImage(rt, NOTIFICATION_CATEGORY_RIDE, SPR_TAB_RIDE_0); - DrawTabImage(rt, NOTIFICATION_CATEGORY_GUEST, SPR_TAB_GUESTS_0); - } - - void DrawTabImage(RenderTarget& rt, 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(rt, 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(&Config::Get().notifications) + ndef->config_offset); - return configValue; - } - - static constexpr int32_t TabAnimationDivisor[3] = { - 1, // Park - 4, // Ride - 4, // Guest - }; - static constexpr int32_t TabAnimationFrames[3] = { - 1, // Park - 16, // Ride - 8, // Guest - }; - }; - - WindowBase* NewsOptionsOpen() - { - auto* windowMgr = GetWindowManager(); - return windowMgr->FocusOrCreate(WindowClass::NotificationOptions, WW, WH, WF_CENTRE_SCREEN); - } -} // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/windows/Windows.h b/src/openrct2-ui/windows/Windows.h index 7644f8e2dd..f88e8bcb27 100644 --- a/src/openrct2-ui/windows/Windows.h +++ b/src/openrct2-ui/windows/Windows.h @@ -170,9 +170,6 @@ namespace OpenRCT2::Ui::Windows // News WindowBase* NewsOpen(); - // NewsOptions - WindowBase* NewsOptionsOpen(); - // NetworkStatus WindowBase* NetworkStatusOpen(const std::string& text, CloseCallback onClose); WindowBase* NetworkStatusOpenPassword(); diff --git a/src/openrct2/interface/WindowClasses.h b/src/openrct2/interface/WindowClasses.h index 6f06f42a8d..38dbe832d0 100644 --- a/src/openrct2/interface/WindowClasses.h +++ b/src/openrct2/interface/WindowClasses.h @@ -61,7 +61,7 @@ enum class WindowClass : uint8_t InstallTrack = 49, ClearScenery = 50, SceneryScatter = 51, - NotificationOptions = 109, + Cheats = 110, Research = 111, Viewport = 112,