* 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
This commit is contained in:
otavepto 2024-06-06 21:07:15 +03:00
parent 7203e320ab
commit 9ee40181b1
4 changed files with 87 additions and 47 deletions

View File

@ -360,6 +360,8 @@ static void load_overlay_appearance(class Settings *settings_client, class Setti
auto pos = Overlay_Appearance::translate_notification_position(value); auto pos = Overlay_Appearance::translate_notification_position(value);
settings_client->overlay_appearance.chat_msg_pos = pos; settings_client->overlay_appearance.chat_msg_pos = pos;
settings_server->overlay_appearance.chat_msg_pos = pos; settings_server->overlay_appearance.chat_msg_pos = pos;
} else {
PRINT_DEBUG("unknown overlay appearance setting");
} }
} catch (...) { } } catch (...) { }

View File

@ -605,7 +605,7 @@ Steam_User_Stats::InternalSetResult<bool> Steam_User_Stats::set_achievement_inte
result.notify_server = !settings->disable_sharing_stats_with_gameserver; 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 (...) {} } 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]["progress"] = nCurProgress;
user_achievements[actual_ach_name]["max_progress"] = nMaxProgress; user_achievements[actual_ach_name]["max_progress"] = nMaxProgress;
save_achievements(); save_achievements();
overlay->AddAchievementNotification(actual_ach_name, user_achievements[actual_ach_name]); overlay->AddAchievementNotification(actual_ach_name, user_achievements[actual_ach_name], true);
} catch (...) {} } catch (...) {}
{ {

View File

@ -57,25 +57,10 @@ enum class notification_type
message = 0, message = 0,
invite, invite,
achievement, achievement,
achievement_progress,
auto_accept_invite, 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<const Friend, friend_window_state>* frd{};
std::weak_ptr<uint64_t> icon{};
};
struct Overlay_Achievement struct Overlay_Achievement
{ {
// avoids spam loading on failure // avoids spam loading on failure
@ -98,6 +83,22 @@ struct Overlay_Achievement
uint8_t icon_gray_load_trials = ICON_LOAD_MAX_TRIALS; 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<const Friend, friend_window_state>* frd{};
std::optional<Overlay_Achievement> ach{};
};
// notification coordinates { x, y } // notification coordinates { x, y }
struct NotificationsCoords struct NotificationsCoords
{ {
@ -201,7 +202,12 @@ class Steam_Overlay
bool is_friend_joinable(std::pair<const Friend, friend_window_state> &f); bool is_friend_joinable(std::pair<const Friend, friend_window_state> &f);
bool got_lobby(); bool got_lobby();
bool submit_notification(notification_type type, const std::string &msg, std::pair<const Friend, friend_window_state> *frd = nullptr, const std::weak_ptr<uint64_t> &icon = {}); bool submit_notification(
notification_type type,
const std::string &msg,
std::pair<const Friend, friend_window_state> *frd = nullptr,
Overlay_Achievement *ach = nullptr
);
void notify_sound_user_invite(friend_window_state& friend_state); void notify_sound_user_invite(friend_window_state& friend_state);
void notify_sound_user_achievement(); void notify_sound_user_achievement();
@ -215,6 +221,7 @@ class Steam_Overlay
void set_next_notification_pos(std::pair<float, float> scrn_size, std::chrono::milliseconds elapsed, const Notification &noti, struct NotificationsCoords &coords); void set_next_notification_pos(std::pair<float, float> scrn_size, std::chrono::milliseconds elapsed, const Notification &noti, struct NotificationsCoords &coords);
// factor controlling the amount of sliding during the animation, 0 means disabled // factor controlling the amount of sliding during the animation, 0 means disabled
float animate_factor(std::chrono::milliseconds elapsed); float animate_factor(std::chrono::milliseconds elapsed);
void add_ach_progressbar(const Overlay_Achievement &ach);
void build_notifications(float width, float height); void build_notifications(float width, float height);
// invite a single friend // invite a single friend
void invite_friend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking); 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_auto_accept_invite_notification();
void add_invite_notification(std::pair<const Friend, friend_window_state> &wnd_state); void add_invite_notification(std::pair<const Friend, friend_window_state> &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 add_chat_message_notification(std::string const& message);
void show_test_achievement(); void show_test_achievement();
@ -278,7 +285,7 @@ public:
void FriendConnect(Friend _friend); void FriendConnect(Friend _friend);
void FriendDisconnect(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 #else // EMU_OVERLAY

View File

@ -593,7 +593,11 @@ int find_free_notification_id(std::vector<Notification> const& notifications)
return find_free_id(ids, base_friend_window_id); return find_free_id(ids, base_friend_window_id);
} }
bool Steam_Overlay::submit_notification(notification_type type, const std::string &msg, std::pair<const Friend, friend_window_state> *frd, const std::weak_ptr<uint64_t> &icon) bool Steam_Overlay::submit_notification(
notification_type type,
const std::string &msg,
std::pair<const Friend, friend_window_state> *frd,
Overlay_Achievement *ach)
{ {
PRINT_DEBUG("%i", (int)type); PRINT_DEBUG("%i", (int)type);
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
@ -611,7 +615,7 @@ bool Steam_Overlay::submit_notification(notification_type type, const std::strin
notif.type = (uint8)type; notif.type = (uint8)type;
notif.message = msg; notif.message = msg;
notif.frd = frd; notif.frd = frd;
notif.icon = icon; if (ach) notif.ach = *ach;
notifications.emplace_back(notif); notifications.emplace_back(notif);
allow_renderer_frame_processing(true); allow_renderer_frame_processing(true);
@ -623,6 +627,7 @@ bool Steam_Overlay::submit_notification(notification_type type, const std::strin
break; break;
// not effective // not effective
case notification_type::achievement_progress:
case notification_type::achievement: case notification_type::achievement:
case notification_type::auto_accept_invite: case notification_type::auto_accept_invite:
case notification_type::message: case notification_type::message:
@ -881,11 +886,12 @@ void Steam_Overlay::set_next_notification_pos(std::pair<float, float> scrn_size,
false, false,
noti_width - padding_all_sides - global_style.ItemSpacing.x noti_width - padding_all_sides - global_style.ItemSpacing.x
).y; ).y;
float noti_height = msg_height + 2 * global_style.WindowPadding.y; float noti_height = msg_height;
// get the required position // get the required position
Overlay_Appearance::NotificationPosition pos = Overlay_Appearance::default_pos; Overlay_Appearance::NotificationPosition pos = Overlay_Appearance::default_pos;
switch ((notification_type)noti.type) { switch ((notification_type)noti.type) {
case notification_type::achievement_progress:
case notification_type::achievement: { case notification_type::achievement: {
pos = settings->overlay_appearance.ach_earned_pos; pos = settings->overlay_appearance.ach_earned_pos;
@ -895,12 +901,18 @@ void Steam_Overlay::set_next_notification_pos(std::pair<float, float> scrn_size,
false, false,
noti_width - padding_all_sides - global_style.ItemSpacing.x - settings->overlay_appearance.icon_size noti_width - padding_all_sides - global_style.ItemSpacing.x - settings->overlay_appearance.icon_size
).y; ).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; if (biggest_noti_height < new_noti_height) biggest_noti_height = new_noti_height;
noti_height = biggest_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; break;
@ -908,6 +920,8 @@ void Steam_Overlay::set_next_notification_pos(std::pair<float, float> scrn_size,
case notification_type::message: pos = settings->overlay_appearance.chat_msg_pos; break; case notification_type::message: pos = settings->overlay_appearance.chat_msg_pos; break;
default: /* satisfy compiler warning */ 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 // 0 on the y-axis is top, 0 on the x-axis is left
float x = 0.0f; float x = 0.0f;
@ -988,6 +1002,15 @@ float Steam_Overlay::animate_factor(std::chrono::milliseconds elapsed)
return factor; 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) void Steam_Overlay::build_notifications(float width, float height)
{ {
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()); auto now = std::chrono::duration_cast<std::chrono::milliseconds>(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) { switch ((notification_type)it->type) {
// games like "Mafia Definitive Edition" will pause the entire game/scene if focus was stolen // 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 // be less intrusive for notifications that do not require interaction
case notification_type::achievement_progress:
case notification_type::achievement: case notification_type::achievement:
case notification_type::auto_accept_invite: case notification_type::auto_accept_invite:
case notification_type::message: 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, if (ImGui::Begin(wnd_name.c_str(), nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | extra_flags)) { ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | extra_flags)) {
switch ((notification_type)it->type) { switch ((notification_type)it->type) {
case notification_type::achievement_progress:
case notification_type::achievement: { 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_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size);
ImGui::TableSetupColumn("imgui_table_text"); ImGui::TableSetupColumn("imgui_table_text");
ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size); ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size);
ImGui::TableSetColumnIndex(0); 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::TableSetColumnIndex(1);
ImGui::TextWrapped("%s", it->message.c_str()); ImGui::TextWrapped("%s", msg.c_str());
ImGui::EndTable(); ImGui::EndTable();
} else { } 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; break;
@ -1110,6 +1141,7 @@ void Steam_Overlay::build_notifications(float width, float height)
break; break;
// not effective // not effective
case notification_type::achievement_progress:
case notification_type::achievement: case notification_type::achievement:
case notification_type::auto_accept_invite: case notification_type::auto_accept_invite:
case notification_type::message: case notification_type::message:
@ -1164,7 +1196,7 @@ void Steam_Overlay::add_invite_notification(std::pair<const Friend, friend_windo
submit_notification(notification_type::invite, tmp, &wnd_state); submit_notification(notification_type::invite, tmp, &wnd_state);
} }
void Steam_Overlay::post_achievement_notification(Overlay_Achievement &ach) void Steam_Overlay::post_achievement_notification(Overlay_Achievement &ach, bool for_progress)
{ {
if (settings->disable_overlay_achievement_notification) return; if (settings->disable_overlay_achievement_notification) return;
@ -1172,12 +1204,12 @@ void Steam_Overlay::post_achievement_notification(Overlay_Achievement &ach)
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (!Ready()) return; if (!Ready()) return;
try_load_ach_icon(ach, true); try_load_ach_icon(ach, !for_progress);
submit_notification( submit_notification(
notification_type::achievement, for_progress ? notification_type::achievement_progress : notification_type::achievement,
ach.title + "\n" + ach.description,
{}, {},
ach.icon {},
&ach
); );
} }
@ -1487,12 +1519,8 @@ void Steam_Overlay::render_main_window()
ImGui::TextColored(ImVec4(0, 255, 0, 255), translationAchievedOn[current_language], buffer); ImGui::TextColored(ImVec4(0, 255, 0, 255), translationAchievedOn[current_language], buffer);
} else { } else {
ImGui::TextColored(ImVec4(255, 0, 0, 255), "%s", translationNotAchieved[current_language]); 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(); 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 // 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 (settings->disable_overlay) return;
if (for_progress && settings->disable_overlay_achievement_progress) return;
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
@ -2004,14 +2033,16 @@ void Steam_Overlay::AddAchievementNotification(const std::string &ach_name, nloh
for (auto &a : achievements) { for (auto &a : achievements) {
if (a.name == ach_name) { if (a.name == ach_name) {
a.achieved = ach.value("earned", false); try {
a.unlock_time = ach.value("earned_time", static_cast<uint32>(0)); a.achieved = ach.value("earned", false);
a.progress = ach.value("progress", static_cast<float>(0)); a.unlock_time = ach.value("earned_time", static_cast<uint32>(0));
a.max_progress = ach.value("max_progress", static_cast<float>(0)); a.progress = ach.value("progress", static_cast<float>(0));
a.max_progress = ach.value("max_progress", static_cast<float>(0));
} catch(...) {}
if (a.achieved) { if (a.achieved) {
post_achievement_notification(a); post_achievement_notification(a, for_progress);
notify_sound_user_achievement(); if (!for_progress) notify_sound_user_achievement();
} }
break; break;
} }