1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-16 12:32:29 +01:00

Rework news options into one tab in the news window

This commit is contained in:
Aaron van Geffen
2025-06-10 23:13:05 +02:00
committed by GitHub
parent ec3f9d1272
commit dac097ce64
10 changed files with 301 additions and 322 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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,

View File

@@ -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:

View File

@@ -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)) },

View File

@@ -197,7 +197,6 @@
<ClCompile Include="windows\NewCampaign.cpp" />
<ClCompile Include="windows\NewRide.cpp" />
<ClCompile Include="windows\News.cpp" />
<ClCompile Include="windows\NewsOptions.cpp" />
<ClCompile Include="windows\ObjectLoadError.cpp" />
<ClCompile Include="windows\Options.cpp" />
<ClCompile Include="windows\OverwritePrompt.cpp" />

View File

@@ -13,6 +13,7 @@
#include <openrct2/GameState.h>
#include <openrct2/SpriteIds.h>
#include <openrct2/audio/Audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/entity/EntityRegistry.h>
#include <openrct2/entity/Peep.h>
@@ -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<Widget> groupWidgetsToInsert{};
std::vector<Widget> 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<int16_t>(baseCheckBox.left - 5),
.right = static_cast<int16_t>(baseCheckBox.right + 5),
.top = y,
.bottom = static_cast<int16_t>(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<int16_t>(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<WidgetIndex>(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<bool*>(
reinterpret_cast<size_t>(&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<int32_t>(getGameState().newsItems.GetArchived().size()) * CalculateItemHeight();
int32_t scrollHeight = static_cast<int32_t>(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;

View File

@@ -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 <cstddef>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Windows.h>
#include <openrct2/SpriteIds.h>
#include <openrct2/config/Config.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/ui/WindowManager.h>
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<bool*>(
reinterpret_cast<size_t>(&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<NewsOptionsWindow>(WindowClass::NotificationOptions, WW, WH, WF_CENTRE_SCREEN);
}
} // namespace OpenRCT2::Ui::Windows

View File

@@ -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();

View File

@@ -61,7 +61,7 @@ enum class WindowClass : uint8_t
InstallTrack = 49,
ClearScenery = 50,
SceneryScatter = 51,
NotificationOptions = 109,
Cheats = 110,
Research = 111,
Viewport = 112,