Repository: RetroAchievements/RAIntegration
Pull request #839 (2021-12-01 02:34:12)

allow building Measured value without comparison target


Patch:
diff --git a/src/Exports.cpp b/src/Exports.cpp
index 92439bc0..2136d314 100644
--- a/src/Exports.cpp
+++ b/src/Exports.cpp
@@ -520,6 +520,97 @@ static void ProcessAchievements()
                 pOverlayManager.RemoveScoreTracker(pChange.nId);
                 break;
             }
+
+            case ra::services::AchievementRuntime::ChangeType::LeaderboardStartReset:
+            {
+                /* ASSERT: this event is only raised if the leaderboard is watching for it */
+                const auto* pLeaderboard = pGameContext.Assets().FindLeaderboard(pChange.nId);
+                if (pLeaderboard)
+                {
+                    auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
+                    pFrameEventQueue.QueuePauseOnReset(L"Start: " + pLeaderboard->GetName());
+                }
+
+                break;
+            }
+
+            case ra::services::AchievementRuntime::ChangeType::LeaderboardSubmitReset:
+            {
+                /* ASSERT: this event is only raised if the leaderboard is watching for it */
+                const auto* pLeaderboard = pGameContext.Assets().FindLeaderboard(pChange.nId);
+                if (pLeaderboard)
+                {
+                    auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
+                    pFrameEventQueue.QueuePauseOnReset(L"Submit: " + pLeaderboard->GetName());
+                }
+
+                break;
+            }
+
+            case ra::services::AchievementRuntime::ChangeType::LeaderboardCancelReset:
+            {
+                /* ASSERT: this event is only raised if the leaderboard is watching for it */
+                const auto* pLeaderboard = pGameContext.Assets().FindLeaderboard(pChange.nId);
+                if (pLeaderboard)
+                {
+                    auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
+                    pFrameEventQueue.QueuePauseOnReset(L"Cancel: " + pLeaderboard->GetName());
+                }
+
+                break;
+            }
+
+            case ra::services::AchievementRuntime::ChangeType::LeaderboardValueReset:
+            {
+                /* ASSERT: this event is only raised if the leaderboard is watching for it */
+                const auto* pLeaderboard = pGameContext.Assets().FindLeaderboard(pChange.nId);
+                if (pLeaderboard)
+                {
+                    auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
+                    pFrameEventQueue.QueuePauseOnReset(L"Value: " + pLeaderboard->GetName());
+                }
+
+                break;
+            }
+
+            case ra::services::AchievementRuntime::ChangeType::LeaderboardStartTriggered:
+            {
+                /* ASSERT: this event is only raised if the leaderboard is watching for it */
+                const auto* pLeaderboard = pGameContext.Assets().FindLeaderboard(pChange.nId);
+                if (pLeaderboard)
+                {
+                    auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
+                    pFrameEventQueue.QueuePauseOnTrigger(L"Start: " + pLeaderboard->GetName());
+                }
+
+                break;
+            }
+
+            case ra::services::AchievementRuntime::ChangeType::LeaderboardSubmitTriggered:
+            {
+                /* ASSERT: this event is only raised if the leaderboard is watching for it */
+                const auto* pLeaderboard = pGameContext.Assets().FindLeaderboard(pChange.nId);
+                if (pLeaderboard)
+                {
+                    auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
+                    pFrameEventQueue.QueuePauseOnTrigger(L"Submit: " + pLeaderboard->GetName());
+                }
+
+                break;
+            }
+
+            case ra::services::AchievementRuntime::ChangeType::LeaderboardCancelTriggered:
+            {
+                /* ASSERT: this event is only raised if the leaderboard is watching for it */
+                const auto* pLeaderboard = pGameContext.Assets().FindLeaderboard(pChange.nId);
+                if (pLeaderboard)
+                {
+                    auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
+                    pFrameEventQueue.QueuePauseOnTrigger(L"Cancel: " + pLeaderboard->GetName());
+                }
+
+                break;
+            }
         }
     }
 }
