Repository: RetroAchievements/RAIntegration
Pull request #740 (2021-05-27 17:18:59)

hide challenge indicator when achievement is paused
As a Paused achievement cannot trigger, it is reasonable to hide the challenge indicator while an achievement was paused. 

Previously, it was intentionally left displayed as existing achievements use Pause to prevent Resets during a challenge (i.e. when a boss regains health or the player pauses the game or opens an inventory screen). In these cases, the challenge is still active, so it makes sense to keep the indicator on screen.

There's also a bunch of achievements that use Pause to disable the achievement (either temporarily, or permanently) if the player does something wrong. In these cases, the indicator would remain on screen, which is misleading as the achievement cannot possibly trigger.

Since Trigger is a new flag, we have to assume that whoever is adding the Trigger flag will make whatever changes are necessary to support it as cleanly as possible. In that regard, as it's impossible to trigger a paused achievement, I've agreed to hide the indicator when an achievement is paused. Achievements using a Pause to prevent a Reset can simply change the Pause to an AndNext and invert the logic. i.e. ("`PauseIf A == 1; ResetIf B == 2`" becomes "`AndNext A != 1; ResetIf B == 2`").


Patch:
diff --git a/src/Exports.cpp b/src/Exports.cpp
index 59e9f2a0..62dc6bbd 100644
--- a/src/Exports.cpp
+++ b/src/Exports.cpp
@@ -414,6 +414,14 @@ static void ProcessAchievements()
                 break;
             }
 
+            case ra::services::AchievementRuntime::ChangeType::AchievementPaused:
+            {
+                // hide the challenge indicator - it'll be reshown when unpaused
+                auto& pOverlayManager = ra::services::ServiceLocator::GetMutable<ra::ui::viewmodels::OverlayManager>();
+                pOverlayManager.RemoveChallengeIndicator(pChange.nId);
+                break;
+            }
+
             case ra::services::AchievementRuntime::ChangeType::AchievementActivated:
             {
                 // state change from Waiting, Paused, or Primed to Active.
diff --git a/src/api/impl/ConnectedServer.cpp b/src/api/impl/ConnectedServer.cpp
index aaa2bd57..156942fb 100644
--- a/src/api/impl/ConnectedServer.cpp
+++ b/src/api/impl/ConnectedServer.cpp
@@ -497,7 +497,7 @@ Login::Response ConnectedServer::Login(const Login::Request& request)
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 Logout::Response ConnectedServer::Logout(const Logout::Request&)
@@ -541,7 +541,7 @@ StartSession::Response ConnectedServer::StartSession(const StartSession::Request
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 Ping::Response ConnectedServer::Ping(const Ping::Request& request)
@@ -582,7 +582,7 @@ Ping::Response ConnectedServer::Ping(const Ping::Request& request)
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 FetchUserUnlocks::Response ConnectedServer::FetchUserUnlocks(const FetchUserUnlocks::Request& request)
@@ -622,7 +622,7 @@ FetchUserUnlocks::Response ConnectedServer::FetchUserUnlocks(const FetchUserUnlo
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 AwardAchievement::Response ConnectedServer::AwardAchievement(const AwardAchievement::Request& request)
@@ -664,7 +664,7 @@ AwardAchievement::Response ConnectedServer::AwardAchievement(const AwardAchievem
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 SubmitLeaderboardEntry::Response ConnectedServer::SubmitLeaderboardEntry(const SubmitLeaderboardEntry::Request& request)
@@ -718,7 +718,7 @@ SubmitLeaderboardEntry::Response ConnectedServer::SubmitLeaderboardEntry(const S
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 FetchUserFriends::Response ConnectedServer::FetchUserFriends(const FetchUserFriends::Request&)
@@ -758,7 +758,7 @@ FetchUserFriends::Response ConnectedServer::FetchUserFriends(const FetchUserFrie
         }
     }
 
-    return std::move(response);
+    return response;
 }
 
 ResolveHash::Response ConnectedServer::ResolveHash(const ResolveHash::Request& request)
@@ -795,7 +795,7 @@ ResolveHash::Response ConnectedServer::ResolveHash(const ResolveHash::Request& r
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 FetchGameData::Response ConnectedServer::FetchGameData(const FetchGameData::Request& request)
@@ -848,7 +848,7 @@ FetchGameData::Response ConnectedServer::FetchGameData(const FetchGameData::Requ
 
     rc_api_destroy_request(&api_request);
 
-    return std::move(response);
+    return response;
 }
 
 void ConnectedServer::ProcessGamePatchData(ra::api::FetchGameData::Response &response, const ra::services::Http::Response& httpResponse)
@@ -931,7 +931,7 @@ FetchCodeNotes::Response ConnectedServer::FetchCodeNotes(const FetchCodeNotes::R
         }
     }
 
-    return std::move(response);
+    return response;
 }
 
 void ConnectedServer::ProcessCodeNotes(ra::api::FetchCodeNotes::Response &response, const rapidjson::Value& CodeNotes)
@@ -968,7 +968,7 @@ UpdateCodeNote::Response ConnectedServer::UpdateCodeNote(const UpdateCodeNote::R
         response.Result = ApiResult::Success;
     }
 
-    return std::move(response);
+    return response;
 }
 
 DeleteCodeNote::Response ConnectedServer::DeleteCodeNote(const DeleteCodeNote::Request& request)
@@ -985,7 +985,7 @@ DeleteCodeNote::Response ConnectedServer::DeleteCodeNote(const DeleteCodeNote::R
         response.Result = ApiResult::Success;
     }
 
-    return std::move(response);
+    return response;
 }
 
 UpdateAchievement::Response ConnectedServer::UpdateAchievement(const UpdateAchievement::Request& request)
