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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)) },
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -61,7 +61,7 @@ enum class WindowClass : uint8_t
|
||||
InstallTrack = 49,
|
||||
ClearScenery = 50,
|
||||
SceneryScatter = 51,
|
||||
NotificationOptions = 109,
|
||||
|
||||
Cheats = 110,
|
||||
Research = 111,
|
||||
Viewport = 112,
|
||||
|
||||
Reference in New Issue
Block a user