diff --git a/src/data/models/LeaderboardModel.cpp b/src/data/models/LeaderboardModel.cpp
index def4a6b1..9ee2f48d 100644
--- a/src/data/models/LeaderboardModel.cpp
+++ b/src/data/models/LeaderboardModel.cpp
@@ -16,6 +16,8 @@ const IntModelProperty LeaderboardModel::SubmitTriggerProperty("LeaderboardModel
 const IntModelProperty LeaderboardModel::CancelTriggerProperty("LeaderboardModel", "CancelTrigger", 0);
 const IntModelProperty LeaderboardModel::ValueDefinitionProperty("LeaderboardModel", "ValueDefinition", 0);
 const IntModelProperty LeaderboardModel::ValueFormatProperty("LeaderboardModel", "ValueFormat", ra::etoi(ValueFormat::Value));
+const IntModelProperty LeaderboardModel::PauseOnResetProperty("LeaderboardModel", "PauseOnReset", ra::etoi(LeaderboardModel::LeaderboardParts::None));
+const IntModelProperty LeaderboardModel::PauseOnTriggerProperty("LeaderboardModel", "PauseOnTrigger", ra::etoi(LeaderboardModel::LeaderboardParts::None));
 const BoolModelProperty LeaderboardModel::LowerIsBetterProperty("LeaderboardModel", "LowerIsBetter", false);
 
 LeaderboardModel::LeaderboardModel() noexcept
diff --git a/src/data/models/LeaderboardModel.hh b/src/data/models/LeaderboardModel.hh
index 310ffff4..27295951 100644
--- a/src/data/models/LeaderboardModel.hh
+++ b/src/data/models/LeaderboardModel.hh
@@ -16,37 +16,46 @@ class LeaderboardModel : public AssetModelBase
 {
 public:
     LeaderboardModel() noexcept;
-/*
+
+    enum class LeaderboardParts
+    {
+        None = 0x00,
+        Start = 0x01,
+        Submit = 0x02,
+        Cancel = 0x04,
+        Value = 0x08
+    };
+
     /// <summary>
     /// The <see cref="ModelProperty" /> for whether or not the emulator should be paused when the trigger is reset.
     /// </summary>
-    static const BoolModelProperty PauseOnResetProperty;
+    static const IntModelProperty PauseOnResetProperty;
 
     /// <summary>
-    /// Gets whether or not the emulator should be paused when the trigger is reset.
+    /// Sets whether or not the emulator should be paused when the trigger is reset.
     /// </summary>
-    bool IsPauseOnReset() const { return GetValue(PauseOnResetProperty); }
+    LeaderboardParts GetPauseOnReset() const { return ra::itoe<LeaderboardParts>(GetValue(PauseOnResetProperty)); }
 
     /// <summary>
     /// Sets whether or not the emulator should be paused when the trigger is reset.
     /// </summary>
-    void SetPauseOnReset(bool nValue) { SetValue(PauseOnResetProperty, nValue); }
+    void SetPauseOnReset(LeaderboardParts nValue) { SetValue(PauseOnResetProperty, ra::etoi(nValue)); }
 
     /// <summary>
-    /// The <see cref="ModelProperty" /> for whether or not the emulator should be paused when the achievement triggers.
+    /// The <see cref="ModelProperty" /> for whether or not the emulator should be paused when a part triggers.
     /// </summary>
-    static const BoolModelProperty PauseOnTriggerProperty;
+    static const IntModelProperty PauseOnTriggerProperty;
 
     /// <summary>
-    /// Gets whether or not the emulator should be paused when the achievement triggers.
+    /// Gets whether or not the emulator should be paused when a part triggers.
     /// </summary>
-    bool IsPauseOnTrigger() const { return GetValue(PauseOnTriggerProperty); }
+    LeaderboardParts GetPauseOnTrigger() const { return ra::itoe<LeaderboardParts>(GetValue(PauseOnTriggerProperty)); }
 
     /// <summary>
-    /// Sets whether or not the emulator should be paused when the achievement triggers.
+    /// Sets whether or not the emulator should be paused when a part triggers.
     /// </summary>
-    void SetPauseOnTrigger(bool nValue) { SetValue(PauseOnTriggerProperty, nValue); }
-*/
+    void SetPauseOnTrigger(LeaderboardParts nValue) { SetValue(PauseOnTriggerProperty, ra::etoi(nValue)); }
+
     /// <summary>
     /// The <see cref="ModelProperty" /> for the start trigger version.
     /// </summary>
diff --git a/src/ra_utility.h b/src/ra_utility.h
index 97a236ea..bf72b67b 100644
--- a/src/ra_utility.h
+++ b/src/ra_utility.h
@@ -180,6 +180,12 @@ _CONSTANT_FN& operator&=(_Inout_ Enum& a, _In_ Enum b) noexcept
 {
     return (a = a & b);
 }
+
+template<typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
+_NODISCARD _CONSTANT_FN operator~(_In_ Enum a) noexcept
+{
+    return (itoe<Enum>(~etoi(a)));
+}
 } // namespace bitwise_ops
 
 namespace arith_ops {
diff --git a/src/services/AchievementRuntime.cpp b/src/services/AchievementRuntime.cpp
index e4526e81..4ba45dfc 100644
--- a/src/services/AchievementRuntime.cpp
+++ b/src/services/AchievementRuntime.cpp
@@ -270,15 +270,146 @@ static void map_event_to_change(const rc_runtime_event_t* pRuntimeEvent)
     g_pChanges->emplace_back(AchievementRuntime::Change{ nChangeType, pRuntimeEvent->id, pRuntimeEvent->value });
 }
 
+typedef struct LeaderboardPauseFlags
+{
+    ra::LeaderboardID nID;
+    ra::data::models::LeaderboardModel::LeaderboardParts nPauseOnReset;
+    ra::data::models::LeaderboardModel::LeaderboardParts nPauseOnTrigger;
+} LeaderboardPauseFlags;
+
+static void GetLeaderboardPauseFlags(std::vector<LeaderboardPauseFlags>& vLeaderboardPauseFlags, const rc_runtime_t* pRuntime)
+{
+    const auto& vAssets = ra::services::ServiceLocator::Get<ra::data::context::GameContext>().Assets();
+    for (gsl::index i = 0; i < gsl::narrow_cast<gsl::index>(vAssets.Count()); ++i)
+    {
+        const auto* pLeaderboard = dynamic_cast<const ra::data::models::LeaderboardModel*>(vAssets.GetItemAt(i));
+        if (pLeaderboard != nullptr)
+        {
+            LeaderboardPauseFlags pLeaderboardPauseFlags;
+            pLeaderboardPauseFlags.nPauseOnReset = pLeaderboard->GetPauseOnReset();
+            pLeaderboardPauseFlags.nPauseOnTrigger = pLeaderboard->GetPauseOnTrigger();
+            if (pLeaderboardPauseFlags.nPauseOnReset != ra::data::models::LeaderboardModel::LeaderboardParts::None ||
+                pLeaderboardPauseFlags.nPauseOnTrigger != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                pLeaderboardPauseFlags.nID = pLeaderboard->GetID();
+
+                const auto* pLBoard = rc_runtime_get_lboard(pRuntime, pLeaderboardPauseFlags.nID);
+                if (pLBoard != nullptr)
+                {
+                    using namespace ra::bitwise_ops;
+
+                    if (pLeaderboardPauseFlags.nPauseOnReset != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+                    {
+                        if (!pLBoard->start.has_hits)
+                            pLeaderboardPauseFlags.nPauseOnReset &= ~ra::data::models::LeaderboardModel::LeaderboardParts::Start;
+                        if (!pLBoard->submit.has_hits)
+                            pLeaderboardPauseFlags.nPauseOnReset &= ~ra::data::models::LeaderboardModel::LeaderboardParts::Submit;
+                        if (!pLBoard->cancel.has_hits)
+                            pLeaderboardPauseFlags.nPauseOnReset &= ~ra::data::models::LeaderboardModel::LeaderboardParts::Cancel;
+                        if (!pLBoard->value.value.value)
+                            pLeaderboardPauseFlags.nPauseOnReset &= ~ra::data::models::LeaderboardModel::LeaderboardParts::Value;
+                    }
+
+                    if (pLeaderboardPauseFlags.nPauseOnTrigger != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+                    {
+                        if (pLBoard->start.state == RC_TRIGGER_STATE_TRIGGERED)
+                            pLeaderboardPauseFlags.nPauseOnTrigger &= ~ra::data::models::LeaderboardModel::LeaderboardParts::Start;
+                        if (pLBoard->submit.state == RC_TRIGGER_STATE_TRIGGERED)
+                            pLeaderboardPauseFlags.nPauseOnTrigger &= ~ra::data::models::LeaderboardModel::LeaderboardParts::Submit;
+                        if (pLBoard->cancel.state == RC_TRIGGER_STATE_TRIGGERED)
+                            pLeaderboardPauseFlags.nPauseOnTrigger &= ~ra::data::models::LeaderboardModel::LeaderboardParts::Cancel;
+                    }
+
+                    const auto nMonitorFlags = pLeaderboardPauseFlags.nPauseOnReset | pLeaderboardPauseFlags.nPauseOnTrigger;
+                    if (nMonitorFlags != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+                        vLeaderboardPauseFlags.push_back(pLeaderboardPauseFlags);
+                }
+            }
+        }
+    }
+}
+
+static void CheckForLeaderboardPauseChanges(const std::vector<LeaderboardPauseFlags>& vLeaderboardPauseFlags,
+    const rc_runtime_t* pRuntime, std::vector<AchievementRuntime::Change>& changes)
+{
+    for (const auto& pLeaderboardPauseFlags : vLeaderboardPauseFlags)
+    {
+        const auto* pLBoard = rc_runtime_get_lboard(pRuntime, pLeaderboardPauseFlags.nID);
+        if (pLBoard != nullptr)
+        {
+            using namespace ra::bitwise_ops;
+
+            if (!pLBoard->start.has_hits &&
+                (pLeaderboardPauseFlags.nPauseOnReset & ra::data::models::LeaderboardModel::LeaderboardParts::Start) != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                changes.emplace_back(AchievementRuntime::Change
+                    { AchievementRuntime::ChangeType::LeaderboardStartReset, pLeaderboardPauseFlags.nID, 0 });
+            }
+
+            if (!pLBoard->submit.has_hits &&
+                (pLeaderboardPauseFlags.nPauseOnReset & ra::data::models::LeaderboardModel::LeaderboardParts::Submit) != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                changes.emplace_back(AchievementRuntime::Change
+                    { AchievementRuntime::ChangeType::LeaderboardSubmitReset, pLeaderboardPauseFlags.nID, 0 });
+            }
+
+            if (!pLBoard->cancel.has_hits &&
+                (pLeaderboardPauseFlags.nPauseOnReset & ra::data::models::LeaderboardModel::LeaderboardParts::Cancel) != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                changes.emplace_back(AchievementRuntime::Change
+                    { AchievementRuntime::ChangeType::LeaderboardCancelReset, pLeaderboardPauseFlags.nID, 0 });
+            }
+
+            if (!pLBoard->value.value.value &&
+                (pLeaderboardPauseFlags.nPauseOnReset & ra::data::models::LeaderboardModel::LeaderboardParts::Value) != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                changes.emplace_back(AchievementRuntime::Change
+                    { AchievementRuntime::ChangeType::LeaderboardValueReset, pLeaderboardPauseFlags.nID, 0 });
+            }
+
+            if (pLBoard->start.state == RC_TRIGGER_STATE_TRIGGERED &&
+                (pLeaderboardPauseFlags.nPauseOnTrigger & ra::data::models::LeaderboardModel::LeaderboardParts::Start) != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                changes.emplace_back(AchievementRuntime::Change
+                    { AchievementRuntime::ChangeType::LeaderboardStartTriggered, pLeaderboardPauseFlags.nID, 0 });
+            }
+
+            if (pLBoard->submit.state == RC_TRIGGER_STATE_TRIGGERED &&
+                (pLeaderboardPauseFlags.nPauseOnTrigger & ra::data::models::LeaderboardModel::LeaderboardParts::Submit) != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                changes.emplace_back(AchievementRuntime::Change
+                    { AchievementRuntime::ChangeType::LeaderboardSubmitTriggered, pLeaderboardPauseFlags.nID, 0 });
+            }
+
+            if (pLBoard->cancel.state == RC_TRIGGER_STATE_TRIGGERED &&
+                (pLeaderboardPauseFlags.nPauseOnTrigger & ra::data::models::LeaderboardModel::LeaderboardParts::Cancel) != ra::data::models::LeaderboardModel::LeaderboardParts::None)
+            {
+                changes.emplace_back(AchievementRuntime::Change
+                    { AchievementRuntime::ChangeType::LeaderboardCancelTriggered, pLeaderboardPauseFlags.nID, 0 });
+            }
+        }
+    }
+}
+
 _Use_decl_annotations_ void AchievementRuntime::Process(std::vector<Change>& changes)
 {
     if (!m_bInitialized || m_bPaused)
         return;
 
-    std::lock_guard<std::mutex> pLock(m_pMutex);
-    g_pChanges = &changes;
-    rc_runtime_do_frame(&m_pRuntime, map_event_to_change, rc_peek_callback, nullptr, nullptr);
-    g_pChanges = nullptr;
+    std::vector<LeaderboardPauseFlags> vLeaderboardPauseFlags;
+    if (m_pRuntime.lboard_count > 0)
+        GetLeaderboardPauseFlags(vLeaderboardPauseFlags, &m_pRuntime);
+
+    {
+        std::lock_guard<std::mutex> pLock(m_pMutex);
+
+        g_pChanges = &changes;
+        rc_runtime_do_frame(&m_pRuntime, map_event_to_change, rc_peek_callback, nullptr, nullptr);
+        g_pChanges = nullptr;
+    }
+
+    if (!vLeaderboardPauseFlags.empty())
+        CheckForLeaderboardPauseChanges(vLeaderboardPauseFlags, &m_pRuntime, changes);
 }
 
 _NODISCARD static _CONSTANT_FN ComparisonSizeToPrefix(_In_ char nSize) noexcept
diff --git a/src/services/AchievementRuntime.hh b/src/services/AchievementRuntime.hh
index 160ad0c2..c2061648 100644
--- a/src/services/AchievementRuntime.hh
+++ b/src/services/AchievementRuntime.hh
@@ -170,6 +170,15 @@ public:
         AchievementPrimed,
         AchievementActivated,
         AchievementUnprimed,
+
+        // runtime only changes
+        LeaderboardStartReset,
+        LeaderboardSubmitReset,
+        LeaderboardCancelReset,
+        LeaderboardValueReset,
+        LeaderboardStartTriggered,
+        LeaderboardSubmitTriggered,
+        LeaderboardCancelTriggered,
     };
 
     struct Change
diff --git a/src/ui/viewmodels/AssetEditorViewModel.cpp b/src/ui/viewmodels/AssetEditorViewModel.cpp
index 7d4ceffe..a25705b4 100644
--- a/src/ui/viewmodels/AssetEditorViewModel.cpp
+++ b/src/ui/viewmodels/AssetEditorViewModel.cpp
@@ -7,6 +7,7 @@
 #include "services\IConfiguration.hh"
 #include "services\ServiceLocator.hh"
 
+#include "ui\EditorTheme.hh"
 #include "ui\ImageReference.hh"
 #include "ui\viewmodels\FileDialogViewModel.hh"
 #include "ui\viewmodels\MessageBoxViewModel.hh"
@@ -31,6 +32,7 @@ const BoolModelProperty AssetEditorViewModel::LowerIsBetterProperty("AssetEditor
 const StringModelProperty AssetEditorViewModel::FormattedValueProperty("AssetEditorViewModel", "FormattedValue", L"0");
 const BoolModelProperty AssetEditorViewModel::PauseOnResetProperty("AssetEditorViewModel", "PauseOnReset", false);
 const BoolModelProperty AssetEditorViewModel::PauseOnTriggerProperty("AssetEditorViewModel", "PauseOnTrigger", false);
+const BoolModelProperty AssetEditorViewModel::IsTriggerProperty("AssetEditorViewModel", "IsTrigger", true);
 const BoolModelProperty AssetEditorViewModel::DebugHighlightsEnabledProperty("AssetEditorViewModel", "DebugHighlightsEnabled", false);
 const BoolModelProperty AssetEditorViewModel::DecimalPreferredProperty("AssetEditorViewModel", "DecimalPreferred", false);
 const BoolModelProperty AssetEditorViewModel::IsAssetLoadedProperty("AssetEditorViewModel", "IsAssetLoaded", false);
@@ -41,6 +43,7 @@ const StringModelProperty AssetEditorViewModel::AssetValidationWarningProperty("
 const BoolModelProperty AssetEditorViewModel::HasMeasuredProperty("AssetEditorViewModel", "HasMeasured", false);
 const StringModelProperty AssetEditorViewModel::MeasuredValueProperty("AssetEditorViewModel", "MeasuredValue", L"[Not Active]");
 const StringModelProperty AssetEditorViewModel::WaitingLabelProperty("AssetEditorViewModel", "WaitingLabel", L"Active");
+const IntModelProperty AssetEditorViewModel::LeaderboardPartViewModel::ColorProperty("LeaderboardPartViewModel", "Color", 0);
 
 AssetEditorViewModel::AssetEditorViewModel() noexcept
 {
@@ -56,8 +59,8 @@ AssetEditorViewModel::AssetEditorViewModel() noexcept
     m_vFormats.Add(ra::etoi(ra::data::ValueFormat::Minutes), L"Minutes");
 
     m_vLeaderboardParts.Add(ra::etoi(LeaderboardPart::Start), L"Start");
-    m_vLeaderboardParts.Add(ra::etoi(LeaderboardPart::Submit), L"Submit");
     m_vLeaderboardParts.Add(ra::etoi(LeaderboardPart::Cancel), L"Cancel");
+    m_vLeaderboardParts.Add(ra::etoi(LeaderboardPart::Submit), L"Submit");
     m_vLeaderboardParts.Add(ra::etoi(LeaderboardPart::Value), L"Value");
     m_vLeaderboardParts.GetItemAt(0)->SetSelected(true);
     m_vLeaderboardParts.AddNotifyTarget(*this);
@@ -162,6 +165,7 @@ void AssetEditorViewModel::LoadAsset(ra::data::models::AssetModelBase* pAsset)
             SetWindowTitle(L"Achievement Editor");
             SetValue(IsLeaderboardProperty, false);
             SetValue(IsAchievementProperty, true);
+            SetValue(IsTriggerProperty, true);
 
             const auto* pAchievement = dynamic_cast<ra::data::models::AchievementModel*>(pAsset);
             if (pAchievement != nullptr)
@@ -222,24 +226,57 @@ void AssetEditorViewModel::LoadAsset(ra::data::models::AssetModelBase* pAsset)
     }
 }
 
+static constexpr ra::data::models::LeaderboardModel::LeaderboardParts LeaderboardPartToParts(AssetEditorViewModel::LeaderboardPart nPart) noexcept
+{
+    switch (nPart)
+    {
+        case AssetEditorViewModel::LeaderboardPart::Start:
+            return ra::data::models::LeaderboardModel::LeaderboardParts::Start;
+        case AssetEditorViewModel::LeaderboardPart::Submit:
+            return ra::data::models::LeaderboardModel::LeaderboardParts::Submit;
+        case AssetEditorViewModel::LeaderboardPart::Cancel:
+            return ra::data::models::LeaderboardModel::LeaderboardParts::Cancel;
+        case AssetEditorViewModel::LeaderboardPart::Value:
+            return ra::data::models::LeaderboardModel::LeaderboardParts::Value;
+        default:
+            return ra::data::models::LeaderboardModel::LeaderboardParts::None;
+    }
+}
+
 void AssetEditorViewModel::UpdateLeaderboardTrigger()
 {
     const auto nPart = GetSelectedLeaderboardPart();
     if (nPart == LeaderboardPart::Value)
     {
+        SetValue(IsTriggerProperty, false);
         SetValue(GroupsHeaderProperty, L"Max of:");
         Trigger().SetIsValue(true);
     }
     else
     {
+        SetValue(IsTriggerProperty, true);
         SetValue(GroupsHeaderProperty, GroupsHeaderProperty.GetDefaultValue());
         Trigger().SetIsValue(false);
     }
 
+    const auto* pLeaderboard = dynamic_cast<ra::data::models::LeaderboardModel*>(m_pAsset);
+    if (pLeaderboard != nullptr)
+    {
+        using namespace ra::bitwise_ops;
+
+        SetPauseOnReset((pLeaderboard->GetPauseOnReset() & LeaderboardPartToParts(nPart)) != ra::data::models::LeaderboardModel::LeaderboardParts::None);
+        SetPauseOnTrigger((pLeaderboard->GetPauseOnTrigger() & LeaderboardPartToParts(nPart)) != ra::data::models::LeaderboardModel::LeaderboardParts::None);
+    }
+    else
+    {
+        SetPauseOnReset(false);
+        SetPauseOnTrigger(false);
+    }
+
     const auto* pLeaderboardDefinition = ra::services::ServiceLocator::Get<ra::services::AchievementRuntime>().GetLeaderboardDefinition(m_pAsset->GetID());
     if (pLeaderboardDefinition != nullptr)
     {
-        switch (GetSelectedLeaderboardPart())
+        switch (nPart)
         {
             case LeaderboardPart::Start:
                 Trigger().InitializeFrom(pLeaderboardDefinition->start);
@@ -257,28 +294,24 @@ void AssetEditorViewModel::UpdateLeaderboardTrigger()
                 break;
         }
     }
-    else
+    else if (pLeaderboard != nullptr)
     {
-        const auto* pLeaderboard = dynamic_cast<ra::data::models::LeaderboardModel*>(m_pAsset);
-        if (pLeaderboard != nullptr)
+        switch (nPart)
         {
-            switch (GetSelectedLeaderboardPart())
-            {
-                case LeaderboardPart::Start:
-                    Trigger().InitializeFrom(pLeaderboard->GetStartTrigger(), pLeaderboard->GetStartCapturedHits());
-                    return;
-                case LeaderboardPart::Submit:
-                    Trigger().InitializeFrom(pLeaderboard->GetSubmitTrigger(), pLeaderboard->GetSubmitCapturedHits());
-                    return;
-                case LeaderboardPart::Cancel:
-                    Trigger().InitializeFrom(pLeaderboard->GetCancelTrigger(), pLeaderboard->GetCancelCapturedHits());
-                    return;
-                case LeaderboardPart::Value:
-                    Trigger().InitializeFrom(pLeaderboard->GetValueDefinition(), pLeaderboard->GetValueCapturedHits());
-                    return;
-                default:
-                    break;
-            }
+            case LeaderboardPart::Start:
+                Trigger().InitializeFrom(pLeaderboard->GetStartTrigger(), pLeaderboard->GetStartCapturedHits());
+                return;
+            case LeaderboardPart::Submit:
+                Trigger().InitializeFrom(pLeaderboard->GetSubmitTrigger(), pLeaderboard->GetSubmitCapturedHits());
+                return;
+            case LeaderboardPart::Cancel:
+                Trigger().InitializeFrom(pLeaderboard->GetCancelTrigger(), pLeaderboard->GetCancelCapturedHits());
+                return;
+            case LeaderboardPart::Value:
+                Trigger().InitializeFrom(pLeaderboard->GetValueDefinition(), pLeaderboard->GetValueCapturedHits());
+                return;
+            default:
+                break;
         }
     }
 
@@ -371,7 +404,35 @@ void AssetEditorViewModel::OnValueChanged(const BoolModelProperty::ChangeArgs& a
         if (pLeaderboard != nullptr)
         {
             if (args.Property == LowerIsBetterProperty)
+            {
                 pLeaderboard->SetLowerIsBetter(args.tNewValue);
+            }
+            else if (args.Property == PauseOnResetProperty)
+            {
+                using namespace ra::bitwise_ops;
+
+                auto nPauseOnResetParts = pLeaderboard->GetPauseOnReset();
+                const auto nSelectedPart = LeaderboardPartToParts(GetSelectedLeaderboardPart());
+                if (args.tNewValue)
+                    nPauseOnResetParts |= nSelectedPart;
+                else
+                    nPauseOnResetParts &= ~nSelectedPart;
+
+                pLeaderboard->SetPauseOnReset(nPauseOnResetParts);
+            }
+            else if (args.Property == PauseOnTriggerProperty)
+            {
+                using namespace ra::bitwise_ops;
+
+                auto nPauseOnTriggerParts = pLeaderboard->GetPauseOnTrigger();
+                const auto nSelectedPart = LeaderboardPartToParts(GetSelectedLeaderboardPart());
+                if (args.tNewValue)
+                    nPauseOnTriggerParts |= nSelectedPart;
+                else
+                    nPauseOnTriggerParts &= ~nSelectedPart;
+
+                pLeaderboard->SetPauseOnTrigger(nPauseOnTriggerParts);
+            }
         }
     }
 
@@ -386,6 +447,8 @@ void AssetEditorViewModel::OnValueChanged(const BoolModelProperty::ChangeArgs& a
     {
         auto& pConfiguration = ra::services::ServiceLocator::GetMutable<ra::services::IConfiguration>();
         pConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, args.tNewValue);
+
+        m_vmTrigger.UpdateConditions();
     }
     else if (args.Property == IsVisibleProperty)
     {
@@ -703,6 +766,87 @@ void AssetEditorViewModel::UpdateAssetFrameValues()
     UpdateMeasuredValue();
 }
 
+static void UpdateLeaderboardPartColor(AssetEditorViewModel::LeaderboardPartViewModel& pPart,
+    const rc_trigger_t& pTrigger, const ra::ui::EditorTheme& pTheme, const ra::ui::Color nDefaultColor)
+{
+    switch (pTrigger.state)
+    {
+        case RC_TRIGGER_STATE_PAUSED:
+            pPart.SetColor(pTheme.ColorTriggerPauseTrue());
+            break;
+        case RC_TRIGGER_STATE_TRIGGERED:
+            pPart.SetColor(pTheme.ColorTriggerIsTrue());
+            break;
+        default:
+            if (pPart.m_nPreviousHits)
+            {
+                if (!pTrigger.has_hits)
+                    pPart.SetColor(pTheme.ColorTriggerResetTrue());
+                else
+                    pPart.SetColor(nDefaultColor);
+            }
+            else
+            {
+                pPart.SetColor(nDefaultColor);
+            }
+            break;
+    }
+
+    pPart.m_nPreviousHits = pTrigger.has_hits;
+}
+
+static bool AreAllLeaderboardValuesPaused(const rc_condset_t* pValue) noexcept
+{
+    for (; pValue != nullptr; pValue = pValue->next)
+    {
+        if (!pValue->has_pause)
+            return false;
+        if (!pValue->is_paused)
+            return false;
+    }
+
+    return true;
+}
+
+static void UpdateLeaderboardPartColors(ViewModelCollection<AssetEditorViewModel::LeaderboardPartViewModel>& vLeaderboardParts, const rc_lboard_t* pLeaderboard)
+{
+    const auto nDefaultColor = ra::ui::Color(ra::to_unsigned(AssetEditorViewModel::LeaderboardPartViewModel::ColorProperty.GetDefaultValue()));
+
+    if (pLeaderboard == nullptr)
+    {
+        // no leaderboard, or leaderboard not active, disable highlights
+        for (gsl::index i = 0; i < gsl::narrow_cast<gsl::index>(vLeaderboardParts.Count()); ++i)
+        {
+            auto* pLeaderboardPart = vLeaderboardParts.GetItemAt(i);
+            if (pLeaderboardPart != nullptr)
+                pLeaderboardPart->SetColor(nDefaultColor);
+        }
+
+        return;
+    }
+
+    const auto& pTheme = ra::services::ServiceLocator::Get<ra::ui::EditorTheme>();
+    if (pLeaderboard->state == RC_LBOARD_STATE_STARTED)
+        UpdateLeaderboardPartColor(*vLeaderboardParts.GetItemAt(0), pLeaderboard->start, pTheme, pTheme.ColorTriggerWasTrue());
+    else
+        UpdateLeaderboardPartColor(*vLeaderboardParts.GetItemAt(0), pLeaderboard->start, pTheme, nDefaultColor);
+
+    UpdateLeaderboardPartColor(*vLeaderboardParts.GetItemAt(1), pLeaderboard->submit, pTheme, nDefaultColor);
+    UpdateLeaderboardPartColor(*vLeaderboardParts.GetItemAt(2), pLeaderboard->cancel, pTheme, nDefaultColor);
+
+    GSL_SUPPRESS_CON4
+    {
+        auto nValueColor = nDefaultColor;
+        if (pLeaderboard->value.conditions != nullptr)
+        {
+            if (AreAllLeaderboardValuesPaused(pLeaderboard->value.conditions))
+                nValueColor = pTheme.ColorTriggerPauseTrue();
+        }
+
+        vLeaderboardParts.GetItemAt(3)->SetColor(nValueColor);
+    }
+}
+
 void AssetEditorViewModel::UpdateDebugHighlights()
 {
     const auto& pRuntime = ra::services::ServiceLocator::Get<ra::services::AchievementRuntime>();
@@ -720,6 +864,8 @@ void AssetEditorViewModel::UpdateDebugHighlights()
             if (pLeaderboardDefinition == nullptr)
                 break;
 
+            UpdateLeaderboardPartColors(m_vLeaderboardParts, pLeaderboardDefinition);
+
             switch (GetSelectedLeaderboardPart())
             {
                 case LeaderboardPart::Start:
diff --git a/src/ui/viewmodels/AssetEditorViewModel.hh b/src/ui/viewmodels/AssetEditorViewModel.hh
index e0073c9a..72bc021c 100644
--- a/src/ui/viewmodels/AssetEditorViewModel.hh
+++ b/src/ui/viewmodels/AssetEditorViewModel.hh
@@ -271,11 +271,28 @@ public:
         Value
     };
 
+    class LeaderboardPartViewModel : public LookupItemViewModel
+    {
+    public:
+        LeaderboardPartViewModel(int nId, const std::wstring& sLabel) noexcept
+            : LookupItemViewModel(nId, sLabel)
+        {
+        }
+
+        LeaderboardPart GetLeaderboardPart() const { return ra::itoe<LeaderboardPart>(GetValue(IDProperty)); }
+
+        static const IntModelProperty ColorProperty;
+        Color GetColor() const { return Color(ra::to_unsigned(GetValue(ColorProperty))); }
+        void SetColor(Color value) { SetValue(ColorProperty, ra::to_signed(value.ARGB)); }
+
+        bool m_nPreviousHits = false;
+    };
+
     /// <summary>
     /// Gets the list of leaderboard parts.
     /// </summary>
-    const LookupItemViewModelCollection& LeaderboardParts() const noexcept { return m_vLeaderboardParts; }
-    LookupItemViewModelCollection& LeaderboardParts() noexcept { return m_vLeaderboardParts; }
+    const ViewModelCollection<LeaderboardPartViewModel>& LeaderboardParts() const noexcept { return m_vLeaderboardParts; }
+    ViewModelCollection<LeaderboardPartViewModel>& LeaderboardParts() noexcept { return m_vLeaderboardParts; }
 
     /// <summary>
     /// The <see cref="ModelProperty" /> for the selected leaderboard part.
@@ -329,6 +346,16 @@ public:
     /// </summary>
     void SetPauseOnTrigger(bool nValue) { SetValue(PauseOnTriggerProperty, nValue); }
 
+    /// <summary>
+    /// The <see cref="ModelProperty" /> for whether or not a trigger is being edited.
+    /// </summary>
+    static const BoolModelProperty IsTriggerProperty;
+
+    /// <summary>
+    /// Gets whether or not a trigger is being edited.
+    /// </summary>
+    bool IsTrigger() const { return GetValue(IsTriggerProperty); }
+
     /// <summary>
     /// The <see cref="ModelProperty" /> for whether or not condition truthiness should be color-highlighted.
     /// </summary>
@@ -424,7 +451,7 @@ protected:
     bool m_bIgnoreTriggerUpdate = false;
 
     LookupItemViewModelCollection m_vFormats;
-    LookupItemViewModelCollection m_vLeaderboardParts;
+    ViewModelCollection<LeaderboardPartViewModel> m_vLeaderboardParts;
 };
 
 } // namespace viewmodels
diff --git a/src/ui/viewmodels/AssetListViewModel.cpp b/src/ui/viewmodels/AssetListViewModel.cpp
index 0f529d07..678b8be5 100644
--- a/src/ui/viewmodels/AssetListViewModel.cpp
+++ b/src/ui/viewmodels/AssetListViewModel.cpp
@@ -147,36 +147,33 @@ void AssetListViewModel::OnDataModelIntValueChanged(gsl::index nIndex, const Int
         if (GetSpecialFilter() != SpecialFilter::All)
             AddOrRemoveFilteredItem(nIndex);
 
-        const auto nNewState = ra::itoe<ra::data::models::AssetState>(args.tNewValue);
-        if (!ra::data::models::AssetModelBase::IsActive(nNewState))
+        // when an active achievement is deactivated, the challenge indicator needs
+        // to be hidden as no event will be raised. similarly, the leaderboard tracker
+        // needs to be hidden when a leaderboard is deactivated.
+        const auto nOldState = ra::itoe<ra::data::models::AssetState>(args.tOldValue);
+        if (nOldState == ra::data::models::AssetState::Primed)
         {
-            // when an active achievement is deactivated, the challenge indicator needs
-            // to be hidden as no event will be raised. similarly, the leaderboard tracker
-            // needs to be hidden when a leaderboard is deactivated.
-            const auto nOldState = ra::itoe<ra::data::models::AssetState>(args.tOldValue);
-            if (ra::data::models::AssetModelBase::IsActive(nOldState))
+            const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
+            const auto* pAsset = pGameContext.Assets().GetItemAt(nIndex);
+            switch (pAsset->GetType())
             {
-                const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
-                const auto* pAsset = pGameContext.Assets().GetItemAt(nIndex);
-                switch (pAsset->GetType())
+                case ra::data::models::AssetType::Achievement:
                 {
-                    case ra::data::models::AssetType::Achievement:
-                    {
-                        auto& pOverlayManager = ra::services::ServiceLocator::GetMutable<ra::ui::viewmodels::OverlayManager>();
-                        pOverlayManager.RemoveChallengeIndicator(pAsset->GetID());
-                        break;
-                    }
+                    auto& pOverlayManager = ra::services::ServiceLocator::GetMutable<ra::ui::viewmodels::OverlayManager>();
+                    pOverlayManager.RemoveChallengeIndicator(pAsset->GetID());
+                    break;
+                }
 
-                    case ra::data::models::AssetType::Leaderboard:
-                    {
-                        auto& pOverlayManager = ra::services::ServiceLocator::GetMutable<ra::ui::viewmodels::OverlayManager>();
-                        pOverlayManager.RemoveScoreTracker(pAsset->GetID());
-                        break;
-                    }
+                case ra::data::models::AssetType::Leaderboard:
+                {
+                    auto& pOverlayManager = ra::services::ServiceLocator::GetMutable<ra::ui::viewmodels::OverlayManager>();
+                    pOverlayManager.RemoveScoreTracker(pAsset->GetID());
+                    break;
                 }
             }
         }
 
+        const auto nNewState = ra::itoe<ra::data::models::AssetState>(args.tNewValue);
         if (nNewState == ra::data::models::AssetState::Triggered && KeepActive())
         {
             // if KeepActive is selected, set a Triggered achievement back to Waiting
@@ -1278,7 +1275,8 @@ void AssetListViewModel::ResetSelected()
     if (vmMessageBox.ShowModal() == DialogResult::No)
         return;
 
-    std::vector<ra::AchievementID> vActiveAchievementsBefore;
+    std::vector<ra::AchievementID> vPrimedAchievementsBefore;
+    std::vector<ra::LeaderboardID> vPrimedLeaderboardsBefore;
 
     auto& pGameContext = ra::services::ServiceLocator::GetMutable<ra::data::context::GameContext>();
     pGameContext.Assets().BeginUpdate();
@@ -1288,14 +1286,24 @@ void AssetListViewModel::ResetSelected()
     {
         RA_LOG_INFO("Resetting all assets");
 
-        // reset all - first remove any "new" items
+        // reset all - identify active assets and remove any "new" items
         for (gsl::index nIndex = gsl::narrow_cast<gsl::index>(pGameContext.Assets().Count()) - 1; nIndex >= 0; --nIndex)
         {
             auto* pAsset = pGameContext.Assets().GetItemAt(nIndex);
             if (pAsset != nullptr)
             {
-                if (pAsset->GetType() == ra::data::models::AssetType::Achievement && pAsset->IsActive())
-                    vActiveAchievementsBefore.push_back(pAsset->GetID());
+                if (pAsset->GetState() == ra::data::models::AssetState::Primed)
+                {
+                    switch (pAsset->GetType())
+                    {
+                        case ra::data::models::AssetType::Achievement:
+                            vPrimedAchievementsBefore.push_back(pAsset->GetID());
+                            break;
+                        case ra::data::models::AssetType::Leaderboard:
+                            vPrimedLeaderboardsBefore.push_back(pAsset->GetID());
+                            break;
+                    }
+                }
 
                 if (pAsset->GetChanges() == ra::data::models::AssetChanges::New)
                     pGameContext.Assets().RemoveAt(nIndex);
@@ -1317,8 +1325,18 @@ void AssetListViewModel::ResetSelected()
                 auto* pAsset = pGameContext.Assets().GetItemAt(nIndex);
                 if (pAsset->GetID() == nId && pAsset->GetType() == nType)
                 {
-                    if (nType == ra::data::models::AssetType::Achievement && pAsset->IsActive())
-                        vActiveAchievementsBefore.push_back(nId);
+                    if (pAsset->GetState() == ra::data::models::AssetState::Primed)
+                    {
+                        switch (nType)
+                        {
+                            case ra::data::models::AssetType::Achievement:
+                                vPrimedAchievementsBefore.push_back(pAsset->GetID());
+                                break;
+                            case ra::data::models::AssetType::Leaderboard:
+                                vPrimedLeaderboardsBefore.push_back(pAsset->GetID());
+                                break;
+                        }
+                    }
 
                     if (pAsset->GetChanges() == ra::data::models::AssetChanges::New)
                         pGameContext.Assets().RemoveAt(nIndex);
@@ -1355,7 +1373,7 @@ void AssetListViewModel::ResetSelected()
     pGameContext.Assets().EndUpdate();
 
     // if any achievements were deleted, disable their challenge indicators
-    for (auto nId : vActiveAchievementsBefore)
+    for (auto nId : vPrimedAchievementsBefore)
     {
         if (!pGameContext.Assets().FindAchievement(nId))
         {
@@ -1364,6 +1382,17 @@ void AssetListViewModel::ResetSelected()
             pOverlayManager.RemoveChallengeIndicator(nId);
         }
     }
+
+    // if any leaderboards were deleted, disable their score trackers
+    for (auto nId : vPrimedLeaderboardsBefore)
+    {
+        if (!pGameContext.Assets().FindLeaderboard(nId))
+        {
+            // when an active leaderboard is deleted, the score tracker needs to be hidden as no event will be raised
+            auto& pOverlayManager = ra::services::ServiceLocator::GetMutable<ra::ui::viewmodels::OverlayManager>();
+            pOverlayManager.RemoveScoreTracker(nId);
+        }
+    }
 }
 
 void AssetListViewModel::RevertSelected()
diff --git a/src/ui/viewmodels/TriggerConditionViewModel.cpp b/src/ui/viewmodels/TriggerConditionViewModel.cpp
index 9bb42ed8..1f93fb2d 100644
--- a/src/ui/viewmodels/TriggerConditionViewModel.cpp
+++ b/src/ui/viewmodels/TriggerConditionViewModel.cpp
@@ -37,6 +37,7 @@ const BoolModelProperty TriggerConditionViewModel::HasSourceSizeProperty("Trigge
 const BoolModelProperty TriggerConditionViewModel::HasTargetProperty("TriggerConditionViewModel", "HasTarget", true);
 const BoolModelProperty TriggerConditionViewModel::HasTargetSizeProperty("TriggerConditionViewModel", "HasTargetSize", false);
 const BoolModelProperty TriggerConditionViewModel::HasHitsProperty("TriggerConditionViewModel", "HasHits", true);
+const BoolModelProperty TriggerConditionViewModel::CanEditHitsProperty("TriggerConditionViewModel", "CanEditHits", true);
 const IntModelProperty TriggerConditionViewModel::RowColorProperty("TriggerConditionViewModel", "RowColor", 0);
 
 std::string TriggerConditionViewModel::Serialize() const
@@ -136,7 +137,7 @@ void TriggerConditionViewModel::SerializeAppend(std::string& sBuffer) const
         SerializeAppendOperand(sBuffer, GetTargetType(), GetTargetSize(), GetTargetValue());
     }
 
-    if (GetValue(HasHitsProperty))
+    if (GetValue(HasHitsProperty) && GetValue(CanEditHitsProperty))
     {
         const auto nRequiredHits = GetRequiredHits();
         if (nRequiredHits > 0)
@@ -372,7 +373,15 @@ void TriggerConditionViewModel::InitializeFrom(const rc_condition_t& pCondition)
     SetOperator(static_cast<TriggerOperatorType>(pCondition.oper));
 
     SetCurrentHits(pCondition.current_hits);
-    SetRequiredHits(pCondition.required_hits);
+
+    // Values have an "infinite" hit target, display 0
+    SetRequiredHits(pCondition.required_hits == 0xFFFFFFFF ? 0 : pCondition.required_hits);
+}
+
+bool TriggerConditionViewModel::IsForValue() const noexcept
+{
+    const auto* pTriggerViewModel = dynamic_cast<const TriggerViewModel*>(m_pTriggerViewModel);
+    return (pTriggerViewModel && pTriggerViewModel->IsValue());
 }
 
 void TriggerConditionViewModel::OnValueChanged(const IntModelProperty::ChangeArgs& args)
@@ -414,6 +423,7 @@ void TriggerConditionViewModel::OnValueChanged(const IntModelProperty::ChangeArg
     else if (args.Property == OperatorProperty)
     {
         SetValue(HasTargetProperty, ra::itoe<TriggerOperatorType>(args.tNewValue) != TriggerOperatorType::None);
+        UpdateHasHits();
     }
     else if (args.Property == TypeProperty)
     {
@@ -421,23 +431,39 @@ void TriggerConditionViewModel::OnValueChanged(const IntModelProperty::ChangeArg
         const auto nNewType = ra::itoe<TriggerConditionType>(args.tNewValue);
         const auto bIsModifying = IsModifying(nNewType);
         if (bIsModifying != IsModifying(nOldType))
-        {
-            if (bIsModifying)
-            {
-                SetValue(HasHitsProperty, false);
-                SetOperator(TriggerOperatorType::None);
-            }
-            else
-            {
-                SetValue(HasHitsProperty, true);
-                SetOperator(TriggerOperatorType::Equals);
-            }
-        }
+            SetOperator(bIsModifying ? TriggerOperatorType::None : TriggerOperatorType::Equals);
+        else if (!bIsModifying && GetOperator() == TriggerOperatorType::None)
+            SetOperator(TriggerOperatorType::Equals);
+
+        UpdateHasHits();
     }
 
     ViewModelBase::OnValueChanged(args);
 }
 
+void TriggerConditionViewModel::UpdateHasHits()
+{
+    const auto nType = GetType();
+
+    if (IsModifying(nType))
+    {
+        SetValue(HasHitsProperty, false);
+        SetValue(CanEditHitsProperty, false);
+    }
+    else if (nType == TriggerConditionType::Measured && IsForValue())
+    {
+        // if Measured value is generated from hit count, show the hit count, but don't allow the target to be edited
+        SetValue(HasHitsProperty, GetOperator() != TriggerOperatorType::None);
+        SetValue(CanEditHitsProperty, false);
+        SetValue(RequiredHitsProperty, 0);
+    }
+    else
+    {
+        SetValue(HasHitsProperty, true);
+        SetValue(CanEditHitsProperty, true);
+    }
+}
+
 void TriggerConditionViewModel::OnValueChanged(const BoolModelProperty::ChangeArgs& args)
 {
     if (args.Property == HasTargetProperty)
@@ -695,6 +721,15 @@ bool TriggerConditionViewModel::IsComparisonVisible(const ViewModelBase& vmItem,
     switch (nComparison)
     {
         case TriggerOperatorType::None:
+            if (vmCondition->IsModifying())
+                return true;
+
+            // "None" can only be selected for the Measured flag when editing a value
+            if (vmCondition->GetType() == TriggerConditionType::Measured && vmCondition->IsForValue())
+                return true;
+
+            return false;
+
         case TriggerOperatorType::Multiply:
         case TriggerOperatorType::Divide:
         case TriggerOperatorType::BitwiseAnd:
diff --git a/src/ui/viewmodels/TriggerConditionViewModel.hh b/src/ui/viewmodels/TriggerConditionViewModel.hh
index 52e543a1..4563439b 100644
--- a/src/ui/viewmodels/TriggerConditionViewModel.hh
+++ b/src/ui/viewmodels/TriggerConditionViewModel.hh
@@ -103,6 +103,7 @@ public:
     void SetTargetValue(float fValue);
 
     static const BoolModelProperty HasHitsProperty;
+    static const BoolModelProperty CanEditHitsProperty;
     static const IntModelProperty CurrentHitsProperty;
     unsigned int GetCurrentHits() const { return ra::to_unsigned(GetValue(CurrentHitsProperty)); }
     void SetCurrentHits(unsigned int nValue) { SetValue(CurrentHitsProperty, ra::to_signed(nValue)); }
@@ -157,6 +158,8 @@ private:
 
     static bool IsModifying(TriggerConditionType nType) noexcept;
     static bool IsAddressType(TriggerOperandType nType) noexcept;
+    void UpdateHasHits();
+    bool IsForValue() const noexcept;
 
     const ViewModelBase* m_pTriggerViewModel = nullptr;
 };
diff --git a/src/ui/viewmodels/TriggerViewModel.cpp b/src/ui/viewmodels/TriggerViewModel.cpp
index da427e2b..680c0231 100644
--- a/src/ui/viewmodels/TriggerViewModel.cpp
+++ b/src/ui/viewmodels/TriggerViewModel.cpp
@@ -735,6 +735,13 @@ void TriggerViewModel::OnValueChanged(const BoolModelProperty::ChangeArgs& args)
     ViewModelBase::OnValueChanged(args);
 }
 
+void TriggerViewModel::UpdateConditions()
+{
+    auto* pGroup = m_vGroups.GetItemAt(GetSelectedGroupIndex());
+    if (pGroup != nullptr)
+        UpdateConditions(pGroup);
+}
+
 void TriggerViewModel::UpdateConditions(const GroupViewModel* pGroup)
 {
     m_vConditions.RemoveNotifyTarget(m_pConditionsMonitor);
diff --git a/src/ui/viewmodels/TriggerViewModel.hh b/src/ui/viewmodels/TriggerViewModel.hh
index 232ae588..b993b9fa 100644
--- a/src/ui/viewmodels/TriggerViewModel.hh
+++ b/src/ui/viewmodels/TriggerViewModel.hh
@@ -140,6 +140,7 @@ public:
 
     void DoFrame();
     void UpdateColors(const rc_trigger_t* pTrigger);
+    void UpdateConditions();
 
     static bool BuildHitChainTooltip(std::wstring& sTooltip, const ViewModelCollection<TriggerConditionViewModel>& vmConditions, gsl::index nIndex);
 
diff --git a/src/ui/win32/AssetEditorDialog.cpp b/src/ui/win32/AssetEditorDialog.cpp
index a64d64b1..41ba38c1 100644
--- a/src/ui/win32/AssetEditorDialog.cpp
+++ b/src/ui/win32/AssetEditorDialog.cpp
@@ -367,6 +367,8 @@ public:
         Expects(pGridBinding != nullptr);
         if (!pGridBinding->GetItems().GetItemValue(pInfo.nItemIndex, TriggerConditionViewModel::HasHitsProperty))
             return nullptr;
+        if (!pGridBinding->GetItems().GetItemValue(pInfo.nItemIndex, TriggerConditionViewModel::CanEditHitsProperty))
+            return nullptr;
 
         m_nEditingItemIndex = pInfo.nItemIndex;
         return GridNumberColumnBinding::CreateInPlaceEditor(hParent, pInfo);
@@ -448,12 +450,6 @@ void AssetEditorDialog::BadgeNameBinding::UpdateTextFromSource(const std::wstrin
     }
 }
 
-void AssetEditorDialog::DecimalPreferredBinding::OnValueChanged()
-{
-    m_pOwner->m_bindConditions.RefreshColumn(4); // source value
-    m_pOwner->m_bindConditions.RefreshColumn(8); // target value
-}
-
 bool AssetEditorDialog::ActiveCheckBoxBinding::IsActive() const
 {
     const auto& vmAssetEditor = GetViewModel<ra::ui::viewmodels::AssetEditorViewModel&>();
@@ -642,7 +638,7 @@ AssetEditorDialog::AssetEditorDialog(AssetEditorViewModel& vmAssetEditor)
     m_bindPauseOnReset(vmAssetEditor),
     m_bindPauseOnTrigger(vmAssetEditor),
     m_bindActive(vmAssetEditor),
-    m_bindDecimalPreferred(this, vmAssetEditor),
+    m_bindDecimalPreferred(vmAssetEditor),
     m_bindGroups(vmAssetEditor.Trigger()),
     m_bindConditions(vmAssetEditor.Trigger())
 {
@@ -677,6 +673,7 @@ AssetEditorDialog::AssetEditorDialog(AssetEditorViewModel& vmAssetEditor)
     m_bindWindow.BindLabel(IDC_RA_MEASURED, AssetEditorViewModel::MeasuredValueProperty);
     m_bindWindow.BindVisible(IDC_RA_LBL_MEASURED, AssetEditorViewModel::HasMeasuredProperty);
     m_bindWindow.BindVisible(IDC_RA_MEASURED, AssetEditorViewModel::HasMeasuredProperty);
+    m_bindWindow.BindEnabled(IDC_RA_CHK_PAUSE_ON_TRIGGER, AssetEditorViewModel::IsTriggerProperty);
 
     // achievement only fields
     m_bindBadge.BindText(AssetEditorViewModel::BadgeProperty);
@@ -714,6 +711,7 @@ AssetEditorDialog::AssetEditorDialog(AssetEditorViewModel& vmAssetEditor)
     m_bindLeaderboardParts.BindColumn(0, std::move(pPartColumn));
     m_bindLeaderboardParts.BindIsSelected(ra::ui::viewmodels::LookupItemViewModel::IsSelectedProperty);
     m_bindLeaderboardParts.BindItems(vmAssetEditor.LeaderboardParts());
+    m_bindLeaderboardParts.BindRowColor(AssetEditorViewModel::LeaderboardPartViewModel::ColorProperty);
 
     m_bindDebugHighlights.BindCheck(AssetEditorViewModel::DebugHighlightsEnabledProperty);
     m_bindPauseOnReset.BindCheck(AssetEditorViewModel::PauseOnResetProperty);
diff --git a/src/ui/win32/AssetEditorDialog.hh b/src/ui/win32/AssetEditorDialog.hh
index 33680bbe..8867886a 100644
--- a/src/ui/win32/AssetEditorDialog.hh
+++ b/src/ui/win32/AssetEditorDialog.hh
@@ -61,19 +61,6 @@ private:
         void UpdateTextFromSource(const std::wstring& sText) override;
     };
 
-    class DecimalPreferredBinding : public ra::ui::win32::bindings::CheckBoxBinding
-    {
-    public:
-        DecimalPreferredBinding(AssetEditorDialog* pOwner, ViewModelBase& vmViewModel) noexcept
-            : ra::ui::win32::bindings::CheckBoxBinding(vmViewModel), m_pOwner(pOwner) {}
-
-    protected:
-        void OnValueChanged() override;
-
-    private:
-        AssetEditorDialog* m_pOwner;
-    };
-
     class ActiveCheckBoxBinding : public ra::ui::win32::bindings::ControlBinding
     {
     public:
@@ -144,7 +131,7 @@ private:
     ra::ui::win32::bindings::CheckBoxBinding m_bindPauseOnReset;
     ra::ui::win32::bindings::CheckBoxBinding m_bindPauseOnTrigger;
     ActiveCheckBoxBinding m_bindActive;
-    DecimalPreferredBinding m_bindDecimalPreferred;
+    ra::ui::win32::bindings::CheckBoxBinding m_bindDecimalPreferred;
 
     ra::ui::win32::bindings::GridBinding m_bindGroups;
     ConditionsGridBinding m_bindConditions;


Test patch:
diff --git a/tests/Exports_Tests.cpp b/tests/Exports_Tests.cpp
index 55369e99..e4469e8a 100644
--- a/tests/Exports_Tests.cpp
+++ b/tests/Exports_Tests.cpp
@@ -735,6 +735,188 @@ public:
         Assert::IsNull(pScore);
     }
 
+    TEST_METHOD(TestDoAchievementsFrameLeaderboardStartPauseOnReset)
+    {
+        DoAchievementsFrameHarness harness;
+        harness.MockAchievement(1U);
+        auto& pLeaderboard = harness.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(1U);
+        pLeaderboard.SetName(L"Title");
+        harness.mockRuntime.QueueChange(ra::services::AchievementRuntime::ChangeType::LeaderboardStartReset, 1U);
+        bool bWasPaused = false;
+        harness.mockEmulatorContext.SetPauseFunction([&bWasPaused] { bWasPaused = true; });
+
+        bool bMessageSeen = false;
+        harness.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>([&bMessageSeen](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+        {
+            bMessageSeen = true;
+            Assert::AreEqual(std::wstring(L"The following triggers have been reset:\n* Start: Title"), vmMessageBox.GetMessage());
+            return ra::ui::DialogResult::OK;
+        });
+
+        _RA_DoAchievementsFrame();
+        harness.mockFrameEventQueue.DoFrame(); // UpdateUIForFrameChange isn't called in the unit test build
+
+        Assert::IsTrue(bMessageSeen);
+        Assert::IsTrue(bWasPaused);
+    }
+
+    TEST_METHOD(TestDoAchievementsFrameLeaderboardSubmitPauseOnReset)
+    {
+        DoAchievementsFrameHarness harness;
+        harness.MockAchievement(1U);
+        auto& pLeaderboard = harness.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(1U);
+        pLeaderboard.SetName(L"Title");
+        harness.mockRuntime.QueueChange(ra::services::AchievementRuntime::ChangeType::LeaderboardSubmitReset, 1U);
+        bool bWasPaused = false;
+        harness.mockEmulatorContext.SetPauseFunction([&bWasPaused] { bWasPaused = true; });
+
+        bool bMessageSeen = false;
+        harness.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>([&bMessageSeen](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+        {
+            bMessageSeen = true;
+            Assert::AreEqual(std::wstring(L"The following triggers have been reset:\n* Submit: Title"), vmMessageBox.GetMessage());
+            return ra::ui::DialogResult::OK;
+        });
+
+        _RA_DoAchievementsFrame();
+        harness.mockFrameEventQueue.DoFrame(); // UpdateUIForFrameChange isn't called in the unit test build
+
+        Assert::IsTrue(bMessageSeen);
+        Assert::IsTrue(bWasPaused);
+    }
+
+    TEST_METHOD(TestDoAchievementsFrameLeaderboardCancelPauseOnReset)
+    {
+        DoAchievementsFrameHarness harness;
+        harness.MockAchievement(1U);
+        auto& pLeaderboard = harness.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(1U);
+        pLeaderboard.SetName(L"Title");
+        harness.mockRuntime.QueueChange(ra::services::AchievementRuntime::ChangeType::LeaderboardCancelReset, 1U);
+        bool bWasPaused = false;
+        harness.mockEmulatorContext.SetPauseFunction([&bWasPaused] { bWasPaused = true; });
+
+        bool bMessageSeen = false;
+        harness.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>([&bMessageSeen](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+        {
+            bMessageSeen = true;
+            Assert::AreEqual(std::wstring(L"The following triggers have been reset:\n* Cancel: Title"), vmMessageBox.GetMessage());
+            return ra::ui::DialogResult::OK;
+        });
+
+        _RA_DoAchievementsFrame();
+        harness.mockFrameEventQueue.DoFrame(); // UpdateUIForFrameChange isn't called in the unit test build
+
+        Assert::IsTrue(bMessageSeen);
+        Assert::IsTrue(bWasPaused);
+    }
+
+    TEST_METHOD(TestDoAchievementsFrameLeaderboardValuePauseOnReset)
+    {
+        DoAchievementsFrameHarness harness;
+        harness.MockAchievement(1U);
+        auto& pLeaderboard = harness.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(1U);
+        pLeaderboard.SetName(L"Title");
+        harness.mockRuntime.QueueChange(ra::services::AchievementRuntime::ChangeType::LeaderboardValueReset, 1U);
+        bool bWasPaused = false;
+        harness.mockEmulatorContext.SetPauseFunction([&bWasPaused] { bWasPaused = true; });
+
+        bool bMessageSeen = false;
+        harness.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>([&bMessageSeen](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+        {
+            bMessageSeen = true;
+            Assert::AreEqual(std::wstring(L"The following triggers have been reset:\n* Value: Title"), vmMessageBox.GetMessage());
+            return ra::ui::DialogResult::OK;
+        });
+
+        _RA_DoAchievementsFrame();
+        harness.mockFrameEventQueue.DoFrame(); // UpdateUIForFrameChange isn't called in the unit test build
+
+        Assert::IsTrue(bMessageSeen);
+        Assert::IsTrue(bWasPaused);
+    }
+
+    TEST_METHOD(TestDoAchievementsFrameLeaderboardStartPauseOnTriggered)
+    {
+        DoAchievementsFrameHarness harness;
+        harness.MockAchievement(1U);
+        auto& pLeaderboard = harness.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(1U);
+        pLeaderboard.SetName(L"Title");
+        harness.mockRuntime.QueueChange(ra::services::AchievementRuntime::ChangeType::LeaderboardStartTriggered, 1U);
+        bool bWasPaused = false;
+        harness.mockEmulatorContext.SetPauseFunction([&bWasPaused] { bWasPaused = true; });
+
+        bool bMessageSeen = false;
+        harness.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>([&bMessageSeen](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+        {
+            bMessageSeen = true;
+            Assert::AreEqual(std::wstring(L"The following triggers have triggered:\n* Start: Title"), vmMessageBox.GetMessage());
+            return ra::ui::DialogResult::OK;
+        });
+
+        _RA_DoAchievementsFrame();
+        harness.mockFrameEventQueue.DoFrame(); // UpdateUIForFrameChange isn't called in the unit test build
+
+        Assert::IsTrue(bMessageSeen);
+        Assert::IsTrue(bWasPaused);
+    }
+
+    TEST_METHOD(TestDoAchievementsFrameLeaderboardSubmitPauseOnTriggered)
+    {
+        DoAchievementsFrameHarness harness;
+        harness.MockAchievement(1U);
+        auto& pLeaderboard = harness.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(1U);
+        pLeaderboard.SetName(L"Title");
+        harness.mockRuntime.QueueChange(ra::services::AchievementRuntime::ChangeType::LeaderboardSubmitTriggered, 1U);
+        bool bWasPaused = false;
+        harness.mockEmulatorContext.SetPauseFunction([&bWasPaused] { bWasPaused = true; });
+
+        bool bMessageSeen = false;
+        harness.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>([&bMessageSeen](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+        {
+            bMessageSeen = true;
+            Assert::AreEqual(std::wstring(L"The following triggers have triggered:\n* Submit: Title"), vmMessageBox.GetMessage());
+            return ra::ui::DialogResult::OK;
+        });
+
+        _RA_DoAchievementsFrame();
+        harness.mockFrameEventQueue.DoFrame(); // UpdateUIForFrameChange isn't called in the unit test build
+
+        Assert::IsTrue(bMessageSeen);
+        Assert::IsTrue(bWasPaused);
+    }
+
+    TEST_METHOD(TestDoAchievementsFrameLeaderboardCancelPauseOnTriggered)
+    {
+        DoAchievementsFrameHarness harness;
+        harness.MockAchievement(1U);
+        auto& pLeaderboard = harness.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(1U);
+        pLeaderboard.SetName(L"Title");
+        harness.mockRuntime.QueueChange(ra::services::AchievementRuntime::ChangeType::LeaderboardCancelTriggered, 1U);
+        bool bWasPaused = false;
+        harness.mockEmulatorContext.SetPauseFunction([&bWasPaused] { bWasPaused = true; });
+
+        bool bMessageSeen = false;
+        harness.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>([&bMessageSeen](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+        {
+            bMessageSeen = true;
+            Assert::AreEqual(std::wstring(L"The following triggers have triggered:\n* Cancel: Title"), vmMessageBox.GetMessage());
+            return ra::ui::DialogResult::OK;
+        });
+
+        _RA_DoAchievementsFrame();
+        harness.mockFrameEventQueue.DoFrame(); // UpdateUIForFrameChange isn't called in the unit test build
+
+        Assert::IsTrue(bMessageSeen);
+        Assert::IsTrue(bWasPaused);
+    }
+
     TEST_METHOD(TestDoAchievementsFrameMultiple)
     {
         DoAchievementsFrameHarness harness;
diff --git a/tests/data/DataAsserts.hh b/tests/data/DataAsserts.hh
index d6818d2e..0fa69661 100644
--- a/tests/data/DataAsserts.hh
+++ b/tests/data/DataAsserts.hh
@@ -132,6 +132,26 @@ std::wstring ToString<ra::data::ValueFormat>(const ra::data::ValueFormat& nForma
     }
 }
 
+template<>
+std::wstring ToString<ra::data::models::LeaderboardModel::LeaderboardParts>(const ra::data::models::LeaderboardModel::LeaderboardParts& nParts)
+{
+    switch (nParts)
+    {
+        case ra::data::models::LeaderboardModel::LeaderboardParts::None:
+            return L"None";
+        case ra::data::models::LeaderboardModel::LeaderboardParts::Start:
+            return L"Start";
+        case ra::data::models::LeaderboardModel::LeaderboardParts::Submit:
+            return L"Submit";
+        case ra::data::models::LeaderboardModel::LeaderboardParts::Cancel:
+            return L"Cancel";
+        case ra::data::models::LeaderboardModel::LeaderboardParts::Value:
+            return L"Value";
+        default:
+            return std::to_wstring(static_cast<int>(nParts));
+    }
+}
+
 #pragma warning(pop)
 
 } // namespace CppUnitTestFramework
diff --git a/tests/services/AchievementRuntime_Tests.cpp b/tests/services/AchievementRuntime_Tests.cpp
index 01a988a9..0b5ef390 100644
--- a/tests/services/AchievementRuntime_Tests.cpp
+++ b/tests/services/AchievementRuntime_Tests.cpp
@@ -949,6 +949,289 @@ public:
 
         Assert::AreEqual(std::wstring(L"Parse error -6 (line 5): Invalid operator"), runtime.GetRichPresenceDisplayString());
     }
+
+    TEST_METHOD(TestProcessLeaderboardStartPauseOnReset)
+    {
+        std::array<unsigned char, 2> memory{ 0x00, 0x00 };
+
+        AchievementRuntimeHarness runtime;
+        runtime.mockEmulatorContext.MockMemory(memory);
+        std::vector<AchievementRuntime::Change> vChanges;
+        auto* pDefinition = "STA:0xH0000=0_0xH0000=9_R:0xH0001=1::SUB:0xH0000=1::CAN:0xH0000=2::VAL:0xH0000";
+
+        auto& pLeaderboard = runtime.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(6U);
+
+        runtime.ActivateLeaderboard(6U, pDefinition);
+        const auto* pLboard = runtime.GetLeaderboardDefinition(6U);
+        runtime.Process(vChanges);
+        Assert::IsTrue(pLboard->start.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::IsFalse(pLboard->start.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        pLeaderboard.SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Start);
+
+        memory.at(1) = 0;
+        runtime.Process(vChanges);
+        Assert::IsTrue(pLboard->start.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::IsFalse(pLboard->start.has_hits);
+
+        runtime.Process(vChanges);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardStartReset, 6U);
+    }
+
+    TEST_METHOD(TestProcessLeaderboardSubmitPauseOnReset)
+    {
+        std::array<unsigned char, 2> memory{ 0x00, 0x00 };
+
+        AchievementRuntimeHarness runtime;
+        runtime.mockEmulatorContext.MockMemory(memory);
+        std::vector<AchievementRuntime::Change> vChanges;
+        auto* pDefinition = "STA:0xH0000=1::SUB:0xH0000=0_0xH0000=9_R:0xH0001=1::CAN:0xH0000=2::VAL:0xH0000";
+
+        auto& pLeaderboard = runtime.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(6U);
+
+        runtime.ActivateLeaderboard(6U, pDefinition);
+        const auto* pLboard = runtime.GetLeaderboardDefinition(6U);
+        runtime.Process(vChanges);
+        Assert::IsTrue(pLboard->submit.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::IsFalse(pLboard->submit.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        pLeaderboard.SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Submit);
+
+        memory.at(1) = 0;
+        runtime.Process(vChanges);
+        Assert::IsTrue(pLboard->submit.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::IsFalse(pLboard->submit.has_hits);
+
+        runtime.Process(vChanges);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardSubmitReset, 6U);
+    }
+
+    TEST_METHOD(TestProcessLeaderboardCancelPauseOnReset)
+    {
+        std::array<unsigned char, 2> memory{ 0x00, 0x00 };
+
+        AchievementRuntimeHarness runtime;
+        runtime.mockEmulatorContext.MockMemory(memory);
+        std::vector<AchievementRuntime::Change> vChanges;
+        auto* pDefinition = "STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=0_0xH0000=9_R:0xH0001=1::VAL:0xH0000";
+
+        auto& pLeaderboard = runtime.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(6U);
+
+        runtime.ActivateLeaderboard(6U, pDefinition);
+        const auto* pLboard = runtime.GetLeaderboardDefinition(6U);
+        runtime.Process(vChanges);
+        Assert::IsTrue(pLboard->cancel.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::IsFalse(pLboard->cancel.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        pLeaderboard.SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Cancel);
+
+        memory.at(1) = 0;
+        runtime.Process(vChanges);
+        Assert::IsTrue(pLboard->cancel.has_hits);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::IsFalse(pLboard->cancel.has_hits);
+
+        runtime.Process(vChanges);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardCancelReset, 6U);
+    }
+
+    TEST_METHOD(TestProcessLeaderboardValuePauseOnReset)
+    {
+        std::array<unsigned char, 2> memory{ 0x05, 0x00 };
+
+        AchievementRuntimeHarness runtime;
+        runtime.mockEmulatorContext.MockMemory(memory);
+        std::vector<AchievementRuntime::Change> vChanges;
+        auto* pDefinition = "STA:0xH0000=0::SUB:0xH0000=2::CAN:0xH0000=3::VAL:Q:0xH0001=0_M:0xH0000";
+
+        auto& pLeaderboard = runtime.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(6U);
+
+        runtime.ActivateLeaderboard(6U, pDefinition);
+        auto* pLboard = runtime.GetLeaderboardDefinition(6U);
+        pLboard->state = RC_LBOARD_STATE_STARTED; // value not calculated if not started
+        runtime.Process(vChanges);
+        Assert::AreEqual(5U, pLboard->value.value.value);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardUpdated, 6U);
+        vChanges.clear();
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::AreEqual(0U, pLboard->value.value.value);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardUpdated, 6U);
+        vChanges.clear();
+
+        pLeaderboard.SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Value);
+
+        memory.at(1) = 0;
+        runtime.Process(vChanges);
+        Assert::AreEqual(5U, pLboard->value.value.value);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardUpdated, 6U);
+        vChanges.clear();
+
+        memory.at(1) = 1;
+        runtime.Process(vChanges);
+        Assert::AreEqual(0U, pLboard->value.value.value);
+
+        runtime.Process(vChanges);
+        Assert::AreEqual({ 2U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardUpdated, 6U);
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardValueReset, 6U);
+    }
+
+    TEST_METHOD(TestProcessLeaderboardStartPauseOnTrigger)
+    {
+        std::array<unsigned char, 2> memory{ 0x00, 0x00 };
+
+        AchievementRuntimeHarness runtime;
+        runtime.mockEmulatorContext.MockMemory(memory);
+        std::vector<AchievementRuntime::Change> vChanges;
+        auto* pDefinition = "STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=3::VAL:0xH0001";
+
+        auto& pLeaderboard = runtime.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(6U);
+
+        runtime.ActivateLeaderboard(6U, pDefinition);
+        const auto* pLboard = runtime.GetLeaderboardDefinition(6U);
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_ACTIVE, (int)pLboard->start.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(0) = 1;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_TRIGGERED, (int)pLboard->start.state);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardStarted, 6U);
+        vChanges.clear();
+
+        pLeaderboard.SetPauseOnTrigger(ra::data::models::LeaderboardModel::LeaderboardParts::Start);
+
+        memory.at(0) = 0;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_ACTIVE, (int)pLboard->start.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(0) = 1;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_TRIGGERED, (int)pLboard->start.state);
+
+        runtime.Process(vChanges);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardStartTriggered, 6U);
+    }
+
+    TEST_METHOD(TestProcessLeaderboardSubmitPauseOnTrigger)
+    {
+        std::array<unsigned char, 2> memory{ 0x00, 0x00 };
+
+        AchievementRuntimeHarness runtime;
+        runtime.mockEmulatorContext.MockMemory(memory);
+        std::vector<AchievementRuntime::Change> vChanges;
+        auto* pDefinition = "STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=3::VAL:0xH0001";
+
+        auto& pLeaderboard = runtime.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(6U);
+
+        runtime.ActivateLeaderboard(6U, pDefinition);
+        const auto* pLboard = runtime.GetLeaderboardDefinition(6U);
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_ACTIVE, (int)pLboard->submit.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(0) = 2;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_TRIGGERED, (int)pLboard->submit.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        pLeaderboard.SetPauseOnTrigger(ra::data::models::LeaderboardModel::LeaderboardParts::Submit);
+
+        memory.at(0) = 0;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_ACTIVE, (int)pLboard->submit.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(0) = 2;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_TRIGGERED, (int)pLboard->submit.state);
+
+        runtime.Process(vChanges);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardSubmitTriggered, 6U);
+    }
+
+    TEST_METHOD(TestProcessLeaderboardCancelPauseOnTrigger)
+    {
+        std::array<unsigned char, 2> memory{ 0x00, 0x00 };
+
+        AchievementRuntimeHarness runtime;
+        runtime.mockEmulatorContext.MockMemory(memory);
+        std::vector<AchievementRuntime::Change> vChanges;
+        auto* pDefinition = "STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=3::VAL:0xH0001";
+
+        auto& pLeaderboard = runtime.mockGameContext.Assets().NewLeaderboard();
+        pLeaderboard.SetID(6U);
+
+        runtime.ActivateLeaderboard(6U, pDefinition);
+        const auto* pLboard = runtime.GetLeaderboardDefinition(6U);
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_ACTIVE, (int)pLboard->cancel.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(0) = 3;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_TRIGGERED, (int)pLboard->cancel.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        pLeaderboard.SetPauseOnTrigger(ra::data::models::LeaderboardModel::LeaderboardParts::Cancel);
+
+        memory.at(0) = 0;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_ACTIVE, (int)pLboard->cancel.state);
+        Assert::AreEqual({ 0U }, vChanges.size());
+
+        memory.at(0) = 3;
+        runtime.Process(vChanges);
+        Assert::AreEqual((int)RC_TRIGGER_STATE_TRIGGERED, (int)pLboard->cancel.state);
+
+        runtime.Process(vChanges);
+        Assert::AreEqual({ 1U }, vChanges.size());
+        AssertChange(vChanges, AchievementRuntime::ChangeType::LeaderboardCancelTriggered, 6U);
+    }
 };
 
 } // namespace tests
diff --git a/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp b/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp
index 95b22460..f4c6d079 100644
--- a/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp
@@ -103,6 +103,7 @@ public:
         Assert::AreEqual(std::wstring(L"[Not Active]"), editor.GetMeasuredValue());
         Assert::IsTrue(editor.IsAchievement());
         Assert::IsFalse(editor.IsLeaderboard());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::AreEqual((int)AssetEditorViewModel::LeaderboardPart::Start, (int)editor.GetSelectedLeaderboardPart());
         Assert::AreEqual(ra::data::ValueFormat::Value, editor.GetValueFormat());
 
@@ -123,10 +124,10 @@ public:
         Assert::AreEqual({ 4U }, editor.LeaderboardParts().Count());
         Assert::AreEqual((int)AssetEditorViewModel::LeaderboardPart::Start, editor.LeaderboardParts().GetItemAt(0)->GetId());
         Assert::AreEqual(std::wstring(L"Start"), editor.LeaderboardParts().GetItemAt(0)->GetLabel());
-        Assert::AreEqual((int)AssetEditorViewModel::LeaderboardPart::Submit, editor.LeaderboardParts().GetItemAt(1)->GetId());
-        Assert::AreEqual(std::wstring(L"Submit"), editor.LeaderboardParts().GetItemAt(1)->GetLabel());
-        Assert::AreEqual((int)AssetEditorViewModel::LeaderboardPart::Cancel, editor.LeaderboardParts().GetItemAt(2)->GetId());
-        Assert::AreEqual(std::wstring(L"Cancel"), editor.LeaderboardParts().GetItemAt(2)->GetLabel());
+        Assert::AreEqual((int)AssetEditorViewModel::LeaderboardPart::Cancel, editor.LeaderboardParts().GetItemAt(1)->GetId());
+        Assert::AreEqual(std::wstring(L"Cancel"), editor.LeaderboardParts().GetItemAt(1)->GetLabel());
+        Assert::AreEqual((int)AssetEditorViewModel::LeaderboardPart::Submit, editor.LeaderboardParts().GetItemAt(2)->GetId());
+        Assert::AreEqual(std::wstring(L"Submit"), editor.LeaderboardParts().GetItemAt(2)->GetLabel());
         Assert::AreEqual((int)AssetEditorViewModel::LeaderboardPart::Value, editor.LeaderboardParts().GetItemAt(3)->GetId());
         Assert::AreEqual(std::wstring(L"Value"), editor.LeaderboardParts().GetItemAt(3)->GetLabel());
     }
@@ -158,6 +159,9 @@ public:
         Assert::IsFalse(editor.IsPauseOnReset());
         Assert::IsFalse(editor.IsPauseOnTrigger());
         Assert::IsTrue(editor.IsAssetLoaded());
+        Assert::IsTrue(editor.IsAchievement());
+        Assert::IsFalse(editor.IsLeaderboard());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::IsFalse(editor.HasMeasured());
         Assert::IsFalse(editor.HasAssetValidationError());
         Assert::IsFalse(editor.HasAssetValidationWarning());
@@ -176,6 +180,9 @@ public:
         Assert::IsFalse(editor.IsPauseOnReset());
         Assert::IsFalse(editor.IsPauseOnTrigger());
         Assert::IsFalse(editor.IsAssetLoaded());
+        Assert::IsTrue(editor.IsAchievement());
+        Assert::IsFalse(editor.IsLeaderboard());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::IsFalse(editor.HasMeasured());
         Assert::IsFalse(editor.HasAssetValidationError());
         Assert::IsFalse(editor.HasAssetValidationWarning());
@@ -478,6 +485,9 @@ public:
         Assert::IsFalse(editor.IsPauseOnReset());
         Assert::IsFalse(editor.IsPauseOnTrigger());
         Assert::IsTrue(editor.IsAssetLoaded());
+        Assert::IsFalse(editor.IsAchievement());
+        Assert::IsTrue(editor.IsLeaderboard());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::IsTrue(editor.HasMeasured());
         Assert::IsFalse(editor.HasAssetValidationError());
         Assert::IsFalse(editor.HasAssetValidationWarning());
@@ -851,6 +861,110 @@ public:
         Assert::IsFalse(achievement.IsPauseOnTrigger());
     }
 
+    TEST_METHOD(TestSyncPauseOnResetLeaderboardStart)
+    {
+        AssetEditorViewModelHarness editor;
+        LeaderboardModel leaderboard;
+        leaderboard.SetPauseOnReset(LeaderboardModel::LeaderboardParts::Start);
+        leaderboard.CreateServerCheckpoint();
+        leaderboard.CreateLocalCheckpoint();
+
+        editor.LoadAsset(&leaderboard);
+        editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Start);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Start, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(true);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Start, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+    }
+
+    TEST_METHOD(TestSyncPauseOnResetLeaderboardSubmit)
+    {
+        AssetEditorViewModelHarness editor;
+        LeaderboardModel leaderboard;
+        leaderboard.SetPauseOnReset(LeaderboardModel::LeaderboardParts::Submit);
+        leaderboard.CreateServerCheckpoint();
+        leaderboard.CreateLocalCheckpoint();
+
+        editor.LoadAsset(&leaderboard);
+        editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Submit);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Submit, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(true);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Submit, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+    }
+
+    TEST_METHOD(TestSyncPauseOnResetLeaderboardCancel)
+    {
+        AssetEditorViewModelHarness editor;
+        LeaderboardModel leaderboard;
+        leaderboard.SetPauseOnReset(LeaderboardModel::LeaderboardParts::Cancel);
+        leaderboard.CreateServerCheckpoint();
+        leaderboard.CreateLocalCheckpoint();
+
+        editor.LoadAsset(&leaderboard);
+        editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Cancel);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Cancel, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(true);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Cancel, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+    }
+
+    TEST_METHOD(TestSyncPauseOnResetLeaderboardValue)
+    {
+        AssetEditorViewModelHarness editor;
+        LeaderboardModel leaderboard;
+        leaderboard.SetPauseOnReset(LeaderboardModel::LeaderboardParts::Value);
+        leaderboard.CreateServerCheckpoint();
+        leaderboard.CreateLocalCheckpoint();
+
+        editor.LoadAsset(&leaderboard);
+        editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Value);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Value, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(true);
+        Assert::IsTrue(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::Value, leaderboard.GetPauseOnReset());
+
+        editor.SetPauseOnReset(false);
+        Assert::IsFalse(editor.IsPauseOnReset());
+        Assert::AreEqual(LeaderboardModel::LeaderboardParts::None, leaderboard.GetPauseOnReset());
+    }
+
     TEST_METHOD(TestSyncOneRecordAtATime)
     {
         AssetEditorViewModelHarness editor;
@@ -1404,6 +1518,47 @@ public:
         Assert::IsFalse(editor.mockConfiguration.IsFeatureEnabled(ra::services::Feature::PreferDecimal));
     }
 
