From 9ee40181b12ab0105975759f813032618164b038 Mon Sep 17 00:00:00 2001 From: otavepto <153766569+otavepto@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:07:15 +0300 Subject: [PATCH] * allow test achievement to include a progress randomly * allow showing the progress indicator in the achievement notifications, not only in the achievements list * don't play sound when showing progress notifications * disable progress notifications if the setting in the .ini was set --- dll/settings_parser.cpp | 2 + dll/steam_user_stats.cpp | 4 +- overlay_experimental/overlay/steam_overlay.h | 45 ++++++----- overlay_experimental/steam_overlay.cpp | 83 ++++++++++++++------ 4 files changed, 87 insertions(+), 47 deletions(-) diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index 33824196..b5375e98 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -360,6 +360,8 @@ static void load_overlay_appearance(class Settings *settings_client, class Setti auto pos = Overlay_Appearance::translate_notification_position(value); settings_client->overlay_appearance.chat_msg_pos = pos; settings_server->overlay_appearance.chat_msg_pos = pos; + } else { + PRINT_DEBUG("unknown overlay appearance setting"); } } catch (...) { } diff --git a/dll/steam_user_stats.cpp b/dll/steam_user_stats.cpp index dbafa907..2e8abd63 100644 --- a/dll/steam_user_stats.cpp +++ b/dll/steam_user_stats.cpp @@ -605,7 +605,7 @@ Steam_User_Stats::InternalSetResult Steam_User_Stats::set_achievement_inte result.notify_server = !settings->disable_sharing_stats_with_gameserver; - overlay->AddAchievementNotification(internal_name, user_achievements[internal_name]); + overlay->AddAchievementNotification(internal_name, user_achievements[internal_name], false); } } catch (...) {} @@ -1154,7 +1154,7 @@ bool Steam_User_Stats::IndicateAchievementProgress( const char *pchName, uint32 user_achievements[actual_ach_name]["progress"] = nCurProgress; user_achievements[actual_ach_name]["max_progress"] = nMaxProgress; save_achievements(); - overlay->AddAchievementNotification(actual_ach_name, user_achievements[actual_ach_name]); + overlay->AddAchievementNotification(actual_ach_name, user_achievements[actual_ach_name], true); } catch (...) {} { diff --git a/overlay_experimental/overlay/steam_overlay.h b/overlay_experimental/overlay/steam_overlay.h index a65f8452..2eea5b0b 100644 --- a/overlay_experimental/overlay/steam_overlay.h +++ b/overlay_experimental/overlay/steam_overlay.h @@ -57,25 +57,10 @@ enum class notification_type message = 0, invite, achievement, + achievement_progress, auto_accept_invite, }; -struct Notification -{ - static constexpr float width_percent = 0.25f; // percentage from total width - static constexpr std::chrono::milliseconds fade_in = std::chrono::milliseconds(2000); - static constexpr std::chrono::milliseconds fade_out = std::chrono::milliseconds(2000); - static constexpr std::chrono::milliseconds show_time = std::chrono::milliseconds(6000) + fade_in + fade_out; - static constexpr std::chrono::milliseconds fade_out_start = show_time - fade_out; - - int id{}; - uint8 type{}; - std::chrono::milliseconds start_time{}; - std::string message{}; - std::pair* frd{}; - std::weak_ptr icon{}; -}; - struct Overlay_Achievement { // avoids spam loading on failure @@ -98,6 +83,22 @@ struct Overlay_Achievement uint8_t icon_gray_load_trials = ICON_LOAD_MAX_TRIALS; }; +struct Notification +{ + static constexpr float width_percent = 0.25f; // percentage from total width + static constexpr std::chrono::milliseconds fade_in = std::chrono::milliseconds(2000); + static constexpr std::chrono::milliseconds fade_out = std::chrono::milliseconds(2000); + static constexpr std::chrono::milliseconds show_time = std::chrono::milliseconds(6000) + fade_in + fade_out; + static constexpr std::chrono::milliseconds fade_out_start = show_time - fade_out; + + int id{}; + uint8 type{}; + std::chrono::milliseconds start_time{}; + std::string message{}; + std::pair* frd{}; + std::optional ach{}; +}; + // notification coordinates { x, y } struct NotificationsCoords { @@ -201,7 +202,12 @@ class Steam_Overlay bool is_friend_joinable(std::pair &f); bool got_lobby(); - bool submit_notification(notification_type type, const std::string &msg, std::pair *frd = nullptr, const std::weak_ptr &icon = {}); + bool submit_notification( + notification_type type, + const std::string &msg, + std::pair *frd = nullptr, + Overlay_Achievement *ach = nullptr + ); void notify_sound_user_invite(friend_window_state& friend_state); void notify_sound_user_achievement(); @@ -215,6 +221,7 @@ class Steam_Overlay void set_next_notification_pos(std::pair scrn_size, std::chrono::milliseconds elapsed, const Notification ¬i, struct NotificationsCoords &coords); // factor controlling the amount of sliding during the animation, 0 means disabled float animate_factor(std::chrono::milliseconds elapsed); + void add_ach_progressbar(const Overlay_Achievement &ach); void build_notifications(float width, float height); // invite a single friend void invite_friend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking); @@ -236,7 +243,7 @@ class Steam_Overlay void add_auto_accept_invite_notification(); void add_invite_notification(std::pair &wnd_state); - void post_achievement_notification(Overlay_Achievement &ach); + void post_achievement_notification(Overlay_Achievement &ach, bool for_progress); void add_chat_message_notification(std::string const& message); void show_test_achievement(); @@ -278,7 +285,7 @@ public: void FriendConnect(Friend _friend); void FriendDisconnect(Friend _friend); - void AddAchievementNotification(const std::string &ach_name, nlohmann::json const& ach); + void AddAchievementNotification(const std::string &ach_name, nlohmann::json const& ach, bool for_progress); }; #else // EMU_OVERLAY diff --git a/overlay_experimental/steam_overlay.cpp b/overlay_experimental/steam_overlay.cpp index 8dd533df..e1a56ccf 100644 --- a/overlay_experimental/steam_overlay.cpp +++ b/overlay_experimental/steam_overlay.cpp @@ -593,7 +593,11 @@ int find_free_notification_id(std::vector const& notifications) return find_free_id(ids, base_friend_window_id); } -bool Steam_Overlay::submit_notification(notification_type type, const std::string &msg, std::pair *frd, const std::weak_ptr &icon) +bool Steam_Overlay::submit_notification( + notification_type type, + const std::string &msg, + std::pair *frd, + Overlay_Achievement *ach) { PRINT_DEBUG("%i", (int)type); std::lock_guard lock(overlay_mutex); @@ -611,7 +615,7 @@ bool Steam_Overlay::submit_notification(notification_type type, const std::strin notif.type = (uint8)type; notif.message = msg; notif.frd = frd; - notif.icon = icon; + if (ach) notif.ach = *ach; notifications.emplace_back(notif); allow_renderer_frame_processing(true); @@ -623,6 +627,7 @@ bool Steam_Overlay::submit_notification(notification_type type, const std::strin break; // not effective + case notification_type::achievement_progress: case notification_type::achievement: case notification_type::auto_accept_invite: case notification_type::message: @@ -881,11 +886,12 @@ void Steam_Overlay::set_next_notification_pos(std::pair scrn_size, false, noti_width - padding_all_sides - global_style.ItemSpacing.x ).y; - float noti_height = msg_height + 2 * global_style.WindowPadding.y; + float noti_height = msg_height; // get the required position Overlay_Appearance::NotificationPosition pos = Overlay_Appearance::default_pos; switch ((notification_type)noti.type) { + case notification_type::achievement_progress: case notification_type::achievement: { pos = settings->overlay_appearance.ach_earned_pos; @@ -895,12 +901,18 @@ void Steam_Overlay::set_next_notification_pos(std::pair scrn_size, false, noti_width - padding_all_sides - global_style.ItemSpacing.x - settings->overlay_appearance.icon_size ).y; - const float new_noti_height = new_msg_height + 2 * global_style.WindowPadding.y; + const float new_noti_height = new_msg_height; - float biggest_noti_height = settings->overlay_appearance.icon_size + 2 * global_style.WindowPadding.y; + float biggest_noti_height = settings->overlay_appearance.icon_size; if (biggest_noti_height < new_noti_height) biggest_noti_height = new_noti_height; noti_height = biggest_noti_height; + + if ((notification_type)noti.type == notification_type::achievement_progress) { + if (!noti.ach.value().achieved && noti.ach.value().max_progress > 0) { + noti_height += settings->overlay_appearance.font_size + global_style.WindowPadding.y; + } + } } break; @@ -908,6 +920,8 @@ void Steam_Overlay::set_next_notification_pos(std::pair scrn_size, case notification_type::message: pos = settings->overlay_appearance.chat_msg_pos; break; default: /* satisfy compiler warning */ break; } + // add some y padding for niceness + noti_height += 2 * global_style.WindowPadding.y; // 0 on the y-axis is top, 0 on the x-axis is left float x = 0.0f; @@ -988,6 +1002,15 @@ float Steam_Overlay::animate_factor(std::chrono::milliseconds elapsed) return factor; } +void Steam_Overlay::add_ach_progressbar(const Overlay_Achievement &ach) +{ + if (!ach.achieved && ach.max_progress > 0) { + char buf[32]{}; + sprintf(buf, "%.1f/%.1f", ach.progress, ach.max_progress); + ImGui::ProgressBar(ach.progress / ach.max_progress, { -1 , settings->overlay_appearance.font_size }, buf); + } +} + void Steam_Overlay::build_notifications(float width, float height) { auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); @@ -1024,6 +1047,7 @@ void Steam_Overlay::build_notifications(float width, float height) switch ((notification_type)it->type) { // games like "Mafia Definitive Edition" will pause the entire game/scene if focus was stolen // be less intrusive for notifications that do not require interaction + case notification_type::achievement_progress: case notification_type::achievement: case notification_type::auto_accept_invite: case notification_type::message: @@ -1043,21 +1067,28 @@ void Steam_Overlay::build_notifications(float width, float height) if (ImGui::Begin(wnd_name.c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | extra_flags)) { switch ((notification_type)it->type) { + case notification_type::achievement_progress: case notification_type::achievement: { - if (!it->icon.expired() && ImGui::BeginTable("imgui_table", 2)) { + const auto &ach = it->ach.value(); + const auto &msg = ach.title + "\n" + ach.description; + if (!ach.icon.expired() && ImGui::BeginTable("imgui_table", 2)) { ImGui::TableSetupColumn("imgui_table_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size); ImGui::TableSetupColumn("imgui_table_text"); ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size); ImGui::TableSetColumnIndex(0); - ImGui::Image((ImTextureID)*it->icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size)); + ImGui::Image((ImTextureID)*ach.icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size)); ImGui::TableSetColumnIndex(1); - ImGui::TextWrapped("%s", it->message.c_str()); + ImGui::TextWrapped("%s", msg.c_str()); ImGui::EndTable(); } else { - ImGui::TextWrapped("%s", it->message.c_str()); + ImGui::TextWrapped("%s", msg.c_str()); + } + + if ((notification_type)it->type == notification_type::achievement_progress) { + add_ach_progressbar(ach); } } break; @@ -1110,6 +1141,7 @@ void Steam_Overlay::build_notifications(float width, float height) break; // not effective + case notification_type::achievement_progress: case notification_type::achievement: case notification_type::auto_accept_invite: case notification_type::message: @@ -1164,7 +1196,7 @@ void Steam_Overlay::add_invite_notification(std::pairdisable_overlay_achievement_notification) return; @@ -1172,12 +1204,12 @@ void Steam_Overlay::post_achievement_notification(Overlay_Achievement &ach) std::lock_guard lock(overlay_mutex); if (!Ready()) return; - try_load_ach_icon(ach, true); + try_load_ach_icon(ach, !for_progress); submit_notification( - notification_type::achievement, - ach.title + "\n" + ach.description, + for_progress ? notification_type::achievement_progress : notification_type::achievement, {}, - ach.icon + {}, + &ach ); } @@ -1487,12 +1519,8 @@ void Steam_Overlay::render_main_window() ImGui::TextColored(ImVec4(0, 255, 0, 255), translationAchievedOn[current_language], buffer); } else { ImGui::TextColored(ImVec4(255, 0, 0, 255), "%s", translationNotAchieved[current_language]); - if (x.max_progress > 0) { - char buf[32]{}; - sprintf(buf, "%d/%d", (int)x.progress, (int)x.max_progress); - ImGui::ProgressBar(x.progress / x.max_progress, { -1 , settings->overlay_appearance.font_size }, buf); - } } + add_ach_progressbar(x); if (could_create_ach_table_entry) ImGui::EndTable(); @@ -1988,9 +2016,10 @@ void Steam_Overlay::FriendDisconnect(Friend _friend) } // show a notification when the user unlocks an achievement -void Steam_Overlay::AddAchievementNotification(const std::string &ach_name, nlohmann::json const &ach) +void Steam_Overlay::AddAchievementNotification(const std::string &ach_name, nlohmann::json const &ach, bool for_progress) { if (settings->disable_overlay) return; + if (for_progress && settings->disable_overlay_achievement_progress) return; PRINT_DEBUG_ENTRY(); std::lock_guard lock(overlay_mutex); @@ -2004,14 +2033,16 @@ void Steam_Overlay::AddAchievementNotification(const std::string &ach_name, nloh for (auto &a : achievements) { if (a.name == ach_name) { - a.achieved = ach.value("earned", false); - a.unlock_time = ach.value("earned_time", static_cast(0)); - a.progress = ach.value("progress", static_cast(0)); - a.max_progress = ach.value("max_progress", static_cast(0)); + try { + a.achieved = ach.value("earned", false); + a.unlock_time = ach.value("earned_time", static_cast(0)); + a.progress = ach.value("progress", static_cast(0)); + a.max_progress = ach.value("max_progress", static_cast(0)); + } catch(...) {} if (a.achieved) { - post_achievement_notification(a); - notify_sound_user_achievement(); + post_achievement_notification(a, for_progress); + if (!for_progress) notify_sound_user_achievement(); } break; }