diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index e31f63074..df0f66a1e 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -121,11 +121,10 @@ static void ClearGameInfo(); static void ClearGameHash(); static std::string GetUserAgent(); static std::string GetGameHash(CDImage* image); -static void SetChallengeMode(bool enabled); +static void SetHardcoreMode(bool enabled); static bool IsLoggedIn(); static void ShowLoginSuccess(const rc_client_t* client); static void ShowLoginNotification(); -static void CancelGameLoad(); static void IdentifyGame(const std::string& path, CDImage* image); static void BeginLoadGame(); static void UpdateGameSummary(); @@ -349,6 +348,11 @@ bool Achievements::HasAchievementsOrLeaderboards() return s_has_achievements || s_has_leaderboards; } +bool Achievements::HasAchievements() +{ + return s_has_achievements; +} + bool Achievements::HasLeaderboards() { return s_has_leaderboards; @@ -389,7 +393,7 @@ bool Achievements::Initialize() rc_client_set_event_handler(s_client, ClientEventHandler); rc_client_set_hardcore_enabled(s_client, s_hardcore_mode); - rc_client_set_encore_mode_enabled(s_client, g_settings.achievements_spectator_mode); + rc_client_set_encore_mode_enabled(s_client, g_settings.achievements_encore_mode); rc_client_set_unofficial_enabled(s_client, g_settings.achievements_unofficial_test_mode); rc_client_set_spectator_mode_enabled(s_client, g_settings.achievements_spectator_mode); @@ -476,22 +480,35 @@ void Achievements::UpdateSettings(const Settings& old_config) } else if (!s_hardcore_mode && g_settings.achievements_hardcore_mode) { - ImGuiFullscreen::ShowToast(std::string(), - TRANSLATE_STR("Achievements", "Hardcore mode will be enabled on system reset."), - Host::OSD_WARNING_DURATION); + if (HasActiveGame() && FullscreenUI::Initialize()) + { + ImGuiFullscreen::ShowToast(std::string(), + TRANSLATE_STR("Achievements", "Hardcore mode will be enabled on system reset."), + Host::OSD_WARNING_DURATION); + } } } // These cannot be modified while a game is loaded, so just toss state and reload. - if (HasActiveGame() && - (g_settings.achievements_encore_mode != old_config.achievements_encore_mode || - g_settings.achievements_spectator_mode != old_config.achievements_spectator_mode || - g_settings.achievements_unofficial_test_mode != old_config.achievements_unofficial_test_mode || - g_settings.achievements_use_first_disc_from_playlist != old_config.achievements_use_first_disc_from_playlist)) + if (HasActiveGame()) { - Shutdown(false); - Initialize(); - return; + if (g_settings.achievements_encore_mode != old_config.achievements_encore_mode || + g_settings.achievements_spectator_mode != old_config.achievements_spectator_mode || + g_settings.achievements_unofficial_test_mode != old_config.achievements_unofficial_test_mode) + { + Shutdown(false); + Initialize(); + return; + } + } + else + { + if (g_settings.achievements_encore_mode != old_config.achievements_encore_mode) + rc_client_set_encore_mode_enabled(s_client, g_settings.achievements_encore_mode); + if (g_settings.achievements_spectator_mode != old_config.achievements_spectator_mode) + rc_client_set_spectator_mode_enabled(s_client, g_settings.achievements_spectator_mode); + if (g_settings.achievements_unofficial_test_mode != old_config.achievements_unofficial_test_mode) + rc_client_set_unofficial_enabled(s_client, g_settings.achievements_unofficial_test_mode); } // in case cache directory changed @@ -520,6 +537,7 @@ bool Achievements::Shutdown(bool allow_cancel) ClearGameInfo(); ClearGameHash(); + DisableHardcoreMode(); if (s_load_game_request) { @@ -765,30 +783,6 @@ void Achievements::GameChanged(const std::string& path, CDImage* image) IdentifyGame(path, image); } -void Achievements::CancelGameLoad() -{ - Log_ErrorPrint("Cancelling game load"); - - if (s_load_game_request) - { - rc_client_abort_async(s_client, s_load_game_request); - s_load_game_request = nullptr; - } - rc_client_unload_game(s_client); - ClearGameHash(); - ClearGameInfo(); - DisableHardcoreMode(); - Host::OnAchievementsRefreshed(); - -#ifdef ENABLE_RAINTEGRATION - if (IsUsingRAIntegration()) - { - RAIntegration::GameChanged(); - return; - } -#endif -} - void Achievements::IdentifyGame(const std::string& path, CDImage* image) { if (s_game_path == path) @@ -804,24 +798,19 @@ void Achievements::IdentifyGame(const std::string& path, CDImage* image) temp_image = CDImage::Open(path.c_str(), g_settings.cdrom_load_image_patches, nullptr); image = temp_image.get(); if (!temp_image) - { Log_ErrorPrintf("Failed to open temporary CD image '%s'", path.c_str()); - CancelGameLoad(); - return; - } } std::string game_hash; if (image) - { game_hash = GetGameHash(image); - if (s_game_hash == game_hash) - { - // only the path has changed - different format/save state/etc. - Log_InfoPrintf("Detected path change from '%s' to '%s'", s_game_path.c_str(), path.c_str()); - s_game_path = path; - return; - } + + if (s_game_hash == game_hash) + { + // only the path has changed - different format/save state/etc. + Log_InfoPrintf("Detected path change from '%s' to '%s'", s_game_path.c_str(), path.c_str()); + s_game_path = path; + return; } ClearGameHash(); @@ -843,6 +832,7 @@ void Achievements::IdentifyGame(const std::string& path, CDImage* image) if (!IsLoggedIn()) { Log_InfoPrintf("Skipping load game because we're not logged in."); + DisableHardcoreMode(); return; } @@ -859,7 +849,6 @@ void Achievements::BeginLoadGame() } ClearGameInfo(); - Host::OnAchievementsRefreshed(); if (s_game_hash.empty()) { @@ -870,7 +859,7 @@ void Achievements::BeginLoadGame() "Failed to read executable from disc. Achievements disabled.", Host::OSD_ERROR_DURATION); } - rc_client_unload_game(s_client); + DisableHardcoreMode(); return; } @@ -881,10 +870,17 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message, { s_load_game_request = nullptr; - if (result != RC_OK) + if (result == RC_NO_GAME_LOADED) + { + // Unknown game. + Log_InfoPrintf("Unknown game '%s', disabling achievements.", s_game_hash.c_str()); + DisableHardcoreMode(); + return; + } + else if (result != RC_OK) { ReportFmtError("Loading game failed: {}", error_message); - SetChallengeMode(false); + DisableHardcoreMode(); return; } @@ -892,7 +888,7 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message, if (!info) { ReportError("rc_client_get_game_info() returned NULL"); - SetChallengeMode(false); + DisableHardcoreMode(); return; } @@ -935,6 +931,14 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message, void Achievements::ClearGameInfo() { ClearUIState(); + + if (s_load_game_request) + { + rc_client_abort_async(s_client, s_load_game_request); + s_load_game_request = nullptr; + } + rc_client_unload_game(s_client); + s_active_leaderboard_trackers = {}; s_active_challenge_indicators = {}; s_active_progress_indicator.reset(); @@ -946,6 +950,8 @@ void Achievements::ClearGameInfo() s_has_rich_presence = false; s_rich_presence_string = {}; s_game_summary = {}; + + Host::OnAchievementsRefreshed(); } void Achievements::ClearGameHash() @@ -1014,8 +1020,8 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event) std::string badge_path = GetAchievementBadgePath(cheevo, cheevo->state); ImGuiFullscreen::AddNotification(fmt::format("achievement_unlock_{}", cheevo->id), - g_settings.achievements_notification_duration, std::move(title), - cheevo->description, std::move(badge_path)); + static_cast(g_settings.achievements_notification_duration), + std::move(title), cheevo->description, std::move(badge_path)); } if (g_settings.achievements_sound_effects) @@ -1075,21 +1081,22 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even if (g_settings.achievements_leaderboard_notifications && FullscreenUI::Initialize()) { static const char* value_strings[NUM_RC_CLIENT_LEADERBOARD_FORMATS] = { - TRANSLATE_NOOP("Achievements", "Your Time: {} (Submitting)"), - TRANSLATE_NOOP("Achievements", "Your Score: {} (Submitting)"), - TRANSLATE_NOOP("Achievements", "Your Value: {} (Submitting)"), + TRANSLATE_NOOP("Achievements", "Your Time: {}{}"), + TRANSLATE_NOOP("Achievements", "Your Score: {}{}"), + TRANSLATE_NOOP("Achievements", "Your Value: {}{}"), }; std::string title = event->leaderboard->title; - std::string message = - fmt::format(fmt::runtime(Host::TranslateToStringView( - "Achievements", - value_strings[std::min(event->leaderboard->format, NUM_RC_CLIENT_LEADERBOARD_FORMATS - 1)])), - event->leaderboard->tracker_value ? event->leaderboard->tracker_value : "Unknown"); + std::string message = fmt::format( + fmt::runtime(Host::TranslateToStringView( + "Achievements", + value_strings[std::min(event->leaderboard->format, NUM_RC_CLIENT_LEADERBOARD_FORMATS - 1)])), + event->leaderboard->tracker_value ? event->leaderboard->tracker_value : "Unknown", + g_settings.achievements_spectator_mode ? std::string_view() : TRANSLATE_SV("Achievements", " (Submitting)")); ImGuiFullscreen::AddNotification(fmt::format("leaderboard_{}", event->leaderboard->id), - g_settings.achievements_leaderboard_duration, std::move(title), std::move(message), - s_game_icon); + static_cast(g_settings.achievements_leaderboard_duration), std::move(title), + std::move(message), s_game_icon); } if (g_settings.achievements_sound_effects) @@ -1119,8 +1126,8 @@ void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* eve event->leaderboard_scoreboard->new_rank, event->leaderboard_scoreboard->num_entries); ImGuiFullscreen::AddNotification(fmt::format("leaderboard_{}", event->leaderboard->id), - g_settings.achievements_leaderboard_duration, std::move(title), std::move(message), - s_game_icon); + static_cast(g_settings.achievements_leaderboard_duration), std::move(title), + std::move(message), s_game_icon); } } @@ -1319,7 +1326,7 @@ void Achievements::DisableHardcoreMode() #endif if (s_hardcore_mode) - SetChallengeMode(false); + SetHardcoreMode(false); } bool Achievements::ResetHardcoreMode() @@ -1335,11 +1342,11 @@ bool Achievements::ResetHardcoreMode() if (s_hardcore_mode == wanted_hardcore_mode) return false; - SetChallengeMode(wanted_hardcore_mode); + SetHardcoreMode(wanted_hardcore_mode); return true; } -void Achievements::SetChallengeMode(bool enabled) +void Achievements::SetHardcoreMode(bool enabled) { if (enabled == s_hardcore_mode) return; @@ -1347,7 +1354,7 @@ void Achievements::SetChallengeMode(bool enabled) // new mode s_hardcore_mode = enabled; - if (HasActiveGame()) + if (HasActiveGame() && FullscreenUI::Initialize()) { ImGuiFullscreen::ShowToast(std::string(), enabled ? TRANSLATE_STR("Achievements", "Hardcore mode is now enabled.") : @@ -1363,7 +1370,7 @@ void Achievements::SetChallengeMode(bool enabled) // Toss away UI state, because it's invalid now ClearUIState(); - Host::OnAchievementsHardcoreModeChanged(); + Host::OnAchievementsHardcoreModeChanged(enabled); } bool Achievements::DoState(StateWrapper& sw) @@ -1693,11 +1700,7 @@ void Achievements::Logout() const auto lock = GetLock(); if (HasActiveGame()) - { ClearGameInfo(); - ClearGameHash(); - Host::OnAchievementsRefreshed(); - } Log_InfoPrint("Logging out..."); rc_client_logout(s_client); @@ -1743,7 +1746,7 @@ bool Achievements::ConfirmHardcoreModeDisable(const char* trigger) void Achievements::ClearUIState() { if (FullscreenUI::IsAchievementsWindowOpen() || FullscreenUI::IsLeaderboardsWindowOpen()) - FullscreenUI::ReturnToMainWindow(); + FullscreenUI::ReturnToPreviousWindow(); s_achievement_badge_paths = {}; @@ -2008,9 +2011,6 @@ void Achievements::DrawAchievementsWindow() auto lock = Achievements::GetLock(); - // ensure image downloads still happen while we're paused - Achievements::IdleUpdate(); - static constexpr float alpha = 0.8f; static constexpr float heading_alpha = 0.95f; static constexpr float heading_height_unscaled = 110.0f; @@ -2058,7 +2058,7 @@ void Achievements::DrawAchievementsWindow() g_large_font) || ImGuiFullscreen::WantsToCloseMenu()) { - FullscreenUI::ReturnToMainWindow(); + FullscreenUI::ReturnToPreviousWindow(); } const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + g_large_font->FontSize)); @@ -2124,6 +2124,12 @@ void Achievements::DrawAchievementsWindow() background, 0.0f, 0.0f, 0)) { static bool buckets_collapsed[NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS] = {}; + static const char* bucket_names[NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS] = { + TRANSLATE_NOOP("Achievements", "Unknown"), TRANSLATE_NOOP("Achievements", "Locked"), + TRANSLATE_NOOP("Achievements", "Unlocked"), TRANSLATE_NOOP("Achievements", "Unsupported"), + TRANSLATE_NOOP("Achievements", "Unofficial"), TRANSLATE_NOOP("Achievements", "Recently Unlocked"), + TRANSLATE_NOOP("Achievements", "Active Challenges"), TRANSLATE_NOOP("Achievements", "Almost There"), + }; ImGuiFullscreen::BeginMenuButtons(); @@ -2140,10 +2146,11 @@ void Achievements::DrawAchievementsWindow() DebugAssert(bucket.bucket_type < NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS); - // TODO: This should be translated. + // TODO: Once subsets are supported, this will need to change. bool& bucket_collapsed = buckets_collapsed[bucket.bucket_type]; - bucket_collapsed ^= ImGuiFullscreen::MenuHeadingButton(bucket.label, bucket_collapsed ? ICON_FA_CHEVRON_DOWN : - ICON_FA_CHEVRON_UP); + bucket_collapsed ^= + ImGuiFullscreen::MenuHeadingButton(Host::TranslateToCString("Achievements", bucket_names[bucket.bucket_type]), + bucket_collapsed ? ICON_FA_CHEVRON_DOWN : ICON_FA_CHEVRON_UP); if (!bucket_collapsed) { for (u32 i = 0; i < bucket.num_achievements; i++) @@ -2310,9 +2317,6 @@ void Achievements::DrawLeaderboardsWindow() auto lock = Achievements::GetLock(); - // ensure image downloads still happen while we're paused - Achievements::IdleUpdate(); - const bool is_leaderboard_open = (s_open_leaderboard != nullptr); bool close_leaderboard_on_exit = false; @@ -2381,7 +2385,7 @@ void Achievements::DrawLeaderboardsWindow() g_large_font) || ImGuiFullscreen::WantsToCloseMenu()) { - FullscreenUI::ReturnToMainWindow(); + FullscreenUI::ReturnToPreviousWindow(); } } else diff --git a/src/core/achievements.h b/src/core/achievements.h index 7f79873df..75b8be487 100644 --- a/src/core/achievements.h +++ b/src/core/achievements.h @@ -91,6 +91,9 @@ u32 GetGameID(); /// Returns true if the current game has any achievements or leaderboards. bool HasAchievementsOrLeaderboards(); +/// Returns true if the current game has any leaderboards. +bool HasAchievements(); + /// Returns true if the current game has any leaderboards. bool HasLeaderboards(); @@ -153,5 +156,5 @@ void OnAchievementsLoginSuccess(const char* display_name, u32 points, u32 sc_poi void OnAchievementsRefreshed(); /// Called whenever hardcore mode is toggled. -void OnAchievementsHardcoreModeChanged(); +void OnAchievementsHardcoreModeChanged(bool enabled); } // namespace Host diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 21eb301cd..a0d9253ed 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -217,11 +217,11 @@ static void CancelAsyncOps(); // Main ////////////////////////////////////////////////////////////////////////// static void ToggleTheme(); -static void PauseForMenuOpen(); +static void PauseForMenuOpen(bool set_pause_menu_open); static void ClosePauseMenu(); static void OpenPauseSubMenu(PauseSubMenu submenu); static void DrawLandingWindow(); -static void DrawPauseMenu(MainWindowType type); +static void DrawPauseMenu(); static void ExitFullscreenAndOpenURL(const std::string_view& url); static void CopyTextToClipboard(std::string title, const std::string_view& text); static void DrawAboutWindow(); @@ -616,7 +616,7 @@ void FullscreenUI::CheckForConfigChanges(const Settings& old_settings) if (old_settings.achievements_enabled && !g_settings.achievements_enabled) { if (s_current_main_window == MainWindowType::Achievements || s_current_main_window == MainWindowType::Leaderboards) - ReturnToMainWindow(); + ReturnToPreviousWindow(); } } @@ -647,6 +647,8 @@ void FullscreenUI::OnSystemDestroyed() return; s_pause_menu_was_open = false; + s_was_paused_on_quick_menu_open = false; + s_current_pause_submenu = PauseSubMenu::None; SwitchToLanding(); } @@ -671,13 +673,13 @@ void FullscreenUI::ToggleTheme() ImGuiFullscreen::SetTheme(new_light); } -void FullscreenUI::PauseForMenuOpen() +void FullscreenUI::PauseForMenuOpen(bool set_pause_menu_open) { s_was_paused_on_quick_menu_open = (System::GetState() == System::State::Paused); if (g_settings.pause_on_menu && !s_was_paused_on_quick_menu_open) Host::RunOnCPUThread([]() { System::PauseSystem(true); }); - s_pause_menu_was_open = true; + s_pause_menu_was_open |= set_pause_menu_open; } void FullscreenUI::OpenPauseMenu() @@ -688,7 +690,7 @@ void FullscreenUI::OpenPauseMenu() if (!Initialize() || s_current_main_window != MainWindowType::None) return; - PauseForMenuOpen(); + PauseForMenuOpen(true); s_current_main_window = MainWindowType::PauseMenu; s_current_pause_submenu = PauseSubMenu::None; QueueResetFocus(); @@ -762,7 +764,7 @@ void FullscreenUI::Render() DrawSettingsWindow(); break; case MainWindowType::PauseMenu: - DrawPauseMenu(s_current_main_window); + DrawPauseMenu(); break; case MainWindowType::Achievements: Achievements::DrawAchievementsWindow(); @@ -818,11 +820,22 @@ void FullscreenUI::InvalidateCoverCache() Host::RunOnCPUThread([]() { s_cover_image_map.clear(); }); } +void FullscreenUI::ReturnToPreviousWindow() +{ + if (System::IsValid() && s_pause_menu_was_open) + { + s_current_main_window = MainWindowType::PauseMenu; + QueueResetFocus(); + } + else + { + ReturnToMainWindow(); + } +} + void FullscreenUI::ReturnToMainWindow() { - if (s_pause_menu_was_open) - ClosePauseMenu(); - + ClosePauseMenu(); s_current_main_window = System::IsValid() ? MainWindowType::None : MainWindowType::Landing; } @@ -956,7 +969,7 @@ void FullscreenUI::DoChangeDiscFromFile() QueueResetFocus(); CloseFileSelector(); - ReturnToMainWindow(); + ReturnToPreviousWindow(); }; OpenFileSelector(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), false, std::move(callback), @@ -991,7 +1004,7 @@ void FullscreenUI::DoChangeDisc() QueueResetFocus(); CloseChoiceDialog(); - ReturnToMainWindow(); + ReturnToPreviousWindow(); }; OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), true, std::move(options), @@ -1055,7 +1068,7 @@ void FullscreenUI::DoCheatsMenu() { Host::AddKeyedOSDMessage("load_cheat_list", fmt::format(FSUI_FSTR("No cheats found for {}."), System::GetGameTitle()), 10.0f); - ReturnToMainWindow(); + ReturnToPreviousWindow(); return; } } @@ -1071,7 +1084,7 @@ void FullscreenUI::DoCheatsMenu() auto callback = [](s32 index, const std::string& title, bool checked) { if (index < 0) { - ReturnToMainWindow(); + ReturnToPreviousWindow(); return; } @@ -2537,7 +2550,7 @@ void FullscreenUI::DrawSettingsWindow() } if (NavButton(ICON_FA_BACKWARD, true, true)) - ReturnToMainWindow(); + ReturnToPreviousWindow(); if (s_game_settings_entry) NavTitle(s_game_settings_entry->title.c_str()); @@ -2568,7 +2581,7 @@ void FullscreenUI::DrawSettingsWindow() if (WantsToCloseMenu()) { if (ImGui::IsWindowFocused()) - ReturnToMainWindow(); + ReturnToPreviousWindow(); } auto lock = Host::GetSettingsLock(); @@ -4429,10 +4442,6 @@ void FullscreenUI::DrawAchievementsSettingsPage() } #endif - const auto lock = Achievements::GetLock(); - if (Achievements::IsActive() && !System::IsRunning()) - Achievements::IdleUpdate(); - SettingsInterface* bsi = GetEditingSettingsInterface(); BeginMenuButtons(); @@ -4443,7 +4452,6 @@ void FullscreenUI::DrawAchievementsSettingsPage() "Cheevos", "Enabled", false); const bool enabled = bsi->GetBoolValue("Cheevos", "Enabled", false); - const bool hardcore = bsi->GetBoolValue("Cheevos", "ChallengeMode", false); if (DrawToggleSetting( bsi, FSUI_ICONSTR(ICON_FA_HARD_HAT, "Hardcore Mode"), @@ -4460,7 +4468,7 @@ void FullscreenUI::DrawAchievementsSettingsPage() "Notifications", true, enabled); DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_LIST_OL, "Leaderboard Notifications"), FSUI_CSTR("Displays popup messages when starting, submitting, or failing a leaderboard challenge."), - "Cheevos", "LeaderboardNotifications", true, enabled && hardcore); + "Cheevos", "LeaderboardNotifications", true, enabled); DrawToggleSetting( bsi, FSUI_ICONSTR(ICON_FA_HEADPHONES, "Sound Effects"), FSUI_CSTR("Plays sound effects for events such as achievement unlocks and leaderboard submissions."), "Cheevos", @@ -4518,6 +4526,8 @@ void FullscreenUI::DrawAchievementsSettingsPage() MenuHeading(FSUI_CSTR("Current Game")); if (Achievements::HasActiveGame()) { + const auto lock = Achievements::GetLock(); + ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyle().Colors[ImGuiCol_Text]); ActiveButton(SmallString::FromFmt(fmt::runtime(FSUI_ICONSTR(ICON_FA_BOOKMARK, "Game: {} ({})")), Achievements::GetGameID(), Achievements::GetGameTitle()), @@ -4656,7 +4666,7 @@ void FullscreenUI::DrawAdvancedSettingsPage() EndMenuButtons(); } -void FullscreenUI::DrawPauseMenu(MainWindowType type) +void FullscreenUI::DrawPauseMenu() { SmallString buffer; @@ -4779,6 +4789,9 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) // NOTE: Menu close must come first, because otherwise VM destruction options will race. const bool has_game = System::IsValid() && !System::GetGameSerial().empty(); + if (just_focused) + ImGui::SetFocusID(ImGui::GetID(FSUI_ICONSTR(ICON_FA_PLAY, "Resume Game")), ImGui::GetCurrentWindow()); + if (ActiveButton(FSUI_ICONSTR(ICON_FA_PLAY, "Resume Game"), false) || WantsToCloseMenu()) ClosePauseMenu(); @@ -4819,7 +4832,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) } if (ActiveButton(FSUI_ICONSTR(ICON_FA_TROPHY, "Achievements"), false, - Achievements::HasActiveGame() && Achievements::HasAchievementsOrLeaderboards())) + Achievements::HasAchievementsOrLeaderboards())) { // skip second menu and go straight to cheevos if there's no lbs if (!Achievements::HasLeaderboards()) @@ -4857,14 +4870,14 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) case PauseSubMenu::Exit: { if (just_focused) + { ImGui::SetFocusID(ImGui::GetID(FSUI_ICONSTR(ICON_FA_POWER_OFF, "Exit Without Saving")), ImGui::GetCurrentWindow()); - - if (ActiveButton(FSUI_ICONSTR(ICON_FA_BACKWARD, "Back To Pause Menu"), false)) - { - OpenPauseSubMenu(PauseSubMenu::None); } + if (ActiveButton(FSUI_ICONSTR(ICON_FA_BACKWARD, "Back To Pause Menu"), false) || WantsToCloseMenu()) + OpenPauseSubMenu(PauseSubMenu::None); + if (ActiveButton(FSUI_ICONSTR(ICON_FA_SYNC, "Reset System"), false)) { ClosePauseMenu(); @@ -4881,7 +4894,13 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) case PauseSubMenu::Achievements: { - if (ActiveButton(FSUI_ICONSTR(ICON_FA_BACKWARD, "Back To Pause Menu"), false)) + if (just_focused) + { + ImGui::SetFocusID(ImGui::GetID(FSUI_ICONSTR(ICON_FA_BACKWARD, "Back To Pause Menu")), + ImGui::GetCurrentWindow()); + } + + if (ActiveButton(FSUI_ICONSTR(ICON_FA_BACKWARD, "Back To Pause Menu"), false) || WantsToCloseMenu()) OpenPauseSubMenu(PauseSubMenu::None); if (ActiveButton(FSUI_ICONSTR(ICON_FA_TROPHY, "Achievements"), false)) @@ -5057,8 +5076,6 @@ void FullscreenUI::CloseSaveStateSelector() s_save_state_selector_loading = false; s_save_state_selector_resuming = false; s_save_state_selector_game_path = {}; - if (s_current_main_window != MainWindowType::GameList) - ReturnToMainWindow(); } void FullscreenUI::DrawSaveStateSelector(bool is_loading) @@ -5089,7 +5106,10 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) ImGui::PopStyleVar(5); if (!is_open) + { CloseSaveStateSelector(); + ReturnToPreviousWindow(); + } return; } @@ -5102,7 +5122,10 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) { BeginNavBar(); if (NavButton(ICON_FA_BACKWARD, true, true)) + { CloseSaveStateSelector(); + ReturnToPreviousWindow(); + } NavTitle(is_loading ? FSUI_CSTR("Load State") : FSUI_CSTR("Save State")); EndNavBar(); @@ -5206,12 +5229,14 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) { DoLoadState(entry.path); CloseSaveStateSelector(); + ReturnToPreviousWindow(); break; } else { DoSaveState(entry.slot, entry.global); CloseSaveStateSelector(); + ReturnToPreviousWindow(); break; } } @@ -5268,6 +5293,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) DoSaveState(entry.slot, entry.global); CloseSaveStateSelector(); + ReturnToPreviousWindow(); closed = true; } @@ -5287,6 +5313,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) if (s_save_state_selector_slots.empty()) { CloseSaveStateSelector(); + ReturnToPreviousWindow(); closed = true; } else @@ -5352,7 +5379,10 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) ImGui::PopStyleVar(5); if (!close_handled && WantsToCloseMenu()) + { CloseSaveStateSelector(); + ReturnToPreviousWindow(); + } } bool FullscreenUI::OpenLoadStateSelectorForGameResume(const GameList::Entry* entry) @@ -5599,7 +5629,7 @@ void FullscreenUI::DrawGameListWindow() } if (NavButton(ICON_FA_BACKWARD, true, true)) - ReturnToMainWindow(); + ReturnToPreviousWindow(); NavTitle(Host::TranslateToCString(TR_CONTEXT, titles[static_cast(s_game_list_page)])); RightAlignNavButtons(count, ITEM_WIDTH, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY); @@ -5851,7 +5881,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) if (WantsToCloseMenu()) { if (ImGui::IsWindowFocused()) - ReturnToMainWindow(); + ReturnToPreviousWindow(); } ResetFocusHere(); @@ -6018,7 +6048,7 @@ void FullscreenUI::DrawGameListSettingsPage(const ImVec2& heading_size) if (WantsToCloseMenu()) { if (ImGui::IsWindowFocused()) - ReturnToMainWindow(); + ReturnToPreviousWindow(); } auto lock = Host::GetSettingsLock(); @@ -6459,20 +6489,29 @@ bool FullscreenUI::DrawConfirmWindow(const char* message, bool* result) return !is_open; } -bool FullscreenUI::OpenAchievementsWindow() +void FullscreenUI::OpenAchievementsWindow() { - if (!System::IsValid() || !Achievements::HasActiveGame() || !Initialize() || - !Achievements::PrepareAchievementsWindow()) + if (!Achievements::IsActive()) { - return false; + Host::AddKeyedOSDMessage("achievements_disabled", FSUI_STR("Achievements are not enabled."), + Host::OSD_INFO_DURATION); + return; + } + + if (!System::IsValid() || !Initialize()) + return; + + if (!Achievements::HasAchievements() || !Achievements::PrepareAchievementsWindow()) + { + ShowToast(std::string(), FSUI_STR("This game has no achievements.")); + return; } if (s_current_main_window != MainWindowType::PauseMenu) - PauseForMenuOpen(); + PauseForMenuOpen(false); s_current_main_window = MainWindowType::Achievements; QueueResetFocus(); - return true; } bool FullscreenUI::IsAchievementsWindowOpen() @@ -6480,17 +6519,29 @@ bool FullscreenUI::IsAchievementsWindowOpen() return (s_current_main_window == MainWindowType::Achievements); } -bool FullscreenUI::OpenLeaderboardsWindow() +void FullscreenUI::OpenLeaderboardsWindow() { - if (!Achievements::HasLeaderboards() || !Initialize() || !Achievements::PrepareLeaderboardsWindow()) - return false; + if (!Achievements::IsActive()) + { + Host::AddKeyedOSDMessage("achievements_disabled", FSUI_STR("Leaderboards are not enabled."), + Host::OSD_INFO_DURATION); + return; + } + + if (!System::IsValid() || !Initialize()) + return; + + if (!Achievements::HasLeaderboards() || !Achievements::PrepareLeaderboardsWindow()) + { + ShowToast(std::string(), FSUI_STR("This game has no leaderboards.")); + return; + } if (s_current_main_window != MainWindowType::PauseMenu) - PauseForMenuOpen(); + PauseForMenuOpen(false); s_current_main_window = MainWindowType::Leaderboards; QueueResetFocus(); - return true; } bool FullscreenUI::IsLeaderboardsWindowOpen() diff --git a/src/core/fullscreen_ui.h b/src/core/fullscreen_ui.h index 83cd3d8cc..64897d920 100644 --- a/src/core/fullscreen_ui.h +++ b/src/core/fullscreen_ui.h @@ -22,11 +22,12 @@ void OnSystemResumed(); void OnSystemDestroyed(); void OnRunningGameChanged(); void OpenPauseMenu(); -bool OpenAchievementsWindow(); +void OpenAchievementsWindow(); bool IsAchievementsWindowOpen(); -bool OpenLeaderboardsWindow(); +void OpenLeaderboardsWindow(); bool IsLeaderboardsWindowOpen(); void ReturnToMainWindow(); +void ReturnToPreviousWindow(); void Shutdown(); void Render(); diff --git a/src/core/hotkeys.cpp b/src/core/hotkeys.cpp index 1f74f4380..fbb3ae28b 100644 --- a/src/core/hotkeys.cpp +++ b/src/core/hotkeys.cpp @@ -164,25 +164,13 @@ DEFINE_HOTKEY("Screenshot", TRANSLATE_NOOP("Hotkeys", "General"), TRANSLATE_NOOP DEFINE_HOTKEY("OpenAchievements", TRANSLATE_NOOP("Hotkeys", "General"), TRANSLATE_NOOP("Hotkeys", "Open Achievement List"), [](s32 pressed) { if (!pressed) - { - if (!FullscreenUI::OpenAchievementsWindow()) - { - Host::AddOSDMessage( - TRANSLATE_STR("OSDMessage", "Achievements are disabled or unavailable for game."), 10.0f); - } - } + FullscreenUI::OpenAchievementsWindow(); }) DEFINE_HOTKEY("OpenLeaderboards", TRANSLATE_NOOP("Hotkeys", "General"), TRANSLATE_NOOP("Hotkeys", "Open Leaderboard List"), [](s32 pressed) { if (!pressed) - { - if (!FullscreenUI::OpenLeaderboardsWindow()) - { - Host::AddOSDMessage( - TRANSLATE_STR("OSDMessage", "Leaderboards are disabled or unavailable for game."), 10.0f); - } - } + FullscreenUI::OpenLeaderboardsWindow(); }) #endif // !defined(__ANDROID__) diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 179fa7e7c..e493a0acd 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -376,9 +376,9 @@ void Settings::Load(SettingsInterface& si) achievements_use_first_disc_from_playlist = si.GetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true); achievements_use_raintegration = si.GetBoolValue("Cheevos", "UseRAIntegration", false); achievements_notification_duration = - si.GetFloatValue("Cheevos", "NotificationsDuration", DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME); + si.GetIntValue("Cheevos", "NotificationsDuration", DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME); achievements_leaderboard_duration = - si.GetFloatValue("Cheevos", "LeaderboardsDuration", DEFAULT_LEADERBOARD_NOTIFICATION_TIME); + si.GetIntValue("Cheevos", "LeaderboardsDuration", DEFAULT_LEADERBOARD_NOTIFICATION_TIME); log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str()) .value_or(DEFAULT_LOG_LEVEL); @@ -578,8 +578,8 @@ void Settings::Save(SettingsInterface& si) const si.SetBoolValue("Cheevos", "UnofficialTestMode", achievements_unofficial_test_mode); si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", achievements_use_first_disc_from_playlist); si.SetBoolValue("Cheevos", "UseRAIntegration", achievements_use_raintegration); - si.SetFloatValue("Cheevos", "NotificationsDuration", achievements_notification_duration); - si.SetFloatValue("Cheevos", "LeaderboardsDuration", achievements_leaderboard_duration); + si.SetIntValue("Cheevos", "NotificationsDuration", achievements_notification_duration); + si.SetIntValue("Cheevos", "LeaderboardsDuration", achievements_leaderboard_duration); si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level)); si.SetStringValue("Logging", "LogFilter", log_filter.c_str()); diff --git a/src/core/settings.h b/src/core/settings.h index 9907ad646..f5a1df61e 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -190,8 +190,8 @@ struct Settings bool achievements_unofficial_test_mode = false; bool achievements_use_first_disc_from_playlist = true; bool achievements_use_raintegration = false; - float achievements_notification_duration = DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME; - float achievements_leaderboard_duration = DEFAULT_LEADERBOARD_NOTIFICATION_TIME; + s32 achievements_notification_duration = DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME; + s32 achievements_leaderboard_duration = DEFAULT_LEADERBOARD_NOTIFICATION_TIME; struct DebugSettings { @@ -475,8 +475,8 @@ struct Settings static constexpr MemoryCardType DEFAULT_MEMORY_CARD_2_TYPE = MemoryCardType::None; static constexpr MultitapMode DEFAULT_MULTITAP_MODE = MultitapMode::Disabled; - static constexpr float DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME = 10.0f; - static constexpr float DEFAULT_LEADERBOARD_NOTIFICATION_TIME = 10.0f; + static constexpr s32 DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME = 5; + static constexpr s32 DEFAULT_LEADERBOARD_NOTIFICATION_TIME = 10; static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO; diff --git a/src/core/system.cpp b/src/core/system.cpp index b3d9e1958..abe93b1a2 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -4752,31 +4752,15 @@ void System::UpdateDiscordPresence() rp.largeImageKey = "duckstation_logo"; rp.largeImageText = "DuckStation PS1/PSX Emulator"; rp.startTimestamp = std::time(nullptr); + rp.details = System::IsValid() ? System::GetGameTitle().c_str() : "No Game Running"; - SmallString details_string; - if (!System::IsShutdown()) - { - details_string.AppendFormattedString("%s (%s)", System::GetGameTitle().c_str(), System::GetGameSerial().c_str()); - } - else - { - details_string.AppendString("No Game Running"); - } - - SmallString state_string; - + std::string state_string; if (Achievements::HasRichPresence()) { const auto lock = Achievements::GetLock(); - const std::string_view richp = Achievements::GetRichPresenceString(); - if (richp.length() >= 128) - state_string.AppendFmtString("{}...", richp.substr(0, 124)); - else - state_string.Assign(richp); - - rp.state = state_string; + state_string = StringUtil::Ellipsise(Achievements::GetRichPresenceString(), 128); + rp.state = state_string.c_str(); } - rp.details = details_string; Discord_UpdatePresence(&rp); } diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp index 3d833d16e..43faa34b6 100644 --- a/src/duckstation-nogui/nogui_host.cpp +++ b/src/duckstation-nogui/nogui_host.cpp @@ -766,7 +766,7 @@ void Host::OnAchievementsRefreshed() // noop } -void Host::OnAchievementsHardcoreModeChanged() +void Host::OnAchievementsHardcoreModeChanged(bool enabled) { // noop } diff --git a/src/duckstation-qt/achievementsettingswidget.cpp b/src/duckstation-qt/achievementsettingswidget.cpp index eb7c8bcfe..b8cf09d7e 100644 --- a/src/duckstation-qt/achievementsettingswidget.cpp +++ b/src/duckstation-qt/achievementsettingswidget.cpp @@ -159,16 +159,16 @@ void AchievementSettingsWidget::onHardcoreModeStateChanged() void AchievementSettingsWidget::onAchievementsNotificationDurationSliderChanged() { - const float duration = m_dialog->getEffectiveFloatValue("Cheevos", "NotificationsDuration", - Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME); - m_ui.achievementNotificationsDurationLabel->setText(tr("%n seconds", nullptr, static_cast(duration))); + const int duration = + m_dialog->getEffectiveIntValue("Cheevos", "NotificationsDuration", Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME); + m_ui.achievementNotificationsDurationLabel->setText(tr("%n seconds", nullptr, duration)); } void AchievementSettingsWidget::onLeaderboardsNotificationDurationSliderChanged() { - const float duration = m_dialog->getEffectiveFloatValue("Cheevos", "LeaderboardsDuration", - Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME); - m_ui.leaderboardNotificationsDurationLabel->setText(tr("%n seconds", nullptr, static_cast(duration))); + const int duration = + m_dialog->getEffectiveIntValue("Cheevos", "LeaderboardsDuration", Settings::DEFAULT_LEADERBOARD_NOTIFICATION_TIME); + m_ui.leaderboardNotificationsDurationLabel->setText(tr("%n seconds", nullptr, duration)); } void AchievementSettingsWidget::updateLoginState() diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 4363a9e71..f5103ea0d 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -2657,10 +2657,9 @@ void MainWindow::onAchievementsLoginSucceeded(const QString& display_name, quint m_ui.statusBar->showMessage(message); } -void MainWindow::onAchievementsChallengeModeChanged() +void MainWindow::onAchievementsChallengeModeChanged(bool enabled) { - const bool active = Achievements::IsHardcoreModeActive(); - if (active) + if (enabled) { if (m_cheat_manager_dialog) { @@ -2677,7 +2676,7 @@ void MainWindow::onAchievementsChallengeModeChanged() } } - updateEmulationActions(false, System::IsValid(), active); + updateEmulationActions(false, System::IsValid(), enabled); } void MainWindow::onToolsMemoryCardEditorTriggered() diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index ecd7bdbe9..2493b27d4 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -134,7 +134,7 @@ private Q_SLOTS: void onAchievementsLoginRequested(Achievements::LoginRequestReason reason); void onAchievementsLoginSucceeded(const QString& display_name, quint32 points, quint32 sc_points, quint32 unread_messages); - void onAchievementsChallengeModeChanged(); + void onAchievementsChallengeModeChanged(bool enabled); void onApplicationStateChanged(Qt::ApplicationState state); void onStartFileActionTriggered(); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 8438c419b..752438914 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1231,7 +1231,7 @@ void Host::OnAchievementsRefreshed() const std::string& rich_presence_string = Achievements::GetRichPresenceString(); if (Achievements::HasRichPresence() && !rich_presence_string.empty()) - game_info.append(QString::fromStdString(rich_presence_string)); + game_info.append(QString::fromStdString(StringUtil::Ellipsise(rich_presence_string, 128))); else game_info.append(qApp->translate("EmuThread", "Rich presence inactive or unsupported.")); } @@ -1243,9 +1243,9 @@ void Host::OnAchievementsRefreshed() emit g_emu_thread->achievementsRefreshed(game_id, game_info); } -void Host::OnAchievementsHardcoreModeChanged() +void Host::OnAchievementsHardcoreModeChanged(bool enabled) { - emit g_emu_thread->achievementsChallengeModeChanged(); + emit g_emu_thread->achievementsChallengeModeChanged(enabled); } void EmuThread::doBackgroundControllerPoll() diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index 3841f8ded..6fe2ebd2e 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -144,7 +144,7 @@ Q_SIGNALS: void achievementsLoginSucceeded(const QString& display_name, quint32 points, quint32 sc_points, quint32 unread_messages); void achievementsRefreshed(quint32 id, const QString& game_info_string); - void achievementsChallengeModeChanged(); + void achievementsChallengeModeChanged(bool enabled); void cheatEnabled(quint32 index, bool enabled); public Q_SLOTS: diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index 0425b5b87..1e1231b82 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -340,7 +340,7 @@ void Host::OnAchievementsRefreshed() // noop } -void Host::OnAchievementsHardcoreModeChanged() +void Host::OnAchievementsHardcoreModeChanged(bool enabled) { // noop } diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp index f798b0171..3e856ca70 100644 --- a/src/util/imgui_fullscreen.cpp +++ b/src/util/imgui_fullscreen.cpp @@ -80,6 +80,7 @@ ImVec4 UISecondaryTextColor; static u32 s_menu_button_index = 0; static u32 s_close_button_state = 0; static bool s_focus_reset_queued = false; +static bool s_light_theme = false; static LRUCache> s_texture_cache(128, true); static std::shared_ptr s_placeholder_texture; @@ -2484,17 +2485,10 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing) ImFont* const title_font = ImGuiFullscreen::g_large_font; ImFont* const text_font = ImGuiFullscreen::g_medium_font; -#if 0 - static constexpr u32 toast_background_color = IM_COL32(241, 241, 241, 255); - static constexpr u32 toast_border_color = IM_COL32(0x88, 0x88, 0x88, 255); - static constexpr u32 toast_title_color = IM_COL32(1, 1, 1, 255); - static constexpr u32 toast_text_color = IM_COL32(0, 0, 0, 255); -#else - static constexpr u32 toast_background_color = IM_COL32(0x21, 0x21, 0x21, 255); - static constexpr u32 toast_border_color = IM_COL32(0x48, 0x48, 0x48, 255); - static constexpr u32 toast_title_color = IM_COL32(0xff, 0xff, 0xff, 255); - static constexpr u32 toast_text_color = IM_COL32(0xff, 0xff, 0xff, 255); -#endif + const u32 toast_background_color = s_light_theme ? IM_COL32(241, 241, 241, 255) : IM_COL32(0x21, 0x21, 0x21, 255); + const u32 toast_border_color = s_light_theme ? IM_COL32(0x88, 0x88, 0x88, 255) : IM_COL32(0x48, 0x48, 0x48, 255); + const u32 toast_title_color = s_light_theme ? IM_COL32(1, 1, 1, 255) : IM_COL32(0xff, 0xff, 0xff, 255); + const u32 toast_text_color = s_light_theme ? IM_COL32(0, 0, 0, 255) : IM_COL32(0xff, 0xff, 0xff, 255); for (u32 index = 0; index < static_cast(s_notifications.size());) { @@ -2668,6 +2662,8 @@ void ImGuiFullscreen::DrawToast() void ImGuiFullscreen::SetTheme(bool light) { + s_light_theme = light; + if (!light) { // dark