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,