@@ -1015,7 +1015,7 @@ UpdateAchievement::Response ConnectedServer::UpdateAchievement(const UpdateAchie
         GetRequiredJsonField(response.AchievementId, document, "AchievementID", response);
     }
 
-    return std::move(response);
+    return response;
 }
 
 FetchAchievementInfo::Response ConnectedServer::FetchAchievementInfo(const FetchAchievementInfo::Request& request)
@@ -1076,7 +1076,7 @@ FetchAchievementInfo::Response ConnectedServer::FetchAchievementInfo(const Fetch
         }
     }
 
-    return std::move(response);
+    return response;
 }
 
 FetchLeaderboardInfo::Response ConnectedServer::FetchLeaderboardInfo(const FetchLeaderboardInfo::Request& request)
@@ -1137,7 +1137,7 @@ FetchLeaderboardInfo::Response ConnectedServer::FetchLeaderboardInfo(const Fetch
         }
     }
 
-    return std::move(response);
+    return response;
 }
 
 LatestClient::Response ConnectedServer::LatestClient(const LatestClient::Request& request)
diff --git a/src/api/impl/OfflineServer.cpp b/src/api/impl/OfflineServer.cpp
index d28d3f4b..8c651f49 100644
--- a/src/api/impl/OfflineServer.cpp
+++ b/src/api/impl/OfflineServer.cpp
@@ -17,14 +17,14 @@ Login::Response OfflineServer::Login(const Login::Request& request)
     response.Result = ApiResult::Success;
     response.Username = request.Username;
     response.ApiToken = "offlineToken";
-    return std::move(response);
+    return response;
 }
 
 Logout::Response OfflineServer::Logout(const Logout::Request&) noexcept
 {
     Logout::Response response;
     response.Result = ApiResult::Success;
-    return std::move(response);
+    return response;
 }
 
 FetchGameData::Response OfflineServer::FetchGameData(const FetchGameData::Request& request)
@@ -57,7 +57,7 @@ FetchGameData::Response OfflineServer::FetchGameData(const FetchGameData::Reques
         ConnectedServer::ProcessGamePatchData(response, httpResponse);
     }
 
-    return std::move(response);
+    return response;
 }
 
 FetchCodeNotes::Response OfflineServer::FetchCodeNotes(const FetchCodeNotes::Request& request)
@@ -85,7 +85,7 @@ FetchCodeNotes::Response OfflineServer::FetchCodeNotes(const FetchCodeNotes::Req
         ConnectedServer::ProcessCodeNotes(response, document);
     }
 
-    return std::move(response);
+    return response;
 }
 
 } // namespace impl