+    TEST_METHOD(TestDecimalPreferredUpdatesConditions)
+    {
+        AssetEditorViewModelHarness editor;
+        AchievementModel achievement;
+        achievement.SetID(1234U);
+        achievement.SetTrigger("0xH1234=1_fF2222=f2.3");
+        achievement.CreateServerCheckpoint();
+        achievement.CreateLocalCheckpoint();
+        achievement.Activate();
+
+        const auto* pTrigger = editor.mockRuntime.GetAchievementTrigger(1234U);
+        Expects(pTrigger != nullptr);
+        pTrigger->requirement->conditions->current_hits = 6U;
+
+        editor.LoadAsset(&achievement);
+        const auto* pCondition1 = editor.Trigger().Conditions().GetItemAt(0);
+        Expects(pCondition1 != nullptr);
+        const auto* pCondition2 = editor.Trigger().Conditions().GetItemAt(1);
+        Expects(pCondition2 != nullptr);
+
+        Assert::IsFalse(editor.IsDecimalPreferred());
+        Assert::AreEqual(std::wstring(L"0x1234"), pCondition1->GetSourceValue());
+        Assert::AreEqual(std::wstring(L"0x01"), pCondition1->GetTargetValue());
+        Assert::AreEqual(std::wstring(L"0x2222"), pCondition2->GetSourceValue());
+        Assert::AreEqual(std::wstring(L"2.3"), pCondition2->GetTargetValue());
+
+        editor.SetDecimalPreferred(true);
+        Assert::IsTrue(editor.IsDecimalPreferred());
+        Assert::AreEqual(std::wstring(L"0x1234"), pCondition1->GetSourceValue());
+        Assert::AreEqual(std::wstring(L"1"), pCondition1->GetTargetValue());
+        Assert::AreEqual(std::wstring(L"0x2222"), pCondition2->GetSourceValue());
+        Assert::AreEqual(std::wstring(L"2.3"), pCondition2->GetTargetValue());
+
+        editor.SetDecimalPreferred(false);
+        Assert::IsFalse(editor.IsDecimalPreferred());
+        Assert::AreEqual(std::wstring(L"0x1234"), pCondition1->GetSourceValue());
+        Assert::AreEqual(std::wstring(L"0x01"), pCondition1->GetTargetValue());
+        Assert::AreEqual(std::wstring(L"0x2222"), pCondition2->GetSourceValue());
+        Assert::AreEqual(std::wstring(L"2.3"), pCondition2->GetTargetValue());
+    }
+
     TEST_METHOD(TestCaptureRestoreHits)
     {
         AssetEditorViewModelHarness editor;
@@ -1541,30 +1696,35 @@ public:
         Assert::AreEqual(std::string("0xH1234=6.1."), editor.Trigger().Serialize());
         Assert::AreEqual(std::wstring(L"Groups:"), editor.GetGroupsHeaderLabel());
         Assert::IsFalse(editor.Trigger().IsValue());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::IsFalse(leaderboard.IsModified());
 
         editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Submit);
         Assert::AreEqual(std::string("0xH2345!=99"), editor.Trigger().Serialize());
         Assert::AreEqual(std::wstring(L"Groups:"), editor.GetGroupsHeaderLabel());
         Assert::IsFalse(editor.Trigger().IsValue());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::IsFalse(leaderboard.IsModified());
 
         editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Cancel);
         Assert::AreEqual(std::string("0xH3456>3"), editor.Trigger().Serialize());
         Assert::AreEqual(std::wstring(L"Groups:"), editor.GetGroupsHeaderLabel());
         Assert::IsFalse(editor.Trigger().IsValue());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::IsFalse(leaderboard.IsModified());
 
         editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Value);
         Assert::AreEqual(std::string("M:0xH4444"), editor.Trigger().Serialize());
         Assert::AreEqual(std::wstring(L"Max of:"), editor.GetGroupsHeaderLabel());
         Assert::IsTrue(editor.Trigger().IsValue());
