Achievements: Split up overlay setting

Split into challenge indicator modes, leaderboard trackers, and progress
indicators.
pull/3498/head
Stenzek 4 months ago
parent e98871ad47
commit 501380ec7c
No known key found for this signature in database

@ -80,7 +80,7 @@
<PropertyGroup>
<MocDefines></MocDefines>
<MocDefines Condition="!$(Configuration.Contains(Debug))">-DQT_NO_DEBUG -DNDEBUG $(MocDefines)</MocDefines>
<MocIncludes>-I"$(QtIncludeDir)." -I"$(SolutionDir)pcsx2" "-I$(SolutionDir)." -I.</MocIncludes>
<MocIncludes>-I"$(QtIncludeDir)." -I.</MocIncludes>
</PropertyGroup>
<Target Name="QtMoc"
BeforeTargets="ClCompile"

@ -112,6 +112,7 @@ struct AchievementChallengeIndicator
{
const rc_client_achievement_t* achievement;
std::string badge_path;
float time_remaining;
float opacity;
bool active;
};
@ -729,6 +730,12 @@ void Achievements::UpdateSettings(const Settings& old_config)
if (g_settings.achievements_unofficial_test_mode != old_config.achievements_unofficial_test_mode)
rc_client_set_unofficial_enabled(s_state.client, g_settings.achievements_unofficial_test_mode);
}
if (!g_settings.achievements_leaderboard_trackers)
s_state.active_leaderboard_trackers.clear();
if (!g_settings.achievements_progress_indicators)
s_state.active_progress_indicator.reset();
}
void Achievements::Shutdown()
@ -1614,6 +1621,9 @@ void Achievements::HandleLeaderboardTrackerShowEvent(const rc_client_event_t* ev
{
DEV_LOG("Showing leaderboard tracker: {}: {}", event->leaderboard_tracker->id, event->leaderboard_tracker->display);
if (!g_settings.achievements_leaderboard_trackers)
return;
const u32 id = event->leaderboard_tracker->id;
auto it = std::find_if(s_state.active_leaderboard_trackers.begin(), s_state.active_leaderboard_trackers.end(),
[id](const auto& it) { return it.tracker_id == id; });
@ -1636,25 +1646,26 @@ void Achievements::HandleLeaderboardTrackerShowEvent(const rc_client_event_t* ev
void Achievements::HandleLeaderboardTrackerHideEvent(const rc_client_event_t* event)
{
const u32 id = event->leaderboard_tracker->id;
DEV_LOG("Hiding leaderboard tracker: {}", id);
auto it = std::find_if(s_state.active_leaderboard_trackers.begin(), s_state.active_leaderboard_trackers.end(),
[id](const auto& it) { return it.tracker_id == id; });
if (it == s_state.active_leaderboard_trackers.end())
return;
DEV_LOG("Hiding leaderboard tracker: {}", id);
it->active = false;
}
void Achievements::HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* event)
{
const u32 id = event->leaderboard_tracker->id;
DEV_LOG("Updating leaderboard tracker: {}: {}", id, event->leaderboard_tracker->display);
auto it = std::find_if(s_state.active_leaderboard_trackers.begin(), s_state.active_leaderboard_trackers.end(),
[id](const auto& it) { return it.tracker_id == id; });
if (it == s_state.active_leaderboard_trackers.end())
return;
DEV_LOG("Updating leaderboard tracker: {}: {}", event->leaderboard_tracker->id, event->leaderboard_tracker->display);
it->text = event->leaderboard_tracker->display;
it->active = true;
}
@ -1670,9 +1681,27 @@ void Achievements::HandleAchievementChallengeIndicatorShowEvent(const rc_client_
return;
}
std::string badge_path = GetAchievementBadgePath(event->achievement, false);
// we still track these even if the option is disabled, so that they can be displayed in the pause menu
if (g_settings.achievements_challenge_indicator_mode == AchievementChallengeIndicatorMode::Notification)
{
std::string description = fmt::format(TRANSLATE_FS("Achievements", "Challenge Started: {}"),
event->achievement->description ? event->achievement->description : "");
GPUThread::RunOnThread([title = std::string(event->achievement->title), description = std::move(description),
badge_path, id = event->achievement->id]() mutable {
if (!FullscreenUI::Initialize())
return;
ImGuiFullscreen::AddNotification(fmt::format("AchievementChallenge{}", id), LEADERBOARD_STARTED_NOTIFICATION_TIME,
std::move(title), std::move(description), std::move(badge_path));
});
}
s_state.active_challenge_indicators.push_back(
AchievementChallengeIndicator{.achievement = event->achievement,
.badge_path = GetAchievementBadgePath(event->achievement, false),
.badge_path = std::move(badge_path),
.time_remaining = LEADERBOARD_STARTED_NOTIFICATION_TIME,
.opacity = 0.0f,
.active = true});
@ -1688,6 +1717,15 @@ void Achievements::HandleAchievementChallengeIndicatorHideEvent(const rc_client_
return;
DEV_LOG("Hide challenge indicator for {} ({})", event->achievement->id, event->achievement->title);
if (g_settings.achievements_challenge_indicator_mode == AchievementChallengeIndicatorMode::Notification ||
g_settings.achievements_challenge_indicator_mode == AchievementChallengeIndicatorMode::Disabled)
{
// remove it here, because it won't naturally decay
s_state.active_challenge_indicators.erase(it);
return;
}
it->active = false;
}
@ -1696,6 +1734,9 @@ void Achievements::HandleAchievementProgressIndicatorShowEvent(const rc_client_e
DEV_LOG("Showing progress indicator: {} ({}): {}", event->achievement->id, event->achievement->title,
event->achievement->measured_progress);
if (!g_settings.achievements_progress_indicators)
return;
if (!s_state.active_progress_indicator.has_value())
s_state.active_progress_indicator.emplace();
@ -1711,6 +1752,13 @@ void Achievements::HandleAchievementProgressIndicatorHideEvent(const rc_client_e
return;
DEV_LOG("Hiding progress indicator");
if (!g_settings.achievements_progress_indicators)
{
s_state.active_progress_indicator.reset();
return;
}
s_state.active_progress_indicator->active = false;
}
@ -1718,6 +1766,9 @@ void Achievements::HandleAchievementProgressIndicatorUpdateEvent(const rc_client
{
DEV_LOG("Updating progress indicator: {} ({}): {}", event->achievement->id, event->achievement->title,
event->achievement->measured_progress);
if (!s_state.active_progress_indicator.has_value())
return;
s_state.active_progress_indicator->achievement = event->achievement;
s_state.active_progress_indicator->active = true;
}
@ -2286,11 +2337,10 @@ void Achievements::ClearUIState()
s_state.achievement_nearest_completion.reset();
}
template<typename T>
static float IndicatorOpacity(float delta_time, T& i)
static float IndicatorOpacity(float delta_time, bool active, float& opacity)
{
float target, rate;
if (i.active)
if (active)
{
target = 1.0f;
rate = Achievements::INDICATOR_FADE_IN_TIME;
@ -2301,10 +2351,10 @@ static float IndicatorOpacity(float delta_time, T& i)
rate = -Achievements::INDICATOR_FADE_OUT_TIME;
}
if (i.opacity != target)
i.opacity = ImSaturate(i.opacity + (delta_time / rate));
if (opacity != target)
opacity = ImSaturate(opacity + (delta_time / rate));
return i.opacity;
return opacity;
}
void Achievements::DrawGameOverlays()
@ -2314,7 +2364,7 @@ void Achievements::DrawGameOverlays()
using ImGuiFullscreen::RenderShadowedTextClipped;
using ImGuiFullscreen::UIStyle;
if (!HasActiveGame() || !g_settings.achievements_overlays)
if (!HasActiveGame())
return;
const auto lock = GetLock();
@ -2331,15 +2381,26 @@ void Achievements::DrawGameOverlays()
ImVec2 position = ImVec2(io.DisplaySize.x - margin, io.DisplaySize.y - margin);
ImDrawList* dl = ImGui::GetBackgroundDrawList();
if (!s_state.active_challenge_indicators.empty())
if (!s_state.active_challenge_indicators.empty() &&
(g_settings.achievements_challenge_indicator_mode == AchievementChallengeIndicatorMode::PersistentIcon ||
g_settings.achievements_challenge_indicator_mode == AchievementChallengeIndicatorMode::TemporaryIcon))
{
const bool use_time_remaining =
(g_settings.achievements_challenge_indicator_mode == AchievementChallengeIndicatorMode::TemporaryIcon);
const float x_advance = image_size.x + spacing;
ImVec2 current_position = ImVec2(position.x - image_size.x, position.y - image_size.y);
for (auto it = s_state.active_challenge_indicators.begin(); it != s_state.active_challenge_indicators.end();)
{
AchievementChallengeIndicator& indicator = *it;
const float opacity = IndicatorOpacity(io.DeltaTime, indicator);
bool active = indicator.active;
if (use_time_remaining)
{
indicator.time_remaining = std::max(indicator.time_remaining - io.DeltaTime, 0.0f);
active = (indicator.time_remaining > 0.0f);
}
const float opacity = IndicatorOpacity(io.DeltaTime, active, indicator.opacity);
GPUTexture* badge = ImGuiFullscreen::GetCachedTextureAsync(indicator.badge_path);
if (badge)
@ -2366,7 +2427,7 @@ void Achievements::DrawGameOverlays()
if (s_state.active_progress_indicator.has_value())
{
AchievementProgressIndicator& indicator = s_state.active_progress_indicator.value();
const float opacity = IndicatorOpacity(io.DeltaTime, indicator);
const float opacity = IndicatorOpacity(io.DeltaTime, indicator.active, indicator.opacity);
const std::string_view text = s_state.active_progress_indicator->achievement->measured_progress;
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
@ -2408,7 +2469,7 @@ void Achievements::DrawGameOverlays()
for (auto it = s_state.active_leaderboard_trackers.begin(); it != s_state.active_leaderboard_trackers.end();)
{
LeaderboardTrackerIndicator& indicator = *it;
const float opacity = IndicatorOpacity(io.DeltaTime, indicator);
const float opacity = IndicatorOpacity(io.DeltaTime, indicator.active, indicator.opacity);
TinyString width_string;
width_string.append(ICON_FA_STOPWATCH);

@ -6414,21 +6414,6 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
}
}
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_BELL, "Achievement Notifications"),
FSUI_VSTR("Displays popup messages on events such as achievement unlocks and leaderboard submissions."), "Cheevos",
"Notifications", true, enabled);
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_LIST_OL, "Leaderboard Notifications"),
FSUI_VSTR("Displays popup messages when starting, submitting, or failing a leaderboard challenge."),
"Cheevos", "LeaderboardNotifications", true, enabled);
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_MUSIC, "Sound Effects"),
FSUI_VSTR("Plays sound effects for events such as achievement unlocks and leaderboard submissions."), "Cheevos",
"SoundEffects", true, enabled);
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_WAND_MAGIC_SPARKLES, "Enable In-Game Overlays"),
FSUI_VSTR("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."),
"Cheevos", "Overlays", true, enabled);
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_ARROW_ROTATE_RIGHT, "Encore Mode"),
FSUI_VSTR("When enabled, each session will behave as if no achievements have been unlocked."),
"Cheevos", "EncoreMode", false, enabled);
@ -6442,6 +6427,34 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
"tracked by RetroAchievements."),
"Cheevos", "UnofficialTestMode", false, enabled);
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_BELL, "Achievement Notifications"),
FSUI_VSTR("Displays popup messages on events such as achievement unlocks and leaderboard submissions."), "Cheevos",
"Notifications", true, enabled);
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_LIST_OL, "Leaderboard Notifications"),
FSUI_VSTR("Displays popup messages when starting, submitting, or failing a leaderboard challenge."),
"Cheevos", "LeaderboardNotifications", true, enabled);
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_CLOCK, "Leaderboard Trackers"),
FSUI_VSTR("Shows a timer in the bottom-right corner of the screen when leaderboard challenges are active."),
"Cheevos", "LeaderboardTrackers", true, enabled);
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_MUSIC, "Sound Effects"),
FSUI_VSTR("Plays sound effects for events such as achievement unlocks and leaderboard submissions."), "Cheevos",
"SoundEffects", true, enabled);
DrawToggleSetting(
bsi, FSUI_ICONVSTR(ICON_FA_BARS_PROGRESS, "Progress Indicators"),
FSUI_VSTR(
"Shows a popup in the lower-right corner of the screen when progress towards a measured achievement changes."),
"Cheevos", "ProgressIndicators", true, enabled);
DrawEnumSetting(
bsi, FSUI_ICONVSTR(ICON_FA_TEMPERATURE_ARROW_UP, "Challenge Indicators"),
FSUI_VSTR("Shows a notification or icons in the lower-right corner of the screen when a challenge/primed "
"achievement is active."),
"Cheevos", "ChallengeIndicatorMode", Settings::DEFAULT_ACHIEVEMENT_CHALLENGE_INDICATOR_MODE,
&Settings::ParseAchievementChallengeIndicatorMode, &Settings::GetAchievementChallengeIndicatorModeName,
&Settings::GetAchievementChallengeIndicatorModeDisplayName, AchievementChallengeIndicatorMode::MaxCount, enabled);
if (!IsEditingGameSettings(bsi))
{
if (MenuButton(FSUI_ICONVSTR(ICON_FA_ARROWS_ROTATE, "Update Progress"),
@ -6461,7 +6474,8 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
Host::NumberFormatType::LongDateTime,
StringUtil::FromChars<s64>(bsi->GetTinyStringValue("Cheevos", "LoginTimestamp", "0")).value_or(0));
MenuButtonWithoutSummary(
SmallString::from_format(fmt::runtime(FSUI_ICONVSTR(ICON_FA_CLOCK, "Login token generated on {}")), ts_string),
SmallString::from_format(fmt::runtime(FSUI_ICONVSTR(ICON_FA_USER_CLOCK, "Login token generated on {}")),
ts_string),
false);
}
}
@ -9478,6 +9492,7 @@ TRANSLATE_NOOP("FullscreenUI", "CPU Emulation");
TRANSLATE_NOOP("FullscreenUI", "CPU Mode");
TRANSLATE_NOOP("FullscreenUI", "Cancel");
TRANSLATE_NOOP("FullscreenUI", "Capture");
TRANSLATE_NOOP("FullscreenUI", "Challenge Indicators");
TRANSLATE_NOOP("FullscreenUI", "Change Disc");
TRANSLATE_NOOP("FullscreenUI", "Change Page");
TRANSLATE_NOOP("FullscreenUI", "Change Selection");
@ -9608,7 +9623,6 @@ TRANSLATE_NOOP("FullscreenUI", "Enable Discord Presence");
TRANSLATE_NOOP("FullscreenUI", "Enable Fast Boot");
TRANSLATE_NOOP("FullscreenUI", "Enable GPU-Based Validation");
TRANSLATE_NOOP("FullscreenUI", "Enable GPU-based validation when supported by the host's renderer API. Only for developer use.");
TRANSLATE_NOOP("FullscreenUI", "Enable In-Game Overlays");
TRANSLATE_NOOP("FullscreenUI", "Enable Overclocking");
TRANSLATE_NOOP("FullscreenUI", "Enable Post Processing");
TRANSLATE_NOOP("FullscreenUI", "Enable Recompiler Block Linking");
@ -9739,6 +9753,7 @@ TRANSLATE_NOOP("FullscreenUI", "Launch a game by selecting a file/disc image.");
TRANSLATE_NOOP("FullscreenUI", "Launch a game from a file, disc, or starts the console without any disc inserted.");
TRANSLATE_NOOP("FullscreenUI", "Launch a game from images scanned from your game directories.");
TRANSLATE_NOOP("FullscreenUI", "Leaderboard Notifications");
TRANSLATE_NOOP("FullscreenUI", "Leaderboard Trackers");
TRANSLATE_NOOP("FullscreenUI", "Leaderboards");
TRANSLATE_NOOP("FullscreenUI", "Left: ");
TRANSLATE_NOOP("FullscreenUI", "Light");
@ -9855,6 +9870,7 @@ TRANSLATE_NOOP("FullscreenUI", "Press To Toggle");
TRANSLATE_NOOP("FullscreenUI", "Pressure");
TRANSLATE_NOOP("FullscreenUI", "Prevents the emulator from producing any audible sound.");
TRANSLATE_NOOP("FullscreenUI", "Prevents the screen saver from activating and the host from sleeping while emulation is running.");
TRANSLATE_NOOP("FullscreenUI", "Progress Indicators");
TRANSLATE_NOOP("FullscreenUI", "Provides vibration and LED control support over Bluetooth.");
TRANSLATE_NOOP("FullscreenUI", "Purple Rain");
TRANSLATE_NOOP("FullscreenUI", "Push a controller button or axis now.");
@ -9989,9 +10005,11 @@ TRANSLATE_NOOP("FullscreenUI", "Show Resolution");
TRANSLATE_NOOP("FullscreenUI", "Show Speed");
TRANSLATE_NOOP("FullscreenUI", "Show Status Indicators");
TRANSLATE_NOOP("FullscreenUI", "Shows a background image or shader when a game isn't running. Backgrounds are located in resources/fullscreenui/backgrounds in the data directory.");
TRANSLATE_NOOP("FullscreenUI", "Shows a notification or icons in the lower-right corner of the screen when a challenge/primed achievement is active.");
TRANSLATE_NOOP("FullscreenUI", "Shows a popup in the lower-right corner of the screen when progress towards a measured achievement changes.");
TRANSLATE_NOOP("FullscreenUI", "Shows a timer in the bottom-right corner of the screen when leaderboard challenges are active.");
TRANSLATE_NOOP("FullscreenUI", "Shows a visual history of frame times in the upper-left corner of the display.");
TRANSLATE_NOOP("FullscreenUI", "Shows enhancement settings in the bottom-right corner of the screen.");
TRANSLATE_NOOP("FullscreenUI", "Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active.");
TRANSLATE_NOOP("FullscreenUI", "Shows information about input and audio latency in the top-right corner of the display.");
TRANSLATE_NOOP("FullscreenUI", "Shows information about the emulated GPU in the top-right corner of the display.");
TRANSLATE_NOOP("FullscreenUI", "Shows on-screen-display messages when events occur.");