diff --git a/src/services/AchievementRuntime.cpp b/src/services/AchievementRuntime.cpp
index d6281894..5e557097 100644
--- a/src/services/AchievementRuntime.cpp
+++ b/src/services/AchievementRuntime.cpp
@@ -232,6 +232,9 @@ static void map_event_to_change(const rc_runtime_event_t* pRuntimeEvent)
         case RC_RUNTIME_EVENT_ACHIEVEMENT_RESET:
             nChangeType = AchievementRuntime::ChangeType::AchievementReset;
             break;
+        case RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED:
+            nChangeType = AchievementRuntime::ChangeType::AchievementPaused;
+            break;
         case RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED:
             nChangeType = AchievementRuntime::ChangeType::AchievementDisabled;
             break;
diff --git a/src/services/AchievementRuntime.hh b/src/services/AchievementRuntime.hh
index a01f4228..19aaa6e8 100644
--- a/src/services/AchievementRuntime.hh
+++ b/src/services/AchievementRuntime.hh
@@ -115,7 +115,16 @@ public:
         if (!m_bInitialized)
             return false;
 
-        return (m_pRuntime.richpresence != nullptr && m_pRuntime.richpresence->richpresence != nullptr);
+        /* valid rich presence */
+        if (m_pRuntime.richpresence != nullptr && m_pRuntime.richpresence->richpresence != nullptr)
+            return true;
+
+        /* invalid rich presence */
+        if (m_nRichPresenceParseResult != RC_OK)
+            return true;
+
+        /* no rich presence */
+        return false;
     }
 
     /// <summary>
@@ -129,6 +138,7 @@ public:
         None = 0,
         AchievementTriggered,
         AchievementReset,
+        AchievementPaused,
         LeaderboardStarted,
         LeaderboardCanceled,
         LeaderboardUpdated,


Test patch:
diff --git a/tests/services/AchievementRuntime_Tests.cpp b/tests/services/AchievementRuntime_Tests.cpp
index 3d24ebd4..01a988a9 100644
--- a/tests/services/AchievementRuntime_Tests.cpp
+++ b/tests/services/AchievementRuntime_Tests.cpp
@@ -881,9 +881,11 @@ public:
 
         AchievementRuntimeHarness runtime;
         runtime.mockEmulatorContext.MockMemory(memory);
+        Assert::IsFalse(runtime.HasRichPresence());
 
         const char* pRichPresence = "Format:Num\nFormatType:Value\n\nDisplay:\n@Num(0xH01) @Num(d0xH01)\n";
         runtime.ActivateRichPresence(pRichPresence);
+        Assert::IsTrue(runtime.HasRichPresence());
 
         // string is evaluated with current memrefs (which will be 0)
         Assert::AreEqual(std::wstring(L"0 0"), runtime.GetRichPresenceDisplayString());
@@ -918,15 +920,19 @@ public:
 
         AchievementRuntimeHarness runtime;
         runtime.mockEmulatorContext.MockMemory(memory);
+        Assert::IsFalse(runtime.HasRichPresence());
 
         runtime.ActivateRichPresence("Display:\nHello, World\n");
         Assert::AreEqual(std::wstring(L"Hello, World"), runtime.GetRichPresenceDisplayString());
+        Assert::IsTrue(runtime.HasRichPresence());
 
         runtime.ActivateRichPresence("Display:\nNew String\n");
         Assert::AreEqual(std::wstring(L"New String"), runtime.GetRichPresenceDisplayString());
+        Assert::IsTrue(runtime.HasRichPresence());
 
         runtime.ActivateRichPresence("");
         Assert::AreEqual(std::wstring(L"No Rich Presence defined."), runtime.GetRichPresenceDisplayString());
+        Assert::IsFalse(runtime.HasRichPresence());
     }
 
     TEST_METHOD(TestActivateRichPresenceWithError)
@@ -939,6 +945,7 @@ public:
 
         const char* pRichPresence = "Format:Num\nFormatType:Value\n\nDisplay:\n@Num(0H01) @Num(d0xH01)\n";
         runtime.ActivateRichPresence(pRichPresence);
+        Assert::IsTrue(runtime.HasRichPresence());
 
         Assert::AreEqual(std::wstring(L"Parse error -6 (line 5): Invalid operator"), runtime.GetRichPresenceDisplayString());
     }