diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index c03f3fa568..202cc767e4 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3927,6 +3927,24 @@ STR_5585 :{SMALLFONT}{BLACK}Unlocks ride operation limits, allowing for thing STR_5586 :Automatically open shops and stalls STR_5587 :{SMALLFONT}{BLACK}When enabled, shops and stalls will be automatically opened after building them STR_5588 :{SMALLFONT}{BLACK}Play with other players +STR_5589 :Notification Settings +STR_5590 :Park awards +STR_5591 :Marketing campaign has finished +STR_5592 :Park warnings +STR_5593 :Park rating warnings +STR_5594 :Ride has broken down +STR_5595 :Ride has crashed +STR_5596 :Ride warnings +STR_5597 :Ride / scenery researched +STR_5598 :Guest warnings +STR_5599 :Guest is lost +STR_5600 :Guest has left the park +STR_5601 :Guest is queuing for ride +STR_5602 :Guest is on ride +STR_5603 :Guest has left ride +STR_5604 :Guest has bought item +STR_5605 :Guest has used facility +STR_5606 :Guest has died ##################### # Rides/attractions # diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 251e80cd70..347d55880e 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -103,6 +103,7 @@ + @@ -374,7 +375,7 @@ $(OpenRCT2_DEFINES);DEBUG;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;CURL_STATICLIB;SDL_MAIN_HANDLED;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreaded true - $(IntDir)fake\%(RelativeDir) + $(IntDir)\%(RelativeDir) 4013 false diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters index 9e4fb16a0d..38cb760f4e 100644 --- a/openrct2.vcxproj.filters +++ b/openrct2.vcxproj.filters @@ -552,9 +552,8 @@ Source - - Source\Drawing - + + @@ -827,9 +826,6 @@ Source\Interface - - Source\Core - Source\Drawing diff --git a/src/config.c b/src/config.c index 708154cb43..241862f72e 100644 --- a/src/config.c +++ b/src/config.c @@ -260,13 +260,34 @@ config_property_definition _networkDefinitions[] = { { offsetof(network_configuration, provider_website), "provider_website", CONFIG_VALUE_TYPE_STRING, {.value_string = NULL }, NULL } }; +config_property_definition _notificationsDefinitions[] = { + { offsetof(notification_configuration, park_award), "park_award", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, park_marketing_campaign_finished), "park_marketing_campaign_finished", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, park_warnings), "park_warnings", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, park_rating_warnings), "park_rating_warnings", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, ride_broken_down), "ride_broken_down", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, ride_crashed), "ride_crashed", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, ride_warnings), "ride_warnings", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, ride_researched), "ride_researched", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_warnings), "guest_warnings", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_lost), "guest_lost", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, + { offsetof(notification_configuration, guest_entered_left_park), "guest_entered_left_park", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_queuing_for_ride), "guest_queuing_for_ride", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_on_ride), "guest_on_ride", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_left_ride), "guest_left_ride", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_bought_item), "guest_bought_item", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_used_facility), "guest_used_facility", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(notification_configuration, guest_died), "guest_died", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, +}; + config_section_definition _sectionDefinitions[] = { { &gConfigGeneral, "general", _generalDefinitions, countof(_generalDefinitions) }, { &gConfigInterface, "interface", _interfaceDefinitions, countof(_interfaceDefinitions) }, { &gConfigSound, "sound", _soundDefinitions, countof(_soundDefinitions) }, { &gConfigCheat, "cheat", _cheatDefinitions, countof(_cheatDefinitions) }, { &gConfigTwitch, "twitch", _twitchDefinitions, countof(_twitchDefinitions) }, - { &gConfigNetwork, "network", _networkDefinitions, countof(_networkDefinitions) } + { &gConfigNetwork, "network", _networkDefinitions, countof(_networkDefinitions) }, + { &gConfigNotifications, "notifications", _notificationsDefinitions, countof(_notificationsDefinitions) }, }; #pragma endregion @@ -277,6 +298,7 @@ sound_configuration gConfigSound; cheat_configuration gConfigCheat; twitch_configuration gConfigTwitch; network_configuration gConfigNetwork; +notification_configuration gConfigNotifications; themes_configuration gConfigThemes; title_sequences_configuration gConfigTitleSequences; diff --git a/src/config.h b/src/config.h index 49d3ef42ad..e45e672991 100644 --- a/src/config.h +++ b/src/config.h @@ -229,6 +229,26 @@ typedef struct { utf8string provider_website; } network_configuration; +typedef struct { + bool park_award; + bool park_marketing_campaign_finished; + bool park_warnings; + bool park_rating_warnings; + bool ride_broken_down; + bool ride_crashed; + bool ride_warnings; + bool ride_researched; + bool guest_warnings; + bool guest_lost; + bool guest_entered_left_park; + bool guest_queuing_for_ride; + bool guest_on_ride; + bool guest_left_ride; + bool guest_bought_item; + bool guest_used_facility; + bool guest_died; +} notification_configuration; + typedef struct theme_window { uint8 colours[6]; @@ -303,6 +323,7 @@ extern sound_configuration gConfigSound; extern cheat_configuration gConfigCheat; extern twitch_configuration gConfigTwitch; extern network_configuration gConfigNetwork; +extern notification_configuration gConfigNotifications; extern themes_configuration gConfigThemes; extern title_sequences_configuration gConfigTitleSequences; diff --git a/src/interface/window.h b/src/interface/window.h index 049127b94f..e538d69091 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -434,6 +434,7 @@ enum { WC_TRACK_DELETE_PROMPT = 48, WC_INSTALL_TRACK = 49, WC_CLEAR_SCENERY = 50, + WC_NOTIFICATION_OPTIONS = 109, WC_CHEATS = 110, WC_RESEARCH = 111, WC_VIEWPORT = 112, @@ -627,6 +628,7 @@ void window_install_track_open(const char* path); void window_banner_open(rct_windownumber number); void window_sign_open(rct_windownumber number); void window_sign_small_open(rct_windownumber number); +void window_news_options_open(); void window_cheats_open(); void window_player_list_open(); void window_network_status_open(const char* text); diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index 517c316c3b..d3e09e203b 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2210,6 +2210,25 @@ enum { STR_SHOW_MULTIPLAYER_TIP = 5588, + STR_NOTIFICATION_SETTINGS = 5589, + STR_NOTIFICATION_PARK_AWARD = 5590, + STR_NOTIFICATION_PARK_MARKETING_CAMPAIGN_FINISHED = 5591, + STR_NOTIFICATION_PARK_WARNINGS = 5592, + STR_NOTIFICATION_PARK_RATING_WARNINGS = 5593, + STR_NOTIFICATION_RIDE_BROKEN_DOWN = 5594, + STR_NOTIFICATION_RIDE_CRASHED = 5595, + STR_NOTIFICATION_RIDE_WARNINGS = 5596, + STR_NOTIFICATION_RIDE_RESEARCHED = 5597, + STR_NOTIFICATION_GUEST_WARNINGS = 5598, + STR_NOTIFICATION_GUEST_LOST = 5599, + STR_NOTIFICATION_GUEST_ENTERED_LEFT_PARK = 5600, + STR_NOTIFICATION_GUEST_QUEUING_FOR_RIDE = 5601, + STR_NOTIFICATION_GUEST_ON_RIDE = 5602, + STR_NOTIFICATION_GUEST_LEFT_RIDE = 5603, + STR_NOTIFICATION_GUEST_BOUGHT_ITEM = 5604, + STR_NOTIFICATION_GUEST_USED_FACILITY = 5605, + STR_NOTIFICATION_GUEST_DIED = 5606, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/windows/news.c b/src/windows/news.c index 2f80ff15f4..5885811365 100644 --- a/src/windows/news.c +++ b/src/windows/news.c @@ -33,6 +33,7 @@ enum WINDOW_NEWS_WIDGET_IDX { WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE, + WIDX_SETTINGS, WIDX_SCROLL }; @@ -40,7 +41,8 @@ static rct_widget window_news_widgets[] = { { WWT_FRAME, 0, 0, 399, 0, 299, 0x0FFFFFFFF, STR_NONE }, // panel / background { WWT_CAPTION, 0, 1, 398, 1, 14, STR_RECENT_MESSAGES, STR_WINDOW_TITLE_TIP }, // title bar { WWT_CLOSEBOX, 0, 387, 397, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button - { WWT_SCROLL, 0, 4, 395, 18, 295, 2, STR_NONE }, // scroll + { WWT_FLATBTN, 0, 372, 395, 18, 41, 5201, STR_NONE }, // settings + { WWT_SCROLL, 0, 4, 395, 44, 295, 2, STR_NONE }, // scroll { WIDGETS_END }, }; @@ -103,7 +105,7 @@ void window_news_open() 0 ); window->widgets = window_news_widgets; - window->enabled_widgets = (1 << WIDX_CLOSE); + window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_SETTINGS); window_init_scroll_widgets(window); window->news.var_480 = -1; } @@ -130,6 +132,9 @@ static void window_news_mouseup(rct_window *w, int widgetIndex) case WIDX_CLOSE: window_close(w); break; + case WIDX_SETTINGS: + window_news_options_open(); + break; } } diff --git a/src/windows/news_options.c b/src/windows/news_options.c new file mode 100644 index 0000000000..20a8760e24 --- /dev/null +++ b/src/windows/news_options.c @@ -0,0 +1,279 @@ +#include "../config.h" +#include "../interface/widget.h" +#include "../interface/window.h" +#include "../localisation/localisation.h" + +enum { + NOTIFICATION_CATEGORY_PARK, + NOTIFICATION_CATEGORY_RIDE, + NOTIFICATION_CATEGORY_GUEST +}; + +typedef struct { + uint8 category; + rct_string_id caption; + size_t config_offset; + +} notification_def; + +static const notification_def NewsItemOptionDefinitions[] = { + { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_AWARD, offsetof(notification_configuration, park_award) }, + { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_MARKETING_CAMPAIGN_FINISHED, offsetof(notification_configuration, park_marketing_campaign_finished) }, + { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_WARNINGS, offsetof(notification_configuration, park_warnings) }, + { NOTIFICATION_CATEGORY_PARK, STR_NOTIFICATION_PARK_RATING_WARNINGS, offsetof(notification_configuration, park_rating_warnings) }, + { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_BROKEN_DOWN, offsetof(notification_configuration, ride_broken_down) }, + { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_CRASHED, offsetof(notification_configuration, ride_crashed) }, + { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_WARNINGS, offsetof(notification_configuration, ride_warnings) }, + { NOTIFICATION_CATEGORY_RIDE, STR_NOTIFICATION_RIDE_RESEARCHED, offsetof(notification_configuration, ride_researched) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_WARNINGS, offsetof(notification_configuration, guest_warnings) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_LOST, offsetof(notification_configuration, guest_lost) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_ENTERED_LEFT_PARK, offsetof(notification_configuration, guest_entered_left_park) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_QUEUING_FOR_RIDE, offsetof(notification_configuration, guest_queuing_for_ride) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_ON_RIDE, offsetof(notification_configuration, guest_on_ride) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_LEFT_RIDE, offsetof(notification_configuration, guest_left_ride) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_BOUGHT_ITEM, offsetof(notification_configuration, guest_bought_item) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_USED_FACILITY, offsetof(notification_configuration, guest_used_facility) }, + { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_DIED, offsetof(notification_configuration, guest_died) }, +}; + +enum WINDOW_NEWS_WIDGET_IDX { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_TAB_CONTENT_PANEL, + WIDX_TAB_PARK, + WIDX_TAB_RIDE, + WIDX_TAB_GUEST, + WIDX_CHECKBOX_0 +}; + +static rct_widget window_news_options_widgets[] = { + { WWT_FRAME, 0, 0, 399, 0, 299, 0x0FFFFFFFF, STR_NONE }, // panel / background + { WWT_CAPTION, 0, 1, 398, 1, 14, STR_NOTIFICATION_SETTINGS, STR_WINDOW_TITLE_TIP }, // title bar + { WWT_CLOSEBOX, 0, 387, 397, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button + { WWT_RESIZE, 1, 0, 399, 43, 299, 0x0FFFFFFFF, STR_NONE }, // tab content panel + { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_NONE }, // tab 1 + { WWT_TAB, 1, 34, 64, 17, 43, 0x02000144E, STR_NONE }, // tab 2 + { WWT_TAB, 1, 65, 95, 17, 43, 0x02000144E, STR_NONE }, // tab 2 + + { WWT_CHECKBOX, 2, 3, 349, 46, 59, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + { WWT_CHECKBOX, 2, 0, 0, 0, 0, STR_NONE, STR_NONE }, + + { WIDGETS_END }, +}; + +static void window_news_options_mouseup(rct_window *w, int widgetIndex); +static void window_news_options_update(rct_window *w); +static void window_news_options_invalidate(rct_window *w); +static void window_news_options_paint(rct_window *w, rct_drawpixelinfo *dpi); + +static rct_window_event_list window_news_options_events = { + NULL, + window_news_options_mouseup, + NULL, + NULL, + NULL, + NULL, + window_news_options_update, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + window_news_options_invalidate, + window_news_options_paint, + NULL +}; + +static void window_news_options_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi); +static bool *get_notification_value_ptr(const notification_def *ndef); + +void window_news_options_open() +{ + rct_window* window; + + // Check if window is already open + window = window_bring_to_front_by_class(WC_NOTIFICATION_OPTIONS); + if (window == NULL) { + window = window_create_centred( + 400, + 300, + &window_news_options_events, + WC_NOTIFICATION_OPTIONS, + 0 + ); + window->widgets = window_news_options_widgets; + window->enabled_widgets = + (1 << WIDX_CLOSE) | + (1 << WIDX_TAB_PARK) | + (1 << WIDX_TAB_RIDE) | + (1 << WIDX_TAB_GUEST); + window_init_scroll_widgets(window); + window->colours[0] = COLOUR_GREY; + window->colours[1] = COLOUR_LIGHT_BLUE; + window->colours[2] = COLOUR_LIGHT_BLUE; + } +} + +static void window_news_options_mouseup(rct_window *w, int widgetIndex) +{ + switch (widgetIndex) { + case WIDX_CLOSE: + window_close(w); + break; + case WIDX_TAB_PARK: + case WIDX_TAB_RIDE: + case WIDX_TAB_GUEST: + w->page = widgetIndex - WIDX_TAB_PARK; + w->frame_no = 0; + window_invalidate(w); + break; + default: + { + int checkBoxIndex = widgetIndex - WIDX_CHECKBOX_0; + if (checkBoxIndex >= 0) { + int matchIndex = 0; + for (int i = 0; i < countof(NewsItemOptionDefinitions); i++) { + const notification_def *ndef = &NewsItemOptionDefinitions[i]; + if (ndef->category != w->page) continue; + + if (matchIndex == checkBoxIndex) { + // Toggle value + bool *configValue = get_notification_value_ptr(ndef); + *configValue = !(*configValue); + + config_save_default(); + + widget_invalidate(w, widgetIndex); + break; + } + matchIndex++; + } + } + break; + } + } +} + +static void window_news_options_update(rct_window *w) +{ + w->frame_no++; + widget_invalidate(w, WIDX_TAB_PARK + w->page); +} + +static void window_news_options_invalidate(rct_window *w) +{ + // colour_scheme_update(w); + + // Set pressed tab + w->pressed_widgets &= ~(1ULL << WIDX_TAB_PARK); + w->pressed_widgets &= ~(1ULL << WIDX_TAB_RIDE); + w->pressed_widgets &= ~(1ULL << WIDX_TAB_GUEST); + w->pressed_widgets |= (1ULL << (WIDX_TAB_PARK + w->page)); + + // Set checkboxes + rct_widget *baseCheckBox = &w->widgets[WIDX_CHECKBOX_0]; + int y = baseCheckBox->top; + + int checkboxWidgetIndex = WIDX_CHECKBOX_0; + rct_widget *checkboxWidget = &w->widgets[checkboxWidgetIndex]; + for (int i = 0; i < countof(NewsItemOptionDefinitions); i++) { + const notification_def *ndef = &NewsItemOptionDefinitions[i]; + if (ndef->category != w->page) continue; + + w->enabled_widgets |= (1ULL << checkboxWidgetIndex); + + checkboxWidget->type = WWT_CHECKBOX; + checkboxWidget->left = baseCheckBox->left; + checkboxWidget->right = baseCheckBox->right; + checkboxWidget->top = y; + checkboxWidget->bottom = checkboxWidget->top + 13; + checkboxWidget->image = ndef->caption; + + const bool *configValue = get_notification_value_ptr(ndef); + widget_set_checkbox_value(w, checkboxWidgetIndex, *configValue); + + checkboxWidgetIndex++; + checkboxWidget++; + y += 13; + } + + // Remove unused checkboxes + while (checkboxWidget->type != WWT_LAST) { + w->enabled_widgets &= ~(1ULL << checkboxWidgetIndex); + + checkboxWidget->type = WWT_EMPTY; + checkboxWidgetIndex++; + checkboxWidget++; + } + + // Resize window to fit checkboxes exactly + y += 3; + + if (w->height != y) { + window_invalidate(w); + w->height = y; + w->widgets[WIDX_BACKGROUND].bottom = y - 1; + w->widgets[WIDX_TAB_CONTENT_PANEL].bottom = y - 1; + window_invalidate(w); + } +} + +static void window_news_options_paint(rct_window *w, rct_drawpixelinfo *dpi) +{ + window_draw_widgets(w, dpi); + window_news_options_draw_tab_images(w, dpi); +} + +const int window_news_option_tab_animation_divisor[] = { 1, 4, 4 }; +const int window_news_option_tab_animation_frames[] = { 1, 16, 8 }; + +static void window_news_options_draw_tab_image(rct_window *w, rct_drawpixelinfo *dpi, int page, int spriteIndex) +{ + int widgetIndex = WIDX_TAB_PARK + page; + + if (!(w->disabled_widgets & (1LL << widgetIndex))) { + if (w->page == page) { + int numFrames = window_news_option_tab_animation_frames[w->page]; + if (numFrames > 1) { + int frame = w->frame_no / window_news_option_tab_animation_divisor[w->page]; + spriteIndex += (frame % numFrames); + } + } + + gfx_draw_sprite(dpi, spriteIndex, w->x + w->widgets[widgetIndex].left, w->y + w->widgets[widgetIndex].top, 0); + } +} + +static void window_news_options_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi) +{ + window_news_options_draw_tab_image(w, dpi, NOTIFICATION_CATEGORY_PARK, 5466); + window_news_options_draw_tab_image(w, dpi, NOTIFICATION_CATEGORY_RIDE, 5442); + window_news_options_draw_tab_image(w, dpi, NOTIFICATION_CATEGORY_GUEST, 5568); +} + +static bool *get_notification_value_ptr(const notification_def *ndef) +{ + bool *configValue = (bool*)((size_t)&gConfigNotifications + ndef->config_offset); + return configValue; +}