@ -437,18 +437,27 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro
achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false);
achievements_hardcore_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false);
achievements_notifications = si.GetBoolValue("Cheevos", "Notifications", true);
achievements_leaderboard_notifications = si.GetBoolValue("Cheevos", "LeaderboardNotifications", true);
achievements_sound_effects = si.GetBoolValue("Cheevos", "SoundEffects", true);
achievements_overlays = si.GetBoolValue("Cheevos", "Overlays", true);
achievements_encore_mode = si.GetBoolValue("Cheevos", "EncoreMode", false);
achievements_spectator_mode = si.GetBoolValue("Cheevos", "SpectatorMode", false);
achievements_unofficial_test_mode = si.GetBoolValue("Cheevos", "UnofficialTestMode", false);
achievements_use_raintegration = si.GetBoolValue("Cheevos", "UseRAIntegration", false);
achievements_notifications = si.GetBoolValue("Cheevos", "Notifications", true);
achievements_leaderboard_notifications = si.GetBoolValue("Cheevos", "LeaderboardNotifications", true);
achievements_leaderboard_trackers = si.GetBoolValue("Cheevos", "LeaderboardTrackers", true);
achievements_sound_effects = si.GetBoolValue("Cheevos", "SoundEffects", true);
achievements_progress_indicators = si.GetBoolValue("Cheevos", "ProgressIndicators", true);
achievements_challenge_indicator_mode =
ParseAchievementChallengeIndicatorMode(
si.GetStringValue("Cheevos", "ChallengeIndicatorMode",
GetAchievementChallengeIndicatorModeName(DEFAULT_ACHIEVEMENT_CHALLENGE_INDICATOR_MODE))
.c_str())
.value_or(DEFAULT_ACHIEVEMENT_CHALLENGE_INDICATOR_MODE);
achievements_notification_duration =
si.GetIntValue("Cheevos", "NotificationsDuration", DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
Truncate8(std::min<u32>(si.GetUIntValue("Cheevos", "NotificationsDuration", DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME),
std::numeric_limits<u8>::max()));
achievements_leaderboard_duration =
si.GetIntValue("Cheevos", "LeaderboardsDuration", DEFAULT_LEADERBOARD_NOTIFICATION_TIME);
Truncate8(std::min<u32>(si.GetUIntValue("Cheevos", "LeaderboardsDuration", DEFAULT_LEADERBOARD_NOTIFICATION_TIME),
std::numeric_limits<u8>::max()));
#ifndef __ANDROID__
enable_gdb_server = si.GetBoolValue("Debug", "EnableGDBServer");
@ -737,16 +746,19 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.SetBoolValue("Cheevos", "Enabled", achievements_enabled);
si.SetBoolValue("Cheevos", "ChallengeMode", achievements_hardcore_mode);
si.SetBoolValue("Cheevos", "Notifications", achievements_notifications);
si.SetBoolValue("Cheevos", "LeaderboardNotifications", achievements_leaderboard_notifications);
si.SetBoolValue("Cheevos", "SoundEffects", achievements_sound_effects);
si.SetBoolValue("Cheevos", "Overlays", achievements_overlays);
si.SetBoolValue("Cheevos", "EncoreMode", achievements_encore_mode);
si.SetBoolValue("Cheevos", "SpectatorMode", achievements_spectator_mode);
si.SetBoolValue("Cheevos", "UnofficialTestMode", achievements_unofficial_test_mode);
si.SetBoolValue("Cheevos", "UseRAIntegration", achievements_use_raintegration);
si.SetIntValue("Cheevos", "NotificationsDuration", achievements_notification_duration);
si.SetIntValue("Cheevos", "LeaderboardsDuration", achievements_leaderboard_duration);
si.SetBoolValue("Cheevos", "Notifications", achievements_notifications);
si.SetBoolValue("Cheevos", "LeaderboardNotifications", achievements_leaderboard_notifications);
si.SetBoolValue("Cheevos", "LeaderboardTrackers", achievements_leaderboard_trackers);
si.SetBoolValue("Cheevos", "SoundEffects", achievements_sound_effects);
si.SetBoolValue("Cheevos", "ProgressIndicators", achievements_progress_indicators);
si.SetStringValue("Cheevos", "ChallengeIndicatorMode",
GetAchievementChallengeIndicatorModeName(achievements_challenge_indicator_mode));
si.SetUIntValue("Cheevos", "NotificationsDuration", achievements_notification_duration);
si.SetUIntValue("Cheevos", "LeaderboardsDuration", achievements_leaderboard_duration);
#ifndef __ANDROID__
si.SetBoolValue("Debug", "EnableGDBServer", enable_gdb_server);
@ -2156,6 +2168,49 @@ std::optional<DisplayScreenshotFormat> Settings::GetDisplayScreenshotFormatFromF
return std::nullopt;
}
static constexpr const std::array s_achievement_challenge_indicator_mode_names = {
"Disabled",
"PersistentIcon",
"TemporaryIcon",
"Notification",
};
static_assert(s_achievement_challenge_indicator_mode_names.size() ==
static_cast<size_t>(AchievementChallengeIndicatorMode::MaxCount));
static constexpr const std::array s_achievement_challenge_indicator_mode_display_names = {
TRANSLATE_DISAMBIG_NOOP("Settings", "Disabled", "AchievementChallengeIndicatorMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Show Persistent Icons", "AchievementChallengeIndicatorMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Show Temporary Icons", "AchievementChallengeIndicatorMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Show Notifications", "AchievementChallengeIndicatorMode"),
};
static_assert(s_achievement_challenge_indicator_mode_display_names.size() ==
static_cast<size_t>(AchievementChallengeIndicatorMode::MaxCount));
std::optional<AchievementChallengeIndicatorMode> Settings::ParseAchievementChallengeIndicatorMode(const char* str)
{
int index = 0;
for (const char* name : s_achievement_challenge_indicator_mode_names)
{
if (StringUtil::Strcasecmp(name, str) == 0)
return static_cast<AchievementChallengeIndicatorMode>(index);
index++;
}
return std::nullopt;
}
const char* Settings::GetAchievementChallengeIndicatorModeName(AchievementChallengeIndicatorMode mode)
{
return s_achievement_challenge_indicator_mode_names[static_cast<size_t>(mode)];
}
const char* Settings::GetAchievementChallengeIndicatorModeDisplayName(AchievementChallengeIndicatorMode mode)
{
return Host::TranslateToCString("Settings",
s_achievement_challenge_indicator_mode_display_names[static_cast<size_t>(mode)],
"AchievementChallengeIndicatorMode");
}
static constexpr const std::array s_memory_card_type_names = {
"None", "Shared", "PerGame", "PerGameTitle", "PerGameFileTitle", "NonPersistent",
};