+        Assert::IsFalse(editor.IsTrigger());
         Assert::IsFalse(leaderboard.IsModified());
 
         editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Start);
         Assert::AreEqual(std::string("0xH1234=6.1."), editor.Trigger().Serialize());
         Assert::AreEqual(std::wstring(L"Groups:"), editor.GetGroupsHeaderLabel());
         Assert::IsFalse(editor.Trigger().IsValue());
+        Assert::IsTrue(editor.IsTrigger());
         Assert::IsFalse(leaderboard.IsModified());
     }
 };
diff --git a/tests/ui/viewmodels/AssetListViewModel_Tests.cpp b/tests/ui/viewmodels/AssetListViewModel_Tests.cpp
index f0022263..c257d3d6 100644
--- a/tests/ui/viewmodels/AssetListViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/AssetListViewModel_Tests.cpp
@@ -4201,7 +4201,7 @@ public:
         Assert::IsNull(vmAssetList.mockWindowManager.AssetEditor.GetAsset());
     }
 
-    TEST_METHOD(TestResetSelectedNoLongerExistsInFileActiveChallenge)
+    TEST_METHOD(TestResetSelectedNoLongerExistsInFilePrimedChallenge)
     {
         AssetListViewModelHarness vmAssetList;
         vmAssetList.MockGameId(22U);
@@ -4240,6 +4240,46 @@ public:
         Assert::IsTrue(pIndicator->IsDestroyPending());
     }
 
