diff --git a/dep/rcheevos/include/rc_client.h b/dep/rcheevos/include/rc_client.h index 2f03528c3..9fb18da64 100644 --- a/dep/rcheevos/include/rc_client.h +++ b/dep/rcheevos/include/rc_client.h @@ -141,6 +141,11 @@ RC_EXPORT void RC_CCONV rc_client_abort_async(rc_client_t* client, rc_client_asy */ RC_EXPORT size_t RC_CCONV rc_client_get_user_agent_clause(rc_client_t* client, char buffer[], size_t buffer_size); +/** + * Returns true if any achievement submissions have failed and are currently pending. + */ +RC_EXPORT int RC_CCONV rc_client_is_disconnected(rc_client_t* client); + /*****************************************************************************\ | Logging | \*****************************************************************************/ @@ -503,6 +508,11 @@ RC_EXPORT void RC_CCONV rc_client_destroy_achievement_list(rc_client_achievement */ RC_EXPORT int RC_CCONV rc_client_has_achievements(rc_client_t* client); +/** + * Returns the number of outstanding achievement unlocks. + */ +RC_EXPORT int RC_CCONV rc_client_get_award_achievement_pending_count(rc_client_t* client); + /*****************************************************************************\ | Leaderboards | \*****************************************************************************/ diff --git a/dep/rcheevos/src/rc_client.c b/dep/rcheevos/src/rc_client.c index a01aa696e..d0fa50377 100644 --- a/dep/rcheevos/src/rc_client.c +++ b/dep/rcheevos/src/rc_client.c @@ -3941,6 +3941,20 @@ static int rc_client_is_award_achievement_pending(const rc_client_t* client, uin return 0; } +int rc_client_get_award_achievement_pending_count(rc_client_t* client) +{ + /* assume lock already held */ + int count = 0; + rc_client_scheduled_callback_data_t* scheduled_callback = client->state.scheduled_callbacks; + for (; scheduled_callback; scheduled_callback = scheduled_callback->next) + { + if (scheduled_callback->callback == rc_client_award_achievement_retry) + count++; + } + + return count; +} + static void rc_client_award_achievement_server_call(rc_client_award_achievement_callback_data_t* ach_data); static void rc_client_award_achievement_retry(rc_client_scheduled_callback_data_t* callback_data, rc_client_t* client, rc_clock_t now) @@ -6411,3 +6425,8 @@ size_t rc_client_get_user_agent_clause(rc_client_t* client, char buffer[], size_ buffer[buffer_size - 1] = '\0'; return result; } + +int rc_client_is_disconnected(rc_client_t* client) +{ + return (client && (client->state.disconnect & (RC_CLIENT_DISCONNECT_VISIBLE | RC_CLIENT_DISCONNECT_SHOW_PENDING) != 0)); +} diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index b31a5f871..8e246a07a 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -2576,6 +2576,8 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y) const float progress_rounding = LayoutScale(5.0f); const float badge_size = LayoutScale(40.0f); const float badge_text_width = box_content_width - badge_size - text_spacing - text_spacing; + const bool disconnected = rc_client_is_disconnected(s_state.client); + const int pending_count = disconnected ? rc_client_get_award_achievement_pending_count(s_state.client) : 0; ImDrawList* dl = ImGui::GetBackgroundDrawList(); @@ -2589,6 +2591,10 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y) float box_height = box_padding + box_padding + UIStyle.MediumFont->FontSize + paragraph_spacing + progress_height + paragraph_spacing; + if (pending_count > 0) + { + box_height += UIStyle.MediumFont->FontSize + paragraph_spacing; + } if (s_state.most_recent_unlock) { box_height += UIStyle.MediumFont->FontSize + paragraph_spacing + @@ -2664,6 +2670,16 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y) progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f)), text_color, buffer.c_str(), buffer.end_ptr()); text_pos.y += progress_height + paragraph_spacing; + + if (pending_count > 0) + { + buffer.format(ICON_EMOJI_WARNING " {}", + TRANSLATE_PLURAL_SSTR("Achievements", "%n unlocks have not been confirmed by the server.", + "Pause Menu", pending_count)); + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, title_text_color, buffer.c_str(), + buffer.end_ptr()); + text_pos.y += UIStyle.MediumFont->FontSize + paragraph_spacing; + } } if (s_state.most_recent_unlock) diff --git a/src/duckstation-qt/translations/duckstation-qt_en.ts b/src/duckstation-qt/translations/duckstation-qt_en.ts index c49a0a4a6..a9003b91f 100644 --- a/src/duckstation-qt/translations/duckstation-qt_en.ts +++ b/src/duckstation-qt/translations/duckstation-qt_en.ts @@ -16,7 +16,7 @@ Achievements - + You have unlocked {} of %n achievements Achievement popup @@ -25,7 +25,7 @@ - + and earned {} of %n points Achievement popup @@ -34,7 +34,7 @@ - + %n achievements Mastery popup @@ -43,8 +43,8 @@ - - + + %n points Achievement points @@ -53,7 +53,16 @@ - + + %n unlocks have not been confirmed by the server. + Pause Menu + + %n unlock has not been confirmed by the server. + %n unlocks have not been confirmed by the server. + + + + You have unlocked all achievements and earned %n points! Point count @@ -62,7 +71,7 @@ - + This game has %n leaderboards. Leaderboard count @@ -74,7 +83,7 @@ Cheats - + %n game patches are active. OSD Message @@ -83,7 +92,7 @@ - + %n cheats are enabled. This may crash games. OSD Message @@ -92,7 +101,7 @@ - + %n cheats Cheats blocked by hardcore mode @@ -101,7 +110,7 @@ - + %n patches Patches blocked by hardcore mode @@ -124,7 +133,7 @@ GPU_HW - + %n replacement textures found. Replacement texture count @@ -136,8 +145,8 @@ GameList - - + + %n hours %n hour @@ -145,8 +154,8 @@ - - + + %n minutes %n minute @@ -168,7 +177,7 @@ MemoryCardEditorWindow - + %n block(s) free%1 %n block free%1