@ -347,16 +347,19 @@ struct Settings : public GPUSettings
// achievements
bool achievements_enabled : 1 = false;
bool achievements_hardcore_mode : 1 = false;
bool achievements_notifications : 1 = true;
bool achievements_leaderboard_notifications : 1 = true;
bool achievements_sound_effects : 1 = true;
bool achievements_overlays : 1 = true;
bool achievements_encore_mode : 1 = false;
bool achievements_spectator_mode : 1 = false;
bool achievements_unofficial_test_mode : 1 = false;
bool achievements_use_raintegration : 1 = false;
s32 achievements_notification_duration = DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME;
s32 achievements_leaderboard_duration = DEFAULT_LEADERBOARD_NOTIFICATION_TIME;
bool achievements_notifications : 1 = true;
bool achievements_leaderboard_notifications : 1 = true;
bool achievements_leaderboard_trackers : 1 = true;
bool achievements_sound_effects : 1 = true;
bool achievements_progress_indicators : 1 = true;
AchievementChallengeIndicatorMode achievements_challenge_indicator_mode =
DEFAULT_ACHIEVEMENT_CHALLENGE_INDICATOR_MODE;
u8 achievements_notification_duration = DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME;
u8 achievements_leaderboard_duration = DEFAULT_LEADERBOARD_NOTIFICATION_TIME;
float emulation_speed = 1.0f;
float fast_forward_speed = 0.0f;
@ -546,6 +549,10 @@ struct Settings : public GPUSettings
static const char* GetDisplayScreenshotModeName(DisplayScreenshotMode mode);
static const char* GetDisplayScreenshotModeDisplayName(DisplayScreenshotMode mode);
static std::optional<AchievementChallengeIndicatorMode> ParseAchievementChallengeIndicatorMode(const char* str);
static const char* GetAchievementChallengeIndicatorModeName(AchievementChallengeIndicatorMode mode);
static const char* GetAchievementChallengeIndicatorModeDisplayName(AchievementChallengeIndicatorMode mode);
static std::optional<DisplayScreenshotFormat> ParseDisplayScreenshotFormat(const char* str);
static const char* GetDisplayScreenshotFormatName(DisplayScreenshotFormat mode);
static const char* GetDisplayScreenshotFormatDisplayName(DisplayScreenshotFormat mode);
@ -602,8 +609,10 @@ struct Settings : public GPUSettings
static constexpr MultitapMode DEFAULT_MULTITAP_MODE = MultitapMode::Disabled;
static constexpr PIODeviceType DEFAULT_PIO_DEVICE_TYPE = PIODeviceType::None;
static constexpr s32 DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME = 5;
static constexpr s32 DEFAULT_LEADERBOARD_NOTIFICATION_TIME = 10;
static constexpr AchievementChallengeIndicatorMode DEFAULT_ACHIEVEMENT_CHALLENGE_INDICATOR_MODE =
AchievementChallengeIndicatorMode::PersistentIcon;
static constexpr u8 DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME = 5;
static constexpr u8 DEFAULT_LEADERBOARD_NOTIFICATION_TIME = 10;
static constexpr Log::Level DEFAULT_LOG_LEVEL = Log::Level::Info;