+    TEST_METHOD(TestResetSelectedNoLongerExistsInFilePrimedLeaderboard)
+    {
+        AssetListViewModelHarness vmAssetList;
+        vmAssetList.MockGameId(22U);
+        vmAssetList.AddLeaderboard(ra::data::models::AssetCategory::Local, L"Leaderboard1");
+        vmAssetList.SetFilterCategory(AssetListViewModel::FilterCategory::Local);
+        vmAssetList.SetAssetTypeFilter(ra::data::models::AssetType::Leaderboard);
+        vmAssetList.FilteredAssets().GetItemAt(0)->SetSelected(true);
+        vmAssetList.ForceUpdateButtons();
+        vmAssetList.AssertButtonState(ResetButtonState::Reset);
+
+        auto* vmAsset = vmAssetList.mockGameContext.Assets().GetItemAt(0);
+        Assert::IsNotNull(vmAsset);
+        Ensures(vmAsset != nullptr);
+        vmAsset->SetState(ra::data::models::AssetState::Primed);
+
+        vmAssetList.mockOverlayManager.AddScoreTracker(vmAsset->GetID());
+        const auto* pIndicator = vmAssetList.mockOverlayManager.GetScoreTracker(vmAsset->GetID());
+        Assert::IsNotNull(pIndicator);
+        Ensures(pIndicator != nullptr);
+        Assert::IsFalse(pIndicator->IsDestroyPending());
+
+        vmAssetList.MockUserFileContents("\n");
+
+        bool bDialogShown = false;
+        vmAssetList.mockDesktop.ExpectWindow<MessageBoxViewModel>([&bDialogShown](MessageBoxViewModel&)
+        {
+            bDialogShown = true;
+            return DialogResult::Yes;
+        });
+
+        vmAssetList.ResetSelected();
+
+        Assert::IsTrue(bDialogShown);
+
+        // RemoveScoreTracker only marks the item as to be destroyed.
+        // it still exists until the next Render()
+        Assert::IsTrue(pIndicator->IsDestroyPending());
+    }
+
     TEST_METHOD(TestResetSelectedSomeNew)
     {
         AssetListViewModelHarness vmAssetList;
diff --git a/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp b/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
index b882c427..110fceaf 100644
--- a/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
@@ -41,6 +41,7 @@ private:
         bool HasTargetSize() const { return GetValue(HasTargetSizeProperty); }
         bool HasTarget() const { return GetValue(HasTargetProperty); }
         bool HasHits() const { return GetValue(HasHitsProperty); }
+        bool CanEditHits() const { return GetValue(CanEditHitsProperty); }
 
     private:
         std::string sBuffer;
@@ -63,6 +64,23 @@ private:
         Assert::AreEqual(sInput, sOutput);
     }
 
+    void ParseAndRegenerateValue(const std::string& sInput)
+    {
+        unsigned char pBuffer[512] = { 0 };
+        const auto nSize = rc_value_size(sInput.c_str());
+        Assert::IsTrue(nSize > 0 && nSize < sizeof(pBuffer));
+
+        const rc_value_t* pValue = rc_parse_value(pBuffer, sInput.c_str(), nullptr, 0);
+        Assert::IsNotNull(pValue);
+        Ensures(pValue != nullptr);
+
+        TriggerConditionViewModelHarness vmCondition;
+        vmCondition.InitializeFrom(*pValue->conditions->conditions);
+
+        const std::string sOutput = vmCondition.Serialize();
+        Assert::AreEqual(sInput, sOutput);
+    }
+
 public:
     TEST_METHOD(TestInitialState)
     {
@@ -83,6 +101,7 @@ public:
         Assert::IsFalse(vmCondition.HasTargetSize());
         Assert::IsTrue(vmCondition.HasTarget());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
     }
 
     TEST_METHOD(TestHasSourceSize)
@@ -249,54 +268,107 @@ public:
         TriggerConditionViewModelHarness vmCondition;
         Assert::AreEqual(TriggerConditionType::Standard, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::AddAddress);
         Assert::AreEqual(TriggerConditionType::AddAddress, vmCondition.GetType());
         Assert::IsFalse(vmCondition.HasHits());