@ -228,6 +228,16 @@ enum class DisplayScreenshotFormat : u8
Count
};
enum class AchievementChallengeIndicatorMode : u8
{
Disabled,
PersistentIcon,
TemporaryIcon,
Notification,
MaxCount
};
enum class ControllerType : u8
{
None,

@ -27,38 +27,33 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* dialog, QWi
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable, "Cheevos", "Enabled", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hardcoreMode, "Cheevos", "ChallengeMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.achievementNotifications, "Cheevos", "Notifications", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.leaderboardNotifications, "Cheevos",
"LeaderboardNotifications", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Cheevos", "SoundEffects", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.overlays, "Cheevos", "Overlays", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.encoreMode, "Cheevos", "EncoreMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.spectatorMode, "Cheevos", "SpectatorMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.unofficialAchievements, "Cheevos", "UnofficialTestMode",
false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.achievementNotifications, "Cheevos", "Notifications", true);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.achievementNotificationsDuration, "Cheevos",
"NotificationsDuration",
Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.leaderboardNotifications, "Cheevos",
"LeaderboardNotifications", true);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.leaderboardNotificationsDuration, "Cheevos",
"LeaderboardsDuration",
Settings::DEFAULT_LEADERBOARD_NOTIFICATION_TIME);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.leaderboardTrackers, "Cheevos", "LeaderboardTrackers", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Cheevos", "SoundEffects", true);
SettingWidgetBinder::BindWidgetToEnumSetting(
sif, m_ui.challengeIndicatorMode, "Cheevos", "ChallengeIndicatorMode",
&Settings::ParseAchievementChallengeIndicatorMode, &Settings::GetAchievementChallengeIndicatorModeName,
&Settings::GetAchievementChallengeIndicatorModeDisplayName, Settings::DEFAULT_ACHIEVEMENT_CHALLENGE_INDICATOR_MODE,
AchievementChallengeIndicatorMode::MaxCount);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.progressIndicators, "Cheevos", "ProgressIndicators", true);
dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
tr("When enabled and logged in, DuckStation will scan for achievements on startup."));
dialog->registerWidgetHelp(m_ui.hardcoreMode, tr("Enable Hardcore Mode"), tr("Unchecked"),
tr("\"Challenge\" mode for achievements, including leaderboard tracking. Disables save "
"state, cheats, and slowdown functions."));
dialog->registerWidgetHelp(m_ui.achievementNotifications, tr("Show Achievement Notifications"), tr("Checked"),
tr("Displays popup messages on events such as achievement unlocks and game completion."));
dialog->registerWidgetHelp(
m_ui.leaderboardNotifications, tr("Show Leaderboard Notifications"), tr("Checked"),
tr("Displays popup messages when starting, submitting, or failing a leaderboard challenge."));
dialog->registerWidgetHelp(
m_ui.soundEffects, tr("Enable Sound Effects"), tr("Checked"),
tr("Plays sound effects for events such as achievement unlocks and leaderboard submissions."));
dialog->registerWidgetHelp(
m_ui.overlays, tr("Enable In-Game Overlays"), tr("Checked"),
tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));
dialog->registerWidgetHelp(m_ui.encoreMode, tr("Enable Encore Mode"), tr("Unchecked"),
tr("When enabled, each session will behave as if no achievements have been unlocked."));
dialog->registerWidgetHelp(m_ui.spectatorMode, tr("Enable Spectator Mode"), tr("Unchecked"),
@ -68,6 +63,23 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* dialog, QWi
m_ui.unofficialAchievements, tr("Test Unofficial Achievements"), tr("Unchecked"),
tr("When enabled, DuckStation will list achievements from unofficial sets. Please note that these achievements are "
"not tracked by RetroAchievements, so they unlock every time."));
dialog->registerWidgetHelp(m_ui.achievementNotifications, tr("Show Achievement Notifications"), tr("Checked"),
tr("Displays popup messages on events such as achievement unlocks and game completion."));
dialog->registerWidgetHelp(
m_ui.leaderboardNotifications, tr("Show Leaderboard Notifications"), tr("Checked"),
tr("Displays popup messages when starting, submitting, or failing a leaderboard challenge."));
dialog->registerWidgetHelp(
m_ui.leaderboardTrackers, tr("Show Leaderboard Trackers"), tr("Checked"),
tr("Shows a timer in the bottom-right corner of the screen when leaderboard challenges are active."));
dialog->registerWidgetHelp(
m_ui.soundEffects, tr("Enable Sound Effects"), tr("Checked"),
tr("Plays sound effects for events such as achievement unlocks and leaderboard submissions."));
dialog->registerWidgetHelp(m_ui.challengeIndicatorMode, tr("Challenge Indicators"), tr("Show Persistent Icons"),
tr("Shows a notification or icons in the lower-right corner of the screen when a "
"challenge/primed achievement is active."));
dialog->registerWidgetHelp(
m_ui.progressIndicators, tr("Show Progress Indicators"), tr("Checked"),
tr("Shows a popup in the lower-right corner of the screen when progress towards a measured achievement changes."));
connect(m_ui.enable, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.hardcoreMode, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
@ -140,8 +152,11 @@ void AchievementSettingsWidget::updateEnableState()
m_ui.achievementNotificationsDurationLabel->setEnabled(notifications);
m_ui.leaderboardNotificationsDuration->setEnabled(lb_notifications);
m_ui.leaderboardNotificationsDurationLabel->setEnabled(lb_notifications);
m_ui.leaderboardTrackers->setEnabled(enabled);
m_ui.soundEffects->setEnabled(enabled);
m_ui.overlays->setEnabled(enabled);
m_ui.challengeIndicatorMode->setEnabled(enabled);
m_ui.challengeIndicatorModeLabel->setEnabled(enabled);
m_ui.progressIndicators->setEnabled(enabled);
m_ui.encoreMode->setEnabled(enabled);
m_ui.spectatorMode->setEnabled(enabled);
m_ui.unofficialAchievements->setEnabled(enabled);
@ -199,11 +214,10 @@ void AchievementSettingsWidget::updateLoginState()
{
const u64 login_unix_timestamp =
StringUtil::FromChars<u64>(Host::GetBaseStringSettingValue("Cheevos", "LoginTimestamp", "0")).value_or(0);
const QString login_timestamp = QtHost::FormatNumber(Host::NumberFormatType::ShortDateTime,
static_cast<s64>(login_unix_timestamp));
m_ui.loginStatus->setText(tr("Username: %1\nLogin token generated on %2.")
.arg(QString::fromStdString(username))
.arg(login_timestamp));
const QString login_timestamp =
QtHost::FormatNumber(Host::NumberFormatType::ShortDateTime, static_cast<s64>(login_unix_timestamp));
m_ui.loginStatus->setText(
tr("Username: %1\nLogin token generated on %2.").arg(QString::fromStdString(username)).arg(login_timestamp));
m_ui.loginButton->setText(tr("Logout"));
}
else

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>674</width>
<height>420</height>
<height>479</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -80,6 +80,13 @@
<string>Notifications</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,1">
<item row="1" column="0">
<widget class="QCheckBox" name="leaderboardNotifications">
<property name="text">
<string>Show Leaderboard Notifications</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,0">
<item>
@ -165,27 +172,46 @@
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="leaderboardNotifications">
<item row="2" column="1">
<widget class="QCheckBox" name="soundEffects">
<property name="text">
<string>Show Leaderboard Notifications</string>
<string>Enable Sound Effects</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="soundEffects">
<widget class="QCheckBox" name="leaderboardTrackers">
<property name="text">
<string>Enable Sound Effects</string>
<string>Show Leaderboard Trackers</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="overlays">
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Progress Tracking</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,1">
<item row="0" column="0">
<widget class="QLabel" name="challengeIndicatorModeLabel">
<property name="text">
<string>Enable In-Game Overlays</string>
<string>Challenge Indicators:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="progressIndicators">
<property name="text">
<string>Show Progress Indicators</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="challengeIndicatorMode"/>
</item>
</layout>
</widget>
</item>

Loading…
Cancel
Save