+        Assert::IsFalse(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::AddHits);
         Assert::AreEqual(TriggerConditionType::AddHits, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::AddSource);
         Assert::AreEqual(TriggerConditionType::AddSource, vmCondition.GetType());
         Assert::IsFalse(vmCondition.HasHits());
+        Assert::IsFalse(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::AndNext);
         Assert::AreEqual(TriggerConditionType::AndNext, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::Measured);
         Assert::AreEqual(TriggerConditionType::Measured, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::MeasuredIf);
         Assert::AreEqual(TriggerConditionType::MeasuredIf, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::OrNext);
         Assert::AreEqual(TriggerConditionType::OrNext, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::PauseIf);
         Assert::AreEqual(TriggerConditionType::PauseIf, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::ResetIf);
         Assert::AreEqual(TriggerConditionType::ResetIf, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::SubHits);
         Assert::AreEqual(TriggerConditionType::SubHits, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::SubSource);
         Assert::AreEqual(TriggerConditionType::SubSource, vmCondition.GetType());
         Assert::IsFalse(vmCondition.HasHits());
+        Assert::IsFalse(vmCondition.CanEditHits());
 
         vmCondition.SetType(TriggerConditionType::Trigger);
         Assert::AreEqual(TriggerConditionType::Trigger, vmCondition.GetType());
         Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
+    }
+
+    TEST_METHOD(TestHasHitsForValue)
+    {
+        TriggerConditionViewModelHarness vmCondition;
+        TriggerViewModel vmTrigger;
+        vmTrigger.SetIsValue(true);
+        vmCondition.SetTriggerViewModel(&vmTrigger);
+
+        Assert::AreEqual(TriggerConditionType::Standard, vmCondition.GetType());
+        Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
+
+        vmCondition.SetType(TriggerConditionType::Measured);
+        Assert::AreEqual(TriggerConditionType::Measured, vmCondition.GetType());
+        Assert::AreEqual(TriggerOperatorType::Equals, vmCondition.GetOperator());
+        Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsFalse(vmCondition.CanEditHits());
+
+        vmCondition.SetOperator(TriggerOperatorType::None);
+        Assert::AreEqual(TriggerConditionType::Measured, vmCondition.GetType());
+        Assert::AreEqual(TriggerOperatorType::None, vmCondition.GetOperator());
+        Assert::IsFalse(vmCondition.HasHits());
+        Assert::IsFalse(vmCondition.CanEditHits());
+
+        vmCondition.SetType(TriggerConditionType::Standard);
+        Assert::AreEqual(TriggerConditionType::Standard, vmCondition.GetType());
+        Assert::AreEqual(TriggerOperatorType::Equals, vmCondition.GetOperator());
+        Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsTrue(vmCondition.CanEditHits());
+
+        vmCondition.SetRequiredHits(5U);
+        Assert::AreEqual(5U, vmCondition.GetRequiredHits());
+
+        vmCondition.SetType(TriggerConditionType::Measured);
+        Assert::AreEqual(TriggerConditionType::Measured, vmCondition.GetType());
+        Assert::AreEqual(TriggerOperatorType::Equals, vmCondition.GetOperator());
+        Assert::IsTrue(vmCondition.HasHits());
+        Assert::IsFalse(vmCondition.CanEditHits());
+        Assert::AreEqual(0U, vmCondition.GetRequiredHits());
     }
 
     TEST_METHOD(TestSerializeHasHits)
@@ -408,6 +480,8 @@ public:
     {
         ParseAndRegenerate("0xH1234=5"); // none
         ParseAndRegenerate("R:0xH1234=5.100."); // 100
+        ParseAndRegenerateValue("M:0xH1234=5"); // measured without hit target
+        ParseAndRegenerateValue("M:0xH1234"); // measured without comparison
     }
 
     TEST_METHOD(TestTooltipAddress)
@@ -865,6 +939,40 @@ public:
         Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Divide)));
         Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::BitwiseAnd)));
     }
+
+    TEST_METHOD(TestIsComparisonVisibleMeasured)
+    {
+        TriggerConditionViewModelHarness condition;
+
+        condition.SetType(TriggerConditionType::Measured);
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Equals)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::NotEquals)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::LessThan)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::LessThanOrEqual)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::GreaterThan)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::GreaterThanOrEqual)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::None)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Multiply)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Divide)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::BitwiseAnd)));
+
+        TriggerViewModel vmTrigger;
+        vmTrigger.SetIsValue(true);
+        condition.SetTriggerViewModel(&vmTrigger);
+
+        // None operator is visible when associated to a Value trigger
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Equals)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::NotEquals)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::LessThan)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::LessThanOrEqual)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::GreaterThan)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::GreaterThanOrEqual)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::None)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Multiply)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Divide)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::BitwiseAnd)));
+    }
+
 };
 
 } // namespace tests